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