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