1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2014 MediaTek Inc. |
4 | * Copyright (c) 2022 Collabora Ltd. |
5 | * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> |
6 | */ |
7 | |
8 | #include <dt-bindings/clock/mt8173-clk.h> |
9 | #include <linux/of_address.h> |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include "clk-fhctl.h" |
13 | #include "clk-mtk.h" |
14 | #include "clk-pll.h" |
15 | #include "clk-pllfh.h" |
16 | |
17 | #define REGOFF_REF2USB 0x8 |
18 | #define REGOFF_HDMI_REF 0x40 |
19 | |
20 | #define MT8173_PLL_FMAX (3000UL * MHZ) |
21 | |
22 | #define CON0_MT8173_RST_BAR BIT(24) |
23 | |
24 | #define PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ |
25 | _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \ |
26 | _pcw_shift, _div_table) { \ |
27 | .id = _id, \ |
28 | .name = _name, \ |
29 | .reg = _reg, \ |
30 | .pwr_reg = _pwr_reg, \ |
31 | .en_mask = _en_mask, \ |
32 | .flags = _flags, \ |
33 | .rst_bar_mask = CON0_MT8173_RST_BAR, \ |
34 | .fmax = MT8173_PLL_FMAX, \ |
35 | .pcwbits = _pcwbits, \ |
36 | .pd_reg = _pd_reg, \ |
37 | .pd_shift = _pd_shift, \ |
38 | .tuner_reg = _tuner_reg, \ |
39 | .pcw_reg = _pcw_reg, \ |
40 | .pcw_shift = _pcw_shift, \ |
41 | .div_table = _div_table, \ |
42 | } |
43 | |
44 | #define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ |
45 | _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, \ |
46 | _pcw_shift) \ |
47 | PLL_B(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ |
48 | _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \ |
49 | NULL) |
50 | |
51 | static const struct mtk_pll_div_table mmpll_div_table[] = { |
52 | { .div = 0, .freq = MT8173_PLL_FMAX }, |
53 | { .div = 1, .freq = 1000000000 }, |
54 | { .div = 2, .freq = 702000000 }, |
55 | { .div = 3, .freq = 253500000 }, |
56 | { .div = 4, .freq = 126750000 }, |
57 | { } /* sentinel */ |
58 | }; |
59 | |
60 | static const struct mtk_pll_data plls[] = { |
61 | PLL(CLK_APMIXED_ARMCA15PLL, "armca15pll" , 0x200, 0x20c, 0, PLL_AO, |
62 | 21, 0x204, 24, 0x0, 0x204, 0), |
63 | PLL(CLK_APMIXED_ARMCA7PLL, "armca7pll" , 0x210, 0x21c, 0, PLL_AO, |
64 | 21, 0x214, 24, 0x0, 0x214, 0), |
65 | PLL(CLK_APMIXED_MAINPLL, "mainpll" , 0x220, 0x22c, 0xf0000100, HAVE_RST_BAR, 21, |
66 | 0x220, 4, 0x0, 0x224, 0), |
67 | PLL(CLK_APMIXED_UNIVPLL, "univpll" , 0x230, 0x23c, 0xfe000000, HAVE_RST_BAR, 7, |
68 | 0x230, 4, 0x0, 0x234, 14), |
69 | PLL_B(CLK_APMIXED_MMPLL, "mmpll" , 0x240, 0x24c, 0, 0, 21, 0x244, 24, 0x0, |
70 | 0x244, 0, mmpll_div_table), |
71 | PLL(CLK_APMIXED_MSDCPLL, "msdcpll" , 0x250, 0x25c, 0, 0, 21, 0x250, 4, 0x0, 0x254, 0), |
72 | PLL(CLK_APMIXED_VENCPLL, "vencpll" , 0x260, 0x26c, 0, 0, 21, 0x260, 4, 0x0, 0x264, 0), |
73 | PLL(CLK_APMIXED_TVDPLL, "tvdpll" , 0x270, 0x27c, 0, 0, 21, 0x270, 4, 0x0, 0x274, 0), |
74 | PLL(CLK_APMIXED_MPLL, "mpll" , 0x280, 0x28c, 0, 0, 21, 0x280, 4, 0x0, 0x284, 0), |
75 | PLL(CLK_APMIXED_VCODECPLL, "vcodecpll" , 0x290, 0x29c, 0, 0, 21, 0x290, 4, 0x0, 0x294, 0), |
76 | PLL(CLK_APMIXED_APLL1, "apll1" , 0x2a0, 0x2b0, 0, 0, 31, 0x2a0, 4, 0x2a4, 0x2a4, 0), |
77 | PLL(CLK_APMIXED_APLL2, "apll2" , 0x2b4, 0x2c4, 0, 0, 31, 0x2b4, 4, 0x2b8, 0x2b8, 0), |
78 | PLL(CLK_APMIXED_LVDSPLL, "lvdspll" , 0x2d0, 0x2dc, 0, 0, 21, 0x2d0, 4, 0x0, 0x2d4, 0), |
79 | PLL(CLK_APMIXED_MSDCPLL2, "msdcpll2" , 0x2f0, 0x2fc, 0, 0, 21, 0x2f0, 4, 0x0, 0x2f4, 0), |
80 | }; |
81 | |
82 | enum fh_pll_id { |
83 | FH_ARMCA7PLL, |
84 | FH_ARMCA15PLL, |
85 | FH_MAINPLL, |
86 | FH_MPLL, |
87 | FH_MSDCPLL, |
88 | FH_MMPLL, |
89 | FH_VENCPLL, |
90 | FH_TVDPLL, |
91 | FH_VCODECPLL, |
92 | FH_LVDSPLL, |
93 | FH_MSDC2PLL, |
94 | FH_NR_FH, |
95 | }; |
96 | |
97 | #define FH(_pllid, _fhid, _offset) { \ |
98 | .data = { \ |
99 | .pll_id = _pllid, \ |
100 | .fh_id = _fhid, \ |
101 | .fh_ver = FHCTL_PLLFH_V1, \ |
102 | .fhx_offset = _offset, \ |
103 | .dds_mask = GENMASK(21, 0), \ |
104 | .slope0_value = 0x6003c97, \ |
105 | .slope1_value = 0x6003c97, \ |
106 | .sfstrx_en = BIT(2), \ |
107 | .frddsx_en = BIT(1), \ |
108 | .fhctlx_en = BIT(0), \ |
109 | .tgl_org = BIT(31), \ |
110 | .dvfs_tri = BIT(31), \ |
111 | .pcwchg = BIT(31), \ |
112 | .dt_val = 0x0, \ |
113 | .df_val = 0x9, \ |
114 | .updnlmt_shft = 16, \ |
115 | .msk_frddsx_dys = GENMASK(23, 20), \ |
116 | .msk_frddsx_dts = GENMASK(19, 16), \ |
117 | }, \ |
118 | } |
119 | |
120 | static struct mtk_pllfh_data pllfhs[] = { |
121 | FH(CLK_APMIXED_ARMCA7PLL, FH_ARMCA7PLL, 0x38), |
122 | FH(CLK_APMIXED_ARMCA15PLL, FH_ARMCA15PLL, 0x4c), |
123 | FH(CLK_APMIXED_MAINPLL, FH_MAINPLL, 0x60), |
124 | FH(CLK_APMIXED_MPLL, FH_MPLL, 0x74), |
125 | FH(CLK_APMIXED_MSDCPLL, FH_MSDCPLL, 0x88), |
126 | FH(CLK_APMIXED_MMPLL, FH_MMPLL, 0x9c), |
127 | FH(CLK_APMIXED_VENCPLL, FH_VENCPLL, 0xb0), |
128 | FH(CLK_APMIXED_TVDPLL, FH_TVDPLL, 0xc4), |
129 | FH(CLK_APMIXED_VCODECPLL, FH_VCODECPLL, 0xd8), |
130 | FH(CLK_APMIXED_LVDSPLL, FH_LVDSPLL, 0xec), |
131 | FH(CLK_APMIXED_MSDCPLL2, FH_MSDC2PLL, 0x100), |
132 | }; |
133 | |
134 | static const struct of_device_id of_match_clk_mt8173_apmixed[] = { |
135 | { .compatible = "mediatek,mt8173-apmixedsys" }, |
136 | { /* sentinel */ } |
137 | }; |
138 | MODULE_DEVICE_TABLE(of, of_match_clk_mt8173_apmixed); |
139 | |
140 | static int clk_mt8173_apmixed_probe(struct platform_device *pdev) |
141 | { |
142 | const u8 *fhctl_node = "mediatek,mt8173-fhctl" ; |
143 | struct device_node *node = pdev->dev.of_node; |
144 | struct clk_hw_onecell_data *clk_data; |
145 | void __iomem *base; |
146 | struct clk_hw *hw; |
147 | int r; |
148 | |
149 | base = of_iomap(node, index: 0); |
150 | if (!base) |
151 | return -ENOMEM; |
152 | |
153 | clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); |
154 | if (IS_ERR_OR_NULL(ptr: clk_data)) { |
155 | r = -ENOMEM; |
156 | goto unmap_io; |
157 | } |
158 | |
159 | fhctl_parse_dt(compatible_node: fhctl_node, pllfhs, ARRAY_SIZE(pllfhs)); |
160 | r = mtk_clk_register_pllfhs(node, plls, ARRAY_SIZE(plls), |
161 | pllfhs, ARRAY_SIZE(pllfhs), clk_data); |
162 | if (r) |
163 | goto free_clk_data; |
164 | |
165 | hw = mtk_clk_register_ref2usb_tx(name: "ref2usb_tx" , parent_name: "clk26m" , reg: base + REGOFF_REF2USB); |
166 | if (IS_ERR(ptr: hw)) { |
167 | r = PTR_ERR(ptr: hw); |
168 | dev_err(&pdev->dev, "Failed to register ref2usb_tx: %d\n" , r); |
169 | goto unregister_plls; |
170 | } |
171 | clk_data->hws[CLK_APMIXED_REF2USB_TX] = hw; |
172 | |
173 | hw = devm_clk_hw_register_divider(&pdev->dev, "hdmi_ref" , "tvdpll_594m" , 0, |
174 | base + REGOFF_HDMI_REF, 16, 3, |
175 | CLK_DIVIDER_POWER_OF_TWO, NULL); |
176 | clk_data->hws[CLK_APMIXED_HDMI_REF] = hw; |
177 | |
178 | r = of_clk_add_hw_provider(np: node, get: of_clk_hw_onecell_get, data: clk_data); |
179 | if (r) |
180 | goto unregister_ref2usb; |
181 | |
182 | return 0; |
183 | |
184 | unregister_ref2usb: |
185 | mtk_clk_unregister_ref2usb_tx(hw: clk_data->hws[CLK_APMIXED_REF2USB_TX]); |
186 | unregister_plls: |
187 | mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs, |
188 | ARRAY_SIZE(pllfhs), clk_data); |
189 | free_clk_data: |
190 | mtk_free_clk_data(clk_data); |
191 | unmap_io: |
192 | iounmap(addr: base); |
193 | return r; |
194 | } |
195 | |
196 | static void clk_mt8173_apmixed_remove(struct platform_device *pdev) |
197 | { |
198 | struct device_node *node = pdev->dev.of_node; |
199 | struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev); |
200 | |
201 | of_clk_del_provider(np: node); |
202 | mtk_clk_unregister_ref2usb_tx(hw: clk_data->hws[CLK_APMIXED_REF2USB_TX]); |
203 | mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs, |
204 | ARRAY_SIZE(pllfhs), clk_data); |
205 | mtk_free_clk_data(clk_data); |
206 | } |
207 | |
208 | static struct platform_driver clk_mt8173_apmixed_drv = { |
209 | .probe = clk_mt8173_apmixed_probe, |
210 | .remove_new = clk_mt8173_apmixed_remove, |
211 | .driver = { |
212 | .name = "clk-mt8173-apmixed" , |
213 | .of_match_table = of_match_clk_mt8173_apmixed, |
214 | }, |
215 | }; |
216 | module_platform_driver(clk_mt8173_apmixed_drv); |
217 | |
218 | MODULE_DESCRIPTION("MediaTek MT8173 apmixed clocks driver" ); |
219 | MODULE_LICENSE("GPL" ); |
220 | |