blob: 0fa14339bdcd95180e491372042cce7316cfec62 [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
12#include <common.h>
13#include <dm.h>
14#include <errno.h>
15#include <fdtdec.h>
Simon Glass0f2af882020-05-10 11:40:05 -060016#include <log.h>
Simon Glass456dd7c2014-10-13 23:42:00 -060017#include <malloc.h>
18#include <spi.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060019#include <asm/global_data.h>
Simon Glass456dd7c2014-10-13 23:42:00 -060020#include <asm/gpio.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060021#include <linux/bitops.h>
Simon Glassdbd79542020-05-10 11:40:11 -060022#include <linux/delay.h>
Simon Glass456dd7c2014-10-13 23:42:00 -060023
24DECLARE_GLOBAL_DATA_PTR;
25
Simon Glassb75b15b2020-12-03 16:55:23 -070026struct soft_spi_plat {
Simon Glass61d10b72015-01-05 20:05:40 -070027 struct gpio_desc cs;
28 struct gpio_desc sclk;
29 struct gpio_desc mosi;
30 struct gpio_desc miso;
Simon Glass456dd7c2014-10-13 23:42:00 -060031 int spi_delay_us;
Peng Fan54108e72016-05-03 10:02:21 +080032 int flags;
Simon Glass456dd7c2014-10-13 23:42:00 -060033};
34
Peng Fan54108e72016-05-03 10:02:21 +080035#define SPI_MASTER_NO_RX BIT(0)
36#define SPI_MASTER_NO_TX BIT(1)
37
Simon Glass456dd7c2014-10-13 23:42:00 -060038struct soft_spi_priv {
39 unsigned int mode;
40};
41
42static int soft_spi_scl(struct udevice *dev, int bit)
43{
Peng Fanfa7dd9b2016-05-03 10:02:20 +080044 struct udevice *bus = dev_get_parent(dev);
Simon Glassb75b15b2020-12-03 16:55:23 -070045 struct soft_spi_plat *plat = dev_get_plat(bus);
Simon Glass456dd7c2014-10-13 23:42:00 -060046
Simon Glass61d10b72015-01-05 20:05:40 -070047 dm_gpio_set_value(&plat->sclk, bit);
Simon Glass456dd7c2014-10-13 23:42:00 -060048
49 return 0;
50}
51
52static int soft_spi_sda(struct udevice *dev, int bit)
53{
Peng Fanfa7dd9b2016-05-03 10:02:20 +080054 struct udevice *bus = dev_get_parent(dev);
Simon Glassb75b15b2020-12-03 16:55:23 -070055 struct soft_spi_plat *plat = dev_get_plat(bus);
Simon Glass456dd7c2014-10-13 23:42:00 -060056
Simon Glass61d10b72015-01-05 20:05:40 -070057 dm_gpio_set_value(&plat->mosi, bit);
Simon Glass456dd7c2014-10-13 23:42:00 -060058
59 return 0;
60}
61
62static int soft_spi_cs_activate(struct udevice *dev)
63{
Peng Fanfa7dd9b2016-05-03 10:02:20 +080064 struct udevice *bus = dev_get_parent(dev);
Johannes Holland84120622020-05-11 15:22:26 +020065 struct soft_spi_priv *priv = dev_get_priv(bus);
Simon Glassb75b15b2020-12-03 16:55:23 -070066 struct soft_spi_plat *plat = dev_get_plat(bus);
Johannes Holland84120622020-05-11 15:22:26 +020067 int cidle = !!(priv->mode & SPI_CPOL);
Simon Glass456dd7c2014-10-13 23:42:00 -060068
Simon Glass61d10b72015-01-05 20:05:40 -070069 dm_gpio_set_value(&plat->cs, 0);
Johannes Holland84120622020-05-11 15:22:26 +020070 dm_gpio_set_value(&plat->sclk, cidle); /* to idle */
Simon Glass61d10b72015-01-05 20:05:40 -070071 dm_gpio_set_value(&plat->cs, 1);
Simon Glass456dd7c2014-10-13 23:42:00 -060072
73 return 0;
74}
75
76static int soft_spi_cs_deactivate(struct udevice *dev)
77{
Peng Fanfa7dd9b2016-05-03 10:02:20 +080078 struct udevice *bus = dev_get_parent(dev);
Simon Glassb75b15b2020-12-03 16:55:23 -070079 struct soft_spi_plat *plat = dev_get_plat(bus);
Simon Glass456dd7c2014-10-13 23:42:00 -060080
Simon Glass61d10b72015-01-05 20:05:40 -070081 dm_gpio_set_value(&plat->cs, 0);
Simon Glass456dd7c2014-10-13 23:42:00 -060082
83 return 0;
84}
85
86static int soft_spi_claim_bus(struct udevice *dev)
87{
Johannes Holland84120622020-05-11 15:22:26 +020088 struct udevice *bus = dev_get_parent(dev);
89 struct soft_spi_priv *priv = dev_get_priv(bus);
90 int cidle = !!(priv->mode & SPI_CPOL);
Simon Glass456dd7c2014-10-13 23:42:00 -060091 /*
92 * Make sure the SPI clock is in idle state as defined for
93 * this slave.
94 */
Johannes Holland84120622020-05-11 15:22:26 +020095 return soft_spi_scl(dev, cidle);
Simon Glass456dd7c2014-10-13 23:42:00 -060096}
97
98static int soft_spi_release_bus(struct udevice *dev)
99{
100 /* Nothing to do */
101 return 0;
102}
103
104/*-----------------------------------------------------------------------
105 * SPI transfer
106 *
107 * This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks
108 * "bitlen" bits in the SPI MISO port. That's just the way SPI works.
109 *
110 * The source of the outgoing bits is the "dout" parameter and the
111 * destination of the input bits is the "din" parameter. Note that "dout"
112 * and "din" can point to the same memory location, in which case the
113 * input data overwrites the output data (since both are buffered by
114 * temporary variables, this is OK).
115 */
116static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen,
117 const void *dout, void *din, unsigned long flags)
118{
Peng Fanfa7dd9b2016-05-03 10:02:20 +0800119 struct udevice *bus = dev_get_parent(dev);
120 struct soft_spi_priv *priv = dev_get_priv(bus);
Simon Glassb75b15b2020-12-03 16:55:23 -0700121 struct soft_spi_plat *plat = dev_get_plat(bus);
Simon Glass456dd7c2014-10-13 23:42:00 -0600122 uchar tmpdin = 0;
123 uchar tmpdout = 0;
124 const u8 *txd = dout;
125 u8 *rxd = din;
Johannes Holland84120622020-05-11 15:22:26 +0200126 int cpha = !!(priv->mode & SPI_CPHA);
127 int cidle = !!(priv->mode & SPI_CPOL);
Simon Glass456dd7c2014-10-13 23:42:00 -0600128 unsigned int j;
129
130 debug("spi_xfer: slave %s:%s dout %08X din %08X bitlen %u\n",
131 dev->parent->name, dev->name, *(uint *)txd, *(uint *)rxd,
132 bitlen);
133
134 if (flags & SPI_XFER_BEGIN)
135 soft_spi_cs_activate(dev);
136
137 for (j = 0; j < bitlen; j++) {
138 /*
139 * Check if it is time to work on a new byte.
140 */
141 if ((j % 8) == 0) {
142 if (txd)
143 tmpdout = *txd++;
144 else
145 tmpdout = 0;
146 if (j != 0) {
147 if (rxd)
148 *rxd++ = tmpdin;
149 }
150 tmpdin = 0;
151 }
152
Johannes Holland84120622020-05-11 15:22:26 +0200153 /*
154 * CPOL 0: idle is low (0), active is high (1)
155 * CPOL 1: idle is high (1), active is low (0)
156 */
157
158 /*
159 * drive bit
160 * CPHA 1: CLK from idle to active
161 */
162 if (cpha)
163 soft_spi_scl(dev, !cidle);
Peng Fan54108e72016-05-03 10:02:21 +0800164 if ((plat->flags & SPI_MASTER_NO_TX) == 0)
165 soft_spi_sda(dev, !!(tmpdout & 0x80));
Simon Glass456dd7c2014-10-13 23:42:00 -0600166 udelay(plat->spi_delay_us);
Johannes Holland84120622020-05-11 15:22:26 +0200167
168 /*
169 * sample bit
170 * CPHA 0: CLK from idle to active
171 * CPHA 1: CLK from active to idle
172 */
173 if (!cpha)
174 soft_spi_scl(dev, !cidle);
Simon Glass456dd7c2014-10-13 23:42:00 -0600175 else
Johannes Holland84120622020-05-11 15:22:26 +0200176 soft_spi_scl(dev, cidle);
Simon Glass456dd7c2014-10-13 23:42:00 -0600177 tmpdin <<= 1;
Peng Fan54108e72016-05-03 10:02:21 +0800178 if ((plat->flags & SPI_MASTER_NO_RX) == 0)
179 tmpdin |= dm_gpio_get_value(&plat->miso);
Simon Glass456dd7c2014-10-13 23:42:00 -0600180 tmpdout <<= 1;
181 udelay(plat->spi_delay_us);
Johannes Holland84120622020-05-11 15:22:26 +0200182
183 /*
184 * drive bit
185 * CPHA 0: CLK from active to idle
186 */
187 if (!cpha)
188 soft_spi_scl(dev, cidle);
Simon Glass456dd7c2014-10-13 23:42:00 -0600189 }
190 /*
191 * If the number of bits isn't a multiple of 8, shift the last
192 * bits over to left-justify them. Then store the last byte
193 * read in.
194 */
195 if (rxd) {
196 if ((bitlen % 8) != 0)
197 tmpdin <<= 8 - (bitlen % 8);
198 *rxd++ = tmpdin;
199 }
200
201 if (flags & SPI_XFER_END)
202 soft_spi_cs_deactivate(dev);
203
204 return 0;
205}
206
207static int soft_spi_set_speed(struct udevice *dev, unsigned int speed)
208{
Johannes Holland84120622020-05-11 15:22:26 +0200209 /* Ignore any speed settings. Speed is implemented via "spi-delay-us" */
Simon Glass456dd7c2014-10-13 23:42:00 -0600210 return 0;
211}
212
213static int soft_spi_set_mode(struct udevice *dev, unsigned int mode)
214{
215 struct soft_spi_priv *priv = dev_get_priv(dev);
216
217 priv->mode = mode;
218
219 return 0;
220}
221
Simon Glass456dd7c2014-10-13 23:42:00 -0600222static const struct dm_spi_ops soft_spi_ops = {
223 .claim_bus = soft_spi_claim_bus,
224 .release_bus = soft_spi_release_bus,
225 .xfer = soft_spi_xfer,
226 .set_speed = soft_spi_set_speed,
227 .set_mode = soft_spi_set_mode,
228};
229
Simon Glassaad29ae2020-12-03 16:55:21 -0700230static int soft_spi_of_to_plat(struct udevice *dev)
Simon Glass456dd7c2014-10-13 23:42:00 -0600231{
Simon Glass95588622020-12-22 19:30:28 -0700232 struct soft_spi_plat *plat = dev_get_plat(dev);
Simon Glass456dd7c2014-10-13 23:42:00 -0600233 const void *blob = gd->fdt_blob;
Simon Glassdd79d6e2017-01-17 16:52:55 -0700234 int node = dev_of_offset(dev);
Simon Glass456dd7c2014-10-13 23:42:00 -0600235
Simon Glass456dd7c2014-10-13 23:42:00 -0600236 plat->spi_delay_us = fdtdec_get_int(blob, node, "spi-delay-us", 0);
237
238 return 0;
239}
240
241static int soft_spi_probe(struct udevice *dev)
242{
Simon Glassde44acf2015-09-28 23:32:01 -0600243 struct spi_slave *slave = dev_get_parent_priv(dev);
Simon Glass95588622020-12-22 19:30:28 -0700244 struct soft_spi_plat *plat = dev_get_plat(dev);
Simon Glass61d10b72015-01-05 20:05:40 -0700245 int cs_flags, clk_flags;
Peng Fan54108e72016-05-03 10:02:21 +0800246 int ret;
Simon Glass456dd7c2014-10-13 23:42:00 -0600247
Christophe Kerellob34a10a2019-08-02 15:46:29 +0200248 cs_flags = (slave && slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW;
249 clk_flags = (slave && slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0;
Peng Fan54108e72016-05-03 10:02:21 +0800250
Fabio Estevam51a7d412023-05-18 19:22:40 -0300251 ret = gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs,
252 GPIOD_IS_OUT | cs_flags);
253 if (ret)
Peng Fan54108e72016-05-03 10:02:21 +0800254 return -EINVAL;
255
Fabio Estevam51a7d412023-05-18 19:22:40 -0300256 ret = gpio_request_by_name(dev, "gpio-sck", 0, &plat->sclk,
257 GPIOD_IS_OUT | clk_flags);
258 if (ret)
259 ret = gpio_request_by_name(dev, "sck-gpios", 0, &plat->sclk,
260 GPIOD_IS_OUT | clk_flags);
261 if (ret)
262 return -EINVAL;
263
Peng Fan54108e72016-05-03 10:02:21 +0800264 ret = gpio_request_by_name(dev, "gpio-mosi", 0, &plat->mosi,
265 GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
266 if (ret)
Fabio Estevam51a7d412023-05-18 19:22:40 -0300267 ret = gpio_request_by_name(dev, "mosi-gpios", 0, &plat->mosi,
268 GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
269 if (ret)
Peng Fan54108e72016-05-03 10:02:21 +0800270 plat->flags |= SPI_MASTER_NO_TX;
271
272 ret = gpio_request_by_name(dev, "gpio-miso", 0, &plat->miso,
273 GPIOD_IS_IN);
274 if (ret)
Fabio Estevam51a7d412023-05-18 19:22:40 -0300275 ret = gpio_request_by_name(dev, "gpio-miso", 0, &plat->miso,
276 GPIOD_IS_IN);
277 if (ret)
Peng Fan54108e72016-05-03 10:02:21 +0800278 plat->flags |= SPI_MASTER_NO_RX;
279
280 if ((plat->flags & (SPI_MASTER_NO_RX | SPI_MASTER_NO_TX)) ==
281 (SPI_MASTER_NO_RX | SPI_MASTER_NO_TX))
Simon Glass61d10b72015-01-05 20:05:40 -0700282 return -EINVAL;
Simon Glass456dd7c2014-10-13 23:42:00 -0600283
284 return 0;
285}
286
287static const struct udevice_id soft_spi_ids[] = {
Peng Fan54108e72016-05-03 10:02:21 +0800288 { .compatible = "spi-gpio" },
Simon Glass456dd7c2014-10-13 23:42:00 -0600289 { }
290};
291
292U_BOOT_DRIVER(soft_spi) = {
293 .name = "soft_spi",
294 .id = UCLASS_SPI,
295 .of_match = soft_spi_ids,
296 .ops = &soft_spi_ops,
Simon Glassaad29ae2020-12-03 16:55:21 -0700297 .of_to_plat = soft_spi_of_to_plat,
Simon Glassb75b15b2020-12-03 16:55:23 -0700298 .plat_auto = sizeof(struct soft_spi_plat),
Simon Glass8a2b47f2020-12-03 16:55:17 -0700299 .priv_auto = sizeof(struct soft_spi_priv),
Simon Glass456dd7c2014-10-13 23:42:00 -0600300 .probe = soft_spi_probe,
Simon Glass456dd7c2014-10-13 23:42:00 -0600301};