Loading Documentation/devicetree/bindings/arm/msm/clock-pm.txt 0 → 100644 +22 −0 Original line number Diff line number Diff line * clock PM This driver is intended to handle any changes related to power management clocks. Registers with CPU PM notification call back to handle bimc/apss hardware clock gating. Required properties - compatible: "qcom,clock-pm" Optional properties - clocks: clock identifers used by clock driver while looking up apss clocks. - clock-names: name of the clock used by the driver. [Example dts] qcom,clock-pm { compatible = "qcom,clock-pm"; clocks = <&clock_gcc clk_apss_axi_clk_sleep>, <&clock_gcc clk_bimc_apss_axi_clk_sleep>; clock-names = "apss_axi", "bimc_apss_axi"; }; drivers/clk/msm/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ obj-y += clock-pll.o obj-y += clock-alpha-pll.o obj-y += clock-rpm.o obj-y += clock-voter.o obj-y += clock-pm.o obj-$(CONFIG_MSM_CLK_CONTROLLER_V2) += msm-clock-controller.o Loading drivers/clk/msm/clock-pm.c 0 → 100644 +163 −0 Original line number Diff line number Diff line /* * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only 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. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/io.h> #include <linux/err.h> #include <linux/clk.h> #include <linux/clk/msm-clk.h> #include <linux/cpu.h> #include <linux/cpu_pm.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/suspend.h> static struct clk **clk; static int clk_cnt; static void clock_axi_sleep_enable(void) { int i, ret; for (i = 0; i < clk_cnt; i++) { ret = clk_enable(clk[i]); if (ret < 0) pr_err("%s(): clk enable failed %d\n", __func__, i); } } static void clock_axi_sleep_disable(void) { int i; for (i = 0; i < clk_cnt; i++) clk_disable(clk[i]); } static int clock_pm_cpu_pm_notify(struct notifier_block *self, unsigned long action, void *v) { switch (action) { case CPU_PM_ENTER: clock_axi_sleep_enable(); break; case CPU_PM_EXIT: clock_axi_sleep_disable(); break; default: break; } return NOTIFY_OK; } static int clock_pm_sys_suspend_noirq(struct device *dev) { clock_axi_sleep_enable(); return 0; } static int clock_pm_sys_resume_noirq(struct device *dev) { clock_axi_sleep_disable(); return 0; } static struct notifier_block clock_pm_cpu_pm_nb = { .notifier_call = clock_pm_cpu_pm_notify, }; static int clock_pm_probe(struct platform_device *pdev) { int i, ret = 0; const char *clk_names; clk_cnt = of_property_count_strings(pdev->dev.of_node, "clock-names"); clk = devm_kzalloc(&pdev->dev, clk_cnt * sizeof(struct clk *), GFP_KERNEL); if (!clk) return -ENOMEM; for (i = 0; i < clk_cnt; i++) { ret = of_property_read_string_index(pdev->dev.of_node, "clock-names", i, &clk_names); if (ret) { pr_err("%s(): Invalid clk name\n", __func__); return ret; } clk[i] = devm_clk_get(&pdev->dev, clk_names); if (IS_ERR(clk[i])) { pr_err("%s(): clk get failed %s\n", __func__, clk_names); return PTR_ERR(clk[i]); } ret = clk_prepare(clk[i]); if (ret < 0) { pr_err("%s(): clk prepare failed %s\n", __func__, clk_names); for (; i > 0; i--) clk_unprepare(clk[i - 1]); return ret; } } cpu_pm_register_notifier(&clock_pm_cpu_pm_nb); return ret; } static int clock_pm_remove(struct platform_device *pdev) { int i; cpu_pm_unregister_notifier(&clock_pm_cpu_pm_nb); for (i = 0; i < clk_cnt; i++) clk_unprepare(clk[i]); return 0; } static const struct dev_pm_ops clock_pm_ops = { .suspend_noirq = clock_pm_sys_suspend_noirq, .resume_noirq = clock_pm_sys_resume_noirq, }; static struct of_device_id clock_pm_match_table[] = { {.compatible = "qcom,clock-pm"}, {} }; static struct platform_driver clock_pm_driver = { .probe = clock_pm_probe, .remove = clock_pm_remove, .driver = { .name = "clock-pm", .pm = &clock_pm_ops, .of_match_table = clock_pm_match_table, .owner = THIS_MODULE, }, }; static int __init clock_pm_init(void) { return platform_driver_register(&clock_pm_driver); } late_initcall(clock_pm_init); Loading
Documentation/devicetree/bindings/arm/msm/clock-pm.txt 0 → 100644 +22 −0 Original line number Diff line number Diff line * clock PM This driver is intended to handle any changes related to power management clocks. Registers with CPU PM notification call back to handle bimc/apss hardware clock gating. Required properties - compatible: "qcom,clock-pm" Optional properties - clocks: clock identifers used by clock driver while looking up apss clocks. - clock-names: name of the clock used by the driver. [Example dts] qcom,clock-pm { compatible = "qcom,clock-pm"; clocks = <&clock_gcc clk_apss_axi_clk_sleep>, <&clock_gcc clk_bimc_apss_axi_clk_sleep>; clock-names = "apss_axi", "bimc_apss_axi"; };
drivers/clk/msm/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ obj-y += clock-pll.o obj-y += clock-alpha-pll.o obj-y += clock-rpm.o obj-y += clock-voter.o obj-y += clock-pm.o obj-$(CONFIG_MSM_CLK_CONTROLLER_V2) += msm-clock-controller.o Loading
drivers/clk/msm/clock-pm.c 0 → 100644 +163 −0 Original line number Diff line number Diff line /* * Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only 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. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/io.h> #include <linux/err.h> #include <linux/clk.h> #include <linux/clk/msm-clk.h> #include <linux/cpu.h> #include <linux/cpu_pm.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/suspend.h> static struct clk **clk; static int clk_cnt; static void clock_axi_sleep_enable(void) { int i, ret; for (i = 0; i < clk_cnt; i++) { ret = clk_enable(clk[i]); if (ret < 0) pr_err("%s(): clk enable failed %d\n", __func__, i); } } static void clock_axi_sleep_disable(void) { int i; for (i = 0; i < clk_cnt; i++) clk_disable(clk[i]); } static int clock_pm_cpu_pm_notify(struct notifier_block *self, unsigned long action, void *v) { switch (action) { case CPU_PM_ENTER: clock_axi_sleep_enable(); break; case CPU_PM_EXIT: clock_axi_sleep_disable(); break; default: break; } return NOTIFY_OK; } static int clock_pm_sys_suspend_noirq(struct device *dev) { clock_axi_sleep_enable(); return 0; } static int clock_pm_sys_resume_noirq(struct device *dev) { clock_axi_sleep_disable(); return 0; } static struct notifier_block clock_pm_cpu_pm_nb = { .notifier_call = clock_pm_cpu_pm_notify, }; static int clock_pm_probe(struct platform_device *pdev) { int i, ret = 0; const char *clk_names; clk_cnt = of_property_count_strings(pdev->dev.of_node, "clock-names"); clk = devm_kzalloc(&pdev->dev, clk_cnt * sizeof(struct clk *), GFP_KERNEL); if (!clk) return -ENOMEM; for (i = 0; i < clk_cnt; i++) { ret = of_property_read_string_index(pdev->dev.of_node, "clock-names", i, &clk_names); if (ret) { pr_err("%s(): Invalid clk name\n", __func__); return ret; } clk[i] = devm_clk_get(&pdev->dev, clk_names); if (IS_ERR(clk[i])) { pr_err("%s(): clk get failed %s\n", __func__, clk_names); return PTR_ERR(clk[i]); } ret = clk_prepare(clk[i]); if (ret < 0) { pr_err("%s(): clk prepare failed %s\n", __func__, clk_names); for (; i > 0; i--) clk_unprepare(clk[i - 1]); return ret; } } cpu_pm_register_notifier(&clock_pm_cpu_pm_nb); return ret; } static int clock_pm_remove(struct platform_device *pdev) { int i; cpu_pm_unregister_notifier(&clock_pm_cpu_pm_nb); for (i = 0; i < clk_cnt; i++) clk_unprepare(clk[i]); return 0; } static const struct dev_pm_ops clock_pm_ops = { .suspend_noirq = clock_pm_sys_suspend_noirq, .resume_noirq = clock_pm_sys_resume_noirq, }; static struct of_device_id clock_pm_match_table[] = { {.compatible = "qcom,clock-pm"}, {} }; static struct platform_driver clock_pm_driver = { .probe = clock_pm_probe, .remove = clock_pm_remove, .driver = { .name = "clock-pm", .pm = &clock_pm_ops, .of_match_table = clock_pm_match_table, .owner = THIS_MODULE, }, }; static int __init clock_pm_init(void) { return platform_driver_register(&clock_pm_driver); } late_initcall(clock_pm_init);