Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0+
2 : : /*
3 : : * PCI Express 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) 2003-2004 Intel Corporation
9 : : *
10 : : * All rights reserved.
11 : : *
12 : : * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
13 : : *
14 : : */
15 : :
16 : : #define dev_fmt(fmt) "pciehp: " fmt
17 : :
18 : : #include <linux/kernel.h>
19 : : #include <linux/types.h>
20 : : #include <linux/pm_runtime.h>
21 : : #include <linux/pci.h>
22 : : #include "pciehp.h"
23 : :
24 : : /* The following routines constitute the bulk of the
25 : : hotplug controller logic
26 : : */
27 : :
28 : : #define SAFE_REMOVAL true
29 : : #define SURPRISE_REMOVAL false
30 : :
31 : 0 : static void set_slot_off(struct controller *ctrl)
32 : : {
33 : : /*
34 : : * Turn off slot, turn on attention indicator, turn off power
35 : : * indicator
36 : : */
37 [ # # ]: 0 : if (POWER_CTRL(ctrl)) {
38 : 0 : pciehp_power_off_slot(ctrl);
39 : :
40 : : /*
41 : : * After turning power off, we must wait for at least 1 second
42 : : * before taking any action that relies on power having been
43 : : * removed from the slot/adapter.
44 : : */
45 : 0 : msleep(1000);
46 : : }
47 : :
48 : 0 : pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
49 : : PCI_EXP_SLTCTL_ATTN_IND_ON);
50 : 0 : }
51 : :
52 : : /**
53 : : * board_added - Called after a board has been added to the system.
54 : : * @ctrl: PCIe hotplug controller where board is added
55 : : *
56 : : * Turns power on for the board.
57 : : * Configures board.
58 : : */
59 : 0 : static int board_added(struct controller *ctrl)
60 : : {
61 : 0 : int retval = 0;
62 : 0 : struct pci_bus *parent = ctrl->pcie->port->subordinate;
63 : :
64 [ # # ]: 0 : if (POWER_CTRL(ctrl)) {
65 : : /* Power on slot */
66 : 0 : retval = pciehp_power_on_slot(ctrl);
67 [ # # ]: 0 : if (retval)
68 : : return retval;
69 : : }
70 : :
71 : 0 : pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK,
72 : : INDICATOR_NOOP);
73 : :
74 : : /* Check link training status */
75 : 0 : retval = pciehp_check_link_status(ctrl);
76 [ # # ]: 0 : if (retval) {
77 : 0 : ctrl_err(ctrl, "Failed to check link status\n");
78 : 0 : goto err_exit;
79 : : }
80 : :
81 : : /* Check for a power fault */
82 [ # # # # ]: 0 : if (ctrl->power_fault_detected || pciehp_query_power_fault(ctrl)) {
83 : 0 : ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl));
84 : 0 : retval = -EIO;
85 : 0 : goto err_exit;
86 : : }
87 : :
88 : 0 : retval = pciehp_configure_device(ctrl);
89 [ # # ]: 0 : if (retval) {
90 [ # # ]: 0 : if (retval != -EEXIST) {
91 : 0 : ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
92 : : pci_domain_nr(parent), parent->number);
93 : 0 : goto err_exit;
94 : : }
95 : : }
96 : :
97 : 0 : pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON,
98 : : PCI_EXP_SLTCTL_ATTN_IND_OFF);
99 : 0 : return 0;
100 : :
101 : 0 : err_exit:
102 : 0 : set_slot_off(ctrl);
103 : 0 : return retval;
104 : : }
105 : :
106 : : /**
107 : : * remove_board - Turn off slot and Power Indicator
108 : : * @ctrl: PCIe hotplug controller where board is being removed
109 : : * @safe_removal: whether the board is safely removed (versus surprise removed)
110 : : */
111 : 0 : static void remove_board(struct controller *ctrl, bool safe_removal)
112 : : {
113 : 0 : pciehp_unconfigure_device(ctrl, safe_removal);
114 : :
115 [ # # ]: 0 : if (POWER_CTRL(ctrl)) {
116 : 0 : pciehp_power_off_slot(ctrl);
117 : :
118 : : /*
119 : : * After turning power off, we must wait for at least 1 second
120 : : * before taking any action that relies on power having been
121 : : * removed from the slot/adapter.
122 : : */
123 : 0 : msleep(1000);
124 : :
125 : : /* Ignore link or presence changes caused by power off */
126 : 0 : atomic_and(~(PCI_EXP_SLTSTA_DLLSC | PCI_EXP_SLTSTA_PDC),
127 : : &ctrl->pending_events);
128 : : }
129 : :
130 : 0 : pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
131 : : INDICATOR_NOOP);
132 : 0 : }
133 : :
134 : : static int pciehp_enable_slot(struct controller *ctrl);
135 : : static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal);
136 : :
137 : 0 : void pciehp_request(struct controller *ctrl, int action)
138 : : {
139 : 0 : atomic_or(action, &ctrl->pending_events);
140 [ # # ]: 0 : if (!pciehp_poll_mode)
141 : 0 : irq_wake_thread(ctrl->pcie->irq, ctrl);
142 : 0 : }
143 : :
144 : 0 : void pciehp_queue_pushbutton_work(struct work_struct *work)
145 : : {
146 : 0 : struct controller *ctrl = container_of(work, struct controller,
147 : : button_work.work);
148 : :
149 : 0 : mutex_lock(&ctrl->state_lock);
150 [ # # # ]: 0 : switch (ctrl->state) {
151 : 0 : case BLINKINGOFF_STATE:
152 : 0 : pciehp_request(ctrl, DISABLE_SLOT);
153 : 0 : break;
154 : 0 : case BLINKINGON_STATE:
155 : 0 : pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
156 : 0 : break;
157 : : default:
158 : : break;
159 : : }
160 : 0 : mutex_unlock(&ctrl->state_lock);
161 : 0 : }
162 : :
163 : 0 : void pciehp_handle_button_press(struct controller *ctrl)
164 : : {
165 : 0 : mutex_lock(&ctrl->state_lock);
166 [ # # # ]: 0 : switch (ctrl->state) {
167 : 0 : case OFF_STATE:
168 : : case ON_STATE:
169 [ # # ]: 0 : if (ctrl->state == ON_STATE) {
170 : 0 : ctrl->state = BLINKINGOFF_STATE;
171 : 0 : ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
172 : : slot_name(ctrl));
173 : : } else {
174 : 0 : ctrl->state = BLINKINGON_STATE;
175 : 0 : ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n",
176 : : slot_name(ctrl));
177 : : }
178 : : /* blink power indicator and turn off attention */
179 : 0 : pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK,
180 : : PCI_EXP_SLTCTL_ATTN_IND_OFF);
181 : 0 : schedule_delayed_work(&ctrl->button_work, 5 * HZ);
182 : : break;
183 : : case BLINKINGOFF_STATE:
184 : : case BLINKINGON_STATE:
185 : : /*
186 : : * Cancel if we are still blinking; this means that we
187 : : * press the attention again before the 5 sec. limit
188 : : * expires to cancel hot-add or hot-remove
189 : : */
190 : 0 : ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(ctrl));
191 : 0 : cancel_delayed_work(&ctrl->button_work);
192 [ # # ]: 0 : if (ctrl->state == BLINKINGOFF_STATE) {
193 : 0 : ctrl->state = ON_STATE;
194 : 0 : pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON,
195 : : PCI_EXP_SLTCTL_ATTN_IND_OFF);
196 : : } else {
197 : 0 : ctrl->state = OFF_STATE;
198 : 0 : pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
199 : : PCI_EXP_SLTCTL_ATTN_IND_OFF);
200 : : }
201 : 0 : ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
202 : : slot_name(ctrl));
203 : 0 : break;
204 : 0 : default:
205 : 0 : ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
206 : : slot_name(ctrl), ctrl->state);
207 : 0 : break;
208 : : }
209 : 0 : mutex_unlock(&ctrl->state_lock);
210 : 0 : }
211 : :
212 : 0 : void pciehp_handle_disable_request(struct controller *ctrl)
213 : : {
214 : 0 : mutex_lock(&ctrl->state_lock);
215 [ # # ]: 0 : switch (ctrl->state) {
216 : 0 : case BLINKINGON_STATE:
217 : : case BLINKINGOFF_STATE:
218 : 0 : cancel_delayed_work(&ctrl->button_work);
219 : 0 : break;
220 : : }
221 : 0 : ctrl->state = POWEROFF_STATE;
222 : 0 : mutex_unlock(&ctrl->state_lock);
223 : :
224 : 0 : ctrl->request_result = pciehp_disable_slot(ctrl, SAFE_REMOVAL);
225 : 0 : }
226 : :
227 : 0 : void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events)
228 : : {
229 : 0 : int present, link_active;
230 : :
231 : : /*
232 : : * If the slot is on and presence or link has changed, turn it off.
233 : : * Even if it's occupied again, we cannot assume the card is the same.
234 : : */
235 : 0 : mutex_lock(&ctrl->state_lock);
236 [ # # # ]: 0 : switch (ctrl->state) {
237 : 0 : case BLINKINGOFF_STATE:
238 : 0 : cancel_delayed_work(&ctrl->button_work);
239 : : /* fall through */
240 : 0 : case ON_STATE:
241 : 0 : ctrl->state = POWEROFF_STATE;
242 : 0 : mutex_unlock(&ctrl->state_lock);
243 [ # # ]: 0 : if (events & PCI_EXP_SLTSTA_DLLSC)
244 : 0 : ctrl_info(ctrl, "Slot(%s): Link Down\n",
245 : : slot_name(ctrl));
246 [ # # ]: 0 : if (events & PCI_EXP_SLTSTA_PDC)
247 : 0 : ctrl_info(ctrl, "Slot(%s): Card not present\n",
248 : : slot_name(ctrl));
249 : 0 : pciehp_disable_slot(ctrl, SURPRISE_REMOVAL);
250 : 0 : break;
251 : 0 : default:
252 : 0 : mutex_unlock(&ctrl->state_lock);
253 : 0 : break;
254 : : }
255 : :
256 : : /* Turn the slot on if it's occupied or link is up */
257 : 0 : mutex_lock(&ctrl->state_lock);
258 : 0 : present = pciehp_card_present(ctrl);
259 : 0 : link_active = pciehp_check_link_active(ctrl);
260 [ # # ]: 0 : if (present <= 0 && link_active <= 0) {
261 : 0 : mutex_unlock(&ctrl->state_lock);
262 : 0 : return;
263 : : }
264 : :
265 [ # # # ]: 0 : switch (ctrl->state) {
266 : 0 : case BLINKINGON_STATE:
267 : 0 : cancel_delayed_work(&ctrl->button_work);
268 : : /* fall through */
269 : 0 : case OFF_STATE:
270 : 0 : ctrl->state = POWERON_STATE;
271 : 0 : mutex_unlock(&ctrl->state_lock);
272 [ # # ]: 0 : if (present)
273 : 0 : ctrl_info(ctrl, "Slot(%s): Card present\n",
274 : : slot_name(ctrl));
275 [ # # ]: 0 : if (link_active)
276 : 0 : ctrl_info(ctrl, "Slot(%s): Link Up\n",
277 : : slot_name(ctrl));
278 : 0 : ctrl->request_result = pciehp_enable_slot(ctrl);
279 : 0 : break;
280 : 0 : default:
281 : 0 : mutex_unlock(&ctrl->state_lock);
282 : 0 : break;
283 : : }
284 : : }
285 : :
286 : 0 : static int __pciehp_enable_slot(struct controller *ctrl)
287 : : {
288 : 0 : u8 getstatus = 0;
289 : :
290 [ # # ]: 0 : if (MRL_SENS(ctrl)) {
291 : 0 : pciehp_get_latch_status(ctrl, &getstatus);
292 [ # # ]: 0 : if (getstatus) {
293 : 0 : ctrl_info(ctrl, "Slot(%s): Latch open\n",
294 : : slot_name(ctrl));
295 : 0 : return -ENODEV;
296 : : }
297 : : }
298 : :
299 [ # # ]: 0 : if (POWER_CTRL(ctrl)) {
300 : 0 : pciehp_get_power_status(ctrl, &getstatus);
301 [ # # ]: 0 : if (getstatus) {
302 : 0 : ctrl_info(ctrl, "Slot(%s): Already enabled\n",
303 : : slot_name(ctrl));
304 : 0 : return 0;
305 : : }
306 : : }
307 : :
308 : 0 : return board_added(ctrl);
309 : : }
310 : :
311 : 0 : static int pciehp_enable_slot(struct controller *ctrl)
312 : : {
313 : 0 : int ret;
314 : :
315 : 0 : pm_runtime_get_sync(&ctrl->pcie->port->dev);
316 : 0 : ret = __pciehp_enable_slot(ctrl);
317 [ # # # # ]: 0 : if (ret && ATTN_BUTTN(ctrl))
318 : : /* may be blinking */
319 : 0 : pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
320 : : INDICATOR_NOOP);
321 : 0 : pm_runtime_put(&ctrl->pcie->port->dev);
322 : :
323 : 0 : mutex_lock(&ctrl->state_lock);
324 [ # # ]: 0 : ctrl->state = ret ? OFF_STATE : ON_STATE;
325 : 0 : mutex_unlock(&ctrl->state_lock);
326 : :
327 : 0 : return ret;
328 : : }
329 : :
330 : 0 : static int __pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
331 : : {
332 : 0 : u8 getstatus = 0;
333 : :
334 [ # # ]: 0 : if (POWER_CTRL(ctrl)) {
335 : 0 : pciehp_get_power_status(ctrl, &getstatus);
336 [ # # ]: 0 : if (!getstatus) {
337 : 0 : ctrl_info(ctrl, "Slot(%s): Already disabled\n",
338 : : slot_name(ctrl));
339 : 0 : return -EINVAL;
340 : : }
341 : : }
342 : :
343 : 0 : remove_board(ctrl, safe_removal);
344 : 0 : return 0;
345 : : }
346 : :
347 : 0 : static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
348 : : {
349 : 0 : int ret;
350 : :
351 : 0 : pm_runtime_get_sync(&ctrl->pcie->port->dev);
352 : 0 : ret = __pciehp_disable_slot(ctrl, safe_removal);
353 : 0 : pm_runtime_put(&ctrl->pcie->port->dev);
354 : :
355 : 0 : mutex_lock(&ctrl->state_lock);
356 : 0 : ctrl->state = OFF_STATE;
357 : 0 : mutex_unlock(&ctrl->state_lock);
358 : :
359 : 0 : return ret;
360 : : }
361 : :
362 : 0 : int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot)
363 : : {
364 : 0 : struct controller *ctrl = to_ctrl(hotplug_slot);
365 : :
366 : 0 : mutex_lock(&ctrl->state_lock);
367 [ # # # # ]: 0 : switch (ctrl->state) {
368 : 0 : case BLINKINGON_STATE:
369 : : case OFF_STATE:
370 : 0 : mutex_unlock(&ctrl->state_lock);
371 : : /*
372 : : * The IRQ thread becomes a no-op if the user pulls out the
373 : : * card before the thread wakes up, so initialize to -ENODEV.
374 : : */
375 : 0 : ctrl->request_result = -ENODEV;
376 : 0 : pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
377 [ # # # # : 0 : wait_event(ctrl->requester,
# # # # ]
378 : : !atomic_read(&ctrl->pending_events) &&
379 : : !ctrl->ist_running);
380 : 0 : return ctrl->request_result;
381 : : case POWERON_STATE:
382 : 0 : ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
383 : : slot_name(ctrl));
384 : 0 : break;
385 : : case BLINKINGOFF_STATE:
386 : : case ON_STATE:
387 : : case POWEROFF_STATE:
388 : 0 : ctrl_info(ctrl, "Slot(%s): Already enabled\n",
389 : : slot_name(ctrl));
390 : 0 : break;
391 : 0 : default:
392 : 0 : ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
393 : : slot_name(ctrl), ctrl->state);
394 : 0 : break;
395 : : }
396 : 0 : mutex_unlock(&ctrl->state_lock);
397 : :
398 : 0 : return -ENODEV;
399 : : }
400 : :
401 : 0 : int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot)
402 : : {
403 : 0 : struct controller *ctrl = to_ctrl(hotplug_slot);
404 : :
405 : 0 : mutex_lock(&ctrl->state_lock);
406 [ # # # # ]: 0 : switch (ctrl->state) {
407 : 0 : case BLINKINGOFF_STATE:
408 : : case ON_STATE:
409 : 0 : mutex_unlock(&ctrl->state_lock);
410 : 0 : pciehp_request(ctrl, DISABLE_SLOT);
411 [ # # # # : 0 : wait_event(ctrl->requester,
# # # # ]
412 : : !atomic_read(&ctrl->pending_events) &&
413 : : !ctrl->ist_running);
414 : 0 : return ctrl->request_result;
415 : : case POWEROFF_STATE:
416 : 0 : ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
417 : : slot_name(ctrl));
418 : 0 : break;
419 : : case BLINKINGON_STATE:
420 : : case OFF_STATE:
421 : : case POWERON_STATE:
422 : 0 : ctrl_info(ctrl, "Slot(%s): Already disabled\n",
423 : : slot_name(ctrl));
424 : 0 : break;
425 : 0 : default:
426 : 0 : ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
427 : : slot_name(ctrl), ctrl->state);
428 : 0 : break;
429 : : }
430 : 0 : mutex_unlock(&ctrl->state_lock);
431 : :
432 : 0 : return -ENODEV;
433 : : }
|