blob: 50bd7be5640bf8fabda3d260e62a86e790704d0d [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass456dd7c2014-10-13 23:42:00 -06002/*
3 * Copyright (c) 2014 Google, Inc
4 *
5 * (C) Copyright 2002
6 * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
7 *
8 * Influenced by code from:
9 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
Simon Glass456dd7c2014-10-13 23:42:00 -060010 */
11
Simon Glass456dd7c2014-10-13 23:42:00 -060012#include <dm.h>
13#include <errno.h>
14#include <fdtdec.h>
Simon Glass0f2af882020-05-10 11:40:05 -060015#include <log.h>
Simon Glass456dd7c2014-10-13 23:42:00 -060016#include <malloc.h>
17#include <spi.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060018#include <asm/global_data.h>
Simon Glass456dd7c2014-10-13 23:42:00 -060019#include <asm/gpio.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060020#include <linux/bitops.h>
Simon Glassdbd79542020-05-10 11:40:11 -060021#include <linux/delay.h>
Simon Glass456dd7c2014-10-13 23:42:00 -060022
23DECLARE_GLOBAL_DATA_PTR;
24
Simon Glassb75b15b2020-12-03 16:55:23 -070025struct soft_spi_plat {
Simon Glass61d10b72015-01-05 20:05:40 -070026 struct gpio_desc cs;
27 struct gpio_desc sclk;
28 struct gpio_desc mosi;
29 struct gpio_desc miso;
Simon Glass456dd7c2014-10-13 23:42:00 -060030 int spi_delay_us;
Peng Fan54108e72016-05-03 10:02:21 +080031 int flags;
Simon Glass456dd7c2014-10-13 23:42:00 -060032};
33
Peng Fan54108e72016-05-03 10:02:21 +080034#define SPI_MASTER_NO_RX BIT(0)
35#define SPI_MASTER_NO_TX BIT(1)
36
Simon Glass456dd7c2014-10-13 23:42:00 -060037struct soft_spi_priv {
38 unsigned int mode;
39};
40
41static int soft_spi_scl(struct udevice *dev, int bit)
42{
Peng Fanfa7dd9b2016-05-03 10:02:20 +080043 struct udevice *bus = dev_get_parent(dev);
Simon Glassb75b15b2020-12-03 16:55:23 -070044 struct soft_spi_plat *plat = dev_get_plat(bus);
Simon Glass456dd7c2014-10-13 23:42:00 -060045
Simon Glass61d10b72015-01-05 20:05:40 -070046 dm_gpio_set_value(&plat->sclk, bit);
Simon Glass456dd7c2014-10-13 23:42:00 -060047
48 return 0;
49}
50
51static int soft_spi_sda(struct udevice *dev, int bit)
52{
Peng Fanfa7dd9b2016-05-03 10:02:20 +080053 struct udevice *bus = dev_get_parent(dev);
Simon Glassb75b15b2020-12-03 16:55:23 -070054 struct soft_spi_plat *plat = dev_get_plat(bus);
Simon Glass456dd7c2014-10-13 23:42:00 -060055
Simon Glass61d10b72015-01-05 20:05:40 -070056 dm_gpio_set_value(&plat->mosi, bit);
Simon Glass456dd7c2014-10-13 23:42:00 -060057
58 return 0;
59}
60
61static int soft_spi_cs_activate(struct udevice *dev)
62{
Peng Fanfa7dd9b2016-05-03 10:02:20 +080063 struct udevice *bus = dev_get_parent(dev);
Johannes Holland84120622020-05-11 15:22:26 +020064 struct soft_spi_priv *priv = dev_get_priv(bus);
Simon Glassb75b15b2020-12-03 16:55:23 -070065 struct soft_spi_plat *plat = dev_get_plat(bus);
Johannes Holland84120622020-05-11 15:22:26 +020066 int cidle = !!(priv->mode & SPI_CPOL);
Simon Glass456dd7c2014-10-13 23:42:00 -060067
Simon Glass61d10b72015-01-05 20:05:40 -070068 dm_gpio_set_value(&plat->cs, 0);
Johannes Holland84120622020-05-11 15:22:26 +020069 dm_gpio_set_value(&plat->sclk, cidle); /* to idle */
Simon Glass61d10b72015-01-05 20:05:40 -070070 dm_gpio_set_value(&plat->cs, 1);
Simon Glass456dd7c2014-10-13 23:42:00 -060071
72 return 0;
73}
74
75static int soft_spi_cs_deactivate(struct udevice *dev)
76{
Peng Fanfa7dd9b2016-05-03 10:02:20 +080077 struct udevice *bus = dev_get_parent(dev);
Simon Glassb75b15b2020-12-03 16:55:23 -070078 struct soft_spi_plat *plat = dev_get_plat(bus);
Simon Glass456dd7c2014-10-13 23:42:00 -060079
Simon Glass61d10b72015-01-05 20:05:40 -070080 dm_gpio_set_value(&plat->cs, 0);
Simon Glass456dd7c2014-10-13 23:42:00 -060081
82 return 0;
83}
84
85static int soft_spi_claim_bus(struct udevice *dev)
86{
Johannes Holland84120622020-05-11 15:22:26 +020087 struct udevice *bus = dev_get_parent(dev);
88 struct soft_spi_priv *priv = dev_get_priv(bus);
89 int cidle = !!(priv->mode & SPI_CPOL);
Simon Glass456dd7c2014-10-13 23:42:00 -060090 /*
91 * Make sure the SPI clock is in idle state as defined for
92 * this slave.
93 */
Johannes Holland84120622020-05-11 15:22:26 +020094 return soft_spi_scl(dev, cidle);
Simon Glass456dd7c2014-10-13 23:42:00 -060095}
96
97static 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 */
115static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen,
116 const void *dout, void *din, unsigned long flags)
117{
Peng Fanfa7dd9b2016-05-03 10:02:20 +0800118 struct udevice *bus = dev_get_parent(dev);
119 struct soft_spi_priv *priv = dev_get_priv(bus);
Simon Glassb75b15b2020-12-03 16:55:23 -0700120 struct soft_spi_plat *plat = dev_get_plat(bus);
Simon Glass456dd7c2014-10-13 23:42:00 -0600121 uchar tmpdin = 0;
122 uchar tmpdout = 0;
123 const u8 *txd = dout;
124 u8 *rxd = din;
Johannes Holland84120622020-05-11 15:22:26 +0200125 int cpha = !!(priv->mode & SPI_CPHA);
126 int cidle = !!(priv->mode & SPI_CPOL);
Hironori KIKUCHIb8fe3912025-01-31 10:38:41 +0900127 int txrx = plat->flags;
Simon Glass456dd7c2014-10-13 23:42:00 -0600128 unsigned int j;
129
Hironori KIKUCHIb8fe3912025-01-31 10:38:41 +0900130 if (priv->mode & SPI_3WIRE) {
131 if (txd && rxd)
132 return -EINVAL;
133
134 txrx = txd ? SPI_MASTER_NO_RX : SPI_MASTER_NO_TX;
135 dm_gpio_set_dir_flags(&plat->mosi,
136 txd ? GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE :
137 GPIOD_IS_IN | GPIOD_PULL_UP);
138 }
139
Simon Glass456dd7c2014-10-13 23:42:00 -0600140 debug("spi_xfer: slave %s:%s dout %08X din %08X bitlen %u\n",
141 dev->parent->name, dev->name, *(uint *)txd, *(uint *)rxd,
142 bitlen);
143
144 if (flags & SPI_XFER_BEGIN)
145 soft_spi_cs_activate(dev);
146
147 for (j = 0; j < bitlen; j++) {
148 /*
149 * Check if it is time to work on a new byte.
150 */
151 if ((j % 8) == 0) {
152 if (txd)
153 tmpdout = *txd++;
154 else
155 tmpdout = 0;
156 if (j != 0) {
157 if (rxd)
158 *rxd++ = tmpdin;
159 }
160 tmpdin = 0;
161 }
162
Johannes Holland84120622020-05-11 15:22:26 +0200163 /*
164 * CPOL 0: idle is low (0), active is high (1)
165 * CPOL 1: idle is high (1), active is low (0)
166 */
167
168 /*
169 * drive bit
170 * CPHA 1: CLK from idle to active
171 */
172 if (cpha)
173 soft_spi_scl(dev, !cidle);
Hironori KIKUCHIb8fe3912025-01-31 10:38:41 +0900174 if ((txrx & SPI_MASTER_NO_TX) == 0)
Peng Fan54108e72016-05-03 10:02:21 +0800175 soft_spi_sda(dev, !!(tmpdout & 0x80));
Simon Glass456dd7c2014-10-13 23:42:00 -0600176 udelay(plat->spi_delay_us);
Johannes Holland84120622020-05-11 15:22:26 +0200177
178 /*
179 * sample bit
180 * CPHA 0: CLK from idle to active
181 * CPHA 1: CLK from active to idle
182 */
183 if (!cpha)
184 soft_spi_scl(dev, !cidle);
Simon Glass456dd7c2014-10-13 23:42:00 -0600185 else
Johannes Holland84120622020-05-11 15:22:26 +0200186 soft_spi_scl(dev, cidle);
Simon Glass456dd7c2014-10-13 23:42:00 -0600187 tmpdin <<= 1;
Hironori KIKUCHIb8fe3912025-01-31 10:38:41 +0900188 if ((txrx & SPI_MASTER_NO_RX) == 0)
189 tmpdin |= dm_gpio_get_value((priv->mode & SPI_3WIRE) ?
190 &plat->mosi :
191 &plat->miso);
Simon Glass456dd7c2014-10-13 23:42:00 -0600192 tmpdout <<= 1;
193 udelay(plat->spi_delay_us);
Johannes Holland84120622020-05-11 15:22:26 +0200194
195 /*
196 * drive bit
197 * CPHA 0: CLK from active to idle
198 */
199 if (!cpha)
200 soft_spi_scl(dev, cidle);
Simon Glass456dd7c2014-10-13 23:42:00 -0600201 }
202 /*
203 * If the number of bits isn't a multiple of 8, shift the last
204 * bits over to left-justify them. Then store the last byte
205 * read in.
206 */
207 if (rxd) {
208 if ((bitlen % 8) != 0)
209 tmpdin <<= 8 - (bitlen % 8);
210 *rxd++ = tmpdin;
211 }
212
213 if (flags & SPI_XFER_END)
214 soft_spi_cs_deactivate(dev);
215
216 return 0;
217}
218
219static int soft_spi_set_speed(struct udevice *dev, unsigned int speed)
220{
Johannes Holland84120622020-05-11 15:22:26 +0200221 /* Ignore any speed settings. Speed is implemented via "spi-delay-us" */
Simon Glass456dd7c2014-10-13 23:42:00 -0600222 return 0;
223}
224
225static int soft_spi_set_mode(struct udevice *dev, unsigned int mode)
226{
227 struct soft_spi_priv *priv = dev_get_priv(dev);
228
229 priv->mode = mode;
230
231 return 0;
232}
233
Simon Glass456dd7c2014-10-13 23:42:00 -0600234static const struct dm_spi_ops soft_spi_ops = {
235 .claim_bus = soft_spi_claim_bus,
236 .release_bus = soft_spi_release_bus,
237 .xfer = soft_spi_xfer,
238 .set_speed = soft_spi_set_speed,
239 .set_mode = soft_spi_set_mode,
240};
241
Simon Glassaad29ae2020-12-03 16:55:21 -0700242static int soft_spi_of_to_plat(struct udevice *dev)
Simon Glass456dd7c2014-10-13 23:42:00 -0600243{
Simon Glass95588622020-12-22 19:30:28 -0700244 struct soft_spi_plat *plat = dev_get_plat(dev);
Simon Glass456dd7c2014-10-13 23:42:00 -0600245 const void *blob = gd->fdt_blob;
Simon Glassdd79d6e2017-01-17 16:52:55 -0700246 int node = dev_of_offset(dev);
Simon Glass456dd7c2014-10-13 23:42:00 -0600247
Simon Glass456dd7c2014-10-13 23:42:00 -0600248 plat->spi_delay_us = fdtdec_get_int(blob, node, "spi-delay-us", 0);
249
250 return 0;
251}
252
Michael Polyntsov16cc79c2024-07-31 08:11:29 +0400253static int retrieve_num_chipselects(struct udevice *dev)
254{
255 int chipselects;
256 int ret;
257
258 ret = ofnode_read_u32(dev_ofnode(dev), "num-chipselects", &chipselects);
259 if (ret)
260 return ret;
261
262 return chipselects;
263}
264
Simon Glass456dd7c2014-10-13 23:42:00 -0600265static int soft_spi_probe(struct udevice *dev)
266{
Simon Glassde44acf2015-09-28 23:32:01 -0600267 struct spi_slave *slave = dev_get_parent_priv(dev);
Simon Glass95588622020-12-22 19:30:28 -0700268 struct soft_spi_plat *plat = dev_get_plat(dev);
Simon Glass61d10b72015-01-05 20:05:40 -0700269 int cs_flags, clk_flags;
Peng Fan54108e72016-05-03 10:02:21 +0800270 int ret;
Simon Glass456dd7c2014-10-13 23:42:00 -0600271
Christophe Kerellob34a10a2019-08-02 15:46:29 +0200272 cs_flags = (slave && slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW;
273 clk_flags = (slave && slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0;
Peng Fan54108e72016-05-03 10:02:21 +0800274
Fabio Estevam51a7d412023-05-18 19:22:40 -0300275 ret = gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs,
276 GPIOD_IS_OUT | cs_flags);
Michael Polyntsov16cc79c2024-07-31 08:11:29 +0400277 /*
278 * If num-chipselects is zero we're ignoring absence of cs-gpios. This
279 * code relies on the fact that `gpio_request_by_name` call above
280 * initiailizes plat->cs to correct value with invalid GPIO even when
281 * there is no cs-gpios node in dts. All other functions which work
282 * with plat->cs verify it via `dm_gpio_is_valid` before using it, so
283 * such value doesn't cause any problems.
284 */
285 if (ret && retrieve_num_chipselects(dev) != 0)
Peng Fan54108e72016-05-03 10:02:21 +0800286 return -EINVAL;
287
Fabio Estevam51a7d412023-05-18 19:22:40 -0300288 ret = gpio_request_by_name(dev, "gpio-sck", 0, &plat->sclk,
289 GPIOD_IS_OUT | clk_flags);
290 if (ret)
291 ret = gpio_request_by_name(dev, "sck-gpios", 0, &plat->sclk,
292 GPIOD_IS_OUT | clk_flags);
293 if (ret)
294 return -EINVAL;
295
Peng Fan54108e72016-05-03 10:02:21 +0800296 ret = gpio_request_by_name(dev, "gpio-mosi", 0, &plat->mosi,
297 GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
298 if (ret)
Fabio Estevam51a7d412023-05-18 19:22:40 -0300299 ret = gpio_request_by_name(dev, "mosi-gpios", 0, &plat->mosi,
300 GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
301 if (ret)
Peng Fan54108e72016-05-03 10:02:21 +0800302 plat->flags |= SPI_MASTER_NO_TX;
303
304 ret = gpio_request_by_name(dev, "gpio-miso", 0, &plat->miso,
305 GPIOD_IS_IN);
306 if (ret)
Mikhail Kshevetskiy7539fe12024-07-31 08:11:28 +0400307 ret = gpio_request_by_name(dev, "miso-gpios", 0, &plat->miso,
Fabio Estevam51a7d412023-05-18 19:22:40 -0300308 GPIOD_IS_IN);
309 if (ret)
Peng Fan54108e72016-05-03 10:02:21 +0800310 plat->flags |= SPI_MASTER_NO_RX;
311
312 if ((plat->flags & (SPI_MASTER_NO_RX | SPI_MASTER_NO_TX)) ==
313 (SPI_MASTER_NO_RX | SPI_MASTER_NO_TX))
Simon Glass61d10b72015-01-05 20:05:40 -0700314 return -EINVAL;
Simon Glass456dd7c2014-10-13 23:42:00 -0600315
316 return 0;
317}
318
319static const struct udevice_id soft_spi_ids[] = {
Peng Fan54108e72016-05-03 10:02:21 +0800320 { .compatible = "spi-gpio" },
Simon Glass456dd7c2014-10-13 23:42:00 -0600321 { }
322};
323
324U_BOOT_DRIVER(soft_spi) = {
325 .name = "soft_spi",
326 .id = UCLASS_SPI,
327 .of_match = soft_spi_ids,
328 .ops = &soft_spi_ops,
Simon Glassaad29ae2020-12-03 16:55:21 -0700329 .of_to_plat = soft_spi_of_to_plat,
Simon Glassb75b15b2020-12-03 16:55:23 -0700330 .plat_auto = sizeof(struct soft_spi_plat),
Simon Glass8a2b47f2020-12-03 16:55:17 -0700331 .priv_auto = sizeof(struct soft_spi_priv),
Simon Glass456dd7c2014-10-13 23:42:00 -0600332 .probe = soft_spi_probe,
Simon Glass456dd7c2014-10-13 23:42:00 -0600333};