blob: 23ac5bb76c049d96a5f6e95e4cc2fc47ae0e180e [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +01002/*
3 * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com>
4 *
5 * Derived from linux/drivers/spi/spi-bcm63xx-hsspi.c:
6 * Copyright (C) 2000-2010 Broadcom Corporation
7 * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org>
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +01008 */
9
10#include <common.h>
11#include <clk.h>
12#include <dm.h>
Simon Glass0f2af882020-05-10 11:40:05 -060013#include <log.h>
Simon Glass9bc15642020-02-03 07:36:16 -070014#include <malloc.h>
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +010015#include <spi.h>
16#include <reset.h>
17#include <wait_bit.h>
18#include <asm/io.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060019#include <linux/bitops.h>
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +010020
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +010021#define HSSPI_PP 0
22
William Zhang9a0ff5d2023-06-07 16:37:04 -070023/*
24 * The maximum frequency for SPI synchronous mode is 30MHz for some chips and
25 * 25MHz for some others. This depends on the chip layout and SPI signals
26 * distance to the pad. We use the lower of these values to cover all relevant
27 * chips.
28 */
29#define SPI_MAX_SYNC_CLOCK 25000000
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +010030
31/* SPI Control register */
32#define SPI_CTL_REG 0x000
33#define SPI_CTL_CS_POL_SHIFT 0
34#define SPI_CTL_CS_POL_MASK (0xff << SPI_CTL_CS_POL_SHIFT)
35#define SPI_CTL_CLK_GATE_SHIFT 16
36#define SPI_CTL_CLK_GATE_MASK (1 << SPI_CTL_CLK_GATE_SHIFT)
37#define SPI_CTL_CLK_POL_SHIFT 17
38#define SPI_CTL_CLK_POL_MASK (1 << SPI_CTL_CLK_POL_SHIFT)
39
40/* SPI Interrupts registers */
41#define SPI_IR_STAT_REG 0x008
42#define SPI_IR_ST_MASK_REG 0x00c
43#define SPI_IR_MASK_REG 0x010
44
45#define SPI_IR_CLEAR_ALL 0xff001f1f
46
47/* SPI Ping-Pong Command registers */
48#define SPI_CMD_REG (0x080 + (0x40 * (HSSPI_PP)) + 0x00)
49#define SPI_CMD_OP_SHIFT 0
50#define SPI_CMD_OP_START (0x1 << SPI_CMD_OP_SHIFT)
51#define SPI_CMD_PFL_SHIFT 8
52#define SPI_CMD_PFL_MASK (0x7 << SPI_CMD_PFL_SHIFT)
53#define SPI_CMD_SLAVE_SHIFT 12
54#define SPI_CMD_SLAVE_MASK (0x7 << SPI_CMD_SLAVE_SHIFT)
55
56/* SPI Ping-Pong Status registers */
57#define SPI_STAT_REG (0x080 + (0x40 * (HSSPI_PP)) + 0x04)
58#define SPI_STAT_SRCBUSY_SHIFT 1
59#define SPI_STAT_SRCBUSY_MASK (1 << SPI_STAT_SRCBUSY_SHIFT)
60
61/* SPI Profile Clock registers */
62#define SPI_PFL_CLK_REG(x) (0x100 + (0x20 * (x)) + 0x00)
63#define SPI_PFL_CLK_FREQ_SHIFT 0
64#define SPI_PFL_CLK_FREQ_MASK (0x3fff << SPI_PFL_CLK_FREQ_SHIFT)
65#define SPI_PFL_CLK_RSTLOOP_SHIFT 15
66#define SPI_PFL_CLK_RSTLOOP_MASK (1 << SPI_PFL_CLK_RSTLOOP_SHIFT)
67
68/* SPI Profile Signal registers */
69#define SPI_PFL_SIG_REG(x) (0x100 + (0x20 * (x)) + 0x04)
70#define SPI_PFL_SIG_LATCHRIS_SHIFT 12
71#define SPI_PFL_SIG_LATCHRIS_MASK (1 << SPI_PFL_SIG_LATCHRIS_SHIFT)
72#define SPI_PFL_SIG_LAUNCHRIS_SHIFT 13
73#define SPI_PFL_SIG_LAUNCHRIS_MASK (1 << SPI_PFL_SIG_LAUNCHRIS_SHIFT)
74#define SPI_PFL_SIG_ASYNCIN_SHIFT 16
75#define SPI_PFL_SIG_ASYNCIN_MASK (1 << SPI_PFL_SIG_ASYNCIN_SHIFT)
76
77/* SPI Profile Mode registers */
78#define SPI_PFL_MODE_REG(x) (0x100 + (0x20 * (x)) + 0x08)
79#define SPI_PFL_MODE_FILL_SHIFT 0
80#define SPI_PFL_MODE_FILL_MASK (0xff << SPI_PFL_MODE_FILL_SHIFT)
William Zhang9a0ff5d2023-06-07 16:37:04 -070081#define SPI_PFL_MODE_MDRDST_SHIFT 8
82#define SPI_PFL_MODE_MDWRST_SHIFT 12
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +010083#define SPI_PFL_MODE_MDRDSZ_SHIFT 16
84#define SPI_PFL_MODE_MDRDSZ_MASK (1 << SPI_PFL_MODE_MDRDSZ_SHIFT)
85#define SPI_PFL_MODE_MDWRSZ_SHIFT 18
86#define SPI_PFL_MODE_MDWRSZ_MASK (1 << SPI_PFL_MODE_MDWRSZ_SHIFT)
87#define SPI_PFL_MODE_3WIRE_SHIFT 20
88#define SPI_PFL_MODE_3WIRE_MASK (1 << SPI_PFL_MODE_3WIRE_SHIFT)
William Zhang9a0ff5d2023-06-07 16:37:04 -070089#define SPI_PFL_MODE_PREPCNT_SHIFT 24
90#define SPI_PFL_MODE_PREPCNT_MASK (4 << SPI_PFL_MODE_PREPCNT_SHIFT)
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +010091
92/* SPI Ping-Pong FIFO registers */
93#define HSSPI_FIFO_SIZE 0x200
94#define HSSPI_FIFO_BASE (0x200 + \
95 (HSSPI_FIFO_SIZE * HSSPI_PP))
96
97/* SPI Ping-Pong FIFO OP register */
98#define HSSPI_FIFO_OP_SIZE 0x2
99#define HSSPI_FIFO_OP_REG (HSSPI_FIFO_BASE + 0x00)
100#define HSSPI_FIFO_OP_BYTES_SHIFT 0
101#define HSSPI_FIFO_OP_BYTES_MASK (0x3ff << HSSPI_FIFO_OP_BYTES_SHIFT)
102#define HSSPI_FIFO_OP_MBIT_SHIFT 11
103#define HSSPI_FIFO_OP_MBIT_MASK (1 << HSSPI_FIFO_OP_MBIT_SHIFT)
104#define HSSPI_FIFO_OP_CODE_SHIFT 13
105#define HSSPI_FIFO_OP_READ_WRITE (1 << HSSPI_FIFO_OP_CODE_SHIFT)
106#define HSSPI_FIFO_OP_CODE_W (2 << HSSPI_FIFO_OP_CODE_SHIFT)
107#define HSSPI_FIFO_OP_CODE_R (3 << HSSPI_FIFO_OP_CODE_SHIFT)
108
William Zhang9a0ff5d2023-06-07 16:37:04 -0700109#define HSSPI_MAX_DATA_SIZE (HSSPI_FIFO_SIZE - HSSPI_FIFO_OP_SIZE)
110#define HSSPI_MAX_PREPEND_SIZE 15
111
112#define HSSPI_XFER_MODE_PREPEND 0
113#define HSSPI_XFER_MODE_DUMMYCS 1
114
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100115struct bcm63xx_hsspi_priv {
116 void __iomem *regs;
117 ulong clk_rate;
118 uint8_t num_cs;
119 uint8_t cs_pols;
120 uint speed;
William Zhang9a0ff5d2023-06-07 16:37:04 -0700121 uint xfer_mode;
122 uint32_t prepend_cnt;
123 uint8_t prepend_buf[HSSPI_MAX_PREPEND_SIZE];
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100124};
125
126static int bcm63xx_hsspi_cs_info(struct udevice *bus, uint cs,
127 struct spi_cs_info *info)
128{
129 struct bcm63xx_hsspi_priv *priv = dev_get_priv(bus);
130
131 if (cs >= priv->num_cs) {
132 printf("no cs %u\n", cs);
Bin Mengf8586f62019-09-09 06:00:01 -0700133 return -EINVAL;
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100134 }
135
136 return 0;
137}
138
139static int bcm63xx_hsspi_set_mode(struct udevice *bus, uint mode)
140{
141 struct bcm63xx_hsspi_priv *priv = dev_get_priv(bus);
142
143 /* clock polarity */
144 if (mode & SPI_CPOL)
Kursad Oney711c7302019-08-14 15:18:34 +0200145 setbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_POL_MASK);
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100146 else
Kursad Oney711c7302019-08-14 15:18:34 +0200147 clrbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_POL_MASK);
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100148
149 return 0;
150}
151
152static int bcm63xx_hsspi_set_speed(struct udevice *bus, uint speed)
153{
154 struct bcm63xx_hsspi_priv *priv = dev_get_priv(bus);
155
156 priv->speed = speed;
157
158 return 0;
159}
160
161static void bcm63xx_hsspi_activate_cs(struct bcm63xx_hsspi_priv *priv,
Simon Glassb75b15b2020-12-03 16:55:23 -0700162 struct dm_spi_slave_plat *plat)
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100163{
164 uint32_t clr, set;
William Zhang9a0ff5d2023-06-07 16:37:04 -0700165 uint speed = priv->speed;
166
167 if (priv->xfer_mode == HSSPI_XFER_MODE_DUMMYCS &&
168 speed > SPI_MAX_SYNC_CLOCK) {
169 speed = SPI_MAX_SYNC_CLOCK;
170 debug("Force to dummy cs mode. Reduce the speed to %dHz\n", speed);
171 }
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100172
173 /* profile clock */
William Zhang9a0ff5d2023-06-07 16:37:04 -0700174 set = DIV_ROUND_UP(priv->clk_rate, speed);
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100175 set = DIV_ROUND_UP(2048, set);
176 set &= SPI_PFL_CLK_FREQ_MASK;
177 set |= SPI_PFL_CLK_RSTLOOP_MASK;
Kursad Oney711c7302019-08-14 15:18:34 +0200178 writel(set, priv->regs + SPI_PFL_CLK_REG(plat->cs));
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100179
180 /* profile signal */
181 set = 0;
182 clr = SPI_PFL_SIG_LAUNCHRIS_MASK |
183 SPI_PFL_SIG_LATCHRIS_MASK |
184 SPI_PFL_SIG_ASYNCIN_MASK;
185
186 /* latch/launch config */
187 if (plat->mode & SPI_CPHA)
188 set |= SPI_PFL_SIG_LAUNCHRIS_MASK;
189 else
190 set |= SPI_PFL_SIG_LATCHRIS_MASK;
191
192 /* async clk */
William Zhang9a0ff5d2023-06-07 16:37:04 -0700193 if (speed > SPI_MAX_SYNC_CLOCK)
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100194 set |= SPI_PFL_SIG_ASYNCIN_MASK;
195
Kursad Oney711c7302019-08-14 15:18:34 +0200196 clrsetbits_32(priv->regs + SPI_PFL_SIG_REG(plat->cs), clr, set);
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100197
198 /* global control */
199 set = 0;
200 clr = 0;
201
William Zhang9a0ff5d2023-06-07 16:37:04 -0700202 if (priv->xfer_mode == HSSPI_XFER_MODE_PREPEND) {
203 if (priv->cs_pols & BIT(plat->cs))
204 set |= BIT(plat->cs);
205 else
206 clr |= BIT(plat->cs);
207 } else {
208 /* invert cs polarity */
209 if (priv->cs_pols & BIT(plat->cs))
210 clr |= BIT(plat->cs);
211 else
212 set |= BIT(plat->cs);
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100213
William Zhang9a0ff5d2023-06-07 16:37:04 -0700214 /* invert dummy cs polarity */
215 if (priv->cs_pols & BIT(!plat->cs))
216 clr |= BIT(!plat->cs);
217 else
218 set |= BIT(!plat->cs);
219 }
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100220
Kursad Oney711c7302019-08-14 15:18:34 +0200221 clrsetbits_32(priv->regs + SPI_CTL_REG, clr, set);
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100222}
223
224static void bcm63xx_hsspi_deactivate_cs(struct bcm63xx_hsspi_priv *priv)
225{
226 /* restore cs polarities */
Kursad Oney711c7302019-08-14 15:18:34 +0200227 clrsetbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CS_POL_MASK,
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100228 priv->cs_pols);
229}
230
231/*
232 * BCM63xx HSSPI driver doesn't allow keeping CS active between transfers
233 * because they are controlled by HW.
234 * However, it provides a mechanism to prepend write transfers prior to read
235 * transfers (with a maximum prepend of 15 bytes), which is usually enough for
236 * SPI-connected flashes since reading requires prepending a write transfer of
237 * 5 bytes. On the other hand it also provides a way to invert each CS
238 * polarity, not only between transfers like the older BCM63xx SPI driver, but
239 * also the rest of the time.
240 *
241 * Instead of using the prepend mechanism, this implementation inverts the
242 * polarity of both the desired CS and another dummy CS when the bus is
243 * claimed. This way, the dummy CS is restored to its inactive value when
244 * transfers are issued and the desired CS is preserved in its active value
245 * all the time. This hack is also used in the upstream linux driver and
Pengfei Fan746271d2022-12-09 09:39:50 +0800246 * allows keeping CS active between transfers even if the HW doesn't give
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100247 * this possibility.
William Zhang9a0ff5d2023-06-07 16:37:04 -0700248 *
249 * This workaround only works when the dummy CS (usually CS1 when the actual
250 * CS is 0) pinmuxed to SPI chip select function if SPI clock is faster than
251 * SPI_MAX_SYNC_CLOCK. In old broadcom chip, CS1 pin is default to chip select
252 * function. But this is not the case for new chips. To make this function
253 * always work, it should be called with maximum clock of SPI_MAX_SYNC_CLOCK.
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100254 */
William Zhang9a0ff5d2023-06-07 16:37:04 -0700255static int bcm63xx_hsspi_xfer_dummy_cs(struct udevice *dev, unsigned int data_bytes,
256 const void *dout, void *din, unsigned long flags)
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100257{
258 struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent);
Simon Glassb75b15b2020-12-03 16:55:23 -0700259 struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100260 size_t step_size = HSSPI_FIFO_SIZE;
261 uint16_t opcode = 0;
William Zhang76333332023-06-07 16:37:02 -0700262 uint32_t val = SPI_PFL_MODE_FILL_MASK;
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100263 const uint8_t *tx = dout;
264 uint8_t *rx = din;
265
266 if (flags & SPI_XFER_BEGIN)
267 bcm63xx_hsspi_activate_cs(priv, plat);
268
269 /* fifo operation */
270 if (tx && rx)
271 opcode = HSSPI_FIFO_OP_READ_WRITE;
272 else if (rx)
273 opcode = HSSPI_FIFO_OP_CODE_R;
274 else if (tx)
275 opcode = HSSPI_FIFO_OP_CODE_W;
276
277 if (opcode != HSSPI_FIFO_OP_CODE_R)
278 step_size -= HSSPI_FIFO_OP_SIZE;
279
280 /* dual mode */
William Zhang76333332023-06-07 16:37:02 -0700281 if ((opcode == HSSPI_FIFO_OP_CODE_R && (plat->mode & SPI_RX_DUAL)) ||
282 (opcode == HSSPI_FIFO_OP_CODE_W && (plat->mode & SPI_TX_DUAL))) {
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100283 opcode |= HSSPI_FIFO_OP_MBIT_MASK;
284
William Zhang76333332023-06-07 16:37:02 -0700285 /* profile mode */
286 if (plat->mode & SPI_RX_DUAL)
287 val |= SPI_PFL_MODE_MDRDSZ_MASK;
288 if (plat->mode & SPI_TX_DUAL)
289 val |= SPI_PFL_MODE_MDWRSZ_MASK;
290 }
291
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100292 if (plat->mode & SPI_3WIRE)
293 val |= SPI_PFL_MODE_3WIRE_MASK;
Kursad Oney711c7302019-08-14 15:18:34 +0200294 writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs));
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100295
296 /* transfer loop */
297 while (data_bytes > 0) {
William Zhang9f3f08c2023-08-11 19:03:19 -0700298 size_t curr_step = min(step_size, (size_t)data_bytes);
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100299 int ret;
300
301 /* copy tx data */
302 if (tx) {
303 memcpy_toio(priv->regs + HSSPI_FIFO_BASE +
304 HSSPI_FIFO_OP_SIZE, tx, curr_step);
305 tx += curr_step;
306 }
307
308 /* set fifo operation */
Kursad Oney711c7302019-08-14 15:18:34 +0200309 writew(cpu_to_be16(opcode | (curr_step & HSSPI_FIFO_OP_BYTES_MASK)),
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100310 priv->regs + HSSPI_FIFO_OP_REG);
311
312 /* issue the transfer */
313 val = SPI_CMD_OP_START;
314 val |= (plat->cs << SPI_CMD_PFL_SHIFT) &
315 SPI_CMD_PFL_MASK;
316 val |= (!plat->cs << SPI_CMD_SLAVE_SHIFT) &
317 SPI_CMD_SLAVE_MASK;
Kursad Oney711c7302019-08-14 15:18:34 +0200318 writel(val, priv->regs + SPI_CMD_REG);
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100319
320 /* wait for completion */
Kursad Oney711c7302019-08-14 15:18:34 +0200321 ret = wait_for_bit_32(priv->regs + SPI_STAT_REG,
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100322 SPI_STAT_SRCBUSY_MASK, false,
323 1000, false);
324 if (ret) {
325 printf("interrupt timeout\n");
326 return ret;
327 }
328
329 /* copy rx data */
330 if (rx) {
331 memcpy_fromio(rx, priv->regs + HSSPI_FIFO_BASE,
332 curr_step);
333 rx += curr_step;
334 }
335
336 data_bytes -= curr_step;
337 }
338
339 if (flags & SPI_XFER_END)
340 bcm63xx_hsspi_deactivate_cs(priv);
341
342 return 0;
343}
344
William Zhang9a0ff5d2023-06-07 16:37:04 -0700345static int bcm63xx_prepare_prepend_transfer(struct bcm63xx_hsspi_priv *priv,
346 unsigned int data_bytes, const void *dout, void *din,
347 unsigned long flags)
348{
349 /*
350 * only support multiple half duplex write transfer + optional
351 * full duplex read/write at the end.
352 */
353 if (flags & SPI_XFER_BEGIN) {
354 /* clear prepends */
355 priv->prepend_cnt = 0;
356 }
357
358 if (din) {
359 /* buffering reads not possible for prepend mode */
360 if (!(flags & SPI_XFER_END)) {
361 debug("unable to buffer reads\n");
362 return HSSPI_XFER_MODE_DUMMYCS;
363 }
364
365 /* check rx size */
366 if (data_bytes > HSSPI_MAX_DATA_SIZE) {
367 debug("max rx bytes exceeded\n");
368 return HSSPI_XFER_MODE_DUMMYCS;
369 }
370 }
371
372 if (dout) {
373 /* check tx size */
374 if (flags & SPI_XFER_END) {
375 if (priv->prepend_cnt + data_bytes > HSSPI_MAX_DATA_SIZE) {
376 debug("max tx bytes exceeded\n");
377 return HSSPI_XFER_MODE_DUMMYCS;
378 }
379 } else {
380 if (priv->prepend_cnt + data_bytes > HSSPI_MAX_PREPEND_SIZE) {
381 debug("max prepend bytes exceeded\n");
382 return HSSPI_XFER_MODE_DUMMYCS;
383 }
384
385 /*
386 * buffer transfer data in the prepend buf in case we have to fall
387 * back to dummy cs mode.
388 */
389 memcpy(&priv->prepend_buf[priv->prepend_cnt], dout, data_bytes);
390 priv->prepend_cnt += data_bytes;
391 }
392 }
393
394 return HSSPI_XFER_MODE_PREPEND;
395}
396
397static int bcm63xx_hsspi_xfer_prepend(struct udevice *dev, unsigned int data_bytes,
398 const void *dout, void *din, unsigned long flags)
399{
400 struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent);
401 struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
402 uint16_t opcode = 0;
403 uint32_t val, offset;
404 int ret;
405
406 if (flags & SPI_XFER_END) {
407 offset = HSSPI_FIFO_BASE + HSSPI_FIFO_OP_SIZE;
408 if (priv->prepend_cnt) {
409 /* copy prepend data */
410 memcpy_toio(priv->regs + offset,
411 priv->prepend_buf, priv->prepend_cnt);
412 }
413
414 if (dout && data_bytes) {
415 /* copy tx data */
416 offset += priv->prepend_cnt;
417 memcpy_toio(priv->regs + offset, dout, data_bytes);
418 }
419
420 bcm63xx_hsspi_activate_cs(priv, plat);
421 if (dout && !din) {
422 /* all half-duplex write. merge to single write */
423 data_bytes += priv->prepend_cnt;
424 opcode = HSSPI_FIFO_OP_CODE_W;
425 priv->prepend_cnt = 0;
426 } else if (!dout && din) {
427 /* half-duplex read with prepend write */
428 opcode = HSSPI_FIFO_OP_CODE_R;
429 } else {
430 /* full duplex read/write */
431 opcode = HSSPI_FIFO_OP_READ_WRITE;
432 }
433
434 /* profile mode */
435 val = SPI_PFL_MODE_FILL_MASK;
436 if (plat->mode & SPI_3WIRE)
437 val |= SPI_PFL_MODE_3WIRE_MASK;
438
439 /* dual mode */
440 if ((opcode == HSSPI_FIFO_OP_CODE_R && (plat->mode & SPI_RX_DUAL)) ||
441 (opcode == HSSPI_FIFO_OP_CODE_W && (plat->mode & SPI_TX_DUAL))) {
442 opcode |= HSSPI_FIFO_OP_MBIT_MASK;
443
444 if (plat->mode & SPI_RX_DUAL) {
445 val |= SPI_PFL_MODE_MDRDSZ_MASK;
446 val |= priv->prepend_cnt << SPI_PFL_MODE_MDRDST_SHIFT;
447 }
448 if (plat->mode & SPI_TX_DUAL) {
449 val |= SPI_PFL_MODE_MDWRSZ_MASK;
450 val |= priv->prepend_cnt << SPI_PFL_MODE_MDWRST_SHIFT;
451 }
452 }
453 val |= (priv->prepend_cnt << SPI_PFL_MODE_PREPCNT_SHIFT);
454 writel(val, priv->regs + SPI_PFL_MODE_REG(plat->cs));
455
456 /* set fifo operation */
457 val = opcode | (data_bytes & HSSPI_FIFO_OP_BYTES_MASK);
458 writew(cpu_to_be16(val),
459 priv->regs + HSSPI_FIFO_OP_REG);
460
461 /* issue the transfer */
462 val = SPI_CMD_OP_START;
463 val |= (plat->cs << SPI_CMD_PFL_SHIFT) &
464 SPI_CMD_PFL_MASK;
465 val |= (plat->cs << SPI_CMD_SLAVE_SHIFT) &
466 SPI_CMD_SLAVE_MASK;
467 writel(val, priv->regs + SPI_CMD_REG);
468
469 /* wait for completion */
470 ret = wait_for_bit_32(priv->regs + SPI_STAT_REG,
471 SPI_STAT_SRCBUSY_MASK, false,
472 1000, false);
473 if (ret) {
474 bcm63xx_hsspi_deactivate_cs(priv);
475 printf("spi polling timeout\n");
476 return ret;
477 }
478
479 /* copy rx data */
480 if (din)
481 memcpy_fromio(din, priv->regs + HSSPI_FIFO_BASE,
482 data_bytes);
483 bcm63xx_hsspi_deactivate_cs(priv);
484 }
485
486 return 0;
487}
488
489static int bcm63xx_hsspi_xfer(struct udevice *dev, unsigned int bitlen,
490 const void *dout, void *din, unsigned long flags)
491{
492 struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent);
493 int ret;
494 u32 data_bytes = bitlen >> 3;
495
496 if (priv->xfer_mode == HSSPI_XFER_MODE_PREPEND) {
497 priv->xfer_mode =
498 bcm63xx_prepare_prepend_transfer(priv, data_bytes, dout, din, flags);
499 }
500
501 /* if not prependable, fall back to dummy cs mode with safe clock */
502 if (priv->xfer_mode == HSSPI_XFER_MODE_DUMMYCS) {
503 /* For pending prepend data from previous transfers, send it first */
504 if (priv->prepend_cnt) {
505 bcm63xx_hsspi_xfer_dummy_cs(dev, priv->prepend_cnt,
506 priv->prepend_buf, NULL,
507 (flags & ~SPI_XFER_END) | SPI_XFER_BEGIN);
508 priv->prepend_cnt = 0;
509 }
510 ret = bcm63xx_hsspi_xfer_dummy_cs(dev, data_bytes, dout, din, flags);
511 } else {
512 ret = bcm63xx_hsspi_xfer_prepend(dev, data_bytes, dout, din, flags);
513 }
514
515 if (flags & SPI_XFER_END)
516 priv->xfer_mode = HSSPI_XFER_MODE_PREPEND;
517
518 return ret;
519}
520
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100521static const struct dm_spi_ops bcm63xx_hsspi_ops = {
522 .cs_info = bcm63xx_hsspi_cs_info,
523 .set_mode = bcm63xx_hsspi_set_mode,
524 .set_speed = bcm63xx_hsspi_set_speed,
525 .xfer = bcm63xx_hsspi_xfer,
526};
527
528static const struct udevice_id bcm63xx_hsspi_ids[] = {
529 { .compatible = "brcm,bcm6328-hsspi", },
William Zhang3f55f272023-06-07 16:37:03 -0700530 { .compatible = "brcm,bcmbca-hsspi-v1.0", },
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100531 { /* sentinel */ }
532};
533
534static int bcm63xx_hsspi_child_pre_probe(struct udevice *dev)
535{
536 struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev->parent);
Simon Glassb75b15b2020-12-03 16:55:23 -0700537 struct dm_spi_slave_plat *plat = dev_get_parent_plat(dev);
William Zhang9a0ff5d2023-06-07 16:37:04 -0700538 struct spi_slave *slave = dev_get_parent_priv(dev);
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100539
540 /* check cs */
541 if (plat->cs >= priv->num_cs) {
542 printf("no cs %u\n", plat->cs);
543 return -ENODEV;
544 }
545
546 /* cs polarity */
547 if (plat->mode & SPI_CS_HIGH)
548 priv->cs_pols |= BIT(plat->cs);
549 else
550 priv->cs_pols &= ~BIT(plat->cs);
551
William Zhang9a0ff5d2023-06-07 16:37:04 -0700552 /*
553 * set the max read/write size to make sure each xfer are within the
554 * prepend limit
555 */
556 slave->max_read_size = HSSPI_MAX_DATA_SIZE;
557 slave->max_write_size = HSSPI_MAX_DATA_SIZE;
558
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100559 return 0;
560}
561
562static int bcm63xx_hsspi_probe(struct udevice *dev)
563{
564 struct bcm63xx_hsspi_priv *priv = dev_get_priv(dev);
565 struct reset_ctl rst_ctl;
566 struct clk clk;
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100567 int ret;
568
Álvaro Fernández Rojas25fb8aa2018-03-22 19:39:37 +0100569 priv->regs = dev_remap_addr(dev);
570 if (!priv->regs)
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100571 return -EINVAL;
572
Álvaro Fernández Rojas25fb8aa2018-03-22 19:39:37 +0100573 priv->num_cs = dev_read_u32_default(dev, "num-cs", 8);
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100574
575 /* enable clock */
576 ret = clk_get_by_name(dev, "hsspi", &clk);
577 if (ret < 0)
578 return ret;
579
580 ret = clk_enable(&clk);
Kursad Oneye31d6f12019-08-14 15:18:35 +0200581 if (ret < 0 && ret != -ENOSYS)
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100582 return ret;
583
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100584 /* get clock rate */
585 ret = clk_get_by_name(dev, "pll", &clk);
Kursad Oneye31d6f12019-08-14 15:18:35 +0200586 if (ret < 0 && ret != -ENOSYS)
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100587 return ret;
588
589 priv->clk_rate = clk_get_rate(&clk);
590
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100591 /* perform reset */
592 ret = reset_get_by_index(dev, 0, &rst_ctl);
Kursad Oneye31d6f12019-08-14 15:18:35 +0200593 if (ret >= 0) {
594 ret = reset_deassert(&rst_ctl);
595 if (ret < 0)
596 return ret;
597 }
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100598
599 ret = reset_free(&rst_ctl);
600 if (ret < 0)
601 return ret;
602
603 /* initialize hardware */
Kursad Oney711c7302019-08-14 15:18:34 +0200604 writel(0, priv->regs + SPI_IR_MASK_REG);
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100605
606 /* clear pending interrupts */
Kursad Oney711c7302019-08-14 15:18:34 +0200607 writel(SPI_IR_CLEAR_ALL, priv->regs + SPI_IR_STAT_REG);
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100608
609 /* enable clk gate */
Kursad Oney711c7302019-08-14 15:18:34 +0200610 setbits_32(priv->regs + SPI_CTL_REG, SPI_CTL_CLK_GATE_MASK);
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100611
612 /* read default cs polarities */
Kursad Oney711c7302019-08-14 15:18:34 +0200613 priv->cs_pols = readl(priv->regs + SPI_CTL_REG) &
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100614 SPI_CTL_CS_POL_MASK;
615
William Zhang9a0ff5d2023-06-07 16:37:04 -0700616 /* default in prepend mode */
617 priv->xfer_mode = HSSPI_XFER_MODE_PREPEND;
618
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100619 return 0;
620}
621
622U_BOOT_DRIVER(bcm63xx_hsspi) = {
623 .name = "bcm63xx_hsspi",
624 .id = UCLASS_SPI,
625 .of_match = bcm63xx_hsspi_ids,
626 .ops = &bcm63xx_hsspi_ops,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700627 .priv_auto = sizeof(struct bcm63xx_hsspi_priv),
Álvaro Fernández Rojas55d96ec2018-01-20 02:13:38 +0100628 .child_pre_probe = bcm63xx_hsspi_child_pre_probe,
629 .probe = bcm63xx_hsspi_probe,
630};