Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0+
2 : : /*
3 : : * ACPI PCI Hot Plug Controller Driver
4 : : *
5 : : * Copyright (C) 1995,2001 Compaq Computer Corporation
6 : : * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
7 : : * Copyright (C) 2001 IBM Corp.
8 : : * Copyright (C) 2002 Hiroshi Aono (h-aono@ap.jp.nec.com)
9 : : * Copyright (C) 2002,2003 Takayoshi Kochi (t-kochi@bq.jp.nec.com)
10 : : * Copyright (C) 2002,2003 NEC Corporation
11 : : * Copyright (C) 2003-2005 Matthew Wilcox (willy@infradead.org)
12 : : * Copyright (C) 2003-2005 Hewlett Packard
13 : : *
14 : : * All rights reserved.
15 : : *
16 : : * Send feedback to <kristen.c.accardi@intel.com>
17 : : *
18 : : */
19 : :
20 : : #define pr_fmt(fmt) "acpiphp: " fmt
21 : :
22 : : #include <linux/init.h>
23 : : #include <linux/module.h>
24 : : #include <linux/moduleparam.h>
25 : :
26 : : #include <linux/kernel.h>
27 : : #include <linux/pci.h>
28 : : #include <linux/pci-acpi.h>
29 : : #include <linux/pci_hotplug.h>
30 : : #include <linux/slab.h>
31 : : #include <linux/smp.h>
32 : : #include "acpiphp.h"
33 : :
34 : : /* name size which is used for entries in pcihpfs */
35 : : #define SLOT_NAME_SIZE 21 /* {_SUN} */
36 : :
37 : : bool acpiphp_disabled;
38 : :
39 : : /* local variables */
40 : : static struct acpiphp_attention_info *attention_info;
41 : :
42 : : #define DRIVER_VERSION "0.5"
43 : : #define DRIVER_AUTHOR "Greg Kroah-Hartman <gregkh@us.ibm.com>, Takayoshi Kochi <t-kochi@bq.jp.nec.com>, Matthew Wilcox <willy@infradead.org>"
44 : : #define DRIVER_DESC "ACPI Hot Plug PCI Controller Driver"
45 : :
46 : : MODULE_AUTHOR(DRIVER_AUTHOR);
47 : : MODULE_DESCRIPTION(DRIVER_DESC);
48 : : MODULE_LICENSE("GPL");
49 : : MODULE_PARM_DESC(disable, "disable acpiphp driver");
50 : : module_param_named(disable, acpiphp_disabled, bool, 0444);
51 : :
52 : : static int enable_slot(struct hotplug_slot *slot);
53 : : static int disable_slot(struct hotplug_slot *slot);
54 : : static int set_attention_status(struct hotplug_slot *slot, u8 value);
55 : : static int get_power_status(struct hotplug_slot *slot, u8 *value);
56 : : static int get_attention_status(struct hotplug_slot *slot, u8 *value);
57 : : static int get_latch_status(struct hotplug_slot *slot, u8 *value);
58 : : static int get_adapter_status(struct hotplug_slot *slot, u8 *value);
59 : :
60 : : static const struct hotplug_slot_ops acpi_hotplug_slot_ops = {
61 : : .enable_slot = enable_slot,
62 : : .disable_slot = disable_slot,
63 : : .set_attention_status = set_attention_status,
64 : : .get_power_status = get_power_status,
65 : : .get_attention_status = get_attention_status,
66 : : .get_latch_status = get_latch_status,
67 : : .get_adapter_status = get_adapter_status,
68 : : };
69 : :
70 : : /**
71 : : * acpiphp_register_attention - set attention LED callback
72 : : * @info: must be completely filled with LED callbacks
73 : : *
74 : : * Description: This is used to register a hardware specific ACPI
75 : : * driver that manipulates the attention LED. All the fields in
76 : : * info must be set.
77 : : */
78 : 0 : int acpiphp_register_attention(struct acpiphp_attention_info *info)
79 : : {
80 : 0 : int retval = -EINVAL;
81 : :
82 [ # # # # : 0 : if (info && info->owner && info->set_attn &&
# # ]
83 [ # # # # ]: 0 : info->get_attn && !attention_info) {
84 : 0 : retval = 0;
85 : 0 : attention_info = info;
86 : : }
87 : 0 : return retval;
88 : : }
89 : : EXPORT_SYMBOL_GPL(acpiphp_register_attention);
90 : :
91 : :
92 : : /**
93 : : * acpiphp_unregister_attention - unset attention LED callback
94 : : * @info: must match the pointer used to register
95 : : *
96 : : * Description: This is used to un-register a hardware specific acpi
97 : : * driver that manipulates the attention LED. The pointer to the
98 : : * info struct must be the same as the one used to set it.
99 : : */
100 : 0 : int acpiphp_unregister_attention(struct acpiphp_attention_info *info)
101 : : {
102 : 0 : int retval = -EINVAL;
103 : :
104 [ # # # # ]: 0 : if (info && attention_info == info) {
105 : 0 : attention_info = NULL;
106 : 0 : retval = 0;
107 : : }
108 : 0 : return retval;
109 : : }
110 : : EXPORT_SYMBOL_GPL(acpiphp_unregister_attention);
111 : :
112 : :
113 : : /**
114 : : * enable_slot - power on and enable a slot
115 : : * @hotplug_slot: slot to enable
116 : : *
117 : : * Actual tasks are done in acpiphp_enable_slot()
118 : : */
119 : 0 : static int enable_slot(struct hotplug_slot *hotplug_slot)
120 : : {
121 : 0 : struct slot *slot = to_slot(hotplug_slot);
122 : :
123 : 0 : pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
124 : :
125 : : /* enable the specified slot */
126 : 0 : return acpiphp_enable_slot(slot->acpi_slot);
127 : : }
128 : :
129 : :
130 : : /**
131 : : * disable_slot - disable and power off a slot
132 : : * @hotplug_slot: slot to disable
133 : : *
134 : : * Actual tasks are done in acpiphp_disable_slot()
135 : : */
136 : 0 : static int disable_slot(struct hotplug_slot *hotplug_slot)
137 : : {
138 : 0 : struct slot *slot = to_slot(hotplug_slot);
139 : :
140 : 0 : pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
141 : :
142 : : /* disable the specified slot */
143 : 0 : return acpiphp_disable_slot(slot->acpi_slot);
144 : : }
145 : :
146 : :
147 : : /**
148 : : * set_attention_status - set attention LED
149 : : * @hotplug_slot: slot to set attention LED on
150 : : * @status: value to set attention LED to (0 or 1)
151 : : *
152 : : * attention status LED, so we use a callback that
153 : : * was registered with us. This allows hardware specific
154 : : * ACPI implementations to blink the light for us.
155 : : */
156 : 0 : static int set_attention_status(struct hotplug_slot *hotplug_slot, u8 status)
157 : : {
158 : 0 : int retval = -ENODEV;
159 : :
160 : 0 : pr_debug("%s - physical_slot = %s\n", __func__,
161 : : hotplug_slot_name(hotplug_slot));
162 : :
163 [ # # # # ]: 0 : if (attention_info && try_module_get(attention_info->owner)) {
164 : 0 : retval = attention_info->set_attn(hotplug_slot, status);
165 : 0 : module_put(attention_info->owner);
166 : : } else
167 : 0 : attention_info = NULL;
168 : 0 : return retval;
169 : : }
170 : :
171 : :
172 : : /**
173 : : * get_power_status - get power status of a slot
174 : : * @hotplug_slot: slot to get status
175 : : * @value: pointer to store status
176 : : *
177 : : * Some platforms may not implement _STA method properly.
178 : : * In that case, the value returned may not be reliable.
179 : : */
180 : 0 : static int get_power_status(struct hotplug_slot *hotplug_slot, u8 *value)
181 : : {
182 : 0 : struct slot *slot = to_slot(hotplug_slot);
183 : :
184 : 0 : pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
185 : :
186 : 0 : *value = acpiphp_get_power_status(slot->acpi_slot);
187 : :
188 : 0 : return 0;
189 : : }
190 : :
191 : :
192 : : /**
193 : : * get_attention_status - get attention LED status
194 : : * @hotplug_slot: slot to get status from
195 : : * @value: returns with value of attention LED
196 : : *
197 : : * ACPI doesn't have known method to determine the state
198 : : * of the attention status LED, so we use a callback that
199 : : * was registered with us. This allows hardware specific
200 : : * ACPI implementations to determine its state.
201 : : */
202 : 0 : static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value)
203 : : {
204 : 0 : int retval = -EINVAL;
205 : :
206 : 0 : pr_debug("%s - physical_slot = %s\n", __func__,
207 : : hotplug_slot_name(hotplug_slot));
208 : :
209 [ # # # # ]: 0 : if (attention_info && try_module_get(attention_info->owner)) {
210 : 0 : retval = attention_info->get_attn(hotplug_slot, value);
211 : 0 : module_put(attention_info->owner);
212 : : } else
213 : 0 : attention_info = NULL;
214 : 0 : return retval;
215 : : }
216 : :
217 : :
218 : : /**
219 : : * get_latch_status - get latch status of a slot
220 : : * @hotplug_slot: slot to get status
221 : : * @value: pointer to store status
222 : : *
223 : : * ACPI doesn't provide any formal means to access latch status.
224 : : * Instead, we fake latch status from _STA.
225 : : */
226 : 0 : static int get_latch_status(struct hotplug_slot *hotplug_slot, u8 *value)
227 : : {
228 : 0 : struct slot *slot = to_slot(hotplug_slot);
229 : :
230 : 0 : pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
231 : :
232 : 0 : *value = acpiphp_get_latch_status(slot->acpi_slot);
233 : :
234 : 0 : return 0;
235 : : }
236 : :
237 : :
238 : : /**
239 : : * get_adapter_status - get adapter status of a slot
240 : : * @hotplug_slot: slot to get status
241 : : * @value: pointer to store status
242 : : *
243 : : * ACPI doesn't provide any formal means to access adapter status.
244 : : * Instead, we fake adapter status from _STA.
245 : : */
246 : 0 : static int get_adapter_status(struct hotplug_slot *hotplug_slot, u8 *value)
247 : : {
248 : 0 : struct slot *slot = to_slot(hotplug_slot);
249 : :
250 : 0 : pr_debug("%s - physical_slot = %s\n", __func__, slot_name(slot));
251 : :
252 : 0 : *value = acpiphp_get_adapter_status(slot->acpi_slot);
253 : :
254 : 0 : return 0;
255 : : }
256 : :
257 : : /* callback routine to initialize 'struct slot' for each slot */
258 : 319 : int acpiphp_register_hotplug_slot(struct acpiphp_slot *acpiphp_slot,
259 : : unsigned int sun)
260 : : {
261 : 319 : struct slot *slot;
262 : 319 : int retval = -ENOMEM;
263 : 319 : char name[SLOT_NAME_SIZE];
264 : :
265 : 319 : slot = kzalloc(sizeof(*slot), GFP_KERNEL);
266 [ - + ]: 319 : if (!slot)
267 : 0 : goto error;
268 : :
269 : 319 : slot->hotplug_slot.ops = &acpi_hotplug_slot_ops;
270 : :
271 : 319 : slot->acpi_slot = acpiphp_slot;
272 : :
273 : 319 : acpiphp_slot->slot = slot;
274 : 319 : slot->sun = sun;
275 : 319 : snprintf(name, SLOT_NAME_SIZE, "%u", sun);
276 : :
277 : 319 : retval = pci_hp_register(&slot->hotplug_slot, acpiphp_slot->bus,
278 : : acpiphp_slot->device, name);
279 [ - + ]: 319 : if (retval == -EBUSY)
280 : 0 : goto error_slot;
281 [ - + ]: 319 : if (retval) {
282 : 0 : pr_err("pci_hp_register failed with error %d\n", retval);
283 : 0 : goto error_slot;
284 : : }
285 : :
286 : 319 : pr_info("Slot [%s] registered\n", slot_name(slot));
287 : :
288 : 319 : return 0;
289 : 0 : error_slot:
290 : 0 : kfree(slot);
291 : : error:
292 : : return retval;
293 : : }
294 : :
295 : :
296 : 0 : void acpiphp_unregister_hotplug_slot(struct acpiphp_slot *acpiphp_slot)
297 : : {
298 : 0 : struct slot *slot = acpiphp_slot->slot;
299 : :
300 : 0 : pr_info("Slot [%s] unregistered\n", slot_name(slot));
301 : :
302 : 0 : pci_hp_deregister(&slot->hotplug_slot);
303 : 0 : kfree(slot);
304 : 0 : }
305 : :
306 : :
307 : 11 : void __init acpiphp_init(void)
308 : : {
309 [ + - ]: 22 : pr_info(DRIVER_DESC " version: " DRIVER_VERSION "%s\n",
310 : : acpiphp_disabled ? ", disabled by user; please report a bug"
311 : : : "");
312 : 11 : }
|