]>
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 clk *ick; |
87 | struct clk *fck; | |
88 | ||
89 | /* Virtual base address of the controller */ | |
90 | void __iomem *base; | |
35c9049b CM |
91 | }; |
92 | ||
93 | struct omap1_spi100k_cs { | |
94 | void __iomem *base; | |
95 | int word_len; | |
96 | }; | |
97 | ||
35c9049b CM |
98 | static void spi100k_enable_clock(struct spi_master *master) |
99 | { | |
100 | unsigned int val; | |
101 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
102 | ||
103 | /* enable SPI */ | |
104 | val = readw(spi100k->base + SPI_SETUP1); | |
105 | val |= SPI_SETUP1_CLOCK_ENABLE; | |
106 | writew(val, spi100k->base + SPI_SETUP1); | |
107 | } | |
108 | ||
109 | static void spi100k_disable_clock(struct spi_master *master) | |
110 | { | |
111 | unsigned int val; | |
112 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
113 | ||
114 | /* disable 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_write_data(struct spi_master *master, int len, int data) | |
121 | { | |
122 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
123 | ||
5c2818cd CM |
124 | /* write 16-bit word, shifting 8-bit data if necessary */ |
125 | if (len <= 8) { | |
126 | data <<= 8; | |
127 | len = 16; | |
128 | } | |
129 | ||
35c9049b | 130 | spi100k_enable_clock(master); |
31804f63 | 131 | writew(data , spi100k->base + SPI_TX_MSB); |
35c9049b CM |
132 | |
133 | writew(SPI_CTRL_SEN(0) | | |
134 | SPI_CTRL_WORD_SIZE(len) | | |
135 | SPI_CTRL_WR, | |
136 | spi100k->base + SPI_CTRL); | |
137 | ||
138 | /* Wait for bit ack send change */ | |
31804f63 JH |
139 | while ((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE) |
140 | ; | |
35c9049b CM |
141 | udelay(1000); |
142 | ||
143 | spi100k_disable_clock(master); | |
144 | } | |
145 | ||
146 | static int spi100k_read_data(struct spi_master *master, int len) | |
147 | { | |
31804f63 | 148 | int dataH, dataL; |
35c9049b CM |
149 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); |
150 | ||
5c2818cd CM |
151 | /* Always do at least 16 bits */ |
152 | if (len <= 8) | |
153 | len = 16; | |
154 | ||
35c9049b CM |
155 | spi100k_enable_clock(master); |
156 | writew(SPI_CTRL_SEN(0) | | |
157 | SPI_CTRL_WORD_SIZE(len) | | |
158 | SPI_CTRL_RD, | |
159 | spi100k->base + SPI_CTRL); | |
160 | ||
31804f63 JH |
161 | while ((readw(spi100k->base + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD) |
162 | ; | |
35c9049b CM |
163 | udelay(1000); |
164 | ||
165 | dataL = readw(spi100k->base + SPI_RX_LSB); | |
166 | dataH = readw(spi100k->base + SPI_RX_MSB); | |
167 | spi100k_disable_clock(master); | |
168 | ||
169 | return dataL; | |
170 | } | |
171 | ||
172 | static void spi100k_open(struct spi_master *master) | |
173 | { | |
174 | /* get control of SPI */ | |
175 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
176 | ||
177 | writew(SPI_SETUP1_INT_READ_ENABLE | | |
178 | SPI_SETUP1_INT_WRITE_ENABLE | | |
179 | SPI_SETUP1_CLOCK_DIVISOR(0), spi100k->base + SPI_SETUP1); | |
180 | ||
181 | /* configure clock and interrupts */ | |
182 | writew(SPI_SETUP2_ACTIVE_EDGE_FALLING | | |
183 | SPI_SETUP2_NEGATIVE_LEVEL | | |
184 | SPI_SETUP2_LEVEL_TRIGGER, spi100k->base + SPI_SETUP2); | |
185 | } | |
186 | ||
187 | static void omap1_spi100k_force_cs(struct omap1_spi100k *spi100k, int enable) | |
188 | { | |
189 | if (enable) | |
190 | writew(0x05fc, spi100k->base + SPI_CTRL); | |
191 | else | |
192 | writew(0x05fd, spi100k->base + SPI_CTRL); | |
193 | } | |
194 | ||
195 | static unsigned | |
196 | omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) | |
197 | { | |
35c9049b CM |
198 | struct omap1_spi100k_cs *cs = spi->controller_state; |
199 | unsigned int count, c; | |
200 | int word_len; | |
201 | ||
35c9049b CM |
202 | count = xfer->len; |
203 | c = count; | |
204 | word_len = cs->word_len; | |
205 | ||
35c9049b CM |
206 | if (word_len <= 8) { |
207 | u8 *rx; | |
208 | const u8 *tx; | |
209 | ||
210 | rx = xfer->rx_buf; | |
211 | tx = xfer->tx_buf; | |
212 | do { | |
31804f63 | 213 | c -= 1; |
35c9049b | 214 | if (xfer->tx_buf != NULL) |
5c2818cd | 215 | spi100k_write_data(spi->master, word_len, *tx++); |
35c9049b | 216 | if (xfer->rx_buf != NULL) |
5c2818cd | 217 | *rx++ = spi100k_read_data(spi->master, word_len); |
31804f63 | 218 | } while (c); |
35c9049b CM |
219 | } else if (word_len <= 16) { |
220 | u16 *rx; | |
221 | const u16 *tx; | |
222 | ||
223 | rx = xfer->rx_buf; | |
224 | tx = xfer->tx_buf; | |
225 | do { | |
31804f63 | 226 | c -= 2; |
35c9049b | 227 | if (xfer->tx_buf != NULL) |
31804f63 | 228 | spi100k_write_data(spi->master, word_len, *tx++); |
35c9049b | 229 | if (xfer->rx_buf != NULL) |
31804f63 JH |
230 | *rx++ = spi100k_read_data(spi->master, word_len); |
231 | } while (c); | |
35c9049b CM |
232 | } else if (word_len <= 32) { |
233 | u32 *rx; | |
234 | const u32 *tx; | |
235 | ||
236 | rx = xfer->rx_buf; | |
237 | tx = xfer->tx_buf; | |
238 | do { | |
31804f63 | 239 | c -= 4; |
35c9049b | 240 | if (xfer->tx_buf != NULL) |
31804f63 | 241 | spi100k_write_data(spi->master, word_len, *tx); |
35c9049b | 242 | if (xfer->rx_buf != NULL) |
31804f63 JH |
243 | *rx = spi100k_read_data(spi->master, word_len); |
244 | } while (c); | |
35c9049b CM |
245 | } |
246 | return count - c; | |
247 | } | |
248 | ||
249 | /* called only when no transfer is active to this device */ | |
250 | static int omap1_spi100k_setup_transfer(struct spi_device *spi, | |
251 | struct spi_transfer *t) | |
252 | { | |
253 | struct omap1_spi100k *spi100k = spi_master_get_devdata(spi->master); | |
254 | struct omap1_spi100k_cs *cs = spi->controller_state; | |
255 | u8 word_len = spi->bits_per_word; | |
256 | ||
257 | if (t != NULL && t->bits_per_word) | |
258 | word_len = t->bits_per_word; | |
259 | if (!word_len) | |
260 | word_len = 8; | |
261 | ||
262 | if (spi->bits_per_word > 32) | |
263 | return -EINVAL; | |
264 | cs->word_len = word_len; | |
265 | ||
266 | /* SPI init before transfer */ | |
267 | writew(0x3e , spi100k->base + SPI_SETUP1); | |
268 | writew(0x00 , spi100k->base + SPI_STATUS); | |
269 | writew(0x3e , spi100k->base + SPI_CTRL); | |
270 | ||
271 | return 0; | |
272 | } | |
273 | ||
274 | /* the spi->mode bits understood by this driver: */ | |
275 | #define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) | |
276 | ||
277 | static int omap1_spi100k_setup(struct spi_device *spi) | |
278 | { | |
279 | int ret; | |
280 | struct omap1_spi100k *spi100k; | |
281 | struct omap1_spi100k_cs *cs = spi->controller_state; | |
282 | ||
35c9049b CM |
283 | spi100k = spi_master_get_devdata(spi->master); |
284 | ||
285 | if (!cs) { | |
d1c18caa | 286 | cs = devm_kzalloc(&spi->dev, sizeof(*cs), GFP_KERNEL); |
35c9049b CM |
287 | if (!cs) |
288 | return -ENOMEM; | |
289 | cs->base = spi100k->base + spi->chip_select * 0x14; | |
290 | spi->controller_state = cs; | |
291 | } | |
292 | ||
293 | spi100k_open(spi->master); | |
294 | ||
13cd19e8 MB |
295 | clk_prepare_enable(spi100k->ick); |
296 | clk_prepare_enable(spi100k->fck); | |
35c9049b CM |
297 | |
298 | ret = omap1_spi100k_setup_transfer(spi, NULL); | |
299 | ||
13cd19e8 MB |
300 | clk_disable_unprepare(spi100k->ick); |
301 | clk_disable_unprepare(spi100k->fck); | |
35c9049b CM |
302 | |
303 | return ret; | |
304 | } | |
305 | ||
da60b855 MB |
306 | static int omap1_spi100k_prepare_hardware(struct spi_master *master) |
307 | { | |
308 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
309 | ||
13cd19e8 MB |
310 | clk_prepare_enable(spi100k->ick); |
311 | clk_prepare_enable(spi100k->fck); | |
da60b855 MB |
312 | |
313 | return 0; | |
314 | } | |
315 | ||
e8153ab3 MB |
316 | static int omap1_spi100k_transfer_one_message(struct spi_master *master, |
317 | struct spi_message *m) | |
318 | { | |
319 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); | |
320 | struct spi_device *spi = m->spi; | |
321 | struct spi_transfer *t = NULL; | |
322 | int cs_active = 0; | |
323 | int par_override = 0; | |
324 | int status = 0; | |
325 | ||
326 | list_for_each_entry(t, &m->transfers, transfer_list) { | |
327 | if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { | |
328 | status = -EINVAL; | |
329 | break; | |
330 | } | |
331 | if (par_override || t->speed_hz || t->bits_per_word) { | |
332 | par_override = 1; | |
333 | status = omap1_spi100k_setup_transfer(spi, t); | |
334 | if (status < 0) | |
335 | break; | |
336 | if (!t->speed_hz && !t->bits_per_word) | |
337 | par_override = 0; | |
338 | } | |
339 | ||
340 | if (!cs_active) { | |
341 | omap1_spi100k_force_cs(spi100k, 1); | |
342 | cs_active = 1; | |
343 | } | |
344 | ||
345 | if (t->len) { | |
346 | unsigned count; | |
347 | ||
348 | count = omap1_spi100k_txrx_pio(spi, t); | |
349 | m->actual_length += count; | |
350 | ||
351 | if (count != t->len) { | |
352 | status = -EIO; | |
353 | break; | |
354 | } | |
355 | } | |
356 | ||
357 | if (t->delay_usecs) | |
358 | udelay(t->delay_usecs); | |
359 | ||
360 | /* ignore the "leave it on after last xfer" hint */ | |
361 | ||
362 | if (t->cs_change) { | |
363 | omap1_spi100k_force_cs(spi100k, 0); | |
364 | cs_active = 0; | |
365 | } | |
366 | } | |
367 | ||
368 | /* Restore defaults if they were overriden */ | |
369 | if (par_override) { | |
370 | par_override = 0; | |
371 | status = omap1_spi100k_setup_transfer(spi, NULL); | |
372 | } | |
373 | ||
374 | if (cs_active) | |
375 | omap1_spi100k_force_cs(spi100k, 0); | |
376 | ||
377 | m->status = status; | |
da60b855 MB |
378 | |
379 | spi_finalize_current_message(master); | |
e8153ab3 MB |
380 | |
381 | return status; | |
382 | } | |
383 | ||
da60b855 | 384 | static int omap1_spi100k_unprepare_hardware(struct spi_master *master) |
35c9049b | 385 | { |
da60b855 | 386 | struct omap1_spi100k *spi100k = spi_master_get_devdata(master); |
35c9049b | 387 | |
13cd19e8 MB |
388 | clk_disable_unprepare(spi100k->ick); |
389 | clk_disable_unprepare(spi100k->fck); | |
35c9049b CM |
390 | |
391 | return 0; | |
392 | } | |
393 | ||
fd4a319b | 394 | static int omap1_spi100k_probe(struct platform_device *pdev) |
35c9049b CM |
395 | { |
396 | struct spi_master *master; | |
397 | struct omap1_spi100k *spi100k; | |
398 | int status = 0; | |
399 | ||
400 | if (!pdev->id) | |
401 | return -EINVAL; | |
402 | ||
31804f63 | 403 | master = spi_alloc_master(&pdev->dev, sizeof(*spi100k)); |
35c9049b CM |
404 | if (master == NULL) { |
405 | dev_dbg(&pdev->dev, "master allocation failed\n"); | |
406 | return -ENOMEM; | |
407 | } | |
408 | ||
409 | if (pdev->id != -1) | |
31804f63 | 410 | master->bus_num = pdev->id; |
35c9049b CM |
411 | |
412 | master->setup = omap1_spi100k_setup; | |
da60b855 MB |
413 | master->transfer_one_message = omap1_spi100k_transfer_one_message; |
414 | master->prepare_transfer_hardware = omap1_spi100k_prepare_hardware; | |
415 | master->unprepare_transfer_hardware = omap1_spi100k_unprepare_hardware; | |
35c9049b CM |
416 | master->cleanup = NULL; |
417 | master->num_chipselect = 2; | |
418 | master->mode_bits = MODEBITS; | |
24778be2 | 419 | master->bits_per_word_mask = SPI_BPW_RANGE_MASK(4, 32); |
69ea672a MB |
420 | master->min_speed_hz = OMAP1_SPI100K_MAX_FREQ/(1<<16); |
421 | master->max_speed_hz = OMAP1_SPI100K_MAX_FREQ; | |
35c9049b | 422 | |
24b5a82c | 423 | platform_set_drvdata(pdev, master); |
35c9049b CM |
424 | |
425 | spi100k = spi_master_get_devdata(master); | |
35c9049b CM |
426 | |
427 | /* | |
428 | * The memory region base address is taken as the platform_data. | |
429 | * You should allocate this with ioremap() before initializing | |
430 | * the SPI. | |
431 | */ | |
8074cf06 | 432 | spi100k->base = (void __iomem *)dev_get_platdata(&pdev->dev); |
35c9049b | 433 | |
022a9412 | 434 | spi100k->ick = devm_clk_get(&pdev->dev, "ick"); |
35c9049b CM |
435 | if (IS_ERR(spi100k->ick)) { |
436 | dev_dbg(&pdev->dev, "can't get spi100k_ick\n"); | |
437 | status = PTR_ERR(spi100k->ick); | |
022a9412 | 438 | goto err; |
35c9049b CM |
439 | } |
440 | ||
022a9412 | 441 | spi100k->fck = devm_clk_get(&pdev->dev, "fck"); |
35c9049b CM |
442 | if (IS_ERR(spi100k->fck)) { |
443 | dev_dbg(&pdev->dev, "can't get spi100k_fck\n"); | |
444 | status = PTR_ERR(spi100k->fck); | |
022a9412 | 445 | goto err; |
35c9049b CM |
446 | } |
447 | ||
5c4c5c7b | 448 | status = devm_spi_register_master(&pdev->dev, master); |
35c9049b | 449 | if (status < 0) |
022a9412 | 450 | goto err; |
35c9049b | 451 | |
35c9049b CM |
452 | return status; |
453 | ||
022a9412 | 454 | err: |
35c9049b CM |
455 | spi_master_put(master); |
456 | return status; | |
457 | } | |
458 | ||
35c9049b CM |
459 | static struct platform_driver omap1_spi100k_driver = { |
460 | .driver = { | |
461 | .name = "omap1_spi100k", | |
462 | .owner = THIS_MODULE, | |
463 | }, | |
2d0c6148 | 464 | .probe = omap1_spi100k_probe, |
35c9049b CM |
465 | }; |
466 | ||
2d0c6148 | 467 | module_platform_driver(omap1_spi100k_driver); |
35c9049b CM |
468 | |
469 | MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver"); | |
470 | MODULE_AUTHOR("Fabrice Crohas <fcrohas@gmail.com>"); | |
471 | MODULE_LICENSE("GPL"); |