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/of_address.h>
10 #include <linux/slab.h>
11 #include <linux/clkdev.h>
12 #include <linux/delay.h>
15 #include "clk-pllfh.h"
16 #include "clk-fhctl.h"
18 static DEFINE_SPINLOCK(pllfh_lock
);
20 inline struct mtk_fh
*to_mtk_fh(struct clk_hw
*hw
)
22 struct mtk_clk_pll
*pll
= to_mtk_clk_pll(hw
);
24 return container_of(pll
, struct mtk_fh
, clk_pll
);
27 static int mtk_fhctl_set_rate(struct clk_hw
*hw
, unsigned long rate
,
28 unsigned long parent_rate
)
30 struct mtk_clk_pll
*pll
= to_mtk_clk_pll(hw
);
31 struct mtk_fh
*fh
= to_mtk_fh(hw
);
35 mtk_pll_calc_values(pll
, &pcw
, &postdiv
, rate
, parent_rate
);
37 return fh
->ops
->hopping(fh
, pcw
, postdiv
);
40 static const struct clk_ops mtk_pllfh_ops
= {
41 .is_prepared
= mtk_pll_is_prepared
,
42 .prepare
= mtk_pll_prepare
,
43 .unprepare
= mtk_pll_unprepare
,
44 .recalc_rate
= mtk_pll_recalc_rate
,
45 .round_rate
= mtk_pll_round_rate
,
46 .set_rate
= mtk_fhctl_set_rate
,
49 static struct mtk_pllfh_data
*get_pllfh_by_id(struct mtk_pllfh_data
*pllfhs
,
50 int num_fhs
, int pll_id
)
54 for (i
= 0; i
< num_fhs
; i
++)
55 if (pllfhs
[i
].data
.pll_id
== pll_id
)
61 void fhctl_parse_dt(const u8
*compatible_node
, struct mtk_pllfh_data
*pllfhs
,
65 struct device_node
*node
;
66 u32 num_clocks
, pll_id
, ssc_rate
;
69 node
= of_find_compatible_node(NULL
, NULL
, compatible_node
);
71 pr_warn("cannot find \"%s\"\n", compatible_node
);
75 base
= of_iomap(node
, 0);
77 pr_err("%s(): ioremap failed\n", __func__
);
81 num_clocks
= of_clk_get_parent_count(node
);
83 pr_err("%s(): failed to get clocks property\n", __func__
);
87 for (i
= 0; i
< num_clocks
; i
++) {
88 struct mtk_pllfh_data
*pllfh
;
92 of_property_read_u32_index(node
, "clocks", offset
+ 1, &pll_id
);
93 of_property_read_u32_index(node
,
94 "mediatek,hopping-ssc-percent",
97 pllfh
= get_pllfh_by_id(pllfhs
, num_fhs
, pll_id
);
101 pllfh
->state
.fh_enable
= 1;
102 pllfh
->state
.ssc_rate
= ssc_rate
;
103 pllfh
->state
.base
= base
;
113 EXPORT_SYMBOL_GPL(fhctl_parse_dt
);
115 static int pllfh_init(struct mtk_fh
*fh
, struct mtk_pllfh_data
*pllfh_data
)
117 struct fh_pll_regs
*regs
= &fh
->regs
;
118 const struct fhctl_offset
*offset
;
119 void __iomem
*base
= pllfh_data
->state
.base
;
120 void __iomem
*fhx_base
= base
+ pllfh_data
->data
.fhx_offset
;
122 offset
= fhctl_get_offset_table(pllfh_data
->data
.fh_ver
);
124 return PTR_ERR(offset
);
126 regs
->reg_hp_en
= base
+ offset
->offset_hp_en
;
127 regs
->reg_clk_con
= base
+ offset
->offset_clk_con
;
128 regs
->reg_rst_con
= base
+ offset
->offset_rst_con
;
129 regs
->reg_slope0
= base
+ offset
->offset_slope0
;
130 regs
->reg_slope1
= base
+ offset
->offset_slope1
;
132 regs
->reg_cfg
= fhx_base
+ offset
->offset_cfg
;
133 regs
->reg_updnlmt
= fhx_base
+ offset
->offset_updnlmt
;
134 regs
->reg_dds
= fhx_base
+ offset
->offset_dds
;
135 regs
->reg_dvfs
= fhx_base
+ offset
->offset_dvfs
;
136 regs
->reg_mon
= fhx_base
+ offset
->offset_mon
;
138 fh
->pllfh_data
= pllfh_data
;
139 fh
->lock
= &pllfh_lock
;
141 fh
->ops
= fhctl_get_ops();
146 static bool fhctl_is_supported_and_enabled(const struct mtk_pllfh_data
*pllfh
)
148 return pllfh
&& (pllfh
->state
.fh_enable
== 1);
151 static struct clk_hw
*
152 mtk_clk_register_pllfh(const struct mtk_pll_data
*pll_data
,
153 struct mtk_pllfh_data
*pllfh_data
, void __iomem
*base
)
159 fh
= kzalloc(sizeof(*fh
), GFP_KERNEL
);
161 return ERR_PTR(-ENOMEM
);
163 ret
= pllfh_init(fh
, pllfh_data
);
169 hw
= mtk_clk_register_pll_ops(&fh
->clk_pll
, pll_data
, base
,
184 static void mtk_clk_unregister_pllfh(struct clk_hw
*hw
)
193 clk_hw_unregister(hw
);
197 int mtk_clk_register_pllfhs(struct device_node
*node
,
198 const struct mtk_pll_data
*plls
, int num_plls
,
199 struct mtk_pllfh_data
*pllfhs
, int num_fhs
,
200 struct clk_hw_onecell_data
*clk_data
)
206 base
= of_iomap(node
, 0);
208 pr_err("%s(): ioremap failed\n", __func__
);
212 for (i
= 0; i
< num_plls
; i
++) {
213 const struct mtk_pll_data
*pll
= &plls
[i
];
214 struct mtk_pllfh_data
*pllfh
;
217 pllfh
= get_pllfh_by_id(pllfhs
, num_fhs
, pll
->id
);
218 use_fhctl
= fhctl_is_supported_and_enabled(pllfh
);
221 hw
= mtk_clk_register_pllfh(pll
, pllfh
, base
);
223 hw
= mtk_clk_register_pll(pll
, base
);
226 pr_err("Failed to register %s clk %s: %ld\n",
227 use_fhctl
? "fhpll" : "pll", pll
->name
,
232 clk_data
->hws
[pll
->id
] = hw
;
239 const struct mtk_pll_data
*pll
= &plls
[i
];
240 struct mtk_pllfh_data
*pllfh
;
243 pllfh
= get_pllfh_by_id(pllfhs
, num_fhs
, pll
->id
);
244 use_fhctl
= fhctl_is_supported_and_enabled(pllfh
);
247 mtk_clk_unregister_pllfh(clk_data
->hws
[pll
->id
]);
249 mtk_clk_unregister_pll(clk_data
->hws
[pll
->id
]);
251 clk_data
->hws
[pll
->id
] = ERR_PTR(-ENOENT
);
258 EXPORT_SYMBOL_GPL(mtk_clk_register_pllfhs
);
260 void mtk_clk_unregister_pllfhs(const struct mtk_pll_data
*plls
, int num_plls
,
261 struct mtk_pllfh_data
*pllfhs
, int num_fhs
,
262 struct clk_hw_onecell_data
*clk_data
)
264 void __iomem
*base
= NULL
, *fhctl_base
= NULL
;
270 for (i
= num_plls
; i
> 0; i
--) {
271 const struct mtk_pll_data
*pll
= &plls
[i
- 1];
272 struct mtk_pllfh_data
*pllfh
;
275 if (IS_ERR_OR_NULL(clk_data
->hws
[pll
->id
]))
278 pllfh
= get_pllfh_by_id(pllfhs
, num_fhs
, pll
->id
);
279 use_fhctl
= fhctl_is_supported_and_enabled(pllfh
);
282 fhctl_base
= pllfh
->state
.base
;
283 mtk_clk_unregister_pllfh(clk_data
->hws
[pll
->id
]);
285 base
= mtk_clk_pll_get_base(clk_data
->hws
[pll
->id
],
287 mtk_clk_unregister_pll(clk_data
->hws
[pll
->id
]);
290 clk_data
->hws
[pll
->id
] = ERR_PTR(-ENOENT
);
298 EXPORT_SYMBOL_GPL(mtk_clk_unregister_pllfhs
);