From e03bf33cb9411de0d1b9c1df2b09ebf3e5c5bae4 Mon Sep 17 00:00:00 2001 From: Taniya Das <tdas@codeaurora.org> Date: Mon, 7 Nov 2016 10:01:38 +0530 Subject: [PATCH] clk: qcom: Add support for debugfs support Add the clock operation list_registers which would print the register content of a clock. This is supported for PLL/RCG/Branch/Gate clocks. Also add support for list_rate for the RCG clock which would list all the frequencies/rates supported by the SW. Change-Id: I3b4cf83e776750d993d53331142223109bf0862e Signed-off-by: Taniya Das <tdas@codeaurora.org> --- drivers/clk/qcom/clk-alpha-pll.c | 37 ++++++++++++++++++++ drivers/clk/qcom/clk-branch.c | 59 ++++++++++++++++++++++++++++++++ drivers/clk/qcom/clk-rcg2.c | 56 ++++++++++++++++++++++++++++++ drivers/clk/qcom/clk-regmap.h | 8 ++++- 4 files changed, 159 insertions(+), 1 deletion(-) diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 085b9acfb9d5..ac45943dc12e 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -562,12 +562,48 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate, return clamp(rate, min_freq, max_freq); } +static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + int size, i, val; + + static struct clk_register_data data[] = { + {"PLL_MODE", 0x0}, + {"PLL_L_VAL", 0x4}, + {"PLL_ALPHA_VAL", 0x8}, + {"PLL_ALPHA_VAL_U", 0xC}, + {"PLL_USER_CTL", 0x10}, + {"PLL_CONFIG_CTL", 0x18}, + }; + + static struct clk_register_data data1[] = { + {"APSS_PLL_VOTE", 0x0}, + }; + + size = ARRAY_SIZE(data); + + 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); + } + + regmap_read(pll->clkr.regmap, pll->offset + data[0].offset, &val); + + 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); + } +} + const struct clk_ops clk_alpha_pll_ops = { .enable = clk_alpha_pll_enable, .disable = clk_alpha_pll_disable, .recalc_rate = clk_alpha_pll_recalc_rate, .round_rate = clk_alpha_pll_round_rate, .set_rate = clk_alpha_pll_set_rate, + .list_registers = clk_alpha_pll_list_registers, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_ops); @@ -577,6 +613,7 @@ const struct clk_ops clk_alpha_pll_hwfsm_ops = { .recalc_rate = clk_alpha_pll_recalc_rate, .round_rate = clk_alpha_pll_round_rate, .set_rate = clk_alpha_pll_set_rate, + .list_registers = clk_alpha_pll_list_registers, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_hwfsm_ops); diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c index 8aea1d519311..6a975052cc85 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -16,10 +16,12 @@ #include <linux/err.h> #include <linux/delay.h> #include <linux/export.h> +#include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/regmap.h> #include "clk-branch.h" +#include "clk-regmap.h" static bool clk_branch_in_hwcg_mode(const struct clk_branch *br) { @@ -181,6 +183,43 @@ const struct clk_ops clk_branch_ops = { }; EXPORT_SYMBOL_GPL(clk_branch_ops); +static void clk_branch2_list_registers(struct seq_file *f, struct clk_hw *hw) +{ + struct clk_branch *br = to_clk_branch(hw); + struct clk_regmap *rclk = to_clk_regmap(hw); + int size, i, val; + + static struct clk_register_data data[] = { + {"CBCR", 0x0}, + }; + + static struct clk_register_data data1[] = { + {"APSS_VOTE", 0x0}, + {"APSS_SLEEP_VOTE", 0x4}, + }; + + size = ARRAY_SIZE(data); + + 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); + } + + if ((br->halt_check & BRANCH_HALT_VOTED) && + !(br->halt_check & BRANCH_VOTED)) { + if (rclk->enable_reg) { + size = ARRAY_SIZE(data1); + 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", + data1[i].name, val); + } + } + } +} + static int clk_branch2_enable(struct clk_hw *hw) { return clk_branch_toggle(hw, true, clk_branch2_check_halt); @@ -196,6 +235,7 @@ const struct clk_ops clk_branch2_ops = { .disable = clk_branch2_disable, .is_enabled = clk_is_enabled_regmap, .set_flags = clk_branch_set_flags, + .list_registers = clk_branch2_list_registers, }; EXPORT_SYMBOL_GPL(clk_branch2_ops); @@ -228,10 +268,29 @@ static void clk_gate2_disable(struct clk_hw *hw) clk_gate_toggle(hw, false); } +static void clk_gate2_list_registers(struct seq_file *f, struct clk_hw *hw) +{ + struct clk_gate2 *gt = to_clk_gate2(hw); + int size, i, val; + + static struct clk_register_data data[] = { + {"EN_REG", 0x0}, + }; + + size = ARRAY_SIZE(data); + + 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); + } +} + const struct clk_ops clk_gate2_ops = { .enable = clk_gate2_enable, .disable = clk_gate2_disable, .is_enabled = clk_is_enabled_regmap, + .list_registers = clk_gate2_list_registers, }; EXPORT_SYMBOL_GPL(clk_gate2_ops); diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index 6d12ddb3e245..3785a7579ce7 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -317,6 +317,53 @@ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) return update_config(rcg); } +static void clk_rcg2_list_registers(struct seq_file *f, struct clk_hw *hw) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + int i = 0, size = 0, val; + + static struct clk_register_data data[] = { + {"CMD_RCGR", 0x0}, + {"CFG_RCGR", 0x4}, + }; + + static struct clk_register_data data1[] = { + {"CMD_RCGR", 0x0}, + {"CFG_RCGR", 0x4}, + {"M_VAL", 0x8}, + {"N_VAL", 0xC}, + {"D_VAL", 0x10}, + }; + + if (rcg->mnd_width) { + size = ARRAY_SIZE(data1); + 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); + } + } 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); + } + } +} + +/* Return the nth supported frequency for a given clock. */ +static long clk_rcg2_list_rate(struct clk_hw *hw, unsigned n, + unsigned long fmax) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + + if (!rcg->freq_tbl) + return -ENXIO; + + return (rcg->freq_tbl + n)->freq; +} + static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate) { struct clk_rcg2 *rcg = to_clk_rcg2(hw); @@ -351,6 +398,8 @@ const struct clk_ops clk_rcg2_ops = { .determine_rate = clk_rcg2_determine_rate, .set_rate = clk_rcg2_set_rate, .set_rate_and_parent = clk_rcg2_set_rate_and_parent, + .list_rate = clk_rcg2_list_rate, + .list_registers = clk_rcg2_list_registers, }; EXPORT_SYMBOL_GPL(clk_rcg2_ops); @@ -557,6 +606,7 @@ const struct clk_ops clk_edp_pixel_ops = { .set_rate = clk_edp_pixel_set_rate, .set_rate_and_parent = clk_edp_pixel_set_rate_and_parent, .determine_rate = clk_edp_pixel_determine_rate, + .list_registers = clk_rcg2_list_registers, }; EXPORT_SYMBOL_GPL(clk_edp_pixel_ops); @@ -615,6 +665,7 @@ const struct clk_ops clk_byte_ops = { .set_rate = clk_byte_set_rate, .set_rate_and_parent = clk_byte_set_rate_and_parent, .determine_rate = clk_byte_determine_rate, + .list_registers = clk_rcg2_list_registers, }; EXPORT_SYMBOL_GPL(clk_byte_ops); @@ -685,6 +736,7 @@ const struct clk_ops clk_byte2_ops = { .set_rate = clk_byte2_set_rate, .set_rate_and_parent = clk_byte2_set_rate_and_parent, .determine_rate = clk_byte2_determine_rate, + .list_registers = clk_rcg2_list_registers, }; EXPORT_SYMBOL_GPL(clk_byte2_ops); @@ -775,6 +827,7 @@ const struct clk_ops clk_pixel_ops = { .set_rate = clk_pixel_set_rate, .set_rate_and_parent = clk_pixel_set_rate_and_parent, .determine_rate = clk_pixel_determine_rate, + .list_registers = clk_rcg2_list_registers, }; EXPORT_SYMBOL_GPL(clk_pixel_ops); @@ -864,6 +917,7 @@ const struct clk_ops clk_gfx3d_ops = { .set_rate = clk_gfx3d_set_rate, .set_rate_and_parent = clk_gfx3d_set_rate_and_parent, .determine_rate = clk_gfx3d_determine_rate, + .list_registers = clk_rcg2_list_registers, }; EXPORT_SYMBOL_GPL(clk_gfx3d_ops); @@ -944,5 +998,7 @@ const struct clk_ops clk_gfx3d_src_ops = { .set_rate = clk_gfx3d_set_rate, .set_rate_and_parent = clk_gfx3d_src_set_rate_and_parent, .determine_rate = clk_gfx3d_src_determine_rate, + .list_rate = clk_rcg2_list_rate, + .list_registers = clk_rcg2_list_registers, }; EXPORT_SYMBOL_GPL(clk_gfx3d_src_ops); diff --git a/drivers/clk/qcom/clk-regmap.h b/drivers/clk/qcom/clk-regmap.h index 491a63d537df..8663dea02d79 100644 --- a/drivers/clk/qcom/clk-regmap.h +++ b/drivers/clk/qcom/clk-regmap.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, The Linux Foundation. All rights reserved. + * Copyright (c) 2014, 2016, 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 @@ -15,6 +15,7 @@ #define __QCOM_CLK_REGMAP_H__ #include <linux/clk-provider.h> +#include <linux/debugfs.h> struct regmap; @@ -42,4 +43,9 @@ void clk_disable_regmap(struct clk_hw *hw); struct clk * devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk); +struct clk_register_data { + char *name; + u32 offset; +}; + #endif