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