]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
5ac07d29 ÁFR |
2 | /* |
3 | * Copyright (C) 2017 Álvaro Fernández Rojas <[email protected]> | |
4 | * | |
5 | * Derived from linux/drivers/spi/spi-bcm63xx.c: | |
6 | * Copyright (C) 2009-2012 Florian Fainelli <[email protected]> | |
7 | * Copyright (C) 2010 Tanguy Bouzeloc <[email protected]> | |
5ac07d29 ÁFR |
8 | */ |
9 | ||
5ac07d29 ÁFR |
10 | #include <clk.h> |
11 | #include <dm.h> | |
f7ae49fc | 12 | #include <log.h> |
336d4615 | 13 | #include <malloc.h> |
5ac07d29 ÁFR |
14 | #include <spi.h> |
15 | #include <reset.h> | |
16 | #include <wait_bit.h> | |
17 | #include <asm/io.h> | |
18 | ||
5ac07d29 ÁFR |
19 | /* BCM6348 SPI core */ |
20 | #define SPI_6348_CLK 0x06 | |
21 | #define SPI_6348_CMD 0x00 | |
22 | #define SPI_6348_CTL 0x40 | |
23 | #define SPI_6348_CTL_SHIFT 6 | |
24 | #define SPI_6348_FILL 0x07 | |
25 | #define SPI_6348_IR_MASK 0x04 | |
26 | #define SPI_6348_IR_STAT 0x02 | |
27 | #define SPI_6348_RX 0x80 | |
28 | #define SPI_6348_RX_SIZE 0x3f | |
29 | #define SPI_6348_TX 0x41 | |
30 | #define SPI_6348_TX_SIZE 0x3f | |
31 | ||
32 | /* BCM6358 SPI core */ | |
33 | #define SPI_6358_CLK 0x706 | |
34 | #define SPI_6358_CMD 0x700 | |
35 | #define SPI_6358_CTL 0x000 | |
36 | #define SPI_6358_CTL_SHIFT 14 | |
37 | #define SPI_6358_FILL 0x707 | |
38 | #define SPI_6358_IR_MASK 0x702 | |
39 | #define SPI_6358_IR_STAT 0x704 | |
40 | #define SPI_6358_RX 0x400 | |
41 | #define SPI_6358_RX_SIZE 0x220 | |
42 | #define SPI_6358_TX 0x002 | |
43 | #define SPI_6358_TX_SIZE 0x21e | |
44 | ||
45 | /* SPI Clock register */ | |
46 | #define SPI_CLK_SHIFT 0 | |
47 | #define SPI_CLK_20MHZ (0 << SPI_CLK_SHIFT) | |
48 | #define SPI_CLK_0_391MHZ (1 << SPI_CLK_SHIFT) | |
49 | #define SPI_CLK_0_781MHZ (2 << SPI_CLK_SHIFT) | |
50 | #define SPI_CLK_1_563MHZ (3 << SPI_CLK_SHIFT) | |
51 | #define SPI_CLK_3_125MHZ (4 << SPI_CLK_SHIFT) | |
52 | #define SPI_CLK_6_250MHZ (5 << SPI_CLK_SHIFT) | |
53 | #define SPI_CLK_12_50MHZ (6 << SPI_CLK_SHIFT) | |
54 | #define SPI_CLK_25MHZ (7 << SPI_CLK_SHIFT) | |
55 | #define SPI_CLK_MASK (7 << SPI_CLK_SHIFT) | |
56 | #define SPI_CLK_SSOFF_SHIFT 3 | |
57 | #define SPI_CLK_SSOFF_2 (2 << SPI_CLK_SSOFF_SHIFT) | |
58 | #define SPI_CLK_SSOFF_MASK (7 << SPI_CLK_SSOFF_SHIFT) | |
59 | #define SPI_CLK_BSWAP_SHIFT 7 | |
60 | #define SPI_CLK_BSWAP_MASK (1 << SPI_CLK_BSWAP_SHIFT) | |
61 | ||
62 | /* SPI Command register */ | |
63 | #define SPI_CMD_OP_SHIFT 0 | |
64 | #define SPI_CMD_OP_START (0x3 << SPI_CMD_OP_SHIFT) | |
65 | #define SPI_CMD_SLAVE_SHIFT 4 | |
66 | #define SPI_CMD_SLAVE_MASK (0xf << SPI_CMD_SLAVE_SHIFT) | |
67 | #define SPI_CMD_PREPEND_SHIFT 8 | |
68 | #define SPI_CMD_PREPEND_BYTES 0xf | |
69 | #define SPI_CMD_3WIRE_SHIFT 12 | |
70 | #define SPI_CMD_3WIRE_MASK (1 << SPI_CMD_3WIRE_SHIFT) | |
71 | ||
72 | /* SPI Control register */ | |
73 | #define SPI_CTL_TYPE_FD_RW 0 | |
74 | #define SPI_CTL_TYPE_HD_W 1 | |
75 | #define SPI_CTL_TYPE_HD_R 2 | |
76 | ||
77 | /* SPI Interrupt registers */ | |
78 | #define SPI_IR_DONE_SHIFT 0 | |
79 | #define SPI_IR_DONE_MASK (1 << SPI_IR_DONE_SHIFT) | |
80 | #define SPI_IR_RXOVER_SHIFT 1 | |
81 | #define SPI_IR_RXOVER_MASK (1 << SPI_IR_RXOVER_SHIFT) | |
82 | #define SPI_IR_TXUNDER_SHIFT 2 | |
83 | #define SPI_IR_TXUNDER_MASK (1 << SPI_IR_TXUNDER_SHIFT) | |
84 | #define SPI_IR_TXOVER_SHIFT 3 | |
85 | #define SPI_IR_TXOVER_MASK (1 << SPI_IR_TXOVER_SHIFT) | |
86 | #define SPI_IR_RXUNDER_SHIFT 4 | |
87 | #define SPI_IR_RXUNDER_MASK (1 << SPI_IR_RXUNDER_SHIFT) | |
88 | #define SPI_IR_CLEAR_MASK (SPI_IR_DONE_MASK |\ | |
89 | SPI_IR_RXOVER_MASK |\ | |
90 | SPI_IR_TXUNDER_MASK |\ | |
91 | SPI_IR_TXOVER_MASK |\ | |
92 | SPI_IR_RXUNDER_MASK) | |
93 | ||
94 | enum bcm63xx_regs_spi { | |
95 | SPI_CLK, | |
96 | SPI_CMD, | |
97 | SPI_CTL, | |
98 | SPI_CTL_SHIFT, | |
99 | SPI_FILL, | |
100 | SPI_IR_MASK, | |
101 | SPI_IR_STAT, | |
102 | SPI_RX, | |
103 | SPI_RX_SIZE, | |
104 | SPI_TX, | |
105 | SPI_TX_SIZE, | |
106 | }; | |
107 | ||
108 | struct bcm63xx_spi_priv { | |
109 | const unsigned long *regs; | |
110 | void __iomem *base; | |
111 | size_t tx_bytes; | |
112 | uint8_t num_cs; | |
113 | }; | |
114 | ||
115 | #define SPI_CLK_CNT 8 | |
116 | static const unsigned bcm63xx_spi_freq_table[SPI_CLK_CNT][2] = { | |
117 | { 25000000, SPI_CLK_25MHZ }, | |
118 | { 20000000, SPI_CLK_20MHZ }, | |
119 | { 12500000, SPI_CLK_12_50MHZ }, | |
120 | { 6250000, SPI_CLK_6_250MHZ }, | |
121 | { 3125000, SPI_CLK_3_125MHZ }, | |
122 | { 1563000, SPI_CLK_1_563MHZ }, | |
123 | { 781000, SPI_CLK_0_781MHZ }, | |
124 | { 391000, SPI_CLK_0_391MHZ } | |
125 | }; | |
126 | ||
127 | static int bcm63xx_spi_cs_info(struct udevice *bus, uint cs, | |
128 | struct spi_cs_info *info) | |
129 | { | |
130 | struct bcm63xx_spi_priv *priv = dev_get_priv(bus); | |
131 | ||
132 | if (cs >= priv->num_cs) { | |
133 | printf("no cs %u\n", cs); | |
4b060003 | 134 | return -EINVAL; |
5ac07d29 ÁFR |
135 | } |
136 | ||
137 | return 0; | |
138 | } | |
139 | ||
140 | static int bcm63xx_spi_set_mode(struct udevice *bus, uint mode) | |
141 | { | |
142 | struct bcm63xx_spi_priv *priv = dev_get_priv(bus); | |
143 | const unsigned long *regs = priv->regs; | |
144 | ||
145 | if (mode & SPI_LSB_FIRST) | |
146 | setbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); | |
147 | else | |
148 | clrbits_8(priv->base + regs[SPI_CLK], SPI_CLK_BSWAP_MASK); | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
153 | static int bcm63xx_spi_set_speed(struct udevice *bus, uint speed) | |
154 | { | |
155 | struct bcm63xx_spi_priv *priv = dev_get_priv(bus); | |
156 | const unsigned long *regs = priv->regs; | |
157 | uint8_t clk_cfg; | |
158 | int i; | |
159 | ||
160 | /* default to lowest clock configuration */ | |
161 | clk_cfg = SPI_CLK_0_391MHZ; | |
162 | ||
163 | /* find the closest clock configuration */ | |
164 | for (i = 0; i < SPI_CLK_CNT; i++) { | |
165 | if (speed >= bcm63xx_spi_freq_table[i][0]) { | |
166 | clk_cfg = bcm63xx_spi_freq_table[i][1]; | |
167 | break; | |
168 | } | |
169 | } | |
170 | ||
171 | /* write clock configuration */ | |
172 | clrsetbits_8(priv->base + regs[SPI_CLK], | |
173 | SPI_CLK_SSOFF_MASK | SPI_CLK_MASK, | |
174 | clk_cfg | SPI_CLK_SSOFF_2); | |
175 | ||
176 | return 0; | |
177 | } | |
178 | ||
179 | /* | |
180 | * BCM63xx SPI driver doesn't allow keeping CS active between transfers since | |
181 | * they are HW controlled. | |
182 | * However, it provides a mechanism to prepend write transfers prior to read | |
183 | * transfers (with a maximum prepend of 15 bytes), which is usually enough for | |
184 | * SPI-connected flashes since reading requires prepending a write transfer of | |
185 | * 5 bytes. | |
186 | * | |
187 | * This implementation takes advantage of the prepend mechanism and combines | |
188 | * multiple transfers into a single one where possible (single/multiple write | |
189 | * transfer(s) followed by a final read/write transfer). | |
190 | * However, it's not possible to buffer reads, which means that read transfers | |
191 | * should always be done as the final ones. | |
192 | * On the other hand, take into account that combining write transfers into | |
193 | * a single one is just buffering and doesn't require prepend mechanism. | |
194 | */ | |
195 | static int bcm63xx_spi_xfer(struct udevice *dev, unsigned int bitlen, | |
196 | const void *dout, void *din, unsigned long flags) | |
197 | { | |
198 | struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); | |
199 | const unsigned long *regs = priv->regs; | |
200 | size_t data_bytes = bitlen / 8; | |
201 | ||
202 | if (flags & SPI_XFER_BEGIN) { | |
203 | /* clear prepends */ | |
204 | priv->tx_bytes = 0; | |
205 | ||
206 | /* initialize hardware */ | |
207 | writeb_be(0, priv->base + regs[SPI_IR_MASK]); | |
208 | } | |
209 | ||
210 | if (din) { | |
211 | /* buffering reads not possible since cs is hw controlled */ | |
212 | if (!(flags & SPI_XFER_END)) { | |
213 | printf("unable to buffer reads\n"); | |
214 | return -EINVAL; | |
215 | } | |
216 | ||
217 | /* check rx size */ | |
218 | if (data_bytes > regs[SPI_RX_SIZE]) { | |
219 | printf("max rx bytes exceeded\n"); | |
220 | return -EMSGSIZE; | |
221 | } | |
222 | } | |
223 | ||
224 | if (dout) { | |
225 | /* check tx size */ | |
226 | if (priv->tx_bytes + data_bytes > regs[SPI_TX_SIZE]) { | |
227 | printf("max tx bytes exceeded\n"); | |
228 | return -EMSGSIZE; | |
229 | } | |
230 | ||
231 | /* copy tx data */ | |
232 | memcpy_toio(priv->base + regs[SPI_TX] + priv->tx_bytes, | |
233 | dout, data_bytes); | |
234 | priv->tx_bytes += data_bytes; | |
235 | } | |
236 | ||
237 | if (flags & SPI_XFER_END) { | |
8a8d24bd | 238 | struct dm_spi_slave_plat *plat = |
caa4daa2 | 239 | dev_get_parent_plat(dev); |
5ac07d29 ÁFR |
240 | uint16_t val, cmd; |
241 | int ret; | |
242 | ||
243 | /* determine control config */ | |
244 | if (dout && !din) { | |
245 | /* buffered write transfers */ | |
246 | val = priv->tx_bytes; | |
247 | val |= (SPI_CTL_TYPE_HD_W << regs[SPI_CTL_SHIFT]); | |
248 | priv->tx_bytes = 0; | |
249 | } else { | |
250 | if (dout && din && (flags & SPI_XFER_ONCE)) { | |
251 | /* full duplex read/write */ | |
252 | val = data_bytes; | |
253 | val |= (SPI_CTL_TYPE_FD_RW << | |
254 | regs[SPI_CTL_SHIFT]); | |
255 | priv->tx_bytes = 0; | |
256 | } else { | |
257 | /* prepended write transfer */ | |
258 | val = data_bytes; | |
259 | val |= (SPI_CTL_TYPE_HD_R << | |
260 | regs[SPI_CTL_SHIFT]); | |
261 | if (priv->tx_bytes > SPI_CMD_PREPEND_BYTES) { | |
262 | printf("max prepend bytes exceeded\n"); | |
263 | return -EMSGSIZE; | |
264 | } | |
265 | } | |
266 | } | |
267 | ||
268 | if (regs[SPI_CTL_SHIFT] >= 8) | |
269 | writew_be(val, priv->base + regs[SPI_CTL]); | |
270 | else | |
271 | writeb_be(val, priv->base + regs[SPI_CTL]); | |
272 | ||
273 | /* clear interrupts */ | |
274 | writeb_be(SPI_IR_CLEAR_MASK, priv->base + regs[SPI_IR_STAT]); | |
275 | ||
276 | /* issue the transfer */ | |
277 | cmd = SPI_CMD_OP_START; | |
278 | cmd |= (plat->cs << SPI_CMD_SLAVE_SHIFT) & SPI_CMD_SLAVE_MASK; | |
279 | cmd |= (priv->tx_bytes << SPI_CMD_PREPEND_SHIFT); | |
280 | if (plat->mode & SPI_3WIRE) | |
281 | cmd |= SPI_CMD_3WIRE_MASK; | |
282 | writew_be(cmd, priv->base + regs[SPI_CMD]); | |
283 | ||
284 | /* enable interrupts */ | |
285 | writeb_be(SPI_IR_DONE_MASK, priv->base + regs[SPI_IR_MASK]); | |
286 | ||
287 | ret = wait_for_bit_8(priv->base + regs[SPI_IR_STAT], | |
288 | SPI_IR_DONE_MASK, true, 1000, false); | |
289 | if (ret) { | |
290 | printf("interrupt timeout\n"); | |
291 | return ret; | |
292 | } | |
293 | ||
294 | /* copy rx data */ | |
295 | if (din) | |
296 | memcpy_fromio(din, priv->base + regs[SPI_RX], | |
297 | data_bytes); | |
298 | } | |
299 | ||
300 | return 0; | |
301 | } | |
302 | ||
303 | static const struct dm_spi_ops bcm63xx_spi_ops = { | |
304 | .cs_info = bcm63xx_spi_cs_info, | |
305 | .set_mode = bcm63xx_spi_set_mode, | |
306 | .set_speed = bcm63xx_spi_set_speed, | |
307 | .xfer = bcm63xx_spi_xfer, | |
308 | }; | |
309 | ||
310 | static const unsigned long bcm6348_spi_regs[] = { | |
311 | [SPI_CLK] = SPI_6348_CLK, | |
312 | [SPI_CMD] = SPI_6348_CMD, | |
313 | [SPI_CTL] = SPI_6348_CTL, | |
314 | [SPI_CTL_SHIFT] = SPI_6348_CTL_SHIFT, | |
315 | [SPI_FILL] = SPI_6348_FILL, | |
316 | [SPI_IR_MASK] = SPI_6348_IR_MASK, | |
317 | [SPI_IR_STAT] = SPI_6348_IR_STAT, | |
318 | [SPI_RX] = SPI_6348_RX, | |
319 | [SPI_RX_SIZE] = SPI_6348_RX_SIZE, | |
320 | [SPI_TX] = SPI_6348_TX, | |
321 | [SPI_TX_SIZE] = SPI_6348_TX_SIZE, | |
322 | }; | |
323 | ||
324 | static const unsigned long bcm6358_spi_regs[] = { | |
325 | [SPI_CLK] = SPI_6358_CLK, | |
326 | [SPI_CMD] = SPI_6358_CMD, | |
327 | [SPI_CTL] = SPI_6358_CTL, | |
328 | [SPI_CTL_SHIFT] = SPI_6358_CTL_SHIFT, | |
329 | [SPI_FILL] = SPI_6358_FILL, | |
330 | [SPI_IR_MASK] = SPI_6358_IR_MASK, | |
331 | [SPI_IR_STAT] = SPI_6358_IR_STAT, | |
332 | [SPI_RX] = SPI_6358_RX, | |
333 | [SPI_RX_SIZE] = SPI_6358_RX_SIZE, | |
334 | [SPI_TX] = SPI_6358_TX, | |
335 | [SPI_TX_SIZE] = SPI_6358_TX_SIZE, | |
336 | }; | |
337 | ||
338 | static const struct udevice_id bcm63xx_spi_ids[] = { | |
339 | { | |
340 | .compatible = "brcm,bcm6348-spi", | |
341 | .data = (ulong)&bcm6348_spi_regs, | |
342 | }, { | |
343 | .compatible = "brcm,bcm6358-spi", | |
344 | .data = (ulong)&bcm6358_spi_regs, | |
345 | }, { /* sentinel */ } | |
346 | }; | |
347 | ||
348 | static int bcm63xx_spi_child_pre_probe(struct udevice *dev) | |
349 | { | |
350 | struct bcm63xx_spi_priv *priv = dev_get_priv(dev->parent); | |
351 | const unsigned long *regs = priv->regs; | |
352 | struct spi_slave *slave = dev_get_parent_priv(dev); | |
8a8d24bd | 353 | struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev); |
5ac07d29 ÁFR |
354 | |
355 | /* check cs */ | |
356 | if (plat->cs >= priv->num_cs) { | |
357 | printf("no cs %u\n", plat->cs); | |
358 | return -ENODEV; | |
359 | } | |
360 | ||
361 | /* max read/write sizes */ | |
362 | slave->max_read_size = regs[SPI_RX_SIZE]; | |
363 | slave->max_write_size = regs[SPI_TX_SIZE]; | |
364 | ||
365 | return 0; | |
366 | } | |
367 | ||
368 | static int bcm63xx_spi_probe(struct udevice *dev) | |
369 | { | |
370 | struct bcm63xx_spi_priv *priv = dev_get_priv(dev); | |
371 | const unsigned long *regs = | |
372 | (const unsigned long *)dev_get_driver_data(dev); | |
373 | struct reset_ctl rst_ctl; | |
374 | struct clk clk; | |
5ac07d29 ÁFR |
375 | int ret; |
376 | ||
85e1ddba ÁFR |
377 | priv->base = dev_remap_addr(dev); |
378 | if (!priv->base) | |
5ac07d29 ÁFR |
379 | return -EINVAL; |
380 | ||
381 | priv->regs = regs; | |
85e1ddba | 382 | priv->num_cs = dev_read_u32_default(dev, "num-cs", 8); |
5ac07d29 ÁFR |
383 | |
384 | /* enable clock */ | |
385 | ret = clk_get_by_index(dev, 0, &clk); | |
386 | if (ret < 0) | |
387 | return ret; | |
388 | ||
389 | ret = clk_enable(&clk); | |
390 | if (ret < 0) | |
391 | return ret; | |
392 | ||
5ac07d29 ÁFR |
393 | /* perform reset */ |
394 | ret = reset_get_by_index(dev, 0, &rst_ctl); | |
395 | if (ret < 0) | |
396 | return ret; | |
397 | ||
398 | ret = reset_deassert(&rst_ctl); | |
399 | if (ret < 0) | |
400 | return ret; | |
401 | ||
402 | ret = reset_free(&rst_ctl); | |
403 | if (ret < 0) | |
404 | return ret; | |
405 | ||
406 | /* initialize hardware */ | |
407 | writeb_be(0, priv->base + regs[SPI_IR_MASK]); | |
408 | ||
409 | /* set fill register */ | |
410 | writeb_be(0xff, priv->base + regs[SPI_FILL]); | |
411 | ||
412 | return 0; | |
413 | } | |
414 | ||
415 | U_BOOT_DRIVER(bcm63xx_spi) = { | |
416 | .name = "bcm63xx_spi", | |
417 | .id = UCLASS_SPI, | |
418 | .of_match = bcm63xx_spi_ids, | |
419 | .ops = &bcm63xx_spi_ops, | |
41575d8e | 420 | .priv_auto = sizeof(struct bcm63xx_spi_priv), |
5ac07d29 ÁFR |
421 | .child_pre_probe = bcm63xx_spi_child_pre_probe, |
422 | .probe = bcm63xx_spi_probe, | |
423 | }; |