Discussion:
[RFC PATCH v3 00/13] ACPI IORT ARM SMMU v3 support
(too old to reply)
Lorenzo Pieralisi
2016-07-20 11:23:57 UTC
Permalink
This RFC patch series is v3 of a previous posting:

https://lkml.org/lkml/2016/6/7/523

v2 -> v3
- Rebased on top of dependencies series [1][2][3](v4.7-rc3)
- Added back reliance on ACPI early probing infrastructure
- Patch[1-3] merged through other dependent series
- Added back IOMMU fwnode generalization
- Move SMMU v3 static functions configuration to IORT code
- Implemented generic IOMMU fwspec API
- Added code to implement fwnode platform device look-up

v1 -> v2:
- Rebased on top of dependencies series [1][2][3](v4.7-rc1)
- Removed IOMMU fwnode generalization
- Implemented ARM SMMU v3 ACPI probing instead of ARM SMMU v2
owing to patch series dependencies [1]
- Moved platform device creation logic to IORT code to
generalize its usage for ARM SMMU v1-v2-v3 components
- Removed reliance on ACPI early device probing
- Created IORT specific iommu_xlate() translation hook leaving
OF code unchanged according to v1 reviews

The ACPI IORT table provides information that allows instantiating
ARM SMMU devices and carrying out id mappings between components on
ARM based systems (devices, IOMMUs, interrupt controllers).

http://infocenter.arm.com/help/topic/com.arm.doc.den0049b/DEN0049B_IO_Remapping_Table.pdf

Building on basic IORT support, available through [2]:

this patchset enables ARM SMMU v3 support on ACPI systems.

Most of the code is aimed at building the required generic ACPI
infrastructure to create and enable IOMMU components and to bring
the IOMMU infrastructure for ACPI on par with DT, which is going to
make future ARM SMMU components easier to integrate.

PATCH (1) adds a FWNODE_IOMMU type to the struct fwnode_handle type.
It is required to attach a fwnode identifier to platform
devices allocated/detected through IORT tables entries;
IOMMU devices have to have an identifier to look them up
eg IOMMU core layer carrying out id translation. This can be
done through a fwnode_handle (ie IOMMU platform devices created
out of IORT tables are not ACPI devices hence they can't be
allocated as such, otherwise they would have a fwnode_handle of
type FWNODE_ACPI). This patch requires discussion and it is key
to the RFC.

PATCH (2) makes use of the ACPI early probing API to add a linker script
section for probing devices via IORT ACPI kernel code.

PATCH (3) provides IORT support for registering IOMMU IORT node through
their fwnode handle.

PATCH (4) implements core code fwnode based platform devices look-up.

PATCH (5) extends iommu_fwspec so that it can be used on ACPI based
system by creating a generic IOMMU fwspec kernel layer.

PATCH (6) implements the of_dma_configure() API in ACPI world -
acpi_dma_configure() - and patches PCI and ACPI core code to
start making use of it.

PATCH (7) provides an IORT function to detect existence of specific type
of IORT components.

PATCH (8) creates the kernel infrastructure required to create ARM SMMU
platform devices for IORT nodes.

PATCH (9) refactors the ARM SMMU v3 driver so that the init functions are
split in a way that groups together code that probes through DT
and code that carries out HW registers FW agnostic probing, in
preparation for adding the ACPI probing path.

PATCH (10) rework ARM SMMU v3 platform driver registration to make it work
on ACPI systems.

PATCH (11) Building on patch (8), it adds ARM SMMU v3 IORT IOMMU
operations to create and probe ARM SMMU v3 components.

PATCH (12) Extend the IORT iort_node_map_rid() to work on a type mask
instead of a single type so that the translation API can
be used on a range of components.

PATCH (13) provides IORT infrastructure to carry out IOMMU configuration
for devices and hook it up to the previously introduced ACPI
DMA configure API.

This patchset is built on top and depends on these three patch series:

[1] R.Murphy "Generic DT bindings for PCI and ARM SMMU v3" v4
https://marc.info/?l=devicetree&m=146739193215518&w=2

[2] T.Nowicki "Introduce ACPI world to ITS irqchip" v7
https://marc.info/?l=linux-arm-kernel&m=146642080022289&w=2

[3] T.Nowicki "Support for ARM64 ACPI based PCI host controller" v8
http://marc.info/?l=linux-acpi&m=146462129816292&w=2

and is provided for early review/testing purposes here:

git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/linux.git acpi/iort-smmu-v3

Tested on FVP models for ARM SMMU v3 probing path.

Lorenzo Pieralisi (13):
drivers: iommu: add FWNODE_IOMMU fwnode type
drivers: acpi: iort: introduce linker section for IORT entries probing
drivers: acpi: iort: add support for IOMMU fwnode registration
drivers: platform: add fwnode base platform devices retrieval
drivers: iommu: make iommu_fwspec OF agnostic
drivers: acpi: implement acpi_dma_configure
drivers: acpi: iort: add node match function
drivers: acpi: iort: add support for ARM SMMU platform devices
creation
drivers: iommu: arm-smmu-v3: split probe functions into DT/generic
portions
drivers: iommu: arm-smmu-v3: enable ACPI driver initialization
drivers: iommu: arm-smmu-v3: add IORT platform device creation
drivers: acpi: iort: replace rid map type with type mask
drivers: acpi: iort: introduce iort_iommu_configure

drivers/acpi/glue.c | 4 +-
drivers/acpi/iort.c | 360 +++++++++++++++++++++++++++++++++++++-
drivers/acpi/scan.c | 29 +++
drivers/base/platform.c | 23 +++
drivers/iommu/Kconfig | 4 +
drivers/iommu/Makefile | 1 +
drivers/iommu/arm-smmu-v3.c | 147 ++++++++++++++--
drivers/iommu/iommu-fwspec.c | 114 ++++++++++++
drivers/iommu/of_iommu.c | 52 ------
drivers/pci/probe.c | 3 +-
include/acpi/acpi_bus.h | 2 +
include/asm-generic/vmlinux.lds.h | 1 +
include/linux/acpi.h | 5 +
include/linux/fwnode.h | 1 +
include/linux/iommu-fwspec.h | 60 +++++++
include/linux/iommu.h | 25 +++
include/linux/iort.h | 19 ++
include/linux/of_iommu.h | 24 +--
include/linux/platform_device.h | 3 +
19 files changed, 782 insertions(+), 95 deletions(-)
create mode 100644 drivers/iommu/iommu-fwspec.c
create mode 100644 include/linux/iommu-fwspec.h
--
2.6.4
Lorenzo Pieralisi
2016-07-20 11:24:04 UTC
Permalink
The ACPI IORT table provide entries for IOMMU (aka SMMU in ARM world)
components that allow creating the kernel data structures required to
probe and initialize the IOMMU devices.

This patch provides support in the IORT kernel code to register IOMMU
components and their respective fwnode.

Signed-off-by: Lorenzo Pieralisi <***@arm.com>
Cc: Hanjun Guo <***@linaro.org>
Cc: Tomasz Nowicki <***@semihalf.com>
Cc: "Rafael J. Wysocki" <***@rjwysocki.net>
---
drivers/acpi/iort.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)

diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c
index 1f440d2..86f6985 100644
--- a/drivers/acpi/iort.c
+++ b/drivers/acpi/iort.c
@@ -20,7 +20,9 @@

#include <linux/iort.h>
#include <linux/kernel.h>
+#include <linux/list.h>
#include <linux/pci.h>
+#include <linux/slab.h>

struct iort_its_msi_chip {
struct list_head list;
@@ -28,6 +30,69 @@ struct iort_its_msi_chip {
u32 translation_id;
};

+struct iort_fwnode {
+ struct list_head list;
+ struct acpi_iort_node *iort_node;
+ struct fwnode_handle *fwnode;
+};
+static LIST_HEAD(iort_fwnode_list);
+static DEFINE_SPINLOCK(iort_fwnode_lock);
+
+/**
+ * iort_set_fwnode() - Create iort_fwnode and use it to register
+ * iommu data in the iort_fwnode_list
+ *
+ * @node: IORT table node associated with the IOMMU
+ * @fwnode: fwnode associated with the IORT node
+ *
+ * Returns: 0 on success
+ * -ENOMEM on failure
+ */
+static inline int iort_set_fwnode(struct acpi_iort_node *iort_node,
+ struct fwnode_handle *fwnode)
+{
+ struct iort_fwnode *np;
+
+ np = kzalloc(sizeof(struct iort_fwnode), GFP_KERNEL);
+
+ if (WARN_ON(!np))
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&np->list);
+ np->iort_node = iort_node;
+ np->fwnode = fwnode;
+
+ spin_lock(&iort_fwnode_lock);
+ list_add_tail(&np->list, &iort_fwnode_list);
+ spin_unlock(&iort_fwnode_lock);
+
+ return 0;
+}
+
+/**
+ * iort_get_fwnode() - Retrieve fwnode associated with an IORT node
+ *
+ * @node: IORT table node to be looked-up
+ *
+ * Returns: fwnode_handle pointer on success NULL on failure
+*/
+static inline struct fwnode_handle *
+iort_get_fwnode(struct acpi_iort_node *node)
+{
+ struct iort_fwnode *curr, *iommu_fwnode = NULL;
+
+ spin_lock(&iort_fwnode_lock);
+ list_for_each_entry(curr, &iort_fwnode_list, list) {
+ if (curr->iort_node == node) {
+ iommu_fwnode = curr;
+ break;
+ }
+ }
+ spin_unlock(&iort_fwnode_lock);
+
+ return iommu_fwnode ? iommu_fwnode->fwnode : NULL;
+}
+
typedef acpi_status (*iort_find_node_callback)
(struct acpi_iort_node *node, void *context);
--
2.6.4
Lorenzo Pieralisi
2016-07-20 11:24:15 UTC
Permalink
On DT based systems, the of_dma_configure() API implements DMA
configuration for a given device. On ACPI systems an API equivalent to
of_dma_configure() is missing which implies that it is currently not
possible to set-up DMA operations for devices through the ACPI generic
kernel layer.

This patch fills the gap by introducing acpi_dma_configure/deconfigure()
calls that for now are just wrappers around arch_setup_dma_ops() and
arch_teardown_dma_ops() and also updates ACPI and PCI core code to use
the newly introduced acpi_dma_configure/acpi_dma_deconfigure functions.

The DMA range size passed to arch_setup_dma_ops() is sized according
to the device coherent_dma_mask (starting at address 0x0), mirroring the
DT probing path behaviour when a dma-ranges property is not provided
for the device being probed; this changes the current arch_setup_dma_ops()
call parameters in the ACPI probing case, but since arch_setup_dma_ops()
is a NOP on all architectures but ARM/ARM64 this patch does not change
the current kernel behaviour on them.

Signed-off-by: Lorenzo Pieralisi <***@arm.com>
Acked-by: Bjorn Helgaas <***@google.com> [pci]
Cc: Bjorn Helgaas <***@google.com>
Cc: Robin Murphy <***@arm.com>
Cc: Tomasz Nowicki <***@semihalf.com>
Cc: Joerg Roedel <***@8bytes.org>
Cc: "Rafael J. Wysocki" <***@rjwysocki.net>
---
drivers/acpi/glue.c | 4 ++--
drivers/acpi/scan.c | 24 ++++++++++++++++++++++++
drivers/pci/probe.c | 3 +--
include/acpi/acpi_bus.h | 2 ++
include/linux/acpi.h | 5 +++++
5 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c
index 5ea5dc2..f8d6564 100644
--- a/drivers/acpi/glue.c
+++ b/drivers/acpi/glue.c
@@ -227,8 +227,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)

attr = acpi_get_dma_attr(acpi_dev);
if (attr != DEV_DMA_NOT_SUPPORTED)
- arch_setup_dma_ops(dev, 0, 0, NULL,
- attr == DEV_DMA_COHERENT);
+ acpi_dma_configure(dev, attr);

acpi_physnode_link_name(physical_node_name, node_id);
retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
@@ -251,6 +250,7 @@ int acpi_bind_one(struct device *dev, struct acpi_device *acpi_dev)
return 0;

err:
+ acpi_dma_deconfigure(dev);
ACPI_COMPANION_SET(dev, NULL);
put_device(dev);
put_device(&acpi_dev->dev);
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 5f28cf7..b4b9064 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1358,6 +1358,30 @@ enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
return DEV_DMA_NON_COHERENT;
}

+/**
+ * acpi_dma_configure - Set-up DMA configuration for the device.
+ * @dev: The pointer to the device
+ * @attr: device dma attributes
+ */
+void acpi_dma_configure(struct device *dev, enum dev_dma_attr attr)
+{
+ /*
+ * Assume dma valid range starts at 0 and covers the whole
+ * coherent_dma_mask.
+ */
+ arch_setup_dma_ops(dev, 0, dev->coherent_dma_mask + 1, NULL,
+ attr == DEV_DMA_COHERENT);
+}
+
+/**
+ * acpi_dma_deconfigure - Tear-down DMA configuration for the device.
+ * @dev: The pointer to the device
+ */
+void acpi_dma_deconfigure(struct device *dev)
+{
+ arch_teardown_dma_ops(dev);
+}
+
static void acpi_init_coherency(struct acpi_device *adev)
{
unsigned long long cca = 0;
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 380d46d..7ef3933 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -1725,8 +1725,7 @@ static void pci_dma_configure(struct pci_dev *dev)
if (attr == DEV_DMA_NOT_SUPPORTED)
dev_warn(&dev->dev, "DMA not supported.\n");
else
- arch_setup_dma_ops(&dev->dev, 0, 0, NULL,
- attr == DEV_DMA_COHERENT);
+ acpi_dma_configure(&dev->dev, attr);
}

pci_put_host_bridge_device(bridge);
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 788c6c35..8b5039a 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -566,6 +566,8 @@ struct acpi_pci_root {

bool acpi_dma_supported(struct acpi_device *adev);
enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev);
+void acpi_dma_configure(struct device *dev, enum dev_dma_attr attr);
+void acpi_dma_deconfigure(struct device *dev);

struct acpi_device *acpi_find_child_device(struct acpi_device *parent,
u64 address, bool check_children);
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 288fac5..135a452 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -676,6 +676,11 @@ static inline enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
return DEV_DMA_NOT_SUPPORTED;
}

+static inline void acpi_dma_configure(struct device *dev,
+ enum dev_dma_attr attr) { }
+
+static inline void acpi_dma_deconfigure(struct device *dev) { }
+
#define ACPI_PTR(_ptr) (NULL)

#endif /* !CONFIG_ACPI */
--
2.6.4
Lorenzo Pieralisi
2016-07-20 11:24:26 UTC
Permalink
Device drivers (eg ARM SMMU) need to know if a specific component
is part of the IORT table, so that kernel data structures are not
initialized at initcalls time if the respective component is not
part of the IORT table.

To this end, this patch adds a trivial function that allows detecting
if a given IORT node type is present or not in the ACPI table, providing
an ACPI IORT equivalent for of_find_matching_node().

Signed-off-by: Lorenzo Pieralisi <***@arm.com>
Cc: Hanjun Guo <***@linaro.org>
Cc: Tomasz Nowicki <***@semihalf.com>
Cc: "Rafael J. Wysocki" <***@rjwysocki.net>
---
drivers/acpi/iort.c | 15 +++++++++++++++
include/linux/iort.h | 2 ++
2 files changed, 17 insertions(+)

diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c
index 86f6985..71516e8 100644
--- a/drivers/acpi/iort.c
+++ b/drivers/acpi/iort.c
@@ -205,6 +205,21 @@ iort_scan_node(enum acpi_iort_node_type type,
}

static acpi_status
+iort_match_callback(struct acpi_iort_node *node, void *context)
+{
+ return AE_OK;
+}
+
+bool iort_node_match(u8 type)
+{
+ struct acpi_iort_node *node;
+
+ node = iort_scan_node(type, iort_match_callback, NULL);
+
+ return node != NULL;
+}
+
+static acpi_status
iort_match_node_callback(struct acpi_iort_node *node, void *context)
{
struct device *dev = context;
diff --git a/include/linux/iort.h b/include/linux/iort.h
index 9bb30c5..ac2706a 100644
--- a/include/linux/iort.h
+++ b/include/linux/iort.h
@@ -27,10 +27,12 @@ int iort_register_domain_token(int trans_id, struct fwnode_handle *fw_node);
void iort_deregister_domain_token(int trans_id);
struct fwnode_handle *iort_find_domain_token(int trans_id);
#ifdef CONFIG_IORT_TABLE
+bool iort_node_match(u8 type);
void iort_table_detect(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);
#else
+static inline bool iort_node_match(u8 type) { return false; }
static inline void iort_table_detect(void) { }
static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id)
{ return req_id; }
--
2.6.4
Lorenzo Pieralisi
2016-07-20 11:24:36 UTC
Permalink
Current ARM SMMUv3 probe functions intermingle HW and DT probing in the
initialization functions to detect and programme the ARM SMMU v3 driver
features. In order to allow probing the ARM SMMUv3 with other firmwares
than DT, this patch splits the ARM SMMUv3 init functions into DT and HW
specific portions so that other FW interfaces (ie ACPI) can reuse the HW
probing functions and skip the DT portion accordingly.

This patch implements no functional change, only code reshuffling.

Signed-off-by: Lorenzo Pieralisi <***@arm.com>
Acked-by: Will Deacon <***@arm.com>
Cc: Will Deacon <***@arm.com>
Cc: Hanjun Guo <***@linaro.org>
Cc: Robin Murphy <***@arm.com>
Cc: Joerg Roedel <***@8bytes.org>
---
drivers/iommu/arm-smmu-v3.c | 63 +++++++++++++++++++++++++++++++++++++--------
1 file changed, 52 insertions(+), 11 deletions(-)

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 052a26c..15e74da 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -20,6 +20,7 @@
* This driver is powered by bad coffee and bombay mix.
*/

+#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/dma-iommu.h>
#include <linux/err.h>
@@ -27,6 +28,7 @@
#include <linux/iommu.h>
#include <linux/iommu-fwspec.h>
#include <linux/iopoll.h>
+#include <linux/iort.h>
#include <linux/module.h>
#include <linux/msi.h>
#include <linux/of.h>
@@ -2377,10 +2379,10 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
return 0;
}

-static int arm_smmu_device_probe(struct arm_smmu_device *smmu)
+static int arm_smmu_device_hw_probe(struct arm_smmu_device *smmu)
{
u32 reg;
- bool coherent;
+ bool coherent = smmu->features & ARM_SMMU_FEAT_COHERENCY;

/* IDR0 */
reg = readl_relaxed(smmu->base + ARM_SMMU_IDR0);
@@ -2432,13 +2434,9 @@ static int arm_smmu_device_probe(struct arm_smmu_device *smmu)
smmu->features |= ARM_SMMU_FEAT_HYP;

/*
- * The dma-coherent property is used in preference to the ID
+ * The coherency feature as set by FW is used in preference to the ID
* register, but warn on mismatch.
*/
- coherent = of_dma_is_coherent(smmu->dev->of_node);
- if (coherent)
- smmu->features |= ARM_SMMU_FEAT_COHERENCY;
-
if (!!(reg & IDR0_COHACC) != coherent)
dev_warn(smmu->dev, "IDR0.COHACC overridden by dma-coherent property (%s)\n",
coherent ? "true" : "false");
@@ -2559,7 +2557,44 @@ static int arm_smmu_device_probe(struct arm_smmu_device *smmu)
return 0;
}

-static int arm_smmu_device_dt_probe(struct platform_device *pdev)
+#ifdef CONFIG_ACPI
+static int arm_smmu_device_acpi_probe(struct platform_device *pdev,
+ struct arm_smmu_device *smmu)
+{
+ struct acpi_iort_smmu_v3 *iort_smmu;
+ struct device *dev = smmu->dev;
+ struct acpi_iort_node *node;
+
+ node = *(struct acpi_iort_node **)dev_get_platdata(dev);
+
+ /* Retrieve SMMUv3 specific data */
+ iort_smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+ if (iort_smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE)
+ smmu->features |= ARM_SMMU_FEAT_COHERENCY;
+
+ return 0;
+}
+#else
+static int arm_smmu_device_acpi_probe(struct platform_device *pdev,
+ struct arm_smmu_device *smmu)
+{
+ return -ENODEV;
+}
+#endif
+
+static int arm_smmu_device_dt_probe(struct platform_device *pdev,
+ struct arm_smmu_device *smmu)
+{
+ parse_driver_options(smmu);
+
+ if (of_dma_is_coherent(smmu->dev->of_node))
+ smmu->features |= ARM_SMMU_FEAT_COHERENCY;
+
+ return 0;
+}
+
+static int arm_smmu_device_probe(struct platform_device *pdev)
{
int irq, ret;
struct resource *res;
@@ -2601,10 +2636,16 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
if (irq > 0)
smmu->gerr_irq = irq;

- parse_driver_options(smmu);
+ if (acpi_disabled)
+ ret = arm_smmu_device_dt_probe(pdev, smmu);
+ else
+ ret = arm_smmu_device_acpi_probe(pdev, smmu);
+
+ if (ret)
+ return ret;

/* Probe the h/w */
- ret = arm_smmu_device_probe(smmu);
+ ret = arm_smmu_device_hw_probe(smmu);
if (ret)
return ret;

@@ -2639,7 +2680,7 @@ static struct platform_driver arm_smmu_driver = {
.name = "arm-smmu-v3",
.of_match_table = of_match_ptr(arm_smmu_of_match),
},
- .probe = arm_smmu_device_dt_probe,
+ .probe = arm_smmu_device_probe,
.remove = arm_smmu_device_remove,
};
--
2.6.4
Lorenzo Pieralisi
2016-07-20 11:24:46 UTC
Permalink
DT based systems have a generic kernel API to configure IOMMUs
for devices (ie of_iommu_configure()).

On ARM based ACPI systems, the of_iommu_configure() equivalent can
be implemented atop ACPI IORT kernel API, with the corresponding
functions to map device identifiers to IOMMUs and retrieve the
corresponding IOMMU operations necessary for DMA operations set-up.

By relying on the iommu_fwspec generic kernel infrastructure,
implement the IORT based IOMMU configuration for ARM ACPI systems
and hook it up in the ACPI kernel layer that implements DMA
configuration for a device.

Signed-off-by: Lorenzo Pieralisi <***@arm.com>
Cc: Hanjun Guo <***@linaro.org>
Cc: Tomasz Nowicki <***@semihalf.com>
Cc: "Rafael J. Wysocki" <***@rjwysocki.net>
---
drivers/acpi/iort.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++
drivers/acpi/scan.c | 7 +++++-
include/linux/iort.h | 4 ++++
3 files changed, 74 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c
index c116b68..a12a4ff 100644
--- a/drivers/acpi/iort.c
+++ b/drivers/acpi/iort.c
@@ -18,6 +18,7 @@

#define pr_fmt(fmt) "ACPI: IORT: " fmt

+#include <linux/iommu-fwspec.h>
#include <linux/iort.h>
#include <linux/kernel.h>
#include <linux/list.h>
@@ -27,6 +28,8 @@

#define IORT_TYPE_MASK(type) (1 << (type))
#define IORT_MSI_TYPE (1 << ACPI_IORT_NODE_ITS_GROUP)
+#define IORT_IOMMU_TYPE ((1 << ACPI_IORT_NODE_SMMU) | \
+ (1 << ACPI_IORT_NODE_SMMU_V3))

struct iort_its_msi_chip {
struct list_head list;
@@ -458,6 +461,67 @@ iort_get_device_domain(struct device *dev, u32 req_id)
return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI);
}

+static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
+{
+ u32 *rid = data;
+
+ *rid = alias;
+ return 0;
+}
+
+static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
+ struct fwnode_handle *fwnode)
+{
+ int ret = iommu_fwspec_init(dev, fwnode);
+
+ if (!ret)
+ ret = iommu_fwspec_add_ids(dev, &streamid, 1);
+
+ return 0;
+}
+
+/**
+ * iort_iommu_configure - Set-up IOMMU configuration for a device.
+ *
+ * @dev: device to configure
+ *
+ * Returns: iommu_ops pointer on configuration success
+ * NULL on configuration failure
+ */
+const struct iommu_ops *iort_iommu_configure(struct device *dev)
+{
+ struct acpi_iort_node *node, *parent;
+ struct fwnode_handle *iort_fwnode;
+ u32 rid = 0, devid = 0;
+
+ if (dev_is_pci(dev)) {
+ struct pci_bus *bus = to_pci_dev(dev)->bus;
+
+ pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
+ &rid);
+
+ node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+ iort_match_node_callback, &bus->dev);
+ } else {
+ node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+ iort_match_node_callback, dev);
+ }
+
+ if (!node)
+ return NULL;
+
+ parent = iort_node_map_rid(node, rid, &devid, IORT_IOMMU_TYPE);
+ if (parent) {
+ iort_fwnode = iort_get_fwnode(parent);
+ if (iort_fwnode) {
+ arm_smmu_iort_xlate(dev, devid, iort_fwnode);
+ return fwspec_iommu_get_ops(iort_fwnode);
+ }
+ }
+
+ return NULL;
+}
+
static void acpi_smmu_v3_register_irq(int hwirq, const char *name,
struct resource *res)
{
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index b4b9064..de28825 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -7,6 +7,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/acpi.h>
+#include <linux/iort.h>
#include <linux/signal.h>
#include <linux/kthread.h>
#include <linux/dmi.h>
@@ -1365,11 +1366,15 @@ enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
*/
void acpi_dma_configure(struct device *dev, enum dev_dma_attr attr)
{
+ const struct iommu_ops *iommu;
+
+ iommu = iort_iommu_configure(dev);
+
/*
* Assume dma valid range starts at 0 and covers the whole
* coherent_dma_mask.
*/
- arch_setup_dma_ops(dev, 0, dev->coherent_dma_mask + 1, NULL,
+ arch_setup_dma_ops(dev, 0, dev->coherent_dma_mask + 1, iommu,
attr == DEV_DMA_COHERENT);
}

diff --git a/include/linux/iort.h b/include/linux/iort.h
index 18e6836..bbe08ef 100644
--- a/include/linux/iort.h
+++ b/include/linux/iort.h
@@ -34,6 +34,8 @@ struct irq_domain *iort_get_device_domain(struct device *dev, u32 req_id);
/* IOMMU interface */
int iort_add_smmu_platform_device(struct fwnode_handle *fwnode,
struct acpi_iort_node *node);
+
+const struct iommu_ops *iort_iommu_configure(struct device *dev);
#else
static inline bool iort_node_match(u8 type) { return false; }
static inline void iort_table_detect(void) { }
@@ -48,6 +50,8 @@ iort_add_smmu_platform_device(struct fwnode_handle *fwnode,
{
return -ENODEV;
}
+static inline const struct iommu_ops *
+iort_iommu_configure(struct device *dev) { return NULL; }
#endif

#define IORT_ACPI_DECLARE(name, table_id, fn) \
--
2.6.4
n***@codeaurora.org
2016-08-03 14:22:41 UTC
Permalink
Post by Lorenzo Pieralisi
DT based systems have a generic kernel API to configure IOMMUs
for devices (ie of_iommu_configure()).
On ARM based ACPI systems, the of_iommu_configure() equivalent can
be implemented atop ACPI IORT kernel API, with the corresponding
functions to map device identifiers to IOMMUs and retrieve the
corresponding IOMMU operations necessary for DMA operations set-up.
By relying on the iommu_fwspec generic kernel infrastructure,
implement the IORT based IOMMU configuration for ARM ACPI systems
and hook it up in the ACPI kernel layer that implements DMA
configuration for a device.
---
drivers/acpi/iort.c | 64
++++++++++++++++++++++++++++++++++++++++++++++++++++
drivers/acpi/scan.c | 7 +++++-
include/linux/iort.h | 4 ++++
3 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c
index c116b68..a12a4ff 100644
--- a/drivers/acpi/iort.c
+++ b/drivers/acpi/iort.c
@@ -18,6 +18,7 @@
#define pr_fmt(fmt) "ACPI: IORT: " fmt
+#include <linux/iommu-fwspec.h>
#include <linux/iort.h>
#include <linux/kernel.h>
#include <linux/list.h>
@@ -27,6 +28,8 @@
#define IORT_TYPE_MASK(type) (1 << (type))
#define IORT_MSI_TYPE (1 << ACPI_IORT_NODE_ITS_GROUP)
+#define IORT_IOMMU_TYPE ((1 << ACPI_IORT_NODE_SMMU) | \
+ (1 << ACPI_IORT_NODE_SMMU_V3))
struct iort_its_msi_chip {
struct list_head list;
@@ -458,6 +461,67 @@ iort_get_device_domain(struct device *dev, u32 req_id)
return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI);
}
+static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
+{
+ u32 *rid = data;
+
+ *rid = alias;
+ return 0;
+}
+
+static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
+ struct fwnode_handle *fwnode)
+{
+ int ret = iommu_fwspec_init(dev, fwnode);
+
+ if (!ret)
+ ret = iommu_fwspec_add_ids(dev, &streamid, 1);
+
+ return 0;
Are you intentionally returning 0 instead of ret? How about doing this
instead?

return ret ? ret : iommu_fwspec_add_ids(dev, &streamid, 1);
Post by Lorenzo Pieralisi
+}
+
+/**
+ * iort_iommu_configure - Set-up IOMMU configuration for a device.
+ *
+ *
+ * Returns: iommu_ops pointer on configuration success
+ * NULL on configuration failure
+ */
+const struct iommu_ops *iort_iommu_configure(struct device *dev)
+{
+ struct acpi_iort_node *node, *parent;
+ struct fwnode_handle *iort_fwnode;
+ u32 rid = 0, devid = 0;
Since this routine maps the RID space of a device to the StreamID space
of its
parent smmu, would you consider renaming the devid variable to some form
of sid
or streamid?
Post by Lorenzo Pieralisi
+
+ if (dev_is_pci(dev)) {
+ struct pci_bus *bus = to_pci_dev(dev)->bus;
+
+ pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
+ &rid);
+
+ node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+ iort_match_node_callback, &bus->dev);
+ } else {
+ node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+ iort_match_node_callback, dev);
+ }
+
+ if (!node)
+ return NULL;
+
+ parent = iort_node_map_rid(node, rid, &devid, IORT_IOMMU_TYPE);
+ if (parent) {
+ iort_fwnode = iort_get_fwnode(parent);
+ if (iort_fwnode) {
+ arm_smmu_iort_xlate(dev, devid, iort_fwnode);
What about named components with multiple stream ids? Since establishing
the
relationship between a named component and its parent smmu is already
dependent
on there being an appropriate mapping of rid 0, it stands to reason that
all of
the stream ids for a named component could be enumerated by mapping
increasing
rid values until the output parent no longer matches that returned for
rid 0.
Post by Lorenzo Pieralisi
+ return fwspec_iommu_get_ops(iort_fwnode);
+ }
+ }
+
+ return NULL;
+}
It should be noted that while trying out the approach described above, I
noticed
that each of the smmu attached named components described in my iort
were ending
up with an extra stream id. The culprit appears to be that the range
checking in
iort_id_map() is overly permissive on the upper bounds. For example,
mappings
with input_base=N and id_count=1 were matching both N and N+1. The
following
change fixed the issue.

@@ -296,7 +296,7 @@ iort_id_map(struct acpi_iort_id_mapping *map, u8
type, u32 rid_in, u32 *rid_out)
}

if (rid_in < map->input_base ||
- (rid_in > map->input_base + map->id_count))
+ (rid_in >= map->input_base + map->id_count))
return -ENXIO;

*rid_out = map->output_base + (rid_in - map->input_base);
Post by Lorenzo Pieralisi
+
static void acpi_smmu_v3_register_irq(int hwirq, const char *name,
struct resource *res)
{
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index b4b9064..de28825 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -7,6 +7,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/acpi.h>
+#include <linux/iort.h>
#include <linux/signal.h>
#include <linux/kthread.h>
#include <linux/dmi.h>
@@ -1365,11 +1366,15 @@ enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
*/
void acpi_dma_configure(struct device *dev, enum dev_dma_attr attr)
{
+ const struct iommu_ops *iommu;
+
+ iommu = iort_iommu_configure(dev);
+
/*
* Assume dma valid range starts at 0 and covers the whole
* coherent_dma_mask.
*/
- arch_setup_dma_ops(dev, 0, dev->coherent_dma_mask + 1, NULL,
+ arch_setup_dma_ops(dev, 0, dev->coherent_dma_mask + 1, iommu,
attr == DEV_DMA_COHERENT);
If dev has a matching named component iort entry with a non-zero value
for
memory_address_limit, why not use that as the size input to
arch_setup_dma_ops?
Post by Lorenzo Pieralisi
}
diff --git a/include/linux/iort.h b/include/linux/iort.h
index 18e6836..bbe08ef 100644
--- a/include/linux/iort.h
+++ b/include/linux/iort.h
@@ -34,6 +34,8 @@ struct irq_domain *iort_get_device_domain(struct
device *dev, u32 req_id);
/* IOMMU interface */
int iort_add_smmu_platform_device(struct fwnode_handle *fwnode,
struct acpi_iort_node *node);
+
+const struct iommu_ops *iort_iommu_configure(struct device *dev);
#else
static inline bool iort_node_match(u8 type) { return false; }
static inline void iort_table_detect(void) { }
@@ -48,6 +50,8 @@ iort_add_smmu_platform_device(struct fwnode_handle *fwnode,
{
return -ENODEV;
}
+static inline const struct iommu_ops *
+iort_iommu_configure(struct device *dev) { return NULL; }
#endif
#define IORT_ACPI_DECLARE(name, table_id, fn) \
--
Qualcomm Datacenter Technologies, Inc. on behalf of Qualcomm
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a
Linux Foundation Collaborative Project.
Lorenzo Pieralisi
2016-08-08 16:16:15 UTC
Permalink
Hi Nate,

thanks for having a look.
Post by n***@codeaurora.org
Post by Lorenzo Pieralisi
DT based systems have a generic kernel API to configure IOMMUs
for devices (ie of_iommu_configure()).
On ARM based ACPI systems, the of_iommu_configure() equivalent can
be implemented atop ACPI IORT kernel API, with the corresponding
functions to map device identifiers to IOMMUs and retrieve the
corresponding IOMMU operations necessary for DMA operations set-up.
By relying on the iommu_fwspec generic kernel infrastructure,
implement the IORT based IOMMU configuration for ARM ACPI systems
and hook it up in the ACPI kernel layer that implements DMA
configuration for a device.
---
drivers/acpi/iort.c | 64
++++++++++++++++++++++++++++++++++++++++++++++++++++
drivers/acpi/scan.c | 7 +++++-
include/linux/iort.h | 4 ++++
3 files changed, 74 insertions(+), 1 deletion(-)
diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c
index c116b68..a12a4ff 100644
--- a/drivers/acpi/iort.c
+++ b/drivers/acpi/iort.c
@@ -18,6 +18,7 @@
#define pr_fmt(fmt) "ACPI: IORT: " fmt
+#include <linux/iommu-fwspec.h>
#include <linux/iort.h>
#include <linux/kernel.h>
#include <linux/list.h>
@@ -27,6 +28,8 @@
#define IORT_TYPE_MASK(type) (1 << (type))
#define IORT_MSI_TYPE (1 << ACPI_IORT_NODE_ITS_GROUP)
+#define IORT_IOMMU_TYPE ((1 << ACPI_IORT_NODE_SMMU) | \
+ (1 << ACPI_IORT_NODE_SMMU_V3))
struct iort_its_msi_chip {
struct list_head list;
@@ -458,6 +461,67 @@ iort_get_device_domain(struct device *dev, u32 req_id)
return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI);
}
+static int __get_pci_rid(struct pci_dev *pdev, u16 alias, void *data)
+{
+ u32 *rid = data;
+
+ *rid = alias;
+ return 0;
+}
+
+static int arm_smmu_iort_xlate(struct device *dev, u32 streamid,
+ struct fwnode_handle *fwnode)
+{
+ int ret = iommu_fwspec_init(dev, fwnode);
+
+ if (!ret)
+ ret = iommu_fwspec_add_ids(dev, &streamid, 1);
+
+ return 0;
Are you intentionally returning 0 instead of ret? How about doing
this instead?
return ret ? ret : iommu_fwspec_add_ids(dev, &streamid, 1);
No, that's a bug, I will return ret as the of_xlate() function in
the ARM SMMU v3 driver does, thanks.
Post by n***@codeaurora.org
Post by Lorenzo Pieralisi
+}
+
+/**
+ * iort_iommu_configure - Set-up IOMMU configuration for a device.
+ *
+ *
+ * Returns: iommu_ops pointer on configuration success
+ * NULL on configuration failure
+ */
+const struct iommu_ops *iort_iommu_configure(struct device *dev)
+{
+ struct acpi_iort_node *node, *parent;
+ struct fwnode_handle *iort_fwnode;
+ u32 rid = 0, devid = 0;
Since this routine maps the RID space of a device to the StreamID
space of its parent smmu, would you consider renaming the devid
variable to some form of sid or streamid?
Yes, I will do.
Post by n***@codeaurora.org
Post by Lorenzo Pieralisi
+ if (dev_is_pci(dev)) {
+ struct pci_bus *bus = to_pci_dev(dev)->bus;
+
+ pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
+ &rid);
+
+ node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+ iort_match_node_callback, &bus->dev);
+ } else {
+ node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+ iort_match_node_callback, dev);
+ }
+
+ if (!node)
+ return NULL;
+
+ parent = iort_node_map_rid(node, rid, &devid, IORT_IOMMU_TYPE);
+ if (parent) {
+ iort_fwnode = iort_get_fwnode(parent);
+ if (iort_fwnode) {
+ arm_smmu_iort_xlate(dev, devid, iort_fwnode);
What about named components with multiple stream ids? Since
establishing the relationship between a named component and its parent
smmu is already dependent on there being an appropriate mapping of rid
0, it stands to reason that all of the stream ids for a named
component could be enumerated by mapping increasing rid values until
the output parent no longer matches that returned for rid 0.
Yes, that's a good point, what I am doing currently for named
components is not correct I will update the handling in the next
version. In particular, I think that we should support only
single mappings for named components for the time being and add
code to carry out the mapping as it is done in DT through the
iommus property handling in of_iommu_configure().
Post by n***@codeaurora.org
Post by Lorenzo Pieralisi
+ return fwspec_iommu_get_ops(iort_fwnode);
+ }
+ }
+
+ return NULL;
+}
It should be noted that while trying out the approach described above,
I noticed that each of the smmu attached named components described in
my iort were ending up with an extra stream id. The culprit appears to
be that the range checking in iort_id_map() is overly permissive on
the upper bounds. For example, mappings with input_base=N and
id_count=1 were matching both N and N+1. The following change fixed
the issue.
I will ask Tomasz to fix it up.
Post by n***@codeaurora.org
@@ -296,7 +296,7 @@ iort_id_map(struct acpi_iort_id_mapping *map, u8
type, u32 rid_in, u32 *rid_out)
}
if (rid_in < map->input_base ||
- (rid_in > map->input_base + map->id_count))
+ (rid_in >= map->input_base + map->id_count))
return -ENXIO;
*rid_out = map->output_base + (rid_in - map->input_base);
Post by Lorenzo Pieralisi
+
static void acpi_smmu_v3_register_irq(int hwirq, const char *name,
struct resource *res)
{
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index b4b9064..de28825 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -7,6 +7,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/acpi.h>
+#include <linux/iort.h>
#include <linux/signal.h>
#include <linux/kthread.h>
#include <linux/dmi.h>
@@ -1365,11 +1366,15 @@ enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
*/
void acpi_dma_configure(struct device *dev, enum dev_dma_attr attr)
{
+ const struct iommu_ops *iommu;
+
+ iommu = iort_iommu_configure(dev);
+
/*
* Assume dma valid range starts at 0 and covers the whole
* coherent_dma_mask.
*/
- arch_setup_dma_ops(dev, 0, dev->coherent_dma_mask + 1, NULL,
+ arch_setup_dma_ops(dev, 0, dev->coherent_dma_mask + 1, iommu,
attr == DEV_DMA_COHERENT);
If dev has a matching named component iort entry with a non-zero value
for memory_address_limit, why not use that as the size input to
arch_setup_dma_ops?
I was hoping to address this through something more generic (ie
_DMA object - I am not sure it was ever used in x86 world though)
in ACPI rather than relying on IORT named component specific
firmware data (similar to "dma-ranges" handling in DT), I will
certainly keep this in mind though.

Thanks !
Lorenzo
Lorenzo Pieralisi
2016-08-11 08:45:06 UTC
Permalink
On Wed, Aug 03, 2016 at 10:19:43AM -0400, ***@codeaurora.org wrote:

[...]
Post by n***@codeaurora.org
Post by Lorenzo Pieralisi
+const struct iommu_ops *iort_iommu_configure(struct device *dev)
+{
+ struct acpi_iort_node *node, *parent;
+ struct fwnode_handle *iort_fwnode;
+ u32 rid = 0, devid = 0;
Since this routine maps the RID space of a device to the StreamID
space of its
parent smmu, would you consider renaming the devid variable to some
form of sid
or streamid?
Post by Lorenzo Pieralisi
+
+ if (dev_is_pci(dev)) {
+ struct pci_bus *bus = to_pci_dev(dev)->bus;
+
+ pci_for_each_dma_alias(to_pci_dev(dev), __get_pci_rid,
+ &rid);
+
+ node = iort_scan_node(ACPI_IORT_NODE_PCI_ROOT_COMPLEX,
+ iort_match_node_callback, &bus->dev);
+ } else {
+ node = iort_scan_node(ACPI_IORT_NODE_NAMED_COMPONENT,
+ iort_match_node_callback, dev);
+ }
+
+ if (!node)
+ return NULL;
+
+ parent = iort_node_map_rid(node, rid, &devid, IORT_IOMMU_TYPE);
+ if (parent) {
+ iort_fwnode = iort_get_fwnode(parent);
+ if (iort_fwnode) {
+ arm_smmu_iort_xlate(dev, devid, iort_fwnode);
What about named components with multiple stream ids? Since
establishing the relationship between a named component and its parent
smmu is already dependent on there being an appropriate mapping of rid
0, it stands to reason that all of the stream ids for a named
component could be enumerated by mapping increasing rid values until
the output parent no longer matches that returned for rid 0.
I have reworked the code since for named component it makes no
sense to carry out a mapping that depends on an input id given
that we do not have one. Instead what we will do is the same
thing DT does (ie "iommus" property), namely walk the array of
single mappings for a given named component (that do not depend
on the input rid, there is not any) and add them to the translation
as we find them.

Ergo, mappings that are not single mappings are pretty much useless
for named components (for the time being), and I won't allow them.

Thoughts ?

Lorenzo
Post by n***@codeaurora.org
Post by Lorenzo Pieralisi
+ return fwspec_iommu_get_ops(iort_fwnode);
+ }
+ }
+
+ return NULL;
+}
It should be noted that while trying out the approach described
above, I noticed
that each of the smmu attached named components described in my iort
were ending
up with an extra stream id. The culprit appears to be that the range
checking in
iort_id_map() is overly permissive on the upper bounds. For example,
mappings
with input_base=N and id_count=1 were matching both N and N+1. The
following
change fixed the issue.
@@ -296,7 +296,7 @@ iort_id_map(struct acpi_iort_id_mapping *map, u8
type, u32 rid_in, u32 *rid_out)
}
if (rid_in < map->input_base ||
- (rid_in > map->input_base + map->id_count))
+ (rid_in >= map->input_base + map->id_count))
return -ENXIO;
*rid_out = map->output_base + (rid_in - map->input_base);
Post by Lorenzo Pieralisi
+
static void acpi_smmu_v3_register_irq(int hwirq, const char *name,
struct resource *res)
{
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index b4b9064..de28825 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -7,6 +7,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/acpi.h>
+#include <linux/iort.h>
#include <linux/signal.h>
#include <linux/kthread.h>
#include <linux/dmi.h>
@@ -1365,11 +1366,15 @@ enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
*/
void acpi_dma_configure(struct device *dev, enum dev_dma_attr attr)
{
+ const struct iommu_ops *iommu;
+
+ iommu = iort_iommu_configure(dev);
+
/*
* Assume dma valid range starts at 0 and covers the whole
* coherent_dma_mask.
*/
- arch_setup_dma_ops(dev, 0, dev->coherent_dma_mask + 1, NULL,
+ arch_setup_dma_ops(dev, 0, dev->coherent_dma_mask + 1, iommu,
attr == DEV_DMA_COHERENT);
If dev has a matching named component iort entry with a non-zero
value for
memory_address_limit, why not use that as the size input to
arch_setup_dma_ops?
Post by Lorenzo Pieralisi
}
diff --git a/include/linux/iort.h b/include/linux/iort.h
index 18e6836..bbe08ef 100644
--- a/include/linux/iort.h
+++ b/include/linux/iort.h
@@ -34,6 +34,8 @@ struct irq_domain *iort_get_device_domain(struct
device *dev, u32 req_id);
/* IOMMU interface */
int iort_add_smmu_platform_device(struct fwnode_handle *fwnode,
struct acpi_iort_node *node);
+
+const struct iommu_ops *iort_iommu_configure(struct device *dev);
#else
static inline bool iort_node_match(u8 type) { return false; }
static inline void iort_table_detect(void) { }
@@ -48,6 +50,8 @@ iort_add_smmu_platform_device(struct
fwnode_handle *fwnode,
{
return -ENODEV;
}
+static inline const struct iommu_ops *
+iort_iommu_configure(struct device *dev) { return NULL; }
#endif
#define IORT_ACPI_DECLARE(name, table_id, fn) \
--
Qualcomm Datacenter Technologies, Inc. on behalf of Qualcomm
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a
Linux Foundation Collaborative Project.
Lorenzo Pieralisi
2016-07-20 11:25:15 UTC
Permalink
IORT tables provide data that allow the kernel to carry out
device ID mappings between endpoints and system components
(eg interrupt controllers, IOMMUs). When the mapping for a
given device ID is carried out, the translation mechanism
is done on a per-subsystem basis rather than a component
subtype (ie the IOMMU kernel layer will look for mappings
from a device to all IORT node types corresponding to IOMMU
components), therefore the corresponding mapping API should
work on a range (ie mask) of IORT node types corresponding
to a common set of components (eg IOMMUs) rather than a
specific node type.

Upgrade the IORT iort_node_map_rid() API to work with a
type mask instead of a single node type so that it can
be used for mappings that span multiple components types
(ie IOMMUs).

Signed-off-by: Lorenzo Pieralisi <***@arm.com>
Cc: Hanjun Guo <***@linaro.org>
Cc: Tomasz Nowicki <***@semihalf.com>
Cc: "Rafael J. Wysocki" <***@rjwysocki.net>
---
drivers/acpi/iort.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c
index c91e45d..c116b68 100644
--- a/drivers/acpi/iort.c
+++ b/drivers/acpi/iort.c
@@ -25,6 +25,9 @@
#include <linux/platform_device.h>
#include <linux/slab.h>

+#define IORT_TYPE_MASK(type) (1 << (type))
+#define IORT_MSI_TYPE (1 << ACPI_IORT_NODE_ITS_GROUP)
+
struct iort_its_msi_chip {
struct list_head list;
struct fwnode_handle *fw_node;
@@ -299,7 +302,7 @@ iort_id_map(struct acpi_iort_id_mapping *map, u8 type, u32 rid_in, u32 *rid_out)

static struct acpi_iort_node *
iort_node_map_rid(struct acpi_iort_node *node, u32 rid_in,
- u32 *rid_out, u8 type)
+ u32 *rid_out, u8 type_mask)
{
u32 rid = rid_in;

@@ -308,7 +311,7 @@ iort_node_map_rid(struct acpi_iort_node *node, u32 rid_in,
struct acpi_iort_id_mapping *map;
int i;

- if (node->type == type) {
+ if (IORT_TYPE_MASK(node->type) & type_mask) {
if (rid_out)
*rid_out = rid;
return node;
@@ -386,7 +389,7 @@ u32 iort_msi_map_rid(struct device *dev, u32 req_id)
return req_id;
}

- iort_node_map_rid(node, req_id, &dev_id, ACPI_IORT_NODE_ITS_GROUP);
+ iort_node_map_rid(node, req_id, &dev_id, IORT_MSI_TYPE);
return dev_id;
}

@@ -411,7 +414,7 @@ iort_dev_find_its_id(struct device *dev, u32 req_id, unsigned int idx,
return -ENXIO;
}

- node = iort_node_map_rid(node, req_id, NULL, ACPI_IORT_NODE_ITS_GROUP);
+ node = iort_node_map_rid(node, req_id, NULL, IORT_MSI_TYPE);
if (!node) {
dev_err(dev, "can't find related ITS node\n");
return -ENXIO;
--
2.6.4
Lorenzo Pieralisi
2016-07-20 11:26:09 UTC
Permalink
In ACPI bases systems, in order to be able to create platform
devices and initialize them for ARM SMMU v3 components, the IORT
kernel implementation requires a set of static functions to be
used by the IORT kernel layer to configure platform devices for
ARM SMMU v3 components.

Add static configuration functions to the IORT kernel layer for
the ARM SMMU v3 components, so that the ARM SMMU v3 driver can
initialize its respective platform device by relying on the IORT
kernel infrastructure and by adding a corresponding ACPI device
early probe section entry.

Signed-off-by: Lorenzo Pieralisi <***@arm.com>
Cc: Will Deacon <***@arm.com>
Cc: Robin Murphy <***@arm.com>
Cc: Joerg Roedel <***@8bytes.org>
---
drivers/acpi/iort.c | 98 ++++++++++++++++++++++++++++++++++++++++++++-
drivers/iommu/arm-smmu-v3.c | 58 +++++++++++++++++++++++++++
2 files changed, 155 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c
index 23c80c7..c91e45d 100644
--- a/drivers/acpi/iort.c
+++ b/drivers/acpi/iort.c
@@ -455,6 +455,90 @@ iort_get_device_domain(struct device *dev, u32 req_id)
return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI);
}

+static void acpi_smmu_v3_register_irq(int hwirq, const char *name,
+ struct resource *res)
+{
+ int irq = acpi_register_gsi(NULL, hwirq, ACPI_EDGE_SENSITIVE,
+ ACPI_ACTIVE_HIGH);
+
+ if (irq < 0) {
+ pr_err("could not register gsi hwirq %d name [%s]\n", hwirq,
+ name);
+ return;
+ }
+
+ res->start = irq;
+ res->end = irq;
+ res->flags = IORESOURCE_IRQ;
+ res->name = name;
+}
+
+static int arm_smmu_v3_count_resources(struct acpi_iort_node *node)
+{
+ struct acpi_iort_smmu_v3 *smmu;
+ /* Always present mem resource */
+ int num_res = 1;
+
+ /* Retrieve SMMUv3 specific data */
+ smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+ if (smmu->event_gsiv)
+ num_res++;
+
+ if (smmu->pri_gsiv)
+ num_res++;
+
+ if (smmu->gerr_gsiv)
+ num_res++;
+
+ if (smmu->sync_gsiv)
+ num_res++;
+
+ return num_res;
+}
+
+static void arm_smmu_v3_init_resources(struct resource *res,
+ struct acpi_iort_node *node)
+{
+ struct acpi_iort_smmu_v3 *smmu;
+ int num_res = 0;
+
+ /* Retrieve SMMUv3 specific data */
+ smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+ res[num_res].start = smmu->base_address;
+ res[num_res].end = smmu->base_address + SZ_128K - 1;
+ res[num_res].flags = IORESOURCE_MEM;
+
+ num_res++;
+
+ if (smmu->event_gsiv)
+ acpi_smmu_v3_register_irq(smmu->event_gsiv, "eventq",
+ &res[num_res++]);
+
+ if (smmu->pri_gsiv)
+ acpi_smmu_v3_register_irq(smmu->pri_gsiv, "priq",
+ &res[num_res++]);
+
+ if (smmu->gerr_gsiv)
+ acpi_smmu_v3_register_irq(smmu->gerr_gsiv, "gerror",
+ &res[num_res++]);
+
+ if (smmu->sync_gsiv)
+ acpi_smmu_v3_register_irq(smmu->sync_gsiv, "cmdq-sync",
+ &res[num_res++]);
+}
+
+static bool arm_smmu_v3_is_coherent(struct acpi_iort_node *node)
+{
+ struct acpi_iort_smmu_v3 *smmu;
+
+ /* Retrieve SMMUv3 specific data */
+ smmu = (struct acpi_iort_smmu_v3 *)node->node_data;
+
+ return smmu->flags & ACPI_IORT_SMMU_V3_COHACC_OVERRIDE;
+}
+
struct iort_iommu_config {
const char *name;
int (*iommu_init)(struct acpi_iort_node *node);
@@ -464,10 +548,22 @@ struct iort_iommu_config {
struct acpi_iort_node *node);
};

+const struct iort_iommu_config iort_arm_smmu_v3_cfg = {
+ .name = "arm-smmu-v3",
+ .iommu_is_coherent = arm_smmu_v3_is_coherent,
+ .iommu_count_resources = arm_smmu_v3_count_resources,
+ .iommu_init_resources = arm_smmu_v3_init_resources
+};
+
static inline const struct iort_iommu_config *
iort_get_iommu_config(struct acpi_iort_node *node)
{
- return NULL;
+ switch (node->type) {
+ case ACPI_IORT_NODE_SMMU_V3:
+ return &iort_arm_smmu_v3_cfg;
+ default:
+ return NULL;
+ }
}

/**
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 1a4e9ce..294ed5e 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -1749,6 +1749,8 @@ arm_smmu_get_by_fwnode(struct fwnode_handle *handle)

if (is_of_node(handle))
smmu_pdev = of_find_device_by_node(to_of_node(handle));
+ else if (is_fwnode_iommu(handle))
+ smmu_pdev = platform_find_device_by_fwnode(handle);

if (!smmu_pdev)
return NULL;
@@ -1771,6 +1773,7 @@ static struct iommu_ops arm_smmu_ops;
static int arm_smmu_add_device(struct device *dev)
{
int i, ret;
+
struct arm_smmu_device *smmu;
struct arm_smmu_master_data *master;
struct iommu_fwspec *fwspec = dev_iommu_fwspec(dev);
@@ -2741,6 +2744,61 @@ static int __init arm_smmu_of_init(struct device_node *np)
}
IOMMU_OF_DECLARE(arm_smmuv3, "arm,smmu-v3", arm_smmu_of_init);

+#ifdef CONFIG_ACPI
+static int acpi_smmu_v3_init(struct acpi_table_header *table)
+{
+ struct acpi_iort_node *iort_node, *iort_end;
+ struct acpi_table_iort *iort;
+ struct fwnode_handle *fwnode;
+ int i, ret;
+
+ /*
+ * table and iort will both point to the start of IORT table, but
+ * have different struct types
+ */
+ iort = (struct acpi_table_iort *)table;
+
+ /* Get the first IORT node */
+ iort_node = ACPI_ADD_PTR(struct acpi_iort_node, table,
+ iort->node_offset);
+ iort_end = ACPI_ADD_PTR(struct acpi_iort_node, table,
+ table->length);
+
+ for (i = 0; i < iort->node_count; i++) {
+
+ if (iort_node >= iort_end) {
+ pr_err("iort node pointer overflows, bad table\n");
+ return -EINVAL;
+ }
+
+ if (iort_node->type == ACPI_IORT_NODE_SMMU_V3) {
+ fwnode = iommu_alloc_fwnode();
+
+ if (!fwnode)
+ return -ENOMEM;
+
+ ret = iort_add_smmu_platform_device(fwnode,
+ iort_node);
+ if (ret)
+ goto free;
+
+ fwspec_iommu_set_ops(fwnode, &arm_smmu_ops);
+ }
+
+ iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node,
+ iort_node->length);
+ }
+
+ return 0;
+free:
+ iommu_free_fwnode(fwnode);
+ return ret;
+
+}
+IORT_ACPI_DECLARE(arm_smmu_v3, ACPI_SIG_IORT, acpi_smmu_v3_init);
+
+#endif
+
MODULE_DESCRIPTION("IOMMU API for ARM architected SMMUv3 implementations");
MODULE_AUTHOR("Will Deacon <***@arm.com>");
MODULE_LICENSE("GPL v2");
--
2.6.4
Lorenzo Pieralisi
2016-07-20 11:26:14 UTC
Permalink
On systems booting with ACPI that enable the ARM SMMU components
in the kernel config options, the ARM SMMU v3 init function
(ie arm_smmu_init(), that registers the driver and sets-up bus
iommu operations) does not run only because the device tree interface
(of_find_matching_node()) fails to find the respective device tree
nodes for ARM SMMU devices.

This works as long as there are no ARM SMMU devices to be probed
with ACPI. If ARM SMMU v3 components are part of the IORT tables,
for them to be instantiated and probed the function registering
the ARM SMMU v3 driver must be able to register the driver and
initialize the bus IOMMU operations accordingly.

This patch changes the logic in arm-smmu-v3 init call to allow
for it to be probed in ACPI systems.

Signed-off-by: Lorenzo Pieralisi <***@arm.com>
Acked-by: Will Deacon <***@arm.com>
Cc: Will Deacon <***@arm.com>
Cc: Robin Murphy <***@arm.com>
Cc: Joerg Roedel <***@8bytes.org>
---
drivers/iommu/arm-smmu-v3.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 15e74da..1a4e9ce 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -2689,11 +2689,16 @@ static int __init arm_smmu_init(void)
struct device_node *np;
int ret;

- np = of_find_matching_node(NULL, arm_smmu_of_match);
- if (!np)
- return 0;
+ if (acpi_disabled) {
+ np = of_find_matching_node(NULL, arm_smmu_of_match);
+ if (!np)
+ return 0;

- of_node_put(np);
+ of_node_put(np);
+ } else {
+ if (!iort_node_match(ACPI_IORT_NODE_SMMU_V3))
+ return 0;
+ }

ret = platform_driver_register(&arm_smmu_driver);
if (ret)
--
2.6.4
Lorenzo Pieralisi
2016-07-20 11:26:24 UTC
Permalink
In ARM ACPI systems, IOMMU components are specified through static
IORT table entries. In order to create platform devices for the
corresponding ARM SMMU components, IORT kernel code should be made
able to parse IORT table entries and create platform devices
dynamically.

This patch adds the generic IORT infrastructure required to create
platform devices for ARM SMMUs.

ARM SMMU versions have different resources requirement therefore this
patch also introduces an IORT specific structure (ie iort_iommu_config)
that contains hooks (to be defined when the corresponding ARM SMMU
driver support is added to the kernel) to be used to define the
platform devices names, init the IOMMUs, count their resources and
finally initialize them.

Signed-off-by: Lorenzo Pieralisi <***@arm.com>
Cc: Hanjun Guo <***@linaro.org>
Cc: Tomasz Nowicki <***@semihalf.com>
Cc: "Rafael J. Wysocki" <***@rjwysocki.net>
---
drivers/acpi/iort.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/iort.h | 10 +++++
2 files changed, 117 insertions(+)

diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c
index 71516e8..23c80c7 100644
--- a/drivers/acpi/iort.c
+++ b/drivers/acpi/iort.c
@@ -22,6 +22,7 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/pci.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>

struct iort_its_msi_chip {
@@ -454,6 +455,112 @@ iort_get_device_domain(struct device *dev, u32 req_id)
return irq_find_matching_fwnode(handle, DOMAIN_BUS_PCI_MSI);
}

+struct iort_iommu_config {
+ const char *name;
+ int (*iommu_init)(struct acpi_iort_node *node);
+ bool (*iommu_is_coherent)(struct acpi_iort_node *node);
+ int (*iommu_count_resources)(struct acpi_iort_node *node);
+ void (*iommu_init_resources)(struct resource *res,
+ struct acpi_iort_node *node);
+};
+
+static inline const struct iort_iommu_config *
+iort_get_iommu_config(struct acpi_iort_node *node)
+{
+ return NULL;
+}
+
+/**
+ * iort_add_smmu_platform_device() - Allocate a platform device for SMMU
+ * @fwnode: Pointer to SMMU firmware node
+ * @node: Pointer to SMMU ACPI IORT node
+ *
+ * Returns: 0 on success, <0 failure
+ */
+int iort_add_smmu_platform_device(struct fwnode_handle *fwnode,
+ struct acpi_iort_node *node)
+{
+ struct platform_device *pdev;
+ struct resource *r;
+ enum dev_dma_attr attr;
+ int ret, count;
+ const struct iort_iommu_config *ops =
+ iort_get_iommu_config(node);
+
+ if (!ops)
+ return -ENODEV;
+
+ pdev = platform_device_alloc(ops->name, PLATFORM_DEVID_AUTO);
+ if (!pdev)
+ return PTR_ERR(pdev);
+
+ count = ops->iommu_count_resources(node);
+
+ r = kcalloc(count, sizeof(*r), GFP_KERNEL);
+ if (!r) {
+ ret = -ENOMEM;
+ goto dev_put;
+ }
+
+ ops->iommu_init_resources(r, node);
+
+ ret = platform_device_add_resources(pdev, r, count);
+ /*
+ * Resources are duplicated in platform_device_add_resources,
+ * free their allocated memory
+ */
+ kfree(r);
+
+ if (ret)
+ goto dev_put;
+
+ /*
+ * Add a copy of IORT node pointer to platform_data to
+ * be used to retrieve IORT data information.
+ */
+ ret = platform_device_add_data(pdev, &node, sizeof(node));
+ if (ret)
+ goto dev_put;
+
+ pdev->dev.dma_mask = kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL);
+ if (!pdev->dev.dma_mask) {
+ ret = -ENOMEM;
+ goto dev_put;
+ }
+
+ pdev->dev.fwnode = fwnode;
+
+ /*
+ * Set default dma mask value for the table walker,
+ * to be overridden on probing with correct value.
+ */
+ *pdev->dev.dma_mask = DMA_BIT_MASK(32);
+ pdev->dev.coherent_dma_mask = *pdev->dev.dma_mask;
+
+ attr = ops->iommu_is_coherent(node) ?
+ DEV_DMA_COHERENT : DEV_DMA_NON_COHERENT;
+
+ /* Configure DMA for the page table walker */
+ acpi_dma_configure(&pdev->dev, attr);
+
+ ret = platform_device_add(pdev);
+ if (ret)
+ goto dma_deconfigure;
+
+ iort_set_fwnode(node, pdev->dev.fwnode);
+
+ return 0;
+
+dma_deconfigure:
+ acpi_dma_deconfigure(&pdev->dev);
+ kfree(pdev->dev.dma_mask);
+
+dev_put:
+ platform_device_put(pdev);
+
+ return ret;
+}
+
void __init iort_table_detect(void)
{
acpi_status status;
diff --git a/include/linux/iort.h b/include/linux/iort.h
index ac2706a..18e6836 100644
--- a/include/linux/iort.h
+++ b/include/linux/iort.h
@@ -31,6 +31,9 @@ bool iort_node_match(u8 type);
void iort_table_detect(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);
+/* IOMMU interface */
+int iort_add_smmu_platform_device(struct fwnode_handle *fwnode,
+ struct acpi_iort_node *node);
#else
static inline bool iort_node_match(u8 type) { return false; }
static inline void iort_table_detect(void) { }
@@ -38,6 +41,13 @@ static inline u32 iort_msi_map_rid(struct device *dev, u32 req_id)
{ return req_id; }
static inline struct irq_domain *
iort_get_device_domain(struct device *dev, u32 req_id) { return NULL; }
+/* IOMMU interface */
+static inline int
+iort_add_smmu_platform_device(struct fwnode_handle *fwnode,
+ struct acpi_iort_node *node)
+{
+ return -ENODEV;
+}
#endif

#define IORT_ACPI_DECLARE(name, table_id, fn) \
--
2.6.4
Lorenzo Pieralisi
2016-07-20 11:27:00 UTC
Permalink
The iommu_fwspec structure, used to hold per device iommu configuration
data is not OF specific and therefore can be moved to a generic
and OF independent compilation unit.

In particular, the iommu_fwspec handling hinges on the device_node
pointer to identify the IOMMU device associated with the iommu_fwspec
structure, that is easily converted to a more generic fwnode_handle
pointer that can cater for OF and non-OF (ie ACPI) systems.

Create the files and related Kconfig entry to decouple iommu_fwspec
structure from the OF iommu kernel layer.

Given that the current iommu_fwspec implementation relies on
the arch specific struct device.archdata.iommu field in its
implementation, by making the code standalone and independent
of the OF layer this patch makes sure that the iommu_fwspec
kernel code can be selected only on arches implementing the
struct device.archdata.iommu field by adding an explicit
arch dependency in its config entry.

Current drivers using the iommu_fwspec for streamid translation
are converted to the new iommu_fwspec API by simply converting
the device_node to its fwnode_handle pointer.

Signed-off-by: Lorenzo Pieralisi <***@arm.com>
Cc: Will Deacon <***@arm.com>
Cc: Hanjun Guo <***@linaro.org>
Cc: Robin Murphy <***@arm.com>
Cc: Joerg Roedel <***@8bytes.org>
---
drivers/iommu/Kconfig | 4 ++
drivers/iommu/Makefile | 1 +
drivers/iommu/arm-smmu-v3.c | 13 +++--
drivers/iommu/iommu-fwspec.c | 114 +++++++++++++++++++++++++++++++++++++++++++
drivers/iommu/of_iommu.c | 52 --------------------
include/linux/iommu-fwspec.h | 60 +++++++++++++++++++++++
include/linux/of_iommu.h | 24 +++------
7 files changed, 196 insertions(+), 72 deletions(-)
create mode 100644 drivers/iommu/iommu-fwspec.c
create mode 100644 include/linux/iommu-fwspec.h

diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index d1c66af..2b26bfb 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -67,6 +67,10 @@ config OF_IOMMU
def_bool y
depends on OF && IOMMU_API

+config IOMMU_FWSPEC
+ def_bool y
+ depends on ARM64 && IOMMU_API
+
# IOMMU-agnostic DMA-mapping layer
config IOMMU_DMA
bool
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index c6edb31..dd85337 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o
obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
obj-$(CONFIG_IOMMU_IOVA) += iova.o
obj-$(CONFIG_OF_IOMMU) += of_iommu.o
+obj-$(CONFIG_IOMMU_FWSPEC) += iommu-fwspec.o
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
index 98e6441..052a26c 100644
--- a/drivers/iommu/arm-smmu-v3.c
+++ b/drivers/iommu/arm-smmu-v3.c
@@ -25,6 +25,7 @@
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
+#include <linux/iommu-fwspec.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/msi.h>
@@ -1739,9 +1740,13 @@ arm_smmu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova)
return ret;
}

-static struct arm_smmu_device *arm_smmu_get_by_node(struct device_node *np)
+static struct arm_smmu_device *
+arm_smmu_get_by_fwnode(struct fwnode_handle *handle)
{
- struct platform_device *smmu_pdev = of_find_device_by_node(np);
+ struct platform_device *smmu_pdev = NULL;
+
+ if (is_of_node(handle))
+ smmu_pdev = of_find_device_by_node(to_of_node(handle));

if (!smmu_pdev)
return NULL;
@@ -1780,7 +1785,7 @@ static int arm_smmu_add_device(struct device *dev)
master = fwspec->iommu_priv;
smmu = master->smmu;
} else {
- smmu = arm_smmu_get_by_node(fwspec->iommu_np);
+ smmu = arm_smmu_get_by_fwnode(fwspec->iommu_fwnode);
if (!smmu)
return -ENODEV;
master = kzalloc(sizeof(*master), GFP_KERNEL);
@@ -1892,7 +1897,7 @@ out_unlock:

static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args)
{
- int ret = iommu_fwspec_init(dev, args->np);
+ int ret = iommu_fwspec_init(dev, &args->np->fwnode);

if (!ret)
ret = iommu_fwspec_add_ids(dev, &args->args[0], 1);
diff --git a/drivers/iommu/iommu-fwspec.c b/drivers/iommu/iommu-fwspec.c
new file mode 100644
index 0000000..0600c17
--- /dev/null
+++ b/drivers/iommu/iommu-fwspec.c
@@ -0,0 +1,114 @@
+/*
+ * Firmware handling helpers for IOMMU
+ *
+ * Copyright (c) 2016 ARM Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/iommu.h>
+#include <linux/iommu-fwspec.h>
+#include <linux/of_iommu.h>
+#include <linux/slab.h>
+
+struct fwspec_iommu_node {
+ struct list_head list;
+ struct fwnode_handle *fwnode;
+ const struct iommu_ops *ops;
+};
+static LIST_HEAD(fwnode_iommu_list);
+static DEFINE_SPINLOCK(fwspec_iommu_lock);
+
+void fwspec_iommu_set_ops(struct fwnode_handle *fwnode,
+ const struct iommu_ops *ops)
+{
+ struct fwspec_iommu_node *iommu =
+ kzalloc(sizeof(*iommu), GFP_KERNEL);
+
+ if (WARN_ON(!iommu))
+ return;
+
+ INIT_LIST_HEAD(&iommu->list);
+ iommu->fwnode = fwnode;
+ iommu->ops = ops;
+ spin_lock(&fwspec_iommu_lock);
+ list_add_tail(&iommu->list, &fwnode_iommu_list);
+ spin_unlock(&fwspec_iommu_lock);
+}
+
+const struct iommu_ops *fwspec_iommu_get_ops(struct fwnode_handle *fwnode)
+{
+ struct fwspec_iommu_node *node;
+ const struct iommu_ops *ops = NULL;
+
+ spin_lock(&fwspec_iommu_lock);
+ list_for_each_entry(node, &fwnode_iommu_list, list)
+ if (node->fwnode == fwnode) {
+ ops = node->ops;
+ break;
+ }
+ spin_unlock(&fwspec_iommu_lock);
+ return ops;
+}
+
+int iommu_fwspec_init(struct device *dev,
+ struct fwnode_handle *iommu_fwnode)
+{
+ struct iommu_fwspec *fwspec = dev->archdata.iommu;
+ const struct iommu_ops *ops;
+
+ if (fwspec)
+ return 0;
+
+ fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);
+ if (!fwspec)
+ return -ENOMEM;
+
+ if (is_of_node(iommu_fwnode))
+ ops = of_iommu_get_ops(to_of_node(iommu_fwnode));
+ else
+ ops = fwspec_iommu_get_ops(iommu_fwnode);
+
+ fwspec->iommu_fwnode = iommu_fwnode;
+ fwspec->iommu_ops = ops;
+
+ dev->archdata.iommu = fwspec;
+ return 0;
+}
+
+void iommu_fwspec_free(struct device *dev)
+{
+ kfree(dev->archdata.iommu);
+}
+
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
+{
+ struct iommu_fwspec *fwspec = dev->archdata.iommu;
+ size_t size;
+
+ if (!fwspec)
+ return -EINVAL;
+
+ size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + 1]);
+ fwspec = krealloc(dev->archdata.iommu, size, GFP_KERNEL);
+ if (!fwspec)
+ return -ENOMEM;
+
+ while (num_ids--)
+ fwspec->ids[fwspec->num_ids++] = *ids++;
+
+ dev->archdata.iommu = fwspec;
+ return 0;
+}
+
+inline struct iommu_fwspec *dev_iommu_fwspec(struct device *dev)
+{
+ return dev->archdata.iommu;
+}
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index 4618e89..1fe1f62 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -216,55 +216,3 @@ void __init of_iommu_init(void)
of_node_full_name(np));
}
}
-
-int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np)
-{
- struct iommu_fwspec *fwspec = dev->archdata.iommu;
-
- if (fwspec)
- return 0;
-
- fwspec = kzalloc(sizeof(*fwspec), GFP_KERNEL);
- if (!fwspec)
- return -ENOMEM;
-
- fwspec->iommu_np = of_node_get(iommu_np);
- fwspec->iommu_ops = of_iommu_get_ops(iommu_np);
- dev->archdata.iommu = fwspec;
- return 0;
-}
-
-void iommu_fwspec_free(struct device *dev)
-{
- struct iommu_fwspec *fwspec = dev->archdata.iommu;
-
- if (fwspec) {
- of_node_put(fwspec->iommu_np);
- kfree(fwspec);
- }
-}
-
-int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
-{
- struct iommu_fwspec *fwspec = dev->archdata.iommu;
- size_t size;
-
- if (!fwspec)
- return -EINVAL;
-
- size = offsetof(struct iommu_fwspec, ids[fwspec->num_ids + 1]);
- fwspec = krealloc(dev->archdata.iommu, size, GFP_KERNEL);
- if (!fwspec)
- return -ENOMEM;
-
- while (num_ids--)
- fwspec->ids[fwspec->num_ids++] = *ids++;
-
- dev->archdata.iommu = fwspec;
- return 0;
-}
-
-inline struct iommu_fwspec *dev_iommu_fwspec(struct device *dev)
-{
- return dev->archdata.iommu;
-}
diff --git a/include/linux/iommu-fwspec.h b/include/linux/iommu-fwspec.h
new file mode 100644
index 0000000..3a572c6
--- /dev/null
+++ b/include/linux/iommu-fwspec.h
@@ -0,0 +1,60 @@
+#ifndef __IOMMU_FWSPEC_H
+#define __IOMMU_FWSPEC_H
+
+#include <linux/device.h>
+#include <linux/iommu.h>
+
+struct iommu_fwspec {
+ const struct iommu_ops *iommu_ops;
+ struct fwnode_handle *iommu_fwnode;
+ void *iommu_priv;
+ unsigned int num_ids;
+ u32 ids[];
+};
+
+#ifdef CONFIG_IOMMU_FWSPEC
+int iommu_fwspec_init(struct device *dev,
+ struct fwnode_handle *iommu_fwnode);
+void iommu_fwspec_free(struct device *dev);
+int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
+struct iommu_fwspec *dev_iommu_fwspec(struct device *dev);
+
+void fwspec_iommu_set_ops(struct fwnode_handle *fwnode,
+ const struct iommu_ops *ops);
+const struct iommu_ops *fwspec_iommu_get_ops(struct fwnode_handle *fwnode);
+#else /* CONFIG_IOMMU_FWSPEC */
+static inline int iommu_fwspec_init(struct device *dev,
+ struct fwnode_handle *iommu_fwnode)
+{
+ return -ENODEV;
+}
+
+static inline void iommu_fwspec_free(struct device *dev)
+{
+}
+
+static inline int iommu_fwspec_add_ids(struct device *dev, u32 *ids,
+ int num_ids)
+{
+ return -ENODEV;
+}
+
+static inline struct iommu_fwspec *dev_iommu_fwspec(struct device *dev)
+{
+ return NULL;
+}
+
+static inline void fwspec_iommu_set_ops(struct fwnode_handle *fwnode,
+ const struct iommu_ops *ops)
+{
+}
+
+static inline const struct iommu_ops *
+fwspec_iommu_get_ops(struct fwnode_handle *fwnode)
+{
+ return NULL;
+}
+
+#endif /* CONFIG_IOMMU_FWSPEC */
+
+#endif /* __IOMMU_FWSPEC_H */
diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h
index 308791f..2362232 100644
--- a/include/linux/of_iommu.h
+++ b/include/linux/of_iommu.h
@@ -15,13 +15,8 @@ extern void of_iommu_init(void);
extern const struct iommu_ops *of_iommu_configure(struct device *dev,
struct device_node *master_np);

-struct iommu_fwspec {
- const struct iommu_ops *iommu_ops;
- struct device_node *iommu_np;
- void *iommu_priv;
- unsigned int num_ids;
- u32 ids[];
-};
+void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
+const struct iommu_ops *of_iommu_get_ops(struct device_node *np);

#else

@@ -39,17 +34,14 @@ static inline const struct iommu_ops *of_iommu_configure(struct device *dev,
return NULL;
}

-struct iommu_fwspec;
-
-#endif /* CONFIG_OF_IOMMU */
+static inline void of_iommu_set_ops(struct device_node *np,
+ const struct iommu_ops *ops)
+{ }

-int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np);
-void iommu_fwspec_free(struct device *dev);
-int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
-struct iommu_fwspec *dev_iommu_fwspec(struct device *dev);
+static inline const struct iommu_ops *
+of_iommu_get_ops(struct device_node *np) { return NULL; }

-void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
-const struct iommu_ops *of_iommu_get_ops(struct device_node *np);
+#endif /* CONFIG_OF_IOMMU */

extern struct of_device_id __iommu_of_table;
--
2.6.4
Robin Murphy
2016-07-25 15:10:15 UTC
Permalink
Hi Lorenzo,
Post by Lorenzo Pieralisi
The iommu_fwspec structure, used to hold per device iommu configuration
data is not OF specific and therefore can be moved to a generic
and OF independent compilation unit.
In particular, the iommu_fwspec handling hinges on the device_node
pointer to identify the IOMMU device associated with the iommu_fwspec
structure, that is easily converted to a more generic fwnode_handle
pointer that can cater for OF and non-OF (ie ACPI) systems.
Create the files and related Kconfig entry to decouple iommu_fwspec
structure from the OF iommu kernel layer.
Given that the current iommu_fwspec implementation relies on
the arch specific struct device.archdata.iommu field in its
implementation, by making the code standalone and independent
of the OF layer this patch makes sure that the iommu_fwspec
kernel code can be selected only on arches implementing the
struct device.archdata.iommu field by adding an explicit
arch dependency in its config entry.
Current drivers using the iommu_fwspec for streamid translation
are converted to the new iommu_fwspec API by simply converting
the device_node to its fwnode_handle pointer.
---
drivers/iommu/Kconfig | 4 ++
drivers/iommu/Makefile | 1 +
drivers/iommu/arm-smmu-v3.c | 13 +++--
drivers/iommu/iommu-fwspec.c | 114 +++++++++++++++++++++++++++++++++++++++++++
drivers/iommu/of_iommu.c | 52 --------------------
include/linux/iommu-fwspec.h | 60 +++++++++++++++++++++++
include/linux/of_iommu.h | 24 +++------
7 files changed, 196 insertions(+), 72 deletions(-)
create mode 100644 drivers/iommu/iommu-fwspec.c
create mode 100644 include/linux/iommu-fwspec.h
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index d1c66af..2b26bfb 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -67,6 +67,10 @@ config OF_IOMMU
def_bool y
depends on OF && IOMMU_API
+config IOMMU_FWSPEC
+ def_bool y
+ depends on ARM64 && IOMMU_API
I think that could be at least (ARM || ARM64).
Post by Lorenzo Pieralisi
+
# IOMMU-agnostic DMA-mapping layer
config IOMMU_DMA
bool
[...]
Post by Lorenzo Pieralisi
diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h
index 308791f..2362232 100644
--- a/include/linux/of_iommu.h
+++ b/include/linux/of_iommu.h
@@ -15,13 +15,8 @@ extern void of_iommu_init(void);
extern const struct iommu_ops *of_iommu_configure(struct device *dev,
struct device_node *master_np);
-struct iommu_fwspec {
- const struct iommu_ops *iommu_ops;
- struct device_node *iommu_np;
- void *iommu_priv;
- unsigned int num_ids;
- u32 ids[];
-};
+void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
+const struct iommu_ops *of_iommu_get_ops(struct device_node *np);
Is there some reason we need to retain the existing definitions of
these? I was assuming we'd be able to move the entire implementation
over to the fwspec code and leave behind nothing more than trivial
wrappers, e.g.:

#define of_iommu_get_ops(np) iommu_fwspec_get_ops(&(np)->fwnode_handle)

Robin.
Post by Lorenzo Pieralisi
#else
@@ -39,17 +34,14 @@ static inline const struct iommu_ops *of_iommu_configure(struct device *dev,
return NULL;
}
-struct iommu_fwspec;
-
-#endif /* CONFIG_OF_IOMMU */
+static inline void of_iommu_set_ops(struct device_node *np,
+ const struct iommu_ops *ops)
+{ }
-int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np);
-void iommu_fwspec_free(struct device *dev);
-int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
-struct iommu_fwspec *dev_iommu_fwspec(struct device *dev);
+static inline const struct iommu_ops *
+of_iommu_get_ops(struct device_node *np) { return NULL; }
-void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
-const struct iommu_ops *of_iommu_get_ops(struct device_node *np);
+#endif /* CONFIG_OF_IOMMU */
extern struct of_device_id __iommu_of_table;
Rob Herring
2016-07-25 15:21:59 UTC
Permalink
Post by Robin Murphy
Hi Lorenzo,
Post by Lorenzo Pieralisi
The iommu_fwspec structure, used to hold per device iommu configuration
data is not OF specific and therefore can be moved to a generic
and OF independent compilation unit.
In particular, the iommu_fwspec handling hinges on the device_node
pointer to identify the IOMMU device associated with the iommu_fwspec
structure, that is easily converted to a more generic fwnode_handle
pointer that can cater for OF and non-OF (ie ACPI) systems.
Create the files and related Kconfig entry to decouple iommu_fwspec
structure from the OF iommu kernel layer.
Given that the current iommu_fwspec implementation relies on
the arch specific struct device.archdata.iommu field in its
implementation, by making the code standalone and independent
of the OF layer this patch makes sure that the iommu_fwspec
kernel code can be selected only on arches implementing the
struct device.archdata.iommu field by adding an explicit
arch dependency in its config entry.
Current drivers using the iommu_fwspec for streamid translation
are converted to the new iommu_fwspec API by simply converting
the device_node to its fwnode_handle pointer.
---
drivers/iommu/Kconfig | 4 ++
drivers/iommu/Makefile | 1 +
drivers/iommu/arm-smmu-v3.c | 13 +++--
drivers/iommu/iommu-fwspec.c | 114 +++++++++++++++++++++++++++++++++++++++++++
drivers/iommu/of_iommu.c | 52 --------------------
include/linux/iommu-fwspec.h | 60 +++++++++++++++++++++++
include/linux/of_iommu.h | 24 +++------
7 files changed, 196 insertions(+), 72 deletions(-)
create mode 100644 drivers/iommu/iommu-fwspec.c
create mode 100644 include/linux/iommu-fwspec.h
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index d1c66af..2b26bfb 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -67,6 +67,10 @@ config OF_IOMMU
def_bool y
depends on OF && IOMMU_API
+config IOMMU_FWSPEC
+ def_bool y
+ depends on ARM64 && IOMMU_API
I think that could be at least (ARM || ARM64).
Why any arch dependency?

Seems like OF_IOMMU (and ACPI?) should select this.

Rob
Lorenzo Pieralisi
2016-07-25 15:56:33 UTC
Permalink
Post by Rob Herring
Post by Robin Murphy
Hi Lorenzo,
Post by Lorenzo Pieralisi
The iommu_fwspec structure, used to hold per device iommu configuration
data is not OF specific and therefore can be moved to a generic
and OF independent compilation unit.
In particular, the iommu_fwspec handling hinges on the device_node
pointer to identify the IOMMU device associated with the iommu_fwspec
structure, that is easily converted to a more generic fwnode_handle
pointer that can cater for OF and non-OF (ie ACPI) systems.
Create the files and related Kconfig entry to decouple iommu_fwspec
structure from the OF iommu kernel layer.
Given that the current iommu_fwspec implementation relies on
the arch specific struct device.archdata.iommu field in its
implementation, by making the code standalone and independent
of the OF layer this patch makes sure that the iommu_fwspec
kernel code can be selected only on arches implementing the
struct device.archdata.iommu field by adding an explicit
arch dependency in its config entry.
Current drivers using the iommu_fwspec for streamid translation
are converted to the new iommu_fwspec API by simply converting
the device_node to its fwnode_handle pointer.
---
drivers/iommu/Kconfig | 4 ++
drivers/iommu/Makefile | 1 +
drivers/iommu/arm-smmu-v3.c | 13 +++--
drivers/iommu/iommu-fwspec.c | 114 +++++++++++++++++++++++++++++++++++++++++++
drivers/iommu/of_iommu.c | 52 --------------------
include/linux/iommu-fwspec.h | 60 +++++++++++++++++++++++
include/linux/of_iommu.h | 24 +++------
7 files changed, 196 insertions(+), 72 deletions(-)
create mode 100644 drivers/iommu/iommu-fwspec.c
create mode 100644 include/linux/iommu-fwspec.h
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index d1c66af..2b26bfb 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -67,6 +67,10 @@ config OF_IOMMU
def_bool y
depends on OF && IOMMU_API
+config IOMMU_FWSPEC
+ def_bool y
+ depends on ARM64 && IOMMU_API
I think that could be at least (ARM || ARM64).
Why any arch dependency?
Seems like OF_IOMMU (and ACPI?) should select this.
Absolutely, that's the end goal. Current issue is that the iommu_fwspec
mechanism relies on dev_archdata.iommu pointer internally to work and
since that's arch specific I can't select it on arches that do not have
that field, it would break the compilation.

I will follow up with Robin to make sure we will be able to
implement what you request above.

Thanks !
Lorenzo
Lorenzo Pieralisi
2016-07-25 15:41:15 UTC
Permalink
Post by Robin Murphy
Hi Lorenzo,
Post by Lorenzo Pieralisi
The iommu_fwspec structure, used to hold per device iommu configuration
data is not OF specific and therefore can be moved to a generic
and OF independent compilation unit.
In particular, the iommu_fwspec handling hinges on the device_node
pointer to identify the IOMMU device associated with the iommu_fwspec
structure, that is easily converted to a more generic fwnode_handle
pointer that can cater for OF and non-OF (ie ACPI) systems.
Create the files and related Kconfig entry to decouple iommu_fwspec
structure from the OF iommu kernel layer.
Given that the current iommu_fwspec implementation relies on
the arch specific struct device.archdata.iommu field in its
implementation, by making the code standalone and independent
of the OF layer this patch makes sure that the iommu_fwspec
kernel code can be selected only on arches implementing the
struct device.archdata.iommu field by adding an explicit
arch dependency in its config entry.
Current drivers using the iommu_fwspec for streamid translation
are converted to the new iommu_fwspec API by simply converting
the device_node to its fwnode_handle pointer.
---
drivers/iommu/Kconfig | 4 ++
drivers/iommu/Makefile | 1 +
drivers/iommu/arm-smmu-v3.c | 13 +++--
drivers/iommu/iommu-fwspec.c | 114 +++++++++++++++++++++++++++++++++++++++++++
drivers/iommu/of_iommu.c | 52 --------------------
include/linux/iommu-fwspec.h | 60 +++++++++++++++++++++++
include/linux/of_iommu.h | 24 +++------
7 files changed, 196 insertions(+), 72 deletions(-)
create mode 100644 drivers/iommu/iommu-fwspec.c
create mode 100644 include/linux/iommu-fwspec.h
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index d1c66af..2b26bfb 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -67,6 +67,10 @@ config OF_IOMMU
def_bool y
depends on OF && IOMMU_API
+config IOMMU_FWSPEC
+ def_bool y
+ depends on ARM64 && IOMMU_API
I think that could be at least (ARM || ARM64).
Yes agreed.
Post by Robin Murphy
Post by Lorenzo Pieralisi
# IOMMU-agnostic DMA-mapping layer
config IOMMU_DMA
bool
[...]
Post by Lorenzo Pieralisi
diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h
index 308791f..2362232 100644
--- a/include/linux/of_iommu.h
+++ b/include/linux/of_iommu.h
@@ -15,13 +15,8 @@ extern void of_iommu_init(void);
extern const struct iommu_ops *of_iommu_configure(struct device *dev,
struct device_node *master_np);
-struct iommu_fwspec {
- const struct iommu_ops *iommu_ops;
- struct device_node *iommu_np;
- void *iommu_priv;
- unsigned int num_ids;
- u32 ids[];
-};
+void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
+const struct iommu_ops *of_iommu_get_ops(struct device_node *np);
Is there some reason we need to retain the existing definitions of
these? I was assuming we'd be able to move the entire implementation
over to the fwspec code and leave behind nothing more than trivial
#define of_iommu_get_ops(np) iommu_fwspec_get_ops(&(np)->fwnode_handle)
Yep, that's exactly what I did but then I was bitten by config
dependencies. If we implement of_iommu_get/set_ops() as wrappers,
we have to compile iommu_fwspec_get/set_ops() on arches that may
not have struct dev_archdata.iommu, unless we introduce yet another
config symbol to avoid compiling that code (see eg iommu_fwspec_init(),
we can't compile it on eg x86 even though we do need of_iommu_get_ops()
on it - so iommu_fwspec_get_ops(), that lives in the same compilation
unit as eg iommu_fwspec_init()).

So short answer is: there is no reason apart from dev_archdata.iommu
being arch specific, if we were able to move iommu_fwspec to generic
code (ie struct device, somehow) I would certainly get rid of this
stupid code duplication (or as I said I can add a config entry for
that, more ideas are welcome).

Thanks,
Lorenzo
Post by Robin Murphy
Robin.
Post by Lorenzo Pieralisi
#else
@@ -39,17 +34,14 @@ static inline const struct iommu_ops *of_iommu_configure(struct device *dev,
return NULL;
}
-struct iommu_fwspec;
-
-#endif /* CONFIG_OF_IOMMU */
+static inline void of_iommu_set_ops(struct device_node *np,
+ const struct iommu_ops *ops)
+{ }
-int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np);
-void iommu_fwspec_free(struct device *dev);
-int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
-struct iommu_fwspec *dev_iommu_fwspec(struct device *dev);
+static inline const struct iommu_ops *
+of_iommu_get_ops(struct device_node *np) { return NULL; }
-void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
-const struct iommu_ops *of_iommu_get_ops(struct device_node *np);
+#endif /* CONFIG_OF_IOMMU */
extern struct of_device_id __iommu_of_table;
Robin Murphy
2016-07-25 15:51:16 UTC
Permalink
On 25/07/16 16:41, Lorenzo Pieralisi wrote:
[...]
Post by Lorenzo Pieralisi
Post by Robin Murphy
Post by Lorenzo Pieralisi
diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h
index 308791f..2362232 100644
--- a/include/linux/of_iommu.h
+++ b/include/linux/of_iommu.h
@@ -15,13 +15,8 @@ extern void of_iommu_init(void);
extern const struct iommu_ops *of_iommu_configure(struct device *dev,
struct device_node *master_np);
-struct iommu_fwspec {
- const struct iommu_ops *iommu_ops;
- struct device_node *iommu_np;
- void *iommu_priv;
- unsigned int num_ids;
- u32 ids[];
-};
+void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
+const struct iommu_ops *of_iommu_get_ops(struct device_node *np);
Is there some reason we need to retain the existing definitions of
these? I was assuming we'd be able to move the entire implementation
over to the fwspec code and leave behind nothing more than trivial
#define of_iommu_get_ops(np) iommu_fwspec_get_ops(&(np)->fwnode_handle)
Yep, that's exactly what I did but then I was bitten by config
dependencies. If we implement of_iommu_get/set_ops() as wrappers,
we have to compile iommu_fwspec_get/set_ops() on arches that may
not have struct dev_archdata.iommu, unless we introduce yet another
config symbol to avoid compiling that code (see eg iommu_fwspec_init(),
we can't compile it on eg x86 even though we do need of_iommu_get_ops()
on it - so iommu_fwspec_get_ops(), that lives in the same compilation
unit as eg iommu_fwspec_init()).
So short answer is: there is no reason apart from dev_archdata.iommu
being arch specific, if we were able to move iommu_fwspec to generic
code (ie struct device, somehow) I would certainly get rid of this
stupid code duplication (or as I said I can add a config entry for
that, more ideas are welcome).
OK, given Rob's comment as well, I guess breaking that dependency is to
everyone's benefit. Since it's quite closely related, how about if we
follow the arch_setup_dma_ops() pattern with an
arch_{get,set}_iommu_fwspec(dev) type thing?

Robin.
Post by Lorenzo Pieralisi
Thanks,
Lorenzo
Post by Robin Murphy
Robin.
Post by Lorenzo Pieralisi
#else
@@ -39,17 +34,14 @@ static inline const struct iommu_ops *of_iommu_configure(struct device *dev,
return NULL;
}
-struct iommu_fwspec;
-
-#endif /* CONFIG_OF_IOMMU */
+static inline void of_iommu_set_ops(struct device_node *np,
+ const struct iommu_ops *ops)
+{ }
-int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np);
-void iommu_fwspec_free(struct device *dev);
-int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
-struct iommu_fwspec *dev_iommu_fwspec(struct device *dev);
+static inline const struct iommu_ops *
+of_iommu_get_ops(struct device_node *np) { return NULL; }
-void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
-const struct iommu_ops *of_iommu_get_ops(struct device_node *np);
+#endif /* CONFIG_OF_IOMMU */
extern struct of_device_id __iommu_of_table;
Lorenzo Pieralisi
2016-07-25 16:12:33 UTC
Permalink
Post by Robin Murphy
[...]
Post by Lorenzo Pieralisi
Post by Robin Murphy
Post by Lorenzo Pieralisi
diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h
index 308791f..2362232 100644
--- a/include/linux/of_iommu.h
+++ b/include/linux/of_iommu.h
@@ -15,13 +15,8 @@ extern void of_iommu_init(void);
extern const struct iommu_ops *of_iommu_configure(struct device *dev,
struct device_node *master_np);
-struct iommu_fwspec {
- const struct iommu_ops *iommu_ops;
- struct device_node *iommu_np;
- void *iommu_priv;
- unsigned int num_ids;
- u32 ids[];
-};
+void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
+const struct iommu_ops *of_iommu_get_ops(struct device_node *np);
Is there some reason we need to retain the existing definitions of
these? I was assuming we'd be able to move the entire implementation
over to the fwspec code and leave behind nothing more than trivial
#define of_iommu_get_ops(np) iommu_fwspec_get_ops(&(np)->fwnode_handle)
Yep, that's exactly what I did but then I was bitten by config
dependencies. If we implement of_iommu_get/set_ops() as wrappers,
we have to compile iommu_fwspec_get/set_ops() on arches that may
not have struct dev_archdata.iommu, unless we introduce yet another
config symbol to avoid compiling that code (see eg iommu_fwspec_init(),
we can't compile it on eg x86 even though we do need of_iommu_get_ops()
on it - so iommu_fwspec_get_ops(), that lives in the same compilation
unit as eg iommu_fwspec_init()).
So short answer is: there is no reason apart from dev_archdata.iommu
being arch specific, if we were able to move iommu_fwspec to generic
code (ie struct device, somehow) I would certainly get rid of this
stupid code duplication (or as I said I can add a config entry for
that, more ideas are welcome).
OK, given Rob's comment as well, I guess breaking that dependency is to
everyone's benefit. Since it's quite closely related, how about if we
follow the arch_setup_dma_ops() pattern with an
arch_{get,set}_iommu_fwspec(dev) type thing?
Yes we can do that too as an intermediate step, that solves the
problem (and it makes this patch much simpler), it is cleaner
than doing it with a(nother) Kconfig entry.

Thanks,
Lorenzo
Post by Robin Murphy
Robin.
Post by Lorenzo Pieralisi
Thanks,
Lorenzo
Post by Robin Murphy
Robin.
Post by Lorenzo Pieralisi
#else
@@ -39,17 +34,14 @@ static inline const struct iommu_ops *of_iommu_configure(struct device *dev,
return NULL;
}
-struct iommu_fwspec;
-
-#endif /* CONFIG_OF_IOMMU */
+static inline void of_iommu_set_ops(struct device_node *np,
+ const struct iommu_ops *ops)
+{ }
-int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np);
-void iommu_fwspec_free(struct device *dev);
-int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
-struct iommu_fwspec *dev_iommu_fwspec(struct device *dev);
+static inline const struct iommu_ops *
+of_iommu_get_ops(struct device_node *np) { return NULL; }
-void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
-const struct iommu_ops *of_iommu_get_ops(struct device_node *np);
+#endif /* CONFIG_OF_IOMMU */
extern struct of_device_id __iommu_of_table;
Lorenzo Pieralisi
2016-08-11 11:26:38 UTC
Permalink
Post by Robin Murphy
[...]
Post by Lorenzo Pieralisi
Post by Robin Murphy
Post by Lorenzo Pieralisi
diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h
index 308791f..2362232 100644
--- a/include/linux/of_iommu.h
+++ b/include/linux/of_iommu.h
@@ -15,13 +15,8 @@ extern void of_iommu_init(void);
extern const struct iommu_ops *of_iommu_configure(struct device *dev,
struct device_node *master_np);
-struct iommu_fwspec {
- const struct iommu_ops *iommu_ops;
- struct device_node *iommu_np;
- void *iommu_priv;
- unsigned int num_ids;
- u32 ids[];
-};
+void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
+const struct iommu_ops *of_iommu_get_ops(struct device_node *np);
Is there some reason we need to retain the existing definitions of
these? I was assuming we'd be able to move the entire implementation
over to the fwspec code and leave behind nothing more than trivial
#define of_iommu_get_ops(np) iommu_fwspec_get_ops(&(np)->fwnode_handle)
Yep, that's exactly what I did but then I was bitten by config
dependencies. If we implement of_iommu_get/set_ops() as wrappers,
we have to compile iommu_fwspec_get/set_ops() on arches that may
not have struct dev_archdata.iommu, unless we introduce yet another
config symbol to avoid compiling that code (see eg iommu_fwspec_init(),
we can't compile it on eg x86 even though we do need of_iommu_get_ops()
on it - so iommu_fwspec_get_ops(), that lives in the same compilation
unit as eg iommu_fwspec_init()).
So short answer is: there is no reason apart from dev_archdata.iommu
being arch specific, if we were able to move iommu_fwspec to generic
code (ie struct device, somehow) I would certainly get rid of this
stupid code duplication (or as I said I can add a config entry for
that, more ideas are welcome).
OK, given Rob's comment as well, I guess breaking that dependency is to
everyone's benefit. Since it's quite closely related, how about if we
follow the arch_setup_dma_ops() pattern with an
arch_{get,set}_iommu_fwspec(dev) type thing?
How about this (on top of your current iommu/generic branch):

If that's ok feel free to squash it in for the next posting,
or I can add it to my IORT series (I'd argue though that the
problem it solves is not strictly related to ACPI), please
let me know.

Thanks !
Lorenzo

-- >8 --
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 2d601d7..dfd331d 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -58,6 +58,7 @@ config ARM
select HAVE_GENERIC_DMA_COHERENT
select HAVE_HW_BREAKPOINT if (PERF_EVENTS && (CPU_V6 || CPU_V6K || CPU_V7))
select HAVE_IDE if PCI || ISA || PCMCIA
+ select HAVE_IOMMU_FWSPEC if IOMMU_API
select HAVE_IRQ_TIME_ACCOUNTING
select HAVE_KERNEL_GZIP
select HAVE_KERNEL_LZ4
diff --git a/arch/arm/include/asm/iommu-fwspec.h b/arch/arm/include/asm/iommu-fwspec.h
new file mode 100644
index 0000000..d6581a1
--- /dev/null
+++ b/arch/arm/include/asm/iommu-fwspec.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_IOMMU_FWSPEC_H
+#define __ASM_IOMMU_FWSPEC_H
+
+static inline void arch_set_iommu_fwspec(struct device *dev,
+ struct iommu_fwspec *fwspec)
+{
+ dev->archdata.iommu = fwspec;
+}
+
+static inline struct iommu_fwspec *arch_get_iommu_fwspec(struct device *dev)
+{
+ return dev->archdata.iommu;
+}
+#endif
+
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 69c8787..90d420f 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -82,6 +82,7 @@ config ARM64
select HAVE_GCC_PLUGINS
select HAVE_GENERIC_DMA_COHERENT
select HAVE_HW_BREAKPOINT if PERF_EVENTS
+ select HAVE_IOMMU_FWSPEC if IOMMU_API
select HAVE_IRQ_TIME_ACCOUNTING
select HAVE_MEMBLOCK
select HAVE_MEMBLOCK_NODE_MAP if NUMA
diff --git a/arch/arm64/include/asm/iommu-fwspec.h b/arch/arm64/include/asm/iommu-fwspec.h
new file mode 100644
index 0000000..d6581a1
--- /dev/null
+++ b/arch/arm64/include/asm/iommu-fwspec.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_IOMMU_FWSPEC_H
+#define __ASM_IOMMU_FWSPEC_H
+
+static inline void arch_set_iommu_fwspec(struct device *dev,
+ struct iommu_fwspec *fwspec)
+{
+ dev->archdata.iommu = fwspec;
+}
+
+static inline struct iommu_fwspec *arch_get_iommu_fwspec(struct device *dev)
+{
+ return dev->archdata.iommu;
+}
+#endif
+
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 8ee54d7..101cb17 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -67,6 +67,9 @@ config OF_IOMMU
def_bool y
depends on OF && IOMMU_API

+config HAVE_IOMMU_FWSPEC
+ bool
+
# IOMMU-agnostic DMA-mapping layer
config IOMMU_DMA
bool
diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c
index bec51eb..e30f860 100644
--- a/drivers/iommu/of_iommu.c
+++ b/drivers/iommu/of_iommu.c
@@ -222,7 +222,7 @@ postcore_initcall_sync(of_iommu_init);

int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np)
{
- struct iommu_fwspec *fwspec = dev->archdata.iommu;
+ struct iommu_fwspec *fwspec = arch_get_iommu_fwspec(dev);

if (fwspec)
return 0;
@@ -233,13 +233,13 @@ int iommu_fwspec_init(struct device *dev, struct device_node *iommu_np)

fwspec->iommu_np = of_node_get(iommu_np);
fwspec->iommu_ops = of_iommu_get_ops(iommu_np);
- dev->archdata.iommu = fwspec;
+ arch_set_iommu_fwspec(dev, fwspec);
return 0;
}

void iommu_fwspec_free(struct device *dev)
{
- struct iommu_fwspec *fwspec = dev->archdata.iommu;
+ struct iommu_fwspec *fwspec = arch_get_iommu_fwspec(dev);

if (fwspec) {
of_node_put(fwspec->iommu_np);
@@ -249,7 +249,7 @@ void iommu_fwspec_free(struct device *dev)

int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
{
- struct iommu_fwspec *fwspec = dev->archdata.iommu;
+ struct iommu_fwspec *fwspec = arch_get_iommu_fwspec(dev);
size_t size;

if (!fwspec)
@@ -263,11 +263,11 @@ int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids)
while (num_ids--)
fwspec->ids[fwspec->num_ids++] = *ids++;

- dev->archdata.iommu = fwspec;
+ arch_set_iommu_fwspec(dev, fwspec);
return 0;
}

inline struct iommu_fwspec *dev_iommu_fwspec(struct device *dev)
{
- return dev->archdata.iommu;
+ return arch_get_iommu_fwspec(dev);
}
diff --git a/include/linux/of_iommu.h b/include/linux/of_iommu.h
index accdc05..358db49 100644
--- a/include/linux/of_iommu.h
+++ b/include/linux/of_iommu.h
@@ -46,6 +46,16 @@ void iommu_fwspec_free(struct device *dev);
int iommu_fwspec_add_ids(struct device *dev, u32 *ids, int num_ids);
struct iommu_fwspec *dev_iommu_fwspec(struct device *dev);

+#ifdef CONFIG_HAVE_IOMMU_FWSPEC
+#include <asm/iommu-fwspec.h>
+#else /* !CONFIG_HAVE_IOMMU_FWSPEC */
+static inline void arch_set_iommu_fwspec(struct device *dev,
+ struct iommu_fwspec *fwspec) {}
+
+static inline struct iommu_fwspec *
+arch_get_iommu_fwspec(struct device *dev) { return NULL; }
+#endif
+
void of_iommu_set_ops(struct device_node *np, const struct iommu_ops *ops);
const struct iommu_ops *of_iommu_get_ops(struct device_node *np);
Lorenzo Pieralisi
2016-07-20 11:27:10 UTC
Permalink
The platform device kernel API does not provide functions to
retrieve a platform device through the corresponding struct
device fwnode pointer.

Implement the fwnode platform_device look-up in drivers core
code by using the bus_find_device() API and a corresponding
matching function. The OF equivalent (eg of_find_device_by_node())
will reuse the newly introduced function when OF code will
take care of setting up the device->fwnode value that is
currently left dangling for platform devices instantiated out
of device tree nodes.

Signed-off-by: Lorenzo Pieralisi <***@arm.com>
Cc: Greg Kroah-Hartman <***@linuxfoundation.org>
Cc: "Rafael J. Wysocki" <***@rjwysocki.net>
---
drivers/base/platform.c | 23 +++++++++++++++++++++++
include/linux/platform_device.h | 3 +++
2 files changed, 26 insertions(+)

diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 6482d47..3ef150d 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -760,6 +760,29 @@ err_out:
}
EXPORT_SYMBOL_GPL(__platform_create_bundle);

+static int fwnode_dev_match(struct device *dev, void *data)
+{
+ return dev->fwnode == data;
+}
+
+/**
+ * platform_find_device_by_fwnode() - Find the platform_device associated
+ * with a fwnode
+ * @fwnode: Pointer to firmware node
+ *
+ * Returns platform_device pointer, or NULL if not found
+ */
+struct platform_device *
+platform_find_device_by_fwnode(struct fwnode_handle *fwnode)
+{
+ struct device *dev;
+
+ dev = bus_find_device(&platform_bus_type, NULL, fwnode,
+ fwnode_dev_match);
+ return dev ? to_platform_device(dev) : NULL;
+}
+EXPORT_SYMBOL(platform_find_device_by_fwnode);
+
/**
* __platform_register_drivers - register an array of platform drivers
* @drivers: an array of drivers to register
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index 98c2a7c..01a3eb2 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -276,6 +276,9 @@ extern struct platform_device *__platform_create_bundle(
struct resource *res, unsigned int n_res,
const void *data, size_t size, struct module *module);

+extern struct platform_device *
+platform_find_device_by_fwnode(struct fwnode_handle *fwnode);
+
int __platform_register_drivers(struct platform_driver * const *drivers,
unsigned int count, struct module *owner);
void platform_unregister_drivers(struct platform_driver * const *drivers,
--
2.6.4
Lorenzo Pieralisi
2016-07-20 11:27:36 UTC
Permalink
Since commit e647b532275b ("ACPI: Add early device probing
infrastructure") the kernel has gained the infrastructure that allows
adding linker script section entries to execute ACPI driver callbacks
(ie probe routines) for all subsystems that register a table entry
in the respective kernel section (eg clocksource, irqchip).

Since ARM IOMMU devices data is described through IORT tables when
booting with ACPI, the ARM IOMMU drivers must be made able to hook ACPI
callback routines that are called to probe IORT entries and initialize
the respective IOMMU devices.

To avoid adding driver specific hooks into IORT table initialization
code (breaking therefore code modularity - ie ACPI IORT code must be made
aware of ARM SMMU drivers ACPI init callbacks), this patch adds code
that allows ARM SMMU drivers to take advantage of the ACPI early probing
infrastructure, so that they can add linker script section entries
containing drivers callback to be executed on IORT tables detection.

Since IORT nodes are differentiated by a type, the callback routines
can easily parse the IORT table entries, check the IORT nodes and
carry out some actions whenever the IORT node type associated with
the driver specific callback is matched.

Signed-off-by: Lorenzo Pieralisi <***@arm.com>
Cc: Tomasz Nowicki <***@semihalf.com>
Cc: "Rafael J. Wysocki" <***@rjwysocki.net>
Cc: Marc Zyngier <***@arm.com>
---
drivers/acpi/iort.c | 2 ++
include/asm-generic/vmlinux.lds.h | 1 +
include/linux/iort.h | 3 +++
3 files changed, 6 insertions(+)

diff --git a/drivers/acpi/iort.c b/drivers/acpi/iort.c
index 6611607..1f440d2 100644
--- a/drivers/acpi/iort.c
+++ b/drivers/acpi/iort.c
@@ -383,4 +383,6 @@ void __init iort_table_detect(void)
const char *msg = acpi_format_exception(status);
pr_err("Failed to get table, %s\n", msg);
}
+
+ acpi_probe_device_table(iort);
}
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 6a67ab9..b896ab2 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -538,6 +538,7 @@
IRQCHIP_OF_MATCH_TABLE() \
ACPI_PROBE_TABLE(irqchip) \
ACPI_PROBE_TABLE(clksrc) \
+ ACPI_PROBE_TABLE(iort) \
EARLYCON_TABLE()

#define INIT_TEXT \
diff --git a/include/linux/iort.h b/include/linux/iort.h
index d7daba1..9bb30c5 100644
--- a/include/linux/iort.h
+++ b/include/linux/iort.h
@@ -38,4 +38,7 @@ static inline struct irq_domain *
iort_get_device_domain(struct device *dev, u32 req_id) { return NULL; }
#endif

+#define IORT_ACPI_DECLARE(name, table_id, fn) \
+ ACPI_DECLARE_PROBE_ENTRY(iort, name, table_id, 0, NULL, 0, fn)
+
#endif /* __IORT_H__ */
--
2.6.4
Lorenzo Pieralisi
2016-07-20 11:27:48 UTC
Permalink
On systems booting with a device tree, every struct device is
associated with a struct device_node, that represents its DT
representation. The device node can be used in generic kernel
contexts (eg IRQ translation, IOMMU streamid mapping), to
retrieve the properties associated with the device and carry
out kernel operation accordingly. Owing to the 1:1 relationship
between the device and its device_node, the device_node can also
be used as a look-up token for the device (eg looking up a device
through its device_node), to retrieve the device in kernel paths
where the device_node is available.

On systems booting with ACPI, the same abstraction provided by
the device_node is required to provide look-up functionality.

Therefore, mirroring the approach implemented in the IRQ domain
kernel layer, this patch adds an additional fwnode type FWNODE_IOMMU.

This patch also implements a glue kernel layer that allows to
allocate/free FWNODE_IOMMU fwnode_handle structures and associate
them with IOMMU devices.

Signed-off-by: Lorenzo Pieralisi <***@arm.com>
Cc: Joerg Roedel <***@8bytes.org>
Cc: "Rafael J. Wysocki" <***@rjwysocki.net>
---
include/linux/fwnode.h | 1 +
include/linux/iommu.h | 25 +++++++++++++++++++++++++
2 files changed, 26 insertions(+)

diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h
index 8516717..6e10050 100644
--- a/include/linux/fwnode.h
+++ b/include/linux/fwnode.h
@@ -19,6 +19,7 @@ enum fwnode_type {
FWNODE_ACPI_DATA,
FWNODE_PDATA,
FWNODE_IRQCHIP,
+ FWNODE_IOMMU,
};

struct fwnode_handle {
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 664683a..298328a 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -38,6 +38,7 @@ struct bus_type;
struct device;
struct iommu_domain;
struct notifier_block;
+struct fwnode_handle;

/* iommu fault flags */
#define IOMMU_FAULT_READ 0x0
@@ -540,4 +541,28 @@ static inline void iommu_device_unlink(struct device *dev, struct device *link)

#endif /* CONFIG_IOMMU_API */

+/* IOMMU fwnode handling */
+static inline bool is_fwnode_iommu(struct fwnode_handle *fwnode)
+{
+ return fwnode && fwnode->type == FWNODE_IOMMU;
+}
+
+static inline struct fwnode_handle *iommu_alloc_fwnode(void)
+{
+ struct fwnode_handle *fwnode;
+
+ fwnode = kzalloc(sizeof(struct fwnode_handle), GFP_KERNEL);
+ fwnode->type = FWNODE_IOMMU;
+
+ return fwnode;
+}
+
+static inline void iommu_free_fwnode(struct fwnode_handle *fwnode)
+{
+ if (WARN_ON(!is_fwnode_iommu(fwnode)))
+ return;
+
+ kfree(fwnode);
+}
+
#endif /* __LINUX_IOMMU_H */
--
2.6.4
Dennis Chen
2016-07-25 05:54:30 UTC
Permalink
Hi
Post by Lorenzo Pieralisi
https://lkml.org/lkml/2016/6/7/523
v2 -> v3
- Rebased on top of dependencies series [1][2][3](v4.7-rc3)
- Added back reliance on ACPI early probing infrastructure
- Patch[1-3] merged through other dependent series
- Added back IOMMU fwnode generalization
- Move SMMU v3 static functions configuration to IORT code
- Implemented generic IOMMU fwspec API
- Added code to implement fwnode platform device look-up
- Rebased on top of dependencies series [1][2][3](v4.7-rc1)
- Removed IOMMU fwnode generalization
- Implemented ARM SMMU v3 ACPI probing instead of ARM SMMU v2
owing to patch series dependencies [1]
- Moved platform device creation logic to IORT code to
generalize its usage for ARM SMMU v1-v2-v3 components
- Removed reliance on ACPI early device probing
- Created IORT specific iommu_xlate() translation hook leaving
OF code unchanged according to v1 reviews
The ACPI IORT table provides information that allows instantiating
ARM SMMU devices and carrying out id mappings between components on
ARM based systems (devices, IOMMUs, interrupt controllers).
http://infocenter.arm.com/help/topic/com.arm.doc.den0049b/DEN0049B_IO_Remapping_Table.pdf
this patchset enables ARM SMMU v3 support on ACPI systems.
Most of the code is aimed at building the required generic ACPI
infrastructure to create and enable IOMMU components and to bring
the IOMMU infrastructure for ACPI on par with DT, which is going to
make future ARM SMMU components easier to integrate.
PATCH (1) adds a FWNODE_IOMMU type to the struct fwnode_handle type.
It is required to attach a fwnode identifier to platform
devices allocated/detected through IORT tables entries;
IOMMU devices have to have an identifier to look them up
eg IOMMU core layer carrying out id translation. This can be
done through a fwnode_handle (ie IOMMU platform devices created
out of IORT tables are not ACPI devices hence they can't be
allocated as such, otherwise they would have a fwnode_handle of
type FWNODE_ACPI). This patch requires discussion and it is key
to the RFC.
PATCH (2) makes use of the ACPI early probing API to add a linker script
section for probing devices via IORT ACPI kernel code.
PATCH (3) provides IORT support for registering IOMMU IORT node through
their fwnode handle.
PATCH (4) implements core code fwnode based platform devices look-up.
PATCH (5) extends iommu_fwspec so that it can be used on ACPI based
system by creating a generic IOMMU fwspec kernel layer.
PATCH (6) implements the of_dma_configure() API in ACPI world -
acpi_dma_configure() - and patches PCI and ACPI core code to
start making use of it.
PATCH (7) provides an IORT function to detect existence of specific type
of IORT components.
PATCH (8) creates the kernel infrastructure required to create ARM SMMU
platform devices for IORT nodes.
PATCH (9) refactors the ARM SMMU v3 driver so that the init functions are
split in a way that groups together code that probes through DT
and code that carries out HW registers FW agnostic probing, in
preparation for adding the ACPI probing path.
PATCH (10) rework ARM SMMU v3 platform driver registration to make it work
on ACPI systems.
PATCH (11) Building on patch (8), it adds ARM SMMU v3 IORT IOMMU
operations to create and probe ARM SMMU v3 components.
PATCH (12) Extend the IORT iort_node_map_rid() to work on a type mask
instead of a single type so that the translation API can
be used on a range of components.
PATCH (13) provides IORT infrastructure to carry out IOMMU configuration
for devices and hook it up to the previously introduced ACPI
DMA configure API.
[1] R.Murphy "Generic DT bindings for PCI and ARM SMMU v3" v4
https://marc.info/?l=devicetree&m=146739193215518&w=2
[2] T.Nowicki "Introduce ACPI world to ITS irqchip" v7
https://marc.info/?l=linux-arm-kernel&m=146642080022289&w=2
[3] T.Nowicki "Support for ARM64 ACPI based PCI host controller" v8
http://marc.info/?l=linux-acpi&m=146462129816292&w=2
git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/linux.git acpi/iort-smmu-v3i
I thought I can got all the 13 patches applied with the above git tree, but I can't find
any ACPI related stuff after I cloned the git repos to my local machine, am I missing
something here?

Thanks,
Dennis
Post by Lorenzo Pieralisi
Tested on FVP models for ARM SMMU v3 probing path.
drivers: iommu: add FWNODE_IOMMU fwnode type
drivers: acpi: iort: introduce linker section for IORT entries probing
drivers: acpi: iort: add support for IOMMU fwnode registration
drivers: platform: add fwnode base platform devices retrieval
drivers: iommu: make iommu_fwspec OF agnostic
drivers: acpi: implement acpi_dma_configure
drivers: acpi: iort: add node match function
drivers: acpi: iort: add support for ARM SMMU platform devices
creation
drivers: iommu: arm-smmu-v3: split probe functions into DT/generic
portions
drivers: iommu: arm-smmu-v3: enable ACPI driver initialization
drivers: iommu: arm-smmu-v3: add IORT platform device creation
drivers: acpi: iort: replace rid map type with type mask
drivers: acpi: iort: introduce iort_iommu_configure
drivers/acpi/glue.c | 4 +-
drivers/acpi/iort.c | 360 +++++++++++++++++++++++++++++++++++++-
drivers/acpi/scan.c | 29 +++
drivers/base/platform.c | 23 +++
drivers/iommu/Kconfig | 4 +
drivers/iommu/Makefile | 1 +
drivers/iommu/arm-smmu-v3.c | 147 ++++++++++++++--
drivers/iommu/iommu-fwspec.c | 114 ++++++++++++
drivers/iommu/of_iommu.c | 52 ------
drivers/pci/probe.c | 3 +-
include/acpi/acpi_bus.h | 2 +
include/asm-generic/vmlinux.lds.h | 1 +
include/linux/acpi.h | 5 +
include/linux/fwnode.h | 1 +
include/linux/iommu-fwspec.h | 60 +++++++
include/linux/iommu.h | 25 +++
include/linux/iort.h | 19 ++
include/linux/of_iommu.h | 24 +--
include/linux/platform_device.h | 3 +
19 files changed, 782 insertions(+), 95 deletions(-)
create mode 100644 drivers/iommu/iommu-fwspec.c
create mode 100644 include/linux/iommu-fwspec.h
--
2.6.4
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
More majordomo info at http://vger.kernel.org/majordomo-info.html
Lorenzo Pieralisi
2016-07-25 08:38:06 UTC
Permalink
Post by Dennis Chen
Hi
Post by Lorenzo Pieralisi
https://lkml.org/lkml/2016/6/7/523
v2 -> v3
- Rebased on top of dependencies series [1][2][3](v4.7-rc3)
- Added back reliance on ACPI early probing infrastructure
- Patch[1-3] merged through other dependent series
- Added back IOMMU fwnode generalization
- Move SMMU v3 static functions configuration to IORT code
- Implemented generic IOMMU fwspec API
- Added code to implement fwnode platform device look-up
- Rebased on top of dependencies series [1][2][3](v4.7-rc1)
- Removed IOMMU fwnode generalization
- Implemented ARM SMMU v3 ACPI probing instead of ARM SMMU v2
owing to patch series dependencies [1]
- Moved platform device creation logic to IORT code to
generalize its usage for ARM SMMU v1-v2-v3 components
- Removed reliance on ACPI early device probing
- Created IORT specific iommu_xlate() translation hook leaving
OF code unchanged according to v1 reviews
The ACPI IORT table provides information that allows instantiating
ARM SMMU devices and carrying out id mappings between components on
ARM based systems (devices, IOMMUs, interrupt controllers).
http://infocenter.arm.com/help/topic/com.arm.doc.den0049b/DEN0049B_IO_Remapping_Table.pdf
this patchset enables ARM SMMU v3 support on ACPI systems.
Most of the code is aimed at building the required generic ACPI
infrastructure to create and enable IOMMU components and to bring
the IOMMU infrastructure for ACPI on par with DT, which is going to
make future ARM SMMU components easier to integrate.
PATCH (1) adds a FWNODE_IOMMU type to the struct fwnode_handle type.
It is required to attach a fwnode identifier to platform
devices allocated/detected through IORT tables entries;
IOMMU devices have to have an identifier to look them up
eg IOMMU core layer carrying out id translation. This can be
done through a fwnode_handle (ie IOMMU platform devices created
out of IORT tables are not ACPI devices hence they can't be
allocated as such, otherwise they would have a fwnode_handle of
type FWNODE_ACPI). This patch requires discussion and it is key
to the RFC.
PATCH (2) makes use of the ACPI early probing API to add a linker script
section for probing devices via IORT ACPI kernel code.
PATCH (3) provides IORT support for registering IOMMU IORT node through
their fwnode handle.
PATCH (4) implements core code fwnode based platform devices look-up.
PATCH (5) extends iommu_fwspec so that it can be used on ACPI based
system by creating a generic IOMMU fwspec kernel layer.
PATCH (6) implements the of_dma_configure() API in ACPI world -
acpi_dma_configure() - and patches PCI and ACPI core code to
start making use of it.
PATCH (7) provides an IORT function to detect existence of specific type
of IORT components.
PATCH (8) creates the kernel infrastructure required to create ARM SMMU
platform devices for IORT nodes.
PATCH (9) refactors the ARM SMMU v3 driver so that the init functions are
split in a way that groups together code that probes through DT
and code that carries out HW registers FW agnostic probing, in
preparation for adding the ACPI probing path.
PATCH (10) rework ARM SMMU v3 platform driver registration to make it work
on ACPI systems.
PATCH (11) Building on patch (8), it adds ARM SMMU v3 IORT IOMMU
operations to create and probe ARM SMMU v3 components.
PATCH (12) Extend the IORT iort_node_map_rid() to work on a type mask
instead of a single type so that the translation API can
be used on a range of components.
PATCH (13) provides IORT infrastructure to carry out IOMMU configuration
for devices and hook it up to the previously introduced ACPI
DMA configure API.
[1] R.Murphy "Generic DT bindings for PCI and ARM SMMU v3" v4
https://marc.info/?l=devicetree&m=146739193215518&w=2
[2] T.Nowicki "Introduce ACPI world to ITS irqchip" v7
https://marc.info/?l=linux-arm-kernel&m=146642080022289&w=2
[3] T.Nowicki "Support for ARM64 ACPI based PCI host controller" v8
http://marc.info/?l=linux-acpi&m=146462129816292&w=2
git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/linux.git acpi/iort-smmu-v3i
I thought I can got all the 13 patches applied with the above git
tree, but I can't find any ACPI related stuff after I cloned the git
repos to my local machine, am I missing something here?
Have you pulled the acpi/iort-smmu-v3 branch ?

Thanks,
Lorenzo
Dennis Chen
2016-07-26 01:16:49 UTC
Permalink
Post by Lorenzo Pieralisi
Post by Dennis Chen
Hi
Post by Lorenzo Pieralisi
https://lkml.org/lkml/2016/6/7/523
v2 -> v3
- Rebased on top of dependencies series [1][2][3](v4.7-rc3)
- Added back reliance on ACPI early probing infrastructure
- Patch[1-3] merged through other dependent series
- Added back IOMMU fwnode generalization
- Move SMMU v3 static functions configuration to IORT code
- Implemented generic IOMMU fwspec API
- Added code to implement fwnode platform device look-up
- Rebased on top of dependencies series [1][2][3](v4.7-rc1)
- Removed IOMMU fwnode generalization
- Implemented ARM SMMU v3 ACPI probing instead of ARM SMMU v2
owing to patch series dependencies [1]
- Moved platform device creation logic to IORT code to
generalize its usage for ARM SMMU v1-v2-v3 components
- Removed reliance on ACPI early device probing
- Created IORT specific iommu_xlate() translation hook leaving
OF code unchanged according to v1 reviews
The ACPI IORT table provides information that allows instantiating
ARM SMMU devices and carrying out id mappings between components on
ARM based systems (devices, IOMMUs, interrupt controllers).
http://infocenter.arm.com/help/topic/com.arm.doc.den0049b/DEN0049B_IO_Remapping_Table.pdf
this patchset enables ARM SMMU v3 support on ACPI systems.
Most of the code is aimed at building the required generic ACPI
infrastructure to create and enable IOMMU components and to bring
the IOMMU infrastructure for ACPI on par with DT, which is going to
make future ARM SMMU components easier to integrate.
PATCH (1) adds a FWNODE_IOMMU type to the struct fwnode_handle type.
It is required to attach a fwnode identifier to platform
devices allocated/detected through IORT tables entries;
IOMMU devices have to have an identifier to look them up
eg IOMMU core layer carrying out id translation. This can be
done through a fwnode_handle (ie IOMMU platform devices created
out of IORT tables are not ACPI devices hence they can't be
allocated as such, otherwise they would have a fwnode_handle of
type FWNODE_ACPI). This patch requires discussion and it is key
to the RFC.
PATCH (2) makes use of the ACPI early probing API to add a linker script
section for probing devices via IORT ACPI kernel code.
PATCH (3) provides IORT support for registering IOMMU IORT node through
their fwnode handle.
PATCH (4) implements core code fwnode based platform devices look-up.
PATCH (5) extends iommu_fwspec so that it can be used on ACPI based
system by creating a generic IOMMU fwspec kernel layer.
PATCH (6) implements the of_dma_configure() API in ACPI world -
acpi_dma_configure() - and patches PCI and ACPI core code to
start making use of it.
PATCH (7) provides an IORT function to detect existence of specific type
of IORT components.
PATCH (8) creates the kernel infrastructure required to create ARM SMMU
platform devices for IORT nodes.
PATCH (9) refactors the ARM SMMU v3 driver so that the init functions are
split in a way that groups together code that probes through DT
and code that carries out HW registers FW agnostic probing, in
preparation for adding the ACPI probing path.
PATCH (10) rework ARM SMMU v3 platform driver registration to make it work
on ACPI systems.
PATCH (11) Building on patch (8), it adds ARM SMMU v3 IORT IOMMU
operations to create and probe ARM SMMU v3 components.
PATCH (12) Extend the IORT iort_node_map_rid() to work on a type mask
instead of a single type so that the translation API can
be used on a range of components.
PATCH (13) provides IORT infrastructure to carry out IOMMU configuration
for devices and hook it up to the previously introduced ACPI
DMA configure API.
[1] R.Murphy "Generic DT bindings for PCI and ARM SMMU v3" v4
https://marc.info/?l=devicetree&m=146739193215518&w=2
[2] T.Nowicki "Introduce ACPI world to ITS irqchip" v7
https://marc.info/?l=linux-arm-kernel&m=146642080022289&w=2
[3] T.Nowicki "Support for ARM64 ACPI based PCI host controller" v8
http://marc.info/?l=linux-acpi&m=146462129816292&w=2
git://git.kernel.org/pub/scm/linux/kernel/git/lpieralisi/linux.git acpi/iort-smmu-v3i
I thought I can got all the 13 patches applied with the above git
tree, but I can't find any ACPI related stuff after I cloned the git
repos to my local machine, am I missing something here?
Have you pulled the acpi/iort-smmu-v3 branch ?
Hello Lorenzo, forgive my carelessness missing the additional checkout of that branch.
Thanks and have a nice day :)
Post by Lorenzo Pieralisi
Thanks,
Lorenzo
Loading...