1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Marvell Armada AP806 System Controller
4 *
5 * Copyright (C) 2016 Marvell
6 *
7 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
8 *
9 */
10
11#define pr_fmt(fmt) "ap806-system-controller: " fmt
12
13#include "armada_ap_cp_helper.h"
14#include <linux/clk-provider.h>
15#include <linux/mfd/syscon.h>
16#include <linux/init.h>
17#include <linux/of.h>
18#include <linux/platform_device.h>
19#include <linux/regmap.h>
20
21#define AP806_SAR_REG			0x400
22#define AP806_SAR_CLKFREQ_MODE_MASK	0x1f
23
24#define AP806_CLK_NUM			6
25
26static struct clk *ap806_clks[AP806_CLK_NUM];
27
28static struct clk_onecell_data ap806_clk_data = {
29	.clks = ap806_clks,
30	.clk_num = AP806_CLK_NUM,
31};
32
33static int ap806_get_sar_clocks(unsigned int freq_mode,
34				unsigned int *cpuclk_freq,
35				unsigned int *dclk_freq)
36{
37	switch (freq_mode) {
38	case 0x0:
39		*cpuclk_freq = 2000;
40		*dclk_freq = 600;
41		break;
42	case 0x1:
43		*cpuclk_freq = 2000;
44		*dclk_freq = 525;
45		break;
46	case 0x6:
47		*cpuclk_freq = 1800;
48		*dclk_freq = 600;
49		break;
50	case 0x7:
51		*cpuclk_freq = 1800;
52		*dclk_freq = 525;
53		break;
54	case 0x4:
55		*cpuclk_freq = 1600;
56		*dclk_freq = 400;
57		break;
58	case 0xB:
59		*cpuclk_freq = 1600;
60		*dclk_freq = 450;
61		break;
62	case 0xD:
63		*cpuclk_freq = 1600;
64		*dclk_freq = 525;
65		break;
66	case 0x1a:
67		*cpuclk_freq = 1400;
68		*dclk_freq = 400;
69		break;
70	case 0x14:
71		*cpuclk_freq = 1300;
72		*dclk_freq = 400;
73		break;
74	case 0x17:
75		*cpuclk_freq = 1300;
76		*dclk_freq = 325;
77		break;
78	case 0x19:
79		*cpuclk_freq = 1200;
80		*dclk_freq = 400;
81		break;
82	case 0x13:
83		*cpuclk_freq = 1000;
84		*dclk_freq = 325;
85		break;
86	case 0x1d:
87		*cpuclk_freq = 1000;
88		*dclk_freq = 400;
89		break;
90	case 0x1c:
91		*cpuclk_freq = 800;
92		*dclk_freq = 400;
93		break;
94	case 0x1b:
95		*cpuclk_freq = 600;
96		*dclk_freq = 400;
97		break;
98	default:
99		return -EINVAL;
100	}
101
102	return 0;
103}
104
105static int ap807_get_sar_clocks(unsigned int freq_mode,
106				unsigned int *cpuclk_freq,
107				unsigned int *dclk_freq)
108{
109	switch (freq_mode) {
110	case 0x0:
111		*cpuclk_freq = 2000;
112		*dclk_freq = 1200;
113		break;
114	case 0x6:
115		*cpuclk_freq = 2200;
116		*dclk_freq = 1200;
117		break;
118	case 0xD:
119		*cpuclk_freq = 1600;
120		*dclk_freq = 1200;
121		break;
122	default:
123		return -EINVAL;
124	}
125
126	return 0;
127}
128
129static int ap806_syscon_common_probe(struct platform_device *pdev,
130				     struct device_node *syscon_node)
131{
132	unsigned int freq_mode, cpuclk_freq, dclk_freq;
133	const char *name, *fixedclk_name;
134	struct device *dev = &pdev->dev;
135	struct device_node *np = dev->of_node;
136	struct regmap *regmap;
137	u32 reg;
138	int ret;
139
140	regmap = syscon_node_to_regmap(syscon_node);
141	if (IS_ERR(regmap)) {
142		dev_err(dev, "cannot get regmap\n");
143		return PTR_ERR(regmap);
144	}
145
146	ret = regmap_read(regmap, AP806_SAR_REG, &reg);
147	if (ret) {
148		dev_err(dev, "cannot read from regmap\n");
149		return ret;
150	}
151
152	freq_mode = reg & AP806_SAR_CLKFREQ_MODE_MASK;
153
154	if (of_device_is_compatible(pdev->dev.of_node,
155				    "marvell,ap806-clock")) {
156		ret = ap806_get_sar_clocks(freq_mode, &cpuclk_freq, &dclk_freq);
157	} else if (of_device_is_compatible(pdev->dev.of_node,
158					   "marvell,ap807-clock")) {
159		ret = ap807_get_sar_clocks(freq_mode, &cpuclk_freq, &dclk_freq);
160	} else {
161		dev_err(dev, "compatible not supported\n");
162		return -EINVAL;
163	}
164
165	if (ret) {
166		dev_err(dev, "invalid Sample at Reset value\n");
167		return ret;
168	}
169
170	/* Convert to hertz */
171	cpuclk_freq *= 1000 * 1000;
172	dclk_freq *= 1000 * 1000;
173
174	/* CPU clocks depend on the Sample At Reset configuration */
175	name = ap_cp_unique_name(dev, syscon_node, "pll-cluster-0");
176	ap806_clks[0] = clk_register_fixed_rate(dev, name, NULL,
177						0, cpuclk_freq);
178	if (IS_ERR(ap806_clks[0])) {
179		ret = PTR_ERR(ap806_clks[0]);
180		goto fail0;
181	}
182
183	name = ap_cp_unique_name(dev, syscon_node, "pll-cluster-1");
184	ap806_clks[1] = clk_register_fixed_rate(dev, name, NULL, 0,
185						cpuclk_freq);
186	if (IS_ERR(ap806_clks[1])) {
187		ret = PTR_ERR(ap806_clks[1]);
188		goto fail1;
189	}
190
191	/* Fixed clock is always 1200 Mhz */
192	fixedclk_name = ap_cp_unique_name(dev, syscon_node, "fixed");
193	ap806_clks[2] = clk_register_fixed_rate(dev, fixedclk_name, NULL,
194						0, 1200 * 1000 * 1000);
195	if (IS_ERR(ap806_clks[2])) {
196		ret = PTR_ERR(ap806_clks[2]);
197		goto fail2;
198	}
199
200	/* MSS Clock is fixed clock divided by 6 */
201	name = ap_cp_unique_name(dev, syscon_node, "mss");
202	ap806_clks[3] = clk_register_fixed_factor(NULL, name, fixedclk_name,
203						  0, 1, 6);
204	if (IS_ERR(ap806_clks[3])) {
205		ret = PTR_ERR(ap806_clks[3]);
206		goto fail3;
207	}
208
209	/* SDIO(/eMMC) Clock is fixed clock divided by 3 */
210	name = ap_cp_unique_name(dev, syscon_node, "sdio");
211	ap806_clks[4] = clk_register_fixed_factor(NULL, name,
212						  fixedclk_name,
213						  0, 1, 3);
214	if (IS_ERR(ap806_clks[4])) {
215		ret = PTR_ERR(ap806_clks[4]);
216		goto fail4;
217	}
218
219	/* AP-DCLK(HCLK) Clock is DDR clock divided by 2 */
220	name = ap_cp_unique_name(dev, syscon_node, "ap-dclk");
221	ap806_clks[5] = clk_register_fixed_rate(dev, name, NULL, 0, dclk_freq);
222	if (IS_ERR(ap806_clks[5])) {
223		ret = PTR_ERR(ap806_clks[5]);
224		goto fail5;
225	}
226
227	ret = of_clk_add_provider(np, of_clk_src_onecell_get, &ap806_clk_data);
228	if (ret)
229		goto fail_clk_add;
230
231	return 0;
232
233fail_clk_add:
234	clk_unregister_fixed_factor(ap806_clks[5]);
235fail5:
236	clk_unregister_fixed_factor(ap806_clks[4]);
237fail4:
238	clk_unregister_fixed_factor(ap806_clks[3]);
239fail3:
240	clk_unregister_fixed_rate(ap806_clks[2]);
241fail2:
242	clk_unregister_fixed_rate(ap806_clks[1]);
243fail1:
244	clk_unregister_fixed_rate(ap806_clks[0]);
245fail0:
246	return ret;
247}
248
249static int ap806_syscon_legacy_probe(struct platform_device *pdev)
250{
251	dev_warn(&pdev->dev, FW_WARN "Using legacy device tree binding\n");
252	dev_warn(&pdev->dev, FW_WARN "Update your device tree:\n");
253	dev_warn(&pdev->dev, FW_WARN
254		 "This binding won't be supported in future kernel\n");
255
256	return ap806_syscon_common_probe(pdev, pdev->dev.of_node);
257
258}
259
260static int ap806_clock_probe(struct platform_device *pdev)
261{
262	return ap806_syscon_common_probe(pdev, pdev->dev.of_node->parent);
263}
264
265static const struct of_device_id ap806_syscon_legacy_of_match[] = {
266	{ .compatible = "marvell,ap806-system-controller", },
267	{ }
268};
269
270static struct platform_driver ap806_syscon_legacy_driver = {
271	.probe = ap806_syscon_legacy_probe,
272	.driver		= {
273		.name	= "marvell-ap806-system-controller",
274		.of_match_table = ap806_syscon_legacy_of_match,
275		.suppress_bind_attrs = true,
276	},
277};
278builtin_platform_driver(ap806_syscon_legacy_driver);
279
280static const struct of_device_id ap806_clock_of_match[] = {
281	{ .compatible = "marvell,ap806-clock", },
282	{ .compatible = "marvell,ap807-clock", },
283	{ }
284};
285
286static struct platform_driver ap806_clock_driver = {
287	.probe = ap806_clock_probe,
288	.driver		= {
289		.name	= "marvell-ap806-clock",
290		.of_match_table = ap806_clock_of_match,
291		.suppress_bind_attrs = true,
292	},
293};
294builtin_platform_driver(ap806_clock_driver);
295