Qualc^WArm SMMU updates for 6.6
- Device-tree binding updates: * Add additional compatible strings for Qualcomm SoCs * Allow ASIDs to be configured in the DT to work around Qualcomm's broken hypervisor * Fix clocks for Qualcomm's MSM8998 SoC - SMMUv2: * Support for Qualcomm's legacy firmware implementation featured on at least MSM8956 and MSM8976. * Match compatible strings for Qualcomm SM6350 and SM6375 SoC variants - SMMUv3: * Use 'ida' instead of a bitmap for VMID allocation -----BEGIN PGP SIGNATURE----- iQFEBAABCgAuFiEEPxTL6PPUbjXGY88ct6xw3ITBYzQFAmTbV6EQHHdpbGxAa2Vy bmVsLm9yZwAKCRC3rHDchMFjNFGECACBId1R3NcchPg6AkCK3S9qugOdA38W3xRt 1yuO5yULXf34EH6jZ7cq9JwQWRzQt1bhnxpzqnDpwSm6e88PRAa99DIy7suxLz/A hJJN0GfwdoT8Be/XUK+g6Gnb7zLI5eifp2qcuWZMSTu3yWY3TCytAWxW4EOhwPQB kOzUaqtM47+HQeXxrCOA7N+l+ebUy2OJoltOUSPgyKOQ5XL4kDMPD82JF8ElwYfX IAT9TcYA/zZ/qv7nre9F7bYKjTWyXMovBA+NjlHY/HhdE8098K9yEBEar+kSXtxE sCZ83rpXM/1CEEk9Qq1AkqMOpuONnsDmPOTa/WLFhA6xC1rXtoz3 =U4Vi -----END PGP SIGNATURE----- Merge tag 'arm-smmu-updates' of git://git.kernel.org/pub/scm/linux/kernel/git/will/linux into arm/smmu Qualc^WArm SMMU updates for 6.6 - Device-tree binding updates: * Add additional compatible strings for Qualcomm SoCs * Allow ASIDs to be configured in the DT to work around Qualcomm's broken hypervisor * Fix clocks for Qualcomm's MSM8998 SoC - SMMUv2: * Support for Qualcomm's legacy firmware implementation featured on at least MSM8956 and MSM8976. * Match compatible strings for Qualcomm SM6350 and SM6375 SoC variants - SMMUv3: * Use 'ida' instead of a bitmap for VMID allocation
This commit is contained in:
commit
90654da4d9
|
@ -270,6 +270,47 @@ allOf:
|
|||
contains:
|
||||
enum:
|
||||
- qcom,msm8998-smmu-v2
|
||||
then:
|
||||
anyOf:
|
||||
- properties:
|
||||
clock-names:
|
||||
items:
|
||||
- const: bus
|
||||
clocks:
|
||||
items:
|
||||
- description: bus clock required for downstream bus access and for
|
||||
the smmu ptw
|
||||
- properties:
|
||||
clock-names:
|
||||
items:
|
||||
- const: iface
|
||||
- const: mem
|
||||
- const: mem_iface
|
||||
clocks:
|
||||
items:
|
||||
- description: interface clock required to access smmu's registers
|
||||
through the TCU's programming interface.
|
||||
- description: bus clock required for memory access
|
||||
- description: bus clock required for GPU memory access
|
||||
- properties:
|
||||
clock-names:
|
||||
items:
|
||||
- const: iface-mm
|
||||
- const: iface-smmu
|
||||
- const: bus-smmu
|
||||
clocks:
|
||||
items:
|
||||
- description: interface clock required to access mnoc's registers
|
||||
through the TCU's programming interface.
|
||||
- description: interface clock required to access smmu's registers
|
||||
through the TCU's programming interface.
|
||||
- description: bus clock required for the smmu ptw
|
||||
|
||||
- if:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- qcom,sdm630-smmu-v2
|
||||
- qcom,sm6375-smmu-v2
|
||||
then:
|
||||
|
|
|
@ -17,11 +17,16 @@ description: |
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- qcom,msm8916-iommu
|
||||
- qcom,msm8953-iommu
|
||||
- const: qcom,msm-iommu-v1
|
||||
oneOf:
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,msm8916-iommu
|
||||
- qcom,msm8953-iommu
|
||||
- const: qcom,msm-iommu-v1
|
||||
- items:
|
||||
- enum:
|
||||
- qcom,msm8976-iommu
|
||||
- const: qcom,msm-iommu-v2
|
||||
|
||||
clocks:
|
||||
items:
|
||||
|
@ -64,6 +69,8 @@ patternProperties:
|
|||
enum:
|
||||
- qcom,msm-iommu-v1-ns
|
||||
- qcom,msm-iommu-v1-sec
|
||||
- qcom,msm-iommu-v2-ns
|
||||
- qcom,msm-iommu-v2-sec
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
@ -71,6 +78,11 @@ patternProperties:
|
|||
reg:
|
||||
maxItems: 1
|
||||
|
||||
qcom,ctx-asid:
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
description:
|
||||
The ASID number associated to the context bank.
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- interrupts
|
||||
|
|
|
@ -2055,24 +2055,6 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
|
|||
return &smmu_domain->domain;
|
||||
}
|
||||
|
||||
static int arm_smmu_bitmap_alloc(unsigned long *map, int span)
|
||||
{
|
||||
int idx, size = 1 << span;
|
||||
|
||||
do {
|
||||
idx = find_first_zero_bit(map, size);
|
||||
if (idx == size)
|
||||
return -ENOSPC;
|
||||
} while (test_and_set_bit(idx, map));
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static void arm_smmu_bitmap_free(unsigned long *map, int idx)
|
||||
{
|
||||
clear_bit(idx, map);
|
||||
}
|
||||
|
||||
static void arm_smmu_domain_free(struct iommu_domain *domain)
|
||||
{
|
||||
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
|
||||
|
@ -2093,7 +2075,7 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
|
|||
} else {
|
||||
struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
|
||||
if (cfg->vmid)
|
||||
arm_smmu_bitmap_free(smmu->vmid_map, cfg->vmid);
|
||||
ida_free(&smmu->vmid_map, cfg->vmid);
|
||||
}
|
||||
|
||||
kfree(smmu_domain);
|
||||
|
@ -2167,7 +2149,9 @@ static int arm_smmu_domain_finalise_s2(struct arm_smmu_domain *smmu_domain,
|
|||
struct arm_smmu_s2_cfg *cfg = &smmu_domain->s2_cfg;
|
||||
typeof(&pgtbl_cfg->arm_lpae_s2_cfg.vtcr) vtcr;
|
||||
|
||||
vmid = arm_smmu_bitmap_alloc(smmu->vmid_map, smmu->vmid_bits);
|
||||
/* Reserve VMID 0 for stage-2 bypass STEs */
|
||||
vmid = ida_alloc_range(&smmu->vmid_map, 1, (1 << smmu->vmid_bits) - 1,
|
||||
GFP_KERNEL);
|
||||
if (vmid < 0)
|
||||
return vmid;
|
||||
|
||||
|
@ -3098,8 +3082,8 @@ static int arm_smmu_init_strtab(struct arm_smmu_device *smmu)
|
|||
reg |= STRTAB_BASE_RA;
|
||||
smmu->strtab_cfg.strtab_base = reg;
|
||||
|
||||
/* Allocate the first VMID for stage-2 bypass STEs */
|
||||
set_bit(0, smmu->vmid_map);
|
||||
ida_init(&smmu->vmid_map);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3923,6 +3907,7 @@ static void arm_smmu_device_remove(struct platform_device *pdev)
|
|||
iommu_device_sysfs_remove(&smmu->iommu);
|
||||
arm_smmu_device_disable(smmu);
|
||||
iopf_queue_free(smmu->evtq.iopf);
|
||||
ida_destroy(&smmu->vmid_map);
|
||||
}
|
||||
|
||||
static void arm_smmu_device_shutdown(struct platform_device *pdev)
|
||||
|
|
|
@ -670,7 +670,7 @@ struct arm_smmu_device {
|
|||
|
||||
#define ARM_SMMU_MAX_VMIDS (1 << 16)
|
||||
unsigned int vmid_bits;
|
||||
DECLARE_BITMAP(vmid_map, ARM_SMMU_MAX_VMIDS);
|
||||
struct ida vmid_map;
|
||||
|
||||
unsigned int ssid_bits;
|
||||
unsigned int sid_bits;
|
||||
|
|
|
@ -251,10 +251,12 @@ static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = {
|
|||
{ .compatible = "qcom,sc7280-mss-pil" },
|
||||
{ .compatible = "qcom,sc8180x-mdss" },
|
||||
{ .compatible = "qcom,sc8280xp-mdss" },
|
||||
{ .compatible = "qcom,sm8150-mdss" },
|
||||
{ .compatible = "qcom,sm8250-mdss" },
|
||||
{ .compatible = "qcom,sdm845-mdss" },
|
||||
{ .compatible = "qcom,sdm845-mss-pil" },
|
||||
{ .compatible = "qcom,sm6350-mdss" },
|
||||
{ .compatible = "qcom,sm6375-mdss" },
|
||||
{ .compatible = "qcom,sm8150-mdss" },
|
||||
{ .compatible = "qcom,sm8250-mdss" },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -528,6 +530,7 @@ static const struct of_device_id __maybe_unused qcom_smmu_impl_of_match[] = {
|
|||
{ .compatible = "qcom,sm6125-smmu-500", .data = &qcom_smmu_500_impl0_data },
|
||||
{ .compatible = "qcom,sm6350-smmu-v2", .data = &qcom_smmu_v2_data },
|
||||
{ .compatible = "qcom,sm6350-smmu-500", .data = &qcom_smmu_500_impl0_data },
|
||||
{ .compatible = "qcom,sm6375-smmu-v2", .data = &qcom_smmu_v2_data },
|
||||
{ .compatible = "qcom,sm6375-smmu-500", .data = &qcom_smmu_500_impl0_data },
|
||||
{ .compatible = "qcom,sm8150-smmu-500", .data = &qcom_smmu_500_impl0_data },
|
||||
{ .compatible = "qcom,sm8250-smmu-500", .data = &qcom_smmu_500_impl0_data },
|
||||
|
|
|
@ -51,14 +51,15 @@ struct qcom_iommu_dev {
|
|||
struct clk_bulk_data clks[CLK_NUM];
|
||||
void __iomem *local_base;
|
||||
u32 sec_id;
|
||||
u8 num_ctxs;
|
||||
struct qcom_iommu_ctx *ctxs[]; /* indexed by asid-1 */
|
||||
u8 max_asid;
|
||||
struct qcom_iommu_ctx *ctxs[]; /* indexed by asid */
|
||||
};
|
||||
|
||||
struct qcom_iommu_ctx {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
bool secure_init;
|
||||
bool secured_ctx;
|
||||
u8 asid; /* asid and ctx bank # are 1:1 */
|
||||
struct iommu_domain *domain;
|
||||
};
|
||||
|
@ -94,7 +95,7 @@ static struct qcom_iommu_ctx * to_ctx(struct qcom_iommu_domain *d, unsigned asid
|
|||
struct qcom_iommu_dev *qcom_iommu = d->iommu;
|
||||
if (!qcom_iommu)
|
||||
return NULL;
|
||||
return qcom_iommu->ctxs[asid - 1];
|
||||
return qcom_iommu->ctxs[asid];
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
@ -273,6 +274,19 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain,
|
|||
ctx->secure_init = true;
|
||||
}
|
||||
|
||||
/* Secured QSMMU-500/QSMMU-v2 contexts cannot be programmed */
|
||||
if (ctx->secured_ctx) {
|
||||
ctx->domain = domain;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Disable context bank before programming */
|
||||
iommu_writel(ctx, ARM_SMMU_CB_SCTLR, 0);
|
||||
|
||||
/* Clear context bank fault address fault status registers */
|
||||
iommu_writel(ctx, ARM_SMMU_CB_FAR, 0);
|
||||
iommu_writel(ctx, ARM_SMMU_CB_FSR, ARM_SMMU_FSR_FAULT);
|
||||
|
||||
/* TTBRs */
|
||||
iommu_writeq(ctx, ARM_SMMU_CB_TTBR0,
|
||||
pgtbl_cfg.arm_lpae_s1_cfg.ttbr |
|
||||
|
@ -527,11 +541,10 @@ static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
|
|||
qcom_iommu = platform_get_drvdata(iommu_pdev);
|
||||
|
||||
/* make sure the asid specified in dt is valid, so we don't have
|
||||
* to sanity check this elsewhere, since 'asid - 1' is used to
|
||||
* index into qcom_iommu->ctxs:
|
||||
* to sanity check this elsewhere:
|
||||
*/
|
||||
if (WARN_ON(asid < 1) ||
|
||||
WARN_ON(asid > qcom_iommu->num_ctxs)) {
|
||||
if (WARN_ON(asid > qcom_iommu->max_asid) ||
|
||||
WARN_ON(qcom_iommu->ctxs[asid] == NULL)) {
|
||||
put_device(&iommu_pdev->dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -617,7 +630,8 @@ free_mem:
|
|||
|
||||
static int get_asid(const struct device_node *np)
|
||||
{
|
||||
u32 reg;
|
||||
u32 reg, val;
|
||||
int asid;
|
||||
|
||||
/* read the "reg" property directly to get the relative address
|
||||
* of the context bank, and calculate the asid from that:
|
||||
|
@ -625,7 +639,17 @@ static int get_asid(const struct device_node *np)
|
|||
if (of_property_read_u32_index(np, "reg", 0, ®))
|
||||
return -ENODEV;
|
||||
|
||||
return reg / 0x1000; /* context banks are 0x1000 apart */
|
||||
/*
|
||||
* Context banks are 0x1000 apart but, in some cases, the ASID
|
||||
* number doesn't match to this logic and needs to be passed
|
||||
* from the DT configuration explicitly.
|
||||
*/
|
||||
if (!of_property_read_u32(np, "qcom,ctx-asid", &val))
|
||||
asid = val;
|
||||
else
|
||||
asid = reg / 0x1000;
|
||||
|
||||
return asid;
|
||||
}
|
||||
|
||||
static int qcom_iommu_ctx_probe(struct platform_device *pdev)
|
||||
|
@ -633,7 +657,6 @@ static int qcom_iommu_ctx_probe(struct platform_device *pdev)
|
|||
struct qcom_iommu_ctx *ctx;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct qcom_iommu_dev *qcom_iommu = dev_get_drvdata(dev->parent);
|
||||
struct resource *res;
|
||||
int ret, irq;
|
||||
|
||||
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
|
||||
|
@ -643,19 +666,22 @@ static int qcom_iommu_ctx_probe(struct platform_device *pdev)
|
|||
ctx->dev = dev;
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ctx->base = devm_ioremap_resource(dev, res);
|
||||
ctx->base = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(ctx->base))
|
||||
return PTR_ERR(ctx->base);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return -ENODEV;
|
||||
return irq;
|
||||
|
||||
if (of_device_is_compatible(dev->of_node, "qcom,msm-iommu-v2-sec"))
|
||||
ctx->secured_ctx = true;
|
||||
|
||||
/* clear IRQs before registering fault handler, just in case the
|
||||
* boot-loader left us a surprise:
|
||||
*/
|
||||
iommu_writel(ctx, ARM_SMMU_CB_FSR, iommu_readl(ctx, ARM_SMMU_CB_FSR));
|
||||
if (!ctx->secured_ctx)
|
||||
iommu_writel(ctx, ARM_SMMU_CB_FSR, iommu_readl(ctx, ARM_SMMU_CB_FSR));
|
||||
|
||||
ret = devm_request_irq(dev, irq,
|
||||
qcom_iommu_fault,
|
||||
|
@ -677,7 +703,7 @@ static int qcom_iommu_ctx_probe(struct platform_device *pdev)
|
|||
|
||||
dev_dbg(dev, "found asid %u\n", ctx->asid);
|
||||
|
||||
qcom_iommu->ctxs[ctx->asid - 1] = ctx;
|
||||
qcom_iommu->ctxs[ctx->asid] = ctx;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -689,12 +715,14 @@ static void qcom_iommu_ctx_remove(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
qcom_iommu->ctxs[ctx->asid - 1] = NULL;
|
||||
qcom_iommu->ctxs[ctx->asid] = NULL;
|
||||
}
|
||||
|
||||
static const struct of_device_id ctx_of_match[] = {
|
||||
{ .compatible = "qcom,msm-iommu-v1-ns" },
|
||||
{ .compatible = "qcom,msm-iommu-v1-sec" },
|
||||
{ .compatible = "qcom,msm-iommu-v2-ns" },
|
||||
{ .compatible = "qcom,msm-iommu-v2-sec" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
@ -712,7 +740,8 @@ static bool qcom_iommu_has_secure_context(struct qcom_iommu_dev *qcom_iommu)
|
|||
struct device_node *child;
|
||||
|
||||
for_each_child_of_node(qcom_iommu->dev->of_node, child) {
|
||||
if (of_device_is_compatible(child, "qcom,msm-iommu-v1-sec")) {
|
||||
if (of_device_is_compatible(child, "qcom,msm-iommu-v1-sec") ||
|
||||
of_device_is_compatible(child, "qcom,msm-iommu-v2-sec")) {
|
||||
of_node_put(child);
|
||||
return true;
|
||||
}
|
||||
|
@ -736,11 +765,11 @@ static int qcom_iommu_device_probe(struct platform_device *pdev)
|
|||
for_each_child_of_node(dev->of_node, child)
|
||||
max_asid = max(max_asid, get_asid(child));
|
||||
|
||||
qcom_iommu = devm_kzalloc(dev, struct_size(qcom_iommu, ctxs, max_asid),
|
||||
qcom_iommu = devm_kzalloc(dev, struct_size(qcom_iommu, ctxs, max_asid + 1),
|
||||
GFP_KERNEL);
|
||||
if (!qcom_iommu)
|
||||
return -ENOMEM;
|
||||
qcom_iommu->num_ctxs = max_asid;
|
||||
qcom_iommu->max_asid = max_asid;
|
||||
qcom_iommu->dev = dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
@ -856,6 +885,7 @@ static const struct dev_pm_ops qcom_iommu_pm_ops = {
|
|||
|
||||
static const struct of_device_id qcom_iommu_of_match[] = {
|
||||
{ .compatible = "qcom,msm-iommu-v1" },
|
||||
{ .compatible = "qcom,msm-iommu-v2" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue