ofw_pci.c revision 98148
1/* 2 * Copyright (c) 1999, 2000 Matthew R. Green 3 * All rights reserved. 4 * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * from: NetBSD: psycho.c,v 1.35 2001/09/10 16:17:06 eeh Exp 30 * 31 * $FreeBSD: head/sys/sparc64/pci/ofw_pci.c 98148 2002-06-12 19:20:57Z tmm $ 32 */ 33 34#include "opt_ofw_pci.h" 35 36#include <sys/param.h> 37#include <sys/kernel.h> 38#include <sys/systm.h> 39#include <sys/bus.h> 40 41#include <dev/pci/pcivar.h> 42#include <dev/pci/pcireg.h> 43 44#include <dev/ofw/ofw_pci.h> 45#include <dev/ofw/openfirm.h> 46 47#include <sparc64/pci/ofw_pci.h> 48 49#include <machine/ofw_bus.h> 50 51#include "pcib_if.h" 52 53u_int8_t pci_bus_cnt; 54phandle_t *pci_bus_map; 55int pci_bus_map_sz; 56 57#define PCI_BUS_MAP_INC 10 58 59u_int32_t 60ofw_pci_route_intr(phandle_t node) 61{ 62 u_int32_t rv; 63 64 rv = ofw_bus_route_intr(node, ORIP_NOINT); 65 if (rv == ORIR_NOTFOUND) 66 return (255); 67 return (rv); 68} 69 70u_int8_t 71ofw_pci_alloc_busno(phandle_t node) 72{ 73 phandle_t *om; 74 int osz; 75 u_int8_t n; 76 77 n = pci_bus_cnt++; 78 /* Establish a mapping between bus numbers and device nodes. */ 79 if (n >= pci_bus_map_sz) { 80 osz = pci_bus_map_sz; 81 om = pci_bus_map; 82 pci_bus_map_sz = n + PCI_BUS_MAP_INC; 83 pci_bus_map = malloc(sizeof(*pci_bus_map) * pci_bus_map_sz, 84 M_DEVBUF, M_WAITOK | M_ZERO); 85 if (om != NULL) { 86 bcopy(om, pci_bus_map, sizeof(*om) * osz); 87 free(om, M_DEVBUF); 88 } 89 } 90 pci_bus_map[n] = node; 91 return (n); 92} 93 94/* 95 * Initialize bridge bus numbers for bridges that implement the primary, 96 * secondary and subordinate bus number registers. 97 */ 98void 99ofw_pci_binit(device_t busdev, struct ofw_pci_bdesc *obd) 100{ 101 102#ifdef OFW_PCI_DEBUG 103 printf("PCI-PCI bridge at %u/%u/%u: setting bus #s to %u/%u/%u\n", 104 obd->obd_bus, obd->obd_slot, obd->obd_func, obd->obd_bus, 105 obd->obd_secbus, obd->obd_subbus); 106#endif /* OFW_PCI_DEBUG */ 107 PCIB_WRITE_CONFIG(busdev, obd->obd_bus, obd->obd_slot, obd->obd_func, 108 PCIR_PRIBUS_1, obd->obd_bus, 1); 109 PCIB_WRITE_CONFIG(busdev, obd->obd_bus, obd->obd_slot, obd->obd_func, 110 PCIR_SECBUS_1, obd->obd_secbus, 1); 111 PCIB_WRITE_CONFIG(busdev, obd->obd_bus, obd->obd_slot, obd->obd_func, 112 PCIR_SUBBUS_1, obd->obd_subbus, 1); 113} 114 115#define OFW_PCI_PCIBUS "pci" 116/* 117 * Walk the PCI bus hierarchy, starting with the root PCI bus and descending 118 * through bridges, and initialize the interrupt line and latency timer 119 * configuration registers of attached devices using firmware information, 120 * as well as the the bus numbers and ranges of the bridges. 121 */ 122void 123ofw_pci_init(device_t dev, phandle_t bushdl, struct ofw_pci_bdesc *obd) 124{ 125 struct ofw_pci_register pcir; 126 struct ofw_pci_bdesc subobd, *tobd; 127 phandle_t node; 128 char type[32]; 129 int intr, freemap; 130 u_int slot, busno, func, sub, lat; 131 132 if ((node = OF_child(bushdl)) == 0) 133 return; 134 freemap = 0; 135 busno = obd->obd_secbus; 136 do { 137 if (node == -1) 138 panic("ofw_pci_init_intr: OF_child failed"); 139 if (OF_getprop(node, "device_type", type, sizeof(type)) == -1) 140 type[0] = '\0'; 141 else 142 type[sizeof(type) - 1] = '\0'; 143 if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1) 144 panic("ofw_pci_route_intr: OF_getprop failed"); 145 slot = OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi); 146 func = OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi); 147 if (strcmp(type, OFW_PCI_PCIBUS) == 0) { 148 /* 149 * This is a pci-pci bridge, initalize the bus number and 150 * recurse to initialize the child bus. The hierarchy is 151 * usually at most 2 levels deep, so recursion is 152 * feasible. 153 */ 154 subobd.obd_bus = busno; 155 subobd.obd_slot = slot; 156 subobd.obd_func = func; 157 sub = ofw_pci_alloc_busno(node); 158 subobd.obd_secbus = subobd.obd_subbus = sub; 159 /* Assume this bridge is mostly standard conforming. */ 160 subobd.obd_init = ofw_pci_binit; 161 subobd.obd_super = obd; 162 /* 163 * Need to change all subordinate bus registers of the 164 * bridges above this one now so that configuration 165 * transactions will get through. 166 */ 167 for (tobd = obd; tobd != NULL; tobd = tobd->obd_super) { 168 tobd->obd_subbus = sub; 169 tobd->obd_init(dev, tobd); 170 } 171 subobd.obd_init(dev, &subobd); 172#ifdef OFW_PCI_DEBUG 173 device_printf(dev, "%s: descending to " 174 "subordinate PCI bus\n", __func__); 175#endif /* OFW_PCI_DEBUG */ 176 ofw_pci_init(dev, node, &subobd); 177 } else { 178 /* 179 * Initialize the latency timer register for 180 * busmaster devices to work properly. This is another 181 * task which the firmware does not always perform. 182 * The Min_Gnt register can be used to compute it's 183 * recommended value: it contains the desired latency 184 * in units of 1/4 us. To calculate the correct latency 185 * timer value, a bus clock of 33 and no wait states 186 * should be assumed. 187 */ 188 lat = PCIB_READ_CONFIG(dev, busno, slot, func, 189 PCIR_MINGNT, 1) * 33 / 4; 190 if (lat != 0) { 191#ifdef OFW_PCI_DEBUG 192 printf("device %d/%d/%d: latency timer %d -> " 193 "%d\n", busno, slot, func, 194 PCIB_READ_CONFIG(dev, busno, slot, func, 195 PCIR_LATTIMER, 1), lat); 196#endif /* OFW_PCI_DEBUG */ 197 PCIB_WRITE_CONFIG(dev, busno, slot, func, 198 PCIR_LATTIMER, imin(lat, 255), 1); 199 } 200 201 /* Initialize the intline registers. */ 202 if ((intr = ofw_pci_route_intr(node)) != 255) { 203#ifdef OFW_PCI_DEBUG 204 device_printf(dev, "%s: mapping intr for " 205 "%d/%d/%d to %d (preset was %d)\n", 206 __func__, busno, slot, func, intr, 207 (int)PCIB_READ_CONFIG(dev, busno, slot, 208 func, PCIR_INTLINE, 1)); 209#endif /* OFW_PCI_DEBUG */ 210 PCIB_WRITE_CONFIG(dev, busno, slot, func, 211 PCIR_INTLINE, intr, 1); 212 } else { 213#ifdef OFW_PCI_DEBUG 214 device_printf(dev, "%s: no interrupt " 215 "mapping found for %d/%d/%d (preset %d)\n", 216 __func__, busno, slot, func, 217 (int)PCIB_READ_CONFIG(dev, busno, slot, 218 func, PCIR_INTLINE, 1)); 219#endif /* OFW_PCI_DEBUG */ 220 /* 221 * The firmware initializes to 0 instead of 222 * 255. 223 */ 224 PCIB_WRITE_CONFIG(dev, busno, slot, func, 225 PCIR_INTLINE, 255, 1); 226 } 227 } 228 } while ((node = OF_peer(node)) != 0); 229} 230 231phandle_t 232ofw_pci_find_node(int bus, int slot, int func) 233{ 234 phandle_t node, bnode; 235 struct ofw_pci_register pcir; 236 237 /* 238 * Retrieve the bus node from the mapping that was created on 239 * initialization. The bus numbers the firmware uses cannot be trusted, 240 * so they might have needed to be changed and this is necessary. 241 */ 242 if (bus >= pci_bus_map_sz) 243 return (0); 244 bnode = pci_bus_map[bus]; 245 if (bnode == 0) 246 return (0); 247 for (node = OF_child(bnode); node != 0 && node != -1; 248 node = OF_peer(node)) { 249 if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1) 250 continue; 251 if (OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi) == slot && 252 OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi) == func) 253 return (node); 254 } 255 return (0); 256} 257 258phandle_t 259ofw_pci_node(device_t dev) 260{ 261 262 return (ofw_pci_find_node(pci_get_bus(dev), pci_get_slot(dev), 263 pci_get_function(dev))); 264} 265