]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - drivers/pci/hotplug/pciehp_ctrl.c
PCI: pciehp: Track enable/disable status
[mirror_ubuntu-jammy-kernel.git] / drivers / pci / hotplug / pciehp_ctrl.c
CommitLineData
736759ef 1// SPDX-License-Identifier: GPL-2.0+
1da177e4
LT
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 *
8cf4c195 12 * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
1da177e4
LT
13 *
14 */
15
1da177e4
LT
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/types.h>
5a0e3ad6 19#include <linux/slab.h>
1da177e4
LT
20#include <linux/pci.h>
21#include "../pci.h"
22#include "pciehp.h"
1da177e4 23
36ed27b0 24/* The following routines constitute the bulk of the
1da177e4
LT
25 hotplug controller logic
26 */
27
3c78bc61 28static void set_slot_off(struct controller *ctrl, struct slot *pslot)
1da177e4 29{
1da177e4 30 /* turn off slot, turn on Amber LED, turn off Green LED if supported*/
ae416e6b 31 if (POWER_CTRL(ctrl)) {
6dae6202
BH
32 pciehp_power_off_slot(pslot);
33
c7b4fee3
KK
34 /*
35 * After turning power off, we must wait for at least 1 second
36 * before taking any action that relies on power having been
37 * removed from the slot/adapter.
38 */
39 msleep(1000);
1da177e4
LT
40 }
41
af9ab791
BH
42 pciehp_green_led_off(pslot);
43 pciehp_set_attention_status(pslot, 1);
1da177e4
LT
44}
45
46/**
47 * board_added - Called after a board has been added to the system.
26e6c66e 48 * @p_slot: &slot where board is added
1da177e4 49 *
26e6c66e
RD
50 * Turns power on for the board.
51 * Configures board.
1da177e4 52 */
ed6cbcf2 53static int board_added(struct slot *p_slot)
1da177e4 54{
44ef4cef 55 int retval = 0;
ca22a5e4 56 struct controller *ctrl = p_slot->ctrl;
385e2491 57 struct pci_bus *parent = ctrl->pcie->port->subordinate;
1da177e4 58
ae416e6b 59 if (POWER_CTRL(ctrl)) {
1da177e4 60 /* Power on slot */
82a9e79e 61 retval = pciehp_power_on_slot(p_slot);
44ef4cef
KK
62 if (retval)
63 return retval;
1da177e4 64 }
36ed27b0 65
af9ab791 66 pciehp_green_led_blink(p_slot);
1da177e4 67
44ef4cef 68 /* Check link training status */
82a9e79e 69 retval = pciehp_check_link_status(ctrl);
44ef4cef 70 if (retval) {
18b341b7 71 ctrl_err(ctrl, "Failed to check link status\n");
5651c48c 72 goto err_exit;
1da177e4
LT
73 }
74
1da177e4 75 /* Check for a power fault */
5651c48c 76 if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) {
6e49b304 77 ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(p_slot));
d9fb42a8 78 retval = -EIO;
71b720c0 79 goto err_exit;
1da177e4
LT
80 }
81
44ef4cef
KK
82 retval = pciehp_configure_device(p_slot);
83 if (retval) {
49902239
MW
84 if (retval != -EEXIST) {
85 ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
86 pci_domain_nr(parent), parent->number);
50277c8b 87 goto err_exit;
49902239 88 }
71b720c0 89 }
1da177e4 90
af9ab791 91 pciehp_green_led_on(p_slot);
8b7c8b46 92 pciehp_set_attention_status(p_slot, 0);
1da177e4 93 return 0;
71b720c0
RS
94
95err_exit:
96 set_slot_off(ctrl, p_slot);
44ef4cef 97 return retval;
1da177e4
LT
98}
99
1da177e4 100/**
26e6c66e
RD
101 * remove_board - Turns off slot and LEDs
102 * @p_slot: slot where board is being removed
1da177e4 103 */
1d2e2673 104static void remove_board(struct slot *p_slot)
1da177e4 105{
ca22a5e4 106 struct controller *ctrl = p_slot->ctrl;
1da177e4 107
1d2e2673 108 pciehp_unconfigure_device(p_slot);
1da177e4 109
ae416e6b 110 if (POWER_CTRL(ctrl)) {
6dae6202
BH
111 pciehp_power_off_slot(p_slot);
112
c7b4fee3
KK
113 /*
114 * After turning power off, we must wait for at least 1 second
115 * before taking any action that relies on power having been
116 * removed from the slot/adapter.
117 */
118 msleep(1000);
1da177e4
LT
119 }
120
82a9e79e 121 /* turn off Green LED */
af9ab791 122 pciehp_green_led_off(p_slot);
1da177e4
LT
123}
124
bee67756
GR
125void pciehp_queue_pushbutton_work(struct work_struct *work)
126{
127 struct slot *p_slot = container_of(work, struct slot, work.work);
1da177e4 128
5d386e1a
KK
129 mutex_lock(&p_slot->lock);
130 switch (p_slot->state) {
131 case BLINKINGOFF_STATE:
0e94916e
LW
132 p_slot->state = POWEROFF_STATE;
133 mutex_unlock(&p_slot->lock);
134 pciehp_disable_slot(p_slot);
135 return;
5d386e1a 136 case BLINKINGON_STATE:
0e94916e
LW
137 p_slot->state = POWERON_STATE;
138 mutex_unlock(&p_slot->lock);
139 pciehp_enable_slot(p_slot);
140 return;
5d386e1a 141 default:
bee67756 142 break;
1da177e4 143 }
5d386e1a 144 mutex_unlock(&p_slot->lock);
1da177e4
LT
145}
146
0e94916e 147void pciehp_handle_button_press(struct slot *p_slot)
1da177e4 148{
5d386e1a 149 struct controller *ctrl = p_slot->ctrl;
1da177e4 150
0e94916e 151 mutex_lock(&p_slot->lock);
5d386e1a 152 switch (p_slot->state) {
9590192f
LW
153 case OFF_STATE:
154 case ON_STATE:
155 if (p_slot->state == ON_STATE) {
5d386e1a 156 p_slot->state = BLINKINGOFF_STATE;
6e49b304 157 ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
227f0647 158 slot_name(p_slot));
5d386e1a
KK
159 } else {
160 p_slot->state = BLINKINGON_STATE;
6e49b304 161 ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n",
227f0647 162 slot_name(p_slot));
5d386e1a
KK
163 }
164 /* blink green LED and turn off amber */
af9ab791
BH
165 pciehp_green_led_blink(p_slot);
166 pciehp_set_attention_status(p_slot, 0);
55a6b7a6 167 schedule_delayed_work(&p_slot->work, 5 * HZ);
5d386e1a
KK
168 break;
169 case BLINKINGOFF_STATE:
170 case BLINKINGON_STATE:
171 /*
172 * Cancel if we are still blinking; this means that we
173 * press the attention again before the 5 sec. limit
174 * expires to cancel hot-add or hot-remove
175 */
6e49b304 176 ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot));
5d386e1a 177 cancel_delayed_work(&p_slot->work);
9590192f
LW
178 if (p_slot->state == BLINKINGOFF_STATE) {
179 p_slot->state = ON_STATE;
af9ab791 180 pciehp_green_led_on(p_slot);
9590192f
LW
181 } else {
182 p_slot->state = OFF_STATE;
af9ab791 183 pciehp_green_led_off(p_slot);
9590192f 184 }
af9ab791 185 pciehp_set_attention_status(p_slot, 0);
6e49b304 186 ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
227f0647 187 slot_name(p_slot));
5d386e1a
KK
188 break;
189 case POWEROFF_STATE:
190 case POWERON_STATE:
191 /*
192 * Ignore if the slot is on power-on or power-off state;
193 * this means that the previous attention button action
194 * to hot-add or hot-remove is undergoing
195 */
6e49b304
BH
196 ctrl_info(ctrl, "Slot(%s): Button ignored\n",
197 slot_name(p_slot));
5d386e1a
KK
198 break;
199 default:
6e49b304
BH
200 ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
201 slot_name(p_slot), p_slot->state);
5d386e1a 202 break;
1da177e4 203 }
0e94916e 204 mutex_unlock(&p_slot->lock);
1da177e4
LT
205}
206
0e94916e 207void pciehp_handle_link_change(struct slot *p_slot)
e48f1b67
RJ
208{
209 struct controller *ctrl = p_slot->ctrl;
0e94916e
LW
210 bool link_active;
211
212 mutex_lock(&p_slot->lock);
213 link_active = pciehp_check_link_active(ctrl);
e48f1b67
RJ
214
215 switch (p_slot->state) {
216 case BLINKINGON_STATE:
217 case BLINKINGOFF_STATE:
218 cancel_delayed_work(&p_slot->work);
219 /* Fall through */
9590192f
LW
220 case ON_STATE:
221 case OFF_STATE:
0e94916e
LW
222 if (link_active) {
223 p_slot->state = POWERON_STATE;
224 mutex_unlock(&p_slot->lock);
225 ctrl_info(ctrl, "Slot(%s): Link Up\n", slot_name(p_slot));
226 pciehp_enable_slot(p_slot);
227 } else {
228 p_slot->state = POWEROFF_STATE;
229 mutex_unlock(&p_slot->lock);
230 ctrl_info(ctrl, "Slot(%s): Link Down\n", slot_name(p_slot));
231 pciehp_disable_slot(p_slot);
232 }
233 return;
e48f1b67
RJ
234 break;
235 case POWERON_STATE:
0e94916e 236 if (link_active) {
6e49b304 237 ctrl_info(ctrl, "Slot(%s): Link Up event ignored; already powering on\n",
e48f1b67 238 slot_name(p_slot));
e48f1b67 239 } else {
0e94916e
LW
240 p_slot->state = POWEROFF_STATE;
241 mutex_unlock(&p_slot->lock);
6e49b304 242 ctrl_info(ctrl, "Slot(%s): Link Down event queued; currently getting powered on\n",
e48f1b67 243 slot_name(p_slot));
0e94916e
LW
244 pciehp_disable_slot(p_slot);
245 return;
e48f1b67
RJ
246 }
247 break;
248 case POWEROFF_STATE:
0e94916e
LW
249 if (link_active) {
250 p_slot->state = POWERON_STATE;
251 mutex_unlock(&p_slot->lock);
6e49b304 252 ctrl_info(ctrl, "Slot(%s): Link Up event queued; currently getting powered off\n",
e48f1b67 253 slot_name(p_slot));
0e94916e
LW
254 pciehp_enable_slot(p_slot);
255 return;
e48f1b67 256 } else {
6e49b304 257 ctrl_info(ctrl, "Slot(%s): Link Down event ignored; already powering off\n",
e48f1b67 258 slot_name(p_slot));
e48f1b67
RJ
259 }
260 break;
261 default:
6e49b304
BH
262 ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
263 slot_name(p_slot), p_slot->state);
e48f1b67
RJ
264 break;
265 }
0e94916e 266 mutex_unlock(&p_slot->lock);
e48f1b67
RJ
267}
268
0e94916e 269void pciehp_handle_presence_change(struct slot *slot)
5d386e1a 270{
0e94916e
LW
271 struct controller *ctrl = slot->ctrl;
272 u8 present;
273
274 mutex_lock(&slot->lock);
275 pciehp_get_adapter_status(slot, &present);
276 ctrl_info(ctrl, "Slot(%s): Card %spresent\n", slot_name(slot),
277 present ? "" : "not ");
278
279 if (present) {
280 slot->state = POWERON_STATE;
281 mutex_unlock(&slot->lock);
282 pciehp_enable_slot(slot);
283 } else {
284 slot->state = POWEROFF_STATE;
285 mutex_unlock(&slot->lock);
286 pciehp_disable_slot(slot);
5d386e1a 287 }
5d386e1a
KK
288}
289
50b52fde
RJ
290/*
291 * Note: This function must be called with slot->hotplug_lock held
292 */
b0ccd9dd 293static int __pciehp_enable_slot(struct slot *p_slot)
1da177e4
LT
294{
295 u8 getstatus = 0;
7f2feec1 296 struct controller *ctrl = p_slot->ctrl;
1da177e4 297
6dae6202
BH
298 pciehp_get_adapter_status(p_slot, &getstatus);
299 if (!getstatus) {
6e49b304 300 ctrl_info(ctrl, "Slot(%s): No adapter\n", slot_name(p_slot));
c9d86d76 301 return -ENODEV;
1da177e4 302 }
ae416e6b 303 if (MRL_SENS(p_slot->ctrl)) {
6dae6202
BH
304 pciehp_get_latch_status(p_slot, &getstatus);
305 if (getstatus) {
6e49b304 306 ctrl_info(ctrl, "Slot(%s): Latch open\n",
18b341b7 307 slot_name(p_slot));
c9d86d76 308 return -ENODEV;
1da177e4
LT
309 }
310 }
36ed27b0 311
ae416e6b 312 if (POWER_CTRL(p_slot->ctrl)) {
6dae6202
BH
313 pciehp_get_power_status(p_slot, &getstatus);
314 if (getstatus) {
6e49b304 315 ctrl_info(ctrl, "Slot(%s): Already enabled\n",
18b341b7 316 slot_name(p_slot));
c4ae2ade 317 return 0;
1da177e4
LT
318 }
319 }
1da177e4 320
29a654e5 321 return board_added(p_slot);
1da177e4
LT
322}
323
b0ccd9dd
LW
324int pciehp_enable_slot(struct slot *slot)
325{
326 struct controller *ctrl = slot->ctrl;
327 int ret;
328
329 mutex_lock(&slot->hotplug_lock);
330 ret = __pciehp_enable_slot(slot);
331 mutex_unlock(&slot->hotplug_lock);
332
333 if (ret && ATTN_BUTTN(ctrl))
334 pciehp_green_led_off(slot); /* may be blinking */
335
336 mutex_lock(&slot->lock);
9590192f 337 slot->state = ret ? OFF_STATE : ON_STATE;
b0ccd9dd
LW
338 mutex_unlock(&slot->lock);
339
340 return ret;
341}
342
50b52fde
RJ
343/*
344 * Note: This function must be called with slot->hotplug_lock held
345 */
b0ccd9dd 346static int __pciehp_disable_slot(struct slot *p_slot)
1da177e4 347{
1da177e4 348 u8 getstatus = 0;
7f2feec1 349 struct controller *ctrl = p_slot->ctrl;
1da177e4 350
ae416e6b 351 if (POWER_CTRL(p_slot->ctrl)) {
6dae6202
BH
352 pciehp_get_power_status(p_slot, &getstatus);
353 if (!getstatus) {
6e49b304 354 ctrl_info(ctrl, "Slot(%s): Already disabled\n",
18b341b7 355 slot_name(p_slot));
c9d86d76 356 return -EINVAL;
1da177e4
LT
357 }
358 }
359
1d2e2673
LW
360 remove_board(p_slot);
361 return 0;
1da177e4
LT
362}
363
b0ccd9dd
LW
364int pciehp_disable_slot(struct slot *slot)
365{
366 int ret;
367
368 mutex_lock(&slot->hotplug_lock);
369 ret = __pciehp_disable_slot(slot);
370 mutex_unlock(&slot->hotplug_lock);
371
372 mutex_lock(&slot->lock);
9590192f 373 slot->state = OFF_STATE;
b0ccd9dd
LW
374 mutex_unlock(&slot->lock);
375
376 return ret;
377}
378
5d386e1a
KK
379int pciehp_sysfs_enable_slot(struct slot *p_slot)
380{
7f2feec1 381 struct controller *ctrl = p_slot->ctrl;
5d386e1a
KK
382
383 mutex_lock(&p_slot->lock);
384 switch (p_slot->state) {
385 case BLINKINGON_STATE:
386 cancel_delayed_work(&p_slot->work);
9590192f 387 case OFF_STATE:
5d386e1a
KK
388 p_slot->state = POWERON_STATE;
389 mutex_unlock(&p_slot->lock);
b0ccd9dd 390 return pciehp_enable_slot(p_slot);
5d386e1a 391 case POWERON_STATE:
6e49b304 392 ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
e1acb24f 393 slot_name(p_slot));
5d386e1a
KK
394 break;
395 case BLINKINGOFF_STATE:
9590192f 396 case ON_STATE:
5d386e1a 397 case POWEROFF_STATE:
6e49b304 398 ctrl_info(ctrl, "Slot(%s): Already enabled\n",
e1acb24f 399 slot_name(p_slot));
5d386e1a
KK
400 break;
401 default:
6e49b304
BH
402 ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
403 slot_name(p_slot), p_slot->state);
5d386e1a
KK
404 break;
405 }
406 mutex_unlock(&p_slot->lock);
407
b0ccd9dd 408 return -ENODEV;
5d386e1a
KK
409}
410
411int pciehp_sysfs_disable_slot(struct slot *p_slot)
412{
7f2feec1 413 struct controller *ctrl = p_slot->ctrl;
5d386e1a
KK
414
415 mutex_lock(&p_slot->lock);
416 switch (p_slot->state) {
417 case BLINKINGOFF_STATE:
418 cancel_delayed_work(&p_slot->work);
9590192f 419 case ON_STATE:
5d386e1a
KK
420 p_slot->state = POWEROFF_STATE;
421 mutex_unlock(&p_slot->lock);
b0ccd9dd 422 return pciehp_disable_slot(p_slot);
5d386e1a 423 case POWEROFF_STATE:
6e49b304 424 ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
e1acb24f 425 slot_name(p_slot));
5d386e1a
KK
426 break;
427 case BLINKINGON_STATE:
9590192f 428 case OFF_STATE:
5d386e1a 429 case POWERON_STATE:
6e49b304 430 ctrl_info(ctrl, "Slot(%s): Already disabled\n",
e1acb24f 431 slot_name(p_slot));
5d386e1a
KK
432 break;
433 default:
6e49b304
BH
434 ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
435 slot_name(p_slot), p_slot->state);
5d386e1a
KK
436 break;
437 }
438 mutex_unlock(&p_slot->lock);
439
b0ccd9dd 440 return -ENODEV;
5d386e1a 441}