Commit | Line | Data |
---|---|---|
f46753c5 AC |
1 | /* |
2 | * drivers/pci/slot.c | |
3 | * Copyright (C) 2006 Matthew Wilcox <matthew@wil.cx> | |
4 | * Copyright (C) 2006-2008 Hewlett-Packard Development Company, L.P. | |
5 | * Alex Chiang <achiang@hp.com> | |
6 | */ | |
7 | ||
8 | #include <linux/kobject.h> | |
9 | #include <linux/pci.h> | |
10 | #include <linux/err.h> | |
11 | #include "pci.h" | |
12 | ||
13 | struct kset *pci_slots_kset; | |
14 | EXPORT_SYMBOL_GPL(pci_slots_kset); | |
15 | ||
16 | static ssize_t pci_slot_attr_show(struct kobject *kobj, | |
17 | struct attribute *attr, char *buf) | |
18 | { | |
19 | struct pci_slot *slot = to_pci_slot(kobj); | |
20 | struct pci_slot_attribute *attribute = to_pci_slot_attr(attr); | |
21 | return attribute->show ? attribute->show(slot, buf) : -EIO; | |
22 | } | |
23 | ||
24 | static ssize_t pci_slot_attr_store(struct kobject *kobj, | |
25 | struct attribute *attr, const char *buf, size_t len) | |
26 | { | |
27 | struct pci_slot *slot = to_pci_slot(kobj); | |
28 | struct pci_slot_attribute *attribute = to_pci_slot_attr(attr); | |
29 | return attribute->store ? attribute->store(slot, buf, len) : -EIO; | |
30 | } | |
31 | ||
32 | static struct sysfs_ops pci_slot_sysfs_ops = { | |
33 | .show = pci_slot_attr_show, | |
34 | .store = pci_slot_attr_store, | |
35 | }; | |
36 | ||
37 | static ssize_t address_read_file(struct pci_slot *slot, char *buf) | |
38 | { | |
39 | if (slot->number == 0xff) | |
40 | return sprintf(buf, "%04x:%02x\n", | |
41 | pci_domain_nr(slot->bus), | |
42 | slot->bus->number); | |
43 | else | |
44 | return sprintf(buf, "%04x:%02x:%02x\n", | |
45 | pci_domain_nr(slot->bus), | |
46 | slot->bus->number, | |
47 | slot->number); | |
48 | } | |
49 | ||
50 | static void pci_slot_release(struct kobject *kobj) | |
51 | { | |
52 | struct pci_slot *slot = to_pci_slot(kobj); | |
53 | ||
54 | pr_debug("%s: releasing pci_slot on %x:%d\n", __func__, | |
55 | slot->bus->number, slot->number); | |
56 | ||
57 | list_del(&slot->list); | |
58 | ||
59 | kfree(slot); | |
60 | } | |
61 | ||
62 | static struct pci_slot_attribute pci_slot_attr_address = | |
63 | __ATTR(address, (S_IFREG | S_IRUGO), address_read_file, NULL); | |
64 | ||
65 | static struct attribute *pci_slot_default_attrs[] = { | |
66 | &pci_slot_attr_address.attr, | |
67 | NULL, | |
68 | }; | |
69 | ||
70 | static struct kobj_type pci_slot_ktype = { | |
71 | .sysfs_ops = &pci_slot_sysfs_ops, | |
72 | .release = &pci_slot_release, | |
73 | .default_attrs = pci_slot_default_attrs, | |
74 | }; | |
75 | ||
76 | /** | |
77 | * pci_create_slot - create or increment refcount for physical PCI slot | |
78 | * @parent: struct pci_bus of parent bridge | |
79 | * @slot_nr: PCI_SLOT(pci_dev->devfn) or -1 for placeholder | |
80 | * @name: user visible string presented in /sys/bus/pci/slots/<name> | |
81 | * | |
82 | * PCI slots have first class attributes such as address, speed, width, | |
83 | * and a &struct pci_slot is used to manage them. This interface will | |
84 | * either return a new &struct pci_slot to the caller, or if the pci_slot | |
85 | * already exists, its refcount will be incremented. | |
86 | * | |
87 | * Slots are uniquely identified by a @pci_bus, @slot_nr, @name tuple. | |
88 | * | |
89 | * Placeholder slots: | |
90 | * In most cases, @pci_bus, @slot_nr will be sufficient to uniquely identify | |
91 | * a slot. There is one notable exception - pSeries (rpaphp), where the | |
92 | * @slot_nr cannot be determined until a device is actually inserted into | |
93 | * the slot. In this scenario, the caller may pass -1 for @slot_nr. | |
94 | * | |
95 | * The following semantics are imposed when the caller passes @slot_nr == | |
96 | * -1. First, the check for existing %struct pci_slot is skipped, as the | |
97 | * caller may know about several unpopulated slots on a given %struct | |
98 | * pci_bus, and each slot would have a @slot_nr of -1. Uniqueness for | |
99 | * these slots is then determined by the @name parameter. We expect | |
100 | * kobject_init_and_add() to warn us if the caller attempts to create | |
101 | * multiple slots with the same name. The other change in semantics is | |
102 | * user-visible, which is the 'address' parameter presented in sysfs will | |
103 | * consist solely of a dddd:bb tuple, where dddd is the PCI domain of the | |
104 | * %struct pci_bus and bb is the bus number. In other words, the devfn of | |
105 | * the 'placeholder' slot will not be displayed. | |
106 | */ | |
107 | ||
108 | struct pci_slot *pci_create_slot(struct pci_bus *parent, int slot_nr, | |
109 | const char *name) | |
110 | { | |
111 | struct pci_slot *slot; | |
112 | int err; | |
113 | ||
114 | down_write(&pci_bus_sem); | |
115 | ||
116 | if (slot_nr == -1) | |
117 | goto placeholder; | |
118 | ||
119 | /* If we've already created this slot, bump refcount and return. */ | |
120 | list_for_each_entry(slot, &parent->slots, list) { | |
121 | if (slot->number == slot_nr) { | |
122 | kobject_get(&slot->kobj); | |
123 | pr_debug("%s: inc refcount to %d on %04x:%02x:%02x\n", | |
124 | __func__, | |
125 | atomic_read(&slot->kobj.kref.refcount), | |
126 | pci_domain_nr(parent), parent->number, | |
127 | slot_nr); | |
128 | goto out; | |
129 | } | |
130 | } | |
131 | ||
132 | placeholder: | |
133 | slot = kzalloc(sizeof(*slot), GFP_KERNEL); | |
134 | if (!slot) { | |
135 | slot = ERR_PTR(-ENOMEM); | |
136 | goto out; | |
137 | } | |
138 | ||
139 | slot->bus = parent; | |
140 | slot->number = slot_nr; | |
141 | ||
142 | slot->kobj.kset = pci_slots_kset; | |
143 | err = kobject_init_and_add(&slot->kobj, &pci_slot_ktype, NULL, | |
144 | "%s", name); | |
145 | if (err) { | |
146 | printk(KERN_ERR "Unable to register kobject %s\n", name); | |
147 | goto err; | |
148 | } | |
149 | ||
150 | INIT_LIST_HEAD(&slot->list); | |
151 | list_add(&slot->list, &parent->slots); | |
152 | ||
153 | /* Don't care if debug printk has a -1 for slot_nr */ | |
154 | pr_debug("%s: created pci_slot on %04x:%02x:%02x\n", | |
155 | __func__, pci_domain_nr(parent), parent->number, slot_nr); | |
156 | ||
157 | out: | |
158 | up_write(&pci_bus_sem); | |
159 | return slot; | |
160 | err: | |
161 | kfree(slot); | |
162 | slot = ERR_PTR(err); | |
163 | goto out; | |
164 | } | |
165 | EXPORT_SYMBOL_GPL(pci_create_slot); | |
166 | ||
167 | /** | |
168 | * pci_update_slot_number - update %struct pci_slot -> number | |
169 | * @slot - %struct pci_slot to update | |
170 | * @slot_nr - new number for slot | |
171 | * | |
172 | * The primary purpose of this interface is to allow callers who earlier | |
173 | * created a placeholder slot in pci_create_slot() by passing a -1 as | |
174 | * slot_nr, to update their %struct pci_slot with the correct @slot_nr. | |
175 | */ | |
176 | ||
177 | void pci_update_slot_number(struct pci_slot *slot, int slot_nr) | |
178 | { | |
179 | int name_count = 0; | |
180 | struct pci_slot *tmp; | |
181 | ||
182 | down_write(&pci_bus_sem); | |
183 | ||
184 | list_for_each_entry(tmp, &slot->bus->slots, list) { | |
185 | WARN_ON(tmp->number == slot_nr); | |
186 | if (!strcmp(kobject_name(&tmp->kobj), kobject_name(&slot->kobj))) | |
187 | name_count++; | |
188 | } | |
189 | ||
190 | if (name_count > 1) | |
191 | printk(KERN_WARNING "pci_update_slot_number found %d slots with the same name: %s\n", name_count, kobject_name(&slot->kobj)); | |
192 | ||
193 | slot->number = slot_nr; | |
194 | up_write(&pci_bus_sem); | |
195 | } | |
196 | EXPORT_SYMBOL_GPL(pci_update_slot_number); | |
197 | ||
198 | /** | |
199 | * pci_destroy_slot - decrement refcount for physical PCI slot | |
200 | * @slot: struct pci_slot to decrement | |
201 | * | |
202 | * %struct pci_slot is refcounted, so destroying them is really easy; we | |
203 | * just call kobject_put on its kobj and let our release methods do the | |
204 | * rest. | |
205 | */ | |
206 | ||
207 | void pci_destroy_slot(struct pci_slot *slot) | |
208 | { | |
209 | pr_debug("%s: dec refcount to %d on %04x:%02x:%02x\n", __func__, | |
210 | atomic_read(&slot->kobj.kref.refcount) - 1, | |
211 | pci_domain_nr(slot->bus), slot->bus->number, slot->number); | |
212 | ||
213 | down_write(&pci_bus_sem); | |
214 | kobject_put(&slot->kobj); | |
215 | up_write(&pci_bus_sem); | |
216 | } | |
217 | EXPORT_SYMBOL_GPL(pci_destroy_slot); | |
218 | ||
219 | static int pci_slot_init(void) | |
220 | { | |
221 | struct kset *pci_bus_kset; | |
222 | ||
223 | pci_bus_kset = bus_get_kset(&pci_bus_type); | |
224 | pci_slots_kset = kset_create_and_add("slots", NULL, | |
225 | &pci_bus_kset->kobj); | |
226 | if (!pci_slots_kset) { | |
227 | printk(KERN_ERR "PCI: Slot initialization failure\n"); | |
228 | return -ENOMEM; | |
229 | } | |
230 | return 0; | |
231 | } | |
232 | ||
233 | subsys_initcall(pci_slot_init); |