15a04522aSIan Romanick /* 25a04522aSIan Romanick * (C) Copyright IBM Corporation 2006 35a04522aSIan Romanick * All Rights Reserved. 45a04522aSIan Romanick * 55a04522aSIan Romanick * Permission is hereby granted, free of charge, to any person obtaining a 65a04522aSIan Romanick * copy of this software and associated documentation files (the "Software"), 75a04522aSIan Romanick * to deal in the Software without restriction, including without limitation 85a04522aSIan Romanick * on the rights to use, copy, modify, merge, publish, distribute, sub 95a04522aSIan Romanick * license, and/or sell copies of the Software, and to permit persons to whom 105a04522aSIan Romanick * the Software is furnished to do so, subject to the following conditions: 115a04522aSIan Romanick * 125a04522aSIan Romanick * The above copyright notice and this permission notice (including the next 135a04522aSIan Romanick * paragraph) shall be included in all copies or substantial portions of the 145a04522aSIan Romanick * Software. 155a04522aSIan Romanick * 165a04522aSIan Romanick * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 175a04522aSIan Romanick * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 185a04522aSIan Romanick * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 195a04522aSIan Romanick * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 205a04522aSIan Romanick * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 215a04522aSIan Romanick * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 225a04522aSIan Romanick * DEALINGS IN THE SOFTWARE. 235a04522aSIan Romanick */ 245a04522aSIan Romanick 255a04522aSIan Romanick /** 265a04522aSIan Romanick * \file linux_sysfs.c 275a04522aSIan Romanick * Access PCI subsystem using Linux's sysfs interface. This interface is 285a04522aSIan Romanick * available starting somewhere in the late 2.5.x kernel phase, and is the 29d43d21c8SAlan Coopersmith * preferred method on all 2.6.x kernels. 305a04522aSIan Romanick * 315a04522aSIan Romanick * \author Ian Romanick <[email protected]> 325a04522aSIan Romanick */ 335a04522aSIan Romanick 345a04522aSIan Romanick #define _GNU_SOURCE 355a04522aSIan Romanick 365a04522aSIan Romanick #include <stdlib.h> 375a04522aSIan Romanick #include <string.h> 385a04522aSIan Romanick #include <stdio.h> 395a04522aSIan Romanick #include <unistd.h> 405a04522aSIan Romanick #include <sys/types.h> 415a04522aSIan Romanick #include <sys/stat.h> 425a04522aSIan Romanick #include <fcntl.h> 435a04522aSIan Romanick #include <sys/mman.h> 445a04522aSIan Romanick #include <dirent.h> 455a04522aSIan Romanick #include <errno.h> 465a04522aSIan Romanick 4708ff9f7fSIan Romanick #include "config.h" 4808ff9f7fSIan Romanick 4908ff9f7fSIan Romanick #ifdef HAVE_MTRR 5008ff9f7fSIan Romanick #include <asm/mtrr.h> 5108ff9f7fSIan Romanick #include <sys/ioctl.h> 5208ff9f7fSIan Romanick #endif 5308ff9f7fSIan Romanick 545a04522aSIan Romanick #include "pciaccess.h" 555a04522aSIan Romanick #include "pciaccess_private.h" 56a7faac3dSIan Romanick #include "linux_devmem.h" 575a04522aSIan Romanick 58*5e8d4c19SAdam Jackson static const struct pci_system_methods linux_sysfs_methods; 595a04522aSIan Romanick 605a04522aSIan Romanick #define SYS_BUS_PCI "/sys/bus/pci/devices" 615a04522aSIan Romanick 62*5e8d4c19SAdam Jackson static int 63*5e8d4c19SAdam Jackson pci_device_linux_sysfs_read( struct pci_device * dev, void * data, 64*5e8d4c19SAdam Jackson pciaddr_t offset, pciaddr_t size, 65*5e8d4c19SAdam Jackson pciaddr_t * bytes_read ); 665a04522aSIan Romanick 67061a011cSIan Romanick static int populate_entries(struct pci_system * pci_sys); 685a04522aSIan Romanick 695a04522aSIan Romanick /** 705a04522aSIan Romanick * Attempt to access PCI subsystem using Linux's sysfs interface. 715a04522aSIan Romanick */ 72adc46f65SJulien Cristau _pci_hidden int 735a04522aSIan Romanick pci_system_linux_sysfs_create( void ) 745a04522aSIan Romanick { 755a04522aSIan Romanick int err = 0; 765a04522aSIan Romanick struct stat st; 775a04522aSIan Romanick 785a04522aSIan Romanick 795a04522aSIan Romanick /* If the directory "/sys/bus/pci/devices" exists, then the PCI subsystem 805a04522aSIan Romanick * can be accessed using this interface. 815a04522aSIan Romanick */ 825a04522aSIan Romanick 835a04522aSIan Romanick if ( stat( SYS_BUS_PCI, & st ) == 0 ) { 845a04522aSIan Romanick pci_sys = calloc( 1, sizeof( struct pci_system ) ); 855a04522aSIan Romanick if ( pci_sys != NULL ) { 865a04522aSIan Romanick pci_sys->methods = & linux_sysfs_methods; 874586bb67SAdam Jackson #ifdef HAVE_MTRR 884586bb67SAdam Jackson pci_sys->mtrr_fd = open("/proc/mtrr", O_WRONLY); 894586bb67SAdam Jackson #endif 90061a011cSIan Romanick err = populate_entries(pci_sys); 915a04522aSIan Romanick } 925a04522aSIan Romanick else { 935a04522aSIan Romanick err = ENOMEM; 945a04522aSIan Romanick } 955a04522aSIan Romanick } 965a04522aSIan Romanick else { 975a04522aSIan Romanick err = errno; 985a04522aSIan Romanick } 995a04522aSIan Romanick 1005a04522aSIan Romanick return err; 1015a04522aSIan Romanick } 1025a04522aSIan Romanick 1035a04522aSIan Romanick 1045a04522aSIan Romanick /** 1055a04522aSIan Romanick * Filter out the names "." and ".." from the scanned sysfs entries. 1065a04522aSIan Romanick * 1075a04522aSIan Romanick * \param d Directory entry being processed by \c scandir. 1085a04522aSIan Romanick * 1095a04522aSIan Romanick * \return 1105a04522aSIan Romanick * Zero if the entry name matches either "." or "..", non-zero otherwise. 1115a04522aSIan Romanick * 1125a04522aSIan Romanick * \sa scandir, populate_entries 1135a04522aSIan Romanick */ 1145a04522aSIan Romanick static int 1155a04522aSIan Romanick scan_sys_pci_filter( const struct dirent * d ) 1165a04522aSIan Romanick { 1175a04522aSIan Romanick return !((strcmp( d->d_name, "." ) == 0) 1185a04522aSIan Romanick || (strcmp( d->d_name, ".." ) == 0)); 1195a04522aSIan Romanick } 1205a04522aSIan Romanick 1215a04522aSIan Romanick 122061a011cSIan Romanick int 1235a04522aSIan Romanick populate_entries( struct pci_system * p ) 1245a04522aSIan Romanick { 1255a04522aSIan Romanick struct dirent ** devices; 1265a04522aSIan Romanick int n; 1275a04522aSIan Romanick int i; 12808ff9f7fSIan Romanick int err = 0; 1295a04522aSIan Romanick 1305a04522aSIan Romanick 1315a04522aSIan Romanick n = scandir( SYS_BUS_PCI, & devices, scan_sys_pci_filter, alphasort ); 1325a04522aSIan Romanick if ( n > 0 ) { 1335a04522aSIan Romanick p->num_devices = n; 1345a04522aSIan Romanick p->devices = calloc( n, sizeof( struct pci_device_private ) ); 1355a04522aSIan Romanick 136061a011cSIan Romanick if (p->devices != NULL) { 1375a04522aSIan Romanick for (i = 0 ; i < n ; i++) { 138061a011cSIan Romanick uint8_t config[48]; 139061a011cSIan Romanick pciaddr_t bytes; 1405a04522aSIan Romanick unsigned dom, bus, dev, func; 141061a011cSIan Romanick struct pci_device_private *device = 142061a011cSIan Romanick (struct pci_device_private *) &p->devices[i]; 1435a04522aSIan Romanick 1445a04522aSIan Romanick 1455a04522aSIan Romanick sscanf(devices[i]->d_name, "%04x:%02x:%02x.%1u", 1465a04522aSIan Romanick & dom, & bus, & dev, & func); 1475a04522aSIan Romanick 148061a011cSIan Romanick device->base.domain = dom; 149061a011cSIan Romanick device->base.bus = bus; 150061a011cSIan Romanick device->base.dev = dev; 151061a011cSIan Romanick device->base.func = func; 152061a011cSIan Romanick 153061a011cSIan Romanick 154061a011cSIan Romanick err = pci_device_linux_sysfs_read(& device->base, config, 0, 155061a011cSIan Romanick 48, & bytes); 156061a011cSIan Romanick if ((bytes == 48) && !err) { 157061a011cSIan Romanick device->base.vendor_id = (uint16_t)config[0] 158061a011cSIan Romanick + ((uint16_t)config[1] << 8); 159061a011cSIan Romanick device->base.device_id = (uint16_t)config[2] 160061a011cSIan Romanick + ((uint16_t)config[3] << 8); 161061a011cSIan Romanick device->base.device_class = (uint32_t)config[9] 162061a011cSIan Romanick + ((uint32_t)config[10] << 8) 163061a011cSIan Romanick + ((uint32_t)config[11] << 16); 164061a011cSIan Romanick device->base.revision = config[8]; 165061a011cSIan Romanick device->base.subvendor_id = (uint16_t)config[44] 166061a011cSIan Romanick + ((uint16_t)config[45] << 8); 167061a011cSIan Romanick device->base.subdevice_id = (uint16_t)config[46] 168061a011cSIan Romanick + ((uint16_t)config[47] << 8); 169061a011cSIan Romanick } 170061a011cSIan Romanick 171061a011cSIan Romanick if (err) { 172061a011cSIan Romanick break; 1735a04522aSIan Romanick } 1745a04522aSIan Romanick } 1755a04522aSIan Romanick } 176061a011cSIan Romanick else { 177061a011cSIan Romanick err = ENOMEM; 178061a011cSIan Romanick } 179061a011cSIan Romanick } 180061a011cSIan Romanick 181cdbdfaf6SKeith Packard for (i = 0; i < n; i++) 182cdbdfaf6SKeith Packard free(devices[i]); 183cdbdfaf6SKeith Packard free(devices); 184cdbdfaf6SKeith Packard 185061a011cSIan Romanick if (err) { 186061a011cSIan Romanick free(p->devices); 187061a011cSIan Romanick p->devices = NULL; 188061a011cSIan Romanick } 189061a011cSIan Romanick 190061a011cSIan Romanick return err; 191061a011cSIan Romanick } 1925a04522aSIan Romanick 1935a04522aSIan Romanick 1945a04522aSIan Romanick static int 1955a04522aSIan Romanick pci_device_linux_sysfs_probe( struct pci_device * dev ) 1965a04522aSIan Romanick { 1975a04522aSIan Romanick char name[256]; 1985a04522aSIan Romanick uint8_t config[256]; 1995a04522aSIan Romanick char resource[512]; 2005a04522aSIan Romanick int fd; 2015a04522aSIan Romanick pciaddr_t bytes; 2025a04522aSIan Romanick unsigned i; 2035a04522aSIan Romanick int err; 2045a04522aSIan Romanick 2055a04522aSIan Romanick 2065a04522aSIan Romanick err = pci_device_linux_sysfs_read( dev, config, 0, 256, & bytes ); 2075a04522aSIan Romanick if ( bytes >= 64 ) { 2082ba1a0e4SIan Romanick struct pci_device_private *priv = (struct pci_device_private *) dev; 2092ba1a0e4SIan Romanick 2105a04522aSIan Romanick dev->irq = config[60]; 2112ba1a0e4SIan Romanick priv->header_type = config[14]; 2122ba1a0e4SIan Romanick 2135a04522aSIan Romanick 2145a04522aSIan Romanick /* The PCI config registers can be used to obtain information 2155a04522aSIan Romanick * about the memory and I/O regions for the device. However, 2165a04522aSIan Romanick * doing so requires some tricky parsing (to correctly handle 2175a04522aSIan Romanick * 64-bit memory regions) and requires writing to the config 2185a04522aSIan Romanick * registers. Since we'd like to avoid having to deal with the 2195a04522aSIan Romanick * parsing issues and non-root users can write to PCI config 2205a04522aSIan Romanick * registers, we use a different file in the device's sysfs 2215a04522aSIan Romanick * directory called "resource". 2225a04522aSIan Romanick * 2235a04522aSIan Romanick * The resource file contains all of the needed information in 2245a04522aSIan Romanick * a format that is consistent across all platforms. Each BAR 2255a04522aSIan Romanick * and the expansion ROM have a single line of data containing 2265a04522aSIan Romanick * 3, 64-bit hex values: the first address in the region, 2275a04522aSIan Romanick * the last address in the region, and the region's flags. 2285a04522aSIan Romanick */ 2295a04522aSIan Romanick snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/resource", 2305a04522aSIan Romanick SYS_BUS_PCI, 2315a04522aSIan Romanick dev->domain, 2325a04522aSIan Romanick dev->bus, 2335a04522aSIan Romanick dev->dev, 2345a04522aSIan Romanick dev->func ); 2355a04522aSIan Romanick fd = open( name, O_RDONLY ); 2365a04522aSIan Romanick if ( fd != -1 ) { 2375a04522aSIan Romanick char * next; 2385a04522aSIan Romanick pciaddr_t low_addr; 2395a04522aSIan Romanick pciaddr_t high_addr; 2405a04522aSIan Romanick pciaddr_t flags; 2415a04522aSIan Romanick 2425a04522aSIan Romanick 2435a04522aSIan Romanick bytes = read( fd, resource, 512 ); 2445a04522aSIan Romanick resource[511] = '\0'; 2455a04522aSIan Romanick 2465a04522aSIan Romanick close( fd ); 2475a04522aSIan Romanick 2485a04522aSIan Romanick next = resource; 2495a04522aSIan Romanick for ( i = 0 ; i < 6 ; i++ ) { 2505a04522aSIan Romanick 2515a04522aSIan Romanick dev->regions[i].base_addr = strtoull( next, & next, 16 ); 2525a04522aSIan Romanick high_addr = strtoull( next, & next, 16 ); 2535a04522aSIan Romanick flags = strtoull( next, & next, 16 ); 2545a04522aSIan Romanick 2555a04522aSIan Romanick if ( dev->regions[i].base_addr != 0 ) { 2565a04522aSIan Romanick dev->regions[i].size = (high_addr 2575a04522aSIan Romanick - dev->regions[i].base_addr) + 1; 2585a04522aSIan Romanick 2595a04522aSIan Romanick dev->regions[i].is_IO = (flags & 0x01); 2605a04522aSIan Romanick dev->regions[i].is_64 = (flags & 0x04); 2615a04522aSIan Romanick dev->regions[i].is_prefetchable = (flags & 0x08); 2625a04522aSIan Romanick } 2635a04522aSIan Romanick } 2645a04522aSIan Romanick 2655a04522aSIan Romanick low_addr = strtoull( next, & next, 16 ); 2665a04522aSIan Romanick high_addr = strtoull( next, & next, 16 ); 2675a04522aSIan Romanick flags = strtoull( next, & next, 16 ); 2685a04522aSIan Romanick if ( low_addr != 0 ) { 269a7faac3dSIan Romanick priv->rom_base = low_addr; 2705a04522aSIan Romanick dev->rom_size = (high_addr - low_addr) + 1; 2715a04522aSIan Romanick } 2725a04522aSIan Romanick } 2735a04522aSIan Romanick } 2745a04522aSIan Romanick 2755a04522aSIan Romanick return err; 2765a04522aSIan Romanick } 2775a04522aSIan Romanick 2785a04522aSIan Romanick 2795a04522aSIan Romanick static int 2805a04522aSIan Romanick pci_device_linux_sysfs_read_rom( struct pci_device * dev, void * buffer ) 2815a04522aSIan Romanick { 2825a04522aSIan Romanick char name[256]; 2835a04522aSIan Romanick int fd; 2845a04522aSIan Romanick struct stat st; 2855a04522aSIan Romanick int err = 0; 286a5c86202SStuart Bennett size_t rom_size; 28764af050cSIan Romanick size_t total_bytes; 2885a04522aSIan Romanick 2895a04522aSIan Romanick 2905a04522aSIan Romanick snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/rom", 2915a04522aSIan Romanick SYS_BUS_PCI, 2925a04522aSIan Romanick dev->domain, 2935a04522aSIan Romanick dev->bus, 2945a04522aSIan Romanick dev->dev, 2955a04522aSIan Romanick dev->func ); 2965a04522aSIan Romanick 2975a04522aSIan Romanick fd = open( name, O_RDWR ); 2985a04522aSIan Romanick if ( fd == -1 ) { 29957cf6f1fSDave Airlie #ifdef LINUX_ROM 300a7faac3dSIan Romanick /* If reading the ROM using sysfs fails, fall back to the old 301a7faac3dSIan Romanick * /dev/mem based interface. 30257cf6f1fSDave Airlie * disable this for newer kernels using configure 303a7faac3dSIan Romanick */ 304a7faac3dSIan Romanick return pci_device_linux_devmem_read_rom(dev, buffer); 30557cf6f1fSDave Airlie #else 30657cf6f1fSDave Airlie return errno; 30757cf6f1fSDave Airlie #endif 3085a04522aSIan Romanick } 3095a04522aSIan Romanick 3105a04522aSIan Romanick 3115a04522aSIan Romanick if ( fstat( fd, & st ) == -1 ) { 3125a04522aSIan Romanick close( fd ); 3135a04522aSIan Romanick return errno; 3145a04522aSIan Romanick } 3155a04522aSIan Romanick 316a5c86202SStuart Bennett rom_size = st.st_size; 317a5c86202SStuart Bennett if ( rom_size == 0 ) 318a5c86202SStuart Bennett rom_size = 0x10000; 3195a04522aSIan Romanick 3205a04522aSIan Romanick /* This is a quirky thing on Linux. Even though the ROM and the file 3215a04522aSIan Romanick * for the ROM in sysfs are read-only, the string "1" must be written to 3225a04522aSIan Romanick * the file to enable the ROM. After the data has been read, "0" must be 3235a04522aSIan Romanick * written to the file to disable the ROM. 3245a04522aSIan Romanick */ 3255a04522aSIan Romanick write( fd, "1", 1 ); 3265a04522aSIan Romanick lseek( fd, 0, SEEK_SET ); 3275a04522aSIan Romanick 328a5c86202SStuart Bennett for ( total_bytes = 0 ; total_bytes < rom_size ; /* empty */ ) { 32964af050cSIan Romanick const int bytes = read( fd, (char *) buffer + total_bytes, 330a5c86202SStuart Bennett rom_size - total_bytes ); 33164af050cSIan Romanick if ( bytes == -1 ) { 3325a04522aSIan Romanick err = errno; 33364af050cSIan Romanick break; 3345a04522aSIan Romanick } 33564af050cSIan Romanick else if ( bytes == 0 ) { 33664af050cSIan Romanick break; 33764af050cSIan Romanick } 33864af050cSIan Romanick 33964af050cSIan Romanick total_bytes += bytes; 34064af050cSIan Romanick } 34164af050cSIan Romanick 3425a04522aSIan Romanick 3435a04522aSIan Romanick lseek( fd, 0, SEEK_SET ); 3445a04522aSIan Romanick write( fd, "0", 1 ); 3455a04522aSIan Romanick 3465a04522aSIan Romanick close( fd ); 3475a04522aSIan Romanick return err; 3485a04522aSIan Romanick } 3495a04522aSIan Romanick 3505a04522aSIan Romanick 3515a04522aSIan Romanick static int 3525a04522aSIan Romanick pci_device_linux_sysfs_read( struct pci_device * dev, void * data, 3535a04522aSIan Romanick pciaddr_t offset, pciaddr_t size, 3545a04522aSIan Romanick pciaddr_t * bytes_read ) 3555a04522aSIan Romanick { 3565a04522aSIan Romanick char name[256]; 3575a04522aSIan Romanick pciaddr_t temp_size = size; 3585a04522aSIan Romanick int err = 0; 3595a04522aSIan Romanick int fd; 3605cf29b06SKeith Packard char *data_bytes = data; 3615a04522aSIan Romanick 3625a04522aSIan Romanick if ( bytes_read != NULL ) { 3635a04522aSIan Romanick *bytes_read = 0; 3645a04522aSIan Romanick } 3655a04522aSIan Romanick 3665a04522aSIan Romanick /* Each device has a directory under sysfs. Within that directory there 3675a04522aSIan Romanick * is a file named "config". This file used to access the PCI config 3685a04522aSIan Romanick * space. It is used here to obtain most of the information about the 3695a04522aSIan Romanick * device. 3705a04522aSIan Romanick */ 3715a04522aSIan Romanick snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/config", 3725a04522aSIan Romanick SYS_BUS_PCI, 3735a04522aSIan Romanick dev->domain, 3745a04522aSIan Romanick dev->bus, 3755a04522aSIan Romanick dev->dev, 3765a04522aSIan Romanick dev->func ); 3775a04522aSIan Romanick 3785a04522aSIan Romanick fd = open( name, O_RDONLY ); 3795a04522aSIan Romanick if ( fd == -1 ) { 3805a04522aSIan Romanick return errno; 3815a04522aSIan Romanick } 3825a04522aSIan Romanick 3835a04522aSIan Romanick 3845a04522aSIan Romanick while ( temp_size > 0 ) { 3855cf29b06SKeith Packard const ssize_t bytes = pread64( fd, data_bytes, temp_size, offset ); 3865a04522aSIan Romanick 3875a04522aSIan Romanick /* If zero bytes were read, then we assume it's the end of the 3885a04522aSIan Romanick * config file. 3895a04522aSIan Romanick */ 3905a04522aSIan Romanick if ( bytes <= 0 ) { 3915a04522aSIan Romanick err = errno; 3925a04522aSIan Romanick break; 3935a04522aSIan Romanick } 3945a04522aSIan Romanick 3955a04522aSIan Romanick temp_size -= bytes; 3965a04522aSIan Romanick offset += bytes; 3975cf29b06SKeith Packard data_bytes += bytes; 3985a04522aSIan Romanick } 3995a04522aSIan Romanick 4005a04522aSIan Romanick if ( bytes_read != NULL ) { 4015a04522aSIan Romanick *bytes_read = size - temp_size; 4025a04522aSIan Romanick } 4035a04522aSIan Romanick 4045a04522aSIan Romanick close( fd ); 4055a04522aSIan Romanick return err; 4065a04522aSIan Romanick } 4075a04522aSIan Romanick 4085a04522aSIan Romanick 4095a04522aSIan Romanick static int 4105a04522aSIan Romanick pci_device_linux_sysfs_write( struct pci_device * dev, const void * data, 4115a04522aSIan Romanick pciaddr_t offset, pciaddr_t size, 4125a04522aSIan Romanick pciaddr_t * bytes_written ) 4135a04522aSIan Romanick { 4145a04522aSIan Romanick char name[256]; 4155a04522aSIan Romanick pciaddr_t temp_size = size; 4165a04522aSIan Romanick int err = 0; 4175a04522aSIan Romanick int fd; 4185cf29b06SKeith Packard const char *data_bytes = data; 4195a04522aSIan Romanick 4205a04522aSIan Romanick if ( bytes_written != NULL ) { 4215a04522aSIan Romanick *bytes_written = 0; 4225a04522aSIan Romanick } 4235a04522aSIan Romanick 4245a04522aSIan Romanick /* Each device has a directory under sysfs. Within that directory there 4255a04522aSIan Romanick * is a file named "config". This file used to access the PCI config 4265a04522aSIan Romanick * space. It is used here to obtain most of the information about the 4275a04522aSIan Romanick * device. 4285a04522aSIan Romanick */ 4295a04522aSIan Romanick snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/config", 4305a04522aSIan Romanick SYS_BUS_PCI, 4315a04522aSIan Romanick dev->domain, 4325a04522aSIan Romanick dev->bus, 4335a04522aSIan Romanick dev->dev, 4345a04522aSIan Romanick dev->func ); 4355a04522aSIan Romanick 4365a04522aSIan Romanick fd = open( name, O_WRONLY ); 4375a04522aSIan Romanick if ( fd == -1 ) { 4385a04522aSIan Romanick return errno; 4395a04522aSIan Romanick } 4405a04522aSIan Romanick 4415a04522aSIan Romanick 4425a04522aSIan Romanick while ( temp_size > 0 ) { 4435cf29b06SKeith Packard const ssize_t bytes = pwrite64( fd, data_bytes, temp_size, offset ); 4445a04522aSIan Romanick 4455a04522aSIan Romanick /* If zero bytes were written, then we assume it's the end of the 4465a04522aSIan Romanick * config file. 4475a04522aSIan Romanick */ 4485a04522aSIan Romanick if ( bytes <= 0 ) { 4495a04522aSIan Romanick err = errno; 4505a04522aSIan Romanick break; 4515a04522aSIan Romanick } 4525a04522aSIan Romanick 4535a04522aSIan Romanick temp_size -= bytes; 4545a04522aSIan Romanick offset += bytes; 4555cf29b06SKeith Packard data_bytes += bytes; 4565a04522aSIan Romanick } 4575a04522aSIan Romanick 4585a04522aSIan Romanick if ( bytes_written != NULL ) { 4595a04522aSIan Romanick *bytes_written = size - temp_size; 4605a04522aSIan Romanick } 4615a04522aSIan Romanick 4625a04522aSIan Romanick close( fd ); 4635a04522aSIan Romanick return err; 4645a04522aSIan Romanick } 4655a04522aSIan Romanick 4667282b53cSJesse Barnes static int 4677282b53cSJesse Barnes pci_device_linux_sysfs_map_range_wc(struct pci_device *dev, 4687282b53cSJesse Barnes struct pci_device_mapping *map) 4697282b53cSJesse Barnes { 4707282b53cSJesse Barnes char name[256]; 4717282b53cSJesse Barnes int fd; 4727282b53cSJesse Barnes const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) 4737282b53cSJesse Barnes ? (PROT_READ | PROT_WRITE) : PROT_READ; 4747282b53cSJesse Barnes const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) 4757282b53cSJesse Barnes ? O_RDWR : O_RDONLY; 4767282b53cSJesse Barnes const off_t offset = map->base - dev->regions[map->region].base_addr; 4777282b53cSJesse Barnes 4787282b53cSJesse Barnes snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/resource%u_wc", 4797282b53cSJesse Barnes SYS_BUS_PCI, 4807282b53cSJesse Barnes dev->domain, 4817282b53cSJesse Barnes dev->bus, 4827282b53cSJesse Barnes dev->dev, 4837282b53cSJesse Barnes dev->func, 4847282b53cSJesse Barnes map->region); 4857282b53cSJesse Barnes fd = open(name, open_flags); 4867282b53cSJesse Barnes if (fd == -1) 4877282b53cSJesse Barnes return errno; 4887282b53cSJesse Barnes 4897282b53cSJesse Barnes map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset); 4907282b53cSJesse Barnes if (map->memory == MAP_FAILED) { 4917282b53cSJesse Barnes map->memory = NULL; 4927282b53cSJesse Barnes close(fd); 4937282b53cSJesse Barnes return errno; 4947282b53cSJesse Barnes } 4957282b53cSJesse Barnes 4967282b53cSJesse Barnes close(fd); 4977282b53cSJesse Barnes 4987282b53cSJesse Barnes return 0; 4997282b53cSJesse Barnes } 5005a04522aSIan Romanick 5015a04522aSIan Romanick /** 5025a04522aSIan Romanick * Map a memory region for a device using the Linux sysfs interface. 5035a04522aSIan Romanick * 5045a04522aSIan Romanick * \param dev Device whose memory region is to be mapped. 50508ff9f7fSIan Romanick * \param map Parameters of the mapping that is to be created. 5065a04522aSIan Romanick * 5075a04522aSIan Romanick * \return 5085a04522aSIan Romanick * Zero on success or an \c errno value on failure. 5095a04522aSIan Romanick * 51008ff9f7fSIan Romanick * \sa pci_device_map_rrange, pci_device_linux_sysfs_unmap_range 5115a04522aSIan Romanick * 5125a04522aSIan Romanick * \todo 5135a04522aSIan Romanick * Some older 2.6.x kernels don't implement the resourceN files. On those 5145a04522aSIan Romanick * systems /dev/mem must be used. On these systems it is also possible that 5155a04522aSIan Romanick * \c mmap64 may need to be used. 5165a04522aSIan Romanick */ 5175a04522aSIan Romanick static int 51808ff9f7fSIan Romanick pci_device_linux_sysfs_map_range(struct pci_device *dev, 51908ff9f7fSIan Romanick struct pci_device_mapping *map) 5205a04522aSIan Romanick { 5215a04522aSIan Romanick char name[256]; 5225a04522aSIan Romanick int fd; 5235a04522aSIan Romanick int err = 0; 52408ff9f7fSIan Romanick const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) 52508ff9f7fSIan Romanick ? (PROT_READ | PROT_WRITE) : PROT_READ; 52608ff9f7fSIan Romanick const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE) != 0) 52708ff9f7fSIan Romanick ? O_RDWR : O_RDONLY; 52808ff9f7fSIan Romanick const off_t offset = map->base - dev->regions[map->region].base_addr; 52908ff9f7fSIan Romanick #ifdef HAVE_MTRR 53008ff9f7fSIan Romanick struct mtrr_sentry sentry = { 53108ff9f7fSIan Romanick .base = map->base, 53208ff9f7fSIan Romanick .size = map->size, 53308ff9f7fSIan Romanick .type = MTRR_TYPE_UNCACHABLE 53408ff9f7fSIan Romanick }; 53508ff9f7fSIan Romanick #endif 5365a04522aSIan Romanick 5377282b53cSJesse Barnes /* For WC mappings, try sysfs resourceN_wc file first */ 5387282b53cSJesse Barnes if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) && 5397282b53cSJesse Barnes !pci_device_linux_sysfs_map_range_wc(dev, map)) 5407282b53cSJesse Barnes return 0; 5417282b53cSJesse Barnes 5425a04522aSIan Romanick snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/resource%u", 5435a04522aSIan Romanick SYS_BUS_PCI, 5445a04522aSIan Romanick dev->domain, 5455a04522aSIan Romanick dev->bus, 5465a04522aSIan Romanick dev->dev, 5475a04522aSIan Romanick dev->func, 54808ff9f7fSIan Romanick map->region); 5495a04522aSIan Romanick 55008ff9f7fSIan Romanick fd = open(name, open_flags); 5515a04522aSIan Romanick if (fd == -1) { 5525a04522aSIan Romanick return errno; 5535a04522aSIan Romanick } 5545a04522aSIan Romanick 5555a04522aSIan Romanick 55608ff9f7fSIan Romanick map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset); 55708ff9f7fSIan Romanick if (map->memory == MAP_FAILED) { 55808ff9f7fSIan Romanick map->memory = NULL; 5595a04522aSIan Romanick close(fd); 560e3adc06bSEric Anholt return errno; 561e3adc06bSEric Anholt } 56208ff9f7fSIan Romanick 56308ff9f7fSIan Romanick #ifdef HAVE_MTRR 56408ff9f7fSIan Romanick if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) != 0) { 56508ff9f7fSIan Romanick sentry.type = MTRR_TYPE_WRBACK; 56608ff9f7fSIan Romanick } else if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) != 0) { 56708ff9f7fSIan Romanick sentry.type = MTRR_TYPE_WRCOMB; 5685a04522aSIan Romanick } 5695a04522aSIan Romanick 5705cf29b06SKeith Packard if (pci_sys->mtrr_fd != -1 && sentry.type != MTRR_TYPE_UNCACHABLE) { 57108ff9f7fSIan Romanick if (ioctl(pci_sys->mtrr_fd, MTRRIOC_ADD_ENTRY, &sentry) < 0) { 57208ff9f7fSIan Romanick /* FIXME: Should we report an error in this case? 5735a04522aSIan Romanick */ 57408ff9f7fSIan Romanick fprintf(stderr, "error setting MTRR " 57508ff9f7fSIan Romanick "(base = 0x%08lx, size = 0x%08x, type = %u) %s (%d)\n", 57608ff9f7fSIan Romanick sentry.base, sentry.size, sentry.type, 57708ff9f7fSIan Romanick strerror(errno), errno); 57808ff9f7fSIan Romanick /* err = errno;*/ 57964af050cSIan Romanick } 580b30d4582SKeith Packard /* KLUDGE ALERT -- rewrite the PTEs to turn off the CD and WT bits */ 581b30d4582SKeith Packard mprotect (map->memory, map->size, PROT_NONE); 582e3adc06bSEric Anholt err = mprotect (map->memory, map->size, PROT_READ|PROT_WRITE); 583e3adc06bSEric Anholt 584e3adc06bSEric Anholt if (err != 0) { 585e3adc06bSEric Anholt fprintf(stderr, "mprotect(PROT_READ | PROT_WRITE) failed: %s\n", 586e3adc06bSEric Anholt strerror(errno)); 587f49f6671SStefan Dirsch fprintf(stderr, "remapping without mprotect performance kludge.\n"); 588e3adc06bSEric Anholt 589e3adc06bSEric Anholt munmap(map->memory, map->size); 590e3adc06bSEric Anholt map->memory = mmap(NULL, map->size, prot, MAP_SHARED, fd, offset); 591e3adc06bSEric Anholt if (map->memory == MAP_FAILED) { 592e3adc06bSEric Anholt map->memory = NULL; 593e3adc06bSEric Anholt close(fd); 594e3adc06bSEric Anholt return errno; 595e3adc06bSEric Anholt } 596e3adc06bSEric Anholt } 59708ff9f7fSIan Romanick } 59808ff9f7fSIan Romanick #endif 59964af050cSIan Romanick 600e3adc06bSEric Anholt close(fd); 601e3adc06bSEric Anholt 602e3adc06bSEric Anholt return 0; 6035a04522aSIan Romanick } 6045cf29b06SKeith Packard 6055cf29b06SKeith Packard /** 6065cf29b06SKeith Packard * Unmap a memory region for a device using the Linux sysfs interface. 6075cf29b06SKeith Packard * 6085cf29b06SKeith Packard * \param dev Device whose memory region is to be unmapped. 6095cf29b06SKeith Packard * \param map Parameters of the mapping that is to be destroyed. 6105cf29b06SKeith Packard * 6115cf29b06SKeith Packard * \return 6125cf29b06SKeith Packard * Zero on success or an \c errno value on failure. 6135cf29b06SKeith Packard * 6145cf29b06SKeith Packard * \sa pci_device_map_rrange, pci_device_linux_sysfs_map_range 6155cf29b06SKeith Packard * 6165cf29b06SKeith Packard * \todo 6175cf29b06SKeith Packard * Some older 2.6.x kernels don't implement the resourceN files. On those 6185cf29b06SKeith Packard * systems /dev/mem must be used. On these systems it is also possible that 6195cf29b06SKeith Packard * \c mmap64 may need to be used. 6205cf29b06SKeith Packard */ 6215cf29b06SKeith Packard static int 6225cf29b06SKeith Packard pci_device_linux_sysfs_unmap_range(struct pci_device *dev, 6235cf29b06SKeith Packard struct pci_device_mapping *map) 6245cf29b06SKeith Packard { 6255cf29b06SKeith Packard int err = 0; 6265cf29b06SKeith Packard #ifdef HAVE_MTRR 6275cf29b06SKeith Packard struct mtrr_sentry sentry = { 6285cf29b06SKeith Packard .base = map->base, 6295cf29b06SKeith Packard .size = map->size, 6305cf29b06SKeith Packard .type = MTRR_TYPE_UNCACHABLE 6315cf29b06SKeith Packard }; 6325cf29b06SKeith Packard #endif 6335cf29b06SKeith Packard 6345cf29b06SKeith Packard err = pci_device_generic_unmap_range (dev, map); 6355cf29b06SKeith Packard if (err) 6365cf29b06SKeith Packard return err; 6375cf29b06SKeith Packard 6385cf29b06SKeith Packard #ifdef HAVE_MTRR 6395cf29b06SKeith Packard if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE) != 0) { 6405cf29b06SKeith Packard sentry.type = MTRR_TYPE_WRBACK; 6415cf29b06SKeith Packard } else if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE) != 0) { 6425cf29b06SKeith Packard sentry.type = MTRR_TYPE_WRCOMB; 6435cf29b06SKeith Packard } 6445cf29b06SKeith Packard 6455cf29b06SKeith Packard if (pci_sys->mtrr_fd != -1 && sentry.type != MTRR_TYPE_UNCACHABLE) { 6465cf29b06SKeith Packard if (ioctl(pci_sys->mtrr_fd, MTRRIOC_DEL_ENTRY, &sentry) < 0) { 6475cf29b06SKeith Packard /* FIXME: Should we report an error in this case? 6485cf29b06SKeith Packard */ 6495cf29b06SKeith Packard fprintf(stderr, "error setting MTRR " 6505cf29b06SKeith Packard "(base = 0x%08lx, size = 0x%08x, type = %u) %s (%d)\n", 6515cf29b06SKeith Packard sentry.base, sentry.size, sentry.type, 6525cf29b06SKeith Packard strerror(errno), errno); 6535cf29b06SKeith Packard /* err = errno;*/ 6545cf29b06SKeith Packard } 6555cf29b06SKeith Packard } 6565cf29b06SKeith Packard #endif 6575cf29b06SKeith Packard 6585cf29b06SKeith Packard return err; 6595cf29b06SKeith Packard } 6604bc9292fSDave Airlie 6614bc9292fSDave Airlie static void pci_device_linux_sysfs_enable(struct pci_device *dev) 6624bc9292fSDave Airlie { 6634bc9292fSDave Airlie char name[256]; 6644bc9292fSDave Airlie int fd; 6654bc9292fSDave Airlie 6664bc9292fSDave Airlie snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/enable", 6674bc9292fSDave Airlie SYS_BUS_PCI, 6684bc9292fSDave Airlie dev->domain, 6694bc9292fSDave Airlie dev->bus, 6704bc9292fSDave Airlie dev->dev, 6714bc9292fSDave Airlie dev->func ); 6724bc9292fSDave Airlie 6734bc9292fSDave Airlie fd = open( name, O_RDWR ); 6744bc9292fSDave Airlie if (fd == -1) 6754bc9292fSDave Airlie return; 6764bc9292fSDave Airlie 6774bc9292fSDave Airlie write( fd, "1", 1 ); 6784bc9292fSDave Airlie close(fd); 6794bc9292fSDave Airlie } 680b2838fb6SDave Airlie 681b2838fb6SDave Airlie static int pci_device_linux_sysfs_boot_vga(struct pci_device *dev) 682b2838fb6SDave Airlie { 683b2838fb6SDave Airlie char name[256]; 684b2838fb6SDave Airlie char reply[3]; 685b2838fb6SDave Airlie int fd, bytes_read; 686b2838fb6SDave Airlie int ret = 0; 687b2838fb6SDave Airlie 688b2838fb6SDave Airlie snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/boot_vga", 689b2838fb6SDave Airlie SYS_BUS_PCI, 690b2838fb6SDave Airlie dev->domain, 691b2838fb6SDave Airlie dev->bus, 692b2838fb6SDave Airlie dev->dev, 693b2838fb6SDave Airlie dev->func ); 694b2838fb6SDave Airlie 6958ba6b02eSAaron Plattner fd = open( name, O_RDONLY ); 696b2838fb6SDave Airlie if (fd == -1) 697b2838fb6SDave Airlie return 0; 698b2838fb6SDave Airlie 699b2838fb6SDave Airlie bytes_read = read(fd, reply, 1); 700b2838fb6SDave Airlie if (bytes_read != 1) 701b2838fb6SDave Airlie goto out; 702b2838fb6SDave Airlie if (reply[0] == '1') 703b2838fb6SDave Airlie ret = 1; 704b2838fb6SDave Airlie out: 705b2838fb6SDave Airlie close(fd); 706b2838fb6SDave Airlie return ret; 707b2838fb6SDave Airlie } 7085d1bdf0cSDave Airlie 7095d1bdf0cSDave Airlie static int pci_device_linux_sysfs_has_kernel_driver(struct pci_device *dev) 7105d1bdf0cSDave Airlie { 7115d1bdf0cSDave Airlie char name[256]; 7125d1bdf0cSDave Airlie struct stat dummy; 7135d1bdf0cSDave Airlie int ret; 7145d1bdf0cSDave Airlie 7155d1bdf0cSDave Airlie snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/driver", 7165d1bdf0cSDave Airlie SYS_BUS_PCI, 7175d1bdf0cSDave Airlie dev->domain, 7185d1bdf0cSDave Airlie dev->bus, 7195d1bdf0cSDave Airlie dev->dev, 7205d1bdf0cSDave Airlie dev->func ); 7215d1bdf0cSDave Airlie 7225d1bdf0cSDave Airlie ret = stat(name, &dummy); 7235d1bdf0cSDave Airlie if (ret < 0) 7245d1bdf0cSDave Airlie return 0; 7255d1bdf0cSDave Airlie return 1; 7265d1bdf0cSDave Airlie } 727*5e8d4c19SAdam Jackson 728*5e8d4c19SAdam Jackson static struct pci_io_handle * 729*5e8d4c19SAdam Jackson pci_device_linux_sysfs_open_device_io(struct pci_io_handle *ret, 730*5e8d4c19SAdam Jackson struct pci_device *dev, int bar, 731*5e8d4c19SAdam Jackson pciaddr_t base, pciaddr_t size) 732*5e8d4c19SAdam Jackson { 733*5e8d4c19SAdam Jackson char name[PATH_MAX]; 734*5e8d4c19SAdam Jackson 735*5e8d4c19SAdam Jackson snprintf(name, PATH_MAX, "%s/%04x:%02x:%02x.%1u/resource%d", 736*5e8d4c19SAdam Jackson SYS_BUS_PCI, dev->domain, dev->bus, dev->dev, dev->func, bar); 737*5e8d4c19SAdam Jackson 738*5e8d4c19SAdam Jackson ret->fd = open(name, O_RDWR); 739*5e8d4c19SAdam Jackson 740*5e8d4c19SAdam Jackson if (ret->fd < 0) 741*5e8d4c19SAdam Jackson return NULL; 742*5e8d4c19SAdam Jackson 743*5e8d4c19SAdam Jackson ret->base = base; 744*5e8d4c19SAdam Jackson ret->size = size; 745*5e8d4c19SAdam Jackson 746*5e8d4c19SAdam Jackson return ret; 747*5e8d4c19SAdam Jackson } 748*5e8d4c19SAdam Jackson 749*5e8d4c19SAdam Jackson static struct pci_io_handle * 750*5e8d4c19SAdam Jackson pci_device_linux_sysfs_open_legacy_io(struct pci_io_handle *ret, 751*5e8d4c19SAdam Jackson struct pci_device *dev, pciaddr_t base, 752*5e8d4c19SAdam Jackson pciaddr_t size) 753*5e8d4c19SAdam Jackson { 754*5e8d4c19SAdam Jackson char name[PATH_MAX]; 755*5e8d4c19SAdam Jackson 756*5e8d4c19SAdam Jackson /* First check if there's a legacy io method for the device */ 757*5e8d4c19SAdam Jackson while (dev) { 758*5e8d4c19SAdam Jackson snprintf(name, PATH_MAX, "/sys/class/pci_bus/%04x:%02x/legacy_io", 759*5e8d4c19SAdam Jackson dev->domain, dev->bus); 760*5e8d4c19SAdam Jackson 761*5e8d4c19SAdam Jackson ret->fd = open(name, O_RDWR); 762*5e8d4c19SAdam Jackson if (ret->fd >= 0) 763*5e8d4c19SAdam Jackson break; 764*5e8d4c19SAdam Jackson 765*5e8d4c19SAdam Jackson dev = pci_device_get_parent_bridge(dev); 766*5e8d4c19SAdam Jackson } 767*5e8d4c19SAdam Jackson 768*5e8d4c19SAdam Jackson /* If not, /dev/port is the best we can do */ 769*5e8d4c19SAdam Jackson if (!dev) 770*5e8d4c19SAdam Jackson ret->fd = open("/dev/port", O_RDWR); 771*5e8d4c19SAdam Jackson 772*5e8d4c19SAdam Jackson if (ret->fd < 0) 773*5e8d4c19SAdam Jackson return NULL; 774*5e8d4c19SAdam Jackson 775*5e8d4c19SAdam Jackson ret->base = base; 776*5e8d4c19SAdam Jackson ret->size = size; 777*5e8d4c19SAdam Jackson 778*5e8d4c19SAdam Jackson return ret; 779*5e8d4c19SAdam Jackson } 780*5e8d4c19SAdam Jackson 781*5e8d4c19SAdam Jackson static void 782*5e8d4c19SAdam Jackson pci_device_linux_sysfs_close_io(struct pci_device *dev, 783*5e8d4c19SAdam Jackson struct pci_io_handle *handle) 784*5e8d4c19SAdam Jackson { 785*5e8d4c19SAdam Jackson close(handle->fd); 786*5e8d4c19SAdam Jackson } 787*5e8d4c19SAdam Jackson 788*5e8d4c19SAdam Jackson static uint32_t 789*5e8d4c19SAdam Jackson pci_device_linux_sysfs_read32(struct pci_io_handle *handle, uint32_t port) 790*5e8d4c19SAdam Jackson { 791*5e8d4c19SAdam Jackson uint32_t ret; 792*5e8d4c19SAdam Jackson 793*5e8d4c19SAdam Jackson pread(handle->fd, &ret, 4, port + handle->base); 794*5e8d4c19SAdam Jackson 795*5e8d4c19SAdam Jackson return ret; 796*5e8d4c19SAdam Jackson } 797*5e8d4c19SAdam Jackson 798*5e8d4c19SAdam Jackson static uint16_t 799*5e8d4c19SAdam Jackson pci_device_linux_sysfs_read16(struct pci_io_handle *handle, uint32_t port) 800*5e8d4c19SAdam Jackson { 801*5e8d4c19SAdam Jackson uint16_t ret; 802*5e8d4c19SAdam Jackson 803*5e8d4c19SAdam Jackson pread(handle->fd, &ret, 2, port + handle->base); 804*5e8d4c19SAdam Jackson 805*5e8d4c19SAdam Jackson return ret; 806*5e8d4c19SAdam Jackson } 807*5e8d4c19SAdam Jackson 808*5e8d4c19SAdam Jackson static uint8_t 809*5e8d4c19SAdam Jackson pci_device_linux_sysfs_read8(struct pci_io_handle *handle, uint32_t port) 810*5e8d4c19SAdam Jackson { 811*5e8d4c19SAdam Jackson uint8_t ret; 812*5e8d4c19SAdam Jackson 813*5e8d4c19SAdam Jackson pread(handle->fd, &ret, 1, port + handle->base); 814*5e8d4c19SAdam Jackson 815*5e8d4c19SAdam Jackson return ret; 816*5e8d4c19SAdam Jackson } 817*5e8d4c19SAdam Jackson 818*5e8d4c19SAdam Jackson static void 819*5e8d4c19SAdam Jackson pci_device_linux_sysfs_write32(struct pci_io_handle *handle, uint32_t port, 820*5e8d4c19SAdam Jackson uint32_t data) 821*5e8d4c19SAdam Jackson { 822*5e8d4c19SAdam Jackson pwrite(handle->fd, &data, 4, port + handle->base); 823*5e8d4c19SAdam Jackson } 824*5e8d4c19SAdam Jackson 825*5e8d4c19SAdam Jackson static void 826*5e8d4c19SAdam Jackson pci_device_linux_sysfs_write16(struct pci_io_handle *handle, uint32_t port, 827*5e8d4c19SAdam Jackson uint16_t data) 828*5e8d4c19SAdam Jackson { 829*5e8d4c19SAdam Jackson pwrite(handle->fd, &data, 2, port + handle->base); 830*5e8d4c19SAdam Jackson } 831*5e8d4c19SAdam Jackson 832*5e8d4c19SAdam Jackson static void 833*5e8d4c19SAdam Jackson pci_device_linux_sysfs_write8(struct pci_io_handle *handle, uint32_t port, 834*5e8d4c19SAdam Jackson uint8_t data) 835*5e8d4c19SAdam Jackson { 836*5e8d4c19SAdam Jackson pwrite(handle->fd, &data, 1, port + handle->base); 837*5e8d4c19SAdam Jackson } 838*5e8d4c19SAdam Jackson 839*5e8d4c19SAdam Jackson static const struct pci_system_methods linux_sysfs_methods = { 840*5e8d4c19SAdam Jackson .destroy = NULL, 841*5e8d4c19SAdam Jackson .destroy_device = NULL, 842*5e8d4c19SAdam Jackson .read_rom = pci_device_linux_sysfs_read_rom, 843*5e8d4c19SAdam Jackson .probe = pci_device_linux_sysfs_probe, 844*5e8d4c19SAdam Jackson .map_range = pci_device_linux_sysfs_map_range, 845*5e8d4c19SAdam Jackson .unmap_range = pci_device_linux_sysfs_unmap_range, 846*5e8d4c19SAdam Jackson 847*5e8d4c19SAdam Jackson .read = pci_device_linux_sysfs_read, 848*5e8d4c19SAdam Jackson .write = pci_device_linux_sysfs_write, 849*5e8d4c19SAdam Jackson 850*5e8d4c19SAdam Jackson .fill_capabilities = pci_fill_capabilities_generic, 851*5e8d4c19SAdam Jackson .enable = pci_device_linux_sysfs_enable, 852*5e8d4c19SAdam Jackson .boot_vga = pci_device_linux_sysfs_boot_vga, 853*5e8d4c19SAdam Jackson .has_kernel_driver = pci_device_linux_sysfs_has_kernel_driver, 854*5e8d4c19SAdam Jackson 855*5e8d4c19SAdam Jackson .open_device_io = pci_device_linux_sysfs_open_device_io, 856*5e8d4c19SAdam Jackson .open_legacy_io = pci_device_linux_sysfs_open_legacy_io, 857*5e8d4c19SAdam Jackson .close_io = pci_device_linux_sysfs_close_io, 858*5e8d4c19SAdam Jackson .read32 = pci_device_linux_sysfs_read32, 859*5e8d4c19SAdam Jackson .read16 = pci_device_linux_sysfs_read16, 860*5e8d4c19SAdam Jackson .read8 = pci_device_linux_sysfs_read8, 861*5e8d4c19SAdam Jackson .write32 = pci_device_linux_sysfs_write32, 862*5e8d4c19SAdam Jackson .write16 = pci_device_linux_sysfs_write16, 863*5e8d4c19SAdam Jackson .write8 = pci_device_linux_sysfs_write8, 864*5e8d4c19SAdam Jackson }; 865