aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBen Hutchings <bhutchings@solarflare.com>2008-03-05 11:52:39 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2008-04-21 00:47:07 -0400
commit94e6108803469a37ee1e3c92dafdd1d59298602f (patch)
tree7f3ee30721411cca238f8eea5971d5bebbb70a55
parent5e0d2a6fc094a9b5047998deefeb1254c66856ee (diff)
PCI: Expose PCI VPD through sysfs
Vital Product Data (VPD) may be exposed by PCI devices in several ways. It is generally unsafe to read this information through the existing interfaces to user-land because of stateful interfaces. This adds: - abstract operations for VPD access (struct pci_vpd_ops) - VPD state information in struct pci_dev (struct pci_vpd) - an implementation of the VPD access method specified in PCI 2.2 (in access.c) - a 'vpd' binary file in sysfs directories for PCI devices with VPD operations defined It adds a probe for PCI 2.2 VPD in pci_scan_device() and release of VPD state in pci_release_dev(). Signed-off-by: Ben Hutchings <bhutchings@solarflare.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-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);