Merge "clk: qcom: clk-alpha-pll: Don't round up alpha value in HW"

This commit is contained in:
qctecmdr 2022-10-25 14:28:08 -07:00 committed by Gerrit - the friendly Code Review server
commit 0e717f897d
6 changed files with 187 additions and 13 deletions

View File

@ -542,7 +542,17 @@ static void clk_alpha_pll_disable(struct clk_hw *hw)
static unsigned long
alpha_pll_calc_rate(u64 prate, u32 l, u32 a, u32 alpha_width)
{
return (prate * l) + ((prate * a) >> ALPHA_SHIFT(alpha_width));
unsigned long rate;
rate = (prate * l) + ((prate * a) >> ALPHA_SHIFT(alpha_width));
/*
* PLLs with narrow ALPHA (e.g. 16 bits) aren't able to hit all
* frequencies precisely and may be under by a few hundred Hz. Round to
* the nearest KHz to avoid reporting strange, slightly lower than
* requested frequencies. The small delta has no functional impact.
*/
return roundup(rate, 1000);
}
static unsigned long
@ -564,10 +574,7 @@ alpha_pll_round_rate(unsigned long rate, unsigned long prate, u32 *l, u64 *a,
/* Upper ALPHA_BITWIDTH bits of Alpha */
quotient = remainder << ALPHA_SHIFT(alpha_width);
remainder = do_div(quotient, prate);
if (remainder)
quotient++;
do_div(quotient, prate);
*a = quotient;
return alpha_pll_calc_rate(prate, *l, *a, alpha_width);
@ -749,7 +756,7 @@ alpha_huayra_pll_calc_rate(u64 prate, u32 l, u32 a)
if (a >= BIT(PLL_HUAYRA_ALPHA_WIDTH - 1))
l -= 1;
return (prate * l) + (prate * a >> PLL_HUAYRA_ALPHA_WIDTH);
return alpha_pll_calc_rate(prate, l, a, PLL_HUAYRA_ALPHA_WIDTH);
}
static unsigned long
@ -769,10 +776,7 @@ alpha_huayra_pll_round_rate(unsigned long rate, unsigned long prate,
}
quotient = remainder << PLL_HUAYRA_ALPHA_WIDTH;
remainder = do_div(quotient, prate);
if (remainder)
quotient++;
do_div(quotient, prate);
/*
* alpha_val should be in twos complement number in the range

View File

@ -184,6 +184,12 @@ static void clk_branch2_list_registers(struct seq_file *f, struct clk_hw *hw)
{"APSS_SLEEP_VOTE", 0x4},
};
static struct clk_register_data data2[] = {
{"MEM_ENABLE", 0x0},
{"MEM_ENABLE_ACK", 0x0},
{"MEM_ENABLE_ACK_MASK", 0x0},
};
size = ARRAY_SIZE(data);
for (i = 0; i < size; i++) {
@ -204,6 +210,18 @@ static void clk_branch2_list_registers(struct seq_file *f, struct clk_hw *hw)
}
}
}
if (br->mem_enable_reg && br->mem_ack_reg) {
regmap_read(br->clkr.regmap, br->mem_enable_reg +
data2[0].offset, &val);
clock_debug_output(f, "%20s: 0x%.8x\n", data2[0].name, val);
regmap_read(br->clkr.regmap, br->mem_ack_reg +
data2[1].offset, &val);
clock_debug_output(f, "%20s: 0x%.8x\n", data2[1].name, val);
clock_debug_output(f, "%20s: 0x%.8x\n", data2[2].name,
br->mem_enable_ack_bit);
}
}
static int clk_branch2_set_flags(struct clk_hw *hw, unsigned long flags)
@ -260,6 +278,39 @@ static int clk_branch2_init(struct clk_hw *hw)
return 0;
}
static int clk_branch2_mem_enable(struct clk_hw *hw)
{
struct clk_branch *br = to_clk_branch(hw);
u32 val;
int count = 200;
regmap_update_bits(br->clkr.regmap, br->mem_enable_reg,
br->mem_enable_ack_bit, br->mem_enable_ack_bit);
regmap_read(br->clkr.regmap, br->mem_ack_reg, &val);
pr_debug("%s Val 0x%x\n", __func__, val);
while (count-- > 0) {
if (val & br->mem_enable_ack_bit) {
pr_debug("%s Val 0x%x\n", __func__, val);
return clk_branch2_enable(hw);
}
udelay(1);
regmap_read(br->clkr.regmap, br->mem_ack_reg, &val);
}
return -EBUSY;
}
static void clk_branch2_mem_disable(struct clk_hw *hw)
{
struct clk_branch *br = to_clk_branch(hw);
regmap_update_bits(br->clkr.regmap, br->mem_enable_reg,
br->mem_enable_ack_bit, 0);
return clk_branch2_disable(hw);
}
const struct clk_ops clk_branch2_ops = {
.prepare = clk_prepare_regmap,
.unprepare = clk_unprepare_regmap,
@ -290,6 +341,15 @@ const struct clk_ops clk_branch2_force_off_ops = {
};
EXPORT_SYMBOL(clk_branch2_force_off_ops);
const struct clk_ops clk_branch2_mem_ops = {
.enable = clk_branch2_mem_enable,
.disable = clk_branch2_mem_disable,
.is_enabled = clk_is_enabled_regmap,
.init = clk_branch2_init,
.debug_init = clk_branch_debug_init,
};
EXPORT_SYMBOL(clk_branch2_mem_ops);
static unsigned long clk_branch2_hw_ctl_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{

View File

@ -23,8 +23,11 @@
struct clk_branch {
u32 hwcg_reg;
u32 halt_reg;
u32 mem_enable_reg;
u32 mem_ack_reg;
u8 hwcg_bit;
u8 halt_bit;
u8 mem_enable_ack_bit;
u8 halt_check;
#define BRANCH_VOTED BIT(7) /* Delay on disable */
#define BRANCH_HALT 0 /* pol: 1 = halt */
@ -43,6 +46,7 @@ extern const struct clk_ops clk_branch2_hw_ctl_ops;
extern const struct clk_ops clk_branch_simple_ops;
extern const struct clk_ops clk_branch2_aon_ops;
extern const struct clk_ops clk_branch2_force_off_ops;
extern const struct clk_ops clk_branch2_mem_ops;
#define to_clk_branch(_hw) \
container_of(to_clk_regmap(_hw), struct clk_branch, clkr)

View File

@ -7,7 +7,7 @@
void clk_hw_populate_clock_opp_table(struct device_node *np,
struct clk_hw *hw);
#define MAX_LEN_OPP_HANDLE 50
#define MAX_LEN_OPP_HANDLE 100
#define LEN_OPP_HANDLE 16
#endif

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
* Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/kernel.h>
@ -23,10 +24,14 @@
#include <linux/mailbox_client.h>
#include <linux/mailbox_controller.h>
#include <linux/mailbox/qmp.h>
#include <linux/interconnect.h>
#include "../../regulator/internal.h"
#include "gdsc-debug.h"
#define CREATE_TRACE_POINTS
#include "trace-gdsc.h"
/* GDSCR */
#define PWR_ON_MASK BIT(31)
#define CLK_DIS_WAIT_MASK (0xF << 12)
@ -74,6 +79,7 @@ struct gdsc {
struct mbox_client mbox_client;
struct mbox_chan *mbox;
struct reset_control **reset_clocks;
struct icc_path **paths;
bool toggle_logic;
bool retain_ff_enable;
bool resets_asserted;
@ -88,6 +94,7 @@ struct gdsc {
int reset_count;
int root_clk_idx;
int sw_reset_count;
int path_count;
u32 gds_timeout;
bool skip_disable_before_enable;
bool skip_disable;
@ -96,8 +103,8 @@ struct gdsc {
};
enum gdscr_status {
ENABLED,
DISABLED,
ENABLED,
};
static inline u32 gdsc_mb(struct gdsc *gds)
@ -144,8 +151,11 @@ static int poll_gdsc_status(struct gdsc *sc, enum gdscr_status status)
break;
}
if (val)
if (val) {
trace_gdsc_time(sc->rdesc.name, status,
sc->gds_timeout - count, 0);
return 0;
}
/*
* There is no guarantee about the delay needed for the enable
* bit in the GDSCR to be set or reset after the GDSC state
@ -156,6 +166,8 @@ static int poll_gdsc_status(struct gdsc *sc, enum gdscr_status status)
udelay(1);
}
trace_gdsc_time(sc->rdesc.name, status,
sc->gds_timeout - count, 1);
return -ETIMEDOUT;
}
@ -258,6 +270,14 @@ static int gdsc_enable(struct regulator_dev *rdev)
}
if (sc->toggle_logic) {
for (i = 0; i < sc->path_count; i++) {
ret = icc_set_bw(sc->paths[i], 1, 1);
if (ret) {
dev_err(&rdev->dev, "Failed to vote BW for %d, ret=%d\n", i, ret);
return ret;
}
}
if (sc->sw_reset_count) {
for (i = 0; i < sc->sw_reset_count; i++)
regmap_set_bits(sc->sw_resets[i], REG_OFFSET,
@ -471,6 +491,13 @@ static int gdsc_disable(struct regulator_dev *rdev)
regmap_write(sc->domain_addr, REG_OFFSET, regval);
}
for (i = 0; i < sc->path_count; i++) {
ret = icc_set_bw(sc->paths[i], 0, 0);
if (ret) {
dev_err(&rdev->dev, "Failed to unvote BW for %d: %d\n", i, ret);
return ret;
}
}
} else {
for (i = sc->reset_count - 1; i >= 0; i--)
reset_control_assert(sc->reset_clocks[i]);
@ -747,6 +774,16 @@ static int gdsc_parse_dt_data(struct gdsc *sc, struct device *dev,
return sc->clock_count;
}
sc->path_count = of_property_count_strings(dev->of_node,
"interconnect-names");
if (sc->path_count == -EINVAL) {
sc->path_count = 0;
} else if (sc->path_count < 0) {
dev_err(dev, "Failed to get interconnect names, ret=%d\n",
sc->path_count);
return sc->path_count;
}
sc->root_en = of_property_read_bool(dev->of_node,
"qcom,enable-root-clk");
sc->force_root_en = of_property_read_bool(dev->of_node,
@ -872,6 +909,26 @@ static int gdsc_get_resources(struct gdsc *sc, struct platform_device *pdev)
sc->root_clk_idx = i;
}
sc->paths = devm_kcalloc(dev, sc->path_count, sizeof(*sc->paths), GFP_KERNEL);
if (sc->path_count && !sc->paths)
return -ENOMEM;
for (i = 0; i < sc->path_count; i++) {
const char *name;
of_property_read_string_index(dev->of_node, "interconnect-names", i,
&name);
sc->paths[i] = of_icc_get(dev, name);
if (IS_ERR(sc->paths[i])) {
ret = PTR_ERR(sc->paths[i]);
if (ret != -EPROBE_DEFER)
dev_err(dev, "Failed to get path %s, ret=%d\n",
name, ret);
return ret;
}
}
if ((sc->root_en || sc->force_root_en) && (sc->root_clk_idx == -1)) {
dev_err(dev, "Failed to get root clock name\n");
return -EINVAL;

View File

@ -0,0 +1,49 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM clk_gdsc
#if !defined(_TRACE_CLOCK_GDSC_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_CLOCK_GDSC
#include <linux/tracepoint.h>
TRACE_EVENT(gdsc_time,
TP_PROTO(const char *name, u32 enabling, u32 time_us, u32 timed_out),
TP_ARGS(name, enabling, time_us, timed_out),
TP_STRUCT__entry(
__string(name, name)
__field(unsigned int, enabling)
__field(unsigned int, time_us)
__field(unsigned int, timed_out)
),
TP_fast_assign(
__assign_str(name, name);
__entry->enabling = enabling;
__entry->time_us = time_us;
__entry->timed_out = timed_out;
),
TP_printk("%s enabling:%d time_us:%d timed_out:%d",
__get_str(name), __entry->enabling,
__entry->time_us, __entry->timed_out)
);
#endif /* _TRACE_CLOCK_GDSC */
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#undef TRACE_INCLUDE_FILE
#define TRACE_INCLUDE_FILE trace-gdsc
#include <trace/define_trace.h>