295 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Marvell Armada AP806 System Controller4 *5 * Copyright (C) 2016 Marvell6 *7 * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>8 *9 */10 11#define pr_fmt(fmt) "ap806-system-controller: " fmt12 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 0x40022#define AP806_SAR_CLKFREQ_MODE_MASK 0x1f23 24#define AP806_CLK_NUM 625 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, ®);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_WARN254 "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