summaryrefslogtreecommitdiff
path: root/drivers/clk/qcom
diff options
context:
space:
mode:
authorTaniya Das <tdas@codeaurora.org>2017-01-27 16:48:48 +0530
committerTaniya Das <tdas@codeaurora.org>2017-02-14 09:56:09 +0530
commite2ff0d9bb2df29c908c8766115765355d55b1601 (patch)
tree518f97870df005f17d6e9ace0662c02896211458 /drivers/clk/qcom
parent2e47ba9a64f870431dd7709f454ba51dca0f89e9 (diff)
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>
Diffstat (limited to 'drivers/clk/qcom')
-rw-r--r--drivers/clk/qcom/clk-alpha-pll.c26
-rw-r--r--drivers/clk/qcom/clk-branch.c18
-rw-r--r--drivers/clk/qcom/clk-rcg2.c41
-rw-r--r--drivers/clk/qcom/common.h19
4 files changed, 82 insertions, 22 deletions
diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 375f1420f3bb..e6054444599c 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -16,8 +16,10 @@
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include <linux/delay.h>
+#include <linux/sched.h>
#include "clk-alpha-pll.h"
+#include "common.h"
#define PLL_MODE 0x00
#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;
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);
off = pll->offset;
ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
if (ret)
return ret;
+ time = sched_clock();
+
for (count = 100; count > 0; count--) {
ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
if (ret)
@@ -93,7 +99,13 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,
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;
}
@@ -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_U", 0xC},
{"PLL_USER_CTL", 0x10},
+ {"PLL_USER_CTL_U", 0x14},
{"PLL_CONFIG_CTL", 0x18},
+ {"PLL_TEST_CTL", 0x1c},
+ {"PLL_TEST_CTL_U", 0x20},
+ {"PLL_STATUS", 0x24},
};
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++) {
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);
@@ -609,7 +626,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);
}
}
diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c
index bfaf4482d668..ca6010db8d78 100644
--- a/drivers/clk/qcom/clk-branch.c
+++ b/drivers/clk/qcom/clk-branch.c
@@ -22,6 +22,7 @@
#include "clk-branch.h"
#include "clk-regmap.h"
+#include "common.h"
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 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 */
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;
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;
@@ -212,7 +216,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) &&
@@ -222,7 +227,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);
}
}
@@ -360,7 +365,8 @@ static void clk_gate2_list_registers(struct seq_file *f, struct clk_hw *hw)
for (i = 0; i < size; i++) {
regmap_read(gt->clkr.regmap, gt->clkr.enable_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);
}
}
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index 40c592457ea1..613dc3b2c347 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -122,7 +122,7 @@ err:
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;
@@ -144,7 +144,11 @@ static int update_config(struct clk_rcg2 *rcg)
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;
}
@@ -153,13 +157,17 @@ 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;
+
+ /* 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 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)
{
- u32 cfg, mask;
+ u32 cfg, mask, old_cfg;
struct clk_hw *hw = &rcg->clkr.hw;
int ret, index = qcom_find_src_index(hw, rcg->parent_map, f->src);
if (index < 0)
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) {
mask = BIT(rcg->mnd_width) - 1;
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)
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)
@@ -359,14 +370,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);
}
}
}
@@ -1176,16 +1189,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,
@@ -1261,9 +1277,12 @@ static int clk_gfx3d_src_set_rate_and_parent(struct clk_hw *hw,
{
struct clk_rcg2 *rcg = to_clk_rcg2(hw);
const struct freq_tbl *f;
- 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);
+
cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
f = qcom_find_freq(rcg->freq_tbl, rate);
@@ -1277,7 +1296,7 @@ static int clk_gfx3d_src_set_rate_and_parent(struct clk_hw *hw,
if (ret)
return ret;
- return update_config(rcg);
+ return update_config(rcg, old_cfg);
}
const struct clk_ops clk_gfx3d_src_ops = {
diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index 76c010970b51..103959cd54a4 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014, 2016-2017, The Linux Foundation. All rights reserved.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -13,6 +13,8 @@
#ifndef __QCOM_CLK_COMMON_H__
#define __QCOM_CLK_COMMON_H__
+#include "../clk.h"
+
struct platform_device;
struct regmap_config;
struct clk_regmap;
@@ -143,4 +145,19 @@ struct clk_debug_mux {
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