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 
173b2bd70fSChen-Yu Tsai #include <linux/clk-provider.h>
183b2bd70fSChen-Yu Tsai #include <linux/clkdev.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 /**
273b2bd70fSChen-Yu Tsai  * sun9i_a80_get_pll4_factors() - calculates n, p, m factors for PLL1
283b2bd70fSChen-Yu Tsai  * PLL4 rate is calculated as follows
293b2bd70fSChen-Yu Tsai  * rate = (parent_rate * n >> p) / (m + 1);
303b2bd70fSChen-Yu Tsai  * 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,
363b2bd70fSChen-Yu Tsai 				       u8 *n, u8 *k, u8 *m, u8 *p)
373b2bd70fSChen-Yu Tsai {
383b2bd70fSChen-Yu Tsai 	int div;
393b2bd70fSChen-Yu Tsai 
403b2bd70fSChen-Yu Tsai 	/* Normalize value to a 6M multiple */
413b2bd70fSChen-Yu Tsai 	div = DIV_ROUND_UP(*freq, 6000000);
423b2bd70fSChen-Yu Tsai 
433b2bd70fSChen-Yu Tsai 	/* divs above 256 cannot be odd */
443b2bd70fSChen-Yu Tsai 	if (div > 256)
453b2bd70fSChen-Yu Tsai 		div = round_up(div, 2);
463b2bd70fSChen-Yu Tsai 
473b2bd70fSChen-Yu Tsai 	/* divs above 512 must be a multiple of 4 */
483b2bd70fSChen-Yu Tsai 	if (div > 512)
493b2bd70fSChen-Yu Tsai 		div = round_up(div, 4);
503b2bd70fSChen-Yu Tsai 
513b2bd70fSChen-Yu Tsai 	*freq = 6000000 * div;
523b2bd70fSChen-Yu Tsai 
533b2bd70fSChen-Yu Tsai 	/* we were called to round the frequency, we can now return */
543b2bd70fSChen-Yu Tsai 	if (n == NULL)
553b2bd70fSChen-Yu Tsai 		return;
563b2bd70fSChen-Yu Tsai 
573b2bd70fSChen-Yu Tsai 	/* p will be 1 for divs under 512 */
583b2bd70fSChen-Yu Tsai 	if (div < 512)
593b2bd70fSChen-Yu Tsai 		*p = 1;
603b2bd70fSChen-Yu Tsai 	else
613b2bd70fSChen-Yu Tsai 		*p = 0;
623b2bd70fSChen-Yu Tsai 
633b2bd70fSChen-Yu Tsai 	/* m will be 1 if div is odd */
643b2bd70fSChen-Yu Tsai 	if (div & 1)
653b2bd70fSChen-Yu Tsai 		*m = 1;
663b2bd70fSChen-Yu Tsai 	else
673b2bd70fSChen-Yu Tsai 		*m = 0;
683b2bd70fSChen-Yu Tsai 
693b2bd70fSChen-Yu Tsai 	/* calculate a suitable n based on m and p */
703b2bd70fSChen-Yu Tsai 	*n = div / (*p + 1) / (*m + 1);
713b2bd70fSChen-Yu Tsai }
723b2bd70fSChen-Yu Tsai 
733b2bd70fSChen-Yu Tsai static struct clk_factors_config sun9i_a80_pll4_config = {
743b2bd70fSChen-Yu Tsai 	.mshift = 18,
753b2bd70fSChen-Yu Tsai 	.mwidth = 1,
763b2bd70fSChen-Yu Tsai 	.nshift = 8,
773b2bd70fSChen-Yu Tsai 	.nwidth = 8,
783b2bd70fSChen-Yu Tsai 	.pshift = 16,
793b2bd70fSChen-Yu Tsai 	.pwidth = 1,
803b2bd70fSChen-Yu Tsai };
813b2bd70fSChen-Yu Tsai 
823b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_pll4_data __initconst = {
833b2bd70fSChen-Yu Tsai 	.enable = 31,
843b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_pll4_config,
853b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_pll4_factors,
863b2bd70fSChen-Yu Tsai };
873b2bd70fSChen-Yu Tsai 
883b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_pll4_lock);
893b2bd70fSChen-Yu Tsai 
903b2bd70fSChen-Yu Tsai static void __init sun9i_a80_pll4_setup(struct device_node *node)
913b2bd70fSChen-Yu Tsai {
923b2bd70fSChen-Yu Tsai 	sunxi_factors_register(node, &sun9i_a80_pll4_data, &sun9i_a80_pll4_lock);
933b2bd70fSChen-Yu Tsai }
943b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_pll4, "allwinner,sun9i-a80-pll4-clk", sun9i_a80_pll4_setup);
953b2bd70fSChen-Yu Tsai 
963b2bd70fSChen-Yu Tsai 
973b2bd70fSChen-Yu Tsai /**
983b2bd70fSChen-Yu Tsai  * sun9i_a80_get_gt_factors() - calculates m factor for GT
993b2bd70fSChen-Yu Tsai  * GT rate is calculated as follows
1003b2bd70fSChen-Yu Tsai  * rate = parent_rate / (m + 1);
1013b2bd70fSChen-Yu Tsai  */
1023b2bd70fSChen-Yu Tsai 
1033b2bd70fSChen-Yu Tsai static void sun9i_a80_get_gt_factors(u32 *freq, u32 parent_rate,
1043b2bd70fSChen-Yu Tsai 				     u8 *n, u8 *k, u8 *m, u8 *p)
1053b2bd70fSChen-Yu Tsai {
1063b2bd70fSChen-Yu Tsai 	u32 div;
1073b2bd70fSChen-Yu Tsai 
1083b2bd70fSChen-Yu Tsai 	if (parent_rate < *freq)
1093b2bd70fSChen-Yu Tsai 		*freq = parent_rate;
1103b2bd70fSChen-Yu Tsai 
1113b2bd70fSChen-Yu Tsai 	div = DIV_ROUND_UP(parent_rate, *freq);
1123b2bd70fSChen-Yu Tsai 
1133b2bd70fSChen-Yu Tsai 	/* maximum divider is 4 */
1143b2bd70fSChen-Yu Tsai 	if (div > 4)
1153b2bd70fSChen-Yu Tsai 		div = 4;
1163b2bd70fSChen-Yu Tsai 
1173b2bd70fSChen-Yu Tsai 	*freq = parent_rate / div;
1183b2bd70fSChen-Yu Tsai 
1193b2bd70fSChen-Yu Tsai 	/* we were called to round the frequency, we can now return */
1203b2bd70fSChen-Yu Tsai 	if (!m)
1213b2bd70fSChen-Yu Tsai 		return;
1223b2bd70fSChen-Yu Tsai 
1233b2bd70fSChen-Yu Tsai 	*m = div;
1243b2bd70fSChen-Yu Tsai }
1253b2bd70fSChen-Yu Tsai 
1263b2bd70fSChen-Yu Tsai static struct clk_factors_config sun9i_a80_gt_config = {
1273b2bd70fSChen-Yu Tsai 	.mshift = 0,
1283b2bd70fSChen-Yu Tsai 	.mwidth = 2,
1293b2bd70fSChen-Yu Tsai };
1303b2bd70fSChen-Yu Tsai 
1313b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_gt_data __initconst = {
1323b2bd70fSChen-Yu Tsai 	.mux = 24,
1333b2bd70fSChen-Yu Tsai 	.muxmask = BIT(1) | BIT(0),
1343b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_gt_config,
1353b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_gt_factors,
1363b2bd70fSChen-Yu Tsai };
1373b2bd70fSChen-Yu Tsai 
1383b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_gt_lock);
1393b2bd70fSChen-Yu Tsai 
1403b2bd70fSChen-Yu Tsai static void __init sun9i_a80_gt_setup(struct device_node *node)
1413b2bd70fSChen-Yu Tsai {
1423b2bd70fSChen-Yu Tsai 	struct clk *gt = sunxi_factors_register(node, &sun9i_a80_gt_data,
1433b2bd70fSChen-Yu Tsai 						&sun9i_a80_gt_lock);
1443b2bd70fSChen-Yu Tsai 
1453b2bd70fSChen-Yu Tsai 	/* The GT bus clock needs to be always enabled */
1463b2bd70fSChen-Yu Tsai 	__clk_get(gt);
1473b2bd70fSChen-Yu Tsai 	clk_prepare_enable(gt);
1483b2bd70fSChen-Yu Tsai }
1493b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_gt, "allwinner,sun9i-a80-gt-clk", sun9i_a80_gt_setup);
1503b2bd70fSChen-Yu Tsai 
1513b2bd70fSChen-Yu Tsai 
1523b2bd70fSChen-Yu Tsai /**
1533b2bd70fSChen-Yu Tsai  * sun9i_a80_get_ahb_factors() - calculates p factor for AHB0/1/2
1543b2bd70fSChen-Yu Tsai  * AHB rate is calculated as follows
1553b2bd70fSChen-Yu Tsai  * rate = parent_rate >> p;
1563b2bd70fSChen-Yu Tsai  */
1573b2bd70fSChen-Yu Tsai 
1583b2bd70fSChen-Yu Tsai static void sun9i_a80_get_ahb_factors(u32 *freq, u32 parent_rate,
1593b2bd70fSChen-Yu Tsai 				      u8 *n, u8 *k, u8 *m, u8 *p)
1603b2bd70fSChen-Yu Tsai {
1613b2bd70fSChen-Yu Tsai 	u32 _p;
1623b2bd70fSChen-Yu Tsai 
1633b2bd70fSChen-Yu Tsai 	if (parent_rate < *freq)
1643b2bd70fSChen-Yu Tsai 		*freq = parent_rate;
1653b2bd70fSChen-Yu Tsai 
1663b2bd70fSChen-Yu Tsai 	_p = order_base_2(DIV_ROUND_UP(parent_rate, *freq));
1673b2bd70fSChen-Yu Tsai 
1683b2bd70fSChen-Yu Tsai 	/* maximum p is 3 */
1693b2bd70fSChen-Yu Tsai 	if (_p > 3)
1703b2bd70fSChen-Yu Tsai 		_p = 3;
1713b2bd70fSChen-Yu Tsai 
1723b2bd70fSChen-Yu Tsai 	*freq = parent_rate >> _p;
1733b2bd70fSChen-Yu Tsai 
1743b2bd70fSChen-Yu Tsai 	/* we were called to round the frequency, we can now return */
1753b2bd70fSChen-Yu Tsai 	if (!p)
1763b2bd70fSChen-Yu Tsai 		return;
1773b2bd70fSChen-Yu Tsai 
1783b2bd70fSChen-Yu Tsai 	*p = _p;
1793b2bd70fSChen-Yu Tsai }
1803b2bd70fSChen-Yu Tsai 
1813b2bd70fSChen-Yu Tsai static struct clk_factors_config sun9i_a80_ahb_config = {
1823b2bd70fSChen-Yu Tsai 	.pshift = 0,
1833b2bd70fSChen-Yu Tsai 	.pwidth = 2,
1843b2bd70fSChen-Yu Tsai };
1853b2bd70fSChen-Yu Tsai 
1863b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_ahb_data __initconst = {
1873b2bd70fSChen-Yu Tsai 	.mux = 24,
1883b2bd70fSChen-Yu Tsai 	.muxmask = BIT(1) | BIT(0),
1893b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_ahb_config,
1903b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_ahb_factors,
1913b2bd70fSChen-Yu Tsai };
1923b2bd70fSChen-Yu Tsai 
1933b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_ahb_lock);
1943b2bd70fSChen-Yu Tsai 
1953b2bd70fSChen-Yu Tsai static void __init sun9i_a80_ahb_setup(struct device_node *node)
1963b2bd70fSChen-Yu Tsai {
1973b2bd70fSChen-Yu Tsai 	sunxi_factors_register(node, &sun9i_a80_ahb_data, &sun9i_a80_ahb_lock);
1983b2bd70fSChen-Yu Tsai }
1993b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_ahb, "allwinner,sun9i-a80-ahb-clk", sun9i_a80_ahb_setup);
2003b2bd70fSChen-Yu Tsai 
2013b2bd70fSChen-Yu Tsai 
2023b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_apb0_data __initconst = {
2033b2bd70fSChen-Yu Tsai 	.mux = 24,
2043b2bd70fSChen-Yu Tsai 	.muxmask = BIT(0),
2053b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_ahb_config,
2063b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_ahb_factors,
2073b2bd70fSChen-Yu Tsai };
2083b2bd70fSChen-Yu Tsai 
2093b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_apb0_lock);
2103b2bd70fSChen-Yu Tsai 
2113b2bd70fSChen-Yu Tsai static void __init sun9i_a80_apb0_setup(struct device_node *node)
2123b2bd70fSChen-Yu Tsai {
2133b2bd70fSChen-Yu Tsai 	sunxi_factors_register(node, &sun9i_a80_apb0_data, &sun9i_a80_apb0_lock);
2143b2bd70fSChen-Yu Tsai }
2153b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_apb0, "allwinner,sun9i-a80-apb0-clk", sun9i_a80_apb0_setup);
2163b2bd70fSChen-Yu Tsai 
2173b2bd70fSChen-Yu Tsai 
2183b2bd70fSChen-Yu Tsai /**
2193b2bd70fSChen-Yu Tsai  * sun9i_a80_get_apb1_factors() - calculates m, p factors for APB1
2203b2bd70fSChen-Yu Tsai  * APB1 rate is calculated as follows
2213b2bd70fSChen-Yu Tsai  * rate = (parent_rate >> p) / (m + 1);
2223b2bd70fSChen-Yu Tsai  */
2233b2bd70fSChen-Yu Tsai 
2243b2bd70fSChen-Yu Tsai static void sun9i_a80_get_apb1_factors(u32 *freq, u32 parent_rate,
2253b2bd70fSChen-Yu Tsai 				       u8 *n, u8 *k, u8 *m, u8 *p)
2263b2bd70fSChen-Yu Tsai {
2273b2bd70fSChen-Yu Tsai 	u32 div;
2283b2bd70fSChen-Yu Tsai 	u8 calcm, calcp;
2293b2bd70fSChen-Yu Tsai 
2303b2bd70fSChen-Yu Tsai 	if (parent_rate < *freq)
2313b2bd70fSChen-Yu Tsai 		*freq = parent_rate;
2323b2bd70fSChen-Yu Tsai 
2333b2bd70fSChen-Yu Tsai 	div = DIV_ROUND_UP(parent_rate, *freq);
2343b2bd70fSChen-Yu Tsai 
2353b2bd70fSChen-Yu Tsai 	/* Highest possible divider is 256 (p = 3, m = 31) */
2363b2bd70fSChen-Yu Tsai 	if (div > 256)
2373b2bd70fSChen-Yu Tsai 		div = 256;
2383b2bd70fSChen-Yu Tsai 
2393b2bd70fSChen-Yu Tsai 	calcp = order_base_2(div);
2403b2bd70fSChen-Yu Tsai 	calcm = (parent_rate >> calcp) - 1;
2413b2bd70fSChen-Yu Tsai 	*freq = (parent_rate >> calcp) / (calcm + 1);
2423b2bd70fSChen-Yu Tsai 
2433b2bd70fSChen-Yu Tsai 	/* we were called to round the frequency, we can now return */
2443b2bd70fSChen-Yu Tsai 	if (n == NULL)
2453b2bd70fSChen-Yu Tsai 		return;
2463b2bd70fSChen-Yu Tsai 
2473b2bd70fSChen-Yu Tsai 	*m = calcm;
2483b2bd70fSChen-Yu Tsai 	*p = calcp;
2493b2bd70fSChen-Yu Tsai }
2503b2bd70fSChen-Yu Tsai 
2513b2bd70fSChen-Yu Tsai static struct clk_factors_config sun9i_a80_apb1_config = {
2523b2bd70fSChen-Yu Tsai 	.mshift = 0,
2533b2bd70fSChen-Yu Tsai 	.mwidth = 5,
2543b2bd70fSChen-Yu Tsai 	.pshift = 16,
2553b2bd70fSChen-Yu Tsai 	.pwidth = 2,
2563b2bd70fSChen-Yu Tsai };
2573b2bd70fSChen-Yu Tsai 
2583b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_apb1_data __initconst = {
2593b2bd70fSChen-Yu Tsai 	.mux = 24,
2603b2bd70fSChen-Yu Tsai 	.muxmask = BIT(0),
2613b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_apb1_config,
2623b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_apb1_factors,
2633b2bd70fSChen-Yu Tsai };
2643b2bd70fSChen-Yu Tsai 
2653b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_apb1_lock);
2663b2bd70fSChen-Yu Tsai 
2673b2bd70fSChen-Yu Tsai static void __init sun9i_a80_apb1_setup(struct device_node *node)
2683b2bd70fSChen-Yu Tsai {
2693b2bd70fSChen-Yu Tsai 	sunxi_factors_register(node, &sun9i_a80_apb1_data, &sun9i_a80_apb1_lock);
2703b2bd70fSChen-Yu Tsai }
2713b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_apb1, "allwinner,sun9i-a80-apb1-clk", sun9i_a80_apb1_setup);
272