aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/pci/hotplug/pciehp.h5
-rw-r--r--drivers/pci/hotplug/pciehp_core.c103
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c55
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
74struct event_info { 75struct 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
149extern int pciehp_event_start_thread(void); 152extern int pciehp_event_start_thread(void);
150extern void pciehp_event_stop_thread(void); 153extern 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 */
39int pciehp_debug; 40int 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 */
94static 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 */
104static 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
114lock_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 */
123static 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);
141set_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 */
150static 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
174static 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
214static spinlock_t hpc_event_lock; 222static 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
485static 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
504static 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
477static int hpc_set_attention_status(struct slot *slot, u8 value) 530static 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,