Added clock enable and disable functions in prci ops Signed-off-by: Pragnesh Patel <pragnesh.pa...@sifive.com> Reviewed-by: Bin Meng <bmeng...@gmail.com> Tested-by: Bin Meng <bmeng...@gmail.com> --- drivers/clk/sifive/fu540-prci.c | 108 ++++++++++++++++++++++++++++---- 1 file changed, 96 insertions(+), 12 deletions(-)
diff --git a/drivers/clk/sifive/fu540-prci.c b/drivers/clk/sifive/fu540-prci.c index 8847178001..bf06c3a3bb 100644 --- a/drivers/clk/sifive/fu540-prci.c +++ b/drivers/clk/sifive/fu540-prci.c @@ -68,6 +68,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 @@ -87,7 +92,7 @@ /* DDRPLLCFG1 */ #define PRCI_DDRPLLCFG1_OFFSET 0x10 -#define PRCI_DDRPLLCFG1_CKE_SHIFT 24 +#define PRCI_DDRPLLCFG1_CKE_SHIFT 31 #define PRCI_DDRPLLCFG1_CKE_MASK (0x1 << PRCI_DDRPLLCFG1_CKE_SHIFT) /* GEMGXLPLLCFG0 */ @@ -114,7 +119,7 @@ /* GEMGXLPLLCFG1 */ #define PRCI_GEMGXLPLLCFG1_OFFSET 0x20 -#define PRCI_GEMGXLPLLCFG1_CKE_SHIFT 24 +#define PRCI_GEMGXLPLLCFG1_CKE_SHIFT 31 #define PRCI_GEMGXLPLLCFG1_CKE_MASK (0x1 << PRCI_GEMGXLPLLCFG1_CKE_SHIFT) /* CORECLKSEL */ @@ -142,7 +147,7 @@ (0x1 << PRCI_DEVICESRESETREG_GEMGXL_RST_N_SHIFT) /* CLKMUXSTATUSREG */ -#define PRCI_CLKMUXSTATUSREG_OFFSET 0x2c +#define PRCI_CLKMUXSTATUSREG_OFFSET 0x2c #define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT 1 #define PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_MASK \ (0x1 << PRCI_CLKMUXSTATUSREG_TLCLKSEL_STATUS_SHIFT) @@ -170,6 +175,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 @@ -180,6 +186,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; }; struct __prci_clock; @@ -194,6 +201,7 @@ struct __prci_clock_ops { unsigned long *parent_rate); unsigned long (*recalc_rate)(struct __prci_clock *pc, unsigned long parent_rate); + int (*enable_clk)(struct __prci_clock *pc, bool enable); }; /** @@ -316,7 +324,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 * @@ -327,14 +335,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 @@ -347,15 +355,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); +} + /* Core clock mux control */ /** @@ -437,7 +459,7 @@ static int sifive_fu540_prci_wrpll_set_rate(struct __prci_clock *pc, 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)); @@ -447,14 +469,35 @@ static int sifive_fu540_prci_wrpll_set_rate(struct __prci_clock *pc, return 0; } +static int sifive_fu540_prci_clock_enable(struct __prci_clock *pc, bool enable) +{ + struct __prci_wrpll_data *pwd = pc->pwd; + struct __prci_data *pd = pc->pd; + + if (enable) { + __prci_wrpll_write_cfg1(pd, pwd, PRCI_COREPLLCFG1_CKE_MASK); + } else { + u32 r; + + r = __prci_readl(pd, pwd->cfg1_offs); + r &= ~PRCI_COREPLLCFG1_CKE_MASK; + + __prci_wrpll_write_cfg1(pd, pwd, r); + } + + return 0; +} + static const struct __prci_clock_ops sifive_fu540_prci_wrpll_clk_ops = { .set_rate = sifive_fu540_prci_wrpll_set_rate, .round_rate = sifive_fu540_prci_wrpll_round_rate, .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate, + .enable_clk = sifive_fu540_prci_clock_enable, }; static const struct __prci_clock_ops sifive_fu540_prci_wrpll_ro_clk_ops = { .recalc_rate = sifive_fu540_prci_wrpll_recalc_rate, + .enable_clk = sifive_fu540_prci_clock_enable, }; /* TLCLKSEL clock integration */ @@ -484,16 +527,19 @@ static const struct __prci_clock_ops sifive_fu540_prci_tlclksel_clk_ops = { static struct __prci_wrpll_data __prci_corepll_data = { .cfg0_offs = PRCI_COREPLLCFG0_OFFSET, + .cfg1_offs = PRCI_COREPLLCFG1_OFFSET, .enable_bypass = __prci_coreclksel_use_hfclk, .disable_bypass = __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, }; /* @@ -580,6 +626,42 @@ static ulong sifive_fu540_prci_set_rate(struct clk *clk, ulong rate) return rate; } +static int sifive_fu540_prci_enable(struct clk *clk) +{ + struct __prci_clock *pc; + int ret = 0; + + if (ARRAY_SIZE(__prci_init_clocks) <= clk->id) + return -ENXIO; + + pc = &__prci_init_clocks[clk->id]; + if (!pc->pd) + return -ENXIO; + + if (pc->ops->enable_clk) + ret = pc->ops->enable_clk(pc, 1); + + return ret; +} + +static int sifive_fu540_prci_disable(struct clk *clk) +{ + struct __prci_clock *pc; + int ret = 0; + + if (ARRAY_SIZE(__prci_init_clocks) <= clk->id) + return -ENXIO; + + pc = &__prci_init_clocks[clk->id]; + if (!pc->pd) + return -ENXIO; + + if (pc->ops->enable_clk) + ret = pc->ops->enable_clk(pc, 0); + + return ret; +} + static int sifive_fu540_prci_probe(struct udevice *dev) { int i, err; @@ -602,7 +684,7 @@ static int sifive_fu540_prci_probe(struct udevice *dev) pc = &__prci_init_clocks[i]; pc->pd = pd; if (pc->pwd) - __prci_wrpll_read_cfg(pd, pc->pwd); + __prci_wrpll_read_cfg0(pd, pc->pwd); } return 0; @@ -611,6 +693,8 @@ static int sifive_fu540_prci_probe(struct udevice *dev) static struct clk_ops sifive_fu540_prci_ops = { .set_rate = sifive_fu540_prci_set_rate, .get_rate = sifive_fu540_prci_get_rate, + .enable = sifive_fu540_prci_enable, + .disable = sifive_fu540_prci_disable, }; static const struct udevice_id sifive_fu540_prci_ids[] = { -- 2.17.1