]>
Commit | Line | Data |
---|---|---|
35c9049b CM |
1 | /* |
2 | * OMAP7xx SPI 100k controller driver | |
3 | * Author: Fabrice Crohas <fcrohas@gmail.com> | |
4 | * from original omap1_mcspi driver | |
5 | * | |
6 | * Copyright (C) 2005, 2006 Nokia Corporation | |
7 | * Author: Samuel Ortiz <samuel.ortiz@nokia.com> and | |
8 | * Juha Yrj�l� <juha.yrjola@nokia.com> | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify | |
11 | * it under the terms of the GNU General Public License as published by | |
12 | * the Free Software Foundation; either version 2 of the License, or | |
13 | * (at your option) any later version. | |
14 | * | |
15 | * This program is distributed in the hope that it will be useful, | |
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | * GNU General Public License for more details. | |
19 | * | |
20 | * You should have received a copy of the GNU General Public License | |
21 | * along with this program; if not, write to the Free Software | |
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
23 | * | |
24 | */ | |
25 | #include <linux/kernel.h> | |
26 | #include <linux/init.h> | |
27 | #include <linux/interrupt.h> | |
28 | #include <linux/module.h> | |
29 | #include <linux/device.h> | |
30 | #include <linux/delay.h> | |
31 | #include <linux/platform_device.h> | |
32 | #include <linux/err.h> | |
33 | #include <linux/clk.h> | |
34 | #include <linux/io.h> | |
35 | #include <linux/gpio.h> | |
5a0e3ad6 | 36 | #include <linux/slab.h> |
35c9049b CM |
37 | |
38 | #include <linux/spi/spi.h> | |
39 | ||
35c9049b CM |
40 | #define OMAP1_SPI100K_MAX_FREQ 48000000 |
41 | ||
42 | #define ICR_SPITAS (OMAP7XX_ICR_BASE + 0x12) | |
43 | ||
44 | #define SPI_SETUP1 0x00 | |
45 | #define SPI_SETUP2 0x02 | |
46 | #define SPI_CTRL 0x04 | |
47 | #define SPI_STATUS 0x06 | |
48 | #define SPI_TX_LSB 0x08 | |
49 | #define SPI_TX_MSB 0x0a | |
50 | #define SPI_RX_LSB 0x0c | |
51 | #define SPI_RX_MSB 0x0e | |
52 | ||
53 | #define SPI_SETUP1_INT_READ_ENABLE (1UL << 5) | |
54 | #define SPI_SETUP1_INT_WRITE_ENABLE (1UL << 4) | |
55 | #define SPI_SETUP1_CLOCK_DIVISOR(x) ((x) << 1) | |
56 | #define SPI_SETUP1_CLOCK_ENABLE (1UL << 0) | |
57 | ||
58 | #define SPI_SETUP2_ACTIVE_EDGE_FALLING (0UL << 0) | |
59 | #define SPI_SETUP2_ACTIVE_EDGE_RISING (1UL << 0) | |
60 | #define SPI_SETUP2_NEGATIVE_LEVEL (0UL << 5) | |
61 | #define SPI_SETUP2_POSITIVE_LEVEL (1UL << 5) | |
62 | #define SPI_SETUP2_LEVEL_TRIGGER (0UL << 10) | |
63 | #define SPI_SETUP2_EDGE_TRIGGER (1UL << 10) | |
64 | ||
65 | #define SPI_CTRL_SEN(x) ((x) << 7) | |
66 | #define SPI_CTRL_WORD_SIZE(x) (((x) - 1) << 2) | |
67 | #define SPI_CTRL_WR (1UL << 1) | |
68 | #define SPI_CTRL_RD (1UL << 0) | |
69 | ||
70 | #define SPI_STATUS_WE (1UL << 1) | |
71 | #define SPI_STATUS_RD (1UL << 0) | |
72 | ||
73 | #define WRITE 0 | |
74 | #define READ 1 | |
75 | ||
76 | ||
77 | /* use PIO for small transfers, avoiding DMA setup/teardown overhead and | |
78 | * cache operations; better heuristics consider wordsize and bitrate. | |
79 | */ | |
80 | #define DMA_MIN_BYTES 8 | |
81 | ||
82 | #define SPI_RUNNING 0 | |
83 | #define SPI_SHUTDOWN 1 | |
84 | ||
85 | struct omap1_spi100k { | |
35c9049b CM |
86 | struct spi_master *master; |
87 | struct clk *ick; | |
88 | struct clk *fck; | |
89 | ||
90 | /* Virtual base address of the controller */ | |
91 | void __iomem *base; | |
92 | ||
93 | /* State of the SPI */ | |
94 | unsigned int state; | |
95 | }; | |
96 | ||
97 | struct omap1_spi100k_cs { | |
98 | void __iomem *base; | |
99 | int word_len; | |
100 | }; | |
101 | ||
35c9049b CM |
102 | #define MOD_REG_BIT(val, mask, set) do { \ |
103 | if (set) \ | |
104 | val |= mask; \ | |
105 | else \ | |
106 | val &= ~mask; \ | |
107 | } while (0) | |
108 | ||
109 | static void spi100k_enable_clock(struct spi_master *master) | |
110 | { | |
111 | unsigned int val; | |
112 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
113 | ||
114 | /* enable SPI */ | |
115 | val = readw(spi100k->base + SPI_SETUP1); | |
116 | val |= SPI_SETUP1_CLOCK_ENABLE; | |
117 | writew(val, spi100k->base + SPI_SETUP1); | |
118 | } | |
119 | ||
120 | static void spi100k_disable_clock(struct spi_master *master) | |
121 | { | |
122 | unsigned int val; | |
123 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
124 | ||
125 | /* disable SPI */ | |
126 | val = readw(spi100k->base + SPI_SETUP1); | |
127 | val &= ~SPI_SETUP1_CLOCK_ENABLE; | |
128 | writew(val, spi100k->base + SPI_SETUP1); | |
129 | } | |
130 | ||
131 | static void spi100k_write_data(struct spi_master *master, int len, int data) | |
132 | { | |
133 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
134 | ||
5c2818cd CM |
135 | /* write 16-bit word, shifting 8-bit data if necessary */ |
136 | if (len <= 8) { | |
137 | data <<= 8; | |
138 | len = 16; | |
139 | } | |
140 | ||
35c9049b CM |
141 | spi100k_enable_clock(master); |
142 | writew( data , spi100k->base + SPI_TX_MSB); | |
143 | ||
144 | writew(SPI_CTRL_SEN(0) | | |
145 | SPI_CTRL_WORD_SIZE(len) | | |
146 | SPI_CTRL_WR, | |
147 | spi100k->base + SPI_CTRL); | |
148 | ||
149 | /* Wait for bit ack send change */ | |
150 | while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE); | |
151 | udelay(1000); | |
152 | ||
153 | spi100k_disable_clock(master); | |
154 | } | |
155 | ||
156 | static int spi100k_read_data(struct spi_master *master, int len) | |
157 | { | |
158 | int dataH,dataL; | |
159 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
160 | ||
5c2818cd CM |
161 | /* Always do at least 16 bits */ |
162 | if (len <= 8) | |
163 | len = 16; | |
164 | ||
35c9049b CM |
165 | spi100k_enable_clock(master); |
166 | writew(SPI_CTRL_SEN(0) | | |
167 | SPI_CTRL_WORD_SIZE(len) | | |
168 | SPI_CTRL_RD, | |
169 | spi100k->base + SPI_CTRL); | |
170 | ||
171 | while((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD); | |
172 | udelay(1000); | |
173 | ||
174 | dataL = readw(spi100k->base + SPI_RX_LSB); | |
175 | dataH = readw(spi100k->base + SPI_RX_MSB); | |
176 | spi100k_disable_clock(master); | |
177 | ||
178 | return dataL; | |
179 | } | |
180 | ||
181 | static void spi100k_open(struct spi_master *master) | |
182 | { | |
183 | /* get control of SPI */ | |
184 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
185 | ||
186 | writew(SPI_SETUP1_INT_READ_ENABLE | | |
187 | SPI_SETUP1_INT_WRITE_ENABLE | | |
188 | SPI_SETUP1_CLOCK_DIVISOR(0), spi100k->base + SPI_SETUP1); | |
189 | ||
190 | /* configure clock and interrupts */ | |
191 | writew(SPI_SETUP2_ACTIVE_EDGE_FALLING | | |
192 | SPI_SETUP2_NEGATIVE_LEVEL | | |
193 | SPI_SETUP2_LEVEL_TRIGGER, spi100k->base + SPI_SETUP2); | |
194 | } | |
195 | ||
196 | static void omap1_spi100k_force_cs(struct omap1_spi100k *spi100k, int enable) | |
197 | { | |
198 | if (enable) | |
199 | writew(0x05fc, spi100k->base + SPI_CTRL); | |
200 | else | |
201 | writew(0x05fd, spi100k->base + SPI_CTRL); | |
202 | } | |
203 | ||
204 | static unsigned | |
205 | omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) | |
206 | { | |
207 | struct omap1_spi100k *spi100k; | |
208 | struct omap1_spi100k_cs *cs = spi->controller_state; | |
209 | unsigned int count, c; | |
210 | int word_len; | |
211 | ||
212 | spi100k = spi_master_get_devdata(spi->master); | |
213 | count = xfer->len; | |
214 | c = count; | |
215 | word_len = cs->word_len; | |
216 | ||
35c9049b CM |
217 | if (word_len <= 8) { |
218 | u8 *rx; | |
219 | const u8 *tx; | |
220 | ||
221 | rx = xfer->rx_buf; | |
222 | tx = xfer->tx_buf; | |
223 | do { | |
224 | c-=1; | |
225 | if (xfer->tx_buf != NULL) | |
5c2818cd | 226 | spi100k_write_data(spi->master, word_len, *tx++); |
35c9049b | 227 | if (xfer->rx_buf != NULL) |
5c2818cd | 228 | *rx++ = spi100k_read_data(spi->master, word_len); |
35c9049b CM |
229 | } while(c); |
230 | } else if (word_len <= 16) { | |
231 | u16 *rx; | |
232 | const u16 *tx; | |
233 | ||
234 | rx = xfer->rx_buf; | |
235 | tx = xfer->tx_buf; | |
236 | do { | |
237 | c-=2; | |
238 | if (xfer->tx_buf != NULL) | |
239 | spi100k_write_data(spi->master,word_len, *tx++); | |
240 | if (xfer->rx_buf != NULL) | |
241 | *rx++ = spi100k_read_data(spi->master,word_len); | |
242 | } while(c); | |
243 | } else if (word_len <= 32) { | |
244 | u32 *rx; | |
245 | const u32 *tx; | |
246 | ||
247 | rx = xfer->rx_buf; | |
248 | tx = xfer->tx_buf; | |
249 | do { | |
250 | c-=4; | |
251 | if (xfer->tx_buf != NULL) | |
252 | spi100k_write_data(spi->master,word_len, *tx); | |
253 | if (xfer->rx_buf != NULL) | |
254 | *rx = spi100k_read_data(spi->master,word_len); | |
255 | } while(c); | |
256 | } | |
257 | return count - c; | |
258 | } | |
259 | ||
260 | /* called only when no transfer is active to this device */ | |
261 | static int omap1_spi100k_setup_transfer(struct spi_device *spi, | |
262 | struct spi_transfer *t) | |
263 | { | |
264 | struct omap1_spi100k *spi100k = spi_master_get_devdata(spi->master); | |
265 | struct omap1_spi100k_cs *cs = spi->controller_state; | |
266 | u8 word_len = spi->bits_per_word; | |
267 | ||
268 | if (t != NULL && t->bits_per_word) | |
269 | word_len = t->bits_per_word; | |
270 | if (!word_len) | |
271 | word_len = 8; | |
272 | ||
273 | if (spi->bits_per_word > 32) | |
274 | return -EINVAL; | |
275 | cs->word_len = word_len; | |
276 | ||
277 | /* SPI init before transfer */ | |
278 | writew(0x3e , spi100k->base + SPI_SETUP1); | |
279 | writew(0x00 , spi100k->base + SPI_STATUS); | |
280 | writew(0x3e , spi100k->base + SPI_CTRL); | |
281 | ||
282 | return 0; | |
283 | } | |
284 | ||
285 | /* the spi->mode bits understood by this driver: */ | |
286 | #define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) | |
287 | ||
288 | static int omap1_spi100k_setup(struct spi_device *spi) | |
289 | { | |
290 | int ret; | |
291 | struct omap1_spi100k *spi100k; | |
292 | struct omap1_spi100k_cs *cs = spi->controller_state; | |
293 | ||
35c9049b CM |
294 | spi100k = spi_master_get_devdata(spi->master); |
295 | ||
296 | if (!cs) { | |
297 | cs = kzalloc(sizeof *cs, GFP_KERNEL); | |
298 | if (!cs) | |
299 | return -ENOMEM; | |
300 | cs->base = spi100k->base + spi->chip_select * 0x14; | |
301 | spi->controller_state = cs; | |
302 | } | |
303 | ||
304 | spi100k_open(spi->master); | |
305 | ||
13cd19e8 MB |
306 | clk_prepare_enable(spi100k->ick); |
307 | clk_prepare_enable(spi100k->fck); | |
35c9049b CM |
308 | |
309 | ret = omap1_spi100k_setup_transfer(spi, NULL); | |
310 | ||
13cd19e8 MB |
311 | clk_disable_unprepare(spi100k->ick); |
312 | clk_disable_unprepare(spi100k->fck); | |
35c9049b CM |
313 | |
314 | return ret; | |
315 | } | |
316 | ||
da60b855 MB |
317 | static int omap1_spi100k_prepare_hardware(struct spi_master *master) |
318 | { | |
319 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
320 | ||
13cd19e8 MB |
321 | clk_prepare_enable(spi100k->ick); |
322 | clk_prepare_enable(spi100k->fck); | |
da60b855 MB |
323 | |
324 | return 0; | |
325 | } | |
326 | ||
e8153ab3 MB |
327 | static int omap1_spi100k_transfer_one_message(struct spi_master *master, |
328 | struct spi_message *m) | |
329 | { | |
330 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
331 | struct spi_device *spi = m->spi; | |
332 | struct spi_transfer *t = NULL; | |
333 | int cs_active = 0; | |
334 | int par_override = 0; | |
335 | int status = 0; | |
336 | ||
337 | list_for_each_entry(t, &m->transfers, transfer_list) { | |
338 | if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { | |
339 | status = -EINVAL; | |
340 | break; | |
341 | } | |
342 | if (par_override || t->speed_hz || t->bits_per_word) { | |
343 | par_override = 1; | |
344 | status = omap1_spi100k_setup_transfer(spi, t); | |
345 | if (status < 0) | |
346 | break; | |
347 | if (!t->speed_hz && !t->bits_per_word) | |
348 | par_override = 0; | |
349 | } | |
350 | ||
351 | if (!cs_active) { | |
352 | omap1_spi100k_force_cs(spi100k, 1); | |
353 | cs_active = 1; | |
354 | } | |
355 | ||
356 | if (t->len) { | |
357 | unsigned count; | |
358 | ||
359 | count = omap1_spi100k_txrx_pio(spi, t); | |
360 | m->actual_length += count; | |
361 | ||
362 | if (count != t->len) { | |
363 | status = -EIO; | |
364 | break; | |
365 | } | |
366 | } | |
367 | ||
368 | if (t->delay_usecs) | |
369 | udelay(t->delay_usecs); | |
370 | ||
371 | /* ignore the "leave it on after last xfer" hint */ | |
372 | ||
373 | if (t->cs_change) { | |
374 | omap1_spi100k_force_cs(spi100k, 0); | |
375 | cs_active = 0; | |
376 | } | |
377 | } | |
378 | ||
379 | /* Restore defaults if they were overriden */ | |
380 | if (par_override) { | |
381 | par_override = 0; | |
382 | status = omap1_spi100k_setup_transfer(spi, NULL); | |
383 | } | |
384 | ||
385 | if (cs_active) | |
386 | omap1_spi100k_force_cs(spi100k, 0); | |
387 | ||
388 | m->status = status; | |
da60b855 MB |
389 | |
390 | spi_finalize_current_message(master); | |
e8153ab3 MB |
391 | |
392 | return status; | |
393 | } | |
394 | ||
da60b855 | 395 | static int omap1_spi100k_unprepare_hardware(struct spi_master *master) |
35c9049b | 396 | { |
da60b855 | 397 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); |
35c9049b | 398 | |
13cd19e8 MB |
399 | clk_disable_unprepare(spi100k->ick); |
400 | clk_disable_unprepare(spi100k->fck); | |
35c9049b CM |
401 | |
402 | return 0; | |
403 | } | |
404 | ||
fd4a319b | 405 | static int omap1_spi100k_probe(struct platform_device *pdev) |
35c9049b CM |
406 | { |
407 | struct spi_master *master; | |
408 | struct omap1_spi100k *spi100k; | |
409 | int status = 0; | |
410 | ||
411 | if (!pdev->id) | |
412 | return -EINVAL; | |
413 | ||
414 | master = spi_alloc_master(&pdev->dev, sizeof *spi100k); | |
415 | if (master == NULL) { | |
416 | dev_dbg(&pdev->dev, "master allocation failed\n"); | |
417 | return -ENOMEM; | |
418 | } | |
419 | ||
420 | if (pdev->id != -1) | |
421 | master->bus_num = pdev->id; | |
422 | ||
423 | master->setup = omap1_spi100k_setup; | |
da60b855 MB |
424 | master->transfer_one_message = omap1_spi100k_transfer_one_message; |
425 | master->prepare_transfer_hardware = omap1_spi100k_prepare_hardware; | |
426 | master->unprepare_transfer_hardware = omap1_spi100k_unprepare_hardware; | |
35c9049b CM |
427 | master->cleanup = NULL; |
428 | master->num_chipselect = 2; | |
429 | master->mode_bits = MODEBITS; | |
24778be2 | 430 | master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); |
69ea672a MB |
431 | master->min_speed_hz = OMAP1_SPI100K_MAX_FREQ/(1<<16); |
432 | master->max_speed_hz = OMAP1_SPI100K_MAX_FREQ; | |
35c9049b | 433 | |
24b5a82c | 434 | platform_set_drvdata(pdev, master); |
35c9049b CM |
435 | |
436 | spi100k = spi_master_get_devdata(master); | |
437 | spi100k->master = master; | |
438 | ||
439 | /* | |
440 | * The memory region base address is taken as the platform_data. | |
441 | * You should allocate this with ioremap() before initializing | |
442 | * the SPI. | |
443 | */ | |
8074cf06 | 444 | spi100k->base = (void __iomem *)dev_get_platdata(&pdev->dev); |
35c9049b | 445 | |
022a9412 | 446 | spi100k->ick = devm_clk_get(&pdev->dev, "ick"); |
35c9049b CM |
447 | if (IS_ERR(spi100k->ick)) { |
448 | dev_dbg(&pdev->dev, "can't get spi100k_ick\n"); | |
449 | status = PTR_ERR(spi100k->ick); | |
022a9412 | 450 | goto err; |
35c9049b CM |
451 | } |
452 | ||
022a9412 | 453 | spi100k->fck = devm_clk_get(&pdev->dev, "fck"); |
35c9049b CM |
454 | if (IS_ERR(spi100k->fck)) { |
455 | dev_dbg(&pdev->dev, "can't get spi100k_fck\n"); | |
456 | status = PTR_ERR(spi100k->fck); | |
022a9412 | 457 | goto err; |
35c9049b CM |
458 | } |
459 | ||
5c4c5c7b | 460 | status = devm_spi_register_master(&pdev->dev, master); |
35c9049b | 461 | if (status < 0) |
022a9412 | 462 | goto err; |
35c9049b CM |
463 | |
464 | spi100k->state = SPI_RUNNING; | |
465 | ||
466 | return status; | |
467 | ||
022a9412 | 468 | err: |
35c9049b CM |
469 | spi_master_put(master); |
470 | return status; | |
471 | } | |
472 | ||
2deff8d6 | 473 | static int omap1_spi100k_remove(struct platform_device *pdev) |
35c9049b CM |
474 | { |
475 | struct spi_master *master; | |
476 | struct omap1_spi100k *spi100k; | |
477 | struct resource *r; | |
35c9049b CM |
478 | int status = 0; |
479 | ||
24b5a82c | 480 | master = platform_get_drvdata(pdev); |
35c9049b CM |
481 | spi100k = spi_master_get_devdata(master); |
482 | ||
35c9049b CM |
483 | if (status != 0) |
484 | return status; | |
485 | ||
35c9049b CM |
486 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
487 | ||
35c9049b CM |
488 | return 0; |
489 | } | |
490 | ||
491 | static struct platform_driver omap1_spi100k_driver = { | |
492 | .driver = { | |
493 | .name = "omap1_spi100k", | |
494 | .owner = THIS_MODULE, | |
495 | }, | |
2d0c6148 | 496 | .probe = omap1_spi100k_probe, |
2deff8d6 | 497 | .remove = omap1_spi100k_remove, |
35c9049b CM |
498 | }; |
499 | ||
2d0c6148 | 500 | module_platform_driver(omap1_spi100k_driver); |
35c9049b CM |
501 | |
502 | MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver"); | |
503 | MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>"); | |
504 | MODULE_LICENSE("GPL"); | |
505 |