diff options
author | Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> | 2008-04-25 17:38:57 -0400 |
---|---|---|
committer | Jesse Barnes <jbarnes@virtuousgeek.org> | 2008-04-25 17:38:57 -0400 |
commit | c6b069e94601aea8887afbbd922afe20a3580a7d (patch) | |
tree | 09c1323f36040f039d3b260e794bcc3c4e16d4f1 | |
parent | 3800345f723fd130d50434d4717b99d4a9f383c8 (diff) |
pciehp: Fix interrupt event handlig
Current pciehp implementation disables and re-enables hotplug interrupts
in its interrupt handler. This operation might be intend to guarantee
that interrupts for the events newly occured during previous events are
being handled will be successfully generated. But current implementaion
has the following prolems.
- Current interrupt service routin clears status changes without
waiting command completion. Because of this, events might not be
cleared properly.
- Current interrupt service routine clears status changes caused by
disabling or enabling hotplug interrupts itself. This will lose new
events that occurs during previous interrupts are being handled.
- Current implementation doesn't have any serialization mechanism
between the code to wait for command completion and the interrupt
handler that clears the command completion events caused by itself.
There is clearly race conditions between them, and it may cause
the problem that waiting for command completion doesn't work for
example.
To fix those problems, this patch stops disabling/re-enabling hotplug
interrupts in interrupt service routine. Instead of this, this patch
re-inspects Slot Status register after clearing what is presumed to
be the last bending interrupt in order to guarantee that all interrupt
events are serviced.
Signed-off-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
Signed-off-by: Kristen Carlson Accardi <kristen.c.accardi@intel.com>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r-- | drivers/pci/hotplug/pciehp.h | 1 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 148 |
2 files changed, 29 insertions, 120 deletions
diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index f14267e197dd..9dd132925ffa 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h | |||
@@ -97,7 +97,6 @@ struct controller { | |||
97 | u8 cap_base; | 97 | u8 cap_base; |
98 | struct timer_list poll_timer; | 98 | struct timer_list poll_timer; |
99 | volatile int cmd_busy; | 99 | volatile int cmd_busy; |
100 | spinlock_t lock; | ||
101 | }; | 100 | }; |
102 | 101 | ||
103 | #define INT_BUTTON_IGNORE 0 | 102 | #define INT_BUTTON_IGNORE 0 |
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index b4bbd07d1e39..51a5055f6965 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c | |||
@@ -252,7 +252,6 @@ static int pcie_write_cmd(struct slot *slot, u16 cmd, u16 mask) | |||
252 | int retval = 0; | 252 | int retval = 0; |
253 | u16 slot_status; | 253 | u16 slot_status; |
254 | u16 slot_ctrl; | 254 | u16 slot_ctrl; |
255 | unsigned long flags; | ||
256 | 255 | ||
257 | mutex_lock(&ctrl->ctrl_lock); | 256 | mutex_lock(&ctrl->ctrl_lock); |
258 | 257 | ||
@@ -270,11 +269,10 @@ static int pcie_write_cmd(struct slot *slot, u16 cmd, u16 mask) | |||
270 | __func__); | 269 | __func__); |
271 | } | 270 | } |
272 | 271 | ||
273 | spin_lock_irqsave(&ctrl->lock, flags); | ||
274 | retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); | 272 | retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); |
275 | if (retval) { | 273 | if (retval) { |
276 | err("%s: Cannot read SLOTCTRL register\n", __func__); | 274 | err("%s: Cannot read SLOTCTRL register\n", __func__); |
277 | goto out_spin_unlock; | 275 | goto out; |
278 | } | 276 | } |
279 | 277 | ||
280 | slot_ctrl &= ~mask; | 278 | slot_ctrl &= ~mask; |
@@ -285,9 +283,6 @@ static int pcie_write_cmd(struct slot *slot, u16 cmd, u16 mask) | |||
285 | if (retval) | 283 | if (retval) |
286 | err("%s: Cannot write to SLOTCTRL register\n", __func__); | 284 | err("%s: Cannot write to SLOTCTRL register\n", __func__); |
287 | 285 | ||
288 | out_spin_unlock: | ||
289 | spin_unlock_irqrestore(&ctrl->lock, flags); | ||
290 | |||
291 | /* | 286 | /* |
292 | * Wait for command completion. | 287 | * Wait for command completion. |
293 | */ | 288 | */ |
@@ -733,139 +728,55 @@ static int hpc_power_off_slot(struct slot * slot) | |||
733 | static irqreturn_t pcie_isr(int irq, void *dev_id) | 728 | static irqreturn_t pcie_isr(int irq, void *dev_id) |
734 | { | 729 | { |
735 | struct controller *ctrl = (struct controller *)dev_id; | 730 | struct controller *ctrl = (struct controller *)dev_id; |
736 | u16 slot_status, intr_detect, intr_loc; | 731 | u16 detected, intr_loc; |
737 | u16 temp_word; | ||
738 | int hp_slot = 0; /* only 1 slot per PCI Express port */ | ||
739 | int rc = 0; | ||
740 | unsigned long flags; | ||
741 | |||
742 | rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); | ||
743 | if (rc) { | ||
744 | err("%s: Cannot read SLOTSTATUS register\n", __func__); | ||
745 | return IRQ_NONE; | ||
746 | } | ||
747 | 732 | ||
748 | intr_detect = (ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED | | 733 | /* |
749 | MRL_SENS_CHANGED | PRSN_DETECT_CHANGED | CMD_COMPLETED); | 734 | * In order to guarantee that all interrupt events are |
750 | 735 | * serviced, we need to re-inspect Slot Status register after | |
751 | intr_loc = slot_status & intr_detect; | 736 | * clearing what is presumed to be the last pending interrupt. |
752 | 737 | */ | |
753 | /* Check to see if it was our interrupt */ | 738 | intr_loc = 0; |
754 | if ( !intr_loc ) | 739 | do { |
755 | return IRQ_NONE; | 740 | if (pciehp_readw(ctrl, SLOTSTATUS, &detected)) { |
756 | 741 | err("%s: Cannot read SLOTSTATUS\n", __func__); | |
757 | dbg("%s: intr_loc %x\n", __func__, intr_loc); | ||
758 | /* Mask Hot-plug Interrupt Enable */ | ||
759 | if (!pciehp_poll_mode) { | ||
760 | spin_lock_irqsave(&ctrl->lock, flags); | ||
761 | rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); | ||
762 | if (rc) { | ||
763 | err("%s: Cannot read SLOT_CTRL register\n", | ||
764 | __func__); | ||
765 | spin_unlock_irqrestore(&ctrl->lock, flags); | ||
766 | return IRQ_NONE; | 742 | return IRQ_NONE; |
767 | } | 743 | } |
768 | 744 | ||
769 | dbg("%s: pciehp_readw(SLOTCTRL) with value %x\n", | 745 | detected &= (ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED | |
770 | __func__, temp_word); | 746 | MRL_SENS_CHANGED | PRSN_DETECT_CHANGED | |
771 | temp_word = (temp_word & ~HP_INTR_ENABLE & | 747 | CMD_COMPLETED); |
772 | ~CMD_CMPL_INTR_ENABLE) | 0x00; | 748 | intr_loc |= detected; |
773 | rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); | 749 | if (!intr_loc) |
774 | if (rc) { | ||
775 | err("%s: Cannot write to SLOTCTRL register\n", | ||
776 | __func__); | ||
777 | spin_unlock_irqrestore(&ctrl->lock, flags); | ||
778 | return IRQ_NONE; | 750 | return IRQ_NONE; |
779 | } | 751 | if (pciehp_writew(ctrl, SLOTSTATUS, detected)) { |
780 | spin_unlock_irqrestore(&ctrl->lock, flags); | 752 | err("%s: Cannot write to SLOTSTATUS\n", __func__); |
781 | |||
782 | rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); | ||
783 | if (rc) { | ||
784 | err("%s: Cannot read SLOT_STATUS register\n", | ||
785 | __func__); | ||
786 | return IRQ_NONE; | 753 | return IRQ_NONE; |
787 | } | 754 | } |
788 | dbg("%s: pciehp_readw(SLOTSTATUS) with value %x\n", | 755 | } while (detected); |
789 | __func__, slot_status); | ||
790 | 756 | ||
791 | /* Clear command complete interrupt caused by this write */ | 757 | dbg("%s: intr_loc %x\n", __FUNCTION__, intr_loc); |
792 | temp_word = 0x1f; | ||
793 | rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); | ||
794 | if (rc) { | ||
795 | err("%s: Cannot write to SLOTSTATUS register\n", | ||
796 | __func__); | ||
797 | return IRQ_NONE; | ||
798 | } | ||
799 | } | ||
800 | 758 | ||
759 | /* Check Command Complete Interrupt Pending */ | ||
801 | if (intr_loc & CMD_COMPLETED) { | 760 | if (intr_loc & CMD_COMPLETED) { |
802 | /* | ||
803 | * Command Complete Interrupt Pending | ||
804 | */ | ||
805 | ctrl->cmd_busy = 0; | 761 | ctrl->cmd_busy = 0; |
806 | wake_up_interruptible(&ctrl->queue); | 762 | wake_up_interruptible(&ctrl->queue); |
807 | } | 763 | } |
808 | 764 | ||
765 | /* Check MRL Sensor Changed */ | ||
809 | if (intr_loc & MRL_SENS_CHANGED) | 766 | if (intr_loc & MRL_SENS_CHANGED) |
810 | pciehp_handle_switch_change(hp_slot, ctrl); | 767 | pciehp_handle_switch_change(0, ctrl); |
811 | 768 | ||
769 | /* Check Attention Button Pressed */ | ||
812 | if (intr_loc & ATTN_BUTTN_PRESSED) | 770 | if (intr_loc & ATTN_BUTTN_PRESSED) |
813 | pciehp_handle_attention_button(hp_slot, ctrl); | 771 | pciehp_handle_attention_button(0, ctrl); |
814 | 772 | ||
773 | /* Check Presence Detect Changed */ | ||
815 | if (intr_loc & PRSN_DETECT_CHANGED) | 774 | if (intr_loc & PRSN_DETECT_CHANGED) |
816 | pciehp_handle_presence_change(hp_slot, ctrl); | 775 | pciehp_handle_presence_change(0, ctrl); |
817 | 776 | ||
777 | /* Check Power Fault Detected */ | ||
818 | if (intr_loc & PWR_FAULT_DETECTED) | 778 | if (intr_loc & PWR_FAULT_DETECTED) |
819 | pciehp_handle_power_fault(hp_slot, ctrl); | 779 | pciehp_handle_power_fault(0, ctrl); |
820 | |||
821 | /* Clear all events after serving them */ | ||
822 | temp_word = 0x1F; | ||
823 | rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); | ||
824 | if (rc) { | ||
825 | err("%s: Cannot write to SLOTSTATUS register\n", __func__); | ||
826 | return IRQ_NONE; | ||
827 | } | ||
828 | /* Unmask Hot-plug Interrupt Enable */ | ||
829 | if (!pciehp_poll_mode) { | ||
830 | spin_lock_irqsave(&ctrl->lock, flags); | ||
831 | rc = pciehp_readw(ctrl, SLOTCTRL, &temp_word); | ||
832 | if (rc) { | ||
833 | err("%s: Cannot read SLOTCTRL register\n", | ||
834 | __func__); | ||
835 | spin_unlock_irqrestore(&ctrl->lock, flags); | ||
836 | return IRQ_NONE; | ||
837 | } | ||
838 | |||
839 | dbg("%s: Unmask Hot-plug Interrupt Enable\n", __func__); | ||
840 | temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE; | ||
841 | |||
842 | rc = pciehp_writew(ctrl, SLOTCTRL, temp_word); | ||
843 | if (rc) { | ||
844 | err("%s: Cannot write to SLOTCTRL register\n", | ||
845 | __func__); | ||
846 | spin_unlock_irqrestore(&ctrl->lock, flags); | ||
847 | return IRQ_NONE; | ||
848 | } | ||
849 | spin_unlock_irqrestore(&ctrl->lock, flags); | ||
850 | |||
851 | rc = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); | ||
852 | if (rc) { | ||
853 | err("%s: Cannot read SLOT_STATUS register\n", | ||
854 | __func__); | ||
855 | return IRQ_NONE; | ||
856 | } | ||
857 | |||
858 | /* Clear command complete interrupt caused by this write */ | ||
859 | temp_word = 0x1F; | ||
860 | rc = pciehp_writew(ctrl, SLOTSTATUS, temp_word); | ||
861 | if (rc) { | ||
862 | err("%s: Cannot write to SLOTSTATUS failed\n", | ||
863 | __func__); | ||
864 | return IRQ_NONE; | ||
865 | } | ||
866 | dbg("%s: pciehp_writew(SLOTSTATUS) with value %x\n", | ||
867 | __func__, temp_word); | ||
868 | } | ||
869 | 780 | ||
870 | return IRQ_HANDLED; | 781 | return IRQ_HANDLED; |
871 | } | 782 | } |
@@ -1334,7 +1245,6 @@ int pcie_init(struct controller *ctrl, struct pcie_device *dev) | |||
1334 | 1245 | ||
1335 | mutex_init(&ctrl->crit_sect); | 1246 | mutex_init(&ctrl->crit_sect); |
1336 | mutex_init(&ctrl->ctrl_lock); | 1247 | mutex_init(&ctrl->ctrl_lock); |
1337 | spin_lock_init(&ctrl->lock); | ||
1338 | 1248 | ||
1339 | /* setup wait queue */ | 1249 | /* setup wait queue */ |
1340 | init_waitqueue_head(&ctrl->queue); | 1250 | init_waitqueue_head(&ctrl->queue); |