blob: 70d76ac66adf39650149967d9ddc9dd2232ca337 [file] [log] [blame]
Nick Hawkins4f689b32022-06-08 16:21:36 -05001// SPDX-License-Identifier: GPL-2.0
2/*
3 * GXP SPI driver
4 *
5 * (C) Copyright 2022 Hewlett Packard Enterprise Development LP.
6 * Author: Nick Hawkins <nick.hawkins@hpe.com>
7 * Author: Jean-Marie Verdun <verdun@hpe.com>
8 */
9
10#include <spi.h>
11#include <asm/io.h>
12#include <dm.h>
13
14#define GXP_SPI0_MAX_CHIPSELECT 2
15
16#define MANUAL_MODE 0
17#define AUTO_MODE 1
18#define OFFSET_SPIMCFG 0x00
19#define OFFSET_SPIMCTRL 0x04
20#define OFFSET_SPICMD 0x05
21#define OFFSET_SPIDCNT 0x06
22#define OFFSET_SPIADDR 0x08
23#define OFFSET_SPILDAT 0x40
24#define GXP_SPILDAT_SIZE 64
25
26#define SPIMCTRL_START 0x01
27#define SPIMCTRL_BUSY 0x02
28
29#define CMD_READ_ARRAY_FAST 0x0b
30
31struct gxp_spi_priv {
32 struct spi_slave slave;
33 void __iomem *base;
34 unsigned int mode;
35
36};
37
38static void spi_set_mode(struct gxp_spi_priv *priv, int mode)
39{
40 unsigned char value;
41
42 value = readb(priv->base + OFFSET_SPIMCTRL);
43 if (mode == MANUAL_MODE) {
44 writeb(0x55, priv->base + OFFSET_SPICMD);
45 writeb(0xaa, priv->base + OFFSET_SPICMD);
46 /* clear bit5 and bit4, auto_start and start_mask */
47 value &= ~(0x03 << 4);
48 } else {
49 value |= (0x03 << 4);
50 }
51 writeb(value, priv->base + OFFSET_SPIMCTRL);
52}
53
54static int gxp_spi_xfer(struct udevice *dev, unsigned int bitlen, const void *dout, void *din,
55 unsigned long flags)
56{
57 struct gxp_spi_priv *priv = dev_get_priv(dev->parent);
58 struct spi_slave *slave = dev_get_parent_priv(dev);
59 struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
60
61 unsigned int len = bitlen / 8;
62 unsigned int value;
63 unsigned int addr = 0;
64 unsigned char uchar_out[len];
65 unsigned char *uchar_in = (unsigned char *)din;
66 int read_len;
67 int read_ptr;
68
69 if (dout && din) {
70 /*
71 * error: gxp spi engin cannot send data to dout and read data from din at the same
72 * time
73 */
74 return -1;
75 }
76
77 memset(uchar_out, 0, sizeof(uchar_out));
78 if (dout)
79 memcpy(uchar_out, dout, len);
80
81 if (flags & SPI_XFER_BEGIN) {
82 /* the dout is cmd + addr, cmd=dout[0], add1~3=dout[1~3]. */
83 /* cmd reg */
84 writeb(uchar_out[0], priv->base + OFFSET_SPICMD);
85
86 /* config reg */
87 value = readl(priv->base + OFFSET_SPIMCFG);
88 value &= ~(1 << 24);
89 /* set chipselect */
90 value |= (slave_plat->cs << 24);
91
92 /* addr reg and addr size */
93 if (len >= 4) {
94 addr = uchar_out[1] << 16 | uchar_out[2] << 8 | uchar_out[3];
95 writel(addr, priv->base + OFFSET_SPIADDR);
96 value &= ~(0x07 << 16);
97 /* set the address size to 3 byte */
98 value |= (3 << 16);
99 } else {
100 writel(0, priv->base + OFFSET_SPIADDR);
101 /* set the address size to 0 byte */
102 value &= ~(0x07 << 16);
103 }
104
105 /* dummy */
106 /* clear dummy_cnt to */
107 value &= ~(0x1f << 19);
108 if (uchar_out[0] == CMD_READ_ARRAY_FAST) {
109 /* fast read needs 8 dummy clocks */
110 value |= (8 << 19);
111 }
112
113 writel(value, priv->base + OFFSET_SPIMCFG);
114
115 if (flags & SPI_XFER_END) {
116 /* no data cmd just start it */
117 /* set the data direction bit to 1 */
118 value = readb(priv->base + OFFSET_SPIMCTRL);
119 value |= (1 << 3);
120 writeb(value, priv->base + OFFSET_SPIMCTRL);
121
122 /* set the data byte count */
123 writeb(0, priv->base + OFFSET_SPIDCNT);
124
125 /* set the start bit */
126 value = readb(priv->base + OFFSET_SPIMCTRL);
127 value |= SPIMCTRL_START;
128 writeb(value, priv->base + OFFSET_SPIMCTRL);
129
130 /* wait busy bit is cleared */
131 do {
132 value = readb(priv->base + OFFSET_SPIMCTRL);
133 } while (value & SPIMCTRL_BUSY);
134 return 0;
135 }
136 }
137
138 if (!(flags & SPI_XFER_END) && (flags & SPI_XFER_BEGIN)) {
139 /* first of spi_xfer calls */
140 return 0;
141 }
142
143 /* if dout != null, write data to buf and start transaction */
144 if (dout) {
145 if (len > slave->max_write_size) {
146 printf("SF: write length is too big(>%d)\n", slave->max_write_size);
147 return -1;
148 }
149
150 /* load the data bytes */
151 memcpy((u8 *)priv->base + OFFSET_SPILDAT, dout, len);
152
153 /* write: set the data direction bit to 1 */
154 value = readb(priv->base + OFFSET_SPIMCTRL);
155 value |= (1 << 3);
156 writeb(value, priv->base + OFFSET_SPIMCTRL);
157
158 /* set the data byte count */
159 writeb(len, priv->base + OFFSET_SPIDCNT);
160
161 /* set the start bit */
162 value = readb(priv->base + OFFSET_SPIMCTRL);
163 value |= SPIMCTRL_START;
164 writeb(value, priv->base + OFFSET_SPIMCTRL);
165
166 /* wait busy bit is cleared */
167 do {
168 value = readb(priv->base + OFFSET_SPIMCTRL);
169 } while (value & SPIMCTRL_BUSY);
170
171 return 0;
172 }
173
174 /* if din !=null, start and read data */
175 if (uchar_in) {
176 read_ptr = 0;
177
178 while (read_ptr < len) {
179 read_len = len - read_ptr;
180 if (read_len > GXP_SPILDAT_SIZE)
181 read_len = GXP_SPILDAT_SIZE;
182
183 /* read: set the data direction bit to 0 */
184 value = readb(priv->base + OFFSET_SPIMCTRL);
185 value &= ~(1 << 3);
186 writeb(value, priv->base + OFFSET_SPIMCTRL);
187
188 /* set the data byte count */
189 writeb(read_len, priv->base + OFFSET_SPIDCNT);
190
191 /* set the start bit */
192 value = readb(priv->base + OFFSET_SPIMCTRL);
193 value |= SPIMCTRL_START;
194 writeb(value, priv->base + OFFSET_SPIMCTRL);
195
196 /* wait busy bit is cleared */
197 do {
198 value = readb(priv->base + OFFSET_SPIMCTRL);
199 } while (value & SPIMCTRL_BUSY);
200
201 /* store the data bytes */
202 memcpy(uchar_in + read_ptr, (u8 *)priv->base + OFFSET_SPILDAT, read_len);
203 /* update read_ptr and addr reg */
204 read_ptr += read_len;
205
206 addr = readl(priv->base + OFFSET_SPIADDR);
207 addr += read_len;
208 writel(addr, priv->base + OFFSET_SPIADDR);
209 }
210
211 return 0;
212 }
213 return -2;
214}
215
216static int gxp_spi_set_speed(struct udevice *dev, unsigned int speed)
217{
218 /* Accept any speed */
219 return 0;
220}
221
222static int gxp_spi_set_mode(struct udevice *dev, unsigned int mode)
223{
224 struct gxp_spi_priv *priv = dev_get_priv(dev->parent);
225
226 priv->mode = mode;
227
228 return 0;
229}
230
231static int gxp_spi_claim_bus(struct udevice *dev)
232{
233 struct gxp_spi_priv *priv = dev_get_priv(dev->parent);
234 unsigned char cmd;
235
236 spi_set_mode(priv, MANUAL_MODE);
237
238 /* exit 4 bytes addr mode, uboot spi_flash only supports 3 byets address mode */
239 cmd = 0xe9;
240 gxp_spi_xfer(dev, 1 * 8, &cmd, NULL, SPI_XFER_BEGIN | SPI_XFER_END);
241 return 0;
242}
243
244static int gxp_spi_release_bus(struct udevice *dev)
245{
246 struct gxp_spi_priv *priv = dev_get_priv(dev->parent);
247
248 spi_set_mode(priv, AUTO_MODE);
249
250 return 0;
251}
252
253int gxp_spi_cs_info(struct udevice *bus, unsigned int cs, struct spi_cs_info *info)
254{
255 if (cs < GXP_SPI0_MAX_CHIPSELECT)
256 return 0;
257 else
258 return -ENODEV;
259}
260
261static int gxp_spi_probe(struct udevice *bus)
262{
263 struct gxp_spi_priv *priv = dev_get_priv(bus);
264
265 priv->base = dev_read_addr_ptr(bus);
266 if (!priv->base)
267 return -ENOENT;
268
269 return 0;
270}
271
272static int gxp_spi_child_pre_probe(struct udevice *dev)
273{
274 struct spi_slave *slave = dev_get_parent_priv(dev);
275
276 slave->max_write_size = GXP_SPILDAT_SIZE;
277
278 return 0;
279}
280
281static const struct dm_spi_ops gxp_spi_ops = {
282 .claim_bus = gxp_spi_claim_bus,
283 .release_bus = gxp_spi_release_bus,
284 .xfer = gxp_spi_xfer,
285 .set_speed = gxp_spi_set_speed,
286 .set_mode = gxp_spi_set_mode,
287 .cs_info = gxp_spi_cs_info,
288};
289
290static const struct udevice_id gxp_spi_ids[] = {
291 { .compatible = "hpe,gxp-spi" },
292 { }
293};
294
295U_BOOT_DRIVER(gxp_spi) = {
296 .name = "gxp_spi",
297 .id = UCLASS_SPI,
298 .of_match = gxp_spi_ids,
299 .ops = &gxp_spi_ops,
300 .priv_auto = sizeof(struct gxp_spi_priv),
301 .probe = gxp_spi_probe,
302 .child_pre_probe = gxp_spi_child_pre_probe,
303};
304