1ac19f918SStefan Eßer /* 25bec6157SStefan Eßer * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 35bec6157SStefan Eßer * All rights reserved. 45bec6157SStefan Eßer * 55bec6157SStefan Eßer * Redistribution and use in source and binary forms, with or without 65bec6157SStefan Eßer * modification, are permitted provided that the following conditions 75bec6157SStefan Eßer * are met: 85bec6157SStefan Eßer * 1. Redistributions of source code must retain the above copyright 95bec6157SStefan Eßer * notice unmodified, this list of conditions, and the following 105bec6157SStefan Eßer * disclaimer. 115bec6157SStefan Eßer * 2. Redistributions in binary form must reproduce the above copyright 125bec6157SStefan Eßer * notice, this list of conditions and the following disclaimer in the 135bec6157SStefan Eßer * documentation and/or other materials provided with the distribution. 145bec6157SStefan Eßer * 155bec6157SStefan Eßer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 165bec6157SStefan Eßer * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 175bec6157SStefan Eßer * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 185bec6157SStefan Eßer * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 195bec6157SStefan Eßer * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 205bec6157SStefan Eßer * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 215bec6157SStefan Eßer * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 225bec6157SStefan Eßer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 235bec6157SStefan Eßer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 245bec6157SStefan Eßer * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 255bec6157SStefan Eßer * 268dc26439SPeter Wemm * $Id: pcibus.c,v 1.41 1997/12/20 09:04:25 se Exp $ 275bec6157SStefan Eßer * 28ac19f918SStefan Eßer */ 29ac19f918SStefan Eßer 308dc26439SPeter Wemm #include <sys/param.h> 315bec6157SStefan Eßer #include <sys/systm.h> 328dc26439SPeter Wemm #include <sys/bus.h> 338dc26439SPeter Wemm #include <sys/kernel.h> 34ac19f918SStefan Eßer 355bec6157SStefan Eßer #include <pci/pcivar.h> 365bec6157SStefan Eßer #include <i386/isa/pcibus.h> 37ac19f918SStefan Eßer 385bec6157SStefan Eßer #ifdef PCI_COMPAT 395bec6157SStefan Eßer /* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */ 405bec6157SStefan Eßer #define cfgmech pci_mechanism 415bec6157SStefan Eßer int cfgmech; 42e30f0011SSatoshi Asami #else 435bec6157SStefan Eßer static int cfgmech; 445bec6157SStefan Eßer #endif /* PCI_COMPAT */ 455bec6157SStefan Eßer static int devmax; 46ac19f918SStefan Eßer 475bec6157SStefan Eßer /* enable configuration space accesses and return data port address */ 48ac19f918SStefan Eßer 49a3adc4f8SStefan Eßer static int 505bec6157SStefan Eßer pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 515bec6157SStefan Eßer { 525bec6157SStefan Eßer int dataport = 0; 535bec6157SStefan Eßer 545bec6157SStefan Eßer if (bus <= PCI_BUSMAX 555bec6157SStefan Eßer && slot < devmax 565bec6157SStefan Eßer && func <= PCI_FUNCMAX 575bec6157SStefan Eßer && reg <= PCI_REGMAX 585bec6157SStefan Eßer && bytes != 3 595bec6157SStefan Eßer && (unsigned) bytes <= 4 605bec6157SStefan Eßer && (reg & (bytes -1)) == 0) { 615bec6157SStefan Eßer switch (cfgmech) { 625bec6157SStefan Eßer case 1: 63b3daa02eSStefan Eßer outl(CONF1_ADDR_PORT, (1 << 31) 64b3daa02eSStefan Eßer | (bus << 16) | (slot << 11) 65b3daa02eSStefan Eßer | (func << 8) | (reg & ~0x03)); 66b3daa02eSStefan Eßer dataport = CONF1_DATA_PORT + (reg & 0x03); 675bec6157SStefan Eßer break; 685bec6157SStefan Eßer case 2: 695bec6157SStefan Eßer outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 705bec6157SStefan Eßer outb(CONF2_FORWARD_PORT, bus); 715bec6157SStefan Eßer dataport = 0xc000 | (slot << 8) | reg; 725bec6157SStefan Eßer break; 735bec6157SStefan Eßer } 745bec6157SStefan Eßer } 755bec6157SStefan Eßer return (dataport); 765bec6157SStefan Eßer } 775bec6157SStefan Eßer 785bec6157SStefan Eßer /* disable configuration space accesses */ 795bec6157SStefan Eßer 805bec6157SStefan Eßer static void 815bec6157SStefan Eßer pci_cfgdisable(void) 825bec6157SStefan Eßer { 835bec6157SStefan Eßer switch (cfgmech) { 845bec6157SStefan Eßer case 1: 855bec6157SStefan Eßer outl(CONF1_ADDR_PORT, 0); 865bec6157SStefan Eßer break; 875bec6157SStefan Eßer case 2: 885bec6157SStefan Eßer outb(CONF2_ENABLE_PORT, 0); 895bec6157SStefan Eßer outb(CONF2_FORWARD_PORT, 0); 905bec6157SStefan Eßer break; 915bec6157SStefan Eßer } 925bec6157SStefan Eßer } 935bec6157SStefan Eßer 945bec6157SStefan Eßer /* read configuration space register */ 955bec6157SStefan Eßer 965bec6157SStefan Eßer int 975bec6157SStefan Eßer pci_cfgread(pcicfgregs *cfg, int reg, int bytes) 985bec6157SStefan Eßer { 995bec6157SStefan Eßer int data = -1; 1005bec6157SStefan Eßer int port; 1015bec6157SStefan Eßer 1025bec6157SStefan Eßer port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); 1035bec6157SStefan Eßer 1045bec6157SStefan Eßer if (port != 0) { 1055bec6157SStefan Eßer switch (bytes) { 1065bec6157SStefan Eßer case 1: 1075bec6157SStefan Eßer data = inb(port); 1085bec6157SStefan Eßer break; 1095bec6157SStefan Eßer case 2: 1105bec6157SStefan Eßer data = inw(port); 1115bec6157SStefan Eßer break; 1125bec6157SStefan Eßer case 4: 1135bec6157SStefan Eßer data = inl(port); 1145bec6157SStefan Eßer break; 1155bec6157SStefan Eßer } 1165bec6157SStefan Eßer pci_cfgdisable(); 1175bec6157SStefan Eßer } 1185bec6157SStefan Eßer return (data); 1195bec6157SStefan Eßer } 1205bec6157SStefan Eßer 1215bec6157SStefan Eßer /* write configuration space register */ 1225bec6157SStefan Eßer 1235bec6157SStefan Eßer void 1245bec6157SStefan Eßer pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes) 1255bec6157SStefan Eßer { 1265bec6157SStefan Eßer int port; 1275bec6157SStefan Eßer 1285bec6157SStefan Eßer port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); 1295bec6157SStefan Eßer if (port != 0) { 1305bec6157SStefan Eßer switch (bytes) { 1315bec6157SStefan Eßer case 1: 1325bec6157SStefan Eßer outb(port, data); 1335bec6157SStefan Eßer break; 1345bec6157SStefan Eßer case 2: 1355bec6157SStefan Eßer outw(port, data); 1365bec6157SStefan Eßer break; 1375bec6157SStefan Eßer case 4: 1385bec6157SStefan Eßer outl(port, data); 1395bec6157SStefan Eßer break; 1405bec6157SStefan Eßer } 1415bec6157SStefan Eßer pci_cfgdisable(); 1425bec6157SStefan Eßer } 1435bec6157SStefan Eßer } 1445bec6157SStefan Eßer 1455bec6157SStefan Eßer /* check whether the configuration mechanism has been correct identified */ 1465bec6157SStefan Eßer 1475bec6157SStefan Eßer static int 1485bec6157SStefan Eßer pci_cfgcheck(int maxdev) 149a3adc4f8SStefan Eßer { 150a3adc4f8SStefan Eßer u_char device; 151a3adc4f8SStefan Eßer 1525bec6157SStefan Eßer if (bootverbose) 1535bec6157SStefan Eßer printf("pci_cfgcheck:\tdevice "); 15477b57314SStefan Eßer 1555bec6157SStefan Eßer for (device = 0; device < maxdev; device++) { 1565bec6157SStefan Eßer unsigned id, class, header; 157c7483249SStefan Eßer if (bootverbose) 158c7483249SStefan Eßer printf("%d ", device); 1595bec6157SStefan Eßer 1605bec6157SStefan Eßer id = inl(pci_cfgenable(0, device, 0, 0, 4)); 1615bec6157SStefan Eßer if (id == 0 || id == -1) 16281cf5d7aSStefan Eßer continue; 16381cf5d7aSStefan Eßer 1645bec6157SStefan Eßer class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; 16581cf5d7aSStefan Eßer if (bootverbose) 1665bec6157SStefan Eßer printf("[class=%06x] ", class); 1678277ac25SStefan Eßer if (class == 0 || (class & 0xf870ff) != 0) 16881cf5d7aSStefan Eßer continue; 16981cf5d7aSStefan Eßer 1705bec6157SStefan Eßer header = inb(pci_cfgenable(0, device, 0, 14, 1)); 17181cf5d7aSStefan Eßer if (bootverbose) 1725bec6157SStefan Eßer printf("[hdr=%02x] ", header); 1735bec6157SStefan Eßer if ((header & 0x7e) != 0) 17481cf5d7aSStefan Eßer continue; 17581cf5d7aSStefan Eßer 1765bec6157SStefan Eßer if (bootverbose) 1775bec6157SStefan Eßer printf("is there (id=%08x)\n", id); 1785bec6157SStefan Eßer 1795bec6157SStefan Eßer pci_cfgdisable(); 1805bec6157SStefan Eßer return (1); 181a3adc4f8SStefan Eßer } 182c7483249SStefan Eßer if (bootverbose) 183c7483249SStefan Eßer printf("-- nothing found\n"); 1845bec6157SStefan Eßer 1855bec6157SStefan Eßer pci_cfgdisable(); 1865bec6157SStefan Eßer return (0); 187a3adc4f8SStefan Eßer } 188d7ea35fcSStefan Eßer 1898dc26439SPeter Wemm static int 1905bec6157SStefan Eßer pci_cfgopen(void) 191ac19f918SStefan Eßer { 192287911bdSStefan Eßer unsigned long mode1res,oldval1; 193287911bdSStefan Eßer unsigned char mode2res,oldval2; 1940847c06dSStefan Eßer 195287911bdSStefan Eßer oldval1 = inl(CONF1_ADDR_PORT); 196a3adc4f8SStefan Eßer 19777b57314SStefan Eßer if (bootverbose) { 1985bec6157SStefan Eßer printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", 1995bec6157SStefan Eßer oldval1); 200a3adc4f8SStefan Eßer } 201a3adc4f8SStefan Eßer 2020e2f699bSStefan Eßer if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 203287911bdSStefan Eßer 2045bec6157SStefan Eßer cfgmech = 1; 2055bec6157SStefan Eßer devmax = 32; 20677b57314SStefan Eßer 20777b57314SStefan Eßer outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 20877b57314SStefan Eßer outb(CONF1_ADDR_PORT +3, 0); 20977b57314SStefan Eßer mode1res = inl(CONF1_ADDR_PORT); 210287911bdSStefan Eßer outl(CONF1_ADDR_PORT, oldval1); 21177b57314SStefan Eßer 21277b57314SStefan Eßer if (bootverbose) 2135bec6157SStefan Eßer printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", 21477b57314SStefan Eßer mode1res, CONF1_ENABLE_CHK); 21577b57314SStefan Eßer 21677b57314SStefan Eßer if (mode1res) { 2175bec6157SStefan Eßer if (pci_cfgcheck(32)) 2185bec6157SStefan Eßer return (cfgmech); 2195bec6157SStefan Eßer } 22077b57314SStefan Eßer 22177b57314SStefan Eßer outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 22277b57314SStefan Eßer mode1res = inl(CONF1_ADDR_PORT); 223287911bdSStefan Eßer outl(CONF1_ADDR_PORT, oldval1); 22477b57314SStefan Eßer 22577b57314SStefan Eßer if (bootverbose) 2265bec6157SStefan Eßer printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", 22777b57314SStefan Eßer mode1res, CONF1_ENABLE_CHK1); 22877b57314SStefan Eßer 229c7483249SStefan Eßer if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 2305bec6157SStefan Eßer if (pci_cfgcheck(32)) 2315bec6157SStefan Eßer return (cfgmech); 232287911bdSStefan Eßer } 2335bec6157SStefan Eßer } 23477b57314SStefan Eßer 235287911bdSStefan Eßer oldval2 = inb(CONF2_ENABLE_PORT); 236287911bdSStefan Eßer 237287911bdSStefan Eßer if (bootverbose) { 2385bec6157SStefan Eßer printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 2395bec6157SStefan Eßer oldval2); 240287911bdSStefan Eßer } 241287911bdSStefan Eßer 242287911bdSStefan Eßer if ((oldval2 & 0xf0) == 0) { 243c7483249SStefan Eßer 2445bec6157SStefan Eßer cfgmech = 2; 2455bec6157SStefan Eßer devmax = 16; 24677b57314SStefan Eßer 247287911bdSStefan Eßer outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 248287911bdSStefan Eßer mode2res = inb(CONF2_ENABLE_PORT); 249287911bdSStefan Eßer outb(CONF2_ENABLE_PORT, oldval2); 250287911bdSStefan Eßer 251287911bdSStefan Eßer if (bootverbose) 2525bec6157SStefan Eßer printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 253287911bdSStefan Eßer mode2res, CONF2_ENABLE_CHK); 254287911bdSStefan Eßer 255287911bdSStefan Eßer if (mode2res == CONF2_ENABLE_RES) { 256287911bdSStefan Eßer if (bootverbose) 2575bec6157SStefan Eßer printf("pci_open(2a):\tnow trying mechanism 2\n"); 258287911bdSStefan Eßer 2595bec6157SStefan Eßer if (pci_cfgcheck(16)) 2605bec6157SStefan Eßer return (cfgmech); 261287911bdSStefan Eßer } 262287911bdSStefan Eßer } 26377b57314SStefan Eßer 2645bec6157SStefan Eßer cfgmech = 0; 2655bec6157SStefan Eßer devmax = 0; 2665bec6157SStefan Eßer return (cfgmech); 267ac19f918SStefan Eßer } 2688dc26439SPeter Wemm 2698dc26439SPeter Wemm static devclass_t pcib_devclass; 2708dc26439SPeter Wemm 2718dc26439SPeter Wemm static int 2728dc26439SPeter Wemm nexus_pcib_probe(device_t dev) 2738dc26439SPeter Wemm { 2748dc26439SPeter Wemm if (pci_cfgopen() != 0) { 2758dc26439SPeter Wemm device_set_desc(dev, "PCI host bus adapter"); 2768dc26439SPeter Wemm 2778dc26439SPeter Wemm device_add_child(dev, "pci", 0, 0); 2788dc26439SPeter Wemm return 0; 2798dc26439SPeter Wemm } 2808dc26439SPeter Wemm return ENXIO; 2818dc26439SPeter Wemm } 2828dc26439SPeter Wemm 2838dc26439SPeter Wemm static device_method_t nexus_pcib_methods[] = { 2848dc26439SPeter Wemm /* Device interface */ 2858dc26439SPeter Wemm DEVMETHOD(device_probe, nexus_pcib_probe), 2868dc26439SPeter Wemm DEVMETHOD(device_attach, bus_generic_attach), 2878dc26439SPeter Wemm DEVMETHOD(device_shutdown, bus_generic_shutdown), 2888dc26439SPeter Wemm DEVMETHOD(device_suspend, bus_generic_suspend), 2898dc26439SPeter Wemm DEVMETHOD(device_resume, bus_generic_resume), 2908dc26439SPeter Wemm 2918dc26439SPeter Wemm /* Bus interface */ 2928dc26439SPeter Wemm DEVMETHOD(bus_print_child, bus_generic_print_child), 2938dc26439SPeter Wemm DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), 2948dc26439SPeter Wemm DEVMETHOD(bus_release_resource, bus_generic_release_resource), 2958dc26439SPeter Wemm DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 2968dc26439SPeter Wemm DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 2978dc26439SPeter Wemm DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 2988dc26439SPeter Wemm DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 2998dc26439SPeter Wemm 3008dc26439SPeter Wemm { 0, 0 } 3018dc26439SPeter Wemm }; 3028dc26439SPeter Wemm 3038dc26439SPeter Wemm static driver_t nexus_pcib_driver = { 3048dc26439SPeter Wemm "pcib", 3058dc26439SPeter Wemm nexus_pcib_methods, 3068dc26439SPeter Wemm 1, 3078dc26439SPeter Wemm }; 3088dc26439SPeter Wemm 3098dc26439SPeter Wemm DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0); 310