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;
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, &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, &reg);
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, &reg);
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, &reg);
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				    &reg) != 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, &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, &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, &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