Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Kristen Carlson Accardi and committed by
Greg Kroah-Hartman
34d03419 262303fe

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