]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
ec33de3d MV |
2 | /* |
3 | * Freescale i.MX28 SPI driver | |
4 | * | |
d99b018a LM |
5 | * Copyright (C) 2019 DENX Software Engineering |
6 | * Lukasz Majewski, DENX Software Engineering, [email protected] | |
7 | * | |
ec33de3d MV |
8 | * Copyright (C) 2011 Marek Vasut <[email protected]> |
9 | * on behalf of DENX Software Engineering GmbH | |
10 | * | |
ec33de3d MV |
11 | * NOTE: This driver only supports the SPI-controller chipselects, |
12 | * GPIO driven chipselects are not supported. | |
13 | */ | |
14 | ||
15 | #include <common.h> | |
7e07b372 JT |
16 | #include <dm.h> |
17 | #include <dt-structs.h> | |
1eb69ae4 | 18 | #include <cpu_func.h> |
7e07b372 | 19 | #include <errno.h> |
f7ae49fc | 20 | #include <log.h> |
ec33de3d | 21 | #include <malloc.h> |
cf92e05c | 22 | #include <memalign.h> |
ec33de3d | 23 | #include <spi.h> |
90526e9f | 24 | #include <asm/cache.h> |
cd93d625 | 25 | #include <linux/bitops.h> |
1221ce45 | 26 | #include <linux/errno.h> |
ec33de3d MV |
27 | #include <asm/io.h> |
28 | #include <asm/arch/clock.h> | |
29 | #include <asm/arch/imx-regs.h> | |
30 | #include <asm/arch/sys_proto.h> | |
552a848e | 31 | #include <asm/mach-imx/dma.h> |
ec33de3d MV |
32 | |
33 | #define MXS_SPI_MAX_TIMEOUT 1000000 | |
34 | #define MXS_SPI_PORT_OFFSET 0x2000 | |
148ca64f FE |
35 | #define MXS_SSP_CHIPSELECT_MASK 0x00300000 |
36 | #define MXS_SSP_CHIPSELECT_SHIFT 20 | |
ec33de3d | 37 | |
7c5e6f7a MV |
38 | #define MXSSSP_SMALL_TRANSFER 512 |
39 | ||
7e07b372 JT |
40 | /* Base numbers of i.MX2[38] clk for ssp0 IP block */ |
41 | #define MXS_SSP_IMX23_CLKID_SSP0 33 | |
42 | #define MXS_SSP_IMX28_CLKID_SSP0 46 | |
ec0c81f8 | 43 | |
8a8d24bd | 44 | struct mxs_spi_plat { |
ec0c81f8 | 45 | #if CONFIG_IS_ENABLED(OF_PLATDATA) |
df17cdc9 | 46 | struct dtd_fsl_imx23_spi dtplat; |
ec0c81f8 | 47 | #endif |
d99b018a LM |
48 | s32 frequency; /* Default clock frequency, -1 for none */ |
49 | fdt_addr_t base; /* SPI IP block base address */ | |
50 | int num_cs; /* Number of CSes supported */ | |
51 | int dma_id; /* ID of the DMA channel */ | |
52 | int clk_id; /* ID of the SSP clock */ | |
53 | }; | |
ec33de3d | 54 | |
d99b018a LM |
55 | struct mxs_spi_priv { |
56 | struct mxs_ssp_regs *regs; | |
57 | unsigned int dma_channel; | |
58 | unsigned int max_freq; | |
59 | unsigned int clk_id; | |
60 | unsigned int mode; | |
61 | }; | |
ec33de3d | 62 | |
7e07b372 JT |
63 | static void mxs_spi_start_xfer(struct mxs_ssp_regs *ssp_regs) |
64 | { | |
65 | writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_set); | |
66 | writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_clr); | |
67 | } | |
68 | ||
69 | static void mxs_spi_end_xfer(struct mxs_ssp_regs *ssp_regs) | |
70 | { | |
71 | writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_clr); | |
72 | writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_set); | |
73 | } | |
74 | ||
d99b018a LM |
75 | static int mxs_spi_xfer_pio(struct mxs_spi_priv *priv, |
76 | char *data, int length, int write, | |
77 | unsigned long flags) | |
78 | { | |
79 | struct mxs_ssp_regs *ssp_regs = priv->regs; | |
c7065fa8 | 80 | |
ec33de3d MV |
81 | if (flags & SPI_XFER_BEGIN) |
82 | mxs_spi_start_xfer(ssp_regs); | |
83 | ||
ccd4d5a0 | 84 | while (length--) { |
ec33de3d | 85 | /* We transfer 1 byte */ |
c96e78cc MV |
86 | #if defined(CONFIG_MX23) |
87 | writel(SSP_CTRL0_XFER_COUNT_MASK, &ssp_regs->hw_ssp_ctrl0_clr); | |
88 | writel(1, &ssp_regs->hw_ssp_ctrl0_set); | |
89 | #elif defined(CONFIG_MX28) | |
ec33de3d | 90 | writel(1, &ssp_regs->hw_ssp_xfer_size); |
c96e78cc | 91 | #endif |
ec33de3d | 92 | |
ccd4d5a0 | 93 | if ((flags & SPI_XFER_END) && !length) |
ec33de3d MV |
94 | mxs_spi_end_xfer(ssp_regs); |
95 | ||
c7065fa8 | 96 | if (write) |
ec33de3d MV |
97 | writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_clr); |
98 | else | |
99 | writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_set); | |
100 | ||
101 | writel(SSP_CTRL0_RUN, &ssp_regs->hw_ssp_ctrl0_set); | |
102 | ||
fa7a51cb | 103 | if (mxs_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg, |
ec33de3d MV |
104 | SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) { |
105 | printf("MXS SPI: Timeout waiting for start\n"); | |
d9fb6a4c | 106 | return -ETIMEDOUT; |
ec33de3d MV |
107 | } |
108 | ||
c7065fa8 MV |
109 | if (write) |
110 | writel(*data++, &ssp_regs->hw_ssp_data); | |
ec33de3d MV |
111 | |
112 | writel(SSP_CTRL0_DATA_XFER, &ssp_regs->hw_ssp_ctrl0_set); | |
113 | ||
c7065fa8 | 114 | if (!write) { |
fa7a51cb | 115 | if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_status_reg, |
ec33de3d MV |
116 | SSP_STATUS_FIFO_EMPTY, MXS_SPI_MAX_TIMEOUT)) { |
117 | printf("MXS SPI: Timeout waiting for data\n"); | |
d9fb6a4c | 118 | return -ETIMEDOUT; |
ec33de3d MV |
119 | } |
120 | ||
c7065fa8 MV |
121 | *data = readl(&ssp_regs->hw_ssp_data); |
122 | data++; | |
ec33de3d MV |
123 | } |
124 | ||
fa7a51cb | 125 | if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg, |
ec33de3d MV |
126 | SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) { |
127 | printf("MXS SPI: Timeout waiting for finish\n"); | |
d9fb6a4c | 128 | return -ETIMEDOUT; |
ec33de3d MV |
129 | } |
130 | } | |
131 | ||
132 | return 0; | |
ccd4d5a0 MV |
133 | } |
134 | ||
d99b018a LM |
135 | static int mxs_spi_xfer_dma(struct mxs_spi_priv *priv, |
136 | char *data, int length, int write, | |
137 | unsigned long flags) | |
138 | { struct mxs_ssp_regs *ssp_regs = priv->regs; | |
2c432144 MV |
139 | const int xfer_max_sz = 0xff00; |
140 | const int desc_count = DIV_ROUND_UP(length, xfer_max_sz) + 1; | |
2c432144 MV |
141 | struct mxs_dma_desc *dp; |
142 | uint32_t ctrl0; | |
7c5e6f7a | 143 | uint32_t cache_data_count; |
88d15559 | 144 | const uint32_t dstart = (uint32_t)data; |
7c5e6f7a | 145 | int dmach; |
2c432144 | 146 | int tl; |
e9f7eafd | 147 | int ret = 0; |
2c432144 | 148 | |
c96e78cc MV |
149 | #if defined(CONFIG_MX23) |
150 | const int mxs_spi_pio_words = 1; | |
151 | #elif defined(CONFIG_MX28) | |
152 | const int mxs_spi_pio_words = 4; | |
153 | #endif | |
154 | ||
2c432144 MV |
155 | ALLOC_CACHE_ALIGN_BUFFER(struct mxs_dma_desc, desc, desc_count); |
156 | ||
157 | memset(desc, 0, sizeof(struct mxs_dma_desc) * desc_count); | |
7c5e6f7a | 158 | |
2c432144 MV |
159 | ctrl0 = readl(&ssp_regs->hw_ssp_ctrl0); |
160 | ctrl0 |= SSP_CTRL0_DATA_XFER; | |
7c5e6f7a MV |
161 | |
162 | if (flags & SPI_XFER_BEGIN) | |
163 | ctrl0 |= SSP_CTRL0_LOCK_CS; | |
7c5e6f7a MV |
164 | if (!write) |
165 | ctrl0 |= SSP_CTRL0_READ; | |
166 | ||
7c5e6f7a MV |
167 | if (length % ARCH_DMA_MINALIGN) |
168 | cache_data_count = roundup(length, ARCH_DMA_MINALIGN); | |
169 | else | |
170 | cache_data_count = length; | |
171 | ||
88d15559 | 172 | /* Flush data to DRAM so DMA can pick them up */ |
2c432144 | 173 | if (write) |
88d15559 MV |
174 | flush_dcache_range(dstart, dstart + cache_data_count); |
175 | ||
176 | /* Invalidate the area, so no writeback into the RAM races with DMA */ | |
177 | invalidate_dcache_range(dstart, dstart + cache_data_count); | |
7c5e6f7a | 178 | |
d99b018a | 179 | dmach = priv->dma_channel; |
2c432144 MV |
180 | |
181 | dp = desc; | |
182 | while (length) { | |
183 | dp->address = (dma_addr_t)dp; | |
184 | dp->cmd.address = (dma_addr_t)data; | |
185 | ||
186 | /* | |
187 | * This is correct, even though it does indeed look insane. | |
188 | * I hereby have to, wholeheartedly, thank Freescale Inc., | |
189 | * for always inventing insane hardware and keeping me busy | |
190 | * and employed ;-) | |
191 | */ | |
192 | if (write) | |
193 | dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ; | |
194 | else | |
195 | dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE; | |
196 | ||
197 | /* | |
198 | * The DMA controller can transfer large chunks (64kB) at | |
199 | * time by setting the transfer length to 0. Setting tl to | |
200 | * 0x10000 will overflow below and make .data contain 0. | |
201 | * Otherwise, 0xff00 is the transfer maximum. | |
202 | */ | |
203 | if (length >= 0x10000) | |
204 | tl = 0x10000; | |
205 | else | |
206 | tl = min(length, xfer_max_sz); | |
207 | ||
208 | dp->cmd.data |= | |
e9f7eafd | 209 | ((tl & 0xffff) << MXS_DMA_DESC_BYTES_OFFSET) | |
c96e78cc | 210 | (mxs_spi_pio_words << MXS_DMA_DESC_PIO_WORDS_OFFSET) | |
2c432144 MV |
211 | MXS_DMA_DESC_HALT_ON_TERMINATE | |
212 | MXS_DMA_DESC_TERMINATE_FLUSH; | |
7c5e6f7a | 213 | |
2c432144 MV |
214 | data += tl; |
215 | length -= tl; | |
216 | ||
e9f7eafd MV |
217 | if (!length) { |
218 | dp->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM; | |
219 | ||
220 | if (flags & SPI_XFER_END) { | |
221 | ctrl0 &= ~SSP_CTRL0_LOCK_CS; | |
222 | ctrl0 |= SSP_CTRL0_IGNORE_CRC; | |
223 | } | |
224 | } | |
225 | ||
226 | /* | |
c96e78cc MV |
227 | * Write CTRL0, CMD0, CMD1 and XFER_SIZE registers in |
228 | * case of MX28, write only CTRL0 in case of MX23 due | |
229 | * to the difference in register layout. It is utterly | |
e9f7eafd MV |
230 | * essential that the XFER_SIZE register is written on |
231 | * a per-descriptor basis with the same size as is the | |
232 | * descriptor! | |
233 | */ | |
234 | dp->cmd.pio_words[0] = ctrl0; | |
c96e78cc | 235 | #ifdef CONFIG_MX28 |
e9f7eafd MV |
236 | dp->cmd.pio_words[1] = 0; |
237 | dp->cmd.pio_words[2] = 0; | |
238 | dp->cmd.pio_words[3] = tl; | |
c96e78cc | 239 | #endif |
e9f7eafd | 240 | |
2c432144 MV |
241 | mxs_dma_desc_append(dmach, dp); |
242 | ||
243 | dp++; | |
244 | } | |
245 | ||
7c5e6f7a | 246 | if (mxs_dma_go(dmach)) |
e9f7eafd | 247 | ret = -EINVAL; |
7c5e6f7a MV |
248 | |
249 | /* The data arrived into DRAM, invalidate cache over them */ | |
88d15559 MV |
250 | if (!write) |
251 | invalidate_dcache_range(dstart, dstart + cache_data_count); | |
7c5e6f7a | 252 | |
e9f7eafd | 253 | return ret; |
7c5e6f7a MV |
254 | } |
255 | ||
d99b018a LM |
256 | int mxs_spi_xfer(struct udevice *dev, unsigned int bitlen, |
257 | const void *dout, void *din, unsigned long flags) | |
258 | { | |
259 | struct udevice *bus = dev_get_parent(dev); | |
260 | struct mxs_spi_priv *priv = dev_get_priv(bus); | |
261 | struct mxs_ssp_regs *ssp_regs = priv->regs; | |
ccd4d5a0 MV |
262 | int len = bitlen / 8; |
263 | char dummy; | |
264 | int write = 0; | |
265 | char *data = NULL; | |
7c5e6f7a | 266 | int dma = 1; |
7c5e6f7a | 267 | |
ccd4d5a0 MV |
268 | if (bitlen == 0) { |
269 | if (flags & SPI_XFER_END) { | |
270 | din = (void *)&dummy; | |
271 | len = 1; | |
272 | } else | |
273 | return 0; | |
274 | } | |
275 | ||
276 | /* Half-duplex only */ | |
277 | if (din && dout) | |
278 | return -EINVAL; | |
279 | /* No data */ | |
280 | if (!din && !dout) | |
281 | return 0; | |
282 | ||
283 | if (dout) { | |
284 | data = (char *)dout; | |
285 | write = 1; | |
286 | } else if (din) { | |
287 | data = (char *)din; | |
288 | write = 0; | |
289 | } | |
290 | ||
7c5e6f7a MV |
291 | /* |
292 | * Check for alignment, if the buffer is aligned, do DMA transfer, | |
293 | * PIO otherwise. This is a temporary workaround until proper bounce | |
294 | * buffer is in place. | |
295 | */ | |
296 | if (dma) { | |
297 | if (((uint32_t)data) & (ARCH_DMA_MINALIGN - 1)) | |
298 | dma = 0; | |
299 | if (((uint32_t)len) & (ARCH_DMA_MINALIGN - 1)) | |
300 | dma = 0; | |
301 | } | |
302 | ||
303 | if (!dma || (len < MXSSSP_SMALL_TRANSFER)) { | |
304 | writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr); | |
d99b018a | 305 | return mxs_spi_xfer_pio(priv, data, len, write, flags); |
7c5e6f7a MV |
306 | } else { |
307 | writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set); | |
d99b018a | 308 | return mxs_spi_xfer_dma(priv, data, len, write, flags); |
d99b018a | 309 | } |
d99b018a LM |
310 | } |
311 | ||
d99b018a LM |
312 | static int mxs_spi_probe(struct udevice *bus) |
313 | { | |
8a8d24bd | 314 | struct mxs_spi_plat *plat = dev_get_plat(bus); |
d99b018a LM |
315 | struct mxs_spi_priv *priv = dev_get_priv(bus); |
316 | int ret; | |
317 | ||
318 | debug("%s: probe\n", __func__); | |
ec0c81f8 LM |
319 | |
320 | #if CONFIG_IS_ENABLED(OF_PLATDATA) | |
df17cdc9 | 321 | struct dtd_fsl_imx23_spi *dtplat = &plat->dtplat; |
ec0c81f8 LM |
322 | struct phandle_1_arg *p1a = &dtplat->clocks[0]; |
323 | ||
324 | priv->regs = (struct mxs_ssp_regs *)dtplat->reg[0]; | |
325 | priv->dma_channel = dtplat->dmas[1]; | |
326 | priv->clk_id = p1a->arg[0]; | |
327 | priv->max_freq = dtplat->spi_max_frequency; | |
328 | plat->num_cs = dtplat->num_cs; | |
329 | ||
330 | debug("OF_PLATDATA: regs: 0x%x max freq: %d clkid: %d\n", | |
331 | (unsigned int)priv->regs, priv->max_freq, priv->clk_id); | |
332 | #else | |
d99b018a LM |
333 | priv->regs = (struct mxs_ssp_regs *)plat->base; |
334 | priv->max_freq = plat->frequency; | |
335 | ||
336 | priv->dma_channel = plat->dma_id; | |
337 | priv->clk_id = plat->clk_id; | |
ec0c81f8 | 338 | #endif |
d99b018a | 339 | |
c2050e10 LM |
340 | mxs_reset_block(&priv->regs->hw_ssp_ctrl0_reg); |
341 | ||
d99b018a LM |
342 | ret = mxs_dma_init_channel(priv->dma_channel); |
343 | if (ret) { | |
344 | printf("%s: DMA init channel error %d\n", __func__, ret); | |
345 | return ret; | |
346 | } | |
347 | ||
348 | return 0; | |
349 | } | |
350 | ||
351 | static int mxs_spi_claim_bus(struct udevice *dev) | |
352 | { | |
353 | struct udevice *bus = dev_get_parent(dev); | |
354 | struct mxs_spi_priv *priv = dev_get_priv(bus); | |
355 | struct mxs_ssp_regs *ssp_regs = priv->regs; | |
356 | int cs = spi_chip_select(dev); | |
357 | ||
358 | /* | |
359 | * i.MX28 supports up to 3 CS (SSn0, SSn1, SSn2) | |
360 | * To set them it uses following tuple (WAIT_FOR_IRQ,WAIT_FOR_CMD), | |
361 | * where: | |
362 | * | |
363 | * WAIT_FOR_IRQ is bit 21 of HW_SSP_CTRL0 | |
364 | * WAIT_FOR_CMD is bit 20 (#defined as MXS_SSP_CHIPSELECT_SHIFT here) of | |
365 | * HW_SSP_CTRL0 | |
366 | * SSn0 b00 | |
367 | * SSn1 b01 | |
368 | * SSn2 b10 (which require setting WAIT_FOR_IRQ) | |
369 | * | |
370 | * However, for now i.MX28 SPI driver will support up till 2 CSes | |
371 | * (SSn0, and SSn1). | |
372 | */ | |
373 | ||
374 | /* Ungate SSP clock and set active CS */ | |
375 | clrsetbits_le32(&ssp_regs->hw_ssp_ctrl0, | |
376 | BIT(MXS_SSP_CHIPSELECT_SHIFT) | | |
377 | SSP_CTRL0_CLKGATE, (cs << MXS_SSP_CHIPSELECT_SHIFT)); | |
378 | ||
379 | return 0; | |
380 | } | |
381 | ||
382 | static int mxs_spi_release_bus(struct udevice *dev) | |
383 | { | |
384 | struct udevice *bus = dev_get_parent(dev); | |
385 | struct mxs_spi_priv *priv = dev_get_priv(bus); | |
386 | struct mxs_ssp_regs *ssp_regs = priv->regs; | |
387 | ||
388 | /* Gate SSP clock */ | |
389 | setbits_le32(&ssp_regs->hw_ssp_ctrl0, SSP_CTRL0_CLKGATE); | |
390 | ||
391 | return 0; | |
392 | } | |
393 | ||
394 | static int mxs_spi_set_speed(struct udevice *bus, uint speed) | |
395 | { | |
396 | struct mxs_spi_priv *priv = dev_get_priv(bus); | |
397 | #ifdef CONFIG_MX28 | |
398 | int clkid = priv->clk_id - MXS_SSP_IMX28_CLKID_SSP0; | |
399 | #else /* CONFIG_MX23 */ | |
400 | int clkid = priv->clk_id - MXS_SSP_IMX23_CLKID_SSP0; | |
401 | #endif | |
402 | if (speed > priv->max_freq) | |
403 | speed = priv->max_freq; | |
404 | ||
405 | debug("%s speed: %u [Hz] clkid: %d\n", __func__, speed, clkid); | |
406 | mxs_set_ssp_busclock(clkid, speed / 1000); | |
407 | ||
408 | return 0; | |
409 | } | |
410 | ||
411 | static int mxs_spi_set_mode(struct udevice *bus, uint mode) | |
412 | { | |
413 | struct mxs_spi_priv *priv = dev_get_priv(bus); | |
414 | struct mxs_ssp_regs *ssp_regs = priv->regs; | |
415 | u32 reg; | |
416 | ||
417 | priv->mode = mode; | |
418 | debug("%s: mode 0x%x\n", __func__, mode); | |
419 | ||
420 | reg = SSP_CTRL1_SSP_MODE_SPI | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS; | |
421 | reg |= (priv->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0; | |
422 | reg |= (priv->mode & SPI_CPHA) ? SSP_CTRL1_PHASE : 0; | |
423 | writel(reg, &ssp_regs->hw_ssp_ctrl1); | |
424 | ||
425 | /* Single bit SPI support */ | |
426 | writel(SSP_CTRL0_BUS_WIDTH_ONE_BIT, &ssp_regs->hw_ssp_ctrl0); | |
427 | ||
428 | return 0; | |
429 | } | |
430 | ||
431 | static const struct dm_spi_ops mxs_spi_ops = { | |
432 | .claim_bus = mxs_spi_claim_bus, | |
433 | .release_bus = mxs_spi_release_bus, | |
434 | .xfer = mxs_spi_xfer, | |
435 | .set_speed = mxs_spi_set_speed, | |
436 | .set_mode = mxs_spi_set_mode, | |
437 | /* | |
438 | * cs_info is not needed, since we require all chip selects to be | |
439 | * in the device tree explicitly | |
440 | */ | |
441 | }; | |
442 | ||
443 | #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) | |
d1998a9f | 444 | static int mxs_of_to_plat(struct udevice *bus) |
d99b018a | 445 | { |
8a8d24bd | 446 | struct mxs_spi_plat *plat = bus->plat; |
d99b018a LM |
447 | u32 prop[2]; |
448 | int ret; | |
449 | ||
450 | plat->base = dev_read_addr(bus); | |
451 | plat->frequency = | |
452 | dev_read_u32_default(bus, "spi-max-frequency", 40000000); | |
453 | plat->num_cs = dev_read_u32_default(bus, "num-cs", 2); | |
454 | ||
455 | ret = dev_read_u32_array(bus, "dmas", prop, ARRAY_SIZE(prop)); | |
456 | if (ret) { | |
457 | printf("%s: Reading 'dmas' property failed!\n", __func__); | |
458 | return ret; | |
459 | } | |
460 | plat->dma_id = prop[1]; | |
461 | ||
462 | ret = dev_read_u32_array(bus, "clocks", prop, ARRAY_SIZE(prop)); | |
463 | if (ret) { | |
464 | printf("%s: Reading 'clocks' property failed!\n", __func__); | |
465 | return ret; | |
466 | } | |
467 | plat->clk_id = prop[1]; | |
468 | ||
469 | debug("%s: base=0x%x, max-frequency=%d num-cs=%d dma_id=%d clk_id=%d\n", | |
470 | __func__, (uint)plat->base, plat->frequency, plat->num_cs, | |
471 | plat->dma_id, plat->clk_id); | |
472 | ||
473 | return 0; | |
474 | } | |
d99b018a LM |
475 | |
476 | static const struct udevice_id mxs_spi_ids[] = { | |
477 | { .compatible = "fsl,imx23-spi" }, | |
478 | { .compatible = "fsl,imx28-spi" }, | |
479 | { } | |
480 | }; | |
ec0c81f8 | 481 | #endif |
d99b018a | 482 | |
e3e2470f | 483 | U_BOOT_DRIVER(fsl_imx23_spi) = { |
ec0c81f8 | 484 | .name = "fsl_imx23_spi", |
d99b018a LM |
485 | .id = UCLASS_SPI, |
486 | #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) | |
487 | .of_match = mxs_spi_ids, | |
d1998a9f | 488 | .of_to_plat = mxs_of_to_plat, |
d99b018a | 489 | #endif |
8a8d24bd | 490 | .plat_auto = sizeof(struct mxs_spi_plat), |
d99b018a | 491 | .ops = &mxs_spi_ops, |
41575d8e | 492 | .priv_auto = sizeof(struct mxs_spi_priv), |
d99b018a LM |
493 | .probe = mxs_spi_probe, |
494 | }; | |
addf358b WL |
495 | |
496 | U_BOOT_DRIVER_ALIAS(fsl_imx23_spi, fsl_imx28_spi) |