Merge "clk: qcom: use spin_lock to guard qcom_regmap_list"

This commit is contained in:
qctecmdr 2022-10-25 18:31:46 -07:00 committed by Gerrit - the friendly Code Review server
commit bc3ed0087b
11 changed files with 166 additions and 53 deletions

View File

@ -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);
}
}

View File

@ -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 <linux/kernel.h>
@ -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;

View File

@ -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;
};

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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 <linux/kernel.h>
@ -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,

View File

@ -9,6 +9,7 @@
#include <linux/export.h>
#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);

View File

@ -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 <linux/device.h>
@ -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);
}

View File

@ -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 <linux/export.h>
@ -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();

View File

@ -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;

View File

@ -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 */