Commit 73186c9460761315a5a5dfd352c631ab2a022f28
1 parent
623b638607
Exists in
v2017.01-smarct4x
and in
37 other branches
dm: exynos: Convert SPI to driver model
Move the exynos SPI driver over to driver model. This removes quite a bit of boilerplate from the driver, although it adds some for driver model. A few device tree additions are needed to make the SPI flash available. Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-by: Jagannadha Sutradharudu Teki <jagannadh.teki@gmail.com>
Showing 5 changed files with 190 additions and 336 deletions Side-by-side Diff
arch/arm/dts/exynos5250-snow.dts
... | ... | @@ -53,6 +53,14 @@ |
53 | 53 | }; |
54 | 54 | }; |
55 | 55 | |
56 | + spi@12d30000 { | |
57 | + spi-max-frequency = <50000000>; | |
58 | + firmware_storage_spi: flash@0 { | |
59 | + compatible = "spi-flash"; | |
60 | + reg = <0>; | |
61 | + }; | |
62 | + }; | |
63 | + | |
56 | 64 | spi@131b0000 { |
57 | 65 | spi-max-frequency = <1000000>; |
58 | 66 | spi-deactivate-delay = <100>; |
arch/arm/dts/exynos5420-peach-pit.dts
board/samsung/common/board.c
drivers/spi/exynos_spi.c
... | ... | @@ -6,6 +6,8 @@ |
6 | 6 | */ |
7 | 7 | |
8 | 8 | #include <common.h> |
9 | +#include <dm.h> | |
10 | +#include <errno.h> | |
9 | 11 | #include <malloc.h> |
10 | 12 | #include <spi.h> |
11 | 13 | #include <fdtdec.h> |
12 | 14 | |
13 | 15 | |
14 | 16 | |
15 | 17 | |
16 | 18 | |
17 | 19 | |
18 | 20 | |
19 | 21 | |
20 | 22 | |
... | ... | @@ -19,176 +21,35 @@ |
19 | 21 | |
20 | 22 | DECLARE_GLOBAL_DATA_PTR; |
21 | 23 | |
22 | -/* Information about each SPI controller */ | |
23 | -struct spi_bus { | |
24 | +struct exynos_spi_platdata { | |
24 | 25 | enum periph_id periph_id; |
25 | 26 | s32 frequency; /* Default clock frequency, -1 for none */ |
26 | 27 | struct exynos_spi *regs; |
27 | - int inited; /* 1 if this bus is ready for use */ | |
28 | - int node; | |
29 | 28 | uint deactivate_delay_us; /* Delay to wait after deactivate */ |
30 | 29 | }; |
31 | 30 | |
32 | -/* A list of spi buses that we know about */ | |
33 | -static struct spi_bus spi_bus[EXYNOS5_SPI_NUM_CONTROLLERS]; | |
34 | -static unsigned int bus_count; | |
35 | - | |
36 | -struct exynos_spi_slave { | |
37 | - struct spi_slave slave; | |
31 | +struct exynos_spi_priv { | |
38 | 32 | struct exynos_spi *regs; |
39 | 33 | unsigned int freq; /* Default frequency */ |
40 | 34 | unsigned int mode; |
41 | 35 | enum periph_id periph_id; /* Peripheral ID for this device */ |
42 | 36 | unsigned int fifo_size; |
43 | 37 | int skip_preamble; |
44 | - struct spi_bus *bus; /* Pointer to our SPI bus info */ | |
45 | 38 | ulong last_transaction_us; /* Time of last transaction end */ |
46 | 39 | }; |
47 | 40 | |
48 | -static struct spi_bus *spi_get_bus(unsigned dev_index) | |
49 | -{ | |
50 | - if (dev_index < bus_count) | |
51 | - return &spi_bus[dev_index]; | |
52 | - debug("%s: invalid bus %d", __func__, dev_index); | |
53 | - | |
54 | - return NULL; | |
55 | -} | |
56 | - | |
57 | -static inline struct exynos_spi_slave *to_exynos_spi(struct spi_slave *slave) | |
58 | -{ | |
59 | - return container_of(slave, struct exynos_spi_slave, slave); | |
60 | -} | |
61 | - | |
62 | 41 | /** |
63 | - * Setup the driver private data | |
64 | - * | |
65 | - * @param bus ID of the bus that the slave is attached to | |
66 | - * @param cs ID of the chip select connected to the slave | |
67 | - * @param max_hz Required spi frequency | |
68 | - * @param mode Required spi mode (clk polarity, clk phase and | |
69 | - * master or slave) | |
70 | - * @return new device or NULL | |
71 | - */ | |
72 | -struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, | |
73 | - unsigned int max_hz, unsigned int mode) | |
74 | -{ | |
75 | - struct exynos_spi_slave *spi_slave; | |
76 | - struct spi_bus *bus; | |
77 | - | |
78 | - if (!spi_cs_is_valid(busnum, cs)) { | |
79 | - debug("%s: Invalid bus/chip select %d, %d\n", __func__, | |
80 | - busnum, cs); | |
81 | - return NULL; | |
82 | - } | |
83 | - | |
84 | - spi_slave = spi_alloc_slave(struct exynos_spi_slave, busnum, cs); | |
85 | - if (!spi_slave) { | |
86 | - debug("%s: Could not allocate spi_slave\n", __func__); | |
87 | - return NULL; | |
88 | - } | |
89 | - | |
90 | - bus = &spi_bus[busnum]; | |
91 | - spi_slave->bus = bus; | |
92 | - spi_slave->regs = bus->regs; | |
93 | - spi_slave->mode = mode; | |
94 | - spi_slave->periph_id = bus->periph_id; | |
95 | - if (bus->periph_id == PERIPH_ID_SPI1 || | |
96 | - bus->periph_id == PERIPH_ID_SPI2) | |
97 | - spi_slave->fifo_size = 64; | |
98 | - else | |
99 | - spi_slave->fifo_size = 256; | |
100 | - | |
101 | - spi_slave->skip_preamble = 0; | |
102 | - spi_slave->last_transaction_us = timer_get_us(); | |
103 | - | |
104 | - spi_slave->freq = bus->frequency; | |
105 | - if (max_hz) | |
106 | - spi_slave->freq = min(max_hz, spi_slave->freq); | |
107 | - | |
108 | - return &spi_slave->slave; | |
109 | -} | |
110 | - | |
111 | -/** | |
112 | - * Free spi controller | |
113 | - * | |
114 | - * @param slave Pointer to spi_slave to which controller has to | |
115 | - * communicate with | |
116 | - */ | |
117 | -void spi_free_slave(struct spi_slave *slave) | |
118 | -{ | |
119 | - struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); | |
120 | - | |
121 | - free(spi_slave); | |
122 | -} | |
123 | - | |
124 | -/** | |
125 | 42 | * Flush spi tx, rx fifos and reset the SPI controller |
126 | 43 | * |
127 | - * @param slave Pointer to spi_slave to which controller has to | |
128 | - * communicate with | |
44 | + * @param regs Pointer to SPI registers | |
129 | 45 | */ |
130 | -static void spi_flush_fifo(struct spi_slave *slave) | |
46 | +static void spi_flush_fifo(struct exynos_spi *regs) | |
131 | 47 | { |
132 | - struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); | |
133 | - struct exynos_spi *regs = spi_slave->regs; | |
134 | - | |
135 | 48 | clrsetbits_le32(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST); |
136 | 49 | clrbits_le32(®s->ch_cfg, SPI_CH_RST); |
137 | 50 | setbits_le32(®s->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON); |
138 | 51 | } |
139 | 52 | |
140 | -/** | |
141 | - * Initialize the spi base registers, set the required clock frequency and | |
142 | - * initialize the gpios | |
143 | - * | |
144 | - * @param slave Pointer to spi_slave to which controller has to | |
145 | - * communicate with | |
146 | - * @return zero on success else a negative value | |
147 | - */ | |
148 | -int spi_claim_bus(struct spi_slave *slave) | |
149 | -{ | |
150 | - struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); | |
151 | - struct exynos_spi *regs = spi_slave->regs; | |
152 | - u32 reg = 0; | |
153 | - int ret; | |
154 | - | |
155 | - ret = set_spi_clk(spi_slave->periph_id, | |
156 | - spi_slave->freq); | |
157 | - if (ret < 0) { | |
158 | - debug("%s: Failed to setup spi clock\n", __func__); | |
159 | - return ret; | |
160 | - } | |
161 | - | |
162 | - exynos_pinmux_config(spi_slave->periph_id, PINMUX_FLAG_NONE); | |
163 | - | |
164 | - spi_flush_fifo(slave); | |
165 | - | |
166 | - reg = readl(®s->ch_cfg); | |
167 | - reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); | |
168 | - | |
169 | - if (spi_slave->mode & SPI_CPHA) | |
170 | - reg |= SPI_CH_CPHA_B; | |
171 | - | |
172 | - if (spi_slave->mode & SPI_CPOL) | |
173 | - reg |= SPI_CH_CPOL_L; | |
174 | - | |
175 | - writel(reg, ®s->ch_cfg); | |
176 | - writel(SPI_FB_DELAY_180, ®s->fb_clk); | |
177 | - | |
178 | - return 0; | |
179 | -} | |
180 | - | |
181 | -/** | |
182 | - * Reset the spi H/W and flush the tx and rx fifos | |
183 | - * | |
184 | - * @param slave Pointer to spi_slave to which controller has to | |
185 | - * communicate with | |
186 | - */ | |
187 | -void spi_release_bus(struct spi_slave *slave) | |
188 | -{ | |
189 | - spi_flush_fifo(slave); | |
190 | -} | |
191 | - | |
192 | 53 | static void spi_get_fifo_levels(struct exynos_spi *regs, |
193 | 54 | int *rx_lvl, int *tx_lvl) |
194 | 55 | { |
... | ... | @@ -208,6 +69,8 @@ |
208 | 69 | */ |
209 | 70 | static void spi_request_bytes(struct exynos_spi *regs, int count, int step) |
210 | 71 | { |
72 | + debug("%s: regs=%p, count=%d, step=%d\n", __func__, regs, count, step); | |
73 | + | |
211 | 74 | /* For word address we need to swap bytes */ |
212 | 75 | if (step == 4) { |
213 | 76 | setbits_le32(®s->mode_cfg, |
214 | 77 | |
... | ... | @@ -230,10 +93,10 @@ |
230 | 93 | writel(count | SPI_PACKET_CNT_EN, ®s->pkt_cnt); |
231 | 94 | } |
232 | 95 | |
233 | -static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo, | |
96 | +static int spi_rx_tx(struct exynos_spi_priv *priv, int todo, | |
234 | 97 | void **dinp, void const **doutp, unsigned long flags) |
235 | 98 | { |
236 | - struct exynos_spi *regs = spi_slave->regs; | |
99 | + struct exynos_spi *regs = priv->regs; | |
237 | 100 | uchar *rxp = *dinp; |
238 | 101 | const uchar *txp = *doutp; |
239 | 102 | int rx_lvl, tx_lvl; |
... | ... | @@ -245,8 +108,8 @@ |
245 | 108 | |
246 | 109 | out_bytes = in_bytes = todo; |
247 | 110 | |
248 | - stopping = spi_slave->skip_preamble && (flags & SPI_XFER_END) && | |
249 | - !(spi_slave->mode & SPI_SLAVE); | |
111 | + stopping = priv->skip_preamble && (flags & SPI_XFER_END) && | |
112 | + !(priv->mode & SPI_SLAVE); | |
250 | 113 | |
251 | 114 | /* |
252 | 115 | * Try to transfer words if we can. This helps read performance at |
... | ... | @@ -254,7 +117,7 @@ |
254 | 117 | */ |
255 | 118 | step = 1; |
256 | 119 | if (!((todo | (uintptr_t)rxp | (uintptr_t)txp) & 3) && |
257 | - !spi_slave->skip_preamble) | |
120 | + !priv->skip_preamble) | |
258 | 121 | step = 4; |
259 | 122 | |
260 | 123 | /* |
... | ... | @@ -279,7 +142,7 @@ |
279 | 142 | * Don't completely fill the txfifo, since we don't want our |
280 | 143 | * rxfifo to overflow, and it may already contain data. |
281 | 144 | */ |
282 | - while (tx_lvl < spi_slave->fifo_size/2 && out_bytes) { | |
145 | + while (tx_lvl < priv->fifo_size/2 && out_bytes) { | |
283 | 146 | if (!txp) |
284 | 147 | temp = -1; |
285 | 148 | else if (step == 4) |
286 | 149 | |
... | ... | @@ -295,9 +158,9 @@ |
295 | 158 | if (rx_lvl >= step) { |
296 | 159 | while (rx_lvl >= step) { |
297 | 160 | temp = readl(®s->rx_data); |
298 | - if (spi_slave->skip_preamble) { | |
161 | + if (priv->skip_preamble) { | |
299 | 162 | if (temp == SPI_PREAMBLE_END_BYTE) { |
300 | - spi_slave->skip_preamble = 0; | |
163 | + priv->skip_preamble = 0; | |
301 | 164 | stopping = 0; |
302 | 165 | } |
303 | 166 | } else { |
... | ... | @@ -326,7 +189,7 @@ |
326 | 189 | txp = NULL; |
327 | 190 | spi_request_bytes(regs, toread, step); |
328 | 191 | } |
329 | - if (spi_slave->skip_preamble && get_timer(start) > 100) { | |
192 | + if (priv->skip_preamble && get_timer(start) > 100) { | |
330 | 193 | printf("SPI timeout: in_bytes=%d, out_bytes=%d, ", |
331 | 194 | in_bytes, out_bytes); |
332 | 195 | return -1; |
333 | 196 | |
334 | 197 | |
335 | 198 | |
336 | 199 | |
337 | 200 | |
... | ... | @@ -340,94 +203,29 @@ |
340 | 203 | } |
341 | 204 | |
342 | 205 | /** |
343 | - * Transfer and receive data | |
344 | - * | |
345 | - * @param slave Pointer to spi_slave to which controller has to | |
346 | - * communicate with | |
347 | - * @param bitlen No of bits to tranfer or receive | |
348 | - * @param dout Pointer to transfer buffer | |
349 | - * @param din Pointer to receive buffer | |
350 | - * @param flags Flags for transfer begin and end | |
351 | - * @return zero on success else a negative value | |
352 | - */ | |
353 | -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, | |
354 | - void *din, unsigned long flags) | |
355 | -{ | |
356 | - struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); | |
357 | - int upto, todo; | |
358 | - int bytelen; | |
359 | - int ret = 0; | |
360 | - | |
361 | - /* spi core configured to do 8 bit transfers */ | |
362 | - if (bitlen % 8) { | |
363 | - debug("Non byte aligned SPI transfer.\n"); | |
364 | - return -1; | |
365 | - } | |
366 | - | |
367 | - /* Start the transaction, if necessary. */ | |
368 | - if ((flags & SPI_XFER_BEGIN)) | |
369 | - spi_cs_activate(slave); | |
370 | - | |
371 | - /* | |
372 | - * Exynos SPI limits each transfer to 65535 transfers. To keep | |
373 | - * things simple, allow a maximum of 65532 bytes. We could allow | |
374 | - * more in word mode, but the performance difference is small. | |
375 | - */ | |
376 | - bytelen = bitlen / 8; | |
377 | - for (upto = 0; !ret && upto < bytelen; upto += todo) { | |
378 | - todo = min(bytelen - upto, (1 << 16) - 4); | |
379 | - ret = spi_rx_tx(spi_slave, todo, &din, &dout, flags); | |
380 | - if (ret) | |
381 | - break; | |
382 | - } | |
383 | - | |
384 | - /* Stop the transaction, if necessary. */ | |
385 | - if ((flags & SPI_XFER_END) && !(spi_slave->mode & SPI_SLAVE)) { | |
386 | - spi_cs_deactivate(slave); | |
387 | - if (spi_slave->skip_preamble) { | |
388 | - assert(!spi_slave->skip_preamble); | |
389 | - debug("Failed to complete premable transaction\n"); | |
390 | - ret = -1; | |
391 | - } | |
392 | - } | |
393 | - | |
394 | - return ret; | |
395 | -} | |
396 | - | |
397 | -/** | |
398 | - * Validates the bus and chip select numbers | |
399 | - * | |
400 | - * @param bus ID of the bus that the slave is attached to | |
401 | - * @param cs ID of the chip select connected to the slave | |
402 | - * @return one on success else zero | |
403 | - */ | |
404 | -int spi_cs_is_valid(unsigned int bus, unsigned int cs) | |
405 | -{ | |
406 | - return spi_get_bus(bus) && cs == 0; | |
407 | -} | |
408 | - | |
409 | -/** | |
410 | 206 | * Activate the CS by driving it LOW |
411 | 207 | * |
412 | 208 | * @param slave Pointer to spi_slave to which controller has to |
413 | 209 | * communicate with |
414 | 210 | */ |
415 | -void spi_cs_activate(struct spi_slave *slave) | |
211 | +static void spi_cs_activate(struct udevice *dev) | |
416 | 212 | { |
417 | - struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); | |
213 | + struct udevice *bus = dev->parent; | |
214 | + struct exynos_spi_platdata *pdata = dev_get_platdata(bus); | |
215 | + struct exynos_spi_priv *priv = dev_get_priv(bus); | |
418 | 216 | |
419 | 217 | /* If it's too soon to do another transaction, wait */ |
420 | - if (spi_slave->bus->deactivate_delay_us && | |
421 | - spi_slave->last_transaction_us) { | |
218 | + if (pdata->deactivate_delay_us && | |
219 | + priv->last_transaction_us) { | |
422 | 220 | ulong delay_us; /* The delay completed so far */ |
423 | - delay_us = timer_get_us() - spi_slave->last_transaction_us; | |
424 | - if (delay_us < spi_slave->bus->deactivate_delay_us) | |
425 | - udelay(spi_slave->bus->deactivate_delay_us - delay_us); | |
221 | + delay_us = timer_get_us() - priv->last_transaction_us; | |
222 | + if (delay_us < pdata->deactivate_delay_us) | |
223 | + udelay(pdata->deactivate_delay_us - delay_us); | |
426 | 224 | } |
427 | 225 | |
428 | - clrbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); | |
429 | - debug("Activate CS, bus %d\n", spi_slave->slave.bus); | |
430 | - spi_slave->skip_preamble = spi_slave->mode & SPI_PREAMBLE; | |
226 | + clrbits_le32(&priv->regs->cs_reg, SPI_SLAVE_SIG_INACT); | |
227 | + debug("Activate CS, bus '%s'\n", bus->name); | |
228 | + priv->skip_preamble = priv->mode & SPI_PREAMBLE; | |
431 | 229 | } |
432 | 230 | |
433 | 231 | /** |
434 | 232 | |
435 | 233 | |
436 | 234 | |
437 | 235 | |
438 | 236 | |
439 | 237 | |
440 | 238 | |
441 | 239 | |
442 | 240 | |
443 | 241 | |
444 | 242 | |
445 | 243 | |
446 | 244 | |
447 | 245 | |
448 | 246 | |
449 | 247 | |
450 | 248 | |
451 | 249 | |
452 | 250 | |
453 | 251 | |
454 | 252 | |
455 | 253 | |
456 | 254 | |
457 | 255 | |
458 | 256 | |
459 | 257 | |
460 | 258 | |
461 | 259 | |
462 | 260 | |
463 | 261 | |
464 | 262 | |
465 | 263 | |
... | ... | @@ -436,149 +234,198 @@ |
436 | 234 | * @param slave Pointer to spi_slave to which controller has to |
437 | 235 | * communicate with |
438 | 236 | */ |
439 | -void spi_cs_deactivate(struct spi_slave *slave) | |
237 | +static void spi_cs_deactivate(struct udevice *dev) | |
440 | 238 | { |
441 | - struct exynos_spi_slave *spi_slave = to_exynos_spi(slave); | |
239 | + struct udevice *bus = dev->parent; | |
240 | + struct exynos_spi_platdata *pdata = dev_get_platdata(bus); | |
241 | + struct exynos_spi_priv *priv = dev_get_priv(bus); | |
442 | 242 | |
443 | - setbits_le32(&spi_slave->regs->cs_reg, SPI_SLAVE_SIG_INACT); | |
243 | + setbits_le32(&priv->regs->cs_reg, SPI_SLAVE_SIG_INACT); | |
444 | 244 | |
445 | 245 | /* Remember time of this transaction so we can honour the bus delay */ |
446 | - if (spi_slave->bus->deactivate_delay_us) | |
447 | - spi_slave->last_transaction_us = timer_get_us(); | |
246 | + if (pdata->deactivate_delay_us) | |
247 | + priv->last_transaction_us = timer_get_us(); | |
448 | 248 | |
449 | - debug("Deactivate CS, bus %d\n", spi_slave->slave.bus); | |
249 | + debug("Deactivate CS, bus '%s'\n", bus->name); | |
450 | 250 | } |
451 | 251 | |
452 | -static inline struct exynos_spi *get_spi_base(int dev_index) | |
252 | +static int exynos_spi_ofdata_to_platdata(struct udevice *bus) | |
453 | 253 | { |
454 | - if (dev_index < 3) | |
455 | - return (struct exynos_spi *)samsung_get_base_spi() + dev_index; | |
456 | - else | |
457 | - return (struct exynos_spi *)samsung_get_base_spi_isp() + | |
458 | - (dev_index - 3); | |
459 | -} | |
254 | + struct exynos_spi_platdata *plat = bus->platdata; | |
255 | + const void *blob = gd->fdt_blob; | |
256 | + int node = bus->of_offset; | |
460 | 257 | |
461 | -/* | |
462 | - * Read the SPI config from the device tree node. | |
463 | - * | |
464 | - * @param blob FDT blob to read from | |
465 | - * @param node Node offset to read from | |
466 | - * @param bus SPI bus structure to fill with information | |
467 | - * @return 0 if ok, or -FDT_ERR_NOTFOUND if something was missing | |
468 | - */ | |
469 | -#ifdef CONFIG_OF_CONTROL | |
470 | -static int spi_get_config(const void *blob, int node, struct spi_bus *bus) | |
471 | -{ | |
472 | - bus->node = node; | |
473 | - bus->regs = (struct exynos_spi *)fdtdec_get_addr(blob, node, "reg"); | |
474 | - bus->periph_id = pinmux_decode_periph_id(blob, node); | |
258 | + plat->regs = (struct exynos_spi *)fdtdec_get_addr(blob, node, "reg"); | |
259 | + plat->periph_id = pinmux_decode_periph_id(blob, node); | |
475 | 260 | |
476 | - if (bus->periph_id == PERIPH_ID_NONE) { | |
261 | + if (plat->periph_id == PERIPH_ID_NONE) { | |
477 | 262 | debug("%s: Invalid peripheral ID %d\n", __func__, |
478 | - bus->periph_id); | |
263 | + plat->periph_id); | |
479 | 264 | return -FDT_ERR_NOTFOUND; |
480 | 265 | } |
481 | 266 | |
482 | 267 | /* Use 500KHz as a suitable default */ |
483 | - bus->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", | |
268 | + plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", | |
484 | 269 | 500000); |
485 | - bus->deactivate_delay_us = fdtdec_get_int(blob, node, | |
270 | + plat->deactivate_delay_us = fdtdec_get_int(blob, node, | |
486 | 271 | "spi-deactivate-delay", 0); |
272 | + debug("%s: regs=%p, periph_id=%d, max-frequency=%d, deactivate_delay=%d\n", | |
273 | + __func__, plat->regs, plat->periph_id, plat->frequency, | |
274 | + plat->deactivate_delay_us); | |
487 | 275 | |
488 | 276 | return 0; |
489 | 277 | } |
490 | 278 | |
491 | -/* | |
492 | - * Process a list of nodes, adding them to our list of SPI ports. | |
493 | - * | |
494 | - * @param blob fdt blob | |
495 | - * @param node_list list of nodes to process (any <=0 are ignored) | |
496 | - * @param count number of nodes to process | |
497 | - * @param is_dvc 1 if these are DVC ports, 0 if standard I2C | |
498 | - * @return 0 if ok, -1 on error | |
499 | - */ | |
500 | -static int process_nodes(const void *blob, int node_list[], int count) | |
279 | +static int exynos_spi_probe(struct udevice *bus) | |
501 | 280 | { |
502 | - int i; | |
281 | + struct exynos_spi_platdata *plat = dev_get_platdata(bus); | |
282 | + struct exynos_spi_priv *priv = dev_get_priv(bus); | |
503 | 283 | |
504 | - /* build the i2c_controllers[] for each controller */ | |
505 | - for (i = 0; i < count; i++) { | |
506 | - int node = node_list[i]; | |
507 | - struct spi_bus *bus; | |
284 | + priv->regs = plat->regs; | |
285 | + if (plat->periph_id == PERIPH_ID_SPI1 || | |
286 | + plat->periph_id == PERIPH_ID_SPI2) | |
287 | + priv->fifo_size = 64; | |
288 | + else | |
289 | + priv->fifo_size = 256; | |
508 | 290 | |
509 | - if (node <= 0) | |
510 | - continue; | |
291 | + priv->skip_preamble = 0; | |
292 | + priv->last_transaction_us = timer_get_us(); | |
293 | + priv->freq = plat->frequency; | |
294 | + priv->periph_id = plat->periph_id; | |
511 | 295 | |
512 | - bus = &spi_bus[i]; | |
513 | - if (spi_get_config(blob, node, bus)) { | |
514 | - printf("exynos spi_init: failed to decode bus %d\n", | |
515 | - i); | |
516 | - return -1; | |
517 | - } | |
296 | + return 0; | |
297 | +} | |
518 | 298 | |
519 | - debug("spi: controller bus %d at %p, periph_id %d\n", | |
520 | - i, bus->regs, bus->periph_id); | |
521 | - bus->inited = 1; | |
522 | - bus_count++; | |
523 | - } | |
299 | +static int exynos_spi_claim_bus(struct udevice *bus) | |
300 | +{ | |
301 | + struct exynos_spi_priv *priv = dev_get_priv(bus); | |
524 | 302 | |
303 | + exynos_pinmux_config(priv->periph_id, PINMUX_FLAG_NONE); | |
304 | + spi_flush_fifo(priv->regs); | |
305 | + | |
306 | + writel(SPI_FB_DELAY_180, &priv->regs->fb_clk); | |
307 | + | |
525 | 308 | return 0; |
526 | 309 | } |
527 | -#endif | |
528 | 310 | |
529 | -/** | |
530 | - * Set up a new SPI slave for an fdt node | |
531 | - * | |
532 | - * @param blob Device tree blob | |
533 | - * @param node SPI peripheral node to use | |
534 | - * @return 0 if ok, -1 on error | |
535 | - */ | |
536 | -struct spi_slave *spi_setup_slave_fdt(const void *blob, int slave_node, | |
537 | - int spi_node) | |
311 | +static int exynos_spi_release_bus(struct udevice *bus) | |
538 | 312 | { |
539 | - struct spi_bus *bus; | |
540 | - unsigned int i; | |
313 | + struct exynos_spi_priv *priv = dev_get_priv(bus); | |
541 | 314 | |
542 | - for (i = 0, bus = spi_bus; i < bus_count; i++, bus++) { | |
543 | - if (bus->node == spi_node) | |
544 | - return spi_base_setup_slave_fdt(blob, i, slave_node); | |
315 | + spi_flush_fifo(priv->regs); | |
316 | + | |
317 | + return 0; | |
318 | +} | |
319 | + | |
320 | +static int exynos_spi_xfer(struct udevice *dev, unsigned int bitlen, | |
321 | + const void *dout, void *din, unsigned long flags) | |
322 | +{ | |
323 | + struct udevice *bus = dev->parent; | |
324 | + struct exynos_spi_priv *priv = dev_get_priv(bus); | |
325 | + int upto, todo; | |
326 | + int bytelen; | |
327 | + int ret = 0; | |
328 | + | |
329 | + /* spi core configured to do 8 bit transfers */ | |
330 | + if (bitlen % 8) { | |
331 | + debug("Non byte aligned SPI transfer.\n"); | |
332 | + return -1; | |
545 | 333 | } |
546 | 334 | |
547 | - debug("%s: Failed to find bus node %d\n", __func__, spi_node); | |
548 | - return NULL; | |
335 | + /* Start the transaction, if necessary. */ | |
336 | + if ((flags & SPI_XFER_BEGIN)) | |
337 | + spi_cs_activate(dev); | |
338 | + | |
339 | + /* | |
340 | + * Exynos SPI limits each transfer to 65535 transfers. To keep | |
341 | + * things simple, allow a maximum of 65532 bytes. We could allow | |
342 | + * more in word mode, but the performance difference is small. | |
343 | + */ | |
344 | + bytelen = bitlen / 8; | |
345 | + for (upto = 0; !ret && upto < bytelen; upto += todo) { | |
346 | + todo = min(bytelen - upto, (1 << 16) - 4); | |
347 | + ret = spi_rx_tx(priv, todo, &din, &dout, flags); | |
348 | + if (ret) | |
349 | + break; | |
350 | + } | |
351 | + | |
352 | + /* Stop the transaction, if necessary. */ | |
353 | + if ((flags & SPI_XFER_END) && !(priv->mode & SPI_SLAVE)) { | |
354 | + spi_cs_deactivate(dev); | |
355 | + if (priv->skip_preamble) { | |
356 | + assert(!priv->skip_preamble); | |
357 | + debug("Failed to complete premable transaction\n"); | |
358 | + ret = -1; | |
359 | + } | |
360 | + } | |
361 | + | |
362 | + return ret; | |
549 | 363 | } |
550 | 364 | |
551 | -/* Sadly there is no error return from this function */ | |
552 | -void spi_init(void) | |
365 | +static int exynos_spi_set_speed(struct udevice *bus, uint speed) | |
553 | 366 | { |
554 | - int count; | |
367 | + struct exynos_spi_platdata *plat = bus->platdata; | |
368 | + struct exynos_spi_priv *priv = dev_get_priv(bus); | |
369 | + int ret; | |
555 | 370 | |
556 | -#ifdef CONFIG_OF_CONTROL | |
557 | - int node_list[EXYNOS5_SPI_NUM_CONTROLLERS]; | |
558 | - const void *blob = gd->fdt_blob; | |
371 | + if (speed > plat->frequency) | |
372 | + speed = plat->frequency; | |
373 | + ret = set_spi_clk(priv->periph_id, speed); | |
374 | + if (ret) | |
375 | + return ret; | |
376 | + priv->freq = speed; | |
377 | + debug("%s: regs=%p, speed=%d\n", __func__, priv->regs, priv->freq); | |
559 | 378 | |
560 | - count = fdtdec_find_aliases_for_id(blob, "spi", | |
561 | - COMPAT_SAMSUNG_EXYNOS_SPI, node_list, | |
562 | - EXYNOS5_SPI_NUM_CONTROLLERS); | |
563 | - if (process_nodes(blob, node_list, count)) | |
564 | - return; | |
379 | + return 0; | |
380 | +} | |
565 | 381 | |
566 | -#else | |
567 | - struct spi_bus *bus; | |
382 | +static int exynos_spi_set_mode(struct udevice *bus, uint mode) | |
383 | +{ | |
384 | + struct exynos_spi_priv *priv = dev_get_priv(bus); | |
385 | + uint32_t reg; | |
568 | 386 | |
569 | - for (count = 0; count < EXYNOS5_SPI_NUM_CONTROLLERS; count++) { | |
570 | - bus = &spi_bus[count]; | |
571 | - bus->regs = get_spi_base(count); | |
572 | - bus->periph_id = PERIPH_ID_SPI0 + count; | |
387 | + reg = readl(&priv->regs->ch_cfg); | |
388 | + reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L); | |
573 | 389 | |
574 | - /* Although Exynos5 supports upto 50Mhz speed, | |
575 | - * we are setting it to 10Mhz for safe side | |
576 | - */ | |
577 | - bus->frequency = 10000000; | |
578 | - bus->inited = 1; | |
579 | - bus->node = 0; | |
580 | - bus_count = EXYNOS5_SPI_NUM_CONTROLLERS; | |
581 | - } | |
582 | -#endif | |
390 | + if (mode & SPI_CPHA) | |
391 | + reg |= SPI_CH_CPHA_B; | |
392 | + | |
393 | + if (mode & SPI_CPOL) | |
394 | + reg |= SPI_CH_CPOL_L; | |
395 | + | |
396 | + writel(reg, &priv->regs->ch_cfg); | |
397 | + priv->mode = mode; | |
398 | + debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode); | |
399 | + | |
400 | + return 0; | |
583 | 401 | } |
402 | + | |
403 | +static const struct dm_spi_ops exynos_spi_ops = { | |
404 | + .claim_bus = exynos_spi_claim_bus, | |
405 | + .release_bus = exynos_spi_release_bus, | |
406 | + .xfer = exynos_spi_xfer, | |
407 | + .set_speed = exynos_spi_set_speed, | |
408 | + .set_mode = exynos_spi_set_mode, | |
409 | + /* | |
410 | + * cs_info is not needed, since we require all chip selects to be | |
411 | + * in the device tree explicitly | |
412 | + */ | |
413 | +}; | |
414 | + | |
415 | +static const struct udevice_id exynos_spi_ids[] = { | |
416 | + { .compatible = "samsung,exynos-spi" }, | |
417 | + { } | |
418 | +}; | |
419 | + | |
420 | +U_BOOT_DRIVER(exynos_spi) = { | |
421 | + .name = "exynos_spi", | |
422 | + .id = UCLASS_SPI, | |
423 | + .of_match = exynos_spi_ids, | |
424 | + .ops = &exynos_spi_ops, | |
425 | + .ofdata_to_platdata = exynos_spi_ofdata_to_platdata, | |
426 | + .platdata_auto_alloc_size = sizeof(struct exynos_spi_platdata), | |
427 | + .priv_auto_alloc_size = sizeof(struct exynos_spi_priv), | |
428 | + .per_child_auto_alloc_size = sizeof(struct spi_slave), | |
429 | + .probe = exynos_spi_probe, | |
430 | +}; |
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f
-
mentioned in commit c5b88f