Merge "clk: qcom: Add support to log PLL/RCGR values in case of failure"

This commit is contained in:
Linux Build Service Account 2017-02-15 17:00:58 -08:00 committed by Gerrit - the friendly Code Review server
commit d76b403a39
6 changed files with 84 additions and 22 deletions

View file

@ -2649,7 +2649,7 @@ static const struct file_operations clk_enabled_list_fops = {
.release = seq_release, .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)) if (IS_ERR_OR_NULL(clk))
return; return;
@ -2663,6 +2663,7 @@ static void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f)
clk->ops->list_registers(f, clk->hw); clk->ops->list_registers(f, clk->hw);
} }
EXPORT_SYMBOL(clk_debug_print_hw);
static int print_hw_show(struct seq_file *m, void *unused) static int print_hw_show(struct seq_file *m, void *unused)
{ {

View file

@ -25,6 +25,7 @@ void __clk_free_clk(struct clk *clk);
void clock_debug_print_enabled(void); void clock_debug_print_enabled(void);
int clk_register_debug(struct clk_hw *hw, struct dentry *dentry); 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_measure_add(struct clk_hw *hw, struct dentry *dentry);
void clk_debug_print_hw(struct clk_core *clk, struct seq_file *f);
#else #else
/* All these casts to avoid ifdefs in clkdev... */ /* All these casts to avoid ifdefs in clkdev... */

View file

@ -16,8 +16,10 @@
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/sched.h>
#include "clk-alpha-pll.h" #include "clk-alpha-pll.h"
#include "common.h"
#define PLL_MODE 0x00 #define PLL_MODE 0x00
#define PLL_OUTCTRL BIT(0) #define PLL_OUTCTRL BIT(0)
@ -74,13 +76,17 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,
u32 val, off; u32 val, off;
int count; int count;
int ret; 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);
off = pll->offset; off = pll->offset;
ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
if (ret) if (ret)
return ret; return ret;
time = sched_clock();
for (count = 100; count > 0; count--) { for (count = 100; count > 0; count--) {
ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val); ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
if (ret) if (ret)
@ -93,7 +99,13 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,
udelay(1); udelay(1);
} }
WARN(1, "%s failed to %s!\n", name, action); time = sched_clock() - time;
pr_err("PLL lock bit detection total wait time: %lld ns", time);
WARN_CLK(hw->core, name, 1, "failed to %s!\n", action);
return -ETIMEDOUT; return -ETIMEDOUT;
} }
@ -589,7 +601,11 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw)
{"PLL_ALPHA_VAL", 0x8}, {"PLL_ALPHA_VAL", 0x8},
{"PLL_ALPHA_VAL_U", 0xC}, {"PLL_ALPHA_VAL_U", 0xC},
{"PLL_USER_CTL", 0x10}, {"PLL_USER_CTL", 0x10},
{"PLL_USER_CTL_U", 0x14},
{"PLL_CONFIG_CTL", 0x18}, {"PLL_CONFIG_CTL", 0x18},
{"PLL_TEST_CTL", 0x1c},
{"PLL_TEST_CTL_U", 0x20},
{"PLL_STATUS", 0x24},
}; };
static struct clk_register_data data1[] = { static struct clk_register_data data1[] = {
@ -601,7 +617,8 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw)
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
regmap_read(pll->clkr.regmap, pll->offset + data[i].offset, regmap_read(pll->clkr.regmap, pll->offset + data[i].offset,
&val); &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); regmap_read(pll->clkr.regmap, pll->offset + data[0].offset, &val);
@ -609,7 +626,8 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw)
if (val & PLL_FSM_ENA) { if (val & PLL_FSM_ENA) {
regmap_read(pll->clkr.regmap, pll->clkr.enable_reg + regmap_read(pll->clkr.regmap, pll->clkr.enable_reg +
data1[0].offset, &val); 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);
} }
} }

View file

@ -22,6 +22,7 @@
#include "clk-branch.h" #include "clk-branch.h"
#include "clk-regmap.h" #include "clk-regmap.h"
#include "common.h"
static bool clk_branch_in_hwcg_mode(const struct clk_branch *br) static bool clk_branch_in_hwcg_mode(const struct clk_branch *br)
{ {
@ -77,7 +78,8 @@ static int clk_branch_wait(const struct clk_branch *br, bool enabling,
bool (check_halt)(const struct clk_branch *, bool)) bool (check_halt)(const struct clk_branch *, bool))
{ {
bool voted = br->halt_check & BRANCH_VOTED; 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 the clock is in hardware gated mode */ /* Skip checking halt bit if the clock is in hardware gated mode */
if (clk_branch_in_hwcg_mode(br)) if (clk_branch_in_hwcg_mode(br))
@ -104,8 +106,10 @@ static int clk_branch_wait(const struct clk_branch *br, bool enabling,
return 0; return 0;
udelay(1); udelay(1);
} }
WARN(1, "%s status stuck at 'o%s'", name,
WARN_CLK(hw->core, name, 1, "status stuck at 'o%s'",
enabling ? "ff" : "n"); enabling ? "ff" : "n");
return -EBUSY; return -EBUSY;
} }
return 0; return 0;
@ -212,7 +216,8 @@ static void clk_branch2_list_registers(struct seq_file *f, struct clk_hw *hw)
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
regmap_read(br->clkr.regmap, br->halt_reg + data[i].offset, regmap_read(br->clkr.regmap, br->halt_reg + data[i].offset,
&val); &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) && if ((br->halt_check & BRANCH_HALT_VOTED) &&
@ -222,7 +227,7 @@ static void clk_branch2_list_registers(struct seq_file *f, struct clk_hw *hw)
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
regmap_read(br->clkr.regmap, rclk->enable_reg + regmap_read(br->clkr.regmap, rclk->enable_reg +
data1[i].offset, &val); data1[i].offset, &val);
seq_printf(f, "%20s: 0x%.8x\n", clock_debug_output(f, false, "%20s: 0x%.8x\n",
data1[i].name, val); data1[i].name, val);
} }
} }
@ -360,7 +365,8 @@ static void clk_gate2_list_registers(struct seq_file *f, struct clk_hw *hw)
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
regmap_read(gt->clkr.regmap, gt->clkr.enable_reg + regmap_read(gt->clkr.regmap, gt->clkr.enable_reg +
data[i].offset, &val); 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);
} }
} }

View file

@ -122,7 +122,7 @@ err:
return 0; return 0;
} }
static int update_config(struct clk_rcg2 *rcg) static int update_config(struct clk_rcg2 *rcg, u32 cfg)
{ {
int count, ret; int count, ret;
u32 cmd; u32 cmd;
@ -144,7 +144,11 @@ static int update_config(struct clk_rcg2 *rcg)
udelay(1); udelay(1);
} }
WARN(1, "%s: rcg didn't update its configuration.", name); pr_err("CFG_RCGR old frequency configuration 0x%x !\n", cfg);
WARN_CLK(hw->core, name, count == 0,
"rcg didn't update its configuration.");
return 0; return 0;
} }
@ -153,13 +157,17 @@ static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index)
struct clk_rcg2 *rcg = to_clk_rcg2(hw); struct clk_rcg2 *rcg = to_clk_rcg2(hw);
int ret; int ret;
u32 cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; u32 cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
u32 old_cfg;
/* 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, ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
CFG_SRC_SEL_MASK, cfg); CFG_SRC_SEL_MASK, cfg);
if (ret) if (ret)
return ret; return ret;
return update_config(rcg); return update_config(rcg, old_cfg);
} }
static void clk_rcg_clear_force_enable(struct clk_hw *hw) static void clk_rcg_clear_force_enable(struct clk_hw *hw)
@ -297,13 +305,16 @@ static int clk_rcg2_determine_rate(struct clk_hw *hw,
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 cfg, mask; u32 cfg, mask, old_cfg;
struct clk_hw *hw = &rcg->clkr.hw; struct clk_hw *hw = &rcg->clkr.hw;
int ret, index = qcom_find_src_index(hw, rcg->parent_map, f->src); int ret, index = qcom_find_src_index(hw, rcg->parent_map, f->src);
if (index < 0) if (index < 0)
return index; return index;
/* Read back the old configuration */
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &old_cfg);
if (rcg->mnd_width && f->n) { if (rcg->mnd_width && f->n) {
mask = BIT(rcg->mnd_width) - 1; mask = BIT(rcg->mnd_width) - 1;
ret = regmap_update_bits(rcg->clkr.regmap, ret = regmap_update_bits(rcg->clkr.regmap,
@ -333,7 +344,7 @@ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
if (ret) if (ret)
return 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) static void clk_rcg2_list_registers(struct seq_file *f, struct clk_hw *hw)
@ -359,14 +370,16 @@ static void clk_rcg2_list_registers(struct seq_file *f, struct clk_hw *hw)
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
regmap_read(rcg->clkr.regmap, (rcg->cmd_rcgr + regmap_read(rcg->clkr.regmap, (rcg->cmd_rcgr +
data1[i].offset), &val); 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 { } else {
size = ARRAY_SIZE(data); size = ARRAY_SIZE(data);
for (i = 0; i < size; i++) { for (i = 0; i < size; i++) {
regmap_read(rcg->clkr.regmap, (rcg->cmd_rcgr + regmap_read(rcg->clkr.regmap, (rcg->cmd_rcgr +
data[i].offset), &val); 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);
} }
} }
} }
@ -1186,16 +1199,19 @@ static int clk_gfx3d_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate, u8 index) unsigned long parent_rate, u8 index)
{ {
struct clk_rcg2 *rcg = to_clk_rcg2(hw); struct clk_rcg2 *rcg = to_clk_rcg2(hw);
u32 cfg; u32 cfg, old_cfg;
int ret; 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 */ /* Just mux it, we don't use the division or m/n hardware */
cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg); ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg);
if (ret) if (ret)
return 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, static int clk_gfx3d_set_rate(struct clk_hw *hw, unsigned long rate,
@ -1271,9 +1287,12 @@ static int clk_gfx3d_src_set_rate_and_parent(struct clk_hw *hw,
{ {
struct clk_rcg2 *rcg = to_clk_rcg2(hw); struct clk_rcg2 *rcg = to_clk_rcg2(hw);
const struct freq_tbl *f; const struct freq_tbl *f;
u32 cfg; u32 cfg, old_cfg;
int ret; int ret;
/* Read back the old configuration */
regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &old_cfg);
cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
f = qcom_find_freq(rcg->freq_tbl, rate); f = qcom_find_freq(rcg->freq_tbl, rate);
@ -1287,7 +1306,7 @@ static int clk_gfx3d_src_set_rate_and_parent(struct clk_hw *hw,
if (ret) if (ret)
return ret; return ret;
return update_config(rcg); return update_config(rcg, old_cfg);
} }
const struct clk_ops clk_gfx3d_src_ops = { const struct clk_ops clk_gfx3d_src_ops = {

View file

@ -13,6 +13,8 @@
#ifndef __QCOM_CLK_COMMON_H__ #ifndef __QCOM_CLK_COMMON_H__
#define __QCOM_CLK_COMMON_H__ #define __QCOM_CLK_COMMON_H__
#include "../clk.h"
struct platform_device; struct platform_device;
struct regmap_config; struct regmap_config;
struct clk_regmap; struct clk_regmap;
@ -153,4 +155,19 @@ struct clk_debug_mux {
extern const struct clk_ops clk_debug_mux_ops; 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__); \
} while (0)
#define clock_debug_output(m, c, fmt, ...) \
do { \
if (m) \
seq_printf(m, fmt, ##__VA_ARGS__); \
else if (c) \
pr_cont(fmt, ##__VA_ARGS__); \
else \
pr_info(fmt, ##__VA_ARGS__); \
} while (0)
#endif #endif