162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * sh73a0 Core CPG Clocks 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Ulrich Hecht 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/clk-provider.h> 962306a36Sopenharmony_ci#include <linux/clk/renesas.h> 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/kernel.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/of_address.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/spinlock.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_cistruct sh73a0_cpg { 1962306a36Sopenharmony_ci struct clk_onecell_data data; 2062306a36Sopenharmony_ci spinlock_t lock; 2162306a36Sopenharmony_ci}; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define CPG_FRQCRA 0x00 2462306a36Sopenharmony_ci#define CPG_FRQCRB 0x04 2562306a36Sopenharmony_ci#define CPG_SD0CKCR 0x74 2662306a36Sopenharmony_ci#define CPG_SD1CKCR 0x78 2762306a36Sopenharmony_ci#define CPG_SD2CKCR 0x7c 2862306a36Sopenharmony_ci#define CPG_PLLECR 0xd0 2962306a36Sopenharmony_ci#define CPG_PLL0CR 0xd8 3062306a36Sopenharmony_ci#define CPG_PLL1CR 0x28 3162306a36Sopenharmony_ci#define CPG_PLL2CR 0x2c 3262306a36Sopenharmony_ci#define CPG_PLL3CR 0xdc 3362306a36Sopenharmony_ci#define CPG_CKSCR 0xc0 3462306a36Sopenharmony_ci#define CPG_DSI0PHYCR 0x6c 3562306a36Sopenharmony_ci#define CPG_DSI1PHYCR 0x70 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define CLK_ENABLE_ON_INIT BIT(0) 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct div4_clk { 4062306a36Sopenharmony_ci const char *name; 4162306a36Sopenharmony_ci const char *parent; 4262306a36Sopenharmony_ci unsigned int reg; 4362306a36Sopenharmony_ci unsigned int shift; 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic const struct div4_clk div4_clks[] = { 4762306a36Sopenharmony_ci { "zg", "pll0", CPG_FRQCRA, 16 }, 4862306a36Sopenharmony_ci { "m3", "pll1", CPG_FRQCRA, 12 }, 4962306a36Sopenharmony_ci { "b", "pll1", CPG_FRQCRA, 8 }, 5062306a36Sopenharmony_ci { "m1", "pll1", CPG_FRQCRA, 4 }, 5162306a36Sopenharmony_ci { "m2", "pll1", CPG_FRQCRA, 0 }, 5262306a36Sopenharmony_ci { "zx", "pll1", CPG_FRQCRB, 12 }, 5362306a36Sopenharmony_ci { "hp", "pll1", CPG_FRQCRB, 4 }, 5462306a36Sopenharmony_ci { NULL, NULL, 0, 0 }, 5562306a36Sopenharmony_ci}; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic const struct clk_div_table div4_div_table[] = { 5862306a36Sopenharmony_ci { 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 }, { 5, 12 }, 5962306a36Sopenharmony_ci { 6, 16 }, { 7, 18 }, { 8, 24 }, { 10, 36 }, { 11, 48 }, 6062306a36Sopenharmony_ci { 12, 7 }, { 0, 0 } 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic const struct clk_div_table z_div_table[] = { 6462306a36Sopenharmony_ci /* ZSEL == 0 */ 6562306a36Sopenharmony_ci { 0, 1 }, { 1, 1 }, { 2, 1 }, { 3, 1 }, { 4, 1 }, { 5, 1 }, 6662306a36Sopenharmony_ci { 6, 1 }, { 7, 1 }, { 8, 1 }, { 9, 1 }, { 10, 1 }, { 11, 1 }, 6762306a36Sopenharmony_ci { 12, 1 }, { 13, 1 }, { 14, 1 }, { 15, 1 }, 6862306a36Sopenharmony_ci /* ZSEL == 1 */ 6962306a36Sopenharmony_ci { 16, 2 }, { 17, 3 }, { 18, 4 }, { 19, 6 }, { 20, 8 }, { 21, 12 }, 7062306a36Sopenharmony_ci { 22, 16 }, { 24, 24 }, { 27, 48 }, { 0, 0 } 7162306a36Sopenharmony_ci}; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic struct clk * __init 7462306a36Sopenharmony_cish73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg, 7562306a36Sopenharmony_ci void __iomem *base, const char *name) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci const struct clk_div_table *table = NULL; 7862306a36Sopenharmony_ci unsigned int shift, reg, width; 7962306a36Sopenharmony_ci const char *parent_name = NULL; 8062306a36Sopenharmony_ci unsigned int mult = 1; 8162306a36Sopenharmony_ci unsigned int div = 1; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (!strcmp(name, "main")) { 8462306a36Sopenharmony_ci /* extal1, extal1_div2, extal2, extal2_div2 */ 8562306a36Sopenharmony_ci u32 parent_idx = (readl(base + CPG_CKSCR) >> 28) & 3; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci parent_name = of_clk_get_parent_name(np, parent_idx >> 1); 8862306a36Sopenharmony_ci div = (parent_idx & 1) + 1; 8962306a36Sopenharmony_ci } else if (!strncmp(name, "pll", 3)) { 9062306a36Sopenharmony_ci void __iomem *enable_reg = base; 9162306a36Sopenharmony_ci u32 enable_bit = name[3] - '0'; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci parent_name = "main"; 9462306a36Sopenharmony_ci switch (enable_bit) { 9562306a36Sopenharmony_ci case 0: 9662306a36Sopenharmony_ci enable_reg += CPG_PLL0CR; 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci case 1: 9962306a36Sopenharmony_ci enable_reg += CPG_PLL1CR; 10062306a36Sopenharmony_ci break; 10162306a36Sopenharmony_ci case 2: 10262306a36Sopenharmony_ci enable_reg += CPG_PLL2CR; 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci case 3: 10562306a36Sopenharmony_ci enable_reg += CPG_PLL3CR; 10662306a36Sopenharmony_ci break; 10762306a36Sopenharmony_ci default: 10862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci if (readl(base + CPG_PLLECR) & BIT(enable_bit)) { 11162306a36Sopenharmony_ci mult = ((readl(enable_reg) >> 24) & 0x3f) + 1; 11262306a36Sopenharmony_ci /* handle CFG bit for PLL1 and PLL2 */ 11362306a36Sopenharmony_ci if (enable_bit == 1 || enable_bit == 2) 11462306a36Sopenharmony_ci if (readl(enable_reg) & BIT(20)) 11562306a36Sopenharmony_ci mult *= 2; 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci } else if (!strcmp(name, "dsi0phy") || !strcmp(name, "dsi1phy")) { 11862306a36Sopenharmony_ci u32 phy_no = name[3] - '0'; 11962306a36Sopenharmony_ci void __iomem *dsi_reg = base + 12062306a36Sopenharmony_ci (phy_no ? CPG_DSI1PHYCR : CPG_DSI0PHYCR); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci parent_name = phy_no ? "dsi1pck" : "dsi0pck"; 12362306a36Sopenharmony_ci mult = readl(dsi_reg); 12462306a36Sopenharmony_ci if (!(mult & 0x8000)) 12562306a36Sopenharmony_ci mult = 1; 12662306a36Sopenharmony_ci else 12762306a36Sopenharmony_ci mult = (mult & 0x3f) + 1; 12862306a36Sopenharmony_ci } else if (!strcmp(name, "z")) { 12962306a36Sopenharmony_ci parent_name = "pll0"; 13062306a36Sopenharmony_ci table = z_div_table; 13162306a36Sopenharmony_ci reg = CPG_FRQCRB; 13262306a36Sopenharmony_ci shift = 24; 13362306a36Sopenharmony_ci width = 5; 13462306a36Sopenharmony_ci } else { 13562306a36Sopenharmony_ci const struct div4_clk *c; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci for (c = div4_clks; c->name; c++) { 13862306a36Sopenharmony_ci if (!strcmp(name, c->name)) { 13962306a36Sopenharmony_ci parent_name = c->parent; 14062306a36Sopenharmony_ci table = div4_div_table; 14162306a36Sopenharmony_ci reg = c->reg; 14262306a36Sopenharmony_ci shift = c->shift; 14362306a36Sopenharmony_ci width = 4; 14462306a36Sopenharmony_ci break; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci if (!c->name) 14862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci if (!table) { 15262306a36Sopenharmony_ci return clk_register_fixed_factor(NULL, name, parent_name, 0, 15362306a36Sopenharmony_ci mult, div); 15462306a36Sopenharmony_ci } else { 15562306a36Sopenharmony_ci return clk_register_divider_table(NULL, name, parent_name, 0, 15662306a36Sopenharmony_ci base + reg, shift, width, 0, 15762306a36Sopenharmony_ci table, &cpg->lock); 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic void __init sh73a0_cpg_clocks_init(struct device_node *np) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci struct sh73a0_cpg *cpg; 16462306a36Sopenharmony_ci void __iomem *base; 16562306a36Sopenharmony_ci struct clk **clks; 16662306a36Sopenharmony_ci unsigned int i; 16762306a36Sopenharmony_ci int num_clks; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci num_clks = of_property_count_strings(np, "clock-output-names"); 17062306a36Sopenharmony_ci if (num_clks < 0) { 17162306a36Sopenharmony_ci pr_err("%s: failed to count clocks\n", __func__); 17262306a36Sopenharmony_ci return; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); 17662306a36Sopenharmony_ci clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL); 17762306a36Sopenharmony_ci if (cpg == NULL || clks == NULL) { 17862306a36Sopenharmony_ci /* We're leaking memory on purpose, there's no point in cleaning 17962306a36Sopenharmony_ci * up as the system won't boot anyway. 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci return; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci spin_lock_init(&cpg->lock); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci cpg->data.clks = clks; 18762306a36Sopenharmony_ci cpg->data.clk_num = num_clks; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci base = of_iomap(np, 0); 19062306a36Sopenharmony_ci if (WARN_ON(base == NULL)) 19162306a36Sopenharmony_ci return; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* Set SDHI clocks to a known state */ 19462306a36Sopenharmony_ci writel(0x108, base + CPG_SD0CKCR); 19562306a36Sopenharmony_ci writel(0x108, base + CPG_SD1CKCR); 19662306a36Sopenharmony_ci writel(0x108, base + CPG_SD2CKCR); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci for (i = 0; i < num_clks; ++i) { 19962306a36Sopenharmony_ci const char *name; 20062306a36Sopenharmony_ci struct clk *clk; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci of_property_read_string_index(np, "clock-output-names", i, 20362306a36Sopenharmony_ci &name); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci clk = sh73a0_cpg_register_clock(np, cpg, base, name); 20662306a36Sopenharmony_ci if (IS_ERR(clk)) 20762306a36Sopenharmony_ci pr_err("%s: failed to register %pOFn %s clock (%ld)\n", 20862306a36Sopenharmony_ci __func__, np, name, PTR_ERR(clk)); 20962306a36Sopenharmony_ci else 21062306a36Sopenharmony_ci cpg->data.clks[i] = clk; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ciCLK_OF_DECLARE(sh73a0_cpg_clks, "renesas,sh73a0-cpg-clocks", 21662306a36Sopenharmony_ci sh73a0_cpg_clocks_init); 217