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