18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Altera Corporation. All rights reserved
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#include <linux/slab.h>
68c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
78c2ecf20Sopenharmony_ci#include <linux/io.h>
88c2ecf20Sopenharmony_ci#include <linux/of.h>
98c2ecf20Sopenharmony_ci#include <linux/of_address.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "clk.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/* Clock Manager offsets */
148c2ecf20Sopenharmony_ci#define CLK_MGR_PLL_CLK_SRC_SHIFT	8
158c2ecf20Sopenharmony_ci#define CLK_MGR_PLL_CLK_SRC_MASK	0x3
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci/* Clock bypass bits */
188c2ecf20Sopenharmony_ci#define SOCFPGA_PLL_BG_PWRDWN		0
198c2ecf20Sopenharmony_ci#define SOCFPGA_PLL_PWR_DOWN		1
208c2ecf20Sopenharmony_ci#define SOCFPGA_PLL_EXT_ENA		2
218c2ecf20Sopenharmony_ci#define SOCFPGA_PLL_DIVF_MASK		0x00001FFF
228c2ecf20Sopenharmony_ci#define SOCFPGA_PLL_DIVF_SHIFT	0
238c2ecf20Sopenharmony_ci#define SOCFPGA_PLL_DIVQ_MASK		0x003F0000
248c2ecf20Sopenharmony_ci#define SOCFPGA_PLL_DIVQ_SHIFT	16
258c2ecf20Sopenharmony_ci#define SOCFGPA_MAX_PARENTS	5
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define SOCFPGA_MAIN_PLL_CLK		"main_pll"
288c2ecf20Sopenharmony_ci#define SOCFPGA_PERIP_PLL_CLK		"periph_pll"
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define to_socfpga_clk(p) container_of(p, struct socfpga_pll, hw.hw)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_civoid __iomem *clk_mgr_a10_base_addr;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic unsigned long clk_pll_recalc_rate(struct clk_hw *hwclk,
358c2ecf20Sopenharmony_ci					 unsigned long parent_rate)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
388c2ecf20Sopenharmony_ci	unsigned long divf, divq, reg;
398c2ecf20Sopenharmony_ci	unsigned long long vco_freq;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/* read VCO1 reg for numerator and denominator */
428c2ecf20Sopenharmony_ci	reg = readl(socfpgaclk->hw.reg + 0x4);
438c2ecf20Sopenharmony_ci	divf = (reg & SOCFPGA_PLL_DIVF_MASK) >> SOCFPGA_PLL_DIVF_SHIFT;
448c2ecf20Sopenharmony_ci	divq = (reg & SOCFPGA_PLL_DIVQ_MASK) >> SOCFPGA_PLL_DIVQ_SHIFT;
458c2ecf20Sopenharmony_ci	vco_freq = (unsigned long long)parent_rate * (divf + 1);
468c2ecf20Sopenharmony_ci	do_div(vco_freq, (1 + divq));
478c2ecf20Sopenharmony_ci	return (unsigned long)vco_freq;
488c2ecf20Sopenharmony_ci}
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic u8 clk_pll_get_parent(struct clk_hw *hwclk)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct socfpga_pll *socfpgaclk = to_socfpga_clk(hwclk);
538c2ecf20Sopenharmony_ci	u32 pll_src;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	pll_src = readl(socfpgaclk->hw.reg);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return (pll_src >> CLK_MGR_PLL_CLK_SRC_SHIFT) &
588c2ecf20Sopenharmony_ci		CLK_MGR_PLL_CLK_SRC_MASK;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_cistatic const struct clk_ops clk_pll_ops = {
628c2ecf20Sopenharmony_ci	.recalc_rate = clk_pll_recalc_rate,
638c2ecf20Sopenharmony_ci	.get_parent = clk_pll_get_parent,
648c2ecf20Sopenharmony_ci};
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic struct clk * __init __socfpga_pll_init(struct device_node *node,
678c2ecf20Sopenharmony_ci	const struct clk_ops *ops)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	u32 reg;
708c2ecf20Sopenharmony_ci	struct clk *clk;
718c2ecf20Sopenharmony_ci	struct socfpga_pll *pll_clk;
728c2ecf20Sopenharmony_ci	const char *clk_name = node->name;
738c2ecf20Sopenharmony_ci	const char *parent_name[SOCFGPA_MAX_PARENTS];
748c2ecf20Sopenharmony_ci	struct clk_init_data init;
758c2ecf20Sopenharmony_ci	struct device_node *clkmgr_np;
768c2ecf20Sopenharmony_ci	int rc;
778c2ecf20Sopenharmony_ci	int i = 0;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	of_property_read_u32(node, "reg", &reg);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
828c2ecf20Sopenharmony_ci	if (WARN_ON(!pll_clk))
838c2ecf20Sopenharmony_ci		return NULL;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	clkmgr_np = of_find_compatible_node(NULL, NULL, "altr,clk-mgr");
868c2ecf20Sopenharmony_ci	clk_mgr_a10_base_addr = of_iomap(clkmgr_np, 0);
878c2ecf20Sopenharmony_ci	of_node_put(clkmgr_np);
888c2ecf20Sopenharmony_ci	BUG_ON(!clk_mgr_a10_base_addr);
898c2ecf20Sopenharmony_ci	pll_clk->hw.reg = clk_mgr_a10_base_addr + reg;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	of_property_read_string(node, "clock-output-names", &clk_name);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	init.name = clk_name;
948c2ecf20Sopenharmony_ci	init.ops = ops;
958c2ecf20Sopenharmony_ci	init.flags = 0;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	while (i < SOCFGPA_MAX_PARENTS && (parent_name[i] =
988c2ecf20Sopenharmony_ci			of_clk_get_parent_name(node, i)) != NULL)
998c2ecf20Sopenharmony_ci		i++;
1008c2ecf20Sopenharmony_ci	init.num_parents = i;
1018c2ecf20Sopenharmony_ci	init.parent_names = parent_name;
1028c2ecf20Sopenharmony_ci	pll_clk->hw.hw.init = &init;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	pll_clk->hw.bit_idx = SOCFPGA_PLL_EXT_ENA;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	clk = clk_register(NULL, &pll_clk->hw.hw);
1078c2ecf20Sopenharmony_ci	if (WARN_ON(IS_ERR(clk))) {
1088c2ecf20Sopenharmony_ci		kfree(pll_clk);
1098c2ecf20Sopenharmony_ci		return NULL;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci	rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
1128c2ecf20Sopenharmony_ci	return clk;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_civoid __init socfpga_a10_pll_init(struct device_node *node)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	__socfpga_pll_init(node, &clk_pll_ops);
1188c2ecf20Sopenharmony_ci}
119