mirror of https://gitee.com/openkylin/linux.git
clk: sifive: Add clock enable and disable ops
Add new functions "sifive_prci_clock_enable(), sifive_prci_clock_disable() and sifive_clk_is_enabled()" to enable or disable the PRCI clock Signed-off-by: Pragnesh Patel <pragnesh.patel@sifive.com> Tested-by: Zong Li <zong.li@sifive.com> Link: https://lore.kernel.org/r/20201209094916.17383-6-zong.li@sifive.com Signed-off-by: Stephen Boyd <sboyd@kernel.org>
This commit is contained in:
parent
263ac39085
commit
732374a0b4
|
@ -27,16 +27,19 @@
|
|||
|
||||
static struct __prci_wrpll_data __prci_corepll_data = {
|
||||
.cfg0_offs = PRCI_COREPLLCFG0_OFFSET,
|
||||
.cfg1_offs = PRCI_COREPLLCFG1_OFFSET,
|
||||
.enable_bypass = sifive_prci_coreclksel_use_hfclk,
|
||||
.disable_bypass = sifive_prci_coreclksel_use_corepll,
|
||||
};
|
||||
|
||||
static struct __prci_wrpll_data __prci_ddrpll_data = {
|
||||
.cfg0_offs = PRCI_DDRPLLCFG0_OFFSET,
|
||||
.cfg1_offs = PRCI_DDRPLLCFG1_OFFSET,
|
||||
};
|
||||
|
||||
static struct __prci_wrpll_data __prci_gemgxlpll_data = {
|
||||
.cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET,
|
||||
.cfg1_offs = PRCI_GEMGXLPLLCFG1_OFFSET,
|
||||
};
|
||||
|
||||
/* Linux clock framework integration */
|
||||
|
@ -45,6 +48,9 @@ static const struct clk_ops sifive_fu540_prci_wrpll_clk_ops = {
|
|||
.set_rate = sifive_prci_wrpll_set_rate,
|
||||
.round_rate = sifive_prci_wrpll_round_rate,
|
||||
.recalc_rate = sifive_prci_wrpll_recalc_rate,
|
||||
.enable = sifive_prci_clock_enable,
|
||||
.disable = sifive_prci_clock_disable,
|
||||
.is_enabled = sifive_clk_is_enabled,
|
||||
};
|
||||
|
||||
static const struct clk_ops sifive_fu540_prci_wrpll_ro_clk_ops = {
|
||||
|
|
|
@ -15,32 +15,38 @@
|
|||
|
||||
static struct __prci_wrpll_data __prci_corepll_data = {
|
||||
.cfg0_offs = PRCI_COREPLLCFG0_OFFSET,
|
||||
.cfg1_offs = PRCI_COREPLLCFG1_OFFSET,
|
||||
.enable_bypass = sifive_prci_coreclksel_use_hfclk,
|
||||
.disable_bypass = sifive_prci_coreclksel_use_final_corepll,
|
||||
};
|
||||
|
||||
static struct __prci_wrpll_data __prci_ddrpll_data = {
|
||||
.cfg0_offs = PRCI_DDRPLLCFG0_OFFSET,
|
||||
.cfg1_offs = PRCI_DDRPLLCFG1_OFFSET,
|
||||
};
|
||||
|
||||
static struct __prci_wrpll_data __prci_gemgxlpll_data = {
|
||||
.cfg0_offs = PRCI_GEMGXLPLLCFG0_OFFSET,
|
||||
.cfg1_offs = PRCI_GEMGXLPLLCFG1_OFFSET,
|
||||
};
|
||||
|
||||
static struct __prci_wrpll_data __prci_dvfscorepll_data = {
|
||||
.cfg0_offs = PRCI_DVFSCOREPLLCFG0_OFFSET,
|
||||
.cfg1_offs = PRCI_DVFSCOREPLLCFG1_OFFSET,
|
||||
.enable_bypass = sifive_prci_corepllsel_use_corepll,
|
||||
.disable_bypass = sifive_prci_corepllsel_use_dvfscorepll,
|
||||
};
|
||||
|
||||
static struct __prci_wrpll_data __prci_hfpclkpll_data = {
|
||||
.cfg0_offs = PRCI_HFPCLKPLLCFG0_OFFSET,
|
||||
.cfg1_offs = PRCI_HFPCLKPLLCFG1_OFFSET,
|
||||
.enable_bypass = sifive_prci_hfpclkpllsel_use_hfclk,
|
||||
.disable_bypass = sifive_prci_hfpclkpllsel_use_hfpclkpll,
|
||||
};
|
||||
|
||||
static struct __prci_wrpll_data __prci_cltxpll_data = {
|
||||
.cfg0_offs = PRCI_CLTXPLLCFG0_OFFSET,
|
||||
.cfg1_offs = PRCI_CLTXPLLCFG1_OFFSET,
|
||||
};
|
||||
|
||||
/* Linux clock framework integration */
|
||||
|
@ -49,6 +55,9 @@ static const struct clk_ops sifive_fu740_prci_wrpll_clk_ops = {
|
|||
.set_rate = sifive_prci_wrpll_set_rate,
|
||||
.round_rate = sifive_prci_wrpll_round_rate,
|
||||
.recalc_rate = sifive_prci_wrpll_recalc_rate,
|
||||
.enable = sifive_prci_clock_enable,
|
||||
.disable = sifive_prci_clock_disable,
|
||||
.is_enabled = sifive_clk_is_enabled,
|
||||
};
|
||||
|
||||
static const struct clk_ops sifive_fu740_prci_wrpll_ro_clk_ops = {
|
||||
|
|
|
@ -113,7 +113,7 @@ static u32 __prci_wrpll_pack(const struct wrpll_cfg *c)
|
|||
}
|
||||
|
||||
/**
|
||||
* __prci_wrpll_read_cfg() - read the WRPLL configuration from the PRCI
|
||||
* __prci_wrpll_read_cfg0() - read the WRPLL configuration from the PRCI
|
||||
* @pd: PRCI context
|
||||
* @pwd: PRCI WRPLL metadata
|
||||
*
|
||||
|
@ -124,14 +124,14 @@ static u32 __prci_wrpll_pack(const struct wrpll_cfg *c)
|
|||
* Context: Any context. Caller must prevent the records pointed to by
|
||||
* @pd and @pwd from changing during execution.
|
||||
*/
|
||||
static void __prci_wrpll_read_cfg(struct __prci_data *pd,
|
||||
struct __prci_wrpll_data *pwd)
|
||||
static void __prci_wrpll_read_cfg0(struct __prci_data *pd,
|
||||
struct __prci_wrpll_data *pwd)
|
||||
{
|
||||
__prci_wrpll_unpack(&pwd->c, __prci_readl(pd, pwd->cfg0_offs));
|
||||
}
|
||||
|
||||
/**
|
||||
* __prci_wrpll_write_cfg() - write WRPLL configuration into the PRCI
|
||||
* __prci_wrpll_write_cfg0() - write WRPLL configuration into the PRCI
|
||||
* @pd: PRCI context
|
||||
* @pwd: PRCI WRPLL metadata
|
||||
* @c: WRPLL configuration record to write
|
||||
|
@ -144,15 +144,29 @@ static void __prci_wrpll_read_cfg(struct __prci_data *pd,
|
|||
* Context: Any context. Caller must prevent the records pointed to by
|
||||
* @pd and @pwd from changing during execution.
|
||||
*/
|
||||
static void __prci_wrpll_write_cfg(struct __prci_data *pd,
|
||||
struct __prci_wrpll_data *pwd,
|
||||
struct wrpll_cfg *c)
|
||||
static void __prci_wrpll_write_cfg0(struct __prci_data *pd,
|
||||
struct __prci_wrpll_data *pwd,
|
||||
struct wrpll_cfg *c)
|
||||
{
|
||||
__prci_writel(__prci_wrpll_pack(c), pwd->cfg0_offs, pd);
|
||||
|
||||
memcpy(&pwd->c, c, sizeof(*c));
|
||||
}
|
||||
|
||||
/**
|
||||
* __prci_wrpll_write_cfg1() - write Clock enable/disable configuration
|
||||
* into the PRCI
|
||||
* @pd: PRCI context
|
||||
* @pwd: PRCI WRPLL metadata
|
||||
* @enable: Clock enable or disable value
|
||||
*/
|
||||
static void __prci_wrpll_write_cfg1(struct __prci_data *pd,
|
||||
struct __prci_wrpll_data *pwd,
|
||||
u32 enable)
|
||||
{
|
||||
__prci_writel(enable, pwd->cfg1_offs, pd);
|
||||
}
|
||||
|
||||
/*
|
||||
* Linux clock framework integration
|
||||
*
|
||||
|
@ -199,16 +213,61 @@ int sifive_prci_wrpll_set_rate(struct clk_hw *hw,
|
|||
if (pwd->enable_bypass)
|
||||
pwd->enable_bypass(pd);
|
||||
|
||||
__prci_wrpll_write_cfg(pd, pwd, &pwd->c);
|
||||
__prci_wrpll_write_cfg0(pd, pwd, &pwd->c);
|
||||
|
||||
udelay(wrpll_calc_max_lock_us(&pwd->c));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sifive_clk_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
|
||||
struct __prci_wrpll_data *pwd = pc->pwd;
|
||||
struct __prci_data *pd = pc->pd;
|
||||
u32 r;
|
||||
|
||||
r = __prci_readl(pd, pwd->cfg1_offs);
|
||||
|
||||
if (r & PRCI_COREPLLCFG1_CKE_MASK)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sifive_prci_clock_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
|
||||
struct __prci_wrpll_data *pwd = pc->pwd;
|
||||
struct __prci_data *pd = pc->pd;
|
||||
|
||||
if (sifive_clk_is_enabled(hw))
|
||||
return 0;
|
||||
|
||||
__prci_wrpll_write_cfg1(pd, pwd, PRCI_COREPLLCFG1_CKE_MASK);
|
||||
|
||||
if (pwd->disable_bypass)
|
||||
pwd->disable_bypass(pd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sifive_prci_clock_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct __prci_clock *pc = clk_hw_to_prci_clock(hw);
|
||||
struct __prci_wrpll_data *pwd = pc->pwd;
|
||||
struct __prci_data *pd = pc->pd;
|
||||
u32 r;
|
||||
|
||||
if (pwd->enable_bypass)
|
||||
pwd->enable_bypass(pd);
|
||||
|
||||
r = __prci_readl(pd, pwd->cfg1_offs);
|
||||
r &= ~PRCI_COREPLLCFG1_CKE_MASK;
|
||||
|
||||
__prci_wrpll_write_cfg1(pd, pwd, r);
|
||||
}
|
||||
|
||||
/* TLCLKSEL clock integration */
|
||||
|
||||
unsigned long sifive_prci_tlclksel_recalc_rate(struct clk_hw *hw,
|
||||
|
@ -427,7 +486,7 @@ static int __prci_register_clocks(struct device *dev, struct __prci_data *pd,
|
|||
pic->pd = pd;
|
||||
|
||||
if (pic->pwd)
|
||||
__prci_wrpll_read_cfg(pd, pic->pwd);
|
||||
__prci_wrpll_read_cfg0(pd, pic->pwd);
|
||||
|
||||
r = devm_clk_hw_register(dev, &pic->hw);
|
||||
if (r) {
|
||||
|
|
|
@ -40,6 +40,11 @@
|
|||
#define PRCI_COREPLLCFG0_LOCK_SHIFT 31
|
||||
#define PRCI_COREPLLCFG0_LOCK_MASK (0x1 << PRCI_COREPLLCFG0_LOCK_SHIFT)
|
||||
|
||||
/* COREPLLCFG1 */
|
||||
#define PRCI_COREPLLCFG1_OFFSET 0x8
|
||||
#define PRCI_COREPLLCFG1_CKE_SHIFT 31
|
||||
#define PRCI_COREPLLCFG1_CKE_MASK (0x1 << PRCI_COREPLLCFG1_CKE_SHIFT)
|
||||
|
||||
/* DDRPLLCFG0 */
|
||||
#define PRCI_DDRPLLCFG0_OFFSET 0xc
|
||||
#define PRCI_DDRPLLCFG0_DIVR_SHIFT 0
|
||||
|
@ -220,6 +225,7 @@ struct __prci_data {
|
|||
* @enable_bypass: fn ptr to code to bypass the WRPLL (if applicable; else NULL)
|
||||
* @disable_bypass: fn ptr to code to not bypass the WRPLL (or NULL)
|
||||
* @cfg0_offs: WRPLL CFG0 register offset (in bytes) from the PRCI base address
|
||||
* @cfg1_offs: WRPLL CFG1 register offset (in bytes) from the PRCI base address
|
||||
*
|
||||
* @enable_bypass and @disable_bypass are used for WRPLL instances
|
||||
* that contain a separate external glitchless clock mux downstream
|
||||
|
@ -230,6 +236,7 @@ struct __prci_wrpll_data {
|
|||
void (*enable_bypass)(struct __prci_data *pd);
|
||||
void (*disable_bypass)(struct __prci_data *pd);
|
||||
u8 cfg0_offs;
|
||||
u8 cfg1_offs;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -279,6 +286,9 @@ long sifive_prci_wrpll_round_rate(struct clk_hw *hw, unsigned long rate,
|
|||
unsigned long *parent_rate);
|
||||
int sifive_prci_wrpll_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate);
|
||||
int sifive_clk_is_enabled(struct clk_hw *hw);
|
||||
int sifive_prci_clock_enable(struct clk_hw *hw);
|
||||
void sifive_prci_clock_disable(struct clk_hw *hw);
|
||||
unsigned long sifive_prci_wrpll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate);
|
||||
unsigned long sifive_prci_tlclksel_recalc_rate(struct clk_hw *hw,
|
||||
|
|
Loading…
Reference in New Issue