summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
authorSifan Naeem <sifan.naeem@imgtec.com>2015-07-29 11:55:26 +0100
committerMark Brown <broonie@kernel.org>2015-08-04 18:46:54 +0100
commitb03ba9e314c12b2127243145b5c1f41b2408de62 (patch)
treee909d33caf1fea825693ef42d3baec7778ea0ae6 /drivers/spi
parent011710e2ab659c7ad6e5e554806414bd7a9508be (diff)
spi: img-spfi: fix multiple calls to request gpio
spfi_setup may be called many times by the spi framework, but gpio_request_one can only be called once without freeing, repeatedly calling gpio_request_one will cause an error to be thrown, which causes the request to spi_setup to be marked as failed. We can have a per-spi_device flag that indicates whether or not the gpio has been requested. If the gpio has already been requested use gpio_direction_output to set the direction of the gpio. Fixes: 8c2c8c03cdcb ("spi: img-spfi: Control CS lines with GPIO") Signed-off-by: Sifan Naeem <sifan.naeem@imgtec.com> Signed-off-by: Mark Brown <broonie@kernel.org> Cc: stable@vger.kernel.org
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/spi-img-spfi.c49
1 files changed, 41 insertions, 8 deletions
diff --git a/drivers/spi/spi-img-spfi.c b/drivers/spi/spi-img-spfi.c
index 83b97418fc8d..1ba90562d72a 100644
--- a/drivers/spi/spi-img-spfi.c
+++ b/drivers/spi/spi-img-spfi.c
@@ -104,6 +104,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);
@@ -440,20 +444,49 @@ 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",
+ 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,