2017-08-12 00:14:07

by Khuong Dinh

[permalink] [raw]
Subject: [PATCH v3 pci] PCI/MSI: pci-xgene-msi: Enable MSI support in ACPI boot for X-Gene v1

This patch makes pci-xgene-msi driver ACPI-aware and provides
MSI capability for X-Gene v1 PCIe controllers in ACPI boot mode.

Signed-off-by: Khuong Dinh <[email protected]>
[Take over from Duc Dang]
Acked-by: Marc Zyngier <[email protected]>
---
v3:
- Input X-Gene MSI base address for irq_domain_alloc_fwnode
- Add a hook to enforce X-Gene MSI be probed prior acpi_bus_scan happens
v2:
- Verify with BIOS version 3.06.25 and 3.07.09
v1:
- Initial version
---
drivers/acpi/Makefile | 2 +-
drivers/acpi/acpi_msi.c | 74 ++++++++++++++++++++++++++++++++++++++
drivers/acpi/internal.h | 1 +
drivers/acpi/scan.c | 1 +
drivers/pci/host/pci-xgene-msi.c | 36 +++++++++++++++++--
5 files changed, 110 insertions(+), 4 deletions(-)
create mode 100644 drivers/acpi/acpi_msi.c

diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index b1aacfc..c15f5cd 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -40,7 +40,7 @@ acpi-y += ec.o
acpi-$(CONFIG_ACPI_DOCK) += dock.o
acpi-y += pci_root.o pci_link.o pci_irq.o
obj-$(CONFIG_ACPI_MCFG) += pci_mcfg.o
-acpi-y += acpi_lpss.o acpi_apd.o
+acpi-y += acpi_lpss.o acpi_apd.o acpi_msi.o
acpi-y += acpi_platform.o
acpi-y += acpi_pnp.o
acpi-$(CONFIG_ARM_AMBA) += acpi_amba.o
diff --git a/drivers/acpi/acpi_msi.c b/drivers/acpi/acpi_msi.c
new file mode 100644
index 0000000..c2f8d26
--- /dev/null
+++ b/drivers/acpi/acpi_msi.c
@@ -0,0 +1,74 @@
+/*
+ * Enforce MSI driver loaded by PCIe controller driver
+ *
+ * Copyright (c) 2017, MACOM Technology Solutions Corporation
+ * Author: Khuong Dinh <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/acpi.h>
+#include "internal.h"
+
+static acpi_status acpi_create_msi_device(acpi_handle handle, u32 Level,
+ void *context, void **retval)
+{
+ struct acpi_device *device = NULL;
+ int type = ACPI_BUS_TYPE_DEVICE;
+ unsigned long long sta;
+ acpi_status status;
+ int ret = 0;
+
+ acpi_bus_get_device(handle, &device);
+ status = acpi_bus_get_status_handle(handle, &sta);
+ if (ACPI_FAILURE(status))
+ sta = 0;
+
+ device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
+ if (!device)
+ return -ENOMEM;
+
+ acpi_init_device_object(device, handle, type, sta);
+ ret = acpi_device_add(device, NULL);
+ if (ret)
+ return ret;
+ device->parent = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
+ INIT_LIST_HEAD(&device->parent->physical_node_list);
+
+ acpi_device_add_finalize(device);
+
+ ret = device_attach(&device->dev);
+ if (ret < 0)
+ return ret;
+
+ acpi_create_platform_device(device, NULL);
+ acpi_device_set_enumerated(device);
+
+ return ret;
+}
+
+static const struct acpi_device_id acpi_msi_device_ids[] = {
+ {"APMC0D0E", 0},
+ { }
+};
+
+int __init acpi_msi_init(void)
+{
+ acpi_status status;
+ int ret = 0;
+
+ status = acpi_get_devices(acpi_msi_device_ids[0].id,
+ acpi_create_msi_device, NULL, NULL);
+ if (ACPI_FAILURE(status))
+ ret = -ENODEV;
+
+ return ret;
+}
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 58dd7ab..3da50d3 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -80,6 +80,7 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
void acpi_lpss_init(void);

void acpi_apd_init(void);
+int acpi_msi_init(void);

acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src);
bool acpi_queue_hotplug_work(struct work_struct *work);
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 3389729..8ec4d39 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -2029,6 +2029,7 @@ int __init acpi_scan_init(void)
acpi_status status;
struct acpi_table_stao *stao_ptr;

+ acpi_msi_init();
acpi_pci_root_init();
acpi_pci_link_init();
acpi_processor_init();
diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
index f1b633b..b1768a1 100644
--- a/drivers/pci/host/pci-xgene-msi.c
+++ b/drivers/pci/host/pci-xgene-msi.c
@@ -24,6 +24,7 @@
#include <linux/pci.h>
#include <linux/platform_device.h>
#include <linux/of_pci.h>
+#include <linux/acpi.h>

#define MSI_IR0 0x000000
#define MSI_INT0 0x800000
@@ -39,7 +40,7 @@ struct xgene_msi_group {
};

struct xgene_msi {
- struct device_node *node;
+ struct fwnode_handle *fwnode;
struct irq_domain *inner_domain;
struct irq_domain *msi_domain;
u64 msi_addr;
@@ -249,6 +250,13 @@ static void xgene_irq_domain_free(struct irq_domain *domain,
.free = xgene_irq_domain_free,
};

+#ifdef CONFIG_ACPI
+static struct fwnode_handle *xgene_msi_get_fwnode(struct device *dev)
+{
+ return xgene_msi_ctrl.fwnode;
+}
+#endif
+
static int xgene_allocate_domains(struct xgene_msi *msi)
{
msi->inner_domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
@@ -256,7 +264,7 @@ static int xgene_allocate_domains(struct xgene_msi *msi)
if (!msi->inner_domain)
return -ENOMEM;

- msi->msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(msi->node),
+ msi->msi_domain = pci_msi_create_irq_domain(msi->fwnode,
&xgene_msi_domain_info,
msi->inner_domain);

@@ -265,6 +273,9 @@ static int xgene_allocate_domains(struct xgene_msi *msi)
return -ENOMEM;
}

+#ifdef CONFIG_ACPI
+ pci_msi_register_fwnode_provider(&xgene_msi_get_fwnode);
+#endif
return 0;
}

@@ -449,6 +460,13 @@ static int xgene_msi_hwirq_free(unsigned int cpu)
{},
};

+#ifdef CONFIG_ACPI
+static const struct acpi_device_id xgene_msi_acpi_ids[] = {
+ {"APMC0D0E", 0},
+ { },
+};
+#endif
+
static int xgene_msi_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -469,7 +487,18 @@ static int xgene_msi_probe(struct platform_device *pdev)
goto error;
}
xgene_msi->msi_addr = res->start;
- xgene_msi->node = pdev->dev.of_node;
+
+ xgene_msi->fwnode = of_node_to_fwnode(pdev->dev.of_node);
+ if (!xgene_msi->fwnode) {
+ xgene_msi->fwnode =
+ irq_domain_alloc_fwnode((void *)xgene_msi->msi_addr);
+ if (!xgene_msi->fwnode) {
+ dev_err(&pdev->dev, "Failed to create fwnode\n");
+ rc = ENOMEM;
+ goto error;
+ }
+ }
+
xgene_msi->num_cpus = num_possible_cpus();

rc = xgene_msi_init_allocator(xgene_msi);
@@ -540,6 +569,7 @@ static int xgene_msi_probe(struct platform_device *pdev)
.driver = {
.name = "xgene-msi",
.of_match_table = xgene_msi_match_table,
+ .acpi_match_table = ACPI_PTR(xgene_msi_acpi_ids),
},
.probe = xgene_msi_probe,
.remove = xgene_msi_remove,
--
1.7.1


2017-08-14 08:21:55

by Marc Zyngier

[permalink] [raw]
Subject: Re: [PATCH v3 pci] PCI/MSI: pci-xgene-msi: Enable MSI support in ACPI boot for X-Gene v1

On 12/08/17 01:02, Khuong Dinh wrote:
> This patch makes pci-xgene-msi driver ACPI-aware and provides
> MSI capability for X-Gene v1 PCIe controllers in ACPI boot mode.
>
> Signed-off-by: Khuong Dinh <[email protected]>
> [Take over from Duc Dang]
> Acked-by: Marc Zyngier <[email protected]>

Given that the patch has changed very substantially, this Ack is no
longer valid.

> ---
> v3:
> - Input X-Gene MSI base address for irq_domain_alloc_fwnode
> - Add a hook to enforce X-Gene MSI be probed prior acpi_bus_scan happens
> v2:
> - Verify with BIOS version 3.06.25 and 3.07.09
> v1:
> - Initial version
> ---
> drivers/acpi/Makefile | 2 +-
> drivers/acpi/acpi_msi.c | 74 ++++++++++++++++++++++++++++++++++++++
> drivers/acpi/internal.h | 1 +
> drivers/acpi/scan.c | 1 +
> drivers/pci/host/pci-xgene-msi.c | 36 +++++++++++++++++--
> 5 files changed, 110 insertions(+), 4 deletions(-)
> create mode 100644 drivers/acpi/acpi_msi.c
>
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index b1aacfc..c15f5cd 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -40,7 +40,7 @@ acpi-y += ec.o
> acpi-$(CONFIG_ACPI_DOCK) += dock.o
> acpi-y += pci_root.o pci_link.o pci_irq.o
> obj-$(CONFIG_ACPI_MCFG) += pci_mcfg.o
> -acpi-y += acpi_lpss.o acpi_apd.o
> +acpi-y += acpi_lpss.o acpi_apd.o acpi_msi.o
> acpi-y += acpi_platform.o
> acpi-y += acpi_pnp.o
> acpi-$(CONFIG_ARM_AMBA) += acpi_amba.o
> diff --git a/drivers/acpi/acpi_msi.c b/drivers/acpi/acpi_msi.c
> new file mode 100644
> index 0000000..c2f8d26
> --- /dev/null
> +++ b/drivers/acpi/acpi_msi.c
> @@ -0,0 +1,74 @@
> +/*
> + * Enforce MSI driver loaded by PCIe controller driver
> + *
> + * Copyright (c) 2017, MACOM Technology Solutions Corporation
> + * Author: Khuong Dinh <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/acpi.h>
> +#include "internal.h"
> +
> +static acpi_status acpi_create_msi_device(acpi_handle handle, u32 Level,
> + void *context, void **retval)
> +{
> + struct acpi_device *device = NULL;
> + int type = ACPI_BUS_TYPE_DEVICE;
> + unsigned long long sta;
> + acpi_status status;
> + int ret = 0;
> +
> + acpi_bus_get_device(handle, &device);
> + status = acpi_bus_get_status_handle(handle, &sta);
> + if (ACPI_FAILURE(status))
> + sta = 0;
> +
> + device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
> + if (!device)
> + return -ENOMEM;
> +
> + acpi_init_device_object(device, handle, type, sta);
> + ret = acpi_device_add(device, NULL);
> + if (ret)
> + return ret;

Hello, memory leak.

> + device->parent = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
> + INIT_LIST_HEAD(&device->parent->physical_node_list);
> +
> + acpi_device_add_finalize(device);
> +
> + ret = device_attach(&device->dev);
> + if (ret < 0)
> + return ret;

And another one.

> +
> + acpi_create_platform_device(device, NULL);
> + acpi_device_set_enumerated(device);
> +
> + return ret;
> +}
> +
> +static const struct acpi_device_id acpi_msi_device_ids[] = {
> + {"APMC0D0E", 0},

Isn't this an APM-specific thing? Is that supposed to be populated by
all MSI controller drivers? If so, this would better be served by a
separate linker section.

> + { }
> +};
> +
> +int __init acpi_msi_init(void)
> +{
> + acpi_status status;
> + int ret = 0;
> +
> + status = acpi_get_devices(acpi_msi_device_ids[0].id,
> + acpi_create_msi_device, NULL, NULL);
> + if (ACPI_FAILURE(status))
> + ret = -ENODEV;
> +
> + return ret;
> +}

Overall, this part should be a separate patch, and you should start by
explaining what it does exactly. Because so far, all I can see is that
it pre-populates random ACPI device structures, and that definitely seem
fishy to me.

> diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
> index 58dd7ab..3da50d3 100644
> --- a/drivers/acpi/internal.h
> +++ b/drivers/acpi/internal.h
> @@ -80,6 +80,7 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
> void acpi_lpss_init(void);
>
> void acpi_apd_init(void);
> +int acpi_msi_init(void);
>
> acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src);
> bool acpi_queue_hotplug_work(struct work_struct *work);
> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
> index 3389729..8ec4d39 100644
> --- a/drivers/acpi/scan.c
> +++ b/drivers/acpi/scan.c
> @@ -2029,6 +2029,7 @@ int __init acpi_scan_init(void)
> acpi_status status;
> struct acpi_table_stao *stao_ptr;
>
> + acpi_msi_init();
> acpi_pci_root_init();
> acpi_pci_link_init();
> acpi_processor_init();
> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
> index f1b633b..b1768a1 100644
> --- a/drivers/pci/host/pci-xgene-msi.c
> +++ b/drivers/pci/host/pci-xgene-msi.c
> @@ -24,6 +24,7 @@
> #include <linux/pci.h>
> #include <linux/platform_device.h>
> #include <linux/of_pci.h>
> +#include <linux/acpi.h>
>
> #define MSI_IR0 0x000000
> #define MSI_INT0 0x800000
> @@ -39,7 +40,7 @@ struct xgene_msi_group {
> };
>
> struct xgene_msi {
> - struct device_node *node;
> + struct fwnode_handle *fwnode;
> struct irq_domain *inner_domain;
> struct irq_domain *msi_domain;
> u64 msi_addr;
> @@ -249,6 +250,13 @@ static void xgene_irq_domain_free(struct irq_domain *domain,
> .free = xgene_irq_domain_free,
> };
>
> +#ifdef CONFIG_ACPI
> +static struct fwnode_handle *xgene_msi_get_fwnode(struct device *dev)
> +{
> + return xgene_msi_ctrl.fwnode;
> +}
> +#endif
> +
> static int xgene_allocate_domains(struct xgene_msi *msi)
> {
> msi->inner_domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
> @@ -256,7 +264,7 @@ static int xgene_allocate_domains(struct xgene_msi *msi)
> if (!msi->inner_domain)
> return -ENOMEM;
>
> - msi->msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(msi->node),
> + msi->msi_domain = pci_msi_create_irq_domain(msi->fwnode,
> &xgene_msi_domain_info,
> msi->inner_domain);
>
> @@ -265,6 +273,9 @@ static int xgene_allocate_domains(struct xgene_msi *msi)
> return -ENOMEM;
> }
>
> +#ifdef CONFIG_ACPI
> + pci_msi_register_fwnode_provider(&xgene_msi_get_fwnode);
> +#endif
> return 0;
> }
>
> @@ -449,6 +460,13 @@ static int xgene_msi_hwirq_free(unsigned int cpu)
> {},
> };
>
> +#ifdef CONFIG_ACPI
> +static const struct acpi_device_id xgene_msi_acpi_ids[] = {
> + {"APMC0D0E", 0},
> + { },
> +};
> +#endif
> +
> static int xgene_msi_probe(struct platform_device *pdev)
> {
> struct resource *res;
> @@ -469,7 +487,18 @@ static int xgene_msi_probe(struct platform_device *pdev)
> goto error;
> }
> xgene_msi->msi_addr = res->start;
> - xgene_msi->node = pdev->dev.of_node;
> +
> + xgene_msi->fwnode = of_node_to_fwnode(pdev->dev.of_node);
> + if (!xgene_msi->fwnode) {
> + xgene_msi->fwnode =
> + irq_domain_alloc_fwnode((void *)xgene_msi->msi_addr);
> + if (!xgene_msi->fwnode) {
> + dev_err(&pdev->dev, "Failed to create fwnode\n");
> + rc = ENOMEM;
> + goto error;
> + }
> + }
> +
> xgene_msi->num_cpus = num_possible_cpus();
>
> rc = xgene_msi_init_allocator(xgene_msi);
> @@ -540,6 +569,7 @@ static int xgene_msi_probe(struct platform_device *pdev)
> .driver = {
> .name = "xgene-msi",
> .of_match_table = xgene_msi_match_table,
> + .acpi_match_table = ACPI_PTR(xgene_msi_acpi_ids),
> },
> .probe = xgene_msi_probe,
> .remove = xgene_msi_remove,
>

Thanks,

M.
--
Jazz is not dead. It just smells funny...

2017-08-14 18:13:07

by Lorenzo Pieralisi

[permalink] [raw]
Subject: Re: [PATCH v3 pci] PCI/MSI: pci-xgene-msi: Enable MSI support in ACPI boot for X-Gene v1

On Fri, Aug 11, 2017 at 06:02:53PM -0600, Khuong Dinh wrote:
> This patch makes pci-xgene-msi driver ACPI-aware and provides
> MSI capability for X-Gene v1 PCIe controllers in ACPI boot mode.
>
> Signed-off-by: Khuong Dinh <[email protected]>
> [Take over from Duc Dang]
> Acked-by: Marc Zyngier <[email protected]>
> ---
> v3:
> - Input X-Gene MSI base address for irq_domain_alloc_fwnode
> - Add a hook to enforce X-Gene MSI be probed prior acpi_bus_scan happens
> v2:
> - Verify with BIOS version 3.06.25 and 3.07.09
> v1:
> - Initial version
> ---
> drivers/acpi/Makefile | 2 +-
> drivers/acpi/acpi_msi.c | 74 ++++++++++++++++++++++++++++++++++++++
> drivers/acpi/internal.h | 1 +
> drivers/acpi/scan.c | 1 +
> drivers/pci/host/pci-xgene-msi.c | 36 +++++++++++++++++--
> 5 files changed, 110 insertions(+), 4 deletions(-)
> create mode 100644 drivers/acpi/acpi_msi.c
>
> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
> index b1aacfc..c15f5cd 100644
> --- a/drivers/acpi/Makefile
> +++ b/drivers/acpi/Makefile
> @@ -40,7 +40,7 @@ acpi-y += ec.o
> acpi-$(CONFIG_ACPI_DOCK) += dock.o
> acpi-y += pci_root.o pci_link.o pci_irq.o
> obj-$(CONFIG_ACPI_MCFG) += pci_mcfg.o
> -acpi-y += acpi_lpss.o acpi_apd.o
> +acpi-y += acpi_lpss.o acpi_apd.o acpi_msi.o
> acpi-y += acpi_platform.o
> acpi-y += acpi_pnp.o
> acpi-$(CONFIG_ARM_AMBA) += acpi_amba.o
> diff --git a/drivers/acpi/acpi_msi.c b/drivers/acpi/acpi_msi.c
> new file mode 100644
> index 0000000..c2f8d26
> --- /dev/null
> +++ b/drivers/acpi/acpi_msi.c
> @@ -0,0 +1,74 @@
> +/*
> + * Enforce MSI driver loaded by PCIe controller driver
> + *
> + * Copyright (c) 2017, MACOM Technology Solutions Corporation
> + * Author: Khuong Dinh <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License as published by the
> + * Free Software Foundation; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/acpi.h>
> +#include "internal.h"
> +
> +static acpi_status acpi_create_msi_device(acpi_handle handle, u32 Level,
> + void *context, void **retval)
> +{
> + struct acpi_device *device = NULL;
> + int type = ACPI_BUS_TYPE_DEVICE;
> + unsigned long long sta;
> + acpi_status status;
> + int ret = 0;
> +
> + acpi_bus_get_device(handle, &device);
> + status = acpi_bus_get_status_handle(handle, &sta);
> + if (ACPI_FAILURE(status))
> + sta = 0;
> +
> + device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
> + if (!device)
> + return -ENOMEM;
> +
> + acpi_init_device_object(device, handle, type, sta);
> + ret = acpi_device_add(device, NULL);
> + if (ret)
> + return ret;
> + device->parent = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
> + INIT_LIST_HEAD(&device->parent->physical_node_list);
> +
> + acpi_device_add_finalize(device);
> +
> + ret = device_attach(&device->dev);
> + if (ret < 0)
> + return ret;
> +
> + acpi_create_platform_device(device, NULL);
> + acpi_device_set_enumerated(device);
> +
> + return ret;
> +}

Can't this be implemented through acpi_bus_scan(handle) ?

(where handle is your MSI controller acpi_handle ?)

[...]

> static int xgene_msi_probe(struct platform_device *pdev)
> {
> struct resource *res;
> @@ -469,7 +487,18 @@ static int xgene_msi_probe(struct platform_device *pdev)
> goto error;
> }
> xgene_msi->msi_addr = res->start;
> - xgene_msi->node = pdev->dev.of_node;
> +
> + xgene_msi->fwnode = of_node_to_fwnode(pdev->dev.of_node);
> + if (!xgene_msi->fwnode) {
> + xgene_msi->fwnode =
> + irq_domain_alloc_fwnode((void *)xgene_msi->msi_addr);
> + if (!xgene_msi->fwnode) {
> + dev_err(&pdev->dev, "Failed to create fwnode\n");
> + rc = ENOMEM;
> + goto error;
> + }
> + }
> +
> xgene_msi->num_cpus = num_possible_cpus();
>
> rc = xgene_msi_init_allocator(xgene_msi);
> @@ -540,6 +569,7 @@ static int xgene_msi_probe(struct platform_device *pdev)
> .driver = {
> .name = "xgene-msi",
> .of_match_table = xgene_msi_match_table,
> + .acpi_match_table = ACPI_PTR(xgene_msi_acpi_ids),
> },
> .probe = xgene_msi_probe,
> .remove = xgene_msi_remove,

AFAIK you still have not solved the link ordering problem, creating
the platform device before scanning the PCI root bridge is not enough,
because you are not guaranteed to probe the MSI driver first, there
has to be way for registering your driver from the ACPI scan hook.

Thanks,
Lorenzo

2017-08-14 23:10:50

by Khuong Dinh

[permalink] [raw]
Subject: Re: [PATCH v3 pci] PCI/MSI: pci-xgene-msi: Enable MSI support in ACPI boot for X-Gene v1

Hi Marc,

On Mon, Aug 14, 2017 at 1:21 AM, Marc Zyngier <[email protected]> wrote:
> On 12/08/17 01:02, Khuong Dinh wrote:
>> This patch makes pci-xgene-msi driver ACPI-aware and provides
>> MSI capability for X-Gene v1 PCIe controllers in ACPI boot mode.
>>
>> Signed-off-by: Khuong Dinh <[email protected]>
>> [Take over from Duc Dang]
>> Acked-by: Marc Zyngier <[email protected]>
>
> Given that the patch has changed very substantially, this Ack is no
> longer valid.

Yes, I'll remove it.

>> ---
>> v3:
>> - Input X-Gene MSI base address for irq_domain_alloc_fwnode
>> - Add a hook to enforce X-Gene MSI be probed prior acpi_bus_scan happens
>> v2:
>> - Verify with BIOS version 3.06.25 and 3.07.09
>> v1:
>> - Initial version
>> ---
>> drivers/acpi/Makefile | 2 +-
>> drivers/acpi/acpi_msi.c | 74 ++++++++++++++++++++++++++++++++++++++
>> drivers/acpi/internal.h | 1 +
>> drivers/acpi/scan.c | 1 +
>> drivers/pci/host/pci-xgene-msi.c | 36 +++++++++++++++++--
>> 5 files changed, 110 insertions(+), 4 deletions(-)
>> create mode 100644 drivers/acpi/acpi_msi.c
>>
>> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
>> index b1aacfc..c15f5cd 100644
>> --- a/drivers/acpi/Makefile
>> +++ b/drivers/acpi/Makefile
>> @@ -40,7 +40,7 @@ acpi-y += ec.o
>> acpi-$(CONFIG_ACPI_DOCK) += dock.o
>> acpi-y += pci_root.o pci_link.o pci_irq.o
>> obj-$(CONFIG_ACPI_MCFG) += pci_mcfg.o
>> -acpi-y += acpi_lpss.o acpi_apd.o
>> +acpi-y += acpi_lpss.o acpi_apd.o acpi_msi.o
>> acpi-y += acpi_platform.o
>> acpi-y += acpi_pnp.o
>> acpi-$(CONFIG_ARM_AMBA) += acpi_amba.o
>> diff --git a/drivers/acpi/acpi_msi.c b/drivers/acpi/acpi_msi.c
>> new file mode 100644
>> index 0000000..c2f8d26
>> --- /dev/null
>> +++ b/drivers/acpi/acpi_msi.c
>> @@ -0,0 +1,74 @@
>> +/*
>> + * Enforce MSI driver loaded by PCIe controller driver
>> + *
>> + * Copyright (c) 2017, MACOM Technology Solutions Corporation
>> + * Author: Khuong Dinh <[email protected]>
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License as published by the
>> + * Free Software Foundation; either version 2 of the License, or (at your
>> + * option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/acpi.h>
>> +#include "internal.h"
>> +
>> +static acpi_status acpi_create_msi_device(acpi_handle handle, u32 Level,
>> + void *context, void **retval)
>> +{
>> + struct acpi_device *device = NULL;
>> + int type = ACPI_BUS_TYPE_DEVICE;
>> + unsigned long long sta;
>> + acpi_status status;
>> + int ret = 0;
>> +
>> + acpi_bus_get_device(handle, &device);
>> + status = acpi_bus_get_status_handle(handle, &sta);
>> + if (ACPI_FAILURE(status))
>> + sta = 0;
>> +
>> + device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
>> + if (!device)
>> + return -ENOMEM;
>> +
>> + acpi_init_device_object(device, handle, type, sta);
>> + ret = acpi_device_add(device, NULL);
>> + if (ret)
>> + return ret;
>
> Hello, memory leak.

I'll update the code to release the memory when the error happens.

>> + device->parent = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
>> + INIT_LIST_HEAD(&device->parent->physical_node_list);
>> +
>> + acpi_device_add_finalize(device);
>> +
>> + ret = device_attach(&device->dev);
>> + if (ret < 0)
>> + return ret;
>
> And another one.

I'll update the code to release the memory when the error happens.

>> +
>> + acpi_create_platform_device(device, NULL);
>> + acpi_device_set_enumerated(device);
>> +
>> + return ret;
>> +}
>> +
>> +static const struct acpi_device_id acpi_msi_device_ids[] = {
>> + {"APMC0D0E", 0},
>
> Isn't this an APM-specific thing? Is that supposed to be populated by
> all MSI controller drivers? If so, this would better be served by a
> separate linker section.

The code is generic for MSI controller drivers.
It will walk through the HID to get the MSI device and enforce it
happens before acpi_bus_scan.
This mechanism will help to avoid MSI driver from relying on ACPI
device nodes ordering.
For now, it just supports for APM X-Gene v1.

>> + { }
>> +};
>> +
>> +int __init acpi_msi_init(void)
>> +{
>> + acpi_status status;
>> + int ret = 0;
>> +
>> + status = acpi_get_devices(acpi_msi_device_ids[0].id,
>> + acpi_create_msi_device, NULL, NULL);
>> + if (ACPI_FAILURE(status))
>> + ret = -ENODEV;
>> +
>> + return ret;
>> +}
>
> Overall, this part should be a separate patch, and you should start by
> explaining what it does exactly. Because so far, all I can see is that
> it pre-populates random ACPI device structures, and that definitely seem
> fishy to me.

I'm still following Lorenzo's approach to resolve the general ACPI
dependence issue.
As soon as it's fine, I'll re-visit my prior suggestion to separate
the original patch to enable ACPI support for PCIe MSI and the general
ACPI dependence issue.
The patch is to add a hook in drivers/acpi/scan.c to enforce MSI
driver to be initialized and registered before acpi_bus_scan happens,
so that MSI driver will not rely on ACPI device nodes ordering.
It will walk through the HID to get the MSI device (e.g:
acip_get_devices). When the device is found, the callback will be
called and we will initialize MSI device by its handle, start to
attach MSI device to a driver, create ACPI platform device for MSI
APCI device node and marked it as visited.
In result, it will happen on top of acpi_bus_scan.

>> diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
>> index 58dd7ab..3da50d3 100644
>> --- a/drivers/acpi/internal.h
>> +++ b/drivers/acpi/internal.h
>> @@ -80,6 +80,7 @@ int acpi_scan_add_handler_with_hotplug(struct acpi_scan_handler *handler,
>> void acpi_lpss_init(void);
>>
>> void acpi_apd_init(void);
>> +int acpi_msi_init(void);
>>
>> acpi_status acpi_hotplug_schedule(struct acpi_device *adev, u32 src);
>> bool acpi_queue_hotplug_work(struct work_struct *work);
>> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
>> index 3389729..8ec4d39 100644
>> --- a/drivers/acpi/scan.c
>> +++ b/drivers/acpi/scan.c
>> @@ -2029,6 +2029,7 @@ int __init acpi_scan_init(void)
>> acpi_status status;
>> struct acpi_table_stao *stao_ptr;
>>
>> + acpi_msi_init();
>> acpi_pci_root_init();
>> acpi_pci_link_init();
>> acpi_processor_init();
>> diff --git a/drivers/pci/host/pci-xgene-msi.c b/drivers/pci/host/pci-xgene-msi.c
>> index f1b633b..b1768a1 100644
>> --- a/drivers/pci/host/pci-xgene-msi.c
>> +++ b/drivers/pci/host/pci-xgene-msi.c
>> @@ -24,6 +24,7 @@
>> #include <linux/pci.h>
>> #include <linux/platform_device.h>
>> #include <linux/of_pci.h>
>> +#include <linux/acpi.h>
>>
>> #define MSI_IR0 0x000000
>> #define MSI_INT0 0x800000
>> @@ -39,7 +40,7 @@ struct xgene_msi_group {
>> };
>>
>> struct xgene_msi {
>> - struct device_node *node;
>> + struct fwnode_handle *fwnode;
>> struct irq_domain *inner_domain;
>> struct irq_domain *msi_domain;
>> u64 msi_addr;
>> @@ -249,6 +250,13 @@ static void xgene_irq_domain_free(struct irq_domain *domain,
>> .free = xgene_irq_domain_free,
>> };
>>
>> +#ifdef CONFIG_ACPI
>> +static struct fwnode_handle *xgene_msi_get_fwnode(struct device *dev)
>> +{
>> + return xgene_msi_ctrl.fwnode;
>> +}
>> +#endif
>> +
>> static int xgene_allocate_domains(struct xgene_msi *msi)
>> {
>> msi->inner_domain = irq_domain_add_linear(NULL, NR_MSI_VEC,
>> @@ -256,7 +264,7 @@ static int xgene_allocate_domains(struct xgene_msi *msi)
>> if (!msi->inner_domain)
>> return -ENOMEM;
>>
>> - msi->msi_domain = pci_msi_create_irq_domain(of_node_to_fwnode(msi->node),
>> + msi->msi_domain = pci_msi_create_irq_domain(msi->fwnode,
>> &xgene_msi_domain_info,
>> msi->inner_domain);
>>
>> @@ -265,6 +273,9 @@ static int xgene_allocate_domains(struct xgene_msi *msi)
>> return -ENOMEM;
>> }
>>
>> +#ifdef CONFIG_ACPI
>> + pci_msi_register_fwnode_provider(&xgene_msi_get_fwnode);
>> +#endif
>> return 0;
>> }
>>
>> @@ -449,6 +460,13 @@ static int xgene_msi_hwirq_free(unsigned int cpu)
>> {},
>> };
>>
>> +#ifdef CONFIG_ACPI
>> +static const struct acpi_device_id xgene_msi_acpi_ids[] = {
>> + {"APMC0D0E", 0},
>> + { },
>> +};
>> +#endif
>> +
>> static int xgene_msi_probe(struct platform_device *pdev)
>> {
>> struct resource *res;
>> @@ -469,7 +487,18 @@ static int xgene_msi_probe(struct platform_device *pdev)
>> goto error;
>> }
>> xgene_msi->msi_addr = res->start;
>> - xgene_msi->node = pdev->dev.of_node;
>> +
>> + xgene_msi->fwnode = of_node_to_fwnode(pdev->dev.of_node);
>> + if (!xgene_msi->fwnode) {
>> + xgene_msi->fwnode =
>> + irq_domain_alloc_fwnode((void *)xgene_msi->msi_addr);
>> + if (!xgene_msi->fwnode) {
>> + dev_err(&pdev->dev, "Failed to create fwnode\n");
>> + rc = ENOMEM;
>> + goto error;
>> + }
>> + }
>> +
>> xgene_msi->num_cpus = num_possible_cpus();
>>
>> rc = xgene_msi_init_allocator(xgene_msi);
>> @@ -540,6 +569,7 @@ static int xgene_msi_probe(struct platform_device *pdev)
>> .driver = {
>> .name = "xgene-msi",
>> .of_match_table = xgene_msi_match_table,
>> + .acpi_match_table = ACPI_PTR(xgene_msi_acpi_ids),
>> },
>> .probe = xgene_msi_probe,
>> .remove = xgene_msi_remove,
>>
>
> Thanks,
>
> M.
> --
> Jazz is not dead. It just smells funny...

2017-08-14 23:17:39

by Khuong Dinh

[permalink] [raw]
Subject: Re: [PATCH v3 pci] PCI/MSI: pci-xgene-msi: Enable MSI support in ACPI boot for X-Gene v1

Hi Lorenzo,


On Mon, Aug 14, 2017 at 11:15 AM, Lorenzo Pieralisi
<[email protected]> wrote:
> On Fri, Aug 11, 2017 at 06:02:53PM -0600, Khuong Dinh wrote:
>> This patch makes pci-xgene-msi driver ACPI-aware and provides
>> MSI capability for X-Gene v1 PCIe controllers in ACPI boot mode.
>>
>> Signed-off-by: Khuong Dinh <[email protected]>
>> [Take over from Duc Dang]
>> Acked-by: Marc Zyngier <[email protected]>
>> ---
>> v3:
>> - Input X-Gene MSI base address for irq_domain_alloc_fwnode
>> - Add a hook to enforce X-Gene MSI be probed prior acpi_bus_scan happens
>> v2:
>> - Verify with BIOS version 3.06.25 and 3.07.09
>> v1:
>> - Initial version
>> ---
>> drivers/acpi/Makefile | 2 +-
>> drivers/acpi/acpi_msi.c | 74 ++++++++++++++++++++++++++++++++++++++
>> drivers/acpi/internal.h | 1 +
>> drivers/acpi/scan.c | 1 +
>> drivers/pci/host/pci-xgene-msi.c | 36 +++++++++++++++++--
>> 5 files changed, 110 insertions(+), 4 deletions(-)
>> create mode 100644 drivers/acpi/acpi_msi.c
>>
>> diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
>> index b1aacfc..c15f5cd 100644
>> --- a/drivers/acpi/Makefile
>> +++ b/drivers/acpi/Makefile
>> @@ -40,7 +40,7 @@ acpi-y += ec.o
>> acpi-$(CONFIG_ACPI_DOCK) += dock.o
>> acpi-y += pci_root.o pci_link.o pci_irq.o
>> obj-$(CONFIG_ACPI_MCFG) += pci_mcfg.o
>> -acpi-y += acpi_lpss.o acpi_apd.o
>> +acpi-y += acpi_lpss.o acpi_apd.o acpi_msi.o
>> acpi-y += acpi_platform.o
>> acpi-y += acpi_pnp.o
>> acpi-$(CONFIG_ARM_AMBA) += acpi_amba.o
>> diff --git a/drivers/acpi/acpi_msi.c b/drivers/acpi/acpi_msi.c
>> new file mode 100644
>> index 0000000..c2f8d26
>> --- /dev/null
>> +++ b/drivers/acpi/acpi_msi.c
>> @@ -0,0 +1,74 @@
>> +/*
>> + * Enforce MSI driver loaded by PCIe controller driver
>> + *
>> + * Copyright (c) 2017, MACOM Technology Solutions Corporation
>> + * Author: Khuong Dinh <[email protected]>
>> + *
>> + * This program is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License as published by the
>> + * Free Software Foundation; either version 2 of the License, or (at your
>> + * option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>> + * GNU General Public License for more details.
>> + */
>> +
>> +#include <linux/acpi.h>
>> +#include "internal.h"
>> +
>> +static acpi_status acpi_create_msi_device(acpi_handle handle, u32 Level,
>> + void *context, void **retval)
>> +{
>> + struct acpi_device *device = NULL;
>> + int type = ACPI_BUS_TYPE_DEVICE;
>> + unsigned long long sta;
>> + acpi_status status;
>> + int ret = 0;
>> +
>> + acpi_bus_get_device(handle, &device);
>> + status = acpi_bus_get_status_handle(handle, &sta);
>> + if (ACPI_FAILURE(status))
>> + sta = 0;
>> +
>> + device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
>> + if (!device)
>> + return -ENOMEM;
>> +
>> + acpi_init_device_object(device, handle, type, sta);
>> + ret = acpi_device_add(device, NULL);
>> + if (ret)
>> + return ret;
>> + device->parent = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
>> + INIT_LIST_HEAD(&device->parent->physical_node_list);
>> +
>> + acpi_device_add_finalize(device);
>> +
>> + ret = device_attach(&device->dev);
>> + if (ret < 0)
>> + return ret;
>> +
>> + acpi_create_platform_device(device, NULL);
>> + acpi_device_set_enumerated(device);
>> +
>> + return ret;
>> +}
>
> Can't this be implemented through acpi_bus_scan(handle) ?
>
> (where handle is your MSI controller acpi_handle ?)

I had an experiment to use acpi_bus_scan(handle) to register MSI
driver, but it's not success as @handle for acpi_bus_scan is the root
of the namespace scope to scan.
The driver registration to ACPI platform happens with the following code path:
acpi_bus_scan
acpi_bus_check_add
acpi_bus_attach
device_attach
acpi_default_enumeration
acpi_create_platform_device
acpi_device_set_enumerated

acpi_bus_attach is recursively happened through ACPI nodes in order.


> [...]
>
>> static int xgene_msi_probe(struct platform_device *pdev)
>> {
>> struct resource *res;
>> @@ -469,7 +487,18 @@ static int xgene_msi_probe(struct platform_device *pdev)
>> goto error;
>> }
>> xgene_msi->msi_addr = res->start;
>> - xgene_msi->node = pdev->dev.of_node;
>> +
>> + xgene_msi->fwnode = of_node_to_fwnode(pdev->dev.of_node);
>> + if (!xgene_msi->fwnode) {
>> + xgene_msi->fwnode =
>> + irq_domain_alloc_fwnode((void *)xgene_msi->msi_addr);
>> + if (!xgene_msi->fwnode) {
>> + dev_err(&pdev->dev, "Failed to create fwnode\n");
>> + rc = ENOMEM;
>> + goto error;
>> + }
>> + }
>> +
>> xgene_msi->num_cpus = num_possible_cpus();
>>
>> rc = xgene_msi_init_allocator(xgene_msi);
>> @@ -540,6 +569,7 @@ static int xgene_msi_probe(struct platform_device *pdev)
>> .driver = {
>> .name = "xgene-msi",
>> .of_match_table = xgene_msi_match_table,
>> + .acpi_match_table = ACPI_PTR(xgene_msi_acpi_ids),
>> },
>> .probe = xgene_msi_probe,
>> .remove = xgene_msi_remove,
>
> AFAIK you still have not solved the link ordering problem, creating
> the platform device before scanning the PCI root bridge is not enough,
> because you are not guaranteed to probe the MSI driver first, there
> has to be way for registering your driver from the ACPI scan hook.

With this implementation, I added a hook to enforce the MSI driver
initialization and registration before acpi_bus_scan happens.
It will walk through the HID to get the MSI device (e.g:
acpi_get_devices). When the device is found, the callback will be
called and we will initialize MSI device by its handle, start to
attach MSI device to a driver, create ACPI platform device for MSI
APCI device node and marked it as visited.
In result, it will happen on top of acpi_bus_scan.


> Thanks,
> Lorenzo

2017-08-15 10:49:52

by Lorenzo Pieralisi

[permalink] [raw]
Subject: Re: [PATCH v3 pci] PCI/MSI: pci-xgene-msi: Enable MSI support in ACPI boot for X-Gene v1

On Mon, Aug 14, 2017 at 04:17:35PM -0700, Khuong Dinh wrote:

[...]

> >> +static acpi_status acpi_create_msi_device(acpi_handle handle, u32 Level,
> >> + void *context, void **retval)
> >> +{
> >> + struct acpi_device *device = NULL;
> >> + int type = ACPI_BUS_TYPE_DEVICE;
> >> + unsigned long long sta;
> >> + acpi_status status;
> >> + int ret = 0;
> >> +
> >> + acpi_bus_get_device(handle, &device);
> >> + status = acpi_bus_get_status_handle(handle, &sta);
> >> + if (ACPI_FAILURE(status))
> >> + sta = 0;
> >> +
> >> + device = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
> >> + if (!device)
> >> + return -ENOMEM;
> >> +
> >> + acpi_init_device_object(device, handle, type, sta);
> >> + ret = acpi_device_add(device, NULL);
> >> + if (ret)
> >> + return ret;
> >> + device->parent = kzalloc(sizeof(struct acpi_device), GFP_KERNEL);
> >> + INIT_LIST_HEAD(&device->parent->physical_node_list);
> >> +
> >> + acpi_device_add_finalize(device);
> >> +
> >> + ret = device_attach(&device->dev);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> + acpi_create_platform_device(device, NULL);
> >> + acpi_device_set_enumerated(device);
> >> +
> >> + return ret;
> >> +}
> >
> > Can't this be implemented through acpi_bus_scan(handle) ?
> >
> > (where handle is your MSI controller acpi_handle ?)
>
> I had an experiment to use acpi_bus_scan(handle) to register MSI
> driver, but it's not success as @handle for acpi_bus_scan is the root
> of the namespace scope to scan.
> The driver registration to ACPI platform happens with the following code path:
> acpi_bus_scan
> acpi_bus_check_add
> acpi_bus_attach
> device_attach
> acpi_default_enumeration
> acpi_create_platform_device
> acpi_device_set_enumerated
>
> acpi_bus_attach is recursively happened through ACPI nodes in order.

What I was saying is to call acpi_bus_scan() on the MSI controller
handle (as start_object - see acpi_walk_namespace()).

> > [...]
> >
> >> static int xgene_msi_probe(struct platform_device *pdev)
> >> {
> >> struct resource *res;
> >> @@ -469,7 +487,18 @@ static int xgene_msi_probe(struct platform_device *pdev)
> >> goto error;
> >> }
> >> xgene_msi->msi_addr = res->start;
> >> - xgene_msi->node = pdev->dev.of_node;
> >> +
> >> + xgene_msi->fwnode = of_node_to_fwnode(pdev->dev.of_node);
> >> + if (!xgene_msi->fwnode) {
> >> + xgene_msi->fwnode =
> >> + irq_domain_alloc_fwnode((void *)xgene_msi->msi_addr);
> >> + if (!xgene_msi->fwnode) {
> >> + dev_err(&pdev->dev, "Failed to create fwnode\n");
> >> + rc = ENOMEM;
> >> + goto error;
> >> + }
> >> + }
> >> +
> >> xgene_msi->num_cpus = num_possible_cpus();
> >>
> >> rc = xgene_msi_init_allocator(xgene_msi);
> >> @@ -540,6 +569,7 @@ static int xgene_msi_probe(struct platform_device *pdev)
> >> .driver = {
> >> .name = "xgene-msi",
> >> .of_match_table = xgene_msi_match_table,
> >> + .acpi_match_table = ACPI_PTR(xgene_msi_acpi_ids),
> >> },
> >> .probe = xgene_msi_probe,
> >> .remove = xgene_msi_remove,
> >
> > AFAIK you still have not solved the link ordering problem, creating
> > the platform device before scanning the PCI root bridge is not enough,
> > because you are not guaranteed to probe the MSI driver first, there
> > has to be way for registering your driver from the ACPI scan hook.
>
> With this implementation, I added a hook to enforce the MSI driver
> initialization and registration before acpi_bus_scan happens.

I do not know if I am mistaken but all I see is still:

subsys_initcall(xgene_pcie_msi_init);

which is not guaranteed to be called before the PCI host bridge
is scanned. You have to have a way to _probe_ the MSI driver before
the PCI host bridge is scanned, which is different.

> It will walk through the HID to get the MSI device (e.g:
> acpi_get_devices). When the device is found, the callback will be
> called and we will initialize MSI device by its handle, start to
> attach MSI device to a driver, create ACPI platform device for MSI
> APCI device node and marked it as visited.

But if the MSI driver is still not registered the whole concept still
does not work. You are not guaranteed ordering between initcalls at
the same level.

Thanks,
Lorenzo