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