blob: e5eb9106e7be9a674b0d2d82a5d91731750eaafd [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * PCI Express Hot Plug Controller Driver
3 *
4 * Copyright (C) 1995,2001 Compaq Computer Corporation
5 * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
6 * Copyright (C) 2001 IBM Corp.
7 * Copyright (C) 2003-2004 Intel Corporation
8 *
9 * All rights reserved.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or (at
14 * your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
19 * NON INFRINGEMENT. See the GNU General Public License for more
20 * details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
Kristen Accardi8cf4c192005-08-16 15:16:10 -070026 * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027 *
28 */
29
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/module.h>
31#include <linux/kernel.h>
32#include <linux/types.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <linux/smp_lock.h>
34#include <linux/pci.h>
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080035#include <linux/workqueue.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include "../pci.h"
37#include "pciehp.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070038
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080039static void interrupt_event_handler(struct work_struct *work);
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080041static int queue_interrupt_event(struct slot *p_slot, u32 event_type)
Kenji Kaneshige49ed2b42006-09-22 10:17:10 -070042{
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080043 struct event_info *info;
44
45 info = kmalloc(sizeof(*info), GFP_ATOMIC);
46 if (!info)
47 return -ENOMEM;
48
49 info->event_type = event_type;
50 info->p_slot = p_slot;
51 INIT_WORK(&info->work, interrupt_event_handler);
52
53 schedule_work(&info->work);
54
55 return 0;
Kenji Kaneshige49ed2b42006-09-22 10:17:10 -070056}
57
Kenji Kaneshige48fe3912006-12-21 17:01:04 -080058u8 pciehp_handle_attention_button(u8 hp_slot, struct controller *ctrl)
Linus Torvalds1da177e2005-04-16 15:20:36 -070059{
Linus Torvalds1da177e2005-04-16 15:20:36 -070060 struct slot *p_slot;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080061 u32 event_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -070062
63 /* Attention Button Change */
64 dbg("pciehp: Attention button interrupt received.\n");
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080065
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
67
Linus Torvalds1da177e2005-04-16 15:20:36 -070068 /*
69 * Button pressed - See if need to TAKE ACTION!!!
70 */
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080071 info("Button pressed on Slot(%s)\n", p_slot->name);
72 event_type = INT_BUTTON_PRESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080074 queue_interrupt_event(p_slot, event_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -070075
76 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070077}
78
Kenji Kaneshige48fe3912006-12-21 17:01:04 -080079u8 pciehp_handle_switch_change(u8 hp_slot, struct controller *ctrl)
Linus Torvalds1da177e2005-04-16 15:20:36 -070080{
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 struct slot *p_slot;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 u8 getstatus;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080083 u32 event_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
85 /* Switch Change */
86 dbg("pciehp: Switch interrupt received.\n");
87
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
90
91 if (getstatus) {
92 /*
93 * Switch opened
94 */
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080095 info("Latch open on Slot(%s)\n", p_slot->name);
96 event_type = INT_SWITCH_OPEN;
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 } else {
98 /*
99 * Switch closed
100 */
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800101 info("Latch close on Slot(%s)\n", p_slot->name);
102 event_type = INT_SWITCH_CLOSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 }
104
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800105 queue_interrupt_event(p_slot, event_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700106
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800107 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108}
109
Kenji Kaneshige48fe3912006-12-21 17:01:04 -0800110u8 pciehp_handle_presence_change(u8 hp_slot, struct controller *ctrl)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 struct slot *p_slot;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800113 u32 event_type;
114 u8 presence_save;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115
116 /* Presence Change */
117 dbg("pciehp: Presence/Notify input change.\n");
118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
120
121 /* Switch is open, assume a presence change
122 * Save the presence state
123 */
rajesh.shah@intel.comed6cbcf2005-10-31 16:20:09 -0800124 p_slot->hpc_ops->get_adapter_status(p_slot, &presence_save);
125 if (presence_save) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 /*
127 * Card Present
128 */
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800129 info("Card present on Slot(%s)\n", p_slot->name);
130 event_type = INT_PRESENCE_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700131 } else {
132 /*
133 * Not Present
134 */
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800135 info("Card not present on Slot(%s)\n", p_slot->name);
136 event_type = INT_PRESENCE_OFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 }
138
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800139 queue_interrupt_event(p_slot, event_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800141 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142}
143
Kenji Kaneshige48fe3912006-12-21 17:01:04 -0800144u8 pciehp_handle_power_fault(u8 hp_slot, struct controller *ctrl)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 struct slot *p_slot;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800147 u32 event_type;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148
149 /* power fault */
150 dbg("pciehp: Power fault interrupt received.\n");
151
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
153
154 if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
155 /*
156 * power fault Cleared
157 */
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800158 info("Power fault cleared on Slot(%s)\n", p_slot->name);
159 event_type = INT_POWER_FAULT_CLEAR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 } else {
161 /*
162 * power fault
163 */
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800164 info("Power fault on Slot(%s)\n", p_slot->name);
165 event_type = INT_POWER_FAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 info("power fault bit %x set\n", hp_slot);
167 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800169 queue_interrupt_event(p_slot, event_type);
170
171 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172}
173
Kenji Kaneshige36ed27b2007-08-09 16:09:36 -0700174/* The following routines constitute the bulk of the
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 hotplug controller logic
176 */
177
178static void set_slot_off(struct controller *ctrl, struct slot * pslot)
179{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 /* turn off slot, turn on Amber LED, turn off Green LED if supported*/
181 if (POWER_CTRL(ctrl->ctrlcap)) {
Kenji Kaneshige36ed27b2007-08-09 16:09:36 -0700182 if (pslot->hpc_ops->power_off_slot(pslot)) {
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800183 err("%s: Issue of Slot Power Off command failed\n",
184 __FUNCTION__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 return;
186 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 }
188
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800189 if (PWR_LED(ctrl->ctrlcap))
Kenji Kaneshige36ed27b2007-08-09 16:09:36 -0700190 pslot->hpc_ops->green_led_off(pslot);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800192 if (ATTN_LED(ctrl->ctrlcap)) {
193 if (pslot->hpc_ops->set_attention_status(pslot, 1)) {
194 err("%s: Issue of Set Attention Led command failed\n",
195 __FUNCTION__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 return;
197 }
Kenji Kaneshige5b57a6c2007-07-05 11:10:45 -0700198 /*
199 * After turning power off, we must wait for at least
200 * 1 second before taking any action that relies on
201 * power having been removed from the slot/adapter.
202 */
203 msleep(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205}
206
207/**
208 * board_added - Called after a board has been added to the system.
Randy Dunlap26e6c662007-11-28 09:04:30 -0800209 * @p_slot: &slot where board is added
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 *
Randy Dunlap26e6c662007-11-28 09:04:30 -0800211 * Turns power on for the board.
212 * Configures board.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 */
rajesh.shah@intel.comed6cbcf2005-10-31 16:20:09 -0800214static int board_added(struct slot *p_slot)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215{
216 u8 hp_slot;
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800217 int retval = 0;
rajesh.shah@intel.comca22a5e2005-10-31 16:20:08 -0800218 struct controller *ctrl = p_slot->ctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219
rajesh.shah@intel.comca22a5e2005-10-31 16:20:08 -0800220 hp_slot = p_slot->device - ctrl->slot_device_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221
rajesh.shah@intel.com1a9ed1b2005-10-31 16:20:10 -0800222 dbg("%s: slot device, slot offset, hp slot = %d, %d ,%d\n",
223 __FUNCTION__, p_slot->device,
224 ctrl->slot_device_offset, hp_slot);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 if (POWER_CTRL(ctrl->ctrlcap)) {
227 /* Power on slot */
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800228 retval = p_slot->hpc_ops->power_on_slot(p_slot);
229 if (retval)
230 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231 }
Kenji Kaneshige36ed27b2007-08-09 16:09:36 -0700232
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800233 if (PWR_LED(ctrl->ctrlcap))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 p_slot->hpc_ops->green_led_blink(p_slot);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
236 /* Wait for ~1 second */
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800237 msleep(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800239 /* Check link training status */
240 retval = p_slot->hpc_ops->check_lnk_status(ctrl);
241 if (retval) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 err("%s: Failed to check link status\n", __FUNCTION__);
243 set_slot_off(ctrl, p_slot);
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800244 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 }
246
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 /* Check for a power fault */
Rajesh Shah5a49f202005-11-23 15:44:54 -0800248 if (p_slot->hpc_ops->query_power_fault(p_slot)) {
249 dbg("%s: power fault detected\n", __FUNCTION__);
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800250 retval = POWER_FAILURE;
rajesh.shah@intel.com71b720c2005-10-31 16:20:06 -0800251 goto err_exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 }
253
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800254 retval = pciehp_configure_device(p_slot);
255 if (retval) {
rajesh.shah@intel.com71b720c2005-10-31 16:20:06 -0800256 err("Cannot add device 0x%x:%x\n", p_slot->bus,
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800257 p_slot->device);
rajesh.shah@intel.com71b720c2005-10-31 16:20:06 -0800258 goto err_exit;
259 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
rajesh.shah@intel.com71b720c2005-10-31 16:20:06 -0800261 /*
262 * Some PCI Express root ports require fixup after hot-plug operation.
263 */
264 if (pcie_mch_quirk)
265 pci_fixup_device(pci_fixup_final, ctrl->pci_dev);
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800266 if (PWR_LED(ctrl->ctrlcap))
rajesh.shah@intel.com71b720c2005-10-31 16:20:06 -0800267 p_slot->hpc_ops->green_led_on(p_slot);
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800268
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 return 0;
rajesh.shah@intel.com71b720c2005-10-31 16:20:06 -0800270
271err_exit:
272 set_slot_off(ctrl, p_slot);
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800273 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274}
275
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276/**
Randy Dunlap26e6c662007-11-28 09:04:30 -0800277 * remove_board - Turns off slot and LEDs
278 * @p_slot: slot where board is being removed
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 */
rajesh.shah@intel.comed6cbcf2005-10-31 16:20:09 -0800280static int remove_board(struct slot *p_slot)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 u8 device;
283 u8 hp_slot;
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800284 int retval = 0;
rajesh.shah@intel.comca22a5e2005-10-31 16:20:08 -0800285 struct controller *ctrl = p_slot->ctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800287 retval = pciehp_unconfigure_device(p_slot);
288 if (retval)
289 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
rajesh.shah@intel.comca22a5e2005-10-31 16:20:08 -0800291 device = p_slot->device;
rajesh.shah@intel.comca22a5e2005-10-31 16:20:08 -0800292 hp_slot = p_slot->device - ctrl->slot_device_offset;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
294
295 dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
296
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297 if (POWER_CTRL(ctrl->ctrlcap)) {
298 /* power off slot */
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800299 retval = p_slot->hpc_ops->power_off_slot(p_slot);
300 if (retval) {
301 err("%s: Issue of Slot Disable command failed\n",
302 __FUNCTION__);
303 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 }
306
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800307 if (PWR_LED(ctrl->ctrlcap))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 /* turn off Green LED */
309 p_slot->hpc_ops->green_led_off(p_slot);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311 return 0;
312}
313
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800314struct power_work_info {
315 struct slot *p_slot;
316 struct work_struct work;
317};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
319/**
Randy Dunlap26e6c662007-11-28 09:04:30 -0800320 * pciehp_power_thread - handle pushbutton events
321 * @work: &struct work_struct describing work to be done
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 *
Randy Dunlap26e6c662007-11-28 09:04:30 -0800323 * Scheduled procedure to handle blocking stuff for the pushbuttons.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 * Handles all pending events and exits.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 */
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800326static void pciehp_power_thread(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327{
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800328 struct power_work_info *info =
329 container_of(work, struct power_work_info, work);
330 struct slot *p_slot = info->p_slot;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800332 mutex_lock(&p_slot->lock);
333 switch (p_slot->state) {
334 case POWEROFF_STATE:
335 mutex_unlock(&p_slot->lock);
336 dbg("%s: disabling bus:device(%x:%x)\n",
337 __FUNCTION__, p_slot->bus, p_slot->device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 pciehp_disable_slot(p_slot);
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800339 mutex_lock(&p_slot->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 p_slot->state = STATIC_STATE;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800341 break;
342 case POWERON_STATE:
343 mutex_unlock(&p_slot->lock);
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800344 if (pciehp_enable_slot(p_slot) &&
345 PWR_LED(p_slot->ctrl->ctrlcap))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 p_slot->hpc_ops->green_led_off(p_slot);
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800347 mutex_lock(&p_slot->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 p_slot->state = STATIC_STATE;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800349 break;
350 default:
351 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 }
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800353 mutex_unlock(&p_slot->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800355 kfree(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356}
357
Kristen Carlson Accardie325e1f2007-03-21 11:45:31 -0700358void pciehp_queue_pushbutton_work(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359{
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800360 struct slot *p_slot = container_of(work, struct slot, work.work);
361 struct power_work_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800363 info = kmalloc(sizeof(*info), GFP_KERNEL);
364 if (!info) {
365 err("%s: Cannot allocate memory\n", __FUNCTION__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366 return;
367 }
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800368 info->p_slot = p_slot;
369 INIT_WORK(&info->work, pciehp_power_thread);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800371 mutex_lock(&p_slot->lock);
372 switch (p_slot->state) {
373 case BLINKINGOFF_STATE:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 p_slot->state = POWEROFF_STATE;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800375 break;
376 case BLINKINGON_STATE:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 p_slot->state = POWERON_STATE;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800378 break;
379 default:
380 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 }
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800382 queue_work(pciehp_wq, &info->work);
383 out:
384 mutex_unlock(&p_slot->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385}
386
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387static int update_slot_info(struct slot *slot)
388{
389 struct hotplug_slot_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 int result;
391
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800392 info = kmalloc(sizeof(*info), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 if (!info)
394 return -ENOMEM;
395
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 slot->hpc_ops->get_power_status(slot, &(info->power_status));
397 slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
398 slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
399 slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
400
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 result = pci_hp_change_slot_info(slot->hotplug_slot, info);
402 kfree (info);
403 return result;
404}
405
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800406/*
407 * Note: This function must be called with slot->lock held
408 */
409static void handle_button_press_event(struct slot *p_slot)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410{
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800411 struct controller *ctrl = p_slot->ctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 u8 getstatus;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800414 switch (p_slot->state) {
415 case STATIC_STATE:
416 p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
417 if (getstatus) {
418 p_slot->state = BLINKINGOFF_STATE;
419 info("PCI slot #%s - powering off due to button "
420 "press.\n", p_slot->name);
421 } else {
422 p_slot->state = BLINKINGON_STATE;
423 info("PCI slot #%s - powering on due to button "
424 "press.\n", p_slot->name);
425 }
426 /* blink green LED and turn off amber */
427 if (PWR_LED(ctrl->ctrlcap))
428 p_slot->hpc_ops->green_led_blink(p_slot);
429 if (ATTN_LED(ctrl->ctrlcap))
430 p_slot->hpc_ops->set_attention_status(p_slot, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800432 schedule_delayed_work(&p_slot->work, 5*HZ);
433 break;
434 case BLINKINGOFF_STATE:
435 case BLINKINGON_STATE:
436 /*
437 * Cancel if we are still blinking; this means that we
438 * press the attention again before the 5 sec. limit
439 * expires to cancel hot-add or hot-remove
440 */
441 info("Button cancel on Slot(%s)\n", p_slot->name);
442 dbg("%s: button cancel\n", __FUNCTION__);
443 cancel_delayed_work(&p_slot->work);
444 if (p_slot->state == BLINKINGOFF_STATE) {
445 if (PWR_LED(ctrl->ctrlcap))
446 p_slot->hpc_ops->green_led_on(p_slot);
447 } else {
448 if (PWR_LED(ctrl->ctrlcap))
449 p_slot->hpc_ops->green_led_off(p_slot);
450 }
451 if (ATTN_LED(ctrl->ctrlcap))
452 p_slot->hpc_ops->set_attention_status(p_slot, 0);
453 info("PCI slot #%s - action canceled due to button press\n",
454 p_slot->name);
455 p_slot->state = STATIC_STATE;
456 break;
457 case POWEROFF_STATE:
458 case POWERON_STATE:
459 /*
460 * Ignore if the slot is on power-on or power-off state;
461 * this means that the previous attention button action
462 * to hot-add or hot-remove is undergoing
463 */
464 info("Button ignore on Slot(%s)\n", p_slot->name);
465 update_slot_info(p_slot);
466 break;
467 default:
468 warn("Not a valid state\n");
469 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 }
471}
472
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800473/*
474 * Note: This function must be called with slot->lock held
475 */
476static void handle_surprise_event(struct slot *p_slot)
477{
478 u8 getstatus;
479 struct power_work_info *info;
480
481 info = kmalloc(sizeof(*info), GFP_KERNEL);
482 if (!info) {
483 err("%s: Cannot allocate memory\n", __FUNCTION__);
484 return;
485 }
486 info->p_slot = p_slot;
487 INIT_WORK(&info->work, pciehp_power_thread);
488
489 p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
490 if (!getstatus)
491 p_slot->state = POWEROFF_STATE;
492 else
493 p_slot->state = POWERON_STATE;
494
495 queue_work(pciehp_wq, &info->work);
496}
497
498static void interrupt_event_handler(struct work_struct *work)
499{
500 struct event_info *info = container_of(work, struct event_info, work);
501 struct slot *p_slot = info->p_slot;
502 struct controller *ctrl = p_slot->ctrl;
503
504 mutex_lock(&p_slot->lock);
505 switch (info->event_type) {
506 case INT_BUTTON_PRESS:
507 handle_button_press_event(p_slot);
508 break;
509 case INT_POWER_FAULT:
510 if (!POWER_CTRL(ctrl->ctrlcap))
511 break;
512 if (ATTN_LED(ctrl->ctrlcap))
513 p_slot->hpc_ops->set_attention_status(p_slot, 1);
514 if (PWR_LED(ctrl->ctrlcap))
515 p_slot->hpc_ops->green_led_off(p_slot);
516 break;
517 case INT_PRESENCE_ON:
518 case INT_PRESENCE_OFF:
519 if (!HP_SUPR_RM(ctrl->ctrlcap))
520 break;
521 dbg("Surprise Removal\n");
522 update_slot_info(p_slot);
523 handle_surprise_event(p_slot);
524 break;
525 default:
526 update_slot_info(p_slot);
527 break;
528 }
529 mutex_unlock(&p_slot->lock);
530
531 kfree(info);
532}
533
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534int pciehp_enable_slot(struct slot *p_slot)
535{
536 u8 getstatus = 0;
537 int rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538
539 /* Check to see if (latch closed, card present, power off) */
Ingo Molnar6aa4cdd2006-01-13 16:02:15 +0100540 mutex_lock(&p_slot->ctrl->crit_sect);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541
542 rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
543 if (rc || !getstatus) {
Kenji Kaneshige49ed2b42006-09-22 10:17:10 -0700544 info("%s: no adapter on slot(%s)\n", __FUNCTION__,
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800545 p_slot->name);
Ingo Molnar6aa4cdd2006-01-13 16:02:15 +0100546 mutex_unlock(&p_slot->ctrl->crit_sect);
Kenji Kaneshigec9d86d72006-09-19 17:04:33 -0700547 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 }
Kenji Kaneshige36ed27b2007-08-09 16:09:36 -0700549 if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
551 if (rc || getstatus) {
Kenji Kaneshige49ed2b42006-09-22 10:17:10 -0700552 info("%s: latch open on slot(%s)\n", __FUNCTION__,
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800553 p_slot->name);
Ingo Molnar6aa4cdd2006-01-13 16:02:15 +0100554 mutex_unlock(&p_slot->ctrl->crit_sect);
Kenji Kaneshigec9d86d72006-09-19 17:04:33 -0700555 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 }
557 }
Kenji Kaneshige36ed27b2007-08-09 16:09:36 -0700558
559 if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
561 if (rc || getstatus) {
Kenji Kaneshige49ed2b42006-09-22 10:17:10 -0700562 info("%s: already enabled on slot(%s)\n", __FUNCTION__,
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800563 p_slot->name);
Ingo Molnar6aa4cdd2006-01-13 16:02:15 +0100564 mutex_unlock(&p_slot->ctrl->crit_sect);
Kenji Kaneshigec9d86d72006-09-19 17:04:33 -0700565 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566 }
567 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570
rajesh.shah@intel.comca22a5e2005-10-31 16:20:08 -0800571 rc = board_added(p_slot);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 }
575
Eric Sesterhenn9ef99772006-09-25 00:56:53 +0200576 update_slot_info(p_slot);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Kenji Kaneshigedd5619c2006-09-22 10:17:29 -0700578 mutex_unlock(&p_slot->ctrl->crit_sect);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 return rc;
580}
581
582
583int pciehp_disable_slot(struct slot *p_slot)
584{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 u8 getstatus = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
588 if (!p_slot->ctrl)
589 return 1;
590
591 /* Check to see if (latch closed, card present, power on) */
Ingo Molnar6aa4cdd2006-01-13 16:02:15 +0100592 mutex_lock(&p_slot->ctrl->crit_sect);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593
Kenji Kaneshige36ed27b2007-08-09 16:09:36 -0700594 if (!HP_SUPR_RM(p_slot->ctrl->ctrlcap)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
596 if (ret || !getstatus) {
Kenji Kaneshige49ed2b42006-09-22 10:17:10 -0700597 info("%s: no adapter on slot(%s)\n", __FUNCTION__,
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800598 p_slot->name);
Ingo Molnar6aa4cdd2006-01-13 16:02:15 +0100599 mutex_unlock(&p_slot->ctrl->crit_sect);
Kenji Kaneshigec9d86d72006-09-19 17:04:33 -0700600 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 }
602 }
603
Kenji Kaneshige36ed27b2007-08-09 16:09:36 -0700604 if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
606 if (ret || getstatus) {
Kenji Kaneshige49ed2b42006-09-22 10:17:10 -0700607 info("%s: latch open on slot(%s)\n", __FUNCTION__,
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800608 p_slot->name);
Ingo Molnar6aa4cdd2006-01-13 16:02:15 +0100609 mutex_unlock(&p_slot->ctrl->crit_sect);
Kenji Kaneshigec9d86d72006-09-19 17:04:33 -0700610 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 }
612 }
613
Kenji Kaneshige36ed27b2007-08-09 16:09:36 -0700614 if (POWER_CTRL(p_slot->ctrl->ctrlcap)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
616 if (ret || !getstatus) {
Kenji Kaneshige49ed2b42006-09-22 10:17:10 -0700617 info("%s: already disabled slot(%s)\n", __FUNCTION__,
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800618 p_slot->name);
Ingo Molnar6aa4cdd2006-01-13 16:02:15 +0100619 mutex_unlock(&p_slot->ctrl->crit_sect);
Kenji Kaneshigec9d86d72006-09-19 17:04:33 -0700620 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 }
Kenji Kaneshige5b57a6c2007-07-05 11:10:45 -0700622 /*
623 * After turning power off, we must wait for at least
624 * 1 second before taking any action that relies on
625 * power having been removed from the slot/adapter.
626 */
627 msleep(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 }
629
rajesh.shah@intel.comca22a5e2005-10-31 16:20:08 -0800630 ret = remove_board(p_slot);
631 update_slot_info(p_slot);
Kenji Kaneshigedd5619c2006-09-22 10:17:29 -0700632
633 mutex_unlock(&p_slot->ctrl->crit_sect);
rajesh.shah@intel.comca22a5e2005-10-31 16:20:08 -0800634 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635}
636
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800637int pciehp_sysfs_enable_slot(struct slot *p_slot)
638{
639 int retval = -ENODEV;
640
641 mutex_lock(&p_slot->lock);
642 switch (p_slot->state) {
643 case BLINKINGON_STATE:
644 cancel_delayed_work(&p_slot->work);
645 case STATIC_STATE:
646 p_slot->state = POWERON_STATE;
647 mutex_unlock(&p_slot->lock);
648 retval = pciehp_enable_slot(p_slot);
649 mutex_lock(&p_slot->lock);
650 p_slot->state = STATIC_STATE;
651 break;
652 case POWERON_STATE:
653 info("Slot %s is already in powering on state\n",
654 p_slot->name);
655 break;
656 case BLINKINGOFF_STATE:
657 case POWEROFF_STATE:
658 info("Already enabled on slot %s\n", p_slot->name);
659 break;
660 default:
661 err("Not a valid state on slot %s\n", p_slot->name);
662 break;
663 }
664 mutex_unlock(&p_slot->lock);
665
666 return retval;
667}
668
669int pciehp_sysfs_disable_slot(struct slot *p_slot)
670{
671 int retval = -ENODEV;
672
673 mutex_lock(&p_slot->lock);
674 switch (p_slot->state) {
675 case BLINKINGOFF_STATE:
676 cancel_delayed_work(&p_slot->work);
677 case STATIC_STATE:
678 p_slot->state = POWEROFF_STATE;
679 mutex_unlock(&p_slot->lock);
680 retval = pciehp_disable_slot(p_slot);
681 mutex_lock(&p_slot->lock);
682 p_slot->state = STATIC_STATE;
683 break;
684 case POWEROFF_STATE:
685 info("Slot %s is already in powering off state\n",
686 p_slot->name);
687 break;
688 case BLINKINGON_STATE:
689 case POWERON_STATE:
690 info("Already disabled on slot %s\n", p_slot->name);
691 break;
692 default:
693 err("Not a valid state on slot %s\n", p_slot->name);
694 break;
695 }
696 mutex_unlock(&p_slot->lock);
697
698 return retval;
699}