]> err.no Git - linux-2.6/blobdiff - drivers/scsi/sata_sil24.c
Merge branch 'master'
[linux-2.6] / drivers / scsi / sata_sil24.c
index 7a3d50dcd93854dbdcf8e33e03aa6744d4e06300..19857814d69fbe25874b19565b58115896cc4128 100644 (file)
@@ -41,9 +41,7 @@
 #include <asm/io.h>
 
 #define DRV_NAME       "sata_sil24"
-#define DRV_VERSION    "0.20"  /* Silicon Image's preview driver was 0.10 */
-
-#define NR_PORTS       4
+#define DRV_VERSION    "0.22"  /* Silicon Image's preview driver was 0.10 */
 
 /*
  * Port request block (PRB) 32 bytes
@@ -107,14 +105,14 @@ enum {
 
        PORT_PM                 = 0x0f80, /* 8 bytes PM * 16 (128 bytes) */
                /* 32 bit regs */
-       PORT_CTRL_STAT          = 0x1000, /* write:ctrl, read:stat */
-       PORT_CTRL_CLR           = 0x1004,
-       PORT_IRQ_STAT           = 0x1008,
-       PORT_IRQ_ENABLE_SET     = 0x1010,
-       PORT_IRQ_ENABLE_CLR     = 0x1014,
+       PORT_CTRL_STAT          = 0x1000, /* write: ctrl-set, read: stat */
+       PORT_CTRL_CLR           = 0x1004, /* write: ctrl-clear */
+       PORT_IRQ_STAT           = 0x1008, /* high: status, low: interrupt */
+       PORT_IRQ_ENABLE_SET     = 0x1010, /* write: enable-set */
+       PORT_IRQ_ENABLE_CLR     = 0x1014, /* write: enable-clear */
        PORT_ACTIVATE_UPPER_ADDR= 0x101c,
-       PORT_EXEC_FIFO          = 0x1020,
-       PORT_CMD_ERR            = 0x1024,
+       PORT_EXEC_FIFO          = 0x1020, /* command execution fifo */
+       PORT_CMD_ERR            = 0x1024, /* command error number */
        PORT_FIS_CFG            = 0x1028,
        PORT_FIFO_THRES         = 0x102c,
                /* 16 bit regs */
@@ -187,7 +185,7 @@ enum {
        PORT_CERR_XFR_TGTABRT   = 33, /* PSD ecode 01 - target abort */
        PORT_CERR_XFR_MSGABRT   = 34, /* PSD ecode 10 - master abort */
        PORT_CERR_XFR_PCIPERR   = 35, /* PSD ecode 11 - PCI prity err during transfer */
-       PORT_CERR_SENDSERVICE   = 36, /* FIS received whiel sending service */
+       PORT_CERR_SENDSERVICE   = 36, /* FIS received while sending service */
 
        /*
         * Other constants
@@ -198,6 +196,7 @@ enum {
        /* board id */
        BID_SIL3124             = 0,
        BID_SIL3132             = 1,
+       BID_SIL3131             = 2,
 
        IRQ_STAT_4PORTS         = 0xf,
 };
@@ -216,6 +215,7 @@ struct sil24_cmd_block {
 struct sil24_port_priv {
        struct sil24_cmd_block *cmd_block;      /* 32 cmd blocks */
        dma_addr_t cmd_block_dma;               /* DMA base addr for them */
+       struct ata_taskfile tf;                 /* Cached taskfile registers */
 };
 
 /* ap->host_set->private_data */
@@ -228,6 +228,7 @@ static u8 sil24_check_status(struct ata_port *ap);
 static u8 sil24_check_err(struct ata_port *ap);
 static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg);
 static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val);
+static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf);
 static void sil24_phy_reset(struct ata_port *ap);
 static void sil24_qc_prep(struct ata_queued_cmd *qc);
 static int sil24_qc_issue(struct ata_queued_cmd *qc);
@@ -242,6 +243,9 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 static struct pci_device_id sil24_pci_tbl[] = {
        { 0x1095, 0x3124, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3124 },
        { 0x1095, 0x3132, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3132 },
+       { 0x1095, 0x3131, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3131 },
+       { 0x1095, 0x3531, PCI_ANY_ID, PCI_ANY_ID, 0, 0, BID_SIL3131 },
+       { } /* terminate list */
 };
 
 static struct pci_driver sil24_pci_driver = {
@@ -279,6 +283,8 @@ static struct ata_port_operations sil24_ops = {
        .check_err              = sil24_check_err,
        .dev_select             = ata_noop_dev_select,
 
+       .tf_read                = sil24_tf_read,
+
        .phy_reset              = sil24_phy_reset,
 
        .qc_prep                = sil24_qc_prep,
@@ -297,13 +303,20 @@ static struct ata_port_operations sil24_ops = {
        .host_stop              = sil24_host_stop,
 };
 
+/*
+ * Use bits 30-31 of host_flags to encode available port numbers.
+ * Current maxium is 4.
+ */
+#define SIL24_NPORTS2FLAG(nports)      ((((unsigned)(nports) - 1) & 0x3) << 30)
+#define SIL24_FLAG2NPORTS(flag)                ((((flag) >> 30) & 0x3) + 1)
+
 static struct ata_port_info sil24_port_info[] = {
        /* sil_3124 */
        {
                .sht            = &sil24_sht,
                .host_flags     = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
                                  ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
-                                 ATA_FLAG_PIO_DMA,
+                                 ATA_FLAG_PIO_DMA | SIL24_NPORTS2FLAG(4),
                .pio_mask       = 0x1f,                 /* pio0-4 */
                .mwdma_mask     = 0x07,                 /* mwdma0-2 */
                .udma_mask      = 0x3f,                 /* udma0-5 */
@@ -314,7 +327,18 @@ static struct ata_port_info sil24_port_info[] = {
                .sht            = &sil24_sht,
                .host_flags     = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
                                  ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
-                                 ATA_FLAG_PIO_DMA,
+                                 ATA_FLAG_PIO_DMA | SIL24_NPORTS2FLAG(2),
+               .pio_mask       = 0x1f,                 /* pio0-4 */
+               .mwdma_mask     = 0x07,                 /* mwdma0-2 */
+               .udma_mask      = 0x3f,                 /* udma0-5 */
+               .port_ops       = &sil24_ops,
+       },
+       /* sil_3131/sil_3531 */
+       {
+               .sht            = &sil24_sht,
+               .host_flags     = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+                                 ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
+                                 ATA_FLAG_PIO_DMA | SIL24_NPORTS2FLAG(1),
                .pio_mask       = 0x1f,                 /* pio0-4 */
                .mwdma_mask     = 0x07,                 /* mwdma0-2 */
                .udma_mask      = 0x3f,                 /* udma0-5 */
@@ -322,14 +346,25 @@ static struct ata_port_info sil24_port_info[] = {
        },
 };
 
+static inline void sil24_update_tf(struct ata_port *ap)
+{
+       struct sil24_port_priv *pp = ap->private_data;
+       void *port = (void *)ap->ioaddr.cmd_addr;
+       struct sil24_prb *prb = port;
+
+       ata_tf_from_fis(prb->fis, &pp->tf);
+}
+
 static u8 sil24_check_status(struct ata_port *ap)
 {
-       return ATA_DRDY;
+       struct sil24_port_priv *pp = ap->private_data;
+       return pp->tf.command;
 }
 
 static u8 sil24_check_err(struct ata_port *ap)
 {
-       return 0;
+       struct sil24_port_priv *pp = ap->private_data;
+       return pp->tf.feature;
 }
 
 static int sil24_scr_map[] = {
@@ -360,6 +395,12 @@ static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val)
        }
 }
 
+static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf)
+{
+       struct sil24_port_priv *pp = ap->private_data;
+       *tf = pp->tf;
+}
+
 static void sil24_phy_reset(struct ata_port *ap)
 {
        __sata_phy_reset(ap);
@@ -426,15 +467,11 @@ static void sil24_irq_clear(struct ata_port *ap)
        /* unused */
 }
 
-static void sil24_reset_controller(struct ata_port *ap)
+static int __sil24_reset_controller(void *port)
 {
-       void *port = (void *)ap->ioaddr.cmd_addr;
        int cnt;
        u32 tmp;
 
-       printk(KERN_NOTICE DRV_NAME
-              " ata%u: resetting controller...\n", ap->id);
-
        /* Reset controller state.  Is this correct? */
        writel(PORT_CS_DEV_RST, port + PORT_CTRL_STAT);
        readl(port + PORT_CTRL_STAT);   /* sync */
@@ -446,9 +483,19 @@ static void sil24_reset_controller(struct ata_port *ap)
                if (!(tmp & PORT_CS_DEV_RST))
                        break;
        }
+
        if (tmp & PORT_CS_DEV_RST)
-               printk(KERN_ERR DRV_NAME
-                      " ata%u: failed to reset controller\n", ap->id);
+               return -1;
+       return 0;
+}
+
+static void sil24_reset_controller(struct ata_port *ap)
+{
+       printk(KERN_NOTICE DRV_NAME
+              " ata%u: resetting controller...\n", ap->id);
+       if (__sil24_reset_controller((void *)ap->ioaddr.cmd_addr))
+                printk(KERN_ERR DRV_NAME
+                       " ata%u: failed to reset controller\n", ap->id);
 }
 
 static void sil24_eng_timeout(struct ata_port *ap)
@@ -479,18 +526,24 @@ static void sil24_eng_timeout(struct ata_port *ap)
 static void sil24_error_intr(struct ata_port *ap, u32 slot_stat)
 {
        struct ata_queued_cmd *qc = ata_qc_from_tag(ap, ap->active_tag);
+       struct sil24_port_priv *pp = ap->private_data;
        void *port = (void *)ap->ioaddr.cmd_addr;
        u32 irq_stat, cmd_err, sstatus, serror;
 
        irq_stat = readl(port + PORT_IRQ_STAT);
+       writel(irq_stat, port + PORT_IRQ_STAT);         /* clear irq */
+
+       if (!(irq_stat & PORT_IRQ_ERROR)) {
+               /* ignore non-completion, non-error irqs for now */
+               printk(KERN_WARNING DRV_NAME
+                      "ata%u: non-error exception irq (irq_stat %x)\n",
+                      ap->id, irq_stat);
+               return;
+       }
+
        cmd_err = readl(port + PORT_CMD_ERR);
        sstatus = readl(port + PORT_SSTATUS);
        serror = readl(port + PORT_SERROR);
-
-       /* Clear IRQ/errors */
-       writel(irq_stat, port + PORT_IRQ_STAT);
-       if (cmd_err)
-               writel(cmd_err, port + PORT_CMD_ERR);
        if (serror)
                writel(serror, port + PORT_SERROR);
 
@@ -498,8 +551,22 @@ static void sil24_error_intr(struct ata_port *ap, u32 slot_stat)
               "  stat=0x%x irq=0x%x cmd_err=%d sstatus=0x%x serror=0x%x\n",
               ap->id, ap->port_no, slot_stat, irq_stat, cmd_err, sstatus, serror);
 
+       if (cmd_err == PORT_CERR_DEV || cmd_err == PORT_CERR_SDB) {
+               /*
+                * Device is reporting error, tf registers are valid.
+                */
+               sil24_update_tf(ap);
+       } else {
+               /*
+                * Other errors.  libata currently doesn't have any
+                * mechanism to report these errors.  Just turn on
+                * ATA_ERR.
+                */
+               pp->tf.command = ATA_ERR;
+       }
+
        if (qc)
-               ata_qc_complete(qc, ATA_ERR);
+               ata_qc_complete(qc, pp->tf.command);
 
        sil24_reset_controller(ap);
 }
@@ -512,8 +579,19 @@ static inline void sil24_host_intr(struct ata_port *ap)
 
        slot_stat = readl(port + PORT_SLOT_STAT);
        if (!(slot_stat & HOST_SSTAT_ATTN)) {
+               struct sil24_port_priv *pp = ap->private_data;
+               /*
+                * !HOST_SSAT_ATTN guarantees successful completion,
+                * so reading back tf registers is unnecessary for
+                * most commands.  TODO: read tf registers for
+                * commands which require these values on successful
+                * completion (EXECUTE DEVICE DIAGNOSTIC, CHECK POWER,
+                * DEVICE RESET and READ PORT MULTIPLIER (any more?).
+                */
+               sil24_update_tf(ap);
+
                if (qc)
-                       ata_qc_complete(qc, 0);
+                       ata_qc_complete(qc, pp->tf.command);
        } else
                sil24_error_intr(ap, slot_stat);
 }
@@ -568,6 +646,8 @@ static int sil24_port_start(struct ata_port *ap)
                return -ENOMEM;
        memset(pp, 0, sizeof(*pp));
 
+       pp->tf.command = ATA_DRDY;
+
        cb = dma_alloc_coherent(dev, cb_size, &cb_dma, GFP_KERNEL);
        if (!cb) {
                kfree(pp);
@@ -606,6 +686,7 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        static int printed_version = 0;
        unsigned int board_id = (unsigned int)ent->driver_data;
+       struct ata_port_info *pinfo = &sil24_port_info[board_id];
        struct ata_probe_ent *probe_ent = NULL;
        struct sil24_host_priv *hpriv = NULL;
        void *host_base = NULL, *port_base = NULL;
@@ -646,12 +727,12 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        probe_ent->dev = pci_dev_to_dev(pdev);
        INIT_LIST_HEAD(&probe_ent->node);
 
-       probe_ent->sht          = sil24_port_info[board_id].sht;
-       probe_ent->host_flags   = sil24_port_info[board_id].host_flags;
-       probe_ent->pio_mask     = sil24_port_info[board_id].pio_mask;
-       probe_ent->udma_mask    = sil24_port_info[board_id].udma_mask;
-       probe_ent->port_ops     = sil24_port_info[board_id].port_ops;
-       probe_ent->n_ports      = (board_id == BID_SIL3124) ? 4 : 2;
+       probe_ent->sht          = pinfo->sht;
+       probe_ent->host_flags   = pinfo->host_flags;
+       probe_ent->pio_mask     = pinfo->pio_mask;
+       probe_ent->udma_mask    = pinfo->udma_mask;
+       probe_ent->port_ops     = pinfo->port_ops;
+       probe_ent->n_ports      = SIL24_FLAG2NPORTS(pinfo->host_flags);
 
        probe_ent->irq = pdev->irq;
        probe_ent->irq_flags = SA_SHIRQ;
@@ -740,6 +821,15 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
                /* Clear interrupts */
                writel(0x0fff0fff, port + PORT_IRQ_STAT);
                writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR);
+
+               /* Clear port multiplier enable and resume bits */
+               writel(PORT_CS_PM_EN | PORT_CS_RESUME, port + PORT_CTRL_CLR);
+
+               /* Reset itself */
+               if (__sil24_reset_controller(port))
+                       printk(KERN_ERR DRV_NAME
+                              "(%s): failed to reset controller\n",
+                              pci_name(pdev));
        }
 
        /* Turn on interrupts */