summaryrefslogtreecommitdiff
path: root/drivers/spi/spi-fsi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-fsi.c')
-rw-r--r--drivers/spi/spi-fsi.c127
1 files changed, 87 insertions, 40 deletions
diff --git a/drivers/spi/spi-fsi.c b/drivers/spi/spi-fsi.c
index 829770b8ec74..d403a7a3021d 100644
--- a/drivers/spi/spi-fsi.c
+++ b/drivers/spi/spi-fsi.c
@@ -25,6 +25,7 @@
#define SPI_FSI_BASE 0x70000
#define SPI_FSI_INIT_TIMEOUT_MS 1000
+#define SPI_FSI_STATUS_TIMEOUT_MS 100
#define SPI_FSI_MAX_RX_SIZE 8
#define SPI_FSI_MAX_TX_SIZE 40
@@ -67,9 +68,14 @@
SPI_FSI_STATUS_RDR_OVERRUN)
#define SPI_FSI_PORT_CTRL 0x9
+struct fsi2spi {
+ struct fsi_device *fsi; /* FSI2SPI CFAM engine device */
+ struct mutex lock; /* lock access to the device */
+};
+
struct fsi_spi {
struct device *dev; /* SPI controller device */
- struct fsi_device *fsi; /* FSI2SPI CFAM engine device */
+ struct fsi2spi *bridge; /* FSI2SPI device */
u32 base;
};
@@ -104,7 +110,7 @@ static int fsi_spi_check_status(struct fsi_spi *ctx)
u32 sts;
__be32 sts_be;
- rc = fsi_device_read(ctx->fsi, FSI2SPI_STATUS, &sts_be,
+ rc = fsi_device_read(ctx->bridge->fsi, FSI2SPI_STATUS, &sts_be,
sizeof(sts_be));
if (rc)
return rc;
@@ -120,73 +126,91 @@ static int fsi_spi_check_status(struct fsi_spi *ctx)
static int fsi_spi_read_reg(struct fsi_spi *ctx, u32 offset, u64 *value)
{
- int rc;
+ int rc = 0;
__be32 cmd_be;
__be32 data_be;
u32 cmd = offset + ctx->base;
+ struct fsi2spi *bridge = ctx->bridge;
*value = 0ULL;
if (cmd & FSI2SPI_CMD_WRITE)
return -EINVAL;
- cmd_be = cpu_to_be32(cmd);
- rc = fsi_device_write(ctx->fsi, FSI2SPI_CMD, &cmd_be, sizeof(cmd_be));
+ rc = mutex_lock_interruptible(&bridge->lock);
if (rc)
return rc;
+ cmd_be = cpu_to_be32(cmd);
+ rc = fsi_device_write(bridge->fsi, FSI2SPI_CMD, &cmd_be,
+ sizeof(cmd_be));
+ if (rc)
+ goto unlock;
+
rc = fsi_spi_check_status(ctx);
if (rc)
- return rc;
+ goto unlock;
- rc = fsi_device_read(ctx->fsi, FSI2SPI_DATA0, &data_be,
+ rc = fsi_device_read(bridge->fsi, FSI2SPI_DATA0, &data_be,
sizeof(data_be));
if (rc)
- return rc;
+ goto unlock;
*value |= (u64)be32_to_cpu(data_be) << 32;
- rc = fsi_device_read(ctx->fsi, FSI2SPI_DATA1, &data_be,
+ rc = fsi_device_read(bridge->fsi, FSI2SPI_DATA1, &data_be,
sizeof(data_be));
if (rc)
- return rc;
+ goto unlock;
*value |= (u64)be32_to_cpu(data_be);
dev_dbg(ctx->dev, "Read %02x[%016llx].\n", offset, *value);
- return 0;
+unlock:
+ mutex_unlock(&bridge->lock);
+ return rc;
}
static int fsi_spi_write_reg(struct fsi_spi *ctx, u32 offset, u64 value)
{
- int rc;
+ int rc = 0;
__be32 cmd_be;
__be32 data_be;
u32 cmd = offset + ctx->base;
+ struct fsi2spi *bridge = ctx->bridge;
if (cmd & FSI2SPI_CMD_WRITE)
return -EINVAL;
+ rc = mutex_lock_interruptible(&bridge->lock);
+ if (rc)
+ return rc;
+
dev_dbg(ctx->dev, "Write %02x[%016llx].\n", offset, value);
data_be = cpu_to_be32(upper_32_bits(value));
- rc = fsi_device_write(ctx->fsi, FSI2SPI_DATA0, &data_be,
+ rc = fsi_device_write(bridge->fsi, FSI2SPI_DATA0, &data_be,
sizeof(data_be));
if (rc)
- return rc;
+ goto unlock;
data_be = cpu_to_be32(lower_32_bits(value));
- rc = fsi_device_write(ctx->fsi, FSI2SPI_DATA1, &data_be,
+ rc = fsi_device_write(bridge->fsi, FSI2SPI_DATA1, &data_be,
sizeof(data_be));
if (rc)
- return rc;
+ goto unlock;
cmd_be = cpu_to_be32(cmd | FSI2SPI_CMD_WRITE);
- rc = fsi_device_write(ctx->fsi, FSI2SPI_CMD, &cmd_be, sizeof(cmd_be));
+ rc = fsi_device_write(bridge->fsi, FSI2SPI_CMD, &cmd_be,
+ sizeof(cmd_be));
if (rc)
- return rc;
+ goto unlock;
+
+ rc = fsi_spi_check_status(ctx);
- return fsi_spi_check_status(ctx);
+unlock:
+ mutex_unlock(&bridge->lock);
+ return rc;
}
static int fsi_spi_data_in(u64 in, u8 *rx, int len)
@@ -234,6 +258,26 @@ static int fsi_spi_reset(struct fsi_spi *ctx)
return fsi_spi_write_reg(ctx, SPI_FSI_STATUS, 0ULL);
}
+static int fsi_spi_status(struct fsi_spi *ctx, u64 *status, const char *dir)
+{
+ int rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS, status);
+
+ if (rc)
+ return rc;
+
+ if (*status & SPI_FSI_STATUS_ANY_ERROR) {
+ dev_err(ctx->dev, "%s error: %016llx\n", dir, *status);
+
+ rc = fsi_spi_reset(ctx);
+ if (rc)
+ return rc;
+
+ return -EREMOTEIO;
+ }
+
+ return 0;
+}
+
static void fsi_spi_sequence_add(struct fsi_spi_sequence *seq, u8 val)
{
/*
@@ -256,6 +300,7 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
struct spi_transfer *transfer)
{
int rc = 0;
+ unsigned long end;
u64 status = 0ULL;
if (transfer->tx_buf) {
@@ -272,19 +317,14 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
if (rc)
return rc;
+ end = jiffies + msecs_to_jiffies(SPI_FSI_STATUS_TIMEOUT_MS);
do {
- rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS,
- &status);
+ rc = fsi_spi_status(ctx, &status, "TX");
if (rc)
return rc;
- if (status & SPI_FSI_STATUS_ANY_ERROR) {
- rc = fsi_spi_reset(ctx);
- if (rc)
- return rc;
-
- return -EREMOTEIO;
- }
+ if (time_after(jiffies, end))
+ return -ETIMEDOUT;
} while (status & SPI_FSI_STATUS_TDR_FULL);
sent += nb;
@@ -295,19 +335,14 @@ static int fsi_spi_transfer_data(struct fsi_spi *ctx,
u8 *rx = transfer->rx_buf;
while (transfer->len > recv) {
+ end = jiffies + msecs_to_jiffies(SPI_FSI_STATUS_TIMEOUT_MS);
do {
- rc = fsi_spi_read_reg(ctx, SPI_FSI_STATUS,
- &status);
+ rc = fsi_spi_status(ctx, &status, "RX");
if (rc)
return rc;
- if (status & SPI_FSI_STATUS_ANY_ERROR) {
- rc = fsi_spi_reset(ctx);
- if (rc)
- return rc;
-
- return -EREMOTEIO;
- }
+ if (time_after(jiffies, end))
+ return -ETIMEDOUT;
} while (!(status & SPI_FSI_STATUS_RDR_FULL));
rc = fsi_spi_read_reg(ctx, SPI_FSI_DATA_RX, &in);
@@ -348,8 +383,12 @@ static int fsi_spi_transfer_init(struct fsi_spi *ctx)
if (status & (SPI_FSI_STATUS_ANY_ERROR |
SPI_FSI_STATUS_TDR_FULL |
SPI_FSI_STATUS_RDR_FULL)) {
- if (reset)
+ if (reset) {
+ dev_err(ctx->dev,
+ "Initialization error: %08llx\n",
+ status);
return -EIO;
+ }
rc = fsi_spi_reset(ctx);
if (rc)
@@ -388,7 +427,7 @@ static int fsi_spi_transfer_one_message(struct spi_controller *ctlr,
struct spi_transfer *transfer;
struct fsi_spi *ctx = spi_controller_get_devdata(ctlr);
- rc = fsi_spi_check_mux(ctx->fsi, ctx->dev);
+ rc = fsi_spi_check_mux(ctx->bridge->fsi, ctx->dev);
if (rc)
goto error;
@@ -478,12 +517,20 @@ static int fsi_spi_probe(struct device *dev)
int rc;
struct device_node *np;
int num_controllers_registered = 0;
+ struct fsi2spi *bridge;
struct fsi_device *fsi = to_fsi_dev(dev);
rc = fsi_spi_check_mux(fsi, dev);
if (rc)
return -ENODEV;
+ bridge = devm_kzalloc(dev, sizeof(*bridge), GFP_KERNEL);
+ if (!bridge)
+ return -ENOMEM;
+
+ bridge->fsi = fsi;
+ mutex_init(&bridge->lock);
+
for_each_available_child_of_node(dev->of_node, np) {
u32 base;
struct fsi_spi *ctx;
@@ -506,7 +553,7 @@ static int fsi_spi_probe(struct device *dev)
ctx = spi_controller_get_devdata(ctlr);
ctx->dev = &ctlr->dev;
- ctx->fsi = fsi;
+ ctx->bridge = bridge;
ctx->base = base + SPI_FSI_BASE;
rc = devm_spi_register_controller(dev, ctlr);