summaryrefslogtreecommitdiff
path: root/drivers/scsi/sata_mv.c
diff options
context:
space:
mode:
authorJeff Garzik <jgarzik@pobox.com>2005-11-13 22:47:51 (GMT)
committerJeff Garzik <jgarzik@pobox.com>2005-11-13 22:47:51 (GMT)
commitc9d39130123238ac18478a42e25cb7996eacfcc0 (patch)
tree7aaae5b14ebfb42f160fe3035ea28f1ba3475106 /drivers/scsi/sata_mv.c
parent2a47ce06d534692f9bd2bf4e90a20fc9b1054c39 (diff)
downloadlinux-c9d39130123238ac18478a42e25cb7996eacfcc0.tar.xz
[libata sata_mv] fix tons of 50XX bugs
Diffstat (limited to 'drivers/scsi/sata_mv.c')
-rw-r--r--drivers/scsi/sata_mv.c301
1 files changed, 252 insertions, 49 deletions
diff --git a/drivers/scsi/sata_mv.c b/drivers/scsi/sata_mv.c
index 5defd05..722ab53 100644
--- a/drivers/scsi/sata_mv.c
+++ b/drivers/scsi/sata_mv.c
@@ -158,6 +158,9 @@ enum {
PHY_MODE3 = 0x310,
PHY_MODE4 = 0x314,
PHY_MODE2 = 0x330,
+ MV5_PHY_MODE = 0x74,
+ MV5_LT_MODE = 0x30,
+ MV5_PHY_CTL = 0x0C,
SATA_INTERFACE_CTL = 0x050,
MV_M2_PREAMP_MASK = 0x7e0,
@@ -214,6 +217,7 @@ enum {
EDMA_DS = (1 << 1),
ATA_RST = (1 << 2),
+ EDMA_IORDY_TMOUT = 0x34,
EDMA_ARB_CFG = 0x38,
/* Host private flags (hp_flags) */
@@ -229,6 +233,7 @@ enum {
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 {
@@ -298,7 +303,8 @@ struct mv_hw_ops {
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);
+ int (*reset_hc)(struct mv_host_priv *hpriv, void __iomem *mmio,
+ unsigned int n_hc);
void (*reset_flash)(struct mv_host_priv *hpriv, void __iomem *mmio);
void (*reset_bus)(struct pci_dev *pdev, void __iomem *mmio);
};
@@ -312,6 +318,8 @@ struct mv_host_priv {
static void mv_irq_clear(struct ata_port *ap);
static u32 mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in);
static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val);
+static u32 mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in);
+static void mv5_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val);
static void mv_phy_reset(struct ata_port *ap);
static void mv_host_stop(struct ata_host_set *host_set);
static int mv_port_start(struct ata_port *ap);
@@ -328,7 +336,8 @@ static void mv5_phy_errata(struct mv_host_priv *hpriv, void __iomem *mmio,
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 int mv5_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio,
+ unsigned int n_hc);
static void mv5_reset_flash(struct mv_host_priv *hpriv, void __iomem *mmio);
static void mv5_reset_bus(struct pci_dev *pdev, void __iomem *mmio);
@@ -337,9 +346,13 @@ static void mv6_phy_errata(struct mv_host_priv *hpriv, void __iomem *mmio,
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 int mv6_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio,
+ unsigned int n_hc);
static void mv6_reset_flash(struct mv_host_priv *hpriv, void __iomem *mmio);
static void mv_reset_pci_bus(struct pci_dev *pdev, void __iomem *mmio);
+static void mv_channel_reset(struct mv_host_priv *hpriv, void __iomem *mmio,
+ unsigned int port_no);
+static void mv_stop_and_reset(struct ata_port *ap);
static struct scsi_host_template mv_sht = {
.module = THIS_MODULE,
@@ -361,7 +374,34 @@ static struct scsi_host_template mv_sht = {
.ordered_flush = 1,
};
-static const struct ata_port_operations mv_ops = {
+static const struct ata_port_operations mv5_ops = {
+ .port_disable = ata_port_disable,
+
+ .tf_load = ata_tf_load,
+ .tf_read = ata_tf_read,
+ .check_status = ata_check_status,
+ .exec_command = ata_exec_command,
+ .dev_select = ata_std_dev_select,
+
+ .phy_reset = mv_phy_reset,
+
+ .qc_prep = mv_qc_prep,
+ .qc_issue = mv_qc_issue,
+
+ .eng_timeout = mv_eng_timeout,
+
+ .irq_handler = mv_interrupt,
+ .irq_clear = mv_irq_clear,
+
+ .scr_read = mv5_scr_read,
+ .scr_write = mv5_scr_write,
+
+ .port_start = mv_port_start,
+ .port_stop = mv_port_stop,
+ .host_stop = mv_host_stop,
+};
+
+static const struct ata_port_operations mv6_ops = {
.port_disable = ata_port_disable,
.tf_load = ata_tf_load,
@@ -393,29 +433,29 @@ static struct ata_port_info mv_port_info[] = {
.sht = &mv_sht,
.host_flags = MV_COMMON_FLAGS,
.pio_mask = 0x1f, /* pio0-4 */
- .udma_mask = 0, /* 0x7f (udma0-6 disabled for now) */
- .port_ops = &mv_ops,
+ .udma_mask = 0x7f, /* udma0-6 */
+ .port_ops = &mv5_ops,
},
{ /* chip_508x */
.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,
+ .udma_mask = 0x7f, /* udma0-6 */
+ .port_ops = &mv5_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,
+ .udma_mask = 0x7f, /* udma0-6 */
+ .port_ops = &mv5_ops,
},
{ /* chip_604x */
.sht = &mv_sht,
.host_flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS),
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 */
- .port_ops = &mv_ops,
+ .port_ops = &mv6_ops,
},
{ /* chip_608x */
.sht = &mv_sht,
@@ -423,17 +463,15 @@ static struct ata_port_info mv_port_info[] = {
MV_FLAG_DUAL_HC),
.pio_mask = 0x1f, /* pio0-4 */
.udma_mask = 0x7f, /* udma0-6 */
- .port_ops = &mv_ops,
+ .port_ops = &mv6_ops,
},
};
static const struct pci_device_id mv_pci_tbl[] = {
-#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_5080},
{PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5081), 0, 0, chip_508x},
-#endif
{PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6040), 0, 0, chip_604x},
{PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x6041), 0, 0, chip_604x},
@@ -484,11 +522,27 @@ static inline void __iomem *mv_hc_base(void __iomem *base, unsigned int hc)
return (base + MV_SATAHC0_REG_BASE + (hc * MV_SATAHC_REG_SZ));
}
+static inline unsigned int mv_hc_from_port(unsigned int port)
+{
+ return port >> MV_PORT_HC_SHIFT;
+}
+
+static inline unsigned int mv_hardport_from_port(unsigned int port)
+{
+ return port & MV_PORT_MASK;
+}
+
+static inline void __iomem *mv_hc_base_from_port(void __iomem *base,
+ unsigned int port)
+{
+ return mv_hc_base(base, mv_hc_from_port(port));
+}
+
static inline void __iomem *mv_port_base(void __iomem *base, unsigned int port)
{
- return (mv_hc_base(base, port >> MV_PORT_HC_SHIFT) +
+ return mv_hc_base_from_port(base, port) +
MV_SATAHC_ARBTR_REG_SZ +
- ((port & MV_PORT_MASK) * MV_PORT_REG_SZ));
+ (mv_hardport_from_port(port) * MV_PORT_REG_SZ);
}
static inline void __iomem *mv_ap_base(struct ata_port *ap)
@@ -1089,7 +1143,7 @@ static void mv_err_intr(struct ata_port *ap)
/* check for fatal here and recover if needed */
if (EDMA_ERR_FATAL & edma_err_cause) {
- mv_phy_reset(ap);
+ mv_stop_and_reset(ap);
}
}
@@ -1236,6 +1290,51 @@ static irqreturn_t mv_interrupt(int irq, void *dev_instance,
return IRQ_RETVAL(handled);
}
+static void __iomem *mv5_phy_base(void __iomem *mmio, unsigned int port)
+{
+ void __iomem *hc_mmio = mv_hc_base_from_port(mmio, port);
+ unsigned long ofs = (mv_hardport_from_port(port) + 1) * 0x100UL;
+
+ return hc_mmio + ofs;
+}
+
+static unsigned int mv5_scr_offset(unsigned int sc_reg_in)
+{
+ unsigned int ofs;
+
+ switch (sc_reg_in) {
+ case SCR_STATUS:
+ case SCR_ERROR:
+ case SCR_CONTROL:
+ ofs = sc_reg_in * sizeof(u32);
+ break;
+ default:
+ ofs = 0xffffffffU;
+ break;
+ }
+ return ofs;
+}
+
+static u32 mv5_scr_read(struct ata_port *ap, unsigned int sc_reg_in)
+{
+ void __iomem *mmio = mv5_phy_base(ap->host_set->mmio_base, ap->port_no);
+ unsigned int ofs = mv5_scr_offset(sc_reg_in);
+
+ if (ofs != 0xffffffffU)
+ return readl(mmio + ofs);
+ else
+ return (u32) ofs;
+}
+
+static void mv5_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val)
+{
+ void __iomem *mmio = mv5_phy_base(ap->host_set->mmio_base, ap->port_no);
+ unsigned int ofs = mv5_scr_offset(sc_reg_in);
+
+ if (ofs != 0xffffffffU)
+ writelfl(val, mmio + ofs);
+}
+
static void mv5_reset_bus(struct pci_dev *pdev, void __iomem *mmio)
{
u8 rev_id;
@@ -1262,7 +1361,13 @@ static void mv5_reset_flash(struct mv_host_priv *hpriv, void __iomem *mmio)
static void mv5_read_preamp(struct mv_host_priv *hpriv, int idx,
void __iomem *mmio)
{
- /* FIXME */
+ void __iomem *phy_mmio = mv5_phy_base(mmio, idx);
+ u32 tmp;
+
+ tmp = readl(phy_mmio + MV5_PHY_MODE);
+
+ hpriv->signal[idx].pre = tmp & 0x1800; /* bits 12:11 */
+ hpriv->signal[idx].amps = tmp & 0xe0; /* bits 7:5 */
}
static void mv5_enable_leds(struct mv_host_priv *hpriv, void __iomem *mmio)
@@ -1281,13 +1386,90 @@ static void mv5_enable_leds(struct mv_host_priv *hpriv, void __iomem *mmio)
static void mv5_phy_errata(struct mv_host_priv *hpriv, void __iomem *mmio,
unsigned int port)
{
- /* FIXME */
+ void __iomem *phy_mmio = mv5_phy_base(mmio, port);
+ const u32 mask = (1<<12) | (1<<11) | (1<<7) | (1<<6) | (1<<5);
+ u32 tmp;
+ int fix_apm_sq = (hpriv->hp_flags & MV_HP_ERRATA_50XXB0);
+
+ if (fix_apm_sq) {
+ tmp = readl(phy_mmio + MV5_LT_MODE);
+ tmp |= (1 << 19);
+ writel(tmp, phy_mmio + MV5_LT_MODE);
+
+ tmp = readl(phy_mmio + MV5_PHY_CTL);
+ tmp &= ~0x3;
+ tmp |= 0x1;
+ writel(tmp, phy_mmio + MV5_PHY_CTL);
+ }
+
+ tmp = readl(phy_mmio + MV5_PHY_MODE);
+ tmp &= ~mask;
+ tmp |= hpriv->signal[port].pre;
+ tmp |= hpriv->signal[port].amps;
+ writel(tmp, phy_mmio + MV5_PHY_MODE);
+}
+
+
+#undef ZERO
+#define ZERO(reg) writel(0, port_mmio + (reg))
+static void mv5_reset_hc_port(struct mv_host_priv *hpriv, void __iomem *mmio,
+ unsigned int port)
+{
+ void __iomem *port_mmio = mv_port_base(mmio, port);
+
+ writelfl(EDMA_DS, port_mmio + EDMA_CMD_OFS);
+
+ mv_channel_reset(hpriv, mmio, port);
+
+ ZERO(0x028); /* command */
+ writel(0x11f, port_mmio + EDMA_CFG_OFS);
+ ZERO(0x004); /* timer */
+ ZERO(0x008); /* irq err cause */
+ ZERO(0x00c); /* irq err mask */
+ ZERO(0x010); /* rq bah */
+ ZERO(0x014); /* rq inp */
+ ZERO(0x018); /* rq outp */
+ ZERO(0x01c); /* respq bah */
+ ZERO(0x024); /* respq outp */
+ ZERO(0x020); /* respq inp */
+ ZERO(0x02c); /* test control */
+ writel(0xbc, port_mmio + EDMA_IORDY_TMOUT);
}
+#undef ZERO
-static int mv5_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio)
+#define ZERO(reg) writel(0, hc_mmio + (reg))
+static void mv5_reset_one_hc(struct mv_host_priv *hpriv, void __iomem *mmio,
+ unsigned int hc)
{
- /* FIXME */
- return 1;
+ void __iomem *hc_mmio = mv_hc_base(mmio, hc);
+ u32 tmp;
+
+ ZERO(0x00c);
+ ZERO(0x010);
+ ZERO(0x014);
+ ZERO(0x018);
+
+ tmp = readl(hc_mmio + 0x20);
+ tmp &= 0x1c1c1c1c;
+ tmp |= 0x03030303;
+ writel(tmp, hc_mmio + 0x20);
+}
+#undef ZERO
+
+static int mv5_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio,
+ unsigned int n_hc)
+{
+ unsigned int hc, port;
+
+ for (hc = 0; hc < n_hc; hc++) {
+ for (port = 0; port < MV_PORTS_PER_HC; port++)
+ mv5_reset_hc_port(hpriv, mmio,
+ (hc * MV_PORTS_PER_HC) + port);
+
+ mv5_reset_one_hc(hpriv, mmio, hc);
+ }
+
+ return 0;
}
#undef ZERO
@@ -1335,7 +1517,8 @@ static void mv6_reset_flash(struct mv_host_priv *hpriv, void __iomem *mmio)
* LOCKING:
* Inherited from caller.
*/
-static int mv6_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio)
+static int mv6_reset_hc(struct mv_host_priv *hpriv, void __iomem *mmio,
+ unsigned int n_hc)
{
void __iomem *reg = mmio + PCI_MAIN_CMD_STS_OFS;
int i, rc = 0;
@@ -1415,9 +1598,11 @@ static void mv6_enable_leds(struct mv_host_priv *hpriv, void __iomem *mmio)
writel(0x00000060, mmio + MV_GPIO_PORT_CTL);
}
-static void mv6_phy_errata(struct mv_host_priv *hpriv, void __iomem *port_mmio,
+static void mv6_phy_errata(struct mv_host_priv *hpriv, void __iomem *mmio,
unsigned int port)
{
+ void __iomem *port_mmio = mv_port_base(mmio, port);
+
u32 hp_flags = hpriv->hp_flags;
int fix_phy_mode2 =
hp_flags & (MV_HP_ERRATA_60X1B2 | MV_HP_ERRATA_60X1C0);
@@ -1473,6 +1658,44 @@ static void mv6_phy_errata(struct mv_host_priv *hpriv, void __iomem *port_mmio,
writel(m2, port_mmio + PHY_MODE2);
}
+static void mv_channel_reset(struct mv_host_priv *hpriv, void __iomem *mmio,
+ unsigned int port_no)
+{
+ void __iomem *port_mmio = mv_port_base(mmio, port_no);
+
+ writelfl(ATA_RST, port_mmio + EDMA_CMD_OFS);
+
+ if (IS_60XX(hpriv)) {
+ u32 ifctl = readl(port_mmio + SATA_INTERFACE_CTL);
+ ifctl |= (1 << 12) | (1 << 7);
+ writelfl(ifctl, port_mmio + SATA_INTERFACE_CTL);
+ }
+
+ udelay(25); /* allow reset propagation */
+
+ /* Spec never mentions clearing the bit. Marvell's driver does
+ * clear the bit, however.
+ */
+ writelfl(0, port_mmio + EDMA_CMD_OFS);
+
+ hpriv->ops->phy_errata(hpriv, mmio, port_no);
+
+ if (IS_50XX(hpriv))
+ mdelay(1);
+}
+
+static void mv_stop_and_reset(struct ata_port *ap)
+{
+ struct mv_host_priv *hpriv = ap->host_set->private_data;
+ void __iomem *mmio = ap->host_set->mmio_base;
+
+ mv_stop_dma(ap);
+
+ mv_channel_reset(hpriv, mmio, ap->port_no);
+
+ mv_phy_reset(ap);
+}
+
/**
* mv_phy_reset - Perform eDMA reset followed by COMRESET
* @ap: ATA channel to manipulate
@@ -1487,7 +1710,6 @@ static void mv6_phy_errata(struct mv_host_priv *hpriv, void __iomem *port_mmio,
static void mv_phy_reset(struct ata_port *ap)
{
struct mv_port_priv *pp = ap->private_data;
- struct mv_host_priv *hpriv = ap->host_set->private_data;
void __iomem *port_mmio = mv_ap_base(ap);
struct ata_taskfile tf;
struct ata_device *dev = &ap->device[0];
@@ -1495,25 +1717,6 @@ static void mv_phy_reset(struct ata_port *ap)
VPRINTK("ENTER, port %u, mmio 0x%p\n", ap->port_no, port_mmio);
- mv_stop_dma(ap);
-
- writelfl(ATA_RST, port_mmio + EDMA_CMD_OFS);
-
- if (IS_60XX(hpriv)) {
- u32 ifctl = readl(port_mmio + SATA_INTERFACE_CTL);
- ifctl |= (1 << 12) | (1 << 7);
- writelfl(ifctl, port_mmio + SATA_INTERFACE_CTL);
- }
-
- udelay(25); /* allow reset propagation */
-
- /* Spec never mentions clearing the bit. Marvell's driver does
- * clear the bit, however.
- */
- writelfl(0, port_mmio + EDMA_CMD_OFS);
-
- hpriv->ops->phy_errata(hpriv, port_mmio, ap->port_no);
-
DPRINTK("S-regs after ATA_RST: SStat 0x%08x SErr 0x%08x "
"SCtrl 0x%08x\n", mv_scr_read(ap, SCR_STATUS),
mv_scr_read(ap, SCR_ERROR), mv_scr_read(ap, SCR_CONTROL));
@@ -1589,7 +1792,7 @@ static void mv_eng_timeout(struct ata_port *ap)
&qc->scsicmd->cmnd);
mv_err_intr(ap);
- mv_phy_reset(ap);
+ mv_stop_and_reset(ap);
if (!qc) {
printk(KERN_ERR "ata%u: BUG: timeout without command\n",
@@ -1765,7 +1968,7 @@ static int mv_init_host(struct pci_dev *pdev, struct ata_probe_ent *probe_ent,
for (port = 0; port < probe_ent->n_ports; port++)
hpriv->ops->read_preamp(hpriv, port, mmio);
- rc = hpriv->ops->reset_hc(hpriv, mmio);
+ rc = hpriv->ops->reset_hc(hpriv, mmio, n_hc);
if (rc)
goto done;
@@ -1774,15 +1977,15 @@ static int mv_init_host(struct pci_dev *pdev, struct ata_probe_ent *probe_ent,
hpriv->ops->enable_leds(hpriv, mmio);
for (port = 0; port < probe_ent->n_ports; port++) {
- void __iomem *port_mmio = mv_port_base(mmio, port);
-
if (IS_60XX(hpriv)) {
+ void __iomem *port_mmio = mv_port_base(mmio, port);
+
u32 ifctl = readl(port_mmio + SATA_INTERFACE_CTL);
ifctl |= (1 << 12);
writelfl(ifctl, port_mmio + SATA_INTERFACE_CTL);
}
- hpriv->ops->phy_errata(hpriv, port_mmio, port);
+ hpriv->ops->phy_errata(hpriv, mmio, port);
}
for (port = 0; port < probe_ent->n_ports; port++) {