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