Linux Audio

Check our new training course

Loading...
v5.4
  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		ctrl_err(ctrl, "Failed to check link status\n");
 78		goto err_exit;
 79	}
 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
134static int pciehp_enable_slot(struct controller *ctrl);
135static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal);
 
 
 
 
 
136
137void pciehp_request(struct controller *ctrl, int action)
 
 
 
 
 
 
 
138{
139	atomic_or(action, &ctrl->pending_events);
140	if (!pciehp_poll_mode)
141		irq_wake_thread(ctrl->pcie->irq, ctrl);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142}
143
144void pciehp_queue_pushbutton_work(struct work_struct *work)
145{
146	struct controller *ctrl = container_of(work, struct controller,
147					       button_work.work);
148
149	mutex_lock(&ctrl->state_lock);
150	switch (ctrl->state) {
151	case BLINKINGOFF_STATE:
152		pciehp_request(ctrl, DISABLE_SLOT);
153		break;
154	case BLINKINGON_STATE:
155		pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
156		break;
157	default:
158		break;
159	}
160	mutex_unlock(&ctrl->state_lock);
161}
162
163void pciehp_handle_button_press(struct controller *ctrl)
 
 
 
164{
165	mutex_lock(&ctrl->state_lock);
166	switch (ctrl->state) {
167	case OFF_STATE:
168	case ON_STATE:
169		if (ctrl->state == ON_STATE) {
170			ctrl->state = BLINKINGOFF_STATE;
 
 
171			ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
172				  slot_name(ctrl));
173		} else {
174			ctrl->state = BLINKINGON_STATE;
175			ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n",
176				  slot_name(ctrl));
177		}
178		/* blink power indicator and turn off attention */
179		pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_BLINK,
180				      PCI_EXP_SLTCTL_ATTN_IND_OFF);
181		schedule_delayed_work(&ctrl->button_work, 5 * HZ);
182		break;
183	case BLINKINGOFF_STATE:
184	case BLINKINGON_STATE:
185		/*
186		 * Cancel if we are still blinking; this means that we
187		 * press the attention again before the 5 sec. limit
188		 * expires to cancel hot-add or hot-remove
189		 */
190		ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(ctrl));
191		cancel_delayed_work(&ctrl->button_work);
192		if (ctrl->state == BLINKINGOFF_STATE) {
193			ctrl->state = ON_STATE;
194			pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_ON,
195					      PCI_EXP_SLTCTL_ATTN_IND_OFF);
196		} else {
197			ctrl->state = OFF_STATE;
198			pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
199					      PCI_EXP_SLTCTL_ATTN_IND_OFF);
200		}
201		ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
202			  slot_name(ctrl));
 
 
 
 
 
 
 
 
 
 
 
203		break;
204	default:
205		ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
206			 slot_name(ctrl), ctrl->state);
207		break;
208	}
209	mutex_unlock(&ctrl->state_lock);
210}
211
212void pciehp_handle_disable_request(struct controller *ctrl)
 
 
 
213{
214	mutex_lock(&ctrl->state_lock);
215	switch (ctrl->state) {
 
216	case BLINKINGON_STATE:
217	case BLINKINGOFF_STATE:
218		cancel_delayed_work(&ctrl->button_work);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
219		break;
220	}
221	ctrl->state = POWEROFF_STATE;
222	mutex_unlock(&ctrl->state_lock);
223
224	ctrl->request_result = pciehp_disable_slot(ctrl, SAFE_REMOVAL);
225}
226
227void pciehp_handle_presence_or_link_change(struct controller *ctrl, u32 events)
228{
229	bool present, link_active;
 
 
230
231	/*
232	 * If the slot is on and presence or link has changed, turn it off.
233	 * Even if it's occupied again, we cannot assume the card is the same.
234	 */
235	mutex_lock(&ctrl->state_lock);
236	switch (ctrl->state) {
237	case BLINKINGOFF_STATE:
238		cancel_delayed_work(&ctrl->button_work);
239		/* fall through */
240	case ON_STATE:
241		ctrl->state = POWEROFF_STATE;
242		mutex_unlock(&ctrl->state_lock);
243		if (events & PCI_EXP_SLTSTA_DLLSC)
244			ctrl_info(ctrl, "Slot(%s): Link Down\n",
245				  slot_name(ctrl));
246		if (events & PCI_EXP_SLTSTA_PDC)
247			ctrl_info(ctrl, "Slot(%s): Card not present\n",
248				  slot_name(ctrl));
249		pciehp_disable_slot(ctrl, SURPRISE_REMOVAL);
250		break;
251	default:
252		mutex_unlock(&ctrl->state_lock);
 
 
 
253		break;
254	}
255
256	/* Turn the slot on if it's occupied or link is up */
257	mutex_lock(&ctrl->state_lock);
258	present = pciehp_card_present(ctrl);
259	link_active = pciehp_check_link_active(ctrl);
260	if (!present && !link_active) {
261		mutex_unlock(&ctrl->state_lock);
262		return;
263	}
264
265	switch (ctrl->state) {
266	case BLINKINGON_STATE:
267		cancel_delayed_work(&ctrl->button_work);
268		/* fall through */
269	case OFF_STATE:
270		ctrl->state = POWERON_STATE;
271		mutex_unlock(&ctrl->state_lock);
272		if (present)
273			ctrl_info(ctrl, "Slot(%s): Card present\n",
274				  slot_name(ctrl));
275		if (link_active)
276			ctrl_info(ctrl, "Slot(%s): Link Up\n",
277				  slot_name(ctrl));
278		ctrl->request_result = pciehp_enable_slot(ctrl);
279		break;
280	default:
281		mutex_unlock(&ctrl->state_lock);
282		break;
283	}
 
 
 
284}
285
286static int __pciehp_enable_slot(struct controller *ctrl)
 
 
 
287{
288	u8 getstatus = 0;
 
289
290	if (MRL_SENS(ctrl)) {
291		pciehp_get_latch_status(ctrl, &getstatus);
 
 
 
 
 
292		if (getstatus) {
293			ctrl_info(ctrl, "Slot(%s): Latch open\n",
294				  slot_name(ctrl));
295			return -ENODEV;
296		}
297	}
298
299	if (POWER_CTRL(ctrl)) {
300		pciehp_get_power_status(ctrl, &getstatus);
301		if (getstatus) {
302			ctrl_info(ctrl, "Slot(%s): Already enabled\n",
303				  slot_name(ctrl));
304			return 0;
305		}
306	}
307
308	return board_added(ctrl);
309}
310
311static int pciehp_enable_slot(struct controller *ctrl)
312{
313	int ret;
314
315	pm_runtime_get_sync(&ctrl->pcie->port->dev);
316	ret = __pciehp_enable_slot(ctrl);
317	if (ret && ATTN_BUTTN(ctrl))
318		/* may be blinking */
319		pciehp_set_indicators(ctrl, PCI_EXP_SLTCTL_PWR_IND_OFF,
320				      INDICATOR_NOOP);
321	pm_runtime_put(&ctrl->pcie->port->dev);
322
323	mutex_lock(&ctrl->state_lock);
324	ctrl->state = ret ? OFF_STATE : ON_STATE;
325	mutex_unlock(&ctrl->state_lock);
326
327	return ret;
328}
329
330static int __pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
331{
332	u8 getstatus = 0;
 
333
334	if (POWER_CTRL(ctrl)) {
335		pciehp_get_power_status(ctrl, &getstatus);
 
 
 
336		if (!getstatus) {
337			ctrl_info(ctrl, "Slot(%s): Already disabled\n",
338				  slot_name(ctrl));
339			return -EINVAL;
340		}
341	}
342
343	remove_board(ctrl, safe_removal);
344	return 0;
345}
346
347static int pciehp_disable_slot(struct controller *ctrl, bool safe_removal)
348{
349	int ret;
350
351	pm_runtime_get_sync(&ctrl->pcie->port->dev);
352	ret = __pciehp_disable_slot(ctrl, safe_removal);
353	pm_runtime_put(&ctrl->pcie->port->dev);
354
355	mutex_lock(&ctrl->state_lock);
356	ctrl->state = OFF_STATE;
357	mutex_unlock(&ctrl->state_lock);
358
359	return ret;
360}
361
362int pciehp_sysfs_enable_slot(struct hotplug_slot *hotplug_slot)
363{
364	struct controller *ctrl = to_ctrl(hotplug_slot);
365
366	mutex_lock(&ctrl->state_lock);
367	switch (ctrl->state) {
368	case BLINKINGON_STATE:
369	case OFF_STATE:
370		mutex_unlock(&ctrl->state_lock);
371		/*
372		 * The IRQ thread becomes a no-op if the user pulls out the
373		 * card before the thread wakes up, so initialize to -ENODEV.
374		 */
375		ctrl->request_result = -ENODEV;
376		pciehp_request(ctrl, PCI_EXP_SLTSTA_PDC);
377		wait_event(ctrl->requester,
378			   !atomic_read(&ctrl->pending_events));
379		return ctrl->request_result;
380	case POWERON_STATE:
381		ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
382			  slot_name(ctrl));
383		break;
384	case BLINKINGOFF_STATE:
385	case ON_STATE:
386	case POWEROFF_STATE:
387		ctrl_info(ctrl, "Slot(%s): Already enabled\n",
388			  slot_name(ctrl));
389		break;
390	default:
391		ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
392			 slot_name(ctrl), ctrl->state);
393		break;
394	}
395	mutex_unlock(&ctrl->state_lock);
396
397	return -ENODEV;
398}
399
400int pciehp_sysfs_disable_slot(struct hotplug_slot *hotplug_slot)
401{
402	struct controller *ctrl = to_ctrl(hotplug_slot);
 
403
404	mutex_lock(&ctrl->state_lock);
405	switch (ctrl->state) {
406	case BLINKINGOFF_STATE:
407	case ON_STATE:
408		mutex_unlock(&ctrl->state_lock);
409		pciehp_request(ctrl, DISABLE_SLOT);
410		wait_event(ctrl->requester,
411			   !atomic_read(&ctrl->pending_events));
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}
v4.17
  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/module.h>
 
 17#include <linux/kernel.h>
 18#include <linux/types.h>
 19#include <linux/slab.h>
 20#include <linux/pci.h>
 21#include "../pci.h"
 22#include "pciehp.h"
 23
 24static void interrupt_event_handler(struct work_struct *work);
 25
 26void pciehp_queue_interrupt_event(struct slot *p_slot, u32 event_type)
 27{
 28	struct event_info *info;
 29
 30	info = kmalloc(sizeof(*info), GFP_ATOMIC);
 31	if (!info) {
 32		ctrl_err(p_slot->ctrl, "dropped event %d (ENOMEM)\n", event_type);
 33		return;
 34	}
 35
 36	INIT_WORK(&info->work, interrupt_event_handler);
 37	info->event_type = event_type;
 38	info->p_slot = p_slot;
 39	queue_work(p_slot->wq, &info->work);
 40}
 41
 42/* The following routines constitute the bulk of the
 43   hotplug controller logic
 44 */
 45
 46static void set_slot_off(struct controller *ctrl, struct slot *pslot)
 
 
 
 47{
 48	/* turn off slot, turn on Amber LED, turn off Green LED if supported*/
 
 
 
 49	if (POWER_CTRL(ctrl)) {
 50		pciehp_power_off_slot(pslot);
 51
 52		/*
 53		 * After turning power off, we must wait for at least 1 second
 54		 * before taking any action that relies on power having been
 55		 * removed from the slot/adapter.
 56		 */
 57		msleep(1000);
 58	}
 59
 60	pciehp_green_led_off(pslot);
 61	pciehp_set_attention_status(pslot, 1);
 62}
 63
 64/**
 65 * board_added - Called after a board has been added to the system.
 66 * @p_slot: &slot where board is added
 67 *
 68 * Turns power on for the board.
 69 * Configures board.
 70 */
 71static int board_added(struct slot *p_slot)
 72{
 73	int retval = 0;
 74	struct controller *ctrl = p_slot->ctrl;
 75	struct pci_bus *parent = ctrl->pcie->port->subordinate;
 76
 77	if (POWER_CTRL(ctrl)) {
 78		/* Power on slot */
 79		retval = pciehp_power_on_slot(p_slot);
 80		if (retval)
 81			return retval;
 82	}
 83
 84	pciehp_green_led_blink(p_slot);
 
 85
 86	/* Check link training status */
 87	retval = pciehp_check_link_status(ctrl);
 88	if (retval) {
 89		ctrl_err(ctrl, "Failed to check link status\n");
 90		goto err_exit;
 91	}
 92
 93	/* Check for a power fault */
 94	if (ctrl->power_fault_detected || pciehp_query_power_fault(p_slot)) {
 95		ctrl_err(ctrl, "Slot(%s): Power fault\n", slot_name(p_slot));
 96		retval = -EIO;
 97		goto err_exit;
 98	}
 99
100	retval = pciehp_configure_device(p_slot);
101	if (retval) {
102		if (retval != -EEXIST) {
103			ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n",
104				 pci_domain_nr(parent), parent->number);
105			goto err_exit;
106		}
107	}
108
109	pciehp_green_led_on(p_slot);
110	pciehp_set_attention_status(p_slot, 0);
111	return 0;
112
113err_exit:
114	set_slot_off(ctrl, p_slot);
115	return retval;
116}
117
118/**
119 * remove_board - Turns off slot and LEDs
120 * @p_slot: slot where board is being removed
 
121 */
122static int remove_board(struct slot *p_slot)
123{
124	int retval;
125	struct controller *ctrl = p_slot->ctrl;
126
127	retval = pciehp_unconfigure_device(p_slot);
128	if (retval)
129		return retval;
130
131	if (POWER_CTRL(ctrl)) {
132		pciehp_power_off_slot(p_slot);
133
134		/*
135		 * After turning power off, we must wait for at least 1 second
136		 * before taking any action that relies on power having been
137		 * removed from the slot/adapter.
138		 */
139		msleep(1000);
 
 
 
 
140	}
141
142	/* turn off Green LED */
143	pciehp_green_led_off(p_slot);
144	return 0;
145}
146
147struct power_work_info {
148	struct slot *p_slot;
149	struct work_struct work;
150	unsigned int req;
151#define DISABLE_REQ 0
152#define ENABLE_REQ  1
153};
154
155/**
156 * pciehp_power_thread - handle pushbutton events
157 * @work: &struct work_struct describing work to be done
158 *
159 * Scheduled procedure to handle blocking stuff for the pushbuttons.
160 * Handles all pending events and exits.
161 */
162static void pciehp_power_thread(struct work_struct *work)
163{
164	struct power_work_info *info =
165		container_of(work, struct power_work_info, work);
166	struct slot *p_slot = info->p_slot;
167	int ret;
168
169	switch (info->req) {
170	case DISABLE_REQ:
171		mutex_lock(&p_slot->hotplug_lock);
172		pciehp_disable_slot(p_slot);
173		mutex_unlock(&p_slot->hotplug_lock);
174		mutex_lock(&p_slot->lock);
175		p_slot->state = STATIC_STATE;
176		mutex_unlock(&p_slot->lock);
177		break;
178	case ENABLE_REQ:
179		mutex_lock(&p_slot->hotplug_lock);
180		ret = pciehp_enable_slot(p_slot);
181		mutex_unlock(&p_slot->hotplug_lock);
182		if (ret)
183			pciehp_green_led_off(p_slot);
184		mutex_lock(&p_slot->lock);
185		p_slot->state = STATIC_STATE;
186		mutex_unlock(&p_slot->lock);
187		break;
188	default:
189		break;
190	}
191
192	kfree(info);
193}
194
195static void pciehp_queue_power_work(struct slot *p_slot, int req)
196{
197	struct power_work_info *info;
198
199	p_slot->state = (req == ENABLE_REQ) ? POWERON_STATE : POWEROFF_STATE;
200
201	info = kmalloc(sizeof(*info), GFP_KERNEL);
202	if (!info) {
203		ctrl_err(p_slot->ctrl, "no memory to queue %s request\n",
204			 (req == ENABLE_REQ) ? "poweron" : "poweroff");
205		return;
206	}
207	info->p_slot = p_slot;
208	INIT_WORK(&info->work, pciehp_power_thread);
209	info->req = req;
210	queue_work(p_slot->wq, &info->work);
211}
212
213void pciehp_queue_pushbutton_work(struct work_struct *work)
214{
215	struct slot *p_slot = container_of(work, struct slot, work.work);
 
216
217	mutex_lock(&p_slot->lock);
218	switch (p_slot->state) {
219	case BLINKINGOFF_STATE:
220		pciehp_queue_power_work(p_slot, DISABLE_REQ);
221		break;
222	case BLINKINGON_STATE:
223		pciehp_queue_power_work(p_slot, ENABLE_REQ);
224		break;
225	default:
226		break;
227	}
228	mutex_unlock(&p_slot->lock);
229}
230
231/*
232 * Note: This function must be called with slot->lock held
233 */
234static void handle_button_press_event(struct slot *p_slot)
235{
236	struct controller *ctrl = p_slot->ctrl;
237	u8 getstatus;
238
239	switch (p_slot->state) {
240	case STATIC_STATE:
241		pciehp_get_power_status(p_slot, &getstatus);
242		if (getstatus) {
243			p_slot->state = BLINKINGOFF_STATE;
244			ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
245				  slot_name(p_slot));
246		} else {
247			p_slot->state = BLINKINGON_STATE;
248			ctrl_info(ctrl, "Slot(%s) Powering on due to button press\n",
249				  slot_name(p_slot));
250		}
251		/* blink green LED and turn off amber */
252		pciehp_green_led_blink(p_slot);
253		pciehp_set_attention_status(p_slot, 0);
254		queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ);
255		break;
256	case BLINKINGOFF_STATE:
257	case BLINKINGON_STATE:
258		/*
259		 * Cancel if we are still blinking; this means that we
260		 * press the attention again before the 5 sec. limit
261		 * expires to cancel hot-add or hot-remove
262		 */
263		ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot));
264		cancel_delayed_work(&p_slot->work);
265		if (p_slot->state == BLINKINGOFF_STATE)
266			pciehp_green_led_on(p_slot);
267		else
268			pciehp_green_led_off(p_slot);
269		pciehp_set_attention_status(p_slot, 0);
 
 
 
 
270		ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
271			  slot_name(p_slot));
272		p_slot->state = STATIC_STATE;
273		break;
274	case POWEROFF_STATE:
275	case POWERON_STATE:
276		/*
277		 * Ignore if the slot is on power-on or power-off state;
278		 * this means that the previous attention button action
279		 * to hot-add or hot-remove is undergoing
280		 */
281		ctrl_info(ctrl, "Slot(%s): Button ignored\n",
282			  slot_name(p_slot));
283		break;
284	default:
285		ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
286			 slot_name(p_slot), p_slot->state);
287		break;
288	}
 
289}
290
291/*
292 * Note: This function must be called with slot->lock held
293 */
294static void handle_link_event(struct slot *p_slot, u32 event)
295{
296	struct controller *ctrl = p_slot->ctrl;
297
298	switch (p_slot->state) {
299	case BLINKINGON_STATE:
300	case BLINKINGOFF_STATE:
301		cancel_delayed_work(&p_slot->work);
302		/* Fall through */
303	case STATIC_STATE:
304		pciehp_queue_power_work(p_slot, event == INT_LINK_UP ?
305					ENABLE_REQ : DISABLE_REQ);
306		break;
307	case POWERON_STATE:
308		if (event == INT_LINK_UP) {
309			ctrl_info(ctrl, "Slot(%s): Link Up event ignored; already powering on\n",
310				  slot_name(p_slot));
311		} else {
312			ctrl_info(ctrl, "Slot(%s): Link Down event queued; currently getting powered on\n",
313				  slot_name(p_slot));
314			pciehp_queue_power_work(p_slot, DISABLE_REQ);
315		}
316		break;
317	case POWEROFF_STATE:
318		if (event == INT_LINK_UP) {
319			ctrl_info(ctrl, "Slot(%s): Link Up event queued; currently getting powered off\n",
320				  slot_name(p_slot));
321			pciehp_queue_power_work(p_slot, ENABLE_REQ);
322		} else {
323			ctrl_info(ctrl, "Slot(%s): Link Down event ignored; already powering off\n",
324				  slot_name(p_slot));
325		}
326		break;
327	default:
328		ctrl_err(ctrl, "Slot(%s): Ignoring invalid state %#x\n",
329			 slot_name(p_slot), p_slot->state);
330		break;
331	}
 
 
 
 
332}
333
334static void interrupt_event_handler(struct work_struct *work)
335{
336	struct event_info *info = container_of(work, struct event_info, work);
337	struct slot *p_slot = info->p_slot;
338	struct controller *ctrl = p_slot->ctrl;
339
340	mutex_lock(&p_slot->lock);
341	switch (info->event_type) {
342	case INT_BUTTON_PRESS:
343		handle_button_press_event(p_slot);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
344		break;
345	case INT_POWER_FAULT:
346		if (!POWER_CTRL(ctrl))
347			break;
348		pciehp_set_attention_status(p_slot, 1);
349		pciehp_green_led_off(p_slot);
350		break;
351	case INT_PRESENCE_ON:
352		pciehp_queue_power_work(p_slot, ENABLE_REQ);
353		break;
354	case INT_PRESENCE_OFF:
355		/*
356		 * Regardless of surprise capability, we need to
357		 * definitely remove a card that has been pulled out!
358		 */
359		pciehp_queue_power_work(p_slot, DISABLE_REQ);
360		break;
361	case INT_LINK_UP:
362	case INT_LINK_DOWN:
363		handle_link_event(p_slot, info->event_type);
 
 
 
 
 
 
 
 
 
 
 
 
364		break;
365	default:
 
366		break;
367	}
368	mutex_unlock(&p_slot->lock);
369
370	kfree(info);
371}
372
373/*
374 * Note: This function must be called with slot->hotplug_lock held
375 */
376int pciehp_enable_slot(struct slot *p_slot)
377{
378	u8 getstatus = 0;
379	struct controller *ctrl = p_slot->ctrl;
380
381	pciehp_get_adapter_status(p_slot, &getstatus);
382	if (!getstatus) {
383		ctrl_info(ctrl, "Slot(%s): No adapter\n", slot_name(p_slot));
384		return -ENODEV;
385	}
386	if (MRL_SENS(p_slot->ctrl)) {
387		pciehp_get_latch_status(p_slot, &getstatus);
388		if (getstatus) {
389			ctrl_info(ctrl, "Slot(%s): Latch open\n",
390				  slot_name(p_slot));
391			return -ENODEV;
392		}
393	}
394
395	if (POWER_CTRL(p_slot->ctrl)) {
396		pciehp_get_power_status(p_slot, &getstatus);
397		if (getstatus) {
398			ctrl_info(ctrl, "Slot(%s): Already enabled\n",
399				  slot_name(p_slot));
400			return 0;
401		}
402	}
403
404	return board_added(p_slot);
405}
406
407/*
408 * Note: This function must be called with slot->hotplug_lock held
409 */
410int pciehp_disable_slot(struct slot *p_slot)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411{
412	u8 getstatus = 0;
413	struct controller *ctrl = p_slot->ctrl;
414
415	if (!p_slot->ctrl)
416		return 1;
417
418	if (POWER_CTRL(p_slot->ctrl)) {
419		pciehp_get_power_status(p_slot, &getstatus);
420		if (!getstatus) {
421			ctrl_info(ctrl, "Slot(%s): Already disabled\n",
422				  slot_name(p_slot));
423			return -EINVAL;
424		}
425	}
426
427	return remove_board(p_slot);
 
428}
429
430int pciehp_sysfs_enable_slot(struct slot *p_slot)
431{
432	int retval = -ENODEV;
433	struct controller *ctrl = p_slot->ctrl;
 
 
 
434
435	mutex_lock(&p_slot->lock);
436	switch (p_slot->state) {
 
 
 
 
 
 
 
 
 
 
 
437	case BLINKINGON_STATE:
438		cancel_delayed_work(&p_slot->work);
439	case STATIC_STATE:
440		p_slot->state = POWERON_STATE;
441		mutex_unlock(&p_slot->lock);
442		mutex_lock(&p_slot->hotplug_lock);
443		retval = pciehp_enable_slot(p_slot);
444		mutex_unlock(&p_slot->hotplug_lock);
445		mutex_lock(&p_slot->lock);
446		p_slot->state = STATIC_STATE;
447		break;
 
448	case POWERON_STATE:
449		ctrl_info(ctrl, "Slot(%s): Already in powering on state\n",
450			  slot_name(p_slot));
451		break;
452	case BLINKINGOFF_STATE:
 
453	case POWEROFF_STATE:
454		ctrl_info(ctrl, "Slot(%s): Already enabled\n",
455			  slot_name(p_slot));
456		break;
457	default:
458		ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
459			 slot_name(p_slot), p_slot->state);
460		break;
461	}
462	mutex_unlock(&p_slot->lock);
463
464	return retval;
465}
466
467int pciehp_sysfs_disable_slot(struct slot *p_slot)
468{
469	int retval = -ENODEV;
470	struct controller *ctrl = p_slot->ctrl;
471
472	mutex_lock(&p_slot->lock);
473	switch (p_slot->state) {
474	case BLINKINGOFF_STATE:
475		cancel_delayed_work(&p_slot->work);
476	case STATIC_STATE:
477		p_slot->state = POWEROFF_STATE;
478		mutex_unlock(&p_slot->lock);
479		mutex_lock(&p_slot->hotplug_lock);
480		retval = pciehp_disable_slot(p_slot);
481		mutex_unlock(&p_slot->hotplug_lock);
482		mutex_lock(&p_slot->lock);
483		p_slot->state = STATIC_STATE;
484		break;
485	case POWEROFF_STATE:
486		ctrl_info(ctrl, "Slot(%s): Already in powering off state\n",
487			  slot_name(p_slot));
488		break;
489	case BLINKINGON_STATE:
 
490	case POWERON_STATE:
491		ctrl_info(ctrl, "Slot(%s): Already disabled\n",
492			  slot_name(p_slot));
493		break;
494	default:
495		ctrl_err(ctrl, "Slot(%s): Invalid state %#x\n",
496			 slot_name(p_slot), p_slot->state);
497		break;
498	}
499	mutex_unlock(&p_slot->lock);
500
501	return retval;
502}