aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorFlorian Fainelli <florian@openwrt.org>2012-02-01 05:14:09 -0500
committerGrant Likely <grant.likely@secretlab.ca>2012-03-09 17:03:03 -0500
commitb42dfed83d95a3c9e9cbd708f1993a7474abb79a (patch)
tree191adabadf1bbb8ce2721df1a079202c4957babe /drivers
parent1cc2df9d6f41b689dc9a562a22de87f860ce6be5 (diff)
spi: add Broadcom BCM63xx SPI controller driver
This patch adds support for the SPI controller found on the Broadcom BCM63xx SoCs. Signed-off-by: Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com> Signed-off-by: Florian Fainelli <florian@openwrt.org> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/spi/Kconfig6
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi-bcm63xx.c486
3 files changed, 493 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 3508648c32a2..3308c12785e6 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -94,6 +94,12 @@ config SPI_AU1550
94 If you say yes to this option, support will be included for the 94 If you say yes to this option, support will be included for the
95 PSC SPI controller found on Au1550, Au1200 and Au1300 series. 95 PSC SPI controller found on Au1550, Au1200 and Au1300 series.
96 96
97config SPI_BCM63XX
98 tristate "Broadcom BCM63xx SPI controller"
99 depends on BCM63XX
100 help
101 Enable support for the SPI controller on the Broadcom BCM63xx SoCs.
102
97config SPI_BITBANG 103config SPI_BITBANG
98 tristate "Utilities for Bitbanging SPI masters" 104 tristate "Utilities for Bitbanging SPI masters"
99 help 105 help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 2e556484345a..a1d48e0ba3dc 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
14obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o 14obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
15obj-$(CONFIG_SPI_ATH79) += spi-ath79.o 15obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
16obj-$(CONFIG_SPI_AU1550) += spi-au1550.o 16obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
17obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o
17obj-$(CONFIG_SPI_BFIN) += spi-bfin5xx.o 18obj-$(CONFIG_SPI_BFIN) += spi-bfin5xx.o
18obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o 19obj-$(CONFIG_SPI_BFIN_SPORT) += spi-bfin-sport.o
19obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o 20obj-$(CONFIG_SPI_BITBANG) += spi-bitbang.o
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
new file mode 100644
index 000000000000..f01b2648452e
--- /dev/null
+++ b/drivers/spi/spi-bcm63xx.c
@@ -0,0 +1,486 @@
1/*
2 * Broadcom BCM63xx SPI controller support
3 *
4 * Copyright (C) 2009-2011 Florian Fainelli <florian@openwrt.org>
5 * Copyright (C) 2010 Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 */
21
22#include <linux/kernel.h>
23#include <linux/init.h>
24#include <linux/clk.h>
25#include <linux/io.h>
26#include <linux/module.h>
27#include <linux/platform_device.h>
28#include <linux/delay.h>
29#include <linux/interrupt.h>
30#include <linux/spi/spi.h>
31#include <linux/completion.h>
32#include <linux/err.h>
33
34#include <bcm63xx_dev_spi.h>
35
36#define PFX KBUILD_MODNAME
37#define DRV_VER "0.1.2"
38
39struct bcm63xx_spi {
40 spinlock_t lock;
41 int stopping;
42 struct completion done;
43
44 void __iomem *regs;
45 int irq;
46
47 /* Platform data */
48 u32 speed_hz;
49 unsigned fifo_size;
50
51 /* Data buffers */
52 const unsigned char *tx_ptr;
53 unsigned char *rx_ptr;
54
55 /* data iomem */
56 u8 __iomem *tx_io;
57 const u8 __iomem *rx_io;
58
59 int remaining_bytes;
60
61 struct clk *clk;
62 struct platform_device *pdev;
63};
64
65static inline u8 bcm_spi_readb(struct bcm63xx_spi *bs,
66 unsigned int offset)
67{
68 return bcm_readb(bs->regs + bcm63xx_spireg(offset));
69}
70
71static inline u16 bcm_spi_readw(struct bcm63xx_spi *bs,
72 unsigned int offset)
73{
74 return bcm_readw(bs->regs + bcm63xx_spireg(offset));
75}
76
77static inline void bcm_spi_writeb(struct bcm63xx_spi *bs,
78 u8 value, unsigned int offset)
79{
80 bcm_writeb(value, bs->regs + bcm63xx_spireg(offset));
81}
82
83static inline void bcm_spi_writew(struct bcm63xx_spi *bs,
84 u16 value, unsigned int offset)
85{
86 bcm_writew(value, bs->regs + bcm63xx_spireg(offset));
87}
88
89static const unsigned bcm63xx_spi_freq_table[SPI_CLK_MASK][2] = {
90 { 20000000, SPI_CLK_20MHZ },
91 { 12500000, SPI_CLK_12_50MHZ },
92 { 6250000, SPI_CLK_6_250MHZ },
93 { 3125000, SPI_CLK_3_125MHZ },
94 { 1563000, SPI_CLK_1_563MHZ },
95 { 781000, SPI_CLK_0_781MHZ },
96 { 391000, SPI_CLK_0_391MHZ }
97};
98
99static int bcm63xx_spi_setup_transfer(struct spi_device *spi,
100 struct spi_transfer *t)
101{
102 struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
103 u8 bits_per_word;
104 u8 clk_cfg, reg;
105 u32 hz;
106 int i;
107
108 bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
109 hz = (t) ? t->speed_hz : spi->max_speed_hz;
110 if (bits_per_word != 8) {
111 dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
112 __func__, bits_per_word);
113 return -EINVAL;
114 }
115
116 if (spi->chip_select > spi->master->num_chipselect) {
117 dev_err(&spi->dev, "%s, unsupported slave %d\n",
118 __func__, spi->chip_select);
119 return -EINVAL;
120 }
121
122 /* Find the closest clock configuration */
123 for (i = 0; i < SPI_CLK_MASK; i++) {
124 if (hz <= bcm63xx_spi_freq_table[i][0]) {
125 clk_cfg = bcm63xx_spi_freq_table[i][1];
126 break;
127 }
128 }
129
130 /* No matching configuration found, default to lowest */
131 if (i == SPI_CLK_MASK)
132 clk_cfg = SPI_CLK_0_391MHZ;
133
134 /* clear existing clock configuration bits of the register */
135 reg = bcm_spi_readb(bs, SPI_CLK_CFG);
136 reg &= ~SPI_CLK_MASK;
137 reg |= clk_cfg;
138
139 bcm_spi_writeb(bs, reg, SPI_CLK_CFG);
140 dev_dbg(&spi->dev, "Setting clock register to %02x (hz %d)\n",
141 clk_cfg, hz);
142
143 return 0;
144}
145
146/* the spi->mode bits understood by this driver: */
147#define MODEBITS (SPI_CPOL | SPI_CPHA)
148
149static int bcm63xx_spi_setup(struct spi_device *spi)
150{
151 struct bcm63xx_spi *bs;
152 int ret;
153
154 bs = spi_master_get_devdata(spi->master);
155
156 if (bs->stopping)
157 return -ESHUTDOWN;
158
159 if (!spi->bits_per_word)
160 spi->bits_per_word = 8;
161
162 if (spi->mode & ~MODEBITS) {
163 dev_err(&spi->dev, "%s, unsupported mode bits %x\n",
164 __func__, spi->mode & ~MODEBITS);
165 return -EINVAL;
166 }
167
168 ret = bcm63xx_spi_setup_transfer(spi, NULL);
169 if (ret < 0) {
170 dev_err(&spi->dev, "setup: unsupported mode bits %x\n",
171 spi->mode & ~MODEBITS);
172 return ret;
173 }
174
175 dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
176 __func__, spi->mode & MODEBITS, spi->bits_per_word, 0);
177
178 return 0;
179}
180
181/* Fill the TX FIFO with as many bytes as possible */
182static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs)
183{
184 u8 size;
185
186 /* Fill the Tx FIFO with as many bytes as possible */
187 size = bs->remaining_bytes < bs->fifo_size ? bs->remaining_bytes :
188 bs->fifo_size;
189 memcpy_toio(bs->tx_io, bs->tx_ptr, size);
190 bs->remaining_bytes -= size;
191}
192
193static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
194{
195 struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
196 u16 msg_ctl;
197 u16 cmd;
198
199 dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
200 t->tx_buf, t->rx_buf, t->len);
201
202 /* Transmitter is inhibited */
203 bs->tx_ptr = t->tx_buf;
204 bs->rx_ptr = t->rx_buf;
205 init_completion(&bs->done);
206
207 if (t->tx_buf) {
208 bs->remaining_bytes = t->len;
209 bcm63xx_spi_fill_tx_fifo(bs);
210 }
211
212 /* Enable the command done interrupt which
213 * we use to determine completion of a command */
214 bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK);
215
216 /* Fill in the Message control register */
217 msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT);
218
219 if (t->rx_buf && t->tx_buf)
220 msg_ctl |= (SPI_FD_RW << SPI_MSG_TYPE_SHIFT);
221 else if (t->rx_buf)
222 msg_ctl |= (SPI_HD_R << SPI_MSG_TYPE_SHIFT);
223 else if (t->tx_buf)
224 msg_ctl |= (SPI_HD_W << SPI_MSG_TYPE_SHIFT);
225
226 bcm_spi_writew(bs, msg_ctl, SPI_MSG_CTL);
227
228 /* Issue the transfer */
229 cmd = SPI_CMD_START_IMMEDIATE;
230 cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
231 cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT);
232 bcm_spi_writew(bs, cmd, SPI_CMD);
233 wait_for_completion(&bs->done);
234
235 /* Disable the CMD_DONE interrupt */
236 bcm_spi_writeb(bs, 0, SPI_INT_MASK);
237
238 return t->len - bs->remaining_bytes;
239}
240
241static int bcm63xx_transfer(struct spi_device *spi, struct spi_message *m)
242{
243 struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
244 struct spi_transfer *t;
245 int ret = 0;
246
247 if (unlikely(list_empty(&m->transfers)))
248 return -EINVAL;
249
250 if (bs->stopping)
251 return -ESHUTDOWN;
252
253 list_for_each_entry(t, &m->transfers, transfer_list) {
254 ret += bcm63xx_txrx_bufs(spi, t);
255 }
256
257 m->complete(m->context);
258
259 return ret;
260}
261
262/* This driver supports single master mode only. Hence
263 * CMD_DONE is the only interrupt we care about
264 */
265static irqreturn_t bcm63xx_spi_interrupt(int irq, void *dev_id)
266{
267 struct spi_master *master = (struct spi_master *)dev_id;
268 struct bcm63xx_spi *bs = spi_master_get_devdata(master);
269 u8 intr;
270 u16 cmd;
271
272 /* Read interupts and clear them immediately */
273 intr = bcm_spi_readb(bs, SPI_INT_STATUS);
274 bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
275 bcm_spi_writeb(bs, 0, SPI_INT_MASK);
276
277 /* A tansfer completed */
278 if (intr & SPI_INTR_CMD_DONE) {
279 u8 rx_tail;
280
281 rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
282
283 /* Read out all the data */
284 if (rx_tail)
285 memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail);
286
287 /* See if there is more data to send */
288 if (bs->remaining_bytes > 0) {
289 bcm63xx_spi_fill_tx_fifo(bs);
290
291 /* Start the transfer */
292 bcm_spi_writew(bs, SPI_HD_W << SPI_MSG_TYPE_SHIFT,
293 SPI_MSG_CTL);
294 cmd = bcm_spi_readw(bs, SPI_CMD);
295 cmd |= SPI_CMD_START_IMMEDIATE;
296 cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
297 bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK);
298 bcm_spi_writew(bs, cmd, SPI_CMD);
299 } else {
300 complete(&bs->done);
301 }
302 }
303
304 return IRQ_HANDLED;
305}
306
307
308static int __devinit bcm63xx_spi_probe(struct platform_device *pdev)
309{
310 struct resource *r;
311 struct device *dev = &pdev->dev;
312 struct bcm63xx_spi_pdata *pdata = pdev->dev.platform_data;
313 int irq;
314 struct spi_master *master;
315 struct clk *clk;
316 struct bcm63xx_spi *bs;
317 int ret;
318
319 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
320 if (!r) {
321 dev_err(dev, "no iomem\n");
322 ret = -ENXIO;
323 goto out;
324 }
325
326 irq = platform_get_irq(pdev, 0);
327 if (irq < 0) {
328 dev_err(dev, "no irq\n");
329 ret = -ENXIO;
330 goto out;
331 }
332
333 clk = clk_get(dev, "spi");
334 if (IS_ERR(clk)) {
335 dev_err(dev, "no clock for device\n");
336 ret = PTR_ERR(clk);
337 goto out;
338 }
339
340 master = spi_alloc_master(dev, sizeof(*bs));
341 if (!master) {
342 dev_err(dev, "out of memory\n");
343 ret = -ENOMEM;
344 goto out_clk;
345 }
346
347 bs = spi_master_get_devdata(master);
348 init_completion(&bs->done);
349
350 platform_set_drvdata(pdev, master);
351 bs->pdev = pdev;
352
353 if (!devm_request_mem_region(&pdev->dev, r->start,
354 resource_size(r), PFX)) {
355 dev_err(dev, "iomem request failed\n");
356 ret = -ENXIO;
357 goto out_err;
358 }
359
360 bs->regs = devm_ioremap_nocache(&pdev->dev, r->start,
361 resource_size(r));
362 if (!bs->regs) {
363 dev_err(dev, "unable to ioremap regs\n");
364 ret = -ENOMEM;
365 goto out_err;
366 }
367
368 bs->irq = irq;
369 bs->clk = clk;
370 bs->fifo_size = pdata->fifo_size;
371
372 ret = devm_request_irq(&pdev->dev, irq, bcm63xx_spi_interrupt, 0,
373 pdev->name, master);
374 if (ret) {
375 dev_err(dev, "unable to request irq\n");
376 goto out_err;
377 }
378
379 master->bus_num = pdata->bus_num;
380 master->num_chipselect = pdata->num_chipselect;
381 master->setup = bcm63xx_spi_setup;
382 master->transfer = bcm63xx_transfer;
383 bs->speed_hz = pdata->speed_hz;
384 bs->stopping = 0;
385 bs->tx_io = (u8 *)(bs->regs + bcm63xx_spireg(SPI_MSG_DATA));
386 bs->rx_io = (const u8 *)(bs->regs + bcm63xx_spireg(SPI_RX_DATA));
387 spin_lock_init(&bs->lock);
388
389 /* Initialize hardware */
390 clk_enable(bs->clk);
391 bcm_spi_writeb(bs, SPI_INTR_CLEAR_ALL, SPI_INT_STATUS);
392
393 /* register and we are done */
394 ret = spi_register_master(master);
395 if (ret) {
396 dev_err(dev, "spi register failed\n");
397 goto out_clk_disable;
398 }
399
400 dev_info(dev, "at 0x%08x (irq %d, FIFOs size %d) v%s\n",
401 r->start, irq, bs->fifo_size, DRV_VER);
402
403 return 0;
404
405out_clk_disable:
406 clk_disable(clk);
407out_err:
408 platform_set_drvdata(pdev, NULL);
409 spi_master_put(master);
410out_clk:
411 clk_put(clk);
412out:
413 return ret;
414}
415
416static int __devexit bcm63xx_spi_remove(struct platform_device *pdev)
417{
418 struct spi_master *master = platform_get_drvdata(pdev);
419 struct bcm63xx_spi *bs = spi_master_get_devdata(master);
420
421 /* reset spi block */
422 bcm_spi_writeb(bs, 0, SPI_INT_MASK);
423 spin_lock(&bs->lock);
424 bs->stopping = 1;
425
426 /* HW shutdown */
427 clk_disable(bs->clk);
428 clk_put(bs->clk);
429
430 spin_unlock(&bs->lock);
431 platform_set_drvdata(pdev, 0);
432 spi_unregister_master(master);
433
434 return 0;
435}
436
437#ifdef CONFIG_PM
438static int bcm63xx_spi_suspend(struct device *dev)
439{
440 struct spi_master *master =
441 platform_get_drvdata(to_platform_device(dev));
442 struct bcm63xx_spi *bs = spi_master_get_devdata(master);
443
444 clk_disable(bs->clk);
445
446 return 0;
447}
448
449static int bcm63xx_spi_resume(struct device *dev)
450{
451 struct spi_master *master =
452 platform_get_drvdata(to_platform_device(dev));
453 struct bcm63xx_spi *bs = spi_master_get_devdata(master);
454
455 clk_enable(bs->clk);
456
457 return 0;
458}
459
460static const struct dev_pm_ops bcm63xx_spi_pm_ops = {
461 .suspend = bcm63xx_spi_suspend,
462 .resume = bcm63xx_spi_resume,
463};
464
465#define BCM63XX_SPI_PM_OPS (&bcm63xx_spi_pm_ops)
466#else
467#define BCM63XX_SPI_PM_OPS NULL
468#endif
469
470static struct platform_driver bcm63xx_spi_driver = {
471 .driver = {
472 .name = "bcm63xx-spi",
473 .owner = THIS_MODULE,
474 .pm = BCM63XX_SPI_PM_OPS,
475 },
476 .probe = bcm63xx_spi_probe,
477 .remove = __devexit_p(bcm63xx_spi_remove),
478};
479
480module_platform_driver(bcm63xx_spi_driver);
481
482MODULE_ALIAS("platform:bcm63xx_spi");
483MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>");
484MODULE_AUTHOR("Tanguy Bouzeloc <tanguy.bouzeloc@efixo.com>");
485MODULE_DESCRIPTION("Broadcom BCM63xx SPI Controller driver");
486MODULE_LICENSE("GPL");