162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Helper routines for SuperH Clock Pulse Generator blocks (CPG). 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2010 Magnus Damm 562306a36Sopenharmony_ci * Copyright (C) 2010 - 2012 Paul Mundt 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 862306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 962306a36Sopenharmony_ci * for more details. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/clk.h> 1262306a36Sopenharmony_ci#include <linux/compiler.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/sh_clk.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#define CPG_CKSTP_BIT BIT(8) 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic unsigned int sh_clk_read(struct clk *clk) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci if (clk->flags & CLK_ENABLE_REG_8BIT) 2262306a36Sopenharmony_ci return ioread8(clk->mapped_reg); 2362306a36Sopenharmony_ci else if (clk->flags & CLK_ENABLE_REG_16BIT) 2462306a36Sopenharmony_ci return ioread16(clk->mapped_reg); 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci return ioread32(clk->mapped_reg); 2762306a36Sopenharmony_ci} 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic void sh_clk_write(int value, struct clk *clk) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci if (clk->flags & CLK_ENABLE_REG_8BIT) 3262306a36Sopenharmony_ci iowrite8(value, clk->mapped_reg); 3362306a36Sopenharmony_ci else if (clk->flags & CLK_ENABLE_REG_16BIT) 3462306a36Sopenharmony_ci iowrite16(value, clk->mapped_reg); 3562306a36Sopenharmony_ci else 3662306a36Sopenharmony_ci iowrite32(value, clk->mapped_reg); 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int sh_clk_mstp_enable(struct clk *clk) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci sh_clk_write(sh_clk_read(clk) & ~(1 << clk->enable_bit), clk); 4262306a36Sopenharmony_ci if (clk->status_reg) { 4362306a36Sopenharmony_ci unsigned int (*read)(const void __iomem *addr); 4462306a36Sopenharmony_ci int i; 4562306a36Sopenharmony_ci void __iomem *mapped_status = (phys_addr_t)clk->status_reg - 4662306a36Sopenharmony_ci (phys_addr_t)clk->enable_reg + clk->mapped_reg; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (clk->flags & CLK_ENABLE_REG_8BIT) 4962306a36Sopenharmony_ci read = ioread8; 5062306a36Sopenharmony_ci else if (clk->flags & CLK_ENABLE_REG_16BIT) 5162306a36Sopenharmony_ci read = ioread16; 5262306a36Sopenharmony_ci else 5362306a36Sopenharmony_ci read = ioread32; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci for (i = 1000; 5662306a36Sopenharmony_ci (read(mapped_status) & (1 << clk->enable_bit)) && i; 5762306a36Sopenharmony_ci i--) 5862306a36Sopenharmony_ci cpu_relax(); 5962306a36Sopenharmony_ci if (!i) { 6062306a36Sopenharmony_ci pr_err("cpg: failed to enable %p[%d]\n", 6162306a36Sopenharmony_ci clk->enable_reg, clk->enable_bit); 6262306a36Sopenharmony_ci return -ETIMEDOUT; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci } 6562306a36Sopenharmony_ci return 0; 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void sh_clk_mstp_disable(struct clk *clk) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci sh_clk_write(sh_clk_read(clk) | (1 << clk->enable_bit), clk); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic struct sh_clk_ops sh_clk_mstp_clk_ops = { 7462306a36Sopenharmony_ci .enable = sh_clk_mstp_enable, 7562306a36Sopenharmony_ci .disable = sh_clk_mstp_disable, 7662306a36Sopenharmony_ci .recalc = followparent_recalc, 7762306a36Sopenharmony_ci}; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ciint __init sh_clk_mstp_register(struct clk *clks, int nr) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci struct clk *clkp; 8262306a36Sopenharmony_ci int ret = 0; 8362306a36Sopenharmony_ci int k; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci for (k = 0; !ret && (k < nr); k++) { 8662306a36Sopenharmony_ci clkp = clks + k; 8762306a36Sopenharmony_ci clkp->ops = &sh_clk_mstp_clk_ops; 8862306a36Sopenharmony_ci ret |= clk_register(clkp); 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci return ret; 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* 9562306a36Sopenharmony_ci * Div/mult table lookup helpers 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cistatic inline struct clk_div_table *clk_to_div_table(struct clk *clk) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci return clk->priv; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic inline struct clk_div_mult_table *clk_to_div_mult_table(struct clk *clk) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci return clk_to_div_table(clk)->div_mult_table; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/* 10862306a36Sopenharmony_ci * Common div ops 10962306a36Sopenharmony_ci */ 11062306a36Sopenharmony_cistatic long sh_clk_div_round_rate(struct clk *clk, unsigned long rate) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci return clk_rate_table_round(clk, clk->freq_table, rate); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_cistatic unsigned long sh_clk_div_recalc(struct clk *clk) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct clk_div_mult_table *table = clk_to_div_mult_table(clk); 11862306a36Sopenharmony_ci unsigned int idx; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, 12162306a36Sopenharmony_ci table, clk->arch_flags ? &clk->arch_flags : NULL); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci idx = (sh_clk_read(clk) >> clk->enable_bit) & clk->div_mask; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return clk->freq_table[idx].frequency; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int sh_clk_div_set_rate(struct clk *clk, unsigned long rate) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct clk_div_table *dt = clk_to_div_table(clk); 13162306a36Sopenharmony_ci unsigned long value; 13262306a36Sopenharmony_ci int idx; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci idx = clk_rate_table_find(clk, clk->freq_table, rate); 13562306a36Sopenharmony_ci if (idx < 0) 13662306a36Sopenharmony_ci return idx; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci value = sh_clk_read(clk); 13962306a36Sopenharmony_ci value &= ~(clk->div_mask << clk->enable_bit); 14062306a36Sopenharmony_ci value |= (idx << clk->enable_bit); 14162306a36Sopenharmony_ci sh_clk_write(value, clk); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* XXX: Should use a post-change notifier */ 14462306a36Sopenharmony_ci if (dt->kick) 14562306a36Sopenharmony_ci dt->kick(clk); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int sh_clk_div_enable(struct clk *clk) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci if (clk->div_mask == SH_CLK_DIV6_MSK) { 15362306a36Sopenharmony_ci int ret = sh_clk_div_set_rate(clk, clk->rate); 15462306a36Sopenharmony_ci if (ret < 0) 15562306a36Sopenharmony_ci return ret; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci sh_clk_write(sh_clk_read(clk) & ~CPG_CKSTP_BIT, clk); 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic void sh_clk_div_disable(struct clk *clk) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci unsigned int val; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci val = sh_clk_read(clk); 16762306a36Sopenharmony_ci val |= CPG_CKSTP_BIT; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* 17062306a36Sopenharmony_ci * div6 clocks require the divisor field to be non-zero or the 17162306a36Sopenharmony_ci * above CKSTP toggle silently fails. Ensure that the divisor 17262306a36Sopenharmony_ci * array is reset to its initial state on disable. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci if (clk->flags & CLK_MASK_DIV_ON_DISABLE) 17562306a36Sopenharmony_ci val |= clk->div_mask; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci sh_clk_write(val, clk); 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic struct sh_clk_ops sh_clk_div_clk_ops = { 18162306a36Sopenharmony_ci .recalc = sh_clk_div_recalc, 18262306a36Sopenharmony_ci .set_rate = sh_clk_div_set_rate, 18362306a36Sopenharmony_ci .round_rate = sh_clk_div_round_rate, 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic struct sh_clk_ops sh_clk_div_enable_clk_ops = { 18762306a36Sopenharmony_ci .recalc = sh_clk_div_recalc, 18862306a36Sopenharmony_ci .set_rate = sh_clk_div_set_rate, 18962306a36Sopenharmony_ci .round_rate = sh_clk_div_round_rate, 19062306a36Sopenharmony_ci .enable = sh_clk_div_enable, 19162306a36Sopenharmony_ci .disable = sh_clk_div_disable, 19262306a36Sopenharmony_ci}; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int __init sh_clk_init_parent(struct clk *clk) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci u32 val; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (clk->parent) 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (!clk->parent_table || !clk->parent_num) 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (!clk->src_width) { 20562306a36Sopenharmony_ci pr_err("sh_clk_init_parent: cannot select parent clock\n"); 20662306a36Sopenharmony_ci return -EINVAL; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci val = (sh_clk_read(clk) >> clk->src_shift); 21062306a36Sopenharmony_ci val &= (1 << clk->src_width) - 1; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (val >= clk->parent_num) { 21362306a36Sopenharmony_ci pr_err("sh_clk_init_parent: parent table size failed\n"); 21462306a36Sopenharmony_ci return -EINVAL; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci clk_reparent(clk, clk->parent_table[val]); 21862306a36Sopenharmony_ci if (!clk->parent) { 21962306a36Sopenharmony_ci pr_err("sh_clk_init_parent: unable to set parent"); 22062306a36Sopenharmony_ci return -EINVAL; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci return 0; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int __init sh_clk_div_register_ops(struct clk *clks, int nr, 22762306a36Sopenharmony_ci struct clk_div_table *table, struct sh_clk_ops *ops) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct clk *clkp; 23062306a36Sopenharmony_ci void *freq_table; 23162306a36Sopenharmony_ci int nr_divs = table->div_mult_table->nr_divisors; 23262306a36Sopenharmony_ci int freq_table_size = sizeof(struct cpufreq_frequency_table); 23362306a36Sopenharmony_ci int ret = 0; 23462306a36Sopenharmony_ci int k; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci freq_table_size *= (nr_divs + 1); 23762306a36Sopenharmony_ci freq_table = kcalloc(nr, freq_table_size, GFP_KERNEL); 23862306a36Sopenharmony_ci if (!freq_table) { 23962306a36Sopenharmony_ci pr_err("%s: unable to alloc memory\n", __func__); 24062306a36Sopenharmony_ci return -ENOMEM; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci for (k = 0; !ret && (k < nr); k++) { 24462306a36Sopenharmony_ci clkp = clks + k; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci clkp->ops = ops; 24762306a36Sopenharmony_ci clkp->priv = table; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci clkp->freq_table = freq_table + (k * freq_table_size); 25062306a36Sopenharmony_ci clkp->freq_table[nr_divs].frequency = CPUFREQ_TABLE_END; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci ret = clk_register(clkp); 25362306a36Sopenharmony_ci if (ret == 0) 25462306a36Sopenharmony_ci ret = sh_clk_init_parent(clkp); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci return ret; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci/* 26162306a36Sopenharmony_ci * div6 support 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistatic int sh_clk_div6_divisors[64] = { 26462306a36Sopenharmony_ci 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 26562306a36Sopenharmony_ci 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 26662306a36Sopenharmony_ci 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 26762306a36Sopenharmony_ci 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 26862306a36Sopenharmony_ci}; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_cistatic struct clk_div_mult_table div6_div_mult_table = { 27162306a36Sopenharmony_ci .divisors = sh_clk_div6_divisors, 27262306a36Sopenharmony_ci .nr_divisors = ARRAY_SIZE(sh_clk_div6_divisors), 27362306a36Sopenharmony_ci}; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic struct clk_div_table sh_clk_div6_table = { 27662306a36Sopenharmony_ci .div_mult_table = &div6_div_mult_table, 27762306a36Sopenharmony_ci}; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cistatic int sh_clk_div6_set_parent(struct clk *clk, struct clk *parent) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct clk_div_mult_table *table = clk_to_div_mult_table(clk); 28262306a36Sopenharmony_ci u32 value; 28362306a36Sopenharmony_ci int ret, i; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (!clk->parent_table || !clk->parent_num) 28662306a36Sopenharmony_ci return -EINVAL; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* Search the parent */ 28962306a36Sopenharmony_ci for (i = 0; i < clk->parent_num; i++) 29062306a36Sopenharmony_ci if (clk->parent_table[i] == parent) 29162306a36Sopenharmony_ci break; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci if (i == clk->parent_num) 29462306a36Sopenharmony_ci return -ENODEV; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci ret = clk_reparent(clk, parent); 29762306a36Sopenharmony_ci if (ret < 0) 29862306a36Sopenharmony_ci return ret; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci value = sh_clk_read(clk) & 30162306a36Sopenharmony_ci ~(((1 << clk->src_width) - 1) << clk->src_shift); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci sh_clk_write(value | (i << clk->src_shift), clk); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci /* Rebuild the frequency table */ 30662306a36Sopenharmony_ci clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, 30762306a36Sopenharmony_ci table, NULL); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic struct sh_clk_ops sh_clk_div6_reparent_clk_ops = { 31362306a36Sopenharmony_ci .recalc = sh_clk_div_recalc, 31462306a36Sopenharmony_ci .round_rate = sh_clk_div_round_rate, 31562306a36Sopenharmony_ci .set_rate = sh_clk_div_set_rate, 31662306a36Sopenharmony_ci .enable = sh_clk_div_enable, 31762306a36Sopenharmony_ci .disable = sh_clk_div_disable, 31862306a36Sopenharmony_ci .set_parent = sh_clk_div6_set_parent, 31962306a36Sopenharmony_ci}; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ciint __init sh_clk_div6_register(struct clk *clks, int nr) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table, 32462306a36Sopenharmony_ci &sh_clk_div_enable_clk_ops); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ciint __init sh_clk_div6_reparent_register(struct clk *clks, int nr) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci return sh_clk_div_register_ops(clks, nr, &sh_clk_div6_table, 33062306a36Sopenharmony_ci &sh_clk_div6_reparent_clk_ops); 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci/* 33462306a36Sopenharmony_ci * div4 support 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_cistatic int sh_clk_div4_set_parent(struct clk *clk, struct clk *parent) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct clk_div_mult_table *table = clk_to_div_mult_table(clk); 33962306a36Sopenharmony_ci u32 value; 34062306a36Sopenharmony_ci int ret; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* we really need a better way to determine parent index, but for 34362306a36Sopenharmony_ci * now assume internal parent comes with CLK_ENABLE_ON_INIT set, 34462306a36Sopenharmony_ci * no CLK_ENABLE_ON_INIT means external clock... 34562306a36Sopenharmony_ci */ 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (parent->flags & CLK_ENABLE_ON_INIT) 34862306a36Sopenharmony_ci value = sh_clk_read(clk) & ~(1 << 7); 34962306a36Sopenharmony_ci else 35062306a36Sopenharmony_ci value = sh_clk_read(clk) | (1 << 7); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci ret = clk_reparent(clk, parent); 35362306a36Sopenharmony_ci if (ret < 0) 35462306a36Sopenharmony_ci return ret; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci sh_clk_write(value, clk); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci /* Rebiuld the frequency table */ 35962306a36Sopenharmony_ci clk_rate_table_build(clk, clk->freq_table, table->nr_divisors, 36062306a36Sopenharmony_ci table, &clk->arch_flags); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return 0; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic struct sh_clk_ops sh_clk_div4_reparent_clk_ops = { 36662306a36Sopenharmony_ci .recalc = sh_clk_div_recalc, 36762306a36Sopenharmony_ci .set_rate = sh_clk_div_set_rate, 36862306a36Sopenharmony_ci .round_rate = sh_clk_div_round_rate, 36962306a36Sopenharmony_ci .enable = sh_clk_div_enable, 37062306a36Sopenharmony_ci .disable = sh_clk_div_disable, 37162306a36Sopenharmony_ci .set_parent = sh_clk_div4_set_parent, 37262306a36Sopenharmony_ci}; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ciint __init sh_clk_div4_register(struct clk *clks, int nr, 37562306a36Sopenharmony_ci struct clk_div4_table *table) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci return sh_clk_div_register_ops(clks, nr, table, &sh_clk_div_clk_ops); 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ciint __init sh_clk_div4_enable_register(struct clk *clks, int nr, 38162306a36Sopenharmony_ci struct clk_div4_table *table) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci return sh_clk_div_register_ops(clks, nr, table, 38462306a36Sopenharmony_ci &sh_clk_div_enable_clk_ops); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ciint __init sh_clk_div4_reparent_register(struct clk *clks, int nr, 38862306a36Sopenharmony_ci struct clk_div4_table *table) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci return sh_clk_div_register_ops(clks, nr, table, 39162306a36Sopenharmony_ci &sh_clk_div4_reparent_clk_ops); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci/* FSI-DIV */ 39562306a36Sopenharmony_cistatic unsigned long fsidiv_recalc(struct clk *clk) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci u32 value; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci value = __raw_readl(clk->mapping->base); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci value >>= 16; 40262306a36Sopenharmony_ci if (value < 2) 40362306a36Sopenharmony_ci return clk->parent->rate; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return clk->parent->rate / value; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic long fsidiv_round_rate(struct clk *clk, unsigned long rate) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci return clk_rate_div_range_round(clk, 1, 0xffff, rate); 41162306a36Sopenharmony_ci} 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic void fsidiv_disable(struct clk *clk) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci __raw_writel(0, clk->mapping->base); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int fsidiv_enable(struct clk *clk) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci u32 value; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci value = __raw_readl(clk->mapping->base) >> 16; 42362306a36Sopenharmony_ci if (value < 2) 42462306a36Sopenharmony_ci return 0; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci __raw_writel((value << 16) | 0x3, clk->mapping->base); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci return 0; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic int fsidiv_set_rate(struct clk *clk, unsigned long rate) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci int idx; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci idx = (clk->parent->rate / rate) & 0xffff; 43662306a36Sopenharmony_ci if (idx < 2) 43762306a36Sopenharmony_ci __raw_writel(0, clk->mapping->base); 43862306a36Sopenharmony_ci else 43962306a36Sopenharmony_ci __raw_writel(idx << 16, clk->mapping->base); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic struct sh_clk_ops fsidiv_clk_ops = { 44562306a36Sopenharmony_ci .recalc = fsidiv_recalc, 44662306a36Sopenharmony_ci .round_rate = fsidiv_round_rate, 44762306a36Sopenharmony_ci .set_rate = fsidiv_set_rate, 44862306a36Sopenharmony_ci .enable = fsidiv_enable, 44962306a36Sopenharmony_ci .disable = fsidiv_disable, 45062306a36Sopenharmony_ci}; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ciint __init sh_clk_fsidiv_register(struct clk *clks, int nr) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct clk_mapping *map; 45562306a36Sopenharmony_ci int i; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci for (i = 0; i < nr; i++) { 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci map = kzalloc(sizeof(struct clk_mapping), GFP_KERNEL); 46062306a36Sopenharmony_ci if (!map) { 46162306a36Sopenharmony_ci pr_err("%s: unable to alloc memory\n", __func__); 46262306a36Sopenharmony_ci return -ENOMEM; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* clks[i].enable_reg came from SH_CLK_FSIDIV() */ 46662306a36Sopenharmony_ci map->phys = (phys_addr_t)clks[i].enable_reg; 46762306a36Sopenharmony_ci map->len = 8; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci clks[i].enable_reg = 0; /* remove .enable_reg */ 47062306a36Sopenharmony_ci clks[i].ops = &fsidiv_clk_ops; 47162306a36Sopenharmony_ci clks[i].mapping = map; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci clk_register(&clks[i]); 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci return 0; 47762306a36Sopenharmony_ci} 478