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, &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