Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[cascardo/linux.git] / drivers / clk / mvebu / ap806-system-controller.c
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/module.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                   4
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 int ap806_syscon_clk_probe(struct platform_device *pdev)
36 {
37         unsigned int freq_mode, cpuclk_freq;
38         const char *name, *fixedclk_name;
39         struct device_node *np = pdev->dev.of_node;
40         struct regmap *regmap;
41         u32 reg;
42         int ret;
43
44         regmap = syscon_node_to_regmap(np);
45         if (IS_ERR(regmap)) {
46                 dev_err(&pdev->dev, "cannot get regmap\n");
47                 return PTR_ERR(regmap);
48         }
49
50         ret = regmap_read(regmap, AP806_SAR_REG, &reg);
51         if (ret) {
52                 dev_err(&pdev->dev, "cannot read from regmap\n");
53                 return ret;
54         }
55
56         freq_mode = reg & AP806_SAR_CLKFREQ_MODE_MASK;
57         switch (freq_mode) {
58         case 0x0 ... 0x5:
59                 cpuclk_freq = 2000;
60                 break;
61         case 0x6 ... 0xB:
62                 cpuclk_freq = 1800;
63                 break;
64         case 0xC ... 0x11:
65                 cpuclk_freq = 1600;
66                 break;
67         case 0x12 ... 0x16:
68                 cpuclk_freq = 1400;
69                 break;
70         case 0x17 ... 0x19:
71                 cpuclk_freq = 1300;
72                 break;
73         default:
74                 dev_err(&pdev->dev, "invalid SAR value\n");
75                 return -EINVAL;
76         }
77
78         /* Convert to hertz */
79         cpuclk_freq *= 1000 * 1000;
80
81         /* CPU clocks depend on the Sample At Reset configuration */
82         of_property_read_string_index(np, "clock-output-names",
83                                       0, &name);
84         ap806_clks[0] = clk_register_fixed_rate(&pdev->dev, name, NULL,
85                                                 0, cpuclk_freq);
86         if (IS_ERR(ap806_clks[0])) {
87                 ret = PTR_ERR(ap806_clks[0]);
88                 goto fail0;
89         }
90
91         of_property_read_string_index(np, "clock-output-names",
92                                       1, &name);
93         ap806_clks[1] = clk_register_fixed_rate(&pdev->dev, name, NULL, 0,
94                                                 cpuclk_freq);
95         if (IS_ERR(ap806_clks[1])) {
96                 ret = PTR_ERR(ap806_clks[1]);
97                 goto fail1;
98         }
99
100         /* Fixed clock is always 1200 Mhz */
101         of_property_read_string_index(np, "clock-output-names",
102                                       2, &fixedclk_name);
103         ap806_clks[2] = clk_register_fixed_rate(&pdev->dev, fixedclk_name, NULL,
104                                                 0, 1200 * 1000 * 1000);
105         if (IS_ERR(ap806_clks[2])) {
106                 ret = PTR_ERR(ap806_clks[2]);
107                 goto fail2;
108         }
109
110         /* MSS Clock is fixed clock divided by 6 */
111         of_property_read_string_index(np, "clock-output-names",
112                                       3, &name);
113         ap806_clks[3] = clk_register_fixed_factor(NULL, name, fixedclk_name,
114                                                   0, 1, 6);
115         if (IS_ERR(ap806_clks[3])) {
116                 ret = PTR_ERR(ap806_clks[3]);
117                 goto fail3;
118         }
119
120         ret = of_clk_add_provider(np, of_clk_src_onecell_get, &ap806_clk_data);
121         if (ret)
122                 goto fail_clk_add;
123
124         return 0;
125
126 fail_clk_add:
127         clk_unregister_fixed_factor(ap806_clks[3]);
128 fail3:
129         clk_unregister_fixed_rate(ap806_clks[2]);
130 fail2:
131         clk_unregister_fixed_rate(ap806_clks[1]);
132 fail1:
133         clk_unregister_fixed_rate(ap806_clks[0]);
134 fail0:
135         return ret;
136 }
137
138 static int ap806_syscon_clk_remove(struct platform_device *pdev)
139 {
140         of_clk_del_provider(pdev->dev.of_node);
141         clk_unregister_fixed_factor(ap806_clks[3]);
142         clk_unregister_fixed_rate(ap806_clks[2]);
143         clk_unregister_fixed_rate(ap806_clks[1]);
144         clk_unregister_fixed_rate(ap806_clks[0]);
145
146         return 0;
147 }
148
149 static const struct of_device_id ap806_syscon_of_match[] = {
150         { .compatible = "marvell,ap806-system-controller", },
151         { }
152 };
153 MODULE_DEVICE_TABLE(of, armada8k_pcie_of_match);
154
155 static struct platform_driver ap806_syscon_driver = {
156         .probe = ap806_syscon_clk_probe,
157         .remove = ap806_syscon_clk_remove,
158         .driver         = {
159                 .name   = "marvell-ap806-system-controller",
160                 .of_match_table = ap806_syscon_of_match,
161         },
162 };
163
164 module_platform_driver(ap806_syscon_driver);
165
166 MODULE_DESCRIPTION("Marvell AP806 System Controller driver");
167 MODULE_AUTHOR("Thomas Petazzoni <thomas.petazzoni@free-electrons.com>");
168 MODULE_LICENSE("GPL");