summaryrefslogtreecommitdiff
path: root/drivers/spi/spi-img-spfi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-img-spfi.c')
-rw-r--r--drivers/spi/spi-img-spfi.c61
1 files changed, 48 insertions, 13 deletions
diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c
index f31d792a4662..823cbc92d1e7 100644
--- a/drivers/spi/spi-img-spfi.c
+++ b/drivers/spi/spi-img-spfi.c
@@ -40,6 +40,7 @@
#define SPFI_CONTROL_SOFT_RESET BIT(11)
#define SPFI_CONTROL_SEND_DMA BIT(10)
#define SPFI_CONTROL_GET_DMA BIT(9)
+#define SPFI_CONTROL_SE BIT(8)
#define SPFI_CONTROL_TMODE_SHIFT 5
#define SPFI_CONTROL_TMODE_MASK 0x7
#define SPFI_CONTROL_TMODE_SINGLE 0
@@ -104,6 +105,10 @@ struct img_spfi {
bool rx_dma_busy;
};
+struct img_spfi_device_data {
+ bool gpio_requested;
+};
+
static inline u32 spfi_readl(struct img_spfi *spfi, u32 reg)
{
return readl(spfi->regs + reg);
@@ -266,15 +271,15 @@ static int img_spfi_start_pio(struct spi_master *master,
cpu_relax();
}
- ret = spfi_wait_all_done(spfi);
- if (ret < 0)
- return ret;
-
if (rx_bytes > 0 || tx_bytes > 0) {
dev_err(spfi->dev, "PIO transfer timed out\n");
return -ETIMEDOUT;
}
+ ret = spfi_wait_all_done(spfi);
+ if (ret < 0)
+ return ret;
+
return 0;
}
@@ -439,21 +444,50 @@ static int img_spfi_unprepare(struct spi_master *master,
static int img_spfi_setup(struct spi_device *spi)
{
- int ret;
-
- ret = gpio_request_one(spi->cs_gpio, (spi->mode & SPI_CS_HIGH) ?
- GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
- dev_name(&spi->dev));
- if (ret)
- dev_err(&spi->dev, "can't request chipselect gpio %d\n",
+ int ret = -EINVAL;
+ struct img_spfi_device_data *spfi_data = spi_get_ctldata(spi);
+
+ if (!spfi_data) {
+ spfi_data = kzalloc(sizeof(*spfi_data), GFP_KERNEL);
+ if (!spfi_data)
+ return -ENOMEM;
+ spfi_data->gpio_requested = false;
+ spi_set_ctldata(spi, spfi_data);
+ }
+ if (!spfi_data->gpio_requested) {
+ ret = gpio_request_one(spi->cs_gpio,
+ (spi->mode & SPI_CS_HIGH) ?
+ GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
+ dev_name(&spi->dev));
+ if (ret)
+ dev_err(&spi->dev, "can't request chipselect gpio %d\n",
spi->cs_gpio);
-
+ else
+ spfi_data->gpio_requested = true;
+ } else {
+ if (gpio_is_valid(spi->cs_gpio)) {
+ int mode = ((spi->mode & SPI_CS_HIGH) ?
+ GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH);
+
+ ret = gpio_direction_output(spi->cs_gpio, mode);
+ if (ret)
+ dev_err(&spi->dev, "chipselect gpio %d setup failed (%d)\n",
+ spi->cs_gpio, ret);
+ }
+ }
return ret;
}
static void img_spfi_cleanup(struct spi_device *spi)
{
- gpio_free(spi->cs_gpio);
+ struct img_spfi_device_data *spfi_data = spi_get_ctldata(spi);
+
+ if (spfi_data) {
+ if (spfi_data->gpio_requested)
+ gpio_free(spi->cs_gpio);
+ kfree(spfi_data);
+ spi_set_ctldata(spi, NULL);
+ }
}
static void img_spfi_config(struct spi_master *master, struct spi_device *spi,
@@ -491,6 +525,7 @@ static void img_spfi_config(struct spi_master *master, struct spi_device *spi,
else if (xfer->tx_nbits == SPI_NBITS_QUAD &&
xfer->rx_nbits == SPI_NBITS_QUAD)
val |= SPFI_CONTROL_TMODE_QUAD << SPFI_CONTROL_TMODE_SHIFT;
+ val |= SPFI_CONTROL_SE;
spfi_writel(spfi, val, SPFI_CONTROL);
}