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