diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index f4ac77632350..9ab0e8922c8e 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -1004,12 +1004,16 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *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}, + {"PLL_MODE", PLL_OFF_MODE}, + {"PLL_L_VAL", PLL_OFF_L_VAL}, + {"PLL_ALPHA_VAL", PLL_OFF_ALPHA_VAL}, + {"PLL_ALPHA_VAL_U", PLL_OFF_ALPHA_VAL_U}, + {"PLL_TEST_CTL", PLL_OFF_TEST_CTL}, + {"PLL_TEST_CTL_U", PLL_OFF_TEST_CTL_U}, + {"PLL_USER_CTL", PLL_OFF_USER_CTL}, + {"PLL_USER_CTL_U", PLL_OFF_USER_CTL_U}, + {"PLL_CONFIG_CTL", PLL_OFF_CONFIG_CTL}, + {"PLL_STATUS", PLL_OFF_STATUS}, }; static struct clk_register_data data1[] = { @@ -1019,12 +1023,13 @@ static void clk_alpha_pll_list_registers(struct seq_file *f, struct clk_hw *hw) size = ARRAY_SIZE(data); for (i = 0; i < size; i++) { - regmap_read(pll->clkr.regmap, pll->offset + data[i].offset, - &val); + regmap_read(pll->clkr.regmap, pll->offset + + pll->regs[data[i].offset], &val); clock_debug_output(f, "%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 + + pll->regs[data[0].offset], &val); if (val & PLL_FSM_ENA) { regmap_read(pll->clkr.regmap, pll->clkr.enable_reg + @@ -1848,6 +1853,10 @@ void clk_trion_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, regmap_update_bits(regmap, PLL_MODE(pll), PLL_UPDATE_BYPASS, PLL_UPDATE_BYPASS); + if (pll->flags & SUPPORTS_FSM_LEGACY_MODE) + regmap_update_bits(regmap, PLL_MODE(pll), PLL_FSM_LEGACY_MODE, + PLL_FSM_LEGACY_MODE); + /* Disable PLL output */ regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, 0); @@ -2010,7 +2019,7 @@ static void lucid_pll_list_registers(struct seq_file *f, if (val & PLL_FSM_ENA) { regmap_read(pll->clkr.regmap, pll->clkr.enable_reg + data1[0].offset, &val); - clock_debug_output(f, "%20s: 0x%.8x\n", data[0].name, val); + clock_debug_output(f, "%20s: 0x%.8x\n", data1[0].name, val); } } @@ -2128,7 +2137,7 @@ static void clk_agera_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); - clock_debug_output(f, "%20s: 0x%.8x\n", data[0].name, val); + clock_debug_output(f, "%20s: 0x%.8x\n", data1[0].name, val); } } @@ -2976,7 +2985,7 @@ static void clk_regera_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); - clock_debug_output(f, "%20s: 0x%.8x\n", data[0].name, val); + clock_debug_output(f, "%20s: 0x%.8x\n", data1[0].name, val); } } @@ -3343,7 +3352,7 @@ static void lucid_evo_pll_list_registers(struct seq_file *f, if (val & LUCID_EVO_ENABLE_VOTE_RUN) { regmap_read(pll->clkr.regmap, pll->clkr.enable_reg + data1[0].offset, &val); - clock_debug_output(f, "%20s: 0x%.8x\n", data[0].name, val); + clock_debug_output(f, "%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 4f967084137e..d19bb2e7f5bb 100644 --- a/drivers/clk/qcom/clk-branch.c +++ b/drivers/clk/qcom/clk-branch.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013, 2016, 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -61,6 +62,10 @@ static bool clk_branch2_check_halt(const struct clk_branch *br, bool enabling) if (enabling) { val &= mask; + + if (br->halt_check == BRANCH_HALT_INVERT) + return (val & BRANCH_CLK_OFF) == BRANCH_CLK_OFF; + return (val & BRANCH_CLK_OFF) == 0 || val == BRANCH_NOC_FSM_STATUS_ON; } else { @@ -68,9 +73,25 @@ static bool clk_branch2_check_halt(const struct clk_branch *br, bool enabling) } } +static int get_branch_timeout(const struct clk_branch *br) +{ + int rate, period_us, timeout; + + /* + * The time it takes a clock branch to toggle is roughly 3 clock cycles. + */ + rate = clk_hw_get_rate(&br->clkr.hw); + period_us = 1000000 / rate; + timeout = 3 * period_us; + + return max(timeout, 200); +} + static int clk_branch_wait(const struct clk_branch *br, bool enabling, bool (check_halt)(const struct clk_branch *, bool)) { + int timeout, count; + bool voted = br->halt_check & BRANCH_VOTED; /* * Skip checking halt bit if we're explicitly ignoring the bit or the @@ -84,15 +105,15 @@ static int clk_branch_wait(const struct clk_branch *br, bool enabling, } else if (br->halt_check == BRANCH_HALT_ENABLE || br->halt_check == BRANCH_HALT || (enabling && voted)) { - int count = 200; + timeout = get_branch_timeout(br); - while (count-- > 0) { + for (count = timeout; count > 0; count--) { if (check_halt(br, enabling)) return 0; udelay(1); } - WARN_CLK((struct clk_hw *)&br->clkr.hw, 1, "status stuck at 'o%s'", - enabling ? "ff" : "n"); + WARN_CLK((struct clk_hw *)&br->clkr.hw, 1, "status stuck at 'o%s' after %d us", + enabling ? "ff" : "n", timeout); return -EBUSY; } return 0; diff --git a/drivers/clk/qcom/clk-branch.h b/drivers/clk/qcom/clk-branch.h index 2657c0ab7486..da471d6f4eb6 100644 --- a/drivers/clk/qcom/clk-branch.h +++ b/drivers/clk/qcom/clk-branch.h @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) 2013, 2016, 2020 The Linux Foundation. All rights reserved. */ +/* Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef __QCOM_CLK_BRANCH_H__ #define __QCOM_CLK_BRANCH_H__ @@ -36,6 +37,7 @@ struct clk_branch { #define BRANCH_HALT_ENABLE_VOTED (BRANCH_HALT_ENABLE | BRANCH_VOTED) #define BRANCH_HALT_DELAY 2 /* No bit to check; just delay */ #define BRANCH_HALT_SKIP 3 /* Don't check halt bit */ +#define BRANCH_HALT_INVERT 4 /* Invert logic for halt bit */ struct clk_regmap clkr; }; diff --git a/drivers/clk/qcom/clk-debug.c b/drivers/clk/qcom/clk-debug.c index 80fd26e84565..ffb36a69d973 100644 --- a/drivers/clk/qcom/clk-debug.c +++ b/drivers/clk/qcom/clk-debug.c @@ -25,6 +25,7 @@ static struct clk_hw *measure; static bool debug_suspend; static bool debug_suspend_atomic; +static bool qcom_clk_debug_inited; static struct dentry *clk_debugfs_suspend; static struct dentry *clk_debugfs_suspend_atomic; @@ -479,6 +480,8 @@ static int clk_debug_measure_get(void *data, u64 *val) *val *= get_mux_divs(measure); disable_debug_clks(measure); } + + trace_clk_measure(clk_hw_get_name(hw), *val); exit: mutex_unlock(&clk_debug_lock); clk_runtime_put_debug_mux(meas); @@ -765,6 +768,10 @@ void clk_common_debug_init(struct clk_hw *hw, struct dentry *dentry) debugfs_create_file("clk_print_regs", 0444, dentry, hw, &clock_print_hw_fops); + if (!qcom_clk_debug_inited) { + clk_debug_init(); + qcom_clk_debug_inited = true; + } } static int clk_list_rate_vdd_level(struct clk_hw *hw, unsigned int rate) @@ -959,9 +966,7 @@ static void clk_debug_suspend_trace_probe(void *unused, { if (start && val > 0 && !strcmp("machine_suspend", action)) { pr_info("Enabled Clocks:\n"); - mutex_lock(&clk_debug_lock); clock_debug_print_enabled_clocks(NULL); - mutex_unlock(&clk_debug_lock); } } diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h index fa1e8533ba35..e3f68354e85b 100644 --- a/drivers/clk/qcom/clk-rcg.h +++ b/drivers/clk/qcom/clk-rcg.h @@ -154,6 +154,7 @@ struct clk_rcg2 { u8 safe_src_index; const struct parent_map *parent_map; const struct freq_tbl *freq_tbl; + unsigned long configured_freq; unsigned long current_freq; bool enable_safe_config; struct clk_regmap clkr; diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index ca1bb000a3af..782ba871e3a0 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013, 2016-2018, 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -118,9 +119,25 @@ static u8 clk_rcg2_get_parent(struct clk_hw *hw) return __clk_rcg2_get_parent(hw, cfg); } +static int get_update_timeout(const struct clk_rcg2 *rcg) +{ + int timeout = 0; + + /* + * The time it takes an RCG to update is roughly 3 clock cycles of the + * old and new clock rates. + */ + if (rcg->current_freq) + timeout += 3 * (1000000 / rcg->current_freq); + if (rcg->configured_freq) + timeout += 3 * (1000000 / rcg->configured_freq); + + return max(timeout, 500); +} + static int update_config(struct clk_rcg2 *rcg) { - int count, ret; + int timeout, count, ret; u32 cmd; struct clk_hw *hw = &rcg->clkr.hw; @@ -129,8 +146,10 @@ static int update_config(struct clk_rcg2 *rcg) if (ret) return ret; + timeout = get_update_timeout(rcg); + /* Wait for update to take effect */ - for (count = 500; count > 0; count--) { + for (count = timeout; count > 0; count--) { ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd); if (ret) return ret; @@ -139,7 +158,7 @@ static int update_config(struct clk_rcg2 *rcg) udelay(1); } - WARN_CLK(hw, 1, "rcg didn't update its configuration."); + WARN_CLK(hw, 1, "rcg didn't update its configuration after %d us.", timeout); return -EBUSY; } @@ -487,6 +506,8 @@ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) if (ret) return ret; + rcg->configured_freq = f->freq; + return update_config(rcg); } @@ -866,6 +887,8 @@ const struct clk_ops clk_rcg2_floor_ops = { .pre_rate_change = clk_pre_change_regmap, .post_rate_change = clk_post_change_regmap, .is_enabled = clk_rcg2_is_enabled, + .enable = clk_rcg2_enable, + .disable = clk_rcg2_disable, .get_parent = clk_rcg2_get_parent, .set_parent = clk_rcg2_set_parent, .recalc_rate = clk_rcg2_recalc_rate, diff --git a/drivers/clk/qcom/clk-regmap-mux.c b/drivers/clk/qcom/clk-regmap-mux.c index 45d9cca28064..b0c2440ab1e7 100644 --- a/drivers/clk/qcom/clk-regmap-mux.c +++ b/drivers/clk/qcom/clk-regmap-mux.c @@ -9,6 +9,7 @@ #include #include "clk-regmap-mux.h" +#include "clk-debug.h" static inline struct clk_regmap_mux *to_clk_regmap_mux(struct clk_hw *hw) { @@ -49,9 +50,34 @@ static int mux_set_parent(struct clk_hw *hw, u8 index) return regmap_update_bits(clkr->regmap, mux->reg, mask, val); } +static void clk_regmap_mux_list_registers(struct seq_file *f, struct clk_hw *hw) +{ + struct clk_regmap_mux *mux = to_clk_regmap_mux(hw); + int val; + + regmap_read(mux->clkr.regmap, mux->reg, &val); + clock_debug_output(f, "%20s: 0x%.8x\n", "MUXR", val); +} + +static struct clk_regmap_ops clk_regmap_mux_regmap_ops = { + .list_registers = clk_regmap_mux_list_registers, +}; + +static int clk_regmap_mux_init(struct clk_hw *hw) +{ + struct clk_regmap *rclk = to_clk_regmap(hw); + + if (!rclk->ops) + rclk->ops = &clk_regmap_mux_regmap_ops; + + return 0; +} + const struct clk_ops clk_regmap_mux_closest_ops = { .get_parent = mux_get_parent, .set_parent = mux_set_parent, .determine_rate = __clk_mux_determine_rate_closest, + .init = clk_regmap_mux_init, + .debug_init = clk_common_debug_init, }; EXPORT_SYMBOL_GPL(clk_regmap_mux_closest_ops); diff --git a/drivers/clk/qcom/clk-regmap.c b/drivers/clk/qcom/clk-regmap.c index 4c0261cbcb18..c1e76bbe0e54 100644 --- a/drivers/clk/qcom/clk-regmap.c +++ b/drivers/clk/qcom/clk-regmap.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2014, 2019-2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2014, 2019-2021 The Linux Foundation. All rights reserved. */ #include @@ -13,7 +13,7 @@ #include "clk-debug.h" static LIST_HEAD(clk_regmap_list); -static DEFINE_MUTEX(clk_regmap_lock); +static DEFINE_SPINLOCK(clk_regmap_lock); /** * clk_is_enabled_regmap - standard is_enabled() for regmap users @@ -195,7 +195,7 @@ EXPORT_SYMBOL(clk_post_change_regmap); int clk_prepare_regmap(struct clk_hw *hw) { struct clk_regmap *rclk = to_clk_regmap(hw); - int rate = clk_hw_get_rate(hw); + unsigned long rate = clk_hw_get_rate(hw); int vdd_level; if (!rclk->vdd_data.rate_max) @@ -251,14 +251,14 @@ bool clk_is_regmap_clk(struct clk_hw *hw) bool is_regmap_clk = false; if (hw) { - mutex_lock(&clk_regmap_lock); + spin_lock(&clk_regmap_lock); list_for_each_entry(rclk, &clk_regmap_list, list_node) { if (&rclk->hw == hw) { is_regmap_clk = true; break; } } - mutex_unlock(&clk_regmap_lock); + spin_unlock(&clk_regmap_lock); } return is_regmap_clk; @@ -299,9 +299,9 @@ int devm_clk_register_regmap(struct device *dev, struct clk_regmap *rclk) ret = devm_clk_hw_register(dev, &rclk->hw); if (!ret) { - mutex_lock(&clk_regmap_lock); + spin_lock(&clk_regmap_lock); list_add(&rclk->list_node, &clk_regmap_list); - mutex_unlock(&clk_regmap_lock); + spin_unlock(&clk_regmap_lock); ret = clk_hw_debug_register(dev, &rclk->hw); } diff --git a/drivers/clk/qcom/common.c b/drivers/clk/qcom/common.c index a45119c50b80..21f5c2ae2354 100644 --- a/drivers/clk/qcom/common.c +++ b/drivers/clk/qcom/common.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2013-2014, 2017-2021, The Linux Foundation. * All rights reserved. + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. */ #include @@ -251,26 +252,29 @@ static void qcom_cc_set_critical(struct device *dev, struct qcom_cc *cc) } of_property_for_each_u32(dev->of_node, "qcom,critical-devices", prop, p, i) { - np = of_find_node_by_phandle(i); - if (!np) - continue; - - cnt = of_count_phandle_with_args(np, "clocks", "#clock-cells"); - - for (i = 0; i < cnt; i++) { - of_parse_phandle_with_args(np, "clocks", "#clock-cells", - i, &args); - clock_idx = args.args[0]; - - if (args.np != dev->of_node || clock_idx >= cc->num_rclks) + for (np = of_find_node_by_phandle(i); np; np = of_get_parent(np)) { + if (!of_property_read_bool(np, "clocks")) { + of_node_put(np); continue; + } - if (cc->rclks[clock_idx]) - cc->rclks[clock_idx]->flags |= QCOM_CLK_IS_CRITICAL; - of_node_put(args.np); + cnt = of_count_phandle_with_args(np, "clocks", "#clock-cells"); + + for (i = 0; i < cnt; i++) { + of_parse_phandle_with_args(np, "clocks", "#clock-cells", + i, &args); + clock_idx = args.args[0]; + + if (args.np != dev->of_node || clock_idx >= cc->num_rclks) + continue; + + if (cc->rclks[clock_idx]) + cc->rclks[clock_idx]->flags |= QCOM_CLK_IS_CRITICAL; + of_node_put(args.np); + } + + of_node_put(np); } - - of_node_put(np); } } @@ -589,12 +593,6 @@ int qcom_cc_runtime_suspend(struct device *dev) } EXPORT_SYMBOL(qcom_cc_runtime_suspend); -static int __init qcom_clk_init(void) -{ - return clk_debug_init(); -} -subsys_initcall(qcom_clk_init); - static void __exit qcom_clk_exit(void) { clk_debug_exit(); diff --git a/drivers/clk/qcom/gdsc-regulator.c b/drivers/clk/qcom/gdsc-regulator.c index 60f4b7695a47..3680a801b02c 100644 --- a/drivers/clk/qcom/gdsc-regulator.c +++ b/drivers/clk/qcom/gdsc-regulator.c @@ -121,7 +121,7 @@ static int poll_gdsc_status(struct gdsc *sc, enum gdscr_status status) int count = sc->gds_timeout; u32 val, reg_offset; - if (sc->hw_ctrl) + if (sc->hw_ctrl && !sc->cfg_gdscr) regmap = sc->hw_ctrl; else regmap = sc->regmap; diff --git a/drivers/clk/qcom/trace.h b/drivers/clk/qcom/trace.h index a95d8076cd74..9fa5d24f53ff 100644 --- a/drivers/clk/qcom/trace.h +++ b/drivers/clk/qcom/trace.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-only * * Copyright (c) 2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. */ #undef TRACE_SYSTEM @@ -47,6 +48,33 @@ DEFINE_EVENT(clk_state_dump, clk_state, TP_ARGS(name, prepare_count, enable_count, rate, vdd_level) ); +DECLARE_EVENT_CLASS(clk_measure_support, + + TP_PROTO(const char *name, unsigned long rate), + + TP_ARGS(name, rate), + + TP_STRUCT__entry( + __string(name, name) + __field(unsigned long, rate) + ), + + TP_fast_assign( + __assign_str(name, name); + __entry->rate = rate; + ), + + TP_printk("%s rate: %lu", + __get_str(name), (unsigned long)__entry->rate) +); + +DEFINE_EVENT(clk_measure_support, clk_measure, + + TP_PROTO(const char *name, unsigned long rate), + + TP_ARGS(name, rate) +); + #endif /* _TRACE_CLOCK_QCOM */ /* This part must be outside protection */