From 8749fb7894601011811dd7409b48401b3aafe0dd Mon Sep 17 00:00:00 2001 From: Pavel Kubelun Date: Wed, 2 Nov 2016 22:08:45 +0300 Subject: [PATCH] ipq806x: update clk-qcom patches Preparing for cpufreq driver switch to generic cpufreq-dt Signed-off-by: Pavel Kubelun --- ...PI-register-brd-clks-bckwrds-cmptblt.patch | 135 ++++++ .../002-make-reset-control-ops-const.patch | 35 ++ ...pport-for-High-Frequency-PLLs-HFPLLs.patch | 2 +- ...lk-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(-) create mode 100644 target/linux/ipq806x/patches-4.4/001-add-API-register-brd-clks-bckwrds-cmptblt.patch create mode 100644 target/linux/ipq806x/patches-4.4/002-make-reset-control-ops-const.patch create mode 100644 target/linux/ipq806x/patches-4.4/175-add-regmap-mux-div.patch 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 0000000000..b9056e9618 --- /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 +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 +Signed-off-by: Stephen Boyd +--- + 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 + #include + #include ++#include + + #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 0000000000..6261125473 --- /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 +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 +Signed-off-by: Stephen Boyd +--- + 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 4924e5ff4b..7e964490e0 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 ecf1ef589c..b1720b0898 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 0000000000..b5c23b5165 --- /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 +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 +--- + 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 ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++#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