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 void 53 pcie_init_plat(dev_info_t *dip) 54 { 55 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 56 bus_p->bus_plat_private = 57 (pcie_x86_priv_t *)kmem_zalloc(sizeof (pcie_x86_priv_t), KM_SLEEP); 58 } 59 60 void 61 pcie_fini_plat(dev_info_t *dip) 62 { 63 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 64 65 kmem_free(bus_p->bus_plat_private, sizeof (pcie_x86_priv_t)); 66 } 67 68 int 69 pcie_acpi_osc(dev_info_t *dip, uint32_t *osc_flags) 70 { 71 ACPI_HANDLE pcibus_obj; 72 int status = AE_ERROR; 73 ACPI_HANDLE osc_hdl; 74 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 75 pcie_x86_priv_t *osc_p = (pcie_x86_priv_t *)bus_p->bus_plat_private; 76 77 /* Mark this so we know _OSC has been called for this device */ 78 osc_p->bus_osc = B_TRUE; 79 80 /* 81 * (1) Find the ACPI device node for this bus node. 82 */ 83 status = acpica_get_handle(dip, &pcibus_obj); 84 if (status != AE_OK) { 85 PCIE_DBG("No ACPI device found (dip %p)\n", (void *)dip); 86 return (DDI_FAILURE); 87 } 88 89 /* 90 * (2) Check if _OSC method is present. 91 */ 92 if (pcie_acpi_find_osc(pcibus_obj, &osc_hdl) != AE_OK) { 93 /* no _OSC method present */ 94 PCIE_DBG("no _OSC method present for dip %p\n", 95 (void *)dip); 96 return (DDI_FAILURE); 97 } 98 99 /* 100 * (3) _OSC method exists; evaluate _OSC. 101 */ 102 if (pcie_acpi_eval_osc(dip, osc_hdl, osc_flags) != AE_OK) { 103 PCIE_DBG("Failed to evaluate _OSC method for dip 0x%p\n", 104 (void *)dip); 105 return (DDI_FAILURE); 106 } 107 108 osc_p->bus_osc_hp = (*osc_flags & OSC_CONTROL_PCIE_NAT_HP) ? 109 B_TRUE : B_FALSE; 110 osc_p->bus_osc_aer = (*osc_flags & OSC_CONTROL_PCIE_ADV_ERR) ? 111 B_TRUE : B_FALSE; 112 113 #ifdef DEBUG 114 if (pcie_debug_flags > 1) 115 pcie_dump_acpi_obj(pcibus_obj); 116 #endif /* DEBUG */ 117 118 return (DDI_SUCCESS); 119 } 120 121 static ACPI_STATUS 122 pcie_acpi_find_osc(ACPI_HANDLE busobj, ACPI_HANDLE *osc_hdlp) 123 { 124 ACPI_HANDLE parentobj = busobj; 125 ACPI_STATUS status = AE_NOT_FOUND; 126 127 *osc_hdlp = NULL; 128 129 /* 130 * Walk up the ACPI device tree looking for _OSC method. 131 */ 132 do { 133 busobj = parentobj; 134 if ((status = AcpiGetHandle(busobj, "_OSC", osc_hdlp)) == AE_OK) 135 break; 136 } while (AcpiGetParent(busobj, &parentobj) == AE_OK); 137 138 if (*osc_hdlp == NULL) 139 status = AE_NOT_FOUND; 140 141 return (status); 142 } 143 144 /* UUID for for PCI/PCI-X/PCI-Exp hierarchy as defined in PCI fw ver 3.0 */ 145 static uint8_t pcie_uuid[16] = 146 {0x5b, 0x4d, 0xdb, 0x33, 0xf7, 0x1f, 0x1c, 0x40, 147 0x96, 0x57, 0x74, 0x41, 0xc0, 0x3d, 0xd7, 0x66}; 148 149 /* 150 * Evaluate _OSC method. 151 */ 152 ACPI_STATUS 153 pcie_acpi_eval_osc(dev_info_t *dip, ACPI_HANDLE osc_hdl, uint32_t *osc_flags) 154 { 155 ACPI_STATUS status; 156 ACPI_OBJECT_LIST arglist; 157 ACPI_OBJECT args[4]; 158 UINT32 caps_buffer[3]; 159 ACPI_BUFFER rb; 160 UINT32 *rbuf; 161 UINT32 tmp_ctrl; 162 163 /* construct argument list */ 164 arglist.Count = 4; 165 arglist.Pointer = args; 166 167 /* arg0 - UUID */ 168 args[0].Type = ACPI_TYPE_BUFFER; 169 args[0].Buffer.Length = 16; /* size of UUID string */ 170 args[0].Buffer.Pointer = pcie_uuid; 171 172 /* arg1 - Revision ID */ 173 args[1].Type = ACPI_TYPE_INTEGER; 174 args[1].Integer.Value = PCIE_OSC_REVISION_ID; 175 176 /* arg2 - Count */ 177 args[2].Type = ACPI_TYPE_INTEGER; 178 args[2].Integer.Value = 3; /* no. of DWORDS in caps_buffer */ 179 180 /* arg3 - Capabilities Buffer */ 181 args[3].Type = ACPI_TYPE_BUFFER; 182 args[3].Buffer.Length = 12; 183 args[3].Buffer.Pointer = (void *)caps_buffer; 184 185 /* Initialize Capabilities Buffer */ 186 187 /* DWORD1: no query flag set */ 188 caps_buffer[0] = 0; 189 /* DWORD2: Support Field */ 190 caps_buffer[1] = OSC_SUPPORT_FIELD_INIT; 191 /* DWORD3: Control Field */ 192 caps_buffer[2] = OSC_CONTROL_FIELD_INIT; 193 194 /* If hotplug is supported add the corresponding control fields */ 195 if (*osc_flags & OSC_CONTROL_PCIE_NAT_HP) 196 caps_buffer[2] |= (OSC_CONTROL_PCIE_NAT_HP | 197 OSC_CONTROL_PCIE_NAT_PM); 198 199 tmp_ctrl = caps_buffer[2]; 200 rb.Length = ACPI_ALLOCATE_BUFFER; 201 rb.Pointer = NULL; 202 203 status = AcpiEvaluateObjectTyped(osc_hdl, NULL, &arglist, &rb, 204 ACPI_TYPE_BUFFER); 205 if (status != AE_OK) { 206 PCIE_DBG("Failed to execute _OSC method (status %d)\n", 207 status); 208 return (status); 209 } 210 211 /* LINTED pointer alignment */ 212 rbuf = (UINT32 *)((ACPI_OBJECT *)rb.Pointer)->Buffer.Pointer; 213 214 /* check the STATUS word in the capability buffer */ 215 if (rbuf[0] & OSC_STATUS_ERRORS) { 216 PCIE_DBG("_OSC method failed (STATUS %d)\n", rbuf[0]); 217 AcpiOsFree(rb.Pointer); 218 return (AE_ERROR); 219 } 220 221 *osc_flags = rbuf[2]; 222 223 PCIE_DBG("_OSC method evaluation completed for 0x%p: " 224 "STATUS 0x%x SUPPORT 0x%x CONTROL req 0x%x, CONTROL ret 0x%x\n", 225 (void *)dip, rbuf[0], rbuf[1], tmp_ctrl, rbuf[2]); 226 227 AcpiOsFree(rb.Pointer); 228 229 return (AE_OK); 230 } 231 232 /* 233 * Checks if _OSC method has been called for this device. 234 */ 235 boolean_t 236 pcie_is_osc(dev_info_t *dip) 237 { 238 pcie_bus_t *bus_p = PCIE_DIP2BUS(dip); 239 pcie_x86_priv_t *osc_p = (pcie_x86_priv_t *)bus_p->bus_plat_private; 240 return (osc_p->bus_osc); 241 } 242 243 #ifdef DEBUG 244 static void 245 pcie_dump_acpi_obj(ACPI_HANDLE pcibus_obj) 246 { 247 int status; 248 ACPI_BUFFER retbuf; 249 250 if (pcibus_obj == NULL) 251 return; 252 253 /* print the full path name */ 254 retbuf.Pointer = NULL; 255 retbuf.Length = ACPI_ALLOCATE_BUFFER; 256 status = AcpiGetName(pcibus_obj, ACPI_FULL_PATHNAME, &retbuf); 257 if (status != AE_OK) 258 return; 259 PCIE_DBG("PCIE BUS PATHNAME: %s\n", (char *)retbuf.Pointer); 260 AcpiOsFree(retbuf.Pointer); 261 262 /* dump all the methods for this bus node */ 263 PCIE_DBG(" METHODS: \n"); 264 status = AcpiWalkNamespace(ACPI_TYPE_METHOD, pcibus_obj, 1, 265 pcie_print_acpi_name, " ", NULL); 266 /* dump all the child devices */ 267 status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, pcibus_obj, 1, 268 pcie_walk_obj_namespace, NULL, NULL); 269 } 270 271 /*ARGSUSED*/ 272 static ACPI_STATUS 273 pcie_walk_obj_namespace(ACPI_HANDLE hdl, uint32_t nl, void *context, 274 void **ret) 275 { 276 int status; 277 ACPI_BUFFER retbuf; 278 char buf[32]; 279 280 /* print the full path name */ 281 retbuf.Pointer = NULL; 282 retbuf.Length = ACPI_ALLOCATE_BUFFER; 283 status = AcpiGetName(hdl, ACPI_FULL_PATHNAME, &retbuf); 284 if (status != AE_OK) 285 return (status); 286 buf[0] = 0; 287 while (nl--) 288 (void) strcat(buf, " "); 289 PCIE_DBG("%sDEVICE: %s\n", buf, (char *)retbuf.Pointer); 290 AcpiOsFree(retbuf.Pointer); 291 292 /* dump all the methods for this device */ 293 PCIE_DBG("%s METHODS: \n", buf); 294 status = AcpiWalkNamespace(ACPI_TYPE_METHOD, hdl, 1, 295 pcie_print_acpi_name, (void *)buf, NULL); 296 return (status); 297 } 298 299 /*ARGSUSED*/ 300 static ACPI_STATUS 301 pcie_print_acpi_name(ACPI_HANDLE hdl, uint32_t nl, void *context, void **ret) 302 { 303 int status; 304 ACPI_BUFFER retbuf; 305 char name[16]; 306 307 retbuf.Pointer = name; 308 retbuf.Length = 16; 309 status = AcpiGetName(hdl, ACPI_SINGLE_NAME, &retbuf); 310 if (status == AE_OK) 311 PCIE_DBG("%s %s \n", (char *)context, name); 312 return (AE_OK); 313 } 314 #endif /* DEBUG */ 315