From 0cbef8b6fc58e084b49b98918cae0fc2c3a6d95c Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Wed, 26 Dec 2018 00:36:10 +0530 Subject: [PATCH] clk: qcom: Add support to log PLL/RCGR values in case of failure In case of PLL lock errors or the RCGR fails to update the new configuration, add support to capture all the PLL and RCGR configuration registers as part of kernel logs. Change-Id: Ifb0cefafc30f8796ba17f2d388fb65ed41aae485 Signed-off-by: Taniya Das --- drivers/clk/clk.c | 3 ++- drivers/clk/clk.h | 2 ++ drivers/clk/qcom/clk-alpha-pll.c | 24 +++++++++++++----- drivers/clk/qcom/clk-branch.c | 13 ++++++---- drivers/clk/qcom/clk-rcg2.c | 43 +++++++++++++++++++++++--------- drivers/clk/qcom/common.h | 17 +++++++++++++ 6 files changed, 78 insertions(+), 24 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 591ac87485c2..84e655d747ec 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -3407,7 +3407,7 @@ static const struct file_operations clk_enabled_list_fops = { .release = seq_release, }; -static void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f) +void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f) { if (IS_ERR_OR_NULL(clk)) return; @@ -3421,6 +3421,7 @@ static void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f) clk->ops->list_registers(f, clk->hw); } +EXPORT_SYMBOL(clk_debug_print_hw); static int print_hw_show(struct seq_file *m, void *unused) { diff --git a/drivers/clk/clk.h b/drivers/clk/clk.h index c5d03171144b..0ae0e1a3cb71 100644 --- a/drivers/clk/clk.h +++ b/drivers/clk/clk.h @@ -10,6 +10,7 @@ */ struct clk_hw; +struct clk_core; #if defined(CONFIG_OF) && defined(CONFIG_COMMON_CLK) struct clk *__of_clk_get_from_provider(struct of_phandle_args *clkspec, @@ -25,6 +26,7 @@ void __clk_put(struct clk *clk); /* Debugfs API to print the enabled clocks */ void clock_debug_print_enabled(void); +void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f); #else /* All these casts to avoid ifdefs in clkdev... */ diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index ef30947b0463..ef25edaca74d 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "clk-alpha-pll.h" #include "common.h" @@ -180,12 +181,16 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse, u32 val; int count; int ret; - const char *name = clk_hw_get_name(&pll->clkr.hw); + u64 time; + struct clk_hw *hw = &pll->clkr.hw; + const char *name = clk_hw_get_name(hw); ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); if (ret) return ret; + time = sched_clock(); + for (count = 100; count > 0; count--) { ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); if (ret) @@ -197,8 +202,11 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse, udelay(1); } + time = sched_clock() - time; - WARN(1, "%s failed to %s!\n", name, action); + pr_err("PLL lock bit detection total wait time: %lld ns\n", time); + + WARN_CLK(hw->core, name, 1, "failed to %s!\n", action); return -ETIMEDOUT; } @@ -794,7 +802,8 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw) for (i = 0; i < size; i++) { regmap_read(pll->clkr.regmap, pll->offset + data[i].offset, &val); - seq_printf(f, "%20s: 0x%.8x\n", data[i].name, val); + clock_debug_output(f, false, + "%20s: 0x%.8x\n", data[i].name, val); } regmap_read(pll->clkr.regmap, pll->offset + data[0].offset, &val); @@ -802,7 +811,8 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw) if (val & PLL_FSM_ENA) { regmap_read(pll->clkr.regmap, pll->clkr.enable_reg + data1[0].offset, &val); - seq_printf(f, "%20s: 0x%.8x\n", data1[0].name, val); + clock_debug_output(f, false, + "%20s: 0x%.8x\n", data1[0].name, val); } } @@ -1056,7 +1066,8 @@ static void clk_zonda_pll_list_registers(struct seq_file *f, struct clk_hw *hw) for (i = 0; i < size; i++) { regmap_read(pll->clkr.regmap, pll->offset + pll_regs[i].offset, &val); - seq_printf(f, "%20s: 0x%.8x\n", pll_regs[i].name, val); + clock_debug_output(f, false, + "%20s: 0x%.8x\n", pll_regs[i].name, val); } regmap_read(pll->clkr.regmap, pll->offset + pll_vote_reg.offset, &val); @@ -1064,7 +1075,8 @@ static void clk_zonda_pll_list_registers(struct seq_file *f, struct clk_hw *hw) if (val & PLL_FSM_ENA) { regmap_read(pll->clkr.regmap, pll->clkr.enable_reg + pll_vote_reg.offset, &val); - seq_printf(f, "%20s: 0x%.8x\n", pll_vote_reg.name, val); + clock_debug_output(f, false, + "%20s: 0x%.8x\n", pll_vote_reg.name, val); } } diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c index 37e3d846ee34..d907b5cecd29 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -16,6 +16,7 @@ #include "clk-branch.h" #include "clk-regmap.h" #include "clk-debug.h" +#include "common.h" static bool clk_branch_in_hwcg_mode(const struct clk_branch *br) { @@ -71,7 +72,8 @@ static int clk_branch_wait(const struct clk_branch *br, bool enabling, bool (check_halt)(const struct clk_branch *, bool)) { bool voted = br->halt_check & BRANCH_VOTED; - const char *name = clk_hw_get_name(&br->clkr.hw); + const struct clk_hw *hw = &br->clkr.hw; + const char *name = clk_hw_get_name(hw); /* * Skip checking halt bit if we're explicitly ignoring the bit or the @@ -92,8 +94,8 @@ static int clk_branch_wait(const struct clk_branch *br, bool enabling, return 0; udelay(1); } - WARN(1, "%s status stuck at 'o%s'", name, - enabling ? "ff" : "n"); + WARN_CLK(hw->core, name, 1, "status stuck at 'o%s'", + enabling ? "ff" : "n"); return -EBUSY; } return 0; @@ -336,7 +338,8 @@ static void clk_branch2_list_registers(struct seq_file *f, struct clk_hw *hw) for (i = 0; i < size; i++) { regmap_read(br->clkr.regmap, br->halt_reg + data[i].offset, &val); - seq_printf(f, "%20s: 0x%.8x\n", data[i].name, val); + clock_debug_output(f, false, + "%20s: 0x%.8x\n", data[i].name, val); } if ((br->halt_check & BRANCH_HALT_VOTED) && @@ -346,7 +349,7 @@ static void clk_branch2_list_registers(struct seq_file *f, struct clk_hw *hw) for (i = 0; i < size; i++) { regmap_read(br->clkr.regmap, rclk->enable_reg + data1[i].offset, &val); - seq_printf(f, "%20s: 0x%.8x\n", + clock_debug_output(f, false, "%20s: 0x%.8x\n", data1[i].name, val); } } diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index 22259c233ab4..25a13f86ab08 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -101,7 +101,7 @@ static u8 clk_rcg2_get_parent(struct clk_hw *hw) return 0; } -static int update_config(struct clk_rcg2 *rcg) +static int update_config(struct clk_rcg2 *rcg, u32 cfg) { int count, ret; u32 cmd; @@ -123,22 +123,28 @@ static int update_config(struct clk_rcg2 *rcg) udelay(1); } - WARN(1, "%s: rcg didn't update its configuration.", name); - return 0; + pr_err("CFG_RCGR old frequency configuration 0x%x !\n", cfg); + + WARN_CLK(hw->core, name, count == 0, + "%s: rcg didn't update its configuration.", name); + return -EBUSY; } static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index) { struct clk_rcg2 *rcg = to_clk_rcg2(hw); int ret; - u32 cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; + u32 old_cfg, cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; + + /* Read back the old configuration */ + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &old_cfg); ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, CFG_SRC_SEL_MASK, cfg); if (ret) return ret; - return update_config(rcg); + return update_config(rcg, old_cfg); } static int clk_rcg2_set_force_enable(struct clk_hw *hw) @@ -395,13 +401,17 @@ static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) { + u32 old_cfg; int ret; + /* Read back the old configuration */ + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &old_cfg); + ret = __clk_rcg2_configure(rcg, f); if (ret) return ret; - return update_config(rcg); + return update_config(rcg, old_cfg); } static void clk_rcg2_list_registers(struct seq_file *f, struct clk_hw *hw) @@ -427,14 +437,16 @@ static void clk_rcg2_list_registers(struct seq_file *f, struct clk_hw *hw) for (i = 0; i < size; i++) { regmap_read(rcg->clkr.regmap, (rcg->cmd_rcgr + data1[i].offset), &val); - seq_printf(f, "%20s: 0x%.8x\n", data1[i].name, val); + clock_debug_output(f, false, + "%20s: 0x%.8x\n", data1[i].name, val); } } else { size = ARRAY_SIZE(data); for (i = 0; i < size; i++) { regmap_read(rcg->clkr.regmap, (rcg->cmd_rcgr + data[i].offset), &val); - seq_printf(f, "%20s: 0x%.8x\n", data[i].name, val); + clock_debug_output(f, false, + "%20s: 0x%.8x\n", data[i].name, val); } } } @@ -1145,16 +1157,19 @@ static int clk_gfx3d_set_rate_and_parent(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate, u8 index) { struct clk_rcg2 *rcg = to_clk_rcg2(hw); - u32 cfg; + u32 cfg, old_cfg; int ret; + /* Read back the old configuration */ + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &old_cfg); + /* Just mux it, we don't use the division or m/n hardware */ cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg); if (ret) return ret; - return update_config(rcg); + return update_config(rcg, old_cfg); } static int clk_gfx3d_set_rate(struct clk_hw *hw, unsigned long rate, @@ -1226,8 +1241,12 @@ static int clk_rcg2_shared_set_rate_and_parent(struct clk_hw *hw, static int clk_rcg2_shared_enable(struct clk_hw *hw) { struct clk_rcg2 *rcg = to_clk_rcg2(hw); + u32 old_cfg; int ret; + /* Read back the old configuration */ + regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &old_cfg); + /* * Set the update bit because required configuration has already * been written in clk_rcg2_shared_set_rate() @@ -1236,7 +1255,7 @@ static int clk_rcg2_shared_enable(struct clk_hw *hw) if (ret) return ret; - ret = update_config(rcg); + ret = update_config(rcg, old_cfg); if (ret) return ret; @@ -1267,7 +1286,7 @@ static void clk_rcg2_shared_disable(struct clk_hw *hw) regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, rcg->safe_src_index << CFG_SRC_SEL_SHIFT); - update_config(rcg); + update_config(rcg, cfg); clk_rcg2_clear_force_enable(hw); diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h index 67c900c55bfb..f7548649a0b7 100644 --- a/drivers/clk/qcom/common.h +++ b/drivers/clk/qcom/common.h @@ -72,4 +72,21 @@ extern int qcom_cc_really_probe(struct platform_device *pdev, extern int qcom_cc_probe(struct platform_device *pdev, const struct qcom_cc_desc *desc); extern const struct clk_ops clk_dummy_ops; + +extern void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f); + +#define WARN_CLK(core, name, cond, fmt, ...) do { \ + clk_debug_print_hw(core, NULL); \ + WARN(cond, "%s: " fmt, name, ##__VA_ARGS__); \ +} while (0) + +#define clock_debug_output(m, c, fmt, ...) \ + do { \ + if (m) \ + seq_printf(m, fmt, ##__VA_ARGS__); \ + else if (c) \ + pr_alert(fmt, ##__VA_ARGS__); \ + else \ + pr_info(fmt, ##__VA_ARGS__); \ +} while (0) #endif