brintos

brintos / linux-shallow public Read only

0
0
Text · 6.6 KiB · 948bd1e Raw
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, &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