]>
Commit | Line | Data |
---|---|---|
5bef6fd7 SR |
1 | /* |
2 | * Designware master SPI core controller driver | |
3 | * | |
4 | * Copyright (C) 2014 Stefan Roese <sr@denx.de> | |
5 | * | |
a72f8020 SR |
6 | * Very loosely based on the Linux driver: |
7 | * drivers/spi/spi-dw.c, which is: | |
5bef6fd7 SR |
8 | * Copyright (c) 2009, Intel Corporation. |
9 | * | |
10 | * SPDX-License-Identifier: GPL-2.0 | |
11 | */ | |
12 | ||
13 | #include <common.h> | |
58c125b9 | 14 | #include <clk.h> |
5bef6fd7 SR |
15 | #include <dm.h> |
16 | #include <errno.h> | |
17 | #include <malloc.h> | |
18 | #include <spi.h> | |
19 | #include <fdtdec.h> | |
20 | #include <linux/compat.h> | |
21 | #include <asm/io.h> | |
22 | ||
23 | DECLARE_GLOBAL_DATA_PTR; | |
24 | ||
25 | /* Register offsets */ | |
26 | #define DW_SPI_CTRL0 0x00 | |
27 | #define DW_SPI_CTRL1 0x04 | |
28 | #define DW_SPI_SSIENR 0x08 | |
29 | #define DW_SPI_MWCR 0x0c | |
30 | #define DW_SPI_SER 0x10 | |
31 | #define DW_SPI_BAUDR 0x14 | |
32 | #define DW_SPI_TXFLTR 0x18 | |
33 | #define DW_SPI_RXFLTR 0x1c | |
34 | #define DW_SPI_TXFLR 0x20 | |
35 | #define DW_SPI_RXFLR 0x24 | |
36 | #define DW_SPI_SR 0x28 | |
37 | #define DW_SPI_IMR 0x2c | |
38 | #define DW_SPI_ISR 0x30 | |
39 | #define DW_SPI_RISR 0x34 | |
40 | #define DW_SPI_TXOICR 0x38 | |
41 | #define DW_SPI_RXOICR 0x3c | |
42 | #define DW_SPI_RXUICR 0x40 | |
43 | #define DW_SPI_MSTICR 0x44 | |
44 | #define DW_SPI_ICR 0x48 | |
45 | #define DW_SPI_DMACR 0x4c | |
46 | #define DW_SPI_DMATDLR 0x50 | |
47 | #define DW_SPI_DMARDLR 0x54 | |
48 | #define DW_SPI_IDR 0x58 | |
49 | #define DW_SPI_VERSION 0x5c | |
50 | #define DW_SPI_DR 0x60 | |
51 | ||
52 | /* Bit fields in CTRLR0 */ | |
53 | #define SPI_DFS_OFFSET 0 | |
54 | ||
55 | #define SPI_FRF_OFFSET 4 | |
56 | #define SPI_FRF_SPI 0x0 | |
57 | #define SPI_FRF_SSP 0x1 | |
58 | #define SPI_FRF_MICROWIRE 0x2 | |
59 | #define SPI_FRF_RESV 0x3 | |
60 | ||
61 | #define SPI_MODE_OFFSET 6 | |
62 | #define SPI_SCPH_OFFSET 6 | |
63 | #define SPI_SCOL_OFFSET 7 | |
64 | ||
65 | #define SPI_TMOD_OFFSET 8 | |
66 | #define SPI_TMOD_MASK (0x3 << SPI_TMOD_OFFSET) | |
67 | #define SPI_TMOD_TR 0x0 /* xmit & recv */ | |
68 | #define SPI_TMOD_TO 0x1 /* xmit only */ | |
69 | #define SPI_TMOD_RO 0x2 /* recv only */ | |
70 | #define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */ | |
71 | ||
72 | #define SPI_SLVOE_OFFSET 10 | |
73 | #define SPI_SRL_OFFSET 11 | |
74 | #define SPI_CFS_OFFSET 12 | |
75 | ||
76 | /* Bit fields in SR, 7 bits */ | |
95e77d90 | 77 | #define SR_MASK GENMASK(6, 0) /* cover 7 bits */ |
431a9f02 JT |
78 | #define SR_BUSY BIT(0) |
79 | #define SR_TF_NOT_FULL BIT(1) | |
80 | #define SR_TF_EMPT BIT(2) | |
81 | #define SR_RF_NOT_EMPT BIT(3) | |
82 | #define SR_RF_FULL BIT(4) | |
83 | #define SR_TX_ERR BIT(5) | |
84 | #define SR_DCOL BIT(6) | |
5bef6fd7 | 85 | |
a72f8020 | 86 | #define RX_TIMEOUT 1000 /* timeout in ms */ |
5bef6fd7 SR |
87 | |
88 | struct dw_spi_platdata { | |
89 | s32 frequency; /* Default clock frequency, -1 for none */ | |
90 | void __iomem *regs; | |
91 | }; | |
92 | ||
93 | struct dw_spi_priv { | |
94 | void __iomem *regs; | |
95 | unsigned int freq; /* Default frequency */ | |
96 | unsigned int mode; | |
58c125b9 EP |
97 | struct clk clk; |
98 | unsigned long bus_clk_rate; | |
5bef6fd7 SR |
99 | |
100 | int bits_per_word; | |
101 | u8 cs; /* chip select pin */ | |
5bef6fd7 SR |
102 | u8 tmode; /* TR/TO/RO/EEPROM */ |
103 | u8 type; /* SPI/SSP/MicroWire */ | |
104 | int len; | |
105 | ||
106 | u32 fifo_len; /* depth of the FIFO buffer */ | |
107 | void *tx; | |
108 | void *tx_end; | |
109 | void *rx; | |
110 | void *rx_end; | |
111 | }; | |
112 | ||
113 | static inline u32 dw_readl(struct dw_spi_priv *priv, u32 offset) | |
114 | { | |
115 | return __raw_readl(priv->regs + offset); | |
116 | } | |
117 | ||
118 | static inline void dw_writel(struct dw_spi_priv *priv, u32 offset, u32 val) | |
119 | { | |
120 | __raw_writel(val, priv->regs + offset); | |
121 | } | |
122 | ||
123 | static inline u16 dw_readw(struct dw_spi_priv *priv, u32 offset) | |
124 | { | |
125 | return __raw_readw(priv->regs + offset); | |
126 | } | |
127 | ||
128 | static inline void dw_writew(struct dw_spi_priv *priv, u32 offset, u16 val) | |
129 | { | |
130 | __raw_writew(val, priv->regs + offset); | |
131 | } | |
132 | ||
133 | static int dw_spi_ofdata_to_platdata(struct udevice *bus) | |
134 | { | |
135 | struct dw_spi_platdata *plat = bus->platdata; | |
136 | const void *blob = gd->fdt_blob; | |
e160f7d4 | 137 | int node = dev_of_offset(bus); |
5bef6fd7 | 138 | |
a821c4af | 139 | plat->regs = (struct dw_spi *)devfdt_get_addr(bus); |
5bef6fd7 SR |
140 | |
141 | /* Use 500KHz as a suitable default */ | |
142 | plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", | |
143 | 500000); | |
144 | debug("%s: regs=%p max-frequency=%d\n", __func__, plat->regs, | |
145 | plat->frequency); | |
146 | ||
147 | return 0; | |
148 | } | |
149 | ||
150 | static inline void spi_enable_chip(struct dw_spi_priv *priv, int enable) | |
151 | { | |
152 | dw_writel(priv, DW_SPI_SSIENR, (enable ? 1 : 0)); | |
153 | } | |
154 | ||
155 | /* Restart the controller, disable all interrupts, clean rx fifo */ | |
156 | static void spi_hw_init(struct dw_spi_priv *priv) | |
157 | { | |
158 | spi_enable_chip(priv, 0); | |
159 | dw_writel(priv, DW_SPI_IMR, 0xff); | |
160 | spi_enable_chip(priv, 1); | |
161 | ||
162 | /* | |
163 | * Try to detect the FIFO depth if not set by interface driver, | |
164 | * the depth could be from 2 to 256 from HW spec | |
165 | */ | |
166 | if (!priv->fifo_len) { | |
167 | u32 fifo; | |
168 | ||
52091ad1 | 169 | for (fifo = 1; fifo < 256; fifo++) { |
5bef6fd7 SR |
170 | dw_writew(priv, DW_SPI_TXFLTR, fifo); |
171 | if (fifo != dw_readw(priv, DW_SPI_TXFLTR)) | |
172 | break; | |
173 | } | |
174 | ||
52091ad1 | 175 | priv->fifo_len = (fifo == 1) ? 0 : fifo; |
5bef6fd7 SR |
176 | dw_writew(priv, DW_SPI_TXFLTR, 0); |
177 | } | |
178 | debug("%s: fifo_len=%d\n", __func__, priv->fifo_len); | |
179 | } | |
180 | ||
58c125b9 EP |
181 | /* |
182 | * We define dw_spi_get_clk function as 'weak' as some targets | |
183 | * (like SOCFPGA_GEN5 and SOCFPGA_ARRIA10) don't use standard clock API | |
184 | * and implement dw_spi_get_clk their own way in their clock manager. | |
185 | */ | |
186 | __weak int dw_spi_get_clk(struct udevice *bus, ulong *rate) | |
187 | { | |
188 | struct dw_spi_priv *priv = dev_get_priv(bus); | |
189 | int ret; | |
190 | ||
191 | ret = clk_get_by_index(bus, 0, &priv->clk); | |
192 | if (ret) | |
193 | return ret; | |
194 | ||
195 | ret = clk_enable(&priv->clk); | |
196 | if (ret && ret != -ENOSYS && ret != -ENOTSUPP) | |
197 | return ret; | |
198 | ||
199 | *rate = clk_get_rate(&priv->clk); | |
200 | if (!*rate) | |
201 | goto err_rate; | |
202 | ||
203 | debug("%s: get spi controller clk via device tree: %lu Hz\n", | |
204 | __func__, *rate); | |
205 | ||
206 | return 0; | |
207 | ||
208 | err_rate: | |
209 | clk_disable(&priv->clk); | |
210 | clk_free(&priv->clk); | |
211 | ||
212 | return -EINVAL; | |
213 | } | |
214 | ||
5bef6fd7 SR |
215 | static int dw_spi_probe(struct udevice *bus) |
216 | { | |
217 | struct dw_spi_platdata *plat = dev_get_platdata(bus); | |
218 | struct dw_spi_priv *priv = dev_get_priv(bus); | |
58c125b9 | 219 | int ret; |
5bef6fd7 SR |
220 | |
221 | priv->regs = plat->regs; | |
222 | priv->freq = plat->frequency; | |
223 | ||
58c125b9 EP |
224 | ret = dw_spi_get_clk(bus, &priv->bus_clk_rate); |
225 | if (ret) | |
226 | return ret; | |
227 | ||
5bef6fd7 SR |
228 | /* Currently only bits_per_word == 8 supported */ |
229 | priv->bits_per_word = 8; | |
5bef6fd7 SR |
230 | |
231 | priv->tmode = 0; /* Tx & Rx */ | |
232 | ||
233 | /* Basic HW init */ | |
234 | spi_hw_init(priv); | |
235 | ||
236 | return 0; | |
237 | } | |
238 | ||
239 | /* Return the max entries we can fill into tx fifo */ | |
240 | static inline u32 tx_max(struct dw_spi_priv *priv) | |
241 | { | |
242 | u32 tx_left, tx_room, rxtx_gap; | |
243 | ||
a72f8020 | 244 | tx_left = (priv->tx_end - priv->tx) / (priv->bits_per_word >> 3); |
5bef6fd7 SR |
245 | tx_room = priv->fifo_len - dw_readw(priv, DW_SPI_TXFLR); |
246 | ||
247 | /* | |
248 | * Another concern is about the tx/rx mismatch, we | |
a72f8020 | 249 | * thought about using (priv->fifo_len - rxflr - txflr) as |
5bef6fd7 SR |
250 | * one maximum value for tx, but it doesn't cover the |
251 | * data which is out of tx/rx fifo and inside the | |
252 | * shift registers. So a control from sw point of | |
253 | * view is taken. | |
254 | */ | |
255 | rxtx_gap = ((priv->rx_end - priv->rx) - (priv->tx_end - priv->tx)) / | |
a72f8020 | 256 | (priv->bits_per_word >> 3); |
5bef6fd7 SR |
257 | |
258 | return min3(tx_left, tx_room, (u32)(priv->fifo_len - rxtx_gap)); | |
259 | } | |
260 | ||
261 | /* Return the max entries we should read out of rx fifo */ | |
262 | static inline u32 rx_max(struct dw_spi_priv *priv) | |
263 | { | |
a72f8020 | 264 | u32 rx_left = (priv->rx_end - priv->rx) / (priv->bits_per_word >> 3); |
5bef6fd7 SR |
265 | |
266 | return min_t(u32, rx_left, dw_readw(priv, DW_SPI_RXFLR)); | |
267 | } | |
268 | ||
269 | static void dw_writer(struct dw_spi_priv *priv) | |
270 | { | |
271 | u32 max = tx_max(priv); | |
272 | u16 txw = 0; | |
273 | ||
274 | while (max--) { | |
275 | /* Set the tx word if the transfer's original "tx" is not null */ | |
276 | if (priv->tx_end - priv->len) { | |
a72f8020 | 277 | if (priv->bits_per_word == 8) |
5bef6fd7 SR |
278 | txw = *(u8 *)(priv->tx); |
279 | else | |
280 | txw = *(u16 *)(priv->tx); | |
281 | } | |
282 | dw_writew(priv, DW_SPI_DR, txw); | |
283 | debug("%s: tx=0x%02x\n", __func__, txw); | |
a72f8020 | 284 | priv->tx += priv->bits_per_word >> 3; |
5bef6fd7 SR |
285 | } |
286 | } | |
287 | ||
288 | static int dw_reader(struct dw_spi_priv *priv) | |
289 | { | |
290 | unsigned start = get_timer(0); | |
291 | u32 max; | |
292 | u16 rxw; | |
293 | ||
294 | /* Wait for rx data to be ready */ | |
295 | while (rx_max(priv) == 0) { | |
296 | if (get_timer(start) > RX_TIMEOUT) | |
297 | return -ETIMEDOUT; | |
298 | } | |
299 | ||
300 | max = rx_max(priv); | |
301 | ||
302 | while (max--) { | |
303 | rxw = dw_readw(priv, DW_SPI_DR); | |
304 | debug("%s: rx=0x%02x\n", __func__, rxw); | |
a72f8020 SR |
305 | |
306 | /* | |
307 | * Care about rx only if the transfer's original "rx" is | |
308 | * not null | |
309 | */ | |
5bef6fd7 | 310 | if (priv->rx_end - priv->len) { |
a72f8020 | 311 | if (priv->bits_per_word == 8) |
5bef6fd7 SR |
312 | *(u8 *)(priv->rx) = rxw; |
313 | else | |
314 | *(u16 *)(priv->rx) = rxw; | |
315 | } | |
a72f8020 | 316 | priv->rx += priv->bits_per_word >> 3; |
5bef6fd7 SR |
317 | } |
318 | ||
319 | return 0; | |
320 | } | |
321 | ||
322 | static int poll_transfer(struct dw_spi_priv *priv) | |
323 | { | |
324 | int ret; | |
325 | ||
326 | do { | |
327 | dw_writer(priv); | |
328 | ret = dw_reader(priv); | |
329 | if (ret < 0) | |
330 | return ret; | |
331 | } while (priv->rx_end > priv->rx); | |
332 | ||
333 | return 0; | |
334 | } | |
335 | ||
336 | static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen, | |
337 | const void *dout, void *din, unsigned long flags) | |
338 | { | |
339 | struct udevice *bus = dev->parent; | |
340 | struct dw_spi_priv *priv = dev_get_priv(bus); | |
341 | const u8 *tx = dout; | |
342 | u8 *rx = din; | |
343 | int ret = 0; | |
344 | u32 cr0 = 0; | |
5bef6fd7 SR |
345 | u32 cs; |
346 | ||
347 | /* spi core configured to do 8 bit transfers */ | |
348 | if (bitlen % 8) { | |
349 | debug("Non byte aligned SPI transfer.\n"); | |
350 | return -1; | |
351 | } | |
352 | ||
a72f8020 | 353 | cr0 = (priv->bits_per_word - 1) | (priv->type << SPI_FRF_OFFSET) | |
5bef6fd7 SR |
354 | (priv->mode << SPI_MODE_OFFSET) | |
355 | (priv->tmode << SPI_TMOD_OFFSET); | |
356 | ||
357 | if (rx && tx) | |
358 | priv->tmode = SPI_TMOD_TR; | |
359 | else if (rx) | |
360 | priv->tmode = SPI_TMOD_RO; | |
361 | else | |
362 | priv->tmode = SPI_TMOD_TO; | |
363 | ||
364 | cr0 &= ~SPI_TMOD_MASK; | |
365 | cr0 |= (priv->tmode << SPI_TMOD_OFFSET); | |
366 | ||
a72f8020 | 367 | priv->len = bitlen >> 3; |
5bef6fd7 SR |
368 | debug("%s: rx=%p tx=%p len=%d [bytes]\n", __func__, rx, tx, priv->len); |
369 | ||
370 | priv->tx = (void *)tx; | |
371 | priv->tx_end = priv->tx + priv->len; | |
372 | priv->rx = rx; | |
373 | priv->rx_end = priv->rx + priv->len; | |
374 | ||
375 | /* Disable controller before writing control registers */ | |
376 | spi_enable_chip(priv, 0); | |
377 | ||
378 | debug("%s: cr0=%08x\n", __func__, cr0); | |
379 | /* Reprogram cr0 only if changed */ | |
380 | if (dw_readw(priv, DW_SPI_CTRL0) != cr0) | |
381 | dw_writew(priv, DW_SPI_CTRL0, cr0); | |
382 | ||
383 | /* | |
384 | * Configure the desired SS (slave select 0...3) in the controller | |
385 | * The DW SPI controller will activate and deactivate this CS | |
386 | * automatically. So no cs_activate() etc is needed in this driver. | |
387 | */ | |
388 | cs = spi_chip_select(dev); | |
389 | dw_writel(priv, DW_SPI_SER, 1 << cs); | |
390 | ||
391 | /* Enable controller after writing control registers */ | |
392 | spi_enable_chip(priv, 1); | |
393 | ||
394 | /* Start transfer in a polling loop */ | |
395 | ret = poll_transfer(priv); | |
396 | ||
397 | return ret; | |
398 | } | |
399 | ||
400 | static int dw_spi_set_speed(struct udevice *bus, uint speed) | |
401 | { | |
402 | struct dw_spi_platdata *plat = bus->platdata; | |
403 | struct dw_spi_priv *priv = dev_get_priv(bus); | |
404 | u16 clk_div; | |
405 | ||
406 | if (speed > plat->frequency) | |
407 | speed = plat->frequency; | |
408 | ||
409 | /* Disable controller before writing control registers */ | |
410 | spi_enable_chip(priv, 0); | |
411 | ||
412 | /* clk_div doesn't support odd number */ | |
58c125b9 | 413 | clk_div = priv->bus_clk_rate / speed; |
5bef6fd7 SR |
414 | clk_div = (clk_div + 1) & 0xfffe; |
415 | dw_writel(priv, DW_SPI_BAUDR, clk_div); | |
416 | ||
417 | /* Enable controller after writing control registers */ | |
418 | spi_enable_chip(priv, 1); | |
419 | ||
420 | priv->freq = speed; | |
421 | debug("%s: regs=%p speed=%d clk_div=%d\n", __func__, priv->regs, | |
422 | priv->freq, clk_div); | |
423 | ||
424 | return 0; | |
425 | } | |
426 | ||
427 | static int dw_spi_set_mode(struct udevice *bus, uint mode) | |
428 | { | |
429 | struct dw_spi_priv *priv = dev_get_priv(bus); | |
430 | ||
431 | /* | |
432 | * Can't set mode yet. Since this depends on if rx, tx, or | |
433 | * rx & tx is requested. So we have to defer this to the | |
434 | * real transfer function. | |
435 | */ | |
436 | priv->mode = mode; | |
437 | debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode); | |
438 | ||
439 | return 0; | |
440 | } | |
441 | ||
442 | static const struct dm_spi_ops dw_spi_ops = { | |
443 | .xfer = dw_spi_xfer, | |
444 | .set_speed = dw_spi_set_speed, | |
445 | .set_mode = dw_spi_set_mode, | |
446 | /* | |
447 | * cs_info is not needed, since we require all chip selects to be | |
448 | * in the device tree explicitly | |
449 | */ | |
450 | }; | |
451 | ||
452 | static const struct udevice_id dw_spi_ids[] = { | |
74114862 | 453 | { .compatible = "snps,dw-apb-ssi" }, |
5bef6fd7 SR |
454 | { } |
455 | }; | |
456 | ||
457 | U_BOOT_DRIVER(dw_spi) = { | |
458 | .name = "dw_spi", | |
459 | .id = UCLASS_SPI, | |
460 | .of_match = dw_spi_ids, | |
461 | .ops = &dw_spi_ops, | |
462 | .ofdata_to_platdata = dw_spi_ofdata_to_platdata, | |
463 | .platdata_auto_alloc_size = sizeof(struct dw_spi_platdata), | |
464 | .priv_auto_alloc_size = sizeof(struct dw_spi_priv), | |
5bef6fd7 SR |
465 | .probe = dw_spi_probe, |
466 | }; |