1 From 8e051b79ae3f66dbad96312fe2976401c28d2148 Mon Sep 17 00:00:00 2001
2 From: Jonas Gorski <jogo@openwrt.org>
3 Date: Sat, 12 Nov 2011 12:19:55 +0100
4 Subject: [PATCH 5/5] spi: add bcm63xx HSSPI driver
6 Add a driver for the High Speed SPI controller found on newer BCM63XX SoCs.
8 It does feature some new modes like 3-wire or dual spi, but neither of it
9 is currently implemented.
11 Signed-off-by: Jonas Gorski <jogo@openwrt.org>
13 drivers/spi/Kconfig | 7 +
14 drivers/spi/Makefile | 1 +
15 drivers/spi/spi-bcm63xx-hsspi.c | 484 ++++++++++++++++++++++++++++++++++++++++
16 3 files changed, 492 insertions(+)
17 create mode 100644 drivers/spi/spi-bcm63xx-hsspi.c
19 --- a/drivers/spi/Kconfig
20 +++ b/drivers/spi/Kconfig
21 @@ -118,6 +118,13 @@ config SPI_BCM63XX
23 Enable support for the SPI controller on the Broadcom BCM63xx SoCs.
25 +config SPI_BCM63XX_HSSPI
26 + tristate "Broadcom BCM63XX HS SPI controller driver"
27 + depends on BCM63XX || COMPILE_TEST
29 + This enables support for the High Speed SPI controller present on
30 + newer Broadcom BCM63XX SoCs.
33 tristate "Utilities for Bitbanging SPI masters"
35 --- a/drivers/spi/Makefile
36 +++ b/drivers/spi/Makefile
37 @@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
38 obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
39 obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o
40 obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o
41 +obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o
42 obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o
43 obj-$(CONFIG_SPI_BFIN_V3) += spi-bfin-v3.o
44 obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o
46 +++ b/drivers/spi/spi-bcm63xx-hsspi.c
49 + * Broadcom BCM63XX High Speed SPI Controller driver
51 + * Copyright 2000-2010 Broadcom Corporation
52 + * Copyright 2012-2013 Jonas Gorski <jogo@openwrt.org>
54 + * Licensed under the GNU/GPL. See COPYING for details.
57 +#include <linux/kernel.h>
58 +#include <linux/init.h>
59 +#include <linux/io.h>
60 +#include <linux/clk.h>
61 +#include <linux/module.h>
62 +#include <linux/platform_device.h>
63 +#include <linux/delay.h>
64 +#include <linux/dma-mapping.h>
65 +#include <linux/err.h>
66 +#include <linux/interrupt.h>
67 +#include <linux/spi/spi.h>
68 +#include <linux/workqueue.h>
69 +#include <linux/mutex.h>
71 +#define HSSPI_GLOBAL_CTRL_REG 0x0
72 +#define GLOBAL_CTRL_CS_POLARITY_SHIFT 0
73 +#define GLOBAL_CTRL_CS_POLARITY_MASK 0x000000ff
74 +#define GLOBAL_CTRL_PLL_CLK_CTRL_SHIFT 8
75 +#define GLOBAL_CTRL_PLL_CLK_CTRL_MASK 0x0000ff00
76 +#define GLOBAL_CTRL_CLK_GATE_SSOFF BIT(16)
77 +#define GLOBAL_CTRL_CLK_POLARITY BIT(17)
78 +#define GLOBAL_CTRL_MOSI_IDLE BIT(18)
80 +#define HSSPI_GLOBAL_EXT_TRIGGER_REG 0x4
82 +#define HSSPI_INT_STATUS_REG 0x8
83 +#define HSSPI_INT_STATUS_MASKED_REG 0xc
84 +#define HSSPI_INT_MASK_REG 0x10
86 +#define HSSPI_PINGx_CMD_DONE(i) BIT((i * 8) + 0)
87 +#define HSSPI_PINGx_RX_OVER(i) BIT((i * 8) + 1)
88 +#define HSSPI_PINGx_TX_UNDER(i) BIT((i * 8) + 2)
89 +#define HSSPI_PINGx_POLL_TIMEOUT(i) BIT((i * 8) + 3)
90 +#define HSSPI_PINGx_CTRL_INVAL(i) BIT((i * 8) + 4)
92 +#define HSSPI_INT_CLEAR_ALL 0xff001f1f
94 +#define HSSPI_PINGPONG_COMMAND_REG(x) (0x80 + (x) * 0x40)
95 +#define PINGPONG_CMD_COMMAND_MASK 0xf
96 +#define PINGPONG_COMMAND_NOOP 0
97 +#define PINGPONG_COMMAND_START_NOW 1
98 +#define PINGPONG_COMMAND_START_TRIGGER 2
99 +#define PINGPONG_COMMAND_HALT 3
100 +#define PINGPONG_COMMAND_FLUSH 4
101 +#define PINGPONG_CMD_PROFILE_SHIFT 8
102 +#define PINGPONG_CMD_SS_SHIFT 12
104 +#define HSSPI_PINGPONG_STATUS_REG(x) (0x84 + (x) * 0x40)
106 +#define HSSPI_PROFILE_CLK_CTRL_REG(x) (0x100 + (x) * 0x20)
107 +#define CLK_CTRL_FREQ_CTRL_MASK 0x0000ffff
108 +#define CLK_CTRL_SPI_CLK_2X_SEL BIT(14)
109 +#define CLK_CTRL_ACCUM_RST_ON_LOOP BIT(15)
111 +#define HSSPI_PROFILE_SIGNAL_CTRL_REG(x) (0x104 + (x) * 0x20)
112 +#define SIGNAL_CTRL_LATCH_RISING BIT(12)
113 +#define SIGNAL_CTRL_LAUNCH_RISING BIT(13)
114 +#define SIGNAL_CTRL_ASYNC_INPUT_PATH BIT(16)
116 +#define HSSPI_PROFILE_MODE_CTRL_REG(x) (0x108 + (x) * 0x20)
117 +#define MODE_CTRL_MULTIDATA_RD_STRT_SHIFT 8
118 +#define MODE_CTRL_MULTIDATA_WR_STRT_SHIFT 12
119 +#define MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT 16
120 +#define MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT 18
121 +#define MODE_CTRL_MODE_3WIRE BIT(20)
122 +#define MODE_CTRL_PREPENDBYTE_CNT_SHIFT 24
124 +#define HSSPI_FIFO_REG(x) (0x200 + (x) * 0x200)
127 +#define HSSPI_OP_CODE_SHIFT 13
128 +#define HSSPI_OP_SLEEP (0 << HSSPI_OP_CODE_SHIFT)
129 +#define HSSPI_OP_READ_WRITE (1 << HSSPI_OP_CODE_SHIFT)
130 +#define HSSPI_OP_WRITE (2 << HSSPI_OP_CODE_SHIFT)
131 +#define HSSPI_OP_READ (3 << HSSPI_OP_CODE_SHIFT)
132 +#define HSSPI_OP_SETIRQ (4 << HSSPI_OP_CODE_SHIFT)
134 +#define HSSPI_BUFFER_LEN 512
135 +#define HSSPI_OPCODE_LEN 2
137 +#define HSSPI_MAX_PREPEND_LEN 15
139 +#define HSSPI_MAX_SYNC_CLOCK 30000000
141 +#define HSSPI_BUS_NUM 1 /* 0 is legacy SPI */
143 +struct bcm63xx_hsspi {
144 + struct completion done;
145 + struct mutex bus_mutex;
147 + struct platform_device *pdev;
149 + void __iomem *regs;
156 +static void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned cs,
161 + mutex_lock(&bs->bus_mutex);
162 + reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG);
165 + if (active == !(bs->cs_polarity & BIT(cs)))
168 + __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG);
169 + mutex_unlock(&bs->bus_mutex);
172 +static void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs,
173 + struct spi_device *spi, int hz)
175 + unsigned profile = spi->chip_select;
178 + reg = DIV_ROUND_UP(2048, DIV_ROUND_UP(bs->speed_hz, hz));
179 + __raw_writel(CLK_CTRL_ACCUM_RST_ON_LOOP | reg,
180 + bs->regs + HSSPI_PROFILE_CLK_CTRL_REG(profile));
182 + reg = __raw_readl(bs->regs + HSSPI_PROFILE_SIGNAL_CTRL_REG(profile));
183 + if (hz > HSSPI_MAX_SYNC_CLOCK)
184 + reg |= SIGNAL_CTRL_ASYNC_INPUT_PATH;
186 + reg &= ~SIGNAL_CTRL_ASYNC_INPUT_PATH;
187 + __raw_writel(reg, bs->regs + HSSPI_PROFILE_SIGNAL_CTRL_REG(profile));
189 + mutex_lock(&bs->bus_mutex);
190 + /* setup clock polarity */
191 + reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG);
192 + reg &= ~GLOBAL_CTRL_CLK_POLARITY;
193 + if (spi->mode & SPI_CPOL)
194 + reg |= GLOBAL_CTRL_CLK_POLARITY;
195 + __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG);
196 + mutex_unlock(&bs->bus_mutex);
199 +static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t)
201 + struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master);
202 + unsigned chip_select = spi->chip_select;
204 + int pending = t->len;
205 + int step_size = HSSPI_BUFFER_LEN;
206 + const u8 *tx = t->tx_buf;
207 + u8 *rx = t->rx_buf;
209 + bcm63xx_hsspi_set_clk(bs, spi, t->speed_hz);
210 + bcm63xx_hsspi_set_cs(bs, spi->chip_select, true);
213 + opcode = HSSPI_OP_READ_WRITE;
215 + opcode = HSSPI_OP_WRITE;
217 + opcode = HSSPI_OP_READ;
219 + if (opcode != HSSPI_OP_READ)
220 + step_size -= HSSPI_OPCODE_LEN;
222 + __raw_writel(0 << MODE_CTRL_PREPENDBYTE_CNT_SHIFT |
223 + 2 << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT |
224 + 2 << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT | 0xff,
225 + bs->regs + HSSPI_PROFILE_MODE_CTRL_REG(chip_select));
227 + while (pending > 0) {
228 + int curr_step = min_t(int, step_size, pending);
230 + init_completion(&bs->done);
232 + memcpy_toio(bs->fifo + HSSPI_OPCODE_LEN, tx, curr_step);
236 + __raw_writew(opcode | curr_step, bs->fifo);
238 + /* enable interrupt */
239 + __raw_writel(HSSPI_PINGx_CMD_DONE(0),
240 + bs->regs + HSSPI_INT_MASK_REG);
242 + /* start the transfer */
243 + __raw_writel(!chip_select << PINGPONG_CMD_SS_SHIFT |
244 + chip_select << PINGPONG_CMD_PROFILE_SHIFT |
245 + PINGPONG_COMMAND_START_NOW,
246 + bs->regs + HSSPI_PINGPONG_COMMAND_REG(0));
248 + if (wait_for_completion_timeout(&bs->done, HZ) == 0) {
249 + dev_err(&bs->pdev->dev, "transfer timed out!\n");
254 + memcpy_fromio(rx, bs->fifo, curr_step);
258 + pending -= curr_step;
264 +static int bcm63xx_hsspi_setup(struct spi_device *spi)
266 + struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master);
269 + reg = __raw_readl(bs->regs +
270 + HSSPI_PROFILE_SIGNAL_CTRL_REG(spi->chip_select));
271 + reg &= ~(SIGNAL_CTRL_LAUNCH_RISING | SIGNAL_CTRL_LATCH_RISING);
272 + if (spi->mode & SPI_CPHA)
273 + reg |= SIGNAL_CTRL_LAUNCH_RISING;
275 + reg |= SIGNAL_CTRL_LATCH_RISING;
276 + __raw_writel(reg, bs->regs +
277 + HSSPI_PROFILE_SIGNAL_CTRL_REG(spi->chip_select));
279 + mutex_lock(&bs->bus_mutex);
280 + reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG);
282 + /* only change actual polarities if there is no transfer */
283 + if ((reg & GLOBAL_CTRL_CS_POLARITY_MASK) == bs->cs_polarity) {
284 + if (spi->mode & SPI_CS_HIGH)
285 + reg |= BIT(spi->chip_select);
287 + reg &= ~BIT(spi->chip_select);
288 + __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG);
291 + if (spi->mode & SPI_CS_HIGH)
292 + bs->cs_polarity |= BIT(spi->chip_select);
294 + bs->cs_polarity &= ~BIT(spi->chip_select);
296 + mutex_unlock(&bs->bus_mutex);
301 +static int bcm63xx_hsspi_transfer_one(struct spi_master *master,
302 + struct spi_message *msg)
304 + struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
305 + struct spi_transfer *t;
306 + struct spi_device *spi = msg->spi;
307 + int status = -EINVAL;
311 + /* This controller does not support keeping CS active during idle.
312 + * To work around this, we use the following ugly hack:
314 + * a. Invert the target chip select's polarity so it will be active.
315 + * b. Select a "dummy" chip select to use as the hardware target.
316 + * c. Invert the dummy chip select's polarity so it will be inactive
317 + * during the actual transfers.
318 + * d. Tell the hardware to send to the dummy chip select. Thanks to
319 + * the multiplexed nature of SPI the actual target will receive
320 + * the transfer and we see its response.
322 + * e. At the end restore the polarities again to their default values.
325 + dummy_cs = !spi->chip_select;
326 + bcm63xx_hsspi_set_cs(bs, dummy_cs, true);
328 + list_for_each_entry(t, &msg->transfers, transfer_list) {
329 + status = bcm63xx_hsspi_do_txrx(spi, t);
333 + msg->actual_length += t->len;
335 + if (t->delay_usecs)
336 + udelay(t->delay_usecs);
339 + bcm63xx_hsspi_set_cs(bs, spi->chip_select, false);
342 + mutex_lock(&bs->bus_mutex);
343 + reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG);
344 + reg &= ~GLOBAL_CTRL_CS_POLARITY_MASK;
345 + reg |= bs->cs_polarity;
346 + __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG);
347 + mutex_unlock(&bs->bus_mutex);
349 + msg->status = status;
350 + spi_finalize_current_message(master);
355 +static irqreturn_t bcm63xx_hsspi_interrupt(int irq, void *dev_id)
357 + struct bcm63xx_hsspi *bs = (struct bcm63xx_hsspi *)dev_id;
359 + if (__raw_readl(bs->regs + HSSPI_INT_STATUS_MASKED_REG) == 0)
362 + __raw_writel(HSSPI_INT_CLEAR_ALL, bs->regs + HSSPI_INT_STATUS_REG);
363 + __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG);
365 + complete(&bs->done);
367 + return IRQ_HANDLED;
370 +static int bcm63xx_hsspi_probe(struct platform_device *pdev)
372 + struct spi_master *master;
373 + struct bcm63xx_hsspi *bs;
374 + struct resource *res_mem;
375 + void __iomem *regs;
376 + struct device *dev = &pdev->dev;
381 + irq = platform_get_irq(pdev, 0);
383 + dev_err(dev, "no irq\n");
387 + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
388 + regs = devm_request_and_ioremap(dev, res_mem);
390 + return PTR_ERR(regs);
392 + clk = clk_get(dev, "hsspi");
395 + return PTR_ERR(clk);
397 + rate = clk_get_rate(clk);
403 + clk_prepare_enable(clk);
405 + master = spi_alloc_master(&pdev->dev, sizeof(*bs));
408 + goto out_disable_clk;
411 + bs = spi_master_get_devdata(master);
415 + bs->speed_hz = rate;
416 + bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0));
418 + mutex_init(&bs->bus_mutex);
420 + master->bus_num = HSSPI_BUS_NUM;
421 + master->num_chipselect = 8;
422 + master->setup = bcm63xx_hsspi_setup;
423 + master->transfer_one_message = bcm63xx_hsspi_transfer_one;
424 + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
425 + master->bits_per_word_mask = SPI_BPW_MASK(8);
426 + master->auto_runtime_pm = true;
428 + platform_set_drvdata(pdev, master);
430 + /* Initialize the hardware */
431 + __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG);
433 + /* clean up any pending interrupts */
434 + __raw_writel(HSSPI_INT_CLEAR_ALL, bs->regs + HSSPI_INT_STATUS_REG);
436 + /* read out default CS polarities */
437 + reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG);
438 + bs->cs_polarity = reg & GLOBAL_CTRL_CS_POLARITY_MASK;
439 + __raw_writel(reg | GLOBAL_CTRL_CLK_GATE_SSOFF,
440 + bs->regs + HSSPI_GLOBAL_CTRL_REG);
442 + ret = devm_request_irq(dev, irq, bcm63xx_hsspi_interrupt, IRQF_SHARED,
446 + goto out_put_master;
448 + /* register and we are done */
449 + ret = spi_register_master(master);
451 + goto out_put_master;
456 + spi_master_put(master);
458 + clk_disable_unprepare(clk);
466 +static int bcm63xx_hsspi_remove(struct platform_device *pdev)
468 + struct spi_master *master = platform_get_drvdata(pdev);
469 + struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
471 + spi_unregister_master(master);
473 + /* reset the hardware and block queue progress */
474 + __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG);
475 + clk_disable_unprepare(bs->clk);
482 +static int bcm63xx_hsspi_suspend(struct device *dev)
484 + struct spi_master *master = dev_get_drvdata(dev);
485 + struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
487 + spi_master_suspend(master);
488 + clk_disable(bs->clk);
493 +static int bcm63xx_hsspi_resume(struct device *dev)
495 + struct spi_master *master = dev_get_drvdata(dev);
496 + struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
498 + clk_enable(bs->clk);
499 + spi_master_resume(master);
504 +static const struct dev_pm_ops bcm63xx_hsspi_pm_ops = {
505 + .suspend = bcm63xx_hsspi_suspend,
506 + .resume = bcm63xx_hsspi_resume,
509 +#define BCM63XX_HSSPI_PM_OPS (&bcm63xx_hsspi_pm_ops)
511 +#define BCM63XX_HSSPI_PM_OPS NULL
516 +static struct platform_driver bcm63xx_hsspi_driver = {
518 + .name = "bcm63xx-hsspi",
519 + .owner = THIS_MODULE,
520 + .pm = BCM63XX_HSSPI_PM_OPS,
522 + .probe = bcm63xx_hsspi_probe,
523 + .remove = bcm63xx_hsspi_remove,
526 +module_platform_driver(bcm63xx_hsspi_driver);
528 +MODULE_ALIAS("platform:bcm63xx_hsspi");
529 +MODULE_DESCRIPTION("Broadcom BCM63xx High Speed SPI Controller driver");
530 +MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
531 +MODULE_LICENSE("GPL");