1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2018 MediaTek Inc. |
4 | * Weiyi Lu <weiyi.lu@mediatek.com> |
5 | * Copyright (c) 2023 Collabora, Ltd. |
6 | * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> |
7 | */ |
8 | |
9 | #include <dt-bindings/clock/mt8183-clk.h> |
10 | #include <linux/clk.h> |
11 | #include <linux/of.h> |
12 | #include <linux/platform_device.h> |
13 | |
14 | #include "clk-gate.h" |
15 | #include "clk-mtk.h" |
16 | #include "clk-pll.h" |
17 | |
18 | static const struct mtk_gate_regs apmixed_cg_regs = { |
19 | .set_ofs = 0x20, |
20 | .clr_ofs = 0x20, |
21 | .sta_ofs = 0x20, |
22 | }; |
23 | |
24 | #define GATE_APMIXED_FLAGS(_id, _name, _parent, _shift, _flags) \ |
25 | GATE_MTK_FLAGS(_id, _name, _parent, &apmixed_cg_regs, \ |
26 | _shift, &mtk_clk_gate_ops_no_setclr_inv, _flags) |
27 | |
28 | #define GATE_APMIXED(_id, _name, _parent, _shift) \ |
29 | GATE_APMIXED_FLAGS(_id, _name, _parent, _shift, 0) |
30 | |
31 | /* |
32 | * CRITICAL CLOCK: |
33 | * apmixed_appll26m is the toppest clock gate of all PLLs. |
34 | */ |
35 | static const struct mtk_gate apmixed_clks[] = { |
36 | /* AUDIO0 */ |
37 | GATE_APMIXED(CLK_APMIXED_SSUSB_26M, "apmixed_ssusb26m" , "f_f26m_ck" , 4), |
38 | GATE_APMIXED_FLAGS(CLK_APMIXED_APPLL_26M, "apmixed_appll26m" , |
39 | "f_f26m_ck" , 5, CLK_IS_CRITICAL), |
40 | GATE_APMIXED(CLK_APMIXED_MIPIC0_26M, "apmixed_mipic026m" , "f_f26m_ck" , 6), |
41 | GATE_APMIXED(CLK_APMIXED_MDPLLGP_26M, "apmixed_mdpll26m" , "f_f26m_ck" , 7), |
42 | GATE_APMIXED(CLK_APMIXED_MMSYS_26M, "apmixed_mmsys26m" , "f_f26m_ck" , 8), |
43 | GATE_APMIXED(CLK_APMIXED_UFS_26M, "apmixed_ufs26m" , "f_f26m_ck" , 9), |
44 | GATE_APMIXED(CLK_APMIXED_MIPIC1_26M, "apmixed_mipic126m" , "f_f26m_ck" , 11), |
45 | GATE_APMIXED(CLK_APMIXED_MEMPLL_26M, "apmixed_mempll26m" , "f_f26m_ck" , 13), |
46 | GATE_APMIXED(CLK_APMIXED_CLKSQ_LVPLL_26M, "apmixed_lvpll26m" , "f_f26m_ck" , 14), |
47 | GATE_APMIXED(CLK_APMIXED_MIPID0_26M, "apmixed_mipid026m" , "f_f26m_ck" , 16), |
48 | GATE_APMIXED(CLK_APMIXED_MIPID1_26M, "apmixed_mipid126m" , "f_f26m_ck" , 17), |
49 | }; |
50 | |
51 | #define MT8183_PLL_FMAX (3800UL * MHZ) |
52 | #define MT8183_PLL_FMIN (1500UL * MHZ) |
53 | |
54 | #define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \ |
55 | _rst_bar_mask, _pcwbits, _pcwibits, _pd_reg, \ |
56 | _pd_shift, _tuner_reg, _tuner_en_reg, \ |
57 | _tuner_en_bit, _pcw_reg, _pcw_shift, \ |
58 | _pcw_chg_reg, _div_table) { \ |
59 | .id = _id, \ |
60 | .name = _name, \ |
61 | .reg = _reg, \ |
62 | .pwr_reg = _pwr_reg, \ |
63 | .en_mask = _en_mask, \ |
64 | .flags = _flags, \ |
65 | .rst_bar_mask = _rst_bar_mask, \ |
66 | .fmax = MT8183_PLL_FMAX, \ |
67 | .fmin = MT8183_PLL_FMIN, \ |
68 | .pcwbits = _pcwbits, \ |
69 | .pcwibits = _pcwibits, \ |
70 | .pd_reg = _pd_reg, \ |
71 | .pd_shift = _pd_shift, \ |
72 | .tuner_reg = _tuner_reg, \ |
73 | .tuner_en_reg = _tuner_en_reg, \ |
74 | .tuner_en_bit = _tuner_en_bit, \ |
75 | .pcw_reg = _pcw_reg, \ |
76 | .pcw_shift = _pcw_shift, \ |
77 | .pcw_chg_reg = _pcw_chg_reg, \ |
78 | .div_table = _div_table, \ |
79 | } |
80 | |
81 | #define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \ |
82 | _rst_bar_mask, _pcwbits, _pcwibits, _pd_reg, \ |
83 | _pd_shift, _tuner_reg, _tuner_en_reg, \ |
84 | _tuner_en_bit, _pcw_reg, _pcw_shift, \ |
85 | _pcw_chg_reg) \ |
86 | PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, \ |
87 | _rst_bar_mask, _pcwbits, _pcwibits, _pd_reg, \ |
88 | _pd_shift, _tuner_reg, _tuner_en_reg, \ |
89 | _tuner_en_bit, _pcw_reg, _pcw_shift, \ |
90 | _pcw_chg_reg, NULL) |
91 | |
92 | static const struct mtk_pll_div_table armpll_div_table[] = { |
93 | { .div = 0, .freq = MT8183_PLL_FMAX }, |
94 | { .div = 1, .freq = 1500 * MHZ }, |
95 | { .div = 2, .freq = 750 * MHZ }, |
96 | { .div = 3, .freq = 375 * MHZ }, |
97 | { .div = 4, .freq = 187500000 }, |
98 | { /* sentinel */ } |
99 | }; |
100 | |
101 | static const struct mtk_pll_div_table mfgpll_div_table[] = { |
102 | { .div = 0, .freq = MT8183_PLL_FMAX }, |
103 | { .div = 1, .freq = 1600 * MHZ }, |
104 | { .div = 2, .freq = 800 * MHZ }, |
105 | { .div = 3, .freq = 400 * MHZ }, |
106 | { .div = 4, .freq = 200 * MHZ }, |
107 | { /* sentinel */ } |
108 | }; |
109 | |
110 | static const struct mtk_pll_data plls[] = { |
111 | PLL_B(CLK_APMIXED_ARMPLL_LL, "armpll_ll" , 0x0200, 0x020C, 0, |
112 | HAVE_RST_BAR | PLL_AO, BIT(24), 22, 8, 0x0204, 24, 0x0, 0x0, 0, |
113 | 0x0204, 0, 0, armpll_div_table), |
114 | PLL_B(CLK_APMIXED_ARMPLL_L, "armpll_l" , 0x0210, 0x021C, 0, |
115 | HAVE_RST_BAR | PLL_AO, BIT(24), 22, 8, 0x0214, 24, 0x0, 0x0, 0, |
116 | 0x0214, 0, 0, armpll_div_table), |
117 | PLL(CLK_APMIXED_CCIPLL, "ccipll" , 0x0290, 0x029C, 0, |
118 | HAVE_RST_BAR | PLL_AO, BIT(24), 22, 8, 0x0294, 24, 0x0, 0x0, 0, |
119 | 0x0294, 0, 0), |
120 | PLL(CLK_APMIXED_MAINPLL, "mainpll" , 0x0220, 0x022C, 0, |
121 | HAVE_RST_BAR, BIT(24), 22, 8, 0x0224, 24, 0x0, 0x0, 0, |
122 | 0x0224, 0, 0), |
123 | PLL(CLK_APMIXED_UNIV2PLL, "univ2pll" , 0x0230, 0x023C, 0, |
124 | HAVE_RST_BAR, BIT(24), 22, 8, 0x0234, 24, 0x0, 0x0, 0, |
125 | 0x0234, 0, 0), |
126 | PLL_B(CLK_APMIXED_MFGPLL, "mfgpll" , 0x0240, 0x024C, 0, |
127 | 0, 0, 22, 8, 0x0244, 24, 0x0, 0x0, 0, 0x0244, 0, 0, |
128 | mfgpll_div_table), |
129 | PLL(CLK_APMIXED_MSDCPLL, "msdcpll" , 0x0250, 0x025C, 0, |
130 | 0, 0, 22, 8, 0x0254, 24, 0x0, 0x0, 0, 0x0254, 0, 0), |
131 | PLL(CLK_APMIXED_TVDPLL, "tvdpll" , 0x0260, 0x026C, 0, |
132 | 0, 0, 22, 8, 0x0264, 24, 0x0, 0x0, 0, 0x0264, 0, 0), |
133 | PLL(CLK_APMIXED_MMPLL, "mmpll" , 0x0270, 0x027C, 0, |
134 | HAVE_RST_BAR, BIT(23), 22, 8, 0x0274, 24, 0x0, 0x0, 0, |
135 | 0x0274, 0, 0), |
136 | PLL(CLK_APMIXED_APLL1, "apll1" , 0x02A0, 0x02B0, 0, |
137 | 0, 0, 32, 8, 0x02A0, 1, 0x02A8, 0x0014, 0, 0x02A4, 0, 0x02A0), |
138 | PLL(CLK_APMIXED_APLL2, "apll2" , 0x02b4, 0x02c4, 0, |
139 | 0, 0, 32, 8, 0x02B4, 1, 0x02BC, 0x0014, 1, 0x02B8, 0, 0x02B4), |
140 | }; |
141 | |
142 | static int clk_mt8183_apmixed_probe(struct platform_device *pdev) |
143 | { |
144 | void __iomem *base; |
145 | struct clk_hw_onecell_data *clk_data; |
146 | struct device_node *node = pdev->dev.of_node; |
147 | struct device *dev = &pdev->dev; |
148 | int ret; |
149 | |
150 | base = devm_platform_ioremap_resource(pdev, index: 0); |
151 | if (IS_ERR(ptr: base)) |
152 | return PTR_ERR(ptr: base); |
153 | |
154 | clk_data = mtk_devm_alloc_clk_data(dev, CLK_APMIXED_NR_CLK); |
155 | if (!clk_data) |
156 | return -ENOMEM; |
157 | |
158 | ret = mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data); |
159 | if (ret) |
160 | return ret; |
161 | |
162 | ret = mtk_clk_register_gates(dev: &pdev->dev, node, clks: apmixed_clks, |
163 | ARRAY_SIZE(apmixed_clks), clk_data); |
164 | if (ret) |
165 | goto unregister_plls; |
166 | |
167 | ret = of_clk_add_hw_provider(np: node, get: of_clk_hw_onecell_get, data: clk_data); |
168 | if (ret) |
169 | goto unregister_gates; |
170 | |
171 | return 0; |
172 | |
173 | unregister_gates: |
174 | mtk_clk_unregister_gates(clks: apmixed_clks, ARRAY_SIZE(apmixed_clks), clk_data); |
175 | unregister_plls: |
176 | mtk_clk_unregister_plls(plls, ARRAY_SIZE(plls), clk_data); |
177 | |
178 | return ret; |
179 | } |
180 | |
181 | static const struct of_device_id of_match_clk_mt8183_apmixed[] = { |
182 | { .compatible = "mediatek,mt8183-apmixedsys" }, |
183 | { /* sentinel */ } |
184 | }; |
185 | MODULE_DEVICE_TABLE(of, of_match_clk_mt8183_apmixed); |
186 | |
187 | static struct platform_driver clk_mt8183_apmixed_drv = { |
188 | .probe = clk_mt8183_apmixed_probe, |
189 | .driver = { |
190 | .name = "clk-mt8183-apmixed" , |
191 | .of_match_table = of_match_clk_mt8183_apmixed, |
192 | }, |
193 | }; |
194 | builtin_platform_driver(clk_mt8183_apmixed_drv) |
195 | MODULE_LICENSE("GPL" ); |
196 | |