1 // SPDX-License-Identifier: GPL-2.0
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>
14 #define GXP_SPI0_MAX_CHIPSELECT 2
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
26 #define SPIMCTRL_START 0x01
27 #define SPIMCTRL_BUSY 0x02
29 #define CMD_READ_ARRAY_FAST 0x0b
32 struct spi_slave slave;
38 static void spi_set_mode(struct gxp_spi_priv *priv, int mode)
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);
51 writeb(value, priv->base + OFFSET_SPIMCTRL);
54 static int gxp_spi_xfer(struct udevice *dev, unsigned int bitlen, const void *dout, void *din,
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);
61 unsigned int len = bitlen / 8;
63 unsigned int addr = 0;
64 unsigned char uchar_out[len];
65 unsigned char *uchar_in = (unsigned char *)din;
71 * error: gxp spi engin cannot send data to dout and read data from din at the same
77 memset(uchar_out, 0, sizeof(uchar_out));
79 memcpy(uchar_out, dout, len);
81 if (flags & SPI_XFER_BEGIN) {
82 /* the dout is cmd + addr, cmd=dout[0], add1~3=dout[1~3]. */
84 writeb(uchar_out[0], priv->base + OFFSET_SPICMD);
87 value = readl(priv->base + OFFSET_SPIMCFG);
90 value |= (slave_plat->cs << 24);
92 /* addr reg and addr size */
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 */
100 writel(0, priv->base + OFFSET_SPIADDR);
101 /* set the address size to 0 byte */
102 value &= ~(0x07 << 16);
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 */
113 writel(value, priv->base + OFFSET_SPIMCFG);
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);
120 writeb(value, priv->base + OFFSET_SPIMCTRL);
122 /* set the data byte count */
123 writeb(0, priv->base + OFFSET_SPIDCNT);
125 /* set the start bit */
126 value = readb(priv->base + OFFSET_SPIMCTRL);
127 value |= SPIMCTRL_START;
128 writeb(value, priv->base + OFFSET_SPIMCTRL);
130 /* wait busy bit is cleared */
132 value = readb(priv->base + OFFSET_SPIMCTRL);
133 } while (value & SPIMCTRL_BUSY);
138 if (!(flags & SPI_XFER_END) && (flags & SPI_XFER_BEGIN)) {
139 /* first of spi_xfer calls */
143 /* if dout != null, write data to buf and start transaction */
145 if (len > slave->max_write_size) {
146 printf("SF: write length is too big(>%d)\n", slave->max_write_size);
150 /* load the data bytes */
151 memcpy((u8 *)priv->base + OFFSET_SPILDAT, dout, len);
153 /* write: set the data direction bit to 1 */
154 value = readb(priv->base + OFFSET_SPIMCTRL);
156 writeb(value, priv->base + OFFSET_SPIMCTRL);
158 /* set the data byte count */
159 writeb(len, priv->base + OFFSET_SPIDCNT);
161 /* set the start bit */
162 value = readb(priv->base + OFFSET_SPIMCTRL);
163 value |= SPIMCTRL_START;
164 writeb(value, priv->base + OFFSET_SPIMCTRL);
166 /* wait busy bit is cleared */
168 value = readb(priv->base + OFFSET_SPIMCTRL);
169 } while (value & SPIMCTRL_BUSY);
174 /* if din !=null, start and read data */
178 while (read_ptr < len) {
179 read_len = len - read_ptr;
180 if (read_len > GXP_SPILDAT_SIZE)
181 read_len = GXP_SPILDAT_SIZE;
183 /* read: set the data direction bit to 0 */
184 value = readb(priv->base + OFFSET_SPIMCTRL);
186 writeb(value, priv->base + OFFSET_SPIMCTRL);
188 /* set the data byte count */
189 writeb(read_len, priv->base + OFFSET_SPIDCNT);
191 /* set the start bit */
192 value = readb(priv->base + OFFSET_SPIMCTRL);
193 value |= SPIMCTRL_START;
194 writeb(value, priv->base + OFFSET_SPIMCTRL);
196 /* wait busy bit is cleared */
198 value = readb(priv->base + OFFSET_SPIMCTRL);
199 } while (value & SPIMCTRL_BUSY);
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;
206 addr = readl(priv->base + OFFSET_SPIADDR);
208 writel(addr, priv->base + OFFSET_SPIADDR);
216 static int gxp_spi_set_speed(struct udevice *dev, unsigned int speed)
218 /* Accept any speed */
222 static int gxp_spi_set_mode(struct udevice *dev, unsigned int mode)
224 struct gxp_spi_priv *priv = dev_get_priv(dev->parent);
231 static int gxp_spi_claim_bus(struct udevice *dev)
233 struct gxp_spi_priv *priv = dev_get_priv(dev->parent);
236 spi_set_mode(priv, MANUAL_MODE);
238 /* exit 4 bytes addr mode, uboot spi_flash only supports 3 byets address mode */
240 gxp_spi_xfer(dev, 1 * 8, &cmd, NULL, SPI_XFER_BEGIN | SPI_XFER_END);
244 static int gxp_spi_release_bus(struct udevice *dev)
246 struct gxp_spi_priv *priv = dev_get_priv(dev->parent);
248 spi_set_mode(priv, AUTO_MODE);
253 int gxp_spi_cs_info(struct udevice *bus, unsigned int cs, struct spi_cs_info *info)
255 if (cs < GXP_SPI0_MAX_CHIPSELECT)
261 static int gxp_spi_probe(struct udevice *bus)
263 struct gxp_spi_priv *priv = dev_get_priv(bus);
265 priv->base = dev_read_addr_ptr(bus);
272 static int gxp_spi_child_pre_probe(struct udevice *dev)
274 struct spi_slave *slave = dev_get_parent_priv(dev);
276 slave->max_write_size = GXP_SPILDAT_SIZE;
281 static 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,
290 static const struct udevice_id gxp_spi_ids[] = {
291 { .compatible = "hpe,gxp-spi" },
295 U_BOOT_DRIVER(gxp_spi) = {
298 .of_match = gxp_spi_ids,
300 .priv_auto = sizeof(struct gxp_spi_priv),
301 .probe = gxp_spi_probe,
302 .child_pre_probe = gxp_spi_child_pre_probe,