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(®s, 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, ®s);
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