1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 #include <sys/sysmacros.h>
  26 #include <sys/types.h>
  27 #include <sys/kmem.h>
  28 #include <sys/ddi.h>
  29 #include <sys/sunddi.h>
  30 #include <sys/sunndi.h>
  31 #include <sys/promif.h>
  32 #include <sys/pcie.h>
  33 #include <sys/pci_cap.h>
  34 #include <sys/pcie_impl.h>
  35 #include <sys/pcie_acpi.h>
  36 #include <sys/acpi/acpi.h>
  37 #include <sys/acpica.h>
  38 
  39 ACPI_STATUS pcie_acpi_eval_osc(dev_info_t *dip, ACPI_HANDLE osc_hdl,
  40         uint32_t *osc_flags);
  41 static ACPI_STATUS pcie_acpi_find_osc(ACPI_HANDLE busobj,
  42         ACPI_HANDLE *osc_hdlp);
  43 
  44 #ifdef DEBUG
  45 static void pcie_dump_acpi_obj(ACPI_HANDLE pcibus_obj);
  46 static ACPI_STATUS pcie_walk_obj_namespace(ACPI_HANDLE hdl, uint32_t nl,
  47         void *context, void **ret);
  48 static ACPI_STATUS pcie_print_acpi_name(ACPI_HANDLE hdl, uint32_t nl,
  49         void *context, void **ret);
  50 #endif /* DEBUG */
  51 
  52 int
  53 pcie_acpi_osc(dev_info_t *dip, uint32_t *osc_flags)
  54 {
  55         ACPI_HANDLE pcibus_obj;
  56         int status = AE_ERROR;
  57         ACPI_HANDLE osc_hdl;
  58         pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
  59         pcie_x86_priv_t *osc_p = (pcie_x86_priv_t *)bus_p->bus_plat_private;
  60 
  61         /* Mark this so we know _OSC has been called for this device */
  62         osc_p->bus_osc = B_TRUE;
  63 
  64         /*
  65          * (1)  Find the ACPI device node for this bus node.
  66          */
  67         status = acpica_get_handle(dip, &pcibus_obj);
  68         if (status != AE_OK) {
  69                 PCIE_DBG("No ACPI device found (dip %p)\n", (void *)dip);
  70                 return (DDI_FAILURE);
  71         }
  72 
  73         /*
  74          * (2)  Check if _OSC method is present.
  75          */
  76         if (pcie_acpi_find_osc(pcibus_obj, &osc_hdl) != AE_OK) {
  77                 /* no _OSC method present */
  78                 PCIE_DBG("no _OSC method present for dip %p\n",
  79                     (void *)dip);
  80                 return (DDI_FAILURE);
  81         }
  82 
  83         /*
  84          * (3)  _OSC method exists; evaluate _OSC.
  85          */
  86         if (pcie_acpi_eval_osc(dip, osc_hdl, osc_flags) != AE_OK) {
  87                 PCIE_DBG("Failed to evaluate _OSC method for dip 0x%p\n",
  88                     (void *)dip);
  89                 return (DDI_FAILURE);
  90         }
  91 
  92         osc_p->bus_osc_hp = (*osc_flags & OSC_CONTROL_PCIE_NAT_HP) ?
  93             B_TRUE : B_FALSE;
  94         osc_p->bus_osc_aer = (*osc_flags & OSC_CONTROL_PCIE_ADV_ERR) ?
  95             B_TRUE : B_FALSE;
  96 
  97 #ifdef DEBUG
  98         if (pcie_debug_flags > 1)
  99                 pcie_dump_acpi_obj(pcibus_obj);
 100 #endif /* DEBUG */
 101 
 102         return (DDI_SUCCESS);
 103 }
 104 
 105 static ACPI_STATUS
 106 pcie_acpi_find_osc(ACPI_HANDLE busobj, ACPI_HANDLE *osc_hdlp)
 107 {
 108         ACPI_HANDLE parentobj = busobj;
 109         ACPI_STATUS status = AE_NOT_FOUND;
 110 
 111         *osc_hdlp = NULL;
 112 
 113         /*
 114          * Walk up the ACPI device tree looking for _OSC method.
 115          */
 116         do {
 117                 busobj = parentobj;
 118                 if ((status = AcpiGetHandle(busobj, "_OSC", osc_hdlp)) == AE_OK)
 119                         break;
 120         } while (AcpiGetParent(busobj, &parentobj) == AE_OK);
 121 
 122         if (*osc_hdlp == NULL)
 123                 status = AE_NOT_FOUND;
 124 
 125         return (status);
 126 }
 127 
 128 /* UUID for for PCI/PCI-X/PCI-Exp hierarchy as defined in PCI fw ver 3.0 */
 129 static uint8_t pcie_uuid[16] =
 130         {0x5b, 0x4d, 0xdb, 0x33, 0xf7, 0x1f, 0x1c, 0x40,
 131         0x96, 0x57, 0x74, 0x41, 0xc0, 0x3d, 0xd7, 0x66};
 132 
 133 /*
 134  * Evaluate _OSC method.
 135  */
 136 ACPI_STATUS
 137 pcie_acpi_eval_osc(dev_info_t *dip, ACPI_HANDLE osc_hdl, uint32_t *osc_flags)
 138 {
 139         ACPI_STATUS             status;
 140         ACPI_OBJECT_LIST        arglist;
 141         ACPI_OBJECT             args[4];
 142         UINT32                  caps_buffer[3];
 143         ACPI_BUFFER             rb;
 144         UINT32                  *rbuf;
 145         UINT32                  tmp_ctrl;
 146 
 147         /* construct argument list */
 148         arglist.Count = 4;
 149         arglist.Pointer = args;
 150 
 151         /* arg0 - UUID */
 152         args[0].Type = ACPI_TYPE_BUFFER;
 153         args[0].Buffer.Length = 16; /* size of UUID string */
 154         args[0].Buffer.Pointer = pcie_uuid;
 155 
 156         /* arg1 - Revision ID */
 157         args[1].Type = ACPI_TYPE_INTEGER;
 158         args[1].Integer.Value = PCIE_OSC_REVISION_ID;
 159 
 160         /* arg2 - Count */
 161         args[2].Type = ACPI_TYPE_INTEGER;
 162         args[2].Integer.Value = 3; /* no. of DWORDS in caps_buffer */
 163 
 164         /* arg3 - Capabilities Buffer */
 165         args[3].Type = ACPI_TYPE_BUFFER;
 166         args[3].Buffer.Length = 12;
 167         args[3].Buffer.Pointer = (void *)caps_buffer;
 168 
 169         /* Initialize Capabilities Buffer */
 170 
 171         /* DWORD1: no query flag set */
 172         caps_buffer[0] = 0;
 173         /* DWORD2: Support Field */
 174         caps_buffer[1] = OSC_SUPPORT_FIELD_INIT;
 175         /* DWORD3: Control Field */
 176         caps_buffer[2] = OSC_CONTROL_FIELD_INIT;
 177 
 178         /* If hotplug is supported add the corresponding control fields */
 179         if (*osc_flags & OSC_CONTROL_PCIE_NAT_HP)
 180                 caps_buffer[2] |= (OSC_CONTROL_PCIE_NAT_HP |
 181                     OSC_CONTROL_PCIE_NAT_PM);
 182 
 183         tmp_ctrl = caps_buffer[2];
 184         rb.Length = ACPI_ALLOCATE_BUFFER;
 185         rb.Pointer = NULL;
 186 
 187         status = AcpiEvaluateObjectTyped(osc_hdl, NULL, &arglist, &rb,
 188             ACPI_TYPE_BUFFER);
 189         if (status != AE_OK) {
 190                 PCIE_DBG("Failed to execute _OSC method (status %d)\n",
 191                     status);
 192                 return (status);
 193         }
 194 
 195         /* LINTED pointer alignment */
 196         rbuf = (UINT32 *)((ACPI_OBJECT *)rb.Pointer)->Buffer.Pointer;
 197 
 198         /* check the STATUS word in the capability buffer */
 199         if (rbuf[0] & OSC_STATUS_ERRORS) {
 200                 PCIE_DBG("_OSC method failed (STATUS %d)\n", rbuf[0]);
 201                 AcpiOsFree(rb.Pointer);
 202                 return (AE_ERROR);
 203         }
 204 
 205         *osc_flags = rbuf[2];
 206 
 207         PCIE_DBG("_OSC method evaluation completed for 0x%p: "
 208             "STATUS 0x%x SUPPORT 0x%x CONTROL req 0x%x, CONTROL ret 0x%x\n",
 209             (void *)dip, rbuf[0], rbuf[1], tmp_ctrl, rbuf[2]);
 210 
 211         AcpiOsFree(rb.Pointer);
 212 
 213         return (AE_OK);
 214 }
 215 
 216 /*
 217  * Checks if _OSC method has been called for this device.
 218  */
 219 boolean_t
 220 pcie_is_osc(dev_info_t *dip)
 221 {
 222         pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
 223         pcie_x86_priv_t *osc_p = (pcie_x86_priv_t *)bus_p->bus_plat_private;
 224         return (osc_p->bus_osc);
 225 }
 226 
 227 #ifdef DEBUG
 228 static void
 229 pcie_dump_acpi_obj(ACPI_HANDLE pcibus_obj)
 230 {
 231         int status;
 232         ACPI_BUFFER retbuf;
 233 
 234         if (pcibus_obj == NULL)
 235                 return;
 236 
 237         /* print the full path name */
 238         retbuf.Pointer = NULL;
 239         retbuf.Length = ACPI_ALLOCATE_BUFFER;
 240         status = AcpiGetName(pcibus_obj, ACPI_FULL_PATHNAME, &retbuf);
 241         if (status != AE_OK)
 242                 return;
 243         PCIE_DBG("PCIE BUS PATHNAME: %s\n", (char *)retbuf.Pointer);
 244         AcpiOsFree(retbuf.Pointer);
 245 
 246         /* dump all the methods for this bus node */
 247         PCIE_DBG("  METHODS: \n");
 248         status = AcpiWalkNamespace(ACPI_TYPE_METHOD, pcibus_obj, 1,
 249             pcie_print_acpi_name, NULL, "  ", NULL);
 250         /* dump all the child devices */
 251         status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, pcibus_obj, 1,
 252             pcie_walk_obj_namespace, NULL, NULL, NULL);
 253 }
 254 
 255 /*ARGSUSED*/
 256 static ACPI_STATUS
 257 pcie_walk_obj_namespace(ACPI_HANDLE hdl, uint32_t nl, void *context,
 258         void **ret)
 259 {
 260         int status;
 261         ACPI_BUFFER retbuf;
 262         char buf[32];
 263 
 264         /* print the full path name */
 265         retbuf.Pointer = NULL;
 266         retbuf.Length = ACPI_ALLOCATE_BUFFER;
 267         status = AcpiGetName(hdl, ACPI_FULL_PATHNAME, &retbuf);
 268         if (status != AE_OK)
 269                 return (status);
 270         buf[0] = 0;
 271         while (nl--)
 272                 (void) strcat(buf, "  ");
 273         PCIE_DBG("%sDEVICE: %s\n", buf, (char *)retbuf.Pointer);
 274         AcpiOsFree(retbuf.Pointer);
 275 
 276         /* dump all the methods for this device */
 277         PCIE_DBG("%s  METHODS: \n", buf);
 278         status = AcpiWalkNamespace(ACPI_TYPE_METHOD, hdl, 1,
 279             pcie_print_acpi_name, NULL, (void *)buf, NULL);
 280         return (status);
 281 }
 282 
 283 /*ARGSUSED*/
 284 static ACPI_STATUS
 285 pcie_print_acpi_name(ACPI_HANDLE hdl, uint32_t nl, void *context, void **ret)
 286 {
 287         int status;
 288         ACPI_BUFFER retbuf;
 289         char name[16];
 290 
 291         retbuf.Pointer = name;
 292         retbuf.Length = 16;
 293         status = AcpiGetName(hdl, ACPI_SINGLE_NAME, &retbuf);
 294         if (status == AE_OK)
 295                 PCIE_DBG("%s    %s \n", (char *)context, name);
 296         return (AE_OK);
 297 }
 298 #endif /* DEBUG */