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