diff mbox series

[v10,1/8] spi: Add multi-cs memories support in SPI core

Message ID 20231118135446.52783-2-amit.kumar-mahapatra@amd.com
State New
Headers show
Series spi: Add support for stacked/parallel memories | expand

Commit Message

Mahapatra, Amit Kumar Nov. 18, 2023, 1:54 p.m. UTC
AMD-Xilinx GQSPI controller has two advanced mode that allows the
controller to consider two flashes as one single device.

One of these two mode is the parallel mode in which each byte of data is
stored in both devices, the even bits in the lower flash & the odd bits in
the upper flash. The byte split is automatically handled by the QSPI
controller.

The other mode is the stacked mode in which both the flashes share the
same SPI bus but each of the device contain half of the data. In this mode,
the controller does not follow CS requests but instead internally wires the
two CS levels with the value of the most significant address bit.

For supporting both these modes SPI core need to be updated for providing
multiple CS for a single SPI device.

For adding multi CS support the SPI device need to be aware of all the CS
values. So, the "chip_select" member in the spi_device structure is now an
array that holds all the CS values.

spi_device structure now has a "cs_index_mask" member. This acts as an
index to the chip_select array. If nth bit of spi->cs_index_mask is set
then the driver would assert spi->chip_select[n].

In parallel mode all the chip selects are asserted/de-asserted
simultaneously and each byte of data is stored in both devices, the even
bits in one, the odd bits in the other. The split is automatically handled
by the GQSPI controller. The GQSPI controller supports a maximum of two
flashes connected in parallel mode. A SPI_CONTROLLER_MULTI_CS flag bit is
added in the spi controller flags, through ctlr->flags the spi core
will make sure that the controller is capable of handling multiple chip
selects at once.

For supporting multiple CS via GPIO the cs_gpiod member of the spi_device
structure is now an array that holds the gpio descriptor for each
chipselect.

CS GPIO is not tested due to unavailability of necessary hardware setup.

Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>
---
Hello @Stefen,
We restructured the SPI core implementation, for handling arbitrary number
of devices connected in parallel(multi-cs) or stacked mode. We have tested
this updated patch on native-cs setup but couldn't test cs-gpio due to
unavailability of a proper setup. If possible,  could you please retest
the cs-gpio use case with this updated patch and provide your feedback.
---
 drivers/spi/spi.c       | 192 +++++++++++++++++++++++++++++++---------
 include/linux/spi/spi.h |  51 ++++++++---
 2 files changed, 191 insertions(+), 52 deletions(-)

Comments

Mark Brown Nov. 20, 2023, 2:02 p.m. UTC | #1
On Sat, Nov 18, 2023 at 07:24:39PM +0530, Amit Kumar Mahapatra wrote:
> AMD-Xilinx GQSPI controller has two advanced mode that allows the
> controller to consider two flashes as one single device.

This breaks an x86 allmodconfig build:

/build/stage/linux/sound/pci/hda/cs35l56_hda_spi.c: In function ‘cs35l56_hda_spi
_probe’:
/build/stage/linux/sound/pci/hda/cs35l56_hda_spi.c:32:52: error: passing argumen
t 2 of ‘cs35l56_hda_common_probe’ makes integer from pointer without a cast [-We
rror=int-conversion]
   32 |         ret = cs35l56_hda_common_probe(cs35l56, spi->chip_select);
      |                                                 ~~~^~~~~~~~~~~~~
      |                                                    |
      |                                                    u8 * {aka unsigned ch
ar *}
In file included from /build/stage/linux/sound/pci/hda/cs35l56_hda_spi.c:12:
/build/stage/linux/sound/pci/hda/cs35l56_hda.h:45:63: note: expected ‘int’ but a
rgument is of type ‘u8 *’ {aka ‘unsigned char *’}
   45 | int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id);
      |                                                           ~~~~^~
Stefan Binding Nov. 20, 2023, 2:30 p.m. UTC | #2
On 18/11/2023 13:54, Amit Kumar Mahapatra wrote:
> AMD-Xilinx GQSPI controller has two advanced mode that allows the
> controller to consider two flashes as one single device.
>
> One of these two mode is the parallel mode in which each byte of data is
> stored in both devices, the even bits in the lower flash & the odd bits in
> the upper flash. The byte split is automatically handled by the QSPI
> controller.
>
> The other mode is the stacked mode in which both the flashes share the
> same SPI bus but each of the device contain half of the data. In this mode,
> the controller does not follow CS requests but instead internally wires the
> two CS levels with the value of the most significant address bit.
>
> For supporting both these modes SPI core need to be updated for providing
> multiple CS for a single SPI device.
>
> For adding multi CS support the SPI device need to be aware of all the CS
> values. So, the "chip_select" member in the spi_device structure is now an
> array that holds all the CS values.
>
> spi_device structure now has a "cs_index_mask" member. This acts as an
> index to the chip_select array. If nth bit of spi->cs_index_mask is set
> then the driver would assert spi->chip_select[n].
>
> In parallel mode all the chip selects are asserted/de-asserted
> simultaneously and each byte of data is stored in both devices, the even
> bits in one, the odd bits in the other. The split is automatically handled
> by the GQSPI controller. The GQSPI controller supports a maximum of two
> flashes connected in parallel mode. A SPI_CONTROLLER_MULTI_CS flag bit is
> added in the spi controller flags, through ctlr->flags the spi core
> will make sure that the controller is capable of handling multiple chip
> selects at once.
>
> For supporting multiple CS via GPIO the cs_gpiod member of the spi_device
> structure is now an array that holds the gpio descriptor for each
> chipselect.
>
> CS GPIO is not tested due to unavailability of necessary hardware setup.
>
> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>
> ---
> Hello @Stefen,
> We restructured the SPI core implementation, for handling arbitrary number
> of devices connected in parallel(multi-cs) or stacked mode. We have tested
> this updated patch on native-cs setup but couldn't test cs-gpio due to
> unavailability of a proper setup. If possible,  could you please retest
> the cs-gpio use case with this updated patch and provide your feedback.

Hi,

I tested this chain on 2 different systems using GPIOs as chip selects, 
and see the
same error on both:
[    2.842045] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use

Let me know if you need any further testing.

Thanks,

Stefan Binding

> ---
>   drivers/spi/spi.c       | 192 +++++++++++++++++++++++++++++++---------
>   include/linux/spi/spi.h |  51 ++++++++---
>   2 files changed, 191 insertions(+), 52 deletions(-)
>
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index 8ead7acb99f3..ff66147ba95f 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -612,10 +612,21 @@ static int spi_dev_check(struct device *dev, void *data)
>   {
>   	struct spi_device *spi = to_spi_device(dev);
>   	struct spi_device *new_spi = data;
> -
> -	if (spi->controller == new_spi->controller &&
> -	    spi_get_chipselect(spi, 0) == spi_get_chipselect(new_spi, 0))
> -		return -EBUSY;
> +	int idx, nw_idx;
> +	u8 cs, cs_nw;
> +
> +	if (spi->controller == new_spi->controller) {
> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> +			cs = spi_get_chipselect(spi, idx);
> +			for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
> +				cs_nw = spi_get_chipselect(spi, nw_idx);
> +				if (cs != 0xFF && cs_nw != 0xFF && cs == cs_nw) {
> +					dev_err(dev, "chipselect %d already in use\n", cs_nw);
> +					return -EBUSY;
> +				}
> +			}
> +		}
> +	}
>   	return 0;
>   }
>   
> @@ -629,13 +640,32 @@ static int __spi_add_device(struct spi_device *spi)
>   {
>   	struct spi_controller *ctlr = spi->controller;
>   	struct device *dev = ctlr->dev.parent;
> -	int status;
> +	int status, idx, nw_idx;
> +	u8 cs, nw_cs;
> +
> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> +		/* Chipselects are numbered 0..max; validate. */
> +		cs = spi_get_chipselect(spi, idx);
> +		if (cs != 0xFF && cs >= ctlr->num_chipselect) {
> +			dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, idx),
> +				ctlr->num_chipselect);
> +			return -EINVAL;
> +		}
> +	}
>   
> -	/* Chipselects are numbered 0..max; validate. */
> -	if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
> -		dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0),
> -			ctlr->num_chipselect);
> -		return -EINVAL;
> +	/*
> +	 * Make sure that multiple logical CS doesn't map to the same physical CS.
> +	 * For example, spi->chip_select[0] != spi->chip_select[1] and so on.
> +	 */
> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> +		cs = spi_get_chipselect(spi, idx);
> +		for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
> +			nw_cs = spi_get_chipselect(spi, nw_idx);
> +			if (cs != 0xFF && nw_cs != 0xFF && cs == nw_cs) {
> +				dev_err(dev, "chipselect %d already in use\n", nw_cs);
> +				return -EBUSY;
> +			}
> +		}
>   	}
>   
>   	/* Set the bus ID string */
> @@ -647,11 +677,8 @@ static int __spi_add_device(struct spi_device *spi)
>   	 * its configuration.
>   	 */
>   	status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
> -	if (status) {
> -		dev_err(dev, "chipselect %d already in use\n",
> -				spi_get_chipselect(spi, 0));
> +	if (status)
>   		return status;
> -	}
>   
>   	/* Controller may unregister concurrently */
>   	if (IS_ENABLED(CONFIG_SPI_DYNAMIC) &&
> @@ -659,8 +686,15 @@ static int __spi_add_device(struct spi_device *spi)
>   		return -ENODEV;
>   	}
>   
> -	if (ctlr->cs_gpiods)
> -		spi_set_csgpiod(spi, 0, ctlr->cs_gpiods[spi_get_chipselect(spi, 0)]);
> +	if (ctlr->cs_gpiods) {
> +		u8 cs;
> +
> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> +			cs = spi_get_chipselect(spi, idx);
> +			if (cs != 0xFF)
> +				spi_set_csgpiod(spi, idx, ctlr->cs_gpiods[cs]);
> +		}
> +	}
>   
>   	/*
>   	 * Drivers may modify this initial i/o setup, but will
> @@ -701,6 +735,9 @@ int spi_add_device(struct spi_device *spi)
>   	struct spi_controller *ctlr = spi->controller;
>   	int status;
>   
> +	/* Set the bus ID string */
> +	spi_dev_set_name(spi);
> +
>   	mutex_lock(&ctlr->add_lock);
>   	status = __spi_add_device(spi);
>   	mutex_unlock(&ctlr->add_lock);
> @@ -942,32 +979,51 @@ static void spi_res_release(struct spi_controller *ctlr, struct spi_message *mes
>   }
>   
>   /*-------------------------------------------------------------------------*/
> +static inline bool spi_is_last_cs(struct spi_device *spi)
> +{
> +	u8 idx;
> +	bool last = false;
> +
> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> +		if ((spi->cs_index_mask >> idx) & 0x01) {
> +			if (spi->controller->last_cs[idx] == spi_get_chipselect(spi, idx))
> +				last = true;
> +		}
> +	}
> +	return last;
> +}
> +
>   
>   static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
>   {
>   	bool activate = enable;
> +	u8 idx;
>   
>   	/*
>   	 * Avoid calling into the driver (or doing delays) if the chip select
>   	 * isn't actually changing from the last time this was called.
>   	 */
> -	if (!force && ((enable && spi->controller->last_cs == spi_get_chipselect(spi, 0)) ||
> -		       (!enable && spi->controller->last_cs != spi_get_chipselect(spi, 0))) &&
> +	if (!force && ((enable && spi->controller->last_cs_index_mask == spi->cs_index_mask &&
> +			spi_is_last_cs(spi)) ||
> +		       (!enable && spi->controller->last_cs_index_mask == spi->cs_index_mask &&
> +			!spi_is_last_cs(spi))) &&
>   	    (spi->controller->last_cs_mode_high == (spi->mode & SPI_CS_HIGH)))
>   		return;
>   
>   	trace_spi_set_cs(spi, activate);
>   
> -	spi->controller->last_cs = enable ? spi_get_chipselect(spi, 0) : -1;
> +	spi->controller->last_cs_index_mask = spi->cs_index_mask;
> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> +		spi->controller->last_cs[idx] = enable ? spi_get_chipselect(spi, 0) : -1;
>   	spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;
>   
> -	if ((spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) && !activate)
> -		spi_delay_exec(&spi->cs_hold, NULL);
> -
>   	if (spi->mode & SPI_CS_HIGH)
>   		enable = !enable;
>   
> -	if (spi_get_csgpiod(spi, 0)) {
> +	if (spi_is_csgpiod(spi)) {
> +		if (!spi->controller->set_cs_timing && !activate)
> +			spi_delay_exec(&spi->cs_hold, NULL);
> +
>   		if (!(spi->mode & SPI_NO_CS)) {
>   			/*
>   			 * Historically ACPI has no means of the GPIO polarity and
> @@ -979,26 +1035,38 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
>   			 * ambiguity. That's why we use enable, that takes SPI_CS_HIGH
>   			 * into account.
>   			 */
> -			if (has_acpi_companion(&spi->dev))
> -				gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), !enable);
> -			else
> -				/* Polarity handled by GPIO library */
> -				gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), activate);
> +			for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> +				if (((spi->cs_index_mask >> idx) & 0x01) &&
> +				    spi_get_csgpiod(spi, idx)) {
> +					if (has_acpi_companion(&spi->dev))
> +						gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
> +									 !enable);
> +					else
> +						/* Polarity handled by GPIO library */
> +						gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
> +									 activate);
> +
> +					if (activate)
> +						spi_delay_exec(&spi->cs_setup, NULL);
> +					else
> +						spi_delay_exec(&spi->cs_inactive, NULL);
> +				}
> +			}
>   		}
>   		/* Some SPI masters need both GPIO CS & slave_select */
>   		if ((spi->controller->flags & SPI_CONTROLLER_GPIO_SS) &&
>   		    spi->controller->set_cs)
>   			spi->controller->set_cs(spi, !enable);
> +
> +		if (!spi->controller->set_cs_timing) {
> +			if (activate)
> +				spi_delay_exec(&spi->cs_setup, NULL);
> +			else
> +				spi_delay_exec(&spi->cs_inactive, NULL);
> +		}
>   	} else if (spi->controller->set_cs) {
>   		spi->controller->set_cs(spi, !enable);
>   	}
> -
> -	if (spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) {
> -		if (activate)
> -			spi_delay_exec(&spi->cs_setup, NULL);
> -		else
> -			spi_delay_exec(&spi->cs_inactive, NULL);
> -	}
>   }
>   
>   #ifdef CONFIG_HAS_DMA
> @@ -2222,8 +2290,8 @@ static void of_spi_parse_dt_cs_delay(struct device_node *nc,
>   static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
>   			   struct device_node *nc)
>   {
> -	u32 value;
> -	int rc;
> +	u32 value, cs[SPI_CS_CNT_MAX];
> +	int rc, idx;
>   
>   	/* Mode (clock phase/polarity/etc.) */
>   	if (of_property_read_bool(nc, "spi-cpha"))
> @@ -2295,14 +2363,52 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
>   		return 0;
>   	}
>   
> +	if (ctlr->num_chipselect > SPI_CS_CNT_MAX) {
> +		dev_err(&ctlr->dev, "No. of CS is more than max. no. of supported CS\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * Zero(0) is a valid physical CS value and can be located at any
> +	 * logical CS in the spi->chip_select[]. If all the physical CS
> +	 * are initialized to 0 then It would be difficult to differentiate
> +	 * between a valid physical CS 0 & an unused logical CS whose physical
> +	 * CS can be 0. As a solution to this issue initialize all the CS to 0xFF.
> +	 * Now all the unused logical CS will have 0xFF physical CS value & can be
> +	 * ignore while performing physical CS validity checks.
> +	 */
> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> +		spi_set_chipselect(spi, idx, 0xFF);
> +
>   	/* Device address */
> -	rc = of_property_read_u32(nc, "reg", &value);
> -	if (rc) {
> +	rc = of_property_read_variable_u32_array(nc, "reg", &cs[0], 1,
> +						 SPI_CS_CNT_MAX);
> +	if (rc < 0) {
>   		dev_err(&ctlr->dev, "%pOF has no valid 'reg' property (%d)\n",
>   			nc, rc);
>   		return rc;
>   	}
> -	spi_set_chipselect(spi, 0, value);
> +	if (rc > ctlr->num_chipselect) {
> +		dev_err(&ctlr->dev, "%pOF has number of CS > ctlr->num_chipselect (%d)\n",
> +			nc, rc);
> +		return rc;
> +	}
> +	if ((of_property_read_bool(nc, "parallel-memories")) &&
> +	    (!(ctlr->flags & SPI_CONTROLLER_MULTI_CS))) {
> +		dev_err(&ctlr->dev, "SPI controller doesn't support multi CS\n");
> +		return -EINVAL;
> +	}
> +	for (idx = 0; idx < rc; idx++)
> +		spi_set_chipselect(spi, idx, cs[idx]);
> +
> +	/* spi->chip_select[i] gives the corresponding physical CS for logical CS i
> +	 * logical CS number is represented by setting the ith bit in spi->cs_index_mask
> +	 * So, for example, if spi->cs_index_mask = 0x01 then logical CS number is 0 and
> +	 * spi->chip_select[0] will give the physical CS.
> +	 * By default spi->chip_select[0] will hold the physical CS number so, set
> +	 * spi->cs_index_mask as 0x01.
> +	 */
> +	spi->cs_index_mask = 0x01;
>   
>   	/* Device speed */
>   	if (!of_property_read_u32(nc, "spi-max-frequency", &value))
> @@ -3100,6 +3206,7 @@ int spi_register_controller(struct spi_controller *ctlr)
>   	struct boardinfo	*bi;
>   	int			first_dynamic;
>   	int			status;
> +	int			idx;
>   
>   	if (!dev)
>   		return -ENODEV;
> @@ -3164,7 +3271,8 @@ int spi_register_controller(struct spi_controller *ctlr)
>   	}
>   
>   	/* Setting last_cs to -1 means no chip selected */
> -	ctlr->last_cs = -1;
> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> +		ctlr->last_cs[idx] = -1;
>   
>   	status = device_add(&ctlr->dev);
>   	if (status < 0)
> @@ -3889,7 +3997,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_get_csgpiod(spi, 0))) {
> +					  spi_is_csgpiod(spi))) {
>   		size_t maxsize = BITS_TO_BYTES(spi->bits_per_word);
>   		int ret;
>   
> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
> index 7b4baff63c5c..871d3a6d879b 100644
> --- a/include/linux/spi/spi.h
> +++ b/include/linux/spi/spi.h
> @@ -20,6 +20,9 @@
>   
>   #include <uapi/linux/spi/spi.h>
>   
> +/* Max no. of CS supported per spi device */
> +#define SPI_CS_CNT_MAX 4
> +
>   struct dma_chan;
>   struct software_node;
>   struct ptp_system_timestamp;
> @@ -132,7 +135,8 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg,
>    * @max_speed_hz: Maximum clock rate to be used with this chip
>    *	(on this board); may be changed by the device's driver.
>    *	The spi_transfer.speed_hz can override this for each transfer.
> - * @chip_select: Chipselect, distinguishing chips handled by @controller.
> + * @chip_select: Array of physical chipselect, spi->chipselect[i] gives
> + *	the corresponding physical CS for logical CS i.
>    * @mode: The spi mode defines how data is clocked out and in.
>    *	This may be changed by the device's driver.
>    *	The "active low" default for chipselect mode can be overridden
> @@ -157,8 +161,8 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg,
>    *	the device will bind to the named driver and only the named driver.
>    *	Do not set directly, because core frees it; use driver_set_override() to
>    *	set or clear it.
> - * @cs_gpiod: GPIO descriptor of the chipselect line (optional, NULL when
> - *	not using a GPIO line)
> + * @cs_gpiod: Array of GPIO descriptors of the corresponding chipselect lines
> + *	(optional, NULL when not using a GPIO line)
>    * @word_delay: delay to be inserted between consecutive
>    *	words of a transfer
>    * @cs_setup: delay to be introduced by the controller after CS is asserted
> @@ -167,6 +171,7 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg,
>    *	deasserted. If @cs_change_delay is used from @spi_transfer, then the
>    *	two delays will be added up.
>    * @pcpu_statistics: statistics for the spi_device
> + * @cs_index_mask: Bit mask of the active chipselect(s) in the chipselect array
>    *
>    * A @spi_device is used to interchange data between an SPI slave
>    * (usually a discrete chip) and CPU memory.
> @@ -182,7 +187,7 @@ struct spi_device {
>   	struct spi_controller	*controller;
>   	struct spi_controller	*master;	/* Compatibility layer */
>   	u32			max_speed_hz;
> -	u8			chip_select;
> +	u8			chip_select[SPI_CS_CNT_MAX];
>   	u8			bits_per_word;
>   	bool			rt;
>   #define SPI_NO_TX		BIT(31)		/* No transmit wire */
> @@ -213,7 +218,7 @@ struct spi_device {
>   	void			*controller_data;
>   	char			modalias[SPI_NAME_SIZE];
>   	const char		*driver_override;
> -	struct gpio_desc	*cs_gpiod;	/* Chip select GPIO descriptor */
> +	struct gpio_desc	*cs_gpiod[SPI_CS_CNT_MAX];	/* Chip select gpio desc */
>   	struct spi_delay	word_delay; /* Inter-word delay */
>   	/* CS delays */
>   	struct spi_delay	cs_setup;
> @@ -223,6 +228,13 @@ struct spi_device {
>   	/* The statistics */
>   	struct spi_statistics __percpu	*pcpu_statistics;
>   
> +	/* Bit mask of the chipselect(s) that the driver need to use from
> +	 * the chipselect array.When the controller is capable to handle
> +	 * multiple chip selects & memories are connected in parallel
> +	 * then more than one bit need to be set in cs_index_mask.
> +	 */
> +	u32			cs_index_mask : SPI_CS_CNT_MAX;
> +
>   	/*
>   	 * Likely need more hooks for more protocol options affecting how
>   	 * the controller talks to each chip, like:
> @@ -279,22 +291,33 @@ static inline void *spi_get_drvdata(const struct spi_device *spi)
>   
>   static inline u8 spi_get_chipselect(const struct spi_device *spi, u8 idx)
>   {
> -	return spi->chip_select;
> +	return spi->chip_select[idx];
>   }
>   
>   static inline void spi_set_chipselect(struct spi_device *spi, u8 idx, u8 chipselect)
>   {
> -	spi->chip_select = chipselect;
> +	spi->chip_select[idx] = chipselect;
>   }
>   
>   static inline struct gpio_desc *spi_get_csgpiod(const struct spi_device *spi, u8 idx)
>   {
> -	return spi->cs_gpiod;
> +	return spi->cs_gpiod[idx];
>   }
>   
>   static inline void spi_set_csgpiod(struct spi_device *spi, u8 idx, struct gpio_desc *csgpiod)
>   {
> -	spi->cs_gpiod = csgpiod;
> +	spi->cs_gpiod[idx] = csgpiod;
> +}
> +
> +static inline bool spi_is_csgpiod(struct spi_device *spi)
> +{
> +	u8 idx;
> +
> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> +		if (spi_get_csgpiod(spi, idx))
> +			return true;
> +	}
> +	return false;
>   }
>   
>   /**
> @@ -399,6 +422,8 @@ extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch
>    * @bus_lock_spinlock: spinlock for SPI bus locking
>    * @bus_lock_mutex: mutex for exclusion of multiple callers
>    * @bus_lock_flag: indicates that the SPI bus is locked for exclusive use
> + * @multi_cs_cap: indicates that the SPI Controller can assert/de-assert
> + *	more than one chip select at once.
>    * @setup: updates the device mode and clocking records used by a
>    *	device's SPI controller; protocol code may call this.  This
>    *	must fail if an unrecognized or unsupported mode is requested.
> @@ -567,6 +592,11 @@ struct spi_controller {
>   #define SPI_CONTROLLER_MUST_TX		BIT(4)	/* Requires tx */
>   #define SPI_CONTROLLER_GPIO_SS		BIT(5)	/* GPIO CS must select slave */
>   #define SPI_CONTROLLER_SUSPENDED	BIT(6)	/* Currently suspended */
> +	/*
> +	 * The spi-controller has multi chip select capability and can
> +	 * assert/de-assert more than one chip select at once.
> +	 */
> +#define SPI_CONTROLLER_MULTI_CS		BIT(7)
>   
>   	/* Flag indicating if the allocation of this struct is devres-managed */
>   	bool			devm_allocated;
> @@ -677,7 +707,8 @@ struct spi_controller {
>   	bool				rt;
>   	bool				auto_runtime_pm;
>   	bool				cur_msg_mapped;
> -	char				last_cs;
> +	char				last_cs[SPI_CS_CNT_MAX];
> +	char				last_cs_index_mask;
>   	bool				last_cs_mode_high;
>   	bool                            fallback;
>   	struct completion               xfer_completion;
Mahapatra, Amit Kumar Nov. 21, 2023, 7:18 a.m. UTC | #3
Hello Mark,

> -----Original Message-----
> From: Mark Brown <broonie@kernel.org>
> Sent: Monday, November 20, 2023 7:33 PM
> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>
> Cc: tudor.ambarus@linaro.org; pratyush@kernel.org;
> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com;
> sbinding@opensource.cirrus.com; linux-spi@vger.kernel.org; linux-
> kernel@vger.kernel.org; michael@walle.cc; linux-mtd@lists.infradead.org;
> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>; linux-
> arm-kernel@lists.infradead.org; git (AMD-Xilinx) <git@amd.com>;
> amitrkcian2002@gmail.com
> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in SPI core
> 
> On Sat, Nov 18, 2023 at 07:24:39PM +0530, Amit Kumar Mahapatra wrote:
> > AMD-Xilinx GQSPI controller has two advanced mode that allows the
> > controller to consider two flashes as one single device.
> 
> This breaks an x86 allmodconfig build:

The cause of this failure are the following patches, which neglects to 
utilize spi_get_chipselect() for retrieving the CS value. Instead, 
it directly accesses spi->chip_select.

https://lore.kernel.org/all/20230525150659.25409-14-rf@opensource.cirrus.com/
https://lore.kernel.org/all/20230216114410.183489-3-jpanis@baylibre.com/

In my upcoming series, I will address these issues.

Regards,
Amit
> 
> /build/stage/linux/sound/pci/hda/cs35l56_hda_spi.c: In function
> ‘cs35l56_hda_spi
> _probe’:
> /build/stage/linux/sound/pci/hda/cs35l56_hda_spi.c:32:52: error: passing
> argumen t 2 of ‘cs35l56_hda_common_probe’ makes integer from pointer
> without a cast [-We rror=int-conversion]
>    32 |         ret = cs35l56_hda_common_probe(cs35l56, spi->chip_select);
>       |                                                 ~~~^~~~~~~~~~~~~
>       |                                                    |
>       |                                                    u8 * {aka unsigned ch
> ar *}
> In file included from /build/stage/linux/sound/pci/hda/cs35l56_hda_spi.c:12:
> /build/stage/linux/sound/pci/hda/cs35l56_hda.h:45:63: note: expected ‘int’
> but a rgument is of type ‘u8 *’ {aka ‘unsigned char *’}
>    45 | int cs35l56_hda_common_probe(struct cs35l56_hda *cs35l56, int id);
>       |                                                           ~~~~^~
Mahapatra, Amit Kumar Nov. 21, 2023, 7:39 a.m. UTC | #4
Hello Stefan,

> -----Original Message-----
> From: Stefan Binding <sbinding@opensource.cirrus.com>
> Sent: Monday, November 20, 2023 8:00 PM
> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> michael@walle.cc; linux-mtd@lists.infradead.org;
> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>; linux-
> arm-kernel@lists.infradead.org; git (AMD-Xilinx) <git@amd.com>;
> amitrkcian2002@gmail.com; patches@opensource.cirrus.com
> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in SPI core
> 
> 
> On 18/11/2023 13:54, Amit Kumar Mahapatra wrote:
> > AMD-Xilinx GQSPI controller has two advanced mode that allows the
> > controller to consider two flashes as one single device.
> >
> > One of these two mode is the parallel mode in which each byte of data
> > is stored in both devices, the even bits in the lower flash & the odd
> > bits in the upper flash. The byte split is automatically handled by
> > the QSPI controller.
> >
> > The other mode is the stacked mode in which both the flashes share the
> > same SPI bus but each of the device contain half of the data. In this
> > mode, the controller does not follow CS requests but instead
> > internally wires the two CS levels with the value of the most significant
> address bit.
> >
> > For supporting both these modes SPI core need to be updated for
> > providing multiple CS for a single SPI device.
> >
> > For adding multi CS support the SPI device need to be aware of all the
> > CS values. So, the "chip_select" member in the spi_device structure is
> > now an array that holds all the CS values.
> >
> > spi_device structure now has a "cs_index_mask" member. This acts as an
> > index to the chip_select array. If nth bit of spi->cs_index_mask is
> > set then the driver would assert spi->chip_select[n].
> >
> > In parallel mode all the chip selects are asserted/de-asserted
> > simultaneously and each byte of data is stored in both devices, the
> > even bits in one, the odd bits in the other. The split is
> > automatically handled by the GQSPI controller. The GQSPI controller
> > supports a maximum of two flashes connected in parallel mode. A
> > SPI_CONTROLLER_MULTI_CS flag bit is added in the spi controller flags,
> > through ctlr->flags the spi core will make sure that the controller is
> > capable of handling multiple chip selects at once.
> >
> > For supporting multiple CS via GPIO the cs_gpiod member of the
> > spi_device structure is now an array that holds the gpio descriptor
> > for each chipselect.
> >
> > CS GPIO is not tested due to unavailability of necessary hardware setup.
> >
> > Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>
> > ---
> > Hello @Stefen,
> > We restructured the SPI core implementation, for handling arbitrary
> > number of devices connected in parallel(multi-cs) or stacked mode. We
> > have tested this updated patch on native-cs setup but couldn't test
> > cs-gpio due to unavailability of a proper setup. If possible,  could
> > you please retest the cs-gpio use case with this updated patch and provide
> your feedback.
> 
> Hi,
> 
> I tested this chain on 2 different systems using GPIOs as chip selects, and see
> the same error on both:
> [    2.842045] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use
> 
> Let me know if you need any further testing.
> 
> Thanks,
> 
> Stefan Binding
> 
> > ---
> >   drivers/spi/spi.c       | 192 +++++++++++++++++++++++++++++++---------
> >   include/linux/spi/spi.h |  51 ++++++++---
> >   2 files changed, 191 insertions(+), 52 deletions(-)
> >
> > diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index
> > 8ead7acb99f3..ff66147ba95f 100644
> > --- a/drivers/spi/spi.c
> > +++ b/drivers/spi/spi.c
> > @@ -612,10 +612,21 @@ static int spi_dev_check(struct device *dev, void
> *data)
> >   {
> >   	struct spi_device *spi = to_spi_device(dev);
> >   	struct spi_device *new_spi = data;
> > -
> > -	if (spi->controller == new_spi->controller &&
> > -	    spi_get_chipselect(spi, 0) == spi_get_chipselect(new_spi, 0))
> > -		return -EBUSY;
> > +	int idx, nw_idx;
> > +	u8 cs, cs_nw;
> > +
> > +	if (spi->controller == new_spi->controller) {
> > +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > +			cs = spi_get_chipselect(spi, idx);
> > +			for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX;
> nw_idx++) {
> > +				cs_nw = spi_get_chipselect(spi, nw_idx);

Thank you for dedicating your time to test my patch.
As per my analysis the error is reported from here as we are 
supplying the former SPI device structure to retrieve the CS 
value for the new SPI devices.
To fix this, could you kindly substitute the above line with 

cs_nw = spi_get_chipselect(new_spi, nw_idx);

and rerun your tests? 
If it works correctly, I will incorporate this fix into my 
upcoming series.

Regards,
Amit

> > +				if (cs != 0xFF && cs_nw != 0xFF && cs ==
> cs_nw) {
> > +					dev_err(dev, "chipselect %d already in
> use\n", cs_nw);
> > +					return -EBUSY;
> > +				}
> > +			}
> > +		}
> > +	}
> >   	return 0;
> >   }
> >
> > @@ -629,13 +640,32 @@ static int __spi_add_device(struct spi_device *spi)
> >   {
> >   	struct spi_controller *ctlr = spi->controller;
> >   	struct device *dev = ctlr->dev.parent;
> > -	int status;
> > +	int status, idx, nw_idx;
> > +	u8 cs, nw_cs;
> > +
> > +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > +		/* Chipselects are numbered 0..max; validate. */
> > +		cs = spi_get_chipselect(spi, idx);
> > +		if (cs != 0xFF && cs >= ctlr->num_chipselect) {
> > +			dev_err(dev, "cs%d >= max %d\n",
> spi_get_chipselect(spi, idx),
> > +				ctlr->num_chipselect);
> > +			return -EINVAL;
> > +		}
> > +	}
> >
> > -	/* Chipselects are numbered 0..max; validate. */
> > -	if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
> > -		dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0),
> > -			ctlr->num_chipselect);
> > -		return -EINVAL;
> > +	/*
> > +	 * Make sure that multiple logical CS doesn't map to the same
> physical CS.
> > +	 * For example, spi->chip_select[0] != spi->chip_select[1] and so on.
> > +	 */
> > +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > +		cs = spi_get_chipselect(spi, idx);
> > +		for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
> > +			nw_cs = spi_get_chipselect(spi, nw_idx);
> > +			if (cs != 0xFF && nw_cs != 0xFF && cs == nw_cs) {
> > +				dev_err(dev, "chipselect %d already in use\n",
> nw_cs);
> > +				return -EBUSY;
> > +			}
> > +		}
> >   	}
> >
> >   	/* Set the bus ID string */
> > @@ -647,11 +677,8 @@ static int __spi_add_device(struct spi_device *spi)
> >   	 * its configuration.
> >   	 */
> >   	status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
> > -	if (status) {
> > -		dev_err(dev, "chipselect %d already in use\n",
> > -				spi_get_chipselect(spi, 0));
> > +	if (status)
> >   		return status;
> > -	}
> >
> >   	/* Controller may unregister concurrently */
> >   	if (IS_ENABLED(CONFIG_SPI_DYNAMIC) && @@ -659,8 +686,15 @@
> static
> > int __spi_add_device(struct spi_device *spi)
> >   		return -ENODEV;
> >   	}
> >
> > -	if (ctlr->cs_gpiods)
> > -		spi_set_csgpiod(spi, 0, ctlr->cs_gpiods[spi_get_chipselect(spi,
> 0)]);
> > +	if (ctlr->cs_gpiods) {
> > +		u8 cs;
> > +
> > +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > +			cs = spi_get_chipselect(spi, idx);
> > +			if (cs != 0xFF)
> > +				spi_set_csgpiod(spi, idx, ctlr->cs_gpiods[cs]);
> > +		}
> > +	}
> >
> >   	/*
> >   	 * Drivers may modify this initial i/o setup, but will @@ -701,6
> > +735,9 @@ int spi_add_device(struct spi_device *spi)
> >   	struct spi_controller *ctlr = spi->controller;
> >   	int status;
> >
> > +	/* Set the bus ID string */
> > +	spi_dev_set_name(spi);
> > +
> >   	mutex_lock(&ctlr->add_lock);
> >   	status = __spi_add_device(spi);
> >   	mutex_unlock(&ctlr->add_lock);
> > @@ -942,32 +979,51 @@ static void spi_res_release(struct spi_controller
> *ctlr, struct spi_message *mes
> >   }
> >
> >
> > /*--------------------------------------------------------------------
> > -----*/
> > +static inline bool spi_is_last_cs(struct spi_device *spi) {
> > +	u8 idx;
> > +	bool last = false;
> > +
> > +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > +		if ((spi->cs_index_mask >> idx) & 0x01) {
> > +			if (spi->controller->last_cs[idx] ==
> spi_get_chipselect(spi, idx))
> > +				last = true;
> > +		}
> > +	}
> > +	return last;
> > +}
> > +
> >
> >   static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
> >   {
> >   	bool activate = enable;
> > +	u8 idx;
> >
> >   	/*
> >   	 * Avoid calling into the driver (or doing delays) if the chip select
> >   	 * isn't actually changing from the last time this was called.
> >   	 */
> > -	if (!force && ((enable && spi->controller->last_cs ==
> spi_get_chipselect(spi, 0)) ||
> > -		       (!enable && spi->controller->last_cs !=
> spi_get_chipselect(spi, 0))) &&
> > +	if (!force && ((enable && spi->controller->last_cs_index_mask == spi-
> >cs_index_mask &&
> > +			spi_is_last_cs(spi)) ||
> > +		       (!enable && spi->controller->last_cs_index_mask == spi-
> >cs_index_mask &&
> > +			!spi_is_last_cs(spi))) &&
> >   	    (spi->controller->last_cs_mode_high == (spi->mode &
> SPI_CS_HIGH)))
> >   		return;
> >
> >   	trace_spi_set_cs(spi, activate);
> >
> > -	spi->controller->last_cs = enable ? spi_get_chipselect(spi, 0) : -1;
> > +	spi->controller->last_cs_index_mask = spi->cs_index_mask;
> > +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> > +		spi->controller->last_cs[idx] = enable ? spi_get_chipselect(spi,
> 0)
> > +: -1;
> >   	spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;
> >
> > -	if ((spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) &&
> !activate)
> > -		spi_delay_exec(&spi->cs_hold, NULL);
> > -
> >   	if (spi->mode & SPI_CS_HIGH)
> >   		enable = !enable;
> >
> > -	if (spi_get_csgpiod(spi, 0)) {
> > +	if (spi_is_csgpiod(spi)) {
> > +		if (!spi->controller->set_cs_timing && !activate)
> > +			spi_delay_exec(&spi->cs_hold, NULL);
> > +
> >   		if (!(spi->mode & SPI_NO_CS)) {
> >   			/*
> >   			 * Historically ACPI has no means of the GPIO polarity
> and @@
> > -979,26 +1035,38 @@ static void spi_set_cs(struct spi_device *spi, bool
> enable, bool force)
> >   			 * ambiguity. That's why we use enable, that takes
> SPI_CS_HIGH
> >   			 * into account.
> >   			 */
> > -			if (has_acpi_companion(&spi->dev))
> > -
> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), !enable);
> > -			else
> > -				/* Polarity handled by GPIO library */
> > -
> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), activate);
> > +			for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > +				if (((spi->cs_index_mask >> idx) & 0x01) &&
> > +				    spi_get_csgpiod(spi, idx)) {
> > +					if (has_acpi_companion(&spi->dev))
> > +
> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
> > +
> !enable);
> > +					else
> > +						/* Polarity handled by GPIO
> library */
> > +
> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
> > +
> activate);
> > +
> > +					if (activate)
> > +						spi_delay_exec(&spi-
> >cs_setup, NULL);
> > +					else
> > +						spi_delay_exec(&spi-
> >cs_inactive, NULL);
> > +				}
> > +			}
> >   		}
> >   		/* Some SPI masters need both GPIO CS & slave_select */
> >   		if ((spi->controller->flags & SPI_CONTROLLER_GPIO_SS) &&
> >   		    spi->controller->set_cs)
> >   			spi->controller->set_cs(spi, !enable);
> > +
> > +		if (!spi->controller->set_cs_timing) {
> > +			if (activate)
> > +				spi_delay_exec(&spi->cs_setup, NULL);
> > +			else
> > +				spi_delay_exec(&spi->cs_inactive, NULL);
> > +		}
> >   	} else if (spi->controller->set_cs) {
> >   		spi->controller->set_cs(spi, !enable);
> >   	}
> > -
> > -	if (spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) {
> > -		if (activate)
> > -			spi_delay_exec(&spi->cs_setup, NULL);
> > -		else
> > -			spi_delay_exec(&spi->cs_inactive, NULL);
> > -	}
> >   }
> >
> >   #ifdef CONFIG_HAS_DMA
> > @@ -2222,8 +2290,8 @@ static void of_spi_parse_dt_cs_delay(struct
> device_node *nc,
> >   static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
> >   			   struct device_node *nc)
> >   {
> > -	u32 value;
> > -	int rc;
> > +	u32 value, cs[SPI_CS_CNT_MAX];
> > +	int rc, idx;
> >
> >   	/* Mode (clock phase/polarity/etc.) */
> >   	if (of_property_read_bool(nc, "spi-cpha")) @@ -2295,14 +2363,52
> @@
> > static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
> >   		return 0;
> >   	}
> >
> > +	if (ctlr->num_chipselect > SPI_CS_CNT_MAX) {
> > +		dev_err(&ctlr->dev, "No. of CS is more than max. no. of
> supported CS\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	/*
> > +	 * Zero(0) is a valid physical CS value and can be located at any
> > +	 * logical CS in the spi->chip_select[]. If all the physical CS
> > +	 * are initialized to 0 then It would be difficult to differentiate
> > +	 * between a valid physical CS 0 & an unused logical CS whose
> physical
> > +	 * CS can be 0. As a solution to this issue initialize all the CS to 0xFF.
> > +	 * Now all the unused logical CS will have 0xFF physical CS value & can
> be
> > +	 * ignore while performing physical CS validity checks.
> > +	 */
> > +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> > +		spi_set_chipselect(spi, idx, 0xFF);
> > +
> >   	/* Device address */
> > -	rc = of_property_read_u32(nc, "reg", &value);
> > -	if (rc) {
> > +	rc = of_property_read_variable_u32_array(nc, "reg", &cs[0], 1,
> > +						 SPI_CS_CNT_MAX);
> > +	if (rc < 0) {
> >   		dev_err(&ctlr->dev, "%pOF has no valid 'reg' property
> (%d)\n",
> >   			nc, rc);
> >   		return rc;
> >   	}
> > -	spi_set_chipselect(spi, 0, value);
> > +	if (rc > ctlr->num_chipselect) {
> > +		dev_err(&ctlr->dev, "%pOF has number of CS > ctlr-
> >num_chipselect (%d)\n",
> > +			nc, rc);
> > +		return rc;
> > +	}
> > +	if ((of_property_read_bool(nc, "parallel-memories")) &&
> > +	    (!(ctlr->flags & SPI_CONTROLLER_MULTI_CS))) {
> > +		dev_err(&ctlr->dev, "SPI controller doesn't support multi
> CS\n");
> > +		return -EINVAL;
> > +	}
> > +	for (idx = 0; idx < rc; idx++)
> > +		spi_set_chipselect(spi, idx, cs[idx]);
> > +
> > +	/* spi->chip_select[i] gives the corresponding physical CS for logical CS
> i
> > +	 * logical CS number is represented by setting the ith bit in spi-
> >cs_index_mask
> > +	 * So, for example, if spi->cs_index_mask = 0x01 then logical CS
> number is 0 and
> > +	 * spi->chip_select[0] will give the physical CS.
> > +	 * By default spi->chip_select[0] will hold the physical CS number so,
> set
> > +	 * spi->cs_index_mask as 0x01.
> > +	 */
> > +	spi->cs_index_mask = 0x01;
> >
> >   	/* Device speed */
> >   	if (!of_property_read_u32(nc, "spi-max-frequency", &value)) @@
> > -3100,6 +3206,7 @@ int spi_register_controller(struct spi_controller *ctlr)
> >   	struct boardinfo	*bi;
> >   	int			first_dynamic;
> >   	int			status;
> > +	int			idx;
> >
> >   	if (!dev)
> >   		return -ENODEV;
> > @@ -3164,7 +3271,8 @@ int spi_register_controller(struct spi_controller
> *ctlr)
> >   	}
> >
> >   	/* Setting last_cs to -1 means no chip selected */
> > -	ctlr->last_cs = -1;
> > +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> > +		ctlr->last_cs[idx] = -1;
> >
> >   	status = device_add(&ctlr->dev);
> >   	if (status < 0)
> > @@ -3889,7 +3997,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_get_csgpiod(spi, 0))) {
> > +					  spi_is_csgpiod(spi))) {
> >   		size_t maxsize = BITS_TO_BYTES(spi->bits_per_word);
> >   		int ret;
> >
> > diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index
> > 7b4baff63c5c..871d3a6d879b 100644
> > --- a/include/linux/spi/spi.h
> > +++ b/include/linux/spi/spi.h
> > @@ -20,6 +20,9 @@
> >
> >   #include <uapi/linux/spi/spi.h>
> >
> > +/* Max no. of CS supported per spi device */ #define SPI_CS_CNT_MAX 4
> > +
> >   struct dma_chan;
> >   struct software_node;
> >   struct ptp_system_timestamp;
> > @@ -132,7 +135,8 @@ extern void
> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> >    * @max_speed_hz: Maximum clock rate to be used with this chip
> >    *	(on this board); may be changed by the device's driver.
> >    *	The spi_transfer.speed_hz can override this for each transfer.
> > - * @chip_select: Chipselect, distinguishing chips handled by @controller.
> > + * @chip_select: Array of physical chipselect, spi->chipselect[i] gives
> > + *	the corresponding physical CS for logical CS i.
> >    * @mode: The spi mode defines how data is clocked out and in.
> >    *	This may be changed by the device's driver.
> >    *	The "active low" default for chipselect mode can be overridden
> > @@ -157,8 +161,8 @@ extern void
> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> >    *	the device will bind to the named driver and only the named driver.
> >    *	Do not set directly, because core frees it; use driver_set_override() to
> >    *	set or clear it.
> > - * @cs_gpiod: GPIO descriptor of the chipselect line (optional, NULL when
> > - *	not using a GPIO line)
> > + * @cs_gpiod: Array of GPIO descriptors of the corresponding chipselect
> lines
> > + *	(optional, NULL when not using a GPIO line)
> >    * @word_delay: delay to be inserted between consecutive
> >    *	words of a transfer
> >    * @cs_setup: delay to be introduced by the controller after CS is
> > asserted @@ -167,6 +171,7 @@ extern void
> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> >    *	deasserted. If @cs_change_delay is used from @spi_transfer, then
> the
> >    *	two delays will be added up.
> >    * @pcpu_statistics: statistics for the spi_device
> > + * @cs_index_mask: Bit mask of the active chipselect(s) in the
> > + chipselect array
> >    *
> >    * A @spi_device is used to interchange data between an SPI slave
> >    * (usually a discrete chip) and CPU memory.
> > @@ -182,7 +187,7 @@ struct spi_device {
> >   	struct spi_controller	*controller;
> >   	struct spi_controller	*master;	/* Compatibility layer */
> >   	u32			max_speed_hz;
> > -	u8			chip_select;
> > +	u8			chip_select[SPI_CS_CNT_MAX];
> >   	u8			bits_per_word;
> >   	bool			rt;
> >   #define SPI_NO_TX		BIT(31)		/* No transmit wire */
> > @@ -213,7 +218,7 @@ struct spi_device {
> >   	void			*controller_data;
> >   	char			modalias[SPI_NAME_SIZE];
> >   	const char		*driver_override;
> > -	struct gpio_desc	*cs_gpiod;	/* Chip select GPIO descriptor
> */
> > +	struct gpio_desc	*cs_gpiod[SPI_CS_CNT_MAX];	/* Chip select
> gpio desc */
> >   	struct spi_delay	word_delay; /* Inter-word delay */
> >   	/* CS delays */
> >   	struct spi_delay	cs_setup;
> > @@ -223,6 +228,13 @@ struct spi_device {
> >   	/* The statistics */
> >   	struct spi_statistics __percpu	*pcpu_statistics;
> >
> > +	/* Bit mask of the chipselect(s) that the driver need to use from
> > +	 * the chipselect array.When the controller is capable to handle
> > +	 * multiple chip selects & memories are connected in parallel
> > +	 * then more than one bit need to be set in cs_index_mask.
> > +	 */
> > +	u32			cs_index_mask : SPI_CS_CNT_MAX;
> > +
> >   	/*
> >   	 * Likely need more hooks for more protocol options affecting how
> >   	 * the controller talks to each chip, like:
> > @@ -279,22 +291,33 @@ static inline void *spi_get_drvdata(const struct
> > spi_device *spi)
> >
> >   static inline u8 spi_get_chipselect(const struct spi_device *spi, u8 idx)
> >   {
> > -	return spi->chip_select;
> > +	return spi->chip_select[idx];
> >   }
> >
> >   static inline void spi_set_chipselect(struct spi_device *spi, u8 idx, u8
> chipselect)
> >   {
> > -	spi->chip_select = chipselect;
> > +	spi->chip_select[idx] = chipselect;
> >   }
> >
> >   static inline struct gpio_desc *spi_get_csgpiod(const struct spi_device *spi,
> u8 idx)
> >   {
> > -	return spi->cs_gpiod;
> > +	return spi->cs_gpiod[idx];
> >   }
> >
> >   static inline void spi_set_csgpiod(struct spi_device *spi, u8 idx, struct
> gpio_desc *csgpiod)
> >   {
> > -	spi->cs_gpiod = csgpiod;
> > +	spi->cs_gpiod[idx] = csgpiod;
> > +}
> > +
> > +static inline bool spi_is_csgpiod(struct spi_device *spi) {
> > +	u8 idx;
> > +
> > +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > +		if (spi_get_csgpiod(spi, idx))
> > +			return true;
> > +	}
> > +	return false;
> >   }
> >
> >   /**
> > @@ -399,6 +422,8 @@ extern struct spi_device
> *spi_new_ancillary_device(struct spi_device *spi, u8 ch
> >    * @bus_lock_spinlock: spinlock for SPI bus locking
> >    * @bus_lock_mutex: mutex for exclusion of multiple callers
> >    * @bus_lock_flag: indicates that the SPI bus is locked for
> > exclusive use
> > + * @multi_cs_cap: indicates that the SPI Controller can assert/de-assert
> > + *	more than one chip select at once.
> >    * @setup: updates the device mode and clocking records used by a
> >    *	device's SPI controller; protocol code may call this.  This
> >    *	must fail if an unrecognized or unsupported mode is requested.
> > @@ -567,6 +592,11 @@ struct spi_controller {
> >   #define SPI_CONTROLLER_MUST_TX		BIT(4)	/* Requires tx */
> >   #define SPI_CONTROLLER_GPIO_SS		BIT(5)	/* GPIO CS must
> select slave */
> >   #define SPI_CONTROLLER_SUSPENDED	BIT(6)	/* Currently
> suspended */
> > +	/*
> > +	 * The spi-controller has multi chip select capability and can
> > +	 * assert/de-assert more than one chip select at once.
> > +	 */
> > +#define SPI_CONTROLLER_MULTI_CS		BIT(7)
> >
> >   	/* Flag indicating if the allocation of this struct is devres-managed */
> >   	bool			devm_allocated;
> > @@ -677,7 +707,8 @@ struct spi_controller {
> >   	bool				rt;
> >   	bool				auto_runtime_pm;
> >   	bool				cur_msg_mapped;
> > -	char				last_cs;
> > +	char				last_cs[SPI_CS_CNT_MAX];
> > +	char				last_cs_index_mask;
> >   	bool				last_cs_mode_high;
> >   	bool                            fallback;
> >   	struct completion               xfer_completion;
Stefan Binding Nov. 21, 2023, 1:58 p.m. UTC | #5
On 21/11/2023 07:39, Mahapatra, Amit Kumar wrote:
> Hello Stefan,
>
>> -----Original Message-----
>> From: Stefan Binding <sbinding@opensource.cirrus.com>
>> Sent: Monday, November 20, 2023 8:00 PM
>> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
>> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
>> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
>> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
>> michael@walle.cc; linux-mtd@lists.infradead.org;
>> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
>> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>; linux-
>> arm-kernel@lists.infradead.org; git (AMD-Xilinx) <git@amd.com>;
>> amitrkcian2002@gmail.com; patches@opensource.cirrus.com
>> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in SPI core
>>
>>
>> On 18/11/2023 13:54, Amit Kumar Mahapatra wrote:
>>> AMD-Xilinx GQSPI controller has two advanced mode that allows the
>>> controller to consider two flashes as one single device.
>>>
>>> One of these two mode is the parallel mode in which each byte of data
>>> is stored in both devices, the even bits in the lower flash & the odd
>>> bits in the upper flash. The byte split is automatically handled by
>>> the QSPI controller.
>>>
>>> The other mode is the stacked mode in which both the flashes share the
>>> same SPI bus but each of the device contain half of the data. In this
>>> mode, the controller does not follow CS requests but instead
>>> internally wires the two CS levels with the value of the most significant
>> address bit.
>>> For supporting both these modes SPI core need to be updated for
>>> providing multiple CS for a single SPI device.
>>>
>>> For adding multi CS support the SPI device need to be aware of all the
>>> CS values. So, the "chip_select" member in the spi_device structure is
>>> now an array that holds all the CS values.
>>>
>>> spi_device structure now has a "cs_index_mask" member. This acts as an
>>> index to the chip_select array. If nth bit of spi->cs_index_mask is
>>> set then the driver would assert spi->chip_select[n].
>>>
>>> In parallel mode all the chip selects are asserted/de-asserted
>>> simultaneously and each byte of data is stored in both devices, the
>>> even bits in one, the odd bits in the other. The split is
>>> automatically handled by the GQSPI controller. The GQSPI controller
>>> supports a maximum of two flashes connected in parallel mode. A
>>> SPI_CONTROLLER_MULTI_CS flag bit is added in the spi controller flags,
>>> through ctlr->flags the spi core will make sure that the controller is
>>> capable of handling multiple chip selects at once.
>>>
>>> For supporting multiple CS via GPIO the cs_gpiod member of the
>>> spi_device structure is now an array that holds the gpio descriptor
>>> for each chipselect.
>>>
>>> CS GPIO is not tested due to unavailability of necessary hardware setup.
>>>
>>> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-mahapatra@amd.com>
>>> ---
>>> Hello @Stefen,
>>> We restructured the SPI core implementation, for handling arbitrary
>>> number of devices connected in parallel(multi-cs) or stacked mode. We
>>> have tested this updated patch on native-cs setup but couldn't test
>>> cs-gpio due to unavailability of a proper setup. If possible,  could
>>> you please retest the cs-gpio use case with this updated patch and provide
>> your feedback.
>>
>> Hi,
>>
>> I tested this chain on 2 different systems using GPIOs as chip selects, and see
>> the same error on both:
>> [    2.842045] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use
>>
>> Let me know if you need any further testing.
>>
>> Thanks,
>>
>> Stefan Binding
>>
>>> ---
>>>    drivers/spi/spi.c       | 192 +++++++++++++++++++++++++++++++---------
>>>    include/linux/spi/spi.h |  51 ++++++++---
>>>    2 files changed, 191 insertions(+), 52 deletions(-)
>>>
>>> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index
>>> 8ead7acb99f3..ff66147ba95f 100644
>>> --- a/drivers/spi/spi.c
>>> +++ b/drivers/spi/spi.c
>>> @@ -612,10 +612,21 @@ static int spi_dev_check(struct device *dev, void
>> *data)
>>>    {
>>>    	struct spi_device *spi = to_spi_device(dev);
>>>    	struct spi_device *new_spi = data;
>>> -
>>> -	if (spi->controller == new_spi->controller &&
>>> -	    spi_get_chipselect(spi, 0) == spi_get_chipselect(new_spi, 0))
>>> -		return -EBUSY;
>>> +	int idx, nw_idx;
>>> +	u8 cs, cs_nw;
>>> +
>>> +	if (spi->controller == new_spi->controller) {
>>> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>> +			cs = spi_get_chipselect(spi, idx);
>>> +			for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX;
>> nw_idx++) {
>>> +				cs_nw = spi_get_chipselect(spi, nw_idx);
> Thank you for dedicating your time to test my patch.
> As per my analysis the error is reported from here as we are
> supplying the former SPI device structure to retrieve the CS
> value for the new SPI devices.
> To fix this, could you kindly substitute the above line with
>
> cs_nw = spi_get_chipselect(new_spi, nw_idx);
>
> and rerun your tests?
> If it works correctly, I will incorporate this fix into my
> upcoming series.
>
> Regards,
> Amit

Hi,

I still see the same error:

[    2.748546] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use

Thanks,

Stefan

>
>>> +				if (cs != 0xFF && cs_nw != 0xFF && cs ==
>> cs_nw) {
>>> +					dev_err(dev, "chipselect %d already in
>> use\n", cs_nw);
>>> +					return -EBUSY;
>>> +				}
>>> +			}
>>> +		}
>>> +	}
>>>    	return 0;
>>>    }
>>>
>>> @@ -629,13 +640,32 @@ static int __spi_add_device(struct spi_device *spi)
>>>    {
>>>    	struct spi_controller *ctlr = spi->controller;
>>>    	struct device *dev = ctlr->dev.parent;
>>> -	int status;
>>> +	int status, idx, nw_idx;
>>> +	u8 cs, nw_cs;
>>> +
>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>> +		/* Chipselects are numbered 0..max; validate. */
>>> +		cs = spi_get_chipselect(spi, idx);
>>> +		if (cs != 0xFF && cs >= ctlr->num_chipselect) {
>>> +			dev_err(dev, "cs%d >= max %d\n",
>> spi_get_chipselect(spi, idx),
>>> +				ctlr->num_chipselect);
>>> +			return -EINVAL;
>>> +		}
>>> +	}
>>>
>>> -	/* Chipselects are numbered 0..max; validate. */
>>> -	if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
>>> -		dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0),
>>> -			ctlr->num_chipselect);
>>> -		return -EINVAL;
>>> +	/*
>>> +	 * Make sure that multiple logical CS doesn't map to the same
>> physical CS.
>>> +	 * For example, spi->chip_select[0] != spi->chip_select[1] and so on.
>>> +	 */
>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>> +		cs = spi_get_chipselect(spi, idx);
>>> +		for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
>>> +			nw_cs = spi_get_chipselect(spi, nw_idx);
>>> +			if (cs != 0xFF && nw_cs != 0xFF && cs == nw_cs) {
>>> +				dev_err(dev, "chipselect %d already in use\n",
>> nw_cs);
>>> +				return -EBUSY;
>>> +			}
>>> +		}
>>>    	}
>>>
>>>    	/* Set the bus ID string */
>>> @@ -647,11 +677,8 @@ static int __spi_add_device(struct spi_device *spi)
>>>    	 * its configuration.
>>>    	 */
>>>    	status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
>>> -	if (status) {
>>> -		dev_err(dev, "chipselect %d already in use\n",
>>> -				spi_get_chipselect(spi, 0));
>>> +	if (status)
>>>    		return status;
>>> -	}
>>>
>>>    	/* Controller may unregister concurrently */
>>>    	if (IS_ENABLED(CONFIG_SPI_DYNAMIC) && @@ -659,8 +686,15 @@
>> static
>>> int __spi_add_device(struct spi_device *spi)
>>>    		return -ENODEV;
>>>    	}
>>>
>>> -	if (ctlr->cs_gpiods)
>>> -		spi_set_csgpiod(spi, 0, ctlr->cs_gpiods[spi_get_chipselect(spi,
>> 0)]);
>>> +	if (ctlr->cs_gpiods) {
>>> +		u8 cs;
>>> +
>>> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>> +			cs = spi_get_chipselect(spi, idx);
>>> +			if (cs != 0xFF)
>>> +				spi_set_csgpiod(spi, idx, ctlr->cs_gpiods[cs]);
>>> +		}
>>> +	}
>>>
>>>    	/*
>>>    	 * Drivers may modify this initial i/o setup, but will @@ -701,6
>>> +735,9 @@ int spi_add_device(struct spi_device *spi)
>>>    	struct spi_controller *ctlr = spi->controller;
>>>    	int status;
>>>
>>> +	/* Set the bus ID string */
>>> +	spi_dev_set_name(spi);
>>> +
>>>    	mutex_lock(&ctlr->add_lock);
>>>    	status = __spi_add_device(spi);
>>>    	mutex_unlock(&ctlr->add_lock);
>>> @@ -942,32 +979,51 @@ static void spi_res_release(struct spi_controller
>> *ctlr, struct spi_message *mes
>>>    }
>>>
>>>
>>> /*--------------------------------------------------------------------
>>> -----*/
>>> +static inline bool spi_is_last_cs(struct spi_device *spi) {
>>> +	u8 idx;
>>> +	bool last = false;
>>> +
>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>> +		if ((spi->cs_index_mask >> idx) & 0x01) {
>>> +			if (spi->controller->last_cs[idx] ==
>> spi_get_chipselect(spi, idx))
>>> +				last = true;
>>> +		}
>>> +	}
>>> +	return last;
>>> +}
>>> +
>>>
>>>    static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
>>>    {
>>>    	bool activate = enable;
>>> +	u8 idx;
>>>
>>>    	/*
>>>    	 * Avoid calling into the driver (or doing delays) if the chip select
>>>    	 * isn't actually changing from the last time this was called.
>>>    	 */
>>> -	if (!force && ((enable && spi->controller->last_cs ==
>> spi_get_chipselect(spi, 0)) ||
>>> -		       (!enable && spi->controller->last_cs !=
>> spi_get_chipselect(spi, 0))) &&
>>> +	if (!force && ((enable && spi->controller->last_cs_index_mask == spi-
>>> cs_index_mask &&
>>> +			spi_is_last_cs(spi)) ||
>>> +		       (!enable && spi->controller->last_cs_index_mask == spi-
>>> cs_index_mask &&
>>> +			!spi_is_last_cs(spi))) &&
>>>    	    (spi->controller->last_cs_mode_high == (spi->mode &
>> SPI_CS_HIGH)))
>>>    		return;
>>>
>>>    	trace_spi_set_cs(spi, activate);
>>>
>>> -	spi->controller->last_cs = enable ? spi_get_chipselect(spi, 0) : -1;
>>> +	spi->controller->last_cs_index_mask = spi->cs_index_mask;
>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
>>> +		spi->controller->last_cs[idx] = enable ? spi_get_chipselect(spi,
>> 0)
>>> +: -1;
>>>    	spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;
>>>
>>> -	if ((spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) &&
>> !activate)
>>> -		spi_delay_exec(&spi->cs_hold, NULL);
>>> -
>>>    	if (spi->mode & SPI_CS_HIGH)
>>>    		enable = !enable;
>>>
>>> -	if (spi_get_csgpiod(spi, 0)) {
>>> +	if (spi_is_csgpiod(spi)) {
>>> +		if (!spi->controller->set_cs_timing && !activate)
>>> +			spi_delay_exec(&spi->cs_hold, NULL);
>>> +
>>>    		if (!(spi->mode & SPI_NO_CS)) {
>>>    			/*
>>>    			 * Historically ACPI has no means of the GPIO polarity
>> and @@
>>> -979,26 +1035,38 @@ static void spi_set_cs(struct spi_device *spi, bool
>> enable, bool force)
>>>    			 * ambiguity. That's why we use enable, that takes
>> SPI_CS_HIGH
>>>    			 * into account.
>>>    			 */
>>> -			if (has_acpi_companion(&spi->dev))
>>> -
>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), !enable);
>>> -			else
>>> -				/* Polarity handled by GPIO library */
>>> -
>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), activate);
>>> +			for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>> +				if (((spi->cs_index_mask >> idx) & 0x01) &&
>>> +				    spi_get_csgpiod(spi, idx)) {
>>> +					if (has_acpi_companion(&spi->dev))
>>> +
>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
>>> +
>> !enable);
>>> +					else
>>> +						/* Polarity handled by GPIO
>> library */
>>> +
>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
>>> +
>> activate);
>>> +
>>> +					if (activate)
>>> +						spi_delay_exec(&spi-
>>> cs_setup, NULL);
>>> +					else
>>> +						spi_delay_exec(&spi-
>>> cs_inactive, NULL);
>>> +				}
>>> +			}
>>>    		}
>>>    		/* Some SPI masters need both GPIO CS & slave_select */
>>>    		if ((spi->controller->flags & SPI_CONTROLLER_GPIO_SS) &&
>>>    		    spi->controller->set_cs)
>>>    			spi->controller->set_cs(spi, !enable);
>>> +
>>> +		if (!spi->controller->set_cs_timing) {
>>> +			if (activate)
>>> +				spi_delay_exec(&spi->cs_setup, NULL);
>>> +			else
>>> +				spi_delay_exec(&spi->cs_inactive, NULL);
>>> +		}
>>>    	} else if (spi->controller->set_cs) {
>>>    		spi->controller->set_cs(spi, !enable);
>>>    	}
>>> -
>>> -	if (spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) {
>>> -		if (activate)
>>> -			spi_delay_exec(&spi->cs_setup, NULL);
>>> -		else
>>> -			spi_delay_exec(&spi->cs_inactive, NULL);
>>> -	}
>>>    }
>>>
>>>    #ifdef CONFIG_HAS_DMA
>>> @@ -2222,8 +2290,8 @@ static void of_spi_parse_dt_cs_delay(struct
>> device_node *nc,
>>>    static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
>>>    			   struct device_node *nc)
>>>    {
>>> -	u32 value;
>>> -	int rc;
>>> +	u32 value, cs[SPI_CS_CNT_MAX];
>>> +	int rc, idx;
>>>
>>>    	/* Mode (clock phase/polarity/etc.) */
>>>    	if (of_property_read_bool(nc, "spi-cpha")) @@ -2295,14 +2363,52
>> @@
>>> static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
>>>    		return 0;
>>>    	}
>>>
>>> +	if (ctlr->num_chipselect > SPI_CS_CNT_MAX) {
>>> +		dev_err(&ctlr->dev, "No. of CS is more than max. no. of
>> supported CS\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	/*
>>> +	 * Zero(0) is a valid physical CS value and can be located at any
>>> +	 * logical CS in the spi->chip_select[]. If all the physical CS
>>> +	 * are initialized to 0 then It would be difficult to differentiate
>>> +	 * between a valid physical CS 0 & an unused logical CS whose
>> physical
>>> +	 * CS can be 0. As a solution to this issue initialize all the CS to 0xFF.
>>> +	 * Now all the unused logical CS will have 0xFF physical CS value & can
>> be
>>> +	 * ignore while performing physical CS validity checks.
>>> +	 */
>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
>>> +		spi_set_chipselect(spi, idx, 0xFF);
>>> +
>>>    	/* Device address */
>>> -	rc = of_property_read_u32(nc, "reg", &value);
>>> -	if (rc) {
>>> +	rc = of_property_read_variable_u32_array(nc, "reg", &cs[0], 1,
>>> +						 SPI_CS_CNT_MAX);
>>> +	if (rc < 0) {
>>>    		dev_err(&ctlr->dev, "%pOF has no valid 'reg' property
>> (%d)\n",
>>>    			nc, rc);
>>>    		return rc;
>>>    	}
>>> -	spi_set_chipselect(spi, 0, value);
>>> +	if (rc > ctlr->num_chipselect) {
>>> +		dev_err(&ctlr->dev, "%pOF has number of CS > ctlr-
>>> num_chipselect (%d)\n",
>>> +			nc, rc);
>>> +		return rc;
>>> +	}
>>> +	if ((of_property_read_bool(nc, "parallel-memories")) &&
>>> +	    (!(ctlr->flags & SPI_CONTROLLER_MULTI_CS))) {
>>> +		dev_err(&ctlr->dev, "SPI controller doesn't support multi
>> CS\n");
>>> +		return -EINVAL;
>>> +	}
>>> +	for (idx = 0; idx < rc; idx++)
>>> +		spi_set_chipselect(spi, idx, cs[idx]);
>>> +
>>> +	/* spi->chip_select[i] gives the corresponding physical CS for logical CS
>> i
>>> +	 * logical CS number is represented by setting the ith bit in spi-
>>> cs_index_mask
>>> +	 * So, for example, if spi->cs_index_mask = 0x01 then logical CS
>> number is 0 and
>>> +	 * spi->chip_select[0] will give the physical CS.
>>> +	 * By default spi->chip_select[0] will hold the physical CS number so,
>> set
>>> +	 * spi->cs_index_mask as 0x01.
>>> +	 */
>>> +	spi->cs_index_mask = 0x01;
>>>
>>>    	/* Device speed */
>>>    	if (!of_property_read_u32(nc, "spi-max-frequency", &value)) @@
>>> -3100,6 +3206,7 @@ int spi_register_controller(struct spi_controller *ctlr)
>>>    	struct boardinfo	*bi;
>>>    	int			first_dynamic;
>>>    	int			status;
>>> +	int			idx;
>>>
>>>    	if (!dev)
>>>    		return -ENODEV;
>>> @@ -3164,7 +3271,8 @@ int spi_register_controller(struct spi_controller
>> *ctlr)
>>>    	}
>>>
>>>    	/* Setting last_cs to -1 means no chip selected */
>>> -	ctlr->last_cs = -1;
>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
>>> +		ctlr->last_cs[idx] = -1;
>>>
>>>    	status = device_add(&ctlr->dev);
>>>    	if (status < 0)
>>> @@ -3889,7 +3997,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_get_csgpiod(spi, 0))) {
>>> +					  spi_is_csgpiod(spi))) {
>>>    		size_t maxsize = BITS_TO_BYTES(spi->bits_per_word);
>>>    		int ret;
>>>
>>> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index
>>> 7b4baff63c5c..871d3a6d879b 100644
>>> --- a/include/linux/spi/spi.h
>>> +++ b/include/linux/spi/spi.h
>>> @@ -20,6 +20,9 @@
>>>
>>>    #include <uapi/linux/spi/spi.h>
>>>
>>> +/* Max no. of CS supported per spi device */ #define SPI_CS_CNT_MAX 4
>>> +
>>>    struct dma_chan;
>>>    struct software_node;
>>>    struct ptp_system_timestamp;
>>> @@ -132,7 +135,8 @@ extern void
>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
>>>     * @max_speed_hz: Maximum clock rate to be used with this chip
>>>     *	(on this board); may be changed by the device's driver.
>>>     *	The spi_transfer.speed_hz can override this for each transfer.
>>> - * @chip_select: Chipselect, distinguishing chips handled by @controller.
>>> + * @chip_select: Array of physical chipselect, spi->chipselect[i] gives
>>> + *	the corresponding physical CS for logical CS i.
>>>     * @mode: The spi mode defines how data is clocked out and in.
>>>     *	This may be changed by the device's driver.
>>>     *	The "active low" default for chipselect mode can be overridden
>>> @@ -157,8 +161,8 @@ extern void
>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
>>>     *	the device will bind to the named driver and only the named driver.
>>>     *	Do not set directly, because core frees it; use driver_set_override() to
>>>     *	set or clear it.
>>> - * @cs_gpiod: GPIO descriptor of the chipselect line (optional, NULL when
>>> - *	not using a GPIO line)
>>> + * @cs_gpiod: Array of GPIO descriptors of the corresponding chipselect
>> lines
>>> + *	(optional, NULL when not using a GPIO line)
>>>     * @word_delay: delay to be inserted between consecutive
>>>     *	words of a transfer
>>>     * @cs_setup: delay to be introduced by the controller after CS is
>>> asserted @@ -167,6 +171,7 @@ extern void
>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
>>>     *	deasserted. If @cs_change_delay is used from @spi_transfer, then
>> the
>>>     *	two delays will be added up.
>>>     * @pcpu_statistics: statistics for the spi_device
>>> + * @cs_index_mask: Bit mask of the active chipselect(s) in the
>>> + chipselect array
>>>     *
>>>     * A @spi_device is used to interchange data between an SPI slave
>>>     * (usually a discrete chip) and CPU memory.
>>> @@ -182,7 +187,7 @@ struct spi_device {
>>>    	struct spi_controller	*controller;
>>>    	struct spi_controller	*master;	/* Compatibility layer */
>>>    	u32			max_speed_hz;
>>> -	u8			chip_select;
>>> +	u8			chip_select[SPI_CS_CNT_MAX];
>>>    	u8			bits_per_word;
>>>    	bool			rt;
>>>    #define SPI_NO_TX		BIT(31)		/* No transmit wire */
>>> @@ -213,7 +218,7 @@ struct spi_device {
>>>    	void			*controller_data;
>>>    	char			modalias[SPI_NAME_SIZE];
>>>    	const char		*driver_override;
>>> -	struct gpio_desc	*cs_gpiod;	/* Chip select GPIO descriptor
>> */
>>> +	struct gpio_desc	*cs_gpiod[SPI_CS_CNT_MAX];	/* Chip select
>> gpio desc */
>>>    	struct spi_delay	word_delay; /* Inter-word delay */
>>>    	/* CS delays */
>>>    	struct spi_delay	cs_setup;
>>> @@ -223,6 +228,13 @@ struct spi_device {
>>>    	/* The statistics */
>>>    	struct spi_statistics __percpu	*pcpu_statistics;
>>>
>>> +	/* Bit mask of the chipselect(s) that the driver need to use from
>>> +	 * the chipselect array.When the controller is capable to handle
>>> +	 * multiple chip selects & memories are connected in parallel
>>> +	 * then more than one bit need to be set in cs_index_mask.
>>> +	 */
>>> +	u32			cs_index_mask : SPI_CS_CNT_MAX;
>>> +
>>>    	/*
>>>    	 * Likely need more hooks for more protocol options affecting how
>>>    	 * the controller talks to each chip, like:
>>> @@ -279,22 +291,33 @@ static inline void *spi_get_drvdata(const struct
>>> spi_device *spi)
>>>
>>>    static inline u8 spi_get_chipselect(const struct spi_device *spi, u8 idx)
>>>    {
>>> -	return spi->chip_select;
>>> +	return spi->chip_select[idx];
>>>    }
>>>
>>>    static inline void spi_set_chipselect(struct spi_device *spi, u8 idx, u8
>> chipselect)
>>>    {
>>> -	spi->chip_select = chipselect;
>>> +	spi->chip_select[idx] = chipselect;
>>>    }
>>>
>>>    static inline struct gpio_desc *spi_get_csgpiod(const struct spi_device *spi,
>> u8 idx)
>>>    {
>>> -	return spi->cs_gpiod;
>>> +	return spi->cs_gpiod[idx];
>>>    }
>>>
>>>    static inline void spi_set_csgpiod(struct spi_device *spi, u8 idx, struct
>> gpio_desc *csgpiod)
>>>    {
>>> -	spi->cs_gpiod = csgpiod;
>>> +	spi->cs_gpiod[idx] = csgpiod;
>>> +}
>>> +
>>> +static inline bool spi_is_csgpiod(struct spi_device *spi) {
>>> +	u8 idx;
>>> +
>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>> +		if (spi_get_csgpiod(spi, idx))
>>> +			return true;
>>> +	}
>>> +	return false;
>>>    }
>>>
>>>    /**
>>> @@ -399,6 +422,8 @@ extern struct spi_device
>> *spi_new_ancillary_device(struct spi_device *spi, u8 ch
>>>     * @bus_lock_spinlock: spinlock for SPI bus locking
>>>     * @bus_lock_mutex: mutex for exclusion of multiple callers
>>>     * @bus_lock_flag: indicates that the SPI bus is locked for
>>> exclusive use
>>> + * @multi_cs_cap: indicates that the SPI Controller can assert/de-assert
>>> + *	more than one chip select at once.
>>>     * @setup: updates the device mode and clocking records used by a
>>>     *	device's SPI controller; protocol code may call this.  This
>>>     *	must fail if an unrecognized or unsupported mode is requested.
>>> @@ -567,6 +592,11 @@ struct spi_controller {
>>>    #define SPI_CONTROLLER_MUST_TX		BIT(4)	/* Requires tx */
>>>    #define SPI_CONTROLLER_GPIO_SS		BIT(5)	/* GPIO CS must
>> select slave */
>>>    #define SPI_CONTROLLER_SUSPENDED	BIT(6)	/* Currently
>> suspended */
>>> +	/*
>>> +	 * The spi-controller has multi chip select capability and can
>>> +	 * assert/de-assert more than one chip select at once.
>>> +	 */
>>> +#define SPI_CONTROLLER_MULTI_CS		BIT(7)
>>>
>>>    	/* Flag indicating if the allocation of this struct is devres-managed */
>>>    	bool			devm_allocated;
>>> @@ -677,7 +707,8 @@ struct spi_controller {
>>>    	bool				rt;
>>>    	bool				auto_runtime_pm;
>>>    	bool				cur_msg_mapped;
>>> -	char				last_cs;
>>> +	char				last_cs[SPI_CS_CNT_MAX];
>>> +	char				last_cs_index_mask;
>>>    	bool				last_cs_mode_high;
>>>    	bool                            fallback;
>>>    	struct completion               xfer_completion;
Mahapatra, Amit Kumar Nov. 21, 2023, 4:35 p.m. UTC | #6
Hello Stefan,

> -----Original Message-----
> From: Stefan Binding <sbinding@opensource.cirrus.com>
> Sent: Tuesday, November 21, 2023 7:28 PM
> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> michael@walle.cc; linux-mtd@lists.infradead.org;
> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>; linux-
> arm-kernel@lists.infradead.org; git (AMD-Xilinx) <git@amd.com>;
> amitrkcian2002@gmail.com; patches@opensource.cirrus.com
> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in SPI core
> 
> 
> On 21/11/2023 07:39, Mahapatra, Amit Kumar wrote:
> > Hello Stefan,
> >
> >> -----Original Message-----
> >> From: Stefan Binding <sbinding@opensource.cirrus.com>
> >> Sent: Monday, November 20, 2023 8:00 PM
> >> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
> >> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
> >> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
> >> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> >> michael@walle.cc; linux-mtd@lists.infradead.org;
> >> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> >> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>;
> >> linux- arm-kernel@lists.infradead.org; git (AMD-Xilinx)
> >> <git@amd.com>; amitrkcian2002@gmail.com;
> >> patches@opensource.cirrus.com
> >> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in
> >> SPI core
> >>
> >>
> >> On 18/11/2023 13:54, Amit Kumar Mahapatra wrote:
> >>> AMD-Xilinx GQSPI controller has two advanced mode that allows the
> >>> controller to consider two flashes as one single device.
> >>>
> >>> One of these two mode is the parallel mode in which each byte of
> >>> data is stored in both devices, the even bits in the lower flash &
> >>> the odd bits in the upper flash. The byte split is automatically
> >>> handled by the QSPI controller.
> >>>
> >>> The other mode is the stacked mode in which both the flashes share
> >>> the same SPI bus but each of the device contain half of the data. In
> >>> this mode, the controller does not follow CS requests but instead
> >>> internally wires the two CS levels with the value of the most
> >>> significant
> >> address bit.
> >>> For supporting both these modes SPI core need to be updated for
> >>> providing multiple CS for a single SPI device.
> >>>
> >>> For adding multi CS support the SPI device need to be aware of all
> >>> the CS values. So, the "chip_select" member in the spi_device
> >>> structure is now an array that holds all the CS values.
> >>>
> >>> spi_device structure now has a "cs_index_mask" member. This acts as
> >>> an index to the chip_select array. If nth bit of spi->cs_index_mask
> >>> is set then the driver would assert spi->chip_select[n].
> >>>
> >>> In parallel mode all the chip selects are asserted/de-asserted
> >>> simultaneously and each byte of data is stored in both devices, the
> >>> even bits in one, the odd bits in the other. The split is
> >>> automatically handled by the GQSPI controller. The GQSPI controller
> >>> supports a maximum of two flashes connected in parallel mode. A
> >>> SPI_CONTROLLER_MULTI_CS flag bit is added in the spi controller
> >>> flags, through ctlr->flags the spi core will make sure that the
> >>> controller is capable of handling multiple chip selects at once.
> >>>
> >>> For supporting multiple CS via GPIO the cs_gpiod member of the
> >>> spi_device structure is now an array that holds the gpio descriptor
> >>> for each chipselect.
> >>>
> >>> CS GPIO is not tested due to unavailability of necessary hardware setup.
> >>>
> >>> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-
> mahapatra@amd.com>
> >>> ---
> >>> Hello @Stefen,
> >>> We restructured the SPI core implementation, for handling arbitrary
> >>> number of devices connected in parallel(multi-cs) or stacked mode.
> >>> We have tested this updated patch on native-cs setup but couldn't
> >>> test cs-gpio due to unavailability of a proper setup. If possible,
> >>> could you please retest the cs-gpio use case with this updated patch
> >>> and provide
> >> your feedback.
> >>
> >> Hi,
> >>
> >> I tested this chain on 2 different systems using GPIOs as chip
> >> selects, and see the same error on both:
> >> [    2.842045] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use
> >>
> >> Let me know if you need any further testing.
> >>
> >> Thanks,
> >>
> >> Stefan Binding
> >>
> >>> ---
> >>>    drivers/spi/spi.c       | 192 +++++++++++++++++++++++++++++++---------
> >>>    include/linux/spi/spi.h |  51 ++++++++---
> >>>    2 files changed, 191 insertions(+), 52 deletions(-)
> >>>
> >>> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index
> >>> 8ead7acb99f3..ff66147ba95f 100644
> >>> --- a/drivers/spi/spi.c
> >>> +++ b/drivers/spi/spi.c
> >>> @@ -612,10 +612,21 @@ static int spi_dev_check(struct device *dev,
> >>> void
> >> *data)
> >>>    {
> >>>    	struct spi_device *spi = to_spi_device(dev);
> >>>    	struct spi_device *new_spi = data;
> >>> -
> >>> -	if (spi->controller == new_spi->controller &&
> >>> -	    spi_get_chipselect(spi, 0) == spi_get_chipselect(new_spi, 0))
> >>> -		return -EBUSY;
> >>> +	int idx, nw_idx;
> >>> +	u8 cs, cs_nw;
> >>> +
> >>> +	if (spi->controller == new_spi->controller) {
> >>> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>> +			cs = spi_get_chipselect(spi, idx);
> >>> +			for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX;
> >> nw_idx++) {
> >>> +				cs_nw = spi_get_chipselect(spi, nw_idx);
> > Thank you for dedicating your time to test my patch.
> > As per my analysis the error is reported from here as we are supplying
> > the former SPI device structure to retrieve the CS value for the new
> > SPI devices.
> > To fix this, could you kindly substitute the above line with
> >
> > cs_nw = spi_get_chipselect(new_spi, nw_idx);
> >
> > and rerun your tests?
> > If it works correctly, I will incorporate this fix into my upcoming
> > series.
> >
> > Regards,
> > Amit
> 
> Hi,
> 
> I still see the same error:
> 
> [    2.748546] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use

Thank you for the quick testing. For further analysis I have incorporated 
additional debug prints on top of 1/8 patch. The corresponding diff is 
shared below. Kindly implement the specified changes, rerun your test and 
share the kernel logs.

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index ff66147ba95f..7f59ea81593d 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -618,8 +618,10 @@ static int spi_dev_check(struct device *dev, void *data)
        if (spi->controller == new_spi->controller) {
                for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
                        cs = spi_get_chipselect(spi, idx);
+                       printk("%s() [%d] CS[%d] = [%d]\n", __func__, __LINE__, idx, cs);
                        for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
-                               cs_nw = spi_get_chipselect(spi, nw_idx);
+                               cs_nw = spi_get_chipselect(new_spi, nw_idx);
+                               printk("%s() [%d] CS_NEW[%d] = [%d]\n", __func__, __LINE__, nw_idx, cs_nw);
                                if (cs != 0xFF && cs_nw != 0xFF && cs == cs_nw) {
                                        dev_err(dev, "chipselect %d already in use\n", cs_nw);
                                        return -EBUSY;
@@ -659,8 +661,10 @@ static int __spi_add_device(struct spi_device *spi)
         */
        for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
                cs = spi_get_chipselect(spi, idx);
+               printk("%s() [%d] CS[%d] = [%d]\n", __func__, __LINE__, idx, cs);
                for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
                        nw_cs = spi_get_chipselect(spi, nw_idx);
+                       printk("%s() [%d] CS_NEW[%d] = [%d]\n", __func__, __LINE__, nw_idx, nw_cs);
                        if (cs != 0xFF && nw_cs != 0xFF && cs == nw_cs) {
                                dev_err(dev, "chipselect %d already in use\n", nw_cs);
                                return -EBUSY;
@@ -2401,6 +2405,9 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
        for (idx = 0; idx < rc; idx++)
                spi_set_chipselect(spi, idx, cs[idx]);
 
+       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
+               printk("%s() [%d] CS[%d] = [%d]\n", __func__, __LINE__, idx, spi_get_chipselect(spi, idx));
+
        /* spi->chip_select[i] gives the corresponding physical CS for logical CS i
         * logical CS number is represented by setting the ith bit in spi->cs_index_mask
         * So, for example, if spi->cs_index_mask = 0x01 then logical CS number is 0 and

Regards,
Amit
 
> 
> Thanks,
> 
> Stefan
> 
> >
> >>> +				if (cs != 0xFF && cs_nw != 0xFF && cs ==
> >> cs_nw) {
> >>> +					dev_err(dev, "chipselect %d already in
> >> use\n", cs_nw);
> >>> +					return -EBUSY;
> >>> +				}
> >>> +			}
> >>> +		}
> >>> +	}
> >>>    	return 0;
> >>>    }
> >>>
> >>> @@ -629,13 +640,32 @@ static int __spi_add_device(struct spi_device
> *spi)
> >>>    {
> >>>    	struct spi_controller *ctlr = spi->controller;
> >>>    	struct device *dev = ctlr->dev.parent;
> >>> -	int status;
> >>> +	int status, idx, nw_idx;
> >>> +	u8 cs, nw_cs;
> >>> +
> >>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>> +		/* Chipselects are numbered 0..max; validate. */
> >>> +		cs = spi_get_chipselect(spi, idx);
> >>> +		if (cs != 0xFF && cs >= ctlr->num_chipselect) {
> >>> +			dev_err(dev, "cs%d >= max %d\n",
> >> spi_get_chipselect(spi, idx),
> >>> +				ctlr->num_chipselect);
> >>> +			return -EINVAL;
> >>> +		}
> >>> +	}
> >>>
> >>> -	/* Chipselects are numbered 0..max; validate. */
> >>> -	if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
> >>> -		dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0),
> >>> -			ctlr->num_chipselect);
> >>> -		return -EINVAL;
> >>> +	/*
> >>> +	 * Make sure that multiple logical CS doesn't map to the same
> >> physical CS.
> >>> +	 * For example, spi->chip_select[0] != spi->chip_select[1] and so on.
> >>> +	 */
> >>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>> +		cs = spi_get_chipselect(spi, idx);
> >>> +		for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
> >>> +			nw_cs = spi_get_chipselect(spi, nw_idx);
> >>> +			if (cs != 0xFF && nw_cs != 0xFF && cs == nw_cs) {
> >>> +				dev_err(dev, "chipselect %d already in use\n",
> >> nw_cs);
> >>> +				return -EBUSY;
> >>> +			}
> >>> +		}
> >>>    	}
> >>>
> >>>    	/* Set the bus ID string */
> >>> @@ -647,11 +677,8 @@ static int __spi_add_device(struct spi_device
> *spi)
> >>>    	 * its configuration.
> >>>    	 */
> >>>    	status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
> >>> -	if (status) {
> >>> -		dev_err(dev, "chipselect %d already in use\n",
> >>> -				spi_get_chipselect(spi, 0));
> >>> +	if (status)
> >>>    		return status;
> >>> -	}
> >>>
> >>>    	/* Controller may unregister concurrently */
> >>>    	if (IS_ENABLED(CONFIG_SPI_DYNAMIC) && @@ -659,8 +686,15 @@
> >> static
> >>> int __spi_add_device(struct spi_device *spi)
> >>>    		return -ENODEV;
> >>>    	}
> >>>
> >>> -	if (ctlr->cs_gpiods)
> >>> -		spi_set_csgpiod(spi, 0, ctlr->cs_gpiods[spi_get_chipselect(spi,
> >> 0)]);
> >>> +	if (ctlr->cs_gpiods) {
> >>> +		u8 cs;
> >>> +
> >>> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>> +			cs = spi_get_chipselect(spi, idx);
> >>> +			if (cs != 0xFF)
> >>> +				spi_set_csgpiod(spi, idx, ctlr->cs_gpiods[cs]);
> >>> +		}
> >>> +	}
> >>>
> >>>    	/*
> >>>    	 * Drivers may modify this initial i/o setup, but will @@ -701,6
> >>> +735,9 @@ int spi_add_device(struct spi_device *spi)
> >>>    	struct spi_controller *ctlr = spi->controller;
> >>>    	int status;
> >>>
> >>> +	/* Set the bus ID string */
> >>> +	spi_dev_set_name(spi);
> >>> +
> >>>    	mutex_lock(&ctlr->add_lock);
> >>>    	status = __spi_add_device(spi);
> >>>    	mutex_unlock(&ctlr->add_lock);
> >>> @@ -942,32 +979,51 @@ static void spi_res_release(struct
> >>> spi_controller
> >> *ctlr, struct spi_message *mes
> >>>    }
> >>>
> >>>
> >>> /*------------------------------------------------------------------
> >>> --
> >>> -----*/
> >>> +static inline bool spi_is_last_cs(struct spi_device *spi) {
> >>> +	u8 idx;
> >>> +	bool last = false;
> >>> +
> >>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>> +		if ((spi->cs_index_mask >> idx) & 0x01) {
> >>> +			if (spi->controller->last_cs[idx] ==
> >> spi_get_chipselect(spi, idx))
> >>> +				last = true;
> >>> +		}
> >>> +	}
> >>> +	return last;
> >>> +}
> >>> +
> >>>
> >>>    static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
> >>>    {
> >>>    	bool activate = enable;
> >>> +	u8 idx;
> >>>
> >>>    	/*
> >>>    	 * Avoid calling into the driver (or doing delays) if the chip select
> >>>    	 * isn't actually changing from the last time this was called.
> >>>    	 */
> >>> -	if (!force && ((enable && spi->controller->last_cs ==
> >> spi_get_chipselect(spi, 0)) ||
> >>> -		       (!enable && spi->controller->last_cs !=
> >> spi_get_chipselect(spi, 0))) &&
> >>> +	if (!force && ((enable && spi->controller->last_cs_index_mask ==
> >>> +spi-
> >>> cs_index_mask &&
> >>> +			spi_is_last_cs(spi)) ||
> >>> +		       (!enable && spi->controller->last_cs_index_mask == spi-
> >>> cs_index_mask &&
> >>> +			!spi_is_last_cs(spi))) &&
> >>>    	    (spi->controller->last_cs_mode_high == (spi->mode &
> >> SPI_CS_HIGH)))
> >>>    		return;
> >>>
> >>>    	trace_spi_set_cs(spi, activate);
> >>>
> >>> -	spi->controller->last_cs = enable ? spi_get_chipselect(spi, 0) : -1;
> >>> +	spi->controller->last_cs_index_mask = spi->cs_index_mask;
> >>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> >>> +		spi->controller->last_cs[idx] = enable ? spi_get_chipselect(spi,
> >> 0)
> >>> +: -1;
> >>>    	spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;
> >>>
> >>> -	if ((spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) &&
> >> !activate)
> >>> -		spi_delay_exec(&spi->cs_hold, NULL);
> >>> -
> >>>    	if (spi->mode & SPI_CS_HIGH)
> >>>    		enable = !enable;
> >>>
> >>> -	if (spi_get_csgpiod(spi, 0)) {
> >>> +	if (spi_is_csgpiod(spi)) {
> >>> +		if (!spi->controller->set_cs_timing && !activate)
> >>> +			spi_delay_exec(&spi->cs_hold, NULL);
> >>> +
> >>>    		if (!(spi->mode & SPI_NO_CS)) {
> >>>    			/*
> >>>    			 * Historically ACPI has no means of the GPIO polarity
> >> and @@
> >>> -979,26 +1035,38 @@ static void spi_set_cs(struct spi_device *spi,
> >>> bool
> >> enable, bool force)
> >>>    			 * ambiguity. That's why we use enable, that takes
> >> SPI_CS_HIGH
> >>>    			 * into account.
> >>>    			 */
> >>> -			if (has_acpi_companion(&spi->dev))
> >>> -
> >> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), !enable);
> >>> -			else
> >>> -				/* Polarity handled by GPIO library */
> >>> -
> >> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), activate);
> >>> +			for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>> +				if (((spi->cs_index_mask >> idx) & 0x01) &&
> >>> +				    spi_get_csgpiod(spi, idx)) {
> >>> +					if (has_acpi_companion(&spi->dev))
> >>> +
> >> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
> >>> +
> >> !enable);
> >>> +					else
> >>> +						/* Polarity handled by GPIO
> >> library */
> >>> +
> >> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
> >>> +
> >> activate);
> >>> +
> >>> +					if (activate)
> >>> +						spi_delay_exec(&spi-
> >>> cs_setup, NULL);
> >>> +					else
> >>> +						spi_delay_exec(&spi-
> >>> cs_inactive, NULL);
> >>> +				}
> >>> +			}
> >>>    		}
> >>>    		/* Some SPI masters need both GPIO CS & slave_select */
> >>>    		if ((spi->controller->flags & SPI_CONTROLLER_GPIO_SS) &&
> >>>    		    spi->controller->set_cs)
> >>>    			spi->controller->set_cs(spi, !enable);
> >>> +
> >>> +		if (!spi->controller->set_cs_timing) {
> >>> +			if (activate)
> >>> +				spi_delay_exec(&spi->cs_setup, NULL);
> >>> +			else
> >>> +				spi_delay_exec(&spi->cs_inactive, NULL);
> >>> +		}
> >>>    	} else if (spi->controller->set_cs) {
> >>>    		spi->controller->set_cs(spi, !enable);
> >>>    	}
> >>> -
> >>> -	if (spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) {
> >>> -		if (activate)
> >>> -			spi_delay_exec(&spi->cs_setup, NULL);
> >>> -		else
> >>> -			spi_delay_exec(&spi->cs_inactive, NULL);
> >>> -	}
> >>>    }
> >>>
> >>>    #ifdef CONFIG_HAS_DMA
> >>> @@ -2222,8 +2290,8 @@ static void of_spi_parse_dt_cs_delay(struct
> >> device_node *nc,
> >>>    static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device
> *spi,
> >>>    			   struct device_node *nc)
> >>>    {
> >>> -	u32 value;
> >>> -	int rc;
> >>> +	u32 value, cs[SPI_CS_CNT_MAX];
> >>> +	int rc, idx;
> >>>
> >>>    	/* Mode (clock phase/polarity/etc.) */
> >>>    	if (of_property_read_bool(nc, "spi-cpha")) @@ -2295,14 +2363,52
> >> @@
> >>> static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
> >>>    		return 0;
> >>>    	}
> >>>
> >>> +	if (ctlr->num_chipselect > SPI_CS_CNT_MAX) {
> >>> +		dev_err(&ctlr->dev, "No. of CS is more than max. no. of
> >> supported CS\n");
> >>> +		return -EINVAL;
> >>> +	}
> >>> +
> >>> +	/*
> >>> +	 * Zero(0) is a valid physical CS value and can be located at any
> >>> +	 * logical CS in the spi->chip_select[]. If all the physical CS
> >>> +	 * are initialized to 0 then It would be difficult to differentiate
> >>> +	 * between a valid physical CS 0 & an unused logical CS whose
> >> physical
> >>> +	 * CS can be 0. As a solution to this issue initialize all the CS to 0xFF.
> >>> +	 * Now all the unused logical CS will have 0xFF physical CS value
> >>> +& can
> >> be
> >>> +	 * ignore while performing physical CS validity checks.
> >>> +	 */
> >>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> >>> +		spi_set_chipselect(spi, idx, 0xFF);
> >>> +
> >>>    	/* Device address */
> >>> -	rc = of_property_read_u32(nc, "reg", &value);
> >>> -	if (rc) {
> >>> +	rc = of_property_read_variable_u32_array(nc, "reg", &cs[0], 1,
> >>> +						 SPI_CS_CNT_MAX);
> >>> +	if (rc < 0) {
> >>>    		dev_err(&ctlr->dev, "%pOF has no valid 'reg' property
> >> (%d)\n",
> >>>    			nc, rc);
> >>>    		return rc;
> >>>    	}
> >>> -	spi_set_chipselect(spi, 0, value);
> >>> +	if (rc > ctlr->num_chipselect) {
> >>> +		dev_err(&ctlr->dev, "%pOF has number of CS > ctlr-
> >>> num_chipselect (%d)\n",
> >>> +			nc, rc);
> >>> +		return rc;
> >>> +	}
> >>> +	if ((of_property_read_bool(nc, "parallel-memories")) &&
> >>> +	    (!(ctlr->flags & SPI_CONTROLLER_MULTI_CS))) {
> >>> +		dev_err(&ctlr->dev, "SPI controller doesn't support multi
> >> CS\n");
> >>> +		return -EINVAL;
> >>> +	}
> >>> +	for (idx = 0; idx < rc; idx++)
> >>> +		spi_set_chipselect(spi, idx, cs[idx]);
> >>> +
> >>> +	/* spi->chip_select[i] gives the corresponding physical CS for
> >>> +logical CS
> >> i
> >>> +	 * logical CS number is represented by setting the ith bit in spi-
> >>> cs_index_mask
> >>> +	 * So, for example, if spi->cs_index_mask = 0x01 then logical CS
> >> number is 0 and
> >>> +	 * spi->chip_select[0] will give the physical CS.
> >>> +	 * By default spi->chip_select[0] will hold the physical CS number
> >>> +so,
> >> set
> >>> +	 * spi->cs_index_mask as 0x01.
> >>> +	 */
> >>> +	spi->cs_index_mask = 0x01;
> >>>
> >>>    	/* Device speed */
> >>>    	if (!of_property_read_u32(nc, "spi-max-frequency", &value)) @@
> >>> -3100,6 +3206,7 @@ int spi_register_controller(struct spi_controller *ctlr)
> >>>    	struct boardinfo	*bi;
> >>>    	int			first_dynamic;
> >>>    	int			status;
> >>> +	int			idx;
> >>>
> >>>    	if (!dev)
> >>>    		return -ENODEV;
> >>> @@ -3164,7 +3271,8 @@ int spi_register_controller(struct
> >>> spi_controller
> >> *ctlr)
> >>>    	}
> >>>
> >>>    	/* Setting last_cs to -1 means no chip selected */
> >>> -	ctlr->last_cs = -1;
> >>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> >>> +		ctlr->last_cs[idx] = -1;
> >>>
> >>>    	status = device_add(&ctlr->dev);
> >>>    	if (status < 0)
> >>> @@ -3889,7 +3997,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_get_csgpiod(spi, 0))) {
> >>> +					  spi_is_csgpiod(spi))) {
> >>>    		size_t maxsize = BITS_TO_BYTES(spi->bits_per_word);
> >>>    		int ret;
> >>>
> >>> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index
> >>> 7b4baff63c5c..871d3a6d879b 100644
> >>> --- a/include/linux/spi/spi.h
> >>> +++ b/include/linux/spi/spi.h
> >>> @@ -20,6 +20,9 @@
> >>>
> >>>    #include <uapi/linux/spi/spi.h>
> >>>
> >>> +/* Max no. of CS supported per spi device */ #define SPI_CS_CNT_MAX
> >>> +4
> >>> +
> >>>    struct dma_chan;
> >>>    struct software_node;
> >>>    struct ptp_system_timestamp;
> >>> @@ -132,7 +135,8 @@ extern void
> >> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> >>>     * @max_speed_hz: Maximum clock rate to be used with this chip
> >>>     *	(on this board); may be changed by the device's driver.
> >>>     *	The spi_transfer.speed_hz can override this for each transfer.
> >>> - * @chip_select: Chipselect, distinguishing chips handled by @controller.
> >>> + * @chip_select: Array of physical chipselect, spi->chipselect[i] gives
> >>> + *	the corresponding physical CS for logical CS i.
> >>>     * @mode: The spi mode defines how data is clocked out and in.
> >>>     *	This may be changed by the device's driver.
> >>>     *	The "active low" default for chipselect mode can be overridden
> >>> @@ -157,8 +161,8 @@ extern void
> >> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> >>>     *	the device will bind to the named driver and only the named driver.
> >>>     *	Do not set directly, because core frees it; use driver_set_override() to
> >>>     *	set or clear it.
> >>> - * @cs_gpiod: GPIO descriptor of the chipselect line (optional, NULL
> when
> >>> - *	not using a GPIO line)
> >>> + * @cs_gpiod: Array of GPIO descriptors of the corresponding
> >>> + chipselect
> >> lines
> >>> + *	(optional, NULL when not using a GPIO line)
> >>>     * @word_delay: delay to be inserted between consecutive
> >>>     *	words of a transfer
> >>>     * @cs_setup: delay to be introduced by the controller after CS
> >>> is asserted @@ -167,6 +171,7 @@ extern void
> >> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> >>>     *	deasserted. If @cs_change_delay is used from @spi_transfer, then
> >> the
> >>>     *	two delays will be added up.
> >>>     * @pcpu_statistics: statistics for the spi_device
> >>> + * @cs_index_mask: Bit mask of the active chipselect(s) in the
> >>> + chipselect array
> >>>     *
> >>>     * A @spi_device is used to interchange data between an SPI slave
> >>>     * (usually a discrete chip) and CPU memory.
> >>> @@ -182,7 +187,7 @@ struct spi_device {
> >>>    	struct spi_controller	*controller;
> >>>    	struct spi_controller	*master;	/* Compatibility layer */
> >>>    	u32			max_speed_hz;
> >>> -	u8			chip_select;
> >>> +	u8			chip_select[SPI_CS_CNT_MAX];
> >>>    	u8			bits_per_word;
> >>>    	bool			rt;
> >>>    #define SPI_NO_TX		BIT(31)		/* No transmit wire */
> >>> @@ -213,7 +218,7 @@ struct spi_device {
> >>>    	void			*controller_data;
> >>>    	char			modalias[SPI_NAME_SIZE];
> >>>    	const char		*driver_override;
> >>> -	struct gpio_desc	*cs_gpiod;	/* Chip select GPIO descriptor
> >> */
> >>> +	struct gpio_desc	*cs_gpiod[SPI_CS_CNT_MAX];	/* Chip select
> >> gpio desc */
> >>>    	struct spi_delay	word_delay; /* Inter-word delay */
> >>>    	/* CS delays */
> >>>    	struct spi_delay	cs_setup;
> >>> @@ -223,6 +228,13 @@ struct spi_device {
> >>>    	/* The statistics */
> >>>    	struct spi_statistics __percpu	*pcpu_statistics;
> >>>
> >>> +	/* Bit mask of the chipselect(s) that the driver need to use from
> >>> +	 * the chipselect array.When the controller is capable to handle
> >>> +	 * multiple chip selects & memories are connected in parallel
> >>> +	 * then more than one bit need to be set in cs_index_mask.
> >>> +	 */
> >>> +	u32			cs_index_mask : SPI_CS_CNT_MAX;
> >>> +
> >>>    	/*
> >>>    	 * Likely need more hooks for more protocol options affecting how
> >>>    	 * the controller talks to each chip, like:
> >>> @@ -279,22 +291,33 @@ static inline void *spi_get_drvdata(const
> >>> struct spi_device *spi)
> >>>
> >>>    static inline u8 spi_get_chipselect(const struct spi_device *spi, u8 idx)
> >>>    {
> >>> -	return spi->chip_select;
> >>> +	return spi->chip_select[idx];
> >>>    }
> >>>
> >>>    static inline void spi_set_chipselect(struct spi_device *spi, u8
> >>> idx, u8
> >> chipselect)
> >>>    {
> >>> -	spi->chip_select = chipselect;
> >>> +	spi->chip_select[idx] = chipselect;
> >>>    }
> >>>
> >>>    static inline struct gpio_desc *spi_get_csgpiod(const struct
> >>> spi_device *spi,
> >> u8 idx)
> >>>    {
> >>> -	return spi->cs_gpiod;
> >>> +	return spi->cs_gpiod[idx];
> >>>    }
> >>>
> >>>    static inline void spi_set_csgpiod(struct spi_device *spi, u8
> >>> idx, struct
> >> gpio_desc *csgpiod)
> >>>    {
> >>> -	spi->cs_gpiod = csgpiod;
> >>> +	spi->cs_gpiod[idx] = csgpiod;
> >>> +}
> >>> +
> >>> +static inline bool spi_is_csgpiod(struct spi_device *spi) {
> >>> +	u8 idx;
> >>> +
> >>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>> +		if (spi_get_csgpiod(spi, idx))
> >>> +			return true;
> >>> +	}
> >>> +	return false;
> >>>    }
> >>>
> >>>    /**
> >>> @@ -399,6 +422,8 @@ extern struct spi_device
> >> *spi_new_ancillary_device(struct spi_device *spi, u8 ch
> >>>     * @bus_lock_spinlock: spinlock for SPI bus locking
> >>>     * @bus_lock_mutex: mutex for exclusion of multiple callers
> >>>     * @bus_lock_flag: indicates that the SPI bus is locked for
> >>> exclusive use
> >>> + * @multi_cs_cap: indicates that the SPI Controller can assert/de-assert
> >>> + *	more than one chip select at once.
> >>>     * @setup: updates the device mode and clocking records used by a
> >>>     *	device's SPI controller; protocol code may call this.  This
> >>>     *	must fail if an unrecognized or unsupported mode is requested.
> >>> @@ -567,6 +592,11 @@ struct spi_controller {
> >>>    #define SPI_CONTROLLER_MUST_TX		BIT(4)	/* Requires tx
> */
> >>>    #define SPI_CONTROLLER_GPIO_SS		BIT(5)	/* GPIO CS
> must
> >> select slave */
> >>>    #define SPI_CONTROLLER_SUSPENDED	BIT(6)	/* Currently
> >> suspended */
> >>> +	/*
> >>> +	 * The spi-controller has multi chip select capability and can
> >>> +	 * assert/de-assert more than one chip select at once.
> >>> +	 */
> >>> +#define SPI_CONTROLLER_MULTI_CS		BIT(7)
> >>>
> >>>    	/* Flag indicating if the allocation of this struct is devres-managed */
> >>>    	bool			devm_allocated;
> >>> @@ -677,7 +707,8 @@ struct spi_controller {
> >>>    	bool				rt;
> >>>    	bool				auto_runtime_pm;
> >>>    	bool				cur_msg_mapped;
> >>> -	char				last_cs;
> >>> +	char				last_cs[SPI_CS_CNT_MAX];
> >>> +	char				last_cs_index_mask;
> >>>    	bool				last_cs_mode_high;
> >>>    	bool                            fallback;
> >>>    	struct completion               xfer_completion;
Stefan Binding Nov. 21, 2023, 5:37 p.m. UTC | #7
On 21/11/2023 16:35, Mahapatra, Amit Kumar wrote:
> Hello Stefan,
>
>> -----Original Message-----
>> From: Stefan Binding <sbinding@opensource.cirrus.com>
>> Sent: Tuesday, November 21, 2023 7:28 PM
>> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
>> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
>> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
>> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
>> michael@walle.cc; linux-mtd@lists.infradead.org;
>> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
>> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>; linux-
>> arm-kernel@lists.infradead.org; git (AMD-Xilinx) <git@amd.com>;
>> amitrkcian2002@gmail.com; patches@opensource.cirrus.com
>> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in SPI core
>>
>>
>> On 21/11/2023 07:39, Mahapatra, Amit Kumar wrote:
>>> Hello Stefan,
>>>
>>>> -----Original Message-----
>>>> From: Stefan Binding <sbinding@opensource.cirrus.com>
>>>> Sent: Monday, November 20, 2023 8:00 PM
>>>> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
>>>> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
>>>> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
>>>> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
>>>> michael@walle.cc; linux-mtd@lists.infradead.org;
>>>> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
>>>> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>;
>>>> linux- arm-kernel@lists.infradead.org; git (AMD-Xilinx)
>>>> <git@amd.com>; amitrkcian2002@gmail.com;
>>>> patches@opensource.cirrus.com
>>>> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in
>>>> SPI core
>>>>
>>>>
>>>> On 18/11/2023 13:54, Amit Kumar Mahapatra wrote:
>>>>> AMD-Xilinx GQSPI controller has two advanced mode that allows the
>>>>> controller to consider two flashes as one single device.
>>>>>
>>>>> One of these two mode is the parallel mode in which each byte of
>>>>> data is stored in both devices, the even bits in the lower flash &
>>>>> the odd bits in the upper flash. The byte split is automatically
>>>>> handled by the QSPI controller.
>>>>>
>>>>> The other mode is the stacked mode in which both the flashes share
>>>>> the same SPI bus but each of the device contain half of the data. In
>>>>> this mode, the controller does not follow CS requests but instead
>>>>> internally wires the two CS levels with the value of the most
>>>>> significant
>>>> address bit.
>>>>> For supporting both these modes SPI core need to be updated for
>>>>> providing multiple CS for a single SPI device.
>>>>>
>>>>> For adding multi CS support the SPI device need to be aware of all
>>>>> the CS values. So, the "chip_select" member in the spi_device
>>>>> structure is now an array that holds all the CS values.
>>>>>
>>>>> spi_device structure now has a "cs_index_mask" member. This acts as
>>>>> an index to the chip_select array. If nth bit of spi->cs_index_mask
>>>>> is set then the driver would assert spi->chip_select[n].
>>>>>
>>>>> In parallel mode all the chip selects are asserted/de-asserted
>>>>> simultaneously and each byte of data is stored in both devices, the
>>>>> even bits in one, the odd bits in the other. The split is
>>>>> automatically handled by the GQSPI controller. The GQSPI controller
>>>>> supports a maximum of two flashes connected in parallel mode. A
>>>>> SPI_CONTROLLER_MULTI_CS flag bit is added in the spi controller
>>>>> flags, through ctlr->flags the spi core will make sure that the
>>>>> controller is capable of handling multiple chip selects at once.
>>>>>
>>>>> For supporting multiple CS via GPIO the cs_gpiod member of the
>>>>> spi_device structure is now an array that holds the gpio descriptor
>>>>> for each chipselect.
>>>>>
>>>>> CS GPIO is not tested due to unavailability of necessary hardware setup.
>>>>>
>>>>> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-
>> mahapatra@amd.com>
>>>>> ---
>>>>> Hello @Stefen,
>>>>> We restructured the SPI core implementation, for handling arbitrary
>>>>> number of devices connected in parallel(multi-cs) or stacked mode.
>>>>> We have tested this updated patch on native-cs setup but couldn't
>>>>> test cs-gpio due to unavailability of a proper setup. If possible,
>>>>> could you please retest the cs-gpio use case with this updated patch
>>>>> and provide
>>>> your feedback.
>>>>
>>>> Hi,
>>>>
>>>> I tested this chain on 2 different systems using GPIOs as chip
>>>> selects, and see the same error on both:
>>>> [    2.842045] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use
>>>>
>>>> Let me know if you need any further testing.
>>>>
>>>> Thanks,
>>>>
>>>> Stefan Binding
>>>>
>>>>> ---
>>>>>     drivers/spi/spi.c       | 192 +++++++++++++++++++++++++++++++---------
>>>>>     include/linux/spi/spi.h |  51 ++++++++---
>>>>>     2 files changed, 191 insertions(+), 52 deletions(-)
>>>>>
>>>>> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index
>>>>> 8ead7acb99f3..ff66147ba95f 100644
>>>>> --- a/drivers/spi/spi.c
>>>>> +++ b/drivers/spi/spi.c
>>>>> @@ -612,10 +612,21 @@ static int spi_dev_check(struct device *dev,
>>>>> void
>>>> *data)
>>>>>     {
>>>>>     	struct spi_device *spi = to_spi_device(dev);
>>>>>     	struct spi_device *new_spi = data;
>>>>> -
>>>>> -	if (spi->controller == new_spi->controller &&
>>>>> -	    spi_get_chipselect(spi, 0) == spi_get_chipselect(new_spi, 0))
>>>>> -		return -EBUSY;
>>>>> +	int idx, nw_idx;
>>>>> +	u8 cs, cs_nw;
>>>>> +
>>>>> +	if (spi->controller == new_spi->controller) {
>>>>> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>>> +			cs = spi_get_chipselect(spi, idx);
>>>>> +			for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX;
>>>> nw_idx++) {
>>>>> +				cs_nw = spi_get_chipselect(spi, nw_idx);
>>> Thank you for dedicating your time to test my patch.
>>> As per my analysis the error is reported from here as we are supplying
>>> the former SPI device structure to retrieve the CS value for the new
>>> SPI devices.
>>> To fix this, could you kindly substitute the above line with
>>>
>>> cs_nw = spi_get_chipselect(new_spi, nw_idx);
>>>
>>> and rerun your tests?
>>> If it works correctly, I will incorporate this fix into my upcoming
>>> series.
>>>
>>> Regards,
>>> Amit

Hi,

I've attached my log.
I notice that you add a print to of_spi_parse_dt, however since the 
laptop I am using is an x86 laptop, it uses ACPI rather than OF, and I 
don't think this function isnt even compiled in.

Thanks,

Stefan

>> Hi,
>>
>> I still see the same error:
>>
>> [    2.748546] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use
> Thank you for the quick testing. For further analysis I have incorporated
> additional debug prints on top of 1/8 patch. The corresponding diff is
> shared below. Kindly implement the specified changes, rerun your test and
> share the kernel logs.
>
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index ff66147ba95f..7f59ea81593d 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -618,8 +618,10 @@ static int spi_dev_check(struct device *dev, void *data)
>          if (spi->controller == new_spi->controller) {
>                  for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>                          cs = spi_get_chipselect(spi, idx);
> +                       printk("%s() [%d] CS[%d] = [%d]\n", __func__, __LINE__, idx, cs);
>                          for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
> -                               cs_nw = spi_get_chipselect(spi, nw_idx);
> +                               cs_nw = spi_get_chipselect(new_spi, nw_idx);
> +                               printk("%s() [%d] CS_NEW[%d] = [%d]\n", __func__, __LINE__, nw_idx, cs_nw);
>                                  if (cs != 0xFF && cs_nw != 0xFF && cs == cs_nw) {
>                                          dev_err(dev, "chipselect %d already in use\n", cs_nw);
>                                          return -EBUSY;
> @@ -659,8 +661,10 @@ static int __spi_add_device(struct spi_device *spi)
>           */
>          for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>                  cs = spi_get_chipselect(spi, idx);
> +               printk("%s() [%d] CS[%d] = [%d]\n", __func__, __LINE__, idx, cs);
>                  for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
>                          nw_cs = spi_get_chipselect(spi, nw_idx);
> +                       printk("%s() [%d] CS_NEW[%d] = [%d]\n", __func__, __LINE__, nw_idx, nw_cs);
>                          if (cs != 0xFF && nw_cs != 0xFF && cs == nw_cs) {
>                                  dev_err(dev, "chipselect %d already in use\n", nw_cs);
>                                  return -EBUSY;
> @@ -2401,6 +2405,9 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
>          for (idx = 0; idx < rc; idx++)
>                  spi_set_chipselect(spi, idx, cs[idx]);
>   
> +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> +               printk("%s() [%d] CS[%d] = [%d]\n", __func__, __LINE__, idx, spi_get_chipselect(spi, idx));
> +
>          /* spi->chip_select[i] gives the corresponding physical CS for logical CS i
>           * logical CS number is represented by setting the ith bit in spi->cs_index_mask
>           * So, for example, if spi->cs_index_mask = 0x01 then logical CS number is 0 and
>
> Regards,
> Amit
>   
>> Thanks,
>>
>> Stefan
>>
>>>>> +				if (cs != 0xFF && cs_nw != 0xFF && cs ==
>>>> cs_nw) {
>>>>> +					dev_err(dev, "chipselect %d already in
>>>> use\n", cs_nw);
>>>>> +					return -EBUSY;
>>>>> +				}
>>>>> +			}
>>>>> +		}
>>>>> +	}
>>>>>     	return 0;
>>>>>     }
>>>>>
>>>>> @@ -629,13 +640,32 @@ static int __spi_add_device(struct spi_device
>> *spi)
>>>>>     {
>>>>>     	struct spi_controller *ctlr = spi->controller;
>>>>>     	struct device *dev = ctlr->dev.parent;
>>>>> -	int status;
>>>>> +	int status, idx, nw_idx;
>>>>> +	u8 cs, nw_cs;
>>>>> +
>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>>> +		/* Chipselects are numbered 0..max; validate. */
>>>>> +		cs = spi_get_chipselect(spi, idx);
>>>>> +		if (cs != 0xFF && cs >= ctlr->num_chipselect) {
>>>>> +			dev_err(dev, "cs%d >= max %d\n",
>>>> spi_get_chipselect(spi, idx),
>>>>> +				ctlr->num_chipselect);
>>>>> +			return -EINVAL;
>>>>> +		}
>>>>> +	}
>>>>>
>>>>> -	/* Chipselects are numbered 0..max; validate. */
>>>>> -	if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
>>>>> -		dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0),
>>>>> -			ctlr->num_chipselect);
>>>>> -		return -EINVAL;
>>>>> +	/*
>>>>> +	 * Make sure that multiple logical CS doesn't map to the same
>>>> physical CS.
>>>>> +	 * For example, spi->chip_select[0] != spi->chip_select[1] and so on.
>>>>> +	 */
>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>>> +		cs = spi_get_chipselect(spi, idx);
>>>>> +		for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
>>>>> +			nw_cs = spi_get_chipselect(spi, nw_idx);
>>>>> +			if (cs != 0xFF && nw_cs != 0xFF && cs == nw_cs) {
>>>>> +				dev_err(dev, "chipselect %d already in use\n",
>>>> nw_cs);
>>>>> +				return -EBUSY;
>>>>> +			}
>>>>> +		}
>>>>>     	}
>>>>>
>>>>>     	/* Set the bus ID string */
>>>>> @@ -647,11 +677,8 @@ static int __spi_add_device(struct spi_device
>> *spi)
>>>>>     	 * its configuration.
>>>>>     	 */
>>>>>     	status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
>>>>> -	if (status) {
>>>>> -		dev_err(dev, "chipselect %d already in use\n",
>>>>> -				spi_get_chipselect(spi, 0));
>>>>> +	if (status)
>>>>>     		return status;
>>>>> -	}
>>>>>
>>>>>     	/* Controller may unregister concurrently */
>>>>>     	if (IS_ENABLED(CONFIG_SPI_DYNAMIC) && @@ -659,8 +686,15 @@
>>>> static
>>>>> int __spi_add_device(struct spi_device *spi)
>>>>>     		return -ENODEV;
>>>>>     	}
>>>>>
>>>>> -	if (ctlr->cs_gpiods)
>>>>> -		spi_set_csgpiod(spi, 0, ctlr->cs_gpiods[spi_get_chipselect(spi,
>>>> 0)]);
>>>>> +	if (ctlr->cs_gpiods) {
>>>>> +		u8 cs;
>>>>> +
>>>>> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>>> +			cs = spi_get_chipselect(spi, idx);
>>>>> +			if (cs != 0xFF)
>>>>> +				spi_set_csgpiod(spi, idx, ctlr->cs_gpiods[cs]);
>>>>> +		}
>>>>> +	}
>>>>>
>>>>>     	/*
>>>>>     	 * Drivers may modify this initial i/o setup, but will @@ -701,6
>>>>> +735,9 @@ int spi_add_device(struct spi_device *spi)
>>>>>     	struct spi_controller *ctlr = spi->controller;
>>>>>     	int status;
>>>>>
>>>>> +	/* Set the bus ID string */
>>>>> +	spi_dev_set_name(spi);
>>>>> +
>>>>>     	mutex_lock(&ctlr->add_lock);
>>>>>     	status = __spi_add_device(spi);
>>>>>     	mutex_unlock(&ctlr->add_lock);
>>>>> @@ -942,32 +979,51 @@ static void spi_res_release(struct
>>>>> spi_controller
>>>> *ctlr, struct spi_message *mes
>>>>>     }
>>>>>
>>>>>
>>>>> /*------------------------------------------------------------------
>>>>> --
>>>>> -----*/
>>>>> +static inline bool spi_is_last_cs(struct spi_device *spi) {
>>>>> +	u8 idx;
>>>>> +	bool last = false;
>>>>> +
>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>>> +		if ((spi->cs_index_mask >> idx) & 0x01) {
>>>>> +			if (spi->controller->last_cs[idx] ==
>>>> spi_get_chipselect(spi, idx))
>>>>> +				last = true;
>>>>> +		}
>>>>> +	}
>>>>> +	return last;
>>>>> +}
>>>>> +
>>>>>
>>>>>     static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
>>>>>     {
>>>>>     	bool activate = enable;
>>>>> +	u8 idx;
>>>>>
>>>>>     	/*
>>>>>     	 * Avoid calling into the driver (or doing delays) if the chip select
>>>>>     	 * isn't actually changing from the last time this was called.
>>>>>     	 */
>>>>> -	if (!force && ((enable && spi->controller->last_cs ==
>>>> spi_get_chipselect(spi, 0)) ||
>>>>> -		       (!enable && spi->controller->last_cs !=
>>>> spi_get_chipselect(spi, 0))) &&
>>>>> +	if (!force && ((enable && spi->controller->last_cs_index_mask ==
>>>>> +spi-
>>>>> cs_index_mask &&
>>>>> +			spi_is_last_cs(spi)) ||
>>>>> +		       (!enable && spi->controller->last_cs_index_mask == spi-
>>>>> cs_index_mask &&
>>>>> +			!spi_is_last_cs(spi))) &&
>>>>>     	    (spi->controller->last_cs_mode_high == (spi->mode &
>>>> SPI_CS_HIGH)))
>>>>>     		return;
>>>>>
>>>>>     	trace_spi_set_cs(spi, activate);
>>>>>
>>>>> -	spi->controller->last_cs = enable ? spi_get_chipselect(spi, 0) : -1;
>>>>> +	spi->controller->last_cs_index_mask = spi->cs_index_mask;
>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
>>>>> +		spi->controller->last_cs[idx] = enable ? spi_get_chipselect(spi,
>>>> 0)
>>>>> +: -1;
>>>>>     	spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;
>>>>>
>>>>> -	if ((spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) &&
>>>> !activate)
>>>>> -		spi_delay_exec(&spi->cs_hold, NULL);
>>>>> -
>>>>>     	if (spi->mode & SPI_CS_HIGH)
>>>>>     		enable = !enable;
>>>>>
>>>>> -	if (spi_get_csgpiod(spi, 0)) {
>>>>> +	if (spi_is_csgpiod(spi)) {
>>>>> +		if (!spi->controller->set_cs_timing && !activate)
>>>>> +			spi_delay_exec(&spi->cs_hold, NULL);
>>>>> +
>>>>>     		if (!(spi->mode & SPI_NO_CS)) {
>>>>>     			/*
>>>>>     			 * Historically ACPI has no means of the GPIO polarity
>>>> and @@
>>>>> -979,26 +1035,38 @@ static void spi_set_cs(struct spi_device *spi,
>>>>> bool
>>>> enable, bool force)
>>>>>     			 * ambiguity. That's why we use enable, that takes
>>>> SPI_CS_HIGH
>>>>>     			 * into account.
>>>>>     			 */
>>>>> -			if (has_acpi_companion(&spi->dev))
>>>>> -
>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), !enable);
>>>>> -			else
>>>>> -				/* Polarity handled by GPIO library */
>>>>> -
>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), activate);
>>>>> +			for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>>> +				if (((spi->cs_index_mask >> idx) & 0x01) &&
>>>>> +				    spi_get_csgpiod(spi, idx)) {
>>>>> +					if (has_acpi_companion(&spi->dev))
>>>>> +
>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
>>>>> +
>>>> !enable);
>>>>> +					else
>>>>> +						/* Polarity handled by GPIO
>>>> library */
>>>>> +
>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
>>>>> +
>>>> activate);
>>>>> +
>>>>> +					if (activate)
>>>>> +						spi_delay_exec(&spi-
>>>>> cs_setup, NULL);
>>>>> +					else
>>>>> +						spi_delay_exec(&spi-
>>>>> cs_inactive, NULL);
>>>>> +				}
>>>>> +			}
>>>>>     		}
>>>>>     		/* Some SPI masters need both GPIO CS & slave_select */
>>>>>     		if ((spi->controller->flags & SPI_CONTROLLER_GPIO_SS) &&
>>>>>     		    spi->controller->set_cs)
>>>>>     			spi->controller->set_cs(spi, !enable);
>>>>> +
>>>>> +		if (!spi->controller->set_cs_timing) {
>>>>> +			if (activate)
>>>>> +				spi_delay_exec(&spi->cs_setup, NULL);
>>>>> +			else
>>>>> +				spi_delay_exec(&spi->cs_inactive, NULL);
>>>>> +		}
>>>>>     	} else if (spi->controller->set_cs) {
>>>>>     		spi->controller->set_cs(spi, !enable);
>>>>>     	}
>>>>> -
>>>>> -	if (spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) {
>>>>> -		if (activate)
>>>>> -			spi_delay_exec(&spi->cs_setup, NULL);
>>>>> -		else
>>>>> -			spi_delay_exec(&spi->cs_inactive, NULL);
>>>>> -	}
>>>>>     }
>>>>>
>>>>>     #ifdef CONFIG_HAS_DMA
>>>>> @@ -2222,8 +2290,8 @@ static void of_spi_parse_dt_cs_delay(struct
>>>> device_node *nc,
>>>>>     static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device
>> *spi,
>>>>>     			   struct device_node *nc)
>>>>>     {
>>>>> -	u32 value;
>>>>> -	int rc;
>>>>> +	u32 value, cs[SPI_CS_CNT_MAX];
>>>>> +	int rc, idx;
>>>>>
>>>>>     	/* Mode (clock phase/polarity/etc.) */
>>>>>     	if (of_property_read_bool(nc, "spi-cpha")) @@ -2295,14 +2363,52
>>>> @@
>>>>> static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
>>>>>     		return 0;
>>>>>     	}
>>>>>
>>>>> +	if (ctlr->num_chipselect > SPI_CS_CNT_MAX) {
>>>>> +		dev_err(&ctlr->dev, "No. of CS is more than max. no. of
>>>> supported CS\n");
>>>>> +		return -EINVAL;
>>>>> +	}
>>>>> +
>>>>> +	/*
>>>>> +	 * Zero(0) is a valid physical CS value and can be located at any
>>>>> +	 * logical CS in the spi->chip_select[]. If all the physical CS
>>>>> +	 * are initialized to 0 then It would be difficult to differentiate
>>>>> +	 * between a valid physical CS 0 & an unused logical CS whose
>>>> physical
>>>>> +	 * CS can be 0. As a solution to this issue initialize all the CS to 0xFF.
>>>>> +	 * Now all the unused logical CS will have 0xFF physical CS value
>>>>> +& can
>>>> be
>>>>> +	 * ignore while performing physical CS validity checks.
>>>>> +	 */
>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
>>>>> +		spi_set_chipselect(spi, idx, 0xFF);
>>>>> +
>>>>>     	/* Device address */
>>>>> -	rc = of_property_read_u32(nc, "reg", &value);
>>>>> -	if (rc) {
>>>>> +	rc = of_property_read_variable_u32_array(nc, "reg", &cs[0], 1,
>>>>> +						 SPI_CS_CNT_MAX);
>>>>> +	if (rc < 0) {
>>>>>     		dev_err(&ctlr->dev, "%pOF has no valid 'reg' property
>>>> (%d)\n",
>>>>>     			nc, rc);
>>>>>     		return rc;
>>>>>     	}
>>>>> -	spi_set_chipselect(spi, 0, value);
>>>>> +	if (rc > ctlr->num_chipselect) {
>>>>> +		dev_err(&ctlr->dev, "%pOF has number of CS > ctlr-
>>>>> num_chipselect (%d)\n",
>>>>> +			nc, rc);
>>>>> +		return rc;
>>>>> +	}
>>>>> +	if ((of_property_read_bool(nc, "parallel-memories")) &&
>>>>> +	    (!(ctlr->flags & SPI_CONTROLLER_MULTI_CS))) {
>>>>> +		dev_err(&ctlr->dev, "SPI controller doesn't support multi
>>>> CS\n");
>>>>> +		return -EINVAL;
>>>>> +	}
>>>>> +	for (idx = 0; idx < rc; idx++)
>>>>> +		spi_set_chipselect(spi, idx, cs[idx]);
>>>>> +
>>>>> +	/* spi->chip_select[i] gives the corresponding physical CS for
>>>>> +logical CS
>>>> i
>>>>> +	 * logical CS number is represented by setting the ith bit in spi-
>>>>> cs_index_mask
>>>>> +	 * So, for example, if spi->cs_index_mask = 0x01 then logical CS
>>>> number is 0 and
>>>>> +	 * spi->chip_select[0] will give the physical CS.
>>>>> +	 * By default spi->chip_select[0] will hold the physical CS number
>>>>> +so,
>>>> set
>>>>> +	 * spi->cs_index_mask as 0x01.
>>>>> +	 */
>>>>> +	spi->cs_index_mask = 0x01;
>>>>>
>>>>>     	/* Device speed */
>>>>>     	if (!of_property_read_u32(nc, "spi-max-frequency", &value)) @@
>>>>> -3100,6 +3206,7 @@ int spi_register_controller(struct spi_controller *ctlr)
>>>>>     	struct boardinfo	*bi;
>>>>>     	int			first_dynamic;
>>>>>     	int			status;
>>>>> +	int			idx;
>>>>>
>>>>>     	if (!dev)
>>>>>     		return -ENODEV;
>>>>> @@ -3164,7 +3271,8 @@ int spi_register_controller(struct
>>>>> spi_controller
>>>> *ctlr)
>>>>>     	}
>>>>>
>>>>>     	/* Setting last_cs to -1 means no chip selected */
>>>>> -	ctlr->last_cs = -1;
>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
>>>>> +		ctlr->last_cs[idx] = -1;
>>>>>
>>>>>     	status = device_add(&ctlr->dev);
>>>>>     	if (status < 0)
>>>>> @@ -3889,7 +3997,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_get_csgpiod(spi, 0))) {
>>>>> +					  spi_is_csgpiod(spi))) {
>>>>>     		size_t maxsize = BITS_TO_BYTES(spi->bits_per_word);
>>>>>     		int ret;
>>>>>
>>>>> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index
>>>>> 7b4baff63c5c..871d3a6d879b 100644
>>>>> --- a/include/linux/spi/spi.h
>>>>> +++ b/include/linux/spi/spi.h
>>>>> @@ -20,6 +20,9 @@
>>>>>
>>>>>     #include <uapi/linux/spi/spi.h>
>>>>>
>>>>> +/* Max no. of CS supported per spi device */ #define SPI_CS_CNT_MAX
>>>>> +4
>>>>> +
>>>>>     struct dma_chan;
>>>>>     struct software_node;
>>>>>     struct ptp_system_timestamp;
>>>>> @@ -132,7 +135,8 @@ extern void
>>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
>>>>>      * @max_speed_hz: Maximum clock rate to be used with this chip
>>>>>      *	(on this board); may be changed by the device's driver.
>>>>>      *	The spi_transfer.speed_hz can override this for each transfer.
>>>>> - * @chip_select: Chipselect, distinguishing chips handled by @controller.
>>>>> + * @chip_select: Array of physical chipselect, spi->chipselect[i] gives
>>>>> + *	the corresponding physical CS for logical CS i.
>>>>>      * @mode: The spi mode defines how data is clocked out and in.
>>>>>      *	This may be changed by the device's driver.
>>>>>      *	The "active low" default for chipselect mode can be overridden
>>>>> @@ -157,8 +161,8 @@ extern void
>>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
>>>>>      *	the device will bind to the named driver and only the named driver.
>>>>>      *	Do not set directly, because core frees it; use driver_set_override() to
>>>>>      *	set or clear it.
>>>>> - * @cs_gpiod: GPIO descriptor of the chipselect line (optional, NULL
>> when
>>>>> - *	not using a GPIO line)
>>>>> + * @cs_gpiod: Array of GPIO descriptors of the corresponding
>>>>> + chipselect
>>>> lines
>>>>> + *	(optional, NULL when not using a GPIO line)
>>>>>      * @word_delay: delay to be inserted between consecutive
>>>>>      *	words of a transfer
>>>>>      * @cs_setup: delay to be introduced by the controller after CS
>>>>> is asserted @@ -167,6 +171,7 @@ extern void
>>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
>>>>>      *	deasserted. If @cs_change_delay is used from @spi_transfer, then
>>>> the
>>>>>      *	two delays will be added up.
>>>>>      * @pcpu_statistics: statistics for the spi_device
>>>>> + * @cs_index_mask: Bit mask of the active chipselect(s) in the
>>>>> + chipselect array
>>>>>      *
>>>>>      * A @spi_device is used to interchange data between an SPI slave
>>>>>      * (usually a discrete chip) and CPU memory.
>>>>> @@ -182,7 +187,7 @@ struct spi_device {
>>>>>     	struct spi_controller	*controller;
>>>>>     	struct spi_controller	*master;	/* Compatibility layer */
>>>>>     	u32			max_speed_hz;
>>>>> -	u8			chip_select;
>>>>> +	u8			chip_select[SPI_CS_CNT_MAX];
>>>>>     	u8			bits_per_word;
>>>>>     	bool			rt;
>>>>>     #define SPI_NO_TX		BIT(31)		/* No transmit wire */
>>>>> @@ -213,7 +218,7 @@ struct spi_device {
>>>>>     	void			*controller_data;
>>>>>     	char			modalias[SPI_NAME_SIZE];
>>>>>     	const char		*driver_override;
>>>>> -	struct gpio_desc	*cs_gpiod;	/* Chip select GPIO descriptor
>>>> */
>>>>> +	struct gpio_desc	*cs_gpiod[SPI_CS_CNT_MAX];	/* Chip select
>>>> gpio desc */
>>>>>     	struct spi_delay	word_delay; /* Inter-word delay */
>>>>>     	/* CS delays */
>>>>>     	struct spi_delay	cs_setup;
>>>>> @@ -223,6 +228,13 @@ struct spi_device {
>>>>>     	/* The statistics */
>>>>>     	struct spi_statistics __percpu	*pcpu_statistics;
>>>>>
>>>>> +	/* Bit mask of the chipselect(s) that the driver need to use from
>>>>> +	 * the chipselect array.When the controller is capable to handle
>>>>> +	 * multiple chip selects & memories are connected in parallel
>>>>> +	 * then more than one bit need to be set in cs_index_mask.
>>>>> +	 */
>>>>> +	u32			cs_index_mask : SPI_CS_CNT_MAX;
>>>>> +
>>>>>     	/*
>>>>>     	 * Likely need more hooks for more protocol options affecting how
>>>>>     	 * the controller talks to each chip, like:
>>>>> @@ -279,22 +291,33 @@ static inline void *spi_get_drvdata(const
>>>>> struct spi_device *spi)
>>>>>
>>>>>     static inline u8 spi_get_chipselect(const struct spi_device *spi, u8 idx)
>>>>>     {
>>>>> -	return spi->chip_select;
>>>>> +	return spi->chip_select[idx];
>>>>>     }
>>>>>
>>>>>     static inline void spi_set_chipselect(struct spi_device *spi, u8
>>>>> idx, u8
>>>> chipselect)
>>>>>     {
>>>>> -	spi->chip_select = chipselect;
>>>>> +	spi->chip_select[idx] = chipselect;
>>>>>     }
>>>>>
>>>>>     static inline struct gpio_desc *spi_get_csgpiod(const struct
>>>>> spi_device *spi,
>>>> u8 idx)
>>>>>     {
>>>>> -	return spi->cs_gpiod;
>>>>> +	return spi->cs_gpiod[idx];
>>>>>     }
>>>>>
>>>>>     static inline void spi_set_csgpiod(struct spi_device *spi, u8
>>>>> idx, struct
>>>> gpio_desc *csgpiod)
>>>>>     {
>>>>> -	spi->cs_gpiod = csgpiod;
>>>>> +	spi->cs_gpiod[idx] = csgpiod;
>>>>> +}
>>>>> +
>>>>> +static inline bool spi_is_csgpiod(struct spi_device *spi) {
>>>>> +	u8 idx;
>>>>> +
>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>>> +		if (spi_get_csgpiod(spi, idx))
>>>>> +			return true;
>>>>> +	}
>>>>> +	return false;
>>>>>     }
>>>>>
>>>>>     /**
>>>>> @@ -399,6 +422,8 @@ extern struct spi_device
>>>> *spi_new_ancillary_device(struct spi_device *spi, u8 ch
>>>>>      * @bus_lock_spinlock: spinlock for SPI bus locking
>>>>>      * @bus_lock_mutex: mutex for exclusion of multiple callers
>>>>>      * @bus_lock_flag: indicates that the SPI bus is locked for
>>>>> exclusive use
>>>>> + * @multi_cs_cap: indicates that the SPI Controller can assert/de-assert
>>>>> + *	more than one chip select at once.
>>>>>      * @setup: updates the device mode and clocking records used by a
>>>>>      *	device's SPI controller; protocol code may call this.  This
>>>>>      *	must fail if an unrecognized or unsupported mode is requested.
>>>>> @@ -567,6 +592,11 @@ struct spi_controller {
>>>>>     #define SPI_CONTROLLER_MUST_TX		BIT(4)	/* Requires tx
>> */
>>>>>     #define SPI_CONTROLLER_GPIO_SS		BIT(5)	/* GPIO CS
>> must
>>>> select slave */
>>>>>     #define SPI_CONTROLLER_SUSPENDED	BIT(6)	/* Currently
>>>> suspended */
>>>>> +	/*
>>>>> +	 * The spi-controller has multi chip select capability and can
>>>>> +	 * assert/de-assert more than one chip select at once.
>>>>> +	 */
>>>>> +#define SPI_CONTROLLER_MULTI_CS		BIT(7)
>>>>>
>>>>>     	/* Flag indicating if the allocation of this struct is devres-managed */
>>>>>     	bool			devm_allocated;
>>>>> @@ -677,7 +707,8 @@ struct spi_controller {
>>>>>     	bool				rt;
>>>>>     	bool				auto_runtime_pm;
>>>>>     	bool				cur_msg_mapped;
>>>>> -	char				last_cs;
>>>>> +	char				last_cs[SPI_CS_CNT_MAX];
>>>>> +	char				last_cs_index_mask;
>>>>>     	bool				last_cs_mode_high;
>>>>>     	bool                            fallback;
>>>>>     	struct completion               xfer_completion;
[    0.000000] Linux version 6.7.0-rc1-next-20231115+ (sbinding@sbinding-cirrus-dsktp2) (gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0, GNU ld (GNU Binutils for Ubuntu) 2.38) #738 SMP PREEMPT_DYNAMIC Tue Nov 21 17:11:55 GMT 2023
[    0.000000] Command line: BOOT_IMAGE=/boot/vmlinuz-6.7.0-rc1-next-20231115+ root=UUID=061502a9-205e-4b73-8251-1220e770c5d8 ro quiet splash pcie_aspm=off log_buf_len=10M trace_event=gpio:* vt.handoff=7
[    0.000000] KERNEL supported cpus:
[    0.000000]   Intel GenuineIntel
[    0.000000]   AMD AuthenticAMD
[    0.000000]   Hygon HygonGenuine
[    0.000000]   Centaur CentaurHauls
[    0.000000]   zhaoxin   Shanghai  
[    0.000000] x86/split lock detection: #AC: crashing the kernel on kernel split_locks and warning on user-space split_locks
[    0.000000] BIOS-provided physical RAM map:
[    0.000000] BIOS-e820: [mem 0x0000000000000000-0x000000000009efff] usable
[    0.000000] BIOS-e820: [mem 0x000000000009f000-0x00000000000fffff] reserved
[    0.000000] BIOS-e820: [mem 0x0000000000100000-0x000000003b8dcfff] usable
[    0.000000] BIOS-e820: [mem 0x000000003b8dd000-0x0000000041933fff] reserved
[    0.000000] BIOS-e820: [mem 0x0000000041934000-0x0000000041b33fff] ACPI NVS
[    0.000000] BIOS-e820: [mem 0x0000000041b34000-0x0000000041bfefff] ACPI data
[    0.000000] BIOS-e820: [mem 0x0000000041bff000-0x0000000041bfffff] usable
[    0.000000] BIOS-e820: [mem 0x0000000041c00000-0x0000000047ffffff] reserved
[    0.000000] BIOS-e820: [mem 0x0000000049400000-0x00000000495fffff] reserved
[    0.000000] BIOS-e820: [mem 0x000000004c000000-0x00000000547fffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000c0000000-0x00000000cfffffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000fed20000-0x00000000fed7ffff] reserved
[    0.000000] BIOS-e820: [mem 0x00000000ff000000-0x00000000ffffffff] reserved
[    0.000000] BIOS-e820: [mem 0x0000000100000000-0x00000008ab7fffff] usable
[    0.000000] NX (Execute Disable) protection: active
[    0.000000] APIC: Static calls initialized
[    0.000000] efi: EFI v2.7 by HP
[    0.000000] efi: ACPI=0x41bfe000 ACPI 2.0=0x41bfe014 TPMFinalLog=0x41a50000 SMBIOS=0x3c04c000 ESRT=0x3c0a8098 MEMATTR=0x2ec52018 MOKvar=0x3c04b000 RNG=0x41b4d018 TPMEventLog=0x2e9c6018 
[    0.000000] random: crng init done
[    0.000000] efi: Remove mem452: MMIO range=[0xc0000000-0xcfffffff] (256MB) from e820 map
[    0.000000] e820: remove [mem 0xc0000000-0xcfffffff] reserved
[    0.000000] efi: Remove mem454: MMIO range=[0xff000000-0xffffffff] (16MB) from e820 map
[    0.000000] e820: remove [mem 0xff000000-0xffffffff] reserved
[    0.000000] SMBIOS 3.4 present.
[    0.000000] DMI: HP HP ZBook Fury 16 G9 Mobile Workstation PC/89C6, BIOS U96 Ver. 92.25.02 09/20/2023
[    0.000000] tsc: Detected 2400.000 MHz processor
[    0.000000] tsc: Detected 2419.200 MHz TSC
[    0.000003] e820: update [mem 0x00000000-0x00000fff] usable ==> reserved
[    0.000005] e820: remove [mem 0x000a0000-0x000fffff] usable
[    0.000009] last_pfn = 0x8ab800 max_arch_pfn = 0x400000000
[    0.000013] total RAM covered: 62720M
[    0.000086] Found optimal setting for mtrr clean up
[    0.000087]  gran_size: 64K 	chunk_size: 64K 	num_reg: 6  	lose cover RAM: 0G
[    0.000089] MTRR map: 5 entries (3 fixed + 2 variable; max 23), built from 10 variable MTRRs
[    0.000090] x86/PAT: Configuration [0-7]: WB  WC  UC- UC  WB  WP  UC- WT  
[    0.000538] e820: update [mem 0x50000000-0xffffffff] usable ==> reserved
[    0.000540] last_pfn = 0x41c00 max_arch_pfn = 0x400000000
[    0.005806] esrt: Reserving ESRT space from 0x000000003c0a8098 to 0x000000003c0a8170.
[    0.005830] Using GB pages for direct mapping
[    0.014923] printk: log_buf_len: 16777216 bytes
[    0.014924] printk: early log buf free: 258720(98%)
[    0.014925] Secure boot disabled
[    0.014925] RAMDISK: [mem 0x26191000-0x2a584fff]
[    0.014929] ACPI: Early table checksum verification disabled
[    0.014932] ACPI: RSDP 0x0000000041BFE014 000024 (v02 HPQOEM)
[    0.014935] ACPI: XSDT 0x0000000041B52228 000194 (v01 HPQOEM SLIC-BPC 00000000      01000013)
[    0.014939] ACPI: FACP 0x0000000041BDB000 000114 (v06 HPQOEM SLIC-BPC 00000000 HP   00000001)
[    0.014943] ACPI: DSDT 0x0000000041B85000 052E09 (v02 HPQOEM 89C6     00000000 INTL 20200717)
[    0.014945] ACPI: FACS 0x0000000041A2D000 000040
[    0.014947] ACPI: SSDT 0x0000000041BFC000 000FDF (v02 HP     PSENWSAR 00001000 INTL 20200717)
[    0.014949] ACPI: RTMA 0x0000000041BFB000 00009E (v01 HP     _HBMART_ 00001000 HP   00000001)
[    0.014951] ACPI: SSDT 0x0000000041BF9000 0017B3 (v02 HP     UcsiAcpi 00000001 INTL 20200717)
[    0.014952] ACPI: SSDT 0x0000000041BF8000 0000FB (v02 HP     UcsiCntr 00000001 INTL 20200717)
[    0.014954] ACPI: SSDT 0x0000000041BF7000 0002D7 (v02 HP     NVTEC    00000001 INTL 20200717)
[    0.014956] ACPI: SSDT 0x0000000041BF6000 00013E (v02 HP     ShmTable 00000001 INTL 20200717)
[    0.014958] ACPI: SSDT 0x0000000041BF5000 00038C (v02 PmaxDv Pmax_Dev 00000001 INTL 20200717)
[    0.014959] ACPI: SSDT 0x0000000041BEF000 005D34 (v02 CpuRef CpuSsdt  00003000 INTL 20200717)
[    0.014961] ACPI: OEML 0x0000000041BEE000 000028 (v03 HPQOEM ADL      00000002      01000013)
[    0.014963] ACPI: SSDT 0x0000000041BEC000 001D8A (v02 HP     LAPTOPPC 00001000 INTL 20200717)
[    0.014965] ACPI: SSDT 0x0000000041BE7000 003256 (v02 INTEL  DTbtSsdt 00001000 INTL 20200717)
[    0.014966] ACPI: SSDT 0x0000000041BE6000 0005FB (v02 HPQOEM Tpm2Tabl 00001000 INTL 20200717)
[    0.014968] ACPI: TPM2 0x0000000041BE5000 00004C (v04 HPQOEM ADL      00000002      01000013)
[    0.014970] ACPI: SSDT 0x0000000041BE2000 002B26 (v02 SaSsdt SaSsdt   00003000 INTL 20200717)
[    0.014972] ACPI: SSDT 0x0000000041BDE000 0033C4 (v02 INTEL  IgfxSsdt 00003000 INTL 20200717)
[    0.014973] ACPI: MSDM 0x0000000041BDD000 000055 (v03 HPQOEM SLIC-BPC 00000000 HP   00000001)
[    0.014975] ACPI: WSMT 0x0000000041BDC000 000028 (v01 HPQOEM 89C6     00000001 HP   00000001)
[    0.014977] ACPI: HPET 0x0000000041BDA000 000038 (v01 HPQOEM 89C6     00000001 HP   00000001)
[    0.014979] ACPI: APIC 0x0000000041BD9000 0001DC (v05 HPQOEM 89C6     00000001 HP   00000001)
[    0.014980] ACPI: MCFG 0x0000000041BFD000 00003C (v01 HPQOEM 89C6     00000001 HP   00000001)
[    0.014982] ACPI: SSDT 0x0000000041B84000 0009D9 (v02 HPQOEM HP_XHC_P 00000000 INTL 20200717)
[    0.014984] ACPI: SSDT 0x0000000041B7D000 006B06 (v02 DptfTb DptfTabl 00001000 INTL 20200717)
[    0.014986] ACPI: LPIT 0x0000000041B7C000 0000CC (v01 HPQOEM          00000000      00000000)
[    0.014987] ACPI: WSMT 0x0000000041B7B000 000028 (v01 HPQOEM          00000000      00000000)
[    0.014989] ACPI: NHLT 0x0000000041B79000 001B35 (v00 HPQOEM ADL      00000002      01000013)
[    0.014991] ACPI: DMAR 0x0000000041B78000 000088 (v01 INTEL  ADL      00000002 INTL 01000013)
[    0.014993] ACPI: FPDT 0x0000000041B77000 000044 (v01 HPQOEM ADL      00000002      01000013)
[    0.014994] ACPI: SSDT 0x0000000041B73000 0039DA (v02 SocGpe SocGpe   00003000 INTL 20200717)
[    0.014996] ACPI: SSDT 0x0000000041B6F000 0039DA (v02 SocCmn SocCmn   00003000 INTL 20200717)
[    0.014998] ACPI: SSDT 0x0000000041B6E000 0002C7 (v02 HP     HPNBCONV 00001000 INTL 20200717)
[    0.015000] ACPI: SSDT 0x0000000041B6D000 000435 (v02 HP     INTTPL   00001000 INTL 20200717)
[    0.015001] ACPI: SSDT 0x0000000041B64000 008658 (v02 HP     HPDTBT   00001000 INTL 20200717)
[    0.015003] ACPI: SSDT 0x0000000041B61000 0024CA (v02 Rtd3   AdlSBTbt 00001000 INTL 20200717)
[    0.015005] ACPI: SSDT 0x0000000041B5C000 00483C (v01 HP     NVHG     00001000 INTL 20200717)
[    0.015007] ACPI: SSDT 0x0000000041B5B000 000522 (v01 HP     NVPOWERC 00001000 INTL 20200717)
[    0.015008] ACPI: SSDT 0x0000000041B57000 003A4A (v02 HP     HPINNBWL 00001000 INTL 20200717)
[    0.015010] ACPI: SSDT 0x0000000041B56000 000032 (v02 HP     HPCONDEV 00001000 INTL 20200717)
[    0.015012] ACPI: SSDT 0x0000000041B55000 000116 (v02 HP     HPSANIMD 00001000 INTL 20200717)
[    0.015014] ACPI: SSDT 0x0000000041B53000 001027 (v02 HP     HPINTELG 00001000 INTL 20200717)
[    0.015015] ACPI: SSDT 0x0000000041BD8000 000069 (v02 HP     HPCAHWID 00001000 INTL 20200717)
[    0.015017] ACPI: SSDT 0x0000000041B51000 00004B (v01 HP     CAPAHWID 00001000 INTL 20200717)
[    0.015019] ACPI: BGRT 0x0000000041B4F000 000038 (v01 HPQOEM ADL      00000002      01000013)
[    0.015021] ACPI: ASF! 0x0000000041B50000 0000A0 (v32 HPQOEM  UYAMIHC 00000002      01000013)
[    0.015022] ACPI: PHAT 0x0000000041B4E000 0004EA (v01 HPQOEM SLIC-BPC 00000005 MSFT 0100000D)
[    0.015024] ACPI: SSDT 0x0000000041BEB000 0002F9 (v02 HP     PwrCtlEv 00000001 INTL 20200717)
[    0.015025] ACPI: Reserving FACP table memory at [mem 0x41bdb000-0x41bdb113]
[    0.015026] ACPI: Reserving DSDT table memory at [mem 0x41b85000-0x41bd7e08]
[    0.015027] ACPI: Reserving FACS table memory at [mem 0x41a2d000-0x41a2d03f]
[    0.015027] ACPI: Reserving SSDT table memory at [mem 0x41bfc000-0x41bfcfde]
[    0.015028] ACPI: Reserving RTMA table memory at [mem 0x41bfb000-0x41bfb09d]
[    0.015028] ACPI: Reserving SSDT table memory at [mem 0x41bf9000-0x41bfa7b2]
[    0.015029] ACPI: Reserving SSDT table memory at [mem 0x41bf8000-0x41bf80fa]
[    0.015029] ACPI: Reserving SSDT table memory at [mem 0x41bf7000-0x41bf72d6]
[    0.015030] ACPI: Reserving SSDT table memory at [mem 0x41bf6000-0x41bf613d]
[    0.015030] ACPI: Reserving SSDT table memory at [mem 0x41bf5000-0x41bf538b]
[    0.015030] ACPI: Reserving SSDT table memory at [mem 0x41bef000-0x41bf4d33]
[    0.015031] ACPI: Reserving OEML table memory at [mem 0x41bee000-0x41bee027]
[    0.015031] ACPI: Reserving SSDT table memory at [mem 0x41bec000-0x41bedd89]
[    0.015032] ACPI: Reserving SSDT table memory at [mem 0x41be7000-0x41bea255]
[    0.015032] ACPI: Reserving SSDT table memory at [mem 0x41be6000-0x41be65fa]
[    0.015033] ACPI: Reserving TPM2 table memory at [mem 0x41be5000-0x41be504b]
[    0.015033] ACPI: Reserving SSDT table memory at [mem 0x41be2000-0x41be4b25]
[    0.015033] ACPI: Reserving SSDT table memory at [mem 0x41bde000-0x41be13c3]
[    0.015034] ACPI: Reserving MSDM table memory at [mem 0x41bdd000-0x41bdd054]
[    0.015034] ACPI: Reserving WSMT table memory at [mem 0x41bdc000-0x41bdc027]
[    0.015035] ACPI: Reserving HPET table memory at [mem 0x41bda000-0x41bda037]
[    0.015035] ACPI: Reserving APIC table memory at [mem 0x41bd9000-0x41bd91db]
[    0.015035] ACPI: Reserving MCFG table memory at [mem 0x41bfd000-0x41bfd03b]
[    0.015036] ACPI: Reserving SSDT table memory at [mem 0x41b84000-0x41b849d8]
[    0.015036] ACPI: Reserving SSDT table memory at [mem 0x41b7d000-0x41b83b05]
[    0.015037] ACPI: Reserving LPIT table memory at [mem 0x41b7c000-0x41b7c0cb]
[    0.015037] ACPI: Reserving WSMT table memory at [mem 0x41b7b000-0x41b7b027]
[    0.015038] ACPI: Reserving NHLT table memory at [mem 0x41b79000-0x41b7ab34]
[    0.015038] ACPI: Reserving DMAR table memory at [mem 0x41b78000-0x41b78087]
[    0.015038] ACPI: Reserving FPDT table memory at [mem 0x41b77000-0x41b77043]
[    0.015039] ACPI: Reserving SSDT table memory at [mem 0x41b73000-0x41b769d9]
[    0.015039] ACPI: Reserving SSDT table memory at [mem 0x41b6f000-0x41b729d9]
[    0.015040] ACPI: Reserving SSDT table memory at [mem 0x41b6e000-0x41b6e2c6]
[    0.015040] ACPI: Reserving SSDT table memory at [mem 0x41b6d000-0x41b6d434]
[    0.015040] ACPI: Reserving SSDT table memory at [mem 0x41b64000-0x41b6c657]
[    0.015041] ACPI: Reserving SSDT table memory at [mem 0x41b61000-0x41b634c9]
[    0.015041] ACPI: Reserving SSDT table memory at [mem 0x41b5c000-0x41b6083b]
[    0.015042] ACPI: Reserving SSDT table memory at [mem 0x41b5b000-0x41b5b521]
[    0.015042] ACPI: Reserving SSDT table memory at [mem 0x41b57000-0x41b5aa49]
[    0.015043] ACPI: Reserving SSDT table memory at [mem 0x41b56000-0x41b56031]
[    0.015043] ACPI: Reserving SSDT table memory at [mem 0x41b55000-0x41b55115]
[    0.015043] ACPI: Reserving SSDT table memory at [mem 0x41b53000-0x41b54026]
[    0.015044] ACPI: Reserving SSDT table memory at [mem 0x41bd8000-0x41bd8068]
[    0.015044] ACPI: Reserving SSDT table memory at [mem 0x41b51000-0x41b5104a]
[    0.015045] ACPI: Reserving BGRT table memory at [mem 0x41b4f000-0x41b4f037]
[    0.015045] ACPI: Reserving ASF! table memory at [mem 0x41b50000-0x41b5009f]
[    0.015046] ACPI: Reserving PHAT table memory at [mem 0x41b4e000-0x41b4e4e9]
[    0.015046] ACPI: Reserving SSDT table memory at [mem 0x41beb000-0x41beb2f8]
[    0.015371] No NUMA configuration found
[    0.015372] Faking a node at [mem 0x0000000000000000-0x00000008ab7fffff]
[    0.015376] NODE_DATA(0) allocated [mem 0x8a6fd5000-0x8a6ffffff]
[    0.015500] Zone ranges:
[    0.015500]   DMA      [mem 0x0000000000001000-0x0000000000ffffff]
[    0.015501]   DMA32    [mem 0x0000000001000000-0x00000000ffffffff]
[    0.015502]   Normal   [mem 0x0000000100000000-0x00000008ab7fffff]
[    0.015503]   Device   empty
[    0.015504] Movable zone start for each node
[    0.015505] Early memory node ranges
[    0.015505]   node   0: [mem 0x0000000000001000-0x000000000009efff]
[    0.015506]   node   0: [mem 0x0000000000100000-0x000000003b8dcfff]
[    0.015507]   node   0: [mem 0x0000000041bff000-0x0000000041bfffff]
[    0.015507]   node   0: [mem 0x0000000100000000-0x00000008ab7fffff]
[    0.015509] Initmem setup node 0 [mem 0x0000000000001000-0x00000008ab7fffff]
[    0.015513] On node 0, zone DMA: 1 pages in unavailable ranges
[    0.015530] On node 0, zone DMA: 97 pages in unavailable ranges
[    0.016723] On node 0, zone DMA32: 25378 pages in unavailable ranges
[    0.050732] On node 0, zone Normal: 25600 pages in unavailable ranges
[    0.050865] On node 0, zone Normal: 18432 pages in unavailable ranges
[    0.050904] Reserving Intel graphics memory at [mem 0x50800000-0x547fffff]
[    0.052574] ACPI: PM-Timer IO Port: 0x1808
[    0.052581] ACPI: LAPIC_NMI (acpi_id[0x01] high edge lint[0x1])
[    0.052582] ACPI: LAPIC_NMI (acpi_id[0x02] high edge lint[0x1])
[    0.052582] ACPI: LAPIC_NMI (acpi_id[0x03] high edge lint[0x1])
[    0.052583] ACPI: LAPIC_NMI (acpi_id[0x04] high edge lint[0x1])
[    0.052583] ACPI: LAPIC_NMI (acpi_id[0x05] high edge lint[0x1])
[    0.052584] ACPI: LAPIC_NMI (acpi_id[0x06] high edge lint[0x1])
[    0.052584] ACPI: LAPIC_NMI (acpi_id[0x07] high edge lint[0x1])
[    0.052584] ACPI: LAPIC_NMI (acpi_id[0x08] high edge lint[0x1])
[    0.052585] ACPI: LAPIC_NMI (acpi_id[0x09] high edge lint[0x1])
[    0.052585] ACPI: LAPIC_NMI (acpi_id[0x0a] high edge lint[0x1])
[    0.052586] ACPI: LAPIC_NMI (acpi_id[0x0b] high edge lint[0x1])
[    0.052586] ACPI: LAPIC_NMI (acpi_id[0x0c] high edge lint[0x1])
[    0.052586] ACPI: LAPIC_NMI (acpi_id[0x0d] high edge lint[0x1])
[    0.052587] ACPI: LAPIC_NMI (acpi_id[0x0e] high edge lint[0x1])
[    0.052587] ACPI: LAPIC_NMI (acpi_id[0x0f] high edge lint[0x1])
[    0.052588] ACPI: LAPIC_NMI (acpi_id[0x10] high edge lint[0x1])
[    0.052588] ACPI: LAPIC_NMI (acpi_id[0x11] high edge lint[0x1])
[    0.052588] ACPI: LAPIC_NMI (acpi_id[0x12] high edge lint[0x1])
[    0.052589] ACPI: LAPIC_NMI (acpi_id[0x13] high edge lint[0x1])
[    0.052589] ACPI: LAPIC_NMI (acpi_id[0x14] high edge lint[0x1])
[    0.052590] ACPI: LAPIC_NMI (acpi_id[0x15] high edge lint[0x1])
[    0.052590] ACPI: LAPIC_NMI (acpi_id[0x16] high edge lint[0x1])
[    0.052590] ACPI: LAPIC_NMI (acpi_id[0x17] high edge lint[0x1])
[    0.052591] ACPI: LAPIC_NMI (acpi_id[0x00] high edge lint[0x1])
[    0.052627] IOAPIC[0]: apic_id 2, version 32, address 0xfec00000, GSI 0-119
[    0.052629] ACPI: INT_SRC_OVR (bus 0 bus_irq 0 global_irq 2 dfl dfl)
[    0.052630] ACPI: INT_SRC_OVR (bus 0 bus_irq 9 global_irq 9 high level)
[    0.052633] ACPI: Using ACPI (MADT) for SMP configuration information
[    0.052633] ACPI: HPET id: 0x8086a201 base: 0xfed00000
[    0.052642] e820: update [mem 0x2e866000-0x2e8dbfff] usable ==> reserved
[    0.052648] TSC deadline timer available
[    0.052648] smpboot: Allowing 24 CPUs, 0 hotplug CPUs
[    0.052656] PM: hibernation: Registered nosave memory: [mem 0x00000000-0x00000fff]
[    0.052657] PM: hibernation: Registered nosave memory: [mem 0x0009f000-0x000fffff]
[    0.052658] PM: hibernation: Registered nosave memory: [mem 0x2e866000-0x2e8dbfff]
[    0.052659] PM: hibernation: Registered nosave memory: [mem 0x3b8dd000-0x41933fff]
[    0.052660] PM: hibernation: Registered nosave memory: [mem 0x41934000-0x41b33fff]
[    0.052660] PM: hibernation: Registered nosave memory: [mem 0x41b34000-0x41bfefff]
[    0.052661] PM: hibernation: Registered nosave memory: [mem 0x41c00000-0x47ffffff]
[    0.052661] PM: hibernation: Registered nosave memory: [mem 0x48000000-0x493fffff]
[    0.052662] PM: hibernation: Registered nosave memory: [mem 0x49400000-0x495fffff]
[    0.052662] PM: hibernation: Registered nosave memory: [mem 0x49600000-0x4bffffff]
[    0.052662] PM: hibernation: Registered nosave memory: [mem 0x4c000000-0x547fffff]
[    0.052663] PM: hibernation: Registered nosave memory: [mem 0x54800000-0xfed1ffff]
[    0.052663] PM: hibernation: Registered nosave memory: [mem 0xfed20000-0xfed7ffff]
[    0.052663] PM: hibernation: Registered nosave memory: [mem 0xfed80000-0xffffffff]
[    0.052664] [mem 0x54800000-0xfed1ffff] available for PCI devices
[    0.052665] Booting paravirtualized kernel on bare hardware
[    0.052667] clocksource: refined-jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645519600211568 ns
[    0.052671] setup_percpu: NR_CPUS:8192 nr_cpumask_bits:24 nr_cpu_ids:24 nr_node_ids:1
[    0.053391] percpu: Embedded 64 pages/cpu s225280 r8192 d28672 u262144
[    0.053394] pcpu-alloc: s225280 r8192 d28672 u262144 alloc=1*2097152
[    0.053396] pcpu-alloc: [0] 00 01 02 03 04 05 06 07 [0] 08 09 10 11 12 13 14 15 
[    0.053401] pcpu-alloc: [0] 16 17 18 19 20 21 22 23 
[    0.053414] Kernel command line: BOOT_IMAGE=/boot/vmlinuz-6.7.0-rc1-next-20231115+ root=UUID=061502a9-205e-4b73-8251-1220e770c5d8 ro quiet splash pcie_aspm=off log_buf_len=10M trace_event=gpio:* vt.handoff=7
[    0.053454] PCIe ASPM is disabled
[    0.053473] Unknown kernel command line parameters "splash BOOT_IMAGE=/boot/vmlinuz-6.7.0-rc1-next-20231115+", will be passed to user space.
[    0.055688] Dentry cache hash table entries: 4194304 (order: 13, 33554432 bytes, linear)
[    0.056809] Inode-cache hash table entries: 2097152 (order: 12, 16777216 bytes, linear)
[    0.056956] Fallback order for Node 0: 0 
[    0.056958] Built 1 zonelists, mobility grouping on.  Total pages: 8156698
[    0.056959] Policy zone: Normal
[    0.056964] mem auto-init: stack:off, heap alloc:on, heap free:off
[    0.056970] software IO TLB: area num 32.
[    0.105875] Memory: 32103252K/33145328K available (20480K kernel code, 4267K rwdata, 7376K rodata, 4712K init, 5184K bss, 1041820K reserved, 0K cma-reserved)
[    0.106059] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=24, Nodes=1
[    0.106081] ftrace: allocating 52890 entries in 207 pages
[    0.111619] ftrace: allocated 207 pages with 6 groups
[    0.112284] Dynamic Preempt: voluntary
[    0.112346] rcu: Preemptible hierarchical RCU implementation.
[    0.112346] rcu: 	RCU restricting CPUs from NR_CPUS=8192 to nr_cpu_ids=24.
[    0.112347] 	Trampoline variant of Tasks RCU enabled.
[    0.112348] 	Rude variant of Tasks RCU enabled.
[    0.112348] 	Tracing variant of Tasks RCU enabled.
[    0.112348] rcu: RCU calculated value of scheduler-enlistment delay is 25 jiffies.
[    0.112349] rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=24
[    0.113910] NR_IRQS: 524544, nr_irqs: 2248, preallocated irqs: 16
[    0.114204] rcu: srcu_init: Setting srcu_struct sizes based on contention.
[    0.114418] Console: colour dummy device 80x25
[    0.114420] printk: legacy console [tty0] enabled
[    0.114452] ACPI: Core revision 20230628
[    0.114711] hpet: HPET dysfunctional in PC10. Force disabled.
[    0.114711] APIC: Switch to symmetric I/O mode setup
[    0.114713] DMAR: Host address width 39
[    0.114714] DMAR: DRHD base: 0x000000fed90000 flags: 0x0
[    0.114717] DMAR: dmar0: reg_base_addr fed90000 ver 4:0 cap 1c0000c40660462 ecap 29a00f0505e
[    0.114719] DMAR: DRHD base: 0x000000fed91000 flags: 0x1
[    0.114724] DMAR: dmar1: reg_base_addr fed91000 ver 5:0 cap d2008c40660462 ecap f050da
[    0.114726] DMAR: RMRR base: 0x00000050000000 end: 0x000000547fffff
[    0.114727] DMAR-IR: IOAPIC id 2 under DRHD base  0xfed91000 IOMMU 1
[    0.114728] DMAR-IR: HPET id 0 under DRHD base 0xfed91000
[    0.114729] DMAR-IR: Queued invalidation will be enabled to support x2apic and Intr-remapping.
[    0.116288] DMAR-IR: Enabled IRQ remapping in x2apic mode
[    0.116289] x2apic enabled
[    0.116338] APIC: Switched APIC routing to: cluster x2apic
[    0.120722] clocksource: tsc-early: mask: 0xffffffffffffffff max_cycles: 0x22df1149949, max_idle_ns: 440795312789 ns
[    0.120727] Calibrating delay loop (skipped), value calculated using timer frequency.. 4838.40 BogoMIPS (lpj=9676800)
[    0.120770] x86/tme: not enabled by BIOS
[    0.120776] CPU0: Thermal monitoring enabled (TM1)
[    0.120778] x86/cpu: User Mode Instruction Prevention (UMIP) activated
[    0.120881] process: using mwait in idle threads
[    0.120883] Last level iTLB entries: 4KB 0, 2MB 0, 4MB 0
[    0.120883] Last level dTLB entries: 4KB 0, 2MB 0, 4MB 0, 1GB 0
[    0.120885] Spectre V1 : Mitigation: usercopy/swapgs barriers and __user pointer sanitization
[    0.120887] Spectre V2 : Mitigation: Enhanced / Automatic IBRS
[    0.120887] Spectre V2 : Spectre v2 / SpectreRSB mitigation: Filling RSB on context switch
[    0.120888] Spectre V2 : Spectre v2 / PBRSB-eIBRS: Retire a single CALL on VMEXIT
[    0.120889] Spectre V2 : mitigation: Enabling conditional Indirect Branch Prediction Barrier
[    0.120890] Speculative Store Bypass: Mitigation: Speculative Store Bypass disabled via prctl
[    0.120898] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
[    0.120899] x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'
[    0.120899] x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers'
[    0.120900] x86/fpu: Supporting XSAVE feature 0x200: 'Protection Keys User registers'
[    0.120900] x86/fpu: Supporting XSAVE feature 0x800: 'Control-flow User registers'
[    0.120901] x86/fpu: xstate_offset[2]:  576, xstate_sizes[2]:  256
[    0.120902] x86/fpu: xstate_offset[9]:  832, xstate_sizes[9]:    8
[    0.120902] x86/fpu: xstate_offset[11]:  840, xstate_sizes[11]:   16
[    0.120903] x86/fpu: Enabled xstate features 0xa07, context size is 856 bytes, using 'compacted' format.
[    0.124725] Freeing SMP alternatives memory: 44K
[    0.124725] pid_max: default: 32768 minimum: 301
[    0.124725] LSM: initializing lsm=lockdown,capability,landlock,yama,apparmor,integrity
[    0.124725] landlock: Up and running.
[    0.124725] Yama: becoming mindful.
[    0.124725] AppArmor: AppArmor initialized
[    0.124725] Mount-cache hash table entries: 65536 (order: 7, 524288 bytes, linear)
[    0.124725] Mountpoint-cache hash table entries: 65536 (order: 7, 524288 bytes, linear)
[    0.124725] smpboot: CPU0: 12th Gen Intel(R) Core(TM) i7-12850HX (family: 0x6, model: 0x97, stepping: 0x2)
[    0.124725] RCU Tasks: Setting shift to 5 and lim to 1 rcu_task_cb_adjust=1.
[    0.124725] RCU Tasks Rude: Setting shift to 5 and lim to 1 rcu_task_cb_adjust=1.
[    0.124725] RCU Tasks Trace: Setting shift to 5 and lim to 1 rcu_task_cb_adjust=1.
[    0.124725] Performance Events: XSAVE Architectural LBR, PEBS fmt4+-baseline,  AnyThread deprecated, Alderlake Hybrid events, 32-deep LBR, full-width counters, Intel PMU driver.
[    0.124725] core: cpu_core PMU driver: 
[    0.124725] ... version:                5
[    0.124725] ... bit width:              48
[    0.124725] ... generic registers:      8
[    0.124725] ... value mask:             0000ffffffffffff
[    0.124725] ... max period:             00007fffffffffff
[    0.124725] ... fixed-purpose events:   4
[    0.124725] ... event mask:             0001000f000000ff
[    0.124725] signal: max sigframe size: 3632
[    0.124725] Estimated ratio of average max frequency by base frequency (times 1024): 1962
[    0.124725] rcu: Hierarchical SRCU implementation.
[    0.124725] rcu: 	Max phase no-delay instances is 1000.
[    0.129797] NMI watchdog: Enabled. Permanently consumes one hw-PMU counter.
[    0.129911] smp: Bringing up secondary CPUs ...
[    0.130100] smpboot: x86: Booting SMP configuration:
[    0.130101] .... node  #0, CPUs:        #2  #4  #6  #8 #10 #12 #14 #16 #17 #18 #19 #20 #21 #22 #23
[    0.010343] core: cpu_atom PMU driver: PEBS-via-PT 
[    0.010343] ... version:                5
[    0.010343] ... bit width:              48
[    0.010343] ... generic registers:      6
[    0.010343] ... value mask:             0000ffffffffffff
[    0.010343] ... max period:             00007fffffffffff
[    0.010343] ... fixed-purpose events:   3
[    0.010343] ... event mask:             000000070000003f
[    0.144905]   #1  #3  #5  #7  #9 #11 #13 #15
[    0.164832] smp: Brought up 1 node, 24 CPUs
[    0.164832] smpboot: Max logical packages: 1
[    0.164832] smpboot: Total of 24 processors activated (116121.60 BogoMIPS)
[    0.169584] devtmpfs: initialized
[    0.169584] x86/mm: Memory block size: 128MB
[    0.170241] ACPI: PM: Registering ACPI NVS region [mem 0x41934000-0x41b33fff] (2097152 bytes)
[    0.170241] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 7645041785100000 ns
[    0.170241] futex hash table entries: 8192 (order: 7, 524288 bytes, linear)
[    0.170241] pinctrl core: initialized pinctrl subsystem
[    0.170241] PM: RTC time: 17:31:31, date: 2023-11-21
[    0.170241] NET: Registered PF_NETLINK/PF_ROUTE protocol family
[    0.170241] DMA: preallocated 4096 KiB GFP_KERNEL pool for atomic allocations
[    0.170241] DMA: preallocated 4096 KiB GFP_KERNEL|GFP_DMA pool for atomic allocations
[    0.172936] DMA: preallocated 4096 KiB GFP_KERNEL|GFP_DMA32 pool for atomic allocations
[    0.172942] audit: initializing netlink subsys (disabled)
[    0.172946] audit: type=2000 audit(1700587891.052:1): state=initialized audit_enabled=0 res=1
[    0.172946] thermal_sys: Registered thermal governor 'fair_share'
[    0.172946] thermal_sys: Registered thermal governor 'bang_bang'
[    0.172946] thermal_sys: Registered thermal governor 'step_wise'
[    0.172946] thermal_sys: Registered thermal governor 'user_space'
[    0.172946] thermal_sys: Registered thermal governor 'power_allocator'
[    0.172946] EISA bus registered
[    0.172946] cpuidle: using governor ladder
[    0.172946] cpuidle: using governor menu
[    0.172946] acpiphp: ACPI Hot Plug PCI Controller Driver version: 0.5
[    0.172946] PCI: MMCONFIG for domain 0000 [bus 00-ff] at [mem 0xc0000000-0xcfffffff] (base 0xc0000000)
[    0.172946] PCI: not using MMCONFIG
[    0.172946] PCI: Using configuration type 1 for base access
[    0.173194] ENERGY_PERF_BIAS: Set to 'normal', was 'performance'
[    0.173195] kprobes: kprobe jump-optimization is enabled. All kprobes are optimized if possible.
[    0.173195] HugeTLB: registered 1.00 GiB page size, pre-allocated 0 pages
[    0.173195] HugeTLB: 16380 KiB vmemmap can be freed for a 1.00 GiB page
[    0.173195] HugeTLB: registered 2.00 MiB page size, pre-allocated 0 pages
[    0.173195] HugeTLB: 28 KiB vmemmap can be freed for a 2.00 MiB page
[    0.173195] ACPI: Added _OSI(Module Device)
[    0.173195] ACPI: Added _OSI(Processor Device)
[    0.173195] ACPI: Added _OSI(3.0 _SCP Extensions)
[    0.173195] ACPI: Added _OSI(Processor Aggregator Device)
[    0.284293] ACPI: 30 ACPI AML tables successfully acquired and loaded
[    0.289118] ACPI: [Firmware Bug]: BIOS _OSI(Linux) query ignored
[    0.368735] ACPI: Dynamic OEM Table Load:
[    0.368735] ACPI: SSDT 0xFFFF8881034D8800 000394 (v02 PmRef  Cpu0Cst  00003001 INTL 20200717)
[    0.369073] ACPI: Dynamic OEM Table Load:
[    0.369081] ACPI: SSDT 0xFFFF88810547B000 00053F (v02 PmRef  Cpu0Ist  00003000 INTL 20200717)
[    0.370396] ACPI: Dynamic OEM Table Load:
[    0.370404] ACPI: SSDT 0xFFFF888105400A00 0001AB (v02 PmRef  Cpu0Psd  00003000 INTL 20200717)
[    0.371626] ACPI: Dynamic OEM Table Load:
[    0.371633] ACPI: SSDT 0xFFFF888105479800 0004BD (v02 PmRef  Cpu0Hwp  00003000 INTL 20200717)
[    0.373314] ACPI: Dynamic OEM Table Load:
[    0.373326] ACPI: SSDT 0xFFFF8881034D4000 001BAF (v02 PmRef  ApIst    00003000 INTL 20200717)
[    0.375399] ACPI: Dynamic OEM Table Load:
[    0.375408] ACPI: SSDT 0xFFFF888105484000 001038 (v02 PmRef  ApHwp    00003000 INTL 20200717)
[    0.377221] ACPI: Dynamic OEM Table Load:
[    0.377232] ACPI: SSDT 0xFFFF888105482000 001349 (v02 PmRef  ApPsd    00003000 INTL 20200717)
[    0.379109] ACPI: Dynamic OEM Table Load:
[    0.379118] ACPI: SSDT 0xFFFF8881034E3000 000FBB (v02 PmRef  ApCst    00003000 INTL 20200717)
[    0.391168] ACPI: _OSC evaluated successfully for all CPUs
[    0.391218] ACPI: EC: EC started
[    0.391219] ACPI: EC: interrupt blocked
[    0.414702] ACPI: EC: EC_CMD/EC_SC=0x66, EC_DATA=0x62
[    0.414705] ACPI: \_SB_.PC00.LPCB.EC0_: Boot DSDT EC used to handle transactions
[    0.414706] ACPI: Interpreter enabled
[    0.414774] ACPI: PM: (supports S0 S3 S4 S5)
[    0.414774] ACPI: Using IOAPIC for interrupt routing
[    0.416485] PCI: MMCONFIG for domain 0000 [bus 00-ff] at [mem 0xc0000000-0xcfffffff] (base 0xc0000000)
[    0.417954] PCI: MMCONFIG at [mem 0xc0000000-0xcfffffff] reserved as ACPI motherboard resource
[    0.417963] PCI: Using host bridge windows from ACPI; if necessary, use "pci=nocrs" and report a bug
[    0.417964] PCI: Ignoring E820 reservations for host bridge windows
[    0.418886] ACPI: Enabled 8 GPEs in block 00 to 7F
[    0.425368] ACPI: \_SB_.PC00.PEG1.PXP_: New power resource
[    0.432018] ACPI: \_SB_.PC00.XHCI.RHUB.HS14.BTPR: New power resource
[    0.438525] ACPI: \_SB_.PC00.CNVW.WRST: New power resource
[    0.445011] ACPI: \_SB_.PC00.RP09.PXP_: New power resource
[    0.464813] ACPI: \PIN_: New power resource
[    0.465292] ACPI: PCI Root Bridge [PC00] (domain 0000 [bus 00-fe])
[    0.465298] acpi PNP0A08:00: _OSC: OS supports [ExtendedConfig Segments MSI EDR HPX-Type3]
[    0.465300] acpi PNP0A08:00: _OSC: not requesting OS control; OS requires [ExtendedConfig ASPM ClockPM MSI]
[    0.470893] PCI host bridge to bus 0000:00
[    0.470895] pci_bus 0000:00: root bus resource [io  0x0000-0x0cf7 window]
[    0.470897] pci_bus 0000:00: root bus resource [io  0x0d00-0xffff window]
[    0.470898] pci_bus 0000:00: root bus resource [mem 0x000a0000-0x000bffff window]
[    0.470899] pci_bus 0000:00: root bus resource [mem 0x54800000-0xbfffffff window]
[    0.470900] pci_bus 0000:00: root bus resource [mem 0x4000000000-0x7fffffffff window]
[    0.470902] pci_bus 0000:00: root bus resource [bus 00-fe]
[    0.470934] pci 0000:00:00.0: [8086:4637] type 00 class 0x060000
[    0.471040] pci 0000:00:01.0: [8086:460d] type 01 class 0x060400
[    0.471086] pci 0000:00:01.0: PME# supported from D0 D3hot D3cold
[    0.471105] pci 0000:00:01.0: PTM enabled (root), 4ns granularity
[    0.471790] pci 0000:00:02.0: [8086:4688] type 00 class 0x030000
[    0.471797] pci 0000:00:02.0: reg 0x10: [mem 0x645b000000-0x645bffffff 64bit]
[    0.471802] pci 0000:00:02.0: reg 0x18: [mem 0x4000000000-0x400fffffff 64bit pref]
[    0.471805] pci 0000:00:02.0: reg 0x20: [io  0x5000-0x503f]
[    0.471818] pci 0000:00:02.0: BAR 2: assigned to efifb
[    0.471819] pci 0000:00:02.0: DMAR: Skip IOMMU disabling for graphics
[    0.471821] pci 0000:00:02.0: Video device with shadowed ROM at [mem 0x000c0000-0x000dffff]
[    0.471841] pci 0000:00:02.0: reg 0x344: [mem 0x00000000-0x00ffffff 64bit]
[    0.471842] pci 0000:00:02.0: VF(n) BAR0 space: [mem 0x00000000-0x06ffffff 64bit] (contains BAR0 for 7 VFs)
[    0.471846] pci 0000:00:02.0: reg 0x34c: [mem 0x00000000-0x1fffffff 64bit pref]
[    0.471847] pci 0000:00:02.0: VF(n) BAR2 space: [mem 0x00000000-0xdfffffff 64bit pref] (contains BAR2 for 7 VFs)
[    0.472000] pci 0000:00:04.0: [8086:461d] type 00 class 0x118000
[    0.472012] pci 0000:00:04.0: reg 0x10: [mem 0x645cb00000-0x645cb1ffff 64bit]
[    0.472338] pci 0000:00:06.0: [8086:464d] type 01 class 0x060400
[    0.472404] pci 0000:00:06.0: PME# supported from D0 D3hot D3cold
[    0.472427] pci 0000:00:06.0: PTM enabled (root), 4ns granularity
[    0.473172] pci 0000:00:08.0: [8086:464f] type 00 class 0x088000
[    0.473179] pci 0000:00:08.0: reg 0x10: [mem 0x645cb66000-0x645cb66fff 64bit]
[    0.473267] pci 0000:00:0a.0: [8086:467d] type 00 class 0x118000
[    0.473274] pci 0000:00:0a.0: reg 0x10: [mem 0x645cb40000-0x645cb47fff 64bit]
[    0.473290] pci 0000:00:0a.0: enabling Extended Tags
[    0.473388] pci 0000:00:12.0: [8086:7af8] type 00 class 0x070000
[    0.473408] pci 0000:00:12.0: reg 0x10: [mem 0x645cb30000-0x645cb3ffff 64bit]
[    0.473478] pci 0000:00:12.0: PME# supported from D0 D3hot
[    0.474105] pci 0000:00:14.0: [8086:7ae0] type 00 class 0x0c0330
[    0.474121] pci 0000:00:14.0: reg 0x10: [mem 0x645cb20000-0x645cb2ffff 64bit]
[    0.474183] pci 0000:00:14.0: PME# supported from D3hot D3cold
[    0.474752] pci 0000:00:14.2: [8086:7aa7] type 00 class 0x050000
[    0.474771] pci 0000:00:14.2: reg 0x10: [mem 0x645cb58000-0x645cb5bfff 64bit]
[    0.474784] pci 0000:00:14.2: reg 0x18: [mem 0x645cb65000-0x645cb65fff 64bit]
[    0.474928] pci 0000:00:14.3: [8086:7af0] type 00 class 0x028000
[    0.474970] pci 0000:00:14.3: reg 0x10: [mem 0x645cb54000-0x645cb57fff 64bit]
[    0.475090] pci 0000:00:14.3: PME# supported from D0 D3hot D3cold
[    0.475414] pci 0000:00:15.0: [8086:7acc] type 00 class 0x0c8000
[    0.475482] pci 0000:00:15.0: reg 0x10: [mem 0x00000000-0x00000fff 64bit]
[    0.475972] pci 0000:00:15.1: [8086:7acd] type 00 class 0x0c8000
[    0.476043] pci 0000:00:15.1: reg 0x10: [mem 0x00000000-0x00000fff 64bit]
[    0.476528] pci 0000:00:15.2: [8086:7ace] type 00 class 0x0c8000
[    0.476595] pci 0000:00:15.2: reg 0x10: [mem 0x00000000-0x00000fff 64bit]
[    0.477061] pci 0000:00:16.0: [8086:7ae8] type 00 class 0x078000
[    0.477082] pci 0000:00:16.0: reg 0x10: [mem 0x645cb61000-0x645cb61fff 64bit]
[    0.477160] pci 0000:00:16.0: PME# supported from D3hot
[    0.477760] pci 0000:00:16.3: [8086:7aeb] type 00 class 0x070002
[    0.477772] pci 0000:00:16.3: reg 0x10: [io  0x5060-0x5067]
[    0.477779] pci 0000:00:16.3: reg 0x14: [mem 0x88da1000-0x88da1fff]
[    0.477996] pci 0000:00:19.0: [8086:7afc] type 00 class 0x0c8000
[    0.478022] pci 0000:00:19.0: reg 0x10: [mem 0x00000000-0x00000fff 64bit]
[    0.478309] pci 0000:00:19.2: [8086:7afe] type 00 class 0x078000
[    0.478338] pci 0000:00:19.2: reg 0x10: [mem 0x00000000-0x00000fff 64bit]
[    0.478870] pci 0000:00:1b.0: [8086:7ac4] type 01 class 0x060400
[    0.479011] pci 0000:00:1b.0: PME# supported from D0 D3hot D3cold
[    0.479058] pci 0000:00:1b.0: PTM enabled (root), 4ns granularity
[    0.479911] pci 0000:00:1c.0: [8086:7abd] type 01 class 0x060400
[    0.480013] pci 0000:00:1c.0: PME# supported from D0 D3hot D3cold
[    0.480053] pci 0000:00:1c.0: PTM enabled (root), 4ns granularity
[    0.480837] pci 0000:00:1d.0: [8086:7ab0] type 01 class 0x060400
[    0.480944] pci 0000:00:1d.0: PME# supported from D0 D3hot D3cold
[    0.480985] pci 0000:00:1d.0: PTM enabled (root), 4ns granularity
[    0.481849] pci 0000:00:1e.0: [8086:7aa8] type 00 class 0x078000
[    0.481918] pci 0000:00:1e.0: reg 0x10: [mem 0x00000000-0x00000fff 64bit]
[    0.482460] pci 0000:00:1e.2: [8086:7aaa] type 00 class 0x0c8000
[    0.482529] pci 0000:00:1e.2: reg 0x10: [mem 0x00000000-0x00000fff 64bit]
[    0.482943] pci 0000:00:1f.0: [8086:7a8d] type 00 class 0x060100
[    0.483328] pci 0000:00:1f.3: [8086:7ad0] type 00 class 0x040100
[    0.483369] pci 0000:00:1f.3: reg 0x10: [mem 0x645cb50000-0x645cb53fff 64bit]
[    0.483421] pci 0000:00:1f.3: reg 0x20: [mem 0x645ca00000-0x645cafffff 64bit]
[    0.483522] pci 0000:00:1f.3: PME# supported from D3hot D3cold
[    0.483618] pci 0000:00:1f.4: [8086:7aa3] type 00 class 0x0c0500
[    0.483639] pci 0000:00:1f.4: reg 0x10: [mem 0x645cb5c000-0x645cb5c0ff 64bit]
[    0.483660] pci 0000:00:1f.4: reg 0x20: [io  0xefa0-0xefbf]
[    0.483946] pci 0000:00:1f.5: [8086:7aa4] type 00 class 0x0c8000
[    0.483964] pci 0000:00:1f.5: reg 0x10: [mem 0xfe010000-0xfe010fff]
[    0.484093] pci 0000:00:1f.6: [8086:1a1c] type 00 class 0x020000
[    0.484118] pci 0000:00:1f.6: reg 0x10: [mem 0x88d80000-0x88d9ffff]
[    0.484237] pci 0000:00:1f.6: PME# supported from D0 D3hot D3cold
[    0.484554] pci 0000:01:00.0: [10de:24b9] type 00 class 0x030000
[    0.484562] pci 0000:01:00.0: reg 0x10: [mem 0x87000000-0x87ffffff]
[    0.484569] pci 0000:01:00.0: reg 0x14: [mem 0x6000000000-0x63ffffffff 64bit pref]
[    0.484576] pci 0000:01:00.0: reg 0x1c: [mem 0x6400000000-0x6401ffffff 64bit pref]
[    0.484581] pci 0000:01:00.0: reg 0x24: [io  0x4000-0x407f]
[    0.484585] pci 0000:01:00.0: reg 0x30: [mem 0xfff80000-0xffffffff pref]
[    0.484626] pci 0000:01:00.0: PME# supported from D0 D3hot
[    0.484891] pci 0000:01:00.1: [10de:228b] type 00 class 0x040300
[    0.484898] pci 0000:01:00.1: reg 0x10: [mem 0x88000000-0x88003fff]
[    0.484997] pci 0000:00:01.0: PCI bridge to [bus 01]
[    0.484999] pci 0000:00:01.0:   bridge window [io  0x4000-0x4fff]
[    0.485001] pci 0000:00:01.0:   bridge window [mem 0x87000000-0x880fffff]
[    0.485004] pci 0000:00:01.0:   bridge window [mem 0x6000000000-0x6401ffffff 64bit pref]
[    0.485772] pci 0000:02:00.0: [144d:a80a] type 00 class 0x010802
[    0.485786] pci 0000:02:00.0: reg 0x10: [mem 0x88c00000-0x88c03fff 64bit]
[    0.485988] pci 0000:00:06.0: PCI bridge to [bus 02]
[    0.485990] pci 0000:00:06.0:   bridge window [mem 0x88c00000-0x88cfffff]
[    0.486319] pci 0000:03:00.0: [15b7:5011] type 00 class 0x010802
[    0.486340] pci 0000:03:00.0: reg 0x10: [mem 0x88b00000-0x88b03fff 64bit]
[    0.486636] pci 0000:00:1b.0: PCI bridge to [bus 03]
[    0.486648] pci 0000:00:1b.0:   bridge window [mem 0x88b00000-0x88bfffff]
[    0.486720] pci 0000:04:00.0: [10ec:5261] type 00 class 0xff0000
[    0.486741] pci 0000:04:00.0: reg 0x10: [mem 0x88100000-0x88100fff]
[    0.486895] pci 0000:04:00.0: supports D1 D2
[    0.486896] pci 0000:04:00.0: PME# supported from D1 D2 D3hot D3cold
[    0.487075] pci 0000:00:1c.0: PCI bridge to [bus 04]
[    0.487085] pci 0000:00:1c.0:   bridge window [io  0x3000-0x3fff]
[    0.487088] pci 0000:00:1c.0:   bridge window [mem 0x88100000-0x88afffff]
[    0.487092] pci 0000:00:1c.0:   bridge window [mem 0x645c000000-0x645c9fffff 64bit pref]
[    0.487210] pci 0000:05:00.0: [8086:1136] type 01 class 0x060400
[    0.487272] pci 0000:05:00.0: enabling Extended Tags
[    0.487391] pci 0000:05:00.0: supports D1 D2
[    0.487392] pci 0000:05:00.0: PME# supported from D0 D1 D2 D3hot D3cold
[    0.487509] pci 0000:05:00.0: PTM enabled, 4ns granularity
[    0.487683] pci 0000:00:1d.0: PCI bridge to [bus 05-6e]
[    0.487694] pci 0000:00:1d.0:   bridge window [mem 0x58000000-0x860fffff]
[    0.487698] pci 0000:00:1d.0:   bridge window [mem 0x6410000000-0x645a0fffff 64bit pref]
[    0.487784] pci 0000:06:00.0: [8086:1136] type 01 class 0x060400
[    0.487847] pci 0000:06:00.0: enabling Extended Tags
[    0.487964] pci 0000:06:00.0: supports D1 D2
[    0.487965] pci 0000:06:00.0: PME# supported from D0 D1 D2 D3hot D3cold
[    0.488198] pci 0000:06:01.0: [8086:1136] type 01 class 0x060400
[    0.488261] pci 0000:06:01.0: enabling Extended Tags
[    0.488380] pci 0000:06:01.0: supports D1 D2
[    0.488381] pci 0000:06:01.0: PME# supported from D0 D1 D2 D3hot D3cold
[    0.488604] pci 0000:06:02.0: [8086:1136] type 01 class 0x060400
[    0.488667] pci 0000:06:02.0: enabling Extended Tags
[    0.488778] pci 0000:06:02.0: supports D1 D2
[    0.488779] pci 0000:06:02.0: PME# supported from D0 D1 D2 D3hot D3cold
[    0.489011] pci 0000:06:03.0: [8086:1136] type 01 class 0x060400
[    0.489075] pci 0000:06:03.0: enabling Extended Tags
[    0.489194] pci 0000:06:03.0: supports D1 D2
[    0.489195] pci 0000:06:03.0: PME# supported from D0 D1 D2 D3hot D3cold
[    0.489427] pci 0000:05:00.0: PCI bridge to [bus 06-6e]
[    0.489436] pci 0000:05:00.0:   bridge window [mem 0x58000000-0x85efffff]
[    0.489442] pci 0000:05:00.0:   bridge window [mem 0x6410000000-0x645a0fffff 64bit pref]
[    0.489522] pci 0000:07:00.0: [8086:1137] type 00 class 0x0c0340
[    0.489548] pci 0000:07:00.0: reg 0x10: [mem 0x645a000000-0x645a03ffff 64bit pref]
[    0.489564] pci 0000:07:00.0: reg 0x18: [mem 0x645a040000-0x645a040fff 64bit pref]
[    0.489600] pci 0000:07:00.0: enabling Extended Tags
[    0.489719] pci 0000:07:00.0: supports D1 D2
[    0.489719] pci 0000:07:00.0: PME# supported from D0 D1 D2 D3hot D3cold
[    0.489914] pci 0000:06:00.0: PCI bridge to [bus 07]
[    0.489937] pci 0000:06:00.0:   bridge window [mem 0x645a000000-0x645a0fffff 64bit pref]
[    0.489984] pci 0000:06:01.0: PCI bridge to [bus 08-3a]
[    0.489994] pci 0000:06:01.0:   bridge window [mem 0x6f000000-0x85efffff]
[    0.490000] pci 0000:06:01.0:   bridge window [mem 0x6435000000-0x6459ffffff 64bit pref]
[    0.490087] pci 0000:3b:00.0: [8086:1138] type 00 class 0x0c0330
[    0.490110] pci 0000:3b:00.0: reg 0x10: [mem 0x6ef00000-0x6ef0ffff 64bit]
[    0.490159] pci 0000:3b:00.0: enabling Extended Tags
[    0.490241] pci 0000:3b:00.0: PME# supported from D3hot D3cold
[    0.490348] pci 0000:06:02.0: PCI bridge to [bus 3b]
[    0.490362] pci 0000:06:02.0:   bridge window [mem 0x6ef00000-0x6effffff]
[    0.490414] pci 0000:06:03.0: PCI bridge to [bus 3c-6e]
[    0.490423] pci 0000:06:03.0:   bridge window [mem 0x58000000-0x6eefffff]
[    0.490430] pci 0000:06:03.0:   bridge window [mem 0x6410000000-0x6434ffffff 64bit pref]
[    0.495478] ACPI: EC: interrupt unblocked
[    0.495480] ACPI: EC: event unblocked
[    0.495495] ACPI: EC: EC_CMD/EC_SC=0x66, EC_DATA=0x62
[    0.495496] ACPI: EC: GPE=0x6e
[    0.495497] ACPI: \_SB_.PC00.LPCB.EC0_: Boot DSDT EC initialization complete
[    0.495498] ACPI: \_SB_.PC00.LPCB.EC0_: EC: Used to handle transactions and events
[    0.495563] iommu: Default domain type: Translated
[    0.495563] iommu: DMA domain TLB invalidation policy: lazy mode
[    0.495563] SCSI subsystem initialized
[    0.495563] libata version 3.00 loaded.
[    0.495563] ACPI: bus type USB registered
[    0.495563] usbcore: registered new interface driver usbfs
[    0.495563] usbcore: registered new interface driver hub
[    0.495563] usbcore: registered new device driver usb
[    0.495563] pps_core: LinuxPPS API ver. 1 registered
[    0.495563] pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
[    0.495563] PTP clock support registered
[    0.495563] EDAC MC: Ver: 3.0.0
[    0.496972] efivars: Registered efivars operations
[    0.496972] NetLabel: Initializing
[    0.496972] NetLabel:  domain hash size = 128
[    0.496972] NetLabel:  protocols = UNLABELED CIPSOv4 CALIPSO
[    0.496972] NetLabel:  unlabeled traffic allowed by default
[    0.496972] mctp: management component transport protocol core
[    0.496972] NET: Registered PF_MCTP protocol family
[    0.496972] PCI: Using ACPI for IRQ routing
[    0.514627] PCI: pci_cache_line_size set to 64 bytes
[    0.515775] pci 0000:00:1f.5: can't claim BAR 0 [mem 0xfe010000-0xfe010fff]: no compatible bridge window
[    0.516708] e820: reserve RAM buffer [mem 0x0009f000-0x0009ffff]
[    0.516709] e820: reserve RAM buffer [mem 0x2e866000-0x2fffffff]
[    0.516710] e820: reserve RAM buffer [mem 0x3b8dd000-0x3bffffff]
[    0.516711] e820: reserve RAM buffer [mem 0x41c00000-0x43ffffff]
[    0.516712] e820: reserve RAM buffer [mem 0x8ab800000-0x8abffffff]
[    0.516737] pci 0000:00:02.0: vgaarb: setting as boot VGA device
[    0.516737] pci 0000:00:02.0: vgaarb: bridge control possible
[    0.516737] pci 0000:00:02.0: vgaarb: VGA device added: decodes=io+mem,owns=io+mem,locks=none
[    0.516738] pci 0000:01:00.0: vgaarb: bridge control possible
[    0.516739] pci 0000:01:00.0: vgaarb: VGA device added: decodes=io+mem,owns=none,locks=none
[    0.516741] vgaarb: loaded
[    0.516834] clocksource: Switched to clocksource tsc-early
[    0.517108] VFS: Disk quotas dquot_6.6.0
[    0.517120] VFS: Dquot-cache hash table entries: 512 (order 0, 4096 bytes)
[    0.517207] AppArmor: AppArmor Filesystem Enabled
[    0.517235] pnp: PnP ACPI init
[    0.517755] system 00:00: [io  0x0680-0x069f] has been reserved
[    0.517758] system 00:00: [io  0x164e-0x164f] has been reserved
[    0.517907] system 00:01: [io  0x1854-0x1857] has been reserved
[    0.518008] system 00:03: [io  0x0200-0x027f] has been reserved
[    0.518010] system 00:03: [mem 0xfedf0000-0xfedfffff] has been reserved
[    0.518407] pnp 00:04: disabling [mem 0xc0000000-0xcfffffff] because it overlaps 0000:00:02.0 BAR 9 [mem 0x00000000-0xdfffffff 64bit pref]
[    0.518435] system 00:04: [mem 0xfedc0000-0xfedc7fff] has been reserved
[    0.518436] system 00:04: [mem 0xfeda0000-0xfeda0fff] has been reserved
[    0.518437] system 00:04: [mem 0xfeda1000-0xfeda1fff] has been reserved
[    0.518439] system 00:04: [mem 0xfed20000-0xfed7ffff] could not be reserved
[    0.518440] system 00:04: [mem 0xfed90000-0xfed93fff] could not be reserved
[    0.518441] system 00:04: [mem 0xfed45000-0xfed8ffff] could not be reserved
[    0.518442] system 00:04: [mem 0xfee00000-0xfeefffff] has been reserved
[    0.518787] system 00:05: [io  0x2000-0x20fe] has been reserved
[    0.519618] pnp: PnP ACPI: found 7 devices
[    0.525167] clocksource: acpi_pm: mask: 0xffffff max_cycles: 0xffffff, max_idle_ns: 2085701024 ns
[    0.525220] NET: Registered PF_INET protocol family
[    0.525414] IP idents hash table entries: 262144 (order: 9, 2097152 bytes, linear)
[    0.528130] tcp_listen_portaddr_hash hash table entries: 16384 (order: 6, 262144 bytes, linear)
[    0.528171] Table-perturb hash table entries: 65536 (order: 6, 262144 bytes, linear)
[    0.528349] TCP established hash table entries: 262144 (order: 9, 2097152 bytes, linear)
[    0.528706] TCP bind hash table entries: 65536 (order: 9, 2097152 bytes, linear)
[    0.528805] TCP: Hash tables configured (established 262144 bind 65536)
[    0.528963] MPTCP token hash table entries: 32768 (order: 7, 786432 bytes, linear)
[    0.529044] UDP hash table entries: 16384 (order: 7, 524288 bytes, linear)
[    0.529119] UDP-Lite hash table entries: 16384 (order: 7, 524288 bytes, linear)
[    0.529197] NET: Registered PF_UNIX/PF_LOCAL protocol family
[    0.529203] NET: Registered PF_XDP protocol family
[    0.529206] pci 0000:01:00.0: can't claim BAR 6 [mem 0xfff80000-0xffffffff pref]: no compatible bridge window
[    0.529212] pci_bus 0000:00: max bus depth: 3 pci_try_num: 4
[    0.529226] pci 0000:00:02.0: BAR 9: assigned [mem 0x4020000000-0x40ffffffff 64bit pref]
[    0.529229] pci 0000:00:02.0: BAR 7: assigned [mem 0x4010000000-0x4016ffffff 64bit]
[    0.529231] pci 0000:00:15.0: BAR 0: assigned [mem 0x4017000000-0x4017000fff 64bit]
[    0.529287] pci 0000:00:15.1: BAR 0: assigned [mem 0x4017001000-0x4017001fff 64bit]
[    0.529301] pci 0000:00:15.2: BAR 0: assigned [mem 0x4017002000-0x4017002fff 64bit]
[    0.529314] pci 0000:00:19.0: BAR 0: assigned [mem 0x4017003000-0x4017003fff 64bit]
[    0.529328] pci 0000:00:19.2: BAR 0: assigned [mem 0x4017004000-0x4017004fff 64bit]
[    0.529383] pci 0000:00:1d.0: BAR 13: assigned [io  0x6000-0x8fff]
[    0.529385] pci 0000:00:1e.0: BAR 0: assigned [mem 0x4017005000-0x4017005fff 64bit]
[    0.529439] pci 0000:00:1e.2: BAR 0: assigned [mem 0x4017006000-0x4017006fff 64bit]
[    0.529453] pci 0000:00:1f.5: BAR 0: assigned [mem 0x54800000-0x54800fff]
[    0.529464] pci 0000:01:00.0: BAR 6: assigned [mem 0x88080000-0x880fffff pref]
[    0.529466] pci 0000:00:01.0: PCI bridge to [bus 01]
[    0.529467] pci 0000:00:01.0:   bridge window [io  0x4000-0x4fff]
[    0.529470] pci 0000:00:01.0:   bridge window [mem 0x87000000-0x880fffff]
[    0.529471] pci 0000:00:01.0:   bridge window [mem 0x6000000000-0x6401ffffff 64bit pref]
[    0.529475] pci 0000:00:06.0: PCI bridge to [bus 02]
[    0.529480] pci 0000:00:06.0:   bridge window [mem 0x88c00000-0x88cfffff]
[    0.529485] pci 0000:00:1b.0: PCI bridge to [bus 03]
[    0.529554] pci 0000:00:1b.0:   bridge window [mem 0x88b00000-0x88bfffff]
[    0.529561] pci 0000:00:1c.0: PCI bridge to [bus 04]
[    0.529563] pci 0000:00:1c.0:   bridge window [io  0x3000-0x3fff]
[    0.529566] pci 0000:00:1c.0:   bridge window [mem 0x88100000-0x88afffff]
[    0.529569] pci 0000:00:1c.0:   bridge window [mem 0x645c000000-0x645c9fffff 64bit pref]
[    0.529574] pci 0000:05:00.0: BAR 13: assigned [io  0x6000-0x7fff]
[    0.529576] pci 0000:06:01.0: BAR 13: assigned [io  0x6000-0x6fff]
[    0.529577] pci 0000:06:03.0: BAR 13: assigned [io  0x7000-0x7fff]
[    0.529578] pci 0000:06:00.0: PCI bridge to [bus 07]
[    0.529586] pci 0000:06:00.0:   bridge window [mem 0x645a000000-0x645a0fffff 64bit pref]
[    0.529593] pci 0000:06:01.0: PCI bridge to [bus 08-3a]
[    0.529595] pci 0000:06:01.0:   bridge window [io  0x6000-0x6fff]
[    0.529600] pci 0000:06:01.0:   bridge window [mem 0x6f000000-0x85efffff]
[    0.529604] pci 0000:06:01.0:   bridge window [mem 0x6435000000-0x6459ffffff 64bit pref]
[    0.529610] pci 0000:06:02.0: PCI bridge to [bus 3b]
[    0.529615] pci 0000:06:02.0:   bridge window [mem 0x6ef00000-0x6effffff]
[    0.529625] pci 0000:06:03.0: PCI bridge to [bus 3c-6e]
[    0.529627] pci 0000:06:03.0:   bridge window [io  0x7000-0x7fff]
[    0.529632] pci 0000:06:03.0:   bridge window [mem 0x58000000-0x6eefffff]
[    0.529636] pci 0000:06:03.0:   bridge window [mem 0x6410000000-0x6434ffffff 64bit pref]
[    0.529642] pci 0000:05:00.0: PCI bridge to [bus 06-6e]
[    0.529645] pci 0000:05:00.0:   bridge window [io  0x6000-0x7fff]
[    0.529650] pci 0000:05:00.0:   bridge window [mem 0x58000000-0x85efffff]
[    0.529653] pci 0000:05:00.0:   bridge window [mem 0x6410000000-0x645a0fffff 64bit pref]
[    0.529660] pci 0000:00:1d.0: PCI bridge to [bus 05-6e]
[    0.529661] pci 0000:00:1d.0:   bridge window [io  0x6000-0x8fff]
[    0.529665] pci 0000:00:1d.0:   bridge window [mem 0x58000000-0x860fffff]
[    0.529668] pci 0000:00:1d.0:   bridge window [mem 0x6410000000-0x645a0fffff 64bit pref]
[    0.529673] pci_bus 0000:00: resource 4 [io  0x0000-0x0cf7 window]
[    0.529674] pci_bus 0000:00: resource 5 [io  0x0d00-0xffff window]
[    0.529675] pci_bus 0000:00: resource 6 [mem 0x000a0000-0x000bffff window]
[    0.529677] pci_bus 0000:00: resource 7 [mem 0x54800000-0xbfffffff window]
[    0.529678] pci_bus 0000:00: resource 8 [mem 0x4000000000-0x7fffffffff window]
[    0.529679] pci_bus 0000:01: resource 0 [io  0x4000-0x4fff]
[    0.529680] pci_bus 0000:01: resource 1 [mem 0x87000000-0x880fffff]
[    0.529681] pci_bus 0000:01: resource 2 [mem 0x6000000000-0x6401ffffff 64bit pref]
[    0.529682] pci_bus 0000:02: resource 1 [mem 0x88c00000-0x88cfffff]
[    0.529683] pci_bus 0000:03: resource 1 [mem 0x88b00000-0x88bfffff]
[    0.529684] pci_bus 0000:04: resource 0 [io  0x3000-0x3fff]
[    0.529685] pci_bus 0000:04: resource 1 [mem 0x88100000-0x88afffff]
[    0.529686] pci_bus 0000:04: resource 2 [mem 0x645c000000-0x645c9fffff 64bit pref]
[    0.529687] pci_bus 0000:05: resource 0 [io  0x6000-0x8fff]
[    0.529688] pci_bus 0000:05: resource 1 [mem 0x58000000-0x860fffff]
[    0.529688] pci_bus 0000:05: resource 2 [mem 0x6410000000-0x645a0fffff 64bit pref]
[    0.529690] pci_bus 0000:06: resource 0 [io  0x6000-0x7fff]
[    0.529690] pci_bus 0000:06: resource 1 [mem 0x58000000-0x85efffff]
[    0.529691] pci_bus 0000:06: resource 2 [mem 0x6410000000-0x645a0fffff 64bit pref]
[    0.529692] pci_bus 0000:07: resource 2 [mem 0x645a000000-0x645a0fffff 64bit pref]
[    0.529693] pci_bus 0000:08: resource 0 [io  0x6000-0x6fff]
[    0.529694] pci_bus 0000:08: resource 1 [mem 0x6f000000-0x85efffff]
[    0.529695] pci_bus 0000:08: resource 2 [mem 0x6435000000-0x6459ffffff 64bit pref]
[    0.529696] pci_bus 0000:3b: resource 1 [mem 0x6ef00000-0x6effffff]
[    0.529697] pci_bus 0000:3c: resource 0 [io  0x7000-0x7fff]
[    0.529698] pci_bus 0000:3c: resource 1 [mem 0x58000000-0x6eefffff]
[    0.529699] pci_bus 0000:3c: resource 2 [mem 0x6410000000-0x6434ffffff 64bit pref]
[    0.529835] pci 0000:00:14.0: enabling device (0000 -> 0002)
[    0.530376] pci 0000:01:00.1: extending delay after power-on from D3hot to 20 msec
[    0.530410] pci 0000:01:00.1: D0 power state depends on 0000:01:00.0
[    0.531667] pci 0000:3b:00.0: enabling device (0000 -> 0002)
[    0.531703] PCI: CLS 0 bytes, default 64
[    0.531711] DMAR: Intel-IOMMU force enabled due to platform opt in
[    0.531721] DMAR: No ATSR found
[    0.531722] DMAR: No SATC found
[    0.531723] DMAR: IOMMU feature fl1gp_support inconsistent
[    0.531724] DMAR: IOMMU feature pgsel_inv inconsistent
[    0.531724] DMAR: IOMMU feature nwfs inconsistent
[    0.531725] DMAR: IOMMU feature dit inconsistent
[    0.531725] DMAR: IOMMU feature sc_support inconsistent
[    0.531726] DMAR: IOMMU feature dev_iotlb_support inconsistent
[    0.531726] DMAR: dmar0: Using Queued invalidation
[    0.531729] DMAR: dmar1: Using Queued invalidation
[    0.531789] Trying to unpack rootfs image as initramfs...
[    0.532526] pci 0000:00:02.0: Adding to iommu group 0
[    0.532565] pci 0000:00:00.0: Adding to iommu group 1
[    0.532576] pci 0000:00:01.0: Adding to iommu group 2
[    0.532585] pci 0000:00:04.0: Adding to iommu group 3
[    0.532600] pci 0000:00:06.0: Adding to iommu group 4
[    0.532608] pci 0000:00:08.0: Adding to iommu group 5
[    0.532617] pci 0000:00:0a.0: Adding to iommu group 6
[    0.532630] pci 0000:00:12.0: Adding to iommu group 7
[    0.532650] pci 0000:00:14.0: Adding to iommu group 8
[    0.532659] pci 0000:00:14.2: Adding to iommu group 8
[    0.532668] pci 0000:00:14.3: Adding to iommu group 9
[    0.532690] pci 0000:00:15.0: Adding to iommu group 10
[    0.532700] pci 0000:00:15.1: Adding to iommu group 10
[    0.532708] pci 0000:00:15.2: Adding to iommu group 10
[    0.532727] pci 0000:00:16.0: Adding to iommu group 11
[    0.532736] pci 0000:00:16.3: Adding to iommu group 11
[    0.532754] pci 0000:00:19.0: Adding to iommu group 12
[    0.532763] pci 0000:00:19.2: Adding to iommu group 12
[    0.532780] pci 0000:00:1b.0: Adding to iommu group 13
[    0.532802] pci 0000:00:1c.0: Adding to iommu group 14
[    0.532812] pci 0000:00:1d.0: Adding to iommu group 15
[    0.532830] pci 0000:00:1e.0: Adding to iommu group 16
[    0.532840] pci 0000:00:1e.2: Adding to iommu group 16
[    0.532872] pci 0000:00:1f.0: Adding to iommu group 17
[    0.532882] pci 0000:00:1f.3: Adding to iommu group 17
[    0.532892] pci 0000:00:1f.4: Adding to iommu group 17
[    0.532902] pci 0000:00:1f.5: Adding to iommu group 17
[    0.532912] pci 0000:00:1f.6: Adding to iommu group 17
[    0.532930] pci 0000:01:00.0: Adding to iommu group 18
[    0.532941] pci 0000:01:00.1: Adding to iommu group 18
[    0.532955] pci 0000:02:00.0: Adding to iommu group 19
[    0.532972] pci 0000:03:00.0: Adding to iommu group 20
[    0.532982] pci 0000:04:00.0: Adding to iommu group 21
[    0.532992] pci 0000:05:00.0: Adding to iommu group 22
[    0.533011] pci 0000:06:00.0: Adding to iommu group 23
[    0.533022] pci 0000:06:01.0: Adding to iommu group 24
[    0.533040] pci 0000:06:02.0: Adding to iommu group 25
[    0.533050] pci 0000:06:03.0: Adding to iommu group 26
[    0.533072] pci 0000:07:00.0: Adding to iommu group 27
[    0.533086] pci 0000:3b:00.0: Adding to iommu group 28
[    0.534362] DMAR: Intel(R) Virtualization Technology for Directed I/O
[    0.534364] PCI-DMA: Using software bounce buffering for IO (SWIOTLB)
[    0.534365] software IO TLB: mapped [mem 0x000000001f8de000-0x00000000238de000] (64MB)
[    0.538769] clocksource: tsc: mask: 0xffffffffffffffff max_cycles: 0x22df1149949, max_idle_ns: 440795312789 ns
[    0.538873] clocksource: Switched to clocksource tsc
[    0.538893] platform rtc_cmos: registered platform RTC device (no PNP device found)
[    0.543156] Initialise system trusted keyrings
[    0.543163] Key type blacklist registered
[    0.543245] workingset: timestamp_bits=36 max_order=23 bucket_order=0
[    0.543250] zbud: loaded
[    0.543426] squashfs: version 4.0 (2009/01/31) Phillip Lougher
[    0.543483] fuse: init (API version 7.39)
[    0.543596] integrity: Platform Keyring initialized
[    0.543599] integrity: Machine keyring initialized
[    0.552373] Key type asymmetric registered
[    0.552375] Asymmetric key parser 'x509' registered
[    0.552387] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 243)
[    0.552446] io scheduler mq-deadline registered
[    0.554675] shpchp: Standard Hot Plug PCI Controller Driver version: 0.4
[    0.554724] efifb: probing for efifb
[    0.554739] efifb: showing boot graphics
[    0.556197] efifb: framebuffer at 0x4000000000, using 9000k, total 9000k
[    0.556199] efifb: mode is 1920x1200x32, linelength=7680, pages=1
[    0.556200] efifb: scrolling: redraw
[    0.556200] efifb: Truecolor: size=8:8:8:8, shift=24:16:8:0
[    0.556232] fbcon: Deferring console take-over
[    0.556233] fb0: EFI VGA frame buffer device
[    0.557443] ACPI: AC: AC Adapter [AC] (on-line)
[    0.557487] input: Sleep Button as /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0E:00/input/input0
[    0.557501] ACPI: button: Sleep Button [SLPB]
[    0.557521] input: Lid Switch as /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0C0D:00/input/input1
[    0.557533] ACPI: button: Lid Switch [LID]
[    0.557555] input: Power Button as /devices/LNXSYSTM:00/LNXPWRBN:00/input/input2
[    0.557592] ACPI: button: Power Button [PWRF]
[    0.569515] thermal LNXTHERM:00: registered as thermal_zone0
[    0.569519] ACPI: thermal: Thermal Zone [CPUZ] (46 C)
[    0.576461] thermal LNXTHERM:01: registered as thermal_zone1
[    0.576464] ACPI: thermal: Thermal Zone [GFXZ] (25 C)
[    0.580600] thermal LNXTHERM:02: registered as thermal_zone2
[    0.580604] ACPI: thermal: Thermal Zone [EXTZ] (42 C)
[    0.584815] thermal LNXTHERM:03: registered as thermal_zone3
[    0.584817] ACPI: thermal: Thermal Zone [LOCZ] (37 C)
[    0.591929] thermal LNXTHERM:04: registered as thermal_zone4
[    0.591932] ACPI: thermal: Thermal Zone [BATZ] (24 C)
[    0.596316] thermal LNXTHERM:05: registered as thermal_zone5
[    0.596319] ACPI: thermal: Thermal Zone [CHGZ] (30 C)
[    0.600681] thermal LNXTHERM:06: registered as thermal_zone6
[    0.600684] ACPI: thermal: Thermal Zone [SK1Z] (30 C)
[    0.604841] thermal LNXTHERM:07: registered as thermal_zone7
[    0.604843] ACPI: thermal: Thermal Zone [SK2Z] (28 C)
[    0.609099] thermal LNXTHERM:08: registered as thermal_zone8
[    0.609101] ACPI: thermal: Thermal Zone [PCHZ] (0 C)
[    0.609354] Serial: 8250/16550 driver, 32 ports, IRQ sharing enabled
[    0.611263] serial 0000:00:12.0: enabling device (0000 -> 0002)
[    0.611537] serial 0000:00:16.3: enabling device (0000 -> 0003)
[    0.632362] 0000:00:16.3: ttyS4 at I/O 0x5060 (irq = 19, base_baud = 115200) is a 16550A
[    0.632706] hpet_acpi_add: no address or irqs in _CRS
[    0.632727] Linux agpgart interface v0.103
[    0.634469] tpm_tis IFX1521:00: 2.0 TPM (device-id 0x1D, rev-id 54)
[    0.641077] ACPI: battery: Slot [BAT0] (battery present)
[    0.784667] loop: module loaded
[    0.784945] tun: Universal TUN/TAP device driver, 1.6
[    0.785001] PPP generic driver version 2.4.2
[    0.785102] i8042: PNP: PS/2 Controller [PNP0303:PS2K] at 0x60,0x64 irq 1
[    0.785104] i8042: PNP: PS/2 appears to have AUX port disabled, if this is incorrect please boot with i8042.nopnp
[    0.785661] serio: i8042 KBD port at 0x60,0x64 irq 1
[    0.785740] mousedev: PS/2 mouse device common for all mice
[    0.785830] rtc_cmos rtc_cmos: RTC can wake from S4
[    0.786725] rtc_cmos rtc_cmos: registered as rtc0
[    0.786910] rtc_cmos rtc_cmos: setting system clock to 2023-11-21T17:31:32 UTC (1700587892)
[    0.786939] rtc_cmos rtc_cmos: alarms up to one month, y3k, 114 bytes nvram
[    0.786948] i2c_dev: i2c /dev entries driver
[    0.788847] device-mapper: core: CONFIG_IMA_DISABLE_HTABLE is disabled. Duplicate IMA measurements will not be recorded in the IMA log.
[    0.788858] device-mapper: uevent: version 1.0.3
[    0.788894] device-mapper: ioctl: 4.48.0-ioctl (2023-03-01) initialised: dm-devel@redhat.com
[    0.788912] platform eisa.0: Probing EISA bus 0
[    0.788914] platform eisa.0: EISA: Cannot allocate resource for mainboard
[    0.788915] platform eisa.0: Cannot allocate resource for EISA slot 1
[    0.788916] platform eisa.0: Cannot allocate resource for EISA slot 2
[    0.788917] platform eisa.0: Cannot allocate resource for EISA slot 3
[    0.788917] platform eisa.0: Cannot allocate resource for EISA slot 4
[    0.788918] platform eisa.0: Cannot allocate resource for EISA slot 5
[    0.788919] platform eisa.0: Cannot allocate resource for EISA slot 6
[    0.788920] platform eisa.0: Cannot allocate resource for EISA slot 7
[    0.788920] platform eisa.0: Cannot allocate resource for EISA slot 8
[    0.788921] platform eisa.0: EISA: Detected 0 cards
[    0.788924] intel_pstate: Intel P-state driver initializing
[    0.791276] intel_pstate: HWP enabled
[    0.796606] Freeing initrd memory: 69584K
[    0.796945] ledtrig-cpu: registered to indicate activity on CPUs
[    0.797159] drop_monitor: Initializing network drop monitor service
[    0.797251] NET: Registered PF_INET6 protocol family
[    0.802001] Segment Routing with IPv6
[    0.802007] In-situ OAM (IOAM) with IPv6
[    0.802027] NET: Registered PF_PACKET protocol family
[    0.802097] Key type dns_resolver registered
[    0.805587] microcode: Microcode Update Driver: v2.2.
[    0.805605] IPI shorthand broadcast: enabled
[    0.806434] sched_clock: Marking stable (800000795, 6343187)->(902752630, -96408648)
[    0.806976] registered taskstats version 1
[    0.810155] Loading compiled-in X.509 certificates
[    0.810596] Loaded X.509 cert 'Build time autogenerated kernel key: 6d7be3ee82ad92e828a425da7de4a03b7ee58532'
[    0.811576] Key type .fscrypt registered
[    0.811577] Key type fscrypt-provisioning registered
[    0.811603] Key type trusted registered
[    0.812561] input: AT Translated Set 2 keyboard as /devices/platform/i8042/serio0/input/input3
[    0.825911] Key type encrypted registered
[    0.825917] AppArmor: AppArmor sha1 policy hashing enabled
[    0.825962] integrity: Loading X.509 certificate: UEFI:db
[    0.825999] integrity: Loaded X.509 cert 'HP Inc.: HP UEFI Secure Boot DB 2017: d9c01b50cfcae89d3b05345c163aa76e5dd589e7'
[    0.826000] integrity: Loading X.509 certificate: UEFI:db
[    0.826028] integrity: Loaded X.509 cert 'Microsoft Windows Production PCA 2011: a92902398e16c49778cd90f99e4f9ae17c55af53'
[    0.826029] integrity: Loading X.509 certificate: UEFI:db
[    0.826053] integrity: Loaded X.509 cert 'Microsoft Corporation UEFI CA 2011: 13adbf4309bd82709c8cd54f316ed522988a1bd4'
[    0.827329] Loading compiled-in module X.509 certificates
[    0.827972] Loaded X.509 cert 'Build time autogenerated kernel key: 6d7be3ee82ad92e828a425da7de4a03b7ee58532'
[    0.827974] ima: Allocated hash algorithm: sha1
[    0.850824] ima: No architecture policies found
[    0.850860] evm: Initialising EVM extended attributes:
[    0.850861] evm: security.selinux
[    0.850864] evm: security.SMACK64
[    0.850866] evm: security.SMACK64EXEC
[    0.850867] evm: security.SMACK64TRANSMUTE
[    0.850868] evm: security.SMACK64MMAP
[    0.850869] evm: security.apparmor
[    0.850871] evm: security.ima
[    0.850872] evm: security.capability
[    0.850873] evm: HMAC attrs: 0x1
[    0.851684] PM:   Magic number: 15:889:542
[    0.851764] platform INTC1046:01: hash matches
[    0.851820] acpi INTC1046:01: hash matches
[    0.852329] RAS: Correctable Errors collector initialized.
[    0.852420] clk: Disabling unused clocks
[    0.861050] Freeing unused decrypted memory: 2028K
[    0.861900] Freeing unused kernel image (initmem) memory: 4712K
[    0.891004] Write protecting the kernel read-only data: 28672k
[    0.892167] Freeing unused kernel image (rodata/data gap) memory: 816K
[    0.909588] x86/mm: Checked W+X mappings: passed, no W+X pages found.
[    0.909593] Run /init as init process
[    0.909594]   with arguments:
[    0.909595]     /init
[    0.909596]     splash
[    0.909597]   with environment:
[    0.909597]     HOME=/
[    0.909598]     TERM=linux
[    0.909599]     BOOT_IMAGE=/boot/vmlinuz-6.7.0-rc1-next-20231115+
[    0.995542] wmi_bus wmi_bus-PNP0C14:02: WQZZ data block query control method not found
[    0.997744] hid: raw HID events driver (C) Jiri Kosina
[    1.002671] xhci_hcd 0000:00:14.0: xHCI Host Controller
[    1.002683] xhci_hcd 0000:00:14.0: new USB bus registered, assigned bus number 1
[    1.003783] xhci_hcd 0000:00:14.0: hcc params 0x20007fc1 hci version 0x120 quirks 0x0000000200009810
[    1.004145] xhci_hcd 0000:00:14.0: xHCI Host Controller
[    1.004149] xhci_hcd 0000:00:14.0: new USB bus registered, assigned bus number 2
[    1.004152] xhci_hcd 0000:00:14.0: Host supports USB 3.2 Enhanced SuperSpeed
[    1.004271] usb usb1: New USB device found, idVendor=1d6b, idProduct=0002, bcdDevice= 6.07
[    1.004274] usb usb1: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    1.004275] usb usb1: Product: xHCI Host Controller
[    1.004276] usb usb1: Manufacturer: Linux 6.7.0-rc1-next-20231115+ xhci-hcd
[    1.004277] usb usb1: SerialNumber: 0000:00:14.0
[    1.004516] hub 1-0:1.0: USB hub found
[    1.004550] hub 1-0:1.0: 16 ports detected
[    1.004948] rtsx_pci 0000:04:00.0: enabling device (0000 -> 0002)
[    1.007016] usb usb2: New USB device found, idVendor=1d6b, idProduct=0003, bcdDevice= 6.07
[    1.007018] usb usb2: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    1.007019] usb usb2: Product: xHCI Host Controller
[    1.007020] usb usb2: Manufacturer: Linux 6.7.0-rc1-next-20231115+ xhci-hcd
[    1.007021] usb usb2: SerialNumber: 0000:00:14.0
[    1.007117] hub 2-0:1.0: USB hub found
[    1.007137] hub 2-0:1.0: 10 ports detected
[    1.007580] usb: port power management may be unreliable
[    1.008596] xhci_hcd 0000:3b:00.0: xHCI Host Controller
[    1.008599] xhci_hcd 0000:3b:00.0: new USB bus registered, assigned bus number 3
[    1.009768] xhci_hcd 0000:3b:00.0: hcc params 0x20007fc1 hci version 0x110 quirks 0x0000000200009810
[    1.010050] xhci_hcd 0000:3b:00.0: xHCI Host Controller
[    1.010052] xhci_hcd 0000:3b:00.0: new USB bus registered, assigned bus number 4
[    1.010053] xhci_hcd 0000:3b:00.0: Host supports USB 3.1 Enhanced SuperSpeed
[    1.010074] usb usb3: New USB device found, idVendor=1d6b, idProduct=0002, bcdDevice= 6.07
[    1.010076] usb usb3: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    1.010077] usb usb3: Product: xHCI Host Controller
[    1.010078] usb usb3: Manufacturer: Linux 6.7.0-rc1-next-20231115+ xhci-hcd
[    1.010078] usb usb3: SerialNumber: 0000:3b:00.0
[    1.010181] hub 3-0:1.0: USB hub found
[    1.010202] hub 3-0:1.0: 2 ports detected
[    1.010661] usb usb4: New USB device found, idVendor=1d6b, idProduct=0003, bcdDevice= 6.07
[    1.010664] usb usb4: New USB device strings: Mfr=3, Product=2, SerialNumber=1
[    1.010665] usb usb4: Product: xHCI Host Controller
[    1.010666] usb usb4: Manufacturer: Linux 6.7.0-rc1-next-20231115+ xhci-hcd
[    1.010666] usb usb4: SerialNumber: 0000:3b:00.0
[    1.010788] hub 4-0:1.0: USB hub found
[    1.010811] hub 4-0:1.0: 2 ports detected
[    1.012947] ACPI: bus type drm_connector registered
[    1.016886] ACPI: bus type thunderbolt registered
[    1.016967] thunderbolt 0000:07:00.0: enabling device (0000 -> 0002)
[    1.019087] e1000e: Intel(R) PRO/1000 Network Driver
[    1.019089] e1000e: Copyright(c) 1999 - 2015 Intel Corporation.
[    1.019124] e1000e 0000:00:1f.6: enabling device (0000 -> 0002)
[    1.019295] i801_smbus 0000:00:1f.4: enabling device (0000 -> 0003)
[    1.019327] __spi_add_device() [664] CS[0] = [0]
[    1.019329] __spi_add_device() [667] CS_NEW[1] = [0]
[    1.019330] intel-spi 0000:00:1f.5: chipselect 0 already in use
[    1.019334] fbcon: Taking over console
[    1.019339] nvme 0000:02:00.0: platform quirk: setting simple suspend
[    1.019339] e1000e 0000:00:1f.6: Interrupt Throttling Rate (ints/sec) set to dynamic conservative mode
[    1.019383] nvme 0000:03:00.0: platform quirk: setting simple suspend
[    1.019441] i801_smbus 0000:00:1f.4: SPD Write Disable is set
[    1.019474] Console: switching to colour frame buffer device 240x75
[    1.019484] i801_smbus 0000:00:1f.4: SMBus using PCI interrupt
[    1.019568] nvme nvme0: pci function 0000:02:00.0
[    1.019621] nvme nvme1: pci function 0000:03:00.0
[    1.020682] i2c i2c-0: 2/4 memory slots populated (from DMI)
[    1.020685] i2c i2c-0: Memory type 0x22 not supported yet, not instantiating SPD
[    1.029171] intel-lpss 0000:00:15.0: enabling device (0000 -> 0002)
[    1.029462] idma64 idma64.0: Found Intel integrated DMA 64-bit
[    1.032649] nvme nvme0: Shutdown timeout set to 10 seconds
[    1.037214] nvme nvme0: 24/0/0 default/read/poll queues
[    1.040011]  nvme0n1: p1 p2 p3
[    1.045310] intel-lpss 0000:00:15.1: enabling device (0000 -> 0002)
[    1.045552] idma64 idma64.1: Found Intel integrated DMA 64-bit
[    1.054023] nvme nvme1: 24/0/0 default/read/poll queues
[    1.058687]  nvme1n1: p1 p2 p3
[    1.061973] intel-lpss 0000:00:15.2: enabling device (0000 -> 0002)
[    1.062517] idma64 idma64.2: Found Intel integrated DMA 64-bit
[    1.082429] intel-lpss 0000:00:19.0: enabling device (0000 -> 0002)
[    1.083424] idma64 idma64.3: Found Intel integrated DMA 64-bit
[    1.096937] e1000e 0000:00:1f.6 0000:00:1f.6 (uninitialized): registered PHC clock
[    1.105236] intel-lpss 0000:00:19.2: enabling device (0000 -> 0002)
[    1.105809] idma64 idma64.4: Found Intel integrated DMA 64-bit
[    1.118446] intel-lpss 0000:00:1e.0: enabling device (0000 -> 0002)
[    1.119257] idma64 idma64.5: Found Intel integrated DMA 64-bit
[    1.131374] intel-lpss 0000:00:1e.2: enabling device (0000 -> 0002)
[    1.132481] idma64 idma64.6: Found Intel integrated DMA 64-bit
[    1.169673] e1000e 0000:00:1f.6 eth0: (PCI Express:2.5GT/s:Width x1) 38:ca:84:d5:b2:4f
[    1.169678] e1000e 0000:00:1f.6 eth0: Intel(R) PRO/1000 Network Connection
[    1.169790] e1000e 0000:00:1f.6 eth0: MAC: 15, PHY: 12, PBA No: FFFFFF-0FF
[    1.266471] usb 1-8: new high-speed USB device number 2 using xhci_hcd
[    1.324380] i2c_hid_acpi i2c-ELAN2513:00: i2c_hid_get_input: IRQ triggered but there's no data
[    1.325374] input: SYNA30F7:00 06CB:CEC3 Mouse as /devices/pci0000:00/0000:00:15.0/i2c_designware.0/i2c-1/i2c-SYNA30F7:00/0018:06CB:CEC3.0001/input/input4
[    1.325502] input: SYNA30F7:00 06CB:CEC3 Touchpad as /devices/pci0000:00/0000:00:15.0/i2c_designware.0/i2c-1/i2c-SYNA30F7:00/0018:06CB:CEC3.0001/input/input5
[    1.325560] hid-generic 0018:06CB:CEC3.0001: input,hidraw0: I2C HID v1.00 Mouse [SYNA30F7:00 06CB:CEC3] on i2c-SYNA30F7:00
[    1.325733] input: ELAN2513:00 04F3:2FE0 Touchscreen as /devices/pci0000:00/0000:00:15.1/i2c_designware.1/i2c-2/i2c-ELAN2513:00/0018:04F3:2FE0.0002/input/input7
[    1.325762] input: ELAN2513:00 04F3:2FE0 as /devices/pci0000:00/0000:00:15.1/i2c_designware.1/i2c-2/i2c-ELAN2513:00/0018:04F3:2FE0.0002/input/input8
[    1.325786] input: ELAN2513:00 04F3:2FE0 as /devices/pci0000:00/0000:00:15.1/i2c_designware.1/i2c-2/i2c-ELAN2513:00/0018:04F3:2FE0.0002/input/input9
[    1.325832] hid-generic 0018:04F3:2FE0.0002: input,hidraw1: I2C HID v1.00 Device [ELAN2513:00 04F3:2FE0] on i2c-ELAN2513:00
[    1.342516] e1000e 0000:00:1f.6 enp0s31f6: renamed from eth0
[    1.493687] usb 1-8: New USB device found, idVendor=0408, idProduct=545b, bcdDevice= 0.06
[    1.493703] usb 1-8: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[    1.493709] usb 1-8: Product: HP 5MP Camera
[    1.493713] usb 1-8: Manufacturer: Quanta
[    1.493717] usb 1-8: SerialNumber: 01.00.00
[    1.638535] usb 1-14: new full-speed USB device number 3 using xhci_hcd
[    1.695360] ish-hid {33AECD58-B679-4E54-9BD9-A04D34F0C226}: [hid-ish]: enum_devices_done OK, num_hid_devices=2
[    1.708381] hid-generic 001F:8087:0AC2.0003: hidraw2: SENSOR HUB HID v2.00 Device [hid-ishtp 8087:0AC2] on 
[    1.716122] hid-generic 001F:8087:0AC3.0004: hidraw3: SENSOR HUB HID v2.00 Device [hid-ishtp 8087:0AC3] on 
[    1.720506] hid-sensor-hub 001F:8087:0AC2.0003: hidraw2: SENSOR HUB HID v2.00 Device [hid-ishtp 8087:0AC2] on 
[    1.722175] hid-sensor-hub 001F:8087:0AC3.0004: hidraw3: SENSOR HUB HID v2.00 Device [hid-ishtp 8087:0AC3] on 
[    1.789094] usb 1-14: New USB device found, idVendor=8087, idProduct=0033, bcdDevice= 0.00
[    1.789110] usb 1-14: New USB device strings: Mfr=0, Product=0, SerialNumber=0
[    1.890344] raid6: avx2x4   gen() 35214 MB/s
[    1.958344] raid6: avx2x2   gen() 38373 MB/s
[    2.026344] raid6: avx2x1   gen() 35654 MB/s
[    2.026344] raid6: using algorithm avx2x2 gen() 38373 MB/s
[    2.094344] raid6: .... xor() 24914 MB/s, rmw enabled
[    2.094345] raid6: using avx2x2 recovery algorithm
[    2.095866] xor: automatically using best checksumming function   avx       
[    2.096777] async_tx: api initialized (async)
[    2.161736] Btrfs loaded, zoned=yes, fsverity=yes
[    2.248664] EXT4-fs (nvme0n1p3): mounted filesystem 061502a9-205e-4b73-8251-1220e770c5d8 ro with ordered data mode. Quota mode: none.
[    2.326932] systemd[1]: Inserted module 'autofs4'
[    2.340701] systemd[1]: systemd 249.11-0ubuntu3.9 running in system mode (+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT +GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBFDISK +PCRE2 -PWQUALITY -P11KIT -QRENCODE +BZIP2 +LZ4 +XZ +ZLIB +ZSTD -XKBCOMMON +UTMP +SYSVINIT default-hierarchy=unified)
[    2.340779] systemd[1]: Detected architecture x86-64.
[    2.340981] systemd[1]: Hostname set to <u-HP-ZBook-Fury-16-G9-Mobile-Workstation-PC>.
[    2.398743] block nvme0n1: the capability attribute has been deprecated.
[    2.477456] systemd[1]: Queued start job for default target Graphical Interface.
[    2.508096] systemd[1]: Created slice Slice /system/modprobe.
[    2.508852] systemd[1]: Created slice Slice /system/systemd-fsck.
[    2.509177] systemd[1]: Created slice User and Session Slice.
[    2.509303] systemd[1]: Started Forward Password Requests to Wall Directory Watch.
[    2.509638] systemd[1]: Set up automount Arbitrary Executable File Formats File System Automount Point.
[    2.509752] systemd[1]: Reached target User and Group Name Lookups.
[    2.509781] systemd[1]: Reached target Remote File Systems.
[    2.509800] systemd[1]: Reached target Slice Units.
[    2.509831] systemd[1]: Reached target Mounting snaps.
[    2.509881] systemd[1]: Reached target Local Verity Protected Volumes.
[    2.510018] systemd[1]: Listening on Device-mapper event daemon FIFOs.
[    2.510201] systemd[1]: Listening on LVM2 poll daemon socket.
[    2.510321] systemd[1]: Listening on Syslog Socket.
[    2.510445] systemd[1]: Listening on fsck to fsckd communication Socket.
[    2.510504] systemd[1]: Listening on initctl Compatibility Named Pipe.
[    2.510754] systemd[1]: Listening on Journal Audit Socket.
[    2.510851] systemd[1]: Listening on Journal Socket (/dev/log).
[    2.510983] systemd[1]: Listening on Journal Socket.
[    2.511253] systemd[1]: Listening on udev Control Socket.
[    2.511347] systemd[1]: Listening on udev Kernel Socket.
[    2.512429] systemd[1]: Mounting Huge Pages File System...
[    2.513492] systemd[1]: Mounting POSIX Message Queue File System...
[    2.514673] systemd[1]: Mounting Kernel Debug File System...
[    2.515596] systemd[1]: Mounting Kernel Trace File System...
[    2.517730] systemd[1]: Starting Journal Service...
[    2.517905] systemd[1]: Finished Availability of block devices.
[    2.518887] systemd[1]: Starting Set the console keyboard layout...
[    2.519749] systemd[1]: Starting Create List of Static Device Nodes...
[    2.520414] systemd[1]: Starting Monitoring of LVM2 mirrors, snapshots etc. using dmeventd or progress polling...
[    2.521054] systemd[1]: Starting Load Kernel Module chromeos_pstore...
[    2.521661] systemd[1]: Starting Load Kernel Module configfs...
[    2.522235] systemd[1]: Starting Load Kernel Module drm...
[    2.522835] systemd[1]: Starting Load Kernel Module efi_pstore...
[    2.523366] systemd[1]: Starting Load Kernel Module fuse...
[    2.524133] systemd[1]: Starting Load Kernel Module pstore_blk...
[    2.525228] systemd[1]: Starting Load Kernel Module pstore_zone...
[    2.525603] systemd[1]: Starting Load Kernel Module ramoops...
[    2.526481] systemd[1]: Starting Load Kernel Modules...
[    2.526717] pstore: Using crash dump compression: deflate
[    2.526975] systemd[1]: Starting Remount Root and Kernel File Systems...
[    2.527376] systemd[1]: Starting Coldplug All udev Devices...
[    2.528074] systemd[1]: Mounted Huge Pages File System.
[    2.528112] systemd[1]: Mounted POSIX Message Queue File System.
[    2.528136] systemd[1]: Mounted Kernel Debug File System.
[    2.528157] systemd[1]: Mounted Kernel Trace File System.
[    2.528283] systemd[1]: Finished Create List of Static Device Nodes.
[    2.528373] systemd[1]: modprobe@configfs.service: Deactivated successfully.
[    2.528469] systemd[1]: Finished Load Kernel Module configfs.
[    2.528574] systemd[1]: modprobe@drm.service: Deactivated successfully.
[    2.528692] systemd[1]: Finished Load Kernel Module drm.
[    2.528786] systemd[1]: modprobe@fuse.service: Deactivated successfully.
[    2.528879] systemd[1]: Finished Load Kernel Module fuse.
[    2.528955] systemd[1]: modprobe@pstore_blk.service: Deactivated successfully.
[    2.529015] pstore: Registered efi_pstore as persistent store backend
[    2.529050] systemd[1]: Finished Load Kernel Module pstore_blk.
[    2.529124] systemd[1]: modprobe@pstore_zone.service: Deactivated successfully.
[    2.529212] systemd[1]: Finished Load Kernel Module pstore_zone.
[    2.529285] systemd[1]: modprobe@efi_pstore.service: Deactivated successfully.
[    2.529452] systemd[1]: Finished Load Kernel Module efi_pstore.
[    2.529652] systemd[1]: modprobe@ramoops.service: Deactivated successfully.
[    2.529882] systemd[1]: Finished Load Kernel Module ramoops.
[    2.530651] systemd[1]: Mounting FUSE Control File System...
[    2.531020] systemd[1]: Mounting Kernel Configuration File System...
[    2.531967] systemd[1]: Mounted FUSE Control File System.
[    2.532245] systemd[1]: Mounted Kernel Configuration File System.
[    2.534239] lp: driver loaded but no devices found
[    2.536191] ppdev: user-space parallel port driver
[    2.537747] parport0: PC-style at 0x278 [PCSPP,EPP]
[    2.548944] systemd[1]: Started Journal Service.
[    2.558697] EXT4-fs (nvme0n1p3): re-mounted 061502a9-205e-4b73-8251-1220e770c5d8 r/w. Quota mode: none.
[    2.562610] systemd-journald[460]: Received client request to flush runtime journal.
[    2.570309] Adding 51783676k swap on /swapfile.  Priority:-2 extents:116 across:486625016k SS
[    2.605316] loop0: detected capacity change from 0 to 8
[    2.605574] loop1: detected capacity change from 0 to 151352
[    2.606188] loop2: detected capacity change from 0 to 489008
[    2.606709] loop3: detected capacity change from 0 to 492168
[    2.607206] loop4: detected capacity change from 0 to 994336
[    2.607787] loop5: detected capacity change from 0 to 1017816
[    2.608263] loop6: detected capacity change from 0 to 187776
[    2.609008] loop7: detected capacity change from 0 to 25240
[    2.610092] loop8: detected capacity change from 0 to 83648
[    2.610559] loop9: detected capacity change from 0 to 83672
[    2.611133] loop10: detected capacity change from 0 to 904
[    2.644214] lp0: using parport0 (polling).
[    2.645779] input: Intel HID events as /devices/platform/INTC1070:00/input/input11
[    2.646076] intel-hid INTC1070:00: platform supports 5 button array
[    2.646103] input: Intel HID 5 button array as /devices/platform/INTC1070:00/input/input12
[    2.651830] Consider using thermal netlink events interface
[    2.681111] __spi_add_device() [664] CS[0] = [0]
[    2.681118] __spi_add_device() [667] CS_NEW[1] = [0]
[    2.681122] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use
[    2.681163] spi_master spi0: error -EBUSY: failed to add SPI device CSC3551:00 from ACPI
[    2.681882] Serial bus multi instantiate pseudo device driver: probe of CSC3551:00 failed with error -16
[    2.731360] mc: Linux media interface: v0.10
[    2.742333] hp_wmi: query 0x4 returned error 0x5
[    2.743004] dw-apb-uart.4: ttyS5 at MMIO 0x4017004000 (irq = 42, base_baud = 6250000) is a 16550A
[    2.756380] cfg80211: Loading compiled-in X.509 certificates for regulatory database
[    2.756591] Loaded X.509 cert 'sforshee: 00b28ddf47aef9cea7'
[    2.757031] mei_me 0000:00:16.0: enabling device (0000 -> 0002)
[    2.757187] intel_rapl_msr: PL4 support detected.
[    2.759023] intel_rapl_common: Found RAPL domain package
[    2.759029] intel_rapl_common: Found RAPL domain core
[    2.759031] RAPL PMU: API unit is 2^-32 Joules, 4 fixed counters, 655360 ms ovfl timer
[    2.759032] intel_rapl_common: Found RAPL domain uncore
[    2.759033] RAPL PMU: hw unit of domain pp0-core 2^-14 Joules
[    2.759034] RAPL PMU: hw unit of domain package 2^-14 Joules
[    2.759034] RAPL PMU: hw unit of domain pp1-gpu 2^-14 Joules
[    2.759035] RAPL PMU: hw unit of domain psys 2^-14 Joules
[    2.759035] intel_rapl_common: Found RAPL domain psys
[    2.759455] input: HP WMI hotkeys as /devices/virtual/input/input13
[    2.761545] dw-apb-uart.5: ttyS6 at MMIO 0x4017005000 (irq = 16, base_baud = 6250000) is a 16550A
[    2.767378] cryptd: max_cpu_qlen set to 1000
[    2.771724] hid_sensor_als HID-SENSOR-200041.4.auto: failed to setup attributes
[    2.771727] hid_sensor_als: probe of HID-SENSOR-200041.4.auto failed with error -1
[    2.776216] videodev: Linux video capture interface: v2.00
[    2.778639] proc_thermal_pci 0000:00:04.0: enabling device (0000 -> 0002)
[    2.778984] AVX2 version of gcm_enc/dec engaged.
[    2.779015] intel_rapl_common: Found RAPL domain package
[    2.779087] AES CTR mode by8 optimization enabled
[    2.780889] Intel(R) Wireless WiFi driver for Linux
[    2.780969] iwlwifi 0000:00:14.3: enabling device (0000 -> 0002)
[    2.783194] iwlwifi 0000:00:14.3: Detected crf-id 0x400410, cnv-id 0x80401 wfpm id 0x80000020
[    2.783220] iwlwifi 0000:00:14.3: PCI dev 7af0/0090, rev=0x430, rfid=0x2010d000
[    2.786378] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-so-a0-gf-a0-86.ucode failed with error -2
[    2.786551] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-so-a0-gf-a0-85.ucode failed with error -2
[    2.786662] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-so-a0-gf-a0-84.ucode failed with error -2
[    2.786680] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-so-a0-gf-a0-83.ucode failed with error -2
[    2.786696] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-so-a0-gf-a0-82.ucode failed with error -2
[    2.786712] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-so-a0-gf-a0-81.ucode failed with error -2
[    2.786729] iwlwifi 0000:00:14.3: Direct firmware load for iwlwifi-so-a0-gf-a0-80.ucode failed with error -2
[    2.792821] iwlwifi 0000:00:14.3: api flags index 2 larger than supported by driver
[    2.792832] iwlwifi 0000:00:14.3: TLV_FW_FSEQ_VERSION: FSEQ Version: 0.0.2.41
[    2.793094] iwlwifi 0000:00:14.3: loaded firmware version 79.27f1c37b.0 so-a0-gf-a0-79.ucode op_mode iwlmvm
[    2.794047] Bluetooth: Core ver 2.22
[    2.794063] NET: Registered PF_BLUETOOTH protocol family
[    2.794064] Bluetooth: HCI device and connection manager initialized
[    2.794067] Bluetooth: HCI socket layer initialized
[    2.794068] Bluetooth: L2CAP socket layer initialized
[    2.794071] Bluetooth: SCO socket layer initialized
[    2.801513] usbcore: registered new interface driver btusb
[    2.803103] Bluetooth: hci0: Device revision is 0
[    2.803107] Bluetooth: hci0: Secure boot is enabled
[    2.803108] Bluetooth: hci0: OTP lock is enabled
[    2.803109] Bluetooth: hci0: API lock is enabled
[    2.803110] Bluetooth: hci0: Debug lock is disabled
[    2.803111] Bluetooth: hci0: Minimum firmware build 1 week 10 2014
[    2.803112] Bluetooth: hci0: Bootloader timestamp 2019.40 buildtype 1 build 38
[    2.803164] ACPI Warning: \_SB.PC00.XHCI.RHUB.HS14._DSM: Argument #4 type mismatch - Found [Integer], ACPI requires [Package] (20230628/nsarguments-61)
[    2.803288] Bluetooth: hci0: DSM reset method type: 0x00
[    2.804409] usb 1-8: Found UVC 1.50 device HP 5MP Camera (0408:545b)
[    2.804545] Bluetooth: hci0: Found device firmware: intel/ibt-1040-0041.sfi
[    2.804554] Bluetooth: hci0: Boot Address: 0x100800
[    2.804555] Bluetooth: hci0: Firmware Version: 107-51.22
[    2.813005] audit: type=1400 audit(1700587894.520:2): apparmor="STATUS" operation="profile_load" profile="unconfined" name="libreoffice-senddoc" pid=785 comm="apparmor_parser"
[    2.813071] audit: type=1400 audit(1700587894.520:3): apparmor="STATUS" operation="profile_load" profile="unconfined" name="libreoffice-oosplash" pid=784 comm="apparmor_parser"
[    2.813193] audit: type=1400 audit(1700587894.520:4): apparmor="STATUS" operation="profile_load" profile="unconfined" name="libreoffice-xpdfimport" pid=787 comm="apparmor_parser"
[    2.813277] audit: type=1400 audit(1700587894.520:5): apparmor="STATUS" operation="profile_load" profile="unconfined" name="nvidia_modprobe" pid=778 comm="apparmor_parser"
[    2.813280] audit: type=1400 audit(1700587894.520:6): apparmor="STATUS" operation="profile_load" profile="unconfined" name="nvidia_modprobe//kmod" pid=778 comm="apparmor_parser"
[    2.813312] audit: type=1400 audit(1700587894.520:7): apparmor="STATUS" operation="profile_load" profile="unconfined" name="lsb_release" pid=777 comm="apparmor_parser"
[    2.813456] audit: type=1400 audit(1700587894.520:8): apparmor="STATUS" operation="profile_load" profile="unconfined" name="/usr/bin/man" pid=781 comm="apparmor_parser"
[    2.813465] audit: type=1400 audit(1700587894.520:9): apparmor="STATUS" operation="profile_load" profile="unconfined" name="man_filter" pid=781 comm="apparmor_parser"
[    2.813468] audit: type=1400 audit(1700587894.520:10): apparmor="STATUS" operation="profile_load" profile="unconfined" name="man_groff" pid=781 comm="apparmor_parser"
[    2.882502] usb 1-8: Found UVC 1.50 device HP 5MP Camera (0408:545b)
[    2.900201] usbcore: registered new interface driver uvcvideo
[    3.053718] i915 0000:00:02.0: [drm] VT-d active for gfx access
[    3.053871] Console: switching to colour dummy device 80x25
[    3.053889] i915 0000:00:02.0: vgaarb: deactivate vga console
[    3.053933] i915 0000:00:02.0: [drm] Using Transparent Hugepages
[    3.054611] i915 0000:00:02.0: vgaarb: VGA decodes changed: olddecodes=io+mem,decodes=none:owns=io+mem
[    3.055546] i915 0000:00:02.0: [drm] Finished loading DMC firmware i915/adls_dmc_ver2_01.bin (v2.1)
[    3.060685] iwlwifi 0000:00:14.3: Detected Intel(R) Wi-Fi 6E AX211 160MHz, REV=0x430
[    3.060770] thermal thermal_zone17: failed to read out thermal zone (-61)
[    3.061231] mei_hdcp 0000:00:16.0-b638ab7e-94e2-4ea2-a552-d1c54b627f04: bound 0000:00:02.0 (ops i915_hdcp_ops [i915])
[    3.064358] snd_hda_intel 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if info 0x040100
[    3.064377] snd_hda_intel 0000:00:1f.3: Digital mics found on Skylake+ platform, using SOF driver
[    3.064434] snd_hda_intel 0000:01:00.1: enabling device (0000 -> 0002)
[    3.064534] snd_hda_intel 0000:01:00.1: Disabling MSI
[    3.064539] snd_hda_intel 0000:01:00.1: Handle vga_switcheroo audio client
[    3.068848] iwlwifi 0000:00:14.3: WRT: Invalid buffer destination
[    3.088809] intel_tcc_cooling: TCC Offset locked
[    3.094503] input: HDA NVidia HDMI/DP,pcm=3 as /devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card0/input14
[    3.094561] input: HDA NVidia HDMI/DP,pcm=7 as /devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card0/input15
[    3.094611] input: HDA NVidia HDMI/DP,pcm=8 as /devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card0/input16
[    3.094663] input: HDA NVidia HDMI/DP,pcm=9 as /devices/pci0000:00/0000:00:01.0/0000:01:00.1/sound/card0/input17
[    3.099580] sof-audio-pci-intel-tgl 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if info 0x040100
[    3.102850] sof-audio-pci-intel-tgl 0000:00:1f.3: Digital mics found on Skylake+ platform, using SOF driver
[    3.102860] sof-audio-pci-intel-tgl 0000:00:1f.3: enabling device (0000 -> 0002)
[    3.103516] sof-audio-pci-intel-tgl 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if 0x040100
[    3.105501] input: SYNA30F7:00 06CB:CEC3 Mouse as /devices/pci0000:00/0000:00:15.0/i2c_designware.0/i2c-1/i2c-SYNA30F7:00/0018:06CB:CEC3.0001/input/input18
[    3.105601] input: SYNA30F7:00 06CB:CEC3 Touchpad as /devices/pci0000:00/0000:00:15.0/i2c_designware.0/i2c-1/i2c-SYNA30F7:00/0018:06CB:CEC3.0001/input/input19
[    3.105678] hid-multitouch 0018:06CB:CEC3.0001: input,hidraw0: I2C HID v1.00 Mouse [SYNA30F7:00 06CB:CEC3] on i2c-SYNA30F7:00
[    3.106994] snd_hda_intel 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if info 0x040100
[    3.108939] snd_hda_intel 0000:00:1f.3: Digital mics found on Skylake+ platform, using SOF driver
[    3.108945] sof-audio-pci-intel-tgl 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if info 0x040100
[    3.108951] sof-audio-pci-intel-tgl 0000:00:1f.3: Digital mics found on Skylake+ platform, using SOF driver
[    3.109080] sof-audio-pci-intel-tgl 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if 0x040100
[    3.214992] Bluetooth: BNEP (Ethernet Emulation) ver 1.3
[    3.214996] Bluetooth: BNEP filters: protocol multicast
[    3.215001] Bluetooth: BNEP socket layer initialized
[    3.230031] iwlwifi 0000:00:14.3: WFPM_UMAC_PD_NOTIFICATION: 0x20
[    3.230062] iwlwifi 0000:00:14.3: WFPM_LMAC2_PD_NOTIFICATION: 0x1f
[    3.230070] iwlwifi 0000:00:14.3: WFPM_AUTH_KEY_0: 0x90
[    3.230080] iwlwifi 0000:00:14.3: CNVI_SCU_SEQ_DATA_DW9: 0x0
[    3.230319] iwlwifi 0000:00:14.3: loaded PNVM version 64acdc51
[    3.247045] iwlwifi 0000:00:14.3: Detected RF GF, rfid=0x2010d000
[    3.315329] iwlwifi 0000:00:14.3: base HW address: e0:d0:45:23:d6:c4
[    3.320910] input: ELAN2513:00 04F3:2FE0 as /devices/pci0000:00/0000:00:15.1/i2c_designware.1/i2c-2/i2c-ELAN2513:00/0018:04F3:2FE0.0002/input/input21
[    3.320963] input: ELAN2513:00 04F3:2FE0 UNKNOWN as /devices/pci0000:00/0000:00:15.1/i2c_designware.1/i2c-2/i2c-ELAN2513:00/0018:04F3:2FE0.0002/input/input22
[    3.320986] input: ELAN2513:00 04F3:2FE0 UNKNOWN as /devices/pci0000:00/0000:00:15.1/i2c_designware.1/i2c-2/i2c-ELAN2513:00/0018:04F3:2FE0.0002/input/input23
[    3.321028] hid-multitouch 0018:04F3:2FE0.0002: input,hidraw1: I2C HID v1.00 Device [ELAN2513:00 04F3:2FE0] on i2c-ELAN2513:00
[    3.321065] snd_hda_intel 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if info 0x040100
[    3.321087] snd_hda_intel 0000:00:1f.3: Digital mics found on Skylake+ platform, using SOF driver
[    3.321093] sof-audio-pci-intel-tgl 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if info 0x040100
[    3.321099] sof-audio-pci-intel-tgl 0000:00:1f.3: Digital mics found on Skylake+ platform, using SOF driver
[    3.321301] sof-audio-pci-intel-tgl 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if 0x040100
[    3.362398] iwlwifi 0000:00:14.3 wlp0s20f3: renamed from wlan0
[    3.603758] iwlwifi 0000:00:14.3: WRT: Invalid buffer destination
[    3.759693] iwlwifi 0000:00:14.3: WFPM_UMAC_PD_NOTIFICATION: 0x20
[    3.759726] iwlwifi 0000:00:14.3: WFPM_LMAC2_PD_NOTIFICATION: 0x1f
[    3.759734] iwlwifi 0000:00:14.3: WFPM_AUTH_KEY_0: 0x90
[    3.759742] iwlwifi 0000:00:14.3: CNVI_SCU_SEQ_DATA_DW9: 0x0
[    3.847915] iwlwifi 0000:00:14.3: Registered PHC clock: iwlwifi-PTP, with index: 1
[    3.966252] loop11: detected capacity change from 0 to 8
[    4.181547] Bluetooth: hci0: Waiting for firmware download to complete
[    4.182068] Bluetooth: hci0: Firmware loaded in 1345235 usecs
[    4.182103] Bluetooth: hci0: Waiting for device to boot
[    4.198070] Bluetooth: hci0: Device booted in 15608 usecs
[    4.198071] Bluetooth: hci0: Malformed MSFT vendor event: 0x02
[    4.198208] Bluetooth: hci0: Found Intel DDC parameters: intel/ibt-1040-0041.ddc
[    4.200072] Bluetooth: hci0: Applying Intel DDC parameters completed
[    4.203095] Bluetooth: hci0: Firmware timestamp 2022.51 buildtype 1 build 56683
[    4.273146] Bluetooth: MGMT ver 1.22
[    4.276125] NET: Registered PF_ALG protocol family
[    8.186548] i915 0000:00:02.0: [drm] GT0: GuC firmware i915/tgl_guc_70.bin (70.12.1) is recommended, but only i915/tgl_guc_70.bin (70.5.1) was found
[    8.186554] i915 0000:00:02.0: [drm] GT0: Consider updating your linux-firmware pkg or downloading from https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/tree/i915
[    8.191286] i915 0000:00:02.0: [drm] GT0: GuC firmware i915/tgl_guc_70.bin version 70.5.1
[    8.191289] i915 0000:00:02.0: [drm] GT0: HuC firmware i915/tgl_huc.bin version 7.9.3
[    8.206366] i915 0000:00:02.0: [drm] GT0: HuC: authenticated for all workloads
[    8.206377] i915 0000:00:02.0: [drm] GT0: GUC: submission disabled
[    8.206381] i915 0000:00:02.0: [drm] GT0: GUC: SLPC disabled
[    8.207416] mei_pxp 0000:00:16.0-fbf6fcf1-96cf-4e2e-a6a6-1bab8cbe36b1: bound 0000:00:02.0 (ops i915_pxp_tee_component_ops [i915])
[    8.207592] i915 0000:00:02.0: [drm] Protected Xe Path (PXP) protected content support initialized
[    8.227022] [drm] Initialized i915 1.6.0 20230929 for 0000:00:02.0 on minor 0
[    8.228063] ACPI: video: Video Device [PEGP] (multi-head: yes  rom: no  post: no)
[    8.228218] input: Video Bus as /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:03/LNXVIDEO:00/input/input25
[    8.228301] snd_hda_intel 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if info 0x040100
[    8.228551] snd_hda_intel 0000:00:1f.3: Digital mics found on Skylake+ platform, using SOF driver
[    8.228559] sof-audio-pci-intel-tgl 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if info 0x040100
[    8.229233] sof-audio-pci-intel-tgl 0000:00:1f.3: Digital mics found on Skylake+ platform, using SOF driver
[    8.229482] sof-audio-pci-intel-tgl 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if 0x040100
[    8.230266] ACPI: video: Video Device [GFX0] (multi-head: yes  rom: no  post: no)
[    8.230578] input: Video Bus as /devices/LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/LNXVIDEO:01/input/input26
[    8.230628] snd_hda_intel 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if info 0x040100
[    8.230638] snd_hda_intel 0000:00:1f.3: Digital mics found on Skylake+ platform, using SOF driver
[    8.230643] sof-audio-pci-intel-tgl 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if info 0x040100
[    8.230651] sof-audio-pci-intel-tgl 0000:00:1f.3: Digital mics found on Skylake+ platform, using SOF driver
[    8.230717] i915 display info: display version: 12
[    8.230719] i915 display info: cursor_needs_physical: no
[    8.230720] i915 display info: has_cdclk_crawl: no
[    8.230721] i915 display info: has_cdclk_squash: no
[    8.230722] i915 display info: has_ddi: yes
[    8.230723] i915 display info: has_dp_mst: yes
[    8.230724] i915 display info: has_dsb: yes
[    8.230725] i915 display info: has_fpga_dbg: yes
[    8.230726] i915 display info: has_gmch: no
[    8.230727] i915 display info: has_hotplug: yes
[    8.230728] i915 display info: has_hti: yes
[    8.230729] i915 display info: has_ipc: yes
[    8.230729] i915 display info: has_overlay: no
[    8.230730] i915 display info: has_psr: yes
[    8.230731] i915 display info: has_psr_hw_tracking: no
[    8.230732] i915 display info: overlay_needs_physical: no
[    8.230733] i915 display info: supports_tv: no
[    8.230734] i915 display info: has_hdcp: yes
[    8.230735] i915 display info: has_dmc: yes
[    8.230736] i915 display info: has_dsc: yes
[    8.230822] sof-audio-pci-intel-tgl 0000:00:1f.3: DSP detected with PCI class/subclass/prog-if 0x040100
[    8.233200] fbcon: i915drmfb (fb0) is primary device
[    8.233161] sof-audio-pci-intel-tgl 0000:00:1f.3: bound 0000:00:02.0 (ops i915_audio_component_bind_ops [i915])
[    8.239720] sof-audio-pci-intel-tgl 0000:00:1f.3: use msi interrupt mode
[    8.241043] Console: switching to colour frame buffer device 240x75
[    8.258767] i915 0000:00:02.0: [drm] fb0: i915drmfb frame buffer device
[    8.304348] sof-audio-pci-intel-tgl 0000:00:1f.3: hda codecs found, mask 5
[    8.304352] sof-audio-pci-intel-tgl 0000:00:1f.3: using HDA machine driver skl_hda_dsp_generic now
[    8.304354] sof-audio-pci-intel-tgl 0000:00:1f.3: DMICs detected in NHLT tables: 4
[    8.305233] sof-audio-pci-intel-tgl 0000:00:1f.3: Firmware info: version 2:0:0-b678a
[    8.305235] sof-audio-pci-intel-tgl 0000:00:1f.3: Firmware: ABI 3:20:0 Kernel ABI 3:23:0
[    8.305238] sof-audio-pci-intel-tgl 0000:00:1f.3: unknown sof_ext_man header type 3 size 0x30
[    8.408869] sof-audio-pci-intel-tgl 0000:00:1f.3: Firmware info: version 2:0:0-b678a
[    8.408873] sof-audio-pci-intel-tgl 0000:00:1f.3: Firmware: ABI 3:20:0 Kernel ABI 3:23:0
[    8.413278] sof-audio-pci-intel-tgl 0000:00:1f.3: Topology: ABI 3:20:0 Kernel ABI 3:23:0
[    8.413451] skl_hda_dsp_generic skl_hda_dsp_generic: ASoC: Parent card not yet available, widget card binding deferred
[    8.432570] snd_hda_codec_realtek ehdaudio0D0: autoconfig for ALC245: line_outs=1 (0x17/0x0/0x0/0x0/0x0) type:speaker
[    8.432575] snd_hda_codec_realtek ehdaudio0D0:    speaker_outs=0 (0x0/0x0/0x0/0x0/0x0)
[    8.432577] snd_hda_codec_realtek ehdaudio0D0:    hp_outs=1 (0x21/0x0/0x0/0x0/0x0)
[    8.432579] snd_hda_codec_realtek ehdaudio0D0:    mono: mono_out=0x0
[    8.432580] snd_hda_codec_realtek ehdaudio0D0:    inputs:
[    8.432581] snd_hda_codec_realtek ehdaudio0D0:      Mic=0x19
[    8.480276] snd_hda_codec_realtek ehdaudio0D0: ASoC: sink widget AIF1TX overwritten
[    8.480284] snd_hda_codec_realtek ehdaudio0D0: ASoC: source widget AIF1RX overwritten
[    8.480413] skl_hda_dsp_generic skl_hda_dsp_generic: ASoC: sink widget hifi3 overwritten
[    8.480418] skl_hda_dsp_generic skl_hda_dsp_generic: ASoC: sink widget hifi2 overwritten
[    8.480422] skl_hda_dsp_generic skl_hda_dsp_generic: ASoC: sink widget hifi1 overwritten
[    8.480426] skl_hda_dsp_generic skl_hda_dsp_generic: ASoC: source widget Codec Output Pin1 overwritten
[    8.480429] skl_hda_dsp_generic skl_hda_dsp_generic: ASoC: sink widget Codec Input Pin1 overwritten
[    8.480434] skl_hda_dsp_generic skl_hda_dsp_generic: ASoC: sink widget Analog Codec Playback overwritten
[    8.480439] skl_hda_dsp_generic skl_hda_dsp_generic: ASoC: sink widget Digital Codec Playback overwritten
[    8.480443] skl_hda_dsp_generic skl_hda_dsp_generic: ASoC: sink widget Alt Analog Codec Playback overwritten
[    8.480449] skl_hda_dsp_generic skl_hda_dsp_generic: ASoC: source widget Analog Codec Capture overwritten
[    8.480453] skl_hda_dsp_generic skl_hda_dsp_generic: ASoC: source widget Digital Codec Capture overwritten
[    8.480458] skl_hda_dsp_generic skl_hda_dsp_generic: ASoC: source widget Alt Analog Codec Capture overwritten
[    8.480467] skl_hda_dsp_generic skl_hda_dsp_generic: hda_dsp_hdmi_build_controls: no PCM in topology for HDMI converter 3
[    8.497683] input: sof-hda-dsp Mic as /devices/pci0000:00/0000:00:1f.3/skl_hda_dsp_generic/sound/card1/input27
[    8.497718] input: sof-hda-dsp Headphone as /devices/pci0000:00/0000:00:1f.3/skl_hda_dsp_generic/sound/card1/input28
[    8.497741] input: sof-hda-dsp HDMI/DP,pcm=3 as /devices/pci0000:00/0000:00:1f.3/skl_hda_dsp_generic/sound/card1/input29
[    8.497769] input: sof-hda-dsp HDMI/DP,pcm=4 as /devices/pci0000:00/0000:00:1f.3/skl_hda_dsp_generic/sound/card1/input30
[    8.497790] input: sof-hda-dsp HDMI/DP,pcm=5 as /devices/pci0000:00/0000:00:1f.3/skl_hda_dsp_generic/sound/card1/input31
[    9.779387] e1000e 0000:00:1f.6 enp0s31f6: NIC Link is Up 100 Mbps Full Duplex, Flow Control: Rx/Tx
[   13.607423] kauditd_printk_skb: 38 callbacks suppressed
[   13.607425] audit: type=1400 audit(1700587905.316:49): apparmor="DENIED" operation="capable" class="cap" profile="/snap/snapd/20290/usr/lib/snapd/snap-confine" pid=1393 comm="snap-confine" capability=12  capname="net_admin"
[   13.607428] audit: type=1400 audit(1700587905.316:50): apparmor="DENIED" operation="capable" class="cap" profile="/snap/snapd/20290/usr/lib/snapd/snap-confine" pid=1393 comm="snap-confine" capability=38  capname="perfmon"
[   13.630116] Bluetooth: RFCOMM TTY layer initialized
[   13.630122] Bluetooth: RFCOMM socket layer initialized
[   13.630125] Bluetooth: RFCOMM ver 1.11
[   16.446904] audit: type=1400 audit(1700587908.156:51): apparmor="DENIED" operation="capable" class="cap" profile="/snap/snapd/20290/usr/lib/snapd/snap-confine" pid=1674 comm="snap-confine" capability=12  capname="net_admin"
[   16.446917] audit: type=1400 audit(1700587908.156:52): apparmor="DENIED" operation="capable" class="cap" profile="/snap/snapd/20290/usr/lib/snapd/snap-confine" pid=1674 comm="snap-confine" capability=38  capname="perfmon"
[   18.708989] audit: type=1400 audit(1700587910.416:53): apparmor="DENIED" operation="capable" class="cap" profile="/snap/snapd/20290/usr/lib/snapd/snap-confine" pid=1751 comm="snap-confine" capability=12  capname="net_admin"
[   18.709004] audit: type=1400 audit(1700587910.416:54): apparmor="DENIED" operation="capable" class="cap" profile="/snap/snapd/20290/usr/lib/snapd/snap-confine" pid=1751 comm="snap-confine" capability=38  capname="perfmon"
[   20.960479] audit: type=1400 audit(1700587912.668:55): apparmor="DENIED" operation="capable" class="cap" profile="/snap/snapd/20290/usr/lib/snapd/snap-confine" pid=1827 comm="snap-confine" capability=12  capname="net_admin"
[   20.960489] audit: type=1400 audit(1700587912.668:56): apparmor="DENIED" operation="capable" class="cap" profile="/snap/snapd/20290/usr/lib/snapd/snap-confine" pid=1827 comm="snap-confine" capability=38  capname="perfmon"
[   23.203823] audit: type=1400 audit(1700587914.912:57): apparmor="DENIED" operation="capable" class="cap" profile="/snap/snapd/20290/usr/lib/snapd/snap-confine" pid=1905 comm="snap-confine" capability=12  capname="net_admin"
[   23.203833] audit: type=1400 audit(1700587914.912:58): apparmor="DENIED" operation="capable" class="cap" profile="/snap/snapd/20290/usr/lib/snapd/snap-confine" pid=1905 comm="snap-confine" capability=38  capname="perfmon"
[   24.068686] i915 0000:00:02.0: [drm] *ERROR* Failed to send PXP TEE message
[   24.921292] rfkill: input handler disabled
[   25.304870] audit: type=1400 audit(1700587917.012:59): apparmor="DENIED" operation="capable" class="cap" profile="/snap/snapd/20290/usr/lib/snapd/snap-confine" pid=2404 comm="snap-confine" capability=12  capname="net_admin"
[   25.304885] audit: type=1400 audit(1700587917.012:60): apparmor="DENIED" operation="capable" class="cap" profile="/snap/snapd/20290/usr/lib/snapd/snap-confine" pid=2404 comm="snap-confine" capability=38  capname="perfmon"
[   29.270972] mei_pxp 0000:00:16.0-fbf6fcf1-96cf-4e2e-a6a6-1bab8cbe36b1: Trying to reset the channel...
[   29.272071] i915 0000:00:02.0: [drm] *ERROR* Failed to send PXP TEE message
[   29.274686] i915 0000:00:02.0: [drm] *ERROR* Failed to send PXP TEE message
[   29.274696] i915 0000:00:02.0: [drm] *ERROR* Failed to send tee msg for inv-stream-key-15, ret=[28]
[   34.390683] mei_pxp 0000:00:16.0-fbf6fcf1-96cf-4e2e-a6a6-1bab8cbe36b1: Trying to reset the channel...
[   34.391750] i915 0000:00:02.0: [drm] *ERROR* Failed to send PXP TEE message
[   34.391772] i915 0000:00:02.0: [drm] *ERROR* Failed to send tee msg init arb session, ret=[-62]
[   34.391778] i915 0000:00:02.0: [drm] *ERROR* tee cmd for arb session creation failed
Mahapatra, Amit Kumar Nov. 21, 2023, 7:18 p.m. UTC | #8
Hello Stefan,

> -----Original Message-----
> From: Stefan Binding <sbinding@opensource.cirrus.com>
> Sent: Tuesday, November 21, 2023 11:07 PM
> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> michael@walle.cc; linux-mtd@lists.infradead.org;
> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>; linux-
> arm-kernel@lists.infradead.org; git (AMD-Xilinx) <git@amd.com>;
> amitrkcian2002@gmail.com; patches@opensource.cirrus.com
> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in SPI core
> 
> 
> On 21/11/2023 16:35, Mahapatra, Amit Kumar wrote:
> > Hello Stefan,
> >
> >> -----Original Message-----
> >> From: Stefan Binding <sbinding@opensource.cirrus.com>
> >> Sent: Tuesday, November 21, 2023 7:28 PM
> >> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
> >> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
> >> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
> >> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> >> michael@walle.cc; linux-mtd@lists.infradead.org;
> >> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> >> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>;
> >> linux- arm-kernel@lists.infradead.org; git (AMD-Xilinx)
> >> <git@amd.com>; amitrkcian2002@gmail.com;
> >> patches@opensource.cirrus.com
> >> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in
> >> SPI core
> >>
> >>
> >> On 21/11/2023 07:39, Mahapatra, Amit Kumar wrote:
> >>> Hello Stefan,
> >>>
> >>>> -----Original Message-----
> >>>> From: Stefan Binding <sbinding@opensource.cirrus.com>
> >>>> Sent: Monday, November 20, 2023 8:00 PM
> >>>> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
> >>>> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
> >>>> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
> >>>> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> >>>> michael@walle.cc; linux-mtd@lists.infradead.org;
> >>>> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> >>>> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>;
> >>>> linux- arm-kernel@lists.infradead.org; git (AMD-Xilinx)
> >>>> <git@amd.com>; amitrkcian2002@gmail.com;
> >>>> patches@opensource.cirrus.com
> >>>> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in
> >>>> SPI core
> >>>>
> >>>>
> >>>> On 18/11/2023 13:54, Amit Kumar Mahapatra wrote:
> >>>>> AMD-Xilinx GQSPI controller has two advanced mode that allows the
> >>>>> controller to consider two flashes as one single device.
> >>>>>
> >>>>> One of these two mode is the parallel mode in which each byte of
> >>>>> data is stored in both devices, the even bits in the lower flash &
> >>>>> the odd bits in the upper flash. The byte split is automatically
> >>>>> handled by the QSPI controller.
> >>>>>
> >>>>> The other mode is the stacked mode in which both the flashes share
> >>>>> the same SPI bus but each of the device contain half of the data.
> >>>>> In this mode, the controller does not follow CS requests but
> >>>>> instead internally wires the two CS levels with the value of the
> >>>>> most significant
> >>>> address bit.
> >>>>> For supporting both these modes SPI core need to be updated for
> >>>>> providing multiple CS for a single SPI device.
> >>>>>
> >>>>> For adding multi CS support the SPI device need to be aware of all
> >>>>> the CS values. So, the "chip_select" member in the spi_device
> >>>>> structure is now an array that holds all the CS values.
> >>>>>
> >>>>> spi_device structure now has a "cs_index_mask" member. This acts
> >>>>> as an index to the chip_select array. If nth bit of
> >>>>> spi->cs_index_mask is set then the driver would assert spi-
> >chip_select[n].
> >>>>>
> >>>>> In parallel mode all the chip selects are asserted/de-asserted
> >>>>> simultaneously and each byte of data is stored in both devices,
> >>>>> the even bits in one, the odd bits in the other. The split is
> >>>>> automatically handled by the GQSPI controller. The GQSPI
> >>>>> controller supports a maximum of two flashes connected in parallel
> >>>>> mode. A SPI_CONTROLLER_MULTI_CS flag bit is added in the spi
> >>>>> controller flags, through ctlr->flags the spi core will make sure
> >>>>> that the controller is capable of handling multiple chip selects at once.
> >>>>>
> >>>>> For supporting multiple CS via GPIO the cs_gpiod member of the
> >>>>> spi_device structure is now an array that holds the gpio
> >>>>> descriptor for each chipselect.
> >>>>>
> >>>>> CS GPIO is not tested due to unavailability of necessary hardware
> setup.
> >>>>>
> >>>>> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-
> >> mahapatra@amd.com>
> >>>>> ---
> >>>>> Hello @Stefen,
> >>>>> We restructured the SPI core implementation, for handling
> >>>>> arbitrary number of devices connected in parallel(multi-cs) or stacked
> mode.
> >>>>> We have tested this updated patch on native-cs setup but couldn't
> >>>>> test cs-gpio due to unavailability of a proper setup. If possible,
> >>>>> could you please retest the cs-gpio use case with this updated
> >>>>> patch and provide
> >>>> your feedback.
> >>>>
> >>>> Hi,
> >>>>
> >>>> I tested this chain on 2 different systems using GPIOs as chip
> >>>> selects, and see the same error on both:
> >>>> [    2.842045] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use
> >>>>
> >>>> Let me know if you need any further testing.
> >>>>
> >>>> Thanks,
> >>>>
> >>>> Stefan Binding
> >>>>
> >>>>> ---
> >>>>>     drivers/spi/spi.c       | 192 +++++++++++++++++++++++++++++++-------
> --
> >>>>>     include/linux/spi/spi.h |  51 ++++++++---
> >>>>>     2 files changed, 191 insertions(+), 52 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index
> >>>>> 8ead7acb99f3..ff66147ba95f 100644
> >>>>> --- a/drivers/spi/spi.c
> >>>>> +++ b/drivers/spi/spi.c
> >>>>> @@ -612,10 +612,21 @@ static int spi_dev_check(struct device *dev,
> >>>>> void
> >>>> *data)
> >>>>>     {
> >>>>>     	struct spi_device *spi = to_spi_device(dev);
> >>>>>     	struct spi_device *new_spi = data;
> >>>>> -
> >>>>> -	if (spi->controller == new_spi->controller &&
> >>>>> -	    spi_get_chipselect(spi, 0) == spi_get_chipselect(new_spi, 0))
> >>>>> -		return -EBUSY;
> >>>>> +	int idx, nw_idx;
> >>>>> +	u8 cs, cs_nw;
> >>>>> +
> >>>>> +	if (spi->controller == new_spi->controller) {
> >>>>> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>>> +			cs = spi_get_chipselect(spi, idx);
> >>>>> +			for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX;
> >>>> nw_idx++) {
> >>>>> +				cs_nw = spi_get_chipselect(spi,
> nw_idx);
> >>> Thank you for dedicating your time to test my patch.
> >>> As per my analysis the error is reported from here as we are
> >>> supplying the former SPI device structure to retrieve the CS value
> >>> for the new SPI devices.
> >>> To fix this, could you kindly substitute the above line with
> >>>
> >>> cs_nw = spi_get_chipselect(new_spi, nw_idx);
> >>>
> >>> and rerun your tests?
> >>> If it works correctly, I will incorporate this fix into my upcoming
> >>> series.
> >>>
> >>> Regards,
> >>> Amit
> 
> Hi,
> 
> I've attached my log.
> I notice that you add a print to of_spi_parse_dt, however since the laptop I
> am using is an x86 laptop, it uses ACPI rather than OF, and I don't think this
> function isnt even compiled in.

That’s correct, I missed it.
Upon reviewing the logs, I discovered that in the ACPI flow, I am not 
initializing the unused CS[] to FF, as I am doing in the OF flow. 
Consequently, in the ACPI flow, all the unused CS[] are automatically 
initialized to 0. During the __spi_add_device process, the SPI core faces 
difficulty distinguishing between a valid CS value of 0 and an unused CS 
value of 0, leading to a failure in the driver probe.

Incorporating the following modifications should resolve this issue. 
Kindly apply these changes on top of 1/8 and re-run the test.

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index ff66147ba95f..d0301c17d4f8 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -619,7 +619,7 @@ static int spi_dev_check(struct device *dev, void *data)
                for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
                        cs = spi_get_chipselect(spi, idx);
                        for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
-                               cs_nw = spi_get_chipselect(spi, nw_idx);
+                               cs_nw = spi_get_chipselect(new_spi, nw_idx);
                                if (cs != 0xFF && cs_nw != 0xFF && cs == cs_nw) {
                                        dev_err(dev, "chipselect %d already in use\n", cs_nw);
                                        return -EBUSY;
@@ -764,6 +764,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,
 {
        struct spi_device       *proxy;
        int                     status;
+       u8                      idx;
 
        /*
         * NOTE:  caller did any chip->bus_num checks necessary.
@@ -779,6 +780,9 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,
 
        WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
 
+       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
+               spi_set_chipselect(proxy, idx, 0xFF);
+
        spi_set_chipselect(proxy, 0, chip->chip_select);
        proxy->max_speed_hz = chip->max_speed_hz;
        proxy->mode = chip->mode;
@@ -2514,6 +2518,7 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
        struct spi_controller *ctlr = spi->controller;
        struct spi_device *ancillary;
        int rc = 0;
+       u8 idx;
 
        /* Alloc an spi_device */
        ancillary = spi_alloc_device(ctlr);
@@ -2524,6 +2529,9 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,

        strscpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias));
 
+       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
+               spi_set_chipselect(ancillary, idx, 0xFF);
+
        /* Use provided chip-select for ancillary device */
        spi_set_chipselect(ancillary, 0, chip_select);
 
@@ -2732,6 +2740,7 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
        struct acpi_spi_lookup lookup = {};
        struct spi_device *spi;
        int ret;
+       u8 idx;
 
        if (!ctlr && index == -1)
                return ERR_PTR(-EINVAL);
@@ -2767,6 +2776,9 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
                return ERR_PTR(-ENOMEM);
        }
 
+       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
+               spi_set_chipselect(spi, idx, 0xFF);
+
        ACPI_COMPANION_SET(&spi->dev, adev);
        spi->max_speed_hz       = lookup.max_speed_hz;
        spi->mode               |= lookup.mode;

Regards,
Amit
 
> Thanks,
> 
> Stefan
> 
> >> Hi,
> >>
> >> I still see the same error:
> >>
> >> [    2.748546] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use
> > Thank you for the quick testing. For further analysis I have
> > incorporated additional debug prints on top of 1/8 patch. The
> > corresponding diff is shared below. Kindly implement the specified
> > changes, rerun your test and share the kernel logs.
> >
> > diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index
> > ff66147ba95f..7f59ea81593d 100644
> > --- a/drivers/spi/spi.c
> > +++ b/drivers/spi/spi.c
> > @@ -618,8 +618,10 @@ static int spi_dev_check(struct device *dev, void
> *data)
> >          if (spi->controller == new_spi->controller) {
> >                  for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >                          cs = spi_get_chipselect(spi, idx);
> > +                       printk("%s() [%d] CS[%d] = [%d]\n", __func__,
> > + __LINE__, idx, cs);
> >                          for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
> > -                               cs_nw = spi_get_chipselect(spi, nw_idx);
> > +                               cs_nw = spi_get_chipselect(new_spi, nw_idx);
> > +                               printk("%s() [%d] CS_NEW[%d] =
> > + [%d]\n", __func__, __LINE__, nw_idx, cs_nw);
> >                                  if (cs != 0xFF && cs_nw != 0xFF && cs == cs_nw) {
> >                                          dev_err(dev, "chipselect %d already in use\n",
> cs_nw);
> >                                          return -EBUSY; @@ -659,8
> > +661,10 @@ static int __spi_add_device(struct spi_device *spi)
> >           */
> >          for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >                  cs = spi_get_chipselect(spi, idx);
> > +               printk("%s() [%d] CS[%d] = [%d]\n", __func__,
> > + __LINE__, idx, cs);
> >                  for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
> >                          nw_cs = spi_get_chipselect(spi, nw_idx);
> > +                       printk("%s() [%d] CS_NEW[%d] = [%d]\n",
> > + __func__, __LINE__, nw_idx, nw_cs);
> >                          if (cs != 0xFF && nw_cs != 0xFF && cs == nw_cs) {
> >                                  dev_err(dev, "chipselect %d already in use\n", nw_cs);
> >                                  return -EBUSY; @@ -2401,6 +2405,9 @@
> > static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
> >          for (idx = 0; idx < rc; idx++)
> >                  spi_set_chipselect(spi, idx, cs[idx]);
> >
> > +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> > +               printk("%s() [%d] CS[%d] = [%d]\n", __func__,
> > + __LINE__, idx, spi_get_chipselect(spi, idx));
> > +
> >          /* spi->chip_select[i] gives the corresponding physical CS for logical CS i
> >           * logical CS number is represented by setting the ith bit in spi-
> >cs_index_mask
> >           * So, for example, if spi->cs_index_mask = 0x01 then logical
> > CS number is 0 and
> >
> > Regards,
> > Amit
> >
> >> Thanks,
> >>
> >> Stefan
> >>
> >>>>> +				if (cs != 0xFF && cs_nw != 0xFF && cs
> ==
> >>>> cs_nw) {
> >>>>> +					dev_err(dev, "chipselect %d
> already in
> >>>> use\n", cs_nw);
> >>>>> +					return -EBUSY;
> >>>>> +				}
> >>>>> +			}
> >>>>> +		}
> >>>>> +	}
> >>>>>     	return 0;
> >>>>>     }
> >>>>>
> >>>>> @@ -629,13 +640,32 @@ static int __spi_add_device(struct
> >>>>> spi_device
> >> *spi)
> >>>>>     {
> >>>>>     	struct spi_controller *ctlr = spi->controller;
> >>>>>     	struct device *dev = ctlr->dev.parent;
> >>>>> -	int status;
> >>>>> +	int status, idx, nw_idx;
> >>>>> +	u8 cs, nw_cs;
> >>>>> +
> >>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>>> +		/* Chipselects are numbered 0..max; validate. */
> >>>>> +		cs = spi_get_chipselect(spi, idx);
> >>>>> +		if (cs != 0xFF && cs >= ctlr->num_chipselect) {
> >>>>> +			dev_err(dev, "cs%d >= max %d\n",
> >>>> spi_get_chipselect(spi, idx),
> >>>>> +				ctlr->num_chipselect);
> >>>>> +			return -EINVAL;
> >>>>> +		}
> >>>>> +	}
> >>>>>
> >>>>> -	/* Chipselects are numbered 0..max; validate. */
> >>>>> -	if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
> >>>>> -		dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0),
> >>>>> -			ctlr->num_chipselect);
> >>>>> -		return -EINVAL;
> >>>>> +	/*
> >>>>> +	 * Make sure that multiple logical CS doesn't map to the same
> >>>> physical CS.
> >>>>> +	 * For example, spi->chip_select[0] != spi->chip_select[1] and
> so on.
> >>>>> +	 */
> >>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>>> +		cs = spi_get_chipselect(spi, idx);
> >>>>> +		for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX;
> nw_idx++) {
> >>>>> +			nw_cs = spi_get_chipselect(spi, nw_idx);
> >>>>> +			if (cs != 0xFF && nw_cs != 0xFF && cs ==
> nw_cs) {
> >>>>> +				dev_err(dev, "chipselect %d already in
> use\n",
> >>>> nw_cs);
> >>>>> +				return -EBUSY;
> >>>>> +			}
> >>>>> +		}
> >>>>>     	}
> >>>>>
> >>>>>     	/* Set the bus ID string */
> >>>>> @@ -647,11 +677,8 @@ static int __spi_add_device(struct spi_device
> >> *spi)
> >>>>>     	 * its configuration.
> >>>>>     	 */
> >>>>>     	status = bus_for_each_dev(&spi_bus_type, NULL, spi,
> spi_dev_check);
> >>>>> -	if (status) {
> >>>>> -		dev_err(dev, "chipselect %d already in use\n",
> >>>>> -				spi_get_chipselect(spi, 0));
> >>>>> +	if (status)
> >>>>>     		return status;
> >>>>> -	}
> >>>>>
> >>>>>     	/* Controller may unregister concurrently */
> >>>>>     	if (IS_ENABLED(CONFIG_SPI_DYNAMIC) && @@ -659,8
> +686,15 @@
> >>>> static
> >>>>> int __spi_add_device(struct spi_device *spi)
> >>>>>     		return -ENODEV;
> >>>>>     	}
> >>>>>
> >>>>> -	if (ctlr->cs_gpiods)
> >>>>> -		spi_set_csgpiod(spi, 0, ctlr->cs_gpiods[spi_get_chipselect(spi,
> >>>> 0)]);
> >>>>> +	if (ctlr->cs_gpiods) {
> >>>>> +		u8 cs;
> >>>>> +
> >>>>> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>>> +			cs = spi_get_chipselect(spi, idx);
> >>>>> +			if (cs != 0xFF)
> >>>>> +				spi_set_csgpiod(spi, idx, ctlr-
> >cs_gpiods[cs]);
> >>>>> +		}
> >>>>> +	}
> >>>>>
> >>>>>     	/*
> >>>>>     	 * Drivers may modify this initial i/o setup, but will @@
> >>>>> -701,6
> >>>>> +735,9 @@ int spi_add_device(struct spi_device *spi)
> >>>>>     	struct spi_controller *ctlr = spi->controller;
> >>>>>     	int status;
> >>>>>
> >>>>> +	/* Set the bus ID string */
> >>>>> +	spi_dev_set_name(spi);
> >>>>> +
> >>>>>     	mutex_lock(&ctlr->add_lock);
> >>>>>     	status = __spi_add_device(spi);
> >>>>>     	mutex_unlock(&ctlr->add_lock); @@ -942,32 +979,51 @@
> static
> >>>>> void spi_res_release(struct spi_controller
> >>>> *ctlr, struct spi_message *mes
> >>>>>     }
> >>>>>
> >>>>>
> >>>>> /*----------------------------------------------------------------
> >>>>> --
> >>>>> --
> >>>>> -----*/
> >>>>> +static inline bool spi_is_last_cs(struct spi_device *spi) {
> >>>>> +	u8 idx;
> >>>>> +	bool last = false;
> >>>>> +
> >>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>>> +		if ((spi->cs_index_mask >> idx) & 0x01) {
> >>>>> +			if (spi->controller->last_cs[idx] ==
> >>>> spi_get_chipselect(spi, idx))
> >>>>> +				last = true;
> >>>>> +		}
> >>>>> +	}
> >>>>> +	return last;
> >>>>> +}
> >>>>> +
> >>>>>
> >>>>>     static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
> >>>>>     {
> >>>>>     	bool activate = enable;
> >>>>> +	u8 idx;
> >>>>>
> >>>>>     	/*
> >>>>>     	 * Avoid calling into the driver (or doing delays) if the chip
> select
> >>>>>     	 * isn't actually changing from the last time this was called.
> >>>>>     	 */
> >>>>> -	if (!force && ((enable && spi->controller->last_cs ==
> >>>> spi_get_chipselect(spi, 0)) ||
> >>>>> -		       (!enable && spi->controller->last_cs !=
> >>>> spi_get_chipselect(spi, 0))) &&
> >>>>> +	if (!force && ((enable && spi->controller->last_cs_index_mask
> ==
> >>>>> +spi-
> >>>>> cs_index_mask &&
> >>>>> +			spi_is_last_cs(spi)) ||
> >>>>> +		       (!enable && spi->controller->last_cs_index_mask
> == spi-
> >>>>> cs_index_mask &&
> >>>>> +			!spi_is_last_cs(spi))) &&
> >>>>>     	    (spi->controller->last_cs_mode_high == (spi->mode &
> >>>> SPI_CS_HIGH)))
> >>>>>     		return;
> >>>>>
> >>>>>     	trace_spi_set_cs(spi, activate);
> >>>>>
> >>>>> -	spi->controller->last_cs = enable ? spi_get_chipselect(spi, 0) : -1;
> >>>>> +	spi->controller->last_cs_index_mask = spi->cs_index_mask;
> >>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> >>>>> +		spi->controller->last_cs[idx] = enable ?
> >>>>> +spi_get_chipselect(spi,
> >>>> 0)
> >>>>> +: -1;
> >>>>>     	spi->controller->last_cs_mode_high = spi->mode &
> SPI_CS_HIGH;
> >>>>>
> >>>>> -	if ((spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) &&
> >>>> !activate)
> >>>>> -		spi_delay_exec(&spi->cs_hold, NULL);
> >>>>> -
> >>>>>     	if (spi->mode & SPI_CS_HIGH)
> >>>>>     		enable = !enable;
> >>>>>
> >>>>> -	if (spi_get_csgpiod(spi, 0)) {
> >>>>> +	if (spi_is_csgpiod(spi)) {
> >>>>> +		if (!spi->controller->set_cs_timing && !activate)
> >>>>> +			spi_delay_exec(&spi->cs_hold, NULL);
> >>>>> +
> >>>>>     		if (!(spi->mode & SPI_NO_CS)) {
> >>>>>     			/*
> >>>>>     			 * Historically ACPI has no means of the GPIO
> polarity
> >>>> and @@
> >>>>> -979,26 +1035,38 @@ static void spi_set_cs(struct spi_device *spi,
> >>>>> bool
> >>>> enable, bool force)
> >>>>>     			 * ambiguity. That's why we use enable, that
> takes
> >>>> SPI_CS_HIGH
> >>>>>     			 * into account.
> >>>>>     			 */
> >>>>> -			if (has_acpi_companion(&spi->dev))
> >>>>> -
> >>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), !enable);
> >>>>> -			else
> >>>>> -				/* Polarity handled by GPIO library */
> >>>>> -
> >>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), activate);
> >>>>> +			for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>>> +				if (((spi->cs_index_mask >> idx) &
> 0x01) &&
> >>>>> +				    spi_get_csgpiod(spi, idx)) {
> >>>>> +					if (has_acpi_companion(&spi-
> >dev))
> >>>>> +
> >>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
> >>>>> +
> >>>> !enable);
> >>>>> +					else
> >>>>> +						/* Polarity handled by
> GPIO
> >>>> library */
> >>>>> +
> >>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
> >>>>> +
> >>>> activate);
> >>>>> +
> >>>>> +					if (activate)
> >>>>> +						spi_delay_exec(&spi-
> >>>>> cs_setup, NULL);
> >>>>> +					else
> >>>>> +						spi_delay_exec(&spi-
> >>>>> cs_inactive, NULL);
> >>>>> +				}
> >>>>> +			}
> >>>>>     		}
> >>>>>     		/* Some SPI masters need both GPIO CS &
> slave_select */
> >>>>>     		if ((spi->controller->flags &
> SPI_CONTROLLER_GPIO_SS) &&
> >>>>>     		    spi->controller->set_cs)
> >>>>>     			spi->controller->set_cs(spi, !enable);
> >>>>> +
> >>>>> +		if (!spi->controller->set_cs_timing) {
> >>>>> +			if (activate)
> >>>>> +				spi_delay_exec(&spi->cs_setup,
> NULL);
> >>>>> +			else
> >>>>> +				spi_delay_exec(&spi->cs_inactive,
> NULL);
> >>>>> +		}
> >>>>>     	} else if (spi->controller->set_cs) {
> >>>>>     		spi->controller->set_cs(spi, !enable);
> >>>>>     	}
> >>>>> -
> >>>>> -	if (spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) {
> >>>>> -		if (activate)
> >>>>> -			spi_delay_exec(&spi->cs_setup, NULL);
> >>>>> -		else
> >>>>> -			spi_delay_exec(&spi->cs_inactive, NULL);
> >>>>> -	}
> >>>>>     }
> >>>>>
> >>>>>     #ifdef CONFIG_HAS_DMA
> >>>>> @@ -2222,8 +2290,8 @@ static void of_spi_parse_dt_cs_delay(struct
> >>>> device_node *nc,
> >>>>>     static int of_spi_parse_dt(struct spi_controller *ctlr, struct
> >>>>> spi_device
> >> *spi,
> >>>>>     			   struct device_node *nc)
> >>>>>     {
> >>>>> -	u32 value;
> >>>>> -	int rc;
> >>>>> +	u32 value, cs[SPI_CS_CNT_MAX];
> >>>>> +	int rc, idx;
> >>>>>
> >>>>>     	/* Mode (clock phase/polarity/etc.) */
> >>>>>     	if (of_property_read_bool(nc, "spi-cpha")) @@ -2295,14
> >>>>> +2363,52
> >>>> @@
> >>>>> static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device
> *spi,
> >>>>>     		return 0;
> >>>>>     	}
> >>>>>
> >>>>> +	if (ctlr->num_chipselect > SPI_CS_CNT_MAX) {
> >>>>> +		dev_err(&ctlr->dev, "No. of CS is more than max. no.
> of
> >>>> supported CS\n");
> >>>>> +		return -EINVAL;
> >>>>> +	}
> >>>>> +
> >>>>> +	/*
> >>>>> +	 * Zero(0) is a valid physical CS value and can be located at
> any
> >>>>> +	 * logical CS in the spi->chip_select[]. If all the physical CS
> >>>>> +	 * are initialized to 0 then It would be difficult to differentiate
> >>>>> +	 * between a valid physical CS 0 & an unused logical CS whose
> >>>> physical
> >>>>> +	 * CS can be 0. As a solution to this issue initialize all the CS to
> 0xFF.
> >>>>> +	 * Now all the unused logical CS will have 0xFF physical CS
> >>>>> +value & can
> >>>> be
> >>>>> +	 * ignore while performing physical CS validity checks.
> >>>>> +	 */
> >>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> >>>>> +		spi_set_chipselect(spi, idx, 0xFF);
> >>>>> +
> >>>>>     	/* Device address */
> >>>>> -	rc = of_property_read_u32(nc, "reg", &value);
> >>>>> -	if (rc) {
> >>>>> +	rc = of_property_read_variable_u32_array(nc, "reg", &cs[0],
> 1,
> >>>>> +						 SPI_CS_CNT_MAX);
> >>>>> +	if (rc < 0) {
> >>>>>     		dev_err(&ctlr->dev, "%pOF has no valid 'reg' property
> >>>> (%d)\n",
> >>>>>     			nc, rc);
> >>>>>     		return rc;
> >>>>>     	}
> >>>>> -	spi_set_chipselect(spi, 0, value);
> >>>>> +	if (rc > ctlr->num_chipselect) {
> >>>>> +		dev_err(&ctlr->dev, "%pOF has number of CS > ctlr-
> >>>>> num_chipselect (%d)\n",
> >>>>> +			nc, rc);
> >>>>> +		return rc;
> >>>>> +	}
> >>>>> +	if ((of_property_read_bool(nc, "parallel-memories")) &&
> >>>>> +	    (!(ctlr->flags & SPI_CONTROLLER_MULTI_CS))) {
> >>>>> +		dev_err(&ctlr->dev, "SPI controller doesn't support
> multi
> >>>> CS\n");
> >>>>> +		return -EINVAL;
> >>>>> +	}
> >>>>> +	for (idx = 0; idx < rc; idx++)
> >>>>> +		spi_set_chipselect(spi, idx, cs[idx]);
> >>>>> +
> >>>>> +	/* spi->chip_select[i] gives the corresponding physical CS for
> >>>>> +logical CS
> >>>> i
> >>>>> +	 * logical CS number is represented by setting the ith bit in
> >>>>> +spi-
> >>>>> cs_index_mask
> >>>>> +	 * So, for example, if spi->cs_index_mask = 0x01 then logical
> CS
> >>>> number is 0 and
> >>>>> +	 * spi->chip_select[0] will give the physical CS.
> >>>>> +	 * By default spi->chip_select[0] will hold the physical CS
> >>>>> +number so,
> >>>> set
> >>>>> +	 * spi->cs_index_mask as 0x01.
> >>>>> +	 */
> >>>>> +	spi->cs_index_mask = 0x01;
> >>>>>
> >>>>>     	/* Device speed */
> >>>>>     	if (!of_property_read_u32(nc, "spi-max-frequency", &value))
> >>>>> @@
> >>>>> -3100,6 +3206,7 @@ int spi_register_controller(struct spi_controller
> *ctlr)
> >>>>>     	struct boardinfo	*bi;
> >>>>>     	int			first_dynamic;
> >>>>>     	int			status;
> >>>>> +	int			idx;
> >>>>>
> >>>>>     	if (!dev)
> >>>>>     		return -ENODEV;
> >>>>> @@ -3164,7 +3271,8 @@ int spi_register_controller(struct
> >>>>> spi_controller
> >>>> *ctlr)
> >>>>>     	}
> >>>>>
> >>>>>     	/* Setting last_cs to -1 means no chip selected */
> >>>>> -	ctlr->last_cs = -1;
> >>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> >>>>> +		ctlr->last_cs[idx] = -1;
> >>>>>
> >>>>>     	status = device_add(&ctlr->dev);
> >>>>>     	if (status < 0)
> >>>>> @@ -3889,7 +3997,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_get_csgpiod(spi, 0))) {
> >>>>> +					  spi_is_csgpiod(spi))) {
> >>>>>     		size_t maxsize = BITS_TO_BYTES(spi->bits_per_word);
> >>>>>     		int ret;
> >>>>>
> >>>>> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
> >>>>> index 7b4baff63c5c..871d3a6d879b 100644
> >>>>> --- a/include/linux/spi/spi.h
> >>>>> +++ b/include/linux/spi/spi.h
> >>>>> @@ -20,6 +20,9 @@
> >>>>>
> >>>>>     #include <uapi/linux/spi/spi.h>
> >>>>>
> >>>>> +/* Max no. of CS supported per spi device */ #define
> >>>>> +SPI_CS_CNT_MAX
> >>>>> +4
> >>>>> +
> >>>>>     struct dma_chan;
> >>>>>     struct software_node;
> >>>>>     struct ptp_system_timestamp;
> >>>>> @@ -132,7 +135,8 @@ extern void
> >>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> >>>>>      * @max_speed_hz: Maximum clock rate to be used with this chip
> >>>>>      *	(on this board); may be changed by the device's driver.
> >>>>>      *	The spi_transfer.speed_hz can override this for each transfer.
> >>>>> - * @chip_select: Chipselect, distinguishing chips handled by
> @controller.
> >>>>> + * @chip_select: Array of physical chipselect, spi->chipselect[i] gives
> >>>>> + *	the corresponding physical CS for logical CS i.
> >>>>>      * @mode: The spi mode defines how data is clocked out and in.
> >>>>>      *	This may be changed by the device's driver.
> >>>>>      *	The "active low" default for chipselect mode can be
> overridden
> >>>>> @@ -157,8 +161,8 @@ extern void
> >>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> >>>>>      *	the device will bind to the named driver and only the named
> driver.
> >>>>>      *	Do not set directly, because core frees it; use
> driver_set_override() to
> >>>>>      *	set or clear it.
> >>>>> - * @cs_gpiod: GPIO descriptor of the chipselect line (optional,
> >>>>> NULL
> >> when
> >>>>> - *	not using a GPIO line)
> >>>>> + * @cs_gpiod: Array of GPIO descriptors of the corresponding
> >>>>> + chipselect
> >>>> lines
> >>>>> + *	(optional, NULL when not using a GPIO line)
> >>>>>      * @word_delay: delay to be inserted between consecutive
> >>>>>      *	words of a transfer
> >>>>>      * @cs_setup: delay to be introduced by the controller after
> >>>>> CS is asserted @@ -167,6 +171,7 @@ extern void
> >>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> >>>>>      *	deasserted. If @cs_change_delay is used from @spi_transfer,
> then
> >>>> the
> >>>>>      *	two delays will be added up.
> >>>>>      * @pcpu_statistics: statistics for the spi_device
> >>>>> + * @cs_index_mask: Bit mask of the active chipselect(s) in the
> >>>>> + chipselect array
> >>>>>      *
> >>>>>      * A @spi_device is used to interchange data between an SPI slave
> >>>>>      * (usually a discrete chip) and CPU memory.
> >>>>> @@ -182,7 +187,7 @@ struct spi_device {
> >>>>>     	struct spi_controller	*controller;
> >>>>>     	struct spi_controller	*master;	/* Compatibility layer
> */
> >>>>>     	u32			max_speed_hz;
> >>>>> -	u8			chip_select;
> >>>>> +	u8			chip_select[SPI_CS_CNT_MAX];
> >>>>>     	u8			bits_per_word;
> >>>>>     	bool			rt;
> >>>>>     #define SPI_NO_TX		BIT(31)		/* No transmit wire */
> >>>>> @@ -213,7 +218,7 @@ struct spi_device {
> >>>>>     	void			*controller_data;
> >>>>>     	char			modalias[SPI_NAME_SIZE];
> >>>>>     	const char		*driver_override;
> >>>>> -	struct gpio_desc	*cs_gpiod;	/* Chip select GPIO descriptor
> >>>> */
> >>>>> +	struct gpio_desc	*cs_gpiod[SPI_CS_CNT_MAX];	/*
> Chip select
> >>>> gpio desc */
> >>>>>     	struct spi_delay	word_delay; /* Inter-word delay */
> >>>>>     	/* CS delays */
> >>>>>     	struct spi_delay	cs_setup;
> >>>>> @@ -223,6 +228,13 @@ struct spi_device {
> >>>>>     	/* The statistics */
> >>>>>     	struct spi_statistics __percpu	*pcpu_statistics;
> >>>>>
> >>>>> +	/* Bit mask of the chipselect(s) that the driver need to use
> from
> >>>>> +	 * the chipselect array.When the controller is capable to
> handle
> >>>>> +	 * multiple chip selects & memories are connected in parallel
> >>>>> +	 * then more than one bit need to be set in cs_index_mask.
> >>>>> +	 */
> >>>>> +	u32			cs_index_mask : SPI_CS_CNT_MAX;
> >>>>> +
> >>>>>     	/*
> >>>>>     	 * Likely need more hooks for more protocol options affecting
> how
> >>>>>     	 * the controller talks to each chip, like:
> >>>>> @@ -279,22 +291,33 @@ static inline void *spi_get_drvdata(const
> >>>>> struct spi_device *spi)
> >>>>>
> >>>>>     static inline u8 spi_get_chipselect(const struct spi_device *spi, u8 idx)
> >>>>>     {
> >>>>> -	return spi->chip_select;
> >>>>> +	return spi->chip_select[idx];
> >>>>>     }
> >>>>>
> >>>>>     static inline void spi_set_chipselect(struct spi_device *spi,
> >>>>> u8 idx, u8
> >>>> chipselect)
> >>>>>     {
> >>>>> -	spi->chip_select = chipselect;
> >>>>> +	spi->chip_select[idx] = chipselect;
> >>>>>     }
> >>>>>
> >>>>>     static inline struct gpio_desc *spi_get_csgpiod(const struct
> >>>>> spi_device *spi,
> >>>> u8 idx)
> >>>>>     {
> >>>>> -	return spi->cs_gpiod;
> >>>>> +	return spi->cs_gpiod[idx];
> >>>>>     }
> >>>>>
> >>>>>     static inline void spi_set_csgpiod(struct spi_device *spi, u8
> >>>>> idx, struct
> >>>> gpio_desc *csgpiod)
> >>>>>     {
> >>>>> -	spi->cs_gpiod = csgpiod;
> >>>>> +	spi->cs_gpiod[idx] = csgpiod;
> >>>>> +}
> >>>>> +
> >>>>> +static inline bool spi_is_csgpiod(struct spi_device *spi) {
> >>>>> +	u8 idx;
> >>>>> +
> >>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>>> +		if (spi_get_csgpiod(spi, idx))
> >>>>> +			return true;
> >>>>> +	}
> >>>>> +	return false;
> >>>>>     }
> >>>>>
> >>>>>     /**
> >>>>> @@ -399,6 +422,8 @@ extern struct spi_device
> >>>> *spi_new_ancillary_device(struct spi_device *spi, u8 ch
> >>>>>      * @bus_lock_spinlock: spinlock for SPI bus locking
> >>>>>      * @bus_lock_mutex: mutex for exclusion of multiple callers
> >>>>>      * @bus_lock_flag: indicates that the SPI bus is locked for
> >>>>> exclusive use
> >>>>> + * @multi_cs_cap: indicates that the SPI Controller can assert/de-
> assert
> >>>>> + *	more than one chip select at once.
> >>>>>      * @setup: updates the device mode and clocking records used by a
> >>>>>      *	device's SPI controller; protocol code may call this.  This
> >>>>>      *	must fail if an unrecognized or unsupported mode is
> requested.
> >>>>> @@ -567,6 +592,11 @@ struct spi_controller {
> >>>>>     #define SPI_CONTROLLER_MUST_TX		BIT(4)	/* Requires tx
> >> */
> >>>>>     #define SPI_CONTROLLER_GPIO_SS		BIT(5)	/* GPIO CS
> >> must
> >>>> select slave */
> >>>>>     #define SPI_CONTROLLER_SUSPENDED	BIT(6)	/* Currently
> >>>> suspended */
> >>>>> +	/*
> >>>>> +	 * The spi-controller has multi chip select capability and can
> >>>>> +	 * assert/de-assert more than one chip select at once.
> >>>>> +	 */
> >>>>> +#define SPI_CONTROLLER_MULTI_CS		BIT(7)
> >>>>>
> >>>>>     	/* Flag indicating if the allocation of this struct is devres-
> managed */
> >>>>>     	bool			devm_allocated;
> >>>>> @@ -677,7 +707,8 @@ struct spi_controller {
> >>>>>     	bool				rt;
> >>>>>     	bool				auto_runtime_pm;
> >>>>>     	bool				cur_msg_mapped;
> >>>>> -	char				last_cs;
> >>>>> +	char				last_cs[SPI_CS_CNT_MAX];
> >>>>> +	char				last_cs_index_mask;
> >>>>>     	bool				last_cs_mode_high;
> >>>>>     	bool                            fallback;
> >>>>>     	struct completion               xfer_completion;
Stefan Binding Nov. 22, 2023, 11:21 a.m. UTC | #9
On 21/11/2023 19:18, Mahapatra, Amit Kumar wrote:
> Hello Stefan,
>
>> -----Original Message-----
>> From: Stefan Binding <sbinding@opensource.cirrus.com>
>> Sent: Tuesday, November 21, 2023 11:07 PM
>> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
>> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
>> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
>> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
>> michael@walle.cc; linux-mtd@lists.infradead.org;
>> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
>> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>; linux-
>> arm-kernel@lists.infradead.org; git (AMD-Xilinx) <git@amd.com>;
>> amitrkcian2002@gmail.com; patches@opensource.cirrus.com
>> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in SPI core
>>
>>
>> On 21/11/2023 16:35, Mahapatra, Amit Kumar wrote:
>>> Hello Stefan,
>>>
>>>> -----Original Message-----
>>>> From: Stefan Binding <sbinding@opensource.cirrus.com>
>>>> Sent: Tuesday, November 21, 2023 7:28 PM
>>>> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
>>>> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
>>>> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
>>>> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
>>>> michael@walle.cc; linux-mtd@lists.infradead.org;
>>>> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
>>>> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>;
>>>> linux- arm-kernel@lists.infradead.org; git (AMD-Xilinx)
>>>> <git@amd.com>; amitrkcian2002@gmail.com;
>>>> patches@opensource.cirrus.com
>>>> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in
>>>> SPI core
>>>>
>>>>
>>>> On 21/11/2023 07:39, Mahapatra, Amit Kumar wrote:
>>>>> Hello Stefan,
>>>>>
>>>>>> -----Original Message-----
>>>>>> From: Stefan Binding <sbinding@opensource.cirrus.com>
>>>>>> Sent: Monday, November 20, 2023 8:00 PM
>>>>>> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
>>>>>> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
>>>>>> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
>>>>>> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
>>>>>> michael@walle.cc; linux-mtd@lists.infradead.org;
>>>>>> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
>>>>>> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>;
>>>>>> linux- arm-kernel@lists.infradead.org; git (AMD-Xilinx)
>>>>>> <git@amd.com>; amitrkcian2002@gmail.com;
>>>>>> patches@opensource.cirrus.com
>>>>>> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in
>>>>>> SPI core
>>>>>>
>>>>>>
>>>>>> On 18/11/2023 13:54, Amit Kumar Mahapatra wrote:
>>>>>>> AMD-Xilinx GQSPI controller has two advanced mode that allows the
>>>>>>> controller to consider two flashes as one single device.
>>>>>>>
>>>>>>> One of these two mode is the parallel mode in which each byte of
>>>>>>> data is stored in both devices, the even bits in the lower flash &
>>>>>>> the odd bits in the upper flash. The byte split is automatically
>>>>>>> handled by the QSPI controller.
>>>>>>>
>>>>>>> The other mode is the stacked mode in which both the flashes share
>>>>>>> the same SPI bus but each of the device contain half of the data.
>>>>>>> In this mode, the controller does not follow CS requests but
>>>>>>> instead internally wires the two CS levels with the value of the
>>>>>>> most significant
>>>>>> address bit.
>>>>>>> For supporting both these modes SPI core need to be updated for
>>>>>>> providing multiple CS for a single SPI device.
>>>>>>>
>>>>>>> For adding multi CS support the SPI device need to be aware of all
>>>>>>> the CS values. So, the "chip_select" member in the spi_device
>>>>>>> structure is now an array that holds all the CS values.
>>>>>>>
>>>>>>> spi_device structure now has a "cs_index_mask" member. This acts
>>>>>>> as an index to the chip_select array. If nth bit of
>>>>>>> spi->cs_index_mask is set then the driver would assert spi-
>>> chip_select[n].
>>>>>>> In parallel mode all the chip selects are asserted/de-asserted
>>>>>>> simultaneously and each byte of data is stored in both devices,
>>>>>>> the even bits in one, the odd bits in the other. The split is
>>>>>>> automatically handled by the GQSPI controller. The GQSPI
>>>>>>> controller supports a maximum of two flashes connected in parallel
>>>>>>> mode. A SPI_CONTROLLER_MULTI_CS flag bit is added in the spi
>>>>>>> controller flags, through ctlr->flags the spi core will make sure
>>>>>>> that the controller is capable of handling multiple chip selects at once.
>>>>>>>
>>>>>>> For supporting multiple CS via GPIO the cs_gpiod member of the
>>>>>>> spi_device structure is now an array that holds the gpio
>>>>>>> descriptor for each chipselect.
>>>>>>>
>>>>>>> CS GPIO is not tested due to unavailability of necessary hardware
>> setup.
>>>>>>> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-
>>>> mahapatra@amd.com>
>>>>>>> ---
>>>>>>> Hello @Stefen,
>>>>>>> We restructured the SPI core implementation, for handling
>>>>>>> arbitrary number of devices connected in parallel(multi-cs) or stacked
>> mode.
>>>>>>> We have tested this updated patch on native-cs setup but couldn't
>>>>>>> test cs-gpio due to unavailability of a proper setup. If possible,
>>>>>>> could you please retest the cs-gpio use case with this updated
>>>>>>> patch and provide
>>>>>> your feedback.
>>>>>>
>>>>>> Hi,
>>>>>>
>>>>>> I tested this chain on 2 different systems using GPIOs as chip
>>>>>> selects, and see the same error on both:
>>>>>> [    2.842045] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use
>>>>>>
>>>>>> Let me know if you need any further testing.
>>>>>>
>>>>>> Thanks,
>>>>>>
>>>>>> Stefan Binding
>>>>>>
>>>>>>> ---
>>>>>>>      drivers/spi/spi.c       | 192 +++++++++++++++++++++++++++++++-------
>> --
>>>>>>>      include/linux/spi/spi.h |  51 ++++++++---
>>>>>>>      2 files changed, 191 insertions(+), 52 deletions(-)
>>>>>>>
>>>>>>> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index
>>>>>>> 8ead7acb99f3..ff66147ba95f 100644
>>>>>>> --- a/drivers/spi/spi.c
>>>>>>> +++ b/drivers/spi/spi.c
>>>>>>> @@ -612,10 +612,21 @@ static int spi_dev_check(struct device *dev,
>>>>>>> void
>>>>>> *data)
>>>>>>>      {
>>>>>>>      	struct spi_device *spi = to_spi_device(dev);
>>>>>>>      	struct spi_device *new_spi = data;
>>>>>>> -
>>>>>>> -	if (spi->controller == new_spi->controller &&
>>>>>>> -	    spi_get_chipselect(spi, 0) == spi_get_chipselect(new_spi, 0))
>>>>>>> -		return -EBUSY;
>>>>>>> +	int idx, nw_idx;
>>>>>>> +	u8 cs, cs_nw;
>>>>>>> +
>>>>>>> +	if (spi->controller == new_spi->controller) {
>>>>>>> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>>>>> +			cs = spi_get_chipselect(spi, idx);
>>>>>>> +			for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX;
>>>>>> nw_idx++) {
>>>>>>> +				cs_nw = spi_get_chipselect(spi,
>> nw_idx);
>>>>> Thank you for dedicating your time to test my patch.
>>>>> As per my analysis the error is reported from here as we are
>>>>> supplying the former SPI device structure to retrieve the CS value
>>>>> for the new SPI devices.
>>>>> To fix this, could you kindly substitute the above line with
>>>>>
>>>>> cs_nw = spi_get_chipselect(new_spi, nw_idx);
>>>>>
>>>>> and rerun your tests?
>>>>> If it works correctly, I will incorporate this fix into my upcoming
>>>>> series.
>>>>>
>>>>> Regards,
>>>>> Amit
>> Hi,
>>
>> I've attached my log.
>> I notice that you add a print to of_spi_parse_dt, however since the laptop I
>> am using is an x86 laptop, it uses ACPI rather than OF, and I don't think this
>> function isnt even compiled in.
> That’s correct, I missed it.
> Upon reviewing the logs, I discovered that in the ACPI flow, I am not
> initializing the unused CS[] to FF, as I am doing in the OF flow.
> Consequently, in the ACPI flow, all the unused CS[] are automatically
> initialized to 0. During the __spi_add_device process, the SPI core faces
> difficulty distinguishing between a valid CS value of 0 and an unused CS
> value of 0, leading to a failure in the driver probe.
>
> Incorporating the following modifications should resolve this issue.
> Kindly apply these changes on top of 1/8 and re-run the test.
>
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index ff66147ba95f..d0301c17d4f8 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -619,7 +619,7 @@ static int spi_dev_check(struct device *dev, void *data)
>                  for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>                          cs = spi_get_chipselect(spi, idx);
>                          for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
> -                               cs_nw = spi_get_chipselect(spi, nw_idx);
> +                               cs_nw = spi_get_chipselect(new_spi, nw_idx);
>                                  if (cs != 0xFF && cs_nw != 0xFF && cs == cs_nw) {
>                                          dev_err(dev, "chipselect %d already in use\n", cs_nw);
>                                          return -EBUSY;
> @@ -764,6 +764,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,
>   {
>          struct spi_device       *proxy;
>          int                     status;
> +       u8                      idx;
>   
>          /*
>           * NOTE:  caller did any chip->bus_num checks necessary.
> @@ -779,6 +780,9 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,
>   
>          WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
>   
> +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> +               spi_set_chipselect(proxy, idx, 0xFF);
> +
>          spi_set_chipselect(proxy, 0, chip->chip_select);
>          proxy->max_speed_hz = chip->max_speed_hz;
>          proxy->mode = chip->mode;
> @@ -2514,6 +2518,7 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
>          struct spi_controller *ctlr = spi->controller;
>          struct spi_device *ancillary;
>          int rc = 0;
> +       u8 idx;
>   
>          /* Alloc an spi_device */
>          ancillary = spi_alloc_device(ctlr);
> @@ -2524,6 +2529,9 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
>
>          strscpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias));
>   
> +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> +               spi_set_chipselect(ancillary, idx, 0xFF);
> +
>          /* Use provided chip-select for ancillary device */
>          spi_set_chipselect(ancillary, 0, chip_select);
>   
> @@ -2732,6 +2740,7 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
>          struct acpi_spi_lookup lookup = {};
>          struct spi_device *spi;
>          int ret;
> +       u8 idx;
>   
>          if (!ctlr && index == -1)
>                  return ERR_PTR(-EINVAL);
> @@ -2767,6 +2776,9 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
>                  return ERR_PTR(-ENOMEM);
>          }
>   
> +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> +               spi_set_chipselect(spi, idx, 0xFF);
> +
>          ACPI_COMPANION_SET(&spi->dev, adev);
>          spi->max_speed_hz       = lookup.max_speed_hz;
>          spi->mode               |= lookup.mode;
>
> Regards,
> Amit
>   

Hi,

I no longer see that error, or any errors printed from SPI, however, all 
of the transactions are now broken.
Every transaction seems to read back 0x0, on all SPI devices, whether 
native CS or GPIO CS, and I dont see it toggling the CS GPIO anymore.

Thanks,

Stefan

>> Thanks,
>>
>> Stefan
>>
>>>> Hi,
>>>>
>>>> I still see the same error:
>>>>
>>>> [    2.748546] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use
>>> Thank you for the quick testing. For further analysis I have
>>> incorporated additional debug prints on top of 1/8 patch. The
>>> corresponding diff is shared below. Kindly implement the specified
>>> changes, rerun your test and share the kernel logs.
>>>
>>> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index
>>> ff66147ba95f..7f59ea81593d 100644
>>> --- a/drivers/spi/spi.c
>>> +++ b/drivers/spi/spi.c
>>> @@ -618,8 +618,10 @@ static int spi_dev_check(struct device *dev, void
>> *data)
>>>           if (spi->controller == new_spi->controller) {
>>>                   for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>                           cs = spi_get_chipselect(spi, idx);
>>> +                       printk("%s() [%d] CS[%d] = [%d]\n", __func__,
>>> + __LINE__, idx, cs);
>>>                           for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
>>> -                               cs_nw = spi_get_chipselect(spi, nw_idx);
>>> +                               cs_nw = spi_get_chipselect(new_spi, nw_idx);
>>> +                               printk("%s() [%d] CS_NEW[%d] =
>>> + [%d]\n", __func__, __LINE__, nw_idx, cs_nw);
>>>                                   if (cs != 0xFF && cs_nw != 0xFF && cs == cs_nw) {
>>>                                           dev_err(dev, "chipselect %d already in use\n",
>> cs_nw);
>>>                                           return -EBUSY; @@ -659,8
>>> +661,10 @@ static int __spi_add_device(struct spi_device *spi)
>>>            */
>>>           for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>                   cs = spi_get_chipselect(spi, idx);
>>> +               printk("%s() [%d] CS[%d] = [%d]\n", __func__,
>>> + __LINE__, idx, cs);
>>>                   for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
>>>                           nw_cs = spi_get_chipselect(spi, nw_idx);
>>> +                       printk("%s() [%d] CS_NEW[%d] = [%d]\n",
>>> + __func__, __LINE__, nw_idx, nw_cs);
>>>                           if (cs != 0xFF && nw_cs != 0xFF && cs == nw_cs) {
>>>                                   dev_err(dev, "chipselect %d already in use\n", nw_cs);
>>>                                   return -EBUSY; @@ -2401,6 +2405,9 @@
>>> static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
>>>           for (idx = 0; idx < rc; idx++)
>>>                   spi_set_chipselect(spi, idx, cs[idx]);
>>>
>>> +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
>>> +               printk("%s() [%d] CS[%d] = [%d]\n", __func__,
>>> + __LINE__, idx, spi_get_chipselect(spi, idx));
>>> +
>>>           /* spi->chip_select[i] gives the corresponding physical CS for logical CS i
>>>            * logical CS number is represented by setting the ith bit in spi-
>>> cs_index_mask
>>>            * So, for example, if spi->cs_index_mask = 0x01 then logical
>>> CS number is 0 and
>>>
>>> Regards,
>>> Amit
>>>
>>>> Thanks,
>>>>
>>>> Stefan
>>>>
>>>>>>> +				if (cs != 0xFF && cs_nw != 0xFF && cs
>> ==
>>>>>> cs_nw) {
>>>>>>> +					dev_err(dev, "chipselect %d
>> already in
>>>>>> use\n", cs_nw);
>>>>>>> +					return -EBUSY;
>>>>>>> +				}
>>>>>>> +			}
>>>>>>> +		}
>>>>>>> +	}
>>>>>>>      	return 0;
>>>>>>>      }
>>>>>>>
>>>>>>> @@ -629,13 +640,32 @@ static int __spi_add_device(struct
>>>>>>> spi_device
>>>> *spi)
>>>>>>>      {
>>>>>>>      	struct spi_controller *ctlr = spi->controller;
>>>>>>>      	struct device *dev = ctlr->dev.parent;
>>>>>>> -	int status;
>>>>>>> +	int status, idx, nw_idx;
>>>>>>> +	u8 cs, nw_cs;
>>>>>>> +
>>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>>>>> +		/* Chipselects are numbered 0..max; validate. */
>>>>>>> +		cs = spi_get_chipselect(spi, idx);
>>>>>>> +		if (cs != 0xFF && cs >= ctlr->num_chipselect) {
>>>>>>> +			dev_err(dev, "cs%d >= max %d\n",
>>>>>> spi_get_chipselect(spi, idx),
>>>>>>> +				ctlr->num_chipselect);
>>>>>>> +			return -EINVAL;
>>>>>>> +		}
>>>>>>> +	}
>>>>>>>
>>>>>>> -	/* Chipselects are numbered 0..max; validate. */
>>>>>>> -	if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
>>>>>>> -		dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0),
>>>>>>> -			ctlr->num_chipselect);
>>>>>>> -		return -EINVAL;
>>>>>>> +	/*
>>>>>>> +	 * Make sure that multiple logical CS doesn't map to the same
>>>>>> physical CS.
>>>>>>> +	 * For example, spi->chip_select[0] != spi->chip_select[1] and
>> so on.
>>>>>>> +	 */
>>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>>>>> +		cs = spi_get_chipselect(spi, idx);
>>>>>>> +		for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX;
>> nw_idx++) {
>>>>>>> +			nw_cs = spi_get_chipselect(spi, nw_idx);
>>>>>>> +			if (cs != 0xFF && nw_cs != 0xFF && cs ==
>> nw_cs) {
>>>>>>> +				dev_err(dev, "chipselect %d already in
>> use\n",
>>>>>> nw_cs);
>>>>>>> +				return -EBUSY;
>>>>>>> +			}
>>>>>>> +		}
>>>>>>>      	}
>>>>>>>
>>>>>>>      	/* Set the bus ID string */
>>>>>>> @@ -647,11 +677,8 @@ static int __spi_add_device(struct spi_device
>>>> *spi)
>>>>>>>      	 * its configuration.
>>>>>>>      	 */
>>>>>>>      	status = bus_for_each_dev(&spi_bus_type, NULL, spi,
>> spi_dev_check);
>>>>>>> -	if (status) {
>>>>>>> -		dev_err(dev, "chipselect %d already in use\n",
>>>>>>> -				spi_get_chipselect(spi, 0));
>>>>>>> +	if (status)
>>>>>>>      		return status;
>>>>>>> -	}
>>>>>>>
>>>>>>>      	/* Controller may unregister concurrently */
>>>>>>>      	if (IS_ENABLED(CONFIG_SPI_DYNAMIC) && @@ -659,8
>> +686,15 @@
>>>>>> static
>>>>>>> int __spi_add_device(struct spi_device *spi)
>>>>>>>      		return -ENODEV;
>>>>>>>      	}
>>>>>>>
>>>>>>> -	if (ctlr->cs_gpiods)
>>>>>>> -		spi_set_csgpiod(spi, 0, ctlr->cs_gpiods[spi_get_chipselect(spi,
>>>>>> 0)]);
>>>>>>> +	if (ctlr->cs_gpiods) {
>>>>>>> +		u8 cs;
>>>>>>> +
>>>>>>> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>>>>> +			cs = spi_get_chipselect(spi, idx);
>>>>>>> +			if (cs != 0xFF)
>>>>>>> +				spi_set_csgpiod(spi, idx, ctlr-
>>> cs_gpiods[cs]);
>>>>>>> +		}
>>>>>>> +	}
>>>>>>>
>>>>>>>      	/*
>>>>>>>      	 * Drivers may modify this initial i/o setup, but will @@
>>>>>>> -701,6
>>>>>>> +735,9 @@ int spi_add_device(struct spi_device *spi)
>>>>>>>      	struct spi_controller *ctlr = spi->controller;
>>>>>>>      	int status;
>>>>>>>
>>>>>>> +	/* Set the bus ID string */
>>>>>>> +	spi_dev_set_name(spi);
>>>>>>> +
>>>>>>>      	mutex_lock(&ctlr->add_lock);
>>>>>>>      	status = __spi_add_device(spi);
>>>>>>>      	mutex_unlock(&ctlr->add_lock); @@ -942,32 +979,51 @@
>> static
>>>>>>> void spi_res_release(struct spi_controller
>>>>>> *ctlr, struct spi_message *mes
>>>>>>>      }
>>>>>>>
>>>>>>>
>>>>>>> /*----------------------------------------------------------------
>>>>>>> --
>>>>>>> --
>>>>>>> -----*/
>>>>>>> +static inline bool spi_is_last_cs(struct spi_device *spi) {
>>>>>>> +	u8 idx;
>>>>>>> +	bool last = false;
>>>>>>> +
>>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>>>>> +		if ((spi->cs_index_mask >> idx) & 0x01) {
>>>>>>> +			if (spi->controller->last_cs[idx] ==
>>>>>> spi_get_chipselect(spi, idx))
>>>>>>> +				last = true;
>>>>>>> +		}
>>>>>>> +	}
>>>>>>> +	return last;
>>>>>>> +}
>>>>>>> +
>>>>>>>
>>>>>>>      static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
>>>>>>>      {
>>>>>>>      	bool activate = enable;
>>>>>>> +	u8 idx;
>>>>>>>
>>>>>>>      	/*
>>>>>>>      	 * Avoid calling into the driver (or doing delays) if the chip
>> select
>>>>>>>      	 * isn't actually changing from the last time this was called.
>>>>>>>      	 */
>>>>>>> -	if (!force && ((enable && spi->controller->last_cs ==
>>>>>> spi_get_chipselect(spi, 0)) ||
>>>>>>> -		       (!enable && spi->controller->last_cs !=
>>>>>> spi_get_chipselect(spi, 0))) &&
>>>>>>> +	if (!force && ((enable && spi->controller->last_cs_index_mask
>> ==
>>>>>>> +spi-
>>>>>>> cs_index_mask &&
>>>>>>> +			spi_is_last_cs(spi)) ||
>>>>>>> +		       (!enable && spi->controller->last_cs_index_mask
>> == spi-
>>>>>>> cs_index_mask &&
>>>>>>> +			!spi_is_last_cs(spi))) &&
>>>>>>>      	    (spi->controller->last_cs_mode_high == (spi->mode &
>>>>>> SPI_CS_HIGH)))
>>>>>>>      		return;
>>>>>>>
>>>>>>>      	trace_spi_set_cs(spi, activate);
>>>>>>>
>>>>>>> -	spi->controller->last_cs = enable ? spi_get_chipselect(spi, 0) : -1;
>>>>>>> +	spi->controller->last_cs_index_mask = spi->cs_index_mask;
>>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
>>>>>>> +		spi->controller->last_cs[idx] = enable ?
>>>>>>> +spi_get_chipselect(spi,
>>>>>> 0)
>>>>>>> +: -1;
>>>>>>>      	spi->controller->last_cs_mode_high = spi->mode &
>> SPI_CS_HIGH;
>>>>>>> -	if ((spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) &&
>>>>>> !activate)
>>>>>>> -		spi_delay_exec(&spi->cs_hold, NULL);
>>>>>>> -
>>>>>>>      	if (spi->mode & SPI_CS_HIGH)
>>>>>>>      		enable = !enable;
>>>>>>>
>>>>>>> -	if (spi_get_csgpiod(spi, 0)) {
>>>>>>> +	if (spi_is_csgpiod(spi)) {
>>>>>>> +		if (!spi->controller->set_cs_timing && !activate)
>>>>>>> +			spi_delay_exec(&spi->cs_hold, NULL);
>>>>>>> +
>>>>>>>      		if (!(spi->mode & SPI_NO_CS)) {
>>>>>>>      			/*
>>>>>>>      			 * Historically ACPI has no means of the GPIO
>> polarity
>>>>>> and @@
>>>>>>> -979,26 +1035,38 @@ static void spi_set_cs(struct spi_device *spi,
>>>>>>> bool
>>>>>> enable, bool force)
>>>>>>>      			 * ambiguity. That's why we use enable, that
>> takes
>>>>>> SPI_CS_HIGH
>>>>>>>      			 * into account.
>>>>>>>      			 */
>>>>>>> -			if (has_acpi_companion(&spi->dev))
>>>>>>> -
>>>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), !enable);
>>>>>>> -			else
>>>>>>> -				/* Polarity handled by GPIO library */
>>>>>>> -
>>>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), activate);
>>>>>>> +			for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>>>>> +				if (((spi->cs_index_mask >> idx) &
>> 0x01) &&
>>>>>>> +				    spi_get_csgpiod(spi, idx)) {
>>>>>>> +					if (has_acpi_companion(&spi-
>>> dev))
>>>>>>> +
>>>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
>>>>>>> +
>>>>>> !enable);
>>>>>>> +					else
>>>>>>> +						/* Polarity handled by
>> GPIO
>>>>>> library */
>>>>>>> +
>>>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
>>>>>>> +
>>>>>> activate);
>>>>>>> +
>>>>>>> +					if (activate)
>>>>>>> +						spi_delay_exec(&spi-
>>>>>>> cs_setup, NULL);
>>>>>>> +					else
>>>>>>> +						spi_delay_exec(&spi-
>>>>>>> cs_inactive, NULL);
>>>>>>> +				}
>>>>>>> +			}
>>>>>>>      		}
>>>>>>>      		/* Some SPI masters need both GPIO CS &
>> slave_select */
>>>>>>>      		if ((spi->controller->flags &
>> SPI_CONTROLLER_GPIO_SS) &&
>>>>>>>      		    spi->controller->set_cs)
>>>>>>>      			spi->controller->set_cs(spi, !enable);
>>>>>>> +
>>>>>>> +		if (!spi->controller->set_cs_timing) {
>>>>>>> +			if (activate)
>>>>>>> +				spi_delay_exec(&spi->cs_setup,
>> NULL);
>>>>>>> +			else
>>>>>>> +				spi_delay_exec(&spi->cs_inactive,
>> NULL);
>>>>>>> +		}
>>>>>>>      	} else if (spi->controller->set_cs) {
>>>>>>>      		spi->controller->set_cs(spi, !enable);
>>>>>>>      	}
>>>>>>> -
>>>>>>> -	if (spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) {
>>>>>>> -		if (activate)
>>>>>>> -			spi_delay_exec(&spi->cs_setup, NULL);
>>>>>>> -		else
>>>>>>> -			spi_delay_exec(&spi->cs_inactive, NULL);
>>>>>>> -	}
>>>>>>>      }
>>>>>>>
>>>>>>>      #ifdef CONFIG_HAS_DMA
>>>>>>> @@ -2222,8 +2290,8 @@ static void of_spi_parse_dt_cs_delay(struct
>>>>>> device_node *nc,
>>>>>>>      static int of_spi_parse_dt(struct spi_controller *ctlr, struct
>>>>>>> spi_device
>>>> *spi,
>>>>>>>      			   struct device_node *nc)
>>>>>>>      {
>>>>>>> -	u32 value;
>>>>>>> -	int rc;
>>>>>>> +	u32 value, cs[SPI_CS_CNT_MAX];
>>>>>>> +	int rc, idx;
>>>>>>>
>>>>>>>      	/* Mode (clock phase/polarity/etc.) */
>>>>>>>      	if (of_property_read_bool(nc, "spi-cpha")) @@ -2295,14
>>>>>>> +2363,52
>>>>>> @@
>>>>>>> static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device
>> *spi,
>>>>>>>      		return 0;
>>>>>>>      	}
>>>>>>>
>>>>>>> +	if (ctlr->num_chipselect > SPI_CS_CNT_MAX) {
>>>>>>> +		dev_err(&ctlr->dev, "No. of CS is more than max. no.
>> of
>>>>>> supported CS\n");
>>>>>>> +		return -EINVAL;
>>>>>>> +	}
>>>>>>> +
>>>>>>> +	/*
>>>>>>> +	 * Zero(0) is a valid physical CS value and can be located at
>> any
>>>>>>> +	 * logical CS in the spi->chip_select[]. If all the physical CS
>>>>>>> +	 * are initialized to 0 then It would be difficult to differentiate
>>>>>>> +	 * between a valid physical CS 0 & an unused logical CS whose
>>>>>> physical
>>>>>>> +	 * CS can be 0. As a solution to this issue initialize all the CS to
>> 0xFF.
>>>>>>> +	 * Now all the unused logical CS will have 0xFF physical CS
>>>>>>> +value & can
>>>>>> be
>>>>>>> +	 * ignore while performing physical CS validity checks.
>>>>>>> +	 */
>>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
>>>>>>> +		spi_set_chipselect(spi, idx, 0xFF);
>>>>>>> +
>>>>>>>      	/* Device address */
>>>>>>> -	rc = of_property_read_u32(nc, "reg", &value);
>>>>>>> -	if (rc) {
>>>>>>> +	rc = of_property_read_variable_u32_array(nc, "reg", &cs[0],
>> 1,
>>>>>>> +						 SPI_CS_CNT_MAX);
>>>>>>> +	if (rc < 0) {
>>>>>>>      		dev_err(&ctlr->dev, "%pOF has no valid 'reg' property
>>>>>> (%d)\n",
>>>>>>>      			nc, rc);
>>>>>>>      		return rc;
>>>>>>>      	}
>>>>>>> -	spi_set_chipselect(spi, 0, value);
>>>>>>> +	if (rc > ctlr->num_chipselect) {
>>>>>>> +		dev_err(&ctlr->dev, "%pOF has number of CS > ctlr-
>>>>>>> num_chipselect (%d)\n",
>>>>>>> +			nc, rc);
>>>>>>> +		return rc;
>>>>>>> +	}
>>>>>>> +	if ((of_property_read_bool(nc, "parallel-memories")) &&
>>>>>>> +	    (!(ctlr->flags & SPI_CONTROLLER_MULTI_CS))) {
>>>>>>> +		dev_err(&ctlr->dev, "SPI controller doesn't support
>> multi
>>>>>> CS\n");
>>>>>>> +		return -EINVAL;
>>>>>>> +	}
>>>>>>> +	for (idx = 0; idx < rc; idx++)
>>>>>>> +		spi_set_chipselect(spi, idx, cs[idx]);
>>>>>>> +
>>>>>>> +	/* spi->chip_select[i] gives the corresponding physical CS for
>>>>>>> +logical CS
>>>>>> i
>>>>>>> +	 * logical CS number is represented by setting the ith bit in
>>>>>>> +spi-
>>>>>>> cs_index_mask
>>>>>>> +	 * So, for example, if spi->cs_index_mask = 0x01 then logical
>> CS
>>>>>> number is 0 and
>>>>>>> +	 * spi->chip_select[0] will give the physical CS.
>>>>>>> +	 * By default spi->chip_select[0] will hold the physical CS
>>>>>>> +number so,
>>>>>> set
>>>>>>> +	 * spi->cs_index_mask as 0x01.
>>>>>>> +	 */
>>>>>>> +	spi->cs_index_mask = 0x01;
>>>>>>>
>>>>>>>      	/* Device speed */
>>>>>>>      	if (!of_property_read_u32(nc, "spi-max-frequency", &value))
>>>>>>> @@
>>>>>>> -3100,6 +3206,7 @@ int spi_register_controller(struct spi_controller
>> *ctlr)
>>>>>>>      	struct boardinfo	*bi;
>>>>>>>      	int			first_dynamic;
>>>>>>>      	int			status;
>>>>>>> +	int			idx;
>>>>>>>
>>>>>>>      	if (!dev)
>>>>>>>      		return -ENODEV;
>>>>>>> @@ -3164,7 +3271,8 @@ int spi_register_controller(struct
>>>>>>> spi_controller
>>>>>> *ctlr)
>>>>>>>      	}
>>>>>>>
>>>>>>>      	/* Setting last_cs to -1 means no chip selected */
>>>>>>> -	ctlr->last_cs = -1;
>>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
>>>>>>> +		ctlr->last_cs[idx] = -1;
>>>>>>>
>>>>>>>      	status = device_add(&ctlr->dev);
>>>>>>>      	if (status < 0)
>>>>>>> @@ -3889,7 +3997,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_get_csgpiod(spi, 0))) {
>>>>>>> +					  spi_is_csgpiod(spi))) {
>>>>>>>      		size_t maxsize = BITS_TO_BYTES(spi->bits_per_word);
>>>>>>>      		int ret;
>>>>>>>
>>>>>>> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
>>>>>>> index 7b4baff63c5c..871d3a6d879b 100644
>>>>>>> --- a/include/linux/spi/spi.h
>>>>>>> +++ b/include/linux/spi/spi.h
>>>>>>> @@ -20,6 +20,9 @@
>>>>>>>
>>>>>>>      #include <uapi/linux/spi/spi.h>
>>>>>>>
>>>>>>> +/* Max no. of CS supported per spi device */ #define
>>>>>>> +SPI_CS_CNT_MAX
>>>>>>> +4
>>>>>>> +
>>>>>>>      struct dma_chan;
>>>>>>>      struct software_node;
>>>>>>>      struct ptp_system_timestamp;
>>>>>>> @@ -132,7 +135,8 @@ extern void
>>>>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
>>>>>>>       * @max_speed_hz: Maximum clock rate to be used with this chip
>>>>>>>       *	(on this board); may be changed by the device's driver.
>>>>>>>       *	The spi_transfer.speed_hz can override this for each transfer.
>>>>>>> - * @chip_select: Chipselect, distinguishing chips handled by
>> @controller.
>>>>>>> + * @chip_select: Array of physical chipselect, spi->chipselect[i] gives
>>>>>>> + *	the corresponding physical CS for logical CS i.
>>>>>>>       * @mode: The spi mode defines how data is clocked out and in.
>>>>>>>       *	This may be changed by the device's driver.
>>>>>>>       *	The "active low" default for chipselect mode can be
>> overridden
>>>>>>> @@ -157,8 +161,8 @@ extern void
>>>>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
>>>>>>>       *	the device will bind to the named driver and only the named
>> driver.
>>>>>>>       *	Do not set directly, because core frees it; use
>> driver_set_override() to
>>>>>>>       *	set or clear it.
>>>>>>> - * @cs_gpiod: GPIO descriptor of the chipselect line (optional,
>>>>>>> NULL
>>>> when
>>>>>>> - *	not using a GPIO line)
>>>>>>> + * @cs_gpiod: Array of GPIO descriptors of the corresponding
>>>>>>> + chipselect
>>>>>> lines
>>>>>>> + *	(optional, NULL when not using a GPIO line)
>>>>>>>       * @word_delay: delay to be inserted between consecutive
>>>>>>>       *	words of a transfer
>>>>>>>       * @cs_setup: delay to be introduced by the controller after
>>>>>>> CS is asserted @@ -167,6 +171,7 @@ extern void
>>>>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
>>>>>>>       *	deasserted. If @cs_change_delay is used from @spi_transfer,
>> then
>>>>>> the
>>>>>>>       *	two delays will be added up.
>>>>>>>       * @pcpu_statistics: statistics for the spi_device
>>>>>>> + * @cs_index_mask: Bit mask of the active chipselect(s) in the
>>>>>>> + chipselect array
>>>>>>>       *
>>>>>>>       * A @spi_device is used to interchange data between an SPI slave
>>>>>>>       * (usually a discrete chip) and CPU memory.
>>>>>>> @@ -182,7 +187,7 @@ struct spi_device {
>>>>>>>      	struct spi_controller	*controller;
>>>>>>>      	struct spi_controller	*master;	/* Compatibility layer
>> */
>>>>>>>      	u32			max_speed_hz;
>>>>>>> -	u8			chip_select;
>>>>>>> +	u8			chip_select[SPI_CS_CNT_MAX];
>>>>>>>      	u8			bits_per_word;
>>>>>>>      	bool			rt;
>>>>>>>      #define SPI_NO_TX		BIT(31)		/* No transmit wire */
>>>>>>> @@ -213,7 +218,7 @@ struct spi_device {
>>>>>>>      	void			*controller_data;
>>>>>>>      	char			modalias[SPI_NAME_SIZE];
>>>>>>>      	const char		*driver_override;
>>>>>>> -	struct gpio_desc	*cs_gpiod;	/* Chip select GPIO descriptor
>>>>>> */
>>>>>>> +	struct gpio_desc	*cs_gpiod[SPI_CS_CNT_MAX];	/*
>> Chip select
>>>>>> gpio desc */
>>>>>>>      	struct spi_delay	word_delay; /* Inter-word delay */
>>>>>>>      	/* CS delays */
>>>>>>>      	struct spi_delay	cs_setup;
>>>>>>> @@ -223,6 +228,13 @@ struct spi_device {
>>>>>>>      	/* The statistics */
>>>>>>>      	struct spi_statistics __percpu	*pcpu_statistics;
>>>>>>>
>>>>>>> +	/* Bit mask of the chipselect(s) that the driver need to use
>> from
>>>>>>> +	 * the chipselect array.When the controller is capable to
>> handle
>>>>>>> +	 * multiple chip selects & memories are connected in parallel
>>>>>>> +	 * then more than one bit need to be set in cs_index_mask.
>>>>>>> +	 */
>>>>>>> +	u32			cs_index_mask : SPI_CS_CNT_MAX;
>>>>>>> +
>>>>>>>      	/*
>>>>>>>      	 * Likely need more hooks for more protocol options affecting
>> how
>>>>>>>      	 * the controller talks to each chip, like:
>>>>>>> @@ -279,22 +291,33 @@ static inline void *spi_get_drvdata(const
>>>>>>> struct spi_device *spi)
>>>>>>>
>>>>>>>      static inline u8 spi_get_chipselect(const struct spi_device *spi, u8 idx)
>>>>>>>      {
>>>>>>> -	return spi->chip_select;
>>>>>>> +	return spi->chip_select[idx];
>>>>>>>      }
>>>>>>>
>>>>>>>      static inline void spi_set_chipselect(struct spi_device *spi,
>>>>>>> u8 idx, u8
>>>>>> chipselect)
>>>>>>>      {
>>>>>>> -	spi->chip_select = chipselect;
>>>>>>> +	spi->chip_select[idx] = chipselect;
>>>>>>>      }
>>>>>>>
>>>>>>>      static inline struct gpio_desc *spi_get_csgpiod(const struct
>>>>>>> spi_device *spi,
>>>>>> u8 idx)
>>>>>>>      {
>>>>>>> -	return spi->cs_gpiod;
>>>>>>> +	return spi->cs_gpiod[idx];
>>>>>>>      }
>>>>>>>
>>>>>>>      static inline void spi_set_csgpiod(struct spi_device *spi, u8
>>>>>>> idx, struct
>>>>>> gpio_desc *csgpiod)
>>>>>>>      {
>>>>>>> -	spi->cs_gpiod = csgpiod;
>>>>>>> +	spi->cs_gpiod[idx] = csgpiod;
>>>>>>> +}
>>>>>>> +
>>>>>>> +static inline bool spi_is_csgpiod(struct spi_device *spi) {
>>>>>>> +	u8 idx;
>>>>>>> +
>>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>>>>>>> +		if (spi_get_csgpiod(spi, idx))
>>>>>>> +			return true;
>>>>>>> +	}
>>>>>>> +	return false;
>>>>>>>      }
>>>>>>>
>>>>>>>      /**
>>>>>>> @@ -399,6 +422,8 @@ extern struct spi_device
>>>>>> *spi_new_ancillary_device(struct spi_device *spi, u8 ch
>>>>>>>       * @bus_lock_spinlock: spinlock for SPI bus locking
>>>>>>>       * @bus_lock_mutex: mutex for exclusion of multiple callers
>>>>>>>       * @bus_lock_flag: indicates that the SPI bus is locked for
>>>>>>> exclusive use
>>>>>>> + * @multi_cs_cap: indicates that the SPI Controller can assert/de-
>> assert
>>>>>>> + *	more than one chip select at once.
>>>>>>>       * @setup: updates the device mode and clocking records used by a
>>>>>>>       *	device's SPI controller; protocol code may call this.  This
>>>>>>>       *	must fail if an unrecognized or unsupported mode is
>> requested.
>>>>>>> @@ -567,6 +592,11 @@ struct spi_controller {
>>>>>>>      #define SPI_CONTROLLER_MUST_TX		BIT(4)	/* Requires tx
>>>> */
>>>>>>>      #define SPI_CONTROLLER_GPIO_SS		BIT(5)	/* GPIO CS
>>>> must
>>>>>> select slave */
>>>>>>>      #define SPI_CONTROLLER_SUSPENDED	BIT(6)	/* Currently
>>>>>> suspended */
>>>>>>> +	/*
>>>>>>> +	 * The spi-controller has multi chip select capability and can
>>>>>>> +	 * assert/de-assert more than one chip select at once.
>>>>>>> +	 */
>>>>>>> +#define SPI_CONTROLLER_MULTI_CS		BIT(7)
>>>>>>>
>>>>>>>      	/* Flag indicating if the allocation of this struct is devres-
>> managed */
>>>>>>>      	bool			devm_allocated;
>>>>>>> @@ -677,7 +707,8 @@ struct spi_controller {
>>>>>>>      	bool				rt;
>>>>>>>      	bool				auto_runtime_pm;
>>>>>>>      	bool				cur_msg_mapped;
>>>>>>> -	char				last_cs;
>>>>>>> +	char				last_cs[SPI_CS_CNT_MAX];
>>>>>>> +	char				last_cs_index_mask;
>>>>>>>      	bool				last_cs_mode_high;
>>>>>>>      	bool                            fallback;
>>>>>>>      	struct completion               xfer_completion;
Mahapatra, Amit Kumar Nov. 22, 2023, 1:22 p.m. UTC | #10
> -----Original Message-----
> From: Stefan Binding <sbinding@opensource.cirrus.com>
> Sent: Wednesday, November 22, 2023 4:51 PM
> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> michael@walle.cc; linux-mtd@lists.infradead.org;
> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>; linux-
> arm-kernel@lists.infradead.org; git (AMD-Xilinx) <git@amd.com>;
> amitrkcian2002@gmail.com; patches@opensource.cirrus.com
> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in SPI core
> 
> 
> On 21/11/2023 19:18, Mahapatra, Amit Kumar wrote:
> > Hello Stefan,
> >
> >> -----Original Message-----
> >> From: Stefan Binding <sbinding@opensource.cirrus.com>
> >> Sent: Tuesday, November 21, 2023 11:07 PM
> >> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
> >> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
> >> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
> >> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> >> michael@walle.cc; linux-mtd@lists.infradead.org;
> >> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> >> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>;
> >> linux- arm-kernel@lists.infradead.org; git (AMD-Xilinx)
> >> <git@amd.com>; amitrkcian2002@gmail.com;
> >> patches@opensource.cirrus.com
> >> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in
> >> SPI core
> >>
> >>
> >> On 21/11/2023 16:35, Mahapatra, Amit Kumar wrote:
> >>> Hello Stefan,
> >>>
> >>>> -----Original Message-----
> >>>> From: Stefan Binding <sbinding@opensource.cirrus.com>
> >>>> Sent: Tuesday, November 21, 2023 7:28 PM
> >>>> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
> >>>> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
> >>>> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
> >>>> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> >>>> michael@walle.cc; linux-mtd@lists.infradead.org;
> >>>> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> >>>> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>;
> >>>> linux- arm-kernel@lists.infradead.org; git (AMD-Xilinx)
> >>>> <git@amd.com>; amitrkcian2002@gmail.com;
> >>>> patches@opensource.cirrus.com
> >>>> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in
> >>>> SPI core
> >>>>
> >>>>
> >>>> On 21/11/2023 07:39, Mahapatra, Amit Kumar wrote:
> >>>>> Hello Stefan,
> >>>>>
> >>>>>> -----Original Message-----
> >>>>>> From: Stefan Binding <sbinding@opensource.cirrus.com>
> >>>>>> Sent: Monday, November 20, 2023 8:00 PM
> >>>>>> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
> >>>>>> broonie@kernel.org; tudor.ambarus@linaro.org;
> >>>>>> pratyush@kernel.org; miquel.raynal@bootlin.com; richard@nod.at;
> >>>>>> vigneshr@ti.com
> >>>>>> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> >>>>>> michael@walle.cc; linux-mtd@lists.infradead.org;
> >>>>>> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> >>>>>> claudiu.beznea@tuxon.dev; Simek, Michal
> <michal.simek@amd.com>;
> >>>>>> linux- arm-kernel@lists.infradead.org; git (AMD-Xilinx)
> >>>>>> <git@amd.com>; amitrkcian2002@gmail.com;
> >>>>>> patches@opensource.cirrus.com
> >>>>>> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support
> >>>>>> in SPI core
> >>>>>>
> >>>>>>
> >>>>>> On 18/11/2023 13:54, Amit Kumar Mahapatra wrote:
> >>>>>>> AMD-Xilinx GQSPI controller has two advanced mode that allows
> >>>>>>> the controller to consider two flashes as one single device.
> >>>>>>>
> >>>>>>> One of these two mode is the parallel mode in which each byte of
> >>>>>>> data is stored in both devices, the even bits in the lower flash
> >>>>>>> & the odd bits in the upper flash. The byte split is
> >>>>>>> automatically handled by the QSPI controller.
> >>>>>>>
> >>>>>>> The other mode is the stacked mode in which both the flashes
> >>>>>>> share the same SPI bus but each of the device contain half of the
> data.
> >>>>>>> In this mode, the controller does not follow CS requests but
> >>>>>>> instead internally wires the two CS levels with the value of the
> >>>>>>> most significant
> >>>>>> address bit.
> >>>>>>> For supporting both these modes SPI core need to be updated for
> >>>>>>> providing multiple CS for a single SPI device.
> >>>>>>>
> >>>>>>> For adding multi CS support the SPI device need to be aware of
> >>>>>>> all the CS values. So, the "chip_select" member in the
> >>>>>>> spi_device structure is now an array that holds all the CS values.
> >>>>>>>
> >>>>>>> spi_device structure now has a "cs_index_mask" member. This acts
> >>>>>>> as an index to the chip_select array. If nth bit of
> >>>>>>> spi->cs_index_mask is set then the driver would assert spi-
> >>> chip_select[n].
> >>>>>>> In parallel mode all the chip selects are asserted/de-asserted
> >>>>>>> simultaneously and each byte of data is stored in both devices,
> >>>>>>> the even bits in one, the odd bits in the other. The split is
> >>>>>>> automatically handled by the GQSPI controller. The GQSPI
> >>>>>>> controller supports a maximum of two flashes connected in
> >>>>>>> parallel mode. A SPI_CONTROLLER_MULTI_CS flag bit is added in
> >>>>>>> the spi controller flags, through ctlr->flags the spi core will
> >>>>>>> make sure that the controller is capable of handling multiple chip
> selects at once.
> >>>>>>>
> >>>>>>> For supporting multiple CS via GPIO the cs_gpiod member of the
> >>>>>>> spi_device structure is now an array that holds the gpio
> >>>>>>> descriptor for each chipselect.
> >>>>>>>
> >>>>>>> CS GPIO is not tested due to unavailability of necessary
> >>>>>>> hardware
> >> setup.
> >>>>>>> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-
> >>>> mahapatra@amd.com>
> >>>>>>> ---
> >>>>>>> Hello @Stefen,
> >>>>>>> We restructured the SPI core implementation, for handling
> >>>>>>> arbitrary number of devices connected in parallel(multi-cs) or
> >>>>>>> stacked
> >> mode.
> >>>>>>> We have tested this updated patch on native-cs setup but
> >>>>>>> couldn't test cs-gpio due to unavailability of a proper setup.
> >>>>>>> If possible, could you please retest the cs-gpio use case with
> >>>>>>> this updated patch and provide
> >>>>>> your feedback.
> >>>>>>
> >>>>>> Hi,
> >>>>>>
> >>>>>> I tested this chain on 2 different systems using GPIOs as chip
> >>>>>> selects, and see the same error on both:
> >>>>>> [    2.842045] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in
> >>>>>> use
> >>>>>>
> >>>>>> Let me know if you need any further testing.
> >>>>>>
> >>>>>> Thanks,
> >>>>>>
> >>>>>> Stefan Binding
> >>>>>>
> >>>>>>> ---
> >>>>>>>      drivers/spi/spi.c       | 192 +++++++++++++++++++++++++++++++---
> ----
> >> --
> >>>>>>>      include/linux/spi/spi.h |  51 ++++++++---
> >>>>>>>      2 files changed, 191 insertions(+), 52 deletions(-)
> >>>>>>>
> >>>>>>> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index
> >>>>>>> 8ead7acb99f3..ff66147ba95f 100644
> >>>>>>> --- a/drivers/spi/spi.c
> >>>>>>> +++ b/drivers/spi/spi.c
> >>>>>>> @@ -612,10 +612,21 @@ static int spi_dev_check(struct device
> >>>>>>> *dev, void
> >>>>>> *data)
> >>>>>>>      {
> >>>>>>>      	struct spi_device *spi = to_spi_device(dev);
> >>>>>>>      	struct spi_device *new_spi = data;
> >>>>>>> -
> >>>>>>> -	if (spi->controller == new_spi->controller &&
> >>>>>>> -	    spi_get_chipselect(spi, 0) == spi_get_chipselect(new_spi, 0))
> >>>>>>> -		return -EBUSY;
> >>>>>>> +	int idx, nw_idx;
> >>>>>>> +	u8 cs, cs_nw;
> >>>>>>> +
> >>>>>>> +	if (spi->controller == new_spi->controller) {
> >>>>>>> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>>>>> +			cs = spi_get_chipselect(spi, idx);
> >>>>>>> +			for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX;
> >>>>>> nw_idx++) {
> >>>>>>> +				cs_nw = spi_get_chipselect(spi,
> >> nw_idx);
> >>>>> Thank you for dedicating your time to test my patch.
> >>>>> As per my analysis the error is reported from here as we are
> >>>>> supplying the former SPI device structure to retrieve the CS value
> >>>>> for the new SPI devices.
> >>>>> To fix this, could you kindly substitute the above line with
> >>>>>
> >>>>> cs_nw = spi_get_chipselect(new_spi, nw_idx);
> >>>>>
> >>>>> and rerun your tests?
> >>>>> If it works correctly, I will incorporate this fix into my
> >>>>> upcoming series.
> >>>>>
> >>>>> Regards,
> >>>>> Amit
> >> Hi,
> >>
> >> I've attached my log.
> >> I notice that you add a print to of_spi_parse_dt, however since the
> >> laptop I am using is an x86 laptop, it uses ACPI rather than OF, and
> >> I don't think this function isnt even compiled in.
> > That’s correct, I missed it.
> > Upon reviewing the logs, I discovered that in the ACPI flow, I am not
> > initializing the unused CS[] to FF, as I am doing in the OF flow.
> > Consequently, in the ACPI flow, all the unused CS[] are automatically
> > initialized to 0. During the __spi_add_device process, the SPI core
> > faces difficulty distinguishing between a valid CS value of 0 and an
> > unused CS value of 0, leading to a failure in the driver probe.
> >
> > Incorporating the following modifications should resolve this issue.
> > Kindly apply these changes on top of 1/8 and re-run the test.
> >
> > diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index
> > ff66147ba95f..d0301c17d4f8 100644
> > --- a/drivers/spi/spi.c
> > +++ b/drivers/spi/spi.c
> > @@ -619,7 +619,7 @@ static int spi_dev_check(struct device *dev, void
> *data)
> >                  for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >                          cs = spi_get_chipselect(spi, idx);
> >                          for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
> > -                               cs_nw = spi_get_chipselect(spi, nw_idx);
> > +                               cs_nw = spi_get_chipselect(new_spi,
> > + nw_idx);
> >                                  if (cs != 0xFF && cs_nw != 0xFF && cs == cs_nw) {
> >                                          dev_err(dev, "chipselect %d already in use\n",
> cs_nw);
> >                                          return -EBUSY; @@ -764,6
> > +764,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,
> >   {
> >          struct spi_device       *proxy;
> >          int                     status;
> > +       u8                      idx;
> >
> >          /*
> >           * NOTE:  caller did any chip->bus_num checks necessary.
> > @@ -779,6 +780,9 @@ struct spi_device *spi_new_device(struct
> > spi_controller *ctlr,
> >
> >          WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
> >
> > +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> > +               spi_set_chipselect(proxy, idx, 0xFF);
> > +
> >          spi_set_chipselect(proxy, 0, chip->chip_select);
> >          proxy->max_speed_hz = chip->max_speed_hz;
> >          proxy->mode = chip->mode;
> > @@ -2514,6 +2518,7 @@ struct spi_device
> *spi_new_ancillary_device(struct spi_device *spi,
> >          struct spi_controller *ctlr = spi->controller;
> >          struct spi_device *ancillary;
> >          int rc = 0;
> > +       u8 idx;
> >
> >          /* Alloc an spi_device */
> >          ancillary = spi_alloc_device(ctlr); @@ -2524,6 +2529,9 @@
> > struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
> >
> >          strscpy(ancillary->modalias, "dummy",
> > sizeof(ancillary->modalias));
> >
> > +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> > +               spi_set_chipselect(ancillary, idx, 0xFF);
> > +
> >          /* Use provided chip-select for ancillary device */
> >          spi_set_chipselect(ancillary, 0, chip_select);
> >
> > @@ -2732,6 +2740,7 @@ struct spi_device *acpi_spi_device_alloc(struct
> spi_controller *ctlr,
> >          struct acpi_spi_lookup lookup = {};
> >          struct spi_device *spi;
> >          int ret;
> > +       u8 idx;
> >
> >          if (!ctlr && index == -1)
> >                  return ERR_PTR(-EINVAL); @@ -2767,6 +2776,9 @@ struct
> > spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
> >                  return ERR_PTR(-ENOMEM);
> >          }
> >
> > +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> > +               spi_set_chipselect(spi, idx, 0xFF);
> > +
> >          ACPI_COMPANION_SET(&spi->dev, adev);
> >          spi->max_speed_hz       = lookup.max_speed_hz;
> >          spi->mode               |= lookup.mode;
> >
> > Regards,
> > Amit
> >
> 
> Hi,
> 
> I no longer see that error, or any errors printed from SPI, however, all of the
> transactions are now broken.
> Every transaction seems to read back 0x0, on all SPI devices, whether native
> CS or GPIO CS, and I dont see it toggling the CS GPIO anymore.

I believe the issue stems from the fact that in the ACPI patch, 
spi->cs_index_mask is not set to 0x01. As a result, in spi_set_cs, 
it fails to meet the condition that governs the CS asset code snippet. 
In the following code diff, I've included the necessary fix along with 
the earlier changes on top of 1/8. I've also included a couple of debug 
prints to aid in further debugging in case my analysis is incorrect. 
Please apply these changes on top of 1/8 and re-run the test.

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index ff66147ba95f..b2e035c3d494 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -619,7 +619,7 @@ static int spi_dev_check(struct device *dev, void *data)
                for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
                        cs = spi_get_chipselect(spi, idx);
                        for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
-                               cs_nw = spi_get_chipselect(spi, nw_idx);
+                               cs_nw = spi_get_chipselect(new_spi, nw_idx);
                                if (cs != 0xFF && cs_nw != 0xFF && cs == cs_nw) {
                                        dev_err(dev, "chipselect %d already in use\n", cs_nw);
                                        return -EBUSY;
@@ -764,6 +764,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,
 {
        struct spi_device       *proxy;
        int                     status;
+       u8                      idx;

        /*
         * NOTE:  caller did any chip->bus_num checks necessary.
@@ -779,6 +780,9 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,

        WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));

+       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
+               spi_set_chipselect(proxy, idx, 0xFF);
+
        spi_set_chipselect(proxy, 0, chip->chip_select);
        proxy->max_speed_hz = chip->max_speed_hz;
        proxy->mode = chip->mode;
@@ -787,6 +791,7 @@ struct spi_device *spi_new_device(struct spi_controller *ctlr,
        proxy->dev.platform_data = (void *) chip->platform_data;
        proxy->controller_data = chip->controller_data;
        proxy->controller_state = NULL;
+       proxy->cs_index_mask = 0x01;

        if (chip->swnode) {
                status = device_add_software_node(&proxy->dev, chip->swnode);
@@ -1024,6 +1029,8 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
                if (!spi->controller->set_cs_timing && !activate)
                        spi_delay_exec(&spi->cs_hold, NULL);

+               printk("%s() [%d] spi->cs_index_mask == [%d]\n",__func__, __LINE__, spi->cs_index_mask);
+
                if (!(spi->mode & SPI_NO_CS)) {
                        /*
                         * Historically ACPI has no means of the GPIO polarity and
@@ -1038,6 +1045,7 @@ static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
                        for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
                                if (((spi->cs_index_mask >> idx) & 0x01) &&
                                    spi_get_csgpiod(spi, idx)) {
+                                   printk("%s() [%d] idx == [%d]\n",__func__, __LINE__, idx);
                                        if (has_acpi_companion(&spi->dev))
                                                gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
                                                                         !enable);
@@ -2514,6 +2522,7 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
        struct spi_controller *ctlr = spi->controller;
        struct spi_device *ancillary;
        int rc = 0;
+       u8 idx;

        /* Alloc an spi_device */
        ancillary = spi_alloc_device(ctlr);
@@ -2524,12 +2533,16 @@ struct spi_device *spi_new_ancillary_device(struct spi_device *spi,

        strscpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias));

+        for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
+               spi_set_chipselect(ancillary, idx, 0xFF);
+
        /* Use provided chip-select for ancillary device */
        spi_set_chipselect(ancillary, 0, chip_select);

        /* Take over SPI mode/speed from SPI main device */
        ancillary->max_speed_hz = spi->max_speed_hz;
        ancillary->mode = spi->mode;
+       ancillary->cs_index_mask = 0x01;

        WARN_ON(!mutex_is_locked(&ctlr->add_lock));

@@ -2732,6 +2745,7 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
        struct acpi_spi_lookup lookup = {};
        struct spi_device *spi;
        int ret;
+       u8 idx;

        if (!ctlr && index == -1)
                return ERR_PTR(-EINVAL);
@@ -2767,12 +2781,16 @@ struct spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
                return ERR_PTR(-ENOMEM);
        }

+       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
+               spi_set_chipselect(spi, idx, 0xFF);
+
        ACPI_COMPANION_SET(&spi->dev, adev);
        spi->max_speed_hz       = lookup.max_speed_hz;
        spi->mode               |= lookup.mode;
        spi->irq                = lookup.irq;
        spi->bits_per_word      = lookup.bits_per_word;
        spi_set_chipselect(spi, 0, lookup.chip_select);
+       spi->cs_index_mask      = 0x01;

        return spi;
 }

Regards,
Amit
                                                                                               
> 
> Thanks,
> 
> Stefan
> 
> >> Thanks,
> >>
> >> Stefan
> >>
> >>>> Hi,
> >>>>
> >>>> I still see the same error:
> >>>>
> >>>> [    2.748546] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use
> >>> Thank you for the quick testing. For further analysis I have
> >>> incorporated additional debug prints on top of 1/8 patch. The
> >>> corresponding diff is shared below. Kindly implement the specified
> >>> changes, rerun your test and share the kernel logs.
> >>>
> >>> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index
> >>> ff66147ba95f..7f59ea81593d 100644
> >>> --- a/drivers/spi/spi.c
> >>> +++ b/drivers/spi/spi.c
> >>> @@ -618,8 +618,10 @@ static int spi_dev_check(struct device *dev,
> >>> void
> >> *data)
> >>>           if (spi->controller == new_spi->controller) {
> >>>                   for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>                           cs = spi_get_chipselect(spi, idx);
> >>> +                       printk("%s() [%d] CS[%d] = [%d]\n",
> >>> + __func__, __LINE__, idx, cs);
> >>>                           for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
> >>> -                               cs_nw = spi_get_chipselect(spi, nw_idx);
> >>> +                               cs_nw = spi_get_chipselect(new_spi, nw_idx);
> >>> +                               printk("%s() [%d] CS_NEW[%d] =
> >>> + [%d]\n", __func__, __LINE__, nw_idx, cs_nw);
> >>>                                   if (cs != 0xFF && cs_nw != 0xFF && cs == cs_nw) {
> >>>                                           dev_err(dev, "chipselect
> >>> %d already in use\n",
> >> cs_nw);
> >>>                                           return -EBUSY; @@ -659,8
> >>> +661,10 @@ static int __spi_add_device(struct spi_device *spi)
> >>>            */
> >>>           for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>                   cs = spi_get_chipselect(spi, idx);
> >>> +               printk("%s() [%d] CS[%d] = [%d]\n", __func__,
> >>> + __LINE__, idx, cs);
> >>>                   for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
> >>>                           nw_cs = spi_get_chipselect(spi, nw_idx);
> >>> +                       printk("%s() [%d] CS_NEW[%d] = [%d]\n",
> >>> + __func__, __LINE__, nw_idx, nw_cs);
> >>>                           if (cs != 0xFF && nw_cs != 0xFF && cs == nw_cs) {
> >>>                                   dev_err(dev, "chipselect %d already in use\n", nw_cs);
> >>>                                   return -EBUSY; @@ -2401,6 +2405,9
> >>> @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device
> *spi,
> >>>           for (idx = 0; idx < rc; idx++)
> >>>                   spi_set_chipselect(spi, idx, cs[idx]);
> >>>
> >>> +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> >>> +               printk("%s() [%d] CS[%d] = [%d]\n", __func__,
> >>> + __LINE__, idx, spi_get_chipselect(spi, idx));
> >>> +
> >>>           /* spi->chip_select[i] gives the corresponding physical CS for logical
> CS i
> >>>            * logical CS number is represented by setting the ith bit
> >>> in spi- cs_index_mask
> >>>            * So, for example, if spi->cs_index_mask = 0x01 then
> >>> logical CS number is 0 and
> >>>
> >>> Regards,
> >>> Amit
> >>>
> >>>> Thanks,
> >>>>
> >>>> Stefan
> >>>>
> >>>>>>> +				if (cs != 0xFF && cs_nw != 0xFF && cs
> >> ==
> >>>>>> cs_nw) {
> >>>>>>> +					dev_err(dev, "chipselect %d
> >> already in
> >>>>>> use\n", cs_nw);
> >>>>>>> +					return -EBUSY;
> >>>>>>> +				}
> >>>>>>> +			}
> >>>>>>> +		}
> >>>>>>> +	}
> >>>>>>>      	return 0;
> >>>>>>>      }
> >>>>>>>
> >>>>>>> @@ -629,13 +640,32 @@ static int __spi_add_device(struct
> >>>>>>> spi_device
> >>>> *spi)
> >>>>>>>      {
> >>>>>>>      	struct spi_controller *ctlr = spi->controller;
> >>>>>>>      	struct device *dev = ctlr->dev.parent;
> >>>>>>> -	int status;
> >>>>>>> +	int status, idx, nw_idx;
> >>>>>>> +	u8 cs, nw_cs;
> >>>>>>> +
> >>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>>>>> +		/* Chipselects are numbered 0..max; validate. */
> >>>>>>> +		cs = spi_get_chipselect(spi, idx);
> >>>>>>> +		if (cs != 0xFF && cs >= ctlr->num_chipselect) {
> >>>>>>> +			dev_err(dev, "cs%d >= max %d\n",
> >>>>>> spi_get_chipselect(spi, idx),
> >>>>>>> +				ctlr->num_chipselect);
> >>>>>>> +			return -EINVAL;
> >>>>>>> +		}
> >>>>>>> +	}
> >>>>>>>
> >>>>>>> -	/* Chipselects are numbered 0..max; validate. */
> >>>>>>> -	if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
> >>>>>>> -		dev_err(dev, "cs%d >= max %d\n",
> spi_get_chipselect(spi, 0),
> >>>>>>> -			ctlr->num_chipselect);
> >>>>>>> -		return -EINVAL;
> >>>>>>> +	/*
> >>>>>>> +	 * Make sure that multiple logical CS doesn't map to the same
> >>>>>> physical CS.
> >>>>>>> +	 * For example, spi->chip_select[0] != spi->chip_select[1] and
> >> so on.
> >>>>>>> +	 */
> >>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>>>>> +		cs = spi_get_chipselect(spi, idx);
> >>>>>>> +		for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX;
> >> nw_idx++) {
> >>>>>>> +			nw_cs = spi_get_chipselect(spi, nw_idx);
> >>>>>>> +			if (cs != 0xFF && nw_cs != 0xFF && cs ==
> >> nw_cs) {
> >>>>>>> +				dev_err(dev, "chipselect %d already in
> >> use\n",
> >>>>>> nw_cs);
> >>>>>>> +				return -EBUSY;
> >>>>>>> +			}
> >>>>>>> +		}
> >>>>>>>      	}
> >>>>>>>
> >>>>>>>      	/* Set the bus ID string */ @@ -647,11 +677,8 @@ static
> >>>>>>> int __spi_add_device(struct spi_device
> >>>> *spi)
> >>>>>>>      	 * its configuration.
> >>>>>>>      	 */
> >>>>>>>      	status = bus_for_each_dev(&spi_bus_type, NULL, spi,
> >> spi_dev_check);
> >>>>>>> -	if (status) {
> >>>>>>> -		dev_err(dev, "chipselect %d already in use\n",
> >>>>>>> -				spi_get_chipselect(spi, 0));
> >>>>>>> +	if (status)
> >>>>>>>      		return status;
> >>>>>>> -	}
> >>>>>>>
> >>>>>>>      	/* Controller may unregister concurrently */
> >>>>>>>      	if (IS_ENABLED(CONFIG_SPI_DYNAMIC) && @@ -659,8
> >> +686,15 @@
> >>>>>> static
> >>>>>>> int __spi_add_device(struct spi_device *spi)
> >>>>>>>      		return -ENODEV;
> >>>>>>>      	}
> >>>>>>>
> >>>>>>> -	if (ctlr->cs_gpiods)
> >>>>>>> -		spi_set_csgpiod(spi, 0, ctlr-
> >cs_gpiods[spi_get_chipselect(spi,
> >>>>>> 0)]);
> >>>>>>> +	if (ctlr->cs_gpiods) {
> >>>>>>> +		u8 cs;
> >>>>>>> +
> >>>>>>> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>>>>> +			cs = spi_get_chipselect(spi, idx);
> >>>>>>> +			if (cs != 0xFF)
> >>>>>>> +				spi_set_csgpiod(spi, idx, ctlr-
> >>> cs_gpiods[cs]);
> >>>>>>> +		}
> >>>>>>> +	}
> >>>>>>>
> >>>>>>>      	/*
> >>>>>>>      	 * Drivers may modify this initial i/o setup, but will @@
> >>>>>>> -701,6
> >>>>>>> +735,9 @@ int spi_add_device(struct spi_device *spi)
> >>>>>>>      	struct spi_controller *ctlr = spi->controller;
> >>>>>>>      	int status;
> >>>>>>>
> >>>>>>> +	/* Set the bus ID string */
> >>>>>>> +	spi_dev_set_name(spi);
> >>>>>>> +
> >>>>>>>      	mutex_lock(&ctlr->add_lock);
> >>>>>>>      	status = __spi_add_device(spi);
> >>>>>>>      	mutex_unlock(&ctlr->add_lock); @@ -942,32 +979,51 @@
> >> static
> >>>>>>> void spi_res_release(struct spi_controller
> >>>>>> *ctlr, struct spi_message *mes
> >>>>>>>      }
> >>>>>>>
> >>>>>>>
> >>>>>>> /*--------------------------------------------------------------
> >>>>>>> --
> >>>>>>> --
> >>>>>>> --
> >>>>>>> -----*/
> >>>>>>> +static inline bool spi_is_last_cs(struct spi_device *spi) {
> >>>>>>> +	u8 idx;
> >>>>>>> +	bool last = false;
> >>>>>>> +
> >>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>>>>> +		if ((spi->cs_index_mask >> idx) & 0x01) {
> >>>>>>> +			if (spi->controller->last_cs[idx] ==
> >>>>>> spi_get_chipselect(spi, idx))
> >>>>>>> +				last = true;
> >>>>>>> +		}
> >>>>>>> +	}
> >>>>>>> +	return last;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>>
> >>>>>>>      static void spi_set_cs(struct spi_device *spi, bool enable, bool
> force)
> >>>>>>>      {
> >>>>>>>      	bool activate = enable;
> >>>>>>> +	u8 idx;
> >>>>>>>
> >>>>>>>      	/*
> >>>>>>>      	 * Avoid calling into the driver (or doing delays) if the
> >>>>>>> chip
> >> select
> >>>>>>>      	 * isn't actually changing from the last time this was called.
> >>>>>>>      	 */
> >>>>>>> -	if (!force && ((enable && spi->controller->last_cs ==
> >>>>>> spi_get_chipselect(spi, 0)) ||
> >>>>>>> -		       (!enable && spi->controller->last_cs !=
> >>>>>> spi_get_chipselect(spi, 0))) &&
> >>>>>>> +	if (!force && ((enable && spi->controller->last_cs_index_mask
> >> ==
> >>>>>>> +spi-
> >>>>>>> cs_index_mask &&
> >>>>>>> +			spi_is_last_cs(spi)) ||
> >>>>>>> +		       (!enable && spi->controller->last_cs_index_mask
> >> == spi-
> >>>>>>> cs_index_mask &&
> >>>>>>> +			!spi_is_last_cs(spi))) &&
> >>>>>>>      	    (spi->controller->last_cs_mode_high == (spi->mode &
> >>>>>> SPI_CS_HIGH)))
> >>>>>>>      		return;
> >>>>>>>
> >>>>>>>      	trace_spi_set_cs(spi, activate);
> >>>>>>>
> >>>>>>> -	spi->controller->last_cs = enable ? spi_get_chipselect(spi, 0) : -
> 1;
> >>>>>>> +	spi->controller->last_cs_index_mask = spi->cs_index_mask;
> >>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> >>>>>>> +		spi->controller->last_cs[idx] = enable ?
> >>>>>>> +spi_get_chipselect(spi,
> >>>>>> 0)
> >>>>>>> +: -1;
> >>>>>>>      	spi->controller->last_cs_mode_high = spi->mode &
> >> SPI_CS_HIGH;
> >>>>>>> -	if ((spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing)
> &&
> >>>>>> !activate)
> >>>>>>> -		spi_delay_exec(&spi->cs_hold, NULL);
> >>>>>>> -
> >>>>>>>      	if (spi->mode & SPI_CS_HIGH)
> >>>>>>>      		enable = !enable;
> >>>>>>>
> >>>>>>> -	if (spi_get_csgpiod(spi, 0)) {
> >>>>>>> +	if (spi_is_csgpiod(spi)) {
> >>>>>>> +		if (!spi->controller->set_cs_timing && !activate)
> >>>>>>> +			spi_delay_exec(&spi->cs_hold, NULL);
> >>>>>>> +
> >>>>>>>      		if (!(spi->mode & SPI_NO_CS)) {
> >>>>>>>      			/*
> >>>>>>>      			 * Historically ACPI has no means of the GPIO
> >> polarity
> >>>>>> and @@
> >>>>>>> -979,26 +1035,38 @@ static void spi_set_cs(struct spi_device
> >>>>>>> *spi, bool
> >>>>>> enable, bool force)
> >>>>>>>      			 * ambiguity. That's why we use enable, that
> >> takes
> >>>>>> SPI_CS_HIGH
> >>>>>>>      			 * into account.
> >>>>>>>      			 */
> >>>>>>> -			if (has_acpi_companion(&spi->dev))
> >>>>>>> -
> >>>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), !enable);
> >>>>>>> -			else
> >>>>>>> -				/* Polarity handled by GPIO library */
> >>>>>>> -
> >>>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), activate);
> >>>>>>> +			for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>>>>> +				if (((spi->cs_index_mask >> idx) &
> >> 0x01) &&
> >>>>>>> +				    spi_get_csgpiod(spi, idx)) {
> >>>>>>> +					if (has_acpi_companion(&spi-
> >>> dev))
> >>>>>>> +
> >>>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
> >>>>>>> +
> >>>>>> !enable);
> >>>>>>> +					else
> >>>>>>> +						/* Polarity handled by
> >> GPIO
> >>>>>> library */
> >>>>>>> +
> >>>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
> >>>>>>> +
> >>>>>> activate);
> >>>>>>> +
> >>>>>>> +					if (activate)
> >>>>>>> +						spi_delay_exec(&spi-
> >>>>>>> cs_setup, NULL);
> >>>>>>> +					else
> >>>>>>> +						spi_delay_exec(&spi-
> >>>>>>> cs_inactive, NULL);
> >>>>>>> +				}
> >>>>>>> +			}
> >>>>>>>      		}
> >>>>>>>      		/* Some SPI masters need both GPIO CS &
> >> slave_select */
> >>>>>>>      		if ((spi->controller->flags &
> >> SPI_CONTROLLER_GPIO_SS) &&
> >>>>>>>      		    spi->controller->set_cs)
> >>>>>>>      			spi->controller->set_cs(spi, !enable);
> >>>>>>> +
> >>>>>>> +		if (!spi->controller->set_cs_timing) {
> >>>>>>> +			if (activate)
> >>>>>>> +				spi_delay_exec(&spi->cs_setup,
> >> NULL);
> >>>>>>> +			else
> >>>>>>> +				spi_delay_exec(&spi->cs_inactive,
> >> NULL);
> >>>>>>> +		}
> >>>>>>>      	} else if (spi->controller->set_cs) {
> >>>>>>>      		spi->controller->set_cs(spi, !enable);
> >>>>>>>      	}
> >>>>>>> -
> >>>>>>> -	if (spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) {
> >>>>>>> -		if (activate)
> >>>>>>> -			spi_delay_exec(&spi->cs_setup, NULL);
> >>>>>>> -		else
> >>>>>>> -			spi_delay_exec(&spi->cs_inactive, NULL);
> >>>>>>> -	}
> >>>>>>>      }
> >>>>>>>
> >>>>>>>      #ifdef CONFIG_HAS_DMA
> >>>>>>> @@ -2222,8 +2290,8 @@ static void
> >>>>>>> of_spi_parse_dt_cs_delay(struct
> >>>>>> device_node *nc,
> >>>>>>>      static int of_spi_parse_dt(struct spi_controller *ctlr,
> >>>>>>> struct spi_device
> >>>> *spi,
> >>>>>>>      			   struct device_node *nc)
> >>>>>>>      {
> >>>>>>> -	u32 value;
> >>>>>>> -	int rc;
> >>>>>>> +	u32 value, cs[SPI_CS_CNT_MAX];
> >>>>>>> +	int rc, idx;
> >>>>>>>
> >>>>>>>      	/* Mode (clock phase/polarity/etc.) */
> >>>>>>>      	if (of_property_read_bool(nc, "spi-cpha")) @@ -2295,14
> >>>>>>> +2363,52
> >>>>>> @@
> >>>>>>> static int of_spi_parse_dt(struct spi_controller *ctlr, struct
> >>>>>>> spi_device
> >> *spi,
> >>>>>>>      		return 0;
> >>>>>>>      	}
> >>>>>>>
> >>>>>>> +	if (ctlr->num_chipselect > SPI_CS_CNT_MAX) {
> >>>>>>> +		dev_err(&ctlr->dev, "No. of CS is more than max. no.
> >> of
> >>>>>> supported CS\n");
> >>>>>>> +		return -EINVAL;
> >>>>>>> +	}
> >>>>>>> +
> >>>>>>> +	/*
> >>>>>>> +	 * Zero(0) is a valid physical CS value and can be located at
> >> any
> >>>>>>> +	 * logical CS in the spi->chip_select[]. If all the physical CS
> >>>>>>> +	 * are initialized to 0 then It would be difficult to differentiate
> >>>>>>> +	 * between a valid physical CS 0 & an unused logical CS whose
> >>>>>> physical
> >>>>>>> +	 * CS can be 0. As a solution to this issue initialize all the
> >>>>>>> +CS to
> >> 0xFF.
> >>>>>>> +	 * Now all the unused logical CS will have 0xFF physical CS
> >>>>>>> +value & can
> >>>>>> be
> >>>>>>> +	 * ignore while performing physical CS validity checks.
> >>>>>>> +	 */
> >>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> >>>>>>> +		spi_set_chipselect(spi, idx, 0xFF);
> >>>>>>> +
> >>>>>>>      	/* Device address */
> >>>>>>> -	rc = of_property_read_u32(nc, "reg", &value);
> >>>>>>> -	if (rc) {
> >>>>>>> +	rc = of_property_read_variable_u32_array(nc, "reg", &cs[0],
> >> 1,
> >>>>>>> +						 SPI_CS_CNT_MAX);
> >>>>>>> +	if (rc < 0) {
> >>>>>>>      		dev_err(&ctlr->dev, "%pOF has no valid 'reg' property
> >>>>>> (%d)\n",
> >>>>>>>      			nc, rc);
> >>>>>>>      		return rc;
> >>>>>>>      	}
> >>>>>>> -	spi_set_chipselect(spi, 0, value);
> >>>>>>> +	if (rc > ctlr->num_chipselect) {
> >>>>>>> +		dev_err(&ctlr->dev, "%pOF has number of CS > ctlr-
> >>>>>>> num_chipselect (%d)\n",
> >>>>>>> +			nc, rc);
> >>>>>>> +		return rc;
> >>>>>>> +	}
> >>>>>>> +	if ((of_property_read_bool(nc, "parallel-memories")) &&
> >>>>>>> +	    (!(ctlr->flags & SPI_CONTROLLER_MULTI_CS))) {
> >>>>>>> +		dev_err(&ctlr->dev, "SPI controller doesn't support
> >> multi
> >>>>>> CS\n");
> >>>>>>> +		return -EINVAL;
> >>>>>>> +	}
> >>>>>>> +	for (idx = 0; idx < rc; idx++)
> >>>>>>> +		spi_set_chipselect(spi, idx, cs[idx]);
> >>>>>>> +
> >>>>>>> +	/* spi->chip_select[i] gives the corresponding physical CS for
> >>>>>>> +logical CS
> >>>>>> i
> >>>>>>> +	 * logical CS number is represented by setting the ith bit in
> >>>>>>> +spi-
> >>>>>>> cs_index_mask
> >>>>>>> +	 * So, for example, if spi->cs_index_mask = 0x01 then logical
> >> CS
> >>>>>> number is 0 and
> >>>>>>> +	 * spi->chip_select[0] will give the physical CS.
> >>>>>>> +	 * By default spi->chip_select[0] will hold the physical CS
> >>>>>>> +number so,
> >>>>>> set
> >>>>>>> +	 * spi->cs_index_mask as 0x01.
> >>>>>>> +	 */
> >>>>>>> +	spi->cs_index_mask = 0x01;
> >>>>>>>
> >>>>>>>      	/* Device speed */
> >>>>>>>      	if (!of_property_read_u32(nc, "spi-max-frequency",
> >>>>>>> &value)) @@
> >>>>>>> -3100,6 +3206,7 @@ int spi_register_controller(struct
> >>>>>>> spi_controller
> >> *ctlr)
> >>>>>>>      	struct boardinfo	*bi;
> >>>>>>>      	int			first_dynamic;
> >>>>>>>      	int			status;
> >>>>>>> +	int			idx;
> >>>>>>>
> >>>>>>>      	if (!dev)
> >>>>>>>      		return -ENODEV;
> >>>>>>> @@ -3164,7 +3271,8 @@ int spi_register_controller(struct
> >>>>>>> spi_controller
> >>>>>> *ctlr)
> >>>>>>>      	}
> >>>>>>>
> >>>>>>>      	/* Setting last_cs to -1 means no chip selected */
> >>>>>>> -	ctlr->last_cs = -1;
> >>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> >>>>>>> +		ctlr->last_cs[idx] = -1;
> >>>>>>>
> >>>>>>>      	status = device_add(&ctlr->dev);
> >>>>>>>      	if (status < 0)
> >>>>>>> @@ -3889,7 +3997,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_get_csgpiod(spi, 0))) {
> >>>>>>> +					  spi_is_csgpiod(spi))) {
> >>>>>>>      		size_t maxsize = BITS_TO_BYTES(spi->bits_per_word);
> >>>>>>>      		int ret;
> >>>>>>>
> >>>>>>> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
> >>>>>>> index 7b4baff63c5c..871d3a6d879b 100644
> >>>>>>> --- a/include/linux/spi/spi.h
> >>>>>>> +++ b/include/linux/spi/spi.h
> >>>>>>> @@ -20,6 +20,9 @@
> >>>>>>>
> >>>>>>>      #include <uapi/linux/spi/spi.h>
> >>>>>>>
> >>>>>>> +/* Max no. of CS supported per spi device */ #define
> >>>>>>> +SPI_CS_CNT_MAX
> >>>>>>> +4
> >>>>>>> +
> >>>>>>>      struct dma_chan;
> >>>>>>>      struct software_node;
> >>>>>>>      struct ptp_system_timestamp; @@ -132,7 +135,8 @@ extern
> >>>>>>> void
> >>>>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> >>>>>>>       * @max_speed_hz: Maximum clock rate to be used with this chip
> >>>>>>>       *	(on this board); may be changed by the device's driver.
> >>>>>>>       *	The spi_transfer.speed_hz can override this for each transfer.
> >>>>>>> - * @chip_select: Chipselect, distinguishing chips handled by
> >> @controller.
> >>>>>>> + * @chip_select: Array of physical chipselect, spi->chipselect[i] gives
> >>>>>>> + *	the corresponding physical CS for logical CS i.
> >>>>>>>       * @mode: The spi mode defines how data is clocked out and in.
> >>>>>>>       *	This may be changed by the device's driver.
> >>>>>>>       *	The "active low" default for chipselect mode can be
> >> overridden
> >>>>>>> @@ -157,8 +161,8 @@ extern void
> >>>>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> >>>>>>>       *	the device will bind to the named driver and only the named
> >> driver.
> >>>>>>>       *	Do not set directly, because core frees it; use
> >> driver_set_override() to
> >>>>>>>       *	set or clear it.
> >>>>>>> - * @cs_gpiod: GPIO descriptor of the chipselect line (optional,
> >>>>>>> NULL
> >>>> when
> >>>>>>> - *	not using a GPIO line)
> >>>>>>> + * @cs_gpiod: Array of GPIO descriptors of the corresponding
> >>>>>>> + chipselect
> >>>>>> lines
> >>>>>>> + *	(optional, NULL when not using a GPIO line)
> >>>>>>>       * @word_delay: delay to be inserted between consecutive
> >>>>>>>       *	words of a transfer
> >>>>>>>       * @cs_setup: delay to be introduced by the controller
> >>>>>>> after CS is asserted @@ -167,6 +171,7 @@ extern void
> >>>>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> >>>>>>>       *	deasserted. If @cs_change_delay is used from @spi_transfer,
> >> then
> >>>>>> the
> >>>>>>>       *	two delays will be added up.
> >>>>>>>       * @pcpu_statistics: statistics for the spi_device
> >>>>>>> + * @cs_index_mask: Bit mask of the active chipselect(s) in the
> >>>>>>> + chipselect array
> >>>>>>>       *
> >>>>>>>       * A @spi_device is used to interchange data between an SPI slave
> >>>>>>>       * (usually a discrete chip) and CPU memory.
> >>>>>>> @@ -182,7 +187,7 @@ struct spi_device {
> >>>>>>>      	struct spi_controller	*controller;
> >>>>>>>      	struct spi_controller	*master;	/* Compatibility layer
> >> */
> >>>>>>>      	u32			max_speed_hz;
> >>>>>>> -	u8			chip_select;
> >>>>>>> +	u8			chip_select[SPI_CS_CNT_MAX];
> >>>>>>>      	u8			bits_per_word;
> >>>>>>>      	bool			rt;
> >>>>>>>      #define SPI_NO_TX		BIT(31)		/* No transmit wire */
> >>>>>>> @@ -213,7 +218,7 @@ struct spi_device {
> >>>>>>>      	void			*controller_data;
> >>>>>>>      	char			modalias[SPI_NAME_SIZE];
> >>>>>>>      	const char		*driver_override;
> >>>>>>> -	struct gpio_desc	*cs_gpiod;	/* Chip select GPIO
> descriptor
> >>>>>> */
> >>>>>>> +	struct gpio_desc	*cs_gpiod[SPI_CS_CNT_MAX];	/*
> >> Chip select
> >>>>>> gpio desc */
> >>>>>>>      	struct spi_delay	word_delay; /* Inter-word delay */
> >>>>>>>      	/* CS delays */
> >>>>>>>      	struct spi_delay	cs_setup;
> >>>>>>> @@ -223,6 +228,13 @@ struct spi_device {
> >>>>>>>      	/* The statistics */
> >>>>>>>      	struct spi_statistics __percpu	*pcpu_statistics;
> >>>>>>>
> >>>>>>> +	/* Bit mask of the chipselect(s) that the driver need to use
> >> from
> >>>>>>> +	 * the chipselect array.When the controller is capable to
> >> handle
> >>>>>>> +	 * multiple chip selects & memories are connected in parallel
> >>>>>>> +	 * then more than one bit need to be set in cs_index_mask.
> >>>>>>> +	 */
> >>>>>>> +	u32			cs_index_mask : SPI_CS_CNT_MAX;
> >>>>>>> +
> >>>>>>>      	/*
> >>>>>>>      	 * Likely need more hooks for more protocol options
> >>>>>>> affecting
> >> how
> >>>>>>>      	 * the controller talks to each chip, like:
> >>>>>>> @@ -279,22 +291,33 @@ static inline void *spi_get_drvdata(const
> >>>>>>> struct spi_device *spi)
> >>>>>>>
> >>>>>>>      static inline u8 spi_get_chipselect(const struct spi_device *spi, u8
> idx)
> >>>>>>>      {
> >>>>>>> -	return spi->chip_select;
> >>>>>>> +	return spi->chip_select[idx];
> >>>>>>>      }
> >>>>>>>
> >>>>>>>      static inline void spi_set_chipselect(struct spi_device
> >>>>>>> *spi,
> >>>>>>> u8 idx, u8
> >>>>>> chipselect)
> >>>>>>>      {
> >>>>>>> -	spi->chip_select = chipselect;
> >>>>>>> +	spi->chip_select[idx] = chipselect;
> >>>>>>>      }
> >>>>>>>
> >>>>>>>      static inline struct gpio_desc *spi_get_csgpiod(const
> >>>>>>> struct spi_device *spi,
> >>>>>> u8 idx)
> >>>>>>>      {
> >>>>>>> -	return spi->cs_gpiod;
> >>>>>>> +	return spi->cs_gpiod[idx];
> >>>>>>>      }
> >>>>>>>
> >>>>>>>      static inline void spi_set_csgpiod(struct spi_device *spi,
> >>>>>>> u8 idx, struct
> >>>>>> gpio_desc *csgpiod)
> >>>>>>>      {
> >>>>>>> -	spi->cs_gpiod = csgpiod;
> >>>>>>> +	spi->cs_gpiod[idx] = csgpiod;
> >>>>>>> +}
> >>>>>>> +
> >>>>>>> +static inline bool spi_is_csgpiod(struct spi_device *spi) {
> >>>>>>> +	u8 idx;
> >>>>>>> +
> >>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> >>>>>>> +		if (spi_get_csgpiod(spi, idx))
> >>>>>>> +			return true;
> >>>>>>> +	}
> >>>>>>> +	return false;
> >>>>>>>      }
> >>>>>>>
> >>>>>>>      /**
> >>>>>>> @@ -399,6 +422,8 @@ extern struct spi_device
> >>>>>> *spi_new_ancillary_device(struct spi_device *spi, u8 ch
> >>>>>>>       * @bus_lock_spinlock: spinlock for SPI bus locking
> >>>>>>>       * @bus_lock_mutex: mutex for exclusion of multiple callers
> >>>>>>>       * @bus_lock_flag: indicates that the SPI bus is locked for
> >>>>>>> exclusive use
> >>>>>>> + * @multi_cs_cap: indicates that the SPI Controller can
> >>>>>>> + assert/de-
> >> assert
> >>>>>>> + *	more than one chip select at once.
> >>>>>>>       * @setup: updates the device mode and clocking records used by
> a
> >>>>>>>       *	device's SPI controller; protocol code may call this.  This
> >>>>>>>       *	must fail if an unrecognized or unsupported mode is
> >> requested.
> >>>>>>> @@ -567,6 +592,11 @@ struct spi_controller {
> >>>>>>>      #define SPI_CONTROLLER_MUST_TX		BIT(4)	/*
> Requires tx
> >>>> */
> >>>>>>>      #define SPI_CONTROLLER_GPIO_SS		BIT(5)	/* GPIO CS
> >>>> must
> >>>>>> select slave */
> >>>>>>>      #define SPI_CONTROLLER_SUSPENDED	BIT(6)	/* Currently
> >>>>>> suspended */
> >>>>>>> +	/*
> >>>>>>> +	 * The spi-controller has multi chip select capability and can
> >>>>>>> +	 * assert/de-assert more than one chip select at once.
> >>>>>>> +	 */
> >>>>>>> +#define SPI_CONTROLLER_MULTI_CS		BIT(7)
> >>>>>>>
> >>>>>>>      	/* Flag indicating if the allocation of this struct is
> >>>>>>> devres-
> >> managed */
> >>>>>>>      	bool			devm_allocated;
> >>>>>>> @@ -677,7 +707,8 @@ struct spi_controller {
> >>>>>>>      	bool				rt;
> >>>>>>>      	bool				auto_runtime_pm;
> >>>>>>>      	bool				cur_msg_mapped;
> >>>>>>> -	char				last_cs;
> >>>>>>> +	char				last_cs[SPI_CS_CNT_MAX];
> >>>>>>> +	char				last_cs_index_mask;
> >>>>>>>      	bool				last_cs_mode_high;
> >>>>>>>      	bool                            fallback;
> >>>>>>>      	struct completion               xfer_completion;
Stefan Binding Nov. 24, 2023, 5:33 p.m. UTC | #11
> -----Original Message-----
> From: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>
> Sent: Wednesday, November 22, 2023 1:23 PM
> To: Stefan Binding <sbinding@opensource.cirrus.com>;
> broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> michael@walle.cc; linux-mtd@lists.infradead.org;
> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>;
> linux-arm-kernel@lists.infradead.org; git (AMD-Xilinx) <git@amd.com>;
> amitrkcian2002@gmail.com; patches@opensource.cirrus.com
> Subject: RE: [PATCH v10 1/8] spi: Add multi-cs memories support in SPI
> core
> 
> 
> 
> > -----Original Message-----
> > From: Stefan Binding <sbinding@opensource.cirrus.com>
> > Sent: Wednesday, November 22, 2023 4:51 PM
> > To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
> > broonie@kernel.org; tudor.ambarus@linaro.org; pratyush@kernel.org;
> > miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
> > Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> > michael@walle.cc; linux-mtd@lists.infradead.org;
> > nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> > claudiu.beznea@tuxon.dev; Simek, Michal <michal.simek@amd.com>;
> linux-
> > arm-kernel@lists.infradead.org; git (AMD-Xilinx) <git@amd.com>;
> > amitrkcian2002@gmail.com; patches@opensource.cirrus.com
> > Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in SPI
> core
> >
> >
> > On 21/11/2023 19:18, Mahapatra, Amit Kumar wrote:
> > > Hello Stefan,
> > >
> > >> -----Original Message-----
> > >> From: Stefan Binding <sbinding@opensource.cirrus.com>
> > >> Sent: Tuesday, November 21, 2023 11:07 PM
> > >> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
> > >> broonie@kernel.org; tudor.ambarus@linaro.org;
> pratyush@kernel.org;
> > >> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
> > >> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> > >> michael@walle.cc; linux-mtd@lists.infradead.org;
> > >> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> > >> claudiu.beznea@tuxon.dev; Simek, Michal
> <michal.simek@amd.com>;
> > >> linux- arm-kernel@lists.infradead.org; git (AMD-Xilinx)
> > >> <git@amd.com>; amitrkcian2002@gmail.com;
> > >> patches@opensource.cirrus.com
> > >> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in
> > >> SPI core
> > >>
> > >>
> > >> On 21/11/2023 16:35, Mahapatra, Amit Kumar wrote:
> > >>> Hello Stefan,
> > >>>
> > >>>> -----Original Message-----
> > >>>> From: Stefan Binding <sbinding@opensource.cirrus.com>
> > >>>> Sent: Tuesday, November 21, 2023 7:28 PM
> > >>>> To: Mahapatra, Amit Kumar <amit.kumar-mahapatra@amd.com>;
> > >>>> broonie@kernel.org; tudor.ambarus@linaro.org;
> pratyush@kernel.org;
> > >>>> miquel.raynal@bootlin.com; richard@nod.at; vigneshr@ti.com
> > >>>> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> > >>>> michael@walle.cc; linux-mtd@lists.infradead.org;
> > >>>> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> > >>>> claudiu.beznea@tuxon.dev; Simek, Michal
> <michal.simek@amd.com>;
> > >>>> linux- arm-kernel@lists.infradead.org; git (AMD-Xilinx)
> > >>>> <git@amd.com>; amitrkcian2002@gmail.com;
> > >>>> patches@opensource.cirrus.com
> > >>>> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support in
> > >>>> SPI core
> > >>>>
> > >>>>
> > >>>> On 21/11/2023 07:39, Mahapatra, Amit Kumar wrote:
> > >>>>> Hello Stefan,
> > >>>>>
> > >>>>>> -----Original Message-----
> > >>>>>> From: Stefan Binding <sbinding@opensource.cirrus.com>
> > >>>>>> Sent: Monday, November 20, 2023 8:00 PM
> > >>>>>> To: Mahapatra, Amit Kumar <amit.kumar-
> mahapatra@amd.com>;
> > >>>>>> broonie@kernel.org; tudor.ambarus@linaro.org;
> > >>>>>> pratyush@kernel.org; miquel.raynal@bootlin.com;
> richard@nod.at;
> > >>>>>> vigneshr@ti.com
> > >>>>>> Cc: linux-spi@vger.kernel.org; linux-kernel@vger.kernel.org;
> > >>>>>> michael@walle.cc; linux-mtd@lists.infradead.org;
> > >>>>>> nicolas.ferre@microchip.com; alexandre.belloni@bootlin.com;
> > >>>>>> claudiu.beznea@tuxon.dev; Simek, Michal
> > <michal.simek@amd.com>;
> > >>>>>> linux- arm-kernel@lists.infradead.org; git (AMD-Xilinx)
> > >>>>>> <git@amd.com>; amitrkcian2002@gmail.com;
> > >>>>>> patches@opensource.cirrus.com
> > >>>>>> Subject: Re: [PATCH v10 1/8] spi: Add multi-cs memories support
> > >>>>>> in SPI core
> > >>>>>>
> > >>>>>>
> > >>>>>> On 18/11/2023 13:54, Amit Kumar Mahapatra wrote:
> > >>>>>>> AMD-Xilinx GQSPI controller has two advanced mode that
> allows
> > >>>>>>> the controller to consider two flashes as one single device.
> > >>>>>>>
> > >>>>>>> One of these two mode is the parallel mode in which each byte
> of
> > >>>>>>> data is stored in both devices, the even bits in the lower flash
> > >>>>>>> & the odd bits in the upper flash. The byte split is
> > >>>>>>> automatically handled by the QSPI controller.
> > >>>>>>>
> > >>>>>>> The other mode is the stacked mode in which both the flashes
> > >>>>>>> share the same SPI bus but each of the device contain half of
> the
> > data.
> > >>>>>>> In this mode, the controller does not follow CS requests but
> > >>>>>>> instead internally wires the two CS levels with the value of the
> > >>>>>>> most significant
> > >>>>>> address bit.
> > >>>>>>> For supporting both these modes SPI core need to be updated
> for
> > >>>>>>> providing multiple CS for a single SPI device.
> > >>>>>>>
> > >>>>>>> For adding multi CS support the SPI device need to be aware of
> > >>>>>>> all the CS values. So, the "chip_select" member in the
> > >>>>>>> spi_device structure is now an array that holds all the CS values.
> > >>>>>>>
> > >>>>>>> spi_device structure now has a "cs_index_mask" member. This
> acts
> > >>>>>>> as an index to the chip_select array. If nth bit of
> > >>>>>>> spi->cs_index_mask is set then the driver would assert spi-
> > >>> chip_select[n].
> > >>>>>>> In parallel mode all the chip selects are asserted/de-asserted
> > >>>>>>> simultaneously and each byte of data is stored in both devices,
> > >>>>>>> the even bits in one, the odd bits in the other. The split is
> > >>>>>>> automatically handled by the GQSPI controller. The GQSPI
> > >>>>>>> controller supports a maximum of two flashes connected in
> > >>>>>>> parallel mode. A SPI_CONTROLLER_MULTI_CS flag bit is added
> in
> > >>>>>>> the spi controller flags, through ctlr->flags the spi core will
> > >>>>>>> make sure that the controller is capable of handling multiple
> chip
> > selects at once.
> > >>>>>>>
> > >>>>>>> For supporting multiple CS via GPIO the cs_gpiod member of the
> > >>>>>>> spi_device structure is now an array that holds the gpio
> > >>>>>>> descriptor for each chipselect.
> > >>>>>>>
> > >>>>>>> CS GPIO is not tested due to unavailability of necessary
> > >>>>>>> hardware
> > >> setup.
> > >>>>>>> Signed-off-by: Amit Kumar Mahapatra <amit.kumar-
> > >>>> mahapatra@amd.com>
> > >>>>>>> ---
> > >>>>>>> Hello @Stefen,
> > >>>>>>> We restructured the SPI core implementation, for handling
> > >>>>>>> arbitrary number of devices connected in parallel(multi-cs) or
> > >>>>>>> stacked
> > >> mode.
> > >>>>>>> We have tested this updated patch on native-cs setup but
> > >>>>>>> couldn't test cs-gpio due to unavailability of a proper setup.
> > >>>>>>> If possible, could you please retest the cs-gpio use case with
> > >>>>>>> this updated patch and provide
> > >>>>>> your feedback.
> > >>>>>>
> > >>>>>> Hi,
> > >>>>>>
> > >>>>>> I tested this chain on 2 different systems using GPIOs as chip
> > >>>>>> selects, and see the same error on both:
> > >>>>>> [    2.842045] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in
> > >>>>>> use
> > >>>>>>
> > >>>>>> Let me know if you need any further testing.
> > >>>>>>
> > >>>>>> Thanks,
> > >>>>>>
> > >>>>>> Stefan Binding
> > >>>>>>
> > >>>>>>> ---
> > >>>>>>>      drivers/spi/spi.c       | 192
> +++++++++++++++++++++++++++++++---
> > ----
> > >> --
> > >>>>>>>      include/linux/spi/spi.h |  51 ++++++++---
> > >>>>>>>      2 files changed, 191 insertions(+), 52 deletions(-)
> > >>>>>>>
> > >>>>>>> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index
> > >>>>>>> 8ead7acb99f3..ff66147ba95f 100644
> > >>>>>>> --- a/drivers/spi/spi.c
> > >>>>>>> +++ b/drivers/spi/spi.c
> > >>>>>>> @@ -612,10 +612,21 @@ static int spi_dev_check(struct
> device
> > >>>>>>> *dev, void
> > >>>>>> *data)
> > >>>>>>>      {
> > >>>>>>>      	struct spi_device *spi = to_spi_device(dev);
> > >>>>>>>      	struct spi_device *new_spi = data;
> > >>>>>>> -
> > >>>>>>> -	if (spi->controller == new_spi->controller &&
> > >>>>>>> -	    spi_get_chipselect(spi, 0) ==
> spi_get_chipselect(new_spi, 0))
> > >>>>>>> -		return -EBUSY;
> > >>>>>>> +	int idx, nw_idx;
> > >>>>>>> +	u8 cs, cs_nw;
> > >>>>>>> +
> > >>>>>>> +	if (spi->controller == new_spi->controller) {
> > >>>>>>> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > >>>>>>> +			cs = spi_get_chipselect(spi, idx);
> > >>>>>>> +			for (nw_idx = 0; nw_idx <
> SPI_CS_CNT_MAX;
> > >>>>>> nw_idx++) {
> > >>>>>>> +				cs_nw = spi_get_chipselect(spi,
> > >> nw_idx);
> > >>>>> Thank you for dedicating your time to test my patch.
> > >>>>> As per my analysis the error is reported from here as we are
> > >>>>> supplying the former SPI device structure to retrieve the CS value
> > >>>>> for the new SPI devices.
> > >>>>> To fix this, could you kindly substitute the above line with
> > >>>>>
> > >>>>> cs_nw = spi_get_chipselect(new_spi, nw_idx);
> > >>>>>
> > >>>>> and rerun your tests?
> > >>>>> If it works correctly, I will incorporate this fix into my
> > >>>>> upcoming series.
> > >>>>>
> > >>>>> Regards,
> > >>>>> Amit
> > >> Hi,
> > >>
> > >> I've attached my log.
> > >> I notice that you add a print to of_spi_parse_dt, however since the
> > >> laptop I am using is an x86 laptop, it uses ACPI rather than OF, and
> > >> I don't think this function isnt even compiled in.
> > > That’s correct, I missed it.
> > > Upon reviewing the logs, I discovered that in the ACPI flow, I am not
> > > initializing the unused CS[] to FF, as I am doing in the OF flow.
> > > Consequently, in the ACPI flow, all the unused CS[] are automatically
> > > initialized to 0. During the __spi_add_device process, the SPI core
> > > faces difficulty distinguishing between a valid CS value of 0 and an
> > > unused CS value of 0, leading to a failure in the driver probe.
> > >
> > > Incorporating the following modifications should resolve this issue.
> > > Kindly apply these changes on top of 1/8 and re-run the test.
> > >
> > > diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index
> > > ff66147ba95f..d0301c17d4f8 100644
> > > --- a/drivers/spi/spi.c
> > > +++ b/drivers/spi/spi.c
> > > @@ -619,7 +619,7 @@ static int spi_dev_check(struct device *dev,
> void
> > *data)
> > >                  for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > >                          cs = spi_get_chipselect(spi, idx);
> > >                          for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
> > > -                               cs_nw = spi_get_chipselect(spi, nw_idx);
> > > +                               cs_nw = spi_get_chipselect(new_spi,
> > > + nw_idx);
> > >                                  if (cs != 0xFF && cs_nw != 0xFF && cs == cs_nw) {
> > >                                          dev_err(dev, "chipselect %d already in use\n",
> > cs_nw);
> > >                                          return -EBUSY; @@ -764,6
> > > +764,7 @@ struct spi_device *spi_new_device(struct spi_controller
> *ctlr,
> > >   {
> > >          struct spi_device       *proxy;
> > >          int                     status;
> > > +       u8                      idx;
> > >
> > >          /*
> > >           * NOTE:  caller did any chip->bus_num checks necessary.
> > > @@ -779,6 +780,9 @@ struct spi_device *spi_new_device(struct
> > > spi_controller *ctlr,
> > >
> > >          WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
> > >
> > > +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> > > +               spi_set_chipselect(proxy, idx, 0xFF);
> > > +
> > >          spi_set_chipselect(proxy, 0, chip->chip_select);
> > >          proxy->max_speed_hz = chip->max_speed_hz;
> > >          proxy->mode = chip->mode;
> > > @@ -2514,6 +2518,7 @@ struct spi_device
> > *spi_new_ancillary_device(struct spi_device *spi,
> > >          struct spi_controller *ctlr = spi->controller;
> > >          struct spi_device *ancillary;
> > >          int rc = 0;
> > > +       u8 idx;
> > >
> > >          /* Alloc an spi_device */
> > >          ancillary = spi_alloc_device(ctlr); @@ -2524,6 +2529,9 @@
> > > struct spi_device *spi_new_ancillary_device(struct spi_device *spi,
> > >
> > >          strscpy(ancillary->modalias, "dummy",
> > > sizeof(ancillary->modalias));
> > >
> > > +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> > > +               spi_set_chipselect(ancillary, idx, 0xFF);
> > > +
> > >          /* Use provided chip-select for ancillary device */
> > >          spi_set_chipselect(ancillary, 0, chip_select);
> > >
> > > @@ -2732,6 +2740,7 @@ struct spi_device
> *acpi_spi_device_alloc(struct
> > spi_controller *ctlr,
> > >          struct acpi_spi_lookup lookup = {};
> > >          struct spi_device *spi;
> > >          int ret;
> > > +       u8 idx;
> > >
> > >          if (!ctlr && index == -1)
> > >                  return ERR_PTR(-EINVAL); @@ -2767,6 +2776,9 @@ struct
> > > spi_device *acpi_spi_device_alloc(struct spi_controller *ctlr,
> > >                  return ERR_PTR(-ENOMEM);
> > >          }
> > >
> > > +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> > > +               spi_set_chipselect(spi, idx, 0xFF);
> > > +
> > >          ACPI_COMPANION_SET(&spi->dev, adev);
> > >          spi->max_speed_hz       = lookup.max_speed_hz;
> > >          spi->mode               |= lookup.mode;
> > >
> > > Regards,
> > > Amit
> > >
> >
> > Hi,
> >
> > I no longer see that error, or any errors printed from SPI, however, all of
> the
> > transactions are now broken.
> > Every transaction seems to read back 0x0, on all SPI devices, whether
> native
> > CS or GPIO CS, and I dont see it toggling the CS GPIO anymore.
> 
> I believe the issue stems from the fact that in the ACPI patch,
> spi->cs_index_mask is not set to 0x01. As a result, in spi_set_cs,
> it fails to meet the condition that governs the CS asset code snippet.
> In the following code diff, I've included the necessary fix along with
> the earlier changes on top of 1/8. I've also included a couple of debug
> prints to aid in further debugging in case my analysis is incorrect.
> Please apply these changes on top of 1/8 and re-run the test.
> 

Hi,

I've tested this on several laptops, and it all seems to work now.

Tested-by: Stefan Binding <sbinding@opensource.cirrus.com>

Thanks,
Stefan

> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index ff66147ba95f..b2e035c3d494 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -619,7 +619,7 @@ static int spi_dev_check(struct device *dev, void
> *data)
>                 for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>                         cs = spi_get_chipselect(spi, idx);
>                         for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
> -                               cs_nw = spi_get_chipselect(spi, nw_idx);
> +                               cs_nw = spi_get_chipselect(new_spi, nw_idx);
>                                 if (cs != 0xFF && cs_nw != 0xFF && cs == cs_nw) {
>                                         dev_err(dev, "chipselect %d already in use\n",
> cs_nw);
>                                         return -EBUSY;
> @@ -764,6 +764,7 @@ struct spi_device *spi_new_device(struct
> spi_controller *ctlr,
>  {
>         struct spi_device       *proxy;
>         int                     status;
> +       u8                      idx;
> 
>         /*
>          * NOTE:  caller did any chip->bus_num checks necessary.
> @@ -779,6 +780,9 @@ struct spi_device *spi_new_device(struct
> spi_controller *ctlr,
> 
>         WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
> 
> +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> +               spi_set_chipselect(proxy, idx, 0xFF);
> +
>         spi_set_chipselect(proxy, 0, chip->chip_select);
>         proxy->max_speed_hz = chip->max_speed_hz;
>         proxy->mode = chip->mode;
> @@ -787,6 +791,7 @@ struct spi_device *spi_new_device(struct
> spi_controller *ctlr,
>         proxy->dev.platform_data = (void *) chip->platform_data;
>         proxy->controller_data = chip->controller_data;
>         proxy->controller_state = NULL;
> +       proxy->cs_index_mask = 0x01;
> 
>         if (chip->swnode) {
>                 status = device_add_software_node(&proxy->dev, chip-
> >swnode);
> @@ -1024,6 +1029,8 @@ static void spi_set_cs(struct spi_device *spi,
> bool enable, bool force)
>                 if (!spi->controller->set_cs_timing && !activate)
>                         spi_delay_exec(&spi->cs_hold, NULL);
> 
> +               printk("%s() [%d] spi->cs_index_mask == [%d]\n",__func__,
> __LINE__, spi->cs_index_mask);
> +
>                 if (!(spi->mode & SPI_NO_CS)) {
>                         /*
>                          * Historically ACPI has no means of the GPIO polarity and
> @@ -1038,6 +1045,7 @@ static void spi_set_cs(struct spi_device *spi,
> bool enable, bool force)
>                         for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
>                                 if (((spi->cs_index_mask >> idx) & 0x01) &&
>                                     spi_get_csgpiod(spi, idx)) {
> +                                   printk("%s() [%d] idx == [%d]\n",__func__, __LINE__,
> idx);
>                                         if (has_acpi_companion(&spi->dev))
>                                                 gpiod_set_value_cansleep(spi_get_csgpiod(spi,
> idx),
>                                                                          !enable);
> @@ -2514,6 +2522,7 @@ struct spi_device
> *spi_new_ancillary_device(struct spi_device *spi,
>         struct spi_controller *ctlr = spi->controller;
>         struct spi_device *ancillary;
>         int rc = 0;
> +       u8 idx;
> 
>         /* Alloc an spi_device */
>         ancillary = spi_alloc_device(ctlr);
> @@ -2524,12 +2533,16 @@ struct spi_device
> *spi_new_ancillary_device(struct spi_device *spi,
> 
>         strscpy(ancillary->modalias, "dummy", sizeof(ancillary->modalias));
> 
> +        for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> +               spi_set_chipselect(ancillary, idx, 0xFF);
> +
>         /* Use provided chip-select for ancillary device */
>         spi_set_chipselect(ancillary, 0, chip_select);
> 
>         /* Take over SPI mode/speed from SPI main device */
>         ancillary->max_speed_hz = spi->max_speed_hz;
>         ancillary->mode = spi->mode;
> +       ancillary->cs_index_mask = 0x01;
> 
>         WARN_ON(!mutex_is_locked(&ctlr->add_lock));
> 
> @@ -2732,6 +2745,7 @@ struct spi_device *acpi_spi_device_alloc(struct
> spi_controller *ctlr,
>         struct acpi_spi_lookup lookup = {};
>         struct spi_device *spi;
>         int ret;
> +       u8 idx;
> 
>         if (!ctlr && index == -1)
>                 return ERR_PTR(-EINVAL);
> @@ -2767,12 +2781,16 @@ struct spi_device
> *acpi_spi_device_alloc(struct spi_controller *ctlr,
>                 return ERR_PTR(-ENOMEM);
>         }
> 
> +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> +               spi_set_chipselect(spi, idx, 0xFF);
> +
>         ACPI_COMPANION_SET(&spi->dev, adev);
>         spi->max_speed_hz       = lookup.max_speed_hz;
>         spi->mode               |= lookup.mode;
>         spi->irq                = lookup.irq;
>         spi->bits_per_word      = lookup.bits_per_word;
>         spi_set_chipselect(spi, 0, lookup.chip_select);
> +       spi->cs_index_mask      = 0x01;
> 
>         return spi;
>  }
> 
> Regards,
> Amit
> 
> >
> > Thanks,
> >
> > Stefan
> >
> > >> Thanks,
> > >>
> > >> Stefan
> > >>
> > >>>> Hi,
> > >>>>
> > >>>> I still see the same error:
> > >>>>
> > >>>> [    2.748546] pxa2xx-spi pxa2xx-spi.6: chipselect 0 already in use
> > >>> Thank you for the quick testing. For further analysis I have
> > >>> incorporated additional debug prints on top of 1/8 patch. The
> > >>> corresponding diff is shared below. Kindly implement the specified
> > >>> changes, rerun your test and share the kernel logs.
> > >>>
> > >>> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index
> > >>> ff66147ba95f..7f59ea81593d 100644
> > >>> --- a/drivers/spi/spi.c
> > >>> +++ b/drivers/spi/spi.c
> > >>> @@ -618,8 +618,10 @@ static int spi_dev_check(struct device *dev,
> > >>> void
> > >> *data)
> > >>>           if (spi->controller == new_spi->controller) {
> > >>>                   for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > >>>                           cs = spi_get_chipselect(spi, idx);
> > >>> +                       printk("%s() [%d] CS[%d] = [%d]\n",
> > >>> + __func__, __LINE__, idx, cs);
> > >>>                           for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX;
> nw_idx++) {
> > >>> -                               cs_nw = spi_get_chipselect(spi, nw_idx);
> > >>> +                               cs_nw = spi_get_chipselect(new_spi, nw_idx);
> > >>> +                               printk("%s() [%d] CS_NEW[%d] =
> > >>> + [%d]\n", __func__, __LINE__, nw_idx, cs_nw);
> > >>>                                   if (cs != 0xFF && cs_nw != 0xFF && cs == cs_nw) {
> > >>>                                           dev_err(dev, "chipselect
> > >>> %d already in use\n",
> > >> cs_nw);
> > >>>                                           return -EBUSY; @@ -659,8
> > >>> +661,10 @@ static int __spi_add_device(struct spi_device *spi)
> > >>>            */
> > >>>           for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > >>>                   cs = spi_get_chipselect(spi, idx);
> > >>> +               printk("%s() [%d] CS[%d] = [%d]\n", __func__,
> > >>> + __LINE__, idx, cs);
> > >>>                   for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX;
> nw_idx++) {
> > >>>                           nw_cs = spi_get_chipselect(spi, nw_idx);
> > >>> +                       printk("%s() [%d] CS_NEW[%d] = [%d]\n",
> > >>> + __func__, __LINE__, nw_idx, nw_cs);
> > >>>                           if (cs != 0xFF && nw_cs != 0xFF && cs == nw_cs) {
> > >>>                                   dev_err(dev, "chipselect %d already in use\n",
> nw_cs);
> > >>>                                   return -EBUSY; @@ -2401,6 +2405,9
> > >>> @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct
> spi_device
> > *spi,
> > >>>           for (idx = 0; idx < rc; idx++)
> > >>>                   spi_set_chipselect(spi, idx, cs[idx]);
> > >>>
> > >>> +       for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> > >>> +               printk("%s() [%d] CS[%d] = [%d]\n", __func__,
> > >>> + __LINE__, idx, spi_get_chipselect(spi, idx));
> > >>> +
> > >>>           /* spi->chip_select[i] gives the corresponding physical CS for
> logical
> > CS i
> > >>>            * logical CS number is represented by setting the ith bit
> > >>> in spi- cs_index_mask
> > >>>            * So, for example, if spi->cs_index_mask = 0x01 then
> > >>> logical CS number is 0 and
> > >>>
> > >>> Regards,
> > >>> Amit
> > >>>
> > >>>> Thanks,
> > >>>>
> > >>>> Stefan
> > >>>>
> > >>>>>>> +				if (cs != 0xFF && cs_nw != 0xFF
> && cs
> > >> ==
> > >>>>>> cs_nw) {
> > >>>>>>> +					dev_err(dev, "chipselect
> %d
> > >> already in
> > >>>>>> use\n", cs_nw);
> > >>>>>>> +					return -EBUSY;
> > >>>>>>> +				}
> > >>>>>>> +			}
> > >>>>>>> +		}
> > >>>>>>> +	}
> > >>>>>>>      	return 0;
> > >>>>>>>      }
> > >>>>>>>
> > >>>>>>> @@ -629,13 +640,32 @@ static int __spi_add_device(struct
> > >>>>>>> spi_device
> > >>>> *spi)
> > >>>>>>>      {
> > >>>>>>>      	struct spi_controller *ctlr = spi->controller;
> > >>>>>>>      	struct device *dev = ctlr->dev.parent;
> > >>>>>>> -	int status;
> > >>>>>>> +	int status, idx, nw_idx;
> > >>>>>>> +	u8 cs, nw_cs;
> > >>>>>>> +
> > >>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > >>>>>>> +		/* Chipselects are numbered 0..max; validate. */
> > >>>>>>> +		cs = spi_get_chipselect(spi, idx);
> > >>>>>>> +		if (cs != 0xFF && cs >= ctlr->num_chipselect) {
> > >>>>>>> +			dev_err(dev, "cs%d >= max %d\n",
> > >>>>>> spi_get_chipselect(spi, idx),
> > >>>>>>> +				ctlr->num_chipselect);
> > >>>>>>> +			return -EINVAL;
> > >>>>>>> +		}
> > >>>>>>> +	}
> > >>>>>>>
> > >>>>>>> -	/* Chipselects are numbered 0..max; validate. */
> > >>>>>>> -	if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
> > >>>>>>> -		dev_err(dev, "cs%d >= max %d\n",
> > spi_get_chipselect(spi, 0),
> > >>>>>>> -			ctlr->num_chipselect);
> > >>>>>>> -		return -EINVAL;
> > >>>>>>> +	/*
> > >>>>>>> +	 * Make sure that multiple logical CS doesn't map to the
> same
> > >>>>>> physical CS.
> > >>>>>>> +	 * For example, spi->chip_select[0] != spi->chip_select[1]
> and
> > >> so on.
> > >>>>>>> +	 */
> > >>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > >>>>>>> +		cs = spi_get_chipselect(spi, idx);
> > >>>>>>> +		for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX;
> > >> nw_idx++) {
> > >>>>>>> +			nw_cs = spi_get_chipselect(spi, nw_idx);
> > >>>>>>> +			if (cs != 0xFF && nw_cs != 0xFF && cs ==
> > >> nw_cs) {
> > >>>>>>> +				dev_err(dev, "chipselect %d
> already in
> > >> use\n",
> > >>>>>> nw_cs);
> > >>>>>>> +				return -EBUSY;
> > >>>>>>> +			}
> > >>>>>>> +		}
> > >>>>>>>      	}
> > >>>>>>>
> > >>>>>>>      	/* Set the bus ID string */ @@ -647,11 +677,8 @@ static
> > >>>>>>> int __spi_add_device(struct spi_device
> > >>>> *spi)
> > >>>>>>>      	 * its configuration.
> > >>>>>>>      	 */
> > >>>>>>>      	status = bus_for_each_dev(&spi_bus_type, NULL, spi,
> > >> spi_dev_check);
> > >>>>>>> -	if (status) {
> > >>>>>>> -		dev_err(dev, "chipselect %d already in use\n",
> > >>>>>>> -				spi_get_chipselect(spi, 0));
> > >>>>>>> +	if (status)
> > >>>>>>>      		return status;
> > >>>>>>> -	}
> > >>>>>>>
> > >>>>>>>      	/* Controller may unregister concurrently */
> > >>>>>>>      	if (IS_ENABLED(CONFIG_SPI_DYNAMIC) && @@ -659,8
> > >> +686,15 @@
> > >>>>>> static
> > >>>>>>> int __spi_add_device(struct spi_device *spi)
> > >>>>>>>      		return -ENODEV;
> > >>>>>>>      	}
> > >>>>>>>
> > >>>>>>> -	if (ctlr->cs_gpiods)
> > >>>>>>> -		spi_set_csgpiod(spi, 0, ctlr-
> > >cs_gpiods[spi_get_chipselect(spi,
> > >>>>>> 0)]);
> > >>>>>>> +	if (ctlr->cs_gpiods) {
> > >>>>>>> +		u8 cs;
> > >>>>>>> +
> > >>>>>>> +		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > >>>>>>> +			cs = spi_get_chipselect(spi, idx);
> > >>>>>>> +			if (cs != 0xFF)
> > >>>>>>> +				spi_set_csgpiod(spi, idx, ctlr-
> > >>> cs_gpiods[cs]);
> > >>>>>>> +		}
> > >>>>>>> +	}
> > >>>>>>>
> > >>>>>>>      	/*
> > >>>>>>>      	 * Drivers may modify this initial i/o setup, but will @@
> > >>>>>>> -701,6
> > >>>>>>> +735,9 @@ int spi_add_device(struct spi_device *spi)
> > >>>>>>>      	struct spi_controller *ctlr = spi->controller;
> > >>>>>>>      	int status;
> > >>>>>>>
> > >>>>>>> +	/* Set the bus ID string */
> > >>>>>>> +	spi_dev_set_name(spi);
> > >>>>>>> +
> > >>>>>>>      	mutex_lock(&ctlr->add_lock);
> > >>>>>>>      	status = __spi_add_device(spi);
> > >>>>>>>      	mutex_unlock(&ctlr->add_lock); @@ -942,32 +979,51
> @@
> > >> static
> > >>>>>>> void spi_res_release(struct spi_controller
> > >>>>>> *ctlr, struct spi_message *mes
> > >>>>>>>      }
> > >>>>>>>
> > >>>>>>>
> > >>>>>>> /*--------------------------------------------------------------
> > >>>>>>> --
> > >>>>>>> --
> > >>>>>>> --
> > >>>>>>> -----*/
> > >>>>>>> +static inline bool spi_is_last_cs(struct spi_device *spi) {
> > >>>>>>> +	u8 idx;
> > >>>>>>> +	bool last = false;
> > >>>>>>> +
> > >>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > >>>>>>> +		if ((spi->cs_index_mask >> idx) & 0x01) {
> > >>>>>>> +			if (spi->controller->last_cs[idx] ==
> > >>>>>> spi_get_chipselect(spi, idx))
> > >>>>>>> +				last = true;
> > >>>>>>> +		}
> > >>>>>>> +	}
> > >>>>>>> +	return last;
> > >>>>>>> +}
> > >>>>>>> +
> > >>>>>>>
> > >>>>>>>      static void spi_set_cs(struct spi_device *spi, bool enable,
> bool
> > force)
> > >>>>>>>      {
> > >>>>>>>      	bool activate = enable;
> > >>>>>>> +	u8 idx;
> > >>>>>>>
> > >>>>>>>      	/*
> > >>>>>>>      	 * Avoid calling into the driver (or doing delays) if the
> > >>>>>>> chip
> > >> select
> > >>>>>>>      	 * isn't actually changing from the last time this was
> called.
> > >>>>>>>      	 */
> > >>>>>>> -	if (!force && ((enable && spi->controller->last_cs ==
> > >>>>>> spi_get_chipselect(spi, 0)) ||
> > >>>>>>> -		       (!enable && spi->controller->last_cs !=
> > >>>>>> spi_get_chipselect(spi, 0))) &&
> > >>>>>>> +	if (!force && ((enable && spi->controller-
> >last_cs_index_mask
> > >> ==
> > >>>>>>> +spi-
> > >>>>>>> cs_index_mask &&
> > >>>>>>> +			spi_is_last_cs(spi)) ||
> > >>>>>>> +		       (!enable && spi->controller-
> >last_cs_index_mask
> > >> == spi-
> > >>>>>>> cs_index_mask &&
> > >>>>>>> +			!spi_is_last_cs(spi))) &&
> > >>>>>>>      	    (spi->controller->last_cs_mode_high == (spi->mode &
> > >>>>>> SPI_CS_HIGH)))
> > >>>>>>>      		return;
> > >>>>>>>
> > >>>>>>>      	trace_spi_set_cs(spi, activate);
> > >>>>>>>
> > >>>>>>> -	spi->controller->last_cs = enable ? spi_get_chipselect(spi,
> 0) : -
> > 1;
> > >>>>>>> +	spi->controller->last_cs_index_mask = spi-
> >cs_index_mask;
> > >>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> > >>>>>>> +		spi->controller->last_cs[idx] = enable ?
> > >>>>>>> +spi_get_chipselect(spi,
> > >>>>>> 0)
> > >>>>>>> +: -1;
> > >>>>>>>      	spi->controller->last_cs_mode_high = spi->mode &
> > >> SPI_CS_HIGH;
> > >>>>>>> -	if ((spi_get_csgpiod(spi, 0) || !spi->controller-
> >set_cs_timing)
> > &&
> > >>>>>> !activate)
> > >>>>>>> -		spi_delay_exec(&spi->cs_hold, NULL);
> > >>>>>>> -
> > >>>>>>>      	if (spi->mode & SPI_CS_HIGH)
> > >>>>>>>      		enable = !enable;
> > >>>>>>>
> > >>>>>>> -	if (spi_get_csgpiod(spi, 0)) {
> > >>>>>>> +	if (spi_is_csgpiod(spi)) {
> > >>>>>>> +		if (!spi->controller->set_cs_timing && !activate)
> > >>>>>>> +			spi_delay_exec(&spi->cs_hold, NULL);
> > >>>>>>> +
> > >>>>>>>      		if (!(spi->mode & SPI_NO_CS)) {
> > >>>>>>>      			/*
> > >>>>>>>      			 * Historically ACPI has no means of the
> GPIO
> > >> polarity
> > >>>>>> and @@
> > >>>>>>> -979,26 +1035,38 @@ static void spi_set_cs(struct spi_device
> > >>>>>>> *spi, bool
> > >>>>>> enable, bool force)
> > >>>>>>>      			 * ambiguity. That's why we use enable,
> that
> > >> takes
> > >>>>>> SPI_CS_HIGH
> > >>>>>>>      			 * into account.
> > >>>>>>>      			 */
> > >>>>>>> -			if (has_acpi_companion(&spi->dev))
> > >>>>>>> -
> > >>>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0),
> !enable);
> > >>>>>>> -			else
> > >>>>>>> -				/* Polarity handled by GPIO
> library */
> > >>>>>>> -
> > >>>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0),
> activate);
> > >>>>>>> +			for (idx = 0; idx < SPI_CS_CNT_MAX;
> idx++) {
> > >>>>>>> +				if (((spi->cs_index_mask >> idx)
> &
> > >> 0x01) &&
> > >>>>>>> +				    spi_get_csgpiod(spi, idx)) {
> > >>>>>>> +					if
> (has_acpi_companion(&spi-
> > >>> dev))
> > >>>>>>> +
> > >>>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
> > >>>>>>> +
> > >>>>>> !enable);
> > >>>>>>> +					else
> > >>>>>>> +						/* Polarity
> handled by
> > >> GPIO
> > >>>>>> library */
> > >>>>>>> +
> > >>>>>> 	gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
> > >>>>>>> +
> > >>>>>> activate);
> > >>>>>>> +
> > >>>>>>> +					if (activate)
> > >>>>>>> +
> 	spi_delay_exec(&spi-
> > >>>>>>> cs_setup, NULL);
> > >>>>>>> +					else
> > >>>>>>> +
> 	spi_delay_exec(&spi-
> > >>>>>>> cs_inactive, NULL);
> > >>>>>>> +				}
> > >>>>>>> +			}
> > >>>>>>>      		}
> > >>>>>>>      		/* Some SPI masters need both GPIO CS &
> > >> slave_select */
> > >>>>>>>      		if ((spi->controller->flags &
> > >> SPI_CONTROLLER_GPIO_SS) &&
> > >>>>>>>      		    spi->controller->set_cs)
> > >>>>>>>      			spi->controller->set_cs(spi, !enable);
> > >>>>>>> +
> > >>>>>>> +		if (!spi->controller->set_cs_timing) {
> > >>>>>>> +			if (activate)
> > >>>>>>> +				spi_delay_exec(&spi->cs_setup,
> > >> NULL);
> > >>>>>>> +			else
> > >>>>>>> +				spi_delay_exec(&spi-
> >cs_inactive,
> > >> NULL);
> > >>>>>>> +		}
> > >>>>>>>      	} else if (spi->controller->set_cs) {
> > >>>>>>>      		spi->controller->set_cs(spi, !enable);
> > >>>>>>>      	}
> > >>>>>>> -
> > >>>>>>> -	if (spi_get_csgpiod(spi, 0) || !spi->controller-
> >set_cs_timing) {
> > >>>>>>> -		if (activate)
> > >>>>>>> -			spi_delay_exec(&spi->cs_setup, NULL);
> > >>>>>>> -		else
> > >>>>>>> -			spi_delay_exec(&spi->cs_inactive, NULL);
> > >>>>>>> -	}
> > >>>>>>>      }
> > >>>>>>>
> > >>>>>>>      #ifdef CONFIG_HAS_DMA
> > >>>>>>> @@ -2222,8 +2290,8 @@ static void
> > >>>>>>> of_spi_parse_dt_cs_delay(struct
> > >>>>>> device_node *nc,
> > >>>>>>>      static int of_spi_parse_dt(struct spi_controller *ctlr,
> > >>>>>>> struct spi_device
> > >>>> *spi,
> > >>>>>>>      			   struct device_node *nc)
> > >>>>>>>      {
> > >>>>>>> -	u32 value;
> > >>>>>>> -	int rc;
> > >>>>>>> +	u32 value, cs[SPI_CS_CNT_MAX];
> > >>>>>>> +	int rc, idx;
> > >>>>>>>
> > >>>>>>>      	/* Mode (clock phase/polarity/etc.) */
> > >>>>>>>      	if (of_property_read_bool(nc, "spi-cpha")) @@ -2295,14
> > >>>>>>> +2363,52
> > >>>>>> @@
> > >>>>>>> static int of_spi_parse_dt(struct spi_controller *ctlr, struct
> > >>>>>>> spi_device
> > >> *spi,
> > >>>>>>>      		return 0;
> > >>>>>>>      	}
> > >>>>>>>
> > >>>>>>> +	if (ctlr->num_chipselect > SPI_CS_CNT_MAX) {
> > >>>>>>> +		dev_err(&ctlr->dev, "No. of CS is more than max.
> no.
> > >> of
> > >>>>>> supported CS\n");
> > >>>>>>> +		return -EINVAL;
> > >>>>>>> +	}
> > >>>>>>> +
> > >>>>>>> +	/*
> > >>>>>>> +	 * Zero(0) is a valid physical CS value and can be located at
> > >> any
> > >>>>>>> +	 * logical CS in the spi->chip_select[]. If all the physical CS
> > >>>>>>> +	 * are initialized to 0 then It would be difficult to
> differentiate
> > >>>>>>> +	 * between a valid physical CS 0 & an unused logical CS
> whose
> > >>>>>> physical
> > >>>>>>> +	 * CS can be 0. As a solution to this issue initialize all the
> > >>>>>>> +CS to
> > >> 0xFF.
> > >>>>>>> +	 * Now all the unused logical CS will have 0xFF physical CS
> > >>>>>>> +value & can
> > >>>>>> be
> > >>>>>>> +	 * ignore while performing physical CS validity checks.
> > >>>>>>> +	 */
> > >>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> > >>>>>>> +		spi_set_chipselect(spi, idx, 0xFF);
> > >>>>>>> +
> > >>>>>>>      	/* Device address */
> > >>>>>>> -	rc = of_property_read_u32(nc, "reg", &value);
> > >>>>>>> -	if (rc) {
> > >>>>>>> +	rc = of_property_read_variable_u32_array(nc, "reg",
> &cs[0],
> > >> 1,
> > >>>>>>> +
> SPI_CS_CNT_MAX);
> > >>>>>>> +	if (rc < 0) {
> > >>>>>>>      		dev_err(&ctlr->dev, "%pOF has no valid 'reg'
> property
> > >>>>>> (%d)\n",
> > >>>>>>>      			nc, rc);
> > >>>>>>>      		return rc;
> > >>>>>>>      	}
> > >>>>>>> -	spi_set_chipselect(spi, 0, value);
> > >>>>>>> +	if (rc > ctlr->num_chipselect) {
> > >>>>>>> +		dev_err(&ctlr->dev, "%pOF has number of CS >
> ctlr-
> > >>>>>>> num_chipselect (%d)\n",
> > >>>>>>> +			nc, rc);
> > >>>>>>> +		return rc;
> > >>>>>>> +	}
> > >>>>>>> +	if ((of_property_read_bool(nc, "parallel-memories")) &&
> > >>>>>>> +	    (!(ctlr->flags & SPI_CONTROLLER_MULTI_CS))) {
> > >>>>>>> +		dev_err(&ctlr->dev, "SPI controller doesn't
> support
> > >> multi
> > >>>>>> CS\n");
> > >>>>>>> +		return -EINVAL;
> > >>>>>>> +	}
> > >>>>>>> +	for (idx = 0; idx < rc; idx++)
> > >>>>>>> +		spi_set_chipselect(spi, idx, cs[idx]);
> > >>>>>>> +
> > >>>>>>> +	/* spi->chip_select[i] gives the corresponding physical CS
> for
> > >>>>>>> +logical CS
> > >>>>>> i
> > >>>>>>> +	 * logical CS number is represented by setting the ith bit in
> > >>>>>>> +spi-
> > >>>>>>> cs_index_mask
> > >>>>>>> +	 * So, for example, if spi->cs_index_mask = 0x01 then
> logical
> > >> CS
> > >>>>>> number is 0 and
> > >>>>>>> +	 * spi->chip_select[0] will give the physical CS.
> > >>>>>>> +	 * By default spi->chip_select[0] will hold the physical CS
> > >>>>>>> +number so,
> > >>>>>> set
> > >>>>>>> +	 * spi->cs_index_mask as 0x01.
> > >>>>>>> +	 */
> > >>>>>>> +	spi->cs_index_mask = 0x01;
> > >>>>>>>
> > >>>>>>>      	/* Device speed */
> > >>>>>>>      	if (!of_property_read_u32(nc, "spi-max-frequency",
> > >>>>>>> &value)) @@
> > >>>>>>> -3100,6 +3206,7 @@ int spi_register_controller(struct
> > >>>>>>> spi_controller
> > >> *ctlr)
> > >>>>>>>      	struct boardinfo	*bi;
> > >>>>>>>      	int			first_dynamic;
> > >>>>>>>      	int			status;
> > >>>>>>> +	int			idx;
> > >>>>>>>
> > >>>>>>>      	if (!dev)
> > >>>>>>>      		return -ENODEV;
> > >>>>>>> @@ -3164,7 +3271,8 @@ int spi_register_controller(struct
> > >>>>>>> spi_controller
> > >>>>>> *ctlr)
> > >>>>>>>      	}
> > >>>>>>>
> > >>>>>>>      	/* Setting last_cs to -1 means no chip selected */
> > >>>>>>> -	ctlr->last_cs = -1;
> > >>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
> > >>>>>>> +		ctlr->last_cs[idx] = -1;
> > >>>>>>>
> > >>>>>>>      	status = device_add(&ctlr->dev);
> > >>>>>>>      	if (status < 0)
> > >>>>>>> @@ -3889,7 +3997,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_get_csgpiod(spi, 0)))
> {
> > >>>>>>> +					  spi_is_csgpiod(spi))) {
> > >>>>>>>      		size_t maxsize = BITS_TO_BYTES(spi-
> >bits_per_word);
> > >>>>>>>      		int ret;
> > >>>>>>>
> > >>>>>>> diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
> > >>>>>>> index 7b4baff63c5c..871d3a6d879b 100644
> > >>>>>>> --- a/include/linux/spi/spi.h
> > >>>>>>> +++ b/include/linux/spi/spi.h
> > >>>>>>> @@ -20,6 +20,9 @@
> > >>>>>>>
> > >>>>>>>      #include <uapi/linux/spi/spi.h>
> > >>>>>>>
> > >>>>>>> +/* Max no. of CS supported per spi device */ #define
> > >>>>>>> +SPI_CS_CNT_MAX
> > >>>>>>> +4
> > >>>>>>> +
> > >>>>>>>      struct dma_chan;
> > >>>>>>>      struct software_node;
> > >>>>>>>      struct ptp_system_timestamp; @@ -132,7 +135,8 @@
> extern
> > >>>>>>> void
> > >>>>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> > >>>>>>>       * @max_speed_hz: Maximum clock rate to be used with this
> chip
> > >>>>>>>       *	(on this board); may be changed by the device's driver.
> > >>>>>>>       *	The spi_transfer.speed_hz can override this for each
> transfer.
> > >>>>>>> - * @chip_select: Chipselect, distinguishing chips handled by
> > >> @controller.
> > >>>>>>> + * @chip_select: Array of physical chipselect, spi->chipselect[i]
> gives
> > >>>>>>> + *	the corresponding physical CS for logical CS i.
> > >>>>>>>       * @mode: The spi mode defines how data is clocked out and
> in.
> > >>>>>>>       *	This may be changed by the device's driver.
> > >>>>>>>       *	The "active low" default for chipselect mode can be
> > >> overridden
> > >>>>>>> @@ -157,8 +161,8 @@ extern void
> > >>>>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> > >>>>>>>       *	the device will bind to the named driver and only the
> named
> > >> driver.
> > >>>>>>>       *	Do not set directly, because core frees it; use
> > >> driver_set_override() to
> > >>>>>>>       *	set or clear it.
> > >>>>>>> - * @cs_gpiod: GPIO descriptor of the chipselect line (optional,
> > >>>>>>> NULL
> > >>>> when
> > >>>>>>> - *	not using a GPIO line)
> > >>>>>>> + * @cs_gpiod: Array of GPIO descriptors of the corresponding
> > >>>>>>> + chipselect
> > >>>>>> lines
> > >>>>>>> + *	(optional, NULL when not using a GPIO line)
> > >>>>>>>       * @word_delay: delay to be inserted between consecutive
> > >>>>>>>       *	words of a transfer
> > >>>>>>>       * @cs_setup: delay to be introduced by the controller
> > >>>>>>> after CS is asserted @@ -167,6 +171,7 @@ extern void
> > >>>>>> spi_transfer_cs_change_delay_exec(struct spi_message *msg,
> > >>>>>>>       *	deasserted. If @cs_change_delay is used from
> @spi_transfer,
> > >> then
> > >>>>>> the
> > >>>>>>>       *	two delays will be added up.
> > >>>>>>>       * @pcpu_statistics: statistics for the spi_device
> > >>>>>>> + * @cs_index_mask: Bit mask of the active chipselect(s) in the
> > >>>>>>> + chipselect array
> > >>>>>>>       *
> > >>>>>>>       * A @spi_device is used to interchange data between an SPI
> slave
> > >>>>>>>       * (usually a discrete chip) and CPU memory.
> > >>>>>>> @@ -182,7 +187,7 @@ struct spi_device {
> > >>>>>>>      	struct spi_controller	*controller;
> > >>>>>>>      	struct spi_controller	*master;	/* Compatibility
> layer
> > >> */
> > >>>>>>>      	u32			max_speed_hz;
> > >>>>>>> -	u8			chip_select;
> > >>>>>>> +	u8			chip_select[SPI_CS_CNT_MAX];
> > >>>>>>>      	u8			bits_per_word;
> > >>>>>>>      	bool			rt;
> > >>>>>>>      #define SPI_NO_TX		BIT(31)		/* No
> transmit wire */
> > >>>>>>> @@ -213,7 +218,7 @@ struct spi_device {
> > >>>>>>>      	void			*controller_data;
> > >>>>>>>      	char			modalias[SPI_NAME_SIZE];
> > >>>>>>>      	const char		*driver_override;
> > >>>>>>> -	struct gpio_desc	*cs_gpiod;	/* Chip select GPIO
> > descriptor
> > >>>>>> */
> > >>>>>>> +	struct gpio_desc	*cs_gpiod[SPI_CS_CNT_MAX];	/*
> > >> Chip select
> > >>>>>> gpio desc */
> > >>>>>>>      	struct spi_delay	word_delay; /* Inter-word delay */
> > >>>>>>>      	/* CS delays */
> > >>>>>>>      	struct spi_delay	cs_setup;
> > >>>>>>> @@ -223,6 +228,13 @@ struct spi_device {
> > >>>>>>>      	/* The statistics */
> > >>>>>>>      	struct spi_statistics __percpu	*pcpu_statistics;
> > >>>>>>>
> > >>>>>>> +	/* Bit mask of the chipselect(s) that the driver need to use
> > >> from
> > >>>>>>> +	 * the chipselect array.When the controller is capable to
> > >> handle
> > >>>>>>> +	 * multiple chip selects & memories are connected in
> parallel
> > >>>>>>> +	 * then more than one bit need to be set in
> cs_index_mask.
> > >>>>>>> +	 */
> > >>>>>>> +	u32			cs_index_mask :
> SPI_CS_CNT_MAX;
> > >>>>>>> +
> > >>>>>>>      	/*
> > >>>>>>>      	 * Likely need more hooks for more protocol options
> > >>>>>>> affecting
> > >> how
> > >>>>>>>      	 * the controller talks to each chip, like:
> > >>>>>>> @@ -279,22 +291,33 @@ static inline void
> *spi_get_drvdata(const
> > >>>>>>> struct spi_device *spi)
> > >>>>>>>
> > >>>>>>>      static inline u8 spi_get_chipselect(const struct spi_device
> *spi, u8
> > idx)
> > >>>>>>>      {
> > >>>>>>> -	return spi->chip_select;
> > >>>>>>> +	return spi->chip_select[idx];
> > >>>>>>>      }
> > >>>>>>>
> > >>>>>>>      static inline void spi_set_chipselect(struct spi_device
> > >>>>>>> *spi,
> > >>>>>>> u8 idx, u8
> > >>>>>> chipselect)
> > >>>>>>>      {
> > >>>>>>> -	spi->chip_select = chipselect;
> > >>>>>>> +	spi->chip_select[idx] = chipselect;
> > >>>>>>>      }
> > >>>>>>>
> > >>>>>>>      static inline struct gpio_desc *spi_get_csgpiod(const
> > >>>>>>> struct spi_device *spi,
> > >>>>>> u8 idx)
> > >>>>>>>      {
> > >>>>>>> -	return spi->cs_gpiod;
> > >>>>>>> +	return spi->cs_gpiod[idx];
> > >>>>>>>      }
> > >>>>>>>
> > >>>>>>>      static inline void spi_set_csgpiod(struct spi_device *spi,
> > >>>>>>> u8 idx, struct
> > >>>>>> gpio_desc *csgpiod)
> > >>>>>>>      {
> > >>>>>>> -	spi->cs_gpiod = csgpiod;
> > >>>>>>> +	spi->cs_gpiod[idx] = csgpiod;
> > >>>>>>> +}
> > >>>>>>> +
> > >>>>>>> +static inline bool spi_is_csgpiod(struct spi_device *spi) {
> > >>>>>>> +	u8 idx;
> > >>>>>>> +
> > >>>>>>> +	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
> > >>>>>>> +		if (spi_get_csgpiod(spi, idx))
> > >>>>>>> +			return true;
> > >>>>>>> +	}
> > >>>>>>> +	return false;
> > >>>>>>>      }
> > >>>>>>>
> > >>>>>>>      /**
> > >>>>>>> @@ -399,6 +422,8 @@ extern struct spi_device
> > >>>>>> *spi_new_ancillary_device(struct spi_device *spi, u8 ch
> > >>>>>>>       * @bus_lock_spinlock: spinlock for SPI bus locking
> > >>>>>>>       * @bus_lock_mutex: mutex for exclusion of multiple callers
> > >>>>>>>       * @bus_lock_flag: indicates that the SPI bus is locked for
> > >>>>>>> exclusive use
> > >>>>>>> + * @multi_cs_cap: indicates that the SPI Controller can
> > >>>>>>> + assert/de-
> > >> assert
> > >>>>>>> + *	more than one chip select at once.
> > >>>>>>>       * @setup: updates the device mode and clocking records
> used by
> > a
> > >>>>>>>       *	device's SPI controller; protocol code may call this.  This
> > >>>>>>>       *	must fail if an unrecognized or unsupported mode is
> > >> requested.
> > >>>>>>> @@ -567,6 +592,11 @@ struct spi_controller {
> > >>>>>>>      #define SPI_CONTROLLER_MUST_TX		BIT(4)
> 	/*
> > Requires tx
> > >>>> */
> > >>>>>>>      #define SPI_CONTROLLER_GPIO_SS		BIT(5)
> 	/* GPIO CS
> > >>>> must
> > >>>>>> select slave */
> > >>>>>>>      #define SPI_CONTROLLER_SUSPENDED	BIT(6)	/*
> Currently
> > >>>>>> suspended */
> > >>>>>>> +	/*
> > >>>>>>> +	 * The spi-controller has multi chip select capability and
> can
> > >>>>>>> +	 * assert/de-assert more than one chip select at once.
> > >>>>>>> +	 */
> > >>>>>>> +#define SPI_CONTROLLER_MULTI_CS		BIT(7)
> > >>>>>>>
> > >>>>>>>      	/* Flag indicating if the allocation of this struct is
> > >>>>>>> devres-
> > >> managed */
> > >>>>>>>      	bool			devm_allocated;
> > >>>>>>> @@ -677,7 +707,8 @@ struct spi_controller {
> > >>>>>>>      	bool				rt;
> > >>>>>>>      	bool				auto_runtime_pm;
> > >>>>>>>      	bool				cur_msg_mapped;
> > >>>>>>> -	char				last_cs;
> > >>>>>>> +	char
> 	last_cs[SPI_CS_CNT_MAX];
> > >>>>>>> +	char				last_cs_index_mask;
> > >>>>>>>      	bool				last_cs_mode_high;
> > >>>>>>>      	bool                            fallback;
> > >>>>>>>      	struct completion               xfer_completion;
diff mbox series

Patch

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 8ead7acb99f3..ff66147ba95f 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -612,10 +612,21 @@  static int spi_dev_check(struct device *dev, void *data)
 {
 	struct spi_device *spi = to_spi_device(dev);
 	struct spi_device *new_spi = data;
-
-	if (spi->controller == new_spi->controller &&
-	    spi_get_chipselect(spi, 0) == spi_get_chipselect(new_spi, 0))
-		return -EBUSY;
+	int idx, nw_idx;
+	u8 cs, cs_nw;
+
+	if (spi->controller == new_spi->controller) {
+		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+			cs = spi_get_chipselect(spi, idx);
+			for (nw_idx = 0; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
+				cs_nw = spi_get_chipselect(spi, nw_idx);
+				if (cs != 0xFF && cs_nw != 0xFF && cs == cs_nw) {
+					dev_err(dev, "chipselect %d already in use\n", cs_nw);
+					return -EBUSY;
+				}
+			}
+		}
+	}
 	return 0;
 }
 
@@ -629,13 +640,32 @@  static int __spi_add_device(struct spi_device *spi)
 {
 	struct spi_controller *ctlr = spi->controller;
 	struct device *dev = ctlr->dev.parent;
-	int status;
+	int status, idx, nw_idx;
+	u8 cs, nw_cs;
+
+	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+		/* Chipselects are numbered 0..max; validate. */
+		cs = spi_get_chipselect(spi, idx);
+		if (cs != 0xFF && cs >= ctlr->num_chipselect) {
+			dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, idx),
+				ctlr->num_chipselect);
+			return -EINVAL;
+		}
+	}
 
-	/* Chipselects are numbered 0..max; validate. */
-	if (spi_get_chipselect(spi, 0) >= ctlr->num_chipselect) {
-		dev_err(dev, "cs%d >= max %d\n", spi_get_chipselect(spi, 0),
-			ctlr->num_chipselect);
-		return -EINVAL;
+	/*
+	 * Make sure that multiple logical CS doesn't map to the same physical CS.
+	 * For example, spi->chip_select[0] != spi->chip_select[1] and so on.
+	 */
+	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+		cs = spi_get_chipselect(spi, idx);
+		for (nw_idx = idx + 1; nw_idx < SPI_CS_CNT_MAX; nw_idx++) {
+			nw_cs = spi_get_chipselect(spi, nw_idx);
+			if (cs != 0xFF && nw_cs != 0xFF && cs == nw_cs) {
+				dev_err(dev, "chipselect %d already in use\n", nw_cs);
+				return -EBUSY;
+			}
+		}
 	}
 
 	/* Set the bus ID string */
@@ -647,11 +677,8 @@  static int __spi_add_device(struct spi_device *spi)
 	 * its configuration.
 	 */
 	status = bus_for_each_dev(&spi_bus_type, NULL, spi, spi_dev_check);
-	if (status) {
-		dev_err(dev, "chipselect %d already in use\n",
-				spi_get_chipselect(spi, 0));
+	if (status)
 		return status;
-	}
 
 	/* Controller may unregister concurrently */
 	if (IS_ENABLED(CONFIG_SPI_DYNAMIC) &&
@@ -659,8 +686,15 @@  static int __spi_add_device(struct spi_device *spi)
 		return -ENODEV;
 	}
 
-	if (ctlr->cs_gpiods)
-		spi_set_csgpiod(spi, 0, ctlr->cs_gpiods[spi_get_chipselect(spi, 0)]);
+	if (ctlr->cs_gpiods) {
+		u8 cs;
+
+		for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+			cs = spi_get_chipselect(spi, idx);
+			if (cs != 0xFF)
+				spi_set_csgpiod(spi, idx, ctlr->cs_gpiods[cs]);
+		}
+	}
 
 	/*
 	 * Drivers may modify this initial i/o setup, but will
@@ -701,6 +735,9 @@  int spi_add_device(struct spi_device *spi)
 	struct spi_controller *ctlr = spi->controller;
 	int status;
 
+	/* Set the bus ID string */
+	spi_dev_set_name(spi);
+
 	mutex_lock(&ctlr->add_lock);
 	status = __spi_add_device(spi);
 	mutex_unlock(&ctlr->add_lock);
@@ -942,32 +979,51 @@  static void spi_res_release(struct spi_controller *ctlr, struct spi_message *mes
 }
 
 /*-------------------------------------------------------------------------*/
+static inline bool spi_is_last_cs(struct spi_device *spi)
+{
+	u8 idx;
+	bool last = false;
+
+	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+		if ((spi->cs_index_mask >> idx) & 0x01) {
+			if (spi->controller->last_cs[idx] == spi_get_chipselect(spi, idx))
+				last = true;
+		}
+	}
+	return last;
+}
+
 
 static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
 {
 	bool activate = enable;
+	u8 idx;
 
 	/*
 	 * Avoid calling into the driver (or doing delays) if the chip select
 	 * isn't actually changing from the last time this was called.
 	 */
-	if (!force && ((enable && spi->controller->last_cs == spi_get_chipselect(spi, 0)) ||
-		       (!enable && spi->controller->last_cs != spi_get_chipselect(spi, 0))) &&
+	if (!force && ((enable && spi->controller->last_cs_index_mask == spi->cs_index_mask &&
+			spi_is_last_cs(spi)) ||
+		       (!enable && spi->controller->last_cs_index_mask == spi->cs_index_mask &&
+			!spi_is_last_cs(spi))) &&
 	    (spi->controller->last_cs_mode_high == (spi->mode & SPI_CS_HIGH)))
 		return;
 
 	trace_spi_set_cs(spi, activate);
 
-	spi->controller->last_cs = enable ? spi_get_chipselect(spi, 0) : -1;
+	spi->controller->last_cs_index_mask = spi->cs_index_mask;
+	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
+		spi->controller->last_cs[idx] = enable ? spi_get_chipselect(spi, 0) : -1;
 	spi->controller->last_cs_mode_high = spi->mode & SPI_CS_HIGH;
 
-	if ((spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) && !activate)
-		spi_delay_exec(&spi->cs_hold, NULL);
-
 	if (spi->mode & SPI_CS_HIGH)
 		enable = !enable;
 
-	if (spi_get_csgpiod(spi, 0)) {
+	if (spi_is_csgpiod(spi)) {
+		if (!spi->controller->set_cs_timing && !activate)
+			spi_delay_exec(&spi->cs_hold, NULL);
+
 		if (!(spi->mode & SPI_NO_CS)) {
 			/*
 			 * Historically ACPI has no means of the GPIO polarity and
@@ -979,26 +1035,38 @@  static void spi_set_cs(struct spi_device *spi, bool enable, bool force)
 			 * ambiguity. That's why we use enable, that takes SPI_CS_HIGH
 			 * into account.
 			 */
-			if (has_acpi_companion(&spi->dev))
-				gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), !enable);
-			else
-				/* Polarity handled by GPIO library */
-				gpiod_set_value_cansleep(spi_get_csgpiod(spi, 0), activate);
+			for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+				if (((spi->cs_index_mask >> idx) & 0x01) &&
+				    spi_get_csgpiod(spi, idx)) {
+					if (has_acpi_companion(&spi->dev))
+						gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
+									 !enable);
+					else
+						/* Polarity handled by GPIO library */
+						gpiod_set_value_cansleep(spi_get_csgpiod(spi, idx),
+									 activate);
+
+					if (activate)
+						spi_delay_exec(&spi->cs_setup, NULL);
+					else
+						spi_delay_exec(&spi->cs_inactive, NULL);
+				}
+			}
 		}
 		/* Some SPI masters need both GPIO CS & slave_select */
 		if ((spi->controller->flags & SPI_CONTROLLER_GPIO_SS) &&
 		    spi->controller->set_cs)
 			spi->controller->set_cs(spi, !enable);
+
+		if (!spi->controller->set_cs_timing) {
+			if (activate)
+				spi_delay_exec(&spi->cs_setup, NULL);
+			else
+				spi_delay_exec(&spi->cs_inactive, NULL);
+		}
 	} else if (spi->controller->set_cs) {
 		spi->controller->set_cs(spi, !enable);
 	}
-
-	if (spi_get_csgpiod(spi, 0) || !spi->controller->set_cs_timing) {
-		if (activate)
-			spi_delay_exec(&spi->cs_setup, NULL);
-		else
-			spi_delay_exec(&spi->cs_inactive, NULL);
-	}
 }
 
 #ifdef CONFIG_HAS_DMA
@@ -2222,8 +2290,8 @@  static void of_spi_parse_dt_cs_delay(struct device_node *nc,
 static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
 			   struct device_node *nc)
 {
-	u32 value;
-	int rc;
+	u32 value, cs[SPI_CS_CNT_MAX];
+	int rc, idx;
 
 	/* Mode (clock phase/polarity/etc.) */
 	if (of_property_read_bool(nc, "spi-cpha"))
@@ -2295,14 +2363,52 @@  static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
 		return 0;
 	}
 
+	if (ctlr->num_chipselect > SPI_CS_CNT_MAX) {
+		dev_err(&ctlr->dev, "No. of CS is more than max. no. of supported CS\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Zero(0) is a valid physical CS value and can be located at any
+	 * logical CS in the spi->chip_select[]. If all the physical CS
+	 * are initialized to 0 then It would be difficult to differentiate
+	 * between a valid physical CS 0 & an unused logical CS whose physical
+	 * CS can be 0. As a solution to this issue initialize all the CS to 0xFF.
+	 * Now all the unused logical CS will have 0xFF physical CS value & can be
+	 * ignore while performing physical CS validity checks.
+	 */
+	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
+		spi_set_chipselect(spi, idx, 0xFF);
+
 	/* Device address */
-	rc = of_property_read_u32(nc, "reg", &value);
-	if (rc) {
+	rc = of_property_read_variable_u32_array(nc, "reg", &cs[0], 1,
+						 SPI_CS_CNT_MAX);
+	if (rc < 0) {
 		dev_err(&ctlr->dev, "%pOF has no valid 'reg' property (%d)\n",
 			nc, rc);
 		return rc;
 	}
-	spi_set_chipselect(spi, 0, value);
+	if (rc > ctlr->num_chipselect) {
+		dev_err(&ctlr->dev, "%pOF has number of CS > ctlr->num_chipselect (%d)\n",
+			nc, rc);
+		return rc;
+	}
+	if ((of_property_read_bool(nc, "parallel-memories")) &&
+	    (!(ctlr->flags & SPI_CONTROLLER_MULTI_CS))) {
+		dev_err(&ctlr->dev, "SPI controller doesn't support multi CS\n");
+		return -EINVAL;
+	}
+	for (idx = 0; idx < rc; idx++)
+		spi_set_chipselect(spi, idx, cs[idx]);
+
+	/* spi->chip_select[i] gives the corresponding physical CS for logical CS i
+	 * logical CS number is represented by setting the ith bit in spi->cs_index_mask
+	 * So, for example, if spi->cs_index_mask = 0x01 then logical CS number is 0 and
+	 * spi->chip_select[0] will give the physical CS.
+	 * By default spi->chip_select[0] will hold the physical CS number so, set
+	 * spi->cs_index_mask as 0x01.
+	 */
+	spi->cs_index_mask = 0x01;
 
 	/* Device speed */
 	if (!of_property_read_u32(nc, "spi-max-frequency", &value))
@@ -3100,6 +3206,7 @@  int spi_register_controller(struct spi_controller *ctlr)
 	struct boardinfo	*bi;
 	int			first_dynamic;
 	int			status;
+	int			idx;
 
 	if (!dev)
 		return -ENODEV;
@@ -3164,7 +3271,8 @@  int spi_register_controller(struct spi_controller *ctlr)
 	}
 
 	/* Setting last_cs to -1 means no chip selected */
-	ctlr->last_cs = -1;
+	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++)
+		ctlr->last_cs[idx] = -1;
 
 	status = device_add(&ctlr->dev);
 	if (status < 0)
@@ -3889,7 +3997,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_get_csgpiod(spi, 0))) {
+					  spi_is_csgpiod(spi))) {
 		size_t maxsize = BITS_TO_BYTES(spi->bits_per_word);
 		int ret;
 
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 7b4baff63c5c..871d3a6d879b 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -20,6 +20,9 @@ 
 
 #include <uapi/linux/spi/spi.h>
 
+/* Max no. of CS supported per spi device */
+#define SPI_CS_CNT_MAX 4
+
 struct dma_chan;
 struct software_node;
 struct ptp_system_timestamp;
@@ -132,7 +135,8 @@  extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg,
  * @max_speed_hz: Maximum clock rate to be used with this chip
  *	(on this board); may be changed by the device's driver.
  *	The spi_transfer.speed_hz can override this for each transfer.
- * @chip_select: Chipselect, distinguishing chips handled by @controller.
+ * @chip_select: Array of physical chipselect, spi->chipselect[i] gives
+ *	the corresponding physical CS for logical CS i.
  * @mode: The spi mode defines how data is clocked out and in.
  *	This may be changed by the device's driver.
  *	The "active low" default for chipselect mode can be overridden
@@ -157,8 +161,8 @@  extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg,
  *	the device will bind to the named driver and only the named driver.
  *	Do not set directly, because core frees it; use driver_set_override() to
  *	set or clear it.
- * @cs_gpiod: GPIO descriptor of the chipselect line (optional, NULL when
- *	not using a GPIO line)
+ * @cs_gpiod: Array of GPIO descriptors of the corresponding chipselect lines
+ *	(optional, NULL when not using a GPIO line)
  * @word_delay: delay to be inserted between consecutive
  *	words of a transfer
  * @cs_setup: delay to be introduced by the controller after CS is asserted
@@ -167,6 +171,7 @@  extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg,
  *	deasserted. If @cs_change_delay is used from @spi_transfer, then the
  *	two delays will be added up.
  * @pcpu_statistics: statistics for the spi_device
+ * @cs_index_mask: Bit mask of the active chipselect(s) in the chipselect array
  *
  * A @spi_device is used to interchange data between an SPI slave
  * (usually a discrete chip) and CPU memory.
@@ -182,7 +187,7 @@  struct spi_device {
 	struct spi_controller	*controller;
 	struct spi_controller	*master;	/* Compatibility layer */
 	u32			max_speed_hz;
-	u8			chip_select;
+	u8			chip_select[SPI_CS_CNT_MAX];
 	u8			bits_per_word;
 	bool			rt;
 #define SPI_NO_TX		BIT(31)		/* No transmit wire */
@@ -213,7 +218,7 @@  struct spi_device {
 	void			*controller_data;
 	char			modalias[SPI_NAME_SIZE];
 	const char		*driver_override;
-	struct gpio_desc	*cs_gpiod;	/* Chip select GPIO descriptor */
+	struct gpio_desc	*cs_gpiod[SPI_CS_CNT_MAX];	/* Chip select gpio desc */
 	struct spi_delay	word_delay; /* Inter-word delay */
 	/* CS delays */
 	struct spi_delay	cs_setup;
@@ -223,6 +228,13 @@  struct spi_device {
 	/* The statistics */
 	struct spi_statistics __percpu	*pcpu_statistics;
 
+	/* Bit mask of the chipselect(s) that the driver need to use from
+	 * the chipselect array.When the controller is capable to handle
+	 * multiple chip selects & memories are connected in parallel
+	 * then more than one bit need to be set in cs_index_mask.
+	 */
+	u32			cs_index_mask : SPI_CS_CNT_MAX;
+
 	/*
 	 * Likely need more hooks for more protocol options affecting how
 	 * the controller talks to each chip, like:
@@ -279,22 +291,33 @@  static inline void *spi_get_drvdata(const struct spi_device *spi)
 
 static inline u8 spi_get_chipselect(const struct spi_device *spi, u8 idx)
 {
-	return spi->chip_select;
+	return spi->chip_select[idx];
 }
 
 static inline void spi_set_chipselect(struct spi_device *spi, u8 idx, u8 chipselect)
 {
-	spi->chip_select = chipselect;
+	spi->chip_select[idx] = chipselect;
 }
 
 static inline struct gpio_desc *spi_get_csgpiod(const struct spi_device *spi, u8 idx)
 {
-	return spi->cs_gpiod;
+	return spi->cs_gpiod[idx];
 }
 
 static inline void spi_set_csgpiod(struct spi_device *spi, u8 idx, struct gpio_desc *csgpiod)
 {
-	spi->cs_gpiod = csgpiod;
+	spi->cs_gpiod[idx] = csgpiod;
+}
+
+static inline bool spi_is_csgpiod(struct spi_device *spi)
+{
+	u8 idx;
+
+	for (idx = 0; idx < SPI_CS_CNT_MAX; idx++) {
+		if (spi_get_csgpiod(spi, idx))
+			return true;
+	}
+	return false;
 }
 
 /**
@@ -399,6 +422,8 @@  extern struct spi_device *spi_new_ancillary_device(struct spi_device *spi, u8 ch
  * @bus_lock_spinlock: spinlock for SPI bus locking
  * @bus_lock_mutex: mutex for exclusion of multiple callers
  * @bus_lock_flag: indicates that the SPI bus is locked for exclusive use
+ * @multi_cs_cap: indicates that the SPI Controller can assert/de-assert
+ *	more than one chip select at once.
  * @setup: updates the device mode and clocking records used by a
  *	device's SPI controller; protocol code may call this.  This
  *	must fail if an unrecognized or unsupported mode is requested.
@@ -567,6 +592,11 @@  struct spi_controller {
 #define SPI_CONTROLLER_MUST_TX		BIT(4)	/* Requires tx */
 #define SPI_CONTROLLER_GPIO_SS		BIT(5)	/* GPIO CS must select slave */
 #define SPI_CONTROLLER_SUSPENDED	BIT(6)	/* Currently suspended */
+	/*
+	 * The spi-controller has multi chip select capability and can
+	 * assert/de-assert more than one chip select at once.
+	 */
+#define SPI_CONTROLLER_MULTI_CS		BIT(7)
 
 	/* Flag indicating if the allocation of this struct is devres-managed */
 	bool			devm_allocated;
@@ -677,7 +707,8 @@  struct spi_controller {
 	bool				rt;
 	bool				auto_runtime_pm;
 	bool				cur_msg_mapped;
-	char				last_cs;
+	char				last_cs[SPI_CS_CNT_MAX];
+	char				last_cs_index_mask;
 	bool				last_cs_mode_high;
 	bool                            fallback;
 	struct completion               xfer_completion;