13b2bd70fSChen-Yu Tsai /*
23b2bd70fSChen-Yu Tsai  * Copyright 2014 Chen-Yu Tsai
33b2bd70fSChen-Yu Tsai  *
43b2bd70fSChen-Yu Tsai  * Chen-Yu Tsai <wens@csie.org>
53b2bd70fSChen-Yu Tsai  *
63b2bd70fSChen-Yu Tsai  * This program is free software; you can redistribute it and/or modify
73b2bd70fSChen-Yu Tsai  * it under the terms of the GNU General Public License as published by
83b2bd70fSChen-Yu Tsai  * the Free Software Foundation; either version 2 of the License, or
93b2bd70fSChen-Yu Tsai  * (at your option) any later version.
103b2bd70fSChen-Yu Tsai  *
113b2bd70fSChen-Yu Tsai  * This program is distributed in the hope that it will be useful,
123b2bd70fSChen-Yu Tsai  * but WITHOUT ANY WARRANTY; without even the implied warranty of
133b2bd70fSChen-Yu Tsai  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
143b2bd70fSChen-Yu Tsai  * GNU General Public License for more details.
153b2bd70fSChen-Yu Tsai  */
163b2bd70fSChen-Yu Tsai 
179dfefe8cSStephen Boyd #include <linux/clk.h>
183b2bd70fSChen-Yu Tsai #include <linux/clk-provider.h>
193b2bd70fSChen-Yu Tsai #include <linux/of.h>
203b2bd70fSChen-Yu Tsai #include <linux/of_address.h>
213b2bd70fSChen-Yu Tsai #include <linux/log2.h>
223b2bd70fSChen-Yu Tsai 
233b2bd70fSChen-Yu Tsai #include "clk-factors.h"
243b2bd70fSChen-Yu Tsai 
253b2bd70fSChen-Yu Tsai 
263b2bd70fSChen-Yu Tsai /**
276424e0aeSHans de Goede  * sun9i_a80_get_pll4_factors() - calculates n, p, m factors for PLL4
283b2bd70fSChen-Yu Tsai  * PLL4 rate is calculated as follows
293b2bd70fSChen-Yu Tsai  * rate = (parent_rate * n >> p) / (m + 1);
306424e0aeSHans de Goede  * parent_rate is always 24MHz
313b2bd70fSChen-Yu Tsai  *
323b2bd70fSChen-Yu Tsai  * p and m are named div1 and div2 in Allwinner's SDK
333b2bd70fSChen-Yu Tsai  */
343b2bd70fSChen-Yu Tsai 
353b2bd70fSChen-Yu Tsai static void sun9i_a80_get_pll4_factors(u32 *freq, u32 parent_rate,
366424e0aeSHans de Goede 				       u8 *n_ret, u8 *k, u8 *m_ret, u8 *p_ret)
373b2bd70fSChen-Yu Tsai {
386424e0aeSHans de Goede 	int n;
396424e0aeSHans de Goede 	int m = 1;
406424e0aeSHans de Goede 	int p = 1;
413b2bd70fSChen-Yu Tsai 
426424e0aeSHans de Goede 	/* Normalize value to a 6 MHz multiple (24 MHz / 4) */
436424e0aeSHans de Goede 	n = DIV_ROUND_UP(*freq, 6000000);
443b2bd70fSChen-Yu Tsai 
456424e0aeSHans de Goede 	/* If n is too large switch to steps of 12 MHz */
466424e0aeSHans de Goede 	if (n > 255) {
476424e0aeSHans de Goede 		m = 0;
486424e0aeSHans de Goede 		n = (n + 1) / 2;
496424e0aeSHans de Goede 	}
503b2bd70fSChen-Yu Tsai 
516424e0aeSHans de Goede 	/* If n is still too large switch to steps of 24 MHz */
526424e0aeSHans de Goede 	if (n > 255) {
536424e0aeSHans de Goede 		p = 0;
546424e0aeSHans de Goede 		n = (n + 1) / 2;
556424e0aeSHans de Goede 	}
563b2bd70fSChen-Yu Tsai 
576424e0aeSHans de Goede 	/* n must be between 12 and 255 */
586424e0aeSHans de Goede 	if (n > 255)
596424e0aeSHans de Goede 		n = 255;
606424e0aeSHans de Goede 	else if (n < 12)
616424e0aeSHans de Goede 		n = 12;
626424e0aeSHans de Goede 
636424e0aeSHans de Goede 	*freq = ((24000000 * n) >> p) / (m + 1);
643b2bd70fSChen-Yu Tsai 
653b2bd70fSChen-Yu Tsai 	/* we were called to round the frequency, we can now return */
666424e0aeSHans de Goede 	if (n_ret == NULL)
673b2bd70fSChen-Yu Tsai 		return;
683b2bd70fSChen-Yu Tsai 
696424e0aeSHans de Goede 	*n_ret = n;
706424e0aeSHans de Goede 	*m_ret = m;
716424e0aeSHans de Goede 	*p_ret = p;
723b2bd70fSChen-Yu Tsai }
733b2bd70fSChen-Yu Tsai 
74b3e919e0SChen-Yu Tsai static const struct clk_factors_config sun9i_a80_pll4_config = {
753b2bd70fSChen-Yu Tsai 	.mshift = 18,
763b2bd70fSChen-Yu Tsai 	.mwidth = 1,
773b2bd70fSChen-Yu Tsai 	.nshift = 8,
783b2bd70fSChen-Yu Tsai 	.nwidth = 8,
793b2bd70fSChen-Yu Tsai 	.pshift = 16,
803b2bd70fSChen-Yu Tsai 	.pwidth = 1,
813b2bd70fSChen-Yu Tsai };
823b2bd70fSChen-Yu Tsai 
833b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_pll4_data __initconst = {
843b2bd70fSChen-Yu Tsai 	.enable = 31,
853b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_pll4_config,
863b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_pll4_factors,
873b2bd70fSChen-Yu Tsai };
883b2bd70fSChen-Yu Tsai 
893b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_pll4_lock);
903b2bd70fSChen-Yu Tsai 
913b2bd70fSChen-Yu Tsai static void __init sun9i_a80_pll4_setup(struct device_node *node)
923b2bd70fSChen-Yu Tsai {
9366e79cf1SChen-Yu Tsai 	void __iomem *reg;
9466e79cf1SChen-Yu Tsai 
9566e79cf1SChen-Yu Tsai 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
965ac382c3SMaxime Ripard 	if (IS_ERR(reg)) {
9766e79cf1SChen-Yu Tsai 		pr_err("Could not get registers for a80-pll4-clk: %s\n",
9866e79cf1SChen-Yu Tsai 		       node->name);
9966e79cf1SChen-Yu Tsai 		return;
10066e79cf1SChen-Yu Tsai 	}
10166e79cf1SChen-Yu Tsai 
10266e79cf1SChen-Yu Tsai 	sunxi_factors_register(node, &sun9i_a80_pll4_data,
10366e79cf1SChen-Yu Tsai 			       &sun9i_a80_pll4_lock, reg);
1043b2bd70fSChen-Yu Tsai }
1053b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_pll4, "allwinner,sun9i-a80-pll4-clk", sun9i_a80_pll4_setup);
1063b2bd70fSChen-Yu Tsai 
1073b2bd70fSChen-Yu Tsai 
1083b2bd70fSChen-Yu Tsai /**
1093b2bd70fSChen-Yu Tsai  * sun9i_a80_get_gt_factors() - calculates m factor for GT
1103b2bd70fSChen-Yu Tsai  * GT rate is calculated as follows
1113b2bd70fSChen-Yu Tsai  * rate = parent_rate / (m + 1);
1123b2bd70fSChen-Yu Tsai  */
1133b2bd70fSChen-Yu Tsai 
1143b2bd70fSChen-Yu Tsai static void sun9i_a80_get_gt_factors(u32 *freq, u32 parent_rate,
1153b2bd70fSChen-Yu Tsai 				     u8 *n, u8 *k, u8 *m, u8 *p)
1163b2bd70fSChen-Yu Tsai {
1173b2bd70fSChen-Yu Tsai 	u32 div;
1183b2bd70fSChen-Yu Tsai 
1193b2bd70fSChen-Yu Tsai 	if (parent_rate < *freq)
1203b2bd70fSChen-Yu Tsai 		*freq = parent_rate;
1213b2bd70fSChen-Yu Tsai 
1223b2bd70fSChen-Yu Tsai 	div = DIV_ROUND_UP(parent_rate, *freq);
1233b2bd70fSChen-Yu Tsai 
1243b2bd70fSChen-Yu Tsai 	/* maximum divider is 4 */
1253b2bd70fSChen-Yu Tsai 	if (div > 4)
1263b2bd70fSChen-Yu Tsai 		div = 4;
1273b2bd70fSChen-Yu Tsai 
1283b2bd70fSChen-Yu Tsai 	*freq = parent_rate / div;
1293b2bd70fSChen-Yu Tsai 
1303b2bd70fSChen-Yu Tsai 	/* we were called to round the frequency, we can now return */
1313b2bd70fSChen-Yu Tsai 	if (!m)
1323b2bd70fSChen-Yu Tsai 		return;
1333b2bd70fSChen-Yu Tsai 
1343b2bd70fSChen-Yu Tsai 	*m = div;
1353b2bd70fSChen-Yu Tsai }
1363b2bd70fSChen-Yu Tsai 
137b3e919e0SChen-Yu Tsai static const struct clk_factors_config sun9i_a80_gt_config = {
1383b2bd70fSChen-Yu Tsai 	.mshift = 0,
1393b2bd70fSChen-Yu Tsai 	.mwidth = 2,
1403b2bd70fSChen-Yu Tsai };
1413b2bd70fSChen-Yu Tsai 
1423b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_gt_data __initconst = {
1433b2bd70fSChen-Yu Tsai 	.mux = 24,
1443b2bd70fSChen-Yu Tsai 	.muxmask = BIT(1) | BIT(0),
1453b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_gt_config,
1463b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_gt_factors,
1473b2bd70fSChen-Yu Tsai };
1483b2bd70fSChen-Yu Tsai 
1493b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_gt_lock);
1503b2bd70fSChen-Yu Tsai 
1513b2bd70fSChen-Yu Tsai static void __init sun9i_a80_gt_setup(struct device_node *node)
1523b2bd70fSChen-Yu Tsai {
15366e79cf1SChen-Yu Tsai 	void __iomem *reg;
15466e79cf1SChen-Yu Tsai 	struct clk *gt;
15566e79cf1SChen-Yu Tsai 
15666e79cf1SChen-Yu Tsai 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
1575ac382c3SMaxime Ripard 	if (IS_ERR(reg)) {
15866e79cf1SChen-Yu Tsai 		pr_err("Could not get registers for a80-gt-clk: %s\n",
15966e79cf1SChen-Yu Tsai 		       node->name);
16066e79cf1SChen-Yu Tsai 		return;
16166e79cf1SChen-Yu Tsai 	}
16266e79cf1SChen-Yu Tsai 
16366e79cf1SChen-Yu Tsai 	gt = sunxi_factors_register(node, &sun9i_a80_gt_data,
16466e79cf1SChen-Yu Tsai 				    &sun9i_a80_gt_lock, reg);
1653b2bd70fSChen-Yu Tsai 
1663b2bd70fSChen-Yu Tsai 	/* The GT bus clock needs to be always enabled */
1673b2bd70fSChen-Yu Tsai 	__clk_get(gt);
1683b2bd70fSChen-Yu Tsai 	clk_prepare_enable(gt);
1693b2bd70fSChen-Yu Tsai }
1703b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_gt, "allwinner,sun9i-a80-gt-clk", sun9i_a80_gt_setup);
1713b2bd70fSChen-Yu Tsai 
1723b2bd70fSChen-Yu Tsai 
1733b2bd70fSChen-Yu Tsai /**
1743b2bd70fSChen-Yu Tsai  * sun9i_a80_get_ahb_factors() - calculates p factor for AHB0/1/2
1753b2bd70fSChen-Yu Tsai  * AHB rate is calculated as follows
1763b2bd70fSChen-Yu Tsai  * rate = parent_rate >> p;
1773b2bd70fSChen-Yu Tsai  */
1783b2bd70fSChen-Yu Tsai 
1793b2bd70fSChen-Yu Tsai static void sun9i_a80_get_ahb_factors(u32 *freq, u32 parent_rate,
1803b2bd70fSChen-Yu Tsai 				      u8 *n, u8 *k, u8 *m, u8 *p)
1813b2bd70fSChen-Yu Tsai {
1823b2bd70fSChen-Yu Tsai 	u32 _p;
1833b2bd70fSChen-Yu Tsai 
1843b2bd70fSChen-Yu Tsai 	if (parent_rate < *freq)
1853b2bd70fSChen-Yu Tsai 		*freq = parent_rate;
1863b2bd70fSChen-Yu Tsai 
1873b2bd70fSChen-Yu Tsai 	_p = order_base_2(DIV_ROUND_UP(parent_rate, *freq));
1883b2bd70fSChen-Yu Tsai 
1893b2bd70fSChen-Yu Tsai 	/* maximum p is 3 */
1903b2bd70fSChen-Yu Tsai 	if (_p > 3)
1913b2bd70fSChen-Yu Tsai 		_p = 3;
1923b2bd70fSChen-Yu Tsai 
1933b2bd70fSChen-Yu Tsai 	*freq = parent_rate >> _p;
1943b2bd70fSChen-Yu Tsai 
1953b2bd70fSChen-Yu Tsai 	/* we were called to round the frequency, we can now return */
1963b2bd70fSChen-Yu Tsai 	if (!p)
1973b2bd70fSChen-Yu Tsai 		return;
1983b2bd70fSChen-Yu Tsai 
1993b2bd70fSChen-Yu Tsai 	*p = _p;
2003b2bd70fSChen-Yu Tsai }
2013b2bd70fSChen-Yu Tsai 
202b3e919e0SChen-Yu Tsai static const struct clk_factors_config sun9i_a80_ahb_config = {
2033b2bd70fSChen-Yu Tsai 	.pshift = 0,
2043b2bd70fSChen-Yu Tsai 	.pwidth = 2,
2053b2bd70fSChen-Yu Tsai };
2063b2bd70fSChen-Yu Tsai 
2073b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_ahb_data __initconst = {
2083b2bd70fSChen-Yu Tsai 	.mux = 24,
2093b2bd70fSChen-Yu Tsai 	.muxmask = BIT(1) | BIT(0),
2103b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_ahb_config,
2113b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_ahb_factors,
2123b2bd70fSChen-Yu Tsai };
2133b2bd70fSChen-Yu Tsai 
2143b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_ahb_lock);
2153b2bd70fSChen-Yu Tsai 
2163b2bd70fSChen-Yu Tsai static void __init sun9i_a80_ahb_setup(struct device_node *node)
2173b2bd70fSChen-Yu Tsai {
21866e79cf1SChen-Yu Tsai 	void __iomem *reg;
21966e79cf1SChen-Yu Tsai 
22066e79cf1SChen-Yu Tsai 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
2215ac382c3SMaxime Ripard 	if (IS_ERR(reg)) {
22266e79cf1SChen-Yu Tsai 		pr_err("Could not get registers for a80-ahb-clk: %s\n",
22366e79cf1SChen-Yu Tsai 		       node->name);
22466e79cf1SChen-Yu Tsai 		return;
22566e79cf1SChen-Yu Tsai 	}
22666e79cf1SChen-Yu Tsai 
22766e79cf1SChen-Yu Tsai 	sunxi_factors_register(node, &sun9i_a80_ahb_data,
22866e79cf1SChen-Yu Tsai 			       &sun9i_a80_ahb_lock, reg);
2293b2bd70fSChen-Yu Tsai }
2303b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_ahb, "allwinner,sun9i-a80-ahb-clk", sun9i_a80_ahb_setup);
2313b2bd70fSChen-Yu Tsai 
2323b2bd70fSChen-Yu Tsai 
2333b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_apb0_data __initconst = {
2343b2bd70fSChen-Yu Tsai 	.mux = 24,
2353b2bd70fSChen-Yu Tsai 	.muxmask = BIT(0),
2363b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_ahb_config,
2373b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_ahb_factors,
2383b2bd70fSChen-Yu Tsai };
2393b2bd70fSChen-Yu Tsai 
2403b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_apb0_lock);
2413b2bd70fSChen-Yu Tsai 
2423b2bd70fSChen-Yu Tsai static void __init sun9i_a80_apb0_setup(struct device_node *node)
2433b2bd70fSChen-Yu Tsai {
24466e79cf1SChen-Yu Tsai 	void __iomem *reg;
24566e79cf1SChen-Yu Tsai 
24666e79cf1SChen-Yu Tsai 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
2475ac382c3SMaxime Ripard 	if (IS_ERR(reg)) {
24866e79cf1SChen-Yu Tsai 		pr_err("Could not get registers for a80-apb0-clk: %s\n",
24966e79cf1SChen-Yu Tsai 		       node->name);
25066e79cf1SChen-Yu Tsai 		return;
25166e79cf1SChen-Yu Tsai 	}
25266e79cf1SChen-Yu Tsai 
25366e79cf1SChen-Yu Tsai 	sunxi_factors_register(node, &sun9i_a80_apb0_data,
25466e79cf1SChen-Yu Tsai 			       &sun9i_a80_apb0_lock, reg);
2553b2bd70fSChen-Yu Tsai }
2563b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_apb0, "allwinner,sun9i-a80-apb0-clk", sun9i_a80_apb0_setup);
2573b2bd70fSChen-Yu Tsai 
2583b2bd70fSChen-Yu Tsai 
2593b2bd70fSChen-Yu Tsai /**
2603b2bd70fSChen-Yu Tsai  * sun9i_a80_get_apb1_factors() - calculates m, p factors for APB1
2613b2bd70fSChen-Yu Tsai  * APB1 rate is calculated as follows
2623b2bd70fSChen-Yu Tsai  * rate = (parent_rate >> p) / (m + 1);
2633b2bd70fSChen-Yu Tsai  */
2643b2bd70fSChen-Yu Tsai 
2653b2bd70fSChen-Yu Tsai static void sun9i_a80_get_apb1_factors(u32 *freq, u32 parent_rate,
2663b2bd70fSChen-Yu Tsai 				       u8 *n, u8 *k, u8 *m, u8 *p)
2673b2bd70fSChen-Yu Tsai {
2683b2bd70fSChen-Yu Tsai 	u32 div;
2693b2bd70fSChen-Yu Tsai 	u8 calcm, calcp;
2703b2bd70fSChen-Yu Tsai 
2713b2bd70fSChen-Yu Tsai 	if (parent_rate < *freq)
2723b2bd70fSChen-Yu Tsai 		*freq = parent_rate;
2733b2bd70fSChen-Yu Tsai 
2743b2bd70fSChen-Yu Tsai 	div = DIV_ROUND_UP(parent_rate, *freq);
2753b2bd70fSChen-Yu Tsai 
2763b2bd70fSChen-Yu Tsai 	/* Highest possible divider is 256 (p = 3, m = 31) */
2773b2bd70fSChen-Yu Tsai 	if (div > 256)
2783b2bd70fSChen-Yu Tsai 		div = 256;
2793b2bd70fSChen-Yu Tsai 
2803b2bd70fSChen-Yu Tsai 	calcp = order_base_2(div);
2813b2bd70fSChen-Yu Tsai 	calcm = (parent_rate >> calcp) - 1;
2823b2bd70fSChen-Yu Tsai 	*freq = (parent_rate >> calcp) / (calcm + 1);
2833b2bd70fSChen-Yu Tsai 
2843b2bd70fSChen-Yu Tsai 	/* we were called to round the frequency, we can now return */
2853b2bd70fSChen-Yu Tsai 	if (n == NULL)
2863b2bd70fSChen-Yu Tsai 		return;
2873b2bd70fSChen-Yu Tsai 
2883b2bd70fSChen-Yu Tsai 	*m = calcm;
2893b2bd70fSChen-Yu Tsai 	*p = calcp;
2903b2bd70fSChen-Yu Tsai }
2913b2bd70fSChen-Yu Tsai 
292b3e919e0SChen-Yu Tsai static const struct clk_factors_config sun9i_a80_apb1_config = {
2933b2bd70fSChen-Yu Tsai 	.mshift = 0,
2943b2bd70fSChen-Yu Tsai 	.mwidth = 5,
2953b2bd70fSChen-Yu Tsai 	.pshift = 16,
2963b2bd70fSChen-Yu Tsai 	.pwidth = 2,
2973b2bd70fSChen-Yu Tsai };
2983b2bd70fSChen-Yu Tsai 
2993b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_apb1_data __initconst = {
3003b2bd70fSChen-Yu Tsai 	.mux = 24,
3013b2bd70fSChen-Yu Tsai 	.muxmask = BIT(0),
3023b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_apb1_config,
3033b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_apb1_factors,
3043b2bd70fSChen-Yu Tsai };
3053b2bd70fSChen-Yu Tsai 
3063b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_apb1_lock);
3073b2bd70fSChen-Yu Tsai 
3083b2bd70fSChen-Yu Tsai static void __init sun9i_a80_apb1_setup(struct device_node *node)
3093b2bd70fSChen-Yu Tsai {
31066e79cf1SChen-Yu Tsai 	void __iomem *reg;
31166e79cf1SChen-Yu Tsai 
31266e79cf1SChen-Yu Tsai 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
3135ac382c3SMaxime Ripard 	if (IS_ERR(reg)) {
31466e79cf1SChen-Yu Tsai 		pr_err("Could not get registers for a80-apb1-clk: %s\n",
31566e79cf1SChen-Yu Tsai 		       node->name);
31666e79cf1SChen-Yu Tsai 		return;
31766e79cf1SChen-Yu Tsai 	}
31866e79cf1SChen-Yu Tsai 
31966e79cf1SChen-Yu Tsai 	sunxi_factors_register(node, &sun9i_a80_apb1_data,
32066e79cf1SChen-Yu Tsai 			       &sun9i_a80_apb1_lock, reg);
3213b2bd70fSChen-Yu Tsai }
3223b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_apb1, "allwinner,sun9i-a80-apb1-clk", sun9i_a80_apb1_setup);
323