diff mbox series

[v8,03/10] ACPI: property: Parse _CRS CSI-2 descriptor

Message ID 20230329100951.1522322-4-sakari.ailus@linux.intel.com (mailing list archive)
State Superseded, archived
Headers show
Series ACPI _CRS CSI-2 and MIPI DisCo for Imaging support | expand

Commit Message

Sakari Ailus March 29, 2023, 10:09 a.m. UTC
Parse newly added ACPI _CRS CSI-2 descriptor for CSI-2 and camera
configuration, associate it with appropriate devices and allocate memory for
software nodes needed to create a DT-like data structure for drivers.

Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
---
 drivers/acpi/Makefile   |   2 +-
 drivers/acpi/internal.h |  27 ++++
 drivers/acpi/mipi.c     | 343 ++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/scan.c     |  50 ++++--
 include/acpi/acpi_bus.h |  11 ++
 5 files changed, 418 insertions(+), 15 deletions(-)
 create mode 100644 drivers/acpi/mipi.c

Comments

Rafael J. Wysocki May 15, 2023, 4:45 p.m. UTC | #1
On Wednesday, March 29, 2023 12:09:44 PM CEST Sakari Ailus wrote:
> Parse newly added ACPI _CRS CSI-2 descriptor for CSI-2 and camera
> configuration, associate it with appropriate devices and allocate memory for
> software nodes needed to create a DT-like data structure for drivers.

It occurred to me, that there would be so many things I would like to change
in this patch, so it would be better to create my own version of it, which
is appended.

It is based on

https://patchwork.kernel.org/project/linux-acpi/patch/2694293.mvXUDI8C0e@kreacher/

that has just been posted.

IIUC, the idea is to extract the ACPI handle for each resource source in every
_CRS CSI-2 resource descriptor and count how many times each handle appears in
a CSI-2 context, either because it is referenced from a _CRS CSI-2 resource
descriptor (as a "resource source"), or because its device object has CSI-2
resource descriptors in _CRS.

This allows a set of software nodes to be allocated for each of these handles.

If I got that totally wrong, please let me know.  Otherwise, I will rework the
remaining patches in the series to match this one.

Thanks!
---
From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Subject: [PATCH] ACPI: scan: Parse _CRS CSI-2 descriptors for all devices

Parse ACPI _CRS CSI-2 resource descriptors defined since ACPI 6.4 (for CSI-2 and
camera configuration) for all device objects in the ACPI namespace, find the
corresponding "remote" device objects for them and allocate memory for software
nodes needed to create a DT-like data structure for drivers (that will be taken
care of by subsequent patches).

Co-developed-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Sakari Ailus <sakari.ailus@linux.intel.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
 drivers/acpi/Makefile             |    2 
 drivers/acpi/internal.h           |    7 
 drivers/acpi/mipi-disco-imaging.c |  313 ++++++++++++++++++++++++++++++++++++++
 drivers/acpi/scan.c               |   49 ++++-
 include/acpi/acpi_bus.h           |   11 +
 5 files changed, 372 insertions(+), 10 deletions(-)
 create mode 100644 drivers/acpi/mipi-disco-imaging.c

Index: linux-pm/drivers/acpi/Makefile
===================================================================
--- linux-pm.orig/drivers/acpi/Makefile
+++ linux-pm/drivers/acpi/Makefile
@@ -37,7 +37,7 @@ acpi-$(CONFIG_ACPI_SLEEP)	+= proc.o
 # ACPI Bus and Device Drivers
 #
 acpi-y				+= bus.o glue.o
-acpi-y				+= scan.o
+acpi-y				+= scan.o mipi-disco-imaging.o
 acpi-y				+= resource.o
 acpi-y				+= acpi_processor.o
 acpi-y				+= processor_core.o
Index: linux-pm/drivers/acpi/internal.h
===================================================================
--- linux-pm.orig/drivers/acpi/internal.h
+++ linux-pm/drivers/acpi/internal.h
@@ -282,4 +282,11 @@ void acpi_init_lpit(void);
 static inline void acpi_init_lpit(void) { }
 #endif
 
+/*--------------------------------------------------------------------------
+		ACPI _CRS CSI-2 and MIPI DisCo for Imaging
+  -------------------------------------------------------------------------- */
+
+void acpi_mipi_check_crs_csi2(acpi_handle handle);
+void acpi_mipi_scan_crs_csi2(void);
+
 #endif /* _ACPI_INTERNAL_H_ */
Index: linux-pm/drivers/acpi/mipi-disco-imaging.c
===================================================================
--- /dev/null
+++ linux-pm/drivers/acpi/mipi-disco-imaging.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MIPI DisCo for Imaging support.
+ *
+ * Copyright (C) 2023 Intel Corporation
+ *
+ * Support MIPI DisCo for Imaging by parsing ACPI _CRS CSI-2 records defined in
+ * Section 6.4.3.8.2.4 "Camera Serial Interface (CSI-2) Connection Resource
+ * Descriptor" of ACPI 6.5.
+ *
+ * The implementation looks for the information in the ACPI namespace (CSI-2
+ * resource descriptors in _CRS) and constructs software nodes that are
+ * compatible with Documentation/firmware-guide/acpi/dsd/graph.rst.
+ */
+
+#include <linux/acpi.h>
+#include <linux/limits.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <linux/string.h>
+
+#include "internal.h"
+
+static LIST_HEAD(acpi_mipi_crs_csi2_list);
+
+/*
+ * List entry for the list of devices with software nodes allocated in
+ * accordance with the information from _CRS CSI-2 device desciptiors.
+ */
+struct crs_csi2_dev_swnodes {
+	struct list_head entry;
+	acpi_handle handle;
+	struct acpi_device_software_nodes *swnodes;
+};
+
+static LIST_HEAD(crs_csi2_swnodes);
+
+static void acpi_mipi_data_tag(acpi_handle handle, void *context)
+{
+}
+
+/* Connection data extracted from one _CRS CSI-2 resource descriptor. */
+struct crs_csi2_connection {
+	struct list_head entry;
+	struct acpi_resource_csi2_serialbus csi2_data;
+	acpi_handle remote_handle;
+	char remote_name[];
+};
+
+/* Data extracted from _CRS CSI-2 resource descriptors for one device. */
+struct crs_csi2 {
+	struct list_head entry;
+	acpi_handle handle;
+	struct list_head connections;
+	u32 port_count;
+};
+
+static acpi_status csi2_resource_extract(struct acpi_resource *res, void *context)
+{
+	struct crs_csi2 *csi2 = context;
+	struct acpi_resource_csi2_serialbus *csi2_res;
+	struct acpi_resource_source *csi2_res_src;
+	u16 csi2_res_src_length;
+	struct crs_csi2_connection *conn;
+	acpi_handle remote_handle;
+
+	if (res->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
+		return AE_OK;
+
+	csi2_res = &res->data.csi2_serial_bus;
+
+	if (csi2_res->type != ACPI_RESOURCE_SERIAL_TYPE_CSI2)
+		return AE_OK;
+
+	csi2_res_src = &csi2_res->resource_source;
+	if (ACPI_FAILURE(acpi_get_handle(NULL, csi2_res_src->string_ptr,
+					 &remote_handle))) {
+		acpi_handle_debug(csi2->handle,
+				  "unable to find resource source\n");
+		return AE_OK;
+	}
+	csi2_res_src_length = csi2_res_src->string_length;
+	if (!csi2_res_src_length) {
+		acpi_handle_debug(csi2->handle,
+				  "invalid resource source string length\n");
+		return AE_OK;
+	}
+
+	conn = kmalloc(struct_size(conn, remote_name, csi2_res_src_length + 1),
+		       GFP_KERNEL);
+	if (!conn)
+		return AE_OK;
+
+	conn->csi2_data = *csi2_res;
+	strscpy(conn->remote_name, csi2_res_src->string_ptr, csi2_res_src_length);
+	conn->csi2_data.resource_source.string_ptr = conn->remote_name;
+	conn->remote_handle = remote_handle;
+
+	list_add(&conn->entry, &csi2->connections);
+
+	return AE_OK;
+}
+
+static struct crs_csi2 *create_crs_csi2_entry(acpi_handle handle)
+{
+	struct crs_csi2 *csi2_entry;
+
+	csi2_entry = kzalloc(sizeof(*csi2_entry), GFP_KERNEL);
+	if (!csi2_entry)
+		return NULL;
+
+	csi2_entry->handle = handle;
+	INIT_LIST_HEAD(&csi2_entry->connections);
+	csi2_entry->port_count = 1;
+
+	if (ACPI_FAILURE(acpi_attach_data(handle, acpi_mipi_data_tag, csi2_entry))) {
+		kfree(csi2_entry);
+		return NULL;
+	}
+
+	return csi2_entry;
+}
+
+/**
+ * acpi_mipi_check_crs_csi2 - Look for devices with _CRS CSI-2 resources
+ * @handle: ACPI namespace walk starting point.
+ *
+ * Find all devices with _CRS CSI-2 resource descriptors in the ACPI namespace
+ * branch starting at @handle and collect them into a list.
+ */
+void acpi_mipi_check_crs_csi2(acpi_handle handle)
+{
+	struct crs_csi2 csi2 = {
+		.handle = handle,
+		.connections = LIST_HEAD_INIT(csi2.connections),
+	};
+	struct crs_csi2 *csi2_entry;
+
+	/*
+	 * Avoid creating _CRS CSI-2 list entries for devices without any CSI-2
+	 * resource descriptions in _CRS to reduce overhead.
+	 */
+	acpi_walk_resources(handle, METHOD_NAME__CRS, csi2_resource_extract, &csi2);
+	if (list_empty(&csi2.connections))
+		return;
+
+	/*
+	 * Create a _CRS CSI-2 entry to store the extracted connection
+	 * information and add it to the global list.
+	 */
+	csi2_entry = create_crs_csi2_entry(handle);
+	if (!csi2_entry)
+		return; /* There's nothing we really can do about this. */
+
+	list_replace(&csi2.connections, &csi2_entry->connections);
+	list_add(&csi2_entry->entry, &acpi_mipi_crs_csi2_list);
+}
+
+#define NO_CSI2_PORT (UINT_MAX - 1)
+
+static void alloc_fill_crs_csi2_swnodes(acpi_handle handle, size_t port_count)
+{
+	struct acpi_device_software_nodes *swnodes;
+	struct crs_csi2_dev_swnodes *swnodes_entry;
+	size_t alloc_size;
+	unsigned int i;
+	bool overflow;
+
+	/*
+	 * Allocate memory for ports, node pointers (number of nodes +
+	 * 1 (guardian), nodes (root + number of ports * 2 (because for
+	 * every port there is an endpoint)).
+	 */
+	overflow = check_mul_overflow(sizeof(*swnodes->ports) +
+				      sizeof(*swnodes->nodes) * 2 +
+				      sizeof(*swnodes->nodeptrs) * 2,
+				      port_count, &alloc_size);
+	overflow = overflow ||
+		   check_add_overflow(sizeof(*swnodes) +
+				      sizeof(*swnodes->nodes) +
+				      sizeof(*swnodes->nodeptrs) * 2,
+				      alloc_size, &alloc_size);
+	if (overflow) {
+		acpi_handle_warn(handle,
+				 "too many _CRS CSI-2 resource handles (%zu)",
+				 port_count);
+		return;
+	}
+
+	swnodes_entry = kzalloc(sizeof(*swnodes_entry), GFP_KERNEL);
+	swnodes = kmalloc(alloc_size, GFP_KERNEL);
+	if (!swnodes_entry || !swnodes)
+		goto out_free;
+
+	swnodes->ports = (struct acpi_device_software_node_port *)(swnodes + 1);
+	swnodes->nodes = (struct software_node *)(swnodes->ports + port_count);
+	swnodes->nodeptrs = (const struct software_node **)(swnodes->nodes + 1 +
+				2 * port_count);
+	swnodes->num_ports = port_count;
+
+	for (i = 0; i < 2 * port_count + 1; i++)
+		swnodes->nodeptrs[i] = &swnodes->nodes[i];
+
+	swnodes->nodeptrs[i] = NULL;
+
+	for (i = 0; i < port_count; i++)
+		swnodes->ports[i].port_nr = NO_CSI2_PORT;
+
+	swnodes_entry->handle = handle;
+	swnodes_entry->swnodes = swnodes;
+
+	list_add(&swnodes_entry->entry, &crs_csi2_swnodes);
+
+	return;
+
+out_free:
+	kfree(swnodes_entry);
+	kfree(swnodes);
+	acpi_handle_debug(handle, "unable to allocate for %zu software nodes\n",
+			  port_count);
+}
+
+static void acpi_mipi_crs_csi2_release(void)
+{
+	struct crs_csi2 *csi2, *csi2_tmp;
+
+	list_for_each_entry_safe(csi2, csi2_tmp, &acpi_mipi_crs_csi2_list, entry) {
+		struct crs_csi2_connection *conn, *conn_tmp;
+
+		list_for_each_entry_safe(conn, conn_tmp, &csi2->connections,
+					 entry) {
+			list_del(&conn->entry);
+			kfree(conn);
+		}
+
+		list_del(&csi2->entry);
+		acpi_detach_data(csi2->handle, acpi_mipi_data_tag);
+		kfree(csi2);
+	}
+}
+
+/**
+ * acpi_mipi_scan_crs_csi2 - Allocate ACPI _CRS CSI-2 software nodes
+ *
+ * Note that this function must be called before any struct acpi_device objects
+ * are bound to any ACPI drivers or scan handlers, so it cannot assume the
+ * existence of struct acpi_device objects for every device present in the ACPI
+ * namespace.
+ *
+ * acpi_scan_lock in scan.c must be held when calling this function.
+ */
+void acpi_mipi_scan_crs_csi2(void)
+{
+	struct crs_csi2 *csi2;
+	LIST_HEAD(aux_list);
+
+	/* Count references to each ACPI handle in the CSI-2 connection graph. */
+	list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) {
+		struct crs_csi2_connection *conn;
+
+		list_for_each_entry(conn, &csi2->connections, entry) {
+			struct crs_csi2 *remote_csi2;
+			acpi_status status;
+
+			status = acpi_get_data_full(conn->remote_handle,
+						    acpi_mipi_data_tag,
+						    (void **)&remote_csi2,
+						    NULL);
+			if (ACPI_SUCCESS(status) && remote_csi2) {
+				remote_csi2->port_count++;
+				continue;
+			}
+
+			/*
+			 * The "remote" device has no _CRS CSI-2 list entry yet,
+			 * so create one for it and add it to the list.
+			 */
+			remote_csi2 = create_crs_csi2_entry(conn->remote_handle);
+			if (!remote_csi2)
+				continue;
+
+			list_add(&remote_csi2->entry, &aux_list);
+		}
+	}
+	list_splice(&aux_list, &acpi_mipi_crs_csi2_list);
+
+	/* Allocate softwware nodes for representing the CSI-2 information. */
+	list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry)
+		alloc_fill_crs_csi2_swnodes(csi2->handle, csi2->port_count);
+
+	acpi_mipi_crs_csi2_release();
+}
+
+static void crs_csi2_swnode_del_free(struct crs_csi2_dev_swnodes *swnodes)
+{
+	list_del(&swnodes->entry);
+	kfree(swnodes);
+}
+
+/**
+ * acpi_mipi_swnodes_release - Free the list of devices with _CRS CSI-2 swnodes
+ */
+void acpi_mipi_swnodes_release(void)
+{
+	struct crs_csi2_dev_swnodes *sn, *sn_tmp;
+
+	list_for_each_entry_safe(sn, sn_tmp, &crs_csi2_swnodes, entry)
+		crs_csi2_swnode_del_free(sn);
+}
Index: linux-pm/include/acpi/acpi_bus.h
===================================================================
--- linux-pm.orig/include/acpi/acpi_bus.h
+++ linux-pm/include/acpi/acpi_bus.h
@@ -363,6 +363,17 @@ struct acpi_device_data {
 
 struct acpi_gpio_mapping;
 
+struct acpi_device_software_node_port {
+	unsigned int port_nr;
+};
+
+struct acpi_device_software_nodes {
+	struct software_node *nodes;
+	const struct software_node **nodeptrs;
+	struct acpi_device_software_node_port *ports;
+	unsigned int num_ports;
+};
+
 /* Device */
 struct acpi_device {
 	u32 pld_crc;
Index: linux-pm/drivers/acpi/scan.c
===================================================================
--- linux-pm.orig/drivers/acpi/scan.c
+++ linux-pm/drivers/acpi/scan.c
@@ -1970,7 +1970,7 @@ static void acpi_scan_init_hotplug(struc
 	}
 }
 
-static u32 acpi_scan_check_dep(acpi_handle handle, bool check_dep)
+static u32 acpi_scan_check_dep(acpi_handle handle)
 {
 	struct acpi_handle_list dep_devices;
 	acpi_status status;
@@ -1983,8 +1983,7 @@ static u32 acpi_scan_check_dep(acpi_hand
 	 * 2. ACPI nodes describing USB ports.
 	 * Still, checking for _HID catches more then just these cases ...
 	 */
-	if (!check_dep || !acpi_has_method(handle, "_DEP") ||
-	    !acpi_has_method(handle, "_HID"))
+	if (!acpi_has_method(handle, "_DEP") || !acpi_has_method(handle, "_HID"))
 		return 0;
 
 	status = acpi_evaluate_reference(handle, "_DEP", NULL, &dep_devices);
@@ -2029,7 +2028,16 @@ static u32 acpi_scan_check_dep(acpi_hand
 	return count;
 }
 
-static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
+static acpi_status acpi_scan_check_crs_csi2_cb(acpi_handle handle,
+					       u32 lvl_not_used,
+					       void *not_used,
+					       void **ret_p_not_used)
+{
+	acpi_mipi_check_crs_csi2(handle);
+	return AE_OK;
+}
+
+static acpi_status acpi_bus_check_add(acpi_handle handle, bool first_pass,
 				      struct acpi_device **adev_p)
 {
 	struct acpi_device *device = acpi_fetch_acpi_dev(handle);
@@ -2047,9 +2055,25 @@ static acpi_status acpi_bus_check_add(ac
 		if (acpi_device_should_be_hidden(handle))
 			return AE_OK;
 
-		/* Bail out if there are dependencies. */
-		if (acpi_scan_check_dep(handle, check_dep) > 0)
-			return AE_CTRL_DEPTH;
+		if (first_pass) {
+			acpi_mipi_check_crs_csi2(handle);
+
+			/* Bail out if there are dependencies. */
+			if (acpi_scan_check_dep(handle) > 0) {
+				/*
+				 * The entire CSI-2 connection graph needs to be
+				 * extracted before any drivers or scan handlers
+				 * are bound to struct device objects, so scan
+				 * _CRS CSI-2 resource descriptors for all
+				 * devices below the current handle.
+				 */
+				acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
+						    ACPI_UINT32_MAX,
+						    acpi_scan_check_crs_csi2_cb,
+						    NULL, NULL, NULL);
+				return AE_CTRL_DEPTH;
+			}
+		}
 
 		fallthrough;
 	case ACPI_TYPE_ANY:	/* for ACPI_ROOT_OBJECT */
@@ -2072,10 +2096,10 @@ static acpi_status acpi_bus_check_add(ac
 	}
 
 	/*
-	 * If check_dep is true at this point, the device has no dependencies,
+	 * If first_pass is true at this point, the device has no dependencies,
 	 * or the creation of the device object would have been postponed above.
 	 */
-	acpi_add_single_object(&device, handle, type, !check_dep);
+	acpi_add_single_object(&device, handle, type, !first_pass);
 	if (!device)
 		return AE_CTRL_DEPTH;
 
@@ -2487,6 +2511,13 @@ int acpi_bus_scan(acpi_handle handle)
 	if (!device)
 		return -ENODEV;
 
+	/*
+	 * Allocate ACPI _CRS CSI-2 software nodes using information extracted
+	 * from the _CRS CSI-2 resource descriptors during the ACPI namespace
+	 * walk above.
+	 */
+	acpi_mipi_scan_crs_csi2();
+
 	acpi_bus_attach(device, (void *)true);
 
 	/* Pass 2: Enumerate all of the remaining devices. */
Sakari Ailus May 16, 2023, 8:57 a.m. UTC | #2
Hi Rafael,

On Mon, May 15, 2023 at 06:45:10PM +0200, Rafael J. Wysocki wrote:
> On Wednesday, March 29, 2023 12:09:44 PM CEST Sakari Ailus wrote:
> > Parse newly added ACPI _CRS CSI-2 descriptor for CSI-2 and camera
> > configuration, associate it with appropriate devices and allocate memory for
> > software nodes needed to create a DT-like data structure for drivers.
> 
> It occurred to me, that there would be so many things I would like to change
> in this patch, so it would be better to create my own version of it, which
> is appended.
> 
> It is based on
> 
> https://patchwork.kernel.org/project/linux-acpi/patch/2694293.mvXUDI8C0e@kreacher/
> 
> that has just been posted.
> 
> IIUC, the idea is to extract the ACPI handle for each resource source in every
> _CRS CSI-2 resource descriptor and count how many times each handle appears in
> a CSI-2 context, either because it is referenced from a _CRS CSI-2 resource
> descriptor (as a "resource source"), or because its device object has CSI-2
> resource descriptors in _CRS.

Correct.

> 
> This allows a set of software nodes to be allocated for each of these handles.
> 
> If I got that totally wrong, please let me know.  Otherwise, I will rework the
> remaining patches in the series to match this one.

It seems about right. I mostly see renames, moving the code around,
using the existing dependency list and then parsing sub-tree for _CRS CSI-2
objects right from the bus scan callback. 

It seems you've also moved the structs from internal.h to what is now
called mipi-disco-imaging.c. They'll be later needed in e.g. scan.c. At
least I'd use names that indicate they're related to scanning the bus:
they're not needed after this is done.

I don't have objections to you reworking the rest, but given the number of
non-trivial changes, will it work after this? :-) I can also do this,
although I would un-do some of the changes in this patch in order to
prepare for the rest (such as moving the structs from internal.h).

See e.g. "ACPI: scan: Generate software nodes based on MIPI DisCo for
Imaging", I think it's the 6th patch.

...

> +/**
> + * acpi_mipi_scan_crs_csi2 - Allocate ACPI _CRS CSI-2 software nodes
> + *
> + * Note that this function must be called before any struct acpi_device objects
> + * are bound to any ACPI drivers or scan handlers, so it cannot assume the
> + * existence of struct acpi_device objects for every device present in the ACPI
> + * namespace.
> + *
> + * acpi_scan_lock in scan.c must be held when calling this function.
> + */
> +void acpi_mipi_scan_crs_csi2(void)
> +{
> +	struct crs_csi2 *csi2;
> +	LIST_HEAD(aux_list);
> +
> +	/* Count references to each ACPI handle in the CSI-2 connection graph. */
> +	list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry) {
> +		struct crs_csi2_connection *conn;
> +
> +		list_for_each_entry(conn, &csi2->connections, entry) {
> +			struct crs_csi2 *remote_csi2;
> +			acpi_status status;
> +
> +			status = acpi_get_data_full(conn->remote_handle,
> +						    acpi_mipi_data_tag,
> +						    (void **)&remote_csi2,
> +						    NULL);
> +			if (ACPI_SUCCESS(status) && remote_csi2) {
> +				remote_csi2->port_count++;
> +				continue;
> +			}
> +
> +			/*
> +			 * The "remote" device has no _CRS CSI-2 list entry yet,
> +			 * so create one for it and add it to the list.
> +			 */
> +			remote_csi2 = create_crs_csi2_entry(conn->remote_handle);
> +			if (!remote_csi2)
> +				continue;
> +
> +			list_add(&remote_csi2->entry, &aux_list);
> +		}
> +	}
> +	list_splice(&aux_list, &acpi_mipi_crs_csi2_list);
> +
> +	/* Allocate softwware nodes for representing the CSI-2 information. */

"software"

> +	list_for_each_entry(csi2, &acpi_mipi_crs_csi2_list, entry)
> +		alloc_fill_crs_csi2_swnodes(csi2->handle, csi2->port_count);
> +
> +	acpi_mipi_crs_csi2_release();
> +}
Rafael J. Wysocki May 16, 2023, 10:12 a.m. UTC | #3
Hi Sakari,

On Tue, May 16, 2023 at 10:57 AM Sakari Ailus
<sakari.ailus@linux.intel.com> wrote:
>
> Hi Rafael,
>
> On Mon, May 15, 2023 at 06:45:10PM +0200, Rafael J. Wysocki wrote:
> > On Wednesday, March 29, 2023 12:09:44 PM CEST Sakari Ailus wrote:
> > > Parse newly added ACPI _CRS CSI-2 descriptor for CSI-2 and camera
> > > configuration, associate it with appropriate devices and allocate memory for
> > > software nodes needed to create a DT-like data structure for drivers.
> >
> > It occurred to me, that there would be so many things I would like to change
> > in this patch, so it would be better to create my own version of it, which
> > is appended.
> >
> > It is based on
> >
> > https://patchwork.kernel.org/project/linux-acpi/patch/2694293.mvXUDI8C0e@kreacher/
> >
> > that has just been posted.
> >
> > IIUC, the idea is to extract the ACPI handle for each resource source in every
> > _CRS CSI-2 resource descriptor and count how many times each handle appears in
> > a CSI-2 context, either because it is referenced from a _CRS CSI-2 resource
> > descriptor (as a "resource source"), or because its device object has CSI-2
> > resource descriptors in _CRS.
>
> Correct.
>
> >
> > This allows a set of software nodes to be allocated for each of these handles.
> >
> > If I got that totally wrong, please let me know.  Otherwise, I will rework the
> > remaining patches in the series to match this one.
>
> It seems about right. I mostly see renames, moving the code around,
> using the existing dependency list and then parsing sub-tree for _CRS CSI-2
> objects right from the bus scan callback.
>
> It seems you've also moved the structs from internal.h to what is now
> called mipi-disco-imaging.c.

No, I haven't moved anything in this direction, I've just dropped them.

They can be added in the next patches if needed.

> They'll be later needed in e.g. scan.c. At
> least I'd use names that indicate they're related to scanning the bus:
> they're not needed after this is done.
>
> I don't have objections to you reworking the rest, but given the number of
> non-trivial changes, will it work after this? :-)

Probably not right from the start, but after some minor adjustments it
should work, unless I've missed something significant.

> I can also do this, although I would un-do some of the changes in this patch in order to
> prepare for the rest (such as moving the structs from internal.h).
>
> See e.g. "ACPI: scan: Generate software nodes based on MIPI DisCo for
> Imaging", I think it's the 6th patch.

I will.

Thanks!
Sakari Ailus May 16, 2023, 11:09 a.m. UTC | #4
Hi Rafael,

On Tue, May 16, 2023 at 12:12:13PM +0200, Rafael J. Wysocki wrote:
> Hi Sakari,
> 
> On Tue, May 16, 2023 at 10:57 AM Sakari Ailus
> <sakari.ailus@linux.intel.com> wrote:
> >
> > Hi Rafael,
> >
> > On Mon, May 15, 2023 at 06:45:10PM +0200, Rafael J. Wysocki wrote:
> > > On Wednesday, March 29, 2023 12:09:44 PM CEST Sakari Ailus wrote:
> > > > Parse newly added ACPI _CRS CSI-2 descriptor for CSI-2 and camera
> > > > configuration, associate it with appropriate devices and allocate memory for
> > > > software nodes needed to create a DT-like data structure for drivers.
> > >
> > > It occurred to me, that there would be so many things I would like to change
> > > in this patch, so it would be better to create my own version of it, which
> > > is appended.
> > >
> > > It is based on
> > >
> > > https://patchwork.kernel.org/project/linux-acpi/patch/2694293.mvXUDI8C0e@kreacher/
> > >
> > > that has just been posted.
> > >
> > > IIUC, the idea is to extract the ACPI handle for each resource source in every
> > > _CRS CSI-2 resource descriptor and count how many times each handle appears in
> > > a CSI-2 context, either because it is referenced from a _CRS CSI-2 resource
> > > descriptor (as a "resource source"), or because its device object has CSI-2
> > > resource descriptors in _CRS.
> >
> > Correct.
> >
> > >
> > > This allows a set of software nodes to be allocated for each of these handles.
> > >
> > > If I got that totally wrong, please let me know.  Otherwise, I will rework the
> > > remaining patches in the series to match this one.
> >
> > It seems about right. I mostly see renames, moving the code around,
> > using the existing dependency list and then parsing sub-tree for _CRS CSI-2
> > objects right from the bus scan callback.
> >
> > It seems you've also moved the structs from internal.h to what is now
> > called mipi-disco-imaging.c.
> 
> No, I haven't moved anything in this direction, I've just dropped them.

Ah, I think the struct was added to scan.c by the earlier patch. Indeed, by
doing the _CRS CSI2 scanning right from the device scanning callback, it's
probably possible to omit these.
diff mbox series

Patch

diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index a5b649e71ab1..a98fa1bc1554 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -37,7 +37,7 @@  acpi-$(CONFIG_ACPI_SLEEP)	+= proc.o
 # ACPI Bus and Device Drivers
 #
 acpi-y				+= bus.o glue.o
-acpi-y				+= scan.o
+acpi-y				+= scan.o mipi.o
 acpi-y				+= resource.o
 acpi-y				+= acpi_processor.o
 acpi-y				+= processor_core.o
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 06ad497067ac..aa5f9c69dbbe 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -102,6 +102,24 @@  struct acpi_device_bus_id {
 	struct list_head node;
 };
 
+/* Context for scanning ACPI device nodes for _CRS CSI2 resource descriptors */
+struct acpi_scan_context_csi2 {
+	struct list_head crs_csi2_head;
+	size_t handle_count;
+};
+
+/**
+ * struct acpi_scan_context - Context for scanning ACPI devices
+ * @postponed_head: The list head of the postponed ACPI handles
+ * @device: The first encountered device, typically the root of the scanned tree
+ * @csi2: Context for scanning _CRS CSI2 resource descriptors
+ */
+struct acpi_scan_context {
+	struct list_head postponed_head;
+	struct acpi_device *device;
+	struct acpi_scan_context_csi2 csi2;
+};
+
 void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
 			     int type, void (*release)(struct device *));
 int acpi_tie_acpi_dev(struct acpi_device *adev);
@@ -282,4 +300,13 @@  void acpi_init_lpit(void);
 static inline void acpi_init_lpit(void) { }
 #endif
 
+/*--------------------------------------------------------------------------
+			ACPI _CRS CSI2 and MIPI DisCo for Imaging conversion
+  -------------------------------------------------------------------------- */
+
+void acpi_crs_csi2_swnodes_del_free(void);
+void acpi_bus_scan_check_crs_csi2(acpi_handle handle, struct acpi_scan_context *ctx);
+void acpi_bus_scan_crs_csi2_release(struct list_head *crs_csi2_handles);
+void acpi_bus_scan_crs_csi2(struct acpi_scan_context_csi2 *ctx);
+
 #endif /* _ACPI_INTERNAL_H_ */
diff --git a/drivers/acpi/mipi.c b/drivers/acpi/mipi.c
new file mode 100644
index 000000000000..d719c879eb83
--- /dev/null
+++ b/drivers/acpi/mipi.c
@@ -0,0 +1,343 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MIPI DisCo for Imaging support.
+ *
+ * Support MIPI DisCo for Imaging by parsing ACPI _CRS CSI2 records and DisCo
+ * for Imaging data structures.
+ *
+ * Also see <URL:https://www.mipi.org/specifications/mipi-disco-imaging>.
+ *
+ * Copyright (C) 2023 Intel Corporation
+ */
+
+#include <linux/acpi.h>
+#include <linux/limits.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <linux/string.h>
+
+#include "internal.h"
+
+/* Temporary ACPI device handle to software node data structure mapping */
+struct crs_csi2_swnodes {
+	struct list_head list;
+	acpi_handle handle;
+	struct acpi_device_software_nodes *ads;
+};
+
+static LIST_HEAD(crs_csi2_swnodes);
+
+static void crs_csi2_swnode_del_free(struct crs_csi2_swnodes *swnodes)
+{
+	list_del(&swnodes->list);
+	kfree(swnodes);
+}
+
+void acpi_crs_csi2_swnodes_del_free(void)
+{
+	struct crs_csi2_swnodes *swnodes, *swnodes_tmp;
+
+	list_for_each_entry_safe(swnodes, swnodes_tmp, &crs_csi2_swnodes, list)
+		crs_csi2_swnode_del_free(swnodes);
+}
+
+/*
+ * Description of a _CRS CSI2 resource descriptor before software node creation
+ */
+struct crs_csi2_instance {
+	struct list_head list;
+	struct acpi_resource_csi2_serialbus csi2;
+	acpi_handle remote_handle;
+	char remote_name[];
+};
+
+/* Temporary list of ACPI device nodes with _CRS CSI2 resource descriptors */
+struct crs_csi2 {
+	struct list_head list;
+	acpi_handle handle;
+	struct list_head buses;
+};
+
+/*
+ * Context for collecting _CRS CSI2 resource descriptors during ACPI namespace
+ * walk
+ */
+struct scan_check_crs_csi2_context {
+	struct list_head res_head;
+	acpi_handle handle;
+	size_t handle_count;
+};
+
+/* Scan a single CSI2 resource descriptor in _CRS. */
+static acpi_status scan_check_crs_csi2_instance(struct acpi_resource *res,
+						void *context)
+{
+	struct scan_check_crs_csi2_context *inst_context = context;
+	struct acpi_resource_csi2_serialbus *csi2;
+	struct crs_csi2_instance *inst;
+	acpi_handle remote_handle;
+
+	if (res->type != ACPI_RESOURCE_TYPE_SERIAL_BUS)
+		return AE_OK;
+
+	csi2 = &res->data.csi2_serial_bus;
+
+	if (csi2->type != ACPI_RESOURCE_SERIAL_TYPE_CSI2)
+		return AE_OK;
+
+	if (!csi2->resource_source.string_length) {
+		acpi_handle_debug(inst_context->handle,
+				  "invalid resource source string length\n");
+		return AE_OK;
+	}
+
+	if (ACPI_FAILURE(acpi_get_handle(NULL, csi2->resource_source.string_ptr,
+					 &remote_handle))) {
+		acpi_handle_warn(inst_context->handle,
+				 "cannot get handle for %s\n",
+				 csi2->resource_source.string_ptr);
+		return AE_OK;
+	}
+
+	/*
+	 * Allocate space to store the _CRS CSI2 entry and its data and add it
+	 * to the list.
+	 */
+	inst = kmalloc(struct_size(inst, remote_name, csi2->resource_source.string_length),
+		       GFP_KERNEL);
+	if (!inst)
+		return AE_OK;
+
+	inst->csi2 = *csi2;
+	strscpy(inst->remote_name, csi2->resource_source.string_ptr,
+		csi2->resource_source.string_length);
+	inst->csi2.resource_source.string_ptr = inst->remote_name;
+	inst->remote_handle = remote_handle;
+
+	list_add(&inst->list, &inst_context->res_head);
+
+	inst_context->handle_count++;
+
+	return AE_OK;
+}
+
+/*
+ * Find all devices with _CRS CSI2 resource descriptors and collect them
+ * into a list for later use.
+ */
+void acpi_bus_scan_check_crs_csi2(acpi_handle handle, struct acpi_scan_context *ctx)
+{
+	struct scan_check_crs_csi2_context inst_context = {
+		.handle = handle,
+		.res_head = LIST_HEAD_INIT(inst_context.res_head),
+	};
+	struct crs_csi2 *csi2;
+
+	acpi_walk_resources(handle, METHOD_NAME__CRS,
+			    scan_check_crs_csi2_instance, &inst_context);
+
+	if (list_empty(&inst_context.res_head))
+		return;
+
+	/*
+	 * Found entry, so allocate memory for it, fill it and add it to the
+	 * list.
+	 */
+	csi2 = kmalloc(sizeof(*csi2), GFP_KERNEL);
+	if (!csi2)
+		return; /* There's nothing we really can do about this. */
+
+	csi2->handle = handle;
+	list_replace(&inst_context.res_head, &csi2->buses);
+	list_add(&csi2->list, &ctx->csi2.crs_csi2_head);
+
+	/* This handle plus remote handles in _CRS CSI2 resource descriptors */
+	ctx->csi2.handle_count += 1 + inst_context.handle_count;
+}
+
+struct acpi_handle_ref {
+	acpi_handle handle;
+	unsigned int count;
+};
+
+#define NO_CSI2_PORT (UINT_MAX - 1)
+
+static int crs_handle_cmp(const void *__a, const void *__b)
+{
+	const struct acpi_handle_ref *a = __a, *b = __b;
+
+	return a->handle < b->handle ? -1 : a->handle > b->handle;
+}
+
+/*
+ * Release entries in temporary storage of ACPI device nodes with _CRS CSI2
+ * resource descriptors.
+ */
+void acpi_bus_scan_crs_csi2_release(struct list_head *crs_csi2_handles)
+{
+	struct crs_csi2 *csi2, *csi2_tmp;
+
+	list_for_each_entry_safe(csi2, csi2_tmp, crs_csi2_handles, list) {
+		struct crs_csi2_instance *inst, *inst_tmp;
+
+		list_for_each_entry_safe(inst, inst_tmp, &csi2->buses, list) {
+			list_del(&inst->list);
+			kfree(inst);
+		}
+
+		list_del(&csi2->list);
+		kfree(csi2);
+	}
+}
+
+/*
+ * Allocate memory and set up software nodes for an ACPI device with given
+ * number of CSI-2 ports.
+ */
+static void acpi_crs_csi2_alloc_fill_swnodes(size_t ports_count, acpi_handle handle)
+{
+	struct acpi_device_software_nodes *ads;
+	struct crs_csi2_swnodes *swnodes;
+	size_t alloc_size;
+	unsigned int i;
+	bool overflow;
+	void *end;
+
+	/*
+	 * Allocate memory for ports, node pointers (number of nodes +
+	 * 1 (guardian), nodes (root + number of ports * 2 (because for
+	 * every port there is an endpoint)).
+	 */
+	overflow = check_mul_overflow(sizeof(*ads->ports) +
+				      sizeof(*ads->nodes) * 2 +
+				      sizeof(*ads->nodeptrs) * 2,
+				      ports_count, &alloc_size);
+	overflow = overflow ||
+		   check_add_overflow(sizeof(*ads) + sizeof(*ads->nodes) +
+				      sizeof(*ads->nodeptrs) * 2,
+				      alloc_size, &alloc_size);
+	if (overflow) {
+		acpi_handle_warn(handle,
+				 "too many _CRS CSI2 resource handles (%zu)",
+				 ports_count);
+		return;
+	}
+
+	swnodes = kzalloc(sizeof(*swnodes), GFP_KERNEL);
+	ads = kmalloc(alloc_size, GFP_KERNEL);
+	if (!swnodes || !ads)
+		goto out_free;
+
+	ads->ports = (void *)(ads + 1);
+	ads->nodes = (void *)(ads->ports + ports_count);
+	ads->nodeptrs = (void *)(ads->nodes + ports_count * 2 + 1);
+	end = ads->nodeptrs + ports_count * 2 + 2;
+	if (WARN_ON((void *)ads + alloc_size != end))
+		goto out_free;
+
+	ads->num_ports = ports_count;
+	for (i = 0; i < ports_count * 2 + 1; i++)
+		ads->nodeptrs[i] = &ads->nodes[i];
+	ads->nodeptrs[i] = NULL;
+	for (i = 0; i < ports_count; i++)
+		ads->ports[i].port_nr = NO_CSI2_PORT;
+	swnodes->handle = handle;
+	swnodes->ads = ads;
+	list_add(&swnodes->list, &crs_csi2_swnodes);
+
+	return;
+
+out_free:
+	kfree(swnodes);
+	kfree(ads);
+	acpi_handle_debug(handle, "cannot allocate for %zu software nodes\n",
+			  ports_count);
+}
+
+/**
+ * acpi_bus_scan_crs_csi2 - Construct software nodes out of ACPI _CRS CSI2
+ *			    resource descriptors
+ * @ctx: ACPI _CRS CSI2 context, gathered during tree walk earlier
+ *
+ * This function does a number of things:
+ *
+ * 1. Count how many references to other devices _CRS CSI-2 instances have in
+ *    total.
+ *
+ * 2. Count the number of references to other devices for each _CRS CSI-2
+ *    instance.
+ *
+ * 3. Allocate memory for swnodes each ACPI device requires later on, and
+ *    generate a list of such allocations.
+ *
+ * Note that struct acpi_device may not be available yet at this time.
+ *
+ * acpi_scan_lock in scan.c must be held when calling this function.
+ */
+void acpi_bus_scan_crs_csi2(struct acpi_scan_context_csi2 *ctx)
+{
+	struct acpi_handle_ref *handle_refs;
+	struct acpi_handle_ref *this = NULL;
+	size_t this_count;
+	unsigned int curr = 0;
+	struct crs_csi2 *csi2;
+
+	/* No handles? Bail out here. */
+	if (!ctx->handle_count)
+		return;
+
+	handle_refs = kcalloc(ctx->handle_count + 1, sizeof(*handle_refs),
+			      GFP_KERNEL);
+	if (!handle_refs) {
+		pr_debug("no memory for %zu handle refs\n",
+			 ctx->handle_count + 1);
+		return;
+	}
+
+	/* Associate handles to the number of references. */
+	list_for_each_entry(csi2, &ctx->crs_csi2_head, list) {
+		struct crs_csi2_instance *inst;
+		struct acpi_handle_ref *handle_ref;
+
+		handle_ref = &handle_refs[curr++];
+		handle_ref->handle = csi2->handle;
+
+		list_for_each_entry(inst, &csi2->buses, list) {
+			handle_refs[curr].handle = inst->remote_handle;
+			handle_refs[curr].count = 1;
+			handle_ref->count++;
+			curr++;
+		}
+	}
+
+	handle_refs[curr].handle = NULL;
+
+	/* Sort the handles by remote so duplicates canbe easily found. */
+	sort(handle_refs, ctx->handle_count, sizeof(*handle_refs), crs_handle_cmp, NULL);
+
+	/*
+	 * Finally count references in each handle, allocate space for device
+	 * specific ports, properties and fill the _CRS CSI2 descriptor
+	 * originated data.
+	 */
+	this = handle_refs;
+	this_count = this->count;
+	for (curr = 1; curr < ctx->handle_count + 1; curr++) {
+		/* Weed out duplicate receiver devices. */
+		if (this->handle == handle_refs[curr].handle) {
+			this_count += handle_refs[curr].count;
+			continue;
+		}
+
+		acpi_crs_csi2_alloc_fill_swnodes(this_count, this->handle);
+
+		this = &handle_refs[curr];
+		this_count = this->count;
+	}
+
+	kfree(handle_refs);
+}
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 280d12e0aa2f..ddf7701a57d0 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2042,16 +2042,6 @@  struct acpi_postponed_handle {
 	acpi_handle handle;
 };
 
-/**
- * struct acpi_scan_context - Context for scanning ACPI devices
- * @postponed_head: The list head of the postponed ACPI handles
- * @device: The first encountered device, typically the root of the scanned tree
- */
-struct acpi_scan_context {
-	struct list_head postponed_head;
-	struct acpi_device *device;
-};
-
 /**
  * acpi_bus_handle_postpone - Add an ACPI handle to a given postponed list
  * @handle: The ACPI handle
@@ -2073,7 +2063,7 @@  static void acpi_bus_handle_postpone(acpi_handle handle,
 	list_add(&ph->list, head);
 }
 
-static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
+static acpi_status acpi_bus_check_add(acpi_handle handle, bool first_pass,
 				      struct acpi_scan_context *ctx)
 {
 	struct acpi_device *device = acpi_fetch_acpi_dev(handle);
@@ -2091,8 +2081,11 @@  static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
 		if (acpi_device_should_be_hidden(handle))
 			return AE_OK;
 
+		if (first_pass)
+			acpi_bus_scan_check_crs_csi2(handle, ctx);
+
 		/* Bail out if there are dependencies. */
-		if (acpi_scan_check_dep(handle, check_dep) > 0) {
+		if (acpi_scan_check_dep(handle, first_pass) > 0) {
 			acpi_bus_handle_postpone(handle, &ctx->postponed_head);
 			return AE_CTRL_DEPTH;
 		}
@@ -2118,10 +2111,10 @@  static acpi_status acpi_bus_check_add(acpi_handle handle, bool check_dep,
 	}
 
 	/*
-	 * If check_dep is true at this point, the device has no dependencies,
+	 * If first_pass is true at this point, the device has no dependencies,
 	 * or the creation of the device object would have been postponed above.
 	 */
-	acpi_add_single_object(&device, handle, type, !check_dep);
+	acpi_add_single_object(&device, handle, type, !first_pass);
 	if (!device)
 		return AE_CTRL_DEPTH;
 
@@ -2146,6 +2139,14 @@  static acpi_status acpi_bus_check_add_2(acpi_handle handle, u32 lvl_not_used,
 	return acpi_bus_check_add(handle, false, (struct acpi_scan_context *)ctx);
 }
 
+static acpi_status acpi_bus_check_csi2(acpi_handle handle, u32 lvl_not_used,
+				       void *ctx, void **unused)
+{
+	acpi_bus_scan_check_crs_csi2(handle, ctx);
+
+	return AE_OK;
+}
+
 static void acpi_default_enumeration(struct acpi_device *device)
 {
 	/*
@@ -2466,6 +2467,7 @@  int acpi_bus_scan(acpi_handle handle)
 {
 	struct acpi_scan_context ctx = {
 		.postponed_head = LIST_HEAD_INIT(ctx.postponed_head),
+		.csi2.crs_csi2_head = LIST_HEAD_INIT(ctx.csi2.crs_csi2_head),
 	};
 	struct acpi_postponed_handle *ph, *tmp_ph;
 	int ret = 0;
@@ -2480,6 +2482,24 @@  int acpi_bus_scan(acpi_handle handle)
 		goto out_release;
 	}
 
+	/*
+	 * Check _CRS CSI2 resource descriptors for devices that were not
+	 * traversed during the previous pass.
+	 */
+	list_for_each_entry_safe(ph, tmp_ph, &ctx.postponed_head, list) {
+		acpi_bus_check_csi2(ph->handle, 0, &ctx, NULL);
+		acpi_walk_namespace(ACPI_TYPE_DEVICE, ph->handle, ACPI_UINT32_MAX,
+				    acpi_bus_check_csi2, NULL, (void *)&ctx,
+				    NULL);
+	}
+
+	/*
+	 * Construct software nodes out of _CRS CSI2 records and release the
+	 * _CRS CSI2 records.
+	 */
+	acpi_bus_scan_crs_csi2(&ctx.csi2);
+	acpi_bus_scan_crs_csi2_release(&ctx.csi2.crs_csi2_head);
+
 	acpi_bus_attach(ctx.device, (void *)true);
 
 	/*
@@ -2511,6 +2531,8 @@  int acpi_bus_scan(acpi_handle handle)
 		kfree(ph);
 	}
 
+	acpi_crs_csi2_swnodes_del_free();
+
 	return ret;
 }
 EXPORT_SYMBOL(acpi_bus_scan);
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 57acb895c038..e4e8faac9212 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -360,6 +360,17 @@  struct acpi_device_data {
 
 struct acpi_gpio_mapping;
 
+struct acpi_device_software_node_port {
+	unsigned int port_nr;
+};
+
+struct acpi_device_software_nodes {
+	struct acpi_device_software_node_port *ports;
+	struct software_node *nodes;
+	const struct software_node **nodeptrs;
+	unsigned int num_ports;
+};
+
 /* Device */
 struct acpi_device {
 	u32 pld_crc;