162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Marvell MV98DX3236 SoC clocks
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 Marvell
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Gregory CLEMENT <gregory.clement@free-electrons.com>
862306a36Sopenharmony_ci * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
962306a36Sopenharmony_ci * Andrew Lunn <andrew@lunn.ch>
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/clk-provider.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/of.h>
1762306a36Sopenharmony_ci#include "common.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/*
2162306a36Sopenharmony_ci * For 98DX4251 Sample At Reset the CPU, DDR and Main PLL clocks are all
2262306a36Sopenharmony_ci * defined at the same time
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * SAR1[20:18]   : CPU frequency    DDR frequency   MPLL frequency
2562306a36Sopenharmony_ci *		 0  =  400 MHz	    400 MHz	    800 MHz
2662306a36Sopenharmony_ci *		 2  =  667 MHz	    667 MHz	    2000 MHz
2762306a36Sopenharmony_ci *		 3  =  800 MHz	    800 MHz	    1600 MHz
2862306a36Sopenharmony_ci *		 others reserved.
2962306a36Sopenharmony_ci *
3062306a36Sopenharmony_ci * For 98DX3236 Sample At Reset the CPU, DDR and Main PLL clocks are all
3162306a36Sopenharmony_ci * defined at the same time
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * SAR1[20:18]   : CPU frequency    DDR frequency   MPLL frequency
3462306a36Sopenharmony_ci *		 1  =  667 MHz	    667 MHz	    2000 MHz
3562306a36Sopenharmony_ci *		 2  =  400 MHz	    400 MHz	    400 MHz
3662306a36Sopenharmony_ci *		 3  =  800 MHz	    800 MHz	    800 MHz
3762306a36Sopenharmony_ci *		 5  =  800 MHz	    400 MHz	    800 MHz
3862306a36Sopenharmony_ci *		 others reserved.
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define SAR1_MV98DX3236_CPU_DDR_MPLL_FREQ_OPT		18
4262306a36Sopenharmony_ci#define SAR1_MV98DX3236_CPU_DDR_MPLL_FREQ_OPT_MASK	0x7
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic u32 __init mv98dx3236_get_tclk_freq(void __iomem *sar)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	/* Tclk = 200MHz, no SaR dependency */
4762306a36Sopenharmony_ci	return 200000000;
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic const u32 mv98dx3236_cpu_frequencies[] __initconst = {
5162306a36Sopenharmony_ci	0,
5262306a36Sopenharmony_ci	667000000,
5362306a36Sopenharmony_ci	400000000,
5462306a36Sopenharmony_ci	800000000,
5562306a36Sopenharmony_ci	0,
5662306a36Sopenharmony_ci	800000000,
5762306a36Sopenharmony_ci	0, 0,
5862306a36Sopenharmony_ci};
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic const u32 mv98dx4251_cpu_frequencies[] __initconst = {
6162306a36Sopenharmony_ci	400000000,
6262306a36Sopenharmony_ci	0,
6362306a36Sopenharmony_ci	667000000,
6462306a36Sopenharmony_ci	800000000,
6562306a36Sopenharmony_ci	0, 0, 0, 0,
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic u32 __init mv98dx3236_get_cpu_freq(void __iomem *sar)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	u32 cpu_freq = 0;
7162306a36Sopenharmony_ci	u8 cpu_freq_select = 0;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	cpu_freq_select = ((readl(sar) >> SAR1_MV98DX3236_CPU_DDR_MPLL_FREQ_OPT) &
7462306a36Sopenharmony_ci			   SAR1_MV98DX3236_CPU_DDR_MPLL_FREQ_OPT_MASK);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (of_machine_is_compatible("marvell,armadaxp-98dx4251"))
7762306a36Sopenharmony_ci		cpu_freq = mv98dx4251_cpu_frequencies[cpu_freq_select];
7862306a36Sopenharmony_ci	else if (of_machine_is_compatible("marvell,armadaxp-98dx3236"))
7962306a36Sopenharmony_ci		cpu_freq = mv98dx3236_cpu_frequencies[cpu_freq_select];
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (!cpu_freq)
8262306a36Sopenharmony_ci		pr_err("CPU freq select unsupported %d\n", cpu_freq_select);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	return cpu_freq;
8562306a36Sopenharmony_ci}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cienum {
8862306a36Sopenharmony_ci	MV98DX3236_CPU_TO_DDR,
8962306a36Sopenharmony_ci	MV98DX3236_CPU_TO_MPLL
9062306a36Sopenharmony_ci};
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic const struct coreclk_ratio mv98dx3236_core_ratios[] __initconst = {
9362306a36Sopenharmony_ci	{ .id = MV98DX3236_CPU_TO_DDR, .name = "ddrclk" },
9462306a36Sopenharmony_ci	{ .id = MV98DX3236_CPU_TO_MPLL, .name = "mpll" },
9562306a36Sopenharmony_ci};
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic const int __initconst mv98dx3236_cpu_mpll_ratios[8][2] = {
9862306a36Sopenharmony_ci	{0, 1}, {3, 1}, {1, 1}, {1, 1},
9962306a36Sopenharmony_ci	{0, 1}, {1, 1}, {0, 1}, {0, 1},
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic const int __initconst mv98dx3236_cpu_ddr_ratios[8][2] = {
10362306a36Sopenharmony_ci	{0, 1}, {1, 1}, {1, 1}, {1, 1},
10462306a36Sopenharmony_ci	{0, 1}, {1, 2}, {0, 1}, {0, 1},
10562306a36Sopenharmony_ci};
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic const int __initconst mv98dx4251_cpu_mpll_ratios[8][2] = {
10862306a36Sopenharmony_ci	{2, 1}, {0, 1}, {3, 1}, {2, 1},
10962306a36Sopenharmony_ci	{0, 1}, {0, 1}, {0, 1}, {0, 1},
11062306a36Sopenharmony_ci};
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic const int __initconst mv98dx4251_cpu_ddr_ratios[8][2] = {
11362306a36Sopenharmony_ci	{1, 1}, {0, 1}, {1, 1}, {1, 1},
11462306a36Sopenharmony_ci	{0, 1}, {0, 1}, {0, 1}, {0, 1},
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic void __init mv98dx3236_get_clk_ratio(
11862306a36Sopenharmony_ci	void __iomem *sar, int id, int *mult, int *div)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	u32 opt = ((readl(sar) >> SAR1_MV98DX3236_CPU_DDR_MPLL_FREQ_OPT) &
12162306a36Sopenharmony_ci		SAR1_MV98DX3236_CPU_DDR_MPLL_FREQ_OPT_MASK);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	switch (id) {
12462306a36Sopenharmony_ci	case MV98DX3236_CPU_TO_DDR:
12562306a36Sopenharmony_ci		if (of_machine_is_compatible("marvell,armadaxp-98dx4251")) {
12662306a36Sopenharmony_ci			*mult = mv98dx4251_cpu_ddr_ratios[opt][0];
12762306a36Sopenharmony_ci			*div = mv98dx4251_cpu_ddr_ratios[opt][1];
12862306a36Sopenharmony_ci		} else if (of_machine_is_compatible("marvell,armadaxp-98dx3236")) {
12962306a36Sopenharmony_ci			*mult = mv98dx3236_cpu_ddr_ratios[opt][0];
13062306a36Sopenharmony_ci			*div = mv98dx3236_cpu_ddr_ratios[opt][1];
13162306a36Sopenharmony_ci		}
13262306a36Sopenharmony_ci		break;
13362306a36Sopenharmony_ci	case MV98DX3236_CPU_TO_MPLL:
13462306a36Sopenharmony_ci		if (of_machine_is_compatible("marvell,armadaxp-98dx4251")) {
13562306a36Sopenharmony_ci			*mult = mv98dx4251_cpu_mpll_ratios[opt][0];
13662306a36Sopenharmony_ci			*div = mv98dx4251_cpu_mpll_ratios[opt][1];
13762306a36Sopenharmony_ci		} else if (of_machine_is_compatible("marvell,armadaxp-98dx3236")) {
13862306a36Sopenharmony_ci			*mult = mv98dx3236_cpu_mpll_ratios[opt][0];
13962306a36Sopenharmony_ci			*div = mv98dx3236_cpu_mpll_ratios[opt][1];
14062306a36Sopenharmony_ci		}
14162306a36Sopenharmony_ci		break;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic const struct coreclk_soc_desc mv98dx3236_core_clocks = {
14662306a36Sopenharmony_ci	.get_tclk_freq = mv98dx3236_get_tclk_freq,
14762306a36Sopenharmony_ci	.get_cpu_freq = mv98dx3236_get_cpu_freq,
14862306a36Sopenharmony_ci	.get_clk_ratio = mv98dx3236_get_clk_ratio,
14962306a36Sopenharmony_ci	.ratios = mv98dx3236_core_ratios,
15062306a36Sopenharmony_ci	.num_ratios = ARRAY_SIZE(mv98dx3236_core_ratios),
15162306a36Sopenharmony_ci};
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/*
15562306a36Sopenharmony_ci * Clock Gating Control
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_cistatic const struct clk_gating_soc_desc mv98dx3236_gating_desc[] __initconst = {
15962306a36Sopenharmony_ci	{ "ge1", NULL, 3, 0 },
16062306a36Sopenharmony_ci	{ "ge0", NULL, 4, 0 },
16162306a36Sopenharmony_ci	{ "pex00", NULL, 5, 0 },
16262306a36Sopenharmony_ci	{ "sdio", NULL, 17, 0 },
16362306a36Sopenharmony_ci	{ "usb0", NULL, 18, 0 },
16462306a36Sopenharmony_ci	{ "xor0", NULL, 22, 0 },
16562306a36Sopenharmony_ci	{ }
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistatic void __init mv98dx3236_clk_init(struct device_node *np)
16962306a36Sopenharmony_ci{
17062306a36Sopenharmony_ci	struct device_node *cgnp =
17162306a36Sopenharmony_ci		of_find_compatible_node(NULL, NULL, "marvell,mv98dx3236-gating-clock");
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	mvebu_coreclk_setup(np, &mv98dx3236_core_clocks);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	if (cgnp) {
17662306a36Sopenharmony_ci		mvebu_clk_gating_setup(cgnp, mv98dx3236_gating_desc);
17762306a36Sopenharmony_ci		of_node_put(cgnp);
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ciCLK_OF_DECLARE(mv98dx3236_clk, "marvell,mv98dx3236-core-clock", mv98dx3236_clk_init);
181