1bbb6b2f9SEddie James // SPDX-License-Identifier: GPL-2.0-or-later
2bbb6b2f9SEddie James // Copyright (C) IBM Corporation 2020
3bbb6b2f9SEddie James
4bbb6b2f9SEddie James #include <linux/bitfield.h>
5bbb6b2f9SEddie James #include <linux/bits.h>
6bbb6b2f9SEddie James #include <linux/fsi.h>
7bbb6b2f9SEddie James #include <linux/jiffies.h>
8bbb6b2f9SEddie James #include <linux/kernel.h>
9bbb6b2f9SEddie James #include <linux/module.h>
10bbb6b2f9SEddie James #include <linux/of.h>
11bbb6b2f9SEddie James #include <linux/spi/spi.h>
12bbb6b2f9SEddie James
13bbb6b2f9SEddie James #define FSI_ENGID_SPI 0x23
14bbb6b2f9SEddie James #define FSI_MBOX_ROOT_CTRL_8 0x2860
159211a441SEddie James #define FSI_MBOX_ROOT_CTRL_8_SPI_MUX 0xf0000000
16bbb6b2f9SEddie James
17bbb6b2f9SEddie James #define FSI2SPI_DATA0 0x00
18bbb6b2f9SEddie James #define FSI2SPI_DATA1 0x04
19bbb6b2f9SEddie James #define FSI2SPI_CMD 0x08
20bbb6b2f9SEddie James #define FSI2SPI_CMD_WRITE BIT(31)
21bbb6b2f9SEddie James #define FSI2SPI_RESET 0x18
22bbb6b2f9SEddie James #define FSI2SPI_STATUS 0x1c
23bbb6b2f9SEddie James #define FSI2SPI_STATUS_ANY_ERROR BIT(31)
24bbb6b2f9SEddie James #define FSI2SPI_IRQ 0x20
25bbb6b2f9SEddie James
26bbb6b2f9SEddie James #define SPI_FSI_BASE 0x70000
2740308f96SEddie James #define SPI_FSI_TIMEOUT_MS 1000
2834d34a56SEddie James #define SPI_FSI_MAX_RX_SIZE 8
2934d34a56SEddie James #define SPI_FSI_MAX_TX_SIZE 40
30bbb6b2f9SEddie James
31bbb6b2f9SEddie James #define SPI_FSI_ERROR 0x0
32bbb6b2f9SEddie James #define SPI_FSI_COUNTER_CFG 0x1
33bbb6b2f9SEddie James #define SPI_FSI_CFG1 0x2
34bbb6b2f9SEddie James #define SPI_FSI_CLOCK_CFG 0x3
35bbb6b2f9SEddie James #define SPI_FSI_CLOCK_CFG_MM_ENABLE BIT_ULL(32)
36bbb6b2f9SEddie James #define SPI_FSI_CLOCK_CFG_ECC_DISABLE (BIT_ULL(35) | BIT_ULL(33))
37bbb6b2f9SEddie James #define SPI_FSI_CLOCK_CFG_RESET1 (BIT_ULL(36) | BIT_ULL(38))
38bbb6b2f9SEddie James #define SPI_FSI_CLOCK_CFG_RESET2 (BIT_ULL(37) | BIT_ULL(39))
39bbb6b2f9SEddie James #define SPI_FSI_CLOCK_CFG_MODE (BIT_ULL(41) | BIT_ULL(42))
40bbb6b2f9SEddie James #define SPI_FSI_CLOCK_CFG_SCK_RECV_DEL GENMASK_ULL(51, 44)
41bbb6b2f9SEddie James #define SPI_FSI_CLOCK_CFG_SCK_NO_DEL BIT_ULL(51)
42bbb6b2f9SEddie James #define SPI_FSI_CLOCK_CFG_SCK_DIV GENMASK_ULL(63, 52)
43bbb6b2f9SEddie James #define SPI_FSI_MMAP 0x4
44bbb6b2f9SEddie James #define SPI_FSI_DATA_TX 0x5
45bbb6b2f9SEddie James #define SPI_FSI_DATA_RX 0x6
46bbb6b2f9SEddie James #define SPI_FSI_SEQUENCE 0x7
47bbb6b2f9SEddie James #define SPI_FSI_SEQUENCE_STOP 0x00
48bbb6b2f9SEddie James #define SPI_FSI_SEQUENCE_SEL_SLAVE(x) (0x10 | ((x) & 0xf))
49bbb6b2f9SEddie James #define SPI_FSI_SEQUENCE_SHIFT_OUT(x) (0x30 | ((x) & 0xf))
50bbb6b2f9SEddie James #define SPI_FSI_SEQUENCE_SHIFT_IN(x) (0x40 | ((x) & 0xf))
51bbb6b2f9SEddie James #define SPI_FSI_SEQUENCE_COPY_DATA_TX 0xc0
52bbb6b2f9SEddie James #define SPI_FSI_SEQUENCE_BRANCH(x) (0xe0 | ((x) & 0xf))
53bbb6b2f9SEddie James #define SPI_FSI_STATUS 0x8
54bbb6b2f9SEddie James #define SPI_FSI_STATUS_ERROR \
55bbb6b2f9SEddie James (GENMASK_ULL(31, 21) | GENMASK_ULL(15, 12))
56bbb6b2f9SEddie James #define SPI_FSI_STATUS_SEQ_STATE GENMASK_ULL(55, 48)
57bbb6b2f9SEddie James #define SPI_FSI_STATUS_SEQ_STATE_IDLE BIT_ULL(48)
58bbb6b2f9SEddie James #define SPI_FSI_STATUS_TDR_UNDERRUN BIT_ULL(57)
59bbb6b2f9SEddie James #define SPI_FSI_STATUS_TDR_OVERRUN BIT_ULL(58)
60bbb6b2f9SEddie James #define SPI_FSI_STATUS_TDR_FULL BIT_ULL(59)
61bbb6b2f9SEddie James #define SPI_FSI_STATUS_RDR_UNDERRUN BIT_ULL(61)
62bbb6b2f9SEddie James #define SPI_FSI_STATUS_RDR_OVERRUN BIT_ULL(62)
63bbb6b2f9SEddie James #define SPI_FSI_STATUS_RDR_FULL BIT_ULL(63)
64bbb6b2f9SEddie James #define SPI_FSI_STATUS_ANY_ERROR \
657909eebbSBrad Bishop (SPI_FSI_STATUS_ERROR | \
66bbb6b2f9SEddie James SPI_FSI_STATUS_TDR_OVERRUN | SPI_FSI_STATUS_RDR_UNDERRUN | \
67bbb6b2f9SEddie James SPI_FSI_STATUS_RDR_OVERRUN)
68bbb6b2f9SEddie James #define SPI_FSI_PORT_CTRL 0x9
69bbb6b2f9SEddie James
70e954af13SEddie James struct fsi2spi {
71e954af13SEddie James struct fsi_device *fsi; /* FSI2SPI CFAM engine device */
72e954af13SEddie James struct mutex lock; /* lock access to the device */
73e954af13SEddie James };
74e954af13SEddie James
75bbb6b2f9SEddie James struct fsi_spi {
76bbb6b2f9SEddie James struct device *dev; /* SPI controller device */
77e954af13SEddie James struct fsi2spi *bridge; /* FSI2SPI device */
78bbb6b2f9SEddie James u32 base;
79bbb6b2f9SEddie James };
80bbb6b2f9SEddie James
81bbb6b2f9SEddie James struct fsi_spi_sequence {
82bbb6b2f9SEddie James int bit;
83bbb6b2f9SEddie James u64 data;
84bbb6b2f9SEddie James };
85bbb6b2f9SEddie James
fsi_spi_check_mux(struct fsi_device * fsi,struct device * dev)869211a441SEddie James static int fsi_spi_check_mux(struct fsi_device *fsi, struct device *dev)
879211a441SEddie James {
889211a441SEddie James int rc;
899211a441SEddie James u32 root_ctrl_8;
909211a441SEddie James __be32 root_ctrl_8_be;
919211a441SEddie James
929211a441SEddie James rc = fsi_slave_read(fsi->slave, FSI_MBOX_ROOT_CTRL_8, &root_ctrl_8_be,
939211a441SEddie James sizeof(root_ctrl_8_be));
949211a441SEddie James if (rc)
959211a441SEddie James return rc;
969211a441SEddie James
979211a441SEddie James root_ctrl_8 = be32_to_cpu(root_ctrl_8_be);
989211a441SEddie James dev_dbg(dev, "Root control register 8: %08x\n", root_ctrl_8);
999211a441SEddie James if ((root_ctrl_8 & FSI_MBOX_ROOT_CTRL_8_SPI_MUX) ==
1009211a441SEddie James FSI_MBOX_ROOT_CTRL_8_SPI_MUX)
1019211a441SEddie James return 0;
1029211a441SEddie James
1039211a441SEddie James return -ENOLINK;
1049211a441SEddie James }
1059211a441SEddie James
fsi_spi_check_status(struct fsi_spi * ctx)106bbb6b2f9SEddie James static int fsi_spi_check_status(struct fsi_spi *ctx)
107bbb6b2f9SEddie James {
108bbb6b2f9SEddie James int rc;
109bbb6b2f9SEddie James u32 sts;
110bbb6b2f9SEddie James __be32 sts_be;
111bbb6b2f9SEddie James
112e954af13SEddie James rc = fsi_device_read(ctx->bridge->fsi, FSI2SPI_STATUS, &sts_be,
113bbb6b2f9SEddie James sizeof(sts_be));
114bbb6b2f9SEddie James if (rc)
115bbb6b2f9SEddie James return rc;
116bbb6b2f9SEddie James
117bbb6b2f9SEddie James sts = be32_to_cpu(sts_be);
118bbb6b2f9SEddie James if (sts & FSI2SPI_STATUS_ANY_ERROR) {
119bbb6b2f9SEddie James dev_err(ctx->dev, "Error with FSI2SPI interface: %08x.\n", sts);
120bbb6b2f9SEddie James return -EIO;
121bbb6b2f9SEddie James }
122bbb6b2f9SEddie James
123bbb6b2f9SEddie James return 0;
124bbb6b2f9SEddie James }
125bbb6b2f9SEddie James
fsi_spi_read_reg(struct fsi_spi * ctx,u32 offset,u64 * value)126bbb6b2f9SEddie James static int fsi_spi_read_reg(struct fsi_spi *ctx, u32 offset, u64 *value)
127bbb6b2f9SEddie James {
128e954af13SEddie James int rc = 0;
129bbb6b2f9SEddie James __be32 cmd_be;
130bbb6b2f9SEddie James __be32 data_be;
131bbb6b2f9SEddie James u32 cmd = offset + ctx->base;
132e954af13SEddie James struct fsi2spi *bridge = ctx->bridge;
133bbb6b2f9SEddie James
134bbb6b2f9SEddie James *value = 0ULL;
135bbb6b2f9SEddie James
136bbb6b2f9SEddie James if (cmd & FSI2SPI_CMD_WRITE)
137bbb6b2f9SEddie James return -EINVAL;
138bbb6b2f9SEddie James
139e954af13SEddie James rc = mutex_lock_interruptible(&bridge->lock);
140bbb6b2f9SEddie James if (rc)
141bbb6b2f9SEddie James return rc;
142bbb6b2f9SEddie James
143e954af13SEddie James cmd_be = cpu_to_be32(cmd);
144e954af13SEddie James rc = fsi_device_write(bridge->fsi, FSI2SPI_CMD, &cmd_be,
145e954af13SEddie James sizeof(cmd_be));
146e954af13SEddie James if (rc)
147e954af13SEddie James goto unlock;
148e954af13SEddie James
149bbb6b2f9SEddie James rc = fsi_spi_check_status(ctx);
150bbb6b2f9SEddie James if (rc)
151e954af13SEddie James goto unlock;
152bbb6b2f9SEddie James
153e954af13SEddie James rc = fsi_device_read(bridge->fsi, FSI2SPI_DATA0, &data_be,
154bbb6b2f9SEddie James sizeof(data_be));
155bbb6b2f9SEddie James if (rc)
156e954af13SEddie James goto unlock;
157bbb6b2f9SEddie James
158bbb6b2f9SEddie James *value |= (u64)be32_to_cpu(data_be) << 32;
159bbb6b2f9SEddie James
160e954af13SEddie James rc = fsi_device_read(bridge->fsi, FSI2SPI_DATA1, &data_be,
161bbb6b2f9SEddie James sizeof(data_be));
162bbb6b2f9SEddie James if (rc)
163e954af13SEddie James goto unlock;
164bbb6b2f9SEddie James
165bbb6b2f9SEddie James *value |= (u64)be32_to_cpu(data_be);
166bbb6b2f9SEddie James dev_dbg(ctx->dev, "Read %02x[%016llx].\n", offset, *value);
167bbb6b2f9SEddie James
168e954af13SEddie James unlock:
169e954af13SEddie James mutex_unlock(&bridge->lock);
170e954af13SEddie James return rc;
171bbb6b2f9SEddie James }
172bbb6b2f9SEddie James
fsi_spi_write_reg(struct fsi_spi * ctx,u32 offset,u64 value)173bbb6b2f9SEddie James static int fsi_spi_write_reg(struct fsi_spi *ctx, u32 offset, u64 value)
174bbb6b2f9SEddie James {
175e954af13SEddie James int rc = 0;
176bbb6b2f9SEddie James __be32 cmd_be;
177bbb6b2f9SEddie James __be32 data_be;
178bbb6b2f9SEddie James u32 cmd = offset + ctx->base;
179e954af13SEddie James struct fsi2spi *bridge = ctx->bridge;
180bbb6b2f9SEddie James
181bbb6b2f9SEddie James if (cmd & FSI2SPI_CMD_WRITE)
182bbb6b2f9SEddie James return -EINVAL;
183bbb6b2f9SEddie James
184e954af13SEddie James rc = mutex_lock_interruptible(&bridge->lock);
185e954af13SEddie James if (rc)
186e954af13SEddie James return rc;
187e954af13SEddie James
188bbb6b2f9SEddie James dev_dbg(ctx->dev, "Write %02x[%016llx].\n", offset, value);
189bbb6b2f9SEddie James
190bbb6b2f9SEddie James data_be = cpu_to_be32(upper_32_bits(value));
191e954af13SEddie James rc = fsi_device_write(bridge->fsi, FSI2SPI_DATA0, &data_be,
192bbb6b2f9SEddie James sizeof(data_be));
193bbb6b2f9SEddie James if (rc)
194e954af13SEddie James goto unlock;
195bbb6b2f9SEddie James
196bbb6b2f9SEddie James data_be = cpu_to_be32(lower_32_bits(value));
197e954af13SEddie James rc = fsi_device_write(bridge->fsi, FSI2SPI_DATA1, &data_be,
198bbb6b2f9SEddie James sizeof(data_be));
199bbb6b2f9SEddie James if (rc)
200e954af13SEddie James goto unlock;
201bbb6b2f9SEddie James
202bbb6b2f9SEddie James cmd_be = cpu_to_be32(cmd | FSI2SPI_CMD_WRITE);
203e954af13SEddie James rc = fsi_device_write(bridge->fsi, FSI2SPI_CMD, &cmd_be,
204e954af13SEddie James sizeof(cmd_be));
205bbb6b2f9SEddie James if (rc)
206e954af13SEddie James goto unlock;
207bbb6b2f9SEddie James
208e954af13SEddie James rc = fsi_spi_check_status(ctx);
209e954af13SEddie James
210e954af13SEddie James unlock:
211e954af13SEddie James mutex_unlock(&bridge->lock);
212e954af13SEddie James return rc;
213bbb6b2f9SEddie James }
214bbb6b2f9SEddie James
fsi_spi_data_in(u64 in,u8 * rx,int len)215bbb6b2f9SEddie James static int fsi_spi_data_in(u64 in, u8 *rx, int len)
216bbb6b2f9SEddie James {
217bbb6b2f9SEddie James int i;
218bbb6b2f9SEddie James int num_bytes = min(len, 8);
219bbb6b2f9SEddie James
220bbb6b2f9SEddie James for (i = 0; i < num_bytes; ++i)
221bbb6b2f9SEddie James rx[i] = (u8)(in >> (8 * ((num_bytes - 1) - i)));
222bbb6b2f9SEddie James
223bbb6b2f9SEddie James return num_bytes;
224bbb6b2f9SEddie James }
225bbb6b2f9SEddie James
fsi_spi_data_out(u64 * out,const u8 * tx,int len)226bbb6b2f9SEddie James static int fsi_spi_data_out(u64 *out, const u8 *tx, int len)
227bbb6b2f9SEddie James {
228bbb6b2f9SEddie James int i;
229bbb6b2f9SEddie James int num_bytes = min(len, 8);
230bbb6b2f9SEddie James u8 *out_bytes = (u8 *)out;
231bbb6b2f9SEddie James
232bbb6b2f9SEddie James /* Unused bytes of the tx data should be 0. */
233bbb6b2f9SEddie James *out = 0ULL;
234bbb6b2f9SEddie James
235bbb6b2f9SEddie James for (i = 0; i < num_bytes; ++i)
236bbb6b2f9SEddie James out_bytes[8 - (i + 1)] = tx[i];
237bbb6b2f9SEddie James
238bbb6b2f9SEddie James return num_bytes;
239bbb6b2f9SEddie James }
240bbb6b2f9SEddie James
fsi_spi_reset(struct fsi_spi * ctx)241bbb6b2f9SEddie James static int fsi_spi_reset(struct fsi_spi *ctx)
242bbb6b2f9SEddie James {
243bbb6b2f9SEddie James int rc;
244bbb6b2f9SEddie James
245bbb6b2f9SEddie James dev_dbg(ctx->dev, "Resetting SPI controller.\n");
246bbb6b2f9SEddie James
247bbb6b2f9SEddie James rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG,
248bbb6b2f9SEddie James SPI_FSI_CLOCK_CFG_RESET1);
249bbb6b2f9SEddie James if (rc)
250bbb6b2f9SEddie James return rc;
251bbb6b2f9SEddie James
25249c9fc1dSEddie James rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG,
253bbb6b2f9SEddie James SPI_FSI_CLOCK_CFG_RESET2);
25449c9fc1dSEddie James if (rc)
25549c9fc1dSEddie James return rc;
25649c9fc1dSEddie James
25749c9fc1dSEddie James return fsi_spi_write_reg(ctx, SPI_FSI_STATUS, 0ULL);
258bbb6b2f9SEddie James }
259bbb6b2f9SEddie James
fsi_spi_status(struct fsi_spi * ctx,u64 * status,const char * dir)26048a78c66SEddie James static int fsi_spi_status(struct fsi_spi *ctx, u64 *status, const char *dir)
26148a78c66SEddie James {
26248a78c66SEddie James int rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, status);
26348a78c66SEddie James
26448a78c66SEddie James if (rc)
26548a78c66SEddie James return rc;
26648a78c66SEddie James
26748a78c66SEddie James if (*status & SPI_FSI_STATUS_ANY_ERROR) {
268e954af13SEddie James dev_err(ctx->dev, "%s error: %016llx\n", dir, *status);
26948a78c66SEddie James
27048a78c66SEddie James rc = fsi_spi_reset(ctx);
27148a78c66SEddie James if (rc)
27248a78c66SEddie James return rc;
27348a78c66SEddie James
27448a78c66SEddie James return -EREMOTEIO;
27548a78c66SEddie James }
27648a78c66SEddie James
27748a78c66SEddie James return 0;
27848a78c66SEddie James }
27948a78c66SEddie James
fsi_spi_sequence_add(struct fsi_spi_sequence * seq,u8 val)28034d34a56SEddie James static void fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val)
281bbb6b2f9SEddie James {
282bbb6b2f9SEddie James /*
283bbb6b2f9SEddie James * Add the next byte of instruction to the 8-byte sequence register.
284bbb6b2f9SEddie James * Then decrement the counter so that the next instruction will go in
28549c9fc1dSEddie James * the right place. Return the index of the slot we just filled in the
28649c9fc1dSEddie James * sequence register.
287bbb6b2f9SEddie James */
288bbb6b2f9SEddie James seq->data |= (u64)val << seq->bit;
289bbb6b2f9SEddie James seq->bit -= 8;
290bbb6b2f9SEddie James }
291bbb6b2f9SEddie James
fsi_spi_sequence_init(struct fsi_spi_sequence * seq)292bbb6b2f9SEddie James static void fsi_spi_sequence_init(struct fsi_spi_sequence *seq)
293bbb6b2f9SEddie James {
294bbb6b2f9SEddie James seq->bit = 56;
295bbb6b2f9SEddie James seq->data = 0ULL;
296bbb6b2f9SEddie James }
297bbb6b2f9SEddie James
fsi_spi_transfer_data(struct fsi_spi * ctx,struct spi_transfer * transfer)298bbb6b2f9SEddie James static int fsi_spi_transfer_data(struct fsi_spi *ctx,
299bbb6b2f9SEddie James struct spi_transfer *transfer)
300bbb6b2f9SEddie James {
30140308f96SEddie James int loops;
302bbb6b2f9SEddie James int rc = 0;
30389b35e3fSEddie James unsigned long end;
304bbb6b2f9SEddie James u64 status = 0ULL;
305bbb6b2f9SEddie James
306bbb6b2f9SEddie James if (transfer->tx_buf) {
307bbb6b2f9SEddie James int nb;
308bbb6b2f9SEddie James int sent = 0;
309bbb6b2f9SEddie James u64 out = 0ULL;
310bbb6b2f9SEddie James const u8 *tx = transfer->tx_buf;
311bbb6b2f9SEddie James
312bbb6b2f9SEddie James while (transfer->len > sent) {
313bbb6b2f9SEddie James nb = fsi_spi_data_out(&out, &tx[sent],
314bbb6b2f9SEddie James (int)transfer->len - sent);
315bbb6b2f9SEddie James
316bbb6b2f9SEddie James rc = fsi_spi_write_reg(ctx, SPI_FSI_DATA_TX, out);
317bbb6b2f9SEddie James if (rc)
318bbb6b2f9SEddie James return rc;
319bbb6b2f9SEddie James
32040308f96SEddie James loops = 0;
32140308f96SEddie James end = jiffies + msecs_to_jiffies(SPI_FSI_TIMEOUT_MS);
322bbb6b2f9SEddie James do {
32340308f96SEddie James if (loops++ && time_after(jiffies, end))
32461bf40efSEddie James return -ETIMEDOUT;
32561bf40efSEddie James
32648a78c66SEddie James rc = fsi_spi_status(ctx, &status, "TX");
327bbb6b2f9SEddie James if (rc)
328bbb6b2f9SEddie James return rc;
329bbb6b2f9SEddie James } while (status & SPI_FSI_STATUS_TDR_FULL);
330bbb6b2f9SEddie James
331bbb6b2f9SEddie James sent += nb;
332bbb6b2f9SEddie James }
333bbb6b2f9SEddie James } else if (transfer->rx_buf) {
334bbb6b2f9SEddie James int recv = 0;
335bbb6b2f9SEddie James u64 in = 0ULL;
336bbb6b2f9SEddie James u8 *rx = transfer->rx_buf;
337bbb6b2f9SEddie James
338bbb6b2f9SEddie James while (transfer->len > recv) {
33940308f96SEddie James loops = 0;
34040308f96SEddie James end = jiffies + msecs_to_jiffies(SPI_FSI_TIMEOUT_MS);
341bbb6b2f9SEddie James do {
34240308f96SEddie James if (loops++ && time_after(jiffies, end))
34361bf40efSEddie James return -ETIMEDOUT;
34461bf40efSEddie James
34548a78c66SEddie James rc = fsi_spi_status(ctx, &status, "RX");
346bbb6b2f9SEddie James if (rc)
347bbb6b2f9SEddie James return rc;
348bbb6b2f9SEddie James } while (!(status & SPI_FSI_STATUS_RDR_FULL));
349bbb6b2f9SEddie James
350bbb6b2f9SEddie James rc = fsi_spi_read_reg(ctx, SPI_FSI_DATA_RX, &in);
351bbb6b2f9SEddie James if (rc)
352bbb6b2f9SEddie James return rc;
353bbb6b2f9SEddie James
354bbb6b2f9SEddie James recv += fsi_spi_data_in(in, &rx[recv],
355bbb6b2f9SEddie James (int)transfer->len - recv);
356bbb6b2f9SEddie James }
357bbb6b2f9SEddie James }
358bbb6b2f9SEddie James
359bbb6b2f9SEddie James return 0;
360bbb6b2f9SEddie James }
361bbb6b2f9SEddie James
fsi_spi_transfer_init(struct fsi_spi * ctx)362bbb6b2f9SEddie James static int fsi_spi_transfer_init(struct fsi_spi *ctx)
363bbb6b2f9SEddie James {
36440308f96SEddie James int loops = 0;
365bbb6b2f9SEddie James int rc;
366bbb6b2f9SEddie James bool reset = false;
367bbb6b2f9SEddie James unsigned long end;
368bbb6b2f9SEddie James u64 seq_state;
369bbb6b2f9SEddie James u64 clock_cfg = 0ULL;
370bbb6b2f9SEddie James u64 status = 0ULL;
371bbb6b2f9SEddie James u64 wanted_clock_cfg = SPI_FSI_CLOCK_CFG_ECC_DISABLE |
372bbb6b2f9SEddie James SPI_FSI_CLOCK_CFG_SCK_NO_DEL |
3730b546bbeSBrad Bishop FIELD_PREP(SPI_FSI_CLOCK_CFG_SCK_DIV, 19);
374bbb6b2f9SEddie James
37540308f96SEddie James end = jiffies + msecs_to_jiffies(SPI_FSI_TIMEOUT_MS);
376bbb6b2f9SEddie James do {
37740308f96SEddie James if (loops++ && time_after(jiffies, end))
378bbb6b2f9SEddie James return -ETIMEDOUT;
379bbb6b2f9SEddie James
380bbb6b2f9SEddie James rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, &status);
381bbb6b2f9SEddie James if (rc)
382bbb6b2f9SEddie James return rc;
383bbb6b2f9SEddie James
384bbb6b2f9SEddie James seq_state = status & SPI_FSI_STATUS_SEQ_STATE;
385bbb6b2f9SEddie James
386bbb6b2f9SEddie James if (status & (SPI_FSI_STATUS_ANY_ERROR |
387bbb6b2f9SEddie James SPI_FSI_STATUS_TDR_FULL |
388bbb6b2f9SEddie James SPI_FSI_STATUS_RDR_FULL)) {
38948a78c66SEddie James if (reset) {
39048a78c66SEddie James dev_err(ctx->dev,
39148a78c66SEddie James "Initialization error: %08llx\n",
39248a78c66SEddie James status);
393bbb6b2f9SEddie James return -EIO;
39448a78c66SEddie James }
395bbb6b2f9SEddie James
396bbb6b2f9SEddie James rc = fsi_spi_reset(ctx);
397bbb6b2f9SEddie James if (rc)
398bbb6b2f9SEddie James return rc;
399bbb6b2f9SEddie James
400bbb6b2f9SEddie James reset = true;
401bbb6b2f9SEddie James continue;
402bbb6b2f9SEddie James }
403bbb6b2f9SEddie James } while (seq_state && (seq_state != SPI_FSI_STATUS_SEQ_STATE_IDLE));
404bbb6b2f9SEddie James
40534d34a56SEddie James rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, 0ULL);
40634d34a56SEddie James if (rc)
40734d34a56SEddie James return rc;
40834d34a56SEddie James
409bbb6b2f9SEddie James rc = fsi_spi_read_reg(ctx, SPI_FSI_CLOCK_CFG, &clock_cfg);
410bbb6b2f9SEddie James if (rc)
411bbb6b2f9SEddie James return rc;
412bbb6b2f9SEddie James
413bbb6b2f9SEddie James if ((clock_cfg & (SPI_FSI_CLOCK_CFG_MM_ENABLE |
414bbb6b2f9SEddie James SPI_FSI_CLOCK_CFG_ECC_DISABLE |
415bbb6b2f9SEddie James SPI_FSI_CLOCK_CFG_MODE |
416bbb6b2f9SEddie James SPI_FSI_CLOCK_CFG_SCK_RECV_DEL |
417bbb6b2f9SEddie James SPI_FSI_CLOCK_CFG_SCK_DIV)) != wanted_clock_cfg)
418bbb6b2f9SEddie James rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG,
419bbb6b2f9SEddie James wanted_clock_cfg);
420bbb6b2f9SEddie James
421bbb6b2f9SEddie James return rc;
422bbb6b2f9SEddie James }
423bbb6b2f9SEddie James
fsi_spi_transfer_one_message(struct spi_controller * ctlr,struct spi_message * mesg)424bbb6b2f9SEddie James static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
425bbb6b2f9SEddie James struct spi_message *mesg)
426bbb6b2f9SEddie James {
4279211a441SEddie James int rc;
4289e264f3fSAmit Kumar Mahapatra via Alsa-devel u8 seq_slave = SPI_FSI_SEQUENCE_SEL_SLAVE(spi_get_chipselect(mesg->spi, 0) + 1);
42934d34a56SEddie James unsigned int len;
430bbb6b2f9SEddie James struct spi_transfer *transfer;
431bbb6b2f9SEddie James struct fsi_spi *ctx = spi_controller_get_devdata(ctlr);
432bbb6b2f9SEddie James
433e954af13SEddie James rc = fsi_spi_check_mux(ctx->bridge->fsi, ctx->dev);
4349211a441SEddie James if (rc)
435ee4ad5d0SEddie James goto error;
4369211a441SEddie James
437bbb6b2f9SEddie James list_for_each_entry(transfer, &mesg->transfers, transfer_list) {
438bbb6b2f9SEddie James struct fsi_spi_sequence seq;
439bbb6b2f9SEddie James struct spi_transfer *next = NULL;
440bbb6b2f9SEddie James
441bbb6b2f9SEddie James /* Sequencer must do shift out (tx) first. */
44234d34a56SEddie James if (!transfer->tx_buf || transfer->len > SPI_FSI_MAX_TX_SIZE) {
443bbb6b2f9SEddie James rc = -EINVAL;
444bbb6b2f9SEddie James goto error;
445bbb6b2f9SEddie James }
446bbb6b2f9SEddie James
447bbb6b2f9SEddie James dev_dbg(ctx->dev, "Start tx of %d bytes.\n", transfer->len);
448bbb6b2f9SEddie James
449bbb6b2f9SEddie James rc = fsi_spi_transfer_init(ctx);
450bbb6b2f9SEddie James if (rc < 0)
451bbb6b2f9SEddie James goto error;
452bbb6b2f9SEddie James
453bbb6b2f9SEddie James fsi_spi_sequence_init(&seq);
454bbb6b2f9SEddie James fsi_spi_sequence_add(&seq, seq_slave);
455bbb6b2f9SEddie James
45634d34a56SEddie James len = transfer->len;
45734d34a56SEddie James while (len > 8) {
45834d34a56SEddie James fsi_spi_sequence_add(&seq,
45934d34a56SEddie James SPI_FSI_SEQUENCE_SHIFT_OUT(8));
46034d34a56SEddie James len -= 8;
46134d34a56SEddie James }
46234d34a56SEddie James fsi_spi_sequence_add(&seq, SPI_FSI_SEQUENCE_SHIFT_OUT(len));
463bbb6b2f9SEddie James
464bbb6b2f9SEddie James if (!list_is_last(&transfer->transfer_list,
465bbb6b2f9SEddie James &mesg->transfers)) {
466bbb6b2f9SEddie James next = list_next_entry(transfer, transfer_list);
467bbb6b2f9SEddie James
468bbb6b2f9SEddie James /* Sequencer can only do shift in (rx) after tx. */
469bbb6b2f9SEddie James if (next->rx_buf) {
47034d34a56SEddie James u8 shift;
47134d34a56SEddie James
47234d34a56SEddie James if (next->len > SPI_FSI_MAX_RX_SIZE) {
473bbb6b2f9SEddie James rc = -EINVAL;
474bbb6b2f9SEddie James goto error;
475bbb6b2f9SEddie James }
476bbb6b2f9SEddie James
477bbb6b2f9SEddie James dev_dbg(ctx->dev, "Sequence rx of %d bytes.\n",
478bbb6b2f9SEddie James next->len);
479bbb6b2f9SEddie James
48034d34a56SEddie James shift = SPI_FSI_SEQUENCE_SHIFT_IN(next->len);
48134d34a56SEddie James fsi_spi_sequence_add(&seq, shift);
482bbb6b2f9SEddie James } else {
483bbb6b2f9SEddie James next = NULL;
484bbb6b2f9SEddie James }
485bbb6b2f9SEddie James }
486bbb6b2f9SEddie James
487bbb6b2f9SEddie James fsi_spi_sequence_add(&seq, SPI_FSI_SEQUENCE_SEL_SLAVE(0));
488bbb6b2f9SEddie James
489bbb6b2f9SEddie James rc = fsi_spi_write_reg(ctx, SPI_FSI_SEQUENCE, seq.data);
490bbb6b2f9SEddie James if (rc)
491bbb6b2f9SEddie James goto error;
492bbb6b2f9SEddie James
493bbb6b2f9SEddie James rc = fsi_spi_transfer_data(ctx, transfer);
494bbb6b2f9SEddie James if (rc)
495bbb6b2f9SEddie James goto error;
496bbb6b2f9SEddie James
497bbb6b2f9SEddie James if (next) {
498bbb6b2f9SEddie James rc = fsi_spi_transfer_data(ctx, next);
499bbb6b2f9SEddie James if (rc)
500bbb6b2f9SEddie James goto error;
501bbb6b2f9SEddie James
502bbb6b2f9SEddie James transfer = next;
503bbb6b2f9SEddie James }
504bbb6b2f9SEddie James }
505bbb6b2f9SEddie James
506bbb6b2f9SEddie James error:
507bbb6b2f9SEddie James mesg->status = rc;
508bbb6b2f9SEddie James spi_finalize_current_message(ctlr);
509bbb6b2f9SEddie James
510bbb6b2f9SEddie James return rc;
511bbb6b2f9SEddie James }
512bbb6b2f9SEddie James
fsi_spi_max_transfer_size(struct spi_device * spi)513bbb6b2f9SEddie James static size_t fsi_spi_max_transfer_size(struct spi_device *spi)
514bbb6b2f9SEddie James {
51534d34a56SEddie James return SPI_FSI_MAX_RX_SIZE;
516bbb6b2f9SEddie James }
517bbb6b2f9SEddie James
fsi_spi_probe(struct device * dev)518bbb6b2f9SEddie James static int fsi_spi_probe(struct device *dev)
519bbb6b2f9SEddie James {
520bbb6b2f9SEddie James int rc;
521bbb6b2f9SEddie James struct device_node *np;
522bbb6b2f9SEddie James int num_controllers_registered = 0;
523e954af13SEddie James struct fsi2spi *bridge;
524bbb6b2f9SEddie James struct fsi_device *fsi = to_fsi_dev(dev);
525bbb6b2f9SEddie James
5269211a441SEddie James rc = fsi_spi_check_mux(fsi, dev);
527bbb6b2f9SEddie James if (rc)
528bbb6b2f9SEddie James return -ENODEV;
529bbb6b2f9SEddie James
530e954af13SEddie James bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL);
531e954af13SEddie James if (!bridge)
532e954af13SEddie James return -ENOMEM;
533e954af13SEddie James
534e954af13SEddie James bridge->fsi = fsi;
535e954af13SEddie James mutex_init(&bridge->lock);
536e954af13SEddie James
537bbb6b2f9SEddie James for_each_available_child_of_node(dev->of_node, np) {
538bbb6b2f9SEddie James u32 base;
539bbb6b2f9SEddie James struct fsi_spi *ctx;
540bbb6b2f9SEddie James struct spi_controller *ctlr;
541bbb6b2f9SEddie James
542bbb6b2f9SEddie James if (of_property_read_u32(np, "reg", &base))
543bbb6b2f9SEddie James continue;
544bbb6b2f9SEddie James
545*d40f10d0SYang Yingliang ctlr = spi_alloc_host(dev, sizeof(*ctx));
54624b5515aSChristophe JAILLET if (!ctlr) {
54724b5515aSChristophe JAILLET of_node_put(np);
548bbb6b2f9SEddie James break;
54924b5515aSChristophe JAILLET }
550bbb6b2f9SEddie James
551bbb6b2f9SEddie James ctlr->dev.of_node = np;
552bbb6b2f9SEddie James ctlr->num_chipselect = of_get_available_child_count(np) ?: 1;
553bbb6b2f9SEddie James ctlr->flags = SPI_CONTROLLER_HALF_DUPLEX;
554bbb6b2f9SEddie James ctlr->max_transfer_size = fsi_spi_max_transfer_size;
555bbb6b2f9SEddie James ctlr->transfer_one_message = fsi_spi_transfer_one_message;
556bbb6b2f9SEddie James
557bbb6b2f9SEddie James ctx = spi_controller_get_devdata(ctlr);
558bbb6b2f9SEddie James ctx->dev = &ctlr->dev;
559e954af13SEddie James ctx->bridge = bridge;
560bbb6b2f9SEddie James ctx->base = base + SPI_FSI_BASE;
561bbb6b2f9SEddie James
562bbb6b2f9SEddie James rc = devm_spi_register_controller(dev, ctlr);
563bbb6b2f9SEddie James if (rc)
564bbb6b2f9SEddie James spi_controller_put(ctlr);
565bbb6b2f9SEddie James else
566bbb6b2f9SEddie James num_controllers_registered++;
567bbb6b2f9SEddie James }
568bbb6b2f9SEddie James
569bbb6b2f9SEddie James if (!num_controllers_registered)
570bbb6b2f9SEddie James return -ENODEV;
571bbb6b2f9SEddie James
572bbb6b2f9SEddie James return 0;
573bbb6b2f9SEddie James }
574bbb6b2f9SEddie James
575bbb6b2f9SEddie James static const struct fsi_device_id fsi_spi_ids[] = {
576bbb6b2f9SEddie James { FSI_ENGID_SPI, FSI_VERSION_ANY },
577bbb6b2f9SEddie James { }
578bbb6b2f9SEddie James };
579bbb6b2f9SEddie James MODULE_DEVICE_TABLE(fsi, fsi_spi_ids);
580bbb6b2f9SEddie James
581bbb6b2f9SEddie James static struct fsi_driver fsi_spi_driver = {
582bbb6b2f9SEddie James .id_table = fsi_spi_ids,
583bbb6b2f9SEddie James .drv = {
584bbb6b2f9SEddie James .name = "spi-fsi",
585bbb6b2f9SEddie James .bus = &fsi_bus_type,
586bbb6b2f9SEddie James .probe = fsi_spi_probe,
587bbb6b2f9SEddie James },
588bbb6b2f9SEddie James };
589bbb6b2f9SEddie James module_fsi_driver(fsi_spi_driver);
590bbb6b2f9SEddie James
591bbb6b2f9SEddie James MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
592bbb6b2f9SEddie James MODULE_DESCRIPTION("FSI attached SPI controller");
593bbb6b2f9SEddie James MODULE_LICENSE("GPL");
594