netbsd_pci.c revision a01999d7
14f5e7dd7Smrg/* 24f5e7dd7Smrg * Copyright (c) 2008 Juan Romero Pardines 34f5e7dd7Smrg * Copyright (c) 2008 Mark Kettenis 48ce851d2Smacallan * Copyright (c) 2009 Michael Lorenz 54f5e7dd7Smrg * 64f5e7dd7Smrg * Permission to use, copy, modify, and distribute this software for any 74f5e7dd7Smrg * purpose with or without fee is hereby granted, provided that the above 84f5e7dd7Smrg * copyright notice and this permission notice appear in all copies. 94f5e7dd7Smrg * 104f5e7dd7Smrg * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 114f5e7dd7Smrg * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 124f5e7dd7Smrg * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 134f5e7dd7Smrg * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 144f5e7dd7Smrg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 154f5e7dd7Smrg * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 164f5e7dd7Smrg * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 174f5e7dd7Smrg */ 184f5e7dd7Smrg 1914e6f28eSmrg#ifdef HAVE_CONFIG_H 2014e6f28eSmrg#include "config.h" 2114e6f28eSmrg#endif 2214e6f28eSmrg 234f5e7dd7Smrg#include <sys/param.h> 244f5e7dd7Smrg#include <sys/ioctl.h> 254f5e7dd7Smrg#include <sys/mman.h> 264f5e7dd7Smrg#include <sys/types.h> 274f5e7dd7Smrg 2822df1cdfSmrg#ifdef HAVE_MTRR 294f5e7dd7Smrg#include <machine/sysarch.h> 304f5e7dd7Smrg#include <machine/mtrr.h> 319266c31dSmrg#ifdef _X86_SYSARCH_L 329266c31dSmrg/* NetBSD 5.x and newer */ 3322df1cdfSmrg#define netbsd_set_mtrr(mr, num) _X86_SYSARCH_L(set_mtrr)(mr, num) 349266c31dSmrg#else 359266c31dSmrg/* NetBSD 4.x and older */ 369266c31dSmrg#ifdef __i386__ 379266c31dSmrg#define netbsd_set_mtrr(mr, num) i386_set_mtrr((mr), (num)) 389266c31dSmrg#endif 399266c31dSmrg#ifdef __amd64__ 409266c31dSmrg#define netbsd_set_mtrr(mr, num) x86_64_set_mtrr((mr), (num)) 419266c31dSmrg#endif 429266c31dSmrg#endif 4322df1cdfSmrg#endif 444f5e7dd7Smrg 4522df1cdfSmrg#include <dev/pci/pcidevs.h> 464f5e7dd7Smrg#include <dev/pci/pciio.h> 474f5e7dd7Smrg#include <dev/pci/pcireg.h> 484f5e7dd7Smrg 494f5e7dd7Smrg#include <errno.h> 504f5e7dd7Smrg#include <fcntl.h> 514f5e7dd7Smrg#include <stdio.h> 524f5e7dd7Smrg#include <stdlib.h> 534f5e7dd7Smrg#include <string.h> 544f5e7dd7Smrg#include <unistd.h> 554f5e7dd7Smrg 564f5e7dd7Smrg 5722df1cdfSmrg#include <pci.h> 586209fe3aScegger#include <dev/wscons/wsconsio.h> 5922df1cdfSmrg 604f5e7dd7Smrg#include "pciaccess.h" 614f5e7dd7Smrg#include "pciaccess_private.h" 624f5e7dd7Smrg 638ce851d2Smacallantypedef struct _pcibus { 648ce851d2Smacallan int fd; /* /dev/pci* */ 658ce851d2Smacallan int num; /* bus number */ 668ce851d2Smacallan int maxdevs; /* maximum number of devices */ 678ce851d2Smacallan} PciBus; 689e884b7eSchristos 6956db2c9dSjmcneillstatic PciBus *buses = NULL; /* indexed by pci_device.domain */ 708ce851d2Smacallanstatic int nbuses = 0; /* number of buses found */ 718ce851d2Smacallan 728ce851d2Smacallan/* 738ce851d2Smacallan * NetBSD's userland has a /dev/pci* entry for each bus but userland has no way 748ce851d2Smacallan * to tell if a bus is a subordinate of another one or if it's on a different 758ce851d2Smacallan * host bridge. On some architectures ( macppc for example ) all root buses have 768ce851d2Smacallan * bus number 0 but on sparc64 for example the two roots in an Ultra60 have 778ce851d2Smacallan * different bus numbers - one is 0 and the other 128. 788ce851d2Smacallan * With each /dev/pci* we can map everything on the same root and we can also 798ce851d2Smacallan * see all devices on the same root, trying to do that causes problems though: 808ce851d2Smacallan * - since we can't tell which /dev/pci* is a subordinate we would find some 818ce851d2Smacallan * devices more than once 828ce851d2Smacallan * - we would have to guess subordinate bus numbers which is a waste of time 838ce851d2Smacallan * since we can ask each /dev/pci* for its bus number so we can scan only the 848ce851d2Smacallan * buses we know exist, not all 256 which may exist in each domain. 858ce851d2Smacallan * - some bus_space_mmap() methods may limit mappings to address ranges which 868ce851d2Smacallan * belong to known devices on that bus only. 878ce851d2Smacallan * Each host bridge may or may not have its own IO range, to avoid guesswork 888ce851d2Smacallan * here each /dev/pci* will let userland map its appropriate IO range at 898ce851d2Smacallan * PCI_MAGIC_IO_RANGE if defined in <machine/param.h> 908ce851d2Smacallan * With all this we should be able to use any PCI graphics device on any PCI 918ce851d2Smacallan * bus on any architecture as long as Xorg has a driver, without allowing 928ce851d2Smacallan * arbitrary mappings via /dev/mem and without userland having to know or care 934b4d14a9Swiz * about translating bus addresses to physical addresses or the other way 948ce851d2Smacallan * around. 958ce851d2Smacallan */ 964f5e7dd7Smrg 974f5e7dd7Smrgstatic int 988ce851d2Smacallanpci_read(int domain, int bus, int dev, int func, uint32_t reg, uint32_t *val) 994f5e7dd7Smrg{ 10022df1cdfSmrg uint32_t rval; 1014f5e7dd7Smrg 10256db2c9dSjmcneill if ((domain < 0) || (domain >= nbuses)) 1038ce851d2Smacallan return -1; 1048ce851d2Smacallan 1058ce851d2Smacallan if (pcibus_conf_read(buses[domain].fd, (unsigned int)bus, 1068ce851d2Smacallan (unsigned int)dev, (unsigned int)func, reg, &rval) == -1) 10722df1cdfSmrg return (-1); 1084f5e7dd7Smrg 10922df1cdfSmrg *val = rval; 1104f5e7dd7Smrg 1114f5e7dd7Smrg return 0; 1124f5e7dd7Smrg} 1134f5e7dd7Smrg 1144f5e7dd7Smrgstatic int 1158ce851d2Smacallanpci_write(int domain, int bus, int dev, int func, uint32_t reg, uint32_t val) 1164f5e7dd7Smrg{ 1178ce851d2Smacallan 11856db2c9dSjmcneill if ((domain < 0) || (domain >= nbuses)) 1198ce851d2Smacallan return -1; 1208ce851d2Smacallan 1218ce851d2Smacallan return pcibus_conf_write(buses[domain].fd, (unsigned int)bus, 1228ce851d2Smacallan (unsigned int)dev, (unsigned int)func, reg, val); 1234f5e7dd7Smrg} 1244f5e7dd7Smrg 1254f5e7dd7Smrgstatic int 1268ce851d2Smacallanpci_nfuncs(int domain, int bus, int dev) 1274f5e7dd7Smrg{ 1284f5e7dd7Smrg uint32_t hdr; 1294f5e7dd7Smrg 13056db2c9dSjmcneill if ((domain < 0) || (domain >= nbuses)) 1318ce851d2Smacallan return -1; 1328ce851d2Smacallan 1338ce851d2Smacallan if (pci_read(domain, bus, dev, 0, PCI_BHLC_REG, &hdr) != 0) 1344f5e7dd7Smrg return -1; 1354f5e7dd7Smrg 1364f5e7dd7Smrg return (PCI_HDRTYPE_MULTIFN(hdr) ? 8 : 1); 1374f5e7dd7Smrg} 1384f5e7dd7Smrg 1399e884b7eSchristos/*ARGSUSED*/ 1404f5e7dd7Smrgstatic int 1414f5e7dd7Smrgpci_device_netbsd_map_range(struct pci_device *dev, 1424f5e7dd7Smrg struct pci_device_mapping *map) 1434f5e7dd7Smrg{ 14422df1cdfSmrg#ifdef HAVE_MTRR 14522df1cdfSmrg struct mtrr m; 14622df1cdfSmrg int n = 1; 14722df1cdfSmrg#endif 1488ce851d2Smacallan int prot, ret = 0; 1494f5e7dd7Smrg 15022df1cdfSmrg prot = PROT_READ; 1514f5e7dd7Smrg 1524f5e7dd7Smrg if (map->flags & PCI_DEV_MAP_FLAG_WRITABLE) 1534f5e7dd7Smrg prot |= PROT_WRITE; 154a5d2dc35Sjmcneill map->memory = mmap(NULL, (size_t)map->size, prot, MAP_SHARED, 1558ce851d2Smacallan buses[dev->domain].fd, (off_t)map->base); 1564f5e7dd7Smrg if (map->memory == MAP_FAILED) 1574f5e7dd7Smrg return errno; 1584f5e7dd7Smrg 15922df1cdfSmrg#ifdef HAVE_MTRR 16022df1cdfSmrg memset(&m, 0, sizeof(m)); 16122df1cdfSmrg 1624f5e7dd7Smrg /* No need to set an MTRR if it's the default mode. */ 1634f5e7dd7Smrg if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || 1644f5e7dd7Smrg (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) { 165a5d2dc35Sjmcneill m.base = map->base; 16622df1cdfSmrg m.flags = MTRR_VALID | MTRR_PRIVATE; 167a5d2dc35Sjmcneill m.len = map->size; 16822df1cdfSmrg m.owner = getpid(); 169a5d2dc35Sjmcneill if (map->flags & PCI_DEV_MAP_FLAG_CACHABLE) 17022df1cdfSmrg m.type = MTRR_TYPE_WB; 1714f5e7dd7Smrg if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) 17222df1cdfSmrg m.type = MTRR_TYPE_WC; 17322df1cdfSmrg 174a5d2dc35Sjmcneill if ((netbsd_set_mtrr(&m, &n)) == -1) { 175a5d2dc35Sjmcneill fprintf(stderr, "mtrr set failed: %s\n", 176a5d2dc35Sjmcneill strerror(errno)); 177a5d2dc35Sjmcneill } 1784f5e7dd7Smrg } 17922df1cdfSmrg#endif 1804f5e7dd7Smrg 18122df1cdfSmrg return ret; 1824f5e7dd7Smrg} 1834f5e7dd7Smrg 1844f5e7dd7Smrgstatic int 1854f5e7dd7Smrgpci_device_netbsd_unmap_range(struct pci_device *dev, 1864f5e7dd7Smrg struct pci_device_mapping *map) 1874f5e7dd7Smrg{ 18822df1cdfSmrg#ifdef HAVE_MTRR 18922df1cdfSmrg struct mtrr m; 19022df1cdfSmrg int n = 1; 19122df1cdfSmrg 19222df1cdfSmrg memset(&m, 0, sizeof(m)); 1934f5e7dd7Smrg 1944f5e7dd7Smrg if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || 1954f5e7dd7Smrg (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) { 19622df1cdfSmrg m.base = map->base; 19722df1cdfSmrg m.flags = 0; 198a5d2dc35Sjmcneill m.len = map->size; 19922df1cdfSmrg m.type = MTRR_TYPE_UC; 20022df1cdfSmrg (void)netbsd_set_mtrr(&m, &n); 2014f5e7dd7Smrg } 20222df1cdfSmrg#endif 2034f5e7dd7Smrg 2044f5e7dd7Smrg return pci_device_generic_unmap_range(dev, map); 2054f5e7dd7Smrg} 2064f5e7dd7Smrg 2074f5e7dd7Smrgstatic int 2084f5e7dd7Smrgpci_device_netbsd_read(struct pci_device *dev, void *data, 2094f5e7dd7Smrg pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) 2104f5e7dd7Smrg{ 21122df1cdfSmrg u_int reg, rval; 2124f5e7dd7Smrg 2134f5e7dd7Smrg *bytes_read = 0; 2144f5e7dd7Smrg while (size > 0) { 2159e884b7eSchristos size_t toread = MIN(size, 4 - (offset & 0x3)); 2164f5e7dd7Smrg 2179e884b7eSchristos reg = (u_int)(offset & ~0x3); 2184f5e7dd7Smrg 2198ce851d2Smacallan if ((pcibus_conf_read(buses[dev->domain].fd, 2208ce851d2Smacallan (unsigned int)dev->bus, (unsigned int)dev->dev, 2218ce851d2Smacallan (unsigned int)dev->func, reg, &rval)) == -1) 2224f5e7dd7Smrg return errno; 2234f5e7dd7Smrg 22422df1cdfSmrg rval = htole32(rval); 22522df1cdfSmrg rval >>= ((offset & 0x3) * 8); 2264f5e7dd7Smrg 22722df1cdfSmrg memcpy(data, &rval, toread); 2284f5e7dd7Smrg 2294f5e7dd7Smrg offset += toread; 2304f5e7dd7Smrg data = (char *)data + toread; 2314f5e7dd7Smrg size -= toread; 2324f5e7dd7Smrg *bytes_read += toread; 2334f5e7dd7Smrg } 2344f5e7dd7Smrg 2354f5e7dd7Smrg return 0; 2364f5e7dd7Smrg} 2374f5e7dd7Smrg 2384f5e7dd7Smrgstatic int 2394f5e7dd7Smrgpci_device_netbsd_write(struct pci_device *dev, const void *data, 2404f5e7dd7Smrg pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written) 2414f5e7dd7Smrg{ 24222df1cdfSmrg u_int reg, val; 2434f5e7dd7Smrg 2444f5e7dd7Smrg if ((offset % 4) != 0 || (size % 4) != 0) 2454f5e7dd7Smrg return EINVAL; 2464f5e7dd7Smrg 2474f5e7dd7Smrg *bytes_written = 0; 2484f5e7dd7Smrg while (size > 0) { 2499e884b7eSchristos reg = (u_int)offset; 25022df1cdfSmrg memcpy(&val, data, 4); 2514f5e7dd7Smrg 2528ce851d2Smacallan if ((pcibus_conf_write(buses[dev->domain].fd, 2538ce851d2Smacallan (unsigned int)dev->bus, (unsigned int)dev->dev, 2548ce851d2Smacallan (unsigned int)dev->func, reg, val)) == -1) 2554f5e7dd7Smrg return errno; 2564f5e7dd7Smrg 2574f5e7dd7Smrg offset += 4; 2589e884b7eSchristos data = (const char *)data + 4; 2594f5e7dd7Smrg size -= 4; 2604f5e7dd7Smrg *bytes_written += 4; 2614f5e7dd7Smrg } 2624f5e7dd7Smrg 2634f5e7dd7Smrg return 0; 2644f5e7dd7Smrg} 2654f5e7dd7Smrg 2669266c31dSmrg#if defined(WSDISPLAYIO_GET_BUSID) 2676209fe3aSceggerstatic int 2686209fe3aSceggerpci_device_netbsd_boot_vga(struct pci_device *dev) 2696209fe3aScegger{ 2706209fe3aScegger int ret; 2716209fe3aScegger struct wsdisplayio_bus_id busid; 2726209fe3aScegger int fd; 2736209fe3aScegger 2746209fe3aScegger fd = open("/dev/ttyE0", O_RDONLY); 2756209fe3aScegger if (fd == -1) { 2766209fe3aScegger fprintf(stderr, "failed to open /dev/ttyE0: %s\n", 2776209fe3aScegger strerror(errno)); 2786209fe3aScegger return 0; 2796209fe3aScegger } 2806209fe3aScegger 2816209fe3aScegger ret = ioctl(fd, WSDISPLAYIO_GET_BUSID, &busid); 2826209fe3aScegger close(fd); 2836209fe3aScegger if (ret == -1) { 2846209fe3aScegger fprintf(stderr, "ioctl WSDISPLAYIO_GET_BUSID failed: %s\n", 2856209fe3aScegger strerror(errno)); 2866209fe3aScegger return 0; 2876209fe3aScegger } 2886209fe3aScegger 2896209fe3aScegger if (busid.bus_type != WSDISPLAYIO_BUS_PCI) 2906209fe3aScegger return 0; 2916209fe3aScegger 2926209fe3aScegger if (busid.ubus.pci.domain != dev->domain) 2936209fe3aScegger return 0; 2946209fe3aScegger if (busid.ubus.pci.bus != dev->bus) 2956209fe3aScegger return 0; 2966209fe3aScegger if (busid.ubus.pci.device != dev->dev) 2976209fe3aScegger return 0; 2986209fe3aScegger if (busid.ubus.pci.function != dev->func) 2996209fe3aScegger return 0; 3006209fe3aScegger 3016209fe3aScegger return 1; 3026209fe3aScegger} 3039266c31dSmrg#endif 304f6db1a1aSmrg 3054f5e7dd7Smrgstatic void 3064f5e7dd7Smrgpci_system_netbsd_destroy(void) 3074f5e7dd7Smrg{ 3088ce851d2Smacallan int i; 3098ce851d2Smacallan 3108ce851d2Smacallan for (i = 0; i < nbuses; i++) { 3118ce851d2Smacallan close(buses[i].fd); 3128ce851d2Smacallan } 3134f5e7dd7Smrg free(pci_sys); 3144f5e7dd7Smrg pci_sys = NULL; 3154f5e7dd7Smrg} 3164f5e7dd7Smrg 3174f5e7dd7Smrgstatic int 3184f5e7dd7Smrgpci_device_netbsd_probe(struct pci_device *device) 3194f5e7dd7Smrg{ 3209e884b7eSchristos struct pci_device_private *priv = 3219e884b7eSchristos (struct pci_device_private *)(void *)device; 3224f5e7dd7Smrg struct pci_mem_region *region; 3234f5e7dd7Smrg uint64_t reg64, size64; 3244f5e7dd7Smrg uint32_t bar, reg, size; 3258ce851d2Smacallan int bus, dev, func, err, domain; 3264f5e7dd7Smrg 3278ce851d2Smacallan domain = device->domain; 3284f5e7dd7Smrg bus = device->bus; 3294f5e7dd7Smrg dev = device->dev; 3304f5e7dd7Smrg func = device->func; 3314f5e7dd7Smrg 3326c7645b9Smrg /* Enable the device if necessary */ 3336c7645b9Smrg err = pci_read(domain, bus, dev, func, PCI_COMMAND_STATUS_REG, ®); 3346c7645b9Smrg if (err) 3356c7645b9Smrg return err; 336f8d5af14Smacallan#ifndef AVOID_DEVICE_ENABLE 3376c7645b9Smrg if ((reg & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE)) != 3386c7645b9Smrg (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE)) { 3396c7645b9Smrg reg |= PCI_COMMAND_IO_ENABLE | 3406c7645b9Smrg PCI_COMMAND_MEM_ENABLE | 3416c7645b9Smrg PCI_COMMAND_MASTER_ENABLE; 3426c7645b9Smrg err = pci_write(domain, bus, dev, func, PCI_COMMAND_STATUS_REG, 3436c7645b9Smrg reg); 3446c7645b9Smrg if (err) 3456c7645b9Smrg return err; 3466c7645b9Smrg } 347f8d5af14Smacallan#endif 3488ce851d2Smacallan err = pci_read(domain, bus, dev, func, PCI_BHLC_REG, ®); 3494f5e7dd7Smrg if (err) 3504f5e7dd7Smrg return err; 3514f5e7dd7Smrg 3524f5e7dd7Smrg priv->header_type = PCI_HDRTYPE_TYPE(reg); 3534f5e7dd7Smrg if (priv->header_type != 0) 3544f5e7dd7Smrg return 0; 3554f5e7dd7Smrg 3564f5e7dd7Smrg region = device->regions; 3574f5e7dd7Smrg for (bar = PCI_MAPREG_START; bar < PCI_MAPREG_END; 3584f5e7dd7Smrg bar += sizeof(uint32_t), region++) { 3598ce851d2Smacallan err = pci_read(domain, bus, dev, func, bar, ®); 3604f5e7dd7Smrg if (err) 3614f5e7dd7Smrg return err; 3624f5e7dd7Smrg 3634f5e7dd7Smrg /* Probe the size of the region. */ 3648ce851d2Smacallan err = pci_write(domain, bus, dev, func, bar, (unsigned int)~0); 3654f5e7dd7Smrg if (err) 3664f5e7dd7Smrg return err; 3678ce851d2Smacallan pci_read(domain, bus, dev, func, bar, &size); 3688ce851d2Smacallan pci_write(domain, bus, dev, func, bar, reg); 3694f5e7dd7Smrg 3704f5e7dd7Smrg if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) { 3714f5e7dd7Smrg region->is_IO = 1; 3724f5e7dd7Smrg region->base_addr = PCI_MAPREG_IO_ADDR(reg); 3734f5e7dd7Smrg region->size = PCI_MAPREG_IO_SIZE(size); 3744f5e7dd7Smrg } else { 3754f5e7dd7Smrg if (PCI_MAPREG_MEM_PREFETCHABLE(reg)) 3764f5e7dd7Smrg region->is_prefetchable = 1; 3774f5e7dd7Smrg switch(PCI_MAPREG_MEM_TYPE(reg)) { 3784f5e7dd7Smrg case PCI_MAPREG_MEM_TYPE_32BIT: 3794f5e7dd7Smrg case PCI_MAPREG_MEM_TYPE_32BIT_1M: 3804f5e7dd7Smrg region->base_addr = PCI_MAPREG_MEM_ADDR(reg); 3814f5e7dd7Smrg region->size = PCI_MAPREG_MEM_SIZE(size); 3824f5e7dd7Smrg break; 3834f5e7dd7Smrg case PCI_MAPREG_MEM_TYPE_64BIT: 3844f5e7dd7Smrg region->is_64 = 1; 3854f5e7dd7Smrg 3864f5e7dd7Smrg reg64 = reg; 3874f5e7dd7Smrg size64 = size; 3884f5e7dd7Smrg 3894f5e7dd7Smrg bar += sizeof(uint32_t); 3904f5e7dd7Smrg 3918ce851d2Smacallan err = pci_read(domain, bus, dev, func, bar, ®); 3924f5e7dd7Smrg if (err) 3934f5e7dd7Smrg return err; 3944f5e7dd7Smrg reg64 |= (uint64_t)reg << 32; 3954f5e7dd7Smrg 3968ce851d2Smacallan err = pci_write(domain, bus, dev, func, bar, 3979e884b7eSchristos (unsigned int)~0); 3984f5e7dd7Smrg if (err) 3994f5e7dd7Smrg return err; 4008ce851d2Smacallan pci_read(domain, bus, dev, func, bar, &size); 4018ce851d2Smacallan pci_write(domain, bus, dev, func, bar, 4029e884b7eSchristos (unsigned int)(reg64 >> 32)); 4034f5e7dd7Smrg size64 |= (uint64_t)size << 32; 4044f5e7dd7Smrg 4059e884b7eSchristos region->base_addr = 4069e884b7eSchristos (unsigned long)PCI_MAPREG_MEM64_ADDR(reg64); 4079e884b7eSchristos region->size = 4089e884b7eSchristos (unsigned long)PCI_MAPREG_MEM64_SIZE(size64); 4094f5e7dd7Smrg region++; 4104f5e7dd7Smrg break; 4114f5e7dd7Smrg } 4124f5e7dd7Smrg } 4134f5e7dd7Smrg } 4144f5e7dd7Smrg 41537a6a21eSmrg /* Probe expansion ROM if present */ 41637a6a21eSmrg err = pci_read(domain, bus, dev, func, PCI_MAPREG_ROM, ®); 41737a6a21eSmrg if (err) 41837a6a21eSmrg return err; 41937a6a21eSmrg if (reg != 0) { 42037a6a21eSmrg err = pci_write(domain, bus, dev, func, PCI_MAPREG_ROM, 42137a6a21eSmrg (uint32_t)(~PCI_MAPREG_ROM_ENABLE)); 42237a6a21eSmrg if (err) 42337a6a21eSmrg return err; 42437a6a21eSmrg pci_read(domain, bus, dev, func, PCI_MAPREG_ROM, &size); 42537a6a21eSmrg pci_write(domain, bus, dev, func, PCI_MAPREG_ROM, reg); 42637a6a21eSmrg if ((reg & PCI_MAPREG_MEM_ADDR_MASK) != 0) { 42737a6a21eSmrg priv->rom_base = reg & PCI_MAPREG_MEM_ADDR_MASK; 42837a6a21eSmrg device->rom_size = -(size & PCI_MAPREG_MEM_ADDR_MASK); 42937a6a21eSmrg } 43037a6a21eSmrg } 43137a6a21eSmrg 4324f5e7dd7Smrg return 0; 4334f5e7dd7Smrg} 4344f5e7dd7Smrg 4359e884b7eSchristos/** 4369e884b7eSchristos * Read a VGA rom using the 0xc0000 mapping. 4379e884b7eSchristos * 4389e884b7eSchristos * This function should be extended to handle access through PCI resources, 4399e884b7eSchristos * which should be more reliable when available. 4409e884b7eSchristos */ 4419e884b7eSchristosstatic int 4429e884b7eSchristospci_device_netbsd_read_rom(struct pci_device *dev, void *buffer) 4439e884b7eSchristos{ 4449e884b7eSchristos struct pci_device_private *priv = (struct pci_device_private *)(void *)dev; 4459e884b7eSchristos void *bios; 4469e884b7eSchristos pciaddr_t rom_base; 4479e884b7eSchristos size_t rom_size; 4489e884b7eSchristos uint32_t bios_val, command_val; 44974c741d0Smacallan int pci_rom; 4509e884b7eSchristos 4519e884b7eSchristos if (((priv->base.device_class >> 16) & 0xff) != PCI_CLASS_DISPLAY || 4529e884b7eSchristos ((priv->base.device_class >> 8) & 0xff) != PCI_SUBCLASS_DISPLAY_VGA) 4539e884b7eSchristos return ENOSYS; 4549e884b7eSchristos 4559e884b7eSchristos if (priv->rom_base == 0) { 4569e884b7eSchristos#if defined(__amd64__) || defined(__i386__) 45737a6a21eSmrg /* 45837a6a21eSmrg * We need a way to detect when this isn't the console and reject 45937a6a21eSmrg * this request outright. 46037a6a21eSmrg */ 4619e884b7eSchristos rom_base = 0xc0000; 4629e884b7eSchristos rom_size = 0x10000; 4639e884b7eSchristos pci_rom = 0; 4649e884b7eSchristos#else 4659e884b7eSchristos return ENOSYS; 4669e884b7eSchristos#endif 4679e884b7eSchristos } else { 4689e884b7eSchristos rom_base = priv->rom_base; 4699e884b7eSchristos rom_size = dev->rom_size; 4709e884b7eSchristos pci_rom = 1; 4718ce851d2Smacallan if ((pcibus_conf_read(buses[dev->domain].fd, (unsigned int)dev->bus, 4729e884b7eSchristos (unsigned int)dev->dev, (unsigned int)dev->func, 4738ce851d2Smacallan PCI_COMMAND_STATUS_REG, &command_val)) == -1) 4749e884b7eSchristos return errno; 4758ce851d2Smacallan if ((command_val & PCI_COMMAND_MEM_ENABLE) == 0) { 4768ce851d2Smacallan if ((pcibus_conf_write(buses[dev->domain].fd, 4778ce851d2Smacallan (unsigned int)dev->bus, (unsigned int)dev->dev, 4788ce851d2Smacallan (unsigned int)dev->func, PCI_COMMAND_STATUS_REG, 4798ce851d2Smacallan command_val | PCI_COMMAND_MEM_ENABLE)) == -1) 4809e884b7eSchristos return errno; 4819e884b7eSchristos } 4828ce851d2Smacallan if ((pcibus_conf_read(buses[dev->domain].fd, (unsigned int)dev->bus, 4839e884b7eSchristos (unsigned int)dev->dev, (unsigned int)dev->func, 4848ce851d2Smacallan PCI_MAPREG_ROM, &bios_val)) == -1) 4859e884b7eSchristos return errno; 4868ce851d2Smacallan if ((bios_val & PCI_MAPREG_ROM_ENABLE) == 0) { 4878ce851d2Smacallan if ((pcibus_conf_write(buses[dev->domain].fd, 4888ce851d2Smacallan (unsigned int)dev->bus, 4899e884b7eSchristos (unsigned int)dev->dev, (unsigned int)dev->func, 4908ce851d2Smacallan PCI_MAPREG_ROM, bios_val | PCI_MAPREG_ROM_ENABLE)) == -1) 4919e884b7eSchristos return errno; 4929e884b7eSchristos } 4939e884b7eSchristos } 4949e884b7eSchristos 4954b4d14a9Swiz fprintf(stderr, "Using rom_base = 0x%lx 0x%lx (pci_rom=%d)\n", 49674c741d0Smacallan (long)rom_base, (long)rom_size, pci_rom); 4979e884b7eSchristos 4984b4d14a9Swiz bios = mmap(NULL, rom_size, PROT_READ, MAP_SHARED, buses[dev->domain].fd, 49974c741d0Smacallan (off_t)rom_base); 5009e884b7eSchristos if (bios == MAP_FAILED) { 5019e884b7eSchristos int serrno = errno; 5029e884b7eSchristos return serrno; 5039e884b7eSchristos } 5049e884b7eSchristos 5059e884b7eSchristos memcpy(buffer, bios, rom_size); 5069e884b7eSchristos 5079e884b7eSchristos munmap(bios, rom_size); 5089e884b7eSchristos 5099e884b7eSchristos if (pci_rom) { 5108ce851d2Smacallan if ((command_val & PCI_COMMAND_MEM_ENABLE) == 0) { 5118ce851d2Smacallan if ((pcibus_conf_write(buses[dev->domain].fd, 5128ce851d2Smacallan (unsigned int)dev->bus, 5139e884b7eSchristos (unsigned int)dev->dev, (unsigned int)dev->func, 5148ce851d2Smacallan PCI_COMMAND_STATUS_REG, command_val)) == -1) 5159e884b7eSchristos return errno; 5169e884b7eSchristos } 5178ce851d2Smacallan if ((bios_val & PCI_MAPREG_ROM_ENABLE) == 0) { 5188ce851d2Smacallan if ((pcibus_conf_write(buses[dev->domain].fd, 5198ce851d2Smacallan (unsigned int)dev->bus, 5209e884b7eSchristos (unsigned int)dev->dev, (unsigned int)dev->func, 5218ce851d2Smacallan PCI_MAPREG_ROM, bios_val)) == -1) 5229e884b7eSchristos return errno; 5239e884b7eSchristos } 5249e884b7eSchristos } 5259e884b7eSchristos 5269e884b7eSchristos return 0; 5279e884b7eSchristos} 5289e884b7eSchristos 5299266c31dSmrg#if defined(__i386__) || defined(__amd64__) 5309266c31dSmrg#include <machine/sysarch.h> 5319266c31dSmrg 5329266c31dSmrg/* 5339266c31dSmrg * Functions to provide access to x86 programmed I/O instructions. 5349266c31dSmrg * 5359266c31dSmrg * The in[bwl]() and out[bwl]() functions are split into two varieties: one to 5369266c31dSmrg * use a small, constant, 8-bit port number, and another to use a large or 5379266c31dSmrg * variable port number. The former can be compiled as a smaller instruction. 5389266c31dSmrg */ 5399266c31dSmrg 5409266c31dSmrg 5419266c31dSmrg#ifdef __OPTIMIZE__ 5429266c31dSmrg 5439266c31dSmrg#define __use_immediate_port(port) \ 5449266c31dSmrg (__builtin_constant_p((port)) && (port) < 0x100) 5459266c31dSmrg 5469266c31dSmrg#else 5479266c31dSmrg 5489266c31dSmrg#define __use_immediate_port(port) 0 5499266c31dSmrg 5509266c31dSmrg#endif 5519266c31dSmrg 5529266c31dSmrg 5539266c31dSmrg#define inb(port) \ 5549266c31dSmrg (/* CONSTCOND */ __use_immediate_port(port) ? __inbc(port) : __inb(port)) 5559266c31dSmrg 5569266c31dSmrgstatic __inline u_int8_t 5579266c31dSmrg__inbc(unsigned port) 5589266c31dSmrg{ 5599266c31dSmrg u_int8_t data; 5609266c31dSmrg __asm __volatile("inb %w1,%0" : "=a" (data) : "id" (port)); 5619266c31dSmrg return data; 5629266c31dSmrg} 5639266c31dSmrg 5649266c31dSmrgstatic __inline u_int8_t 5659266c31dSmrg__inb(unsigned port) 5669266c31dSmrg{ 5679266c31dSmrg u_int8_t data; 5689266c31dSmrg __asm __volatile("inb %w1,%0" : "=a" (data) : "d" (port)); 5699266c31dSmrg return data; 5709266c31dSmrg} 5719266c31dSmrg 5729266c31dSmrgstatic __inline void 5739266c31dSmrginsb(unsigned port, void *addr, int cnt) 5749266c31dSmrg{ 5759266c31dSmrg void *dummy1; 5769266c31dSmrg int dummy2; 5779266c31dSmrg __asm __volatile("cld\n\trepne\n\tinsb" : 5789266c31dSmrg "=D" (dummy1), "=c" (dummy2) : 5799266c31dSmrg "d" (port), "0" (addr), "1" (cnt) : 5809266c31dSmrg "memory"); 5819266c31dSmrg} 5829266c31dSmrg 5839266c31dSmrg#define inw(port) \ 5849266c31dSmrg (/* CONSTCOND */ __use_immediate_port(port) ? __inwc(port) : __inw(port)) 5859266c31dSmrg 5869266c31dSmrgstatic __inline u_int16_t 5879266c31dSmrg__inwc(unsigned port) 5889266c31dSmrg{ 5899266c31dSmrg u_int16_t data; 5909266c31dSmrg __asm __volatile("inw %w1,%0" : "=a" (data) : "id" (port)); 5919266c31dSmrg return data; 5929266c31dSmrg} 5939266c31dSmrg 5949266c31dSmrgstatic __inline u_int16_t 5959266c31dSmrg__inw(unsigned port) 5969266c31dSmrg{ 5979266c31dSmrg u_int16_t data; 5989266c31dSmrg __asm __volatile("inw %w1,%0" : "=a" (data) : "d" (port)); 5999266c31dSmrg return data; 6009266c31dSmrg} 6019266c31dSmrg 6029266c31dSmrgstatic __inline void 6039266c31dSmrginsw(unsigned port, void *addr, int cnt) 6049266c31dSmrg{ 6059266c31dSmrg void *dummy1; 6069266c31dSmrg int dummy2; 6079266c31dSmrg __asm __volatile("cld\n\trepne\n\tinsw" : 6089266c31dSmrg "=D" (dummy1), "=c" (dummy2) : 6099266c31dSmrg "d" (port), "0" (addr), "1" (cnt) : 6109266c31dSmrg "memory"); 6119266c31dSmrg} 6129266c31dSmrg 6139266c31dSmrg#define inl(port) \ 6149266c31dSmrg (/* CONSTCOND */ __use_immediate_port(port) ? __inlc(port) : __inl(port)) 6159266c31dSmrg 6169266c31dSmrgstatic __inline u_int32_t 6179266c31dSmrg__inlc(unsigned port) 6189266c31dSmrg{ 6199266c31dSmrg u_int32_t data; 6209266c31dSmrg __asm __volatile("inl %w1,%0" : "=a" (data) : "id" (port)); 6219266c31dSmrg return data; 6229266c31dSmrg} 6239266c31dSmrg 6249266c31dSmrgstatic __inline u_int32_t 6259266c31dSmrg__inl(unsigned port) 6269266c31dSmrg{ 6279266c31dSmrg u_int32_t data; 6289266c31dSmrg __asm __volatile("inl %w1,%0" : "=a" (data) : "d" (port)); 6299266c31dSmrg return data; 6309266c31dSmrg} 6319266c31dSmrg 6329266c31dSmrgstatic __inline void 6339266c31dSmrginsl(unsigned port, void *addr, int cnt) 6349266c31dSmrg{ 6359266c31dSmrg void *dummy1; 6369266c31dSmrg int dummy2; 6379266c31dSmrg __asm __volatile("cld\n\trepne\n\tinsl" : 6389266c31dSmrg "=D" (dummy1), "=c" (dummy2) : 6399266c31dSmrg "d" (port), "0" (addr), "1" (cnt) : 6409266c31dSmrg "memory"); 6419266c31dSmrg} 6429266c31dSmrg 6439266c31dSmrg#define outb(port, data) \ 6449266c31dSmrg (/* CONSTCOND */__use_immediate_port(port) ? __outbc(port, data) : \ 6459266c31dSmrg __outb(port, data)) 6469266c31dSmrg 6479266c31dSmrgstatic __inline void 6489266c31dSmrg__outbc(unsigned port, u_int8_t data) 6499266c31dSmrg{ 6509266c31dSmrg __asm __volatile("outb %0,%w1" : : "a" (data), "id" (port)); 6519266c31dSmrg} 6529266c31dSmrg 6539266c31dSmrgstatic __inline void 6549266c31dSmrg__outb(unsigned port, u_int8_t data) 6559266c31dSmrg{ 6569266c31dSmrg __asm __volatile("outb %0,%w1" : : "a" (data), "d" (port)); 6579266c31dSmrg} 6589266c31dSmrg 6599266c31dSmrgstatic __inline void 6609266c31dSmrgoutsb(unsigned port, const void *addr, int cnt) 6619266c31dSmrg{ 6629266c31dSmrg void *dummy1; 6639266c31dSmrg int dummy2; 6649266c31dSmrg __asm __volatile("cld\n\trepne\n\toutsb" : 6659266c31dSmrg "=S" (dummy1), "=c" (dummy2) : 6669266c31dSmrg "d" (port), "0" (addr), "1" (cnt)); 6679266c31dSmrg} 6689266c31dSmrg 6699266c31dSmrg#define outw(port, data) \ 6709266c31dSmrg (/* CONSTCOND */ __use_immediate_port(port) ? __outwc(port, data) : \ 6719266c31dSmrg __outw(port, data)) 6729266c31dSmrg 6739266c31dSmrgstatic __inline void 6749266c31dSmrg__outwc(unsigned port, u_int16_t data) 6759266c31dSmrg{ 6769266c31dSmrg __asm __volatile("outw %0,%w1" : : "a" (data), "id" (port)); 6779266c31dSmrg} 6789266c31dSmrg 6799266c31dSmrgstatic __inline void 6809266c31dSmrg__outw(unsigned port, u_int16_t data) 6819266c31dSmrg{ 6829266c31dSmrg __asm __volatile("outw %0,%w1" : : "a" (data), "d" (port)); 6839266c31dSmrg} 6849266c31dSmrg 6859266c31dSmrgstatic __inline void 6869266c31dSmrgoutsw(unsigned port, const void *addr, int cnt) 6879266c31dSmrg{ 6889266c31dSmrg void *dummy1; 6899266c31dSmrg int dummy2; 6909266c31dSmrg __asm __volatile("cld\n\trepne\n\toutsw" : 6919266c31dSmrg "=S" (dummy1), "=c" (dummy2) : 6929266c31dSmrg "d" (port), "0" (addr), "1" (cnt)); 6939266c31dSmrg} 6949266c31dSmrg 6959266c31dSmrg#define outl(port, data) \ 6969266c31dSmrg (/* CONSTCOND */ __use_immediate_port(port) ? __outlc(port, data) : \ 6979266c31dSmrg __outl(port, data)) 6989266c31dSmrg 6999266c31dSmrgstatic __inline void 7009266c31dSmrg__outlc(unsigned port, u_int32_t data) 7019266c31dSmrg{ 7029266c31dSmrg __asm __volatile("outl %0,%w1" : : "a" (data), "id" (port)); 7039266c31dSmrg} 7049266c31dSmrg 7059266c31dSmrgstatic __inline void 7069266c31dSmrg__outl(unsigned port, u_int32_t data) 7079266c31dSmrg{ 7089266c31dSmrg __asm __volatile("outl %0,%w1" : : "a" (data), "d" (port)); 7099266c31dSmrg} 7109266c31dSmrg 7119266c31dSmrgstatic __inline void 7129266c31dSmrgoutsl(unsigned port, const void *addr, int cnt) 7139266c31dSmrg{ 7149266c31dSmrg void *dummy1; 7159266c31dSmrg int dummy2; 7169266c31dSmrg __asm __volatile("cld\n\trepne\n\toutsl" : 7179266c31dSmrg "=S" (dummy1), "=c" (dummy2) : 7189266c31dSmrg "d" (port), "0" (addr), "1" (cnt)); 7199266c31dSmrg} 7209266c31dSmrg 7219266c31dSmrg#endif 7229266c31dSmrg 7239266c31dSmrg 7249266c31dSmrgstatic struct pci_io_handle * 7259266c31dSmrgpci_device_netbsd_open_legacy_io(struct pci_io_handle *ret, 7269266c31dSmrg struct pci_device *dev, pciaddr_t base, pciaddr_t size) 7279266c31dSmrg{ 7289266c31dSmrg#if defined(__i386__) 7299266c31dSmrg struct i386_iopl_args ia; 7309266c31dSmrg 7319266c31dSmrg ia.iopl = 1; 7329266c31dSmrg if (sysarch(I386_IOPL, &ia)) 7339266c31dSmrg return NULL; 7349266c31dSmrg 7359266c31dSmrg ret->base = base; 7369266c31dSmrg ret->size = size; 73714e6f28eSmrg ret->is_legacy = 1; 7389266c31dSmrg return ret; 7399266c31dSmrg#elif defined(__amd64__) 7409266c31dSmrg struct x86_64_iopl_args ia; 7419266c31dSmrg 7429266c31dSmrg ia.iopl = 1; 7439266c31dSmrg if (sysarch(X86_64_IOPL, &ia)) 7449266c31dSmrg return NULL; 7459266c31dSmrg 7469266c31dSmrg ret->base = base; 7479266c31dSmrg ret->size = size; 74814e6f28eSmrg ret->is_legacy = 1; 7499266c31dSmrg return ret; 7509266c31dSmrg#else 7519266c31dSmrg return NULL; 7529266c31dSmrg#endif 7539266c31dSmrg} 7549266c31dSmrg 7559266c31dSmrgstatic uint32_t 7569266c31dSmrgpci_device_netbsd_read32(struct pci_io_handle *handle, uint32_t reg) 7579266c31dSmrg{ 7589266c31dSmrg#if defined(__i386__) || defined(__amd64__) 7599266c31dSmrg return inl(handle->base + reg); 7609266c31dSmrg#else 7619266c31dSmrg return *(uint32_t *)((uintptr_t)handle->memory + reg); 7629266c31dSmrg#endif 7639266c31dSmrg} 7649266c31dSmrg 7659266c31dSmrgstatic uint16_t 7669266c31dSmrgpci_device_netbsd_read16(struct pci_io_handle *handle, uint32_t reg) 7679266c31dSmrg{ 7689266c31dSmrg#if defined(__i386__) || defined(__amd64__) 7699266c31dSmrg return inw(handle->base + reg); 7709266c31dSmrg#else 7719266c31dSmrg return *(uint16_t *)((uintptr_t)handle->memory + reg); 7729266c31dSmrg#endif 7739266c31dSmrg} 7749266c31dSmrg 7759266c31dSmrgstatic uint8_t 7769266c31dSmrgpci_device_netbsd_read8(struct pci_io_handle *handle, uint32_t reg) 7779266c31dSmrg{ 7789266c31dSmrg#if defined(__i386__) || defined(__amd64__) 7799266c31dSmrg return inb(handle->base + reg); 7809266c31dSmrg#else 7819266c31dSmrg return *(uint8_t *)((uintptr_t)handle->memory + reg); 7829266c31dSmrg#endif 7839266c31dSmrg} 7849266c31dSmrg 7859266c31dSmrgstatic void 7869266c31dSmrgpci_device_netbsd_write32(struct pci_io_handle *handle, uint32_t reg, 7879266c31dSmrg uint32_t data) 7889266c31dSmrg{ 7899266c31dSmrg#if defined(__i386__) || defined(__amd64__) 7909266c31dSmrg outl(handle->base + reg, data); 7919266c31dSmrg#else 7929266c31dSmrg *(uint16_t *)((uintptr_t)handle->memory + reg) = data; 7939266c31dSmrg#endif 7949266c31dSmrg} 7959266c31dSmrg 7969266c31dSmrgstatic void 7979266c31dSmrgpci_device_netbsd_write16(struct pci_io_handle *handle, uint32_t reg, 7989266c31dSmrg uint16_t data) 7999266c31dSmrg{ 8009266c31dSmrg#if defined(__i386__) || defined(__amd64__) 8019266c31dSmrg outw(handle->base + reg, data); 8029266c31dSmrg#else 8039266c31dSmrg *(uint8_t *)((uintptr_t)handle->memory + reg) = data; 8049266c31dSmrg#endif 8059266c31dSmrg} 8069266c31dSmrg 8079266c31dSmrgstatic void 8089266c31dSmrgpci_device_netbsd_write8(struct pci_io_handle *handle, uint32_t reg, 8099266c31dSmrg uint8_t data) 8109266c31dSmrg{ 8119266c31dSmrg#if defined(__i386__) || defined(__amd64__) 8129266c31dSmrg outb(handle->base + reg, data); 8139266c31dSmrg#else 8149266c31dSmrg *(uint32_t *)((uintptr_t)handle->memory + reg) = data; 8159266c31dSmrg#endif 8169266c31dSmrg} 8179266c31dSmrg 8189266c31dSmrgstatic int 8199266c31dSmrgpci_device_netbsd_map_legacy(struct pci_device *dev, pciaddr_t base, 8209266c31dSmrg pciaddr_t size, unsigned map_flags, void **addr) 8219266c31dSmrg{ 8229266c31dSmrg struct pci_device_mapping map; 8239266c31dSmrg int err; 8249266c31dSmrg 8259266c31dSmrg map.base = base; 8269266c31dSmrg map.size = size; 8279266c31dSmrg map.flags = map_flags; 8289266c31dSmrg map.memory = NULL; 8299266c31dSmrg err = pci_device_netbsd_map_range(dev, &map); 8309266c31dSmrg *addr = map.memory; 8319266c31dSmrg 8329266c31dSmrg return err; 8339266c31dSmrg} 8349266c31dSmrg 8359266c31dSmrgstatic int 8369266c31dSmrgpci_device_netbsd_unmap_legacy(struct pci_device *dev, void *addr, 8379266c31dSmrg pciaddr_t size) 8389266c31dSmrg{ 8399266c31dSmrg struct pci_device_mapping map; 8409266c31dSmrg 8419266c31dSmrg map.memory = addr; 8429266c31dSmrg map.size = size; 8439266c31dSmrg map.flags = 0; 8449266c31dSmrg return pci_device_netbsd_unmap_range(dev, &map); 8459266c31dSmrg} 8469266c31dSmrg 84773ade301Smrgstatic int 84873ade301Smrgpci_device_netbsd_has_kernel_driver(struct pci_device *dev) 84973ade301Smrg{ 85073ade301Smrg#ifdef PCI_IOC_DRVNAME 85173ade301Smrg /* 852b6fc90d8Smrg * NetBSD PCI_IOC_DRVNAME appears at the same time as pci_drvname(3), 853b6fc90d8Smrg * same as the better onbus version. 85473ade301Smrg */ 85573ade301Smrg char drvname[16]; 856b6fc90d8Smrg int i; 85773ade301Smrg 85873ade301Smrg if (dev->bus >= nbuses) 85973ade301Smrg return 0; 86073ade301Smrg 86173ade301Smrg /* 86273ade301Smrg * vga(4) should be considered "not bound". 86373ade301Smrg */ 864b6fc90d8Smrg for (i = 0; i < nbuses; i++) { 865b6fc90d8Smrg if (buses[i].num == dev->bus) { 866b6fc90d8Smrg int rv; 867b6fc90d8Smrg 868b6fc90d8Smrg#ifdef PCI_IOC_DRVNAMEONBUS 869b6fc90d8Smrg rv = pci_drvnameonbus(buses[i].fd, dev->bus, 870b6fc90d8Smrg dev->dev, dev->func, drvname, sizeof drvname); 871b6fc90d8Smrg#else 872b6fc90d8Smrg rv = pci_drvname(buses[i].fd, 873b6fc90d8Smrg dev->dev, dev->func, drvname, sizeof drvname); 874b6fc90d8Smrg#endif 875b6fc90d8Smrg if (rv == 0 && strncmp(drvname, "vga", 3) != 0) 876b6fc90d8Smrg return 1; 877b6fc90d8Smrg return 0; 878b6fc90d8Smrg } 879b6fc90d8Smrg } 88073ade301Smrg#endif 88173ade301Smrg return 0; 88273ade301Smrg} 88373ade301Smrg 8844f5e7dd7Smrgstatic const struct pci_system_methods netbsd_pci_methods = { 8859e884b7eSchristos .destroy = pci_system_netbsd_destroy, 8869e884b7eSchristos .destroy_device = NULL, 8879e884b7eSchristos .read_rom = pci_device_netbsd_read_rom, 8889e884b7eSchristos .probe = pci_device_netbsd_probe, 8899e884b7eSchristos .map_range = pci_device_netbsd_map_range, 8909e884b7eSchristos .unmap_range = pci_device_netbsd_unmap_range, 8919e884b7eSchristos .read = pci_device_netbsd_read, 8929e884b7eSchristos .write = pci_device_netbsd_write, 8936209fe3aScegger .fill_capabilities = pci_fill_capabilities_generic, 8949266c31dSmrg#if defined(WSDISPLAYIO_GET_BUSID) 8956209fe3aScegger .boot_vga = pci_device_netbsd_boot_vga, 8969266c31dSmrg#else 8979266c31dSmrg .boot_vga = NULL, 8989266c31dSmrg#endif 8999266c31dSmrg .open_legacy_io = pci_device_netbsd_open_legacy_io, 9009266c31dSmrg .read32 = pci_device_netbsd_read32, 9019266c31dSmrg .read16 = pci_device_netbsd_read16, 9029266c31dSmrg .read8 = pci_device_netbsd_read8, 9039266c31dSmrg .write32 = pci_device_netbsd_write32, 9049266c31dSmrg .write16 = pci_device_netbsd_write16, 9059266c31dSmrg .write8 = pci_device_netbsd_write8, 906f6db1a1aSmrg .map_legacy = pci_device_netbsd_map_legacy, 907f6db1a1aSmrg .unmap_legacy = pci_device_netbsd_unmap_legacy, 90873ade301Smrg .has_kernel_driver = pci_device_netbsd_has_kernel_driver, 9094f5e7dd7Smrg}; 9104f5e7dd7Smrg 91156db2c9dSjmcneillstatic int 91256db2c9dSjmcneillpci_system_netbsd_open_device(int unit) 91356db2c9dSjmcneill{ 91456db2c9dSjmcneill char netbsd_devname[32]; 91556db2c9dSjmcneill int pcifd; 91656db2c9dSjmcneill 91756db2c9dSjmcneill snprintf(netbsd_devname, 32, "/dev/pci%d", unit); 91856db2c9dSjmcneill pcifd = open(netbsd_devname, O_RDWR | O_CLOEXEC); 91956db2c9dSjmcneill if (pcifd == -1) 92056db2c9dSjmcneill pcifd = open(netbsd_devname, O_RDONLY | O_CLOEXEC); 92156db2c9dSjmcneill 92256db2c9dSjmcneill return pcifd; 92356db2c9dSjmcneill} 92456db2c9dSjmcneill 92556db2c9dSjmcneillstatic int 92656db2c9dSjmcneillpci_system_netbsd_count_buses(void) 92756db2c9dSjmcneill{ 9285cfc5640Sjmcneill int pcifd; 92956db2c9dSjmcneill 93056db2c9dSjmcneill do { 93156db2c9dSjmcneill pcifd = pci_system_netbsd_open_device(nbuses); 93256db2c9dSjmcneill if (pcifd != -1) { 93356db2c9dSjmcneill close(pcifd); 93456db2c9dSjmcneill nbuses++; 93556db2c9dSjmcneill } 93656db2c9dSjmcneill } while (pcifd != -1); 93756db2c9dSjmcneill 93856db2c9dSjmcneill return nbuses; 93956db2c9dSjmcneill} 94056db2c9dSjmcneill 9414f5e7dd7Smrgint 9424f5e7dd7Smrgpci_system_netbsd_create(void) 9434f5e7dd7Smrg{ 9444f5e7dd7Smrg struct pci_device_private *device; 94556db2c9dSjmcneill int bus, dev, func, ndevs, nfuncs, domain, pcifd, n; 9464f5e7dd7Smrg uint32_t reg; 9478ce851d2Smacallan struct pciio_businfo businfo; 9484f5e7dd7Smrg 9494f5e7dd7Smrg pci_sys = calloc(1, sizeof(struct pci_system)); 9504f5e7dd7Smrg 9514f5e7dd7Smrg pci_sys->methods = &netbsd_pci_methods; 9524f5e7dd7Smrg 9534f5e7dd7Smrg ndevs = 0; 95456db2c9dSjmcneill nbuses = pci_system_netbsd_count_buses(); 95556db2c9dSjmcneill if (nbuses > 0) 95656db2c9dSjmcneill buses = calloc(nbuses, sizeof(PciBus)); 95756db2c9dSjmcneill 95856db2c9dSjmcneill for (n = 0; n < nbuses; n++) { 95956db2c9dSjmcneill pcifd = pci_system_netbsd_open_device(n); 9603adb3582Smaya 9618ce851d2Smacallan ioctl(pcifd, PCI_IOC_BUSINFO, &businfo); 96256db2c9dSjmcneill buses[n].fd = pcifd; 96356db2c9dSjmcneill buses[n].num = bus = businfo.busno; 96456db2c9dSjmcneill buses[n].maxdevs = businfo.maxdevs; 96556db2c9dSjmcneill domain = n; 9668ce851d2Smacallan for (dev = 0; dev < businfo.maxdevs; dev++) { 9678ce851d2Smacallan nfuncs = pci_nfuncs(domain, bus, dev); 9684f5e7dd7Smrg for (func = 0; func < nfuncs; func++) { 9698ce851d2Smacallan if (pci_read(domain, bus, dev, func, PCI_ID_REG, 9704f5e7dd7Smrg ®) != 0) 9714f5e7dd7Smrg continue; 9724f5e7dd7Smrg if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || 9734f5e7dd7Smrg PCI_VENDOR(reg) == 0) 9744f5e7dd7Smrg continue; 9754f5e7dd7Smrg 9764f5e7dd7Smrg ndevs++; 9774f5e7dd7Smrg } 9784f5e7dd7Smrg } 9794f5e7dd7Smrg } 9804f5e7dd7Smrg 9814f5e7dd7Smrg pci_sys->num_devices = ndevs; 9824f5e7dd7Smrg pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private)); 9834f5e7dd7Smrg if (pci_sys->devices == NULL) { 9848ce851d2Smacallan int i; 9858ce851d2Smacallan 9868ce851d2Smacallan for (i = 0; i < nbuses; i++) 9878ce851d2Smacallan close(buses[i].fd); 9884f5e7dd7Smrg free(pci_sys); 989a01999d7Smrg pci_sys = NULL; 9904f5e7dd7Smrg return ENOMEM; 9914f5e7dd7Smrg } 9924f5e7dd7Smrg 9934f5e7dd7Smrg device = pci_sys->devices; 9948ce851d2Smacallan for (domain = 0; domain < nbuses; domain++) { 9958ce851d2Smacallan bus = buses[domain].num; 9968ce851d2Smacallan for (dev = 0; dev < buses[domain].maxdevs; dev++) { 9978ce851d2Smacallan nfuncs = pci_nfuncs(domain, bus, dev); 9984f5e7dd7Smrg for (func = 0; func < nfuncs; func++) { 9998ce851d2Smacallan if (pci_read(domain, bus, dev, func, 10008ce851d2Smacallan PCI_ID_REG, ®) != 0) 10014f5e7dd7Smrg continue; 10024f5e7dd7Smrg if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || 10034f5e7dd7Smrg PCI_VENDOR(reg) == 0) 10044f5e7dd7Smrg continue; 10054f5e7dd7Smrg 10068ce851d2Smacallan device->base.domain = domain; 10077deda49cSmrg if (domain > 0xffff) 10087deda49cSmrg device->base.domain_16 = 0xffff; 10097deda49cSmrg else 10107deda49cSmrg device->base.domain_16 = domain & 0xffff; 10114f5e7dd7Smrg device->base.bus = bus; 10124f5e7dd7Smrg device->base.dev = dev; 10134f5e7dd7Smrg device->base.func = func; 10144f5e7dd7Smrg device->base.vendor_id = PCI_VENDOR(reg); 10154f5e7dd7Smrg device->base.device_id = PCI_PRODUCT(reg); 10164f5e7dd7Smrg 10178ce851d2Smacallan if (pci_read(domain, bus, dev, func, 10188ce851d2Smacallan PCI_CLASS_REG, ®) != 0) 10194f5e7dd7Smrg continue; 10204f5e7dd7Smrg 10214f5e7dd7Smrg device->base.device_class = 10224f5e7dd7Smrg PCI_INTERFACE(reg) | PCI_CLASS(reg) << 16 | 10234f5e7dd7Smrg PCI_SUBCLASS(reg) << 8; 10244f5e7dd7Smrg device->base.revision = PCI_REVISION(reg); 10254f5e7dd7Smrg 10268ce851d2Smacallan if (pci_read(domain, bus, dev, func, 10278ce851d2Smacallan PCI_SUBSYS_ID_REG, ®) != 0) 10284f5e7dd7Smrg continue; 10294f5e7dd7Smrg 10304f5e7dd7Smrg device->base.subvendor_id = PCI_VENDOR(reg); 10314f5e7dd7Smrg device->base.subdevice_id = PCI_PRODUCT(reg); 10324f5e7dd7Smrg 10334f5e7dd7Smrg device++; 10344f5e7dd7Smrg } 10354f5e7dd7Smrg } 10364f5e7dd7Smrg } 10374f5e7dd7Smrg 10384f5e7dd7Smrg return 0; 10394f5e7dd7Smrg} 1040