diff options
Diffstat (limited to 'drivers/pci/access.c')
-rw-r--r-- | drivers/pci/access.c | 226 |
1 files changed, 151 insertions, 75 deletions
diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 39bb96b413ef..381444794778 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c | |||
@@ -66,6 +66,39 @@ EXPORT_SYMBOL(pci_bus_write_config_byte); | |||
66 | EXPORT_SYMBOL(pci_bus_write_config_word); | 66 | EXPORT_SYMBOL(pci_bus_write_config_word); |
67 | EXPORT_SYMBOL(pci_bus_write_config_dword); | 67 | EXPORT_SYMBOL(pci_bus_write_config_dword); |
68 | 68 | ||
69 | |||
70 | /** | ||
71 | * pci_read_vpd - Read one entry from Vital Product Data | ||
72 | * @dev: pci device struct | ||
73 | * @pos: offset in vpd space | ||
74 | * @count: number of bytes to read | ||
75 | * @buf: pointer to where to store result | ||
76 | * | ||
77 | */ | ||
78 | ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf) | ||
79 | { | ||
80 | if (!dev->vpd || !dev->vpd->ops) | ||
81 | return -ENODEV; | ||
82 | return dev->vpd->ops->read(dev, pos, count, buf); | ||
83 | } | ||
84 | EXPORT_SYMBOL(pci_read_vpd); | ||
85 | |||
86 | /** | ||
87 | * pci_write_vpd - Write entry to Vital Product Data | ||
88 | * @dev: pci device struct | ||
89 | * @pos: offset in vpd space | ||
90 | * @count: number of bytes to read | ||
91 | * @val: value to write | ||
92 | * | ||
93 | */ | ||
94 | ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf) | ||
95 | { | ||
96 | if (!dev->vpd || !dev->vpd->ops) | ||
97 | return -ENODEV; | ||
98 | return dev->vpd->ops->write(dev, pos, count, buf); | ||
99 | } | ||
100 | EXPORT_SYMBOL(pci_write_vpd); | ||
101 | |||
69 | /* | 102 | /* |
70 | * The following routines are to prevent the user from accessing PCI config | 103 | * The following routines are to prevent the user from accessing PCI config |
71 | * space when it's unsafe to do so. Some devices require this during BIST and | 104 | * space when it's unsafe to do so. Some devices require this during BIST and |
@@ -133,125 +166,145 @@ PCI_USER_WRITE_CONFIG(dword, u32) | |||
133 | 166 | ||
134 | struct pci_vpd_pci22 { | 167 | struct pci_vpd_pci22 { |
135 | struct pci_vpd base; | 168 | struct pci_vpd base; |
136 | spinlock_t lock; /* controls access to hardware and the flags */ | 169 | struct mutex lock; |
137 | u8 cap; | 170 | u16 flag; |
138 | bool busy; | 171 | bool busy; |
139 | bool flag; /* value of F bit to wait for */ | 172 | u8 cap; |
140 | }; | 173 | }; |
141 | 174 | ||
142 | /* Wait for last operation to complete */ | 175 | /* |
176 | * Wait for last operation to complete. | ||
177 | * This code has to spin since there is no other notification from the PCI | ||
178 | * hardware. Since the VPD is often implemented by serial attachment to an | ||
179 | * EEPROM, it may take many milliseconds to complete. | ||
180 | */ | ||
143 | static int pci_vpd_pci22_wait(struct pci_dev *dev) | 181 | static int pci_vpd_pci22_wait(struct pci_dev *dev) |
144 | { | 182 | { |
145 | struct pci_vpd_pci22 *vpd = | 183 | struct pci_vpd_pci22 *vpd = |
146 | container_of(dev->vpd, struct pci_vpd_pci22, base); | 184 | container_of(dev->vpd, struct pci_vpd_pci22, base); |
147 | u16 flag, status; | 185 | unsigned long timeout = jiffies + HZ/20 + 2; |
148 | int wait; | 186 | u16 status; |
149 | int ret; | 187 | int ret; |
150 | 188 | ||
151 | if (!vpd->busy) | 189 | if (!vpd->busy) |
152 | return 0; | 190 | return 0; |
153 | 191 | ||
154 | flag = vpd->flag ? PCI_VPD_ADDR_F : 0; | ||
155 | wait = vpd->flag ? 10 : 1000; /* read: 100 us; write: 10 ms */ | ||
156 | for (;;) { | 192 | for (;;) { |
157 | ret = pci_user_read_config_word(dev, | 193 | ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR, |
158 | vpd->cap + PCI_VPD_ADDR, | ||
159 | &status); | 194 | &status); |
160 | if (ret < 0) | 195 | if (ret) |
161 | return ret; | 196 | return ret; |
162 | if ((status & PCI_VPD_ADDR_F) == flag) { | 197 | |
198 | if ((status & PCI_VPD_ADDR_F) == vpd->flag) { | ||
163 | vpd->busy = false; | 199 | vpd->busy = false; |
164 | return 0; | 200 | return 0; |
165 | } | 201 | } |
166 | if (wait-- == 0) | 202 | |
203 | if (time_after(jiffies, timeout)) | ||
167 | return -ETIMEDOUT; | 204 | return -ETIMEDOUT; |
168 | udelay(10); | 205 | if (fatal_signal_pending(current)) |
206 | return -EINTR; | ||
207 | if (!cond_resched()) | ||
208 | udelay(10); | ||
169 | } | 209 | } |
170 | } | 210 | } |
171 | 211 | ||
172 | static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size, | 212 | static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count, |
173 | char *buf) | 213 | void *arg) |
174 | { | 214 | { |
175 | struct pci_vpd_pci22 *vpd = | 215 | struct pci_vpd_pci22 *vpd = |
176 | container_of(dev->vpd, struct pci_vpd_pci22, base); | 216 | container_of(dev->vpd, struct pci_vpd_pci22, base); |
177 | u32 val; | ||
178 | int ret; | 217 | int ret; |
179 | int begin, end, i; | 218 | loff_t end = pos + count; |
219 | u8 *buf = arg; | ||
180 | 220 | ||
181 | if (pos < 0 || pos > vpd->base.len || size > vpd->base.len - pos) | 221 | if (pos < 0 || pos > vpd->base.len || end > vpd->base.len) |
182 | return -EINVAL; | 222 | return -EINVAL; |
183 | if (size == 0) | ||
184 | return 0; | ||
185 | 223 | ||
186 | spin_lock_irq(&vpd->lock); | 224 | if (mutex_lock_killable(&vpd->lock)) |
187 | ret = pci_vpd_pci22_wait(dev); | 225 | return -EINTR; |
188 | if (ret < 0) | 226 | |
189 | goto out; | ||
190 | ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, | ||
191 | pos & ~3); | ||
192 | if (ret < 0) | ||
193 | goto out; | ||
194 | vpd->busy = true; | ||
195 | vpd->flag = 1; | ||
196 | ret = pci_vpd_pci22_wait(dev); | 227 | ret = pci_vpd_pci22_wait(dev); |
197 | if (ret < 0) | 228 | if (ret < 0) |
198 | goto out; | 229 | goto out; |
199 | ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, | 230 | |
200 | &val); | 231 | while (pos < end) { |
201 | out: | 232 | u32 val; |
202 | spin_unlock_irq(&vpd->lock); | 233 | unsigned int i, skip; |
203 | if (ret < 0) | 234 | |
204 | return ret; | 235 | ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, |
205 | 236 | pos & ~3); | |
206 | /* Convert to bytes */ | 237 | if (ret < 0) |
207 | begin = pos & 3; | 238 | break; |
208 | end = min(4, begin + size); | 239 | vpd->busy = true; |
209 | for (i = 0; i < end; ++i) { | 240 | vpd->flag = PCI_VPD_ADDR_F; |
210 | if (i >= begin) | 241 | ret = pci_vpd_pci22_wait(dev); |
211 | *buf++ = val; | 242 | if (ret < 0) |
212 | val >>= 8; | 243 | break; |
244 | |||
245 | ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, &val); | ||
246 | if (ret < 0) | ||
247 | break; | ||
248 | |||
249 | skip = pos & 3; | ||
250 | for (i = 0; i < sizeof(u32); i++) { | ||
251 | if (i >= skip) { | ||
252 | *buf++ = val; | ||
253 | if (++pos == end) | ||
254 | break; | ||
255 | } | ||
256 | val >>= 8; | ||
257 | } | ||
213 | } | 258 | } |
214 | return end - begin; | 259 | out: |
260 | mutex_unlock(&vpd->lock); | ||
261 | return ret ? ret : count; | ||
215 | } | 262 | } |
216 | 263 | ||
217 | static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size, | 264 | static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count, |
218 | const char *buf) | 265 | const void *arg) |
219 | { | 266 | { |
220 | struct pci_vpd_pci22 *vpd = | 267 | struct pci_vpd_pci22 *vpd = |
221 | container_of(dev->vpd, struct pci_vpd_pci22, base); | 268 | container_of(dev->vpd, struct pci_vpd_pci22, base); |
222 | u32 val; | 269 | const u8 *buf = arg; |
223 | int ret; | 270 | loff_t end = pos + count; |
271 | int ret = 0; | ||
224 | 272 | ||
225 | if (pos < 0 || pos > vpd->base.len || pos & 3 || | 273 | if (pos < 0 || (pos & 3) || (count & 3) || end > vpd->base.len) |
226 | size > vpd->base.len - pos || size < 4) | ||
227 | return -EINVAL; | 274 | return -EINVAL; |
228 | 275 | ||
229 | val = (u8) *buf++; | 276 | if (mutex_lock_killable(&vpd->lock)) |
230 | val |= ((u8) *buf++) << 8; | 277 | return -EINTR; |
231 | val |= ((u8) *buf++) << 16; | ||
232 | val |= ((u32)(u8) *buf++) << 24; | ||
233 | 278 | ||
234 | spin_lock_irq(&vpd->lock); | ||
235 | ret = pci_vpd_pci22_wait(dev); | 279 | ret = pci_vpd_pci22_wait(dev); |
236 | if (ret < 0) | 280 | if (ret < 0) |
237 | goto out; | 281 | goto out; |
238 | ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, | ||
239 | val); | ||
240 | if (ret < 0) | ||
241 | goto out; | ||
242 | ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, | ||
243 | pos | PCI_VPD_ADDR_F); | ||
244 | if (ret < 0) | ||
245 | goto out; | ||
246 | vpd->busy = true; | ||
247 | vpd->flag = 0; | ||
248 | ret = pci_vpd_pci22_wait(dev); | ||
249 | out: | ||
250 | spin_unlock_irq(&vpd->lock); | ||
251 | if (ret < 0) | ||
252 | return ret; | ||
253 | 282 | ||
254 | return 4; | 283 | while (pos < end) { |
284 | u32 val; | ||
285 | |||
286 | val = *buf++; | ||
287 | val |= *buf++ << 8; | ||
288 | val |= *buf++ << 16; | ||
289 | val |= *buf++ << 24; | ||
290 | |||
291 | ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val); | ||
292 | if (ret < 0) | ||
293 | break; | ||
294 | ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, | ||
295 | pos | PCI_VPD_ADDR_F); | ||
296 | if (ret < 0) | ||
297 | break; | ||
298 | |||
299 | vpd->busy = true; | ||
300 | vpd->flag = 0; | ||
301 | ret = pci_vpd_pci22_wait(dev); | ||
302 | |||
303 | pos += sizeof(u32); | ||
304 | } | ||
305 | out: | ||
306 | mutex_unlock(&vpd->lock); | ||
307 | return ret ? ret : count; | ||
255 | } | 308 | } |
256 | 309 | ||
257 | static void pci_vpd_pci22_release(struct pci_dev *dev) | 310 | static void pci_vpd_pci22_release(struct pci_dev *dev) |
@@ -259,7 +312,7 @@ static void pci_vpd_pci22_release(struct pci_dev *dev) | |||
259 | kfree(container_of(dev->vpd, struct pci_vpd_pci22, base)); | 312 | kfree(container_of(dev->vpd, struct pci_vpd_pci22, base)); |
260 | } | 313 | } |
261 | 314 | ||
262 | static struct pci_vpd_ops pci_vpd_pci22_ops = { | 315 | static const struct pci_vpd_ops pci_vpd_pci22_ops = { |
263 | .read = pci_vpd_pci22_read, | 316 | .read = pci_vpd_pci22_read, |
264 | .write = pci_vpd_pci22_write, | 317 | .write = pci_vpd_pci22_write, |
265 | .release = pci_vpd_pci22_release, | 318 | .release = pci_vpd_pci22_release, |
@@ -279,7 +332,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev) | |||
279 | 332 | ||
280 | vpd->base.len = PCI_VPD_PCI22_SIZE; | 333 | vpd->base.len = PCI_VPD_PCI22_SIZE; |
281 | vpd->base.ops = &pci_vpd_pci22_ops; | 334 | vpd->base.ops = &pci_vpd_pci22_ops; |
282 | spin_lock_init(&vpd->lock); | 335 | mutex_init(&vpd->lock); |
283 | vpd->cap = cap; | 336 | vpd->cap = cap; |
284 | vpd->busy = false; | 337 | vpd->busy = false; |
285 | dev->vpd = &vpd->base; | 338 | dev->vpd = &vpd->base; |
@@ -287,6 +340,29 @@ int pci_vpd_pci22_init(struct pci_dev *dev) | |||
287 | } | 340 | } |
288 | 341 | ||
289 | /** | 342 | /** |
343 | * pci_vpd_truncate - Set available Vital Product Data size | ||
344 | * @dev: pci device struct | ||
345 | * @size: available memory in bytes | ||
346 | * | ||
347 | * Adjust size of available VPD area. | ||
348 | */ | ||
349 | int pci_vpd_truncate(struct pci_dev *dev, size_t size) | ||
350 | { | ||
351 | if (!dev->vpd) | ||
352 | return -EINVAL; | ||
353 | |||
354 | /* limited by the access method */ | ||
355 | if (size > dev->vpd->len) | ||
356 | return -EINVAL; | ||
357 | |||
358 | dev->vpd->len = size; | ||
359 | dev->vpd->attr->size = size; | ||
360 | |||
361 | return 0; | ||
362 | } | ||
363 | EXPORT_SYMBOL(pci_vpd_truncate); | ||
364 | |||
365 | /** | ||
290 | * pci_block_user_cfg_access - Block userspace PCI config reads/writes | 366 | * pci_block_user_cfg_access - Block userspace PCI config reads/writes |
291 | * @dev: pci device struct | 367 | * @dev: pci device struct |
292 | * | 368 | * |