Commit c07a9c49 authored by Mikael Pettersson's avatar Mikael Pettersson Committed by Jeff Garzik

sata_promise: fix hardreset hotplug events, take 2

A Promise SATA controller will signal hotplug events when a hard
reset (COMRESET) is done on a port. These events aren't masked by
the driver, and the unexpected interrupts will cause a sequence
of failed reset attempts util libata's EH finally gives up.

This has not been a common problem so far, but the pending libata
hardreset-by-default changes makes it a critical issue.

The solution is to disable hotplug events before a reset, and to
reenable them afterwards. (Promise's driver does this too.)

This patch adds SATA-specific versions of ->freeze() and ->thaw()
that also disable and enable hotplug events. PATA ports continue
to use the old versions of ->freeze() and ->thaw().

Accesses to the hotplug register must be serialised via host->lock.
We rely on ap->lock == &ap->host->lock and that libata takes this
lock before ->freeze() and ->thaw(). Document this requirement.
The interrupt handler is adjusted so its hotplug register accesses
are inside the region protected by host->lock.

Tested on various chips (SATA300TX4, SATA300TX2plus, SATAII150TX4,
FastTrack TX4000) with various combinations of SATA and PATA disks,
with and without the pending hardreset-by-default changes.
Signed-off-by: default avatarMikael Pettersson <mikpe@it.uu.se>
Acked-by: default avatarTejun Heo <htejun@gmail.com>
Signed-off-by: default avatarJeff Garzik <jeff@garzik.org>
parent dda7aba1
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
#include "sata_promise.h" #include "sata_promise.h"
#define DRV_NAME "sata_promise" #define DRV_NAME "sata_promise"
#define DRV_VERSION "2.11" #define DRV_VERSION "2.12"
enum { enum {
PDC_MAX_PORTS = 4, PDC_MAX_PORTS = 4,
...@@ -145,7 +145,9 @@ static int pdc_old_sata_check_atapi_dma(struct ata_queued_cmd *qc); ...@@ -145,7 +145,9 @@ static int pdc_old_sata_check_atapi_dma(struct ata_queued_cmd *qc);
static void pdc_irq_clear(struct ata_port *ap); static void pdc_irq_clear(struct ata_port *ap);
static unsigned int pdc_qc_issue_prot(struct ata_queued_cmd *qc); static unsigned int pdc_qc_issue_prot(struct ata_queued_cmd *qc);
static void pdc_freeze(struct ata_port *ap); static void pdc_freeze(struct ata_port *ap);
static void pdc_sata_freeze(struct ata_port *ap);
static void pdc_thaw(struct ata_port *ap); static void pdc_thaw(struct ata_port *ap);
static void pdc_sata_thaw(struct ata_port *ap);
static void pdc_pata_error_handler(struct ata_port *ap); static void pdc_pata_error_handler(struct ata_port *ap);
static void pdc_sata_error_handler(struct ata_port *ap); static void pdc_sata_error_handler(struct ata_port *ap);
static void pdc_post_internal_cmd(struct ata_queued_cmd *qc); static void pdc_post_internal_cmd(struct ata_queued_cmd *qc);
...@@ -180,8 +182,8 @@ static const struct ata_port_operations pdc_sata_ops = { ...@@ -180,8 +182,8 @@ static const struct ata_port_operations pdc_sata_ops = {
.qc_prep = pdc_qc_prep, .qc_prep = pdc_qc_prep,
.qc_issue = pdc_qc_issue_prot, .qc_issue = pdc_qc_issue_prot,
.freeze = pdc_freeze, .freeze = pdc_sata_freeze,
.thaw = pdc_thaw, .thaw = pdc_sata_thaw,
.error_handler = pdc_sata_error_handler, .error_handler = pdc_sata_error_handler,
.post_internal_cmd = pdc_post_internal_cmd, .post_internal_cmd = pdc_post_internal_cmd,
.cable_detect = pdc_sata_cable_detect, .cable_detect = pdc_sata_cable_detect,
...@@ -205,8 +207,8 @@ static const struct ata_port_operations pdc_old_sata_ops = { ...@@ -205,8 +207,8 @@ static const struct ata_port_operations pdc_old_sata_ops = {
.qc_prep = pdc_qc_prep, .qc_prep = pdc_qc_prep,
.qc_issue = pdc_qc_issue_prot, .qc_issue = pdc_qc_issue_prot,
.freeze = pdc_freeze, .freeze = pdc_sata_freeze,
.thaw = pdc_thaw, .thaw = pdc_sata_thaw,
.error_handler = pdc_sata_error_handler, .error_handler = pdc_sata_error_handler,
.post_internal_cmd = pdc_post_internal_cmd, .post_internal_cmd = pdc_post_internal_cmd,
.cable_detect = pdc_sata_cable_detect, .cable_detect = pdc_sata_cable_detect,
...@@ -631,6 +633,41 @@ static void pdc_qc_prep(struct ata_queued_cmd *qc) ...@@ -631,6 +633,41 @@ static void pdc_qc_prep(struct ata_queued_cmd *qc)
} }
} }
static int pdc_is_sataii_tx4(unsigned long flags)
{
const unsigned long mask = PDC_FLAG_GEN_II | PDC_FLAG_4_PORTS;
return (flags & mask) == mask;
}
static unsigned int pdc_port_no_to_ata_no(unsigned int port_no,
int is_sataii_tx4)
{
static const unsigned char sataii_tx4_port_remap[4] = { 3, 1, 0, 2};
return is_sataii_tx4 ? sataii_tx4_port_remap[port_no] : port_no;
}
static unsigned int pdc_sata_nr_ports(const struct ata_port *ap)
{
return (ap->flags & PDC_FLAG_4_PORTS) ? 4 : 2;
}
static unsigned int pdc_sata_ata_port_to_ata_no(const struct ata_port *ap)
{
const struct ata_host *host = ap->host;
unsigned int nr_ports = pdc_sata_nr_ports(ap);
unsigned int i;
for(i = 0; i < nr_ports && host->ports[i] != ap; ++i)
;
BUG_ON(i >= nr_ports);
return pdc_port_no_to_ata_no(i, pdc_is_sataii_tx4(ap->flags));
}
static unsigned int pdc_sata_hotplug_offset(const struct ata_port *ap)
{
return (ap->flags & PDC_FLAG_GEN_II) ? PDC2_SATA_PLUG_CSR : PDC_SATA_PLUG_CSR;
}
static void pdc_freeze(struct ata_port *ap) static void pdc_freeze(struct ata_port *ap)
{ {
void __iomem *mmio = ap->ioaddr.cmd_addr; void __iomem *mmio = ap->ioaddr.cmd_addr;
...@@ -643,6 +680,29 @@ static void pdc_freeze(struct ata_port *ap) ...@@ -643,6 +680,29 @@ static void pdc_freeze(struct ata_port *ap)
readl(mmio + PDC_CTLSTAT); /* flush */ readl(mmio + PDC_CTLSTAT); /* flush */
} }
static void pdc_sata_freeze(struct ata_port *ap)
{
struct ata_host *host = ap->host;
void __iomem *host_mmio = host->iomap[PDC_MMIO_BAR];
unsigned int hotplug_offset = pdc_sata_hotplug_offset(ap);
unsigned int ata_no = pdc_sata_ata_port_to_ata_no(ap);
u32 hotplug_status;
/* Disable hotplug events on this port.
*
* Locking:
* 1) hotplug register accesses must be serialised via host->lock
* 2) ap->lock == &ap->host->lock
* 3) ->freeze() and ->thaw() are called with ap->lock held
*/
hotplug_status = readl(host_mmio + hotplug_offset);
hotplug_status |= 0x11 << (ata_no + 16);
writel(hotplug_status, host_mmio + hotplug_offset);
readl(host_mmio + hotplug_offset); /* flush */
pdc_freeze(ap);
}
static void pdc_thaw(struct ata_port *ap) static void pdc_thaw(struct ata_port *ap)
{ {
void __iomem *mmio = ap->ioaddr.cmd_addr; void __iomem *mmio = ap->ioaddr.cmd_addr;
...@@ -658,6 +718,26 @@ static void pdc_thaw(struct ata_port *ap) ...@@ -658,6 +718,26 @@ static void pdc_thaw(struct ata_port *ap)
readl(mmio + PDC_CTLSTAT); /* flush */ readl(mmio + PDC_CTLSTAT); /* flush */
} }
static void pdc_sata_thaw(struct ata_port *ap)
{
struct ata_host *host = ap->host;
void __iomem *host_mmio = host->iomap[PDC_MMIO_BAR];
unsigned int hotplug_offset = pdc_sata_hotplug_offset(ap);
unsigned int ata_no = pdc_sata_ata_port_to_ata_no(ap);
u32 hotplug_status;
pdc_thaw(ap);
/* Enable hotplug events on this port.
* Locking: see pdc_sata_freeze().
*/
hotplug_status = readl(host_mmio + hotplug_offset);
hotplug_status |= 0x11 << ata_no;
hotplug_status &= ~(0x11 << (ata_no + 16));
writel(hotplug_status, host_mmio + hotplug_offset);
readl(host_mmio + hotplug_offset); /* flush */
}
static void pdc_common_error_handler(struct ata_port *ap, ata_reset_fn_t hardreset) static void pdc_common_error_handler(struct ata_port *ap, ata_reset_fn_t hardreset)
{ {
if (!(ap->pflags & ATA_PFLAG_FROZEN)) if (!(ap->pflags & ATA_PFLAG_FROZEN))
...@@ -765,19 +845,6 @@ static void pdc_irq_clear(struct ata_port *ap) ...@@ -765,19 +845,6 @@ static void pdc_irq_clear(struct ata_port *ap)
readl(mmio + PDC_INT_SEQMASK); readl(mmio + PDC_INT_SEQMASK);
} }
static int pdc_is_sataii_tx4(unsigned long flags)
{
const unsigned long mask = PDC_FLAG_GEN_II | PDC_FLAG_4_PORTS;
return (flags & mask) == mask;
}
static unsigned int pdc_port_no_to_ata_no(unsigned int port_no,
int is_sataii_tx4)
{
static const unsigned char sataii_tx4_port_remap[4] = { 3, 1, 0, 2};
return is_sataii_tx4 ? sataii_tx4_port_remap[port_no] : port_no;
}
static irqreturn_t pdc_interrupt(int irq, void *dev_instance) static irqreturn_t pdc_interrupt(int irq, void *dev_instance)
{ {
struct ata_host *host = dev_instance; struct ata_host *host = dev_instance;
...@@ -799,6 +866,8 @@ static irqreturn_t pdc_interrupt(int irq, void *dev_instance) ...@@ -799,6 +866,8 @@ static irqreturn_t pdc_interrupt(int irq, void *dev_instance)
mmio_base = host->iomap[PDC_MMIO_BAR]; mmio_base = host->iomap[PDC_MMIO_BAR];
spin_lock(&host->lock);
/* read and clear hotplug flags for all ports */ /* read and clear hotplug flags for all ports */
if (host->ports[0]->flags & PDC_FLAG_GEN_II) if (host->ports[0]->flags & PDC_FLAG_GEN_II)
hotplug_offset = PDC2_SATA_PLUG_CSR; hotplug_offset = PDC2_SATA_PLUG_CSR;
...@@ -814,11 +883,9 @@ static irqreturn_t pdc_interrupt(int irq, void *dev_instance) ...@@ -814,11 +883,9 @@ static irqreturn_t pdc_interrupt(int irq, void *dev_instance)
if (mask == 0xffffffff && hotplug_status == 0) { if (mask == 0xffffffff && hotplug_status == 0) {
VPRINTK("QUICK EXIT 2\n"); VPRINTK("QUICK EXIT 2\n");
return IRQ_NONE; goto done_irq;
} }
spin_lock(&host->lock);
mask &= 0xffff; /* only 16 tags possible */ mask &= 0xffff; /* only 16 tags possible */
if (mask == 0 && hotplug_status == 0) { if (mask == 0 && hotplug_status == 0) {
VPRINTK("QUICK EXIT 3\n"); VPRINTK("QUICK EXIT 3\n");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment