From 2f89497a34775e840389d3b9f58beae230ef7075 Mon Sep 17 00:00:00 2001 From: Deepak Katragadda Date: Thu, 21 Jun 2018 16:40:00 +0100 Subject: [PATCH] clk: qcom: clk-dummy: Add a dummy clock provider Add a dummy clock provider that registers a simple callback that in turn always returns the dummy clock for any clk_get call. In addition, consumers of dummy clock provider requires support for dummy resets, which would make a dummy assert/deassert of the reset signals. Change-Id: I08fcb174fd0e0c49f8069e106b48597bcdfe847d Signed-off-by: Taniya Das Signed-off-by: Deepak Katragadda Signed-off-by: David Dai Signed-off-by: Vivek Aknurwar --- drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/clk-dummy.c | 137 +++++++++++++++++++++++++++++++++++ drivers/clk/qcom/common.h | 14 +++- 3 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 drivers/clk/qcom/clk-dummy.c diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 1718c34d3551..ef610a7236ea 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -14,6 +14,7 @@ clk-qcom-y += clk-regmap-mux-div.o clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o clk-qcom-y += clk-hfpll.o clk-qcom-y += reset.o +clk-qcom-y += clk-dummy.o clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o # Keep alphabetically sorted by config diff --git a/drivers/clk/qcom/clk-dummy.c b/drivers/clk/qcom/clk-dummy.c new file mode 100644 index 000000000000..6aaba55e748c --- /dev/null +++ b/drivers/clk/qcom/clk-dummy.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include + +#include "common.h" + +#define to_clk_dummy(_hw) container_of(_hw, struct clk_dummy, hw) + +#define RESET_MAX 100 + +static int dummy_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_dummy *dummy = to_clk_dummy(hw); + + dummy->rrate = rate; + + pr_debug("%s: rate %lu\n", __func__, rate); + + return 0; +} + +static long dummy_clk_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return rate; +} + +static unsigned long dummy_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_dummy *dummy = to_clk_dummy(hw); + + pr_debug("%s: returning a clock rate of %lu\n", + __func__, dummy->rrate); + + return dummy->rrate; +} + +const struct clk_ops clk_dummy_ops = { + .set_rate = dummy_clk_set_rate, + .round_rate = dummy_clk_round_rate, + .recalc_rate = dummy_clk_recalc_rate, +}; +EXPORT_SYMBOL(clk_dummy_ops); + +static int dummy_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return 0; +} + +static int dummy_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return 0; +} + +static struct reset_control_ops dummy_reset_ops = { + .assert = dummy_reset_assert, + .deassert = dummy_reset_deassert, +}; + +/** + * clk_register_dummy - register dummy clock with the + * clock framework + * @dev: device that is registering this clock + * @name: name of this clock + * @flags: framework-specific flags + * @node: device node + */ +static struct clk *clk_register_dummy(struct device *dev, const char *name, + unsigned long flags, struct device_node *node) +{ + struct clk_dummy *dummy; + struct clk *clk; + struct clk_init_data init = {}; + + /* allocate dummy clock */ + dummy = kzalloc(sizeof(*dummy), GFP_KERNEL); + if (!dummy) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_dummy_ops; + init.flags = flags; + init.num_parents = 0; + dummy->hw.init = &init; + + /* register the clock */ + clk = clk_register(dev, &dummy->hw); + if (IS_ERR(clk)) { + kfree(dummy); + return clk; + } + + dummy->reset.of_node = node; + dummy->reset.ops = &dummy_reset_ops; + dummy->reset.nr_resets = RESET_MAX; + + if (reset_controller_register(&dummy->reset)) + pr_err("Failed to register reset controller for %s\n", name); + else + pr_info("Successfully registered dummy reset controller for %s\n", name); + + return clk; +} + +/** + * of_dummy_clk_setup() - Setup function for simple fixed rate clock + */ +static void of_dummy_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = "dummy_clk"; + + of_property_read_string(node, "clock-output-names", &clk_name); + + clk = clk_register_dummy(NULL, clk_name, 0, node); + if (!IS_ERR(clk)) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + } else { + pr_err("Failed to register dummy clock controller for %s\n", + clk_name); + return; + } + + pr_info("Successfully registered dummy clock controller for %s\n", + clk_name); +} +CLK_OF_DECLARE(dummy_clk, "qcom,dummycc", of_dummy_clk_setup); diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 9c8f7b798d9f..3c672ae04258 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -1,9 +1,11 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* Copyright (c) 2014, The Linux Foundation. All rights reserved. */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2014, 2018, The Linux Foundation. All rights reserved. */ #ifndef __QCOM_CLK_COMMON_H__ #define __QCOM_CLK_COMMON_H__ +#include + struct platform_device; struct regmap_config; struct clk_regmap; @@ -41,6 +43,12 @@ struct parent_map { u8 cfg; }; +struct clk_dummy { + struct clk_hw hw; + struct reset_controller_dev reset; + unsigned long rrate; +}; + extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f, unsigned long rate); extern const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f, @@ -65,5 +73,5 @@ extern int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc); extern int qcom_cc_probe_by_index(struct platform_device *pdev, int index, const struct qcom_cc_desc *desc); - +extern const struct clk_ops clk_dummy_ops; #endif