]> err.no Git - linux-2.6/blobdiff - drivers/scsi/sata_mv.c
Merge branch 'block-dir' of git://brick.kernel.dk/data/git/linux-2.6-block
[linux-2.6] / drivers / scsi / sata_mv.c
index c3084f8b3ee7a2a31486bb4db494c38c4c0e96fd..0f469e3dabe2e5e8fd0d21ced1f9b7e743c396b4 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/sched.h>
 #include <linux/dma-mapping.h>
+#include <linux/device.h>
 #include "scsi.h"
 #include <scsi/scsi_host.h>
 #include <linux/libata.h>
 #include <asm/io.h>
 
 #define DRV_NAME       "sata_mv"
-#define DRV_VERSION    "0.23"
+#define DRV_VERSION    "0.25"
 
 enum {
        /* BAR's are enumerated in terms of pci_resource_start() terms */
@@ -258,7 +259,6 @@ 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 u8 mv_check_err(struct ata_port *ap);
 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);
@@ -290,13 +290,12 @@ static Scsi_Host_Template mv_sht = {
        .ordered_flush          = 1,
 };
 
-static struct ata_port_operations mv_ops = {
+static const struct ata_port_operations mv_ops = {
        .port_disable           = ata_port_disable,
 
        .tf_load                = ata_tf_load,
        .tf_read                = ata_tf_read,
        .check_status           = ata_check_status,
-       .check_err              = mv_check_err,
        .exec_command           = ata_exec_command,
        .dev_select             = ata_std_dev_select,
 
@@ -406,6 +405,17 @@ static void mv_irq_clear(struct ata_port *ap)
 {
 }
 
+/**
+ *      mv_start_dma - Enable eDMA engine
+ *      @base: port base address
+ *      @pp: port private data
+ *
+ *      Verify the local cache of the eDMA state is accurate with an
+ *      assert.
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static void mv_start_dma(void __iomem *base, struct mv_port_priv *pp)
 {
        if (!(MV_PP_FLAG_EDMA_EN & pp->pp_flags)) {
@@ -415,6 +425,16 @@ static void mv_start_dma(void __iomem *base, struct mv_port_priv *pp)
        assert(EDMA_EN & readl(base + EDMA_CMD_OFS));
 }
 
+/**
+ *      mv_stop_dma - Disable eDMA engine
+ *      @ap: ATA channel to manipulate
+ *
+ *      Verify the local cache of the eDMA state is accurate with an
+ *      assert.
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static void mv_stop_dma(struct ata_port *ap)
 {
        void __iomem *port_mmio = mv_ap_base(ap);
@@ -446,9 +466,9 @@ static void mv_stop_dma(struct ata_port *ap)
        }
 }
 
+#ifdef ATA_DEBUG
 static void mv_dump_mem(void __iomem *start, unsigned bytes)
 {
-#ifdef ATA_DEBUG
        int b, w;
        for (b = 0; b < bytes; ) {
                DPRINTK("%p: ", start + b);
@@ -458,8 +478,9 @@ static void mv_dump_mem(void __iomem *start, unsigned bytes)
                }
                printk("\n");
        }
-#endif
 }
+#endif
+
 static void mv_dump_pci_cfg(struct pci_dev *pdev, unsigned bytes)
 {
 #ifdef ATA_DEBUG
@@ -561,7 +582,15 @@ static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val)
        }
 }
 
-/* This routine only applies to 6xxx parts */
+/**
+ *      mv_global_soft_reset - Perform the 6xxx global soft reset
+ *      @mmio_base: base address of the HBA
+ *
+ *      This routine only applies to 6xxx parts.
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static int mv_global_soft_reset(void __iomem *mmio_base)
 {
        void __iomem *reg = mmio_base + PCI_MAIN_CMD_STS_OFS;
@@ -617,6 +646,16 @@ done:
        return rc;
 }
 
+/**
+ *      mv_host_stop - Host specific cleanup/stop routine.
+ *      @host_set: host data structure
+ *
+ *      Disable ints, cleanup host memory, call general purpose
+ *      host_stop.
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static void mv_host_stop(struct ata_host_set *host_set)
 {
        struct mv_host_priv *hpriv = host_set->private_data;
@@ -631,6 +670,21 @@ static void mv_host_stop(struct ata_host_set *host_set)
        ata_host_stop(host_set);
 }
 
+static inline void mv_priv_free(struct mv_port_priv *pp, struct device *dev)
+{
+       dma_free_coherent(dev, MV_PORT_PRIV_DMA_SZ, pp->crpb, pp->crpb_dma);
+}
+
+/**
+ *      mv_port_start - Port specific init/start routine.
+ *      @ap: ATA channel to manipulate
+ *
+ *      Allocate and point to DMA memory, init port private memory,
+ *      zero indices.
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static int mv_port_start(struct ata_port *ap)
 {
        struct device *dev = ap->host_set->dev;
@@ -638,21 +692,23 @@ static int mv_port_start(struct ata_port *ap)
        void __iomem *port_mmio = mv_ap_base(ap);
        void *mem;
        dma_addr_t mem_dma;
+       int rc = -ENOMEM;
 
        pp = kmalloc(sizeof(*pp), GFP_KERNEL);
-       if (!pp) {
-               return -ENOMEM;
-       }
+       if (!pp)
+               goto err_out;
        memset(pp, 0, sizeof(*pp));
 
        mem = dma_alloc_coherent(dev, MV_PORT_PRIV_DMA_SZ, &mem_dma, 
                                 GFP_KERNEL);
-       if (!mem) {
-               kfree(pp);
-               return -ENOMEM;
-       }
+       if (!mem)
+               goto err_out_pp;
        memset(mem, 0, MV_PORT_PRIV_DMA_SZ);
 
+       rc = ata_pad_alloc(ap, dev);
+       if (rc)
+               goto err_out_priv;
+
        /* First item in chunk of DMA memory: 
         * 32-slot command request table (CRQB), 32 bytes each in size
         */
@@ -697,8 +753,24 @@ static int mv_port_start(struct ata_port *ap)
         */
        ap->private_data = pp;
        return 0;
+
+err_out_priv:
+       mv_priv_free(pp, dev);
+err_out_pp:
+       kfree(pp);
+err_out:
+       return rc;
 }
 
+/**
+ *      mv_port_stop - Port specific cleanup/stop routine.
+ *      @ap: ATA channel to manipulate
+ *
+ *      Stop DMA, cleanup port memory.
+ *
+ *      LOCKING:
+ *      This routine uses the host_set lock to protect the DMA stop.
+ */
 static void mv_port_stop(struct ata_port *ap)
 {
        struct device *dev = ap->host_set->dev;
@@ -710,29 +782,41 @@ static void mv_port_stop(struct ata_port *ap)
        spin_unlock_irqrestore(&ap->host_set->lock, flags);
 
        ap->private_data = NULL;
-       dma_free_coherent(dev, MV_PORT_PRIV_DMA_SZ, pp->crpb, pp->crpb_dma);
+       ata_pad_free(ap, dev);
+       mv_priv_free(pp, dev);
        kfree(pp);
 }
 
+/**
+ *      mv_fill_sg - Fill out the Marvell ePRD (scatter gather) entries
+ *      @qc: queued command whose SG list to source from
+ *
+ *      Populate the SG list and mark the last entry.
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static void mv_fill_sg(struct ata_queued_cmd *qc)
 {
        struct mv_port_priv *pp = qc->ap->private_data;
-       unsigned int i;
+       unsigned int i = 0;
+       struct scatterlist *sg;
 
-       for (i = 0; i < qc->n_elem; i++) {
+       ata_for_each_sg(sg, qc) {
                u32 sg_len;
                dma_addr_t addr;
 
-               addr = sg_dma_address(&qc->sg[i]);
-               sg_len = sg_dma_len(&qc->sg[i]);
+               addr = sg_dma_address(sg);
+               sg_len = sg_dma_len(sg);
 
                pp->sg_tbl[i].addr = cpu_to_le32(addr & 0xffffffff);
                pp->sg_tbl[i].addr_hi = cpu_to_le32((addr >> 16) >> 16);
                assert(0 == (sg_len & ~MV_DMA_BOUNDARY));
                pp->sg_tbl[i].flags_size = cpu_to_le32(sg_len);
-       }
-       if (0 < qc->n_elem) {
-               pp->sg_tbl[qc->n_elem - 1].flags_size |= EPRD_FLAG_END_OF_TBL;
+               if (ata_sg_is_last(sg, qc))
+                       pp->sg_tbl[i].flags_size |= cpu_to_le32(EPRD_FLAG_END_OF_TBL);
+
+               i++;
        }
 }
 
@@ -748,6 +832,18 @@ static inline void mv_crqb_pack_cmd(u16 *cmdw, u8 data, u8 addr, unsigned last)
                (last ? CRQB_CMD_LAST : 0);
 }
 
+/**
+ *      mv_qc_prep - Host specific command preparation.
+ *      @qc: queued command to prepare
+ *
+ *      This routine simply redirects to the general purpose routine
+ *      if command is not DMA.  Else, it handles prep of the CRQB
+ *      (command request block), does some sanity checking, and calls
+ *      the SG load routine.
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static void mv_qc_prep(struct ata_queued_cmd *qc)
 {
        struct ata_port *ap = qc->ap;
@@ -830,6 +926,18 @@ static void mv_qc_prep(struct ata_queued_cmd *qc)
        mv_fill_sg(qc);
 }
 
+/**
+ *      mv_qc_issue - Initiate a command to the host
+ *      @qc: queued command to start
+ *
+ *      This routine simply redirects to the general purpose routine
+ *      if command is not DMA.  Else, it sanity checks our local
+ *      caches of the request producer/consumer indices then enables
+ *      DMA and bumps the request producer index.
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static int mv_qc_issue(struct ata_queued_cmd *qc)
 {
        void __iomem *port_mmio = mv_ap_base(qc->ap);
@@ -867,6 +975,19 @@ static int mv_qc_issue(struct ata_queued_cmd *qc)
        return 0;
 }
 
+/**
+ *      mv_get_crpb_status - get status from most recently completed cmd
+ *      @ap: ATA channel to manipulate
+ *
+ *      This routine is for use when the port is in DMA mode, when it
+ *      will be using the CRPB (command response block) method of
+ *      returning command completion information.  We assert indices
+ *      are good, grab status, and bump the response consumer index to
+ *      prove that we're up to date.
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static u8 mv_get_crpb_status(struct ata_port *ap)
 {
        void __iomem *port_mmio = mv_ap_base(ap);
@@ -896,6 +1017,19 @@ static u8 mv_get_crpb_status(struct ata_port *ap)
        return (pp->crpb[pp->rsp_consumer].flags >> CRPB_FLAG_STATUS_SHIFT);
 }
 
+/**
+ *      mv_err_intr - Handle error interrupts on the port
+ *      @ap: ATA channel to manipulate
+ *
+ *      In most cases, just clear the interrupt and move on.  However,
+ *      some cases require an eDMA reset, which is done right before
+ *      the COMRESET in mv_phy_reset().  The SERR case requires a
+ *      clear of pending errors in the SATA SERROR register.  Finally,
+ *      if the port disabled DMA, update our cached copy to match.
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static void mv_err_intr(struct ata_port *ap)
 {
        void __iomem *port_mmio = mv_ap_base(ap);
@@ -923,7 +1057,22 @@ static void mv_err_intr(struct ata_port *ap)
        }
 }
 
-/* Handle any outstanding interrupts in a single SATAHC */
+/**
+ *      mv_host_intr - Handle all interrupts on the given host controller
+ *      @host_set: host specific structure
+ *      @relevant: port error bits relevant to this host controller
+ *      @hc: which host controller we're to look at
+ *
+ *      Read then write clear the HC interrupt status then walk each
+ *      port connected to the HC and see if it needs servicing.  Port
+ *      success ints are reported in the HC interrupt status reg, the
+ *      port error ints are reported in the higher level main
+ *      interrupt status register and thus are passed in via the
+ *      'relevant' argument.
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static void mv_host_intr(struct ata_host_set *host_set, u32 relevant,
                         unsigned int hc)
 {
@@ -933,6 +1082,7 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant,
        struct ata_queued_cmd *qc;
        u32 hc_irq_cause;
        int shift, port, port0, hard_port, handled;
+       unsigned int err_mask;
        u8 ata_status = 0;
 
        if (hc == 0) {
@@ -968,15 +1118,15 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant,
                        handled++;
                }
 
+               err_mask = ac_err_mask(ata_status);
+
                shift = port << 1;              /* (port * 2) */
                if (port >= MV_PORTS_PER_HC) {
                        shift++;        /* skip bit 8 in the HC Main IRQ reg */
                }
                if ((PORT0_ERR << shift) & relevant) {
                        mv_err_intr(ap);
-                       /* OR in ATA_ERR to ensure libata knows we took one */
-                       ata_status = readb((void __iomem *)
-                                          ap->ioaddr.status_addr) | ATA_ERR;
+                       err_mask |= AC_ERR_OTHER;
                        handled++;
                }
                
@@ -986,13 +1136,28 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant,
                                VPRINTK("port %u IRQ found for qc, "
                                        "ata_status 0x%x\n", port,ata_status);
                                /* mark qc status appropriately */
-                               ata_qc_complete(qc, ata_status);
+                               ata_qc_complete(qc, err_mask);
                        }
                }
        }
        VPRINTK("EXIT\n");
 }
 
+/**
+ *      mv_interrupt - 
+ *      @irq: unused
+ *      @dev_instance: private data; in this case the host structure
+ *      @regs: unused
+ *
+ *      Read the read only register to determine if any host
+ *      controllers have pending interrupts.  If so, call lower level
+ *      routine to handle.  Also check for PCI errors which are only
+ *      reported here.
+ *
+ *      LOCKING: 
+ *      This routine holds the host_set lock while processing pending
+ *      interrupts.
+ */
 static irqreturn_t mv_interrupt(int irq, void *dev_instance,
                                struct pt_regs *regs)
 {
@@ -1035,14 +1200,16 @@ static irqreturn_t mv_interrupt(int irq, void *dev_instance,
        return IRQ_RETVAL(handled);
 }
 
-static u8 mv_check_err(struct ata_port *ap)
-{
-       mv_stop_dma(ap);                /* can't read shadow regs if DMA on */
-       return readb((void __iomem *) ap->ioaddr.error_addr);
-}
-
-/* Part of this is taken from __sata_phy_reset and modified to not sleep
- * since this routine gets called from interrupt level.
+/**
+ *      mv_phy_reset - Perform eDMA reset followed by COMRESET
+ *      @ap: ATA channel to manipulate
+ *
+ *      Part of this is taken from __sata_phy_reset and modified to
+ *      not sleep since this routine gets called from interrupt level.
+ *
+ *      LOCKING:
+ *      Inherited from caller.  This is coded to safe to call at
+ *      interrupt level, i.e. it does not sleep.
  */
 static void mv_phy_reset(struct ata_port *ap)
 {
@@ -1105,6 +1272,16 @@ static void mv_phy_reset(struct ata_port *ap)
        VPRINTK("EXIT\n");
 }
 
+/**
+ *      mv_eng_timeout - Routine called by libata when SCSI times out I/O
+ *      @ap: ATA channel to manipulate
+ *
+ *      Intent is to clear all pending error conditions, reset the
+ *      chip/bus, fail the command, and move on.
+ *
+ *      LOCKING:
+ *      This routine holds the host_set lock while failing the command.
+ */
 static void mv_eng_timeout(struct ata_port *ap)
 {
        struct ata_queued_cmd *qc;
@@ -1135,11 +1312,23 @@ static void mv_eng_timeout(struct ata_port *ap)
                 */
                spin_lock_irqsave(&ap->host_set->lock, flags);
                qc->scsidone = scsi_finish_command;
-               ata_qc_complete(qc, ATA_ERR);
+               ata_qc_complete(qc, AC_ERR_OTHER);
                spin_unlock_irqrestore(&ap->host_set->lock, flags);
        }
 }
 
+/**
+ *      mv_port_init - Perform some early initialization on a single port.
+ *      @port: libata data structure storing shadow register addresses
+ *      @port_mmio: base address of the port
+ *
+ *      Initialize shadow register mmio addresses, clear outstanding
+ *      interrupts on the port, and unmask interrupts for the future
+ *      start of the port.
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static void mv_port_init(struct ata_ioports *port,  void __iomem *port_mmio)
 {
        unsigned long shd_base = (unsigned long) port_mmio + SHD_BLK_OFS;
@@ -1177,6 +1366,16 @@ static void mv_port_init(struct ata_ioports *port,  void __iomem *port_mmio)
                readl(port_mmio + EDMA_ERR_IRQ_MASK_OFS));
 }
 
+/**
+ *      mv_host_init - Perform some early initialization of the host.
+ *      @probe_ent: early data struct representing the host
+ *
+ *      If possible, do an early global reset of the host.  Then do
+ *      our port init and clear/unmask all/relevant host interrupts.
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static int mv_host_init(struct ata_probe_ent *probe_ent)
 {
        int rc = 0, n_hc, port, hc;
@@ -1226,7 +1425,15 @@ done:
        return rc;
 }
 
-/* FIXME: complete this */
+/**
+ *      mv_print_info - Dump key info to kernel log for perusal.
+ *      @probe_ent: early data struct representing the host
+ *
+ *      FIXME: complete this.
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static void mv_print_info(struct ata_probe_ent *probe_ent)
 {
        struct pci_dev *pdev = to_pci_dev(probe_ent->dev);
@@ -1247,12 +1454,20 @@ static void mv_print_info(struct ata_probe_ent *probe_ent)
        else
                scc_s = "unknown";
 
-       printk(KERN_INFO DRV_NAME 
-              "(%s) %u slots %u ports %s mode IRQ via %s\n",
-              pci_name(pdev), (unsigned)MV_MAX_Q_DEPTH, probe_ent->n_ports, 
+       dev_printk(KERN_INFO, &pdev->dev,
+              "%u slots %u ports %s mode IRQ via %s\n",
+              (unsigned)MV_MAX_Q_DEPTH, probe_ent->n_ports, 
               scc_s, (MV_HP_FLAG_MSI & hpriv->hp_flags) ? "MSI" : "INTx");
 }
 
+/**
+ *      mv_init_one - handle a positive probe of a Marvell host
+ *      @pdev: PCI device found
+ *      @ent: PCI device ID entry for the matched host
+ *
+ *      LOCKING:
+ *      Inherited from caller.
+ */
 static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
        static int printed_version = 0;
@@ -1262,9 +1477,8 @@ static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        void __iomem *mmio_base;
        int pci_dev_busy = 0, rc;
 
-       if (!printed_version++) {
-               printk(KERN_INFO DRV_NAME " version " DRV_VERSION "\n");
-       }
+       if (!printed_version++)
+               dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n");
 
        rc = pci_enable_device(pdev);
        if (rc) {