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