diff options
author | Rafał Miłecki <zajec5@gmail.com> | 2014-08-17 12:33:38 -0400 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2014-08-19 12:30:55 -0400 |
commit | 0fc6a323e19173fc89e17940bb1e19447aa0224e (patch) | |
tree | e02ffb5dbea410af6f6ba489dad3d10b7e441397 /drivers | |
parent | 7d1311b93e58ed55f3a31cc8f94c4b8fe988a2b9 (diff) |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/spi/Kconfig | 6 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/spi-bcm53xx.c | 295 | ||||
-rw-r--r-- | drivers/spi/spi-bcm53xx.h | 72 |
4 files changed, 374 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 62e2242ad7e0..b14b829fb45c 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig | |||
@@ -112,6 +112,12 @@ config SPI_AU1550 | |||
112 | If you say yes to this option, support will be included for the | 112 | If you say yes to this option, support will be included for the |
113 | PSC SPI controller found on Au1550, Au1200 and Au1300 series. | 113 | PSC SPI controller found on Au1550, Au1200 and Au1300 series. |
114 | 114 | ||
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 | |||
115 | config SPI_BCM63XX | 121 | config SPI_BCM63XX |
116 | tristate "Broadcom BCM63xx SPI controller" | 122 | tristate "Broadcom BCM63xx SPI controller" |
117 | depends on BCM63XX | 123 | depends on BCM63XX |
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 762da0741148..78f24ca36fcf 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile | |||
@@ -15,6 +15,7 @@ obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o | |||
15 | obj-$(CONFIG_SPI_ATH79) += spi-ath79.o | 15 | obj-$(CONFIG_SPI_ATH79) += spi-ath79.o |
16 | obj-$(CONFIG_SPI_AU1550) += spi-au1550.o | 16 | obj-$(CONFIG_SPI_AU1550) += spi-au1550.o |
17 | obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o | 17 | obj-$(CONFIG_SPI_BCM2835) += spi-bcm2835.o |
18 | obj-$(CONFIG_SPI_BCM53XX) += spi-bcm53xx.o | ||
18 | obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o | 19 | obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o |
19 | obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o | 20 | obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o |
20 | obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o | 21 | obj-$(CONFIG_SPI_BFIN5XX) += spi-bfin5xx.o |
diff --git a/drivers/spi/spi-bcm53xx.c b/drivers/spi/spi-bcm53xx.c new file mode 100644 index 000000000000..48f13dfe7cf0 --- /dev/null +++ b/drivers/spi/spi-bcm53xx.c | |||
@@ -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); | ||
diff --git a/drivers/spi/spi-bcm53xx.h b/drivers/spi/spi-bcm53xx.h new file mode 100644 index 000000000000..73575dfe6916 --- /dev/null +++ b/drivers/spi/spi-bcm53xx.h | |||
@@ -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 */ | ||