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