blob: cce6e5f659e863f82152a54209ea138053c6ff99 [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 Kaneshigedbd79ae2008-05-27 19:03:16 +090058u8 pciehp_handle_attention_button(struct slot *p_slot)
Linus Torvalds1da177e2005-04-16 15:20:36 -070059{
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080060 u32 event_type;
Taku Izumi7f2feec2008-09-05 12:11:26 +090061 struct controller *ctrl = p_slot->ctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -070062
63 /* Attention Button Change */
Taku Izumi7f2feec2008-09-05 12:11:26 +090064 ctrl_dbg(ctrl, "Attention button interrupt received.\n");
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080065
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 /*
67 * Button pressed - See if need to TAKE ACTION!!!
68 */
Alex Chiange1acb242008-10-20 17:41:38 -060069 ctrl_info(ctrl, "Button pressed on Slot(%s)\n", slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080070 event_type = INT_BUTTON_PRESS;
Linus Torvalds1da177e2005-04-16 15:20:36 -070071
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080072 queue_interrupt_event(p_slot, event_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
74 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070075}
76
Kenji Kaneshigedbd79ae2008-05-27 19:03:16 +090077u8 pciehp_handle_switch_change(struct slot *p_slot)
Linus Torvalds1da177e2005-04-16 15:20:36 -070078{
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 u8 getstatus;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080080 u32 event_type;
Taku Izumi7f2feec2008-09-05 12:11:26 +090081 struct controller *ctrl = p_slot->ctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082
83 /* Switch Change */
Taku Izumi7f2feec2008-09-05 12:11:26 +090084 ctrl_dbg(ctrl, "Switch interrupt received.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -070085
Linus Torvalds1da177e2005-04-16 15:20:36 -070086 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 if (getstatus) {
88 /*
89 * Switch opened
90 */
Alex Chiange1acb242008-10-20 17:41:38 -060091 ctrl_info(ctrl, "Latch open on Slot(%s)\n", slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080092 event_type = INT_SWITCH_OPEN;
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 } else {
94 /*
95 * Switch closed
96 */
Alex Chiange1acb242008-10-20 17:41:38 -060097 ctrl_info(ctrl, "Latch close on Slot(%s)\n", slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -080098 event_type = INT_SWITCH_CLOSE;
Linus Torvalds1da177e2005-04-16 15:20:36 -070099 }
100
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800101 queue_interrupt_event(p_slot, event_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800103 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104}
105
Kenji Kaneshigedbd79ae2008-05-27 19:03:16 +0900106u8 pciehp_handle_presence_change(struct slot *p_slot)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107{
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800108 u32 event_type;
109 u8 presence_save;
Taku Izumi7f2feec2008-09-05 12:11:26 +0900110 struct controller *ctrl = p_slot->ctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111
112 /* Presence Change */
Taku Izumi7f2feec2008-09-05 12:11:26 +0900113 ctrl_dbg(ctrl, "Presence/Notify input change.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115 /* Switch is open, assume a presence change
116 * Save the presence state
117 */
rajesh.shah@intel.comed6cbcf2005-10-31 16:20:09 -0800118 p_slot->hpc_ops->get_adapter_status(p_slot, &presence_save);
119 if (presence_save) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 /*
121 * Card Present
122 */
Alex Chiange1acb242008-10-20 17:41:38 -0600123 ctrl_info(ctrl, "Card present on Slot(%s)\n", slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800124 event_type = INT_PRESENCE_ON;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 } else {
126 /*
127 * Not Present
128 */
Alex Chiange1acb242008-10-20 17:41:38 -0600129 ctrl_info(ctrl, "Card not present on Slot(%s)\n",
130 slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800131 event_type = INT_PRESENCE_OFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132 }
133
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800134 queue_interrupt_event(p_slot, event_type);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800136 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137}
138
Kenji Kaneshigedbd79ae2008-05-27 19:03:16 +0900139u8 pciehp_handle_power_fault(struct slot *p_slot)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140{
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800141 u32 event_type;
Taku Izumi7f2feec2008-09-05 12:11:26 +0900142 struct controller *ctrl = p_slot->ctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143
144 /* power fault */
Taku Izumi7f2feec2008-09-05 12:11:26 +0900145 ctrl_dbg(ctrl, "Power fault interrupt received.\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147 if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
148 /*
149 * power fault Cleared
150 */
Taku Izumi7f2feec2008-09-05 12:11:26 +0900151 ctrl_info(ctrl, "Power fault cleared on Slot(%s)\n",
Alex Chiange1acb242008-10-20 17:41:38 -0600152 slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800153 event_type = INT_POWER_FAULT_CLEAR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 } else {
155 /*
156 * power fault
157 */
Alex Chiange1acb242008-10-20 17:41:38 -0600158 ctrl_info(ctrl, "Power fault on Slot(%s)\n", slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800159 event_type = INT_POWER_FAULT;
Taku Izumi7f2feec2008-09-05 12:11:26 +0900160 ctrl_info(ctrl, "power fault bit %x set\n", 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800163 queue_interrupt_event(p_slot, event_type);
164
165 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166}
167
Kenji Kaneshige36ed27b2007-08-09 16:09:36 -0700168/* The following routines constitute the bulk of the
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 hotplug controller logic
170 */
171
172static void set_slot_off(struct controller *ctrl, struct slot * pslot)
173{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 /* turn off slot, turn on Amber LED, turn off Green LED if supported*/
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700175 if (POWER_CTRL(ctrl)) {
Kenji Kaneshige36ed27b2007-08-09 16:09:36 -0700176 if (pslot->hpc_ops->power_off_slot(pslot)) {
Taku Izumi7f2feec2008-09-05 12:11:26 +0900177 ctrl_err(ctrl,
178 "%s: Issue of Slot Power Off command failed\n",
179 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180 return;
181 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 }
183
Kenji Kaneshige0711c702008-05-27 19:06:22 +0900184 /*
185 * After turning power off, we must wait for at least 1 second
186 * before taking any action that relies on power having been
187 * removed from the slot/adapter.
188 */
189 msleep(1000);
190
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700191 if (PWR_LED(ctrl))
Kenji Kaneshige36ed27b2007-08-09 16:09:36 -0700192 pslot->hpc_ops->green_led_off(pslot);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700194 if (ATTN_LED(ctrl)) {
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800195 if (pslot->hpc_ops->set_attention_status(pslot, 1)) {
Taku Izumi7f2feec2008-09-05 12:11:26 +0900196 ctrl_err(ctrl, "%s: Issue of Set Attention "
197 "Led command failed\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 return;
199 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201}
202
203/**
204 * board_added - Called after a board has been added to the system.
Randy Dunlap26e6c662007-11-28 09:04:30 -0800205 * @p_slot: &slot where board is added
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 *
Randy Dunlap26e6c662007-11-28 09:04:30 -0800207 * Turns power on for the board.
208 * Configures board.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209 */
rajesh.shah@intel.comed6cbcf2005-10-31 16:20:09 -0800210static int board_added(struct slot *p_slot)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211{
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800212 int retval = 0;
rajesh.shah@intel.comca22a5e2005-10-31 16:20:08 -0800213 struct controller *ctrl = p_slot->ctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214
Taku Izumi7f2feec2008-09-05 12:11:26 +0900215 ctrl_dbg(ctrl, "%s: slot device, slot offset, hp slot = %d, %d ,%d\n",
216 __func__, p_slot->device, ctrl->slot_device_offset,
217 p_slot->hp_slot);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700219 if (POWER_CTRL(ctrl)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 /* Power on slot */
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800221 retval = p_slot->hpc_ops->power_on_slot(p_slot);
222 if (retval)
223 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 }
Kenji Kaneshige36ed27b2007-08-09 16:09:36 -0700225
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700226 if (PWR_LED(ctrl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 p_slot->hpc_ops->green_led_blink(p_slot);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228
229 /* Wait for ~1 second */
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800230 msleep(1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800232 /* Check link training status */
233 retval = p_slot->hpc_ops->check_lnk_status(ctrl);
234 if (retval) {
Taku Izumi7f2feec2008-09-05 12:11:26 +0900235 ctrl_err(ctrl, "%s: Failed to check link status\n", __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236 set_slot_off(ctrl, p_slot);
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800237 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 }
239
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 /* Check for a power fault */
Rajesh Shah5a49f202005-11-23 15:44:54 -0800241 if (p_slot->hpc_ops->query_power_fault(p_slot)) {
Taku Izumi7f2feec2008-09-05 12:11:26 +0900242 ctrl_dbg(ctrl, "%s: power fault detected\n", __func__);
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800243 retval = POWER_FAILURE;
rajesh.shah@intel.com71b720c2005-10-31 16:20:06 -0800244 goto err_exit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 }
246
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800247 retval = pciehp_configure_device(p_slot);
248 if (retval) {
Taku Izumi7f2feec2008-09-05 12:11:26 +0900249 ctrl_err(ctrl, "Cannot add device 0x%x:%x\n",
250 p_slot->bus, p_slot->device);
rajesh.shah@intel.com71b720c2005-10-31 16:20:06 -0800251 goto err_exit;
252 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
rajesh.shah@intel.com71b720c2005-10-31 16:20:06 -0800254 /*
255 * Some PCI Express root ports require fixup after hot-plug operation.
256 */
257 if (pcie_mch_quirk)
258 pci_fixup_device(pci_fixup_final, ctrl->pci_dev);
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700259 if (PWR_LED(ctrl))
rajesh.shah@intel.com71b720c2005-10-31 16:20:06 -0800260 p_slot->hpc_ops->green_led_on(p_slot);
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800261
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 return 0;
rajesh.shah@intel.com71b720c2005-10-31 16:20:06 -0800263
264err_exit:
265 set_slot_off(ctrl, p_slot);
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800266 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267}
268
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269/**
Randy Dunlap26e6c662007-11-28 09:04:30 -0800270 * remove_board - Turns off slot and LEDs
271 * @p_slot: slot where board is being removed
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 */
rajesh.shah@intel.comed6cbcf2005-10-31 16:20:09 -0800273static int remove_board(struct slot *p_slot)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274{
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800275 int retval = 0;
rajesh.shah@intel.comca22a5e2005-10-31 16:20:08 -0800276 struct controller *ctrl = p_slot->ctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800278 retval = pciehp_unconfigure_device(p_slot);
279 if (retval)
280 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
Taku Izumi7f2feec2008-09-05 12:11:26 +0900282 ctrl_dbg(ctrl, "In %s, hp_slot = %d\n", __func__, p_slot->hp_slot);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700284 if (POWER_CTRL(ctrl)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 /* power off slot */
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800286 retval = p_slot->hpc_ops->power_off_slot(p_slot);
287 if (retval) {
Taku Izumi7f2feec2008-09-05 12:11:26 +0900288 ctrl_err(ctrl, "%s: Issue of Slot Disable command "
289 "failed\n", __func__);
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800290 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 }
293
Kenji Kaneshige0711c702008-05-27 19:06:22 +0900294 /*
295 * After turning power off, we must wait for at least 1 second
296 * before taking any action that relies on power having been
297 * removed from the slot/adapter.
298 */
299 msleep(1000);
300
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700301 if (PWR_LED(ctrl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 /* turn off Green LED */
303 p_slot->hpc_ops->green_led_off(p_slot);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 return 0;
306}
307
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800308struct power_work_info {
309 struct slot *p_slot;
310 struct work_struct work;
311};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
313/**
Randy Dunlap26e6c662007-11-28 09:04:30 -0800314 * pciehp_power_thread - handle pushbutton events
315 * @work: &struct work_struct describing work to be done
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316 *
Randy Dunlap26e6c662007-11-28 09:04:30 -0800317 * Scheduled procedure to handle blocking stuff for the pushbuttons.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318 * Handles all pending events and exits.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 */
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800320static void pciehp_power_thread(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321{
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800322 struct power_work_info *info =
323 container_of(work, struct power_work_info, work);
324 struct slot *p_slot = info->p_slot;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800326 mutex_lock(&p_slot->lock);
327 switch (p_slot->state) {
328 case POWEROFF_STATE:
329 mutex_unlock(&p_slot->lock);
Taku Izumi7f2feec2008-09-05 12:11:26 +0900330 ctrl_dbg(p_slot->ctrl, "%s: disabling bus:device(%x:%x)\n",
331 __func__, p_slot->bus, p_slot->device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 pciehp_disable_slot(p_slot);
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800333 mutex_lock(&p_slot->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 p_slot->state = STATIC_STATE;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800335 break;
336 case POWERON_STATE:
337 mutex_unlock(&p_slot->lock);
Kenji Kaneshige44ef4ce2006-12-21 17:01:09 -0800338 if (pciehp_enable_slot(p_slot) &&
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700339 PWR_LED(p_slot->ctrl))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 p_slot->hpc_ops->green_led_off(p_slot);
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800341 mutex_lock(&p_slot->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 p_slot->state = STATIC_STATE;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800343 break;
344 default:
345 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 }
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800347 mutex_unlock(&p_slot->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800349 kfree(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350}
351
Kristen Carlson Accardie325e1f2007-03-21 11:45:31 -0700352void pciehp_queue_pushbutton_work(struct work_struct *work)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353{
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800354 struct slot *p_slot = container_of(work, struct slot, work.work);
355 struct power_work_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800357 info = kmalloc(sizeof(*info), GFP_KERNEL);
358 if (!info) {
Taku Izumi7f2feec2008-09-05 12:11:26 +0900359 ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
360 __func__);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 return;
362 }
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800363 info->p_slot = p_slot;
364 INIT_WORK(&info->work, pciehp_power_thread);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800366 mutex_lock(&p_slot->lock);
367 switch (p_slot->state) {
368 case BLINKINGOFF_STATE:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700369 p_slot->state = POWEROFF_STATE;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800370 break;
371 case BLINKINGON_STATE:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 p_slot->state = POWERON_STATE;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800373 break;
374 default:
375 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 }
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800377 queue_work(pciehp_wq, &info->work);
378 out:
379 mutex_unlock(&p_slot->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700380}
381
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382static int update_slot_info(struct slot *slot)
383{
384 struct hotplug_slot_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385 int result;
386
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800387 info = kmalloc(sizeof(*info), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 if (!info)
389 return -ENOMEM;
390
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 slot->hpc_ops->get_power_status(slot, &(info->power_status));
392 slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
393 slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
394 slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
395
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396 result = pci_hp_change_slot_info(slot->hotplug_slot, info);
397 kfree (info);
398 return result;
399}
400
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800401/*
402 * Note: This function must be called with slot->lock held
403 */
404static void handle_button_press_event(struct slot *p_slot)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405{
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800406 struct controller *ctrl = p_slot->ctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407 u8 getstatus;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800409 switch (p_slot->state) {
410 case STATIC_STATE:
411 p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
412 if (getstatus) {
413 p_slot->state = BLINKINGOFF_STATE;
Taku Izumi7f2feec2008-09-05 12:11:26 +0900414 ctrl_info(ctrl,
415 "PCI slot #%s - powering off due to button "
Alex Chiange1acb242008-10-20 17:41:38 -0600416 "press.\n", slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800417 } else {
418 p_slot->state = BLINKINGON_STATE;
Taku Izumi7f2feec2008-09-05 12:11:26 +0900419 ctrl_info(ctrl,
420 "PCI slot #%s - powering on due to button "
Alex Chiange1acb242008-10-20 17:41:38 -0600421 "press.\n", slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800422 }
423 /* blink green LED and turn off amber */
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700424 if (PWR_LED(ctrl))
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800425 p_slot->hpc_ops->green_led_blink(p_slot);
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700426 if (ATTN_LED(ctrl))
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800427 p_slot->hpc_ops->set_attention_status(p_slot, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800429 schedule_delayed_work(&p_slot->work, 5*HZ);
430 break;
431 case BLINKINGOFF_STATE:
432 case BLINKINGON_STATE:
433 /*
434 * Cancel if we are still blinking; this means that we
435 * press the attention again before the 5 sec. limit
436 * expires to cancel hot-add or hot-remove
437 */
Alex Chiange1acb242008-10-20 17:41:38 -0600438 ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot));
Taku Izumi7f2feec2008-09-05 12:11:26 +0900439 ctrl_dbg(ctrl, "%s: button cancel\n", __func__);
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800440 cancel_delayed_work(&p_slot->work);
441 if (p_slot->state == BLINKINGOFF_STATE) {
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700442 if (PWR_LED(ctrl))
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800443 p_slot->hpc_ops->green_led_on(p_slot);
444 } else {
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700445 if (PWR_LED(ctrl))
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800446 p_slot->hpc_ops->green_led_off(p_slot);
447 }
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700448 if (ATTN_LED(ctrl))
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800449 p_slot->hpc_ops->set_attention_status(p_slot, 0);
Taku Izumi7f2feec2008-09-05 12:11:26 +0900450 ctrl_info(ctrl, "PCI slot #%s - action canceled "
Alex Chiange1acb242008-10-20 17:41:38 -0600451 "due to button press\n", slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800452 p_slot->state = STATIC_STATE;
453 break;
454 case POWEROFF_STATE:
455 case POWERON_STATE:
456 /*
457 * Ignore if the slot is on power-on or power-off state;
458 * this means that the previous attention button action
459 * to hot-add or hot-remove is undergoing
460 */
Alex Chiange1acb242008-10-20 17:41:38 -0600461 ctrl_info(ctrl, "Button ignore on Slot(%s)\n", slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800462 update_slot_info(p_slot);
463 break;
464 default:
Taku Izumi7f2feec2008-09-05 12:11:26 +0900465 ctrl_warn(ctrl, "Not a valid state\n");
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800466 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 }
468}
469
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800470/*
471 * Note: This function must be called with slot->lock held
472 */
473static void handle_surprise_event(struct slot *p_slot)
474{
475 u8 getstatus;
476 struct power_work_info *info;
477
478 info = kmalloc(sizeof(*info), GFP_KERNEL);
479 if (!info) {
Taku Izumi7f2feec2008-09-05 12:11:26 +0900480 ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n",
481 __func__);
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800482 return;
483 }
484 info->p_slot = p_slot;
485 INIT_WORK(&info->work, pciehp_power_thread);
486
487 p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
488 if (!getstatus)
489 p_slot->state = POWEROFF_STATE;
490 else
491 p_slot->state = POWERON_STATE;
492
493 queue_work(pciehp_wq, &info->work);
494}
495
496static void interrupt_event_handler(struct work_struct *work)
497{
498 struct event_info *info = container_of(work, struct event_info, work);
499 struct slot *p_slot = info->p_slot;
500 struct controller *ctrl = p_slot->ctrl;
501
502 mutex_lock(&p_slot->lock);
503 switch (info->event_type) {
504 case INT_BUTTON_PRESS:
505 handle_button_press_event(p_slot);
506 break;
507 case INT_POWER_FAULT:
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700508 if (!POWER_CTRL(ctrl))
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800509 break;
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700510 if (ATTN_LED(ctrl))
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800511 p_slot->hpc_ops->set_attention_status(p_slot, 1);
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700512 if (PWR_LED(ctrl))
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800513 p_slot->hpc_ops->green_led_off(p_slot);
514 break;
515 case INT_PRESENCE_ON:
516 case INT_PRESENCE_OFF:
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700517 if (!HP_SUPR_RM(ctrl))
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800518 break;
Taku Izumi7f2feec2008-09-05 12:11:26 +0900519 ctrl_dbg(ctrl, "Surprise Removal\n");
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800520 update_slot_info(p_slot);
521 handle_surprise_event(p_slot);
522 break;
523 default:
524 update_slot_info(p_slot);
525 break;
526 }
527 mutex_unlock(&p_slot->lock);
528
529 kfree(info);
530}
531
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532int pciehp_enable_slot(struct slot *p_slot)
533{
534 u8 getstatus = 0;
535 int rc;
Taku Izumi7f2feec2008-09-05 12:11:26 +0900536 struct controller *ctrl = p_slot->ctrl;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
538 /* Check to see if (latch closed, card present, power off) */
Ingo Molnar6aa4cdd2006-01-13 16:02:15 +0100539 mutex_lock(&p_slot->ctrl->crit_sect);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
541 rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
542 if (rc || !getstatus) {
Taku Izumi7f2feec2008-09-05 12:11:26 +0900543 ctrl_info(ctrl, "%s: no adapter on slot(%s)\n",
Alex Chiange1acb242008-10-20 17:41:38 -0600544 __func__, slot_name(p_slot));
Ingo Molnar6aa4cdd2006-01-13 16:02:15 +0100545 mutex_unlock(&p_slot->ctrl->crit_sect);
Kenji Kaneshigec9d86d72006-09-19 17:04:33 -0700546 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 }
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700548 if (MRL_SENS(p_slot->ctrl)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
550 if (rc || getstatus) {
Taku Izumi7f2feec2008-09-05 12:11:26 +0900551 ctrl_info(ctrl, "%s: latch open on slot(%s)\n",
Alex Chiange1acb242008-10-20 17:41:38 -0600552 __func__, slot_name(p_slot));
Ingo Molnar6aa4cdd2006-01-13 16:02:15 +0100553 mutex_unlock(&p_slot->ctrl->crit_sect);
Kenji Kaneshigec9d86d72006-09-19 17:04:33 -0700554 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 }
556 }
Kenji Kaneshige36ed27b2007-08-09 16:09:36 -0700557
Kenji Kaneshigeae416e62008-04-25 14:39:06 -0700558 if (POWER_CTRL(p_slot->ctrl)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559 rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
560 if (rc || getstatus) {
Taku Izumi7f2feec2008-09-05 12:11:26 +0900561 ctrl_info(ctrl, "%s: already enabled on slot(%s)\n",
Alex Chiange1acb242008-10-20 17:41:38 -0600562 __func__, slot_name(p_slot));
Ingo Molnar6aa4cdd2006-01-13 16:02:15 +0100563 mutex_unlock(&p_slot->ctrl->crit_sect);
Kenji Kaneshigec9d86d72006-09-19 17:04:33 -0700564 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 }
566 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700569
rajesh.shah@intel.comca22a5e2005-10-31 16:20:08 -0800570 rc = board_added(p_slot);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 if (rc) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572 p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 }
574
Eric Sesterhenn9ef99772006-09-25 00:56:53 +0200575 update_slot_info(p_slot);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576
Kenji Kaneshigedd5619c2006-09-22 10:17:29 -0700577 mutex_unlock(&p_slot->ctrl->crit_sect);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 return rc;
579}
580
581
582int pciehp_disable_slot(struct slot *p_slot)
583{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 u8 getstatus = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 int ret = 0;
Taku Izumi7f2feec2008-09-05 12:11:26 +0900586 struct controller *ctrl = p_slot->ctrl;
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 Kaneshigeae416e62008-04-25 14:39:06 -0700594 if (!HP_SUPR_RM(p_slot->ctrl)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
596 if (ret || !getstatus) {
Taku Izumi7f2feec2008-09-05 12:11:26 +0900597 ctrl_info(ctrl, "%s: no adapter on slot(%s)\n",
Alex Chiange1acb242008-10-20 17:41:38 -0600598 __func__, slot_name(p_slot));
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 Kaneshigeae416e62008-04-25 14:39:06 -0700604 if (MRL_SENS(p_slot->ctrl)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700605 ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
606 if (ret || getstatus) {
Taku Izumi7f2feec2008-09-05 12:11:26 +0900607 ctrl_info(ctrl, "%s: latch open on slot(%s)\n",
Alex Chiange1acb242008-10-20 17:41:38 -0600608 __func__, slot_name(p_slot));
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 Kaneshigeae416e62008-04-25 14:39:06 -0700614 if (POWER_CTRL(p_slot->ctrl)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
616 if (ret || !getstatus) {
Taku Izumi7f2feec2008-09-05 12:11:26 +0900617 ctrl_info(ctrl, "%s: already disabled slot(%s)\n",
Alex Chiange1acb242008-10-20 17:41:38 -0600618 __func__, slot_name(p_slot));
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 }
622 }
623
rajesh.shah@intel.comca22a5e2005-10-31 16:20:08 -0800624 ret = remove_board(p_slot);
625 update_slot_info(p_slot);
Kenji Kaneshigedd5619c2006-09-22 10:17:29 -0700626
627 mutex_unlock(&p_slot->ctrl->crit_sect);
rajesh.shah@intel.comca22a5e2005-10-31 16:20:08 -0800628 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629}
630
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800631int pciehp_sysfs_enable_slot(struct slot *p_slot)
632{
633 int retval = -ENODEV;
Taku Izumi7f2feec2008-09-05 12:11:26 +0900634 struct controller *ctrl = p_slot->ctrl;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800635
636 mutex_lock(&p_slot->lock);
637 switch (p_slot->state) {
638 case BLINKINGON_STATE:
639 cancel_delayed_work(&p_slot->work);
640 case STATIC_STATE:
641 p_slot->state = POWERON_STATE;
642 mutex_unlock(&p_slot->lock);
643 retval = pciehp_enable_slot(p_slot);
644 mutex_lock(&p_slot->lock);
645 p_slot->state = STATIC_STATE;
646 break;
647 case POWERON_STATE:
Taku Izumi7f2feec2008-09-05 12:11:26 +0900648 ctrl_info(ctrl, "Slot %s is already in powering on state\n",
Alex Chiange1acb242008-10-20 17:41:38 -0600649 slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800650 break;
651 case BLINKINGOFF_STATE:
652 case POWEROFF_STATE:
Alex Chiange1acb242008-10-20 17:41:38 -0600653 ctrl_info(ctrl, "Already enabled on slot %s\n",
654 slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800655 break;
656 default:
Alex Chiange1acb242008-10-20 17:41:38 -0600657 ctrl_err(ctrl, "Not a valid state on slot %s\n",
658 slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800659 break;
660 }
661 mutex_unlock(&p_slot->lock);
662
663 return retval;
664}
665
666int pciehp_sysfs_disable_slot(struct slot *p_slot)
667{
668 int retval = -ENODEV;
Taku Izumi7f2feec2008-09-05 12:11:26 +0900669 struct controller *ctrl = p_slot->ctrl;
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800670
671 mutex_lock(&p_slot->lock);
672 switch (p_slot->state) {
673 case BLINKINGOFF_STATE:
674 cancel_delayed_work(&p_slot->work);
675 case STATIC_STATE:
676 p_slot->state = POWEROFF_STATE;
677 mutex_unlock(&p_slot->lock);
678 retval = pciehp_disable_slot(p_slot);
679 mutex_lock(&p_slot->lock);
680 p_slot->state = STATIC_STATE;
681 break;
682 case POWEROFF_STATE:
Taku Izumi7f2feec2008-09-05 12:11:26 +0900683 ctrl_info(ctrl, "Slot %s is already in powering off state\n",
Alex Chiange1acb242008-10-20 17:41:38 -0600684 slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800685 break;
686 case BLINKINGON_STATE:
687 case POWERON_STATE:
Alex Chiange1acb242008-10-20 17:41:38 -0600688 ctrl_info(ctrl, "Already disabled on slot %s\n",
689 slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800690 break;
691 default:
Alex Chiange1acb242008-10-20 17:41:38 -0600692 ctrl_err(ctrl, "Not a valid state on slot %s\n",
693 slot_name(p_slot));
Kenji Kaneshige5d386e12007-03-06 15:02:26 -0800694 break;
695 }
696 mutex_unlock(&p_slot->lock);
697
698 return retval;
699}