1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2022 Collabora Ltd. |
4 | * Author: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> |
5 | */ |
6 | |
7 | #include <dt-bindings/clock/mediatek,mt6795-clk.h> |
8 | #include <linux/module.h> |
9 | #include <linux/platform_device.h> |
10 | #include "clk-fhctl.h" |
11 | #include "clk-mtk.h" |
12 | #include "clk-pll.h" |
13 | #include "clk-pllfh.h" |
14 | |
15 | #define REG_REF2USB 0x8 |
16 | #define REG_AP_PLL_CON7 0x1c |
17 | #define MD1_MTCMOS_OFF BIT(0) |
18 | #define MD1_MEM_OFF BIT(1) |
19 | #define MD1_CLK_OFF BIT(4) |
20 | #define MD1_ISO_OFF BIT(8) |
21 | |
22 | #define MT6795_PLL_FMAX (3000UL * MHZ) |
23 | #define MT6795_CON0_EN BIT(0) |
24 | #define MT6795_CON0_RST_BAR BIT(24) |
25 | |
26 | #define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ |
27 | _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift) { \ |
28 | .id = _id, \ |
29 | .name = _name, \ |
30 | .reg = _reg, \ |
31 | .pwr_reg = _pwr_reg, \ |
32 | .en_mask = MT6795_CON0_EN | _en_mask, \ |
33 | .flags = _flags, \ |
34 | .rst_bar_mask = MT6795_CON0_RST_BAR, \ |
35 | .fmax = MT6795_PLL_FMAX, \ |
36 | .pcwbits = _pcwbits, \ |
37 | .pd_reg = _pd_reg, \ |
38 | .pd_shift = _pd_shift, \ |
39 | .tuner_reg = _tuner_reg, \ |
40 | .pcw_reg = _pcw_reg, \ |
41 | .pcw_shift = _pcw_shift, \ |
42 | .div_table = NULL, \ |
43 | .pll_en_bit = 0, \ |
44 | } |
45 | |
46 | static const struct mtk_pll_data plls[] = { |
47 | PLL(CLK_APMIXED_ARMCA53PLL, "armca53pll" , 0x200, 0x20c, 0, PLL_AO, |
48 | 21, 0x204, 24, 0x0, 0x204, 0), |
49 | PLL(CLK_APMIXED_MAINPLL, "mainpll" , 0x220, 0x22c, 0xf0000101, HAVE_RST_BAR, |
50 | 21, 0x220, 4, 0x0, 0x224, 0), |
51 | PLL(CLK_APMIXED_UNIVPLL, "univpll" , 0x230, 0x23c, 0xfe000101, HAVE_RST_BAR, |
52 | 7, 0x230, 4, 0x0, 0x234, 14), |
53 | PLL(CLK_APMIXED_MMPLL, "mmpll" , 0x240, 0x24c, 0, 0, 21, 0x244, 24, 0x0, 0x244, 0), |
54 | PLL(CLK_APMIXED_MSDCPLL, "msdcpll" , 0x250, 0x25c, 0, 0, 21, 0x250, 4, 0x0, 0x254, 0), |
55 | PLL(CLK_APMIXED_VENCPLL, "vencpll" , 0x260, 0x26c, 0, 0, 21, 0x260, 4, 0x0, 0x264, 0), |
56 | PLL(CLK_APMIXED_TVDPLL, "tvdpll" , 0x270, 0x27c, 0, 0, 21, 0x270, 4, 0x0, 0x274, 0), |
57 | PLL(CLK_APMIXED_MPLL, "mpll" , 0x280, 0x28c, 0, 0, 21, 0x280, 4, 0x0, 0x284, 0), |
58 | PLL(CLK_APMIXED_VCODECPLL, "vcodecpll" , 0x290, 0x29c, 0, 0, 21, 0x290, 4, 0x0, 0x294, 0), |
59 | PLL(CLK_APMIXED_APLL1, "apll1" , 0x2a0, 0x2b0, 0, 0, 31, 0x2a0, 4, 0x2a8, 0x2a4, 0), |
60 | PLL(CLK_APMIXED_APLL2, "apll2" , 0x2b4, 0x2c4, 0, 0, 31, 0x2b4, 4, 0x2bc, 0x2b8, 0), |
61 | }; |
62 | |
63 | enum fh_pll_id { |
64 | FH_CA53PLL_LL, |
65 | FH_CA53PLL_BL, |
66 | FH_MAINPLL, |
67 | FH_MPLL, |
68 | FH_MSDCPLL, |
69 | FH_MMPLL, |
70 | FH_VENCPLL, |
71 | FH_TVDPLL, |
72 | FH_VCODECPLL, |
73 | FH_NR_FH, |
74 | }; |
75 | |
76 | #define _FH(_pllid, _fhid, _slope, _offset) { \ |
77 | .data = { \ |
78 | .pll_id = _pllid, \ |
79 | .fh_id = _fhid, \ |
80 | .fh_ver = FHCTL_PLLFH_V1, \ |
81 | .fhx_offset = _offset, \ |
82 | .dds_mask = GENMASK(21, 0), \ |
83 | .slope0_value = _slope, \ |
84 | .slope1_value = _slope, \ |
85 | .sfstrx_en = BIT(2), \ |
86 | .frddsx_en = BIT(1), \ |
87 | .fhctlx_en = BIT(0), \ |
88 | .tgl_org = BIT(31), \ |
89 | .dvfs_tri = BIT(31), \ |
90 | .pcwchg = BIT(31), \ |
91 | .dt_val = 0x0, \ |
92 | .df_val = 0x9, \ |
93 | .updnlmt_shft = 16, \ |
94 | .msk_frddsx_dys = GENMASK(23, 20), \ |
95 | .msk_frddsx_dts = GENMASK(19, 16), \ |
96 | }, \ |
97 | } |
98 | |
99 | #define FH(_pllid, _fhid, _offset) _FH(_pllid, _fhid, 0x6003c97, _offset) |
100 | #define FH_M(_pllid, _fhid, _offset) _FH(_pllid, _fhid, 0x6000140, _offset) |
101 | |
102 | static struct mtk_pllfh_data pllfhs[] = { |
103 | FH(CLK_APMIXED_ARMCA53PLL, FH_CA53PLL_BL, 0x38), |
104 | FH(CLK_APMIXED_MAINPLL, FH_MAINPLL, 0x60), |
105 | FH_M(CLK_APMIXED_MPLL, FH_MPLL, 0x74), |
106 | FH(CLK_APMIXED_MSDCPLL, FH_MSDCPLL, 0x88), |
107 | FH(CLK_APMIXED_MMPLL, FH_MMPLL, 0x9c), |
108 | FH(CLK_APMIXED_VENCPLL, FH_VENCPLL, 0xb0), |
109 | FH(CLK_APMIXED_TVDPLL, FH_TVDPLL, 0xc4), |
110 | FH(CLK_APMIXED_VCODECPLL, FH_VCODECPLL, 0xd8), |
111 | }; |
112 | |
113 | static void clk_mt6795_apmixed_setup_md1(void __iomem *base) |
114 | { |
115 | void __iomem *reg = base + REG_AP_PLL_CON7; |
116 | |
117 | /* Turn on MD1 internal clock */ |
118 | writel(readl(addr: reg) & ~MD1_CLK_OFF, addr: reg); |
119 | |
120 | /* Unlock MD1's MTCMOS power path */ |
121 | writel(readl(addr: reg) & ~MD1_MTCMOS_OFF, addr: reg); |
122 | |
123 | /* Turn on ISO */ |
124 | writel(readl(addr: reg) & ~MD1_ISO_OFF, addr: reg); |
125 | |
126 | /* Turn on memory */ |
127 | writel(readl(addr: reg) & ~MD1_MEM_OFF, addr: reg); |
128 | } |
129 | |
130 | static const struct of_device_id of_match_clk_mt6795_apmixed[] = { |
131 | { .compatible = "mediatek,mt6795-apmixedsys" }, |
132 | { /* sentinel */ } |
133 | }; |
134 | MODULE_DEVICE_TABLE(of, of_match_clk_mt6795_apmixed); |
135 | |
136 | static int clk_mt6795_apmixed_probe(struct platform_device *pdev) |
137 | { |
138 | struct clk_hw_onecell_data *clk_data; |
139 | struct device *dev = &pdev->dev; |
140 | struct device_node *node = dev->of_node; |
141 | const u8 *fhctl_node = "mediatek,mt6795-fhctl" ; |
142 | void __iomem *base; |
143 | struct clk_hw *hw; |
144 | int ret; |
145 | |
146 | base = devm_platform_ioremap_resource(pdev, index: 0); |
147 | if (IS_ERR(ptr: base)) |
148 | return PTR_ERR(ptr: base); |
149 | |
150 | clk_data = mtk_alloc_clk_data(CLK_APMIXED_NR_CLK); |
151 | if (!clk_data) |
152 | return -ENOMEM; |
153 | |
154 | fhctl_parse_dt(compatible_node: fhctl_node, pllfhs, ARRAY_SIZE(pllfhs)); |
155 | ret = mtk_clk_register_pllfhs(node, plls, ARRAY_SIZE(plls), |
156 | pllfhs, ARRAY_SIZE(pllfhs), clk_data); |
157 | if (ret) |
158 | goto free_clk_data; |
159 | |
160 | hw = mtk_clk_register_ref2usb_tx(name: "ref2usb_tx" , parent_name: "clk26m" , reg: base + REG_REF2USB); |
161 | if (IS_ERR(ptr: hw)) { |
162 | ret = PTR_ERR(ptr: hw); |
163 | dev_err(dev, "Failed to register ref2usb_tx: %d\n" , ret); |
164 | goto unregister_plls; |
165 | } |
166 | clk_data->hws[CLK_APMIXED_REF2USB_TX] = hw; |
167 | |
168 | ret = of_clk_add_hw_provider(np: node, get: of_clk_hw_onecell_get, data: clk_data); |
169 | if (ret) { |
170 | dev_err(dev, "Cannot register clock provider: %d\n" , ret); |
171 | goto unregister_ref2usb; |
172 | } |
173 | |
174 | /* Setup MD1 to avoid random crashes */ |
175 | dev_dbg(dev, "Performing initial setup for MD1\n" ); |
176 | clk_mt6795_apmixed_setup_md1(base); |
177 | |
178 | return 0; |
179 | |
180 | unregister_ref2usb: |
181 | mtk_clk_unregister_ref2usb_tx(hw: clk_data->hws[CLK_APMIXED_REF2USB_TX]); |
182 | unregister_plls: |
183 | mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs, |
184 | ARRAY_SIZE(pllfhs), clk_data); |
185 | free_clk_data: |
186 | mtk_free_clk_data(clk_data); |
187 | return ret; |
188 | } |
189 | |
190 | static void clk_mt6795_apmixed_remove(struct platform_device *pdev) |
191 | { |
192 | struct device_node *node = pdev->dev.of_node; |
193 | struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev); |
194 | |
195 | of_clk_del_provider(np: node); |
196 | mtk_clk_unregister_ref2usb_tx(hw: clk_data->hws[CLK_APMIXED_REF2USB_TX]); |
197 | mtk_clk_unregister_pllfhs(plls, ARRAY_SIZE(plls), pllfhs, |
198 | ARRAY_SIZE(pllfhs), clk_data); |
199 | mtk_free_clk_data(clk_data); |
200 | } |
201 | |
202 | static struct platform_driver clk_mt6795_apmixed_drv = { |
203 | .probe = clk_mt6795_apmixed_probe, |
204 | .remove_new = clk_mt6795_apmixed_remove, |
205 | .driver = { |
206 | .name = "clk-mt6795-apmixed" , |
207 | .of_match_table = of_match_clk_mt6795_apmixed, |
208 | }, |
209 | }; |
210 | module_platform_driver(clk_mt6795_apmixed_drv); |
211 | |
212 | MODULE_DESCRIPTION("MediaTek MT6795 apmixed clocks driver" ); |
213 | MODULE_LICENSE("GPL" ); |
214 | |