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