mirror of
https://github.com/torvalds/linux.git
synced 2024-11-01 01:31:44 +00:00
clk: sunxi-ng: Add MP_MMC clocks that support MMC timing modes switching
All of our MMC clocks are of the MP clock type. A few MMC clocks on some SoCs, such as MMC2 on the A83T, support new/old timing mode switching. >From a clock rate point of view, when the new timing mode is active. the output clock rate is halved. This patch adds a special wrapper class of clocks, MP_MMC, around the generic MP type clocks. The rate related callbacks in ccu_mp_mmc_ops for this class look at the timing mode bit and apply the /2 post-divider when needed, before passing it through to the generic class ops, ccu_mp_ops. Signed-off-by: Chen-Yu Tsai <wens@csie.org> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
parent
f6f64ed868
commit
dc8797e39f
@ -172,3 +172,83 @@ const struct clk_ops ccu_mp_ops = {
|
||||
.recalc_rate = ccu_mp_recalc_rate,
|
||||
.set_rate = ccu_mp_set_rate,
|
||||
};
|
||||
|
||||
/*
|
||||
* Support for MMC timing mode switching
|
||||
*
|
||||
* The MMC clocks on some SoCs support switching between old and
|
||||
* new timing modes. A platform specific API is provided to query
|
||||
* and set the timing mode on supported SoCs.
|
||||
*
|
||||
* In addition, a special class of ccu_mp_ops is provided, which
|
||||
* takes in to account the timing mode switch. When the new timing
|
||||
* mode is active, the clock output rate is halved. This new class
|
||||
* is a wrapper around the generic ccu_mp_ops. When clock rates
|
||||
* are passed through to ccu_mp_ops callbacks, they are doubled
|
||||
* if the new timing mode bit is set, to account for the post
|
||||
* divider. Conversely, when clock rates are passed back, they
|
||||
* are halved if the mode bit is set.
|
||||
*/
|
||||
|
||||
static unsigned long ccu_mp_mmc_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned long rate = ccu_mp_recalc_rate(hw, parent_rate);
|
||||
struct ccu_common *cm = hw_to_ccu_common(hw);
|
||||
u32 val = readl(cm->base + cm->reg);
|
||||
|
||||
if (val & CCU_MMC_NEW_TIMING_MODE)
|
||||
return rate / 2;
|
||||
return rate;
|
||||
}
|
||||
|
||||
static int ccu_mp_mmc_determine_rate(struct clk_hw *hw,
|
||||
struct clk_rate_request *req)
|
||||
{
|
||||
struct ccu_common *cm = hw_to_ccu_common(hw);
|
||||
u32 val = readl(cm->base + cm->reg);
|
||||
int ret;
|
||||
|
||||
/* adjust the requested clock rate */
|
||||
if (val & CCU_MMC_NEW_TIMING_MODE) {
|
||||
req->rate *= 2;
|
||||
req->min_rate *= 2;
|
||||
req->max_rate *= 2;
|
||||
}
|
||||
|
||||
ret = ccu_mp_determine_rate(hw, req);
|
||||
|
||||
/* re-adjust the requested clock rate back */
|
||||
if (val & CCU_MMC_NEW_TIMING_MODE) {
|
||||
req->rate /= 2;
|
||||
req->min_rate /= 2;
|
||||
req->max_rate /= 2;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ccu_mp_mmc_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct ccu_common *cm = hw_to_ccu_common(hw);
|
||||
u32 val = readl(cm->base + cm->reg);
|
||||
|
||||
if (val & CCU_MMC_NEW_TIMING_MODE)
|
||||
rate *= 2;
|
||||
|
||||
return ccu_mp_set_rate(hw, rate, parent_rate);
|
||||
}
|
||||
|
||||
const struct clk_ops ccu_mp_mmc_ops = {
|
||||
.disable = ccu_mp_disable,
|
||||
.enable = ccu_mp_enable,
|
||||
.is_enabled = ccu_mp_is_enabled,
|
||||
|
||||
.get_parent = ccu_mp_get_parent,
|
||||
.set_parent = ccu_mp_set_parent,
|
||||
|
||||
.determine_rate = ccu_mp_mmc_determine_rate,
|
||||
.recalc_rate = ccu_mp_mmc_recalc_rate,
|
||||
.set_rate = ccu_mp_mmc_set_rate,
|
||||
};
|
||||
|
@ -14,6 +14,7 @@
|
||||
#ifndef _CCU_MP_H_
|
||||
#define _CCU_MP_H_
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#include "ccu_common.h"
|
||||
@ -74,4 +75,33 @@ static inline struct ccu_mp *hw_to_ccu_mp(struct clk_hw *hw)
|
||||
|
||||
extern const struct clk_ops ccu_mp_ops;
|
||||
|
||||
/*
|
||||
* Special class of M-P clock that supports MMC timing modes
|
||||
*
|
||||
* Since the MMC clock registers all follow the same layout, we can
|
||||
* simplify the macro for this particular case. In addition, as
|
||||
* switching modes also affects the output clock rate, we need to
|
||||
* have CLK_GET_RATE_NOCACHE for all these types of clocks.
|
||||
*/
|
||||
|
||||
#define SUNXI_CCU_MP_MMC_WITH_MUX_GATE(_struct, _name, _parents, _reg, \
|
||||
_flags) \
|
||||
struct ccu_mp _struct = { \
|
||||
.enable = BIT(31), \
|
||||
.m = _SUNXI_CCU_DIV(0, 4), \
|
||||
.p = _SUNXI_CCU_DIV(16, 2), \
|
||||
.mux = _SUNXI_CCU_MUX(24, 2), \
|
||||
.common = { \
|
||||
.reg = _reg, \
|
||||
.features = CCU_FEATURE_MMC_TIMING_SWITCH, \
|
||||
.hw.init = CLK_HW_INIT_PARENTS(_name, \
|
||||
_parents, \
|
||||
&ccu_mp_mmc_ops, \
|
||||
CLK_GET_RATE_NOCACHE | \
|
||||
_flags), \
|
||||
} \
|
||||
}
|
||||
|
||||
extern const struct clk_ops ccu_mp_mmc_ops;
|
||||
|
||||
#endif /* _CCU_MP_H_ */
|
||||
|
Loading…
Reference in New Issue
Block a user