Home | History | Annotate | Line # | Download | only in pci
pci_machdep_ofw.c revision 1.3
      1 /* $NetBSD: pci_machdep_ofw.c,v 1.3 2007/10/25 16:55:51 garbled Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2007 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Tim Rightnour
      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. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *        This product includes software developed by the NetBSD
     21  *        Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 /*
     40  * Generic OFW routines for pci_machdep
     41  */
     42 
     43 #include <sys/cdefs.h>
     44 __KERNEL_RCSID(0, "$NetBSD: pci_machdep_ofw.c,v 1.3 2007/10/25 16:55:51 garbled Exp $");
     45 
     46 #include <sys/types.h>
     47 #include <sys/param.h>
     48 #include <sys/time.h>
     49 #include <sys/systm.h>
     50 #include <sys/errno.h>
     51 #include <sys/device.h>
     52 #include <sys/malloc.h>
     53 
     54 #include <uvm/uvm_extern.h>
     55 
     56 #include <machine/bus.h>
     57 
     58 #include <machine/autoconf.h>
     59 #include <machine/pio.h>
     60 #include <machine/intr.h>
     61 
     62 #include <dev/pci/pcivar.h>
     63 #include <dev/pci/pcireg.h>
     64 #include <dev/pci/ppbreg.h>
     65 #include <dev/pci/pcidevs.h>
     66 #include <dev/pci/pciconf.h>
     67 
     68 #include <dev/ofw/openfirm.h>
     69 #include <dev/ofw/ofw_pci.h>
     70 
     71 pcitag_t genppc_pci_indirect_make_tag(void *, int, int, int);
     72 void genppc_pci_indirect_decompose_tag(void *, pcitag_t, int *, int *, int *);
     73 
     74 ofw_pic_node_t picnodes[8];
     75 int nrofpics = 0;
     76 
     77 int
     78 genofw_find_picnode(int node)
     79 {
     80 	int i;
     81 
     82 	for (i = 0; i < 8; i++)
     83 		if (node == picnodes[i].node)
     84 			return i;
     85 	return -1;
     86 }
     87 
     88 void
     89 genofw_find_ofpics(int startnode)
     90 {
     91 	int node, iparent, child, iranges[2];
     92 	char name[32];
     93 
     94 	for (node = startnode; node; node = OF_peer(node)) {
     95 		if ((child = OF_child(node)) != 0)
     96 			genofw_find_ofpics(child);
     97 		memset(name, 0, sizeof(name));
     98 		if (OF_getprop(node, "name", name, sizeof(name)) == -1)
     99 			continue;
    100 		if (strncmp(name, "interrupt-controller", 20) == 0)
    101 			goto foundic;
    102 
    103 		if (OF_getprop(node, "interrupt-controller", name,
    104 		    sizeof(name)) > -1)
    105 			goto foundic;
    106 
    107 		if (OF_getprop(node, "device_type", name, sizeof(name)) == -1)
    108 			continue;
    109 		if (strncmp(name, "interrupt-controller", 20) == 0)
    110 			goto foundic;
    111 
    112 		/* if we didn't find one, skip to the next */
    113 		continue;
    114 foundic:
    115 		picnodes[nrofpics].node = node;
    116 		if (OF_getprop(node, "interrupt-parent", &iparent,
    117 		    sizeof(iparent)) == sizeof(iparent))
    118 			picnodes[nrofpics].parent = iparent;
    119 		if (OF_getprop(node, "#interrupt-cells", &iparent,
    120 		    sizeof(iparent)) == sizeof(iparent))
    121 			picnodes[nrofpics].cells = iparent;
    122 		else
    123 			picnodes[nrofpics].cells = 1;
    124 		if (OF_getprop(node, "interrupt-ranges", iranges,
    125 		    sizeof(int)*2) == sizeof(int)*2)
    126 			picnodes[nrofpics].intrs = iranges[1];
    127 		else
    128 			picnodes[nrofpics].intrs = 16;
    129 		if (nrofpics > 0)
    130 			picnodes[nrofpics].offset = picnodes[nrofpics-1].offset
    131 			    + picnodes[nrofpics-1].intrs;
    132 		else
    133 			picnodes[nrofpics].offset = 0;
    134 		OF_getprop(node, "device_type", name, sizeof(name));
    135 		if (strcmp(name, "open-pic") == 0)
    136 			picnodes[nrofpics].type = PICNODE_TYPE_OPENPIC;
    137 		if (strcmp(name, "interrupt-controller") == 0) {
    138 			OF_getprop(node, "compatible", name, sizeof(name));
    139 			if (strcmp(name, "heathrow") != 0)
    140 				picnodes[nrofpics].type = PICNODE_TYPE_HEATHROW;
    141 			if (strcmp(name, "chrp,iic") != 0)
    142 				picnodes[nrofpics].type = PICNODE_TYPE_8259;
    143 		}
    144 		if (strlen(name) == 0) {
    145 			/* probably a Pegasos, assume 8259 */
    146 			picnodes[nrofpics].type = PICNODE_TYPE_8259;
    147 		}
    148 		nrofpics++;
    149 	}
    150 }
    151 
    152 /* Fix up the various picnode offsets */
    153 void
    154 genofw_fixup_picnode_offsets(void)
    155 {
    156 	int i, curoff;
    157 
    158 	curoff=0;
    159 
    160 	for (i=0; i < nrofpics; i++) {
    161 		if (picnodes[i].type == PICNODE_TYPE_8259) {
    162 			picnodes[i].offset = 0;
    163 			curoff = picnodes[i].intrs;
    164 		}
    165 	}
    166 	for (i=0; i < nrofpics; i++) {
    167 		/* now skip the 8259 */
    168 		if (picnodes[i].type == PICNODE_TYPE_8259)
    169 			continue;
    170 		picnodes[i].offset = curoff;
    171 		curoff += picnodes[i].intrs;
    172 	}
    173 }
    174 
    175 /* we are given a pci devnode, and dig from there */
    176 void
    177 genofw_setup_pciintr_map(struct genppc_pci_chipset_businfo *pbi, int pcinode)
    178 {
    179 	int node;
    180 	u_int32_t map[160];
    181 	int parent, len;
    182 	int curdev;
    183 	int i, reclen, nrofpcidevs=0;
    184 	u_int32_t acells, icells, pcells;
    185 	prop_dictionary_t dict;
    186 	prop_dictionary_t sub=0;
    187 
    188 	len = OF_getprop(pcinode, "interrupt-map", map, sizeof(map));
    189 	if (len == -1)
    190 		goto nomap;
    191 
    192 	if (OF_getprop(pcinode, "#address-cells", &acells,
    193 	    sizeof(acells)) == -1)
    194 		acells = 1;
    195 	if (OF_getprop(pcinode, "#interrupt-cells", &icells,
    196 	    sizeof(icells)) == -1)
    197 		icells = 1;
    198 
    199 	parent = map[acells+icells+1];
    200 	if (OF_getprop(parent, "#interrupt-cells", &pcells,
    201 	    sizeof(pcells)) == -1)
    202 		pcells = 1;
    203 
    204 	reclen = acells+pcells+icells+1;
    205 	nrofpcidevs = len / (reclen * sizeof(int));
    206 
    207 	dict = prop_dictionary_create_with_capacity(nrofpcidevs*2);
    208 	KASSERT(dict != NULL);
    209 
    210 	curdev = -1;
    211 	prop_dictionary_set(pbi->pbi_properties, "ofw-pci-intrmap", dict);
    212 	for (i = 0; i < nrofpcidevs; i++) {
    213 		prop_number_t intr_num;
    214 		int dev, pin, pic;
    215 		char key[20];
    216 
    217 		pic = genofw_find_picnode(map[i*reclen + acells + icells]);
    218 		KASSERT(pic != -1);
    219 		dev = (map[i*reclen] >> 8) / 0x8;
    220 		if (curdev != dev)
    221 			sub = prop_dictionary_create_with_capacity(4);
    222 		pin = map[i*reclen + acells];
    223 		intr_num = prop_number_create_integer(map[i*reclen + acells + icells + 1] + picnodes[pic].offset);
    224 		sprintf(key, "pin-%c", 'A' + pin);
    225 		prop_dictionary_set(sub, key, intr_num);
    226 		prop_object_release(intr_num);
    227 		/* should we care about level? */
    228 
    229 		sprintf(key, "devfunc-%d", dev);
    230 		prop_dictionary_set(dict, key, sub);
    231 		if (curdev != dev) {
    232 			prop_object_release(sub);
    233 			curdev = dev;
    234 		}
    235 	}
    236 	/* the mapping is complete */
    237 	prop_object_release(dict);
    238 	return;
    239 
    240 nomap:
    241 	/* so, we have one of those annoying machines that doesn't provide
    242 	 * a nice simple map of interrupts.  We get to do this the hard
    243 	 * way instead.  Lucky us.
    244 	 */
    245 	for (node = OF_child(pcinode), nrofpcidevs=0; node;
    246 	    node = OF_peer(node))
    247 		nrofpcidevs++;
    248 	dict = prop_dictionary_create_with_capacity(nrofpcidevs*2);
    249 	KASSERT(dict != NULL);
    250 	prop_dictionary_set(pbi->pbi_properties, "ofw-pci-intrmap", dict);
    251 
    252 	for (node = OF_child(pcinode); node; node = OF_peer(node)) {
    253 		uint32_t irqs[4], reg[5];
    254 		prop_number_t intr_num;
    255 		int dev, pin;
    256 		char key[20];
    257 
    258 		/* walk the bus looking for pci devices and map them */
    259 		if (OF_getprop(node, "AAPL,interrupts", irqs, 4) > 0) {
    260 			dev = 0;
    261 			if (OF_getprop(node, "reg", reg, 5) > 0)
    262 				dev = ((reg[0] & 0x0000ff00) >> 8) / 0x8;
    263 			else if (OF_getprop(node, "assigned-addresses",
    264 				     reg, 5) > 0)
    265 				dev = ((reg[0] & 0x0000ff00) >> 8) / 0x8;
    266 			if (dev == 0) {
    267 				aprint_error("cannot figure out device num "
    268 				    "for node 0x%x\n", node);
    269 				continue;
    270 			}
    271 			sub = prop_dictionary_create_with_capacity(4);
    272 			if (OF_getprop(node, "interrupts", &pin, 4) < 0)
    273 				pin = 1;
    274 			intr_num = prop_number_create_integer(irqs[0]);
    275 			sprintf(key, "pin-%c", 'A' + pin);
    276 			prop_dictionary_set(sub, key, intr_num);
    277 			prop_object_release(intr_num);
    278 			sprintf(key, "devfunc-%d", dev);
    279 			prop_dictionary_set(dict, key, sub);
    280 			prop_object_release(sub);
    281 		}
    282 	}
    283 	aprint_normal("%s\n", prop_dictionary_externalize(pbi->pbi_properties));
    284 }
    285 
    286 int
    287 genofw_find_node_by_devfunc(int startnode, int bus, int dev, int func)
    288 {
    289 	int node, sz, p=0;
    290 	uint32_t reg;
    291 
    292 	for (node = startnode; node; node = p) {
    293 		sz = OF_getprop(node, "reg", &reg, sizeof(reg));
    294 		if (sz != sizeof(reg))
    295 			continue;
    296 		if (OFW_PCI_PHYS_HI_BUS(reg) == bus &&
    297 		    OFW_PCI_PHYS_HI_DEVICE(reg) == dev &&
    298 		    OFW_PCI_PHYS_HI_FUNCTION(reg) == func)
    299 			return node;
    300 		if ((p = OF_child(node)))
    301 			continue;
    302 		while (node) {
    303 			if ((p = OF_peer(node)))
    304 				break;
    305 			node = OF_parent(node);
    306 		}
    307 	}
    308 	/* couldn't find it */
    309 	return -1;
    310 }
    311 
    312 int
    313 genofw_pci_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
    314 {
    315 	struct genppc_pci_chipset_businfo *pbi;
    316 	prop_dictionary_t dict, devsub;
    317 	prop_object_t pinsub;
    318 	prop_number_t pbus;
    319 	int busno, bus, pin, line, swiz, dev, origdev, i;
    320 	char key[20];
    321 
    322 	pin = pa->pa_intrpin;
    323 	line = pa->pa_intrline;
    324 	bus = busno = pa->pa_bus;
    325 	swiz = pa->pa_intrswiz;
    326 	origdev = dev = pa->pa_device;
    327 	i = 0;
    328 
    329 	pbi = SIMPLEQ_FIRST(&pa->pa_pc->pc_pbi);
    330 	while (busno--)
    331 		pbi = SIMPLEQ_NEXT(pbi, next);
    332 	KASSERT(pbi != NULL);
    333 
    334 	dict = prop_dictionary_get(pbi->pbi_properties, "ofw-pci-intrmap");
    335 
    336 	if (dict != NULL)
    337 		i = prop_dictionary_count(dict);
    338 
    339 	if (dict == NULL || i == 0) {
    340 		/* We have an unmapped bus, now it gets hard */
    341 		pbus = prop_dictionary_get(pbi->pbi_properties,
    342 		    "ofw-pcibus-parent");
    343 		if (pbus == NULL)
    344 			goto bad;
    345 		busno = prop_number_integer_value(pbus);
    346 		pbus = prop_dictionary_get(pbi->pbi_properties,
    347 		    "ofw-pcibus-rawdevnum");
    348 		dev = prop_number_integer_value(pbus);
    349 
    350 		/* now that we know the parent bus, we need to find it's pbi */
    351 		pbi = SIMPLEQ_FIRST(&pa->pa_pc->pc_pbi);
    352 		while (busno--)
    353 			pbi = SIMPLEQ_NEXT(pbi, next);
    354 		KASSERT(pbi != NULL);
    355 
    356 		/* swizzle the pin */
    357 		pin = ((pin + origdev - 1) & 3) + 1;
    358 
    359 		/* now we have the pbi, ask for dict again */
    360 		dict = prop_dictionary_get(pbi->pbi_properties,
    361 		    "ofw-pci-intrmap");
    362 		if (dict == NULL)
    363 			goto bad;
    364 	}
    365 
    366 	/* No IRQ used. */
    367 	if (pin == 0)
    368 		goto bad;
    369 	if (pin > 4) {
    370 		aprint_error("pci_intr_map: bad interrupt pin %d\n", pin);
    371 		goto bad;
    372 	}
    373 
    374 	sprintf(key, "devfunc-%d", dev);
    375 	devsub = prop_dictionary_get(dict, key);
    376 	if (devsub == NULL)
    377 		goto bad;
    378 	sprintf(key, "pin-%c", 'A' + (pin-1));
    379 	pinsub = prop_dictionary_get(devsub, key);
    380 	if (pinsub == NULL)
    381 		goto bad;
    382 	line = prop_number_integer_value(pinsub);
    383 
    384 	if (line == 0 || line == 255) {
    385 		aprint_error("pci_intr_map: no mapping for pin %c\n",'@' + pin);
    386 		goto bad;
    387 	}
    388 
    389 	*ihp = line;
    390 	return 0;
    391 
    392 bad:
    393 	*ihp = -1;
    394 	return 1;
    395 }
    396 
    397 int
    398 genofw_pci_conf_hook(pci_chipset_tag_t pct, int bus, int dev, int func,
    399 	pcireg_t id)
    400 {
    401 	struct genppc_pci_chipset_businfo *pbi;
    402 	prop_number_t pbus;
    403 	pcitag_t tag;
    404 	pcireg_t class;
    405 	int node;
    406 
    407 	/* We have already mapped MPIC's if we have them, so leave them alone */
    408 	if (PCI_VENDOR(id) == PCI_VENDOR_IBM &&
    409 	    PCI_PRODUCT(id) == PCI_PRODUCT_IBM_MPIC2)
    410 		return 0;
    411 
    412 	if (PCI_VENDOR(id) == PCI_VENDOR_IBM &&
    413 	    PCI_PRODUCT(id) == PCI_PRODUCT_IBM_MPIC)
    414 		return 0;
    415 
    416 	/* I highly doubt there are any CHRP ravens, but just in case */
    417 	if (PCI_VENDOR(id) == PCI_VENDOR_MOT &&
    418 	    PCI_PRODUCT(id) == PCI_PRODUCT_MOT_RAVEN)
    419 		return (PCI_CONF_ALL & ~PCI_CONF_MAP_MEM);
    420 
    421 	/* NOTE, all device specific stuff must be above this line */
    422 	/* don't do this on the primary host bridge */
    423 	if (bus == 0 && dev == 0 && func == 0)
    424 		return PCI_CONF_DEFAULT;
    425 
    426 	tag = genppc_pci_indirect_make_tag(pct, bus, dev, func);
    427 	class = genppc_pci_indirect_conf_read(pct, tag, PCI_CLASS_REG);
    428 
    429 	/*
    430 	 * PCI bridges have special needs.  We need to discover where they
    431 	 * came from, and wire them appropriately.
    432 	 */
    433 	if (PCI_CLASS(class) == PCI_CLASS_BRIDGE &&
    434 	    PCI_SUBCLASS(class) == PCI_SUBCLASS_BRIDGE_PCI) {
    435 		pbi = malloc(sizeof(struct genppc_pci_chipset_businfo),
    436 		    M_DEVBUF, M_NOWAIT);
    437 		KASSERT(pbi != NULL);
    438 		pbi->pbi_properties = prop_dictionary_create();
    439 		KASSERT(pbi->pbi_properties != NULL);
    440 		node = genofw_find_node_by_devfunc(pct->pc_node, bus, dev,
    441 		    func);
    442 		if (node == -1) {
    443 			aprint_error("Cannot find node for device "
    444 			    "bus %d dev %d func %d\n", bus, dev, func);
    445 			prop_object_release(pbi->pbi_properties);
    446 			free(pbi, M_DEVBUF);
    447 			return (PCI_CONF_DEFAULT);
    448 		}
    449 		genofw_setup_pciintr_map(pbi, node);
    450 
    451 		/* record the parent bus, and the parent device number */
    452 		pbus = prop_number_create_integer(bus);
    453 		prop_dictionary_set(pbi->pbi_properties, "ofw-pcibus-parent",
    454 		    pbus);
    455 		prop_object_release(pbus);
    456 		pbus = prop_number_create_integer(dev);
    457 		prop_dictionary_set(pbi->pbi_properties, "ofw-pcibus-rawdevnum",
    458 		    pbus);
    459 		prop_object_release(pbus);
    460 
    461 		SIMPLEQ_INSERT_TAIL(&pct->pc_pbi, pbi, next);
    462 	}
    463 
    464 	return (PCI_CONF_DEFAULT);
    465 }
    466