qcom: Add support for GDSCs
Subsystems (5)
v7 cover qcom: Add support for GDSCs
GDSCs (Global Distributed Switch Controllers) control switches that supply power to an on-chip power domain and hence can be programmed in SW to safely power collapse and restore power to the respective PDs. They are part of a considerable number of recent QCOM SoCs (This series adds support for msm8974, msm8916 and apq8084 devices) and are part of the Clock control block.
The series implements support for GDSC using the genpd framework modelling these as SW controllable power domains and uses PM clocks to control clocks within the gdsc.
* Client drivers which plan to use GDSC can refer to Documentation/devicetree/bindings/power/power_domain.txt to know how to hook up the power domain for the device through DT
* Runtime PM specific documentation can be found in Documentation/power/runtime_pm.txt
* Patches are based of 4.2-rc4
Tested with and without CONFIG_PM on db8074, IFC6540 and db410c (arm64)
Changes since v6:
* removed hardcoded con_ids, now part of gdsc struct
* other minor review fixes
Changes since v5:
* Added support to manage clocks with !CONFIG_PM
* Added RET/OFF support
* Added support to assert/deassert reset while the GDSC is ON
Changes since v4:
* Added clock control support using PM clocks
* Included WA to enable an RCG (needed for Oxili gdsc in 8916)
Changes since v3:
* static inline'd gdsc_register/unregister stubs
* error check fixes in gdsc_register
* dropped oxili_gdsc for 8916 as its broken and needs additional WA's not part of this series
* split dts and driver changes into seperate patches
* Updated and included Stephanes patch to add gdscs to apq8084 mmcc in the series
Changes since v2:
* gdsc_unregister added
* gdsc_register/unregister introduced in patch 1/6
Changes since v1:
* added err checks for regmap apis
* added gdsc_register() in gdsc.c
Rajendra Nayak (9):
clk: qcom: gdsc: Prepare common clk probe to register gdscs
clk: qcom: gdsc: Use PM clocks to control gdsc clocks
clk: qcom: gdsc: Manage clocks with !CONFIG_PM
clk: qcom: gdsc: Enable an RCG before turing on the gdsc
clk: qcom: gdsc: Add support for Memory RET/OFF
clk: qcom: gdsc: Add support for ON only state
clk: qcom: gdsc: Add GDSCs in msm8916 GCC
clk: qcom: gdsc: Add GDSCs in apq8084 GCC
arm: dts: qcom: Add #power-domain-cells property
Stephane Viau (1):
clk: qcom: gdsc: Add GDSCs in apq8084 MMCC
Stephen Boyd (3):
clk: qcom: Add support for GDSCs
clk: qcom: gdsc: Add GDSCs in msm8974 GCC
clk: qcom: gdsc: Add GDSCs in msm8974 MMCC
arch/arm/boot/dts/qcom-apq8084.dtsi | 1 + arch/arm/boot/dts/qcom-msm8974.dtsi | 2 + arch/arm64/boot/dts/qcom/msm8916.dtsi | 1 + drivers/clk/qcom/Kconfig | 9 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/common.c | 16 +- drivers/clk/qcom/common.h | 2 + drivers/clk/qcom/gcc-apq8084.c | 46 ++++ drivers/clk/qcom/gcc-msm8916.c | 57 ++++ drivers/clk/qcom/gcc-msm8974.c | 16 ++ drivers/clk/qcom/gdsc.c | 368 ++++++++++++++++++++++++++ drivers/clk/qcom/gdsc.h | 75 ++++++ drivers/clk/qcom/mmcc-apq8084.c | 74 +++++- drivers/clk/qcom/mmcc-msm8974.c | 78 ++++++ include/dt-bindings/clock/qcom,gcc-apq8084.h | 6 + include/dt-bindings/clock/qcom,gcc-msm8916.h | 8 + include/dt-bindings/clock/qcom,gcc-msm8974.h | 3 + include/dt-bindings/clock/qcom,mmcc-apq8084.h | 8 + include/dt-bindings/clock/qcom,mmcc-msm8974.h | 8 + 19 files changed, 777 insertions(+), 2 deletions(-) create mode 100644 drivers/clk/qcom/gdsc.c create mode 100644 drivers/clk/qcom/gdsc.h
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v7 1/13 clk: qcom: Add support for GDSCs
From: Stephen Boyd <[email protected]>
GDSCs (Global Distributed Switch Controllers) are responsible for safely collapsing and restoring power to peripherals in the SoC. These are best modelled as power domains using genpd and given the registers are scattered throughout the clock controller register space, its best to have the support added through the clock driver.
--- drivers/clk/qcom/Kconfig | 4 ++ drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/gdsc.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/clk/qcom/gdsc.h | 46 +++++++++++++ 4 files changed, 222 insertions(+) create mode 100644 drivers/clk/qcom/gdsc.c create mode 100644 drivers/clk/qcom/gdsc.h
drivers/clk/qcom/Kconfig +4 -0
@@ -39,6 +39,10 @@ config IPQ_LCC_806X Say Y if you want to use audio devices such as i2s, pcm, S/PDIF, etc. +config QCOM_GDSC + bool + select PM_GENERIC_DOMAINS if PM + config MSM_GCC_8660 tristate "MSM8660 Global Clock Controller" depends on COMMON_CLK_QCOM
drivers/clk/qcom/Makefile +1 -0
@@ -9,6 +9,7 @@ clk-qcom-y += clk-branch.o clk-qcom-y += clk-regmap-divider.o clk-qcom-y += clk-regmap-mux.o clk-qcom-y += reset.o +clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o
drivers/clk/qcom/gdsc.c new file +171 -0
171 lines not on this page · raw message
drivers/clk/qcom/gdsc.h new file +46 -0
46 lines not on this page · raw message
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v7 1/13 clk: qcom: Add support for GDSCs
On Tue 28 Jul 02:33 PDT 2015, Rajendra Nayak wrote:
[..]
Quoted 1 line
drivers/clk/qcom/Kconfig +0
-0
[..]
Quoted 41 lines
drivers/clk/qcom/Kconfig +41
-0+static int gdsc_init(struct gdsc *sc) +{ + u32 mask, val; + int on, ret; + + /* + * Disable HW trigger: collapse/restore occur based on registers writes. + * Disable SW override: Use hardware state-machine for sequencing. + * Configure wait time between states. + */ + mask = HW_CONTROL_MASK | SW_OVERRIDE_MASK | + EN_REST_WAIT_MASK | EN_FEW_WAIT_MASK | CLK_DIS_WAIT_MASK; + val = EN_REST_WAIT_VAL | EN_FEW_WAIT_VAL | CLK_DIS_WAIT_VAL; + ret = regmap_update_bits(sc->regmap, sc->gdscr, mask, val); + if (ret) + return ret; + + on = gdsc_is_enabled(sc); + if (on < 0) + return on; + + sc->pd.power_off = gdsc_disable; + sc->pd.power_on = gdsc_enable; + pm_genpd_init(&sc->pd, NULL, !on); + + return 0; +} + +int gdsc_register(struct device *dev, struct gdsc **scs, size_t num, + struct regmap *regmap) +{ + int i, ret; + struct genpd_onecell_data *data; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->domains = devm_kcalloc(dev, num, sizeof(*data->domains), + GFP_KERNEL); + if (!data->domains)
drivers/clk/qcom/Kconfig
Quoted 10 lines
drivers/clk/qcom/Kconfig +10
-0+ return -ENOMEM; + + data->num_domains = num; + for (i = 0; i < num; i++) { + if (!scs[i]) + continue; + scs[i]->regmap = regmap; + ret = gdsc_init(scs[i]); + if (ret) + return ret;
By this time we have disabled the HW control and here we just leave it as that, do you expect any interesting consequences of this? Or do we just not care (this shouldn't happen anyways).
Quoted 11 lines
drivers/clk/qcom/Kconfig +10
-0+ data->domains[i] = &scs[i]->pd; + } + + return of_genpd_add_provider_onecell(dev->of_node, data); +} + +void gdsc_unregister(struct device *dev) +{ + of_genpd_del_provider(dev->of_node); +}drivers/clk/qcom/gdsc.h +0
-0
[..]
drivers/clk/qcom/gdsc.h line 41
Quoted 8 lines
drivers/clk/qcom/gdsc.h +8
-0+#ifdef CONFIG_QCOM_GDSC +int gdsc_register (struct device *,struct gdsc **,size_t n,struct regmap *); +void gdsc_unregister (struct device *); +#else +static inline int gdsc_register (struct device *d,struct gdsc **g,size_t n, +struct regmap *r) +{ +return -ENOSYS ;
This will cause qcom_cc_really_probe() to abort and unregister both its resets and clocks. I think you should just pretend that everything went fine and return 0...
drivers/clk/qcom/gdsc.h
Quoted 2 lines
drivers/clk/qcom/gdsc.h +2
-0+} +
Regards,
Bjorn
PATCH v7 1/13 clk: qcom: Add support for GDSCs
[]..
Quoted 19 lines
Quoted 19 lines
drivers/clk/qcom/gdsc.c new file +19
-0+int gdsc_register (struct device *dev,struct gdsc **scs,size_t num, +struct regmap *regmap) +{ +int i, ret; +struct genpd_onecell_data *data; + + data =devm_kzalloc (dev,sizeof (*data),GFP_KERNEL ); +if (!data) +return -ENOMEM ; + + data->domains =devm_kcalloc (dev, num,sizeof (*data->domains), +GFP_KERNEL ); +if (!data->domains) +return -ENOMEM ; + + data->num_domains = num; +for (i =0 ; i < num; i++) { +if (!scs[i]) +continue ;
Quoted 8 lines
drivers/clk/qcom/gdsc.c line 161
Quoted 4 lines
drivers/clk/qcom/gdsc.c new file +4
-0+ scs[i]->regmap = regmap; + ret =gdsc_init (scs[i]); +if (ret) +return ret;By this time we have disabled the HW control and here we just leave it as that, do you expect any interesting consequences of this? Or do we just not care (this shouldn't happen anyways).
The only cases where this could happen are when the regmap apis fail, which ideally should not if the data passed is correct. Besides if regmap apis do end up failing, the one which disables HW control would probably fail as well?
Quoted 13 lines
Quoted 12 lines
drivers/clk/qcom/gdsc.c new file +10
-0+ data->domains[i] = &scs[i]->pd; + } + +return of_genpd_add_provider_onecell (dev->of_node, data); +} + +void gdsc_unregister (struct device *dev) +{ +of_genpd_del_provider (dev->of_node); +}drivers/clk/qcom/gdsc.h +0
-0[..]
Quoted 12 lines
drivers/clk/qcom/gdsc.h line 41
Quoted 8 lines
drivers/clk/qcom/gdsc.h +8
-0+#ifdef CONFIG_QCOM_GDSC +int gdsc_register (struct device *,struct gdsc **,size_t n,struct regmap *); +void gdsc_unregister (struct device *); +#else +static inline int gdsc_register (struct device *d,struct gdsc **g,size_t n, +struct regmap *r) +{ +return -ENOSYS ;This will cause
qcom_cc_really_probe()to abort and unregister both its resets and clocks. I think you should just pretend that everything went fine and return 0...
qcom_cc_really_probe() ends up calling gdsc_register() only in cases there are valid gdscs to be registered..
from drivers/clk/qcom/common.c
--
if (desc->gdscs && desc->num_gdscs)
gdsc_register(...
--
..in which case I guess its fair to expect the platform with valid gdscs to be registered has CONFIG_QCOM_GDSC enabled, and pretending everything went fine might not be the right thing to do?
PATCH v7 1/13 clk: qcom: Add support for GDSCs
In reply to Rajendra Nayakdrivers/clk/qcom/gdsc.c line 27
Quoted 5 lines · Rajendra Nayak
drivers/clk/qcom/gdsc.c new file +5
-0+ +#define PWR_ON_MASK BIT(31) +#define EN_REST_WAIT_MASK GENMASK(23, 20) +#define EN_FEW_WAIT_MASK GENMASK(19, 16) +#define CLK_DIS_WAIT_MASK GENMASK(15, 12)
This trips up some static checker...
drivers/clk/qcom/gdsc.c:234:53: warning: cast truncates bits from constant value (fffffffffff006 becomes fffff006)
So I think we can use GENMASK_ULL here instead.
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v7 1/13 clk: qcom: Add support for GDSCs
Quoted 9 lines · Stephen Boyd
drivers/clk/qcom/gdsc.c line 27
Quoted 5 lines · Rajendra Nayak
drivers/clk/qcom/gdsc.c new file +5
-0+ +#define PWR_ON_MASK BIT(31) +#define EN_REST_WAIT_MASK GENMASK(23, 20) +#define EN_FEW_WAIT_MASK GENMASK(19, 16) +#define CLK_DIS_WAIT_MASK GENMASK(15, 12)This trips up some static checker...
Quoted 4 lines
drivers/clk/qcom/gdsc.c:234:53: warning: cast truncates bits from constant value (fffffffffff006becomesfffff006)So I think we can use
GENMASK_ULLhere instead.
Sure. btw, what static checker is this? I did not see it with sparse.
PATCH v7 1/13 clk: qcom: Add support for GDSCs
Quoted 12 lines · Rajendra Nayak
Quoted 9 lines · Stephen Boyd
drivers/clk/qcom/gdsc.c line 27
Quoted 5 lines · Rajendra Nayak
drivers/clk/qcom/gdsc.c new file +5
-0+ +#define PWR_ON_MASK BIT(31) +#define EN_REST_WAIT_MASK GENMASK(23, 20) +#define EN_FEW_WAIT_MASK GENMASK(19, 16) +#define CLK_DIS_WAIT_MASK GENMASK(15, 12)This trips up some static checker...
Quoted 6 lines
Quoted 4 lines
drivers/clk/qcom/gdsc.c:234:53: warning: cast truncates bits from constant value (fffffffffff006becomesfffff006)So I think we can use
GENMASK_ULLhere instead.Sure. btw, what static checker is this? I did not see it with sparse.
Pretty sure this was with sparse. Which now has me concerned that my sparse is old!
$ sparse --version v0.5.0-47-g0f71312959ed
But I updated to
$ sparse --version v0.5.0-51-ga53cea28f0db
And I still see it.
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v7 2/13 clk: qcom: gdsc: Prepare common clk probe to register gdscs
In reply to Rajendra NayakThe common clk probe registers a clk provider and a reset controller. Update it to register a genpd provider using the gdsc data provided by each platform.
--- drivers/clk/qcom/common.c | 15 ++++++++++++++- drivers/clk/qcom/common.h | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-)
drivers/clk/qcom/common.c +14 -1
@@ -21,6 +21,7 @@#include "clk-rcg.h" #include "clk-regmap.h" #include "reset.h" +#include "gdsc.h" struct qcom_cc {struct qcom_reset_controller reset;
@@ -120,8 +121,19 @@ int qcom_cc_really_probe(struct platform_device *pdev, ret =reset_controller_register (&reset->rcdev);if (ret) -of_clk_del_provider (dev->of_node); + goto err_reset; +if (desc->gdscs && desc->num_gdscs) { + ret =gdsc_register (dev, desc->gdscs, desc->num_gdscs, regmap); +if (ret) + goto err_pd; + } + +return 0 ; +err_pd: +reset_controller_unregister (&reset->rcdev); +err_reset: +of_clk_del_provider (dev->of_node);return ret; }EXPORT_SYMBOL_GPL (qcom_cc_really_probe);
@@ -140,6 +152,7 @@ EXPORT_SYMBOL_GPL(qcom_cc_probe);void qcom_cc_remove (struct platform_device *pdev) { +gdsc_unregister (&pdev->dev);of_clk_del_provider (pdev->dev.of_node);reset_controller_unregister (platform_get_drvdata (pdev)); }
drivers/clk/qcom/common.h +2 -0
@@ -28,6 +28,8 @@ struct qcom_cc_desc {size_t num_clks;const struct qcom_reset_map *resets;size_t num_resets; +struct gdsc **gdscs; +size_t num_gdscs; }; extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f,
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v7 3/13 clk: qcom: gdsc: Use PM clocks to control gdsc clocks
In reply to Rajendra NayakThe devices within a gdsc power domain, quite often have additional clocks to be turned on/off along with the power domain itself. Once the drivers for these devices are converted to use runtime PM, it would be possible to remove all clock handling from the drivers if the gdsc driver can handle it.
Use PM clocks to add support for this. A list of con_ids[] specified per gdsc would be the clocks turned on/off on every device start/stop callbacks.
--- drivers/clk/qcom/gdsc.c | 42 ++++++++++++++++++++++++++++++++++++++++++ drivers/clk/qcom/gdsc.h | 2 ++ 2 files changed, 44 insertions(+)
drivers/clk/qcom/gdsc.c +42 -0
@@ -16,6 +16,7 @@#include <linux/err.h> #include <linux/jiffies.h> #include <linux/kernel.h> +#include <linux/pm_clock.h> #include <linux/pm_domain.h> #include <linux/regmap.h> #include <linux/slab.h>
@@ -108,6 +109,44 @@ static int gdsc_disable(struct generic_pm_domain *domain)return gdsc_toggle_logic (sc, false); } +static int gdsc_attach (struct generic_pm_domain *domain,struct device *dev) +{ +int ret; +struct gdsc *sc =domain_to_gdsc (domain); +const char **con_id; + +if (!sc->con_ids[0 ]) +return 0 ; + + ret =pm_clk_create (dev); +if (ret) { +dev_dbg (dev,"pm_clk_create failed %d\n" , ret); +return ret; + } + +for (con_id = sc->con_ids; *con_id; con_id++) { + ret =pm_clk_add (dev, *con_id); +if (ret) { +dev_dbg (dev,"pm_clk_add failed %d\n" , ret); + goto fail; + } + } +return 0 ; +fail: +pm_clk_destroy (dev); +return ret; +}; + +static void gdsc_detach (struct generic_pm_domain *domain,struct device *dev) +{ +struct gdsc *sc =domain_to_gdsc (domain); + +if (!sc->con_ids[0 ]) +return ; + +pm_clk_destroy (dev); +}; +static int gdsc_init (struct gdsc *sc) {u32 mask, val;
@@ -131,6 +170,9 @@ static int gdsc_init(struct gdsc *sc) sc->pd.power_off = gdsc_disable; sc->pd.power_on = gdsc_enable; + sc->pd.attach_dev = gdsc_attach; + sc->pd.detach_dev = gdsc_detach; + sc->pd.flags =GENPD_FLAG_PM_CLK ;pm_genpd_init (&sc->pd,NULL , !on);return 0 ;
drivers/clk/qcom/gdsc.h +2 -0
@@ -24,11 +24,13 @@ struct regmap;* @pd: generic power domain * @regmap: regmap for MMIO accesses * @gdscr: gsdc control register +* @con_ids: List of clocks to be controlled for the gdsc */ struct gdsc {struct generic_pm_domain pd;struct regmap *regmap;unsigned int gdscr; +const char *con_ids[]; };#ifdef CONFIG_QCOM_GDSC
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v7 4/13 clk: qcom: gdsc: Manage clocks with !CONFIG_PM
In reply to Rajendra NayakWith CONFIG_PM disabled, turn the devices clocks on during driver binding to the device, and turn them off when the driver is unbound from the device.
--- drivers/clk/qcom/gdsc.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+)
drivers/clk/qcom/gdsc.c +71 -0
@@ -12,10 +12,12 @@*/ #include <linux/bitops.h> +#include <linux/clk.h> #include <linux/delay.h> #include <linux/err.h> #include <linux/jiffies.h> #include <linux/kernel.h> +#include <linux/platform_device.h> #include <linux/pm_clock.h> #include <linux/pm_domain.h> #include <linux/regmap.h>
@@ -211,3 +213,72 @@ void gdsc_unregister(struct device *dev) {of_genpd_del_provider (dev->of_node); } + +#ifndef CONFIG_PM +struct gdsc_notifier_block { +struct notifier_block nb; +const char *con_ids[]; +}; + +static void enable_clock (struct device *dev,const char *con_id) +{ +struct clk *clk; + + clk =clk_get (dev, con_id); +if (!IS_ERR (clk)) { +clk_prepare_enable (clk); +clk_put (clk); + } +} + +static void disable_clock (struct device *dev,const char *con_id) +{ +struct clk *clk; + + clk =clk_get (dev, con_id); +if (!IS_ERR (clk)) { +clk_disable_unprepare (clk); +clk_put (clk); + } +} + +static int clk_notify (struct notifier_block *nb,unsigned long action, +void *data) +{ +int sz; +struct device *dev = data; +const char **con_id; +struct gdsc_notifier_block *gdsc_nb; + +if (!of_find_property (dev->of_node,"power-domains" , &sz)) +return 0 ; + + gdsc_nb = container_of(nb, struct gdsc_notifier_block, nb); + +if (!gdsc_nb->con_ids[0 ]) +return 0 ; + +switch (action) { +case BUS_NOTIFY_BIND_DRIVER : +for (con_id = gdsc_nb->con_ids; *con_id; con_id++) +enable_clock (dev, *con_id); +break ; +case BUS_NOTIFY_UNBOUND_DRIVER : +for (con_id = gdsc_nb->con_ids; *con_id; con_id++) +disable_clock (dev, *con_id); +break ; + } +return 0 ; +} + +static struct gdsc_notifier_block gdsc_nb = { + .nb.notifier_call = clk_notify, + .con_ids = {NULL }, +}; + +static __init int qcom_pm_runtime_init (void ) +{ +return bus_register_notifier (&platform_bus_type, &gdsc_nb.nb); +} +core_initcall (qcom_pm_runtime_init); +#endif
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v7 5/13 clk: qcom: gdsc: Enable an RCG before turing on the gdsc
In reply to Rajendra NayakSome gdsc instances require a certain root clock (RCG) to be turned on *before* the power domain itself can be turned on. Handle this as part of the gdsc enable/disable callbacks.
--- drivers/clk/qcom/gdsc.c | 20 +++++++++++++++++++- drivers/clk/qcom/gdsc.h | 5 +++++ 2 files changed, 24 insertions(+), 1 deletion(-)
drivers/clk/qcom/gdsc.c +19 -1
@@ -89,6 +89,9 @@ static int gdsc_enable(struct generic_pm_domain *domain)struct gdsc *sc =domain_to_gdsc (domain);int ret; +if (sc->root_clk) +clk_prepare_enable (sc->root_clk); + ret =gdsc_toggle_logic (sc, true);if (ret)return ret;
@@ -106,9 +109,15 @@ static int gdsc_enable(struct generic_pm_domain *domain)static int gdsc_disable (struct generic_pm_domain *domain) { +int ret;struct gdsc *sc =domain_to_gdsc (domain); -return gdsc_toggle_logic (sc, false); + ret =gdsc_toggle_logic (sc, false); + +if (sc->root_clk) +clk_disable_unprepare (sc->root_clk); + +return ret; }static int gdsc_attach (struct generic_pm_domain *domain,struct device *dev)
@@ -133,6 +142,15 @@ static int gdsc_attach(struct generic_pm_domain *domain, struct device *dev) goto fail; } } + +if (sc->root_con_id) { + sc->root_clk =clk_get (dev, sc->root_con_id); +if (IS_ERR (sc->root_clk)) { +dev_err (dev,"failed to get root clock\n" ); +return PTR_ERR (sc->root_clk); + } + } +return 0 ; fail:pm_clk_destroy (dev);
drivers/clk/qcom/gdsc.h +5 -0
@@ -17,6 +17,7 @@#include <linux/err.h> #include <linux/pm_domain.h> +struct clk ;struct regmap ;/**
@@ -24,12 +25,16 @@ struct regmap;* @pd: generic power domain * @regmap: regmap for MMIO accesses * @gdscr: gsdc control register +* @root_con_id: root clock to be enabled +* @root_clk: clk handle for the root clk * @con_ids: List of clocks to be controlled for the gdsc */ struct gdsc {struct generic_pm_domain pd;struct regmap *regmap;unsigned int gdscr; +char *root_con_id; +struct clk *root_clk;const char *con_ids[]; };
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v7 6/13 clk: qcom: gdsc: Add support for Memory RET/OFF
In reply to Rajendra NayakAlong with the GDSC power switch, there is additional control to either retain all memory (core and peripheral) within a given powerdomain or to turn them off while the GDSC is powered down. Add support for these by modelling a RET state where all
memory is retained and an OFF state where all memory gets turned off.
The controls provided are granular enough to be able to support various differnt levels of RET states, like a 'shallow RET' with all memory retained and a 'deep RET' with some memory retained while some others are lost. The current patch does not support this and considers just one RET state where all memory is retained. Futher work, if needed can support multiple different levels of RET state.
--- drivers/clk/qcom/gdsc.c | 33 +++++++++++++++++++++++++++++++++ drivers/clk/qcom/gdsc.h | 13 +++++++++++++ 2 files changed, 46 insertions(+)
drivers/clk/qcom/gdsc.c +33 -0
@@ -37,6 +37,9 @@#define EN_FEW_WAIT_VAL (0x8 << 16)#define CLK_DIS_WAIT_VAL (0x2 << 12) +#define RETAIN_MEM BIT(14) +#define RETAIN_PERIPH BIT(13) +#define TIMEOUT_US 100#define domain_to_gdsc (domain) container_of(domain, struct gdsc, pd)
@@ -84,6 +87,24 @@ static int gdsc_toggle_logic(struct gdsc *sc, bool en)return -ETIMEDOUT ; } +static inline void gdsc_force_mem_on (struct gdsc *sc) +{ +int i; +u32 mask =RETAIN_MEM |RETAIN_PERIPH ; + +for (i =0 ; i < sc->cxc_count; i++) +regmap_update_bits (sc->regmap, sc->cxcs[i], mask, mask); +} + +static inline void gdsc_clear_mem_on (struct gdsc *sc) +{ +int i; +u32 mask =RETAIN_MEM |RETAIN_PERIPH ; + +for (i =0 ; i < sc->cxc_count; i++) +regmap_update_bits (sc->regmap, sc->cxcs[i], mask,0 ); +} +static int gdsc_enable (struct generic_pm_domain *domain) {struct gdsc *sc =domain_to_gdsc (domain);
@@ -95,6 +116,10 @@ static int gdsc_enable(struct generic_pm_domain *domain) ret =gdsc_toggle_logic (sc, true);if (ret)return ret; + +if (sc->pwrsts &PWRSTS_OFF ) +gdsc_force_mem_on (sc); +/* * If clocks to this power domain were already on, they will take an * additional 4 clock cycles to re-enable after the power domain is
@@ -114,6 +139,9 @@ static int gdsc_disable(struct generic_pm_domain *domain) ret =gdsc_toggle_logic (sc, false); +if (sc->pwrsts &PWRSTS_OFF ) +gdsc_clear_mem_on (sc); +if (sc->root_clk)clk_disable_unprepare (sc->root_clk);
@@ -188,6 +216,11 @@ static int gdsc_init(struct gdsc *sc)if (on <0 )return on; +if (on || (sc->pwrsts &PWRSTS_RET )) +gdsc_force_mem_on (sc); +else +gdsc_clear_mem_on (sc); + sc->pd.power_off = gdsc_disable; sc->pd.power_on = gdsc_enable; sc->pd.attach_dev = gdsc_attach;
drivers/clk/qcom/gdsc.h +13 -0
@@ -20,6 +20,13 @@struct clk ;struct regmap ; +/* Powerdomain allowable state bitfields */ +#define PWRSTS_OFF BIT(0) +#define PWRSTS_RET BIT(1) +#define PWRSTS_ON BIT(2) +#define PWRSTS_OFF_ON (PWRSTS_OFF | PWRSTS_ON) +#define PWRSTS_RET_ON (PWRSTS_RET | PWRSTS_ON) +/** * struct gdsc - Globally Distributed Switch Controller * @pd: generic power domain
@@ -27,6 +34,9 @@ struct regmap;* @gdscr: gsdc control register * @root_con_id: root clock to be enabled * @root_clk: clk handle for the root clk +* @cxcs: offsets of branch registers to toggle mem/periph bits in +* @cxc_count: number of @cxcs +* @pwrsts: Possible powerdomain power states * @con_ids: List of clocks to be controlled for the gdsc */ struct gdsc {
@@ -35,6 +45,9 @@ struct gdsc {unsigned int gdscr;char *root_con_id;struct clk *root_clk; +unsigned int *cxcs; +unsigned int cxc_count; +const u8 pwrsts;const char *con_ids[]; };
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v7 7/13 clk: qcom: gdsc: Add support for ON only state
In reply to Rajendra NayakCertain devices can have GDSCs' which support ON as the only state. They can't be power collapsed to either hit RET or OFF. The clients drivers for these GDSCs' however would expect the state of the core to be reset following a GDSC disable and re-enable. To do this assert/deassert reset lines every time the client driver would request the GDSC to be powered on/off instead.
--- drivers/clk/qcom/common.c | 3 ++- drivers/clk/qcom/gdsc.c | 35 ++++++++++++++++++++++++++++++++++- drivers/clk/qcom/gdsc.h | 11 ++++++++++- 3 files changed, 46 insertions(+), 3 deletions(-)
drivers/clk/qcom/common.c +2 -1
@@ -124,7 +124,8 @@ int qcom_cc_really_probe(struct platform_device *pdev, goto err_reset;if (desc->gdscs && desc->num_gdscs) { - ret =gdsc_register (dev, desc->gdscs, desc->num_gdscs, regmap); + ret =gdsc_register (dev, desc->gdscs, desc->num_gdscs, + &reset->rcdev, regmap);if (ret) goto err_pd; }
drivers/clk/qcom/gdsc.c +34 -1
@@ -21,6 +21,7 @@#include <linux/pm_clock.h> #include <linux/pm_domain.h> #include <linux/regmap.h> +#include <linux/reset-controller.h> #include <linux/slab.h> #include "gdsc.h"
@@ -87,6 +88,24 @@ static int gdsc_toggle_logic(struct gdsc *sc, bool en)return -ETIMEDOUT ; } +static inline int gdsc_deassert_reset (struct gdsc *sc) +{ +int i; + +for (i =0 ; i < sc->reset_count; i++) + sc->rcdev->ops->deassert (sc->rcdev, sc->resets[i]); +return 0 ; +} + +static inline int gdsc_assert_reset (struct gdsc *sc) +{ +int i; + +for (i =0 ; i < sc->reset_count; i++) + sc->rcdev->ops->assert (sc->rcdev, sc->resets[i]); +return 0 ; +} +static inline void gdsc_force_mem_on (struct gdsc *sc) {int i;
@@ -110,6 +129,9 @@ static int gdsc_enable(struct generic_pm_domain *domain)struct gdsc *sc =domain_to_gdsc (domain);int ret; +if (sc->pwrsts ==PWRSTS_ON ) +return gdsc_deassert_reset (sc); +if (sc->root_clk)clk_prepare_enable (sc->root_clk);
@@ -137,6 +159,9 @@ static int gdsc_disable(struct generic_pm_domain *domain)int ret;struct gdsc *sc =domain_to_gdsc (domain); +if (sc->pwrsts ==PWRSTS_ON ) +return gdsc_assert_reset (sc); + ret =gdsc_toggle_logic (sc, false);if (sc->pwrsts &PWRSTS_OFF )
@@ -212,6 +237,13 @@ static int gdsc_init(struct gdsc *sc)if (ret)return ret; +/* Force gdsc ON if only ON state is supported */ +if (sc->pwrsts ==PWRSTS_ON ) { + ret =gdsc_toggle_logic (sc, true); +if (ret) +return ret; + } + on =gdsc_is_enabled (sc);if (on <0 )return on;
@@ -232,7 +264,7 @@ static int gdsc_init(struct gdsc *sc) }int gdsc_register (struct device *dev,struct gdsc **scs,size_t num, -struct regmap *regmap) +struct reset_controller_dev *rcdev,struct regmap *regmap) {int i, ret;struct genpd_onecell_data *data;
@@ -251,6 +283,7 @@ int gdsc_register(struct device *dev, struct gdsc **scs, size_t num,if (!scs[i])continue ; scs[i]->regmap = regmap; + scs[i]->rcdev = rcdev; ret =gdsc_init (scs[i]);if (ret)return ret;
drivers/clk/qcom/gdsc.h +10 -1
@@ -19,6 +19,7 @@struct clk ;struct regmap ; +struct reset_controller_dev ;/* Powerdomain allowable state bitfields */ #define PWRSTS_OFF BIT(0)
@@ -36,6 +37,9 @@ struct regmap;* @root_clk: clk handle for the root clk * @cxcs: offsets of branch registers to toggle mem/periph bits in * @cxc_count: number of @cxcs +* @resets: ids of resets associated with this gdsc +* @reset_count: number of @resets +* @rcdev: reset controller * @pwrsts: Possible powerdomain power states * @con_ids: List of clocks to be controlled for the gdsc */
@@ -48,14 +52,19 @@ struct gdsc {unsigned int *cxcs;unsigned int cxc_count;const u8 pwrsts; +struct reset_controller_dev *rcdev; +unsigned int *resets; +unsigned int reset_count;const char *con_ids[]; };#ifdef CONFIG_QCOM_GDSC -int gdsc_register (struct device *,struct gdsc **,size_t n,struct regmap *); +int gdsc_register (struct device *,struct gdsc **,size_t n, +struct reset_controller_dev *,struct regmap *);void gdsc_unregister (struct device *);#else static inline int gdsc_register (struct device *d,struct gdsc **g,size_t n, +struct reset_controller_dev *rcdev,struct regmap *r) {return -ENOSYS ;
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v7 8/13 clk: qcom: gdsc: Add GDSCs in msm8916 GCC
In reply to Rajendra NayakAdd all data for the GDSCs which are part of msm8916 GCC block.
--- drivers/clk/qcom/Kconfig | 1 + drivers/clk/qcom/gcc-msm8916.c | 57 ++++++++++++++++++++++++++++ include/dt-bindings/clock/qcom,gcc-msm8916.h | 8 ++++ 3 files changed, 66 insertions(+)
drivers/clk/qcom/Kconfig +1 -0
@@ -53,6 +53,7 @@ config MSM_GCC_8660 config MSM_GCC_8916 tristate "MSM8916 Global Clock Controller" + select QCOM_GDSC depends on COMMON_CLK_QCOM help Support for the global clock controller on msm8916 devices.
drivers/clk/qcom/gcc-msm8916.c +57 -0
81 lines not on this page · raw message
include/dt-bindings/clock/qcom,gcc-msm8916.h +8 -0
@@ -153,4 +153,12 @@#define GCC_VENUS0_AXI_CLK 136#define GCC_VENUS0_VCODEC0_CLK 137 +/* Indexes for GDSCs */ +#define BIMC_GDSC 0 +#define VENUS_GDSC 1 +#define MDSS_GDSC 2 +#define JPEG_GDSC 3 +#define VFE_GDSC 4 +#define OXILI_GDSC 5 +#endif
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v7 9/13 clk: qcom: gdsc: Add GDSCs in msm8974 GCC
In reply to Rajendra NayakFrom: Stephen Boyd <[email protected]>
There's just one GDSC as part of the msm8974 GCC block.
--- drivers/clk/qcom/Kconfig | 1 + drivers/clk/qcom/gcc-msm8974.c | 16 ++++++++++++++++ include/dt-bindings/clock/qcom,gcc-msm8974.h | 3 +++ 3 files changed, 20 insertions(+)
drivers/clk/qcom/Kconfig +1 -0
@@ -88,6 +88,7 @@ config MSM_MMCC_8960 config MSM_GCC_8974 tristate "MSM8974 Global Clock Controller" + select QCOM_GDSC depends on COMMON_CLK_QCOM help Support for the global clock controller on msm8974 devices.
drivers/clk/qcom/gcc-msm8974.c +16 -0
@@ -31,6 +31,7 @@#include "clk-rcg.h" #include "clk-branch.h" #include "reset.h" +#include "gdsc.h" enum {P_XO ,
@@ -2431,6 +2432,15 @@ static struct clk_branch gcc_usb_hsic_system_clk = { }, }; +static struct gdsc usb_hs_hsic_gdsc = { + .gdscr =0x404 , + .pd = { + .name ="usb_hs_hsic" , + }, + .pwrsts =PWRSTS_OFF_ON , + .con_ids = {NULL }, +}; +static struct clk_regmap *gcc_msm8974_clocks[] = { [GPLL0 ] = &gpll0.clkr, [GPLL0_VOTE ] = &gpll0_vote,
@@ -2660,6 +2670,10 @@ static const struct qcom_reset_map gcc_msm8974_resets[] = { [GCC_VENUS_RESTART] = { 0x1740 }, }; +static struct gdsc *gcc_msm8974_gdscs[] = { + [USB_HS_HSIC_GDSC ] = &usb_hs_hsic_gdsc, +}; +static const struct regmap_config gcc_msm8974_regmap_config = { .reg_bits =32 , .reg_stride =4 ,
@@ -2674,6 +2688,8 @@ static const struct qcom_cc_desc gcc_msm8974_desc = { .num_clks = ARRAY_SIZE(gcc_msm8974_clocks), .resets = gcc_msm8974_resets, .num_resets = ARRAY_SIZE(gcc_msm8974_resets), + .gdscs = gcc_msm8974_gdscs, + .num_gdscs = ARRAY_SIZE(gcc_msm8974_gdscs), };static const struct of_device_id gcc_msm8974_match_table[] = {
include/dt-bindings/clock/qcom,gcc-msm8974.h +3 -0
@@ -321,4 +321,7 @@#define GCC_SDCC1_CDCCAL_SLEEP_CLK 304#define GCC_SDCC1_CDCCAL_FF_CLK 305 +/* gdscs */ +#define USB_HS_HSIC_GDSC 0 +#endif
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v7 10/13 clk: qcom: gdsc: Add GDSCs in msm8974 MMCC
In reply to Rajendra NayakFrom: Stephen Boyd <[email protected]>
Add the GDSC instances that exist as part of msm8974 MMCC block
--- drivers/clk/qcom/Kconfig | 1 + drivers/clk/qcom/mmcc-msm8974.c | 78 +++++++++++++++++++++++++++ include/dt-bindings/clock/qcom,mmcc-msm8974.h | 8 +++ 3 files changed, 87 insertions(+)
drivers/clk/qcom/Kconfig +1 -0
@@ -98,6 +98,7 @@ config MSM_GCC_8974 config MSM_MMCC_8974 tristate "MSM8974 Multimedia Clock Controller" select MSM_GCC_8974 + select QCOM_GDSC depends on COMMON_CLK_QCOM help Support for the multimedia clock controller on msm8974 devices.
drivers/clk/qcom/mmcc-msm8974.c +78 -0
102 lines not on this page · raw message
include/dt-bindings/clock/qcom,mmcc-msm8974.h +8 -0
@@ -158,4 +158,12 @@#define SPDM_RM_AXI 141#define SPDM_RM_OCMEMNOC 142 +/* gdscs */ +#define VENUS0_GDSC 0 +#define MDSS_GDSC 1 +#define CAMSS_JPEG_GDSC 2 +#define CAMSS_VFE_GDSC 3 +#define OXILI_GDSC 4 +#define OXILICX_GDSC 5 +#endif
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v7 11/13 clk: qcom: gdsc: Add GDSCs in apq8084 GCC
In reply to Rajendra NayakAdd the GDSC instances that exist as part of apq8084 GCC block
--- drivers/clk/qcom/Kconfig | 1 + drivers/clk/qcom/gcc-apq8084.c | 46 ++++++++++++++++++++++++++++ include/dt-bindings/clock/qcom,gcc-apq8084.h | 6 ++++ 3 files changed, 53 insertions(+)
drivers/clk/qcom/Kconfig +1 -0
@@ -7,6 +7,7 @@ config COMMON_CLK_QCOM config APQ_GCC_8084 tristate "APQ8084 Global Clock Controller" + select QCOM_GDSC depends on COMMON_CLK_QCOM help Support for the global clock controller on apq8084 devices.
drivers/clk/qcom/gcc-apq8084.c +46 -0
70 lines not on this page · raw message
include/dt-bindings/clock/qcom,gcc-apq8084.h +6 -0
@@ -348,4 +348,10 @@#define GCC_PCIE_1_PIPE_CLK 331#define GCC_PCIE_1_SLV_AXI_CLK 332 +/* gdscs */ +#define USB_HS_HSIC_GDSC 0 +#define PCIE0_GDSC 1 +#define PCIE1_GDSC 2 +#define USB30_GDSC 3 +#endif
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v7 12/13 clk: qcom: gdsc: Add GDSCs in apq8084 MMCC
In reply to Rajendra NayakFrom: Stephane Viau <[email protected]>
Add the GDSC instances that exist as part of apq8084 MMCC block.
--- drivers/clk/qcom/Kconfig | 1 + drivers/clk/qcom/mmcc-apq8084.c | 74 ++++++++++++++++++++++++++- include/dt-bindings/clock/qcom,mmcc-apq8084.h | 8 +++ 3 files changed, 82 insertions(+), 1 deletion(-)
drivers/clk/qcom/Kconfig +1 -0
@@ -17,6 +17,7 @@ config APQ_GCC_8084 config APQ_MMCC_8084 tristate "APQ8084 Multimedia Clock Controller" select APQ_GCC_8084 + select QCOM_GDSC depends on COMMON_CLK_QCOM help Support for the multimedia clock controller on apq8084 devices.
drivers/clk/qcom/mmcc-apq8084.c +73 -1
102 lines not on this page · raw message
include/dt-bindings/clock/qcom,mmcc-apq8084.h +8 -0
@@ -180,4 +180,12 @@#define VPU_SLEEP_CLK 163#define VPU_VDP_CLK 164 +/* GDSCs */ +#define VENUS0_GDSC 0 +#define MDSS_GDSC 1 +#define CAMSS_JPEG_GDSC 2 +#define CAMSS_VFE_GDSC 3 +#define OXILI_GDSC 4 +#define OXILICX_GDSC 5 +#endif
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
PATCH v7 12/13 clk: qcom: gdsc: Add GDSCs in apq8084 MMCC
<snip>
drivers/clk/qcom/mmcc-apq8084.c static const struct pll_config mmpll3_config = { · line 3089
Quoted 10 lines
drivers/clk/qcom/mmcc-apq8084.c +9
-0+static struct gdsc venus0_gdsc = { + .gdscr =0x1024 , + .pd = { + .name ="venus0" , + }, + .pwrsts =PWRSTS_OFF_ON , + .con_ids = {NULL }, +}; +
Rajendra, according to downstream kernel apq8084 has second instance of gdsc i.e. venus1_gdsc, is that correct?
Signature · 2 lines
regards, Stan
PATCH v7 12/13 clk: qcom: gdsc: Add GDSCs in apq8084 MMCC
Quoted 4 lines · Stanimir Varbanov
<snip>
Quoted 1 line
Quoted 12 lines
drivers/clk/qcom/mmcc-apq8084.c static const struct pll_config mmpll3_config = { · line 3089
Quoted 9 lines
drivers/clk/qcom/mmcc-apq8084.c +9
-0+static struct gdsc venus0_gdsc = { + .gdscr =0x1024 , + .pd = { + .name ="venus0" , + }, + .pwrsts =PWRSTS_OFF_ON , + .con_ids = {NULL }, +}; +Rajendra, according to downstream kernel apq8084 has second instance of gdsc i.e.
venus1_gdsc, is that correct?
Stan, it does seem like there are 3 instances of venus gdscs', venus0, venus0_core0 and venus0_core1. I will add the missing ones when I repost,thanks.
PATCH v7 13/13 arm: dts: qcom: Add #power-domain-cells property
In reply to Rajendra Nayakmsm8974 has gcc and mmcc nodes, and apq8084 has a gcc node which implement gdsc powerdomains. Add the #power-domain-cells property to them.
--- arch/arm/boot/dts/qcom-apq8084.dtsi | 1 + arch/arm/boot/dts/qcom-msm8974.dtsi | 2 ++ arch/arm64/boot/dts/qcom/msm8916.dtsi | 1 + 3 files changed, 4 insertions(+)
arch/arm/boot/dts/qcom-apq8084.dtsi +1 -0
@@ -221,6 +221,7 @@ compatible = "qcom,gcc-apq8084"; #clock-cells = <1>; #reset-cells = <1>; + #power-domain-cells = <1>; reg = <0xfc400000 0x4000>; };
arch/arm/boot/dts/qcom-msm8974.dtsi +2 -0
@@ -217,6 +217,7 @@ compatible = "qcom,gcc-msm8974"; #clock-cells = <1>; #reset-cells = <1>; + #power-domain-cells = <1>; reg = <0xfc400000 0x4000>; };
@@ -224,6 +225,7 @@ compatible = "qcom,mmcc-msm8974"; #clock-cells = <1>; #reset-cells = <1>; + #power-domain-cells = <1>; reg = <0xfd8c0000 0x6000>; };
arch/arm64/boot/dts/qcom/msm8916.dtsi +1 -0
@@ -120,6 +120,7 @@ compatible = "qcom,gcc-msm8916"; #clock-cells = <1>; #reset-cells = <1>; + #power-domain-cells = <1>; reg = <0x1800000 0x80000>; };
Signature · 2 lines
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation