162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Marvell EBU SoC common clock handling 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/slab.h> 1562306a36Sopenharmony_ci#include <linux/clk.h> 1662306a36Sopenharmony_ci#include <linux/clk-provider.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <linux/of.h> 1962306a36Sopenharmony_ci#include <linux/of_address.h> 2062306a36Sopenharmony_ci#include <linux/syscore_ops.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include "common.h" 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* 2562306a36Sopenharmony_ci * Core Clocks 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3) 2962306a36Sopenharmony_ci#define SSCG_SPREAD_DOWN 0x0 3062306a36Sopenharmony_ci#define SSCG_SPREAD_UP 0x1 3162306a36Sopenharmony_ci#define SSCG_SPREAD_CENTRAL 0x2 3262306a36Sopenharmony_ci#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF) 3362306a36Sopenharmony_ci#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic struct clk_onecell_data clk_data; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* 3862306a36Sopenharmony_ci * This function can be used by the Kirkwood, the Armada 370, the 3962306a36Sopenharmony_ci * Armada XP and the Armada 375 SoC. The name of the function was 4062306a36Sopenharmony_ci * chosen following the dt convention: using the first known SoC 4162306a36Sopenharmony_ci * compatible with it. 4262306a36Sopenharmony_ci */ 4362306a36Sopenharmony_ciu32 kirkwood_fix_sscg_deviation(u32 system_clk) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci struct device_node *sscg_np = NULL; 4662306a36Sopenharmony_ci void __iomem *sscg_map; 4762306a36Sopenharmony_ci u32 sscg_reg; 4862306a36Sopenharmony_ci s32 low_bound, high_bound; 4962306a36Sopenharmony_ci u64 freq_swing_half; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci sscg_np = of_find_node_by_name(NULL, "sscg"); 5262306a36Sopenharmony_ci if (sscg_np == NULL) { 5362306a36Sopenharmony_ci pr_err("cannot get SSCG register node\n"); 5462306a36Sopenharmony_ci return system_clk; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci sscg_map = of_iomap(sscg_np, 0); 5862306a36Sopenharmony_ci if (sscg_map == NULL) { 5962306a36Sopenharmony_ci pr_err("cannot map SSCG register\n"); 6062306a36Sopenharmony_ci goto out; 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci sscg_reg = readl(sscg_map); 6462306a36Sopenharmony_ci high_bound = SSCG_CONF_HIGH(sscg_reg); 6562306a36Sopenharmony_ci low_bound = SSCG_CONF_LOW(sscg_reg); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if ((high_bound - low_bound) <= 0) 6862306a36Sopenharmony_ci goto out; 6962306a36Sopenharmony_ci /* 7062306a36Sopenharmony_ci * From Marvell engineer we got the following formula (when 7162306a36Sopenharmony_ci * this code was written, the datasheet was erroneous) 7262306a36Sopenharmony_ci * Spread percentage = 1/96 * (H - L) / H 7362306a36Sopenharmony_ci * H = SSCG_High_Boundary 7462306a36Sopenharmony_ci * L = SSCG_Low_Boundary 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * As the deviation is half of spread then it lead to the 7762306a36Sopenharmony_ci * following formula in the code. 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * To avoid an overflow and not lose any significant digit in 8062306a36Sopenharmony_ci * the same time we have to use a 64 bit integer. 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci freq_swing_half = (((u64)high_bound - (u64)low_bound) 8462306a36Sopenharmony_ci * (u64)system_clk); 8562306a36Sopenharmony_ci do_div(freq_swing_half, (2 * 96 * high_bound)); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci switch (SSCG_CONF_MODE(sscg_reg)) { 8862306a36Sopenharmony_ci case SSCG_SPREAD_DOWN: 8962306a36Sopenharmony_ci system_clk -= freq_swing_half; 9062306a36Sopenharmony_ci break; 9162306a36Sopenharmony_ci case SSCG_SPREAD_UP: 9262306a36Sopenharmony_ci system_clk += freq_swing_half; 9362306a36Sopenharmony_ci break; 9462306a36Sopenharmony_ci case SSCG_SPREAD_CENTRAL: 9562306a36Sopenharmony_ci default: 9662306a36Sopenharmony_ci break; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci iounmap(sscg_map); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ciout: 10262306a36Sopenharmony_ci of_node_put(sscg_np); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return system_clk; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_civoid __init mvebu_coreclk_setup(struct device_node *np, 10862306a36Sopenharmony_ci const struct coreclk_soc_desc *desc) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci const char *tclk_name = "tclk"; 11162306a36Sopenharmony_ci const char *cpuclk_name = "cpuclk"; 11262306a36Sopenharmony_ci void __iomem *base; 11362306a36Sopenharmony_ci unsigned long rate; 11462306a36Sopenharmony_ci int n; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci base = of_iomap(np, 0); 11762306a36Sopenharmony_ci if (WARN_ON(!base)) 11862306a36Sopenharmony_ci return; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Allocate struct for TCLK, cpu clk, and core ratio clocks */ 12162306a36Sopenharmony_ci clk_data.clk_num = 2 + desc->num_ratios; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* One more clock for the optional refclk */ 12462306a36Sopenharmony_ci if (desc->get_refclk_freq) 12562306a36Sopenharmony_ci clk_data.clk_num += 1; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci clk_data.clks = kcalloc(clk_data.clk_num, sizeof(*clk_data.clks), 12862306a36Sopenharmony_ci GFP_KERNEL); 12962306a36Sopenharmony_ci if (WARN_ON(!clk_data.clks)) { 13062306a36Sopenharmony_ci iounmap(base); 13162306a36Sopenharmony_ci return; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* Register TCLK */ 13562306a36Sopenharmony_ci of_property_read_string_index(np, "clock-output-names", 0, 13662306a36Sopenharmony_ci &tclk_name); 13762306a36Sopenharmony_ci rate = desc->get_tclk_freq(base); 13862306a36Sopenharmony_ci clk_data.clks[0] = clk_register_fixed_rate(NULL, tclk_name, NULL, 0, 13962306a36Sopenharmony_ci rate); 14062306a36Sopenharmony_ci WARN_ON(IS_ERR(clk_data.clks[0])); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* Register CPU clock */ 14362306a36Sopenharmony_ci of_property_read_string_index(np, "clock-output-names", 1, 14462306a36Sopenharmony_ci &cpuclk_name); 14562306a36Sopenharmony_ci rate = desc->get_cpu_freq(base); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (desc->is_sscg_enabled && desc->fix_sscg_deviation 14862306a36Sopenharmony_ci && desc->is_sscg_enabled(base)) 14962306a36Sopenharmony_ci rate = desc->fix_sscg_deviation(rate); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL, 0, 15262306a36Sopenharmony_ci rate); 15362306a36Sopenharmony_ci WARN_ON(IS_ERR(clk_data.clks[1])); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Register fixed-factor clocks derived from CPU clock */ 15662306a36Sopenharmony_ci for (n = 0; n < desc->num_ratios; n++) { 15762306a36Sopenharmony_ci const char *rclk_name = desc->ratios[n].name; 15862306a36Sopenharmony_ci int mult, div; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci of_property_read_string_index(np, "clock-output-names", 16162306a36Sopenharmony_ci 2+n, &rclk_name); 16262306a36Sopenharmony_ci desc->get_clk_ratio(base, desc->ratios[n].id, &mult, &div); 16362306a36Sopenharmony_ci clk_data.clks[2+n] = clk_register_fixed_factor(NULL, rclk_name, 16462306a36Sopenharmony_ci cpuclk_name, 0, mult, div); 16562306a36Sopenharmony_ci WARN_ON(IS_ERR(clk_data.clks[2+n])); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Register optional refclk */ 16962306a36Sopenharmony_ci if (desc->get_refclk_freq) { 17062306a36Sopenharmony_ci const char *name = "refclk"; 17162306a36Sopenharmony_ci of_property_read_string_index(np, "clock-output-names", 17262306a36Sopenharmony_ci 2 + desc->num_ratios, &name); 17362306a36Sopenharmony_ci rate = desc->get_refclk_freq(base); 17462306a36Sopenharmony_ci clk_data.clks[2 + desc->num_ratios] = 17562306a36Sopenharmony_ci clk_register_fixed_rate(NULL, name, NULL, 0, rate); 17662306a36Sopenharmony_ci WARN_ON(IS_ERR(clk_data.clks[2 + desc->num_ratios])); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* SAR register isn't needed anymore */ 18062306a36Sopenharmony_ci iounmap(base); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci/* 18662306a36Sopenharmony_ci * Clock Gating Control 18762306a36Sopenharmony_ci */ 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciDEFINE_SPINLOCK(ctrl_gating_lock); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistruct clk_gating_ctrl { 19262306a36Sopenharmony_ci spinlock_t *lock; 19362306a36Sopenharmony_ci struct clk **gates; 19462306a36Sopenharmony_ci int num_gates; 19562306a36Sopenharmony_ci void __iomem *base; 19662306a36Sopenharmony_ci u32 saved_reg; 19762306a36Sopenharmony_ci}; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic struct clk_gating_ctrl *ctrl; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic struct clk *clk_gating_get_src( 20262306a36Sopenharmony_ci struct of_phandle_args *clkspec, void *data) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci int n; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (clkspec->args_count < 1) 20762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci for (n = 0; n < ctrl->num_gates; n++) { 21062306a36Sopenharmony_ci struct clk_gate *gate = 21162306a36Sopenharmony_ci to_clk_gate(__clk_get_hw(ctrl->gates[n])); 21262306a36Sopenharmony_ci if (clkspec->args[0] == gate->bit_idx) 21362306a36Sopenharmony_ci return ctrl->gates[n]; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic int mvebu_clk_gating_suspend(void) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci ctrl->saved_reg = readl(ctrl->base); 22162306a36Sopenharmony_ci return 0; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic void mvebu_clk_gating_resume(void) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci writel(ctrl->saved_reg, ctrl->base); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic struct syscore_ops clk_gate_syscore_ops = { 23062306a36Sopenharmony_ci .suspend = mvebu_clk_gating_suspend, 23162306a36Sopenharmony_ci .resume = mvebu_clk_gating_resume, 23262306a36Sopenharmony_ci}; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_civoid __init mvebu_clk_gating_setup(struct device_node *np, 23562306a36Sopenharmony_ci const struct clk_gating_soc_desc *desc) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci struct clk *clk; 23862306a36Sopenharmony_ci void __iomem *base; 23962306a36Sopenharmony_ci const char *default_parent = NULL; 24062306a36Sopenharmony_ci int n; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (ctrl) { 24362306a36Sopenharmony_ci pr_err("mvebu-clk-gating: cannot instantiate more than one gateable clock device\n"); 24462306a36Sopenharmony_ci return; 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci base = of_iomap(np, 0); 24862306a36Sopenharmony_ci if (WARN_ON(!base)) 24962306a36Sopenharmony_ci return; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci clk = of_clk_get(np, 0); 25262306a36Sopenharmony_ci if (!IS_ERR(clk)) { 25362306a36Sopenharmony_ci default_parent = __clk_get_name(clk); 25462306a36Sopenharmony_ci clk_put(clk); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); 25862306a36Sopenharmony_ci if (WARN_ON(!ctrl)) 25962306a36Sopenharmony_ci goto ctrl_out; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* lock must already be initialized */ 26262306a36Sopenharmony_ci ctrl->lock = &ctrl_gating_lock; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci ctrl->base = base; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Count, allocate, and register clock gates */ 26762306a36Sopenharmony_ci for (n = 0; desc[n].name;) 26862306a36Sopenharmony_ci n++; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci ctrl->num_gates = n; 27162306a36Sopenharmony_ci ctrl->gates = kcalloc(ctrl->num_gates, sizeof(*ctrl->gates), 27262306a36Sopenharmony_ci GFP_KERNEL); 27362306a36Sopenharmony_ci if (WARN_ON(!ctrl->gates)) 27462306a36Sopenharmony_ci goto gates_out; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci for (n = 0; n < ctrl->num_gates; n++) { 27762306a36Sopenharmony_ci const char *parent = 27862306a36Sopenharmony_ci (desc[n].parent) ? desc[n].parent : default_parent; 27962306a36Sopenharmony_ci ctrl->gates[n] = clk_register_gate(NULL, desc[n].name, parent, 28062306a36Sopenharmony_ci desc[n].flags, base, desc[n].bit_idx, 28162306a36Sopenharmony_ci 0, ctrl->lock); 28262306a36Sopenharmony_ci WARN_ON(IS_ERR(ctrl->gates[n])); 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci of_clk_add_provider(np, clk_gating_get_src, ctrl); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci register_syscore_ops(&clk_gate_syscore_ops); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci return; 29062306a36Sopenharmony_cigates_out: 29162306a36Sopenharmony_ci kfree(ctrl); 29262306a36Sopenharmony_cictrl_out: 29362306a36Sopenharmony_ci iounmap(base); 29462306a36Sopenharmony_ci} 295