mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 10:00:17 +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 *sck;
|
||||||
struct gpio_desc *miso;
|
struct gpio_desc *miso;
|
||||||
struct gpio_desc *mosi;
|
struct gpio_desc *mosi;
|
||||||
bool sck_idle_input;
|
|
||||||
struct gpio_desc **cs_gpios;
|
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);
|
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) {
|
if (spi_gpio->cs_gpios) {
|
||||||
struct gpio_desc *cs = spi_gpio->cs_gpios[spi_get_chipselect(spi, 0)];
|
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, val);
|
||||||
gpiod_set_value_cansleep(cs, (spi->mode & SPI_CS_HIGH) ? is_active : !is_active);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spi_gpio->sck_idle_input && !is_active)
|
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
|
* The CS GPIOs have already been
|
||||||
* initialized from the descriptor lookup.
|
* initialized from the descriptor lookup.
|
||||||
|
* Here we set them to the non-asserted state.
|
||||||
*/
|
*/
|
||||||
if (spi_gpio->cs_gpios) {
|
if (spi_gpio->cs_gpios) {
|
||||||
cs = spi_gpio->cs_gpios[spi_get_chipselect(spi, 0)];
|
cs = spi_gpio->cs_gpios[spi_get_chipselect(spi, 0)];
|
||||||
if (!spi->controller_state && cs) {
|
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)
|
if (ret)
|
||||||
return 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);
|
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;
|
int i;
|
||||||
|
struct spi_gpio *spi_gpio = spi_controller_get_devdata(master);
|
||||||
|
|
||||||
if (!pdata)
|
spi_gpio->cs_dont_invert = gpio_defines_polarity;
|
||||||
return -ENODEV;
|
spi_gpio->cs_gpios = devm_kcalloc(dev, master->num_chipselect,
|
||||||
|
|
||||||
/* 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,
|
|
||||||
sizeof(*spi_gpio->cs_gpios),
|
sizeof(*spi_gpio->cs_gpios),
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!spi_gpio->cs_gpios)
|
if (!spi_gpio->cs_gpios)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
for (i = 0; i < host->num_chipselect; i++) {
|
for (i = 0; i < master->num_chipselect; i++) {
|
||||||
spi_gpio->cs_gpios[i] = devm_gpiod_get_index(dev, "cs", i,
|
spi_gpio->cs_gpios[i] =
|
||||||
GPIOD_OUT_HIGH);
|
devm_gpiod_get_index(dev, "cs", i,
|
||||||
|
gpio_defines_polarity ?
|
||||||
|
GPIOD_OUT_LOW : GPIOD_OUT_HIGH);
|
||||||
if (IS_ERR(spi_gpio->cs_gpios[i]))
|
if (IS_ERR(spi_gpio->cs_gpios[i]))
|
||||||
return PTR_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;
|
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)
|
static int spi_gpio_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int status;
|
int status;
|
||||||
|
|||||||
Reference in New Issue
Block a user