1 /*
2  * Marvell Armada AP806 System Controller
3  *
4  * Copyright (C) 2016 Marvell
5  *
6  * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
7  *
8  * This file is licensed under the terms of the GNU General Public
9  * License version 2.  This program is licensed "as is" without any
10  * warranty of any kind, whether express or implied.
11  */
12 
13 #define pr_fmt(fmt) "ap806-system-controller: " fmt
14 
15 #include <linux/clk-provider.h>
16 #include <linux/mfd/syscon.h>
17 #include <linux/init.h>
18 #include <linux/of.h>
19 #include <linux/of_address.h>
20 #include <linux/platform_device.h>
21 #include <linux/regmap.h>
22 
23 #define AP806_SAR_REG			0x400
24 #define AP806_SAR_CLKFREQ_MODE_MASK	0x1f
25 
26 #define AP806_CLK_NUM			5
27 
28 static struct clk *ap806_clks[AP806_CLK_NUM];
29 
30 static struct clk_onecell_data ap806_clk_data = {
31 	.clks = ap806_clks,
32 	.clk_num = AP806_CLK_NUM,
33 };
34 
35 static char *ap806_unique_name(struct device *dev, struct device_node *np,
36 			       char *name)
37 {
38 	const __be32 *reg;
39 	u64 addr;
40 
41 	reg = of_get_property(np, "reg", NULL);
42 	addr = of_translate_address(np, reg);
43 	return devm_kasprintf(dev, GFP_KERNEL, "%llx-%s",
44 			(unsigned long long)addr, name);
45 }
46 
47 static int ap806_syscon_common_probe(struct platform_device *pdev,
48 				     struct device_node *syscon_node)
49 {
50 	unsigned int freq_mode, cpuclk_freq;
51 	const char *name, *fixedclk_name;
52 	struct device *dev = &pdev->dev;
53 	struct device_node *np = dev->of_node;
54 	struct regmap *regmap;
55 	u32 reg;
56 	int ret;
57 
58 	regmap = syscon_node_to_regmap(syscon_node);
59 	if (IS_ERR(regmap)) {
60 		dev_err(dev, "cannot get regmap\n");
61 		return PTR_ERR(regmap);
62 	}
63 
64 	ret = regmap_read(regmap, AP806_SAR_REG, &reg);
65 	if (ret) {
66 		dev_err(dev, "cannot read from regmap\n");
67 		return ret;
68 	}
69 
70 	freq_mode = reg & AP806_SAR_CLKFREQ_MODE_MASK;
71 	switch (freq_mode) {
72 	case 0x0:
73 	case 0x1:
74 		cpuclk_freq = 2000;
75 		break;
76 	case 0x6:
77 	case 0x7:
78 		cpuclk_freq = 1800;
79 		break;
80 	case 0x4:
81 	case 0xB:
82 	case 0xD:
83 		cpuclk_freq = 1600;
84 		break;
85 	case 0x1a:
86 		cpuclk_freq = 1400;
87 		break;
88 	case 0x14:
89 	case 0x17:
90 		cpuclk_freq = 1300;
91 		break;
92 	case 0x19:
93 		cpuclk_freq = 1200;
94 		break;
95 	case 0x13:
96 	case 0x1d:
97 		cpuclk_freq = 1000;
98 		break;
99 	case 0x1c:
100 		cpuclk_freq = 800;
101 		break;
102 	case 0x1b:
103 		cpuclk_freq = 600;
104 		break;
105 	default:
106 		dev_err(dev, "invalid SAR value\n");
107 		return -EINVAL;
108 	}
109 
110 	/* Convert to hertz */
111 	cpuclk_freq *= 1000 * 1000;
112 
113 	/* CPU clocks depend on the Sample At Reset configuration */
114 	name = ap806_unique_name(dev, syscon_node, "cpu-cluster-0");
115 	ap806_clks[0] = clk_register_fixed_rate(dev, name, NULL,
116 						0, cpuclk_freq);
117 	if (IS_ERR(ap806_clks[0])) {
118 		ret = PTR_ERR(ap806_clks[0]);
119 		goto fail0;
120 	}
121 
122 	name = ap806_unique_name(dev, syscon_node, "cpu-cluster-1");
123 	ap806_clks[1] = clk_register_fixed_rate(dev, name, NULL, 0,
124 						cpuclk_freq);
125 	if (IS_ERR(ap806_clks[1])) {
126 		ret = PTR_ERR(ap806_clks[1]);
127 		goto fail1;
128 	}
129 
130 	/* Fixed clock is always 1200 Mhz */
131 	fixedclk_name = ap806_unique_name(dev, syscon_node, "fixed");
132 	ap806_clks[2] = clk_register_fixed_rate(dev, fixedclk_name, NULL,
133 						0, 1200 * 1000 * 1000);
134 	if (IS_ERR(ap806_clks[2])) {
135 		ret = PTR_ERR(ap806_clks[2]);
136 		goto fail2;
137 	}
138 
139 	/* MSS Clock is fixed clock divided by 6 */
140 	name = ap806_unique_name(dev, syscon_node, "mss");
141 	ap806_clks[3] = clk_register_fixed_factor(NULL, name, fixedclk_name,
142 						  0, 1, 6);
143 	if (IS_ERR(ap806_clks[3])) {
144 		ret = PTR_ERR(ap806_clks[3]);
145 		goto fail3;
146 	}
147 
148 	/* SDIO(/eMMC) Clock is fixed clock divided by 3 */
149 	name = ap806_unique_name(dev, syscon_node, "sdio");
150 	ap806_clks[4] = clk_register_fixed_factor(NULL, name,
151 						  fixedclk_name,
152 						  0, 1, 3);
153 	if (IS_ERR(ap806_clks[4])) {
154 		ret = PTR_ERR(ap806_clks[4]);
155 		goto fail4;
156 	}
157 
158 	of_clk_add_provider(np, of_clk_src_onecell_get, &ap806_clk_data);
159 	ret = of_clk_add_provider(np, of_clk_src_onecell_get, &ap806_clk_data);
160 	if (ret)
161 		goto fail_clk_add;
162 
163 	return 0;
164 
165 fail_clk_add:
166 	clk_unregister_fixed_factor(ap806_clks[4]);
167 fail4:
168 	clk_unregister_fixed_factor(ap806_clks[3]);
169 fail3:
170 	clk_unregister_fixed_rate(ap806_clks[2]);
171 fail2:
172 	clk_unregister_fixed_rate(ap806_clks[1]);
173 fail1:
174 	clk_unregister_fixed_rate(ap806_clks[0]);
175 fail0:
176 	return ret;
177 }
178 
179 static int ap806_syscon_legacy_probe(struct platform_device *pdev)
180 {
181 	dev_warn(&pdev->dev, FW_WARN "Using legacy device tree binding\n");
182 	dev_warn(&pdev->dev, FW_WARN "Update your device tree:\n");
183 	dev_warn(&pdev->dev, FW_WARN
184 		 "This binding won't be supported in future kernel\n");
185 
186 	return ap806_syscon_common_probe(pdev, pdev->dev.of_node);
187 
188 }
189 
190 static int ap806_clock_probe(struct platform_device *pdev)
191 {
192 	return ap806_syscon_common_probe(pdev, pdev->dev.of_node->parent);
193 }
194 
195 static const struct of_device_id ap806_syscon_legacy_of_match[] = {
196 	{ .compatible = "marvell,ap806-system-controller", },
197 	{ }
198 };
199 
200 static struct platform_driver ap806_syscon_legacy_driver = {
201 	.probe = ap806_syscon_legacy_probe,
202 	.driver		= {
203 		.name	= "marvell-ap806-system-controller",
204 		.of_match_table = ap806_syscon_legacy_of_match,
205 		.suppress_bind_attrs = true,
206 	},
207 };
208 builtin_platform_driver(ap806_syscon_legacy_driver);
209 
210 static const struct of_device_id ap806_clock_of_match[] = {
211 	{ .compatible = "marvell,ap806-clock", },
212 	{ }
213 };
214 
215 static struct platform_driver ap806_clock_driver = {
216 	.probe = ap806_clock_probe,
217 	.driver		= {
218 		.name	= "marvell-ap806-clock",
219 		.of_match_table = ap806_clock_of_match,
220 		.suppress_bind_attrs = true,
221 	},
222 };
223 builtin_platform_driver(ap806_clock_driver);
224