1*992a56e4SMaxime Ripard /* 2*992a56e4SMaxime Ripard * Copyright 2013 Emilio López 3*992a56e4SMaxime Ripard * 4*992a56e4SMaxime Ripard * Emilio López <emilio@elopez.com.ar> 5*992a56e4SMaxime Ripard * 6*992a56e4SMaxime Ripard * This program is free software; you can redistribute it and/or modify 7*992a56e4SMaxime Ripard * it under the terms of the GNU General Public License as published by 8*992a56e4SMaxime Ripard * the Free Software Foundation; either version 2 of the License, or 9*992a56e4SMaxime Ripard * (at your option) any later version. 10*992a56e4SMaxime Ripard * 11*992a56e4SMaxime Ripard * This program is distributed in the hope that it will be useful, 12*992a56e4SMaxime Ripard * but WITHOUT ANY WARRANTY; without even the implied warranty of 13*992a56e4SMaxime Ripard * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14*992a56e4SMaxime Ripard * GNU General Public License for more details. 15*992a56e4SMaxime Ripard */ 16*992a56e4SMaxime Ripard 17*992a56e4SMaxime Ripard #include <linux/clk-provider.h> 18*992a56e4SMaxime Ripard #include <linux/clkdev.h> 19*992a56e4SMaxime Ripard 20*992a56e4SMaxime Ripard #include "clk-factors.h" 21*992a56e4SMaxime Ripard 22*992a56e4SMaxime Ripard /** 23*992a56e4SMaxime Ripard * sun4i_get_mod0_factors() - calculates m, n factors for MOD0-style clocks 24*992a56e4SMaxime Ripard * MOD0 rate is calculated as follows 25*992a56e4SMaxime Ripard * rate = (parent_rate >> p) / (m + 1); 26*992a56e4SMaxime Ripard */ 27*992a56e4SMaxime Ripard 28*992a56e4SMaxime Ripard static void sun4i_a10_get_mod0_factors(u32 *freq, u32 parent_rate, 29*992a56e4SMaxime Ripard u8 *n, u8 *k, u8 *m, u8 *p) 30*992a56e4SMaxime Ripard { 31*992a56e4SMaxime Ripard u8 div, calcm, calcp; 32*992a56e4SMaxime Ripard 33*992a56e4SMaxime Ripard /* These clocks can only divide, so we will never be able to achieve 34*992a56e4SMaxime Ripard * frequencies higher than the parent frequency */ 35*992a56e4SMaxime Ripard if (*freq > parent_rate) 36*992a56e4SMaxime Ripard *freq = parent_rate; 37*992a56e4SMaxime Ripard 38*992a56e4SMaxime Ripard div = DIV_ROUND_UP(parent_rate, *freq); 39*992a56e4SMaxime Ripard 40*992a56e4SMaxime Ripard if (div < 16) 41*992a56e4SMaxime Ripard calcp = 0; 42*992a56e4SMaxime Ripard else if (div / 2 < 16) 43*992a56e4SMaxime Ripard calcp = 1; 44*992a56e4SMaxime Ripard else if (div / 4 < 16) 45*992a56e4SMaxime Ripard calcp = 2; 46*992a56e4SMaxime Ripard else 47*992a56e4SMaxime Ripard calcp = 3; 48*992a56e4SMaxime Ripard 49*992a56e4SMaxime Ripard calcm = DIV_ROUND_UP(div, 1 << calcp); 50*992a56e4SMaxime Ripard 51*992a56e4SMaxime Ripard *freq = (parent_rate >> calcp) / calcm; 52*992a56e4SMaxime Ripard 53*992a56e4SMaxime Ripard /* we were called to round the frequency, we can now return */ 54*992a56e4SMaxime Ripard if (n == NULL) 55*992a56e4SMaxime Ripard return; 56*992a56e4SMaxime Ripard 57*992a56e4SMaxime Ripard *m = calcm - 1; 58*992a56e4SMaxime Ripard *p = calcp; 59*992a56e4SMaxime Ripard } 60*992a56e4SMaxime Ripard 61*992a56e4SMaxime Ripard /* user manual says "n" but it's really "p" */ 62*992a56e4SMaxime Ripard static struct clk_factors_config sun4i_a10_mod0_config = { 63*992a56e4SMaxime Ripard .mshift = 0, 64*992a56e4SMaxime Ripard .mwidth = 4, 65*992a56e4SMaxime Ripard .pshift = 16, 66*992a56e4SMaxime Ripard .pwidth = 2, 67*992a56e4SMaxime Ripard }; 68*992a56e4SMaxime Ripard 69*992a56e4SMaxime Ripard static const struct factors_data sun4i_a10_mod0_data __initconst = { 70*992a56e4SMaxime Ripard .enable = 31, 71*992a56e4SMaxime Ripard .mux = 24, 72*992a56e4SMaxime Ripard .table = &sun4i_a10_mod0_config, 73*992a56e4SMaxime Ripard .getter = sun4i_a10_get_mod0_factors, 74*992a56e4SMaxime Ripard }; 75*992a56e4SMaxime Ripard 76*992a56e4SMaxime Ripard static DEFINE_SPINLOCK(sun4i_a10_mod0_lock); 77*992a56e4SMaxime Ripard 78*992a56e4SMaxime Ripard static void __init sun4i_a10_mod0_setup(struct device_node *node) 79*992a56e4SMaxime Ripard { 80*992a56e4SMaxime Ripard sunxi_factors_register(node, &sun4i_a10_mod0_data, &sun4i_a10_mod0_lock); 81*992a56e4SMaxime Ripard } 82*992a56e4SMaxime Ripard CLK_OF_DECLARE(sun4i_a10_mod0, "allwinner,sun4i-a10-mod0-clk", sun4i_a10_mod0_setup); 83