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 <tdas@codeaurora.org>
This commit is contained in:
parent
a3d9530ab5
commit
0cbef8b6fc
6 changed files with 78 additions and 24 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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... */
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/clk-provider.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched/clock.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue