[PATCH 2/4] iommu/arm-smmu: Add pm_runtime/sleep ops
Mathieu Poirier
mathieu.poirier at linaro.org
Mon Oct 24 09:40:57 PDT 2016
On 21 October 2016 at 11:14, Sricharan R <sricharan at codeaurora.org> wrote:
> The smmu needs to be functional when the respective
> master/s using it are active. As there is a device_link
> between the master and smmu, the pm runtime ops for the smmu
> gets invoked in sync with the master's runtime, thus the
> smmu remains powered only when needed.
>
> There are couple of places in the driver during probe,
> master attach, where the smmu needs to be clocked without
> the context of the master. So doing a pm_runtime_get/put sync
> in those places separately.
>
> Signed-off-by: Sricharan R <sricharan at codeaurora.org>
> ---
> drivers/iommu/arm-smmu.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 108 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
> index 083489e4..45f2762 100644
> --- a/drivers/iommu/arm-smmu.c
> +++ b/drivers/iommu/arm-smmu.c
> @@ -45,6 +45,7 @@
> #include <linux/of_iommu.h>
> #include <linux/pci.h>
> #include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> #include <linux/slab.h>
> #include <linux/spinlock.h>
>
> @@ -373,6 +374,8 @@ struct arm_smmu_device {
> u32 num_global_irqs;
> u32 num_context_irqs;
> unsigned int *irqs;
> + int num_clocks;
> + struct clk **clocks;
>
> u32 cavium_id_base; /* Specific to Cavium */
> };
> @@ -430,6 +433,31 @@ static struct arm_smmu_domain *to_smmu_domain(struct iommu_domain *dom)
> return container_of(dom, struct arm_smmu_domain, domain);
> }
>
> +static int arm_smmu_enable_clocks(struct arm_smmu_device *smmu)
> +{
> + int i, ret = 0;
> +
> + for (i = 0; i < smmu->num_clocks; ++i) {
> + ret = clk_prepare_enable(smmu->clocks[i]);
> + if (ret) {
> + dev_err(smmu->dev, "Couldn't enable clock #%d\n", i);
> + while (i--)
> + clk_disable_unprepare(smmu->clocks[i]);
> + break;
> + }
> + }
> +
> + return ret;
> +}
> +
> +static void arm_smmu_disable_clocks(struct arm_smmu_device *smmu)
> +{
> + int i;
> +
> + for (i = 0; i < smmu->num_clocks; ++i)
> + clk_disable_unprepare(smmu->clocks[i]);
> +}
> +
> static void parse_driver_options(struct arm_smmu_device *smmu)
> {
> int i = 0;
> @@ -1187,11 +1215,13 @@ static void arm_smmu_master_free_smes(struct iommu_fwspec *fwspec)
> int i, idx;
>
> mutex_lock(&smmu->stream_map_mutex);
> + pm_runtime_get_sync(smmu->dev);
Since this is generic code it is probably a good idea to check the
return value, same for _put_sync() below.
> for_each_cfg_sme(fwspec, i, idx) {
> if (arm_smmu_free_sme(smmu, idx))
> arm_smmu_write_sme(smmu, idx);
> cfg->smendx[i] = INVALID_SMENDX;
> }
> + pm_runtime_put_sync(smmu->dev);
> mutex_unlock(&smmu->stream_map_mutex);
> }
>
> @@ -1424,9 +1454,11 @@ static int arm_smmu_add_device(struct device *dev)
> while (i--)
> cfg->smendx[i] = INVALID_SMENDX;
>
> + pm_runtime_get_sync(smmu->dev);
> ret = arm_smmu_master_alloc_smes(dev);
> if (ret)
> goto out_free;
> + pm_runtime_put_sync(smmu->dev);
>
> return 0;
>
> @@ -1650,6 +1682,44 @@ static int arm_smmu_id_size_to_bits(int size)
> }
> }
>
> +static int arm_smmu_init_clocks(struct arm_smmu_device *smmu)
> +{
> + const char *cname;
> + struct property *prop;
> + int i;
> + struct device *dev = smmu->dev;
> +
> + smmu->num_clocks =
> + of_property_count_strings(dev->of_node, "clock-names");
> +
> + if (smmu->num_clocks < 1)
> + return 0;
> +
> + smmu->clocks = devm_kzalloc(dev,
> + sizeof(*smmu->clocks) * smmu->num_clocks,
> + GFP_KERNEL);
> +
> + if (!smmu->clocks) {
> + dev_err(dev, "Failed to allocate memory for clocks\n");
> + return -ENODEV;
> + }
> +
> + i = 0;
Please do the initialisation above when you declare the variable.
> + of_property_for_each_string(dev->of_node, "clock-names", prop, cname) {
> + struct clk *c = devm_clk_get(dev, cname);
> +
> + if (IS_ERR(c)) {
> + dev_err(dev, "Couldn't get clock: %s", cname);
> + return -EPROBE_DEFER;
> + }
> +
> + smmu->clocks[i] = c;
> + ++i;
> + }
> +
> + return 0;
> +}
> +
> static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
> {
> unsigned long size;
> @@ -1968,6 +2038,13 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
> smmu->irqs[i] = irq;
> }
>
> + err = arm_smmu_init_clocks(smmu);
> + if (err)
> + return err;
> +
> + platform_set_drvdata(pdev, smmu);
> + pm_runtime_enable(dev);
> + pm_runtime_get_sync(dev);
> err = arm_smmu_device_cfg_probe(smmu);
> if (err)
> return err;
> @@ -1996,8 +2073,8 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
> }
>
> of_iommu_set_ops(dev->of_node, &arm_smmu_ops);
> - platform_set_drvdata(pdev, smmu);
> arm_smmu_device_reset(smmu);
> + pm_runtime_put_sync(dev);
>
> /* Oh, for a proper bus abstraction */
> if (!iommu_present(&platform_bus_type))
> @@ -2027,13 +2104,43 @@ static int arm_smmu_device_remove(struct platform_device *pdev)
>
> /* Turn the thing off */
> writel(sCR0_CLIENTPD, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sCR0);
> +
> + pm_runtime_force_suspend(smmu->dev);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int arm_smmu_resume(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
> +
> + return arm_smmu_enable_clocks(smmu);
> +}
> +
> +static int arm_smmu_suspend(struct device *dev)
> +{
> + struct platform_device *pdev = to_platform_device(dev);
> + struct arm_smmu_device *smmu = platform_get_drvdata(pdev);
> +
> + arm_smmu_disable_clocks(smmu);
> +
> return 0;
> }
> +#endif
> +
> +static const struct dev_pm_ops arm_smmu_pm_ops = {
> + SET_RUNTIME_PM_OPS(arm_smmu_suspend, arm_smmu_resume, NULL)
> + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> + pm_runtime_force_resume)
> +};
>
> static struct platform_driver arm_smmu_driver = {
> .driver = {
> .name = "arm-smmu",
> .of_match_table = of_match_ptr(arm_smmu_of_match),
> + .pm = &arm_smmu_pm_ops,
> },
> .probe = arm_smmu_device_dt_probe,
> .remove = arm_smmu_device_remove,
> --
> QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
More information about the linux-arm-kernel
mailing list