1 /* 2 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 3 * 4 * This program is free software; you can redistribute it and/or modify it 5 * under the terms and conditions of the GNU General Public License, 6 * version 2, as published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope it will be useful, but WITHOUT 9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11 * more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17 #include <linux/kernel.h> 18 #include <linux/io.h> 19 #include <linux/err.h> 20 #include <linux/slab.h> 21 #include <linux/clk-provider.h> 22 #include <linux/clk.h> 23 24 #include "clk.h" 25 26 #define pll_out_override(p) (BIT((p->shift - 6))) 27 #define div_mask(d) ((1 << (d->width)) - 1) 28 #define get_mul(d) (1 << d->frac_width) 29 #define get_max_div(d) div_mask(d) 30 31 #define PERIPH_CLK_UART_DIV_ENB BIT(24) 32 33 static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate, 34 unsigned long parent_rate) 35 { 36 s64 divider_ux1 = parent_rate; 37 u8 flags = divider->flags; 38 int mul; 39 40 if (!rate) 41 return 0; 42 43 mul = get_mul(divider); 44 45 if (!(flags & TEGRA_DIVIDER_INT)) 46 divider_ux1 *= mul; 47 48 if (flags & TEGRA_DIVIDER_ROUND_UP) 49 divider_ux1 += rate - 1; 50 51 do_div(divider_ux1, rate); 52 53 if (flags & TEGRA_DIVIDER_INT) 54 divider_ux1 *= mul; 55 56 divider_ux1 -= mul; 57 58 if (divider_ux1 < 0) 59 return 0; 60 61 if (divider_ux1 > get_max_div(divider)) 62 return -EINVAL; 63 64 return divider_ux1; 65 } 66 67 static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw, 68 unsigned long parent_rate) 69 { 70 struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); 71 u32 reg; 72 int div, mul; 73 u64 rate = parent_rate; 74 75 reg = readl_relaxed(divider->reg) >> divider->shift; 76 div = reg & div_mask(divider); 77 78 mul = get_mul(divider); 79 div += mul; 80 81 rate *= mul; 82 rate += div - 1; 83 do_div(rate, div); 84 85 return rate; 86 } 87 88 static long clk_frac_div_round_rate(struct clk_hw *hw, unsigned long rate, 89 unsigned long *prate) 90 { 91 struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); 92 int div, mul; 93 unsigned long output_rate = *prate; 94 95 if (!rate) 96 return output_rate; 97 98 div = get_div(divider, rate, output_rate); 99 if (div < 0) 100 return *prate; 101 102 mul = get_mul(divider); 103 104 return DIV_ROUND_UP(output_rate * mul, div + mul); 105 } 106 107 static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate, 108 unsigned long parent_rate) 109 { 110 struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); 111 int div; 112 unsigned long flags = 0; 113 u32 val; 114 115 div = get_div(divider, rate, parent_rate); 116 if (div < 0) 117 return div; 118 119 if (divider->lock) 120 spin_lock_irqsave(divider->lock, flags); 121 122 val = readl_relaxed(divider->reg); 123 val &= ~(div_mask(divider) << divider->shift); 124 val |= div << divider->shift; 125 126 if (divider->flags & TEGRA_DIVIDER_UART) { 127 if (div) 128 val |= PERIPH_CLK_UART_DIV_ENB; 129 else 130 val &= ~PERIPH_CLK_UART_DIV_ENB; 131 } 132 133 if (divider->flags & TEGRA_DIVIDER_FIXED) 134 val |= pll_out_override(divider); 135 136 writel_relaxed(val, divider->reg); 137 138 if (divider->lock) 139 spin_unlock_irqrestore(divider->lock, flags); 140 141 return 0; 142 } 143 144 const struct clk_ops tegra_clk_frac_div_ops = { 145 .recalc_rate = clk_frac_div_recalc_rate, 146 .set_rate = clk_frac_div_set_rate, 147 .round_rate = clk_frac_div_round_rate, 148 }; 149 150 struct clk *tegra_clk_register_divider(const char *name, 151 const char *parent_name, void __iomem *reg, 152 unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width, 153 u8 frac_width, spinlock_t *lock) 154 { 155 struct tegra_clk_frac_div *divider; 156 struct clk *clk; 157 struct clk_init_data init; 158 159 divider = kzalloc(sizeof(*divider), GFP_KERNEL); 160 if (!divider) { 161 pr_err("%s: could not allocate fractional divider clk\n", 162 __func__); 163 return ERR_PTR(-ENOMEM); 164 } 165 166 init.name = name; 167 init.ops = &tegra_clk_frac_div_ops; 168 init.flags = flags; 169 init.parent_names = parent_name ? &parent_name : NULL; 170 init.num_parents = parent_name ? 1 : 0; 171 172 divider->reg = reg; 173 divider->shift = shift; 174 divider->width = width; 175 divider->frac_width = frac_width; 176 divider->lock = lock; 177 divider->flags = clk_divider_flags; 178 179 /* Data in .init is copied by clk_register(), so stack variable OK */ 180 divider->hw.init = &init; 181 182 clk = clk_register(NULL, ÷r->hw); 183 if (IS_ERR(clk)) 184 kfree(divider); 185 186 return clk; 187 } 188