1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2016 Freescale Semiconductor, Inc. 4 * Copyright 2017~2018 NXP 5 * 6 * Author: Dong Aisheng <aisheng.dong@nxp.com> 7 * 8 */ 9 10 #include <linux/clk-provider.h> 11 #include <linux/err.h> 12 #include <linux/iopoll.h> 13 #include <linux/slab.h> 14 15 #include "clk.h" 16 17 /** 18 * struct clk_pfdv2 - IMX PFD clock 19 * @clk_hw: clock source 20 * @reg: PFD register address 21 * @gate_bit: Gate bit offset 22 * @vld_bit: Valid bit offset 23 * @frac_off: PLL Fractional Divider offset 24 */ 25 26 struct clk_pfdv2 { 27 struct clk_hw hw; 28 void __iomem *reg; 29 u8 gate_bit; 30 u8 vld_bit; 31 u8 frac_off; 32 }; 33 34 #define to_clk_pfdv2(_hw) container_of(_hw, struct clk_pfdv2, hw) 35 36 #define CLK_PFDV2_FRAC_MASK 0x3f 37 38 #define LOCK_TIMEOUT_US USEC_PER_MSEC 39 40 static DEFINE_SPINLOCK(pfd_lock); 41 42 static int clk_pfdv2_wait(struct clk_pfdv2 *pfd) 43 { 44 u32 val; 45 46 return readl_poll_timeout(pfd->reg, val, val & pfd->vld_bit, 47 0, LOCK_TIMEOUT_US); 48 } 49 50 static int clk_pfdv2_enable(struct clk_hw *hw) 51 { 52 struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 53 unsigned long flags; 54 u32 val; 55 56 spin_lock_irqsave(&pfd_lock, flags); 57 val = readl_relaxed(pfd->reg); 58 val &= ~pfd->gate_bit; 59 writel_relaxed(val, pfd->reg); 60 spin_unlock_irqrestore(&pfd_lock, flags); 61 62 return clk_pfdv2_wait(pfd); 63 } 64 65 static void clk_pfdv2_disable(struct clk_hw *hw) 66 { 67 struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 68 unsigned long flags; 69 u32 val; 70 71 spin_lock_irqsave(&pfd_lock, flags); 72 val = readl_relaxed(pfd->reg); 73 val |= pfd->gate_bit; 74 writel_relaxed(val, pfd->reg); 75 spin_unlock_irqrestore(&pfd_lock, flags); 76 } 77 78 static unsigned long clk_pfdv2_recalc_rate(struct clk_hw *hw, 79 unsigned long parent_rate) 80 { 81 struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 82 u64 tmp = parent_rate; 83 u8 frac; 84 85 frac = (readl_relaxed(pfd->reg) >> pfd->frac_off) 86 & CLK_PFDV2_FRAC_MASK; 87 88 if (!frac) { 89 pr_debug("clk_pfdv2: %s invalid pfd frac value 0\n", 90 clk_hw_get_name(hw)); 91 return 0; 92 } 93 94 tmp *= 18; 95 do_div(tmp, frac); 96 97 return tmp; 98 } 99 100 static long clk_pfdv2_round_rate(struct clk_hw *hw, unsigned long rate, 101 unsigned long *prate) 102 { 103 u64 tmp = *prate; 104 u8 frac; 105 106 tmp = tmp * 18 + rate / 2; 107 do_div(tmp, rate); 108 frac = tmp; 109 110 if (frac < 12) 111 frac = 12; 112 else if (frac > 35) 113 frac = 35; 114 115 tmp = *prate; 116 tmp *= 18; 117 do_div(tmp, frac); 118 119 return tmp; 120 } 121 122 static int clk_pfdv2_is_enabled(struct clk_hw *hw) 123 { 124 struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 125 126 if (readl_relaxed(pfd->reg) & pfd->gate_bit) 127 return 0; 128 129 return 1; 130 } 131 132 static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate, 133 unsigned long parent_rate) 134 { 135 struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 136 unsigned long flags; 137 u64 tmp = parent_rate; 138 u32 val; 139 u8 frac; 140 141 tmp = tmp * 18 + rate / 2; 142 do_div(tmp, rate); 143 frac = tmp; 144 if (frac < 12) 145 frac = 12; 146 else if (frac > 35) 147 frac = 35; 148 149 spin_lock_irqsave(&pfd_lock, flags); 150 val = readl_relaxed(pfd->reg); 151 val &= ~(CLK_PFDV2_FRAC_MASK << pfd->frac_off); 152 val |= frac << pfd->frac_off; 153 writel_relaxed(val, pfd->reg); 154 spin_unlock_irqrestore(&pfd_lock, flags); 155 156 return 0; 157 } 158 159 static const struct clk_ops clk_pfdv2_ops = { 160 .enable = clk_pfdv2_enable, 161 .disable = clk_pfdv2_disable, 162 .recalc_rate = clk_pfdv2_recalc_rate, 163 .round_rate = clk_pfdv2_round_rate, 164 .set_rate = clk_pfdv2_set_rate, 165 .is_enabled = clk_pfdv2_is_enabled, 166 }; 167 168 struct clk_hw *imx_clk_pfdv2(const char *name, const char *parent_name, 169 void __iomem *reg, u8 idx) 170 { 171 struct clk_init_data init; 172 struct clk_pfdv2 *pfd; 173 struct clk_hw *hw; 174 int ret; 175 176 WARN_ON(idx > 3); 177 178 pfd = kzalloc(sizeof(*pfd), GFP_KERNEL); 179 if (!pfd) 180 return ERR_PTR(-ENOMEM); 181 182 pfd->reg = reg; 183 pfd->gate_bit = 1 << ((idx + 1) * 8 - 1); 184 pfd->vld_bit = pfd->gate_bit - 1; 185 pfd->frac_off = idx * 8; 186 187 init.name = name; 188 init.ops = &clk_pfdv2_ops; 189 init.parent_names = &parent_name; 190 init.num_parents = 1; 191 init.flags = CLK_SET_RATE_GATE; 192 193 pfd->hw.init = &init; 194 195 hw = &pfd->hw; 196 ret = clk_hw_register(NULL, hw); 197 if (ret) { 198 kfree(pfd); 199 hw = ERR_PTR(ret); 200 } 201 202 return hw; 203 } 204