]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
10e8bf88 SR |
2 | /* |
3 | * Copyright (C) 2012 | |
4 | * Altera Corporation <www.altera.com> | |
10e8bf88 SR |
5 | */ |
6 | ||
d678a59d | 7 | #include <common.h> |
64c7c8c9 | 8 | #include <clk.h> |
f7ae49fc | 9 | #include <log.h> |
10e8bf88 SR |
10 | #include <dm.h> |
11 | #include <fdtdec.h> | |
12 | #include <malloc.h> | |
ac7e14ae | 13 | #include <reset.h> |
10e8bf88 | 14 | #include <spi.h> |
d6407720 | 15 | #include <spi-mem.h> |
336d4615 | 16 | #include <dm/device_compat.h> |
61b29b82 | 17 | #include <linux/err.h> |
1221ce45 | 18 | #include <linux/errno.h> |
e515a2bb | 19 | #include <linux/io.h> |
ffab2121 | 20 | #include <linux/sizes.h> |
13248d66 | 21 | #include <linux/time.h> |
248fe9f3 | 22 | #include <zynqmp_firmware.h> |
10e8bf88 | 23 | #include "cadence_qspi.h" |
248fe9f3 | 24 | #include <dt-bindings/power/xlnx-versal-power.h> |
10e8bf88 SR |
25 | |
26 | #define CQSPI_STIG_READ 0 | |
27 | #define CQSPI_STIG_WRITE 1 | |
ffab2121 VR |
28 | #define CQSPI_READ 2 |
29 | #define CQSPI_WRITE 3 | |
10e8bf88 | 30 | |
f7d4cab1 | 31 | __weak int cadence_qspi_apb_dma_read(struct cadence_spi_priv *priv, |
cf553bf2 KR |
32 | const struct spi_mem_op *op) |
33 | { | |
34 | return 0; | |
35 | } | |
36 | ||
bf8dae5f KR |
37 | __weak int cadence_qspi_versal_flash_reset(struct udevice *dev) |
38 | { | |
39 | return 0; | |
40 | } | |
41 | ||
c77efca2 UK |
42 | __weak ofnode cadence_qspi_get_subnode(struct udevice *dev) |
43 | { | |
44 | return dev_read_first_subnode(dev); | |
45 | } | |
46 | ||
10e8bf88 SR |
47 | static int cadence_spi_write_speed(struct udevice *bus, uint hz) |
48 | { | |
10e8bf88 SR |
49 | struct cadence_spi_priv *priv = dev_get_priv(bus); |
50 | ||
51 | cadence_qspi_apb_config_baudrate_div(priv->regbase, | |
f7d4cab1 | 52 | priv->ref_clk_hz, hz); |
10e8bf88 SR |
53 | |
54 | /* Reconfigure delay timing if speed is changed. */ | |
f7d4cab1 ARS |
55 | cadence_qspi_apb_delay(priv->regbase, priv->ref_clk_hz, hz, |
56 | priv->tshsl_ns, priv->tsd2d_ns, | |
57 | priv->tchsh_ns, priv->tslch_ns); | |
10e8bf88 SR |
58 | |
59 | return 0; | |
60 | } | |
61 | ||
f7d4cab1 | 62 | static int cadence_spi_read_id(struct cadence_spi_priv *priv, u8 len, |
38b0852b | 63 | u8 *idcode) |
d6407720 | 64 | { |
d0003b5e | 65 | int err; |
f7d4cab1 | 66 | |
d6407720 VR |
67 | struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x9F, 1), |
68 | SPI_MEM_OP_NO_ADDR, | |
69 | SPI_MEM_OP_NO_DUMMY, | |
70 | SPI_MEM_OP_DATA_IN(len, idcode, 1)); | |
71 | ||
f7d4cab1 | 72 | err = cadence_qspi_apb_command_read_setup(priv, &op); |
d0003b5e | 73 | if (!err) |
f7d4cab1 | 74 | err = cadence_qspi_apb_command_read(priv, &op); |
d0003b5e ARS |
75 | |
76 | return err; | |
d6407720 VR |
77 | } |
78 | ||
10e8bf88 | 79 | /* Calibration sequence to determine the read data capture delay register */ |
98fbd71d | 80 | static int spi_calibration(struct udevice *bus, uint hz) |
10e8bf88 | 81 | { |
10e8bf88 SR |
82 | struct cadence_spi_priv *priv = dev_get_priv(bus); |
83 | void *base = priv->regbase; | |
10e8bf88 SR |
84 | unsigned int idcode = 0, temp = 0; |
85 | int err = 0, i, range_lo = -1, range_hi = -1; | |
86 | ||
87 | /* start with slowest clock (1 MHz) */ | |
88 | cadence_spi_write_speed(bus, 1000000); | |
89 | ||
90 | /* configure the read data capture delay register to 0 */ | |
91 | cadence_qspi_apb_readdata_capture(base, 1, 0); | |
92 | ||
93 | /* Enable QSPI */ | |
94 | cadence_qspi_apb_controller_enable(base); | |
95 | ||
96 | /* read the ID which will be our golden value */ | |
f7d4cab1 | 97 | err = cadence_spi_read_id(priv, 3, (u8 *)&idcode); |
10e8bf88 SR |
98 | if (err) { |
99 | puts("SF: Calibration failed (read)\n"); | |
100 | return err; | |
101 | } | |
102 | ||
103 | /* use back the intended clock and find low range */ | |
98fbd71d | 104 | cadence_spi_write_speed(bus, hz); |
10e8bf88 SR |
105 | for (i = 0; i < CQSPI_READ_CAPTURE_MAX_DELAY; i++) { |
106 | /* Disable QSPI */ | |
107 | cadence_qspi_apb_controller_disable(base); | |
108 | ||
109 | /* reconfigure the read data capture delay register */ | |
110 | cadence_qspi_apb_readdata_capture(base, 1, i); | |
111 | ||
112 | /* Enable back QSPI */ | |
113 | cadence_qspi_apb_controller_enable(base); | |
114 | ||
115 | /* issue a RDID to get the ID value */ | |
f7d4cab1 | 116 | err = cadence_spi_read_id(priv, 3, (u8 *)&temp); |
10e8bf88 SR |
117 | if (err) { |
118 | puts("SF: Calibration failed (read)\n"); | |
119 | return err; | |
120 | } | |
121 | ||
122 | /* search for range lo */ | |
123 | if (range_lo == -1 && temp == idcode) { | |
124 | range_lo = i; | |
125 | continue; | |
126 | } | |
127 | ||
128 | /* search for range hi */ | |
129 | if (range_lo != -1 && temp != idcode) { | |
130 | range_hi = i - 1; | |
131 | break; | |
132 | } | |
133 | range_hi = i; | |
134 | } | |
135 | ||
136 | if (range_lo == -1) { | |
137 | puts("SF: Calibration failed (low range)\n"); | |
138 | return err; | |
139 | } | |
140 | ||
141 | /* Disable QSPI for subsequent initialization */ | |
142 | cadence_qspi_apb_controller_disable(base); | |
143 | ||
144 | /* configure the final value for read data capture delay register */ | |
145 | cadence_qspi_apb_readdata_capture(base, 1, (range_hi + range_lo) / 2); | |
146 | debug("SF: Read data capture delay calibrated to %i (%i - %i)\n", | |
147 | (range_hi + range_lo) / 2, range_lo, range_hi); | |
148 | ||
149 | /* just to ensure we do once only when speed or chip select change */ | |
98fbd71d | 150 | priv->qspi_calibrated_hz = hz; |
10e8bf88 SR |
151 | priv->qspi_calibrated_cs = spi_chip_select(bus); |
152 | ||
153 | return 0; | |
154 | } | |
155 | ||
156 | static int cadence_spi_set_speed(struct udevice *bus, uint hz) | |
157 | { | |
10e8bf88 SR |
158 | struct cadence_spi_priv *priv = dev_get_priv(bus); |
159 | int err; | |
160 | ||
f7d4cab1 ARS |
161 | if (!hz || hz > priv->max_hz) |
162 | hz = priv->max_hz; | |
10e8bf88 SR |
163 | /* Disable QSPI */ |
164 | cadence_qspi_apb_controller_disable(priv->regbase); | |
165 | ||
98fbd71d | 166 | /* |
bd8c8dcd PY |
167 | * If the device tree already provides a read delay value, use that |
168 | * instead of calibrating. | |
98fbd71d | 169 | */ |
f7d4cab1 | 170 | if (priv->read_delay >= 0) { |
bd8c8dcd PY |
171 | cadence_spi_write_speed(bus, hz); |
172 | cadence_qspi_apb_readdata_capture(priv->regbase, 1, | |
f7d4cab1 | 173 | priv->read_delay); |
bd8c8dcd PY |
174 | } else if (priv->previous_hz != hz || |
175 | priv->qspi_calibrated_hz != hz || | |
176 | priv->qspi_calibrated_cs != spi_chip_select(bus)) { | |
177 | /* | |
178 | * Calibration required for different current SCLK speed, | |
179 | * requested SCLK speed or chip select | |
180 | */ | |
98fbd71d | 181 | err = spi_calibration(bus, hz); |
10e8bf88 SR |
182 | if (err) |
183 | return err; | |
98fbd71d CLS |
184 | |
185 | /* prevent calibration run when same as previous request */ | |
186 | priv->previous_hz = hz; | |
10e8bf88 SR |
187 | } |
188 | ||
189 | /* Enable QSPI */ | |
190 | cadence_qspi_apb_controller_enable(priv->regbase); | |
191 | ||
192 | debug("%s: speed=%d\n", __func__, hz); | |
193 | ||
194 | return 0; | |
195 | } | |
196 | ||
197 | static int cadence_spi_probe(struct udevice *bus) | |
198 | { | |
0fd3d911 | 199 | struct cadence_spi_plat *plat = dev_get_plat(bus); |
10e8bf88 | 200 | struct cadence_spi_priv *priv = dev_get_priv(bus); |
0a9c2874 | 201 | struct clk clk; |
ac7e14ae | 202 | int ret; |
10e8bf88 | 203 | |
f7d4cab1 ARS |
204 | priv->regbase = plat->regbase; |
205 | priv->ahbbase = plat->ahbbase; | |
206 | priv->is_dma = plat->is_dma; | |
207 | priv->is_decoded_cs = plat->is_decoded_cs; | |
208 | priv->fifo_depth = plat->fifo_depth; | |
209 | priv->fifo_width = plat->fifo_width; | |
210 | priv->trigger_address = plat->trigger_address; | |
211 | priv->read_delay = plat->read_delay; | |
212 | priv->ahbsize = plat->ahbsize; | |
213 | priv->max_hz = plat->max_hz; | |
214 | ||
215 | priv->page_size = plat->page_size; | |
216 | priv->block_size = plat->block_size; | |
217 | priv->tshsl_ns = plat->tshsl_ns; | |
218 | priv->tsd2d_ns = plat->tsd2d_ns; | |
219 | priv->tchsh_ns = plat->tchsh_ns; | |
220 | priv->tslch_ns = plat->tslch_ns; | |
10e8bf88 | 221 | |
8581d992 | 222 | if (IS_ENABLED(CONFIG_ZYNQMP_FIRMWARE)) |
248fe9f3 KR |
223 | xilinx_pm_request(PM_REQUEST_NODE, PM_DEV_OSPI, |
224 | ZYNQMP_PM_CAPABILITY_ACCESS, ZYNQMP_PM_MAX_QOS, | |
225 | ZYNQMP_PM_REQUEST_ACK_NO, NULL); | |
226 | ||
f7d4cab1 | 227 | if (priv->ref_clk_hz == 0) { |
0a9c2874 PY |
228 | ret = clk_get_by_index(bus, 0, &clk); |
229 | if (ret) { | |
55b3ba4c | 230 | #ifdef CONFIG_HAS_CQSPI_REF_CLK |
f7d4cab1 | 231 | priv->ref_clk_hz = CONFIG_CQSPI_REF_CLK; |
55b3ba4c | 232 | #elif defined(CONFIG_ARCH_SOCFPGA) |
f7d4cab1 | 233 | priv->ref_clk_hz = cm_get_qspi_controller_clk_hz(); |
0a9c2874 PY |
234 | #else |
235 | return ret; | |
236 | #endif | |
237 | } else { | |
f7d4cab1 | 238 | priv->ref_clk_hz = clk_get_rate(&clk); |
f7d4cab1 ARS |
239 | if (IS_ERR_VALUE(priv->ref_clk_hz)) |
240 | return priv->ref_clk_hz; | |
0a9c2874 PY |
241 | } |
242 | } | |
243 | ||
e145606f CG |
244 | priv->resets = devm_reset_bulk_get_optional(bus); |
245 | if (priv->resets) | |
246 | reset_deassert_bulk(priv->resets); | |
ac7e14ae | 247 | |
10e8bf88 | 248 | if (!priv->qspi_is_init) { |
f7d4cab1 | 249 | cadence_qspi_apb_controller_init(priv); |
10e8bf88 SR |
250 | priv->qspi_is_init = 1; |
251 | } | |
252 | ||
f7d4cab1 | 253 | priv->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC, priv->ref_clk_hz); |
a6903aa7 | 254 | |
34dec6a4 ARS |
255 | /* Versal and Versal-NET use spi calibration to set read delay */ |
256 | if (CONFIG_IS_ENABLED(ARCH_VERSAL) || | |
257 | CONFIG_IS_ENABLED(ARCH_VERSAL_NET)) | |
f7d4cab1 ARS |
258 | if (priv->read_delay >= 0) |
259 | priv->read_delay = -1; | |
bf8dae5f | 260 | |
34dec6a4 ARS |
261 | /* Reset ospi flash device */ |
262 | return cadence_qspi_versal_flash_reset(bus); | |
10e8bf88 SR |
263 | } |
264 | ||
ac7e14ae SG |
265 | static int cadence_spi_remove(struct udevice *dev) |
266 | { | |
267 | struct cadence_spi_priv *priv = dev_get_priv(dev); | |
e145606f | 268 | int ret = 0; |
ac7e14ae | 269 | |
e145606f CG |
270 | if (priv->resets) |
271 | ret = reset_release_bulk(priv->resets); | |
272 | ||
273 | return ret; | |
ac7e14ae SG |
274 | } |
275 | ||
10e8bf88 SR |
276 | static int cadence_spi_set_mode(struct udevice *bus, uint mode) |
277 | { | |
278 | struct cadence_spi_priv *priv = dev_get_priv(bus); | |
10e8bf88 SR |
279 | |
280 | /* Disable QSPI */ | |
281 | cadence_qspi_apb_controller_disable(priv->regbase); | |
282 | ||
283 | /* Set SPI mode */ | |
7d403f28 | 284 | cadence_qspi_apb_set_clk_mode(priv->regbase, mode); |
10e8bf88 | 285 | |
ffab2121 | 286 | /* Enable Direct Access Controller */ |
f7d4cab1 | 287 | if (priv->use_dac_mode) |
ffab2121 VR |
288 | cadence_qspi_apb_dac_mode_enable(priv->regbase); |
289 | ||
10e8bf88 SR |
290 | /* Enable QSPI */ |
291 | cadence_qspi_apb_controller_enable(priv->regbase); | |
292 | ||
293 | return 0; | |
294 | } | |
295 | ||
d6407720 VR |
296 | static int cadence_spi_mem_exec_op(struct spi_slave *spi, |
297 | const struct spi_mem_op *op) | |
10e8bf88 | 298 | { |
d6407720 | 299 | struct udevice *bus = spi->dev->parent; |
10e8bf88 SR |
300 | struct cadence_spi_priv *priv = dev_get_priv(bus); |
301 | void *base = priv->regbase; | |
10e8bf88 | 302 | int err = 0; |
d6407720 | 303 | u32 mode; |
10e8bf88 SR |
304 | |
305 | /* Set Chip select */ | |
d6407720 | 306 | cadence_qspi_apb_chipselect(base, spi_chip_select(spi->dev), |
f7d4cab1 | 307 | priv->is_decoded_cs); |
10e8bf88 | 308 | |
d6407720 | 309 | if (op->data.dir == SPI_MEM_DATA_IN && op->data.buf.in) { |
53f4ef0a DG |
310 | /* |
311 | * Performing reads in DAC mode forces to read minimum 4 bytes | |
312 | * which is unsupported on some flash devices during register | |
313 | * reads, prefer STIG mode for such small reads. | |
314 | */ | |
8077d296 | 315 | if (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX) |
d6407720 VR |
316 | mode = CQSPI_STIG_READ; |
317 | else | |
ffab2121 | 318 | mode = CQSPI_READ; |
d6407720 | 319 | } else { |
8077d296 | 320 | if (op->data.nbytes <= CQSPI_STIG_DATA_LEN_MAX) |
d6407720 VR |
321 | mode = CQSPI_STIG_WRITE; |
322 | else | |
ffab2121 | 323 | mode = CQSPI_WRITE; |
d6407720 | 324 | } |
10e8bf88 | 325 | |
d6407720 VR |
326 | switch (mode) { |
327 | case CQSPI_STIG_READ: | |
f7d4cab1 | 328 | err = cadence_qspi_apb_command_read_setup(priv, op); |
38b0852b | 329 | if (!err) |
f7d4cab1 | 330 | err = cadence_qspi_apb_command_read(priv, op); |
10e8bf88 | 331 | break; |
d6407720 | 332 | case CQSPI_STIG_WRITE: |
f7d4cab1 | 333 | err = cadence_qspi_apb_command_write_setup(priv, op); |
38b0852b | 334 | if (!err) |
f7d4cab1 | 335 | err = cadence_qspi_apb_command_write(priv, op); |
10e8bf88 | 336 | break; |
ffab2121 | 337 | case CQSPI_READ: |
f7d4cab1 | 338 | err = cadence_qspi_apb_read_setup(priv, op); |
cf553bf2 | 339 | if (!err) { |
f7d4cab1 ARS |
340 | if (priv->is_dma) |
341 | err = cadence_qspi_apb_dma_read(priv, op); | |
cf553bf2 | 342 | else |
f7d4cab1 | 343 | err = cadence_qspi_apb_read_execute(priv, op); |
cf553bf2 | 344 | } |
d6407720 | 345 | break; |
ffab2121 | 346 | case CQSPI_WRITE: |
f7d4cab1 | 347 | err = cadence_qspi_apb_write_setup(priv, op); |
ffab2121 | 348 | if (!err) |
f7d4cab1 | 349 | err = cadence_qspi_apb_write_execute(priv, op); |
d6407720 VR |
350 | break; |
351 | default: | |
352 | err = -1; | |
353 | break; | |
10e8bf88 SR |
354 | } |
355 | ||
356 | return err; | |
357 | } | |
358 | ||
38b0852b PY |
359 | static bool cadence_spi_mem_supports_op(struct spi_slave *slave, |
360 | const struct spi_mem_op *op) | |
361 | { | |
362 | bool all_true, all_false; | |
363 | ||
44e2de04 AN |
364 | /* |
365 | * op->dummy.dtr is required for converting nbytes into ncycles. | |
366 | * Also, don't check the dtr field of the op phase having zero nbytes. | |
367 | */ | |
368 | all_true = op->cmd.dtr && | |
369 | (!op->addr.nbytes || op->addr.dtr) && | |
370 | (!op->dummy.nbytes || op->dummy.dtr) && | |
371 | (!op->data.nbytes || op->data.dtr); | |
372 | ||
38b0852b PY |
373 | all_false = !op->cmd.dtr && !op->addr.dtr && !op->dummy.dtr && |
374 | !op->data.dtr; | |
375 | ||
376 | /* Mixed DTR modes not supported. */ | |
377 | if (!(all_true || all_false)) | |
378 | return false; | |
379 | ||
380 | if (all_true) | |
381 | return spi_mem_dtr_supports_op(slave, op); | |
382 | else | |
383 | return spi_mem_default_supports_op(slave, op); | |
384 | } | |
385 | ||
d1998a9f | 386 | static int cadence_spi_of_to_plat(struct udevice *bus) |
10e8bf88 | 387 | { |
0fd3d911 | 388 | struct cadence_spi_plat *plat = dev_get_plat(bus); |
f7d4cab1 | 389 | struct cadence_spi_priv *priv = dev_get_priv(bus); |
46b633d7 | 390 | ofnode subnode; |
10e8bf88 | 391 | |
320a1938 | 392 | plat->regbase = devfdt_get_addr_index_ptr(bus, 0); |
842fb5de | 393 | plat->ahbbase = devfdt_get_addr_size_index_ptr(bus, 1, &plat->ahbsize); |
46b633d7 SG |
394 | plat->is_decoded_cs = dev_read_bool(bus, "cdns,is-decoded-cs"); |
395 | plat->fifo_depth = dev_read_u32_default(bus, "cdns,fifo-depth", 128); | |
396 | plat->fifo_width = dev_read_u32_default(bus, "cdns,fifo-width", 4); | |
397 | plat->trigger_address = dev_read_u32_default(bus, | |
398 | "cdns,trigger-address", | |
399 | 0); | |
ffab2121 VR |
400 | /* Use DAC mode only when MMIO window is at least 8M wide */ |
401 | if (plat->ahbsize >= SZ_8M) | |
f7d4cab1 | 402 | priv->use_dac_mode = true; |
10e8bf88 | 403 | |
cf553bf2 KR |
404 | plat->is_dma = dev_read_bool(bus, "cdns,is-dma"); |
405 | ||
d466f620 | 406 | /* All other parameters are embedded in the child node */ |
c77efca2 | 407 | subnode = cadence_qspi_get_subnode(bus); |
46b633d7 | 408 | if (!ofnode_valid(subnode)) { |
10e8bf88 SR |
409 | printf("Error: subnode with SPI flash config missing!\n"); |
410 | return -ENODEV; | |
411 | } | |
412 | ||
040f4ba7 | 413 | /* Use 500 KHz as a suitable default */ |
46b633d7 SG |
414 | plat->max_hz = ofnode_read_u32_default(subnode, "spi-max-frequency", |
415 | 500000); | |
040f4ba7 | 416 | |
10e8bf88 | 417 | /* Read other parameters from DT */ |
46b633d7 SG |
418 | plat->page_size = ofnode_read_u32_default(subnode, "page-size", 256); |
419 | plat->block_size = ofnode_read_u32_default(subnode, "block-size", 16); | |
420 | plat->tshsl_ns = ofnode_read_u32_default(subnode, "cdns,tshsl-ns", | |
421 | 200); | |
422 | plat->tsd2d_ns = ofnode_read_u32_default(subnode, "cdns,tsd2d-ns", | |
423 | 255); | |
424 | plat->tchsh_ns = ofnode_read_u32_default(subnode, "cdns,tchsh-ns", 20); | |
425 | plat->tslch_ns = ofnode_read_u32_default(subnode, "cdns,tslch-ns", 20); | |
bd8c8dcd PY |
426 | /* |
427 | * Read delay should be an unsigned value but we use a signed integer | |
428 | * so that negative values can indicate that the device tree did not | |
429 | * specify any signed values and we need to perform the calibration | |
430 | * sequence to find it out. | |
431 | */ | |
432 | plat->read_delay = ofnode_read_s32_default(subnode, "cdns,read-delay", | |
433 | -1); | |
10e8bf88 SR |
434 | |
435 | debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n", | |
436 | __func__, plat->regbase, plat->ahbbase, plat->max_hz, | |
437 | plat->page_size); | |
438 | ||
439 | return 0; | |
440 | } | |
441 | ||
d6407720 VR |
442 | static const struct spi_controller_mem_ops cadence_spi_mem_ops = { |
443 | .exec_op = cadence_spi_mem_exec_op, | |
38b0852b | 444 | .supports_op = cadence_spi_mem_supports_op, |
d6407720 VR |
445 | }; |
446 | ||
10e8bf88 | 447 | static const struct dm_spi_ops cadence_spi_ops = { |
10e8bf88 SR |
448 | .set_speed = cadence_spi_set_speed, |
449 | .set_mode = cadence_spi_set_mode, | |
d6407720 | 450 | .mem_ops = &cadence_spi_mem_ops, |
10e8bf88 SR |
451 | /* |
452 | * cs_info is not needed, since we require all chip selects to be | |
453 | * in the device tree explicitly | |
454 | */ | |
455 | }; | |
456 | ||
457 | static const struct udevice_id cadence_spi_ids[] = { | |
2a3a9993 | 458 | { .compatible = "cdns,qspi-nor" }, |
daa9405d | 459 | { .compatible = "ti,am654-ospi" }, |
10e8bf88 SR |
460 | { } |
461 | }; | |
462 | ||
463 | U_BOOT_DRIVER(cadence_spi) = { | |
464 | .name = "cadence_spi", | |
465 | .id = UCLASS_SPI, | |
466 | .of_match = cadence_spi_ids, | |
467 | .ops = &cadence_spi_ops, | |
d1998a9f | 468 | .of_to_plat = cadence_spi_of_to_plat, |
8a8d24bd | 469 | .plat_auto = sizeof(struct cadence_spi_plat), |
41575d8e | 470 | .priv_auto = sizeof(struct cadence_spi_priv), |
10e8bf88 | 471 | .probe = cadence_spi_probe, |
ac7e14ae SG |
472 | .remove = cadence_spi_remove, |
473 | .flags = DM_FLAG_OS_PREPARE, | |
10e8bf88 | 474 | }; |