Commit c0835b83 authored by Jeff Garzik's avatar Jeff Garzik

[libata ahci] fix several bugs

  
* PCI IDs from test version didn't make it into mainline... doh
* do all command setup in ->qc_prep
* phy_reset routine that does signature check
* check SATA phy for errors
* reset hardware from scratch, in case card BIOS didn't run
* implement staggered spinup by default
* adding additional debugging output
parent 971db06e
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
#include <asm/io.h> #include <asm/io.h>
#define DRV_NAME "ahci" #define DRV_NAME "ahci"
#define DRV_VERSION "0.10" #define DRV_VERSION "0.11"
enum { enum {
...@@ -52,6 +52,7 @@ enum { ...@@ -52,6 +52,7 @@ enum {
AHCI_PORT_PRIV_DMA_SZ = AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_SZ + AHCI_PORT_PRIV_DMA_SZ = AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_SZ +
AHCI_RX_FIS_SZ, AHCI_RX_FIS_SZ,
AHCI_IRQ_ON_SG = (1 << 31), AHCI_IRQ_ON_SG = (1 << 31),
AHCI_CMD_ATAPI = (1 << 5),
AHCI_CMD_WRITE = (1 << 6), AHCI_CMD_WRITE = (1 << 6),
RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */ RX_FIS_D2H_REG = 0x40, /* offset of D2H Register FIS data */
...@@ -82,8 +83,10 @@ enum { ...@@ -82,8 +83,10 @@ enum {
PORT_IRQ_MASK = 0x14, /* interrupt enable/disable mask */ PORT_IRQ_MASK = 0x14, /* interrupt enable/disable mask */
PORT_CMD = 0x18, /* port command */ PORT_CMD = 0x18, /* port command */
PORT_TFDATA = 0x20, /* taskfile data */ PORT_TFDATA = 0x20, /* taskfile data */
PORT_SIG = 0x24, /* device TF signature */
PORT_CMD_ISSUE = 0x38, /* command issue */ PORT_CMD_ISSUE = 0x38, /* command issue */
PORT_SCR = 0x28, /* SATA phy register block */ PORT_SCR = 0x28, /* SATA phy register block */
PORT_SCR_STAT = 0x28, /* SATA phy register: SStatus */
PORT_SCR_CTL = 0x2c, /* SATA phy register: SControl */ PORT_SCR_CTL = 0x2c, /* SATA phy register: SControl */
PORT_SCR_ERR = 0x30, /* SATA phy register: SError */ PORT_SCR_ERR = 0x30, /* SATA phy register: SError */
...@@ -161,9 +164,9 @@ struct ahci_port_priv { ...@@ -161,9 +164,9 @@ struct ahci_port_priv {
static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg); static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg);
static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
static void ahci_dma_setup(struct ata_queued_cmd *qc); static int ahci_qc_issue(struct ata_queued_cmd *qc);
static void ahci_dma_start(struct ata_queued_cmd *qc);
static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs); static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs);
static void ahci_phy_reset(struct ata_port *ap);
static void ahci_irq_clear(struct ata_port *ap); static void ahci_irq_clear(struct ata_port *ap);
static void ahci_eng_timeout(struct ata_port *ap); static void ahci_eng_timeout(struct ata_port *ap);
static int ahci_port_start(struct ata_port *ap); static int ahci_port_start(struct ata_port *ap);
...@@ -202,13 +205,12 @@ static struct ata_port_operations ahci_ops = { ...@@ -202,13 +205,12 @@ static struct ata_port_operations ahci_ops = {
.tf_read = ahci_tf_read, .tf_read = ahci_tf_read,
.check_status = ahci_check_status, .check_status = ahci_check_status,
.exec_command = ahci_exec_command, .exec_command = ahci_exec_command,
.dev_select = ata_noop_dev_select,
.phy_reset = sata_phy_reset, .phy_reset = ahci_phy_reset,
.bmdma_setup = ahci_dma_setup,
.bmdma_start = ahci_dma_start,
.qc_prep = ahci_qc_prep, .qc_prep = ahci_qc_prep,
.qc_issue = ata_qc_issue_prot, .qc_issue = ahci_qc_issue,
.eng_timeout = ahci_eng_timeout, .eng_timeout = ahci_eng_timeout,
...@@ -236,7 +238,9 @@ static struct ata_port_info ahci_port_info[] = { ...@@ -236,7 +238,9 @@ static struct ata_port_info ahci_port_info[] = {
}; };
static struct pci_device_id ahci_pci_tbl[] = { static struct pci_device_id ahci_pci_tbl[] = {
{ PCI_VENDOR_ID_INTEL, 0x0000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, { PCI_VENDOR_ID_INTEL, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
board_ahci },
{ PCI_VENDOR_ID_INTEL, 0x2653, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
board_ahci }, board_ahci },
{ } /* terminate list */ { } /* terminate list */
}; };
...@@ -292,6 +296,7 @@ static int ahci_port_start(struct ata_port *ap) ...@@ -292,6 +296,7 @@ static int ahci_port_start(struct ata_port *ap)
rc = -ENOMEM; rc = -ENOMEM;
goto err_out_kfree; goto err_out_kfree;
} }
memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ);
pp->rx_fis = mem; pp->rx_fis = mem;
pp->rx_fis_dma = mem_dma; pp->rx_fis_dma = mem_dma;
...@@ -302,7 +307,7 @@ static int ahci_port_start(struct ata_port *ap) ...@@ -302,7 +307,7 @@ static int ahci_port_start(struct ata_port *ap)
pp->cmd_tbl = mem; pp->cmd_tbl = mem;
pp->cmd_tbl_dma = mem_dma; pp->cmd_tbl_dma = mem_dma;
mem2 = mem + 128; mem2 = mem + 0x80;
pp->cmd_tbl_sg = mem2; pp->cmd_tbl_sg = mem2;
mem += AHCI_CMD_TBL_SZ; mem += AHCI_CMD_TBL_SZ;
...@@ -398,6 +403,29 @@ static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in, ...@@ -398,6 +403,29 @@ static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in,
writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4)); writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4));
} }
static void ahci_phy_reset(struct ata_port *ap)
{
void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
struct ata_taskfile tf;
struct ata_device *dev = &ap->device[0];
u32 tmp;
__sata_phy_reset(ap);
if (ap->flags & ATA_FLAG_PORT_DISABLED)
return;
tmp = readl(port_mmio + PORT_SIG);
tf.lbah = (tmp >> 24) & 0xff;
tf.lbam = (tmp >> 16) & 0xff;
tf.lbal = (tmp >> 8) & 0xff;
tf.nsect = (tmp) & 0xff;
dev->class = ata_dev_classify(&tf);
if (!ata_dev_present(dev))
ata_port_disable(ap);
}
static u8 ahci_check_status(struct ata_port *ap) static u8 ahci_check_status(struct ata_port *ap)
{ {
void *mmio = (void *) ap->ioaddr.cmd_addr; void *mmio = (void *) ap->ioaddr.cmd_addr;
...@@ -424,7 +452,7 @@ static void ahci_fill_sg(struct ata_queued_cmd *qc) ...@@ -424,7 +452,7 @@ static void ahci_fill_sg(struct ata_queued_cmd *qc)
pp->cmd_tbl_sg[i].addr = cpu_to_le32(addr & 0xffffffff); pp->cmd_tbl_sg[i].addr = cpu_to_le32(addr & 0xffffffff);
pp->cmd_tbl_sg[i].addr_hi = cpu_to_le32((addr >> 16) >> 16); pp->cmd_tbl_sg[i].addr_hi = cpu_to_le32((addr >> 16) >> 16);
pp->cmd_tbl_sg[i].flags_size = cpu_to_le32(sg_len); pp->cmd_tbl_sg[i].flags_size = cpu_to_le32(sg_len - 1);
} }
} }
...@@ -442,6 +470,19 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc) ...@@ -442,6 +470,19 @@ static void ahci_qc_prep(struct ata_queued_cmd *qc)
opts = (qc->n_elem << 16) | cmd_fis_len; opts = (qc->n_elem << 16) | cmd_fis_len;
if (qc->tf.flags & ATA_TFLAG_WRITE) if (qc->tf.flags & ATA_TFLAG_WRITE)
opts |= AHCI_CMD_WRITE; opts |= AHCI_CMD_WRITE;
switch (qc->tf.protocol) {
case ATA_PROT_ATAPI:
case ATA_PROT_ATAPI_NODATA:
case ATA_PROT_ATAPI_DMA:
opts |= AHCI_CMD_ATAPI;
break;
default:
/* do nothing */
break;
}
pp->cmd_slot->opts = cpu_to_le32(opts); pp->cmd_slot->opts = cpu_to_le32(opts);
pp->cmd_slot->status = 0; pp->cmd_slot->status = 0;
pp->cmd_slot->tbl_addr = cpu_to_le32(pp->cmd_tbl_dma & 0xffffffff); pp->cmd_slot->tbl_addr = cpu_to_le32(pp->cmd_tbl_dma & 0xffffffff);
...@@ -547,11 +588,12 @@ static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc) ...@@ -547,11 +588,12 @@ static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
{ {
void *mmio = ap->host_set->mmio_base; void *mmio = ap->host_set->mmio_base;
void *port_mmio = ahci_port_base(mmio, ap->port_no); void *port_mmio = ahci_port_base(mmio, ap->port_no);
u32 status, ci; u32 status, serr, ci;
serr = readl(port_mmio + PORT_SCR_ERR);
writel(serr, port_mmio + PORT_SCR_ERR);
status = readl(port_mmio + PORT_IRQ_STAT); status = readl(port_mmio + PORT_IRQ_STAT);
if (!status)
return 0;
writel(status, port_mmio + PORT_IRQ_STAT); writel(status, port_mmio + PORT_IRQ_STAT);
ci = readl(port_mmio + PORT_CMD_ISSUE); ci = readl(port_mmio + PORT_CMD_ISSUE);
...@@ -624,18 +666,15 @@ static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs * ...@@ -624,18 +666,15 @@ static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *
return IRQ_RETVAL(handled); return IRQ_RETVAL(handled);
} }
static void ahci_dma_setup(struct ata_queued_cmd *qc) static int ahci_qc_issue(struct ata_queued_cmd *qc)
{
/* do nothing... for now */
}
static void ahci_dma_start(struct ata_queued_cmd *qc)
{ {
struct ata_port *ap = qc->ap; struct ata_port *ap = qc->ap;
void *mmio = (void *) ap->ioaddr.cmd_addr; void *mmio = (void *) ap->ioaddr.cmd_addr;
writel(1, mmio + PORT_CMD_ISSUE); writel(1, mmio + PORT_CMD_ISSUE);
readl(mmio + PORT_CMD_ISSUE); /* flush */ readl(mmio + PORT_CMD_ISSUE); /* flush */
return 0;
} }
static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf) static void ahci_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
...@@ -663,20 +702,30 @@ static void ahci_exec_command(struct ata_port *ap, struct ata_taskfile *tf) ...@@ -663,20 +702,30 @@ static void ahci_exec_command(struct ata_port *ap, struct ata_taskfile *tf)
static void ahci_setup_port(struct ata_ioports *port, unsigned long base, static void ahci_setup_port(struct ata_ioports *port, unsigned long base,
unsigned int port_idx) unsigned int port_idx)
{ {
VPRINTK("ENTER, base==0x%lx, port_idx %u\n", base, port_idx);
base = ahci_port_base_ul(base, port_idx); base = ahci_port_base_ul(base, port_idx);
VPRINTK("base now==0x%lx\n", base);
port->cmd_addr = base; port->cmd_addr = base;
port->scr_addr = base + PORT_SCR; port->scr_addr = base + PORT_SCR;
VPRINTK("EXIT\n");
} }
static int ahci_host_init(struct ata_probe_ent *probe_ent) static int ahci_host_init(struct ata_probe_ent *probe_ent)
{ {
struct ahci_host_priv *hpriv = probe_ent->private_data; struct ahci_host_priv *hpriv = probe_ent->private_data;
struct pci_dev *pdev = probe_ent->pdev; struct pci_dev *pdev = probe_ent->pdev;
void *mmio = probe_ent->mmio_base; void __iomem *mmio = probe_ent->mmio_base;
u32 tmp; u32 tmp, cap_save;
unsigned int i, using_dac; u16 tmp16;
unsigned int i, j, using_dac;
int rc; int rc;
void __iomem *port_mmio;
cap_save = readl(mmio + HOST_CAP);
cap_save &= ( (1<<28) | (1<<17) );
cap_save |= (1 << 27);
/* global controller reset */ /* global controller reset */
tmp = readl(mmio + HOST_CTL); tmp = readl(mmio + HOST_CTL);
...@@ -698,23 +747,22 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) ...@@ -698,23 +747,22 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent)
return -EIO; return -EIO;
} }
/* make sure we're in AHCI mode, with irq disabled */ writel(HOST_AHCI_EN, mmio + HOST_CTL);
if ((tmp & (HOST_AHCI_EN | HOST_IRQ_EN)) != HOST_AHCI_EN) { (void) readl(mmio + HOST_CTL); /* flush */
tmp |= HOST_AHCI_EN; writel(cap_save, mmio + HOST_CAP);
tmp &= ~HOST_IRQ_EN; writel(0xf, mmio + HOST_PORTS_IMPL);
writel(tmp, mmio + HOST_CTL); (void) readl(mmio + HOST_PORTS_IMPL); /* flush */
readl(mmio + HOST_CTL); /* flush */
} pci_read_config_word(pdev, 0x92, &tmp16);
tmp = readl(mmio + HOST_CTL); tmp16 |= 0xf;
if ((tmp & (HOST_AHCI_EN | HOST_IRQ_EN)) != HOST_AHCI_EN) { pci_write_config_word(pdev, 0x92, tmp16);
printk(KERN_ERR DRV_NAME "(%s): AHCI enable failed (0x%x)\n",
pci_name(pdev), tmp);
return -EIO;
}
hpriv->cap = readl(mmio + HOST_CAP); hpriv->cap = readl(mmio + HOST_CAP);
hpriv->port_map = readl(mmio + HOST_PORTS_IMPL); hpriv->port_map = readl(mmio + HOST_PORTS_IMPL);
probe_ent->n_ports = hpriv->cap & 0x1f; probe_ent->n_ports = (hpriv->cap & 0x1f) + 1;
VPRINTK("cap 0x%x port_map 0x%x n_ports %d\n",
hpriv->cap, hpriv->port_map, probe_ent->n_ports);
using_dac = hpriv->cap & HOST_CAP_64; using_dac = hpriv->cap & HOST_CAP_64;
if (using_dac && if (using_dac &&
...@@ -746,18 +794,18 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) ...@@ -746,18 +794,18 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent)
} }
for (i = 0; i < probe_ent->n_ports; i++) { for (i = 0; i < probe_ent->n_ports; i++) {
void *port_mmio;
if (!(hpriv->port_map & (1 << i))) if (!(hpriv->port_map & (1 << i)))
continue; continue;
port_mmio = ahci_port_base(mmio, i); port_mmio = ahci_port_base(mmio, i);
VPRINTK("mmio %p port_mmio %p\n", mmio, port_mmio);
ahci_setup_port(&probe_ent->port[i], ahci_setup_port(&probe_ent->port[i],
(unsigned long) mmio, i); (unsigned long) mmio, i);
/* make sure port is not active */ /* make sure port is not active */
tmp = readl(port_mmio + PORT_CMD); tmp = readl(port_mmio + PORT_CMD);
VPRINTK("PORT_CMD 0x%x\n", tmp);
if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON | if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
PORT_CMD_FIS_RX | PORT_CMD_START)) { PORT_CMD_FIS_RX | PORT_CMD_START)) {
tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON | tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
...@@ -772,14 +820,27 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) ...@@ -772,14 +820,27 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent)
schedule_timeout((HZ / 2) + 1); schedule_timeout((HZ / 2) + 1);
} }
writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD);
j = 0;
while (j < 100) {
msleep(10);
tmp = readl(port_mmio + PORT_SCR_STAT);
if ((tmp & 0xf) == 0x3)
break;
j++;
}
tmp = readl(port_mmio + PORT_SCR_ERR);
VPRINTK("PORT_SCR_ERR 0x%x\n", tmp);
writel(tmp, port_mmio + PORT_SCR_ERR);
/* ack any pending irq events for this port */ /* ack any pending irq events for this port */
tmp = readl(port_mmio + PORT_IRQ_STAT); tmp = readl(port_mmio + PORT_IRQ_STAT);
VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);
if (tmp) if (tmp)
writel(tmp, port_mmio + PORT_IRQ_STAT); writel(tmp, port_mmio + PORT_IRQ_STAT);
tmp = readl(port_mmio + PORT_SCR_ERR);
writel(tmp, port_mmio + PORT_SCR_ERR);
writel(1 << i, mmio + HOST_IRQ_STAT); writel(1 << i, mmio + HOST_IRQ_STAT);
/* set irq mask (enables interrupts) */ /* set irq mask (enables interrupts) */
...@@ -787,7 +848,10 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) ...@@ -787,7 +848,10 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent)
} }
tmp = readl(mmio + HOST_CTL); tmp = readl(mmio + HOST_CTL);
VPRINTK("HOST_CTL 0x%x\n", tmp);
writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL); writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL);
tmp = readl(mmio + HOST_CTL);
VPRINTK("HOST_CTL 0x%x\n", tmp);
pci_set_master(pdev); pci_set_master(pdev);
...@@ -830,10 +894,12 @@ static void ahci_print_info(struct ata_probe_ent *probe_ent) ...@@ -830,10 +894,12 @@ static void ahci_print_info(struct ata_probe_ent *probe_ent)
"%u slots %u ports %s Gbps 0x%x impl\n" "%u slots %u ports %s Gbps 0x%x impl\n"
, ,
pci_name(pdev), pci_name(pdev),
(vers >> 24) & 0xff, (vers >> 24) & 0xff,
(vers >> 16) & 0xff, (vers >> 16) & 0xff,
(vers >> 8) & 0xff, (vers >> 8) & 0xff,
vers & 0xff, vers & 0xff,
((cap >> 8) & 0x1f) + 1, ((cap >> 8) & 0x1f) + 1,
(cap & 0x1f) + 1, (cap & 0x1f) + 1,
speed_s, speed_s,
...@@ -872,6 +938,8 @@ static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) ...@@ -872,6 +938,8 @@ static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
unsigned int board_idx = (unsigned int) ent->driver_data; unsigned int board_idx = (unsigned int) ent->driver_data;
int rc; int rc;
VPRINTK("ENTER\n");
if (!printed_version++) if (!printed_version++)
printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\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