linux/drivers/spi/dw_spi_mid.c
<<
>>
Prefs
   1/*
   2 * dw_spi_mid.c - special handling for DW core on Intel MID platform
   3 *
   4 * Copyright (c) 2009, Intel Corporation.
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms and conditions of the GNU General Public License,
   8 * version 2, as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14 *
  15 * You should have received a copy of the GNU General Public License along
  16 * with this program; if not, write to the Free Software Foundation,
  17 * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  18 */
  19
  20#include <linux/dma-mapping.h>
  21#include <linux/dmaengine.h>
  22#include <linux/interrupt.h>
  23#include <linux/slab.h>
  24#include <linux/spi/spi.h>
  25#include <linux/spi/dw_spi.h>
  26
  27#ifdef CONFIG_SPI_DW_MID_DMA
  28#include <linux/intel_mid_dma.h>
  29#include <linux/pci.h>
  30
  31struct mid_dma {
  32        struct intel_mid_dma_slave      dmas_tx;
  33        struct intel_mid_dma_slave      dmas_rx;
  34};
  35
  36static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param)
  37{
  38        struct dw_spi *dws = param;
  39
  40        return dws->dmac && (&dws->dmac->dev == chan->device->dev);
  41}
  42
  43static int mid_spi_dma_init(struct dw_spi *dws)
  44{
  45        struct mid_dma *dw_dma = dws->dma_priv;
  46        struct intel_mid_dma_slave *rxs, *txs;
  47        dma_cap_mask_t mask;
  48
  49        /*
  50         * Get pci device for DMA controller, currently it could only
  51         * be the DMA controller of either Moorestown or Medfield
  52         */
  53        dws->dmac = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0813, NULL);
  54        if (!dws->dmac)
  55                dws->dmac = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0827, NULL);
  56
  57        dma_cap_zero(mask);
  58        dma_cap_set(DMA_SLAVE, mask);
  59
  60        /* 1. Init rx channel */
  61        dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws);
  62        if (!dws->rxchan)
  63                goto err_exit;
  64        rxs = &dw_dma->dmas_rx;
  65        rxs->hs_mode = LNW_DMA_HW_HS;
  66        rxs->cfg_mode = LNW_DMA_PER_TO_MEM;
  67        dws->rxchan->private = rxs;
  68
  69        /* 2. Init tx channel */
  70        dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws);
  71        if (!dws->txchan)
  72                goto free_rxchan;
  73        txs = &dw_dma->dmas_tx;
  74        txs->hs_mode = LNW_DMA_HW_HS;
  75        txs->cfg_mode = LNW_DMA_MEM_TO_PER;
  76        dws->txchan->private = txs;
  77
  78        dws->dma_inited = 1;
  79        return 0;
  80
  81free_rxchan:
  82        dma_release_channel(dws->rxchan);
  83err_exit:
  84        return -1;
  85
  86}
  87
  88static void mid_spi_dma_exit(struct dw_spi *dws)
  89{
  90        dma_release_channel(dws->txchan);
  91        dma_release_channel(dws->rxchan);
  92}
  93
  94/*
  95 * dws->dma_chan_done is cleared before the dma transfer starts,
  96 * callback for rx/tx channel will each increment it by 1.
  97 * Reaching 2 means the whole spi transaction is done.
  98 */
  99static void dw_spi_dma_done(void *arg)
 100{
 101        struct dw_spi *dws = arg;
 102
 103        if (++dws->dma_chan_done != 2)
 104                return;
 105        dw_spi_xfer_done(dws);
 106}
 107
 108static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
 109{
 110        struct dma_async_tx_descriptor *txdesc = NULL, *rxdesc = NULL;
 111        struct dma_chan *txchan, *rxchan;
 112        struct dma_slave_config txconf, rxconf;
 113        u16 dma_ctrl = 0;
 114
 115        /* 1. setup DMA related registers */
 116        if (cs_change) {
 117                spi_enable_chip(dws, 0);
 118                dw_writew(dws, dmardlr, 0xf);
 119                dw_writew(dws, dmatdlr, 0x10);
 120                if (dws->tx_dma)
 121                        dma_ctrl |= 0x2;
 122                if (dws->rx_dma)
 123                        dma_ctrl |= 0x1;
 124                dw_writew(dws, dmacr, dma_ctrl);
 125                spi_enable_chip(dws, 1);
 126        }
 127
 128        dws->dma_chan_done = 0;
 129        txchan = dws->txchan;
 130        rxchan = dws->rxchan;
 131
 132        /* 2. Prepare the TX dma transfer */
 133        txconf.direction = DMA_TO_DEVICE;
 134        txconf.dst_addr = dws->dma_addr;
 135        txconf.dst_maxburst = LNW_DMA_MSIZE_16;
 136        txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 137        txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
 138
 139        txchan->device->device_control(txchan, DMA_SLAVE_CONFIG,
 140                                       (unsigned long) &txconf);
 141
 142        memset(&dws->tx_sgl, 0, sizeof(dws->tx_sgl));
 143        dws->tx_sgl.dma_address = dws->tx_dma;
 144        dws->tx_sgl.length = dws->len;
 145
 146        txdesc = txchan->device->device_prep_slave_sg(txchan,
 147                                &dws->tx_sgl,
 148                                1,
 149                                DMA_TO_DEVICE,
 150                                DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP);
 151        txdesc->callback = dw_spi_dma_done;
 152        txdesc->callback_param = dws;
 153
 154        /* 3. Prepare the RX dma transfer */
 155        rxconf.direction = DMA_FROM_DEVICE;
 156        rxconf.src_addr = dws->dma_addr;
 157        rxconf.src_maxburst = LNW_DMA_MSIZE_16;
 158        rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 159        rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
 160
 161        rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG,
 162                                       (unsigned long) &rxconf);
 163
 164        memset(&dws->rx_sgl, 0, sizeof(dws->rx_sgl));
 165        dws->rx_sgl.dma_address = dws->rx_dma;
 166        dws->rx_sgl.length = dws->len;
 167
 168        rxdesc = rxchan->device->device_prep_slave_sg(rxchan,
 169                                &dws->rx_sgl,
 170                                1,
 171                                DMA_FROM_DEVICE,
 172                                DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP);
 173        rxdesc->callback = dw_spi_dma_done;
 174        rxdesc->callback_param = dws;
 175
 176        /* rx must be started before tx due to spi instinct */
 177        rxdesc->tx_submit(rxdesc);
 178        txdesc->tx_submit(txdesc);
 179        return 0;
 180}
 181
 182static struct dw_spi_dma_ops mid_dma_ops = {
 183        .dma_init       = mid_spi_dma_init,
 184        .dma_exit       = mid_spi_dma_exit,
 185        .dma_transfer   = mid_spi_dma_transfer,
 186};
 187#endif
 188
 189/* Some specific info for SPI0 controller on Moorestown */
 190
 191/* HW info for MRST CLk Control Unit, one 32b reg */
 192#define MRST_SPI_CLK_BASE       100000000       /* 100m */
 193#define MRST_CLK_SPI0_REG       0xff11d86c
 194#define CLK_SPI_BDIV_OFFSET     0
 195#define CLK_SPI_BDIV_MASK       0x00000007
 196#define CLK_SPI_CDIV_OFFSET     9
 197#define CLK_SPI_CDIV_MASK       0x00000e00
 198#define CLK_SPI_DISABLE_OFFSET  8
 199
 200int dw_spi_mid_init(struct dw_spi *dws)
 201{
 202        u32 *clk_reg, clk_cdiv;
 203
 204        clk_reg = ioremap_nocache(MRST_CLK_SPI0_REG, 16);
 205        if (!clk_reg)
 206                return -ENOMEM;
 207
 208        /* get SPI controller operating freq info */
 209        clk_cdiv  = (readl(clk_reg) & CLK_SPI_CDIV_MASK) >> CLK_SPI_CDIV_OFFSET;
 210        dws->max_freq = MRST_SPI_CLK_BASE / (clk_cdiv + 1);
 211        iounmap(clk_reg);
 212
 213        dws->num_cs = 16;
 214        dws->fifo_len = 40;     /* FIFO has 40 words buffer */
 215
 216#ifdef CONFIG_SPI_DW_MID_DMA
 217        dws->dma_priv = kzalloc(sizeof(struct mid_dma), GFP_KERNEL);
 218        if (!dws->dma_priv)
 219                return -ENOMEM;
 220        dws->dma_ops = &mid_dma_ops;
 221#endif
 222        return 0;
 223}
 224