18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Marvell Armada AP806 System Controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Marvell 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ap806-system-controller: " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include "armada_ap_cp_helper.h" 148c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 158c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 168c2ecf20Sopenharmony_ci#include <linux/init.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 198c2ecf20Sopenharmony_ci#include <linux/regmap.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define AP806_SAR_REG 0x400 228c2ecf20Sopenharmony_ci#define AP806_SAR_CLKFREQ_MODE_MASK 0x1f 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define AP806_CLK_NUM 6 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_cistatic struct clk *ap806_clks[AP806_CLK_NUM]; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic struct clk_onecell_data ap806_clk_data = { 298c2ecf20Sopenharmony_ci .clks = ap806_clks, 308c2ecf20Sopenharmony_ci .clk_num = AP806_CLK_NUM, 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic int ap806_get_sar_clocks(unsigned int freq_mode, 348c2ecf20Sopenharmony_ci unsigned int *cpuclk_freq, 358c2ecf20Sopenharmony_ci unsigned int *dclk_freq) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci switch (freq_mode) { 388c2ecf20Sopenharmony_ci case 0x0: 398c2ecf20Sopenharmony_ci *cpuclk_freq = 2000; 408c2ecf20Sopenharmony_ci *dclk_freq = 600; 418c2ecf20Sopenharmony_ci break; 428c2ecf20Sopenharmony_ci case 0x1: 438c2ecf20Sopenharmony_ci *cpuclk_freq = 2000; 448c2ecf20Sopenharmony_ci *dclk_freq = 525; 458c2ecf20Sopenharmony_ci break; 468c2ecf20Sopenharmony_ci case 0x6: 478c2ecf20Sopenharmony_ci *cpuclk_freq = 1800; 488c2ecf20Sopenharmony_ci *dclk_freq = 600; 498c2ecf20Sopenharmony_ci break; 508c2ecf20Sopenharmony_ci case 0x7: 518c2ecf20Sopenharmony_ci *cpuclk_freq = 1800; 528c2ecf20Sopenharmony_ci *dclk_freq = 525; 538c2ecf20Sopenharmony_ci break; 548c2ecf20Sopenharmony_ci case 0x4: 558c2ecf20Sopenharmony_ci *cpuclk_freq = 1600; 568c2ecf20Sopenharmony_ci *dclk_freq = 400; 578c2ecf20Sopenharmony_ci break; 588c2ecf20Sopenharmony_ci case 0xB: 598c2ecf20Sopenharmony_ci *cpuclk_freq = 1600; 608c2ecf20Sopenharmony_ci *dclk_freq = 450; 618c2ecf20Sopenharmony_ci break; 628c2ecf20Sopenharmony_ci case 0xD: 638c2ecf20Sopenharmony_ci *cpuclk_freq = 1600; 648c2ecf20Sopenharmony_ci *dclk_freq = 525; 658c2ecf20Sopenharmony_ci break; 668c2ecf20Sopenharmony_ci case 0x1a: 678c2ecf20Sopenharmony_ci *cpuclk_freq = 1400; 688c2ecf20Sopenharmony_ci *dclk_freq = 400; 698c2ecf20Sopenharmony_ci break; 708c2ecf20Sopenharmony_ci case 0x14: 718c2ecf20Sopenharmony_ci *cpuclk_freq = 1300; 728c2ecf20Sopenharmony_ci *dclk_freq = 400; 738c2ecf20Sopenharmony_ci break; 748c2ecf20Sopenharmony_ci case 0x17: 758c2ecf20Sopenharmony_ci *cpuclk_freq = 1300; 768c2ecf20Sopenharmony_ci *dclk_freq = 325; 778c2ecf20Sopenharmony_ci break; 788c2ecf20Sopenharmony_ci case 0x19: 798c2ecf20Sopenharmony_ci *cpuclk_freq = 1200; 808c2ecf20Sopenharmony_ci *dclk_freq = 400; 818c2ecf20Sopenharmony_ci break; 828c2ecf20Sopenharmony_ci case 0x13: 838c2ecf20Sopenharmony_ci *cpuclk_freq = 1000; 848c2ecf20Sopenharmony_ci *dclk_freq = 325; 858c2ecf20Sopenharmony_ci break; 868c2ecf20Sopenharmony_ci case 0x1d: 878c2ecf20Sopenharmony_ci *cpuclk_freq = 1000; 888c2ecf20Sopenharmony_ci *dclk_freq = 400; 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci case 0x1c: 918c2ecf20Sopenharmony_ci *cpuclk_freq = 800; 928c2ecf20Sopenharmony_ci *dclk_freq = 400; 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci case 0x1b: 958c2ecf20Sopenharmony_ci *cpuclk_freq = 600; 968c2ecf20Sopenharmony_ci *dclk_freq = 400; 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci default: 998c2ecf20Sopenharmony_ci return -EINVAL; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic int ap807_get_sar_clocks(unsigned int freq_mode, 1068c2ecf20Sopenharmony_ci unsigned int *cpuclk_freq, 1078c2ecf20Sopenharmony_ci unsigned int *dclk_freq) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci switch (freq_mode) { 1108c2ecf20Sopenharmony_ci case 0x0: 1118c2ecf20Sopenharmony_ci *cpuclk_freq = 2000; 1128c2ecf20Sopenharmony_ci *dclk_freq = 1200; 1138c2ecf20Sopenharmony_ci break; 1148c2ecf20Sopenharmony_ci case 0x6: 1158c2ecf20Sopenharmony_ci *cpuclk_freq = 2200; 1168c2ecf20Sopenharmony_ci *dclk_freq = 1200; 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci case 0xD: 1198c2ecf20Sopenharmony_ci *cpuclk_freq = 1600; 1208c2ecf20Sopenharmony_ci *dclk_freq = 1200; 1218c2ecf20Sopenharmony_ci break; 1228c2ecf20Sopenharmony_ci default: 1238c2ecf20Sopenharmony_ci return -EINVAL; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return 0; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int ap806_syscon_common_probe(struct platform_device *pdev, 1308c2ecf20Sopenharmony_ci struct device_node *syscon_node) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci unsigned int freq_mode, cpuclk_freq, dclk_freq; 1338c2ecf20Sopenharmony_ci const char *name, *fixedclk_name; 1348c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1358c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 1368c2ecf20Sopenharmony_ci struct regmap *regmap; 1378c2ecf20Sopenharmony_ci u32 reg; 1388c2ecf20Sopenharmony_ci int ret; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci regmap = syscon_node_to_regmap(syscon_node); 1418c2ecf20Sopenharmony_ci if (IS_ERR(regmap)) { 1428c2ecf20Sopenharmony_ci dev_err(dev, "cannot get regmap\n"); 1438c2ecf20Sopenharmony_ci return PTR_ERR(regmap); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci ret = regmap_read(regmap, AP806_SAR_REG, ®); 1478c2ecf20Sopenharmony_ci if (ret) { 1488c2ecf20Sopenharmony_ci dev_err(dev, "cannot read from regmap\n"); 1498c2ecf20Sopenharmony_ci return ret; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci freq_mode = reg & AP806_SAR_CLKFREQ_MODE_MASK; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (of_device_is_compatible(pdev->dev.of_node, 1558c2ecf20Sopenharmony_ci "marvell,ap806-clock")) { 1568c2ecf20Sopenharmony_ci ret = ap806_get_sar_clocks(freq_mode, &cpuclk_freq, &dclk_freq); 1578c2ecf20Sopenharmony_ci } else if (of_device_is_compatible(pdev->dev.of_node, 1588c2ecf20Sopenharmony_ci "marvell,ap807-clock")) { 1598c2ecf20Sopenharmony_ci ret = ap807_get_sar_clocks(freq_mode, &cpuclk_freq, &dclk_freq); 1608c2ecf20Sopenharmony_ci } else { 1618c2ecf20Sopenharmony_ci dev_err(dev, "compatible not supported\n"); 1628c2ecf20Sopenharmony_ci return -EINVAL; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci if (ret) { 1668c2ecf20Sopenharmony_ci dev_err(dev, "invalid Sample at Reset value\n"); 1678c2ecf20Sopenharmony_ci return ret; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* Convert to hertz */ 1718c2ecf20Sopenharmony_ci cpuclk_freq *= 1000 * 1000; 1728c2ecf20Sopenharmony_ci dclk_freq *= 1000 * 1000; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* CPU clocks depend on the Sample At Reset configuration */ 1758c2ecf20Sopenharmony_ci name = ap_cp_unique_name(dev, syscon_node, "pll-cluster-0"); 1768c2ecf20Sopenharmony_ci ap806_clks[0] = clk_register_fixed_rate(dev, name, NULL, 1778c2ecf20Sopenharmony_ci 0, cpuclk_freq); 1788c2ecf20Sopenharmony_ci if (IS_ERR(ap806_clks[0])) { 1798c2ecf20Sopenharmony_ci ret = PTR_ERR(ap806_clks[0]); 1808c2ecf20Sopenharmony_ci goto fail0; 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci name = ap_cp_unique_name(dev, syscon_node, "pll-cluster-1"); 1848c2ecf20Sopenharmony_ci ap806_clks[1] = clk_register_fixed_rate(dev, name, NULL, 0, 1858c2ecf20Sopenharmony_ci cpuclk_freq); 1868c2ecf20Sopenharmony_ci if (IS_ERR(ap806_clks[1])) { 1878c2ecf20Sopenharmony_ci ret = PTR_ERR(ap806_clks[1]); 1888c2ecf20Sopenharmony_ci goto fail1; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* Fixed clock is always 1200 Mhz */ 1928c2ecf20Sopenharmony_ci fixedclk_name = ap_cp_unique_name(dev, syscon_node, "fixed"); 1938c2ecf20Sopenharmony_ci ap806_clks[2] = clk_register_fixed_rate(dev, fixedclk_name, NULL, 1948c2ecf20Sopenharmony_ci 0, 1200 * 1000 * 1000); 1958c2ecf20Sopenharmony_ci if (IS_ERR(ap806_clks[2])) { 1968c2ecf20Sopenharmony_ci ret = PTR_ERR(ap806_clks[2]); 1978c2ecf20Sopenharmony_ci goto fail2; 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci /* MSS Clock is fixed clock divided by 6 */ 2018c2ecf20Sopenharmony_ci name = ap_cp_unique_name(dev, syscon_node, "mss"); 2028c2ecf20Sopenharmony_ci ap806_clks[3] = clk_register_fixed_factor(NULL, name, fixedclk_name, 2038c2ecf20Sopenharmony_ci 0, 1, 6); 2048c2ecf20Sopenharmony_ci if (IS_ERR(ap806_clks[3])) { 2058c2ecf20Sopenharmony_ci ret = PTR_ERR(ap806_clks[3]); 2068c2ecf20Sopenharmony_ci goto fail3; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* SDIO(/eMMC) Clock is fixed clock divided by 3 */ 2108c2ecf20Sopenharmony_ci name = ap_cp_unique_name(dev, syscon_node, "sdio"); 2118c2ecf20Sopenharmony_ci ap806_clks[4] = clk_register_fixed_factor(NULL, name, 2128c2ecf20Sopenharmony_ci fixedclk_name, 2138c2ecf20Sopenharmony_ci 0, 1, 3); 2148c2ecf20Sopenharmony_ci if (IS_ERR(ap806_clks[4])) { 2158c2ecf20Sopenharmony_ci ret = PTR_ERR(ap806_clks[4]); 2168c2ecf20Sopenharmony_ci goto fail4; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci /* AP-DCLK(HCLK) Clock is DDR clock divided by 2 */ 2208c2ecf20Sopenharmony_ci name = ap_cp_unique_name(dev, syscon_node, "ap-dclk"); 2218c2ecf20Sopenharmony_ci ap806_clks[5] = clk_register_fixed_rate(dev, name, NULL, 0, dclk_freq); 2228c2ecf20Sopenharmony_ci if (IS_ERR(ap806_clks[5])) { 2238c2ecf20Sopenharmony_ci ret = PTR_ERR(ap806_clks[5]); 2248c2ecf20Sopenharmony_ci goto fail5; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci ret = of_clk_add_provider(np, of_clk_src_onecell_get, &ap806_clk_data); 2288c2ecf20Sopenharmony_ci if (ret) 2298c2ecf20Sopenharmony_ci goto fail_clk_add; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return 0; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cifail_clk_add: 2348c2ecf20Sopenharmony_ci clk_unregister_fixed_factor(ap806_clks[5]); 2358c2ecf20Sopenharmony_cifail5: 2368c2ecf20Sopenharmony_ci clk_unregister_fixed_factor(ap806_clks[4]); 2378c2ecf20Sopenharmony_cifail4: 2388c2ecf20Sopenharmony_ci clk_unregister_fixed_factor(ap806_clks[3]); 2398c2ecf20Sopenharmony_cifail3: 2408c2ecf20Sopenharmony_ci clk_unregister_fixed_rate(ap806_clks[2]); 2418c2ecf20Sopenharmony_cifail2: 2428c2ecf20Sopenharmony_ci clk_unregister_fixed_rate(ap806_clks[1]); 2438c2ecf20Sopenharmony_cifail1: 2448c2ecf20Sopenharmony_ci clk_unregister_fixed_rate(ap806_clks[0]); 2458c2ecf20Sopenharmony_cifail0: 2468c2ecf20Sopenharmony_ci return ret; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int ap806_syscon_legacy_probe(struct platform_device *pdev) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, FW_WARN "Using legacy device tree binding\n"); 2528c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, FW_WARN "Update your device tree:\n"); 2538c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, FW_WARN 2548c2ecf20Sopenharmony_ci "This binding won't be supported in future kernel\n"); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return ap806_syscon_common_probe(pdev, pdev->dev.of_node); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic int ap806_clock_probe(struct platform_device *pdev) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci return ap806_syscon_common_probe(pdev, pdev->dev.of_node->parent); 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic const struct of_device_id ap806_syscon_legacy_of_match[] = { 2668c2ecf20Sopenharmony_ci { .compatible = "marvell,ap806-system-controller", }, 2678c2ecf20Sopenharmony_ci { } 2688c2ecf20Sopenharmony_ci}; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic struct platform_driver ap806_syscon_legacy_driver = { 2718c2ecf20Sopenharmony_ci .probe = ap806_syscon_legacy_probe, 2728c2ecf20Sopenharmony_ci .driver = { 2738c2ecf20Sopenharmony_ci .name = "marvell-ap806-system-controller", 2748c2ecf20Sopenharmony_ci .of_match_table = ap806_syscon_legacy_of_match, 2758c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 2768c2ecf20Sopenharmony_ci }, 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_cibuiltin_platform_driver(ap806_syscon_legacy_driver); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic const struct of_device_id ap806_clock_of_match[] = { 2818c2ecf20Sopenharmony_ci { .compatible = "marvell,ap806-clock", }, 2828c2ecf20Sopenharmony_ci { .compatible = "marvell,ap807-clock", }, 2838c2ecf20Sopenharmony_ci { } 2848c2ecf20Sopenharmony_ci}; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic struct platform_driver ap806_clock_driver = { 2878c2ecf20Sopenharmony_ci .probe = ap806_clock_probe, 2888c2ecf20Sopenharmony_ci .driver = { 2898c2ecf20Sopenharmony_ci .name = "marvell-ap806-clock", 2908c2ecf20Sopenharmony_ci .of_match_table = ap806_clock_of_match, 2918c2ecf20Sopenharmony_ci .suppress_bind_attrs = true, 2928c2ecf20Sopenharmony_ci }, 2938c2ecf20Sopenharmony_ci}; 2948c2ecf20Sopenharmony_cibuiltin_platform_driver(ap806_clock_driver); 295