Commit f652e7d2916fe2fcf9e7d709aa5b7476b431e2dd
1 parent
d347e75847
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
PCI: shpchp: Use per-slot workqueues to avoid deadlock
When we have an SHPC-capable bridge with a second SHPC-capable bridge below it, pushing the upstream bridge's attention button causes a deadlock. The deadlock happens because we use the shpchp_wq workqueue to run shpchp_pushbutton_thread(), which uses shpchp_disable_slot() to remove devices below the upstream bridge. When we remove the downstream bridge, we call shpc_remove(), the shpchp driver's .remove() method. That calls flush_workqueue(shpchp_wq), which deadlocks because the shpchp_pushbutton_thread() work item is still running. This patch avoids the deadlock by creating a workqueue for every slot and removing the single shared workqueue. Here's the call path that leads to the deadlock: shpchp_queue_pushbutton_work queue_work(shpchp_wq) # shpchp_pushbutton_thread ... shpchp_pushbutton_thread shpchp_disable_slot remove_board shpchp_unconfigure_device pci_stop_and_remove_bus_device ... shpc_remove # shpchp driver .remove method hpc_release_ctlr cleanup_slots flush_workqueue(shpchp_wq) This change is based on code inspection, since we don't have hardware with this topology. Based-on-patch-by: Yijing Wang <wangyijing@huawei.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> CC: stable@vger.kernel.org
Showing 3 changed files with 18 additions and 16 deletions Inline Diff
drivers/pci/hotplug/shpchp.h
1 | /* | 1 | /* |
2 | * Standard Hot Plug Controller Driver | 2 | * Standard Hot Plug Controller Driver |
3 | * | 3 | * |
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | 4 | * Copyright (C) 1995,2001 Compaq Computer Corporation |
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | 5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) |
6 | * Copyright (C) 2001 IBM | 6 | * Copyright (C) 2001 IBM |
7 | * Copyright (C) 2003-2004 Intel Corporation | 7 | * Copyright (C) 2003-2004 Intel Corporation |
8 | * | 8 | * |
9 | * All rights reserved. | 9 | * All rights reserved. |
10 | * | 10 | * |
11 | * This program is free software; you can redistribute it and/or modify | 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 | 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 | 13 | * the Free Software Foundation; either version 2 of the License, or (at |
14 | * your option) any later version. | 14 | * your option) any later version. |
15 | * | 15 | * |
16 | * This program is distributed in the hope that it will be useful, but | 16 | * This program is distributed in the hope that it will be useful, but |
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 17 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | 18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or |
19 | * NON INFRINGEMENT. See the GNU General Public License for more | 19 | * NON INFRINGEMENT. See the GNU General Public License for more |
20 | * details. | 20 | * details. |
21 | * | 21 | * |
22 | * You should have received a copy of the GNU General Public License | 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 | 23 | * along with this program; if not, write to the Free Software |
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
25 | * | 25 | * |
26 | * Send feedback to <greg@kroah.com>,<kristen.c.accardi@intel.com> | 26 | * Send feedback to <greg@kroah.com>,<kristen.c.accardi@intel.com> |
27 | * | 27 | * |
28 | */ | 28 | */ |
29 | #ifndef _SHPCHP_H | 29 | #ifndef _SHPCHP_H |
30 | #define _SHPCHP_H | 30 | #define _SHPCHP_H |
31 | 31 | ||
32 | #include <linux/types.h> | 32 | #include <linux/types.h> |
33 | #include <linux/pci.h> | 33 | #include <linux/pci.h> |
34 | #include <linux/pci_hotplug.h> | 34 | #include <linux/pci_hotplug.h> |
35 | #include <linux/delay.h> | 35 | #include <linux/delay.h> |
36 | #include <linux/sched.h> /* signal_pending(), struct timer_list */ | 36 | #include <linux/sched.h> /* signal_pending(), struct timer_list */ |
37 | #include <linux/mutex.h> | 37 | #include <linux/mutex.h> |
38 | #include <linux/workqueue.h> | 38 | #include <linux/workqueue.h> |
39 | 39 | ||
40 | #if !defined(MODULE) | 40 | #if !defined(MODULE) |
41 | #define MY_NAME "shpchp" | 41 | #define MY_NAME "shpchp" |
42 | #else | 42 | #else |
43 | #define MY_NAME THIS_MODULE->name | 43 | #define MY_NAME THIS_MODULE->name |
44 | #endif | 44 | #endif |
45 | 45 | ||
46 | extern bool shpchp_poll_mode; | 46 | extern bool shpchp_poll_mode; |
47 | extern int shpchp_poll_time; | 47 | extern int shpchp_poll_time; |
48 | extern bool shpchp_debug; | 48 | extern bool shpchp_debug; |
49 | extern struct workqueue_struct *shpchp_wq; | ||
50 | 49 | ||
51 | #define dbg(format, arg...) \ | 50 | #define dbg(format, arg...) \ |
52 | do { \ | 51 | do { \ |
53 | if (shpchp_debug) \ | 52 | if (shpchp_debug) \ |
54 | printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); \ | 53 | printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); \ |
55 | } while (0) | 54 | } while (0) |
56 | #define err(format, arg...) \ | 55 | #define err(format, arg...) \ |
57 | printk(KERN_ERR "%s: " format, MY_NAME , ## arg) | 56 | printk(KERN_ERR "%s: " format, MY_NAME , ## arg) |
58 | #define info(format, arg...) \ | 57 | #define info(format, arg...) \ |
59 | printk(KERN_INFO "%s: " format, MY_NAME , ## arg) | 58 | printk(KERN_INFO "%s: " format, MY_NAME , ## arg) |
60 | #define warn(format, arg...) \ | 59 | #define warn(format, arg...) \ |
61 | printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) | 60 | printk(KERN_WARNING "%s: " format, MY_NAME , ## arg) |
62 | 61 | ||
63 | #define ctrl_dbg(ctrl, format, arg...) \ | 62 | #define ctrl_dbg(ctrl, format, arg...) \ |
64 | do { \ | 63 | do { \ |
65 | if (shpchp_debug) \ | 64 | if (shpchp_debug) \ |
66 | dev_printk(KERN_DEBUG, &ctrl->pci_dev->dev, \ | 65 | dev_printk(KERN_DEBUG, &ctrl->pci_dev->dev, \ |
67 | format, ## arg); \ | 66 | format, ## arg); \ |
68 | } while (0) | 67 | } while (0) |
69 | #define ctrl_err(ctrl, format, arg...) \ | 68 | #define ctrl_err(ctrl, format, arg...) \ |
70 | dev_err(&ctrl->pci_dev->dev, format, ## arg) | 69 | dev_err(&ctrl->pci_dev->dev, format, ## arg) |
71 | #define ctrl_info(ctrl, format, arg...) \ | 70 | #define ctrl_info(ctrl, format, arg...) \ |
72 | dev_info(&ctrl->pci_dev->dev, format, ## arg) | 71 | dev_info(&ctrl->pci_dev->dev, format, ## arg) |
73 | #define ctrl_warn(ctrl, format, arg...) \ | 72 | #define ctrl_warn(ctrl, format, arg...) \ |
74 | dev_warn(&ctrl->pci_dev->dev, format, ## arg) | 73 | dev_warn(&ctrl->pci_dev->dev, format, ## arg) |
75 | 74 | ||
76 | 75 | ||
77 | #define SLOT_NAME_SIZE 10 | 76 | #define SLOT_NAME_SIZE 10 |
78 | struct slot { | 77 | struct slot { |
79 | u8 bus; | 78 | u8 bus; |
80 | u8 device; | 79 | u8 device; |
81 | u16 status; | 80 | u16 status; |
82 | u32 number; | 81 | u32 number; |
83 | u8 is_a_board; | 82 | u8 is_a_board; |
84 | u8 state; | 83 | u8 state; |
85 | u8 presence_save; | 84 | u8 presence_save; |
86 | u8 pwr_save; | 85 | u8 pwr_save; |
87 | struct controller *ctrl; | 86 | struct controller *ctrl; |
88 | struct hpc_ops *hpc_ops; | 87 | struct hpc_ops *hpc_ops; |
89 | struct hotplug_slot *hotplug_slot; | 88 | struct hotplug_slot *hotplug_slot; |
90 | struct list_head slot_list; | 89 | struct list_head slot_list; |
91 | struct delayed_work work; /* work for button event */ | 90 | struct delayed_work work; /* work for button event */ |
92 | struct mutex lock; | 91 | struct mutex lock; |
92 | struct workqueue_struct *wq; | ||
93 | u8 hp_slot; | 93 | u8 hp_slot; |
94 | }; | 94 | }; |
95 | 95 | ||
96 | struct event_info { | 96 | struct event_info { |
97 | u32 event_type; | 97 | u32 event_type; |
98 | struct slot *p_slot; | 98 | struct slot *p_slot; |
99 | struct work_struct work; | 99 | struct work_struct work; |
100 | }; | 100 | }; |
101 | 101 | ||
102 | struct controller { | 102 | struct controller { |
103 | struct mutex crit_sect; /* critical section mutex */ | 103 | struct mutex crit_sect; /* critical section mutex */ |
104 | struct mutex cmd_lock; /* command lock */ | 104 | struct mutex cmd_lock; /* command lock */ |
105 | int num_slots; /* Number of slots on ctlr */ | 105 | int num_slots; /* Number of slots on ctlr */ |
106 | int slot_num_inc; /* 1 or -1 */ | 106 | int slot_num_inc; /* 1 or -1 */ |
107 | struct pci_dev *pci_dev; | 107 | struct pci_dev *pci_dev; |
108 | struct list_head slot_list; | 108 | struct list_head slot_list; |
109 | struct hpc_ops *hpc_ops; | 109 | struct hpc_ops *hpc_ops; |
110 | wait_queue_head_t queue; /* sleep & wake process */ | 110 | wait_queue_head_t queue; /* sleep & wake process */ |
111 | u8 slot_device_offset; | 111 | u8 slot_device_offset; |
112 | u32 pcix_misc2_reg; /* for amd pogo errata */ | 112 | u32 pcix_misc2_reg; /* for amd pogo errata */ |
113 | u32 first_slot; /* First physical slot number */ | 113 | u32 first_slot; /* First physical slot number */ |
114 | u32 cap_offset; | 114 | u32 cap_offset; |
115 | unsigned long mmio_base; | 115 | unsigned long mmio_base; |
116 | unsigned long mmio_size; | 116 | unsigned long mmio_size; |
117 | void __iomem *creg; | 117 | void __iomem *creg; |
118 | struct timer_list poll_timer; | 118 | struct timer_list poll_timer; |
119 | }; | 119 | }; |
120 | 120 | ||
121 | /* Define AMD SHPC ID */ | 121 | /* Define AMD SHPC ID */ |
122 | #define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450 | 122 | #define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450 |
123 | #define PCI_DEVICE_ID_AMD_POGO_7458 0x7458 | 123 | #define PCI_DEVICE_ID_AMD_POGO_7458 0x7458 |
124 | 124 | ||
125 | /* AMD PCI-X bridge registers */ | 125 | /* AMD PCI-X bridge registers */ |
126 | #define PCIX_MEM_BASE_LIMIT_OFFSET 0x1C | 126 | #define PCIX_MEM_BASE_LIMIT_OFFSET 0x1C |
127 | #define PCIX_MISCII_OFFSET 0x48 | 127 | #define PCIX_MISCII_OFFSET 0x48 |
128 | #define PCIX_MISC_BRIDGE_ERRORS_OFFSET 0x80 | 128 | #define PCIX_MISC_BRIDGE_ERRORS_OFFSET 0x80 |
129 | 129 | ||
130 | /* AMD PCIX_MISCII masks and offsets */ | 130 | /* AMD PCIX_MISCII masks and offsets */ |
131 | #define PERRNONFATALENABLE_MASK 0x00040000 | 131 | #define PERRNONFATALENABLE_MASK 0x00040000 |
132 | #define PERRFATALENABLE_MASK 0x00080000 | 132 | #define PERRFATALENABLE_MASK 0x00080000 |
133 | #define PERRFLOODENABLE_MASK 0x00100000 | 133 | #define PERRFLOODENABLE_MASK 0x00100000 |
134 | #define SERRNONFATALENABLE_MASK 0x00200000 | 134 | #define SERRNONFATALENABLE_MASK 0x00200000 |
135 | #define SERRFATALENABLE_MASK 0x00400000 | 135 | #define SERRFATALENABLE_MASK 0x00400000 |
136 | 136 | ||
137 | /* AMD PCIX_MISC_BRIDGE_ERRORS masks and offsets */ | 137 | /* AMD PCIX_MISC_BRIDGE_ERRORS masks and offsets */ |
138 | #define PERR_OBSERVED_MASK 0x00000001 | 138 | #define PERR_OBSERVED_MASK 0x00000001 |
139 | 139 | ||
140 | /* AMD PCIX_MEM_BASE_LIMIT masks */ | 140 | /* AMD PCIX_MEM_BASE_LIMIT masks */ |
141 | #define RSE_MASK 0x40000000 | 141 | #define RSE_MASK 0x40000000 |
142 | 142 | ||
143 | #define INT_BUTTON_IGNORE 0 | 143 | #define INT_BUTTON_IGNORE 0 |
144 | #define INT_PRESENCE_ON 1 | 144 | #define INT_PRESENCE_ON 1 |
145 | #define INT_PRESENCE_OFF 2 | 145 | #define INT_PRESENCE_OFF 2 |
146 | #define INT_SWITCH_CLOSE 3 | 146 | #define INT_SWITCH_CLOSE 3 |
147 | #define INT_SWITCH_OPEN 4 | 147 | #define INT_SWITCH_OPEN 4 |
148 | #define INT_POWER_FAULT 5 | 148 | #define INT_POWER_FAULT 5 |
149 | #define INT_POWER_FAULT_CLEAR 6 | 149 | #define INT_POWER_FAULT_CLEAR 6 |
150 | #define INT_BUTTON_PRESS 7 | 150 | #define INT_BUTTON_PRESS 7 |
151 | #define INT_BUTTON_RELEASE 8 | 151 | #define INT_BUTTON_RELEASE 8 |
152 | #define INT_BUTTON_CANCEL 9 | 152 | #define INT_BUTTON_CANCEL 9 |
153 | 153 | ||
154 | #define STATIC_STATE 0 | 154 | #define STATIC_STATE 0 |
155 | #define BLINKINGON_STATE 1 | 155 | #define BLINKINGON_STATE 1 |
156 | #define BLINKINGOFF_STATE 2 | 156 | #define BLINKINGOFF_STATE 2 |
157 | #define POWERON_STATE 3 | 157 | #define POWERON_STATE 3 |
158 | #define POWEROFF_STATE 4 | 158 | #define POWEROFF_STATE 4 |
159 | 159 | ||
160 | /* Error messages */ | 160 | /* Error messages */ |
161 | #define INTERLOCK_OPEN 0x00000002 | 161 | #define INTERLOCK_OPEN 0x00000002 |
162 | #define ADD_NOT_SUPPORTED 0x00000003 | 162 | #define ADD_NOT_SUPPORTED 0x00000003 |
163 | #define CARD_FUNCTIONING 0x00000005 | 163 | #define CARD_FUNCTIONING 0x00000005 |
164 | #define ADAPTER_NOT_SAME 0x00000006 | 164 | #define ADAPTER_NOT_SAME 0x00000006 |
165 | #define NO_ADAPTER_PRESENT 0x00000009 | 165 | #define NO_ADAPTER_PRESENT 0x00000009 |
166 | #define NOT_ENOUGH_RESOURCES 0x0000000B | 166 | #define NOT_ENOUGH_RESOURCES 0x0000000B |
167 | #define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C | 167 | #define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C |
168 | #define WRONG_BUS_FREQUENCY 0x0000000D | 168 | #define WRONG_BUS_FREQUENCY 0x0000000D |
169 | #define POWER_FAILURE 0x0000000E | 169 | #define POWER_FAILURE 0x0000000E |
170 | 170 | ||
171 | extern int __must_check shpchp_create_ctrl_files(struct controller *ctrl); | 171 | extern int __must_check shpchp_create_ctrl_files(struct controller *ctrl); |
172 | extern void shpchp_remove_ctrl_files(struct controller *ctrl); | 172 | extern void shpchp_remove_ctrl_files(struct controller *ctrl); |
173 | extern int shpchp_sysfs_enable_slot(struct slot *slot); | 173 | extern int shpchp_sysfs_enable_slot(struct slot *slot); |
174 | extern int shpchp_sysfs_disable_slot(struct slot *slot); | 174 | extern int shpchp_sysfs_disable_slot(struct slot *slot); |
175 | extern u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl); | 175 | extern u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl); |
176 | extern u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl); | 176 | extern u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl); |
177 | extern u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl); | 177 | extern u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl); |
178 | extern u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl); | 178 | extern u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl); |
179 | extern int shpchp_configure_device(struct slot *p_slot); | 179 | extern int shpchp_configure_device(struct slot *p_slot); |
180 | extern int shpchp_unconfigure_device(struct slot *p_slot); | 180 | extern int shpchp_unconfigure_device(struct slot *p_slot); |
181 | extern void cleanup_slots(struct controller *ctrl); | 181 | extern void cleanup_slots(struct controller *ctrl); |
182 | extern void shpchp_queue_pushbutton_work(struct work_struct *work); | 182 | extern void shpchp_queue_pushbutton_work(struct work_struct *work); |
183 | extern int shpc_init( struct controller *ctrl, struct pci_dev *pdev); | 183 | extern int shpc_init( struct controller *ctrl, struct pci_dev *pdev); |
184 | 184 | ||
185 | static inline const char *slot_name(struct slot *slot) | 185 | static inline const char *slot_name(struct slot *slot) |
186 | { | 186 | { |
187 | return hotplug_slot_name(slot->hotplug_slot); | 187 | return hotplug_slot_name(slot->hotplug_slot); |
188 | } | 188 | } |
189 | 189 | ||
190 | #ifdef CONFIG_ACPI | 190 | #ifdef CONFIG_ACPI |
191 | #include <linux/pci-acpi.h> | 191 | #include <linux/pci-acpi.h> |
192 | static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev) | 192 | static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev) |
193 | { | 193 | { |
194 | u32 flags = OSC_SHPC_NATIVE_HP_CONTROL; | 194 | u32 flags = OSC_SHPC_NATIVE_HP_CONTROL; |
195 | return acpi_get_hp_hw_control_from_firmware(dev, flags); | 195 | return acpi_get_hp_hw_control_from_firmware(dev, flags); |
196 | } | 196 | } |
197 | #else | 197 | #else |
198 | #define get_hp_hw_control_from_firmware(dev) (0) | 198 | #define get_hp_hw_control_from_firmware(dev) (0) |
199 | #endif | 199 | #endif |
200 | 200 | ||
201 | struct ctrl_reg { | 201 | struct ctrl_reg { |
202 | volatile u32 base_offset; | 202 | volatile u32 base_offset; |
203 | volatile u32 slot_avail1; | 203 | volatile u32 slot_avail1; |
204 | volatile u32 slot_avail2; | 204 | volatile u32 slot_avail2; |
205 | volatile u32 slot_config; | 205 | volatile u32 slot_config; |
206 | volatile u16 sec_bus_config; | 206 | volatile u16 sec_bus_config; |
207 | volatile u8 msi_ctrl; | 207 | volatile u8 msi_ctrl; |
208 | volatile u8 prog_interface; | 208 | volatile u8 prog_interface; |
209 | volatile u16 cmd; | 209 | volatile u16 cmd; |
210 | volatile u16 cmd_status; | 210 | volatile u16 cmd_status; |
211 | volatile u32 intr_loc; | 211 | volatile u32 intr_loc; |
212 | volatile u32 serr_loc; | 212 | volatile u32 serr_loc; |
213 | volatile u32 serr_intr_enable; | 213 | volatile u32 serr_intr_enable; |
214 | volatile u32 slot1; | 214 | volatile u32 slot1; |
215 | } __attribute__ ((packed)); | 215 | } __attribute__ ((packed)); |
216 | 216 | ||
217 | /* offsets to the controller registers based on the above structure layout */ | 217 | /* offsets to the controller registers based on the above structure layout */ |
218 | enum ctrl_offsets { | 218 | enum ctrl_offsets { |
219 | BASE_OFFSET = offsetof(struct ctrl_reg, base_offset), | 219 | BASE_OFFSET = offsetof(struct ctrl_reg, base_offset), |
220 | SLOT_AVAIL1 = offsetof(struct ctrl_reg, slot_avail1), | 220 | SLOT_AVAIL1 = offsetof(struct ctrl_reg, slot_avail1), |
221 | SLOT_AVAIL2 = offsetof(struct ctrl_reg, slot_avail2), | 221 | SLOT_AVAIL2 = offsetof(struct ctrl_reg, slot_avail2), |
222 | SLOT_CONFIG = offsetof(struct ctrl_reg, slot_config), | 222 | SLOT_CONFIG = offsetof(struct ctrl_reg, slot_config), |
223 | SEC_BUS_CONFIG = offsetof(struct ctrl_reg, sec_bus_config), | 223 | SEC_BUS_CONFIG = offsetof(struct ctrl_reg, sec_bus_config), |
224 | MSI_CTRL = offsetof(struct ctrl_reg, msi_ctrl), | 224 | MSI_CTRL = offsetof(struct ctrl_reg, msi_ctrl), |
225 | PROG_INTERFACE = offsetof(struct ctrl_reg, prog_interface), | 225 | PROG_INTERFACE = offsetof(struct ctrl_reg, prog_interface), |
226 | CMD = offsetof(struct ctrl_reg, cmd), | 226 | CMD = offsetof(struct ctrl_reg, cmd), |
227 | CMD_STATUS = offsetof(struct ctrl_reg, cmd_status), | 227 | CMD_STATUS = offsetof(struct ctrl_reg, cmd_status), |
228 | INTR_LOC = offsetof(struct ctrl_reg, intr_loc), | 228 | INTR_LOC = offsetof(struct ctrl_reg, intr_loc), |
229 | SERR_LOC = offsetof(struct ctrl_reg, serr_loc), | 229 | SERR_LOC = offsetof(struct ctrl_reg, serr_loc), |
230 | SERR_INTR_ENABLE = offsetof(struct ctrl_reg, serr_intr_enable), | 230 | SERR_INTR_ENABLE = offsetof(struct ctrl_reg, serr_intr_enable), |
231 | SLOT1 = offsetof(struct ctrl_reg, slot1), | 231 | SLOT1 = offsetof(struct ctrl_reg, slot1), |
232 | }; | 232 | }; |
233 | 233 | ||
234 | static inline struct slot *get_slot(struct hotplug_slot *hotplug_slot) | 234 | static inline struct slot *get_slot(struct hotplug_slot *hotplug_slot) |
235 | { | 235 | { |
236 | return hotplug_slot->private; | 236 | return hotplug_slot->private; |
237 | } | 237 | } |
238 | 238 | ||
239 | static inline struct slot *shpchp_find_slot(struct controller *ctrl, u8 device) | 239 | static inline struct slot *shpchp_find_slot(struct controller *ctrl, u8 device) |
240 | { | 240 | { |
241 | struct slot *slot; | 241 | struct slot *slot; |
242 | 242 | ||
243 | list_for_each_entry(slot, &ctrl->slot_list, slot_list) { | 243 | list_for_each_entry(slot, &ctrl->slot_list, slot_list) { |
244 | if (slot->device == device) | 244 | if (slot->device == device) |
245 | return slot; | 245 | return slot; |
246 | } | 246 | } |
247 | 247 | ||
248 | ctrl_err(ctrl, "Slot (device=0x%02x) not found\n", device); | 248 | ctrl_err(ctrl, "Slot (device=0x%02x) not found\n", device); |
249 | return NULL; | 249 | return NULL; |
250 | } | 250 | } |
251 | 251 | ||
252 | static inline void amd_pogo_errata_save_misc_reg(struct slot *p_slot) | 252 | static inline void amd_pogo_errata_save_misc_reg(struct slot *p_slot) |
253 | { | 253 | { |
254 | u32 pcix_misc2_temp; | 254 | u32 pcix_misc2_temp; |
255 | 255 | ||
256 | /* save MiscII register */ | 256 | /* save MiscII register */ |
257 | pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp); | 257 | pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp); |
258 | 258 | ||
259 | p_slot->ctrl->pcix_misc2_reg = pcix_misc2_temp; | 259 | p_slot->ctrl->pcix_misc2_reg = pcix_misc2_temp; |
260 | 260 | ||
261 | /* clear SERR/PERR enable bits */ | 261 | /* clear SERR/PERR enable bits */ |
262 | pcix_misc2_temp &= ~SERRFATALENABLE_MASK; | 262 | pcix_misc2_temp &= ~SERRFATALENABLE_MASK; |
263 | pcix_misc2_temp &= ~SERRNONFATALENABLE_MASK; | 263 | pcix_misc2_temp &= ~SERRNONFATALENABLE_MASK; |
264 | pcix_misc2_temp &= ~PERRFLOODENABLE_MASK; | 264 | pcix_misc2_temp &= ~PERRFLOODENABLE_MASK; |
265 | pcix_misc2_temp &= ~PERRFATALENABLE_MASK; | 265 | pcix_misc2_temp &= ~PERRFATALENABLE_MASK; |
266 | pcix_misc2_temp &= ~PERRNONFATALENABLE_MASK; | 266 | pcix_misc2_temp &= ~PERRNONFATALENABLE_MASK; |
267 | pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, pcix_misc2_temp); | 267 | pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, pcix_misc2_temp); |
268 | } | 268 | } |
269 | 269 | ||
270 | static inline void amd_pogo_errata_restore_misc_reg(struct slot *p_slot) | 270 | static inline void amd_pogo_errata_restore_misc_reg(struct slot *p_slot) |
271 | { | 271 | { |
272 | u32 pcix_misc2_temp; | 272 | u32 pcix_misc2_temp; |
273 | u32 pcix_bridge_errors_reg; | 273 | u32 pcix_bridge_errors_reg; |
274 | u32 pcix_mem_base_reg; | 274 | u32 pcix_mem_base_reg; |
275 | u8 perr_set; | 275 | u8 perr_set; |
276 | u8 rse_set; | 276 | u8 rse_set; |
277 | 277 | ||
278 | /* write-one-to-clear Bridge_Errors[ PERR_OBSERVED ] */ | 278 | /* write-one-to-clear Bridge_Errors[ PERR_OBSERVED ] */ |
279 | pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISC_BRIDGE_ERRORS_OFFSET, &pcix_bridge_errors_reg); | 279 | pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MISC_BRIDGE_ERRORS_OFFSET, &pcix_bridge_errors_reg); |
280 | perr_set = pcix_bridge_errors_reg & PERR_OBSERVED_MASK; | 280 | perr_set = pcix_bridge_errors_reg & PERR_OBSERVED_MASK; |
281 | if (perr_set) { | 281 | if (perr_set) { |
282 | ctrl_dbg(p_slot->ctrl, | 282 | ctrl_dbg(p_slot->ctrl, |
283 | "Bridge_Errors[ PERR_OBSERVED = %08X] (W1C)\n", | 283 | "Bridge_Errors[ PERR_OBSERVED = %08X] (W1C)\n", |
284 | perr_set); | 284 | perr_set); |
285 | 285 | ||
286 | pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISC_BRIDGE_ERRORS_OFFSET, perr_set); | 286 | pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISC_BRIDGE_ERRORS_OFFSET, perr_set); |
287 | } | 287 | } |
288 | 288 | ||
289 | /* write-one-to-clear Memory_Base_Limit[ RSE ] */ | 289 | /* write-one-to-clear Memory_Base_Limit[ RSE ] */ |
290 | pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, &pcix_mem_base_reg); | 290 | pci_read_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, &pcix_mem_base_reg); |
291 | rse_set = pcix_mem_base_reg & RSE_MASK; | 291 | rse_set = pcix_mem_base_reg & RSE_MASK; |
292 | if (rse_set) { | 292 | if (rse_set) { |
293 | ctrl_dbg(p_slot->ctrl, "Memory_Base_Limit[ RSE ] (W1C)\n"); | 293 | ctrl_dbg(p_slot->ctrl, "Memory_Base_Limit[ RSE ] (W1C)\n"); |
294 | 294 | ||
295 | pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, rse_set); | 295 | pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MEM_BASE_LIMIT_OFFSET, rse_set); |
296 | } | 296 | } |
297 | /* restore MiscII register */ | 297 | /* restore MiscII register */ |
298 | pci_read_config_dword( p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp ); | 298 | pci_read_config_dword( p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, &pcix_misc2_temp ); |
299 | 299 | ||
300 | if (p_slot->ctrl->pcix_misc2_reg & SERRFATALENABLE_MASK) | 300 | if (p_slot->ctrl->pcix_misc2_reg & SERRFATALENABLE_MASK) |
301 | pcix_misc2_temp |= SERRFATALENABLE_MASK; | 301 | pcix_misc2_temp |= SERRFATALENABLE_MASK; |
302 | else | 302 | else |
303 | pcix_misc2_temp &= ~SERRFATALENABLE_MASK; | 303 | pcix_misc2_temp &= ~SERRFATALENABLE_MASK; |
304 | 304 | ||
305 | if (p_slot->ctrl->pcix_misc2_reg & SERRNONFATALENABLE_MASK) | 305 | if (p_slot->ctrl->pcix_misc2_reg & SERRNONFATALENABLE_MASK) |
306 | pcix_misc2_temp |= SERRNONFATALENABLE_MASK; | 306 | pcix_misc2_temp |= SERRNONFATALENABLE_MASK; |
307 | else | 307 | else |
308 | pcix_misc2_temp &= ~SERRNONFATALENABLE_MASK; | 308 | pcix_misc2_temp &= ~SERRNONFATALENABLE_MASK; |
309 | 309 | ||
310 | if (p_slot->ctrl->pcix_misc2_reg & PERRFLOODENABLE_MASK) | 310 | if (p_slot->ctrl->pcix_misc2_reg & PERRFLOODENABLE_MASK) |
311 | pcix_misc2_temp |= PERRFLOODENABLE_MASK; | 311 | pcix_misc2_temp |= PERRFLOODENABLE_MASK; |
312 | else | 312 | else |
313 | pcix_misc2_temp &= ~PERRFLOODENABLE_MASK; | 313 | pcix_misc2_temp &= ~PERRFLOODENABLE_MASK; |
314 | 314 | ||
315 | if (p_slot->ctrl->pcix_misc2_reg & PERRFATALENABLE_MASK) | 315 | if (p_slot->ctrl->pcix_misc2_reg & PERRFATALENABLE_MASK) |
316 | pcix_misc2_temp |= PERRFATALENABLE_MASK; | 316 | pcix_misc2_temp |= PERRFATALENABLE_MASK; |
317 | else | 317 | else |
318 | pcix_misc2_temp &= ~PERRFATALENABLE_MASK; | 318 | pcix_misc2_temp &= ~PERRFATALENABLE_MASK; |
319 | 319 | ||
320 | if (p_slot->ctrl->pcix_misc2_reg & PERRNONFATALENABLE_MASK) | 320 | if (p_slot->ctrl->pcix_misc2_reg & PERRNONFATALENABLE_MASK) |
321 | pcix_misc2_temp |= PERRNONFATALENABLE_MASK; | 321 | pcix_misc2_temp |= PERRNONFATALENABLE_MASK; |
322 | else | 322 | else |
323 | pcix_misc2_temp &= ~PERRNONFATALENABLE_MASK; | 323 | pcix_misc2_temp &= ~PERRNONFATALENABLE_MASK; |
324 | pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, pcix_misc2_temp); | 324 | pci_write_config_dword(p_slot->ctrl->pci_dev, PCIX_MISCII_OFFSET, pcix_misc2_temp); |
325 | } | 325 | } |
326 | 326 | ||
327 | struct hpc_ops { | 327 | struct hpc_ops { |
328 | int (*power_on_slot)(struct slot *slot); | 328 | int (*power_on_slot)(struct slot *slot); |
329 | int (*slot_enable)(struct slot *slot); | 329 | int (*slot_enable)(struct slot *slot); |
330 | int (*slot_disable)(struct slot *slot); | 330 | int (*slot_disable)(struct slot *slot); |
331 | int (*set_bus_speed_mode)(struct slot *slot, enum pci_bus_speed speed); | 331 | int (*set_bus_speed_mode)(struct slot *slot, enum pci_bus_speed speed); |
332 | int (*get_power_status)(struct slot *slot, u8 *status); | 332 | int (*get_power_status)(struct slot *slot, u8 *status); |
333 | int (*get_attention_status)(struct slot *slot, u8 *status); | 333 | int (*get_attention_status)(struct slot *slot, u8 *status); |
334 | int (*set_attention_status)(struct slot *slot, u8 status); | 334 | int (*set_attention_status)(struct slot *slot, u8 status); |
335 | int (*get_latch_status)(struct slot *slot, u8 *status); | 335 | int (*get_latch_status)(struct slot *slot, u8 *status); |
336 | int (*get_adapter_status)(struct slot *slot, u8 *status); | 336 | int (*get_adapter_status)(struct slot *slot, u8 *status); |
337 | int (*get_adapter_speed)(struct slot *slot, enum pci_bus_speed *speed); | 337 | int (*get_adapter_speed)(struct slot *slot, enum pci_bus_speed *speed); |
338 | int (*get_mode1_ECC_cap)(struct slot *slot, u8 *mode); | 338 | int (*get_mode1_ECC_cap)(struct slot *slot, u8 *mode); |
339 | int (*get_prog_int)(struct slot *slot, u8 *prog_int); | 339 | int (*get_prog_int)(struct slot *slot, u8 *prog_int); |
340 | int (*query_power_fault)(struct slot *slot); | 340 | int (*query_power_fault)(struct slot *slot); |
341 | void (*green_led_on)(struct slot *slot); | 341 | void (*green_led_on)(struct slot *slot); |
342 | void (*green_led_off)(struct slot *slot); | 342 | void (*green_led_off)(struct slot *slot); |
343 | void (*green_led_blink)(struct slot *slot); | 343 | void (*green_led_blink)(struct slot *slot); |
344 | void (*release_ctlr)(struct controller *ctrl); | 344 | void (*release_ctlr)(struct controller *ctrl); |
345 | int (*check_cmd_status)(struct controller *ctrl); | 345 | int (*check_cmd_status)(struct controller *ctrl); |
346 | }; | 346 | }; |
347 | 347 | ||
348 | #endif /* _SHPCHP_H */ | 348 | #endif /* _SHPCHP_H */ |
drivers/pci/hotplug/shpchp_core.c
1 | /* | 1 | /* |
2 | * Standard Hot Plug Controller Driver | 2 | * Standard Hot Plug Controller Driver |
3 | * | 3 | * |
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | 4 | * Copyright (C) 1995,2001 Compaq Computer Corporation |
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | 5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) |
6 | * Copyright (C) 2001 IBM Corp. | 6 | * Copyright (C) 2001 IBM Corp. |
7 | * Copyright (C) 2003-2004 Intel Corporation | 7 | * Copyright (C) 2003-2004 Intel Corporation |
8 | * | 8 | * |
9 | * All rights reserved. | 9 | * All rights reserved. |
10 | * | 10 | * |
11 | * This program is free software; you can redistribute it and/or modify | 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 | 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 | 13 | * the Free Software Foundation; either version 2 of the License, or (at |
14 | * your option) any later version. | 14 | * your option) any later version. |
15 | * | 15 | * |
16 | * This program is distributed in the hope that it will be useful, but | 16 | * This program is distributed in the hope that it will be useful, but |
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 17 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | 18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or |
19 | * NON INFRINGEMENT. See the GNU General Public License for more | 19 | * NON INFRINGEMENT. See the GNU General Public License for more |
20 | * details. | 20 | * details. |
21 | * | 21 | * |
22 | * You should have received a copy of the GNU General Public License | 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 | 23 | * along with this program; if not, write to the Free Software |
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
25 | * | 25 | * |
26 | * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> | 26 | * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> |
27 | * | 27 | * |
28 | */ | 28 | */ |
29 | 29 | ||
30 | #include <linux/module.h> | 30 | #include <linux/module.h> |
31 | #include <linux/moduleparam.h> | 31 | #include <linux/moduleparam.h> |
32 | #include <linux/kernel.h> | 32 | #include <linux/kernel.h> |
33 | #include <linux/types.h> | 33 | #include <linux/types.h> |
34 | #include <linux/slab.h> | 34 | #include <linux/slab.h> |
35 | #include <linux/pci.h> | 35 | #include <linux/pci.h> |
36 | #include "shpchp.h" | 36 | #include "shpchp.h" |
37 | 37 | ||
38 | /* Global variables */ | 38 | /* Global variables */ |
39 | bool shpchp_debug; | 39 | bool shpchp_debug; |
40 | bool shpchp_poll_mode; | 40 | bool shpchp_poll_mode; |
41 | int shpchp_poll_time; | 41 | int shpchp_poll_time; |
42 | struct workqueue_struct *shpchp_wq; | ||
43 | 42 | ||
44 | #define DRIVER_VERSION "0.4" | 43 | #define DRIVER_VERSION "0.4" |
45 | #define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>" | 44 | #define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>" |
46 | #define DRIVER_DESC "Standard Hot Plug PCI Controller Driver" | 45 | #define DRIVER_DESC "Standard Hot Plug PCI Controller Driver" |
47 | 46 | ||
48 | MODULE_AUTHOR(DRIVER_AUTHOR); | 47 | MODULE_AUTHOR(DRIVER_AUTHOR); |
49 | MODULE_DESCRIPTION(DRIVER_DESC); | 48 | MODULE_DESCRIPTION(DRIVER_DESC); |
50 | MODULE_LICENSE("GPL"); | 49 | MODULE_LICENSE("GPL"); |
51 | 50 | ||
52 | module_param(shpchp_debug, bool, 0644); | 51 | module_param(shpchp_debug, bool, 0644); |
53 | module_param(shpchp_poll_mode, bool, 0644); | 52 | module_param(shpchp_poll_mode, bool, 0644); |
54 | module_param(shpchp_poll_time, int, 0644); | 53 | module_param(shpchp_poll_time, int, 0644); |
55 | MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not"); | 54 | MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not"); |
56 | MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not"); | 55 | MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not"); |
57 | MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds"); | 56 | MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds"); |
58 | 57 | ||
59 | #define SHPC_MODULE_NAME "shpchp" | 58 | #define SHPC_MODULE_NAME "shpchp" |
60 | 59 | ||
61 | static int set_attention_status (struct hotplug_slot *slot, u8 value); | 60 | static int set_attention_status (struct hotplug_slot *slot, u8 value); |
62 | static int enable_slot (struct hotplug_slot *slot); | 61 | static int enable_slot (struct hotplug_slot *slot); |
63 | static int disable_slot (struct hotplug_slot *slot); | 62 | static int disable_slot (struct hotplug_slot *slot); |
64 | static int get_power_status (struct hotplug_slot *slot, u8 *value); | 63 | static int get_power_status (struct hotplug_slot *slot, u8 *value); |
65 | static int get_attention_status (struct hotplug_slot *slot, u8 *value); | 64 | static int get_attention_status (struct hotplug_slot *slot, u8 *value); |
66 | static int get_latch_status (struct hotplug_slot *slot, u8 *value); | 65 | static int get_latch_status (struct hotplug_slot *slot, u8 *value); |
67 | static int get_adapter_status (struct hotplug_slot *slot, u8 *value); | 66 | static int get_adapter_status (struct hotplug_slot *slot, u8 *value); |
68 | 67 | ||
69 | static struct hotplug_slot_ops shpchp_hotplug_slot_ops = { | 68 | static struct hotplug_slot_ops shpchp_hotplug_slot_ops = { |
70 | .set_attention_status = set_attention_status, | 69 | .set_attention_status = set_attention_status, |
71 | .enable_slot = enable_slot, | 70 | .enable_slot = enable_slot, |
72 | .disable_slot = disable_slot, | 71 | .disable_slot = disable_slot, |
73 | .get_power_status = get_power_status, | 72 | .get_power_status = get_power_status, |
74 | .get_attention_status = get_attention_status, | 73 | .get_attention_status = get_attention_status, |
75 | .get_latch_status = get_latch_status, | 74 | .get_latch_status = get_latch_status, |
76 | .get_adapter_status = get_adapter_status, | 75 | .get_adapter_status = get_adapter_status, |
77 | }; | 76 | }; |
78 | 77 | ||
79 | /** | 78 | /** |
80 | * release_slot - free up the memory used by a slot | 79 | * release_slot - free up the memory used by a slot |
81 | * @hotplug_slot: slot to free | 80 | * @hotplug_slot: slot to free |
82 | */ | 81 | */ |
83 | static void release_slot(struct hotplug_slot *hotplug_slot) | 82 | static void release_slot(struct hotplug_slot *hotplug_slot) |
84 | { | 83 | { |
85 | struct slot *slot = hotplug_slot->private; | 84 | struct slot *slot = hotplug_slot->private; |
86 | 85 | ||
87 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", | 86 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", |
88 | __func__, slot_name(slot)); | 87 | __func__, slot_name(slot)); |
89 | 88 | ||
90 | kfree(slot->hotplug_slot->info); | 89 | kfree(slot->hotplug_slot->info); |
91 | kfree(slot->hotplug_slot); | 90 | kfree(slot->hotplug_slot); |
92 | kfree(slot); | 91 | kfree(slot); |
93 | } | 92 | } |
94 | 93 | ||
95 | static int init_slots(struct controller *ctrl) | 94 | static int init_slots(struct controller *ctrl) |
96 | { | 95 | { |
97 | struct slot *slot; | 96 | struct slot *slot; |
98 | struct hotplug_slot *hotplug_slot; | 97 | struct hotplug_slot *hotplug_slot; |
99 | struct hotplug_slot_info *info; | 98 | struct hotplug_slot_info *info; |
100 | char name[SLOT_NAME_SIZE]; | 99 | char name[SLOT_NAME_SIZE]; |
101 | int retval; | 100 | int retval; |
102 | int i; | 101 | int i; |
103 | 102 | ||
104 | for (i = 0; i < ctrl->num_slots; i++) { | 103 | for (i = 0; i < ctrl->num_slots; i++) { |
105 | slot = kzalloc(sizeof(*slot), GFP_KERNEL); | 104 | slot = kzalloc(sizeof(*slot), GFP_KERNEL); |
106 | if (!slot) { | 105 | if (!slot) { |
107 | retval = -ENOMEM; | 106 | retval = -ENOMEM; |
108 | goto error; | 107 | goto error; |
109 | } | 108 | } |
110 | 109 | ||
111 | hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL); | 110 | hotplug_slot = kzalloc(sizeof(*hotplug_slot), GFP_KERNEL); |
112 | if (!hotplug_slot) { | 111 | if (!hotplug_slot) { |
113 | retval = -ENOMEM; | 112 | retval = -ENOMEM; |
114 | goto error_slot; | 113 | goto error_slot; |
115 | } | 114 | } |
116 | slot->hotplug_slot = hotplug_slot; | 115 | slot->hotplug_slot = hotplug_slot; |
117 | 116 | ||
118 | info = kzalloc(sizeof(*info), GFP_KERNEL); | 117 | info = kzalloc(sizeof(*info), GFP_KERNEL); |
119 | if (!info) { | 118 | if (!info) { |
120 | retval = -ENOMEM; | 119 | retval = -ENOMEM; |
121 | goto error_hpslot; | 120 | goto error_hpslot; |
122 | } | 121 | } |
123 | hotplug_slot->info = info; | 122 | hotplug_slot->info = info; |
124 | 123 | ||
125 | slot->hp_slot = i; | 124 | slot->hp_slot = i; |
126 | slot->ctrl = ctrl; | 125 | slot->ctrl = ctrl; |
127 | slot->bus = ctrl->pci_dev->subordinate->number; | 126 | slot->bus = ctrl->pci_dev->subordinate->number; |
128 | slot->device = ctrl->slot_device_offset + i; | 127 | slot->device = ctrl->slot_device_offset + i; |
129 | slot->hpc_ops = ctrl->hpc_ops; | 128 | slot->hpc_ops = ctrl->hpc_ops; |
130 | slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i); | 129 | slot->number = ctrl->first_slot + (ctrl->slot_num_inc * i); |
130 | |||
131 | snprintf(name, sizeof(name), "shpchp-%d", slot->number); | ||
132 | slot->wq = alloc_workqueue(name, 0, 0); | ||
133 | if (!slot->wq) { | ||
134 | retval = -ENOMEM; | ||
135 | goto error_info; | ||
136 | } | ||
137 | |||
131 | mutex_init(&slot->lock); | 138 | mutex_init(&slot->lock); |
132 | INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work); | 139 | INIT_DELAYED_WORK(&slot->work, shpchp_queue_pushbutton_work); |
133 | 140 | ||
134 | /* register this slot with the hotplug pci core */ | 141 | /* register this slot with the hotplug pci core */ |
135 | hotplug_slot->private = slot; | 142 | hotplug_slot->private = slot; |
136 | hotplug_slot->release = &release_slot; | 143 | hotplug_slot->release = &release_slot; |
137 | snprintf(name, SLOT_NAME_SIZE, "%d", slot->number); | 144 | snprintf(name, SLOT_NAME_SIZE, "%d", slot->number); |
138 | hotplug_slot->ops = &shpchp_hotplug_slot_ops; | 145 | hotplug_slot->ops = &shpchp_hotplug_slot_ops; |
139 | 146 | ||
140 | ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x " | 147 | ctrl_dbg(ctrl, "Registering domain:bus:dev=%04x:%02x:%02x " |
141 | "hp_slot=%x sun=%x slot_device_offset=%x\n", | 148 | "hp_slot=%x sun=%x slot_device_offset=%x\n", |
142 | pci_domain_nr(ctrl->pci_dev->subordinate), | 149 | pci_domain_nr(ctrl->pci_dev->subordinate), |
143 | slot->bus, slot->device, slot->hp_slot, slot->number, | 150 | slot->bus, slot->device, slot->hp_slot, slot->number, |
144 | ctrl->slot_device_offset); | 151 | ctrl->slot_device_offset); |
145 | retval = pci_hp_register(slot->hotplug_slot, | 152 | retval = pci_hp_register(slot->hotplug_slot, |
146 | ctrl->pci_dev->subordinate, slot->device, name); | 153 | ctrl->pci_dev->subordinate, slot->device, name); |
147 | if (retval) { | 154 | if (retval) { |
148 | ctrl_err(ctrl, "pci_hp_register failed with error %d\n", | 155 | ctrl_err(ctrl, "pci_hp_register failed with error %d\n", |
149 | retval); | 156 | retval); |
150 | goto error_info; | 157 | goto error_slotwq; |
151 | } | 158 | } |
152 | 159 | ||
153 | get_power_status(hotplug_slot, &info->power_status); | 160 | get_power_status(hotplug_slot, &info->power_status); |
154 | get_attention_status(hotplug_slot, &info->attention_status); | 161 | get_attention_status(hotplug_slot, &info->attention_status); |
155 | get_latch_status(hotplug_slot, &info->latch_status); | 162 | get_latch_status(hotplug_slot, &info->latch_status); |
156 | get_adapter_status(hotplug_slot, &info->adapter_status); | 163 | get_adapter_status(hotplug_slot, &info->adapter_status); |
157 | 164 | ||
158 | list_add(&slot->slot_list, &ctrl->slot_list); | 165 | list_add(&slot->slot_list, &ctrl->slot_list); |
159 | } | 166 | } |
160 | 167 | ||
161 | return 0; | 168 | return 0; |
169 | error_slotwq: | ||
170 | destroy_workqueue(slot->wq); | ||
162 | error_info: | 171 | error_info: |
163 | kfree(info); | 172 | kfree(info); |
164 | error_hpslot: | 173 | error_hpslot: |
165 | kfree(hotplug_slot); | 174 | kfree(hotplug_slot); |
166 | error_slot: | 175 | error_slot: |
167 | kfree(slot); | 176 | kfree(slot); |
168 | error: | 177 | error: |
169 | return retval; | 178 | return retval; |
170 | } | 179 | } |
171 | 180 | ||
172 | void cleanup_slots(struct controller *ctrl) | 181 | void cleanup_slots(struct controller *ctrl) |
173 | { | 182 | { |
174 | struct list_head *tmp; | 183 | struct list_head *tmp; |
175 | struct list_head *next; | 184 | struct list_head *next; |
176 | struct slot *slot; | 185 | struct slot *slot; |
177 | 186 | ||
178 | list_for_each_safe(tmp, next, &ctrl->slot_list) { | 187 | list_for_each_safe(tmp, next, &ctrl->slot_list) { |
179 | slot = list_entry(tmp, struct slot, slot_list); | 188 | slot = list_entry(tmp, struct slot, slot_list); |
180 | list_del(&slot->slot_list); | 189 | list_del(&slot->slot_list); |
181 | cancel_delayed_work(&slot->work); | 190 | cancel_delayed_work(&slot->work); |
182 | flush_workqueue(shpchp_wq); | 191 | destroy_workqueue(slot->wq); |
183 | pci_hp_deregister(slot->hotplug_slot); | 192 | pci_hp_deregister(slot->hotplug_slot); |
184 | } | 193 | } |
185 | } | 194 | } |
186 | 195 | ||
187 | /* | 196 | /* |
188 | * set_attention_status - Turns the Amber LED for a slot on, off or blink | 197 | * set_attention_status - Turns the Amber LED for a slot on, off or blink |
189 | */ | 198 | */ |
190 | static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status) | 199 | static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status) |
191 | { | 200 | { |
192 | struct slot *slot = get_slot(hotplug_slot); | 201 | struct slot *slot = get_slot(hotplug_slot); |
193 | 202 | ||
194 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", | 203 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", |
195 | __func__, slot_name(slot)); | 204 | __func__, slot_name(slot)); |
196 | 205 | ||
197 | hotplug_slot->info->attention_status = status; | 206 | hotplug_slot->info->attention_status = status; |
198 | slot->hpc_ops->set_attention_status(slot, status); | 207 | slot->hpc_ops->set_attention_status(slot, status); |
199 | 208 | ||
200 | return 0; | 209 | return 0; |
201 | } | 210 | } |
202 | 211 | ||
203 | static int enable_slot (struct hotplug_slot *hotplug_slot) | 212 | static int enable_slot (struct hotplug_slot *hotplug_slot) |
204 | { | 213 | { |
205 | struct slot *slot = get_slot(hotplug_slot); | 214 | struct slot *slot = get_slot(hotplug_slot); |
206 | 215 | ||
207 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", | 216 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", |
208 | __func__, slot_name(slot)); | 217 | __func__, slot_name(slot)); |
209 | 218 | ||
210 | return shpchp_sysfs_enable_slot(slot); | 219 | return shpchp_sysfs_enable_slot(slot); |
211 | } | 220 | } |
212 | 221 | ||
213 | static int disable_slot (struct hotplug_slot *hotplug_slot) | 222 | static int disable_slot (struct hotplug_slot *hotplug_slot) |
214 | { | 223 | { |
215 | struct slot *slot = get_slot(hotplug_slot); | 224 | struct slot *slot = get_slot(hotplug_slot); |
216 | 225 | ||
217 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", | 226 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", |
218 | __func__, slot_name(slot)); | 227 | __func__, slot_name(slot)); |
219 | 228 | ||
220 | return shpchp_sysfs_disable_slot(slot); | 229 | return shpchp_sysfs_disable_slot(slot); |
221 | } | 230 | } |
222 | 231 | ||
223 | static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value) | 232 | static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value) |
224 | { | 233 | { |
225 | struct slot *slot = get_slot(hotplug_slot); | 234 | struct slot *slot = get_slot(hotplug_slot); |
226 | int retval; | 235 | int retval; |
227 | 236 | ||
228 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", | 237 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", |
229 | __func__, slot_name(slot)); | 238 | __func__, slot_name(slot)); |
230 | 239 | ||
231 | retval = slot->hpc_ops->get_power_status(slot, value); | 240 | retval = slot->hpc_ops->get_power_status(slot, value); |
232 | if (retval < 0) | 241 | if (retval < 0) |
233 | *value = hotplug_slot->info->power_status; | 242 | *value = hotplug_slot->info->power_status; |
234 | 243 | ||
235 | return 0; | 244 | return 0; |
236 | } | 245 | } |
237 | 246 | ||
238 | static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value) | 247 | static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value) |
239 | { | 248 | { |
240 | struct slot *slot = get_slot(hotplug_slot); | 249 | struct slot *slot = get_slot(hotplug_slot); |
241 | int retval; | 250 | int retval; |
242 | 251 | ||
243 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", | 252 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", |
244 | __func__, slot_name(slot)); | 253 | __func__, slot_name(slot)); |
245 | 254 | ||
246 | retval = slot->hpc_ops->get_attention_status(slot, value); | 255 | retval = slot->hpc_ops->get_attention_status(slot, value); |
247 | if (retval < 0) | 256 | if (retval < 0) |
248 | *value = hotplug_slot->info->attention_status; | 257 | *value = hotplug_slot->info->attention_status; |
249 | 258 | ||
250 | return 0; | 259 | return 0; |
251 | } | 260 | } |
252 | 261 | ||
253 | static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value) | 262 | static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value) |
254 | { | 263 | { |
255 | struct slot *slot = get_slot(hotplug_slot); | 264 | struct slot *slot = get_slot(hotplug_slot); |
256 | int retval; | 265 | int retval; |
257 | 266 | ||
258 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", | 267 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", |
259 | __func__, slot_name(slot)); | 268 | __func__, slot_name(slot)); |
260 | 269 | ||
261 | retval = slot->hpc_ops->get_latch_status(slot, value); | 270 | retval = slot->hpc_ops->get_latch_status(slot, value); |
262 | if (retval < 0) | 271 | if (retval < 0) |
263 | *value = hotplug_slot->info->latch_status; | 272 | *value = hotplug_slot->info->latch_status; |
264 | 273 | ||
265 | return 0; | 274 | return 0; |
266 | } | 275 | } |
267 | 276 | ||
268 | static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value) | 277 | static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value) |
269 | { | 278 | { |
270 | struct slot *slot = get_slot(hotplug_slot); | 279 | struct slot *slot = get_slot(hotplug_slot); |
271 | int retval; | 280 | int retval; |
272 | 281 | ||
273 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", | 282 | ctrl_dbg(slot->ctrl, "%s: physical_slot = %s\n", |
274 | __func__, slot_name(slot)); | 283 | __func__, slot_name(slot)); |
275 | 284 | ||
276 | retval = slot->hpc_ops->get_adapter_status(slot, value); | 285 | retval = slot->hpc_ops->get_adapter_status(slot, value); |
277 | if (retval < 0) | 286 | if (retval < 0) |
278 | *value = hotplug_slot->info->adapter_status; | 287 | *value = hotplug_slot->info->adapter_status; |
279 | 288 | ||
280 | return 0; | 289 | return 0; |
281 | } | 290 | } |
282 | 291 | ||
283 | static int is_shpc_capable(struct pci_dev *dev) | 292 | static int is_shpc_capable(struct pci_dev *dev) |
284 | { | 293 | { |
285 | if (dev->vendor == PCI_VENDOR_ID_AMD && | 294 | if (dev->vendor == PCI_VENDOR_ID_AMD && |
286 | dev->device == PCI_DEVICE_ID_AMD_GOLAM_7450) | 295 | dev->device == PCI_DEVICE_ID_AMD_GOLAM_7450) |
287 | return 1; | 296 | return 1; |
288 | if (!pci_find_capability(dev, PCI_CAP_ID_SHPC)) | 297 | if (!pci_find_capability(dev, PCI_CAP_ID_SHPC)) |
289 | return 0; | 298 | return 0; |
290 | if (get_hp_hw_control_from_firmware(dev)) | 299 | if (get_hp_hw_control_from_firmware(dev)) |
291 | return 0; | 300 | return 0; |
292 | return 1; | 301 | return 1; |
293 | } | 302 | } |
294 | 303 | ||
295 | static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) | 304 | static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) |
296 | { | 305 | { |
297 | int rc; | 306 | int rc; |
298 | struct controller *ctrl; | 307 | struct controller *ctrl; |
299 | 308 | ||
300 | if (!is_shpc_capable(pdev)) | 309 | if (!is_shpc_capable(pdev)) |
301 | return -ENODEV; | 310 | return -ENODEV; |
302 | 311 | ||
303 | ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); | 312 | ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); |
304 | if (!ctrl) { | 313 | if (!ctrl) { |
305 | dev_err(&pdev->dev, "%s: Out of memory\n", __func__); | 314 | dev_err(&pdev->dev, "%s: Out of memory\n", __func__); |
306 | goto err_out_none; | 315 | goto err_out_none; |
307 | } | 316 | } |
308 | INIT_LIST_HEAD(&ctrl->slot_list); | 317 | INIT_LIST_HEAD(&ctrl->slot_list); |
309 | 318 | ||
310 | rc = shpc_init(ctrl, pdev); | 319 | rc = shpc_init(ctrl, pdev); |
311 | if (rc) { | 320 | if (rc) { |
312 | ctrl_dbg(ctrl, "Controller initialization failed\n"); | 321 | ctrl_dbg(ctrl, "Controller initialization failed\n"); |
313 | goto err_out_free_ctrl; | 322 | goto err_out_free_ctrl; |
314 | } | 323 | } |
315 | 324 | ||
316 | pci_set_drvdata(pdev, ctrl); | 325 | pci_set_drvdata(pdev, ctrl); |
317 | 326 | ||
318 | /* Setup the slot information structures */ | 327 | /* Setup the slot information structures */ |
319 | rc = init_slots(ctrl); | 328 | rc = init_slots(ctrl); |
320 | if (rc) { | 329 | if (rc) { |
321 | ctrl_err(ctrl, "Slot initialization failed\n"); | 330 | ctrl_err(ctrl, "Slot initialization failed\n"); |
322 | goto err_out_release_ctlr; | 331 | goto err_out_release_ctlr; |
323 | } | 332 | } |
324 | 333 | ||
325 | rc = shpchp_create_ctrl_files(ctrl); | 334 | rc = shpchp_create_ctrl_files(ctrl); |
326 | if (rc) | 335 | if (rc) |
327 | goto err_cleanup_slots; | 336 | goto err_cleanup_slots; |
328 | 337 | ||
329 | return 0; | 338 | return 0; |
330 | 339 | ||
331 | err_cleanup_slots: | 340 | err_cleanup_slots: |
332 | cleanup_slots(ctrl); | 341 | cleanup_slots(ctrl); |
333 | err_out_release_ctlr: | 342 | err_out_release_ctlr: |
334 | ctrl->hpc_ops->release_ctlr(ctrl); | 343 | ctrl->hpc_ops->release_ctlr(ctrl); |
335 | err_out_free_ctrl: | 344 | err_out_free_ctrl: |
336 | kfree(ctrl); | 345 | kfree(ctrl); |
337 | err_out_none: | 346 | err_out_none: |
338 | return -ENODEV; | 347 | return -ENODEV; |
339 | } | 348 | } |
340 | 349 | ||
341 | static void shpc_remove(struct pci_dev *dev) | 350 | static void shpc_remove(struct pci_dev *dev) |
342 | { | 351 | { |
343 | struct controller *ctrl = pci_get_drvdata(dev); | 352 | struct controller *ctrl = pci_get_drvdata(dev); |
344 | 353 | ||
345 | shpchp_remove_ctrl_files(ctrl); | 354 | shpchp_remove_ctrl_files(ctrl); |
346 | ctrl->hpc_ops->release_ctlr(ctrl); | 355 | ctrl->hpc_ops->release_ctlr(ctrl); |
347 | kfree(ctrl); | 356 | kfree(ctrl); |
348 | } | 357 | } |
349 | 358 | ||
350 | static struct pci_device_id shpcd_pci_tbl[] = { | 359 | static struct pci_device_id shpcd_pci_tbl[] = { |
351 | {PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0)}, | 360 | {PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), ~0)}, |
352 | { /* end: all zeroes */ } | 361 | { /* end: all zeroes */ } |
353 | }; | 362 | }; |
354 | MODULE_DEVICE_TABLE(pci, shpcd_pci_tbl); | 363 | MODULE_DEVICE_TABLE(pci, shpcd_pci_tbl); |
355 | 364 | ||
356 | static struct pci_driver shpc_driver = { | 365 | static struct pci_driver shpc_driver = { |
357 | .name = SHPC_MODULE_NAME, | 366 | .name = SHPC_MODULE_NAME, |
358 | .id_table = shpcd_pci_tbl, | 367 | .id_table = shpcd_pci_tbl, |
359 | .probe = shpc_probe, | 368 | .probe = shpc_probe, |
360 | .remove = shpc_remove, | 369 | .remove = shpc_remove, |
361 | }; | 370 | }; |
362 | 371 | ||
363 | static int __init shpcd_init(void) | 372 | static int __init shpcd_init(void) |
364 | { | 373 | { |
365 | int retval = 0; | 374 | int retval; |
366 | 375 | ||
367 | shpchp_wq = alloc_workqueue("shpchp", 0, 0); | ||
368 | if (!shpchp_wq) | ||
369 | return -ENOMEM; | ||
370 | |||
371 | retval = pci_register_driver(&shpc_driver); | 376 | retval = pci_register_driver(&shpc_driver); |
372 | dbg("%s: pci_register_driver = %d\n", __func__, retval); | 377 | dbg("%s: pci_register_driver = %d\n", __func__, retval); |
373 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); | 378 | info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); |
374 | if (retval) { | 379 | |
375 | destroy_workqueue(shpchp_wq); | ||
376 | } | ||
377 | return retval; | 380 | return retval; |
378 | } | 381 | } |
379 | 382 | ||
380 | static void __exit shpcd_cleanup(void) | 383 | static void __exit shpcd_cleanup(void) |
381 | { | 384 | { |
382 | dbg("unload_shpchpd()\n"); | 385 | dbg("unload_shpchpd()\n"); |
drivers/pci/hotplug/shpchp_ctrl.c
1 | /* | 1 | /* |
2 | * Standard Hot Plug Controller Driver | 2 | * Standard Hot Plug Controller Driver |
3 | * | 3 | * |
4 | * Copyright (C) 1995,2001 Compaq Computer Corporation | 4 | * Copyright (C) 1995,2001 Compaq Computer Corporation |
5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) | 5 | * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) |
6 | * Copyright (C) 2001 IBM Corp. | 6 | * Copyright (C) 2001 IBM Corp. |
7 | * Copyright (C) 2003-2004 Intel Corporation | 7 | * Copyright (C) 2003-2004 Intel Corporation |
8 | * | 8 | * |
9 | * All rights reserved. | 9 | * All rights reserved. |
10 | * | 10 | * |
11 | * This program is free software; you can redistribute it and/or modify | 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 | 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 | 13 | * the Free Software Foundation; either version 2 of the License, or (at |
14 | * your option) any later version. | 14 | * your option) any later version. |
15 | * | 15 | * |
16 | * This program is distributed in the hope that it will be useful, but | 16 | * This program is distributed in the hope that it will be useful, but |
17 | * WITHOUT ANY WARRANTY; without even the implied warranty of | 17 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | 18 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or |
19 | * NON INFRINGEMENT. See the GNU General Public License for more | 19 | * NON INFRINGEMENT. See the GNU General Public License for more |
20 | * details. | 20 | * details. |
21 | * | 21 | * |
22 | * You should have received a copy of the GNU General Public License | 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 | 23 | * along with this program; if not, write to the Free Software |
24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 24 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
25 | * | 25 | * |
26 | * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> | 26 | * Send feedback to <greg@kroah.com>, <kristen.c.accardi@intel.com> |
27 | * | 27 | * |
28 | */ | 28 | */ |
29 | 29 | ||
30 | #include <linux/module.h> | 30 | #include <linux/module.h> |
31 | #include <linux/kernel.h> | 31 | #include <linux/kernel.h> |
32 | #include <linux/types.h> | 32 | #include <linux/types.h> |
33 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
34 | #include <linux/pci.h> | 34 | #include <linux/pci.h> |
35 | #include "../pci.h" | 35 | #include "../pci.h" |
36 | #include "shpchp.h" | 36 | #include "shpchp.h" |
37 | 37 | ||
38 | static void interrupt_event_handler(struct work_struct *work); | 38 | static void interrupt_event_handler(struct work_struct *work); |
39 | static int shpchp_enable_slot(struct slot *p_slot); | 39 | static int shpchp_enable_slot(struct slot *p_slot); |
40 | static int shpchp_disable_slot(struct slot *p_slot); | 40 | static int shpchp_disable_slot(struct slot *p_slot); |
41 | 41 | ||
42 | static int queue_interrupt_event(struct slot *p_slot, u32 event_type) | 42 | static int queue_interrupt_event(struct slot *p_slot, u32 event_type) |
43 | { | 43 | { |
44 | struct event_info *info; | 44 | struct event_info *info; |
45 | 45 | ||
46 | info = kmalloc(sizeof(*info), GFP_ATOMIC); | 46 | info = kmalloc(sizeof(*info), GFP_ATOMIC); |
47 | if (!info) | 47 | if (!info) |
48 | return -ENOMEM; | 48 | return -ENOMEM; |
49 | 49 | ||
50 | info->event_type = event_type; | 50 | info->event_type = event_type; |
51 | info->p_slot = p_slot; | 51 | info->p_slot = p_slot; |
52 | INIT_WORK(&info->work, interrupt_event_handler); | 52 | INIT_WORK(&info->work, interrupt_event_handler); |
53 | 53 | ||
54 | queue_work(shpchp_wq, &info->work); | 54 | queue_work(p_slot->wq, &info->work); |
55 | 55 | ||
56 | return 0; | 56 | return 0; |
57 | } | 57 | } |
58 | 58 | ||
59 | u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl) | 59 | u8 shpchp_handle_attention_button(u8 hp_slot, struct controller *ctrl) |
60 | { | 60 | { |
61 | struct slot *p_slot; | 61 | struct slot *p_slot; |
62 | u32 event_type; | 62 | u32 event_type; |
63 | 63 | ||
64 | /* Attention Button Change */ | 64 | /* Attention Button Change */ |
65 | ctrl_dbg(ctrl, "Attention button interrupt received\n"); | 65 | ctrl_dbg(ctrl, "Attention button interrupt received\n"); |
66 | 66 | ||
67 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | 67 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); |
68 | p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); | 68 | p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); |
69 | 69 | ||
70 | /* | 70 | /* |
71 | * Button pressed - See if need to TAKE ACTION!!! | 71 | * Button pressed - See if need to TAKE ACTION!!! |
72 | */ | 72 | */ |
73 | ctrl_info(ctrl, "Button pressed on Slot(%s)\n", slot_name(p_slot)); | 73 | ctrl_info(ctrl, "Button pressed on Slot(%s)\n", slot_name(p_slot)); |
74 | event_type = INT_BUTTON_PRESS; | 74 | event_type = INT_BUTTON_PRESS; |
75 | 75 | ||
76 | queue_interrupt_event(p_slot, event_type); | 76 | queue_interrupt_event(p_slot, event_type); |
77 | 77 | ||
78 | return 0; | 78 | return 0; |
79 | 79 | ||
80 | } | 80 | } |
81 | 81 | ||
82 | u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl) | 82 | u8 shpchp_handle_switch_change(u8 hp_slot, struct controller *ctrl) |
83 | { | 83 | { |
84 | struct slot *p_slot; | 84 | struct slot *p_slot; |
85 | u8 getstatus; | 85 | u8 getstatus; |
86 | u32 event_type; | 86 | u32 event_type; |
87 | 87 | ||
88 | /* Switch Change */ | 88 | /* Switch Change */ |
89 | ctrl_dbg(ctrl, "Switch interrupt received\n"); | 89 | ctrl_dbg(ctrl, "Switch interrupt received\n"); |
90 | 90 | ||
91 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | 91 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); |
92 | p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); | 92 | p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); |
93 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | 93 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
94 | ctrl_dbg(ctrl, "Card present %x Power status %x\n", | 94 | ctrl_dbg(ctrl, "Card present %x Power status %x\n", |
95 | p_slot->presence_save, p_slot->pwr_save); | 95 | p_slot->presence_save, p_slot->pwr_save); |
96 | 96 | ||
97 | if (getstatus) { | 97 | if (getstatus) { |
98 | /* | 98 | /* |
99 | * Switch opened | 99 | * Switch opened |
100 | */ | 100 | */ |
101 | ctrl_info(ctrl, "Latch open on Slot(%s)\n", slot_name(p_slot)); | 101 | ctrl_info(ctrl, "Latch open on Slot(%s)\n", slot_name(p_slot)); |
102 | event_type = INT_SWITCH_OPEN; | 102 | event_type = INT_SWITCH_OPEN; |
103 | if (p_slot->pwr_save && p_slot->presence_save) { | 103 | if (p_slot->pwr_save && p_slot->presence_save) { |
104 | event_type = INT_POWER_FAULT; | 104 | event_type = INT_POWER_FAULT; |
105 | ctrl_err(ctrl, "Surprise Removal of card\n"); | 105 | ctrl_err(ctrl, "Surprise Removal of card\n"); |
106 | } | 106 | } |
107 | } else { | 107 | } else { |
108 | /* | 108 | /* |
109 | * Switch closed | 109 | * Switch closed |
110 | */ | 110 | */ |
111 | ctrl_info(ctrl, "Latch close on Slot(%s)\n", slot_name(p_slot)); | 111 | ctrl_info(ctrl, "Latch close on Slot(%s)\n", slot_name(p_slot)); |
112 | event_type = INT_SWITCH_CLOSE; | 112 | event_type = INT_SWITCH_CLOSE; |
113 | } | 113 | } |
114 | 114 | ||
115 | queue_interrupt_event(p_slot, event_type); | 115 | queue_interrupt_event(p_slot, event_type); |
116 | 116 | ||
117 | return 1; | 117 | return 1; |
118 | } | 118 | } |
119 | 119 | ||
120 | u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl) | 120 | u8 shpchp_handle_presence_change(u8 hp_slot, struct controller *ctrl) |
121 | { | 121 | { |
122 | struct slot *p_slot; | 122 | struct slot *p_slot; |
123 | u32 event_type; | 123 | u32 event_type; |
124 | 124 | ||
125 | /* Presence Change */ | 125 | /* Presence Change */ |
126 | ctrl_dbg(ctrl, "Presence/Notify input change\n"); | 126 | ctrl_dbg(ctrl, "Presence/Notify input change\n"); |
127 | 127 | ||
128 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | 128 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); |
129 | 129 | ||
130 | /* | 130 | /* |
131 | * Save the presence state | 131 | * Save the presence state |
132 | */ | 132 | */ |
133 | p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); | 133 | p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); |
134 | if (p_slot->presence_save) { | 134 | if (p_slot->presence_save) { |
135 | /* | 135 | /* |
136 | * Card Present | 136 | * Card Present |
137 | */ | 137 | */ |
138 | ctrl_info(ctrl, "Card present on Slot(%s)\n", | 138 | ctrl_info(ctrl, "Card present on Slot(%s)\n", |
139 | slot_name(p_slot)); | 139 | slot_name(p_slot)); |
140 | event_type = INT_PRESENCE_ON; | 140 | event_type = INT_PRESENCE_ON; |
141 | } else { | 141 | } else { |
142 | /* | 142 | /* |
143 | * Not Present | 143 | * Not Present |
144 | */ | 144 | */ |
145 | ctrl_info(ctrl, "Card not present on Slot(%s)\n", | 145 | ctrl_info(ctrl, "Card not present on Slot(%s)\n", |
146 | slot_name(p_slot)); | 146 | slot_name(p_slot)); |
147 | event_type = INT_PRESENCE_OFF; | 147 | event_type = INT_PRESENCE_OFF; |
148 | } | 148 | } |
149 | 149 | ||
150 | queue_interrupt_event(p_slot, event_type); | 150 | queue_interrupt_event(p_slot, event_type); |
151 | 151 | ||
152 | return 1; | 152 | return 1; |
153 | } | 153 | } |
154 | 154 | ||
155 | u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl) | 155 | u8 shpchp_handle_power_fault(u8 hp_slot, struct controller *ctrl) |
156 | { | 156 | { |
157 | struct slot *p_slot; | 157 | struct slot *p_slot; |
158 | u32 event_type; | 158 | u32 event_type; |
159 | 159 | ||
160 | /* Power fault */ | 160 | /* Power fault */ |
161 | ctrl_dbg(ctrl, "Power fault interrupt received\n"); | 161 | ctrl_dbg(ctrl, "Power fault interrupt received\n"); |
162 | 162 | ||
163 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | 163 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); |
164 | 164 | ||
165 | if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { | 165 | if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) { |
166 | /* | 166 | /* |
167 | * Power fault Cleared | 167 | * Power fault Cleared |
168 | */ | 168 | */ |
169 | ctrl_info(ctrl, "Power fault cleared on Slot(%s)\n", | 169 | ctrl_info(ctrl, "Power fault cleared on Slot(%s)\n", |
170 | slot_name(p_slot)); | 170 | slot_name(p_slot)); |
171 | p_slot->status = 0x00; | 171 | p_slot->status = 0x00; |
172 | event_type = INT_POWER_FAULT_CLEAR; | 172 | event_type = INT_POWER_FAULT_CLEAR; |
173 | } else { | 173 | } else { |
174 | /* | 174 | /* |
175 | * Power fault | 175 | * Power fault |
176 | */ | 176 | */ |
177 | ctrl_info(ctrl, "Power fault on Slot(%s)\n", slot_name(p_slot)); | 177 | ctrl_info(ctrl, "Power fault on Slot(%s)\n", slot_name(p_slot)); |
178 | event_type = INT_POWER_FAULT; | 178 | event_type = INT_POWER_FAULT; |
179 | /* set power fault status for this board */ | 179 | /* set power fault status for this board */ |
180 | p_slot->status = 0xFF; | 180 | p_slot->status = 0xFF; |
181 | ctrl_info(ctrl, "Power fault bit %x set\n", hp_slot); | 181 | ctrl_info(ctrl, "Power fault bit %x set\n", hp_slot); |
182 | } | 182 | } |
183 | 183 | ||
184 | queue_interrupt_event(p_slot, event_type); | 184 | queue_interrupt_event(p_slot, event_type); |
185 | 185 | ||
186 | return 1; | 186 | return 1; |
187 | } | 187 | } |
188 | 188 | ||
189 | /* The following routines constitute the bulk of the | 189 | /* The following routines constitute the bulk of the |
190 | hotplug controller logic | 190 | hotplug controller logic |
191 | */ | 191 | */ |
192 | static int change_bus_speed(struct controller *ctrl, struct slot *p_slot, | 192 | static int change_bus_speed(struct controller *ctrl, struct slot *p_slot, |
193 | enum pci_bus_speed speed) | 193 | enum pci_bus_speed speed) |
194 | { | 194 | { |
195 | int rc = 0; | 195 | int rc = 0; |
196 | 196 | ||
197 | ctrl_dbg(ctrl, "Change speed to %d\n", speed); | 197 | ctrl_dbg(ctrl, "Change speed to %d\n", speed); |
198 | if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) { | 198 | if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, speed))) { |
199 | ctrl_err(ctrl, "%s: Issue of set bus speed mode command " | 199 | ctrl_err(ctrl, "%s: Issue of set bus speed mode command " |
200 | "failed\n", __func__); | 200 | "failed\n", __func__); |
201 | return WRONG_BUS_FREQUENCY; | 201 | return WRONG_BUS_FREQUENCY; |
202 | } | 202 | } |
203 | return rc; | 203 | return rc; |
204 | } | 204 | } |
205 | 205 | ||
206 | static int fix_bus_speed(struct controller *ctrl, struct slot *pslot, | 206 | static int fix_bus_speed(struct controller *ctrl, struct slot *pslot, |
207 | u8 flag, enum pci_bus_speed asp, enum pci_bus_speed bsp, | 207 | u8 flag, enum pci_bus_speed asp, enum pci_bus_speed bsp, |
208 | enum pci_bus_speed msp) | 208 | enum pci_bus_speed msp) |
209 | { | 209 | { |
210 | int rc = 0; | 210 | int rc = 0; |
211 | 211 | ||
212 | /* | 212 | /* |
213 | * If other slots on the same bus are occupied, we cannot | 213 | * If other slots on the same bus are occupied, we cannot |
214 | * change the bus speed. | 214 | * change the bus speed. |
215 | */ | 215 | */ |
216 | if (flag) { | 216 | if (flag) { |
217 | if (asp < bsp) { | 217 | if (asp < bsp) { |
218 | ctrl_err(ctrl, "Speed of bus %x and adapter %x " | 218 | ctrl_err(ctrl, "Speed of bus %x and adapter %x " |
219 | "mismatch\n", bsp, asp); | 219 | "mismatch\n", bsp, asp); |
220 | rc = WRONG_BUS_FREQUENCY; | 220 | rc = WRONG_BUS_FREQUENCY; |
221 | } | 221 | } |
222 | return rc; | 222 | return rc; |
223 | } | 223 | } |
224 | 224 | ||
225 | if (asp < msp) { | 225 | if (asp < msp) { |
226 | if (bsp != asp) | 226 | if (bsp != asp) |
227 | rc = change_bus_speed(ctrl, pslot, asp); | 227 | rc = change_bus_speed(ctrl, pslot, asp); |
228 | } else { | 228 | } else { |
229 | if (bsp != msp) | 229 | if (bsp != msp) |
230 | rc = change_bus_speed(ctrl, pslot, msp); | 230 | rc = change_bus_speed(ctrl, pslot, msp); |
231 | } | 231 | } |
232 | return rc; | 232 | return rc; |
233 | } | 233 | } |
234 | 234 | ||
235 | /** | 235 | /** |
236 | * board_added - Called after a board has been added to the system. | 236 | * board_added - Called after a board has been added to the system. |
237 | * @p_slot: target &slot | 237 | * @p_slot: target &slot |
238 | * | 238 | * |
239 | * Turns power on for the board. | 239 | * Turns power on for the board. |
240 | * Configures board. | 240 | * Configures board. |
241 | */ | 241 | */ |
242 | static int board_added(struct slot *p_slot) | 242 | static int board_added(struct slot *p_slot) |
243 | { | 243 | { |
244 | u8 hp_slot; | 244 | u8 hp_slot; |
245 | u8 slots_not_empty = 0; | 245 | u8 slots_not_empty = 0; |
246 | int rc = 0; | 246 | int rc = 0; |
247 | enum pci_bus_speed asp, bsp, msp; | 247 | enum pci_bus_speed asp, bsp, msp; |
248 | struct controller *ctrl = p_slot->ctrl; | 248 | struct controller *ctrl = p_slot->ctrl; |
249 | struct pci_bus *parent = ctrl->pci_dev->subordinate; | 249 | struct pci_bus *parent = ctrl->pci_dev->subordinate; |
250 | 250 | ||
251 | hp_slot = p_slot->device - ctrl->slot_device_offset; | 251 | hp_slot = p_slot->device - ctrl->slot_device_offset; |
252 | 252 | ||
253 | ctrl_dbg(ctrl, | 253 | ctrl_dbg(ctrl, |
254 | "%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n", | 254 | "%s: p_slot->device, slot_offset, hp_slot = %d, %d ,%d\n", |
255 | __func__, p_slot->device, ctrl->slot_device_offset, hp_slot); | 255 | __func__, p_slot->device, ctrl->slot_device_offset, hp_slot); |
256 | 256 | ||
257 | /* Power on slot without connecting to bus */ | 257 | /* Power on slot without connecting to bus */ |
258 | rc = p_slot->hpc_ops->power_on_slot(p_slot); | 258 | rc = p_slot->hpc_ops->power_on_slot(p_slot); |
259 | if (rc) { | 259 | if (rc) { |
260 | ctrl_err(ctrl, "Failed to power on slot\n"); | 260 | ctrl_err(ctrl, "Failed to power on slot\n"); |
261 | return -1; | 261 | return -1; |
262 | } | 262 | } |
263 | 263 | ||
264 | if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) { | 264 | if ((ctrl->pci_dev->vendor == 0x8086) && (ctrl->pci_dev->device == 0x0332)) { |
265 | if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) { | 265 | if ((rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, PCI_SPEED_33MHz))) { |
266 | ctrl_err(ctrl, "%s: Issue of set bus speed mode command" | 266 | ctrl_err(ctrl, "%s: Issue of set bus speed mode command" |
267 | " failed\n", __func__); | 267 | " failed\n", __func__); |
268 | return WRONG_BUS_FREQUENCY; | 268 | return WRONG_BUS_FREQUENCY; |
269 | } | 269 | } |
270 | 270 | ||
271 | /* turn on board, blink green LED, turn off Amber LED */ | 271 | /* turn on board, blink green LED, turn off Amber LED */ |
272 | if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) { | 272 | if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) { |
273 | ctrl_err(ctrl, "Issue of Slot Enable command failed\n"); | 273 | ctrl_err(ctrl, "Issue of Slot Enable command failed\n"); |
274 | return rc; | 274 | return rc; |
275 | } | 275 | } |
276 | } | 276 | } |
277 | 277 | ||
278 | rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &asp); | 278 | rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &asp); |
279 | if (rc) { | 279 | if (rc) { |
280 | ctrl_err(ctrl, "Can't get adapter speed or " | 280 | ctrl_err(ctrl, "Can't get adapter speed or " |
281 | "bus mode mismatch\n"); | 281 | "bus mode mismatch\n"); |
282 | return WRONG_BUS_FREQUENCY; | 282 | return WRONG_BUS_FREQUENCY; |
283 | } | 283 | } |
284 | 284 | ||
285 | bsp = ctrl->pci_dev->bus->cur_bus_speed; | 285 | bsp = ctrl->pci_dev->bus->cur_bus_speed; |
286 | msp = ctrl->pci_dev->bus->max_bus_speed; | 286 | msp = ctrl->pci_dev->bus->max_bus_speed; |
287 | 287 | ||
288 | /* Check if there are other slots or devices on the same bus */ | 288 | /* Check if there are other slots or devices on the same bus */ |
289 | if (!list_empty(&ctrl->pci_dev->subordinate->devices)) | 289 | if (!list_empty(&ctrl->pci_dev->subordinate->devices)) |
290 | slots_not_empty = 1; | 290 | slots_not_empty = 1; |
291 | 291 | ||
292 | ctrl_dbg(ctrl, "%s: slots_not_empty %d, adapter_speed %d, bus_speed %d," | 292 | ctrl_dbg(ctrl, "%s: slots_not_empty %d, adapter_speed %d, bus_speed %d," |
293 | " max_bus_speed %d\n", __func__, slots_not_empty, asp, | 293 | " max_bus_speed %d\n", __func__, slots_not_empty, asp, |
294 | bsp, msp); | 294 | bsp, msp); |
295 | 295 | ||
296 | rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, asp, bsp, msp); | 296 | rc = fix_bus_speed(ctrl, p_slot, slots_not_empty, asp, bsp, msp); |
297 | if (rc) | 297 | if (rc) |
298 | return rc; | 298 | return rc; |
299 | 299 | ||
300 | /* turn on board, blink green LED, turn off Amber LED */ | 300 | /* turn on board, blink green LED, turn off Amber LED */ |
301 | if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) { | 301 | if ((rc = p_slot->hpc_ops->slot_enable(p_slot))) { |
302 | ctrl_err(ctrl, "Issue of Slot Enable command failed\n"); | 302 | ctrl_err(ctrl, "Issue of Slot Enable command failed\n"); |
303 | return rc; | 303 | return rc; |
304 | } | 304 | } |
305 | 305 | ||
306 | /* Wait for ~1 second */ | 306 | /* Wait for ~1 second */ |
307 | msleep(1000); | 307 | msleep(1000); |
308 | 308 | ||
309 | ctrl_dbg(ctrl, "%s: slot status = %x\n", __func__, p_slot->status); | 309 | ctrl_dbg(ctrl, "%s: slot status = %x\n", __func__, p_slot->status); |
310 | /* Check for a power fault */ | 310 | /* Check for a power fault */ |
311 | if (p_slot->status == 0xFF) { | 311 | if (p_slot->status == 0xFF) { |
312 | /* power fault occurred, but it was benign */ | 312 | /* power fault occurred, but it was benign */ |
313 | ctrl_dbg(ctrl, "%s: Power fault\n", __func__); | 313 | ctrl_dbg(ctrl, "%s: Power fault\n", __func__); |
314 | rc = POWER_FAILURE; | 314 | rc = POWER_FAILURE; |
315 | p_slot->status = 0; | 315 | p_slot->status = 0; |
316 | goto err_exit; | 316 | goto err_exit; |
317 | } | 317 | } |
318 | 318 | ||
319 | if (shpchp_configure_device(p_slot)) { | 319 | if (shpchp_configure_device(p_slot)) { |
320 | ctrl_err(ctrl, "Cannot add device at %04x:%02x:%02x\n", | 320 | ctrl_err(ctrl, "Cannot add device at %04x:%02x:%02x\n", |
321 | pci_domain_nr(parent), p_slot->bus, p_slot->device); | 321 | pci_domain_nr(parent), p_slot->bus, p_slot->device); |
322 | goto err_exit; | 322 | goto err_exit; |
323 | } | 323 | } |
324 | 324 | ||
325 | p_slot->status = 0; | 325 | p_slot->status = 0; |
326 | p_slot->is_a_board = 0x01; | 326 | p_slot->is_a_board = 0x01; |
327 | p_slot->pwr_save = 1; | 327 | p_slot->pwr_save = 1; |
328 | 328 | ||
329 | p_slot->hpc_ops->green_led_on(p_slot); | 329 | p_slot->hpc_ops->green_led_on(p_slot); |
330 | 330 | ||
331 | return 0; | 331 | return 0; |
332 | 332 | ||
333 | err_exit: | 333 | err_exit: |
334 | /* turn off slot, turn on Amber LED, turn off Green LED */ | 334 | /* turn off slot, turn on Amber LED, turn off Green LED */ |
335 | rc = p_slot->hpc_ops->slot_disable(p_slot); | 335 | rc = p_slot->hpc_ops->slot_disable(p_slot); |
336 | if (rc) { | 336 | if (rc) { |
337 | ctrl_err(ctrl, "%s: Issue of Slot Disable command failed\n", | 337 | ctrl_err(ctrl, "%s: Issue of Slot Disable command failed\n", |
338 | __func__); | 338 | __func__); |
339 | return rc; | 339 | return rc; |
340 | } | 340 | } |
341 | 341 | ||
342 | return(rc); | 342 | return(rc); |
343 | } | 343 | } |
344 | 344 | ||
345 | 345 | ||
346 | /** | 346 | /** |
347 | * remove_board - Turns off slot and LEDs | 347 | * remove_board - Turns off slot and LEDs |
348 | * @p_slot: target &slot | 348 | * @p_slot: target &slot |
349 | */ | 349 | */ |
350 | static int remove_board(struct slot *p_slot) | 350 | static int remove_board(struct slot *p_slot) |
351 | { | 351 | { |
352 | struct controller *ctrl = p_slot->ctrl; | 352 | struct controller *ctrl = p_slot->ctrl; |
353 | u8 hp_slot; | 353 | u8 hp_slot; |
354 | int rc; | 354 | int rc; |
355 | 355 | ||
356 | if (shpchp_unconfigure_device(p_slot)) | 356 | if (shpchp_unconfigure_device(p_slot)) |
357 | return(1); | 357 | return(1); |
358 | 358 | ||
359 | hp_slot = p_slot->device - ctrl->slot_device_offset; | 359 | hp_slot = p_slot->device - ctrl->slot_device_offset; |
360 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); | 360 | p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); |
361 | 361 | ||
362 | ctrl_dbg(ctrl, "%s: hp_slot = %d\n", __func__, hp_slot); | 362 | ctrl_dbg(ctrl, "%s: hp_slot = %d\n", __func__, hp_slot); |
363 | 363 | ||
364 | /* Change status to shutdown */ | 364 | /* Change status to shutdown */ |
365 | if (p_slot->is_a_board) | 365 | if (p_slot->is_a_board) |
366 | p_slot->status = 0x01; | 366 | p_slot->status = 0x01; |
367 | 367 | ||
368 | /* turn off slot, turn on Amber LED, turn off Green LED */ | 368 | /* turn off slot, turn on Amber LED, turn off Green LED */ |
369 | rc = p_slot->hpc_ops->slot_disable(p_slot); | 369 | rc = p_slot->hpc_ops->slot_disable(p_slot); |
370 | if (rc) { | 370 | if (rc) { |
371 | ctrl_err(ctrl, "%s: Issue of Slot Disable command failed\n", | 371 | ctrl_err(ctrl, "%s: Issue of Slot Disable command failed\n", |
372 | __func__); | 372 | __func__); |
373 | return rc; | 373 | return rc; |
374 | } | 374 | } |
375 | 375 | ||
376 | rc = p_slot->hpc_ops->set_attention_status(p_slot, 0); | 376 | rc = p_slot->hpc_ops->set_attention_status(p_slot, 0); |
377 | if (rc) { | 377 | if (rc) { |
378 | ctrl_err(ctrl, "Issue of Set Attention command failed\n"); | 378 | ctrl_err(ctrl, "Issue of Set Attention command failed\n"); |
379 | return rc; | 379 | return rc; |
380 | } | 380 | } |
381 | 381 | ||
382 | p_slot->pwr_save = 0; | 382 | p_slot->pwr_save = 0; |
383 | p_slot->is_a_board = 0; | 383 | p_slot->is_a_board = 0; |
384 | 384 | ||
385 | return 0; | 385 | return 0; |
386 | } | 386 | } |
387 | 387 | ||
388 | 388 | ||
389 | struct pushbutton_work_info { | 389 | struct pushbutton_work_info { |
390 | struct slot *p_slot; | 390 | struct slot *p_slot; |
391 | struct work_struct work; | 391 | struct work_struct work; |
392 | }; | 392 | }; |
393 | 393 | ||
394 | /** | 394 | /** |
395 | * shpchp_pushbutton_thread - handle pushbutton events | 395 | * shpchp_pushbutton_thread - handle pushbutton events |
396 | * @work: &struct work_struct to be handled | 396 | * @work: &struct work_struct to be handled |
397 | * | 397 | * |
398 | * Scheduled procedure to handle blocking stuff for the pushbuttons. | 398 | * Scheduled procedure to handle blocking stuff for the pushbuttons. |
399 | * Handles all pending events and exits. | 399 | * Handles all pending events and exits. |
400 | */ | 400 | */ |
401 | static void shpchp_pushbutton_thread(struct work_struct *work) | 401 | static void shpchp_pushbutton_thread(struct work_struct *work) |
402 | { | 402 | { |
403 | struct pushbutton_work_info *info = | 403 | struct pushbutton_work_info *info = |
404 | container_of(work, struct pushbutton_work_info, work); | 404 | container_of(work, struct pushbutton_work_info, work); |
405 | struct slot *p_slot = info->p_slot; | 405 | struct slot *p_slot = info->p_slot; |
406 | 406 | ||
407 | mutex_lock(&p_slot->lock); | 407 | mutex_lock(&p_slot->lock); |
408 | switch (p_slot->state) { | 408 | switch (p_slot->state) { |
409 | case POWEROFF_STATE: | 409 | case POWEROFF_STATE: |
410 | mutex_unlock(&p_slot->lock); | 410 | mutex_unlock(&p_slot->lock); |
411 | shpchp_disable_slot(p_slot); | 411 | shpchp_disable_slot(p_slot); |
412 | mutex_lock(&p_slot->lock); | 412 | mutex_lock(&p_slot->lock); |
413 | p_slot->state = STATIC_STATE; | 413 | p_slot->state = STATIC_STATE; |
414 | break; | 414 | break; |
415 | case POWERON_STATE: | 415 | case POWERON_STATE: |
416 | mutex_unlock(&p_slot->lock); | 416 | mutex_unlock(&p_slot->lock); |
417 | if (shpchp_enable_slot(p_slot)) | 417 | if (shpchp_enable_slot(p_slot)) |
418 | p_slot->hpc_ops->green_led_off(p_slot); | 418 | p_slot->hpc_ops->green_led_off(p_slot); |
419 | mutex_lock(&p_slot->lock); | 419 | mutex_lock(&p_slot->lock); |
420 | p_slot->state = STATIC_STATE; | 420 | p_slot->state = STATIC_STATE; |
421 | break; | 421 | break; |
422 | default: | 422 | default: |
423 | break; | 423 | break; |
424 | } | 424 | } |
425 | mutex_unlock(&p_slot->lock); | 425 | mutex_unlock(&p_slot->lock); |
426 | 426 | ||
427 | kfree(info); | 427 | kfree(info); |
428 | } | 428 | } |
429 | 429 | ||
430 | void shpchp_queue_pushbutton_work(struct work_struct *work) | 430 | void shpchp_queue_pushbutton_work(struct work_struct *work) |
431 | { | 431 | { |
432 | struct slot *p_slot = container_of(work, struct slot, work.work); | 432 | struct slot *p_slot = container_of(work, struct slot, work.work); |
433 | struct pushbutton_work_info *info; | 433 | struct pushbutton_work_info *info; |
434 | 434 | ||
435 | info = kmalloc(sizeof(*info), GFP_KERNEL); | 435 | info = kmalloc(sizeof(*info), GFP_KERNEL); |
436 | if (!info) { | 436 | if (!info) { |
437 | ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n", | 437 | ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n", |
438 | __func__); | 438 | __func__); |
439 | return; | 439 | return; |
440 | } | 440 | } |
441 | info->p_slot = p_slot; | 441 | info->p_slot = p_slot; |
442 | INIT_WORK(&info->work, shpchp_pushbutton_thread); | 442 | INIT_WORK(&info->work, shpchp_pushbutton_thread); |
443 | 443 | ||
444 | mutex_lock(&p_slot->lock); | 444 | mutex_lock(&p_slot->lock); |
445 | switch (p_slot->state) { | 445 | switch (p_slot->state) { |
446 | case BLINKINGOFF_STATE: | 446 | case BLINKINGOFF_STATE: |
447 | p_slot->state = POWEROFF_STATE; | 447 | p_slot->state = POWEROFF_STATE; |
448 | break; | 448 | break; |
449 | case BLINKINGON_STATE: | 449 | case BLINKINGON_STATE: |
450 | p_slot->state = POWERON_STATE; | 450 | p_slot->state = POWERON_STATE; |
451 | break; | 451 | break; |
452 | default: | 452 | default: |
453 | kfree(info); | 453 | kfree(info); |
454 | goto out; | 454 | goto out; |
455 | } | 455 | } |
456 | queue_work(shpchp_wq, &info->work); | 456 | queue_work(p_slot->wq, &info->work); |
457 | out: | 457 | out: |
458 | mutex_unlock(&p_slot->lock); | 458 | mutex_unlock(&p_slot->lock); |
459 | } | 459 | } |
460 | 460 | ||
461 | static int update_slot_info (struct slot *slot) | 461 | static int update_slot_info (struct slot *slot) |
462 | { | 462 | { |
463 | struct hotplug_slot_info *info; | 463 | struct hotplug_slot_info *info; |
464 | int result; | 464 | int result; |
465 | 465 | ||
466 | info = kmalloc(sizeof(*info), GFP_KERNEL); | 466 | info = kmalloc(sizeof(*info), GFP_KERNEL); |
467 | if (!info) | 467 | if (!info) |
468 | return -ENOMEM; | 468 | return -ENOMEM; |
469 | 469 | ||
470 | slot->hpc_ops->get_power_status(slot, &(info->power_status)); | 470 | slot->hpc_ops->get_power_status(slot, &(info->power_status)); |
471 | slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); | 471 | slot->hpc_ops->get_attention_status(slot, &(info->attention_status)); |
472 | slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); | 472 | slot->hpc_ops->get_latch_status(slot, &(info->latch_status)); |
473 | slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); | 473 | slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status)); |
474 | 474 | ||
475 | result = pci_hp_change_slot_info(slot->hotplug_slot, info); | 475 | result = pci_hp_change_slot_info(slot->hotplug_slot, info); |
476 | kfree (info); | 476 | kfree (info); |
477 | return result; | 477 | return result; |
478 | } | 478 | } |
479 | 479 | ||
480 | /* | 480 | /* |
481 | * Note: This function must be called with slot->lock held | 481 | * Note: This function must be called with slot->lock held |
482 | */ | 482 | */ |
483 | static void handle_button_press_event(struct slot *p_slot) | 483 | static void handle_button_press_event(struct slot *p_slot) |
484 | { | 484 | { |
485 | u8 getstatus; | 485 | u8 getstatus; |
486 | struct controller *ctrl = p_slot->ctrl; | 486 | struct controller *ctrl = p_slot->ctrl; |
487 | 487 | ||
488 | switch (p_slot->state) { | 488 | switch (p_slot->state) { |
489 | case STATIC_STATE: | 489 | case STATIC_STATE: |
490 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | 490 | p_slot->hpc_ops->get_power_status(p_slot, &getstatus); |
491 | if (getstatus) { | 491 | if (getstatus) { |
492 | p_slot->state = BLINKINGOFF_STATE; | 492 | p_slot->state = BLINKINGOFF_STATE; |
493 | ctrl_info(ctrl, "PCI slot #%s - powering off due to " | 493 | ctrl_info(ctrl, "PCI slot #%s - powering off due to " |
494 | "button press.\n", slot_name(p_slot)); | 494 | "button press.\n", slot_name(p_slot)); |
495 | } else { | 495 | } else { |
496 | p_slot->state = BLINKINGON_STATE; | 496 | p_slot->state = BLINKINGON_STATE; |
497 | ctrl_info(ctrl, "PCI slot #%s - powering on due to " | 497 | ctrl_info(ctrl, "PCI slot #%s - powering on due to " |
498 | "button press.\n", slot_name(p_slot)); | 498 | "button press.\n", slot_name(p_slot)); |
499 | } | 499 | } |
500 | /* blink green LED and turn off amber */ | 500 | /* blink green LED and turn off amber */ |
501 | p_slot->hpc_ops->green_led_blink(p_slot); | 501 | p_slot->hpc_ops->green_led_blink(p_slot); |
502 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | 502 | p_slot->hpc_ops->set_attention_status(p_slot, 0); |
503 | 503 | ||
504 | queue_delayed_work(shpchp_wq, &p_slot->work, 5*HZ); | 504 | queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ); |
505 | break; | 505 | break; |
506 | case BLINKINGOFF_STATE: | 506 | case BLINKINGOFF_STATE: |
507 | case BLINKINGON_STATE: | 507 | case BLINKINGON_STATE: |
508 | /* | 508 | /* |
509 | * Cancel if we are still blinking; this means that we | 509 | * Cancel if we are still blinking; this means that we |
510 | * press the attention again before the 5 sec. limit | 510 | * press the attention again before the 5 sec. limit |
511 | * expires to cancel hot-add or hot-remove | 511 | * expires to cancel hot-add or hot-remove |
512 | */ | 512 | */ |
513 | ctrl_info(ctrl, "Button cancel on Slot(%s)\n", | 513 | ctrl_info(ctrl, "Button cancel on Slot(%s)\n", |
514 | slot_name(p_slot)); | 514 | slot_name(p_slot)); |
515 | cancel_delayed_work(&p_slot->work); | 515 | cancel_delayed_work(&p_slot->work); |
516 | if (p_slot->state == BLINKINGOFF_STATE) | 516 | if (p_slot->state == BLINKINGOFF_STATE) |
517 | p_slot->hpc_ops->green_led_on(p_slot); | 517 | p_slot->hpc_ops->green_led_on(p_slot); |
518 | else | 518 | else |
519 | p_slot->hpc_ops->green_led_off(p_slot); | 519 | p_slot->hpc_ops->green_led_off(p_slot); |
520 | p_slot->hpc_ops->set_attention_status(p_slot, 0); | 520 | p_slot->hpc_ops->set_attention_status(p_slot, 0); |
521 | ctrl_info(ctrl, "PCI slot #%s - action canceled due to " | 521 | ctrl_info(ctrl, "PCI slot #%s - action canceled due to " |
522 | "button press\n", slot_name(p_slot)); | 522 | "button press\n", slot_name(p_slot)); |
523 | p_slot->state = STATIC_STATE; | 523 | p_slot->state = STATIC_STATE; |
524 | break; | 524 | break; |
525 | case POWEROFF_STATE: | 525 | case POWEROFF_STATE: |
526 | case POWERON_STATE: | 526 | case POWERON_STATE: |
527 | /* | 527 | /* |
528 | * Ignore if the slot is on power-on or power-off state; | 528 | * Ignore if the slot is on power-on or power-off state; |
529 | * this means that the previous attention button action | 529 | * this means that the previous attention button action |
530 | * to hot-add or hot-remove is undergoing | 530 | * to hot-add or hot-remove is undergoing |
531 | */ | 531 | */ |
532 | ctrl_info(ctrl, "Button ignore on Slot(%s)\n", | 532 | ctrl_info(ctrl, "Button ignore on Slot(%s)\n", |
533 | slot_name(p_slot)); | 533 | slot_name(p_slot)); |
534 | update_slot_info(p_slot); | 534 | update_slot_info(p_slot); |
535 | break; | 535 | break; |
536 | default: | 536 | default: |
537 | ctrl_warn(ctrl, "Not a valid state\n"); | 537 | ctrl_warn(ctrl, "Not a valid state\n"); |
538 | break; | 538 | break; |
539 | } | 539 | } |
540 | } | 540 | } |
541 | 541 | ||
542 | static void interrupt_event_handler(struct work_struct *work) | 542 | static void interrupt_event_handler(struct work_struct *work) |
543 | { | 543 | { |
544 | struct event_info *info = container_of(work, struct event_info, work); | 544 | struct event_info *info = container_of(work, struct event_info, work); |
545 | struct slot *p_slot = info->p_slot; | 545 | struct slot *p_slot = info->p_slot; |
546 | 546 | ||
547 | mutex_lock(&p_slot->lock); | 547 | mutex_lock(&p_slot->lock); |
548 | switch (info->event_type) { | 548 | switch (info->event_type) { |
549 | case INT_BUTTON_PRESS: | 549 | case INT_BUTTON_PRESS: |
550 | handle_button_press_event(p_slot); | 550 | handle_button_press_event(p_slot); |
551 | break; | 551 | break; |
552 | case INT_POWER_FAULT: | 552 | case INT_POWER_FAULT: |
553 | ctrl_dbg(p_slot->ctrl, "%s: Power fault\n", __func__); | 553 | ctrl_dbg(p_slot->ctrl, "%s: Power fault\n", __func__); |
554 | p_slot->hpc_ops->set_attention_status(p_slot, 1); | 554 | p_slot->hpc_ops->set_attention_status(p_slot, 1); |
555 | p_slot->hpc_ops->green_led_off(p_slot); | 555 | p_slot->hpc_ops->green_led_off(p_slot); |
556 | break; | 556 | break; |
557 | default: | 557 | default: |
558 | update_slot_info(p_slot); | 558 | update_slot_info(p_slot); |
559 | break; | 559 | break; |
560 | } | 560 | } |
561 | mutex_unlock(&p_slot->lock); | 561 | mutex_unlock(&p_slot->lock); |
562 | 562 | ||
563 | kfree(info); | 563 | kfree(info); |
564 | } | 564 | } |
565 | 565 | ||
566 | 566 | ||
567 | static int shpchp_enable_slot (struct slot *p_slot) | 567 | static int shpchp_enable_slot (struct slot *p_slot) |
568 | { | 568 | { |
569 | u8 getstatus = 0; | 569 | u8 getstatus = 0; |
570 | int rc, retval = -ENODEV; | 570 | int rc, retval = -ENODEV; |
571 | struct controller *ctrl = p_slot->ctrl; | 571 | struct controller *ctrl = p_slot->ctrl; |
572 | 572 | ||
573 | /* Check to see if (latch closed, card present, power off) */ | 573 | /* Check to see if (latch closed, card present, power off) */ |
574 | mutex_lock(&p_slot->ctrl->crit_sect); | 574 | mutex_lock(&p_slot->ctrl->crit_sect); |
575 | rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | 575 | rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); |
576 | if (rc || !getstatus) { | 576 | if (rc || !getstatus) { |
577 | ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot)); | 577 | ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot)); |
578 | goto out; | 578 | goto out; |
579 | } | 579 | } |
580 | rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | 580 | rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
581 | if (rc || getstatus) { | 581 | if (rc || getstatus) { |
582 | ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot)); | 582 | ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot)); |
583 | goto out; | 583 | goto out; |
584 | } | 584 | } |
585 | rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | 585 | rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); |
586 | if (rc || getstatus) { | 586 | if (rc || getstatus) { |
587 | ctrl_info(ctrl, "Already enabled on slot(%s)\n", | 587 | ctrl_info(ctrl, "Already enabled on slot(%s)\n", |
588 | slot_name(p_slot)); | 588 | slot_name(p_slot)); |
589 | goto out; | 589 | goto out; |
590 | } | 590 | } |
591 | 591 | ||
592 | p_slot->is_a_board = 1; | 592 | p_slot->is_a_board = 1; |
593 | 593 | ||
594 | /* We have to save the presence info for these slots */ | 594 | /* We have to save the presence info for these slots */ |
595 | p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); | 595 | p_slot->hpc_ops->get_adapter_status(p_slot, &(p_slot->presence_save)); |
596 | p_slot->hpc_ops->get_power_status(p_slot, &(p_slot->pwr_save)); | 596 | p_slot->hpc_ops->get_power_status(p_slot, &(p_slot->pwr_save)); |
597 | ctrl_dbg(ctrl, "%s: p_slot->pwr_save %x\n", __func__, p_slot->pwr_save); | 597 | ctrl_dbg(ctrl, "%s: p_slot->pwr_save %x\n", __func__, p_slot->pwr_save); |
598 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | 598 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
599 | 599 | ||
600 | if(((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) || | 600 | if(((p_slot->ctrl->pci_dev->vendor == PCI_VENDOR_ID_AMD) || |
601 | (p_slot->ctrl->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458)) | 601 | (p_slot->ctrl->pci_dev->device == PCI_DEVICE_ID_AMD_POGO_7458)) |
602 | && p_slot->ctrl->num_slots == 1) { | 602 | && p_slot->ctrl->num_slots == 1) { |
603 | /* handle amd pogo errata; this must be done before enable */ | 603 | /* handle amd pogo errata; this must be done before enable */ |
604 | amd_pogo_errata_save_misc_reg(p_slot); | 604 | amd_pogo_errata_save_misc_reg(p_slot); |
605 | retval = board_added(p_slot); | 605 | retval = board_added(p_slot); |
606 | /* handle amd pogo errata; this must be done after enable */ | 606 | /* handle amd pogo errata; this must be done after enable */ |
607 | amd_pogo_errata_restore_misc_reg(p_slot); | 607 | amd_pogo_errata_restore_misc_reg(p_slot); |
608 | } else | 608 | } else |
609 | retval = board_added(p_slot); | 609 | retval = board_added(p_slot); |
610 | 610 | ||
611 | if (retval) { | 611 | if (retval) { |
612 | p_slot->hpc_ops->get_adapter_status(p_slot, | 612 | p_slot->hpc_ops->get_adapter_status(p_slot, |
613 | &(p_slot->presence_save)); | 613 | &(p_slot->presence_save)); |
614 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | 614 | p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
615 | } | 615 | } |
616 | 616 | ||
617 | update_slot_info(p_slot); | 617 | update_slot_info(p_slot); |
618 | out: | 618 | out: |
619 | mutex_unlock(&p_slot->ctrl->crit_sect); | 619 | mutex_unlock(&p_slot->ctrl->crit_sect); |
620 | return retval; | 620 | return retval; |
621 | } | 621 | } |
622 | 622 | ||
623 | 623 | ||
624 | static int shpchp_disable_slot (struct slot *p_slot) | 624 | static int shpchp_disable_slot (struct slot *p_slot) |
625 | { | 625 | { |
626 | u8 getstatus = 0; | 626 | u8 getstatus = 0; |
627 | int rc, retval = -ENODEV; | 627 | int rc, retval = -ENODEV; |
628 | struct controller *ctrl = p_slot->ctrl; | 628 | struct controller *ctrl = p_slot->ctrl; |
629 | 629 | ||
630 | if (!p_slot->ctrl) | 630 | if (!p_slot->ctrl) |
631 | return -ENODEV; | 631 | return -ENODEV; |
632 | 632 | ||
633 | /* Check to see if (latch closed, card present, power on) */ | 633 | /* Check to see if (latch closed, card present, power on) */ |
634 | mutex_lock(&p_slot->ctrl->crit_sect); | 634 | mutex_lock(&p_slot->ctrl->crit_sect); |
635 | 635 | ||
636 | rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); | 636 | rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus); |
637 | if (rc || !getstatus) { | 637 | if (rc || !getstatus) { |
638 | ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot)); | 638 | ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot)); |
639 | goto out; | 639 | goto out; |
640 | } | 640 | } |
641 | rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); | 641 | rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus); |
642 | if (rc || getstatus) { | 642 | if (rc || getstatus) { |
643 | ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot)); | 643 | ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot)); |
644 | goto out; | 644 | goto out; |
645 | } | 645 | } |
646 | rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); | 646 | rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus); |
647 | if (rc || !getstatus) { | 647 | if (rc || !getstatus) { |
648 | ctrl_info(ctrl, "Already disabled on slot(%s)\n", | 648 | ctrl_info(ctrl, "Already disabled on slot(%s)\n", |
649 | slot_name(p_slot)); | 649 | slot_name(p_slot)); |
650 | goto out; | 650 | goto out; |
651 | } | 651 | } |
652 | 652 | ||
653 | retval = remove_board(p_slot); | 653 | retval = remove_board(p_slot); |
654 | update_slot_info(p_slot); | 654 | update_slot_info(p_slot); |
655 | out: | 655 | out: |
656 | mutex_unlock(&p_slot->ctrl->crit_sect); | 656 | mutex_unlock(&p_slot->ctrl->crit_sect); |
657 | return retval; | 657 | return retval; |
658 | } | 658 | } |
659 | 659 | ||
660 | int shpchp_sysfs_enable_slot(struct slot *p_slot) | 660 | int shpchp_sysfs_enable_slot(struct slot *p_slot) |
661 | { | 661 | { |
662 | int retval = -ENODEV; | 662 | int retval = -ENODEV; |
663 | struct controller *ctrl = p_slot->ctrl; | 663 | struct controller *ctrl = p_slot->ctrl; |
664 | 664 | ||
665 | mutex_lock(&p_slot->lock); | 665 | mutex_lock(&p_slot->lock); |
666 | switch (p_slot->state) { | 666 | switch (p_slot->state) { |
667 | case BLINKINGON_STATE: | 667 | case BLINKINGON_STATE: |
668 | cancel_delayed_work(&p_slot->work); | 668 | cancel_delayed_work(&p_slot->work); |
669 | case STATIC_STATE: | 669 | case STATIC_STATE: |
670 | p_slot->state = POWERON_STATE; | 670 | p_slot->state = POWERON_STATE; |
671 | mutex_unlock(&p_slot->lock); | 671 | mutex_unlock(&p_slot->lock); |
672 | retval = shpchp_enable_slot(p_slot); | 672 | retval = shpchp_enable_slot(p_slot); |
673 | mutex_lock(&p_slot->lock); | 673 | mutex_lock(&p_slot->lock); |
674 | p_slot->state = STATIC_STATE; | 674 | p_slot->state = STATIC_STATE; |
675 | break; | 675 | break; |
676 | case POWERON_STATE: | 676 | case POWERON_STATE: |
677 | ctrl_info(ctrl, "Slot %s is already in powering on state\n", | 677 | ctrl_info(ctrl, "Slot %s is already in powering on state\n", |
678 | slot_name(p_slot)); | 678 | slot_name(p_slot)); |
679 | break; | 679 | break; |
680 | case BLINKINGOFF_STATE: | 680 | case BLINKINGOFF_STATE: |
681 | case POWEROFF_STATE: | 681 | case POWEROFF_STATE: |
682 | ctrl_info(ctrl, "Already enabled on slot %s\n", | 682 | ctrl_info(ctrl, "Already enabled on slot %s\n", |
683 | slot_name(p_slot)); | 683 | slot_name(p_slot)); |
684 | break; | 684 | break; |
685 | default: | 685 | default: |
686 | ctrl_err(ctrl, "Not a valid state on slot %s\n", | 686 | ctrl_err(ctrl, "Not a valid state on slot %s\n", |
687 | slot_name(p_slot)); | 687 | slot_name(p_slot)); |
688 | break; | 688 | break; |
689 | } | 689 | } |
690 | mutex_unlock(&p_slot->lock); | 690 | mutex_unlock(&p_slot->lock); |
691 | 691 | ||
692 | return retval; | 692 | return retval; |
693 | } | 693 | } |
694 | 694 | ||
695 | int shpchp_sysfs_disable_slot(struct slot *p_slot) | 695 | int shpchp_sysfs_disable_slot(struct slot *p_slot) |
696 | { | 696 | { |
697 | int retval = -ENODEV; | 697 | int retval = -ENODEV; |
698 | struct controller *ctrl = p_slot->ctrl; | 698 | struct controller *ctrl = p_slot->ctrl; |
699 | 699 | ||
700 | mutex_lock(&p_slot->lock); | 700 | mutex_lock(&p_slot->lock); |
701 | switch (p_slot->state) { | 701 | switch (p_slot->state) { |
702 | case BLINKINGOFF_STATE: | 702 | case BLINKINGOFF_STATE: |
703 | cancel_delayed_work(&p_slot->work); | 703 | cancel_delayed_work(&p_slot->work); |
704 | case STATIC_STATE: | 704 | case STATIC_STATE: |
705 | p_slot->state = POWEROFF_STATE; | 705 | p_slot->state = POWEROFF_STATE; |
706 | mutex_unlock(&p_slot->lock); | 706 | mutex_unlock(&p_slot->lock); |
707 | retval = shpchp_disable_slot(p_slot); | 707 | retval = shpchp_disable_slot(p_slot); |
708 | mutex_lock(&p_slot->lock); | 708 | mutex_lock(&p_slot->lock); |
709 | p_slot->state = STATIC_STATE; | 709 | p_slot->state = STATIC_STATE; |
710 | break; | 710 | break; |
711 | case POWEROFF_STATE: | 711 | case POWEROFF_STATE: |
712 | ctrl_info(ctrl, "Slot %s is already in powering off state\n", | 712 | ctrl_info(ctrl, "Slot %s is already in powering off state\n", |
713 | slot_name(p_slot)); | 713 | slot_name(p_slot)); |
714 | break; | 714 | break; |
715 | case BLINKINGON_STATE: | 715 | case BLINKINGON_STATE: |
716 | case POWERON_STATE: | 716 | case POWERON_STATE: |
717 | ctrl_info(ctrl, "Already disabled on slot %s\n", | 717 | ctrl_info(ctrl, "Already disabled on slot %s\n", |
718 | slot_name(p_slot)); | 718 | slot_name(p_slot)); |
719 | break; | 719 | break; |
720 | default: | 720 | default: |
721 | ctrl_err(ctrl, "Not a valid state on slot %s\n", | 721 | ctrl_err(ctrl, "Not a valid state on slot %s\n", |
722 | slot_name(p_slot)); | 722 | slot_name(p_slot)); |
723 | break; | 723 | break; |
724 | } | 724 | } |
725 | mutex_unlock(&p_slot->lock); | 725 | mutex_unlock(&p_slot->lock); |
726 | 726 | ||
727 | return retval; | 727 | return retval; |
728 | } | 728 | } |
729 | 729 |