18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * (C) Copyright 2002-2004 Greg Kroah-Hartman <greg@kroah.com> 48c2ecf20Sopenharmony_ci * (C) Copyright 2002-2004 IBM Corp. 58c2ecf20Sopenharmony_ci * (C) Copyright 2003 Matthew Wilcox 68c2ecf20Sopenharmony_ci * (C) Copyright 2003 Hewlett-Packard 78c2ecf20Sopenharmony_ci * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com> 88c2ecf20Sopenharmony_ci * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * File attributes for PCI devices 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * Modeled after usb's driverfs.c 138c2ecf20Sopenharmony_ci */ 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/sched.h> 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <linux/stat.h> 208c2ecf20Sopenharmony_ci#include <linux/export.h> 218c2ecf20Sopenharmony_ci#include <linux/topology.h> 228c2ecf20Sopenharmony_ci#include <linux/mm.h> 238c2ecf20Sopenharmony_ci#include <linux/fs.h> 248c2ecf20Sopenharmony_ci#include <linux/capability.h> 258c2ecf20Sopenharmony_ci#include <linux/security.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci#include <linux/vgaarb.h> 288c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 298c2ecf20Sopenharmony_ci#include <linux/of.h> 308c2ecf20Sopenharmony_ci#include "pci.h" 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int sysfs_initialized; /* = 0 */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* show configuration fields */ 358c2ecf20Sopenharmony_ci#define pci_config_attr(field, format_string) \ 368c2ecf20Sopenharmony_cistatic ssize_t \ 378c2ecf20Sopenharmony_cifield##_show(struct device *dev, struct device_attribute *attr, char *buf) \ 388c2ecf20Sopenharmony_ci{ \ 398c2ecf20Sopenharmony_ci struct pci_dev *pdev; \ 408c2ecf20Sopenharmony_ci \ 418c2ecf20Sopenharmony_ci pdev = to_pci_dev(dev); \ 428c2ecf20Sopenharmony_ci return sprintf(buf, format_string, pdev->field); \ 438c2ecf20Sopenharmony_ci} \ 448c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(field) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cipci_config_attr(vendor, "0x%04x\n"); 478c2ecf20Sopenharmony_cipci_config_attr(device, "0x%04x\n"); 488c2ecf20Sopenharmony_cipci_config_attr(subsystem_vendor, "0x%04x\n"); 498c2ecf20Sopenharmony_cipci_config_attr(subsystem_device, "0x%04x\n"); 508c2ecf20Sopenharmony_cipci_config_attr(revision, "0x%02x\n"); 518c2ecf20Sopenharmony_cipci_config_attr(class, "0x%06x\n"); 528c2ecf20Sopenharmony_cipci_config_attr(irq, "%u\n"); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic ssize_t broken_parity_status_show(struct device *dev, 558c2ecf20Sopenharmony_ci struct device_attribute *attr, 568c2ecf20Sopenharmony_ci char *buf) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 598c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", pdev->broken_parity_status); 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic ssize_t broken_parity_status_store(struct device *dev, 638c2ecf20Sopenharmony_ci struct device_attribute *attr, 648c2ecf20Sopenharmony_ci const char *buf, size_t count) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 678c2ecf20Sopenharmony_ci unsigned long val; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 708c2ecf20Sopenharmony_ci return -EINVAL; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci pdev->broken_parity_status = !!val; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return count; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(broken_parity_status); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic ssize_t pci_dev_show_local_cpu(struct device *dev, bool list, 798c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci const struct cpumask *mask; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 848c2ecf20Sopenharmony_ci mask = (dev_to_node(dev) == -1) ? cpu_online_mask : 858c2ecf20Sopenharmony_ci cpumask_of_node(dev_to_node(dev)); 868c2ecf20Sopenharmony_ci#else 878c2ecf20Sopenharmony_ci mask = cpumask_of_pcibus(to_pci_dev(dev)->bus); 888c2ecf20Sopenharmony_ci#endif 898c2ecf20Sopenharmony_ci return cpumap_print_to_pagebuf(list, buf, mask); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic ssize_t local_cpus_show(struct device *dev, 938c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci return pci_dev_show_local_cpu(dev, false, attr, buf); 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(local_cpus); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic ssize_t local_cpulist_show(struct device *dev, 1008c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci return pci_dev_show_local_cpu(dev, true, attr, buf); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(local_cpulist); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* 1078c2ecf20Sopenharmony_ci * PCI Bus Class Devices 1088c2ecf20Sopenharmony_ci */ 1098c2ecf20Sopenharmony_cistatic ssize_t cpuaffinity_show(struct device *dev, 1108c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci const struct cpumask *cpumask = cpumask_of_pcibus(to_pci_bus(dev)); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return cpumap_print_to_pagebuf(false, buf, cpumask); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(cpuaffinity); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic ssize_t cpulistaffinity_show(struct device *dev, 1198c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci const struct cpumask *cpumask = cpumask_of_pcibus(to_pci_bus(dev)); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, cpumask); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(cpulistaffinity); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci/* show resources */ 1288c2ecf20Sopenharmony_cistatic ssize_t resource_show(struct device *dev, struct device_attribute *attr, 1298c2ecf20Sopenharmony_ci char *buf) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 1328c2ecf20Sopenharmony_ci char *str = buf; 1338c2ecf20Sopenharmony_ci int i; 1348c2ecf20Sopenharmony_ci int max; 1358c2ecf20Sopenharmony_ci resource_size_t start, end; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (pci_dev->subordinate) 1388c2ecf20Sopenharmony_ci max = DEVICE_COUNT_RESOURCE; 1398c2ecf20Sopenharmony_ci else 1408c2ecf20Sopenharmony_ci max = PCI_BRIDGE_RESOURCES; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci for (i = 0; i < max; i++) { 1438c2ecf20Sopenharmony_ci struct resource *res = &pci_dev->resource[i]; 1448c2ecf20Sopenharmony_ci pci_resource_to_user(pci_dev, i, res, &start, &end); 1458c2ecf20Sopenharmony_ci str += sprintf(str, "0x%016llx 0x%016llx 0x%016llx\n", 1468c2ecf20Sopenharmony_ci (unsigned long long)start, 1478c2ecf20Sopenharmony_ci (unsigned long long)end, 1488c2ecf20Sopenharmony_ci (unsigned long long)res->flags); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci return (str - buf); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(resource); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic ssize_t max_link_speed_show(struct device *dev, 1558c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", 1608c2ecf20Sopenharmony_ci pci_speed_string(pcie_get_speed_cap(pdev))); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(max_link_speed); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic ssize_t max_link_width_show(struct device *dev, 1658c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", pcie_get_width_cap(pdev)); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(max_link_width); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_cistatic ssize_t current_link_speed_show(struct device *dev, 1748c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1758c2ecf20Sopenharmony_ci{ 1768c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 1778c2ecf20Sopenharmony_ci u16 linkstat; 1788c2ecf20Sopenharmony_ci int err; 1798c2ecf20Sopenharmony_ci enum pci_bus_speed speed; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat); 1828c2ecf20Sopenharmony_ci if (err) 1838c2ecf20Sopenharmony_ci return -EINVAL; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci speed = pcie_link_speed[linkstat & PCI_EXP_LNKSTA_CLS]; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", pci_speed_string(speed)); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(current_link_speed); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic ssize_t current_link_width_show(struct device *dev, 1928c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 1958c2ecf20Sopenharmony_ci u16 linkstat; 1968c2ecf20Sopenharmony_ci int err; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat); 1998c2ecf20Sopenharmony_ci if (err) 2008c2ecf20Sopenharmony_ci return -EINVAL; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", 2038c2ecf20Sopenharmony_ci (linkstat & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(current_link_width); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic ssize_t secondary_bus_number_show(struct device *dev, 2088c2ecf20Sopenharmony_ci struct device_attribute *attr, 2098c2ecf20Sopenharmony_ci char *buf) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 2128c2ecf20Sopenharmony_ci u8 sec_bus; 2138c2ecf20Sopenharmony_ci int err; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci err = pci_read_config_byte(pci_dev, PCI_SECONDARY_BUS, &sec_bus); 2168c2ecf20Sopenharmony_ci if (err) 2178c2ecf20Sopenharmony_ci return -EINVAL; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", sec_bus); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(secondary_bus_number); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic ssize_t subordinate_bus_number_show(struct device *dev, 2248c2ecf20Sopenharmony_ci struct device_attribute *attr, 2258c2ecf20Sopenharmony_ci char *buf) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 2288c2ecf20Sopenharmony_ci u8 sub_bus; 2298c2ecf20Sopenharmony_ci int err; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci err = pci_read_config_byte(pci_dev, PCI_SUBORDINATE_BUS, &sub_bus); 2328c2ecf20Sopenharmony_ci if (err) 2338c2ecf20Sopenharmony_ci return -EINVAL; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", sub_bus); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(subordinate_bus_number); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic ssize_t ari_enabled_show(struct device *dev, 2408c2ecf20Sopenharmony_ci struct device_attribute *attr, 2418c2ecf20Sopenharmony_ci char *buf) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", pci_ari_enabled(pci_dev->bus)); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(ari_enabled); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 2508c2ecf20Sopenharmony_ci char *buf) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci return sprintf(buf, "pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02X\n", 2558c2ecf20Sopenharmony_ci pci_dev->vendor, pci_dev->device, 2568c2ecf20Sopenharmony_ci pci_dev->subsystem_vendor, pci_dev->subsystem_device, 2578c2ecf20Sopenharmony_ci (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8), 2588c2ecf20Sopenharmony_ci (u8)(pci_dev->class)); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(modalias); 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic ssize_t enable_store(struct device *dev, struct device_attribute *attr, 2638c2ecf20Sopenharmony_ci const char *buf, size_t count) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 2668c2ecf20Sopenharmony_ci unsigned long val; 2678c2ecf20Sopenharmony_ci ssize_t result = kstrtoul(buf, 0, &val); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (result < 0) 2708c2ecf20Sopenharmony_ci return result; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* this can crash the machine when done on the "wrong" device */ 2738c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 2748c2ecf20Sopenharmony_ci return -EPERM; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci device_lock(dev); 2778c2ecf20Sopenharmony_ci if (dev->driver) 2788c2ecf20Sopenharmony_ci result = -EBUSY; 2798c2ecf20Sopenharmony_ci else if (val) 2808c2ecf20Sopenharmony_ci result = pci_enable_device(pdev); 2818c2ecf20Sopenharmony_ci else if (pci_is_enabled(pdev)) 2828c2ecf20Sopenharmony_ci pci_disable_device(pdev); 2838c2ecf20Sopenharmony_ci else 2848c2ecf20Sopenharmony_ci result = -EIO; 2858c2ecf20Sopenharmony_ci device_unlock(dev); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return result < 0 ? result : count; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_cistatic ssize_t enable_show(struct device *dev, struct device_attribute *attr, 2918c2ecf20Sopenharmony_ci char *buf) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci struct pci_dev *pdev; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci pdev = to_pci_dev(dev); 2968c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", atomic_read(&pdev->enable_cnt)); 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(enable); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 3018c2ecf20Sopenharmony_cistatic ssize_t numa_node_store(struct device *dev, 3028c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 3038c2ecf20Sopenharmony_ci size_t count) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 3068c2ecf20Sopenharmony_ci int node, ret; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 3098c2ecf20Sopenharmony_ci return -EPERM; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci ret = kstrtoint(buf, 0, &node); 3128c2ecf20Sopenharmony_ci if (ret) 3138c2ecf20Sopenharmony_ci return ret; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if ((node < 0 && node != NUMA_NO_NODE) || node >= MAX_NUMNODES) 3168c2ecf20Sopenharmony_ci return -EINVAL; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci if (node != NUMA_NO_NODE && !node_online(node)) 3198c2ecf20Sopenharmony_ci return -EINVAL; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK); 3228c2ecf20Sopenharmony_ci pci_alert(pdev, FW_BUG "Overriding NUMA node to %d. Contact your vendor for updates.", 3238c2ecf20Sopenharmony_ci node); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci dev->numa_node = node; 3268c2ecf20Sopenharmony_ci return count; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic ssize_t numa_node_show(struct device *dev, struct device_attribute *attr, 3308c2ecf20Sopenharmony_ci char *buf) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", dev->numa_node); 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(numa_node); 3358c2ecf20Sopenharmony_ci#endif 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic ssize_t dma_mask_bits_show(struct device *dev, 3388c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", fls64(pdev->dma_mask)); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(dma_mask_bits); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic ssize_t consistent_dma_mask_bits_show(struct device *dev, 3478c2ecf20Sopenharmony_ci struct device_attribute *attr, 3488c2ecf20Sopenharmony_ci char *buf) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", fls64(dev->coherent_dma_mask)); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(consistent_dma_mask_bits); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_cistatic ssize_t msi_bus_show(struct device *dev, struct device_attribute *attr, 3558c2ecf20Sopenharmony_ci char *buf) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 3588c2ecf20Sopenharmony_ci struct pci_bus *subordinate = pdev->subordinate; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", subordinate ? 3618c2ecf20Sopenharmony_ci !(subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI) 3628c2ecf20Sopenharmony_ci : !pdev->no_msi); 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic ssize_t msi_bus_store(struct device *dev, struct device_attribute *attr, 3668c2ecf20Sopenharmony_ci const char *buf, size_t count) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 3698c2ecf20Sopenharmony_ci struct pci_bus *subordinate = pdev->subordinate; 3708c2ecf20Sopenharmony_ci unsigned long val; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 3738c2ecf20Sopenharmony_ci return -EINVAL; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 3768c2ecf20Sopenharmony_ci return -EPERM; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* 3798c2ecf20Sopenharmony_ci * "no_msi" and "bus_flags" only affect what happens when a driver 3808c2ecf20Sopenharmony_ci * requests MSI or MSI-X. They don't affect any drivers that have 3818c2ecf20Sopenharmony_ci * already requested MSI or MSI-X. 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci if (!subordinate) { 3848c2ecf20Sopenharmony_ci pdev->no_msi = !val; 3858c2ecf20Sopenharmony_ci pci_info(pdev, "MSI/MSI-X %s for future drivers\n", 3868c2ecf20Sopenharmony_ci val ? "allowed" : "disallowed"); 3878c2ecf20Sopenharmony_ci return count; 3888c2ecf20Sopenharmony_ci } 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (val) 3918c2ecf20Sopenharmony_ci subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI; 3928c2ecf20Sopenharmony_ci else 3938c2ecf20Sopenharmony_ci subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci dev_info(&subordinate->dev, "MSI/MSI-X %s for future drivers of devices on this bus\n", 3968c2ecf20Sopenharmony_ci val ? "allowed" : "disallowed"); 3978c2ecf20Sopenharmony_ci return count; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(msi_bus); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_cistatic ssize_t rescan_store(struct bus_type *bus, const char *buf, size_t count) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci unsigned long val; 4048c2ecf20Sopenharmony_ci struct pci_bus *b = NULL; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 4078c2ecf20Sopenharmony_ci return -EINVAL; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci if (val) { 4108c2ecf20Sopenharmony_ci pci_lock_rescan_remove(); 4118c2ecf20Sopenharmony_ci while ((b = pci_find_next_bus(b)) != NULL) 4128c2ecf20Sopenharmony_ci pci_rescan_bus(b); 4138c2ecf20Sopenharmony_ci pci_unlock_rescan_remove(); 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci return count; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_cistatic BUS_ATTR_WO(rescan); 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_cistatic struct attribute *pci_bus_attrs[] = { 4208c2ecf20Sopenharmony_ci &bus_attr_rescan.attr, 4218c2ecf20Sopenharmony_ci NULL, 4228c2ecf20Sopenharmony_ci}; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic const struct attribute_group pci_bus_group = { 4258c2ecf20Sopenharmony_ci .attrs = pci_bus_attrs, 4268c2ecf20Sopenharmony_ci}; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ciconst struct attribute_group *pci_bus_groups[] = { 4298c2ecf20Sopenharmony_ci &pci_bus_group, 4308c2ecf20Sopenharmony_ci NULL, 4318c2ecf20Sopenharmony_ci}; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_cistatic ssize_t dev_rescan_store(struct device *dev, 4348c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, 4358c2ecf20Sopenharmony_ci size_t count) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci unsigned long val; 4388c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 4418c2ecf20Sopenharmony_ci return -EINVAL; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (val) { 4448c2ecf20Sopenharmony_ci pci_lock_rescan_remove(); 4458c2ecf20Sopenharmony_ci pci_rescan_bus(pdev->bus); 4468c2ecf20Sopenharmony_ci pci_unlock_rescan_remove(); 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci return count; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_cistatic struct device_attribute dev_attr_dev_rescan = __ATTR(rescan, 0200, NULL, 4518c2ecf20Sopenharmony_ci dev_rescan_store); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_cistatic ssize_t remove_store(struct device *dev, struct device_attribute *attr, 4548c2ecf20Sopenharmony_ci const char *buf, size_t count) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci unsigned long val; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 4598c2ecf20Sopenharmony_ci return -EINVAL; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (val && device_remove_file_self(dev, attr)) 4628c2ecf20Sopenharmony_ci pci_stop_and_remove_bus_device_locked(to_pci_dev(dev)); 4638c2ecf20Sopenharmony_ci return count; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_cistatic DEVICE_ATTR_IGNORE_LOCKDEP(remove, 0220, NULL, 4668c2ecf20Sopenharmony_ci remove_store); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic ssize_t bus_rescan_store(struct device *dev, 4698c2ecf20Sopenharmony_ci struct device_attribute *attr, 4708c2ecf20Sopenharmony_ci const char *buf, size_t count) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci unsigned long val; 4738c2ecf20Sopenharmony_ci struct pci_bus *bus = to_pci_bus(dev); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 4768c2ecf20Sopenharmony_ci return -EINVAL; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (val) { 4798c2ecf20Sopenharmony_ci pci_lock_rescan_remove(); 4808c2ecf20Sopenharmony_ci if (!pci_is_root_bus(bus) && list_empty(&bus->devices)) 4818c2ecf20Sopenharmony_ci pci_rescan_bus_bridge_resize(bus->self); 4828c2ecf20Sopenharmony_ci else 4838c2ecf20Sopenharmony_ci pci_rescan_bus(bus); 4848c2ecf20Sopenharmony_ci pci_unlock_rescan_remove(); 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci return count; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_cistatic struct device_attribute dev_attr_bus_rescan = __ATTR(rescan, 0200, NULL, 4898c2ecf20Sopenharmony_ci bus_rescan_store); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci#if defined(CONFIG_PM) && defined(CONFIG_ACPI) 4928c2ecf20Sopenharmony_cistatic ssize_t d3cold_allowed_store(struct device *dev, 4938c2ecf20Sopenharmony_ci struct device_attribute *attr, 4948c2ecf20Sopenharmony_ci const char *buf, size_t count) 4958c2ecf20Sopenharmony_ci{ 4968c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 4978c2ecf20Sopenharmony_ci unsigned long val; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 5008c2ecf20Sopenharmony_ci return -EINVAL; 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci pdev->d3cold_allowed = !!val; 5038c2ecf20Sopenharmony_ci pci_bridge_d3_update(pdev); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci pm_runtime_resume(dev); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci return count; 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic ssize_t d3cold_allowed_show(struct device *dev, 5118c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 5148c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", pdev->d3cold_allowed); 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(d3cold_allowed); 5178c2ecf20Sopenharmony_ci#endif 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 5208c2ecf20Sopenharmony_cistatic ssize_t devspec_show(struct device *dev, 5218c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 5228c2ecf20Sopenharmony_ci{ 5238c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 5248c2ecf20Sopenharmony_ci struct device_node *np = pci_device_to_OF_node(pdev); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (np == NULL) 5278c2ecf20Sopenharmony_ci return 0; 5288c2ecf20Sopenharmony_ci return sprintf(buf, "%pOF", np); 5298c2ecf20Sopenharmony_ci} 5308c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(devspec); 5318c2ecf20Sopenharmony_ci#endif 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_cistatic ssize_t driver_override_store(struct device *dev, 5348c2ecf20Sopenharmony_ci struct device_attribute *attr, 5358c2ecf20Sopenharmony_ci const char *buf, size_t count) 5368c2ecf20Sopenharmony_ci{ 5378c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 5388c2ecf20Sopenharmony_ci char *driver_override, *old, *cp; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* We need to keep extra room for a newline */ 5418c2ecf20Sopenharmony_ci if (count >= (PAGE_SIZE - 1)) 5428c2ecf20Sopenharmony_ci return -EINVAL; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci driver_override = kstrndup(buf, count, GFP_KERNEL); 5458c2ecf20Sopenharmony_ci if (!driver_override) 5468c2ecf20Sopenharmony_ci return -ENOMEM; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci cp = strchr(driver_override, '\n'); 5498c2ecf20Sopenharmony_ci if (cp) 5508c2ecf20Sopenharmony_ci *cp = '\0'; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci device_lock(dev); 5538c2ecf20Sopenharmony_ci old = pdev->driver_override; 5548c2ecf20Sopenharmony_ci if (strlen(driver_override)) { 5558c2ecf20Sopenharmony_ci pdev->driver_override = driver_override; 5568c2ecf20Sopenharmony_ci } else { 5578c2ecf20Sopenharmony_ci kfree(driver_override); 5588c2ecf20Sopenharmony_ci pdev->driver_override = NULL; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci device_unlock(dev); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci kfree(old); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci return count; 5658c2ecf20Sopenharmony_ci} 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_cistatic ssize_t driver_override_show(struct device *dev, 5688c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 5718c2ecf20Sopenharmony_ci ssize_t len; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci device_lock(dev); 5748c2ecf20Sopenharmony_ci len = scnprintf(buf, PAGE_SIZE, "%s\n", pdev->driver_override); 5758c2ecf20Sopenharmony_ci device_unlock(dev); 5768c2ecf20Sopenharmony_ci return len; 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(driver_override); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic struct attribute *pci_dev_attrs[] = { 5818c2ecf20Sopenharmony_ci &dev_attr_resource.attr, 5828c2ecf20Sopenharmony_ci &dev_attr_vendor.attr, 5838c2ecf20Sopenharmony_ci &dev_attr_device.attr, 5848c2ecf20Sopenharmony_ci &dev_attr_subsystem_vendor.attr, 5858c2ecf20Sopenharmony_ci &dev_attr_subsystem_device.attr, 5868c2ecf20Sopenharmony_ci &dev_attr_revision.attr, 5878c2ecf20Sopenharmony_ci &dev_attr_class.attr, 5888c2ecf20Sopenharmony_ci &dev_attr_irq.attr, 5898c2ecf20Sopenharmony_ci &dev_attr_local_cpus.attr, 5908c2ecf20Sopenharmony_ci &dev_attr_local_cpulist.attr, 5918c2ecf20Sopenharmony_ci &dev_attr_modalias.attr, 5928c2ecf20Sopenharmony_ci#ifdef CONFIG_NUMA 5938c2ecf20Sopenharmony_ci &dev_attr_numa_node.attr, 5948c2ecf20Sopenharmony_ci#endif 5958c2ecf20Sopenharmony_ci &dev_attr_dma_mask_bits.attr, 5968c2ecf20Sopenharmony_ci &dev_attr_consistent_dma_mask_bits.attr, 5978c2ecf20Sopenharmony_ci &dev_attr_enable.attr, 5988c2ecf20Sopenharmony_ci &dev_attr_broken_parity_status.attr, 5998c2ecf20Sopenharmony_ci &dev_attr_msi_bus.attr, 6008c2ecf20Sopenharmony_ci#if defined(CONFIG_PM) && defined(CONFIG_ACPI) 6018c2ecf20Sopenharmony_ci &dev_attr_d3cold_allowed.attr, 6028c2ecf20Sopenharmony_ci#endif 6038c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 6048c2ecf20Sopenharmony_ci &dev_attr_devspec.attr, 6058c2ecf20Sopenharmony_ci#endif 6068c2ecf20Sopenharmony_ci &dev_attr_driver_override.attr, 6078c2ecf20Sopenharmony_ci &dev_attr_ari_enabled.attr, 6088c2ecf20Sopenharmony_ci NULL, 6098c2ecf20Sopenharmony_ci}; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic struct attribute *pci_bridge_attrs[] = { 6128c2ecf20Sopenharmony_ci &dev_attr_subordinate_bus_number.attr, 6138c2ecf20Sopenharmony_ci &dev_attr_secondary_bus_number.attr, 6148c2ecf20Sopenharmony_ci NULL, 6158c2ecf20Sopenharmony_ci}; 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic struct attribute *pcie_dev_attrs[] = { 6188c2ecf20Sopenharmony_ci &dev_attr_current_link_speed.attr, 6198c2ecf20Sopenharmony_ci &dev_attr_current_link_width.attr, 6208c2ecf20Sopenharmony_ci &dev_attr_max_link_width.attr, 6218c2ecf20Sopenharmony_ci &dev_attr_max_link_speed.attr, 6228c2ecf20Sopenharmony_ci NULL, 6238c2ecf20Sopenharmony_ci}; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic struct attribute *pcibus_attrs[] = { 6268c2ecf20Sopenharmony_ci &dev_attr_bus_rescan.attr, 6278c2ecf20Sopenharmony_ci &dev_attr_cpuaffinity.attr, 6288c2ecf20Sopenharmony_ci &dev_attr_cpulistaffinity.attr, 6298c2ecf20Sopenharmony_ci NULL, 6308c2ecf20Sopenharmony_ci}; 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_cistatic const struct attribute_group pcibus_group = { 6338c2ecf20Sopenharmony_ci .attrs = pcibus_attrs, 6348c2ecf20Sopenharmony_ci}; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ciconst struct attribute_group *pcibus_groups[] = { 6378c2ecf20Sopenharmony_ci &pcibus_group, 6388c2ecf20Sopenharmony_ci NULL, 6398c2ecf20Sopenharmony_ci}; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_cistatic ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr, 6428c2ecf20Sopenharmony_ci char *buf) 6438c2ecf20Sopenharmony_ci{ 6448c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 6458c2ecf20Sopenharmony_ci struct pci_dev *vga_dev = vga_default_device(); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci if (vga_dev) 6488c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", (pdev == vga_dev)); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", 6518c2ecf20Sopenharmony_ci !!(pdev->resource[PCI_ROM_RESOURCE].flags & 6528c2ecf20Sopenharmony_ci IORESOURCE_ROM_SHADOW)); 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(boot_vga); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic ssize_t pci_read_config(struct file *filp, struct kobject *kobj, 6578c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 6588c2ecf20Sopenharmony_ci loff_t off, size_t count) 6598c2ecf20Sopenharmony_ci{ 6608c2ecf20Sopenharmony_ci struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); 6618c2ecf20Sopenharmony_ci unsigned int size = 64; 6628c2ecf20Sopenharmony_ci loff_t init_off = off; 6638c2ecf20Sopenharmony_ci u8 *data = (u8 *) buf; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci /* Several chips lock up trying to read undefined config space */ 6668c2ecf20Sopenharmony_ci if (file_ns_capable(filp, &init_user_ns, CAP_SYS_ADMIN)) 6678c2ecf20Sopenharmony_ci size = dev->cfg_size; 6688c2ecf20Sopenharmony_ci else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) 6698c2ecf20Sopenharmony_ci size = 128; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci if (off > size) 6728c2ecf20Sopenharmony_ci return 0; 6738c2ecf20Sopenharmony_ci if (off + count > size) { 6748c2ecf20Sopenharmony_ci size -= off; 6758c2ecf20Sopenharmony_ci count = size; 6768c2ecf20Sopenharmony_ci } else { 6778c2ecf20Sopenharmony_ci size = count; 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci pci_config_pm_runtime_get(dev); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci if ((off & 1) && size) { 6838c2ecf20Sopenharmony_ci u8 val; 6848c2ecf20Sopenharmony_ci pci_user_read_config_byte(dev, off, &val); 6858c2ecf20Sopenharmony_ci data[off - init_off] = val; 6868c2ecf20Sopenharmony_ci off++; 6878c2ecf20Sopenharmony_ci size--; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if ((off & 3) && size > 2) { 6918c2ecf20Sopenharmony_ci u16 val; 6928c2ecf20Sopenharmony_ci pci_user_read_config_word(dev, off, &val); 6938c2ecf20Sopenharmony_ci data[off - init_off] = val & 0xff; 6948c2ecf20Sopenharmony_ci data[off - init_off + 1] = (val >> 8) & 0xff; 6958c2ecf20Sopenharmony_ci off += 2; 6968c2ecf20Sopenharmony_ci size -= 2; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci while (size > 3) { 7008c2ecf20Sopenharmony_ci u32 val; 7018c2ecf20Sopenharmony_ci pci_user_read_config_dword(dev, off, &val); 7028c2ecf20Sopenharmony_ci data[off - init_off] = val & 0xff; 7038c2ecf20Sopenharmony_ci data[off - init_off + 1] = (val >> 8) & 0xff; 7048c2ecf20Sopenharmony_ci data[off - init_off + 2] = (val >> 16) & 0xff; 7058c2ecf20Sopenharmony_ci data[off - init_off + 3] = (val >> 24) & 0xff; 7068c2ecf20Sopenharmony_ci off += 4; 7078c2ecf20Sopenharmony_ci size -= 4; 7088c2ecf20Sopenharmony_ci cond_resched(); 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci if (size >= 2) { 7128c2ecf20Sopenharmony_ci u16 val; 7138c2ecf20Sopenharmony_ci pci_user_read_config_word(dev, off, &val); 7148c2ecf20Sopenharmony_ci data[off - init_off] = val & 0xff; 7158c2ecf20Sopenharmony_ci data[off - init_off + 1] = (val >> 8) & 0xff; 7168c2ecf20Sopenharmony_ci off += 2; 7178c2ecf20Sopenharmony_ci size -= 2; 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (size > 0) { 7218c2ecf20Sopenharmony_ci u8 val; 7228c2ecf20Sopenharmony_ci pci_user_read_config_byte(dev, off, &val); 7238c2ecf20Sopenharmony_ci data[off - init_off] = val; 7248c2ecf20Sopenharmony_ci off++; 7258c2ecf20Sopenharmony_ci --size; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci pci_config_pm_runtime_put(dev); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci return count; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic ssize_t pci_write_config(struct file *filp, struct kobject *kobj, 7348c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 7358c2ecf20Sopenharmony_ci loff_t off, size_t count) 7368c2ecf20Sopenharmony_ci{ 7378c2ecf20Sopenharmony_ci struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); 7388c2ecf20Sopenharmony_ci unsigned int size = count; 7398c2ecf20Sopenharmony_ci loff_t init_off = off; 7408c2ecf20Sopenharmony_ci u8 *data = (u8 *) buf; 7418c2ecf20Sopenharmony_ci int ret; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci ret = security_locked_down(LOCKDOWN_PCI_ACCESS); 7448c2ecf20Sopenharmony_ci if (ret) 7458c2ecf20Sopenharmony_ci return ret; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (off > dev->cfg_size) 7488c2ecf20Sopenharmony_ci return 0; 7498c2ecf20Sopenharmony_ci if (off + count > dev->cfg_size) { 7508c2ecf20Sopenharmony_ci size = dev->cfg_size - off; 7518c2ecf20Sopenharmony_ci count = size; 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci pci_config_pm_runtime_get(dev); 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci if ((off & 1) && size) { 7578c2ecf20Sopenharmony_ci pci_user_write_config_byte(dev, off, data[off - init_off]); 7588c2ecf20Sopenharmony_ci off++; 7598c2ecf20Sopenharmony_ci size--; 7608c2ecf20Sopenharmony_ci } 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci if ((off & 3) && size > 2) { 7638c2ecf20Sopenharmony_ci u16 val = data[off - init_off]; 7648c2ecf20Sopenharmony_ci val |= (u16) data[off - init_off + 1] << 8; 7658c2ecf20Sopenharmony_ci pci_user_write_config_word(dev, off, val); 7668c2ecf20Sopenharmony_ci off += 2; 7678c2ecf20Sopenharmony_ci size -= 2; 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci while (size > 3) { 7718c2ecf20Sopenharmony_ci u32 val = data[off - init_off]; 7728c2ecf20Sopenharmony_ci val |= (u32) data[off - init_off + 1] << 8; 7738c2ecf20Sopenharmony_ci val |= (u32) data[off - init_off + 2] << 16; 7748c2ecf20Sopenharmony_ci val |= (u32) data[off - init_off + 3] << 24; 7758c2ecf20Sopenharmony_ci pci_user_write_config_dword(dev, off, val); 7768c2ecf20Sopenharmony_ci off += 4; 7778c2ecf20Sopenharmony_ci size -= 4; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci if (size >= 2) { 7818c2ecf20Sopenharmony_ci u16 val = data[off - init_off]; 7828c2ecf20Sopenharmony_ci val |= (u16) data[off - init_off + 1] << 8; 7838c2ecf20Sopenharmony_ci pci_user_write_config_word(dev, off, val); 7848c2ecf20Sopenharmony_ci off += 2; 7858c2ecf20Sopenharmony_ci size -= 2; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (size) { 7898c2ecf20Sopenharmony_ci pci_user_write_config_byte(dev, off, data[off - init_off]); 7908c2ecf20Sopenharmony_ci off++; 7918c2ecf20Sopenharmony_ci --size; 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci pci_config_pm_runtime_put(dev); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci return count; 7978c2ecf20Sopenharmony_ci} 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci#ifdef HAVE_PCI_LEGACY 8008c2ecf20Sopenharmony_ci/** 8018c2ecf20Sopenharmony_ci * pci_read_legacy_io - read byte(s) from legacy I/O port space 8028c2ecf20Sopenharmony_ci * @filp: open sysfs file 8038c2ecf20Sopenharmony_ci * @kobj: kobject corresponding to file to read from 8048c2ecf20Sopenharmony_ci * @bin_attr: struct bin_attribute for this file 8058c2ecf20Sopenharmony_ci * @buf: buffer to store results 8068c2ecf20Sopenharmony_ci * @off: offset into legacy I/O port space 8078c2ecf20Sopenharmony_ci * @count: number of bytes to read 8088c2ecf20Sopenharmony_ci * 8098c2ecf20Sopenharmony_ci * Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific 8108c2ecf20Sopenharmony_ci * callback routine (pci_legacy_read). 8118c2ecf20Sopenharmony_ci */ 8128c2ecf20Sopenharmony_cistatic ssize_t pci_read_legacy_io(struct file *filp, struct kobject *kobj, 8138c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 8148c2ecf20Sopenharmony_ci loff_t off, size_t count) 8158c2ecf20Sopenharmony_ci{ 8168c2ecf20Sopenharmony_ci struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* Only support 1, 2 or 4 byte accesses */ 8198c2ecf20Sopenharmony_ci if (count != 1 && count != 2 && count != 4) 8208c2ecf20Sopenharmony_ci return -EINVAL; 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci return pci_legacy_read(bus, off, (u32 *)buf, count); 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci/** 8268c2ecf20Sopenharmony_ci * pci_write_legacy_io - write byte(s) to legacy I/O port space 8278c2ecf20Sopenharmony_ci * @filp: open sysfs file 8288c2ecf20Sopenharmony_ci * @kobj: kobject corresponding to file to read from 8298c2ecf20Sopenharmony_ci * @bin_attr: struct bin_attribute for this file 8308c2ecf20Sopenharmony_ci * @buf: buffer containing value to be written 8318c2ecf20Sopenharmony_ci * @off: offset into legacy I/O port space 8328c2ecf20Sopenharmony_ci * @count: number of bytes to write 8338c2ecf20Sopenharmony_ci * 8348c2ecf20Sopenharmony_ci * Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific 8358c2ecf20Sopenharmony_ci * callback routine (pci_legacy_write). 8368c2ecf20Sopenharmony_ci */ 8378c2ecf20Sopenharmony_cistatic ssize_t pci_write_legacy_io(struct file *filp, struct kobject *kobj, 8388c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 8398c2ecf20Sopenharmony_ci loff_t off, size_t count) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci /* Only support 1, 2 or 4 byte accesses */ 8448c2ecf20Sopenharmony_ci if (count != 1 && count != 2 && count != 4) 8458c2ecf20Sopenharmony_ci return -EINVAL; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci return pci_legacy_write(bus, off, *(u32 *)buf, count); 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci/** 8518c2ecf20Sopenharmony_ci * pci_mmap_legacy_mem - map legacy PCI memory into user memory space 8528c2ecf20Sopenharmony_ci * @filp: open sysfs file 8538c2ecf20Sopenharmony_ci * @kobj: kobject corresponding to device to be mapped 8548c2ecf20Sopenharmony_ci * @attr: struct bin_attribute for this file 8558c2ecf20Sopenharmony_ci * @vma: struct vm_area_struct passed to mmap 8568c2ecf20Sopenharmony_ci * 8578c2ecf20Sopenharmony_ci * Uses an arch specific callback, pci_mmap_legacy_mem_page_range, to mmap 8588c2ecf20Sopenharmony_ci * legacy memory space (first meg of bus space) into application virtual 8598c2ecf20Sopenharmony_ci * memory space. 8608c2ecf20Sopenharmony_ci */ 8618c2ecf20Sopenharmony_cistatic int pci_mmap_legacy_mem(struct file *filp, struct kobject *kobj, 8628c2ecf20Sopenharmony_ci struct bin_attribute *attr, 8638c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 8648c2ecf20Sopenharmony_ci{ 8658c2ecf20Sopenharmony_ci struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci return pci_mmap_legacy_page_range(bus, vma, pci_mmap_mem); 8688c2ecf20Sopenharmony_ci} 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci/** 8718c2ecf20Sopenharmony_ci * pci_mmap_legacy_io - map legacy PCI IO into user memory space 8728c2ecf20Sopenharmony_ci * @filp: open sysfs file 8738c2ecf20Sopenharmony_ci * @kobj: kobject corresponding to device to be mapped 8748c2ecf20Sopenharmony_ci * @attr: struct bin_attribute for this file 8758c2ecf20Sopenharmony_ci * @vma: struct vm_area_struct passed to mmap 8768c2ecf20Sopenharmony_ci * 8778c2ecf20Sopenharmony_ci * Uses an arch specific callback, pci_mmap_legacy_io_page_range, to mmap 8788c2ecf20Sopenharmony_ci * legacy IO space (first meg of bus space) into application virtual 8798c2ecf20Sopenharmony_ci * memory space. Returns -ENOSYS if the operation isn't supported 8808c2ecf20Sopenharmony_ci */ 8818c2ecf20Sopenharmony_cistatic int pci_mmap_legacy_io(struct file *filp, struct kobject *kobj, 8828c2ecf20Sopenharmony_ci struct bin_attribute *attr, 8838c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 8848c2ecf20Sopenharmony_ci{ 8858c2ecf20Sopenharmony_ci struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io); 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci/** 8918c2ecf20Sopenharmony_ci * pci_adjust_legacy_attr - adjustment of legacy file attributes 8928c2ecf20Sopenharmony_ci * @b: bus to create files under 8938c2ecf20Sopenharmony_ci * @mmap_type: I/O port or memory 8948c2ecf20Sopenharmony_ci * 8958c2ecf20Sopenharmony_ci * Stub implementation. Can be overridden by arch if necessary. 8968c2ecf20Sopenharmony_ci */ 8978c2ecf20Sopenharmony_civoid __weak pci_adjust_legacy_attr(struct pci_bus *b, 8988c2ecf20Sopenharmony_ci enum pci_mmap_state mmap_type) 8998c2ecf20Sopenharmony_ci{ 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci/** 9038c2ecf20Sopenharmony_ci * pci_create_legacy_files - create legacy I/O port and memory files 9048c2ecf20Sopenharmony_ci * @b: bus to create files under 9058c2ecf20Sopenharmony_ci * 9068c2ecf20Sopenharmony_ci * Some platforms allow access to legacy I/O port and ISA memory space on 9078c2ecf20Sopenharmony_ci * a per-bus basis. This routine creates the files and ties them into 9088c2ecf20Sopenharmony_ci * their associated read, write and mmap files from pci-sysfs.c 9098c2ecf20Sopenharmony_ci * 9108c2ecf20Sopenharmony_ci * On error unwind, but don't propagate the error to the caller 9118c2ecf20Sopenharmony_ci * as it is ok to set up the PCI bus without these files. 9128c2ecf20Sopenharmony_ci */ 9138c2ecf20Sopenharmony_civoid pci_create_legacy_files(struct pci_bus *b) 9148c2ecf20Sopenharmony_ci{ 9158c2ecf20Sopenharmony_ci int error; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci b->legacy_io = kcalloc(2, sizeof(struct bin_attribute), 9188c2ecf20Sopenharmony_ci GFP_ATOMIC); 9198c2ecf20Sopenharmony_ci if (!b->legacy_io) 9208c2ecf20Sopenharmony_ci goto kzalloc_err; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci sysfs_bin_attr_init(b->legacy_io); 9238c2ecf20Sopenharmony_ci b->legacy_io->attr.name = "legacy_io"; 9248c2ecf20Sopenharmony_ci b->legacy_io->size = 0xffff; 9258c2ecf20Sopenharmony_ci b->legacy_io->attr.mode = 0600; 9268c2ecf20Sopenharmony_ci b->legacy_io->read = pci_read_legacy_io; 9278c2ecf20Sopenharmony_ci b->legacy_io->write = pci_write_legacy_io; 9288c2ecf20Sopenharmony_ci b->legacy_io->mmap = pci_mmap_legacy_io; 9298c2ecf20Sopenharmony_ci pci_adjust_legacy_attr(b, pci_mmap_io); 9308c2ecf20Sopenharmony_ci error = device_create_bin_file(&b->dev, b->legacy_io); 9318c2ecf20Sopenharmony_ci if (error) 9328c2ecf20Sopenharmony_ci goto legacy_io_err; 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci /* Allocated above after the legacy_io struct */ 9358c2ecf20Sopenharmony_ci b->legacy_mem = b->legacy_io + 1; 9368c2ecf20Sopenharmony_ci sysfs_bin_attr_init(b->legacy_mem); 9378c2ecf20Sopenharmony_ci b->legacy_mem->attr.name = "legacy_mem"; 9388c2ecf20Sopenharmony_ci b->legacy_mem->size = 1024*1024; 9398c2ecf20Sopenharmony_ci b->legacy_mem->attr.mode = 0600; 9408c2ecf20Sopenharmony_ci b->legacy_mem->mmap = pci_mmap_legacy_mem; 9418c2ecf20Sopenharmony_ci pci_adjust_legacy_attr(b, pci_mmap_mem); 9428c2ecf20Sopenharmony_ci error = device_create_bin_file(&b->dev, b->legacy_mem); 9438c2ecf20Sopenharmony_ci if (error) 9448c2ecf20Sopenharmony_ci goto legacy_mem_err; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci return; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cilegacy_mem_err: 9498c2ecf20Sopenharmony_ci device_remove_bin_file(&b->dev, b->legacy_io); 9508c2ecf20Sopenharmony_cilegacy_io_err: 9518c2ecf20Sopenharmony_ci kfree(b->legacy_io); 9528c2ecf20Sopenharmony_ci b->legacy_io = NULL; 9538c2ecf20Sopenharmony_cikzalloc_err: 9548c2ecf20Sopenharmony_ci dev_warn(&b->dev, "could not create legacy I/O port and ISA memory resources in sysfs\n"); 9558c2ecf20Sopenharmony_ci} 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_civoid pci_remove_legacy_files(struct pci_bus *b) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci if (b->legacy_io) { 9608c2ecf20Sopenharmony_ci device_remove_bin_file(&b->dev, b->legacy_io); 9618c2ecf20Sopenharmony_ci device_remove_bin_file(&b->dev, b->legacy_mem); 9628c2ecf20Sopenharmony_ci kfree(b->legacy_io); /* both are allocated here */ 9638c2ecf20Sopenharmony_ci } 9648c2ecf20Sopenharmony_ci} 9658c2ecf20Sopenharmony_ci#endif /* HAVE_PCI_LEGACY */ 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ciint pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma, 9708c2ecf20Sopenharmony_ci enum pci_mmap_api mmap_api) 9718c2ecf20Sopenharmony_ci{ 9728c2ecf20Sopenharmony_ci unsigned long nr, start, size; 9738c2ecf20Sopenharmony_ci resource_size_t pci_start = 0, pci_end; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci if (pci_resource_len(pdev, resno) == 0) 9768c2ecf20Sopenharmony_ci return 0; 9778c2ecf20Sopenharmony_ci nr = vma_pages(vma); 9788c2ecf20Sopenharmony_ci start = vma->vm_pgoff; 9798c2ecf20Sopenharmony_ci size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1; 9808c2ecf20Sopenharmony_ci if (mmap_api == PCI_MMAP_PROCFS) { 9818c2ecf20Sopenharmony_ci pci_resource_to_user(pdev, resno, &pdev->resource[resno], 9828c2ecf20Sopenharmony_ci &pci_start, &pci_end); 9838c2ecf20Sopenharmony_ci pci_start >>= PAGE_SHIFT; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci if (start >= pci_start && start < pci_start + size && 9868c2ecf20Sopenharmony_ci start + nr <= pci_start + size) 9878c2ecf20Sopenharmony_ci return 1; 9888c2ecf20Sopenharmony_ci return 0; 9898c2ecf20Sopenharmony_ci} 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci/** 9928c2ecf20Sopenharmony_ci * pci_mmap_resource - map a PCI resource into user memory space 9938c2ecf20Sopenharmony_ci * @kobj: kobject for mapping 9948c2ecf20Sopenharmony_ci * @attr: struct bin_attribute for the file being mapped 9958c2ecf20Sopenharmony_ci * @vma: struct vm_area_struct passed into the mmap 9968c2ecf20Sopenharmony_ci * @write_combine: 1 for write_combine mapping 9978c2ecf20Sopenharmony_ci * 9988c2ecf20Sopenharmony_ci * Use the regular PCI mapping routines to map a PCI resource into userspace. 9998c2ecf20Sopenharmony_ci */ 10008c2ecf20Sopenharmony_cistatic int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, 10018c2ecf20Sopenharmony_ci struct vm_area_struct *vma, int write_combine) 10028c2ecf20Sopenharmony_ci{ 10038c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 10048c2ecf20Sopenharmony_ci int bar = (unsigned long)attr->private; 10058c2ecf20Sopenharmony_ci enum pci_mmap_state mmap_type; 10068c2ecf20Sopenharmony_ci struct resource *res = &pdev->resource[bar]; 10078c2ecf20Sopenharmony_ci int ret; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci ret = security_locked_down(LOCKDOWN_PCI_ACCESS); 10108c2ecf20Sopenharmony_ci if (ret) 10118c2ecf20Sopenharmony_ci return ret; 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start)) 10148c2ecf20Sopenharmony_ci return -EINVAL; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci if (!pci_mmap_fits(pdev, bar, vma, PCI_MMAP_SYSFS)) 10178c2ecf20Sopenharmony_ci return -EINVAL; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci return pci_mmap_resource_range(pdev, bar, vma, mmap_type, write_combine); 10228c2ecf20Sopenharmony_ci} 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_cistatic int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj, 10258c2ecf20Sopenharmony_ci struct bin_attribute *attr, 10268c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 10278c2ecf20Sopenharmony_ci{ 10288c2ecf20Sopenharmony_ci return pci_mmap_resource(kobj, attr, vma, 0); 10298c2ecf20Sopenharmony_ci} 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_cistatic int pci_mmap_resource_wc(struct file *filp, struct kobject *kobj, 10328c2ecf20Sopenharmony_ci struct bin_attribute *attr, 10338c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci return pci_mmap_resource(kobj, attr, vma, 1); 10368c2ecf20Sopenharmony_ci} 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_cistatic ssize_t pci_resource_io(struct file *filp, struct kobject *kobj, 10398c2ecf20Sopenharmony_ci struct bin_attribute *attr, char *buf, 10408c2ecf20Sopenharmony_ci loff_t off, size_t count, bool write) 10418c2ecf20Sopenharmony_ci{ 10428c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 10438c2ecf20Sopenharmony_ci int bar = (unsigned long)attr->private; 10448c2ecf20Sopenharmony_ci unsigned long port = off; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci port += pci_resource_start(pdev, bar); 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci if (port > pci_resource_end(pdev, bar)) 10498c2ecf20Sopenharmony_ci return 0; 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci if (port + count - 1 > pci_resource_end(pdev, bar)) 10528c2ecf20Sopenharmony_ci return -EINVAL; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci switch (count) { 10558c2ecf20Sopenharmony_ci case 1: 10568c2ecf20Sopenharmony_ci if (write) 10578c2ecf20Sopenharmony_ci outb(*(u8 *)buf, port); 10588c2ecf20Sopenharmony_ci else 10598c2ecf20Sopenharmony_ci *(u8 *)buf = inb(port); 10608c2ecf20Sopenharmony_ci return 1; 10618c2ecf20Sopenharmony_ci case 2: 10628c2ecf20Sopenharmony_ci if (write) 10638c2ecf20Sopenharmony_ci outw(*(u16 *)buf, port); 10648c2ecf20Sopenharmony_ci else 10658c2ecf20Sopenharmony_ci *(u16 *)buf = inw(port); 10668c2ecf20Sopenharmony_ci return 2; 10678c2ecf20Sopenharmony_ci case 4: 10688c2ecf20Sopenharmony_ci if (write) 10698c2ecf20Sopenharmony_ci outl(*(u32 *)buf, port); 10708c2ecf20Sopenharmony_ci else 10718c2ecf20Sopenharmony_ci *(u32 *)buf = inl(port); 10728c2ecf20Sopenharmony_ci return 4; 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci return -EINVAL; 10758c2ecf20Sopenharmony_ci} 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_cistatic ssize_t pci_read_resource_io(struct file *filp, struct kobject *kobj, 10788c2ecf20Sopenharmony_ci struct bin_attribute *attr, char *buf, 10798c2ecf20Sopenharmony_ci loff_t off, size_t count) 10808c2ecf20Sopenharmony_ci{ 10818c2ecf20Sopenharmony_ci return pci_resource_io(filp, kobj, attr, buf, off, count, false); 10828c2ecf20Sopenharmony_ci} 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_cistatic ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj, 10858c2ecf20Sopenharmony_ci struct bin_attribute *attr, char *buf, 10868c2ecf20Sopenharmony_ci loff_t off, size_t count) 10878c2ecf20Sopenharmony_ci{ 10888c2ecf20Sopenharmony_ci int ret; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci ret = security_locked_down(LOCKDOWN_PCI_ACCESS); 10918c2ecf20Sopenharmony_ci if (ret) 10928c2ecf20Sopenharmony_ci return ret; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci return pci_resource_io(filp, kobj, attr, buf, off, count, true); 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci/** 10988c2ecf20Sopenharmony_ci * pci_remove_resource_files - cleanup resource files 10998c2ecf20Sopenharmony_ci * @pdev: dev to cleanup 11008c2ecf20Sopenharmony_ci * 11018c2ecf20Sopenharmony_ci * If we created resource files for @pdev, remove them from sysfs and 11028c2ecf20Sopenharmony_ci * free their resources. 11038c2ecf20Sopenharmony_ci */ 11048c2ecf20Sopenharmony_cistatic void pci_remove_resource_files(struct pci_dev *pdev) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci int i; 11078c2ecf20Sopenharmony_ci 11088c2ecf20Sopenharmony_ci for (i = 0; i < PCI_STD_NUM_BARS; i++) { 11098c2ecf20Sopenharmony_ci struct bin_attribute *res_attr; 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci res_attr = pdev->res_attr[i]; 11128c2ecf20Sopenharmony_ci if (res_attr) { 11138c2ecf20Sopenharmony_ci sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); 11148c2ecf20Sopenharmony_ci kfree(res_attr); 11158c2ecf20Sopenharmony_ci } 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci res_attr = pdev->res_attr_wc[i]; 11188c2ecf20Sopenharmony_ci if (res_attr) { 11198c2ecf20Sopenharmony_ci sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); 11208c2ecf20Sopenharmony_ci kfree(res_attr); 11218c2ecf20Sopenharmony_ci } 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_cistatic int pci_create_attr(struct pci_dev *pdev, int num, int write_combine) 11268c2ecf20Sopenharmony_ci{ 11278c2ecf20Sopenharmony_ci /* allocate attribute structure, piggyback attribute name */ 11288c2ecf20Sopenharmony_ci int name_len = write_combine ? 13 : 10; 11298c2ecf20Sopenharmony_ci struct bin_attribute *res_attr; 11308c2ecf20Sopenharmony_ci char *res_attr_name; 11318c2ecf20Sopenharmony_ci int retval; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC); 11348c2ecf20Sopenharmony_ci if (!res_attr) 11358c2ecf20Sopenharmony_ci return -ENOMEM; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci res_attr_name = (char *)(res_attr + 1); 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci sysfs_bin_attr_init(res_attr); 11408c2ecf20Sopenharmony_ci if (write_combine) { 11418c2ecf20Sopenharmony_ci sprintf(res_attr_name, "resource%d_wc", num); 11428c2ecf20Sopenharmony_ci res_attr->mmap = pci_mmap_resource_wc; 11438c2ecf20Sopenharmony_ci } else { 11448c2ecf20Sopenharmony_ci sprintf(res_attr_name, "resource%d", num); 11458c2ecf20Sopenharmony_ci if (pci_resource_flags(pdev, num) & IORESOURCE_IO) { 11468c2ecf20Sopenharmony_ci res_attr->read = pci_read_resource_io; 11478c2ecf20Sopenharmony_ci res_attr->write = pci_write_resource_io; 11488c2ecf20Sopenharmony_ci if (arch_can_pci_mmap_io()) 11498c2ecf20Sopenharmony_ci res_attr->mmap = pci_mmap_resource_uc; 11508c2ecf20Sopenharmony_ci } else { 11518c2ecf20Sopenharmony_ci res_attr->mmap = pci_mmap_resource_uc; 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci res_attr->attr.name = res_attr_name; 11558c2ecf20Sopenharmony_ci res_attr->attr.mode = 0600; 11568c2ecf20Sopenharmony_ci res_attr->size = pci_resource_len(pdev, num); 11578c2ecf20Sopenharmony_ci res_attr->private = (void *)(unsigned long)num; 11588c2ecf20Sopenharmony_ci retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); 11598c2ecf20Sopenharmony_ci if (retval) { 11608c2ecf20Sopenharmony_ci kfree(res_attr); 11618c2ecf20Sopenharmony_ci return retval; 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci if (write_combine) 11658c2ecf20Sopenharmony_ci pdev->res_attr_wc[num] = res_attr; 11668c2ecf20Sopenharmony_ci else 11678c2ecf20Sopenharmony_ci pdev->res_attr[num] = res_attr; 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci return 0; 11708c2ecf20Sopenharmony_ci} 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci/** 11738c2ecf20Sopenharmony_ci * pci_create_resource_files - create resource files in sysfs for @dev 11748c2ecf20Sopenharmony_ci * @pdev: dev in question 11758c2ecf20Sopenharmony_ci * 11768c2ecf20Sopenharmony_ci * Walk the resources in @pdev creating files for each resource available. 11778c2ecf20Sopenharmony_ci */ 11788c2ecf20Sopenharmony_cistatic int pci_create_resource_files(struct pci_dev *pdev) 11798c2ecf20Sopenharmony_ci{ 11808c2ecf20Sopenharmony_ci int i; 11818c2ecf20Sopenharmony_ci int retval; 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* Expose the PCI resources from this device as files */ 11848c2ecf20Sopenharmony_ci for (i = 0; i < PCI_STD_NUM_BARS; i++) { 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* skip empty resources */ 11878c2ecf20Sopenharmony_ci if (!pci_resource_len(pdev, i)) 11888c2ecf20Sopenharmony_ci continue; 11898c2ecf20Sopenharmony_ci 11908c2ecf20Sopenharmony_ci retval = pci_create_attr(pdev, i, 0); 11918c2ecf20Sopenharmony_ci /* for prefetchable resources, create a WC mappable file */ 11928c2ecf20Sopenharmony_ci if (!retval && arch_can_pci_mmap_wc() && 11938c2ecf20Sopenharmony_ci pdev->resource[i].flags & IORESOURCE_PREFETCH) 11948c2ecf20Sopenharmony_ci retval = pci_create_attr(pdev, i, 1); 11958c2ecf20Sopenharmony_ci if (retval) { 11968c2ecf20Sopenharmony_ci pci_remove_resource_files(pdev); 11978c2ecf20Sopenharmony_ci return retval; 11988c2ecf20Sopenharmony_ci } 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci return 0; 12018c2ecf20Sopenharmony_ci} 12028c2ecf20Sopenharmony_ci#else /* !(defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)) */ 12038c2ecf20Sopenharmony_ciint __weak pci_create_resource_files(struct pci_dev *dev) { return 0; } 12048c2ecf20Sopenharmony_civoid __weak pci_remove_resource_files(struct pci_dev *dev) { return; } 12058c2ecf20Sopenharmony_ci#endif 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci/** 12088c2ecf20Sopenharmony_ci * pci_write_rom - used to enable access to the PCI ROM display 12098c2ecf20Sopenharmony_ci * @filp: sysfs file 12108c2ecf20Sopenharmony_ci * @kobj: kernel object handle 12118c2ecf20Sopenharmony_ci * @bin_attr: struct bin_attribute for this file 12128c2ecf20Sopenharmony_ci * @buf: user input 12138c2ecf20Sopenharmony_ci * @off: file offset 12148c2ecf20Sopenharmony_ci * @count: number of byte in input 12158c2ecf20Sopenharmony_ci * 12168c2ecf20Sopenharmony_ci * writing anything except 0 enables it 12178c2ecf20Sopenharmony_ci */ 12188c2ecf20Sopenharmony_cistatic ssize_t pci_write_rom(struct file *filp, struct kobject *kobj, 12198c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 12208c2ecf20Sopenharmony_ci loff_t off, size_t count) 12218c2ecf20Sopenharmony_ci{ 12228c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci if ((off == 0) && (*buf == '0') && (count == 2)) 12258c2ecf20Sopenharmony_ci pdev->rom_attr_enabled = 0; 12268c2ecf20Sopenharmony_ci else 12278c2ecf20Sopenharmony_ci pdev->rom_attr_enabled = 1; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci return count; 12308c2ecf20Sopenharmony_ci} 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci/** 12338c2ecf20Sopenharmony_ci * pci_read_rom - read a PCI ROM 12348c2ecf20Sopenharmony_ci * @filp: sysfs file 12358c2ecf20Sopenharmony_ci * @kobj: kernel object handle 12368c2ecf20Sopenharmony_ci * @bin_attr: struct bin_attribute for this file 12378c2ecf20Sopenharmony_ci * @buf: where to put the data we read from the ROM 12388c2ecf20Sopenharmony_ci * @off: file offset 12398c2ecf20Sopenharmony_ci * @count: number of bytes to read 12408c2ecf20Sopenharmony_ci * 12418c2ecf20Sopenharmony_ci * Put @count bytes starting at @off into @buf from the ROM in the PCI 12428c2ecf20Sopenharmony_ci * device corresponding to @kobj. 12438c2ecf20Sopenharmony_ci */ 12448c2ecf20Sopenharmony_cistatic ssize_t pci_read_rom(struct file *filp, struct kobject *kobj, 12458c2ecf20Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 12468c2ecf20Sopenharmony_ci loff_t off, size_t count) 12478c2ecf20Sopenharmony_ci{ 12488c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 12498c2ecf20Sopenharmony_ci void __iomem *rom; 12508c2ecf20Sopenharmony_ci size_t size; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci if (!pdev->rom_attr_enabled) 12538c2ecf20Sopenharmony_ci return -EINVAL; 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci rom = pci_map_rom(pdev, &size); /* size starts out as PCI window size */ 12568c2ecf20Sopenharmony_ci if (!rom || !size) 12578c2ecf20Sopenharmony_ci return -EIO; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci if (off >= size) 12608c2ecf20Sopenharmony_ci count = 0; 12618c2ecf20Sopenharmony_ci else { 12628c2ecf20Sopenharmony_ci if (off + count > size) 12638c2ecf20Sopenharmony_ci count = size - off; 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci memcpy_fromio(buf, rom + off, count); 12668c2ecf20Sopenharmony_ci } 12678c2ecf20Sopenharmony_ci pci_unmap_rom(pdev, rom); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci return count; 12708c2ecf20Sopenharmony_ci} 12718c2ecf20Sopenharmony_ci 12728c2ecf20Sopenharmony_cistatic const struct bin_attribute pci_config_attr = { 12738c2ecf20Sopenharmony_ci .attr = { 12748c2ecf20Sopenharmony_ci .name = "config", 12758c2ecf20Sopenharmony_ci .mode = 0644, 12768c2ecf20Sopenharmony_ci }, 12778c2ecf20Sopenharmony_ci .size = PCI_CFG_SPACE_SIZE, 12788c2ecf20Sopenharmony_ci .read = pci_read_config, 12798c2ecf20Sopenharmony_ci .write = pci_write_config, 12808c2ecf20Sopenharmony_ci}; 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_cistatic const struct bin_attribute pcie_config_attr = { 12838c2ecf20Sopenharmony_ci .attr = { 12848c2ecf20Sopenharmony_ci .name = "config", 12858c2ecf20Sopenharmony_ci .mode = 0644, 12868c2ecf20Sopenharmony_ci }, 12878c2ecf20Sopenharmony_ci .size = PCI_CFG_SPACE_EXP_SIZE, 12888c2ecf20Sopenharmony_ci .read = pci_read_config, 12898c2ecf20Sopenharmony_ci .write = pci_write_config, 12908c2ecf20Sopenharmony_ci}; 12918c2ecf20Sopenharmony_ci 12928c2ecf20Sopenharmony_cistatic ssize_t reset_store(struct device *dev, struct device_attribute *attr, 12938c2ecf20Sopenharmony_ci const char *buf, size_t count) 12948c2ecf20Sopenharmony_ci{ 12958c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 12968c2ecf20Sopenharmony_ci unsigned long val; 12978c2ecf20Sopenharmony_ci ssize_t result = kstrtoul(buf, 0, &val); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci if (result < 0) 13008c2ecf20Sopenharmony_ci return result; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci if (val != 1) 13038c2ecf20Sopenharmony_ci return -EINVAL; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 13068c2ecf20Sopenharmony_ci result = pci_reset_function(pdev); 13078c2ecf20Sopenharmony_ci pm_runtime_put(dev); 13088c2ecf20Sopenharmony_ci if (result < 0) 13098c2ecf20Sopenharmony_ci return result; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci return count; 13128c2ecf20Sopenharmony_ci} 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_cistatic DEVICE_ATTR(reset, 0200, NULL, reset_store); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_cistatic int pci_create_capabilities_sysfs(struct pci_dev *dev) 13178c2ecf20Sopenharmony_ci{ 13188c2ecf20Sopenharmony_ci int retval; 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci pcie_vpd_create_sysfs_dev_files(dev); 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if (dev->reset_fn) { 13238c2ecf20Sopenharmony_ci retval = device_create_file(&dev->dev, &dev_attr_reset); 13248c2ecf20Sopenharmony_ci if (retval) 13258c2ecf20Sopenharmony_ci goto error; 13268c2ecf20Sopenharmony_ci } 13278c2ecf20Sopenharmony_ci return 0; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_cierror: 13308c2ecf20Sopenharmony_ci pcie_vpd_remove_sysfs_dev_files(dev); 13318c2ecf20Sopenharmony_ci return retval; 13328c2ecf20Sopenharmony_ci} 13338c2ecf20Sopenharmony_ci 13348c2ecf20Sopenharmony_ciint __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev) 13358c2ecf20Sopenharmony_ci{ 13368c2ecf20Sopenharmony_ci int retval; 13378c2ecf20Sopenharmony_ci int rom_size; 13388c2ecf20Sopenharmony_ci struct bin_attribute *attr; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci if (!sysfs_initialized) 13418c2ecf20Sopenharmony_ci return -EACCES; 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci if (pdev->cfg_size > PCI_CFG_SPACE_SIZE) 13448c2ecf20Sopenharmony_ci retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr); 13458c2ecf20Sopenharmony_ci else 13468c2ecf20Sopenharmony_ci retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); 13478c2ecf20Sopenharmony_ci if (retval) 13488c2ecf20Sopenharmony_ci goto err; 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci retval = pci_create_resource_files(pdev); 13518c2ecf20Sopenharmony_ci if (retval) 13528c2ecf20Sopenharmony_ci goto err_config_file; 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci /* If the device has a ROM, try to expose it in sysfs. */ 13558c2ecf20Sopenharmony_ci rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE); 13568c2ecf20Sopenharmony_ci if (rom_size) { 13578c2ecf20Sopenharmony_ci attr = kzalloc(sizeof(*attr), GFP_ATOMIC); 13588c2ecf20Sopenharmony_ci if (!attr) { 13598c2ecf20Sopenharmony_ci retval = -ENOMEM; 13608c2ecf20Sopenharmony_ci goto err_resource_files; 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci sysfs_bin_attr_init(attr); 13638c2ecf20Sopenharmony_ci attr->size = rom_size; 13648c2ecf20Sopenharmony_ci attr->attr.name = "rom"; 13658c2ecf20Sopenharmony_ci attr->attr.mode = 0600; 13668c2ecf20Sopenharmony_ci attr->read = pci_read_rom; 13678c2ecf20Sopenharmony_ci attr->write = pci_write_rom; 13688c2ecf20Sopenharmony_ci retval = sysfs_create_bin_file(&pdev->dev.kobj, attr); 13698c2ecf20Sopenharmony_ci if (retval) { 13708c2ecf20Sopenharmony_ci kfree(attr); 13718c2ecf20Sopenharmony_ci goto err_resource_files; 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci pdev->rom_attr = attr; 13748c2ecf20Sopenharmony_ci } 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci /* add sysfs entries for various capabilities */ 13778c2ecf20Sopenharmony_ci retval = pci_create_capabilities_sysfs(pdev); 13788c2ecf20Sopenharmony_ci if (retval) 13798c2ecf20Sopenharmony_ci goto err_rom_file; 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci pci_create_firmware_label_files(pdev); 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_ci return 0; 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_cierr_rom_file: 13868c2ecf20Sopenharmony_ci if (pdev->rom_attr) { 13878c2ecf20Sopenharmony_ci sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); 13888c2ecf20Sopenharmony_ci kfree(pdev->rom_attr); 13898c2ecf20Sopenharmony_ci pdev->rom_attr = NULL; 13908c2ecf20Sopenharmony_ci } 13918c2ecf20Sopenharmony_cierr_resource_files: 13928c2ecf20Sopenharmony_ci pci_remove_resource_files(pdev); 13938c2ecf20Sopenharmony_cierr_config_file: 13948c2ecf20Sopenharmony_ci if (pdev->cfg_size > PCI_CFG_SPACE_SIZE) 13958c2ecf20Sopenharmony_ci sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); 13968c2ecf20Sopenharmony_ci else 13978c2ecf20Sopenharmony_ci sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); 13988c2ecf20Sopenharmony_cierr: 13998c2ecf20Sopenharmony_ci return retval; 14008c2ecf20Sopenharmony_ci} 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_cistatic void pci_remove_capabilities_sysfs(struct pci_dev *dev) 14038c2ecf20Sopenharmony_ci{ 14048c2ecf20Sopenharmony_ci pcie_vpd_remove_sysfs_dev_files(dev); 14058c2ecf20Sopenharmony_ci if (dev->reset_fn) { 14068c2ecf20Sopenharmony_ci device_remove_file(&dev->dev, &dev_attr_reset); 14078c2ecf20Sopenharmony_ci dev->reset_fn = 0; 14088c2ecf20Sopenharmony_ci } 14098c2ecf20Sopenharmony_ci} 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci/** 14128c2ecf20Sopenharmony_ci * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files 14138c2ecf20Sopenharmony_ci * @pdev: device whose entries we should free 14148c2ecf20Sopenharmony_ci * 14158c2ecf20Sopenharmony_ci * Cleanup when @pdev is removed from sysfs. 14168c2ecf20Sopenharmony_ci */ 14178c2ecf20Sopenharmony_civoid pci_remove_sysfs_dev_files(struct pci_dev *pdev) 14188c2ecf20Sopenharmony_ci{ 14198c2ecf20Sopenharmony_ci if (!sysfs_initialized) 14208c2ecf20Sopenharmony_ci return; 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci pci_remove_capabilities_sysfs(pdev); 14238c2ecf20Sopenharmony_ci 14248c2ecf20Sopenharmony_ci if (pdev->cfg_size > PCI_CFG_SPACE_SIZE) 14258c2ecf20Sopenharmony_ci sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); 14268c2ecf20Sopenharmony_ci else 14278c2ecf20Sopenharmony_ci sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci pci_remove_resource_files(pdev); 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_ci if (pdev->rom_attr) { 14328c2ecf20Sopenharmony_ci sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); 14338c2ecf20Sopenharmony_ci kfree(pdev->rom_attr); 14348c2ecf20Sopenharmony_ci pdev->rom_attr = NULL; 14358c2ecf20Sopenharmony_ci } 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci pci_remove_firmware_label_files(pdev); 14388c2ecf20Sopenharmony_ci} 14398c2ecf20Sopenharmony_ci 14408c2ecf20Sopenharmony_cistatic int __init pci_sysfs_init(void) 14418c2ecf20Sopenharmony_ci{ 14428c2ecf20Sopenharmony_ci struct pci_dev *pdev = NULL; 14438c2ecf20Sopenharmony_ci int retval; 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci sysfs_initialized = 1; 14468c2ecf20Sopenharmony_ci for_each_pci_dev(pdev) { 14478c2ecf20Sopenharmony_ci retval = pci_create_sysfs_dev_files(pdev); 14488c2ecf20Sopenharmony_ci if (retval) { 14498c2ecf20Sopenharmony_ci pci_dev_put(pdev); 14508c2ecf20Sopenharmony_ci return retval; 14518c2ecf20Sopenharmony_ci } 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci return 0; 14558c2ecf20Sopenharmony_ci} 14568c2ecf20Sopenharmony_cilate_initcall(pci_sysfs_init); 14578c2ecf20Sopenharmony_ci 14588c2ecf20Sopenharmony_cistatic struct attribute *pci_dev_dev_attrs[] = { 14598c2ecf20Sopenharmony_ci &dev_attr_boot_vga.attr, 14608c2ecf20Sopenharmony_ci NULL, 14618c2ecf20Sopenharmony_ci}; 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_cistatic umode_t pci_dev_attrs_are_visible(struct kobject *kobj, 14648c2ecf20Sopenharmony_ci struct attribute *a, int n) 14658c2ecf20Sopenharmony_ci{ 14668c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 14678c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci if (a == &dev_attr_boot_vga.attr) 14708c2ecf20Sopenharmony_ci if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA) 14718c2ecf20Sopenharmony_ci return 0; 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci return a->mode; 14748c2ecf20Sopenharmony_ci} 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_cistatic struct attribute *pci_dev_hp_attrs[] = { 14778c2ecf20Sopenharmony_ci &dev_attr_remove.attr, 14788c2ecf20Sopenharmony_ci &dev_attr_dev_rescan.attr, 14798c2ecf20Sopenharmony_ci NULL, 14808c2ecf20Sopenharmony_ci}; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_cistatic umode_t pci_dev_hp_attrs_are_visible(struct kobject *kobj, 14838c2ecf20Sopenharmony_ci struct attribute *a, int n) 14848c2ecf20Sopenharmony_ci{ 14858c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 14868c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci if (pdev->is_virtfn) 14898c2ecf20Sopenharmony_ci return 0; 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci return a->mode; 14928c2ecf20Sopenharmony_ci} 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_cistatic umode_t pci_bridge_attrs_are_visible(struct kobject *kobj, 14958c2ecf20Sopenharmony_ci struct attribute *a, int n) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 14988c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci if (pci_is_bridge(pdev)) 15018c2ecf20Sopenharmony_ci return a->mode; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_ci return 0; 15048c2ecf20Sopenharmony_ci} 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_cistatic umode_t pcie_dev_attrs_are_visible(struct kobject *kobj, 15078c2ecf20Sopenharmony_ci struct attribute *a, int n) 15088c2ecf20Sopenharmony_ci{ 15098c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 15108c2ecf20Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci if (pci_is_pcie(pdev)) 15138c2ecf20Sopenharmony_ci return a->mode; 15148c2ecf20Sopenharmony_ci 15158c2ecf20Sopenharmony_ci return 0; 15168c2ecf20Sopenharmony_ci} 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_cistatic const struct attribute_group pci_dev_group = { 15198c2ecf20Sopenharmony_ci .attrs = pci_dev_attrs, 15208c2ecf20Sopenharmony_ci}; 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ciconst struct attribute_group *pci_dev_groups[] = { 15238c2ecf20Sopenharmony_ci &pci_dev_group, 15248c2ecf20Sopenharmony_ci NULL, 15258c2ecf20Sopenharmony_ci}; 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_cistatic const struct attribute_group pci_dev_hp_attr_group = { 15288c2ecf20Sopenharmony_ci .attrs = pci_dev_hp_attrs, 15298c2ecf20Sopenharmony_ci .is_visible = pci_dev_hp_attrs_are_visible, 15308c2ecf20Sopenharmony_ci}; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_cistatic const struct attribute_group pci_dev_attr_group = { 15338c2ecf20Sopenharmony_ci .attrs = pci_dev_dev_attrs, 15348c2ecf20Sopenharmony_ci .is_visible = pci_dev_attrs_are_visible, 15358c2ecf20Sopenharmony_ci}; 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_cistatic const struct attribute_group pci_bridge_attr_group = { 15388c2ecf20Sopenharmony_ci .attrs = pci_bridge_attrs, 15398c2ecf20Sopenharmony_ci .is_visible = pci_bridge_attrs_are_visible, 15408c2ecf20Sopenharmony_ci}; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_cistatic const struct attribute_group pcie_dev_attr_group = { 15438c2ecf20Sopenharmony_ci .attrs = pcie_dev_attrs, 15448c2ecf20Sopenharmony_ci .is_visible = pcie_dev_attrs_are_visible, 15458c2ecf20Sopenharmony_ci}; 15468c2ecf20Sopenharmony_ci 15478c2ecf20Sopenharmony_cistatic const struct attribute_group *pci_dev_attr_groups[] = { 15488c2ecf20Sopenharmony_ci &pci_dev_attr_group, 15498c2ecf20Sopenharmony_ci &pci_dev_hp_attr_group, 15508c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_IOV 15518c2ecf20Sopenharmony_ci &sriov_dev_attr_group, 15528c2ecf20Sopenharmony_ci#endif 15538c2ecf20Sopenharmony_ci &pci_bridge_attr_group, 15548c2ecf20Sopenharmony_ci &pcie_dev_attr_group, 15558c2ecf20Sopenharmony_ci#ifdef CONFIG_PCIEAER 15568c2ecf20Sopenharmony_ci &aer_stats_attr_group, 15578c2ecf20Sopenharmony_ci#endif 15588c2ecf20Sopenharmony_ci#ifdef CONFIG_PCIEASPM 15598c2ecf20Sopenharmony_ci &aspm_ctrl_attr_group, 15608c2ecf20Sopenharmony_ci#endif 15618c2ecf20Sopenharmony_ci NULL, 15628c2ecf20Sopenharmony_ci}; 15638c2ecf20Sopenharmony_ci 15648c2ecf20Sopenharmony_ciconst struct device_type pci_dev_type = { 15658c2ecf20Sopenharmony_ci .groups = pci_dev_attr_groups, 15668c2ecf20Sopenharmony_ci}; 1567