162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * RZ/A1 Core CPG Clocks 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Ideas On Board SPRL 662306a36Sopenharmony_ci * Copyright (C) 2014 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/clk-provider.h> 1062306a36Sopenharmony_ci#include <linux/clk/renesas.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/kernel.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/of_address.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define CPG_FRQCR 0x10 1962306a36Sopenharmony_ci#define CPG_FRQCR2 0x14 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define PPR0 0xFCFE3200 2262306a36Sopenharmony_ci#define PIBC0 0xFCFE7000 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define MD_CLK(x) ((x >> 2) & 1) /* P0_2 */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* ----------------------------------------------------------------------------- 2762306a36Sopenharmony_ci * Initialization 2862306a36Sopenharmony_ci */ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic u16 __init rz_cpg_read_mode_pins(void) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci void __iomem *ppr0, *pibc0; 3362306a36Sopenharmony_ci u16 modes; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci ppr0 = ioremap(PPR0, 2); 3662306a36Sopenharmony_ci pibc0 = ioremap(PIBC0, 2); 3762306a36Sopenharmony_ci BUG_ON(!ppr0 || !pibc0); 3862306a36Sopenharmony_ci iowrite16(4, pibc0); /* enable input buffer */ 3962306a36Sopenharmony_ci modes = ioread16(ppr0); 4062306a36Sopenharmony_ci iounmap(ppr0); 4162306a36Sopenharmony_ci iounmap(pibc0); 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci return modes; 4462306a36Sopenharmony_ci} 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic struct clk * __init 4762306a36Sopenharmony_cirz_cpg_register_clock(struct device_node *np, void __iomem *base, 4862306a36Sopenharmony_ci const char *name) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci u32 val; 5162306a36Sopenharmony_ci unsigned mult; 5262306a36Sopenharmony_ci static const unsigned frqcr_tab[4] = { 3, 2, 0, 1 }; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (strcmp(name, "pll") == 0) { 5562306a36Sopenharmony_ci unsigned int cpg_mode = MD_CLK(rz_cpg_read_mode_pins()); 5662306a36Sopenharmony_ci const char *parent_name = of_clk_get_parent_name(np, cpg_mode); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci mult = cpg_mode ? (32 / 4) : 30; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, 1); 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci /* If mapping regs failed, skip non-pll clocks. System will boot anyhow */ 6462306a36Sopenharmony_ci if (!base) 6562306a36Sopenharmony_ci return ERR_PTR(-ENXIO); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci /* FIXME:"i" and "g" are variable clocks with non-integer dividers (e.g. 2/3) 6862306a36Sopenharmony_ci * and the constraint that always g <= i. To get the rz platform started, 6962306a36Sopenharmony_ci * let them run at fixed current speed and implement the details later. 7062306a36Sopenharmony_ci */ 7162306a36Sopenharmony_ci if (strcmp(name, "i") == 0) 7262306a36Sopenharmony_ci val = (readl(base + CPG_FRQCR) >> 8) & 3; 7362306a36Sopenharmony_ci else if (strcmp(name, "g") == 0) 7462306a36Sopenharmony_ci val = readl(base + CPG_FRQCR2) & 3; 7562306a36Sopenharmony_ci else 7662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci mult = frqcr_tab[val]; 7962306a36Sopenharmony_ci return clk_register_fixed_factor(NULL, name, "pll", 0, mult, 3); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void __init rz_cpg_clocks_init(struct device_node *np) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci struct clk_onecell_data *data; 8562306a36Sopenharmony_ci struct clk **clks; 8662306a36Sopenharmony_ci void __iomem *base; 8762306a36Sopenharmony_ci unsigned i; 8862306a36Sopenharmony_ci int num_clks; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci num_clks = of_property_count_strings(np, "clock-output-names"); 9162306a36Sopenharmony_ci if (WARN(num_clks <= 0, "can't count CPG clocks\n")) 9262306a36Sopenharmony_ci return; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci data = kzalloc(sizeof(*data), GFP_KERNEL); 9562306a36Sopenharmony_ci clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL); 9662306a36Sopenharmony_ci BUG_ON(!data || !clks); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci data->clks = clks; 9962306a36Sopenharmony_ci data->clk_num = num_clks; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci base = of_iomap(np, 0); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci for (i = 0; i < num_clks; ++i) { 10462306a36Sopenharmony_ci const char *name; 10562306a36Sopenharmony_ci struct clk *clk; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci of_property_read_string_index(np, "clock-output-names", i, &name); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci clk = rz_cpg_register_clock(np, base, name); 11062306a36Sopenharmony_ci if (IS_ERR(clk)) 11162306a36Sopenharmony_ci pr_err("%s: failed to register %pOFn %s clock (%ld)\n", 11262306a36Sopenharmony_ci __func__, np, name, PTR_ERR(clk)); 11362306a36Sopenharmony_ci else 11462306a36Sopenharmony_ci data->clks[i] = clk; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci of_clk_add_provider(np, of_clk_src_onecell_get, data); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci cpg_mstp_add_clk_domain(np); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ciCLK_OF_DECLARE(rz_cpg_clks, "renesas,rz-cpg-clocks", rz_cpg_clocks_init); 122