]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
d7af6a48 SG |
2 | /* |
3 | * Copyright (c) 2014 Google, Inc | |
d7af6a48 SG |
4 | */ |
5 | ||
46c5391b PD |
6 | #define LOG_CATEGORY UCLASS_SPI |
7 | ||
d7af6a48 SG |
8 | #include <common.h> |
9 | #include <dm.h> | |
10 | #include <errno.h> | |
f7ae49fc | 11 | #include <log.h> |
d7af6a48 SG |
12 | #include <malloc.h> |
13 | #include <spi.h> | |
46c5391b | 14 | #include <dm/device_compat.h> |
d7af6a48 SG |
15 | #include <dm/device-internal.h> |
16 | #include <dm/uclass-internal.h> | |
d7af6a48 SG |
17 | #include <dm/lists.h> |
18 | #include <dm/util.h> | |
19 | ||
20 | DECLARE_GLOBAL_DATA_PTR; | |
21 | ||
12bfb2e0 SG |
22 | #define SPI_DEFAULT_SPEED_HZ 100000 |
23 | ||
d7af6a48 SG |
24 | static int spi_set_speed_mode(struct udevice *bus, int speed, int mode) |
25 | { | |
26 | struct dm_spi_ops *ops; | |
27 | int ret; | |
28 | ||
29 | ops = spi_get_ops(bus); | |
30 | if (ops->set_speed) | |
31 | ret = ops->set_speed(bus, speed); | |
32 | else | |
33 | ret = -EINVAL; | |
34 | if (ret) { | |
46c5391b | 35 | dev_err(bus, "Cannot set speed (err=%d)\n", ret); |
d7af6a48 SG |
36 | return ret; |
37 | } | |
38 | ||
39 | if (ops->set_mode) | |
40 | ret = ops->set_mode(bus, mode); | |
41 | else | |
42 | ret = -EINVAL; | |
43 | if (ret) { | |
46c5391b | 44 | dev_err(bus, "Cannot set mode (err=%d)\n", ret); |
d7af6a48 SG |
45 | return ret; |
46 | } | |
47 | ||
48 | return 0; | |
49 | } | |
50 | ||
7a3eff4c | 51 | int dm_spi_claim_bus(struct udevice *dev) |
d7af6a48 | 52 | { |
d7af6a48 SG |
53 | struct udevice *bus = dev->parent; |
54 | struct dm_spi_ops *ops = spi_get_ops(bus); | |
e564f054 | 55 | struct dm_spi_bus *spi = dev_get_uclass_priv(bus); |
7a3eff4c | 56 | struct spi_slave *slave = dev_get_parent_priv(dev); |
741280e9 | 57 | uint speed, mode; |
d7af6a48 SG |
58 | |
59 | speed = slave->max_hz; | |
741280e9 OP |
60 | mode = slave->mode; |
61 | ||
d7af6a48 SG |
62 | if (spi->max_hz) { |
63 | if (speed) | |
741280e9 | 64 | speed = min(speed, spi->max_hz); |
d7af6a48 SG |
65 | else |
66 | speed = spi->max_hz; | |
67 | } | |
68 | if (!speed) | |
12bfb2e0 | 69 | speed = SPI_DEFAULT_SPEED_HZ; |
741280e9 OP |
70 | |
71 | if (speed != spi->speed || mode != spi->mode) { | |
24fc1ec2 MS |
72 | int ret = spi_set_speed_mode(bus, speed, slave->mode); |
73 | ||
60e2809a | 74 | if (ret) |
5e24a2ef | 75 | return log_ret(ret); |
741280e9 OP |
76 | |
77 | spi->speed = speed; | |
78 | spi->mode = mode; | |
60e2809a | 79 | } |
d7af6a48 | 80 | |
5e24a2ef | 81 | return log_ret(ops->claim_bus ? ops->claim_bus(dev) : 0); |
d7af6a48 SG |
82 | } |
83 | ||
7a3eff4c | 84 | void dm_spi_release_bus(struct udevice *dev) |
d7af6a48 | 85 | { |
d7af6a48 SG |
86 | struct udevice *bus = dev->parent; |
87 | struct dm_spi_ops *ops = spi_get_ops(bus); | |
88 | ||
89 | if (ops->release_bus) | |
9694b724 | 90 | ops->release_bus(dev); |
d7af6a48 SG |
91 | } |
92 | ||
7a3eff4c PF |
93 | int dm_spi_xfer(struct udevice *dev, unsigned int bitlen, |
94 | const void *dout, void *din, unsigned long flags) | |
d7af6a48 | 95 | { |
d7af6a48 | 96 | struct udevice *bus = dev->parent; |
ccdabd89 | 97 | struct dm_spi_ops *ops = spi_get_ops(bus); |
d7af6a48 SG |
98 | |
99 | if (bus->uclass->uc_drv->id != UCLASS_SPI) | |
100 | return -EOPNOTSUPP; | |
ccdabd89 SG |
101 | if (!ops->xfer) |
102 | return -ENOSYS; | |
d7af6a48 | 103 | |
ccdabd89 | 104 | return ops->xfer(dev, bitlen, dout, din, flags); |
7a3eff4c PF |
105 | } |
106 | ||
c53b318e SG |
107 | int dm_spi_get_mmap(struct udevice *dev, ulong *map_basep, uint *map_sizep, |
108 | uint *offsetp) | |
109 | { | |
110 | struct udevice *bus = dev->parent; | |
111 | struct dm_spi_ops *ops = spi_get_ops(bus); | |
112 | ||
113 | if (bus->uclass->uc_drv->id != UCLASS_SPI) | |
114 | return -EOPNOTSUPP; | |
115 | if (!ops->get_mmap) | |
116 | return -ENOSYS; | |
117 | ||
118 | return ops->get_mmap(dev, map_basep, map_sizep, offsetp); | |
119 | } | |
120 | ||
7a3eff4c PF |
121 | int spi_claim_bus(struct spi_slave *slave) |
122 | { | |
5e24a2ef | 123 | return log_ret(dm_spi_claim_bus(slave->dev)); |
7a3eff4c PF |
124 | } |
125 | ||
126 | void spi_release_bus(struct spi_slave *slave) | |
127 | { | |
128 | dm_spi_release_bus(slave->dev); | |
129 | } | |
130 | ||
131 | int spi_xfer(struct spi_slave *slave, unsigned int bitlen, | |
132 | const void *dout, void *din, unsigned long flags) | |
133 | { | |
134 | return dm_spi_xfer(slave->dev, bitlen, dout, din, flags); | |
d7af6a48 SG |
135 | } |
136 | ||
8473b321 JT |
137 | int spi_write_then_read(struct spi_slave *slave, const u8 *opcode, |
138 | size_t n_opcode, const u8 *txbuf, u8 *rxbuf, | |
139 | size_t n_buf) | |
140 | { | |
141 | unsigned long flags = SPI_XFER_BEGIN; | |
142 | int ret; | |
143 | ||
144 | if (n_buf == 0) | |
145 | flags |= SPI_XFER_END; | |
146 | ||
147 | ret = spi_xfer(slave, n_opcode * 8, opcode, NULL, flags); | |
148 | if (ret) { | |
46c5391b PD |
149 | dev_dbg(slave->dev, |
150 | "spi: failed to send command (%zu bytes): %d\n", | |
151 | n_opcode, ret); | |
8473b321 JT |
152 | } else if (n_buf != 0) { |
153 | ret = spi_xfer(slave, n_buf * 8, txbuf, rxbuf, SPI_XFER_END); | |
154 | if (ret) | |
46c5391b PD |
155 | dev_dbg(slave->dev, |
156 | "spi: failed to transfer %zu bytes of data: %d\n", | |
157 | n_buf, ret); | |
8473b321 JT |
158 | } |
159 | ||
160 | return ret; | |
161 | } | |
162 | ||
71634f28 | 163 | #if !CONFIG_IS_ENABLED(OF_PLATDATA) |
6f849c30 | 164 | static int spi_child_post_bind(struct udevice *dev) |
d7af6a48 | 165 | { |
8a8d24bd | 166 | struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev); |
d7af6a48 | 167 | |
7d14ee44 | 168 | if (!dev_has_ofnode(dev)) |
d0cff03e SG |
169 | return 0; |
170 | ||
d1998a9f | 171 | return spi_slave_of_to_plat(dev, plat); |
d0cff03e | 172 | } |
71634f28 | 173 | #endif |
d0cff03e | 174 | |
6f849c30 | 175 | static int spi_post_probe(struct udevice *bus) |
d0cff03e | 176 | { |
71634f28 | 177 | #if !CONFIG_IS_ENABLED(OF_PLATDATA) |
e564f054 | 178 | struct dm_spi_bus *spi = dev_get_uclass_priv(bus); |
d0cff03e | 179 | |
279e26f5 | 180 | spi->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 0); |
71634f28 | 181 | #endif |
281f1566 MS |
182 | #if defined(CONFIG_NEEDS_MANUAL_RELOC) |
183 | struct dm_spi_ops *ops = spi_get_ops(bus); | |
4d9b1afa ARS |
184 | static int reloc_done; |
185 | ||
186 | if (!reloc_done) { | |
187 | if (ops->claim_bus) | |
188 | ops->claim_bus += gd->reloc_off; | |
189 | if (ops->release_bus) | |
190 | ops->release_bus += gd->reloc_off; | |
191 | if (ops->set_wordlen) | |
192 | ops->set_wordlen += gd->reloc_off; | |
193 | if (ops->xfer) | |
194 | ops->xfer += gd->reloc_off; | |
195 | if (ops->set_speed) | |
196 | ops->set_speed += gd->reloc_off; | |
197 | if (ops->set_mode) | |
198 | ops->set_mode += gd->reloc_off; | |
199 | if (ops->cs_info) | |
200 | ops->cs_info += gd->reloc_off; | |
201 | reloc_done++; | |
202 | } | |
281f1566 MS |
203 | #endif |
204 | ||
d7af6a48 SG |
205 | return 0; |
206 | } | |
207 | ||
6f849c30 | 208 | static int spi_child_pre_probe(struct udevice *dev) |
440714ee | 209 | { |
8a8d24bd | 210 | struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev); |
bcbe3d15 | 211 | struct spi_slave *slave = dev_get_parent_priv(dev); |
440714ee | 212 | |
d0cff03e SG |
213 | /* |
214 | * This is needed because we pass struct spi_slave around the place | |
215 | * instead slave->dev (a struct udevice). So we have to have some | |
216 | * way to access the slave udevice given struct spi_slave. Once we | |
217 | * change the SPI API to use udevice instead of spi_slave, we can | |
218 | * drop this. | |
219 | */ | |
440714ee SG |
220 | slave->dev = dev; |
221 | ||
d0cff03e SG |
222 | slave->max_hz = plat->max_hz; |
223 | slave->mode = plat->mode; | |
674f3609 | 224 | slave->wordlen = SPI_DEFAULT_WORDLEN; |
d0cff03e | 225 | |
440714ee SG |
226 | return 0; |
227 | } | |
228 | ||
d7af6a48 SG |
229 | int spi_chip_select(struct udevice *dev) |
230 | { | |
8a8d24bd | 231 | struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev); |
d7af6a48 | 232 | |
d0cff03e | 233 | return plat ? plat->cs : -ENOENT; |
d7af6a48 SG |
234 | } |
235 | ||
ff56bba2 | 236 | int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp) |
d7af6a48 | 237 | { |
7bacce52 BM |
238 | struct dm_spi_ops *ops; |
239 | struct spi_cs_info info; | |
d7af6a48 | 240 | struct udevice *dev; |
7bacce52 BM |
241 | int ret; |
242 | ||
243 | /* | |
244 | * Ask the driver. For the moment we don't have CS info. | |
245 | * When we do we could provide the driver with a helper function | |
246 | * to figure out what chip selects are valid, or just handle the | |
247 | * request. | |
248 | */ | |
249 | ops = spi_get_ops(bus); | |
250 | if (ops->cs_info) { | |
251 | ret = ops->cs_info(bus, cs, &info); | |
252 | } else { | |
253 | /* | |
254 | * We could assume there is at least one valid chip select. | |
255 | * The driver didn't care enough to tell us. | |
256 | */ | |
257 | ret = 0; | |
258 | } | |
259 | ||
260 | if (ret) { | |
46c5391b | 261 | dev_err(bus, "Invalid cs %d (err=%d)\n", cs, ret); |
7bacce52 BM |
262 | return ret; |
263 | } | |
d7af6a48 SG |
264 | |
265 | for (device_find_first_child(bus, &dev); dev; | |
266 | device_find_next_child(&dev)) { | |
8a8d24bd | 267 | struct dm_spi_slave_plat *plat; |
d7af6a48 | 268 | |
caa4daa2 | 269 | plat = dev_get_parent_plat(dev); |
46c5391b | 270 | dev_dbg(bus, "%s: plat=%p, cs=%d\n", __func__, plat, plat->cs); |
d0cff03e | 271 | if (plat->cs == cs) { |
d7af6a48 SG |
272 | *devp = dev; |
273 | return 0; | |
274 | } | |
275 | } | |
276 | ||
277 | return -ENODEV; | |
278 | } | |
279 | ||
280 | int spi_cs_is_valid(unsigned int busnum, unsigned int cs) | |
281 | { | |
282 | struct spi_cs_info info; | |
283 | struct udevice *bus; | |
284 | int ret; | |
285 | ||
99175919 | 286 | ret = uclass_find_device_by_seq(UCLASS_SPI, busnum, &bus); |
d7af6a48 | 287 | if (ret) { |
46c5391b | 288 | log_debug("%s: No bus %d\n", __func__, busnum); |
d7af6a48 SG |
289 | return ret; |
290 | } | |
291 | ||
292 | return spi_cs_info(bus, cs, &info); | |
293 | } | |
294 | ||
295 | int spi_cs_info(struct udevice *bus, uint cs, struct spi_cs_info *info) | |
296 | { | |
297 | struct spi_cs_info local_info; | |
d7af6a48 SG |
298 | int ret; |
299 | ||
300 | if (!info) | |
301 | info = &local_info; | |
302 | ||
303 | /* If there is a device attached, return it */ | |
304 | info->dev = NULL; | |
305 | ret = spi_find_chip_select(bus, cs, &info->dev); | |
7bacce52 | 306 | return ret == -ENODEV ? 0 : ret; |
d7af6a48 SG |
307 | } |
308 | ||
d7af6a48 SG |
309 | int spi_find_bus_and_cs(int busnum, int cs, struct udevice **busp, |
310 | struct udevice **devp) | |
311 | { | |
312 | struct udevice *bus, *dev; | |
313 | int ret; | |
314 | ||
99175919 | 315 | ret = uclass_find_device_by_seq(UCLASS_SPI, busnum, &bus); |
d7af6a48 | 316 | if (ret) { |
46c5391b | 317 | log_debug("%s: No bus %d\n", __func__, busnum); |
d7af6a48 SG |
318 | return ret; |
319 | } | |
320 | ret = spi_find_chip_select(bus, cs, &dev); | |
321 | if (ret) { | |
46c5391b | 322 | dev_dbg(bus, "%s: No cs %d\n", __func__, cs); |
d7af6a48 SG |
323 | return ret; |
324 | } | |
325 | *busp = bus; | |
326 | *devp = dev; | |
327 | ||
328 | return ret; | |
329 | } | |
330 | ||
331 | int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode, | |
332 | const char *drv_name, const char *dev_name, | |
333 | struct udevice **busp, struct spi_slave **devp) | |
334 | { | |
335 | struct udevice *bus, *dev; | |
8a8d24bd | 336 | struct dm_spi_slave_plat *plat; |
741280e9 | 337 | struct dm_spi_bus *bus_data; |
f7dd5370 | 338 | struct spi_slave *slave; |
d7af6a48 SG |
339 | bool created = false; |
340 | int ret; | |
341 | ||
640abba5 | 342 | #if CONFIG_IS_ENABLED(OF_PLATDATA) |
71634f28 SG |
343 | ret = uclass_first_device_err(UCLASS_SPI, &bus); |
344 | #else | |
d7af6a48 | 345 | ret = uclass_get_device_by_seq(UCLASS_SPI, busnum, &bus); |
71634f28 | 346 | #endif |
d7af6a48 | 347 | if (ret) { |
46c5391b | 348 | log_err("Invalid bus %d (err=%d)\n", busnum, ret); |
d7af6a48 SG |
349 | return ret; |
350 | } | |
351 | ret = spi_find_chip_select(bus, cs, &dev); | |
352 | ||
353 | /* | |
354 | * If there is no such device, create one automatically. This means | |
355 | * that we don't need a device tree node or platform data for the | |
356 | * SPI flash chip - we will bind to the correct driver. | |
357 | */ | |
358 | if (ret == -ENODEV && drv_name) { | |
46c5391b PD |
359 | dev_dbg(bus, "%s: Binding new device '%s', busnum=%d, cs=%d, driver=%s\n", |
360 | __func__, dev_name, busnum, cs, drv_name); | |
6b18656a | 361 | ret = device_bind_driver(bus, drv_name, dev_name, &dev); |
28f98858 | 362 | if (ret) { |
46c5391b PD |
363 | dev_dbg(bus, "%s: Unable to bind driver (ret=%d)\n", |
364 | __func__, ret); | |
d7af6a48 | 365 | return ret; |
28f98858 | 366 | } |
caa4daa2 | 367 | plat = dev_get_parent_plat(dev); |
d0cff03e | 368 | plat->cs = cs; |
12bfb2e0 SG |
369 | if (speed) { |
370 | plat->max_hz = speed; | |
371 | } else { | |
46c5391b PD |
372 | dev_warn(bus, |
373 | "Warning: SPI speed fallback to %u kHz\n", | |
374 | SPI_DEFAULT_SPEED_HZ / 1000); | |
12bfb2e0 SG |
375 | plat->max_hz = SPI_DEFAULT_SPEED_HZ; |
376 | } | |
d0cff03e | 377 | plat->mode = mode; |
d7af6a48 SG |
378 | created = true; |
379 | } else if (ret) { | |
46c5391b | 380 | dev_err(bus, "Invalid chip select %d:%d (err=%d)\n", busnum, cs, ret); |
d7af6a48 SG |
381 | return ret; |
382 | } | |
383 | ||
384 | if (!device_active(dev)) { | |
d0cff03e | 385 | struct spi_slave *slave; |
d7af6a48 | 386 | |
d0cff03e | 387 | ret = device_probe(dev); |
d7af6a48 SG |
388 | if (ret) |
389 | goto err; | |
bcbe3d15 | 390 | slave = dev_get_parent_priv(dev); |
d7af6a48 | 391 | slave->dev = dev; |
d7af6a48 SG |
392 | } |
393 | ||
f7dd5370 | 394 | slave = dev_get_parent_priv(dev); |
741280e9 | 395 | bus_data = dev_get_uclass_priv(bus); |
b0cc1b84 | 396 | |
f7dd5370 MW |
397 | /* |
398 | * In case the operation speed is not yet established by | |
399 | * dm_spi_claim_bus() ensure the bus is configured properly. | |
400 | */ | |
741280e9 | 401 | if (!bus_data->speed) { |
f7dd5370 MW |
402 | ret = spi_claim_bus(slave); |
403 | if (ret) | |
404 | goto err; | |
96907c0f | 405 | } |
d7af6a48 SG |
406 | |
407 | *busp = bus; | |
f7dd5370 | 408 | *devp = slave; |
46c5391b | 409 | log_debug("%s: bus=%p, slave=%p\n", __func__, bus, *devp); |
d7af6a48 SG |
410 | |
411 | return 0; | |
412 | ||
413 | err: | |
46c5391b PD |
414 | log_debug("%s: Error path, created=%d, device '%s'\n", __func__, |
415 | created, dev->name); | |
d7af6a48 | 416 | if (created) { |
706865af | 417 | device_remove(dev, DM_REMOVE_NORMAL); |
d7af6a48 SG |
418 | device_unbind(dev); |
419 | } | |
420 | ||
421 | return ret; | |
422 | } | |
423 | ||
d7af6a48 SG |
424 | /* Compatibility function - to be removed */ |
425 | struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs, | |
426 | unsigned int speed, unsigned int mode) | |
427 | { | |
428 | struct spi_slave *slave; | |
429 | struct udevice *dev; | |
430 | int ret; | |
431 | ||
432 | ret = spi_get_bus_and_cs(busnum, cs, speed, mode, NULL, 0, &dev, | |
279e26f5 | 433 | &slave); |
d7af6a48 SG |
434 | if (ret) |
435 | return NULL; | |
436 | ||
437 | return slave; | |
438 | } | |
439 | ||
440 | void spi_free_slave(struct spi_slave *slave) | |
441 | { | |
706865af | 442 | device_remove(slave->dev, DM_REMOVE_NORMAL); |
d7af6a48 SG |
443 | } |
444 | ||
8a8d24bd | 445 | int spi_slave_of_to_plat(struct udevice *dev, struct dm_spi_slave_plat *plat) |
d7af6a48 | 446 | { |
08fe9c29 | 447 | int mode = 0; |
f8e2f92d | 448 | int value; |
d7af6a48 | 449 | |
279e26f5 | 450 | plat->cs = dev_read_u32_default(dev, "reg", -1); |
12bfb2e0 SG |
451 | plat->max_hz = dev_read_u32_default(dev, "spi-max-frequency", |
452 | SPI_DEFAULT_SPEED_HZ); | |
279e26f5 | 453 | if (dev_read_bool(dev, "spi-cpol")) |
d7af6a48 | 454 | mode |= SPI_CPOL; |
279e26f5 | 455 | if (dev_read_bool(dev, "spi-cpha")) |
d7af6a48 | 456 | mode |= SPI_CPHA; |
279e26f5 | 457 | if (dev_read_bool(dev, "spi-cs-high")) |
d7af6a48 | 458 | mode |= SPI_CS_HIGH; |
279e26f5 | 459 | if (dev_read_bool(dev, "spi-3wire")) |
379b49d8 | 460 | mode |= SPI_3WIRE; |
279e26f5 | 461 | if (dev_read_bool(dev, "spi-half-duplex")) |
d7af6a48 | 462 | mode |= SPI_PREAMBLE; |
f8e2f92d M |
463 | |
464 | /* Device DUAL/QUAD mode */ | |
279e26f5 | 465 | value = dev_read_u32_default(dev, "spi-tx-bus-width", 1); |
f8e2f92d M |
466 | switch (value) { |
467 | case 1: | |
468 | break; | |
469 | case 2: | |
470 | mode |= SPI_TX_DUAL; | |
471 | break; | |
472 | case 4: | |
473 | mode |= SPI_TX_QUAD; | |
474 | break; | |
658df8bd VR |
475 | case 8: |
476 | mode |= SPI_TX_OCTAL; | |
477 | break; | |
f8e2f92d | 478 | default: |
1b7c28f5 | 479 | warn_non_spl("spi-tx-bus-width %d not supported\n", value); |
f8e2f92d M |
480 | break; |
481 | } | |
482 | ||
279e26f5 | 483 | value = dev_read_u32_default(dev, "spi-rx-bus-width", 1); |
f8e2f92d M |
484 | switch (value) { |
485 | case 1: | |
486 | break; | |
487 | case 2: | |
08fe9c29 | 488 | mode |= SPI_RX_DUAL; |
f8e2f92d M |
489 | break; |
490 | case 4: | |
08fe9c29 | 491 | mode |= SPI_RX_QUAD; |
f8e2f92d | 492 | break; |
658df8bd VR |
493 | case 8: |
494 | mode |= SPI_RX_OCTAL; | |
495 | break; | |
f8e2f92d | 496 | default: |
1b7c28f5 | 497 | warn_non_spl("spi-rx-bus-width %d not supported\n", value); |
f8e2f92d M |
498 | break; |
499 | } | |
500 | ||
08fe9c29 | 501 | plat->mode = mode; |
f8e2f92d | 502 | |
d7af6a48 SG |
503 | return 0; |
504 | } | |
505 | ||
506 | UCLASS_DRIVER(spi) = { | |
507 | .id = UCLASS_SPI, | |
508 | .name = "spi", | |
9cc36a2b | 509 | .flags = DM_UC_FLAG_SEQ_ALIAS, |
280af011 | 510 | #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) |
91195485 | 511 | .post_bind = dm_scan_fdt_dev, |
71634f28 | 512 | #endif |
d7af6a48 | 513 | .post_probe = spi_post_probe, |
440714ee | 514 | .child_pre_probe = spi_child_pre_probe, |
41575d8e SG |
515 | .per_device_auto = sizeof(struct dm_spi_bus), |
516 | .per_child_auto = sizeof(struct spi_slave), | |
8a8d24bd | 517 | .per_child_plat_auto = sizeof(struct dm_spi_slave_plat), |
71634f28 | 518 | #if !CONFIG_IS_ENABLED(OF_PLATDATA) |
d0cff03e | 519 | .child_post_bind = spi_child_post_bind, |
71634f28 | 520 | #endif |
d7af6a48 SG |
521 | }; |
522 | ||
523 | UCLASS_DRIVER(spi_generic) = { | |
524 | .id = UCLASS_SPI_GENERIC, | |
525 | .name = "spi_generic", | |
526 | }; | |
527 | ||
528 | U_BOOT_DRIVER(spi_generic_drv) = { | |
529 | .name = "spi_generic_drv", | |
530 | .id = UCLASS_SPI_GENERIC, | |
531 | }; |