aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorFeng Tang <feng.tang@intel.com>2010-12-24 00:59:11 -0500
committerGrant Likely <grant.likely@secretlab.ca>2010-12-24 03:23:25 -0500
commit7063c0d942a1af2993531fbe52b4c74c1db818c4 (patch)
treefe91b01bd61449849736c47e0a05840fc47a5eb3 /drivers
parent79290a2aa2fd1c179a285218472092475630dc0e (diff)
spi/dw_spi: add DMA support
dw_spi driver in upstream only supports PIO mode, and this patch will support it to cowork with the Designware dma controller used on Intel Moorestown platform, at the same time it provides a general framework to support dw_spi core to cowork with dma controllers on other platforms It has been tested with a Option GTM501L 3G modem and Infenion 60x60 modem. To use DMA mode, DMA controller 2 of Moorestown has to be enabled Also change the dma interface suggested by Linus Walleij. Acked-by: Linus Walleij <linus.walleij@stericsson.com> Signed-off-by: Feng Tang <feng.tang@intel.com> [Typo fix and renames to match intel_mid_dma renaming] Signed-off-by: Vinod Koul <vinod.koul@intel.com> Signed-off-by: Alan Cox <alan@linux.intel.com> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/spi/Kconfig4
-rw-r--r--drivers/spi/Makefile3
-rw-r--r--drivers/spi/dw_spi.c33
-rw-r--r--drivers/spi/dw_spi_mid.c223
-rw-r--r--drivers/spi/dw_spi_pci.c20
5 files changed, 264 insertions, 19 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 665d03d4e022..caa2e84cc0a6 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -382,6 +382,10 @@ config SPI_DW_PCI
382 tristate "PCI interface driver for DW SPI core" 382 tristate "PCI interface driver for DW SPI core"
383 depends on SPI_DESIGNWARE && PCI 383 depends on SPI_DESIGNWARE && PCI
384 384
385config SPI_DW_MID_DMA
386 bool "DMA support for DW SPI controller on Intel Moorestown platform"
387 depends on SPI_DW_PCI && INTEL_MID_DMAC
388
385config SPI_DW_MMIO 389config SPI_DW_MMIO
386 tristate "Memory-mapped io interface driver for DW SPI core" 390 tristate "Memory-mapped io interface driver for DW SPI core"
387 depends on SPI_DESIGNWARE && HAVE_CLK 391 depends on SPI_DESIGNWARE && HAVE_CLK
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 02dad4ae412d..7ec375b6c0c3 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -17,7 +17,8 @@ obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o
17obj-$(CONFIG_SPI_COLDFIRE_QSPI) += coldfire_qspi.o 17obj-$(CONFIG_SPI_COLDFIRE_QSPI) += coldfire_qspi.o
18obj-$(CONFIG_SPI_DAVINCI) += davinci_spi.o 18obj-$(CONFIG_SPI_DAVINCI) += davinci_spi.o
19obj-$(CONFIG_SPI_DESIGNWARE) += dw_spi.o 19obj-$(CONFIG_SPI_DESIGNWARE) += dw_spi.o
20obj-$(CONFIG_SPI_DW_PCI) += dw_spi_pci.o 20obj-$(CONFIG_SPI_DW_PCI) += dw_spi_midpci.o
21dw_spi_midpci-objs := dw_spi_pci.o dw_spi_mid.o
21obj-$(CONFIG_SPI_DW_MMIO) += dw_spi_mmio.o 22obj-$(CONFIG_SPI_DW_MMIO) += dw_spi_mmio.o
22obj-$(CONFIG_SPI_EP93XX) += ep93xx_spi.o 23obj-$(CONFIG_SPI_EP93XX) += ep93xx_spi.o
23obj-$(CONFIG_SPI_GPIO) += spi_gpio.o 24obj-$(CONFIG_SPI_GPIO) += spi_gpio.o
diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c
index b50bf5ba873f..497ecb3ab83f 100644
--- a/drivers/spi/dw_spi.c
+++ b/drivers/spi/dw_spi.c
@@ -288,8 +288,10 @@ static void *next_transfer(struct dw_spi *dws)
288 */ 288 */
289static int map_dma_buffers(struct dw_spi *dws) 289static int map_dma_buffers(struct dw_spi *dws)
290{ 290{
291 if (!dws->cur_msg->is_dma_mapped || !dws->dma_inited 291 if (!dws->cur_msg->is_dma_mapped
292 || !dws->cur_chip->enable_dma) 292 || !dws->dma_inited
293 || !dws->cur_chip->enable_dma
294 || !dws->dma_ops)
293 return 0; 295 return 0;
294 296
295 if (dws->cur_transfer->tx_dma) 297 if (dws->cur_transfer->tx_dma)
@@ -341,7 +343,7 @@ static void int_error_stop(struct dw_spi *dws, const char *msg)
341 tasklet_schedule(&dws->pump_transfers); 343 tasklet_schedule(&dws->pump_transfers);
342} 344}
343 345
344static void transfer_complete(struct dw_spi *dws) 346void dw_spi_xfer_done(struct dw_spi *dws)
345{ 347{
346 /* Update total byte transfered return count actual bytes read */ 348 /* Update total byte transfered return count actual bytes read */
347 dws->cur_msg->actual_length += dws->len; 349 dws->cur_msg->actual_length += dws->len;
@@ -356,6 +358,7 @@ static void transfer_complete(struct dw_spi *dws)
356 } else 358 } else
357 tasklet_schedule(&dws->pump_transfers); 359 tasklet_schedule(&dws->pump_transfers);
358} 360}
361EXPORT_SYMBOL_GPL(dw_spi_xfer_done);
359 362
360static irqreturn_t interrupt_transfer(struct dw_spi *dws) 363static irqreturn_t interrupt_transfer(struct dw_spi *dws)
361{ 364{
@@ -387,7 +390,7 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
387 if (dws->tx_end > dws->tx) 390 if (dws->tx_end > dws->tx)
388 spi_umask_intr(dws, SPI_INT_TXEI); 391 spi_umask_intr(dws, SPI_INT_TXEI);
389 else 392 else
390 transfer_complete(dws); 393 dw_spi_xfer_done(dws);
391 } 394 }
392 395
393 return IRQ_HANDLED; 396 return IRQ_HANDLED;
@@ -422,11 +425,7 @@ static void poll_transfer(struct dw_spi *dws)
422 */ 425 */
423 dws->read(dws); 426 dws->read(dws);
424 427
425 transfer_complete(dws); 428 dw_spi_xfer_done(dws);
426}
427
428static void dma_transfer(struct dw_spi *dws, int cs_change)
429{
430} 429}
431 430
432static void pump_transfers(unsigned long data) 431static void pump_transfers(unsigned long data)
@@ -608,7 +607,7 @@ static void pump_transfers(unsigned long data)
608 } 607 }
609 608
610 if (dws->dma_mapped) 609 if (dws->dma_mapped)
611 dma_transfer(dws, cs_change); 610 dws->dma_ops->dma_transfer(dws, cs_change);
612 611
613 if (chip->poll_mode) 612 if (chip->poll_mode)
614 poll_transfer(dws); 613 poll_transfer(dws);
@@ -904,11 +903,17 @@ int __devinit dw_spi_add_host(struct dw_spi *dws)
904 master->setup = dw_spi_setup; 903 master->setup = dw_spi_setup;
905 master->transfer = dw_spi_transfer; 904 master->transfer = dw_spi_transfer;
906 905
907 dws->dma_inited = 0;
908
909 /* Basic HW init */ 906 /* Basic HW init */
910 spi_hw_init(dws); 907 spi_hw_init(dws);
911 908
909 if (dws->dma_ops && dws->dma_ops->dma_init) {
910 ret = dws->dma_ops->dma_init(dws);
911 if (ret) {
912 dev_warn(&master->dev, "DMA init failed\n");
913 dws->dma_inited = 0;
914 }
915 }
916
912 /* Initial and start queue */ 917 /* Initial and start queue */
913 ret = init_queue(dws); 918 ret = init_queue(dws);
914 if (ret) { 919 if (ret) {
@@ -933,6 +938,8 @@ int __devinit dw_spi_add_host(struct dw_spi *dws)
933 938
934err_queue_alloc: 939err_queue_alloc:
935 destroy_queue(dws); 940 destroy_queue(dws);
941 if (dws->dma_ops && dws->dma_ops->dma_exit)
942 dws->dma_ops->dma_exit(dws);
936err_diable_hw: 943err_diable_hw:
937 spi_enable_chip(dws, 0); 944 spi_enable_chip(dws, 0);
938 free_irq(dws->irq, dws); 945 free_irq(dws->irq, dws);
@@ -957,6 +964,8 @@ void __devexit dw_spi_remove_host(struct dw_spi *dws)
957 dev_err(&dws->master->dev, "dw_spi_remove: workqueue will not " 964 dev_err(&dws->master->dev, "dw_spi_remove: workqueue will not "
958 "complete, message memory not freed\n"); 965 "complete, message memory not freed\n");
959 966
967 if (dws->dma_ops && dws->dma_ops->dma_exit)
968 dws->dma_ops->dma_exit(dws);
960 spi_enable_chip(dws, 0); 969 spi_enable_chip(dws, 0);
961 /* Disable clk */ 970 /* Disable clk */
962 spi_set_clk(dws, 0); 971 spi_set_clk(dws, 0);
diff --git a/drivers/spi/dw_spi_mid.c b/drivers/spi/dw_spi_mid.c
new file mode 100644
index 000000000000..c91c966e0717
--- /dev/null
+++ b/drivers/spi/dw_spi_mid.c
@@ -0,0 +1,223 @@
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}
diff --git a/drivers/spi/dw_spi_pci.c b/drivers/spi/dw_spi_pci.c
index 1f52755dc878..49ec3aa1219f 100644
--- a/drivers/spi/dw_spi_pci.c
+++ b/drivers/spi/dw_spi_pci.c
@@ -1,5 +1,5 @@
1/* 1/*
2 * mrst_spi_pci.c - PCI interface driver for DW SPI Core 2 * dw_spi_pci.c - PCI interface driver for DW SPI Core
3 * 3 *
4 * Copyright (c) 2009, Intel Corporation. 4 * Copyright (c) 2009, Intel Corporation.
5 * 5 *
@@ -26,8 +26,8 @@
26#define DRIVER_NAME "dw_spi_pci" 26#define DRIVER_NAME "dw_spi_pci"
27 27
28struct dw_spi_pci { 28struct dw_spi_pci {
29 struct pci_dev *pdev; 29 struct pci_dev *pdev;
30 struct dw_spi dws; 30 struct dw_spi dws;
31}; 31};
32 32
33static int __devinit spi_pci_probe(struct pci_dev *pdev, 33static int __devinit spi_pci_probe(struct pci_dev *pdev,
@@ -72,9 +72,17 @@ static int __devinit spi_pci_probe(struct pci_dev *pdev,
72 dws->parent_dev = &pdev->dev; 72 dws->parent_dev = &pdev->dev;
73 dws->bus_num = 0; 73 dws->bus_num = 0;
74 dws->num_cs = 4; 74 dws->num_cs = 4;
75 dws->max_freq = 25000000; /* for Moorestwon */
76 dws->irq = pdev->irq; 75 dws->irq = pdev->irq;
77 dws->fifo_len = 40; /* FIFO has 40 words buffer */ 76
77 /*
78 * Specific handling for Intel MID paltforms, like dma setup,
79 * clock rate, FIFO depth.
80 */
81 if (pdev->device == 0x0800) {
82 ret = dw_spi_mid_init(dws);
83 if (ret)
84 goto err_unmap;
85 }
78 86
79 ret = dw_spi_add_host(dws); 87 ret = dw_spi_add_host(dws);
80 if (ret) 88 if (ret)
@@ -140,7 +148,7 @@ static int spi_resume(struct pci_dev *pdev)
140#endif 148#endif
141 149
142static const struct pci_device_id pci_ids[] __devinitdata = { 150static const struct pci_device_id pci_ids[] __devinitdata = {
143 /* Intel Moorestown platform SPI controller 0 */ 151 /* Intel MID platform SPI controller 0 */
144 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0800) }, 152 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0800) },
145 {}, 153 {},
146}; 154};