Merge tag 'xfs-4.20-merge-2' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux
[linux-2.6-block.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/kernel.h>
17#include <linux/types.h>
83503074 18#include <linux/pm_runtime.h>
1da177e4 19#include <linux/pci.h>
1da177e4 20#include "pciehp.h"
1da177e4 21
36ed27b0 22/* The following routines constitute the bulk of the
1da177e4
LT
23 hotplug controller logic
24 */
25
11e87702
LW
26#define SAFE_REMOVAL true
27#define SURPRISE_REMOVAL false
28
5790a9c7 29static void set_slot_off(struct controller *ctrl)
1da177e4 30{
1da177e4 31 /* turn off slot, turn on Amber LED, turn off Green LED if supported*/
ae416e6b 32 if (POWER_CTRL(ctrl)) {
5790a9c7 33 pciehp_power_off_slot(ctrl);
6dae6202 34
c7b4fee3
KK
35 /*
36 * After turning power off, we must wait for at least 1 second
37 * before taking any action that relies on power having been
38 * removed from the slot/adapter.
39 */
40 msleep(1000);
1da177e4
LT
41 }
42
5790a9c7
LW
43 pciehp_green_led_off(ctrl);
44 pciehp_set_attention_status(ctrl, 1);
1da177e4
LT
45}
46
47/**
48 * board_added - Called after a board has been added to the system.
5790a9c7 49 * @ctrl: PCIe hotplug controller where board is added
1da177e4 50 *
26e6c66e
RD
51 * Turns power on for the board.
52 * Configures board.
1da177e4 53 */
5790a9c7 54static int board_added(struct controller *ctrl)
1da177e4 55{
44ef4cef 56 int retval = 0;
385e2491 57 struct pci_bus *parent = ctrl->pcie->port->subordinate;
1da177e4 58
ae416e6b 59 if (POWER_CTRL(ctrl)) {
1da177e4 60 /* Power on slot */
5790a9c7 61 retval = pciehp_power_on_slot(ctrl);
44ef4cef
KK
62 if (retval)
63 return retval;
1da177e4 64 }
36ed27b0 65
5790a9c7 66 pciehp_green_led_blink(ctrl);
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 */
5790a9c7
LW
76 if (ctrl->power_fault_detected || pciehp_query_power_fault(ctrl)) {
77 ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(ctrl));
d9fb42a8 78 retval = -EIO;
71b720c0 79 goto err_exit;
1da177e4
LT
80 }
81
5790a9c7 82 retval = pciehp_configure_device(ctrl);
44ef4cef 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
5790a9c7
LW
91 pciehp_green_led_on(ctrl);
92 pciehp_set_attention_status(ctrl, 0);
1da177e4 93 return 0;
71b720c0 94
95err_exit:
5790a9c7 96 set_slot_off(ctrl);
44ef4cef 97 return retval;
1da177e4
LT
98}
99
1da177e4 100/**
26e6c66e 101 * remove_board - Turns off slot and LEDs
5790a9c7 102 * @ctrl: PCIe hotplug controller where board is being removed
11e87702 103 * @safe_removal: whether the board is safely removed (versus surprise removed)
1da177e4 104 */
5790a9c7 105static void remove_board(struct controller *ctrl, bool safe_removal)
1da177e4 106{
5790a9c7 107 pciehp_unconfigure_device(ctrl, safe_removal);
1da177e4 108
ae416e6b 109 if (POWER_CTRL(ctrl)) {
5790a9c7 110 pciehp_power_off_slot(ctrl);
6dae6202 111
c7b4fee3
KK
112 /*
113 * After turning power off, we must wait for at least 1 second
114 * before taking any action that relies on power having been
115 * removed from the slot/adapter.
116 */
117 msleep(1000);
1da177e4
LT
118 }
119
82a9e79e 120 /* turn off Green LED */
5790a9c7 121 pciehp_green_led_off(ctrl);
1da177e4
LT
122}
123
5790a9c7
LW
124static int pciehp_enable_slot(struct controller *ctrl);
125static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal);
25c83b84 126
32a8cef2
LW
127void pciehp_request(struct controller *ctrl, int action)
128{
129 atomic_or(action, &ctrl->pending_events);
130 if (!pciehp_poll_mode)
131 irq_wake_thread(ctrl->pcie->irq, ctrl);
132}
133
bee67756
GR
134void pciehp_queue_pushbutton_work(struct work_struct *work)
135{
5790a9c7 136 struct controller *ctrl = container_of(work, struct controller,
4ff3126e 137 button_work.work);
1da177e4 138
4ff3126e 139 mutex_lock(&ctrl->state_lock);
5790a9c7 140 switch (ctrl->state) {
5d386e1a 141 case BLINKINGOFF_STATE:
32a8cef2
LW
142 pciehp_request(ctrl, DISABLE_SLOT);
143 break;
5d386e1a 144 case BLINKINGON_STATE:
32a8cef2
LW
145 pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
146 break;
5d386e1a 147 default:
bee67756 148 break;
1da177e4 149 }
4ff3126e 150 mutex_unlock(&ctrl->state_lock);
1da177e4
LT
151}
152
5790a9c7 153void pciehp_handle_button_press(struct controller *ctrl)
1da177e4 154{
4ff3126e 155 mutex_lock(&ctrl->state_lock);
5790a9c7 156 switch (ctrl->state) {
9590192f
LW
157 case OFF_STATE:
158 case ON_STATE:
5790a9c7
LW
159 if (ctrl->state == ON_STATE) {
160 ctrl->state = BLINKINGOFF_STATE;
6e49b304 161 ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
5790a9c7 162 slot_name(ctrl));
5d386e1a 163 } else {
5790a9c7 164 ctrl->state = BLINKINGON_STATE;
6e49b304 165 ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n",
5790a9c7 166 slot_name(ctrl));
5d386e1a
KK
167 }
168 /* blink green LED and turn off amber */
5790a9c7
LW
169 pciehp_green_led_blink(ctrl);
170 pciehp_set_attention_status(ctrl, 0);
4ff3126e 171 schedule_delayed_work(&ctrl->button_work, 5 * HZ);
5d386e1a
KK
172 break;
173 case BLINKINGOFF_STATE:
174 case BLINKINGON_STATE:
175 /*
176 * Cancel if we are still blinking; this means that we
177 * press the attention again before the 5 sec. limit
178 * expires to cancel hot-add or hot-remove
179 */
5790a9c7 180 ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(ctrl));
4ff3126e 181 cancel_delayed_work(&ctrl->button_work);
5790a9c7
LW
182 if (ctrl->state == BLINKINGOFF_STATE) {
183 ctrl->state = ON_STATE;
184 pciehp_green_led_on(ctrl);
9590192f 185 } else {
5790a9c7
LW
186 ctrl->state = OFF_STATE;
187 pciehp_green_led_off(ctrl);
9590192f 188 }
5790a9c7 189 pciehp_set_attention_status(ctrl, 0);
6e49b304 190 ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
5790a9c7 191 slot_name(ctrl));
5d386e1a 192 break;
5d386e1a 193 default:
6e49b304 194 ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
5790a9c7 195 slot_name(ctrl), ctrl->state);
5d386e1a 196 break;
1da177e4 197 }
4ff3126e 198 mutex_unlock(&ctrl->state_lock);
1da177e4
LT
199}
200
5790a9c7 201void pciehp_handle_disable_request(struct controller *ctrl)
32a8cef2 202{
4ff3126e 203 mutex_lock(&ctrl->state_lock);
5790a9c7 204 switch (ctrl->state) {
32a8cef2
LW
205 case BLINKINGON_STATE:
206 case BLINKINGOFF_STATE:
4ff3126e 207 cancel_delayed_work(&ctrl->button_work);
8bb46b07 208 break;
32a8cef2 209 }
5790a9c7 210 ctrl->state = POWEROFF_STATE;
4ff3126e 211 mutex_unlock(&ctrl->state_lock);
32a8cef2 212
5790a9c7 213 ctrl->request_result = pciehp_disable_slot(ctrl, SAFE_REMOVAL);
32a8cef2
LW
214}
215
5790a9c7 216void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events)
e48f1b67 217{
80696f99 218 bool present, link_active;
0e94916e 219
d331710e
LW
220 /*
221 * If the slot is on and presence or link has changed, turn it off.
222 * Even if it's occupied again, we cannot assume the card is the same.
223 */
4ff3126e 224 mutex_lock(&ctrl->state_lock);
5790a9c7 225 switch (ctrl->state) {
e48f1b67 226 case BLINKINGOFF_STATE:
4ff3126e 227 cancel_delayed_work(&ctrl->button_work);
8bb46b07 228 /* fall through */
9590192f 229 case ON_STATE:
5790a9c7 230 ctrl->state = POWEROFF_STATE;
4ff3126e 231 mutex_unlock(&ctrl->state_lock);
d331710e
LW
232 if (events & PCI_EXP_SLTSTA_DLLSC)
233 ctrl_info(ctrl, "Slot(%s): Link Down\n",
5790a9c7 234 slot_name(ctrl));
d331710e
LW
235 if (events & PCI_EXP_SLTSTA_PDC)
236 ctrl_info(ctrl, "Slot(%s): Card not present\n",
5790a9c7
LW
237 slot_name(ctrl));
238 pciehp_disable_slot(ctrl, SURPRISE_REMOVAL);
e48f1b67 239 break;
e48f1b67 240 default:
4ff3126e 241 mutex_unlock(&ctrl->state_lock);
8bb46b07 242 break;
e48f1b67 243 }
0e94916e 244
d331710e 245 /* Turn the slot on if it's occupied or link is up */
4ff3126e 246 mutex_lock(&ctrl->state_lock);
80696f99 247 present = pciehp_card_present(ctrl);
d331710e
LW
248 link_active = pciehp_check_link_active(ctrl);
249 if (!present && !link_active) {
4ff3126e 250 mutex_unlock(&ctrl->state_lock);
d331710e
LW
251 return;
252 }
253
5790a9c7 254 switch (ctrl->state) {
32a8cef2 255 case BLINKINGON_STATE:
4ff3126e 256 cancel_delayed_work(&ctrl->button_work);
8bb46b07 257 /* fall through */
d331710e 258 case OFF_STATE:
5790a9c7 259 ctrl->state = POWERON_STATE;
4ff3126e 260 mutex_unlock(&ctrl->state_lock);
d331710e
LW
261 if (present)
262 ctrl_info(ctrl, "Slot(%s): Card present\n",
5790a9c7 263 slot_name(ctrl));
d331710e
LW
264 if (link_active)
265 ctrl_info(ctrl, "Slot(%s): Link Up\n",
5790a9c7
LW
266 slot_name(ctrl));
267 ctrl->request_result = pciehp_enable_slot(ctrl);
d331710e
LW
268 break;
269 default:
4ff3126e 270 mutex_unlock(&ctrl->state_lock);
8bb46b07 271 break;
5d386e1a 272 }
5d386e1a
KK
273}
274
5790a9c7 275static int __pciehp_enable_slot(struct controller *ctrl)
1da177e4
LT
276{
277 u8 getstatus = 0;
1da177e4 278
5790a9c7
LW
279 if (MRL_SENS(ctrl)) {
280 pciehp_get_latch_status(ctrl, &getstatus);
6dae6202 281 if (getstatus) {
6e49b304 282 ctrl_info(ctrl, "Slot(%s): Latch open\n",
5790a9c7 283 slot_name(ctrl));
c9d86d76 284 return -ENODEV;
1da177e4
LT
285 }
286 }
36ed27b0 287
5790a9c7
LW
288 if (POWER_CTRL(ctrl)) {
289 pciehp_get_power_status(ctrl, &getstatus);
6dae6202 290 if (getstatus) {
6e49b304 291 ctrl_info(ctrl, "Slot(%s): Already enabled\n",
5790a9c7 292 slot_name(ctrl));
c4ae2ade 293 return 0;
1da177e4
LT
294 }
295 }
1da177e4 296
5790a9c7 297 return board_added(ctrl);
1da177e4
LT
298}
299
5790a9c7 300static int pciehp_enable_slot(struct controller *ctrl)
b0ccd9dd 301{
b0ccd9dd
LW
302 int ret;
303
83503074 304 pm_runtime_get_sync(&ctrl->pcie->port->dev);
5790a9c7 305 ret = __pciehp_enable_slot(ctrl);
b0ccd9dd 306 if (ret && ATTN_BUTTN(ctrl))
5790a9c7 307 pciehp_green_led_off(ctrl); /* may be blinking */
83503074 308 pm_runtime_put(&ctrl->pcie->port->dev);
b0ccd9dd 309
4ff3126e 310 mutex_lock(&ctrl->state_lock);
5790a9c7 311 ctrl->state = ret ? OFF_STATE : ON_STATE;
4ff3126e 312 mutex_unlock(&ctrl->state_lock);
b0ccd9dd
LW
313
314 return ret;
315}
316
5790a9c7 317static int __pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
1da177e4 318{
1da177e4 319 u8 getstatus = 0;
1da177e4 320
5790a9c7
LW
321 if (POWER_CTRL(ctrl)) {
322 pciehp_get_power_status(ctrl, &getstatus);
6dae6202 323 if (!getstatus) {
6e49b304 324 ctrl_info(ctrl, "Slot(%s): Already disabled\n",
5790a9c7 325 slot_name(ctrl));
c9d86d76 326 return -EINVAL;
1da177e4
LT
327 }
328 }
329
5790a9c7 330 remove_board(ctrl, safe_removal);
1d2e2673 331 return 0;
1da177e4
LT
332}
333
5790a9c7 334static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
b0ccd9dd
LW
335{
336 int ret;
337
83503074 338 pm_runtime_get_sync(&ctrl->pcie->port->dev);
5790a9c7 339 ret = __pciehp_disable_slot(ctrl, safe_removal);
83503074 340 pm_runtime_put(&ctrl->pcie->port->dev);
b0ccd9dd 341
4ff3126e 342 mutex_lock(&ctrl->state_lock);
5790a9c7 343 ctrl->state = OFF_STATE;
4ff3126e 344 mutex_unlock(&ctrl->state_lock);
b0ccd9dd
LW
345
346 return ret;
347}
348
eee6e273 349int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot)
5d386e1a 350{
125450f8 351 struct controller *ctrl = to_ctrl(hotplug_slot);
5d386e1a 352
4ff3126e 353 mutex_lock(&ctrl->state_lock);
5790a9c7 354 switch (ctrl->state) {
5d386e1a 355 case BLINKINGON_STATE:
9590192f 356 case OFF_STATE:
4ff3126e 357 mutex_unlock(&ctrl->state_lock);
32a8cef2
LW
358 /*
359 * The IRQ thread becomes a no-op if the user pulls out the
360 * card before the thread wakes up, so initialize to -ENODEV.
361 */
362 ctrl->request_result = -ENODEV;
363 pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
364 wait_event(ctrl->requester,
365 !atomic_read(&ctrl->pending_events));
366 return ctrl->request_result;
5d386e1a 367 case POWERON_STATE:
6e49b304 368 ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
5790a9c7 369 slot_name(ctrl));
5d386e1a
KK
370 break;
371 case BLINKINGOFF_STATE:
9590192f 372 case ON_STATE:
5d386e1a 373 case POWEROFF_STATE:
6e49b304 374 ctrl_info(ctrl, "Slot(%s): Already enabled\n",
5790a9c7 375 slot_name(ctrl));
5d386e1a
KK
376 break;
377 default:
6e49b304 378 ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
5790a9c7 379 slot_name(ctrl), ctrl->state);
5d386e1a
KK
380 break;
381 }
4ff3126e 382 mutex_unlock(&ctrl->state_lock);
5d386e1a 383
b0ccd9dd 384 return -ENODEV;
5d386e1a
KK
385}
386
eee6e273 387int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot)
5d386e1a 388{
125450f8 389 struct controller *ctrl = to_ctrl(hotplug_slot);
5d386e1a 390
4ff3126e 391 mutex_lock(&ctrl->state_lock);
5790a9c7 392 switch (ctrl->state) {
5d386e1a 393 case BLINKINGOFF_STATE:
9590192f 394 case ON_STATE:
4ff3126e 395 mutex_unlock(&ctrl->state_lock);
32a8cef2
LW
396 pciehp_request(ctrl, DISABLE_SLOT);
397 wait_event(ctrl->requester,
398 !atomic_read(&ctrl->pending_events));
399 return ctrl->request_result;
5d386e1a 400 case POWEROFF_STATE:
6e49b304 401 ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
5790a9c7 402 slot_name(ctrl));
5d386e1a
KK
403 break;
404 case BLINKINGON_STATE:
9590192f 405 case OFF_STATE:
5d386e1a 406 case POWERON_STATE:
6e49b304 407 ctrl_info(ctrl, "Slot(%s): Already disabled\n",
5790a9c7 408 slot_name(ctrl));
5d386e1a
KK
409 break;
410 default:
6e49b304 411 ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
5790a9c7 412 slot_name(ctrl), ctrl->state);
5d386e1a
KK
413 break;
414 }
4ff3126e 415 mutex_unlock(&ctrl->state_lock);
5d386e1a 416
b0ccd9dd 417 return -ENODEV;
5d386e1a 418}