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; | |
3b2aa89e | 59 | u8 cs; |
b5f3294f SH |
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, | |
0b599603 | 68 | SPI_IMX_VER_2_3, |
f4ba6315 UKK |
69 | SPI_IMX_VER_AUTODETECT, |
70 | }; | |
71 | ||
72 | struct spi_imx_data; | |
73 | ||
74 | struct spi_imx_devtype_data { | |
75 | void (*intctrl)(struct spi_imx_data *, int); | |
76 | int (*config)(struct spi_imx_data *, struct spi_imx_config *); | |
77 | void (*trigger)(struct spi_imx_data *); | |
78 | int (*rx_available)(struct spi_imx_data *); | |
1723e66b | 79 | void (*reset)(struct spi_imx_data *); |
6ff554e0 | 80 | unsigned int fifosize; |
f4ba6315 UKK |
81 | }; |
82 | ||
6cdeb002 | 83 | struct spi_imx_data { |
b5f3294f SH |
84 | struct spi_bitbang bitbang; |
85 | ||
86 | struct completion xfer_done; | |
87 | void *base; | |
88 | int irq; | |
89 | struct clk *clk; | |
90 | unsigned long spi_clk; | |
91 | int *chipselect; | |
92 | ||
93 | unsigned int count; | |
6cdeb002 UKK |
94 | void (*tx)(struct spi_imx_data *); |
95 | void (*rx)(struct spi_imx_data *); | |
b5f3294f SH |
96 | void *rx_buf; |
97 | const void *tx_buf; | |
98 | unsigned int txfifo; /* number of words pushed in tx FIFO */ | |
99 | ||
f4ba6315 | 100 | struct spi_imx_devtype_data devtype_data; |
b5f3294f SH |
101 | }; |
102 | ||
103 | #define MXC_SPI_BUF_RX(type) \ | |
6cdeb002 | 104 | static void spi_imx_buf_rx_##type(struct spi_imx_data *spi_imx) \ |
b5f3294f | 105 | { \ |
6cdeb002 | 106 | unsigned int val = readl(spi_imx->base + MXC_CSPIRXDATA); \ |
b5f3294f | 107 | \ |
6cdeb002 UKK |
108 | if (spi_imx->rx_buf) { \ |
109 | *(type *)spi_imx->rx_buf = val; \ | |
110 | spi_imx->rx_buf += sizeof(type); \ | |
b5f3294f SH |
111 | } \ |
112 | } | |
113 | ||
114 | #define MXC_SPI_BUF_TX(type) \ | |
6cdeb002 | 115 | static void spi_imx_buf_tx_##type(struct spi_imx_data *spi_imx) \ |
b5f3294f SH |
116 | { \ |
117 | type val = 0; \ | |
118 | \ | |
6cdeb002 UKK |
119 | if (spi_imx->tx_buf) { \ |
120 | val = *(type *)spi_imx->tx_buf; \ | |
121 | spi_imx->tx_buf += sizeof(type); \ | |
b5f3294f SH |
122 | } \ |
123 | \ | |
6cdeb002 | 124 | spi_imx->count -= sizeof(type); \ |
b5f3294f | 125 | \ |
6cdeb002 | 126 | writel(val, spi_imx->base + MXC_CSPITXDATA); \ |
b5f3294f SH |
127 | } |
128 | ||
129 | MXC_SPI_BUF_RX(u8) | |
130 | MXC_SPI_BUF_TX(u8) | |
131 | MXC_SPI_BUF_RX(u16) | |
132 | MXC_SPI_BUF_TX(u16) | |
133 | MXC_SPI_BUF_RX(u32) | |
134 | MXC_SPI_BUF_TX(u32) | |
135 | ||
136 | /* First entry is reserved, second entry is valid only if SDHC_SPIEN is set | |
137 | * (which is currently not the case in this driver) | |
138 | */ | |
139 | static int mxc_clkdivs[] = {0, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, | |
140 | 256, 384, 512, 768, 1024}; | |
141 | ||
142 | /* MX21, MX27 */ | |
6cdeb002 | 143 | static unsigned int spi_imx_clkdiv_1(unsigned int fin, |
b5f3294f SH |
144 | unsigned int fspi) |
145 | { | |
146 | int i, max; | |
147 | ||
148 | if (cpu_is_mx21()) | |
149 | max = 18; | |
150 | else | |
151 | max = 16; | |
152 | ||
153 | for (i = 2; i < max; i++) | |
154 | if (fspi * mxc_clkdivs[i] >= fin) | |
155 | return i; | |
156 | ||
157 | return max; | |
158 | } | |
159 | ||
0b599603 | 160 | /* MX1, MX31, MX35, MX51 CSPI */ |
6cdeb002 | 161 | static unsigned int spi_imx_clkdiv_2(unsigned int fin, |
b5f3294f SH |
162 | unsigned int fspi) |
163 | { | |
164 | int i, div = 4; | |
165 | ||
166 | for (i = 0; i < 7; i++) { | |
167 | if (fspi * div >= fin) | |
168 | return i; | |
169 | div <<= 1; | |
170 | } | |
171 | ||
172 | return 7; | |
173 | } | |
174 | ||
0b599603 UKK |
175 | #define SPI_IMX2_3_CTRL 0x08 |
176 | #define SPI_IMX2_3_CTRL_ENABLE (1 << 0) | |
177 | #define SPI_IMX2_3_CTRL_XCH (1 << 2) | |
178 | #define SPI_IMX2_3_CTRL_MODE(cs) (1 << ((cs) + 4)) | |
179 | #define SPI_IMX2_3_CTRL_POSTDIV_OFFSET 8 | |
180 | #define SPI_IMX2_3_CTRL_PREDIV_OFFSET 12 | |
181 | #define SPI_IMX2_3_CTRL_CS(cs) ((cs) << 18) | |
182 | #define SPI_IMX2_3_CTRL_BL_OFFSET 20 | |
183 | ||
184 | #define SPI_IMX2_3_CONFIG 0x0c | |
185 | #define SPI_IMX2_3_CONFIG_SCLKPHA(cs) (1 << ((cs) + 0)) | |
186 | #define SPI_IMX2_3_CONFIG_SCLKPOL(cs) (1 << ((cs) + 4)) | |
187 | #define SPI_IMX2_3_CONFIG_SBBCTRL(cs) (1 << ((cs) + 8)) | |
188 | #define SPI_IMX2_3_CONFIG_SSBPOL(cs) (1 << ((cs) + 12)) | |
189 | ||
190 | #define SPI_IMX2_3_INT 0x10 | |
191 | #define SPI_IMX2_3_INT_TEEN (1 << 0) | |
192 | #define SPI_IMX2_3_INT_RREN (1 << 3) | |
193 | ||
194 | #define SPI_IMX2_3_STAT 0x18 | |
195 | #define SPI_IMX2_3_STAT_RR (1 << 3) | |
196 | ||
197 | /* MX51 eCSPI */ | |
198 | static unsigned int spi_imx2_3_clkdiv(unsigned int fin, unsigned int fspi) | |
199 | { | |
200 | /* | |
201 | * there are two 4-bit dividers, the pre-divider divides by | |
202 | * $pre, the post-divider by 2^$post | |
203 | */ | |
204 | unsigned int pre, post; | |
205 | ||
206 | if (unlikely(fspi > fin)) | |
207 | return 0; | |
208 | ||
209 | post = fls(fin) - fls(fspi); | |
210 | if (fin > fspi << post) | |
211 | post++; | |
212 | ||
213 | /* now we have: (fin <= fspi << post) with post being minimal */ | |
214 | ||
215 | post = max(4U, post) - 4; | |
216 | if (unlikely(post > 0xf)) { | |
217 | pr_err("%s: cannot set clock freq: %u (base freq: %u)\n", | |
218 | __func__, fspi, fin); | |
219 | return 0xff; | |
220 | } | |
221 | ||
222 | pre = DIV_ROUND_UP(fin, fspi << post) - 1; | |
223 | ||
224 | pr_debug("%s: fin: %u, fspi: %u, post: %u, pre: %u\n", | |
225 | __func__, fin, fspi, post, pre); | |
226 | return (pre << SPI_IMX2_3_CTRL_PREDIV_OFFSET) | | |
227 | (post << SPI_IMX2_3_CTRL_POSTDIV_OFFSET); | |
228 | } | |
229 | ||
230 | static void __maybe_unused spi_imx2_3_intctrl(struct spi_imx_data *spi_imx, int enable) | |
231 | { | |
232 | unsigned val = 0; | |
233 | ||
234 | if (enable & MXC_INT_TE) | |
235 | val |= SPI_IMX2_3_INT_TEEN; | |
236 | ||
237 | if (enable & MXC_INT_RR) | |
238 | val |= SPI_IMX2_3_INT_RREN; | |
239 | ||
240 | writel(val, spi_imx->base + SPI_IMX2_3_INT); | |
241 | } | |
242 | ||
243 | static void __maybe_unused spi_imx2_3_trigger(struct spi_imx_data *spi_imx) | |
244 | { | |
245 | u32 reg; | |
246 | ||
247 | reg = readl(spi_imx->base + SPI_IMX2_3_CTRL); | |
248 | reg |= SPI_IMX2_3_CTRL_XCH; | |
249 | writel(reg, spi_imx->base + SPI_IMX2_3_CTRL); | |
250 | } | |
251 | ||
252 | static int __maybe_unused spi_imx2_3_config(struct spi_imx_data *spi_imx, | |
253 | struct spi_imx_config *config) | |
254 | { | |
255 | u32 ctrl = SPI_IMX2_3_CTRL_ENABLE, cfg = 0; | |
256 | ||
257 | /* set master mode */ | |
258 | ctrl |= SPI_IMX2_3_CTRL_MODE(config->cs); | |
259 | ||
260 | /* set clock speed */ | |
261 | ctrl |= spi_imx2_3_clkdiv(spi_imx->spi_clk, config->speed_hz); | |
262 | ||
263 | /* set chip select to use */ | |
264 | ctrl |= SPI_IMX2_3_CTRL_CS(config->cs); | |
265 | ||
266 | ctrl |= (config->bpw - 1) << SPI_IMX2_3_CTRL_BL_OFFSET; | |
267 | ||
268 | cfg |= SPI_IMX2_3_CONFIG_SBBCTRL(config->cs); | |
269 | ||
270 | if (config->mode & SPI_CPHA) | |
271 | cfg |= SPI_IMX2_3_CONFIG_SCLKPHA(config->cs); | |
272 | ||
273 | if (config->mode & SPI_CPOL) | |
274 | cfg |= SPI_IMX2_3_CONFIG_SCLKPOL(config->cs); | |
275 | ||
276 | if (config->mode & SPI_CS_HIGH) | |
277 | cfg |= SPI_IMX2_3_CONFIG_SSBPOL(config->cs); | |
278 | ||
279 | writel(ctrl, spi_imx->base + SPI_IMX2_3_CTRL); | |
280 | writel(cfg, spi_imx->base + SPI_IMX2_3_CONFIG); | |
281 | ||
282 | return 0; | |
283 | } | |
284 | ||
285 | static int __maybe_unused spi_imx2_3_rx_available(struct spi_imx_data *spi_imx) | |
286 | { | |
287 | return readl(spi_imx->base + SPI_IMX2_3_STAT) & SPI_IMX2_3_STAT_RR; | |
288 | } | |
289 | ||
290 | static void __maybe_unused spi_imx2_3_reset(struct spi_imx_data *spi_imx) | |
291 | { | |
292 | /* drain receive buffer */ | |
293 | while (spi_imx2_3_rx_available(spi_imx)) | |
294 | readl(spi_imx->base + MXC_CSPIRXDATA); | |
295 | } | |
296 | ||
b5f3294f SH |
297 | #define MX31_INTREG_TEEN (1 << 0) |
298 | #define MX31_INTREG_RREN (1 << 3) | |
299 | ||
300 | #define MX31_CSPICTRL_ENABLE (1 << 0) | |
301 | #define MX31_CSPICTRL_MASTER (1 << 1) | |
302 | #define MX31_CSPICTRL_XCH (1 << 2) | |
303 | #define MX31_CSPICTRL_POL (1 << 4) | |
304 | #define MX31_CSPICTRL_PHA (1 << 5) | |
305 | #define MX31_CSPICTRL_SSCTL (1 << 6) | |
306 | #define MX31_CSPICTRL_SSPOL (1 << 7) | |
307 | #define MX31_CSPICTRL_BC_SHIFT 8 | |
308 | #define MX35_CSPICTRL_BL_SHIFT 20 | |
309 | #define MX31_CSPICTRL_CS_SHIFT 24 | |
310 | #define MX35_CSPICTRL_CS_SHIFT 12 | |
311 | #define MX31_CSPICTRL_DR_SHIFT 16 | |
312 | ||
313 | #define MX31_CSPISTATUS 0x14 | |
314 | #define MX31_STATUS_RR (1 << 3) | |
315 | ||
316 | /* These functions also work for the i.MX35, but be aware that | |
317 | * the i.MX35 has a slightly different register layout for bits | |
318 | * we do not use here. | |
319 | */ | |
f4ba6315 | 320 | static void __maybe_unused mx31_intctrl(struct spi_imx_data *spi_imx, int enable) |
b5f3294f SH |
321 | { |
322 | unsigned int val = 0; | |
323 | ||
324 | if (enable & MXC_INT_TE) | |
325 | val |= MX31_INTREG_TEEN; | |
326 | if (enable & MXC_INT_RR) | |
327 | val |= MX31_INTREG_RREN; | |
328 | ||
6cdeb002 | 329 | writel(val, spi_imx->base + MXC_CSPIINT); |
b5f3294f SH |
330 | } |
331 | ||
f4ba6315 | 332 | static void __maybe_unused mx31_trigger(struct spi_imx_data *spi_imx) |
b5f3294f SH |
333 | { |
334 | unsigned int reg; | |
335 | ||
6cdeb002 | 336 | reg = readl(spi_imx->base + MXC_CSPICTRL); |
b5f3294f | 337 | reg |= MX31_CSPICTRL_XCH; |
6cdeb002 | 338 | writel(reg, spi_imx->base + MXC_CSPICTRL); |
b5f3294f SH |
339 | } |
340 | ||
1723e66b | 341 | static int __maybe_unused spi_imx0_4_config(struct spi_imx_data *spi_imx, |
6cdeb002 | 342 | struct spi_imx_config *config) |
b5f3294f SH |
343 | { |
344 | unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; | |
3b2aa89e | 345 | int cs = spi_imx->chipselect[config->cs]; |
b5f3294f | 346 | |
6cdeb002 | 347 | reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << |
b5f3294f SH |
348 | MX31_CSPICTRL_DR_SHIFT; |
349 | ||
1723e66b | 350 | reg |= (config->bpw - 1) << MX31_CSPICTRL_BC_SHIFT; |
b5f3294f SH |
351 | |
352 | if (config->mode & SPI_CPHA) | |
353 | reg |= MX31_CSPICTRL_PHA; | |
354 | if (config->mode & SPI_CPOL) | |
355 | reg |= MX31_CSPICTRL_POL; | |
356 | if (config->mode & SPI_CS_HIGH) | |
357 | reg |= MX31_CSPICTRL_SSPOL; | |
3b2aa89e UKK |
358 | if (cs < 0) |
359 | reg |= (cs + 32) << MX31_CSPICTRL_CS_SHIFT; | |
b5f3294f | 360 | |
6cdeb002 | 361 | writel(reg, spi_imx->base + MXC_CSPICTRL); |
b5f3294f SH |
362 | |
363 | return 0; | |
364 | } | |
365 | ||
1723e66b UKK |
366 | static int __maybe_unused spi_imx0_7_config(struct spi_imx_data *spi_imx, |
367 | struct spi_imx_config *config) | |
368 | { | |
369 | unsigned int reg = MX31_CSPICTRL_ENABLE | MX31_CSPICTRL_MASTER; | |
3b2aa89e | 370 | int cs = spi_imx->chipselect[config->cs]; |
1723e66b UKK |
371 | |
372 | reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << | |
373 | MX31_CSPICTRL_DR_SHIFT; | |
374 | ||
375 | reg |= (config->bpw - 1) << MX35_CSPICTRL_BL_SHIFT; | |
376 | reg |= MX31_CSPICTRL_SSCTL; | |
377 | ||
378 | if (config->mode & SPI_CPHA) | |
379 | reg |= MX31_CSPICTRL_PHA; | |
380 | if (config->mode & SPI_CPOL) | |
381 | reg |= MX31_CSPICTRL_POL; | |
382 | if (config->mode & SPI_CS_HIGH) | |
383 | reg |= MX31_CSPICTRL_SSPOL; | |
3b2aa89e UKK |
384 | if (cs < 0) |
385 | reg |= (cs + 32) << MX35_CSPICTRL_CS_SHIFT; | |
1723e66b UKK |
386 | |
387 | writel(reg, spi_imx->base + MXC_CSPICTRL); | |
388 | ||
389 | return 0; | |
390 | } | |
391 | ||
f4ba6315 | 392 | static int __maybe_unused mx31_rx_available(struct spi_imx_data *spi_imx) |
b5f3294f | 393 | { |
6cdeb002 | 394 | return readl(spi_imx->base + MX31_CSPISTATUS) & MX31_STATUS_RR; |
b5f3294f SH |
395 | } |
396 | ||
1723e66b UKK |
397 | static void __maybe_unused spi_imx0_4_reset(struct spi_imx_data *spi_imx) |
398 | { | |
399 | /* drain receive buffer */ | |
400 | while (readl(spi_imx->base + MX3_CSPISTAT) & MX3_CSPISTAT_RR) | |
401 | readl(spi_imx->base + MXC_CSPIRXDATA); | |
402 | } | |
403 | ||
b5f3294f SH |
404 | #define MX27_INTREG_RR (1 << 4) |
405 | #define MX27_INTREG_TEEN (1 << 9) | |
406 | #define MX27_INTREG_RREN (1 << 13) | |
407 | ||
408 | #define MX27_CSPICTRL_POL (1 << 5) | |
409 | #define MX27_CSPICTRL_PHA (1 << 6) | |
410 | #define MX27_CSPICTRL_SSPOL (1 << 8) | |
411 | #define MX27_CSPICTRL_XCH (1 << 9) | |
412 | #define MX27_CSPICTRL_ENABLE (1 << 10) | |
413 | #define MX27_CSPICTRL_MASTER (1 << 11) | |
414 | #define MX27_CSPICTRL_DR_SHIFT 14 | |
415 | #define MX27_CSPICTRL_CS_SHIFT 19 | |
416 | ||
f4ba6315 | 417 | static void __maybe_unused mx27_intctrl(struct spi_imx_data *spi_imx, int enable) |
b5f3294f SH |
418 | { |
419 | unsigned int val = 0; | |
420 | ||
421 | if (enable & MXC_INT_TE) | |
422 | val |= MX27_INTREG_TEEN; | |
423 | if (enable & MXC_INT_RR) | |
424 | val |= MX27_INTREG_RREN; | |
425 | ||
6cdeb002 | 426 | writel(val, spi_imx->base + MXC_CSPIINT); |
b5f3294f SH |
427 | } |
428 | ||
f4ba6315 | 429 | static void __maybe_unused mx27_trigger(struct spi_imx_data *spi_imx) |
b5f3294f SH |
430 | { |
431 | unsigned int reg; | |
432 | ||
6cdeb002 | 433 | reg = readl(spi_imx->base + MXC_CSPICTRL); |
b5f3294f | 434 | reg |= MX27_CSPICTRL_XCH; |
6cdeb002 | 435 | writel(reg, spi_imx->base + MXC_CSPICTRL); |
b5f3294f SH |
436 | } |
437 | ||
f4ba6315 | 438 | static int __maybe_unused mx27_config(struct spi_imx_data *spi_imx, |
6cdeb002 | 439 | struct spi_imx_config *config) |
b5f3294f SH |
440 | { |
441 | unsigned int reg = MX27_CSPICTRL_ENABLE | MX27_CSPICTRL_MASTER; | |
3b2aa89e | 442 | int cs = spi_imx->chipselect[config->cs]; |
b5f3294f | 443 | |
6cdeb002 | 444 | reg |= spi_imx_clkdiv_1(spi_imx->spi_clk, config->speed_hz) << |
b5f3294f SH |
445 | MX27_CSPICTRL_DR_SHIFT; |
446 | reg |= config->bpw - 1; | |
447 | ||
448 | if (config->mode & SPI_CPHA) | |
449 | reg |= MX27_CSPICTRL_PHA; | |
450 | if (config->mode & SPI_CPOL) | |
451 | reg |= MX27_CSPICTRL_POL; | |
452 | if (config->mode & SPI_CS_HIGH) | |
453 | reg |= MX27_CSPICTRL_SSPOL; | |
3b2aa89e UKK |
454 | if (cs < 0) |
455 | reg |= (cs + 32) << MX27_CSPICTRL_CS_SHIFT; | |
b5f3294f | 456 | |
6cdeb002 | 457 | writel(reg, spi_imx->base + MXC_CSPICTRL); |
b5f3294f SH |
458 | |
459 | return 0; | |
460 | } | |
461 | ||
f4ba6315 | 462 | static int __maybe_unused mx27_rx_available(struct spi_imx_data *spi_imx) |
b5f3294f | 463 | { |
6cdeb002 | 464 | return readl(spi_imx->base + MXC_CSPIINT) & MX27_INTREG_RR; |
b5f3294f SH |
465 | } |
466 | ||
1723e66b UKK |
467 | static void __maybe_unused spi_imx0_0_reset(struct spi_imx_data *spi_imx) |
468 | { | |
469 | writel(1, spi_imx->base + MXC_RESET); | |
470 | } | |
471 | ||
b5f3294f SH |
472 | #define MX1_INTREG_RR (1 << 3) |
473 | #define MX1_INTREG_TEEN (1 << 8) | |
474 | #define MX1_INTREG_RREN (1 << 11) | |
475 | ||
476 | #define MX1_CSPICTRL_POL (1 << 4) | |
477 | #define MX1_CSPICTRL_PHA (1 << 5) | |
478 | #define MX1_CSPICTRL_XCH (1 << 8) | |
479 | #define MX1_CSPICTRL_ENABLE (1 << 9) | |
480 | #define MX1_CSPICTRL_MASTER (1 << 10) | |
481 | #define MX1_CSPICTRL_DR_SHIFT 13 | |
482 | ||
f4ba6315 | 483 | static void __maybe_unused mx1_intctrl(struct spi_imx_data *spi_imx, int enable) |
b5f3294f SH |
484 | { |
485 | unsigned int val = 0; | |
486 | ||
487 | if (enable & MXC_INT_TE) | |
488 | val |= MX1_INTREG_TEEN; | |
489 | if (enable & MXC_INT_RR) | |
490 | val |= MX1_INTREG_RREN; | |
491 | ||
6cdeb002 | 492 | writel(val, spi_imx->base + MXC_CSPIINT); |
b5f3294f SH |
493 | } |
494 | ||
f4ba6315 | 495 | static void __maybe_unused mx1_trigger(struct spi_imx_data *spi_imx) |
b5f3294f SH |
496 | { |
497 | unsigned int reg; | |
498 | ||
6cdeb002 | 499 | reg = readl(spi_imx->base + MXC_CSPICTRL); |
b5f3294f | 500 | reg |= MX1_CSPICTRL_XCH; |
6cdeb002 | 501 | writel(reg, spi_imx->base + MXC_CSPICTRL); |
b5f3294f SH |
502 | } |
503 | ||
f4ba6315 | 504 | static int __maybe_unused mx1_config(struct spi_imx_data *spi_imx, |
6cdeb002 | 505 | struct spi_imx_config *config) |
b5f3294f SH |
506 | { |
507 | unsigned int reg = MX1_CSPICTRL_ENABLE | MX1_CSPICTRL_MASTER; | |
508 | ||
6cdeb002 | 509 | reg |= spi_imx_clkdiv_2(spi_imx->spi_clk, config->speed_hz) << |
b5f3294f SH |
510 | MX1_CSPICTRL_DR_SHIFT; |
511 | reg |= config->bpw - 1; | |
512 | ||
513 | if (config->mode & SPI_CPHA) | |
514 | reg |= MX1_CSPICTRL_PHA; | |
515 | if (config->mode & SPI_CPOL) | |
516 | reg |= MX1_CSPICTRL_POL; | |
517 | ||
6cdeb002 | 518 | writel(reg, spi_imx->base + MXC_CSPICTRL); |
b5f3294f SH |
519 | |
520 | return 0; | |
521 | } | |
522 | ||
f4ba6315 | 523 | static int __maybe_unused mx1_rx_available(struct spi_imx_data *spi_imx) |
b5f3294f | 524 | { |
6cdeb002 | 525 | return readl(spi_imx->base + MXC_CSPIINT) & MX1_INTREG_RR; |
b5f3294f SH |
526 | } |
527 | ||
1723e66b UKK |
528 | static void __maybe_unused mx1_reset(struct spi_imx_data *spi_imx) |
529 | { | |
530 | writel(1, spi_imx->base + MXC_RESET); | |
531 | } | |
532 | ||
f4ba6315 UKK |
533 | /* |
534 | * These version numbers are taken from the Freescale driver. Unfortunately it | |
535 | * doesn't support i.MX1, so this entry doesn't match the scheme. :-( | |
536 | */ | |
537 | static struct spi_imx_devtype_data spi_imx_devtype_data[] __devinitdata = { | |
538 | #ifdef CONFIG_SPI_IMX_VER_IMX1 | |
539 | [SPI_IMX_VER_IMX1] = { | |
540 | .intctrl = mx1_intctrl, | |
541 | .config = mx1_config, | |
542 | .trigger = mx1_trigger, | |
543 | .rx_available = mx1_rx_available, | |
1723e66b | 544 | .reset = mx1_reset, |
6ff554e0 | 545 | .fifosize = 8, |
f4ba6315 UKK |
546 | }, |
547 | #endif | |
548 | #ifdef CONFIG_SPI_IMX_VER_0_0 | |
549 | [SPI_IMX_VER_0_0] = { | |
550 | .intctrl = mx27_intctrl, | |
551 | .config = mx27_config, | |
552 | .trigger = mx27_trigger, | |
553 | .rx_available = mx27_rx_available, | |
1723e66b | 554 | .reset = spi_imx0_0_reset, |
6ff554e0 | 555 | .fifosize = 8, |
f4ba6315 UKK |
556 | }, |
557 | #endif | |
558 | #ifdef CONFIG_SPI_IMX_VER_0_4 | |
559 | [SPI_IMX_VER_0_4] = { | |
560 | .intctrl = mx31_intctrl, | |
1723e66b | 561 | .config = spi_imx0_4_config, |
f4ba6315 UKK |
562 | .trigger = mx31_trigger, |
563 | .rx_available = mx31_rx_available, | |
1723e66b | 564 | .reset = spi_imx0_4_reset, |
6ff554e0 | 565 | .fifosize = 8, |
f4ba6315 UKK |
566 | }, |
567 | #endif | |
568 | #ifdef CONFIG_SPI_IMX_VER_0_7 | |
569 | [SPI_IMX_VER_0_7] = { | |
570 | .intctrl = mx31_intctrl, | |
1723e66b | 571 | .config = spi_imx0_7_config, |
f4ba6315 UKK |
572 | .trigger = mx31_trigger, |
573 | .rx_available = mx31_rx_available, | |
1723e66b | 574 | .reset = spi_imx0_4_reset, |
6ff554e0 | 575 | .fifosize = 8, |
f4ba6315 UKK |
576 | }, |
577 | #endif | |
0b599603 UKK |
578 | #ifdef CONFIG_SPI_IMX_VER_2_3 |
579 | [SPI_IMX_VER_2_3] = { | |
580 | .intctrl = spi_imx2_3_intctrl, | |
581 | .config = spi_imx2_3_config, | |
582 | .trigger = spi_imx2_3_trigger, | |
583 | .rx_available = spi_imx2_3_rx_available, | |
584 | .reset = spi_imx2_3_reset, | |
6ff554e0 | 585 | .fifosize = 64, |
0b599603 UKK |
586 | }, |
587 | #endif | |
f4ba6315 UKK |
588 | }; |
589 | ||
6cdeb002 | 590 | static void spi_imx_chipselect(struct spi_device *spi, int is_active) |
b5f3294f | 591 | { |
6cdeb002 | 592 | struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); |
6cdeb002 | 593 | int gpio = spi_imx->chipselect[spi->chip_select]; |
e6a0a8bf UKK |
594 | int active = is_active != BITBANG_CS_INACTIVE; |
595 | int dev_is_lowactive = !(spi->mode & SPI_CS_HIGH); | |
b5f3294f | 596 | |
e6a0a8bf | 597 | if (gpio < 0) |
b5f3294f | 598 | return; |
b5f3294f | 599 | |
e6a0a8bf | 600 | gpio_set_value(gpio, dev_is_lowactive ^ active); |
b5f3294f SH |
601 | } |
602 | ||
6cdeb002 | 603 | static void spi_imx_push(struct spi_imx_data *spi_imx) |
b5f3294f | 604 | { |
6ff554e0 | 605 | while (spi_imx->txfifo < spi_imx->devtype_data.fifosize) { |
6cdeb002 | 606 | if (!spi_imx->count) |
b5f3294f | 607 | break; |
6cdeb002 UKK |
608 | spi_imx->tx(spi_imx); |
609 | spi_imx->txfifo++; | |
b5f3294f SH |
610 | } |
611 | ||
f4ba6315 | 612 | spi_imx->devtype_data.trigger(spi_imx); |
b5f3294f SH |
613 | } |
614 | ||
6cdeb002 | 615 | static irqreturn_t spi_imx_isr(int irq, void *dev_id) |
b5f3294f | 616 | { |
6cdeb002 | 617 | struct spi_imx_data *spi_imx = dev_id; |
b5f3294f | 618 | |
f4ba6315 | 619 | while (spi_imx->devtype_data.rx_available(spi_imx)) { |
6cdeb002 UKK |
620 | spi_imx->rx(spi_imx); |
621 | spi_imx->txfifo--; | |
b5f3294f SH |
622 | } |
623 | ||
6cdeb002 UKK |
624 | if (spi_imx->count) { |
625 | spi_imx_push(spi_imx); | |
b5f3294f SH |
626 | return IRQ_HANDLED; |
627 | } | |
628 | ||
6cdeb002 | 629 | if (spi_imx->txfifo) { |
b5f3294f SH |
630 | /* No data left to push, but still waiting for rx data, |
631 | * enable receive data available interrupt. | |
632 | */ | |
f4ba6315 UKK |
633 | spi_imx->devtype_data.intctrl( |
634 | spi_imx, MXC_INT_RR); | |
b5f3294f SH |
635 | return IRQ_HANDLED; |
636 | } | |
637 | ||
f4ba6315 | 638 | spi_imx->devtype_data.intctrl(spi_imx, 0); |
6cdeb002 | 639 | complete(&spi_imx->xfer_done); |
b5f3294f SH |
640 | |
641 | return IRQ_HANDLED; | |
642 | } | |
643 | ||
6cdeb002 | 644 | static int spi_imx_setupxfer(struct spi_device *spi, |
b5f3294f SH |
645 | struct spi_transfer *t) |
646 | { | |
6cdeb002 UKK |
647 | struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); |
648 | struct spi_imx_config config; | |
b5f3294f SH |
649 | |
650 | config.bpw = t ? t->bits_per_word : spi->bits_per_word; | |
651 | config.speed_hz = t ? t->speed_hz : spi->max_speed_hz; | |
652 | config.mode = spi->mode; | |
3b2aa89e | 653 | config.cs = spi->chip_select; |
b5f3294f | 654 | |
462d26b5 SH |
655 | if (!config.speed_hz) |
656 | config.speed_hz = spi->max_speed_hz; | |
657 | if (!config.bpw) | |
658 | config.bpw = spi->bits_per_word; | |
659 | if (!config.speed_hz) | |
660 | config.speed_hz = spi->max_speed_hz; | |
661 | ||
e6a0a8bf UKK |
662 | /* Initialize the functions for transfer */ |
663 | if (config.bpw <= 8) { | |
664 | spi_imx->rx = spi_imx_buf_rx_u8; | |
665 | spi_imx->tx = spi_imx_buf_tx_u8; | |
666 | } else if (config.bpw <= 16) { | |
667 | spi_imx->rx = spi_imx_buf_rx_u16; | |
668 | spi_imx->tx = spi_imx_buf_tx_u16; | |
669 | } else if (config.bpw <= 32) { | |
670 | spi_imx->rx = spi_imx_buf_rx_u32; | |
671 | spi_imx->tx = spi_imx_buf_tx_u32; | |
672 | } else | |
673 | BUG(); | |
674 | ||
f4ba6315 | 675 | spi_imx->devtype_data.config(spi_imx, &config); |
b5f3294f SH |
676 | |
677 | return 0; | |
678 | } | |
679 | ||
6cdeb002 | 680 | static int spi_imx_transfer(struct spi_device *spi, |
b5f3294f SH |
681 | struct spi_transfer *transfer) |
682 | { | |
6cdeb002 | 683 | struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); |
b5f3294f | 684 | |
6cdeb002 UKK |
685 | spi_imx->tx_buf = transfer->tx_buf; |
686 | spi_imx->rx_buf = transfer->rx_buf; | |
687 | spi_imx->count = transfer->len; | |
688 | spi_imx->txfifo = 0; | |
b5f3294f | 689 | |
6cdeb002 | 690 | init_completion(&spi_imx->xfer_done); |
b5f3294f | 691 | |
6cdeb002 | 692 | spi_imx_push(spi_imx); |
b5f3294f | 693 | |
f4ba6315 | 694 | spi_imx->devtype_data.intctrl(spi_imx, MXC_INT_TE); |
b5f3294f | 695 | |
6cdeb002 | 696 | wait_for_completion(&spi_imx->xfer_done); |
b5f3294f SH |
697 | |
698 | return transfer->len; | |
699 | } | |
700 | ||
6cdeb002 | 701 | static int spi_imx_setup(struct spi_device *spi) |
b5f3294f | 702 | { |
6c23e5d4 SH |
703 | struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); |
704 | int gpio = spi_imx->chipselect[spi->chip_select]; | |
705 | ||
f4d4ecfe | 706 | dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n", __func__, |
b5f3294f SH |
707 | spi->mode, spi->bits_per_word, spi->max_speed_hz); |
708 | ||
6c23e5d4 SH |
709 | if (gpio >= 0) |
710 | gpio_direction_output(gpio, spi->mode & SPI_CS_HIGH ? 0 : 1); | |
711 | ||
6cdeb002 | 712 | spi_imx_chipselect(spi, BITBANG_CS_INACTIVE); |
b5f3294f SH |
713 | |
714 | return 0; | |
715 | } | |
716 | ||
6cdeb002 | 717 | static void spi_imx_cleanup(struct spi_device *spi) |
b5f3294f SH |
718 | { |
719 | } | |
720 | ||
f4ba6315 UKK |
721 | static struct platform_device_id spi_imx_devtype[] = { |
722 | { | |
723 | .name = DRIVER_NAME, | |
724 | .driver_data = SPI_IMX_VER_AUTODETECT, | |
725 | }, { | |
726 | .name = "imx1-cspi", | |
727 | .driver_data = SPI_IMX_VER_IMX1, | |
728 | }, { | |
729 | .name = "imx21-cspi", | |
730 | .driver_data = SPI_IMX_VER_0_0, | |
731 | }, { | |
732 | .name = "imx25-cspi", | |
733 | .driver_data = SPI_IMX_VER_0_7, | |
734 | }, { | |
735 | .name = "imx27-cspi", | |
736 | .driver_data = SPI_IMX_VER_0_0, | |
737 | }, { | |
738 | .name = "imx31-cspi", | |
739 | .driver_data = SPI_IMX_VER_0_4, | |
740 | }, { | |
741 | .name = "imx35-cspi", | |
742 | .driver_data = SPI_IMX_VER_0_7, | |
0b599603 UKK |
743 | }, { |
744 | .name = "imx51-cspi", | |
745 | .driver_data = SPI_IMX_VER_0_7, | |
746 | }, { | |
747 | .name = "imx51-ecspi", | |
748 | .driver_data = SPI_IMX_VER_2_3, | |
f4ba6315 UKK |
749 | }, { |
750 | /* sentinel */ | |
751 | } | |
752 | }; | |
753 | ||
965346e3 | 754 | static int __devinit spi_imx_probe(struct platform_device *pdev) |
b5f3294f SH |
755 | { |
756 | struct spi_imx_master *mxc_platform_info; | |
757 | struct spi_master *master; | |
6cdeb002 | 758 | struct spi_imx_data *spi_imx; |
b5f3294f SH |
759 | struct resource *res; |
760 | int i, ret; | |
761 | ||
980f3bee | 762 | mxc_platform_info = dev_get_platdata(&pdev->dev); |
b5f3294f SH |
763 | if (!mxc_platform_info) { |
764 | dev_err(&pdev->dev, "can't get the platform data\n"); | |
765 | return -EINVAL; | |
766 | } | |
767 | ||
6cdeb002 | 768 | master = spi_alloc_master(&pdev->dev, sizeof(struct spi_imx_data)); |
b5f3294f SH |
769 | if (!master) |
770 | return -ENOMEM; | |
771 | ||
772 | platform_set_drvdata(pdev, master); | |
773 | ||
774 | master->bus_num = pdev->id; | |
775 | master->num_chipselect = mxc_platform_info->num_chipselect; | |
776 | ||
6cdeb002 UKK |
777 | spi_imx = spi_master_get_devdata(master); |
778 | spi_imx->bitbang.master = spi_master_get(master); | |
779 | spi_imx->chipselect = mxc_platform_info->chipselect; | |
b5f3294f SH |
780 | |
781 | for (i = 0; i < master->num_chipselect; i++) { | |
6cdeb002 | 782 | if (spi_imx->chipselect[i] < 0) |
b5f3294f | 783 | continue; |
6cdeb002 | 784 | ret = gpio_request(spi_imx->chipselect[i], DRIVER_NAME); |
b5f3294f | 785 | if (ret) { |
bbd050af JO |
786 | while (i > 0) { |
787 | i--; | |
6cdeb002 | 788 | if (spi_imx->chipselect[i] >= 0) |
bbd050af JO |
789 | gpio_free(spi_imx->chipselect[i]); |
790 | } | |
791 | dev_err(&pdev->dev, "can't get cs gpios\n"); | |
b5f3294f SH |
792 | goto out_master_put; |
793 | } | |
b5f3294f SH |
794 | } |
795 | ||
6cdeb002 UKK |
796 | spi_imx->bitbang.chipselect = spi_imx_chipselect; |
797 | spi_imx->bitbang.setup_transfer = spi_imx_setupxfer; | |
798 | spi_imx->bitbang.txrx_bufs = spi_imx_transfer; | |
799 | spi_imx->bitbang.master->setup = spi_imx_setup; | |
800 | spi_imx->bitbang.master->cleanup = spi_imx_cleanup; | |
3910f2cf | 801 | spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; |
b5f3294f | 802 | |
6cdeb002 | 803 | init_completion(&spi_imx->xfer_done); |
b5f3294f | 804 | |
f4ba6315 UKK |
805 | if (pdev->id_entry->driver_data == SPI_IMX_VER_AUTODETECT) { |
806 | if (cpu_is_mx25() || cpu_is_mx35()) | |
807 | spi_imx->devtype_data = | |
808 | spi_imx_devtype_data[SPI_IMX_VER_0_7]; | |
809 | else if (cpu_is_mx25() || cpu_is_mx31() || cpu_is_mx35()) | |
810 | spi_imx->devtype_data = | |
811 | spi_imx_devtype_data[SPI_IMX_VER_0_4]; | |
812 | else if (cpu_is_mx27() || cpu_is_mx21()) | |
813 | spi_imx->devtype_data = | |
814 | spi_imx_devtype_data[SPI_IMX_VER_0_0]; | |
815 | else if (cpu_is_mx1()) | |
816 | spi_imx->devtype_data = | |
817 | spi_imx_devtype_data[SPI_IMX_VER_IMX1]; | |
818 | else | |
819 | BUG(); | |
820 | } else | |
821 | spi_imx->devtype_data = | |
822 | spi_imx_devtype_data[pdev->id_entry->driver_data]; | |
823 | ||
824 | if (!spi_imx->devtype_data.intctrl) { | |
825 | dev_err(&pdev->dev, "no support for this device compiled in\n"); | |
826 | ret = -ENODEV; | |
827 | goto out_gpio_free; | |
828 | } | |
829 | ||
b5f3294f SH |
830 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
831 | if (!res) { | |
832 | dev_err(&pdev->dev, "can't get platform resource\n"); | |
833 | ret = -ENOMEM; | |
834 | goto out_gpio_free; | |
835 | } | |
836 | ||
837 | if (!request_mem_region(res->start, resource_size(res), pdev->name)) { | |
838 | dev_err(&pdev->dev, "request_mem_region failed\n"); | |
839 | ret = -EBUSY; | |
840 | goto out_gpio_free; | |
841 | } | |
842 | ||
6cdeb002 UKK |
843 | spi_imx->base = ioremap(res->start, resource_size(res)); |
844 | if (!spi_imx->base) { | |
b5f3294f SH |
845 | ret = -EINVAL; |
846 | goto out_release_mem; | |
847 | } | |
848 | ||
6cdeb002 | 849 | spi_imx->irq = platform_get_irq(pdev, 0); |
60f675a1 | 850 | if (spi_imx->irq <= 0) { |
b5f3294f SH |
851 | ret = -EINVAL; |
852 | goto out_iounmap; | |
853 | } | |
854 | ||
6cdeb002 | 855 | ret = request_irq(spi_imx->irq, spi_imx_isr, 0, DRIVER_NAME, spi_imx); |
b5f3294f | 856 | if (ret) { |
6cdeb002 | 857 | dev_err(&pdev->dev, "can't get irq%d: %d\n", spi_imx->irq, ret); |
b5f3294f SH |
858 | goto out_iounmap; |
859 | } | |
860 | ||
6cdeb002 UKK |
861 | spi_imx->clk = clk_get(&pdev->dev, NULL); |
862 | if (IS_ERR(spi_imx->clk)) { | |
b5f3294f | 863 | dev_err(&pdev->dev, "unable to get clock\n"); |
6cdeb002 | 864 | ret = PTR_ERR(spi_imx->clk); |
b5f3294f SH |
865 | goto out_free_irq; |
866 | } | |
867 | ||
6cdeb002 UKK |
868 | clk_enable(spi_imx->clk); |
869 | spi_imx->spi_clk = clk_get_rate(spi_imx->clk); | |
b5f3294f | 870 | |
1723e66b | 871 | spi_imx->devtype_data.reset(spi_imx); |
ce1807b2 | 872 | |
f4ba6315 | 873 | spi_imx->devtype_data.intctrl(spi_imx, 0); |
b5f3294f | 874 | |
6cdeb002 | 875 | ret = spi_bitbang_start(&spi_imx->bitbang); |
b5f3294f SH |
876 | if (ret) { |
877 | dev_err(&pdev->dev, "bitbang start failed with %d\n", ret); | |
878 | goto out_clk_put; | |
879 | } | |
880 | ||
881 | dev_info(&pdev->dev, "probed\n"); | |
882 | ||
883 | return ret; | |
884 | ||
885 | out_clk_put: | |
6cdeb002 UKK |
886 | clk_disable(spi_imx->clk); |
887 | clk_put(spi_imx->clk); | |
b5f3294f | 888 | out_free_irq: |
6cdeb002 | 889 | free_irq(spi_imx->irq, spi_imx); |
b5f3294f | 890 | out_iounmap: |
6cdeb002 | 891 | iounmap(spi_imx->base); |
b5f3294f SH |
892 | out_release_mem: |
893 | release_mem_region(res->start, resource_size(res)); | |
894 | out_gpio_free: | |
895 | for (i = 0; i < master->num_chipselect; i++) | |
6cdeb002 UKK |
896 | if (spi_imx->chipselect[i] >= 0) |
897 | gpio_free(spi_imx->chipselect[i]); | |
b5f3294f SH |
898 | out_master_put: |
899 | spi_master_put(master); | |
900 | kfree(master); | |
901 | platform_set_drvdata(pdev, NULL); | |
902 | return ret; | |
903 | } | |
904 | ||
965346e3 | 905 | static int __devexit spi_imx_remove(struct platform_device *pdev) |
b5f3294f SH |
906 | { |
907 | struct spi_master *master = platform_get_drvdata(pdev); | |
908 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
6cdeb002 | 909 | struct spi_imx_data *spi_imx = spi_master_get_devdata(master); |
b5f3294f SH |
910 | int i; |
911 | ||
6cdeb002 | 912 | spi_bitbang_stop(&spi_imx->bitbang); |
b5f3294f | 913 | |
6cdeb002 UKK |
914 | writel(0, spi_imx->base + MXC_CSPICTRL); |
915 | clk_disable(spi_imx->clk); | |
916 | clk_put(spi_imx->clk); | |
917 | free_irq(spi_imx->irq, spi_imx); | |
918 | iounmap(spi_imx->base); | |
b5f3294f SH |
919 | |
920 | for (i = 0; i < master->num_chipselect; i++) | |
6cdeb002 UKK |
921 | if (spi_imx->chipselect[i] >= 0) |
922 | gpio_free(spi_imx->chipselect[i]); | |
b5f3294f SH |
923 | |
924 | spi_master_put(master); | |
925 | ||
926 | release_mem_region(res->start, resource_size(res)); | |
927 | ||
928 | platform_set_drvdata(pdev, NULL); | |
929 | ||
930 | return 0; | |
931 | } | |
932 | ||
6cdeb002 | 933 | static struct platform_driver spi_imx_driver = { |
b5f3294f SH |
934 | .driver = { |
935 | .name = DRIVER_NAME, | |
936 | .owner = THIS_MODULE, | |
937 | }, | |
f4ba6315 | 938 | .id_table = spi_imx_devtype, |
6cdeb002 | 939 | .probe = spi_imx_probe, |
965346e3 | 940 | .remove = __devexit_p(spi_imx_remove), |
b5f3294f SH |
941 | }; |
942 | ||
6cdeb002 | 943 | static int __init spi_imx_init(void) |
b5f3294f | 944 | { |
6cdeb002 | 945 | return platform_driver_register(&spi_imx_driver); |
b5f3294f SH |
946 | } |
947 | ||
6cdeb002 | 948 | static void __exit spi_imx_exit(void) |
b5f3294f | 949 | { |
6cdeb002 | 950 | platform_driver_unregister(&spi_imx_driver); |
b5f3294f SH |
951 | } |
952 | ||
6cdeb002 UKK |
953 | module_init(spi_imx_init); |
954 | module_exit(spi_imx_exit); | |
b5f3294f SH |
955 | |
956 | MODULE_DESCRIPTION("SPI Master Controller driver"); | |
957 | MODULE_AUTHOR("Sascha Hauer, Pengutronix"); | |
958 | MODULE_LICENSE("GPL"); |