sata_dwc_460ex: device tree may specify dma_channel
[linux-2.6-microblaze.git] / drivers / ata / sata_dwc_460ex.c
old mode 100644 (file)
new mode 100755 (executable)
index 69f7cde..937aeb3
@@ -158,6 +158,7 @@ enum {
 /* Assign HW handshaking interface (x) to destination / source peripheral */
 #define        DMA_CFG_HW_HS_DEST(int_num) (((int_num) & 0xF) << 11)
 #define        DMA_CFG_HW_HS_SRC(int_num) (((int_num) & 0xF) << 7)
+#define        DMA_CFG_HW_CH_PRIOR(int_num) (((int_num) & 0xF) << 5)
 #define        DMA_LLP_LMS(addr, master) (((addr) & 0xfffffffc) | (master))
 
 /*
@@ -318,6 +319,7 @@ struct sata_dwc_host_priv {
        u32     dma_interrupt_count;
        struct  ahb_dma_regs    *sata_dma_regs;
        struct  device  *dwc_dev;
+       int     dma_channel;
 };
 struct sata_dwc_host_priv host_pvt;
 /*
@@ -437,15 +439,12 @@ static  void clear_chan_interrupts(int c)
  */
 static int dma_request_channel(void)
 {
-       int i;
-
-       for (i = 0; i < DMA_NUM_CHANS; i++) {
-               if (!(in_le32(&(host_pvt.sata_dma_regs->dma_chan_en.low)) &\
-                       DMA_CHANNEL(i)))
-                       return i;
-       }
-       dev_err(host_pvt.dwc_dev, "%s NO channel chan_en: 0x%08x\n", __func__,
-               in_le32(&(host_pvt.sata_dma_regs->dma_chan_en.low)));
+       /* Check if the channel is not currently in use */
+       if (!(in_le32(&(host_pvt.sata_dma_regs->dma_chan_en.low)) &
+               DMA_CHANNEL(host_pvt.dma_channel)))
+               return host_pvt.dma_channel;
+       dev_err(host_pvt.dwc_dev, "%s Channel %d is currently in use\n",
+               __func__, host_pvt.dma_channel);
        return -1;
 }
 
@@ -481,7 +480,8 @@ static irqreturn_t dma_dwc_interrupt(int irq, void *hsdev_instance)
        dev_dbg(ap->dev, "eot=0x%08x err=0x%08x pending=%d active port=%d\n",
                tfr_reg, err_reg, hsdevp->dma_pending[tag], port);
 
-       for (chan = 0; chan < DMA_NUM_CHANS; chan++) {
+       chan = host_pvt.dma_channel;
+       if (chan >= 0) {
                /* Check for end-of-transfer interrupt. */
                if (tfr_reg & DMA_CHANNEL(chan)) {
                        /*
@@ -534,9 +534,9 @@ static irqreturn_t dma_dwc_interrupt(int irq, void *hsdev_instance)
 static int dma_request_interrupts(struct sata_dwc_device *hsdev, int irq)
 {
        int retval = 0;
-       int chan;
+       int chan = host_pvt.dma_channel;
 
-       for (chan = 0; chan < DMA_NUM_CHANS; chan++) {
+       if (chan >= 0) {
                /* Unmask error interrupt */
                out_le32(&(host_pvt.sata_dma_regs)->interrupt_mask.error.low,
                         DMA_ENABLE_CHAN(chan));
@@ -575,7 +575,10 @@ static int map_sg_to_lli(struct scatterlist *sg, int num_elems,
        int fis_len = 0;
        dma_addr_t next_llp;
        int bl;
+       int sms_val, dms_val;
 
+       sms_val = 0;
+       dms_val = 1 + host_pvt.dma_channel;
        dev_dbg(host_pvt.dwc_dev, "%s: sg=%p nelem=%d lli=%p dma_lli=0x%08x"
                " dmadr=0x%08x\n", __func__, sg, num_elems, lli, (u32)dma_lli,
                (u32)dmadr_addr);
@@ -635,8 +638,8 @@ static int map_sg_to_lli(struct scatterlist *sg, int num_elems,
 
                                lli[idx].ctl.low = cpu_to_le32(
                                        DMA_CTL_TTFC(DMA_CTL_TTFC_P2M_DMAC) |
-                                       DMA_CTL_SMS(0) |
-                                       DMA_CTL_DMS(1) |
+                                       DMA_CTL_SMS(sms_val) |
+                                       DMA_CTL_DMS(dms_val) |
                                        DMA_CTL_SRC_MSIZE(bl) |
                                        DMA_CTL_DST_MSIZE(bl) |
                                        DMA_CTL_SINC_NOCHANGE |
@@ -651,8 +654,8 @@ static int map_sg_to_lli(struct scatterlist *sg, int num_elems,
 
                                lli[idx].ctl.low = cpu_to_le32(
                                        DMA_CTL_TTFC(DMA_CTL_TTFC_M2P_PER) |
-                                       DMA_CTL_SMS(1) |
-                                       DMA_CTL_DMS(0) |
+                                       DMA_CTL_SMS(dms_val) |
+                                       DMA_CTL_DMS(sms_val) |
                                        DMA_CTL_SRC_MSIZE(bl) |
                                        DMA_CTL_DST_MSIZE(bl) |
                                        DMA_CTL_DINC_NOCHANGE |
@@ -744,8 +747,10 @@ static int dma_dwc_xfer_setup(struct scatterlist *sg, int num_elems,
 
        /* Program the CFG register. */
        out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].cfg.high),
+                DMA_CFG_HW_HS_SRC(dma_ch) | DMA_CFG_HW_HS_DEST(dma_ch) |
                 DMA_CFG_PROTCTL | DMA_CFG_FCMOD_REQ);
-       out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].cfg.low), 0);
+       out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].cfg.low),
+                DMA_CFG_HW_CH_PRIOR(dma_ch));
 
        /* Program the address of the linked list */
        out_le32(&(host_pvt.sata_dma_regs->chan_regs[dma_ch].llp.low),
@@ -1581,10 +1586,31 @@ static void sata_dwc_qc_prep(struct ata_queued_cmd *qc)
 
 static void sata_dwc_error_handler(struct ata_port *ap)
 {
-       ap->link.flags |= ATA_LFLAG_NO_HRST;
        ata_sff_error_handler(ap);
 }
 
+int sata_dwc_hardreset(struct ata_link *link, unsigned int *class,
+                       unsigned long deadline)
+{
+       struct sata_dwc_device *hsdev = HSDEV_FROM_AP(link->ap);
+       int ret;
+
+       ret = sata_sff_hardreset(link, class, deadline);
+
+       sata_dwc_enable_interrupts(hsdev);
+
+       /* Reconfigure the DMA control register */
+       out_le32(&hsdev->sata_dwc_regs->dmacr,
+                SATA_DWC_DMACR_TXRXCH_CLEAR);
+
+       /* Reconfigure the DMA Burst Transaction Size register */
+       out_le32(&hsdev->sata_dwc_regs->dbtsr,
+                SATA_DWC_DBTSR_MWR(AHB_DMA_BRST_DFLT) |
+                SATA_DWC_DBTSR_MRD(AHB_DMA_BRST_DFLT));
+
+       return ret;
+}
+
 /*
  * scsi mid-layer and libata interface structures
  */
@@ -1604,6 +1630,7 @@ static struct ata_port_operations sata_dwc_ops = {
        .inherits               = &ata_sff_port_ops,
 
        .error_handler          = sata_dwc_error_handler,
+       .hardreset              = sata_dwc_hardreset,
 
        .qc_prep                = sata_dwc_qc_prep,
        .qc_issue               = sata_dwc_qc_issue,
@@ -1638,6 +1665,8 @@ static int sata_dwc_probe(struct platform_device *ofdev)
        struct ata_host *host;
        struct ata_port_info pi = sata_dwc_port_info[0];
        const struct ata_port_info *ppi[] = { &pi, NULL };
+       struct device_node *np = ofdev->dev.of_node;
+       u32 dma_chan;
 
        /* Allocate DWC SATA device */
        hsdev = kzalloc(sizeof(*hsdev), GFP_KERNEL);
@@ -1647,6 +1676,13 @@ static int sata_dwc_probe(struct platform_device *ofdev)
                goto error;
        }
 
+       if (of_property_read_u32(np, "dma-channel", &dma_chan)) {
+               dev_warn(&ofdev->dev, "no dma-channel property set."
+                        " Use channel 0\n");
+               dma_chan = 0;
+       }
+       host_pvt.dma_channel = dma_chan;
+
        /* Ioremap SATA registers */
        base = of_iomap(ofdev->dev.of_node, 0);
        if (!base) {