1992a56e4SMaxime Ripard /* 2992a56e4SMaxime Ripard * Copyright 2013 Emilio López 3992a56e4SMaxime Ripard * 4992a56e4SMaxime Ripard * Emilio López <emilio@elopez.com.ar> 5992a56e4SMaxime Ripard * 6992a56e4SMaxime Ripard * This program is free software; you can redistribute it and/or modify 7992a56e4SMaxime Ripard * it under the terms of the GNU General Public License as published by 8992a56e4SMaxime Ripard * the Free Software Foundation; either version 2 of the License, or 9992a56e4SMaxime Ripard * (at your option) any later version. 10992a56e4SMaxime Ripard * 11992a56e4SMaxime Ripard * This program is distributed in the hope that it will be useful, 12992a56e4SMaxime Ripard * but WITHOUT ANY WARRANTY; without even the implied warranty of 13992a56e4SMaxime Ripard * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14992a56e4SMaxime Ripard * GNU General Public License for more details. 15992a56e4SMaxime Ripard */ 16992a56e4SMaxime Ripard 17992a56e4SMaxime Ripard #include <linux/clk-provider.h> 18992a56e4SMaxime Ripard #include <linux/clkdev.h> 19992a56e4SMaxime Ripard 20992a56e4SMaxime Ripard #include "clk-factors.h" 21992a56e4SMaxime Ripard 22992a56e4SMaxime Ripard /** 23992a56e4SMaxime Ripard * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks 24992a56e4SMaxime Ripard * MOD0 rate is calculated as follows 25992a56e4SMaxime Ripard * rate = (parent_rate >> p) / (m + 1); 26992a56e4SMaxime Ripard */ 27992a56e4SMaxime Ripard 28992a56e4SMaxime Ripard static void sun4i_a10_get_mod0_factors(u32 *freq, u32 parent_rate, 29992a56e4SMaxime Ripard u8 *n, u8 *k, u8 *m, u8 *p) 30992a56e4SMaxime Ripard { 31992a56e4SMaxime Ripard u8 div, calcm, calcp; 32992a56e4SMaxime Ripard 33992a56e4SMaxime Ripard /* These clocks can only divide, so we will never be able to achieve 34992a56e4SMaxime Ripard * frequencies higher than the parent frequency */ 35992a56e4SMaxime Ripard if (*freq > parent_rate) 36992a56e4SMaxime Ripard *freq = parent_rate; 37992a56e4SMaxime Ripard 38992a56e4SMaxime Ripard div = DIV_ROUND_UP(parent_rate, *freq); 39992a56e4SMaxime Ripard 40992a56e4SMaxime Ripard if (div < 16) 41992a56e4SMaxime Ripard calcp = 0; 42992a56e4SMaxime Ripard else if (div / 2 < 16) 43992a56e4SMaxime Ripard calcp = 1; 44992a56e4SMaxime Ripard else if (div / 4 < 16) 45992a56e4SMaxime Ripard calcp = 2; 46992a56e4SMaxime Ripard else 47992a56e4SMaxime Ripard calcp = 3; 48992a56e4SMaxime Ripard 49992a56e4SMaxime Ripard calcm = DIV_ROUND_UP(div, 1 << calcp); 50992a56e4SMaxime Ripard 51992a56e4SMaxime Ripard *freq = (parent_rate >> calcp) / calcm; 52992a56e4SMaxime Ripard 53992a56e4SMaxime Ripard /* we were called to round the frequency, we can now return */ 54992a56e4SMaxime Ripard if (n == NULL) 55992a56e4SMaxime Ripard return; 56992a56e4SMaxime Ripard 57992a56e4SMaxime Ripard *m = calcm - 1; 58992a56e4SMaxime Ripard *p = calcp; 59992a56e4SMaxime Ripard } 60992a56e4SMaxime Ripard 61992a56e4SMaxime Ripard /* user manual says "n" but it's really "p" */ 62992a56e4SMaxime Ripard static struct clk_factors_config sun4i_a10_mod0_config = { 63992a56e4SMaxime Ripard .mshift = 0, 64992a56e4SMaxime Ripard .mwidth = 4, 65992a56e4SMaxime Ripard .pshift = 16, 66992a56e4SMaxime Ripard .pwidth = 2, 67992a56e4SMaxime Ripard }; 68992a56e4SMaxime Ripard 69992a56e4SMaxime Ripard static const struct factors_data sun4i_a10_mod0_data __initconst = { 70992a56e4SMaxime Ripard .enable = 31, 71992a56e4SMaxime Ripard .mux = 24, 72992a56e4SMaxime Ripard .table = &sun4i_a10_mod0_config, 73992a56e4SMaxime Ripard .getter = sun4i_a10_get_mod0_factors, 74992a56e4SMaxime Ripard }; 75992a56e4SMaxime Ripard 76992a56e4SMaxime Ripard static DEFINE_SPINLOCK(sun4i_a10_mod0_lock); 77992a56e4SMaxime Ripard 78992a56e4SMaxime Ripard static void __init sun4i_a10_mod0_setup(struct device_node *node) 79992a56e4SMaxime Ripard { 80992a56e4SMaxime Ripard sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun4i_a10_mod0_lock); 81992a56e4SMaxime Ripard } 82992a56e4SMaxime Ripard CLK_OF_DECLARE(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk", sun4i_a10_mod0_setup); 83*eaa18f5dSMaxime Ripard 84*eaa18f5dSMaxime Ripard static DEFINE_SPINLOCK(sun5i_a13_mbus_lock); 85*eaa18f5dSMaxime Ripard 86*eaa18f5dSMaxime Ripard static void __init sun5i_a13_mbus_setup(struct device_node *node) 87*eaa18f5dSMaxime Ripard { 88*eaa18f5dSMaxime Ripard struct clk *mbus = sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun5i_a13_mbus_lock); 89*eaa18f5dSMaxime Ripard 90*eaa18f5dSMaxime Ripard /* The MBUS clocks needs to be always enabled */ 91*eaa18f5dSMaxime Ripard __clk_get(mbus); 92*eaa18f5dSMaxime Ripard clk_prepare_enable(mbus); 93*eaa18f5dSMaxime Ripard } 94*eaa18f5dSMaxime Ripard CLK_OF_DECLARE(sun5i_a13_mbus, "allwinner,sun5i-a13-mbus-clk", sun5i_a13_mbus_setup); 95