ofw_pci.c revision 86231
186231Stmm/*
286231Stmm * Copyright (c) 1999, 2000 Matthew R. Green
386231Stmm * All rights reserved.
486231Stmm * Copyright 2001 by Thomas Moestl <tmm@FreeBSD.org>.  All rights reserved.
586231Stmm *
686231Stmm * Redistribution and use in source and binary forms, with or without
786231Stmm * modification, are permitted provided that the following conditions
886231Stmm * are met:
986231Stmm * 1. Redistributions of source code must retain the above copyright
1086231Stmm *    notice, this list of conditions and the following disclaimer.
1186231Stmm * 2. Redistributions in binary form must reproduce the above copyright
1286231Stmm *    notice, this list of conditions and the following disclaimer in the
1386231Stmm *    documentation and/or other materials provided with the distribution.
1486231Stmm * 3. The name of the author may not be used to endorse or promote products
1586231Stmm *    derived from this software without specific prior written permission.
1686231Stmm *
1786231Stmm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1886231Stmm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1986231Stmm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2086231Stmm * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2186231Stmm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2286231Stmm * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2386231Stmm * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2486231Stmm * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2586231Stmm * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2686231Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2786231Stmm * SUCH DAMAGE.
2886231Stmm *
2986231Stmm *	from: NetBSD: psycho.c,v 1.35 2001/09/10 16:17:06 eeh Exp
3086231Stmm *
3186231Stmm * $FreeBSD: head/sys/sparc64/pci/ofw_pci.c 86231 2001-11-09 20:19:58Z tmm $
3286231Stmm */
3386231Stmm
3486231Stmm#include "opt_ofw_pci.h"
3586231Stmm
3686231Stmm#include <sys/param.h>
3786231Stmm#include <sys/kernel.h>
3886231Stmm#include <sys/systm.h>
3986231Stmm#include <sys/bus.h>
4086231Stmm
4186231Stmm#include <dev/pci/pcivar.h>
4286231Stmm#include <dev/pci/pcireg.h>
4386231Stmm
4486231Stmm#include <dev/ofw/ofw_pci.h>
4586231Stmm#include <dev/ofw/openfirm.h>
4686231Stmm
4786231Stmm#include <sparc64/pci/ofw_pci.h>
4886231Stmm
4986231Stmm#include <machine/ofw_bus.h>
5086231Stmm
5186231Stmm#include "pcib_if.h"
5286231Stmm
5386231Stmm/*
5486231Stmm * Find the interrupt-map properties for a node. This might not be a property
5586231Stmm * of the parent, because there may be bridges in between, so go up through the
5686231Stmm * tree to find it.
5786231Stmm * This seems to be only needed for PCI systems, so it has not been moved to
5886231Stmm * ofw_bus.c
5986231Stmm */
6086231Stmmint
6186231Stmmofw_pci_find_imap(phandle_t node, struct ofw_pci_imap **imap,
6286231Stmm    struct ofw_pci_imap_msk *imapmsk)
6386231Stmm{
6486231Stmm	int nimap;
6586231Stmm
6686231Stmm	nimap = -1;
6786231Stmm	while ((node = OF_parent(node)) != 0) {
6886231Stmm		if ((nimap = OF_getprop_alloc(node, "interrupt-map",
6986231Stmm		    sizeof(**imap), (void **)imap)) == -1 ||
7086231Stmm		    OF_getprop(node, "interrupt-map-mask",
7186231Stmm		    imapmsk, sizeof(*imapmsk)) == -1) {
7286231Stmm			if (*imap != NULL) {
7386231Stmm				free(*imap, M_OFWPROP);
7486231Stmm				*imap = NULL;
7586231Stmm			}
7686231Stmm			nimap = -1;
7786231Stmm		} else
7886231Stmm			break;
7986231Stmm	}
8086231Stmm	return (nimap);
8186231Stmm}
8286231Stmm
8386231Stmm/*
8486231Stmm * Route an interrupt using the firmware nodes. Returns 255 for interrupts
8586231Stmm * that cannot be routed (suitable for the PCI code).
8686231Stmm */
8786231Stmmint
8886231Stmmofw_pci_route_intr2(int intr, struct ofw_pci_register *pcir,
8986231Stmm    struct ofw_pci_imap *imap, int nimap, struct ofw_pci_imap_msk *imapmsk)
9086231Stmm{
9186231Stmm	char regm[12];
9286231Stmm	int cintr;
9386231Stmm
9486231Stmm	cintr = ofw_bus_route_intr(intr, pcir, sizeof(*pcir), 12, 1, imap,
9586231Stmm	    nimap, imapmsk, regm);
9686231Stmm	if (cintr == -1)
9786231Stmm		return (255);
9886231Stmm	else
9986231Stmm		return (cintr);
10086231Stmm}
10186231Stmm
10286231Stmmint
10386231Stmmofw_pci_route_intr(phandle_t node, struct ofw_pci_register *pcir,
10486231Stmm    struct ofw_pci_imap *intrmap, int nintrmap,
10586231Stmm    struct ofw_pci_imap_msk *intrmapmsk)
10686231Stmm{
10786231Stmm	int intr;
10886231Stmm
10986231Stmm	if (OF_getprop(node, "interrupts", &intr, sizeof(intr)) == -1)
11086231Stmm		return (255);
11186231Stmm
11286231Stmm	return (ofw_pci_route_intr2(intr, pcir, intrmap, nintrmap, intrmapmsk));
11386231Stmm}
11486231Stmm
11586231Stmm#define	OFW_PCI_PCIBUS	"pci"
11686231Stmm/*
11786231Stmm * Walk the PCI bus hierarchy, starting with the root PCI bus and descending
11886231Stmm * through bridges, and initialize the interrupt line configuration registers
11986231Stmm * of attached devices using firmware information.
12086231Stmm */
12186231Stmmvoid
12286231Stmmofw_pci_init_intr(device_t dev, phandle_t bus, struct ofw_pci_imap *intrmap,
12386231Stmm    int nintrmap, struct ofw_pci_imap_msk *intrmapmsk)
12486231Stmm{
12586231Stmm	struct ofw_pci_imap_msk lintrmapmsk;
12686231Stmm	struct ofw_pci_register pcir;
12786231Stmm	phandle_t node;
12886231Stmm	char type[32];
12986231Stmm	int intr;
13086231Stmm	int freemap;
13186231Stmm
13286231Stmm	if ((node = OF_child(bus)) == 0)
13386231Stmm		return;
13486231Stmm	freemap = 0;
13586231Stmm	do {
13686231Stmm		if (node == -1)
13786231Stmm			panic("ofw_pci_init_intr: OF_child failed");
13886231Stmm		if (OF_getprop(node, "device_type", type, sizeof(type)) == -1)
13986231Stmm			type[0] = '\0';
14086231Stmm		else
14186231Stmm			type[sizeof(type) - 1] = '\0';
14286231Stmm		if (strcmp(type, OFW_PCI_PCIBUS) == 0) {
14386231Stmm			/*
14486231Stmm			 * This is a pci-pci bridge, recurse to initialize the
14586231Stmm			 * child bus. The hierarchy is usually at most 2 levels
14686231Stmm			 * deep, so recursion is feasible.
14786231Stmm			 */
14886231Stmm#ifdef OFW_PCI_DEBUG
14986231Stmm			device_printf(dev, __func__": descending to "
15086231Stmm			    "subordinate PCI bus\n");
15186231Stmm#endif
15286231Stmm			ofw_pci_init_intr(dev, node, NULL, 0, NULL);
15386231Stmm		} else {
15486231Stmm			if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1)
15586231Stmm				panic("ofw_pci_route_intr: OF_getprop failed");
15686231Stmm			/*
15786231Stmm			 * If we didn't get interrupt map properties passed,
15886231Stmm			 * try to find them now. On some systems, buses that
15986231Stmm			 * have no non-bridge children have no such properties,
16086231Stmm			 * so only try to find them at need.
16186231Stmm			 */
16286231Stmm			if (intrmap == NULL) {
16386231Stmm				nintrmap = OF_getprop_alloc(bus,
16486231Stmm				    "interrupt-map", sizeof(*intrmap),
16586231Stmm				    (void **)&intrmap);
16686231Stmm				if (nintrmap == -1 ||
16786231Stmm				    OF_getprop(bus, "interrupt-map-mask",
16886231Stmm				    &lintrmapmsk, sizeof(lintrmapmsk)) == -1) {
16986231Stmm					panic("ofw_pci_init_intr: could not get "
17086231Stmm					    "interrupt map properties");
17186231Stmm				}
17286231Stmm				intrmapmsk = &lintrmapmsk;
17386231Stmm				freemap = 1;
17486231Stmm			}
17586231Stmm			if ((intr = ofw_pci_route_intr(node, &pcir, intrmap,
17686231Stmm			    nintrmap, intrmapmsk)) != 255) {
17786231Stmm#ifdef OFW_PCI_DEBUG
17886231Stmm				device_printf(dev, __func__": mapping intr for "
17986231Stmm				    "%d/%d/%d to %d (preset was %d)\n",
18086231Stmm				    OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
18186231Stmm				    OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
18286231Stmm				    OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
18386231Stmm				    intr,
18486231Stmm				    (int)PCIB_READ_CONFIG(dev,
18586231Stmm					OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
18686231Stmm					OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
18786231Stmm					OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
18886231Stmm					PCIR_INTLINE, 1));
18986231Stmm
19086231Stmm#endif /* OFW_PCI_DEBUG */
19186231Stmm				PCIB_WRITE_CONFIG(dev,
19286231Stmm				    OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
19386231Stmm				    OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
19486231Stmm				    OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
19586231Stmm				    PCIR_INTLINE, intr, 1);
19686231Stmm			} else {
19786231Stmm#ifdef OFW_PCI_DEBUG
19886231Stmm				device_printf(dev, __func__": no interrupt "
19986231Stmm				    "mapping found for %d/%d/%d (preset %d)\n",
20086231Stmm				    OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
20186231Stmm				    OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
20286231Stmm				    OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
20386231Stmm				    (int)PCIB_READ_CONFIG(dev,
20486231Stmm					OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
20586231Stmm					OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
20686231Stmm					OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
20786231Stmm					PCIR_INTLINE, 1));
20886231Stmm#endif /* OFW_PCI_DEBUG */
20986231Stmm				/* The firmware initializes to 0 instead 255 */
21086231Stmm				PCIB_WRITE_CONFIG(dev,
21186231Stmm				    OFW_PCI_PHYS_HI_BUS(pcir.phys_hi),
21286231Stmm				    OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi),
21386231Stmm				    OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi),
21486231Stmm				    PCIR_INTLINE, 255, 1);
21586231Stmm			}
21686231Stmm		}
21786231Stmm	} while ((node = OF_peer(node)) != 0);
21886231Stmm	if (freemap)
21986231Stmm		free(intrmap, M_OFWPROP);
22086231Stmm}
22186231Stmm
22286231Stmmphandle_t
22386231Stmmofw_pci_find_node(int bus, int slot, int func)
22486231Stmm{
22586231Stmm	phandle_t node, bnode, parent;
22686231Stmm	struct ofw_pci_register pcir;
22786231Stmm	int br[2];
22886231Stmm	char name[16];
22986231Stmm
23086231Stmm	/* 1. Try to find the bus in question. */
23186231Stmm	bnode = 0;
23286231Stmm	name[sizeof(name) - 1] = '\0';
23386231Stmm	parent = OF_peer(0);
23486231Stmm	node = OF_child(parent);
23586231Stmm	while (node != 0 && node != -1) {
23686231Stmm		if (OF_getprop(node, "name", name, sizeof(name) - 1) != -1 &&
23786231Stmm		    strcmp(name, "pci") == 0 &&
23886231Stmm		    OF_getprop(node, "bus-range", br, sizeof(br)) != -1) {
23986231Stmm			/* Found the bus? */
24086231Stmm			if (bus == br[0]) {
24186231Stmm				bnode = node;
24286231Stmm				break;
24386231Stmm			}
24486231Stmm			/* Need to descend? */
24586231Stmm			if (bus > br[0] && bus <= br[1]) {
24686231Stmm				parent = node;
24786231Stmm				node = OF_child(node);
24886231Stmm				continue;
24986231Stmm			}
25086231Stmm		}
25186231Stmm		node = OF_peer(node);
25286231Stmm	}
25386231Stmm	if (bnode == 0)
25486231Stmm		return (0);
25586231Stmm	for (node = OF_child(bnode); node != 0 && node != -1;
25686231Stmm	     node = OF_peer(node)) {
25786231Stmm		if (OF_getprop(node, "reg", &pcir, sizeof(pcir)) == -1)
25886231Stmm			continue;
25986231Stmm		if (OFW_PCI_PHYS_HI_DEVICE(pcir.phys_hi) == slot &&
26086231Stmm		    OFW_PCI_PHYS_HI_FUNCTION(pcir.phys_hi) == func) {
26186231Stmm			if (OFW_PCI_PHYS_HI_BUS(pcir.phys_hi) != bus)
26286231Stmm				panic("ofw_pci_find_node: bus number mismatch");
26386231Stmm			return (node);
26486231Stmm		}
26586231Stmm	}
26686231Stmm	return (0);
26786231Stmm}
268