mirror of
https://github.com/openwrt/openwrt.git
synced 2024-12-21 14:37:57 +00:00
154 lines
4.9 KiB
Diff
154 lines
4.9 KiB
Diff
|
From 78ced8c841ba08b41eacd064e7c7a9b3fe7556ad Mon Sep 17 00:00:00 2001
|
||
|
From: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
|
||
|
Date: Wed, 1 Mar 2023 17:57:11 +0000
|
||
|
Subject: [PATCH 0553/1085] spi: gpio: Fix spi-gpio to correctly implement
|
||
|
sck-idle-input
|
||
|
|
||
|
Formerly, if configured using DT, CS GPIOs were driven from spi.c
|
||
|
and it was possible for CS to be asserted (low) *before* starting
|
||
|
to drive SCK. CS GPIOs have been brought under control of this
|
||
|
driver in both ACPI and DT cases, with a fixup for GPIO polarity.
|
||
|
|
||
|
Signed-off-by: Nick Hollinghurst <nick.hollinghurst@raspberrypi.com>
|
||
|
---
|
||
|
drivers/spi/spi-gpio.c | 76 +++++++++++++++++++++++++++++-------------
|
||
|
1 file changed, 52 insertions(+), 24 deletions(-)
|
||
|
|
||
|
--- a/drivers/spi/spi-gpio.c
|
||
|
+++ b/drivers/spi/spi-gpio.c
|
||
|
@@ -34,8 +34,9 @@ struct spi_gpio {
|
||
|
struct gpio_desc *sck;
|
||
|
struct gpio_desc *miso;
|
||
|
struct gpio_desc *mosi;
|
||
|
- bool sck_idle_input;
|
||
|
struct gpio_desc **cs_gpios;
|
||
|
+ bool sck_idle_input;
|
||
|
+ bool cs_dont_invert;
|
||
|
};
|
||
|
|
||
|
/*----------------------------------------------------------------------*/
|
||
|
@@ -232,12 +233,18 @@ static void spi_gpio_chipselect(struct s
|
||
|
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)
|
||
|
@@ -253,12 +260,14 @@ static int spi_gpio_setup(struct spi_dev
|
||
|
/*
|
||
|
* 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)
|
||
|
status = gpiod_direction_output(cs,
|
||
|
- !(spi->mode & SPI_CS_HIGH));
|
||
|
+ !((spi->mode & SPI_CS_HIGH) ||
|
||
|
+ spi_gpio->cs_dont_invert));
|
||
|
}
|
||
|
|
||
|
if (!status)
|
||
|
@@ -335,6 +344,38 @@ static int spi_gpio_request(struct devic
|
||
|
return PTR_ERR_OR_ZERO(spi_gpio->sck);
|
||
|
}
|
||
|
|
||
|
+/*
|
||
|
+ * 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_master *master,
|
||
|
+ bool gpio_defines_polarity)
|
||
|
+{
|
||
|
+ int i;
|
||
|
+ struct spi_gpio *spi_gpio = spi_master_get_devdata(master);
|
||
|
+
|
||
|
+ 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 < 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]);
|
||
|
+ }
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
#ifdef CONFIG_OF
|
||
|
static const struct of_device_id spi_gpio_dt_ids[] = {
|
||
|
{ .compatible = "spi-gpio" },
|
||
|
@@ -345,10 +386,12 @@ MODULE_DEVICE_TABLE(of, spi_gpio_dt_ids)
|
||
|
static int spi_gpio_probe_dt(struct platform_device *pdev,
|
||
|
struct spi_controller *host)
|
||
|
{
|
||
|
- host->dev.of_node = pdev->dev.of_node;
|
||
|
- host->use_gpio_descriptors = true;
|
||
|
+ struct device *dev = &pdev->dev;
|
||
|
|
||
|
- return 0;
|
||
|
+ host->dev.of_node = dev->of_node;
|
||
|
+ host->num_chipselect = gpiod_count(dev, "cs");
|
||
|
+
|
||
|
+ return spi_gpio_probe_get_cs_gpios(dev, host, true);
|
||
|
}
|
||
|
#else
|
||
|
static inline int spi_gpio_probe_dt(struct platform_device *pdev,
|
||
|
@@ -363,8 +406,6 @@ static int spi_gpio_probe_pdata(struct p
|
||
|
{
|
||
|
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;
|
||
|
|
||
|
#ifdef GENERIC_BITBANG
|
||
|
if (!pdata || !pdata->num_chipselect)
|
||
|
@@ -376,20 +417,7 @@ static int spi_gpio_probe_pdata(struct p
|
||
|
*/
|
||
|
host->num_chipselect = pdata->num_chipselect ?: 1;
|
||
|
|
||
|
- spi_gpio->cs_gpios = devm_kcalloc(dev, host->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);
|
||
|
- if (IS_ERR(spi_gpio->cs_gpios[i]))
|
||
|
- return PTR_ERR(spi_gpio->cs_gpios[i]);
|
||
|
- }
|
||
|
-
|
||
|
- return 0;
|
||
|
+ return spi_gpio_probe_get_cs_gpios(dev, host, false);
|
||
|
}
|
||
|
|
||
|
static int spi_gpio_probe(struct platform_device *pdev)
|