aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/access.c
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 /drivers/pci/access.c
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>
Diffstat (limited to 'drivers/pci/access.c')
-rw-r--r--drivers/pci/access.c166
1 files changed, 166 insertions, 0 deletions
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