Commit | Line | Data |
---|---|---|
b5f3294f SH |
1 | /* |
2 | * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. | |
3 | * Copyright (C) 2008 Juergen Beisert | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or | |
6 | * modify it under the terms of the GNU General Public License | |
7 | * as published by the Free Software Foundation; either version 2 | |
8 | * of the License, or (at your option) any later version. | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License | |
15 | * along with this program; if not, write to the | |
16 | * Free Software Foundation | |
17 | * 51 Franklin Street, Fifth Floor | |
18 | * Boston, MA 02110-1301, USA. | |
19 | */ | |
20 | ||
21 | #include <linux/clk.h> | |
22 | #include <linux/completion.h> | |
23 | #include <linux/delay.h> | |
24 | #include <linux/err.h> | |
25 | #include <linux/gpio.h> | |
26 | #include <linux/init.h> | |
27 | #include <linux/interrupt.h> | |
28 | #include <linux/io.h> | |
29 | #include <linux/irq.h> | |
30 | #include <linux/kernel.h> | |
31 | #include <linux/module.h> | |
32 | #include <linux/platform_device.h> | |
5a0e3ad6 | 33 | #include <linux/slab.h> |
b5f3294f SH |
34 | #include <linux/spi/spi.h> |
35 | #include <linux/spi/spi_bitbang.h> | |
36 | #include <linux/types.h> | |
37 | ||
38 | #include <mach/spi.h> | |
39 | ||
40 | #define DRIVER_NAME "spi_imx" | |
41 | ||
42 | #define MXC_CSPIRXDATA 0x00 | |
43 | #define MXC_CSPITXDATA 0x04 | |
44 | #define MXC_CSPICTRL 0x08 | |
45 | #define MXC_CSPIINT 0x0c | |
46 | #define MXC_RESET 0x1c | |
47 | ||
ce1807b2 DM |
48 | #define MX3_CSPISTAT 0x14 |
49 | #define MX3_CSPISTAT_RR (1 << 3) | |
50 | ||
b5f3294f SH |
51 | /* generic defines to abstract from the different register layouts */ |
52 | #define MXC_INT_RR (1 << 0) /* Receive data ready interrupt */ | |
53 | #define MXC_INT_TE (1 << 1) /* Transmit FIFO empty interrupt */ | |
54 | ||
6cdeb002 | 55 | struct spi_imx_config { |
b5f3294f SH |
56 | unsigned int speed_hz; |
57 | unsigned int bpw; | |
58 | unsigned int mode; | |
59 | int cs; | |
60 | }; | |
61 | ||
f4ba6315 UKK |
62 | enum spi_imx_devtype { |
63 | SPI_IMX_VER_IMX1, | |
64 | SPI_IMX_VER_0_0, | |
65 | SPI_IMX_VER_0_4, | |
66 | SPI_IMX_VER_0_5, | |
67 | SPI_IMX_VER_0_7, | |
68 | SPI_IMX_VER_AUTODETECT, | |
69 | }; | |
70 | ||
71 | struct spi_imx_data; | |
72 | ||
73 | struct spi_imx_devtype_data { | |
74 | void (*intctrl)(struct spi_imx_data *, int); | |
75 | int (*config)(struct spi_imx_data *, struct spi_imx_config *); | |
76 | void (*trigger)(struct spi_imx_data *); | |
77 | int (*rx_available)(struct spi_imx_data *); | |
1723e66b | 78 | void (*reset)(struct spi_imx_data *); |
f4ba6315 UKK |
79 | }; |
80 | ||
6cdeb002 | 81 | struct spi_imx_data { |
b5f3294f SH |
82 | struct spi_bitbang bitbang; |
83 | ||
84 | struct completion xfer_done; | |
85 | void *base; | |
86 | int irq; | |
87 | struct clk *clk; | |
88 | unsigned long spi_clk; | |
89 | int *chipselect; | |
90 | ||
91 | unsigned int count; | |
6cdeb002 UKK |
92 | void (*tx)(struct spi_imx_data *); |
93 | void (*rx)(struct spi_imx_data *); | |
b5f3294f SH |
94 | void *rx_buf; |
95 | const void *tx_buf; | |
96 | unsigned int txfifo; /* number of words pushed in tx FIFO */ | |
97 | ||
f4ba6315 | 98 | struct spi_imx_devtype_data devtype_data; |
b5f3294f SH |
99 | }; |
100 | ||
101 | #define MXC_SPI_BUF_RX(type) \ | |
6cdeb002 | 102 | static void spi_imx_buf_rx_##type(struct spi_imx_data *spi_imx) \ |
b5f3294f | 103 | { \ |
6cdeb002 | 104 | unsigned int val = readl(spi_imx->base + MXC_CSPIRXDATA); \ |
b5f3294f | 105 | \ |
6cdeb002 UKK |
106 | if (spi_imx->rx_buf) { \ |
107 | *(type *)spi_imx->rx_buf = val; \ | |
108 | spi_imx->rx_buf += sizeof(type); \ | |
b5f3294f SH |
109 | } \ |
110 | } | |
111 | ||
112 | #define MXC_SPI_BUF_TX(type) \ | |
6cdeb002 | 113 | static void spi_imx_buf_tx_##type(struct spi_imx_data *spi_imx) \ |
b5f3294f SH |
114 | { \ |
115 | type val = 0; \ | |
116 | \ | |
6cdeb002 UKK |
117 | if (spi_imx->tx_buf) { \ |
118 | val = *(type *)spi_imx->tx_buf; \ | |
119 | spi_imx->tx_buf += sizeof(type); \ | |
b5f3294f SH |
120 | } \ |
121 | \ | |
6cdeb002 | 122 | spi_imx->count -= sizeof(type); \ |
b5f3294f | 123 | \ |
6cdeb002 | 124 | writel(val, spi_imx->base + MXC_CSPITXDATA); \ |
b5f3294f SH |
125 | } |
126 | ||
127 | MXC_SPI_BUF_RX(u8) | |
128 | MXC_SPI_BUF_TX(u8) | |
129 | MXC_SPI_BUF_RX(u16) | |
130 | MXC_SPI_BUF_TX(u16) | |
131 | MXC_SPI_BUF_RX(u32) | |
132 | MXC_SPI_BUF_TX(u32) | |
133 | ||
134 | /* First entry is reserved, second entry is valid only if SDHC_SPIEN is set | |
135 | * (which is currently not the case in this driver) | |
136 | */ | |
137 | static int mxc_clkdivs[] = {0, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, | |
138 | 256, 384, 512, 768, 1024}; | |
139 | ||
140 | /* MX21, MX27 */ | |
6cdeb002 | 141 | static unsigned int spi_imx_clkdiv_1(unsigned int fin, |
b5f3294f SH |
142 | unsigned int fspi) |
143 | { | |
144 | int i, max; | |
145 | ||
146 | if (cpu_is_mx21()) | |
147 | max = 18; | |
148 | else | |
149 | max = 16; | |
150 | ||
151 | for (i = 2; i < max; i++) | |
152 | if (fspi * mxc_clkdivs[i] >= fin) | |
153 | return i; | |
154 | ||
155 | return max; | |
156 | } | |
157 | ||
158 | /* MX1, MX31, MX35 */ | |
6cdeb002 | 159 | static unsigned int spi_imx_clkdiv_2(unsigned int fin, |
b5f3294f SH |
160 | unsigned int fspi) |
161 | { | |
162 | int i, div = 4; | |
163 | ||
164 | for (i = 0; i < 7; i++) { | |
165 | if (fspi * div >= fin) | |
166 | return i; | |
167 | div <<= 1; | |
168 | } | |
169 | ||
170 | return 7; | |
171 | } | |
172 | ||
173 | #define MX31_INTREG_TEEN (1 << 0) | |
174 | #define MX31_INTREG_RREN (1 << 3) | |
175 | ||
176 | #define MX31_CSPICTRL_ENABLE (1 << 0) | |
177 | #define MX31_CSPICTRL_MASTER (1 << 1) | |
178 | #define MX31_CSPICTRL_XCH (1 << 2) | |
179 | #define MX31_CSPICTRL_POL (1 << 4) | |
180 | #define MX31_CSPICTRL_PHA (1 << 5) | |
181 | #define MX31_CSPICTRL_SSCTL (1 << 6) | |
182 | #define MX31_CSPICTRL_SSPOL (1 << 7) | |
183 | #define MX31_CSPICTRL_BC_SHIFT 8 | |
184 | #define MX35_CSPICTRL_BL_SHIFT 20 | |
185 | #define MX31_CSPICTRL_CS_SHIFT 24 | |
186 | #define MX35_CSPICTRL_CS_SHIFT 12 | |
187 | #define MX31_CSPICTRL_DR_SHIFT 16 | |
188 | ||
189 | #define MX31_CSPISTATUS 0x14 | |
190 | #define MX31_STATUS_RR (1 << 3) | |
191 | ||
192 | /* These functions also work for the i.MX35, but be aware that | |
193 | * the i.MX35 has a slightly different register layout for bits | |
194 | * we do not use here. | |
195 | */ | |
f4ba6315 | 196 | static void __maybe_unused mx31_intctrl(struct spi_imx_data *spi_imx, int enable) |
b5f3294f SH |
197 | { |
198 | unsigned int val = 0; | |
199 | ||
200 | if (enable & MXC_INT_TE) | |
201 | val |= MX31_INTREG_TEEN; | |
202 | if (enable & MXC_INT_RR) | |
203 | val |= MX31_INTREG_RREN; | |
204 | ||
6cdeb002 | 205 | writel(val, spi_imx->base + MXC_CSPIINT); |
b5f3294f SH |
206 | } |
207 | ||
f4ba6315 | 208 | static void __maybe_unused mx31_trigger(struct spi_imx_data *spi_imx) |
b5f3294f SH |
209 | { |
210 | unsigned int reg; | |
211 | ||
6cdeb002 | 212 | reg = readl(spi_imx->base + MXC_CSPICTRL); |
b5f3294f | 213 | reg |= MX31_CSPICTRL_XCH; |
6cdeb002 | 214 | writel(reg, spi_imx->base + MXC_CSPICTRL); |
b5f3294f SH |
215 | } |
216 | ||
1723e66b | 217 | static int __maybe_unused spi_imx0_4_config(struct spi_imx_data *spi_imx, |
6cdeb002 | 218 | struct spi_imx_config *config) |
b5f3294f SH |
219 | { |
220 | unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; | |
221 | ||
6cdeb002 | 222 | reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << |
b5f3294f SH |
223 | MX31_CSPICTRL_DR_SHIFT; |
224 | ||
1723e66b | 225 | reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT; |
b5f3294f SH |
226 | |
227 | if (config->mode & SPI_CPHA) | |
228 | reg |= MX31_CSPICTRL_PHA; | |
229 | if (config->mode & SPI_CPOL) | |
230 | reg |= MX31_CSPICTRL_POL; | |
231 | if (config->mode & SPI_CS_HIGH) | |
232 | reg |= MX31_CSPICTRL_SSPOL; | |
233 | if (config->cs < 0) { | |
1723e66b | 234 | reg |= (config->cs + 32) << MX31_CSPICTRL_CS_SHIFT; |
b5f3294f SH |
235 | } |
236 | ||
6cdeb002 | 237 | writel(reg, spi_imx->base + MXC_CSPICTRL); |
b5f3294f SH |
238 | |
239 | return 0; | |
240 | } | |
241 | ||
1723e66b UKK |
242 | static int __maybe_unused spi_imx0_7_config(struct spi_imx_data *spi_imx, |
243 | struct spi_imx_config *config) | |
244 | { | |
245 | unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; | |
246 | ||
247 | reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << | |
248 | MX31_CSPICTRL_DR_SHIFT; | |
249 | ||
250 | reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT; | |
251 | reg |= MX31_CSPICTRL_SSCTL; | |
252 | ||
253 | if (config->mode & SPI_CPHA) | |
254 | reg |= MX31_CSPICTRL_PHA; | |
255 | if (config->mode & SPI_CPOL) | |
256 | reg |= MX31_CSPICTRL_POL; | |
257 | if (config->mode & SPI_CS_HIGH) | |
258 | reg |= MX31_CSPICTRL_SSPOL; | |
259 | if (config->cs < 0) | |
260 | reg |= (config->cs + 32) << MX35_CSPICTRL_CS_SHIFT; | |
261 | ||
262 | writel(reg, spi_imx->base + MXC_CSPICTRL); | |
263 | ||
264 | return 0; | |
265 | } | |
266 | ||
f4ba6315 | 267 | static int __maybe_unused mx31_rx_available(struct spi_imx_data *spi_imx) |
b5f3294f | 268 | { |
6cdeb002 | 269 | return readl(spi_imx->base + MX31_CSPISTATUS) & MX31_STATUS_RR; |
b5f3294f SH |
270 | } |
271 | ||
1723e66b UKK |
272 | static void __maybe_unused spi_imx0_4_reset(struct spi_imx_data *spi_imx) |
273 | { | |
274 | /* drain receive buffer */ | |
275 | while (readl(spi_imx->base + MX3_CSPISTAT) & MX3_CSPISTAT_RR) | |
276 | readl(spi_imx->base + MXC_CSPIRXDATA); | |
277 | } | |
278 | ||
b5f3294f SH |
279 | #define MX27_INTREG_RR (1 << 4) |
280 | #define MX27_INTREG_TEEN (1 << 9) | |
281 | #define MX27_INTREG_RREN (1 << 13) | |
282 | ||
283 | #define MX27_CSPICTRL_POL (1 << 5) | |
284 | #define MX27_CSPICTRL_PHA (1 << 6) | |
285 | #define MX27_CSPICTRL_SSPOL (1 << 8) | |
286 | #define MX27_CSPICTRL_XCH (1 << 9) | |
287 | #define MX27_CSPICTRL_ENABLE (1 << 10) | |
288 | #define MX27_CSPICTRL_MASTER (1 << 11) | |
289 | #define MX27_CSPICTRL_DR_SHIFT 14 | |
290 | #define MX27_CSPICTRL_CS_SHIFT 19 | |
291 | ||
f4ba6315 | 292 | static void __maybe_unused mx27_intctrl(struct spi_imx_data *spi_imx, int enable) |
b5f3294f SH |
293 | { |
294 | unsigned int val = 0; | |
295 | ||
296 | if (enable & MXC_INT_TE) | |
297 | val |= MX27_INTREG_TEEN; | |
298 | if (enable & MXC_INT_RR) | |
299 | val |= MX27_INTREG_RREN; | |
300 | ||
6cdeb002 | 301 | writel(val, spi_imx->base + MXC_CSPIINT); |
b5f3294f SH |
302 | } |
303 | ||
f4ba6315 | 304 | static void __maybe_unused mx27_trigger(struct spi_imx_data *spi_imx) |
b5f3294f SH |
305 | { |
306 | unsigned int reg; | |
307 | ||
6cdeb002 | 308 | reg = readl(spi_imx->base + MXC_CSPICTRL); |
b5f3294f | 309 | reg |= MX27_CSPICTRL_XCH; |
6cdeb002 | 310 | writel(reg, spi_imx->base + MXC_CSPICTRL); |
b5f3294f SH |
311 | } |
312 | ||
f4ba6315 | 313 | static int __maybe_unused mx27_config(struct spi_imx_data *spi_imx, |
6cdeb002 | 314 | struct spi_imx_config *config) |
b5f3294f SH |
315 | { |
316 | unsigned int reg = MX27_CSPICTRL_ENABLE | MX27_CSPICTRL_MASTER; | |
317 | ||
6cdeb002 | 318 | reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, config->speed_hz) << |
b5f3294f SH |
319 | MX27_CSPICTRL_DR_SHIFT; |
320 | reg |= config->bpw - 1; | |
321 | ||
322 | if (config->mode & SPI_CPHA) | |
323 | reg |= MX27_CSPICTRL_PHA; | |
324 | if (config->mode & SPI_CPOL) | |
325 | reg |= MX27_CSPICTRL_POL; | |
326 | if (config->mode & SPI_CS_HIGH) | |
327 | reg |= MX27_CSPICTRL_SSPOL; | |
328 | if (config->cs < 0) | |
329 | reg |= (config->cs + 32) << MX27_CSPICTRL_CS_SHIFT; | |
330 | ||
6cdeb002 | 331 | writel(reg, spi_imx->base + MXC_CSPICTRL); |
b5f3294f SH |
332 | |
333 | return 0; | |
334 | } | |
335 | ||
f4ba6315 | 336 | static int __maybe_unused mx27_rx_available(struct spi_imx_data *spi_imx) |
b5f3294f | 337 | { |
6cdeb002 | 338 | return readl(spi_imx->base + MXC_CSPIINT) & MX27_INTREG_RR; |
b5f3294f SH |
339 | } |
340 | ||
1723e66b UKK |
341 | static void __maybe_unused spi_imx0_0_reset(struct spi_imx_data *spi_imx) |
342 | { | |
343 | writel(1, spi_imx->base + MXC_RESET); | |
344 | } | |
345 | ||
b5f3294f SH |
346 | #define MX1_INTREG_RR (1 << 3) |
347 | #define MX1_INTREG_TEEN (1 << 8) | |
348 | #define MX1_INTREG_RREN (1 << 11) | |
349 | ||
350 | #define MX1_CSPICTRL_POL (1 << 4) | |
351 | #define MX1_CSPICTRL_PHA (1 << 5) | |
352 | #define MX1_CSPICTRL_XCH (1 << 8) | |
353 | #define MX1_CSPICTRL_ENABLE (1 << 9) | |
354 | #define MX1_CSPICTRL_MASTER (1 << 10) | |
355 | #define MX1_CSPICTRL_DR_SHIFT 13 | |
356 | ||
f4ba6315 | 357 | static void __maybe_unused mx1_intctrl(struct spi_imx_data *spi_imx, int enable) |
b5f3294f SH |
358 | { |
359 | unsigned int val = 0; | |
360 | ||
361 | if (enable & MXC_INT_TE) | |
362 | val |= MX1_INTREG_TEEN; | |
363 | if (enable & MXC_INT_RR) | |
364 | val |= MX1_INTREG_RREN; | |
365 | ||
6cdeb002 | 366 | writel(val, spi_imx->base + MXC_CSPIINT); |
b5f3294f SH |
367 | } |
368 | ||
f4ba6315 | 369 | static void __maybe_unused mx1_trigger(struct spi_imx_data *spi_imx) |
b5f3294f SH |
370 | { |
371 | unsigned int reg; | |
372 | ||
6cdeb002 | 373 | reg = readl(spi_imx->base + MXC_CSPICTRL); |
b5f3294f | 374 | reg |= MX1_CSPICTRL_XCH; |
6cdeb002 | 375 | writel(reg, spi_imx->base + MXC_CSPICTRL); |
b5f3294f SH |
376 | } |
377 | ||
f4ba6315 | 378 | static int __maybe_unused mx1_config(struct spi_imx_data *spi_imx, |
6cdeb002 | 379 | struct spi_imx_config *config) |
b5f3294f SH |
380 | { |
381 | unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_MASTER; | |
382 | ||
6cdeb002 | 383 | reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << |
b5f3294f SH |
384 | MX1_CSPICTRL_DR_SHIFT; |
385 | reg |= config->bpw - 1; | |
386 | ||
387 | if (config->mode & SPI_CPHA) | |
388 | reg |= MX1_CSPICTRL_PHA; | |
389 | if (config->mode & SPI_CPOL) | |
390 | reg |= MX1_CSPICTRL_POL; | |
391 | ||
6cdeb002 | 392 | writel(reg, spi_imx->base + MXC_CSPICTRL); |
b5f3294f SH |
393 | |
394 | return 0; | |
395 | } | |
396 | ||
f4ba6315 | 397 | static int __maybe_unused mx1_rx_available(struct spi_imx_data *spi_imx) |
b5f3294f | 398 | { |
6cdeb002 | 399 | return readl(spi_imx->base + MXC_CSPIINT) & MX1_INTREG_RR; |
b5f3294f SH |
400 | } |
401 | ||
1723e66b UKK |
402 | static void __maybe_unused mx1_reset(struct spi_imx_data *spi_imx) |
403 | { | |
404 | writel(1, spi_imx->base + MXC_RESET); | |
405 | } | |
406 | ||
f4ba6315 UKK |
407 | /* |
408 | * These version numbers are taken from the Freescale driver. Unfortunately it | |
409 | * doesn't support i.MX1, so this entry doesn't match the scheme. :-( | |
410 | */ | |
411 | static struct spi_imx_devtype_data spi_imx_devtype_data[] __devinitdata = { | |
412 | #ifdef CONFIG_SPI_IMX_VER_IMX1 | |
413 | [SPI_IMX_VER_IMX1] = { | |
414 | .intctrl = mx1_intctrl, | |
415 | .config = mx1_config, | |
416 | .trigger = mx1_trigger, | |
417 | .rx_available = mx1_rx_available, | |
1723e66b | 418 | .reset = mx1_reset, |
f4ba6315 UKK |
419 | }, |
420 | #endif | |
421 | #ifdef CONFIG_SPI_IMX_VER_0_0 | |
422 | [SPI_IMX_VER_0_0] = { | |
423 | .intctrl = mx27_intctrl, | |
424 | .config = mx27_config, | |
425 | .trigger = mx27_trigger, | |
426 | .rx_available = mx27_rx_available, | |
1723e66b | 427 | .reset = spi_imx0_0_reset, |
f4ba6315 UKK |
428 | }, |
429 | #endif | |
430 | #ifdef CONFIG_SPI_IMX_VER_0_4 | |
431 | [SPI_IMX_VER_0_4] = { | |
432 | .intctrl = mx31_intctrl, | |
1723e66b | 433 | .config = spi_imx0_4_config, |
f4ba6315 UKK |
434 | .trigger = mx31_trigger, |
435 | .rx_available = mx31_rx_available, | |
1723e66b | 436 | .reset = spi_imx0_4_reset, |
f4ba6315 UKK |
437 | }, |
438 | #endif | |
439 | #ifdef CONFIG_SPI_IMX_VER_0_7 | |
440 | [SPI_IMX_VER_0_7] = { | |
441 | .intctrl = mx31_intctrl, | |
1723e66b | 442 | .config = spi_imx0_7_config, |
f4ba6315 UKK |
443 | .trigger = mx31_trigger, |
444 | .rx_available = mx31_rx_available, | |
1723e66b | 445 | .reset = spi_imx0_4_reset, |
f4ba6315 UKK |
446 | }, |
447 | #endif | |
448 | }; | |
449 | ||
6cdeb002 | 450 | static void spi_imx_chipselect(struct spi_device *spi, int is_active) |
b5f3294f | 451 | { |
6cdeb002 | 452 | struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); |
6cdeb002 | 453 | int gpio = spi_imx->chipselect[spi->chip_select]; |
e6a0a8bf UKK |
454 | int active = is_active != BITBANG_CS_INACTIVE; |
455 | int dev_is_lowactive = !(spi->mode & SPI_CS_HIGH); | |
b5f3294f | 456 | |
e6a0a8bf | 457 | if (gpio < 0) |
b5f3294f | 458 | return; |
b5f3294f | 459 | |
e6a0a8bf | 460 | gpio_set_value(gpio, dev_is_lowactive ^ active); |
b5f3294f SH |
461 | } |
462 | ||
6cdeb002 | 463 | static void spi_imx_push(struct spi_imx_data *spi_imx) |
b5f3294f | 464 | { |
6cdeb002 UKK |
465 | while (spi_imx->txfifo < 8) { |
466 | if (!spi_imx->count) | |
b5f3294f | 467 | break; |
6cdeb002 UKK |
468 | spi_imx->tx(spi_imx); |
469 | spi_imx->txfifo++; | |
b5f3294f SH |
470 | } |
471 | ||
f4ba6315 | 472 | spi_imx->devtype_data.trigger(spi_imx); |
b5f3294f SH |
473 | } |
474 | ||
6cdeb002 | 475 | static irqreturn_t spi_imx_isr(int irq, void *dev_id) |
b5f3294f | 476 | { |
6cdeb002 | 477 | struct spi_imx_data *spi_imx = dev_id; |
b5f3294f | 478 | |
f4ba6315 | 479 | while (spi_imx->devtype_data.rx_available(spi_imx)) { |
6cdeb002 UKK |
480 | spi_imx->rx(spi_imx); |
481 | spi_imx->txfifo--; | |
b5f3294f SH |
482 | } |
483 | ||
6cdeb002 UKK |
484 | if (spi_imx->count) { |
485 | spi_imx_push(spi_imx); | |
b5f3294f SH |
486 | return IRQ_HANDLED; |
487 | } | |
488 | ||
6cdeb002 | 489 | if (spi_imx->txfifo) { |
b5f3294f SH |
490 | /* No data left to push, but still waiting for rx data, |
491 | * enable receive data available interrupt. | |
492 | */ | |
f4ba6315 UKK |
493 | spi_imx->devtype_data.intctrl( |
494 | spi_imx, MXC_INT_RR); | |
b5f3294f SH |
495 | return IRQ_HANDLED; |
496 | } | |
497 | ||
f4ba6315 | 498 | spi_imx->devtype_data.intctrl(spi_imx, 0); |
6cdeb002 | 499 | complete(&spi_imx->xfer_done); |
b5f3294f SH |
500 | |
501 | return IRQ_HANDLED; | |
502 | } | |
503 | ||
6cdeb002 | 504 | static int spi_imx_setupxfer(struct spi_device *spi, |
b5f3294f SH |
505 | struct spi_transfer *t) |
506 | { | |
6cdeb002 UKK |
507 | struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); |
508 | struct spi_imx_config config; | |
b5f3294f SH |
509 | |
510 | config.bpw = t ? t->bits_per_word : spi->bits_per_word; | |
511 | config.speed_hz = t ? t->speed_hz : spi->max_speed_hz; | |
512 | config.mode = spi->mode; | |
d1c627b5 | 513 | config.cs = spi_imx->chipselect[spi->chip_select]; |
b5f3294f | 514 | |
462d26b5 SH |
515 | if (!config.speed_hz) |
516 | config.speed_hz = spi->max_speed_hz; | |
517 | if (!config.bpw) | |
518 | config.bpw = spi->bits_per_word; | |
519 | if (!config.speed_hz) | |
520 | config.speed_hz = spi->max_speed_hz; | |
521 | ||
e6a0a8bf UKK |
522 | /* Initialize the functions for transfer */ |
523 | if (config.bpw <= 8) { | |
524 | spi_imx->rx = spi_imx_buf_rx_u8; | |
525 | spi_imx->tx = spi_imx_buf_tx_u8; | |
526 | } else if (config.bpw <= 16) { | |
527 | spi_imx->rx = spi_imx_buf_rx_u16; | |
528 | spi_imx->tx = spi_imx_buf_tx_u16; | |
529 | } else if (config.bpw <= 32) { | |
530 | spi_imx->rx = spi_imx_buf_rx_u32; | |
531 | spi_imx->tx = spi_imx_buf_tx_u32; | |
532 | } else | |
533 | BUG(); | |
534 | ||
f4ba6315 | 535 | spi_imx->devtype_data.config(spi_imx, &config); |
b5f3294f SH |
536 | |
537 | return 0; | |
538 | } | |
539 | ||
6cdeb002 | 540 | static int spi_imx_transfer(struct spi_device *spi, |
b5f3294f SH |
541 | struct spi_transfer *transfer) |
542 | { | |
6cdeb002 | 543 | struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); |
b5f3294f | 544 | |
6cdeb002 UKK |
545 | spi_imx->tx_buf = transfer->tx_buf; |
546 | spi_imx->rx_buf = transfer->rx_buf; | |
547 | spi_imx->count = transfer->len; | |
548 | spi_imx->txfifo = 0; | |
b5f3294f | 549 | |
6cdeb002 | 550 | init_completion(&spi_imx->xfer_done); |
b5f3294f | 551 | |
6cdeb002 | 552 | spi_imx_push(spi_imx); |
b5f3294f | 553 | |
f4ba6315 | 554 | spi_imx->devtype_data.intctrl(spi_imx, MXC_INT_TE); |
b5f3294f | 555 | |
6cdeb002 | 556 | wait_for_completion(&spi_imx->xfer_done); |
b5f3294f SH |
557 | |
558 | return transfer->len; | |
559 | } | |
560 | ||
6cdeb002 | 561 | static int spi_imx_setup(struct spi_device *spi) |
b5f3294f | 562 | { |
6c23e5d4 SH |
563 | struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); |
564 | int gpio = spi_imx->chipselect[spi->chip_select]; | |
565 | ||
f4d4ecfe | 566 | dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n", __func__, |
b5f3294f SH |
567 | spi->mode, spi->bits_per_word, spi->max_speed_hz); |
568 | ||
6c23e5d4 SH |
569 | if (gpio >= 0) |
570 | gpio_direction_output(gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); | |
571 | ||
6cdeb002 | 572 | spi_imx_chipselect(spi, BITBANG_CS_INACTIVE); |
b5f3294f SH |
573 | |
574 | return 0; | |
575 | } | |
576 | ||
6cdeb002 | 577 | static void spi_imx_cleanup(struct spi_device *spi) |
b5f3294f SH |
578 | { |
579 | } | |
580 | ||
f4ba6315 UKK |
581 | static struct platform_device_id spi_imx_devtype[] = { |
582 | { | |
583 | .name = DRIVER_NAME, | |
584 | .driver_data = SPI_IMX_VER_AUTODETECT, | |
585 | }, { | |
586 | .name = "imx1-cspi", | |
587 | .driver_data = SPI_IMX_VER_IMX1, | |
588 | }, { | |
589 | .name = "imx21-cspi", | |
590 | .driver_data = SPI_IMX_VER_0_0, | |
591 | }, { | |
592 | .name = "imx25-cspi", | |
593 | .driver_data = SPI_IMX_VER_0_7, | |
594 | }, { | |
595 | .name = "imx27-cspi", | |
596 | .driver_data = SPI_IMX_VER_0_0, | |
597 | }, { | |
598 | .name = "imx31-cspi", | |
599 | .driver_data = SPI_IMX_VER_0_4, | |
600 | }, { | |
601 | .name = "imx35-cspi", | |
602 | .driver_data = SPI_IMX_VER_0_7, | |
603 | }, { | |
604 | /* sentinel */ | |
605 | } | |
606 | }; | |
607 | ||
965346e3 | 608 | static int __devinit spi_imx_probe(struct platform_device *pdev) |
b5f3294f SH |
609 | { |
610 | struct spi_imx_master *mxc_platform_info; | |
611 | struct spi_master *master; | |
6cdeb002 | 612 | struct spi_imx_data *spi_imx; |
b5f3294f SH |
613 | struct resource *res; |
614 | int i, ret; | |
615 | ||
980f3bee | 616 | mxc_platform_info = dev_get_platdata(&pdev->dev); |
b5f3294f SH |
617 | if (!mxc_platform_info) { |
618 | dev_err(&pdev->dev, "can't get the platform data\n"); | |
619 | return -EINVAL; | |
620 | } | |
621 | ||
6cdeb002 | 622 | master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data)); |
b5f3294f SH |
623 | if (!master) |
624 | return -ENOMEM; | |
625 | ||
626 | platform_set_drvdata(pdev, master); | |
627 | ||
628 | master->bus_num = pdev->id; | |
629 | master->num_chipselect = mxc_platform_info->num_chipselect; | |
630 | ||
6cdeb002 UKK |
631 | spi_imx = spi_master_get_devdata(master); |
632 | spi_imx->bitbang.master = spi_master_get(master); | |
633 | spi_imx->chipselect = mxc_platform_info->chipselect; | |
b5f3294f SH |
634 | |
635 | for (i = 0; i < master->num_chipselect; i++) { | |
6cdeb002 | 636 | if (spi_imx->chipselect[i] < 0) |
b5f3294f | 637 | continue; |
6cdeb002 | 638 | ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME); |
b5f3294f | 639 | if (ret) { |
bbd050af JO |
640 | while (i > 0) { |
641 | i--; | |
6cdeb002 | 642 | if (spi_imx->chipselect[i] >= 0) |
bbd050af JO |
643 | gpio_free(spi_imx->chipselect[i]); |
644 | } | |
645 | dev_err(&pdev->dev, "can't get cs gpios\n"); | |
b5f3294f SH |
646 | goto out_master_put; |
647 | } | |
b5f3294f SH |
648 | } |
649 | ||
6cdeb002 UKK |
650 | spi_imx->bitbang.chipselect = spi_imx_chipselect; |
651 | spi_imx->bitbang.setup_transfer = spi_imx_setupxfer; | |
652 | spi_imx->bitbang.txrx_bufs = spi_imx_transfer; | |
653 | spi_imx->bitbang.master->setup = spi_imx_setup; | |
654 | spi_imx->bitbang.master->cleanup = spi_imx_cleanup; | |
3910f2cf | 655 | spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; |
b5f3294f | 656 | |
6cdeb002 | 657 | init_completion(&spi_imx->xfer_done); |
b5f3294f | 658 | |
f4ba6315 UKK |
659 | if (pdev->id_entry->driver_data == SPI_IMX_VER_AUTODETECT) { |
660 | if (cpu_is_mx25() || cpu_is_mx35()) | |
661 | spi_imx->devtype_data = | |
662 | spi_imx_devtype_data[SPI_IMX_VER_0_7]; | |
663 | else if (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx35()) | |
664 | spi_imx->devtype_data = | |
665 | spi_imx_devtype_data[SPI_IMX_VER_0_4]; | |
666 | else if (cpu_is_mx27() || cpu_is_mx21()) | |
667 | spi_imx->devtype_data = | |
668 | spi_imx_devtype_data[SPI_IMX_VER_0_0]; | |
669 | else if (cpu_is_mx1()) | |
670 | spi_imx->devtype_data = | |
671 | spi_imx_devtype_data[SPI_IMX_VER_IMX1]; | |
672 | else | |
673 | BUG(); | |
674 | } else | |
675 | spi_imx->devtype_data = | |
676 | spi_imx_devtype_data[pdev->id_entry->driver_data]; | |
677 | ||
678 | if (!spi_imx->devtype_data.intctrl) { | |
679 | dev_err(&pdev->dev, "no support for this device compiled in\n"); | |
680 | ret = -ENODEV; | |
681 | goto out_gpio_free; | |
682 | } | |
683 | ||
b5f3294f SH |
684 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
685 | if (!res) { | |
686 | dev_err(&pdev->dev, "can't get platform resource\n"); | |
687 | ret = -ENOMEM; | |
688 | goto out_gpio_free; | |
689 | } | |
690 | ||
691 | if (!request_mem_region(res->start, resource_size(res), pdev->name)) { | |
692 | dev_err(&pdev->dev, "request_mem_region failed\n"); | |
693 | ret = -EBUSY; | |
694 | goto out_gpio_free; | |
695 | } | |
696 | ||
6cdeb002 UKK |
697 | spi_imx->base = ioremap(res->start, resource_size(res)); |
698 | if (!spi_imx->base) { | |
b5f3294f SH |
699 | ret = -EINVAL; |
700 | goto out_release_mem; | |
701 | } | |
702 | ||
6cdeb002 | 703 | spi_imx->irq = platform_get_irq(pdev, 0); |
60f675a1 | 704 | if (spi_imx->irq <= 0) { |
b5f3294f SH |
705 | ret = -EINVAL; |
706 | goto out_iounmap; | |
707 | } | |
708 | ||
6cdeb002 | 709 | ret = request_irq(spi_imx->irq, spi_imx_isr, 0, DRIVER_NAME, spi_imx); |
b5f3294f | 710 | if (ret) { |
6cdeb002 | 711 | dev_err(&pdev->dev, "can't get irq%d: %d\n", spi_imx->irq, ret); |
b5f3294f SH |
712 | goto out_iounmap; |
713 | } | |
714 | ||
6cdeb002 UKK |
715 | spi_imx->clk = clk_get(&pdev->dev, NULL); |
716 | if (IS_ERR(spi_imx->clk)) { | |
b5f3294f | 717 | dev_err(&pdev->dev, "unable to get clock\n"); |
6cdeb002 | 718 | ret = PTR_ERR(spi_imx->clk); |
b5f3294f SH |
719 | goto out_free_irq; |
720 | } | |
721 | ||
6cdeb002 UKK |
722 | clk_enable(spi_imx->clk); |
723 | spi_imx->spi_clk = clk_get_rate(spi_imx->clk); | |
b5f3294f | 724 | |
1723e66b | 725 | spi_imx->devtype_data.reset(spi_imx); |
ce1807b2 | 726 | |
f4ba6315 | 727 | spi_imx->devtype_data.intctrl(spi_imx, 0); |
b5f3294f | 728 | |
6cdeb002 | 729 | ret = spi_bitbang_start(&spi_imx->bitbang); |
b5f3294f SH |
730 | if (ret) { |
731 | dev_err(&pdev->dev, "bitbang start failed with %d\n", ret); | |
732 | goto out_clk_put; | |
733 | } | |
734 | ||
735 | dev_info(&pdev->dev, "probed\n"); | |
736 | ||
737 | return ret; | |
738 | ||
739 | out_clk_put: | |
6cdeb002 UKK |
740 | clk_disable(spi_imx->clk); |
741 | clk_put(spi_imx->clk); | |
b5f3294f | 742 | out_free_irq: |
6cdeb002 | 743 | free_irq(spi_imx->irq, spi_imx); |
b5f3294f | 744 | out_iounmap: |
6cdeb002 | 745 | iounmap(spi_imx->base); |
b5f3294f SH |
746 | out_release_mem: |
747 | release_mem_region(res->start, resource_size(res)); | |
748 | out_gpio_free: | |
749 | for (i = 0; i < master->num_chipselect; i++) | |
6cdeb002 UKK |
750 | if (spi_imx->chipselect[i] >= 0) |
751 | gpio_free(spi_imx->chipselect[i]); | |
b5f3294f SH |
752 | out_master_put: |
753 | spi_master_put(master); | |
754 | kfree(master); | |
755 | platform_set_drvdata(pdev, NULL); | |
756 | return ret; | |
757 | } | |
758 | ||
965346e3 | 759 | static int __devexit spi_imx_remove(struct platform_device *pdev) |
b5f3294f SH |
760 | { |
761 | struct spi_master *master = platform_get_drvdata(pdev); | |
762 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
6cdeb002 | 763 | struct spi_imx_data *spi_imx = spi_master_get_devdata(master); |
b5f3294f SH |
764 | int i; |
765 | ||
6cdeb002 | 766 | spi_bitbang_stop(&spi_imx->bitbang); |
b5f3294f | 767 | |
6cdeb002 UKK |
768 | writel(0, spi_imx->base + MXC_CSPICTRL); |
769 | clk_disable(spi_imx->clk); | |
770 | clk_put(spi_imx->clk); | |
771 | free_irq(spi_imx->irq, spi_imx); | |
772 | iounmap(spi_imx->base); | |
b5f3294f SH |
773 | |
774 | for (i = 0; i < master->num_chipselect; i++) | |
6cdeb002 UKK |
775 | if (spi_imx->chipselect[i] >= 0) |
776 | gpio_free(spi_imx->chipselect[i]); | |
b5f3294f SH |
777 | |
778 | spi_master_put(master); | |
779 | ||
780 | release_mem_region(res->start, resource_size(res)); | |
781 | ||
782 | platform_set_drvdata(pdev, NULL); | |
783 | ||
784 | return 0; | |
785 | } | |
786 | ||
6cdeb002 | 787 | static struct platform_driver spi_imx_driver = { |
b5f3294f SH |
788 | .driver = { |
789 | .name = DRIVER_NAME, | |
790 | .owner = THIS_MODULE, | |
791 | }, | |
f4ba6315 | 792 | .id_table = spi_imx_devtype, |
6cdeb002 | 793 | .probe = spi_imx_probe, |
965346e3 | 794 | .remove = __devexit_p(spi_imx_remove), |
b5f3294f SH |
795 | }; |
796 | ||
6cdeb002 | 797 | static int __init spi_imx_init(void) |
b5f3294f | 798 | { |
6cdeb002 | 799 | return platform_driver_register(&spi_imx_driver); |
b5f3294f SH |
800 | } |
801 | ||
6cdeb002 | 802 | static void __exit spi_imx_exit(void) |
b5f3294f | 803 | { |
6cdeb002 | 804 | platform_driver_unregister(&spi_imx_driver); |
b5f3294f SH |
805 | } |
806 | ||
6cdeb002 UKK |
807 | module_init(spi_imx_init); |
808 | module_exit(spi_imx_exit); | |
b5f3294f SH |
809 | |
810 | MODULE_DESCRIPTION("SPI Master Controller driver"); | |
811 | MODULE_AUTHOR("Sascha Hauer, Pengutronix"); | |
812 | MODULE_LICENSE("GPL"); |