]>
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> | |
14 | #include <dm.h> | |
15 | #include <errno.h> | |
16 | #include <malloc.h> | |
17 | #include <spi.h> | |
18 | #include <fdtdec.h> | |
19 | #include <linux/compat.h> | |
20 | #include <asm/io.h> | |
a72f8020 | 21 | #include <asm/arch/clock_manager.h> |
5bef6fd7 SR |
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; | |
97 | ||
98 | int bits_per_word; | |
99 | u8 cs; /* chip select pin */ | |
5bef6fd7 SR |
100 | u8 tmode; /* TR/TO/RO/EEPROM */ |
101 | u8 type; /* SPI/SSP/MicroWire */ | |
102 | int len; | |
103 | ||
104 | u32 fifo_len; /* depth of the FIFO buffer */ | |
105 | void *tx; | |
106 | void *tx_end; | |
107 | void *rx; | |
108 | void *rx_end; | |
109 | }; | |
110 | ||
111 | static inline u32 dw_readl(struct dw_spi_priv *priv, u32 offset) | |
112 | { | |
113 | return __raw_readl(priv->regs + offset); | |
114 | } | |
115 | ||
116 | static inline void dw_writel(struct dw_spi_priv *priv, u32 offset, u32 val) | |
117 | { | |
118 | __raw_writel(val, priv->regs + offset); | |
119 | } | |
120 | ||
121 | static inline u16 dw_readw(struct dw_spi_priv *priv, u32 offset) | |
122 | { | |
123 | return __raw_readw(priv->regs + offset); | |
124 | } | |
125 | ||
126 | static inline void dw_writew(struct dw_spi_priv *priv, u32 offset, u16 val) | |
127 | { | |
128 | __raw_writew(val, priv->regs + offset); | |
129 | } | |
130 | ||
131 | static int dw_spi_ofdata_to_platdata(struct udevice *bus) | |
132 | { | |
133 | struct dw_spi_platdata *plat = bus->platdata; | |
134 | const void *blob = gd->fdt_blob; | |
e160f7d4 | 135 | int node = dev_of_offset(bus); |
5bef6fd7 | 136 | |
a821c4af | 137 | plat->regs = (struct dw_spi *)devfdt_get_addr(bus); |
5bef6fd7 SR |
138 | |
139 | /* Use 500KHz as a suitable default */ | |
140 | plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", | |
141 | 500000); | |
142 | debug("%s: regs=%p max-frequency=%d\n", __func__, plat->regs, | |
143 | plat->frequency); | |
144 | ||
145 | return 0; | |
146 | } | |
147 | ||
148 | static inline void spi_enable_chip(struct dw_spi_priv *priv, int enable) | |
149 | { | |
150 | dw_writel(priv, DW_SPI_SSIENR, (enable ? 1 : 0)); | |
151 | } | |
152 | ||
153 | /* Restart the controller, disable all interrupts, clean rx fifo */ | |
154 | static void spi_hw_init(struct dw_spi_priv *priv) | |
155 | { | |
156 | spi_enable_chip(priv, 0); | |
157 | dw_writel(priv, DW_SPI_IMR, 0xff); | |
158 | spi_enable_chip(priv, 1); | |
159 | ||
160 | /* | |
161 | * Try to detect the FIFO depth if not set by interface driver, | |
162 | * the depth could be from 2 to 256 from HW spec | |
163 | */ | |
164 | if (!priv->fifo_len) { | |
165 | u32 fifo; | |
166 | ||
52091ad1 | 167 | for (fifo = 1; fifo < 256; fifo++) { |
5bef6fd7 SR |
168 | dw_writew(priv, DW_SPI_TXFLTR, fifo); |
169 | if (fifo != dw_readw(priv, DW_SPI_TXFLTR)) | |
170 | break; | |
171 | } | |
172 | ||
52091ad1 | 173 | priv->fifo_len = (fifo == 1) ? 0 : fifo; |
5bef6fd7 SR |
174 | dw_writew(priv, DW_SPI_TXFLTR, 0); |
175 | } | |
176 | debug("%s: fifo_len=%d\n", __func__, priv->fifo_len); | |
177 | } | |
178 | ||
179 | static int dw_spi_probe(struct udevice *bus) | |
180 | { | |
181 | struct dw_spi_platdata *plat = dev_get_platdata(bus); | |
182 | struct dw_spi_priv *priv = dev_get_priv(bus); | |
183 | ||
184 | priv->regs = plat->regs; | |
185 | priv->freq = plat->frequency; | |
186 | ||
187 | /* Currently only bits_per_word == 8 supported */ | |
188 | priv->bits_per_word = 8; | |
5bef6fd7 SR |
189 | |
190 | priv->tmode = 0; /* Tx & Rx */ | |
191 | ||
192 | /* Basic HW init */ | |
193 | spi_hw_init(priv); | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | /* Return the max entries we can fill into tx fifo */ | |
199 | static inline u32 tx_max(struct dw_spi_priv *priv) | |
200 | { | |
201 | u32 tx_left, tx_room, rxtx_gap; | |
202 | ||
a72f8020 | 203 | tx_left = (priv->tx_end - priv->tx) / (priv->bits_per_word >> 3); |
5bef6fd7 SR |
204 | tx_room = priv->fifo_len - dw_readw(priv, DW_SPI_TXFLR); |
205 | ||
206 | /* | |
207 | * Another concern is about the tx/rx mismatch, we | |
a72f8020 | 208 | * thought about using (priv->fifo_len - rxflr - txflr) as |
5bef6fd7 SR |
209 | * one maximum value for tx, but it doesn't cover the |
210 | * data which is out of tx/rx fifo and inside the | |
211 | * shift registers. So a control from sw point of | |
212 | * view is taken. | |
213 | */ | |
214 | rxtx_gap = ((priv->rx_end - priv->rx) - (priv->tx_end - priv->tx)) / | |
a72f8020 | 215 | (priv->bits_per_word >> 3); |
5bef6fd7 SR |
216 | |
217 | return min3(tx_left, tx_room, (u32)(priv->fifo_len - rxtx_gap)); | |
218 | } | |
219 | ||
220 | /* Return the max entries we should read out of rx fifo */ | |
221 | static inline u32 rx_max(struct dw_spi_priv *priv) | |
222 | { | |
a72f8020 | 223 | u32 rx_left = (priv->rx_end - priv->rx) / (priv->bits_per_word >> 3); |
5bef6fd7 SR |
224 | |
225 | return min_t(u32, rx_left, dw_readw(priv, DW_SPI_RXFLR)); | |
226 | } | |
227 | ||
228 | static void dw_writer(struct dw_spi_priv *priv) | |
229 | { | |
230 | u32 max = tx_max(priv); | |
231 | u16 txw = 0; | |
232 | ||
233 | while (max--) { | |
234 | /* Set the tx word if the transfer's original "tx" is not null */ | |
235 | if (priv->tx_end - priv->len) { | |
a72f8020 | 236 | if (priv->bits_per_word == 8) |
5bef6fd7 SR |
237 | txw = *(u8 *)(priv->tx); |
238 | else | |
239 | txw = *(u16 *)(priv->tx); | |
240 | } | |
241 | dw_writew(priv, DW_SPI_DR, txw); | |
242 | debug("%s: tx=0x%02x\n", __func__, txw); | |
a72f8020 | 243 | priv->tx += priv->bits_per_word >> 3; |
5bef6fd7 SR |
244 | } |
245 | } | |
246 | ||
247 | static int dw_reader(struct dw_spi_priv *priv) | |
248 | { | |
249 | unsigned start = get_timer(0); | |
250 | u32 max; | |
251 | u16 rxw; | |
252 | ||
253 | /* Wait for rx data to be ready */ | |
254 | while (rx_max(priv) == 0) { | |
255 | if (get_timer(start) > RX_TIMEOUT) | |
256 | return -ETIMEDOUT; | |
257 | } | |
258 | ||
259 | max = rx_max(priv); | |
260 | ||
261 | while (max--) { | |
262 | rxw = dw_readw(priv, DW_SPI_DR); | |
263 | debug("%s: rx=0x%02x\n", __func__, rxw); | |
a72f8020 SR |
264 | |
265 | /* | |
266 | * Care about rx only if the transfer's original "rx" is | |
267 | * not null | |
268 | */ | |
5bef6fd7 | 269 | if (priv->rx_end - priv->len) { |
a72f8020 | 270 | if (priv->bits_per_word == 8) |
5bef6fd7 SR |
271 | *(u8 *)(priv->rx) = rxw; |
272 | else | |
273 | *(u16 *)(priv->rx) = rxw; | |
274 | } | |
a72f8020 | 275 | priv->rx += priv->bits_per_word >> 3; |
5bef6fd7 SR |
276 | } |
277 | ||
278 | return 0; | |
279 | } | |
280 | ||
281 | static int poll_transfer(struct dw_spi_priv *priv) | |
282 | { | |
283 | int ret; | |
284 | ||
285 | do { | |
286 | dw_writer(priv); | |
287 | ret = dw_reader(priv); | |
288 | if (ret < 0) | |
289 | return ret; | |
290 | } while (priv->rx_end > priv->rx); | |
291 | ||
292 | return 0; | |
293 | } | |
294 | ||
295 | static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen, | |
296 | const void *dout, void *din, unsigned long flags) | |
297 | { | |
298 | struct udevice *bus = dev->parent; | |
299 | struct dw_spi_priv *priv = dev_get_priv(bus); | |
300 | const u8 *tx = dout; | |
301 | u8 *rx = din; | |
302 | int ret = 0; | |
303 | u32 cr0 = 0; | |
5bef6fd7 SR |
304 | u32 cs; |
305 | ||
306 | /* spi core configured to do 8 bit transfers */ | |
307 | if (bitlen % 8) { | |
308 | debug("Non byte aligned SPI transfer.\n"); | |
309 | return -1; | |
310 | } | |
311 | ||
a72f8020 | 312 | cr0 = (priv->bits_per_word - 1) | (priv->type << SPI_FRF_OFFSET) | |
5bef6fd7 SR |
313 | (priv->mode << SPI_MODE_OFFSET) | |
314 | (priv->tmode << SPI_TMOD_OFFSET); | |
315 | ||
316 | if (rx && tx) | |
317 | priv->tmode = SPI_TMOD_TR; | |
318 | else if (rx) | |
319 | priv->tmode = SPI_TMOD_RO; | |
320 | else | |
321 | priv->tmode = SPI_TMOD_TO; | |
322 | ||
323 | cr0 &= ~SPI_TMOD_MASK; | |
324 | cr0 |= (priv->tmode << SPI_TMOD_OFFSET); | |
325 | ||
a72f8020 | 326 | priv->len = bitlen >> 3; |
5bef6fd7 SR |
327 | debug("%s: rx=%p tx=%p len=%d [bytes]\n", __func__, rx, tx, priv->len); |
328 | ||
329 | priv->tx = (void *)tx; | |
330 | priv->tx_end = priv->tx + priv->len; | |
331 | priv->rx = rx; | |
332 | priv->rx_end = priv->rx + priv->len; | |
333 | ||
334 | /* Disable controller before writing control registers */ | |
335 | spi_enable_chip(priv, 0); | |
336 | ||
337 | debug("%s: cr0=%08x\n", __func__, cr0); | |
338 | /* Reprogram cr0 only if changed */ | |
339 | if (dw_readw(priv, DW_SPI_CTRL0) != cr0) | |
340 | dw_writew(priv, DW_SPI_CTRL0, cr0); | |
341 | ||
342 | /* | |
343 | * Configure the desired SS (slave select 0...3) in the controller | |
344 | * The DW SPI controller will activate and deactivate this CS | |
345 | * automatically. So no cs_activate() etc is needed in this driver. | |
346 | */ | |
347 | cs = spi_chip_select(dev); | |
348 | dw_writel(priv, DW_SPI_SER, 1 << cs); | |
349 | ||
350 | /* Enable controller after writing control registers */ | |
351 | spi_enable_chip(priv, 1); | |
352 | ||
353 | /* Start transfer in a polling loop */ | |
354 | ret = poll_transfer(priv); | |
355 | ||
356 | return ret; | |
357 | } | |
358 | ||
359 | static int dw_spi_set_speed(struct udevice *bus, uint speed) | |
360 | { | |
361 | struct dw_spi_platdata *plat = bus->platdata; | |
362 | struct dw_spi_priv *priv = dev_get_priv(bus); | |
363 | u16 clk_div; | |
364 | ||
365 | if (speed > plat->frequency) | |
366 | speed = plat->frequency; | |
367 | ||
368 | /* Disable controller before writing control registers */ | |
369 | spi_enable_chip(priv, 0); | |
370 | ||
371 | /* clk_div doesn't support odd number */ | |
a72f8020 | 372 | clk_div = cm_get_spi_controller_clk_hz() / speed; |
5bef6fd7 SR |
373 | clk_div = (clk_div + 1) & 0xfffe; |
374 | dw_writel(priv, DW_SPI_BAUDR, clk_div); | |
375 | ||
376 | /* Enable controller after writing control registers */ | |
377 | spi_enable_chip(priv, 1); | |
378 | ||
379 | priv->freq = speed; | |
380 | debug("%s: regs=%p speed=%d clk_div=%d\n", __func__, priv->regs, | |
381 | priv->freq, clk_div); | |
382 | ||
383 | return 0; | |
384 | } | |
385 | ||
386 | static int dw_spi_set_mode(struct udevice *bus, uint mode) | |
387 | { | |
388 | struct dw_spi_priv *priv = dev_get_priv(bus); | |
389 | ||
390 | /* | |
391 | * Can't set mode yet. Since this depends on if rx, tx, or | |
392 | * rx & tx is requested. So we have to defer this to the | |
393 | * real transfer function. | |
394 | */ | |
395 | priv->mode = mode; | |
396 | debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode); | |
397 | ||
398 | return 0; | |
399 | } | |
400 | ||
401 | static const struct dm_spi_ops dw_spi_ops = { | |
402 | .xfer = dw_spi_xfer, | |
403 | .set_speed = dw_spi_set_speed, | |
404 | .set_mode = dw_spi_set_mode, | |
405 | /* | |
406 | * cs_info is not needed, since we require all chip selects to be | |
407 | * in the device tree explicitly | |
408 | */ | |
409 | }; | |
410 | ||
411 | static const struct udevice_id dw_spi_ids[] = { | |
74114862 | 412 | { .compatible = "snps,dw-apb-ssi" }, |
5bef6fd7 SR |
413 | { } |
414 | }; | |
415 | ||
416 | U_BOOT_DRIVER(dw_spi) = { | |
417 | .name = "dw_spi", | |
418 | .id = UCLASS_SPI, | |
419 | .of_match = dw_spi_ids, | |
420 | .ops = &dw_spi_ops, | |
421 | .ofdata_to_platdata = dw_spi_ofdata_to_platdata, | |
422 | .platdata_auto_alloc_size = sizeof(struct dw_spi_platdata), | |
423 | .priv_auto_alloc_size = sizeof(struct dw_spi_priv), | |
5bef6fd7 SR |
424 | .probe = dw_spi_probe, |
425 | }; |