ap806-system-controller.c (6744B)
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 "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 0x400 22#define AP806_SAR_CLKFREQ_MODE_MASK 0x1f 23 24#define AP806_CLK_NUM 6 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 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_WARN 254 "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);