]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/spi/spi-omap2-mcspi.c
Merge remote-tracking branches 'spi/topic/spidev', 'spi/topic/sunxi', 'spi/topic...
[linux.git] / drivers / spi / spi-omap2-mcspi.c
index 1d237e93a2895c3f05ecca846df0d1c1db99ba5a..d5157b2222ce1d8837ed204598107f4968218aef 100644 (file)
@@ -419,16 +419,13 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi,
 
        if (mcspi_dma->dma_tx) {
                struct dma_async_tx_descriptor *tx;
-               struct scatterlist sg;
 
                dmaengine_slave_config(mcspi_dma->dma_tx, &cfg);
 
-               sg_init_table(&sg, 1);
-               sg_dma_address(&sg) = xfer->tx_dma;
-               sg_dma_len(&sg) = xfer->len;
-
-               tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1,
-               DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+               tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, xfer->tx_sg.sgl,
+                                            xfer->tx_sg.nents,
+                                            DMA_MEM_TO_DEV,
+                                            DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
                if (tx) {
                        tx->callback = omap2_mcspi_tx_callback;
                        tx->callback_param = spi;
@@ -449,7 +446,10 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
 {
        struct omap2_mcspi      *mcspi;
        struct omap2_mcspi_dma  *mcspi_dma;
-       unsigned int            count, dma_count;
+       unsigned int            count, transfer_reduction = 0;
+       struct scatterlist      *sg_out[2];
+       int                     nb_sizes = 0, out_mapped_nents[2], ret, x;
+       size_t                  sizes[2];
        u32                     l;
        int                     elements = 0;
        int                     word_len, element_count;
@@ -457,10 +457,14 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
        mcspi = spi_master_get_devdata(spi->master);
        mcspi_dma = &mcspi->dma_channels[spi->chip_select];
        count = xfer->len;
-       dma_count = xfer->len;
 
+       /*
+        *  In the "End-of-Transfer Procedure" section for DMA RX in OMAP35x TRM
+        *  it mentions reducing DMA transfer length by one element in master
+        *  normal mode.
+        */
        if (mcspi->fifo_depth == 0)
-               dma_count -= es;
+               transfer_reduction = es;
 
        word_len = cs->word_len;
        l = mcspi_cached_chconf0(spi);
@@ -474,20 +478,46 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
 
        if (mcspi_dma->dma_rx) {
                struct dma_async_tx_descriptor *tx;
-               struct scatterlist sg;
 
                dmaengine_slave_config(mcspi_dma->dma_rx, &cfg);
 
+               /*
+                *  Reduce DMA transfer length by one more if McSPI is
+                *  configured in turbo mode.
+                */
                if ((l & OMAP2_MCSPI_CHCONF_TURBO) && mcspi->fifo_depth == 0)
-                       dma_count -= es;
+                       transfer_reduction += es;
+
+               if (transfer_reduction) {
+                       /* Split sgl into two. The second sgl won't be used. */
+                       sizes[0] = count - transfer_reduction;
+                       sizes[1] = transfer_reduction;
+                       nb_sizes = 2;
+               } else {
+                       /*
+                        * Don't bother splitting the sgl. This essentially
+                        * clones the original sgl.
+                        */
+                       sizes[0] = count;
+                       nb_sizes = 1;
+               }
+
+               ret = sg_split(xfer->rx_sg.sgl, xfer->rx_sg.nents,
+                              0, nb_sizes,
+                              sizes,
+                              sg_out, out_mapped_nents,
+                              GFP_KERNEL);
 
-               sg_init_table(&sg, 1);
-               sg_dma_address(&sg) = xfer->rx_dma;
-               sg_dma_len(&sg) = dma_count;
+               if (ret < 0) {
+                       dev_err(&spi->dev, "sg_split failed\n");
+                       return 0;
+               }
 
-               tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1,
-                               DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT |
-                               DMA_CTRL_ACK);
+               tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx,
+                                            sg_out[0],
+                                            out_mapped_nents[0],
+                                            DMA_DEV_TO_MEM,
+                                            DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
                if (tx) {
                        tx->callback = omap2_mcspi_rx_callback;
                        tx->callback_param = spi;
@@ -501,12 +531,17 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer,
        omap2_mcspi_set_dma_req(spi, 1, 1);
 
        wait_for_completion(&mcspi_dma->dma_rx_completion);
-       dma_unmap_single(mcspi->dev, xfer->rx_dma, count,
-                        DMA_FROM_DEVICE);
+
+       for (x = 0; x < nb_sizes; x++)
+               kfree(sg_out[x]);
 
        if (mcspi->fifo_depth > 0)
                return count;
 
+       /*
+        *  Due to the DMA transfer length reduction the missing bytes must
+        *  be read manually to receive all of the expected data.
+        */
        omap2_mcspi_set_enable(spi, 0);
 
        elements = element_count - 1;
@@ -615,8 +650,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
 
        if (tx != NULL) {
                wait_for_completion(&mcspi_dma->dma_tx_completion);
-               dma_unmap_single(mcspi->dev, xfer->tx_dma, xfer->len,
-                                DMA_TO_DEVICE);
 
                if (mcspi->fifo_depth > 0) {
                        irqstat_reg = mcspi->base + OMAP2_MCSPI_IRQSTATUS;
@@ -1074,8 +1107,9 @@ static void omap2_mcspi_cleanup(struct spi_device *spi)
                gpio_free(spi->cs_gpio);
 }
 
-static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi,
-               struct spi_device *spi, struct spi_transfer *t)
+static int omap2_mcspi_transfer_one(struct spi_master *master,
+                                   struct spi_device *spi,
+                                   struct spi_transfer *t)
 {
 
        /* We only enable one channel at a time -- the one whose message is
@@ -1085,7 +1119,7 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi,
         * chipselect with the FORCE bit ... CS != channel enable.
         */
 
-       struct spi_master               *master;
+       struct omap2_mcspi              *mcspi;
        struct omap2_mcspi_dma          *mcspi_dma;
        struct omap2_mcspi_cs           *cs;
        struct omap2_mcspi_device_config *cd;
@@ -1093,7 +1127,7 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi,
        int                             status = 0;
        u32                             chconf;
 
-       master = spi->master;
+       mcspi = spi_master_get_devdata(master);
        mcspi_dma = mcspi->dma_channels + spi->chip_select;
        cs = spi->controller_state;
        cd = spi->controller_data;
@@ -1153,7 +1187,8 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi,
                unsigned        count;
 
                if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
-                   (t->len >= DMA_MIN_BYTES))
+                   master->cur_msg_mapped &&
+                   master->can_dma(master, spi, t))
                        omap2_mcspi_set_fifo(spi, t, 1);
 
                omap2_mcspi_set_enable(spi, 1);
@@ -1164,7 +1199,8 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi,
                                        + OMAP2_MCSPI_TX0);
 
                if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) &&
-                   (t->len >= DMA_MIN_BYTES))
+                   master->cur_msg_mapped &&
+                   master->can_dma(master, spi, t))
                        count = omap2_mcspi_txrx_dma(spi, t);
                else
                        count = omap2_mcspi_txrx_pio(spi, t);
@@ -1233,55 +1269,11 @@ static int omap2_mcspi_prepare_message(struct spi_master *master,
        return 0;
 }
 
-static int omap2_mcspi_transfer_one(struct spi_master *master,
-               struct spi_device *spi, struct spi_transfer *t)
+static bool omap2_mcspi_can_dma(struct spi_master *master,
+                               struct spi_device *spi,
+                               struct spi_transfer *xfer)
 {
-       struct omap2_mcspi      *mcspi;
-       struct omap2_mcspi_dma  *mcspi_dma;
-       const void      *tx_buf = t->tx_buf;
-       void            *rx_buf = t->rx_buf;
-       unsigned        len = t->len;
-
-       mcspi = spi_master_get_devdata(master);
-       mcspi_dma = mcspi->dma_channels + spi->chip_select;
-
-       if ((len && !(rx_buf || tx_buf))) {
-               dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
-                               t->speed_hz,
-                               len,
-                               tx_buf ? "tx" : "",
-                               rx_buf ? "rx" : "",
-                               t->bits_per_word);
-               return -EINVAL;
-       }
-
-       if (len < DMA_MIN_BYTES)
-               goto skip_dma_map;
-
-       if (mcspi_dma->dma_tx && tx_buf != NULL) {
-               t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf,
-                               len, DMA_TO_DEVICE);
-               if (dma_mapping_error(mcspi->dev, t->tx_dma)) {
-                       dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
-                                       'T', len);
-                       return -EINVAL;
-               }
-       }
-       if (mcspi_dma->dma_rx && rx_buf != NULL) {
-               t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len,
-                               DMA_FROM_DEVICE);
-               if (dma_mapping_error(mcspi->dev, t->rx_dma)) {
-                       dev_dbg(mcspi->dev, "dma %cX %d bytes error\n",
-                                       'R', len);
-                       if (tx_buf != NULL)
-                               dma_unmap_single(mcspi->dev, t->tx_dma,
-                                               len, DMA_TO_DEVICE);
-                       return -EINVAL;
-               }
-       }
-
-skip_dma_map:
-       return omap2_mcspi_work_one(mcspi, spi, t);
+       return (xfer->len >= DMA_MIN_BYTES);
 }
 
 static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
@@ -1361,6 +1353,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev)
        master->setup = omap2_mcspi_setup;
        master->auto_runtime_pm = true;
        master->prepare_message = omap2_mcspi_prepare_message;
+       master->can_dma = omap2_mcspi_can_dma;
        master->transfer_one = omap2_mcspi_transfer_one;
        master->set_cs = omap2_mcspi_set_cs;
        master->cleanup = omap2_mcspi_cleanup;