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/io.h> 13 #include <linux/iopoll.h> 14 #include <linux/slab.h> 15 16 #include "clk.h" 17 18 /** 19 * struct clk_pfdv2 - IMX PFD clock 20 * @clk_hw: clock source 21 * @reg: PFD register address 22 * @gate_bit: Gate bit offset 23 * @vld_bit: Valid bit offset 24 * @frac_off: PLL Fractional Divider offset 25 */ 26 27 struct clk_pfdv2 { 28 struct clk_hw hw; 29 void __iomem *reg; 30 u8 gate_bit; 31 u8 vld_bit; 32 u8 frac_off; 33 }; 34 35 #define to_clk_pfdv2(_hw) container_of(_hw, struct clk_pfdv2, hw) 36 37 #define CLK_PFDV2_FRAC_MASK 0x3f 38 39 #define LOCK_TIMEOUT_US USEC_PER_MSEC 40 41 static DEFINE_SPINLOCK(pfd_lock); 42 43 static int clk_pfdv2_wait(struct clk_pfdv2 *pfd) 44 { 45 u32 val; 46 47 return readl_poll_timeout(pfd->reg, val, val & (1 << pfd->vld_bit), 48 0, LOCK_TIMEOUT_US); 49 } 50 51 static int clk_pfdv2_enable(struct clk_hw *hw) 52 { 53 struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 54 unsigned long flags; 55 u32 val; 56 57 spin_lock_irqsave(&pfd_lock, flags); 58 val = readl_relaxed(pfd->reg); 59 val &= ~(1 << pfd->gate_bit); 60 writel_relaxed(val, pfd->reg); 61 spin_unlock_irqrestore(&pfd_lock, flags); 62 63 return clk_pfdv2_wait(pfd); 64 } 65 66 static void clk_pfdv2_disable(struct clk_hw *hw) 67 { 68 struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 69 unsigned long flags; 70 u32 val; 71 72 spin_lock_irqsave(&pfd_lock, flags); 73 val = readl_relaxed(pfd->reg); 74 val |= (1 << pfd->gate_bit); 75 writel_relaxed(val, pfd->reg); 76 spin_unlock_irqrestore(&pfd_lock, flags); 77 } 78 79 static unsigned long clk_pfdv2_recalc_rate(struct clk_hw *hw, 80 unsigned long parent_rate) 81 { 82 struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 83 u64 tmp = parent_rate; 84 u8 frac; 85 86 frac = (readl_relaxed(pfd->reg) >> pfd->frac_off) 87 & CLK_PFDV2_FRAC_MASK; 88 89 if (!frac) { 90 pr_debug("clk_pfdv2: %s invalid pfd frac value 0\n", 91 clk_hw_get_name(hw)); 92 return 0; 93 } 94 95 tmp *= 18; 96 do_div(tmp, frac); 97 98 return tmp; 99 } 100 101 static int clk_pfdv2_determine_rate(struct clk_hw *hw, 102 struct clk_rate_request *req) 103 { 104 unsigned long parent_rates[] = { 105 480000000, 106 528000000, 107 req->best_parent_rate 108 }; 109 unsigned long best_rate = -1UL, rate = req->rate; 110 unsigned long best_parent_rate = req->best_parent_rate; 111 u64 tmp; 112 u8 frac; 113 int i; 114 115 for (i = 0; i < ARRAY_SIZE(parent_rates); i++) { 116 tmp = parent_rates[i]; 117 tmp = tmp * 18 + rate / 2; 118 do_div(tmp, rate); 119 frac = tmp; 120 121 if (frac < 12) 122 frac = 12; 123 else if (frac > 35) 124 frac = 35; 125 126 tmp = parent_rates[i]; 127 tmp *= 18; 128 do_div(tmp, frac); 129 130 if (abs(tmp - req->rate) < abs(best_rate - req->rate)) { 131 best_rate = tmp; 132 best_parent_rate = parent_rates[i]; 133 } 134 } 135 136 req->best_parent_rate = best_parent_rate; 137 req->rate = best_rate; 138 139 return 0; 140 } 141 142 static int clk_pfdv2_is_enabled(struct clk_hw *hw) 143 { 144 struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 145 146 if (readl_relaxed(pfd->reg) & (1 << pfd->gate_bit)) 147 return 0; 148 149 return 1; 150 } 151 152 static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate, 153 unsigned long parent_rate) 154 { 155 struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 156 unsigned long flags; 157 u64 tmp = parent_rate; 158 u32 val; 159 u8 frac; 160 161 if (!rate) 162 return -EINVAL; 163 164 /* PFD can NOT change rate without gating */ 165 WARN_ON(clk_pfdv2_is_enabled(hw)); 166 167 tmp = tmp * 18 + rate / 2; 168 do_div(tmp, rate); 169 frac = tmp; 170 if (frac < 12) 171 frac = 12; 172 else if (frac > 35) 173 frac = 35; 174 175 spin_lock_irqsave(&pfd_lock, flags); 176 val = readl_relaxed(pfd->reg); 177 val &= ~(CLK_PFDV2_FRAC_MASK << pfd->frac_off); 178 val |= frac << pfd->frac_off; 179 writel_relaxed(val, pfd->reg); 180 spin_unlock_irqrestore(&pfd_lock, flags); 181 182 return 0; 183 } 184 185 static const struct clk_ops clk_pfdv2_ops = { 186 .enable = clk_pfdv2_enable, 187 .disable = clk_pfdv2_disable, 188 .recalc_rate = clk_pfdv2_recalc_rate, 189 .determine_rate = clk_pfdv2_determine_rate, 190 .set_rate = clk_pfdv2_set_rate, 191 .is_enabled = clk_pfdv2_is_enabled, 192 }; 193 194 struct clk_hw *imx_clk_hw_pfdv2(const char *name, const char *parent_name, 195 void __iomem *reg, u8 idx) 196 { 197 struct clk_init_data init; 198 struct clk_pfdv2 *pfd; 199 struct clk_hw *hw; 200 int ret; 201 202 WARN_ON(idx > 3); 203 204 pfd = kzalloc(sizeof(*pfd), GFP_KERNEL); 205 if (!pfd) 206 return ERR_PTR(-ENOMEM); 207 208 pfd->reg = reg; 209 pfd->gate_bit = (idx + 1) * 8 - 1; 210 pfd->vld_bit = pfd->gate_bit - 1; 211 pfd->frac_off = idx * 8; 212 213 init.name = name; 214 init.ops = &clk_pfdv2_ops; 215 init.parent_names = &parent_name; 216 init.num_parents = 1; 217 init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; 218 219 pfd->hw.init = &init; 220 221 hw = &pfd->hw; 222 ret = clk_hw_register(NULL, hw); 223 if (ret) { 224 kfree(pfd); 225 hw = ERR_PTR(ret); 226 } 227 228 return hw; 229 } 230