diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index af14004aa323..4ee77f31051a 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -11,6 +11,7 @@ #include #include "clk-alpha-pll.h" +#include "clk-debug.h" #include "common.h" #define PLL_MODE(p) ((p)->offset + 0x0) @@ -1331,6 +1332,53 @@ static void clk_zonda_5lpe_pll_disable(struct clk_hw *hw) return; } +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); + } +} + +static struct clk_regmap_ops clk_alpha_pll_regmap_ops = { + .list_registers = clk_alpha_pll_list_registers, +}; + +static void clk_alpha_pll_init(struct clk_hw *hw) +{ + struct clk_regmap *rclk = to_clk_regmap(hw); + + if (!rclk->ops) + rclk->ops = &clk_alpha_pll_regmap_ops; +} + const struct clk_ops clk_alpha_pll_ops = { .prepare = clk_prepare_regmap, .unprepare = clk_unprepare_regmap, @@ -1342,6 +1390,11 @@ const struct clk_ops clk_alpha_pll_ops = { .recalc_rate = clk_alpha_pll_recalc_rate, .round_rate = clk_alpha_pll_round_rate, .set_rate = clk_alpha_pll_set_rate, + .init = clk_alpha_pll_init, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL_GPL(clk_alpha_pll_ops); @@ -1356,6 +1409,9 @@ const struct clk_ops clk_alpha_pll_huayra_ops = { .recalc_rate = alpha_pll_huayra_recalc_rate, .round_rate = alpha_pll_huayra_round_rate, .set_rate = alpha_pll_huayra_set_rate, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL_GPL(clk_alpha_pll_huayra_ops); @@ -1370,6 +1426,10 @@ 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_hwfsm_set_rate, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL_GPL(clk_alpha_pll_hwfsm_ops); @@ -1383,6 +1443,9 @@ const struct clk_ops clk_trion_fixed_pll_ops = { .is_enabled = clk_trion_pll_is_enabled, .recalc_rate = clk_trion_pll_recalc_rate, .round_rate = clk_trion_pll_round_rate, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL_GPL(clk_trion_fixed_pll_ops); @@ -1397,6 +1460,9 @@ const struct clk_ops clk_alpha_pll_zonda_ops = { .recalc_rate = clk_zonda_pll_recalc_rate, .round_rate = clk_alpha_pll_round_rate, .set_rate = clk_zonda_pll_set_rate, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL(clk_alpha_pll_zonda_ops); @@ -1681,6 +1747,9 @@ const struct clk_ops clk_alpha_pll_fabia_ops = { .set_rate = alpha_pll_fabia_set_rate, .recalc_rate = alpha_pll_fabia_recalc_rate, .round_rate = clk_alpha_pll_round_rate, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL_GPL(clk_alpha_pll_fabia_ops); @@ -1694,6 +1763,9 @@ const struct clk_ops clk_alpha_pll_fixed_fabia_ops = { .is_enabled = clk_alpha_pll_is_enabled, .recalc_rate = alpha_pll_fabia_recalc_rate, .round_rate = clk_alpha_pll_round_rate, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL_GPL(clk_alpha_pll_fixed_fabia_ops); @@ -2476,6 +2548,9 @@ const struct clk_ops clk_alpha_pll_lucid_ops = { .recalc_rate = alpha_pll_lucid_recalc_rate, .round_rate = clk_alpha_pll_round_rate, .set_rate = alpha_pll_lucid_set_rate, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL(clk_alpha_pll_lucid_ops); @@ -2490,6 +2565,10 @@ const struct clk_ops clk_alpha_pll_lucid_5lpe_ops = { .recalc_rate = alpha_pll_lucid_recalc_rate, .round_rate = clk_alpha_pll_round_rate, .set_rate = alpha_pll_lucid_5lpe_set_rate, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL(clk_alpha_pll_lucid_5lpe_ops); @@ -2503,6 +2582,9 @@ const struct clk_ops clk_alpha_pll_fixed_lucid_ops = { .is_enabled = alpha_pll_lucid_is_enabled, .recalc_rate = alpha_pll_lucid_recalc_rate, .round_rate = clk_alpha_pll_round_rate, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL(clk_alpha_pll_fixed_lucid_ops); @@ -2523,6 +2605,10 @@ const struct clk_ops clk_alpha_pll_fixed_lucid_5lpe_ops = { .is_enabled = alpha_pll_lucid_is_enabled, .recalc_rate = alpha_pll_lucid_recalc_rate, .round_rate = clk_alpha_pll_round_rate, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL(clk_alpha_pll_fixed_lucid_5lpe_ops); diff --git a/drivers/clk/qcom/clk-branch.c b/drivers/clk/qcom/clk-branch.c index 07f91876a250..5a29357997cc 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2013, 2016, The Linux Foundation. All rights reserved. + * Copyright (c) 2013, 2016-2019, The Linux Foundation. All rights reserved. */ #include @@ -14,6 +14,7 @@ #include "clk-branch.h" #include "clk-debug.h" +#include "clk-regmap.h" static bool clk_branch_in_hwcg_mode(const struct clk_branch *br) { @@ -124,10 +125,17 @@ static void clk_branch_disable(struct clk_hw *hw) clk_branch_toggle(hw, false, clk_branch_check_halt); } +static void clk_branch_debug_init(struct clk_hw *hw, struct dentry *dentry) +{ + clk_common_debug_init(hw, dentry); + clk_debug_measure_add(hw, dentry); +} + const struct clk_ops clk_branch_ops = { .enable = clk_branch_enable, .disable = clk_branch_disable, .is_enabled = clk_is_enabled_regmap, + .debug_init = clk_branch_debug_init, }; EXPORT_SYMBOL_GPL(clk_branch_ops); @@ -141,17 +149,69 @@ static void clk_branch2_disable(struct clk_hw *hw) clk_branch_toggle(hw, false, clk_branch2_check_halt); } +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 struct clk_regmap_ops clk_branch2_regmap_ops = { + .list_registers = clk_branch2_list_registers, +}; + +static void clk_branch2_init(struct clk_hw *hw) +{ + struct clk_regmap *rclk = to_clk_regmap(hw); + + if (!rclk->ops) + rclk->ops = &clk_branch2_regmap_ops; +} + const struct clk_ops clk_branch2_ops = { .enable = clk_branch2_enable, .disable = clk_branch2_disable, .is_enabled = clk_is_enabled_regmap, - .debug_init = clk_debug_measure_add, + .init = clk_branch2_init, + .debug_init = clk_branch_debug_init, }; EXPORT_SYMBOL_GPL(clk_branch2_ops); const struct clk_ops clk_branch2_aon_ops = { .enable = clk_branch2_enable, .is_enabled = clk_is_enabled_regmap, + .init = clk_branch2_init, + .debug_init = clk_branch_debug_init, }; EXPORT_SYMBOL_GPL(clk_branch2_aon_ops); diff --git a/drivers/clk/qcom/clk-debug.c b/drivers/clk/qcom/clk-debug.c index 686ca87a9f3e..a63429096d71 100644 --- a/drivers/clk/qcom/clk-debug.c +++ b/drivers/clk/qcom/clk-debug.c @@ -386,3 +386,177 @@ int map_debug_bases(struct platform_device *pdev, const char *base, return 0; } EXPORT_SYMBOL(map_debug_bases); + +static void clock_print_rate_max_by_level(struct clk_hw *hw, + struct seq_file *s, int level) +{ + struct clk_regmap *rclk = to_clk_regmap(hw); + struct clk_vdd_class_data *vdd_data = &rclk->vdd_data; + struct clk_vdd_class *vdd_class = vdd_data->vdd_class; + int off, i, vdd_level, nregs = vdd_class->num_regulators; + unsigned long rate; + + rate = clk_get_rate(hw->clk); + vdd_level = clk_find_vdd_level(hw, vdd_data, rate); + + seq_printf(s, "%2s%10lu", vdd_level == level ? "[" : "", + vdd_data->rate_max[level]); + + for (i = 0; i < nregs; i++) { + off = nregs*level + i; + if (vdd_class->vdd_uv) + seq_printf(s, "%10u", vdd_class->vdd_uv[off]); + } + + if (vdd_level == level) + seq_puts(s, "]"); + + seq_puts(s, "\n"); +} + +static int rate_max_show(struct seq_file *s, void *unused) +{ + struct clk_hw *hw = s->private; + struct clk_regmap *rclk = to_clk_regmap(hw); + struct clk_vdd_class_data *vdd_data = &rclk->vdd_data; + struct clk_vdd_class *vdd_class = vdd_data->vdd_class; + int level = 0, i, nregs = vdd_class->num_regulators, vdd_level; + char reg_name[10]; + unsigned long rate; + + rate = clk_get_rate(hw->clk); + vdd_level = clk_find_vdd_level(hw, vdd_data, rate); + + if (vdd_level < 0) { + seq_printf(s, "could not find_vdd_level for %s, %ld\n", + clk_hw_get_name(hw), rate); + return 0; + } + + seq_printf(s, "%12s", ""); + for (i = 0; i < nregs; i++) { + snprintf(reg_name, ARRAY_SIZE(reg_name), "reg %d", i); + seq_printf(s, "%10s", reg_name); + } + + seq_printf(s, "\n%12s", "freq"); + for (i = 0; i < nregs; i++) + seq_printf(s, "%10s", "uV"); + + seq_puts(s, "\n"); + + for (level = 0; level < vdd_data->num_rate_max; level++) + clock_print_rate_max_by_level(hw, s, level); + + return 0; +} + +static int rate_max_open(struct inode *inode, struct file *file) +{ + return single_open(file, rate_max_show, inode->i_private); +} + +static const struct file_operations rate_max_fops = { + .open = rate_max_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static int list_rates_show(struct seq_file *s, void *unused) +{ + struct clk_hw *hw = s->private; + struct clk_regmap *rclk = to_clk_regmap(hw); + struct clk_vdd_class_data *vdd_data = &rclk->vdd_data; + struct clk_vdd_class *vdd_class = vdd_data->vdd_class; + int i = 0, level; + unsigned long rate, rate_max = 0; + + /* Find max frequency supported within voltage constraints. */ + if (!vdd_class) { + rate_max = ULONG_MAX; + } else { + for (level = 0; level < vdd_data->num_rate_max; level++) + if (vdd_data->rate_max[level]) + rate_max = vdd_data->rate_max[level]; + } + + /* + * List supported frequencies <= rate_max. Higher frequencies may + * appear in the frequency table, but are not valid and should not + * be listed. + */ + while (!IS_ERR_VALUE(rate = rclk->ops->list_rate(hw, i++, rate_max))) { + if (rate <= 0) + break; + seq_printf(s, "%lu\n", rate); + } + + return 0; +} + +static int list_rates_open(struct inode *inode, struct file *file) +{ + return single_open(file, list_rates_show, inode->i_private); +} + +static const struct file_operations list_rates_fops = { + .open = list_rates_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void clk_debug_print_hw(struct clk_hw *hw, struct seq_file *f) +{ + struct clk_regmap *rclk = to_clk_regmap(hw); + + if (IS_ERR_OR_NULL(hw)) + return; + + clk_debug_print_hw(clk_hw_get_parent(hw), f); + + seq_printf(f, "%s\n", clk_hw_get_name(hw)); + + if (rclk->ops && rclk->ops->list_registers) + rclk->ops->list_registers(f, hw); +} + +static int print_hw_show(struct seq_file *m, void *unused) +{ + struct clk_hw *hw = m->private; + + clk_debug_print_hw(hw, m); + + return 0; +} + +static int print_hw_open(struct inode *inode, struct file *file) +{ + return single_open(file, print_hw_show, inode->i_private); +} + +static const struct file_operations clock_print_hw_fops = { + .open = print_hw_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +void clk_common_debug_init(struct clk_hw *hw, struct dentry *dentry) +{ + struct clk_regmap *rclk = to_clk_regmap(hw); + + if (rclk->vdd_data.vdd_class) + debugfs_create_file("clk_rate_max", 0444, dentry, hw, + &rate_max_fops); + + if (rclk->ops && rclk->ops->list_rate) + debugfs_create_file("clk_list_rates", 0444, dentry, hw, + &list_rates_fops); + + debugfs_create_file("clk_print_regs", 0444, dentry, hw, + &clock_print_hw_fops); + +}; + diff --git a/drivers/clk/qcom/clk-debug.h b/drivers/clk/qcom/clk-debug.h index 84fb4aeec41c..e37e8628ae8c 100644 --- a/drivers/clk/qcom/clk-debug.h +++ b/drivers/clk/qcom/clk-debug.h @@ -90,4 +90,6 @@ void clk_debug_measure_add(struct clk_hw *hw, struct dentry *dentry); int map_debug_bases(struct platform_device *pdev, const char *base, struct clk_debug_mux *mux); +void clk_common_debug_init(struct clk_hw *hw, struct dentry *dentry); + #endif diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index a19cae9e2c1d..2e42e627af4a 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -20,6 +20,7 @@ #include "clk-rcg.h" #include "common.h" +#include "clk-debug.h" #define CMD_REG 0x0 #define CMD_UPDATE BIT(0) @@ -444,6 +445,61 @@ 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 int n, + unsigned long fmax) +{ + struct clk_rcg2 *rcg = to_clk_rcg2(hw); + const struct freq_tbl *f = rcg->freq_tbl; + size_t freq_tbl_size = 0; + + if (!f) + return -ENXIO; + + for (; f->freq; f++) + freq_tbl_size++; + + if (n > freq_tbl_size - 1) + return -EINVAL; + + return (rcg->freq_tbl + n)->freq; +} + static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate, enum freq_policy policy) { @@ -628,6 +684,19 @@ static void clk_rcg2_disable(struct clk_hw *hw) clk_rcg2_clear_force_enable(hw); } +static struct clk_regmap_ops clk_rcg2_regmap_ops = { + .list_rate = clk_rcg2_list_rate, + .list_registers = clk_rcg2_list_registers, +}; + +static void clk_rcg2_init(struct clk_hw *hw) +{ + struct clk_regmap *rclk = to_clk_regmap(hw); + + if (!rclk->ops) + rclk->ops = &clk_rcg2_regmap_ops; +} + const struct clk_ops clk_rcg2_ops = { .prepare = clk_prepare_regmap, .unprepare = clk_unprepare_regmap, @@ -642,6 +711,11 @@ 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, + .init = clk_rcg2_init, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL_GPL(clk_rcg2_ops); @@ -657,6 +731,11 @@ const struct clk_ops clk_rcg2_floor_ops = { .determine_rate = clk_rcg2_determine_floor_rate, .set_rate = clk_rcg2_set_floor_rate, .set_rate_and_parent = clk_rcg2_set_floor_rate_and_parent, + .init = clk_rcg2_init, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL_GPL(clk_rcg2_floor_ops); @@ -790,6 +869,11 @@ 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, + .init = clk_rcg2_init, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL_GPL(clk_edp_pixel_ops); @@ -854,6 +938,11 @@ 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, + .init = clk_rcg2_init, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL_GPL(clk_byte_ops); @@ -928,6 +1017,11 @@ 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, + .init = clk_rcg2_init, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL_GPL(clk_byte2_ops); @@ -1022,6 +1116,11 @@ 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, + .init = clk_rcg2_init, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL_GPL(clk_pixel_ops); @@ -1108,6 +1207,11 @@ const struct clk_ops clk_dp_ops = { .set_rate = clk_dp_set_rate, .set_rate_and_parent = clk_dp_set_rate_and_parent, .determine_rate = clk_dp_determine_rate, + .init = clk_rcg2_init, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL(clk_dp_ops); @@ -1203,6 +1307,11 @@ 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, + .init = clk_rcg2_init, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL_GPL(clk_gfx3d_ops); @@ -1314,6 +1423,11 @@ const struct clk_ops clk_rcg2_shared_ops = { .determine_rate = clk_rcg2_determine_rate, .set_rate = clk_rcg2_shared_set_rate, .set_rate_and_parent = clk_rcg2_shared_set_rate_and_parent, + .init = clk_rcg2_init, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL_GPL(clk_rcg2_shared_ops); @@ -1458,6 +1572,11 @@ static const struct clk_ops clk_rcg2_dfs_ops = { .get_parent = clk_rcg2_get_parent, .determine_rate = clk_rcg2_dfs_determine_rate, .recalc_rate = clk_rcg2_dfs_recalc_rate, + .init = clk_rcg2_init, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; static int clk_rcg2_enable_dfs(const struct clk_rcg_dfs_data *data, @@ -1601,5 +1720,10 @@ const struct clk_ops clk_rcg2_dependent_ops = { .determine_rate = clk_rcg2_dependent_determine_rate, .set_rate = clk_rcg2_dependent_set_rate, .set_rate_and_parent = clk_rcg2_dependent_set_rate_and_parent, + .init = clk_rcg2_init, + .debug_init = clk_common_debug_init, +#ifdef CONFIG_COMMON_CLK_QCOM_DEBUG + .list_rate_vdd_level = clk_list_rate_vdd_level, +#endif }; EXPORT_SYMBOL(clk_rcg2_dependent_ops); diff --git a/drivers/clk/qcom/clk-regmap.h b/drivers/clk/qcom/clk-regmap.h index 9efb74b2a6cb..30f1ae366f9e 100644 --- a/drivers/clk/qcom/clk-regmap.h +++ b/drivers/clk/qcom/clk-regmap.h @@ -5,10 +5,28 @@ #define __QCOM_CLK_REGMAP_H__ #include +#include #include "vdd-class.h" struct regmap; +/** + * struct clk_regmap_ops - Operations for clk_regmap. + * + * @list_registers: Queries the hardware to get the current register contents. + * This callback is optional. + * + * @list_rate: On success, return the nth supported frequency for a given + * clock that is below rate_max. Return -ENXIO in case there is + * no frequency table. + */ +struct clk_regmap_ops { + void (*list_registers)(struct seq_file *f, + struct clk_hw *hw); + long (*list_rate)(struct clk_hw *hw, unsigned int n, + unsigned long rate_max); +}; + /** * struct clk_regmap - regmap supporting clock * @hw: handle between common and hardware-specific interfaces @@ -19,7 +37,9 @@ struct regmap; * @enable_is_inverted: flag to indicate set enable_mask bits to disable * when using clock_enable_regmap and friends APIs. * @vdd_data: struct containing vdd-class data for this clock + * @ops: operations this clk_regmap supports */ + struct clk_regmap { struct clk_hw hw; struct clk_hw *dependent_hw; @@ -28,6 +48,7 @@ struct clk_regmap { unsigned int enable_mask; bool enable_is_inverted; struct clk_vdd_class_data vdd_data; + struct clk_regmap_ops *ops; }; #define to_clk_regmap(_hw) container_of(_hw, struct clk_regmap, hw) @@ -42,4 +63,9 @@ int clk_post_change_regmap(struct clk_hw *hw, unsigned long old_rate, unsigned long cur_rate); int devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk); +struct clk_register_data { + char *name; + u32 offset; +}; + #endif diff --git a/drivers/clk/qcom/vdd-class.c b/drivers/clk/qcom/vdd-class.c index fcef07077d4b..f2bcdb08a062 100644 --- a/drivers/clk/qcom/vdd-class.c +++ b/drivers/clk/qcom/vdd-class.c @@ -6,6 +6,7 @@ #include #include "vdd-class.h" +#include "clk-regmap.h" /* * Aggregate the vdd_class level votes and call regulator framework functions @@ -169,3 +170,15 @@ int clk_find_vdd_level(struct clk_hw *hw, return level; } EXPORT_SYMBOL(clk_find_vdd_level); + +int clk_list_rate_vdd_level(struct clk_hw *hw, unsigned int rate) +{ + struct clk_regmap *rclk = to_clk_regmap(hw); + struct clk_vdd_class_data *vdd_data = &rclk->vdd_data; + + if (!vdd_data->vdd_class) + return 0; + + return clk_find_vdd_level(hw, vdd_data, rate); +} + diff --git a/drivers/clk/qcom/vdd-class.h b/drivers/clk/qcom/vdd-class.h index 9b2b884debdb..a7b8d7b06b34 100644 --- a/drivers/clk/qcom/vdd-class.h +++ b/drivers/clk/qcom/vdd-class.h @@ -66,5 +66,6 @@ int clk_find_vdd_level(struct clk_hw *hw, struct clk_vdd_class_data *vdd_data, unsigned long rate); int clk_vote_vdd_level(struct clk_vdd_class_data *vdd_class, int level); int clk_unvote_vdd_level(struct clk_vdd_class_data *vdd_class, int level); +int clk_list_rate_vdd_level(struct clk_hw *hw, unsigned int rate); #endif