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

Commit e49856d8 authored by Mark Lord's avatar Mark Lord Committed by Jeff Garzik
Browse files

sata_mv add basic port multiplier support



Add basic port-multiplier support to sata_mv.
This works in Command-based-switching mode for Gen-II chipsets,
and in FIS-based-switching mode for Gen-IIe chipsets.

Error handling remains at the primary port level for now
(works okay, but not great).  This will get fixed in a subsequent
patch series for IRQ/EH handling fixes.  There are also some
known NCQ/PMP errata to be dealt with in the near future,
once we have this basic PMP support in place.

Signed-off-by: default avatarMark Lord <mlord@pobox.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@redhat.com>
parent 02c1f32f
Loading
Loading
Loading
Loading
+89 −4
Original line number Original line Diff line number Diff line
@@ -40,7 +40,7 @@


  5) Investigate problems with PCI Message Signalled Interrupts (MSI).
  5) Investigate problems with PCI Message Signalled Interrupts (MSI).


  6) Add port multiplier support (intermediate)
  6) Cache frequently-accessed registers in mv_port_priv to reduce overhead.


  7) Fix/reenable hot plug/unplug (should happen as a side-effect of (2) above).
  7) Fix/reenable hot plug/unplug (should happen as a side-effect of (2) above).


@@ -528,6 +528,12 @@ static int mv_stop_edma(struct ata_port *ap);
static int mv_stop_edma_engine(void __iomem *port_mmio);
static int mv_stop_edma_engine(void __iomem *port_mmio);
static void mv_edma_cfg(struct ata_port *ap, int want_ncq);
static void mv_edma_cfg(struct ata_port *ap, int want_ncq);


static void mv_pmp_select(struct ata_port *ap, int pmp);
static int mv_pmp_hardreset(struct ata_link *link, unsigned int *class,
				unsigned long deadline);
static int  mv_softreset(struct ata_link *link, unsigned int *class,
				unsigned long deadline);

/* .sg_tablesize is (MV_MAX_SG_CT / 2) in the structures below
/* .sg_tablesize is (MV_MAX_SG_CT / 2) in the structures below
 * because we have to allow room for worst case splitting of
 * because we have to allow room for worst case splitting of
 * PRDs for 64K boundaries in mv_fill_sg().
 * PRDs for 64K boundaries in mv_fill_sg().
@@ -566,14 +572,20 @@ static struct ata_port_operations mv5_ops = {


static struct ata_port_operations mv6_ops = {
static struct ata_port_operations mv6_ops = {
	.inherits		= &mv5_ops,
	.inherits		= &mv5_ops,
	.qc_defer		= ata_std_qc_defer,
	.qc_defer		= sata_pmp_qc_defer_cmd_switch,
	.dev_config             = mv6_dev_config,
	.dev_config             = mv6_dev_config,
	.scr_read		= mv_scr_read,
	.scr_read		= mv_scr_read,
	.scr_write		= mv_scr_write,
	.scr_write		= mv_scr_write,

	.pmp_hardreset		= mv_pmp_hardreset,
	.pmp_softreset		= mv_softreset,
	.softreset		= mv_softreset,
	.error_handler		= sata_pmp_error_handler,
};
};


static struct ata_port_operations mv_iie_ops = {
static struct ata_port_operations mv_iie_ops = {
	.inherits		= &mv6_ops,
	.inherits		= &mv6_ops,
	.qc_defer		= ata_std_qc_defer, /* FIS-based switching */
	.dev_config		= ATA_OP_NULL,
	.dev_config		= ATA_OP_NULL,
	.qc_prep		= mv_qc_prep_iie,
	.qc_prep		= mv_qc_prep_iie,
};
};
@@ -599,6 +611,7 @@ static const struct ata_port_info mv_port_info[] = {
	},
	},
	{  /* chip_604x */
	{  /* chip_604x */
		.flags		= MV_COMMON_FLAGS | MV_6XXX_FLAGS |
		.flags		= MV_COMMON_FLAGS | MV_6XXX_FLAGS |
				  ATA_FLAG_PMP | ATA_FLAG_ACPI_SATA |
				  ATA_FLAG_NCQ,
				  ATA_FLAG_NCQ,
		.pio_mask	= 0x1f,	/* pio0-4 */
		.pio_mask	= 0x1f,	/* pio0-4 */
		.udma_mask	= ATA_UDMA6,
		.udma_mask	= ATA_UDMA6,
@@ -606,6 +619,7 @@ static const struct ata_port_info mv_port_info[] = {
	},
	},
	{  /* chip_608x */
	{  /* chip_608x */
		.flags		= MV_COMMON_FLAGS | MV_6XXX_FLAGS |
		.flags		= MV_COMMON_FLAGS | MV_6XXX_FLAGS |
				  ATA_FLAG_PMP | ATA_FLAG_ACPI_SATA |
				  ATA_FLAG_NCQ | MV_FLAG_DUAL_HC,
				  ATA_FLAG_NCQ | MV_FLAG_DUAL_HC,
		.pio_mask	= 0x1f,	/* pio0-4 */
		.pio_mask	= 0x1f,	/* pio0-4 */
		.udma_mask	= ATA_UDMA6,
		.udma_mask	= ATA_UDMA6,
@@ -613,6 +627,7 @@ static const struct ata_port_info mv_port_info[] = {
	},
	},
	{  /* chip_6042 */
	{  /* chip_6042 */
		.flags		= MV_COMMON_FLAGS | MV_6XXX_FLAGS |
		.flags		= MV_COMMON_FLAGS | MV_6XXX_FLAGS |
				  ATA_FLAG_PMP | ATA_FLAG_ACPI_SATA |
				  ATA_FLAG_NCQ,
				  ATA_FLAG_NCQ,
		.pio_mask	= 0x1f,	/* pio0-4 */
		.pio_mask	= 0x1f,	/* pio0-4 */
		.udma_mask	= ATA_UDMA6,
		.udma_mask	= ATA_UDMA6,
@@ -620,6 +635,7 @@ static const struct ata_port_info mv_port_info[] = {
	},
	},
	{  /* chip_7042 */
	{  /* chip_7042 */
		.flags		= MV_COMMON_FLAGS | MV_6XXX_FLAGS |
		.flags		= MV_COMMON_FLAGS | MV_6XXX_FLAGS |
				  ATA_FLAG_PMP | ATA_FLAG_ACPI_SATA |
				  ATA_FLAG_NCQ,
				  ATA_FLAG_NCQ,
		.pio_mask	= 0x1f,	/* pio0-4 */
		.pio_mask	= 0x1f,	/* pio0-4 */
		.udma_mask	= ATA_UDMA6,
		.udma_mask	= ATA_UDMA6,
@@ -627,6 +643,7 @@ static const struct ata_port_info mv_port_info[] = {
	},
	},
	{  /* chip_soc */
	{  /* chip_soc */
		.flags		= MV_COMMON_FLAGS | MV_6XXX_FLAGS |
		.flags		= MV_COMMON_FLAGS | MV_6XXX_FLAGS |
				  ATA_FLAG_PMP | ATA_FLAG_ACPI_SATA |
				  ATA_FLAG_NCQ | MV_FLAG_SOC,
				  ATA_FLAG_NCQ | MV_FLAG_SOC,
		.pio_mask	= 0x1f,	/* pio0-4 */
		.pio_mask	= 0x1f,	/* pio0-4 */
		.udma_mask	= ATA_UDMA6,
		.udma_mask	= ATA_UDMA6,
@@ -1006,13 +1023,43 @@ static int mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val)
static void mv6_dev_config(struct ata_device *adev)
static void mv6_dev_config(struct ata_device *adev)
{
{
	/*
	/*
	 * Deal with Gen-II ("mv6") hardware quirks/restrictions:
	 *
	 * Gen-II does not support NCQ over a port multiplier
	 *  (no FIS-based switching).
	 *
	 * We don't have hob_nsect when doing NCQ commands on Gen-II.
	 * We don't have hob_nsect when doing NCQ commands on Gen-II.
	 * See mv_qc_prep() for more info.
	 * See mv_qc_prep() for more info.
	 */
	 */
	if (adev->flags & ATA_DFLAG_NCQ)
	if (adev->flags & ATA_DFLAG_NCQ) {
		if (adev->max_sectors > ATA_MAX_SECTORS)
		if (sata_pmp_attached(adev->link->ap))
			adev->flags &= ~ATA_DFLAG_NCQ;
		else if (adev->max_sectors > ATA_MAX_SECTORS)
			adev->max_sectors = ATA_MAX_SECTORS;
			adev->max_sectors = ATA_MAX_SECTORS;
	}
	}
}

static void mv_config_fbs(void __iomem *port_mmio, int enable_fbs)
{
	u32 old_fcfg, new_fcfg, old_ltmode, new_ltmode;
	/*
	 * Various bit settings required for operation
	 * in FIS-based switching (fbs) mode on GenIIe:
	 */
	old_fcfg   = readl(port_mmio + FIS_CFG_OFS);
	old_ltmode = readl(port_mmio + LTMODE_OFS);
	if (enable_fbs) {
		new_fcfg   = old_fcfg   |  FIS_CFG_SINGLE_SYNC;
		new_ltmode = old_ltmode |  LTMODE_BIT8;
	} else { /* disable fbs */
		new_fcfg   = old_fcfg   & ~FIS_CFG_SINGLE_SYNC;
		new_ltmode = old_ltmode & ~LTMODE_BIT8;
	}
	if (new_fcfg != old_fcfg)
		writelfl(new_fcfg, port_mmio + FIS_CFG_OFS);
	if (new_ltmode != old_ltmode)
		writelfl(new_ltmode, port_mmio + LTMODE_OFS);
}


static void mv_edma_cfg(struct ata_port *ap, int want_ncq)
static void mv_edma_cfg(struct ata_port *ap, int want_ncq)
{
{
@@ -1035,6 +1082,13 @@ static void mv_edma_cfg(struct ata_port *ap, int want_ncq)
		cfg |= (1 << 22);	/* enab 4-entry host queue cache */
		cfg |= (1 << 22);	/* enab 4-entry host queue cache */
		cfg |= (1 << 18);	/* enab early completion */
		cfg |= (1 << 18);	/* enab early completion */
		cfg |= (1 << 17);	/* enab cut-through (dis stor&forwrd) */
		cfg |= (1 << 17);	/* enab cut-through (dis stor&forwrd) */

		if (want_ncq && sata_pmp_attached(ap)) {
			cfg |= EDMA_CFG_EDMA_FBS; /* FIS-based switching */
			mv_config_fbs(port_mmio, 1);
		} else {
			mv_config_fbs(port_mmio, 0);
		}
	}
	}


	if (want_ncq) {
	if (want_ncq) {
@@ -1240,6 +1294,7 @@ static void mv_qc_prep(struct ata_queued_cmd *qc)
		flags |= CRQB_FLAG_READ;
		flags |= CRQB_FLAG_READ;
	WARN_ON(MV_MAX_Q_DEPTH <= qc->tag);
	WARN_ON(MV_MAX_Q_DEPTH <= qc->tag);
	flags |= qc->tag << CRQB_TAG_SHIFT;
	flags |= qc->tag << CRQB_TAG_SHIFT;
	flags |= (qc->dev->link->pmp & 0xf) << CRQB_PMP_SHIFT;


	/* get current queue index from software */
	/* get current queue index from software */
	in_index = pp->req_idx & MV_MAX_Q_DEPTH_MASK;
	in_index = pp->req_idx & MV_MAX_Q_DEPTH_MASK;
@@ -1331,6 +1386,7 @@ static void mv_qc_prep_iie(struct ata_queued_cmd *qc)
	WARN_ON(MV_MAX_Q_DEPTH <= qc->tag);
	WARN_ON(MV_MAX_Q_DEPTH <= qc->tag);
	flags |= qc->tag << CRQB_TAG_SHIFT;
	flags |= qc->tag << CRQB_TAG_SHIFT;
	flags |= qc->tag << CRQB_HOSTQ_SHIFT;
	flags |= qc->tag << CRQB_HOSTQ_SHIFT;
	flags |= (qc->dev->link->pmp & 0xf) << CRQB_PMP_SHIFT;


	/* get current queue index from software */
	/* get current queue index from software */
	in_index = pp->req_idx & MV_MAX_Q_DEPTH_MASK;
	in_index = pp->req_idx & MV_MAX_Q_DEPTH_MASK;
@@ -1394,6 +1450,7 @@ static unsigned int mv_qc_issue(struct ata_queued_cmd *qc)
		 * shadow block, etc registers.
		 * shadow block, etc registers.
		 */
		 */
		mv_stop_edma(ap);
		mv_stop_edma(ap);
		mv_pmp_select(ap, qc->dev->link->pmp);
		return ata_sff_qc_issue(qc);
		return ata_sff_qc_issue(qc);
	}
	}


@@ -2289,6 +2346,34 @@ static void mv_reset_channel(struct mv_host_priv *hpriv, void __iomem *mmio,
		mdelay(1);
		mdelay(1);
}
}


static void mv_pmp_select(struct ata_port *ap, int pmp)
{
	if (sata_pmp_supported(ap)) {
		void __iomem *port_mmio = mv_ap_base(ap);
		u32 reg = readl(port_mmio + SATA_IFCTL_OFS);
		int old = reg & 0xf;

		if (old != pmp) {
			reg = (reg & ~0xf) | pmp;
			writelfl(reg, port_mmio + SATA_IFCTL_OFS);
		}
	}
}

static int mv_pmp_hardreset(struct ata_link *link, unsigned int *class,
				unsigned long deadline)
{
	mv_pmp_select(link->ap, sata_srst_pmp(link));
	return sata_std_hardreset(link, class, deadline);
}

static int mv_softreset(struct ata_link *link, unsigned int *class,
				unsigned long deadline)
{
	mv_pmp_select(link->ap, sata_srst_pmp(link));
	return ata_sff_softreset(link, class, deadline);
}

static int mv_hardreset(struct ata_link *link, unsigned int *class,
static int mv_hardreset(struct ata_link *link, unsigned int *class,
			unsigned long deadline)
			unsigned long deadline)
{
{