aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGao Pan <pandy.gao@nxp.com>2016-11-22 08:52:17 -0500
committerMark Brown <broonie@kernel.org>2016-11-22 14:13:16 -0500
commit5314987de5e5f5e38436ef4a69328bc472bbd63e (patch)
treef56b1f5c10fb2dce70a3544ae7b7709ed4860e18
parent1001354ca34179f3db924eb66672442a173147dc (diff)
spi: imx: add lpspi bus driver
This patch adds lpspi driver to support new i.MX products which use lpspi instead of ecspi. The lpspi can continue operating in stop mode when an appropriate clock is available. It is also designed for low CPU overhead with DMA offloading of FIFO register accesses. Signed-off-by: Gao Pan <pandy.gao@nxp.com> Reviewed-by: Fugang Duan <B38611@freescale.com> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--drivers/spi/Kconfig6
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi-fsl-lpspi.c512
3 files changed, 519 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b7995474148c..91ae18035b6b 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -264,6 +264,12 @@ config SPI_FALCON
264 has only been tested with m25p80 type chips. The hardware has no 264 has only been tested with m25p80 type chips. The hardware has no
265 support for other types of SPI peripherals. 265 support for other types of SPI peripherals.
266 266
267config SPI_FSL_LPSPI
268 tristate "Freescale i.MX LPSPI controller"
269 depends on ARCH_MXC || COMPILE_TEST
270 help
271 This enables Freescale i.MX LPSPI controllers in master mode.
272
267config SPI_GPIO 273config SPI_GPIO
268 tristate "GPIO-based bitbanging SPI Master" 274 tristate "GPIO-based bitbanging SPI Master"
269 depends on GPIOLIB || COMPILE_TEST 275 depends on GPIOLIB || COMPILE_TEST
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index aa939d955521..1e890d98db7a 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_SPI_FSL_CPM) += spi-fsl-cpm.o
43obj-$(CONFIG_SPI_FSL_DSPI) += spi-fsl-dspi.o 43obj-$(CONFIG_SPI_FSL_DSPI) += spi-fsl-dspi.o
44obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o 44obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o
45obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o 45obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o
46obj-$(CONFIG_SPI_FSL_LPSPI) += spi-fsl-lpspi.o
46obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o 47obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o
47obj-$(CONFIG_SPI_GPIO) += spi-gpio.o 48obj-$(CONFIG_SPI_GPIO) += spi-gpio.o
48obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o 49obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o
diff --git a/drivers/spi/spi-fsl-lpspi.c b/drivers/spi/spi-fsl-lpspi.c
new file mode 100644
index 000000000000..bcb7b284d2f2
--- /dev/null
+++ b/drivers/spi/spi-fsl-lpspi.c
@@ -0,0 +1,512 @@
1/*
2 * Freescale i.MX7ULP LPSPI driver
3 *
4 * Copyright 2016 Freescale Semiconductor, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 */
17
18#include <linux/clk.h>
19#include <linux/completion.h>
20#include <linux/delay.h>
21#include <linux/err.h>
22#include <linux/interrupt.h>
23#include <linux/io.h>
24#include <linux/irq.h>
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/of.h>
28#include <linux/of_device.h>
29#include <linux/platform_device.h>
30#include <linux/slab.h>
31#include <linux/spi/spi.h>
32#include <linux/spi/spi_bitbang.h>
33#include <linux/types.h>
34
35#define DRIVER_NAME "fsl_lpspi"
36
37/* i.MX7ULP LPSPI registers */
38#define IMX7ULP_VERID 0x0
39#define IMX7ULP_PARAM 0x4
40#define IMX7ULP_CR 0x10
41#define IMX7ULP_SR 0x14
42#define IMX7ULP_IER 0x18
43#define IMX7ULP_DER 0x1c
44#define IMX7ULP_CFGR0 0x20
45#define IMX7ULP_CFGR1 0x24
46#define IMX7ULP_DMR0 0x30
47#define IMX7ULP_DMR1 0x34
48#define IMX7ULP_CCR 0x40
49#define IMX7ULP_FCR 0x58
50#define IMX7ULP_FSR 0x5c
51#define IMX7ULP_TCR 0x60
52#define IMX7ULP_TDR 0x64
53#define IMX7ULP_RSR 0x70
54#define IMX7ULP_RDR 0x74
55
56/* General control register field define */
57#define CR_RRF BIT(9)
58#define CR_RTF BIT(8)
59#define CR_RST BIT(1)
60#define CR_MEN BIT(0)
61#define SR_TCF BIT(10)
62#define SR_RDF BIT(1)
63#define SR_TDF BIT(0)
64#define IER_TCIE BIT(10)
65#define IER_RDIE BIT(1)
66#define IER_TDIE BIT(0)
67#define CFGR1_PCSCFG BIT(27)
68#define CFGR1_PCSPOL BIT(8)
69#define CFGR1_NOSTALL BIT(3)
70#define CFGR1_MASTER BIT(0)
71#define RSR_RXEMPTY BIT(1)
72#define TCR_CPOL BIT(31)
73#define TCR_CPHA BIT(30)
74#define TCR_CONT BIT(21)
75#define TCR_CONTC BIT(20)
76#define TCR_RXMSK BIT(19)
77#define TCR_TXMSK BIT(18)
78
79static int clkdivs[] = {1, 2, 4, 8, 16, 32, 64, 128};
80
81struct lpspi_config {
82 u8 bpw;
83 u8 chip_select;
84 u8 prescale;
85 u16 mode;
86 u32 speed_hz;
87};
88
89struct fsl_lpspi_data {
90 struct device *dev;
91 void __iomem *base;
92 struct clk *clk;
93
94 void *rx_buf;
95 const void *tx_buf;
96 void (*tx)(struct fsl_lpspi_data *);
97 void (*rx)(struct fsl_lpspi_data *);
98
99 u32 remain;
100 u8 txfifosize;
101 u8 rxfifosize;
102
103 struct lpspi_config config;
104 struct completion xfer_done;
105};
106
107static const struct of_device_id fsl_lpspi_dt_ids[] = {
108 { .compatible = "fsl,imx7ulp-spi", },
109 { /* sentinel */ }
110};
111MODULE_DEVICE_TABLE(of, fsl_lpspi_dt_ids);
112
113#define LPSPI_BUF_RX(type) \
114static void fsl_lpspi_buf_rx_##type(struct fsl_lpspi_data *fsl_lpspi) \
115{ \
116 unsigned int val = readl(fsl_lpspi->base + IMX7ULP_RDR); \
117 \
118 if (fsl_lpspi->rx_buf) { \
119 *(type *)fsl_lpspi->rx_buf = val; \
120 fsl_lpspi->rx_buf += sizeof(type); \
121 } \
122}
123
124#define LPSPI_BUF_TX(type) \
125static void fsl_lpspi_buf_tx_##type(struct fsl_lpspi_data *fsl_lpspi) \
126{ \
127 type val = 0; \
128 \
129 if (fsl_lpspi->tx_buf) { \
130 val = *(type *)fsl_lpspi->tx_buf; \
131 fsl_lpspi->tx_buf += sizeof(type); \
132 } \
133 \
134 fsl_lpspi->remain -= sizeof(type); \
135 writel(val, fsl_lpspi->base + IMX7ULP_TDR); \
136}
137
138LPSPI_BUF_RX(u8)
139LPSPI_BUF_TX(u8)
140LPSPI_BUF_RX(u16)
141LPSPI_BUF_TX(u16)
142LPSPI_BUF_RX(u32)
143LPSPI_BUF_TX(u32)
144
145static void fsl_lpspi_intctrl(struct fsl_lpspi_data *fsl_lpspi,
146 unsigned int enable)
147{
148 writel(enable, fsl_lpspi->base + IMX7ULP_IER);
149}
150
151static int fsl_lpspi_prepare_message(struct spi_master *master,
152 struct spi_message *msg)
153{
154 struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master);
155
156 return clk_prepare_enable(fsl_lpspi->clk);
157}
158
159static int
160fsl_lpspi_unprepare_message(struct spi_master *master, struct spi_message *msg)
161{
162 struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master);
163
164 clk_disable_unprepare(fsl_lpspi->clk);
165
166 return 0;
167}
168
169static int fsl_lpspi_txfifo_empty(struct fsl_lpspi_data *fsl_lpspi)
170{
171 u32 txcnt;
172 unsigned long orig_jiffies = jiffies;
173
174 do {
175 txcnt = readl(fsl_lpspi->base + IMX7ULP_FSR) & 0xff;
176
177 if (time_after(jiffies, orig_jiffies + msecs_to_jiffies(500))) {
178 dev_dbg(fsl_lpspi->dev, "txfifo empty timeout\n");
179 return -ETIMEDOUT;
180 }
181 schedule();
182
183 } while (txcnt);
184
185 return 0;
186}
187
188static void fsl_lpspi_write_tx_fifo(struct fsl_lpspi_data *fsl_lpspi)
189{
190 u8 txfifo_cnt;
191
192 txfifo_cnt = readl(fsl_lpspi->base + IMX7ULP_FSR) & 0xff;
193
194 while (txfifo_cnt < fsl_lpspi->txfifosize) {
195 if (!fsl_lpspi->remain)
196 break;
197 fsl_lpspi->tx(fsl_lpspi);
198 txfifo_cnt++;
199 }
200
201 if (!fsl_lpspi->remain && (txfifo_cnt < fsl_lpspi->txfifosize))
202 writel(0, fsl_lpspi->base + IMX7ULP_TDR);
203 else
204 fsl_lpspi_intctrl(fsl_lpspi, IER_TDIE);
205}
206
207static void fsl_lpspi_read_rx_fifo(struct fsl_lpspi_data *fsl_lpspi)
208{
209 while (!(readl(fsl_lpspi->base + IMX7ULP_RSR) & RSR_RXEMPTY))
210 fsl_lpspi->rx(fsl_lpspi);
211}
212
213static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi,
214 bool is_first_xfer)
215{
216 u32 temp = 0;
217
218 temp |= fsl_lpspi->config.bpw - 1;
219 temp |= fsl_lpspi->config.prescale << 27;
220 temp |= (fsl_lpspi->config.mode & 0x11) << 30;
221 temp |= (fsl_lpspi->config.chip_select & 0x3) << 24;
222
223 /*
224 * Set TCR_CONT will keep SS asserted after current transfer.
225 * For the first transfer, clear TCR_CONTC to assert SS.
226 * For subsequent transfer, set TCR_CONTC to keep SS asserted.
227 */
228 temp |= TCR_CONT;
229 if (is_first_xfer)
230 temp &= ~TCR_CONTC;
231 else
232 temp |= TCR_CONTC;
233
234 writel(temp, fsl_lpspi->base + IMX7ULP_TCR);
235
236 dev_dbg(fsl_lpspi->dev, "TCR=0x%x\n", temp);
237}
238
239static void fsl_lpspi_set_watermark(struct fsl_lpspi_data *fsl_lpspi)
240{
241 u8 txwatermark, rxwatermark;
242 u32 temp;
243
244 temp = readl(fsl_lpspi->base + IMX7ULP_PARAM);
245 fsl_lpspi->txfifosize = 1 << (temp & 0x0f);
246 fsl_lpspi->rxfifosize = 1 << ((temp >> 8) & 0x0f);
247 rxwatermark = fsl_lpspi->txfifosize >> 1;
248 txwatermark = fsl_lpspi->rxfifosize >> 1;
249 temp = txwatermark | rxwatermark << 16;
250
251 writel(temp, fsl_lpspi->base + IMX7ULP_FCR);
252
253 dev_dbg(fsl_lpspi->dev, "FCR=0x%x\n", temp);
254}
255
256static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi)
257{
258 struct lpspi_config config = fsl_lpspi->config;
259 unsigned int perclk_rate, scldiv;
260 u8 prescale;
261
262 perclk_rate = clk_get_rate(fsl_lpspi->clk);
263 for (prescale = 0; prescale < 8; prescale++) {
264 scldiv = perclk_rate /
265 (clkdivs[prescale] * config.speed_hz) - 2;
266 if (scldiv < 256) {
267 fsl_lpspi->config.prescale = prescale;
268 break;
269 }
270 }
271
272 if (prescale == 8 && scldiv >= 256)
273 return -EINVAL;
274
275 writel(scldiv, fsl_lpspi->base + IMX7ULP_CCR);
276
277 dev_dbg(fsl_lpspi->dev, "perclk=%d, speed=%d, prescale =%d, scldiv=%d\n",
278 perclk_rate, config.speed_hz, prescale, scldiv);
279
280 return 0;
281}
282
283static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi)
284{
285 u32 temp;
286 int ret;
287
288 temp = CR_RST;
289 writel(temp, fsl_lpspi->base + IMX7ULP_CR);
290 writel(0, fsl_lpspi->base + IMX7ULP_CR);
291
292 ret = fsl_lpspi_set_bitrate(fsl_lpspi);
293 if (ret)
294 return ret;
295
296 fsl_lpspi_set_watermark(fsl_lpspi);
297
298 temp = CFGR1_PCSCFG | CFGR1_MASTER | CFGR1_NOSTALL;
299 if (fsl_lpspi->config.mode & SPI_CS_HIGH)
300 temp |= CFGR1_PCSPOL;
301 writel(temp, fsl_lpspi->base + IMX7ULP_CFGR1);
302
303 temp = readl(fsl_lpspi->base + IMX7ULP_CR);
304 temp |= CR_RRF | CR_RTF | CR_MEN;
305 writel(temp, fsl_lpspi->base + IMX7ULP_CR);
306
307 return 0;
308}
309
310static void fsl_lpspi_setup_transfer(struct spi_device *spi,
311 struct spi_transfer *t)
312{
313 struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(spi->master);
314
315 fsl_lpspi->config.mode = spi->mode;
316 fsl_lpspi->config.bpw = t ? t->bits_per_word : spi->bits_per_word;
317 fsl_lpspi->config.speed_hz = t ? t->speed_hz : spi->max_speed_hz;
318 fsl_lpspi->config.chip_select = spi->chip_select;
319
320 if (!fsl_lpspi->config.speed_hz)
321 fsl_lpspi->config.speed_hz = spi->max_speed_hz;
322 if (!fsl_lpspi->config.bpw)
323 fsl_lpspi->config.bpw = spi->bits_per_word;
324
325 /* Initialize the functions for transfer */
326 if (fsl_lpspi->config.bpw <= 8) {
327 fsl_lpspi->rx = fsl_lpspi_buf_rx_u8;
328 fsl_lpspi->tx = fsl_lpspi_buf_tx_u8;
329 } else if (fsl_lpspi->config.bpw <= 16) {
330 fsl_lpspi->rx = fsl_lpspi_buf_rx_u16;
331 fsl_lpspi->tx = fsl_lpspi_buf_tx_u16;
332 } else {
333 fsl_lpspi->rx = fsl_lpspi_buf_rx_u32;
334 fsl_lpspi->tx = fsl_lpspi_buf_tx_u32;
335 }
336
337 fsl_lpspi_config(fsl_lpspi);
338}
339
340static int fsl_lpspi_transfer_one(struct spi_master *master,
341 struct spi_device *spi,
342 struct spi_transfer *t)
343{
344 struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master);
345 int ret;
346
347 fsl_lpspi->tx_buf = t->tx_buf;
348 fsl_lpspi->rx_buf = t->rx_buf;
349 fsl_lpspi->remain = t->len;
350
351 reinit_completion(&fsl_lpspi->xfer_done);
352 fsl_lpspi_write_tx_fifo(fsl_lpspi);
353 wait_for_completion(&fsl_lpspi->xfer_done);
354
355 ret = fsl_lpspi_txfifo_empty(fsl_lpspi);
356 fsl_lpspi_read_rx_fifo(fsl_lpspi);
357
358 return ret;
359}
360
361static int fsl_lpspi_transfer_one_msg(struct spi_master *master,
362 struct spi_message *msg)
363{
364 struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master);
365 struct spi_device *spi = msg->spi;
366 struct spi_transfer *xfer;
367 bool is_first_xfer = true;
368 u32 temp;
369 int ret;
370
371 msg->status = 0;
372 msg->actual_length = 0;
373
374 list_for_each_entry(xfer, &msg->transfers, transfer_list) {
375 fsl_lpspi_setup_transfer(spi, xfer);
376 fsl_lpspi_set_cmd(fsl_lpspi, is_first_xfer);
377
378 is_first_xfer = false;
379
380 ret = fsl_lpspi_transfer_one(master, spi, xfer);
381 if (ret < 0)
382 goto complete;
383
384 msg->actual_length += xfer->len;
385 }
386
387complete:
388 /* de-assert SS, then finalize current message */
389 temp = readl(fsl_lpspi->base + IMX7ULP_TCR);
390 temp &= ~TCR_CONTC;
391 writel(temp, fsl_lpspi->base + IMX7ULP_TCR);
392
393 msg->status = ret;
394 spi_finalize_current_message(master);
395
396 return ret;
397}
398
399static irqreturn_t fsl_lpspi_isr(int irq, void *dev_id)
400{
401 struct fsl_lpspi_data *fsl_lpspi = dev_id;
402 u32 temp;
403
404 fsl_lpspi_intctrl(fsl_lpspi, 0);
405 temp = readl(fsl_lpspi->base + IMX7ULP_SR);
406
407 fsl_lpspi_read_rx_fifo(fsl_lpspi);
408
409 if (temp & SR_TDF) {
410 fsl_lpspi_write_tx_fifo(fsl_lpspi);
411
412 if (!fsl_lpspi->remain)
413 complete(&fsl_lpspi->xfer_done);
414
415 return IRQ_HANDLED;
416 }
417
418 return IRQ_NONE;
419}
420
421static int fsl_lpspi_probe(struct platform_device *pdev)
422{
423 struct fsl_lpspi_data *fsl_lpspi;
424 struct spi_master *master;
425 struct resource *res;
426 int ret, irq;
427
428 master = spi_alloc_master(&pdev->dev, sizeof(struct fsl_lpspi_data));
429 if (!master)
430 return -ENOMEM;
431
432 platform_set_drvdata(pdev, master);
433
434 master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32);
435 master->bus_num = pdev->id;
436
437 fsl_lpspi = spi_master_get_devdata(master);
438 fsl_lpspi->dev = &pdev->dev;
439
440 master->transfer_one_message = fsl_lpspi_transfer_one_msg;
441 master->prepare_message = fsl_lpspi_prepare_message;
442 master->unprepare_message = fsl_lpspi_unprepare_message;
443 master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
444 master->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
445 master->dev.of_node = pdev->dev.of_node;
446 master->bus_num = pdev->id;
447
448 init_completion(&fsl_lpspi->xfer_done);
449
450 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
451 fsl_lpspi->base = devm_ioremap_resource(&pdev->dev, res);
452 if (IS_ERR(fsl_lpspi->base)) {
453 ret = PTR_ERR(fsl_lpspi->base);
454 goto out_master_put;
455 }
456
457 irq = platform_get_irq(pdev, 0);
458 if (irq < 0) {
459 ret = irq;
460 goto out_master_put;
461 }
462
463 ret = devm_request_irq(&pdev->dev, irq, fsl_lpspi_isr, 0,
464 dev_name(&pdev->dev), fsl_lpspi);
465 if (ret) {
466 dev_err(&pdev->dev, "can't get irq%d: %d\n", irq, ret);
467 goto out_master_put;
468 }
469
470 fsl_lpspi->clk = devm_clk_get(&pdev->dev, "ipg");
471 if (IS_ERR(fsl_lpspi->clk)) {
472 ret = PTR_ERR(fsl_lpspi->clk);
473 goto out_master_put;
474 }
475
476 ret = devm_spi_register_master(&pdev->dev, master);
477 if (ret < 0) {
478 dev_err(&pdev->dev, "spi_register_master error.\n");
479 goto out_master_put;
480 }
481
482 return 0;
483
484out_master_put:
485 spi_master_put(master);
486
487 return ret;
488}
489
490static int fsl_lpspi_remove(struct platform_device *pdev)
491{
492 struct spi_master *master = platform_get_drvdata(pdev);
493 struct fsl_lpspi_data *fsl_lpspi = spi_master_get_devdata(master);
494
495 clk_disable_unprepare(fsl_lpspi->clk);
496
497 return 0;
498}
499
500static struct platform_driver fsl_lpspi_driver = {
501 .driver = {
502 .name = DRIVER_NAME,
503 .of_match_table = fsl_lpspi_dt_ids,
504 },
505 .probe = fsl_lpspi_probe,
506 .remove = fsl_lpspi_remove,
507};
508module_platform_driver(fsl_lpspi_driver);
509
510MODULE_DESCRIPTION("LPSPI Master Controller driver");
511MODULE_AUTHOR("Gao Pan <pandy.gao@nxp.com>");
512MODULE_LICENSE("GPL v2");