X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fpci%2Fhotplug%2Fpciehp_hpc.c;h=79f104963166bdce55420df4005966df49e0dcc6;hb=c54f9da1c8ceee19436430afac0798a989eb886d;hp=698975a6a21c712b46654cba06b71b641612a143;hpb=6f58d79598c184dc4f18ceddfaa5fa7483bce0a5;p=linux-2.6 diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 698975a6a2..79f1049631 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -221,14 +221,64 @@ static void start_int_poll_timer(struct controller *ctrl, int sec) add_timer(&ctrl->poll_timer); } -static inline int pcie_wait_cmd(struct controller *ctrl) +static inline int pciehp_request_irq(struct controller *ctrl) +{ + int retval, irq = ctrl->pci_dev->irq; + + /* Install interrupt polling timer. Start with 10 sec delay */ + if (pciehp_poll_mode) { + init_timer(&ctrl->poll_timer); + start_int_poll_timer(ctrl, 10); + return 0; + } + + /* Installs the interrupt handler */ + retval = request_irq(irq, pcie_isr, IRQF_SHARED, MY_NAME, ctrl); + if (retval) + err("Cannot get irq %d for the hotplug controller\n", irq); + return retval; +} + +static inline void pciehp_free_irq(struct controller *ctrl) +{ + if (pciehp_poll_mode) + del_timer_sync(&ctrl->poll_timer); + else + free_irq(ctrl->pci_dev->irq, ctrl); +} + +static inline int pcie_poll_cmd(struct controller *ctrl) +{ + u16 slot_status; + int timeout = 1000; + + if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status)) + if (slot_status & CMD_COMPLETED) + goto completed; + for (timeout = 1000; timeout > 0; timeout -= 100) { + msleep(100); + if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status)) + if (slot_status & CMD_COMPLETED) + goto completed; + } + return 0; /* timeout */ + +completed: + pciehp_writew(ctrl, SLOTSTATUS, CMD_COMPLETED); + return timeout; +} + +static inline int pcie_wait_cmd(struct controller *ctrl, int poll) { int retval = 0; unsigned int msecs = pciehp_poll_mode ? 2500 : 1000; unsigned long timeout = msecs_to_jiffies(msecs); int rc; - rc = wait_event_interruptible_timeout(ctrl->queue, + if (poll) + rc = pcie_poll_cmd(ctrl); + else + rc = wait_event_interruptible_timeout(ctrl->queue, !ctrl->cmd_busy, timeout); if (!rc) dbg("Command not completed in 1000 msec\n"); @@ -242,57 +292,81 @@ static inline int pcie_wait_cmd(struct controller *ctrl) /** * pcie_write_cmd - Issue controller command - * @slot: slot to which the command is issued + * @ctrl: controller to which the command is issued * @cmd: command value written to slot control register * @mask: bitmask of slot control register to be modified */ -static int pcie_write_cmd(struct slot *slot, u16 cmd, u16 mask) +static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) { - struct controller *ctrl = slot->ctrl; int retval = 0; u16 slot_status; u16 slot_ctrl; - unsigned long flags; mutex_lock(&ctrl->ctrl_lock); retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); if (retval) { - err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); + err("%s: Cannot read SLOTSTATUS register\n", __func__); goto out; } - if ((slot_status & CMD_COMPLETED) == CMD_COMPLETED ) { - /* After 1 sec and CMD_COMPLETED still not set, just - proceed forward to issue the next command according - to spec. Just print out the error message */ - dbg("%s: CMD_COMPLETED not clear after 1 sec.\n", - __FUNCTION__); + if (slot_status & CMD_COMPLETED) { + if (!ctrl->no_cmd_complete) { + /* + * After 1 sec and CMD_COMPLETED still not set, just + * proceed forward to issue the next command according + * to spec. Just print out the error message. + */ + dbg("%s: CMD_COMPLETED not clear after 1 sec.\n", + __func__); + } else if (!NO_CMD_CMPL(ctrl)) { + /* + * This controller semms to notify of command completed + * event even though it supports none of power + * controller, attention led, power led and EMI. + */ + dbg("%s: Unexpected CMD_COMPLETED. Need to wait for " + "command completed event.\n", __func__); + ctrl->no_cmd_complete = 0; + } else { + dbg("%s: Unexpected CMD_COMPLETED. Maybe the " + "controller is broken.\n", __func__); + } } - spin_lock_irqsave(&ctrl->lock, flags); retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); if (retval) { - err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); - goto out_spin_unlock; + err("%s: Cannot read SLOTCTRL register\n", __func__); + goto out; } slot_ctrl &= ~mask; - slot_ctrl |= ((cmd & mask) | CMD_CMPL_INTR_ENABLE); + slot_ctrl |= (cmd & mask); + /* Don't enable command completed if caller is changing it. */ + if (!(mask & CMD_CMPL_INTR_ENABLE)) + slot_ctrl |= CMD_CMPL_INTR_ENABLE; ctrl->cmd_busy = 1; + smp_mb(); retval = pciehp_writew(ctrl, SLOTCTRL, slot_ctrl); if (retval) - err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__); - - out_spin_unlock: - spin_unlock_irqrestore(&ctrl->lock, flags); + err("%s: Cannot write to SLOTCTRL register\n", __func__); /* * Wait for command completion. */ - if (!retval) - retval = pcie_wait_cmd(ctrl); + if (!retval && !ctrl->no_cmd_complete) { + int poll = 0; + /* + * if hotplug interrupt is not enabled or command + * completed interrupt is not enabled, we need to poll + * command completed event. + */ + if (!(slot_ctrl & HP_INTR_ENABLE) || + !(slot_ctrl & CMD_CMPL_INTR_ENABLE)) + poll = 1; + retval = pcie_wait_cmd(ctrl, poll); + } out: mutex_unlock(&ctrl->ctrl_lock); return retval; @@ -305,14 +379,14 @@ static int hpc_check_lnk_status(struct controller *ctrl) retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status); if (retval) { - err("%s: Cannot read LNKSTATUS register\n", __FUNCTION__); + err("%s: Cannot read LNKSTATUS register\n", __func__); return retval; } - dbg("%s: lnk_status = %x\n", __FUNCTION__, lnk_status); + dbg("%s: lnk_status = %x\n", __func__, lnk_status); if ( (lnk_status & LNK_TRN) || (lnk_status & LNK_TRN_ERR) || !(lnk_status & NEG_LINK_WD)) { - err("%s : Link Training Error occurs \n", __FUNCTION__); + err("%s : Link Training Error occurs \n", __func__); retval = -1; return retval; } @@ -329,12 +403,12 @@ static int hpc_get_attention_status(struct slot *slot, u8 *status) retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); if (retval) { - err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); + err("%s: Cannot read SLOTCTRL register\n", __func__); return retval; } dbg("%s: SLOTCTRL %x, value read %x\n", - __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_ctrl); + __func__, ctrl->cap_base + SLOTCTRL, slot_ctrl); atten_led_state = (slot_ctrl & ATTN_LED_CTRL) >> 6; @@ -368,11 +442,11 @@ static int hpc_get_power_status(struct slot *slot, u8 *status) retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); if (retval) { - err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); + err("%s: Cannot read SLOTCTRL register\n", __func__); return retval; } dbg("%s: SLOTCTRL %x value read %x\n", - __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_ctrl); + __func__, ctrl->cap_base + SLOTCTRL, slot_ctrl); pwr_state = (slot_ctrl & PWR_CTRL) >> 10; @@ -399,7 +473,7 @@ static int hpc_get_latch_status(struct slot *slot, u8 *status) retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); if (retval) { - err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); + err("%s: Cannot read SLOTSTATUS register\n", __func__); return retval; } @@ -417,7 +491,7 @@ static int hpc_get_adapter_status(struct slot *slot, u8 *status) retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); if (retval) { - err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); + err("%s: Cannot read SLOTSTATUS register\n", __func__); return retval; } card_state = (u8)((slot_status & PRSN_STATE) >> 6); @@ -435,7 +509,7 @@ static int hpc_query_power_fault(struct slot *slot) retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); if (retval) { - err("%s: Cannot check for power fault\n", __FUNCTION__); + err("%s: Cannot check for power fault\n", __func__); return retval; } pwr_fault = (u8)((slot_status & PWR_FAULT_DETECTED) >> 1); @@ -451,7 +525,7 @@ static int hpc_get_emi_status(struct slot *slot, u8 *status) retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); if (retval) { - err("%s : Cannot check EMI status\n", __FUNCTION__); + err("%s : Cannot check EMI status\n", __func__); return retval; } *status = (slot_status & EMI_STATE) >> EMI_STATUS_BIT; @@ -467,12 +541,7 @@ static int hpc_toggle_emi(struct slot *slot) slot_cmd = EMI_CTRL; cmd_mask = EMI_CTRL; - if (!pciehp_poll_mode) { - slot_cmd = slot_cmd | HP_INTR_ENABLE; - cmd_mask = cmd_mask | HP_INTR_ENABLE; - } - - rc = pcie_write_cmd(slot, slot_cmd, cmd_mask); + rc = pcie_write_cmd(slot->ctrl, slot_cmd, cmd_mask); slot->last_emi_toggle = get_seconds(); return rc; @@ -499,14 +568,9 @@ static int hpc_set_attention_status(struct slot *slot, u8 value) default: return -1; } - if (!pciehp_poll_mode) { - slot_cmd = slot_cmd | HP_INTR_ENABLE; - cmd_mask = cmd_mask | HP_INTR_ENABLE; - } - - rc = pcie_write_cmd(slot, slot_cmd, cmd_mask); + rc = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); dbg("%s: SLOTCTRL %x write cmd %x\n", - __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); return rc; } @@ -519,15 +583,9 @@ static void hpc_set_green_led_on(struct slot *slot) slot_cmd = 0x0100; cmd_mask = PWR_LED_CTRL; - if (!pciehp_poll_mode) { - slot_cmd = slot_cmd | HP_INTR_ENABLE; - cmd_mask = cmd_mask | HP_INTR_ENABLE; - } - - pcie_write_cmd(slot, slot_cmd, cmd_mask); - + pcie_write_cmd(ctrl, slot_cmd, cmd_mask); dbg("%s: SLOTCTRL %x write cmd %x\n", - __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); } static void hpc_set_green_led_off(struct slot *slot) @@ -538,14 +596,9 @@ static void hpc_set_green_led_off(struct slot *slot) slot_cmd = 0x0300; cmd_mask = PWR_LED_CTRL; - if (!pciehp_poll_mode) { - slot_cmd = slot_cmd | HP_INTR_ENABLE; - cmd_mask = cmd_mask | HP_INTR_ENABLE; - } - - pcie_write_cmd(slot, slot_cmd, cmd_mask); + pcie_write_cmd(ctrl, slot_cmd, cmd_mask); dbg("%s: SLOTCTRL %x write cmd %x\n", - __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); } static void hpc_set_green_led_blink(struct slot *slot) @@ -556,23 +609,19 @@ static void hpc_set_green_led_blink(struct slot *slot) slot_cmd = 0x0200; cmd_mask = PWR_LED_CTRL; - if (!pciehp_poll_mode) { - slot_cmd = slot_cmd | HP_INTR_ENABLE; - cmd_mask = cmd_mask | HP_INTR_ENABLE; - } - - pcie_write_cmd(slot, slot_cmd, cmd_mask); - + pcie_write_cmd(ctrl, slot_cmd, cmd_mask); dbg("%s: SLOTCTRL %x write cmd %x\n", - __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); } static void hpc_release_ctlr(struct controller *ctrl) { - if (pciehp_poll_mode) - del_timer(&ctrl->poll_timer); - else - free_irq(ctrl->pci_dev->irq, ctrl); + /* Mask Hot-plug Interrupt Enable */ + if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE)) + err("%s: Cannot mask hotplut interrupt enable\n", __func__); + + /* Free interrupt handler or interrupt polling timer */ + pciehp_free_irq(ctrl); /* * If this is the last controller to be released, destroy the @@ -590,12 +639,12 @@ static int hpc_power_on_slot(struct slot * slot) u16 slot_status; int retval = 0; - dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot); + dbg("%s: slot->hp_slot %x\n", __func__, slot->hp_slot); /* Clear sticky power-fault bit from previous power failures */ retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); if (retval) { - err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); + err("%s: Cannot read SLOTSTATUS register\n", __func__); return retval; } slot_status &= PWR_FAULT_DETECTED; @@ -603,7 +652,7 @@ static int hpc_power_on_slot(struct slot * slot) retval = pciehp_writew(ctrl, SLOTSTATUS, slot_status); if (retval) { err("%s: Cannot write to SLOTSTATUS register\n", - __FUNCTION__); + __func__); return retval; } } @@ -612,26 +661,20 @@ static int hpc_power_on_slot(struct slot * slot) cmd_mask = PWR_CTRL; /* Enable detection that we turned off at slot power-off time */ if (!pciehp_poll_mode) { - slot_cmd = slot_cmd | - PWR_FAULT_DETECT_ENABLE | - MRL_DETECT_ENABLE | - PRSN_DETECT_ENABLE | - HP_INTR_ENABLE; - cmd_mask = cmd_mask | - PWR_FAULT_DETECT_ENABLE | - MRL_DETECT_ENABLE | - PRSN_DETECT_ENABLE | - HP_INTR_ENABLE; + slot_cmd |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | + PRSN_DETECT_ENABLE); + cmd_mask |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | + PRSN_DETECT_ENABLE); } - retval = pcie_write_cmd(slot, slot_cmd, cmd_mask); + retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); if (retval) { - err("%s: Write %x command failed!\n", __FUNCTION__, slot_cmd); + err("%s: Write %x command failed!\n", __func__, slot_cmd); return -1; } dbg("%s: SLOTCTRL %x write cmd %x\n", - __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); return retval; } @@ -677,7 +720,7 @@ static int hpc_power_off_slot(struct slot * slot) int retval = 0; int changed; - dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot); + dbg("%s: slot->hp_slot %x\n", __func__, slot->hp_slot); /* * Set Bad DLLP Mask bit in Correctable Error Mask @@ -697,32 +740,20 @@ static int hpc_power_off_slot(struct slot * slot) * till the slot is powered on again. */ if (!pciehp_poll_mode) { - slot_cmd = (slot_cmd & - ~PWR_FAULT_DETECT_ENABLE & - ~MRL_DETECT_ENABLE & - ~PRSN_DETECT_ENABLE) | HP_INTR_ENABLE; - cmd_mask = cmd_mask | - PWR_FAULT_DETECT_ENABLE | - MRL_DETECT_ENABLE | - PRSN_DETECT_ENABLE | - HP_INTR_ENABLE; + slot_cmd &= ~(PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | + PRSN_DETECT_ENABLE); + cmd_mask |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | + PRSN_DETECT_ENABLE); } - retval = pcie_write_cmd(slot, slot_cmd, cmd_mask); + retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); if (retval) { - err("%s: Write command failed!\n", __FUNCTION__); + err("%s: Write command failed!\n", __func__); retval = -1; goto out; } dbg("%s: SLOTCTRL %x write cmd %x\n", - __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_cmd); - - /* - * After turning power off, we must wait for at least 1 second - * before taking any action that relies on power having been - * removed from the slot/adapter. - */ - msleep(1000); + __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); out: if (changed) pcie_unmask_bad_dllp(ctrl); @@ -733,139 +764,74 @@ static int hpc_power_off_slot(struct slot * slot) static irqreturn_t pcie_isr(int irq, void *dev_id) { struct controller *ctrl = (struct controller *)dev_id; - u16 slot_status, intr_detect, intr_loc; - u16 temp_word; - int hp_slot = 0; /* only 1 slot per PCI Express port */ - int rc = 0; - unsigned long flags; - - rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); - if (rc) { - err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); - return IRQ_NONE; - } - - intr_detect = (ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED | - MRL_SENS_CHANGED | PRSN_DETECT_CHANGED | CMD_COMPLETED); - - intr_loc = slot_status & intr_detect; - - /* Check to see if it was our interrupt */ - if ( !intr_loc ) - return IRQ_NONE; + u16 detected, intr_loc; + struct slot *p_slot; - dbg("%s: intr_loc %x\n", __FUNCTION__, intr_loc); - /* Mask Hot-plug Interrupt Enable */ - if (!pciehp_poll_mode) { - spin_lock_irqsave(&ctrl->lock, flags); - rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); - if (rc) { - err("%s: Cannot read SLOT_CTRL register\n", - __FUNCTION__); - spin_unlock_irqrestore(&ctrl->lock, flags); + /* + * In order to guarantee that all interrupt events are + * serviced, we need to re-inspect Slot Status register after + * clearing what is presumed to be the last pending interrupt. + */ + intr_loc = 0; + do { + if (pciehp_readw(ctrl, SLOTSTATUS, &detected)) { + err("%s: Cannot read SLOTSTATUS\n", __func__); return IRQ_NONE; } - dbg("%s: pciehp_readw(SLOTCTRL) with value %x\n", - __FUNCTION__, temp_word); - temp_word = (temp_word & ~HP_INTR_ENABLE & - ~CMD_CMPL_INTR_ENABLE) | 0x00; - rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); - if (rc) { - err("%s: Cannot write to SLOTCTRL register\n", - __FUNCTION__); - spin_unlock_irqrestore(&ctrl->lock, flags); + detected &= (ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED | + MRL_SENS_CHANGED | PRSN_DETECT_CHANGED | + CMD_COMPLETED); + intr_loc |= detected; + if (!intr_loc) return IRQ_NONE; - } - spin_unlock_irqrestore(&ctrl->lock, flags); - - rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); - if (rc) { - err("%s: Cannot read SLOT_STATUS register\n", - __FUNCTION__); + if (pciehp_writew(ctrl, SLOTSTATUS, detected)) { + err("%s: Cannot write to SLOTSTATUS\n", __func__); return IRQ_NONE; } - dbg("%s: pciehp_readw(SLOTSTATUS) with value %x\n", - __FUNCTION__, slot_status); + } while (detected); - /* Clear command complete interrupt caused by this write */ - temp_word = 0x1f; - rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); - if (rc) { - err("%s: Cannot write to SLOTSTATUS register\n", - __FUNCTION__); - return IRQ_NONE; - } - } + dbg("%s: intr_loc %x\n", __FUNCTION__, intr_loc); + /* Check Command Complete Interrupt Pending */ if (intr_loc & CMD_COMPLETED) { - /* - * Command Complete Interrupt Pending - */ ctrl->cmd_busy = 0; + smp_mb(); wake_up_interruptible(&ctrl->queue); } + if (!(intr_loc & ~CMD_COMPLETED)) + return IRQ_HANDLED; + + /* + * Return without handling events if this handler routine is + * called before controller initialization is done. This may + * happen if hotplug event or another interrupt that shares + * the IRQ with pciehp arrives before slot initialization is + * done after interrupt handler is registered. + * + * FIXME - Need more structural fixes. We need to be ready to + * handle the event before installing interrupt handler. + */ + p_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); + if (!p_slot || !p_slot->hpc_ops) + return IRQ_HANDLED; + + /* Check MRL Sensor Changed */ if (intr_loc & MRL_SENS_CHANGED) - pciehp_handle_switch_change(hp_slot, ctrl); + pciehp_handle_switch_change(p_slot); + /* Check Attention Button Pressed */ if (intr_loc & ATTN_BUTTN_PRESSED) - pciehp_handle_attention_button(hp_slot, ctrl); + pciehp_handle_attention_button(p_slot); + /* Check Presence Detect Changed */ if (intr_loc & PRSN_DETECT_CHANGED) - pciehp_handle_presence_change(hp_slot, ctrl); + pciehp_handle_presence_change(p_slot); + /* Check Power Fault Detected */ if (intr_loc & PWR_FAULT_DETECTED) - pciehp_handle_power_fault(hp_slot, ctrl); - - /* Clear all events after serving them */ - temp_word = 0x1F; - rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); - if (rc) { - err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__); - return IRQ_NONE; - } - /* Unmask Hot-plug Interrupt Enable */ - if (!pciehp_poll_mode) { - spin_lock_irqsave(&ctrl->lock, flags); - rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); - if (rc) { - err("%s: Cannot read SLOTCTRL register\n", - __FUNCTION__); - spin_unlock_irqrestore(&ctrl->lock, flags); - return IRQ_NONE; - } - - dbg("%s: Unmask Hot-plug Interrupt Enable\n", __FUNCTION__); - temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE; - - rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); - if (rc) { - err("%s: Cannot write to SLOTCTRL register\n", - __FUNCTION__); - spin_unlock_irqrestore(&ctrl->lock, flags); - return IRQ_NONE; - } - spin_unlock_irqrestore(&ctrl->lock, flags); - - rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); - if (rc) { - err("%s: Cannot read SLOT_STATUS register\n", - __FUNCTION__); - return IRQ_NONE; - } - - /* Clear command complete interrupt caused by this write */ - temp_word = 0x1F; - rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); - if (rc) { - err("%s: Cannot write to SLOTSTATUS failed\n", - __FUNCTION__); - return IRQ_NONE; - } - dbg("%s: pciehp_writew(SLOTSTATUS) with value %x\n", - __FUNCTION__, temp_word); - } + pciehp_handle_power_fault(p_slot); return IRQ_HANDLED; } @@ -879,7 +845,7 @@ static int hpc_get_max_lnk_speed(struct slot *slot, enum pci_bus_speed *value) retval = pciehp_readl(ctrl, LNKCAP, &lnk_cap); if (retval) { - err("%s: Cannot read LNKCAP register\n", __FUNCTION__); + err("%s: Cannot read LNKCAP register\n", __func__); return retval; } @@ -908,7 +874,7 @@ static int hpc_get_max_lnk_width(struct slot *slot, retval = pciehp_readl(ctrl, LNKCAP, &lnk_cap); if (retval) { - err("%s: Cannot read LNKCAP register\n", __FUNCTION__); + err("%s: Cannot read LNKCAP register\n", __func__); return retval; } @@ -957,7 +923,7 @@ static int hpc_get_cur_lnk_speed(struct slot *slot, enum pci_bus_speed *value) retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status); if (retval) { - err("%s: Cannot read LNKSTATUS register\n", __FUNCTION__); + err("%s: Cannot read LNKSTATUS register\n", __func__); return retval; } @@ -986,7 +952,7 @@ static int hpc_get_cur_lnk_width(struct slot *slot, retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status); if (retval) { - err("%s: Cannot read LNKSTATUS register\n", __FUNCTION__); + err("%s: Cannot read LNKSTATUS register\n", __func__); return retval; } @@ -1052,7 +1018,7 @@ static struct hpc_ops pciehp_hpc_ops = { }; #ifdef CONFIG_ACPI -int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev) +static int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev) { acpi_status status; acpi_handle chandle, handle = DEVICE_ACPI_HANDLE(&(dev->dev)); @@ -1112,7 +1078,7 @@ int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev) break; } - err("Cannot get control of hotplug hardware for pci %s\n", + dbg("Cannot get control of hotplug hardware for pci %s\n", pci_name(dev)); kfree(string.pointer); @@ -1123,45 +1089,15 @@ int pciehp_acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev) static int pcie_init_hardware_part1(struct controller *ctrl, struct pcie_device *dev) { - int rc; - u16 temp_word; - u32 slot_cap; - u16 slot_status; - - rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap); - if (rc) { - err("%s: Cannot read SLOTCAP register\n", __FUNCTION__); + /* Clear all remaining event bits in Slot Status register */ + if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) { + err("%s: Cannot write to SLOTSTATUS register\n", __func__); return -1; } /* Mask Hot-plug Interrupt Enable */ - rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); - if (rc) { - err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); - return -1; - } - - dbg("%s: SLOTCTRL %x value read %x\n", - __FUNCTION__, ctrl->cap_base + SLOTCTRL, temp_word); - temp_word = (temp_word & ~HP_INTR_ENABLE & ~CMD_CMPL_INTR_ENABLE) | - 0x00; - - rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); - if (rc) { - err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__); - return -1; - } - - rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); - if (rc) { - err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); - return -1; - } - - temp_word = 0x1F; /* Clear all events */ - rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); - if (rc) { - err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__); + if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE)) { + err("%s: Cannot mask hotplug interrupt enable\n", __func__); return -1; } return 0; @@ -1169,205 +1105,125 @@ static int pcie_init_hardware_part1(struct controller *ctrl, int pcie_init_hardware_part2(struct controller *ctrl, struct pcie_device *dev) { - int rc; - u16 temp_word; - u16 intr_enable = 0; - u32 slot_cap; - u16 slot_status; - - rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); - if (rc) { - err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); - goto abort; - } - - intr_enable = intr_enable | PRSN_DETECT_ENABLE; - - rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap); - if (rc) { - err("%s: Cannot read SLOTCAP register\n", __FUNCTION__); - goto abort; - } - - if (ATTN_BUTTN(slot_cap)) - intr_enable = intr_enable | ATTN_BUTTN_ENABLE; - - if (POWER_CTRL(slot_cap)) - intr_enable = intr_enable | PWR_FAULT_DETECT_ENABLE; - - if (MRL_SENS(slot_cap)) - intr_enable = intr_enable | MRL_DETECT_ENABLE; - - temp_word = (temp_word & ~intr_enable) | intr_enable; - - if (pciehp_poll_mode) { - temp_word = (temp_word & ~HP_INTR_ENABLE) | 0x0; - } else { - temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE; - } - - /* - * Unmask Hot-plug Interrupt Enable for the interrupt - * notification mechanism case. - */ - rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); - if (rc) { - err("%s: Cannot write to SLOTCTRL register\n", __FUNCTION__); + u16 cmd, mask; + + cmd = PRSN_DETECT_ENABLE; + if (ATTN_BUTTN(ctrl)) + cmd |= ATTN_BUTTN_ENABLE; + if (POWER_CTRL(ctrl)) + cmd |= PWR_FAULT_DETECT_ENABLE; + if (MRL_SENS(ctrl)) + cmd |= MRL_DETECT_ENABLE; + if (!pciehp_poll_mode) + cmd |= HP_INTR_ENABLE; + + mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | + PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | HP_INTR_ENABLE; + + if (pcie_write_cmd(ctrl, cmd, mask)) { + err("%s: Cannot enable software notification\n", __func__); goto abort; } - rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); - if (rc) { - err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); - goto abort_disable_intr; - } - - temp_word = 0x1F; /* Clear all events */ - rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); - if (rc) { - err("%s: Cannot write to SLOTSTATUS register\n", __FUNCTION__); - goto abort_disable_intr; - } - if (pciehp_force) { + if (pciehp_force) dbg("Bypassing BIOS check for pciehp use on %s\n", pci_name(ctrl->pci_dev)); - } else { - rc = pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev); - if (rc) - goto abort_disable_intr; - } + else if (pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev)) + goto abort_disable_intr; return 0; /* We end up here for the many possible ways to fail this API. */ abort_disable_intr: - rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); - if (!rc) { - temp_word &= ~(intr_enable | HP_INTR_ENABLE); - rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); - } - if (rc) - err("%s : disabling interrupts failed\n", __FUNCTION__); + if (pcie_write_cmd(ctrl, 0, HP_INTR_ENABLE)) + err("%s : disabling interrupts failed\n", __func__); abort: return -1; } -int pcie_init(struct controller *ctrl, struct pcie_device *dev) +static inline void dbg_ctrl(struct controller *ctrl) { - int rc; - u16 cap_reg; - u32 slot_cap; - int cap_base; - u16 slot_status, slot_ctrl; - struct pci_dev *pdev; - - pdev = dev->port; - ctrl->pci_dev = pdev; /* save pci_dev in context */ - - dbg("%s: hotplug controller vendor id 0x%x device id 0x%x\n", - __FUNCTION__, pdev->vendor, pdev->device); - - cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP); - if (cap_base == 0) { - dbg("%s: Can't find PCI_CAP_ID_EXP (0x10)\n", __FUNCTION__); - goto abort; - } - - ctrl->cap_base = cap_base; + int i; + u16 reg16; + struct pci_dev *pdev = ctrl->pci_dev; - dbg("%s: pcie_cap_base %x\n", __FUNCTION__, cap_base); + if (!pciehp_debug) + return; - rc = pciehp_readw(ctrl, CAPREG, &cap_reg); - if (rc) { - err("%s: Cannot read CAPREG register\n", __FUNCTION__); - goto abort; - } - dbg("%s: CAPREG offset %x cap_reg %x\n", - __FUNCTION__, ctrl->cap_base + CAPREG, cap_reg); - - if (((cap_reg & SLOT_IMPL) == 0) || - (((cap_reg & DEV_PORT_TYPE) != 0x0040) - && ((cap_reg & DEV_PORT_TYPE) != 0x0060))) { - dbg("%s : This is not a root port or the port is not " - "connected to a slot\n", __FUNCTION__); - goto abort; - } + dbg("Hotplug Controller:\n"); + dbg(" Seg/Bus/Dev/Func/IRQ : %s IRQ %d\n", pci_name(pdev), pdev->irq); + dbg(" Vendor ID : 0x%04x\n", pdev->vendor); + dbg(" Device ID : 0x%04x\n", pdev->device); + dbg(" Subsystem ID : 0x%04x\n", pdev->subsystem_device); + dbg(" Subsystem Vendor ID : 0x%04x\n", pdev->subsystem_vendor); + dbg(" PCIe Cap offset : 0x%02x\n", ctrl->cap_base); + for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) { + if (!pci_resource_len(pdev, i)) + continue; + dbg(" PCI resource [%d] : 0x%llx@0x%llx\n", i, + (unsigned long long)pci_resource_len(pdev, i), + (unsigned long long)pci_resource_start(pdev, i)); + } + dbg("Slot Capabilities : 0x%08x\n", ctrl->slot_cap); + dbg(" Physical Slot Number : %d\n", ctrl->first_slot); + dbg(" Attention Button : %3s\n", ATTN_BUTTN(ctrl) ? "yes" : "no"); + dbg(" Power Controller : %3s\n", POWER_CTRL(ctrl) ? "yes" : "no"); + dbg(" MRL Sensor : %3s\n", MRL_SENS(ctrl) ? "yes" : "no"); + dbg(" Attention Indicator : %3s\n", ATTN_LED(ctrl) ? "yes" : "no"); + dbg(" Power Indicator : %3s\n", PWR_LED(ctrl) ? "yes" : "no"); + dbg(" Hot-Plug Surprise : %3s\n", HP_SUPR_RM(ctrl) ? "yes" : "no"); + dbg(" EMI Present : %3s\n", EMI(ctrl) ? "yes" : "no"); + dbg(" Comamnd Completed : %3s\n", NO_CMD_CMPL(ctrl)? "no" : "yes"); + pciehp_readw(ctrl, SLOTSTATUS, ®16); + dbg("Slot Status : 0x%04x\n", reg16); + pciehp_readw(ctrl, SLOTSTATUS, ®16); + dbg("Slot Control : 0x%04x\n", reg16); +} - rc = pciehp_readl(ctrl, SLOTCAP, &slot_cap); - if (rc) { - err("%s: Cannot read SLOTCAP register\n", __FUNCTION__); - goto abort; - } - dbg("%s: SLOTCAP offset %x slot_cap %x\n", - __FUNCTION__, ctrl->cap_base + SLOTCAP, slot_cap); +int pcie_init(struct controller *ctrl, struct pcie_device *dev) +{ + u32 slot_cap; + struct pci_dev *pdev = dev->port; - if (!(slot_cap & HP_CAP)) { - dbg("%s : This slot is not hot-plug capable\n", __FUNCTION__); - goto abort; - } - /* For debugging purpose */ - rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); - if (rc) { - err("%s: Cannot read SLOTSTATUS register\n", __FUNCTION__); + ctrl->pci_dev = pdev; + ctrl->cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP); + if (!ctrl->cap_base) { + err("%s: Cannot find PCI Express capability\n", __func__); goto abort; } - dbg("%s: SLOTSTATUS offset %x slot_status %x\n", - __FUNCTION__, ctrl->cap_base + SLOTSTATUS, slot_status); - - rc = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); - if (rc) { - err("%s: Cannot read SLOTCTRL register\n", __FUNCTION__); + if (pciehp_readl(ctrl, SLOTCAP, &slot_cap)) { + err("%s: Cannot read SLOTCAP register\n", __func__); goto abort; } - dbg("%s: SLOTCTRL offset %x slot_ctrl %x\n", - __FUNCTION__, ctrl->cap_base + SLOTCTRL, slot_ctrl); - - for (rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++) - if (pci_resource_len(pdev, rc) > 0) - dbg("pci resource[%d] start=0x%llx(len=0x%llx)\n", rc, - (unsigned long long)pci_resource_start(pdev, rc), - (unsigned long long)pci_resource_len(pdev, rc)); - - info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", - pdev->vendor, pdev->device, - pdev->subsystem_vendor, pdev->subsystem_device); + ctrl->slot_cap = slot_cap; + ctrl->first_slot = slot_cap >> 19; + ctrl->slot_device_offset = 0; + ctrl->num_slots = 1; + ctrl->hpc_ops = &pciehp_hpc_ops; mutex_init(&ctrl->crit_sect); mutex_init(&ctrl->ctrl_lock); - spin_lock_init(&ctrl->lock); - - /* setup wait queue */ init_waitqueue_head(&ctrl->queue); + dbg_ctrl(ctrl); + /* + * Controller doesn't notify of command completion if the "No + * Command Completed Support" bit is set in Slot Capability + * register or the controller supports none of power + * controller, attention led, power led and EMI. + */ + if (NO_CMD_CMPL(ctrl) || + !(POWER_CTRL(ctrl) | ATTN_LED(ctrl) | PWR_LED(ctrl) | EMI(ctrl))) + ctrl->no_cmd_complete = 1; - /* return PCI Controller Info */ - ctrl->slot_device_offset = 0; - ctrl->num_slots = 1; - ctrl->first_slot = slot_cap >> 19; - ctrl->ctrlcap = slot_cap & 0x0000007f; + info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", + pdev->vendor, pdev->device, + pdev->subsystem_vendor, pdev->subsystem_device); - rc = pcie_init_hardware_part1(ctrl, dev); - if (rc) + if (pcie_init_hardware_part1(ctrl, dev)) goto abort; - if (pciehp_poll_mode) { - /* Install interrupt polling timer. Start with 10 sec delay */ - init_timer(&ctrl->poll_timer); - start_int_poll_timer(ctrl, 10); - } else { - /* Installs the interrupt handler */ - rc = request_irq(ctrl->pci_dev->irq, pcie_isr, IRQF_SHARED, - MY_NAME, (void *)ctrl); - dbg("%s: request_irq %d for hpc%d (returns %d)\n", - __FUNCTION__, ctrl->pci_dev->irq, - atomic_read(&pciehp_num_controllers), rc); - if (rc) { - err("Can't get irq %d for the hotplug controller\n", - ctrl->pci_dev->irq); - goto abort; - } - } - dbg("pciehp ctrl b:d:f:irq=0x%x:%x:%x:%x\n", pdev->bus->number, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), dev->irq); + if (pciehp_request_irq(ctrl)) + goto abort; /* * If this is the first controller to be initialized, @@ -1376,21 +1232,17 @@ int pcie_init(struct controller *ctrl, struct pcie_device *dev) if (atomic_add_return(1, &pciehp_num_controllers) == 1) { pciehp_wq = create_singlethread_workqueue("pciehpd"); if (!pciehp_wq) { - rc = -ENOMEM; goto abort_free_irq; } } - rc = pcie_init_hardware_part2(ctrl, dev); - if (rc == 0) { - ctrl->hpc_ops = &pciehp_hpc_ops; - return 0; - } + if (pcie_init_hardware_part2(ctrl, dev)) + goto abort_free_irq; + + return 0; + abort_free_irq: - if (pciehp_poll_mode) - del_timer_sync(&ctrl->poll_timer); - else - free_irq(ctrl->pci_dev->irq, ctrl); + pciehp_free_irq(ctrl); abort: return -1; }