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 /drivers/pci/hotplug/pciehp_core.c | |
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>
Diffstat (limited to 'drivers/pci/hotplug/pciehp_core.c')
-rw-r--r-- | drivers/pci/hotplug/pciehp_core.c | 103 |
1 files changed, 103 insertions, 0 deletions
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 | } |