aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJonas Gorski <jogo@openwrt.org>2013-11-30 06:42:06 -0500
committerMark Brown <broonie@linaro.org>2013-12-04 08:29:13 -0500
commit142168eba9dc5c20538a67049ad53c49bc6f8336 (patch)
treebf7db5d22abc27edbac18b5cd20e3c9c7316f15c /drivers
parent6ce4eac1f600b34f2f7f58f9cd8f0503d79e42ae (diff)
spi: bcm63xx-hsspi: add bcm63xx HSSPI driver
Add a driver for the High Speed SPI controller found on newer BCM63XX SoCs. It does feature some new modes like 3-wire or dual spi, but neither of it is currently implemented. Signed-off-by: Jonas Gorski <jogo@openwrt.org> Signed-off-by: Mark Brown <broonie@linaro.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/spi/Kconfig7
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi-bcm63xx-hsspi.c484
3 files changed, 492 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index eb1f1ef5fa2e..62ce2a9ec612 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -118,6 +118,13 @@ config SPI_BCM63XX
118 help 118 help
119 Enable support for the SPI controller on the Broadcom BCM63xx SoCs. 119 Enable support for the SPI controller on the Broadcom BCM63xx SoCs.
120 120
121config SPI_BCM63XX_HSSPI
122 tristate "Broadcom BCM63XX HS SPI controller driver"
123 depends on BCM63XX || COMPILE_TEST
124 help
125 This enables support for the High Speed SPI controller present on
126 newer Broadcom BCM63XX SoCs.
127
121config SPI_BITBANG 128config SPI_BITBANG
122 tristate "Utilities for Bitbanging SPI masters" 129 tristate "Utilities for Bitbanging SPI masters"
123 help 130 help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index ab8d8644af0e..95af48d2d360 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
16obj-$(CONFIG_SPI_AU1550) += spi-au1550.o 16obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
17obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o 17obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o
18obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o 18obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o
19obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o
19obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o 20obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o
20obj-$(CONFIG_SPI_BFIN_V3) += spi-bfin-v3.o 21obj-$(CONFIG_SPI_BFIN_V3) += spi-bfin-v3.o
21obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o 22obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o
diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c
new file mode 100644
index 000000000000..bc8d848d33b7
--- /dev/null
+++ b/drivers/spi/spi-bcm63xx-hsspi.c
@@ -0,0 +1,484 @@
1/*
2 * Broadcom BCM63XX High Speed SPI Controller driver
3 *
4 * Copyright 2000-2010 Broadcom Corporation
5 * Copyright 2012-2013 Jonas Gorski <jogo@openwrt.org>
6 *
7 * Licensed under the GNU/GPL. See COPYING for details.
8 */
9
10#include <linux/kernel.h>
11#include <linux/init.h>
12#include <linux/io.h>
13#include <linux/clk.h>
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/delay.h>
17#include <linux/dma-mapping.h>
18#include <linux/err.h>
19#include <linux/interrupt.h>
20#include <linux/spi/spi.h>
21#include <linux/workqueue.h>
22#include <linux/mutex.h>
23
24#define HSSPI_GLOBAL_CTRL_REG 0x0
25#define GLOBAL_CTRL_CS_POLARITY_SHIFT 0
26#define GLOBAL_CTRL_CS_POLARITY_MASK 0x000000ff
27#define GLOBAL_CTRL_PLL_CLK_CTRL_SHIFT 8
28#define GLOBAL_CTRL_PLL_CLK_CTRL_MASK 0x0000ff00
29#define GLOBAL_CTRL_CLK_GATE_SSOFF BIT(16)
30#define GLOBAL_CTRL_CLK_POLARITY BIT(17)
31#define GLOBAL_CTRL_MOSI_IDLE BIT(18)
32
33#define HSSPI_GLOBAL_EXT_TRIGGER_REG 0x4
34
35#define HSSPI_INT_STATUS_REG 0x8
36#define HSSPI_INT_STATUS_MASKED_REG 0xc
37#define HSSPI_INT_MASK_REG 0x10
38
39#define HSSPI_PINGx_CMD_DONE(i) BIT((i * 8) + 0)
40#define HSSPI_PINGx_RX_OVER(i) BIT((i * 8) + 1)
41#define HSSPI_PINGx_TX_UNDER(i) BIT((i * 8) + 2)
42#define HSSPI_PINGx_POLL_TIMEOUT(i) BIT((i * 8) + 3)
43#define HSSPI_PINGx_CTRL_INVAL(i) BIT((i * 8) + 4)
44
45#define HSSPI_INT_CLEAR_ALL 0xff001f1f
46
47#define HSSPI_PINGPONG_COMMAND_REG(x) (0x80 + (x) * 0x40)
48#define PINGPONG_CMD_COMMAND_MASK 0xf
49#define PINGPONG_COMMAND_NOOP 0
50#define PINGPONG_COMMAND_START_NOW 1
51#define PINGPONG_COMMAND_START_TRIGGER 2
52#define PINGPONG_COMMAND_HALT 3
53#define PINGPONG_COMMAND_FLUSH 4
54#define PINGPONG_CMD_PROFILE_SHIFT 8
55#define PINGPONG_CMD_SS_SHIFT 12
56
57#define HSSPI_PINGPONG_STATUS_REG(x) (0x84 + (x) * 0x40)
58
59#define HSSPI_PROFILE_CLK_CTRL_REG(x) (0x100 + (x) * 0x20)
60#define CLK_CTRL_FREQ_CTRL_MASK 0x0000ffff
61#define CLK_CTRL_SPI_CLK_2X_SEL BIT(14)
62#define CLK_CTRL_ACCUM_RST_ON_LOOP BIT(15)
63
64#define HSSPI_PROFILE_SIGNAL_CTRL_REG(x) (0x104 + (x) * 0x20)
65#define SIGNAL_CTRL_LATCH_RISING BIT(12)
66#define SIGNAL_CTRL_LAUNCH_RISING BIT(13)
67#define SIGNAL_CTRL_ASYNC_INPUT_PATH BIT(16)
68
69#define HSSPI_PROFILE_MODE_CTRL_REG(x) (0x108 + (x) * 0x20)
70#define MODE_CTRL_MULTIDATA_RD_STRT_SHIFT 8
71#define MODE_CTRL_MULTIDATA_WR_STRT_SHIFT 12
72#define MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT 16
73#define MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT 18
74#define MODE_CTRL_MODE_3WIRE BIT(20)
75#define MODE_CTRL_PREPENDBYTE_CNT_SHIFT 24
76
77#define HSSPI_FIFO_REG(x) (0x200 + (x) * 0x200)
78
79
80#define HSSPI_OP_CODE_SHIFT 13
81#define HSSPI_OP_SLEEP (0 << HSSPI_OP_CODE_SHIFT)
82#define HSSPI_OP_READ_WRITE (1 << HSSPI_OP_CODE_SHIFT)
83#define HSSPI_OP_WRITE (2 << HSSPI_OP_CODE_SHIFT)
84#define HSSPI_OP_READ (3 << HSSPI_OP_CODE_SHIFT)
85#define HSSPI_OP_SETIRQ (4 << HSSPI_OP_CODE_SHIFT)
86
87#define HSSPI_BUFFER_LEN 512
88#define HSSPI_OPCODE_LEN 2
89
90#define HSSPI_MAX_PREPEND_LEN 15
91
92#define HSSPI_MAX_SYNC_CLOCK 30000000
93
94#define HSSPI_BUS_NUM 1 /* 0 is legacy SPI */
95
96struct bcm63xx_hsspi {
97 struct completion done;
98 struct mutex bus_mutex;
99
100 struct platform_device *pdev;
101 struct clk *clk;
102 void __iomem *regs;
103 u8 __iomem *fifo;
104
105 u32 speed_hz;
106 u8 cs_polarity;
107};
108
109static void bcm63xx_hsspi_set_cs(struct bcm63xx_hsspi *bs, unsigned cs,
110 bool active)
111{
112 u32 reg;
113
114 mutex_lock(&bs->bus_mutex);
115 reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG);
116
117 reg &= ~BIT(cs);
118 if (active == !(bs->cs_polarity & BIT(cs)))
119 reg |= BIT(cs);
120
121 __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG);
122 mutex_unlock(&bs->bus_mutex);
123}
124
125static void bcm63xx_hsspi_set_clk(struct bcm63xx_hsspi *bs,
126 struct spi_device *spi, int hz)
127{
128 unsigned profile = spi->chip_select;
129 u32 reg;
130
131 reg = DIV_ROUND_UP(2048, DIV_ROUND_UP(bs->speed_hz, hz));
132 __raw_writel(CLK_CTRL_ACCUM_RST_ON_LOOP | reg,
133 bs->regs + HSSPI_PROFILE_CLK_CTRL_REG(profile));
134
135 reg = __raw_readl(bs->regs + HSSPI_PROFILE_SIGNAL_CTRL_REG(profile));
136 if (hz > HSSPI_MAX_SYNC_CLOCK)
137 reg |= SIGNAL_CTRL_ASYNC_INPUT_PATH;
138 else
139 reg &= ~SIGNAL_CTRL_ASYNC_INPUT_PATH;
140 __raw_writel(reg, bs->regs + HSSPI_PROFILE_SIGNAL_CTRL_REG(profile));
141
142 mutex_lock(&bs->bus_mutex);
143 /* setup clock polarity */
144 reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG);
145 reg &= ~GLOBAL_CTRL_CLK_POLARITY;
146 if (spi->mode & SPI_CPOL)
147 reg |= GLOBAL_CTRL_CLK_POLARITY;
148 __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG);
149 mutex_unlock(&bs->bus_mutex);
150}
151
152static int bcm63xx_hsspi_do_txrx(struct spi_device *spi, struct spi_transfer *t)
153{
154 struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master);
155 unsigned chip_select = spi->chip_select;
156 u16 opcode = 0;
157 int pending = t->len;
158 int step_size = HSSPI_BUFFER_LEN;
159 const u8 *tx = t->tx_buf;
160 u8 *rx = t->rx_buf;
161
162 bcm63xx_hsspi_set_clk(bs, spi, t->speed_hz);
163 bcm63xx_hsspi_set_cs(bs, spi->chip_select, true);
164
165 if (tx && rx)
166 opcode = HSSPI_OP_READ_WRITE;
167 else if (tx)
168 opcode = HSSPI_OP_WRITE;
169 else if (rx)
170 opcode = HSSPI_OP_READ;
171
172 if (opcode != HSSPI_OP_READ)
173 step_size -= HSSPI_OPCODE_LEN;
174
175 __raw_writel(0 << MODE_CTRL_PREPENDBYTE_CNT_SHIFT |
176 2 << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT |
177 2 << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT | 0xff,
178 bs->regs + HSSPI_PROFILE_MODE_CTRL_REG(chip_select));
179
180 while (pending > 0) {
181 int curr_step = min_t(int, step_size, pending);
182
183 init_completion(&bs->done);
184 if (tx) {
185 memcpy_toio(bs->fifo + HSSPI_OPCODE_LEN, tx, curr_step);
186 tx += curr_step;
187 }
188
189 __raw_writew(opcode | curr_step, bs->fifo);
190
191 /* enable interrupt */
192 __raw_writel(HSSPI_PINGx_CMD_DONE(0),
193 bs->regs + HSSPI_INT_MASK_REG);
194
195 /* start the transfer */
196 __raw_writel(!chip_select << PINGPONG_CMD_SS_SHIFT |
197 chip_select << PINGPONG_CMD_PROFILE_SHIFT |
198 PINGPONG_COMMAND_START_NOW,
199 bs->regs + HSSPI_PINGPONG_COMMAND_REG(0));
200
201 if (wait_for_completion_timeout(&bs->done, HZ) == 0) {
202 dev_err(&bs->pdev->dev, "transfer timed out!\n");
203 return -ETIMEDOUT;
204 }
205
206 if (rx) {
207 memcpy_fromio(rx, bs->fifo, curr_step);
208 rx += curr_step;
209 }
210
211 pending -= curr_step;
212 }
213
214 return 0;
215}
216
217static int bcm63xx_hsspi_setup(struct spi_device *spi)
218{
219 struct bcm63xx_hsspi *bs = spi_master_get_devdata(spi->master);
220 u32 reg;
221
222 reg = __raw_readl(bs->regs +
223 HSSPI_PROFILE_SIGNAL_CTRL_REG(spi->chip_select));
224 reg &= ~(SIGNAL_CTRL_LAUNCH_RISING | SIGNAL_CTRL_LATCH_RISING);
225 if (spi->mode & SPI_CPHA)
226 reg |= SIGNAL_CTRL_LAUNCH_RISING;
227 else
228 reg |= SIGNAL_CTRL_LATCH_RISING;
229 __raw_writel(reg, bs->regs +
230 HSSPI_PROFILE_SIGNAL_CTRL_REG(spi->chip_select));
231
232 mutex_lock(&bs->bus_mutex);
233 reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG);
234
235 /* only change actual polarities if there is no transfer */
236 if ((reg & GLOBAL_CTRL_CS_POLARITY_MASK) == bs->cs_polarity) {
237 if (spi->mode & SPI_CS_HIGH)
238 reg |= BIT(spi->chip_select);
239 else
240 reg &= ~BIT(spi->chip_select);
241 __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG);
242 }
243
244 if (spi->mode & SPI_CS_HIGH)
245 bs->cs_polarity |= BIT(spi->chip_select);
246 else
247 bs->cs_polarity &= ~BIT(spi->chip_select);
248
249 mutex_unlock(&bs->bus_mutex);
250
251 return 0;
252}
253
254static int bcm63xx_hsspi_transfer_one(struct spi_master *master,
255 struct spi_message *msg)
256{
257 struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
258 struct spi_transfer *t;
259 struct spi_device *spi = msg->spi;
260 int status = -EINVAL;
261 int dummy_cs;
262 u32 reg;
263
264 /* This controller does not support keeping CS active during idle.
265 * To work around this, we use the following ugly hack:
266 *
267 * a. Invert the target chip select's polarity so it will be active.
268 * b. Select a "dummy" chip select to use as the hardware target.
269 * c. Invert the dummy chip select's polarity so it will be inactive
270 * during the actual transfers.
271 * d. Tell the hardware to send to the dummy chip select. Thanks to
272 * the multiplexed nature of SPI the actual target will receive
273 * the transfer and we see its response.
274 *
275 * e. At the end restore the polarities again to their default values.
276 */
277
278 dummy_cs = !spi->chip_select;
279 bcm63xx_hsspi_set_cs(bs, dummy_cs, true);
280
281 list_for_each_entry(t, &msg->transfers, transfer_list) {
282 status = bcm63xx_hsspi_do_txrx(spi, t);
283 if (status)
284 break;
285
286 msg->actual_length += t->len;
287
288 if (t->delay_usecs)
289 udelay(t->delay_usecs);
290
291 if (t->cs_change)
292 bcm63xx_hsspi_set_cs(bs, spi->chip_select, false);
293 }
294
295 mutex_lock(&bs->bus_mutex);
296 reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG);
297 reg &= ~GLOBAL_CTRL_CS_POLARITY_MASK;
298 reg |= bs->cs_polarity;
299 __raw_writel(reg, bs->regs + HSSPI_GLOBAL_CTRL_REG);
300 mutex_unlock(&bs->bus_mutex);
301
302 msg->status = status;
303 spi_finalize_current_message(master);
304
305 return 0;
306}
307
308static irqreturn_t bcm63xx_hsspi_interrupt(int irq, void *dev_id)
309{
310 struct bcm63xx_hsspi *bs = (struct bcm63xx_hsspi *)dev_id;
311
312 if (__raw_readl(bs->regs + HSSPI_INT_STATUS_MASKED_REG) == 0)
313 return IRQ_NONE;
314
315 __raw_writel(HSSPI_INT_CLEAR_ALL, bs->regs + HSSPI_INT_STATUS_REG);
316 __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG);
317
318 complete(&bs->done);
319
320 return IRQ_HANDLED;
321}
322
323static int bcm63xx_hsspi_probe(struct platform_device *pdev)
324{
325 struct spi_master *master;
326 struct bcm63xx_hsspi *bs;
327 struct resource *res_mem;
328 void __iomem *regs;
329 struct device *dev = &pdev->dev;
330 struct clk *clk;
331 int irq, ret;
332 u32 reg, rate;
333
334 irq = platform_get_irq(pdev, 0);
335 if (irq < 0) {
336 dev_err(dev, "no irq\n");
337 return -ENXIO;
338 }
339
340 res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
341 regs = devm_request_and_ioremap(dev, res_mem);
342 if (IS_ERR(regs))
343 return PTR_ERR(regs);
344
345 clk = clk_get(dev, "hsspi");
346
347 if (IS_ERR(clk))
348 return PTR_ERR(clk);
349
350 rate = clk_get_rate(clk);
351 if (!rate) {
352 ret = -EINVAL;
353 goto out_put_clk;
354 }
355
356 clk_prepare_enable(clk);
357
358 master = spi_alloc_master(&pdev->dev, sizeof(*bs));
359 if (!master) {
360 ret = -ENOMEM;
361 goto out_disable_clk;
362 }
363
364 bs = spi_master_get_devdata(master);
365 bs->pdev = pdev;
366 bs->clk = clk;
367 bs->regs = regs;
368 bs->speed_hz = rate;
369 bs->fifo = (u8 __iomem *)(bs->regs + HSSPI_FIFO_REG(0));
370
371 mutex_init(&bs->bus_mutex);
372
373 master->bus_num = HSSPI_BUS_NUM;
374 master->num_chipselect = 8;
375 master->setup = bcm63xx_hsspi_setup;
376 master->transfer_one_message = bcm63xx_hsspi_transfer_one;
377 master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
378 master->bits_per_word_mask = SPI_BPW_MASK(8);
379 master->auto_runtime_pm = true;
380
381 platform_set_drvdata(pdev, master);
382
383 /* Initialize the hardware */
384 __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG);
385
386 /* clean up any pending interrupts */
387 __raw_writel(HSSPI_INT_CLEAR_ALL, bs->regs + HSSPI_INT_STATUS_REG);
388
389 /* read out default CS polarities */
390 reg = __raw_readl(bs->regs + HSSPI_GLOBAL_CTRL_REG);
391 bs->cs_polarity = reg & GLOBAL_CTRL_CS_POLARITY_MASK;
392 __raw_writel(reg | GLOBAL_CTRL_CLK_GATE_SSOFF,
393 bs->regs + HSSPI_GLOBAL_CTRL_REG);
394
395 ret = devm_request_irq(dev, irq, bcm63xx_hsspi_interrupt, IRQF_SHARED,
396 pdev->name, bs);
397
398 if (ret)
399 goto out_put_master;
400
401 /* register and we are done */
402 ret = spi_register_master(master);
403 if (ret)
404 goto out_put_master;
405
406 return 0;
407
408out_put_master:
409 spi_master_put(master);
410out_disable_clk:
411 clk_disable_unprepare(clk);
412out_put_clk:
413 clk_put(clk);
414
415 return ret;
416}
417
418
419static int bcm63xx_hsspi_remove(struct platform_device *pdev)
420{
421 struct spi_master *master = platform_get_drvdata(pdev);
422 struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
423
424 spi_unregister_master(master);
425
426 /* reset the hardware and block queue progress */
427 __raw_writel(0, bs->regs + HSSPI_INT_MASK_REG);
428 clk_disable_unprepare(bs->clk);
429 clk_put(bs->clk);
430
431 return 0;
432}
433
434#ifdef CONFIG_PM
435static int bcm63xx_hsspi_suspend(struct device *dev)
436{
437 struct spi_master *master = dev_get_drvdata(dev);
438 struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
439
440 spi_master_suspend(master);
441 clk_disable(bs->clk);
442
443 return 0;
444}
445
446static int bcm63xx_hsspi_resume(struct device *dev)
447{
448 struct spi_master *master = dev_get_drvdata(dev);
449 struct bcm63xx_hsspi *bs = spi_master_get_devdata(master);
450
451 clk_enable(bs->clk);
452 spi_master_resume(master);
453
454 return 0;
455}
456
457static const struct dev_pm_ops bcm63xx_hsspi_pm_ops = {
458 .suspend = bcm63xx_hsspi_suspend,
459 .resume = bcm63xx_hsspi_resume,
460};
461
462#define BCM63XX_HSSPI_PM_OPS (&bcm63xx_hsspi_pm_ops)
463#else
464#define BCM63XX_HSSPI_PM_OPS NULL
465#endif
466
467
468
469static struct platform_driver bcm63xx_hsspi_driver = {
470 .driver = {
471 .name = "bcm63xx-hsspi",
472 .owner = THIS_MODULE,
473 .pm = BCM63XX_HSSPI_PM_OPS,
474 },
475 .probe = bcm63xx_hsspi_probe,
476 .remove = bcm63xx_hsspi_remove,
477};
478
479module_platform_driver(bcm63xx_hsspi_driver);
480
481MODULE_ALIAS("platform:bcm63xx_hsspi");
482MODULE_DESCRIPTION("Broadcom BCM63xx High Speed SPI Controller driver");
483MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
484MODULE_LICENSE("GPL");