diff mbox series

[v4,2/8] ACPI: property: Parse _CRS CSI-2 descriptor

Message ID 20230208152807.3064242-3-sakari.ailus@linux.intel.com
State New
Headers show
Series ACPI _CRS CSI-2 and MIPI DisCo for Imaging support | expand

Commit Message

Sakari Ailus Feb. 8, 2023, 3:28 p.m. UTC
Parse newly added ACPI _CRS CSI-2 descriptor for CSI-2 and camera
configuration. For now, only figure out where the descriptor is present in
order to allow adding information from it to related devices.

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

Comments

Sakari Ailus Feb. 8, 2023, 8:26 p.m. UTC | #1
Hi Andy,

Thanks for the review.

On Wed, Feb 08, 2023 at 06:43:36PM +0200, Andy Shevchenko wrote:
> On Wed, Feb 08, 2023 at 05:28:01PM +0200, Sakari Ailus wrote:
> > Parse newly added ACPI _CRS CSI-2 descriptor for CSI-2 and camera
> > configuration. For now, only figure out where the descriptor is present in
> > order to allow adding information from it to related devices.
> 
> Nit-picks below that may be ignored. Up to you, guys.
> 
> ...
> 
> > +#define NO_CSI2_PORT (~1U)
> 
> A bit unclear why this value. Is it bitfield? Then GENMASK() would be better.
> Is it a plain value with a type limit? Then (UINT_MAX - 1) probably the best.

This value is used to signify that a port node isn't yet allocated for a
CSI-2 port. I can change this to UINT_MAX - 1.

> 
> ...
> 
> > +	ads->nodeptrs = (void *)(ads->nodes +
> > +				 ports_count * 2 + 1);
> 
> Now this fits one line.

Yes.

> 
> ...
> 
> > +	handle_refs = kcalloc(csi2_all.handle_count + 1, sizeof(*handle_refs),
> > +			      GFP_KERNEL);
> > +	if (!handle_refs) {
> > +		acpi_handle_debug(handle, "no memory for %zu handle refs\n",
> > +				  csi2_all.handle_count + 1);
> > +		return;
> > +	}
> 
> In a code above you used "1 + foo" approach if I'm not mistaken. Why here is
> the difference?

The last entry is a guardian (NULL handle). 1 + number of lanes is used
where the first lane is the clock lane.

> 
> ...
> 
> In some code comments which I removed before remarking you forgot the
> grammatical period.

Some comments aren't proper sentences. I'll see which could be improved by
adding a period.
kernel test robot Feb. 8, 2023, 9:04 p.m. UTC | #2
Hi Sakari,

I love your patch! Perhaps something to improve:

[auto build test WARNING on rafael-pm/linux-next]
[also build test WARNING on sailus-media-tree/streams linus/master v6.2-rc7 next-20230208]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Sakari-Ailus/ACPI-property-Parse-data-node-string-references-in-properties/20230208-233112
base:   https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
patch link:    https://lore.kernel.org/r/20230208152807.3064242-3-sakari.ailus%40linux.intel.com
patch subject: [PATCH v4 2/8] ACPI: property: Parse _CRS CSI-2 descriptor
config: x86_64-rhel-8.3-syz (https://download.01.org/0day-ci/archive/20230209/202302090459.kIM95vle-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.3.0-8) 11.3.0
reproduce (this is a W=1 build):
        # https://github.com/intel-lab-lkp/linux/commit/d78f47f2d5051c50bdcea131da1779ec0fc8e266
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Sakari-Ailus/ACPI-property-Parse-data-node-string-references-in-properties/20230208-233112
        git checkout d78f47f2d5051c50bdcea131da1779ec0fc8e266
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 O=build_dir ARCH=x86_64 olddefconfig
        make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/acpi/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Link: https://lore.kernel.org/oe-kbuild-all/20230208152807.3064242-3-sakari.ailus@linux.intel.com

All warnings (new ones prefixed by >>):

>> drivers/acpi/mipi.c:205:6: warning: no previous prototype for 'acpi_crs_csi2_alloc_fill_swnodes' [-Wmissing-prototypes]
     205 | void acpi_crs_csi2_alloc_fill_swnodes(size_t ports_count, acpi_handle handle)
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


vim +/acpi_crs_csi2_alloc_fill_swnodes +205 drivers/acpi/mipi.c

   200	
   201	/*
   202	 * Allocate memory and set up software nodes for an ACPI device with given
   203	 * number of CSI-2 ports.
   204	 */
 > 205	void acpi_crs_csi2_alloc_fill_swnodes(size_t ports_count, acpi_handle handle)
   206	{
   207		struct acpi_device_software_nodes *ads;
   208		struct crs_csi2_swnodes *swnodes;
   209		size_t alloc_size;
   210		unsigned int i;
   211		bool overflow;
   212		void *end;
   213	
   214		/*
   215		 * Allocate memory for ports, node pointers (number of nodes +
   216		 * 1 (guardian), nodes (root + number of ports * 2 (for for
   217		 * every port there is an endpoint)).
   218		 */
   219		overflow = check_mul_overflow(sizeof(*ads->ports) +
   220					      sizeof(*ads->nodes) * 2 +
   221					      sizeof(*ads->nodeptrs) * 2,
   222					      ports_count, &alloc_size);
   223		overflow = overflow ||
   224			   check_add_overflow(sizeof(*ads) + sizeof(*ads->nodes) +
   225					      sizeof(*ads->nodeptrs) * 2,
   226					      alloc_size, &alloc_size);
   227		if (overflow) {
   228			acpi_handle_warn(handle,
   229					 "too many _CRS CSI2 resource handles (%zu)",
   230					 ports_count);
   231			return;
   232		}
   233	
   234		swnodes = kzalloc(sizeof(*swnodes), GFP_KERNEL);
   235		ads = kzalloc(alloc_size, GFP_KERNEL);
   236		ads->ports = (void *)(ads + 1);
   237		ads->nodes = (void *)(ads->ports + ports_count);
   238		ads->nodeptrs = (void *)(ads->nodes +
   239					 ports_count * 2 + 1);
   240		end = ads->nodeptrs + ports_count * 2 + 2;
   241		if (!swnodes || !ads || WARN_ON((void *)ads + alloc_size != end)) {
   242			kfree(swnodes);
   243			kfree(ads);
   244			acpi_handle_debug(handle,
   245					  "cannot allocate for %zu software nodes\n",
   246					  ports_count);
   247			return;
   248		}
   249	
   250		ads->num_ports = ports_count;
   251		for (i = 0; i < ports_count * 2 + 1; i++)
   252			ads->nodeptrs[i] = &ads->nodes[i];
   253		ads->nodeptrs[i] = NULL;
   254		for (i = 0; i < ports_count; i++)
   255			ads->ports[i].port_nr = NO_CSI2_PORT;
   256		swnodes->handle = handle;
   257		swnodes->ads = ads;
   258		list_add(&swnodes->list, &crs_csi2_swnodes);
   259	}
   260
Sakari Ailus Feb. 8, 2023, 10:16 p.m. UTC | #3
On Thu, Feb 09, 2023 at 04:43:45AM +0800, kernel test robot wrote:
> >> drivers/acpi/mipi.c:205:6: warning: no previous prototype for 'acpi_crs_csi2_alloc_fill_swnodes' [-Wmissing-prototypes]
>      205 | void acpi_crs_csi2_alloc_fill_swnodes(size_t ports_count, acpi_handle handle)

I was wondering what wast the actual problem here as having no prototype
isn't an issue really. The function should have been static, of course.
I'll address this in v6, but waiting for other comments for now.
Philip Li Feb. 9, 2023, 2:43 a.m. UTC | #4
On Thu, Feb 09, 2023 at 05:04:11AM +0800, kernel test robot wrote:
> Hi Sakari,
> 
> I love your patch! Perhaps something to improve:
> 
> [auto build test WARNING on rafael-pm/linux-next]
> [also build test WARNING on sailus-media-tree/streams linus/master v6.2-rc7 next-20230208]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
> 
> url:    https://github.com/intel-lab-lkp/linux/commits/Sakari-Ailus/ACPI-property-Parse-data-node-string-references-in-properties/20230208-233112
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git linux-next
> patch link:    https://lore.kernel.org/r/20230208152807.3064242-3-sakari.ailus%40linux.intel.com
> patch subject: [PATCH v4 2/8] ACPI: property: Parse _CRS CSI-2 descriptor
> config: x86_64-rhel-8.3-syz (https://download.01.org/0day-ci/archive/20230209/202302090459.kIM95vle-lkp@intel.com/config)
> compiler: gcc-11 (Debian 11.3.0-8) 11.3.0
> reproduce (this is a W=1 build):
>         # https://github.com/intel-lab-lkp/linux/commit/d78f47f2d5051c50bdcea131da1779ec0fc8e266
>         git remote add linux-review https://github.com/intel-lab-lkp/linux
>         git fetch --no-tags linux-review Sakari-Ailus/ACPI-property-Parse-data-node-string-references-in-properties/20230208-233112
>         git checkout d78f47f2d5051c50bdcea131da1779ec0fc8e266
>         # save the config file
>         mkdir build_dir && cp config build_dir/.config
>         make W=1 O=build_dir ARCH=x86_64 olddefconfig
>         make W=1 O=build_dir ARCH=x86_64 SHELL=/bin/bash drivers/acpi/
> 
> If you fix the issue, kindly add following tag where applicable
> | Reported-by: kernel test robot <lkp@intel.com>
> | Link: https://lore.kernel.org/oe-kbuild-all/20230208152807.3064242-3-sakari.ailus@linux.intel.com

Sorry that the link is wrong, it should be
	Link: https://lore.kernel.org/oe-kbuild-all/202302090459.kIM95vle-lkp@intel.com/

> 
> All warnings (new ones prefixed by >>):
> 
> >> drivers/acpi/mipi.c:205:6: warning: no previous prototype for 'acpi_crs_csi2_alloc_fill_swnodes' [-Wmissing-prototypes]
>      205 | void acpi_crs_csi2_alloc_fill_swnodes(size_t ports_count, acpi_handle handle)
>          |      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> 
> 
> vim +/acpi_crs_csi2_alloc_fill_swnodes +205 drivers/acpi/mipi.c
> 
>    200	
>    201	/*
>    202	 * Allocate memory and set up software nodes for an ACPI device with given
>    203	 * number of CSI-2 ports.
>    204	 */
>  > 205	void acpi_crs_csi2_alloc_fill_swnodes(size_t ports_count, acpi_handle handle)
>    206	{
>    207		struct acpi_device_software_nodes *ads;
>    208		struct crs_csi2_swnodes *swnodes;
>    209		size_t alloc_size;
>    210		unsigned int i;
>    211		bool overflow;
>    212		void *end;
>    213	
>    214		/*
>    215		 * Allocate memory for ports, node pointers (number of nodes +
>    216		 * 1 (guardian), nodes (root + number of ports * 2 (for for
>    217		 * every port there is an endpoint)).
>    218		 */
>    219		overflow = check_mul_overflow(sizeof(*ads->ports) +
>    220					      sizeof(*ads->nodes) * 2 +
>    221					      sizeof(*ads->nodeptrs) * 2,
>    222					      ports_count, &alloc_size);
>    223		overflow = overflow ||
>    224			   check_add_overflow(sizeof(*ads) + sizeof(*ads->nodes) +
>    225					      sizeof(*ads->nodeptrs) * 2,
>    226					      alloc_size, &alloc_size);
>    227		if (overflow) {
>    228			acpi_handle_warn(handle,
>    229					 "too many _CRS CSI2 resource handles (%zu)",
>    230					 ports_count);
>    231			return;
>    232		}
>    233	
>    234		swnodes = kzalloc(sizeof(*swnodes), GFP_KERNEL);
>    235		ads = kzalloc(alloc_size, GFP_KERNEL);
>    236		ads->ports = (void *)(ads + 1);
>    237		ads->nodes = (void *)(ads->ports + ports_count);
>    238		ads->nodeptrs = (void *)(ads->nodes +
>    239					 ports_count * 2 + 1);
>    240		end = ads->nodeptrs + ports_count * 2 + 2;
>    241		if (!swnodes || !ads || WARN_ON((void *)ads + alloc_size != end)) {
>    242			kfree(swnodes);
>    243			kfree(ads);
>    244			acpi_handle_debug(handle,
>    245					  "cannot allocate for %zu software nodes\n",
>    246					  ports_count);
>    247			return;
>    248		}
>    249	
>    250		ads->num_ports = ports_count;
>    251		for (i = 0; i < ports_count * 2 + 1; i++)
>    252			ads->nodeptrs[i] = &ads->nodes[i];
>    253		ads->nodeptrs[i] = NULL;
>    254		for (i = 0; i < ports_count; i++)
>    255			ads->ports[i].port_nr = NO_CSI2_PORT;
>    256		swnodes->handle = handle;
>    257		swnodes->ads = ads;
>    258		list_add(&swnodes->list, &crs_csi2_swnodes);
>    259	}
>    260	
> 
> -- 
> 0-DAY CI Kernel Test Service
> https://github.com/intel/lkp-tests
>
diff mbox series

Patch

diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index a5b649e71ab1b..a98fa1bc15548 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 ec584442fb298..1ec4aa92bf17a 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -282,4 +282,11 @@  void acpi_init_lpit(void);
 static inline void acpi_init_lpit(void) { }
 #endif
 
+/*--------------------------------------------------------------------------
+				ACPI and MIPI DisCo for Imaging conversion
+  -------------------------------------------------------------------------- */
+
+void acpi_crs_csi2_swnodes_del_free(void);
+void acpi_bus_scan_crs_csi2(acpi_handle handle);
+
 #endif /* _ACPI_INTERNAL_H_ */
diff --git a/drivers/acpi/mipi.c b/drivers/acpi/mipi.c
new file mode 100644
index 0000000000000..0df6147dc8c9d
--- /dev/null
+++ b/drivers/acpi/mipi.c
@@ -0,0 +1,358 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MIPI DisCo for Imaging support.
+ *
+ * Copyright (C) 2023 Intel Corporation
+ */
+
+#include <linux/acpi.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;
+};
+
+/* Context for scanning ACPI device nodes for _CRS CSI2 resource descriptors */
+struct crs_csi2_all {
+	struct list_head crs_csi2_head;
+	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
+ */
+static acpi_status scan_check_crs_csi2(acpi_handle handle, u32 nesting_level,
+				       void *context, void **ret)
+{
+	struct scan_check_crs_csi2_context inst_context = {
+		.handle = handle,
+		.res_head = LIST_HEAD_INIT(inst_context.res_head),
+	};
+	struct crs_csi2_all *csi2_all = context;
+	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 AE_OK;
+
+	/*
+	 * Found entry, so allocate memory for it, fill it and add it to the
+	 * list.
+	 */
+	csi2 = kmalloc(sizeof(*csi2), GFP_KERNEL);
+	if (!csi2)
+		return AE_OK;
+
+	csi2->handle = handle;
+	list_replace(&inst_context.res_head, &csi2->buses);
+	list_add(&csi2->list, &csi2_all->crs_csi2_head);
+
+	/* This handle plus remote handles in _CRS CSI2 resource descriptors */
+	csi2_all->handle_count += 1 + inst_context.handle_count;
+
+	return AE_OK;
+}
+
+struct acpi_handle_ref {
+	acpi_handle handle;
+	unsigned int count;
+};
+
+#define NO_CSI2_PORT (~1U)
+
+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.
+ */
+static void 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.
+ */
+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 (for 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 = kzalloc(alloc_size, GFP_KERNEL);
+	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 (!swnodes || !ads || WARN_ON((void *)ads + alloc_size != end)) {
+		kfree(swnodes);
+		kfree(ads);
+		acpi_handle_debug(handle,
+				  "cannot allocate for %zu software nodes\n",
+				  ports_count);
+		return;
+	}
+
+	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);
+}
+
+/**
+ * acpi_bus_scan_crs_csi2 - Scan a device and its child devices for _CRS CSI-2
+ *
+ * @handle: Root of the ACPI Namespace branch to scan
+ *
+ * This function does a number of things:
+ *
+ * 1. Iteratively scan all ACPI device nodes starting from a handle for _CRS
+ *    CSI-2 instances. The instances are stored for later use.
+ *
+ * 2. Count how many references to other devices _CRS CSI-2 instances have in
+ *    total.
+ *
+ * 3. Count the number of references to other devices for each _CRS CSI-2
+ *    instance.
+ *
+ * 4. Allocate memory for swnodes each ACPI device requires later on, and
+ *    generate a list of such allocations.
+ *
+ * Note that struct acpi_device isn't available yet at this time.
+ *
+ * acpi_scan_lock in scan.c must be held when calling this function.
+ */
+void acpi_bus_scan_crs_csi2(acpi_handle handle)
+{
+	struct acpi_handle_ref *handle_refs;
+	struct acpi_handle_ref *this = NULL;
+	struct crs_csi2_all csi2_all = {
+		.crs_csi2_head = LIST_HEAD_INIT(csi2_all.crs_csi2_head),
+	};
+	size_t this_count;
+	unsigned int curr = 0;
+	struct crs_csi2 *csi2;
+
+	/*
+	 * Collect the devices that have a _CRS CSI-2 resource. Each CSI-2 TX
+	 * port has one.
+	 */
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, ACPI_UINT32_MAX,
+			    scan_check_crs_csi2, NULL, &csi2_all, NULL);
+
+	/* No handles? Bail out here. */
+	if (!csi2_all.handle_count)
+		return;
+
+	handle_refs = kcalloc(csi2_all.handle_count + 1, sizeof(*handle_refs),
+			      GFP_KERNEL);
+	if (!handle_refs) {
+		acpi_handle_debug(handle, "no memory for %zu handle refs\n",
+				  csi2_all.handle_count + 1);
+		return;
+	}
+
+	/* Associate handles to the number of references. */
+	list_for_each_entry(csi2, &csi2_all.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, csi2_all.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 < csi2_all.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);
+
+	crs_csi2_release(&csi2_all.crs_csi2_head);
+}
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 0c6f06abe3f47..50de874b8f208 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2423,9 +2423,12 @@  EXPORT_SYMBOL_GPL(acpi_dev_get_next_consumer_dev);
 int acpi_bus_scan(acpi_handle handle)
 {
 	struct acpi_device *device = NULL;
+	int ret = 0;
 
 	acpi_bus_scan_second_pass = false;
 
+	acpi_bus_scan_crs_csi2(handle);
+
 	/* Pass 1: Avoid enumerating devices with missing dependencies. */
 
 	if (ACPI_SUCCESS(acpi_bus_check_add(handle, true, &device)))
@@ -2433,13 +2436,15 @@  int acpi_bus_scan(acpi_handle handle)
 				    acpi_bus_check_add_1, NULL, NULL,
 				    (void **)&device);
 
-	if (!device)
-		return -ENODEV;
+	if (!device) {
+		ret = -ENODEV;
+		goto out_release;
+	}
 
 	acpi_bus_attach(device, (void *)true);
 
 	if (!acpi_bus_scan_second_pass)
-		return 0;
+		goto out_release;
 
 	/* Pass 2: Enumerate all of the remaining devices. */
 
@@ -2452,7 +2457,10 @@  int acpi_bus_scan(acpi_handle handle)
 
 	acpi_bus_attach(device, NULL);
 
-	return 0;
+out_release:
+	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 e44be31115a67..a05fe22c1175c 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;