162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci// Copyright (C) IBM Corporation 2020
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <linux/bitfield.h>
562306a36Sopenharmony_ci#include <linux/bits.h>
662306a36Sopenharmony_ci#include <linux/fsi.h>
762306a36Sopenharmony_ci#include <linux/jiffies.h>
862306a36Sopenharmony_ci#include <linux/kernel.h>
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/of.h>
1162306a36Sopenharmony_ci#include <linux/spi/spi.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#define FSI_ENGID_SPI			0x23
1462306a36Sopenharmony_ci#define FSI_MBOX_ROOT_CTRL_8		0x2860
1562306a36Sopenharmony_ci#define  FSI_MBOX_ROOT_CTRL_8_SPI_MUX	 0xf0000000
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define FSI2SPI_DATA0			0x00
1862306a36Sopenharmony_ci#define FSI2SPI_DATA1			0x04
1962306a36Sopenharmony_ci#define FSI2SPI_CMD			0x08
2062306a36Sopenharmony_ci#define  FSI2SPI_CMD_WRITE		 BIT(31)
2162306a36Sopenharmony_ci#define FSI2SPI_RESET			0x18
2262306a36Sopenharmony_ci#define FSI2SPI_STATUS			0x1c
2362306a36Sopenharmony_ci#define  FSI2SPI_STATUS_ANY_ERROR	 BIT(31)
2462306a36Sopenharmony_ci#define FSI2SPI_IRQ			0x20
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define SPI_FSI_BASE			0x70000
2762306a36Sopenharmony_ci#define SPI_FSI_TIMEOUT_MS		1000
2862306a36Sopenharmony_ci#define SPI_FSI_MAX_RX_SIZE		8
2962306a36Sopenharmony_ci#define SPI_FSI_MAX_TX_SIZE		40
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#define SPI_FSI_ERROR			0x0
3262306a36Sopenharmony_ci#define SPI_FSI_COUNTER_CFG		0x1
3362306a36Sopenharmony_ci#define SPI_FSI_CFG1			0x2
3462306a36Sopenharmony_ci#define SPI_FSI_CLOCK_CFG		0x3
3562306a36Sopenharmony_ci#define  SPI_FSI_CLOCK_CFG_MM_ENABLE	 BIT_ULL(32)
3662306a36Sopenharmony_ci#define  SPI_FSI_CLOCK_CFG_ECC_DISABLE	 (BIT_ULL(35) | BIT_ULL(33))
3762306a36Sopenharmony_ci#define  SPI_FSI_CLOCK_CFG_RESET1	 (BIT_ULL(36) | BIT_ULL(38))
3862306a36Sopenharmony_ci#define  SPI_FSI_CLOCK_CFG_RESET2	 (BIT_ULL(37) | BIT_ULL(39))
3962306a36Sopenharmony_ci#define  SPI_FSI_CLOCK_CFG_MODE		 (BIT_ULL(41) | BIT_ULL(42))
4062306a36Sopenharmony_ci#define  SPI_FSI_CLOCK_CFG_SCK_RECV_DEL	 GENMASK_ULL(51, 44)
4162306a36Sopenharmony_ci#define   SPI_FSI_CLOCK_CFG_SCK_NO_DEL	  BIT_ULL(51)
4262306a36Sopenharmony_ci#define  SPI_FSI_CLOCK_CFG_SCK_DIV	 GENMASK_ULL(63, 52)
4362306a36Sopenharmony_ci#define SPI_FSI_MMAP			0x4
4462306a36Sopenharmony_ci#define SPI_FSI_DATA_TX			0x5
4562306a36Sopenharmony_ci#define SPI_FSI_DATA_RX			0x6
4662306a36Sopenharmony_ci#define SPI_FSI_SEQUENCE		0x7
4762306a36Sopenharmony_ci#define  SPI_FSI_SEQUENCE_STOP		 0x00
4862306a36Sopenharmony_ci#define  SPI_FSI_SEQUENCE_SEL_SLAVE(x)	 (0x10 | ((x) & 0xf))
4962306a36Sopenharmony_ci#define  SPI_FSI_SEQUENCE_SHIFT_OUT(x)	 (0x30 | ((x) & 0xf))
5062306a36Sopenharmony_ci#define  SPI_FSI_SEQUENCE_SHIFT_IN(x)	 (0x40 | ((x) & 0xf))
5162306a36Sopenharmony_ci#define  SPI_FSI_SEQUENCE_COPY_DATA_TX	 0xc0
5262306a36Sopenharmony_ci#define  SPI_FSI_SEQUENCE_BRANCH(x)	 (0xe0 | ((x) & 0xf))
5362306a36Sopenharmony_ci#define SPI_FSI_STATUS			0x8
5462306a36Sopenharmony_ci#define  SPI_FSI_STATUS_ERROR		 \
5562306a36Sopenharmony_ci	(GENMASK_ULL(31, 21) | GENMASK_ULL(15, 12))
5662306a36Sopenharmony_ci#define  SPI_FSI_STATUS_SEQ_STATE	 GENMASK_ULL(55, 48)
5762306a36Sopenharmony_ci#define   SPI_FSI_STATUS_SEQ_STATE_IDLE	  BIT_ULL(48)
5862306a36Sopenharmony_ci#define  SPI_FSI_STATUS_TDR_UNDERRUN	 BIT_ULL(57)
5962306a36Sopenharmony_ci#define  SPI_FSI_STATUS_TDR_OVERRUN	 BIT_ULL(58)
6062306a36Sopenharmony_ci#define  SPI_FSI_STATUS_TDR_FULL	 BIT_ULL(59)
6162306a36Sopenharmony_ci#define  SPI_FSI_STATUS_RDR_UNDERRUN	 BIT_ULL(61)
6262306a36Sopenharmony_ci#define  SPI_FSI_STATUS_RDR_OVERRUN	 BIT_ULL(62)
6362306a36Sopenharmony_ci#define  SPI_FSI_STATUS_RDR_FULL	 BIT_ULL(63)
6462306a36Sopenharmony_ci#define  SPI_FSI_STATUS_ANY_ERROR	 \
6562306a36Sopenharmony_ci	(SPI_FSI_STATUS_ERROR | \
6662306a36Sopenharmony_ci	 SPI_FSI_STATUS_TDR_OVERRUN | SPI_FSI_STATUS_RDR_UNDERRUN | \
6762306a36Sopenharmony_ci	 SPI_FSI_STATUS_RDR_OVERRUN)
6862306a36Sopenharmony_ci#define SPI_FSI_PORT_CTRL		0x9
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistruct fsi2spi {
7162306a36Sopenharmony_ci	struct fsi_device *fsi; /* FSI2SPI CFAM engine device */
7262306a36Sopenharmony_ci	struct mutex lock; /* lock access to the device */
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistruct fsi_spi {
7662306a36Sopenharmony_ci	struct device *dev;	/* SPI controller device */
7762306a36Sopenharmony_ci	struct fsi2spi *bridge; /* FSI2SPI device */
7862306a36Sopenharmony_ci	u32 base;
7962306a36Sopenharmony_ci};
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistruct fsi_spi_sequence {
8262306a36Sopenharmony_ci	int bit;
8362306a36Sopenharmony_ci	u64 data;
8462306a36Sopenharmony_ci};
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int fsi_spi_check_mux(struct fsi_device *fsi, struct device *dev)
8762306a36Sopenharmony_ci{
8862306a36Sopenharmony_ci	int rc;
8962306a36Sopenharmony_ci	u32 root_ctrl_8;
9062306a36Sopenharmony_ci	__be32 root_ctrl_8_be;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	rc = fsi_slave_read(fsi->slave, FSI_MBOX_ROOT_CTRL_8, &root_ctrl_8_be,
9362306a36Sopenharmony_ci			    sizeof(root_ctrl_8_be));
9462306a36Sopenharmony_ci	if (rc)
9562306a36Sopenharmony_ci		return rc;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	root_ctrl_8 = be32_to_cpu(root_ctrl_8_be);
9862306a36Sopenharmony_ci	dev_dbg(dev, "Root control register 8: %08x\n", root_ctrl_8);
9962306a36Sopenharmony_ci	if ((root_ctrl_8 & FSI_MBOX_ROOT_CTRL_8_SPI_MUX) ==
10062306a36Sopenharmony_ci	     FSI_MBOX_ROOT_CTRL_8_SPI_MUX)
10162306a36Sopenharmony_ci		return 0;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	return -ENOLINK;
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int fsi_spi_check_status(struct fsi_spi *ctx)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	int rc;
10962306a36Sopenharmony_ci	u32 sts;
11062306a36Sopenharmony_ci	__be32 sts_be;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	rc = fsi_device_read(ctx->bridge->fsi, FSI2SPI_STATUS, &sts_be,
11362306a36Sopenharmony_ci			     sizeof(sts_be));
11462306a36Sopenharmony_ci	if (rc)
11562306a36Sopenharmony_ci		return rc;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	sts = be32_to_cpu(sts_be);
11862306a36Sopenharmony_ci	if (sts & FSI2SPI_STATUS_ANY_ERROR) {
11962306a36Sopenharmony_ci		dev_err(ctx->dev, "Error with FSI2SPI interface: %08x.\n", sts);
12062306a36Sopenharmony_ci		return -EIO;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return 0;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int fsi_spi_read_reg(struct fsi_spi *ctx, u32 offset, u64 *value)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	int rc = 0;
12962306a36Sopenharmony_ci	__be32 cmd_be;
13062306a36Sopenharmony_ci	__be32 data_be;
13162306a36Sopenharmony_ci	u32 cmd = offset + ctx->base;
13262306a36Sopenharmony_ci	struct fsi2spi *bridge = ctx->bridge;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	*value = 0ULL;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (cmd & FSI2SPI_CMD_WRITE)
13762306a36Sopenharmony_ci		return -EINVAL;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	rc = mutex_lock_interruptible(&bridge->lock);
14062306a36Sopenharmony_ci	if (rc)
14162306a36Sopenharmony_ci		return rc;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	cmd_be = cpu_to_be32(cmd);
14462306a36Sopenharmony_ci	rc = fsi_device_write(bridge->fsi, FSI2SPI_CMD, &cmd_be,
14562306a36Sopenharmony_ci			      sizeof(cmd_be));
14662306a36Sopenharmony_ci	if (rc)
14762306a36Sopenharmony_ci		goto unlock;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	rc = fsi_spi_check_status(ctx);
15062306a36Sopenharmony_ci	if (rc)
15162306a36Sopenharmony_ci		goto unlock;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	rc = fsi_device_read(bridge->fsi, FSI2SPI_DATA0, &data_be,
15462306a36Sopenharmony_ci			     sizeof(data_be));
15562306a36Sopenharmony_ci	if (rc)
15662306a36Sopenharmony_ci		goto unlock;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	*value |= (u64)be32_to_cpu(data_be) << 32;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	rc = fsi_device_read(bridge->fsi, FSI2SPI_DATA1, &data_be,
16162306a36Sopenharmony_ci			     sizeof(data_be));
16262306a36Sopenharmony_ci	if (rc)
16362306a36Sopenharmony_ci		goto unlock;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	*value |= (u64)be32_to_cpu(data_be);
16662306a36Sopenharmony_ci	dev_dbg(ctx->dev, "Read %02x[%016llx].\n", offset, *value);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ciunlock:
16962306a36Sopenharmony_ci	mutex_unlock(&bridge->lock);
17062306a36Sopenharmony_ci	return rc;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic int fsi_spi_write_reg(struct fsi_spi *ctx, u32 offset, u64 value)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	int rc = 0;
17662306a36Sopenharmony_ci	__be32 cmd_be;
17762306a36Sopenharmony_ci	__be32 data_be;
17862306a36Sopenharmony_ci	u32 cmd = offset + ctx->base;
17962306a36Sopenharmony_ci	struct fsi2spi *bridge = ctx->bridge;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	if (cmd & FSI2SPI_CMD_WRITE)
18262306a36Sopenharmony_ci		return -EINVAL;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	rc = mutex_lock_interruptible(&bridge->lock);
18562306a36Sopenharmony_ci	if (rc)
18662306a36Sopenharmony_ci		return rc;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	dev_dbg(ctx->dev, "Write %02x[%016llx].\n", offset, value);
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	data_be = cpu_to_be32(upper_32_bits(value));
19162306a36Sopenharmony_ci	rc = fsi_device_write(bridge->fsi, FSI2SPI_DATA0, &data_be,
19262306a36Sopenharmony_ci			      sizeof(data_be));
19362306a36Sopenharmony_ci	if (rc)
19462306a36Sopenharmony_ci		goto unlock;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	data_be = cpu_to_be32(lower_32_bits(value));
19762306a36Sopenharmony_ci	rc = fsi_device_write(bridge->fsi, FSI2SPI_DATA1, &data_be,
19862306a36Sopenharmony_ci			      sizeof(data_be));
19962306a36Sopenharmony_ci	if (rc)
20062306a36Sopenharmony_ci		goto unlock;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	cmd_be = cpu_to_be32(cmd | FSI2SPI_CMD_WRITE);
20362306a36Sopenharmony_ci	rc = fsi_device_write(bridge->fsi, FSI2SPI_CMD, &cmd_be,
20462306a36Sopenharmony_ci			      sizeof(cmd_be));
20562306a36Sopenharmony_ci	if (rc)
20662306a36Sopenharmony_ci		goto unlock;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	rc = fsi_spi_check_status(ctx);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ciunlock:
21162306a36Sopenharmony_ci	mutex_unlock(&bridge->lock);
21262306a36Sopenharmony_ci	return rc;
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int fsi_spi_data_in(u64 in, u8 *rx, int len)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	int i;
21862306a36Sopenharmony_ci	int num_bytes = min(len, 8);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	for (i = 0; i < num_bytes; ++i)
22162306a36Sopenharmony_ci		rx[i] = (u8)(in >> (8 * ((num_bytes - 1) - i)));
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	return num_bytes;
22462306a36Sopenharmony_ci}
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int fsi_spi_data_out(u64 *out, const u8 *tx, int len)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	int i;
22962306a36Sopenharmony_ci	int num_bytes = min(len, 8);
23062306a36Sopenharmony_ci	u8 *out_bytes = (u8 *)out;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	/* Unused bytes of the tx data should be 0. */
23362306a36Sopenharmony_ci	*out = 0ULL;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	for (i = 0; i < num_bytes; ++i)
23662306a36Sopenharmony_ci		out_bytes[8 - (i + 1)] = tx[i];
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return num_bytes;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic int fsi_spi_reset(struct fsi_spi *ctx)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	int rc;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	dev_dbg(ctx->dev, "Resetting SPI controller.\n");
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG,
24862306a36Sopenharmony_ci			       SPI_FSI_CLOCK_CFG_RESET1);
24962306a36Sopenharmony_ci	if (rc)
25062306a36Sopenharmony_ci		return rc;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG,
25362306a36Sopenharmony_ci			       SPI_FSI_CLOCK_CFG_RESET2);
25462306a36Sopenharmony_ci	if (rc)
25562306a36Sopenharmony_ci		return rc;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	return fsi_spi_write_reg(ctx, SPI_FSI_STATUS, 0ULL);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_cistatic int fsi_spi_status(struct fsi_spi *ctx, u64 *status, const char *dir)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	int rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, status);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (rc)
26562306a36Sopenharmony_ci		return rc;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (*status & SPI_FSI_STATUS_ANY_ERROR) {
26862306a36Sopenharmony_ci		dev_err(ctx->dev, "%s error: %016llx\n", dir, *status);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		rc = fsi_spi_reset(ctx);
27162306a36Sopenharmony_ci		if (rc)
27262306a36Sopenharmony_ci			return rc;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci		return -EREMOTEIO;
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	return 0;
27862306a36Sopenharmony_ci}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_cistatic void fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	/*
28362306a36Sopenharmony_ci	 * Add the next byte of instruction to the 8-byte sequence register.
28462306a36Sopenharmony_ci	 * Then decrement the counter so that the next instruction will go in
28562306a36Sopenharmony_ci	 * the right place. Return the index of the slot we just filled in the
28662306a36Sopenharmony_ci	 * sequence register.
28762306a36Sopenharmony_ci	 */
28862306a36Sopenharmony_ci	seq->data |= (u64)val << seq->bit;
28962306a36Sopenharmony_ci	seq->bit -= 8;
29062306a36Sopenharmony_ci}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_cistatic void fsi_spi_sequence_init(struct fsi_spi_sequence *seq)
29362306a36Sopenharmony_ci{
29462306a36Sopenharmony_ci	seq->bit = 56;
29562306a36Sopenharmony_ci	seq->data = 0ULL;
29662306a36Sopenharmony_ci}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_cistatic int fsi_spi_transfer_data(struct fsi_spi *ctx,
29962306a36Sopenharmony_ci				 struct spi_transfer *transfer)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	int loops;
30262306a36Sopenharmony_ci	int rc = 0;
30362306a36Sopenharmony_ci	unsigned long end;
30462306a36Sopenharmony_ci	u64 status = 0ULL;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	if (transfer->tx_buf) {
30762306a36Sopenharmony_ci		int nb;
30862306a36Sopenharmony_ci		int sent = 0;
30962306a36Sopenharmony_ci		u64 out = 0ULL;
31062306a36Sopenharmony_ci		const u8 *tx = transfer->tx_buf;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci		while (transfer->len > sent) {
31362306a36Sopenharmony_ci			nb = fsi_spi_data_out(&out, &tx[sent],
31462306a36Sopenharmony_ci					      (int)transfer->len - sent);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci			rc = fsi_spi_write_reg(ctx, SPI_FSI_DATA_TX, out);
31762306a36Sopenharmony_ci			if (rc)
31862306a36Sopenharmony_ci				return rc;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci			loops = 0;
32162306a36Sopenharmony_ci			end = jiffies + msecs_to_jiffies(SPI_FSI_TIMEOUT_MS);
32262306a36Sopenharmony_ci			do {
32362306a36Sopenharmony_ci				if (loops++ && time_after(jiffies, end))
32462306a36Sopenharmony_ci					return -ETIMEDOUT;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci				rc = fsi_spi_status(ctx, &status, "TX");
32762306a36Sopenharmony_ci				if (rc)
32862306a36Sopenharmony_ci					return rc;
32962306a36Sopenharmony_ci			} while (status & SPI_FSI_STATUS_TDR_FULL);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci			sent += nb;
33262306a36Sopenharmony_ci		}
33362306a36Sopenharmony_ci	} else if (transfer->rx_buf) {
33462306a36Sopenharmony_ci		int recv = 0;
33562306a36Sopenharmony_ci		u64 in = 0ULL;
33662306a36Sopenharmony_ci		u8 *rx = transfer->rx_buf;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci		while (transfer->len > recv) {
33962306a36Sopenharmony_ci			loops = 0;
34062306a36Sopenharmony_ci			end = jiffies + msecs_to_jiffies(SPI_FSI_TIMEOUT_MS);
34162306a36Sopenharmony_ci			do {
34262306a36Sopenharmony_ci				if (loops++ && time_after(jiffies, end))
34362306a36Sopenharmony_ci					return -ETIMEDOUT;
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci				rc = fsi_spi_status(ctx, &status, "RX");
34662306a36Sopenharmony_ci				if (rc)
34762306a36Sopenharmony_ci					return rc;
34862306a36Sopenharmony_ci			} while (!(status & SPI_FSI_STATUS_RDR_FULL));
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci			rc = fsi_spi_read_reg(ctx, SPI_FSI_DATA_RX, &in);
35162306a36Sopenharmony_ci			if (rc)
35262306a36Sopenharmony_ci				return rc;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci			recv += fsi_spi_data_in(in, &rx[recv],
35562306a36Sopenharmony_ci						(int)transfer->len - recv);
35662306a36Sopenharmony_ci		}
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	return 0;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cistatic int fsi_spi_transfer_init(struct fsi_spi *ctx)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	int loops = 0;
36562306a36Sopenharmony_ci	int rc;
36662306a36Sopenharmony_ci	bool reset = false;
36762306a36Sopenharmony_ci	unsigned long end;
36862306a36Sopenharmony_ci	u64 seq_state;
36962306a36Sopenharmony_ci	u64 clock_cfg = 0ULL;
37062306a36Sopenharmony_ci	u64 status = 0ULL;
37162306a36Sopenharmony_ci	u64 wanted_clock_cfg = SPI_FSI_CLOCK_CFG_ECC_DISABLE |
37262306a36Sopenharmony_ci		SPI_FSI_CLOCK_CFG_SCK_NO_DEL |
37362306a36Sopenharmony_ci		FIELD_PREP(SPI_FSI_CLOCK_CFG_SCK_DIV, 19);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	end = jiffies + msecs_to_jiffies(SPI_FSI_TIMEOUT_MS);
37662306a36Sopenharmony_ci	do {
37762306a36Sopenharmony_ci		if (loops++ && time_after(jiffies, end))
37862306a36Sopenharmony_ci			return -ETIMEDOUT;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci		rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, &status);
38162306a36Sopenharmony_ci		if (rc)
38262306a36Sopenharmony_ci			return rc;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci		seq_state = status & SPI_FSI_STATUS_SEQ_STATE;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		if (status & (SPI_FSI_STATUS_ANY_ERROR |
38762306a36Sopenharmony_ci			      SPI_FSI_STATUS_TDR_FULL |
38862306a36Sopenharmony_ci			      SPI_FSI_STATUS_RDR_FULL)) {
38962306a36Sopenharmony_ci			if (reset) {
39062306a36Sopenharmony_ci				dev_err(ctx->dev,
39162306a36Sopenharmony_ci					"Initialization error: %08llx\n",
39262306a36Sopenharmony_ci					status);
39362306a36Sopenharmony_ci				return -EIO;
39462306a36Sopenharmony_ci			}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci			rc = fsi_spi_reset(ctx);
39762306a36Sopenharmony_ci			if (rc)
39862306a36Sopenharmony_ci				return rc;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci			reset = true;
40162306a36Sopenharmony_ci			continue;
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci	} while (seq_state && (seq_state != SPI_FSI_STATUS_SEQ_STATE_IDLE));
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, 0ULL);
40662306a36Sopenharmony_ci	if (rc)
40762306a36Sopenharmony_ci		return rc;
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	rc = fsi_spi_read_reg(ctx, SPI_FSI_CLOCK_CFG, &clock_cfg);
41062306a36Sopenharmony_ci	if (rc)
41162306a36Sopenharmony_ci		return rc;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	if ((clock_cfg & (SPI_FSI_CLOCK_CFG_MM_ENABLE |
41462306a36Sopenharmony_ci			  SPI_FSI_CLOCK_CFG_ECC_DISABLE |
41562306a36Sopenharmony_ci			  SPI_FSI_CLOCK_CFG_MODE |
41662306a36Sopenharmony_ci			  SPI_FSI_CLOCK_CFG_SCK_RECV_DEL |
41762306a36Sopenharmony_ci			  SPI_FSI_CLOCK_CFG_SCK_DIV)) != wanted_clock_cfg)
41862306a36Sopenharmony_ci		rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG,
41962306a36Sopenharmony_ci				       wanted_clock_cfg);
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	return rc;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
42562306a36Sopenharmony_ci					struct spi_message *mesg)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	int rc;
42862306a36Sopenharmony_ci	u8 seq_slave = SPI_FSI_SEQUENCE_SEL_SLAVE(spi_get_chipselect(mesg->spi, 0) + 1);
42962306a36Sopenharmony_ci	unsigned int len;
43062306a36Sopenharmony_ci	struct spi_transfer *transfer;
43162306a36Sopenharmony_ci	struct fsi_spi *ctx = spi_controller_get_devdata(ctlr);
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	rc = fsi_spi_check_mux(ctx->bridge->fsi, ctx->dev);
43462306a36Sopenharmony_ci	if (rc)
43562306a36Sopenharmony_ci		goto error;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	list_for_each_entry(transfer, &mesg->transfers, transfer_list) {
43862306a36Sopenharmony_ci		struct fsi_spi_sequence seq;
43962306a36Sopenharmony_ci		struct spi_transfer *next = NULL;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci		/* Sequencer must do shift out (tx) first. */
44262306a36Sopenharmony_ci		if (!transfer->tx_buf || transfer->len > SPI_FSI_MAX_TX_SIZE) {
44362306a36Sopenharmony_ci			rc = -EINVAL;
44462306a36Sopenharmony_ci			goto error;
44562306a36Sopenharmony_ci		}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci		dev_dbg(ctx->dev, "Start tx of %d bytes.\n", transfer->len);
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		rc = fsi_spi_transfer_init(ctx);
45062306a36Sopenharmony_ci		if (rc < 0)
45162306a36Sopenharmony_ci			goto error;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		fsi_spi_sequence_init(&seq);
45462306a36Sopenharmony_ci		fsi_spi_sequence_add(&seq, seq_slave);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci		len = transfer->len;
45762306a36Sopenharmony_ci		while (len > 8) {
45862306a36Sopenharmony_ci			fsi_spi_sequence_add(&seq,
45962306a36Sopenharmony_ci					     SPI_FSI_SEQUENCE_SHIFT_OUT(8));
46062306a36Sopenharmony_ci			len -= 8;
46162306a36Sopenharmony_ci		}
46262306a36Sopenharmony_ci		fsi_spi_sequence_add(&seq, SPI_FSI_SEQUENCE_SHIFT_OUT(len));
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci		if (!list_is_last(&transfer->transfer_list,
46562306a36Sopenharmony_ci				  &mesg->transfers)) {
46662306a36Sopenharmony_ci			next = list_next_entry(transfer, transfer_list);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci			/* Sequencer can only do shift in (rx) after tx. */
46962306a36Sopenharmony_ci			if (next->rx_buf) {
47062306a36Sopenharmony_ci				u8 shift;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci				if (next->len > SPI_FSI_MAX_RX_SIZE) {
47362306a36Sopenharmony_ci					rc = -EINVAL;
47462306a36Sopenharmony_ci					goto error;
47562306a36Sopenharmony_ci				}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci				dev_dbg(ctx->dev, "Sequence rx of %d bytes.\n",
47862306a36Sopenharmony_ci					next->len);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci				shift = SPI_FSI_SEQUENCE_SHIFT_IN(next->len);
48162306a36Sopenharmony_ci				fsi_spi_sequence_add(&seq, shift);
48262306a36Sopenharmony_ci			} else {
48362306a36Sopenharmony_ci				next = NULL;
48462306a36Sopenharmony_ci			}
48562306a36Sopenharmony_ci		}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		fsi_spi_sequence_add(&seq, SPI_FSI_SEQUENCE_SEL_SLAVE(0));
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci		rc = fsi_spi_write_reg(ctx, SPI_FSI_SEQUENCE, seq.data);
49062306a36Sopenharmony_ci		if (rc)
49162306a36Sopenharmony_ci			goto error;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci		rc = fsi_spi_transfer_data(ctx, transfer);
49462306a36Sopenharmony_ci		if (rc)
49562306a36Sopenharmony_ci			goto error;
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci		if (next) {
49862306a36Sopenharmony_ci			rc = fsi_spi_transfer_data(ctx, next);
49962306a36Sopenharmony_ci			if (rc)
50062306a36Sopenharmony_ci				goto error;
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci			transfer = next;
50362306a36Sopenharmony_ci		}
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cierror:
50762306a36Sopenharmony_ci	mesg->status = rc;
50862306a36Sopenharmony_ci	spi_finalize_current_message(ctlr);
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	return rc;
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_cistatic size_t fsi_spi_max_transfer_size(struct spi_device *spi)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	return SPI_FSI_MAX_RX_SIZE;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic int fsi_spi_probe(struct device *dev)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	int rc;
52162306a36Sopenharmony_ci	struct device_node *np;
52262306a36Sopenharmony_ci	int num_controllers_registered = 0;
52362306a36Sopenharmony_ci	struct fsi2spi *bridge;
52462306a36Sopenharmony_ci	struct fsi_device *fsi = to_fsi_dev(dev);
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	rc = fsi_spi_check_mux(fsi, dev);
52762306a36Sopenharmony_ci	if (rc)
52862306a36Sopenharmony_ci		return -ENODEV;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL);
53162306a36Sopenharmony_ci	if (!bridge)
53262306a36Sopenharmony_ci		return -ENOMEM;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	bridge->fsi = fsi;
53562306a36Sopenharmony_ci	mutex_init(&bridge->lock);
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	for_each_available_child_of_node(dev->of_node, np) {
53862306a36Sopenharmony_ci		u32 base;
53962306a36Sopenharmony_ci		struct fsi_spi *ctx;
54062306a36Sopenharmony_ci		struct spi_controller *ctlr;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		if (of_property_read_u32(np, "reg", &base))
54362306a36Sopenharmony_ci			continue;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci		ctlr = spi_alloc_host(dev, sizeof(*ctx));
54662306a36Sopenharmony_ci		if (!ctlr) {
54762306a36Sopenharmony_ci			of_node_put(np);
54862306a36Sopenharmony_ci			break;
54962306a36Sopenharmony_ci		}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		ctlr->dev.of_node = np;
55262306a36Sopenharmony_ci		ctlr->num_chipselect = of_get_available_child_count(np) ?: 1;
55362306a36Sopenharmony_ci		ctlr->flags = SPI_CONTROLLER_HALF_DUPLEX;
55462306a36Sopenharmony_ci		ctlr->max_transfer_size = fsi_spi_max_transfer_size;
55562306a36Sopenharmony_ci		ctlr->transfer_one_message = fsi_spi_transfer_one_message;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci		ctx = spi_controller_get_devdata(ctlr);
55862306a36Sopenharmony_ci		ctx->dev = &ctlr->dev;
55962306a36Sopenharmony_ci		ctx->bridge = bridge;
56062306a36Sopenharmony_ci		ctx->base = base + SPI_FSI_BASE;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		rc = devm_spi_register_controller(dev, ctlr);
56362306a36Sopenharmony_ci		if (rc)
56462306a36Sopenharmony_ci			spi_controller_put(ctlr);
56562306a36Sopenharmony_ci		else
56662306a36Sopenharmony_ci			num_controllers_registered++;
56762306a36Sopenharmony_ci	}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	if (!num_controllers_registered)
57062306a36Sopenharmony_ci		return -ENODEV;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	return 0;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic const struct fsi_device_id fsi_spi_ids[] = {
57662306a36Sopenharmony_ci	{ FSI_ENGID_SPI, FSI_VERSION_ANY },
57762306a36Sopenharmony_ci	{ }
57862306a36Sopenharmony_ci};
57962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(fsi, fsi_spi_ids);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic struct fsi_driver fsi_spi_driver = {
58262306a36Sopenharmony_ci	.id_table = fsi_spi_ids,
58362306a36Sopenharmony_ci	.drv = {
58462306a36Sopenharmony_ci		.name = "spi-fsi",
58562306a36Sopenharmony_ci		.bus = &fsi_bus_type,
58662306a36Sopenharmony_ci		.probe = fsi_spi_probe,
58762306a36Sopenharmony_ci	},
58862306a36Sopenharmony_ci};
58962306a36Sopenharmony_cimodule_fsi_driver(fsi_spi_driver);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ciMODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
59262306a36Sopenharmony_ciMODULE_DESCRIPTION("FSI attached SPI controller");
59362306a36Sopenharmony_ciMODULE_LICENSE("GPL");
594