[libata sata_mv] mv_hw_ops for hardware families; new errata

- eliminate a bunch of redundant tests by creating a per-chip-family
  set of hooks, mv_hw_ops
- implement more errata, from newer Marvell GPL'd driver
diff --git a/drivers/scsi/sata_mv.c b/drivers/scsi/sata_mv.c
index 9d116d0..4ca4b35 100644
--- a/drivers/scsi/sata_mv.c
+++ b/drivers/scsi/sata_mv.c
@@ -83,16 +83,15 @@
 	/* Host Flags */
 	MV_FLAG_DUAL_HC		= (1 << 30),  /* two SATA Host Controllers */
 	MV_FLAG_IRQ_COALESCE	= (1 << 29),  /* IRQ coalescing capability */
-	MV_FLAG_GLBL_SFT_RST	= (1 << 28),  /* Global Soft Reset support */
 	MV_COMMON_FLAGS		= (ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
 				   ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO),
-	MV_6XXX_FLAGS		= (MV_FLAG_IRQ_COALESCE |
-				   MV_FLAG_GLBL_SFT_RST),
+	MV_6XXX_FLAGS		= MV_FLAG_IRQ_COALESCE,
 
 	chip_504x		= 0,
 	chip_508x		= 1,
-	chip_604x		= 2,
-	chip_608x		= 3,
+	chip_5080		= 2,
+	chip_604x		= 3,
+	chip_608x		= 4,
 
 	CRQB_FLAG_READ		= (1 << 0),
 	CRQB_TAG_SHIFT		= 1,
@@ -150,6 +149,7 @@
 	/* SATA registers */
 	SATA_STATUS_OFS		= 0x300,  /* ctrl, err regs follow status */
 	SATA_ACTIVE_OFS		= 0x350,
+	PHY_MODE3		= 0x310,
 	PHY_MODE4		= 0x314,
 	PHY_MODE2		= 0x330,
 	SATA_INTERFACE_CTL	= 0x050,
@@ -209,23 +209,20 @@
 	ATA_RST			= (1 << 2),
 
 	EDMA_ARB_CFG		= 0x38,
-	EDMA_NO_SNOOP		= (1 << 6),
 
 	/* Host private flags (hp_flags) */
 	MV_HP_FLAG_MSI		= (1 << 0),
-	MV_HP_ERRATA_60X1A1	= (1 << 1),
-	MV_HP_ERRATA_60X1B0	= (1 << 2),
-	MV_HP_ERRATA_50XXB0	= (1 << 3),
-	MV_HP_ERRATA_50XXB1	= (1 << 4),
-	MV_HP_ERRATA_50XXB2	= (1 << 5),
-	MV_HP_50XX		= (1 << 6),
+	MV_HP_ERRATA_50XXB0	= (1 << 1),
+	MV_HP_ERRATA_50XXB2	= (1 << 2),
+	MV_HP_ERRATA_60X1B2	= (1 << 3),
+	MV_HP_ERRATA_60X1C0	= (1 << 4),
+	MV_HP_50XX		= (1 << 5),
 
 	/* Port private flags (pp_flags) */
 	MV_PP_FLAG_EDMA_EN	= (1 << 0),
 	MV_PP_FLAG_EDMA_DS_ACT	= (1 << 1),
 };
 
-#define IS_50XX(hpriv) ((hpriv)->hp_flags & MV_HP_50XX)
 #define IS_60XX(hpriv) (((hpriv)->hp_flags & MV_HP_50XX) == 0)
 
 enum {
@@ -280,9 +277,19 @@
 	u32			pre;
 };
 
+struct mv_host_priv;
+struct mv_hw_ops {
+	void (*phy_errata)(struct ata_port *ap);
+	void (*enable_leds)(struct mv_host_priv *hpriv, void __iomem *mmio);
+	void (*read_preamp)(struct mv_host_priv *hpriv, int idx,
+			   void __iomem *mmio);
+	int (*reset_hc)(struct mv_host_priv *hpriv, void __iomem *mmio);
+};
+
 struct mv_host_priv {
 	u32			hp_flags;
 	struct mv_port_signal	signal[8];
+	const struct mv_hw_ops	*ops;
 };
 
 static void mv_irq_clear(struct ata_port *ap);
@@ -299,6 +306,18 @@
 static void mv_eng_timeout(struct ata_port *ap);
 static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent);
 
+static void mv5_phy_errata(struct ata_port *ap);
+static void mv5_enable_leds(struct mv_host_priv *hpriv, void __iomem *mmio);
+static void mv5_read_preamp(struct mv_host_priv *hpriv, int idx,
+			   void __iomem *mmio);
+static int mv5_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio);
+
+static void mv6_phy_errata(struct ata_port *ap);
+static void mv6_enable_leds(struct mv_host_priv *hpriv, void __iomem *mmio);
+static void mv6_read_preamp(struct mv_host_priv *hpriv, int idx,
+			   void __iomem *mmio);
+static int mv6_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio);
+
 static struct scsi_host_template mv_sht = {
 	.module			= THIS_MODULE,
 	.name			= DRV_NAME,
@@ -361,6 +380,13 @@
 		.udma_mask	= 0,	/* 0x7f (udma0-6 disabled for now) */
 		.port_ops	= &mv_ops,
 	},
+	{  /* chip_5080 */
+		.sht		= &mv_sht,
+		.host_flags	= (MV_COMMON_FLAGS | MV_FLAG_DUAL_HC),
+		.pio_mask	= 0x1f,	/* pio0-4 */
+		.udma_mask	= 0,	/* 0x7f (udma0-6 disabled for now) */
+		.port_ops	= &mv_ops,
+	},
 	{  /* chip_604x */
 		.sht		= &mv_sht,
 		.host_flags	= (MV_COMMON_FLAGS | MV_6XXX_FLAGS),
@@ -382,7 +408,7 @@
 #if 0 /* unusably broken right now */
 	{PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5040), 0, 0, chip_504x},
 	{PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5041), 0, 0, chip_504x},
-	{PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5080), 0, 0, chip_508x},
+	{PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5080), 0, 0, chip_5080},
 	{PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5081), 0, 0, chip_508x},
 #endif
 
@@ -402,6 +428,20 @@
 	.remove			= ata_pci_remove_one,
 };
 
+static const struct mv_hw_ops mv5xxx_ops = {
+	.phy_errata		= mv5_phy_errata,
+	.enable_leds		= mv5_enable_leds,
+	.read_preamp		= mv5_read_preamp,
+	.reset_hc		= mv5_reset_hc,
+};
+
+static const struct mv_hw_ops mv6xxx_ops = {
+	.phy_errata		= mv6_phy_errata,
+	.enable_leds		= mv6_enable_leds,
+	.read_preamp		= mv6_read_preamp,
+	.reset_hc		= mv6_reset_hc,
+};
+
 /*
  * Functions
  */
@@ -624,9 +664,9 @@
  *      LOCKING:
  *      Inherited from caller.
  */
-static int mv_global_soft_reset(void __iomem *mmio_base)
+static int mv6_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio)
 {
-	void __iomem *reg = mmio_base + PCI_MAIN_CMD_STS_OFS;
+	void __iomem *reg = mmio + PCI_MAIN_CMD_STS_OFS;
 	int i, rc = 0;
 	u32 t;
 
@@ -721,7 +761,6 @@
 static int mv_port_start(struct ata_port *ap)
 {
 	struct device *dev = ap->host_set->dev;
-	struct mv_host_priv *hpriv = ap->host_set->private_data;
 	struct mv_port_priv *pp;
 	void __iomem *port_mmio = mv_ap_base(ap);
 	void *mem;
@@ -779,15 +818,6 @@
 	writelfl(pp->crpb_dma & EDMA_RSP_Q_BASE_LO_MASK,
 		 port_mmio + EDMA_RSP_Q_OUT_PTR_OFS);
 
-	if (hpriv->hp_flags & MV_HP_ERRATA_60X1A1) {
-		u32 new_tmp, tmp;
-
-		new_tmp = tmp = readl(port_mmio + EDMA_ARB_CFG);
-		new_tmp &= ~EDMA_NO_SNOOP;
-		if (new_tmp != tmp)
-			writel(new_tmp, port_mmio + EDMA_ARB_CFG);
-	}
-
 	pp->req_producer = pp->rsp_consumer = 0;
 
 	/* Don't turn on EDMA here...do it before DMA commands only.  Else
@@ -1243,39 +1273,37 @@
 	return IRQ_RETVAL(handled);
 }
 
-static void mv_cfg_signal5(struct mv_host_priv *hpriv, int idx,
+static void mv5_read_preamp(struct mv_host_priv *hpriv, int idx,
 			   void __iomem *mmio)
 {
 	/* FIXME */
 }
 
-static void mv_enable_leds5(struct mv_host_priv *hpriv, void __iomem *mmio)
+static void mv5_enable_leds(struct mv_host_priv *hpriv, void __iomem *mmio)
 {
 	/* FIXME */
 }
 
-static void mv_phy_errata5(struct ata_port *ap)
+static void mv5_phy_errata(struct ata_port *ap)
 {
 	/* FIXME */
 }
 
-static void mv_cfg_signal6(struct mv_host_priv *hpriv, int idx,
+static int mv5_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio)
+{
+	/* FIXME */
+	return 1;
+}
+
+static void mv6_read_preamp(struct mv_host_priv *hpriv, int idx,
 			   void __iomem *mmio)
 {
 	void __iomem *port_mmio;
 	u32 tmp;
 
-	if (hpriv->hp_flags & MV_HP_ERRATA_60X1A1) {
-		hpriv->signal[idx].amps = 0x5 << 8;
-		hpriv->signal[idx].pre = 0x3 << 5;
-		return;
-	}
-
-	assert (hpriv->hp_flags & MV_HP_ERRATA_60X1B0);
-
 	tmp = readl(mmio + MV_RESET_CFG);
 	if ((tmp & (1 << 0)) == 0) {
-		hpriv->signal[idx].amps = 0x4 << 8;
+		hpriv->signal[idx].amps = 0x7 << 8;
 		hpriv->signal[idx].pre = 0x1 << 5;
 		return;
 	}
@@ -1287,34 +1315,57 @@
 	hpriv->signal[idx].pre = tmp & 0xe0;	/* bits 7:5 */
 }
 
-static void mv_enable_leds6(struct mv_host_priv *hpriv, void __iomem *mmio)
+static void mv6_enable_leds(struct mv_host_priv *hpriv, void __iomem *mmio)
 {
-	if (hpriv->hp_flags & MV_HP_ERRATA_60X1A1)
-		writel(0x00020060, mmio + MV_GPIO_PORT_CTL);
-
-	else if (hpriv->hp_flags & MV_HP_ERRATA_60X1B0)
-		writel(0x00000060, mmio + MV_GPIO_PORT_CTL);
+	writel(0x00000060, mmio + MV_GPIO_PORT_CTL);
 }
 
-static void mv_phy_errata6(struct ata_port *ap)
+static void mv6_phy_errata(struct ata_port *ap)
 {
 	struct mv_host_priv *hpriv = ap->host_set->private_data;
 	u32 hp_flags = hpriv->hp_flags;
 	void __iomem *port_mmio = mv_ap_base(ap);
+	int fix_phy_mode2 =
+		hp_flags & (MV_HP_ERRATA_60X1B2 | MV_HP_ERRATA_60X1C0);
 	int fix_phy_mode4 =
-		hp_flags & (MV_HP_ERRATA_60X1A1 | MV_HP_ERRATA_60X1B0);
-	u32 m2;
+		hp_flags & (MV_HP_ERRATA_60X1B2 | MV_HP_ERRATA_60X1C0);
+	u32 m2, tmp;
+
+	if (fix_phy_mode2) {
+		m2 = readl(port_mmio + PHY_MODE2);
+		m2 &= ~(1 << 16);
+		m2 |= (1 << 31);
+		writel(m2, port_mmio + PHY_MODE2);
+
+		udelay(200);
+
+		m2 = readl(port_mmio + PHY_MODE2);
+		m2 &= ~((1 << 16) | (1 << 31));
+		writel(m2, port_mmio + PHY_MODE2);
+
+		udelay(200);
+	}
+
+	/* who knows what this magic does */
+	tmp = readl(port_mmio + PHY_MODE3);
+	tmp &= ~0x7F800000;
+	tmp |= 0x2A800000;
+	writel(tmp, port_mmio + PHY_MODE3);
 
 	if (fix_phy_mode4) {
-		u32 tmp, m4;
+		u32 m4;
 
 		m4 = readl(port_mmio + PHY_MODE4);
-		tmp = readl(port_mmio + 0x310);
+
+		if (hp_flags & MV_HP_ERRATA_60X1B2)
+			tmp = readl(port_mmio + 0x310);
 
 		m4 = (m4 & ~(1 << 1)) | (1 << 0);
 
 		writel(m4, port_mmio + PHY_MODE4);
-		writel(tmp, port_mmio + 0x310);
+
+		if (hp_flags & MV_HP_ERRATA_60X1B2)
+			writel(tmp, port_mmio + 0x310);
 	}
 
 	/* Revert values of pre-emphasis and signal amps to the saved ones */
@@ -1323,20 +1374,11 @@
 	m2 &= ~MV_M2_PREAMP_MASK;
 	m2 |= hpriv->signal[ap->port_no].amps;
 	m2 |= hpriv->signal[ap->port_no].pre;
+	m2 &= ~(1 << 16);
 
 	writel(m2, port_mmio + PHY_MODE2);
 }
 
-static void mv_phy_errata(struct ata_port *ap)
-{
-	struct mv_host_priv *hpriv = ap->host_set->private_data;
-
-	if (IS_50XX(hpriv))
-		mv_phy_errata5(ap);
-	else
-		mv_phy_errata6(ap);
-}
-
 /**
  *      mv_phy_reset - Perform eDMA reset followed by COMRESET
  *      @ap: ATA channel to manipulate
@@ -1376,7 +1418,7 @@
 	 */
 	writelfl(0, port_mmio + EDMA_CMD_OFS);
 
-	mv_phy_errata(ap);
+	hpriv->ops->phy_errata(ap);
 
 	DPRINTK("S-regs after ATA_RST: SStat 0x%08x SErr 0x%08x "
 		"SCtrl 0x%08x\n", mv_scr_read(ap, SCR_STATUS),
@@ -1521,15 +1563,7 @@
 		readl(port_mmio + EDMA_ERR_IRQ_MASK_OFS));
 }
 
-static void mv_enable_leds(struct mv_host_priv *hpriv, void __iomem *mmio)
-{
-	if (IS_50XX(hpriv))
-		mv_enable_leds5(hpriv, mmio);
-	else
-		mv_enable_leds6(hpriv, mmio);
-}
-
-static int mv_cfg_errata(struct pci_dev *pdev, struct mv_host_priv *hpriv,
+static int mv_chip_id(struct pci_dev *pdev, struct mv_host_priv *hpriv,
 			 unsigned int board_idx)
 {
 	u8 rev_id;
@@ -1538,70 +1572,60 @@
 	pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id);
 
 	switch(board_idx) {
-	case chip_504x:
-	case chip_508x:
+	case chip_5080:
+		hpriv->ops = &mv5xxx_ops;
 		hp_flags |= MV_HP_50XX;
 
-		if (pdev->device == 0x5080) {
-			switch (rev_id) {
-			case 0x0:
-				dev_printk(KERN_WARNING, &pdev->dev,
-					   "Applying B0 workarounds to unknown rev 0\n");
-				/* fall through */
-			case 0x1:
-				hp_flags |= MV_HP_ERRATA_50XXB0;
-				break;
-			case 0x2:
-				hp_flags |= MV_HP_ERRATA_50XXB1;
-				break;
-			case 0x3:
-				hp_flags |= MV_HP_ERRATA_50XXB2;
-				break;
-			default:
-				dev_printk(KERN_WARNING, &pdev->dev,
-					   "Applying B2 workarounds to future rev\n");
-				hp_flags |= MV_HP_ERRATA_50XXB2;
-				break;
-			}
-		} else {
-			switch (rev_id) {
-			case 0x0:
-				hp_flags |= MV_HP_ERRATA_50XXB0;
-				break;
-			case 0x1:
-				dev_printk(KERN_WARNING, &pdev->dev,
-			          "Applying B1 workarounds to unknown rev 1\n");
-				/* fall through */
-			case 0x2:
-				hp_flags |= MV_HP_ERRATA_50XXB1;
-				break;
-			default:
-				dev_printk(KERN_WARNING, &pdev->dev,
-				   "Applying B2 workarounds to future rev\n");
-				/* fall through */
-			case 0x3:
-				hp_flags |= MV_HP_ERRATA_50XXB2;
-				break;
-			}
+		switch (rev_id) {
+		case 0x1:
+			hp_flags |= MV_HP_ERRATA_50XXB0;
+			break;
+		case 0x3:
+			hp_flags |= MV_HP_ERRATA_50XXB2;
+			break;
+		default:
+			dev_printk(KERN_WARNING, &pdev->dev,
+			   "Applying 50XXB2 workarounds to unknown rev\n");
+			hp_flags |= MV_HP_ERRATA_50XXB2;
+			break;
+		}
+		break;
+
+	case chip_504x:
+	case chip_508x:
+		hpriv->ops = &mv5xxx_ops;
+		hp_flags |= MV_HP_50XX;
+
+		switch (rev_id) {
+		case 0x0:
+			hp_flags |= MV_HP_ERRATA_50XXB0;
+			break;
+		case 0x3:
+			hp_flags |= MV_HP_ERRATA_50XXB2;
+			break;
+		default:
+			dev_printk(KERN_WARNING, &pdev->dev,
+			   "Applying B2 workarounds to unknown rev\n");
+			hp_flags |= MV_HP_ERRATA_50XXB2;
+			break;
 		}
 		break;
 
 	case chip_604x:
 	case chip_608x:
+		hpriv->ops = &mv6xxx_ops;
+
 		switch (rev_id) {
-		case 0x0:
-			dev_printk(KERN_WARNING, &pdev->dev,
-			          "Applying A1 workarounds to unknown rev 0\n");
-			/* fall through */
-		case 0x1:
-			hp_flags |= MV_HP_ERRATA_60X1A1;
+		case 0x7:
+			hp_flags |= MV_HP_ERRATA_60X1B2;
+			break;
+		case 0x9:
+			hp_flags |= MV_HP_ERRATA_60X1C0;
 			break;
 		default:
 			dev_printk(KERN_WARNING, &pdev->dev,
-				   "Applying B0 workarounds to future rev\n");
-			/* fall through */
-		case 0x2:
-			hp_flags |= MV_HP_ERRATA_60X1B0;
+				   "Applying B2 workarounds to unknown rev\n");
+			hp_flags |= MV_HP_ERRATA_60X1B2;
 			break;
 		}
 		break;
@@ -1617,7 +1641,7 @@
 }
 
 /**
- *      mv_host_init - Perform some early initialization of the host.
+ *      mv_init_host - Perform some early initialization of the host.
  *	@pdev: host PCI device
  *      @probe_ent: early data struct representing the host
  *
@@ -1627,7 +1651,7 @@
  *      LOCKING:
  *      Inherited from caller.
  */
-static int mv_host_init(struct pci_dev *pdev, struct ata_probe_ent *probe_ent,
+static int mv_init_host(struct pci_dev *pdev, struct ata_probe_ent *probe_ent,
 			unsigned int board_idx)
 {
 	int rc = 0, n_hc, port, hc;
@@ -1635,28 +1659,24 @@
 	void __iomem *port_mmio;
 	struct mv_host_priv *hpriv = probe_ent->private_data;
 
-	rc = mv_cfg_errata(pdev, hpriv, board_idx);
+	/* global interrupt mask */
+	writel(0, mmio + HC_MAIN_IRQ_MASK_OFS);
+
+	rc = mv_chip_id(pdev, hpriv, board_idx);
 	if (rc)
 		goto done;
 
 	n_hc = mv_get_hc_count(probe_ent->host_flags);
 	probe_ent->n_ports = MV_PORTS_PER_HC * n_hc;
 
-	if (IS_50XX(hpriv)) {
-		for (port = 0; port < probe_ent->n_ports; port++)
-			mv_cfg_signal5(hpriv, port, mmio);
-	} else {
-		for (port = 0; port < probe_ent->n_ports; port++)
-			mv_cfg_signal6(hpriv, port, mmio);
-	}
+	for (port = 0; port < probe_ent->n_ports; port++)
+		hpriv->ops->read_preamp(hpriv, port, mmio);
 
-	if ((MV_FLAG_GLBL_SFT_RST & probe_ent->host_flags) &&
-	    mv_global_soft_reset(probe_ent->mmio_base)) {
-		rc = 1;
+	rc = hpriv->ops->reset_hc(hpriv, mmio);
+	if (rc)
 		goto done;
-	}
 
-	mv_enable_leds(hpriv, mmio);
+	hpriv->ops->enable_leds(hpriv, mmio);
 
 	for (port = 0; port < probe_ent->n_ports; port++) {
 		port_mmio = mv_port_base(mmio, port);
@@ -1794,7 +1814,7 @@
 	probe_ent->private_data = hpriv;
 
 	/* initialize adapter */
-	rc = mv_host_init(pdev, probe_ent, board_idx);
+	rc = mv_init_host(pdev, probe_ent, board_idx);
 	if (rc) {
 		goto err_out_hpriv;
 	}