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 
35cfa63688SChen-Yu Tsai static void sun9i_a80_get_pll4_factors(struct factors_request *req)
363b2bd70fSChen-Yu Tsai {
376424e0aeSHans de Goede 	int n;
386424e0aeSHans de Goede 	int m = 1;
396424e0aeSHans de Goede 	int p = 1;
403b2bd70fSChen-Yu Tsai 
416424e0aeSHans de Goede 	/* Normalize value to a 6 MHz multiple (24 MHz / 4) */
42cfa63688SChen-Yu Tsai 	n = DIV_ROUND_UP(req->rate, 6000000);
433b2bd70fSChen-Yu Tsai 
446424e0aeSHans de Goede 	/* If n is too large switch to steps of 12 MHz */
456424e0aeSHans de Goede 	if (n > 255) {
466424e0aeSHans de Goede 		m = 0;
476424e0aeSHans de Goede 		n = (n + 1) / 2;
486424e0aeSHans de Goede 	}
493b2bd70fSChen-Yu Tsai 
506424e0aeSHans de Goede 	/* If n is still too large switch to steps of 24 MHz */
516424e0aeSHans de Goede 	if (n > 255) {
526424e0aeSHans de Goede 		p = 0;
536424e0aeSHans de Goede 		n = (n + 1) / 2;
546424e0aeSHans de Goede 	}
553b2bd70fSChen-Yu Tsai 
566424e0aeSHans de Goede 	/* n must be between 12 and 255 */
576424e0aeSHans de Goede 	if (n > 255)
586424e0aeSHans de Goede 		n = 255;
596424e0aeSHans de Goede 	else if (n < 12)
606424e0aeSHans de Goede 		n = 12;
616424e0aeSHans de Goede 
62cfa63688SChen-Yu Tsai 	req->rate = ((24000000 * n) >> p) / (m + 1);
63cfa63688SChen-Yu Tsai 	req->n = n;
64cfa63688SChen-Yu Tsai 	req->m = m;
65cfa63688SChen-Yu Tsai 	req->p = p;
663b2bd70fSChen-Yu Tsai }
673b2bd70fSChen-Yu Tsai 
68b3e919e0SChen-Yu Tsai static const struct clk_factors_config sun9i_a80_pll4_config = {
693b2bd70fSChen-Yu Tsai 	.mshift = 18,
703b2bd70fSChen-Yu Tsai 	.mwidth = 1,
713b2bd70fSChen-Yu Tsai 	.nshift = 8,
723b2bd70fSChen-Yu Tsai 	.nwidth = 8,
733b2bd70fSChen-Yu Tsai 	.pshift = 16,
743b2bd70fSChen-Yu Tsai 	.pwidth = 1,
753b2bd70fSChen-Yu Tsai };
763b2bd70fSChen-Yu Tsai 
773b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_pll4_data __initconst = {
783b2bd70fSChen-Yu Tsai 	.enable = 31,
793b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_pll4_config,
803b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_pll4_factors,
813b2bd70fSChen-Yu Tsai };
823b2bd70fSChen-Yu Tsai 
833b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_pll4_lock);
843b2bd70fSChen-Yu Tsai 
853b2bd70fSChen-Yu Tsai static void __init sun9i_a80_pll4_setup(struct device_node *node)
863b2bd70fSChen-Yu Tsai {
8766e79cf1SChen-Yu Tsai 	void __iomem *reg;
8866e79cf1SChen-Yu Tsai 
8966e79cf1SChen-Yu Tsai 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
905ac382c3SMaxime Ripard 	if (IS_ERR(reg)) {
9166e79cf1SChen-Yu Tsai 		pr_err("Could not get registers for a80-pll4-clk: %s\n",
9266e79cf1SChen-Yu Tsai 		       node->name);
9366e79cf1SChen-Yu Tsai 		return;
9466e79cf1SChen-Yu Tsai 	}
9566e79cf1SChen-Yu Tsai 
9666e79cf1SChen-Yu Tsai 	sunxi_factors_register(node, &sun9i_a80_pll4_data,
9766e79cf1SChen-Yu Tsai 			       &sun9i_a80_pll4_lock, reg);
983b2bd70fSChen-Yu Tsai }
993b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_pll4, "allwinner,sun9i-a80-pll4-clk", sun9i_a80_pll4_setup);
1003b2bd70fSChen-Yu Tsai 
1013b2bd70fSChen-Yu Tsai 
1023b2bd70fSChen-Yu Tsai /**
1033b2bd70fSChen-Yu Tsai  * sun9i_a80_get_gt_factors() - calculates m factor for GT
1043b2bd70fSChen-Yu Tsai  * GT rate is calculated as follows
1053b2bd70fSChen-Yu Tsai  * rate = parent_rate / (m + 1);
1063b2bd70fSChen-Yu Tsai  */
1073b2bd70fSChen-Yu Tsai 
108cfa63688SChen-Yu Tsai static void sun9i_a80_get_gt_factors(struct factors_request *req)
1093b2bd70fSChen-Yu Tsai {
1103b2bd70fSChen-Yu Tsai 	u32 div;
1113b2bd70fSChen-Yu Tsai 
112cfa63688SChen-Yu Tsai 	if (req->parent_rate < req->rate)
113cfa63688SChen-Yu Tsai 		req->rate = req->parent_rate;
1143b2bd70fSChen-Yu Tsai 
115cfa63688SChen-Yu Tsai 	div = DIV_ROUND_UP(req->parent_rate, req->rate);
1163b2bd70fSChen-Yu Tsai 
1173b2bd70fSChen-Yu Tsai 	/* maximum divider is 4 */
1183b2bd70fSChen-Yu Tsai 	if (div > 4)
1193b2bd70fSChen-Yu Tsai 		div = 4;
1203b2bd70fSChen-Yu Tsai 
121cfa63688SChen-Yu Tsai 	req->rate = req->parent_rate / div;
122cfa63688SChen-Yu Tsai 	req->m = div;
1233b2bd70fSChen-Yu Tsai }
1243b2bd70fSChen-Yu Tsai 
125b3e919e0SChen-Yu Tsai static const struct clk_factors_config sun9i_a80_gt_config = {
1263b2bd70fSChen-Yu Tsai 	.mshift = 0,
1273b2bd70fSChen-Yu Tsai 	.mwidth = 2,
1283b2bd70fSChen-Yu Tsai };
1293b2bd70fSChen-Yu Tsai 
1303b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_gt_data __initconst = {
1313b2bd70fSChen-Yu Tsai 	.mux = 24,
1323b2bd70fSChen-Yu Tsai 	.muxmask = BIT(1) | BIT(0),
1333b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_gt_config,
1343b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_gt_factors,
1353b2bd70fSChen-Yu Tsai };
1363b2bd70fSChen-Yu Tsai 
1373b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_gt_lock);
1383b2bd70fSChen-Yu Tsai 
1393b2bd70fSChen-Yu Tsai static void __init sun9i_a80_gt_setup(struct device_node *node)
1403b2bd70fSChen-Yu Tsai {
14166e79cf1SChen-Yu Tsai 	void __iomem *reg;
14266e79cf1SChen-Yu Tsai 
14366e79cf1SChen-Yu Tsai 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
1445ac382c3SMaxime Ripard 	if (IS_ERR(reg)) {
14566e79cf1SChen-Yu Tsai 		pr_err("Could not get registers for a80-gt-clk: %s\n",
14666e79cf1SChen-Yu Tsai 		       node->name);
14766e79cf1SChen-Yu Tsai 		return;
14866e79cf1SChen-Yu Tsai 	}
14966e79cf1SChen-Yu Tsai 
1503b2bd70fSChen-Yu Tsai 	/* The GT bus clock needs to be always enabled */
1519919d44fSStephen Boyd 	sunxi_factors_register_critical(node, &sun9i_a80_gt_data,
1529919d44fSStephen Boyd 					&sun9i_a80_gt_lock, reg);
1533b2bd70fSChen-Yu Tsai }
1543b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_gt, "allwinner,sun9i-a80-gt-clk", sun9i_a80_gt_setup);
1553b2bd70fSChen-Yu Tsai 
1563b2bd70fSChen-Yu Tsai 
1573b2bd70fSChen-Yu Tsai /**
1583b2bd70fSChen-Yu Tsai  * sun9i_a80_get_ahb_factors() - calculates p factor for AHB0/1/2
1593b2bd70fSChen-Yu Tsai  * AHB rate is calculated as follows
1603b2bd70fSChen-Yu Tsai  * rate = parent_rate >> p;
1613b2bd70fSChen-Yu Tsai  */
1623b2bd70fSChen-Yu Tsai 
163cfa63688SChen-Yu Tsai static void sun9i_a80_get_ahb_factors(struct factors_request *req)
1643b2bd70fSChen-Yu Tsai {
1653b2bd70fSChen-Yu Tsai 	u32 _p;
1663b2bd70fSChen-Yu Tsai 
167cfa63688SChen-Yu Tsai 	if (req->parent_rate < req->rate)
168cfa63688SChen-Yu Tsai 		req->rate = req->parent_rate;
1693b2bd70fSChen-Yu Tsai 
170cfa63688SChen-Yu Tsai 	_p = order_base_2(DIV_ROUND_UP(req->parent_rate, req->rate));
1713b2bd70fSChen-Yu Tsai 
1723b2bd70fSChen-Yu Tsai 	/* maximum p is 3 */
1733b2bd70fSChen-Yu Tsai 	if (_p > 3)
1743b2bd70fSChen-Yu Tsai 		_p = 3;
1753b2bd70fSChen-Yu Tsai 
176cfa63688SChen-Yu Tsai 	req->rate = req->parent_rate >> _p;
177cfa63688SChen-Yu Tsai 	req->p = _p;
1783b2bd70fSChen-Yu Tsai }
1793b2bd70fSChen-Yu Tsai 
180b3e919e0SChen-Yu Tsai static const struct clk_factors_config sun9i_a80_ahb_config = {
1813b2bd70fSChen-Yu Tsai 	.pshift = 0,
1823b2bd70fSChen-Yu Tsai 	.pwidth = 2,
1833b2bd70fSChen-Yu Tsai };
1843b2bd70fSChen-Yu Tsai 
1853b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_ahb_data __initconst = {
1863b2bd70fSChen-Yu Tsai 	.mux = 24,
1873b2bd70fSChen-Yu Tsai 	.muxmask = BIT(1) | BIT(0),
1883b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_ahb_config,
1893b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_ahb_factors,
1903b2bd70fSChen-Yu Tsai };
1913b2bd70fSChen-Yu Tsai 
1923b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_ahb_lock);
1933b2bd70fSChen-Yu Tsai 
1943b2bd70fSChen-Yu Tsai static void __init sun9i_a80_ahb_setup(struct device_node *node)
1953b2bd70fSChen-Yu Tsai {
19666e79cf1SChen-Yu Tsai 	void __iomem *reg;
19766e79cf1SChen-Yu Tsai 
19866e79cf1SChen-Yu Tsai 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
1995ac382c3SMaxime Ripard 	if (IS_ERR(reg)) {
20066e79cf1SChen-Yu Tsai 		pr_err("Could not get registers for a80-ahb-clk: %s\n",
20166e79cf1SChen-Yu Tsai 		       node->name);
20266e79cf1SChen-Yu Tsai 		return;
20366e79cf1SChen-Yu Tsai 	}
20466e79cf1SChen-Yu Tsai 
20566e79cf1SChen-Yu Tsai 	sunxi_factors_register(node, &sun9i_a80_ahb_data,
20666e79cf1SChen-Yu Tsai 			       &sun9i_a80_ahb_lock, reg);
2073b2bd70fSChen-Yu Tsai }
2083b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_ahb, "allwinner,sun9i-a80-ahb-clk", sun9i_a80_ahb_setup);
2093b2bd70fSChen-Yu Tsai 
2103b2bd70fSChen-Yu Tsai 
2113b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_apb0_data __initconst = {
2123b2bd70fSChen-Yu Tsai 	.mux = 24,
2133b2bd70fSChen-Yu Tsai 	.muxmask = BIT(0),
2143b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_ahb_config,
2153b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_ahb_factors,
2163b2bd70fSChen-Yu Tsai };
2173b2bd70fSChen-Yu Tsai 
2183b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_apb0_lock);
2193b2bd70fSChen-Yu Tsai 
2203b2bd70fSChen-Yu Tsai static void __init sun9i_a80_apb0_setup(struct device_node *node)
2213b2bd70fSChen-Yu Tsai {
22266e79cf1SChen-Yu Tsai 	void __iomem *reg;
22366e79cf1SChen-Yu Tsai 
22466e79cf1SChen-Yu Tsai 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
2255ac382c3SMaxime Ripard 	if (IS_ERR(reg)) {
22666e79cf1SChen-Yu Tsai 		pr_err("Could not get registers for a80-apb0-clk: %s\n",
22766e79cf1SChen-Yu Tsai 		       node->name);
22866e79cf1SChen-Yu Tsai 		return;
22966e79cf1SChen-Yu Tsai 	}
23066e79cf1SChen-Yu Tsai 
23166e79cf1SChen-Yu Tsai 	sunxi_factors_register(node, &sun9i_a80_apb0_data,
23266e79cf1SChen-Yu Tsai 			       &sun9i_a80_apb0_lock, reg);
2333b2bd70fSChen-Yu Tsai }
2343b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_apb0, "allwinner,sun9i-a80-apb0-clk", sun9i_a80_apb0_setup);
2353b2bd70fSChen-Yu Tsai 
2363b2bd70fSChen-Yu Tsai 
2373b2bd70fSChen-Yu Tsai /**
2383b2bd70fSChen-Yu Tsai  * sun9i_a80_get_apb1_factors() - calculates m, p factors for APB1
2393b2bd70fSChen-Yu Tsai  * APB1 rate is calculated as follows
2403b2bd70fSChen-Yu Tsai  * rate = (parent_rate >> p) / (m + 1);
2413b2bd70fSChen-Yu Tsai  */
2423b2bd70fSChen-Yu Tsai 
243cfa63688SChen-Yu Tsai static void sun9i_a80_get_apb1_factors(struct factors_request *req)
2443b2bd70fSChen-Yu Tsai {
2453b2bd70fSChen-Yu Tsai 	u32 div;
2463b2bd70fSChen-Yu Tsai 
247cfa63688SChen-Yu Tsai 	if (req->parent_rate < req->rate)
248cfa63688SChen-Yu Tsai 		req->rate = req->parent_rate;
2493b2bd70fSChen-Yu Tsai 
250cfa63688SChen-Yu Tsai 	div = DIV_ROUND_UP(req->parent_rate, req->rate);
2513b2bd70fSChen-Yu Tsai 
2523b2bd70fSChen-Yu Tsai 	/* Highest possible divider is 256 (p = 3, m = 31) */
2533b2bd70fSChen-Yu Tsai 	if (div > 256)
2543b2bd70fSChen-Yu Tsai 		div = 256;
2553b2bd70fSChen-Yu Tsai 
256cfa63688SChen-Yu Tsai 	req->p = order_base_2(div);
257cfa63688SChen-Yu Tsai 	req->m = (req->parent_rate >> req->p) - 1;
258cfa63688SChen-Yu Tsai 	req->rate = (req->parent_rate >> req->p) / (req->m + 1);
2593b2bd70fSChen-Yu Tsai }
2603b2bd70fSChen-Yu Tsai 
261b3e919e0SChen-Yu Tsai static const struct clk_factors_config sun9i_a80_apb1_config = {
2623b2bd70fSChen-Yu Tsai 	.mshift = 0,
2633b2bd70fSChen-Yu Tsai 	.mwidth = 5,
2643b2bd70fSChen-Yu Tsai 	.pshift = 16,
2653b2bd70fSChen-Yu Tsai 	.pwidth = 2,
2663b2bd70fSChen-Yu Tsai };
2673b2bd70fSChen-Yu Tsai 
2683b2bd70fSChen-Yu Tsai static const struct factors_data sun9i_a80_apb1_data __initconst = {
2693b2bd70fSChen-Yu Tsai 	.mux = 24,
2703b2bd70fSChen-Yu Tsai 	.muxmask = BIT(0),
2713b2bd70fSChen-Yu Tsai 	.table = &sun9i_a80_apb1_config,
2723b2bd70fSChen-Yu Tsai 	.getter = sun9i_a80_get_apb1_factors,
2733b2bd70fSChen-Yu Tsai };
2743b2bd70fSChen-Yu Tsai 
2753b2bd70fSChen-Yu Tsai static DEFINE_SPINLOCK(sun9i_a80_apb1_lock);
2763b2bd70fSChen-Yu Tsai 
2773b2bd70fSChen-Yu Tsai static void __init sun9i_a80_apb1_setup(struct device_node *node)
2783b2bd70fSChen-Yu Tsai {
27966e79cf1SChen-Yu Tsai 	void __iomem *reg;
28066e79cf1SChen-Yu Tsai 
28166e79cf1SChen-Yu Tsai 	reg = of_io_request_and_map(node, 0, of_node_full_name(node));
2825ac382c3SMaxime Ripard 	if (IS_ERR(reg)) {
28366e79cf1SChen-Yu Tsai 		pr_err("Could not get registers for a80-apb1-clk: %s\n",
28466e79cf1SChen-Yu Tsai 		       node->name);
28566e79cf1SChen-Yu Tsai 		return;
28666e79cf1SChen-Yu Tsai 	}
28766e79cf1SChen-Yu Tsai 
28866e79cf1SChen-Yu Tsai 	sunxi_factors_register(node, &sun9i_a80_apb1_data,
28966e79cf1SChen-Yu Tsai 			       &sun9i_a80_apb1_lock, reg);
2903b2bd70fSChen-Yu Tsai }
2913b2bd70fSChen-Yu Tsai CLK_OF_DECLARE(sun9i_a80_apb1, "allwinner,sun9i-a80-apb1-clk", sun9i_a80_apb1_setup);
292