1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * (C) Copyright 2002-2004 Greg Kroah-Hartman <greg@kroah.com>
41da177e4SLinus Torvalds * (C) Copyright 2002-2004 IBM Corp.
51da177e4SLinus Torvalds * (C) Copyright 2003 Matthew Wilcox
61da177e4SLinus Torvalds * (C) Copyright 2003 Hewlett-Packard
71da177e4SLinus Torvalds * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
81da177e4SLinus Torvalds * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
91da177e4SLinus Torvalds *
101da177e4SLinus Torvalds * File attributes for PCI devices
111da177e4SLinus Torvalds *
121da177e4SLinus Torvalds * Modeled after usb's driverfs.c
131da177e4SLinus Torvalds */
141da177e4SLinus Torvalds
15d4c5d6fcSIlpo Järvinen #include <linux/bitfield.h>
161da177e4SLinus Torvalds #include <linux/kernel.h>
17b5ff7df3SLinus Torvalds #include <linux/sched.h>
181da177e4SLinus Torvalds #include <linux/pci.h>
191da177e4SLinus Torvalds #include <linux/stat.h>
20363c75dbSPaul Gortmaker #include <linux/export.h>
211da177e4SLinus Torvalds #include <linux/topology.h>
221da177e4SLinus Torvalds #include <linux/mm.h>
23de139a33SChris Wright #include <linux/fs.h>
24aa0ac365SAlexey Dobriyan #include <linux/capability.h>
25a628e7b8SChris Wright #include <linux/security.h>
265a0e3ad6STejun Heo #include <linux/slab.h>
271a39b310SMatthew Garrett #include <linux/vgaarb.h>
28448bd857SHuang Ying #include <linux/pm_runtime.h>
29ac8e3cefSBarry Song #include <linux/msi.h>
30dfc73e7aSSebastian Ott #include <linux/of.h>
3191fa1277SAlex Williamson #include <linux/aperture.h>
321da177e4SLinus Torvalds #include "pci.h"
331da177e4SLinus Torvalds
341da177e4SLinus Torvalds static int sysfs_initialized; /* = 0 */
351da177e4SLinus Torvalds
361da177e4SLinus Torvalds /* show configuration fields */
371da177e4SLinus Torvalds #define pci_config_attr(field, format_string) \
381da177e4SLinus Torvalds static ssize_t \
39e404e274SYani Ioannou field##_show(struct device *dev, struct device_attribute *attr, char *buf) \
401da177e4SLinus Torvalds { \
411da177e4SLinus Torvalds struct pci_dev *pdev; \
421da177e4SLinus Torvalds \
431da177e4SLinus Torvalds pdev = to_pci_dev(dev); \
44ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, format_string, pdev->field); \
455136b2daSGreg Kroah-Hartman } \
465136b2daSGreg Kroah-Hartman static DEVICE_ATTR_RO(field)
471da177e4SLinus Torvalds
481da177e4SLinus Torvalds pci_config_attr(vendor, "0x%04x\n");
491da177e4SLinus Torvalds pci_config_attr(device, "0x%04x\n");
501da177e4SLinus Torvalds pci_config_attr(subsystem_vendor, "0x%04x\n");
511da177e4SLinus Torvalds pci_config_attr(subsystem_device, "0x%04x\n");
52702ed3beSEmil Velikov pci_config_attr(revision, "0x%02x\n");
531da177e4SLinus Torvalds pci_config_attr(class, "0x%06x\n");
54ac8e3cefSBarry Song
irq_show(struct device * dev,struct device_attribute * attr,char * buf)55ac8e3cefSBarry Song static ssize_t irq_show(struct device *dev,
56ac8e3cefSBarry Song struct device_attribute *attr,
57ac8e3cefSBarry Song char *buf)
58ac8e3cefSBarry Song {
59ac8e3cefSBarry Song struct pci_dev *pdev = to_pci_dev(dev);
60ac8e3cefSBarry Song
61ac8e3cefSBarry Song #ifdef CONFIG_PCI_MSI
62ac8e3cefSBarry Song /*
63ac8e3cefSBarry Song * For MSI, show the first MSI IRQ; for all other cases including
64ac8e3cefSBarry Song * MSI-X, show the legacy INTx IRQ.
65ac8e3cefSBarry Song */
66793c5006SThomas Gleixner if (pdev->msi_enabled)
67793c5006SThomas Gleixner return sysfs_emit(buf, "%u\n", pci_irq_vector(pdev, 0));
68ac8e3cefSBarry Song #endif
69ac8e3cefSBarry Song
70ac8e3cefSBarry Song return sysfs_emit(buf, "%u\n", pdev->irq);
71ac8e3cefSBarry Song }
72ac8e3cefSBarry Song static DEVICE_ATTR_RO(irq);
731da177e4SLinus Torvalds
broken_parity_status_show(struct device * dev,struct device_attribute * attr,char * buf)74bdee9d98SDoug Thompson static ssize_t broken_parity_status_show(struct device *dev,
75bdee9d98SDoug Thompson struct device_attribute *attr,
76bdee9d98SDoug Thompson char *buf)
77bdee9d98SDoug Thompson {
78bdee9d98SDoug Thompson struct pci_dev *pdev = to_pci_dev(dev);
79ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%u\n", pdev->broken_parity_status);
80bdee9d98SDoug Thompson }
81bdee9d98SDoug Thompson
broken_parity_status_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)82bdee9d98SDoug Thompson static ssize_t broken_parity_status_store(struct device *dev,
83bdee9d98SDoug Thompson struct device_attribute *attr,
84bdee9d98SDoug Thompson const char *buf, size_t count)
85bdee9d98SDoug Thompson {
86bdee9d98SDoug Thompson struct pci_dev *pdev = to_pci_dev(dev);
8792425a40STrent Piepho unsigned long val;
88bdee9d98SDoug Thompson
899a994e8eSJingoo Han if (kstrtoul(buf, 0, &val) < 0)
9092425a40STrent Piepho return -EINVAL;
9192425a40STrent Piepho
9292425a40STrent Piepho pdev->broken_parity_status = !!val;
9392425a40STrent Piepho
9492425a40STrent Piepho return count;
95bdee9d98SDoug Thompson }
965136b2daSGreg Kroah-Hartman static DEVICE_ATTR_RW(broken_parity_status);
97bdee9d98SDoug Thompson
pci_dev_show_local_cpu(struct device * dev,bool list,struct device_attribute * attr,char * buf)985aaba363SSudeep Holla static ssize_t pci_dev_show_local_cpu(struct device *dev, bool list,
993c78bc61SRyan Desfosses struct device_attribute *attr, char *buf)
1001da177e4SLinus Torvalds {
1013be83050SMike Travis const struct cpumask *mask;
1024327edf6SAlan Cox
103e0cd5160SAndreas Herrmann #ifdef CONFIG_NUMA
104cee0ad4aSMax Gurtovoy if (dev_to_node(dev) == NUMA_NO_NODE)
105cee0ad4aSMax Gurtovoy mask = cpu_online_mask;
106cee0ad4aSMax Gurtovoy else
107cee0ad4aSMax Gurtovoy mask = cpumask_of_node(dev_to_node(dev));
108e0cd5160SAndreas Herrmann #else
1093be83050SMike Travis mask = cpumask_of_pcibus(to_pci_dev(dev)->bus);
110e0cd5160SAndreas Herrmann #endif
1115aaba363SSudeep Holla return cpumap_print_to_pagebuf(list, buf, mask);
11239106dcfSMike Travis }
11339106dcfSMike Travis
local_cpus_show(struct device * dev,struct device_attribute * attr,char * buf)114c489f5fbSYijing Wang static ssize_t local_cpus_show(struct device *dev,
115c489f5fbSYijing Wang struct device_attribute *attr, char *buf)
116c489f5fbSYijing Wang {
1175aaba363SSudeep Holla return pci_dev_show_local_cpu(dev, false, attr, buf);
118c489f5fbSYijing Wang }
1195136b2daSGreg Kroah-Hartman static DEVICE_ATTR_RO(local_cpus);
12039106dcfSMike Travis
local_cpulist_show(struct device * dev,struct device_attribute * attr,char * buf)12139106dcfSMike Travis static ssize_t local_cpulist_show(struct device *dev,
12239106dcfSMike Travis struct device_attribute *attr, char *buf)
12339106dcfSMike Travis {
1245aaba363SSudeep Holla return pci_dev_show_local_cpu(dev, true, attr, buf);
1251da177e4SLinus Torvalds }
1265136b2daSGreg Kroah-Hartman static DEVICE_ATTR_RO(local_cpulist);
1271da177e4SLinus Torvalds
128dc2c2c9dSYinghai Lu /*
129dc2c2c9dSYinghai Lu * PCI Bus Class Devices
130dc2c2c9dSYinghai Lu */
cpuaffinity_show(struct device * dev,struct device_attribute * attr,char * buf)13156039e65SGreg Kroah-Hartman static ssize_t cpuaffinity_show(struct device *dev,
13256039e65SGreg Kroah-Hartman struct device_attribute *attr, char *buf)
133dc2c2c9dSYinghai Lu {
1345aaba363SSudeep Holla const struct cpumask *cpumask = cpumask_of_pcibus(to_pci_bus(dev));
1355aaba363SSudeep Holla
1365aaba363SSudeep Holla return cpumap_print_to_pagebuf(false, buf, cpumask);
137dc2c2c9dSYinghai Lu }
13856039e65SGreg Kroah-Hartman static DEVICE_ATTR_RO(cpuaffinity);
139dc2c2c9dSYinghai Lu
cpulistaffinity_show(struct device * dev,struct device_attribute * attr,char * buf)14056039e65SGreg Kroah-Hartman static ssize_t cpulistaffinity_show(struct device *dev,
14156039e65SGreg Kroah-Hartman struct device_attribute *attr, char *buf)
142dc2c2c9dSYinghai Lu {
1435aaba363SSudeep Holla const struct cpumask *cpumask = cpumask_of_pcibus(to_pci_bus(dev));
1445aaba363SSudeep Holla
1455aaba363SSudeep Holla return cpumap_print_to_pagebuf(true, buf, cpumask);
146dc2c2c9dSYinghai Lu }
14756039e65SGreg Kroah-Hartman static DEVICE_ATTR_RO(cpulistaffinity);
148dc2c2c9dSYinghai Lu
power_state_show(struct device * dev,struct device_attribute * attr,char * buf)14980a129afSMaximilian Luz static ssize_t power_state_show(struct device *dev,
15080a129afSMaximilian Luz struct device_attribute *attr, char *buf)
15180a129afSMaximilian Luz {
15280a129afSMaximilian Luz struct pci_dev *pdev = to_pci_dev(dev);
15380a129afSMaximilian Luz
154ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%s\n", pci_power_name(pdev->current_state));
15580a129afSMaximilian Luz }
15680a129afSMaximilian Luz static DEVICE_ATTR_RO(power_state);
15780a129afSMaximilian Luz
1581da177e4SLinus Torvalds /* show resources */
resource_show(struct device * dev,struct device_attribute * attr,char * buf)1593c78bc61SRyan Desfosses static ssize_t resource_show(struct device *dev, struct device_attribute *attr,
1603c78bc61SRyan Desfosses char *buf)
1611da177e4SLinus Torvalds {
1621da177e4SLinus Torvalds struct pci_dev *pci_dev = to_pci_dev(dev);
1631da177e4SLinus Torvalds int i;
164fde09c6dSYu Zhao int max;
165e31dd6e4SGreg Kroah-Hartman resource_size_t start, end;
166ad025f8eSKrzysztof Wilczyński size_t len = 0;
1671da177e4SLinus Torvalds
1681da177e4SLinus Torvalds if (pci_dev->subordinate)
1691da177e4SLinus Torvalds max = DEVICE_COUNT_RESOURCE;
170fde09c6dSYu Zhao else
171fde09c6dSYu Zhao max = PCI_BRIDGE_RESOURCES;
1721da177e4SLinus Torvalds
1731da177e4SLinus Torvalds for (i = 0; i < max; i++) {
1742311b1f2SMichael Ellerman struct resource *res = &pci_dev->resource[i];
1752311b1f2SMichael Ellerman pci_resource_to_user(pci_dev, i, res, &start, &end);
176ad025f8eSKrzysztof Wilczyński len += sysfs_emit_at(buf, len, "0x%016llx 0x%016llx 0x%016llx\n",
1772311b1f2SMichael Ellerman (unsigned long long)start,
1782311b1f2SMichael Ellerman (unsigned long long)end,
1792311b1f2SMichael Ellerman (unsigned long long)res->flags);
1801da177e4SLinus Torvalds }
181ad025f8eSKrzysztof Wilczyński return len;
1821da177e4SLinus Torvalds }
1835136b2daSGreg Kroah-Hartman static DEVICE_ATTR_RO(resource);
1841da177e4SLinus Torvalds
max_link_speed_show(struct device * dev,struct device_attribute * attr,char * buf)18556c1af46SWong Vee Khee static ssize_t max_link_speed_show(struct device *dev,
18656c1af46SWong Vee Khee struct device_attribute *attr, char *buf)
18756c1af46SWong Vee Khee {
1886cf57be0STal Gilboa struct pci_dev *pdev = to_pci_dev(dev);
18956c1af46SWong Vee Khee
190ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%s\n",
1916348a34dSBjorn Helgaas pci_speed_string(pcie_get_speed_cap(pdev)));
19256c1af46SWong Vee Khee }
19356c1af46SWong Vee Khee static DEVICE_ATTR_RO(max_link_speed);
19456c1af46SWong Vee Khee
max_link_width_show(struct device * dev,struct device_attribute * attr,char * buf)19556c1af46SWong Vee Khee static ssize_t max_link_width_show(struct device *dev,
19656c1af46SWong Vee Khee struct device_attribute *attr, char *buf)
19756c1af46SWong Vee Khee {
198c70b65fbSTal Gilboa struct pci_dev *pdev = to_pci_dev(dev);
19956c1af46SWong Vee Khee
200ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%u\n", pcie_get_width_cap(pdev));
20156c1af46SWong Vee Khee }
20256c1af46SWong Vee Khee static DEVICE_ATTR_RO(max_link_width);
20356c1af46SWong Vee Khee
current_link_speed_show(struct device * dev,struct device_attribute * attr,char * buf)20456c1af46SWong Vee Khee static ssize_t current_link_speed_show(struct device *dev,
20556c1af46SWong Vee Khee struct device_attribute *attr, char *buf)
20656c1af46SWong Vee Khee {
20756c1af46SWong Vee Khee struct pci_dev *pci_dev = to_pci_dev(dev);
20856c1af46SWong Vee Khee u16 linkstat;
20956c1af46SWong Vee Khee int err;
2106348a34dSBjorn Helgaas enum pci_bus_speed speed;
21156c1af46SWong Vee Khee
21256c1af46SWong Vee Khee err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat);
21356c1af46SWong Vee Khee if (err)
21456c1af46SWong Vee Khee return -EINVAL;
21556c1af46SWong Vee Khee
2166348a34dSBjorn Helgaas speed = pcie_link_speed[linkstat & PCI_EXP_LNKSTA_CLS];
21756c1af46SWong Vee Khee
218ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%s\n", pci_speed_string(speed));
21956c1af46SWong Vee Khee }
22056c1af46SWong Vee Khee static DEVICE_ATTR_RO(current_link_speed);
22156c1af46SWong Vee Khee
current_link_width_show(struct device * dev,struct device_attribute * attr,char * buf)22256c1af46SWong Vee Khee static ssize_t current_link_width_show(struct device *dev,
22356c1af46SWong Vee Khee struct device_attribute *attr, char *buf)
22456c1af46SWong Vee Khee {
22556c1af46SWong Vee Khee struct pci_dev *pci_dev = to_pci_dev(dev);
22656c1af46SWong Vee Khee u16 linkstat;
22756c1af46SWong Vee Khee int err;
22856c1af46SWong Vee Khee
22956c1af46SWong Vee Khee err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat);
23056c1af46SWong Vee Khee if (err)
23156c1af46SWong Vee Khee return -EINVAL;
23256c1af46SWong Vee Khee
233d4c5d6fcSIlpo Järvinen return sysfs_emit(buf, "%u\n", FIELD_GET(PCI_EXP_LNKSTA_NLW, linkstat));
23456c1af46SWong Vee Khee }
23556c1af46SWong Vee Khee static DEVICE_ATTR_RO(current_link_width);
23656c1af46SWong Vee Khee
secondary_bus_number_show(struct device * dev,struct device_attribute * attr,char * buf)23756c1af46SWong Vee Khee static ssize_t secondary_bus_number_show(struct device *dev,
23856c1af46SWong Vee Khee struct device_attribute *attr,
23956c1af46SWong Vee Khee char *buf)
24056c1af46SWong Vee Khee {
24156c1af46SWong Vee Khee struct pci_dev *pci_dev = to_pci_dev(dev);
24256c1af46SWong Vee Khee u8 sec_bus;
24356c1af46SWong Vee Khee int err;
24456c1af46SWong Vee Khee
24556c1af46SWong Vee Khee err = pci_read_config_byte(pci_dev, PCI_SECONDARY_BUS, &sec_bus);
24656c1af46SWong Vee Khee if (err)
24756c1af46SWong Vee Khee return -EINVAL;
24856c1af46SWong Vee Khee
249ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%u\n", sec_bus);
25056c1af46SWong Vee Khee }
25156c1af46SWong Vee Khee static DEVICE_ATTR_RO(secondary_bus_number);
25256c1af46SWong Vee Khee
subordinate_bus_number_show(struct device * dev,struct device_attribute * attr,char * buf)25356c1af46SWong Vee Khee static ssize_t subordinate_bus_number_show(struct device *dev,
25456c1af46SWong Vee Khee struct device_attribute *attr,
25556c1af46SWong Vee Khee char *buf)
25656c1af46SWong Vee Khee {
25756c1af46SWong Vee Khee struct pci_dev *pci_dev = to_pci_dev(dev);
25856c1af46SWong Vee Khee u8 sub_bus;
25956c1af46SWong Vee Khee int err;
26056c1af46SWong Vee Khee
26156c1af46SWong Vee Khee err = pci_read_config_byte(pci_dev, PCI_SUBORDINATE_BUS, &sub_bus);
26256c1af46SWong Vee Khee if (err)
26356c1af46SWong Vee Khee return -EINVAL;
26456c1af46SWong Vee Khee
265ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%u\n", sub_bus);
26656c1af46SWong Vee Khee }
26756c1af46SWong Vee Khee static DEVICE_ATTR_RO(subordinate_bus_number);
26856c1af46SWong Vee Khee
ari_enabled_show(struct device * dev,struct device_attribute * attr,char * buf)2690077a845SStuart Hayes static ssize_t ari_enabled_show(struct device *dev,
2700077a845SStuart Hayes struct device_attribute *attr,
2710077a845SStuart Hayes char *buf)
2720077a845SStuart Hayes {
2730077a845SStuart Hayes struct pci_dev *pci_dev = to_pci_dev(dev);
2740077a845SStuart Hayes
275ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%u\n", pci_ari_enabled(pci_dev->bus));
2760077a845SStuart Hayes }
2770077a845SStuart Hayes static DEVICE_ATTR_RO(ari_enabled);
2780077a845SStuart Hayes
modalias_show(struct device * dev,struct device_attribute * attr,char * buf)2793c78bc61SRyan Desfosses static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
2803c78bc61SRyan Desfosses char *buf)
2819888549eSGreg KH {
2829888549eSGreg KH struct pci_dev *pci_dev = to_pci_dev(dev);
2839888549eSGreg KH
284ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02X\n",
2859888549eSGreg KH pci_dev->vendor, pci_dev->device,
2869888549eSGreg KH pci_dev->subsystem_vendor, pci_dev->subsystem_device,
2879888549eSGreg KH (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8),
2889888549eSGreg KH (u8)(pci_dev->class));
2899888549eSGreg KH }
2905136b2daSGreg Kroah-Hartman static DEVICE_ATTR_RO(modalias);
291bae94d02SInaky Perez-Gonzalez
enable_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)292d8e7d53aSGreg Kroah-Hartman static ssize_t enable_store(struct device *dev, struct device_attribute *attr,
2933c78bc61SRyan Desfosses const char *buf, size_t count)
2949f125d30SArjan van de Ven {
2959f125d30SArjan van de Ven struct pci_dev *pdev = to_pci_dev(dev);
29692425a40STrent Piepho unsigned long val;
29736f354ecSKrzysztof Wilczyński ssize_t result = 0;
2989f125d30SArjan van de Ven
2999f125d30SArjan van de Ven /* this can crash the machine when done on the "wrong" device */
3009f125d30SArjan van de Ven if (!capable(CAP_SYS_ADMIN))
30192425a40STrent Piepho return -EPERM;
3029f125d30SArjan van de Ven
30336f354ecSKrzysztof Wilczyński if (kstrtoul(buf, 0, &val) < 0)
30436f354ecSKrzysztof Wilczyński return -EINVAL;
30595e83e21SKrzysztof Wilczyński
3066f5cdfa8SChristoph Hellwig device_lock(dev);
3076f5cdfa8SChristoph Hellwig if (dev->driver)
3086f5cdfa8SChristoph Hellwig result = -EBUSY;
3096f5cdfa8SChristoph Hellwig else if (val)
3106f5cdfa8SChristoph Hellwig result = pci_enable_device(pdev);
3116f5cdfa8SChristoph Hellwig else if (pci_is_enabled(pdev))
3129f125d30SArjan van de Ven pci_disable_device(pdev);
313bae94d02SInaky Perez-Gonzalez else
314bae94d02SInaky Perez-Gonzalez result = -EIO;
3156f5cdfa8SChristoph Hellwig device_unlock(dev);
3169f125d30SArjan van de Ven
317bae94d02SInaky Perez-Gonzalez return result < 0 ? result : count;
318bae94d02SInaky Perez-Gonzalez }
3199f125d30SArjan van de Ven
enable_show(struct device * dev,struct device_attribute * attr,char * buf)320d8e7d53aSGreg Kroah-Hartman static ssize_t enable_show(struct device *dev, struct device_attribute *attr,
3213c78bc61SRyan Desfosses char *buf)
322bae94d02SInaky Perez-Gonzalez {
323bae94d02SInaky Perez-Gonzalez struct pci_dev *pdev;
324bae94d02SInaky Perez-Gonzalez
325bae94d02SInaky Perez-Gonzalez pdev = to_pci_dev(dev);
326ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%u\n", atomic_read(&pdev->enable_cnt));
3279f125d30SArjan van de Ven }
328d8e7d53aSGreg Kroah-Hartman static DEVICE_ATTR_RW(enable);
3299f125d30SArjan van de Ven
33081bb0e19SBrice Goglin #ifdef CONFIG_NUMA
numa_node_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)33163692df1SPrarit Bhargava static ssize_t numa_node_store(struct device *dev,
33263692df1SPrarit Bhargava struct device_attribute *attr, const char *buf,
33363692df1SPrarit Bhargava size_t count)
33463692df1SPrarit Bhargava {
33563692df1SPrarit Bhargava struct pci_dev *pdev = to_pci_dev(dev);
33636f354ecSKrzysztof Wilczyński int node;
33763692df1SPrarit Bhargava
33863692df1SPrarit Bhargava if (!capable(CAP_SYS_ADMIN))
33963692df1SPrarit Bhargava return -EPERM;
34063692df1SPrarit Bhargava
34136f354ecSKrzysztof Wilczyński if (kstrtoint(buf, 0, &node) < 0)
34236f354ecSKrzysztof Wilczyński return -EINVAL;
34363692df1SPrarit Bhargava
3443dcc8d39SMathias Krause if ((node < 0 && node != NUMA_NO_NODE) || node >= MAX_NUMNODES)
3453dcc8d39SMathias Krause return -EINVAL;
3463dcc8d39SMathias Krause
3473dcc8d39SMathias Krause if (node != NUMA_NO_NODE && !node_online(node))
34863692df1SPrarit Bhargava return -EINVAL;
34963692df1SPrarit Bhargava
35063692df1SPrarit Bhargava add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_STILL_OK);
3517506dc79SFrederick Lawler pci_alert(pdev, FW_BUG "Overriding NUMA node to %d. Contact your vendor for updates.",
35263692df1SPrarit Bhargava node);
35363692df1SPrarit Bhargava
35463692df1SPrarit Bhargava dev->numa_node = node;
35563692df1SPrarit Bhargava return count;
35663692df1SPrarit Bhargava }
35763692df1SPrarit Bhargava
numa_node_show(struct device * dev,struct device_attribute * attr,char * buf)3583c78bc61SRyan Desfosses static ssize_t numa_node_show(struct device *dev, struct device_attribute *attr,
3593c78bc61SRyan Desfosses char *buf)
36081bb0e19SBrice Goglin {
361ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%d\n", dev->numa_node);
36281bb0e19SBrice Goglin }
36363692df1SPrarit Bhargava static DEVICE_ATTR_RW(numa_node);
36481bb0e19SBrice Goglin #endif
36581bb0e19SBrice Goglin
dma_mask_bits_show(struct device * dev,struct device_attribute * attr,char * buf)3663c78bc61SRyan Desfosses static ssize_t dma_mask_bits_show(struct device *dev,
3673c78bc61SRyan Desfosses struct device_attribute *attr, char *buf)
368bb965401SYinghai Lu {
369bb965401SYinghai Lu struct pci_dev *pdev = to_pci_dev(dev);
370bb965401SYinghai Lu
371ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%d\n", fls64(pdev->dma_mask));
372bb965401SYinghai Lu }
3735136b2daSGreg Kroah-Hartman static DEVICE_ATTR_RO(dma_mask_bits);
374bb965401SYinghai Lu
consistent_dma_mask_bits_show(struct device * dev,struct device_attribute * attr,char * buf)3753c78bc61SRyan Desfosses static ssize_t consistent_dma_mask_bits_show(struct device *dev,
3763c78bc61SRyan Desfosses struct device_attribute *attr,
377bb965401SYinghai Lu char *buf)
378bb965401SYinghai Lu {
379ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%d\n", fls64(dev->coherent_dma_mask));
380bb965401SYinghai Lu }
3815136b2daSGreg Kroah-Hartman static DEVICE_ATTR_RO(consistent_dma_mask_bits);
382bb965401SYinghai Lu
msi_bus_show(struct device * dev,struct device_attribute * attr,char * buf)3833c78bc61SRyan Desfosses static ssize_t msi_bus_show(struct device *dev, struct device_attribute *attr,
3843c78bc61SRyan Desfosses char *buf)
385fe97064cSBrice Goglin {
386fe97064cSBrice Goglin struct pci_dev *pdev = to_pci_dev(dev);
387468ff15aSYijing Wang struct pci_bus *subordinate = pdev->subordinate;
388fe97064cSBrice Goglin
389ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%u\n", subordinate ?
390468ff15aSYijing Wang !(subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI)
391468ff15aSYijing Wang : !pdev->no_msi);
392fe97064cSBrice Goglin }
393fe97064cSBrice Goglin
msi_bus_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)3943c78bc61SRyan Desfosses static ssize_t msi_bus_store(struct device *dev, struct device_attribute *attr,
395fe97064cSBrice Goglin const char *buf, size_t count)
396fe97064cSBrice Goglin {
397fe97064cSBrice Goglin struct pci_dev *pdev = to_pci_dev(dev);
398468ff15aSYijing Wang struct pci_bus *subordinate = pdev->subordinate;
39992425a40STrent Piepho unsigned long val;
40092425a40STrent Piepho
401fe97064cSBrice Goglin if (!capable(CAP_SYS_ADMIN))
40292425a40STrent Piepho return -EPERM;
403fe97064cSBrice Goglin
40495e83e21SKrzysztof Wilczyński if (kstrtoul(buf, 0, &val) < 0)
40595e83e21SKrzysztof Wilczyński return -EINVAL;
40695e83e21SKrzysztof Wilczyński
407f7625980SBjorn Helgaas /*
408468ff15aSYijing Wang * "no_msi" and "bus_flags" only affect what happens when a driver
409468ff15aSYijing Wang * requests MSI or MSI-X. They don't affect any drivers that have
410468ff15aSYijing Wang * already requested MSI or MSI-X.
411f7625980SBjorn Helgaas */
412468ff15aSYijing Wang if (!subordinate) {
413468ff15aSYijing Wang pdev->no_msi = !val;
4147506dc79SFrederick Lawler pci_info(pdev, "MSI/MSI-X %s for future drivers\n",
415468ff15aSYijing Wang val ? "allowed" : "disallowed");
416fe97064cSBrice Goglin return count;
417fe97064cSBrice Goglin }
418fe97064cSBrice Goglin
419468ff15aSYijing Wang if (val)
420468ff15aSYijing Wang subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI;
421468ff15aSYijing Wang else
422468ff15aSYijing Wang subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
423468ff15aSYijing Wang
424468ff15aSYijing Wang dev_info(&subordinate->dev, "MSI/MSI-X %s for future drivers of devices on this bus\n",
425468ff15aSYijing Wang val ? "allowed" : "disallowed");
426fe97064cSBrice Goglin return count;
427fe97064cSBrice Goglin }
4285136b2daSGreg Kroah-Hartman static DEVICE_ATTR_RW(msi_bus);
4299888549eSGreg KH
rescan_store(const struct bus_type * bus,const char * buf,size_t count)43075cff725SGreg Kroah-Hartman static ssize_t rescan_store(const struct bus_type *bus, const char *buf, size_t count)
431705b1aaaSAlex Chiang {
432705b1aaaSAlex Chiang unsigned long val;
433705b1aaaSAlex Chiang struct pci_bus *b = NULL;
434705b1aaaSAlex Chiang
4359a994e8eSJingoo Han if (kstrtoul(buf, 0, &val) < 0)
436705b1aaaSAlex Chiang return -EINVAL;
437705b1aaaSAlex Chiang
438705b1aaaSAlex Chiang if (val) {
4399d16947bSRafael J. Wysocki pci_lock_rescan_remove();
440705b1aaaSAlex Chiang while ((b = pci_find_next_bus(b)) != NULL)
441705b1aaaSAlex Chiang pci_rescan_bus(b);
4429d16947bSRafael J. Wysocki pci_unlock_rescan_remove();
443705b1aaaSAlex Chiang }
444705b1aaaSAlex Chiang return count;
445705b1aaaSAlex Chiang }
4461094f6d0SGreg Kroah-Hartman static BUS_ATTR_WO(rescan);
447705b1aaaSAlex Chiang
448bf22c90fSSachin Kamat static struct attribute *pci_bus_attrs[] = {
4490f49ba55SGreg Kroah-Hartman &bus_attr_rescan.attr,
4500f49ba55SGreg Kroah-Hartman NULL,
4510f49ba55SGreg Kroah-Hartman };
4520f49ba55SGreg Kroah-Hartman
4530f49ba55SGreg Kroah-Hartman static const struct attribute_group pci_bus_group = {
4540f49ba55SGreg Kroah-Hartman .attrs = pci_bus_attrs,
4550f49ba55SGreg Kroah-Hartman };
4560f49ba55SGreg Kroah-Hartman
4570f49ba55SGreg Kroah-Hartman const struct attribute_group *pci_bus_groups[] = {
4580f49ba55SGreg Kroah-Hartman &pci_bus_group,
4590f49ba55SGreg Kroah-Hartman NULL,
460705b1aaaSAlex Chiang };
46177c27c7bSAlex Chiang
dev_rescan_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)4623c78bc61SRyan Desfosses static ssize_t dev_rescan_store(struct device *dev,
4633c78bc61SRyan Desfosses struct device_attribute *attr, const char *buf,
4643c78bc61SRyan Desfosses size_t count)
465738a6396SAlex Chiang {
466738a6396SAlex Chiang unsigned long val;
467738a6396SAlex Chiang struct pci_dev *pdev = to_pci_dev(dev);
468738a6396SAlex Chiang
4699a994e8eSJingoo Han if (kstrtoul(buf, 0, &val) < 0)
470738a6396SAlex Chiang return -EINVAL;
471738a6396SAlex Chiang
472738a6396SAlex Chiang if (val) {
4739d16947bSRafael J. Wysocki pci_lock_rescan_remove();
474738a6396SAlex Chiang pci_rescan_bus(pdev->bus);
4759d16947bSRafael J. Wysocki pci_unlock_rescan_remove();
476738a6396SAlex Chiang }
477738a6396SAlex Chiang return count;
478738a6396SAlex Chiang }
479bd641fd8SKelsey Skunberg static struct device_attribute dev_attr_dev_rescan = __ATTR(rescan, 0200, NULL,
480bd641fd8SKelsey Skunberg dev_rescan_store);
481738a6396SAlex Chiang
remove_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)4823c78bc61SRyan Desfosses static ssize_t remove_store(struct device *dev, struct device_attribute *attr,
48377c27c7bSAlex Chiang const char *buf, size_t count)
48477c27c7bSAlex Chiang {
48577c27c7bSAlex Chiang unsigned long val;
48677c27c7bSAlex Chiang
4879a994e8eSJingoo Han if (kstrtoul(buf, 0, &val) < 0)
48877c27c7bSAlex Chiang return -EINVAL;
48977c27c7bSAlex Chiang
490bc6caf02STejun Heo if (val && device_remove_file_self(dev, attr))
491bc6caf02STejun Heo pci_stop_and_remove_bus_device_locked(to_pci_dev(dev));
49277c27c7bSAlex Chiang return count;
49377c27c7bSAlex Chiang }
494e2154044SKelsey Skunberg static DEVICE_ATTR_IGNORE_LOCKDEP(remove, 0220, NULL,
4958bdfa145SKelsey Skunberg remove_store);
496b9d320fcSYinghai Lu
bus_rescan_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)4974e2b7943SKelsey Skunberg static ssize_t bus_rescan_store(struct device *dev,
4983c78bc61SRyan Desfosses struct device_attribute *attr,
499b9d320fcSYinghai Lu const char *buf, size_t count)
500b9d320fcSYinghai Lu {
501b9d320fcSYinghai Lu unsigned long val;
502b9d320fcSYinghai Lu struct pci_bus *bus = to_pci_bus(dev);
503b9d320fcSYinghai Lu
5049a994e8eSJingoo Han if (kstrtoul(buf, 0, &val) < 0)
505b9d320fcSYinghai Lu return -EINVAL;
506b9d320fcSYinghai Lu
507b9d320fcSYinghai Lu if (val) {
5089d16947bSRafael J. Wysocki pci_lock_rescan_remove();
5092f320521SYinghai Lu if (!pci_is_root_bus(bus) && list_empty(&bus->devices))
5102f320521SYinghai Lu pci_rescan_bus_bridge_resize(bus->self);
5112f320521SYinghai Lu else
512b9d320fcSYinghai Lu pci_rescan_bus(bus);
5139d16947bSRafael J. Wysocki pci_unlock_rescan_remove();
514b9d320fcSYinghai Lu }
515b9d320fcSYinghai Lu return count;
516b9d320fcSYinghai Lu }
517bd641fd8SKelsey Skunberg static struct device_attribute dev_attr_bus_rescan = __ATTR(rescan, 0200, NULL,
518bd641fd8SKelsey Skunberg bus_rescan_store);
519b9d320fcSYinghai Lu
reset_subordinate_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)520*407476ebSKeith Busch static ssize_t reset_subordinate_store(struct device *dev,
521*407476ebSKeith Busch struct device_attribute *attr,
522*407476ebSKeith Busch const char *buf, size_t count)
523*407476ebSKeith Busch {
524*407476ebSKeith Busch struct pci_dev *pdev = to_pci_dev(dev);
525*407476ebSKeith Busch struct pci_bus *bus = pdev->subordinate;
526*407476ebSKeith Busch unsigned long val;
527*407476ebSKeith Busch
528*407476ebSKeith Busch if (!capable(CAP_SYS_ADMIN))
529*407476ebSKeith Busch return -EPERM;
530*407476ebSKeith Busch
531*407476ebSKeith Busch if (kstrtoul(buf, 0, &val) < 0)
532*407476ebSKeith Busch return -EINVAL;
533*407476ebSKeith Busch
534*407476ebSKeith Busch if (val) {
535*407476ebSKeith Busch int ret = __pci_reset_bus(bus);
536*407476ebSKeith Busch
537*407476ebSKeith Busch if (ret)
538*407476ebSKeith Busch return ret;
539*407476ebSKeith Busch }
540*407476ebSKeith Busch
541*407476ebSKeith Busch return count;
542*407476ebSKeith Busch }
543*407476ebSKeith Busch static DEVICE_ATTR_WO(reset_subordinate);
544*407476ebSKeith Busch
545fbb988beSRafael J. Wysocki #if defined(CONFIG_PM) && defined(CONFIG_ACPI)
d3cold_allowed_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)546448bd857SHuang Ying static ssize_t d3cold_allowed_store(struct device *dev,
547448bd857SHuang Ying struct device_attribute *attr,
548448bd857SHuang Ying const char *buf, size_t count)
549448bd857SHuang Ying {
550448bd857SHuang Ying struct pci_dev *pdev = to_pci_dev(dev);
551448bd857SHuang Ying unsigned long val;
552448bd857SHuang Ying
5539a994e8eSJingoo Han if (kstrtoul(buf, 0, &val) < 0)
554448bd857SHuang Ying return -EINVAL;
555448bd857SHuang Ying
556448bd857SHuang Ying pdev->d3cold_allowed = !!val;
5570763bcefSLukas Wunner pci_bridge_d3_update(pdev);
5589d26d3a8SMika Westerberg
559448bd857SHuang Ying pm_runtime_resume(dev);
560448bd857SHuang Ying
561448bd857SHuang Ying return count;
562448bd857SHuang Ying }
563448bd857SHuang Ying
d3cold_allowed_show(struct device * dev,struct device_attribute * attr,char * buf)564448bd857SHuang Ying static ssize_t d3cold_allowed_show(struct device *dev,
565448bd857SHuang Ying struct device_attribute *attr, char *buf)
566448bd857SHuang Ying {
567448bd857SHuang Ying struct pci_dev *pdev = to_pci_dev(dev);
568ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%u\n", pdev->d3cold_allowed);
569448bd857SHuang Ying }
5705136b2daSGreg Kroah-Hartman static DEVICE_ATTR_RW(d3cold_allowed);
571448bd857SHuang Ying #endif
572448bd857SHuang Ying
573dfc73e7aSSebastian Ott #ifdef CONFIG_OF
devspec_show(struct device * dev,struct device_attribute * attr,char * buf)574dfc73e7aSSebastian Ott static ssize_t devspec_show(struct device *dev,
575dfc73e7aSSebastian Ott struct device_attribute *attr, char *buf)
576dfc73e7aSSebastian Ott {
577dfc73e7aSSebastian Ott struct pci_dev *pdev = to_pci_dev(dev);
578dfc73e7aSSebastian Ott struct device_node *np = pci_device_to_OF_node(pdev);
579dfc73e7aSSebastian Ott
580b63773a8SRob Herring if (np == NULL)
581dfc73e7aSSebastian Ott return 0;
58214c19b2aSKrzysztof Wilczyński return sysfs_emit(buf, "%pOF\n", np);
583dfc73e7aSSebastian Ott }
584dfc73e7aSSebastian Ott static DEVICE_ATTR_RO(devspec);
585dfc73e7aSSebastian Ott #endif
586dfc73e7aSSebastian Ott
driver_override_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)587782a985dSAlex Williamson static ssize_t driver_override_store(struct device *dev,
588782a985dSAlex Williamson struct device_attribute *attr,
589782a985dSAlex Williamson const char *buf, size_t count)
590782a985dSAlex Williamson {
591782a985dSAlex Williamson struct pci_dev *pdev = to_pci_dev(dev);
59223d99bafSKrzysztof Kozlowski int ret;
593782a985dSAlex Williamson
59423d99bafSKrzysztof Kozlowski ret = driver_set_override(dev, &pdev->driver_override, buf, count);
59523d99bafSKrzysztof Kozlowski if (ret)
59623d99bafSKrzysztof Kozlowski return ret;
597782a985dSAlex Williamson
598782a985dSAlex Williamson return count;
599782a985dSAlex Williamson }
600782a985dSAlex Williamson
driver_override_show(struct device * dev,struct device_attribute * attr,char * buf)601782a985dSAlex Williamson static ssize_t driver_override_show(struct device *dev,
602782a985dSAlex Williamson struct device_attribute *attr, char *buf)
603782a985dSAlex Williamson {
604782a985dSAlex Williamson struct pci_dev *pdev = to_pci_dev(dev);
6059561475dSNicolai Stange ssize_t len;
606782a985dSAlex Williamson
6079561475dSNicolai Stange device_lock(dev);
608ad025f8eSKrzysztof Wilczyński len = sysfs_emit(buf, "%s\n", pdev->driver_override);
6099561475dSNicolai Stange device_unlock(dev);
6109561475dSNicolai Stange return len;
611782a985dSAlex Williamson }
612782a985dSAlex Williamson static DEVICE_ATTR_RW(driver_override);
613782a985dSAlex Williamson
614bf22c90fSSachin Kamat static struct attribute *pci_dev_attrs[] = {
61580a129afSMaximilian Luz &dev_attr_power_state.attr,
6165136b2daSGreg Kroah-Hartman &dev_attr_resource.attr,
6175136b2daSGreg Kroah-Hartman &dev_attr_vendor.attr,
6185136b2daSGreg Kroah-Hartman &dev_attr_device.attr,
6195136b2daSGreg Kroah-Hartman &dev_attr_subsystem_vendor.attr,
6205136b2daSGreg Kroah-Hartman &dev_attr_subsystem_device.attr,
621702ed3beSEmil Velikov &dev_attr_revision.attr,
6225136b2daSGreg Kroah-Hartman &dev_attr_class.attr,
6235136b2daSGreg Kroah-Hartman &dev_attr_irq.attr,
6245136b2daSGreg Kroah-Hartman &dev_attr_local_cpus.attr,
6255136b2daSGreg Kroah-Hartman &dev_attr_local_cpulist.attr,
6265136b2daSGreg Kroah-Hartman &dev_attr_modalias.attr,
62781bb0e19SBrice Goglin #ifdef CONFIG_NUMA
6285136b2daSGreg Kroah-Hartman &dev_attr_numa_node.attr,
62981bb0e19SBrice Goglin #endif
6305136b2daSGreg Kroah-Hartman &dev_attr_dma_mask_bits.attr,
6315136b2daSGreg Kroah-Hartman &dev_attr_consistent_dma_mask_bits.attr,
632d8e7d53aSGreg Kroah-Hartman &dev_attr_enable.attr,
6335136b2daSGreg Kroah-Hartman &dev_attr_broken_parity_status.attr,
6345136b2daSGreg Kroah-Hartman &dev_attr_msi_bus.attr,
635fbb988beSRafael J. Wysocki #if defined(CONFIG_PM) && defined(CONFIG_ACPI)
6365136b2daSGreg Kroah-Hartman &dev_attr_d3cold_allowed.attr,
637448bd857SHuang Ying #endif
638dfc73e7aSSebastian Ott #ifdef CONFIG_OF
639dfc73e7aSSebastian Ott &dev_attr_devspec.attr,
640dfc73e7aSSebastian Ott #endif
641782a985dSAlex Williamson &dev_attr_driver_override.attr,
6420077a845SStuart Hayes &dev_attr_ari_enabled.attr,
6435136b2daSGreg Kroah-Hartman NULL,
6445136b2daSGreg Kroah-Hartman };
6455136b2daSGreg Kroah-Hartman
64656c1af46SWong Vee Khee static struct attribute *pci_bridge_attrs[] = {
64756c1af46SWong Vee Khee &dev_attr_subordinate_bus_number.attr,
64856c1af46SWong Vee Khee &dev_attr_secondary_bus_number.attr,
649*407476ebSKeith Busch &dev_attr_reset_subordinate.attr,
65056c1af46SWong Vee Khee NULL,
6515136b2daSGreg Kroah-Hartman };
6525136b2daSGreg Kroah-Hartman
65356c1af46SWong Vee Khee static struct attribute *pcie_dev_attrs[] = {
65456c1af46SWong Vee Khee &dev_attr_current_link_speed.attr,
65556c1af46SWong Vee Khee &dev_attr_current_link_width.attr,
65656c1af46SWong Vee Khee &dev_attr_max_link_width.attr,
65756c1af46SWong Vee Khee &dev_attr_max_link_speed.attr,
6585136b2daSGreg Kroah-Hartman NULL,
6591da177e4SLinus Torvalds };
6601da177e4SLinus Torvalds
66156039e65SGreg Kroah-Hartman static struct attribute *pcibus_attrs[] = {
6628bdfa145SKelsey Skunberg &dev_attr_bus_rescan.attr,
66356039e65SGreg Kroah-Hartman &dev_attr_cpuaffinity.attr,
66456039e65SGreg Kroah-Hartman &dev_attr_cpulistaffinity.attr,
66556039e65SGreg Kroah-Hartman NULL,
66656039e65SGreg Kroah-Hartman };
66756039e65SGreg Kroah-Hartman
66856039e65SGreg Kroah-Hartman static const struct attribute_group pcibus_group = {
66956039e65SGreg Kroah-Hartman .attrs = pcibus_attrs,
67056039e65SGreg Kroah-Hartman };
67156039e65SGreg Kroah-Hartman
67256039e65SGreg Kroah-Hartman const struct attribute_group *pcibus_groups[] = {
67356039e65SGreg Kroah-Hartman &pcibus_group,
67456039e65SGreg Kroah-Hartman NULL,
675b9d320fcSYinghai Lu };
676b9d320fcSYinghai Lu
boot_vga_show(struct device * dev,struct device_attribute * attr,char * buf)6773c78bc61SRyan Desfosses static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr,
6783c78bc61SRyan Desfosses char *buf)
679217f45deSDave Airlie {
680217f45deSDave Airlie struct pci_dev *pdev = to_pci_dev(dev);
6811a39b310SMatthew Garrett struct pci_dev *vga_dev = vga_default_device();
6821a39b310SMatthew Garrett
6831a39b310SMatthew Garrett if (vga_dev)
684ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%u\n", (pdev == vga_dev));
685217f45deSDave Airlie
686ad025f8eSKrzysztof Wilczyński return sysfs_emit(buf, "%u\n",
687217f45deSDave Airlie !!(pdev->resource[PCI_ROM_RESOURCE].flags &
688217f45deSDave Airlie IORESOURCE_ROM_SHADOW));
689217f45deSDave Airlie }
6908bdfa145SKelsey Skunberg static DEVICE_ATTR_RO(boot_vga);
691217f45deSDave Airlie
pci_read_config(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)6923c78bc61SRyan Desfosses static ssize_t pci_read_config(struct file *filp, struct kobject *kobj,
6933c78bc61SRyan Desfosses struct bin_attribute *bin_attr, char *buf,
6943c78bc61SRyan Desfosses loff_t off, size_t count)
6951da177e4SLinus Torvalds {
696554a6037SGeliang Tang struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
6971da177e4SLinus Torvalds unsigned int size = 64;
6981da177e4SLinus Torvalds loff_t init_off = off;
6994c0619adSssant@in.ibm.com u8 *data = (u8 *) buf;
7001da177e4SLinus Torvalds
7011da177e4SLinus Torvalds /* Several chips lock up trying to read undefined config space */
702ab0fa82bSLinus Torvalds if (file_ns_capable(filp, &init_user_ns, CAP_SYS_ADMIN))
7031da177e4SLinus Torvalds size = dev->cfg_size;
7043c78bc61SRyan Desfosses else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
7051da177e4SLinus Torvalds size = 128;
7061da177e4SLinus Torvalds
7071da177e4SLinus Torvalds if (off > size)
7081da177e4SLinus Torvalds return 0;
7091da177e4SLinus Torvalds if (off + count > size) {
7101da177e4SLinus Torvalds size -= off;
7111da177e4SLinus Torvalds count = size;
7121da177e4SLinus Torvalds } else {
7131da177e4SLinus Torvalds size = count;
7141da177e4SLinus Torvalds }
7151da177e4SLinus Torvalds
7163d8387efSHuang Ying pci_config_pm_runtime_get(dev);
7173d8387efSHuang Ying
7184c0619adSssant@in.ibm.com if ((off & 1) && size) {
7194c0619adSssant@in.ibm.com u8 val;
720e04b0ea2SBrian King pci_user_read_config_byte(dev, off, &val);
7214c0619adSssant@in.ibm.com data[off - init_off] = val;
7221da177e4SLinus Torvalds off++;
7234c0619adSssant@in.ibm.com size--;
7244c0619adSssant@in.ibm.com }
7254c0619adSssant@in.ibm.com
7264c0619adSssant@in.ibm.com if ((off & 3) && size > 2) {
7274c0619adSssant@in.ibm.com u16 val;
728e04b0ea2SBrian King pci_user_read_config_word(dev, off, &val);
7294c0619adSssant@in.ibm.com data[off - init_off] = val & 0xff;
7304c0619adSssant@in.ibm.com data[off - init_off + 1] = (val >> 8) & 0xff;
7314c0619adSssant@in.ibm.com off += 2;
7324c0619adSssant@in.ibm.com size -= 2;
7331da177e4SLinus Torvalds }
7341da177e4SLinus Torvalds
7351da177e4SLinus Torvalds while (size > 3) {
7364c0619adSssant@in.ibm.com u32 val;
737e04b0ea2SBrian King pci_user_read_config_dword(dev, off, &val);
7384c0619adSssant@in.ibm.com data[off - init_off] = val & 0xff;
7394c0619adSssant@in.ibm.com data[off - init_off + 1] = (val >> 8) & 0xff;
7404c0619adSssant@in.ibm.com data[off - init_off + 2] = (val >> 16) & 0xff;
7414c0619adSssant@in.ibm.com data[off - init_off + 3] = (val >> 24) & 0xff;
7421da177e4SLinus Torvalds off += 4;
7431da177e4SLinus Torvalds size -= 4;
7442ce02a86SJiang Biao cond_resched();
7451da177e4SLinus Torvalds }
7461da177e4SLinus Torvalds
7474c0619adSssant@in.ibm.com if (size >= 2) {
7484c0619adSssant@in.ibm.com u16 val;
749e04b0ea2SBrian King pci_user_read_config_word(dev, off, &val);
7504c0619adSssant@in.ibm.com data[off - init_off] = val & 0xff;
7514c0619adSssant@in.ibm.com data[off - init_off + 1] = (val >> 8) & 0xff;
7524c0619adSssant@in.ibm.com off += 2;
7534c0619adSssant@in.ibm.com size -= 2;
7544c0619adSssant@in.ibm.com }
7554c0619adSssant@in.ibm.com
7564c0619adSssant@in.ibm.com if (size > 0) {
7574c0619adSssant@in.ibm.com u8 val;
758e04b0ea2SBrian King pci_user_read_config_byte(dev, off, &val);
7594c0619adSssant@in.ibm.com data[off - init_off] = val;
7601da177e4SLinus Torvalds }
7611da177e4SLinus Torvalds
7623d8387efSHuang Ying pci_config_pm_runtime_put(dev);
7633d8387efSHuang Ying
7641da177e4SLinus Torvalds return count;
7651da177e4SLinus Torvalds }
7661da177e4SLinus Torvalds
pci_write_config(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)7673c78bc61SRyan Desfosses static ssize_t pci_write_config(struct file *filp, struct kobject *kobj,
7683c78bc61SRyan Desfosses struct bin_attribute *bin_attr, char *buf,
7693c78bc61SRyan Desfosses loff_t off, size_t count)
7701da177e4SLinus Torvalds {
771554a6037SGeliang Tang struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj));
7721da177e4SLinus Torvalds unsigned int size = count;
7731da177e4SLinus Torvalds loff_t init_off = off;
7744c0619adSssant@in.ibm.com u8 *data = (u8 *) buf;
775eb627e17SMatthew Garrett int ret;
776eb627e17SMatthew Garrett
777eb627e17SMatthew Garrett ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
778eb627e17SMatthew Garrett if (ret)
779eb627e17SMatthew Garrett return ret;
7801da177e4SLinus Torvalds
78127829479SIra Weiny if (resource_is_exclusive(&dev->driver_exclusive_resource, off,
78227829479SIra Weiny count)) {
78327829479SIra Weiny pci_warn_once(dev, "%s: Unexpected write to kernel-exclusive config offset %llx",
78427829479SIra Weiny current->comm, off);
78527829479SIra Weiny add_taint(TAINT_USER, LOCKDEP_STILL_OK);
78627829479SIra Weiny }
78727829479SIra Weiny
7881da177e4SLinus Torvalds if (off > dev->cfg_size)
7891da177e4SLinus Torvalds return 0;
7901da177e4SLinus Torvalds if (off + count > dev->cfg_size) {
7911da177e4SLinus Torvalds size = dev->cfg_size - off;
7921da177e4SLinus Torvalds count = size;
7931da177e4SLinus Torvalds }
7941da177e4SLinus Torvalds
7953d8387efSHuang Ying pci_config_pm_runtime_get(dev);
7963d8387efSHuang Ying
7974c0619adSssant@in.ibm.com if ((off & 1) && size) {
798e04b0ea2SBrian King pci_user_write_config_byte(dev, off, data[off - init_off]);
7991da177e4SLinus Torvalds off++;
8004c0619adSssant@in.ibm.com size--;
8014c0619adSssant@in.ibm.com }
8024c0619adSssant@in.ibm.com
8034c0619adSssant@in.ibm.com if ((off & 3) && size > 2) {
8044c0619adSssant@in.ibm.com u16 val = data[off - init_off];
8054c0619adSssant@in.ibm.com val |= (u16) data[off - init_off + 1] << 8;
806e04b0ea2SBrian King pci_user_write_config_word(dev, off, val);
8074c0619adSssant@in.ibm.com off += 2;
8084c0619adSssant@in.ibm.com size -= 2;
8091da177e4SLinus Torvalds }
8101da177e4SLinus Torvalds
8111da177e4SLinus Torvalds while (size > 3) {
8124c0619adSssant@in.ibm.com u32 val = data[off - init_off];
8134c0619adSssant@in.ibm.com val |= (u32) data[off - init_off + 1] << 8;
8144c0619adSssant@in.ibm.com val |= (u32) data[off - init_off + 2] << 16;
8154c0619adSssant@in.ibm.com val |= (u32) data[off - init_off + 3] << 24;
816e04b0ea2SBrian King pci_user_write_config_dword(dev, off, val);
8171da177e4SLinus Torvalds off += 4;
8181da177e4SLinus Torvalds size -= 4;
8191da177e4SLinus Torvalds }
8201da177e4SLinus Torvalds
8214c0619adSssant@in.ibm.com if (size >= 2) {
8224c0619adSssant@in.ibm.com u16 val = data[off - init_off];
8234c0619adSssant@in.ibm.com val |= (u16) data[off - init_off + 1] << 8;
824e04b0ea2SBrian King pci_user_write_config_word(dev, off, val);
8254c0619adSssant@in.ibm.com off += 2;
8264c0619adSssant@in.ibm.com size -= 2;
8274c0619adSssant@in.ibm.com }
8284c0619adSssant@in.ibm.com
829c50762a8SBjorn Helgaas if (size)
830e04b0ea2SBrian King pci_user_write_config_byte(dev, off, data[off - init_off]);
8311da177e4SLinus Torvalds
8323d8387efSHuang Ying pci_config_pm_runtime_put(dev);
8333d8387efSHuang Ying
8341da177e4SLinus Torvalds return count;
8351da177e4SLinus Torvalds }
836e1d3f326SKrzysztof Wilczyński static BIN_ATTR(config, 0644, pci_read_config, pci_write_config, 0);
837e1d3f326SKrzysztof Wilczyński
838e1d3f326SKrzysztof Wilczyński static struct bin_attribute *pci_dev_config_attrs[] = {
839e1d3f326SKrzysztof Wilczyński &bin_attr_config,
840e1d3f326SKrzysztof Wilczyński NULL,
841e1d3f326SKrzysztof Wilczyński };
842e1d3f326SKrzysztof Wilczyński
pci_dev_config_attr_is_visible(struct kobject * kobj,struct bin_attribute * a,int n)843e1d3f326SKrzysztof Wilczyński static umode_t pci_dev_config_attr_is_visible(struct kobject *kobj,
844e1d3f326SKrzysztof Wilczyński struct bin_attribute *a, int n)
845e1d3f326SKrzysztof Wilczyński {
846e1d3f326SKrzysztof Wilczyński struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
847e1d3f326SKrzysztof Wilczyński
848e1d3f326SKrzysztof Wilczyński a->size = PCI_CFG_SPACE_SIZE;
849e1d3f326SKrzysztof Wilczyński if (pdev->cfg_size > PCI_CFG_SPACE_SIZE)
850e1d3f326SKrzysztof Wilczyński a->size = PCI_CFG_SPACE_EXP_SIZE;
851e1d3f326SKrzysztof Wilczyński
852e1d3f326SKrzysztof Wilczyński return a->attr.mode;
853e1d3f326SKrzysztof Wilczyński }
854e1d3f326SKrzysztof Wilczyński
855e1d3f326SKrzysztof Wilczyński static const struct attribute_group pci_dev_config_attr_group = {
856e1d3f326SKrzysztof Wilczyński .bin_attrs = pci_dev_config_attrs,
857e1d3f326SKrzysztof Wilczyński .is_bin_visible = pci_dev_config_attr_is_visible,
858e1d3f326SKrzysztof Wilczyński };
8591da177e4SLinus Torvalds
8601da177e4SLinus Torvalds #ifdef HAVE_PCI_LEGACY
8611da177e4SLinus Torvalds /**
8621da177e4SLinus Torvalds * pci_read_legacy_io - read byte(s) from legacy I/O port space
8632c3c8beaSChris Wright * @filp: open sysfs file
8641da177e4SLinus Torvalds * @kobj: kobject corresponding to file to read from
865cffb2fafSRandy Dunlap * @bin_attr: struct bin_attribute for this file
8661da177e4SLinus Torvalds * @buf: buffer to store results
8671da177e4SLinus Torvalds * @off: offset into legacy I/O port space
8681da177e4SLinus Torvalds * @count: number of bytes to read
8691da177e4SLinus Torvalds *
8701da177e4SLinus Torvalds * Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific
8711da177e4SLinus Torvalds * callback routine (pci_legacy_read).
8721da177e4SLinus Torvalds */
pci_read_legacy_io(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)8733c78bc61SRyan Desfosses static ssize_t pci_read_legacy_io(struct file *filp, struct kobject *kobj,
8743c78bc61SRyan Desfosses struct bin_attribute *bin_attr, char *buf,
8753c78bc61SRyan Desfosses loff_t off, size_t count)
8761da177e4SLinus Torvalds {
877554a6037SGeliang Tang struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
8781da177e4SLinus Torvalds
8791da177e4SLinus Torvalds /* Only support 1, 2 or 4 byte accesses */
8801da177e4SLinus Torvalds if (count != 1 && count != 2 && count != 4)
8811da177e4SLinus Torvalds return -EINVAL;
8821da177e4SLinus Torvalds
8831da177e4SLinus Torvalds return pci_legacy_read(bus, off, (u32 *)buf, count);
8841da177e4SLinus Torvalds }
8851da177e4SLinus Torvalds
8861da177e4SLinus Torvalds /**
8871da177e4SLinus Torvalds * pci_write_legacy_io - write byte(s) to legacy I/O port space
8882c3c8beaSChris Wright * @filp: open sysfs file
8891da177e4SLinus Torvalds * @kobj: kobject corresponding to file to read from
890cffb2fafSRandy Dunlap * @bin_attr: struct bin_attribute for this file
8911da177e4SLinus Torvalds * @buf: buffer containing value to be written
8921da177e4SLinus Torvalds * @off: offset into legacy I/O port space
8931da177e4SLinus Torvalds * @count: number of bytes to write
8941da177e4SLinus Torvalds *
8951da177e4SLinus Torvalds * Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific
8961da177e4SLinus Torvalds * callback routine (pci_legacy_write).
8971da177e4SLinus Torvalds */
pci_write_legacy_io(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)8983c78bc61SRyan Desfosses static ssize_t pci_write_legacy_io(struct file *filp, struct kobject *kobj,
8993c78bc61SRyan Desfosses struct bin_attribute *bin_attr, char *buf,
9003c78bc61SRyan Desfosses loff_t off, size_t count)
9011da177e4SLinus Torvalds {
902554a6037SGeliang Tang struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
9033c78bc61SRyan Desfosses
9041da177e4SLinus Torvalds /* Only support 1, 2 or 4 byte accesses */
9051da177e4SLinus Torvalds if (count != 1 && count != 2 && count != 4)
9061da177e4SLinus Torvalds return -EINVAL;
9071da177e4SLinus Torvalds
9081da177e4SLinus Torvalds return pci_legacy_write(bus, off, *(u32 *)buf, count);
9091da177e4SLinus Torvalds }
9101da177e4SLinus Torvalds
9111da177e4SLinus Torvalds /**
9121da177e4SLinus Torvalds * pci_mmap_legacy_mem - map legacy PCI memory into user memory space
9132c3c8beaSChris Wright * @filp: open sysfs file
9141da177e4SLinus Torvalds * @kobj: kobject corresponding to device to be mapped
9151da177e4SLinus Torvalds * @attr: struct bin_attribute for this file
9161da177e4SLinus Torvalds * @vma: struct vm_area_struct passed to mmap
9171da177e4SLinus Torvalds *
918f19aeb1fSBenjamin Herrenschmidt * Uses an arch specific callback, pci_mmap_legacy_mem_page_range, to mmap
9191da177e4SLinus Torvalds * legacy memory space (first meg of bus space) into application virtual
9201da177e4SLinus Torvalds * memory space.
9211da177e4SLinus Torvalds */
pci_mmap_legacy_mem(struct file * filp,struct kobject * kobj,struct bin_attribute * attr,struct vm_area_struct * vma)9223c78bc61SRyan Desfosses static int pci_mmap_legacy_mem(struct file *filp, struct kobject *kobj,
9232c3c8beaSChris Wright struct bin_attribute *attr,
9241da177e4SLinus Torvalds struct vm_area_struct *vma)
9251da177e4SLinus Torvalds {
926554a6037SGeliang Tang struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
9271da177e4SLinus Torvalds
928f19aeb1fSBenjamin Herrenschmidt return pci_mmap_legacy_page_range(bus, vma, pci_mmap_mem);
929f19aeb1fSBenjamin Herrenschmidt }
930f19aeb1fSBenjamin Herrenschmidt
931f19aeb1fSBenjamin Herrenschmidt /**
932f19aeb1fSBenjamin Herrenschmidt * pci_mmap_legacy_io - map legacy PCI IO into user memory space
9332c3c8beaSChris Wright * @filp: open sysfs file
934f19aeb1fSBenjamin Herrenschmidt * @kobj: kobject corresponding to device to be mapped
935f19aeb1fSBenjamin Herrenschmidt * @attr: struct bin_attribute for this file
936f19aeb1fSBenjamin Herrenschmidt * @vma: struct vm_area_struct passed to mmap
937f19aeb1fSBenjamin Herrenschmidt *
938f19aeb1fSBenjamin Herrenschmidt * Uses an arch specific callback, pci_mmap_legacy_io_page_range, to mmap
939f19aeb1fSBenjamin Herrenschmidt * legacy IO space (first meg of bus space) into application virtual
940f19aeb1fSBenjamin Herrenschmidt * memory space. Returns -ENOSYS if the operation isn't supported
941f19aeb1fSBenjamin Herrenschmidt */
pci_mmap_legacy_io(struct file * filp,struct kobject * kobj,struct bin_attribute * attr,struct vm_area_struct * vma)9423c78bc61SRyan Desfosses static int pci_mmap_legacy_io(struct file *filp, struct kobject *kobj,
9432c3c8beaSChris Wright struct bin_attribute *attr,
944f19aeb1fSBenjamin Herrenschmidt struct vm_area_struct *vma)
945f19aeb1fSBenjamin Herrenschmidt {
946554a6037SGeliang Tang struct pci_bus *bus = to_pci_bus(kobj_to_dev(kobj));
947f19aeb1fSBenjamin Herrenschmidt
948f19aeb1fSBenjamin Herrenschmidt return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io);
949f19aeb1fSBenjamin Herrenschmidt }
950f19aeb1fSBenjamin Herrenschmidt
951f19aeb1fSBenjamin Herrenschmidt /**
95210a0ef39SIvan Kokshaysky * pci_adjust_legacy_attr - adjustment of legacy file attributes
95310a0ef39SIvan Kokshaysky * @b: bus to create files under
95410a0ef39SIvan Kokshaysky * @mmap_type: I/O port or memory
95510a0ef39SIvan Kokshaysky *
95610a0ef39SIvan Kokshaysky * Stub implementation. Can be overridden by arch if necessary.
95710a0ef39SIvan Kokshaysky */
pci_adjust_legacy_attr(struct pci_bus * b,enum pci_mmap_state mmap_type)9583c78bc61SRyan Desfosses void __weak pci_adjust_legacy_attr(struct pci_bus *b,
9593c78bc61SRyan Desfosses enum pci_mmap_state mmap_type)
96010a0ef39SIvan Kokshaysky {
96110a0ef39SIvan Kokshaysky }
96210a0ef39SIvan Kokshaysky
96310a0ef39SIvan Kokshaysky /**
964f19aeb1fSBenjamin Herrenschmidt * pci_create_legacy_files - create legacy I/O port and memory files
965f19aeb1fSBenjamin Herrenschmidt * @b: bus to create files under
966f19aeb1fSBenjamin Herrenschmidt *
967f19aeb1fSBenjamin Herrenschmidt * Some platforms allow access to legacy I/O port and ISA memory space on
968f19aeb1fSBenjamin Herrenschmidt * a per-bus basis. This routine creates the files and ties them into
969f19aeb1fSBenjamin Herrenschmidt * their associated read, write and mmap files from pci-sysfs.c
970f19aeb1fSBenjamin Herrenschmidt *
97125985edcSLucas De Marchi * On error unwind, but don't propagate the error to the caller
972f19aeb1fSBenjamin Herrenschmidt * as it is ok to set up the PCI bus without these files.
973f19aeb1fSBenjamin Herrenschmidt */
pci_create_legacy_files(struct pci_bus * b)974f19aeb1fSBenjamin Herrenschmidt void pci_create_legacy_files(struct pci_bus *b)
975f19aeb1fSBenjamin Herrenschmidt {
976f19aeb1fSBenjamin Herrenschmidt int error;
977f19aeb1fSBenjamin Herrenschmidt
978efd532a6SDaniel Vetter if (!sysfs_initialized)
979efd532a6SDaniel Vetter return;
980efd532a6SDaniel Vetter
9816396bb22SKees Cook b->legacy_io = kcalloc(2, sizeof(struct bin_attribute),
982f19aeb1fSBenjamin Herrenschmidt GFP_ATOMIC);
983f19aeb1fSBenjamin Herrenschmidt if (!b->legacy_io)
984f19aeb1fSBenjamin Herrenschmidt goto kzalloc_err;
985f19aeb1fSBenjamin Herrenschmidt
98662e877b8SStephen Rothwell sysfs_bin_attr_init(b->legacy_io);
987f19aeb1fSBenjamin Herrenschmidt b->legacy_io->attr.name = "legacy_io";
988f19aeb1fSBenjamin Herrenschmidt b->legacy_io->size = 0xffff;
989e2154044SKelsey Skunberg b->legacy_io->attr.mode = 0600;
990f19aeb1fSBenjamin Herrenschmidt b->legacy_io->read = pci_read_legacy_io;
991f19aeb1fSBenjamin Herrenschmidt b->legacy_io->write = pci_write_legacy_io;
992f19aeb1fSBenjamin Herrenschmidt b->legacy_io->mmap = pci_mmap_legacy_io;
993f06aff92SKrzysztof Wilczyński b->legacy_io->f_mapping = iomem_get_mapping;
99410a0ef39SIvan Kokshaysky pci_adjust_legacy_attr(b, pci_mmap_io);
995f19aeb1fSBenjamin Herrenschmidt error = device_create_bin_file(&b->dev, b->legacy_io);
996f19aeb1fSBenjamin Herrenschmidt if (error)
997f19aeb1fSBenjamin Herrenschmidt goto legacy_io_err;
998f19aeb1fSBenjamin Herrenschmidt
999f19aeb1fSBenjamin Herrenschmidt /* Allocated above after the legacy_io struct */
1000f19aeb1fSBenjamin Herrenschmidt b->legacy_mem = b->legacy_io + 1;
10016757eca3SMel Gorman sysfs_bin_attr_init(b->legacy_mem);
1002f19aeb1fSBenjamin Herrenschmidt b->legacy_mem->attr.name = "legacy_mem";
1003f19aeb1fSBenjamin Herrenschmidt b->legacy_mem->size = 1024*1024;
1004e2154044SKelsey Skunberg b->legacy_mem->attr.mode = 0600;
1005f19aeb1fSBenjamin Herrenschmidt b->legacy_mem->mmap = pci_mmap_legacy_mem;
1006c6c3c570SLinus Torvalds b->legacy_mem->f_mapping = iomem_get_mapping;
100710a0ef39SIvan Kokshaysky pci_adjust_legacy_attr(b, pci_mmap_mem);
1008f19aeb1fSBenjamin Herrenschmidt error = device_create_bin_file(&b->dev, b->legacy_mem);
1009f19aeb1fSBenjamin Herrenschmidt if (error)
1010f19aeb1fSBenjamin Herrenschmidt goto legacy_mem_err;
1011f19aeb1fSBenjamin Herrenschmidt
1012f19aeb1fSBenjamin Herrenschmidt return;
1013f19aeb1fSBenjamin Herrenschmidt
1014f19aeb1fSBenjamin Herrenschmidt legacy_mem_err:
1015f19aeb1fSBenjamin Herrenschmidt device_remove_bin_file(&b->dev, b->legacy_io);
1016f19aeb1fSBenjamin Herrenschmidt legacy_io_err:
1017f19aeb1fSBenjamin Herrenschmidt kfree(b->legacy_io);
1018f19aeb1fSBenjamin Herrenschmidt b->legacy_io = NULL;
1019f19aeb1fSBenjamin Herrenschmidt kzalloc_err:
10207db4af43SBjorn Helgaas dev_warn(&b->dev, "could not create legacy I/O port and ISA memory resources in sysfs\n");
1021f19aeb1fSBenjamin Herrenschmidt }
1022f19aeb1fSBenjamin Herrenschmidt
pci_remove_legacy_files(struct pci_bus * b)1023f19aeb1fSBenjamin Herrenschmidt void pci_remove_legacy_files(struct pci_bus *b)
1024f19aeb1fSBenjamin Herrenschmidt {
1025f19aeb1fSBenjamin Herrenschmidt if (b->legacy_io) {
1026f19aeb1fSBenjamin Herrenschmidt device_remove_bin_file(&b->dev, b->legacy_io);
1027f19aeb1fSBenjamin Herrenschmidt device_remove_bin_file(&b->dev, b->legacy_mem);
1028f19aeb1fSBenjamin Herrenschmidt kfree(b->legacy_io); /* both are allocated here */
1029f19aeb1fSBenjamin Herrenschmidt }
10301da177e4SLinus Torvalds }
10311da177e4SLinus Torvalds #endif /* HAVE_PCI_LEGACY */
10321da177e4SLinus Torvalds
1033f7195824SDavid Woodhouse #if defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)
1034b5ff7df3SLinus Torvalds
pci_mmap_fits(struct pci_dev * pdev,int resno,struct vm_area_struct * vma,enum pci_mmap_api mmap_api)10353b519e4eSMartin Wilck int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma,
10363b519e4eSMartin Wilck enum pci_mmap_api mmap_api)
1037b5ff7df3SLinus Torvalds {
10386bccc7f4SDavid Woodhouse unsigned long nr, start, size;
10396bccc7f4SDavid Woodhouse resource_size_t pci_start = 0, pci_end;
1040b5ff7df3SLinus Torvalds
10413b519e4eSMartin Wilck if (pci_resource_len(pdev, resno) == 0)
10423b519e4eSMartin Wilck return 0;
104364b00175SLibin nr = vma_pages(vma);
1044b5ff7df3SLinus Torvalds start = vma->vm_pgoff;
104588e7df0bSEd Swierk size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1;
10466bccc7f4SDavid Woodhouse if (mmap_api == PCI_MMAP_PROCFS) {
10476bccc7f4SDavid Woodhouse pci_resource_to_user(pdev, resno, &pdev->resource[resno],
10486bccc7f4SDavid Woodhouse &pci_start, &pci_end);
10496bccc7f4SDavid Woodhouse pci_start >>= PAGE_SHIFT;
10506bccc7f4SDavid Woodhouse }
10513b519e4eSMartin Wilck if (start >= pci_start && start < pci_start + size &&
10523b519e4eSMartin Wilck start + nr <= pci_start + size)
1053b5ff7df3SLinus Torvalds return 1;
1054b5ff7df3SLinus Torvalds return 0;
1055b5ff7df3SLinus Torvalds }
1056b5ff7df3SLinus Torvalds
10571da177e4SLinus Torvalds /**
10581da177e4SLinus Torvalds * pci_mmap_resource - map a PCI resource into user memory space
10591da177e4SLinus Torvalds * @kobj: kobject for mapping
10601da177e4SLinus Torvalds * @attr: struct bin_attribute for the file being mapped
10611da177e4SLinus Torvalds * @vma: struct vm_area_struct passed into the mmap
106245aec1aeSvenkatesh.pallipadi@intel.com * @write_combine: 1 for write_combine mapping
10631da177e4SLinus Torvalds *
10641da177e4SLinus Torvalds * Use the regular PCI mapping routines to map a PCI resource into userspace.
10651da177e4SLinus Torvalds */
pci_mmap_resource(struct kobject * kobj,struct bin_attribute * attr,struct vm_area_struct * vma,int write_combine)10663c78bc61SRyan Desfosses static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
106745aec1aeSvenkatesh.pallipadi@intel.com struct vm_area_struct *vma, int write_combine)
10681da177e4SLinus Torvalds {
1069554a6037SGeliang Tang struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
1070dca40b18SDavid Woodhouse int bar = (unsigned long)attr->private;
10711da177e4SLinus Torvalds enum pci_mmap_state mmap_type;
1072dca40b18SDavid Woodhouse struct resource *res = &pdev->resource[bar];
1073eb627e17SMatthew Garrett int ret;
1074eb627e17SMatthew Garrett
1075eb627e17SMatthew Garrett ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
1076eb627e17SMatthew Garrett if (ret)
1077eb627e17SMatthew Garrett return ret;
10782311b1f2SMichael Ellerman
1079ca620723SBjorn Helgaas if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
1080ca620723SBjorn Helgaas return -EINVAL;
1081ca620723SBjorn Helgaas
10827a094909SBjorn Helgaas if (!pci_mmap_fits(pdev, bar, vma, PCI_MMAP_SYSFS))
1083b5ff7df3SLinus Torvalds return -EINVAL;
10847a094909SBjorn Helgaas
10851da177e4SLinus Torvalds mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
1086f7195824SDavid Woodhouse
1087f7195824SDavid Woodhouse return pci_mmap_resource_range(pdev, bar, vma, mmap_type, write_combine);
108845aec1aeSvenkatesh.pallipadi@intel.com }
108945aec1aeSvenkatesh.pallipadi@intel.com
pci_mmap_resource_uc(struct file * filp,struct kobject * kobj,struct bin_attribute * attr,struct vm_area_struct * vma)10903c78bc61SRyan Desfosses static int pci_mmap_resource_uc(struct file *filp, struct kobject *kobj,
10912c3c8beaSChris Wright struct bin_attribute *attr,
109245aec1aeSvenkatesh.pallipadi@intel.com struct vm_area_struct *vma)
109345aec1aeSvenkatesh.pallipadi@intel.com {
109445aec1aeSvenkatesh.pallipadi@intel.com return pci_mmap_resource(kobj, attr, vma, 0);
109545aec1aeSvenkatesh.pallipadi@intel.com }
109645aec1aeSvenkatesh.pallipadi@intel.com
pci_mmap_resource_wc(struct file * filp,struct kobject * kobj,struct bin_attribute * attr,struct vm_area_struct * vma)10973c78bc61SRyan Desfosses static int pci_mmap_resource_wc(struct file *filp, struct kobject *kobj,
10982c3c8beaSChris Wright struct bin_attribute *attr,
109945aec1aeSvenkatesh.pallipadi@intel.com struct vm_area_struct *vma)
110045aec1aeSvenkatesh.pallipadi@intel.com {
110145aec1aeSvenkatesh.pallipadi@intel.com return pci_mmap_resource(kobj, attr, vma, 1);
11021da177e4SLinus Torvalds }
11031da177e4SLinus Torvalds
pci_resource_io(struct file * filp,struct kobject * kobj,struct bin_attribute * attr,char * buf,loff_t off,size_t count,bool write)11043c78bc61SRyan Desfosses static ssize_t pci_resource_io(struct file *filp, struct kobject *kobj,
11058633328bSAlex Williamson struct bin_attribute *attr, char *buf,
11068633328bSAlex Williamson loff_t off, size_t count, bool write)
11078633328bSAlex Williamson {
11085da1b588SNiklas Schnelle #ifdef CONFIG_HAS_IOPORT
1109554a6037SGeliang Tang struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
1110dca40b18SDavid Woodhouse int bar = (unsigned long)attr->private;
11118633328bSAlex Williamson unsigned long port = off;
11128633328bSAlex Williamson
1113dca40b18SDavid Woodhouse port += pci_resource_start(pdev, bar);
11148633328bSAlex Williamson
1115dca40b18SDavid Woodhouse if (port > pci_resource_end(pdev, bar))
11168633328bSAlex Williamson return 0;
11178633328bSAlex Williamson
1118dca40b18SDavid Woodhouse if (port + count - 1 > pci_resource_end(pdev, bar))
11198633328bSAlex Williamson return -EINVAL;
11208633328bSAlex Williamson
11218633328bSAlex Williamson switch (count) {
11228633328bSAlex Williamson case 1:
11238633328bSAlex Williamson if (write)
11248633328bSAlex Williamson outb(*(u8 *)buf, port);
11258633328bSAlex Williamson else
11268633328bSAlex Williamson *(u8 *)buf = inb(port);
11278633328bSAlex Williamson return 1;
11288633328bSAlex Williamson case 2:
11298633328bSAlex Williamson if (write)
11308633328bSAlex Williamson outw(*(u16 *)buf, port);
11318633328bSAlex Williamson else
11328633328bSAlex Williamson *(u16 *)buf = inw(port);
11338633328bSAlex Williamson return 2;
11348633328bSAlex Williamson case 4:
11358633328bSAlex Williamson if (write)
11368633328bSAlex Williamson outl(*(u32 *)buf, port);
11378633328bSAlex Williamson else
11388633328bSAlex Williamson *(u32 *)buf = inl(port);
11398633328bSAlex Williamson return 4;
11408633328bSAlex Williamson }
11418633328bSAlex Williamson return -EINVAL;
11425da1b588SNiklas Schnelle #else
11435da1b588SNiklas Schnelle return -ENXIO;
11445da1b588SNiklas Schnelle #endif
11458633328bSAlex Williamson }
11468633328bSAlex Williamson
pci_read_resource_io(struct file * filp,struct kobject * kobj,struct bin_attribute * attr,char * buf,loff_t off,size_t count)11473c78bc61SRyan Desfosses static ssize_t pci_read_resource_io(struct file *filp, struct kobject *kobj,
11488633328bSAlex Williamson struct bin_attribute *attr, char *buf,
11498633328bSAlex Williamson loff_t off, size_t count)
11508633328bSAlex Williamson {
11518633328bSAlex Williamson return pci_resource_io(filp, kobj, attr, buf, off, count, false);
11528633328bSAlex Williamson }
11538633328bSAlex Williamson
pci_write_resource_io(struct file * filp,struct kobject * kobj,struct bin_attribute * attr,char * buf,loff_t off,size_t count)11543c78bc61SRyan Desfosses static ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj,
11558633328bSAlex Williamson struct bin_attribute *attr, char *buf,
11568633328bSAlex Williamson loff_t off, size_t count)
11578633328bSAlex Williamson {
1158eb627e17SMatthew Garrett int ret;
1159eb627e17SMatthew Garrett
1160eb627e17SMatthew Garrett ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
1161eb627e17SMatthew Garrett if (ret)
1162eb627e17SMatthew Garrett return ret;
1163eb627e17SMatthew Garrett
11648633328bSAlex Williamson return pci_resource_io(filp, kobj, attr, buf, off, count, true);
11658633328bSAlex Williamson }
11668633328bSAlex Williamson
11671da177e4SLinus Torvalds /**
1168b19441afSGreg Kroah-Hartman * pci_remove_resource_files - cleanup resource files
1169cffb2fafSRandy Dunlap * @pdev: dev to cleanup
1170b19441afSGreg Kroah-Hartman *
1171cffb2fafSRandy Dunlap * If we created resource files for @pdev, remove them from sysfs and
1172b19441afSGreg Kroah-Hartman * free their resources.
1173b19441afSGreg Kroah-Hartman */
pci_remove_resource_files(struct pci_dev * pdev)11743c78bc61SRyan Desfosses static void pci_remove_resource_files(struct pci_dev *pdev)
1175b19441afSGreg Kroah-Hartman {
1176b19441afSGreg Kroah-Hartman int i;
1177b19441afSGreg Kroah-Hartman
1178c9c13ba4SDenis Efremov for (i = 0; i < PCI_STD_NUM_BARS; i++) {
1179b19441afSGreg Kroah-Hartman struct bin_attribute *res_attr;
1180b19441afSGreg Kroah-Hartman
1181b19441afSGreg Kroah-Hartman res_attr = pdev->res_attr[i];
1182b19441afSGreg Kroah-Hartman if (res_attr) {
1183b19441afSGreg Kroah-Hartman sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
1184b19441afSGreg Kroah-Hartman kfree(res_attr);
1185b19441afSGreg Kroah-Hartman }
118645aec1aeSvenkatesh.pallipadi@intel.com
118745aec1aeSvenkatesh.pallipadi@intel.com res_attr = pdev->res_attr_wc[i];
118845aec1aeSvenkatesh.pallipadi@intel.com if (res_attr) {
118945aec1aeSvenkatesh.pallipadi@intel.com sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
119045aec1aeSvenkatesh.pallipadi@intel.com kfree(res_attr);
1191b19441afSGreg Kroah-Hartman }
1192b19441afSGreg Kroah-Hartman }
119345aec1aeSvenkatesh.pallipadi@intel.com }
119445aec1aeSvenkatesh.pallipadi@intel.com
pci_create_attr(struct pci_dev * pdev,int num,int write_combine)119545aec1aeSvenkatesh.pallipadi@intel.com static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
119645aec1aeSvenkatesh.pallipadi@intel.com {
119745aec1aeSvenkatesh.pallipadi@intel.com /* allocate attribute structure, piggyback attribute name */
119845aec1aeSvenkatesh.pallipadi@intel.com int name_len = write_combine ? 13 : 10;
119945aec1aeSvenkatesh.pallipadi@intel.com struct bin_attribute *res_attr;
1200bd5174dfSBjorn Helgaas char *res_attr_name;
120145aec1aeSvenkatesh.pallipadi@intel.com int retval;
120245aec1aeSvenkatesh.pallipadi@intel.com
120345aec1aeSvenkatesh.pallipadi@intel.com res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC);
1204bd5174dfSBjorn Helgaas if (!res_attr)
1205bd5174dfSBjorn Helgaas return -ENOMEM;
1206bd5174dfSBjorn Helgaas
1207bd5174dfSBjorn Helgaas res_attr_name = (char *)(res_attr + 1);
120845aec1aeSvenkatesh.pallipadi@intel.com
1209a07e4156SEric W. Biederman sysfs_bin_attr_init(res_attr);
121045aec1aeSvenkatesh.pallipadi@intel.com if (write_combine) {
121145aec1aeSvenkatesh.pallipadi@intel.com sprintf(res_attr_name, "resource%d_wc", num);
121245aec1aeSvenkatesh.pallipadi@intel.com res_attr->mmap = pci_mmap_resource_wc;
121345aec1aeSvenkatesh.pallipadi@intel.com } else {
121445aec1aeSvenkatesh.pallipadi@intel.com sprintf(res_attr_name, "resource%d", num);
12158633328bSAlex Williamson if (pci_resource_flags(pdev, num) & IORESOURCE_IO) {
12168633328bSAlex Williamson res_attr->read = pci_read_resource_io;
12178633328bSAlex Williamson res_attr->write = pci_write_resource_io;
1218e854d8b2SDavid Woodhouse if (arch_can_pci_mmap_io())
1219e854d8b2SDavid Woodhouse res_attr->mmap = pci_mmap_resource_uc;
1220e854d8b2SDavid Woodhouse } else {
1221e854d8b2SDavid Woodhouse res_attr->mmap = pci_mmap_resource_uc;
1222e854d8b2SDavid Woodhouse }
12238633328bSAlex Williamson }
1224636b21b5SDaniel Vetter if (res_attr->mmap)
1225f06aff92SKrzysztof Wilczyński res_attr->f_mapping = iomem_get_mapping;
122645aec1aeSvenkatesh.pallipadi@intel.com res_attr->attr.name = res_attr_name;
1227e2154044SKelsey Skunberg res_attr->attr.mode = 0600;
122845aec1aeSvenkatesh.pallipadi@intel.com res_attr->size = pci_resource_len(pdev, num);
1229dca40b18SDavid Woodhouse res_attr->private = (void *)(unsigned long)num;
123045aec1aeSvenkatesh.pallipadi@intel.com retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
1231aa382ffaSSascha Hauer if (retval) {
1232b562ec8fSBjorn Helgaas kfree(res_attr);
123345aec1aeSvenkatesh.pallipadi@intel.com return retval;
123445aec1aeSvenkatesh.pallipadi@intel.com }
1235b19441afSGreg Kroah-Hartman
1236aa382ffaSSascha Hauer if (write_combine)
1237aa382ffaSSascha Hauer pdev->res_attr_wc[num] = res_attr;
1238aa382ffaSSascha Hauer else
1239aa382ffaSSascha Hauer pdev->res_attr[num] = res_attr;
1240aa382ffaSSascha Hauer
1241aa382ffaSSascha Hauer return 0;
1242aa382ffaSSascha Hauer }
1243aa382ffaSSascha Hauer
1244b19441afSGreg Kroah-Hartman /**
12451da177e4SLinus Torvalds * pci_create_resource_files - create resource files in sysfs for @dev
1246cffb2fafSRandy Dunlap * @pdev: dev in question
12471da177e4SLinus Torvalds *
1248cffb2fafSRandy Dunlap * Walk the resources in @pdev creating files for each resource available.
12491da177e4SLinus Torvalds */
pci_create_resource_files(struct pci_dev * pdev)1250b19441afSGreg Kroah-Hartman static int pci_create_resource_files(struct pci_dev *pdev)
12511da177e4SLinus Torvalds {
12521da177e4SLinus Torvalds int i;
1253b19441afSGreg Kroah-Hartman int retval;
12541da177e4SLinus Torvalds
12551da177e4SLinus Torvalds /* Expose the PCI resources from this device as files */
1256c9c13ba4SDenis Efremov for (i = 0; i < PCI_STD_NUM_BARS; i++) {
12571da177e4SLinus Torvalds
12581da177e4SLinus Torvalds /* skip empty resources */
12591da177e4SLinus Torvalds if (!pci_resource_len(pdev, i))
12601da177e4SLinus Torvalds continue;
12611da177e4SLinus Torvalds
126245aec1aeSvenkatesh.pallipadi@intel.com retval = pci_create_attr(pdev, i, 0);
126345aec1aeSvenkatesh.pallipadi@intel.com /* for prefetchable resources, create a WC mappable file */
1264ae749c7aSDavid Woodhouse if (!retval && arch_can_pci_mmap_wc() &&
1265ae749c7aSDavid Woodhouse pdev->resource[i].flags & IORESOURCE_PREFETCH)
126645aec1aeSvenkatesh.pallipadi@intel.com retval = pci_create_attr(pdev, i, 1);
1267b19441afSGreg Kroah-Hartman if (retval) {
1268b19441afSGreg Kroah-Hartman pci_remove_resource_files(pdev);
1269b19441afSGreg Kroah-Hartman return retval;
1270b19441afSGreg Kroah-Hartman }
12711da177e4SLinus Torvalds }
1272b19441afSGreg Kroah-Hartman return 0;
12731da177e4SLinus Torvalds }
12748c46d543SClint Sbisa #else /* !(defined(HAVE_PCI_MMAP) || defined(ARCH_GENERIC_PCI_MMAP_RESOURCE)) */
pci_create_resource_files(struct pci_dev * dev)127510a0ef39SIvan Kokshaysky int __weak pci_create_resource_files(struct pci_dev *dev) { return 0; }
pci_remove_resource_files(struct pci_dev * dev)127610a0ef39SIvan Kokshaysky void __weak pci_remove_resource_files(struct pci_dev *dev) { return; }
12778c46d543SClint Sbisa #endif
12781da177e4SLinus Torvalds
12791da177e4SLinus Torvalds /**
12801da177e4SLinus Torvalds * pci_write_rom - used to enable access to the PCI ROM display
12812c3c8beaSChris Wright * @filp: sysfs file
12821da177e4SLinus Torvalds * @kobj: kernel object handle
1283cffb2fafSRandy Dunlap * @bin_attr: struct bin_attribute for this file
12841da177e4SLinus Torvalds * @buf: user input
12851da177e4SLinus Torvalds * @off: file offset
12861da177e4SLinus Torvalds * @count: number of byte in input
12871da177e4SLinus Torvalds *
12881da177e4SLinus Torvalds * writing anything except 0 enables it
12891da177e4SLinus Torvalds */
pci_write_rom(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)12903c78bc61SRyan Desfosses static ssize_t pci_write_rom(struct file *filp, struct kobject *kobj,
12913c78bc61SRyan Desfosses struct bin_attribute *bin_attr, char *buf,
12923c78bc61SRyan Desfosses loff_t off, size_t count)
12931da177e4SLinus Torvalds {
1294554a6037SGeliang Tang struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
12951da177e4SLinus Torvalds
12961da177e4SLinus Torvalds if ((off == 0) && (*buf == '0') && (count == 2))
12971da177e4SLinus Torvalds pdev->rom_attr_enabled = 0;
12981da177e4SLinus Torvalds else
12991da177e4SLinus Torvalds pdev->rom_attr_enabled = 1;
13001da177e4SLinus Torvalds
13011da177e4SLinus Torvalds return count;
13021da177e4SLinus Torvalds }
13031da177e4SLinus Torvalds
13041da177e4SLinus Torvalds /**
13051da177e4SLinus Torvalds * pci_read_rom - read a PCI ROM
13062c3c8beaSChris Wright * @filp: sysfs file
13071da177e4SLinus Torvalds * @kobj: kernel object handle
1308cffb2fafSRandy Dunlap * @bin_attr: struct bin_attribute for this file
13091da177e4SLinus Torvalds * @buf: where to put the data we read from the ROM
13101da177e4SLinus Torvalds * @off: file offset
13111da177e4SLinus Torvalds * @count: number of bytes to read
13121da177e4SLinus Torvalds *
13131da177e4SLinus Torvalds * Put @count bytes starting at @off into @buf from the ROM in the PCI
13141da177e4SLinus Torvalds * device corresponding to @kobj.
13151da177e4SLinus Torvalds */
pci_read_rom(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)13163c78bc61SRyan Desfosses static ssize_t pci_read_rom(struct file *filp, struct kobject *kobj,
13173c78bc61SRyan Desfosses struct bin_attribute *bin_attr, char *buf,
13183c78bc61SRyan Desfosses loff_t off, size_t count)
13191da177e4SLinus Torvalds {
1320554a6037SGeliang Tang struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
13211da177e4SLinus Torvalds void __iomem *rom;
13221da177e4SLinus Torvalds size_t size;
13231da177e4SLinus Torvalds
13241da177e4SLinus Torvalds if (!pdev->rom_attr_enabled)
13251da177e4SLinus Torvalds return -EINVAL;
13261da177e4SLinus Torvalds
13271da177e4SLinus Torvalds rom = pci_map_rom(pdev, &size); /* size starts out as PCI window size */
132897c44836STimothy S. Nelson if (!rom || !size)
132997c44836STimothy S. Nelson return -EIO;
13301da177e4SLinus Torvalds
13311da177e4SLinus Torvalds if (off >= size)
13321da177e4SLinus Torvalds count = 0;
13331da177e4SLinus Torvalds else {
13341da177e4SLinus Torvalds if (off + count > size)
13351da177e4SLinus Torvalds count = size - off;
13361da177e4SLinus Torvalds
13371da177e4SLinus Torvalds memcpy_fromio(buf, rom + off, count);
13381da177e4SLinus Torvalds }
13391da177e4SLinus Torvalds pci_unmap_rom(pdev, rom);
13401da177e4SLinus Torvalds
13411da177e4SLinus Torvalds return count;
13421da177e4SLinus Torvalds }
1343527139d7SKrzysztof Wilczyński static BIN_ATTR(rom, 0600, pci_read_rom, pci_write_rom, 0);
13441da177e4SLinus Torvalds
1345527139d7SKrzysztof Wilczyński static struct bin_attribute *pci_dev_rom_attrs[] = {
1346527139d7SKrzysztof Wilczyński &bin_attr_rom,
1347527139d7SKrzysztof Wilczyński NULL,
13481da177e4SLinus Torvalds };
13491da177e4SLinus Torvalds
pci_dev_rom_attr_is_visible(struct kobject * kobj,struct bin_attribute * a,int n)1350527139d7SKrzysztof Wilczyński static umode_t pci_dev_rom_attr_is_visible(struct kobject *kobj,
1351527139d7SKrzysztof Wilczyński struct bin_attribute *a, int n)
1352527139d7SKrzysztof Wilczyński {
1353527139d7SKrzysztof Wilczyński struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
1354527139d7SKrzysztof Wilczyński size_t rom_size;
1355527139d7SKrzysztof Wilczyński
1356527139d7SKrzysztof Wilczyński /* If the device has a ROM, try to expose it in sysfs. */
1357527139d7SKrzysztof Wilczyński rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
1358527139d7SKrzysztof Wilczyński if (!rom_size)
1359527139d7SKrzysztof Wilczyński return 0;
1360527139d7SKrzysztof Wilczyński
1361527139d7SKrzysztof Wilczyński a->size = rom_size;
1362527139d7SKrzysztof Wilczyński
1363527139d7SKrzysztof Wilczyński return a->attr.mode;
1364527139d7SKrzysztof Wilczyński }
1365527139d7SKrzysztof Wilczyński
1366527139d7SKrzysztof Wilczyński static const struct attribute_group pci_dev_rom_attr_group = {
1367527139d7SKrzysztof Wilczyński .bin_attrs = pci_dev_rom_attrs,
1368527139d7SKrzysztof Wilczyński .is_bin_visible = pci_dev_rom_attr_is_visible,
13691da177e4SLinus Torvalds };
13701da177e4SLinus Torvalds
reset_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)13713c78bc61SRyan Desfosses static ssize_t reset_store(struct device *dev, struct device_attribute *attr,
13723c78bc61SRyan Desfosses const char *buf, size_t count)
1373711d5779SMichael S. Tsirkin {
1374711d5779SMichael S. Tsirkin struct pci_dev *pdev = to_pci_dev(dev);
1375711d5779SMichael S. Tsirkin unsigned long val;
137636f354ecSKrzysztof Wilczyński ssize_t result;
1377711d5779SMichael S. Tsirkin
137836f354ecSKrzysztof Wilczyński if (kstrtoul(buf, 0, &val) < 0)
137936f354ecSKrzysztof Wilczyński return -EINVAL;
1380711d5779SMichael S. Tsirkin
1381711d5779SMichael S. Tsirkin if (val != 1)
1382711d5779SMichael S. Tsirkin return -EINVAL;
1383447c5dd7SMichal Schmidt
138482c3fbffSLukas Wunner pm_runtime_get_sync(dev);
1385447c5dd7SMichal Schmidt result = pci_reset_function(pdev);
138682c3fbffSLukas Wunner pm_runtime_put(dev);
1387447c5dd7SMichal Schmidt if (result < 0)
1388447c5dd7SMichal Schmidt return result;
1389447c5dd7SMichal Schmidt
1390447c5dd7SMichal Schmidt return count;
1391711d5779SMichael S. Tsirkin }
1392f42c35eaSKrzysztof Wilczyński static DEVICE_ATTR_WO(reset);
1393711d5779SMichael S. Tsirkin
1394f42c35eaSKrzysztof Wilczyński static struct attribute *pci_dev_reset_attrs[] = {
1395f42c35eaSKrzysztof Wilczyński &dev_attr_reset.attr,
1396f42c35eaSKrzysztof Wilczyński NULL,
1397f42c35eaSKrzysztof Wilczyński };
1398711d5779SMichael S. Tsirkin
pci_dev_reset_attr_is_visible(struct kobject * kobj,struct attribute * a,int n)1399f42c35eaSKrzysztof Wilczyński static umode_t pci_dev_reset_attr_is_visible(struct kobject *kobj,
1400f42c35eaSKrzysztof Wilczyński struct attribute *a, int n)
1401280c73d3SZhao, Yu {
1402f42c35eaSKrzysztof Wilczyński struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
1403280c73d3SZhao, Yu
14044ec36dfeSAmey Narkhede if (!pci_reset_supported(pdev))
1405280c73d3SZhao, Yu return 0;
1406711d5779SMichael S. Tsirkin
1407f42c35eaSKrzysztof Wilczyński return a->mode;
1408280c73d3SZhao, Yu }
1409280c73d3SZhao, Yu
1410f42c35eaSKrzysztof Wilczyński static const struct attribute_group pci_dev_reset_attr_group = {
1411f42c35eaSKrzysztof Wilczyński .attrs = pci_dev_reset_attrs,
1412f42c35eaSKrzysztof Wilczyński .is_visible = pci_dev_reset_attr_is_visible,
1413f42c35eaSKrzysztof Wilczyński };
1414f42c35eaSKrzysztof Wilczyński
141591fa1277SAlex Williamson #define pci_dev_resource_resize_attr(n) \
141691fa1277SAlex Williamson static ssize_t resource##n##_resize_show(struct device *dev, \
141791fa1277SAlex Williamson struct device_attribute *attr, \
141891fa1277SAlex Williamson char * buf) \
141991fa1277SAlex Williamson { \
142091fa1277SAlex Williamson struct pci_dev *pdev = to_pci_dev(dev); \
142191fa1277SAlex Williamson ssize_t ret; \
142291fa1277SAlex Williamson \
142391fa1277SAlex Williamson pci_config_pm_runtime_get(pdev); \
142491fa1277SAlex Williamson \
142591fa1277SAlex Williamson ret = sysfs_emit(buf, "%016llx\n", \
142691fa1277SAlex Williamson (u64)pci_rebar_get_possible_sizes(pdev, n)); \
142791fa1277SAlex Williamson \
142891fa1277SAlex Williamson pci_config_pm_runtime_put(pdev); \
142991fa1277SAlex Williamson \
143091fa1277SAlex Williamson return ret; \
143191fa1277SAlex Williamson } \
143291fa1277SAlex Williamson \
143391fa1277SAlex Williamson static ssize_t resource##n##_resize_store(struct device *dev, \
143491fa1277SAlex Williamson struct device_attribute *attr,\
143591fa1277SAlex Williamson const char *buf, size_t count)\
143691fa1277SAlex Williamson { \
143791fa1277SAlex Williamson struct pci_dev *pdev = to_pci_dev(dev); \
143891fa1277SAlex Williamson unsigned long size, flags; \
143991fa1277SAlex Williamson int ret, i; \
144091fa1277SAlex Williamson u16 cmd; \
144191fa1277SAlex Williamson \
144291fa1277SAlex Williamson if (kstrtoul(buf, 0, &size) < 0) \
144391fa1277SAlex Williamson return -EINVAL; \
144491fa1277SAlex Williamson \
144591fa1277SAlex Williamson device_lock(dev); \
144691fa1277SAlex Williamson if (dev->driver) { \
144791fa1277SAlex Williamson ret = -EBUSY; \
144891fa1277SAlex Williamson goto unlock; \
144991fa1277SAlex Williamson } \
145091fa1277SAlex Williamson \
145191fa1277SAlex Williamson pci_config_pm_runtime_get(pdev); \
145291fa1277SAlex Williamson \
145391fa1277SAlex Williamson if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) { \
145491fa1277SAlex Williamson ret = aperture_remove_conflicting_pci_devices(pdev, \
145591fa1277SAlex Williamson "resourceN_resize"); \
145691fa1277SAlex Williamson if (ret) \
145791fa1277SAlex Williamson goto pm_put; \
145891fa1277SAlex Williamson } \
145991fa1277SAlex Williamson \
146091fa1277SAlex Williamson pci_read_config_word(pdev, PCI_COMMAND, &cmd); \
146191fa1277SAlex Williamson pci_write_config_word(pdev, PCI_COMMAND, \
146291fa1277SAlex Williamson cmd & ~PCI_COMMAND_MEMORY); \
146391fa1277SAlex Williamson \
146491fa1277SAlex Williamson flags = pci_resource_flags(pdev, n); \
146591fa1277SAlex Williamson \
146691fa1277SAlex Williamson pci_remove_resource_files(pdev); \
146791fa1277SAlex Williamson \
146891fa1277SAlex Williamson for (i = 0; i < PCI_STD_NUM_BARS; i++) { \
146991fa1277SAlex Williamson if (pci_resource_len(pdev, i) && \
147091fa1277SAlex Williamson pci_resource_flags(pdev, i) == flags) \
147191fa1277SAlex Williamson pci_release_resource(pdev, i); \
147291fa1277SAlex Williamson } \
147391fa1277SAlex Williamson \
147491fa1277SAlex Williamson ret = pci_resize_resource(pdev, n, size); \
147591fa1277SAlex Williamson \
147691fa1277SAlex Williamson pci_assign_unassigned_bus_resources(pdev->bus); \
147791fa1277SAlex Williamson \
147891fa1277SAlex Williamson if (pci_create_resource_files(pdev)) \
147991fa1277SAlex Williamson pci_warn(pdev, "Failed to recreate resource files after BAR resizing\n");\
148091fa1277SAlex Williamson \
148191fa1277SAlex Williamson pci_write_config_word(pdev, PCI_COMMAND, cmd); \
148291fa1277SAlex Williamson pm_put: \
148391fa1277SAlex Williamson pci_config_pm_runtime_put(pdev); \
148491fa1277SAlex Williamson unlock: \
148591fa1277SAlex Williamson device_unlock(dev); \
148691fa1277SAlex Williamson \
148791fa1277SAlex Williamson return ret ? ret : count; \
148891fa1277SAlex Williamson } \
148991fa1277SAlex Williamson static DEVICE_ATTR_RW(resource##n##_resize)
149091fa1277SAlex Williamson
149191fa1277SAlex Williamson pci_dev_resource_resize_attr(0);
149291fa1277SAlex Williamson pci_dev_resource_resize_attr(1);
149391fa1277SAlex Williamson pci_dev_resource_resize_attr(2);
149491fa1277SAlex Williamson pci_dev_resource_resize_attr(3);
149591fa1277SAlex Williamson pci_dev_resource_resize_attr(4);
149691fa1277SAlex Williamson pci_dev_resource_resize_attr(5);
149791fa1277SAlex Williamson
149891fa1277SAlex Williamson static struct attribute *resource_resize_attrs[] = {
149991fa1277SAlex Williamson &dev_attr_resource0_resize.attr,
150091fa1277SAlex Williamson &dev_attr_resource1_resize.attr,
150191fa1277SAlex Williamson &dev_attr_resource2_resize.attr,
150291fa1277SAlex Williamson &dev_attr_resource3_resize.attr,
150391fa1277SAlex Williamson &dev_attr_resource4_resize.attr,
150491fa1277SAlex Williamson &dev_attr_resource5_resize.attr,
150591fa1277SAlex Williamson NULL,
150691fa1277SAlex Williamson };
150791fa1277SAlex Williamson
resource_resize_is_visible(struct kobject * kobj,struct attribute * a,int n)150891fa1277SAlex Williamson static umode_t resource_resize_is_visible(struct kobject *kobj,
150991fa1277SAlex Williamson struct attribute *a, int n)
151091fa1277SAlex Williamson {
151191fa1277SAlex Williamson struct pci_dev *pdev = to_pci_dev(kobj_to_dev(kobj));
151291fa1277SAlex Williamson
151391fa1277SAlex Williamson return pci_rebar_get_current_size(pdev, n) < 0 ? 0 : a->mode;
151491fa1277SAlex Williamson }
151591fa1277SAlex Williamson
151691fa1277SAlex Williamson static const struct attribute_group pci_dev_resource_resize_group = {
151791fa1277SAlex Williamson .attrs = resource_resize_attrs,
151891fa1277SAlex Williamson .is_visible = resource_resize_is_visible,
151991fa1277SAlex Williamson };
152091fa1277SAlex Williamson
pci_create_sysfs_dev_files(struct pci_dev * pdev)1521b19441afSGreg Kroah-Hartman int __must_check pci_create_sysfs_dev_files(struct pci_dev *pdev)
15221da177e4SLinus Torvalds {
15231da177e4SLinus Torvalds if (!sysfs_initialized)
15241da177e4SLinus Torvalds return -EACCES;
15251da177e4SLinus Torvalds
1526506140f9SKrzysztof Wilczyński return pci_create_resource_files(pdev);
1527280c73d3SZhao, Yu }
1528280c73d3SZhao, Yu
15291da177e4SLinus Torvalds /**
15301da177e4SLinus Torvalds * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
15311da177e4SLinus Torvalds * @pdev: device whose entries we should free
15321da177e4SLinus Torvalds *
15331da177e4SLinus Torvalds * Cleanup when @pdev is removed from sysfs.
15341da177e4SLinus Torvalds */
pci_remove_sysfs_dev_files(struct pci_dev * pdev)15351da177e4SLinus Torvalds void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
15361da177e4SLinus Torvalds {
1537d67afe5eSDavid Miller if (!sysfs_initialized)
1538d67afe5eSDavid Miller return;
1539d67afe5eSDavid Miller
15401da177e4SLinus Torvalds pci_remove_resource_files(pdev);
15411da177e4SLinus Torvalds }
15421da177e4SLinus Torvalds
pci_sysfs_init(void)15431da177e4SLinus Torvalds static int __init pci_sysfs_init(void)
15441da177e4SLinus Torvalds {
15451da177e4SLinus Torvalds struct pci_dev *pdev = NULL;
1546efd532a6SDaniel Vetter struct pci_bus *pbus = NULL;
1547b19441afSGreg Kroah-Hartman int retval;
15481da177e4SLinus Torvalds
15491da177e4SLinus Torvalds sysfs_initialized = 1;
1550b19441afSGreg Kroah-Hartman for_each_pci_dev(pdev) {
1551b19441afSGreg Kroah-Hartman retval = pci_create_sysfs_dev_files(pdev);
1552151fc5dfSJulia Lawall if (retval) {
1553151fc5dfSJulia Lawall pci_dev_put(pdev);
1554b19441afSGreg Kroah-Hartman return retval;
1555b19441afSGreg Kroah-Hartman }
1556151fc5dfSJulia Lawall }
15571da177e4SLinus Torvalds
1558efd532a6SDaniel Vetter while ((pbus = pci_find_next_bus(pbus)))
1559efd532a6SDaniel Vetter pci_create_legacy_files(pbus);
1560efd532a6SDaniel Vetter
15611da177e4SLinus Torvalds return 0;
15621da177e4SLinus Torvalds }
156340ee9e9fSJesse Barnes late_initcall(pci_sysfs_init);
15644e15c46bSYinghai Lu
15654e15c46bSYinghai Lu static struct attribute *pci_dev_dev_attrs[] = {
15668bdfa145SKelsey Skunberg &dev_attr_boot_vga.attr,
15674e15c46bSYinghai Lu NULL,
15684e15c46bSYinghai Lu };
15694e15c46bSYinghai Lu
pci_dev_attrs_are_visible(struct kobject * kobj,struct attribute * a,int n)15704e15c46bSYinghai Lu static umode_t pci_dev_attrs_are_visible(struct kobject *kobj,
15714e15c46bSYinghai Lu struct attribute *a, int n)
15724e15c46bSYinghai Lu {
1573554a6037SGeliang Tang struct device *dev = kobj_to_dev(kobj);
1574625e1d59SYinghai Lu struct pci_dev *pdev = to_pci_dev(dev);
1575625e1d59SYinghai Lu
15768bdfa145SKelsey Skunberg if (a == &dev_attr_boot_vga.attr)
1577625e1d59SYinghai Lu if ((pdev->class >> 8) != PCI_CLASS_DISPLAY_VGA)
1578625e1d59SYinghai Lu return 0;
1579625e1d59SYinghai Lu
15804e15c46bSYinghai Lu return a->mode;
15814e15c46bSYinghai Lu }
15824e15c46bSYinghai Lu
1583dfab88beSJiang Liu static struct attribute *pci_dev_hp_attrs[] = {
15848bdfa145SKelsey Skunberg &dev_attr_remove.attr,
15854e2b7943SKelsey Skunberg &dev_attr_dev_rescan.attr,
1586dfab88beSJiang Liu NULL,
1587dfab88beSJiang Liu };
1588dfab88beSJiang Liu
pci_dev_hp_attrs_are_visible(struct kobject * kobj,struct attribute * a,int n)1589dfab88beSJiang Liu static umode_t pci_dev_hp_attrs_are_visible(struct kobject *kobj,
1590dfab88beSJiang Liu struct attribute *a, int n)
1591dfab88beSJiang Liu {
1592554a6037SGeliang Tang struct device *dev = kobj_to_dev(kobj);
1593dfab88beSJiang Liu struct pci_dev *pdev = to_pci_dev(dev);
1594dfab88beSJiang Liu
1595dfab88beSJiang Liu if (pdev->is_virtfn)
1596dfab88beSJiang Liu return 0;
1597dfab88beSJiang Liu
1598dfab88beSJiang Liu return a->mode;
1599dfab88beSJiang Liu }
1600dfab88beSJiang Liu
pci_bridge_attrs_are_visible(struct kobject * kobj,struct attribute * a,int n)160156c1af46SWong Vee Khee static umode_t pci_bridge_attrs_are_visible(struct kobject *kobj,
160256c1af46SWong Vee Khee struct attribute *a, int n)
160356c1af46SWong Vee Khee {
160456c1af46SWong Vee Khee struct device *dev = kobj_to_dev(kobj);
160556c1af46SWong Vee Khee struct pci_dev *pdev = to_pci_dev(dev);
160656c1af46SWong Vee Khee
160756c1af46SWong Vee Khee if (pci_is_bridge(pdev))
160856c1af46SWong Vee Khee return a->mode;
160956c1af46SWong Vee Khee
161056c1af46SWong Vee Khee return 0;
161156c1af46SWong Vee Khee }
161256c1af46SWong Vee Khee
pcie_dev_attrs_are_visible(struct kobject * kobj,struct attribute * a,int n)161356c1af46SWong Vee Khee static umode_t pcie_dev_attrs_are_visible(struct kobject *kobj,
161456c1af46SWong Vee Khee struct attribute *a, int n)
161556c1af46SWong Vee Khee {
161656c1af46SWong Vee Khee struct device *dev = kobj_to_dev(kobj);
161756c1af46SWong Vee Khee struct pci_dev *pdev = to_pci_dev(dev);
161856c1af46SWong Vee Khee
161956c1af46SWong Vee Khee if (pci_is_pcie(pdev))
162056c1af46SWong Vee Khee return a->mode;
162156c1af46SWong Vee Khee
162256c1af46SWong Vee Khee return 0;
162356c1af46SWong Vee Khee }
162456c1af46SWong Vee Khee
162556c1af46SWong Vee Khee static const struct attribute_group pci_dev_group = {
162656c1af46SWong Vee Khee .attrs = pci_dev_attrs,
162756c1af46SWong Vee Khee };
162856c1af46SWong Vee Khee
162956c1af46SWong Vee Khee const struct attribute_group *pci_dev_groups[] = {
163056c1af46SWong Vee Khee &pci_dev_group,
1631e1d3f326SKrzysztof Wilczyński &pci_dev_config_attr_group,
1632527139d7SKrzysztof Wilczyński &pci_dev_rom_attr_group,
1633f42c35eaSKrzysztof Wilczyński &pci_dev_reset_attr_group,
1634d88f521dSAmey Narkhede &pci_dev_reset_method_attr_group,
1635d93f8399SKrzysztof Wilczyński &pci_dev_vpd_attr_group,
1636506140f9SKrzysztof Wilczyński #ifdef CONFIG_DMI
1637506140f9SKrzysztof Wilczyński &pci_dev_smbios_attr_group,
1638506140f9SKrzysztof Wilczyński #endif
1639506140f9SKrzysztof Wilczyński #ifdef CONFIG_ACPI
1640506140f9SKrzysztof Wilczyński &pci_dev_acpi_attr_group,
1641506140f9SKrzysztof Wilczyński #endif
164291fa1277SAlex Williamson &pci_dev_resource_resize_group,
164356c1af46SWong Vee Khee NULL,
164456c1af46SWong Vee Khee };
164556c1af46SWong Vee Khee
1646e7ea9825SArvind Yadav static const struct attribute_group pci_dev_hp_attr_group = {
1647dfab88beSJiang Liu .attrs = pci_dev_hp_attrs,
1648dfab88beSJiang Liu .is_visible = pci_dev_hp_attrs_are_visible,
1649dfab88beSJiang Liu };
1650dfab88beSJiang Liu
1651e7ea9825SArvind Yadav static const struct attribute_group pci_dev_attr_group = {
16524e15c46bSYinghai Lu .attrs = pci_dev_dev_attrs,
16534e15c46bSYinghai Lu .is_visible = pci_dev_attrs_are_visible,
16544e15c46bSYinghai Lu };
16554e15c46bSYinghai Lu
1656e7ea9825SArvind Yadav static const struct attribute_group pci_bridge_attr_group = {
165756c1af46SWong Vee Khee .attrs = pci_bridge_attrs,
165856c1af46SWong Vee Khee .is_visible = pci_bridge_attrs_are_visible,
165956c1af46SWong Vee Khee };
166056c1af46SWong Vee Khee
1661e7ea9825SArvind Yadav static const struct attribute_group pcie_dev_attr_group = {
166256c1af46SWong Vee Khee .attrs = pcie_dev_attrs,
166356c1af46SWong Vee Khee .is_visible = pcie_dev_attrs_are_visible,
166456c1af46SWong Vee Khee };
166556c1af46SWong Vee Khee
16664e15c46bSYinghai Lu static const struct attribute_group *pci_dev_attr_groups[] = {
16674e15c46bSYinghai Lu &pci_dev_attr_group,
1668dfab88beSJiang Liu &pci_dev_hp_attr_group,
16691789382aSDonald Dutile #ifdef CONFIG_PCI_IOV
1670c3d5c2d9SLeon Romanovsky &sriov_pf_dev_attr_group,
1671c3d5c2d9SLeon Romanovsky &sriov_vf_dev_attr_group,
16721789382aSDonald Dutile #endif
167356c1af46SWong Vee Khee &pci_bridge_attr_group,
167456c1af46SWong Vee Khee &pcie_dev_attr_group,
167581aa5206SRajat Jain #ifdef CONFIG_PCIEAER
167681aa5206SRajat Jain &aer_stats_attr_group,
167781aa5206SRajat Jain #endif
167872ea91afSHeiner Kallweit #ifdef CONFIG_PCIEASPM
167972ea91afSHeiner Kallweit &aspm_ctrl_attr_group,
168072ea91afSHeiner Kallweit #endif
16814e15c46bSYinghai Lu NULL,
16824e15c46bSYinghai Lu };
16834e15c46bSYinghai Lu
168469f2dc24SBhumika Goyal const struct device_type pci_dev_type = {
16854e15c46bSYinghai Lu .groups = pci_dev_attr_groups,
16864e15c46bSYinghai Lu };
1687