Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+88 −21
Original line number Diff line number Diff line
@@ -46,7 +46,7 @@
#include "sata_promise.h"

#define DRV_NAME	"sata_promise"
#define DRV_VERSION	"2.11"
#define DRV_VERSION	"2.12"

enum {
	PDC_MAX_PORTS		= 4,
@@ -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 unsigned int pdc_qc_issue_prot(struct ata_queued_cmd *qc);
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_sata_thaw(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_post_internal_cmd(struct ata_queued_cmd *qc);
@@ -180,8 +182,8 @@ static const struct ata_port_operations pdc_sata_ops = {

	.qc_prep		= pdc_qc_prep,
	.qc_issue		= pdc_qc_issue_prot,
	.freeze			= pdc_freeze,
	.thaw			= pdc_thaw,
	.freeze			= pdc_sata_freeze,
	.thaw			= pdc_sata_thaw,
	.error_handler		= pdc_sata_error_handler,
	.post_internal_cmd	= pdc_post_internal_cmd,
	.cable_detect		= pdc_sata_cable_detect,
@@ -205,8 +207,8 @@ static const struct ata_port_operations pdc_old_sata_ops = {

	.qc_prep		= pdc_qc_prep,
	.qc_issue		= pdc_qc_issue_prot,
	.freeze			= pdc_freeze,
	.thaw			= pdc_thaw,
	.freeze			= pdc_sata_freeze,
	.thaw			= pdc_sata_thaw,
	.error_handler		= pdc_sata_error_handler,
	.post_internal_cmd	= pdc_post_internal_cmd,
	.cable_detect		= pdc_sata_cable_detect,
@@ -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)
{
	void __iomem *mmio = ap->ioaddr.cmd_addr;
@@ -643,6 +680,29 @@ static void pdc_freeze(struct ata_port *ap)
	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)
{
	void __iomem *mmio = ap->ioaddr.cmd_addr;
@@ -658,6 +718,26 @@ static void pdc_thaw(struct ata_port *ap)
	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)
{
	if (!(ap->pflags & ATA_PFLAG_FROZEN))
@@ -765,19 +845,6 @@ static void pdc_irq_clear(struct ata_port *ap)
	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)
{
	struct ata_host *host = dev_instance;
@@ -799,6 +866,8 @@ static irqreturn_t pdc_interrupt(int irq, void *dev_instance)

	mmio_base = host->iomap[PDC_MMIO_BAR];

	spin_lock(&host->lock);

	/* read and clear hotplug flags for all ports */
	if (host->ports[0]->flags & PDC_FLAG_GEN_II)
		hotplug_offset = PDC2_SATA_PLUG_CSR;
@@ -814,11 +883,9 @@ static irqreturn_t pdc_interrupt(int irq, void *dev_instance)

	if (mask == 0xffffffff && hotplug_status == 0) {
		VPRINTK("QUICK EXIT 2\n");
		return IRQ_NONE;
		goto done_irq;
	}

	spin_lock(&host->lock);

	mask &= 0xffff;		/* only 16 tags possible */
	if (mask == 0 && hotplug_status == 0) {
		VPRINTK("QUICK EXIT 3\n");