1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // Copyright (c) 2021 MediaTek Inc.
4 // Author: Chun-Jie Chen <chun-jie.chen@mediatek.com>
5 
6 #include "clk-mtk.h"
7 #include "clk-pll.h"
8 
9 #include <dt-bindings/clock/mt8195-clk.h>
10 #include <linux/clk-provider.h>
11 #include <linux/platform_device.h>
12 
13 #define MT8195_PLL_FMAX		(3800UL * MHZ)
14 #define MT8195_PLL_FMIN		(1500UL * MHZ)
15 #define MT8195_INTEGER_BITS	(8)
16 #define MT8195_PCW_BITS		(22)
17 #define MT8195_POSDIV_SHIFT	(24)
18 #define MT8195_PLL_EN_BIT	(0)
19 #define MT8195_PCW_SHIFT	(0)
20 
21 /*
22  * The "en_reg" and "pcw_chg_reg" fields are standard offset register compared
23  * with "reg" field, so set zero to imply it.
24  * No tuner control in apu pll, so set "tuner_XXX" as zero to imply it.
25  * No rst or post divider enable in apu pll, so set "rst_bar_mask" and "en_mask"
26  * as zero to imply it.
27  */
28 #define PLL(_id, _name, _reg, _pwr_reg, _pd_reg, _pcw_reg) {		\
29 		.id = _id,						\
30 		.name = _name,						\
31 		.reg = _reg,						\
32 		.pwr_reg = _pwr_reg,					\
33 		.en_mask = 0,						\
34 		.flags = 0,						\
35 		.rst_bar_mask = 0,					\
36 		.fmax = MT8195_PLL_FMAX,				\
37 		.fmin = MT8195_PLL_FMIN,				\
38 		.pcwbits = MT8195_PCW_BITS,				\
39 		.pcwibits = MT8195_INTEGER_BITS,			\
40 		.pd_reg = _pd_reg,					\
41 		.pd_shift = MT8195_POSDIV_SHIFT,			\
42 		.tuner_reg = 0,						\
43 		.tuner_en_reg = 0,					\
44 		.tuner_en_bit = 0,					\
45 		.pcw_reg = _pcw_reg,					\
46 		.pcw_shift = MT8195_PCW_SHIFT,				\
47 		.pcw_chg_reg = 0,					\
48 		.en_reg = 0,						\
49 		.pll_en_bit = MT8195_PLL_EN_BIT,			\
50 	}
51 
52 static const struct mtk_pll_data apusys_plls[] = {
53 	PLL(CLK_APUSYS_PLL_APUPLL, "apusys_pll_apupll", 0x008, 0x014, 0x00c, 0x00c),
54 	PLL(CLK_APUSYS_PLL_NPUPLL, "apusys_pll_npupll", 0x018, 0x024, 0x01c, 0x01c),
55 	PLL(CLK_APUSYS_PLL_APUPLL1, "apusys_pll_apupll1", 0x028, 0x034, 0x02c, 0x02c),
56 	PLL(CLK_APUSYS_PLL_APUPLL2, "apusys_pll_apupll2", 0x038, 0x044, 0x03c, 0x03c),
57 };
58 
59 static int clk_mt8195_apusys_pll_probe(struct platform_device *pdev)
60 {
61 	struct clk_hw_onecell_data *clk_data;
62 	struct device_node *node = pdev->dev.of_node;
63 	int r;
64 
65 	clk_data = mtk_alloc_clk_data(CLK_APUSYS_PLL_NR_CLK);
66 	if (!clk_data)
67 		return -ENOMEM;
68 
69 	r = mtk_clk_register_plls(node, apusys_plls, ARRAY_SIZE(apusys_plls), clk_data);
70 	if (r)
71 		goto free_apusys_pll_data;
72 
73 	r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
74 	if (r)
75 		goto unregister_plls;
76 
77 	platform_set_drvdata(pdev, clk_data);
78 
79 	return r;
80 
81 unregister_plls:
82 	mtk_clk_unregister_plls(apusys_plls, ARRAY_SIZE(apusys_plls), clk_data);
83 free_apusys_pll_data:
84 	mtk_free_clk_data(clk_data);
85 	return r;
86 }
87 
88 static void clk_mt8195_apusys_pll_remove(struct platform_device *pdev)
89 {
90 	struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
91 	struct device_node *node = pdev->dev.of_node;
92 
93 	of_clk_del_provider(node);
94 	mtk_clk_unregister_plls(apusys_plls, ARRAY_SIZE(apusys_plls), clk_data);
95 	mtk_free_clk_data(clk_data);
96 }
97 
98 static const struct of_device_id of_match_clk_mt8195_apusys_pll[] = {
99 	{ .compatible = "mediatek,mt8195-apusys_pll", },
100 	{}
101 };
102 MODULE_DEVICE_TABLE(of, of_match_clk_mt8195_apusys_pll);
103 
104 static struct platform_driver clk_mt8195_apusys_pll_drv = {
105 	.probe = clk_mt8195_apusys_pll_probe,
106 	.remove_new = clk_mt8195_apusys_pll_remove,
107 	.driver = {
108 		.name = "clk-mt8195-apusys_pll",
109 		.of_match_table = of_match_clk_mt8195_apusys_pll,
110 	},
111 };
112 module_platform_driver(clk_mt8195_apusys_pll_drv);
113 MODULE_LICENSE("GPL");
114