1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright 2012 Freescale Semiconductor, Inc. 4 */ 5 6 #include <linux/clk-provider.h> 7 #include <linux/err.h> 8 #include <linux/io.h> 9 #include <linux/slab.h> 10 #include "clk.h" 11 12 /** 13 * struct clk_ref - mxs reference clock 14 * @hw: clk_hw for the reference clock 15 * @reg: register address 16 * @idx: the index of the reference clock within the same register 17 * 18 * The mxs reference clock sources from pll. Every 4 reference clocks share 19 * one register space, and @idx is used to identify them. Each reference 20 * clock has a gate control and a fractional * divider. The rate is calculated 21 * as pll rate * (18 / FRAC), where FRAC = 18 ~ 35. 22 */ 23 struct clk_ref { 24 struct clk_hw hw; 25 void __iomem *reg; 26 u8 idx; 27 }; 28 29 #define to_clk_ref(_hw) container_of(_hw, struct clk_ref, hw) 30 31 static int clk_ref_enable(struct clk_hw *hw) 32 { 33 struct clk_ref *ref = to_clk_ref(hw); 34 35 writel_relaxed(1 << ((ref->idx + 1) * 8 - 1), ref->reg + CLR); 36 37 return 0; 38 } 39 40 static void clk_ref_disable(struct clk_hw *hw) 41 { 42 struct clk_ref *ref = to_clk_ref(hw); 43 44 writel_relaxed(1 << ((ref->idx + 1) * 8 - 1), ref->reg + SET); 45 } 46 47 static unsigned long clk_ref_recalc_rate(struct clk_hw *hw, 48 unsigned long parent_rate) 49 { 50 struct clk_ref *ref = to_clk_ref(hw); 51 u64 tmp = parent_rate; 52 u8 frac = (readl_relaxed(ref->reg) >> (ref->idx * 8)) & 0x3f; 53 54 tmp *= 18; 55 do_div(tmp, frac); 56 57 return tmp; 58 } 59 60 static long clk_ref_round_rate(struct clk_hw *hw, unsigned long rate, 61 unsigned long *prate) 62 { 63 unsigned long parent_rate = *prate; 64 u64 tmp = parent_rate; 65 u8 frac; 66 67 tmp = tmp * 18 + rate / 2; 68 do_div(tmp, rate); 69 frac = clamp(tmp, 18, 35); 70 71 tmp = parent_rate; 72 tmp *= 18; 73 do_div(tmp, frac); 74 75 return tmp; 76 } 77 78 static int clk_ref_set_rate(struct clk_hw *hw, unsigned long rate, 79 unsigned long parent_rate) 80 { 81 struct clk_ref *ref = to_clk_ref(hw); 82 unsigned long flags; 83 u64 tmp = parent_rate; 84 u32 val; 85 u8 frac, shift = ref->idx * 8; 86 87 tmp = tmp * 18 + rate / 2; 88 do_div(tmp, rate); 89 frac = clamp(tmp, 18, 35); 90 91 spin_lock_irqsave(&mxs_lock, flags); 92 93 val = readl_relaxed(ref->reg); 94 val &= ~(0x3f << shift); 95 val |= frac << shift; 96 writel_relaxed(val, ref->reg); 97 98 spin_unlock_irqrestore(&mxs_lock, flags); 99 100 return 0; 101 } 102 103 static const struct clk_ops clk_ref_ops = { 104 .enable = clk_ref_enable, 105 .disable = clk_ref_disable, 106 .recalc_rate = clk_ref_recalc_rate, 107 .round_rate = clk_ref_round_rate, 108 .set_rate = clk_ref_set_rate, 109 }; 110 111 struct clk *mxs_clk_ref(const char *name, const char *parent_name, 112 void __iomem *reg, u8 idx) 113 { 114 struct clk_ref *ref; 115 struct clk *clk; 116 struct clk_init_data init; 117 118 ref = kzalloc(sizeof(*ref), GFP_KERNEL); 119 if (!ref) 120 return ERR_PTR(-ENOMEM); 121 122 init.name = name; 123 init.ops = &clk_ref_ops; 124 init.flags = 0; 125 init.parent_names = (parent_name ? &parent_name: NULL); 126 init.num_parents = (parent_name ? 1 : 0); 127 128 ref->reg = reg; 129 ref->idx = idx; 130 ref->hw.init = &init; 131 132 clk = clk_register(NULL, &ref->hw); 133 if (IS_ERR(clk)) 134 kfree(ref); 135 136 return clk; 137 } 138