1 /* $NetBSD: acpi_pci.c,v 1.4 2010/03/09 18:15:22 jruoho Exp $ */ 2 3 /* 4 * Copyright (c) 2009 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Christoph Egger. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 /* 32 * ACPI PCI Bus 33 */ 34 35 #include <sys/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: acpi_pci.c,v 1.4 2010/03/09 18:15:22 jruoho Exp $"); 37 38 #include <sys/param.h> 39 #include <sys/device.h> 40 #include <sys/kmem.h> 41 #include <sys/queue.h> 42 #include <sys/systm.h> 43 44 #include <dev/acpi/acpireg.h> 45 #include <dev/acpi/acpivar.h> 46 #include <dev/acpi/acpi_pci.h> 47 48 struct acpi_pcidev; 49 50 static TAILQ_HEAD(, acpi_pcidev) acpi_pcidevlist = 51 TAILQ_HEAD_INITIALIZER(acpi_pcidevlist); 52 53 struct acpi_pcidev { 54 struct acpi_devnode *ap_node; 55 uint32_t ap_pciseg; 56 uint32_t ap_pcibus; 57 uint32_t ap_pcidev; 58 uint32_t ap_pcifunc; 59 bool ap_pcihost; 60 TAILQ_ENTRY(acpi_pcidev) ap_list; 61 }; 62 63 64 static const char * const acpi_pcidev_ids[] = { 65 "PNP0A??", /* ACPI PCI host controllers */ 66 NULL 67 }; 68 69 static bool 70 acpi_pcidev_add(struct acpi_softc *sc, struct acpi_devnode *ad) 71 { 72 struct acpi_pcidev *ap; 73 ACPI_STATUS rv; 74 ACPI_INTEGER seg, bus, addr; 75 76 /* 77 * ACPI spec: "The _BBN object is located under a 78 * PCI host bridge and must be unique for every 79 * host bridge within a segment since it is the PCI bus number." 80 */ 81 rv = acpi_eval_integer(ad->ad_handle, "_BBN", &bus); 82 if (ACPI_FAILURE(rv)) 83 return false; 84 /* 85 * The ACPI address (_ADR) is equal to: (device << 16) | function. 86 */ 87 rv = acpi_eval_integer(ad->ad_handle, "_ADR", &addr); 88 if (ACPI_FAILURE(rv)) 89 return false; 90 91 /* 92 * ACPI spec: "The optional _SEG object is located under a PCI host 93 * bridge and evaluates to an integer that describes the 94 * PCI Segment Group (see PCI Firmware Specification v3.0)." 95 * 96 * "PCI Segment Group supports more than 256 buses 97 * in a system by allowing the reuse of the PCI bus numbers. 98 * Within each PCI Segment Group, the bus numbers for the PCI 99 * buses must be unique. PCI buses in different PCI Segment 100 * Group are permitted to have the same bus number." 101 */ 102 rv = acpi_eval_integer(ad->ad_handle, "_SEG", &seg); 103 if (ACPI_FAILURE(rv)) { 104 /* 105 * ACPI spec: "If _SEG does not exist, OSPM assumes that all 106 * PCI bus segments are in PCI Segment Group 0." 107 */ 108 seg = 0; 109 } 110 111 ap = kmem_alloc(sizeof(*ap), KM_SLEEP); 112 if (ap == NULL) { 113 aprint_error("%s: kmem_alloc failed\n", __func__); 114 return false; 115 } 116 117 if (acpi_match_hid(ad->ad_devinfo, acpi_pcidev_ids)) 118 ap->ap_pcihost = true; 119 else 120 ap->ap_pcihost = false; 121 122 ap->ap_node = ad; 123 ap->ap_pciseg = seg; 124 ap->ap_pcibus = bus; 125 ap->ap_pcidev = addr >> 16; 126 ap->ap_pcifunc = addr & 0xffff; 127 128 TAILQ_INSERT_TAIL(&acpi_pcidevlist, ap, ap_list); 129 130 return true; 131 } 132 133 static void 134 acpi_pcidev_print(struct acpi_pcidev *ap) 135 { 136 aprint_debug(" %s", ap->ap_node->ad_name); 137 } 138 139 int 140 acpi_pcidev_scan(struct acpi_softc *sc) 141 { 142 struct acpi_devnode *ad; 143 struct acpi_pcidev *ap; 144 ACPI_DEVICE_INFO *di; 145 int count = 0; 146 147 #define ACPI_STA_DEV_VALID \ 148 (ACPI_STA_DEV_PRESENT|ACPI_STA_DEV_ENABLED|ACPI_STA_DEV_OK) 149 150 SIMPLEQ_FOREACH(ad, &sc->sc_devnodes, ad_list) { 151 152 di = ad->ad_devinfo; 153 154 if (di->Type != ACPI_TYPE_DEVICE) 155 continue; 156 157 if ((di->Valid & ACPI_VALID_STA) != 0 && 158 (di->CurrentStatus & ACPI_STA_DEV_VALID) != 159 ACPI_STA_DEV_VALID) 160 continue; 161 162 if (acpi_pcidev_add(sc, ad) == true) 163 ++count; 164 } 165 166 #undef ACPI_STA_DEV_VALID 167 168 if (count == 0) 169 return 0; 170 171 aprint_debug_dev(sc->sc_dev, "pci devices:"); 172 TAILQ_FOREACH(ap, &acpi_pcidevlist, ap_list) 173 acpi_pcidev_print(ap); 174 aprint_debug("\n"); 175 176 return count; 177 } 178 179 /* 180 * acpi_pcidev_find: 181 * 182 * Finds a PCI device in the ACPI name space. 183 * The return status is either: 184 * - AE_NOT_FOUND if no such device was found. 185 * - AE_OK if one and only one such device was found. 186 */ 187 ACPI_STATUS 188 acpi_pcidev_find(u_int segment, u_int bus, u_int device, u_int function, 189 ACPI_HANDLE *handlep) 190 { 191 struct acpi_pcidev *ap; 192 ACPI_HANDLE hdl; 193 194 hdl = NULL; 195 TAILQ_FOREACH(ap, &acpi_pcidevlist, ap_list) { 196 if (ap->ap_pciseg != segment) 197 continue; 198 if (ap->ap_pcibus != bus) 199 continue; 200 if (ap->ap_pcidev != device) 201 continue; 202 if (ap->ap_pcifunc != function) 203 continue; 204 205 hdl = ap->ap_node->ad_handle; 206 break; 207 } 208 209 *handlep = hdl; 210 return (hdl != NULL) ? AE_OK : AE_NOT_FOUND; 211 } 212