diff mbox

[RFC,v2,06/10] spi: add support for ACPI reconfigure notifications

Message ID 1461105548-20618-7-git-send-email-octavian.purdila@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Octavian Purdila April 19, 2016, 10:39 p.m. UTC
This patch adds supports for SPI device enumeration and removal via
ACPI reconfiguration notifications that are send as a result of an
ACPI table load or unload operation.

Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>
---
 drivers/spi/spi.c | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 87 insertions(+), 7 deletions(-)

Comments

Mika Westerberg April 28, 2016, 3 p.m. UTC | #1
On Wed, Apr 20, 2016 at 01:39:04AM +0300, Octavian Purdila wrote:
> This patch adds supports for SPI device enumeration and removal via
> ACPI reconfiguration notifications that are send as a result of an
> ACPI table load or unload operation.
> 
> Signed-off-by: Octavian Purdila <octavian.purdila@intel.com>

Same here, pretty straightforward.

Reviewed-by: Mika Westerberg <mika.westerberg@linux.intel.com>
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mark Brown April 28, 2016, 5:42 p.m. UTC | #2
On Wed, Apr 20, 2016 at 01:39:04AM +0300, Octavian Purdila wrote:

> +	switch (value) {
> +	case ACPI_RECONFIG_DEVICE_ADD:
> +		master = acpi_spi_find_master_by_adev(adev->parent);
> +		if (!master)
> +			break;
> +
> +		acpi_register_spi_device(master, adev);
> +		put_device(&master->dev);
> +		break;
> +	case ACPI_RECONFIG_DEVICE_REMOVE:
> +		spi = acpi_spi_find_device_by_adev(adev);
> +		if (!spi)
> +			break;

There's more code here now than I remember but this all looks *really*
close to the DT code except for the OF_POPULATED flag that we set when
things are instantiated in DT.  The duplication seems bad but the fact
that we're missing the flag worries me...  do we have guarantees that
ACPI won't double register?
Octavian Purdila April 28, 2016, 7:37 p.m. UTC | #3
On Thu, Apr 28, 2016 at 8:42 PM, Mark Brown <broonie@kernel.org> wrote:
> On Wed, Apr 20, 2016 at 01:39:04AM +0300, Octavian Purdila wrote:
>
>> +     switch (value) {
>> +     case ACPI_RECONFIG_DEVICE_ADD:
>> +             master = acpi_spi_find_master_by_adev(adev->parent);
>> +             if (!master)
>> +                     break;
>> +
>> +             acpi_register_spi_device(master, adev);
>> +             put_device(&master->dev);
>> +             break;
>> +     case ACPI_RECONFIG_DEVICE_REMOVE:
>> +             spi = acpi_spi_find_device_by_adev(adev);
>> +             if (!spi)
>> +                     break;
>
> There's more code here now than I remember but this all looks *really*
> close to the DT code except for the OF_POPULATED flag that we set when
> things are instantiated in DT.  The duplication seems bad but the fact
> that we're missing the flag worries me...  do we have guarantees that
> ACPI won't double register?

We use the adev->flags.visited to check when a device has been already
enumerated, and we skip registering a new SPI slave in that case.
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Mark Brown May 3, 2016, 12:19 p.m. UTC | #4
On Thu, Apr 28, 2016 at 10:37:57PM +0300, Octavian Purdila wrote:
> On Thu, Apr 28, 2016 at 8:42 PM, Mark Brown <broonie@kernel.org> wrote:

> > There's more code here now than I remember but this all looks *really*
> > close to the DT code except for the OF_POPULATED flag that we set when
> > things are instantiated in DT.  The duplication seems bad but the fact
> > that we're missing the flag worries me...  do we have guarantees that
> > ACPI won't double register?

> We use the adev->flags.visited to check when a device has been already
> enumerated, and we skip registering a new SPI slave in that case.

OK, but the fact that I need to know that isn't exactly thrilling -
that's really the issue with the not quite duplication here.  It's not
just if the code works but also the maintainability.
Octavian Purdila May 4, 2016, 3:04 p.m. UTC | #5
On Tue, May 3, 2016 at 3:19 PM, Mark Brown <broonie@kernel.org> wrote:
> On Thu, Apr 28, 2016 at 10:37:57PM +0300, Octavian Purdila wrote:
>> On Thu, Apr 28, 2016 at 8:42 PM, Mark Brown <broonie@kernel.org> wrote:
>
>> > There's more code here now than I remember but this all looks *really*
>> > close to the DT code except for the OF_POPULATED flag that we set when
>> > things are instantiated in DT.  The duplication seems bad but the fact
>> > that we're missing the flag worries me...  do we have guarantees that
>> > ACPI won't double register?
>
>> We use the adev->flags.visited to check when a device has been already
>> enumerated, and we skip registering a new SPI slave in that case.
>
> OK, but the fact that I need to know that isn't exactly thrilling -
> that's really the issue with the not quite duplication here.  It's not
> just if the code works but also the maintainability.

I agree that this is unfortunate. I could not find a common path for
both device tree and ACPI. Perhaps when we remove of_node from struct
device and move to fwnode we could remove some duplication.

I can add a couple of comments around  acpi_device_enumerated() and
adev->flags.visited = true; to make it clear that we won't double
enumerate. Will that help?
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index de2f2f9..d6c5a9c 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1644,18 +1644,15 @@  static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)
 	return 1;
 }
 
-static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
-				       void *data, void **return_value)
+static acpi_status acpi_register_spi_device(struct spi_master *master,
+					    struct acpi_device *adev)
 {
-	struct spi_master *master = data;
 	struct list_head resource_list;
-	struct acpi_device *adev;
 	struct spi_device *spi;
 	int ret;
 
-	if (acpi_bus_get_device(handle, &adev))
-		return AE_OK;
-	if (acpi_bus_get_status(adev) || !adev->status.present)
+	if (acpi_bus_get_status(adev) || !adev->status.present ||
+	    acpi_device_enumerated(adev))
 		return AE_OK;
 
 	spi = spi_alloc_device(master);
@@ -1681,6 +1678,8 @@  static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
 	if (spi->irq < 0)
 		spi->irq = acpi_dev_gpio_irq_get(adev, 0);
 
+	adev->flags.visited = true;
+
 	adev->power.flags.ignore_parent = true;
 	strlcpy(spi->modalias, acpi_device_hid(adev), sizeof(spi->modalias));
 	if (spi_add_device(spi)) {
@@ -1693,6 +1692,18 @@  static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
 	return AE_OK;
 }
 
+static acpi_status acpi_spi_add_device(acpi_handle handle, u32 level,
+				       void *data, void **return_value)
+{
+	struct spi_master *master = data;
+	struct acpi_device *adev;
+
+	if (acpi_bus_get_device(handle, &adev))
+		return AE_OK;
+
+	return acpi_register_spi_device(master, adev);
+}
+
 static void acpi_register_spi_devices(struct spi_master *master)
 {
 	acpi_status status;
@@ -3104,6 +3115,73 @@  static struct notifier_block spi_of_notifier = {
 extern struct notifier_block spi_of_notifier;
 #endif /* IS_ENABLED(CONFIG_OF_DYNAMIC) */
 
+#if IS_ENABLED(CONFIG_ACPI)
+static int spi_acpi_master_match(struct device *dev, const void *data)
+{
+	return ACPI_COMPANION(dev->parent) == data;
+}
+
+static int spi_acpi_device_match(struct device *dev, void *data)
+{
+	return ACPI_COMPANION(dev) == data;
+}
+
+static struct spi_master *acpi_spi_find_master_by_adev(struct acpi_device *adev)
+{
+	struct device *dev;
+
+	dev = class_find_device(&spi_master_class, NULL, adev,
+				spi_acpi_master_match);
+	if (!dev)
+		return NULL;
+
+	return container_of(dev, struct spi_master, dev);
+}
+
+static struct spi_device *acpi_spi_find_device_by_adev(struct acpi_device *adev)
+{
+	struct device *dev;
+
+	dev = bus_find_device(&spi_bus_type, NULL, adev, spi_acpi_device_match);
+
+	return dev ? to_spi_device(dev) : NULL;
+}
+
+static int acpi_spi_notify(struct notifier_block *nb, unsigned long value,
+			   void *arg)
+{
+	struct acpi_device *adev = arg;
+	struct spi_master *master;
+	struct spi_device *spi;
+
+	switch (value) {
+	case ACPI_RECONFIG_DEVICE_ADD:
+		master = acpi_spi_find_master_by_adev(adev->parent);
+		if (!master)
+			break;
+
+		acpi_register_spi_device(master, adev);
+		put_device(&master->dev);
+		break;
+	case ACPI_RECONFIG_DEVICE_REMOVE:
+		spi = acpi_spi_find_device_by_adev(adev);
+		if (!spi)
+			break;
+
+		spi_unregister_device(spi);
+		put_device(&spi->dev);
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block spi_acpi_notifier = {
+	.notifier_call = acpi_spi_notify,
+};
+#else
+extern struct notifier_block spi_acpi_notifier;
+#endif
+
 static int __init spi_init(void)
 {
 	int	status;
@@ -3124,6 +3202,8 @@  static int __init spi_init(void)
 
 	if (IS_ENABLED(CONFIG_OF_DYNAMIC))
 		WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
+	if (IS_ENABLED(CONFIG_ACPI))
+		WARN_ON(acpi_reconfig_notifier_register(&spi_acpi_notifier));
 
 	return 0;