Re: [RFC PATCH] spi/acpi: enumerate all SPI slaves in the namespace

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

 



+Rafael, Jarkko and linux-acpi.

On Tue, May 28, 2019 at 06:40:40PM +0200, Ard Biesheuvel wrote:
> Currently, the ACPI enumeration that takes place when registering a
> SPI master only considers immediate child devices in the ACPI namespace,
> rather than checking the ResourceSource field in the SpiSerialBus()
> resource descriptor.
> 
> This is incorrect: SPI slaves could reside anywhere in the ACPI
> namespace, and so we should enumerate the entire namespace and look for
> any device that refers to the newly registered SPI master in its
> resource descriptor.

Yes, that's right.

> In order to prevent potential regressions, retain the old code and run
> it first. Only then, enumerate the entire namespace. Note that this
> could result in child devices wrongly being associated with a SPI master
> but this can only occur if a SPI master has a child device representing
> a SPI slave that is connected to another master.

I don't think we need to retain the old behavior here. We did the same
already for I2C and I don't remember seeing any regressions. Also I have
not seen too many real SPI slaves in ACPI based systems so most probably
nobody even notices this change ;-)

> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@xxxxxxxxxx>
> ---
>  drivers/spi/spi.c | 75 ++++++++++++++++++--
>  1 file changed, 69 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
> index 5e75944ad5d1..d2f4332d9cc3 100644
> --- a/drivers/spi/spi.c
> +++ b/drivers/spi/spi.c
> @@ -1882,10 +1882,6 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
>  	struct spi_device *spi;
>  	int ret;
>  
> -	if (acpi_bus_get_status(adev) || !adev->status.present ||
> -	    acpi_device_enumerated(adev))
> -		return AE_OK;
> -
>  	spi = spi_alloc_device(ctlr);
>  	if (!spi) {
>  		dev_err(&ctlr->dev, "failed to allocate SPI device for %s\n",
> @@ -1927,18 +1923,64 @@ static acpi_status acpi_register_spi_device(struct spi_controller *ctlr,
>  	return AE_OK;
>  }
>  
> +static acpi_status acpi_spi_add_child_device(acpi_handle handle, u32 level,
> +					     void *data, void **return_value)
> +{
> +	struct spi_controller *ctlr = data;
> +	struct acpi_device *adev;
> +
> +	if (acpi_bus_get_device(handle, &adev) ||
> +	    acpi_bus_get_status(adev) ||
> +	    !adev->status.present ||
> +	    acpi_device_enumerated(adev))
> +		return AE_OK;
> +
> +	return acpi_register_spi_device(ctlr, adev);
> +}
> +
> +static int acpi_spi_check_parent(struct acpi_resource *ares, void *data)
> +{
> +	struct acpi_resource_spi_serialbus *sb;
> +
> +	if (ares->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
> +		return 1;
> +
> +	sb = &ares->data.spi_serial_bus;
> +
> +	/* check whether this resource refers our controller */
> +	acpi_get_handle(NULL, sb->resource_source.string_ptr, data);
> +	return 1;
> +}
> +
>  static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
>  				       void *data, void **return_value)
>  {
>  	struct spi_controller *ctlr = data;
> +	acpi_handle parent_handle = NULL;
> +	struct list_head resource_list;
>  	struct acpi_device *adev;
> +	int ret;
>  
> -	if (acpi_bus_get_device(handle, &adev))
> +	if (acpi_bus_get_device(handle, &adev) ||
> +	    acpi_bus_get_status(adev) ||
> +	    !adev->status.present ||
> +	    acpi_device_enumerated(adev))
> +		return AE_OK;
> +
> +	INIT_LIST_HEAD(&resource_list);
> +	ret = acpi_dev_get_resources(adev, &resource_list,
> +				     acpi_spi_check_parent,
> +				     &parent_handle);
> +	acpi_dev_free_resource_list(&resource_list);
> +
> +	if (ret < 0 || ACPI_HANDLE(ctlr->dev.parent) != parent_handle)
>  		return AE_OK;
>  
>  	return acpi_register_spi_device(ctlr, adev);
>  }
>  
> +#define SPI_ACPI_ENUMERATE_MAX_DEPTH		32
> +
>  static void acpi_register_spi_devices(struct spi_controller *ctlr)
>  {
>  	acpi_status status;
> @@ -1948,10 +1990,31 @@ static void acpi_register_spi_devices(struct spi_controller *ctlr)
>  	if (!handle)
>  		return;
>  
> +	/*
> +	 * Enumerate child nodes of this controller. This logic is retained to
> +	 * prevent potential regressions on systems where the ResourceSource of
> +	 * a device's SpiSerialBus() resource does not refer to the parent SPI
> +	 * controller device.
> +	 */
>  	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
> +				     acpi_spi_add_child_device, NULL, ctlr,
> +				     NULL);
> +	if (ACPI_FAILURE(status)) {
> +		dev_warn(&ctlr->dev, "failed to enumerate SPI children\n");
> +		return;
> +	}
> +
> +	/*
> +	 * Walk the entire namespace and enumerate all devices containing a
> +	 * SpiSerialBus() resource whose ResourceSource argument refers to this
> +	 * SPI controller device.
> +	 */
> +	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
> +				     SPI_ACPI_ENUMERATE_MAX_DEPTH,
>  				     acpi_spi_add_device, NULL, ctlr, NULL);
>  	if (ACPI_FAILURE(status))
> -		dev_warn(&ctlr->dev, "failed to enumerate SPI slaves\n");
> +		dev_warn(&ctlr->dev,
> +			 "failed to enumerate SPI slaves in the ACPI namespace\n");
>  }
>  #else
>  static inline void acpi_register_spi_devices(struct spi_controller *ctlr) {}
> -- 
> 2.20.1



[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux