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 */