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