mirror of
https://github.com/torvalds/linux.git
synced 2026-07-31 11:37:06 +02:00
clk: qcom: regmap: Add regmap support for voltage voting
Add support for prepare, unprepare, pre and post rate change regmap clk_ops callback functions for regmap clocks to handle regulator requirements. Change-Id: If4657adcc047e1ca005d3d4f7ab88146dba85ee0 Signed-off-by: David Dai <daidavid1@codeaurora.org>
This commit is contained in:
parent
70d08917fd
commit
dc2598a2fe
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2014, 2019 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
|
|
@ -84,6 +84,152 @@ void clk_disable_regmap(struct clk_hw *hw)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(clk_disable_regmap);
|
||||
|
||||
/**
|
||||
* clk_pre_change_regmap() - standard pre_change call back for regmap clks
|
||||
*
|
||||
* @hw: clk to operate on
|
||||
* @cur_rate: current rate of the clk
|
||||
* @new_rate: new rate about to be set for the clk
|
||||
*
|
||||
* Finds the new vdd level corresponding to the new rate and swap out the
|
||||
* existing vdd vote if the new_vdd_level is greater than the vdd_level and
|
||||
* the clock is prepared.
|
||||
*
|
||||
* Returns 0 on success, -EERROR otherwise.
|
||||
*/
|
||||
int clk_pre_change_regmap(struct clk_hw *hw, unsigned long cur_rate,
|
||||
unsigned long new_rate)
|
||||
{
|
||||
struct clk_regmap *rclk = to_clk_regmap(hw);
|
||||
int vdd_level = rclk->vdd_data.vdd_level;
|
||||
int new_vdd_level;
|
||||
int ret = 0;
|
||||
|
||||
if (!rclk->vdd_data.vdd_class)
|
||||
return 0;
|
||||
|
||||
if (!clk_hw_is_prepared(hw))
|
||||
return 0;
|
||||
|
||||
new_vdd_level = clk_find_vdd_level(hw, &rclk->vdd_data, new_rate);
|
||||
if (new_vdd_level < 0)
|
||||
return new_vdd_level;
|
||||
|
||||
if (new_vdd_level <= vdd_level)
|
||||
return 0;
|
||||
|
||||
ret = clk_vote_vdd_level(&rclk->vdd_data, new_vdd_level);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_unvote_vdd_level(&rclk->vdd_data, vdd_level);
|
||||
rclk->vdd_data.vdd_level = new_vdd_level;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_pre_change_regmap);
|
||||
|
||||
/**
|
||||
* clk_post_change_regmap() - standard post_change call back for regmap clks
|
||||
*
|
||||
* @hw: clk to operate on
|
||||
* @old_rate: previous rate of the clk
|
||||
* @cur_rate: current rate of the recently changed clk
|
||||
*
|
||||
* Finds the vdd level corresponding to the cur rate and swap out the
|
||||
* existing vdd vote if the cur_vdd_level is less than the vdd_level and the
|
||||
* clock is prepared.
|
||||
*
|
||||
* Returns 0 on success, -EERROR otherwise.
|
||||
*/
|
||||
int clk_post_change_regmap(struct clk_hw *hw, unsigned long old_rate,
|
||||
unsigned long cur_rate)
|
||||
{
|
||||
struct clk_regmap *rclk = to_clk_regmap(hw);
|
||||
int vdd_level = rclk->vdd_data.vdd_level;
|
||||
int cur_vdd_level;
|
||||
int ret = 0;
|
||||
|
||||
if (!rclk->vdd_data.vdd_class)
|
||||
return 0;
|
||||
|
||||
if (!clk_hw_is_prepared(hw))
|
||||
return 0;
|
||||
|
||||
cur_vdd_level = clk_find_vdd_level(hw, &rclk->vdd_data, cur_rate);
|
||||
if (cur_vdd_level < 0)
|
||||
return cur_vdd_level;
|
||||
|
||||
if (cur_vdd_level >= vdd_level)
|
||||
return 0;
|
||||
|
||||
ret = clk_vote_vdd_level(&rclk->vdd_data, cur_vdd_level);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
clk_unvote_vdd_level(&rclk->vdd_data, vdd_level);
|
||||
rclk->vdd_data.vdd_level = cur_vdd_level;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(clk_post_change_regmap);
|
||||
|
||||
/**
|
||||
* clk_prepare_regmap() - standard prepare call back for regmap clks
|
||||
*
|
||||
* @hw: clk to operate on
|
||||
*
|
||||
* Prepare the clock by updating the vdd_level to level required by
|
||||
* the current rate of the clock if it hasn't been initialized before.
|
||||
* Vdd_level and level required by current clock rate mismatches can
|
||||
* occur due to error cases and upon initial clock registration
|
||||
* if the clock becomes an orphan and is later reparented.
|
||||
*
|
||||
* Returns 0 on success, -EERROR otherwise.
|
||||
*/
|
||||
int clk_prepare_regmap(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap *rclk = to_clk_regmap(hw);
|
||||
int rate = clk_hw_get_rate(hw);
|
||||
int vdd_level;
|
||||
|
||||
if (!rclk->vdd_data.vdd_class)
|
||||
return 0;
|
||||
|
||||
vdd_level = clk_find_vdd_level(hw, &rclk->vdd_data, rate);
|
||||
if (vdd_level < 0)
|
||||
return vdd_level;
|
||||
|
||||
if (rclk->vdd_data.vdd_level == 0)
|
||||
rclk->vdd_data.vdd_level = vdd_level;
|
||||
|
||||
WARN(vdd_level > rclk->vdd_data.vdd_level,
|
||||
"%s level:%d > vdd_level:%d\n", clk_hw_get_name(hw),
|
||||
vdd_level, rclk->vdd_data.vdd_level);
|
||||
|
||||
return clk_vote_vdd_level(&rclk->vdd_data, rclk->vdd_data.vdd_level);
|
||||
}
|
||||
EXPORT_SYMBOL(clk_prepare_regmap);
|
||||
|
||||
/**
|
||||
* clk_prepare_regmap() - standard prepare call back for regmap clks
|
||||
*
|
||||
* @hw: clk to operate on
|
||||
*
|
||||
* Unprepare the clock by removing the outstanding vdd_level vote.
|
||||
*
|
||||
*/
|
||||
void clk_unprepare_regmap(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_regmap *rclk = to_clk_regmap(hw);
|
||||
|
||||
if (!rclk->vdd_data.vdd_class)
|
||||
return;
|
||||
|
||||
clk_unvote_vdd_level(&rclk->vdd_data, rclk->vdd_data.vdd_level);
|
||||
}
|
||||
EXPORT_SYMBOL(clk_unprepare_regmap);
|
||||
|
||||
/**
|
||||
* devm_clk_register_regmap - register a clk_regmap clock
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* Copyright (c) 2014, The Linux Foundation. All rights reserved. */
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/* Copyright (c) 2014, 2019, The Linux Foundation. All rights reserved. */
|
||||
|
||||
#ifndef __QCOM_CLK_REGMAP_H__
|
||||
#define __QCOM_CLK_REGMAP_H__
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include "vdd-class.h"
|
||||
|
||||
struct regmap;
|
||||
|
||||
|
|
@ -16,6 +17,7 @@ struct regmap;
|
|||
* @enable_mask: mask when using regmap enable/disable ops
|
||||
* @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
|
||||
*/
|
||||
struct clk_regmap {
|
||||
struct clk_hw hw;
|
||||
|
|
@ -23,6 +25,7 @@ struct clk_regmap {
|
|||
unsigned int enable_reg;
|
||||
unsigned int enable_mask;
|
||||
bool enable_is_inverted;
|
||||
struct clk_vdd_class_data vdd_data;
|
||||
};
|
||||
|
||||
static inline struct clk_regmap *to_clk_regmap(struct clk_hw *hw)
|
||||
|
|
@ -33,6 +36,12 @@ static inline struct clk_regmap *to_clk_regmap(struct clk_hw *hw)
|
|||
int clk_is_enabled_regmap(struct clk_hw *hw);
|
||||
int clk_enable_regmap(struct clk_hw *hw);
|
||||
void clk_disable_regmap(struct clk_hw *hw);
|
||||
int clk_prepare_regmap(struct clk_hw *hw);
|
||||
void clk_unprepare_regmap(struct clk_hw *hw);
|
||||
int clk_pre_change_regmap(struct clk_hw *hw, unsigned long cur_rate,
|
||||
unsigned long new_rate);
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user