diff options
-rw-r--r-- | drivers/clk/mvebu/Kconfig | 3 | ||||
-rw-r--r-- | drivers/clk/mvebu/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/mvebu/ap806-system-controller.c | 168 |
3 files changed, 172 insertions, 0 deletions
diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig index eaee8f099c8c..bf7ae003f20f 100644 --- a/drivers/clk/mvebu/Kconfig +++ b/drivers/clk/mvebu/Kconfig | |||
@@ -29,6 +29,9 @@ config ARMADA_XP_CLK | |||
29 | select MVEBU_CLK_COMMON | 29 | select MVEBU_CLK_COMMON |
30 | select MVEBU_CLK_CPU | 30 | select MVEBU_CLK_CPU |
31 | 31 | ||
32 | config ARMADA_AP806_SYSCON | ||
33 | bool | ||
34 | |||
32 | config DOVE_CLK | 35 | config DOVE_CLK |
33 | bool | 36 | bool |
34 | select MVEBU_CLK_COMMON | 37 | select MVEBU_CLK_COMMON |
diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile index 8866115486f7..f4aa481678dd 100644 --- a/drivers/clk/mvebu/Makefile +++ b/drivers/clk/mvebu/Makefile | |||
@@ -7,6 +7,7 @@ obj-$(CONFIG_ARMADA_375_CLK) += armada-375.o | |||
7 | obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o | 7 | obj-$(CONFIG_ARMADA_38X_CLK) += armada-38x.o |
8 | obj-$(CONFIG_ARMADA_39X_CLK) += armada-39x.o | 8 | obj-$(CONFIG_ARMADA_39X_CLK) += armada-39x.o |
9 | obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o | 9 | obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o |
10 | obj-$(CONFIG_ARMADA_AP806_SYSCON) += ap806-system-controller.o | ||
10 | obj-$(CONFIG_DOVE_CLK) += dove.o dove-divider.o | 11 | obj-$(CONFIG_DOVE_CLK) += dove.o dove-divider.o |
11 | obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o | 12 | obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o |
12 | obj-$(CONFIG_ORION_CLK) += orion.o | 13 | obj-$(CONFIG_ORION_CLK) += orion.o |
diff --git a/drivers/clk/mvebu/ap806-system-controller.c b/drivers/clk/mvebu/ap806-system-controller.c new file mode 100644 index 000000000000..02023baf86c9 --- /dev/null +++ b/drivers/clk/mvebu/ap806-system-controller.c | |||
@@ -0,0 +1,168 @@ | |||
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, ®); | ||
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"); | ||