From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Thu, 5 Aug 2021 09:07:16 +0100 Subject: [PATCH 01/19] iommu: Introduce a union to struct iommu_resv_region A union is introduced to struct iommu_resv_region to hold any firmware specific data. This is in preparation to add support for IORT RMR reserve regions and the union now holds the RMR specific information. Signed-off-by: Shameer Kolothum --- include/linux/iommu.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/linux/iommu.h b/include/linux/iommu.h index d2f3435e7d17..d5cfd0c6a217 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -126,6 +126,13 @@ enum iommu_resv_type { IOMMU_RESV_SW_MSI, }; +struct iommu_iort_rmr_data { +#define IOMMU_RMR_REMAP_PERMITTED (1 << 0) + u32 flags; + u32 sid; /* Stream Id associated with RMR entry */ + void *smmu; /* Associated IORT SMMU node pointer */ +}; + /** * struct iommu_resv_region - descriptor for a reserved memory region * @list: Linked list pointers @@ -133,6 +140,7 @@ enum iommu_resv_type { * @length: Length of the region in bytes * @prot: IOMMU Protection flags (READ/WRITE/...) * @type: Type of the reserved region + * @rmr: ACPI IORT RMR specific data */ struct iommu_resv_region { struct list_head list; @@ -140,6 +148,9 @@ struct iommu_resv_region { size_t length; int prot; enum iommu_resv_type type; + union { + struct iommu_iort_rmr_data rmr; + } fw_data; }; /** -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Thu, 5 Aug 2021 09:07:17 +0100 Subject: [PATCH 02/19] ACPI/IORT: Add support for RMR node parsing Add support for parsing RMR node information from ACPI. Find the associated streamid and smmu node info from the RMR node and populate a linked list with RMR memory descriptors. Signed-off-by: Shameer Kolothum --- drivers/acpi/arm64/iort.c | 134 +++++++++++++++++++++++++++++++++++++- 1 file changed, 133 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c index f2f8f05662de..7df83d80819b 100644 --- a/drivers/acpi/arm64/iort.c +++ b/drivers/acpi/arm64/iort.c @@ -40,6 +40,8 @@ struct iort_fwnode { static LIST_HEAD(iort_fwnode_list); static DEFINE_SPINLOCK(iort_fwnode_lock); +static LIST_HEAD(iort_rmr_list); /* list of RMR regions from ACPI */ + /** * iort_set_fwnode() - Create iort_fwnode and use it to register * iommu data in the iort_fwnode_list @@ -393,7 +395,8 @@ static struct acpi_iort_node *iort_node_get_id(struct acpi_iort_node *node, if (node->type == ACPI_IORT_NODE_NAMED_COMPONENT || node->type == ACPI_IORT_NODE_PCI_ROOT_COMPLEX || node->type == ACPI_IORT_NODE_SMMU_V3 || - node->type == ACPI_IORT_NODE_PMCG) { + node->type == ACPI_IORT_NODE_PMCG || + node->type == ACPI_IORT_NODE_RMR) { *id_out = map->output_base; return parent; } @@ -1574,6 +1577,134 @@ static void __init iort_enable_acs(struct acpi_iort_node *iort_node) #else static inline void iort_enable_acs(struct acpi_iort_node *iort_node) { } #endif +static void iort_rmr_desc_check_overlap(struct acpi_iort_rmr_desc *desc, u32 count) +{ + int i, j; + + for (i = 0; i < count; i++) { + u64 end, start = desc[i].base_address, length = desc[i].length; + + end = start + length - 1; + + /* Check for address overlap */ + for (j = i + 1; j < count; j++) { + u64 e_start = desc[j].base_address; + u64 e_end = e_start + desc[j].length - 1; + + if (start <= e_end && end >= e_start) + pr_err(FW_BUG "RMR descriptor[0x%llx - 0x%llx] overlaps, continue anyway\n", + start, end); + } + } +} + +static void __init iort_node_get_rmr_info(struct acpi_iort_node *iort_node) +{ + struct acpi_iort_node *smmu; + struct acpi_iort_rmr *rmr; + struct acpi_iort_rmr_desc *rmr_desc; + u32 map_count = iort_node->mapping_count; + u32 sid; + int i; + + if (!iort_node->mapping_offset || map_count != 1) { + pr_err(FW_BUG "Invalid ID mapping, skipping RMR node %p\n", + iort_node); + return; + } + + /* Retrieve associated smmu and stream id */ + smmu = iort_node_get_id(iort_node, &sid, 0); + if (!smmu) { + pr_err(FW_BUG "Invalid SMMU reference, skipping RMR node %p\n", + iort_node); + return; + } + + /* Retrieve RMR data */ + rmr = (struct acpi_iort_rmr *)iort_node->node_data; + if (!rmr->rmr_offset || !rmr->rmr_count) { + pr_err(FW_BUG "Invalid RMR descriptor array, skipping RMR node %p\n", + iort_node); + return; + } + + rmr_desc = ACPI_ADD_PTR(struct acpi_iort_rmr_desc, iort_node, + rmr->rmr_offset); + + iort_rmr_desc_check_overlap(rmr_desc, rmr->rmr_count); + + for (i = 0; i < rmr->rmr_count; i++, rmr_desc++) { + struct iommu_resv_region *region; + enum iommu_resv_type type; + int prot = IOMMU_READ | IOMMU_WRITE; + u64 addr = rmr_desc->base_address, size = rmr_desc->length; + + if (!IS_ALIGNED(addr, SZ_64K) || !IS_ALIGNED(size, SZ_64K)) { + /* PAGE align base addr and size */ + addr &= PAGE_MASK; + size = PAGE_ALIGN(size + offset_in_page(rmr_desc->base_address)); + + pr_err(FW_BUG "RMR descriptor[0x%llx - 0x%llx] not aligned to 64K, continue with [0x%llx - 0x%llx]\n", + rmr_desc->base_address, + rmr_desc->base_address + rmr_desc->length - 1, + addr, addr + size - 1); + } + if (rmr->flags & IOMMU_RMR_REMAP_PERMITTED) { + type = IOMMU_RESV_DIRECT_RELAXABLE; + /* + * Set IOMMU_CACHE as IOMMU_RESV_DIRECT_RELAXABLE is + * normally used for allocated system memory that is + * then used for device specific reserved regions. + */ + prot |= IOMMU_CACHE; + } else { + type = IOMMU_RESV_DIRECT; + /* + * Set IOMMU_MMIO as IOMMU_RESV_DIRECT is normally used + * for device memory like MSI doorbell. + */ + prot |= IOMMU_MMIO; + } + + region = iommu_alloc_resv_region(addr, size, prot, type); + if (region) { + region->fw_data.rmr.flags = rmr->flags; + region->fw_data.rmr.sid = sid; + region->fw_data.rmr.smmu = smmu; + list_add_tail(®ion->list, &iort_rmr_list); + } + } +} + +static void __init iort_parse_rmr(void) +{ + struct acpi_iort_node *iort_node, *iort_end; + struct acpi_table_iort *iort; + int i; + + if (iort_table->revision < 3) + return; + + iort = (struct acpi_table_iort *)iort_table; + + iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort, + iort->node_offset); + iort_end = ACPI_ADD_PTR(struct acpi_iort_node, iort, + iort_table->length); + + for (i = 0; i < iort->node_count; i++) { + if (WARN_TAINT(iort_node >= iort_end, TAINT_FIRMWARE_WORKAROUND, + "IORT node pointer overflows, bad table!\n")) + return; + + if (iort_node->type == ACPI_IORT_NODE_RMR) + iort_node_get_rmr_info(iort_node); + + iort_node = ACPI_ADD_PTR(struct acpi_iort_node, iort_node, + iort_node->length); + } +} static void __init iort_init_platform_devices(void) { @@ -1644,6 +1775,7 @@ void __init acpi_iort_init(void) } iort_init_platform_devices(); + iort_parse_rmr(); } #ifdef CONFIG_ZONE_DMA -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Thu, 5 Aug 2021 09:07:18 +0100 Subject: [PATCH 03/19] iommu/dma: Introduce generic helper to retrieve RMR info Reserved Memory Regions(RMR) associated with an IOMMU can be described through ACPI IORT tables in systems with devices that require a unity mapping or bypass for those regions. Introduce a generic interface so that IOMMU drivers can retrieve and set up necessary mappings. Signed-off-by: Shameer Kolothum --- drivers/iommu/dma-iommu.c | 29 +++++++++++++++++++++++++++++ include/linux/dma-iommu.h | 13 +++++++++++++ 2 files changed, 42 insertions(+) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 2d6021644000..b49651349efb 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -174,6 +174,35 @@ void iommu_put_dma_cookie(struct iommu_domain *domain) } EXPORT_SYMBOL(iommu_put_dma_cookie); +/** + * + * iommu_dma_get_rmrs - Retrieve Reserved Memory Regions(RMRs) associated + * with a given IOMMU + * @iommu_fwnode: fwnode associated with IOMMU + * @list: RMR list to be populated + * + */ +int iommu_dma_get_rmrs(struct fwnode_handle *iommu_fwnode, + struct list_head *list) +{ + return -EINVAL; +} +EXPORT_SYMBOL(iommu_dma_get_rmrs); + +/** + * + * iommu_dma_put_rmrs - Release Reserved Memory Regions(RMRs) associated + * with a given IOMMU + * @iommu_fwnode: fwnode associated with IOMMU + * @list: RMR list + * + */ +void iommu_dma_put_rmrs(struct fwnode_handle *iommu_fwnode, + struct list_head *list) +{ +} +EXPORT_SYMBOL(iommu_dma_put_rmrs); + /** * iommu_dma_get_resv_regions - Reserved region driver helper * @dev: Device from iommu_get_resv_regions() diff --git a/include/linux/dma-iommu.h b/include/linux/dma-iommu.h index 24607dc3c2ac..7579c014e274 100644 --- a/include/linux/dma-iommu.h +++ b/include/linux/dma-iommu.h @@ -43,12 +43,16 @@ void iommu_dma_free_cpu_cached_iovas(unsigned int cpu, extern bool iommu_dma_forcedac; +int iommu_dma_get_rmrs(struct fwnode_handle *iommu, struct list_head *list); +void iommu_dma_put_rmrs(struct fwnode_handle *iommu, struct list_head *list); + #else /* CONFIG_IOMMU_DMA */ struct iommu_domain; struct msi_desc; struct msi_msg; struct device; +struct fwnode_handle; static inline void iommu_setup_dma_ops(struct device *dev, u64 dma_base, u64 dma_limit) @@ -89,5 +93,14 @@ static inline void iommu_dma_get_resv_regions(struct device *dev, struct list_he { } +static int iommu_dma_get_rmrs(struct fwnode_handle *iommu, struct list_head *list) +{ + return -ENODEV; +} + +static void iommu_dma_put_rmrs(struct fwnode_handle *iommu, struct list_head *list) +{ +} + #endif /* CONFIG_IOMMU_DMA */ #endif /* __DMA_IOMMU_H */ -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Thu, 5 Aug 2021 09:07:19 +0100 Subject: [PATCH 04/19] ACPI/IORT: Add a helper to retrieve RMR memory regions Add a helper function (iort_iommu_get_rmrs()) that retrieves RMR memory descriptors associated with a given IOMMU. This will be used by IOMMU drivers to setup necessary mappings. Invoke it from the generic helper iommu_dma_get_rmrs(). Signed-off-by: Shameer Kolothum --- drivers/acpi/arm64/iort.c | 38 ++++++++++++++++++++++++++++++++++++++ drivers/iommu/dma-iommu.c | 4 ++++ include/linux/acpi_iort.h | 7 +++++++ 3 files changed, 49 insertions(+) diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c index 7df83d80819b..66d200e577cb 100644 --- a/drivers/acpi/arm64/iort.c +++ b/drivers/acpi/arm64/iort.c @@ -809,6 +809,42 @@ static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev) return NULL; } +/** + * iort_iommu_get_rmrs() - Helper to retrieve RMR info associated with IOMMU + * @iommu_fwnode: fwnode for the IOMMU + * @head: RMR list head to be populated + * + * Returns: 0 on success, <0 failure. Please note, we will keep the already + * allocated RMR reserve regions in case of a kmemdup() + * failure. + */ +int iort_iommu_get_rmrs(struct fwnode_handle *iommu_fwnode, + struct list_head *head) +{ + struct iommu_resv_region *e; + struct acpi_iort_node *iommu; + int rmrs = 0; + + iommu = iort_get_iort_node(iommu_fwnode); + if (!iommu || list_empty(&iort_rmr_list)) + return -ENODEV; + + list_for_each_entry(e, &iort_rmr_list, list) { + struct iommu_resv_region *region; + + if (e->fw_data.rmr.smmu != iommu) + continue; + + region = kmemdup(e, sizeof(*region), GFP_KERNEL); + if (region) { + list_add_tail(®ion->list, head); + rmrs++; + } + } + + return (rmrs == 0) ? -ENODEV : 0; +} + /** * iort_iommu_msi_get_resv_regions - Reserved region driver helper * @dev: Device from iommu_get_resv_regions() @@ -1041,6 +1077,8 @@ int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) { return 0; } int iort_iommu_configure_id(struct device *dev, const u32 *input_id) { return -ENODEV; } +int iort_iommu_get_rmrs(struct fwnode_handle *fwnode, struct list_head *head) +{ return -ENODEV; } #endif static int nc_dma_get_range(struct device *dev, u64 *size) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index b49651349efb..9e27978ce111 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -185,6 +185,9 @@ EXPORT_SYMBOL(iommu_put_dma_cookie); int iommu_dma_get_rmrs(struct fwnode_handle *iommu_fwnode, struct list_head *list) { + if (!is_of_node(iommu_fwnode)) + return iort_iommu_get_rmrs(iommu_fwnode, list); + return -EINVAL; } EXPORT_SYMBOL(iommu_dma_get_rmrs); @@ -200,6 +203,7 @@ EXPORT_SYMBOL(iommu_dma_get_rmrs); void iommu_dma_put_rmrs(struct fwnode_handle *iommu_fwnode, struct list_head *list) { + generic_iommu_put_resv_regions(iommu_fwnode->dev, list); } EXPORT_SYMBOL(iommu_dma_put_rmrs); diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h index f1f0842a2cb2..d8c030c103f5 100644 --- a/include/linux/acpi_iort.h +++ b/include/linux/acpi_iort.h @@ -38,6 +38,8 @@ int iort_dma_get_ranges(struct device *dev, u64 *size); int iort_iommu_configure_id(struct device *dev, const u32 *id_in); int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head); phys_addr_t acpi_iort_dma_get_max_cpu_address(void); +int iort_iommu_get_rmrs(struct fwnode_handle *iommu_fwnode, + struct list_head *list); #else static inline void acpi_iort_init(void) { } static inline u32 iort_msi_map_id(struct device *dev, u32 id) @@ -57,6 +59,11 @@ int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head) static inline phys_addr_t acpi_iort_dma_get_max_cpu_address(void) { return PHYS_ADDR_MAX; } + +static inline +int iort_iommu_get_rmrs(struct fwnode_handle *iommu_fwnode, + struct list_head *list) +{ return -ENODEV; } #endif #endif /* __ACPI_IORT_H__ */ -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Thu, 5 Aug 2021 09:07:20 +0100 Subject: [PATCH 05/19] iommu/arm-smmu-v3: Introduce strtab init helper Introduce a helper to check the sid range and to init the l2 strtab entries(bypass). This will be useful when we have to initialize the l2 strtab with bypass for RMR SIDs. Signed-off-by: Shameer Kolothum --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 28 +++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index a388e318f86e..23acac6d89c7 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -2529,6 +2529,19 @@ static bool arm_smmu_sid_in_range(struct arm_smmu_device *smmu, u32 sid) return sid < limit; } +static int arm_smmu_init_sid_strtab(struct arm_smmu_device *smmu, u32 sid) +{ + /* Check the SIDs are in range of the SMMU and our stream table */ + if (!arm_smmu_sid_in_range(smmu, sid)) + return -ERANGE; + + /* Ensure l2 strtab is initialised */ + if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) + return arm_smmu_init_l2_strtab(smmu, sid); + + return 0; +} + static int arm_smmu_insert_master(struct arm_smmu_device *smmu, struct arm_smmu_master *master) { @@ -2552,20 +2565,9 @@ static int arm_smmu_insert_master(struct arm_smmu_device *smmu, new_stream->id = sid; new_stream->master = master; - /* - * Check the SIDs are in range of the SMMU and our stream table - */ - if (!arm_smmu_sid_in_range(smmu, sid)) { - ret = -ERANGE; + ret = arm_smmu_init_sid_strtab(smmu, sid); + if (ret) break; - } - - /* Ensure l2 strtab is initialised */ - if (smmu->features & ARM_SMMU_FEAT_2_LVL_STRTAB) { - ret = arm_smmu_init_l2_strtab(smmu, sid); - if (ret) - break; - } /* Insert into SID tree */ new_node = &(smmu->streams.rb_node); -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Thu, 5 Aug 2021 09:07:21 +0100 Subject: [PATCH 06/19] =?UTF-8?q?iommu/arm-smmu-v3:=20Refactor=C2=A0arm=5F?= =?UTF-8?q?smmu=5Finit=5Fbypass=5Fstes()=20to=20force=20bypass?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default, disable_bypass flag is set and any dev without an iommu domain installs STE with CFG_ABORT during arm_smmu_init_bypass_stes(). Introduce a "force" flag and move the STE update logic to arm_smmu_init_bypass_stes() so that we can force it to install CFG_BYPASS STE for specific SIDs. This will be useful in follow-up patch to install bypass for IORT RMR SIDs. Signed-off-by: Shameer Kolothum --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index 23acac6d89c7..12b5c9677df8 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -1374,12 +1374,21 @@ static void arm_smmu_write_strtab_ent(struct arm_smmu_master *master, u32 sid, arm_smmu_cmdq_issue_cmd(smmu, &prefetch_cmd); } -static void arm_smmu_init_bypass_stes(__le64 *strtab, unsigned int nent) +static void arm_smmu_init_bypass_stes(__le64 *strtab, unsigned int nent, bool force) { unsigned int i; + u64 val = STRTAB_STE_0_V; + + if (disable_bypass && !force) + val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_ABORT); + else + val |= FIELD_PREP(STRTAB_STE_0_CFG, STRTAB_STE_0_CFG_BYPASS); for (i = 0; i < nent; ++i) { - arm_smmu_write_strtab_ent(NULL, -1, strtab); + strtab[0] = cpu_to_le64(val); + strtab[1] = cpu_to_le64(FIELD_PREP(STRTAB_STE_1_SHCFG, + STRTAB_STE_1_SHCFG_INCOMING)); + strtab[2] = 0; strtab += STRTAB_STE_DWORDS; } } @@ -1407,7 +1416,7 @@ static int arm_smmu_init_l2_strtab(struct arm_smmu_device *smmu, u32 sid) return -ENOMEM; } - arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT); + arm_smmu_init_bypass_stes(desc->l2ptr, 1 << STRTAB_SPLIT, false); arm_smmu_write_strtab_l1_desc(strtab, desc); return 0; } @@ -3053,7 +3062,7 @@ static int arm_smmu_init_strtab_linear(struct arm_smmu_device *smmu) reg |= FIELD_PREP(STRTAB_BASE_CFG_LOG2SIZE, smmu->sid_bits); cfg->strtab_base_cfg = reg; - arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents); + arm_smmu_init_bypass_stes(strtab, cfg->num_l1_ents, false); return 0; } -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Thu, 5 Aug 2021 09:07:22 +0100 Subject: [PATCH 07/19] iommu/arm-smmu-v3: Get associated RMR info and install bypass STE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check if there is any RMR info associated with the devices behind the SMMUv3 and if any, install bypass STEs for them. This is to keep any ongoing traffic associated with these devices alive when we enable/reset SMMUv3 during probe(). Signed-off-by: Shameer Kolothum --- drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 31 +++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c index 12b5c9677df8..22fa2900ad44 100644 --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c @@ -3769,6 +3769,34 @@ static void __iomem *arm_smmu_ioremap(struct device *dev, resource_size_t start, return devm_ioremap_resource(dev, &res); } +static void arm_smmu_rmr_install_bypass_ste(struct arm_smmu_device *smmu) +{ + struct list_head rmr_list; + struct iommu_resv_region *e; + int ret; + + INIT_LIST_HEAD(&rmr_list); + if (iommu_dma_get_rmrs(dev_fwnode(smmu->dev), &rmr_list)) + return; + + list_for_each_entry(e, &rmr_list, list) { + __le64 *step; + u32 sid = e->fw_data.rmr.sid; + + ret = arm_smmu_init_sid_strtab(smmu, sid); + if (ret) { + dev_err(smmu->dev, "RMR SID(0x%x) bypass failed\n", + sid); + continue; + } + + step = arm_smmu_get_step_for_sid(smmu, sid); + arm_smmu_init_bypass_stes(step, 1, true); + } + + iommu_dma_put_rmrs(dev_fwnode(smmu->dev), &rmr_list); +} + static int arm_smmu_device_probe(struct platform_device *pdev) { int irq, ret; @@ -3850,6 +3878,9 @@ static int arm_smmu_device_probe(struct platform_device *pdev) /* Record our private device structure */ platform_set_drvdata(pdev, smmu); + /* Check for RMRs and install bypass STEs if any */ + arm_smmu_rmr_install_bypass_ste(smmu); + /* Reset the device */ ret = arm_smmu_device_reset(smmu, bypass); if (ret) -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Thu, 5 Aug 2021 09:07:23 +0100 Subject: [PATCH 08/19] iommu/arm-smmu: Get associated RMR info and install bypass SMR Check if there is any RMR info associated with the devices behind the SMMU and if any, install bypass SMRs for them. This is to keep any ongoing traffic associated with these devices alive when we enable/reset SMMU during probe(). Signed-off-by: Jon Nettleton Signed-off-by: Steven Price Signed-off-by: Shameer Kolothum --- drivers/iommu/arm/arm-smmu/arm-smmu.c | 48 +++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/drivers/iommu/arm/arm-smmu/arm-smmu.c b/drivers/iommu/arm/arm-smmu/arm-smmu.c index 4bc75c4ce402..6c6b0b97756a 100644 --- a/drivers/iommu/arm/arm-smmu/arm-smmu.c +++ b/drivers/iommu/arm/arm-smmu/arm-smmu.c @@ -2066,6 +2066,50 @@ err_reset_platform_ops: __maybe_unused; return err; } +static void arm_smmu_rmr_install_bypass_smr(struct arm_smmu_device *smmu) +{ + struct list_head rmr_list; + struct iommu_resv_region *e; + int i, cnt = 0; + u32 reg; + + INIT_LIST_HEAD(&rmr_list); + if (iommu_dma_get_rmrs(dev_fwnode(smmu->dev), &rmr_list)) + return; + + /* + * Rather than trying to look at existing mappings that + * are setup by the firmware and then invalidate the ones + * that do no have matching RMR entries, just disable the + * SMMU until it gets enabled again in the reset routine. + */ + reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sCR0); + reg |= ARM_SMMU_sCR0_CLIENTPD; + arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sCR0, reg); + + list_for_each_entry(e, &rmr_list, list) { + u32 sid = e->fw_data.rmr.sid; + + i = arm_smmu_find_sme(smmu, sid, ~0); + if (i < 0) + continue; + if (smmu->s2crs[i].count == 0) { + smmu->smrs[i].id = sid; + smmu->smrs[i].mask = 0; + smmu->smrs[i].valid = true; + } + smmu->s2crs[i].count++; + smmu->s2crs[i].type = S2CR_TYPE_BYPASS; + smmu->s2crs[i].privcfg = S2CR_PRIVCFG_DEFAULT; + + cnt++; + } + + dev_notice(smmu->dev, "\tpreserved %d boot mapping%s\n", cnt, + cnt == 1 ? "" : "s"); + iommu_dma_put_rmrs(dev_fwnode(smmu->dev), &rmr_list); +} + static int arm_smmu_device_probe(struct platform_device *pdev) { struct resource *res; @@ -2192,6 +2236,10 @@ static int arm_smmu_device_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, smmu); + + /* Check for RMRs and install bypass SMRs if any */ + arm_smmu_rmr_install_bypass_smr(smmu); + arm_smmu_device_reset(smmu); arm_smmu_test_smr_masks(smmu); -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Thu, 5 Aug 2021 09:07:24 +0100 Subject: [PATCH 09/19] iommu/dma: Reserve any RMR regions associated with a dev Get ACPI IORT RMR regions associated with a dev reserved so that there is a unity mapping for them in SMMU. Signed-off-by: Shameer Kolothum --- drivers/iommu/dma-iommu.c | 56 +++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 9e27978ce111..7164acaafcbd 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -207,22 +207,68 @@ void iommu_dma_put_rmrs(struct fwnode_handle *iommu_fwnode, } EXPORT_SYMBOL(iommu_dma_put_rmrs); +static bool iommu_dma_dev_has_rmr(struct iommu_fwspec *fwspec, + struct iommu_resv_region *e) +{ + int i; + + for (i = 0; i < fwspec->num_ids; i++) { + if (e->fw_data.rmr.sid == fwspec->ids[i]) + return true; + } + + return false; +} + +static void iommu_dma_get_rmr_resv_regions(struct device *dev, + struct list_head *list) +{ + struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev); + struct list_head rmr_list; + struct iommu_resv_region *rmr, *tmp; + + INIT_LIST_HEAD(&rmr_list); + if (iommu_dma_get_rmrs(fwspec->iommu_fwnode, &rmr_list)) + return; + + if (dev_is_pci(dev)) { + struct pci_dev *pdev = to_pci_dev(dev); + struct pci_host_bridge *host = pci_find_host_bridge(pdev->bus); + + if (!host->preserve_config) + return; + } + + list_for_each_entry_safe(rmr, tmp, &rmr_list, list) { + if (!iommu_dma_dev_has_rmr(fwspec, rmr)) + continue; + + /* Remove from iommu RMR list and add to dev resv_regions */ + list_del_init(&rmr->list); + list_add_tail(&rmr->list, list); + } + + iommu_dma_put_rmrs(fwspec->iommu_fwnode, &rmr_list); +} + /** * iommu_dma_get_resv_regions - Reserved region driver helper * @dev: Device from iommu_get_resv_regions() * @list: Reserved region list from iommu_get_resv_regions() * * IOMMU drivers can use this to implement their .get_resv_regions callback - * for general non-IOMMU-specific reservations. Currently, this covers GICv3 - * ITS region reservation on ACPI based ARM platforms that may require HW MSI - * reservation. + * for general non-IOMMU-specific reservations. Currently this covers, + * -GICv3 ITS region reservation on ACPI based ARM platforms that may + * require HW MSI reservation. + * -Any ACPI IORT RMR memory range reservations (IORT spec rev E.b) */ void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list) { - if (!is_of_node(dev_iommu_fwspec_get(dev)->iommu_fwnode)) + if (!is_of_node(dev_iommu_fwspec_get(dev)->iommu_fwnode)) { iort_iommu_msi_get_resv_regions(dev, list); - + iommu_dma_get_rmr_resv_regions(dev, list); + } } EXPORT_SYMBOL(iommu_dma_get_resv_regions); -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Fri, 17 Sep 2021 12:07:26 +0100 Subject: [PATCH 10/19] iommu/dma: Update RMR mem attributes Since we dont have enough information from the IORT spec, make use of ACPI table and EFI memory map to set the RMR reserved region prot value. [Not tested] Signed-off-by: Shameer Kolothum --- drivers/acpi/arm64/iort.c | 24 +++++++++++++----------- drivers/iommu/dma-iommu.c | 1 + include/linux/acpi_iort.h | 8 ++++++++ 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/drivers/acpi/arm64/iort.c b/drivers/acpi/arm64/iort.c index 66d200e577cb..c397c980a7f4 100644 --- a/drivers/acpi/arm64/iort.c +++ b/drivers/acpi/arm64/iort.c @@ -809,6 +809,16 @@ static struct acpi_iort_node *iort_get_msi_resv_iommu(struct device *dev) return NULL; } +void iort_iommu_rmr_update_mem_attr(struct device *dev, + struct iommu_resv_region *rmr) +{ + if (device_get_dma_attr(dev) == DEV_DMA_COHERENT) + rmr->prot |= IOMMU_CACHE; + + if (efi_mem_type(rmr->start) == EFI_MEMORY_MAPPED_IO) + rmr->prot |= IOMMU_MMIO; +} + /** * iort_iommu_get_rmrs() - Helper to retrieve RMR info associated with IOMMU * @iommu_fwnode: fwnode for the IOMMU @@ -1079,6 +1089,9 @@ int iort_iommu_configure_id(struct device *dev, const u32 *input_id) { return -ENODEV; } int iort_iommu_get_rmrs(struct fwnode_handle *fwnode, struct list_head *head) { return -ENODEV; } +void iort_iommu_rmr_update_mem_attr(struct device *dev, + struct iommu_resv_region *rmr) +{ } #endif static int nc_dma_get_range(struct device *dev, u64 *size) @@ -1690,19 +1703,8 @@ static void __init iort_node_get_rmr_info(struct acpi_iort_node *iort_node) } if (rmr->flags & IOMMU_RMR_REMAP_PERMITTED) { type = IOMMU_RESV_DIRECT_RELAXABLE; - /* - * Set IOMMU_CACHE as IOMMU_RESV_DIRECT_RELAXABLE is - * normally used for allocated system memory that is - * then used for device specific reserved regions. - */ - prot |= IOMMU_CACHE; } else { type = IOMMU_RESV_DIRECT; - /* - * Set IOMMU_MMIO as IOMMU_RESV_DIRECT is normally used - * for device memory like MSI doorbell. - */ - prot |= IOMMU_MMIO; } region = iommu_alloc_resv_region(addr, size, prot, type); diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c index 7164acaafcbd..a406c374be71 100644 --- a/drivers/iommu/dma-iommu.c +++ b/drivers/iommu/dma-iommu.c @@ -243,6 +243,7 @@ static void iommu_dma_get_rmr_resv_regions(struct device *dev, if (!iommu_dma_dev_has_rmr(fwspec, rmr)) continue; + iort_iommu_rmr_update_mem_attr(dev, rmr); /* Remove from iommu RMR list and add to dev resv_regions */ list_del_init(&rmr->list); list_add_tail(&rmr->list, list); diff --git a/include/linux/acpi_iort.h b/include/linux/acpi_iort.h index d8c030c103f5..f0a3882c26d4 100644 --- a/include/linux/acpi_iort.h +++ b/include/linux/acpi_iort.h @@ -10,6 +10,7 @@ #include #include #include +#include #define IORT_IRQ_MASK(irq) (irq & 0xffffffffULL) #define IORT_IRQ_TRIGGER_MASK(irq) ((irq >> 32) & 0xffffffffULL) @@ -40,6 +41,8 @@ int iort_iommu_msi_get_resv_regions(struct device *dev, struct list_head *head); phys_addr_t acpi_iort_dma_get_max_cpu_address(void); int iort_iommu_get_rmrs(struct fwnode_handle *iommu_fwnode, struct list_head *list); +void iort_iommu_rmr_update_mem_attr(struct device *dev, + struct iommu_resv_region *rmr); #else static inline void acpi_iort_init(void) { } static inline u32 iort_msi_map_id(struct device *dev, u32 id) @@ -64,6 +67,11 @@ static inline int iort_iommu_get_rmrs(struct fwnode_handle *iommu_fwnode, struct list_head *list) { return -ENODEV; } + +static inline +void iort_iommu_rmr_update_mem_attr(struct device *dev, + struct iommu_resv_region *rmr) +{ } #endif #endif /* __ACPI_IORT_H__ */ -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Makarand Pawagi Date: Tue, 21 Apr 2020 11:25:53 +0530 Subject: [PATCH 11/19] soc: fsl: enable acpi support for Guts driver ACPI support is added in the Guts driver This is in accordance with the DSDT table added for Guts Signed-off-by: Makarand Pawagi --- drivers/soc/fsl/guts.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c index 75eabfb916cb..b080721eace7 100644 --- a/drivers/soc/fsl/guts.c +++ b/drivers/soc/fsl/guts.c @@ -3,6 +3,7 @@ * Freescale QorIQ Platforms GUTS Driver * * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2020 NXP */ #include @@ -149,7 +150,8 @@ static int fsl_guts_probe(struct platform_device *pdev) if (!guts) return -ENOMEM; - guts->little_endian = of_property_read_bool(np, "little-endian"); + guts->little_endian = fwnode_property_read_bool(pdev->dev.fwnode, + "little-endian"); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); guts->regs = devm_ioremap_resource(dev, res); @@ -157,17 +159,23 @@ static int fsl_guts_probe(struct platform_device *pdev) return PTR_ERR(guts->regs); /* Register soc device */ - root = of_find_node_by_path("/"); - if (of_property_read_string(root, "model", &machine)) - of_property_read_string_index(root, "compatible", 0, &machine); + if (dev_of_node(&pdev->dev)) { + root = of_find_node_by_path("/"); + if (of_property_read_string(root, "model", &machine)) + of_property_read_string_index(root, + "compatible", 0, &machine); + of_node_put(root); + } else { + fwnode_property_read_string(pdev->dev.fwnode, + "model", &machine); + } + if (machine) { soc_dev_attr.machine = devm_kstrdup(dev, machine, GFP_KERNEL); if (!soc_dev_attr.machine) { - of_node_put(root); return -ENOMEM; } } - of_node_put(root); svr = fsl_guts_get_svr(); soc_die = fsl_soc_die_match(svr, fsl_soc_die); @@ -238,10 +246,17 @@ static const struct of_device_id fsl_guts_of_match[] = { }; MODULE_DEVICE_TABLE(of, fsl_guts_of_match); +static const struct acpi_device_id fsl_guts_acpi_match[] = { + {"NXP0030", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, fsl_guts_acpi_match); + static struct platform_driver fsl_guts_driver = { .driver = { .name = "fsl-guts", .of_match_table = fsl_guts_of_match, + .acpi_match_table = fsl_guts_acpi_match, }, .probe = fsl_guts_probe, .remove = fsl_guts_remove, -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Meenakshi Aggarwal Date: Wed, 27 May 2020 21:35:11 +0530 Subject: [PATCH 12/19] mmc: sdhci-of-esdhc: Add ACPI support This patch is to add acpi support in esdhc controller driver Signed-off-by: Meenakshi Aggarwal --- drivers/mmc/host/sdhci-of-esdhc.c | 62 +++++++++++++++++++------------ 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 0f3658b36513..c11544f6047b 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -10,6 +10,7 @@ * Anton Vorontsov */ +#include #include #include #include @@ -73,6 +74,14 @@ static const struct of_device_id sdhci_esdhc_of_match[] = { }; MODULE_DEVICE_TABLE(of, sdhci_esdhc_of_match); +#ifdef CONFIG_ACPI +static const struct acpi_device_id sdhci_esdhc_ids[] = { + {"NXP0003" }, + { } +}; +MODULE_DEVICE_TABLE(acpi, sdhci_esdhc_ids); +#endif + struct sdhci_esdhc { u8 vendor_ver; u8 spec_ver; @@ -1370,29 +1379,35 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) esdhc->clk_fixup = match->data; np = pdev->dev.of_node; - if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { - esdhc->quirk_delay_before_data_reset = true; - esdhc->quirk_trans_complete_erratum = true; - } + /* in case of device tree, get clock from framework */ + if (np) { + if (of_device_is_compatible(np, "fsl,p2020-esdhc")) { + esdhc->quirk_delay_before_data_reset = true; + esdhc->quirk_trans_complete_erratum = true; + } - clk = of_clk_get(np, 0); - if (!IS_ERR(clk)) { - /* - * esdhc->peripheral_clock would be assigned with a value - * which is eSDHC base clock when use periperal clock. - * For some platforms, the clock value got by common clk - * API is peripheral clock while the eSDHC base clock is - * 1/2 peripheral clock. - */ - if (of_device_is_compatible(np, "fsl,ls1046a-esdhc") || - of_device_is_compatible(np, "fsl,ls1028a-esdhc") || - of_device_is_compatible(np, "fsl,ls1088a-esdhc")) - esdhc->peripheral_clock = clk_get_rate(clk) / 2; - else - esdhc->peripheral_clock = clk_get_rate(clk); - - clk_put(clk); - } + clk = of_clk_get(np, 0); + if (!IS_ERR(clk)) { + /* + * esdhc->peripheral_clock would be assigned with a value + * which is eSDHC base clock when use periperal clock. + * For some platforms, the clock value got by common clk + * API is peripheral clock while the eSDHC base clock is + * 1/2 peripheral clock. + */ + if (of_device_is_compatible(np, "fsl,ls1046a-esdhc") || + of_device_is_compatible(np, "fsl,ls1028a-esdhc") || + of_device_is_compatible(np, "fsl,ls1088a-esdhc")) + esdhc->peripheral_clock = clk_get_rate(clk) / 2; + else + esdhc->peripheral_clock = clk_get_rate(clk); + + clk_put(clk); + } + } else { + device_property_read_u32(&pdev->dev, "clock-frequency", + &esdhc->peripheral_clock); + } esdhc_clock_enable(host, false); val = sdhci_readl(host, ESDHC_DMA_SYSCTL); @@ -1425,7 +1440,7 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) np = pdev->dev.of_node; - if (of_property_read_bool(np, "little-endian")) + if (device_property_read_bool(&pdev->dev, "little-endian")) host = sdhci_pltfm_init(pdev, &sdhci_esdhc_le_pdata, sizeof(struct sdhci_esdhc)); else @@ -1510,6 +1525,7 @@ static struct platform_driver sdhci_esdhc_driver = { .name = "sdhci-esdhc", .probe_type = PROBE_PREFER_ASYNCHRONOUS, .of_match_table = sdhci_esdhc_of_match, + .acpi_match_table = sdhci_esdhc_ids, .pm = &esdhc_of_dev_pm_ops, }, .probe = sdhci_esdhc_probe, -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Meharbaan Date: Tue, 28 Jul 2020 17:41:31 +0530 Subject: [PATCH 13/19] drivers/mmc/host/sdhci-of-esdhc : Fix DMA coherent check in ACPI mode. DMA-coherent check to set ESDHC_DMA_SNOOP mask was bypassed when booted in ACPI mode. Now it also checks the acpi device and its parents for _CCA property in the device, and sets the flag accordingly. Signed-off-by: Meharbaan --- drivers/base/property.c | 38 +++++++++++++++++++++++++++++++ drivers/mmc/host/sdhci-of-esdhc.c | 7 ++++-- include/linux/property.h | 2 ++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/drivers/base/property.c b/drivers/base/property.c index 4c77837769c6..f478a0d10634 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -17,6 +17,7 @@ #include #include #include +#include struct fwnode_handle *dev_fwnode(struct device *dev) { @@ -893,6 +894,43 @@ enum dev_dma_attr device_get_dma_attr(struct device *dev) } EXPORT_SYMBOL_GPL(device_get_dma_attr); +/** + * device_match_fw_node - Check if the device is the parent node. + * @dev: Pointer to the device. + * @parent_fwnode Pointer to the parent's firmware node. + * + * The function returns true if the device has no parent. + * + */ +static int device_match_fw_node(struct device *dev, const void *parent_fwnode) +{ + return dev->fwnode == parent_fwnode; +} + +/** + * dev_dma_is_coherent - Check if the device or any of its parents has + * dma support enabled. + * @dev: Pointer to the device. + * + * The function gets the device pointer and check for device_dma_supported() + * on the device pointer passed and then recursively on its parent nodes. + */ + +bool dev_dma_is_coherent(struct device *dev) +{ + struct fwnode_handle *parent_fwnode; + + while (dev) { + if (device_dma_supported(dev)) + return true; + parent_fwnode = fwnode_get_next_parent(dev->fwnode); + dev = bus_find_device(&platform_bus_type, NULL, parent_fwnode, + device_match_fw_node); + } + return false; +} +EXPORT_SYMBOL_GPL(dev_dma_is_coherent); + /** * fwnode_get_phy_mode - Get phy mode for given firmware node * @fwnode: Pointer to the given node diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index c11544f6047b..6e67bff51454 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -545,8 +545,11 @@ static int esdhc_of_enable_dma(struct sdhci_host *host) } value = sdhci_readl(host, ESDHC_DMA_SYSCTL); - - if (of_dma_is_coherent(dev->of_node)) + /* + * of_dma_is_coherent() returns false in case of acpi hence + * dev_dma_is_coherent() is used along with it. + */ + if (of_dma_is_coherent(dev->of_node) || dev_dma_is_coherent(dev)) value |= ESDHC_DMA_SNOOP; else value &= ~ESDHC_DMA_SNOOP; diff --git a/include/linux/property.h b/include/linux/property.h index 357513a977e5..a9009883ab9e 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -385,6 +385,8 @@ bool device_dma_supported(struct device *dev); enum dev_dma_attr device_get_dma_attr(struct device *dev); +bool dev_dma_is_coherent(struct device *dev); + const void *device_get_match_data(struct device *dev); int device_get_phy_mode(struct device *dev); -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Fri, 2 Jul 2021 07:28:21 -0400 Subject: [PATCH 14/19] ACPI: APD: Allow apd device to override fixed_clk_rate Currently by default the apd drivers are always using the fixed_clk_rate assigned in the matched acpi_device_desc. This causes an issue on the LX2160a platform because the NXP0001 settings do not match the platform and instead the I2C bus is only running at 24000kHZ rather than the expect 100000. Instead of patching the source with more static numbers that may or may not change instead add a check for the device property "fixed-clock-rate" that can be added to the ACPI tables to instruct the driver what rate to use. I have chosen fixed-clock-rate because clock-frequency is already used by I2C devices in acpi and device-tree to specify by bus speed, and fixed-clock-rate matches the fixed_clk_rate used by the apd_device_desc. If this device property is not set then the default static values are used so this should cause no regressions. Signed-off-by: Jon Nettleton --- drivers/acpi/acpi_apd.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/acpi_apd.c b/drivers/acpi/acpi_apd.c index 6e02448d15d9..f79757c34a77 100644 --- a/drivers/acpi/acpi_apd.c +++ b/drivers/acpi/acpi_apd.c @@ -46,12 +46,21 @@ struct apd_private_data { static int acpi_apd_setup(struct apd_private_data *pdata) { const struct apd_device_desc *dev_desc = pdata->dev_desc; + struct acpi_device *adev = pdata->adev; + const union acpi_object *obj; + unsigned int fixed_clk_rate; struct clk *clk; - if (dev_desc->fixed_clk_rate) { + if (!acpi_dev_get_property(adev, "uefi-clock-frequency", ACPI_TYPE_INTEGER, &obj)) { + fixed_clk_rate = obj->integer.value; + } else if (dev_desc->fixed_clk_rate) { + fixed_clk_rate = dev_desc->fixed_clk_rate; + } + + if (fixed_clk_rate) { clk = clk_register_fixed_rate(&pdata->adev->dev, dev_name(&pdata->adev->dev), - NULL, 0, dev_desc->fixed_clk_rate); + NULL, 0, fixed_clk_rate); clk_register_clkdev(clk, NULL, dev_name(&pdata->adev->dev)); pdata->clk = clk; } -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 24 Dec 2019 14:46:48 +0000 Subject: [PATCH 15/19] bus: fsl-mc: fix dprc object reading race When modifying the objects attached to a DPRC, we may end up reading the list of objects from the firmware while another thread is changing changing the list. Since we read the objects via: - Read the number of DPRC objects - Iterate over this number of objects retrieving their details and objects can be added in the middle of the list, this causes the last few objects to unexpectedly disappear. The side effect of this is if network interfaces are added after boot, they come and go. This can result in already configured interfaces unexpectedly disappearing. This has been easy to provoke with the restool interface added, and a script which adds network interfaces one after each other; the kernel rescanning runs asynchronously to restool. NXP's approach to fixing this was to introduce a sysfs "attribute" in their vendor tree, /sys/bus/fsl-mc/rescan, which userspace poked at to request the kernel to rescan the DPRC object tree each time the "restool" command completed (whether or not the tool changed anything.) This has the effect of making the kernel's rescan synchronous with a scripted restool, but still fails if we have multiple restools running concurrently. This patch takes a different approach: - Read the number of DPRC objects - Iterate over this number of objects retrieving their details - Re-read the number of DPRC objects - If the number of DPRC objects has changed while reading, repeat. This solves the issue where network interfaces unexpectedly disappear while adding others via ls-addni, because they've fallen off the end of the object list. This does *not* solve the issue that if an object is deleted while another is added while we are reading the objects - that requires firmware modification, or a more elaborate solution on the Linux side (e.g., CRCing the object details and reading all objects at least twice to check the CRC is stable.) However, without firmware modification, this is probably the best way to ensure that we read all the objects. Signed-off-by: Russell King --- drivers/bus/fsl-mc/dprc-driver.c | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/drivers/bus/fsl-mc/dprc-driver.c b/drivers/bus/fsl-mc/dprc-driver.c index 315e830b6ecd..2268869bf6ab 100644 --- a/drivers/bus/fsl-mc/dprc-driver.c +++ b/drivers/bus/fsl-mc/dprc-driver.c @@ -240,11 +240,11 @@ static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev, int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, bool alloc_interrupts) { - int num_child_objects; + int num_child_objects, num_child_objects2; int dprc_get_obj_failures; int error; - unsigned int irq_count = mc_bus_dev->obj_desc.irq_count; - struct fsl_mc_obj_desc *child_obj_desc_array = NULL; + unsigned int irq_count; + struct fsl_mc_obj_desc *child_obj_desc_array; struct fsl_mc_bus *mc_bus = to_fsl_mc_bus(mc_bus_dev); error = dprc_get_obj_count(mc_bus_dev->mc_io, @@ -257,6 +257,9 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, return error; } +retry: + irq_count = mc_bus_dev->obj_desc.irq_count; + child_obj_desc_array = NULL; if (num_child_objects != 0) { int i; @@ -315,6 +318,29 @@ int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev, } } + error = dprc_get_obj_count(mc_bus_dev->mc_io, + 0, + mc_bus_dev->mc_handle, + &num_child_objects2); + if (error < 0) { + if (child_obj_desc_array) + devm_kfree(&mc_bus_dev->dev, child_obj_desc_array); + dev_err(&mc_bus_dev->dev, "dprc_get_obj_count() failed: %d\n", + error); + return error; + } + + if (num_child_objects != num_child_objects2) { + /* + * Something changed while reading the number of objects. + * Retry reading the child object list. + */ + if (child_obj_desc_array) + devm_kfree(&mc_bus_dev->dev, child_obj_desc_array); + num_child_objects = num_child_objects2; + goto retry; + } + /* * Allocate IRQ's before binding the scanned devices with their * respective drivers. -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 24 Jan 2020 17:59:49 +0000 Subject: [PATCH 16/19] iommu: silence iommu group prints On the LX2160A, there are lots (about 160) of IOMMU messages produced during boot; this is excessive. Reduce the severity of these messages to debug level. Signed-off-by: Russell King --- drivers/iommu/iommu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 7f409e9eea4b..2dc9592ff309 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -905,7 +905,7 @@ int iommu_group_add_device(struct iommu_group *group, struct device *dev) trace_add_device_to_group(group->id, dev); - dev_info(dev, "Adding to iommu group %d\n", group->id); + dev_dbg(dev, "Adding to iommu group %d\n", group->id); return 0; @@ -942,7 +942,7 @@ void iommu_group_remove_device(struct device *dev) if (!group) return; - dev_info(dev, "Removing from iommu group %d\n", group->id); + dev_dbg(dev, "Removing from iommu group %d\n", group->id); /* Pre-notify listeners that a device is being removed. */ blocking_notifier_call_chain(&group->notifier, -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Diana Craciun Date: Wed, 22 Sep 2021 14:05:29 +0300 Subject: [PATCH 17/19] bus/fsl-mc: Add generic implementation for open/reset/close commands The open/reset/close commands format is similar for all objects. Currently there are multiple implementations for these commands scattered through various drivers. The code is cavsi-identical. Create a generic implementation for the open/reset/close commands. One of the consumer will be the VFIO driver which needs to be able to reset a device. Signed-off-by: Diana Craciun Reviewed-by: Laurentiu Tudor Link: https://lore.kernel.org/r/20210922110530.24736-1-diana.craciun@oss.nxp.com Signed-off-by: Alex Williamson --- drivers/bus/fsl-mc/Makefile | 3 +- drivers/bus/fsl-mc/fsl-mc-private.h | 39 +++++++++-- drivers/bus/fsl-mc/obj-api.c | 103 ++++++++++++++++++++++++++++ include/linux/fsl/mc.h | 14 ++++ 4 files changed, 154 insertions(+), 5 deletions(-) create mode 100644 drivers/bus/fsl-mc/obj-api.c diff --git a/drivers/bus/fsl-mc/Makefile b/drivers/bus/fsl-mc/Makefile index 4ae292a30e53..892946245527 100644 --- a/drivers/bus/fsl-mc/Makefile +++ b/drivers/bus/fsl-mc/Makefile @@ -15,7 +15,8 @@ mc-bus-driver-objs := fsl-mc-bus.o \ dprc-driver.o \ fsl-mc-allocator.o \ fsl-mc-msi.o \ - dpmcp.o + dpmcp.o \ + obj-api.o # MC userspace support obj-$(CONFIG_FSL_MC_UAPI_SUPPORT) += fsl-mc-uapi.o diff --git a/drivers/bus/fsl-mc/fsl-mc-private.h b/drivers/bus/fsl-mc/fsl-mc-private.h index 1958fa065360..b3520ea1b9f4 100644 --- a/drivers/bus/fsl-mc/fsl-mc-private.h +++ b/drivers/bus/fsl-mc/fsl-mc-private.h @@ -48,7 +48,6 @@ struct dpmng_rsp_get_version { /* DPMCP command IDs */ #define DPMCP_CMDID_CLOSE DPMCP_CMD(0x800) -#define DPMCP_CMDID_OPEN DPMCP_CMD(0x80b) #define DPMCP_CMDID_RESET DPMCP_CMD(0x005) struct dpmcp_cmd_open { @@ -91,7 +90,6 @@ int dpmcp_reset(struct fsl_mc_io *mc_io, /* DPRC command IDs */ #define DPRC_CMDID_CLOSE DPRC_CMD(0x800) -#define DPRC_CMDID_OPEN DPRC_CMD(0x805) #define DPRC_CMDID_GET_API_VERSION DPRC_CMD(0xa05) #define DPRC_CMDID_GET_ATTR DPRC_CMD(0x004) @@ -453,7 +451,6 @@ int dprc_get_connection(struct fsl_mc_io *mc_io, /* Command IDs */ #define DPBP_CMDID_CLOSE DPBP_CMD(0x800) -#define DPBP_CMDID_OPEN DPBP_CMD(0x804) #define DPBP_CMDID_ENABLE DPBP_CMD(0x002) #define DPBP_CMDID_DISABLE DPBP_CMD(0x003) @@ -492,7 +489,6 @@ struct dpbp_rsp_get_attributes { /* Command IDs */ #define DPCON_CMDID_CLOSE DPCON_CMD(0x800) -#define DPCON_CMDID_OPEN DPCON_CMD(0x808) #define DPCON_CMDID_ENABLE DPCON_CMD(0x002) #define DPCON_CMDID_DISABLE DPCON_CMD(0x003) @@ -524,6 +520,41 @@ struct dpcon_cmd_set_notification { __le64 user_ctx; }; +/* + * Generic FSL MC API + */ + +/* generic command versioning */ +#define OBJ_CMD_BASE_VERSION 1 +#define OBJ_CMD_ID_OFFSET 4 + +#define OBJ_CMD(id) (((id) << OBJ_CMD_ID_OFFSET) | OBJ_CMD_BASE_VERSION) + +/* open command codes */ +#define DPRTC_CMDID_OPEN OBJ_CMD(0x810) +#define DPNI_CMDID_OPEN OBJ_CMD(0x801) +#define DPSW_CMDID_OPEN OBJ_CMD(0x802) +#define DPIO_CMDID_OPEN OBJ_CMD(0x803) +#define DPBP_CMDID_OPEN OBJ_CMD(0x804) +#define DPRC_CMDID_OPEN OBJ_CMD(0x805) +#define DPDMUX_CMDID_OPEN OBJ_CMD(0x806) +#define DPCI_CMDID_OPEN OBJ_CMD(0x807) +#define DPCON_CMDID_OPEN OBJ_CMD(0x808) +#define DPSECI_CMDID_OPEN OBJ_CMD(0x809) +#define DPAIOP_CMDID_OPEN OBJ_CMD(0x80a) +#define DPMCP_CMDID_OPEN OBJ_CMD(0x80b) +#define DPMAC_CMDID_OPEN OBJ_CMD(0x80c) +#define DPDCEI_CMDID_OPEN OBJ_CMD(0x80d) +#define DPDMAI_CMDID_OPEN OBJ_CMD(0x80e) +#define DPDBG_CMDID_OPEN OBJ_CMD(0x80f) + +/* Generic object command IDs */ +#define OBJ_CMDID_CLOSE OBJ_CMD(0x800) +#define OBJ_CMDID_RESET OBJ_CMD(0x005) + +struct fsl_mc_obj_cmd_open { + __le32 obj_id; +}; /** * struct fsl_mc_resource_pool - Pool of MC resources of a given diff --git a/drivers/bus/fsl-mc/obj-api.c b/drivers/bus/fsl-mc/obj-api.c new file mode 100644 index 000000000000..06c1dd84e38d --- /dev/null +++ b/drivers/bus/fsl-mc/obj-api.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) +/* + * Copyright 2021 NXP + * + */ +#include +#include + +#include "fsl-mc-private.h" + +static int fsl_mc_get_open_cmd_id(const char *type) +{ + static const struct { + int cmd_id; + const char *type; + } dev_ids[] = { + { DPRTC_CMDID_OPEN, "dprtc" }, + { DPRC_CMDID_OPEN, "dprc" }, + { DPNI_CMDID_OPEN, "dpni" }, + { DPIO_CMDID_OPEN, "dpio" }, + { DPSW_CMDID_OPEN, "dpsw" }, + { DPBP_CMDID_OPEN, "dpbp" }, + { DPCON_CMDID_OPEN, "dpcon" }, + { DPMCP_CMDID_OPEN, "dpmcp" }, + { DPMAC_CMDID_OPEN, "dpmac" }, + { DPSECI_CMDID_OPEN, "dpseci" }, + { DPDMUX_CMDID_OPEN, "dpdmux" }, + { DPDCEI_CMDID_OPEN, "dpdcei" }, + { DPAIOP_CMDID_OPEN, "dpaiop" }, + { DPCI_CMDID_OPEN, "dpci" }, + { DPDMAI_CMDID_OPEN, "dpdmai" }, + { DPDBG_CMDID_OPEN, "dpdbg" }, + { 0, NULL } + }; + int i; + + for (i = 0; dev_ids[i].type; i++) + if (!strcmp(dev_ids[i].type, type)) + return dev_ids[i].cmd_id; + + return -1; +} + +int fsl_mc_obj_open(struct fsl_mc_io *mc_io, + u32 cmd_flags, + int obj_id, + char *obj_type, + u16 *token) +{ + struct fsl_mc_command cmd = { 0 }; + struct fsl_mc_obj_cmd_open *cmd_params; + int err = 0; + int cmd_id = fsl_mc_get_open_cmd_id(obj_type); + + if (cmd_id == -1) + return -ENODEV; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(cmd_id, cmd_flags, 0); + cmd_params = (struct fsl_mc_obj_cmd_open *)cmd.params; + cmd_params->obj_id = cpu_to_le32(obj_id); + + /* send command to mc*/ + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + /* retrieve response parameters */ + *token = mc_cmd_hdr_read_token(&cmd); + + return err; +} +EXPORT_SYMBOL_GPL(fsl_mc_obj_open); + +int fsl_mc_obj_close(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token) +{ + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(OBJ_CMDID_CLOSE, cmd_flags, + token); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} +EXPORT_SYMBOL_GPL(fsl_mc_obj_close); + +int fsl_mc_obj_reset(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token) +{ + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(OBJ_CMDID_RESET, cmd_flags, + token); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} +EXPORT_SYMBOL_GPL(fsl_mc_obj_reset); diff --git a/include/linux/fsl/mc.h b/include/linux/fsl/mc.h index 30ece3ae6df7..e026f6c48b49 100644 --- a/include/linux/fsl/mc.h +++ b/include/linux/fsl/mc.h @@ -620,6 +620,20 @@ int dpcon_reset(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token); +int fsl_mc_obj_open(struct fsl_mc_io *mc_io, + u32 cmd_flags, + int obj_id, + char *obj_type, + u16 *token); + +int fsl_mc_obj_close(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token); + +int fsl_mc_obj_reset(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token); + /** * struct dpcon_attr - Structure representing DPCON attributes * @id: DPCON object ID -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Diana Craciun Date: Wed, 22 Sep 2021 14:05:30 +0300 Subject: [PATCH 18/19] vfio/fsl-mc: Add per device reset support Currently when a fsl-mc device is reset, the entire DPRC container is reset which is very inefficient because the devices within a container will be reset multiple times. Add support for individually resetting a device. Signed-off-by: Diana Craciun Reviewed-by: Laurentiu Tudor Link: https://lore.kernel.org/r/20210922110530.24736-2-diana.craciun@oss.nxp.com Signed-off-by: Alex Williamson --- drivers/vfio/fsl-mc/vfio_fsl_mc.c | 45 ++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/drivers/vfio/fsl-mc/vfio_fsl_mc.c b/drivers/vfio/fsl-mc/vfio_fsl_mc.c index 0ead91bfa838..6d7b2d2571a2 100644 --- a/drivers/vfio/fsl-mc/vfio_fsl_mc.c +++ b/drivers/vfio/fsl-mc/vfio_fsl_mc.c @@ -65,6 +65,34 @@ static void vfio_fsl_mc_regions_cleanup(struct vfio_fsl_mc_device *vdev) kfree(vdev->regions); } +static int vfio_fsl_mc_reset_device(struct vfio_fsl_mc_device *vdev) +{ + struct fsl_mc_device *mc_dev = vdev->mc_dev; + int ret = 0; + + if (is_fsl_mc_bus_dprc(vdev->mc_dev)) { + return dprc_reset_container(mc_dev->mc_io, 0, + mc_dev->mc_handle, + mc_dev->obj_desc.id, + DPRC_RESET_OPTION_NON_RECURSIVE); + } else { + u16 token; + + ret = fsl_mc_obj_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, + mc_dev->obj_desc.type, + &token); + if (ret) + goto out; + ret = fsl_mc_obj_reset(mc_dev->mc_io, 0, token); + if (ret) { + fsl_mc_obj_close(mc_dev->mc_io, 0, token); + goto out; + } + ret = fsl_mc_obj_close(mc_dev->mc_io, 0, token); + } +out: + return ret; +} static void vfio_fsl_mc_close_device(struct vfio_device *core_vdev) { @@ -78,9 +106,7 @@ static void vfio_fsl_mc_close_device(struct vfio_device *core_vdev) vfio_fsl_mc_regions_cleanup(vdev); /* reset the device before cleaning up the interrupts */ - ret = dprc_reset_container(mc_cont->mc_io, 0, mc_cont->mc_handle, - mc_cont->obj_desc.id, - DPRC_RESET_OPTION_NON_RECURSIVE); + ret = vfio_fsl_mc_reset_device(vdev); if (WARN_ON(ret)) dev_warn(&mc_cont->dev, @@ -203,18 +229,7 @@ static long vfio_fsl_mc_ioctl(struct vfio_device *core_vdev, } case VFIO_DEVICE_RESET: { - int ret; - struct fsl_mc_device *mc_dev = vdev->mc_dev; - - /* reset is supported only for the DPRC */ - if (!is_fsl_mc_bus_dprc(mc_dev)) - return -ENOTTY; - - ret = dprc_reset_container(mc_dev->mc_io, 0, - mc_dev->mc_handle, - mc_dev->obj_desc.id, - DPRC_RESET_OPTION_NON_RECURSIVE); - return ret; + return vfio_fsl_mc_reset_device(vdev); } default: -- 2.18.4 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Jon Nettleton Date: Mon, 20 Dec 2021 12:49:27 +0100 Subject: [PATCH 19/19] bus: fsl-mc: list more commands as accepted through the ioctl This adds the commands needed to use the DCE engine from userspace. It includes the generic reset,enable,disable as well as DPDCEI_* ioctls. Signed-off-by: Jon Nettleton --- drivers/bus/fsl-mc/fsl-mc-uapi.c | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/bus/fsl-mc/fsl-mc-uapi.c b/drivers/bus/fsl-mc/fsl-mc-uapi.c index 9c4c1395fcdb..0b8733f0f189 100644 --- a/drivers/bus/fsl-mc/fsl-mc-uapi.c +++ b/drivers/bus/fsl-mc/fsl-mc-uapi.c @@ -61,6 +61,9 @@ enum fsl_mc_cmd_index { DPNI_GET_STATISTICS, DPNI_GET_LINK_STATE, DPNI_GET_MAX_FRAME_LENGTH, + DPDCEI_CMDID_SET_RX_QUEUE, + DPDCEI_CMDID_GET_RX_QUEUE, + DPDCEI_CMDID_GET_TX_QUEUE, DPSW_GET_TAILDROP, DPSW_SET_TAILDROP, DPSW_IF_GET_COUNTER, @@ -71,6 +74,9 @@ enum fsl_mc_cmd_index { GET_IRQ_MASK, GET_IRQ_STATUS, CLOSE, + RESET, + ENABLE, + DISABLE, OPEN, GET_API_VERSION, DESTROY, @@ -311,6 +317,24 @@ static struct fsl_mc_cmd_desc fsl_mc_accepted_cmds[] = { .token = true, .size = 10, }, + [DPDCEI_CMDID_SET_RX_QUEUE] = { + .cmdid_value = 0x1b00, + .cmdid_mask = 0xFFF0, + .token = true, + .size = 8, + }, + [DPDCEI_CMDID_GET_RX_QUEUE] = { + .cmdid_value = 0x1b10, + .cmdid_mask = 0xFFF0, + .token = true, + .size = 8, + }, + [DPDCEI_CMDID_GET_TX_QUEUE] = { + .cmdid_value = 0x1b20, + .cmdid_mask = 0xFFF0, + .token = true, + .size = 8, + }, [GET_ATTR] = { .cmdid_value = 0x0040, .cmdid_mask = 0xFFF0, @@ -335,6 +359,24 @@ static struct fsl_mc_cmd_desc fsl_mc_accepted_cmds[] = { .token = true, .size = 8, }, + [RESET] = { + .cmdid_value = 0x0050, + .cmdid_mask = 0xFFF0, + .token = true, + .size = 8, + }, + [ENABLE] = { + .cmdid_value = 0x0020, + .cmdid_mask = 0xFFF0, + .token = true, + .size = 8, + }, + [DISABLE] = { + .cmdid_value = 0x0030, + .cmdid_mask = 0xFFF0, + .token = true, + .size = 8, + }, /* Common commands amongst all types of objects. Must be checked last. */ [OPEN] = { -- 2.18.4