Merge "clk: qcom: Add support to log PLL/RCGR values in case of failure"
This commit is contained in:
commit
d76b403a39
6 changed files with 84 additions and 22 deletions
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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... */
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
|
||||||
enabling ? "ff" : "n");
|
WARN_CLK(hw->core, name, 1, "status stuck at 'o%s'",
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue