diff options
Diffstat (limited to 'drivers/clk/qcom/common.c')
| -rw-r--r-- | drivers/clk/qcom/common.c | 239 |
1 files changed, 3 insertions, 236 deletions
diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index f6ce3106ab6f..8ecb207a97e5 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -1,5 +1,7 @@ /* - * Copyright (c) 2013-2014, 2016-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2014, 2016-2017, + * + * 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 @@ -287,239 +289,4 @@ int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc) } EXPORT_SYMBOL_GPL(qcom_cc_probe); -/* Debugfs Support */ -static struct clk_hw *measure; - -DEFINE_SPINLOCK(clk_reg_lock); - -/* Sample clock for 'ticks' reference clock ticks. */ -static u32 run_measurement(unsigned ticks, struct regmap *regmap, - u32 ctl_reg, u32 status_reg) -{ - u32 regval; - - /* Stop counters and set the XO4 counter start value. */ - regmap_write(regmap, ctl_reg, ticks); - - regmap_read(regmap, status_reg, ®val); - - /* Wait for timer to become ready. */ - while ((regval & BIT(25)) != 0) { - cpu_relax(); - regmap_read(regmap, status_reg, ®val); - } - - /* Run measurement and wait for completion. */ - regmap_write(regmap, ctl_reg, (BIT(20)|ticks)); - regmap_read(regmap, ctl_reg, ®val); - - regmap_read(regmap, status_reg, ®val); - - while ((regval & BIT(25)) == 0) { - cpu_relax(); - regmap_read(regmap, status_reg, ®val); - } - - /* Return measured ticks. */ - regmap_read(regmap, status_reg, ®val); - regval &= BM(24, 0); - - return regval; -} - -/* - * Perform a hardware rate measurement for a given clock. - * FOR DEBUG USE ONLY: Measurements take ~15 ms! - */ -static unsigned long clk_debug_mux_measure_rate(struct clk_hw *hw) -{ - unsigned long flags, ret = 0; - u32 gcc_xo4_reg, sample_ticks = 0x10000, multiplier; - u64 raw_count_short, raw_count_full; - struct clk_debug_mux *meas = to_clk_measure(hw); - struct measure_clk_data *data = meas->priv; - - clk_prepare_enable(data->cxo); - - spin_lock_irqsave(&clk_reg_lock, flags); - - multiplier = meas->multiplier + 1; - - /* Enable CXO/4 and RINGOSC branch. */ - regmap_read(meas->regmap[GCC], data->xo_div4_cbcr, &gcc_xo4_reg); - gcc_xo4_reg |= BIT(0); - regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg); - - /* - * The ring oscillator counter will not reset if the measured clock - * is not running. To detect this, run a short measurement before - * the full measurement. If the raw results of the two are the same - * then the clock must be off. - */ - - /* Run a short measurement. (~1 ms) */ - raw_count_short = run_measurement(0x1000, meas->regmap[GCC], - data->ctl_reg, data->status_reg); - - /* Run a full measurement. (~14 ms) */ - raw_count_full = run_measurement(sample_ticks, meas->regmap[GCC], - data->ctl_reg, data->status_reg); - - gcc_xo4_reg &= ~BIT(0); - regmap_write(meas->regmap[GCC], data->xo_div4_cbcr, gcc_xo4_reg); - - /* Return 0 if the clock is off. */ - if (raw_count_full == raw_count_short) - ret = 0; - else { - /* Compute rate in Hz. */ - raw_count_full = ((raw_count_full * 10) + 15) * 4800000; - do_div(raw_count_full, ((sample_ticks * 10) + 35)); - ret = (raw_count_full * multiplier); - } - - spin_unlock_irqrestore(&clk_reg_lock, flags); - - clk_disable_unprepare(data->cxo); - - return ret; -} - -static u8 clk_debug_mux_get_parent(struct clk_hw *hw) -{ - struct clk_debug_mux *meas = to_clk_measure(hw); - int i, num_parents = clk_hw_get_num_parents(hw); - - for (i = 0; i < num_parents; i++) { - if (!strcmp(meas->parent[i].parents, - hw->init->parent_names[i])) { - pr_debug("%s :Clock name %s index %d\n", __func__, - hw->init->name, i); - return i; - } - } - - return 0; -} - -static int clk_debug_mux_set_parent(struct clk_hw *hw, u8 index) -{ - struct clk_debug_mux *meas = to_clk_measure(hw); - unsigned long lsb = 0; - u32 regval = 0; - int dbg_cc = 0; - - dbg_cc = meas->parent[index].dbg_cc; - - if (dbg_cc != GCC) { - regmap_read(meas->regmap[dbg_cc], 0x0, ®val); - - /* Clear & Set post divider bits */ - if (meas->parent[index].post_div_mask) { - regval &= ~meas->parent[index].post_div_mask; - lsb = find_first_bit((unsigned long *) - &meas->parent[index].post_div_mask, 32); - regval |= (meas->parent[index].post_div_val << lsb) & - meas->parent[index].post_div_mask; - meas->multiplier = meas->parent[index].post_div_val; - } - - if (meas->parent[index].mask) - regval &= ~meas->parent[index].mask << - meas->parent[index].shift; - else - regval &= ~meas->mask; - - regval |= (meas->parent[index].next_sel & meas->mask); - - if (meas->parent[index].en_mask == 0xFF) - /* Skip en_mask */ - regval = regval; - else if (meas->parent[index].en_mask) - regval |= meas->parent[index].en_mask; - else - regval |= meas->en_mask; - - regmap_write(meas->regmap[dbg_cc], 0x0, regval); - } - - /* update the debug sel for GCC */ - regmap_read(meas->regmap[GCC], meas->debug_offset, ®val); - - /* clear post divider bits */ - regval &= ~BM(15, 12); - lsb = find_first_bit((unsigned long *) - &meas->parent[index].post_div_mask, 32); - regval |= (meas->parent[index].post_div_val << lsb) & - meas->parent[index].post_div_mask; - meas->multiplier = meas->parent[index].post_div_val; - regval &= ~meas->mask; - regval |= (meas->parent[index].sel & meas->mask); - regval |= meas->en_mask; - - regmap_write(meas->regmap[GCC], meas->debug_offset, regval); - - return 0; -} - -const struct clk_ops clk_debug_mux_ops = { - .get_parent = clk_debug_mux_get_parent, - .set_parent = clk_debug_mux_set_parent, -}; -EXPORT_SYMBOL_GPL(clk_debug_mux_ops); - -static int clk_debug_measure_get(void *data, u64 *val) -{ - struct clk_hw *hw = data, *par; - int ret = 0; - unsigned long meas_rate, sw_rate; - - ret = clk_set_parent(measure->clk, hw->clk); - if (!ret) { - par = measure; - while (par && par != hw) { - if (par->init->ops->enable) - par->init->ops->enable(par); - par = clk_hw_get_parent(par); - } - *val = clk_debug_mux_measure_rate(measure); - } - - meas_rate = clk_get_rate(hw->clk); - sw_rate = clk_get_rate(clk_hw_get_parent(measure)->clk); - if (sw_rate && meas_rate >= (sw_rate * 2)) - *val *= DIV_ROUND_CLOSEST(meas_rate, sw_rate); - - return ret; -} - -DEFINE_SIMPLE_ATTRIBUTE(clk_measure_fops, clk_debug_measure_get, - NULL, "%lld\n"); - -void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry) -{ - if (IS_ERR_OR_NULL(measure)) - return; - - if (clk_set_parent(measure->clk, hw->clk)) - return; - - debugfs_create_file("measure", S_IRUGO, dentry, hw, - &clk_measure_fops); -} -EXPORT_SYMBOL_GPL(clk_debug_measure_add); - -int clk_register_debug(struct clk_hw *hw, struct dentry *dentry) -{ - if (IS_ERR_OR_NULL(measure)) { - if (hw->init->flags & CLK_IS_MEASURE) - measure = hw; - if (!IS_ERR_OR_NULL(measure)) - clk_debug_measure_add(hw, dentry); - } - - return 0; -} -EXPORT_SYMBOL_GPL(clk_register_debug); - MODULE_LICENSE("GPL v2"); |
