Example #1
0
void
fix_node_irq(int node, struct pcibus_attach_args *pba)
{
	struct { 
		u_int32_t phys_hi, phys_mid, phys_lo;
		u_int32_t size_hi, size_lo;
	} addr [8];
	u_int32_t map[144];
	int len;
	pcitag_t tag;
	u_int32_t irq;
	u_int32_t intr;
	int parent;

	pci_chipset_tag_t pc = pba->pba_pc;

	len = OF_getprop(node, "assigned-addresses", addr, sizeof(addr));
	if (len == -1 || len < sizeof(addr[0]))
		return;

	/* if this node has a AAPL,interrupts property, firmware
	 * has initialized the register correctly.
	 */
	len = OF_getprop(node, "AAPL,interrupts", &intr, 4);
	if (len != 4) {

		parent = OF_parent(node);

		irq = -1;

		/* we want the first interrupt, set size_hi to 1 */
		addr[0].size_hi = 1;
		if (find_node_intr(parent, &addr[0].phys_hi, &irq) == -1) {
			len = OF_getprop(node, "interrupts", map,
			    sizeof(map));
			if (len != -1 && len != 4) {
				irq = map[0];
			} else 
				return;
		}
	} else
		irq = intr;
	/* program the interrupt line register with the value
	 * found in openfirmware
	 */

	tag = pci_make_tag(pc, pcibus(addr[0].phys_hi),
	    pcidev(addr[0].phys_hi), pcifunc(addr[0].phys_hi));

	intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
	intr &= ~PCI_INTERRUPT_LINE_MASK;
	intr |= irq & PCI_INTERRUPT_LINE_MASK;
	pci_conf_write(pc, tag, PCI_INTERRUPT_REG, intr);
}
Example #2
0
/*
 * Find PCI IRQ of the node from OF tree.
 */
static int
find_node_intr(int node, u_int32_t *addr, uint32_t *intr)
{
	int parent, len, mlen, iparent;
	int match, i;
	u_int32_t map[160];
	const u_int32_t *mp;
	u_int32_t imapmask[8], maskedaddr[8];
	u_int32_t acells, icells;
	char name[32];

	/* XXXSL: 1st check for a  interrupt-parent property */
        if (OF_getprop(node, "interrupt-parent", &iparent, sizeof(iparent)) == sizeof(iparent))
	{
		/* How many cells to specify an interrupt ?? */
		if (OF_getprop(iparent, "#interrupt-cells", &icells, 4) != 4)
			return -1;

		if (OF_getprop(node, "interrupts", &map, sizeof(map)) != (icells * 4))
			return -1;

		memcpy(intr, map, icells * 4);
		return (icells * 4);
	}

	parent = OF_parent(node);
	len = OF_getprop(parent, "interrupt-map", map, sizeof(map));
	mlen = OF_getprop(parent, "interrupt-map-mask", imapmask,
	    sizeof(imapmask));

	if (mlen != -1)
		memcpy(maskedaddr, addr, mlen);
again:
	if (len == -1 || mlen == -1)
		goto nomap;

#ifdef DIAGNOSTIC
	if (mlen == sizeof(imapmask)) {
		aprint_error("interrupt-map too long\n");
		return -1;
	}
#endif

	/* mask addr by "interrupt-map-mask" */
	for (i = 0; i < mlen / 4; i++)
		maskedaddr[i] &= imapmask[i];

	mp = map;
	i = 0;
	while (len > mlen) {
		match = memcmp(maskedaddr, mp, mlen);
		mp += mlen / 4;
		len -= mlen;

		/*
		 * We must read "#address-cells" and "#interrupt-cells" each
		 * time because each interrupt-parent may be different.
		 */
		iparent = *mp++;
		len -= 4;
		i = OF_getprop(iparent, "#address-cells", &acells, 4);
		if (i <= 0)
			acells = 0;
		else if (i != 4)
			return -1;
		if (OF_getprop(iparent, "#interrupt-cells", &icells, 4) != 4)
			return -1;

		/* Found. */
		if (match == 0) {
			/*
			 * We matched on address/interrupt, but are we done?
			 */
			if (acells == 0) { /* XXX */
				/*
				 * If we are at the interrupt controller,
				 * we are finally done.  Save the result and
				 * return.
				 */
				memcpy(intr, mp, icells * 4);
				return icells * 4;
			}
			/*
			 * We are now at an intermedia interrupt node.  We
			 * need to use its interrupt mask and map the 
			 * supplied address/interrupt via its map.
			 */
			mlen = OF_getprop(iparent, "interrupt-map-mask",
			    imapmask, sizeof(imapmask));
#ifdef DIAGNOSTIC
			if (mlen != (acells + icells)*4) {
				aprint_error("interrupt-map inconsistent (%d, %d)\n",
				    mlen, (acells + icells)*4);
				return -1;
			}
#endif
			memcpy(maskedaddr, mp, mlen);
			len = OF_getprop(iparent, "interrupt-map", map,
			    sizeof(map));
			goto again;
		}

		mp += (acells + icells);
		len -= (acells + icells) * 4;
	}

nomap:
	/*
	 * If the node has no interrupt property and the parent is a
	 * pci-bridge, use parent's interrupt.  This occurs on a PCI
	 * slot.  (e.g. AHA-3940)
	 */
	memset(name, 0, sizeof(name));
	OF_getprop(parent, "name", name, sizeof(name));
	if (strcmp(name, "pci-bridge") == 0) {
		len = OF_getprop(parent, "AAPL,interrupts", intr, 4) ;
		if (len == 4)
			return len;
#if 0
		/*
		 * XXX I don't know what is the correct local address.
		 * XXX Use the first entry for now.
		 */
		len = OF_getprop(parent, "interrupt-map", map, sizeof(map));
		if (len >= 36) {
			addr = &map[5];
			return find_node_intr(parent, addr, intr);
		}
#endif
	}

	/*
	 * If all else fails, attempt to get AAPL, interrupts property.
	 * Grackle, at least, uses this instead of above in some cases.
	 */
	len = OF_getprop(node, "AAPL,interrupts", intr, 4) ;
	if (len == 4)
		return len;

	return -1;
}
Example #3
0
/* 
 * Find PCI IRQ from OF.
 */
int
find_node_intr(int parent, u_int32_t *addr, u_int32_t *intr)
{
	int iparent, len, mlen, alen, ilen;
	int match, i, step;
	u_int32_t map[144], *mp, *mp1;
	u_int32_t imask[8], maskedaddr[8];
	u_int32_t address_cells, interrupt_cells, mask_cells;

	len = OF_getprop(parent, "interrupt-map", map, sizeof(map));
	mlen = OF_getprop(parent, "interrupt-map-mask", imask, sizeof(imask));
	alen = OF_getprop(parent, "#address-cells",
	    &address_cells, sizeof(address_cells));
	ilen = OF_getprop(parent, "#interrupt-cells",
	    &interrupt_cells, sizeof(interrupt_cells));

	if (len == -1 || mlen == -1 || alen == -1 || ilen == -1)
		goto nomap;

	mask_cells = address_cells + interrupt_cells;
	if (mask_cells != (mlen / sizeof(u_int32_t)))
		goto nomap;
	for (i = 0; i < mask_cells; i++)
		maskedaddr[i] = addr[i] & imask[i];

	/* interrupt-map is formatted as follows
	 * int * #address-cells, int * #interrupt-cells, int, int, int
	 * eg
	 * address-cells = 3
	 * interrupt-cells = 1
	 * 00001000 00000000 00000000 00000000 ff911258 00000034 00000001 
	 * 00001800 00000000 00000000 00000000 ff911258 00000035 00000001 
	 * 00002000 00000000 00000000 00000000 ff911258 00000036 00000001 
	 * | address cells          | | intr | |parent| | irq  | |edge/level|
	 *                            | cells|          | interrupt cells   |
	 *                                              | of parent         |
	 * or at least something close to that.
	 */

	mp = map;
	while (len > mlen) {
		mp1 = mp + mask_cells;

		iparent = *mp1;
		alen = OF_getprop(iparent, "#address-cells",
		    &address_cells, sizeof(address_cells));
		if (alen == -1)
			address_cells = 0;
		ilen = OF_getprop(iparent, "#interrupt-cells",
		    &interrupt_cells, sizeof(interrupt_cells));
		if (ilen == -1)
			goto nomap;

		step = mask_cells + 1 + address_cells + interrupt_cells;

		match = bcmp(maskedaddr, mp, mlen);
		if (match == 0) {
			if (OF_getprop(iparent, "interrupt-controller",
				       NULL, 0) == 0) {
				*intr = mp1[1];
				return 1;
			}
			/* Recurse with new 'addr'. */
			return find_node_intr(iparent, &mp1[1], intr);
		}
		len -= step * sizeof(u_int32_t);
		mp += step;
	}
nomap:
	return -1;
}
Example #4
0
static void
fixpci(int parent, pci_chipset_tag_t pc)
{
	int node;
	pcitag_t tag;
	pcireg_t csr, intr, id, cr;
	int len, i, ilen;
	int32_t irqs[4];
	struct {
		u_int32_t phys_hi, phys_mid, phys_lo;
		u_int32_t size_hi, size_lo;
	} addr[8];
	struct {
		u_int32_t phys_hi, phys_mid, phys_lo;
		u_int32_t icells[5];
	} iaddr;

	/*
	 * first hack - here we make the Ethernet portion of a
	 * UMAX E100 card work
	 */
#ifdef UMAX_E100_HACK
	tag = pci_make_tag(pc, 0, 17, 0);
	id = pci_conf_read(pc, tag, PCI_ID_REG);
	if ((PCI_VENDOR(id) == PCI_VENDOR_DEC) &&
	    (PCI_PRODUCT(id) == PCI_PRODUCT_DEC_21140)) {
		/* this could be one */
		pcireg_t isp, reg;
		pcitag_t tag_isp = pci_make_tag(pc, 0, 13, 0);
		/*
		 * here we go. We shouldn't encounter this anywhere else
		 * than on a UMAX S900 with an E100 board 
		 * look at 00:0d:00 for a Qlogic ISP 1020 to
		 * make sure we really have an E100 here 
		 */
		aprint_debug("\nfound E100 candidate tlp");
		isp = pci_conf_read(pc, tag_isp, PCI_ID_REG);
		if ((PCI_VENDOR(isp) == PCI_VENDOR_QLOGIC) &&
		    (PCI_PRODUCT(isp) == PCI_PRODUCT_QLOGIC_ISP1020)) {

			aprint_verbose("\nenabling UMAX E100 ethernet");

			pci_conf_write(pc, tag, 0x14, 0x80000000);

			/* now enable MMIO and busmastering */
			reg = pci_conf_read(pc, tag,
			    PCI_COMMAND_STATUS_REG);
			reg |= PCI_COMMAND_MEM_ENABLE |
			       PCI_COMMAND_MASTER_ENABLE;
			pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG,
			    reg);

			/* and finally the interrupt */
			reg = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
			reg &= ~PCI_INTERRUPT_LINE_MASK;
			reg |= 23;
			pci_conf_write(pc, tag, PCI_INTERRUPT_REG, reg);
		}
	}
#endif

	len = OF_getprop(parent, "#interrupt-cells", &ilen, sizeof(ilen));
	if (len < 0)            
		ilen = 0;       
	for (node = OF_child(parent); node; node = OF_peer(node)) {
		len = OF_getprop(node, "assigned-addresses", addr,
				 sizeof(addr));
		if (len < (int)sizeof(addr[0]))
			continue;

		tag = pci_make_tag(pc, pcibus(addr[0].phys_hi),
				   pcidev(addr[0].phys_hi),
				   pcifunc(addr[0].phys_hi));

		/*
		 * Make sure the IO and MEM enable bits are set in the CSR.
		 */
		csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG);
		csr &= ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE);

		for (i = 0; i < len / sizeof(addr[0]); i++) {
			switch (addr[i].phys_hi & OFW_PCI_PHYS_HI_SPACEMASK) {
			case OFW_PCI_PHYS_HI_SPACE_IO:
				csr |= PCI_COMMAND_IO_ENABLE;
				break;

			case OFW_PCI_PHYS_HI_SPACE_MEM32:
			case OFW_PCI_PHYS_HI_SPACE_MEM64:
				csr |= PCI_COMMAND_MEM_ENABLE;
				break;
			}
		}

		pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr);

		/*
		 * Make sure the line register is programmed with the
		 * interrupt mapping.
		 */
		if (ilen == 0) {
			/*
			 * Early Apple OFW implementation don't handle
			 * interrupts as defined by the OFW PCI bindings.
			 */
			len = OF_getprop(node, "AAPL,interrupts", irqs, 4);
		} else {
			iaddr.phys_hi = addr[0].phys_hi;
			iaddr.phys_mid = addr[0].phys_mid;
			iaddr.phys_lo = addr[0].phys_lo;
			/*
			 * Thankfully, PCI can only have one entry in its
			 * "interrupts" property.
			 */
			len = OF_getprop(node, "interrupts", &iaddr.icells[0],
			    4*ilen);
			if (len != 4*ilen)
				continue;
			len = find_node_intr(node, &iaddr.phys_hi, irqs);
		}
		if (len <= 0) {
			/*
			 * If we still don't have an interrupt, try one
			 * more time.  This case covers devices behind the
			 * PCI-PCI bridge in a UMAX S900 or similar (9500?)
			 * system.  These slots all share the bridge's
			 * interrupt.
			 */
			len = find_node_intr(node, &addr[0].phys_hi, irqs);
			if (len <= 0)
				continue;
		}

		/* 
		 * For PowerBook 2400, 3400 and original G3:
		 * check if we have a 2nd ohare PIC - if so frob the built-in 
		 * tlp's IRQ to 60
		 * first see if we have something on bus 0 device 13 and if 
		 * it's a DEC 21041
		 */
		id = pci_conf_read(pc, tag, PCI_ID_REG);
		if ((tag == pci_make_tag(pc, 0, 13, 0)) &&
		    (PCI_VENDOR(id) == PCI_VENDOR_DEC) && 
		    (PCI_PRODUCT(id) == PCI_PRODUCT_DEC_21041)) {

			/* now look for the 2nd ohare */
			if (OF_finddevice("/bandit/pci106b,7") != -1) {

				irqs[0] = 60;
				aprint_verbose("\nohare: frobbing tlp IRQ to 60");
			}
		}

		intr = pci_conf_read(pc, tag, PCI_INTERRUPT_REG);
		intr &= ~PCI_INTERRUPT_LINE_MASK;
		intr |= irqs[0] & PCI_INTERRUPT_LINE_MASK;
		pci_conf_write(pc, tag, PCI_INTERRUPT_REG, intr);

		/* fix secondary bus numbers on CardBus bridges */
		cr = pci_conf_read(pc, tag, PCI_CLASS_REG);
		if ((PCI_CLASS(cr) == PCI_CLASS_BRIDGE) &&
		    (PCI_SUBCLASS(cr) == PCI_SUBCLASS_BRIDGE_CARDBUS)) {
			uint32_t bi, busid;

			/*
			 * we found a CardBus bridge. Check if the bus number
			 * is sane
			 */
			bi = pci_conf_read(pc, tag, PPB_REG_BUSINFO);
			busid = bi & 0xff;
			if (busid == 0) {
				fix_cardbus_bridge(node, pc, tag);
			}
		}
	}
}