netbsd_pci.c revision 56db2c9d
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, &reg);
3346c7645b9Smrg	if (err)
3356c7645b9Smrg		return err;
3366c7645b9Smrg	if ((reg & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE)) !=
3376c7645b9Smrg	    (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE)) {
3386c7645b9Smrg		reg |= PCI_COMMAND_IO_ENABLE |
3396c7645b9Smrg		       PCI_COMMAND_MEM_ENABLE |
3406c7645b9Smrg		       PCI_COMMAND_MASTER_ENABLE;
3416c7645b9Smrg		err = pci_write(domain, bus, dev, func, PCI_COMMAND_STATUS_REG,
3426c7645b9Smrg				reg);
3436c7645b9Smrg		if (err)
3446c7645b9Smrg			return err;
3456c7645b9Smrg	}
3466c7645b9Smrg
3478ce851d2Smacallan	err = pci_read(domain, bus, dev, func, PCI_BHLC_REG, &reg);
3484f5e7dd7Smrg	if (err)
3494f5e7dd7Smrg		return err;
3504f5e7dd7Smrg
3514f5e7dd7Smrg	priv->header_type = PCI_HDRTYPE_TYPE(reg);
3524f5e7dd7Smrg	if (priv->header_type != 0)
3534f5e7dd7Smrg		return 0;
3544f5e7dd7Smrg
3554f5e7dd7Smrg	region = device->regions;
3564f5e7dd7Smrg	for (bar = PCI_MAPREG_START; bar < PCI_MAPREG_END;
3574f5e7dd7Smrg	     bar += sizeof(uint32_t), region++) {
3588ce851d2Smacallan		err = pci_read(domain, bus, dev, func, bar, &reg);
3594f5e7dd7Smrg		if (err)
3604f5e7dd7Smrg			return err;
3614f5e7dd7Smrg
3624f5e7dd7Smrg		/* Probe the size of the region. */
3638ce851d2Smacallan		err = pci_write(domain, bus, dev, func, bar, (unsigned int)~0);
3644f5e7dd7Smrg		if (err)
3654f5e7dd7Smrg			return err;
3668ce851d2Smacallan		pci_read(domain, bus, dev, func, bar, &size);
3678ce851d2Smacallan		pci_write(domain, bus, dev, func, bar, reg);
3684f5e7dd7Smrg
3694f5e7dd7Smrg		if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) {
3704f5e7dd7Smrg			region->is_IO = 1;
3714f5e7dd7Smrg			region->base_addr = PCI_MAPREG_IO_ADDR(reg);
3724f5e7dd7Smrg			region->size = PCI_MAPREG_IO_SIZE(size);
3734f5e7dd7Smrg		} else {
3744f5e7dd7Smrg			if (PCI_MAPREG_MEM_PREFETCHABLE(reg))
3754f5e7dd7Smrg				region->is_prefetchable = 1;
3764f5e7dd7Smrg			switch(PCI_MAPREG_MEM_TYPE(reg)) {
3774f5e7dd7Smrg			case PCI_MAPREG_MEM_TYPE_32BIT:
3784f5e7dd7Smrg			case PCI_MAPREG_MEM_TYPE_32BIT_1M:
3794f5e7dd7Smrg				region->base_addr = PCI_MAPREG_MEM_ADDR(reg);
3804f5e7dd7Smrg				region->size = PCI_MAPREG_MEM_SIZE(size);
3814f5e7dd7Smrg				break;
3824f5e7dd7Smrg			case PCI_MAPREG_MEM_TYPE_64BIT:
3834f5e7dd7Smrg				region->is_64 = 1;
3844f5e7dd7Smrg
3854f5e7dd7Smrg				reg64 = reg;
3864f5e7dd7Smrg				size64 = size;
3874f5e7dd7Smrg
3884f5e7dd7Smrg				bar += sizeof(uint32_t);
3894f5e7dd7Smrg
3908ce851d2Smacallan				err = pci_read(domain, bus, dev, func, bar, &reg);
3914f5e7dd7Smrg				if (err)
3924f5e7dd7Smrg					return err;
3934f5e7dd7Smrg				reg64 |= (uint64_t)reg << 32;
3944f5e7dd7Smrg
3958ce851d2Smacallan				err = pci_write(domain, bus, dev, func, bar,
3969e884b7eSchristos				    (unsigned int)~0);
3974f5e7dd7Smrg				if (err)
3984f5e7dd7Smrg					return err;
3998ce851d2Smacallan				pci_read(domain, bus, dev, func, bar, &size);
4008ce851d2Smacallan				pci_write(domain, bus, dev, func, bar,
4019e884b7eSchristos				    (unsigned int)(reg64 >> 32));
4024f5e7dd7Smrg				size64 |= (uint64_t)size << 32;
4034f5e7dd7Smrg
4049e884b7eSchristos				region->base_addr =
4059e884b7eSchristos				    (unsigned long)PCI_MAPREG_MEM64_ADDR(reg64);
4069e884b7eSchristos				region->size =
4079e884b7eSchristos				    (unsigned long)PCI_MAPREG_MEM64_SIZE(size64);
4084f5e7dd7Smrg				region++;
4094f5e7dd7Smrg				break;
4104f5e7dd7Smrg			}
4114f5e7dd7Smrg		}
4124f5e7dd7Smrg	}
4134f5e7dd7Smrg
41437a6a21eSmrg	/* Probe expansion ROM if present */
41537a6a21eSmrg	err = pci_read(domain, bus, dev, func, PCI_MAPREG_ROM, &reg);
41637a6a21eSmrg	if (err)
41737a6a21eSmrg		return err;
41837a6a21eSmrg	if (reg != 0) {
41937a6a21eSmrg		err = pci_write(domain, bus, dev, func, PCI_MAPREG_ROM,
42037a6a21eSmrg		    (uint32_t)(~PCI_MAPREG_ROM_ENABLE));
42137a6a21eSmrg		if (err)
42237a6a21eSmrg			return err;
42337a6a21eSmrg		pci_read(domain, bus, dev, func, PCI_MAPREG_ROM, &size);
42437a6a21eSmrg		pci_write(domain, bus, dev, func, PCI_MAPREG_ROM, reg);
42537a6a21eSmrg		if ((reg & PCI_MAPREG_MEM_ADDR_MASK) != 0) {
42637a6a21eSmrg			priv->rom_base = reg & PCI_MAPREG_MEM_ADDR_MASK;
42737a6a21eSmrg			device->rom_size = -(size & PCI_MAPREG_MEM_ADDR_MASK);
42837a6a21eSmrg		}
42937a6a21eSmrg	}
43037a6a21eSmrg
4314f5e7dd7Smrg	return 0;
4324f5e7dd7Smrg}
4334f5e7dd7Smrg
4349e884b7eSchristos/**
4359e884b7eSchristos * Read a VGA rom using the 0xc0000 mapping.
4369e884b7eSchristos *
4379e884b7eSchristos * This function should be extended to handle access through PCI resources,
4389e884b7eSchristos * which should be more reliable when available.
4399e884b7eSchristos */
4409e884b7eSchristosstatic int
4419e884b7eSchristospci_device_netbsd_read_rom(struct pci_device *dev, void *buffer)
4429e884b7eSchristos{
4439e884b7eSchristos    struct pci_device_private *priv = (struct pci_device_private *)(void *)dev;
4449e884b7eSchristos    void *bios;
4459e884b7eSchristos    pciaddr_t rom_base;
4469e884b7eSchristos    size_t rom_size;
4479e884b7eSchristos    uint32_t bios_val, command_val;
44874c741d0Smacallan    int pci_rom;
4499e884b7eSchristos
4509e884b7eSchristos    if (((priv->base.device_class >> 16) & 0xff) != PCI_CLASS_DISPLAY ||
4519e884b7eSchristos	((priv->base.device_class >> 8) & 0xff) != PCI_SUBCLASS_DISPLAY_VGA)
4529e884b7eSchristos	return ENOSYS;
4539e884b7eSchristos
4549e884b7eSchristos    if (priv->rom_base == 0) {
4559e884b7eSchristos#if defined(__amd64__) || defined(__i386__)
45637a6a21eSmrg	/*
45737a6a21eSmrg	 * We need a way to detect when this isn't the console and reject
45837a6a21eSmrg	 * this request outright.
45937a6a21eSmrg	 */
4609e884b7eSchristos	rom_base = 0xc0000;
4619e884b7eSchristos	rom_size = 0x10000;
4629e884b7eSchristos	pci_rom = 0;
4639e884b7eSchristos#else
4649e884b7eSchristos	return ENOSYS;
4659e884b7eSchristos#endif
4669e884b7eSchristos    } else {
4679e884b7eSchristos	rom_base = priv->rom_base;
4689e884b7eSchristos	rom_size = dev->rom_size;
4699e884b7eSchristos	pci_rom = 1;
4708ce851d2Smacallan	if ((pcibus_conf_read(buses[dev->domain].fd, (unsigned int)dev->bus,
4719e884b7eSchristos	    (unsigned int)dev->dev, (unsigned int)dev->func,
4728ce851d2Smacallan	    PCI_COMMAND_STATUS_REG, &command_val)) == -1)
4739e884b7eSchristos	    return errno;
4748ce851d2Smacallan	if ((command_val & PCI_COMMAND_MEM_ENABLE) == 0) {
4758ce851d2Smacallan	    if ((pcibus_conf_write(buses[dev->domain].fd,
4768ce851d2Smacallan	        (unsigned int)dev->bus, (unsigned int)dev->dev,
4778ce851d2Smacallan		(unsigned int)dev->func, PCI_COMMAND_STATUS_REG,
4788ce851d2Smacallan		command_val | PCI_COMMAND_MEM_ENABLE)) == -1)
4799e884b7eSchristos		return errno;
4809e884b7eSchristos	}
4818ce851d2Smacallan	if ((pcibus_conf_read(buses[dev->domain].fd, (unsigned int)dev->bus,
4829e884b7eSchristos	    (unsigned int)dev->dev, (unsigned int)dev->func,
4838ce851d2Smacallan	    PCI_MAPREG_ROM, &bios_val)) == -1)
4849e884b7eSchristos	    return errno;
4858ce851d2Smacallan	if ((bios_val & PCI_MAPREG_ROM_ENABLE) == 0) {
4868ce851d2Smacallan	    if ((pcibus_conf_write(buses[dev->domain].fd,
4878ce851d2Smacallan	        (unsigned int)dev->bus,
4889e884b7eSchristos		(unsigned int)dev->dev, (unsigned int)dev->func,
4898ce851d2Smacallan		PCI_MAPREG_ROM, bios_val | PCI_MAPREG_ROM_ENABLE)) == -1)
4909e884b7eSchristos		return errno;
4919e884b7eSchristos	}
4929e884b7eSchristos    }
4939e884b7eSchristos
4944b4d14a9Swiz    fprintf(stderr, "Using rom_base = 0x%lx 0x%lx (pci_rom=%d)\n",
49574c741d0Smacallan        (long)rom_base, (long)rom_size, pci_rom);
4969e884b7eSchristos
4974b4d14a9Swiz    bios = mmap(NULL, rom_size, PROT_READ, MAP_SHARED, buses[dev->domain].fd,
49874c741d0Smacallan        (off_t)rom_base);
4999e884b7eSchristos    if (bios == MAP_FAILED) {
5009e884b7eSchristos	int serrno = errno;
5019e884b7eSchristos	return serrno;
5029e884b7eSchristos    }
5039e884b7eSchristos
5049e884b7eSchristos    memcpy(buffer, bios, rom_size);
5059e884b7eSchristos
5069e884b7eSchristos    munmap(bios, rom_size);
5079e884b7eSchristos
5089e884b7eSchristos    if (pci_rom) {
5098ce851d2Smacallan	if ((command_val & PCI_COMMAND_MEM_ENABLE) == 0) {
5108ce851d2Smacallan	    if ((pcibus_conf_write(buses[dev->domain].fd,
5118ce851d2Smacallan	        (unsigned int)dev->bus,
5129e884b7eSchristos		(unsigned int)dev->dev, (unsigned int)dev->func,
5138ce851d2Smacallan		PCI_COMMAND_STATUS_REG, command_val)) == -1)
5149e884b7eSchristos		return errno;
5159e884b7eSchristos	}
5168ce851d2Smacallan	if ((bios_val & PCI_MAPREG_ROM_ENABLE) == 0) {
5178ce851d2Smacallan	    if ((pcibus_conf_write(buses[dev->domain].fd,
5188ce851d2Smacallan	        (unsigned int)dev->bus,
5199e884b7eSchristos		(unsigned int)dev->dev, (unsigned int)dev->func,
5208ce851d2Smacallan		PCI_MAPREG_ROM, bios_val)) == -1)
5219e884b7eSchristos		return errno;
5229e884b7eSchristos	}
5239e884b7eSchristos    }
5249e884b7eSchristos
5259e884b7eSchristos    return 0;
5269e884b7eSchristos}
5279e884b7eSchristos
5289266c31dSmrg#if defined(__i386__) || defined(__amd64__)
5299266c31dSmrg#include <machine/sysarch.h>
5309266c31dSmrg
5319266c31dSmrg/*
5329266c31dSmrg * Functions to provide access to x86 programmed I/O instructions.
5339266c31dSmrg *
5349266c31dSmrg * The in[bwl]() and out[bwl]() functions are split into two varieties: one to
5359266c31dSmrg * use a small, constant, 8-bit port number, and another to use a large or
5369266c31dSmrg * variable port number.  The former can be compiled as a smaller instruction.
5379266c31dSmrg */
5389266c31dSmrg
5399266c31dSmrg
5409266c31dSmrg#ifdef __OPTIMIZE__
5419266c31dSmrg
5429266c31dSmrg#define	__use_immediate_port(port) \
5439266c31dSmrg	(__builtin_constant_p((port)) && (port) < 0x100)
5449266c31dSmrg
5459266c31dSmrg#else
5469266c31dSmrg
5479266c31dSmrg#define	__use_immediate_port(port)	0
5489266c31dSmrg
5499266c31dSmrg#endif
5509266c31dSmrg
5519266c31dSmrg
5529266c31dSmrg#define	inb(port) \
5539266c31dSmrg    (/* CONSTCOND */ __use_immediate_port(port) ? __inbc(port) : __inb(port))
5549266c31dSmrg
5559266c31dSmrgstatic __inline u_int8_t
5569266c31dSmrg__inbc(unsigned port)
5579266c31dSmrg{
5589266c31dSmrg	u_int8_t data;
5599266c31dSmrg	__asm __volatile("inb %w1,%0" : "=a" (data) : "id" (port));
5609266c31dSmrg	return data;
5619266c31dSmrg}
5629266c31dSmrg
5639266c31dSmrgstatic __inline u_int8_t
5649266c31dSmrg__inb(unsigned port)
5659266c31dSmrg{
5669266c31dSmrg	u_int8_t data;
5679266c31dSmrg	__asm __volatile("inb %w1,%0" : "=a" (data) : "d" (port));
5689266c31dSmrg	return data;
5699266c31dSmrg}
5709266c31dSmrg
5719266c31dSmrgstatic __inline void
5729266c31dSmrginsb(unsigned port, void *addr, int cnt)
5739266c31dSmrg{
5749266c31dSmrg	void *dummy1;
5759266c31dSmrg	int dummy2;
5769266c31dSmrg	__asm __volatile("cld\n\trepne\n\tinsb"			:
5779266c31dSmrg			 "=D" (dummy1), "=c" (dummy2) 		:
5789266c31dSmrg			 "d" (port), "0" (addr), "1" (cnt)	:
5799266c31dSmrg			 "memory");
5809266c31dSmrg}
5819266c31dSmrg
5829266c31dSmrg#define	inw(port) \
5839266c31dSmrg    (/* CONSTCOND */ __use_immediate_port(port) ? __inwc(port) : __inw(port))
5849266c31dSmrg
5859266c31dSmrgstatic __inline u_int16_t
5869266c31dSmrg__inwc(unsigned port)
5879266c31dSmrg{
5889266c31dSmrg	u_int16_t data;
5899266c31dSmrg	__asm __volatile("inw %w1,%0" : "=a" (data) : "id" (port));
5909266c31dSmrg	return data;
5919266c31dSmrg}
5929266c31dSmrg
5939266c31dSmrgstatic __inline u_int16_t
5949266c31dSmrg__inw(unsigned port)
5959266c31dSmrg{
5969266c31dSmrg	u_int16_t data;
5979266c31dSmrg	__asm __volatile("inw %w1,%0" : "=a" (data) : "d" (port));
5989266c31dSmrg	return data;
5999266c31dSmrg}
6009266c31dSmrg
6019266c31dSmrgstatic __inline void
6029266c31dSmrginsw(unsigned port, void *addr, int cnt)
6039266c31dSmrg{
6049266c31dSmrg	void *dummy1;
6059266c31dSmrg	int dummy2;
6069266c31dSmrg	__asm __volatile("cld\n\trepne\n\tinsw"			:
6079266c31dSmrg			 "=D" (dummy1), "=c" (dummy2)		:
6089266c31dSmrg			 "d" (port), "0" (addr), "1" (cnt)	:
6099266c31dSmrg			 "memory");
6109266c31dSmrg}
6119266c31dSmrg
6129266c31dSmrg#define	inl(port) \
6139266c31dSmrg    (/* CONSTCOND */ __use_immediate_port(port) ? __inlc(port) : __inl(port))
6149266c31dSmrg
6159266c31dSmrgstatic __inline u_int32_t
6169266c31dSmrg__inlc(unsigned port)
6179266c31dSmrg{
6189266c31dSmrg	u_int32_t data;
6199266c31dSmrg	__asm __volatile("inl %w1,%0" : "=a" (data) : "id" (port));
6209266c31dSmrg	return data;
6219266c31dSmrg}
6229266c31dSmrg
6239266c31dSmrgstatic __inline u_int32_t
6249266c31dSmrg__inl(unsigned port)
6259266c31dSmrg{
6269266c31dSmrg	u_int32_t data;
6279266c31dSmrg	__asm __volatile("inl %w1,%0" : "=a" (data) : "d" (port));
6289266c31dSmrg	return data;
6299266c31dSmrg}
6309266c31dSmrg
6319266c31dSmrgstatic __inline void
6329266c31dSmrginsl(unsigned port, void *addr, int cnt)
6339266c31dSmrg{
6349266c31dSmrg	void *dummy1;
6359266c31dSmrg	int dummy2;
6369266c31dSmrg	__asm __volatile("cld\n\trepne\n\tinsl"			:
6379266c31dSmrg			 "=D" (dummy1), "=c" (dummy2)		:
6389266c31dSmrg			 "d" (port), "0" (addr), "1" (cnt)	:
6399266c31dSmrg			 "memory");
6409266c31dSmrg}
6419266c31dSmrg
6429266c31dSmrg#define	outb(port, data) \
6439266c31dSmrg    (/* CONSTCOND */__use_immediate_port(port) ? __outbc(port, data) : \
6449266c31dSmrg						__outb(port, data))
6459266c31dSmrg
6469266c31dSmrgstatic __inline void
6479266c31dSmrg__outbc(unsigned port, u_int8_t data)
6489266c31dSmrg{
6499266c31dSmrg	__asm __volatile("outb %0,%w1" : : "a" (data), "id" (port));
6509266c31dSmrg}
6519266c31dSmrg
6529266c31dSmrgstatic __inline void
6539266c31dSmrg__outb(unsigned port, u_int8_t data)
6549266c31dSmrg{
6559266c31dSmrg	__asm __volatile("outb %0,%w1" : : "a" (data), "d" (port));
6569266c31dSmrg}
6579266c31dSmrg
6589266c31dSmrgstatic __inline void
6599266c31dSmrgoutsb(unsigned port, const void *addr, int cnt)
6609266c31dSmrg{
6619266c31dSmrg	void *dummy1;
6629266c31dSmrg	int dummy2;
6639266c31dSmrg	__asm __volatile("cld\n\trepne\n\toutsb"		:
6649266c31dSmrg			 "=S" (dummy1), "=c" (dummy2)		:
6659266c31dSmrg			 "d" (port), "0" (addr), "1" (cnt));
6669266c31dSmrg}
6679266c31dSmrg
6689266c31dSmrg#define	outw(port, data) \
6699266c31dSmrg    (/* CONSTCOND */ __use_immediate_port(port) ? __outwc(port, data) : \
6709266c31dSmrg						__outw(port, data))
6719266c31dSmrg
6729266c31dSmrgstatic __inline void
6739266c31dSmrg__outwc(unsigned port, u_int16_t data)
6749266c31dSmrg{
6759266c31dSmrg	__asm __volatile("outw %0,%w1" : : "a" (data), "id" (port));
6769266c31dSmrg}
6779266c31dSmrg
6789266c31dSmrgstatic __inline void
6799266c31dSmrg__outw(unsigned port, u_int16_t data)
6809266c31dSmrg{
6819266c31dSmrg	__asm __volatile("outw %0,%w1" : : "a" (data), "d" (port));
6829266c31dSmrg}
6839266c31dSmrg
6849266c31dSmrgstatic __inline void
6859266c31dSmrgoutsw(unsigned port, const void *addr, int cnt)
6869266c31dSmrg{
6879266c31dSmrg	void *dummy1;
6889266c31dSmrg	int dummy2;
6899266c31dSmrg	__asm __volatile("cld\n\trepne\n\toutsw"		:
6909266c31dSmrg			 "=S" (dummy1), "=c" (dummy2)		:
6919266c31dSmrg			 "d" (port), "0" (addr), "1" (cnt));
6929266c31dSmrg}
6939266c31dSmrg
6949266c31dSmrg#define	outl(port, data) \
6959266c31dSmrg    (/* CONSTCOND */ __use_immediate_port(port) ? __outlc(port, data) : \
6969266c31dSmrg						__outl(port, data))
6979266c31dSmrg
6989266c31dSmrgstatic __inline void
6999266c31dSmrg__outlc(unsigned port, u_int32_t data)
7009266c31dSmrg{
7019266c31dSmrg	__asm __volatile("outl %0,%w1" : : "a" (data), "id" (port));
7029266c31dSmrg}
7039266c31dSmrg
7049266c31dSmrgstatic __inline void
7059266c31dSmrg__outl(unsigned port, u_int32_t data)
7069266c31dSmrg{
7079266c31dSmrg	__asm __volatile("outl %0,%w1" : : "a" (data), "d" (port));
7089266c31dSmrg}
7099266c31dSmrg
7109266c31dSmrgstatic __inline void
7119266c31dSmrgoutsl(unsigned port, const void *addr, int cnt)
7129266c31dSmrg{
7139266c31dSmrg	void *dummy1;
7149266c31dSmrg	int dummy2;
7159266c31dSmrg	__asm __volatile("cld\n\trepne\n\toutsl"		:
7169266c31dSmrg			 "=S" (dummy1), "=c" (dummy2)		:
7179266c31dSmrg			 "d" (port), "0" (addr), "1" (cnt));
7189266c31dSmrg}
7199266c31dSmrg
7209266c31dSmrg#endif
7219266c31dSmrg
7229266c31dSmrg
7239266c31dSmrgstatic struct pci_io_handle *
7249266c31dSmrgpci_device_netbsd_open_legacy_io(struct pci_io_handle *ret,
7259266c31dSmrg    struct pci_device *dev, pciaddr_t base, pciaddr_t size)
7269266c31dSmrg{
7279266c31dSmrg#if defined(__i386__)
7289266c31dSmrg	struct i386_iopl_args ia;
7299266c31dSmrg
7309266c31dSmrg	ia.iopl = 1;
7319266c31dSmrg	if (sysarch(I386_IOPL, &ia))
7329266c31dSmrg		return NULL;
7339266c31dSmrg
7349266c31dSmrg	ret->base = base;
7359266c31dSmrg	ret->size = size;
73614e6f28eSmrg	ret->is_legacy = 1;
7379266c31dSmrg	return ret;
7389266c31dSmrg#elif defined(__amd64__)
7399266c31dSmrg	struct x86_64_iopl_args ia;
7409266c31dSmrg
7419266c31dSmrg	ia.iopl = 1;
7429266c31dSmrg	if (sysarch(X86_64_IOPL, &ia))
7439266c31dSmrg		return NULL;
7449266c31dSmrg
7459266c31dSmrg	ret->base = base;
7469266c31dSmrg	ret->size = size;
74714e6f28eSmrg	ret->is_legacy = 1;
7489266c31dSmrg	return ret;
7499266c31dSmrg#else
7509266c31dSmrg	return NULL;
7519266c31dSmrg#endif
7529266c31dSmrg}
7539266c31dSmrg
7549266c31dSmrgstatic uint32_t
7559266c31dSmrgpci_device_netbsd_read32(struct pci_io_handle *handle, uint32_t reg)
7569266c31dSmrg{
7579266c31dSmrg#if defined(__i386__) || defined(__amd64__)
7589266c31dSmrg	return inl(handle->base + reg);
7599266c31dSmrg#else
7609266c31dSmrg	return *(uint32_t *)((uintptr_t)handle->memory + reg);
7619266c31dSmrg#endif
7629266c31dSmrg}
7639266c31dSmrg
7649266c31dSmrgstatic uint16_t
7659266c31dSmrgpci_device_netbsd_read16(struct pci_io_handle *handle, uint32_t reg)
7669266c31dSmrg{
7679266c31dSmrg#if defined(__i386__) || defined(__amd64__)
7689266c31dSmrg	return inw(handle->base + reg);
7699266c31dSmrg#else
7709266c31dSmrg	return *(uint16_t *)((uintptr_t)handle->memory + reg);
7719266c31dSmrg#endif
7729266c31dSmrg}
7739266c31dSmrg
7749266c31dSmrgstatic uint8_t
7759266c31dSmrgpci_device_netbsd_read8(struct pci_io_handle *handle, uint32_t reg)
7769266c31dSmrg{
7779266c31dSmrg#if defined(__i386__) || defined(__amd64__)
7789266c31dSmrg	return inb(handle->base + reg);
7799266c31dSmrg#else
7809266c31dSmrg	return *(uint8_t *)((uintptr_t)handle->memory + reg);
7819266c31dSmrg#endif
7829266c31dSmrg}
7839266c31dSmrg
7849266c31dSmrgstatic void
7859266c31dSmrgpci_device_netbsd_write32(struct pci_io_handle *handle, uint32_t reg,
7869266c31dSmrg    uint32_t data)
7879266c31dSmrg{
7889266c31dSmrg#if defined(__i386__) || defined(__amd64__)
7899266c31dSmrg	outl(handle->base + reg, data);
7909266c31dSmrg#else
7919266c31dSmrg	*(uint16_t *)((uintptr_t)handle->memory + reg) = data;
7929266c31dSmrg#endif
7939266c31dSmrg}
7949266c31dSmrg
7959266c31dSmrgstatic void
7969266c31dSmrgpci_device_netbsd_write16(struct pci_io_handle *handle, uint32_t reg,
7979266c31dSmrg    uint16_t data)
7989266c31dSmrg{
7999266c31dSmrg#if defined(__i386__) || defined(__amd64__)
8009266c31dSmrg	outw(handle->base + reg, data);
8019266c31dSmrg#else
8029266c31dSmrg	*(uint8_t *)((uintptr_t)handle->memory + reg) = data;
8039266c31dSmrg#endif
8049266c31dSmrg}
8059266c31dSmrg
8069266c31dSmrgstatic void
8079266c31dSmrgpci_device_netbsd_write8(struct pci_io_handle *handle, uint32_t reg,
8089266c31dSmrg    uint8_t data)
8099266c31dSmrg{
8109266c31dSmrg#if defined(__i386__) || defined(__amd64__)
8119266c31dSmrg	outb(handle->base + reg, data);
8129266c31dSmrg#else
8139266c31dSmrg	*(uint32_t *)((uintptr_t)handle->memory + reg) = data;
8149266c31dSmrg#endif
8159266c31dSmrg}
8169266c31dSmrg
8179266c31dSmrgstatic int
8189266c31dSmrgpci_device_netbsd_map_legacy(struct pci_device *dev, pciaddr_t base,
8199266c31dSmrg    pciaddr_t size, unsigned map_flags, void **addr)
8209266c31dSmrg{
8219266c31dSmrg	struct pci_device_mapping map;
8229266c31dSmrg	int err;
8239266c31dSmrg
8249266c31dSmrg	map.base = base;
8259266c31dSmrg	map.size = size;
8269266c31dSmrg	map.flags = map_flags;
8279266c31dSmrg	map.memory = NULL;
8289266c31dSmrg	err = pci_device_netbsd_map_range(dev, &map);
8299266c31dSmrg	*addr = map.memory;
8309266c31dSmrg
8319266c31dSmrg	return err;
8329266c31dSmrg}
8339266c31dSmrg
8349266c31dSmrgstatic int
8359266c31dSmrgpci_device_netbsd_unmap_legacy(struct pci_device *dev, void *addr,
8369266c31dSmrg    pciaddr_t size)
8379266c31dSmrg{
8389266c31dSmrg	struct pci_device_mapping map;
8399266c31dSmrg
8409266c31dSmrg	map.memory = addr;
8419266c31dSmrg	map.size = size;
8429266c31dSmrg	map.flags = 0;
8439266c31dSmrg	return pci_device_netbsd_unmap_range(dev, &map);
8449266c31dSmrg}
8459266c31dSmrg
84673ade301Smrgstatic int
84773ade301Smrgpci_device_netbsd_has_kernel_driver(struct pci_device *dev)
84873ade301Smrg{
84973ade301Smrg#ifdef PCI_IOC_DRVNAME
85073ade301Smrg	/*
851b6fc90d8Smrg	 * NetBSD PCI_IOC_DRVNAME appears at the same time as pci_drvname(3),
852b6fc90d8Smrg	 * same as the better onbus version.
85373ade301Smrg	 */
85473ade301Smrg	char drvname[16];
855b6fc90d8Smrg	int i;
85673ade301Smrg
85773ade301Smrg	if (dev->bus >= nbuses)
85873ade301Smrg		return 0;
85973ade301Smrg
86073ade301Smrg	/*
86173ade301Smrg	 * vga(4) should be considered "not bound".
86273ade301Smrg	 */
863b6fc90d8Smrg	for (i = 0; i < nbuses; i++) {
864b6fc90d8Smrg		if (buses[i].num == dev->bus) {
865b6fc90d8Smrg			int rv;
866b6fc90d8Smrg
867b6fc90d8Smrg#ifdef PCI_IOC_DRVNAMEONBUS
868b6fc90d8Smrg			rv = pci_drvnameonbus(buses[i].fd, dev->bus,
869b6fc90d8Smrg			    dev->dev, dev->func, drvname, sizeof drvname);
870b6fc90d8Smrg#else
871b6fc90d8Smrg			rv = pci_drvname(buses[i].fd,
872b6fc90d8Smrg			    dev->dev, dev->func, drvname, sizeof drvname);
873b6fc90d8Smrg#endif
874b6fc90d8Smrg			if (rv == 0 && strncmp(drvname, "vga", 3) != 0)
875b6fc90d8Smrg				return 1;
876b6fc90d8Smrg			return 0;
877b6fc90d8Smrg		}
878b6fc90d8Smrg	}
87973ade301Smrg#endif
88073ade301Smrg	return 0;
88173ade301Smrg}
88273ade301Smrg
8834f5e7dd7Smrgstatic const struct pci_system_methods netbsd_pci_methods = {
8849e884b7eSchristos	.destroy = pci_system_netbsd_destroy,
8859e884b7eSchristos	.destroy_device = NULL,
8869e884b7eSchristos	.read_rom = pci_device_netbsd_read_rom,
8879e884b7eSchristos	.probe = pci_device_netbsd_probe,
8889e884b7eSchristos	.map_range = pci_device_netbsd_map_range,
8899e884b7eSchristos	.unmap_range = pci_device_netbsd_unmap_range,
8909e884b7eSchristos	.read = pci_device_netbsd_read,
8919e884b7eSchristos	.write = pci_device_netbsd_write,
8926209fe3aScegger	.fill_capabilities = pci_fill_capabilities_generic,
8939266c31dSmrg#if defined(WSDISPLAYIO_GET_BUSID)
8946209fe3aScegger	.boot_vga = pci_device_netbsd_boot_vga,
8959266c31dSmrg#else
8969266c31dSmrg	.boot_vga = NULL,
8979266c31dSmrg#endif
8989266c31dSmrg	.open_legacy_io = pci_device_netbsd_open_legacy_io,
8999266c31dSmrg	.read32 = pci_device_netbsd_read32,
9009266c31dSmrg	.read16 = pci_device_netbsd_read16,
9019266c31dSmrg	.read8 = pci_device_netbsd_read8,
9029266c31dSmrg	.write32 = pci_device_netbsd_write32,
9039266c31dSmrg	.write16 = pci_device_netbsd_write16,
9049266c31dSmrg	.write8 = pci_device_netbsd_write8,
905f6db1a1aSmrg	.map_legacy = pci_device_netbsd_map_legacy,
906f6db1a1aSmrg	.unmap_legacy = pci_device_netbsd_unmap_legacy,
90773ade301Smrg	.has_kernel_driver = pci_device_netbsd_has_kernel_driver,
9084f5e7dd7Smrg};
9094f5e7dd7Smrg
91056db2c9dSjmcneillstatic int
91156db2c9dSjmcneillpci_system_netbsd_open_device(int unit)
91256db2c9dSjmcneill{
91356db2c9dSjmcneill	char netbsd_devname[32];
91456db2c9dSjmcneill	int pcifd;
91556db2c9dSjmcneill
91656db2c9dSjmcneill	snprintf(netbsd_devname, 32, "/dev/pci%d", unit);
91756db2c9dSjmcneill	pcifd = open(netbsd_devname, O_RDWR | O_CLOEXEC);
91856db2c9dSjmcneill	if (pcifd == -1)
91956db2c9dSjmcneill		pcifd = open(netbsd_devname, O_RDONLY | O_CLOEXEC);
92056db2c9dSjmcneill
92156db2c9dSjmcneill	return pcifd;
92256db2c9dSjmcneill}
92356db2c9dSjmcneill
92456db2c9dSjmcneillstatic int
92556db2c9dSjmcneillpci_system_netbsd_count_buses(void)
92656db2c9dSjmcneill{
92756db2c9dSjmcneill	int pcifd, nbuses;
92856db2c9dSjmcneill
92956db2c9dSjmcneill	do {
93056db2c9dSjmcneill		pcifd = pci_system_netbsd_open_device(nbuses);
93156db2c9dSjmcneill		if (pcifd != -1) {
93256db2c9dSjmcneill			close(pcifd);
93356db2c9dSjmcneill			nbuses++;
93456db2c9dSjmcneill		}
93556db2c9dSjmcneill	} while (pcifd != -1);
93656db2c9dSjmcneill
93756db2c9dSjmcneill	return nbuses;
93856db2c9dSjmcneill}
93956db2c9dSjmcneill
9404f5e7dd7Smrgint
9414f5e7dd7Smrgpci_system_netbsd_create(void)
9424f5e7dd7Smrg{
9434f5e7dd7Smrg	struct pci_device_private *device;
94456db2c9dSjmcneill	int bus, dev, func, ndevs, nfuncs, domain, pcifd, n;
9454f5e7dd7Smrg	uint32_t reg;
9468ce851d2Smacallan	struct pciio_businfo businfo;
9474f5e7dd7Smrg
9484f5e7dd7Smrg	pci_sys = calloc(1, sizeof(struct pci_system));
9494f5e7dd7Smrg
9504f5e7dd7Smrg	pci_sys->methods = &netbsd_pci_methods;
9514f5e7dd7Smrg
9524f5e7dd7Smrg	ndevs = 0;
95356db2c9dSjmcneill	nbuses = pci_system_netbsd_count_buses();
95456db2c9dSjmcneill	if (nbuses > 0)
95556db2c9dSjmcneill		buses = calloc(nbuses, sizeof(PciBus));
95656db2c9dSjmcneill
95756db2c9dSjmcneill	for (n = 0; n < nbuses; n++) {
95856db2c9dSjmcneill		pcifd = pci_system_netbsd_open_device(n);
9593adb3582Smaya
9608ce851d2Smacallan		ioctl(pcifd, PCI_IOC_BUSINFO, &businfo);
96156db2c9dSjmcneill		buses[n].fd = pcifd;
96256db2c9dSjmcneill		buses[n].num = bus = businfo.busno;
96356db2c9dSjmcneill		buses[n].maxdevs = businfo.maxdevs;
96456db2c9dSjmcneill		domain = n;
9658ce851d2Smacallan		for (dev = 0; dev < businfo.maxdevs; dev++) {
9668ce851d2Smacallan			nfuncs = pci_nfuncs(domain, bus, dev);
9674f5e7dd7Smrg			for (func = 0; func < nfuncs; func++) {
9688ce851d2Smacallan				if (pci_read(domain, bus, dev, func, PCI_ID_REG,
9694f5e7dd7Smrg				    &reg) != 0)
9704f5e7dd7Smrg					continue;
9714f5e7dd7Smrg				if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
9724f5e7dd7Smrg				    PCI_VENDOR(reg) == 0)
9734f5e7dd7Smrg					continue;
9744f5e7dd7Smrg
9754f5e7dd7Smrg				ndevs++;
9764f5e7dd7Smrg			}
9774f5e7dd7Smrg		}
9784f5e7dd7Smrg	}
9794f5e7dd7Smrg
9804f5e7dd7Smrg	pci_sys->num_devices = ndevs;
9814f5e7dd7Smrg	pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private));
9824f5e7dd7Smrg	if (pci_sys->devices == NULL) {
9838ce851d2Smacallan		int i;
9848ce851d2Smacallan
9858ce851d2Smacallan		for (i = 0; i < nbuses; i++)
9868ce851d2Smacallan			close(buses[i].fd);
9874f5e7dd7Smrg		free(pci_sys);
9884f5e7dd7Smrg		return ENOMEM;
9894f5e7dd7Smrg	}
9904f5e7dd7Smrg
9914f5e7dd7Smrg	device = pci_sys->devices;
9928ce851d2Smacallan	for (domain = 0; domain < nbuses; domain++) {
9938ce851d2Smacallan		bus = buses[domain].num;
9948ce851d2Smacallan		for (dev = 0; dev < buses[domain].maxdevs; dev++) {
9958ce851d2Smacallan			nfuncs = pci_nfuncs(domain, bus, dev);
9964f5e7dd7Smrg			for (func = 0; func < nfuncs; func++) {
9978ce851d2Smacallan				if (pci_read(domain, bus, dev, func,
9988ce851d2Smacallan				    PCI_ID_REG, &reg) != 0)
9994f5e7dd7Smrg					continue;
10004f5e7dd7Smrg				if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
10014f5e7dd7Smrg				    PCI_VENDOR(reg) == 0)
10024f5e7dd7Smrg					continue;
10034f5e7dd7Smrg
10048ce851d2Smacallan				device->base.domain = domain;
10057deda49cSmrg				if (domain > 0xffff)
10067deda49cSmrg				    device->base.domain_16 = 0xffff;
10077deda49cSmrg				else
10087deda49cSmrg				    device->base.domain_16 = domain & 0xffff;
10094f5e7dd7Smrg				device->base.bus = bus;
10104f5e7dd7Smrg				device->base.dev = dev;
10114f5e7dd7Smrg				device->base.func = func;
10124f5e7dd7Smrg				device->base.vendor_id = PCI_VENDOR(reg);
10134f5e7dd7Smrg				device->base.device_id = PCI_PRODUCT(reg);
10144f5e7dd7Smrg
10158ce851d2Smacallan				if (pci_read(domain, bus, dev, func,
10168ce851d2Smacallan				    PCI_CLASS_REG, &reg) != 0)
10174f5e7dd7Smrg					continue;
10184f5e7dd7Smrg
10194f5e7dd7Smrg				device->base.device_class =
10204f5e7dd7Smrg				    PCI_INTERFACE(reg) | PCI_CLASS(reg) << 16 |
10214f5e7dd7Smrg				    PCI_SUBCLASS(reg) << 8;
10224f5e7dd7Smrg				device->base.revision = PCI_REVISION(reg);
10234f5e7dd7Smrg
10248ce851d2Smacallan				if (pci_read(domain, bus, dev, func,
10258ce851d2Smacallan				    PCI_SUBSYS_ID_REG, &reg) != 0)
10264f5e7dd7Smrg					continue;
10274f5e7dd7Smrg
10284f5e7dd7Smrg				device->base.subvendor_id = PCI_VENDOR(reg);
10294f5e7dd7Smrg				device->base.subdevice_id = PCI_PRODUCT(reg);
10304f5e7dd7Smrg
10314f5e7dd7Smrg				device++;
10324f5e7dd7Smrg			}
10334f5e7dd7Smrg		}
10344f5e7dd7Smrg	}
10354f5e7dd7Smrg
10364f5e7dd7Smrg	return 0;
10374f5e7dd7Smrg}
1038