18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci// Copyright (C) IBM Corporation 2020
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
58c2ecf20Sopenharmony_ci#include <linux/bits.h>
68c2ecf20Sopenharmony_ci#include <linux/fsi.h>
78c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/of.h>
118c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define FSI_ENGID_SPI			0x23
148c2ecf20Sopenharmony_ci#define FSI_MBOX_ROOT_CTRL_8		0x2860
158c2ecf20Sopenharmony_ci#define  FSI_MBOX_ROOT_CTRL_8_SPI_MUX	 0xf0000000
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define FSI2SPI_DATA0			0x00
188c2ecf20Sopenharmony_ci#define FSI2SPI_DATA1			0x04
198c2ecf20Sopenharmony_ci#define FSI2SPI_CMD			0x08
208c2ecf20Sopenharmony_ci#define  FSI2SPI_CMD_WRITE		 BIT(31)
218c2ecf20Sopenharmony_ci#define FSI2SPI_RESET			0x18
228c2ecf20Sopenharmony_ci#define FSI2SPI_STATUS			0x1c
238c2ecf20Sopenharmony_ci#define  FSI2SPI_STATUS_ANY_ERROR	 BIT(31)
248c2ecf20Sopenharmony_ci#define FSI2SPI_IRQ			0x20
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define SPI_FSI_BASE			0x70000
278c2ecf20Sopenharmony_ci#define SPI_FSI_INIT_TIMEOUT_MS		1000
288c2ecf20Sopenharmony_ci#define SPI_FSI_MAX_XFR_SIZE		2048
298c2ecf20Sopenharmony_ci#define SPI_FSI_MAX_XFR_SIZE_RESTRICTED	32
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define SPI_FSI_ERROR			0x0
328c2ecf20Sopenharmony_ci#define SPI_FSI_COUNTER_CFG		0x1
338c2ecf20Sopenharmony_ci#define  SPI_FSI_COUNTER_CFG_LOOPS(x)	 (((u64)(x) & 0xffULL) << 32)
348c2ecf20Sopenharmony_ci#define  SPI_FSI_COUNTER_CFG_N2_RX	 BIT_ULL(8)
358c2ecf20Sopenharmony_ci#define  SPI_FSI_COUNTER_CFG_N2_TX	 BIT_ULL(9)
368c2ecf20Sopenharmony_ci#define  SPI_FSI_COUNTER_CFG_N2_IMPLICIT BIT_ULL(10)
378c2ecf20Sopenharmony_ci#define  SPI_FSI_COUNTER_CFG_N2_RELOAD	 BIT_ULL(11)
388c2ecf20Sopenharmony_ci#define SPI_FSI_CFG1			0x2
398c2ecf20Sopenharmony_ci#define SPI_FSI_CLOCK_CFG		0x3
408c2ecf20Sopenharmony_ci#define  SPI_FSI_CLOCK_CFG_MM_ENABLE	 BIT_ULL(32)
418c2ecf20Sopenharmony_ci#define  SPI_FSI_CLOCK_CFG_ECC_DISABLE	 (BIT_ULL(35) | BIT_ULL(33))
428c2ecf20Sopenharmony_ci#define  SPI_FSI_CLOCK_CFG_RESET1	 (BIT_ULL(36) | BIT_ULL(38))
438c2ecf20Sopenharmony_ci#define  SPI_FSI_CLOCK_CFG_RESET2	 (BIT_ULL(37) | BIT_ULL(39))
448c2ecf20Sopenharmony_ci#define  SPI_FSI_CLOCK_CFG_MODE		 (BIT_ULL(41) | BIT_ULL(42))
458c2ecf20Sopenharmony_ci#define  SPI_FSI_CLOCK_CFG_SCK_RECV_DEL	 GENMASK_ULL(51, 44)
468c2ecf20Sopenharmony_ci#define   SPI_FSI_CLOCK_CFG_SCK_NO_DEL	  BIT_ULL(51)
478c2ecf20Sopenharmony_ci#define  SPI_FSI_CLOCK_CFG_SCK_DIV	 GENMASK_ULL(63, 52)
488c2ecf20Sopenharmony_ci#define SPI_FSI_MMAP			0x4
498c2ecf20Sopenharmony_ci#define SPI_FSI_DATA_TX			0x5
508c2ecf20Sopenharmony_ci#define SPI_FSI_DATA_RX			0x6
518c2ecf20Sopenharmony_ci#define SPI_FSI_SEQUENCE		0x7
528c2ecf20Sopenharmony_ci#define  SPI_FSI_SEQUENCE_STOP		 0x00
538c2ecf20Sopenharmony_ci#define  SPI_FSI_SEQUENCE_SEL_SLAVE(x)	 (0x10 | ((x) & 0xf))
548c2ecf20Sopenharmony_ci#define  SPI_FSI_SEQUENCE_SHIFT_OUT(x)	 (0x30 | ((x) & 0xf))
558c2ecf20Sopenharmony_ci#define  SPI_FSI_SEQUENCE_SHIFT_IN(x)	 (0x40 | ((x) & 0xf))
568c2ecf20Sopenharmony_ci#define  SPI_FSI_SEQUENCE_COPY_DATA_TX	 0xc0
578c2ecf20Sopenharmony_ci#define  SPI_FSI_SEQUENCE_BRANCH(x)	 (0xe0 | ((x) & 0xf))
588c2ecf20Sopenharmony_ci#define SPI_FSI_STATUS			0x8
598c2ecf20Sopenharmony_ci#define  SPI_FSI_STATUS_ERROR		 \
608c2ecf20Sopenharmony_ci	(GENMASK_ULL(31, 21) | GENMASK_ULL(15, 12))
618c2ecf20Sopenharmony_ci#define  SPI_FSI_STATUS_SEQ_STATE	 GENMASK_ULL(55, 48)
628c2ecf20Sopenharmony_ci#define   SPI_FSI_STATUS_SEQ_STATE_IDLE	  BIT_ULL(48)
638c2ecf20Sopenharmony_ci#define  SPI_FSI_STATUS_TDR_UNDERRUN	 BIT_ULL(57)
648c2ecf20Sopenharmony_ci#define  SPI_FSI_STATUS_TDR_OVERRUN	 BIT_ULL(58)
658c2ecf20Sopenharmony_ci#define  SPI_FSI_STATUS_TDR_FULL	 BIT_ULL(59)
668c2ecf20Sopenharmony_ci#define  SPI_FSI_STATUS_RDR_UNDERRUN	 BIT_ULL(61)
678c2ecf20Sopenharmony_ci#define  SPI_FSI_STATUS_RDR_OVERRUN	 BIT_ULL(62)
688c2ecf20Sopenharmony_ci#define  SPI_FSI_STATUS_RDR_FULL	 BIT_ULL(63)
698c2ecf20Sopenharmony_ci#define  SPI_FSI_STATUS_ANY_ERROR	 \
708c2ecf20Sopenharmony_ci	(SPI_FSI_STATUS_ERROR | \
718c2ecf20Sopenharmony_ci	 SPI_FSI_STATUS_TDR_OVERRUN | SPI_FSI_STATUS_RDR_UNDERRUN | \
728c2ecf20Sopenharmony_ci	 SPI_FSI_STATUS_RDR_OVERRUN)
738c2ecf20Sopenharmony_ci#define SPI_FSI_PORT_CTRL		0x9
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistruct fsi_spi {
768c2ecf20Sopenharmony_ci	struct device *dev;	/* SPI controller device */
778c2ecf20Sopenharmony_ci	struct fsi_device *fsi;	/* FSI2SPI CFAM engine device */
788c2ecf20Sopenharmony_ci	u32 base;
798c2ecf20Sopenharmony_ci	size_t max_xfr_size;
808c2ecf20Sopenharmony_ci	bool restricted;
818c2ecf20Sopenharmony_ci};
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistruct fsi_spi_sequence {
848c2ecf20Sopenharmony_ci	int bit;
858c2ecf20Sopenharmony_ci	u64 data;
868c2ecf20Sopenharmony_ci};
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int fsi_spi_check_mux(struct fsi_device *fsi, struct device *dev)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	int rc;
918c2ecf20Sopenharmony_ci	u32 root_ctrl_8;
928c2ecf20Sopenharmony_ci	__be32 root_ctrl_8_be;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	rc = fsi_slave_read(fsi->slave, FSI_MBOX_ROOT_CTRL_8, &root_ctrl_8_be,
958c2ecf20Sopenharmony_ci			    sizeof(root_ctrl_8_be));
968c2ecf20Sopenharmony_ci	if (rc)
978c2ecf20Sopenharmony_ci		return rc;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	root_ctrl_8 = be32_to_cpu(root_ctrl_8_be);
1008c2ecf20Sopenharmony_ci	dev_dbg(dev, "Root control register 8: %08x\n", root_ctrl_8);
1018c2ecf20Sopenharmony_ci	if ((root_ctrl_8 & FSI_MBOX_ROOT_CTRL_8_SPI_MUX) ==
1028c2ecf20Sopenharmony_ci	     FSI_MBOX_ROOT_CTRL_8_SPI_MUX)
1038c2ecf20Sopenharmony_ci		return 0;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	return -ENOLINK;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic int fsi_spi_check_status(struct fsi_spi *ctx)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	int rc;
1118c2ecf20Sopenharmony_ci	u32 sts;
1128c2ecf20Sopenharmony_ci	__be32 sts_be;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	rc = fsi_device_read(ctx->fsi, FSI2SPI_STATUS, &sts_be,
1158c2ecf20Sopenharmony_ci			     sizeof(sts_be));
1168c2ecf20Sopenharmony_ci	if (rc)
1178c2ecf20Sopenharmony_ci		return rc;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	sts = be32_to_cpu(sts_be);
1208c2ecf20Sopenharmony_ci	if (sts & FSI2SPI_STATUS_ANY_ERROR) {
1218c2ecf20Sopenharmony_ci		dev_err(ctx->dev, "Error with FSI2SPI interface: %08x.\n", sts);
1228c2ecf20Sopenharmony_ci		return -EIO;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return 0;
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic int fsi_spi_read_reg(struct fsi_spi *ctx, u32 offset, u64 *value)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	int rc;
1318c2ecf20Sopenharmony_ci	__be32 cmd_be;
1328c2ecf20Sopenharmony_ci	__be32 data_be;
1338c2ecf20Sopenharmony_ci	u32 cmd = offset + ctx->base;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	*value = 0ULL;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (cmd & FSI2SPI_CMD_WRITE)
1388c2ecf20Sopenharmony_ci		return -EINVAL;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	cmd_be = cpu_to_be32(cmd);
1418c2ecf20Sopenharmony_ci	rc = fsi_device_write(ctx->fsi, FSI2SPI_CMD, &cmd_be, sizeof(cmd_be));
1428c2ecf20Sopenharmony_ci	if (rc)
1438c2ecf20Sopenharmony_ci		return rc;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	rc = fsi_spi_check_status(ctx);
1468c2ecf20Sopenharmony_ci	if (rc)
1478c2ecf20Sopenharmony_ci		return rc;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	rc = fsi_device_read(ctx->fsi, FSI2SPI_DATA0, &data_be,
1508c2ecf20Sopenharmony_ci			     sizeof(data_be));
1518c2ecf20Sopenharmony_ci	if (rc)
1528c2ecf20Sopenharmony_ci		return rc;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	*value |= (u64)be32_to_cpu(data_be) << 32;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	rc = fsi_device_read(ctx->fsi, FSI2SPI_DATA1, &data_be,
1578c2ecf20Sopenharmony_ci			     sizeof(data_be));
1588c2ecf20Sopenharmony_ci	if (rc)
1598c2ecf20Sopenharmony_ci		return rc;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	*value |= (u64)be32_to_cpu(data_be);
1628c2ecf20Sopenharmony_ci	dev_dbg(ctx->dev, "Read %02x[%016llx].\n", offset, *value);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic int fsi_spi_write_reg(struct fsi_spi *ctx, u32 offset, u64 value)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	int rc;
1708c2ecf20Sopenharmony_ci	__be32 cmd_be;
1718c2ecf20Sopenharmony_ci	__be32 data_be;
1728c2ecf20Sopenharmony_ci	u32 cmd = offset + ctx->base;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (cmd & FSI2SPI_CMD_WRITE)
1758c2ecf20Sopenharmony_ci		return -EINVAL;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	dev_dbg(ctx->dev, "Write %02x[%016llx].\n", offset, value);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	data_be = cpu_to_be32(upper_32_bits(value));
1808c2ecf20Sopenharmony_ci	rc = fsi_device_write(ctx->fsi, FSI2SPI_DATA0, &data_be,
1818c2ecf20Sopenharmony_ci			      sizeof(data_be));
1828c2ecf20Sopenharmony_ci	if (rc)
1838c2ecf20Sopenharmony_ci		return rc;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	data_be = cpu_to_be32(lower_32_bits(value));
1868c2ecf20Sopenharmony_ci	rc = fsi_device_write(ctx->fsi, FSI2SPI_DATA1, &data_be,
1878c2ecf20Sopenharmony_ci			      sizeof(data_be));
1888c2ecf20Sopenharmony_ci	if (rc)
1898c2ecf20Sopenharmony_ci		return rc;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	cmd_be = cpu_to_be32(cmd | FSI2SPI_CMD_WRITE);
1928c2ecf20Sopenharmony_ci	rc = fsi_device_write(ctx->fsi, FSI2SPI_CMD, &cmd_be, sizeof(cmd_be));
1938c2ecf20Sopenharmony_ci	if (rc)
1948c2ecf20Sopenharmony_ci		return rc;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return fsi_spi_check_status(ctx);
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int fsi_spi_data_in(u64 in, u8 *rx, int len)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	int i;
2028c2ecf20Sopenharmony_ci	int num_bytes = min(len, 8);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	for (i = 0; i < num_bytes; ++i)
2058c2ecf20Sopenharmony_ci		rx[i] = (u8)(in >> (8 * ((num_bytes - 1) - i)));
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	return num_bytes;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_cistatic int fsi_spi_data_out(u64 *out, const u8 *tx, int len)
2118c2ecf20Sopenharmony_ci{
2128c2ecf20Sopenharmony_ci	int i;
2138c2ecf20Sopenharmony_ci	int num_bytes = min(len, 8);
2148c2ecf20Sopenharmony_ci	u8 *out_bytes = (u8 *)out;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	/* Unused bytes of the tx data should be 0. */
2178c2ecf20Sopenharmony_ci	*out = 0ULL;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	for (i = 0; i < num_bytes; ++i)
2208c2ecf20Sopenharmony_ci		out_bytes[8 - (i + 1)] = tx[i];
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	return num_bytes;
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic int fsi_spi_reset(struct fsi_spi *ctx)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	int rc;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	dev_dbg(ctx->dev, "Resetting SPI controller.\n");
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG,
2328c2ecf20Sopenharmony_ci			       SPI_FSI_CLOCK_CFG_RESET1);
2338c2ecf20Sopenharmony_ci	if (rc)
2348c2ecf20Sopenharmony_ci		return rc;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG,
2378c2ecf20Sopenharmony_ci			       SPI_FSI_CLOCK_CFG_RESET2);
2388c2ecf20Sopenharmony_ci	if (rc)
2398c2ecf20Sopenharmony_ci		return rc;
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	return fsi_spi_write_reg(ctx, SPI_FSI_STATUS, 0ULL);
2428c2ecf20Sopenharmony_ci}
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_cistatic int fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	/*
2478c2ecf20Sopenharmony_ci	 * Add the next byte of instruction to the 8-byte sequence register.
2488c2ecf20Sopenharmony_ci	 * Then decrement the counter so that the next instruction will go in
2498c2ecf20Sopenharmony_ci	 * the right place. Return the index of the slot we just filled in the
2508c2ecf20Sopenharmony_ci	 * sequence register.
2518c2ecf20Sopenharmony_ci	 */
2528c2ecf20Sopenharmony_ci	seq->data |= (u64)val << seq->bit;
2538c2ecf20Sopenharmony_ci	seq->bit -= 8;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	return ((64 - seq->bit) / 8) - 2;
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic void fsi_spi_sequence_init(struct fsi_spi_sequence *seq)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	seq->bit = 56;
2618c2ecf20Sopenharmony_ci	seq->data = 0ULL;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic int fsi_spi_sequence_transfer(struct fsi_spi *ctx,
2658c2ecf20Sopenharmony_ci				     struct fsi_spi_sequence *seq,
2668c2ecf20Sopenharmony_ci				     struct spi_transfer *transfer)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	bool docfg = false;
2698c2ecf20Sopenharmony_ci	int loops;
2708c2ecf20Sopenharmony_ci	int idx;
2718c2ecf20Sopenharmony_ci	int rc;
2728c2ecf20Sopenharmony_ci	u8 val = 0;
2738c2ecf20Sopenharmony_ci	u8 len = min(transfer->len, 8U);
2748c2ecf20Sopenharmony_ci	u8 rem = transfer->len % len;
2758c2ecf20Sopenharmony_ci	u64 cfg = 0ULL;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	loops = transfer->len / len;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	if (transfer->tx_buf) {
2808c2ecf20Sopenharmony_ci		val = SPI_FSI_SEQUENCE_SHIFT_OUT(len);
2818c2ecf20Sopenharmony_ci		idx = fsi_spi_sequence_add(seq, val);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci		if (rem)
2848c2ecf20Sopenharmony_ci			rem = SPI_FSI_SEQUENCE_SHIFT_OUT(rem);
2858c2ecf20Sopenharmony_ci	} else if (transfer->rx_buf) {
2868c2ecf20Sopenharmony_ci		val = SPI_FSI_SEQUENCE_SHIFT_IN(len);
2878c2ecf20Sopenharmony_ci		idx = fsi_spi_sequence_add(seq, val);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		if (rem)
2908c2ecf20Sopenharmony_ci			rem = SPI_FSI_SEQUENCE_SHIFT_IN(rem);
2918c2ecf20Sopenharmony_ci	} else {
2928c2ecf20Sopenharmony_ci		return -EINVAL;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	if (ctx->restricted) {
2968c2ecf20Sopenharmony_ci		const int eidx = rem ? 5 : 6;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci		while (loops > 1 && idx <= eidx) {
2998c2ecf20Sopenharmony_ci			idx = fsi_spi_sequence_add(seq, val);
3008c2ecf20Sopenharmony_ci			loops--;
3018c2ecf20Sopenharmony_ci			docfg = true;
3028c2ecf20Sopenharmony_ci		}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci		if (loops > 1) {
3058c2ecf20Sopenharmony_ci			dev_warn(ctx->dev, "No sequencer slots; aborting.\n");
3068c2ecf20Sopenharmony_ci			return -EINVAL;
3078c2ecf20Sopenharmony_ci		}
3088c2ecf20Sopenharmony_ci	}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (loops > 1) {
3118c2ecf20Sopenharmony_ci		fsi_spi_sequence_add(seq, SPI_FSI_SEQUENCE_BRANCH(idx));
3128c2ecf20Sopenharmony_ci		docfg = true;
3138c2ecf20Sopenharmony_ci	}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (docfg) {
3168c2ecf20Sopenharmony_ci		cfg = SPI_FSI_COUNTER_CFG_LOOPS(loops - 1);
3178c2ecf20Sopenharmony_ci		if (transfer->rx_buf)
3188c2ecf20Sopenharmony_ci			cfg |= SPI_FSI_COUNTER_CFG_N2_RX |
3198c2ecf20Sopenharmony_ci				SPI_FSI_COUNTER_CFG_N2_TX |
3208c2ecf20Sopenharmony_ci				SPI_FSI_COUNTER_CFG_N2_IMPLICIT |
3218c2ecf20Sopenharmony_ci				SPI_FSI_COUNTER_CFG_N2_RELOAD;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		rc = fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, cfg);
3248c2ecf20Sopenharmony_ci		if (rc)
3258c2ecf20Sopenharmony_ci			return rc;
3268c2ecf20Sopenharmony_ci	} else {
3278c2ecf20Sopenharmony_ci		fsi_spi_write_reg(ctx, SPI_FSI_COUNTER_CFG, 0ULL);
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (rem)
3318c2ecf20Sopenharmony_ci		fsi_spi_sequence_add(seq, rem);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	return 0;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistatic int fsi_spi_transfer_data(struct fsi_spi *ctx,
3378c2ecf20Sopenharmony_ci				 struct spi_transfer *transfer)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	int rc = 0;
3408c2ecf20Sopenharmony_ci	u64 status = 0ULL;
3418c2ecf20Sopenharmony_ci	u64 cfg = 0ULL;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	if (transfer->tx_buf) {
3448c2ecf20Sopenharmony_ci		int nb;
3458c2ecf20Sopenharmony_ci		int sent = 0;
3468c2ecf20Sopenharmony_ci		u64 out = 0ULL;
3478c2ecf20Sopenharmony_ci		const u8 *tx = transfer->tx_buf;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci		while (transfer->len > sent) {
3508c2ecf20Sopenharmony_ci			nb = fsi_spi_data_out(&out, &tx[sent],
3518c2ecf20Sopenharmony_ci					      (int)transfer->len - sent);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci			rc = fsi_spi_write_reg(ctx, SPI_FSI_DATA_TX, out);
3548c2ecf20Sopenharmony_ci			if (rc)
3558c2ecf20Sopenharmony_ci				return rc;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci			do {
3588c2ecf20Sopenharmony_ci				rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS,
3598c2ecf20Sopenharmony_ci						      &status);
3608c2ecf20Sopenharmony_ci				if (rc)
3618c2ecf20Sopenharmony_ci					return rc;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci				if (status & SPI_FSI_STATUS_ANY_ERROR) {
3648c2ecf20Sopenharmony_ci					rc = fsi_spi_reset(ctx);
3658c2ecf20Sopenharmony_ci					if (rc)
3668c2ecf20Sopenharmony_ci						return rc;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci					return -EREMOTEIO;
3698c2ecf20Sopenharmony_ci				}
3708c2ecf20Sopenharmony_ci			} while (status & SPI_FSI_STATUS_TDR_FULL);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci			sent += nb;
3738c2ecf20Sopenharmony_ci		}
3748c2ecf20Sopenharmony_ci	} else if (transfer->rx_buf) {
3758c2ecf20Sopenharmony_ci		int recv = 0;
3768c2ecf20Sopenharmony_ci		u64 in = 0ULL;
3778c2ecf20Sopenharmony_ci		u8 *rx = transfer->rx_buf;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		rc = fsi_spi_read_reg(ctx, SPI_FSI_COUNTER_CFG, &cfg);
3808c2ecf20Sopenharmony_ci		if (rc)
3818c2ecf20Sopenharmony_ci			return rc;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci		if (cfg & SPI_FSI_COUNTER_CFG_N2_IMPLICIT) {
3848c2ecf20Sopenharmony_ci			rc = fsi_spi_write_reg(ctx, SPI_FSI_DATA_TX, 0);
3858c2ecf20Sopenharmony_ci			if (rc)
3868c2ecf20Sopenharmony_ci				return rc;
3878c2ecf20Sopenharmony_ci		}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		while (transfer->len > recv) {
3908c2ecf20Sopenharmony_ci			do {
3918c2ecf20Sopenharmony_ci				rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS,
3928c2ecf20Sopenharmony_ci						      &status);
3938c2ecf20Sopenharmony_ci				if (rc)
3948c2ecf20Sopenharmony_ci					return rc;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci				if (status & SPI_FSI_STATUS_ANY_ERROR) {
3978c2ecf20Sopenharmony_ci					rc = fsi_spi_reset(ctx);
3988c2ecf20Sopenharmony_ci					if (rc)
3998c2ecf20Sopenharmony_ci						return rc;
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci					return -EREMOTEIO;
4028c2ecf20Sopenharmony_ci				}
4038c2ecf20Sopenharmony_ci			} while (!(status & SPI_FSI_STATUS_RDR_FULL));
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci			rc = fsi_spi_read_reg(ctx, SPI_FSI_DATA_RX, &in);
4068c2ecf20Sopenharmony_ci			if (rc)
4078c2ecf20Sopenharmony_ci				return rc;
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci			recv += fsi_spi_data_in(in, &rx[recv],
4108c2ecf20Sopenharmony_ci						(int)transfer->len - recv);
4118c2ecf20Sopenharmony_ci		}
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	return 0;
4158c2ecf20Sopenharmony_ci}
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_cistatic int fsi_spi_transfer_init(struct fsi_spi *ctx)
4188c2ecf20Sopenharmony_ci{
4198c2ecf20Sopenharmony_ci	int rc;
4208c2ecf20Sopenharmony_ci	bool reset = false;
4218c2ecf20Sopenharmony_ci	unsigned long end;
4228c2ecf20Sopenharmony_ci	u64 seq_state;
4238c2ecf20Sopenharmony_ci	u64 clock_cfg = 0ULL;
4248c2ecf20Sopenharmony_ci	u64 status = 0ULL;
4258c2ecf20Sopenharmony_ci	u64 wanted_clock_cfg = SPI_FSI_CLOCK_CFG_ECC_DISABLE |
4268c2ecf20Sopenharmony_ci		SPI_FSI_CLOCK_CFG_SCK_NO_DEL |
4278c2ecf20Sopenharmony_ci		FIELD_PREP(SPI_FSI_CLOCK_CFG_SCK_DIV, 19);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	end = jiffies + msecs_to_jiffies(SPI_FSI_INIT_TIMEOUT_MS);
4308c2ecf20Sopenharmony_ci	do {
4318c2ecf20Sopenharmony_ci		if (time_after(jiffies, end))
4328c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci		rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, &status);
4358c2ecf20Sopenharmony_ci		if (rc)
4368c2ecf20Sopenharmony_ci			return rc;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci		seq_state = status & SPI_FSI_STATUS_SEQ_STATE;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci		if (status & (SPI_FSI_STATUS_ANY_ERROR |
4418c2ecf20Sopenharmony_ci			      SPI_FSI_STATUS_TDR_FULL |
4428c2ecf20Sopenharmony_ci			      SPI_FSI_STATUS_RDR_FULL)) {
4438c2ecf20Sopenharmony_ci			if (reset)
4448c2ecf20Sopenharmony_ci				return -EIO;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci			rc = fsi_spi_reset(ctx);
4478c2ecf20Sopenharmony_ci			if (rc)
4488c2ecf20Sopenharmony_ci				return rc;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci			reset = true;
4518c2ecf20Sopenharmony_ci			continue;
4528c2ecf20Sopenharmony_ci		}
4538c2ecf20Sopenharmony_ci	} while (seq_state && (seq_state != SPI_FSI_STATUS_SEQ_STATE_IDLE));
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	rc = fsi_spi_read_reg(ctx, SPI_FSI_CLOCK_CFG, &clock_cfg);
4568c2ecf20Sopenharmony_ci	if (rc)
4578c2ecf20Sopenharmony_ci		return rc;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	if ((clock_cfg & (SPI_FSI_CLOCK_CFG_MM_ENABLE |
4608c2ecf20Sopenharmony_ci			  SPI_FSI_CLOCK_CFG_ECC_DISABLE |
4618c2ecf20Sopenharmony_ci			  SPI_FSI_CLOCK_CFG_MODE |
4628c2ecf20Sopenharmony_ci			  SPI_FSI_CLOCK_CFG_SCK_RECV_DEL |
4638c2ecf20Sopenharmony_ci			  SPI_FSI_CLOCK_CFG_SCK_DIV)) != wanted_clock_cfg)
4648c2ecf20Sopenharmony_ci		rc = fsi_spi_write_reg(ctx, SPI_FSI_CLOCK_CFG,
4658c2ecf20Sopenharmony_ci				       wanted_clock_cfg);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	return rc;
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
4718c2ecf20Sopenharmony_ci					struct spi_message *mesg)
4728c2ecf20Sopenharmony_ci{
4738c2ecf20Sopenharmony_ci	int rc;
4748c2ecf20Sopenharmony_ci	u8 seq_slave = SPI_FSI_SEQUENCE_SEL_SLAVE(mesg->spi->chip_select + 1);
4758c2ecf20Sopenharmony_ci	struct spi_transfer *transfer;
4768c2ecf20Sopenharmony_ci	struct fsi_spi *ctx = spi_controller_get_devdata(ctlr);
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	rc = fsi_spi_check_mux(ctx->fsi, ctx->dev);
4798c2ecf20Sopenharmony_ci	if (rc)
4808c2ecf20Sopenharmony_ci		goto error;
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	list_for_each_entry(transfer, &mesg->transfers, transfer_list) {
4838c2ecf20Sopenharmony_ci		struct fsi_spi_sequence seq;
4848c2ecf20Sopenharmony_ci		struct spi_transfer *next = NULL;
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci		/* Sequencer must do shift out (tx) first. */
4878c2ecf20Sopenharmony_ci		if (!transfer->tx_buf ||
4888c2ecf20Sopenharmony_ci		    transfer->len > (ctx->max_xfr_size + 8)) {
4898c2ecf20Sopenharmony_ci			rc = -EINVAL;
4908c2ecf20Sopenharmony_ci			goto error;
4918c2ecf20Sopenharmony_ci		}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci		dev_dbg(ctx->dev, "Start tx of %d bytes.\n", transfer->len);
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci		rc = fsi_spi_transfer_init(ctx);
4968c2ecf20Sopenharmony_ci		if (rc < 0)
4978c2ecf20Sopenharmony_ci			goto error;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci		fsi_spi_sequence_init(&seq);
5008c2ecf20Sopenharmony_ci		fsi_spi_sequence_add(&seq, seq_slave);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci		rc = fsi_spi_sequence_transfer(ctx, &seq, transfer);
5038c2ecf20Sopenharmony_ci		if (rc)
5048c2ecf20Sopenharmony_ci			goto error;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci		if (!list_is_last(&transfer->transfer_list,
5078c2ecf20Sopenharmony_ci				  &mesg->transfers)) {
5088c2ecf20Sopenharmony_ci			next = list_next_entry(transfer, transfer_list);
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci			/* Sequencer can only do shift in (rx) after tx. */
5118c2ecf20Sopenharmony_ci			if (next->rx_buf) {
5128c2ecf20Sopenharmony_ci				if (next->len > ctx->max_xfr_size) {
5138c2ecf20Sopenharmony_ci					rc = -EINVAL;
5148c2ecf20Sopenharmony_ci					goto error;
5158c2ecf20Sopenharmony_ci				}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci				dev_dbg(ctx->dev, "Sequence rx of %d bytes.\n",
5188c2ecf20Sopenharmony_ci					next->len);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci				rc = fsi_spi_sequence_transfer(ctx, &seq,
5218c2ecf20Sopenharmony_ci							       next);
5228c2ecf20Sopenharmony_ci				if (rc)
5238c2ecf20Sopenharmony_ci					goto error;
5248c2ecf20Sopenharmony_ci			} else {
5258c2ecf20Sopenharmony_ci				next = NULL;
5268c2ecf20Sopenharmony_ci			}
5278c2ecf20Sopenharmony_ci		}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci		fsi_spi_sequence_add(&seq, SPI_FSI_SEQUENCE_SEL_SLAVE(0));
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci		rc = fsi_spi_write_reg(ctx, SPI_FSI_SEQUENCE, seq.data);
5328c2ecf20Sopenharmony_ci		if (rc)
5338c2ecf20Sopenharmony_ci			goto error;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci		rc = fsi_spi_transfer_data(ctx, transfer);
5368c2ecf20Sopenharmony_ci		if (rc)
5378c2ecf20Sopenharmony_ci			goto error;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci		if (next) {
5408c2ecf20Sopenharmony_ci			rc = fsi_spi_transfer_data(ctx, next);
5418c2ecf20Sopenharmony_ci			if (rc)
5428c2ecf20Sopenharmony_ci				goto error;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci			transfer = next;
5458c2ecf20Sopenharmony_ci		}
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_cierror:
5498c2ecf20Sopenharmony_ci	mesg->status = rc;
5508c2ecf20Sopenharmony_ci	spi_finalize_current_message(ctlr);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	return rc;
5538c2ecf20Sopenharmony_ci}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_cistatic size_t fsi_spi_max_transfer_size(struct spi_device *spi)
5568c2ecf20Sopenharmony_ci{
5578c2ecf20Sopenharmony_ci	struct fsi_spi *ctx = spi_controller_get_devdata(spi->controller);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	return ctx->max_xfr_size;
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_cistatic int fsi_spi_probe(struct device *dev)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	int rc;
5658c2ecf20Sopenharmony_ci	struct device_node *np;
5668c2ecf20Sopenharmony_ci	int num_controllers_registered = 0;
5678c2ecf20Sopenharmony_ci	struct fsi_device *fsi = to_fsi_dev(dev);
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	rc = fsi_spi_check_mux(fsi, dev);
5708c2ecf20Sopenharmony_ci	if (rc)
5718c2ecf20Sopenharmony_ci		return -ENODEV;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	for_each_available_child_of_node(dev->of_node, np) {
5748c2ecf20Sopenharmony_ci		u32 base;
5758c2ecf20Sopenharmony_ci		struct fsi_spi *ctx;
5768c2ecf20Sopenharmony_ci		struct spi_controller *ctlr;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci		if (of_property_read_u32(np, "reg", &base))
5798c2ecf20Sopenharmony_ci			continue;
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci		ctlr = spi_alloc_master(dev, sizeof(*ctx));
5828c2ecf20Sopenharmony_ci		if (!ctlr)
5838c2ecf20Sopenharmony_ci			break;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci		ctlr->dev.of_node = np;
5868c2ecf20Sopenharmony_ci		ctlr->num_chipselect = of_get_available_child_count(np) ?: 1;
5878c2ecf20Sopenharmony_ci		ctlr->flags = SPI_CONTROLLER_HALF_DUPLEX;
5888c2ecf20Sopenharmony_ci		ctlr->max_transfer_size = fsi_spi_max_transfer_size;
5898c2ecf20Sopenharmony_ci		ctlr->transfer_one_message = fsi_spi_transfer_one_message;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci		ctx = spi_controller_get_devdata(ctlr);
5928c2ecf20Sopenharmony_ci		ctx->dev = &ctlr->dev;
5938c2ecf20Sopenharmony_ci		ctx->fsi = fsi;
5948c2ecf20Sopenharmony_ci		ctx->base = base + SPI_FSI_BASE;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci		if (of_device_is_compatible(np, "ibm,fsi2spi-restricted")) {
5978c2ecf20Sopenharmony_ci			ctx->restricted = true;
5988c2ecf20Sopenharmony_ci			ctx->max_xfr_size = SPI_FSI_MAX_XFR_SIZE_RESTRICTED;
5998c2ecf20Sopenharmony_ci		} else {
6008c2ecf20Sopenharmony_ci			ctx->restricted = false;
6018c2ecf20Sopenharmony_ci			ctx->max_xfr_size = SPI_FSI_MAX_XFR_SIZE;
6028c2ecf20Sopenharmony_ci		}
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci		rc = devm_spi_register_controller(dev, ctlr);
6058c2ecf20Sopenharmony_ci		if (rc)
6068c2ecf20Sopenharmony_ci			spi_controller_put(ctlr);
6078c2ecf20Sopenharmony_ci		else
6088c2ecf20Sopenharmony_ci			num_controllers_registered++;
6098c2ecf20Sopenharmony_ci	}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	if (!num_controllers_registered)
6128c2ecf20Sopenharmony_ci		return -ENODEV;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	return 0;
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_cistatic const struct fsi_device_id fsi_spi_ids[] = {
6188c2ecf20Sopenharmony_ci	{ FSI_ENGID_SPI, FSI_VERSION_ANY },
6198c2ecf20Sopenharmony_ci	{ }
6208c2ecf20Sopenharmony_ci};
6218c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(fsi, fsi_spi_ids);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic struct fsi_driver fsi_spi_driver = {
6248c2ecf20Sopenharmony_ci	.id_table = fsi_spi_ids,
6258c2ecf20Sopenharmony_ci	.drv = {
6268c2ecf20Sopenharmony_ci		.name = "spi-fsi",
6278c2ecf20Sopenharmony_ci		.bus = &fsi_bus_type,
6288c2ecf20Sopenharmony_ci		.probe = fsi_spi_probe,
6298c2ecf20Sopenharmony_ci	},
6308c2ecf20Sopenharmony_ci};
6318c2ecf20Sopenharmony_cimodule_fsi_driver(fsi_spi_driver);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
6348c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("FSI attached SPI controller");
6358c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
636