diff options
author | Mikael Pettersson <mikpe@it.uu.se> | 2007-07-02 19:09:05 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2007-07-10 21:14:19 -0400 |
commit | a77720ad0a4049e4bc6355e4febf899966a48222 (patch) | |
tree | 74cbffa3159ce4aa23a76930ac710612b9ca9983 /drivers | |
parent | 15ce09432a4399e61b57f2ceb564522d6534c15f (diff) |
sata_promise: SATA hotplug support, take 2
This patch enables hotplugging of SATA devices in the
sata_promise driver. It's been tested successfully on
both first- and second-generation Promise SATA chips:
SATA150 TX2plus, SATAII150 TX2plus, SATAII150 TX4,
SATA300 TX2plus, and SATA300 TX4.
The only quirk I've seen is that hotplugging (insertion)
on the first-generation SATA150 TX2plus requires a lengthier
EH sequence than on the second-generation chips.
On the second-generation chips a simple soft reset seems
to suffice, but on the first-generation chip there's a
"port is slow to respond" after the initial soft reset,
after which libata issues a hard reset, and then the
device is recognised.
The hotplug checks are high up in the interrupt handling
path, not deep down in error_intr as in ahci/sata_sil24.
That's because the chip doesn't signal hotplug status changes
in the per-port status register: instead a global register
contains hotplug control and status flags for all ports.
I considered following the ahci/sata_sil24 structure, but
that would have required non-trivial changes to the interrupt
handling path, so I chose to keep the hotplug changes simple
and unobtrusive.
Signed-off-by: Mikael Pettersson <mikpe@it.uu.se>
--
This patch depends on the "sata_promise: cleanups" patch.
Changes since the previous version (posted June 19):
- Correct pdc_interrupt() to increment 'handled' also in
the hotplug case. This prevents IRQ_NONE from being
returned when an interrupt only has hotplug events to
handle, which could confuse the kernel's IRQ machinery.
- Added testing on the SATAII150 TX4.
drivers/ata/sata_promise.c | 41 ++++++++++++++++++++++++++++++++++++-----
1 files changed, 36 insertions(+), 5 deletions(-)
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ata/sata_promise.c | 41 |
1 files changed, 36 insertions, 5 deletions
diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index 2ad5872fe90c..d2fcb9a6bec2 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c | |||
@@ -45,7 +45,7 @@ | |||
45 | #include "sata_promise.h" | 45 | #include "sata_promise.h" |
46 | 46 | ||
47 | #define DRV_NAME "sata_promise" | 47 | #define DRV_NAME "sata_promise" |
48 | #define DRV_VERSION "2.08" | 48 | #define DRV_VERSION "2.09" |
49 | 49 | ||
50 | enum { | 50 | enum { |
51 | PDC_MAX_PORTS = 4, | 51 | PDC_MAX_PORTS = 4, |
@@ -716,6 +716,9 @@ static irqreturn_t pdc_interrupt (int irq, void *dev_instance) | |||
716 | unsigned int i, tmp; | 716 | unsigned int i, tmp; |
717 | unsigned int handled = 0; | 717 | unsigned int handled = 0; |
718 | void __iomem *mmio_base; | 718 | void __iomem *mmio_base; |
719 | unsigned int hotplug_offset, ata_no; | ||
720 | u32 hotplug_status; | ||
721 | int is_sataii_tx4; | ||
719 | 722 | ||
720 | VPRINTK("ENTER\n"); | 723 | VPRINTK("ENTER\n"); |
721 | 724 | ||
@@ -726,10 +729,20 @@ static irqreturn_t pdc_interrupt (int irq, void *dev_instance) | |||
726 | 729 | ||
727 | mmio_base = host->iomap[PDC_MMIO_BAR]; | 730 | mmio_base = host->iomap[PDC_MMIO_BAR]; |
728 | 731 | ||
732 | /* read and clear hotplug flags for all ports */ | ||
733 | if (host->ports[0]->flags & PDC_FLAG_GEN_II) | ||
734 | hotplug_offset = PDC2_SATA_PLUG_CSR; | ||
735 | else | ||
736 | hotplug_offset = PDC_SATA_PLUG_CSR; | ||
737 | hotplug_status = readl(mmio_base + hotplug_offset); | ||
738 | if (hotplug_status & 0xff) | ||
739 | writel(hotplug_status | 0xff, mmio_base + hotplug_offset); | ||
740 | hotplug_status &= 0xff; /* clear uninteresting bits */ | ||
741 | |||
729 | /* reading should also clear interrupts */ | 742 | /* reading should also clear interrupts */ |
730 | mask = readl(mmio_base + PDC_INT_SEQMASK); | 743 | mask = readl(mmio_base + PDC_INT_SEQMASK); |
731 | 744 | ||
732 | if (mask == 0xffffffff) { | 745 | if (mask == 0xffffffff && hotplug_status == 0) { |
733 | VPRINTK("QUICK EXIT 2\n"); | 746 | VPRINTK("QUICK EXIT 2\n"); |
734 | return IRQ_NONE; | 747 | return IRQ_NONE; |
735 | } | 748 | } |
@@ -737,16 +750,34 @@ static irqreturn_t pdc_interrupt (int irq, void *dev_instance) | |||
737 | spin_lock(&host->lock); | 750 | spin_lock(&host->lock); |
738 | 751 | ||
739 | mask &= 0xffff; /* only 16 tags possible */ | 752 | mask &= 0xffff; /* only 16 tags possible */ |
740 | if (!mask) { | 753 | if (mask == 0 && hotplug_status == 0) { |
741 | VPRINTK("QUICK EXIT 3\n"); | 754 | VPRINTK("QUICK EXIT 3\n"); |
742 | goto done_irq; | 755 | goto done_irq; |
743 | } | 756 | } |
744 | 757 | ||
745 | writel(mask, mmio_base + PDC_INT_SEQMASK); | 758 | writel(mask, mmio_base + PDC_INT_SEQMASK); |
746 | 759 | ||
760 | is_sataii_tx4 = pdc_is_sataii_tx4(host->ports[0]->flags); | ||
761 | |||
747 | for (i = 0; i < host->n_ports; i++) { | 762 | for (i = 0; i < host->n_ports; i++) { |
748 | VPRINTK("port %u\n", i); | 763 | VPRINTK("port %u\n", i); |
749 | ap = host->ports[i]; | 764 | ap = host->ports[i]; |
765 | |||
766 | /* check for a plug or unplug event */ | ||
767 | ata_no = pdc_port_no_to_ata_no(i, is_sataii_tx4); | ||
768 | tmp = hotplug_status & (0x11 << ata_no); | ||
769 | if (tmp && ap && | ||
770 | !(ap->flags & ATA_FLAG_DISABLED)) { | ||
771 | struct ata_eh_info *ehi = &ap->eh_info; | ||
772 | ata_ehi_clear_desc(ehi); | ||
773 | ata_ehi_hotplugged(ehi); | ||
774 | ata_ehi_push_desc(ehi, "hotplug_status %#x", tmp); | ||
775 | ata_port_freeze(ap); | ||
776 | ++handled; | ||
777 | continue; | ||
778 | } | ||
779 | |||
780 | /* check for a packet interrupt */ | ||
750 | tmp = mask & (1 << (i + 1)); | 781 | tmp = mask & (1 << (i + 1)); |
751 | if (tmp && ap && | 782 | if (tmp && ap && |
752 | !(ap->flags & ATA_FLAG_DISABLED)) { | 783 | !(ap->flags & ATA_FLAG_DISABLED)) { |
@@ -902,9 +933,9 @@ static void pdc_host_init(struct ata_host *host) | |||
902 | tmp = readl(mmio + hotplug_offset); | 933 | tmp = readl(mmio + hotplug_offset); |
903 | writel(tmp | 0xff, mmio + hotplug_offset); | 934 | writel(tmp | 0xff, mmio + hotplug_offset); |
904 | 935 | ||
905 | /* mask plug/unplug ints */ | 936 | /* unmask plug/unplug ints */ |
906 | tmp = readl(mmio + hotplug_offset); | 937 | tmp = readl(mmio + hotplug_offset); |
907 | writel(tmp | 0xff0000, mmio + hotplug_offset); | 938 | writel(tmp & ~0xff0000, mmio + hotplug_offset); |
908 | 939 | ||
909 | /* don't initialise TBG or SLEW on 2nd generation chips */ | 940 | /* don't initialise TBG or SLEW on 2nd generation chips */ |
910 | if (is_gen2) | 941 | if (is_gen2) |