]>
Commit | Line | Data |
---|---|---|
0fc6a323 RM |
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? */ | |
a7b221d8 | 13 | #define BCM53XXSPI_FLASH_WINDOW SZ_32M |
0fc6a323 RM |
14 | |
15 | /* The longest observed required wait was 19 ms */ | |
16 | #define BCM53XXSPI_SPE_TIMEOUT_MS 80 | |
17 | ||
18 | struct bcm53xxspi { | |
19 | struct bcma_device *core; | |
20 | struct spi_master *master; | |
a7b221d8 | 21 | void __iomem *mmio_base; |
0fc6a323 RM |
22 | |
23 | size_t read_offset; | |
a7b221d8 | 24 | bool bspi; /* Boot SPI mode with memory mapping */ |
0fc6a323 RM |
25 | }; |
26 | ||
27 | static inline u32 bcm53xxspi_read(struct bcm53xxspi *b53spi, u16 offset) | |
28 | { | |
29 | return bcma_read32(b53spi->core, offset); | |
30 | } | |
31 | ||
32 | static inline void bcm53xxspi_write(struct bcm53xxspi *b53spi, u16 offset, | |
33 | u32 value) | |
34 | { | |
35 | bcma_write32(b53spi->core, offset, value); | |
36 | } | |
37 | ||
a7b221d8 RM |
38 | static void bcm53xxspi_disable_bspi(struct bcm53xxspi *b53spi) |
39 | { | |
40 | struct device *dev = &b53spi->core->dev; | |
41 | unsigned long deadline; | |
42 | u32 tmp; | |
43 | ||
44 | if (!b53spi->bspi) | |
45 | return; | |
46 | ||
47 | tmp = bcm53xxspi_read(b53spi, B53SPI_BSPI_MAST_N_BOOT_CTRL); | |
48 | if (tmp & 0x1) | |
49 | return; | |
50 | ||
51 | deadline = jiffies + usecs_to_jiffies(200); | |
52 | do { | |
53 | tmp = bcm53xxspi_read(b53spi, B53SPI_BSPI_BUSY_STATUS); | |
54 | if (!(tmp & 0x1)) { | |
55 | bcm53xxspi_write(b53spi, B53SPI_BSPI_MAST_N_BOOT_CTRL, | |
56 | 0x1); | |
57 | ndelay(200); | |
58 | b53spi->bspi = false; | |
59 | return; | |
60 | } | |
61 | udelay(1); | |
62 | } while (!time_after_eq(jiffies, deadline)); | |
63 | ||
64 | dev_warn(dev, "Timeout disabling BSPI\n"); | |
65 | } | |
66 | ||
67 | static void bcm53xxspi_enable_bspi(struct bcm53xxspi *b53spi) | |
68 | { | |
69 | u32 tmp; | |
70 | ||
71 | if (b53spi->bspi) | |
72 | return; | |
73 | ||
74 | tmp = bcm53xxspi_read(b53spi, B53SPI_BSPI_MAST_N_BOOT_CTRL); | |
75 | if (!(tmp & 0x1)) | |
76 | return; | |
77 | ||
78 | bcm53xxspi_write(b53spi, B53SPI_BSPI_MAST_N_BOOT_CTRL, 0x0); | |
79 | b53spi->bspi = true; | |
80 | } | |
81 | ||
0fc6a323 RM |
82 | static inline unsigned int bcm53xxspi_calc_timeout(size_t len) |
83 | { | |
84 | /* Do some magic calculation based on length and buad. Add 10% and 1. */ | |
85 | return (len * 9000 / BCM53XXSPI_MAX_SPI_BAUD * 110 / 100) + 1; | |
86 | } | |
87 | ||
88 | static int bcm53xxspi_wait(struct bcm53xxspi *b53spi, unsigned int timeout_ms) | |
89 | { | |
90 | unsigned long deadline; | |
91 | u32 tmp; | |
92 | ||
93 | /* SPE bit has to be 0 before we read MSPI STATUS */ | |
b3e7766b | 94 | deadline = jiffies + msecs_to_jiffies(BCM53XXSPI_SPE_TIMEOUT_MS); |
0fc6a323 RM |
95 | do { |
96 | tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2); | |
97 | if (!(tmp & B53SPI_MSPI_SPCR2_SPE)) | |
98 | break; | |
99 | udelay(5); | |
100 | } while (!time_after_eq(jiffies, deadline)); | |
101 | ||
102 | if (tmp & B53SPI_MSPI_SPCR2_SPE) | |
103 | goto spi_timeout; | |
104 | ||
105 | /* Check status */ | |
b3e7766b | 106 | deadline = jiffies + msecs_to_jiffies(timeout_ms); |
0fc6a323 RM |
107 | do { |
108 | tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_MSPI_STATUS); | |
109 | if (tmp & B53SPI_MSPI_MSPI_STATUS_SPIF) { | |
110 | bcm53xxspi_write(b53spi, B53SPI_MSPI_MSPI_STATUS, 0); | |
111 | return 0; | |
112 | } | |
113 | ||
114 | cpu_relax(); | |
115 | udelay(100); | |
116 | } while (!time_after_eq(jiffies, deadline)); | |
117 | ||
118 | spi_timeout: | |
119 | bcm53xxspi_write(b53spi, B53SPI_MSPI_MSPI_STATUS, 0); | |
120 | ||
121 | pr_err("Timeout waiting for SPI to be ready!\n"); | |
122 | ||
123 | return -EBUSY; | |
124 | } | |
125 | ||
126 | static void bcm53xxspi_buf_write(struct bcm53xxspi *b53spi, u8 *w_buf, | |
127 | size_t len, bool cont) | |
128 | { | |
129 | u32 tmp; | |
130 | int i; | |
131 | ||
132 | for (i = 0; i < len; i++) { | |
133 | /* Transmit Register File MSB */ | |
134 | bcm53xxspi_write(b53spi, B53SPI_MSPI_TXRAM + 4 * (i * 2), | |
135 | (unsigned int)w_buf[i]); | |
136 | } | |
137 | ||
138 | for (i = 0; i < len; i++) { | |
139 | tmp = B53SPI_CDRAM_CONT | B53SPI_CDRAM_PCS_DISABLE_ALL | | |
140 | B53SPI_CDRAM_PCS_DSCK; | |
141 | if (!cont && i == len - 1) | |
142 | tmp &= ~B53SPI_CDRAM_CONT; | |
143 | tmp &= ~0x1; | |
144 | /* Command Register File */ | |
145 | bcm53xxspi_write(b53spi, B53SPI_MSPI_CDRAM + 4 * i, tmp); | |
146 | } | |
147 | ||
148 | /* Set queue pointers */ | |
149 | bcm53xxspi_write(b53spi, B53SPI_MSPI_NEWQP, 0); | |
150 | bcm53xxspi_write(b53spi, B53SPI_MSPI_ENDQP, len - 1); | |
151 | ||
152 | if (cont) | |
153 | bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 1); | |
154 | ||
155 | /* Start SPI transfer */ | |
156 | tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2); | |
157 | tmp |= B53SPI_MSPI_SPCR2_SPE; | |
158 | if (cont) | |
159 | tmp |= B53SPI_MSPI_SPCR2_CONT_AFTER_CMD; | |
160 | bcm53xxspi_write(b53spi, B53SPI_MSPI_SPCR2, tmp); | |
161 | ||
162 | /* Wait for SPI to finish */ | |
163 | bcm53xxspi_wait(b53spi, bcm53xxspi_calc_timeout(len)); | |
164 | ||
165 | if (!cont) | |
166 | bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 0); | |
167 | ||
168 | b53spi->read_offset = len; | |
169 | } | |
170 | ||
171 | static void bcm53xxspi_buf_read(struct bcm53xxspi *b53spi, u8 *r_buf, | |
172 | size_t len, bool cont) | |
173 | { | |
174 | u32 tmp; | |
175 | int i; | |
176 | ||
177 | for (i = 0; i < b53spi->read_offset + len; i++) { | |
178 | tmp = B53SPI_CDRAM_CONT | B53SPI_CDRAM_PCS_DISABLE_ALL | | |
179 | B53SPI_CDRAM_PCS_DSCK; | |
180 | if (!cont && i == b53spi->read_offset + len - 1) | |
181 | tmp &= ~B53SPI_CDRAM_CONT; | |
182 | tmp &= ~0x1; | |
183 | /* Command Register File */ | |
184 | bcm53xxspi_write(b53spi, B53SPI_MSPI_CDRAM + 4 * i, tmp); | |
185 | } | |
186 | ||
187 | /* Set queue pointers */ | |
188 | bcm53xxspi_write(b53spi, B53SPI_MSPI_NEWQP, 0); | |
189 | bcm53xxspi_write(b53spi, B53SPI_MSPI_ENDQP, | |
190 | b53spi->read_offset + len - 1); | |
191 | ||
192 | if (cont) | |
193 | bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 1); | |
194 | ||
195 | /* Start SPI transfer */ | |
196 | tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2); | |
197 | tmp |= B53SPI_MSPI_SPCR2_SPE; | |
198 | if (cont) | |
199 | tmp |= B53SPI_MSPI_SPCR2_CONT_AFTER_CMD; | |
200 | bcm53xxspi_write(b53spi, B53SPI_MSPI_SPCR2, tmp); | |
201 | ||
202 | /* Wait for SPI to finish */ | |
203 | bcm53xxspi_wait(b53spi, bcm53xxspi_calc_timeout(len)); | |
204 | ||
205 | if (!cont) | |
206 | bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 0); | |
207 | ||
208 | for (i = 0; i < len; ++i) { | |
209 | int offset = b53spi->read_offset + i; | |
210 | ||
211 | /* Data stored in the transmit register file LSB */ | |
212 | r_buf[i] = (u8)bcm53xxspi_read(b53spi, B53SPI_MSPI_RXRAM + 4 * (1 + offset * 2)); | |
213 | } | |
214 | ||
215 | b53spi->read_offset = 0; | |
216 | } | |
217 | ||
218 | static int bcm53xxspi_transfer_one(struct spi_master *master, | |
219 | struct spi_device *spi, | |
220 | struct spi_transfer *t) | |
221 | { | |
222 | struct bcm53xxspi *b53spi = spi_master_get_devdata(master); | |
223 | u8 *buf; | |
224 | size_t left; | |
225 | ||
a7b221d8 RM |
226 | bcm53xxspi_disable_bspi(b53spi); |
227 | ||
0fc6a323 RM |
228 | if (t->tx_buf) { |
229 | buf = (u8 *)t->tx_buf; | |
230 | left = t->len; | |
231 | while (left) { | |
232 | size_t to_write = min_t(size_t, 16, left); | |
233 | bool cont = left - to_write > 0; | |
234 | ||
235 | bcm53xxspi_buf_write(b53spi, buf, to_write, cont); | |
236 | left -= to_write; | |
237 | buf += to_write; | |
238 | } | |
239 | } | |
240 | ||
241 | if (t->rx_buf) { | |
242 | buf = (u8 *)t->rx_buf; | |
243 | left = t->len; | |
244 | while (left) { | |
245 | size_t to_read = min_t(size_t, 16 - b53spi->read_offset, | |
246 | left); | |
247 | bool cont = left - to_read > 0; | |
248 | ||
249 | bcm53xxspi_buf_read(b53spi, buf, to_read, cont); | |
250 | left -= to_read; | |
251 | buf += to_read; | |
252 | } | |
253 | } | |
254 | ||
255 | return 0; | |
256 | } | |
257 | ||
a7b221d8 RM |
258 | static int bcm53xxspi_flash_read(struct spi_device *spi, |
259 | struct spi_flash_read_message *msg) | |
260 | { | |
261 | struct bcm53xxspi *b53spi = spi_master_get_devdata(spi->master); | |
262 | int ret = 0; | |
263 | ||
264 | if (msg->from + msg->len > BCM53XXSPI_FLASH_WINDOW) | |
265 | return -EINVAL; | |
266 | ||
267 | bcm53xxspi_enable_bspi(b53spi); | |
268 | memcpy_fromio(msg->buf, b53spi->mmio_base + msg->from, msg->len); | |
269 | msg->retlen = msg->len; | |
270 | ||
271 | return ret; | |
272 | } | |
273 | ||
0fc6a323 RM |
274 | /************************************************** |
275 | * BCMA | |
276 | **************************************************/ | |
277 | ||
050429a7 | 278 | static struct spi_board_info bcm53xx_info = { |
0fc6a323 RM |
279 | .modalias = "bcm53xxspiflash", |
280 | }; | |
281 | ||
282 | static const struct bcma_device_id bcm53xxspi_bcma_tbl[] = { | |
283 | BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_QSPI, BCMA_ANY_REV, BCMA_ANY_CLASS), | |
f7219b52 | 284 | {}, |
0fc6a323 RM |
285 | }; |
286 | MODULE_DEVICE_TABLE(bcma, bcm53xxspi_bcma_tbl); | |
287 | ||
288 | static int bcm53xxspi_bcma_probe(struct bcma_device *core) | |
289 | { | |
a7b221d8 | 290 | struct device *dev = &core->dev; |
0fc6a323 RM |
291 | struct bcm53xxspi *b53spi; |
292 | struct spi_master *master; | |
293 | int err; | |
294 | ||
295 | if (core->bus->drv_cc.core->id.rev != 42) { | |
296 | pr_err("SPI on SoC with unsupported ChipCommon rev\n"); | |
297 | return -ENOTSUPP; | |
298 | } | |
299 | ||
a7b221d8 | 300 | master = spi_alloc_master(dev, sizeof(*b53spi)); |
0fc6a323 RM |
301 | if (!master) |
302 | return -ENOMEM; | |
303 | ||
304 | b53spi = spi_master_get_devdata(master); | |
305 | b53spi->master = master; | |
306 | b53spi->core = core; | |
307 | ||
a7b221d8 RM |
308 | if (core->addr_s[0]) |
309 | b53spi->mmio_base = devm_ioremap(dev, core->addr_s[0], | |
310 | BCM53XXSPI_FLASH_WINDOW); | |
311 | b53spi->bspi = true; | |
312 | bcm53xxspi_disable_bspi(b53spi); | |
313 | ||
0fc6a323 | 314 | master->transfer_one = bcm53xxspi_transfer_one; |
a7b221d8 RM |
315 | if (b53spi->mmio_base) |
316 | master->spi_flash_read = bcm53xxspi_flash_read; | |
0fc6a323 RM |
317 | |
318 | bcma_set_drvdata(core, b53spi); | |
319 | ||
a7b221d8 | 320 | err = devm_spi_register_master(dev, master); |
0fc6a323 RM |
321 | if (err) { |
322 | spi_master_put(master); | |
323 | bcma_set_drvdata(core, NULL); | |
6774eea6 | 324 | return err; |
0fc6a323 RM |
325 | } |
326 | ||
327 | /* Broadcom SoCs (at least with the CC rev 42) use SPI for flash only */ | |
328 | spi_new_device(master, &bcm53xx_info); | |
329 | ||
6774eea6 | 330 | return 0; |
0fc6a323 RM |
331 | } |
332 | ||
333 | static struct bcma_driver bcm53xxspi_bcma_driver = { | |
334 | .name = KBUILD_MODNAME, | |
335 | .id_table = bcm53xxspi_bcma_tbl, | |
336 | .probe = bcm53xxspi_bcma_probe, | |
0fc6a323 RM |
337 | }; |
338 | ||
339 | /************************************************** | |
340 | * Init & exit | |
341 | **************************************************/ | |
342 | ||
343 | static int __init bcm53xxspi_module_init(void) | |
344 | { | |
345 | int err = 0; | |
346 | ||
347 | err = bcma_driver_register(&bcm53xxspi_bcma_driver); | |
348 | if (err) | |
349 | pr_err("Failed to register bcma driver: %d\n", err); | |
350 | ||
351 | return err; | |
352 | } | |
353 | ||
354 | static void __exit bcm53xxspi_module_exit(void) | |
355 | { | |
356 | bcma_driver_unregister(&bcm53xxspi_bcma_driver); | |
357 | } | |
358 | ||
359 | module_init(bcm53xxspi_module_init); | |
360 | module_exit(bcm53xxspi_module_exit); | |
61d38b9a AL |
361 | |
362 | MODULE_DESCRIPTION("Broadcom BCM53xx SPI Controller driver"); | |
363 | MODULE_AUTHOR("Rafał Miłecki <[email protected]>"); | |
364 | MODULE_LICENSE("GPL"); |