android_kernel_xiaomi_sm8350/drivers/clk/qcom/clk-branch.c
Veera Vegivada 22fcc4ebff clk: qcom: common: Add set_flags op in clk_regmap_ops
Add set_flags op in clk_regmap_ops and update the
set_flags op for branch's clk_regmap_ops.
Update qcom_clk_set_flags to use clk_regmap_ops
for invoking the set_flags op.

Change-Id: I82b699e04854e5d9dcc0e82caccf33fe3dd85315
Signed-off-by: Veera Vegivada <vvegivad@codeaurora.org>
2021-02-15 11:38:02 +05:30

349 lines
8.1 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013, 2016-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/bitops.h>
#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 <linux/clk/qcom.h>
#include "clk-branch.h"
#include "clk-debug.h"
#include "clk-regmap.h"
static bool clk_branch_in_hwcg_mode(const struct clk_branch *br)
{
u32 val;
if (!br->hwcg_reg)
return false;
regmap_read(br->clkr.regmap, br->hwcg_reg, &val);
return !!(val & BIT(br->hwcg_bit));
}
static bool clk_branch_check_halt(const struct clk_branch *br, bool enabling)
{
bool invert = (br->halt_check == BRANCH_HALT_ENABLE);
u32 val;
regmap_read(br->clkr.regmap, br->halt_reg, &val);
val &= BIT(br->halt_bit);
if (invert)
val = !val;
return !!val == !enabling;
}
#define BRANCH_CLK_OFF BIT(31)
#define BRANCH_NOC_FSM_STATUS_SHIFT 28
#define BRANCH_NOC_FSM_STATUS_MASK 0x7
#define BRANCH_NOC_FSM_STATUS_ON (0x2 << BRANCH_NOC_FSM_STATUS_SHIFT)
#define BRANCH_CLK_DIS_MASK BIT(22)
static bool clk_branch2_check_halt(const struct clk_branch *br, bool enabling)
{
u32 val;
u32 mask;
mask = BRANCH_NOC_FSM_STATUS_MASK << BRANCH_NOC_FSM_STATUS_SHIFT;
mask |= BRANCH_CLK_OFF;
regmap_read(br->clkr.regmap, br->halt_reg, &val);
if (enabling) {
val &= mask;
return (val & BRANCH_CLK_OFF) == 0 ||
val == BRANCH_NOC_FSM_STATUS_ON;
} else {
return val & BRANCH_CLK_OFF;
}
}
static int clk_branch_wait(const struct clk_branch *br, bool enabling,
bool (check_halt)(const struct clk_branch *, bool))
{
bool voted = br->halt_check & BRANCH_VOTED;
/*
* Skip checking halt bit if we're explicitly ignoring the bit or the
* clock is in hardware gated mode
*/
if (br->halt_check == BRANCH_HALT_SKIP || clk_branch_in_hwcg_mode(br))
return 0;
if (br->halt_check == BRANCH_HALT_DELAY || (!enabling && voted)) {
udelay(10);
} else if (br->halt_check == BRANCH_HALT_ENABLE ||
br->halt_check == BRANCH_HALT ||
(enabling && voted)) {
int count = 200;
while (count-- > 0) {
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");
return -EBUSY;
}
return 0;
}
static int clk_branch_toggle(struct clk_hw *hw, bool en,
bool (check_halt)(const struct clk_branch *, bool))
{
struct clk_branch *br = to_clk_branch(hw);
int ret;
if (en) {
ret = clk_enable_regmap(hw);
if (ret)
return ret;
} else {
clk_disable_regmap(hw);
}
return clk_branch_wait(br, en, check_halt);
}
static int clk_branch_enable(struct clk_hw *hw)
{
return clk_branch_toggle(hw, true, clk_branch_check_halt);
}
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);
static int clk_branch2_enable(struct clk_hw *hw)
{
return clk_branch_toggle(hw, true, clk_branch2_check_halt);
}
static void clk_branch2_disable(struct clk_hw *hw)
{
clk_branch_toggle(hw, false, clk_branch2_check_halt);
}
static int clk_branch2_force_off_enable(struct clk_hw *hw)
{
struct clk_regmap *rclk = to_clk_regmap(hw);
regmap_update_bits(rclk->regmap, rclk->enable_reg,
BRANCH_CLK_DIS_MASK,
0x0);
return clk_branch2_enable(hw);
}
static void clk_branch2_force_off_disable(struct clk_hw *hw)
{
struct clk_regmap *rclk = to_clk_regmap(hw);
regmap_update_bits(rclk->regmap, rclk->enable_reg,
BRANCH_CLK_DIS_MASK,
BRANCH_CLK_DIS_MASK);
clk_branch2_disable(hw);
}
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);
clock_debug_output(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);
clock_debug_output(f, "%20s: 0x%.8x\n",
data1[i].name, val);
}
}
}
}
static int clk_branch2_set_flags(struct clk_hw *hw, unsigned long flags)
{
struct clk_branch *br = to_clk_branch(hw);
u32 cbcr_val = 0, cbcr_mask;
int ret;
switch (flags) {
case CLKFLAG_PERIPH_OFF_SET:
cbcr_val = cbcr_mask = BIT(12);
break;
case CLKFLAG_PERIPH_OFF_CLEAR:
cbcr_mask = BIT(12);
break;
case CLKFLAG_RETAIN_PERIPH:
cbcr_val = cbcr_mask = BIT(13);
break;
case CLKFLAG_NORETAIN_PERIPH:
cbcr_mask = BIT(13);
break;
case CLKFLAG_RETAIN_MEM:
cbcr_val = cbcr_mask = BIT(14);
break;
case CLKFLAG_NORETAIN_MEM:
cbcr_mask = BIT(14);
break;
default:
return -EINVAL;
}
ret = regmap_update_bits(br->clkr.regmap, br->halt_reg, cbcr_mask,
cbcr_val);
/* Make sure power is enabled/disabled before returning. */
mb();
udelay(1);
return ret;
}
static struct clk_regmap_ops clk_branch2_regmap_ops = {
.list_registers = clk_branch2_list_registers,
.set_flags = clk_branch2_set_flags,
};
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 = {
.prepare = clk_prepare_regmap,
.unprepare = clk_unprepare_regmap,
.pre_rate_change = clk_pre_change_regmap,
.post_rate_change = clk_post_change_regmap,
.enable = clk_branch2_enable,
.disable = clk_branch2_disable,
.is_enabled = clk_is_enabled_regmap,
.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);
const struct clk_ops clk_branch2_force_off_ops = {
.enable = clk_branch2_force_off_enable,
.disable = clk_branch2_force_off_disable,
.is_enabled = clk_is_enabled_regmap,
.init = clk_branch2_init,
.debug_init = clk_branch_debug_init,
};
EXPORT_SYMBOL(clk_branch2_force_off_ops);
static unsigned long clk_branch2_hw_ctl_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
return parent_rate;
}
static int clk_branch2_hw_ctl_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_hw *clkp;
clkp = clk_hw_get_parent(hw);
if (!clkp)
return -EINVAL;
req->best_parent_hw = clkp;
req->best_parent_rate = clk_round_rate(clkp->clk, req->rate);
return 0;
}
static int clk_branch2_hw_ctl_enable(struct clk_hw *hw)
{
struct clk_hw *parent = clk_hw_get_parent(hw);
/* The parent branch clock should have been prepared prior to this. */
if (!parent || (parent && !clk_hw_is_prepared(parent)))
return -EINVAL;
return clk_enable_regmap(hw);
}
static void clk_branch2_hw_ctl_disable(struct clk_hw *hw)
{
struct clk_hw *parent = clk_hw_get_parent(hw);
if (!parent)
return;
clk_disable_regmap(hw);
}
const struct clk_ops clk_branch2_hw_ctl_ops = {
.enable = clk_branch2_hw_ctl_enable,
.disable = clk_branch2_hw_ctl_disable,
.is_enabled = clk_is_enabled_regmap,
.recalc_rate = clk_branch2_hw_ctl_recalc_rate,
.determine_rate = clk_branch2_hw_ctl_determine_rate,
};
EXPORT_SYMBOL(clk_branch2_hw_ctl_ops);
const struct clk_ops clk_branch_simple_ops = {
.enable = clk_enable_regmap,
.disable = clk_disable_regmap,
.is_enabled = clk_is_enabled_regmap,
};
EXPORT_SYMBOL_GPL(clk_branch_simple_ops);