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