aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-bus-pci11
-rw-r--r--drivers/pci/access.c166
-rw-r--r--drivers/pci/pci-sysfs.c109
-rw-r--r--drivers/pci/pci.h19
-rw-r--r--drivers/pci/probe.c3
-rw-r--r--include/linux/pci.h3
6 files changed, 297 insertions, 14 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci
new file mode 100644
index 000000000000..ceddcff4082a
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-pci
@@ -0,0 +1,11 @@
1What: /sys/bus/pci/devices/.../vpd
2Date: February 2008
3Contact: Ben Hutchings <bhutchings@solarflare.com>
4Description:
5 A file named vpd in a device directory will be a
6 binary file containing the Vital Product Data for the
7 device. It should follow the VPD format defined in
8 PCI Specification 2.1 or 2.2, but users should consider
9 that some devices may have malformatted data. If the
10 underlying VPD has a writable section then the
11 corresponding section of this file will be writable.
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index fc405f0165d9..ec8f7002b09d 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -1,3 +1,4 @@
1#include <linux/delay.h>
1#include <linux/pci.h> 2#include <linux/pci.h>
2#include <linux/module.h> 3#include <linux/module.h>
3#include <linux/sched.h> 4#include <linux/sched.h>
@@ -126,6 +127,171 @@ PCI_USER_WRITE_CONFIG(byte, u8)
126PCI_USER_WRITE_CONFIG(word, u16) 127PCI_USER_WRITE_CONFIG(word, u16)
127PCI_USER_WRITE_CONFIG(dword, u32) 128PCI_USER_WRITE_CONFIG(dword, u32)
128 129
130/* VPD access through PCI 2.2+ VPD capability */
131
132#define PCI_VPD_PCI22_SIZE (PCI_VPD_ADDR_MASK + 1)
133
134struct pci_vpd_pci22 {
135 struct pci_vpd base;
136 spinlock_t lock; /* controls access to hardware and the flags */
137 u8 cap;
138 bool busy;
139 bool flag; /* value of F bit to wait for */
140};
141
142/* Wait for last operation to complete */
143static int pci_vpd_pci22_wait(struct pci_dev *dev)
144{
145 struct pci_vpd_pci22 *vpd =
146 container_of(dev->vpd, struct pci_vpd_pci22, base);
147 u16 flag, status;
148 int wait;
149 int ret;
150
151 if (!vpd->busy)
152 return 0;
153
154 flag = vpd->flag ? PCI_VPD_ADDR_F : 0;
155 wait = vpd->flag ? 10 : 1000; /* read: 100 us; write: 10 ms */
156 for (;;) {
157 ret = pci_user_read_config_word(dev,
158 vpd->cap + PCI_VPD_ADDR,
159 &status);
160 if (ret < 0)
161 return ret;
162 if ((status & PCI_VPD_ADDR_F) == flag) {
163 vpd->busy = false;
164 return 0;
165 }
166 if (wait-- == 0)
167 return -ETIMEDOUT;
168 udelay(10);
169 }
170}
171
172static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size,
173 char *buf)
174{
175 struct pci_vpd_pci22 *vpd =
176 container_of(dev->vpd, struct pci_vpd_pci22, base);
177 u32 val;
178 int ret;
179 int begin, end, i;
180
181 if (pos < 0 || pos > PCI_VPD_PCI22_SIZE ||
182 size > PCI_VPD_PCI22_SIZE - pos)
183 return -EINVAL;
184 if (size == 0)
185 return 0;
186
187 spin_lock_irq(&vpd->lock);
188 ret = pci_vpd_pci22_wait(dev);
189 if (ret < 0)
190 goto out;
191 ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
192 pos & ~3);
193 if (ret < 0)
194 goto out;
195 vpd->busy = true;
196 vpd->flag = 1;
197 ret = pci_vpd_pci22_wait(dev);
198 if (ret < 0)
199 goto out;
200 ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA,
201 &val);
202out:
203 spin_unlock_irq(&vpd->lock);
204 if (ret < 0)
205 return ret;
206
207 /* Convert to bytes */
208 begin = pos & 3;
209 end = min(4, begin + size);
210 for (i = 0; i < end; ++i) {
211 if (i >= begin)
212 *buf++ = val;
213 val >>= 8;
214 }
215 return end - begin;
216}
217
218static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size,
219 const char *buf)
220{
221 struct pci_vpd_pci22 *vpd =
222 container_of(dev->vpd, struct pci_vpd_pci22, base);
223 u32 val;
224 int ret;
225
226 if (pos < 0 || pos > PCI_VPD_PCI22_SIZE || pos & 3 ||
227 size > PCI_VPD_PCI22_SIZE - pos || size < 4)
228 return -EINVAL;
229
230 val = (u8) *buf++;
231 val |= ((u8) *buf++) << 8;
232 val |= ((u8) *buf++) << 16;
233 val |= ((u32)(u8) *buf++) << 24;
234
235 spin_lock_irq(&vpd->lock);
236 ret = pci_vpd_pci22_wait(dev);
237 if (ret < 0)
238 goto out;
239 ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA,
240 val);
241 if (ret < 0)
242 goto out;
243 ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR,
244 pos | PCI_VPD_ADDR_F);
245 if (ret < 0)
246 goto out;
247 vpd->busy = true;
248 vpd->flag = 0;
249 ret = pci_vpd_pci22_wait(dev);
250out:
251 spin_unlock_irq(&vpd->lock);
252 if (ret < 0)
253 return ret;
254
255 return 4;
256}
257
258static int pci_vpd_pci22_get_size(struct pci_dev *dev)
259{
260 return PCI_VPD_PCI22_SIZE;
261}
262
263static void pci_vpd_pci22_release(struct pci_dev *dev)
264{
265 kfree(container_of(dev->vpd, struct pci_vpd_pci22, base));
266}
267
268static struct pci_vpd_ops pci_vpd_pci22_ops = {
269 .read = pci_vpd_pci22_read,
270 .write = pci_vpd_pci22_write,
271 .get_size = pci_vpd_pci22_get_size,
272 .release = pci_vpd_pci22_release,
273};
274
275int pci_vpd_pci22_init(struct pci_dev *dev)
276{
277 struct pci_vpd_pci22 *vpd;
278 u8 cap;
279
280 cap = pci_find_capability(dev, PCI_CAP_ID_VPD);
281 if (!cap)
282 return -ENODEV;
283 vpd = kzalloc(sizeof(*vpd), GFP_ATOMIC);
284 if (!vpd)
285 return -ENOMEM;
286
287 vpd->base.ops = &pci_vpd_pci22_ops;
288 spin_lock_init(&vpd->lock);
289 vpd->cap = cap;
290 vpd->busy = false;
291 dev->vpd = &vpd->base;
292 return 0;
293}
294
129/** 295/**
130 * pci_block_user_cfg_access - Block userspace PCI config reads/writes 296 * pci_block_user_cfg_access - Block userspace PCI config reads/writes
131 * @dev: pci device struct 297 * @dev: pci device struct
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index f5b0b622c189..ae9a7695be97 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -343,6 +343,58 @@ pci_write_config(struct kobject *kobj, struct bin_attribute *bin_attr,
343 return count; 343 return count;
344} 344}
345 345
346static ssize_t
347pci_read_vpd(struct kobject *kobj, struct bin_attribute *bin_attr,
348 char *buf, loff_t off, size_t count)
349{
350 struct pci_dev *dev =
351 to_pci_dev(container_of(kobj, struct device, kobj));
352 int end;
353 int ret;
354
355 if (off > bin_attr->size)
356 count = 0;
357 else if (count > bin_attr->size - off)
358 count = bin_attr->size - off;
359 end = off + count;
360
361 while (off < end) {
362 ret = dev->vpd->ops->read(dev, off, end - off, buf);
363 if (ret < 0)
364 return ret;
365 buf += ret;
366 off += ret;
367 }
368
369 return count;
370}
371
372static ssize_t
373pci_write_vpd(struct kobject *kobj, struct bin_attribute *bin_attr,
374 char *buf, loff_t off, size_t count)
375{
376 struct pci_dev *dev =
377 to_pci_dev(container_of(kobj, struct device, kobj));
378 int end;
379 int ret;
380
381 if (off > bin_attr->size)
382 count = 0;
383 else if (count > bin_attr->size - off)
384 count = bin_attr->size - off;
385 end = off + count;
386
387 while (off < end) {
388 ret = dev->vpd->ops->write(dev, off, end - off, buf);
389 if (ret < 0)
390 return ret;
391 buf += ret;
392 off += ret;
393 }
394
395 return count;
396}
397
346#ifdef HAVE_PCI_LEGACY 398#ifdef HAVE_PCI_LEGACY
347/** 399/**
348 * pci_read_legacy_io - read byte(s) from legacy I/O port space 400 * pci_read_legacy_io - read byte(s) from legacy I/O port space
@@ -611,7 +663,7 @@ int __attribute__ ((weak)) pcibios_add_platform_entries(struct pci_dev *dev)
611 663
612int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) 664int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
613{ 665{
614 struct bin_attribute *rom_attr = NULL; 666 struct bin_attribute *attr = NULL;
615 int retval; 667 int retval;
616 668
617 if (!sysfs_initialized) 669 if (!sysfs_initialized)
@@ -624,22 +676,41 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
624 if (retval) 676 if (retval)
625 goto err; 677 goto err;
626 678
679 /* If the device has VPD, try to expose it in sysfs. */
680 if (pdev->vpd) {
681 attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
682 if (attr) {
683 pdev->vpd->attr = attr;
684 attr->size = pdev->vpd->ops->get_size(pdev);
685 attr->attr.name = "vpd";
686 attr->attr.mode = S_IRUGO | S_IWUSR;
687 attr->read = pci_read_vpd;
688 attr->write = pci_write_vpd;
689 retval = sysfs_create_bin_file(&pdev->dev.kobj, attr);
690 if (retval)
691 goto err_vpd;
692 } else {
693 retval = -ENOMEM;
694 goto err_config_file;
695 }
696 }
697
627 retval = pci_create_resource_files(pdev); 698 retval = pci_create_resource_files(pdev);
628 if (retval) 699 if (retval)
629 goto err_bin_file; 700 goto err_vpd_file;
630 701
631 /* If the device has a ROM, try to expose it in sysfs. */ 702 /* If the device has a ROM, try to expose it in sysfs. */
632 if (pci_resource_len(pdev, PCI_ROM_RESOURCE) || 703 if (pci_resource_len(pdev, PCI_ROM_RESOURCE) ||
633 (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)) { 704 (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)) {
634 rom_attr = kzalloc(sizeof(*rom_attr), GFP_ATOMIC); 705 attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
635 if (rom_attr) { 706 if (attr) {
636 pdev->rom_attr = rom_attr; 707 pdev->rom_attr = attr;
637 rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE); 708 attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
638 rom_attr->attr.name = "rom"; 709 attr->attr.name = "rom";
639 rom_attr->attr.mode = S_IRUSR; 710 attr->attr.mode = S_IRUSR;
640 rom_attr->read = pci_read_rom; 711 attr->read = pci_read_rom;
641 rom_attr->write = pci_write_rom; 712 attr->write = pci_write_rom;
642 retval = sysfs_create_bin_file(&pdev->dev.kobj, rom_attr); 713 retval = sysfs_create_bin_file(&pdev->dev.kobj, attr);
643 if (retval) 714 if (retval)
644 goto err_rom; 715 goto err_rom;
645 } else { 716 } else {
@@ -657,12 +728,18 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
657 728
658err_rom_file: 729err_rom_file:
659 if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) 730 if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
660 sysfs_remove_bin_file(&pdev->dev.kobj, rom_attr); 731 sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
661err_rom: 732err_rom:
662 kfree(rom_attr); 733 kfree(pdev->rom_attr);
663err_resource_files: 734err_resource_files:
664 pci_remove_resource_files(pdev); 735 pci_remove_resource_files(pdev);
665err_bin_file: 736err_vpd_file:
737 if (pdev->vpd) {
738 sysfs_remove_bin_file(&pdev->dev.kobj, pdev->vpd->attr);
739err_vpd:
740 kfree(pdev->vpd->attr);
741 }
742err_config_file:
666 if (pdev->cfg_size < 4096) 743 if (pdev->cfg_size < 4096)
667 sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); 744 sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
668 else 745 else
@@ -684,6 +761,10 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
684 761
685 pcie_aspm_remove_sysfs_dev_files(pdev); 762 pcie_aspm_remove_sysfs_dev_files(pdev);
686 763
764 if (pdev->vpd) {
765 sysfs_remove_bin_file(&pdev->dev.kobj, pdev->vpd->attr);
766 kfree(pdev->vpd->attr);
767 }
687 if (pdev->cfg_size < 4096) 768 if (pdev->cfg_size < 4096)
688 sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); 769 sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
689 else 770 else
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index eabeb1f2ec99..0a497c1b4227 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -18,6 +18,25 @@ extern int pci_user_write_config_byte(struct pci_dev *dev, int where, u8 val);
18extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val); 18extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val);
19extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); 19extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val);
20 20
21struct pci_vpd_ops {
22 int (*read)(struct pci_dev *dev, int pos, int size, char *buf);
23 int (*write)(struct pci_dev *dev, int pos, int size, const char *buf);
24 int (*get_size)(struct pci_dev *dev);
25 void (*release)(struct pci_dev *dev);
26};
27
28struct pci_vpd {
29 struct pci_vpd_ops *ops;
30 struct bin_attribute *attr; /* descriptor for sysfs VPD entry */
31};
32
33extern int pci_vpd_pci22_init(struct pci_dev *dev);
34static inline void pci_vpd_release(struct pci_dev *dev)
35{
36 if (dev->vpd)
37 dev->vpd->ops->release(dev);
38}
39
21/* PCI /proc functions */ 40/* PCI /proc functions */
22#ifdef CONFIG_PROC_FS 41#ifdef CONFIG_PROC_FS
23extern int pci_proc_attach_device(struct pci_dev *dev); 42extern int pci_proc_attach_device(struct pci_dev *dev);
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 284ef392c3ea..c2e99fd87faf 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -794,6 +794,7 @@ static void pci_release_dev(struct device *dev)
794 struct pci_dev *pci_dev; 794 struct pci_dev *pci_dev;
795 795
796 pci_dev = to_pci_dev(dev); 796 pci_dev = to_pci_dev(dev);
797 pci_vpd_release(pci_dev);
797 kfree(pci_dev); 798 kfree(pci_dev);
798} 799}
799 800
@@ -933,6 +934,8 @@ pci_scan_device(struct pci_bus *bus, int devfn)
933 return NULL; 934 return NULL;
934 } 935 }
935 936
937 pci_vpd_pci22_init(dev);
938
936 return dev; 939 return dev;
937} 940}
938 941
diff --git a/include/linux/pci.h b/include/linux/pci.h
index e2f46b05cf8b..292491324b01 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -20,6 +20,8 @@
20/* Include the pci register defines */ 20/* Include the pci register defines */
21#include <linux/pci_regs.h> 21#include <linux/pci_regs.h>
22 22
23struct pci_vpd;
24
23/* 25/*
24 * The PCI interface treats multi-function devices as independent 26 * The PCI interface treats multi-function devices as independent
25 * devices. The slot/function address of each device is encoded 27 * devices. The slot/function address of each device is encoded
@@ -206,6 +208,7 @@ struct pci_dev {
206#ifdef CONFIG_PCI_MSI 208#ifdef CONFIG_PCI_MSI
207 struct list_head msi_list; 209 struct list_head msi_list;
208#endif 210#endif
211 struct pci_vpd *vpd;
209}; 212};
210 213
211extern struct pci_dev *alloc_pci_dev(void); 214extern struct pci_dev *alloc_pci_dev(void);