]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
1bf43b82 RS |
2 | /* |
3 | * (C) Copyright 2012 SAMSUNG Electronics | |
4 | * Padmavathi Venna <[email protected]> | |
1bf43b82 RS |
5 | */ |
6 | ||
7 | #include <common.h> | |
73186c94 SG |
8 | #include <dm.h> |
9 | #include <errno.h> | |
f7ae49fc | 10 | #include <log.h> |
1bf43b82 RS |
11 | #include <malloc.h> |
12 | #include <spi.h> | |
4d3acb9d | 13 | #include <fdtdec.h> |
1045315d | 14 | #include <time.h> |
1bf43b82 RS |
15 | #include <asm/arch/clk.h> |
16 | #include <asm/arch/clock.h> | |
17 | #include <asm/arch/cpu.h> | |
18 | #include <asm/arch/gpio.h> | |
19 | #include <asm/arch/pinmux.h> | |
77b55e8c | 20 | #include <asm/arch/spi.h> |
401d1c4f | 21 | #include <asm/global_data.h> |
1bf43b82 | 22 | #include <asm/io.h> |
c05ed00a | 23 | #include <linux/delay.h> |
1bf43b82 | 24 | |
4d3acb9d RS |
25 | DECLARE_GLOBAL_DATA_PTR; |
26 | ||
8a8d24bd | 27 | struct exynos_spi_plat { |
1bf43b82 RS |
28 | enum periph_id periph_id; |
29 | s32 frequency; /* Default clock frequency, -1 for none */ | |
30 | struct exynos_spi *regs; | |
8d203afd | 31 | uint deactivate_delay_us; /* Delay to wait after deactivate */ |
1bf43b82 RS |
32 | }; |
33 | ||
73186c94 | 34 | struct exynos_spi_priv { |
1bf43b82 RS |
35 | struct exynos_spi *regs; |
36 | unsigned int freq; /* Default frequency */ | |
37 | unsigned int mode; | |
38 | enum periph_id periph_id; /* Peripheral ID for this device */ | |
39 | unsigned int fifo_size; | |
e4eaef89 | 40 | int skip_preamble; |
8d203afd | 41 | ulong last_transaction_us; /* Time of last transaction end */ |
1bf43b82 RS |
42 | }; |
43 | ||
1bf43b82 RS |
44 | /** |
45 | * Flush spi tx, rx fifos and reset the SPI controller | |
46 | * | |
73186c94 | 47 | * @param regs Pointer to SPI registers |
1bf43b82 | 48 | */ |
73186c94 | 49 | static void spi_flush_fifo(struct exynos_spi *regs) |
1bf43b82 | 50 | { |
1bf43b82 RS |
51 | clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST); |
52 | clrbits_le32(®s->ch_cfg, SPI_CH_RST); | |
53 | setbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON); | |
54 | } | |
55 | ||
1bf43b82 RS |
56 | static void spi_get_fifo_levels(struct exynos_spi *regs, |
57 | int *rx_lvl, int *tx_lvl) | |
58 | { | |
59 | uint32_t spi_sts = readl(®s->spi_sts); | |
60 | ||
61 | *rx_lvl = (spi_sts >> SPI_RX_LVL_OFFSET) & SPI_FIFO_LVL_MASK; | |
62 | *tx_lvl = (spi_sts >> SPI_TX_LVL_OFFSET) & SPI_FIFO_LVL_MASK; | |
63 | } | |
64 | ||
65 | /** | |
66 | * If there's something to transfer, do a software reset and set a | |
67 | * transaction size. | |
68 | * | |
69 | * @param regs SPI peripheral registers | |
70 | * @param count Number of bytes to transfer | |
c4a79632 | 71 | * @param step Number of bytes to transfer in each packet (1 or 4) |
1bf43b82 | 72 | */ |
c4a79632 | 73 | static void spi_request_bytes(struct exynos_spi *regs, int count, int step) |
1bf43b82 | 74 | { |
73186c94 SG |
75 | debug("%s: regs=%p, count=%d, step=%d\n", __func__, regs, count, step); |
76 | ||
c4a79632 RS |
77 | /* For word address we need to swap bytes */ |
78 | if (step == 4) { | |
79 | setbits_le32(®s->mode_cfg, | |
80 | SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD); | |
81 | count /= 4; | |
82 | setbits_le32(®s->swap_cfg, SPI_TX_SWAP_EN | SPI_RX_SWAP_EN | | |
83 | SPI_TX_BYTE_SWAP | SPI_RX_BYTE_SWAP | | |
84 | SPI_TX_HWORD_SWAP | SPI_RX_HWORD_SWAP); | |
85 | } else { | |
86 | /* Select byte access and clear the swap configuration */ | |
87 | clrbits_le32(®s->mode_cfg, | |
88 | SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD); | |
89 | writel(0, ®s->swap_cfg); | |
90 | } | |
91 | ||
1bf43b82 RS |
92 | assert(count && count < (1 << 16)); |
93 | setbits_le32(®s->ch_cfg, SPI_CH_RST); | |
94 | clrbits_le32(®s->ch_cfg, SPI_CH_RST); | |
c4a79632 | 95 | |
1bf43b82 RS |
96 | writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt); |
97 | } | |
98 | ||
73186c94 | 99 | static int spi_rx_tx(struct exynos_spi_priv *priv, int todo, |
e4eaef89 | 100 | void **dinp, void const **doutp, unsigned long flags) |
1bf43b82 | 101 | { |
73186c94 | 102 | struct exynos_spi *regs = priv->regs; |
1bf43b82 RS |
103 | uchar *rxp = *dinp; |
104 | const uchar *txp = *doutp; | |
105 | int rx_lvl, tx_lvl; | |
106 | uint out_bytes, in_bytes; | |
e4eaef89 RS |
107 | int toread; |
108 | unsigned start = get_timer(0); | |
109 | int stopping; | |
c4a79632 | 110 | int step; |
1bf43b82 RS |
111 | |
112 | out_bytes = in_bytes = todo; | |
113 | ||
73186c94 SG |
114 | stopping = priv->skip_preamble && (flags & SPI_XFER_END) && |
115 | !(priv->mode & SPI_SLAVE); | |
e4eaef89 | 116 | |
c4a79632 RS |
117 | /* |
118 | * Try to transfer words if we can. This helps read performance at | |
119 | * SPI clock speeds above about 20MHz. | |
120 | */ | |
121 | step = 1; | |
122 | if (!((todo | (uintptr_t)rxp | (uintptr_t)txp) & 3) && | |
73186c94 | 123 | !priv->skip_preamble) |
c4a79632 RS |
124 | step = 4; |
125 | ||
1bf43b82 RS |
126 | /* |
127 | * If there's something to send, do a software reset and set a | |
128 | * transaction size. | |
129 | */ | |
c4a79632 | 130 | spi_request_bytes(regs, todo, step); |
1bf43b82 RS |
131 | |
132 | /* | |
133 | * Bytes are transmitted/received in pairs. Wait to receive all the | |
134 | * data because then transmission will be done as well. | |
135 | */ | |
e4eaef89 RS |
136 | toread = in_bytes; |
137 | ||
1bf43b82 RS |
138 | while (in_bytes) { |
139 | int temp; | |
140 | ||
141 | /* Keep the fifos full/empty. */ | |
142 | spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl); | |
c4a79632 RS |
143 | |
144 | /* | |
145 | * Don't completely fill the txfifo, since we don't want our | |
146 | * rxfifo to overflow, and it may already contain data. | |
147 | */ | |
73186c94 | 148 | while (tx_lvl < priv->fifo_size/2 && out_bytes) { |
c4a79632 RS |
149 | if (!txp) |
150 | temp = -1; | |
151 | else if (step == 4) | |
152 | temp = *(uint32_t *)txp; | |
153 | else | |
154 | temp = *txp; | |
1bf43b82 | 155 | writel(temp, ®s->tx_data); |
c4a79632 RS |
156 | out_bytes -= step; |
157 | if (txp) | |
158 | txp += step; | |
159 | tx_lvl += step; | |
1bf43b82 | 160 | } |
c4a79632 RS |
161 | if (rx_lvl >= step) { |
162 | while (rx_lvl >= step) { | |
120af157 | 163 | temp = readl(®s->rx_data); |
73186c94 | 164 | if (priv->skip_preamble) { |
120af157 | 165 | if (temp == SPI_PREAMBLE_END_BYTE) { |
73186c94 | 166 | priv->skip_preamble = 0; |
120af157 RS |
167 | stopping = 0; |
168 | } | |
169 | } else { | |
c4a79632 | 170 | if (rxp || stopping) { |
e76d2a81 AS |
171 | if (step == 4) |
172 | *(uint32_t *)rxp = temp; | |
173 | else | |
174 | *rxp = temp; | |
c4a79632 RS |
175 | rxp += step; |
176 | } | |
177 | in_bytes -= step; | |
e4eaef89 | 178 | } |
c4a79632 RS |
179 | toread -= step; |
180 | rx_lvl -= step; | |
181 | } | |
e4eaef89 RS |
182 | } else if (!toread) { |
183 | /* | |
184 | * We have run out of input data, but haven't read | |
185 | * enough bytes after the preamble yet. Read some more, | |
186 | * and make sure that we transmit dummy bytes too, to | |
187 | * keep things going. | |
188 | */ | |
189 | assert(!out_bytes); | |
190 | out_bytes = in_bytes; | |
191 | toread = in_bytes; | |
192 | txp = NULL; | |
c4a79632 | 193 | spi_request_bytes(regs, toread, step); |
e4eaef89 | 194 | } |
73186c94 | 195 | if (priv->skip_preamble && get_timer(start) > 100) { |
c7d50e7f SG |
196 | debug("SPI timeout: in_bytes=%d, out_bytes=%d, ", |
197 | in_bytes, out_bytes); | |
198 | return -ETIMEDOUT; | |
1bf43b82 RS |
199 | } |
200 | } | |
e4eaef89 | 201 | |
1bf43b82 RS |
202 | *dinp = rxp; |
203 | *doutp = txp; | |
e4eaef89 RS |
204 | |
205 | return 0; | |
1bf43b82 RS |
206 | } |
207 | ||
1bf43b82 RS |
208 | /** |
209 | * Activate the CS by driving it LOW | |
210 | * | |
211 | * @param slave Pointer to spi_slave to which controller has to | |
212 | * communicate with | |
213 | */ | |
73186c94 | 214 | static void spi_cs_activate(struct udevice *dev) |
1bf43b82 | 215 | { |
73186c94 | 216 | struct udevice *bus = dev->parent; |
8a8d24bd | 217 | struct exynos_spi_plat *pdata = dev_get_plat(bus); |
73186c94 | 218 | struct exynos_spi_priv *priv = dev_get_priv(bus); |
1bf43b82 | 219 | |
8d203afd | 220 | /* If it's too soon to do another transaction, wait */ |
73186c94 SG |
221 | if (pdata->deactivate_delay_us && |
222 | priv->last_transaction_us) { | |
8d203afd | 223 | ulong delay_us; /* The delay completed so far */ |
73186c94 SG |
224 | delay_us = timer_get_us() - priv->last_transaction_us; |
225 | if (delay_us < pdata->deactivate_delay_us) | |
226 | udelay(pdata->deactivate_delay_us - delay_us); | |
8d203afd RS |
227 | } |
228 | ||
73186c94 SG |
229 | clrbits_le32(&priv->regs->cs_reg, SPI_SLAVE_SIG_INACT); |
230 | debug("Activate CS, bus '%s'\n", bus->name); | |
231 | priv->skip_preamble = priv->mode & SPI_PREAMBLE; | |
1bf43b82 RS |
232 | } |
233 | ||
234 | /** | |
235 | * Deactivate the CS by driving it HIGH | |
236 | * | |
237 | * @param slave Pointer to spi_slave to which controller has to | |
238 | * communicate with | |
239 | */ | |
73186c94 | 240 | static void spi_cs_deactivate(struct udevice *dev) |
1bf43b82 | 241 | { |
73186c94 | 242 | struct udevice *bus = dev->parent; |
8a8d24bd | 243 | struct exynos_spi_plat *pdata = dev_get_plat(bus); |
73186c94 | 244 | struct exynos_spi_priv *priv = dev_get_priv(bus); |
1bf43b82 | 245 | |
73186c94 | 246 | setbits_le32(&priv->regs->cs_reg, SPI_SLAVE_SIG_INACT); |
a4e29db2 SG |
247 | |
248 | /* Remember time of this transaction so we can honour the bus delay */ | |
73186c94 SG |
249 | if (pdata->deactivate_delay_us) |
250 | priv->last_transaction_us = timer_get_us(); | |
a4e29db2 | 251 | |
73186c94 | 252 | debug("Deactivate CS, bus '%s'\n", bus->name); |
1bf43b82 RS |
253 | } |
254 | ||
d1998a9f | 255 | static int exynos_spi_of_to_plat(struct udevice *bus) |
1bf43b82 | 256 | { |
0fd3d911 | 257 | struct exynos_spi_plat *plat = dev_get_plat(bus); |
73186c94 | 258 | const void *blob = gd->fdt_blob; |
e160f7d4 | 259 | int node = dev_of_offset(bus); |
1bf43b82 | 260 | |
8613c8d8 | 261 | plat->regs = dev_read_addr_ptr(bus); |
73186c94 | 262 | plat->periph_id = pinmux_decode_periph_id(blob, node); |
4d3acb9d | 263 | |
73186c94 | 264 | if (plat->periph_id == PERIPH_ID_NONE) { |
4d3acb9d | 265 | debug("%s: Invalid peripheral ID %d\n", __func__, |
73186c94 | 266 | plat->periph_id); |
4d3acb9d RS |
267 | return -FDT_ERR_NOTFOUND; |
268 | } | |
269 | ||
270 | /* Use 500KHz as a suitable default */ | |
73186c94 | 271 | plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", |
4d3acb9d | 272 | 500000); |
73186c94 | 273 | plat->deactivate_delay_us = fdtdec_get_int(blob, node, |
8d203afd | 274 | "spi-deactivate-delay", 0); |
73186c94 SG |
275 | debug("%s: regs=%p, periph_id=%d, max-frequency=%d, deactivate_delay=%d\n", |
276 | __func__, plat->regs, plat->periph_id, plat->frequency, | |
277 | plat->deactivate_delay_us); | |
4d3acb9d RS |
278 | |
279 | return 0; | |
280 | } | |
281 | ||
73186c94 | 282 | static int exynos_spi_probe(struct udevice *bus) |
4d3acb9d | 283 | { |
8a8d24bd | 284 | struct exynos_spi_plat *plat = dev_get_plat(bus); |
73186c94 | 285 | struct exynos_spi_priv *priv = dev_get_priv(bus); |
4d3acb9d | 286 | |
73186c94 SG |
287 | priv->regs = plat->regs; |
288 | if (plat->periph_id == PERIPH_ID_SPI1 || | |
289 | plat->periph_id == PERIPH_ID_SPI2) | |
290 | priv->fifo_size = 64; | |
291 | else | |
292 | priv->fifo_size = 256; | |
4d3acb9d | 293 | |
73186c94 SG |
294 | priv->skip_preamble = 0; |
295 | priv->last_transaction_us = timer_get_us(); | |
296 | priv->freq = plat->frequency; | |
297 | priv->periph_id = plat->periph_id; | |
4d3acb9d | 298 | |
73186c94 SG |
299 | return 0; |
300 | } | |
4d3acb9d | 301 | |
9694b724 | 302 | static int exynos_spi_claim_bus(struct udevice *dev) |
73186c94 | 303 | { |
9694b724 | 304 | struct udevice *bus = dev->parent; |
73186c94 SG |
305 | struct exynos_spi_priv *priv = dev_get_priv(bus); |
306 | ||
307 | exynos_pinmux_config(priv->periph_id, PINMUX_FLAG_NONE); | |
308 | spi_flush_fifo(priv->regs); | |
309 | ||
310 | writel(SPI_FB_DELAY_180, &priv->regs->fb_clk); | |
4d3acb9d RS |
311 | |
312 | return 0; | |
313 | } | |
314 | ||
9694b724 | 315 | static int exynos_spi_release_bus(struct udevice *dev) |
f3424c55 | 316 | { |
9694b724 | 317 | struct udevice *bus = dev->parent; |
73186c94 SG |
318 | struct exynos_spi_priv *priv = dev_get_priv(bus); |
319 | ||
320 | spi_flush_fifo(priv->regs); | |
321 | ||
322 | return 0; | |
323 | } | |
324 | ||
325 | static int exynos_spi_xfer(struct udevice *dev, unsigned int bitlen, | |
326 | const void *dout, void *din, unsigned long flags) | |
327 | { | |
328 | struct udevice *bus = dev->parent; | |
329 | struct exynos_spi_priv *priv = dev_get_priv(bus); | |
330 | int upto, todo; | |
331 | int bytelen; | |
332 | int ret = 0; | |
333 | ||
334 | /* spi core configured to do 8 bit transfers */ | |
335 | if (bitlen % 8) { | |
336 | debug("Non byte aligned SPI transfer.\n"); | |
337 | return -1; | |
338 | } | |
339 | ||
340 | /* Start the transaction, if necessary. */ | |
341 | if ((flags & SPI_XFER_BEGIN)) | |
342 | spi_cs_activate(dev); | |
343 | ||
344 | /* | |
345 | * Exynos SPI limits each transfer to 65535 transfers. To keep | |
346 | * things simple, allow a maximum of 65532 bytes. We could allow | |
347 | * more in word mode, but the performance difference is small. | |
348 | */ | |
349 | bytelen = bitlen / 8; | |
350 | for (upto = 0; !ret && upto < bytelen; upto += todo) { | |
351 | todo = min(bytelen - upto, (1 << 16) - 4); | |
352 | ret = spi_rx_tx(priv, todo, &din, &dout, flags); | |
353 | if (ret) | |
354 | break; | |
355 | } | |
f3424c55 | 356 | |
73186c94 SG |
357 | /* Stop the transaction, if necessary. */ |
358 | if ((flags & SPI_XFER_END) && !(priv->mode & SPI_SLAVE)) { | |
359 | spi_cs_deactivate(dev); | |
360 | if (priv->skip_preamble) { | |
361 | assert(!priv->skip_preamble); | |
362 | debug("Failed to complete premable transaction\n"); | |
363 | ret = -1; | |
364 | } | |
f3424c55 HT |
365 | } |
366 | ||
73186c94 | 367 | return ret; |
f3424c55 HT |
368 | } |
369 | ||
73186c94 | 370 | static int exynos_spi_set_speed(struct udevice *bus, uint speed) |
1bf43b82 | 371 | { |
0fd3d911 | 372 | struct exynos_spi_plat *plat = dev_get_plat(bus); |
73186c94 SG |
373 | struct exynos_spi_priv *priv = dev_get_priv(bus); |
374 | int ret; | |
4d3acb9d | 375 | |
73186c94 SG |
376 | if (speed > plat->frequency) |
377 | speed = plat->frequency; | |
378 | ret = set_spi_clk(priv->periph_id, speed); | |
379 | if (ret) | |
380 | return ret; | |
381 | priv->freq = speed; | |
382 | debug("%s: regs=%p, speed=%d\n", __func__, priv->regs, priv->freq); | |
383 | ||
384 | return 0; | |
385 | } | |
4d3acb9d | 386 | |
73186c94 SG |
387 | static int exynos_spi_set_mode(struct udevice *bus, uint mode) |
388 | { | |
389 | struct exynos_spi_priv *priv = dev_get_priv(bus); | |
390 | uint32_t reg; | |
4d3acb9d | 391 | |
73186c94 SG |
392 | reg = readl(&priv->regs->ch_cfg); |
393 | reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); | |
1bf43b82 | 394 | |
73186c94 SG |
395 | if (mode & SPI_CPHA) |
396 | reg |= SPI_CH_CPHA_B; | |
1bf43b82 | 397 | |
73186c94 SG |
398 | if (mode & SPI_CPOL) |
399 | reg |= SPI_CH_CPOL_L; | |
400 | ||
401 | writel(reg, &priv->regs->ch_cfg); | |
402 | priv->mode = mode; | |
403 | debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode); | |
404 | ||
405 | return 0; | |
1bf43b82 | 406 | } |
73186c94 SG |
407 | |
408 | static const struct dm_spi_ops exynos_spi_ops = { | |
409 | .claim_bus = exynos_spi_claim_bus, | |
410 | .release_bus = exynos_spi_release_bus, | |
411 | .xfer = exynos_spi_xfer, | |
412 | .set_speed = exynos_spi_set_speed, | |
413 | .set_mode = exynos_spi_set_mode, | |
414 | /* | |
415 | * cs_info is not needed, since we require all chip selects to be | |
416 | * in the device tree explicitly | |
417 | */ | |
418 | }; | |
419 | ||
420 | static const struct udevice_id exynos_spi_ids[] = { | |
421 | { .compatible = "samsung,exynos-spi" }, | |
422 | { } | |
423 | }; | |
424 | ||
425 | U_BOOT_DRIVER(exynos_spi) = { | |
426 | .name = "exynos_spi", | |
427 | .id = UCLASS_SPI, | |
428 | .of_match = exynos_spi_ids, | |
429 | .ops = &exynos_spi_ops, | |
d1998a9f | 430 | .of_to_plat = exynos_spi_of_to_plat, |
8a8d24bd | 431 | .plat_auto = sizeof(struct exynos_spi_plat), |
41575d8e | 432 | .priv_auto = sizeof(struct exynos_spi_priv), |
73186c94 SG |
433 | .probe = exynos_spi_probe, |
434 | }; |