summaryrefslogtreecommitdiff
path: root/drivers/pci/hotplug/pciehp_ctrl.c
diff options
context:
space:
mode:
authorLukas Wunner <lukas@wunner.de>2018-07-19 17:27:45 -0500
committerBjorn Helgaas <helgaas@kernel.org>2018-07-23 17:04:14 -0500
commit9590192f2584c2cfc2fee88be22fe6e8921ed115 (patch)
treeeabe29fdb92fcb3b936c0ef513eb07a904390c84 /drivers/pci/hotplug/pciehp_ctrl.c
parent774d446b0f9222f46772dde8770d7c1c169c0284 (diff)
PCI: pciehp: Track enable/disable status
handle_button_press_event() currently determines whether the slot has been turned on or off by looking at the Power Controller Control bit in the Slot Control register. This assumes that an attention button implies presence of a power controller even though that's not mandated by the spec. Moreover the Power Controller Control bit is unreliable when a power fault occurs (PCIe r4.0, sec 6.7.1.8). This issue has existed since the driver was introduced in 2004. Fix by replacing STATIC_STATE with ON_STATE and OFF_STATE and tracking whether the slot has been turned on or off. This is also a required ingredient to make pciehp resilient to missed events, which is the object of an upcoming commit. Signed-off-by: Lukas Wunner <lukas@wunner.de> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Diffstat (limited to 'drivers/pci/hotplug/pciehp_ctrl.c')
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c28
1 files changed, 16 insertions, 12 deletions
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index a4a8a5457aca..627e846df802 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -147,13 +147,12 @@ void pciehp_queue_pushbutton_work(struct work_struct *work)
void pciehp_handle_button_press(struct slot *p_slot)
{
struct controller *ctrl = p_slot->ctrl;
- u8 getstatus;
mutex_lock(&p_slot->lock);
switch (p_slot->state) {
- case STATIC_STATE:
- pciehp_get_power_status(p_slot, &getstatus);
- if (getstatus) {
+ case OFF_STATE:
+ case ON_STATE:
+ if (p_slot->state == ON_STATE) {
p_slot->state = BLINKINGOFF_STATE;
ctrl_info(ctrl, "Slot(%s): Powering off due to button press\n",
slot_name(p_slot));
@@ -176,14 +175,16 @@ void pciehp_handle_button_press(struct slot *p_slot)
*/
ctrl_info(ctrl, "Slot(%s): Button cancel\n", slot_name(p_slot));
cancel_delayed_work(&p_slot->work);
- if (p_slot->state == BLINKINGOFF_STATE)
+ if (p_slot->state == BLINKINGOFF_STATE) {
+ p_slot->state = ON_STATE;
pciehp_green_led_on(p_slot);
- else
+ } else {
+ p_slot->state = OFF_STATE;
pciehp_green_led_off(p_slot);
+ }
pciehp_set_attention_status(p_slot, 0);
ctrl_info(ctrl, "Slot(%s): Action canceled due to button press\n",
slot_name(p_slot));
- p_slot->state = STATIC_STATE;
break;
case POWEROFF_STATE:
case POWERON_STATE:
@@ -216,7 +217,8 @@ void pciehp_handle_link_change(struct slot *p_slot)
case BLINKINGOFF_STATE:
cancel_delayed_work(&p_slot->work);
/* Fall through */
- case STATIC_STATE:
+ case ON_STATE:
+ case OFF_STATE:
if (link_active) {
p_slot->state = POWERON_STATE;
mutex_unlock(&p_slot->lock);
@@ -332,7 +334,7 @@ int pciehp_enable_slot(struct slot *slot)
pciehp_green_led_off(slot); /* may be blinking */
mutex_lock(&slot->lock);
- slot->state = STATIC_STATE;
+ slot->state = ret ? OFF_STATE : ON_STATE;
mutex_unlock(&slot->lock);
return ret;
@@ -368,7 +370,7 @@ int pciehp_disable_slot(struct slot *slot)
mutex_unlock(&slot->hotplug_lock);
mutex_lock(&slot->lock);
- slot->state = STATIC_STATE;
+ slot->state = OFF_STATE;
mutex_unlock(&slot->lock);
return ret;
@@ -382,7 +384,7 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
switch (p_slot->state) {
case BLINKINGON_STATE:
cancel_delayed_work(&p_slot->work);
- case STATIC_STATE:
+ case OFF_STATE:
p_slot->state = POWERON_STATE;
mutex_unlock(&p_slot->lock);
return pciehp_enable_slot(p_slot);
@@ -391,6 +393,7 @@ int pciehp_sysfs_enable_slot(struct slot *p_slot)
slot_name(p_slot));
break;
case BLINKINGOFF_STATE:
+ case ON_STATE:
case POWEROFF_STATE:
ctrl_info(ctrl, "Slot(%s): Already enabled\n",
slot_name(p_slot));
@@ -413,7 +416,7 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot)
switch (p_slot->state) {
case BLINKINGOFF_STATE:
cancel_delayed_work(&p_slot->work);
- case STATIC_STATE:
+ case ON_STATE:
p_slot->state = POWEROFF_STATE;
mutex_unlock(&p_slot->lock);
return pciehp_disable_slot(p_slot);
@@ -422,6 +425,7 @@ int pciehp_sysfs_disable_slot(struct slot *p_slot)
slot_name(p_slot));
break;
case BLINKINGON_STATE:
+ case OFF_STATE:
case POWERON_STATE:
ctrl_info(ctrl, "Slot(%s): Already disabled\n",
slot_name(p_slot));