[PATCH] bus: fsl-mc: Add ACPI support for fsl-mc

From: Makarand Pawagi
Date: Tue Jan 28 2020 - 03:10:07 EST


ACPI support is added in the fsl-mc driver. Driver will parse
MC DSDT table to extract memory and other resorces.

Interrupt (GIC ITS) information will be extracted from MADT table
by drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c.

IORT table will be parsed to configure DMA.

Signed-off-by: Makarand Pawagi <makarand.pawagi@xxxxxxx>
---
drivers/acpi/arm64/iort.c | 53 +++++++++++++++++++++
drivers/bus/fsl-mc/dprc-driver.c | 3 +-
drivers/bus/fsl-mc/fsl-mc-bus.c | 48 +++++++++++++------
drivers/bus/fsl-mc/fsl-mc-msi.c | 10 +++-
drivers/bus/fsl-mc/fsl-mc-private.h | 4 +-
drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c | 71 ++++++++++++++++++++++++++++-
include/linux/acpi_iort.h | 5 ++
7 files changed, 174 insertions(+), 20 deletions(-)

diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c
index 33f7198..beb9cd5 100644
--- a/drivers/acpi/arm64/iort.c
+++ b/drivers/acpi/arm64/iort.c
@@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/pci.h>
+#include <linux/fsl/mc.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

@@ -622,6 +623,29 @@ static int iort_dev_find_its_id(struct device *dev, u32 req_id,
}

/**
+ * iort_get_fsl_mc_device_domain() - Find MSI domain related to a device
+ * @dev: The device.
+ * @mc_icid: ICID for the fsl_mc device.
+ *
+ * Returns: the MSI domain for this device, NULL otherwise
+ */
+struct irq_domain *iort_get_fsl_mc_device_domain(struct device *dev,
+ u32 mc_icid)
+{
+ struct fwnode_handle *handle;
+ int its_id;
+
+ if (iort_dev_find_its_id(dev, mc_icid, 0, &its_id))
+ return NULL;
+
+ handle = iort_find_domain_token(its_id);
+ if (!handle)
+ return NULL;
+
+ return irq_find_matching_fwnode(handle, DOMAIN_BUS_FSL_MC_MSI);
+}
+
+/**
* iort_get_device_domain() - Find MSI domain related to a device
* @dev: The device.
* @req_id: Requester ID for the device.
@@ -924,6 +948,21 @@ static int iort_pci_iommu_init(struct pci_dev *pdev, u16 alias, void *data)
return iort_iommu_xlate(info->dev, parent, streamid);
}

+static int iort_fsl_mc_iommu_init(struct device *dev,
+ struct acpi_iort_node *node, u32 *streamid)
+{
+ struct acpi_iort_node *parent;
+ struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev);
+
+ parent = iort_node_map_id(node, mc_dev->icid, streamid,
+ IORT_IOMMU_TYPE);
+
+ if (parent)
+ return iort_iommu_xlate(dev, parent, *streamid);
+
+ return 0;
+}
+
/**
* iort_iommu_configure - Set-up IOMMU configuration for a device.
*
@@ -962,6 +1001,20 @@ const struct iommu_ops *iort_iommu_configure(struct device *dev)

if (!err && iort_pci_rc_supports_ats(node))
dev->iommu_fwspec->flags |= IOMMU_FWSPEC_PCI_RC_ATS;
+ } else if (dev_is_fsl_mc(dev)) {
+ struct device *dma_dev = dev;
+
+ if (!(to_acpi_device_node(dma_dev->fwnode))) {
+ while (dev_is_fsl_mc(dma_dev))
+ dma_dev = dma_dev->parent;
+ }
+
+ node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+ iort_match_node_callback, dma_dev);
+ if (!node)
+ return NULL;
+
+ err = iort_fsl_mc_iommu_init(dev, node, &streamid);
} else {
int i = 0;

diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c
index c8b1c38..31dd790 100644
--- a/drivers/bus/fsl-mc/dprc-driver.c
+++ b/drivers/bus/fsl-mc/dprc-driver.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
* Author: German Rivera <German.Rivera@xxxxxxxxxxxxx>
+ * Copyright 2018-2020 NXP
*
*/

@@ -638,7 +639,7 @@ static int dprc_probe(struct fsl_mc_device *mc_dev)
return -EINVAL;

error = fsl_mc_find_msi_domain(parent_dev,
- &mc_msi_domain);
+ &mc_msi_domain, mc_dev);
if (error < 0) {
dev_warn(&mc_dev->dev,
"WARNING: MC bus without interrupt support\n");
diff --git a/drivers/bus/fsl-mc/fsl-mc-bus.c b/drivers/bus/fsl-mc/fsl-mc-bus.c
index a07cc19..5d388e4 100644
--- a/drivers/bus/fsl-mc/fsl-mc-bus.c
+++ b/drivers/bus/fsl-mc/fsl-mc-bus.c
@@ -4,11 +4,13 @@
*
* Copyright (C) 2014-2016 Freescale Semiconductor, Inc.
* Author: German Rivera <German.Rivera@xxxxxxxxxxxxx>
+ * Copyright 2018-2020 NXP
*
*/

#define pr_fmt(fmt) "fsl-mc: " fmt

+#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
@@ -129,12 +131,25 @@ static int fsl_mc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)

static int fsl_mc_dma_configure(struct device *dev)
{
+ enum dev_dma_attr attr;
struct device *dma_dev = dev;
+ int err = -ENODEV;

while (dev_is_fsl_mc(dma_dev))
dma_dev = dma_dev->parent;

- return of_dma_configure(dev, dma_dev->of_node, 0);
+ if (dma_dev->of_node)
+ err = of_dma_configure(dev, dma_dev->of_node, 1);
+ else {
+ if (has_acpi_companion(dma_dev)) {
+ attr = acpi_get_dma_attr(to_acpi_device_node
+ (dma_dev->fwnode));
+ if (attr != DEV_DMA_NOT_SUPPORTED)
+ err = acpi_dma_configure(dev, attr);
+ }
+ }
+
+ return err;
}

static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
@@ -863,7 +878,7 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
phys_addr_t mc_portal_phys_addr;
u32 mc_portal_size;
struct mc_version mc_version;
- struct resource res;
+ struct resource *plat_res;

mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL);
if (!mc)
@@ -874,16 +889,9 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
/*
* Get physical address of MC portal for the root DPRC:
*/
- error = of_address_to_resource(pdev->dev.of_node, 0, &res);
- if (error < 0) {
- dev_err(&pdev->dev,
- "of_address_to_resource() failed for %pOF\n",
- pdev->dev.of_node);
- return error;
- }
-
- mc_portal_phys_addr = res.start;
- mc_portal_size = resource_size(&res);
+ plat_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ mc_portal_phys_addr = plat_res->start;
+ mc_portal_size = resource_size(plat_res);
error = fsl_create_mc_io(&pdev->dev, mc_portal_phys_addr,
mc_portal_size, NULL,
FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, &mc_io);
@@ -900,11 +908,13 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "MC firmware version: %u.%u.%u\n",
mc_version.major, mc_version.minor, mc_version.revision);

- error = get_mc_addr_translation_ranges(&pdev->dev,
+ if (dev_of_node(&pdev->dev)) {
+ error = get_mc_addr_translation_ranges(&pdev->dev,
&mc->translation_ranges,
&mc->num_translation_ranges);
- if (error < 0)
- goto error_cleanup_mc_io;
+ if (error < 0)
+ goto error_cleanup_mc_io;
+ }

error = dprc_get_container_id(mc_io, 0, &container_id);
if (error < 0) {
@@ -931,6 +941,7 @@ static int fsl_mc_bus_probe(struct platform_device *pdev)
goto error_cleanup_mc_io;

mc->root_mc_bus_dev = mc_bus_dev;
+ mc_bus_dev->dev.fwnode = pdev->dev.fwnode;
return 0;

error_cleanup_mc_io:
@@ -964,11 +975,18 @@ static const struct of_device_id fsl_mc_bus_match_table[] = {

MODULE_DEVICE_TABLE(of, fsl_mc_bus_match_table);

+static const struct acpi_device_id fsl_mc_bus_acpi_match_table[] = {
+ {"NXP0008", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, fsl_mc_bus_acpi_match_table);
+
static struct platform_driver fsl_mc_bus_driver = {
.driver = {
.name = "fsl_mc_bus",
.pm = NULL,
.of_match_table = fsl_mc_bus_match_table,
+ .acpi_match_table = fsl_mc_bus_acpi_match_table,
},
.probe = fsl_mc_bus_probe,
.remove = fsl_mc_bus_remove,
diff --git a/drivers/bus/fsl-mc/fsl-mc-msi.c b/drivers/bus/fsl-mc/fsl-mc-msi.c
index 8b9c66d..bd10952 100644
--- a/drivers/bus/fsl-mc/fsl-mc-msi.c
+++ b/drivers/bus/fsl-mc/fsl-mc-msi.c
@@ -4,6 +4,7 @@
*
* Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
* Author: German Rivera <German.Rivera@xxxxxxxxxxxxx>
+ * Copyright 2020 NXP
*
*/

@@ -13,6 +14,7 @@
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/msi.h>
+#include <linux/acpi_iort.h>

#include "fsl-mc-private.h"

@@ -178,13 +180,19 @@ struct irq_domain *fsl_mc_msi_create_irq_domain(struct fwnode_handle *fwnode,
}

int fsl_mc_find_msi_domain(struct device *mc_platform_dev,
- struct irq_domain **mc_msi_domain)
+ struct irq_domain **mc_msi_domain,
+ struct fsl_mc_device *mc_dev)
{
struct irq_domain *msi_domain;
struct device_node *mc_of_node = mc_platform_dev->of_node;

msi_domain = of_msi_get_domain(mc_platform_dev, mc_of_node,
DOMAIN_BUS_FSL_MC_MSI);
+
+ if (!msi_domain)
+ msi_domain = iort_get_fsl_mc_device_domain(mc_platform_dev,
+ mc_dev->icid);
+
if (!msi_domain) {
pr_err("Unable to find fsl-mc MSI domain for %pOF\n",
mc_of_node);
diff --git a/drivers/bus/fsl-mc/fsl-mc-private.h b/drivers/bus/fsl-mc/fsl-mc-private.h
index 21ca8c756..e8f4c0f 100644
--- a/drivers/bus/fsl-mc/fsl-mc-private.h
+++ b/drivers/bus/fsl-mc/fsl-mc-private.h
@@ -3,6 +3,7 @@
* Freescale Management Complex (MC) bus private declarations
*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
+ * Copyright 2020 NXP
*
*/
#ifndef _FSL_MC_PRIVATE_H_
@@ -596,7 +597,8 @@ int fsl_mc_msi_domain_alloc_irqs(struct device *dev,
void fsl_mc_msi_domain_free_irqs(struct device *dev);

int fsl_mc_find_msi_domain(struct device *mc_platform_dev,
- struct irq_domain **mc_msi_domain);
+ struct irq_domain **mc_msi_domain,
+ struct fsl_mc_device *mc_dev);

int fsl_mc_populate_irq_pool(struct fsl_mc_bus *mc_bus,
unsigned int irq_count);
diff --git a/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c b/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c
index 606efa6..df99170 100644
--- a/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c
+++ b/drivers/irqchip/irq-gic-v3-its-fsl-mc-msi.c
@@ -4,9 +4,11 @@
*
* Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
* Author: German Rivera <German.Rivera@xxxxxxxxxxxxx>
+ * Copyright 2020 NXP
*
*/

+#include <linux/acpi_iort.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/irq.h>
@@ -66,7 +68,66 @@ static const struct of_device_id its_device_id[] = {
{},
};

-static int __init its_fsl_mc_msi_init(void)
+static int __init its_fsl_mc_msi_init_one(struct fwnode_handle *handle,
+ const char *name)
+{
+ struct irq_domain *parent;
+ struct irq_domain *mc_msi_domain;
+
+ parent = irq_find_matching_fwnode(handle, DOMAIN_BUS_NEXUS);
+ if (!parent || !msi_get_domain_info(parent)) {
+ pr_err("%s: Unable to locate ITS domain\n", name);
+ return -ENXIO;
+ }
+
+ mc_msi_domain = fsl_mc_msi_create_irq_domain(
+ handle,
+ &its_fsl_mc_msi_domain_info,
+ parent);
+ if (!mc_msi_domain)
+ pr_err("ACPIF: unable to create fsl-mc domain\n");
+
+ pr_info("fsl-mc MSI: domain created\n");
+
+ return 0;
+}
+
+static int __init
+its_fsl_mc_msi_parse_madt(union acpi_subtable_headers *header,
+ const unsigned long end)
+{
+ struct acpi_madt_generic_translator *its_entry;
+ struct fwnode_handle *dom_handle;
+ const char *node_name;
+ int err = -ENXIO;
+
+ its_entry = (struct acpi_madt_generic_translator *)header;
+ node_name = kasprintf(GFP_KERNEL, "ITS@0x%lx",
+ (long)its_entry->base_address);
+
+ dom_handle = iort_find_domain_token(its_entry->translation_id);
+ if (!dom_handle) {
+ pr_err("%s: Unable to locate ITS domain handle\n", node_name);
+ goto out;
+ }
+
+ err = its_fsl_mc_msi_init_one(dom_handle, node_name);
+ if (!err)
+ pr_info("fsl-mc MSI: %s domain created\n", node_name);
+
+out:
+ kfree(node_name);
+ return err;
+}
+
+static int __init its_fsl_mc_acpi_msi_init(void)
+{
+ acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_TRANSLATOR,
+ its_fsl_mc_msi_parse_madt, 0);
+ return 0;
+}
+
+static int __init its_fsl_mc_of_msi_init(void)
{
struct device_node *np;
struct irq_domain *parent;
@@ -96,8 +157,14 @@ static int __init its_fsl_mc_msi_init(void)

pr_info("fsl-mc MSI: %pOF domain created\n", np);
}
-
return 0;
}

+static int __init its_fsl_mc_msi_init(void)
+{
+ its_fsl_mc_of_msi_init();
+ its_fsl_mc_acpi_msi_init();
+
+ return 0;
+}
early_initcall(its_fsl_mc_msi_init);
diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h
index 8e7e2ec..0afc608 100644
--- a/include/linux/acpi_iort.h
+++ b/include/linux/acpi_iort.h
@@ -30,6 +30,8 @@ struct fwnode_handle *iort_find_domain_token(int trans_id);
void acpi_iort_init(void);
u32 iort_msi_map_rid(struct device *dev, u32 req_id);
struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id);
+struct irq_domain *iort_get_fsl_mc_device_domain(struct device *dev,
+ u32 req_id);
void acpi_configure_pmsi_domain(struct device *dev);
int iort_pmsi_get_dev_id(struct device *dev, u32 *dev_id);
/* IOMMU interface */
@@ -40,6 +42,9 @@ int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head);
static inline void acpi_iort_init(void) { }
static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id)
{ return req_id; }
+static inline struct irq_domain *iort_get_fsl_mc_device_domain
+ (struct device *dev, u32 req_id)
+{ return NULL; }
static inline struct irq_domain *iort_get_device_domain(struct device *dev,
u32 req_id)
{ return NULL; }
--
2.7.4