1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2022 MediaTek Inc.
4 * Author: Edward-JW Yang <edward-jw.yang@mediatek.com>
8 #include <linux/iopoll.h>
11 #include "clk-pllfh.h"
12 #include "clk-fhctl.h"
14 #define PERCENT_TO_DDSLMT(dds, percent_m10) \
15 ((((dds) * (percent_m10)) >> 5) / 100)
17 static const struct fhctl_offset fhctl_offset_v1
= {
19 .offset_clk_con
= 0x4,
20 .offset_rst_con
= 0x8,
22 .offset_slope1
= 0x10,
24 .offset_updnlmt
= 0x4,
30 static const struct fhctl_offset fhctl_offset_v2
= {
32 .offset_clk_con
= 0x8,
33 .offset_rst_con
= 0xc,
34 .offset_slope0
= 0x10,
35 .offset_slope1
= 0x14,
37 .offset_updnlmt
= 0x4,
43 const struct fhctl_offset
*fhctl_get_offset_table(enum fhctl_variant v
)
47 return &fhctl_offset_v1
;
49 return &fhctl_offset_v2
;
51 return ERR_PTR(-EINVAL
);
55 static void dump_hw(struct mtk_clk_pll
*pll
, struct fh_pll_regs
*regs
,
56 const struct fh_pll_data
*data
)
58 pr_info("hp_en<%x>,clk_con<%x>,slope0<%x>,slope1<%x>\n",
59 readl(regs
->reg_hp_en
), readl(regs
->reg_clk_con
),
60 readl(regs
->reg_slope0
), readl(regs
->reg_slope1
));
61 pr_info("cfg<%x>,lmt<%x>,dds<%x>,dvfs<%x>,mon<%x>\n",
62 readl(regs
->reg_cfg
), readl(regs
->reg_updnlmt
),
63 readl(regs
->reg_dds
), readl(regs
->reg_dvfs
),
64 readl(regs
->reg_mon
));
65 pr_info("pcw<%x>\n", readl(pll
->pcw_addr
));
68 static int fhctl_set_ssc_regs(struct mtk_clk_pll
*pll
, struct fh_pll_regs
*regs
,
69 const struct fh_pll_data
*data
, u32 rate
)
73 writel((readl(regs
->reg_cfg
) & ~(data
->frddsx_en
)), regs
->reg_cfg
);
74 writel((readl(regs
->reg_cfg
) & ~(data
->sfstrx_en
)), regs
->reg_cfg
);
75 writel((readl(regs
->reg_cfg
) & ~(data
->fhctlx_en
)), regs
->reg_cfg
);
78 /* Set the relative parameter registers (dt/df/upbnd/downbnd) */
79 r
= readl(regs
->reg_cfg
);
80 r
&= ~(data
->msk_frddsx_dys
);
81 r
|= (data
->df_val
<< (ffs(data
->msk_frddsx_dys
) - 1));
82 writel(r
, regs
->reg_cfg
);
84 r
= readl(regs
->reg_cfg
);
85 r
&= ~(data
->msk_frddsx_dts
);
86 r
|= (data
->dt_val
<< (ffs(data
->msk_frddsx_dts
) - 1));
87 writel(r
, regs
->reg_cfg
);
89 writel((readl(pll
->pcw_addr
) & data
->dds_mask
) | data
->tgl_org
,
92 /* Calculate UPDNLMT */
93 updnlmt_val
= PERCENT_TO_DDSLMT((readl(regs
->reg_dds
) &
94 data
->dds_mask
), rate
) <<
97 writel(updnlmt_val
, regs
->reg_updnlmt
);
98 writel(readl(regs
->reg_hp_en
) | BIT(data
->fh_id
),
101 writel(readl(regs
->reg_cfg
) | data
->frddsx_en
, regs
->reg_cfg
);
102 /* Enable Hopping control */
103 writel(readl(regs
->reg_cfg
) | data
->fhctlx_en
, regs
->reg_cfg
);
106 /* Switch to APMIXEDSYS control */
107 writel(readl(regs
->reg_hp_en
) & ~BIT(data
->fh_id
),
109 /* Wait for DDS to be stable */
116 static int hopping_hw_flow(struct mtk_clk_pll
*pll
, struct fh_pll_regs
*regs
,
117 const struct fh_pll_data
*data
,
118 struct fh_pll_state
*state
, unsigned int new_dds
)
120 u32 dds_mask
= data
->dds_mask
;
126 fhctl_set_ssc_regs(pll
, regs
, data
, 0);
128 writel((readl(pll
->pcw_addr
) & dds_mask
) | data
->tgl_org
,
131 writel(readl(regs
->reg_cfg
) | data
->sfstrx_en
, regs
->reg_cfg
);
132 writel(readl(regs
->reg_cfg
) | data
->fhctlx_en
, regs
->reg_cfg
);
133 writel(data
->slope0_value
, regs
->reg_slope0
);
134 writel(data
->slope1_value
, regs
->reg_slope1
);
136 writel(readl(regs
->reg_hp_en
) | BIT(data
->fh_id
), regs
->reg_hp_en
);
137 writel((new_dds
) | (data
->dvfs_tri
), regs
->reg_dvfs
);
139 /* Wait 1000 us until DDS stable */
140 ret
= readl_poll_timeout_atomic(regs
->reg_mon
, mon_dds
,
141 (mon_dds
& dds_mask
) == new_dds
,
144 pr_warn("%s: FHCTL hopping timeout\n", pll
->data
->name
);
145 dump_hw(pll
, regs
, data
);
148 con_pcw_tmp
= readl(pll
->pcw_addr
) & (~dds_mask
);
149 con_pcw_tmp
= (con_pcw_tmp
| (readl(regs
->reg_mon
) & dds_mask
) |
152 writel(con_pcw_tmp
, pll
->pcw_addr
);
153 writel(readl(regs
->reg_hp_en
) & ~BIT(data
->fh_id
), regs
->reg_hp_en
);
156 fhctl_set_ssc_regs(pll
, regs
, data
, state
->ssc_rate
);
161 static unsigned int __get_postdiv(struct mtk_clk_pll
*pll
)
165 regval
= readl(pll
->pd_addr
) >> pll
->data
->pd_shift
;
166 regval
&= POSTDIV_MASK
;
171 static void __set_postdiv(struct mtk_clk_pll
*pll
, unsigned int postdiv
)
175 regval
= readl(pll
->pd_addr
);
176 regval
&= ~(POSTDIV_MASK
<< pll
->data
->pd_shift
);
177 regval
|= (ffs(postdiv
) - 1) << pll
->data
->pd_shift
;
178 writel(regval
, pll
->pd_addr
);
181 static int fhctl_hopping(struct mtk_fh
*fh
, unsigned int new_dds
,
182 unsigned int postdiv
)
184 const struct fh_pll_data
*data
= &fh
->pllfh_data
->data
;
185 struct fh_pll_state
*state
= &fh
->pllfh_data
->state
;
186 struct fh_pll_regs
*regs
= &fh
->regs
;
187 struct mtk_clk_pll
*pll
= &fh
->clk_pll
;
188 spinlock_t
*lock
= fh
->lock
;
189 unsigned int pll_postdiv
;
190 unsigned long flags
= 0;
194 pll_postdiv
= __get_postdiv(pll
);
196 if (postdiv
> pll_postdiv
)
197 __set_postdiv(pll
, postdiv
);
200 spin_lock_irqsave(lock
, flags
);
202 ret
= hopping_hw_flow(pll
, regs
, data
, state
, new_dds
);
204 spin_unlock_irqrestore(lock
, flags
);
206 if (postdiv
&& postdiv
< pll_postdiv
)
207 __set_postdiv(pll
, postdiv
);
212 static int fhctl_ssc_enable(struct mtk_fh
*fh
, u32 rate
)
214 const struct fh_pll_data
*data
= &fh
->pllfh_data
->data
;
215 struct fh_pll_state
*state
= &fh
->pllfh_data
->state
;
216 struct fh_pll_regs
*regs
= &fh
->regs
;
217 struct mtk_clk_pll
*pll
= &fh
->clk_pll
;
218 spinlock_t
*lock
= fh
->lock
;
219 unsigned long flags
= 0;
221 spin_lock_irqsave(lock
, flags
);
223 fhctl_set_ssc_regs(pll
, regs
, data
, rate
);
224 state
->ssc_rate
= rate
;
226 spin_unlock_irqrestore(lock
, flags
);
231 static const struct fh_operation fhctl_ops
= {
232 .hopping
= fhctl_hopping
,
233 .ssc_enable
= fhctl_ssc_enable
,
236 const struct fh_operation
*fhctl_get_ops(void)
241 void fhctl_hw_init(struct mtk_fh
*fh
)
243 const struct fh_pll_data data
= fh
->pllfh_data
->data
;
244 struct fh_pll_state state
= fh
->pllfh_data
->state
;
245 struct fh_pll_regs regs
= fh
->regs
;
248 /* initial hw register */
249 val
= readl(regs
.reg_clk_con
) | BIT(data
.fh_id
);
250 writel(val
, regs
.reg_clk_con
);
252 val
= readl(regs
.reg_rst_con
) & ~BIT(data
.fh_id
);
253 writel(val
, regs
.reg_rst_con
);
254 val
= readl(regs
.reg_rst_con
) | BIT(data
.fh_id
);
255 writel(val
, regs
.reg_rst_con
);
257 writel(0x0, regs
.reg_cfg
);
258 writel(0x0, regs
.reg_updnlmt
);
259 writel(0x0, regs
.reg_dds
);
261 /* enable ssc if needed */
263 fh
->ops
->ssc_enable(fh
, state
.ssc_rate
);