1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34#define pr_fmt(fmt) "acpiphp: " fmt
35
36#include <linux/init.h>
37#include <linux/module.h>
38#include <linux/moduleparam.h>
39
40#include <linux/kernel.h>
41#include <linux/pci.h>
42#include <linux/pci-acpi.h>
43#include <linux/pci_hotplug.h>
44#include <linux/slab.h>
45#include <linux/smp.h>
46#include "acpiphp.h"
47
48
49#define SLOT_NAME_SIZE 21
50
51bool acpiphp_disabled;
52
53
54static struct acpiphp_attention_info *attention_info;
55
56#define DRIVER_VERSION "0.5"
57#define DRIVER_AUTHOR "Greg Kroah-Hartman <gregkh@us.ibm.com>, Takayoshi Kochi <t-kochi@bq.jp.nec.com>, Matthew Wilcox <willy@hp.com>"
58#define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver"
59
60MODULE_AUTHOR(DRIVER_AUTHOR);
61MODULE_DESCRIPTION(DRIVER_DESC);
62MODULE_LICENSE("GPL");
63MODULE_PARM_DESC(disable, "disable acpiphp driver");
64module_param_named(disable, acpiphp_disabled, bool, 0444);
65
66
67EXPORT_SYMBOL_GPL(acpiphp_register_attention);
68EXPORT_SYMBOL_GPL(acpiphp_unregister_attention);
69
70static int enable_slot (struct hotplug_slot *slot);
71static int disable_slot (struct hotplug_slot *slot);
72static int set_attention_status (struct hotplug_slot *slot, u8 value);
73static int get_power_status (struct hotplug_slot *slot, u8 *value);
74static int get_attention_status (struct hotplug_slot *slot, u8 *value);
75static int get_latch_status (struct hotplug_slot *slot, u8 *value);
76static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
77
78static struct hotplug_slot_ops acpi_hotplug_slot_ops = {
79 .enable_slot = enable_slot,
80 .disable_slot = disable_slot,
81 .set_attention_status = set_attention_status,
82 .get_power_status = get_power_status,
83 .get_attention_status = get_attention_status,
84 .get_latch_status = get_latch_status,
85 .get_adapter_status = get_adapter_status,
86};
87
88
89
90
91
92
93
94
95
96int acpiphp_register_attention(struct acpiphp_attention_info *info)
97{
98 int retval = -EINVAL;
99
100 if (info && info->owner && info->set_attn &&
101 info->get_attn && !attention_info) {
102 retval = 0;
103 attention_info = info;
104 }
105 return retval;
106}
107
108
109
110
111
112
113
114
115
116
117int acpiphp_unregister_attention(struct acpiphp_attention_info *info)
118{
119 int retval = -EINVAL;
120
121 if (info && attention_info == info) {
122 attention_info = NULL;
123 retval = 0;
124 }
125 return retval;
126}
127
128
129
130
131
132
133
134
135static int enable_slot(struct hotplug_slot *hotplug_slot)
136{
137 struct slot *slot = hotplug_slot->private;
138
139 pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
140
141
142 return acpiphp_enable_slot(slot->acpi_slot);
143}
144
145
146
147
148
149
150
151
152static int disable_slot(struct hotplug_slot *hotplug_slot)
153{
154 struct slot *slot = hotplug_slot->private;
155
156 pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
157
158
159 return acpiphp_disable_and_eject_slot(slot->acpi_slot);
160}
161
162
163
164
165
166
167
168
169
170
171
172static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
173{
174 int retval = -ENODEV;
175
176 pr_debug("%s - physical_slot = %s\n", __func__,
177 hotplug_slot_name(hotplug_slot));
178
179 if (attention_info && try_module_get(attention_info->owner)) {
180 retval = attention_info->set_attn(hotplug_slot, status);
181 module_put(attention_info->owner);
182 } else
183 attention_info = NULL;
184 return retval;
185}
186
187
188
189
190
191
192
193
194
195
196static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
197{
198 struct slot *slot = hotplug_slot->private;
199
200 pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
201
202 *value = acpiphp_get_power_status(slot->acpi_slot);
203
204 return 0;
205}
206
207
208
209
210
211
212
213
214
215
216
217
218static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
219{
220 int retval = -EINVAL;
221
222 pr_debug("%s - physical_slot = %s\n", __func__,
223 hotplug_slot_name(hotplug_slot));
224
225 if (attention_info && try_module_get(attention_info->owner)) {
226 retval = attention_info->get_attn(hotplug_slot, value);
227 module_put(attention_info->owner);
228 } else
229 attention_info = NULL;
230 return retval;
231}
232
233
234
235
236
237
238
239
240
241
242static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
243{
244 struct slot *slot = hotplug_slot->private;
245
246 pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
247
248 *value = acpiphp_get_latch_status(slot->acpi_slot);
249
250 return 0;
251}
252
253
254
255
256
257
258
259
260
261
262static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
263{
264 struct slot *slot = hotplug_slot->private;
265
266 pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
267
268 *value = acpiphp_get_adapter_status(slot->acpi_slot);
269
270 return 0;
271}
272
273
274
275
276
277static void release_slot(struct hotplug_slot *hotplug_slot)
278{
279 struct slot *slot = hotplug_slot->private;
280
281 pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
282
283 kfree(slot->hotplug_slot);
284 kfree(slot);
285}
286
287
288int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
289 unsigned int sun)
290{
291 struct slot *slot;
292 int retval = -ENOMEM;
293 char name[SLOT_NAME_SIZE];
294
295 slot = kzalloc(sizeof(*slot), GFP_KERNEL);
296 if (!slot)
297 goto error;
298
299 slot->hotplug_slot = kzalloc(sizeof(*slot->hotplug_slot), GFP_KERNEL);
300 if (!slot->hotplug_slot)
301 goto error_slot;
302
303 slot->hotplug_slot->info = &slot->info;
304
305 slot->hotplug_slot->private = slot;
306 slot->hotplug_slot->release = &release_slot;
307 slot->hotplug_slot->ops = &acpi_hotplug_slot_ops;
308
309 slot->acpi_slot = acpiphp_slot;
310 slot->hotplug_slot->info->power_status = acpiphp_get_power_status(slot->acpi_slot);
311 slot->hotplug_slot->info->attention_status = 0;
312 slot->hotplug_slot->info->latch_status = acpiphp_get_latch_status(slot->acpi_slot);
313 slot->hotplug_slot->info->adapter_status = acpiphp_get_adapter_status(slot->acpi_slot);
314
315 acpiphp_slot->slot = slot;
316 slot->sun = sun;
317 snprintf(name, SLOT_NAME_SIZE, "%u", sun);
318
319 retval = pci_hp_register(slot->hotplug_slot, acpiphp_slot->bus,
320 acpiphp_slot->device, name);
321 if (retval == -EBUSY)
322 goto error_hpslot;
323 if (retval) {
324 pr_err("pci_hp_register failed with error %d\n", retval);
325 goto error_hpslot;
326 }
327
328 pr_info("Slot [%s] registered\n", slot_name(slot));
329
330 return 0;
331error_hpslot:
332 kfree(slot->hotplug_slot);
333error_slot:
334 kfree(slot);
335error:
336 return retval;
337}
338
339
340void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
341{
342 struct slot *slot = acpiphp_slot->slot;
343 int retval = 0;
344
345 pr_info("Slot [%s] unregistered\n", slot_name(slot));
346
347 retval = pci_hp_deregister(slot->hotplug_slot);
348 if (retval)
349 pr_err("pci_hp_deregister failed with error %d\n", retval);
350}
351
352
353void __init acpiphp_init(void)
354{
355 pr_info(DRIVER_DESC " version: " DRIVER_VERSION "%s\n",
356 acpiphp_disabled ? ", disabled by user; please report a bug"
357 : "");
358}
359