xref: /onnv-gate/usr/src/uts/i86pc/os/pci_bios.c (revision 12054:bcacc803343d)
11494Sjg /*
21494Sjg  * CDDL HEADER START
31494Sjg  *
41494Sjg  * The contents of this file are subject to the terms of the
51494Sjg  * Common Development and Distribution License (the "License").
61494Sjg  * You may not use this file except in compliance with the License.
71494Sjg  *
81494Sjg  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91494Sjg  * or http://www.opensolaris.org/os/licensing.
101494Sjg  * See the License for the specific language governing permissions
111494Sjg  * and limitations under the License.
121494Sjg  *
131494Sjg  * When distributing Covered Code, include this CDDL HEADER in each
141494Sjg  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151494Sjg  * If applicable, add the following below this CDDL HEADER, with the
161494Sjg  * fields enclosed by brackets "[]" replaced with your own identifying
171494Sjg  * information: Portions Copyright [yyyy] [name of copyright owner]
181494Sjg  *
191494Sjg  * CDDL HEADER END
201494Sjg  */
211494Sjg /*
22*12054SStephen.Hanson@Sun.COM  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
231494Sjg  */
241494Sjg 
251494Sjg #include <sys/types.h>
261494Sjg #include <sys/stat.h>
271494Sjg #include <sys/sunndi.h>
281494Sjg #include <sys/pci.h>
291494Sjg #include <sys/pci_impl.h>
301494Sjg #include <sys/pci_cfgspace.h>
311494Sjg #include <sys/pci_cfgspace_impl.h>
321494Sjg #include <sys/memlist.h>
331494Sjg #include <sys/bootconf.h>
341494Sjg #include <sys/psw.h>
351494Sjg 
361494Sjg /*
371494Sjg  * pci irq routing information table
381494Sjg  */
39*12054SStephen.Hanson@Sun.COM int				pci_irq_nroutes;
401494Sjg static pci_irq_route_t		*pci_irq_routes;
411494Sjg 
421494Sjg 
431494Sjg static int pci_bios_get_irq_routing(pci_irq_route_t *, int, int *);
441494Sjg static void pci_get_irq_routing_table(void);
451494Sjg 
461494Sjg 
471494Sjg /*
481494Sjg  * Retrieve information from the bios needed for system
491494Sjg  * configuration early during startup.
501494Sjg  */
511494Sjg void
startup_pci_bios(void)521494Sjg startup_pci_bios(void)
531494Sjg {
541494Sjg 	pci_get_irq_routing_table();
551494Sjg }
561494Sjg 
571494Sjg 
581494Sjg /*
591494Sjg  * Issue the bios get irq routing information table interrupt
601494Sjg  *
611494Sjg  * Despite the name, the information in the table is only
621494Sjg  * used to derive slot names for some named pci hot-plug slots.
631494Sjg  *
641494Sjg  * Returns the number of irq routing table entries returned
651494Sjg  * by the bios, or 0 and optionally, the number of entries required.
661494Sjg  */
671494Sjg static int
pci_bios_get_irq_routing(pci_irq_route_t * routes,int nroutes,int * nneededp)681494Sjg pci_bios_get_irq_routing(pci_irq_route_t *routes, int nroutes, int *nneededp)
691494Sjg {
701494Sjg 	struct bop_regs regs;
711494Sjg 	uchar_t		*hdrp;
721494Sjg 	uchar_t		*bufp;
731494Sjg 	int 		i, n;
741494Sjg 	int		rval = 0;
751494Sjg 
761494Sjg 	if (nneededp)
771494Sjg 		*nneededp = 0;
781494Sjg 
791494Sjg 	/*
801494Sjg 	 * Set up irq routing header with the size and address
811494Sjg 	 * of some useable low-memory data addresses.  Initalize
821494Sjg 	 * data area to zero, avoiding memcpy/bzero.
831494Sjg 	 */
841494Sjg 	hdrp = (uchar_t *)BIOS_IRQ_ROUTING_HDR;
851494Sjg 	bufp = (uchar_t *)BIOS_IRQ_ROUTING_DATA;
861494Sjg 
871494Sjg 	n = nroutes * sizeof (pci_irq_route_t);
881494Sjg 	for (i = 0; i < n; i++)
891494Sjg 		bufp[i] = 0;
901494Sjg 	((pci_irq_route_hdr_t *)hdrp)->pir_size = n;
911494Sjg 	((pci_irq_route_hdr_t *)hdrp)->pir_addr = (uint32_t)(uintptr_t)bufp;
921494Sjg 
931494Sjg 	bzero(&regs, sizeof (regs));
941494Sjg 	regs.eax.word.ax = (PCI_FUNCTION_ID << 8) | PCI_GET_IRQ_ROUTING;
951494Sjg 
961494Sjg 	regs.ds = 0xf000;
971494Sjg 	regs.es = FP_SEG((uint_t)(uintptr_t)hdrp);
981494Sjg 	regs.edi.word.di = FP_OFF((uint_t)(uintptr_t)hdrp);
991494Sjg 
1001494Sjg 	BOP_DOINT(bootops, 0x1a, &regs);
1011494Sjg 
1021494Sjg 	n = (int)(((pci_irq_route_hdr_t *)hdrp)->pir_size /
1031494Sjg 	    sizeof (pci_irq_route_t));
1041494Sjg 
1051494Sjg 	if ((regs.eflags & PS_C) != 0) {
1061494Sjg 		if (nneededp)
1071494Sjg 			*nneededp = n;
1081494Sjg 	} else {
1091494Sjg 		/*
1101494Sjg 		 * Copy resulting irq routing data from low memory up to
1111494Sjg 		 * the kernel address space, avoiding memcpy as usual.
1121494Sjg 		 */
1131494Sjg 		if (n <= nroutes) {
1141494Sjg 			for (i = 0; i < n * sizeof (pci_irq_route_t); i++)
1151494Sjg 				((uchar_t *)routes)[i] = bufp[i];
1161494Sjg 			rval = n;
1171494Sjg 		}
1181494Sjg 	}
1191494Sjg 	return (rval);
1201494Sjg }
1211494Sjg 
1221494Sjg static void
pci_get_irq_routing_table(void)1231494Sjg pci_get_irq_routing_table(void)
1241494Sjg {
1251494Sjg 	pci_irq_route_t	*routes;
1261494Sjg 	int		n = N_PCI_IRQ_ROUTES;
1271494Sjg 	int		nneeded = 0;
1281494Sjg 	int		nroutes;
1291494Sjg 
1301494Sjg 	/*
1311494Sjg 	 * Get irq routing table information.
1321494Sjg 	 * Allocate a buffer for an initial default number of entries.
1331494Sjg 	 * If the bios indicates it needs a larger buffer, try it again.
1341494Sjg 	 * Drive on if it still won't cooperate and play nice after that.
1351494Sjg 	 */
1361494Sjg 	routes = kmem_zalloc(n * sizeof (pci_irq_route_t), KM_SLEEP);
1371494Sjg 	nroutes = pci_bios_get_irq_routing(routes, n, &nneeded);
1381494Sjg 	if (nroutes == 0 && nneeded > n) {
1391494Sjg 		kmem_free(routes, n * sizeof (pci_irq_route_t));
1401494Sjg 		if (nneeded > N_PCI_IRQ_ROUTES_MAX) {
1411494Sjg 			cmn_err(CE_CONT,
1421494Sjg 			    "pci: unable to get IRQ routing information, "
1431494Sjg 			    "required buffer space of %d entries exceeds max\n",
1441494Sjg 			    nneeded);
1451494Sjg 			return;
1461494Sjg 		}
1471494Sjg 		n = nneeded;
1481494Sjg 		routes = kmem_zalloc(n * sizeof (pci_irq_route_t), KM_SLEEP);
1491494Sjg 		nroutes = pci_bios_get_irq_routing(routes, n, NULL);
1501494Sjg 		if (nroutes == 0) {
1511494Sjg 			cmn_err(CE_CONT,
1521494Sjg 			    "pci: unable to get IRQ routing information, "
1531494Sjg 			    "required buffer space for %d entries\n", n);
1541494Sjg 			kmem_free(routes, n * sizeof (pci_irq_route_t));
1551494Sjg 		}
1561494Sjg 	}
1571494Sjg 
1581494Sjg 	if (nroutes > 0) {
1591494Sjg 		pci_irq_routes = routes;
1601494Sjg 		pci_irq_nroutes = nroutes;
1611494Sjg 	}
1621494Sjg }
1631494Sjg 
1641494Sjg /*
1651494Sjg  * Use the results of the PCI BIOS call that returned the routing tables
1661494Sjg  * to build the 1275 slot-names property for the indicated bus.
1671494Sjg  * Results are returned in buf.  Length is return value, -1 is returned on
1681494Sjg  * overflow and zero is returned if no data exists to build a property.
1691494Sjg  */
1701494Sjg int
pci_slot_names_prop(int bus,char * buf,int len)1711494Sjg pci_slot_names_prop(int bus, char *buf, int len)
1721494Sjg {
1731494Sjg 	uchar_t		dev;
1741494Sjg 	uchar_t		slot[N_PCI_IRQ_ROUTES_MAX+1];
1751494Sjg 	uint32_t	 mask;
1761494Sjg 	int		i, nnames, plen;
1771494Sjg 
1781494Sjg 	ASSERT(pci_irq_nroutes <= N_PCI_IRQ_ROUTES_MAX);
1791494Sjg 
1801494Sjg 	if (pci_irq_nroutes == 0)
1811494Sjg 		return (0);
1821494Sjg 	nnames = 0;
1831494Sjg 	mask = 0;
1841494Sjg 	for (i = 0; i < pci_irq_nroutes; i++)
1851494Sjg 		slot[i] = 0xff;
1861494Sjg 	for (i = 0; i < pci_irq_nroutes; i++) {
1871494Sjg 		if (pci_irq_routes[i].pir_bus != bus)
1881494Sjg 			continue;
1891494Sjg 		if (pci_irq_routes[i].pir_slot != 0) {
1901494Sjg 			dev = (pci_irq_routes[i].pir_dev & 0xf8) >> 3;
1911494Sjg 			slot[dev] = pci_irq_routes[i].pir_slot;
1921494Sjg 			mask |= (1 << dev);
1931494Sjg 			nnames++;
1941494Sjg 		}
1951494Sjg 	}
1961494Sjg 
1971494Sjg 	if (nnames == 0)
1981494Sjg 		return (0);
1991494Sjg 
2001494Sjg 	if (len < (4 + nnames * 8))
2011494Sjg 		return (-1);
2021494Sjg 	*(uint32_t *)buf = mask;
2031494Sjg 	plen = 4;
2041494Sjg 	for (i = 0; i < pci_irq_nroutes; i++) {
2051494Sjg 		if (slot[i] == 0xff)
2061494Sjg 			continue;
2071494Sjg 		(void) sprintf(buf + plen, "Slot%d", slot[i]);
2081494Sjg 		plen += strlen(buf+plen) + 1;
2091494Sjg 		*(buf + plen) = 0;
2101494Sjg 	}
2111494Sjg 	for (; plen % 4; plen++)
2121494Sjg 		*(buf + plen) = 0;
2131494Sjg 	return (plen);
2141494Sjg }
215