Skip to content

Commit

Permalink
Merge tag 'arm-smmu-updates' of git://git.kernel.org/pub/scm/linux/ke…
Browse files Browse the repository at this point in the history
…rnel/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
  • Loading branch information
joergroedel committed Aug 17, 2023
2 parents 6eaae19 + afe92fb commit 90654da
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 49 deletions.
41 changes: 41 additions & 0 deletions Documentation/devicetree/bindings/iommu/arm,smmu.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
22 changes: 17 additions & 5 deletions Documentation/devicetree/bindings/iommu/qcom,iommu.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -64,13 +69,20 @@ 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

reg:
maxItems: 1

qcom,ctx-asid:
$ref: /schemas/types.yaml#/definitions/uint32
description:
The ASID number associated to the context bank.

required:
- compatible
- interrupts
Expand Down
29 changes: 7 additions & 22 deletions drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 5 additions & 2 deletions drivers/iommu/arm/arm-smmu/arm-smmu-qcom.c
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
{ }
};

Expand Down Expand Up @@ -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 },
Expand Down
68 changes: 49 additions & 19 deletions drivers/iommu/arm/arm-smmu/qcom_iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 |
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -617,23 +630,33 @@ static int qcom_iommu_sec_ptbl_init(struct device *dev)

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:
*/
if (of_property_read_u32_index(np, "reg", 0, &reg))
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)
{
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);
Expand All @@ -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,
Expand All @@ -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;
}
Expand All @@ -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 */ }
};

Expand All @@ -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;
}
Expand All @@ -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);
Expand Down Expand Up @@ -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 */ }
};

Expand Down

0 comments on commit 90654da

Please sign in to comment.