18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Marvell EBU SoC common clock handling 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Marvell 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Gregory CLEMENT <gregory.clement@free-electrons.com> 88c2ecf20Sopenharmony_ci * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> 98c2ecf20Sopenharmony_ci * Andrew Lunn <andrew@lunn.ch> 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/clk.h> 168c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_address.h> 208c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "common.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 258c2ecf20Sopenharmony_ci * Core Clocks 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define SSCG_CONF_MODE(reg) (((reg) >> 16) & 0x3) 298c2ecf20Sopenharmony_ci#define SSCG_SPREAD_DOWN 0x0 308c2ecf20Sopenharmony_ci#define SSCG_SPREAD_UP 0x1 318c2ecf20Sopenharmony_ci#define SSCG_SPREAD_CENTRAL 0x2 328c2ecf20Sopenharmony_ci#define SSCG_CONF_LOW(reg) (((reg) >> 8) & 0xFF) 338c2ecf20Sopenharmony_ci#define SSCG_CONF_HIGH(reg) ((reg) & 0xFF) 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct clk_onecell_data clk_data; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * This function can be used by the Kirkwood, the Armada 370, the 398c2ecf20Sopenharmony_ci * Armada XP and the Armada 375 SoC. The name of the function was 408c2ecf20Sopenharmony_ci * chosen following the dt convention: using the first known SoC 418c2ecf20Sopenharmony_ci * compatible with it. 428c2ecf20Sopenharmony_ci */ 438c2ecf20Sopenharmony_ciu32 kirkwood_fix_sscg_deviation(u32 system_clk) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct device_node *sscg_np = NULL; 468c2ecf20Sopenharmony_ci void __iomem *sscg_map; 478c2ecf20Sopenharmony_ci u32 sscg_reg; 488c2ecf20Sopenharmony_ci s32 low_bound, high_bound; 498c2ecf20Sopenharmony_ci u64 freq_swing_half; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci sscg_np = of_find_node_by_name(NULL, "sscg"); 528c2ecf20Sopenharmony_ci if (sscg_np == NULL) { 538c2ecf20Sopenharmony_ci pr_err("cannot get SSCG register node\n"); 548c2ecf20Sopenharmony_ci return system_clk; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci sscg_map = of_iomap(sscg_np, 0); 588c2ecf20Sopenharmony_ci if (sscg_map == NULL) { 598c2ecf20Sopenharmony_ci pr_err("cannot map SSCG register\n"); 608c2ecf20Sopenharmony_ci goto out; 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci sscg_reg = readl(sscg_map); 648c2ecf20Sopenharmony_ci high_bound = SSCG_CONF_HIGH(sscg_reg); 658c2ecf20Sopenharmony_ci low_bound = SSCG_CONF_LOW(sscg_reg); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if ((high_bound - low_bound) <= 0) 688c2ecf20Sopenharmony_ci goto out; 698c2ecf20Sopenharmony_ci /* 708c2ecf20Sopenharmony_ci * From Marvell engineer we got the following formula (when 718c2ecf20Sopenharmony_ci * this code was written, the datasheet was erroneous) 728c2ecf20Sopenharmony_ci * Spread percentage = 1/96 * (H - L) / H 738c2ecf20Sopenharmony_ci * H = SSCG_High_Boundary 748c2ecf20Sopenharmony_ci * L = SSCG_Low_Boundary 758c2ecf20Sopenharmony_ci * 768c2ecf20Sopenharmony_ci * As the deviation is half of spread then it lead to the 778c2ecf20Sopenharmony_ci * following formula in the code. 788c2ecf20Sopenharmony_ci * 798c2ecf20Sopenharmony_ci * To avoid an overflow and not lose any significant digit in 808c2ecf20Sopenharmony_ci * the same time we have to use a 64 bit integer. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci freq_swing_half = (((u64)high_bound - (u64)low_bound) 848c2ecf20Sopenharmony_ci * (u64)system_clk); 858c2ecf20Sopenharmony_ci do_div(freq_swing_half, (2 * 96 * high_bound)); 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci switch (SSCG_CONF_MODE(sscg_reg)) { 888c2ecf20Sopenharmony_ci case SSCG_SPREAD_DOWN: 898c2ecf20Sopenharmony_ci system_clk -= freq_swing_half; 908c2ecf20Sopenharmony_ci break; 918c2ecf20Sopenharmony_ci case SSCG_SPREAD_UP: 928c2ecf20Sopenharmony_ci system_clk += freq_swing_half; 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci case SSCG_SPREAD_CENTRAL: 958c2ecf20Sopenharmony_ci default: 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci iounmap(sscg_map); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ciout: 1028c2ecf20Sopenharmony_ci of_node_put(sscg_np); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci return system_clk; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_civoid __init mvebu_coreclk_setup(struct device_node *np, 1088c2ecf20Sopenharmony_ci const struct coreclk_soc_desc *desc) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci const char *tclk_name = "tclk"; 1118c2ecf20Sopenharmony_ci const char *cpuclk_name = "cpuclk"; 1128c2ecf20Sopenharmony_ci void __iomem *base; 1138c2ecf20Sopenharmony_ci unsigned long rate; 1148c2ecf20Sopenharmony_ci int n; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci base = of_iomap(np, 0); 1178c2ecf20Sopenharmony_ci if (WARN_ON(!base)) 1188c2ecf20Sopenharmony_ci return; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci /* Allocate struct for TCLK, cpu clk, and core ratio clocks */ 1218c2ecf20Sopenharmony_ci clk_data.clk_num = 2 + desc->num_ratios; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* One more clock for the optional refclk */ 1248c2ecf20Sopenharmony_ci if (desc->get_refclk_freq) 1258c2ecf20Sopenharmony_ci clk_data.clk_num += 1; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci clk_data.clks = kcalloc(clk_data.clk_num, sizeof(*clk_data.clks), 1288c2ecf20Sopenharmony_ci GFP_KERNEL); 1298c2ecf20Sopenharmony_ci if (WARN_ON(!clk_data.clks)) { 1308c2ecf20Sopenharmony_ci iounmap(base); 1318c2ecf20Sopenharmony_ci return; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* Register TCLK */ 1358c2ecf20Sopenharmony_ci of_property_read_string_index(np, "clock-output-names", 0, 1368c2ecf20Sopenharmony_ci &tclk_name); 1378c2ecf20Sopenharmony_ci rate = desc->get_tclk_freq(base); 1388c2ecf20Sopenharmony_ci clk_data.clks[0] = clk_register_fixed_rate(NULL, tclk_name, NULL, 0, 1398c2ecf20Sopenharmony_ci rate); 1408c2ecf20Sopenharmony_ci WARN_ON(IS_ERR(clk_data.clks[0])); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Register CPU clock */ 1438c2ecf20Sopenharmony_ci of_property_read_string_index(np, "clock-output-names", 1, 1448c2ecf20Sopenharmony_ci &cpuclk_name); 1458c2ecf20Sopenharmony_ci rate = desc->get_cpu_freq(base); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (desc->is_sscg_enabled && desc->fix_sscg_deviation 1488c2ecf20Sopenharmony_ci && desc->is_sscg_enabled(base)) 1498c2ecf20Sopenharmony_ci rate = desc->fix_sscg_deviation(rate); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci clk_data.clks[1] = clk_register_fixed_rate(NULL, cpuclk_name, NULL, 0, 1528c2ecf20Sopenharmony_ci rate); 1538c2ecf20Sopenharmony_ci WARN_ON(IS_ERR(clk_data.clks[1])); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci /* Register fixed-factor clocks derived from CPU clock */ 1568c2ecf20Sopenharmony_ci for (n = 0; n < desc->num_ratios; n++) { 1578c2ecf20Sopenharmony_ci const char *rclk_name = desc->ratios[n].name; 1588c2ecf20Sopenharmony_ci int mult, div; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci of_property_read_string_index(np, "clock-output-names", 1618c2ecf20Sopenharmony_ci 2+n, &rclk_name); 1628c2ecf20Sopenharmony_ci desc->get_clk_ratio(base, desc->ratios[n].id, &mult, &div); 1638c2ecf20Sopenharmony_ci clk_data.clks[2+n] = clk_register_fixed_factor(NULL, rclk_name, 1648c2ecf20Sopenharmony_ci cpuclk_name, 0, mult, div); 1658c2ecf20Sopenharmony_ci WARN_ON(IS_ERR(clk_data.clks[2+n])); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Register optional refclk */ 1698c2ecf20Sopenharmony_ci if (desc->get_refclk_freq) { 1708c2ecf20Sopenharmony_ci const char *name = "refclk"; 1718c2ecf20Sopenharmony_ci of_property_read_string_index(np, "clock-output-names", 1728c2ecf20Sopenharmony_ci 2 + desc->num_ratios, &name); 1738c2ecf20Sopenharmony_ci rate = desc->get_refclk_freq(base); 1748c2ecf20Sopenharmony_ci clk_data.clks[2 + desc->num_ratios] = 1758c2ecf20Sopenharmony_ci clk_register_fixed_rate(NULL, name, NULL, 0, rate); 1768c2ecf20Sopenharmony_ci WARN_ON(IS_ERR(clk_data.clks[2 + desc->num_ratios])); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* SAR register isn't needed anymore */ 1808c2ecf20Sopenharmony_ci iounmap(base); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* 1868c2ecf20Sopenharmony_ci * Clock Gating Control 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ciDEFINE_SPINLOCK(ctrl_gating_lock); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistruct clk_gating_ctrl { 1928c2ecf20Sopenharmony_ci spinlock_t *lock; 1938c2ecf20Sopenharmony_ci struct clk **gates; 1948c2ecf20Sopenharmony_ci int num_gates; 1958c2ecf20Sopenharmony_ci void __iomem *base; 1968c2ecf20Sopenharmony_ci u32 saved_reg; 1978c2ecf20Sopenharmony_ci}; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic struct clk_gating_ctrl *ctrl; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_cistatic struct clk *clk_gating_get_src( 2028c2ecf20Sopenharmony_ci struct of_phandle_args *clkspec, void *data) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci int n; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (clkspec->args_count < 1) 2078c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci for (n = 0; n < ctrl->num_gates; n++) { 2108c2ecf20Sopenharmony_ci struct clk_gate *gate = 2118c2ecf20Sopenharmony_ci to_clk_gate(__clk_get_hw(ctrl->gates[n])); 2128c2ecf20Sopenharmony_ci if (clkspec->args[0] == gate->bit_idx) 2138c2ecf20Sopenharmony_ci return ctrl->gates[n]; 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int mvebu_clk_gating_suspend(void) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci ctrl->saved_reg = readl(ctrl->base); 2218c2ecf20Sopenharmony_ci return 0; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void mvebu_clk_gating_resume(void) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci writel(ctrl->saved_reg, ctrl->base); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic struct syscore_ops clk_gate_syscore_ops = { 2308c2ecf20Sopenharmony_ci .suspend = mvebu_clk_gating_suspend, 2318c2ecf20Sopenharmony_ci .resume = mvebu_clk_gating_resume, 2328c2ecf20Sopenharmony_ci}; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_civoid __init mvebu_clk_gating_setup(struct device_node *np, 2358c2ecf20Sopenharmony_ci const struct clk_gating_soc_desc *desc) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct clk *clk; 2388c2ecf20Sopenharmony_ci void __iomem *base; 2398c2ecf20Sopenharmony_ci const char *default_parent = NULL; 2408c2ecf20Sopenharmony_ci int n; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (ctrl) { 2438c2ecf20Sopenharmony_ci pr_err("mvebu-clk-gating: cannot instantiate more than one gateable clock device\n"); 2448c2ecf20Sopenharmony_ci return; 2458c2ecf20Sopenharmony_ci } 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci base = of_iomap(np, 0); 2488c2ecf20Sopenharmony_ci if (WARN_ON(!base)) 2498c2ecf20Sopenharmony_ci return; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci clk = of_clk_get(np, 0); 2528c2ecf20Sopenharmony_ci if (!IS_ERR(clk)) { 2538c2ecf20Sopenharmony_ci default_parent = __clk_get_name(clk); 2548c2ecf20Sopenharmony_ci clk_put(clk); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL); 2588c2ecf20Sopenharmony_ci if (WARN_ON(!ctrl)) 2598c2ecf20Sopenharmony_ci goto ctrl_out; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci /* lock must already be initialized */ 2628c2ecf20Sopenharmony_ci ctrl->lock = &ctrl_gating_lock; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci ctrl->base = base; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci /* Count, allocate, and register clock gates */ 2678c2ecf20Sopenharmony_ci for (n = 0; desc[n].name;) 2688c2ecf20Sopenharmony_ci n++; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci ctrl->num_gates = n; 2718c2ecf20Sopenharmony_ci ctrl->gates = kcalloc(ctrl->num_gates, sizeof(*ctrl->gates), 2728c2ecf20Sopenharmony_ci GFP_KERNEL); 2738c2ecf20Sopenharmony_ci if (WARN_ON(!ctrl->gates)) 2748c2ecf20Sopenharmony_ci goto gates_out; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci for (n = 0; n < ctrl->num_gates; n++) { 2778c2ecf20Sopenharmony_ci const char *parent = 2788c2ecf20Sopenharmony_ci (desc[n].parent) ? desc[n].parent : default_parent; 2798c2ecf20Sopenharmony_ci ctrl->gates[n] = clk_register_gate(NULL, desc[n].name, parent, 2808c2ecf20Sopenharmony_ci desc[n].flags, base, desc[n].bit_idx, 2818c2ecf20Sopenharmony_ci 0, ctrl->lock); 2828c2ecf20Sopenharmony_ci WARN_ON(IS_ERR(ctrl->gates[n])); 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci of_clk_add_provider(np, clk_gating_get_src, ctrl); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci register_syscore_ops(&clk_gate_syscore_ops); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci return; 2908c2ecf20Sopenharmony_cigates_out: 2918c2ecf20Sopenharmony_ci kfree(ctrl); 2928c2ecf20Sopenharmony_cictrl_out: 2938c2ecf20Sopenharmony_ci iounmap(base); 2948c2ecf20Sopenharmony_ci} 295