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 int clk_ref_determine_rate(struct clk_hw *hw, 61 struct clk_rate_request *req) 62 { 63 unsigned long parent_rate = req->best_parent_rate; 64 u64 tmp = parent_rate; 65 u8 frac; 66 67 tmp = tmp * 18 + req->rate / 2; 68 do_div(tmp, req->rate); 69 frac = clamp(tmp, 18, 35); 70 71 tmp = parent_rate; 72 tmp *= 18; 73 do_div(tmp, frac); 74 75 req->rate = tmp; 76 77 return 0; 78 } 79 80 static int clk_ref_set_rate(struct clk_hw *hw, unsigned long rate, 81 unsigned long parent_rate) 82 { 83 struct clk_ref *ref = to_clk_ref(hw); 84 unsigned long flags; 85 u64 tmp = parent_rate; 86 u32 val; 87 u8 frac, shift = ref->idx * 8; 88 89 tmp = tmp * 18 + rate / 2; 90 do_div(tmp, rate); 91 frac = clamp(tmp, 18, 35); 92 93 spin_lock_irqsave(&mxs_lock, flags); 94 95 val = readl_relaxed(ref->reg); 96 val &= ~(0x3f << shift); 97 val |= frac << shift; 98 writel_relaxed(val, ref->reg); 99 100 spin_unlock_irqrestore(&mxs_lock, flags); 101 102 return 0; 103 } 104 105 static const struct clk_ops clk_ref_ops = { 106 .enable = clk_ref_enable, 107 .disable = clk_ref_disable, 108 .recalc_rate = clk_ref_recalc_rate, 109 .determine_rate = clk_ref_determine_rate, 110 .set_rate = clk_ref_set_rate, 111 }; 112 113 struct clk *mxs_clk_ref(const char *name, const char *parent_name, 114 void __iomem *reg, u8 idx) 115 { 116 struct clk_ref *ref; 117 struct clk *clk; 118 struct clk_init_data init; 119 120 ref = kzalloc(sizeof(*ref), GFP_KERNEL); 121 if (!ref) 122 return ERR_PTR(-ENOMEM); 123 124 init.name = name; 125 init.ops = &clk_ref_ops; 126 init.flags = 0; 127 init.parent_names = (parent_name ? &parent_name: NULL); 128 init.num_parents = (parent_name ? 1 : 0); 129 130 ref->reg = reg; 131 ref->idx = idx; 132 ref->hw.init = &init; 133 134 clk = clk_register(NULL, &ref->hw); 135 if (IS_ERR(clk)) 136 kfree(ref); 137 138 return clk; 139 } 140