1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. 4 */ 5 6 #include <linux/kernel.h> 7 #include <linux/io.h> 8 #include <linux/err.h> 9 #include <linux/slab.h> 10 #include <linux/clk-provider.h> 11 12 #include "clk.h" 13 14 #define pll_out_override(p) (BIT((p->shift - 6))) 15 #define div_mask(d) ((1 << (d->width)) - 1) 16 #define get_mul(d) (1 << d->frac_width) 17 #define get_max_div(d) div_mask(d) 18 19 #define PERIPH_CLK_UART_DIV_ENB BIT(24) 20 21 static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate, 22 unsigned long parent_rate) 23 { 24 int div; 25 26 div = div_frac_get(rate, parent_rate, divider->width, 27 divider->frac_width, divider->flags); 28 29 if (div < 0) 30 return 0; 31 32 return div; 33 } 34 35 static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw, 36 unsigned long parent_rate) 37 { 38 struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); 39 u32 reg; 40 int div, mul; 41 u64 rate = parent_rate; 42 43 reg = readl_relaxed(divider->reg); 44 45 if ((divider->flags & TEGRA_DIVIDER_UART) && 46 !(reg & PERIPH_CLK_UART_DIV_ENB)) 47 return rate; 48 49 div = (reg >> divider->shift) & div_mask(divider); 50 51 mul = get_mul(divider); 52 div += mul; 53 54 rate *= mul; 55 rate += div - 1; 56 do_div(rate, div); 57 58 return rate; 59 } 60 61 static int clk_frac_div_determine_rate(struct clk_hw *hw, 62 struct clk_rate_request *req) 63 { 64 struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); 65 int div, mul; 66 unsigned long output_rate = req->best_parent_rate; 67 68 if (!req->rate) { 69 req->rate = output_rate; 70 71 return 0; 72 } 73 74 div = get_div(divider, req->rate, output_rate); 75 if (div < 0) { 76 req->rate = req->best_parent_rate; 77 78 return 0; 79 } 80 81 mul = get_mul(divider); 82 83 req->rate = DIV_ROUND_UP(output_rate * mul, div + mul); 84 85 return 0; 86 } 87 88 static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate, 89 unsigned long parent_rate) 90 { 91 struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); 92 int div; 93 unsigned long flags = 0; 94 u32 val; 95 96 div = get_div(divider, rate, parent_rate); 97 if (div < 0) 98 return div; 99 100 if (divider->lock) 101 spin_lock_irqsave(divider->lock, flags); 102 103 val = readl_relaxed(divider->reg); 104 val &= ~(div_mask(divider) << divider->shift); 105 val |= div << divider->shift; 106 107 if (divider->flags & TEGRA_DIVIDER_UART) { 108 if (div) 109 val |= PERIPH_CLK_UART_DIV_ENB; 110 else 111 val &= ~PERIPH_CLK_UART_DIV_ENB; 112 } 113 114 if (divider->flags & TEGRA_DIVIDER_FIXED) 115 val |= pll_out_override(divider); 116 117 writel_relaxed(val, divider->reg); 118 119 if (divider->lock) 120 spin_unlock_irqrestore(divider->lock, flags); 121 122 return 0; 123 } 124 125 static void clk_divider_restore_context(struct clk_hw *hw) 126 { 127 struct clk_hw *parent = clk_hw_get_parent(hw); 128 unsigned long parent_rate = clk_hw_get_rate(parent); 129 unsigned long rate = clk_hw_get_rate(hw); 130 131 if (clk_frac_div_set_rate(hw, rate, parent_rate) < 0) 132 WARN_ON(1); 133 } 134 135 const struct clk_ops tegra_clk_frac_div_ops = { 136 .recalc_rate = clk_frac_div_recalc_rate, 137 .set_rate = clk_frac_div_set_rate, 138 .determine_rate = clk_frac_div_determine_rate, 139 .restore_context = clk_divider_restore_context, 140 }; 141 142 struct clk *tegra_clk_register_divider(const char *name, 143 const char *parent_name, void __iomem *reg, 144 unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width, 145 u8 frac_width, spinlock_t *lock) 146 { 147 struct tegra_clk_frac_div *divider; 148 struct clk *clk; 149 struct clk_init_data init; 150 151 divider = kzalloc(sizeof(*divider), GFP_KERNEL); 152 if (!divider) { 153 pr_err("%s: could not allocate fractional divider clk\n", 154 __func__); 155 return ERR_PTR(-ENOMEM); 156 } 157 158 init.name = name; 159 init.ops = &tegra_clk_frac_div_ops; 160 init.flags = flags; 161 init.parent_names = parent_name ? &parent_name : NULL; 162 init.num_parents = parent_name ? 1 : 0; 163 164 divider->reg = reg; 165 divider->shift = shift; 166 divider->width = width; 167 divider->frac_width = frac_width; 168 divider->lock = lock; 169 divider->flags = clk_divider_flags; 170 171 /* Data in .init is copied by clk_register(), so stack variable OK */ 172 divider->hw.init = &init; 173 174 clk = clk_register(NULL, ÷r->hw); 175 if (IS_ERR(clk)) 176 kfree(divider); 177 178 return clk; 179 } 180 181 static const struct clk_div_table mc_div_table[] = { 182 { .val = 0, .div = 2 }, 183 { .val = 1, .div = 1 }, 184 { .val = 0, .div = 0 }, 185 }; 186 187 struct clk *tegra_clk_register_mc(const char *name, const char *parent_name, 188 void __iomem *reg, spinlock_t *lock) 189 { 190 return clk_register_divider_table(NULL, name, parent_name, 191 CLK_IS_CRITICAL, 192 reg, 16, 1, CLK_DIVIDER_READ_ONLY, 193 mc_div_table, lock); 194 } 195