18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * RZ/A1 Core CPG Clocks
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Ideas On Board SPRL
68c2ecf20Sopenharmony_ci * Copyright (C) 2014 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
108c2ecf20Sopenharmony_ci#include <linux/clk/renesas.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/io.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/of.h>
158c2ecf20Sopenharmony_ci#include <linux/of_address.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistruct rz_cpg {
198c2ecf20Sopenharmony_ci	struct clk_onecell_data data;
208c2ecf20Sopenharmony_ci	void __iomem *reg;
218c2ecf20Sopenharmony_ci};
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define CPG_FRQCR	0x10
248c2ecf20Sopenharmony_ci#define CPG_FRQCR2	0x14
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#define PPR0		0xFCFE3200
278c2ecf20Sopenharmony_ci#define PIBC0		0xFCFE7000
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define MD_CLK(x)	((x >> 2) & 1)	/* P0_2 */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* -----------------------------------------------------------------------------
328c2ecf20Sopenharmony_ci * Initialization
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cistatic u16 __init rz_cpg_read_mode_pins(void)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	void __iomem *ppr0, *pibc0;
388c2ecf20Sopenharmony_ci	u16 modes;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	ppr0 = ioremap(PPR0, 2);
418c2ecf20Sopenharmony_ci	pibc0 = ioremap(PIBC0, 2);
428c2ecf20Sopenharmony_ci	BUG_ON(!ppr0 || !pibc0);
438c2ecf20Sopenharmony_ci	iowrite16(4, pibc0);	/* enable input buffer */
448c2ecf20Sopenharmony_ci	modes = ioread16(ppr0);
458c2ecf20Sopenharmony_ci	iounmap(ppr0);
468c2ecf20Sopenharmony_ci	iounmap(pibc0);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	return modes;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic struct clk * __init
528c2ecf20Sopenharmony_cirz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *name)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	u32 val;
558c2ecf20Sopenharmony_ci	unsigned mult;
568c2ecf20Sopenharmony_ci	static const unsigned frqcr_tab[4] = { 3, 2, 0, 1 };
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	if (strcmp(name, "pll") == 0) {
598c2ecf20Sopenharmony_ci		unsigned int cpg_mode = MD_CLK(rz_cpg_read_mode_pins());
608c2ecf20Sopenharmony_ci		const char *parent_name = of_clk_get_parent_name(np, cpg_mode);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci		mult = cpg_mode ? (32 / 4) : 30;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci		return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, 1);
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	/* If mapping regs failed, skip non-pll clocks. System will boot anyhow */
688c2ecf20Sopenharmony_ci	if (!cpg->reg)
698c2ecf20Sopenharmony_ci		return ERR_PTR(-ENXIO);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	/* FIXME:"i" and "g" are variable clocks with non-integer dividers (e.g. 2/3)
728c2ecf20Sopenharmony_ci	 * and the constraint that always g <= i. To get the rz platform started,
738c2ecf20Sopenharmony_ci	 * let them run at fixed current speed and implement the details later.
748c2ecf20Sopenharmony_ci	 */
758c2ecf20Sopenharmony_ci	if (strcmp(name, "i") == 0)
768c2ecf20Sopenharmony_ci		val = (readl(cpg->reg + CPG_FRQCR) >> 8) & 3;
778c2ecf20Sopenharmony_ci	else if (strcmp(name, "g") == 0)
788c2ecf20Sopenharmony_ci		val = readl(cpg->reg + CPG_FRQCR2) & 3;
798c2ecf20Sopenharmony_ci	else
808c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	mult = frqcr_tab[val];
838c2ecf20Sopenharmony_ci	return clk_register_fixed_factor(NULL, name, "pll", 0, mult, 3);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic void __init rz_cpg_clocks_init(struct device_node *np)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct rz_cpg *cpg;
898c2ecf20Sopenharmony_ci	struct clk **clks;
908c2ecf20Sopenharmony_ci	unsigned i;
918c2ecf20Sopenharmony_ci	int num_clks;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	num_clks = of_property_count_strings(np, "clock-output-names");
948c2ecf20Sopenharmony_ci	if (WARN(num_clks <= 0, "can't count CPG clocks\n"))
958c2ecf20Sopenharmony_ci		return;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	cpg = kzalloc(sizeof(*cpg), GFP_KERNEL);
988c2ecf20Sopenharmony_ci	clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL);
998c2ecf20Sopenharmony_ci	BUG_ON(!cpg || !clks);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	cpg->data.clks = clks;
1028c2ecf20Sopenharmony_ci	cpg->data.clk_num = num_clks;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	cpg->reg = of_iomap(np, 0);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	for (i = 0; i < num_clks; ++i) {
1078c2ecf20Sopenharmony_ci		const char *name;
1088c2ecf20Sopenharmony_ci		struct clk *clk;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		of_property_read_string_index(np, "clock-output-names", i, &name);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		clk = rz_cpg_register_clock(np, cpg, name);
1138c2ecf20Sopenharmony_ci		if (IS_ERR(clk))
1148c2ecf20Sopenharmony_ci			pr_err("%s: failed to register %pOFn %s clock (%ld)\n",
1158c2ecf20Sopenharmony_ci			       __func__, np, name, PTR_ERR(clk));
1168c2ecf20Sopenharmony_ci		else
1178c2ecf20Sopenharmony_ci			cpg->data.clks[i] = clk;
1188c2ecf20Sopenharmony_ci	}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	cpg_mstp_add_clk_domain(np);
1238c2ecf20Sopenharmony_ci}
1248c2ecf20Sopenharmony_ciCLK_OF_DECLARE(rz_cpg_clks, "renesas,rz-cpg-clocks", rz_cpg_clocks_init);
125