diff mbox series

[10/46] cxl/core: Define a 'struct cxl_root_decoder' for tracking CXL window resources

Message ID 165603877351.551046.12325060612893557716.stgit@dwillia2-xfh (mailing list archive)
State Superseded
Headers show
Series CXL PMEM Region Provisioning | expand

Commit Message

Dan Williams June 24, 2022, 2:46 a.m. UTC
Previously the target routing specifics of switch decoders were factored
out of 'struct cxl_decoder' into 'struct cxl_switch_decoder'.

This patch, 2 of 3, adds a 'struct cxl_root_decoder' as a superset of a
switch decoder that also track the associated CXL window platform
resource.

Note that the reason the resource for a given root decoder needs to be
looked up after the fact (i.e. after cxl_parse_cfmws() and
add_cxl_resource()) is because add_cxl_resource() may have merged CXL
windows in order to keep them at the top of the resource tree / decode
hierarchy.

Co-developed-by: Ben Widawsky <bwidawsk@kernel.org>
Signed-off-by: Ben Widawsky <bwidawsk@kernel.org>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
---
 drivers/cxl/acpi.c      |   40 ++++++++++++++++++++++++++++++++++++----
 drivers/cxl/core/port.c |   43 +++++++++++++++++++++++++++++++++++++------
 drivers/cxl/cxl.h       |   15 +++++++++++++--
 3 files changed, 86 insertions(+), 12 deletions(-)

Comments

Jonathan Cameron June 28, 2022, 4:49 p.m. UTC | #1
On Thu, 23 Jun 2022 19:46:13 -0700
Dan Williams <dan.j.williams@intel.com> wrote:

> Previously the target routing specifics of switch decoders were factored
> out of 'struct cxl_decoder' into 'struct cxl_switch_decoder'.
> 
> This patch, 2 of 3, adds a 'struct cxl_root_decoder' as a superset of a
> switch decoder that also track the associated CXL window platform
> resource.
> 
> Note that the reason the resource for a given root decoder needs to be
> looked up after the fact (i.e. after cxl_parse_cfmws() and
> add_cxl_resource()) is because add_cxl_resource() may have merged CXL
> windows in order to keep them at the top of the resource tree / decode
> hierarchy.

One trivial comment below that follows from earlier patch.

Otherwise, I'll look again at this when I understand what the constraints
of CXL windows are that you are dealing with.  I don't get why they might not
be at the top of the resource tree without the merging!

> 
> Co-developed-by: Ben Widawsky <bwidawsk@kernel.org>
> Signed-off-by: Ben Widawsky <bwidawsk@kernel.org>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
>  drivers/cxl/acpi.c      |   40 ++++++++++++++++++++++++++++++++++++----
>  drivers/cxl/core/port.c |   43 +++++++++++++++++++++++++++++++++++++------
>  drivers/cxl/cxl.h       |   15 +++++++++++++--
>  3 files changed, 86 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index 003fa4fde357..5972f380cdf2 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -82,7 +82,7 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
>  	int target_map[CXL_DECODER_MAX_INTERLEAVE];
>  	struct cxl_cfmws_context *ctx = arg;
>  	struct cxl_port *root_port = ctx->root_port;
> -	struct cxl_switch_decoder *cxlsd;
> +	struct cxl_root_decoder *cxlrd;
>  	struct device *dev = ctx->dev;
>  	struct acpi_cedt_cfmws *cfmws;
>  	struct resource *cxl_res;
> @@ -128,11 +128,11 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
>  	if (rc)
>  		goto err_insert;
>  
> -	cxlsd = cxl_root_decoder_alloc(root_port, ways);
> -	if (IS_ERR(cxld))
> +	cxlrd = cxl_root_decoder_alloc(root_port, ways);
> +	if (IS_ERR(cxlrd))
>  		return 0;
>  
> -	cxld = &cxlsd->cxld;
> +	cxld = &cxlrd->cxlsd.cxld;
>  	cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
>  	cxld->target_type = CXL_DECODER_EXPANDER;
>  	cxld->hpa_range = (struct range) {
> @@ -375,6 +375,32 @@ static int add_cxl_resources(struct resource *cxl)
>  	return 0;
>  }
>  
> +static int pair_cxl_resource(struct device *dev, void *data)
> +{
> +	struct resource *cxl_res = data;
> +	struct resource *p;
> +
> +	if (!is_root_decoder(dev))
> +		return 0;
> +
> +	for (p = cxl_res->child; p; p = p->sibling) {
> +		struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
> +		struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
> +		struct resource res = {
> +			.start = cxld->hpa_range.start,
> +			.end = cxld->hpa_range.end,
> +			.flags = IORESOURCE_MEM,
> +		};
> +
> +		if (resource_contains(p, &res)) {
> +			cxlrd->res = (struct resource *)p->desc;
> +			break;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
>  static int cxl_acpi_probe(struct platform_device *pdev)
>  {
>  	int rc;
> @@ -425,6 +451,12 @@ static int cxl_acpi_probe(struct platform_device *pdev)
>  	if (rc)
>  		return rc;
>  
> +	/*
> +	 * Populate the root decoders with their related iomem resource,
> +	 * if present
> +	 */
> +	device_for_each_child(&root_port->dev, cxl_res, pair_cxl_resource);
> +
>  	/*
>  	 * Root level scanned with host-bridge as dports, now scan host-bridges
>  	 * for their role as CXL uports to their CXL-capable PCIe Root Ports.
> diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> index fd1cac13cd2e..abf3455c4eff 100644
> --- a/drivers/cxl/core/port.c
> +++ b/drivers/cxl/core/port.c
> @@ -259,6 +259,23 @@ static void cxl_switch_decoder_release(struct device *dev)
>  	kfree(cxlsd);
>  }
>  
> +struct cxl_root_decoder *to_cxl_root_decoder(struct device *dev)
> +{
> +	if (dev_WARN_ONCE(dev, !is_root_decoder(dev),
> +			  "not a cxl_root_decoder device\n"))
> +		return NULL;
> +	return container_of(dev, struct cxl_root_decoder, cxlsd.cxld.dev);
> +}
> +EXPORT_SYMBOL_NS_GPL(to_cxl_root_decoder, CXL);
> +
> +static void cxl_root_decoder_release(struct device *dev)
> +{
> +	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
> +
> +	__cxl_decoder_release(&cxlrd->cxlsd.cxld);
> +	kfree(cxlrd);
> +}
> +
>  static const struct device_type cxl_decoder_endpoint_type = {
>  	.name = "cxl_decoder_endpoint",
>  	.release = cxl_decoder_release,
> @@ -273,7 +290,7 @@ static const struct device_type cxl_decoder_switch_type = {
>  
>  static const struct device_type cxl_decoder_root_type = {
>  	.name = "cxl_decoder_root",
> -	.release = cxl_switch_decoder_release,
> +	.release = cxl_root_decoder_release,
>  	.groups = cxl_decoder_root_attribute_groups,
>  };
>  
> @@ -1218,9 +1235,23 @@ static struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port,
>  
>  	if (nr_targets) {
>  		struct cxl_switch_decoder *cxlsd;
> +		struct cxl_root_decoder *cxlrd;
> +
> +		if (is_cxl_root(port)) {
> +			alloc = kzalloc(struct_size(cxlrd, cxlsd.target,
> +						    nr_targets),
> +					GFP_KERNEL);
> +			cxlrd = alloc;
> +			if (cxlrd)
> +				cxlsd = &cxlrd->cxlsd;
> +			else
> +				cxlsd = NULL;
> +		} else {
> +			alloc = kzalloc(struct_size(cxlsd, target, nr_targets),
> +					GFP_KERNEL);
> +			cxlsd = alloc;

As earlier, I'd prefer you just handled errors when they happened rather than
dancing onwards...

> +		}
>  
> -		alloc = kzalloc(struct_size(cxlsd, target, nr_targets), GFP_KERNEL);
> -		cxlsd = alloc;
>  		if (cxlsd) {
>  			cxlsd->nr_targets = nr_targets;
>  			seqlock_init(&cxlsd->target_lock);
> @@ -1279,8 +1310,8 @@ static struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port,
>   * firmware description of CXL resources into a CXL standard decode
>   * topology.
>   */
> -struct cxl_switch_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
> -						  unsigned int nr_targets)
> +struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
> +						unsigned int nr_targets)
>  {
>  	struct cxl_decoder *cxld;
>  
> @@ -1290,7 +1321,7 @@ struct cxl_switch_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
>  	cxld = cxl_decoder_alloc(port, nr_targets);
>  	if (IS_ERR(cxld))
>  		return ERR_CAST(cxld);
> -	return to_cxl_switch_decoder(&cxld->dev);
> +	return to_cxl_root_decoder(&cxld->dev);
>  }
>  EXPORT_SYMBOL_NS_GPL(cxl_root_decoder_alloc, CXL);
>  
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 7525b55b11bb..6dd1e4c57a67 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -253,6 +253,16 @@ struct cxl_switch_decoder {
>  	struct cxl_dport *target[];
>  };
>  
> +/**
> + * struct cxl_root_decoder - Static platform CXL address decoder
> + * @res: host / parent resource for region allocations
> + * @cxlsd: base cxl switch decoder
> + */
> +struct cxl_root_decoder {
> +	struct resource *res;
> +	struct cxl_switch_decoder cxlsd;

Could be nice to those container of macros and just put the cxlsd first.

> +};
> +
>  /**
>   * enum cxl_nvdimm_brige_state - state machine for managing bus rescans
>   * @CXL_NVB_NEW: Set at bridge create and after cxl_pmem_wq is destroyed
> @@ -368,10 +378,11 @@ struct cxl_dport *cxl_find_dport_by_dev(struct cxl_port *port,
>  					const struct device *dev);
>  
>  struct cxl_decoder *to_cxl_decoder(struct device *dev);
> +struct cxl_root_decoder *to_cxl_root_decoder(struct device *dev);
>  bool is_root_decoder(struct device *dev);
>  bool is_endpoint_decoder(struct device *dev);
> -struct cxl_switch_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
> -						  unsigned int nr_targets);
> +struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
> +						unsigned int nr_targets);
>  struct cxl_switch_decoder *cxl_switch_decoder_alloc(struct cxl_port *port,
>  						    unsigned int nr_targets);
>  int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map);
>
Jonathan Cameron June 28, 2022, 4:53 p.m. UTC | #2
>  
> +/**
> + * struct cxl_root_decoder - Static platform CXL address decoder
> + * @res: host / parent resource for region allocations
> + * @cxlsd: base cxl switch decoder
> + */
> +struct cxl_root_decoder {
> +	struct resource *res;
> +	struct cxl_switch_decoder cxlsd;

Ordering in these inheriting structures is inconsistent. I'd put
the cxlsd entry first here.  Doesn't matter hugely but seems a bit
odd when looking at next patch

> +};
> +
>  /**
Dan Williams July 10, 2022, 2:20 a.m. UTC | #3
Jonathan Cameron wrote:
> On Thu, 23 Jun 2022 19:46:13 -0700
> Dan Williams <dan.j.williams@intel.com> wrote:
> 
> > Previously the target routing specifics of switch decoders were factored
> > out of 'struct cxl_decoder' into 'struct cxl_switch_decoder'.
> > 
> > This patch, 2 of 3, adds a 'struct cxl_root_decoder' as a superset of a
> > switch decoder that also track the associated CXL window platform
> > resource.
> > 
> > Note that the reason the resource for a given root decoder needs to be
> > looked up after the fact (i.e. after cxl_parse_cfmws() and
> > add_cxl_resource()) is because add_cxl_resource() may have merged CXL
> > windows in order to keep them at the top of the resource tree / decode
> > hierarchy.
> 
> One trivial comment below that follows from earlier patch.
> 
> Otherwise, I'll look again at this when I understand what the constraints
> of CXL windows are that you are dealing with.  I don't get why they might not
> be at the top of the resource tree without the merging!
> 
> > 
> > Co-developed-by: Ben Widawsky <bwidawsk@kernel.org>
> > Signed-off-by: Ben Widawsky <bwidawsk@kernel.org>
> > Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> > ---
> >  drivers/cxl/acpi.c      |   40 ++++++++++++++++++++++++++++++++++++----
> >  drivers/cxl/core/port.c |   43 +++++++++++++++++++++++++++++++++++++------
> >  drivers/cxl/cxl.h       |   15 +++++++++++++--
> >  3 files changed, 86 insertions(+), 12 deletions(-)
> > 
> > diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> > index 003fa4fde357..5972f380cdf2 100644
> > --- a/drivers/cxl/acpi.c
> > +++ b/drivers/cxl/acpi.c
> > @@ -82,7 +82,7 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
> >  	int target_map[CXL_DECODER_MAX_INTERLEAVE];
> >  	struct cxl_cfmws_context *ctx = arg;
> >  	struct cxl_port *root_port = ctx->root_port;
> > -	struct cxl_switch_decoder *cxlsd;
> > +	struct cxl_root_decoder *cxlrd;
> >  	struct device *dev = ctx->dev;
> >  	struct acpi_cedt_cfmws *cfmws;
> >  	struct resource *cxl_res;
> > @@ -128,11 +128,11 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
> >  	if (rc)
> >  		goto err_insert;
> >  
> > -	cxlsd = cxl_root_decoder_alloc(root_port, ways);
> > -	if (IS_ERR(cxld))
> > +	cxlrd = cxl_root_decoder_alloc(root_port, ways);
> > +	if (IS_ERR(cxlrd))
> >  		return 0;
> >  
> > -	cxld = &cxlsd->cxld;
> > +	cxld = &cxlrd->cxlsd.cxld;
> >  	cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
> >  	cxld->target_type = CXL_DECODER_EXPANDER;
> >  	cxld->hpa_range = (struct range) {
> > @@ -375,6 +375,32 @@ static int add_cxl_resources(struct resource *cxl)
> >  	return 0;
> >  }
> >  
> > +static int pair_cxl_resource(struct device *dev, void *data)
> > +{
> > +	struct resource *cxl_res = data;
> > +	struct resource *p;
> > +
> > +	if (!is_root_decoder(dev))
> > +		return 0;
> > +
> > +	for (p = cxl_res->child; p; p = p->sibling) {
> > +		struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
> > +		struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
> > +		struct resource res = {
> > +			.start = cxld->hpa_range.start,
> > +			.end = cxld->hpa_range.end,
> > +			.flags = IORESOURCE_MEM,
> > +		};
> > +
> > +		if (resource_contains(p, &res)) {
> > +			cxlrd->res = (struct resource *)p->desc;
> > +			break;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> >  static int cxl_acpi_probe(struct platform_device *pdev)
> >  {
> >  	int rc;
> > @@ -425,6 +451,12 @@ static int cxl_acpi_probe(struct platform_device *pdev)
> >  	if (rc)
> >  		return rc;
> >  
> > +	/*
> > +	 * Populate the root decoders with their related iomem resource,
> > +	 * if present
> > +	 */
> > +	device_for_each_child(&root_port->dev, cxl_res, pair_cxl_resource);
> > +
> >  	/*
> >  	 * Root level scanned with host-bridge as dports, now scan host-bridges
> >  	 * for their role as CXL uports to their CXL-capable PCIe Root Ports.
> > diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
> > index fd1cac13cd2e..abf3455c4eff 100644
> > --- a/drivers/cxl/core/port.c
> > +++ b/drivers/cxl/core/port.c
> > @@ -259,6 +259,23 @@ static void cxl_switch_decoder_release(struct device *dev)
> >  	kfree(cxlsd);
> >  }
> >  
> > +struct cxl_root_decoder *to_cxl_root_decoder(struct device *dev)
> > +{
> > +	if (dev_WARN_ONCE(dev, !is_root_decoder(dev),
> > +			  "not a cxl_root_decoder device\n"))
> > +		return NULL;
> > +	return container_of(dev, struct cxl_root_decoder, cxlsd.cxld.dev);
> > +}
> > +EXPORT_SYMBOL_NS_GPL(to_cxl_root_decoder, CXL);
> > +
> > +static void cxl_root_decoder_release(struct device *dev)
> > +{
> > +	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
> > +
> > +	__cxl_decoder_release(&cxlrd->cxlsd.cxld);
> > +	kfree(cxlrd);
> > +}
> > +
> >  static const struct device_type cxl_decoder_endpoint_type = {
> >  	.name = "cxl_decoder_endpoint",
> >  	.release = cxl_decoder_release,
> > @@ -273,7 +290,7 @@ static const struct device_type cxl_decoder_switch_type = {
> >  
> >  static const struct device_type cxl_decoder_root_type = {
> >  	.name = "cxl_decoder_root",
> > -	.release = cxl_switch_decoder_release,
> > +	.release = cxl_root_decoder_release,
> >  	.groups = cxl_decoder_root_attribute_groups,
> >  };
> >  
> > @@ -1218,9 +1235,23 @@ static struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port,
> >  
> >  	if (nr_targets) {
> >  		struct cxl_switch_decoder *cxlsd;
> > +		struct cxl_root_decoder *cxlrd;
> > +
> > +		if (is_cxl_root(port)) {
> > +			alloc = kzalloc(struct_size(cxlrd, cxlsd.target,
> > +						    nr_targets),
> > +					GFP_KERNEL);
> > +			cxlrd = alloc;
> > +			if (cxlrd)
> > +				cxlsd = &cxlrd->cxlsd;
> > +			else
> > +				cxlsd = NULL;
> > +		} else {
> > +			alloc = kzalloc(struct_size(cxlsd, target, nr_targets),
> > +					GFP_KERNEL);
> > +			cxlsd = alloc;
> 
> As earlier, I'd prefer you just handled errors when they happened rather than
> dancing onwards...

Yes, this gets cleaned up with moving the allocation to
cxl_root_decoder_alloc() directly.

> 
> > +		}
> >  
> > -		alloc = kzalloc(struct_size(cxlsd, target, nr_targets), GFP_KERNEL);
> > -		cxlsd = alloc;
> >  		if (cxlsd) {
> >  			cxlsd->nr_targets = nr_targets;
> >  			seqlock_init(&cxlsd->target_lock);
> > @@ -1279,8 +1310,8 @@ static struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port,
> >   * firmware description of CXL resources into a CXL standard decode
> >   * topology.
> >   */
> > -struct cxl_switch_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
> > -						  unsigned int nr_targets)
> > +struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
> > +						unsigned int nr_targets)
> >  {
> >  	struct cxl_decoder *cxld;
> >  
> > @@ -1290,7 +1321,7 @@ struct cxl_switch_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
> >  	cxld = cxl_decoder_alloc(port, nr_targets);
> >  	if (IS_ERR(cxld))
> >  		return ERR_CAST(cxld);
> > -	return to_cxl_switch_decoder(&cxld->dev);
> > +	return to_cxl_root_decoder(&cxld->dev);
> >  }
> >  EXPORT_SYMBOL_NS_GPL(cxl_root_decoder_alloc, CXL);
> >  
> > diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> > index 7525b55b11bb..6dd1e4c57a67 100644
> > --- a/drivers/cxl/cxl.h
> > +++ b/drivers/cxl/cxl.h
> > @@ -253,6 +253,16 @@ struct cxl_switch_decoder {
> >  	struct cxl_dport *target[];
> >  };
> >  
> > +/**
> > + * struct cxl_root_decoder - Static platform CXL address decoder
> > + * @res: host / parent resource for region allocations
> > + * @cxlsd: base cxl switch decoder
> > + */
> > +struct cxl_root_decoder {
> > +	struct resource *res;
> > +	struct cxl_switch_decoder cxlsd;
> 
> Could be nice to those container of macros and just put the cxlsd first.

Not possible. @cxlsd needs to be the last attribute because it has a
variably sized flex-array at its end.
diff mbox series

Patch

diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
index 003fa4fde357..5972f380cdf2 100644
--- a/drivers/cxl/acpi.c
+++ b/drivers/cxl/acpi.c
@@ -82,7 +82,7 @@  static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
 	int target_map[CXL_DECODER_MAX_INTERLEAVE];
 	struct cxl_cfmws_context *ctx = arg;
 	struct cxl_port *root_port = ctx->root_port;
-	struct cxl_switch_decoder *cxlsd;
+	struct cxl_root_decoder *cxlrd;
 	struct device *dev = ctx->dev;
 	struct acpi_cedt_cfmws *cfmws;
 	struct resource *cxl_res;
@@ -128,11 +128,11 @@  static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
 	if (rc)
 		goto err_insert;
 
-	cxlsd = cxl_root_decoder_alloc(root_port, ways);
-	if (IS_ERR(cxld))
+	cxlrd = cxl_root_decoder_alloc(root_port, ways);
+	if (IS_ERR(cxlrd))
 		return 0;
 
-	cxld = &cxlsd->cxld;
+	cxld = &cxlrd->cxlsd.cxld;
 	cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
 	cxld->target_type = CXL_DECODER_EXPANDER;
 	cxld->hpa_range = (struct range) {
@@ -375,6 +375,32 @@  static int add_cxl_resources(struct resource *cxl)
 	return 0;
 }
 
+static int pair_cxl_resource(struct device *dev, void *data)
+{
+	struct resource *cxl_res = data;
+	struct resource *p;
+
+	if (!is_root_decoder(dev))
+		return 0;
+
+	for (p = cxl_res->child; p; p = p->sibling) {
+		struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
+		struct cxl_decoder *cxld = &cxlrd->cxlsd.cxld;
+		struct resource res = {
+			.start = cxld->hpa_range.start,
+			.end = cxld->hpa_range.end,
+			.flags = IORESOURCE_MEM,
+		};
+
+		if (resource_contains(p, &res)) {
+			cxlrd->res = (struct resource *)p->desc;
+			break;
+		}
+	}
+
+	return 0;
+}
+
 static int cxl_acpi_probe(struct platform_device *pdev)
 {
 	int rc;
@@ -425,6 +451,12 @@  static int cxl_acpi_probe(struct platform_device *pdev)
 	if (rc)
 		return rc;
 
+	/*
+	 * Populate the root decoders with their related iomem resource,
+	 * if present
+	 */
+	device_for_each_child(&root_port->dev, cxl_res, pair_cxl_resource);
+
 	/*
 	 * Root level scanned with host-bridge as dports, now scan host-bridges
 	 * for their role as CXL uports to their CXL-capable PCIe Root Ports.
diff --git a/drivers/cxl/core/port.c b/drivers/cxl/core/port.c
index fd1cac13cd2e..abf3455c4eff 100644
--- a/drivers/cxl/core/port.c
+++ b/drivers/cxl/core/port.c
@@ -259,6 +259,23 @@  static void cxl_switch_decoder_release(struct device *dev)
 	kfree(cxlsd);
 }
 
+struct cxl_root_decoder *to_cxl_root_decoder(struct device *dev)
+{
+	if (dev_WARN_ONCE(dev, !is_root_decoder(dev),
+			  "not a cxl_root_decoder device\n"))
+		return NULL;
+	return container_of(dev, struct cxl_root_decoder, cxlsd.cxld.dev);
+}
+EXPORT_SYMBOL_NS_GPL(to_cxl_root_decoder, CXL);
+
+static void cxl_root_decoder_release(struct device *dev)
+{
+	struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
+
+	__cxl_decoder_release(&cxlrd->cxlsd.cxld);
+	kfree(cxlrd);
+}
+
 static const struct device_type cxl_decoder_endpoint_type = {
 	.name = "cxl_decoder_endpoint",
 	.release = cxl_decoder_release,
@@ -273,7 +290,7 @@  static const struct device_type cxl_decoder_switch_type = {
 
 static const struct device_type cxl_decoder_root_type = {
 	.name = "cxl_decoder_root",
-	.release = cxl_switch_decoder_release,
+	.release = cxl_root_decoder_release,
 	.groups = cxl_decoder_root_attribute_groups,
 };
 
@@ -1218,9 +1235,23 @@  static struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port,
 
 	if (nr_targets) {
 		struct cxl_switch_decoder *cxlsd;
+		struct cxl_root_decoder *cxlrd;
+
+		if (is_cxl_root(port)) {
+			alloc = kzalloc(struct_size(cxlrd, cxlsd.target,
+						    nr_targets),
+					GFP_KERNEL);
+			cxlrd = alloc;
+			if (cxlrd)
+				cxlsd = &cxlrd->cxlsd;
+			else
+				cxlsd = NULL;
+		} else {
+			alloc = kzalloc(struct_size(cxlsd, target, nr_targets),
+					GFP_KERNEL);
+			cxlsd = alloc;
+		}
 
-		alloc = kzalloc(struct_size(cxlsd, target, nr_targets), GFP_KERNEL);
-		cxlsd = alloc;
 		if (cxlsd) {
 			cxlsd->nr_targets = nr_targets;
 			seqlock_init(&cxlsd->target_lock);
@@ -1279,8 +1310,8 @@  static struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port,
  * firmware description of CXL resources into a CXL standard decode
  * topology.
  */
-struct cxl_switch_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
-						  unsigned int nr_targets)
+struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
+						unsigned int nr_targets)
 {
 	struct cxl_decoder *cxld;
 
@@ -1290,7 +1321,7 @@  struct cxl_switch_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
 	cxld = cxl_decoder_alloc(port, nr_targets);
 	if (IS_ERR(cxld))
 		return ERR_CAST(cxld);
-	return to_cxl_switch_decoder(&cxld->dev);
+	return to_cxl_root_decoder(&cxld->dev);
 }
 EXPORT_SYMBOL_NS_GPL(cxl_root_decoder_alloc, CXL);
 
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index 7525b55b11bb..6dd1e4c57a67 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -253,6 +253,16 @@  struct cxl_switch_decoder {
 	struct cxl_dport *target[];
 };
 
+/**
+ * struct cxl_root_decoder - Static platform CXL address decoder
+ * @res: host / parent resource for region allocations
+ * @cxlsd: base cxl switch decoder
+ */
+struct cxl_root_decoder {
+	struct resource *res;
+	struct cxl_switch_decoder cxlsd;
+};
+
 /**
  * enum cxl_nvdimm_brige_state - state machine for managing bus rescans
  * @CXL_NVB_NEW: Set at bridge create and after cxl_pmem_wq is destroyed
@@ -368,10 +378,11 @@  struct cxl_dport *cxl_find_dport_by_dev(struct cxl_port *port,
 					const struct device *dev);
 
 struct cxl_decoder *to_cxl_decoder(struct device *dev);
+struct cxl_root_decoder *to_cxl_root_decoder(struct device *dev);
 bool is_root_decoder(struct device *dev);
 bool is_endpoint_decoder(struct device *dev);
-struct cxl_switch_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
-						  unsigned int nr_targets);
+struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
+						unsigned int nr_targets);
 struct cxl_switch_decoder *cxl_switch_decoder_alloc(struct cxl_port *port,
 						    unsigned int nr_targets);
 int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map);