--- zzzz-none-000/linux-3.10.107/drivers/pci/hotplug/pciehp_ctrl.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/pci/hotplug/pciehp_ctrl.c 2021-02-04 17:41:59.000000000 +0000 @@ -37,132 +37,32 @@ static void interrupt_event_handler(struct work_struct *work); -static int queue_interrupt_event(struct slot *p_slot, u32 event_type) +void pciehp_queue_interrupt_event(struct slot *p_slot, u32 event_type) { struct event_info *info; info = kmalloc(sizeof(*info), GFP_ATOMIC); - if (!info) - return -ENOMEM; + if (!info) { + ctrl_err(p_slot->ctrl, "dropped event %d (ENOMEM)\n", event_type); + return; + } + INIT_WORK(&info->work, interrupt_event_handler); info->event_type = event_type; info->p_slot = p_slot; - INIT_WORK(&info->work, interrupt_event_handler); - queue_work(p_slot->wq, &info->work); - - return 0; -} - -u8 pciehp_handle_attention_button(struct slot *p_slot) -{ - u32 event_type; - struct controller *ctrl = p_slot->ctrl; - - /* Attention Button Change */ - ctrl_dbg(ctrl, "Attention button interrupt received\n"); - - /* - * Button pressed - See if need to TAKE ACTION!!! - */ - ctrl_info(ctrl, "Button pressed on Slot(%s)\n", slot_name(p_slot)); - event_type = INT_BUTTON_PRESS; - - queue_interrupt_event(p_slot, event_type); - - return 0; -} - -u8 pciehp_handle_switch_change(struct slot *p_slot) -{ - u8 getstatus; - u32 event_type; - struct controller *ctrl = p_slot->ctrl; - - /* Switch Change */ - ctrl_dbg(ctrl, "Switch interrupt received\n"); - - pciehp_get_latch_status(p_slot, &getstatus); - if (getstatus) { - /* - * Switch opened - */ - ctrl_info(ctrl, "Latch open on Slot(%s)\n", slot_name(p_slot)); - event_type = INT_SWITCH_OPEN; - } else { - /* - * Switch closed - */ - ctrl_info(ctrl, "Latch close on Slot(%s)\n", slot_name(p_slot)); - event_type = INT_SWITCH_CLOSE; - } - - queue_interrupt_event(p_slot, event_type); - - return 1; -} - -u8 pciehp_handle_presence_change(struct slot *p_slot) -{ - u32 event_type; - u8 presence_save; - struct controller *ctrl = p_slot->ctrl; - - /* Presence Change */ - ctrl_dbg(ctrl, "Presence/Notify input change\n"); - - /* Switch is open, assume a presence change - * Save the presence state - */ - pciehp_get_adapter_status(p_slot, &presence_save); - if (presence_save) { - /* - * Card Present - */ - ctrl_info(ctrl, "Card present on Slot(%s)\n", slot_name(p_slot)); - event_type = INT_PRESENCE_ON; - } else { - /* - * Not Present - */ - ctrl_info(ctrl, "Card not present on Slot(%s)\n", - slot_name(p_slot)); - event_type = INT_PRESENCE_OFF; - } - - queue_interrupt_event(p_slot, event_type); - - return 1; -} - -u8 pciehp_handle_power_fault(struct slot *p_slot) -{ - u32 event_type; - struct controller *ctrl = p_slot->ctrl; - - /* power fault */ - ctrl_dbg(ctrl, "Power fault interrupt received\n"); - ctrl_err(ctrl, "Power fault on slot %s\n", slot_name(p_slot)); - event_type = INT_POWER_FAULT; - ctrl_info(ctrl, "Power fault bit %x set\n", 0); - queue_interrupt_event(p_slot, event_type); - - return 1; } /* The following routines constitute the bulk of the hotplug controller logic */ -static void set_slot_off(struct controller *ctrl, struct slot * pslot) +static void set_slot_off(struct controller *ctrl, struct slot *pslot) { /* turn off slot, turn on Amber LED, turn off Green LED if supported*/ if (POWER_CTRL(ctrl)) { - if (pciehp_power_off_slot(pslot)) { - ctrl_err(ctrl, - "Issue of Slot Power Off command failed\n"); - return; - } + pciehp_power_off_slot(pslot); + /* * After turning power off, we must wait for at least 1 second * before taking any action that relies on power having been @@ -171,16 +71,8 @@ msleep(1000); } - if (PWR_LED(ctrl)) - pciehp_green_led_off(pslot); - - if (ATTN_LED(ctrl)) { - if (pciehp_set_attention_status(pslot, 1)) { - ctrl_err(ctrl, - "Issue of Set Attention Led command failed\n"); - return; - } - } + pciehp_green_led_off(pslot); + pciehp_set_attention_status(pslot, 1); } /** @@ -203,8 +95,7 @@ return retval; } - if (PWR_LED(ctrl)) - pciehp_green_led_blink(p_slot); + pciehp_green_led_blink(p_slot); /* Check link training status */ retval = pciehp_check_link_status(ctrl); @@ -224,12 +115,11 @@ if (retval) { ctrl_err(ctrl, "Cannot add device at %04x:%02x:00\n", pci_domain_nr(parent), parent->number); - goto err_exit; + if (retval != -EEXIST) + goto err_exit; } - if (PWR_LED(ctrl)) - pciehp_green_led_on(p_slot); - + pciehp_green_led_on(p_slot); return 0; err_exit: @@ -243,7 +133,7 @@ */ static int remove_board(struct slot *p_slot) { - int retval = 0; + int retval; struct controller *ctrl = p_slot->ctrl; retval = pciehp_unconfigure_device(p_slot); @@ -251,13 +141,8 @@ return retval; if (POWER_CTRL(ctrl)) { - /* power off slot */ - retval = pciehp_power_off_slot(p_slot); - if (retval) { - ctrl_err(ctrl, - "Issue of Slot Disable command failed\n"); - return retval; - } + pciehp_power_off_slot(p_slot); + /* * After turning power off, we must wait for at least 1 second * before taking any action that relies on power having been @@ -267,15 +152,16 @@ } /* turn off Green LED */ - if (PWR_LED(ctrl)) - pciehp_green_led_off(p_slot); - + pciehp_green_led_off(p_slot); return 0; } struct power_work_info { struct slot *p_slot; struct work_struct work; + unsigned int req; +#define DISABLE_REQ 0 +#define ENABLE_REQ 1 }; /** @@ -290,62 +176,67 @@ struct power_work_info *info = container_of(work, struct power_work_info, work); struct slot *p_slot = info->p_slot; + int ret; - mutex_lock(&p_slot->lock); - switch (p_slot->state) { - case POWEROFF_STATE: - mutex_unlock(&p_slot->lock); - ctrl_dbg(p_slot->ctrl, - "Disabling domain:bus:device=%04x:%02x:00\n", - pci_domain_nr(p_slot->ctrl->pcie->port->subordinate), - p_slot->ctrl->pcie->port->subordinate->number); + switch (info->req) { + case DISABLE_REQ: + mutex_lock(&p_slot->hotplug_lock); pciehp_disable_slot(p_slot); + mutex_unlock(&p_slot->hotplug_lock); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; - break; - case POWERON_STATE: mutex_unlock(&p_slot->lock); - if (pciehp_enable_slot(p_slot) && PWR_LED(p_slot->ctrl)) + break; + case ENABLE_REQ: + mutex_lock(&p_slot->hotplug_lock); + ret = pciehp_enable_slot(p_slot); + mutex_unlock(&p_slot->hotplug_lock); + if (ret) pciehp_green_led_off(p_slot); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; + mutex_unlock(&p_slot->lock); break; default: break; } - mutex_unlock(&p_slot->lock); kfree(info); } -void pciehp_queue_pushbutton_work(struct work_struct *work) +static void pciehp_queue_power_work(struct slot *p_slot, int req) { - struct slot *p_slot = container_of(work, struct slot, work.work); struct power_work_info *info; + p_slot->state = (req == ENABLE_REQ) ? POWERON_STATE : POWEROFF_STATE; + info = kmalloc(sizeof(*info), GFP_KERNEL); if (!info) { - ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n", - __func__); + ctrl_err(p_slot->ctrl, "no memory to queue %s request\n", + (req == ENABLE_REQ) ? "poweron" : "poweroff"); return; } info->p_slot = p_slot; INIT_WORK(&info->work, pciehp_power_thread); + info->req = req; + queue_work(p_slot->wq, &info->work); +} + +void pciehp_queue_pushbutton_work(struct work_struct *work) +{ + struct slot *p_slot = container_of(work, struct slot, work.work); mutex_lock(&p_slot->lock); switch (p_slot->state) { case BLINKINGOFF_STATE: - p_slot->state = POWEROFF_STATE; + pciehp_queue_power_work(p_slot, DISABLE_REQ); break; case BLINKINGON_STATE: - p_slot->state = POWERON_STATE; + pciehp_queue_power_work(p_slot, ENABLE_REQ); break; default: - kfree(info); - goto out; + break; } - queue_work(p_slot->wq, &info->work); - out: mutex_unlock(&p_slot->lock); } @@ -362,21 +253,16 @@ pciehp_get_power_status(p_slot, &getstatus); if (getstatus) { p_slot->state = BLINKINGOFF_STATE; - ctrl_info(ctrl, - "PCI slot #%s - powering off due to button " - "press.\n", slot_name(p_slot)); + ctrl_info(ctrl, "PCI slot #%s - powering off due to button press\n", + slot_name(p_slot)); } else { p_slot->state = BLINKINGON_STATE; - ctrl_info(ctrl, - "PCI slot #%s - powering on due to button " - "press.\n", slot_name(p_slot)); + ctrl_info(ctrl, "PCI slot #%s - powering on due to button press\n", + slot_name(p_slot)); } /* blink green LED and turn off amber */ - if (PWR_LED(ctrl)) - pciehp_green_led_blink(p_slot); - if (ATTN_LED(ctrl)) - pciehp_set_attention_status(p_slot, 0); - + pciehp_green_led_blink(p_slot); + pciehp_set_attention_status(p_slot, 0); queue_delayed_work(p_slot->wq, &p_slot->work, 5*HZ); break; case BLINKINGOFF_STATE: @@ -388,17 +274,13 @@ */ ctrl_info(ctrl, "Button cancel on Slot(%s)\n", slot_name(p_slot)); cancel_delayed_work(&p_slot->work); - if (p_slot->state == BLINKINGOFF_STATE) { - if (PWR_LED(ctrl)) - pciehp_green_led_on(p_slot); - } else { - if (PWR_LED(ctrl)) - pciehp_green_led_off(p_slot); - } - if (ATTN_LED(ctrl)) - pciehp_set_attention_status(p_slot, 0); - ctrl_info(ctrl, "PCI slot #%s - action canceled " - "due to button press\n", slot_name(p_slot)); + if (p_slot->state == BLINKINGOFF_STATE) + pciehp_green_led_on(p_slot); + else + pciehp_green_led_off(p_slot); + pciehp_set_attention_status(p_slot, 0); + ctrl_info(ctrl, "PCI slot #%s - action canceled due to button press\n", + slot_name(p_slot)); p_slot->state = STATIC_STATE; break; case POWEROFF_STATE: @@ -411,7 +293,7 @@ ctrl_info(ctrl, "Button ignore on Slot(%s)\n", slot_name(p_slot)); break; default: - ctrl_warn(ctrl, "Not a valid state\n"); + ctrl_warn(ctrl, "ignoring invalid state %#x\n", p_slot->state); break; } } @@ -422,24 +304,59 @@ static void handle_surprise_event(struct slot *p_slot) { u8 getstatus; - struct power_work_info *info; - - info = kmalloc(sizeof(*info), GFP_KERNEL); - if (!info) { - ctrl_err(p_slot->ctrl, "%s: Cannot allocate memory\n", - __func__); - return; - } - info->p_slot = p_slot; - INIT_WORK(&info->work, pciehp_power_thread); pciehp_get_adapter_status(p_slot, &getstatus); if (!getstatus) - p_slot->state = POWEROFF_STATE; + pciehp_queue_power_work(p_slot, DISABLE_REQ); else - p_slot->state = POWERON_STATE; + pciehp_queue_power_work(p_slot, ENABLE_REQ); +} - queue_work(p_slot->wq, &info->work); +/* + * Note: This function must be called with slot->lock held + */ +static void handle_link_event(struct slot *p_slot, u32 event) +{ + struct controller *ctrl = p_slot->ctrl; + + switch (p_slot->state) { + case BLINKINGON_STATE: + case BLINKINGOFF_STATE: + cancel_delayed_work(&p_slot->work); + /* Fall through */ + case STATIC_STATE: + pciehp_queue_power_work(p_slot, event == INT_LINK_UP ? + ENABLE_REQ : DISABLE_REQ); + break; + case POWERON_STATE: + if (event == INT_LINK_UP) { + ctrl_info(ctrl, + "Link Up event ignored on slot(%s): already powering on\n", + slot_name(p_slot)); + } else { + ctrl_info(ctrl, + "Link Down event queued on slot(%s): currently getting powered on\n", + slot_name(p_slot)); + pciehp_queue_power_work(p_slot, DISABLE_REQ); + } + break; + case POWEROFF_STATE: + if (event == INT_LINK_UP) { + ctrl_info(ctrl, + "Link Up event queued on slot(%s): currently getting powered off\n", + slot_name(p_slot)); + pciehp_queue_power_work(p_slot, ENABLE_REQ); + } else { + ctrl_info(ctrl, + "Link Down event ignored on slot(%s): already powering off\n", + slot_name(p_slot)); + } + break; + default: + ctrl_err(ctrl, "ignoring invalid state %#x on slot(%s)\n", + p_slot->state, slot_name(p_slot)); + break; + } } static void interrupt_event_handler(struct work_struct *work) @@ -456,18 +373,23 @@ case INT_POWER_FAULT: if (!POWER_CTRL(ctrl)) break; - if (ATTN_LED(ctrl)) - pciehp_set_attention_status(p_slot, 1); - if (PWR_LED(ctrl)) - pciehp_green_led_off(p_slot); + pciehp_set_attention_status(p_slot, 1); + pciehp_green_led_off(p_slot); break; case INT_PRESENCE_ON: + handle_surprise_event(p_slot); + break; case INT_PRESENCE_OFF: - if (!HP_SUPR_RM(ctrl)) - break; - ctrl_dbg(ctrl, "Surprise Removal\n"); + /* + * Regardless of surprise capability, we need to + * definitely remove a card that has been pulled out! + */ handle_surprise_event(p_slot); break; + case INT_LINK_UP: + case INT_LINK_DOWN: + handle_link_event(p_slot, info->event_type); + break; default: break; } @@ -476,20 +398,23 @@ kfree(info); } +/* + * Note: This function must be called with slot->hotplug_lock held + */ int pciehp_enable_slot(struct slot *p_slot) { u8 getstatus = 0; int rc; struct controller *ctrl = p_slot->ctrl; - rc = pciehp_get_adapter_status(p_slot, &getstatus); - if (rc || !getstatus) { + pciehp_get_adapter_status(p_slot, &getstatus); + if (!getstatus) { ctrl_info(ctrl, "No adapter on slot(%s)\n", slot_name(p_slot)); return -ENODEV; } if (MRL_SENS(p_slot->ctrl)) { - rc = pciehp_get_latch_status(p_slot, &getstatus); - if (rc || getstatus) { + pciehp_get_latch_status(p_slot, &getstatus); + if (getstatus) { ctrl_info(ctrl, "Latch open on slot(%s)\n", slot_name(p_slot)); return -ENODEV; @@ -497,8 +422,8 @@ } if (POWER_CTRL(p_slot->ctrl)) { - rc = pciehp_get_power_status(p_slot, &getstatus); - if (rc || getstatus) { + pciehp_get_power_status(p_slot, &getstatus); + if (getstatus) { ctrl_info(ctrl, "Already enabled on slot(%s)\n", slot_name(p_slot)); return -EINVAL; @@ -508,43 +433,26 @@ pciehp_get_latch_status(p_slot, &getstatus); rc = board_added(p_slot); - if (rc) { + if (rc) pciehp_get_latch_status(p_slot, &getstatus); - } + return rc; } - +/* + * Note: This function must be called with slot->hotplug_lock held + */ int pciehp_disable_slot(struct slot *p_slot) { u8 getstatus = 0; - int ret = 0; struct controller *ctrl = p_slot->ctrl; if (!p_slot->ctrl) return 1; - if (!HP_SUPR_RM(p_slot->ctrl)) { - ret = pciehp_get_adapter_status(p_slot, &getstatus); - if (ret || !getstatus) { - ctrl_info(ctrl, "No adapter on slot(%s)\n", - slot_name(p_slot)); - return -ENODEV; - } - } - - if (MRL_SENS(p_slot->ctrl)) { - ret = pciehp_get_latch_status(p_slot, &getstatus); - if (ret || getstatus) { - ctrl_info(ctrl, "Latch open on slot(%s)\n", - slot_name(p_slot)); - return -ENODEV; - } - } - if (POWER_CTRL(p_slot->ctrl)) { - ret = pciehp_get_power_status(p_slot, &getstatus); - if (ret || !getstatus) { + pciehp_get_power_status(p_slot, &getstatus); + if (!getstatus) { ctrl_info(ctrl, "Already disabled on slot(%s)\n", slot_name(p_slot)); return -EINVAL; @@ -566,7 +474,9 @@ case STATIC_STATE: p_slot->state = POWERON_STATE; mutex_unlock(&p_slot->lock); + mutex_lock(&p_slot->hotplug_lock); retval = pciehp_enable_slot(p_slot); + mutex_unlock(&p_slot->hotplug_lock); mutex_lock(&p_slot->lock); p_slot->state = STATIC_STATE; break; @@ -580,8 +490,8 @@ slot_name(p_slot)); break; default: - ctrl_err(ctrl, "Not a valid state on slot %s\n", - slot_name(p_slot)); + ctrl_err(ctrl, "invalid state %#x on slot %s\n", + p_slot->state, slot_name(p_slot)); break; } mutex_unlock(&p_slot->lock); @@ -615,8 +525,8 @@ slot_name(p_slot)); break; default: - ctrl_err(ctrl, "Not a valid state on slot %s\n", - slot_name(p_slot)); + ctrl_err(ctrl, "invalid state %#x on slot %s\n", + p_slot->state, slot_name(p_slot)); break; } mutex_unlock(&p_slot->lock);