Re: [PATCH 1/7 v1] spi: Optionally use GPIO descriptors for CS GPIOs

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On 2018/12/16 7:38, Linus Walleij wrote:
> This augments the SPI core to optionally use GPIO descriptors
> for chip select on a per-master-driver opt-in basis.
> 
> Drivers using this will rely on the SPI core to look up
> GPIO descriptors associated with the device, such as
> when using device tree or board files with GPIO descriptor
> tables.
> 
> When getting descriptors from the device tree, this will in
> turn activate the code in gpiolib that was
> added in commit 6953c57ab172
> ("gpio: of: Handle SPI chipselect legacy bindings")
> which means that these descriptors are aware of the active
> low semantics that is the default for SPI CS GPIO lines
> and we can assume that all of these are "active high" and
> thus assign SPI_CS_HIGH to all CS lines on the DT path.
> 
> The previously used gpio_set_value() would call down into
> gpiod_set_raw_value() and ignore the polarity inversion
> semantics.
> 
> It seems like many drivers go to great lengths to set up the
> CS GPIO line as non-asserted, respecting SPI_CS_HIGH. We pull
> this out of the SPI drivers and into the core, and by simply
> requesting the line as GPIOD_OUT_LOW when retrieveing it from
> the device and relying on the gpiolib to handle any inversion
> semantics. This way a lot of code can be simplified and
> removed in each converted driver.
> 
> The end goal after dealing with each driver in turn, is to
> delete the non-descriptor path (of_spi_register_master() for
> example) and let the core deal with only descriptors.
> 
> The different SPI drivers have complex interactions with the
> core so we cannot simply change them all over, we need to use
> a stepwise, bisectable approach so that each driver can be
> converted and fixed in isolation.
> 
> Cc: Linuxarm <linuxarm@xxxxxxxxxx>
> Signed-off-by: Linus Walleij <linus.walleij@xxxxxxxxxx>
> ---
> Jay, I think this patch should cover your ACPI usecase
> as well, as in subject "spi: add ACPI support for SPI controller
> chip select lines(cs-gpios)"
> ---

Thank you Linus
Yes, this patch cover my ACPI usecase already.
I have tested the patch on my board, and it works well.


>  drivers/spi/spi.c       | 105 ++++++++++++++++++++++++++++++++++++----
>  include/linux/spi/spi.h |  23 +++++++--
>  2 files changed, 114 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index 6ca59406b0b7..05e73290cdf3 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -21,6 +21,7 @@
>  #include <linux/spi/spi.h>
>  #include <linux/spi/spi-mem.h>
>  #include <linux/of_gpio.h>
> +#include <linux/gpio/consumer.h>
>  #include <linux/pm_runtime.h>
>  #include <linux/pm_domain.h>
>  #include <linux/property.h>
> @@ -509,6 +510,7 @@ struct spi_device *spi_alloc_device(struct spi_controller *ctlr)
>  	spi->dev.bus = &spi_bus_type;
>  	spi->dev.release = spidev_release;
>  	spi->cs_gpio = -ENOENT;
> +	spi->cs_gpiod = NULL;
>  
>  	spin_lock_init(&spi->statistics.lock);
>  
> @@ -580,7 +582,10 @@ int spi_add_device(struct spi_device *spi)
>  		goto done;
>  	}
>  
> -	if (ctlr->cs_gpios)
> +	/* Descriptors take precedence */
> +	if (ctlr->cs_gpiods)
> +		spi->cs_gpiod = ctlr->cs_gpiods[spi->chip_select];
> +	else if (ctlr->cs_gpios)
>  		spi->cs_gpio = ctlr->cs_gpios[spi->chip_select];
>  
>  	/* Drivers may modify this initial i/o setup, but will
> @@ -774,10 +779,20 @@ static void spi_set_cs(struct spi_device *spi, bool enable)
>  	if (spi->mode & SPI_CS_HIGH)
>  		enable = !enable;
>  
> -	if (gpio_is_valid(spi->cs_gpio)) {
> -		/* Honour the SPI_NO_CS flag */
> -		if (!(spi->mode & SPI_NO_CS))
> -			gpio_set_value(spi->cs_gpio, !enable);
> +	if (spi->cs_gpiod || gpio_is_valid(spi->cs_gpio)) {
> +		/*
> +		 * Honour the SPI_NO_CS flag and invert the enable line, as
> +		 * active low is default for SPI. Execution paths that handle
> +		 * polarity inversion in gpiolib (such as device tree) will
> +		 * enforce active high using the SPI_CS_HIGH resulting in a
> +		 * double inversion through the code above.
> +		 */
> +		if (!(spi->mode & SPI_NO_CS)) {
> +			if (spi->cs_gpiod)
> +				gpiod_set_value(spi->cs_gpiod, !enable);
> +			else
> +				gpio_set_value(spi->cs_gpio, !enable);
> +		}
>  		/* Some SPI masters need both GPIO CS & slave_select */
>  		if ((spi->controller->flags & SPI_MASTER_GPIO_SS) &&
>  		    spi->controller->set_cs)
> @@ -1599,13 +1614,21 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
>  		spi->mode |= SPI_CPHA;
>  	if (of_property_read_bool(nc, "spi-cpol"))
>  		spi->mode |= SPI_CPOL;
> -	if (of_property_read_bool(nc, "spi-cs-high"))
> -		spi->mode |= SPI_CS_HIGH;
>  	if (of_property_read_bool(nc, "spi-3wire"))
>  		spi->mode |= SPI_3WIRE;
>  	if (of_property_read_bool(nc, "spi-lsb-first"))
>  		spi->mode |= SPI_LSB_FIRST;
>  
> +	/*
> +	 * For descriptors associated with the device, polarity inversion is
> +	 * handled in the gpiolib, so all chip selects are "active high" in
> +	 * the logical sense, the gpiolib will invert the line if need be.
> +	 */
> +	if (ctlr->use_gpio_descriptors)
> +		spi->mode |= SPI_CS_HIGH;
> +	else if (of_property_read_bool(nc, "spi-cs-high"))
> +		spi->mode |= SPI_CS_HIGH;
> +
>  	/* Device DUAL/QUAD mode */
>  	if (!of_property_read_u32(nc, "spi-tx-bus-width", &value)) {
>  		switch (value) {
> @@ -2115,6 +2138,60 @@ static int of_spi_register_master(struct spi_controller *ctlr)
>  }
>  #endif
>  
> +/**
> + * spi_get_gpio_descs() - grab chip select GPIOs for the master
> + * @ctlr: The SPI master to grab GPIO descriptors for
> + */
> +static int spi_get_gpio_descs(struct spi_controller *ctlr)
> +{
> +	int nb, i;
> +	struct gpio_desc **cs;
> +	struct device *dev = &ctlr->dev;
> +
> +	nb = gpiod_count(dev, "cs");
> +	ctlr->num_chipselect = max_t(int, nb, ctlr->num_chipselect);
> +
> +	/* No GPIOs at all is fine, else return the error */
> +	if (nb == 0 || nb == -ENOENT)
> +		return 0;
> +	else if (nb < 0)
> +		return nb;
> +
> +	cs = devm_kcalloc(dev, ctlr->num_chipselect, sizeof(*cs),
> +			  GFP_KERNEL);
> +	if (!cs)
> +		return -ENOMEM;
> +	ctlr->cs_gpiods = cs;
> +
> +	for (i = 0; i < nb; i++) {
> +		/*
> +		 * Most chipselects are active low, the inverted
> +		 * semantics are handled by special quirks in gpiolib,
> +		 * so initializing them GPIOD_OUT_LOW here means
> +		 * "unasserted", in most cases the will drive the physical
> +		 * line high.
> +		 */
> +		cs[i] = devm_gpiod_get_index_optional(dev, "cs", i,
> +						      GPIOD_OUT_LOW);
> +
> +		if (cs[i]) {
> +			/*
> +			 * If we find a CS GPIO, name it after the device and
> +			 * chip select line.
> +			 */
> +			char *gpioname;
> +
> +			gpioname = devm_kasprintf(dev, GFP_KERNEL, "%s CS%d",
> +						  dev_name(dev), i);
> +			if (!gpioname)
> +				return -ENOMEM;
> +			gpiod_set_consumer_name(cs[i], gpioname);
> +		}
> +	}
> +
> +	return 0;
> +}
> +
>  static int spi_controller_check_ops(struct spi_controller *ctlr)
>  {
>  	/*
> @@ -2177,9 +2254,16 @@ int spi_register_controller(struct spi_controller *ctlr)
>  		return status;
>  
>  	if (!spi_controller_is_slave(ctlr)) {
> -		status = of_spi_register_master(ctlr);
> -		if (status)
> -			return status;
> +		if (ctlr->use_gpio_descriptors) {
> +			status = spi_get_gpio_descs(ctlr);
> +			if (status)
> +				return status;
> +		} else {
> +			/* Legacy code path for GPIOs from DT */
> +			status = of_spi_register_master(ctlr);
> +			if (status)
> +				return status;
> +		}
>  	}
>  
>  	/* even if it's just one always-selected device, there must
> @@ -2891,6 +2975,7 @@ static int __spi_validate(struct spi_device *spi, struct spi_message *message)
>  	 * cs_change is set for each transfer.
>  	 */
>  	if ((spi->mode & SPI_CS_WORD) && (!(ctlr->mode_bits & SPI_CS_WORD) ||
> +					  spi->cs_gpiod ||
>  					  gpio_is_valid(spi->cs_gpio))) {
>  		size_t maxsize;
>  		int ret;
> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
> index 6be77fa5ab90..68d77cfc8bb7 100644
> --- a/include/linux/spi/spi.h
> +++ b/include/linux/spi/spi.h
> @@ -12,6 +12,7 @@
>  #include <linux/kthread.h>
>  #include <linux/completion.h>
>  #include <linux/scatterlist.h>
> +#include <linux/gpio/consumer.h>
>  
>  struct dma_chan;
>  struct property_entry;
> @@ -116,7 +117,10 @@ void spi_statistics_add_transfer_stats(struct spi_statistics *stats,
>   * @modalias: Name of the driver to use with this device, or an alias
>   *	for that name.  This appears in the sysfs "modalias" attribute
>   *	for driver coldplugging, and in uevents used for hotplugging
> - * @cs_gpio: gpio number of the chipselect line (optional, -ENOENT when
> + * @cs_gpio: LEGACY: gpio number of the chipselect line (optional, -ENOENT when
> + *	not using a GPIO line) use cs_gpiod in new drivers by opting in on
> + *	the spi_master.
> + * @cs_gpiod: gpio descriptor of the chipselect line (optional, NULL when
>   *	not using a GPIO line)
>   *
>   * @statistics: statistics for the spi_device
> @@ -160,7 +164,8 @@ struct spi_device {
>  	void			*controller_data;
>  	char			modalias[SPI_NAME_SIZE];
>  	const char		*driver_override;
> -	int			cs_gpio;	/* chip select gpio */
> +	int			cs_gpio;	/* LEGACY: chip select gpio */
> +	struct gpio_desc	*cs_gpiod;	/* chip select gpio desc */
>  
>  	/* the statistics */
>  	struct spi_statistics	statistics;
> @@ -373,9 +378,17 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
>   *	     controller has native support for memory like operations.
>   * @unprepare_message: undo any work done by prepare_message().
>   * @slave_abort: abort the ongoing transfer request on an SPI slave controller
> - * @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
> - *	number. Any individual value may be -ENOENT for CS lines that
> + * @cs_gpios: LEGACY: array of GPIO descs to use as chip select lines; one per
> + *	CS number. Any individual value may be -ENOENT for CS lines that
> + *	are not GPIOs (driven by the SPI controller itself). Use the cs_gpiods
> + *	in new drivers.
> + * @cs_gpiods: Array of GPIO descs to use as chip select lines; one per CS
> + *	number. Any individual value may be NULL for CS lines that
>   *	are not GPIOs (driven by the SPI controller itself).
> + * @use_gpio_descriptors: Turns on the code in the SPI core to parse and grab
> + *	GPIO descriptors rather than using global GPIO numbers grabbed by the
> + *	driver. This will fill in @cs_gpiods and @cs_gpios should not be used,
> + *	and SPI devices will have the cs_gpiod assigned rather than cs_gpio.
>   * @statistics: statistics for the spi_controller
>   * @dma_tx: DMA transmit channel
>   * @dma_rx: DMA receive channel
> @@ -554,6 +567,8 @@ struct spi_controller {
>  
>  	/* gpio chip select */
>  	int			*cs_gpios;
> +	struct gpio_desc	**cs_gpiods;
> +	bool			use_gpio_descriptors;
>  
>  	/* statistics */
>  	struct spi_statistics	statistics;
> 




[Index of Archives]     [Linux SPI]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux