diff options
author | Kristen Carlson Accardi <kristen.c.accardi@intel.com> | 2007-01-09 16:02:36 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-02-07 18:50:05 -0500 |
commit | 34d03419f03bcfdf70d9617a9b90b60c93482c4a (patch) | |
tree | 4371f46298a0bd24df178cbe73fe815bf146faad | |
parent | 262303fe329a51463925f3749aafc358a4201397 (diff) |
PCIEHP: Add Electro Mechanical Interlock (EMI) support to the PCIE hotplug driver.
Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/pci/hotplug/pciehp.h | 5 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 103 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 55 |
3 files changed, 163 insertions, 0 deletions
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 927dba9911ed..d19fcae8a7c0 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h | |||
@@ -69,6 +69,7 @@ struct slot { | |||
69 | struct hotplug_slot *hotplug_slot; | 69 | struct hotplug_slot *hotplug_slot; |
70 | struct list_head slot_list; | 70 | struct list_head slot_list; |
71 | char name[SLOT_NAME_SIZE]; | 71 | char name[SLOT_NAME_SIZE]; |
72 | unsigned long last_emi_toggle; | ||
72 | }; | 73 | }; |
73 | 74 | ||
74 | struct event_info { | 75 | struct event_info { |
@@ -138,6 +139,7 @@ struct controller { | |||
138 | #define ATTN_LED_PRSN 0x00000008 | 139 | #define ATTN_LED_PRSN 0x00000008 |
139 | #define PWR_LED_PRSN 0x00000010 | 140 | #define PWR_LED_PRSN 0x00000010 |
140 | #define HP_SUPR_RM_SUP 0x00000020 | 141 | #define HP_SUPR_RM_SUP 0x00000020 |
142 | #define EMI_PRSN 0x00020000 | ||
141 | 143 | ||
142 | #define ATTN_BUTTN(cap) (cap & ATTN_BUTTN_PRSN) | 144 | #define ATTN_BUTTN(cap) (cap & ATTN_BUTTN_PRSN) |
143 | #define POWER_CTRL(cap) (cap & PWR_CTRL_PRSN) | 145 | #define POWER_CTRL(cap) (cap & PWR_CTRL_PRSN) |
@@ -145,6 +147,7 @@ struct controller { | |||
145 | #define ATTN_LED(cap) (cap & ATTN_LED_PRSN) | 147 | #define ATTN_LED(cap) (cap & ATTN_LED_PRSN) |
146 | #define PWR_LED(cap) (cap & PWR_LED_PRSN) | 148 | #define PWR_LED(cap) (cap & PWR_LED_PRSN) |
147 | #define HP_SUPR_RM(cap) (cap & HP_SUPR_RM_SUP) | 149 | #define HP_SUPR_RM(cap) (cap & HP_SUPR_RM_SUP) |
150 | #define EMI(cap) (cap & EMI_PRSN) | ||
148 | 151 | ||
149 | extern int pciehp_event_start_thread(void); | 152 | extern int pciehp_event_start_thread(void); |
150 | extern void pciehp_event_stop_thread(void); | 153 | extern void pciehp_event_stop_thread(void); |
@@ -182,6 +185,8 @@ struct hpc_ops { | |||
182 | int (*set_attention_status)(struct slot *slot, u8 status); | 185 | int (*set_attention_status)(struct slot *slot, u8 status); |
183 | int (*get_latch_status)(struct slot *slot, u8 *status); | 186 | int (*get_latch_status)(struct slot *slot, u8 *status); |
184 | int (*get_adapter_status)(struct slot *slot, u8 *status); | 187 | int (*get_adapter_status)(struct slot *slot, u8 *status); |
188 | int (*get_emi_status)(struct slot *slot, u8 *status); | ||
189 | int (*toggle_emi)(struct slot *slot); | ||
185 | int (*get_max_bus_speed)(struct slot *slot, enum pci_bus_speed *speed); | 190 | int (*get_max_bus_speed)(struct slot *slot, enum pci_bus_speed *speed); |
186 | int (*get_cur_bus_speed)(struct slot *slot, enum pci_bus_speed *speed); | 191 | int (*get_cur_bus_speed)(struct slot *slot, enum pci_bus_speed *speed); |
187 | int (*get_max_lnk_width)(struct slot *slot, enum pcie_link_width *val); | 192 | int (*get_max_lnk_width)(struct slot *slot, enum pcie_link_width *val); |
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index f7b8e0504fec..a92eda6e02f6 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/pci.h> | 34 | #include <linux/pci.h> |
35 | #include "pciehp.h" | 35 | #include "pciehp.h" |
36 | #include <linux/interrupt.h> | 36 | #include <linux/interrupt.h> |
37 | #include <linux/time.h> | ||
37 | 38 | ||
38 | /* Global variables */ | 39 | /* Global variables */ |
39 | int pciehp_debug; | 40 | int pciehp_debug; |
@@ -87,6 +88,95 @@ static struct hotplug_slot_ops pciehp_hotplug_slot_ops = { | |||
87 | .get_cur_bus_speed = get_cur_bus_speed, | 88 | .get_cur_bus_speed = get_cur_bus_speed, |
88 | }; | 89 | }; |
89 | 90 | ||
91 | /* | ||
92 | * Check the status of the Electro Mechanical Interlock (EMI) | ||
93 | */ | ||
94 | static int get_lock_status(struct hotplug_slot *hotplug_slot, u8 *value) | ||
95 | { | ||
96 | struct slot *slot = hotplug_slot->private; | ||
97 | return (slot->hpc_ops->get_emi_status(slot, value)); | ||
98 | } | ||
99 | |||
100 | /* | ||
101 | * sysfs interface for the Electro Mechanical Interlock (EMI) | ||
102 | * 1 == locked, 0 == unlocked | ||
103 | */ | ||
104 | static ssize_t lock_read_file(struct hotplug_slot *slot, char *buf) | ||
105 | { | ||
106 | int retval; | ||
107 | u8 value; | ||
108 | |||
109 | retval = get_lock_status(slot, &value); | ||
110 | if (retval) | ||
111 | goto lock_read_exit; | ||
112 | retval = sprintf (buf, "%d\n", value); | ||
113 | |||
114 | lock_read_exit: | ||
115 | return retval; | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * Change the status of the Electro Mechanical Interlock (EMI) | ||
120 | * This is a toggle - in addition there must be at least 1 second | ||
121 | * in between toggles. | ||
122 | */ | ||
123 | static int set_lock_status(struct hotplug_slot *hotplug_slot, u8 status) | ||
124 | { | ||
125 | struct slot *slot = hotplug_slot->private; | ||
126 | int retval; | ||
127 | u8 value; | ||
128 | |||
129 | mutex_lock(&slot->ctrl->crit_sect); | ||
130 | |||
131 | /* has it been >1 sec since our last toggle? */ | ||
132 | if ((get_seconds() - slot->last_emi_toggle) < 1) | ||
133 | return -EINVAL; | ||
134 | |||
135 | /* see what our current state is */ | ||
136 | retval = get_lock_status(hotplug_slot, &value); | ||
137 | if (retval || (value == status)) | ||
138 | goto set_lock_exit; | ||
139 | |||
140 | slot->hpc_ops->toggle_emi(slot); | ||
141 | set_lock_exit: | ||
142 | mutex_unlock(&slot->ctrl->crit_sect); | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | /* | ||
147 | * sysfs interface which allows the user to toggle the Electro Mechanical | ||
148 | * Interlock. Valid values are either 0 or 1. 0 == unlock, 1 == lock | ||
149 | */ | ||
150 | static ssize_t lock_write_file(struct hotplug_slot *slot, const char *buf, | ||
151 | size_t count) | ||
152 | { | ||
153 | unsigned long llock; | ||
154 | u8 lock; | ||
155 | int retval = 0; | ||
156 | |||
157 | llock = simple_strtoul(buf, NULL, 10); | ||
158 | lock = (u8)(llock & 0xff); | ||
159 | |||
160 | switch (lock) { | ||
161 | case 0: | ||
162 | case 1: | ||
163 | retval = set_lock_status(slot, lock); | ||
164 | break; | ||
165 | default: | ||
166 | err ("%d is an invalid lock value\n", lock); | ||
167 | retval = -EINVAL; | ||
168 | } | ||
169 | if (retval) | ||
170 | return retval; | ||
171 | return count; | ||
172 | } | ||
173 | |||
174 | static struct hotplug_slot_attribute hotplug_slot_attr_lock = { | ||
175 | .attr = {.name = "lock", .mode = S_IFREG | S_IRUGO | S_IWUSR}, | ||
176 | .show = lock_read_file, | ||
177 | .store = lock_write_file | ||
178 | }; | ||
179 | |||
90 | /** | 180 | /** |
91 | * release_slot - free up the memory used by a slot | 181 | * release_slot - free up the memory used by a slot |
92 | * @hotplug_slot: slot to free | 182 | * @hotplug_slot: slot to free |
@@ -159,6 +249,16 @@ static int init_slots(struct controller *ctrl) | |||
159 | err ("pci_hp_register failed with error %d\n", retval); | 249 | err ("pci_hp_register failed with error %d\n", retval); |
160 | goto error_info; | 250 | goto error_info; |
161 | } | 251 | } |
252 | /* create additional sysfs entries */ | ||
253 | if (EMI(ctrl->ctrlcap)) { | ||
254 | retval = sysfs_create_file(&hotplug_slot->kobj, | ||
255 | &hotplug_slot_attr_lock.attr); | ||
256 | if (retval) { | ||
257 | pci_hp_deregister(hotplug_slot); | ||
258 | err("cannot create additional sysfs entries\n"); | ||
259 | goto error_info; | ||
260 | } | ||
261 | } | ||
162 | 262 | ||
163 | list_add(&slot->slot_list, &ctrl->slot_list); | 263 | list_add(&slot->slot_list, &ctrl->slot_list); |
164 | } | 264 | } |
@@ -183,6 +283,9 @@ static void cleanup_slots(struct controller *ctrl) | |||
183 | list_for_each_safe(tmp, next, &ctrl->slot_list) { | 283 | list_for_each_safe(tmp, next, &ctrl->slot_list) { |
184 | slot = list_entry(tmp, struct slot, slot_list); | 284 | slot = list_entry(tmp, struct slot, slot_list); |
185 | list_del(&slot->slot_list); | 285 | list_del(&slot->slot_list); |
286 | if (EMI(ctrl->ctrlcap)) | ||
287 | sysfs_remove_file(&slot->hotplug_slot->kobj, | ||
288 | &hotplug_slot_attr_lock.attr); | ||
186 | pci_hp_deregister(slot->hotplug_slot); | 289 | pci_hp_deregister(slot->hotplug_slot); |
187 | } | 290 | } |
188 | } | 291 | } |
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index eb1862b50bb1..fbc64aa2dd68 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/timer.h> | 35 | #include <linux/timer.h> |
36 | #include <linux/pci.h> | 36 | #include <linux/pci.h> |
37 | #include <linux/interrupt.h> | 37 | #include <linux/interrupt.h> |
38 | #include <linux/time.h> | ||
38 | 39 | ||
39 | #include "../pci.h" | 40 | #include "../pci.h" |
40 | #include "pciehp.h" | 41 | #include "pciehp.h" |
@@ -192,6 +193,7 @@ static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value) | |||
192 | #define ATTN_LED_CTRL 0x00C0 | 193 | #define ATTN_LED_CTRL 0x00C0 |
193 | #define PWR_LED_CTRL 0x0300 | 194 | #define PWR_LED_CTRL 0x0300 |
194 | #define PWR_CTRL 0x0400 | 195 | #define PWR_CTRL 0x0400 |
196 | #define EMI_CTRL 0x0800 | ||
195 | 197 | ||
196 | /* Attention indicator and Power indicator states */ | 198 | /* Attention indicator and Power indicator states */ |
197 | #define LED_ON 0x01 | 199 | #define LED_ON 0x01 |
@@ -202,6 +204,10 @@ static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value) | |||
202 | #define POWER_ON 0 | 204 | #define POWER_ON 0 |
203 | #define POWER_OFF 0x0400 | 205 | #define POWER_OFF 0x0400 |
204 | 206 | ||
207 | /* EMI Status defines */ | ||
208 | #define EMI_DISENGAGED 0 | ||
209 | #define EMI_ENGAGED 1 | ||
210 | |||
205 | /* Field definitions in Slot Status Register */ | 211 | /* Field definitions in Slot Status Register */ |
206 | #define ATTN_BUTTN_PRESSED 0x0001 | 212 | #define ATTN_BUTTN_PRESSED 0x0001 |
207 | #define PWR_FAULT_DETECTED 0x0002 | 213 | #define PWR_FAULT_DETECTED 0x0002 |
@@ -210,6 +216,8 @@ static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value) | |||
210 | #define CMD_COMPLETED 0x0010 | 216 | #define CMD_COMPLETED 0x0010 |
211 | #define MRL_STATE 0x0020 | 217 | #define MRL_STATE 0x0020 |
212 | #define PRSN_STATE 0x0040 | 218 | #define PRSN_STATE 0x0040 |
219 | #define EMI_STATE 0x0080 | ||
220 | #define EMI_STATUS_BIT 7 | ||
213 | 221 | ||
214 | static spinlock_t hpc_event_lock; | 222 | static spinlock_t hpc_event_lock; |
215 | 223 | ||
@@ -474,6 +482,51 @@ static int hpc_query_power_fault(struct slot *slot) | |||
474 | return pwr_fault; | 482 | return pwr_fault; |
475 | } | 483 | } |
476 | 484 | ||
485 | static int hpc_get_emi_status(struct slot *slot, u8 *status) | ||
486 | { | ||
487 | struct controller *ctrl = slot->ctrl; | ||
488 | u16 slot_status; | ||
489 | int retval = 0; | ||
490 | |||
491 | DBG_ENTER_ROUTINE | ||
492 | |||
493 | retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); | ||
494 | if (retval) { | ||
495 | err("%s : Cannot check EMI status\n", __FUNCTION__); | ||
496 | return retval; | ||
497 | } | ||
498 | *status = (slot_status & EMI_STATE) >> EMI_STATUS_BIT; | ||
499 | |||
500 | DBG_LEAVE_ROUTINE | ||
501 | return retval; | ||
502 | } | ||
503 | |||
504 | static int hpc_toggle_emi(struct slot *slot) | ||
505 | { | ||
506 | struct controller *ctrl = slot->ctrl; | ||
507 | u16 slot_cmd = 0; | ||
508 | u16 slot_ctrl; | ||
509 | int rc = 0; | ||
510 | |||
511 | DBG_ENTER_ROUTINE | ||
512 | |||
513 | rc = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); | ||
514 | if (rc) { | ||
515 | err("%s : hp_register_read_word SLOT_CTRL failed\n", | ||
516 | __FUNCTION__); | ||
517 | return rc; | ||
518 | } | ||
519 | |||
520 | slot_cmd = (slot_ctrl | EMI_CTRL); | ||
521 | if (!pciehp_poll_mode) | ||
522 | slot_cmd = slot_cmd | HP_INTR_ENABLE; | ||
523 | |||
524 | pcie_write_cmd(slot, slot_cmd); | ||
525 | slot->last_emi_toggle = get_seconds(); | ||
526 | DBG_LEAVE_ROUTINE | ||
527 | return rc; | ||
528 | } | ||
529 | |||
477 | static int hpc_set_attention_status(struct slot *slot, u8 value) | 530 | static int hpc_set_attention_status(struct slot *slot, u8 value) |
478 | { | 531 | { |
479 | struct controller *ctrl = slot->ctrl; | 532 | struct controller *ctrl = slot->ctrl; |
@@ -1009,6 +1062,8 @@ static struct hpc_ops pciehp_hpc_ops = { | |||
1009 | .get_attention_status = hpc_get_attention_status, | 1062 | .get_attention_status = hpc_get_attention_status, |
1010 | .get_latch_status = hpc_get_latch_status, | 1063 | .get_latch_status = hpc_get_latch_status, |
1011 | .get_adapter_status = hpc_get_adapter_status, | 1064 | .get_adapter_status = hpc_get_adapter_status, |
1065 | .get_emi_status = hpc_get_emi_status, | ||
1066 | .toggle_emi = hpc_toggle_emi, | ||
1012 | 1067 | ||
1013 | .get_max_bus_speed = hpc_get_max_lnk_speed, | 1068 | .get_max_bus_speed = hpc_get_max_lnk_speed, |
1014 | .get_cur_bus_speed = hpc_get_cur_lnk_speed, | 1069 | .get_cur_bus_speed = hpc_get_cur_lnk_speed, |