1 /* $NetBSD: acpi_pci.c,v 1.3 2010/03/05 14:00:17 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.3 2010/03/05 14:00:17 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_scope *as; 143 struct acpi_devnode *ad; 144 struct acpi_pcidev *ap; 145 ACPI_DEVICE_INFO *di; 146 int count = 0; 147 148 #define ACPI_STA_DEV_VALID \ 149 (ACPI_STA_DEV_PRESENT|ACPI_STA_DEV_ENABLED|ACPI_STA_DEV_OK) 150 151 TAILQ_FOREACH(as, &sc->sc_scopes, as_list) { 152 TAILQ_FOREACH(ad, &as->as_devnodes, ad_list) { 153 di = ad->ad_devinfo; 154 if (di->Type != ACPI_TYPE_DEVICE) 155 continue; 156 if ((di->Valid & ACPI_VALID_STA) != 0 && 157 (di->CurrentStatus & ACPI_STA_DEV_VALID) != 158 ACPI_STA_DEV_VALID) 159 continue; 160 if (acpi_pcidev_add(sc, ad) == true) 161 ++count; 162 } 163 } 164 165 #undef ACPI_STA_DEV_VALID 166 167 if (count == 0) 168 return 0; 169 170 aprint_debug_dev(sc->sc_dev, "pci devices:"); 171 TAILQ_FOREACH(ap, &acpi_pcidevlist, ap_list) 172 acpi_pcidev_print(ap); 173 aprint_debug("\n"); 174 175 return count; 176 } 177 178 /* 179 * acpi_pcidev_find: 180 * 181 * Finds a PCI device in the ACPI name space. 182 * The return status is either: 183 * - AE_NOT_FOUND if no such device was found. 184 * - AE_OK if one and only one such device was found. 185 */ 186 ACPI_STATUS 187 acpi_pcidev_find(u_int segment, u_int bus, u_int device, u_int function, 188 ACPI_HANDLE *handlep) 189 { 190 struct acpi_pcidev *ap; 191 ACPI_HANDLE hdl; 192 193 hdl = NULL; 194 TAILQ_FOREACH(ap, &acpi_pcidevlist, ap_list) { 195 if (ap->ap_pciseg != segment) 196 continue; 197 if (ap->ap_pcibus != bus) 198 continue; 199 if (ap->ap_pcidev != device) 200 continue; 201 if (ap->ap_pcifunc != function) 202 continue; 203 204 hdl = ap->ap_node->ad_handle; 205 break; 206 } 207 208 *handlep = hdl; 209 return (hdl != NULL) ? AE_OK : AE_NOT_FOUND; 210 } 211