18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Helper routines for SuperH Clock Pulse Generator blocks (CPG). 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2010 Magnus Damm 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 - 2012 Paul Mundt 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 88c2ecf20Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 98c2ecf20Sopenharmony_ci * for more details. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/clk.h> 128c2ecf20Sopenharmony_ci#include <linux/compiler.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/sh_clk.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define CPG_CKSTP_BIT BIT(8) 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic unsigned int sh_clk_read(struct clk *clk) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci if (clk->flags & CLK_ENABLE_REG_8BIT) 228c2ecf20Sopenharmony_ci return ioread8(clk->mapped_reg); 238c2ecf20Sopenharmony_ci else if (clk->flags & CLK_ENABLE_REG_16BIT) 248c2ecf20Sopenharmony_ci return ioread16(clk->mapped_reg); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci return ioread32(clk->mapped_reg); 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic void sh_clk_write(int value, struct clk *clk) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci if (clk->flags & CLK_ENABLE_REG_8BIT) 328c2ecf20Sopenharmony_ci iowrite8(value, clk->mapped_reg); 338c2ecf20Sopenharmony_ci else if (clk->flags & CLK_ENABLE_REG_16BIT) 348c2ecf20Sopenharmony_ci iowrite16(value, clk->mapped_reg); 358c2ecf20Sopenharmony_ci else 368c2ecf20Sopenharmony_ci iowrite32(value, clk->mapped_reg); 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int sh_clk_mstp_enable(struct clk *clk) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci sh_clk_write(sh_clk_read(clk) & ~(1 << clk->enable_bit), clk); 428c2ecf20Sopenharmony_ci if (clk->status_reg) { 438c2ecf20Sopenharmony_ci unsigned int (*read)(const void __iomem *addr); 448c2ecf20Sopenharmony_ci int i; 458c2ecf20Sopenharmony_ci void __iomem *mapped_status = (phys_addr_t)clk->status_reg - 468c2ecf20Sopenharmony_ci (phys_addr_t)clk->enable_reg + clk->mapped_reg; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (clk->flags & CLK_ENABLE_REG_8BIT) 498c2ecf20Sopenharmony_ci read = ioread8; 508c2ecf20Sopenharmony_ci else if (clk->flags & CLK_ENABLE_REG_16BIT) 518c2ecf20Sopenharmony_ci read = ioread16; 528c2ecf20Sopenharmony_ci else 538c2ecf20Sopenharmony_ci read = ioread32; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci for (i = 1000; 568c2ecf20Sopenharmony_ci (read(mapped_status) & (1 << clk->enable_bit)) && i; 578c2ecf20Sopenharmony_ci i--) 588c2ecf20Sopenharmony_ci cpu_relax(); 598c2ecf20Sopenharmony_ci if (!i) { 608c2ecf20Sopenharmony_ci pr_err("cpg: failed to enable %p[%d]\n", 618c2ecf20Sopenharmony_ci clk->enable_reg, clk->enable_bit); 628c2ecf20Sopenharmony_ci return -ETIMEDOUT; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void sh_clk_mstp_disable(struct clk *clk) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci sh_clk_write(sh_clk_read(clk) | (1 << clk->enable_bit), clk); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic struct sh_clk_ops sh_clk_mstp_clk_ops = { 748c2ecf20Sopenharmony_ci .enable = sh_clk_mstp_enable, 758c2ecf20Sopenharmony_ci .disable = sh_clk_mstp_disable, 768c2ecf20Sopenharmony_ci .recalc = followparent_recalc, 778c2ecf20Sopenharmony_ci}; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ciint __init sh_clk_mstp_register(struct clk *clks, int nr) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct clk *clkp; 828c2ecf20Sopenharmony_ci int ret = 0; 838c2ecf20Sopenharmony_ci int k; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci for (k = 0; !ret && (k < nr); k++) { 868c2ecf20Sopenharmony_ci clkp = clks + k; 878c2ecf20Sopenharmony_ci clkp->ops = &sh_clk_mstp_clk_ops; 888c2ecf20Sopenharmony_ci ret |= clk_register(clkp); 898c2ecf20Sopenharmony_ci } 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci return ret; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci/* 958c2ecf20Sopenharmony_ci * Div/mult table lookup helpers 968c2ecf20Sopenharmony_ci */ 978c2ecf20Sopenharmony_cistatic inline struct clk_div_table *clk_to_div_table(struct clk *clk) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci return clk->priv; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic inline struct clk_div_mult_table *clk_to_div_mult_table(struct clk *clk) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci return clk_to_div_table(clk)->div_mult_table; 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* 1088c2ecf20Sopenharmony_ci * Common div ops 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_cistatic long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci return clk_rate_table_round(clk, clk->freq_table, rate); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic unsigned long sh_clk_div_recalc(struct clk *clk) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci struct clk_div_mult_table *table = clk_to_div_mult_table(clk); 1188c2ecf20Sopenharmony_ci unsigned int idx; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, 1218c2ecf20Sopenharmony_ci table, clk->arch_flags ? &clk->arch_flags : NULL); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci return clk->freq_table[idx].frequency; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int sh_clk_div_set_rate(struct clk *clk, unsigned long rate) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct clk_div_table *dt = clk_to_div_table(clk); 1318c2ecf20Sopenharmony_ci unsigned long value; 1328c2ecf20Sopenharmony_ci int idx; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci idx = clk_rate_table_find(clk, clk->freq_table, rate); 1358c2ecf20Sopenharmony_ci if (idx < 0) 1368c2ecf20Sopenharmony_ci return idx; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci value = sh_clk_read(clk); 1398c2ecf20Sopenharmony_ci value &= ~(clk->div_mask << clk->enable_bit); 1408c2ecf20Sopenharmony_ci value |= (idx << clk->enable_bit); 1418c2ecf20Sopenharmony_ci sh_clk_write(value, clk); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* XXX: Should use a post-change notifier */ 1448c2ecf20Sopenharmony_ci if (dt->kick) 1458c2ecf20Sopenharmony_ci dt->kick(clk); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci return 0; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int sh_clk_div_enable(struct clk *clk) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci if (clk->div_mask == SH_CLK_DIV6_MSK) { 1538c2ecf20Sopenharmony_ci int ret = sh_clk_div_set_rate(clk, clk->rate); 1548c2ecf20Sopenharmony_ci if (ret < 0) 1558c2ecf20Sopenharmony_ci return ret; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk); 1598c2ecf20Sopenharmony_ci return 0; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic void sh_clk_div_disable(struct clk *clk) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci unsigned int val; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci val = sh_clk_read(clk); 1678c2ecf20Sopenharmony_ci val |= CPG_CKSTP_BIT; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * div6 clocks require the divisor field to be non-zero or the 1718c2ecf20Sopenharmony_ci * above CKSTP toggle silently fails. Ensure that the divisor 1728c2ecf20Sopenharmony_ci * array is reset to its initial state on disable. 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci if (clk->flags & CLK_MASK_DIV_ON_DISABLE) 1758c2ecf20Sopenharmony_ci val |= clk->div_mask; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci sh_clk_write(val, clk); 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic struct sh_clk_ops sh_clk_div_clk_ops = { 1818c2ecf20Sopenharmony_ci .recalc = sh_clk_div_recalc, 1828c2ecf20Sopenharmony_ci .set_rate = sh_clk_div_set_rate, 1838c2ecf20Sopenharmony_ci .round_rate = sh_clk_div_round_rate, 1848c2ecf20Sopenharmony_ci}; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic struct sh_clk_ops sh_clk_div_enable_clk_ops = { 1878c2ecf20Sopenharmony_ci .recalc = sh_clk_div_recalc, 1888c2ecf20Sopenharmony_ci .set_rate = sh_clk_div_set_rate, 1898c2ecf20Sopenharmony_ci .round_rate = sh_clk_div_round_rate, 1908c2ecf20Sopenharmony_ci .enable = sh_clk_div_enable, 1918c2ecf20Sopenharmony_ci .disable = sh_clk_div_disable, 1928c2ecf20Sopenharmony_ci}; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int __init sh_clk_init_parent(struct clk *clk) 1958c2ecf20Sopenharmony_ci{ 1968c2ecf20Sopenharmony_ci u32 val; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci if (clk->parent) 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (!clk->parent_table || !clk->parent_num) 2028c2ecf20Sopenharmony_ci return 0; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (!clk->src_width) { 2058c2ecf20Sopenharmony_ci pr_err("sh_clk_init_parent: cannot select parent clock\n"); 2068c2ecf20Sopenharmony_ci return -EINVAL; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci val = (sh_clk_read(clk) >> clk->src_shift); 2108c2ecf20Sopenharmony_ci val &= (1 << clk->src_width) - 1; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (val >= clk->parent_num) { 2138c2ecf20Sopenharmony_ci pr_err("sh_clk_init_parent: parent table size failed\n"); 2148c2ecf20Sopenharmony_ci return -EINVAL; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci clk_reparent(clk, clk->parent_table[val]); 2188c2ecf20Sopenharmony_ci if (!clk->parent) { 2198c2ecf20Sopenharmony_ci pr_err("sh_clk_init_parent: unable to set parent"); 2208c2ecf20Sopenharmony_ci return -EINVAL; 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int __init sh_clk_div_register_ops(struct clk *clks, int nr, 2278c2ecf20Sopenharmony_ci struct clk_div_table *table, struct sh_clk_ops *ops) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci struct clk *clkp; 2308c2ecf20Sopenharmony_ci void *freq_table; 2318c2ecf20Sopenharmony_ci int nr_divs = table->div_mult_table->nr_divisors; 2328c2ecf20Sopenharmony_ci int freq_table_size = sizeof(struct cpufreq_frequency_table); 2338c2ecf20Sopenharmony_ci int ret = 0; 2348c2ecf20Sopenharmony_ci int k; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci freq_table_size *= (nr_divs + 1); 2378c2ecf20Sopenharmony_ci freq_table = kcalloc(nr, freq_table_size, GFP_KERNEL); 2388c2ecf20Sopenharmony_ci if (!freq_table) { 2398c2ecf20Sopenharmony_ci pr_err("%s: unable to alloc memory\n", __func__); 2408c2ecf20Sopenharmony_ci return -ENOMEM; 2418c2ecf20Sopenharmony_ci } 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci for (k = 0; !ret && (k < nr); k++) { 2448c2ecf20Sopenharmony_ci clkp = clks + k; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci clkp->ops = ops; 2478c2ecf20Sopenharmony_ci clkp->priv = table; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci clkp->freq_table = freq_table + (k * freq_table_size); 2508c2ecf20Sopenharmony_ci clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci ret = clk_register(clkp); 2538c2ecf20Sopenharmony_ci if (ret == 0) 2548c2ecf20Sopenharmony_ci ret = sh_clk_init_parent(clkp); 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci return ret; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci/* 2618c2ecf20Sopenharmony_ci * div6 support 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_cistatic int sh_clk_div6_divisors[64] = { 2648c2ecf20Sopenharmony_ci 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 2658c2ecf20Sopenharmony_ci 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 2668c2ecf20Sopenharmony_ci 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 2678c2ecf20Sopenharmony_ci 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 2688c2ecf20Sopenharmony_ci}; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic struct clk_div_mult_table div6_div_mult_table = { 2718c2ecf20Sopenharmony_ci .divisors = sh_clk_div6_divisors, 2728c2ecf20Sopenharmony_ci .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors), 2738c2ecf20Sopenharmony_ci}; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic struct clk_div_table sh_clk_div6_table = { 2768c2ecf20Sopenharmony_ci .div_mult_table = &div6_div_mult_table, 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct clk_div_mult_table *table = clk_to_div_mult_table(clk); 2828c2ecf20Sopenharmony_ci u32 value; 2838c2ecf20Sopenharmony_ci int ret, i; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (!clk->parent_table || !clk->parent_num) 2868c2ecf20Sopenharmony_ci return -EINVAL; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci /* Search the parent */ 2898c2ecf20Sopenharmony_ci for (i = 0; i < clk->parent_num; i++) 2908c2ecf20Sopenharmony_ci if (clk->parent_table[i] == parent) 2918c2ecf20Sopenharmony_ci break; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (i == clk->parent_num) 2948c2ecf20Sopenharmony_ci return -ENODEV; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci ret = clk_reparent(clk, parent); 2978c2ecf20Sopenharmony_ci if (ret < 0) 2988c2ecf20Sopenharmony_ci return ret; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci value = sh_clk_read(clk) & 3018c2ecf20Sopenharmony_ci ~(((1 << clk->src_width) - 1) << clk->src_shift); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci sh_clk_write(value | (i << clk->src_shift), clk); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci /* Rebuild the frequency table */ 3068c2ecf20Sopenharmony_ci clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, 3078c2ecf20Sopenharmony_ci table, NULL); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci return 0; 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { 3138c2ecf20Sopenharmony_ci .recalc = sh_clk_div_recalc, 3148c2ecf20Sopenharmony_ci .round_rate = sh_clk_div_round_rate, 3158c2ecf20Sopenharmony_ci .set_rate = sh_clk_div_set_rate, 3168c2ecf20Sopenharmony_ci .enable = sh_clk_div_enable, 3178c2ecf20Sopenharmony_ci .disable = sh_clk_div_disable, 3188c2ecf20Sopenharmony_ci .set_parent = sh_clk_div6_set_parent, 3198c2ecf20Sopenharmony_ci}; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ciint __init sh_clk_div6_register(struct clk *clks, int nr) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table, 3248c2ecf20Sopenharmony_ci &sh_clk_div_enable_clk_ops); 3258c2ecf20Sopenharmony_ci} 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ciint __init sh_clk_div6_reparent_register(struct clk *clks, int nr) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table, 3308c2ecf20Sopenharmony_ci &sh_clk_div6_reparent_clk_ops); 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci/* 3348c2ecf20Sopenharmony_ci * div4 support 3358c2ecf20Sopenharmony_ci */ 3368c2ecf20Sopenharmony_cistatic int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct clk_div_mult_table *table = clk_to_div_mult_table(clk); 3398c2ecf20Sopenharmony_ci u32 value; 3408c2ecf20Sopenharmony_ci int ret; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* we really need a better way to determine parent index, but for 3438c2ecf20Sopenharmony_ci * now assume internal parent comes with CLK_ENABLE_ON_INIT set, 3448c2ecf20Sopenharmony_ci * no CLK_ENABLE_ON_INIT means external clock... 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (parent->flags & CLK_ENABLE_ON_INIT) 3488c2ecf20Sopenharmony_ci value = sh_clk_read(clk) & ~(1 << 7); 3498c2ecf20Sopenharmony_ci else 3508c2ecf20Sopenharmony_ci value = sh_clk_read(clk) | (1 << 7); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci ret = clk_reparent(clk, parent); 3538c2ecf20Sopenharmony_ci if (ret < 0) 3548c2ecf20Sopenharmony_ci return ret; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci sh_clk_write(value, clk); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci /* Rebiuld the frequency table */ 3598c2ecf20Sopenharmony_ci clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, 3608c2ecf20Sopenharmony_ci table, &clk->arch_flags); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci return 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { 3668c2ecf20Sopenharmony_ci .recalc = sh_clk_div_recalc, 3678c2ecf20Sopenharmony_ci .set_rate = sh_clk_div_set_rate, 3688c2ecf20Sopenharmony_ci .round_rate = sh_clk_div_round_rate, 3698c2ecf20Sopenharmony_ci .enable = sh_clk_div_enable, 3708c2ecf20Sopenharmony_ci .disable = sh_clk_div_disable, 3718c2ecf20Sopenharmony_ci .set_parent = sh_clk_div4_set_parent, 3728c2ecf20Sopenharmony_ci}; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ciint __init sh_clk_div4_register(struct clk *clks, int nr, 3758c2ecf20Sopenharmony_ci struct clk_div4_table *table) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci return sh_clk_div_register_ops(clks, nr, table, &sh_clk_div_clk_ops); 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ciint __init sh_clk_div4_enable_register(struct clk *clks, int nr, 3818c2ecf20Sopenharmony_ci struct clk_div4_table *table) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci return sh_clk_div_register_ops(clks, nr, table, 3848c2ecf20Sopenharmony_ci &sh_clk_div_enable_clk_ops); 3858c2ecf20Sopenharmony_ci} 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ciint __init sh_clk_div4_reparent_register(struct clk *clks, int nr, 3888c2ecf20Sopenharmony_ci struct clk_div4_table *table) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci return sh_clk_div_register_ops(clks, nr, table, 3918c2ecf20Sopenharmony_ci &sh_clk_div4_reparent_clk_ops); 3928c2ecf20Sopenharmony_ci} 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci/* FSI-DIV */ 3958c2ecf20Sopenharmony_cistatic unsigned long fsidiv_recalc(struct clk *clk) 3968c2ecf20Sopenharmony_ci{ 3978c2ecf20Sopenharmony_ci u32 value; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci value = __raw_readl(clk->mapping->base); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci value >>= 16; 4028c2ecf20Sopenharmony_ci if (value < 2) 4038c2ecf20Sopenharmony_ci return clk->parent->rate; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci return clk->parent->rate / value; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic long fsidiv_round_rate(struct clk *clk, unsigned long rate) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci return clk_rate_div_range_round(clk, 1, 0xffff, rate); 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic void fsidiv_disable(struct clk *clk) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci __raw_writel(0, clk->mapping->base); 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic int fsidiv_enable(struct clk *clk) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci u32 value; 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci value = __raw_readl(clk->mapping->base) >> 16; 4238c2ecf20Sopenharmony_ci if (value < 2) 4248c2ecf20Sopenharmony_ci return 0; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci __raw_writel((value << 16) | 0x3, clk->mapping->base); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int fsidiv_set_rate(struct clk *clk, unsigned long rate) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci int idx; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci idx = (clk->parent->rate / rate) & 0xffff; 4368c2ecf20Sopenharmony_ci if (idx < 2) 4378c2ecf20Sopenharmony_ci __raw_writel(0, clk->mapping->base); 4388c2ecf20Sopenharmony_ci else 4398c2ecf20Sopenharmony_ci __raw_writel(idx << 16, clk->mapping->base); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci return 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic struct sh_clk_ops fsidiv_clk_ops = { 4458c2ecf20Sopenharmony_ci .recalc = fsidiv_recalc, 4468c2ecf20Sopenharmony_ci .round_rate = fsidiv_round_rate, 4478c2ecf20Sopenharmony_ci .set_rate = fsidiv_set_rate, 4488c2ecf20Sopenharmony_ci .enable = fsidiv_enable, 4498c2ecf20Sopenharmony_ci .disable = fsidiv_disable, 4508c2ecf20Sopenharmony_ci}; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ciint __init sh_clk_fsidiv_register(struct clk *clks, int nr) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci struct clk_mapping *map; 4558c2ecf20Sopenharmony_ci int i; 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci for (i = 0; i < nr; i++) { 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci map = kzalloc(sizeof(struct clk_mapping), GFP_KERNEL); 4608c2ecf20Sopenharmony_ci if (!map) { 4618c2ecf20Sopenharmony_ci pr_err("%s: unable to alloc memory\n", __func__); 4628c2ecf20Sopenharmony_ci return -ENOMEM; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* clks[i].enable_reg came from SH_CLK_FSIDIV() */ 4668c2ecf20Sopenharmony_ci map->phys = (phys_addr_t)clks[i].enable_reg; 4678c2ecf20Sopenharmony_ci map->len = 8; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci clks[i].enable_reg = 0; /* remove .enable_reg */ 4708c2ecf20Sopenharmony_ci clks[i].ops = &fsidiv_clk_ops; 4718c2ecf20Sopenharmony_ci clks[i].mapping = map; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci clk_register(&clks[i]); 4748c2ecf20Sopenharmony_ci } 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return 0; 4778c2ecf20Sopenharmony_ci} 478