netbsd_pci.c revision 6209fe3a
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 194f5e7dd7Smrg#include <sys/param.h> 204f5e7dd7Smrg#include <sys/ioctl.h> 214f5e7dd7Smrg#include <sys/mman.h> 224f5e7dd7Smrg#include <sys/types.h> 234f5e7dd7Smrg 2422df1cdfSmrg#ifdef HAVE_MTRR 254f5e7dd7Smrg#include <machine/sysarch.h> 264f5e7dd7Smrg#include <machine/mtrr.h> 2722df1cdfSmrg#define netbsd_set_mtrr(mr, num) _X86_SYSARCH_L(set_mtrr)(mr, num) 2822df1cdfSmrg#endif 294f5e7dd7Smrg 3022df1cdfSmrg#include <dev/pci/pcidevs.h> 314f5e7dd7Smrg#include <dev/pci/pciio.h> 324f5e7dd7Smrg#include <dev/pci/pcireg.h> 334f5e7dd7Smrg 344f5e7dd7Smrg#include <errno.h> 354f5e7dd7Smrg#include <fcntl.h> 364f5e7dd7Smrg#include <stdio.h> 374f5e7dd7Smrg#include <stdlib.h> 384f5e7dd7Smrg#include <string.h> 394f5e7dd7Smrg#include <unistd.h> 404f5e7dd7Smrg 414f5e7dd7Smrg 4222df1cdfSmrg#include <pci.h> 436209fe3aScegger#include <dev/wscons/wsconsio.h> 4422df1cdfSmrg 454f5e7dd7Smrg#include "pciaccess.h" 464f5e7dd7Smrg#include "pciaccess_private.h" 474f5e7dd7Smrg 488ce851d2Smacallantypedef struct _pcibus { 498ce851d2Smacallan int fd; /* /dev/pci* */ 508ce851d2Smacallan int num; /* bus number */ 518ce851d2Smacallan int maxdevs; /* maximum number of devices */ 528ce851d2Smacallan} PciBus; 539e884b7eSchristos 548ce851d2Smacallanstatic PciBus buses[32]; /* indexed by pci_device.domain */ 558ce851d2Smacallanstatic int nbuses = 0; /* number of buses found */ 568ce851d2Smacallan 578ce851d2Smacallan/* 588ce851d2Smacallan * NetBSD's userland has a /dev/pci* entry for each bus but userland has no way 598ce851d2Smacallan * to tell if a bus is a subordinate of another one or if it's on a different 608ce851d2Smacallan * host bridge. On some architectures ( macppc for example ) all root buses have 618ce851d2Smacallan * bus number 0 but on sparc64 for example the two roots in an Ultra60 have 628ce851d2Smacallan * different bus numbers - one is 0 and the other 128. 638ce851d2Smacallan * With each /dev/pci* we can map everything on the same root and we can also 648ce851d2Smacallan * see all devices on the same root, trying to do that causes problems though: 658ce851d2Smacallan * - since we can't tell which /dev/pci* is a subordinate we would find some 668ce851d2Smacallan * devices more than once 678ce851d2Smacallan * - we would have to guess subordinate bus numbers which is a waste of time 688ce851d2Smacallan * since we can ask each /dev/pci* for its bus number so we can scan only the 698ce851d2Smacallan * buses we know exist, not all 256 which may exist in each domain. 708ce851d2Smacallan * - some bus_space_mmap() methods may limit mappings to address ranges which 718ce851d2Smacallan * belong to known devices on that bus only. 728ce851d2Smacallan * Each host bridge may or may not have its own IO range, to avoid guesswork 738ce851d2Smacallan * here each /dev/pci* will let userland map its appropriate IO range at 748ce851d2Smacallan * PCI_MAGIC_IO_RANGE if defined in <machine/param.h> 758ce851d2Smacallan * With all this we should be able to use any PCI graphics device on any PCI 768ce851d2Smacallan * bus on any architecture as long as Xorg has a driver, without allowing 778ce851d2Smacallan * arbitrary mappings via /dev/mem and without userland having to know or care 788ce851d2Smacallan * about translating bus addresses to physical addresses or the other way 798ce851d2Smacallan * around. 808ce851d2Smacallan */ 814f5e7dd7Smrg 824f5e7dd7Smrgstatic int 838ce851d2Smacallanpci_read(int domain, int bus, int dev, int func, uint32_t reg, uint32_t *val) 844f5e7dd7Smrg{ 8522df1cdfSmrg uint32_t rval; 864f5e7dd7Smrg 878ce851d2Smacallan if ((domain < 0) || (domain > nbuses)) 888ce851d2Smacallan return -1; 898ce851d2Smacallan 908ce851d2Smacallan if (pcibus_conf_read(buses[domain].fd, (unsigned int)bus, 918ce851d2Smacallan (unsigned int)dev, (unsigned int)func, reg, &rval) == -1) 9222df1cdfSmrg return (-1); 934f5e7dd7Smrg 9422df1cdfSmrg *val = rval; 954f5e7dd7Smrg 964f5e7dd7Smrg return 0; 974f5e7dd7Smrg} 984f5e7dd7Smrg 994f5e7dd7Smrgstatic int 1008ce851d2Smacallanpci_write(int domain, int bus, int dev, int func, uint32_t reg, uint32_t val) 1014f5e7dd7Smrg{ 1028ce851d2Smacallan 1038ce851d2Smacallan if ((domain < 0) || (domain > nbuses)) 1048ce851d2Smacallan return -1; 1058ce851d2Smacallan 1068ce851d2Smacallan return pcibus_conf_write(buses[domain].fd, (unsigned int)bus, 1078ce851d2Smacallan (unsigned int)dev, (unsigned int)func, reg, val); 1084f5e7dd7Smrg} 1094f5e7dd7Smrg 1104f5e7dd7Smrgstatic int 1118ce851d2Smacallanpci_nfuncs(int domain, int bus, int dev) 1124f5e7dd7Smrg{ 1134f5e7dd7Smrg uint32_t hdr; 1144f5e7dd7Smrg 1158ce851d2Smacallan if ((domain < 0) || (domain > nbuses)) 1168ce851d2Smacallan return -1; 1178ce851d2Smacallan 1188ce851d2Smacallan if (pci_read(domain, bus, dev, 0, PCI_BHLC_REG, &hdr) != 0) 1194f5e7dd7Smrg return -1; 1204f5e7dd7Smrg 1214f5e7dd7Smrg return (PCI_HDRTYPE_MULTIFN(hdr) ? 8 : 1); 1224f5e7dd7Smrg} 1234f5e7dd7Smrg 1249e884b7eSchristos/*ARGSUSED*/ 1254f5e7dd7Smrgstatic int 1264f5e7dd7Smrgpci_device_netbsd_map_range(struct pci_device *dev, 1274f5e7dd7Smrg struct pci_device_mapping *map) 1284f5e7dd7Smrg{ 12922df1cdfSmrg#ifdef HAVE_MTRR 13022df1cdfSmrg struct mtrr m; 13122df1cdfSmrg int n = 1; 13222df1cdfSmrg#endif 1338ce851d2Smacallan int prot, ret = 0; 1344f5e7dd7Smrg 13522df1cdfSmrg prot = PROT_READ; 1364f5e7dd7Smrg 1374f5e7dd7Smrg if (map->flags & PCI_DEV_MAP_FLAG_WRITABLE) 1384f5e7dd7Smrg prot |= PROT_WRITE; 139a5d2dc35Sjmcneill map->memory = mmap(NULL, (size_t)map->size, prot, MAP_SHARED, 1408ce851d2Smacallan buses[dev->domain].fd, (off_t)map->base); 1414f5e7dd7Smrg if (map->memory == MAP_FAILED) 1424f5e7dd7Smrg return errno; 1434f5e7dd7Smrg 14422df1cdfSmrg#ifdef HAVE_MTRR 14522df1cdfSmrg memset(&m, 0, sizeof(m)); 14622df1cdfSmrg 1474f5e7dd7Smrg /* No need to set an MTRR if it's the default mode. */ 1484f5e7dd7Smrg if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || 1494f5e7dd7Smrg (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) { 150a5d2dc35Sjmcneill m.base = map->base; 15122df1cdfSmrg m.flags = MTRR_VALID | MTRR_PRIVATE; 152a5d2dc35Sjmcneill m.len = map->size; 15322df1cdfSmrg m.owner = getpid(); 154a5d2dc35Sjmcneill if (map->flags & PCI_DEV_MAP_FLAG_CACHABLE) 15522df1cdfSmrg m.type = MTRR_TYPE_WB; 1564f5e7dd7Smrg if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) 15722df1cdfSmrg m.type = MTRR_TYPE_WC; 15822df1cdfSmrg 159a5d2dc35Sjmcneill if ((netbsd_set_mtrr(&m, &n)) == -1) { 160a5d2dc35Sjmcneill fprintf(stderr, "mtrr set failed: %s\n", 161a5d2dc35Sjmcneill strerror(errno)); 162a5d2dc35Sjmcneill } 1634f5e7dd7Smrg } 16422df1cdfSmrg#endif 1654f5e7dd7Smrg 16622df1cdfSmrg return ret; 1674f5e7dd7Smrg} 1684f5e7dd7Smrg 1694f5e7dd7Smrgstatic int 1704f5e7dd7Smrgpci_device_netbsd_unmap_range(struct pci_device *dev, 1714f5e7dd7Smrg struct pci_device_mapping *map) 1724f5e7dd7Smrg{ 17322df1cdfSmrg#ifdef HAVE_MTRR 17422df1cdfSmrg struct mtrr m; 17522df1cdfSmrg int n = 1; 17622df1cdfSmrg 17722df1cdfSmrg memset(&m, 0, sizeof(m)); 1784f5e7dd7Smrg 1794f5e7dd7Smrg if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) || 1804f5e7dd7Smrg (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)) { 18122df1cdfSmrg m.base = map->base; 18222df1cdfSmrg m.flags = 0; 183a5d2dc35Sjmcneill m.len = map->size; 18422df1cdfSmrg m.type = MTRR_TYPE_UC; 18522df1cdfSmrg (void)netbsd_set_mtrr(&m, &n); 1864f5e7dd7Smrg } 18722df1cdfSmrg#endif 1884f5e7dd7Smrg 1894f5e7dd7Smrg return pci_device_generic_unmap_range(dev, map); 1904f5e7dd7Smrg} 1914f5e7dd7Smrg 1924f5e7dd7Smrgstatic int 1934f5e7dd7Smrgpci_device_netbsd_read(struct pci_device *dev, void *data, 1944f5e7dd7Smrg pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read) 1954f5e7dd7Smrg{ 19622df1cdfSmrg u_int reg, rval; 1974f5e7dd7Smrg 1984f5e7dd7Smrg *bytes_read = 0; 1994f5e7dd7Smrg while (size > 0) { 2009e884b7eSchristos size_t toread = MIN(size, 4 - (offset & 0x3)); 2014f5e7dd7Smrg 2029e884b7eSchristos reg = (u_int)(offset & ~0x3); 2034f5e7dd7Smrg 2048ce851d2Smacallan if ((pcibus_conf_read(buses[dev->domain].fd, 2058ce851d2Smacallan (unsigned int)dev->bus, (unsigned int)dev->dev, 2068ce851d2Smacallan (unsigned int)dev->func, reg, &rval)) == -1) 2074f5e7dd7Smrg return errno; 2084f5e7dd7Smrg 20922df1cdfSmrg rval = htole32(rval); 21022df1cdfSmrg rval >>= ((offset & 0x3) * 8); 2114f5e7dd7Smrg 21222df1cdfSmrg memcpy(data, &rval, toread); 2134f5e7dd7Smrg 2144f5e7dd7Smrg offset += toread; 2154f5e7dd7Smrg data = (char *)data + toread; 2164f5e7dd7Smrg size -= toread; 2174f5e7dd7Smrg *bytes_read += toread; 2184f5e7dd7Smrg } 2194f5e7dd7Smrg 2204f5e7dd7Smrg return 0; 2214f5e7dd7Smrg} 2224f5e7dd7Smrg 2234f5e7dd7Smrgstatic int 2244f5e7dd7Smrgpci_device_netbsd_write(struct pci_device *dev, const void *data, 2254f5e7dd7Smrg pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written) 2264f5e7dd7Smrg{ 22722df1cdfSmrg u_int reg, val; 2284f5e7dd7Smrg 2294f5e7dd7Smrg if ((offset % 4) != 0 || (size % 4) != 0) 2304f5e7dd7Smrg return EINVAL; 2314f5e7dd7Smrg 2324f5e7dd7Smrg *bytes_written = 0; 2334f5e7dd7Smrg while (size > 0) { 2349e884b7eSchristos reg = (u_int)offset; 23522df1cdfSmrg memcpy(&val, data, 4); 2364f5e7dd7Smrg 2378ce851d2Smacallan if ((pcibus_conf_write(buses[dev->domain].fd, 2388ce851d2Smacallan (unsigned int)dev->bus, (unsigned int)dev->dev, 2398ce851d2Smacallan (unsigned int)dev->func, reg, val)) == -1) 2404f5e7dd7Smrg return errno; 2414f5e7dd7Smrg 2424f5e7dd7Smrg offset += 4; 2439e884b7eSchristos data = (const char *)data + 4; 2444f5e7dd7Smrg size -= 4; 2454f5e7dd7Smrg *bytes_written += 4; 2464f5e7dd7Smrg } 2474f5e7dd7Smrg 2484f5e7dd7Smrg return 0; 2494f5e7dd7Smrg} 2504f5e7dd7Smrg 2516209fe3aSceggerstatic int 2526209fe3aSceggerpci_device_netbsd_boot_vga(struct pci_device *dev) 2536209fe3aScegger{ 2546209fe3aScegger int ret; 2556209fe3aScegger struct wsdisplayio_bus_id busid; 2566209fe3aScegger int fd; 2576209fe3aScegger 2586209fe3aScegger fd = open("/dev/ttyE0", O_RDONLY); 2596209fe3aScegger if (fd == -1) { 2606209fe3aScegger fprintf(stderr, "failed to open /dev/ttyE0: %s\n", 2616209fe3aScegger strerror(errno)); 2626209fe3aScegger return 0; 2636209fe3aScegger } 2646209fe3aScegger 2656209fe3aScegger ret = ioctl(fd, WSDISPLAYIO_GET_BUSID, &busid); 2666209fe3aScegger close(fd); 2676209fe3aScegger if (ret == -1) { 2686209fe3aScegger fprintf(stderr, "ioctl WSDISPLAYIO_GET_BUSID failed: %s\n", 2696209fe3aScegger strerror(errno)); 2706209fe3aScegger return 0; 2716209fe3aScegger } 2726209fe3aScegger 2736209fe3aScegger if (busid.bus_type != WSDISPLAYIO_BUS_PCI) 2746209fe3aScegger return 0; 2756209fe3aScegger 2766209fe3aScegger if (busid.ubus.pci.domain != dev->domain) 2776209fe3aScegger return 0; 2786209fe3aScegger if (busid.ubus.pci.bus != dev->bus) 2796209fe3aScegger return 0; 2806209fe3aScegger if (busid.ubus.pci.device != dev->dev) 2816209fe3aScegger return 0; 2826209fe3aScegger if (busid.ubus.pci.function != dev->func) 2836209fe3aScegger return 0; 2846209fe3aScegger 2856209fe3aScegger return 1; 2866209fe3aScegger} 2876209fe3aScegger 2884f5e7dd7Smrgstatic void 2894f5e7dd7Smrgpci_system_netbsd_destroy(void) 2904f5e7dd7Smrg{ 2918ce851d2Smacallan int i; 2928ce851d2Smacallan 2938ce851d2Smacallan for (i = 0; i < nbuses; i++) { 2948ce851d2Smacallan close(buses[i].fd); 2958ce851d2Smacallan } 2964f5e7dd7Smrg free(pci_sys); 2974f5e7dd7Smrg pci_sys = NULL; 2984f5e7dd7Smrg} 2994f5e7dd7Smrg 3004f5e7dd7Smrgstatic int 3014f5e7dd7Smrgpci_device_netbsd_probe(struct pci_device *device) 3024f5e7dd7Smrg{ 3039e884b7eSchristos struct pci_device_private *priv = 3049e884b7eSchristos (struct pci_device_private *)(void *)device; 3054f5e7dd7Smrg struct pci_mem_region *region; 3064f5e7dd7Smrg uint64_t reg64, size64; 3074f5e7dd7Smrg uint32_t bar, reg, size; 3088ce851d2Smacallan int bus, dev, func, err, domain; 3094f5e7dd7Smrg 3108ce851d2Smacallan domain = device->domain; 3114f5e7dd7Smrg bus = device->bus; 3124f5e7dd7Smrg dev = device->dev; 3134f5e7dd7Smrg func = device->func; 3144f5e7dd7Smrg 3156c7645b9Smrg /* Enable the device if necessary */ 3166c7645b9Smrg err = pci_read(domain, bus, dev, func, PCI_COMMAND_STATUS_REG, ®); 3176c7645b9Smrg if (err) 3186c7645b9Smrg return err; 3196c7645b9Smrg if ((reg & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE)) != 3206c7645b9Smrg (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE)) { 3216c7645b9Smrg reg |= PCI_COMMAND_IO_ENABLE | 3226c7645b9Smrg PCI_COMMAND_MEM_ENABLE | 3236c7645b9Smrg PCI_COMMAND_MASTER_ENABLE; 3246c7645b9Smrg err = pci_write(domain, bus, dev, func, PCI_COMMAND_STATUS_REG, 3256c7645b9Smrg reg); 3266c7645b9Smrg if (err) 3276c7645b9Smrg return err; 3286c7645b9Smrg } 3296c7645b9Smrg 3308ce851d2Smacallan err = pci_read(domain, bus, dev, func, PCI_BHLC_REG, ®); 3314f5e7dd7Smrg if (err) 3324f5e7dd7Smrg return err; 3334f5e7dd7Smrg 3344f5e7dd7Smrg priv->header_type = PCI_HDRTYPE_TYPE(reg); 3354f5e7dd7Smrg if (priv->header_type != 0) 3364f5e7dd7Smrg return 0; 3374f5e7dd7Smrg 3384f5e7dd7Smrg region = device->regions; 3394f5e7dd7Smrg for (bar = PCI_MAPREG_START; bar < PCI_MAPREG_END; 3404f5e7dd7Smrg bar += sizeof(uint32_t), region++) { 3418ce851d2Smacallan err = pci_read(domain, bus, dev, func, bar, ®); 3424f5e7dd7Smrg if (err) 3434f5e7dd7Smrg return err; 3444f5e7dd7Smrg 3454f5e7dd7Smrg /* Probe the size of the region. */ 3468ce851d2Smacallan err = pci_write(domain, bus, dev, func, bar, (unsigned int)~0); 3474f5e7dd7Smrg if (err) 3484f5e7dd7Smrg return err; 3498ce851d2Smacallan pci_read(domain, bus, dev, func, bar, &size); 3508ce851d2Smacallan pci_write(domain, bus, dev, func, bar, reg); 3514f5e7dd7Smrg 3524f5e7dd7Smrg if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) { 3534f5e7dd7Smrg region->is_IO = 1; 3544f5e7dd7Smrg region->base_addr = PCI_MAPREG_IO_ADDR(reg); 3554f5e7dd7Smrg region->size = PCI_MAPREG_IO_SIZE(size); 3564f5e7dd7Smrg } else { 3574f5e7dd7Smrg if (PCI_MAPREG_MEM_PREFETCHABLE(reg)) 3584f5e7dd7Smrg region->is_prefetchable = 1; 3594f5e7dd7Smrg switch(PCI_MAPREG_MEM_TYPE(reg)) { 3604f5e7dd7Smrg case PCI_MAPREG_MEM_TYPE_32BIT: 3614f5e7dd7Smrg case PCI_MAPREG_MEM_TYPE_32BIT_1M: 3624f5e7dd7Smrg region->base_addr = PCI_MAPREG_MEM_ADDR(reg); 3634f5e7dd7Smrg region->size = PCI_MAPREG_MEM_SIZE(size); 3644f5e7dd7Smrg break; 3654f5e7dd7Smrg case PCI_MAPREG_MEM_TYPE_64BIT: 3664f5e7dd7Smrg region->is_64 = 1; 3674f5e7dd7Smrg 3684f5e7dd7Smrg reg64 = reg; 3694f5e7dd7Smrg size64 = size; 3704f5e7dd7Smrg 3714f5e7dd7Smrg bar += sizeof(uint32_t); 3724f5e7dd7Smrg 3738ce851d2Smacallan err = pci_read(domain, bus, dev, func, bar, ®); 3744f5e7dd7Smrg if (err) 3754f5e7dd7Smrg return err; 3764f5e7dd7Smrg reg64 |= (uint64_t)reg << 32; 3774f5e7dd7Smrg 3788ce851d2Smacallan err = pci_write(domain, bus, dev, func, bar, 3799e884b7eSchristos (unsigned int)~0); 3804f5e7dd7Smrg if (err) 3814f5e7dd7Smrg return err; 3828ce851d2Smacallan pci_read(domain, bus, dev, func, bar, &size); 3838ce851d2Smacallan pci_write(domain, bus, dev, func, bar, 3849e884b7eSchristos (unsigned int)(reg64 >> 32)); 3854f5e7dd7Smrg size64 |= (uint64_t)size << 32; 3864f5e7dd7Smrg 3879e884b7eSchristos region->base_addr = 3889e884b7eSchristos (unsigned long)PCI_MAPREG_MEM64_ADDR(reg64); 3899e884b7eSchristos region->size = 3909e884b7eSchristos (unsigned long)PCI_MAPREG_MEM64_SIZE(size64); 3914f5e7dd7Smrg region++; 3924f5e7dd7Smrg break; 3934f5e7dd7Smrg } 3944f5e7dd7Smrg } 3954f5e7dd7Smrg } 3964f5e7dd7Smrg 39737a6a21eSmrg /* Probe expansion ROM if present */ 39837a6a21eSmrg err = pci_read(domain, bus, dev, func, PCI_MAPREG_ROM, ®); 39937a6a21eSmrg if (err) 40037a6a21eSmrg return err; 40137a6a21eSmrg if (reg != 0) { 40237a6a21eSmrg err = pci_write(domain, bus, dev, func, PCI_MAPREG_ROM, 40337a6a21eSmrg (uint32_t)(~PCI_MAPREG_ROM_ENABLE)); 40437a6a21eSmrg if (err) 40537a6a21eSmrg return err; 40637a6a21eSmrg pci_read(domain, bus, dev, func, PCI_MAPREG_ROM, &size); 40737a6a21eSmrg pci_write(domain, bus, dev, func, PCI_MAPREG_ROM, reg); 40837a6a21eSmrg if ((reg & PCI_MAPREG_MEM_ADDR_MASK) != 0) { 40937a6a21eSmrg priv->rom_base = reg & PCI_MAPREG_MEM_ADDR_MASK; 41037a6a21eSmrg device->rom_size = -(size & PCI_MAPREG_MEM_ADDR_MASK); 41137a6a21eSmrg } 41237a6a21eSmrg } 41337a6a21eSmrg 4144f5e7dd7Smrg return 0; 4154f5e7dd7Smrg} 4164f5e7dd7Smrg 4179e884b7eSchristos/** 4189e884b7eSchristos * Read a VGA rom using the 0xc0000 mapping. 4199e884b7eSchristos * 4209e884b7eSchristos * This function should be extended to handle access through PCI resources, 4219e884b7eSchristos * which should be more reliable when available. 4229e884b7eSchristos */ 4239e884b7eSchristosstatic int 4249e884b7eSchristospci_device_netbsd_read_rom(struct pci_device *dev, void *buffer) 4259e884b7eSchristos{ 4269e884b7eSchristos struct pci_device_private *priv = (struct pci_device_private *)(void *)dev; 4279e884b7eSchristos void *bios; 4289e884b7eSchristos pciaddr_t rom_base; 4299e884b7eSchristos size_t rom_size; 4309e884b7eSchristos uint32_t bios_val, command_val; 43174c741d0Smacallan int pci_rom; 4329e884b7eSchristos 4339e884b7eSchristos if (((priv->base.device_class >> 16) & 0xff) != PCI_CLASS_DISPLAY || 4349e884b7eSchristos ((priv->base.device_class >> 8) & 0xff) != PCI_SUBCLASS_DISPLAY_VGA) 4359e884b7eSchristos return ENOSYS; 4369e884b7eSchristos 4379e884b7eSchristos if (priv->rom_base == 0) { 4389e884b7eSchristos#if defined(__amd64__) || defined(__i386__) 43937a6a21eSmrg /* 44037a6a21eSmrg * We need a way to detect when this isn't the console and reject 44137a6a21eSmrg * this request outright. 44237a6a21eSmrg */ 4439e884b7eSchristos rom_base = 0xc0000; 4449e884b7eSchristos rom_size = 0x10000; 4459e884b7eSchristos pci_rom = 0; 4469e884b7eSchristos#else 4479e884b7eSchristos return ENOSYS; 4489e884b7eSchristos#endif 4499e884b7eSchristos } else { 4509e884b7eSchristos rom_base = priv->rom_base; 4519e884b7eSchristos rom_size = dev->rom_size; 4529e884b7eSchristos pci_rom = 1; 4538ce851d2Smacallan if ((pcibus_conf_read(buses[dev->domain].fd, (unsigned int)dev->bus, 4549e884b7eSchristos (unsigned int)dev->dev, (unsigned int)dev->func, 4558ce851d2Smacallan PCI_COMMAND_STATUS_REG, &command_val)) == -1) 4569e884b7eSchristos return errno; 4578ce851d2Smacallan if ((command_val & PCI_COMMAND_MEM_ENABLE) == 0) { 4588ce851d2Smacallan if ((pcibus_conf_write(buses[dev->domain].fd, 4598ce851d2Smacallan (unsigned int)dev->bus, (unsigned int)dev->dev, 4608ce851d2Smacallan (unsigned int)dev->func, PCI_COMMAND_STATUS_REG, 4618ce851d2Smacallan command_val | PCI_COMMAND_MEM_ENABLE)) == -1) 4629e884b7eSchristos return errno; 4639e884b7eSchristos } 4648ce851d2Smacallan if ((pcibus_conf_read(buses[dev->domain].fd, (unsigned int)dev->bus, 4659e884b7eSchristos (unsigned int)dev->dev, (unsigned int)dev->func, 4668ce851d2Smacallan PCI_MAPREG_ROM, &bios_val)) == -1) 4679e884b7eSchristos return errno; 4688ce851d2Smacallan if ((bios_val & PCI_MAPREG_ROM_ENABLE) == 0) { 4698ce851d2Smacallan if ((pcibus_conf_write(buses[dev->domain].fd, 4708ce851d2Smacallan (unsigned int)dev->bus, 4719e884b7eSchristos (unsigned int)dev->dev, (unsigned int)dev->func, 4728ce851d2Smacallan PCI_MAPREG_ROM, bios_val | PCI_MAPREG_ROM_ENABLE)) == -1) 4739e884b7eSchristos return errno; 4749e884b7eSchristos } 4759e884b7eSchristos } 4769e884b7eSchristos 47774c741d0Smacallan fprintf(stderr, "Using rom_base = 0x%lx 0x%lx (pci_rom=%d)\n", 47874c741d0Smacallan (long)rom_base, (long)rom_size, pci_rom); 4799e884b7eSchristos 48074c741d0Smacallan bios = mmap(NULL, rom_size, PROT_READ, MAP_SHARED, buses[dev->domain].fd, 48174c741d0Smacallan (off_t)rom_base); 4829e884b7eSchristos if (bios == MAP_FAILED) { 4839e884b7eSchristos int serrno = errno; 4849e884b7eSchristos return serrno; 4859e884b7eSchristos } 4869e884b7eSchristos 4879e884b7eSchristos memcpy(buffer, bios, rom_size); 4889e884b7eSchristos 4899e884b7eSchristos munmap(bios, rom_size); 4909e884b7eSchristos 4919e884b7eSchristos if (pci_rom) { 4928ce851d2Smacallan if ((command_val & PCI_COMMAND_MEM_ENABLE) == 0) { 4938ce851d2Smacallan if ((pcibus_conf_write(buses[dev->domain].fd, 4948ce851d2Smacallan (unsigned int)dev->bus, 4959e884b7eSchristos (unsigned int)dev->dev, (unsigned int)dev->func, 4968ce851d2Smacallan PCI_COMMAND_STATUS_REG, command_val)) == -1) 4979e884b7eSchristos return errno; 4989e884b7eSchristos } 4998ce851d2Smacallan if ((bios_val & PCI_MAPREG_ROM_ENABLE) == 0) { 5008ce851d2Smacallan if ((pcibus_conf_write(buses[dev->domain].fd, 5018ce851d2Smacallan (unsigned int)dev->bus, 5029e884b7eSchristos (unsigned int)dev->dev, (unsigned int)dev->func, 5038ce851d2Smacallan PCI_MAPREG_ROM, bios_val)) == -1) 5049e884b7eSchristos return errno; 5059e884b7eSchristos } 5069e884b7eSchristos } 5079e884b7eSchristos 5089e884b7eSchristos return 0; 5099e884b7eSchristos} 5109e884b7eSchristos 5114f5e7dd7Smrgstatic const struct pci_system_methods netbsd_pci_methods = { 5129e884b7eSchristos .destroy = pci_system_netbsd_destroy, 5139e884b7eSchristos .destroy_device = NULL, 5149e884b7eSchristos .read_rom = pci_device_netbsd_read_rom, 5159e884b7eSchristos .probe = pci_device_netbsd_probe, 5169e884b7eSchristos .map_range = pci_device_netbsd_map_range, 5179e884b7eSchristos .unmap_range = pci_device_netbsd_unmap_range, 5189e884b7eSchristos .read = pci_device_netbsd_read, 5199e884b7eSchristos .write = pci_device_netbsd_write, 5206209fe3aScegger .fill_capabilities = pci_fill_capabilities_generic, 5216209fe3aScegger .boot_vga = pci_device_netbsd_boot_vga, 5224f5e7dd7Smrg}; 5234f5e7dd7Smrg 5244f5e7dd7Smrgint 5254f5e7dd7Smrgpci_system_netbsd_create(void) 5264f5e7dd7Smrg{ 5274f5e7dd7Smrg struct pci_device_private *device; 5288ce851d2Smacallan int bus, dev, func, ndevs, nfuncs, domain, pcifd; 5294f5e7dd7Smrg uint32_t reg; 5306c7645b9Smrg char netbsd_devname[32]; 5318ce851d2Smacallan struct pciio_businfo businfo; 5324f5e7dd7Smrg 5334f5e7dd7Smrg pci_sys = calloc(1, sizeof(struct pci_system)); 5344f5e7dd7Smrg 5354f5e7dd7Smrg pci_sys->methods = &netbsd_pci_methods; 5364f5e7dd7Smrg 5374f5e7dd7Smrg ndevs = 0; 5388ce851d2Smacallan nbuses = 0; 5396c7645b9Smrg snprintf(netbsd_devname, 32, "/dev/pci%d", nbuses); 5406c7645b9Smrg pcifd = open(netbsd_devname, O_RDWR); 5418ce851d2Smacallan while (pcifd > 0) { 5428ce851d2Smacallan ioctl(pcifd, PCI_IOC_BUSINFO, &businfo); 5438ce851d2Smacallan buses[nbuses].fd = pcifd; 5448ce851d2Smacallan buses[nbuses].num = bus = businfo.busno; 5458ce851d2Smacallan buses[nbuses].maxdevs = businfo.maxdevs; 5468ce851d2Smacallan domain = nbuses; 5478ce851d2Smacallan nbuses++; 5488ce851d2Smacallan for (dev = 0; dev < businfo.maxdevs; dev++) { 5498ce851d2Smacallan nfuncs = pci_nfuncs(domain, bus, dev); 5504f5e7dd7Smrg for (func = 0; func < nfuncs; func++) { 5518ce851d2Smacallan if (pci_read(domain, bus, dev, func, PCI_ID_REG, 5524f5e7dd7Smrg ®) != 0) 5534f5e7dd7Smrg continue; 5544f5e7dd7Smrg if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || 5554f5e7dd7Smrg PCI_VENDOR(reg) == 0) 5564f5e7dd7Smrg continue; 5574f5e7dd7Smrg 5584f5e7dd7Smrg ndevs++; 5594f5e7dd7Smrg } 5604f5e7dd7Smrg } 5616c7645b9Smrg snprintf(netbsd_devname, 32, "/dev/pci%d", nbuses); 5626c7645b9Smrg pcifd = open(netbsd_devname, O_RDWR); 5634f5e7dd7Smrg } 5644f5e7dd7Smrg 5654f5e7dd7Smrg pci_sys->num_devices = ndevs; 5664f5e7dd7Smrg pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private)); 5674f5e7dd7Smrg if (pci_sys->devices == NULL) { 5688ce851d2Smacallan int i; 5698ce851d2Smacallan 5708ce851d2Smacallan for (i = 0; i < nbuses; i++) 5718ce851d2Smacallan close(buses[i].fd); 5724f5e7dd7Smrg free(pci_sys); 5734f5e7dd7Smrg return ENOMEM; 5744f5e7dd7Smrg } 5754f5e7dd7Smrg 5764f5e7dd7Smrg device = pci_sys->devices; 5778ce851d2Smacallan for (domain = 0; domain < nbuses; domain++) { 5788ce851d2Smacallan bus = buses[domain].num; 5798ce851d2Smacallan for (dev = 0; dev < buses[domain].maxdevs; dev++) { 5808ce851d2Smacallan nfuncs = pci_nfuncs(domain, bus, dev); 5814f5e7dd7Smrg for (func = 0; func < nfuncs; func++) { 5828ce851d2Smacallan if (pci_read(domain, bus, dev, func, 5838ce851d2Smacallan PCI_ID_REG, ®) != 0) 5844f5e7dd7Smrg continue; 5854f5e7dd7Smrg if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID || 5864f5e7dd7Smrg PCI_VENDOR(reg) == 0) 5874f5e7dd7Smrg continue; 5884f5e7dd7Smrg 5898ce851d2Smacallan device->base.domain = domain; 5904f5e7dd7Smrg device->base.bus = bus; 5914f5e7dd7Smrg device->base.dev = dev; 5924f5e7dd7Smrg device->base.func = func; 5934f5e7dd7Smrg device->base.vendor_id = PCI_VENDOR(reg); 5944f5e7dd7Smrg device->base.device_id = PCI_PRODUCT(reg); 5954f5e7dd7Smrg 5968ce851d2Smacallan if (pci_read(domain, bus, dev, func, 5978ce851d2Smacallan PCI_CLASS_REG, ®) != 0) 5984f5e7dd7Smrg continue; 5994f5e7dd7Smrg 6004f5e7dd7Smrg device->base.device_class = 6014f5e7dd7Smrg PCI_INTERFACE(reg) | PCI_CLASS(reg) << 16 | 6024f5e7dd7Smrg PCI_SUBCLASS(reg) << 8; 6034f5e7dd7Smrg device->base.revision = PCI_REVISION(reg); 6044f5e7dd7Smrg 6058ce851d2Smacallan if (pci_read(domain, bus, dev, func, 6068ce851d2Smacallan PCI_SUBSYS_ID_REG, ®) != 0) 6074f5e7dd7Smrg continue; 6084f5e7dd7Smrg 6094f5e7dd7Smrg device->base.subvendor_id = PCI_VENDOR(reg); 6104f5e7dd7Smrg device->base.subdevice_id = PCI_PRODUCT(reg); 6114f5e7dd7Smrg 6124f5e7dd7Smrg device++; 6134f5e7dd7Smrg } 6144f5e7dd7Smrg } 6154f5e7dd7Smrg } 6164f5e7dd7Smrg 6174f5e7dd7Smrg return 0; 6184f5e7dd7Smrg} 619