diff options
| author | saturneric <[email protected]> | 2025-01-12 15:50:28 +0000 |
|---|---|---|
| committer | saturneric <[email protected]> | 2025-01-12 15:50:28 +0000 |
| commit | b7c94eb866dd341a28367bdfabd7c2d1b14d688e (patch) | |
| tree | b6df897425af99dbfef5ad4d91a969f1e58fdae6 /drivers/spi/spi-gpio.c | |
| parent | Merge tag 'hwmon-for-v6.13-rc7' of git://git.kernel.org/pub/scm/linux/kernel/... (diff) | |
| download | kernel-b7c94eb866dd341a28367bdfabd7c2d1b14d688e.tar.gz kernel-b7c94eb866dd341a28367bdfabd7c2d1b14d688e.zip | |
fix: drivers patch make raspberry pi 5 work properly
Diffstat (limited to 'drivers/spi/spi-gpio.c')
| -rw-r--r-- | drivers/spi/spi-gpio.c | 97 |
1 files changed, 70 insertions, 27 deletions
diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c index 4f192e013cd6..139641fd2329 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; }; /*----------------------------------------------------------------------*/ @@ -108,12 +111,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" @@ -224,16 +233,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) @@ -253,11 +275,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; } @@ -329,17 +354,48 @@ 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); } +/* + * 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) +{ + int i; + struct spi_gpio *spi_gpio = spi_controller_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; +} + 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); - struct spi_gpio *spi_gpio = spi_controller_get_devdata(host); - int i; #ifdef GENERIC_BITBANG if (!pdata || !pdata->num_chipselect) @@ -351,20 +407,7 @@ static int spi_gpio_probe_pdata(struct platform_device *pdev, */ 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) |
