[source] ipq806x: update clk-qcom patches

LEDE Commits lede-commits at lists.infradead.org
Thu Nov 3 00:35:06 PDT 2016


blogic pushed a commit to source.git, branch master:
https://git.lede-project.org/8749fb7894601011811dd7409b48401b3aafe0dd

commit 8749fb7894601011811dd7409b48401b3aafe0dd
Author: Pavel Kubelun <be.dissent at gmail.com>
AuthorDate: Wed Nov 2 22:08:45 2016 +0300

    ipq806x: update clk-qcom patches
    
    Preparing for cpufreq driver switch to generic cpufreq-dt
    
    Signed-off-by: Pavel Kubelun <be.dissent at gmail.com>
---
 ...add-API-register-brd-clks-bckwrds-cmptblt.patch | 135 +++++++
 .../002-make-reset-control-ops-const.patch         |  35 ++
 ...dd-support-for-High-Frequency-PLLs-HFPLLs.patch |   2 +-
 ...140-clk-qcom-Add-support-for-Krait-clocks.patch |  11 +-
 .../patches-4.4/175-add-regmap-mux-div.patch       | 386 +++++++++++++++++++++
 5 files changed, 563 insertions(+), 6 deletions(-)

diff --git a/target/linux/ipq806x/patches-4.4/001-add-API-register-brd-clks-bckwrds-cmptblt.patch b/target/linux/ipq806x/patches-4.4/001-add-API-register-brd-clks-bckwrds-cmptblt.patch
new file mode 100644
index 0000000..b9056e9
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/001-add-API-register-brd-clks-bckwrds-cmptblt.patch
@@ -0,0 +1,135 @@
+From ee15faffef11309219aa87a24efc86f6dd13f7cb Mon Sep 17 00:00:00 2001
+From: Stephen Boyd <sboyd at codeaurora.org>
+Date: Mon, 26 Oct 2015 17:11:32 -0700
+Subject: clk: qcom: common: Add API to register board clocks backwards
+ compatibly
+
+We want to put the XO board clocks into the dt files, but we also
+need to be backwards compatible with an older dtb. Add an API to
+the common code to do this. This also makes a place for us to
+handle the case when the RPM clock driver is enabled and we don't
+want to register the fixed factor clock.
+
+Cc: Georgi Djakov <georgi.djakov at linaro.org>
+Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
+---
+ drivers/clk/qcom/common.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++
+ drivers/clk/qcom/common.h |  4 +++
+ 2 files changed, 91 insertions(+)
+
+--- a/drivers/clk/qcom/common.c
++++ b/drivers/clk/qcom/common.c
+@@ -17,6 +17,7 @@
+ #include <linux/platform_device.h>
+ #include <linux/clk-provider.h>
+ #include <linux/reset-controller.h>
++#include <linux/of.h>
+ 
+ #include "common.h"
+ #include "clk-rcg.h"
+@@ -88,6 +89,92 @@ static void qcom_cc_gdsc_unregister(void
+ 	gdsc_unregister(data);
+ }
+ 
++/*
++ * Backwards compatibility with old DTs. Register a pass-through factor 1/1
++ * clock to translate 'path' clk into 'name' clk and regsiter the 'path'
++ * clk as a fixed rate clock if it isn't present.
++ */
++static int _qcom_cc_register_board_clk(struct device *dev, const char *path,
++				       const char *name, unsigned long rate,
++				       bool add_factor)
++{
++	struct device_node *node = NULL;
++	struct device_node *clocks_node;
++	struct clk_fixed_factor *factor;
++	struct clk_fixed_rate *fixed;
++	struct clk *clk;
++	struct clk_init_data init_data = { };
++
++	clocks_node = of_find_node_by_path("/clocks");
++	if (clocks_node)
++		node = of_find_node_by_name(clocks_node, path);
++	of_node_put(clocks_node);
++
++	if (!node) {
++		fixed = devm_kzalloc(dev, sizeof(*fixed), GFP_KERNEL);
++		if (!fixed)
++			return -EINVAL;
++
++		fixed->fixed_rate = rate;
++		fixed->hw.init = &init_data;
++
++		init_data.name = path;
++		init_data.flags = CLK_IS_ROOT;
++		init_data.ops = &clk_fixed_rate_ops;
++
++		clk = devm_clk_register(dev, &fixed->hw);
++		if (IS_ERR(clk))
++			return PTR_ERR(clk);
++	}
++	of_node_put(node);
++
++	if (add_factor) {
++		factor = devm_kzalloc(dev, sizeof(*factor), GFP_KERNEL);
++		if (!factor)
++			return -EINVAL;
++
++		factor->mult = factor->div = 1;
++		factor->hw.init = &init_data;
++
++		init_data.name = name;
++		init_data.parent_names = &path;
++		init_data.num_parents = 1;
++		init_data.flags = 0;
++		init_data.ops = &clk_fixed_factor_ops;
++
++		clk = devm_clk_register(dev, &factor->hw);
++		if (IS_ERR(clk))
++			return PTR_ERR(clk);
++	}
++
++	return 0;
++}
++
++int qcom_cc_register_board_clk(struct device *dev, const char *path,
++			       const char *name, unsigned long rate)
++{
++	bool add_factor = true;
++	struct device_node *node;
++
++	/* The RPM clock driver will add the factor clock if present */
++	if (IS_ENABLED(CONFIG_QCOM_RPMCC)) {
++		node = of_find_compatible_node(NULL, NULL, "qcom,rpmcc");
++		if (of_device_is_available(node))
++			add_factor = false;
++		of_node_put(node);
++	}
++
++	return _qcom_cc_register_board_clk(dev, path, name, rate, add_factor);
++}
++EXPORT_SYMBOL_GPL(qcom_cc_register_board_clk);
++
++int qcom_cc_register_sleep_clk(struct device *dev)
++{
++	return _qcom_cc_register_board_clk(dev, "sleep_clk", "sleep_clk_src",
++					   32768, true);
++}
++EXPORT_SYMBOL_GPL(qcom_cc_register_sleep_clk);
++
+ int qcom_cc_really_probe(struct platform_device *pdev,
+ 			 const struct qcom_cc_desc *desc, struct regmap *regmap)
+ {
+--- a/drivers/clk/qcom/common.h
++++ b/drivers/clk/qcom/common.h
+@@ -37,6 +37,10 @@ extern const struct freq_tbl *qcom_find_
+ extern int qcom_find_src_index(struct clk_hw *hw, const struct parent_map *map,
+ 			       u8 src);
+ 
++extern int qcom_cc_register_board_clk(struct device *dev, const char *path,
++				      const char *name, unsigned long rate);
++extern int qcom_cc_register_sleep_clk(struct device *dev);
++
+ extern struct regmap *qcom_cc_map(struct platform_device *pdev,
+ 				  const struct qcom_cc_desc *desc);
+ extern int qcom_cc_really_probe(struct platform_device *pdev,
diff --git a/target/linux/ipq806x/patches-4.4/002-make-reset-control-ops-const.patch b/target/linux/ipq806x/patches-4.4/002-make-reset-control-ops-const.patch
new file mode 100644
index 0000000..6261125
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/002-make-reset-control-ops-const.patch
@@ -0,0 +1,35 @@
+From add479eeb1a208a31ab913ae7c97506a81383079 Mon Sep 17 00:00:00 2001
+From: Philipp Zabel <p.zabel at pengutronix.de>
+Date: Thu, 25 Feb 2016 10:45:12 +0100
+Subject: clk: qcom: Make reset_control_ops const
+
+The qcom_reset_ops structure is never modified. Make it const.
+
+Signed-off-by: Philipp Zabel <p.zabel at pengutronix.de>
+Signed-off-by: Stephen Boyd <sboyd at codeaurora.org>
+---
+ drivers/clk/qcom/reset.c | 2 +-
+ drivers/clk/qcom/reset.h | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/clk/qcom/reset.c
++++ b/drivers/clk/qcom/reset.c
+@@ -55,7 +55,7 @@ qcom_reset_deassert(struct reset_control
+ 	return regmap_update_bits(rst->regmap, map->reg, mask, 0);
+ }
+ 
+-struct reset_control_ops qcom_reset_ops = {
++const struct reset_control_ops qcom_reset_ops = {
+ 	.reset = qcom_reset,
+ 	.assert = qcom_reset_assert,
+ 	.deassert = qcom_reset_deassert,
+--- a/drivers/clk/qcom/reset.h
++++ b/drivers/clk/qcom/reset.h
+@@ -32,6 +32,6 @@ struct qcom_reset_controller {
+ #define to_qcom_reset_controller(r) \
+ 	container_of(r, struct qcom_reset_controller, rcdev);
+ 
+-extern struct reset_control_ops qcom_reset_ops;
++extern const struct reset_control_ops qcom_reset_ops;
+ 
+ #endif
diff --git a/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch b/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch
index 4924e5f..7e96449 100644
--- a/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch
+++ b/target/linux/ipq806x/patches-4.4/137-clk-qcom-Add-support-for-High-Frequency-PLLs-HFPLLs.patch
@@ -86,7 +86,7 @@ I'd really like to get rid of __clk_hfpll_init_once() if possible...
 +		u32 regval = hd->user_val;
 +		unsigned long rate;
 +
-+		rate = clk_hw_get_rate(hw->clk);
++		rate = clk_hw_get_rate(hw);
 +
 +		/* Pick the right VCO. */
 +		if (hd->user_vco_mask && rate > hd->low_vco_max_rate)
diff --git a/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch b/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch
index ecf1ef5..b1720b0 100644
--- a/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch
+++ b/target/linux/ipq806x/patches-4.4/140-clk-qcom-Add-support-for-Krait-clocks.patch
@@ -50,7 +50,7 @@ drivers/clk/qcom/Kconfig     |   4 ++
  clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o
 --- /dev/null
 +++ b/drivers/clk/qcom/clk-krait.c
-@@ -0,0 +1,166 @@
+@@ -0,0 +1,167 @@
 +/*
 + * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
 + *
@@ -128,18 +128,19 @@ drivers/clk/qcom/Kconfig     |   4 ++
 +	return clk_mux_get_parent(hw, sel, mux->parent_map, 0);
 +}
 +
-+static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw)
++static struct clk_hw *krait_mux_get_safe_parent(struct clk_hw *hw,
++						unsigned long *safe_freq)
 +{
 +	int i;
 +	struct krait_mux_clk *mux = to_krait_mux_clk(hw);
-+	int num_parents = clk_hw_get_num_parents(hw->clk);
++	int num_parents = clk_hw_get_num_parents(hw);
 +
 +	i = mux->safe_sel;
 +	for (i = 0; i < num_parents; i++)
 +		if (mux->safe_sel == mux->parent_map[i])
 +			break;
 +
-+	return __clk_get_hw(clk_hw_get_parent_by_index(hw->clk, i));
++	return clk_hw_get_parent_by_index(hw, i);
 +}
 +
 +static int krait_mux_enable(struct clk_hw *hw)
@@ -172,7 +173,7 @@ drivers/clk/qcom/Kconfig     |   4 ++
 +static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate,
 +				  unsigned long *parent_rate)
 +{
-+	*parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw->clk), rate * 2);
++	*parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2);
 +	return DIV_ROUND_UP(*parent_rate, 2);
 +}
 +
diff --git a/target/linux/ipq806x/patches-4.4/175-add-regmap-mux-div.patch b/target/linux/ipq806x/patches-4.4/175-add-regmap-mux-div.patch
new file mode 100644
index 0000000..b5c23b5
--- /dev/null
+++ b/target/linux/ipq806x/patches-4.4/175-add-regmap-mux-div.patch
@@ -0,0 +1,386 @@
+From 7727b1cae43e9abac746ef016c3dbf50ee81a6d6 Mon Sep 17 00:00:00 2001
+From: Georgi Djakov <georgi.djakov at linaro.org>
+Date: Wed, 18 Mar 2015 17:23:29 +0200
+Subject: clk: qcom: Add support for regmap mux-div clocks
+
+Add support for hardware that support switching both parent clocks and the
+divider at the same time. This avoids generating intermediate frequencies
+from either the old parent clock and new divider or new parent clock and
+old divider combinations.
+
+Signed-off-by: Georgi Djakov <georgi.djakov at linaro.org>
+---
+ drivers/clk/qcom/Makefile             |   1 +
+ drivers/clk/qcom/clk-regmap-mux-div.c | 288 ++++++++++++++++++++++++++++++++++
+ drivers/clk/qcom/clk-regmap-mux-div.h |  63 ++++++++
+ 3 files changed, 352 insertions(+)
+ create mode 100644 drivers/clk/qcom/clk-regmap-mux-div.c
+ create mode 100644 drivers/clk/qcom/clk-regmap-mux-div.h
+
+--- a/drivers/clk/qcom/Makefile
++++ b/drivers/clk/qcom/Makefile
+@@ -8,6 +8,7 @@ clk-qcom-y += clk-rcg2.o
+ clk-qcom-y += clk-branch.o
+ clk-qcom-y += clk-regmap-divider.o
+ clk-qcom-y += clk-regmap-mux.o
++clk-qcom-y += clk-regmap-mux-div.o
+ clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o
+ obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o
+ clk-qcom-y += clk-hfpll.o
+--- /dev/null
++++ b/drivers/clk/qcom/clk-regmap-mux-div.c
+@@ -0,0 +1,288 @@
++/*
++ * Copyright (c) 2015, Linaro Limited
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * 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/bitops.h>
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/export.h>
++#include <linux/kernel.h>
++#include <linux/regmap.h>
++
++#include "clk-regmap-mux-div.h"
++
++#define CMD_RCGR			0x0
++#define CMD_RCGR_UPDATE			BIT(0)
++#define CMD_RCGR_DIRTY_CFG		BIT(4)
++#define CMD_RCGR_ROOT_OFF		BIT(31)
++#define CFG_RCGR			0x4
++
++static int __mux_div_update_config(struct clk_regmap_mux_div *md)
++{
++	int ret;
++	u32 val, count;
++	const char *name = clk_hw_get_name(&md->clkr.hw);
++
++	ret = regmap_update_bits(md->clkr.regmap, CMD_RCGR + md->reg_offset,
++				 CMD_RCGR_UPDATE, CMD_RCGR_UPDATE);
++	if (ret)
++		return ret;
++
++	/* Wait for update to take effect */
++	for (count = 500; count > 0; count--) {
++		ret = regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset,
++				  &val);
++		if (ret)
++			return ret;
++		if (!(val & CMD_RCGR_UPDATE))
++			return 0;
++		udelay(1);
++	}
++
++	pr_err("%s: RCG did not update its configuration", name);
++	return -EBUSY;
++}
++
++static int __mux_div_set_src_div(struct clk_regmap_mux_div *md, u32 src_sel,
++				 u32 src_div)
++{
++	int ret;
++	u32 val, mask;
++
++	val = (src_div << md->hid_shift) | (src_sel << md->src_shift);
++	mask = ((BIT(md->hid_width) - 1) << md->hid_shift) |
++	       ((BIT(md->src_width) - 1) << md->src_shift);
++
++	ret = regmap_update_bits(md->clkr.regmap, CFG_RCGR + md->reg_offset,
++				 mask, val);
++	if (ret)
++		return ret;
++
++	return __mux_div_update_config(md);
++}
++
++static void __mux_div_get_src_div(struct clk_regmap_mux_div *md, u32 *src_sel,
++				  u32 *src_div)
++{
++	u32 val, div, src;
++	const char *name = clk_hw_get_name(&md->clkr.hw);
++
++	regmap_read(md->clkr.regmap, CMD_RCGR + md->reg_offset, &val);
++
++	if (val & CMD_RCGR_DIRTY_CFG) {
++		pr_err("%s: RCG configuration is pending\n", name);
++		return;
++	}
++
++	regmap_read(md->clkr.regmap, CFG_RCGR + md->reg_offset, &val);
++	src = (val >> md->src_shift);
++	src &= BIT(md->src_width) - 1;
++	*src_sel = src;
++
++	div = (val >> md->hid_shift);
++	div &= BIT(md->hid_width) - 1;
++	*src_div = div;
++}
++
++static int mux_div_enable(struct clk_hw *hw)
++{
++	struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++
++	return __mux_div_set_src_div(md, md->src_sel, md->div);
++}
++
++static inline bool is_better_rate(unsigned long req, unsigned long best,
++				  unsigned long new)
++{
++	return (req <= new && new < best) || (best < req && best < new);
++}
++
++static int mux_div_determine_rate(struct clk_hw *hw,
++				  struct clk_rate_request *req)
++{
++	struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++	unsigned int i, div, max_div;
++	unsigned long actual_rate, best_rate = 0;
++	unsigned long req_rate = req->rate;
++
++	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
++		struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
++		unsigned long parent_rate = clk_hw_get_rate(parent);
++
++		max_div = BIT(md->hid_width) - 1;
++		for (div = 1; div < max_div; div++) {
++			parent_rate = mult_frac(req_rate, div, 2);
++			parent_rate = clk_hw_round_rate(parent, parent_rate);
++			actual_rate = mult_frac(parent_rate, 2, div);
++
++			if (is_better_rate(req_rate, best_rate, actual_rate)) {
++				best_rate = actual_rate;
++				req->rate = best_rate;
++				req->best_parent_rate = parent_rate;
++				req->best_parent_hw = parent;
++			}
++
++			if (actual_rate < req_rate || best_rate <= req_rate)
++				break;
++		}
++	}
++
++	if (!best_rate)
++		return -EINVAL;
++
++	return 0;
++}
++
++static int __mux_div_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
++					 unsigned long prate, u32 src_sel)
++{
++	struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++	int ret, i;
++	u32 div, max_div, best_src = 0, best_div = 0;
++	unsigned long actual_rate, best_rate = 0;
++
++	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
++		struct clk_hw *parent = clk_hw_get_parent_by_index(hw, i);
++		unsigned long parent_rate = clk_hw_get_rate(parent);
++
++		max_div = BIT(md->hid_width) - 1;
++		for (div = 1; div < max_div; div++) {
++			parent_rate = mult_frac(rate, div, 2);
++			parent_rate = clk_hw_round_rate(parent, parent_rate);
++			actual_rate = mult_frac(parent_rate, 2, div);
++
++			if (is_better_rate(rate, best_rate, actual_rate)) {
++				best_rate = actual_rate;
++				best_src = md->parent_map[i].cfg;
++				best_div = div - 1;
++			}
++
++			if (actual_rate < rate || best_rate <= rate)
++				break;
++		}
++	}
++
++	ret = __mux_div_set_src_div(md, best_src, best_div);
++	if (!ret) {
++		md->div = best_div;
++		md->src_sel = best_src;
++	}
++
++	return ret;
++}
++
++static u8 mux_div_get_parent(struct clk_hw *hw)
++{
++	struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++	const char *name = clk_hw_get_name(hw);
++	u32 i, div, src;
++
++	__mux_div_get_src_div(md, &src, &div);
++
++	for (i = 0; i < clk_hw_get_num_parents(hw); i++)
++		if (src == md->parent_map[i].cfg)
++			return i;
++
++	pr_err("%s: Can't find parent %d\n", name, src);
++	return 0;
++}
++
++static int mux_div_set_parent(struct clk_hw *hw, u8 index)
++{
++	struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++
++	return __mux_div_set_src_div(md, md->parent_map[index].cfg, md->div);
++}
++
++static int mux_div_set_rate(struct clk_hw *hw,
++			    unsigned long rate, unsigned long prate)
++{
++	struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++
++	return __mux_div_set_rate_and_parent(hw, rate, prate, md->src_sel);
++}
++
++static int mux_div_set_rate_and_parent(struct clk_hw *hw,  unsigned long rate,
++				       unsigned long prate, u8 index)
++{
++	struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++
++	return __mux_div_set_rate_and_parent(hw, rate, prate,
++					     md->parent_map[index].cfg);
++}
++
++static unsigned long mux_div_recalc_rate(struct clk_hw *hw, unsigned long prate)
++{
++	struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++	u32 div, src;
++	int i, num_parents = clk_hw_get_num_parents(hw);
++	const char *name = clk_hw_get_name(hw);
++
++	__mux_div_get_src_div(md, &src, &div);
++	for (i = 0; i < num_parents; i++)
++		if (src == md->parent_map[i].cfg) {
++			struct clk_hw *p = clk_hw_get_parent_by_index(hw, i);
++			unsigned long parent_rate = clk_hw_get_rate(p);
++
++			return mult_frac(parent_rate, 2, div + 1);
++		}
++
++	pr_err("%s: Can't find parent %d\n", name, src);
++	return 0;
++}
++
++static struct clk_hw *mux_div_get_safe_parent(struct clk_hw *hw,
++					      unsigned long *safe_freq)
++{
++	int i;
++	struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++
++	if (md->safe_freq)
++		*safe_freq = md->safe_freq;
++
++	for (i = 0; i < clk_hw_get_num_parents(hw); i++)
++		if (md->safe_src == md->parent_map[i].cfg)
++			break;
++
++	return clk_hw_get_parent_by_index(hw, i);
++}
++
++static void mux_div_disable(struct clk_hw *hw)
++{
++	struct clk_regmap_mux_div *md = to_clk_regmap_mux_div(hw);
++	struct clk_hw *parent;
++	u32 div;
++
++	if (!md->safe_freq || !md->safe_src)
++		return;
++
++	parent = mux_div_get_safe_parent(hw, &md->safe_freq);
++	div = divider_get_val(md->safe_freq, clk_get_rate(parent->clk), NULL,
++			      md->hid_width, CLK_DIVIDER_ROUND_CLOSEST);
++	div = 2 * div + 1;
++
++	__mux_div_set_src_div(md, md->safe_src, div);
++}
++
++const struct clk_ops clk_regmap_mux_div_ops = {
++	.enable = mux_div_enable,
++	.disable = mux_div_disable,
++	.get_parent = mux_div_get_parent,
++	.set_parent = mux_div_set_parent,
++	.set_rate = mux_div_set_rate,
++	.set_rate_and_parent = mux_div_set_rate_and_parent,
++	.determine_rate = mux_div_determine_rate,
++	.recalc_rate = mux_div_recalc_rate,
++	.get_safe_parent = mux_div_get_safe_parent,
++};
++EXPORT_SYMBOL_GPL(clk_regmap_mux_div_ops);
+--- /dev/null
++++ b/drivers/clk/qcom/clk-regmap-mux-div.h
+@@ -0,0 +1,63 @@
++/*
++ * Copyright (c) 2015, Linaro Limited
++ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
++ *
++ * This software is licensed under the terms of the GNU General Public
++ * License version 2, as published by the Free Software Foundation, and
++ * may be copied, distributed, and modified under those terms.
++ *
++ * 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.
++ */
++
++#ifndef __QCOM_CLK_REGMAP_MUX_DIV_H__
++#define __QCOM_CLK_REGMAP_MUX_DIV_H__
++
++#include <linux/clk-provider.h>
++#include "clk-regmap.h"
++#include "clk-rcg.h"
++
++/**
++ * struct mux_div_clk - combined mux/divider clock
++ * @reg_offset: offset of the mux/divider register
++ * @hid_width:	number of bits in half integer divider
++ * @hid_shift:	lowest bit of hid value field
++ * @src_width:	number of bits in source select
++ * @src_shift:	lowest bit of source select field
++ * @div:	the divider raw configuration value
++ * @src_sel:	the mux index which will be used if the clock is enabled
++ * @safe_src:	the safe source mux index for this clock
++ * @safe_freq:	When switching rates from A to B, the mux div clock will
++ *		instead switch from A -> safe_freq -> B. This allows the
++ *		mux_div clock to change rates while enabled, even if this
++ *		behavior is not supported by the parent clocks.
++ *		If changing the rate of parent A also causes the rate of
++ *		parent B to change, then safe_freq must be defined.
++ *		safe_freq is expected to have a source clock which is always
++ *		on and runs at only one rate.
++ * @parent_map:	pointer to parent_map struct
++ * @clkr:	handle between common and hardware-specific interfaces
++ */
++
++struct clk_regmap_mux_div {
++	u32				reg_offset;
++	u32				hid_width;
++	u32				hid_shift;
++	u32				src_width;
++	u32				src_shift;
++	u32				div;
++	u32				src_sel;
++	u32				safe_src;
++	unsigned long			safe_freq;
++	const struct parent_map		*parent_map;
++	struct clk_regmap		clkr;
++};
++
++#define to_clk_regmap_mux_div(_hw) \
++	container_of(to_clk_regmap(_hw), struct clk_regmap_mux_div, clkr)
++
++extern const struct clk_ops clk_regmap_mux_div_ops;
++
++#endif



More information about the lede-commits mailing list