Blame view

drivers/pci/pci-sysfs.c 27.3 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * drivers/pci/pci-sysfs.c
   *
   * (C) Copyright 2002-2004 Greg Kroah-Hartman <greg@kroah.com>
   * (C) Copyright 2002-2004 IBM Corp.
   * (C) Copyright 2003 Matthew Wilcox
   * (C) Copyright 2003 Hewlett-Packard
   * (C) Copyright 2004 Jon Smirl <jonsmirl@yahoo.com>
   * (C) Copyright 2004 Silicon Graphics, Inc. Jesse Barnes <jbarnes@sgi.com>
   *
   * File attributes for PCI devices
   *
   * Modeled after usb's driverfs.c 
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
16
  #include <linux/kernel.h>
b5ff7df3d   Linus Torvalds   Check mapped rang...
17
  #include <linux/sched.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
21
  #include <linux/pci.h>
  #include <linux/stat.h>
  #include <linux/topology.h>
  #include <linux/mm.h>
aa0ac3651   Alexey Dobriyan   Remove capability...
22
  #include <linux/capability.h>
7d715a6c1   Shaohua Li   PCI: add PCI Expr...
23
  #include <linux/pci-aspm.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
24
25
26
27
28
29
30
  #include "pci.h"
  
  static int sysfs_initialized;	/* = 0 */
  
  /* show configuration fields */
  #define pci_config_attr(field, format_string)				\
  static ssize_t								\
e404e274f   Yani Ioannou   [PATCH] Driver Co...
31
  field##_show(struct device *dev, struct device_attribute *attr, char *buf)				\
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  {									\
  	struct pci_dev *pdev;						\
  									\
  	pdev = to_pci_dev (dev);					\
  	return sprintf (buf, format_string, pdev->field);		\
  }
  
  pci_config_attr(vendor, "0x%04x
  ");
  pci_config_attr(device, "0x%04x
  ");
  pci_config_attr(subsystem_vendor, "0x%04x
  ");
  pci_config_attr(subsystem_device, "0x%04x
  ");
  pci_config_attr(class, "0x%06x
  ");
  pci_config_attr(irq, "%u
  ");
bdee9d98d   Doug Thompson   [PATCH] PCI: Bus ...
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  static ssize_t broken_parity_status_show(struct device *dev,
  					 struct device_attribute *attr,
  					 char *buf)
  {
  	struct pci_dev *pdev = to_pci_dev(dev);
  	return sprintf (buf, "%u
  ", pdev->broken_parity_status);
  }
  
  static ssize_t broken_parity_status_store(struct device *dev,
  					  struct device_attribute *attr,
  					  const char *buf, size_t count)
  {
  	struct pci_dev *pdev = to_pci_dev(dev);
92425a405   Trent Piepho   PCI: Make settabl...
65
  	unsigned long val;
bdee9d98d   Doug Thompson   [PATCH] PCI: Bus ...
66

92425a405   Trent Piepho   PCI: Make settabl...
67
68
69
70
71
72
  	if (strict_strtoul(buf, 0, &val) < 0)
  		return -EINVAL;
  
  	pdev->broken_parity_status = !!val;
  
  	return count;
bdee9d98d   Doug Thompson   [PATCH] PCI: Bus ...
73
  }
4327edf6b   Alan Cox   [PATCH] Subject: ...
74
75
  static ssize_t local_cpus_show(struct device *dev,
  			struct device_attribute *attr, char *buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
76
  {		
3be83050d   Mike Travis   cpumask: update l...
77
  	const struct cpumask *mask;
4327edf6b   Alan Cox   [PATCH] Subject: ...
78
  	int len;
3be83050d   Mike Travis   cpumask: update l...
79
80
  	mask = cpumask_of_pcibus(to_pci_dev(dev)->bus);
  	len = cpumask_scnprintf(buf, PAGE_SIZE-2, mask);
39106dcf8   Mike Travis   cpumask: use new ...
81
82
83
84
85
86
87
88
89
90
  	buf[len++] = '
  ';
  	buf[len] = '\0';
  	return len;
  }
  
  
  static ssize_t local_cpulist_show(struct device *dev,
  			struct device_attribute *attr, char *buf)
  {
3be83050d   Mike Travis   cpumask: update l...
91
  	const struct cpumask *mask;
39106dcf8   Mike Travis   cpumask: use new ...
92
  	int len;
3be83050d   Mike Travis   cpumask: update l...
93
94
  	mask = cpumask_of_pcibus(to_pci_dev(dev)->bus);
  	len = cpulist_scnprintf(buf, PAGE_SIZE-2, mask);
39106dcf8   Mike Travis   cpumask: use new ...
95
96
97
98
  	buf[len++] = '
  ';
  	buf[len] = '\0';
  	return len;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
99
100
101
102
  }
  
  /* show resources */
  static ssize_t
e404e274f   Yani Ioannou   [PATCH] Driver Co...
103
  resource_show(struct device * dev, struct device_attribute *attr, char * buf)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
104
105
106
107
  {
  	struct pci_dev * pci_dev = to_pci_dev(dev);
  	char * str = buf;
  	int i;
fde09c6d8   Yu Zhao   PCI: define PCI r...
108
  	int max;
e31dd6e45   Greg Kroah-Hartman   [PATCH] 64bit res...
109
  	resource_size_t start, end;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
110
111
112
  
  	if (pci_dev->subordinate)
  		max = DEVICE_COUNT_RESOURCE;
fde09c6d8   Yu Zhao   PCI: define PCI r...
113
114
  	else
  		max = PCI_BRIDGE_RESOURCES;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
115
116
  
  	for (i = 0; i < max; i++) {
2311b1f2b   Michael Ellerman   [PATCH] PCI: fix-...
117
118
119
120
121
122
123
  		struct resource *res =  &pci_dev->resource[i];
  		pci_resource_to_user(pci_dev, i, res, &start, &end);
  		str += sprintf(str,"0x%016llx 0x%016llx 0x%016llx
  ",
  			       (unsigned long long)start,
  			       (unsigned long long)end,
  			       (unsigned long long)res->flags);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
124
125
126
  	}
  	return (str - buf);
  }
87c8a4433   Greg Kroah-Hartman   [PATCH] PCI: fix ...
127
  static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf)
9888549e0   Greg Kroah-Hartman   [PATCH] PCI: add ...
128
129
130
131
132
133
134
135
136
137
  {
  	struct pci_dev *pci_dev = to_pci_dev(dev);
  
  	return sprintf(buf, "pci:v%08Xd%08Xsv%08Xsd%08Xbc%02Xsc%02Xi%02x
  ",
  		       pci_dev->vendor, pci_dev->device,
  		       pci_dev->subsystem_vendor, pci_dev->subsystem_device,
  		       (u8)(pci_dev->class >> 16), (u8)(pci_dev->class >> 8),
  		       (u8)(pci_dev->class));
  }
bae94d023   Inaky Perez-Gonzalez   PCI: switch pci_{...
138
139
140
141
  
  static ssize_t is_enabled_store(struct device *dev,
  				struct device_attribute *attr, const char *buf,
  				size_t count)
9f125d304   Arjan van de Ven   [PATCH] PCI: Add ...
142
143
  {
  	struct pci_dev *pdev = to_pci_dev(dev);
92425a405   Trent Piepho   PCI: Make settabl...
144
145
146
147
148
  	unsigned long val;
  	ssize_t result = strict_strtoul(buf, 0, &val);
  
  	if (result < 0)
  		return result;
9f125d304   Arjan van de Ven   [PATCH] PCI: Add ...
149
150
151
  
  	/* this can crash the machine when done on the "wrong" device */
  	if (!capable(CAP_SYS_ADMIN))
92425a405   Trent Piepho   PCI: Make settabl...
152
  		return -EPERM;
9f125d304   Arjan van de Ven   [PATCH] PCI: Add ...
153

92425a405   Trent Piepho   PCI: Make settabl...
154
  	if (!val) {
bae94d023   Inaky Perez-Gonzalez   PCI: switch pci_{...
155
156
157
158
  		if (atomic_read(&pdev->enable_cnt) != 0)
  			pci_disable_device(pdev);
  		else
  			result = -EIO;
92425a405   Trent Piepho   PCI: Make settabl...
159
  	} else
bae94d023   Inaky Perez-Gonzalez   PCI: switch pci_{...
160
  		result = pci_enable_device(pdev);
9f125d304   Arjan van de Ven   [PATCH] PCI: Add ...
161

bae94d023   Inaky Perez-Gonzalez   PCI: switch pci_{...
162
163
164
165
166
167
168
  	return result < 0 ? result : count;
  }
  
  static ssize_t is_enabled_show(struct device *dev,
  			       struct device_attribute *attr, char *buf)
  {
  	struct pci_dev *pdev;
9f125d304   Arjan van de Ven   [PATCH] PCI: Add ...
169

bae94d023   Inaky Perez-Gonzalez   PCI: switch pci_{...
170
171
172
  	pdev = to_pci_dev (dev);
  	return sprintf (buf, "%u
  ", atomic_read(&pdev->enable_cnt));
9f125d304   Arjan van de Ven   [PATCH] PCI: Add ...
173
  }
81bb0e198   Brice Goglin   PCI: Make PCI dev...
174
175
176
177
178
179
180
181
  #ifdef CONFIG_NUMA
  static ssize_t
  numa_node_show(struct device *dev, struct device_attribute *attr, char *buf)
  {
  	return sprintf (buf, "%d
  ", dev->numa_node);
  }
  #endif
fe97064c2   Brice Goglin   MSI: Export the P...
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
  static ssize_t
  msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf)
  {
  	struct pci_dev *pdev = to_pci_dev(dev);
  
  	if (!pdev->subordinate)
  		return 0;
  
  	return sprintf (buf, "%u
  ",
  			!(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI));
  }
  
  static ssize_t
  msi_bus_store(struct device *dev, struct device_attribute *attr,
  	      const char *buf, size_t count)
  {
  	struct pci_dev *pdev = to_pci_dev(dev);
92425a405   Trent Piepho   PCI: Make settabl...
200
201
202
203
  	unsigned long val;
  
  	if (strict_strtoul(buf, 0, &val) < 0)
  		return -EINVAL;
fe97064c2   Brice Goglin   MSI: Export the P...
204
205
206
207
  
  	/* bad things may happen if the no_msi flag is changed
  	 * while some drivers are loaded */
  	if (!capable(CAP_SYS_ADMIN))
92425a405   Trent Piepho   PCI: Make settabl...
208
  		return -EPERM;
fe97064c2   Brice Goglin   MSI: Export the P...
209

92425a405   Trent Piepho   PCI: Make settabl...
210
211
  	/* Maybe pci devices without subordinate busses shouldn't even have this
  	 * attribute in the first place?  */
fe97064c2   Brice Goglin   MSI: Export the P...
212
213
  	if (!pdev->subordinate)
  		return count;
92425a405   Trent Piepho   PCI: Make settabl...
214
215
216
217
  	/* Is the flag going to change, or keep the value it already had? */
  	if (!(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI) ^
  	    !!val) {
  		pdev->subordinate->bus_flags ^= PCI_BUS_FLAGS_NO_MSI;
fe97064c2   Brice Goglin   MSI: Export the P...
218

92425a405   Trent Piepho   PCI: Make settabl...
219
220
221
  		dev_warn(&pdev->dev, "forced subordinate bus to%s support MSI,"
  			 " bad things could happen
  ", val ? "" : " not");
fe97064c2   Brice Goglin   MSI: Export the P...
222
223
224
225
  	}
  
  	return count;
  }
9888549e0   Greg Kroah-Hartman   [PATCH] PCI: add ...
226

705b1aaa8   Alex Chiang   PCI: Introduce /s...
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
  #ifdef CONFIG_HOTPLUG
  static DEFINE_MUTEX(pci_remove_rescan_mutex);
  static ssize_t bus_rescan_store(struct bus_type *bus, const char *buf,
  				size_t count)
  {
  	unsigned long val;
  	struct pci_bus *b = NULL;
  
  	if (strict_strtoul(buf, 0, &val) < 0)
  		return -EINVAL;
  
  	if (val) {
  		mutex_lock(&pci_remove_rescan_mutex);
  		while ((b = pci_find_next_bus(b)) != NULL)
  			pci_rescan_bus(b);
  		mutex_unlock(&pci_remove_rescan_mutex);
  	}
  	return count;
  }
  
  struct bus_attribute pci_bus_attrs[] = {
  	__ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, bus_rescan_store),
  	__ATTR_NULL
  };
77c27c7b4   Alex Chiang   PCI: Introduce /s...
251

738a6396c   Alex Chiang   PCI: Introduce /s...
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
  static ssize_t
  dev_rescan_store(struct device *dev, struct device_attribute *attr,
  		 const char *buf, size_t count)
  {
  	unsigned long val;
  	struct pci_dev *pdev = to_pci_dev(dev);
  
  	if (strict_strtoul(buf, 0, &val) < 0)
  		return -EINVAL;
  
  	if (val) {
  		mutex_lock(&pci_remove_rescan_mutex);
  		pci_rescan_bus(pdev->bus);
  		mutex_unlock(&pci_remove_rescan_mutex);
  	}
  	return count;
  }
77c27c7b4   Alex Chiang   PCI: Introduce /s...
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
  static void remove_callback(struct device *dev)
  {
  	struct pci_dev *pdev = to_pci_dev(dev);
  
  	mutex_lock(&pci_remove_rescan_mutex);
  	pci_remove_bus_device(pdev);
  	mutex_unlock(&pci_remove_rescan_mutex);
  }
  
  static ssize_t
  remove_store(struct device *dev, struct device_attribute *dummy,
  	     const char *buf, size_t count)
  {
  	int ret = 0;
  	unsigned long val;
  	struct pci_dev *pdev = to_pci_dev(dev);
  
  	if (strict_strtoul(buf, 0, &val) < 0)
  		return -EINVAL;
  
  	if (pci_is_root_bus(pdev->bus))
  		return -EBUSY;
  
  	/* An attribute cannot be unregistered by one of its own methods,
  	 * so we have to use this roundabout approach.
  	 */
  	if (val)
  		ret = device_schedule_callback(dev, remove_callback);
  	if (ret)
  		count = ret;
  	return count;
  }
705b1aaa8   Alex Chiang   PCI: Introduce /s...
301
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
302
303
304
305
306
307
308
309
310
  struct device_attribute pci_dev_attrs[] = {
  	__ATTR_RO(resource),
  	__ATTR_RO(vendor),
  	__ATTR_RO(device),
  	__ATTR_RO(subsystem_vendor),
  	__ATTR_RO(subsystem_device),
  	__ATTR_RO(class),
  	__ATTR_RO(irq),
  	__ATTR_RO(local_cpus),
39106dcf8   Mike Travis   cpumask: use new ...
311
  	__ATTR_RO(local_cpulist),
9888549e0   Greg Kroah-Hartman   [PATCH] PCI: add ...
312
  	__ATTR_RO(modalias),
81bb0e198   Brice Goglin   PCI: Make PCI dev...
313
314
315
  #ifdef CONFIG_NUMA
  	__ATTR_RO(numa_node),
  #endif
9f125d304   Arjan van de Ven   [PATCH] PCI: Add ...
316
  	__ATTR(enable, 0600, is_enabled_show, is_enabled_store),
bdee9d98d   Doug Thompson   [PATCH] PCI: Bus ...
317
318
  	__ATTR(broken_parity_status,(S_IRUGO|S_IWUSR),
  		broken_parity_status_show,broken_parity_status_store),
fe97064c2   Brice Goglin   MSI: Export the P...
319
  	__ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store),
77c27c7b4   Alex Chiang   PCI: Introduce /s...
320
321
  #ifdef CONFIG_HOTPLUG
  	__ATTR(remove, (S_IWUSR|S_IWGRP), NULL, remove_store),
738a6396c   Alex Chiang   PCI: Introduce /s...
322
  	__ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store),
77c27c7b4   Alex Chiang   PCI: Introduce /s...
323
  #endif
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
324
325
326
327
  	__ATTR_NULL,
  };
  
  static ssize_t
217f45de3   Dave Airlie   PCI: expose boot ...
328
329
330
331
332
333
334
335
336
337
338
339
  boot_vga_show(struct device *dev, struct device_attribute *attr, char *buf)
  {
  	struct pci_dev *pdev = to_pci_dev(dev);
  
  	return sprintf(buf, "%u
  ",
  		!!(pdev->resource[PCI_ROM_RESOURCE].flags &
  		   IORESOURCE_ROM_SHADOW));
  }
  struct device_attribute vga_attr = __ATTR_RO(boot_vga);
  
  static ssize_t
91a690295   Zhang Rui   sysfs: add parame...
340
341
  pci_read_config(struct kobject *kobj, struct bin_attribute *bin_attr,
  		char *buf, loff_t off, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
342
343
344
345
  {
  	struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
  	unsigned int size = 64;
  	loff_t init_off = off;
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
346
  	u8 *data = (u8*) buf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
  
  	/* Several chips lock up trying to read undefined config space */
  	if (capable(CAP_SYS_ADMIN)) {
  		size = dev->cfg_size;
  	} else if (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
  		size = 128;
  	}
  
  	if (off > size)
  		return 0;
  	if (off + count > size) {
  		size -= off;
  		count = size;
  	} else {
  		size = count;
  	}
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
363
364
  	if ((off & 1) && size) {
  		u8 val;
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
365
  		pci_user_read_config_byte(dev, off, &val);
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
366
  		data[off - init_off] = val;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
367
  		off++;
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
368
369
370
371
372
  		size--;
  	}
  
  	if ((off & 3) && size > 2) {
  		u16 val;
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
373
  		pci_user_read_config_word(dev, off, &val);
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
374
375
376
377
  		data[off - init_off] = val & 0xff;
  		data[off - init_off + 1] = (val >> 8) & 0xff;
  		off += 2;
  		size -= 2;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
378
379
380
  	}
  
  	while (size > 3) {
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
381
  		u32 val;
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
382
  		pci_user_read_config_dword(dev, off, &val);
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
383
384
385
386
  		data[off - init_off] = val & 0xff;
  		data[off - init_off + 1] = (val >> 8) & 0xff;
  		data[off - init_off + 2] = (val >> 16) & 0xff;
  		data[off - init_off + 3] = (val >> 24) & 0xff;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
387
388
389
  		off += 4;
  		size -= 4;
  	}
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
390
391
  	if (size >= 2) {
  		u16 val;
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
392
  		pci_user_read_config_word(dev, off, &val);
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
393
394
395
396
397
398
399
400
  		data[off - init_off] = val & 0xff;
  		data[off - init_off + 1] = (val >> 8) & 0xff;
  		off += 2;
  		size -= 2;
  	}
  
  	if (size > 0) {
  		u8 val;
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
401
  		pci_user_read_config_byte(dev, off, &val);
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
402
  		data[off - init_off] = val;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
403
404
405
406
407
408
409
410
  		off++;
  		--size;
  	}
  
  	return count;
  }
  
  static ssize_t
91a690295   Zhang Rui   sysfs: add parame...
411
412
  pci_write_config(struct kobject *kobj, struct bin_attribute *bin_attr,
  		 char *buf, loff_t off, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
413
414
415
416
  {
  	struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj));
  	unsigned int size = count;
  	loff_t init_off = off;
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
417
  	u8 *data = (u8*) buf;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
418
419
420
421
422
423
424
  
  	if (off > dev->cfg_size)
  		return 0;
  	if (off + count > dev->cfg_size) {
  		size = dev->cfg_size - off;
  		count = size;
  	}
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
425
426
  	
  	if ((off & 1) && size) {
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
427
  		pci_user_write_config_byte(dev, off, data[off - init_off]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
428
  		off++;
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
429
  		size--;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
430
  	}
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
431
432
433
434
  	
  	if ((off & 3) && size > 2) {
  		u16 val = data[off - init_off];
  		val |= (u16) data[off - init_off + 1] << 8;
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
435
                  pci_user_write_config_word(dev, off, val);
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
436
437
438
                  off += 2;
                  size -= 2;
          }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
439
440
  
  	while (size > 3) {
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
441
442
443
444
  		u32 val = data[off - init_off];
  		val |= (u32) data[off - init_off + 1] << 8;
  		val |= (u32) data[off - init_off + 2] << 16;
  		val |= (u32) data[off - init_off + 3] << 24;
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
445
  		pci_user_write_config_dword(dev, off, val);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
446
447
448
  		off += 4;
  		size -= 4;
  	}
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
449
450
451
452
  	
  	if (size >= 2) {
  		u16 val = data[off - init_off];
  		val |= (u16) data[off - init_off + 1] << 8;
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
453
  		pci_user_write_config_word(dev, off, val);
4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
454
455
456
  		off += 2;
  		size -= 2;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
457

4c0619add   Sachin P Sant   [PATCH] PCI: fix ...
458
  	if (size) {
e04b0ea2e   Brian King   [PATCH] PCI: Bloc...
459
  		pci_user_write_config_byte(dev, off, data[off - init_off]);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
460
461
462
463
464
465
  		off++;
  		--size;
  	}
  
  	return count;
  }
94e610880   Ben Hutchings   PCI: Expose PCI V...
466
  static ssize_t
287d19ce2   Stephen Hemminger   PCI: revise VPD a...
467
468
  read_vpd_attr(struct kobject *kobj, struct bin_attribute *bin_attr,
  	      char *buf, loff_t off, size_t count)
94e610880   Ben Hutchings   PCI: Expose PCI V...
469
470
471
  {
  	struct pci_dev *dev =
  		to_pci_dev(container_of(kobj, struct device, kobj));
94e610880   Ben Hutchings   PCI: Expose PCI V...
472
473
474
475
476
  
  	if (off > bin_attr->size)
  		count = 0;
  	else if (count > bin_attr->size - off)
  		count = bin_attr->size - off;
94e610880   Ben Hutchings   PCI: Expose PCI V...
477

287d19ce2   Stephen Hemminger   PCI: revise VPD a...
478
  	return pci_read_vpd(dev, off, count, buf);
94e610880   Ben Hutchings   PCI: Expose PCI V...
479
480
481
  }
  
  static ssize_t
287d19ce2   Stephen Hemminger   PCI: revise VPD a...
482
483
  write_vpd_attr(struct kobject *kobj, struct bin_attribute *bin_attr,
  	       char *buf, loff_t off, size_t count)
94e610880   Ben Hutchings   PCI: Expose PCI V...
484
485
486
  {
  	struct pci_dev *dev =
  		to_pci_dev(container_of(kobj, struct device, kobj));
94e610880   Ben Hutchings   PCI: Expose PCI V...
487
488
489
490
491
  
  	if (off > bin_attr->size)
  		count = 0;
  	else if (count > bin_attr->size - off)
  		count = bin_attr->size - off;
94e610880   Ben Hutchings   PCI: Expose PCI V...
492

287d19ce2   Stephen Hemminger   PCI: revise VPD a...
493
  	return pci_write_vpd(dev, off, count, buf);
94e610880   Ben Hutchings   PCI: Expose PCI V...
494
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
495
496
497
498
499
500
501
502
503
504
505
  #ifdef HAVE_PCI_LEGACY
  /**
   * pci_read_legacy_io - read byte(s) from legacy I/O port space
   * @kobj: kobject corresponding to file to read from
   * @buf: buffer to store results
   * @off: offset into legacy I/O port space
   * @count: number of bytes to read
   *
   * Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific
   * callback routine (pci_legacy_read).
   */
f19aeb1f3   Benjamin Herrenschmidt   PCI: Add ability ...
506
  static ssize_t
91a690295   Zhang Rui   sysfs: add parame...
507
508
  pci_read_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr,
  		   char *buf, loff_t off, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
509
510
  {
          struct pci_bus *bus = to_pci_bus(container_of(kobj,
fd7d1ced2   Greg Kroah-Hartman   PCI: make pci_bus...
511
                                                        struct device,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
  						      kobj));
  
          /* Only support 1, 2 or 4 byte accesses */
          if (count != 1 && count != 2 && count != 4)
                  return -EINVAL;
  
          return pci_legacy_read(bus, off, (u32 *)buf, count);
  }
  
  /**
   * pci_write_legacy_io - write byte(s) to legacy I/O port space
   * @kobj: kobject corresponding to file to read from
   * @buf: buffer containing value to be written
   * @off: offset into legacy I/O port space
   * @count: number of bytes to write
   *
   * Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific
   * callback routine (pci_legacy_write).
   */
f19aeb1f3   Benjamin Herrenschmidt   PCI: Add ability ...
531
  static ssize_t
91a690295   Zhang Rui   sysfs: add parame...
532
533
  pci_write_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr,
  		    char *buf, loff_t off, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
534
535
  {
          struct pci_bus *bus = to_pci_bus(container_of(kobj,
fd7d1ced2   Greg Kroah-Hartman   PCI: make pci_bus...
536
  						      struct device,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
537
538
539
540
541
542
543
544
545
546
547
548
549
550
  						      kobj));
          /* Only support 1, 2 or 4 byte accesses */
          if (count != 1 && count != 2 && count != 4)
                  return -EINVAL;
  
          return pci_legacy_write(bus, off, *(u32 *)buf, count);
  }
  
  /**
   * pci_mmap_legacy_mem - map legacy PCI memory into user memory space
   * @kobj: kobject corresponding to device to be mapped
   * @attr: struct bin_attribute for this file
   * @vma: struct vm_area_struct passed to mmap
   *
f19aeb1f3   Benjamin Herrenschmidt   PCI: Add ability ...
551
   * Uses an arch specific callback, pci_mmap_legacy_mem_page_range, to mmap
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
552
553
554
   * legacy memory space (first meg of bus space) into application virtual
   * memory space.
   */
f19aeb1f3   Benjamin Herrenschmidt   PCI: Add ability ...
555
  static int
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
556
557
558
559
  pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr,
                      struct vm_area_struct *vma)
  {
          struct pci_bus *bus = to_pci_bus(container_of(kobj,
fd7d1ced2   Greg Kroah-Hartman   PCI: make pci_bus...
560
                                                        struct device,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
561
  						      kobj));
f19aeb1f3   Benjamin Herrenschmidt   PCI: Add ability ...
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
          return pci_mmap_legacy_page_range(bus, vma, pci_mmap_mem);
  }
  
  /**
   * pci_mmap_legacy_io - map legacy PCI IO into user memory space
   * @kobj: kobject corresponding to device to be mapped
   * @attr: struct bin_attribute for this file
   * @vma: struct vm_area_struct passed to mmap
   *
   * Uses an arch specific callback, pci_mmap_legacy_io_page_range, to mmap
   * legacy IO space (first meg of bus space) into application virtual
   * memory space. Returns -ENOSYS if the operation isn't supported
   */
  static int
  pci_mmap_legacy_io(struct kobject *kobj, struct bin_attribute *attr,
  		   struct vm_area_struct *vma)
  {
          struct pci_bus *bus = to_pci_bus(container_of(kobj,
                                                        struct device,
  						      kobj));
  
          return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io);
  }
  
  /**
10a0ef39f   Ivan Kokshaysky   PCI/alpha: pci sy...
587
588
589
590
591
592
593
594
595
596
597
598
599
   * pci_adjust_legacy_attr - adjustment of legacy file attributes
   * @b: bus to create files under
   * @mmap_type: I/O port or memory
   *
   * Stub implementation. Can be overridden by arch if necessary.
   */
  void __weak
  pci_adjust_legacy_attr(struct pci_bus *b, enum pci_mmap_state mmap_type)
  {
  	return;
  }
  
  /**
f19aeb1f3   Benjamin Herrenschmidt   PCI: Add ability ...
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
   * pci_create_legacy_files - create legacy I/O port and memory files
   * @b: bus to create files under
   *
   * Some platforms allow access to legacy I/O port and ISA memory space on
   * a per-bus basis.  This routine creates the files and ties them into
   * their associated read, write and mmap files from pci-sysfs.c
   *
   * On error unwind, but don't propogate the error to the caller
   * as it is ok to set up the PCI bus without these files.
   */
  void pci_create_legacy_files(struct pci_bus *b)
  {
  	int error;
  
  	b->legacy_io = kzalloc(sizeof(struct bin_attribute) * 2,
  			       GFP_ATOMIC);
  	if (!b->legacy_io)
  		goto kzalloc_err;
  
  	b->legacy_io->attr.name = "legacy_io";
  	b->legacy_io->size = 0xffff;
  	b->legacy_io->attr.mode = S_IRUSR | S_IWUSR;
  	b->legacy_io->read = pci_read_legacy_io;
  	b->legacy_io->write = pci_write_legacy_io;
  	b->legacy_io->mmap = pci_mmap_legacy_io;
10a0ef39f   Ivan Kokshaysky   PCI/alpha: pci sy...
625
  	pci_adjust_legacy_attr(b, pci_mmap_io);
f19aeb1f3   Benjamin Herrenschmidt   PCI: Add ability ...
626
627
628
629
630
631
632
633
634
635
  	error = device_create_bin_file(&b->dev, b->legacy_io);
  	if (error)
  		goto legacy_io_err;
  
  	/* Allocated above after the legacy_io struct */
  	b->legacy_mem = b->legacy_io + 1;
  	b->legacy_mem->attr.name = "legacy_mem";
  	b->legacy_mem->size = 1024*1024;
  	b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR;
  	b->legacy_mem->mmap = pci_mmap_legacy_mem;
10a0ef39f   Ivan Kokshaysky   PCI/alpha: pci sy...
636
  	pci_adjust_legacy_attr(b, pci_mmap_mem);
f19aeb1f3   Benjamin Herrenschmidt   PCI: Add ability ...
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
  	error = device_create_bin_file(&b->dev, b->legacy_mem);
  	if (error)
  		goto legacy_mem_err;
  
  	return;
  
  legacy_mem_err:
  	device_remove_bin_file(&b->dev, b->legacy_io);
  legacy_io_err:
  	kfree(b->legacy_io);
  	b->legacy_io = NULL;
  kzalloc_err:
  	printk(KERN_WARNING "pci: warning: could not create legacy I/O port "
  	       "and ISA memory resources to sysfs
  ");
  	return;
  }
  
  void pci_remove_legacy_files(struct pci_bus *b)
  {
  	if (b->legacy_io) {
  		device_remove_bin_file(&b->dev, b->legacy_io);
  		device_remove_bin_file(&b->dev, b->legacy_mem);
  		kfree(b->legacy_io); /* both are allocated here */
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
662
663
664
665
  }
  #endif /* HAVE_PCI_LEGACY */
  
  #ifdef HAVE_PCI_MMAP
b5ff7df3d   Linus Torvalds   Check mapped rang...
666

9eff02e20   Jesse Barnes   PCI: check mmap r...
667
  int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma)
b5ff7df3d   Linus Torvalds   Check mapped rang...
668
669
670
671
672
  {
  	unsigned long nr, start, size;
  
  	nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
  	start = vma->vm_pgoff;
88e7df0b7   Ed Swierk   PCI: fix range ch...
673
  	size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1;
b5ff7df3d   Linus Torvalds   Check mapped rang...
674
675
676
677
678
679
680
  	if (start < size && size - start >= nr)
  		return 1;
  	WARN(1, "process \"%s\" tried to map 0x%08lx-0x%08lx on %s BAR %d (size 0x%08lx)
  ",
  		current->comm, start, start+nr, pci_name(pdev), resno, size);
  	return 0;
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
681
682
683
684
685
  /**
   * pci_mmap_resource - map a PCI resource into user memory space
   * @kobj: kobject for mapping
   * @attr: struct bin_attribute for the file being mapped
   * @vma: struct vm_area_struct passed into the mmap
45aec1ae7   venkatesh.pallipadi@intel.com   x86: PAT export r...
686
   * @write_combine: 1 for write_combine mapping
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
687
688
   *
   * Use the regular PCI mapping routines to map a PCI resource into userspace.
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
689
690
691
   */
  static int
  pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
45aec1ae7   venkatesh.pallipadi@intel.com   x86: PAT export r...
692
  		  struct vm_area_struct *vma, int write_combine)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
693
694
695
696
697
  {
  	struct pci_dev *pdev = to_pci_dev(container_of(kobj,
  						       struct device, kobj));
  	struct resource *res = (struct resource *)attr->private;
  	enum pci_mmap_state mmap_type;
e31dd6e45   Greg Kroah-Hartman   [PATCH] 64bit res...
698
  	resource_size_t start, end;
2311b1f2b   Michael Ellerman   [PATCH] PCI: fix-...
699
  	int i;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
700

2311b1f2b   Michael Ellerman   [PATCH] PCI: fix-...
701
702
703
704
705
  	for (i = 0; i < PCI_ROM_RESOURCE; i++)
  		if (res == &pdev->resource[i])
  			break;
  	if (i >= PCI_ROM_RESOURCE)
  		return -ENODEV;
b5ff7df3d   Linus Torvalds   Check mapped rang...
706
707
  	if (!pci_mmap_fits(pdev, i, vma))
  		return -EINVAL;
2311b1f2b   Michael Ellerman   [PATCH] PCI: fix-...
708
709
710
711
712
713
  	/* pci_mmap_page_range() expects the same kind of entry as coming
  	 * from /proc/bus/pci/ which is a "user visible" value. If this is
  	 * different from the resource itself, arch will do necessary fixup.
  	 */
  	pci_resource_to_user(pdev, i, res, &start, &end);
  	vma->vm_pgoff += start >> PAGE_SHIFT;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
714
  	mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io;
e8de1481f   Arjan van de Ven   resource: allow M...
715
716
  	if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(start))
  		return -EINVAL;
45aec1ae7   venkatesh.pallipadi@intel.com   x86: PAT export r...
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
  	return pci_mmap_page_range(pdev, vma, mmap_type, write_combine);
  }
  
  static int
  pci_mmap_resource_uc(struct kobject *kobj, struct bin_attribute *attr,
  		     struct vm_area_struct *vma)
  {
  	return pci_mmap_resource(kobj, attr, vma, 0);
  }
  
  static int
  pci_mmap_resource_wc(struct kobject *kobj, struct bin_attribute *attr,
  		     struct vm_area_struct *vma)
  {
  	return pci_mmap_resource(kobj, attr, vma, 1);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
732
733
734
  }
  
  /**
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
   * pci_remove_resource_files - cleanup resource files
   * @dev: dev to cleanup
   *
   * If we created resource files for @dev, remove them from sysfs and
   * free their resources.
   */
  static void
  pci_remove_resource_files(struct pci_dev *pdev)
  {
  	int i;
  
  	for (i = 0; i < PCI_ROM_RESOURCE; i++) {
  		struct bin_attribute *res_attr;
  
  		res_attr = pdev->res_attr[i];
  		if (res_attr) {
  			sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
  			kfree(res_attr);
  		}
45aec1ae7   venkatesh.pallipadi@intel.com   x86: PAT export r...
754
755
756
757
758
759
  
  		res_attr = pdev->res_attr_wc[i];
  		if (res_attr) {
  			sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
  			kfree(res_attr);
  		}
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
760
761
  	}
  }
45aec1ae7   venkatesh.pallipadi@intel.com   x86: PAT export r...
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
  static int pci_create_attr(struct pci_dev *pdev, int num, int write_combine)
  {
  	/* allocate attribute structure, piggyback attribute name */
  	int name_len = write_combine ? 13 : 10;
  	struct bin_attribute *res_attr;
  	int retval;
  
  	res_attr = kzalloc(sizeof(*res_attr) + name_len, GFP_ATOMIC);
  	if (res_attr) {
  		char *res_attr_name = (char *)(res_attr + 1);
  
  		if (write_combine) {
  			pdev->res_attr_wc[num] = res_attr;
  			sprintf(res_attr_name, "resource%d_wc", num);
  			res_attr->mmap = pci_mmap_resource_wc;
  		} else {
  			pdev->res_attr[num] = res_attr;
  			sprintf(res_attr_name, "resource%d", num);
  			res_attr->mmap = pci_mmap_resource_uc;
  		}
  		res_attr->attr.name = res_attr_name;
  		res_attr->attr.mode = S_IRUSR | S_IWUSR;
  		res_attr->size = pci_resource_len(pdev, num);
  		res_attr->private = &pdev->resource[num];
  		retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
  	} else
  		retval = -ENOMEM;
  
  	return retval;
  }
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
792
  /**
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
793
794
795
796
797
   * pci_create_resource_files - create resource files in sysfs for @dev
   * @dev: dev in question
   *
   * Walk the resources in @dev creating files for each resource available.
   */
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
798
  static int pci_create_resource_files(struct pci_dev *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
799
800
  {
  	int i;
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
801
  	int retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
802
803
804
  
  	/* Expose the PCI resources from this device as files */
  	for (i = 0; i < PCI_ROM_RESOURCE; i++) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
805
806
807
808
  
  		/* skip empty resources */
  		if (!pci_resource_len(pdev, i))
  			continue;
45aec1ae7   venkatesh.pallipadi@intel.com   x86: PAT export r...
809
810
811
812
813
814
815
816
  		retval = pci_create_attr(pdev, i, 0);
  		/* for prefetchable resources, create a WC mappable file */
  		if (!retval && pdev->resource[i].flags & IORESOURCE_PREFETCH)
  			retval = pci_create_attr(pdev, i, 1);
  
  		if (retval) {
  			pci_remove_resource_files(pdev);
  			return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
817
818
  		}
  	}
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
819
  	return 0;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
820
821
  }
  #else /* !HAVE_PCI_MMAP */
10a0ef39f   Ivan Kokshaysky   PCI/alpha: pci sy...
822
823
  int __weak pci_create_resource_files(struct pci_dev *dev) { return 0; }
  void __weak pci_remove_resource_files(struct pci_dev *dev) { return; }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
824
825
826
827
828
829
830
831
832
833
834
835
  #endif /* HAVE_PCI_MMAP */
  
  /**
   * pci_write_rom - used to enable access to the PCI ROM display
   * @kobj: kernel object handle
   * @buf: user input
   * @off: file offset
   * @count: number of byte in input
   *
   * writing anything except 0 enables it
   */
  static ssize_t
91a690295   Zhang Rui   sysfs: add parame...
836
837
  pci_write_rom(struct kobject *kobj, struct bin_attribute *bin_attr,
  	      char *buf, loff_t off, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
  {
  	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
  
  	if ((off ==  0) && (*buf == '0') && (count == 2))
  		pdev->rom_attr_enabled = 0;
  	else
  		pdev->rom_attr_enabled = 1;
  
  	return count;
  }
  
  /**
   * pci_read_rom - read a PCI ROM
   * @kobj: kernel object handle
   * @buf: where to put the data we read from the ROM
   * @off: file offset
   * @count: number of bytes to read
   *
   * Put @count bytes starting at @off into @buf from the ROM in the PCI
   * device corresponding to @kobj.
   */
  static ssize_t
91a690295   Zhang Rui   sysfs: add parame...
860
861
  pci_read_rom(struct kobject *kobj, struct bin_attribute *bin_attr,
  	     char *buf, loff_t off, size_t count)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
862
863
864
865
866
867
868
869
870
  {
  	struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj));
  	void __iomem *rom;
  	size_t size;
  
  	if (!pdev->rom_attr_enabled)
  		return -EINVAL;
  	
  	rom = pci_map_rom(pdev, &size);	/* size starts out as PCI window size */
97c44836c   Timothy S. Nelson   PCI: return error...
871
872
  	if (!rom || !size)
  		return -EIO;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
  		
  	if (off >= size)
  		count = 0;
  	else {
  		if (off + count > size)
  			count = size - off;
  		
  		memcpy_fromio(buf, rom + off, count);
  	}
  	pci_unmap_rom(pdev, rom);
  		
  	return count;
  }
  
  static struct bin_attribute pci_config_attr = {
  	.attr =	{
  		.name = "config",
  		.mode = S_IRUGO | S_IWUSR,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
891
  	},
557848c3c   Zhao, Yu   PCI: replace cfg ...
892
  	.size = PCI_CFG_SPACE_SIZE,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
893
894
895
896
897
898
899
900
  	.read = pci_read_config,
  	.write = pci_write_config,
  };
  
  static struct bin_attribute pcie_config_attr = {
  	.attr =	{
  		.name = "config",
  		.mode = S_IRUGO | S_IWUSR,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
901
  	},
557848c3c   Zhao, Yu   PCI: replace cfg ...
902
  	.size = PCI_CFG_SPACE_EXP_SIZE,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
903
904
905
  	.read = pci_read_config,
  	.write = pci_write_config,
  };
a2cd52ca9   Michael Ellerman   PCI: Make pcibios...
906
  int __attribute__ ((weak)) pcibios_add_platform_entries(struct pci_dev *dev)
575e3348c   Michael Ellerman   PCI: Use a weak s...
907
  {
a2cd52ca9   Michael Ellerman   PCI: Make pcibios...
908
  	return 0;
575e3348c   Michael Ellerman   PCI: Use a weak s...
909
  }
280c73d36   Zhao, Yu   PCI: centralize t...
910
911
912
913
914
915
916
917
918
919
920
921
922
923
  static int pci_create_capabilities_sysfs(struct pci_dev *dev)
  {
  	int retval;
  	struct bin_attribute *attr;
  
  	/* If the device has VPD, try to expose it in sysfs. */
  	if (dev->vpd) {
  		attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
  		if (!attr)
  			return -ENOMEM;
  
  		attr->size = dev->vpd->len;
  		attr->attr.name = "vpd";
  		attr->attr.mode = S_IRUSR | S_IWUSR;
287d19ce2   Stephen Hemminger   PCI: revise VPD a...
924
925
  		attr->read = read_vpd_attr;
  		attr->write = write_vpd_attr;
280c73d36   Zhao, Yu   PCI: centralize t...
926
927
928
929
930
931
932
933
934
935
936
937
938
  		retval = sysfs_create_bin_file(&dev->dev.kobj, attr);
  		if (retval) {
  			kfree(dev->vpd->attr);
  			return retval;
  		}
  		dev->vpd->attr = attr;
  	}
  
  	/* Active State Power Management */
  	pcie_aspm_create_sysfs_dev_files(dev);
  
  	return 0;
  }
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
939
  int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
940
  {
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
941
  	int retval;
280c73d36   Zhao, Yu   PCI: centralize t...
942
943
  	int rom_size = 0;
  	struct bin_attribute *attr;
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
944

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
945
946
  	if (!sysfs_initialized)
  		return -EACCES;
557848c3c   Zhao, Yu   PCI: replace cfg ...
947
  	if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
948
  		retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
949
  	else
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
950
951
952
  		retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
  	if (retval)
  		goto err;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
953

b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
954
955
  	retval = pci_create_resource_files(pdev);
  	if (retval)
280c73d36   Zhao, Yu   PCI: centralize t...
956
957
958
959
960
961
  		goto err_config_file;
  
  	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
  		rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
  	else if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
  		rom_size = 0x20000;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
962
963
  
  	/* If the device has a ROM, try to expose it in sysfs. */
280c73d36   Zhao, Yu   PCI: centralize t...
964
  	if (rom_size) {
94e610880   Ben Hutchings   PCI: Expose PCI V...
965
  		attr = kzalloc(sizeof(*attr), GFP_ATOMIC);
280c73d36   Zhao, Yu   PCI: centralize t...
966
  		if (!attr) {
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
967
  			retval = -ENOMEM;
9890b12a4   Michael Ellerman   PCI: Free resourc...
968
  			goto err_resource_files;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
969
  		}
280c73d36   Zhao, Yu   PCI: centralize t...
970
971
972
973
974
975
976
977
978
979
980
  		attr->size = rom_size;
  		attr->attr.name = "rom";
  		attr->attr.mode = S_IRUSR;
  		attr->read = pci_read_rom;
  		attr->write = pci_write_rom;
  		retval = sysfs_create_bin_file(&pdev->dev.kobj, attr);
  		if (retval) {
  			kfree(attr);
  			goto err_resource_files;
  		}
  		pdev->rom_attr = attr;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
981
  	}
280c73d36   Zhao, Yu   PCI: centralize t...
982

217f45de3   Dave Airlie   PCI: expose boot ...
983
984
985
986
987
  	if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA) {
  		retval = device_create_file(&pdev->dev, &vga_attr);
  		if (retval)
  			goto err_rom_file;
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
988
  	/* add platform-specific attributes */
280c73d36   Zhao, Yu   PCI: centralize t...
989
990
  	retval = pcibios_add_platform_entries(pdev);
  	if (retval)
217f45de3   Dave Airlie   PCI: expose boot ...
991
  		goto err_vga_file;
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
992

280c73d36   Zhao, Yu   PCI: centralize t...
993
994
995
  	/* add sysfs entries for various capabilities */
  	retval = pci_create_capabilities_sysfs(pdev);
  	if (retval)
217f45de3   Dave Airlie   PCI: expose boot ...
996
  		goto err_vga_file;
7d715a6c1   Shaohua Li   PCI: add PCI Expr...
997

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
998
  	return 0;
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
999

217f45de3   Dave Airlie   PCI: expose boot ...
1000
1001
1002
  err_vga_file:
  	if ((pdev->class >> 8) == PCI_CLASS_DISPLAY_VGA)
  		device_remove_file(&pdev->dev, &vga_attr);
a2cd52ca9   Michael Ellerman   PCI: Make pcibios...
1003
  err_rom_file:
280c73d36   Zhao, Yu   PCI: centralize t...
1004
  	if (rom_size) {
94e610880   Ben Hutchings   PCI: Expose PCI V...
1005
  		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
280c73d36   Zhao, Yu   PCI: centralize t...
1006
1007
1008
  		kfree(pdev->rom_attr);
  		pdev->rom_attr = NULL;
  	}
9890b12a4   Michael Ellerman   PCI: Free resourc...
1009
1010
  err_resource_files:
  	pci_remove_resource_files(pdev);
94e610880   Ben Hutchings   PCI: Expose PCI V...
1011
  err_config_file:
557848c3c   Zhao, Yu   PCI: replace cfg ...
1012
  	if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
1013
1014
1015
1016
1017
  		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
  	else
  		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
  err:
  	return retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1018
  }
280c73d36   Zhao, Yu   PCI: centralize t...
1019
1020
1021
1022
1023
1024
1025
1026
1027
  static void pci_remove_capabilities_sysfs(struct pci_dev *dev)
  {
  	if (dev->vpd && dev->vpd->attr) {
  		sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr);
  		kfree(dev->vpd->attr);
  	}
  
  	pcie_aspm_remove_sysfs_dev_files(dev);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1028
1029
1030
1031
1032
1033
1034
1035
  /**
   * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files
   * @pdev: device whose entries we should free
   *
   * Cleanup when @pdev is removed from sysfs.
   */
  void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
  {
280c73d36   Zhao, Yu   PCI: centralize t...
1036
  	int rom_size = 0;
d67afe5ed   David Miller   [PATCH] pci: don'...
1037
1038
  	if (!sysfs_initialized)
  		return;
280c73d36   Zhao, Yu   PCI: centralize t...
1039
  	pci_remove_capabilities_sysfs(pdev);
7d715a6c1   Shaohua Li   PCI: add PCI Expr...
1040

557848c3c   Zhao, Yu   PCI: replace cfg ...
1041
  	if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1042
1043
1044
1045
1046
  		sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
  	else
  		sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
  
  	pci_remove_resource_files(pdev);
280c73d36   Zhao, Yu   PCI: centralize t...
1047
1048
1049
1050
1051
1052
1053
1054
  	if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
  		rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
  	else if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
  		rom_size = 0x20000;
  
  	if (rom_size && pdev->rom_attr) {
  		sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
  		kfree(pdev->rom_attr);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1055
1056
1057
1058
1059
1060
  	}
  }
  
  static int __init pci_sysfs_init(void)
  {
  	struct pci_dev *pdev = NULL;
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
1061
  	int retval;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1062
  	sysfs_initialized = 1;
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
1063
1064
  	for_each_pci_dev(pdev) {
  		retval = pci_create_sysfs_dev_files(pdev);
151fc5dfc   Julia Lawall   PCI: drivers/pci/...
1065
1066
  		if (retval) {
  			pci_dev_put(pdev);
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
1067
  			return retval;
151fc5dfc   Julia Lawall   PCI: drivers/pci/...
1068
  		}
b19441af1   Greg Kroah-Hartman   PCI: fix __must_c...
1069
  	}
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1070
1071
1072
  
  	return 0;
  }
40ee9e9f8   Jesse Barnes   PCI: fix sysfs ro...
1073
  late_initcall(pci_sysfs_init);