diff options
author | Lee Jones <lee.jones@linaro.org> | 2014-12-09 15:21:30 -0500 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2014-12-22 13:16:14 -0500 |
commit | 9e862375c5420a329521c458a3c808c127e9038f (patch) | |
tree | e09af31c1bedc22526baa4ebf5c176c897f507af /drivers/spi/spi-st-ssc4.c | |
parent | 97bf6af1f928216fd6c5a66e8a57bfa95a659672 (diff) |
spi: Add new driver for STMicroelectronics' SPI Controller
This patch adds support for the SPI portion of ST's SSC device.
Signed-off-by: Lee Jones <lee.jones@linaro.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/spi/spi-st-ssc4.c')
-rw-r--r-- | drivers/spi/spi-st-ssc4.c | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c new file mode 100644 index 000000000000..8f8770a97ba7 --- /dev/null +++ b/drivers/spi/spi-st-ssc4.c | |||
@@ -0,0 +1,510 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2008-2014 STMicroelectronics Limited | ||
3 | * | ||
4 | * Author: Angus Clark <Angus.Clark@st.com> | ||
5 | * Patrice Chotard <patrice.chotard@st.com> | ||
6 | * Lee Jones <lee.jones@linaro.org> | ||
7 | * | ||
8 | * SPI master mode controller driver, used in STMicroelectronics devices. | ||
9 | * | ||
10 | * May be copied or modified under the terms of the GNU General Public | ||
11 | * License Version 2.0 only. See linux/COPYING for more information. | ||
12 | */ | ||
13 | |||
14 | #include <linux/clk.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/pinctrl/consumer.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/of.h> | ||
22 | #include <linux/of_gpio.h> | ||
23 | #include <linux/of_irq.h> | ||
24 | #include <linux/pm_runtime.h> | ||
25 | #include <linux/spi/spi.h> | ||
26 | #include <linux/spi/spi_bitbang.h> | ||
27 | |||
28 | /* SSC registers */ | ||
29 | #define SSC_BRG 0x000 | ||
30 | #define SSC_TBUF 0x004 | ||
31 | #define SSC_RBUF 0x008 | ||
32 | #define SSC_CTL 0x00C | ||
33 | #define SSC_IEN 0x010 | ||
34 | #define SSC_I2C 0x018 | ||
35 | |||
36 | /* SSC Control */ | ||
37 | #define SSC_CTL_DATA_WIDTH_9 0x8 | ||
38 | #define SSC_CTL_DATA_WIDTH_MSK 0xf | ||
39 | #define SSC_CTL_BM 0xf | ||
40 | #define SSC_CTL_HB BIT(4) | ||
41 | #define SSC_CTL_PH BIT(5) | ||
42 | #define SSC_CTL_PO BIT(6) | ||
43 | #define SSC_CTL_SR BIT(7) | ||
44 | #define SSC_CTL_MS BIT(8) | ||
45 | #define SSC_CTL_EN BIT(9) | ||
46 | #define SSC_CTL_LPB BIT(10) | ||
47 | #define SSC_CTL_EN_TX_FIFO BIT(11) | ||
48 | #define SSC_CTL_EN_RX_FIFO BIT(12) | ||
49 | #define SSC_CTL_EN_CLST_RX BIT(13) | ||
50 | |||
51 | /* SSC Interrupt Enable */ | ||
52 | #define SSC_IEN_TEEN BIT(2) | ||
53 | |||
54 | #define FIFO_SIZE 8 | ||
55 | |||
56 | struct spi_st { | ||
57 | /* SSC SPI Controller */ | ||
58 | void __iomem *base; | ||
59 | struct clk *clk; | ||
60 | struct device *dev; | ||
61 | |||
62 | /* SSC SPI current transaction */ | ||
63 | const u8 *tx_ptr; | ||
64 | u8 *rx_ptr; | ||
65 | u16 bytes_per_word; | ||
66 | unsigned int words_remaining; | ||
67 | unsigned int baud; | ||
68 | struct completion done; | ||
69 | }; | ||
70 | |||
71 | static int spi_st_clk_enable(struct spi_st *spi_st) | ||
72 | { | ||
73 | /* | ||
74 | * Current platforms use one of the core clocks for SPI and I2C. | ||
75 | * If we attempt to disable the clock, the system will hang. | ||
76 | * | ||
77 | * TODO: Remove this when platform supports power domains. | ||
78 | */ | ||
79 | return 0; | ||
80 | |||
81 | return clk_prepare_enable(spi_st->clk); | ||
82 | } | ||
83 | |||
84 | static void spi_st_clk_disable(struct spi_st *spi_st) | ||
85 | { | ||
86 | /* | ||
87 | * Current platforms use one of the core clocks for SPI and I2C. | ||
88 | * If we attempt to disable the clock, the system will hang. | ||
89 | * | ||
90 | * TODO: Remove this when platform supports power domains. | ||
91 | */ | ||
92 | return; | ||
93 | |||
94 | clk_disable_unprepare(spi_st->clk); | ||
95 | } | ||
96 | |||
97 | /* Load the TX FIFO */ | ||
98 | static void ssc_write_tx_fifo(struct spi_st *spi_st) | ||
99 | { | ||
100 | unsigned int count, i; | ||
101 | uint32_t word = 0; | ||
102 | |||
103 | if (spi_st->words_remaining > FIFO_SIZE) | ||
104 | count = FIFO_SIZE; | ||
105 | else | ||
106 | count = spi_st->words_remaining; | ||
107 | |||
108 | for (i = 0; i < count; i++) { | ||
109 | if (spi_st->tx_ptr) { | ||
110 | if (spi_st->bytes_per_word == 1) { | ||
111 | word = *spi_st->tx_ptr++; | ||
112 | } else { | ||
113 | word = *spi_st->tx_ptr++; | ||
114 | word = *spi_st->tx_ptr++ | (word << 8); | ||
115 | } | ||
116 | } | ||
117 | writel_relaxed(word, spi_st->base + SSC_TBUF); | ||
118 | } | ||
119 | } | ||
120 | |||
121 | /* Read the RX FIFO */ | ||
122 | static void ssc_read_rx_fifo(struct spi_st *spi_st) | ||
123 | { | ||
124 | unsigned int count, i; | ||
125 | uint32_t word = 0; | ||
126 | |||
127 | if (spi_st->words_remaining > FIFO_SIZE) | ||
128 | count = FIFO_SIZE; | ||
129 | else | ||
130 | count = spi_st->words_remaining; | ||
131 | |||
132 | for (i = 0; i < count; i++) { | ||
133 | word = readl_relaxed(spi_st->base + SSC_RBUF); | ||
134 | |||
135 | if (spi_st->rx_ptr) { | ||
136 | if (spi_st->bytes_per_word == 1) { | ||
137 | *spi_st->rx_ptr++ = (uint8_t)word; | ||
138 | } else { | ||
139 | *spi_st->rx_ptr++ = (word >> 8); | ||
140 | *spi_st->rx_ptr++ = word & 0xff; | ||
141 | } | ||
142 | } | ||
143 | } | ||
144 | spi_st->words_remaining -= count; | ||
145 | } | ||
146 | |||
147 | static int spi_st_transfer_one(struct spi_master *master, | ||
148 | struct spi_device *spi, struct spi_transfer *t) | ||
149 | { | ||
150 | struct spi_st *spi_st = spi_master_get_devdata(master); | ||
151 | uint32_t ctl = 0; | ||
152 | |||
153 | /* Setup transfer */ | ||
154 | spi_st->tx_ptr = t->tx_buf; | ||
155 | spi_st->rx_ptr = t->rx_buf; | ||
156 | |||
157 | if (spi->bits_per_word > 8) { | ||
158 | /* | ||
159 | * Anything greater than 8 bits-per-word requires 2 | ||
160 | * bytes-per-word in the RX/TX buffers | ||
161 | */ | ||
162 | spi_st->bytes_per_word = 2; | ||
163 | spi_st->words_remaining = t->len / 2; | ||
164 | |||
165 | } else if (spi->bits_per_word == 8 && !(t->len & 0x1)) { | ||
166 | /* | ||
167 | * If transfer is even-length, and 8 bits-per-word, then | ||
168 | * implement as half-length 16 bits-per-word transfer | ||
169 | */ | ||
170 | spi_st->bytes_per_word = 2; | ||
171 | spi_st->words_remaining = t->len / 2; | ||
172 | |||
173 | /* Set SSC_CTL to 16 bits-per-word */ | ||
174 | ctl = readl_relaxed(spi_st->base + SSC_CTL); | ||
175 | writel_relaxed((ctl | 0xf), spi_st->base + SSC_CTL); | ||
176 | |||
177 | readl_relaxed(spi_st->base + SSC_RBUF); | ||
178 | |||
179 | } else { | ||
180 | spi_st->bytes_per_word = 1; | ||
181 | spi_st->words_remaining = t->len; | ||
182 | } | ||
183 | |||
184 | reinit_completion(&spi_st->done); | ||
185 | |||
186 | /* Start transfer by writing to the TX FIFO */ | ||
187 | ssc_write_tx_fifo(spi_st); | ||
188 | writel_relaxed(SSC_IEN_TEEN, spi_st->base + SSC_IEN); | ||
189 | |||
190 | /* Wait for transfer to complete */ | ||
191 | wait_for_completion(&spi_st->done); | ||
192 | |||
193 | /* Restore SSC_CTL if necessary */ | ||
194 | if (ctl) | ||
195 | writel_relaxed(ctl, spi_st->base + SSC_CTL); | ||
196 | |||
197 | spi_finalize_current_transfer(spi->master); | ||
198 | |||
199 | return t->len; | ||
200 | } | ||
201 | |||
202 | static void spi_st_cleanup(struct spi_device *spi) | ||
203 | { | ||
204 | int cs = spi->cs_gpio; | ||
205 | |||
206 | if (gpio_is_valid(cs)) | ||
207 | devm_gpio_free(&spi->dev, cs); | ||
208 | } | ||
209 | |||
210 | /* the spi->mode bits understood by this driver: */ | ||
211 | #define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_LOOP | SPI_CS_HIGH) | ||
212 | static int spi_st_setup(struct spi_device *spi) | ||
213 | { | ||
214 | struct spi_st *spi_st = spi_master_get_devdata(spi->master); | ||
215 | u32 spi_st_clk, sscbrg, var; | ||
216 | u32 hz = spi->max_speed_hz; | ||
217 | int cs = spi->cs_gpio; | ||
218 | int ret; | ||
219 | |||
220 | if (spi->mode & ~MODEBITS) { | ||
221 | dev_err(&spi->dev, "unsupported mode bits 0x%x\n", | ||
222 | spi->mode & ~MODEBITS); | ||
223 | return -EINVAL; | ||
224 | } | ||
225 | |||
226 | if (!hz) { | ||
227 | dev_err(&spi->dev, "max_speed_hz unspecified\n"); | ||
228 | return -EINVAL; | ||
229 | } | ||
230 | |||
231 | if (!gpio_is_valid(cs)) { | ||
232 | dev_err(&spi->dev, "%d is not a valid gpio\n", cs); | ||
233 | return -EINVAL; | ||
234 | } | ||
235 | |||
236 | if (devm_gpio_request(&spi->dev, cs, dev_name(&spi->dev))) { | ||
237 | dev_err(&spi->dev, "could not request gpio:%d\n", cs); | ||
238 | return -EINVAL; | ||
239 | } | ||
240 | |||
241 | ret = gpio_direction_output(cs, spi->mode & SPI_CS_HIGH); | ||
242 | if (ret) | ||
243 | return ret; | ||
244 | |||
245 | spi_st_clk = clk_get_rate(spi_st->clk); | ||
246 | |||
247 | /* Set SSC_BRF */ | ||
248 | sscbrg = spi_st_clk / (2 * hz); | ||
249 | if (sscbrg < 0x07 || sscbrg > BIT(16)) { | ||
250 | dev_err(&spi->dev, | ||
251 | "baudrate %d outside valid range %d\n", sscbrg, hz); | ||
252 | return -EINVAL; | ||
253 | } | ||
254 | |||
255 | spi_st->baud = spi_st_clk / (2 * sscbrg); | ||
256 | if (sscbrg == BIT(16)) /* 16-bit counter wraps */ | ||
257 | sscbrg = 0x0; | ||
258 | |||
259 | writel_relaxed(sscbrg, spi_st->base + SSC_BRG); | ||
260 | |||
261 | dev_dbg(&spi->dev, | ||
262 | "setting baudrate:target= %u hz, actual= %u hz, sscbrg= %u\n", | ||
263 | hz, spi_st->baud, sscbrg); | ||
264 | |||
265 | /* Set SSC_CTL and enable SSC */ | ||
266 | var = readl_relaxed(spi_st->base + SSC_CTL); | ||
267 | var |= SSC_CTL_MS; | ||
268 | |||
269 | if (spi->mode & SPI_CPOL) | ||
270 | var |= SSC_CTL_PO; | ||
271 | else | ||
272 | var &= ~SSC_CTL_PO; | ||
273 | |||
274 | if (spi->mode & SPI_CPHA) | ||
275 | var |= SSC_CTL_PH; | ||
276 | else | ||
277 | var &= ~SSC_CTL_PH; | ||
278 | |||
279 | if ((spi->mode & SPI_LSB_FIRST) == 0) | ||
280 | var |= SSC_CTL_HB; | ||
281 | else | ||
282 | var &= ~SSC_CTL_HB; | ||
283 | |||
284 | if (spi->mode & SPI_LOOP) | ||
285 | var |= SSC_CTL_LPB; | ||
286 | else | ||
287 | var &= ~SSC_CTL_LPB; | ||
288 | |||
289 | var &= ~SSC_CTL_DATA_WIDTH_MSK; | ||
290 | var |= (spi->bits_per_word - 1); | ||
291 | |||
292 | var |= SSC_CTL_EN_TX_FIFO | SSC_CTL_EN_RX_FIFO; | ||
293 | var |= SSC_CTL_EN; | ||
294 | |||
295 | writel_relaxed(var, spi_st->base + SSC_CTL); | ||
296 | |||
297 | /* Clear the status register */ | ||
298 | readl_relaxed(spi_st->base + SSC_RBUF); | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | /* Interrupt fired when TX shift register becomes empty */ | ||
304 | static irqreturn_t spi_st_irq(int irq, void *dev_id) | ||
305 | { | ||
306 | struct spi_st *spi_st = (struct spi_st *)dev_id; | ||
307 | |||
308 | /* Read RX FIFO */ | ||
309 | ssc_read_rx_fifo(spi_st); | ||
310 | |||
311 | /* Fill TX FIFO */ | ||
312 | if (spi_st->words_remaining) { | ||
313 | ssc_write_tx_fifo(spi_st); | ||
314 | } else { | ||
315 | /* TX/RX complete */ | ||
316 | writel_relaxed(0x0, spi_st->base + SSC_IEN); | ||
317 | /* | ||
318 | * read SSC_IEN to ensure that this bit is set | ||
319 | * before re-enabling interrupt | ||
320 | */ | ||
321 | readl(spi_st->base + SSC_IEN); | ||
322 | complete(&spi_st->done); | ||
323 | } | ||
324 | |||
325 | return IRQ_HANDLED; | ||
326 | } | ||
327 | |||
328 | static int spi_st_probe(struct platform_device *pdev) | ||
329 | { | ||
330 | struct device_node *np = pdev->dev.of_node; | ||
331 | struct spi_master *master; | ||
332 | struct resource *res; | ||
333 | struct spi_st *spi_st; | ||
334 | int irq, ret = 0; | ||
335 | u32 var; | ||
336 | |||
337 | master = spi_alloc_master(&pdev->dev, sizeof(*spi_st)); | ||
338 | if (!master) | ||
339 | return -ENOMEM; | ||
340 | |||
341 | master->dev.of_node = np; | ||
342 | master->mode_bits = MODEBITS; | ||
343 | master->setup = spi_st_setup; | ||
344 | master->cleanup = spi_st_cleanup; | ||
345 | master->transfer_one = spi_st_transfer_one; | ||
346 | master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); | ||
347 | master->auto_runtime_pm = true; | ||
348 | master->bus_num = pdev->id; | ||
349 | spi_st = spi_master_get_devdata(master); | ||
350 | |||
351 | spi_st->clk = devm_clk_get(&pdev->dev, "ssc"); | ||
352 | if (IS_ERR(spi_st->clk)) { | ||
353 | dev_err(&pdev->dev, "Unable to request clock\n"); | ||
354 | return PTR_ERR(spi_st->clk); | ||
355 | } | ||
356 | |||
357 | ret = spi_st_clk_enable(spi_st); | ||
358 | if (ret) | ||
359 | return ret; | ||
360 | |||
361 | init_completion(&spi_st->done); | ||
362 | |||
363 | /* Get resources */ | ||
364 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
365 | spi_st->base = devm_ioremap_resource(&pdev->dev, res); | ||
366 | if (IS_ERR(spi_st->base)) { | ||
367 | ret = PTR_ERR(spi_st->base); | ||
368 | goto clk_disable; | ||
369 | } | ||
370 | |||
371 | /* Disable I2C and Reset SSC */ | ||
372 | writel_relaxed(0x0, spi_st->base + SSC_I2C); | ||
373 | var = readw_relaxed(spi_st->base + SSC_CTL); | ||
374 | var |= SSC_CTL_SR; | ||
375 | writel_relaxed(var, spi_st->base + SSC_CTL); | ||
376 | |||
377 | udelay(1); | ||
378 | var = readl_relaxed(spi_st->base + SSC_CTL); | ||
379 | var &= ~SSC_CTL_SR; | ||
380 | writel_relaxed(var, spi_st->base + SSC_CTL); | ||
381 | |||
382 | /* Set SSC into slave mode before reconfiguring PIO pins */ | ||
383 | var = readl_relaxed(spi_st->base + SSC_CTL); | ||
384 | var &= ~SSC_CTL_MS; | ||
385 | writel_relaxed(var, spi_st->base + SSC_CTL); | ||
386 | |||
387 | irq = irq_of_parse_and_map(np, 0); | ||
388 | if (!irq) { | ||
389 | dev_err(&pdev->dev, "IRQ missing or invalid\n"); | ||
390 | ret = -EINVAL; | ||
391 | goto clk_disable; | ||
392 | } | ||
393 | |||
394 | ret = devm_request_irq(&pdev->dev, irq, spi_st_irq, 0, | ||
395 | pdev->name, spi_st); | ||
396 | if (ret) { | ||
397 | dev_err(&pdev->dev, "Failed to request irq %d\n", irq); | ||
398 | goto clk_disable; | ||
399 | } | ||
400 | |||
401 | /* by default the device is on */ | ||
402 | pm_runtime_set_active(&pdev->dev); | ||
403 | pm_runtime_enable(&pdev->dev); | ||
404 | |||
405 | platform_set_drvdata(pdev, master); | ||
406 | |||
407 | ret = devm_spi_register_master(&pdev->dev, master); | ||
408 | if (ret) { | ||
409 | dev_err(&pdev->dev, "Failed to register master\n"); | ||
410 | goto clk_disable; | ||
411 | } | ||
412 | |||
413 | return 0; | ||
414 | |||
415 | clk_disable: | ||
416 | spi_st_clk_disable(spi_st); | ||
417 | |||
418 | return ret; | ||
419 | } | ||
420 | |||
421 | static int spi_st_remove(struct platform_device *pdev) | ||
422 | { | ||
423 | struct spi_master *master = platform_get_drvdata(pdev); | ||
424 | struct spi_st *spi_st = spi_master_get_devdata(master); | ||
425 | |||
426 | spi_st_clk_disable(spi_st); | ||
427 | |||
428 | pinctrl_pm_select_sleep_state(&pdev->dev); | ||
429 | |||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | #ifdef CONFIG_PM | ||
434 | static int spi_st_runtime_suspend(struct device *dev) | ||
435 | { | ||
436 | struct spi_master *master = dev_get_drvdata(dev); | ||
437 | struct spi_st *spi_st = spi_master_get_devdata(master); | ||
438 | |||
439 | writel_relaxed(0, spi_st->base + SSC_IEN); | ||
440 | pinctrl_pm_select_sleep_state(dev); | ||
441 | |||
442 | spi_st_clk_disable(spi_st); | ||
443 | |||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | static int spi_st_runtime_resume(struct device *dev) | ||
448 | { | ||
449 | struct spi_master *master = dev_get_drvdata(dev); | ||
450 | struct spi_st *spi_st = spi_master_get_devdata(master); | ||
451 | int ret; | ||
452 | |||
453 | ret = spi_st_clk_enable(spi_st); | ||
454 | pinctrl_pm_select_default_state(dev); | ||
455 | |||
456 | return ret; | ||
457 | } | ||
458 | #endif | ||
459 | |||
460 | #ifdef CONFIG_PM_SLEEP | ||
461 | static int spi_st_suspend(struct device *dev) | ||
462 | { | ||
463 | struct spi_master *master = dev_get_drvdata(dev); | ||
464 | int ret; | ||
465 | |||
466 | ret = spi_master_suspend(master); | ||
467 | if (ret) | ||
468 | return ret; | ||
469 | |||
470 | return pm_runtime_force_suspend(dev); | ||
471 | } | ||
472 | |||
473 | static int spi_st_resume(struct device *dev) | ||
474 | { | ||
475 | struct spi_master *master = dev_get_drvdata(dev); | ||
476 | int ret; | ||
477 | |||
478 | ret = spi_master_resume(master); | ||
479 | if (ret) | ||
480 | return ret; | ||
481 | |||
482 | return pm_runtime_force_resume(dev); | ||
483 | } | ||
484 | #endif | ||
485 | |||
486 | static const struct dev_pm_ops spi_st_pm = { | ||
487 | SET_SYSTEM_SLEEP_PM_OPS(spi_st_suspend, spi_st_resume) | ||
488 | SET_RUNTIME_PM_OPS(spi_st_runtime_suspend, spi_st_runtime_resume, NULL) | ||
489 | }; | ||
490 | |||
491 | static struct of_device_id stm_spi_match[] = { | ||
492 | { .compatible = "st,comms-ssc4-spi", }, | ||
493 | {}, | ||
494 | }; | ||
495 | MODULE_DEVICE_TABLE(of, stm_spi_match); | ||
496 | |||
497 | static struct platform_driver spi_st_driver = { | ||
498 | .driver = { | ||
499 | .name = "spi-st", | ||
500 | .pm = &spi_st_pm, | ||
501 | .of_match_table = of_match_ptr(stm_spi_match), | ||
502 | }, | ||
503 | .probe = spi_st_probe, | ||
504 | .remove = spi_st_remove, | ||
505 | }; | ||
506 | module_platform_driver(spi_st_driver); | ||
507 | |||
508 | MODULE_AUTHOR("Patrice Chotard <patrice.chotard@st.com>"); | ||
509 | MODULE_DESCRIPTION("STM SSC SPI driver"); | ||
510 | MODULE_LICENSE("GPL v2"); | ||