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

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

 



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.

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.

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 Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux