]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
623b6386 SG |
2 | /* |
3 | * Copyright (c) 2014 Google, Inc | |
4 | * | |
5 | * (C) Copyright 2002 | |
6 | * Gerald Van Baren, Custom IDEAS, [email protected]. | |
7 | * | |
8 | * Influenced by code from: | |
9 | * Wolfgang Denk, DENX Software Engineering, [email protected]. | |
623b6386 SG |
10 | */ |
11 | ||
12 | #include <common.h> | |
13 | #include <dm.h> | |
14 | #include <errno.h> | |
15 | #include <fdtdec.h> | |
f7ae49fc | 16 | #include <log.h> |
623b6386 SG |
17 | #include <malloc.h> |
18 | #include <spi.h> | |
19 | #include <asm/gpio.h> | |
cd93d625 | 20 | #include <linux/bitops.h> |
c05ed00a | 21 | #include <linux/delay.h> |
623b6386 SG |
22 | |
23 | DECLARE_GLOBAL_DATA_PTR; | |
24 | ||
25 | struct soft_spi_platdata { | |
050fb909 SG |
26 | struct gpio_desc cs; |
27 | struct gpio_desc sclk; | |
28 | struct gpio_desc mosi; | |
29 | struct gpio_desc miso; | |
623b6386 | 30 | int spi_delay_us; |
102412c4 | 31 | int flags; |
623b6386 SG |
32 | }; |
33 | ||
102412c4 PF |
34 | #define SPI_MASTER_NO_RX BIT(0) |
35 | #define SPI_MASTER_NO_TX BIT(1) | |
36 | ||
623b6386 SG |
37 | struct soft_spi_priv { |
38 | unsigned int mode; | |
39 | }; | |
40 | ||
41 | static int soft_spi_scl(struct udevice *dev, int bit) | |
42 | { | |
b6d54d52 PF |
43 | struct udevice *bus = dev_get_parent(dev); |
44 | struct soft_spi_platdata *plat = dev_get_platdata(bus); | |
623b6386 | 45 | |
050fb909 | 46 | dm_gpio_set_value(&plat->sclk, bit); |
623b6386 SG |
47 | |
48 | return 0; | |
49 | } | |
50 | ||
51 | static int soft_spi_sda(struct udevice *dev, int bit) | |
52 | { | |
b6d54d52 PF |
53 | struct udevice *bus = dev_get_parent(dev); |
54 | struct soft_spi_platdata *plat = dev_get_platdata(bus); | |
623b6386 | 55 | |
050fb909 | 56 | dm_gpio_set_value(&plat->mosi, bit); |
623b6386 SG |
57 | |
58 | return 0; | |
59 | } | |
60 | ||
61 | static int soft_spi_cs_activate(struct udevice *dev) | |
62 | { | |
b6d54d52 | 63 | struct udevice *bus = dev_get_parent(dev); |
0e146993 | 64 | struct soft_spi_priv *priv = dev_get_priv(bus); |
b6d54d52 | 65 | struct soft_spi_platdata *plat = dev_get_platdata(bus); |
0e146993 | 66 | int cidle = !!(priv->mode & SPI_CPOL); |
623b6386 | 67 | |
050fb909 | 68 | dm_gpio_set_value(&plat->cs, 0); |
0e146993 | 69 | dm_gpio_set_value(&plat->sclk, cidle); /* to idle */ |
050fb909 | 70 | dm_gpio_set_value(&plat->cs, 1); |
623b6386 SG |
71 | |
72 | return 0; | |
73 | } | |
74 | ||
75 | static int soft_spi_cs_deactivate(struct udevice *dev) | |
76 | { | |
b6d54d52 PF |
77 | struct udevice *bus = dev_get_parent(dev); |
78 | struct soft_spi_platdata *plat = dev_get_platdata(bus); | |
623b6386 | 79 | |
050fb909 | 80 | dm_gpio_set_value(&plat->cs, 0); |
623b6386 SG |
81 | |
82 | return 0; | |
83 | } | |
84 | ||
85 | static int soft_spi_claim_bus(struct udevice *dev) | |
86 | { | |
0e146993 JH |
87 | struct udevice *bus = dev_get_parent(dev); |
88 | struct soft_spi_priv *priv = dev_get_priv(bus); | |
89 | int cidle = !!(priv->mode & SPI_CPOL); | |
623b6386 SG |
90 | /* |
91 | * Make sure the SPI clock is in idle state as defined for | |
92 | * this slave. | |
93 | */ | |
0e146993 | 94 | return soft_spi_scl(dev, cidle); |
623b6386 SG |
95 | } |
96 | ||
97 | static int soft_spi_release_bus(struct udevice *dev) | |
98 | { | |
99 | /* Nothing to do */ | |
100 | return 0; | |
101 | } | |
102 | ||
103 | /*----------------------------------------------------------------------- | |
104 | * SPI transfer | |
105 | * | |
106 | * This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks | |
107 | * "bitlen" bits in the SPI MISO port. That's just the way SPI works. | |
108 | * | |
109 | * The source of the outgoing bits is the "dout" parameter and the | |
110 | * destination of the input bits is the "din" parameter. Note that "dout" | |
111 | * and "din" can point to the same memory location, in which case the | |
112 | * input data overwrites the output data (since both are buffered by | |
113 | * temporary variables, this is OK). | |
114 | */ | |
115 | static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen, | |
116 | const void *dout, void *din, unsigned long flags) | |
117 | { | |
b6d54d52 PF |
118 | struct udevice *bus = dev_get_parent(dev); |
119 | struct soft_spi_priv *priv = dev_get_priv(bus); | |
120 | struct soft_spi_platdata *plat = dev_get_platdata(bus); | |
623b6386 SG |
121 | uchar tmpdin = 0; |
122 | uchar tmpdout = 0; | |
123 | const u8 *txd = dout; | |
124 | u8 *rxd = din; | |
0e146993 JH |
125 | int cpha = !!(priv->mode & SPI_CPHA); |
126 | int cidle = !!(priv->mode & SPI_CPOL); | |
623b6386 SG |
127 | unsigned int j; |
128 | ||
129 | debug("spi_xfer: slave %s:%s dout %08X din %08X bitlen %u\n", | |
130 | dev->parent->name, dev->name, *(uint *)txd, *(uint *)rxd, | |
131 | bitlen); | |
132 | ||
133 | if (flags & SPI_XFER_BEGIN) | |
134 | soft_spi_cs_activate(dev); | |
135 | ||
136 | for (j = 0; j < bitlen; j++) { | |
137 | /* | |
138 | * Check if it is time to work on a new byte. | |
139 | */ | |
140 | if ((j % 8) == 0) { | |
141 | if (txd) | |
142 | tmpdout = *txd++; | |
143 | else | |
144 | tmpdout = 0; | |
145 | if (j != 0) { | |
146 | if (rxd) | |
147 | *rxd++ = tmpdin; | |
148 | } | |
149 | tmpdin = 0; | |
150 | } | |
151 | ||
0e146993 JH |
152 | /* |
153 | * CPOL 0: idle is low (0), active is high (1) | |
154 | * CPOL 1: idle is high (1), active is low (0) | |
155 | */ | |
156 | ||
157 | /* | |
158 | * drive bit | |
159 | * CPHA 1: CLK from idle to active | |
160 | */ | |
161 | if (cpha) | |
162 | soft_spi_scl(dev, !cidle); | |
102412c4 PF |
163 | if ((plat->flags & SPI_MASTER_NO_TX) == 0) |
164 | soft_spi_sda(dev, !!(tmpdout & 0x80)); | |
623b6386 | 165 | udelay(plat->spi_delay_us); |
0e146993 JH |
166 | |
167 | /* | |
168 | * sample bit | |
169 | * CPHA 0: CLK from idle to active | |
170 | * CPHA 1: CLK from active to idle | |
171 | */ | |
172 | if (!cpha) | |
173 | soft_spi_scl(dev, !cidle); | |
623b6386 | 174 | else |
0e146993 | 175 | soft_spi_scl(dev, cidle); |
623b6386 | 176 | tmpdin <<= 1; |
102412c4 PF |
177 | if ((plat->flags & SPI_MASTER_NO_RX) == 0) |
178 | tmpdin |= dm_gpio_get_value(&plat->miso); | |
623b6386 SG |
179 | tmpdout <<= 1; |
180 | udelay(plat->spi_delay_us); | |
0e146993 JH |
181 | |
182 | /* | |
183 | * drive bit | |
184 | * CPHA 0: CLK from active to idle | |
185 | */ | |
186 | if (!cpha) | |
187 | soft_spi_scl(dev, cidle); | |
623b6386 SG |
188 | } |
189 | /* | |
190 | * If the number of bits isn't a multiple of 8, shift the last | |
191 | * bits over to left-justify them. Then store the last byte | |
192 | * read in. | |
193 | */ | |
194 | if (rxd) { | |
195 | if ((bitlen % 8) != 0) | |
196 | tmpdin <<= 8 - (bitlen % 8); | |
197 | *rxd++ = tmpdin; | |
198 | } | |
199 | ||
200 | if (flags & SPI_XFER_END) | |
201 | soft_spi_cs_deactivate(dev); | |
202 | ||
203 | return 0; | |
204 | } | |
205 | ||
206 | static int soft_spi_set_speed(struct udevice *dev, unsigned int speed) | |
207 | { | |
0e146993 | 208 | /* Ignore any speed settings. Speed is implemented via "spi-delay-us" */ |
623b6386 SG |
209 | return 0; |
210 | } | |
211 | ||
212 | static int soft_spi_set_mode(struct udevice *dev, unsigned int mode) | |
213 | { | |
214 | struct soft_spi_priv *priv = dev_get_priv(dev); | |
215 | ||
216 | priv->mode = mode; | |
217 | ||
218 | return 0; | |
219 | } | |
220 | ||
623b6386 SG |
221 | static const struct dm_spi_ops soft_spi_ops = { |
222 | .claim_bus = soft_spi_claim_bus, | |
223 | .release_bus = soft_spi_release_bus, | |
224 | .xfer = soft_spi_xfer, | |
225 | .set_speed = soft_spi_set_speed, | |
226 | .set_mode = soft_spi_set_mode, | |
227 | }; | |
228 | ||
229 | static int soft_spi_ofdata_to_platdata(struct udevice *dev) | |
230 | { | |
231 | struct soft_spi_platdata *plat = dev->platdata; | |
232 | const void *blob = gd->fdt_blob; | |
e160f7d4 | 233 | int node = dev_of_offset(dev); |
623b6386 | 234 | |
623b6386 SG |
235 | plat->spi_delay_us = fdtdec_get_int(blob, node, "spi-delay-us", 0); |
236 | ||
237 | return 0; | |
238 | } | |
239 | ||
240 | static int soft_spi_probe(struct udevice *dev) | |
241 | { | |
bcbe3d15 | 242 | struct spi_slave *slave = dev_get_parent_priv(dev); |
623b6386 | 243 | struct soft_spi_platdata *plat = dev->platdata; |
050fb909 | 244 | int cs_flags, clk_flags; |
102412c4 | 245 | int ret; |
050fb909 | 246 | |
dfe72d08 CK |
247 | cs_flags = (slave && slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW; |
248 | clk_flags = (slave && slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0; | |
102412c4 PF |
249 | |
250 | if (gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs, | |
050fb909 | 251 | GPIOD_IS_OUT | cs_flags) || |
102412c4 PF |
252 | gpio_request_by_name(dev, "gpio-sck", 0, &plat->sclk, |
253 | GPIOD_IS_OUT | clk_flags)) | |
254 | return -EINVAL; | |
255 | ||
256 | ret = gpio_request_by_name(dev, "gpio-mosi", 0, &plat->mosi, | |
257 | GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE); | |
258 | if (ret) | |
259 | plat->flags |= SPI_MASTER_NO_TX; | |
260 | ||
261 | ret = gpio_request_by_name(dev, "gpio-miso", 0, &plat->miso, | |
262 | GPIOD_IS_IN); | |
263 | if (ret) | |
264 | plat->flags |= SPI_MASTER_NO_RX; | |
265 | ||
266 | if ((plat->flags & (SPI_MASTER_NO_RX | SPI_MASTER_NO_TX)) == | |
267 | (SPI_MASTER_NO_RX | SPI_MASTER_NO_TX)) | |
050fb909 | 268 | return -EINVAL; |
623b6386 SG |
269 | |
270 | return 0; | |
271 | } | |
272 | ||
273 | static const struct udevice_id soft_spi_ids[] = { | |
102412c4 | 274 | { .compatible = "spi-gpio" }, |
623b6386 SG |
275 | { } |
276 | }; | |
277 | ||
278 | U_BOOT_DRIVER(soft_spi) = { | |
279 | .name = "soft_spi", | |
280 | .id = UCLASS_SPI, | |
281 | .of_match = soft_spi_ids, | |
282 | .ops = &soft_spi_ops, | |
283 | .ofdata_to_platdata = soft_spi_ofdata_to_platdata, | |
41575d8e SG |
284 | .platdata_auto = sizeof(struct soft_spi_platdata), |
285 | .priv_auto = sizeof(struct soft_spi_priv), | |
623b6386 | 286 | .probe = soft_spi_probe, |
623b6386 | 287 | }; |