aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-gpio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/spi/spi-gpio.c')
-rw-r--r--drivers/spi/spi-gpio.c100
1 files changed, 72 insertions, 28 deletions
diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c
index c8dadb532c40..4a0497f0ba0a 100644
--- a/drivers/spi/spi-gpio.c
+++ b/drivers/spi/spi-gpio.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
+#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
@@ -35,6 +36,8 @@ struct spi_gpio {
struct gpio_desc *miso;
struct gpio_desc *mosi;
struct gpio_desc **cs_gpios;
+ bool sck_idle_input;
+ bool cs_dont_invert;
};
/*----------------------------------------------------------------------*/
@@ -80,12 +83,18 @@ static inline int getmiso(const struct spi_device *spi)
}
/*
- * NOTE: this clocks "as fast as we can". It "should" be a function of the
- * requested device clock. Software overhead means we usually have trouble
- * reaching even one Mbit/sec (except when we can inline bitops), so for now
- * we'll just assume we never need additional per-bit slowdowns.
+ * Generic bit-banged GPIO SPI might free-run at something in the range
+ * 1Mbps ~ 10Mbps (depending on the platform), and some SPI devices may
+ * need to be clocked at a lower rate. ndelay() is often implemented by
+ * udelay() with rounding up, so do the delay only for nsecs >= 500
+ * (<= 1Mbps). The conditional test adds a small overhead.
*/
-#define spidelay(nsecs) do {} while (0)
+
+static inline void spidelay(unsigned long nsecs)
+{
+ if (nsecs >= 500)
+ ndelay(nsecs);
+}
#include "spi-bitbang-txrx.h"
@@ -196,16 +205,29 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
struct spi_gpio *spi_gpio = spi_to_spi_gpio(spi);
/* set initial clock line level */
- if (is_active)
- gpiod_set_value_cansleep(spi_gpio->sck, spi->mode & SPI_CPOL);
+ if (is_active) {
+ if (spi_gpio->sck_idle_input)
+ gpiod_direction_output(spi_gpio->sck, spi->mode & SPI_CPOL);
+ else
+ gpiod_set_value_cansleep(spi_gpio->sck, spi->mode & SPI_CPOL);
+ }
- /* Drive chip select line, if we have one */
+ /*
+ * Drive chip select line, if we have one.
+ * SPI chip selects are normally active-low, but when
+ * cs_dont_invert is set, we assume their polarity is
+ * controlled by the GPIO, and write '1' to assert.
+ */
if (spi_gpio->cs_gpios) {
struct gpio_desc *cs = spi_gpio->cs_gpios[spi_get_chipselect(spi, 0)];
+ int val = ((spi->mode & SPI_CS_HIGH) || spi_gpio->cs_dont_invert) ?
+ is_active : !is_active;
- /* SPI chip selects are normally active-low */
- gpiod_set_value_cansleep(cs, (spi->mode & SPI_CS_HIGH) ? is_active : !is_active);
+ gpiod_set_value_cansleep(cs, val);
}
+
+ if (spi_gpio->sck_idle_input && !is_active)
+ gpiod_direction_input(spi_gpio->sck);
}
static void spi_gpio_set_mosi_idle(struct spi_device *spi)
@@ -225,11 +247,14 @@ static int spi_gpio_setup(struct spi_device *spi)
/*
* The CS GPIOs have already been
* initialized from the descriptor lookup.
+ * Here we set them to the non-asserted state.
*/
if (spi_gpio->cs_gpios) {
cs = spi_gpio->cs_gpios[spi_get_chipselect(spi, 0)];
if (!spi->controller_state && cs) {
- ret = gpiod_direction_output(cs, !(spi->mode & SPI_CS_HIGH));
+ ret = gpiod_direction_output(cs,
+ !((spi->mode & SPI_CS_HIGH) ||
+ spi_gpio->cs_dont_invert));
if (ret)
return ret;
}
@@ -301,35 +326,36 @@ static int spi_gpio_request(struct device *dev, struct spi_gpio *spi_gpio)
if (IS_ERR(spi_gpio->miso))
return PTR_ERR(spi_gpio->miso);
+ spi_gpio->sck_idle_input = device_property_read_bool(dev, "sck-idle-input");
spi_gpio->sck = devm_gpiod_get(dev, "sck", GPIOD_OUT_LOW);
return PTR_ERR_OR_ZERO(spi_gpio->sck);
}
-static int spi_gpio_probe_pdata(struct platform_device *pdev,
- struct spi_controller *host)
+/*
+ * In order to implement "sck-idle-input" (which requires SCK
+ * direction and CS level to be switched in a particular order),
+ * we need to control GPIO chip selects from within this driver.
+ */
+
+static int spi_gpio_probe_get_cs_gpios(struct device *dev,
+ struct spi_controller *master,
+ bool gpio_defines_polarity)
{
- struct device *dev = &pdev->dev;
- struct spi_gpio_platform_data *pdata = dev_get_platdata(dev);
- struct spi_gpio *spi_gpio = spi_controller_get_devdata(host);
int i;
+ struct spi_gpio *spi_gpio = spi_controller_get_devdata(master);
- if (!pdata)
- return -ENODEV;
-
- /* It's just one always-selected device, fine to continue */
- if (!pdata->num_chipselect)
- return 0;
-
- host->num_chipselect = pdata->num_chipselect;
- spi_gpio->cs_gpios = devm_kcalloc(dev, host->num_chipselect,
+ spi_gpio->cs_dont_invert = gpio_defines_polarity;
+ spi_gpio->cs_gpios = devm_kcalloc(dev, master->num_chipselect,
sizeof(*spi_gpio->cs_gpios),
GFP_KERNEL);
if (!spi_gpio->cs_gpios)
return -ENOMEM;
- for (i = 0; i < host->num_chipselect; i++) {
- spi_gpio->cs_gpios[i] = devm_gpiod_get_index(dev, "cs", i,
- GPIOD_OUT_HIGH);
+ for (i = 0; i < master->num_chipselect; i++) {
+ spi_gpio->cs_gpios[i] =
+ devm_gpiod_get_index(dev, "cs", i,
+ gpio_defines_polarity ?
+ GPIOD_OUT_LOW : GPIOD_OUT_HIGH);
if (IS_ERR(spi_gpio->cs_gpios[i]))
return PTR_ERR(spi_gpio->cs_gpios[i]);
}
@@ -337,6 +363,24 @@ static int spi_gpio_probe_pdata(struct platform_device *pdev,
return 0;
}
+static int spi_gpio_probe_pdata(struct platform_device *pdev,
+ struct spi_controller *host)
+{
+ struct device *dev = &pdev->dev;
+ struct spi_gpio_platform_data *pdata = dev_get_platdata(dev);
+
+ if (!pdata)
+ return -ENODEV;
+
+ /*
+ * The host needs to think there is a chipselect even if not
+ * connected
+ */
+ host->num_chipselect = pdata->num_chipselect ?: 1;
+
+ return spi_gpio_probe_get_cs_gpios(dev, host, false);
+}
+
static int spi_gpio_probe(struct platform_device *pdev)
{
int status;