/src/u-boot/drivers/spi/sandbox_spi.c
Line | Count | Source |
1 | | /* |
2 | | * Simulate a SPI port |
3 | | * |
4 | | * Copyright (c) 2011-2013 The Chromium OS Authors. |
5 | | * See file CREDITS for list of people who contributed to this |
6 | | * project. |
7 | | * |
8 | | * Licensed under the GPL-2 or later. |
9 | | */ |
10 | | |
11 | | #define LOG_CATEGORY UCLASS_SPI |
12 | | |
13 | | #include <dm.h> |
14 | | #include <log.h> |
15 | | #include <malloc.h> |
16 | | #include <spi.h> |
17 | | #include <spi_flash.h> |
18 | | #include <os.h> |
19 | | |
20 | | #include <linux/errno.h> |
21 | | #include <asm/spi.h> |
22 | | #include <asm/state.h> |
23 | | #include <dm/acpi.h> |
24 | | #include <dm/device-internal.h> |
25 | | |
26 | | /** |
27 | | * struct sandbox_spi_priv - Sandbox SPI private data |
28 | | * |
29 | | * Helper struct to keep track of the sandbox SPI bus internal state. It is |
30 | | * used in unit tests to verify that dm spi functions update the bus |
31 | | * speed/mode properly (for instance, when jumping back and forth between spi |
32 | | * slaves claiming the bus, we need to make sure that the bus speed is updated |
33 | | * accordingly for each slave). |
34 | | * |
35 | | * @speed: Current bus speed. |
36 | | * @mode: Current bus mode. |
37 | | */ |
38 | | struct sandbox_spi_priv { |
39 | | uint speed; |
40 | | uint mode; |
41 | | }; |
42 | | |
43 | | __weak int sandbox_spi_get_emul(struct sandbox_state *state, |
44 | | struct udevice *bus, struct udevice *slave, |
45 | | struct udevice **emulp) |
46 | | { |
47 | | return -ENOENT; |
48 | | } |
49 | | |
50 | | uint sandbox_spi_get_speed(struct udevice *dev) |
51 | 0 | { |
52 | 0 | struct sandbox_spi_priv *priv = dev_get_priv(dev); |
53 | |
|
54 | 0 | return priv->speed; |
55 | 0 | } |
56 | | |
57 | | uint sandbox_spi_get_mode(struct udevice *dev) |
58 | 0 | { |
59 | 0 | struct sandbox_spi_priv *priv = dev_get_priv(dev); |
60 | |
|
61 | 0 | return priv->mode; |
62 | 0 | } |
63 | | |
64 | | static int sandbox_spi_xfer(struct udevice *slave, unsigned int bitlen, |
65 | | const void *dout, void *din, unsigned long flags) |
66 | 0 | { |
67 | 0 | struct udevice *bus = slave->parent; |
68 | 0 | struct sandbox_state *state = state_get_current(); |
69 | 0 | struct dm_spi_emul_ops *ops; |
70 | 0 | struct udevice *emul; |
71 | 0 | uint bytes = bitlen / 8, i; |
72 | 0 | int ret; |
73 | 0 | uint busnum, cs; |
74 | |
|
75 | 0 | if (bitlen == 0) |
76 | 0 | return 0; |
77 | | |
78 | | /* we can only do 8 bit transfers */ |
79 | 0 | if (bitlen % 8) { |
80 | 0 | printf("sandbox_spi: xfer: invalid bitlen size %u; needs to be 8bit\n", |
81 | 0 | bitlen); |
82 | 0 | return -EINVAL; |
83 | 0 | } |
84 | | |
85 | 0 | busnum = dev_seq(bus); |
86 | 0 | cs = spi_chip_select(slave); |
87 | 0 | if (busnum >= CONFIG_SANDBOX_SPI_MAX_BUS || |
88 | 0 | cs >= CONFIG_SANDBOX_SPI_MAX_CS) { |
89 | 0 | printf("%s: busnum=%u, cs=%u: out of range\n", __func__, |
90 | 0 | busnum, cs); |
91 | 0 | return -ENOENT; |
92 | 0 | } |
93 | 0 | ret = sandbox_spi_get_emul(state, bus, slave, &emul); |
94 | 0 | if (ret) { |
95 | 0 | printf("%s: busnum=%u, cs=%u: no emulation available (err=%d)\n", |
96 | 0 | __func__, busnum, cs, ret); |
97 | 0 | return -ENOENT; |
98 | 0 | } |
99 | 0 | ret = device_probe(emul); |
100 | 0 | if (ret) |
101 | 0 | return ret; |
102 | | |
103 | 0 | ops = spi_emul_get_ops(emul); |
104 | 0 | ret = ops->xfer(emul, bitlen, dout, din, flags); |
105 | |
|
106 | 0 | log_content("sandbox_spi: xfer: got back %i (that's %s)\n rx:", |
107 | 0 | ret, ret ? "bad" : "good"); |
108 | 0 | if (din) { |
109 | 0 | for (i = 0; i < bytes; ++i) |
110 | 0 | log_content(" %u:%02x", i, ((u8 *)din)[i]); |
111 | 0 | } |
112 | 0 | log_content("\n"); |
113 | |
|
114 | 0 | return ret; |
115 | 0 | } |
116 | | |
117 | | static int sandbox_spi_set_speed(struct udevice *bus, uint speed) |
118 | 0 | { |
119 | 0 | struct sandbox_spi_priv *priv = dev_get_priv(bus); |
120 | |
|
121 | 0 | priv->speed = speed; |
122 | |
|
123 | 0 | return 0; |
124 | 0 | } |
125 | | |
126 | | static int sandbox_spi_set_mode(struct udevice *bus, uint mode) |
127 | 0 | { |
128 | 0 | struct sandbox_spi_priv *priv = dev_get_priv(bus); |
129 | |
|
130 | 0 | priv->mode = mode; |
131 | |
|
132 | 0 | return 0; |
133 | 0 | } |
134 | | |
135 | | static int sandbox_cs_info(struct udevice *bus, uint cs, |
136 | | struct spi_cs_info *info) |
137 | 0 | { |
138 | | /* Always allow activity on CS 0, CS 1 */ |
139 | 0 | if (cs >= 2) |
140 | 0 | return -EINVAL; |
141 | | |
142 | 0 | return 0; |
143 | 0 | } |
144 | | |
145 | | static int sandbox_spi_get_mmap(struct udevice *dev, ulong *map_basep, |
146 | | uint *map_sizep, uint *offsetp) |
147 | 0 | { |
148 | 0 | *map_basep = 0x1000; |
149 | 0 | *map_sizep = 0x2000; |
150 | 0 | *offsetp = 0x100; |
151 | |
|
152 | 0 | return 0; |
153 | 0 | } |
154 | | |
155 | | static const struct dm_spi_ops sandbox_spi_ops = { |
156 | | .xfer = sandbox_spi_xfer, |
157 | | .set_speed = sandbox_spi_set_speed, |
158 | | .set_mode = sandbox_spi_set_mode, |
159 | | .cs_info = sandbox_cs_info, |
160 | | .get_mmap = sandbox_spi_get_mmap, |
161 | | }; |
162 | | |
163 | | static const struct udevice_id sandbox_spi_ids[] = { |
164 | | { .compatible = "sandbox,spi" }, |
165 | | { } |
166 | | }; |
167 | | |
168 | | U_BOOT_DRIVER(sandbox_spi) = { |
169 | | .name = "sandbox_spi", |
170 | | .id = UCLASS_SPI, |
171 | | .of_match = sandbox_spi_ids, |
172 | | .ops = &sandbox_spi_ops, |
173 | | .priv_auto = sizeof(struct sandbox_spi_priv), |
174 | | }; |