diff options
author | Grant Likely <grant.likely@secretlab.ca> | 2011-06-06 03:16:30 -0400 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2011-06-06 03:16:30 -0400 |
commit | ca632f556697d45d67ed5cada7cedf3ddfe0db4b (patch) | |
tree | f393534b929abb32813ea5c495f1ac6d93a10d1d /drivers/spi/spi-s3c64xx.c | |
parent | 8c99268431a117207a89be5167ecd69429fd4bda (diff) |
spi: reorganize drivers
Sort the SPI makefile and enforce the naming convention spi_*.c for
spi drivers.
This change also rolls the contents of atmel_spi.h into the .c file
since there is only one user of that particular include file.
v2: - Use 'spi-' prefix instead of 'spi_' to match what seems to be
be the predominant pattern for subsystem prefixes.
- Clean up filenames in Kconfig and header comment blocks
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Acked-by: Wolfram Sang <w.sang@pengutronix.de>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/spi/spi-s3c64xx.c')
-rw-r--r-- | drivers/spi/spi-s3c64xx.c | 1247 |
1 files changed, 1247 insertions, 0 deletions
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c new file mode 100644 index 000000000000..75e3a9bd3b06 --- /dev/null +++ b/drivers/spi/spi-s3c64xx.c | |||
@@ -0,0 +1,1247 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009 Samsung Electronics Ltd. | ||
3 | * Jaswinder Singh <jassi.brar@samsung.com> | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License as published by | ||
7 | * the Free Software Foundation; either version 2 of the License, or | ||
8 | * (at your option) any later version. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | */ | ||
19 | |||
20 | #include <linux/init.h> | ||
21 | #include <linux/module.h> | ||
22 | #include <linux/workqueue.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/clk.h> | ||
25 | #include <linux/dma-mapping.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/spi/spi.h> | ||
28 | |||
29 | #include <mach/dma.h> | ||
30 | #include <plat/s3c64xx-spi.h> | ||
31 | |||
32 | /* Registers and bit-fields */ | ||
33 | |||
34 | #define S3C64XX_SPI_CH_CFG 0x00 | ||
35 | #define S3C64XX_SPI_CLK_CFG 0x04 | ||
36 | #define S3C64XX_SPI_MODE_CFG 0x08 | ||
37 | #define S3C64XX_SPI_SLAVE_SEL 0x0C | ||
38 | #define S3C64XX_SPI_INT_EN 0x10 | ||
39 | #define S3C64XX_SPI_STATUS 0x14 | ||
40 | #define S3C64XX_SPI_TX_DATA 0x18 | ||
41 | #define S3C64XX_SPI_RX_DATA 0x1C | ||
42 | #define S3C64XX_SPI_PACKET_CNT 0x20 | ||
43 | #define S3C64XX_SPI_PENDING_CLR 0x24 | ||
44 | #define S3C64XX_SPI_SWAP_CFG 0x28 | ||
45 | #define S3C64XX_SPI_FB_CLK 0x2C | ||
46 | |||
47 | #define S3C64XX_SPI_CH_HS_EN (1<<6) /* High Speed Enable */ | ||
48 | #define S3C64XX_SPI_CH_SW_RST (1<<5) | ||
49 | #define S3C64XX_SPI_CH_SLAVE (1<<4) | ||
50 | #define S3C64XX_SPI_CPOL_L (1<<3) | ||
51 | #define S3C64XX_SPI_CPHA_B (1<<2) | ||
52 | #define S3C64XX_SPI_CH_RXCH_ON (1<<1) | ||
53 | #define S3C64XX_SPI_CH_TXCH_ON (1<<0) | ||
54 | |||
55 | #define S3C64XX_SPI_CLKSEL_SRCMSK (3<<9) | ||
56 | #define S3C64XX_SPI_CLKSEL_SRCSHFT 9 | ||
57 | #define S3C64XX_SPI_ENCLK_ENABLE (1<<8) | ||
58 | #define S3C64XX_SPI_PSR_MASK 0xff | ||
59 | |||
60 | #define S3C64XX_SPI_MODE_CH_TSZ_BYTE (0<<29) | ||
61 | #define S3C64XX_SPI_MODE_CH_TSZ_HALFWORD (1<<29) | ||
62 | #define S3C64XX_SPI_MODE_CH_TSZ_WORD (2<<29) | ||
63 | #define S3C64XX_SPI_MODE_CH_TSZ_MASK (3<<29) | ||
64 | #define S3C64XX_SPI_MODE_BUS_TSZ_BYTE (0<<17) | ||
65 | #define S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD (1<<17) | ||
66 | #define S3C64XX_SPI_MODE_BUS_TSZ_WORD (2<<17) | ||
67 | #define S3C64XX_SPI_MODE_BUS_TSZ_MASK (3<<17) | ||
68 | #define S3C64XX_SPI_MODE_RXDMA_ON (1<<2) | ||
69 | #define S3C64XX_SPI_MODE_TXDMA_ON (1<<1) | ||
70 | #define S3C64XX_SPI_MODE_4BURST (1<<0) | ||
71 | |||
72 | #define S3C64XX_SPI_SLAVE_AUTO (1<<1) | ||
73 | #define S3C64XX_SPI_SLAVE_SIG_INACT (1<<0) | ||
74 | |||
75 | #define S3C64XX_SPI_ACT(c) writel(0, (c)->regs + S3C64XX_SPI_SLAVE_SEL) | ||
76 | |||
77 | #define S3C64XX_SPI_DEACT(c) writel(S3C64XX_SPI_SLAVE_SIG_INACT, \ | ||
78 | (c)->regs + S3C64XX_SPI_SLAVE_SEL) | ||
79 | |||
80 | #define S3C64XX_SPI_INT_TRAILING_EN (1<<6) | ||
81 | #define S3C64XX_SPI_INT_RX_OVERRUN_EN (1<<5) | ||
82 | #define S3C64XX_SPI_INT_RX_UNDERRUN_EN (1<<4) | ||
83 | #define S3C64XX_SPI_INT_TX_OVERRUN_EN (1<<3) | ||
84 | #define S3C64XX_SPI_INT_TX_UNDERRUN_EN (1<<2) | ||
85 | #define S3C64XX_SPI_INT_RX_FIFORDY_EN (1<<1) | ||
86 | #define S3C64XX_SPI_INT_TX_FIFORDY_EN (1<<0) | ||
87 | |||
88 | #define S3C64XX_SPI_ST_RX_OVERRUN_ERR (1<<5) | ||
89 | #define S3C64XX_SPI_ST_RX_UNDERRUN_ERR (1<<4) | ||
90 | #define S3C64XX_SPI_ST_TX_OVERRUN_ERR (1<<3) | ||
91 | #define S3C64XX_SPI_ST_TX_UNDERRUN_ERR (1<<2) | ||
92 | #define S3C64XX_SPI_ST_RX_FIFORDY (1<<1) | ||
93 | #define S3C64XX_SPI_ST_TX_FIFORDY (1<<0) | ||
94 | |||
95 | #define S3C64XX_SPI_PACKET_CNT_EN (1<<16) | ||
96 | |||
97 | #define S3C64XX_SPI_PND_TX_UNDERRUN_CLR (1<<4) | ||
98 | #define S3C64XX_SPI_PND_TX_OVERRUN_CLR (1<<3) | ||
99 | #define S3C64XX_SPI_PND_RX_UNDERRUN_CLR (1<<2) | ||
100 | #define S3C64XX_SPI_PND_RX_OVERRUN_CLR (1<<1) | ||
101 | #define S3C64XX_SPI_PND_TRAILING_CLR (1<<0) | ||
102 | |||
103 | #define S3C64XX_SPI_SWAP_RX_HALF_WORD (1<<7) | ||
104 | #define S3C64XX_SPI_SWAP_RX_BYTE (1<<6) | ||
105 | #define S3C64XX_SPI_SWAP_RX_BIT (1<<5) | ||
106 | #define S3C64XX_SPI_SWAP_RX_EN (1<<4) | ||
107 | #define S3C64XX_SPI_SWAP_TX_HALF_WORD (1<<3) | ||
108 | #define S3C64XX_SPI_SWAP_TX_BYTE (1<<2) | ||
109 | #define S3C64XX_SPI_SWAP_TX_BIT (1<<1) | ||
110 | #define S3C64XX_SPI_SWAP_TX_EN (1<<0) | ||
111 | |||
112 | #define S3C64XX_SPI_FBCLK_MSK (3<<0) | ||
113 | |||
114 | #define S3C64XX_SPI_ST_TRLCNTZ(v, i) ((((v) >> (i)->rx_lvl_offset) & \ | ||
115 | (((i)->fifo_lvl_mask + 1))) \ | ||
116 | ? 1 : 0) | ||
117 | |||
118 | #define S3C64XX_SPI_ST_TX_DONE(v, i) ((((v) >> (i)->rx_lvl_offset) & \ | ||
119 | (((i)->fifo_lvl_mask + 1) << 1)) \ | ||
120 | ? 1 : 0) | ||
121 | #define TX_FIFO_LVL(v, i) (((v) >> 6) & (i)->fifo_lvl_mask) | ||
122 | #define RX_FIFO_LVL(v, i) (((v) >> (i)->rx_lvl_offset) & (i)->fifo_lvl_mask) | ||
123 | |||
124 | #define S3C64XX_SPI_MAX_TRAILCNT 0x3ff | ||
125 | #define S3C64XX_SPI_TRAILCNT_OFF 19 | ||
126 | |||
127 | #define S3C64XX_SPI_TRAILCNT S3C64XX_SPI_MAX_TRAILCNT | ||
128 | |||
129 | #define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t) | ||
130 | |||
131 | #define SUSPND (1<<0) | ||
132 | #define SPIBUSY (1<<1) | ||
133 | #define RXBUSY (1<<2) | ||
134 | #define TXBUSY (1<<3) | ||
135 | |||
136 | /** | ||
137 | * struct s3c64xx_spi_driver_data - Runtime info holder for SPI driver. | ||
138 | * @clk: Pointer to the spi clock. | ||
139 | * @src_clk: Pointer to the clock used to generate SPI signals. | ||
140 | * @master: Pointer to the SPI Protocol master. | ||
141 | * @workqueue: Work queue for the SPI xfer requests. | ||
142 | * @cntrlr_info: Platform specific data for the controller this driver manages. | ||
143 | * @tgl_spi: Pointer to the last CS left untoggled by the cs_change hint. | ||
144 | * @work: Work | ||
145 | * @queue: To log SPI xfer requests. | ||
146 | * @lock: Controller specific lock. | ||
147 | * @state: Set of FLAGS to indicate status. | ||
148 | * @rx_dmach: Controller's DMA channel for Rx. | ||
149 | * @tx_dmach: Controller's DMA channel for Tx. | ||
150 | * @sfr_start: BUS address of SPI controller regs. | ||
151 | * @regs: Pointer to ioremap'ed controller registers. | ||
152 | * @xfer_completion: To indicate completion of xfer task. | ||
153 | * @cur_mode: Stores the active configuration of the controller. | ||
154 | * @cur_bpw: Stores the active bits per word settings. | ||
155 | * @cur_speed: Stores the active xfer clock speed. | ||
156 | */ | ||
157 | struct s3c64xx_spi_driver_data { | ||
158 | void __iomem *regs; | ||
159 | struct clk *clk; | ||
160 | struct clk *src_clk; | ||
161 | struct platform_device *pdev; | ||
162 | struct spi_master *master; | ||
163 | struct workqueue_struct *workqueue; | ||
164 | struct s3c64xx_spi_info *cntrlr_info; | ||
165 | struct spi_device *tgl_spi; | ||
166 | struct work_struct work; | ||
167 | struct list_head queue; | ||
168 | spinlock_t lock; | ||
169 | enum dma_ch rx_dmach; | ||
170 | enum dma_ch tx_dmach; | ||
171 | unsigned long sfr_start; | ||
172 | struct completion xfer_completion; | ||
173 | unsigned state; | ||
174 | unsigned cur_mode, cur_bpw; | ||
175 | unsigned cur_speed; | ||
176 | }; | ||
177 | |||
178 | static struct s3c2410_dma_client s3c64xx_spi_dma_client = { | ||
179 | .name = "samsung-spi-dma", | ||
180 | }; | ||
181 | |||
182 | static void flush_fifo(struct s3c64xx_spi_driver_data *sdd) | ||
183 | { | ||
184 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | ||
185 | void __iomem *regs = sdd->regs; | ||
186 | unsigned long loops; | ||
187 | u32 val; | ||
188 | |||
189 | writel(0, regs + S3C64XX_SPI_PACKET_CNT); | ||
190 | |||
191 | val = readl(regs + S3C64XX_SPI_CH_CFG); | ||
192 | val |= S3C64XX_SPI_CH_SW_RST; | ||
193 | val &= ~S3C64XX_SPI_CH_HS_EN; | ||
194 | writel(val, regs + S3C64XX_SPI_CH_CFG); | ||
195 | |||
196 | /* Flush TxFIFO*/ | ||
197 | loops = msecs_to_loops(1); | ||
198 | do { | ||
199 | val = readl(regs + S3C64XX_SPI_STATUS); | ||
200 | } while (TX_FIFO_LVL(val, sci) && loops--); | ||
201 | |||
202 | if (loops == 0) | ||
203 | dev_warn(&sdd->pdev->dev, "Timed out flushing TX FIFO\n"); | ||
204 | |||
205 | /* Flush RxFIFO*/ | ||
206 | loops = msecs_to_loops(1); | ||
207 | do { | ||
208 | val = readl(regs + S3C64XX_SPI_STATUS); | ||
209 | if (RX_FIFO_LVL(val, sci)) | ||
210 | readl(regs + S3C64XX_SPI_RX_DATA); | ||
211 | else | ||
212 | break; | ||
213 | } while (loops--); | ||
214 | |||
215 | if (loops == 0) | ||
216 | dev_warn(&sdd->pdev->dev, "Timed out flushing RX FIFO\n"); | ||
217 | |||
218 | val = readl(regs + S3C64XX_SPI_CH_CFG); | ||
219 | val &= ~S3C64XX_SPI_CH_SW_RST; | ||
220 | writel(val, regs + S3C64XX_SPI_CH_CFG); | ||
221 | |||
222 | val = readl(regs + S3C64XX_SPI_MODE_CFG); | ||
223 | val &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); | ||
224 | writel(val, regs + S3C64XX_SPI_MODE_CFG); | ||
225 | |||
226 | val = readl(regs + S3C64XX_SPI_CH_CFG); | ||
227 | val &= ~(S3C64XX_SPI_CH_RXCH_ON | S3C64XX_SPI_CH_TXCH_ON); | ||
228 | writel(val, regs + S3C64XX_SPI_CH_CFG); | ||
229 | } | ||
230 | |||
231 | static void enable_datapath(struct s3c64xx_spi_driver_data *sdd, | ||
232 | struct spi_device *spi, | ||
233 | struct spi_transfer *xfer, int dma_mode) | ||
234 | { | ||
235 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | ||
236 | void __iomem *regs = sdd->regs; | ||
237 | u32 modecfg, chcfg; | ||
238 | |||
239 | modecfg = readl(regs + S3C64XX_SPI_MODE_CFG); | ||
240 | modecfg &= ~(S3C64XX_SPI_MODE_TXDMA_ON | S3C64XX_SPI_MODE_RXDMA_ON); | ||
241 | |||
242 | chcfg = readl(regs + S3C64XX_SPI_CH_CFG); | ||
243 | chcfg &= ~S3C64XX_SPI_CH_TXCH_ON; | ||
244 | |||
245 | if (dma_mode) { | ||
246 | chcfg &= ~S3C64XX_SPI_CH_RXCH_ON; | ||
247 | } else { | ||
248 | /* Always shift in data in FIFO, even if xfer is Tx only, | ||
249 | * this helps setting PCKT_CNT value for generating clocks | ||
250 | * as exactly needed. | ||
251 | */ | ||
252 | chcfg |= S3C64XX_SPI_CH_RXCH_ON; | ||
253 | writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) | ||
254 | | S3C64XX_SPI_PACKET_CNT_EN, | ||
255 | regs + S3C64XX_SPI_PACKET_CNT); | ||
256 | } | ||
257 | |||
258 | if (xfer->tx_buf != NULL) { | ||
259 | sdd->state |= TXBUSY; | ||
260 | chcfg |= S3C64XX_SPI_CH_TXCH_ON; | ||
261 | if (dma_mode) { | ||
262 | modecfg |= S3C64XX_SPI_MODE_TXDMA_ON; | ||
263 | s3c2410_dma_config(sdd->tx_dmach, sdd->cur_bpw / 8); | ||
264 | s3c2410_dma_enqueue(sdd->tx_dmach, (void *)sdd, | ||
265 | xfer->tx_dma, xfer->len); | ||
266 | s3c2410_dma_ctrl(sdd->tx_dmach, S3C2410_DMAOP_START); | ||
267 | } else { | ||
268 | switch (sdd->cur_bpw) { | ||
269 | case 32: | ||
270 | iowrite32_rep(regs + S3C64XX_SPI_TX_DATA, | ||
271 | xfer->tx_buf, xfer->len / 4); | ||
272 | break; | ||
273 | case 16: | ||
274 | iowrite16_rep(regs + S3C64XX_SPI_TX_DATA, | ||
275 | xfer->tx_buf, xfer->len / 2); | ||
276 | break; | ||
277 | default: | ||
278 | iowrite8_rep(regs + S3C64XX_SPI_TX_DATA, | ||
279 | xfer->tx_buf, xfer->len); | ||
280 | break; | ||
281 | } | ||
282 | } | ||
283 | } | ||
284 | |||
285 | if (xfer->rx_buf != NULL) { | ||
286 | sdd->state |= RXBUSY; | ||
287 | |||
288 | if (sci->high_speed && sdd->cur_speed >= 30000000UL | ||
289 | && !(sdd->cur_mode & SPI_CPHA)) | ||
290 | chcfg |= S3C64XX_SPI_CH_HS_EN; | ||
291 | |||
292 | if (dma_mode) { | ||
293 | modecfg |= S3C64XX_SPI_MODE_RXDMA_ON; | ||
294 | chcfg |= S3C64XX_SPI_CH_RXCH_ON; | ||
295 | writel(((xfer->len * 8 / sdd->cur_bpw) & 0xffff) | ||
296 | | S3C64XX_SPI_PACKET_CNT_EN, | ||
297 | regs + S3C64XX_SPI_PACKET_CNT); | ||
298 | s3c2410_dma_config(sdd->rx_dmach, sdd->cur_bpw / 8); | ||
299 | s3c2410_dma_enqueue(sdd->rx_dmach, (void *)sdd, | ||
300 | xfer->rx_dma, xfer->len); | ||
301 | s3c2410_dma_ctrl(sdd->rx_dmach, S3C2410_DMAOP_START); | ||
302 | } | ||
303 | } | ||
304 | |||
305 | writel(modecfg, regs + S3C64XX_SPI_MODE_CFG); | ||
306 | writel(chcfg, regs + S3C64XX_SPI_CH_CFG); | ||
307 | } | ||
308 | |||
309 | static inline void enable_cs(struct s3c64xx_spi_driver_data *sdd, | ||
310 | struct spi_device *spi) | ||
311 | { | ||
312 | struct s3c64xx_spi_csinfo *cs; | ||
313 | |||
314 | if (sdd->tgl_spi != NULL) { /* If last device toggled after mssg */ | ||
315 | if (sdd->tgl_spi != spi) { /* if last mssg on diff device */ | ||
316 | /* Deselect the last toggled device */ | ||
317 | cs = sdd->tgl_spi->controller_data; | ||
318 | cs->set_level(cs->line, | ||
319 | spi->mode & SPI_CS_HIGH ? 0 : 1); | ||
320 | } | ||
321 | sdd->tgl_spi = NULL; | ||
322 | } | ||
323 | |||
324 | cs = spi->controller_data; | ||
325 | cs->set_level(cs->line, spi->mode & SPI_CS_HIGH ? 1 : 0); | ||
326 | } | ||
327 | |||
328 | static int wait_for_xfer(struct s3c64xx_spi_driver_data *sdd, | ||
329 | struct spi_transfer *xfer, int dma_mode) | ||
330 | { | ||
331 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | ||
332 | void __iomem *regs = sdd->regs; | ||
333 | unsigned long val; | ||
334 | int ms; | ||
335 | |||
336 | /* millisecs to xfer 'len' bytes @ 'cur_speed' */ | ||
337 | ms = xfer->len * 8 * 1000 / sdd->cur_speed; | ||
338 | ms += 10; /* some tolerance */ | ||
339 | |||
340 | if (dma_mode) { | ||
341 | val = msecs_to_jiffies(ms) + 10; | ||
342 | val = wait_for_completion_timeout(&sdd->xfer_completion, val); | ||
343 | } else { | ||
344 | u32 status; | ||
345 | val = msecs_to_loops(ms); | ||
346 | do { | ||
347 | status = readl(regs + S3C64XX_SPI_STATUS); | ||
348 | } while (RX_FIFO_LVL(status, sci) < xfer->len && --val); | ||
349 | } | ||
350 | |||
351 | if (!val) | ||
352 | return -EIO; | ||
353 | |||
354 | if (dma_mode) { | ||
355 | u32 status; | ||
356 | |||
357 | /* | ||
358 | * DmaTx returns after simply writing data in the FIFO, | ||
359 | * w/o waiting for real transmission on the bus to finish. | ||
360 | * DmaRx returns only after Dma read data from FIFO which | ||
361 | * needs bus transmission to finish, so we don't worry if | ||
362 | * Xfer involved Rx(with or without Tx). | ||
363 | */ | ||
364 | if (xfer->rx_buf == NULL) { | ||
365 | val = msecs_to_loops(10); | ||
366 | status = readl(regs + S3C64XX_SPI_STATUS); | ||
367 | while ((TX_FIFO_LVL(status, sci) | ||
368 | || !S3C64XX_SPI_ST_TX_DONE(status, sci)) | ||
369 | && --val) { | ||
370 | cpu_relax(); | ||
371 | status = readl(regs + S3C64XX_SPI_STATUS); | ||
372 | } | ||
373 | |||
374 | if (!val) | ||
375 | return -EIO; | ||
376 | } | ||
377 | } else { | ||
378 | /* If it was only Tx */ | ||
379 | if (xfer->rx_buf == NULL) { | ||
380 | sdd->state &= ~TXBUSY; | ||
381 | return 0; | ||
382 | } | ||
383 | |||
384 | switch (sdd->cur_bpw) { | ||
385 | case 32: | ||
386 | ioread32_rep(regs + S3C64XX_SPI_RX_DATA, | ||
387 | xfer->rx_buf, xfer->len / 4); | ||
388 | break; | ||
389 | case 16: | ||
390 | ioread16_rep(regs + S3C64XX_SPI_RX_DATA, | ||
391 | xfer->rx_buf, xfer->len / 2); | ||
392 | break; | ||
393 | default: | ||
394 | ioread8_rep(regs + S3C64XX_SPI_RX_DATA, | ||
395 | xfer->rx_buf, xfer->len); | ||
396 | break; | ||
397 | } | ||
398 | sdd->state &= ~RXBUSY; | ||
399 | } | ||
400 | |||
401 | return 0; | ||
402 | } | ||
403 | |||
404 | static inline void disable_cs(struct s3c64xx_spi_driver_data *sdd, | ||
405 | struct spi_device *spi) | ||
406 | { | ||
407 | struct s3c64xx_spi_csinfo *cs = spi->controller_data; | ||
408 | |||
409 | if (sdd->tgl_spi == spi) | ||
410 | sdd->tgl_spi = NULL; | ||
411 | |||
412 | cs->set_level(cs->line, spi->mode & SPI_CS_HIGH ? 0 : 1); | ||
413 | } | ||
414 | |||
415 | static void s3c64xx_spi_config(struct s3c64xx_spi_driver_data *sdd) | ||
416 | { | ||
417 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | ||
418 | void __iomem *regs = sdd->regs; | ||
419 | u32 val; | ||
420 | |||
421 | /* Disable Clock */ | ||
422 | if (sci->clk_from_cmu) { | ||
423 | clk_disable(sdd->src_clk); | ||
424 | } else { | ||
425 | val = readl(regs + S3C64XX_SPI_CLK_CFG); | ||
426 | val &= ~S3C64XX_SPI_ENCLK_ENABLE; | ||
427 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | ||
428 | } | ||
429 | |||
430 | /* Set Polarity and Phase */ | ||
431 | val = readl(regs + S3C64XX_SPI_CH_CFG); | ||
432 | val &= ~(S3C64XX_SPI_CH_SLAVE | | ||
433 | S3C64XX_SPI_CPOL_L | | ||
434 | S3C64XX_SPI_CPHA_B); | ||
435 | |||
436 | if (sdd->cur_mode & SPI_CPOL) | ||
437 | val |= S3C64XX_SPI_CPOL_L; | ||
438 | |||
439 | if (sdd->cur_mode & SPI_CPHA) | ||
440 | val |= S3C64XX_SPI_CPHA_B; | ||
441 | |||
442 | writel(val, regs + S3C64XX_SPI_CH_CFG); | ||
443 | |||
444 | /* Set Channel & DMA Mode */ | ||
445 | val = readl(regs + S3C64XX_SPI_MODE_CFG); | ||
446 | val &= ~(S3C64XX_SPI_MODE_BUS_TSZ_MASK | ||
447 | | S3C64XX_SPI_MODE_CH_TSZ_MASK); | ||
448 | |||
449 | switch (sdd->cur_bpw) { | ||
450 | case 32: | ||
451 | val |= S3C64XX_SPI_MODE_BUS_TSZ_WORD; | ||
452 | val |= S3C64XX_SPI_MODE_CH_TSZ_WORD; | ||
453 | break; | ||
454 | case 16: | ||
455 | val |= S3C64XX_SPI_MODE_BUS_TSZ_HALFWORD; | ||
456 | val |= S3C64XX_SPI_MODE_CH_TSZ_HALFWORD; | ||
457 | break; | ||
458 | default: | ||
459 | val |= S3C64XX_SPI_MODE_BUS_TSZ_BYTE; | ||
460 | val |= S3C64XX_SPI_MODE_CH_TSZ_BYTE; | ||
461 | break; | ||
462 | } | ||
463 | |||
464 | writel(val, regs + S3C64XX_SPI_MODE_CFG); | ||
465 | |||
466 | if (sci->clk_from_cmu) { | ||
467 | /* Configure Clock */ | ||
468 | /* There is half-multiplier before the SPI */ | ||
469 | clk_set_rate(sdd->src_clk, sdd->cur_speed * 2); | ||
470 | /* Enable Clock */ | ||
471 | clk_enable(sdd->src_clk); | ||
472 | } else { | ||
473 | /* Configure Clock */ | ||
474 | val = readl(regs + S3C64XX_SPI_CLK_CFG); | ||
475 | val &= ~S3C64XX_SPI_PSR_MASK; | ||
476 | val |= ((clk_get_rate(sdd->src_clk) / sdd->cur_speed / 2 - 1) | ||
477 | & S3C64XX_SPI_PSR_MASK); | ||
478 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | ||
479 | |||
480 | /* Enable Clock */ | ||
481 | val = readl(regs + S3C64XX_SPI_CLK_CFG); | ||
482 | val |= S3C64XX_SPI_ENCLK_ENABLE; | ||
483 | writel(val, regs + S3C64XX_SPI_CLK_CFG); | ||
484 | } | ||
485 | } | ||
486 | |||
487 | static void s3c64xx_spi_dma_rxcb(struct s3c2410_dma_chan *chan, void *buf_id, | ||
488 | int size, enum s3c2410_dma_buffresult res) | ||
489 | { | ||
490 | struct s3c64xx_spi_driver_data *sdd = buf_id; | ||
491 | unsigned long flags; | ||
492 | |||
493 | spin_lock_irqsave(&sdd->lock, flags); | ||
494 | |||
495 | if (res == S3C2410_RES_OK) | ||
496 | sdd->state &= ~RXBUSY; | ||
497 | else | ||
498 | dev_err(&sdd->pdev->dev, "DmaAbrtRx-%d\n", size); | ||
499 | |||
500 | /* If the other done */ | ||
501 | if (!(sdd->state & TXBUSY)) | ||
502 | complete(&sdd->xfer_completion); | ||
503 | |||
504 | spin_unlock_irqrestore(&sdd->lock, flags); | ||
505 | } | ||
506 | |||
507 | static void s3c64xx_spi_dma_txcb(struct s3c2410_dma_chan *chan, void *buf_id, | ||
508 | int size, enum s3c2410_dma_buffresult res) | ||
509 | { | ||
510 | struct s3c64xx_spi_driver_data *sdd = buf_id; | ||
511 | unsigned long flags; | ||
512 | |||
513 | spin_lock_irqsave(&sdd->lock, flags); | ||
514 | |||
515 | if (res == S3C2410_RES_OK) | ||
516 | sdd->state &= ~TXBUSY; | ||
517 | else | ||
518 | dev_err(&sdd->pdev->dev, "DmaAbrtTx-%d \n", size); | ||
519 | |||
520 | /* If the other done */ | ||
521 | if (!(sdd->state & RXBUSY)) | ||
522 | complete(&sdd->xfer_completion); | ||
523 | |||
524 | spin_unlock_irqrestore(&sdd->lock, flags); | ||
525 | } | ||
526 | |||
527 | #define XFER_DMAADDR_INVALID DMA_BIT_MASK(32) | ||
528 | |||
529 | static int s3c64xx_spi_map_mssg(struct s3c64xx_spi_driver_data *sdd, | ||
530 | struct spi_message *msg) | ||
531 | { | ||
532 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | ||
533 | struct device *dev = &sdd->pdev->dev; | ||
534 | struct spi_transfer *xfer; | ||
535 | |||
536 | if (msg->is_dma_mapped) | ||
537 | return 0; | ||
538 | |||
539 | /* First mark all xfer unmapped */ | ||
540 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { | ||
541 | xfer->rx_dma = XFER_DMAADDR_INVALID; | ||
542 | xfer->tx_dma = XFER_DMAADDR_INVALID; | ||
543 | } | ||
544 | |||
545 | /* Map until end or first fail */ | ||
546 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { | ||
547 | |||
548 | if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1)) | ||
549 | continue; | ||
550 | |||
551 | if (xfer->tx_buf != NULL) { | ||
552 | xfer->tx_dma = dma_map_single(dev, | ||
553 | (void *)xfer->tx_buf, xfer->len, | ||
554 | DMA_TO_DEVICE); | ||
555 | if (dma_mapping_error(dev, xfer->tx_dma)) { | ||
556 | dev_err(dev, "dma_map_single Tx failed\n"); | ||
557 | xfer->tx_dma = XFER_DMAADDR_INVALID; | ||
558 | return -ENOMEM; | ||
559 | } | ||
560 | } | ||
561 | |||
562 | if (xfer->rx_buf != NULL) { | ||
563 | xfer->rx_dma = dma_map_single(dev, xfer->rx_buf, | ||
564 | xfer->len, DMA_FROM_DEVICE); | ||
565 | if (dma_mapping_error(dev, xfer->rx_dma)) { | ||
566 | dev_err(dev, "dma_map_single Rx failed\n"); | ||
567 | dma_unmap_single(dev, xfer->tx_dma, | ||
568 | xfer->len, DMA_TO_DEVICE); | ||
569 | xfer->tx_dma = XFER_DMAADDR_INVALID; | ||
570 | xfer->rx_dma = XFER_DMAADDR_INVALID; | ||
571 | return -ENOMEM; | ||
572 | } | ||
573 | } | ||
574 | } | ||
575 | |||
576 | return 0; | ||
577 | } | ||
578 | |||
579 | static void s3c64xx_spi_unmap_mssg(struct s3c64xx_spi_driver_data *sdd, | ||
580 | struct spi_message *msg) | ||
581 | { | ||
582 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | ||
583 | struct device *dev = &sdd->pdev->dev; | ||
584 | struct spi_transfer *xfer; | ||
585 | |||
586 | if (msg->is_dma_mapped) | ||
587 | return; | ||
588 | |||
589 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { | ||
590 | |||
591 | if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1)) | ||
592 | continue; | ||
593 | |||
594 | if (xfer->rx_buf != NULL | ||
595 | && xfer->rx_dma != XFER_DMAADDR_INVALID) | ||
596 | dma_unmap_single(dev, xfer->rx_dma, | ||
597 | xfer->len, DMA_FROM_DEVICE); | ||
598 | |||
599 | if (xfer->tx_buf != NULL | ||
600 | && xfer->tx_dma != XFER_DMAADDR_INVALID) | ||
601 | dma_unmap_single(dev, xfer->tx_dma, | ||
602 | xfer->len, DMA_TO_DEVICE); | ||
603 | } | ||
604 | } | ||
605 | |||
606 | static void handle_msg(struct s3c64xx_spi_driver_data *sdd, | ||
607 | struct spi_message *msg) | ||
608 | { | ||
609 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | ||
610 | struct spi_device *spi = msg->spi; | ||
611 | struct s3c64xx_spi_csinfo *cs = spi->controller_data; | ||
612 | struct spi_transfer *xfer; | ||
613 | int status = 0, cs_toggle = 0; | ||
614 | u32 speed; | ||
615 | u8 bpw; | ||
616 | |||
617 | /* If Master's(controller) state differs from that needed by Slave */ | ||
618 | if (sdd->cur_speed != spi->max_speed_hz | ||
619 | || sdd->cur_mode != spi->mode | ||
620 | || sdd->cur_bpw != spi->bits_per_word) { | ||
621 | sdd->cur_bpw = spi->bits_per_word; | ||
622 | sdd->cur_speed = spi->max_speed_hz; | ||
623 | sdd->cur_mode = spi->mode; | ||
624 | s3c64xx_spi_config(sdd); | ||
625 | } | ||
626 | |||
627 | /* Map all the transfers if needed */ | ||
628 | if (s3c64xx_spi_map_mssg(sdd, msg)) { | ||
629 | dev_err(&spi->dev, | ||
630 | "Xfer: Unable to map message buffers!\n"); | ||
631 | status = -ENOMEM; | ||
632 | goto out; | ||
633 | } | ||
634 | |||
635 | /* Configure feedback delay */ | ||
636 | writel(cs->fb_delay & 0x3, sdd->regs + S3C64XX_SPI_FB_CLK); | ||
637 | |||
638 | list_for_each_entry(xfer, &msg->transfers, transfer_list) { | ||
639 | |||
640 | unsigned long flags; | ||
641 | int use_dma; | ||
642 | |||
643 | INIT_COMPLETION(sdd->xfer_completion); | ||
644 | |||
645 | /* Only BPW and Speed may change across transfers */ | ||
646 | bpw = xfer->bits_per_word ? : spi->bits_per_word; | ||
647 | speed = xfer->speed_hz ? : spi->max_speed_hz; | ||
648 | |||
649 | if (xfer->len % (bpw / 8)) { | ||
650 | dev_err(&spi->dev, | ||
651 | "Xfer length(%u) not a multiple of word size(%u)\n", | ||
652 | xfer->len, bpw / 8); | ||
653 | status = -EIO; | ||
654 | goto out; | ||
655 | } | ||
656 | |||
657 | if (bpw != sdd->cur_bpw || speed != sdd->cur_speed) { | ||
658 | sdd->cur_bpw = bpw; | ||
659 | sdd->cur_speed = speed; | ||
660 | s3c64xx_spi_config(sdd); | ||
661 | } | ||
662 | |||
663 | /* Polling method for xfers not bigger than FIFO capacity */ | ||
664 | if (xfer->len <= ((sci->fifo_lvl_mask >> 1) + 1)) | ||
665 | use_dma = 0; | ||
666 | else | ||
667 | use_dma = 1; | ||
668 | |||
669 | spin_lock_irqsave(&sdd->lock, flags); | ||
670 | |||
671 | /* Pending only which is to be done */ | ||
672 | sdd->state &= ~RXBUSY; | ||
673 | sdd->state &= ~TXBUSY; | ||
674 | |||
675 | enable_datapath(sdd, spi, xfer, use_dma); | ||
676 | |||
677 | /* Slave Select */ | ||
678 | enable_cs(sdd, spi); | ||
679 | |||
680 | /* Start the signals */ | ||
681 | S3C64XX_SPI_ACT(sdd); | ||
682 | |||
683 | spin_unlock_irqrestore(&sdd->lock, flags); | ||
684 | |||
685 | status = wait_for_xfer(sdd, xfer, use_dma); | ||
686 | |||
687 | /* Quiese the signals */ | ||
688 | S3C64XX_SPI_DEACT(sdd); | ||
689 | |||
690 | if (status) { | ||
691 | dev_err(&spi->dev, "I/O Error: " | ||
692 | "rx-%d tx-%d res:rx-%c tx-%c len-%d\n", | ||
693 | xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0, | ||
694 | (sdd->state & RXBUSY) ? 'f' : 'p', | ||
695 | (sdd->state & TXBUSY) ? 'f' : 'p', | ||
696 | xfer->len); | ||
697 | |||
698 | if (use_dma) { | ||
699 | if (xfer->tx_buf != NULL | ||
700 | && (sdd->state & TXBUSY)) | ||
701 | s3c2410_dma_ctrl(sdd->tx_dmach, | ||
702 | S3C2410_DMAOP_FLUSH); | ||
703 | if (xfer->rx_buf != NULL | ||
704 | && (sdd->state & RXBUSY)) | ||
705 | s3c2410_dma_ctrl(sdd->rx_dmach, | ||
706 | S3C2410_DMAOP_FLUSH); | ||
707 | } | ||
708 | |||
709 | goto out; | ||
710 | } | ||
711 | |||
712 | if (xfer->delay_usecs) | ||
713 | udelay(xfer->delay_usecs); | ||
714 | |||
715 | if (xfer->cs_change) { | ||
716 | /* Hint that the next mssg is gonna be | ||
717 | for the same device */ | ||
718 | if (list_is_last(&xfer->transfer_list, | ||
719 | &msg->transfers)) | ||
720 | cs_toggle = 1; | ||
721 | else | ||
722 | disable_cs(sdd, spi); | ||
723 | } | ||
724 | |||
725 | msg->actual_length += xfer->len; | ||
726 | |||
727 | flush_fifo(sdd); | ||
728 | } | ||
729 | |||
730 | out: | ||
731 | if (!cs_toggle || status) | ||
732 | disable_cs(sdd, spi); | ||
733 | else | ||
734 | sdd->tgl_spi = spi; | ||
735 | |||
736 | s3c64xx_spi_unmap_mssg(sdd, msg); | ||
737 | |||
738 | msg->status = status; | ||
739 | |||
740 | if (msg->complete) | ||
741 | msg->complete(msg->context); | ||
742 | } | ||
743 | |||
744 | static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) | ||
745 | { | ||
746 | if (s3c2410_dma_request(sdd->rx_dmach, | ||
747 | &s3c64xx_spi_dma_client, NULL) < 0) { | ||
748 | dev_err(&sdd->pdev->dev, "cannot get RxDMA\n"); | ||
749 | return 0; | ||
750 | } | ||
751 | s3c2410_dma_set_buffdone_fn(sdd->rx_dmach, s3c64xx_spi_dma_rxcb); | ||
752 | s3c2410_dma_devconfig(sdd->rx_dmach, S3C2410_DMASRC_HW, | ||
753 | sdd->sfr_start + S3C64XX_SPI_RX_DATA); | ||
754 | |||
755 | if (s3c2410_dma_request(sdd->tx_dmach, | ||
756 | &s3c64xx_spi_dma_client, NULL) < 0) { | ||
757 | dev_err(&sdd->pdev->dev, "cannot get TxDMA\n"); | ||
758 | s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client); | ||
759 | return 0; | ||
760 | } | ||
761 | s3c2410_dma_set_buffdone_fn(sdd->tx_dmach, s3c64xx_spi_dma_txcb); | ||
762 | s3c2410_dma_devconfig(sdd->tx_dmach, S3C2410_DMASRC_MEM, | ||
763 | sdd->sfr_start + S3C64XX_SPI_TX_DATA); | ||
764 | |||
765 | return 1; | ||
766 | } | ||
767 | |||
768 | static void s3c64xx_spi_work(struct work_struct *work) | ||
769 | { | ||
770 | struct s3c64xx_spi_driver_data *sdd = container_of(work, | ||
771 | struct s3c64xx_spi_driver_data, work); | ||
772 | unsigned long flags; | ||
773 | |||
774 | /* Acquire DMA channels */ | ||
775 | while (!acquire_dma(sdd)) | ||
776 | msleep(10); | ||
777 | |||
778 | spin_lock_irqsave(&sdd->lock, flags); | ||
779 | |||
780 | while (!list_empty(&sdd->queue) | ||
781 | && !(sdd->state & SUSPND)) { | ||
782 | |||
783 | struct spi_message *msg; | ||
784 | |||
785 | msg = container_of(sdd->queue.next, struct spi_message, queue); | ||
786 | |||
787 | list_del_init(&msg->queue); | ||
788 | |||
789 | /* Set Xfer busy flag */ | ||
790 | sdd->state |= SPIBUSY; | ||
791 | |||
792 | spin_unlock_irqrestore(&sdd->lock, flags); | ||
793 | |||
794 | handle_msg(sdd, msg); | ||
795 | |||
796 | spin_lock_irqsave(&sdd->lock, flags); | ||
797 | |||
798 | sdd->state &= ~SPIBUSY; | ||
799 | } | ||
800 | |||
801 | spin_unlock_irqrestore(&sdd->lock, flags); | ||
802 | |||
803 | /* Free DMA channels */ | ||
804 | s3c2410_dma_free(sdd->tx_dmach, &s3c64xx_spi_dma_client); | ||
805 | s3c2410_dma_free(sdd->rx_dmach, &s3c64xx_spi_dma_client); | ||
806 | } | ||
807 | |||
808 | static int s3c64xx_spi_transfer(struct spi_device *spi, | ||
809 | struct spi_message *msg) | ||
810 | { | ||
811 | struct s3c64xx_spi_driver_data *sdd; | ||
812 | unsigned long flags; | ||
813 | |||
814 | sdd = spi_master_get_devdata(spi->master); | ||
815 | |||
816 | spin_lock_irqsave(&sdd->lock, flags); | ||
817 | |||
818 | if (sdd->state & SUSPND) { | ||
819 | spin_unlock_irqrestore(&sdd->lock, flags); | ||
820 | return -ESHUTDOWN; | ||
821 | } | ||
822 | |||
823 | msg->status = -EINPROGRESS; | ||
824 | msg->actual_length = 0; | ||
825 | |||
826 | list_add_tail(&msg->queue, &sdd->queue); | ||
827 | |||
828 | queue_work(sdd->workqueue, &sdd->work); | ||
829 | |||
830 | spin_unlock_irqrestore(&sdd->lock, flags); | ||
831 | |||
832 | return 0; | ||
833 | } | ||
834 | |||
835 | /* | ||
836 | * Here we only check the validity of requested configuration | ||
837 | * and save the configuration in a local data-structure. | ||
838 | * The controller is actually configured only just before we | ||
839 | * get a message to transfer. | ||
840 | */ | ||
841 | static int s3c64xx_spi_setup(struct spi_device *spi) | ||
842 | { | ||
843 | struct s3c64xx_spi_csinfo *cs = spi->controller_data; | ||
844 | struct s3c64xx_spi_driver_data *sdd; | ||
845 | struct s3c64xx_spi_info *sci; | ||
846 | struct spi_message *msg; | ||
847 | unsigned long flags; | ||
848 | int err = 0; | ||
849 | |||
850 | if (cs == NULL || cs->set_level == NULL) { | ||
851 | dev_err(&spi->dev, "No CS for SPI(%d)\n", spi->chip_select); | ||
852 | return -ENODEV; | ||
853 | } | ||
854 | |||
855 | sdd = spi_master_get_devdata(spi->master); | ||
856 | sci = sdd->cntrlr_info; | ||
857 | |||
858 | spin_lock_irqsave(&sdd->lock, flags); | ||
859 | |||
860 | list_for_each_entry(msg, &sdd->queue, queue) { | ||
861 | /* Is some mssg is already queued for this device */ | ||
862 | if (msg->spi == spi) { | ||
863 | dev_err(&spi->dev, | ||
864 | "setup: attempt while mssg in queue!\n"); | ||
865 | spin_unlock_irqrestore(&sdd->lock, flags); | ||
866 | return -EBUSY; | ||
867 | } | ||
868 | } | ||
869 | |||
870 | if (sdd->state & SUSPND) { | ||
871 | spin_unlock_irqrestore(&sdd->lock, flags); | ||
872 | dev_err(&spi->dev, | ||
873 | "setup: SPI-%d not active!\n", spi->master->bus_num); | ||
874 | return -ESHUTDOWN; | ||
875 | } | ||
876 | |||
877 | spin_unlock_irqrestore(&sdd->lock, flags); | ||
878 | |||
879 | if (spi->bits_per_word != 8 | ||
880 | && spi->bits_per_word != 16 | ||
881 | && spi->bits_per_word != 32) { | ||
882 | dev_err(&spi->dev, "setup: %dbits/wrd not supported!\n", | ||
883 | spi->bits_per_word); | ||
884 | err = -EINVAL; | ||
885 | goto setup_exit; | ||
886 | } | ||
887 | |||
888 | /* Check if we can provide the requested rate */ | ||
889 | if (!sci->clk_from_cmu) { | ||
890 | u32 psr, speed; | ||
891 | |||
892 | /* Max possible */ | ||
893 | speed = clk_get_rate(sdd->src_clk) / 2 / (0 + 1); | ||
894 | |||
895 | if (spi->max_speed_hz > speed) | ||
896 | spi->max_speed_hz = speed; | ||
897 | |||
898 | psr = clk_get_rate(sdd->src_clk) / 2 / spi->max_speed_hz - 1; | ||
899 | psr &= S3C64XX_SPI_PSR_MASK; | ||
900 | if (psr == S3C64XX_SPI_PSR_MASK) | ||
901 | psr--; | ||
902 | |||
903 | speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); | ||
904 | if (spi->max_speed_hz < speed) { | ||
905 | if (psr+1 < S3C64XX_SPI_PSR_MASK) { | ||
906 | psr++; | ||
907 | } else { | ||
908 | err = -EINVAL; | ||
909 | goto setup_exit; | ||
910 | } | ||
911 | } | ||
912 | |||
913 | speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); | ||
914 | if (spi->max_speed_hz >= speed) | ||
915 | spi->max_speed_hz = speed; | ||
916 | else | ||
917 | err = -EINVAL; | ||
918 | } | ||
919 | |||
920 | setup_exit: | ||
921 | |||
922 | /* setup() returns with device de-selected */ | ||
923 | disable_cs(sdd, spi); | ||
924 | |||
925 | return err; | ||
926 | } | ||
927 | |||
928 | static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) | ||
929 | { | ||
930 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | ||
931 | void __iomem *regs = sdd->regs; | ||
932 | unsigned int val; | ||
933 | |||
934 | sdd->cur_speed = 0; | ||
935 | |||
936 | S3C64XX_SPI_DEACT(sdd); | ||
937 | |||
938 | /* Disable Interrupts - we use Polling if not DMA mode */ | ||
939 | writel(0, regs + S3C64XX_SPI_INT_EN); | ||
940 | |||
941 | if (!sci->clk_from_cmu) | ||
942 | writel(sci->src_clk_nr << S3C64XX_SPI_CLKSEL_SRCSHFT, | ||
943 | regs + S3C64XX_SPI_CLK_CFG); | ||
944 | writel(0, regs + S3C64XX_SPI_MODE_CFG); | ||
945 | writel(0, regs + S3C64XX_SPI_PACKET_CNT); | ||
946 | |||
947 | /* Clear any irq pending bits */ | ||
948 | writel(readl(regs + S3C64XX_SPI_PENDING_CLR), | ||
949 | regs + S3C64XX_SPI_PENDING_CLR); | ||
950 | |||
951 | writel(0, regs + S3C64XX_SPI_SWAP_CFG); | ||
952 | |||
953 | val = readl(regs + S3C64XX_SPI_MODE_CFG); | ||
954 | val &= ~S3C64XX_SPI_MODE_4BURST; | ||
955 | val &= ~(S3C64XX_SPI_MAX_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF); | ||
956 | val |= (S3C64XX_SPI_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF); | ||
957 | writel(val, regs + S3C64XX_SPI_MODE_CFG); | ||
958 | |||
959 | flush_fifo(sdd); | ||
960 | } | ||
961 | |||
962 | static int __init s3c64xx_spi_probe(struct platform_device *pdev) | ||
963 | { | ||
964 | struct resource *mem_res, *dmatx_res, *dmarx_res; | ||
965 | struct s3c64xx_spi_driver_data *sdd; | ||
966 | struct s3c64xx_spi_info *sci; | ||
967 | struct spi_master *master; | ||
968 | int ret; | ||
969 | |||
970 | if (pdev->id < 0) { | ||
971 | dev_err(&pdev->dev, | ||
972 | "Invalid platform device id-%d\n", pdev->id); | ||
973 | return -ENODEV; | ||
974 | } | ||
975 | |||
976 | if (pdev->dev.platform_data == NULL) { | ||
977 | dev_err(&pdev->dev, "platform_data missing!\n"); | ||
978 | return -ENODEV; | ||
979 | } | ||
980 | |||
981 | sci = pdev->dev.platform_data; | ||
982 | if (!sci->src_clk_name) { | ||
983 | dev_err(&pdev->dev, | ||
984 | "Board init must call s3c64xx_spi_set_info()\n"); | ||
985 | return -EINVAL; | ||
986 | } | ||
987 | |||
988 | /* Check for availability of necessary resource */ | ||
989 | |||
990 | dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
991 | if (dmatx_res == NULL) { | ||
992 | dev_err(&pdev->dev, "Unable to get SPI-Tx dma resource\n"); | ||
993 | return -ENXIO; | ||
994 | } | ||
995 | |||
996 | dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1); | ||
997 | if (dmarx_res == NULL) { | ||
998 | dev_err(&pdev->dev, "Unable to get SPI-Rx dma resource\n"); | ||
999 | return -ENXIO; | ||
1000 | } | ||
1001 | |||
1002 | mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1003 | if (mem_res == NULL) { | ||
1004 | dev_err(&pdev->dev, "Unable to get SPI MEM resource\n"); | ||
1005 | return -ENXIO; | ||
1006 | } | ||
1007 | |||
1008 | master = spi_alloc_master(&pdev->dev, | ||
1009 | sizeof(struct s3c64xx_spi_driver_data)); | ||
1010 | if (master == NULL) { | ||
1011 | dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); | ||
1012 | return -ENOMEM; | ||
1013 | } | ||
1014 | |||
1015 | platform_set_drvdata(pdev, master); | ||
1016 | |||
1017 | sdd = spi_master_get_devdata(master); | ||
1018 | sdd->master = master; | ||
1019 | sdd->cntrlr_info = sci; | ||
1020 | sdd->pdev = pdev; | ||
1021 | sdd->sfr_start = mem_res->start; | ||
1022 | sdd->tx_dmach = dmatx_res->start; | ||
1023 | sdd->rx_dmach = dmarx_res->start; | ||
1024 | |||
1025 | sdd->cur_bpw = 8; | ||
1026 | |||
1027 | master->bus_num = pdev->id; | ||
1028 | master->setup = s3c64xx_spi_setup; | ||
1029 | master->transfer = s3c64xx_spi_transfer; | ||
1030 | master->num_chipselect = sci->num_cs; | ||
1031 | master->dma_alignment = 8; | ||
1032 | /* the spi->mode bits understood by this driver: */ | ||
1033 | master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; | ||
1034 | |||
1035 | if (request_mem_region(mem_res->start, | ||
1036 | resource_size(mem_res), pdev->name) == NULL) { | ||
1037 | dev_err(&pdev->dev, "Req mem region failed\n"); | ||
1038 | ret = -ENXIO; | ||
1039 | goto err0; | ||
1040 | } | ||
1041 | |||
1042 | sdd->regs = ioremap(mem_res->start, resource_size(mem_res)); | ||
1043 | if (sdd->regs == NULL) { | ||
1044 | dev_err(&pdev->dev, "Unable to remap IO\n"); | ||
1045 | ret = -ENXIO; | ||
1046 | goto err1; | ||
1047 | } | ||
1048 | |||
1049 | if (sci->cfg_gpio == NULL || sci->cfg_gpio(pdev)) { | ||
1050 | dev_err(&pdev->dev, "Unable to config gpio\n"); | ||
1051 | ret = -EBUSY; | ||
1052 | goto err2; | ||
1053 | } | ||
1054 | |||
1055 | /* Setup clocks */ | ||
1056 | sdd->clk = clk_get(&pdev->dev, "spi"); | ||
1057 | if (IS_ERR(sdd->clk)) { | ||
1058 | dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n"); | ||
1059 | ret = PTR_ERR(sdd->clk); | ||
1060 | goto err3; | ||
1061 | } | ||
1062 | |||
1063 | if (clk_enable(sdd->clk)) { | ||
1064 | dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n"); | ||
1065 | ret = -EBUSY; | ||
1066 | goto err4; | ||
1067 | } | ||
1068 | |||
1069 | sdd->src_clk = clk_get(&pdev->dev, sci->src_clk_name); | ||
1070 | if (IS_ERR(sdd->src_clk)) { | ||
1071 | dev_err(&pdev->dev, | ||
1072 | "Unable to acquire clock '%s'\n", sci->src_clk_name); | ||
1073 | ret = PTR_ERR(sdd->src_clk); | ||
1074 | goto err5; | ||
1075 | } | ||
1076 | |||
1077 | if (clk_enable(sdd->src_clk)) { | ||
1078 | dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", | ||
1079 | sci->src_clk_name); | ||
1080 | ret = -EBUSY; | ||
1081 | goto err6; | ||
1082 | } | ||
1083 | |||
1084 | sdd->workqueue = create_singlethread_workqueue( | ||
1085 | dev_name(master->dev.parent)); | ||
1086 | if (sdd->workqueue == NULL) { | ||
1087 | dev_err(&pdev->dev, "Unable to create workqueue\n"); | ||
1088 | ret = -ENOMEM; | ||
1089 | goto err7; | ||
1090 | } | ||
1091 | |||
1092 | /* Setup Deufult Mode */ | ||
1093 | s3c64xx_spi_hwinit(sdd, pdev->id); | ||
1094 | |||
1095 | spin_lock_init(&sdd->lock); | ||
1096 | init_completion(&sdd->xfer_completion); | ||
1097 | INIT_WORK(&sdd->work, s3c64xx_spi_work); | ||
1098 | INIT_LIST_HEAD(&sdd->queue); | ||
1099 | |||
1100 | if (spi_register_master(master)) { | ||
1101 | dev_err(&pdev->dev, "cannot register SPI master\n"); | ||
1102 | ret = -EBUSY; | ||
1103 | goto err8; | ||
1104 | } | ||
1105 | |||
1106 | dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d " | ||
1107 | "with %d Slaves attached\n", | ||
1108 | pdev->id, master->num_chipselect); | ||
1109 | dev_dbg(&pdev->dev, "\tIOmem=[0x%x-0x%x]\tDMA=[Rx-%d, Tx-%d]\n", | ||
1110 | mem_res->end, mem_res->start, | ||
1111 | sdd->rx_dmach, sdd->tx_dmach); | ||
1112 | |||
1113 | return 0; | ||
1114 | |||
1115 | err8: | ||
1116 | destroy_workqueue(sdd->workqueue); | ||
1117 | err7: | ||
1118 | clk_disable(sdd->src_clk); | ||
1119 | err6: | ||
1120 | clk_put(sdd->src_clk); | ||
1121 | err5: | ||
1122 | clk_disable(sdd->clk); | ||
1123 | err4: | ||
1124 | clk_put(sdd->clk); | ||
1125 | err3: | ||
1126 | err2: | ||
1127 | iounmap((void *) sdd->regs); | ||
1128 | err1: | ||
1129 | release_mem_region(mem_res->start, resource_size(mem_res)); | ||
1130 | err0: | ||
1131 | platform_set_drvdata(pdev, NULL); | ||
1132 | spi_master_put(master); | ||
1133 | |||
1134 | return ret; | ||
1135 | } | ||
1136 | |||
1137 | static int s3c64xx_spi_remove(struct platform_device *pdev) | ||
1138 | { | ||
1139 | struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); | ||
1140 | struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); | ||
1141 | struct resource *mem_res; | ||
1142 | unsigned long flags; | ||
1143 | |||
1144 | spin_lock_irqsave(&sdd->lock, flags); | ||
1145 | sdd->state |= SUSPND; | ||
1146 | spin_unlock_irqrestore(&sdd->lock, flags); | ||
1147 | |||
1148 | while (sdd->state & SPIBUSY) | ||
1149 | msleep(10); | ||
1150 | |||
1151 | spi_unregister_master(master); | ||
1152 | |||
1153 | destroy_workqueue(sdd->workqueue); | ||
1154 | |||
1155 | clk_disable(sdd->src_clk); | ||
1156 | clk_put(sdd->src_clk); | ||
1157 | |||
1158 | clk_disable(sdd->clk); | ||
1159 | clk_put(sdd->clk); | ||
1160 | |||
1161 | iounmap((void *) sdd->regs); | ||
1162 | |||
1163 | mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
1164 | if (mem_res != NULL) | ||
1165 | release_mem_region(mem_res->start, resource_size(mem_res)); | ||
1166 | |||
1167 | platform_set_drvdata(pdev, NULL); | ||
1168 | spi_master_put(master); | ||
1169 | |||
1170 | return 0; | ||
1171 | } | ||
1172 | |||
1173 | #ifdef CONFIG_PM | ||
1174 | static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state) | ||
1175 | { | ||
1176 | struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); | ||
1177 | struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); | ||
1178 | unsigned long flags; | ||
1179 | |||
1180 | spin_lock_irqsave(&sdd->lock, flags); | ||
1181 | sdd->state |= SUSPND; | ||
1182 | spin_unlock_irqrestore(&sdd->lock, flags); | ||
1183 | |||
1184 | while (sdd->state & SPIBUSY) | ||
1185 | msleep(10); | ||
1186 | |||
1187 | /* Disable the clock */ | ||
1188 | clk_disable(sdd->src_clk); | ||
1189 | clk_disable(sdd->clk); | ||
1190 | |||
1191 | sdd->cur_speed = 0; /* Output Clock is stopped */ | ||
1192 | |||
1193 | return 0; | ||
1194 | } | ||
1195 | |||
1196 | static int s3c64xx_spi_resume(struct platform_device *pdev) | ||
1197 | { | ||
1198 | struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); | ||
1199 | struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); | ||
1200 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | ||
1201 | unsigned long flags; | ||
1202 | |||
1203 | sci->cfg_gpio(pdev); | ||
1204 | |||
1205 | /* Enable the clock */ | ||
1206 | clk_enable(sdd->src_clk); | ||
1207 | clk_enable(sdd->clk); | ||
1208 | |||
1209 | s3c64xx_spi_hwinit(sdd, pdev->id); | ||
1210 | |||
1211 | spin_lock_irqsave(&sdd->lock, flags); | ||
1212 | sdd->state &= ~SUSPND; | ||
1213 | spin_unlock_irqrestore(&sdd->lock, flags); | ||
1214 | |||
1215 | return 0; | ||
1216 | } | ||
1217 | #else | ||
1218 | #define s3c64xx_spi_suspend NULL | ||
1219 | #define s3c64xx_spi_resume NULL | ||
1220 | #endif /* CONFIG_PM */ | ||
1221 | |||
1222 | static struct platform_driver s3c64xx_spi_driver = { | ||
1223 | .driver = { | ||
1224 | .name = "s3c64xx-spi", | ||
1225 | .owner = THIS_MODULE, | ||
1226 | }, | ||
1227 | .remove = s3c64xx_spi_remove, | ||
1228 | .suspend = s3c64xx_spi_suspend, | ||
1229 | .resume = s3c64xx_spi_resume, | ||
1230 | }; | ||
1231 | MODULE_ALIAS("platform:s3c64xx-spi"); | ||
1232 | |||
1233 | static int __init s3c64xx_spi_init(void) | ||
1234 | { | ||
1235 | return platform_driver_probe(&s3c64xx_spi_driver, s3c64xx_spi_probe); | ||
1236 | } | ||
1237 | subsys_initcall(s3c64xx_spi_init); | ||
1238 | |||
1239 | static void __exit s3c64xx_spi_exit(void) | ||
1240 | { | ||
1241 | platform_driver_unregister(&s3c64xx_spi_driver); | ||
1242 | } | ||
1243 | module_exit(s3c64xx_spi_exit); | ||
1244 | |||
1245 | MODULE_AUTHOR("Jaswinder Singh <jassi.brar@samsung.com>"); | ||
1246 | MODULE_DESCRIPTION("S3C64XX SPI Controller Driver"); | ||
1247 | MODULE_LICENSE("GPL"); | ||