Linux Audio

Check our new training course

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