xref: /titanic_51/usr/src/uts/i86pc/os/pci_bios.c (revision 0db3240d392634cfff2f95fb6da34b56b8dc574f)
175bcd456Sjg /*
275bcd456Sjg  * CDDL HEADER START
375bcd456Sjg  *
475bcd456Sjg  * The contents of this file are subject to the terms of the
575bcd456Sjg  * Common Development and Distribution License (the "License").
675bcd456Sjg  * You may not use this file except in compliance with the License.
775bcd456Sjg  *
875bcd456Sjg  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
975bcd456Sjg  * or http://www.opensolaris.org/os/licensing.
1075bcd456Sjg  * See the License for the specific language governing permissions
1175bcd456Sjg  * and limitations under the License.
1275bcd456Sjg  *
1375bcd456Sjg  * When distributing Covered Code, include this CDDL HEADER in each
1475bcd456Sjg  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1575bcd456Sjg  * If applicable, add the following below this CDDL HEADER, with the
1675bcd456Sjg  * fields enclosed by brackets "[]" replaced with your own identifying
1775bcd456Sjg  * information: Portions Copyright [yyyy] [name of copyright owner]
1875bcd456Sjg  *
1975bcd456Sjg  * CDDL HEADER END
2075bcd456Sjg  */
2175bcd456Sjg /*
22*0db3240dSStephen Hanson  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
2375bcd456Sjg  */
2475bcd456Sjg 
2575bcd456Sjg #include <sys/types.h>
2675bcd456Sjg #include <sys/stat.h>
2775bcd456Sjg #include <sys/sunndi.h>
2875bcd456Sjg #include <sys/pci.h>
2975bcd456Sjg #include <sys/pci_impl.h>
3075bcd456Sjg #include <sys/pci_cfgspace.h>
3175bcd456Sjg #include <sys/pci_cfgspace_impl.h>
3275bcd456Sjg #include <sys/memlist.h>
3375bcd456Sjg #include <sys/bootconf.h>
3475bcd456Sjg #include <sys/psw.h>
3575bcd456Sjg 
3675bcd456Sjg /*
3775bcd456Sjg  * pci irq routing information table
3875bcd456Sjg  */
39*0db3240dSStephen Hanson int				pci_irq_nroutes;
4075bcd456Sjg static pci_irq_route_t		*pci_irq_routes;
4175bcd456Sjg 
4275bcd456Sjg 
4375bcd456Sjg static int pci_bios_get_irq_routing(pci_irq_route_t *, int, int *);
4475bcd456Sjg static void pci_get_irq_routing_table(void);
4575bcd456Sjg 
4675bcd456Sjg 
4775bcd456Sjg /*
4875bcd456Sjg  * Retrieve information from the bios needed for system
4975bcd456Sjg  * configuration early during startup.
5075bcd456Sjg  */
5175bcd456Sjg void
5275bcd456Sjg startup_pci_bios(void)
5375bcd456Sjg {
5475bcd456Sjg 	pci_get_irq_routing_table();
5575bcd456Sjg }
5675bcd456Sjg 
5775bcd456Sjg 
5875bcd456Sjg /*
5975bcd456Sjg  * Issue the bios get irq routing information table interrupt
6075bcd456Sjg  *
6175bcd456Sjg  * Despite the name, the information in the table is only
6275bcd456Sjg  * used to derive slot names for some named pci hot-plug slots.
6375bcd456Sjg  *
6475bcd456Sjg  * Returns the number of irq routing table entries returned
6575bcd456Sjg  * by the bios, or 0 and optionally, the number of entries required.
6675bcd456Sjg  */
6775bcd456Sjg static int
6875bcd456Sjg pci_bios_get_irq_routing(pci_irq_route_t *routes, int nroutes, int *nneededp)
6975bcd456Sjg {
7075bcd456Sjg 	struct bop_regs regs;
7175bcd456Sjg 	uchar_t		*hdrp;
7275bcd456Sjg 	uchar_t		*bufp;
7375bcd456Sjg 	int 		i, n;
7475bcd456Sjg 	int		rval = 0;
7575bcd456Sjg 
7675bcd456Sjg 	if (nneededp)
7775bcd456Sjg 		*nneededp = 0;
7875bcd456Sjg 
7975bcd456Sjg 	/*
8075bcd456Sjg 	 * Set up irq routing header with the size and address
8175bcd456Sjg 	 * of some useable low-memory data addresses.  Initalize
8275bcd456Sjg 	 * data area to zero, avoiding memcpy/bzero.
8375bcd456Sjg 	 */
8475bcd456Sjg 	hdrp = (uchar_t *)BIOS_IRQ_ROUTING_HDR;
8575bcd456Sjg 	bufp = (uchar_t *)BIOS_IRQ_ROUTING_DATA;
8675bcd456Sjg 
8775bcd456Sjg 	n = nroutes * sizeof (pci_irq_route_t);
8875bcd456Sjg 	for (i = 0; i < n; i++)
8975bcd456Sjg 		bufp[i] = 0;
9075bcd456Sjg 	((pci_irq_route_hdr_t *)hdrp)->pir_size = n;
9175bcd456Sjg 	((pci_irq_route_hdr_t *)hdrp)->pir_addr = (uint32_t)(uintptr_t)bufp;
9275bcd456Sjg 
9375bcd456Sjg 	bzero(&regs, sizeof (regs));
9475bcd456Sjg 	regs.eax.word.ax = (PCI_FUNCTION_ID << 8) | PCI_GET_IRQ_ROUTING;
9575bcd456Sjg 
9675bcd456Sjg 	regs.ds = 0xf000;
9775bcd456Sjg 	regs.es = FP_SEG((uint_t)(uintptr_t)hdrp);
9875bcd456Sjg 	regs.edi.word.di = FP_OFF((uint_t)(uintptr_t)hdrp);
9975bcd456Sjg 
10075bcd456Sjg 	BOP_DOINT(bootops, 0x1a, &regs);
10175bcd456Sjg 
10275bcd456Sjg 	n = (int)(((pci_irq_route_hdr_t *)hdrp)->pir_size /
10375bcd456Sjg 	    sizeof (pci_irq_route_t));
10475bcd456Sjg 
10575bcd456Sjg 	if ((regs.eflags & PS_C) != 0) {
10675bcd456Sjg 		if (nneededp)
10775bcd456Sjg 			*nneededp = n;
10875bcd456Sjg 	} else {
10975bcd456Sjg 		/*
11075bcd456Sjg 		 * Copy resulting irq routing data from low memory up to
11175bcd456Sjg 		 * the kernel address space, avoiding memcpy as usual.
11275bcd456Sjg 		 */
11375bcd456Sjg 		if (n <= nroutes) {
11475bcd456Sjg 			for (i = 0; i < n * sizeof (pci_irq_route_t); i++)
11575bcd456Sjg 				((uchar_t *)routes)[i] = bufp[i];
11675bcd456Sjg 			rval = n;
11775bcd456Sjg 		}
11875bcd456Sjg 	}
11975bcd456Sjg 	return (rval);
12075bcd456Sjg }
12175bcd456Sjg 
12275bcd456Sjg static void
12375bcd456Sjg pci_get_irq_routing_table(void)
12475bcd456Sjg {
12575bcd456Sjg 	pci_irq_route_t	*routes;
12675bcd456Sjg 	int		n = N_PCI_IRQ_ROUTES;
12775bcd456Sjg 	int		nneeded = 0;
12875bcd456Sjg 	int		nroutes;
12975bcd456Sjg 
13075bcd456Sjg 	/*
13175bcd456Sjg 	 * Get irq routing table information.
13275bcd456Sjg 	 * Allocate a buffer for an initial default number of entries.
13375bcd456Sjg 	 * If the bios indicates it needs a larger buffer, try it again.
13475bcd456Sjg 	 * Drive on if it still won't cooperate and play nice after that.
13575bcd456Sjg 	 */
13675bcd456Sjg 	routes = kmem_zalloc(n * sizeof (pci_irq_route_t), KM_SLEEP);
13775bcd456Sjg 	nroutes = pci_bios_get_irq_routing(routes, n, &nneeded);
13875bcd456Sjg 	if (nroutes == 0 && nneeded > n) {
13975bcd456Sjg 		kmem_free(routes, n * sizeof (pci_irq_route_t));
14075bcd456Sjg 		if (nneeded > N_PCI_IRQ_ROUTES_MAX) {
14175bcd456Sjg 			cmn_err(CE_CONT,
14275bcd456Sjg 			    "pci: unable to get IRQ routing information, "
14375bcd456Sjg 			    "required buffer space of %d entries exceeds max\n",
14475bcd456Sjg 			    nneeded);
14575bcd456Sjg 			return;
14675bcd456Sjg 		}
14775bcd456Sjg 		n = nneeded;
14875bcd456Sjg 		routes = kmem_zalloc(n * sizeof (pci_irq_route_t), KM_SLEEP);
14975bcd456Sjg 		nroutes = pci_bios_get_irq_routing(routes, n, NULL);
15075bcd456Sjg 		if (nroutes == 0) {
15175bcd456Sjg 			cmn_err(CE_CONT,
15275bcd456Sjg 			    "pci: unable to get IRQ routing information, "
15375bcd456Sjg 			    "required buffer space for %d entries\n", n);
15475bcd456Sjg 			kmem_free(routes, n * sizeof (pci_irq_route_t));
15575bcd456Sjg 		}
15675bcd456Sjg 	}
15775bcd456Sjg 
15875bcd456Sjg 	if (nroutes > 0) {
15975bcd456Sjg 		pci_irq_routes = routes;
16075bcd456Sjg 		pci_irq_nroutes = nroutes;
16175bcd456Sjg 	}
16275bcd456Sjg }
16375bcd456Sjg 
16475bcd456Sjg /*
16575bcd456Sjg  * Use the results of the PCI BIOS call that returned the routing tables
16675bcd456Sjg  * to build the 1275 slot-names property for the indicated bus.
16775bcd456Sjg  * Results are returned in buf.  Length is return value, -1 is returned on
16875bcd456Sjg  * overflow and zero is returned if no data exists to build a property.
16975bcd456Sjg  */
17075bcd456Sjg int
17175bcd456Sjg pci_slot_names_prop(int bus, char *buf, int len)
17275bcd456Sjg {
17375bcd456Sjg 	uchar_t		dev;
17475bcd456Sjg 	uchar_t		slot[N_PCI_IRQ_ROUTES_MAX+1];
17575bcd456Sjg 	uint32_t	 mask;
17675bcd456Sjg 	int		i, nnames, plen;
17775bcd456Sjg 
17875bcd456Sjg 	ASSERT(pci_irq_nroutes <= N_PCI_IRQ_ROUTES_MAX);
17975bcd456Sjg 
18075bcd456Sjg 	if (pci_irq_nroutes == 0)
18175bcd456Sjg 		return (0);
18275bcd456Sjg 	nnames = 0;
18375bcd456Sjg 	mask = 0;
18475bcd456Sjg 	for (i = 0; i < pci_irq_nroutes; i++)
18575bcd456Sjg 		slot[i] = 0xff;
18675bcd456Sjg 	for (i = 0; i < pci_irq_nroutes; i++) {
18775bcd456Sjg 		if (pci_irq_routes[i].pir_bus != bus)
18875bcd456Sjg 			continue;
18975bcd456Sjg 		if (pci_irq_routes[i].pir_slot != 0) {
19075bcd456Sjg 			dev = (pci_irq_routes[i].pir_dev & 0xf8) >> 3;
19175bcd456Sjg 			slot[dev] = pci_irq_routes[i].pir_slot;
19275bcd456Sjg 			mask |= (1 << dev);
19375bcd456Sjg 			nnames++;
19475bcd456Sjg 		}
19575bcd456Sjg 	}
19675bcd456Sjg 
19775bcd456Sjg 	if (nnames == 0)
19875bcd456Sjg 		return (0);
19975bcd456Sjg 
20075bcd456Sjg 	if (len < (4 + nnames * 8))
20175bcd456Sjg 		return (-1);
20275bcd456Sjg 	*(uint32_t *)buf = mask;
20375bcd456Sjg 	plen = 4;
20475bcd456Sjg 	for (i = 0; i < pci_irq_nroutes; i++) {
20575bcd456Sjg 		if (slot[i] == 0xff)
20675bcd456Sjg 			continue;
20775bcd456Sjg 		(void) sprintf(buf + plen, "Slot%d", slot[i]);
20875bcd456Sjg 		plen += strlen(buf+plen) + 1;
20975bcd456Sjg 		*(buf + plen) = 0;
21075bcd456Sjg 	}
21175bcd456Sjg 	for (; plen % 4; plen++)
21275bcd456Sjg 		*(buf + plen) = 0;
21375bcd456Sjg 	return (plen);
21475bcd456Sjg }
215