netbsd_pci.c revision 4b4d14a9
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
784b4d14a9Swiz * 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
288f6db1a1aSmrgstatic int
289f6db1a1aSmrgpci_device_netbsd_map_legacy(struct pci_device *dev, pciaddr_t base,
290f6db1a1aSmrg				  pciaddr_t size, unsigned map_flags, void **addr)
291f6db1a1aSmrg{
292f6db1a1aSmrg	struct pci_device_mapping map;
293f6db1a1aSmrg	int err;
294f6db1a1aSmrg
295f6db1a1aSmrg	map.base = base;
296f6db1a1aSmrg	map.size = size;
297f6db1a1aSmrg	map.flags = map_flags;
298f6db1a1aSmrg	map.memory = NULL;
299f6db1a1aSmrg	err = pci_device_netbsd_map_range(dev, &map);
300f6db1a1aSmrg	*addr = map.memory;
301f6db1a1aSmrg
302f6db1a1aSmrg	return err;
303f6db1a1aSmrg}
304f6db1a1aSmrg
305f6db1a1aSmrgstatic int
306f6db1a1aSmrgpci_device_netbsd_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size)
307f6db1a1aSmrg{
308f6db1a1aSmrg	struct pci_device_mapping map;
309f6db1a1aSmrg
310f6db1a1aSmrg	map.memory = addr;
311f6db1a1aSmrg	map.size = size;
312f6db1a1aSmrg	map.flags = 0;
313f6db1a1aSmrg	return pci_device_netbsd_unmap_range(dev, &map);
314f6db1a1aSmrg}
315f6db1a1aSmrg
316f6db1a1aSmrg
3174f5e7dd7Smrgstatic void
3184f5e7dd7Smrgpci_system_netbsd_destroy(void)
3194f5e7dd7Smrg{
3208ce851d2Smacallan	int i;
3218ce851d2Smacallan
3228ce851d2Smacallan	for (i = 0; i < nbuses; i++) {
3238ce851d2Smacallan		close(buses[i].fd);
3248ce851d2Smacallan	}
3254f5e7dd7Smrg	free(pci_sys);
3264f5e7dd7Smrg	pci_sys = NULL;
3274f5e7dd7Smrg}
3284f5e7dd7Smrg
3294f5e7dd7Smrgstatic int
3304f5e7dd7Smrgpci_device_netbsd_probe(struct pci_device *device)
3314f5e7dd7Smrg{
3329e884b7eSchristos	struct pci_device_private *priv =
3339e884b7eSchristos	    (struct pci_device_private *)(void *)device;
3344f5e7dd7Smrg	struct pci_mem_region *region;
3354f5e7dd7Smrg	uint64_t reg64, size64;
3364f5e7dd7Smrg	uint32_t bar, reg, size;
3378ce851d2Smacallan	int bus, dev, func, err, domain;
3384f5e7dd7Smrg
3398ce851d2Smacallan	domain = device->domain;
3404f5e7dd7Smrg	bus = device->bus;
3414f5e7dd7Smrg	dev = device->dev;
3424f5e7dd7Smrg	func = device->func;
3434f5e7dd7Smrg
3446c7645b9Smrg	/* Enable the device if necessary */
3456c7645b9Smrg	err = pci_read(domain, bus, dev, func, PCI_COMMAND_STATUS_REG, &reg);
3466c7645b9Smrg	if (err)
3476c7645b9Smrg		return err;
3486c7645b9Smrg	if ((reg & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE)) !=
3496c7645b9Smrg	    (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE)) {
3506c7645b9Smrg		reg |= PCI_COMMAND_IO_ENABLE |
3516c7645b9Smrg		       PCI_COMMAND_MEM_ENABLE |
3526c7645b9Smrg		       PCI_COMMAND_MASTER_ENABLE;
3536c7645b9Smrg		err = pci_write(domain, bus, dev, func, PCI_COMMAND_STATUS_REG,
3546c7645b9Smrg				reg);
3556c7645b9Smrg		if (err)
3566c7645b9Smrg			return err;
3576c7645b9Smrg	}
3586c7645b9Smrg
3598ce851d2Smacallan	err = pci_read(domain, bus, dev, func, PCI_BHLC_REG, &reg);
3604f5e7dd7Smrg	if (err)
3614f5e7dd7Smrg		return err;
3624f5e7dd7Smrg
3634f5e7dd7Smrg	priv->header_type = PCI_HDRTYPE_TYPE(reg);
3644f5e7dd7Smrg	if (priv->header_type != 0)
3654f5e7dd7Smrg		return 0;
3664f5e7dd7Smrg
3674f5e7dd7Smrg	region = device->regions;
3684f5e7dd7Smrg	for (bar = PCI_MAPREG_START; bar < PCI_MAPREG_END;
3694f5e7dd7Smrg	     bar += sizeof(uint32_t), region++) {
3708ce851d2Smacallan		err = pci_read(domain, bus, dev, func, bar, &reg);
3714f5e7dd7Smrg		if (err)
3724f5e7dd7Smrg			return err;
3734f5e7dd7Smrg
3744f5e7dd7Smrg		/* Probe the size of the region. */
3758ce851d2Smacallan		err = pci_write(domain, bus, dev, func, bar, (unsigned int)~0);
3764f5e7dd7Smrg		if (err)
3774f5e7dd7Smrg			return err;
3788ce851d2Smacallan		pci_read(domain, bus, dev, func, bar, &size);
3798ce851d2Smacallan		pci_write(domain, bus, dev, func, bar, reg);
3804f5e7dd7Smrg
3814f5e7dd7Smrg		if (PCI_MAPREG_TYPE(reg) == PCI_MAPREG_TYPE_IO) {
3824f5e7dd7Smrg			region->is_IO = 1;
3834f5e7dd7Smrg			region->base_addr = PCI_MAPREG_IO_ADDR(reg);
3844f5e7dd7Smrg			region->size = PCI_MAPREG_IO_SIZE(size);
3854f5e7dd7Smrg		} else {
3864f5e7dd7Smrg			if (PCI_MAPREG_MEM_PREFETCHABLE(reg))
3874f5e7dd7Smrg				region->is_prefetchable = 1;
3884f5e7dd7Smrg			switch(PCI_MAPREG_MEM_TYPE(reg)) {
3894f5e7dd7Smrg			case PCI_MAPREG_MEM_TYPE_32BIT:
3904f5e7dd7Smrg			case PCI_MAPREG_MEM_TYPE_32BIT_1M:
3914f5e7dd7Smrg				region->base_addr = PCI_MAPREG_MEM_ADDR(reg);
3924f5e7dd7Smrg				region->size = PCI_MAPREG_MEM_SIZE(size);
3934f5e7dd7Smrg				break;
3944f5e7dd7Smrg			case PCI_MAPREG_MEM_TYPE_64BIT:
3954f5e7dd7Smrg				region->is_64 = 1;
3964f5e7dd7Smrg
3974f5e7dd7Smrg				reg64 = reg;
3984f5e7dd7Smrg				size64 = size;
3994f5e7dd7Smrg
4004f5e7dd7Smrg				bar += sizeof(uint32_t);
4014f5e7dd7Smrg
4028ce851d2Smacallan				err = pci_read(domain, bus, dev, func, bar, &reg);
4034f5e7dd7Smrg				if (err)
4044f5e7dd7Smrg					return err;
4054f5e7dd7Smrg				reg64 |= (uint64_t)reg << 32;
4064f5e7dd7Smrg
4078ce851d2Smacallan				err = pci_write(domain, bus, dev, func, bar,
4089e884b7eSchristos				    (unsigned int)~0);
4094f5e7dd7Smrg				if (err)
4104f5e7dd7Smrg					return err;
4118ce851d2Smacallan				pci_read(domain, bus, dev, func, bar, &size);
4128ce851d2Smacallan				pci_write(domain, bus, dev, func, bar,
4139e884b7eSchristos				    (unsigned int)(reg64 >> 32));
4144f5e7dd7Smrg				size64 |= (uint64_t)size << 32;
4154f5e7dd7Smrg
4169e884b7eSchristos				region->base_addr =
4179e884b7eSchristos				    (unsigned long)PCI_MAPREG_MEM64_ADDR(reg64);
4189e884b7eSchristos				region->size =
4199e884b7eSchristos				    (unsigned long)PCI_MAPREG_MEM64_SIZE(size64);
4204f5e7dd7Smrg				region++;
4214f5e7dd7Smrg				break;
4224f5e7dd7Smrg			}
4234f5e7dd7Smrg		}
4244f5e7dd7Smrg	}
4254f5e7dd7Smrg
42637a6a21eSmrg	/* Probe expansion ROM if present */
42737a6a21eSmrg	err = pci_read(domain, bus, dev, func, PCI_MAPREG_ROM, &reg);
42837a6a21eSmrg	if (err)
42937a6a21eSmrg		return err;
43037a6a21eSmrg	if (reg != 0) {
43137a6a21eSmrg		err = pci_write(domain, bus, dev, func, PCI_MAPREG_ROM,
43237a6a21eSmrg		    (uint32_t)(~PCI_MAPREG_ROM_ENABLE));
43337a6a21eSmrg		if (err)
43437a6a21eSmrg			return err;
43537a6a21eSmrg		pci_read(domain, bus, dev, func, PCI_MAPREG_ROM, &size);
43637a6a21eSmrg		pci_write(domain, bus, dev, func, PCI_MAPREG_ROM, reg);
43737a6a21eSmrg		if ((reg & PCI_MAPREG_MEM_ADDR_MASK) != 0) {
43837a6a21eSmrg			priv->rom_base = reg & PCI_MAPREG_MEM_ADDR_MASK;
43937a6a21eSmrg			device->rom_size = -(size & PCI_MAPREG_MEM_ADDR_MASK);
44037a6a21eSmrg		}
44137a6a21eSmrg	}
44237a6a21eSmrg
4434f5e7dd7Smrg	return 0;
4444f5e7dd7Smrg}
4454f5e7dd7Smrg
4469e884b7eSchristos/**
4479e884b7eSchristos * Read a VGA rom using the 0xc0000 mapping.
4489e884b7eSchristos *
4499e884b7eSchristos * This function should be extended to handle access through PCI resources,
4509e884b7eSchristos * which should be more reliable when available.
4519e884b7eSchristos */
4529e884b7eSchristosstatic int
4539e884b7eSchristospci_device_netbsd_read_rom(struct pci_device *dev, void *buffer)
4549e884b7eSchristos{
4559e884b7eSchristos    struct pci_device_private *priv = (struct pci_device_private *)(void *)dev;
4569e884b7eSchristos    void *bios;
4579e884b7eSchristos    pciaddr_t rom_base;
4589e884b7eSchristos    size_t rom_size;
4599e884b7eSchristos    uint32_t bios_val, command_val;
46074c741d0Smacallan    int pci_rom;
4619e884b7eSchristos
4629e884b7eSchristos    if (((priv->base.device_class >> 16) & 0xff) != PCI_CLASS_DISPLAY ||
4639e884b7eSchristos	((priv->base.device_class >> 8) & 0xff) != PCI_SUBCLASS_DISPLAY_VGA)
4649e884b7eSchristos	return ENOSYS;
4659e884b7eSchristos
4669e884b7eSchristos    if (priv->rom_base == 0) {
4679e884b7eSchristos#if defined(__amd64__) || defined(__i386__)
46837a6a21eSmrg	/*
46937a6a21eSmrg	 * We need a way to detect when this isn't the console and reject
47037a6a21eSmrg	 * this request outright.
47137a6a21eSmrg	 */
4729e884b7eSchristos	rom_base = 0xc0000;
4739e884b7eSchristos	rom_size = 0x10000;
4749e884b7eSchristos	pci_rom = 0;
4759e884b7eSchristos#else
4769e884b7eSchristos	return ENOSYS;
4779e884b7eSchristos#endif
4789e884b7eSchristos    } else {
4799e884b7eSchristos	rom_base = priv->rom_base;
4809e884b7eSchristos	rom_size = dev->rom_size;
4819e884b7eSchristos	pci_rom = 1;
4828ce851d2Smacallan	if ((pcibus_conf_read(buses[dev->domain].fd, (unsigned int)dev->bus,
4839e884b7eSchristos	    (unsigned int)dev->dev, (unsigned int)dev->func,
4848ce851d2Smacallan	    PCI_COMMAND_STATUS_REG, &command_val)) == -1)
4859e884b7eSchristos	    return errno;
4868ce851d2Smacallan	if ((command_val & PCI_COMMAND_MEM_ENABLE) == 0) {
4878ce851d2Smacallan	    if ((pcibus_conf_write(buses[dev->domain].fd,
4888ce851d2Smacallan	        (unsigned int)dev->bus, (unsigned int)dev->dev,
4898ce851d2Smacallan		(unsigned int)dev->func, PCI_COMMAND_STATUS_REG,
4908ce851d2Smacallan		command_val | PCI_COMMAND_MEM_ENABLE)) == -1)
4919e884b7eSchristos		return errno;
4929e884b7eSchristos	}
4938ce851d2Smacallan	if ((pcibus_conf_read(buses[dev->domain].fd, (unsigned int)dev->bus,
4949e884b7eSchristos	    (unsigned int)dev->dev, (unsigned int)dev->func,
4958ce851d2Smacallan	    PCI_MAPREG_ROM, &bios_val)) == -1)
4969e884b7eSchristos	    return errno;
4978ce851d2Smacallan	if ((bios_val & PCI_MAPREG_ROM_ENABLE) == 0) {
4988ce851d2Smacallan	    if ((pcibus_conf_write(buses[dev->domain].fd,
4998ce851d2Smacallan	        (unsigned int)dev->bus,
5009e884b7eSchristos		(unsigned int)dev->dev, (unsigned int)dev->func,
5018ce851d2Smacallan		PCI_MAPREG_ROM, bios_val | PCI_MAPREG_ROM_ENABLE)) == -1)
5029e884b7eSchristos		return errno;
5039e884b7eSchristos	}
5049e884b7eSchristos    }
5059e884b7eSchristos
5064b4d14a9Swiz    fprintf(stderr, "Using rom_base = 0x%lx 0x%lx (pci_rom=%d)\n",
50774c741d0Smacallan        (long)rom_base, (long)rom_size, pci_rom);
5089e884b7eSchristos
5094b4d14a9Swiz    bios = mmap(NULL, rom_size, PROT_READ, MAP_SHARED, buses[dev->domain].fd,
51074c741d0Smacallan        (off_t)rom_base);
5119e884b7eSchristos    if (bios == MAP_FAILED) {
5129e884b7eSchristos	int serrno = errno;
5139e884b7eSchristos	return serrno;
5149e884b7eSchristos    }
5159e884b7eSchristos
5169e884b7eSchristos    memcpy(buffer, bios, rom_size);
5179e884b7eSchristos
5189e884b7eSchristos    munmap(bios, rom_size);
5199e884b7eSchristos
5209e884b7eSchristos    if (pci_rom) {
5218ce851d2Smacallan	if ((command_val & PCI_COMMAND_MEM_ENABLE) == 0) {
5228ce851d2Smacallan	    if ((pcibus_conf_write(buses[dev->domain].fd,
5238ce851d2Smacallan	        (unsigned int)dev->bus,
5249e884b7eSchristos		(unsigned int)dev->dev, (unsigned int)dev->func,
5258ce851d2Smacallan		PCI_COMMAND_STATUS_REG, command_val)) == -1)
5269e884b7eSchristos		return errno;
5279e884b7eSchristos	}
5288ce851d2Smacallan	if ((bios_val & PCI_MAPREG_ROM_ENABLE) == 0) {
5298ce851d2Smacallan	    if ((pcibus_conf_write(buses[dev->domain].fd,
5308ce851d2Smacallan	        (unsigned int)dev->bus,
5319e884b7eSchristos		(unsigned int)dev->dev, (unsigned int)dev->func,
5328ce851d2Smacallan		PCI_MAPREG_ROM, bios_val)) == -1)
5339e884b7eSchristos		return errno;
5349e884b7eSchristos	}
5359e884b7eSchristos    }
5369e884b7eSchristos
5379e884b7eSchristos    return 0;
5389e884b7eSchristos}
5399e884b7eSchristos
5404f5e7dd7Smrgstatic const struct pci_system_methods netbsd_pci_methods = {
5419e884b7eSchristos	.destroy = pci_system_netbsd_destroy,
5429e884b7eSchristos	.destroy_device = NULL,
5439e884b7eSchristos	.read_rom = pci_device_netbsd_read_rom,
5449e884b7eSchristos	.probe = pci_device_netbsd_probe,
5459e884b7eSchristos	.map_range = pci_device_netbsd_map_range,
5469e884b7eSchristos	.unmap_range = pci_device_netbsd_unmap_range,
5479e884b7eSchristos	.read = pci_device_netbsd_read,
5489e884b7eSchristos	.write = pci_device_netbsd_write,
5496209fe3aScegger	.fill_capabilities = pci_fill_capabilities_generic,
5506209fe3aScegger	.boot_vga = pci_device_netbsd_boot_vga,
551f6db1a1aSmrg	.map_legacy = pci_device_netbsd_map_legacy,
552f6db1a1aSmrg	.unmap_legacy = pci_device_netbsd_unmap_legacy,
5534f5e7dd7Smrg};
5544f5e7dd7Smrg
5554f5e7dd7Smrgint
5564f5e7dd7Smrgpci_system_netbsd_create(void)
5574f5e7dd7Smrg{
5584f5e7dd7Smrg	struct pci_device_private *device;
5598ce851d2Smacallan	int bus, dev, func, ndevs, nfuncs, domain, pcifd;
5604f5e7dd7Smrg	uint32_t reg;
5616c7645b9Smrg	char netbsd_devname[32];
5628ce851d2Smacallan	struct pciio_businfo businfo;
5634f5e7dd7Smrg
5644f5e7dd7Smrg	pci_sys = calloc(1, sizeof(struct pci_system));
5654f5e7dd7Smrg
5664f5e7dd7Smrg	pci_sys->methods = &netbsd_pci_methods;
5674f5e7dd7Smrg
5684f5e7dd7Smrg	ndevs = 0;
5698ce851d2Smacallan	nbuses = 0;
5706c7645b9Smrg	snprintf(netbsd_devname, 32, "/dev/pci%d", nbuses);
5712e46f441Smrg	pcifd = open(netbsd_devname, O_RDWR | O_CLOEXEC);
5728ce851d2Smacallan	while (pcifd > 0) {
5738ce851d2Smacallan		ioctl(pcifd, PCI_IOC_BUSINFO, &businfo);
5748ce851d2Smacallan		buses[nbuses].fd = pcifd;
5758ce851d2Smacallan		buses[nbuses].num = bus = businfo.busno;
5768ce851d2Smacallan		buses[nbuses].maxdevs = businfo.maxdevs;
5778ce851d2Smacallan		domain = nbuses;
5788ce851d2Smacallan		nbuses++;
5798ce851d2Smacallan		for (dev = 0; dev < businfo.maxdevs; dev++) {
5808ce851d2Smacallan			nfuncs = pci_nfuncs(domain, bus, dev);
5814f5e7dd7Smrg			for (func = 0; func < nfuncs; func++) {
5828ce851d2Smacallan				if (pci_read(domain, bus, dev, func, PCI_ID_REG,
5834f5e7dd7Smrg				    &reg) != 0)
5844f5e7dd7Smrg					continue;
5854f5e7dd7Smrg				if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
5864f5e7dd7Smrg				    PCI_VENDOR(reg) == 0)
5874f5e7dd7Smrg					continue;
5884f5e7dd7Smrg
5894f5e7dd7Smrg				ndevs++;
5904f5e7dd7Smrg			}
5914f5e7dd7Smrg		}
5926c7645b9Smrg		snprintf(netbsd_devname, 32, "/dev/pci%d", nbuses);
5936c7645b9Smrg		pcifd = open(netbsd_devname, O_RDWR);
5944f5e7dd7Smrg	}
5954f5e7dd7Smrg
5964f5e7dd7Smrg	pci_sys->num_devices = ndevs;
5974f5e7dd7Smrg	pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private));
5984f5e7dd7Smrg	if (pci_sys->devices == NULL) {
5998ce851d2Smacallan		int i;
6008ce851d2Smacallan
6018ce851d2Smacallan		for (i = 0; i < nbuses; i++)
6028ce851d2Smacallan			close(buses[i].fd);
6034f5e7dd7Smrg		free(pci_sys);
6044f5e7dd7Smrg		return ENOMEM;
6054f5e7dd7Smrg	}
6064f5e7dd7Smrg
6074f5e7dd7Smrg	device = pci_sys->devices;
6088ce851d2Smacallan	for (domain = 0; domain < nbuses; domain++) {
6098ce851d2Smacallan		bus = buses[domain].num;
6108ce851d2Smacallan		for (dev = 0; dev < buses[domain].maxdevs; dev++) {
6118ce851d2Smacallan			nfuncs = pci_nfuncs(domain, bus, dev);
6124f5e7dd7Smrg			for (func = 0; func < nfuncs; func++) {
6138ce851d2Smacallan				if (pci_read(domain, bus, dev, func,
6148ce851d2Smacallan				    PCI_ID_REG, &reg) != 0)
6154f5e7dd7Smrg					continue;
6164f5e7dd7Smrg				if (PCI_VENDOR(reg) == PCI_VENDOR_INVALID ||
6174f5e7dd7Smrg				    PCI_VENDOR(reg) == 0)
6184f5e7dd7Smrg					continue;
6194f5e7dd7Smrg
6208ce851d2Smacallan				device->base.domain = domain;
6214f5e7dd7Smrg				device->base.bus = bus;
6224f5e7dd7Smrg				device->base.dev = dev;
6234f5e7dd7Smrg				device->base.func = func;
6244f5e7dd7Smrg				device->base.vendor_id = PCI_VENDOR(reg);
6254f5e7dd7Smrg				device->base.device_id = PCI_PRODUCT(reg);
6264f5e7dd7Smrg
6278ce851d2Smacallan				if (pci_read(domain, bus, dev, func,
6288ce851d2Smacallan				    PCI_CLASS_REG, &reg) != 0)
6294f5e7dd7Smrg					continue;
6304f5e7dd7Smrg
6314f5e7dd7Smrg				device->base.device_class =
6324f5e7dd7Smrg				    PCI_INTERFACE(reg) | PCI_CLASS(reg) << 16 |
6334f5e7dd7Smrg				    PCI_SUBCLASS(reg) << 8;
6344f5e7dd7Smrg				device->base.revision = PCI_REVISION(reg);
6354f5e7dd7Smrg
6368ce851d2Smacallan				if (pci_read(domain, bus, dev, func,
6378ce851d2Smacallan				    PCI_SUBSYS_ID_REG, &reg) != 0)
6384f5e7dd7Smrg					continue;
6394f5e7dd7Smrg
6404f5e7dd7Smrg				device->base.subvendor_id = PCI_VENDOR(reg);
6414f5e7dd7Smrg				device->base.subdevice_id = PCI_PRODUCT(reg);
6424f5e7dd7Smrg
6434f5e7dd7Smrg				device++;
6444f5e7dd7Smrg			}
6454f5e7dd7Smrg		}
6464f5e7dd7Smrg	}
6474f5e7dd7Smrg
6484f5e7dd7Smrg	return 0;
6494f5e7dd7Smrg}
650