diff options
Diffstat (limited to 'drivers/spi/dw_spi_mid.c')
-rw-r--r-- | drivers/spi/dw_spi_mid.c | 223 |
1 files changed, 223 insertions, 0 deletions
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 | |||
31 | struct mid_dma { | ||
32 | struct intel_mid_dma_slave dmas_tx; | ||
33 | struct intel_mid_dma_slave dmas_rx; | ||
34 | }; | ||
35 | |||
36 | static 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 | |||
43 | static 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 | |||
81 | free_rxchan: | ||
82 | dma_release_channel(dws->rxchan); | ||
83 | err_exit: | ||
84 | return -1; | ||
85 | |||
86 | } | ||
87 | |||
88 | static 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 | */ | ||
99 | static 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 | |||
108 | static 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 | |||
182 | static 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 | |||
200 | int 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 | } | ||