162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * (C) Copyright 2002-2004 Greg Kroah-Hartman <greg@kroah.com> 462306a36Sopenharmony_ci * (C) Copyright 2002-2004 IBM Corp. 562306a36Sopenharmony_ci * (C) Copyright 2003 Matthew Wilcox 662306a36Sopenharmony_ci * (C) Copyright 2003 Hewlett-Packard 762306a36Sopenharmony_ci * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com> 862306a36Sopenharmony_ci * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * File attributes for PCI devices 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * Modeled after usb's driverfs.c 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/bitfield.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/sched.h> 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci#include <linux/stat.h> 2062306a36Sopenharmony_ci#include <linux/export.h> 2162306a36Sopenharmony_ci#include <linux/topology.h> 2262306a36Sopenharmony_ci#include <linux/mm.h> 2362306a36Sopenharmony_ci#include <linux/fs.h> 2462306a36Sopenharmony_ci#include <linux/capability.h> 2562306a36Sopenharmony_ci#include <linux/security.h> 2662306a36Sopenharmony_ci#include <linux/slab.h> 2762306a36Sopenharmony_ci#include <linux/vgaarb.h> 2862306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2962306a36Sopenharmony_ci#include <linux/msi.h> 3062306a36Sopenharmony_ci#include <linux/of.h> 3162306a36Sopenharmony_ci#include <linux/aperture.h> 3262306a36Sopenharmony_ci#include "pci.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int sysfs_initialized; /* = 0 */ 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci/* show configuration fields */ 3762306a36Sopenharmony_ci#define pci_config_attr(field, format_string) \ 3862306a36Sopenharmony_cistatic ssize_t \ 3962306a36Sopenharmony_cifield##_show(struct device *dev, struct device_attribute *attr, char *buf) \ 4062306a36Sopenharmony_ci{ \ 4162306a36Sopenharmony_ci struct pci_dev *pdev; \ 4262306a36Sopenharmony_ci \ 4362306a36Sopenharmony_ci pdev = to_pci_dev(dev); \ 4462306a36Sopenharmony_ci return sysfs_emit(buf, format_string, pdev->field); \ 4562306a36Sopenharmony_ci} \ 4662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(field) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cipci_config_attr(vendor, "0x%04x\n"); 4962306a36Sopenharmony_cipci_config_attr(device, "0x%04x\n"); 5062306a36Sopenharmony_cipci_config_attr(subsystem_vendor, "0x%04x\n"); 5162306a36Sopenharmony_cipci_config_attr(subsystem_device, "0x%04x\n"); 5262306a36Sopenharmony_cipci_config_attr(revision, "0x%02x\n"); 5362306a36Sopenharmony_cipci_config_attr(class, "0x%06x\n"); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic ssize_t irq_show(struct device *dev, 5662306a36Sopenharmony_ci struct device_attribute *attr, 5762306a36Sopenharmony_ci char *buf) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#ifdef CONFIG_PCI_MSI 6262306a36Sopenharmony_ci /* 6362306a36Sopenharmony_ci * For MSI, show the first MSI IRQ; for all other cases including 6462306a36Sopenharmony_ci * MSI-X, show the legacy INTx IRQ. 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_ci if (pdev->msi_enabled) 6762306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pci_irq_vector(pdev, 0)); 6862306a36Sopenharmony_ci#endif 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pdev->irq); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(irq); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic ssize_t broken_parity_status_show(struct device *dev, 7562306a36Sopenharmony_ci struct device_attribute *attr, 7662306a36Sopenharmony_ci char *buf) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 7962306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pdev->broken_parity_status); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic ssize_t broken_parity_status_store(struct device *dev, 8362306a36Sopenharmony_ci struct device_attribute *attr, 8462306a36Sopenharmony_ci const char *buf, size_t count) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 8762306a36Sopenharmony_ci unsigned long val; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 9062306a36Sopenharmony_ci return -EINVAL; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci pdev->broken_parity_status = !!val; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return count; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(broken_parity_status); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic ssize_t pci_dev_show_local_cpu(struct device *dev, bool list, 9962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci const struct cpumask *mask; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#ifdef CONFIG_NUMA 10462306a36Sopenharmony_ci if (dev_to_node(dev) == NUMA_NO_NODE) 10562306a36Sopenharmony_ci mask = cpu_online_mask; 10662306a36Sopenharmony_ci else 10762306a36Sopenharmony_ci mask = cpumask_of_node(dev_to_node(dev)); 10862306a36Sopenharmony_ci#else 10962306a36Sopenharmony_ci mask = cpumask_of_pcibus(to_pci_dev(dev)->bus); 11062306a36Sopenharmony_ci#endif 11162306a36Sopenharmony_ci return cpumap_print_to_pagebuf(list, buf, mask); 11262306a36Sopenharmony_ci} 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_cistatic ssize_t local_cpus_show(struct device *dev, 11562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci return pci_dev_show_local_cpu(dev, false, attr, buf); 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(local_cpus); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic ssize_t local_cpulist_show(struct device *dev, 12262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci return pci_dev_show_local_cpu(dev, true, attr, buf); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(local_cpulist); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/* 12962306a36Sopenharmony_ci * PCI Bus Class Devices 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_cistatic ssize_t cpuaffinity_show(struct device *dev, 13262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci const struct cpumask *cpumask = cpumask_of_pcibus(to_pci_bus(dev)); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return cpumap_print_to_pagebuf(false, buf, cpumask); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_cistatic DEVICE_ATTR_RO(cpuaffinity); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic ssize_t cpulistaffinity_show(struct device *dev, 14162306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci const struct cpumask *cpumask = cpumask_of_pcibus(to_pci_bus(dev)); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci return cpumap_print_to_pagebuf(true, buf, cpumask); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(cpulistaffinity); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_cistatic ssize_t power_state_show(struct device *dev, 15062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", pci_power_name(pdev->current_state)); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(power_state); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/* show resources */ 15962306a36Sopenharmony_cistatic ssize_t resource_show(struct device *dev, struct device_attribute *attr, 16062306a36Sopenharmony_ci char *buf) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 16362306a36Sopenharmony_ci int i; 16462306a36Sopenharmony_ci int max; 16562306a36Sopenharmony_ci resource_size_t start, end; 16662306a36Sopenharmony_ci size_t len = 0; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (pci_dev->subordinate) 16962306a36Sopenharmony_ci max = DEVICE_COUNT_RESOURCE; 17062306a36Sopenharmony_ci else 17162306a36Sopenharmony_ci max = PCI_BRIDGE_RESOURCES; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci for (i = 0; i < max; i++) { 17462306a36Sopenharmony_ci struct resource *res = &pci_dev->resource[i]; 17562306a36Sopenharmony_ci pci_resource_to_user(pci_dev, i, res, &start, &end); 17662306a36Sopenharmony_ci len += sysfs_emit_at(buf, len, "0x%016llx 0x%016llx 0x%016llx\n", 17762306a36Sopenharmony_ci (unsigned long long)start, 17862306a36Sopenharmony_ci (unsigned long long)end, 17962306a36Sopenharmony_ci (unsigned long long)res->flags); 18062306a36Sopenharmony_ci } 18162306a36Sopenharmony_ci return len; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(resource); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic ssize_t max_link_speed_show(struct device *dev, 18662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", 19162306a36Sopenharmony_ci pci_speed_string(pcie_get_speed_cap(pdev))); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(max_link_speed); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic ssize_t max_link_width_show(struct device *dev, 19662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pcie_get_width_cap(pdev)); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_cistatic DEVICE_ATTR_RO(max_link_width); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic ssize_t current_link_speed_show(struct device *dev, 20562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 20662306a36Sopenharmony_ci{ 20762306a36Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 20862306a36Sopenharmony_ci u16 linkstat; 20962306a36Sopenharmony_ci int err; 21062306a36Sopenharmony_ci enum pci_bus_speed speed; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat); 21362306a36Sopenharmony_ci if (err) 21462306a36Sopenharmony_ci return -EINVAL; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci speed = pcie_link_speed[linkstat & PCI_EXP_LNKSTA_CLS]; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return sysfs_emit(buf, "%s\n", pci_speed_string(speed)); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(current_link_speed); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic ssize_t current_link_width_show(struct device *dev, 22362306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 22662306a36Sopenharmony_ci u16 linkstat; 22762306a36Sopenharmony_ci int err; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat); 23062306a36Sopenharmony_ci if (err) 23162306a36Sopenharmony_ci return -EINVAL; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", FIELD_GET(PCI_EXP_LNKSTA_NLW, linkstat)); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(current_link_width); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic ssize_t secondary_bus_number_show(struct device *dev, 23862306a36Sopenharmony_ci struct device_attribute *attr, 23962306a36Sopenharmony_ci char *buf) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 24262306a36Sopenharmony_ci u8 sec_bus; 24362306a36Sopenharmony_ci int err; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci err = pci_read_config_byte(pci_dev, PCI_SECONDARY_BUS, &sec_bus); 24662306a36Sopenharmony_ci if (err) 24762306a36Sopenharmony_ci return -EINVAL; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", sec_bus); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(secondary_bus_number); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic ssize_t subordinate_bus_number_show(struct device *dev, 25462306a36Sopenharmony_ci struct device_attribute *attr, 25562306a36Sopenharmony_ci char *buf) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 25862306a36Sopenharmony_ci u8 sub_bus; 25962306a36Sopenharmony_ci int err; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci err = pci_read_config_byte(pci_dev, PCI_SUBORDINATE_BUS, &sub_bus); 26262306a36Sopenharmony_ci if (err) 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", sub_bus); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(subordinate_bus_number); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic ssize_t ari_enabled_show(struct device *dev, 27062306a36Sopenharmony_ci struct device_attribute *attr, 27162306a36Sopenharmony_ci char *buf) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pci_ari_enabled(pci_dev->bus)); 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_cistatic DEVICE_ATTR_RO(ari_enabled); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic ssize_t modalias_show(struct device *dev, struct device_attribute *attr, 28062306a36Sopenharmony_ci char *buf) 28162306a36Sopenharmony_ci{ 28262306a36Sopenharmony_ci struct pci_dev *pci_dev = to_pci_dev(dev); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return sysfs_emit(buf, "pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02X\n", 28562306a36Sopenharmony_ci pci_dev->vendor, pci_dev->device, 28662306a36Sopenharmony_ci pci_dev->subsystem_vendor, pci_dev->subsystem_device, 28762306a36Sopenharmony_ci (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8), 28862306a36Sopenharmony_ci (u8)(pci_dev->class)); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_cistatic DEVICE_ATTR_RO(modalias); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic ssize_t enable_store(struct device *dev, struct device_attribute *attr, 29362306a36Sopenharmony_ci const char *buf, size_t count) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 29662306a36Sopenharmony_ci unsigned long val; 29762306a36Sopenharmony_ci ssize_t result = 0; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* this can crash the machine when done on the "wrong" device */ 30062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 30162306a36Sopenharmony_ci return -EPERM; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 30462306a36Sopenharmony_ci return -EINVAL; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci device_lock(dev); 30762306a36Sopenharmony_ci if (dev->driver) 30862306a36Sopenharmony_ci result = -EBUSY; 30962306a36Sopenharmony_ci else if (val) 31062306a36Sopenharmony_ci result = pci_enable_device(pdev); 31162306a36Sopenharmony_ci else if (pci_is_enabled(pdev)) 31262306a36Sopenharmony_ci pci_disable_device(pdev); 31362306a36Sopenharmony_ci else 31462306a36Sopenharmony_ci result = -EIO; 31562306a36Sopenharmony_ci device_unlock(dev); 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci return result < 0 ? result : count; 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic ssize_t enable_show(struct device *dev, struct device_attribute *attr, 32162306a36Sopenharmony_ci char *buf) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci struct pci_dev *pdev; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci pdev = to_pci_dev(dev); 32662306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", atomic_read(&pdev->enable_cnt)); 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(enable); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci#ifdef CONFIG_NUMA 33162306a36Sopenharmony_cistatic ssize_t numa_node_store(struct device *dev, 33262306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, 33362306a36Sopenharmony_ci size_t count) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 33662306a36Sopenharmony_ci int node; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 33962306a36Sopenharmony_ci return -EPERM; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (kstrtoint(buf, 0, &node) < 0) 34262306a36Sopenharmony_ci return -EINVAL; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if ((node < 0 && node != NUMA_NO_NODE) || node >= MAX_NUMNODES) 34562306a36Sopenharmony_ci return -EINVAL; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (node != NUMA_NO_NODE && !node_online(node)) 34862306a36Sopenharmony_ci return -EINVAL; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK); 35162306a36Sopenharmony_ci pci_alert(pdev, FW_BUG "Overriding NUMA node to %d. Contact your vendor for updates.", 35262306a36Sopenharmony_ci node); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci dev->numa_node = node; 35562306a36Sopenharmony_ci return count; 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic ssize_t numa_node_show(struct device *dev, struct device_attribute *attr, 35962306a36Sopenharmony_ci char *buf) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", dev->numa_node); 36262306a36Sopenharmony_ci} 36362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(numa_node); 36462306a36Sopenharmony_ci#endif 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic ssize_t dma_mask_bits_show(struct device *dev, 36762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", fls64(pdev->dma_mask)); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(dma_mask_bits); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic ssize_t consistent_dma_mask_bits_show(struct device *dev, 37662306a36Sopenharmony_ci struct device_attribute *attr, 37762306a36Sopenharmony_ci char *buf) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", fls64(dev->coherent_dma_mask)); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(consistent_dma_mask_bits); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic ssize_t msi_bus_show(struct device *dev, struct device_attribute *attr, 38462306a36Sopenharmony_ci char *buf) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 38762306a36Sopenharmony_ci struct pci_bus *subordinate = pdev->subordinate; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", subordinate ? 39062306a36Sopenharmony_ci !(subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI) 39162306a36Sopenharmony_ci : !pdev->no_msi); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic ssize_t msi_bus_store(struct device *dev, struct device_attribute *attr, 39562306a36Sopenharmony_ci const char *buf, size_t count) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 39862306a36Sopenharmony_ci struct pci_bus *subordinate = pdev->subordinate; 39962306a36Sopenharmony_ci unsigned long val; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 40262306a36Sopenharmony_ci return -EPERM; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 40562306a36Sopenharmony_ci return -EINVAL; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* 40862306a36Sopenharmony_ci * "no_msi" and "bus_flags" only affect what happens when a driver 40962306a36Sopenharmony_ci * requests MSI or MSI-X. They don't affect any drivers that have 41062306a36Sopenharmony_ci * already requested MSI or MSI-X. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ci if (!subordinate) { 41362306a36Sopenharmony_ci pdev->no_msi = !val; 41462306a36Sopenharmony_ci pci_info(pdev, "MSI/MSI-X %s for future drivers\n", 41562306a36Sopenharmony_ci val ? "allowed" : "disallowed"); 41662306a36Sopenharmony_ci return count; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci if (val) 42062306a36Sopenharmony_ci subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI; 42162306a36Sopenharmony_ci else 42262306a36Sopenharmony_ci subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci dev_info(&subordinate->dev, "MSI/MSI-X %s for future drivers of devices on this bus\n", 42562306a36Sopenharmony_ci val ? "allowed" : "disallowed"); 42662306a36Sopenharmony_ci return count; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(msi_bus); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic ssize_t rescan_store(const struct bus_type *bus, const char *buf, size_t count) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci unsigned long val; 43362306a36Sopenharmony_ci struct pci_bus *b = NULL; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 43662306a36Sopenharmony_ci return -EINVAL; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (val) { 43962306a36Sopenharmony_ci pci_lock_rescan_remove(); 44062306a36Sopenharmony_ci while ((b = pci_find_next_bus(b)) != NULL) 44162306a36Sopenharmony_ci pci_rescan_bus(b); 44262306a36Sopenharmony_ci pci_unlock_rescan_remove(); 44362306a36Sopenharmony_ci } 44462306a36Sopenharmony_ci return count; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_cistatic BUS_ATTR_WO(rescan); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic struct attribute *pci_bus_attrs[] = { 44962306a36Sopenharmony_ci &bus_attr_rescan.attr, 45062306a36Sopenharmony_ci NULL, 45162306a36Sopenharmony_ci}; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic const struct attribute_group pci_bus_group = { 45462306a36Sopenharmony_ci .attrs = pci_bus_attrs, 45562306a36Sopenharmony_ci}; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ciconst struct attribute_group *pci_bus_groups[] = { 45862306a36Sopenharmony_ci &pci_bus_group, 45962306a36Sopenharmony_ci NULL, 46062306a36Sopenharmony_ci}; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic ssize_t dev_rescan_store(struct device *dev, 46362306a36Sopenharmony_ci struct device_attribute *attr, const char *buf, 46462306a36Sopenharmony_ci size_t count) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci unsigned long val; 46762306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 47062306a36Sopenharmony_ci return -EINVAL; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (val) { 47362306a36Sopenharmony_ci pci_lock_rescan_remove(); 47462306a36Sopenharmony_ci pci_rescan_bus(pdev->bus); 47562306a36Sopenharmony_ci pci_unlock_rescan_remove(); 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci return count; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_cistatic struct device_attribute dev_attr_dev_rescan = __ATTR(rescan, 0200, NULL, 48062306a36Sopenharmony_ci dev_rescan_store); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic ssize_t remove_store(struct device *dev, struct device_attribute *attr, 48362306a36Sopenharmony_ci const char *buf, size_t count) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci unsigned long val; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 48862306a36Sopenharmony_ci return -EINVAL; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (val && device_remove_file_self(dev, attr)) 49162306a36Sopenharmony_ci pci_stop_and_remove_bus_device_locked(to_pci_dev(dev)); 49262306a36Sopenharmony_ci return count; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_cistatic DEVICE_ATTR_IGNORE_LOCKDEP(remove, 0220, NULL, 49562306a36Sopenharmony_ci remove_store); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic ssize_t bus_rescan_store(struct device *dev, 49862306a36Sopenharmony_ci struct device_attribute *attr, 49962306a36Sopenharmony_ci const char *buf, size_t count) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci unsigned long val; 50262306a36Sopenharmony_ci struct pci_bus *bus = to_pci_bus(dev); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 50562306a36Sopenharmony_ci return -EINVAL; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (val) { 50862306a36Sopenharmony_ci pci_lock_rescan_remove(); 50962306a36Sopenharmony_ci if (!pci_is_root_bus(bus) && list_empty(&bus->devices)) 51062306a36Sopenharmony_ci pci_rescan_bus_bridge_resize(bus->self); 51162306a36Sopenharmony_ci else 51262306a36Sopenharmony_ci pci_rescan_bus(bus); 51362306a36Sopenharmony_ci pci_unlock_rescan_remove(); 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci return count; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_cistatic struct device_attribute dev_attr_bus_rescan = __ATTR(rescan, 0200, NULL, 51862306a36Sopenharmony_ci bus_rescan_store); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci#if defined(CONFIG_PM) && defined(CONFIG_ACPI) 52162306a36Sopenharmony_cistatic ssize_t d3cold_allowed_store(struct device *dev, 52262306a36Sopenharmony_ci struct device_attribute *attr, 52362306a36Sopenharmony_ci const char *buf, size_t count) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 52662306a36Sopenharmony_ci unsigned long val; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 52962306a36Sopenharmony_ci return -EINVAL; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci pdev->d3cold_allowed = !!val; 53262306a36Sopenharmony_ci pci_bridge_d3_update(pdev); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci pm_runtime_resume(dev); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return count; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic ssize_t d3cold_allowed_show(struct device *dev, 54062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 54362306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", pdev->d3cold_allowed); 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_cistatic DEVICE_ATTR_RW(d3cold_allowed); 54662306a36Sopenharmony_ci#endif 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci#ifdef CONFIG_OF 54962306a36Sopenharmony_cistatic ssize_t devspec_show(struct device *dev, 55062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 55362306a36Sopenharmony_ci struct device_node *np = pci_device_to_OF_node(pdev); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci if (np == NULL) 55662306a36Sopenharmony_ci return 0; 55762306a36Sopenharmony_ci return sysfs_emit(buf, "%pOF\n", np); 55862306a36Sopenharmony_ci} 55962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(devspec); 56062306a36Sopenharmony_ci#endif 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic ssize_t driver_override_store(struct device *dev, 56362306a36Sopenharmony_ci struct device_attribute *attr, 56462306a36Sopenharmony_ci const char *buf, size_t count) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 56762306a36Sopenharmony_ci int ret; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci ret = driver_set_override(dev, &pdev->driver_override, buf, count); 57062306a36Sopenharmony_ci if (ret) 57162306a36Sopenharmony_ci return ret; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci return count; 57462306a36Sopenharmony_ci} 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_cistatic ssize_t driver_override_show(struct device *dev, 57762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 57862306a36Sopenharmony_ci{ 57962306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 58062306a36Sopenharmony_ci ssize_t len; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci device_lock(dev); 58362306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", pdev->driver_override); 58462306a36Sopenharmony_ci device_unlock(dev); 58562306a36Sopenharmony_ci return len; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_cistatic DEVICE_ATTR_RW(driver_override); 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic struct attribute *pci_dev_attrs[] = { 59062306a36Sopenharmony_ci &dev_attr_power_state.attr, 59162306a36Sopenharmony_ci &dev_attr_resource.attr, 59262306a36Sopenharmony_ci &dev_attr_vendor.attr, 59362306a36Sopenharmony_ci &dev_attr_device.attr, 59462306a36Sopenharmony_ci &dev_attr_subsystem_vendor.attr, 59562306a36Sopenharmony_ci &dev_attr_subsystem_device.attr, 59662306a36Sopenharmony_ci &dev_attr_revision.attr, 59762306a36Sopenharmony_ci &dev_attr_class.attr, 59862306a36Sopenharmony_ci &dev_attr_irq.attr, 59962306a36Sopenharmony_ci &dev_attr_local_cpus.attr, 60062306a36Sopenharmony_ci &dev_attr_local_cpulist.attr, 60162306a36Sopenharmony_ci &dev_attr_modalias.attr, 60262306a36Sopenharmony_ci#ifdef CONFIG_NUMA 60362306a36Sopenharmony_ci &dev_attr_numa_node.attr, 60462306a36Sopenharmony_ci#endif 60562306a36Sopenharmony_ci &dev_attr_dma_mask_bits.attr, 60662306a36Sopenharmony_ci &dev_attr_consistent_dma_mask_bits.attr, 60762306a36Sopenharmony_ci &dev_attr_enable.attr, 60862306a36Sopenharmony_ci &dev_attr_broken_parity_status.attr, 60962306a36Sopenharmony_ci &dev_attr_msi_bus.attr, 61062306a36Sopenharmony_ci#if defined(CONFIG_PM) && defined(CONFIG_ACPI) 61162306a36Sopenharmony_ci &dev_attr_d3cold_allowed.attr, 61262306a36Sopenharmony_ci#endif 61362306a36Sopenharmony_ci#ifdef CONFIG_OF 61462306a36Sopenharmony_ci &dev_attr_devspec.attr, 61562306a36Sopenharmony_ci#endif 61662306a36Sopenharmony_ci &dev_attr_driver_override.attr, 61762306a36Sopenharmony_ci &dev_attr_ari_enabled.attr, 61862306a36Sopenharmony_ci NULL, 61962306a36Sopenharmony_ci}; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic struct attribute *pci_bridge_attrs[] = { 62262306a36Sopenharmony_ci &dev_attr_subordinate_bus_number.attr, 62362306a36Sopenharmony_ci &dev_attr_secondary_bus_number.attr, 62462306a36Sopenharmony_ci NULL, 62562306a36Sopenharmony_ci}; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic struct attribute *pcie_dev_attrs[] = { 62862306a36Sopenharmony_ci &dev_attr_current_link_speed.attr, 62962306a36Sopenharmony_ci &dev_attr_current_link_width.attr, 63062306a36Sopenharmony_ci &dev_attr_max_link_width.attr, 63162306a36Sopenharmony_ci &dev_attr_max_link_speed.attr, 63262306a36Sopenharmony_ci NULL, 63362306a36Sopenharmony_ci}; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_cistatic struct attribute *pcibus_attrs[] = { 63662306a36Sopenharmony_ci &dev_attr_bus_rescan.attr, 63762306a36Sopenharmony_ci &dev_attr_cpuaffinity.attr, 63862306a36Sopenharmony_ci &dev_attr_cpulistaffinity.attr, 63962306a36Sopenharmony_ci NULL, 64062306a36Sopenharmony_ci}; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic const struct attribute_group pcibus_group = { 64362306a36Sopenharmony_ci .attrs = pcibus_attrs, 64462306a36Sopenharmony_ci}; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ciconst struct attribute_group *pcibus_groups[] = { 64762306a36Sopenharmony_ci &pcibus_group, 64862306a36Sopenharmony_ci NULL, 64962306a36Sopenharmony_ci}; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr, 65262306a36Sopenharmony_ci char *buf) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 65562306a36Sopenharmony_ci struct pci_dev *vga_dev = vga_default_device(); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci if (vga_dev) 65862306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", (pdev == vga_dev)); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci return sysfs_emit(buf, "%u\n", 66162306a36Sopenharmony_ci !!(pdev->resource[PCI_ROM_RESOURCE].flags & 66262306a36Sopenharmony_ci IORESOURCE_ROM_SHADOW)); 66362306a36Sopenharmony_ci} 66462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(boot_vga); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic ssize_t pci_read_config(struct file *filp, struct kobject *kobj, 66762306a36Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 66862306a36Sopenharmony_ci loff_t off, size_t count) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); 67162306a36Sopenharmony_ci unsigned int size = 64; 67262306a36Sopenharmony_ci loff_t init_off = off; 67362306a36Sopenharmony_ci u8 *data = (u8 *) buf; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* Several chips lock up trying to read undefined config space */ 67662306a36Sopenharmony_ci if (file_ns_capable(filp, &init_user_ns, CAP_SYS_ADMIN)) 67762306a36Sopenharmony_ci size = dev->cfg_size; 67862306a36Sopenharmony_ci else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) 67962306a36Sopenharmony_ci size = 128; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (off > size) 68262306a36Sopenharmony_ci return 0; 68362306a36Sopenharmony_ci if (off + count > size) { 68462306a36Sopenharmony_ci size -= off; 68562306a36Sopenharmony_ci count = size; 68662306a36Sopenharmony_ci } else { 68762306a36Sopenharmony_ci size = count; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci pci_config_pm_runtime_get(dev); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if ((off & 1) && size) { 69362306a36Sopenharmony_ci u8 val; 69462306a36Sopenharmony_ci pci_user_read_config_byte(dev, off, &val); 69562306a36Sopenharmony_ci data[off - init_off] = val; 69662306a36Sopenharmony_ci off++; 69762306a36Sopenharmony_ci size--; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if ((off & 3) && size > 2) { 70162306a36Sopenharmony_ci u16 val; 70262306a36Sopenharmony_ci pci_user_read_config_word(dev, off, &val); 70362306a36Sopenharmony_ci data[off - init_off] = val & 0xff; 70462306a36Sopenharmony_ci data[off - init_off + 1] = (val >> 8) & 0xff; 70562306a36Sopenharmony_ci off += 2; 70662306a36Sopenharmony_ci size -= 2; 70762306a36Sopenharmony_ci } 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci while (size > 3) { 71062306a36Sopenharmony_ci u32 val; 71162306a36Sopenharmony_ci pci_user_read_config_dword(dev, off, &val); 71262306a36Sopenharmony_ci data[off - init_off] = val & 0xff; 71362306a36Sopenharmony_ci data[off - init_off + 1] = (val >> 8) & 0xff; 71462306a36Sopenharmony_ci data[off - init_off + 2] = (val >> 16) & 0xff; 71562306a36Sopenharmony_ci data[off - init_off + 3] = (val >> 24) & 0xff; 71662306a36Sopenharmony_ci off += 4; 71762306a36Sopenharmony_ci size -= 4; 71862306a36Sopenharmony_ci cond_resched(); 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci if (size >= 2) { 72262306a36Sopenharmony_ci u16 val; 72362306a36Sopenharmony_ci pci_user_read_config_word(dev, off, &val); 72462306a36Sopenharmony_ci data[off - init_off] = val & 0xff; 72562306a36Sopenharmony_ci data[off - init_off + 1] = (val >> 8) & 0xff; 72662306a36Sopenharmony_ci off += 2; 72762306a36Sopenharmony_ci size -= 2; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci if (size > 0) { 73162306a36Sopenharmony_ci u8 val; 73262306a36Sopenharmony_ci pci_user_read_config_byte(dev, off, &val); 73362306a36Sopenharmony_ci data[off - init_off] = val; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci pci_config_pm_runtime_put(dev); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci return count; 73962306a36Sopenharmony_ci} 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_cistatic ssize_t pci_write_config(struct file *filp, struct kobject *kobj, 74262306a36Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 74362306a36Sopenharmony_ci loff_t off, size_t count) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); 74662306a36Sopenharmony_ci unsigned int size = count; 74762306a36Sopenharmony_ci loff_t init_off = off; 74862306a36Sopenharmony_ci u8 *data = (u8 *) buf; 74962306a36Sopenharmony_ci int ret; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci ret = security_locked_down(LOCKDOWN_PCI_ACCESS); 75262306a36Sopenharmony_ci if (ret) 75362306a36Sopenharmony_ci return ret; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (resource_is_exclusive(&dev->driver_exclusive_resource, off, 75662306a36Sopenharmony_ci count)) { 75762306a36Sopenharmony_ci pci_warn_once(dev, "%s: Unexpected write to kernel-exclusive config offset %llx", 75862306a36Sopenharmony_ci current->comm, off); 75962306a36Sopenharmony_ci add_taint(TAINT_USER, LOCKDEP_STILL_OK); 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci if (off > dev->cfg_size) 76362306a36Sopenharmony_ci return 0; 76462306a36Sopenharmony_ci if (off + count > dev->cfg_size) { 76562306a36Sopenharmony_ci size = dev->cfg_size - off; 76662306a36Sopenharmony_ci count = size; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci pci_config_pm_runtime_get(dev); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if ((off & 1) && size) { 77262306a36Sopenharmony_ci pci_user_write_config_byte(dev, off, data[off - init_off]); 77362306a36Sopenharmony_ci off++; 77462306a36Sopenharmony_ci size--; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if ((off & 3) && size > 2) { 77862306a36Sopenharmony_ci u16 val = data[off - init_off]; 77962306a36Sopenharmony_ci val |= (u16) data[off - init_off + 1] << 8; 78062306a36Sopenharmony_ci pci_user_write_config_word(dev, off, val); 78162306a36Sopenharmony_ci off += 2; 78262306a36Sopenharmony_ci size -= 2; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci while (size > 3) { 78662306a36Sopenharmony_ci u32 val = data[off - init_off]; 78762306a36Sopenharmony_ci val |= (u32) data[off - init_off + 1] << 8; 78862306a36Sopenharmony_ci val |= (u32) data[off - init_off + 2] << 16; 78962306a36Sopenharmony_ci val |= (u32) data[off - init_off + 3] << 24; 79062306a36Sopenharmony_ci pci_user_write_config_dword(dev, off, val); 79162306a36Sopenharmony_ci off += 4; 79262306a36Sopenharmony_ci size -= 4; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (size >= 2) { 79662306a36Sopenharmony_ci u16 val = data[off - init_off]; 79762306a36Sopenharmony_ci val |= (u16) data[off - init_off + 1] << 8; 79862306a36Sopenharmony_ci pci_user_write_config_word(dev, off, val); 79962306a36Sopenharmony_ci off += 2; 80062306a36Sopenharmony_ci size -= 2; 80162306a36Sopenharmony_ci } 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (size) 80462306a36Sopenharmony_ci pci_user_write_config_byte(dev, off, data[off - init_off]); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci pci_config_pm_runtime_put(dev); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci return count; 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_cistatic BIN_ATTR(config, 0644, pci_read_config, pci_write_config, 0); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic struct bin_attribute *pci_dev_config_attrs[] = { 81362306a36Sopenharmony_ci &bin_attr_config, 81462306a36Sopenharmony_ci NULL, 81562306a36Sopenharmony_ci}; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistatic umode_t pci_dev_config_attr_is_visible(struct kobject *kobj, 81862306a36Sopenharmony_ci struct bin_attribute *a, int n) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci a->size = PCI_CFG_SPACE_SIZE; 82362306a36Sopenharmony_ci if (pdev->cfg_size > PCI_CFG_SPACE_SIZE) 82462306a36Sopenharmony_ci a->size = PCI_CFG_SPACE_EXP_SIZE; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci return a->attr.mode; 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic const struct attribute_group pci_dev_config_attr_group = { 83062306a36Sopenharmony_ci .bin_attrs = pci_dev_config_attrs, 83162306a36Sopenharmony_ci .is_bin_visible = pci_dev_config_attr_is_visible, 83262306a36Sopenharmony_ci}; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci#ifdef HAVE_PCI_LEGACY 83562306a36Sopenharmony_ci/** 83662306a36Sopenharmony_ci * pci_read_legacy_io - read byte(s) from legacy I/O port space 83762306a36Sopenharmony_ci * @filp: open sysfs file 83862306a36Sopenharmony_ci * @kobj: kobject corresponding to file to read from 83962306a36Sopenharmony_ci * @bin_attr: struct bin_attribute for this file 84062306a36Sopenharmony_ci * @buf: buffer to store results 84162306a36Sopenharmony_ci * @off: offset into legacy I/O port space 84262306a36Sopenharmony_ci * @count: number of bytes to read 84362306a36Sopenharmony_ci * 84462306a36Sopenharmony_ci * Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific 84562306a36Sopenharmony_ci * callback routine (pci_legacy_read). 84662306a36Sopenharmony_ci */ 84762306a36Sopenharmony_cistatic ssize_t pci_read_legacy_io(struct file *filp, struct kobject *kobj, 84862306a36Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 84962306a36Sopenharmony_ci loff_t off, size_t count) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci /* Only support 1, 2 or 4 byte accesses */ 85462306a36Sopenharmony_ci if (count != 1 && count != 2 && count != 4) 85562306a36Sopenharmony_ci return -EINVAL; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci return pci_legacy_read(bus, off, (u32 *)buf, count); 85862306a36Sopenharmony_ci} 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci/** 86162306a36Sopenharmony_ci * pci_write_legacy_io - write byte(s) to legacy I/O port space 86262306a36Sopenharmony_ci * @filp: open sysfs file 86362306a36Sopenharmony_ci * @kobj: kobject corresponding to file to read from 86462306a36Sopenharmony_ci * @bin_attr: struct bin_attribute for this file 86562306a36Sopenharmony_ci * @buf: buffer containing value to be written 86662306a36Sopenharmony_ci * @off: offset into legacy I/O port space 86762306a36Sopenharmony_ci * @count: number of bytes to write 86862306a36Sopenharmony_ci * 86962306a36Sopenharmony_ci * Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific 87062306a36Sopenharmony_ci * callback routine (pci_legacy_write). 87162306a36Sopenharmony_ci */ 87262306a36Sopenharmony_cistatic ssize_t pci_write_legacy_io(struct file *filp, struct kobject *kobj, 87362306a36Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 87462306a36Sopenharmony_ci loff_t off, size_t count) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci /* Only support 1, 2 or 4 byte accesses */ 87962306a36Sopenharmony_ci if (count != 1 && count != 2 && count != 4) 88062306a36Sopenharmony_ci return -EINVAL; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci return pci_legacy_write(bus, off, *(u32 *)buf, count); 88362306a36Sopenharmony_ci} 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci/** 88662306a36Sopenharmony_ci * pci_mmap_legacy_mem - map legacy PCI memory into user memory space 88762306a36Sopenharmony_ci * @filp: open sysfs file 88862306a36Sopenharmony_ci * @kobj: kobject corresponding to device to be mapped 88962306a36Sopenharmony_ci * @attr: struct bin_attribute for this file 89062306a36Sopenharmony_ci * @vma: struct vm_area_struct passed to mmap 89162306a36Sopenharmony_ci * 89262306a36Sopenharmony_ci * Uses an arch specific callback, pci_mmap_legacy_mem_page_range, to mmap 89362306a36Sopenharmony_ci * legacy memory space (first meg of bus space) into application virtual 89462306a36Sopenharmony_ci * memory space. 89562306a36Sopenharmony_ci */ 89662306a36Sopenharmony_cistatic int pci_mmap_legacy_mem(struct file *filp, struct kobject *kobj, 89762306a36Sopenharmony_ci struct bin_attribute *attr, 89862306a36Sopenharmony_ci struct vm_area_struct *vma) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci return pci_mmap_legacy_page_range(bus, vma, pci_mmap_mem); 90362306a36Sopenharmony_ci} 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci/** 90662306a36Sopenharmony_ci * pci_mmap_legacy_io - map legacy PCI IO into user memory space 90762306a36Sopenharmony_ci * @filp: open sysfs file 90862306a36Sopenharmony_ci * @kobj: kobject corresponding to device to be mapped 90962306a36Sopenharmony_ci * @attr: struct bin_attribute for this file 91062306a36Sopenharmony_ci * @vma: struct vm_area_struct passed to mmap 91162306a36Sopenharmony_ci * 91262306a36Sopenharmony_ci * Uses an arch specific callback, pci_mmap_legacy_io_page_range, to mmap 91362306a36Sopenharmony_ci * legacy IO space (first meg of bus space) into application virtual 91462306a36Sopenharmony_ci * memory space. Returns -ENOSYS if the operation isn't supported 91562306a36Sopenharmony_ci */ 91662306a36Sopenharmony_cistatic int pci_mmap_legacy_io(struct file *filp, struct kobject *kobj, 91762306a36Sopenharmony_ci struct bin_attribute *attr, 91862306a36Sopenharmony_ci struct vm_area_struct *vma) 91962306a36Sopenharmony_ci{ 92062306a36Sopenharmony_ci struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj)); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io); 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci/** 92662306a36Sopenharmony_ci * pci_adjust_legacy_attr - adjustment of legacy file attributes 92762306a36Sopenharmony_ci * @b: bus to create files under 92862306a36Sopenharmony_ci * @mmap_type: I/O port or memory 92962306a36Sopenharmony_ci * 93062306a36Sopenharmony_ci * Stub implementation. Can be overridden by arch if necessary. 93162306a36Sopenharmony_ci */ 93262306a36Sopenharmony_civoid __weak pci_adjust_legacy_attr(struct pci_bus *b, 93362306a36Sopenharmony_ci enum pci_mmap_state mmap_type) 93462306a36Sopenharmony_ci{ 93562306a36Sopenharmony_ci} 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci/** 93862306a36Sopenharmony_ci * pci_create_legacy_files - create legacy I/O port and memory files 93962306a36Sopenharmony_ci * @b: bus to create files under 94062306a36Sopenharmony_ci * 94162306a36Sopenharmony_ci * Some platforms allow access to legacy I/O port and ISA memory space on 94262306a36Sopenharmony_ci * a per-bus basis. This routine creates the files and ties them into 94362306a36Sopenharmony_ci * their associated read, write and mmap files from pci-sysfs.c 94462306a36Sopenharmony_ci * 94562306a36Sopenharmony_ci * On error unwind, but don't propagate the error to the caller 94662306a36Sopenharmony_ci * as it is ok to set up the PCI bus without these files. 94762306a36Sopenharmony_ci */ 94862306a36Sopenharmony_civoid pci_create_legacy_files(struct pci_bus *b) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci int error; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (!sysfs_initialized) 95362306a36Sopenharmony_ci return; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci b->legacy_io = kcalloc(2, sizeof(struct bin_attribute), 95662306a36Sopenharmony_ci GFP_ATOMIC); 95762306a36Sopenharmony_ci if (!b->legacy_io) 95862306a36Sopenharmony_ci goto kzalloc_err; 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci sysfs_bin_attr_init(b->legacy_io); 96162306a36Sopenharmony_ci b->legacy_io->attr.name = "legacy_io"; 96262306a36Sopenharmony_ci b->legacy_io->size = 0xffff; 96362306a36Sopenharmony_ci b->legacy_io->attr.mode = 0600; 96462306a36Sopenharmony_ci b->legacy_io->read = pci_read_legacy_io; 96562306a36Sopenharmony_ci b->legacy_io->write = pci_write_legacy_io; 96662306a36Sopenharmony_ci b->legacy_io->mmap = pci_mmap_legacy_io; 96762306a36Sopenharmony_ci b->legacy_io->f_mapping = iomem_get_mapping; 96862306a36Sopenharmony_ci pci_adjust_legacy_attr(b, pci_mmap_io); 96962306a36Sopenharmony_ci error = device_create_bin_file(&b->dev, b->legacy_io); 97062306a36Sopenharmony_ci if (error) 97162306a36Sopenharmony_ci goto legacy_io_err; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci /* Allocated above after the legacy_io struct */ 97462306a36Sopenharmony_ci b->legacy_mem = b->legacy_io + 1; 97562306a36Sopenharmony_ci sysfs_bin_attr_init(b->legacy_mem); 97662306a36Sopenharmony_ci b->legacy_mem->attr.name = "legacy_mem"; 97762306a36Sopenharmony_ci b->legacy_mem->size = 1024*1024; 97862306a36Sopenharmony_ci b->legacy_mem->attr.mode = 0600; 97962306a36Sopenharmony_ci b->legacy_mem->mmap = pci_mmap_legacy_mem; 98062306a36Sopenharmony_ci b->legacy_mem->f_mapping = iomem_get_mapping; 98162306a36Sopenharmony_ci pci_adjust_legacy_attr(b, pci_mmap_mem); 98262306a36Sopenharmony_ci error = device_create_bin_file(&b->dev, b->legacy_mem); 98362306a36Sopenharmony_ci if (error) 98462306a36Sopenharmony_ci goto legacy_mem_err; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci return; 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cilegacy_mem_err: 98962306a36Sopenharmony_ci device_remove_bin_file(&b->dev, b->legacy_io); 99062306a36Sopenharmony_cilegacy_io_err: 99162306a36Sopenharmony_ci kfree(b->legacy_io); 99262306a36Sopenharmony_ci b->legacy_io = NULL; 99362306a36Sopenharmony_cikzalloc_err: 99462306a36Sopenharmony_ci dev_warn(&b->dev, "could not create legacy I/O port and ISA memory resources in sysfs\n"); 99562306a36Sopenharmony_ci} 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_civoid pci_remove_legacy_files(struct pci_bus *b) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci if (b->legacy_io) { 100062306a36Sopenharmony_ci device_remove_bin_file(&b->dev, b->legacy_io); 100162306a36Sopenharmony_ci device_remove_bin_file(&b->dev, b->legacy_mem); 100262306a36Sopenharmony_ci kfree(b->legacy_io); /* both are allocated here */ 100362306a36Sopenharmony_ci } 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci#endif /* HAVE_PCI_LEGACY */ 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci#if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE) 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ciint pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma, 101062306a36Sopenharmony_ci enum pci_mmap_api mmap_api) 101162306a36Sopenharmony_ci{ 101262306a36Sopenharmony_ci unsigned long nr, start, size; 101362306a36Sopenharmony_ci resource_size_t pci_start = 0, pci_end; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (pci_resource_len(pdev, resno) == 0) 101662306a36Sopenharmony_ci return 0; 101762306a36Sopenharmony_ci nr = vma_pages(vma); 101862306a36Sopenharmony_ci start = vma->vm_pgoff; 101962306a36Sopenharmony_ci size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1; 102062306a36Sopenharmony_ci if (mmap_api == PCI_MMAP_PROCFS) { 102162306a36Sopenharmony_ci pci_resource_to_user(pdev, resno, &pdev->resource[resno], 102262306a36Sopenharmony_ci &pci_start, &pci_end); 102362306a36Sopenharmony_ci pci_start >>= PAGE_SHIFT; 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci if (start >= pci_start && start < pci_start + size && 102662306a36Sopenharmony_ci start + nr <= pci_start + size) 102762306a36Sopenharmony_ci return 1; 102862306a36Sopenharmony_ci return 0; 102962306a36Sopenharmony_ci} 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci/** 103262306a36Sopenharmony_ci * pci_mmap_resource - map a PCI resource into user memory space 103362306a36Sopenharmony_ci * @kobj: kobject for mapping 103462306a36Sopenharmony_ci * @attr: struct bin_attribute for the file being mapped 103562306a36Sopenharmony_ci * @vma: struct vm_area_struct passed into the mmap 103662306a36Sopenharmony_ci * @write_combine: 1 for write_combine mapping 103762306a36Sopenharmony_ci * 103862306a36Sopenharmony_ci * Use the regular PCI mapping routines to map a PCI resource into userspace. 103962306a36Sopenharmony_ci */ 104062306a36Sopenharmony_cistatic int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, 104162306a36Sopenharmony_ci struct vm_area_struct *vma, int write_combine) 104262306a36Sopenharmony_ci{ 104362306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 104462306a36Sopenharmony_ci int bar = (unsigned long)attr->private; 104562306a36Sopenharmony_ci enum pci_mmap_state mmap_type; 104662306a36Sopenharmony_ci struct resource *res = &pdev->resource[bar]; 104762306a36Sopenharmony_ci int ret; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci ret = security_locked_down(LOCKDOWN_PCI_ACCESS); 105062306a36Sopenharmony_ci if (ret) 105162306a36Sopenharmony_ci return ret; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start)) 105462306a36Sopenharmony_ci return -EINVAL; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci if (!pci_mmap_fits(pdev, bar, vma, PCI_MMAP_SYSFS)) 105762306a36Sopenharmony_ci return -EINVAL; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; 106062306a36Sopenharmony_ci 106162306a36Sopenharmony_ci return pci_mmap_resource_range(pdev, bar, vma, mmap_type, write_combine); 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cistatic int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj, 106562306a36Sopenharmony_ci struct bin_attribute *attr, 106662306a36Sopenharmony_ci struct vm_area_struct *vma) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci return pci_mmap_resource(kobj, attr, vma, 0); 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_cistatic int pci_mmap_resource_wc(struct file *filp, struct kobject *kobj, 107262306a36Sopenharmony_ci struct bin_attribute *attr, 107362306a36Sopenharmony_ci struct vm_area_struct *vma) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci return pci_mmap_resource(kobj, attr, vma, 1); 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_cistatic ssize_t pci_resource_io(struct file *filp, struct kobject *kobj, 107962306a36Sopenharmony_ci struct bin_attribute *attr, char *buf, 108062306a36Sopenharmony_ci loff_t off, size_t count, bool write) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci#ifdef CONFIG_HAS_IOPORT 108362306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 108462306a36Sopenharmony_ci int bar = (unsigned long)attr->private; 108562306a36Sopenharmony_ci unsigned long port = off; 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci port += pci_resource_start(pdev, bar); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci if (port > pci_resource_end(pdev, bar)) 109062306a36Sopenharmony_ci return 0; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci if (port + count - 1 > pci_resource_end(pdev, bar)) 109362306a36Sopenharmony_ci return -EINVAL; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci switch (count) { 109662306a36Sopenharmony_ci case 1: 109762306a36Sopenharmony_ci if (write) 109862306a36Sopenharmony_ci outb(*(u8 *)buf, port); 109962306a36Sopenharmony_ci else 110062306a36Sopenharmony_ci *(u8 *)buf = inb(port); 110162306a36Sopenharmony_ci return 1; 110262306a36Sopenharmony_ci case 2: 110362306a36Sopenharmony_ci if (write) 110462306a36Sopenharmony_ci outw(*(u16 *)buf, port); 110562306a36Sopenharmony_ci else 110662306a36Sopenharmony_ci *(u16 *)buf = inw(port); 110762306a36Sopenharmony_ci return 2; 110862306a36Sopenharmony_ci case 4: 110962306a36Sopenharmony_ci if (write) 111062306a36Sopenharmony_ci outl(*(u32 *)buf, port); 111162306a36Sopenharmony_ci else 111262306a36Sopenharmony_ci *(u32 *)buf = inl(port); 111362306a36Sopenharmony_ci return 4; 111462306a36Sopenharmony_ci } 111562306a36Sopenharmony_ci return -EINVAL; 111662306a36Sopenharmony_ci#else 111762306a36Sopenharmony_ci return -ENXIO; 111862306a36Sopenharmony_ci#endif 111962306a36Sopenharmony_ci} 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_cistatic ssize_t pci_read_resource_io(struct file *filp, struct kobject *kobj, 112262306a36Sopenharmony_ci struct bin_attribute *attr, char *buf, 112362306a36Sopenharmony_ci loff_t off, size_t count) 112462306a36Sopenharmony_ci{ 112562306a36Sopenharmony_ci return pci_resource_io(filp, kobj, attr, buf, off, count, false); 112662306a36Sopenharmony_ci} 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_cistatic ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj, 112962306a36Sopenharmony_ci struct bin_attribute *attr, char *buf, 113062306a36Sopenharmony_ci loff_t off, size_t count) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci int ret; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci ret = security_locked_down(LOCKDOWN_PCI_ACCESS); 113562306a36Sopenharmony_ci if (ret) 113662306a36Sopenharmony_ci return ret; 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci return pci_resource_io(filp, kobj, attr, buf, off, count, true); 113962306a36Sopenharmony_ci} 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci/** 114262306a36Sopenharmony_ci * pci_remove_resource_files - cleanup resource files 114362306a36Sopenharmony_ci * @pdev: dev to cleanup 114462306a36Sopenharmony_ci * 114562306a36Sopenharmony_ci * If we created resource files for @pdev, remove them from sysfs and 114662306a36Sopenharmony_ci * free their resources. 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_cistatic void pci_remove_resource_files(struct pci_dev *pdev) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci int i; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci for (i = 0; i < PCI_STD_NUM_BARS; i++) { 115362306a36Sopenharmony_ci struct bin_attribute *res_attr; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci res_attr = pdev->res_attr[i]; 115662306a36Sopenharmony_ci if (res_attr) { 115762306a36Sopenharmony_ci sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); 115862306a36Sopenharmony_ci kfree(res_attr); 115962306a36Sopenharmony_ci } 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci res_attr = pdev->res_attr_wc[i]; 116262306a36Sopenharmony_ci if (res_attr) { 116362306a36Sopenharmony_ci sysfs_remove_bin_file(&pdev->dev.kobj, res_attr); 116462306a36Sopenharmony_ci kfree(res_attr); 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci} 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cistatic int pci_create_attr(struct pci_dev *pdev, int num, int write_combine) 117062306a36Sopenharmony_ci{ 117162306a36Sopenharmony_ci /* allocate attribute structure, piggyback attribute name */ 117262306a36Sopenharmony_ci int name_len = write_combine ? 13 : 10; 117362306a36Sopenharmony_ci struct bin_attribute *res_attr; 117462306a36Sopenharmony_ci char *res_attr_name; 117562306a36Sopenharmony_ci int retval; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC); 117862306a36Sopenharmony_ci if (!res_attr) 117962306a36Sopenharmony_ci return -ENOMEM; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci res_attr_name = (char *)(res_attr + 1); 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci sysfs_bin_attr_init(res_attr); 118462306a36Sopenharmony_ci if (write_combine) { 118562306a36Sopenharmony_ci sprintf(res_attr_name, "resource%d_wc", num); 118662306a36Sopenharmony_ci res_attr->mmap = pci_mmap_resource_wc; 118762306a36Sopenharmony_ci } else { 118862306a36Sopenharmony_ci sprintf(res_attr_name, "resource%d", num); 118962306a36Sopenharmony_ci if (pci_resource_flags(pdev, num) & IORESOURCE_IO) { 119062306a36Sopenharmony_ci res_attr->read = pci_read_resource_io; 119162306a36Sopenharmony_ci res_attr->write = pci_write_resource_io; 119262306a36Sopenharmony_ci if (arch_can_pci_mmap_io()) 119362306a36Sopenharmony_ci res_attr->mmap = pci_mmap_resource_uc; 119462306a36Sopenharmony_ci } else { 119562306a36Sopenharmony_ci res_attr->mmap = pci_mmap_resource_uc; 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci if (res_attr->mmap) 119962306a36Sopenharmony_ci res_attr->f_mapping = iomem_get_mapping; 120062306a36Sopenharmony_ci res_attr->attr.name = res_attr_name; 120162306a36Sopenharmony_ci res_attr->attr.mode = 0600; 120262306a36Sopenharmony_ci res_attr->size = pci_resource_len(pdev, num); 120362306a36Sopenharmony_ci res_attr->private = (void *)(unsigned long)num; 120462306a36Sopenharmony_ci retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr); 120562306a36Sopenharmony_ci if (retval) { 120662306a36Sopenharmony_ci kfree(res_attr); 120762306a36Sopenharmony_ci return retval; 120862306a36Sopenharmony_ci } 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (write_combine) 121162306a36Sopenharmony_ci pdev->res_attr_wc[num] = res_attr; 121262306a36Sopenharmony_ci else 121362306a36Sopenharmony_ci pdev->res_attr[num] = res_attr; 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci return 0; 121662306a36Sopenharmony_ci} 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci/** 121962306a36Sopenharmony_ci * pci_create_resource_files - create resource files in sysfs for @dev 122062306a36Sopenharmony_ci * @pdev: dev in question 122162306a36Sopenharmony_ci * 122262306a36Sopenharmony_ci * Walk the resources in @pdev creating files for each resource available. 122362306a36Sopenharmony_ci */ 122462306a36Sopenharmony_cistatic int pci_create_resource_files(struct pci_dev *pdev) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci int i; 122762306a36Sopenharmony_ci int retval; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci /* Expose the PCI resources from this device as files */ 123062306a36Sopenharmony_ci for (i = 0; i < PCI_STD_NUM_BARS; i++) { 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci /* skip empty resources */ 123362306a36Sopenharmony_ci if (!pci_resource_len(pdev, i)) 123462306a36Sopenharmony_ci continue; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci retval = pci_create_attr(pdev, i, 0); 123762306a36Sopenharmony_ci /* for prefetchable resources, create a WC mappable file */ 123862306a36Sopenharmony_ci if (!retval && arch_can_pci_mmap_wc() && 123962306a36Sopenharmony_ci pdev->resource[i].flags & IORESOURCE_PREFETCH) 124062306a36Sopenharmony_ci retval = pci_create_attr(pdev, i, 1); 124162306a36Sopenharmony_ci if (retval) { 124262306a36Sopenharmony_ci pci_remove_resource_files(pdev); 124362306a36Sopenharmony_ci return retval; 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci } 124662306a36Sopenharmony_ci return 0; 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci#else /* !(defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)) */ 124962306a36Sopenharmony_ciint __weak pci_create_resource_files(struct pci_dev *dev) { return 0; } 125062306a36Sopenharmony_civoid __weak pci_remove_resource_files(struct pci_dev *dev) { return; } 125162306a36Sopenharmony_ci#endif 125262306a36Sopenharmony_ci 125362306a36Sopenharmony_ci/** 125462306a36Sopenharmony_ci * pci_write_rom - used to enable access to the PCI ROM display 125562306a36Sopenharmony_ci * @filp: sysfs file 125662306a36Sopenharmony_ci * @kobj: kernel object handle 125762306a36Sopenharmony_ci * @bin_attr: struct bin_attribute for this file 125862306a36Sopenharmony_ci * @buf: user input 125962306a36Sopenharmony_ci * @off: file offset 126062306a36Sopenharmony_ci * @count: number of byte in input 126162306a36Sopenharmony_ci * 126262306a36Sopenharmony_ci * writing anything except 0 enables it 126362306a36Sopenharmony_ci */ 126462306a36Sopenharmony_cistatic ssize_t pci_write_rom(struct file *filp, struct kobject *kobj, 126562306a36Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 126662306a36Sopenharmony_ci loff_t off, size_t count) 126762306a36Sopenharmony_ci{ 126862306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci if ((off == 0) && (*buf == '0') && (count == 2)) 127162306a36Sopenharmony_ci pdev->rom_attr_enabled = 0; 127262306a36Sopenharmony_ci else 127362306a36Sopenharmony_ci pdev->rom_attr_enabled = 1; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_ci return count; 127662306a36Sopenharmony_ci} 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci/** 127962306a36Sopenharmony_ci * pci_read_rom - read a PCI ROM 128062306a36Sopenharmony_ci * @filp: sysfs file 128162306a36Sopenharmony_ci * @kobj: kernel object handle 128262306a36Sopenharmony_ci * @bin_attr: struct bin_attribute for this file 128362306a36Sopenharmony_ci * @buf: where to put the data we read from the ROM 128462306a36Sopenharmony_ci * @off: file offset 128562306a36Sopenharmony_ci * @count: number of bytes to read 128662306a36Sopenharmony_ci * 128762306a36Sopenharmony_ci * Put @count bytes starting at @off into @buf from the ROM in the PCI 128862306a36Sopenharmony_ci * device corresponding to @kobj. 128962306a36Sopenharmony_ci */ 129062306a36Sopenharmony_cistatic ssize_t pci_read_rom(struct file *filp, struct kobject *kobj, 129162306a36Sopenharmony_ci struct bin_attribute *bin_attr, char *buf, 129262306a36Sopenharmony_ci loff_t off, size_t count) 129362306a36Sopenharmony_ci{ 129462306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 129562306a36Sopenharmony_ci void __iomem *rom; 129662306a36Sopenharmony_ci size_t size; 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci if (!pdev->rom_attr_enabled) 129962306a36Sopenharmony_ci return -EINVAL; 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci rom = pci_map_rom(pdev, &size); /* size starts out as PCI window size */ 130262306a36Sopenharmony_ci if (!rom || !size) 130362306a36Sopenharmony_ci return -EIO; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci if (off >= size) 130662306a36Sopenharmony_ci count = 0; 130762306a36Sopenharmony_ci else { 130862306a36Sopenharmony_ci if (off + count > size) 130962306a36Sopenharmony_ci count = size - off; 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci memcpy_fromio(buf, rom + off, count); 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci pci_unmap_rom(pdev, rom); 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci return count; 131662306a36Sopenharmony_ci} 131762306a36Sopenharmony_cistatic BIN_ATTR(rom, 0600, pci_read_rom, pci_write_rom, 0); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_cistatic struct bin_attribute *pci_dev_rom_attrs[] = { 132062306a36Sopenharmony_ci &bin_attr_rom, 132162306a36Sopenharmony_ci NULL, 132262306a36Sopenharmony_ci}; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_cistatic umode_t pci_dev_rom_attr_is_visible(struct kobject *kobj, 132562306a36Sopenharmony_ci struct bin_attribute *a, int n) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 132862306a36Sopenharmony_ci size_t rom_size; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci /* If the device has a ROM, try to expose it in sysfs. */ 133162306a36Sopenharmony_ci rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE); 133262306a36Sopenharmony_ci if (!rom_size) 133362306a36Sopenharmony_ci return 0; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci a->size = rom_size; 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci return a->attr.mode; 133862306a36Sopenharmony_ci} 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_cistatic const struct attribute_group pci_dev_rom_attr_group = { 134162306a36Sopenharmony_ci .bin_attrs = pci_dev_rom_attrs, 134262306a36Sopenharmony_ci .is_bin_visible = pci_dev_rom_attr_is_visible, 134362306a36Sopenharmony_ci}; 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_cistatic ssize_t reset_store(struct device *dev, struct device_attribute *attr, 134662306a36Sopenharmony_ci const char *buf, size_t count) 134762306a36Sopenharmony_ci{ 134862306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 134962306a36Sopenharmony_ci unsigned long val; 135062306a36Sopenharmony_ci ssize_t result; 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci if (kstrtoul(buf, 0, &val) < 0) 135362306a36Sopenharmony_ci return -EINVAL; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci if (val != 1) 135662306a36Sopenharmony_ci return -EINVAL; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci pm_runtime_get_sync(dev); 135962306a36Sopenharmony_ci result = pci_reset_function(pdev); 136062306a36Sopenharmony_ci pm_runtime_put(dev); 136162306a36Sopenharmony_ci if (result < 0) 136262306a36Sopenharmony_ci return result; 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci return count; 136562306a36Sopenharmony_ci} 136662306a36Sopenharmony_cistatic DEVICE_ATTR_WO(reset); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_cistatic struct attribute *pci_dev_reset_attrs[] = { 136962306a36Sopenharmony_ci &dev_attr_reset.attr, 137062306a36Sopenharmony_ci NULL, 137162306a36Sopenharmony_ci}; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_cistatic umode_t pci_dev_reset_attr_is_visible(struct kobject *kobj, 137462306a36Sopenharmony_ci struct attribute *a, int n) 137562306a36Sopenharmony_ci{ 137662306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci if (!pci_reset_supported(pdev)) 137962306a36Sopenharmony_ci return 0; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci return a->mode; 138262306a36Sopenharmony_ci} 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_cistatic const struct attribute_group pci_dev_reset_attr_group = { 138562306a36Sopenharmony_ci .attrs = pci_dev_reset_attrs, 138662306a36Sopenharmony_ci .is_visible = pci_dev_reset_attr_is_visible, 138762306a36Sopenharmony_ci}; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci#define pci_dev_resource_resize_attr(n) \ 139062306a36Sopenharmony_cistatic ssize_t resource##n##_resize_show(struct device *dev, \ 139162306a36Sopenharmony_ci struct device_attribute *attr, \ 139262306a36Sopenharmony_ci char * buf) \ 139362306a36Sopenharmony_ci{ \ 139462306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); \ 139562306a36Sopenharmony_ci ssize_t ret; \ 139662306a36Sopenharmony_ci \ 139762306a36Sopenharmony_ci pci_config_pm_runtime_get(pdev); \ 139862306a36Sopenharmony_ci \ 139962306a36Sopenharmony_ci ret = sysfs_emit(buf, "%016llx\n", \ 140062306a36Sopenharmony_ci (u64)pci_rebar_get_possible_sizes(pdev, n)); \ 140162306a36Sopenharmony_ci \ 140262306a36Sopenharmony_ci pci_config_pm_runtime_put(pdev); \ 140362306a36Sopenharmony_ci \ 140462306a36Sopenharmony_ci return ret; \ 140562306a36Sopenharmony_ci} \ 140662306a36Sopenharmony_ci \ 140762306a36Sopenharmony_cistatic ssize_t resource##n##_resize_store(struct device *dev, \ 140862306a36Sopenharmony_ci struct device_attribute *attr,\ 140962306a36Sopenharmony_ci const char *buf, size_t count)\ 141062306a36Sopenharmony_ci{ \ 141162306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); \ 141262306a36Sopenharmony_ci unsigned long size, flags; \ 141362306a36Sopenharmony_ci int ret, i; \ 141462306a36Sopenharmony_ci u16 cmd; \ 141562306a36Sopenharmony_ci \ 141662306a36Sopenharmony_ci if (kstrtoul(buf, 0, &size) < 0) \ 141762306a36Sopenharmony_ci return -EINVAL; \ 141862306a36Sopenharmony_ci \ 141962306a36Sopenharmony_ci device_lock(dev); \ 142062306a36Sopenharmony_ci if (dev->driver) { \ 142162306a36Sopenharmony_ci ret = -EBUSY; \ 142262306a36Sopenharmony_ci goto unlock; \ 142362306a36Sopenharmony_ci } \ 142462306a36Sopenharmony_ci \ 142562306a36Sopenharmony_ci pci_config_pm_runtime_get(pdev); \ 142662306a36Sopenharmony_ci \ 142762306a36Sopenharmony_ci if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) { \ 142862306a36Sopenharmony_ci ret = aperture_remove_conflicting_pci_devices(pdev, \ 142962306a36Sopenharmony_ci "resourceN_resize"); \ 143062306a36Sopenharmony_ci if (ret) \ 143162306a36Sopenharmony_ci goto pm_put; \ 143262306a36Sopenharmony_ci } \ 143362306a36Sopenharmony_ci \ 143462306a36Sopenharmony_ci pci_read_config_word(pdev, PCI_COMMAND, &cmd); \ 143562306a36Sopenharmony_ci pci_write_config_word(pdev, PCI_COMMAND, \ 143662306a36Sopenharmony_ci cmd & ~PCI_COMMAND_MEMORY); \ 143762306a36Sopenharmony_ci \ 143862306a36Sopenharmony_ci flags = pci_resource_flags(pdev, n); \ 143962306a36Sopenharmony_ci \ 144062306a36Sopenharmony_ci pci_remove_resource_files(pdev); \ 144162306a36Sopenharmony_ci \ 144262306a36Sopenharmony_ci for (i = 0; i < PCI_STD_NUM_BARS; i++) { \ 144362306a36Sopenharmony_ci if (pci_resource_len(pdev, i) && \ 144462306a36Sopenharmony_ci pci_resource_flags(pdev, i) == flags) \ 144562306a36Sopenharmony_ci pci_release_resource(pdev, i); \ 144662306a36Sopenharmony_ci } \ 144762306a36Sopenharmony_ci \ 144862306a36Sopenharmony_ci ret = pci_resize_resource(pdev, n, size); \ 144962306a36Sopenharmony_ci \ 145062306a36Sopenharmony_ci pci_assign_unassigned_bus_resources(pdev->bus); \ 145162306a36Sopenharmony_ci \ 145262306a36Sopenharmony_ci if (pci_create_resource_files(pdev)) \ 145362306a36Sopenharmony_ci pci_warn(pdev, "Failed to recreate resource files after BAR resizing\n");\ 145462306a36Sopenharmony_ci \ 145562306a36Sopenharmony_ci pci_write_config_word(pdev, PCI_COMMAND, cmd); \ 145662306a36Sopenharmony_cipm_put: \ 145762306a36Sopenharmony_ci pci_config_pm_runtime_put(pdev); \ 145862306a36Sopenharmony_ciunlock: \ 145962306a36Sopenharmony_ci device_unlock(dev); \ 146062306a36Sopenharmony_ci \ 146162306a36Sopenharmony_ci return ret ? ret : count; \ 146262306a36Sopenharmony_ci} \ 146362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(resource##n##_resize) 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_cipci_dev_resource_resize_attr(0); 146662306a36Sopenharmony_cipci_dev_resource_resize_attr(1); 146762306a36Sopenharmony_cipci_dev_resource_resize_attr(2); 146862306a36Sopenharmony_cipci_dev_resource_resize_attr(3); 146962306a36Sopenharmony_cipci_dev_resource_resize_attr(4); 147062306a36Sopenharmony_cipci_dev_resource_resize_attr(5); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_cistatic struct attribute *resource_resize_attrs[] = { 147362306a36Sopenharmony_ci &dev_attr_resource0_resize.attr, 147462306a36Sopenharmony_ci &dev_attr_resource1_resize.attr, 147562306a36Sopenharmony_ci &dev_attr_resource2_resize.attr, 147662306a36Sopenharmony_ci &dev_attr_resource3_resize.attr, 147762306a36Sopenharmony_ci &dev_attr_resource4_resize.attr, 147862306a36Sopenharmony_ci &dev_attr_resource5_resize.attr, 147962306a36Sopenharmony_ci NULL, 148062306a36Sopenharmony_ci}; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_cistatic umode_t resource_resize_is_visible(struct kobject *kobj, 148362306a36Sopenharmony_ci struct attribute *a, int n) 148462306a36Sopenharmony_ci{ 148562306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj)); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci return pci_rebar_get_current_size(pdev, n) < 0 ? 0 : a->mode; 148862306a36Sopenharmony_ci} 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_cistatic const struct attribute_group pci_dev_resource_resize_group = { 149162306a36Sopenharmony_ci .attrs = resource_resize_attrs, 149262306a36Sopenharmony_ci .is_visible = resource_resize_is_visible, 149362306a36Sopenharmony_ci}; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ciint __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev) 149662306a36Sopenharmony_ci{ 149762306a36Sopenharmony_ci if (!sysfs_initialized) 149862306a36Sopenharmony_ci return -EACCES; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_ci return pci_create_resource_files(pdev); 150162306a36Sopenharmony_ci} 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci/** 150462306a36Sopenharmony_ci * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files 150562306a36Sopenharmony_ci * @pdev: device whose entries we should free 150662306a36Sopenharmony_ci * 150762306a36Sopenharmony_ci * Cleanup when @pdev is removed from sysfs. 150862306a36Sopenharmony_ci */ 150962306a36Sopenharmony_civoid pci_remove_sysfs_dev_files(struct pci_dev *pdev) 151062306a36Sopenharmony_ci{ 151162306a36Sopenharmony_ci if (!sysfs_initialized) 151262306a36Sopenharmony_ci return; 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci pci_remove_resource_files(pdev); 151562306a36Sopenharmony_ci} 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_cistatic int __init pci_sysfs_init(void) 151862306a36Sopenharmony_ci{ 151962306a36Sopenharmony_ci struct pci_dev *pdev = NULL; 152062306a36Sopenharmony_ci struct pci_bus *pbus = NULL; 152162306a36Sopenharmony_ci int retval; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci sysfs_initialized = 1; 152462306a36Sopenharmony_ci for_each_pci_dev(pdev) { 152562306a36Sopenharmony_ci retval = pci_create_sysfs_dev_files(pdev); 152662306a36Sopenharmony_ci if (retval) { 152762306a36Sopenharmony_ci pci_dev_put(pdev); 152862306a36Sopenharmony_ci return retval; 152962306a36Sopenharmony_ci } 153062306a36Sopenharmony_ci } 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci while ((pbus = pci_find_next_bus(pbus))) 153362306a36Sopenharmony_ci pci_create_legacy_files(pbus); 153462306a36Sopenharmony_ci 153562306a36Sopenharmony_ci return 0; 153662306a36Sopenharmony_ci} 153762306a36Sopenharmony_cilate_initcall(pci_sysfs_init); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_cistatic struct attribute *pci_dev_dev_attrs[] = { 154062306a36Sopenharmony_ci &dev_attr_boot_vga.attr, 154162306a36Sopenharmony_ci NULL, 154262306a36Sopenharmony_ci}; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_cistatic umode_t pci_dev_attrs_are_visible(struct kobject *kobj, 154562306a36Sopenharmony_ci struct attribute *a, int n) 154662306a36Sopenharmony_ci{ 154762306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 154862306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 154962306a36Sopenharmony_ci 155062306a36Sopenharmony_ci if (a == &dev_attr_boot_vga.attr) 155162306a36Sopenharmony_ci if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA) 155262306a36Sopenharmony_ci return 0; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci return a->mode; 155562306a36Sopenharmony_ci} 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_cistatic struct attribute *pci_dev_hp_attrs[] = { 155862306a36Sopenharmony_ci &dev_attr_remove.attr, 155962306a36Sopenharmony_ci &dev_attr_dev_rescan.attr, 156062306a36Sopenharmony_ci NULL, 156162306a36Sopenharmony_ci}; 156262306a36Sopenharmony_ci 156362306a36Sopenharmony_cistatic umode_t pci_dev_hp_attrs_are_visible(struct kobject *kobj, 156462306a36Sopenharmony_ci struct attribute *a, int n) 156562306a36Sopenharmony_ci{ 156662306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 156762306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 156862306a36Sopenharmony_ci 156962306a36Sopenharmony_ci if (pdev->is_virtfn) 157062306a36Sopenharmony_ci return 0; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ci return a->mode; 157362306a36Sopenharmony_ci} 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_cistatic umode_t pci_bridge_attrs_are_visible(struct kobject *kobj, 157662306a36Sopenharmony_ci struct attribute *a, int n) 157762306a36Sopenharmony_ci{ 157862306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 157962306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci if (pci_is_bridge(pdev)) 158262306a36Sopenharmony_ci return a->mode; 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci return 0; 158562306a36Sopenharmony_ci} 158662306a36Sopenharmony_ci 158762306a36Sopenharmony_cistatic umode_t pcie_dev_attrs_are_visible(struct kobject *kobj, 158862306a36Sopenharmony_ci struct attribute *a, int n) 158962306a36Sopenharmony_ci{ 159062306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 159162306a36Sopenharmony_ci struct pci_dev *pdev = to_pci_dev(dev); 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci if (pci_is_pcie(pdev)) 159462306a36Sopenharmony_ci return a->mode; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci return 0; 159762306a36Sopenharmony_ci} 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_cistatic const struct attribute_group pci_dev_group = { 160062306a36Sopenharmony_ci .attrs = pci_dev_attrs, 160162306a36Sopenharmony_ci}; 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ciconst struct attribute_group *pci_dev_groups[] = { 160462306a36Sopenharmony_ci &pci_dev_group, 160562306a36Sopenharmony_ci &pci_dev_config_attr_group, 160662306a36Sopenharmony_ci &pci_dev_rom_attr_group, 160762306a36Sopenharmony_ci &pci_dev_reset_attr_group, 160862306a36Sopenharmony_ci &pci_dev_reset_method_attr_group, 160962306a36Sopenharmony_ci &pci_dev_vpd_attr_group, 161062306a36Sopenharmony_ci#ifdef CONFIG_DMI 161162306a36Sopenharmony_ci &pci_dev_smbios_attr_group, 161262306a36Sopenharmony_ci#endif 161362306a36Sopenharmony_ci#ifdef CONFIG_ACPI 161462306a36Sopenharmony_ci &pci_dev_acpi_attr_group, 161562306a36Sopenharmony_ci#endif 161662306a36Sopenharmony_ci &pci_dev_resource_resize_group, 161762306a36Sopenharmony_ci NULL, 161862306a36Sopenharmony_ci}; 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_cistatic const struct attribute_group pci_dev_hp_attr_group = { 162162306a36Sopenharmony_ci .attrs = pci_dev_hp_attrs, 162262306a36Sopenharmony_ci .is_visible = pci_dev_hp_attrs_are_visible, 162362306a36Sopenharmony_ci}; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_cistatic const struct attribute_group pci_dev_attr_group = { 162662306a36Sopenharmony_ci .attrs = pci_dev_dev_attrs, 162762306a36Sopenharmony_ci .is_visible = pci_dev_attrs_are_visible, 162862306a36Sopenharmony_ci}; 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_cistatic const struct attribute_group pci_bridge_attr_group = { 163162306a36Sopenharmony_ci .attrs = pci_bridge_attrs, 163262306a36Sopenharmony_ci .is_visible = pci_bridge_attrs_are_visible, 163362306a36Sopenharmony_ci}; 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_cistatic const struct attribute_group pcie_dev_attr_group = { 163662306a36Sopenharmony_ci .attrs = pcie_dev_attrs, 163762306a36Sopenharmony_ci .is_visible = pcie_dev_attrs_are_visible, 163862306a36Sopenharmony_ci}; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_cistatic const struct attribute_group *pci_dev_attr_groups[] = { 164162306a36Sopenharmony_ci &pci_dev_attr_group, 164262306a36Sopenharmony_ci &pci_dev_hp_attr_group, 164362306a36Sopenharmony_ci#ifdef CONFIG_PCI_IOV 164462306a36Sopenharmony_ci &sriov_pf_dev_attr_group, 164562306a36Sopenharmony_ci &sriov_vf_dev_attr_group, 164662306a36Sopenharmony_ci#endif 164762306a36Sopenharmony_ci &pci_bridge_attr_group, 164862306a36Sopenharmony_ci &pcie_dev_attr_group, 164962306a36Sopenharmony_ci#ifdef CONFIG_PCIEAER 165062306a36Sopenharmony_ci &aer_stats_attr_group, 165162306a36Sopenharmony_ci#endif 165262306a36Sopenharmony_ci#ifdef CONFIG_PCIEASPM 165362306a36Sopenharmony_ci &aspm_ctrl_attr_group, 165462306a36Sopenharmony_ci#endif 165562306a36Sopenharmony_ci NULL, 165662306a36Sopenharmony_ci}; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ciconst struct device_type pci_dev_type = { 165962306a36Sopenharmony_ci .groups = pci_dev_attr_groups, 166062306a36Sopenharmony_ci}; 1661