14f5e7dd7Smrg/*
24f5e7dd7Smrg * (C) Copyright Eric Anholt 2006
34f5e7dd7Smrg * (C) Copyright IBM Corporation 2006
486ea1d58Smrg * (C) Copyright Mark Kettenis 2011
586ea1d58Smrg * (C) Copyright Robert Millan 2012
64f5e7dd7Smrg * All Rights Reserved.
74f5e7dd7Smrg *
84f5e7dd7Smrg * Permission is hereby granted, free of charge, to any person obtaining a
94f5e7dd7Smrg * copy of this software and associated documentation files (the "Software"),
104f5e7dd7Smrg * to deal in the Software without restriction, including without limitation
114f5e7dd7Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub
124f5e7dd7Smrg * license, and/or sell copies of the Software, and to permit persons to whom
134f5e7dd7Smrg * the Software is furnished to do so, subject to the following conditions:
144f5e7dd7Smrg *
154f5e7dd7Smrg * The above copyright notice and this permission notice (including the next
164f5e7dd7Smrg * paragraph) shall be included in all copies or substantial portions of the
174f5e7dd7Smrg * Software.
184f5e7dd7Smrg *
194f5e7dd7Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
204f5e7dd7Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
214f5e7dd7Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
224f5e7dd7Smrg * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
234f5e7dd7Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
244f5e7dd7Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
254f5e7dd7Smrg * DEALINGS IN THE SOFTWARE.
264f5e7dd7Smrg */
274f5e7dd7Smrg
284f5e7dd7Smrg/**
294f5e7dd7Smrg * \file freebsd_pci.c
304f5e7dd7Smrg *
314f5e7dd7Smrg * Access the kernel PCI support using /dev/pci's ioctl and mmap interface.
324f5e7dd7Smrg *
334f5e7dd7Smrg * \author Eric Anholt <eric@anholt.net>
344f5e7dd7Smrg */
354f5e7dd7Smrg
366a94483fSmrg#ifdef HAVE_CONFIG_H
376a94483fSmrg#include "config.h"
386a94483fSmrg#endif
396a94483fSmrg
404f5e7dd7Smrg#include <stdlib.h>
414f5e7dd7Smrg#include <stdio.h>
424f5e7dd7Smrg#include <string.h>
434f5e7dd7Smrg#include <unistd.h>
444f5e7dd7Smrg#include <fcntl.h>
454f5e7dd7Smrg#include <errno.h>
462029f493Smrg#if defined(__i386__) || defined(__amd64__)
472029f493Smrg#include <machine/cpufunc.h>
482029f493Smrg#else
492029f493Smrg#include <dev/io/iodev.h>
502029f493Smrg#endif
514f5e7dd7Smrg#include <sys/types.h>
524f5e7dd7Smrg#include <sys/param.h>
534f5e7dd7Smrg#include <sys/pciio.h>
544f5e7dd7Smrg#include <sys/mman.h>
554f5e7dd7Smrg#include <sys/memrange.h>
564f5e7dd7Smrg
574f5e7dd7Smrg#include "pciaccess.h"
584f5e7dd7Smrg#include "pciaccess_private.h"
594f5e7dd7Smrg
602029f493Smrg#define	PCIC_DISPLAY		0x03
614f5e7dd7Smrg#define	PCIS_DISPLAY_VGA	0x00
624f5e7dd7Smrg#define	PCIS_DISPLAY_XGA	0x01
634f5e7dd7Smrg#define	PCIS_DISPLAY_3D		0x02
644f5e7dd7Smrg#define	PCIS_DISPLAY_OTHER	0x80
654f5e7dd7Smrg
664f5e7dd7Smrg/* Registers taken from pcireg.h */
672029f493Smrg#define PCIR_COMMAND		0x04
682029f493Smrg#define PCIM_CMD_PORTEN		0x0001
692029f493Smrg#define PCIM_CMD_MEMEN		0x0002
702029f493Smrg#define PCIR_BIOS		0x30
714f5e7dd7Smrg#define PCIM_BIOS_ENABLE	0x01
724f5e7dd7Smrg#define PCIM_BIOS_ADDR_MASK	0xfffff800
734f5e7dd7Smrg
742029f493Smrg#define PCIR_BARS		0x10
752029f493Smrg#define PCIR_BAR(x)		(PCIR_BARS + (x) * 4)
762029f493Smrg#define PCI_BAR_IO(x)		(((x) & PCIM_BAR_SPACE) == PCIM_BAR_IO_SPACE)
772029f493Smrg#define PCI_BAR_MEM(x)		(((x) & PCIM_BAR_SPACE) == PCIM_BAR_MEM_SPACE)
782029f493Smrg#define PCIM_BAR_MEM_TYPE	0x00000006
792029f493Smrg#define PCIM_BAR_MEM_64		4
802029f493Smrg#define PCIM_BAR_MEM_PREFETCH	0x00000008
812029f493Smrg#define PCIM_BAR_SPACE		0x00000001
822029f493Smrg#define PCIM_BAR_MEM_SPACE	0
832029f493Smrg#define PCIM_BAR_IO_SPACE	1
842029f493Smrg
854f5e7dd7Smrg/**
864f5e7dd7Smrg * FreeBSD private pci_system structure that extends the base pci_system
874f5e7dd7Smrg * structure.
884f5e7dd7Smrg *
894f5e7dd7Smrg * It is initialized once and used as a global, just as pci_system is used.
904f5e7dd7Smrg */
914f5e7dd7Smrg_pci_hidden
924f5e7dd7Smrgstruct freebsd_pci_system {
934f5e7dd7Smrg    /* This must be the first entry in the structure, as pci_system_cleanup()
944f5e7dd7Smrg     * frees pci_sys.
954f5e7dd7Smrg     */
964f5e7dd7Smrg    struct pci_system pci_sys;
974f5e7dd7Smrg
984f5e7dd7Smrg    int pcidev; /**< fd for /dev/pci */
994f5e7dd7Smrg} *freebsd_pci_sys;
1004f5e7dd7Smrg
1014f5e7dd7Smrg/**
1024f5e7dd7Smrg * Map a memory region for a device using /dev/mem.
103cad31331Smrg *
1044f5e7dd7Smrg * \param dev   Device whose memory region is to be mapped.
1054f5e7dd7Smrg * \param map   Parameters of the mapping that is to be created.
106cad31331Smrg *
1074f5e7dd7Smrg * \return
1084f5e7dd7Smrg * Zero on success or an \c errno value on failure.
1094f5e7dd7Smrg */
1104f5e7dd7Smrgstatic int
1112029f493Smrgpci_device_freebsd_map_range( struct pci_device *dev,
1122029f493Smrg			      struct pci_device_mapping *map )
1134f5e7dd7Smrg{
114cad31331Smrg    const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0)
1154f5e7dd7Smrg        ? (PROT_READ | PROT_WRITE) : PROT_READ;
1164f5e7dd7Smrg    struct mem_range_desc mrd;
1174f5e7dd7Smrg    struct mem_range_op mro;
1184f5e7dd7Smrg
1194f5e7dd7Smrg    int fd, err = 0;
1204f5e7dd7Smrg
121cad31331Smrg    fd = open("/dev/mem", O_RDWR | O_CLOEXEC);
1224f5e7dd7Smrg    if (fd == -1)
1234f5e7dd7Smrg	return errno;
1244f5e7dd7Smrg
1254f5e7dd7Smrg    map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, map->base);
1264f5e7dd7Smrg
1274f5e7dd7Smrg    if (map->memory == MAP_FAILED) {
1284f5e7dd7Smrg	err = errno;
1294f5e7dd7Smrg    }
1304f5e7dd7Smrg
1314f5e7dd7Smrg    mrd.mr_base = map->base;
1324f5e7dd7Smrg    mrd.mr_len = map->size;
1334f5e7dd7Smrg    strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner));
1344f5e7dd7Smrg    if (map->flags & PCI_DEV_MAP_FLAG_CACHABLE)
1354f5e7dd7Smrg	mrd.mr_flags = MDF_WRITEBACK;
1364f5e7dd7Smrg    else if (map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE)
1374f5e7dd7Smrg	mrd.mr_flags = MDF_WRITECOMBINE;
1384f5e7dd7Smrg    else
1394f5e7dd7Smrg	mrd.mr_flags = MDF_UNCACHEABLE;
1404f5e7dd7Smrg    mro.mo_desc = &mrd;
1414f5e7dd7Smrg    mro.mo_arg[0] = MEMRANGE_SET_UPDATE;
1424f5e7dd7Smrg
1434f5e7dd7Smrg    /* No need to set an MTRR if it's the default mode. */
1444f5e7dd7Smrg    if (mrd.mr_flags != MDF_UNCACHEABLE) {
1454f5e7dd7Smrg	if (ioctl(fd, MEMRANGE_SET, &mro)) {
1464f5e7dd7Smrg	    fprintf(stderr, "failed to set mtrr: %s\n", strerror(errno));
1474f5e7dd7Smrg	}
1484f5e7dd7Smrg    }
1494f5e7dd7Smrg
1504f5e7dd7Smrg    close(fd);
1514f5e7dd7Smrg
1524f5e7dd7Smrg    return err;
1534f5e7dd7Smrg}
1544f5e7dd7Smrg
1554f5e7dd7Smrgstatic int
1564f5e7dd7Smrgpci_device_freebsd_unmap_range( struct pci_device *dev,
1574f5e7dd7Smrg				struct pci_device_mapping *map )
1584f5e7dd7Smrg{
1594f5e7dd7Smrg    struct mem_range_desc mrd;
1604f5e7dd7Smrg    struct mem_range_op mro;
1614f5e7dd7Smrg    int fd;
1624f5e7dd7Smrg
1634f5e7dd7Smrg    if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) ||
1644f5e7dd7Smrg	(map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE))
1654f5e7dd7Smrg    {
166cad31331Smrg	fd = open("/dev/mem", O_RDWR | O_CLOEXEC);
1674f5e7dd7Smrg	if (fd != -1) {
1684f5e7dd7Smrg	    mrd.mr_base = map->base;
1694f5e7dd7Smrg	    mrd.mr_len = map->size;
1704f5e7dd7Smrg	    strncpy(mrd.mr_owner, "pciaccess", sizeof(mrd.mr_owner));
1714f5e7dd7Smrg	    mrd.mr_flags = MDF_UNCACHEABLE;
1724f5e7dd7Smrg	    mro.mo_desc = &mrd;
1734f5e7dd7Smrg	    mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
1744f5e7dd7Smrg
1754f5e7dd7Smrg	    if (ioctl(fd, MEMRANGE_SET, &mro)) {
1764f5e7dd7Smrg		fprintf(stderr, "failed to unset mtrr: %s\n", strerror(errno));
1774f5e7dd7Smrg	    }
1784f5e7dd7Smrg
1794f5e7dd7Smrg	    close(fd);
1804f5e7dd7Smrg	} else {
1814f5e7dd7Smrg	    fprintf(stderr, "Failed to open /dev/mem\n");
1824f5e7dd7Smrg	}
1834f5e7dd7Smrg    }
1844f5e7dd7Smrg
1854f5e7dd7Smrg    return pci_device_generic_unmap_range(dev, map);
1864f5e7dd7Smrg}
1874f5e7dd7Smrg
1884f5e7dd7Smrgstatic int
1894f5e7dd7Smrgpci_device_freebsd_read( struct pci_device * dev, void * data,
1904f5e7dd7Smrg			 pciaddr_t offset, pciaddr_t size,
1914f5e7dd7Smrg			 pciaddr_t * bytes_read )
1924f5e7dd7Smrg{
1934f5e7dd7Smrg    struct pci_io io;
1944f5e7dd7Smrg
1954f5e7dd7Smrg    io.pi_sel.pc_domain = dev->domain;
1964f5e7dd7Smrg    io.pi_sel.pc_bus = dev->bus;
1974f5e7dd7Smrg    io.pi_sel.pc_dev = dev->dev;
1984f5e7dd7Smrg    io.pi_sel.pc_func = dev->func;
1994f5e7dd7Smrg
2004f5e7dd7Smrg    *bytes_read = 0;
2014f5e7dd7Smrg    while ( size > 0 ) {
2024f5e7dd7Smrg	int toread = (size < 4) ? size : 4;
2034f5e7dd7Smrg
2044f5e7dd7Smrg	/* Only power of two allowed. */
2054f5e7dd7Smrg	if (toread == 3)
2064f5e7dd7Smrg	    toread = 2;
2074f5e7dd7Smrg
2084f5e7dd7Smrg	io.pi_reg = offset;
2094f5e7dd7Smrg	io.pi_width = toread;
2104f5e7dd7Smrg
211cad31331Smrg	if ( ioctl( freebsd_pci_sys->pcidev, PCIOCREAD, &io ) < 0 )
2124f5e7dd7Smrg	    return errno;
2134f5e7dd7Smrg
2144f5e7dd7Smrg	memcpy(data, &io.pi_data, toread );
2154f5e7dd7Smrg
2164f5e7dd7Smrg	offset += toread;
2174f5e7dd7Smrg	data = (char *)data + toread;
2184f5e7dd7Smrg	size -= toread;
2194f5e7dd7Smrg	*bytes_read += toread;
2204f5e7dd7Smrg    }
2214f5e7dd7Smrg
2224f5e7dd7Smrg    return 0;
2234f5e7dd7Smrg}
2244f5e7dd7Smrg
2254f5e7dd7Smrg
2264f5e7dd7Smrgstatic int
2274f5e7dd7Smrgpci_device_freebsd_write( struct pci_device * dev, const void * data,
2284f5e7dd7Smrg			  pciaddr_t offset, pciaddr_t size,
2294f5e7dd7Smrg			  pciaddr_t * bytes_written )
2304f5e7dd7Smrg{
2314f5e7dd7Smrg    struct pci_io io;
2324f5e7dd7Smrg
2334f5e7dd7Smrg    io.pi_sel.pc_domain = dev->domain;
2344f5e7dd7Smrg    io.pi_sel.pc_bus = dev->bus;
2354f5e7dd7Smrg    io.pi_sel.pc_dev = dev->dev;
2364f5e7dd7Smrg    io.pi_sel.pc_func = dev->func;
2374f5e7dd7Smrg
2384f5e7dd7Smrg    *bytes_written = 0;
2394f5e7dd7Smrg    while ( size > 0 ) {
2404f5e7dd7Smrg	int towrite = (size < 4 ? size : 4);
2414f5e7dd7Smrg
2424f5e7dd7Smrg	/* Only power of two allowed. */
2434f5e7dd7Smrg	if (towrite == 3)
2444f5e7dd7Smrg	    towrite = 2;
2454f5e7dd7Smrg
2464f5e7dd7Smrg	io.pi_reg = offset;
2474f5e7dd7Smrg	io.pi_width = towrite;
2484f5e7dd7Smrg	memcpy( &io.pi_data, data, towrite );
2494f5e7dd7Smrg
250cad31331Smrg	if ( ioctl( freebsd_pci_sys->pcidev, PCIOCWRITE, &io ) < 0 )
2514f5e7dd7Smrg	    return errno;
2524f5e7dd7Smrg
2534f5e7dd7Smrg	offset += towrite;
2544f5e7dd7Smrg	data = (char *)data + towrite;
2554f5e7dd7Smrg	size -= towrite;
2564f5e7dd7Smrg	*bytes_written += towrite;
2574f5e7dd7Smrg    }
2584f5e7dd7Smrg
2594f5e7dd7Smrg    return 0;
2604f5e7dd7Smrg}
2614f5e7dd7Smrg
2624f5e7dd7Smrg/**
2634f5e7dd7Smrg * Read a VGA rom using the 0xc0000 mapping.
2644f5e7dd7Smrg *
2654f5e7dd7Smrg * This function should be extended to handle access through PCI resources,
2664f5e7dd7Smrg * which should be more reliable when available.
2674f5e7dd7Smrg */
2684f5e7dd7Smrgstatic int
2694f5e7dd7Smrgpci_device_freebsd_read_rom( struct pci_device * dev, void * buffer )
2704f5e7dd7Smrg{
2714f5e7dd7Smrg    struct pci_device_private *priv = (struct pci_device_private *) dev;
2724f5e7dd7Smrg    void *bios;
2734f5e7dd7Smrg    pciaddr_t rom_base;
2744f5e7dd7Smrg    uint32_t rom;
2754f5e7dd7Smrg    uint16_t reg;
2764f5e7dd7Smrg    int pci_rom, memfd;
2774f5e7dd7Smrg
2784f5e7dd7Smrg    if ( ( dev->device_class & 0x00ffff00 ) !=
2794f5e7dd7Smrg	 ( ( PCIC_DISPLAY << 16 ) | ( PCIS_DISPLAY_VGA << 8 ) ) )
2804f5e7dd7Smrg    {
2814f5e7dd7Smrg	return ENOSYS;
2824f5e7dd7Smrg    }
2834f5e7dd7Smrg
2844f5e7dd7Smrg    if (priv->rom_base == 0) {
2854f5e7dd7Smrg#if defined(__amd64__) || defined(__i386__)
2864f5e7dd7Smrg	rom_base = 0xc0000;
2874f5e7dd7Smrg	pci_rom = 0;
2884f5e7dd7Smrg#else
2894f5e7dd7Smrg	return ENOSYS;
2904f5e7dd7Smrg#endif
2914f5e7dd7Smrg    } else {
2924f5e7dd7Smrg	rom_base = priv->rom_base;
2934f5e7dd7Smrg	pci_rom = 1;
2944f5e7dd7Smrg
2954f5e7dd7Smrg	pci_device_cfg_read_u16( dev, &reg, PCIR_COMMAND );
2964f5e7dd7Smrg	pci_device_cfg_write_u16( dev, reg | PCIM_CMD_MEMEN, PCIR_COMMAND );
2974f5e7dd7Smrg	pci_device_cfg_read_u32( dev, &rom, PCIR_BIOS );
2984f5e7dd7Smrg	pci_device_cfg_write_u32( dev, rom | PCIM_BIOS_ENABLE, PCIR_BIOS );
2994f5e7dd7Smrg    }
3004f5e7dd7Smrg
3014f5e7dd7Smrg    printf("Using rom_base = 0x%lx\n", (long)rom_base);
302cad31331Smrg    memfd = open( "/dev/mem", O_RDONLY | O_CLOEXEC );
3034f5e7dd7Smrg    if ( memfd == -1 )
3044f5e7dd7Smrg	return errno;
3054f5e7dd7Smrg
3064f5e7dd7Smrg    bios = mmap( NULL, dev->rom_size, PROT_READ, 0, memfd, rom_base );
3074f5e7dd7Smrg    if ( bios == MAP_FAILED ) {
3084f5e7dd7Smrg	close( memfd );
3094f5e7dd7Smrg	return errno;
3104f5e7dd7Smrg    }
3114f5e7dd7Smrg
3124f5e7dd7Smrg    memcpy( buffer, bios, dev->rom_size );
3134f5e7dd7Smrg
3144f5e7dd7Smrg    munmap( bios, dev->rom_size );
3154f5e7dd7Smrg    close( memfd );
3164f5e7dd7Smrg
3174f5e7dd7Smrg    if (pci_rom) {
3184f5e7dd7Smrg	pci_device_cfg_write_u32( dev, PCIR_BIOS, rom );
3194f5e7dd7Smrg	pci_device_cfg_write_u16( dev, PCIR_COMMAND, reg );
3204f5e7dd7Smrg    }
3214f5e7dd7Smrg
3224f5e7dd7Smrg    return 0;
3234f5e7dd7Smrg}
3244f5e7dd7Smrg
3254f5e7dd7Smrg/** Returns the number of regions (base address registers) the device has */
3264f5e7dd7Smrg
3274f5e7dd7Smrgstatic int
3284f5e7dd7Smrgpci_device_freebsd_get_num_regions( struct pci_device * dev )
3294f5e7dd7Smrg{
3304f5e7dd7Smrg    struct pci_device_private *priv = (struct pci_device_private *) dev;
3314f5e7dd7Smrg
3324f5e7dd7Smrg    switch (priv->header_type) {
3334f5e7dd7Smrg    case 0:
3344f5e7dd7Smrg	return 6;
3354f5e7dd7Smrg    case 1:
3364f5e7dd7Smrg	return 2;
3374f5e7dd7Smrg    case 2:
3384f5e7dd7Smrg	return 1;
3394f5e7dd7Smrg    default:
3404f5e7dd7Smrg	printf("unknown header type %02x\n", priv->header_type);
3414f5e7dd7Smrg	return 0;
3424f5e7dd7Smrg    }
3434f5e7dd7Smrg}
3444f5e7dd7Smrg
3454f5e7dd7Smrgstatic int
3464f5e7dd7Smrgpci_device_freebsd_probe( struct pci_device * dev )
3474f5e7dd7Smrg{
3484f5e7dd7Smrg    struct pci_bar_io bar;
3494f5e7dd7Smrg    uint8_t irq;
3504f5e7dd7Smrg    int err, i;
3514f5e7dd7Smrg
3524f5e7dd7Smrg    bar.pbi_sel.pc_domain = dev->domain;
3534f5e7dd7Smrg    bar.pbi_sel.pc_bus = dev->bus;
3544f5e7dd7Smrg    bar.pbi_sel.pc_dev = dev->dev;
3554f5e7dd7Smrg    bar.pbi_sel.pc_func = dev->func;
3564f5e7dd7Smrg
3574f5e7dd7Smrg
3584f5e7dd7Smrg    /* Many of the fields were filled in during initial device enumeration.
3594f5e7dd7Smrg     * At this point, we need to fill in regions, rom_size, and irq.
3604f5e7dd7Smrg     */
3614f5e7dd7Smrg
3624f5e7dd7Smrg    err = pci_device_cfg_read_u8( dev, &irq, 60 );
3634f5e7dd7Smrg    if (err)
3644f5e7dd7Smrg	return errno;
3654f5e7dd7Smrg    dev->irq = irq;
3664f5e7dd7Smrg
3674f5e7dd7Smrg    for (i = 0; i < pci_device_freebsd_get_num_regions( dev ); i++) {
3684f5e7dd7Smrg	bar.pbi_reg = PCIR_BAR(i);
369cad31331Smrg	if ( ioctl( freebsd_pci_sys->pcidev, PCIOCGETBAR, &bar ) < 0 )
3704f5e7dd7Smrg	    continue;
3714f5e7dd7Smrg
3724f5e7dd7Smrg	if (PCI_BAR_IO(bar.pbi_base))
3734f5e7dd7Smrg	    dev->regions[i].is_IO = 1;
3744f5e7dd7Smrg
3754f5e7dd7Smrg	if ((bar.pbi_base & PCIM_BAR_MEM_TYPE) == PCIM_BAR_MEM_64)
3764f5e7dd7Smrg	    dev->regions[i].is_64 = 1;
3774f5e7dd7Smrg
3784f5e7dd7Smrg	if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH)
3794f5e7dd7Smrg	    dev->regions[i].is_prefetchable = 1;
3804f5e7dd7Smrg
3814f5e7dd7Smrg	dev->regions[i].base_addr = bar.pbi_base & ~((uint64_t)0xf);
3824f5e7dd7Smrg	dev->regions[i].size = bar.pbi_length;
3834f5e7dd7Smrg    }
3844f5e7dd7Smrg
3854f5e7dd7Smrg    /* If it's a VGA device, set up the rom size for read_rom using the
3864f5e7dd7Smrg     * 0xc0000 mapping.
3874f5e7dd7Smrg     */
3884f5e7dd7Smrg     if ((dev->device_class & 0x00ffff00) ==
3894f5e7dd7Smrg	((PCIC_DISPLAY << 16) | (PCIS_DISPLAY_VGA << 8))) {
3904f5e7dd7Smrg	     dev->rom_size = 64 * 1024;
3914f5e7dd7Smrg     }
3924f5e7dd7Smrg
3934f5e7dd7Smrg     return 0;
3944f5e7dd7Smrg}
3954f5e7dd7Smrg
3962029f493Smrgstatic void
3972029f493Smrgpci_system_freebsd_destroy( void )
3984f5e7dd7Smrg{
3992029f493Smrg    close(freebsd_pci_sys->pcidev);
4002029f493Smrg    free(freebsd_pci_sys->pci_sys.devices);
4012029f493Smrg    freebsd_pci_sys = NULL;
4024f5e7dd7Smrg}
4034f5e7dd7Smrg
4044f5e7dd7Smrgstatic int
4052029f493Smrgpci_device_freebsd_has_kernel_driver( struct pci_device *dev )
4064f5e7dd7Smrg{
4072029f493Smrg    struct pci_io io;
4084f5e7dd7Smrg
4092029f493Smrg    io.pi_sel.pc_domain = dev->domain;
4102029f493Smrg    io.pi_sel.pc_bus = dev->bus;
4112029f493Smrg    io.pi_sel.pc_dev = dev->dev;
4122029f493Smrg    io.pi_sel.pc_func = dev->func;
4132029f493Smrg
4142029f493Smrg    if ( ioctl( freebsd_pci_sys->pcidev, PCIOCATTACHED, &io ) < 0 ) {
4152029f493Smrg	return 0;
4164f5e7dd7Smrg    }
4174f5e7dd7Smrg
4182029f493Smrg    /* if io.pi_data is 0, no driver is attached */
4192029f493Smrg    return io.pi_data == 0 ? 0 : 1;
4204f5e7dd7Smrg}
4214f5e7dd7Smrg
4222029f493Smrgstatic struct pci_io_handle *
4232029f493Smrgpci_device_freebsd_open_legacy_io( struct pci_io_handle *ret,
4242029f493Smrg				   struct pci_device *dev, pciaddr_t base,
4252029f493Smrg				   pciaddr_t size )
4264f5e7dd7Smrg{
4272029f493Smrg    ret->fd = open( "/dev/io", O_RDWR | O_CLOEXEC );
4282029f493Smrg    if ( ret->fd < 0 )
4292029f493Smrg	return NULL;
4302029f493Smrg    ret->base = base;
4312029f493Smrg    ret->size = size;
4322029f493Smrg    ret->is_legacy = 1;
4332029f493Smrg    return ret;
4344f5e7dd7Smrg}
4354f5e7dd7Smrg
43686ea1d58Smrgstatic struct pci_io_handle *
4372029f493Smrgpci_device_freebsd_open_io( struct pci_io_handle *ret,
4382029f493Smrg			    struct pci_device *dev, int bar,
4392029f493Smrg			    pciaddr_t base, pciaddr_t size )
44086ea1d58Smrg{
4412029f493Smrg    ret = pci_device_freebsd_open_legacy_io( ret, dev, base, size );
4422029f493Smrg    if ( ret != NULL )
4432029f493Smrg	ret->is_legacy = 0;
4442029f493Smrg    return ret;
44586ea1d58Smrg}
44686ea1d58Smrg
44786ea1d58Smrgstatic void
4482029f493Smrgpci_device_freebsd_close_io( struct pci_device *dev,
4492029f493Smrg			     struct pci_io_handle *handle )
45086ea1d58Smrg{
4512029f493Smrg    if ( handle->fd > -1 )
4522029f493Smrg	close( handle->fd );
4532029f493Smrg}
45486ea1d58Smrg
45586ea1d58Smrgstatic uint32_t
4562029f493Smrgpci_device_freebsd_read32( struct pci_io_handle *handle, uint32_t reg )
45786ea1d58Smrg{
4585ad99bdfSmrg#if defined(__i386__) || defined(__amd64__)
4592029f493Smrg    return inl( handle->base + reg );
46086ea1d58Smrg#else
4612029f493Smrg    struct iodev_pio_req req = { IODEV_PIO_READ, handle->base + reg, 4, 0 };
4622029f493Smrg    if ( handle->fd > -1 )
4632029f493Smrg	ioctl( handle->fd, IODEV_PIO, &req );
4642029f493Smrg    return req.val;
46586ea1d58Smrg#endif
46686ea1d58Smrg}
46786ea1d58Smrg
46886ea1d58Smrgstatic uint16_t
4692029f493Smrgpci_device_freebsd_read16( struct pci_io_handle *handle, uint32_t reg )
47086ea1d58Smrg{
4715ad99bdfSmrg#if defined(__i386__) || defined(__amd64__)
4722029f493Smrg    return inw( handle->base + reg );
47386ea1d58Smrg#else
4742029f493Smrg    struct iodev_pio_req req = { IODEV_PIO_READ, handle->base + reg, 2, 0 };
4752029f493Smrg    if ( handle->fd > -1 )
4762029f493Smrg	ioctl( handle->fd, IODEV_PIO, &req );
4772029f493Smrg    return req.val;
47886ea1d58Smrg#endif
47986ea1d58Smrg}
48086ea1d58Smrg
48186ea1d58Smrgstatic uint8_t
4822029f493Smrgpci_device_freebsd_read8( struct pci_io_handle *handle, uint32_t reg )
48386ea1d58Smrg{
4845ad99bdfSmrg#if defined(__i386__) || defined(__amd64__)
4852029f493Smrg    return inb( handle->base + reg );
48686ea1d58Smrg#else
4872029f493Smrg    struct iodev_pio_req req = { IODEV_PIO_READ, handle->base + reg, 1, 0 };
4882029f493Smrg    if ( handle->fd > -1 )
4892029f493Smrg	ioctl( handle->fd, IODEV_PIO, &req );
4902029f493Smrg    return req.val;
49186ea1d58Smrg#endif
49286ea1d58Smrg}
49386ea1d58Smrg
49486ea1d58Smrgstatic void
4952029f493Smrgpci_device_freebsd_write32( struct pci_io_handle *handle, uint32_t reg,
4962029f493Smrg			    uint32_t data )
49786ea1d58Smrg{
4985ad99bdfSmrg#if defined(__i386__) || defined(__amd64__)
4992029f493Smrg    outl( handle->base + reg, data );
50086ea1d58Smrg#else
5012029f493Smrg    struct iodev_pio_req req = { IODEV_PIO_WRITE, handle->base + reg, 4, data };
5022029f493Smrg    if ( handle->fd > -1 )
5032029f493Smrg	ioctl( handle->fd, IODEV_PIO, &req );
50486ea1d58Smrg#endif
50586ea1d58Smrg}
50686ea1d58Smrg
50786ea1d58Smrgstatic void
5082029f493Smrgpci_device_freebsd_write16( struct pci_io_handle *handle, uint32_t reg,
5092029f493Smrg			    uint16_t data )
51086ea1d58Smrg{
5115ad99bdfSmrg#if defined(__i386__) || defined(__amd64__)
5122029f493Smrg    outw( handle->base + reg, data );
51386ea1d58Smrg#else
5142029f493Smrg    struct iodev_pio_req req = { IODEV_PIO_WRITE, handle->base + reg, 2, data };
5152029f493Smrg    if ( handle->fd > -1 )
5162029f493Smrg	ioctl( handle->fd, IODEV_PIO, &req );
51786ea1d58Smrg#endif
51886ea1d58Smrg}
51986ea1d58Smrg
52086ea1d58Smrgstatic void
5212029f493Smrgpci_device_freebsd_write8( struct pci_io_handle *handle, uint32_t reg,
5222029f493Smrg			   uint8_t data )
52386ea1d58Smrg{
5245ad99bdfSmrg#if defined(__i386__) || defined(__amd64__)
5252029f493Smrg    outb( handle->base + reg, data );
52686ea1d58Smrg#else
5272029f493Smrg    struct iodev_pio_req req = { IODEV_PIO_WRITE, handle->base + reg, 1, data };
5282029f493Smrg    if ( handle->fd > -1 )
5292029f493Smrg	ioctl( handle->fd, IODEV_PIO, &req );
53086ea1d58Smrg#endif
53186ea1d58Smrg}
53286ea1d58Smrg
53386ea1d58Smrgstatic int
5342029f493Smrgpci_device_freebsd_map_legacy( struct pci_device *dev, pciaddr_t base,
5352029f493Smrg			       pciaddr_t size, unsigned map_flags, void **addr )
53686ea1d58Smrg{
5372029f493Smrg    struct pci_device_mapping map;
5382029f493Smrg    int err;
53986ea1d58Smrg
5402029f493Smrg    map.base = base;
5412029f493Smrg    map.size = size;
5422029f493Smrg    map.flags = map_flags;
5432029f493Smrg    map.memory = NULL;
5442029f493Smrg    err = pci_device_freebsd_map_range( dev, &map );
5452029f493Smrg    *addr = map.memory;
54686ea1d58Smrg
5472029f493Smrg    return err;
54886ea1d58Smrg}
54986ea1d58Smrg
55086ea1d58Smrgstatic int
5512029f493Smrgpci_device_freebsd_unmap_legacy( struct pci_device *dev, void *addr,
5522029f493Smrg				 pciaddr_t size )
55386ea1d58Smrg{
5542029f493Smrg    struct pci_device_mapping map;
55586ea1d58Smrg
5562029f493Smrg    map.memory = addr;
5572029f493Smrg    map.size = size;
5582029f493Smrg    map.flags = 0;
5592029f493Smrg    return pci_device_freebsd_unmap_range( dev, &map );
56086ea1d58Smrg}
56186ea1d58Smrg
5624f5e7dd7Smrgstatic const struct pci_system_methods freebsd_pci_methods = {
5634f5e7dd7Smrg    .destroy = pci_system_freebsd_destroy,
5644f5e7dd7Smrg    .destroy_device = NULL, /* nothing to do for this */
5654f5e7dd7Smrg    .read_rom = pci_device_freebsd_read_rom,
5664f5e7dd7Smrg    .probe = pci_device_freebsd_probe,
5674f5e7dd7Smrg    .map_range = pci_device_freebsd_map_range,
5684f5e7dd7Smrg    .unmap_range = pci_device_freebsd_unmap_range,
5692029f493Smrg
5704f5e7dd7Smrg    .read = pci_device_freebsd_read,
5714f5e7dd7Smrg    .write = pci_device_freebsd_write,
5722029f493Smrg
5734f5e7dd7Smrg    .fill_capabilities = pci_fill_capabilities_generic,
5742029f493Smrg    .enable = NULL,
5752029f493Smrg    .boot_vga = NULL,
5762029f493Smrg    .has_kernel_driver = pci_device_freebsd_has_kernel_driver,
5772029f493Smrg
5782029f493Smrg    .open_device_io = pci_device_freebsd_open_io,
57986ea1d58Smrg    .open_legacy_io = pci_device_freebsd_open_legacy_io,
58086ea1d58Smrg    .close_io = pci_device_freebsd_close_io,
58186ea1d58Smrg    .read32 = pci_device_freebsd_read32,
58286ea1d58Smrg    .read16 = pci_device_freebsd_read16,
58386ea1d58Smrg    .read8 = pci_device_freebsd_read8,
58486ea1d58Smrg    .write32 = pci_device_freebsd_write32,
58586ea1d58Smrg    .write16 = pci_device_freebsd_write16,
58686ea1d58Smrg    .write8 = pci_device_freebsd_write8,
5872029f493Smrg
58886ea1d58Smrg    .map_legacy = pci_device_freebsd_map_legacy,
58986ea1d58Smrg    .unmap_legacy = pci_device_freebsd_unmap_legacy,
5904f5e7dd7Smrg};
5914f5e7dd7Smrg
5924f5e7dd7Smrg/**
5934f5e7dd7Smrg * Attempt to access the FreeBSD PCI interface.
5944f5e7dd7Smrg */
5954f5e7dd7Smrg_pci_hidden int
5964f5e7dd7Smrgpci_system_freebsd_create( void )
5974f5e7dd7Smrg{
5984f5e7dd7Smrg    struct pci_conf_io pciconfio;
5994f5e7dd7Smrg    struct pci_conf pciconf[255];
6004f5e7dd7Smrg    int pcidev;
6014f5e7dd7Smrg    int i;
6024f5e7dd7Smrg
6034f5e7dd7Smrg    /* Try to open the PCI device */
604cad31331Smrg    pcidev = open( "/dev/pci", O_RDWR | O_CLOEXEC );
6054f5e7dd7Smrg    if ( pcidev == -1 )
6064f5e7dd7Smrg	return ENXIO;
6074f5e7dd7Smrg
6084f5e7dd7Smrg    freebsd_pci_sys = calloc( 1, sizeof( struct freebsd_pci_system ) );
6094f5e7dd7Smrg    if ( freebsd_pci_sys == NULL ) {
6104f5e7dd7Smrg	close( pcidev );
6114f5e7dd7Smrg	return ENOMEM;
6124f5e7dd7Smrg    }
6134f5e7dd7Smrg    pci_sys = &freebsd_pci_sys->pci_sys;
6144f5e7dd7Smrg
6154f5e7dd7Smrg    pci_sys->methods = & freebsd_pci_methods;
6164f5e7dd7Smrg    freebsd_pci_sys->pcidev = pcidev;
6174f5e7dd7Smrg
6184f5e7dd7Smrg    /* Probe the list of devices known by the system */
6194f5e7dd7Smrg    bzero( &pciconfio, sizeof( struct pci_conf_io ) );
6204f5e7dd7Smrg    pciconfio.match_buf_len = sizeof(pciconf);
6214f5e7dd7Smrg    pciconfio.matches = pciconf;
6224f5e7dd7Smrg
6234f5e7dd7Smrg    if ( ioctl( pcidev, PCIOCGETCONF, &pciconfio ) == -1) {
6244f5e7dd7Smrg	free( pci_sys );
62548becaf0Smrg	pci_sys = NULL;
6264f5e7dd7Smrg	close( pcidev );
6274f5e7dd7Smrg	return errno;
6284f5e7dd7Smrg    }
6294f5e7dd7Smrg
6304f5e7dd7Smrg    if (pciconfio.status == PCI_GETCONF_ERROR ) {
6314f5e7dd7Smrg	free( pci_sys );
63248becaf0Smrg	pci_sys = NULL;
6334f5e7dd7Smrg	close( pcidev );
6344f5e7dd7Smrg	return EINVAL;
6354f5e7dd7Smrg    }
6364f5e7dd7Smrg
6374f5e7dd7Smrg    /* Translate the list of devices into pciaccess's format. */
6384f5e7dd7Smrg    pci_sys->num_devices = pciconfio.num_matches;
6394f5e7dd7Smrg    pci_sys->devices = calloc( pciconfio.num_matches,
6404f5e7dd7Smrg			       sizeof( struct pci_device_private ) );
6414f5e7dd7Smrg
6424f5e7dd7Smrg    for ( i = 0; i < pciconfio.num_matches; i++ ) {
6434f5e7dd7Smrg	struct pci_conf *p = &pciconf[ i ];
6444f5e7dd7Smrg
6454f5e7dd7Smrg	pci_sys->devices[ i ].base.domain = p->pc_sel.pc_domain;
6464f5e7dd7Smrg	pci_sys->devices[ i ].base.bus = p->pc_sel.pc_bus;
6474f5e7dd7Smrg	pci_sys->devices[ i ].base.dev = p->pc_sel.pc_dev;
6484f5e7dd7Smrg	pci_sys->devices[ i ].base.func = p->pc_sel.pc_func;
6494f5e7dd7Smrg	pci_sys->devices[ i ].base.vendor_id = p->pc_vendor;
6504f5e7dd7Smrg	pci_sys->devices[ i ].base.device_id = p->pc_device;
6514f5e7dd7Smrg	pci_sys->devices[ i ].base.subvendor_id = p->pc_subvendor;
6524f5e7dd7Smrg	pci_sys->devices[ i ].base.subdevice_id = p->pc_subdevice;
6534f5e7dd7Smrg	pci_sys->devices[ i ].base.revision = p->pc_revid;
6544f5e7dd7Smrg	pci_sys->devices[ i ].base.device_class = (uint32_t)p->pc_class << 16 |
6554f5e7dd7Smrg	    (uint32_t)p->pc_subclass << 8 | (uint32_t)p->pc_progif;
6564f5e7dd7Smrg	pci_sys->devices[ i ].header_type = p->pc_hdr & 0x7f;
6574f5e7dd7Smrg    }
6584f5e7dd7Smrg
6594f5e7dd7Smrg    return 0;
6604f5e7dd7Smrg}
661