162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Marvell Armada AP806 System Controller 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2016 Marvell 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Thomas Petazzoni <thomas.petazzoni@free-electrons.com> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#define pr_fmt(fmt) "ap806-system-controller: " fmt 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "armada_ap_cp_helper.h" 1462306a36Sopenharmony_ci#include <linux/clk-provider.h> 1562306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1662306a36Sopenharmony_ci#include <linux/init.h> 1762306a36Sopenharmony_ci#include <linux/of.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci#include <linux/regmap.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define AP806_SAR_REG 0x400 2262306a36Sopenharmony_ci#define AP806_SAR_CLKFREQ_MODE_MASK 0x1f 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define AP806_CLK_NUM 6 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic struct clk *ap806_clks[AP806_CLK_NUM]; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistatic struct clk_onecell_data ap806_clk_data = { 2962306a36Sopenharmony_ci .clks = ap806_clks, 3062306a36Sopenharmony_ci .clk_num = AP806_CLK_NUM, 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic int ap806_get_sar_clocks(unsigned int freq_mode, 3462306a36Sopenharmony_ci unsigned int *cpuclk_freq, 3562306a36Sopenharmony_ci unsigned int *dclk_freq) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci switch (freq_mode) { 3862306a36Sopenharmony_ci case 0x0: 3962306a36Sopenharmony_ci *cpuclk_freq = 2000; 4062306a36Sopenharmony_ci *dclk_freq = 600; 4162306a36Sopenharmony_ci break; 4262306a36Sopenharmony_ci case 0x1: 4362306a36Sopenharmony_ci *cpuclk_freq = 2000; 4462306a36Sopenharmony_ci *dclk_freq = 525; 4562306a36Sopenharmony_ci break; 4662306a36Sopenharmony_ci case 0x6: 4762306a36Sopenharmony_ci *cpuclk_freq = 1800; 4862306a36Sopenharmony_ci *dclk_freq = 600; 4962306a36Sopenharmony_ci break; 5062306a36Sopenharmony_ci case 0x7: 5162306a36Sopenharmony_ci *cpuclk_freq = 1800; 5262306a36Sopenharmony_ci *dclk_freq = 525; 5362306a36Sopenharmony_ci break; 5462306a36Sopenharmony_ci case 0x4: 5562306a36Sopenharmony_ci *cpuclk_freq = 1600; 5662306a36Sopenharmony_ci *dclk_freq = 400; 5762306a36Sopenharmony_ci break; 5862306a36Sopenharmony_ci case 0xB: 5962306a36Sopenharmony_ci *cpuclk_freq = 1600; 6062306a36Sopenharmony_ci *dclk_freq = 450; 6162306a36Sopenharmony_ci break; 6262306a36Sopenharmony_ci case 0xD: 6362306a36Sopenharmony_ci *cpuclk_freq = 1600; 6462306a36Sopenharmony_ci *dclk_freq = 525; 6562306a36Sopenharmony_ci break; 6662306a36Sopenharmony_ci case 0x1a: 6762306a36Sopenharmony_ci *cpuclk_freq = 1400; 6862306a36Sopenharmony_ci *dclk_freq = 400; 6962306a36Sopenharmony_ci break; 7062306a36Sopenharmony_ci case 0x14: 7162306a36Sopenharmony_ci *cpuclk_freq = 1300; 7262306a36Sopenharmony_ci *dclk_freq = 400; 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci case 0x17: 7562306a36Sopenharmony_ci *cpuclk_freq = 1300; 7662306a36Sopenharmony_ci *dclk_freq = 325; 7762306a36Sopenharmony_ci break; 7862306a36Sopenharmony_ci case 0x19: 7962306a36Sopenharmony_ci *cpuclk_freq = 1200; 8062306a36Sopenharmony_ci *dclk_freq = 400; 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci case 0x13: 8362306a36Sopenharmony_ci *cpuclk_freq = 1000; 8462306a36Sopenharmony_ci *dclk_freq = 325; 8562306a36Sopenharmony_ci break; 8662306a36Sopenharmony_ci case 0x1d: 8762306a36Sopenharmony_ci *cpuclk_freq = 1000; 8862306a36Sopenharmony_ci *dclk_freq = 400; 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci case 0x1c: 9162306a36Sopenharmony_ci *cpuclk_freq = 800; 9262306a36Sopenharmony_ci *dclk_freq = 400; 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci case 0x1b: 9562306a36Sopenharmony_ci *cpuclk_freq = 600; 9662306a36Sopenharmony_ci *dclk_freq = 400; 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci default: 9962306a36Sopenharmony_ci return -EINVAL; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic int ap807_get_sar_clocks(unsigned int freq_mode, 10662306a36Sopenharmony_ci unsigned int *cpuclk_freq, 10762306a36Sopenharmony_ci unsigned int *dclk_freq) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci switch (freq_mode) { 11062306a36Sopenharmony_ci case 0x0: 11162306a36Sopenharmony_ci *cpuclk_freq = 2000; 11262306a36Sopenharmony_ci *dclk_freq = 1200; 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci case 0x6: 11562306a36Sopenharmony_ci *cpuclk_freq = 2200; 11662306a36Sopenharmony_ci *dclk_freq = 1200; 11762306a36Sopenharmony_ci break; 11862306a36Sopenharmony_ci case 0xD: 11962306a36Sopenharmony_ci *cpuclk_freq = 1600; 12062306a36Sopenharmony_ci *dclk_freq = 1200; 12162306a36Sopenharmony_ci break; 12262306a36Sopenharmony_ci default: 12362306a36Sopenharmony_ci return -EINVAL; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci return 0; 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int ap806_syscon_common_probe(struct platform_device *pdev, 13062306a36Sopenharmony_ci struct device_node *syscon_node) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci unsigned int freq_mode, cpuclk_freq, dclk_freq; 13362306a36Sopenharmony_ci const char *name, *fixedclk_name; 13462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 13562306a36Sopenharmony_ci struct device_node *np = dev->of_node; 13662306a36Sopenharmony_ci struct regmap *regmap; 13762306a36Sopenharmony_ci u32 reg; 13862306a36Sopenharmony_ci int ret; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci regmap = syscon_node_to_regmap(syscon_node); 14162306a36Sopenharmony_ci if (IS_ERR(regmap)) { 14262306a36Sopenharmony_ci dev_err(dev, "cannot get regmap\n"); 14362306a36Sopenharmony_ci return PTR_ERR(regmap); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci ret = regmap_read(regmap, AP806_SAR_REG, ®); 14762306a36Sopenharmony_ci if (ret) { 14862306a36Sopenharmony_ci dev_err(dev, "cannot read from regmap\n"); 14962306a36Sopenharmony_ci return ret; 15062306a36Sopenharmony_ci } 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci freq_mode = reg & AP806_SAR_CLKFREQ_MODE_MASK; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci if (of_device_is_compatible(pdev->dev.of_node, 15562306a36Sopenharmony_ci "marvell,ap806-clock")) { 15662306a36Sopenharmony_ci ret = ap806_get_sar_clocks(freq_mode, &cpuclk_freq, &dclk_freq); 15762306a36Sopenharmony_ci } else if (of_device_is_compatible(pdev->dev.of_node, 15862306a36Sopenharmony_ci "marvell,ap807-clock")) { 15962306a36Sopenharmony_ci ret = ap807_get_sar_clocks(freq_mode, &cpuclk_freq, &dclk_freq); 16062306a36Sopenharmony_ci } else { 16162306a36Sopenharmony_ci dev_err(dev, "compatible not supported\n"); 16262306a36Sopenharmony_ci return -EINVAL; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci if (ret) { 16662306a36Sopenharmony_ci dev_err(dev, "invalid Sample at Reset value\n"); 16762306a36Sopenharmony_ci return ret; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* Convert to hertz */ 17162306a36Sopenharmony_ci cpuclk_freq *= 1000 * 1000; 17262306a36Sopenharmony_ci dclk_freq *= 1000 * 1000; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* CPU clocks depend on the Sample At Reset configuration */ 17562306a36Sopenharmony_ci name = ap_cp_unique_name(dev, syscon_node, "pll-cluster-0"); 17662306a36Sopenharmony_ci ap806_clks[0] = clk_register_fixed_rate(dev, name, NULL, 17762306a36Sopenharmony_ci 0, cpuclk_freq); 17862306a36Sopenharmony_ci if (IS_ERR(ap806_clks[0])) { 17962306a36Sopenharmony_ci ret = PTR_ERR(ap806_clks[0]); 18062306a36Sopenharmony_ci goto fail0; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci name = ap_cp_unique_name(dev, syscon_node, "pll-cluster-1"); 18462306a36Sopenharmony_ci ap806_clks[1] = clk_register_fixed_rate(dev, name, NULL, 0, 18562306a36Sopenharmony_ci cpuclk_freq); 18662306a36Sopenharmony_ci if (IS_ERR(ap806_clks[1])) { 18762306a36Sopenharmony_ci ret = PTR_ERR(ap806_clks[1]); 18862306a36Sopenharmony_ci goto fail1; 18962306a36Sopenharmony_ci } 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Fixed clock is always 1200 Mhz */ 19262306a36Sopenharmony_ci fixedclk_name = ap_cp_unique_name(dev, syscon_node, "fixed"); 19362306a36Sopenharmony_ci ap806_clks[2] = clk_register_fixed_rate(dev, fixedclk_name, NULL, 19462306a36Sopenharmony_ci 0, 1200 * 1000 * 1000); 19562306a36Sopenharmony_ci if (IS_ERR(ap806_clks[2])) { 19662306a36Sopenharmony_ci ret = PTR_ERR(ap806_clks[2]); 19762306a36Sopenharmony_ci goto fail2; 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* MSS Clock is fixed clock divided by 6 */ 20162306a36Sopenharmony_ci name = ap_cp_unique_name(dev, syscon_node, "mss"); 20262306a36Sopenharmony_ci ap806_clks[3] = clk_register_fixed_factor(NULL, name, fixedclk_name, 20362306a36Sopenharmony_ci 0, 1, 6); 20462306a36Sopenharmony_ci if (IS_ERR(ap806_clks[3])) { 20562306a36Sopenharmony_ci ret = PTR_ERR(ap806_clks[3]); 20662306a36Sopenharmony_ci goto fail3; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* SDIO(/eMMC) Clock is fixed clock divided by 3 */ 21062306a36Sopenharmony_ci name = ap_cp_unique_name(dev, syscon_node, "sdio"); 21162306a36Sopenharmony_ci ap806_clks[4] = clk_register_fixed_factor(NULL, name, 21262306a36Sopenharmony_ci fixedclk_name, 21362306a36Sopenharmony_ci 0, 1, 3); 21462306a36Sopenharmony_ci if (IS_ERR(ap806_clks[4])) { 21562306a36Sopenharmony_ci ret = PTR_ERR(ap806_clks[4]); 21662306a36Sopenharmony_ci goto fail4; 21762306a36Sopenharmony_ci } 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* AP-DCLK(HCLK) Clock is DDR clock divided by 2 */ 22062306a36Sopenharmony_ci name = ap_cp_unique_name(dev, syscon_node, "ap-dclk"); 22162306a36Sopenharmony_ci ap806_clks[5] = clk_register_fixed_rate(dev, name, NULL, 0, dclk_freq); 22262306a36Sopenharmony_ci if (IS_ERR(ap806_clks[5])) { 22362306a36Sopenharmony_ci ret = PTR_ERR(ap806_clks[5]); 22462306a36Sopenharmony_ci goto fail5; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ret = of_clk_add_provider(np, of_clk_src_onecell_get, &ap806_clk_data); 22862306a36Sopenharmony_ci if (ret) 22962306a36Sopenharmony_ci goto fail_clk_add; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cifail_clk_add: 23462306a36Sopenharmony_ci clk_unregister_fixed_factor(ap806_clks[5]); 23562306a36Sopenharmony_cifail5: 23662306a36Sopenharmony_ci clk_unregister_fixed_factor(ap806_clks[4]); 23762306a36Sopenharmony_cifail4: 23862306a36Sopenharmony_ci clk_unregister_fixed_factor(ap806_clks[3]); 23962306a36Sopenharmony_cifail3: 24062306a36Sopenharmony_ci clk_unregister_fixed_rate(ap806_clks[2]); 24162306a36Sopenharmony_cifail2: 24262306a36Sopenharmony_ci clk_unregister_fixed_rate(ap806_clks[1]); 24362306a36Sopenharmony_cifail1: 24462306a36Sopenharmony_ci clk_unregister_fixed_rate(ap806_clks[0]); 24562306a36Sopenharmony_cifail0: 24662306a36Sopenharmony_ci return ret; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int ap806_syscon_legacy_probe(struct platform_device *pdev) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci dev_warn(&pdev->dev, FW_WARN "Using legacy device tree binding\n"); 25262306a36Sopenharmony_ci dev_warn(&pdev->dev, FW_WARN "Update your device tree:\n"); 25362306a36Sopenharmony_ci dev_warn(&pdev->dev, FW_WARN 25462306a36Sopenharmony_ci "This binding won't be supported in future kernel\n"); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return ap806_syscon_common_probe(pdev, pdev->dev.of_node); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic int ap806_clock_probe(struct platform_device *pdev) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci return ap806_syscon_common_probe(pdev, pdev->dev.of_node->parent); 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistatic const struct of_device_id ap806_syscon_legacy_of_match[] = { 26662306a36Sopenharmony_ci { .compatible = "marvell,ap806-system-controller", }, 26762306a36Sopenharmony_ci { } 26862306a36Sopenharmony_ci}; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic struct platform_driver ap806_syscon_legacy_driver = { 27162306a36Sopenharmony_ci .probe = ap806_syscon_legacy_probe, 27262306a36Sopenharmony_ci .driver = { 27362306a36Sopenharmony_ci .name = "marvell-ap806-system-controller", 27462306a36Sopenharmony_ci .of_match_table = ap806_syscon_legacy_of_match, 27562306a36Sopenharmony_ci .suppress_bind_attrs = true, 27662306a36Sopenharmony_ci }, 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_cibuiltin_platform_driver(ap806_syscon_legacy_driver); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic const struct of_device_id ap806_clock_of_match[] = { 28162306a36Sopenharmony_ci { .compatible = "marvell,ap806-clock", }, 28262306a36Sopenharmony_ci { .compatible = "marvell,ap807-clock", }, 28362306a36Sopenharmony_ci { } 28462306a36Sopenharmony_ci}; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic struct platform_driver ap806_clock_driver = { 28762306a36Sopenharmony_ci .probe = ap806_clock_probe, 28862306a36Sopenharmony_ci .driver = { 28962306a36Sopenharmony_ci .name = "marvell-ap806-clock", 29062306a36Sopenharmony_ci .of_match_table = ap806_clock_of_match, 29162306a36Sopenharmony_ci .suppress_bind_attrs = true, 29262306a36Sopenharmony_ci }, 29362306a36Sopenharmony_ci}; 29462306a36Sopenharmony_cibuiltin_platform_driver(ap806_clock_driver); 295