clk-pfdv2.c (5158B)
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 * @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 27struct 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 41static DEFINE_SPINLOCK(pfd_lock); 42 43static 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 51static 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 66static 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 79static 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 101static 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 142static 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 152static 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 /* 165 * PFD can NOT change rate without gating. 166 * as the PFDs may enabled in HW by default but no 167 * consumer used it, the enable count is '0', so the 168 * 'SET_RATE_GATE' can NOT help on blocking the set_rate 169 * ops especially for 'assigned-clock-xxx'. In order 170 * to simplify the case, just disable the PFD if it is 171 * enabled in HW but not in SW. 172 */ 173 if (clk_pfdv2_is_enabled(hw)) 174 clk_pfdv2_disable(hw); 175 176 tmp = tmp * 18 + rate / 2; 177 do_div(tmp, rate); 178 frac = tmp; 179 if (frac < 12) 180 frac = 12; 181 else if (frac > 35) 182 frac = 35; 183 184 spin_lock_irqsave(&pfd_lock, flags); 185 val = readl_relaxed(pfd->reg); 186 val &= ~(CLK_PFDV2_FRAC_MASK << pfd->frac_off); 187 val |= frac << pfd->frac_off; 188 writel_relaxed(val, pfd->reg); 189 spin_unlock_irqrestore(&pfd_lock, flags); 190 191 return 0; 192} 193 194static const struct clk_ops clk_pfdv2_ops = { 195 .enable = clk_pfdv2_enable, 196 .disable = clk_pfdv2_disable, 197 .recalc_rate = clk_pfdv2_recalc_rate, 198 .determine_rate = clk_pfdv2_determine_rate, 199 .set_rate = clk_pfdv2_set_rate, 200 .is_enabled = clk_pfdv2_is_enabled, 201}; 202 203struct clk_hw *imx_clk_hw_pfdv2(enum imx_pfdv2_type type, const char *name, 204 const char *parent_name, void __iomem *reg, u8 idx) 205{ 206 struct clk_init_data init; 207 struct clk_pfdv2 *pfd; 208 struct clk_hw *hw; 209 int ret; 210 211 WARN_ON(idx > 3); 212 213 pfd = kzalloc(sizeof(*pfd), GFP_KERNEL); 214 if (!pfd) 215 return ERR_PTR(-ENOMEM); 216 217 pfd->reg = reg; 218 pfd->gate_bit = (idx + 1) * 8 - 1; 219 pfd->vld_bit = pfd->gate_bit - 1; 220 pfd->frac_off = idx * 8; 221 222 init.name = name; 223 init.ops = &clk_pfdv2_ops; 224 init.parent_names = &parent_name; 225 init.num_parents = 1; 226 if (type == IMX_PFDV2_IMX7ULP) 227 init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; 228 else 229 init.flags = CLK_SET_RATE_GATE; 230 231 pfd->hw.init = &init; 232 233 hw = &pfd->hw; 234 ret = clk_hw_register(NULL, hw); 235 if (ret) { 236 kfree(pfd); 237 hw = ERR_PTR(ret); 238 } 239 240 return hw; 241} 242EXPORT_SYMBOL_GPL(imx_clk_hw_pfdv2);