mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 01:49:46 +00:00
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>
This commit is contained in:
committed by
Dom Cobley
parent
f713bcde6a
commit
869c93c56d
@@ -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;
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
@@ -204,12 +205,18 @@ static void spi_gpio_chipselect(struct spi_device *spi, int is_active)
|
||||
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)
|
||||
@@ -233,11 +240,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;
|
||||
}
|
||||
@@ -314,31 +324,31 @@ static int spi_gpio_request(struct device *dev, struct spi_gpio *spi_gpio)
|
||||
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]);
|
||||
}
|
||||
@@ -346,6 +356,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;
|
||||
|
||||
Reference in New Issue
Block a user