1 // SPDX-License-Identifier: GPL-2.0
3 * StarFive JH7110 Isp Clock Driver
5 * Copyright (C) 2022 Xingyu Wu <xingyu.wu@starfivetech.com>
8 #include <linux/bits.h>
10 #include <linux/clk-provider.h>
11 #include <linux/debugfs.h>
12 #include <linux/device.h>
13 #include <linux/init.h>
15 #include <linux/kernel.h>
16 #include <linux/mod_devicetable.h>
17 #include <linux/module.h>
18 #include <linux/platform_device.h>
19 #include <linux/of_device.h>
20 #include <linux/reset.h>
21 #include <linux/reset-controller.h>
22 #include <dt-bindings/clock/starfive-jh7110-isp.h>
23 #include <linux/pm_runtime.h>
25 #include "clk-starfive-jh7110.h"
28 #define JH7110_ISP_TOP_CLK_ISPCORE_2X_CLKGEN (JH7110_CLK_ISP_END + 0)
29 #define JH7110_ISP_TOP_CLK_ISP_AXI_CLKGEN (JH7110_CLK_ISP_END + 1)
30 #define JH7110_ISP_TOP_CLK_BIST_APB_CLKGEN (JH7110_CLK_ISP_END + 2)
31 #define JH7110_ISP_TOP_CLK_DVP_CLKGEN (JH7110_CLK_ISP_END + 3)
35 struct clk_bulk_data *clks;
36 struct reset_control *rsts;
39 static const struct jh7110_clk_data jh7110_clk_isp_data[] __initconst = {
41 JH7110__DIV(JH7110_DOM4_APB_FUNC, "dom4_apb_func",
42 15, JH7110_ISP_TOP_CLK_ISP_AXI_CLKGEN),
44 JH7110__DIV(JH7110_MIPI_RX0_PXL, "mipi_rx0_pxl",
45 8, JH7110_ISP_TOP_CLK_ISPCORE_2X_CLKGEN),
46 JH7110__INV(JH7110_DVP_INV, "dvp_inv", JH7110_ISP_TOP_CLK_DVP_CLKGEN),
48 JH7110__DIV(JH7110_U0_M31DPHY_CFGCLK_IN, "u0_m31dphy_cfgclk_in",
49 16, JH7110_ISP_TOP_CLK_ISPCORE_2X_CLKGEN),
50 JH7110__DIV(JH7110_U0_M31DPHY_REFCLK_IN, "u0_m31dphy_refclk_in",
51 16, JH7110_ISP_TOP_CLK_ISPCORE_2X_CLKGEN),
52 JH7110__DIV(JH7110_U0_M31DPHY_TXCLKESC_LAN0, "u0_m31dphy_txclkesc_lan0",
53 60, JH7110_ISP_TOP_CLK_ISPCORE_2X_CLKGEN),
54 JH7110_GATE(JH7110_U0_VIN_PCLK, "u0_vin_pclk",
55 CLK_IGNORE_UNUSED, JH7110_DOM4_APB),
56 JH7110__DIV(JH7110_U0_VIN_SYS_CLK, "u0_vin_sys_clk",
57 8, JH7110_ISP_TOP_CLK_ISPCORE_2X_CLKGEN),
58 JH7110_GATE(JH7110_U0_VIN_PIXEL_CLK_IF0, "u0_vin_pixel_clk_if0",
59 CLK_IGNORE_UNUSED, JH7110_MIPI_RX0_PXL),
60 JH7110_GATE(JH7110_U0_VIN_PIXEL_CLK_IF1, "u0_vin_pixel_clk_if1",
61 CLK_IGNORE_UNUSED, JH7110_MIPI_RX0_PXL),
62 JH7110_GATE(JH7110_U0_VIN_PIXEL_CLK_IF2, "u0_vin_pixel_clk_if2",
63 CLK_IGNORE_UNUSED, JH7110_MIPI_RX0_PXL),
64 JH7110_GATE(JH7110_U0_VIN_PIXEL_CLK_IF3, "u0_vin_pixel_clk_if3",
65 CLK_IGNORE_UNUSED, JH7110_MIPI_RX0_PXL),
66 JH7110__MUX(JH7110_U0_VIN_CLK_P_AXIWR, "u0_vin_clk_p_axiwr",
71 JH7110_GMUX(JH7110_U0_ISPV2_TOP_WRAPPER_CLK_C,
72 "u0_ispv2_top_wrapper_clk_c",
73 CLK_IGNORE_UNUSED, PARENT_NUMS_2,
78 static struct clk_bulk_data isp_top_clks[] = {
79 { .id = "u0_dom_isp_top_clk_dom_isp_top_clk_ispcore_2x" },
80 { .id = "u0_dom_isp_top_clk_dom_isp_top_clk_isp_axi" },
83 static int jh7110_isp_crg_get(struct device *dev, struct isp_init_crg *crg)
87 crg->rsts = devm_reset_control_array_get_shared(dev);
88 if (IS_ERR(crg->rsts)) {
89 dev_err(dev, "rst get failed\n");
90 return PTR_ERR(crg->rsts);
93 crg->clks = isp_top_clks;
94 crg->num_clks = ARRAY_SIZE(isp_top_clks);
95 ret = clk_bulk_get(dev, crg->num_clks, crg->clks);
97 dev_err(dev, "clks get failed: %d\n", ret);
104 reset_control_assert(crg->rsts);
105 reset_control_put(crg->rsts);
110 static int jh7110_isp_crg_enable(struct device *dev, struct isp_init_crg *crg, bool enable)
114 dev_dbg(dev, "starfive jh7110 isp clk&rst %sable\n", enable ? "en":"dis");
116 ret = reset_control_deassert(crg->rsts);
118 dev_err(dev, "rst deassert failed: %d\n", ret);
122 ret = clk_bulk_prepare_enable(crg->num_clks, crg->clks);
124 dev_err(dev, "clks enable failed: %d\n", ret);
128 clk_bulk_disable_unprepare(crg->num_clks, crg->clks);
136 #ifdef CONFIG_PM_SLEEP
137 static int clk_isp_system_suspend(struct device *dev)
139 return pm_runtime_force_suspend(dev);
142 static int clk_isp_system_resume(struct device *dev)
144 return pm_runtime_force_resume(dev);
149 static int clk_isp_runtime_suspend(struct device *dev)
151 struct isp_init_crg *crg = dev_get_drvdata(dev);
153 return jh7110_isp_crg_enable(dev, crg, false);
156 static int clk_isp_runtime_resume(struct device *dev)
158 struct isp_init_crg *crg = dev_get_drvdata(dev);
160 return jh7110_isp_crg_enable(dev, crg, true);
164 static const struct dev_pm_ops clk_isp_pm_ops = {
165 SET_RUNTIME_PM_OPS(clk_isp_runtime_suspend, clk_isp_runtime_resume, NULL)
166 SET_LATE_SYSTEM_SLEEP_PM_OPS(clk_isp_system_suspend, clk_isp_system_resume)
169 static struct clk_hw *jh7110_isp_clk_get(struct of_phandle_args *clkspec,
172 struct jh7110_clk_priv *priv = data;
173 unsigned int idx = clkspec->args[0];
175 if (idx < JH7110_CLK_ISP_REG_END)
176 return &priv->reg[idx].hw;
178 if (idx < JH7110_CLK_ISP_END)
179 return priv->pll[PLL_OFI(idx)];
181 return ERR_PTR(-EINVAL);
184 static int __init clk_starfive_jh7110_isp_probe(struct platform_device *pdev)
186 struct jh7110_clk_priv *priv;
187 struct isp_init_crg *crg;
191 priv = devm_kzalloc(&pdev->dev, struct_size(priv, reg,
192 JH7110_CLK_ISP_END), GFP_KERNEL);
196 spin_lock_init(&priv->rmw_lock);
197 priv->dev = &pdev->dev;
198 priv->isp_base = devm_platform_ioremap_resource(pdev, 0);
199 if (IS_ERR(priv->isp_base))
200 return PTR_ERR(priv->isp_base);
202 crg = devm_kzalloc(&pdev->dev, sizeof(*crg), GFP_KERNEL);
205 dev_set_drvdata(&pdev->dev, crg);
207 ret = jh7110_isp_crg_get(&pdev->dev, crg);
211 pm_runtime_use_autosuspend(&pdev->dev);
212 pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
213 pm_runtime_enable(&pdev->dev);
214 ret = pm_runtime_get_sync(&pdev->dev);
216 dev_err(&pdev->dev, "failed to get pm runtime: %d\n", ret);
220 priv->pll[PLL_OFI(JH7110_U3_PCLK_MUX_FUNC_PCLK)] =
221 devm_clk_hw_register_fixed_factor(
222 priv->dev, "u3_pclk_mux_func_pclk",
223 "dom4_apb_func", 0, 1, 1);
224 priv->pll[PLL_OFI(JH7110_U3_PCLK_MUX_BIST_PCLK)] =
225 devm_clk_hw_register_fixed_factor(
226 priv->dev, "u3_pclk_mux_bist_pclk",
227 "u0_dom_isp_top_clk_dom_isp_top_clk_bist_apb", 0, 1, 1);
228 priv->pll[PLL_OFI(JH7110_DOM4_APB)] =
229 devm_clk_hw_register_fixed_factor(priv->dev, "dom4_apb",
230 "u3_pclk_mux_pclk", 0, 1, 1);
232 priv->pll[PLL_OFI(JH7110_U0_VIN_PCLK_FREE)] =
233 devm_clk_hw_register_fixed_factor(
234 priv->dev, "u0_vin_pclk_free",
235 "dom4_apb", 0, 1, 1);
236 priv->pll[PLL_OFI(JH7110_U0_VIN_CLK_P_AXIRD)] =
237 devm_clk_hw_register_fixed_factor(
238 priv->dev, "u0_vin_clk_p_axird",
239 "mipi_rx0_pxl", 0, 1, 1);
240 priv->pll[PLL_OFI(JH7110_U0_VIN_ACLK)] =
241 devm_clk_hw_register_fixed_factor(
242 priv->dev, "u0_vin_ACLK",
243 "u0_dom_isp_top_clk_dom_isp_top_clk_isp_axi", 0, 1, 1);
244 priv->pll[PLL_OFI(JH7110_U0_ISPV2_TOP_WRAPPER_CLK_ISP_AXI_IN)] =
245 devm_clk_hw_register_fixed_factor(priv->dev,
246 "u0_ispv2_top_wrapper_clk_isp_axi_in",
247 "u0_dom_isp_top_clk_dom_isp_top_clk_isp_axi", 0, 1, 1);
248 priv->pll[PLL_OFI(JH7110_U0_ISPV2_TOP_WRAPPER_CLK_ISP_X2)] =
249 devm_clk_hw_register_fixed_factor(priv->dev,
250 "u0_ispv2_top_wrapper_clk_isp_x2",
251 "u0_dom_isp_top_clk_dom_isp_top_clk_ispcore_2x",
254 priv->pll[PLL_OFI(JH7110_U0_ISPV2_TOP_WRAPPER_CLK_ISP)] =
255 devm_clk_hw_register_fixed_factor(priv->dev,
256 "u0_ispv2_top_wrapper_clk_isp",
257 "u0_dom_isp_top_clk_dom_isp_top_clk_isp_axi", 0, 1, 1);
258 priv->pll[PLL_OFI(JH7110_U0_ISPV2_TOP_WRAPPER_CLK_P)] =
259 devm_clk_hw_register_fixed_factor(priv->dev,
260 "u0_ispv2_top_wrapper_clk_p",
261 "mipi_rx0_pxl", 0, 1, 1);
262 priv->pll[PLL_OFI(JH7110_U0_CRG_PCLK)] =
263 devm_clk_hw_register_fixed_factor(priv->dev,
264 "u0_crg_pclk", "dom4_apb", 0, 1, 1);
265 priv->pll[PLL_OFI(JH7110_U0_SYSCON_PCLK)] =
266 devm_clk_hw_register_fixed_factor(priv->dev,
267 "u0_syscon_pclk", "dom4_apb", 0, 1, 1);
268 priv->pll[PLL_OFI(JH7110_U0_M31DPHY_APBCFG_PCLK)] =
269 devm_clk_hw_register_fixed_factor(priv->dev,
270 "u0_m31dphy_apbcfg_pclk", "dom4_apb", 0, 1, 1);
271 priv->pll[PLL_OFI(JH7110_U0_AXI2APB_BRIDGE_CLK_DOM4_APB)] =
272 devm_clk_hw_register_fixed_factor(priv->dev,
273 "u0_axi2apb_bridge_clk_dom4_apb", "dom4_apb", 0, 1, 1);
274 priv->pll[PLL_OFI(JH7110_U0_AXI2APB_BRIDGE_ISP_AXI4SLV_CLK)] =
275 devm_clk_hw_register_fixed_factor(priv->dev,
276 "u0_axi2apb_bridge_isp_axi4slv_clk",
277 "u0_dom_isp_top_clk_dom_isp_top_clk_isp_axi", 0, 1, 1);
278 priv->pll[PLL_OFI(JH7110_U3_PCLK_MUX_PCLK)] =
279 devm_clk_hw_register_fixed_factor(priv->dev,
280 "u3_pclk_mux_pclk", "u3_pclk_mux_func_pclk", 0, 1, 1);
282 for (idx = 0; idx < JH7110_CLK_ISP_REG_END; idx++) {
283 u32 max = jh7110_clk_isp_data[idx].max;
284 struct clk_parent_data parents[2] = {};
285 struct clk_init_data init = {
286 .name = jh7110_clk_isp_data[idx].name,
287 .ops = starfive_jh7110_clk_ops(max),
288 .parent_data = parents,
289 .num_parents = ((max & JH7110_CLK_MUX_MASK) >>
290 JH7110_CLK_MUX_SHIFT) + 1,
291 .flags = jh7110_clk_isp_data[idx].flags,
293 struct jh7110_clk *clk = &priv->reg[idx];
296 "u0_dom_isp_top_clk_dom_isp_top_clk_ispcore_2x",
297 "u0_dom_isp_top_clk_dom_isp_top_clk_isp_axi",
298 "u0_dom_isp_top_clk_dom_isp_top_clk_bist_apb",
299 "u0_dom_isp_top_clk_dom_isp_top_clk_dvp"
302 for (i = 0; i < init.num_parents; i++) {
303 unsigned int pidx = jh7110_clk_isp_data[idx].parents[i];
305 if (pidx < JH7110_CLK_ISP_REG_END)
306 parents[i].hw = &priv->reg[pidx].hw;
307 else if (pidx < JH7110_CLK_ISP_END)
308 parents[i].hw = priv->pll[PLL_OFI(pidx)];
309 else if (pidx == JH7110_ISP_TOP_CLK_ISPCORE_2X_CLKGEN)
310 parents[i].fw_name = fw_name[0];
311 else if (pidx == JH7110_ISP_TOP_CLK_ISP_AXI_CLKGEN)
312 parents[i].fw_name = fw_name[1];
313 else if (pidx == JH7110_ISP_TOP_CLK_BIST_APB_CLKGEN)
314 parents[i].fw_name = fw_name[2];
315 else if (pidx == JH7110_ISP_TOP_CLK_DVP_CLKGEN)
316 parents[i].fw_name = fw_name[3];
319 clk->hw.init = &init;
321 clk->max_div = max & JH7110_CLK_DIV_MASK;
322 clk->reg_flags = JH7110_CLK_ISP_FLAG;
324 ret = devm_clk_hw_register(priv->dev, &clk->hw);
329 ret = devm_of_clk_add_hw_provider(priv->dev, jh7110_isp_clk_get, priv);
333 pm_runtime_put_sync(&pdev->dev);
335 dev_info(&pdev->dev, "starfive JH7110 clk_isp init successfully.");
342 static const struct of_device_id clk_starfive_jh7110_isp_match[] = {
343 {.compatible = "starfive,jh7110-clk-isp" },
347 static struct platform_driver clk_starfive_jh7110_isp_driver = {
348 .probe = clk_starfive_jh7110_isp_probe,
350 .name = "clk-starfive-jh7110-isp",
351 .of_match_table = clk_starfive_jh7110_isp_match,
352 .pm = &clk_isp_pm_ops,
355 module_platform_driver(clk_starfive_jh7110_isp_driver);
357 MODULE_AUTHOR("Xingyu Wu <xingyu.wu@starfivetech.com>");
358 MODULE_DESCRIPTION("StarFive JH7110 isp clock driver");
359 MODULE_LICENSE("GPL");