diff options
| author | Arun KS <arunks@codeaurora.org> | 2017-04-06 15:45:04 +0530 |
|---|---|---|
| committer | Arun KS <arunks@codeaurora.org> | 2017-04-06 15:45:04 +0530 |
| commit | e8a49b120cbc3a0472aebcd0f8b06cf95f775a54 (patch) | |
| tree | 7b58ec70803bbfda534ed2a8b50d321a1ecb22ba /drivers/clk | |
| parent | 9a0d24cf9f0e1d4cad7cf92f7e50939fb605e075 (diff) | |
| parent | a3851309dbf7e919b27e2ec927ba3f6350347dff (diff) | |
Merge remote-tracking branch 'remotes/origin/msm-4.4' into dev/msm-4.4-8996au
Conflicts:
arch/arm/boot/dts/qcom/msm8996pro.dtsi
arch/arm64/kernel/Makefile
drivers/leds/leds-qpnp-flash.c
sound/soc/msm/apq8096-auto.c
Change-Id: Idea5d05fec354b8f38ea70643decb03f7b80ddb7
Signed-off-by: Arun KS <arunks@codeaurora.org>
Diffstat (limited to 'drivers/clk')
33 files changed, 676 insertions, 387 deletions
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 6029313aa995..35ab89fe9d7b 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -1082,7 +1082,9 @@ static void bcm2835_pll_divider_off(struct clk_hw *hw) cprman_write(cprman, data->cm_reg, (cprman_read(cprman, data->cm_reg) & ~data->load_mask) | data->hold_mask); - cprman_write(cprman, data->a2w_reg, A2W_PLL_CHANNEL_DISABLE); + cprman_write(cprman, data->a2w_reg, + cprman_read(cprman, data->a2w_reg) | + A2W_PLL_CHANNEL_DISABLE); spin_unlock(&cprman->regs_lock); } diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 0c83ffc22dd2..f45d923ed3cd 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -354,7 +354,7 @@ static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate, /* if read only, just return current value */ if (divider->flags & CLK_DIVIDER_READ_ONLY) { - bestdiv = readl(divider->reg) >> divider->shift; + bestdiv = clk_readl(divider->reg) >> divider->shift; bestdiv &= div_mask(divider->width); bestdiv = _get_div(divider->table, bestdiv, divider->flags, divider->width); diff --git a/drivers/clk/clk-qoriq.c b/drivers/clk/clk-qoriq.c index 7bc1c4527ae4..a5070f9cb0d4 100644 --- a/drivers/clk/clk-qoriq.c +++ b/drivers/clk/clk-qoriq.c @@ -700,6 +700,7 @@ static struct clk * __init create_mux_common(struct clockgen *cg, struct mux_hwclock *hwc, const struct clk_ops *ops, unsigned long min_rate, + unsigned long max_rate, unsigned long pct80_rate, const char *fmt, int idx) { @@ -728,6 +729,8 @@ static struct clk * __init create_mux_common(struct clockgen *cg, continue; if (rate < min_rate) continue; + if (rate > max_rate) + continue; parent_names[j] = div->name; hwc->parent_to_clksel[j] = i; @@ -759,14 +762,18 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx) struct mux_hwclock *hwc; const struct clockgen_pll_div *div; unsigned long plat_rate, min_rate; - u64 pct80_rate; + u64 max_rate, pct80_rate; u32 clksel; hwc = kzalloc(sizeof(*hwc), GFP_KERNEL); if (!hwc) return NULL; - hwc->reg = cg->regs + 0x20 * idx; + if (cg->info.flags & CG_VER3) + hwc->reg = cg->regs + 0x70000 + 0x20 * idx; + else + hwc->reg = cg->regs + 0x20 * idx; + hwc->info = cg->info.cmux_groups[cg->info.cmux_to_group[idx]]; /* @@ -783,8 +790,8 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx) return NULL; } - pct80_rate = clk_get_rate(div->clk); - pct80_rate *= 8; + max_rate = clk_get_rate(div->clk); + pct80_rate = max_rate * 8; do_div(pct80_rate, 10); plat_rate = clk_get_rate(cg->pll[PLATFORM_PLL].div[PLL_DIV1].clk); @@ -794,7 +801,7 @@ static struct clk * __init create_one_cmux(struct clockgen *cg, int idx) else min_rate = plat_rate / 2; - return create_mux_common(cg, hwc, &cmux_ops, min_rate, + return create_mux_common(cg, hwc, &cmux_ops, min_rate, max_rate, pct80_rate, "cg-cmux%d", idx); } @@ -809,7 +816,7 @@ static struct clk * __init create_one_hwaccel(struct clockgen *cg, int idx) hwc->reg = cg->regs + 0x20 * idx + 0x10; hwc->info = cg->info.hwaccel[idx]; - return create_mux_common(cg, hwc, &hwaccel_ops, 0, 0, + return create_mux_common(cg, hwc, &hwaccel_ops, 0, ULONG_MAX, 0, "cg-hwaccel%d", idx); } diff --git a/drivers/clk/clk-wm831x.c b/drivers/clk/clk-wm831x.c index 43f9d15255f4..763aed2de893 100644 --- a/drivers/clk/clk-wm831x.c +++ b/drivers/clk/clk-wm831x.c @@ -247,7 +247,7 @@ static int wm831x_clkout_is_prepared(struct clk_hw *hw) if (ret < 0) { dev_err(wm831x->dev, "Unable to read CLOCK_CONTROL_1: %d\n", ret); - return true; + return false; } return (ret & WM831X_CLKOUT_ENA) != 0; diff --git a/drivers/clk/clk-xgene.c b/drivers/clk/clk-xgene.c index 10224b01b97c..b134a8b15e2c 100644 --- a/drivers/clk/clk-xgene.c +++ b/drivers/clk/clk-xgene.c @@ -351,8 +351,8 @@ static int xgene_clk_set_rate(struct clk_hw *hw, unsigned long rate, /* Set new divider */ data = xgene_clk_read(pclk->param.divider_reg + pclk->param.reg_divider_offset); - data &= ~((1 << pclk->param.reg_divider_width) - 1) - << pclk->param.reg_divider_shift; + data &= ~(((1 << pclk->param.reg_divider_width) - 1) + << pclk->param.reg_divider_shift); data |= divider; xgene_clk_write(data, pclk->param.divider_reg + pclk->param.reg_divider_offset); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index e8e48015d3af..4a9e034f939f 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -2874,8 +2874,6 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) goto err_out; } - clk_debug_measure_add(core->hw, core->dentry); - ret = 0; goto out; @@ -3005,10 +3003,8 @@ static int __init clk_debug_init(void) return -ENOMEM; mutex_lock(&clk_debug_lock); - hlist_for_each_entry(core, &clk_debug_list, debug_node) { - clk_register_debug(core->hw, core->dentry); + hlist_for_each_entry(core, &clk_debug_list, debug_node) clk_debug_create_one(core, rootdir); - } inited = 1; mutex_unlock(&clk_debug_lock); diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h index 25bf4bc85f5c..87af7af7aac2 100644 --- a/drivers/clk/clk.h +++ b/drivers/clk/clk.h @@ -23,8 +23,6 @@ void __clk_free_clk(struct clk *clk); /* Debugfs API to print the enabled clocks */ void clock_debug_print_enabled(void); -int clk_register_debug(struct clk_hw *hw, struct dentry *dentry); -void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry); void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f); #else diff --git a/drivers/clk/imx/clk-imx31.c b/drivers/clk/imx/clk-imx31.c index 6a964144a5b5..6a49ba2b9671 100644 --- a/drivers/clk/imx/clk-imx31.c +++ b/drivers/clk/imx/clk-imx31.c @@ -157,10 +157,8 @@ static void __init _mx31_clocks_init(unsigned long fref) } } -int __init mx31_clocks_init(void) +int __init mx31_clocks_init(unsigned long fref) { - u32 fref = 26000000; /* default */ - _mx31_clocks_init(fref); clk_register_clkdev(clk[gpt_gate], "per", "imx-gpt.0"); diff --git a/drivers/clk/imx/clk-imx35.c b/drivers/clk/imx/clk-imx35.c index b0978d3b83e2..d302ed3b8225 100644 --- a/drivers/clk/imx/clk-imx35.c +++ b/drivers/clk/imx/clk-imx35.c @@ -115,7 +115,7 @@ static void __init _mx35_clocks_init(void) } clk[ckih] = imx_clk_fixed("ckih", 24000000); - clk[ckil] = imx_clk_fixed("ckih", 32768); + clk[ckil] = imx_clk_fixed("ckil", 32768); clk[mpll] = imx_clk_pllv1(IMX_PLLV1_IMX35, "mpll", "ckih", base + MX35_CCM_MPCTL); clk[ppll] = imx_clk_pllv1(IMX_PLLV1_IMX35, "ppll", "ckih", base + MX35_CCM_PPCTL); diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index c1935081d34a..aab64205d866 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -550,6 +550,24 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) if (IS_ENABLED(CONFIG_PCI_IMX6)) clk_set_parent(clk[IMX6QDL_CLK_LVDS1_SEL], clk[IMX6QDL_CLK_SATA_REF_100M]); + /* + * Initialize the GPU clock muxes, so that the maximum specified clock + * rates for the respective SoC are not exceeded. + */ + if (clk_on_imx6dl()) { + clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL], + clk[IMX6QDL_CLK_PLL2_PFD1_594M]); + clk_set_parent(clk[IMX6QDL_CLK_GPU2D_CORE_SEL], + clk[IMX6QDL_CLK_PLL2_PFD1_594M]); + } else if (clk_on_imx6q()) { + clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL], + clk[IMX6QDL_CLK_MMDC_CH0_AXI]); + clk_set_parent(clk[IMX6QDL_CLK_GPU3D_SHADER_SEL], + clk[IMX6QDL_CLK_PLL2_PFD1_594M]); + clk_set_parent(clk[IMX6QDL_CLK_GPU2D_CORE_SEL], + clk[IMX6QDL_CLK_PLL3_USB_OTG]); + } + imx_register_uart_clocks(uart_clks); } CLK_OF_DECLARE(imx6q, "fsl,imx6q-ccm", imx6q_clocks_init); diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c index 251533d87c65..f261b1d292c7 100644 --- a/drivers/clk/mmp/clk-of-mmp2.c +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -313,7 +313,7 @@ static void __init mmp2_clk_init(struct device_node *np) } pxa_unit->apmu_base = of_iomap(np, 1); - if (!pxa_unit->mpmu_base) { + if (!pxa_unit->apmu_base) { pr_err("failed to map apmu registers\n"); return; } diff --git a/drivers/clk/mmp/clk-of-pxa168.c b/drivers/clk/mmp/clk-of-pxa168.c index 64eaf4141c69..427f4bb08665 100644 --- a/drivers/clk/mmp/clk-of-pxa168.c +++ b/drivers/clk/mmp/clk-of-pxa168.c @@ -262,7 +262,7 @@ static void __init pxa168_clk_init(struct device_node *np) } pxa_unit->apmu_base = of_iomap(np, 1); - if (!pxa_unit->mpmu_base) { + if (!pxa_unit->apmu_base) { pr_err("failed to map apmu registers\n"); return; } diff --git a/drivers/clk/mmp/clk-of-pxa910.c b/drivers/clk/mmp/clk-of-pxa910.c index 13d6173326a4..cdf5ba566d3b 100644 --- a/drivers/clk/mmp/clk-of-pxa910.c +++ b/drivers/clk/mmp/clk-of-pxa910.c @@ -282,7 +282,7 @@ static void __init pxa910_clk_init(struct device_node *np) } pxa_unit->apmu_base = of_iomap(np, 1); - if (!pxa_unit->mpmu_base) { + if (!pxa_unit->apmu_base) { pr_err("failed to map apmu registers\n"); return; } @@ -294,7 +294,7 @@ static void __init pxa910_clk_init(struct device_node *np) } pxa_unit->apbcp_base = of_iomap(np, 3); - if (!pxa_unit->mpmu_base) { + if (!pxa_unit->apbcp_base) { pr_err("failed to map apbcp registers\n"); return; } diff --git a/drivers/clk/msm/clock-gcc-8998.c b/drivers/clk/msm/clock-gcc-8998.c index f9d713a22c76..b1c8cc43769f 100644 --- a/drivers/clk/msm/clock-gcc-8998.c +++ b/drivers/clk/msm/clock-gcc-8998.c @@ -42,6 +42,7 @@ static void __iomem *virt_dbgbase; #define gpll0_out_main_source_val 1 #define gpll0_ao_source_val 1 #define gpll4_out_main_source_val 5 +#define gpll0_early_div_source_val 6 #define FIXDIV(div) (div ? (2 * (div) - 1) : (0)) @@ -164,6 +165,7 @@ static struct pll_vote_clk gpll0_ao = { }; DEFINE_EXT_CLK(gpll0_out_main, &gpll0.c); +DEFINE_EXT_CLK(gpll0_early_div, &gpll0.c); static struct local_vote_clk gcc_mmss_gpll0_clk = { .cbcr_reg = GCC_APCS_CLOCK_BRANCH_ENA_VOTE_1, @@ -328,7 +330,7 @@ static struct clk_freq_tbl ftbl_blsp_qup_spi_apps_clk_src[] = { F( 960000, cxo_clk_src, 10, 1, 2), F( 4800000, cxo_clk_src, 4, 0, 0), F( 9600000, cxo_clk_src, 2, 0, 0), - F( 15000000, gpll0_out_main, 10, 1, 4), + F( 15000000, gpll0_early_div, 5, 1, 4), F( 19200000, cxo_clk_src, 1, 0, 0), F( 25000000, gpll0_out_main, 12, 1, 2), F( 50000000, gpll0_out_main, 12, 0, 0), @@ -496,10 +498,10 @@ static struct rcg_clk blsp1_qup6_spi_apps_clk_src = { }; static struct clk_freq_tbl ftbl_blsp_uart_apps_clk_src[] = { - F( 3686400, gpll0_out_main, 1, 96, 15625), - F( 7372800, gpll0_out_main, 1, 192, 15625), - F( 14745600, gpll0_out_main, 1, 384, 15625), - F( 16000000, gpll0_out_main, 5, 2, 15), + F( 3686400, gpll0_early_div, 1, 192, 15625), + F( 7372800, gpll0_early_div, 1, 384, 15625), + F( 14745600, gpll0_early_div, 1, 768, 15625), + F( 16000000, gpll0_early_div, 1, 4, 75), F( 19200000, cxo_clk_src, 1, 0, 0), F( 24000000, gpll0_out_main, 5, 1, 5), F( 32000000, gpll0_out_main, 1, 4, 75), @@ -2732,6 +2734,8 @@ static int msm_gcc_8998_probe(struct platform_device *pdev) if (ret) return ret; + gpll0_early_div.c.rate = 300000000; + ret = enable_rpm_scaling(); if (ret < 0) return ret; diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 317091a8003d..4c18181c047c 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -12,7 +12,7 @@ clk-qcom-y += clk-regmap-mux.o clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o clk-qcom-y += clk-hfpll.o clk-qcom-y += reset.o clk-voter.o -clk-qcom-y += clk-dummy.o +clk-qcom-y += clk-dummy.o clk-debug.o clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o gdsc-regulator.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c index ca6010db8d78..3e9cd9909b86 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -21,6 +21,7 @@ #include <linux/regmap.h> #include "clk-branch.h" +#include "clk-debug.h" #include "clk-regmap.h" #include "common.h" @@ -250,6 +251,7 @@ const struct clk_ops clk_branch2_ops = { .is_enabled = clk_is_enabled_regmap, .set_flags = clk_branch_set_flags, .list_registers = clk_branch2_list_registers, + .debug_init = clk_debug_measure_add, }; EXPORT_SYMBOL_GPL(clk_branch2_ops); @@ -384,6 +386,7 @@ const struct clk_ops clk_gate2_ops = { .is_enabled = clk_is_enabled_regmap, .list_registers = clk_gate2_list_registers, .set_flags = clk_gate2_set_flags, + .debug_init = clk_debug_measure_add, }; EXPORT_SYMBOL_GPL(clk_gate2_ops); diff --git a/drivers/clk/qcom/clk-cpu-osm.c b/drivers/clk/qcom/clk-cpu-osm.c index 6b00bee337a1..f82ddc3b008b 100644 --- a/drivers/clk/qcom/clk-cpu-osm.c +++ b/drivers/clk/qcom/clk-cpu-osm.c @@ -39,6 +39,7 @@ #include "common.h" #include "clk-regmap.h" #include "clk-rcg.h" +#include "clk-debug.h" enum { LMH_LITE_CLK_SRC, @@ -757,6 +758,7 @@ static struct clk_ops clk_ops_cpu_osm = { .round_rate = clk_osm_round_rate, .list_rate = clk_osm_list_rate, .recalc_rate = clk_osm_recalc_rate, + .debug_init = clk_debug_measure_add, }; static const struct parent_map gcc_parent_map_1[] = { diff --git a/drivers/clk/qcom/clk-debug.c b/drivers/clk/qcom/clk-debug.c new file mode 100644 index 000000000000..50d0d01188ed --- /dev/null +++ b/drivers/clk/qcom/clk-debug.c @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2013-2014, 2016-2017, + * + * The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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/clk.h> +#include <linux/export.h> +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/platform_device.h> +#include <linux/clk-provider.h> +#include <linux/of.h> + +#include "clk-regmap.h" +#include "clk-debug.h" +#include "common.h" + +static struct clk_hw *measure; + +static DEFINE_SPINLOCK(clk_reg_lock); +static DEFINE_MUTEX(clk_debug_lock); + +#define TCXO_DIV_4_HZ 4800000 +#define SAMPLE_TICKS_1_MS 0x1000 +#define SAMPLE_TICKS_14_MS 0x10000 + +#define XO_DIV4_CNT_DONE BIT(25) +#define CNT_EN BIT(20) +#define MEASURE_CNT BM(24, 0) + +/* 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 & XO_DIV4_CNT_DONE) != 0) { + cpu_relax(); + regmap_read(regmap, status_reg, ®val); + } + + /* Run measurement and wait for completion. */ + regmap_write(regmap, ctl_reg, (CNT_EN|ticks)); + + regmap_read(regmap, status_reg, ®val); + + while ((regval & XO_DIV4_CNT_DONE) == 0) { + cpu_relax(); + regmap_read(regmap, status_reg, ®val); + } + + /* Return measured ticks. */ + regmap_read(regmap, status_reg, ®val); + regval &= MEASURE_CNT; + + 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, 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(SAMPLE_TICKS_1_MS, meas->regmap[GCC], + data->ctl_reg, data->status_reg); + + /* Run a full measurement. (~14 ms) */ + raw_count_full = run_measurement(SAMPLE_TICKS_14_MS, 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) * TCXO_DIV_4_HZ; + do_div(raw_count_full, ((SAMPLE_TICKS_14_MS * 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(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; + + mutex_lock(&clk_debug_lock); + + 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); + + mutex_unlock(&clk_debug_lock); + + return ret; +} + +DEFINE_SIMPLE_ATTRIBUTE(clk_measure_fops, clk_debug_measure_get, + NULL, "%lld\n"); + +int clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry) +{ + if (IS_ERR_OR_NULL(measure)) { + pr_err_once("Please check if `measure` clk is registered!!!\n"); + return 0; + } + + if (clk_set_parent(measure->clk, hw->clk)) + return 0; + + debugfs_create_file("clk_measure", S_IRUGO, dentry, hw, + &clk_measure_fops); + return 0; +} +EXPORT_SYMBOL(clk_debug_measure_add); + +int clk_register_debug(struct clk_hw *hw) +{ + if (IS_ERR_OR_NULL(measure)) { + if (hw->init->flags & CLK_IS_MEASURE) { + measure = hw; + return 0; + } + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL(clk_register_debug); + diff --git a/drivers/clk/qcom/clk-debug.h b/drivers/clk/qcom/clk-debug.h new file mode 100644 index 000000000000..40e8730a9466 --- /dev/null +++ b/drivers/clk/qcom/clk-debug.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2014, 2016-2017, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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_DEBUG_H__ +#define __QCOM_CLK_DEBUG_H__ + +#include "../clk.h" + +/* Debugfs Measure Clocks */ + +/** + * struct measure_clk_data - Structure of clk measure + * + * @cxo: XO clock. + * @xo_div4_cbcr: offset of debug XO/4 div register. + * @ctl_reg: offset of debug control register. + * @status_reg: offset of debug status register. + * + */ +struct measure_clk_data { + struct clk *cxo; + u32 xo_div4_cbcr; + u32 ctl_reg; + u32 status_reg; +}; + +/** + * List of Debug clock controllers. + */ +enum debug_cc { + GCC, + MMCC, + GPU, + CPU, +}; + +/** + * struct clk_src - Struture of clock source for debug mux + * + * @parents: clock name to be used as parent for debug mux. + * @sel: debug mux index at global clock controller. + * @dbg_cc: indicates the clock controller for recursive debug clock + * controllers. + * @next_sel: indicates the debug mux index at recursive debug mux. + * @mask: indicates the mask required at recursive debug mux. + * @shift: indicates the shift required at recursive debug mux. + * @en_mask: indicates the enable bit mask at recursive debug mux. + * Incase the recursive debug mux does not have a enable bit, + * 0xFF should be used to indicate the same, otherwise global + * enable bit would be used. + * @post_div_mask: indicates the post div mask to be used at debug/recursive + * debug mux. + * @post_div_val: indicates the post div value to be used at debug/recursive + * debug mux. + */ +struct clk_src { + const char *parents; + int sel; + enum debug_cc dbg_cc; + int next_sel; + u32 mask; + u32 shift; + u32 en_mask; + u32 post_div_mask; + u32 post_div_val; +}; + +#define MUX_SRC_LIST(...) \ + .parent = (struct clk_src[]){__VA_ARGS__}, \ + .num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__})) + +/** + * struct clk_debug_mux - Struture of clock debug mux + * + * @parent: structure of clk_src + * @num_parents: number of parents + * @regmap: regmaps of debug mux + * @num_parent_regmap: number of regmap of debug mux + * @priv: private measure_clk_data to be used by debug mux + * @en_mask: indicates the enable bit mask at global clock + * controller debug mux. + * @mask: indicates the mask to be used at global clock + * controller debug mux. + * @debug_offset: debug mux offset. + * @hw: handle between common and hardware-specific interfaces. + * @multiplier: internally used by debug mux as post div multiplier. + */ +struct clk_debug_mux { + struct clk_src *parent; + int num_parents; + struct regmap **regmap; + int num_parent_regmap; + void *priv; + u32 en_mask; + u32 mask; + u32 debug_offset; + struct clk_hw hw; + + /* internal */ + u32 multiplier; +}; + +#define to_clk_measure(_hw) container_of((_hw), struct clk_debug_mux, hw) + +extern const struct clk_ops clk_debug_mux_ops; + +int clk_register_debug(struct clk_hw *hw); +int clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry); + +#endif diff --git a/drivers/clk/qcom/clk-dummy.c b/drivers/clk/qcom/clk-dummy.c index e06a829e508c..c08136563e31 100644 --- a/drivers/clk/qcom/clk-dummy.c +++ b/drivers/clk/qcom/clk-dummy.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,6 +17,8 @@ #include <linux/slab.h> #include <linux/reset-controller.h> +#include "clk-debug.h" + struct clk_dummy { struct clk_hw hw; struct reset_controller_dev reset; @@ -65,6 +67,7 @@ struct clk_ops clk_dummy_ops = { .round_rate = dummy_clk_round_rate, .recalc_rate = dummy_clk_recalc_rate, .set_flags = dummy_clk_set_flags, + .debug_init = clk_debug_measure_add, }; EXPORT_SYMBOL_GPL(clk_dummy_ops); diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index 58fbd07e6f15..0bcada9aac5d 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013, 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 @@ -192,5 +192,6 @@ extern const struct clk_ops clk_pixel_ops; extern const struct clk_ops clk_gfx3d_ops; extern const struct clk_ops clk_gfx3d_src_ops; extern const struct clk_ops clk_dp_ops; +extern const struct clk_ops clk_esc_ops; #endif diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index 9e5c0b6f7a0e..ff0c8327fabe 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -513,7 +513,7 @@ static int clk_rcg2_enable(struct clk_hw *hw) * is always on while APPS is online. Therefore, the RCG can safely be * switched. */ - rate = clk_get_rate(hw->clk); + rate = rcg->current_freq; f = qcom_find_freq(rcg->freq_tbl, rate); if (!f) return -EINVAL; @@ -580,6 +580,9 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) rcg->curr_index = 0; else { f = qcom_find_freq(rcg->freq_tbl, rcg->current_freq); + if (!f) + return -EINVAL; + rcg->curr_index = qcom_find_src_index(hw, rcg->parent_map, f->src); @@ -627,6 +630,9 @@ static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) rcg->new_index, false); } + /* Update current frequency with the frequency requested. */ + rcg->current_freq = rate; + return ret; } @@ -1343,3 +1349,73 @@ const struct clk_ops clk_gfx3d_src_ops = { .list_registers = clk_rcg2_list_registers, }; EXPORT_SYMBOL_GPL(clk_gfx3d_src_ops); + +static int clk_esc_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + unsigned long parent_rate, div; + u32 mask = BIT(rcg->hid_width) - 1; + struct clk_hw *p; + unsigned long rate = req->rate; + + if (rate == 0) + return -EINVAL; + + p = req->best_parent_hw; + req->best_parent_rate = parent_rate = clk_hw_round_rate(p, rate); + + div = ((2 * parent_rate) / rate) - 1; + div = min_t(u32, div, mask); + + req->rate = calc_rate(parent_rate, 0, 0, 0, div); + + return 0; +} + +static int clk_esc_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + struct freq_tbl f = { 0 }; + unsigned long div; + int i, num_parents = clk_hw_get_num_parents(hw); + u32 mask = BIT(rcg->hid_width) - 1; + u32 cfg; + + div = ((2 * parent_rate) / rate) - 1; + div = min_t(u32, div, mask); + + f.pre_div = div; + + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg); + cfg &= CFG_SRC_SEL_MASK; + cfg >>= CFG_SRC_SEL_SHIFT; + + for (i = 0; i < num_parents; i++) { + if (cfg == rcg->parent_map[i].cfg) { + f.src = rcg->parent_map[i].src; + return clk_rcg2_configure(rcg, &f); + } + } + + return -EINVAL; +} + +static int clk_esc_set_rate_and_parent(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate, u8 index) +{ + return clk_esc_set_rate(hw, rate, parent_rate); +} + +const struct clk_ops clk_esc_ops = { + .is_enabled = clk_rcg2_is_enabled, + .get_parent = clk_rcg2_get_parent, + .set_parent = clk_rcg2_set_parent, + .recalc_rate = clk_rcg2_recalc_rate, + .determine_rate = clk_esc_determine_rate, + .set_rate = clk_esc_set_rate, + .set_rate_and_parent = clk_esc_set_rate_and_parent, + .list_registers = clk_rcg2_list_registers, +}; +EXPORT_SYMBOL(clk_esc_ops); diff --git a/drivers/clk/qcom/clk-smd-rpm.c b/drivers/clk/qcom/clk-smd-rpm.c index 0d76b6b28985..8dcdf2929ca6 100644 --- a/drivers/clk/qcom/clk-smd-rpm.c +++ b/drivers/clk/qcom/clk-smd-rpm.c @@ -30,6 +30,7 @@ #include <dt-bindings/mfd/qcom-rpm.h> #include "clk-voter.h" +#include "clk-debug.h" #define QCOM_RPM_KEY_SOFTWARE_ENABLE 0x6e657773 #define QCOM_RPM_KEY_PIN_CTRL_CLK_BUFFER_ENABLE_KEY 0x62636370 @@ -463,6 +464,7 @@ static const struct clk_ops clk_smd_rpm_ops = { .round_rate = clk_smd_rpm_round_rate, .recalc_rate = clk_smd_rpm_recalc_rate, .is_enabled = clk_smd_rpm_is_enabled, + .debug_init = clk_debug_measure_add, }; static const struct clk_ops clk_smd_rpm_branch_ops = { @@ -471,6 +473,7 @@ static const struct clk_ops clk_smd_rpm_branch_ops = { .round_rate = clk_smd_rpm_round_rate, .recalc_rate = clk_smd_rpm_recalc_rate, .is_enabled = clk_smd_rpm_is_enabled, + .debug_init = clk_debug_measure_add, }; /* msm8916 */ @@ -570,6 +573,10 @@ static DEFINE_CLK_VOTER(pnoc_msmbus_clk, pnoc_clk, LONG_MAX); static DEFINE_CLK_VOTER(pnoc_msmbus_a_clk, pnoc_a_clk, LONG_MAX); static DEFINE_CLK_VOTER(pnoc_pm_clk, pnoc_clk, LONG_MAX); static DEFINE_CLK_VOTER(pnoc_sps_clk, pnoc_clk, 0); +static DEFINE_CLK_VOTER(aggre2_noc_msmbus_clk, aggre2_noc_clk, LONG_MAX); +static DEFINE_CLK_VOTER(aggre2_noc_msmbus_a_clk, aggre2_noc_a_clk, LONG_MAX); +static DEFINE_CLK_VOTER(aggre2_noc_usb_clk, aggre2_noc_clk, 19200000); +static DEFINE_CLK_VOTER(aggre2_noc_smmu_clk, aggre2_noc_clk, 1000); /* Voter Branch clocks */ static DEFINE_CLK_BRANCH_VOTER(cxo_dwc3_clk, cxo); @@ -738,6 +745,10 @@ static struct clk_hw *sdm660_clks[] = { [CXO_PIL_LPASS_CLK] = &cxo_pil_lpass_clk.hw, [CXO_PIL_CDSP_CLK] = &cxo_pil_cdsp_clk.hw, [CNOC_PERIPH_KEEPALIVE_A_CLK] = &cnoc_periph_keepalive_a_clk.hw, + [AGGR2_NOC_MSMBUS_CLK] = &aggre2_noc_msmbus_clk.hw, + [AGGR2_NOC_MSMBUS_A_CLK] = &aggre2_noc_msmbus_a_clk.hw, + [AGGR2_NOC_SMMU_CLK] = &aggre2_noc_smmu_clk.hw, + [AGGR2_NOC_USB_CLK] = &aggre2_noc_usb_clk.hw, }; static const struct rpm_smd_clk_desc rpm_clk_sdm660 = { 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"); diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 95408a47fef0..c532444a0d40 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -13,7 +13,7 @@ #ifndef __QCOM_CLK_COMMON_H__ #define __QCOM_CLK_COMMON_H__ -#include "../clk.h" +#include "clk-debug.h" struct platform_device; struct regmap_config; @@ -54,107 +54,9 @@ extern int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc); extern struct clk_ops clk_dummy_ops; -/* Debugfs Measure Clocks */ - -/** - * struct measure_clk_data - Structure of clk measure - * - * @cxo: XO clock. - * @xo_div4_cbcr: offset of debug XO/4 div register. - * @ctl_reg: offset of debug control register. - * @status_reg: offset of debug status register. - * - */ -struct measure_clk_data { - struct clk *cxo; - u32 xo_div4_cbcr; - u32 ctl_reg; - u32 status_reg; -}; - -/** - * List of Debug clock controllers. - */ -enum debug_cc { - GCC, - MMCC, - GPU, - CPU, -}; - -/** - * struct clk_src - Struture of clock source for debug mux - * - * @parents: clock name to be used as parent for debug mux. - * @sel: debug mux index at global clock controller. - * @dbg_cc: indicates the clock controller for recursive debug clock - * controllers. - * @next_sel: indicates the debug mux index at recursive debug mux. - * @mask: indicates the mask required at recursive debug mux. - * @shift: indicates the shift required at recursive debug mux. - * @en_mask: indicates the enable bit mask at recursive debug mux. - * Incase the recursive debug mux does not have a enable bit, - * 0xFF should be used to indicate the same, otherwise global - * enable bit would be used. - * @post_div_mask: indicates the post div mask to be used at debug/recursive - * debug mux. - * @post_div_val: indicates the post div value to be used at debug/recursive - * debug mux. - */ -struct clk_src { - const char *parents; - int sel; - enum debug_cc dbg_cc; - int next_sel; - u32 mask; - u32 shift; - u32 en_mask; - u32 post_div_mask; - u32 post_div_val; -}; - -#define MUX_SRC_LIST(...) \ - .parent = (struct clk_src[]){__VA_ARGS__}, \ - .num_parents = ARRAY_SIZE(((struct clk_src[]){__VA_ARGS__})) - -/** - * struct clk_debug_mux - Struture of clock debug mux - * - * @parent: structure of clk_src - * @num_parents: number of parents - * @regmap: regmaps of debug mux - * @num_parent_regmap: number of regmap of debug mux - * @priv: private measure_clk_data to be used by debug mux - * @en_mask: indicates the enable bit mask at global clock - * controller debug mux. - * @mask: indicates the mask to be used at global clock - * controller debug mux. - * @debug_offset: Start of debug mux offset. - * @hw: handle between common and hardware-specific interfaces. - * @multiplier: internally used by debug mux as post div multiplier. - */ -struct clk_debug_mux { - struct clk_src *parent; - int num_parents; - struct regmap **regmap; - int num_parent_regmap; - void *priv; - u32 en_mask; - u32 mask; - u32 debug_offset; - struct clk_hw hw; - - /* internal */ - u32 multiplier; -}; - #define BM(msb, lsb) (((((uint32_t)-1) << (31-msb)) >> (31-msb+lsb)) << lsb) #define BVAL(msb, lsb, val) (((val) << lsb) & BM(msb, lsb)) -#define to_clk_measure(_hw) container_of((_hw), struct clk_debug_mux, hw) - -extern const struct clk_ops clk_debug_mux_ops; - #define WARN_CLK(core, name, cond, fmt, ...) do { \ clk_debug_print_hw(core, NULL); \ WARN(cond, "%s: " fmt, name, ##__VA_ARGS__); \ diff --git a/drivers/clk/qcom/gcc-sdm660.c b/drivers/clk/qcom/gcc-sdm660.c index c282253da584..b55310e091af 100644 --- a/drivers/clk/qcom/gcc-sdm660.c +++ b/drivers/clk/qcom/gcc-sdm660.c @@ -27,6 +27,7 @@ #include "clk-alpha-pll.h" #include "clk-branch.h" +#include "clk-debug.h" #include "common.h" #include "clk-pll.h" #include "clk-regmap.h" @@ -3328,7 +3329,11 @@ static int clk_debug_660_probe(struct platform_device *pdev) return PTR_ERR(clk); } - dev_info(&pdev->dev, "Registered debug mux successfully\n"); + ret = clk_register_debug(&gcc_debug_mux.hw); + if (ret) + dev_err(&pdev->dev, "Could not register Measure clock\n"); + else + dev_info(&pdev->dev, "Registered debug mux successfully\n"); return ret; } diff --git a/drivers/clk/qcom/mdss/mdss-pll.c b/drivers/clk/qcom/mdss/mdss-pll.c index 833bb4a17b6a..9b7f014e1cec 100644 --- a/drivers/clk/qcom/mdss/mdss-pll.c +++ b/drivers/clk/qcom/mdss/mdss-pll.c @@ -149,6 +149,9 @@ static int mdss_pll_resource_parse(struct platform_device *pdev, } else if (!strcmp(compatible_stream, "qcom,mdss_dp_pll_sdm660")) { pll_res->target_id = MDSS_PLL_TARGET_SDM660; pll_res->pll_interface_type = MDSS_DP_PLL_SDM660; + } else if (!strcmp(compatible_stream, "qcom,mdss_dp_pll_sdm630")) { + pll_res->target_id = MDSS_PLL_TARGET_SDM630; + pll_res->pll_interface_type = MDSS_DP_PLL_SDM630; } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996")) { pll_res->pll_interface_type = MDSS_HDMI_PLL_8996; } else if (!strcmp(compatible_stream, "qcom,mdss_hdmi_pll_8996_v2")) { @@ -189,6 +192,9 @@ static int mdss_pll_clock_register(struct platform_device *pdev, case MDSS_DP_PLL_SDM660: rc = dp_pll_clock_register_14nm(pdev, pll_res); break; + case MDSS_DP_PLL_SDM630: + rc = dp_pll_clock_register_14nm(pdev, pll_res); + break; case MDSS_UNKNOWN_PLL: default: rc = -EINVAL; @@ -396,6 +402,7 @@ static const struct of_device_id mdss_pll_dt_match[] = { {.compatible = "qcom,mdss_dsi_pll_sdm660"}, {.compatible = "qcom,mdss_dp_pll_sdm660"}, {.compatible = "qcom,mdss_dsi_pll_sdm630"}, + {.compatible = "qcom,mdss_dp_pll_sdm630"}, {} }; diff --git a/drivers/clk/qcom/mdss/mdss-pll.h b/drivers/clk/qcom/mdss/mdss-pll.h index cb6918127041..369c36407ff7 100644 --- a/drivers/clk/qcom/mdss/mdss-pll.h +++ b/drivers/clk/qcom/mdss/mdss-pll.h @@ -42,6 +42,7 @@ enum { MDSS_DSI_PLL_8998, MDSS_DP_PLL_8998, MDSS_DP_PLL_SDM660, + MDSS_DP_PLL_SDM630, MDSS_HDMI_PLL_8996, MDSS_HDMI_PLL_8996_V2, MDSS_HDMI_PLL_8996_V3, diff --git a/drivers/clk/qcom/mmcc-sdm660.c b/drivers/clk/qcom/mmcc-sdm660.c index 87e6f8c6be0a..910c36c65b6a 100644 --- a/drivers/clk/qcom/mmcc-sdm660.c +++ b/drivers/clk/qcom/mmcc-sdm660.c @@ -978,12 +978,11 @@ static struct clk_rcg2 esc0_clk_src = { .mnd_width = 0, .hid_width = 5, .parent_map = mmcc_parent_map_1, - .freq_tbl = ftbl_dp_aux_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "esc0_clk_src", .parent_names = mmcc_parent_names_1, .num_parents = 4, - .ops = &clk_rcg2_ops, + .ops = &clk_esc_ops, VDD_DIG_FMAX_MAP1( LOWER, 19200000), }, @@ -994,12 +993,11 @@ static struct clk_rcg2 esc1_clk_src = { .mnd_width = 0, .hid_width = 5, .parent_map = mmcc_parent_map_1, - .freq_tbl = ftbl_dp_aux_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "esc1_clk_src", .parent_names = mmcc_parent_names_1, .num_parents = 4, - .ops = &clk_rcg2_ops, + .ops = &clk_esc_ops, VDD_DIG_FMAX_MAP1( LOWER, 19200000), }, @@ -1041,7 +1039,7 @@ static const struct freq_tbl ftbl_mclk0_clk_src[] = { F(9600000, P_CXO, 2, 0, 0), F(16666667, P_GPLL0_OUT_MAIN_DIV, 2, 1, 9), F(19200000, P_CXO, 1, 0, 0), - F(24000000, P_GPLL0_OUT_MAIN_DIV, 1, 2, 25), + F(24000000, P_MMPLL10_PLL_OUT_MAIN, 1, 1, 24), F(33333333, P_GPLL0_OUT_MAIN_DIV, 1, 1, 9), F(48000000, P_GPLL0_OUT_MAIN, 1, 2, 25), F(66666667, P_GPLL0_OUT_MAIN, 1, 1, 9), diff --git a/drivers/clk/ti/clk-3xxx.c b/drivers/clk/ti/clk-3xxx.c index 8831e1a05367..11d8aa3ec186 100644 --- a/drivers/clk/ti/clk-3xxx.c +++ b/drivers/clk/ti/clk-3xxx.c @@ -22,13 +22,6 @@ #include "clock.h" -/* - * DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks - * that are sourced by DPLL5, and both of these require this clock - * to be at 120 MHz for proper operation. - */ -#define DPLL5_FREQ_FOR_USBHOST 120000000 - #define OMAP3430ES2_ST_DSS_IDLE_SHIFT 1 #define OMAP3430ES2_ST_HSOTGUSB_IDLE_SHIFT 5 #define OMAP3430ES2_ST_SSI_IDLE_SHIFT 8 @@ -546,14 +539,21 @@ void __init omap3_clk_lock_dpll5(void) struct clk *dpll5_clk; struct clk *dpll5_m2_clk; + /* + * Errata sprz319f advisory 2.1 documents a USB host clock drift issue + * that can be worked around using specially crafted dpll5 settings + * with a dpll5_m2 divider set to 8. Set the dpll5 rate to 8x the USB + * host clock rate, its .set_rate handler() will detect that frequency + * and use the errata settings. + */ dpll5_clk = clk_get(NULL, "dpll5_ck"); - clk_set_rate(dpll5_clk, DPLL5_FREQ_FOR_USBHOST); + clk_set_rate(dpll5_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST * 8); clk_prepare_enable(dpll5_clk); - /* Program dpll5_m2_clk divider for no division */ + /* Program dpll5_m2_clk divider */ dpll5_m2_clk = clk_get(NULL, "dpll5_m2_ck"); clk_prepare_enable(dpll5_m2_clk); - clk_set_rate(dpll5_m2_clk, DPLL5_FREQ_FOR_USBHOST); + clk_set_rate(dpll5_m2_clk, OMAP3_DPLL5_FREQ_FOR_USBHOST); clk_disable_unprepare(dpll5_m2_clk); clk_disable_unprepare(dpll5_clk); diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h index 90f3f472ae1c..13c37f48d9d6 100644 --- a/drivers/clk/ti/clock.h +++ b/drivers/clk/ti/clock.h @@ -257,11 +257,20 @@ long omap2_dpll_round_rate(struct clk_hw *hw, unsigned long target_rate, unsigned long omap3_clkoutx2_recalc(struct clk_hw *hw, unsigned long parent_rate); +/* + * OMAP3_DPLL5_FREQ_FOR_USBHOST: USBHOST and USBTLL are the only clocks + * that are sourced by DPLL5, and both of these require this clock + * to be at 120 MHz for proper operation. + */ +#define OMAP3_DPLL5_FREQ_FOR_USBHOST 120000000 + unsigned long omap3_dpll_recalc(struct clk_hw *hw, unsigned long parent_rate); int omap3_dpll4_set_rate(struct clk_hw *clk, unsigned long rate, unsigned long parent_rate); int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index); +int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate); void omap3_clk_lock_dpll5(void); unsigned long omap4_dpll_regm4xen_recalc(struct clk_hw *hw, diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c index 5519b386edc0..f9a5089ddc79 100644 --- a/drivers/clk/ti/dpll.c +++ b/drivers/clk/ti/dpll.c @@ -114,6 +114,18 @@ static const struct clk_ops omap3_dpll_ck_ops = { .round_rate = &omap2_dpll_round_rate, }; +static const struct clk_ops omap3_dpll5_ck_ops = { + .enable = &omap3_noncore_dpll_enable, + .disable = &omap3_noncore_dpll_disable, + .get_parent = &omap2_init_dpll_parent, + .recalc_rate = &omap3_dpll_recalc, + .set_rate = &omap3_dpll5_set_rate, + .set_parent = &omap3_noncore_dpll_set_parent, + .set_rate_and_parent = &omap3_noncore_dpll_set_rate_and_parent, + .determine_rate = &omap3_noncore_dpll_determine_rate, + .round_rate = &omap2_dpll_round_rate, +}; + static const struct clk_ops omap3_dpll_per_ck_ops = { .enable = &omap3_noncore_dpll_enable, .disable = &omap3_noncore_dpll_disable, @@ -461,7 +473,12 @@ static void __init of_ti_omap3_dpll_setup(struct device_node *node) .modes = (1 << DPLL_LOW_POWER_BYPASS) | (1 << DPLL_LOCKED), }; - of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd); + if ((of_machine_is_compatible("ti,omap3630") || + of_machine_is_compatible("ti,omap36xx")) && + !strcmp(node->name, "dpll5_ck")) + of_ti_dpll_setup(node, &omap3_dpll5_ck_ops, &dd); + else + of_ti_dpll_setup(node, &omap3_dpll_ck_ops, &dd); } CLK_OF_DECLARE(ti_omap3_dpll_clock, "ti,omap3-dpll-clock", of_ti_omap3_dpll_setup); diff --git a/drivers/clk/ti/dpll3xxx.c b/drivers/clk/ti/dpll3xxx.c index f4dec00fb684..0e9119fae760 100644 --- a/drivers/clk/ti/dpll3xxx.c +++ b/drivers/clk/ti/dpll3xxx.c @@ -815,3 +815,70 @@ int omap3_dpll4_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, return omap3_noncore_dpll_set_rate_and_parent(hw, rate, parent_rate, index); } + +/* Apply DM3730 errata sprz319 advisory 2.1. */ +static bool omap3_dpll5_apply_errata(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct omap3_dpll5_settings { + unsigned int rate, m, n; + }; + + static const struct omap3_dpll5_settings precomputed[] = { + /* + * From DM3730 errata advisory 2.1, table 35 and 36. + * The N value is increased by 1 compared to the tables as the + * errata lists register values while last_rounded_field is the + * real divider value. + */ + { 12000000, 80, 0 + 1 }, + { 13000000, 443, 5 + 1 }, + { 19200000, 50, 0 + 1 }, + { 26000000, 443, 11 + 1 }, + { 38400000, 25, 0 + 1 } + }; + + const struct omap3_dpll5_settings *d; + struct clk_hw_omap *clk = to_clk_hw_omap(hw); + struct dpll_data *dd; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(precomputed); ++i) { + if (parent_rate == precomputed[i].rate) + break; + } + + if (i == ARRAY_SIZE(precomputed)) + return false; + + d = &precomputed[i]; + + /* Update the M, N and rounded rate values and program the DPLL. */ + dd = clk->dpll_data; + dd->last_rounded_m = d->m; + dd->last_rounded_n = d->n; + dd->last_rounded_rate = div_u64((u64)parent_rate * d->m, d->n); + omap3_noncore_dpll_program(clk, 0); + + return true; +} + +/** + * omap3_dpll5_set_rate - set rate for omap3 dpll5 + * @hw: clock to change + * @rate: target rate for clock + * @parent_rate: rate of the parent clock + * + * Set rate for the DPLL5 clock. Apply the sprz319 advisory 2.1 on OMAP36xx if + * the DPLL is used for USB host (detected through the requested rate). + */ +int omap3_dpll5_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + if (rate == OMAP3_DPLL5_FREQ_FOR_USBHOST * 8) { + if (omap3_dpll5_apply_errata(hw, parent_rate)) + return 0; + } + + return omap3_noncore_dpll_set_rate(hw, rate, parent_rate); +} |
