Skip to content

Commit 0fc6a32

Browse files
rmileckibroonie
authored andcommitted
spi: bcm53xx: driver for SPI controller on Broadcom bcma SoC
Broadcom 53xx ARM SoCs use bcma bus that contains various cores (AKA devices). If board has a serial flash, it's connected over SPI and the bcma bus includes a SPI controller. Example log from such a board: bus0: Found chip with id 53010, rev 0x00 and package 0x02 (...) bus0: Core 18 found: SPI flash controller (manuf 0x4BF, id 0x50A, rev 0x01, class 0x0) This patch adds a bcma driver for SPI core, it registers SPI master controller and "bcm53xxspiflash" SPI device. Signed-off-by: Rafał Miłecki <zajec5@gmail.com> Signed-off-by: Mark Brown <broonie@linaro.org>
1 parent 7d1311b commit 0fc6a32

File tree

4 files changed

+374
-0
lines changed

4 files changed

+374
-0
lines changed

drivers/spi/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,12 @@ config SPI_AU1550
112112
If you say yes to this option, support will be included for the
113113
PSC SPI controller found on Au1550, Au1200 and Au1300 series.
114114

115+
config SPI_BCM53XX
116+
tristate "Broadcom BCM53xx SPI controller"
117+
depends on ARCH_BCM_5301X
118+
help
119+
Enable support for the SPI controller on Broadcom BCM53xx ARM SoCs.
120+
115121
config SPI_BCM63XX
116122
tristate "Broadcom BCM63xx SPI controller"
117123
depends on BCM63XX

drivers/spi/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
1515
obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
1616
obj-$(CONFIG_SPI_AU1550) += spi-au1550.o
1717
obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o
18+
obj-$(CONFIG_SPI_BCM53XX) += spi-bcm53xx.o
1819
obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o
1920
obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o
2021
obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o

drivers/spi/spi-bcm53xx.c

Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2+
3+
#include <linux/kernel.h>
4+
#include <linux/module.h>
5+
#include <linux/slab.h>
6+
#include <linux/delay.h>
7+
#include <linux/bcma/bcma.h>
8+
#include <linux/spi/spi.h>
9+
10+
#include "spi-bcm53xx.h"
11+
12+
#define BCM53XXSPI_MAX_SPI_BAUD 13500000 /* 216 MHz? */
13+
14+
/* The longest observed required wait was 19 ms */
15+
#define BCM53XXSPI_SPE_TIMEOUT_MS 80
16+
17+
struct bcm53xxspi {
18+
struct bcma_device *core;
19+
struct spi_master *master;
20+
21+
size_t read_offset;
22+
};
23+
24+
static inline u32 bcm53xxspi_read(struct bcm53xxspi *b53spi, u16 offset)
25+
{
26+
return bcma_read32(b53spi->core, offset);
27+
}
28+
29+
static inline void bcm53xxspi_write(struct bcm53xxspi *b53spi, u16 offset,
30+
u32 value)
31+
{
32+
bcma_write32(b53spi->core, offset, value);
33+
}
34+
35+
static inline unsigned int bcm53xxspi_calc_timeout(size_t len)
36+
{
37+
/* Do some magic calculation based on length and buad. Add 10% and 1. */
38+
return (len * 9000 / BCM53XXSPI_MAX_SPI_BAUD * 110 / 100) + 1;
39+
}
40+
41+
static int bcm53xxspi_wait(struct bcm53xxspi *b53spi, unsigned int timeout_ms)
42+
{
43+
unsigned long deadline;
44+
u32 tmp;
45+
46+
/* SPE bit has to be 0 before we read MSPI STATUS */
47+
deadline = jiffies + BCM53XXSPI_SPE_TIMEOUT_MS * HZ / 1000;
48+
do {
49+
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
50+
if (!(tmp & B53SPI_MSPI_SPCR2_SPE))
51+
break;
52+
udelay(5);
53+
} while (!time_after_eq(jiffies, deadline));
54+
55+
if (tmp & B53SPI_MSPI_SPCR2_SPE)
56+
goto spi_timeout;
57+
58+
/* Check status */
59+
deadline = jiffies + timeout_ms * HZ / 1000;
60+
do {
61+
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_MSPI_STATUS);
62+
if (tmp & B53SPI_MSPI_MSPI_STATUS_SPIF) {
63+
bcm53xxspi_write(b53spi, B53SPI_MSPI_MSPI_STATUS, 0);
64+
return 0;
65+
}
66+
67+
cpu_relax();
68+
udelay(100);
69+
} while (!time_after_eq(jiffies, deadline));
70+
71+
spi_timeout:
72+
bcm53xxspi_write(b53spi, B53SPI_MSPI_MSPI_STATUS, 0);
73+
74+
pr_err("Timeout waiting for SPI to be ready!\n");
75+
76+
return -EBUSY;
77+
}
78+
79+
static void bcm53xxspi_buf_write(struct bcm53xxspi *b53spi, u8 *w_buf,
80+
size_t len, bool cont)
81+
{
82+
u32 tmp;
83+
int i;
84+
85+
for (i = 0; i < len; i++) {
86+
/* Transmit Register File MSB */
87+
bcm53xxspi_write(b53spi, B53SPI_MSPI_TXRAM + 4 * (i * 2),
88+
(unsigned int)w_buf[i]);
89+
}
90+
91+
for (i = 0; i < len; i++) {
92+
tmp = B53SPI_CDRAM_CONT | B53SPI_CDRAM_PCS_DISABLE_ALL |
93+
B53SPI_CDRAM_PCS_DSCK;
94+
if (!cont && i == len - 1)
95+
tmp &= ~B53SPI_CDRAM_CONT;
96+
tmp &= ~0x1;
97+
/* Command Register File */
98+
bcm53xxspi_write(b53spi, B53SPI_MSPI_CDRAM + 4 * i, tmp);
99+
}
100+
101+
/* Set queue pointers */
102+
bcm53xxspi_write(b53spi, B53SPI_MSPI_NEWQP, 0);
103+
bcm53xxspi_write(b53spi, B53SPI_MSPI_ENDQP, len - 1);
104+
105+
if (cont)
106+
bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 1);
107+
108+
/* Start SPI transfer */
109+
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
110+
tmp |= B53SPI_MSPI_SPCR2_SPE;
111+
if (cont)
112+
tmp |= B53SPI_MSPI_SPCR2_CONT_AFTER_CMD;
113+
bcm53xxspi_write(b53spi, B53SPI_MSPI_SPCR2, tmp);
114+
115+
/* Wait for SPI to finish */
116+
bcm53xxspi_wait(b53spi, bcm53xxspi_calc_timeout(len));
117+
118+
if (!cont)
119+
bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 0);
120+
121+
b53spi->read_offset = len;
122+
}
123+
124+
static void bcm53xxspi_buf_read(struct bcm53xxspi *b53spi, u8 *r_buf,
125+
size_t len, bool cont)
126+
{
127+
u32 tmp;
128+
int i;
129+
130+
for (i = 0; i < b53spi->read_offset + len; i++) {
131+
tmp = B53SPI_CDRAM_CONT | B53SPI_CDRAM_PCS_DISABLE_ALL |
132+
B53SPI_CDRAM_PCS_DSCK;
133+
if (!cont && i == b53spi->read_offset + len - 1)
134+
tmp &= ~B53SPI_CDRAM_CONT;
135+
tmp &= ~0x1;
136+
/* Command Register File */
137+
bcm53xxspi_write(b53spi, B53SPI_MSPI_CDRAM + 4 * i, tmp);
138+
}
139+
140+
/* Set queue pointers */
141+
bcm53xxspi_write(b53spi, B53SPI_MSPI_NEWQP, 0);
142+
bcm53xxspi_write(b53spi, B53SPI_MSPI_ENDQP,
143+
b53spi->read_offset + len - 1);
144+
145+
if (cont)
146+
bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 1);
147+
148+
/* Start SPI transfer */
149+
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
150+
tmp |= B53SPI_MSPI_SPCR2_SPE;
151+
if (cont)
152+
tmp |= B53SPI_MSPI_SPCR2_CONT_AFTER_CMD;
153+
bcm53xxspi_write(b53spi, B53SPI_MSPI_SPCR2, tmp);
154+
155+
/* Wait for SPI to finish */
156+
bcm53xxspi_wait(b53spi, bcm53xxspi_calc_timeout(len));
157+
158+
if (!cont)
159+
bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 0);
160+
161+
for (i = 0; i < len; ++i) {
162+
int offset = b53spi->read_offset + i;
163+
164+
/* Data stored in the transmit register file LSB */
165+
r_buf[i] = (u8)bcm53xxspi_read(b53spi, B53SPI_MSPI_RXRAM + 4 * (1 + offset * 2));
166+
}
167+
168+
b53spi->read_offset = 0;
169+
}
170+
171+
static int bcm53xxspi_transfer_one(struct spi_master *master,
172+
struct spi_device *spi,
173+
struct spi_transfer *t)
174+
{
175+
struct bcm53xxspi *b53spi = spi_master_get_devdata(master);
176+
u8 *buf;
177+
size_t left;
178+
179+
if (t->tx_buf) {
180+
buf = (u8 *)t->tx_buf;
181+
left = t->len;
182+
while (left) {
183+
size_t to_write = min_t(size_t, 16, left);
184+
bool cont = left - to_write > 0;
185+
186+
bcm53xxspi_buf_write(b53spi, buf, to_write, cont);
187+
left -= to_write;
188+
buf += to_write;
189+
}
190+
}
191+
192+
if (t->rx_buf) {
193+
buf = (u8 *)t->rx_buf;
194+
left = t->len;
195+
while (left) {
196+
size_t to_read = min_t(size_t, 16 - b53spi->read_offset,
197+
left);
198+
bool cont = left - to_read > 0;
199+
200+
bcm53xxspi_buf_read(b53spi, buf, to_read, cont);
201+
left -= to_read;
202+
buf += to_read;
203+
}
204+
}
205+
206+
return 0;
207+
}
208+
209+
/**************************************************
210+
* BCMA
211+
**************************************************/
212+
213+
struct spi_board_info bcm53xx_info = {
214+
.modalias = "bcm53xxspiflash",
215+
};
216+
217+
static const struct bcma_device_id bcm53xxspi_bcma_tbl[] = {
218+
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_QSPI, BCMA_ANY_REV, BCMA_ANY_CLASS),
219+
BCMA_CORETABLE_END
220+
};
221+
MODULE_DEVICE_TABLE(bcma, bcm53xxspi_bcma_tbl);
222+
223+
static int bcm53xxspi_bcma_probe(struct bcma_device *core)
224+
{
225+
struct bcm53xxspi *b53spi;
226+
struct spi_master *master;
227+
int err;
228+
229+
if (core->bus->drv_cc.core->id.rev != 42) {
230+
pr_err("SPI on SoC with unsupported ChipCommon rev\n");
231+
return -ENOTSUPP;
232+
}
233+
234+
master = spi_alloc_master(&core->dev, sizeof(*b53spi));
235+
if (!master)
236+
return -ENOMEM;
237+
238+
b53spi = spi_master_get_devdata(master);
239+
b53spi->master = master;
240+
b53spi->core = core;
241+
242+
master->transfer_one = bcm53xxspi_transfer_one;
243+
244+
bcma_set_drvdata(core, b53spi);
245+
246+
err = devm_spi_register_master(&core->dev, master);
247+
if (err) {
248+
spi_master_put(master);
249+
bcma_set_drvdata(core, NULL);
250+
goto out;
251+
}
252+
253+
/* Broadcom SoCs (at least with the CC rev 42) use SPI for flash only */
254+
spi_new_device(master, &bcm53xx_info);
255+
256+
out:
257+
return err;
258+
}
259+
260+
static void bcm53xxspi_bcma_remove(struct bcma_device *core)
261+
{
262+
struct bcm53xxspi *b53spi = bcma_get_drvdata(core);
263+
264+
spi_unregister_master(b53spi->master);
265+
}
266+
267+
static struct bcma_driver bcm53xxspi_bcma_driver = {
268+
.name = KBUILD_MODNAME,
269+
.id_table = bcm53xxspi_bcma_tbl,
270+
.probe = bcm53xxspi_bcma_probe,
271+
.remove = bcm53xxspi_bcma_remove,
272+
};
273+
274+
/**************************************************
275+
* Init & exit
276+
**************************************************/
277+
278+
static int __init bcm53xxspi_module_init(void)
279+
{
280+
int err = 0;
281+
282+
err = bcma_driver_register(&bcm53xxspi_bcma_driver);
283+
if (err)
284+
pr_err("Failed to register bcma driver: %d\n", err);
285+
286+
return err;
287+
}
288+
289+
static void __exit bcm53xxspi_module_exit(void)
290+
{
291+
bcma_driver_unregister(&bcm53xxspi_bcma_driver);
292+
}
293+
294+
module_init(bcm53xxspi_module_init);
295+
module_exit(bcm53xxspi_module_exit);

drivers/spi/spi-bcm53xx.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#ifndef SPI_BCM53XX_H
2+
#define SPI_BCM53XX_H
3+
4+
#define B53SPI_BSPI_REVISION_ID 0x000
5+
#define B53SPI_BSPI_SCRATCH 0x004
6+
#define B53SPI_BSPI_MAST_N_BOOT_CTRL 0x008
7+
#define B53SPI_BSPI_BUSY_STATUS 0x00c
8+
#define B53SPI_BSPI_INTR_STATUS 0x010
9+
#define B53SPI_BSPI_B0_STATUS 0x014
10+
#define B53SPI_BSPI_B0_CTRL 0x018
11+
#define B53SPI_BSPI_B1_STATUS 0x01c
12+
#define B53SPI_BSPI_B1_CTRL 0x020
13+
#define B53SPI_BSPI_STRAP_OVERRIDE_CTRL 0x024
14+
#define B53SPI_BSPI_FLEX_MODE_ENABLE 0x028
15+
#define B53SPI_BSPI_BITS_PER_CYCLE 0x02c
16+
#define B53SPI_BSPI_BITS_PER_PHASE 0x030
17+
#define B53SPI_BSPI_CMD_AND_MODE_BYTE 0x034
18+
#define B53SPI_BSPI_BSPI_FLASH_UPPER_ADDR_BYTE 0x038
19+
#define B53SPI_BSPI_BSPI_XOR_VALUE 0x03c
20+
#define B53SPI_BSPI_BSPI_XOR_ENABLE 0x040
21+
#define B53SPI_BSPI_BSPI_PIO_MODE_ENABLE 0x044
22+
#define B53SPI_BSPI_BSPI_PIO_IODIR 0x048
23+
#define B53SPI_BSPI_BSPI_PIO_DATA 0x04c
24+
25+
/* RAF */
26+
#define B53SPI_RAF_START_ADDR 0x100
27+
#define B53SPI_RAF_NUM_WORDS 0x104
28+
#define B53SPI_RAF_CTRL 0x108
29+
#define B53SPI_RAF_FULLNESS 0x10c
30+
#define B53SPI_RAF_WATERMARK 0x110
31+
#define B53SPI_RAF_STATUS 0x114
32+
#define B53SPI_RAF_READ_DATA 0x118
33+
#define B53SPI_RAF_WORD_CNT 0x11c
34+
#define B53SPI_RAF_CURR_ADDR 0x120
35+
36+
/* MSPI */
37+
#define B53SPI_MSPI_SPCR0_LSB 0x200
38+
#define B53SPI_MSPI_SPCR0_MSB 0x204
39+
#define B53SPI_MSPI_SPCR1_LSB 0x208
40+
#define B53SPI_MSPI_SPCR1_MSB 0x20c
41+
#define B53SPI_MSPI_NEWQP 0x210
42+
#define B53SPI_MSPI_ENDQP 0x214
43+
#define B53SPI_MSPI_SPCR2 0x218
44+
#define B53SPI_MSPI_SPCR2_SPE 0x00000040
45+
#define B53SPI_MSPI_SPCR2_CONT_AFTER_CMD 0x00000080
46+
#define B53SPI_MSPI_MSPI_STATUS 0x220
47+
#define B53SPI_MSPI_MSPI_STATUS_SPIF 0x00000001
48+
#define B53SPI_MSPI_CPTQP 0x224
49+
#define B53SPI_MSPI_TXRAM 0x240 /* 32 registers, up to 0x2b8 */
50+
#define B53SPI_MSPI_RXRAM 0x2c0 /* 32 registers, up to 0x33c */
51+
#define B53SPI_MSPI_CDRAM 0x340 /* 16 registers, up to 0x37c */
52+
#define B53SPI_CDRAM_PCS_PCS0 0x00000001
53+
#define B53SPI_CDRAM_PCS_PCS1 0x00000002
54+
#define B53SPI_CDRAM_PCS_PCS2 0x00000004
55+
#define B53SPI_CDRAM_PCS_PCS3 0x00000008
56+
#define B53SPI_CDRAM_PCS_DISABLE_ALL 0x0000000f
57+
#define B53SPI_CDRAM_PCS_DSCK 0x00000010
58+
#define B53SPI_CDRAM_BITSE 0x00000040
59+
#define B53SPI_CDRAM_CONT 0x00000080
60+
#define B53SPI_MSPI_WRITE_LOCK 0x380
61+
#define B53SPI_MSPI_DISABLE_FLUSH_GEN 0x384
62+
63+
/* Interrupt */
64+
#define B53SPI_INTR_RAF_LR_FULLNESS_REACHED 0x3a0
65+
#define B53SPI_INTR_RAF_LR_TRUNCATED 0x3a4
66+
#define B53SPI_INTR_RAF_LR_IMPATIENT 0x3a8
67+
#define B53SPI_INTR_RAF_LR_SESSION_DONE 0x3ac
68+
#define B53SPI_INTR_RAF_LR_OVERREAD 0x3b0
69+
#define B53SPI_INTR_MSPI_DONE 0x3b4
70+
#define B53SPI_INTR_MSPI_HALT_SET_TRANSACTION_DONE 0x3b8
71+
72+
#endif /* SPI_BCM53XX_H */

0 commit comments

Comments
 (0)