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 long clk_pfdv2_round_rate(struct clk_hw *hw, unsigned long rate, 102 unsigned long *prate) 103 { 104 u64 tmp = *prate; 105 u8 frac; 106 107 tmp = tmp * 18 + rate / 2; 108 do_div(tmp, rate); 109 frac = tmp; 110 111 if (frac < 12) 112 frac = 12; 113 else if (frac > 35) 114 frac = 35; 115 116 tmp = *prate; 117 tmp *= 18; 118 do_div(tmp, frac); 119 120 return tmp; 121 } 122 123 static int clk_pfdv2_is_enabled(struct clk_hw *hw) 124 { 125 struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 126 127 if (readl_relaxed(pfd->reg) & (1 << pfd->gate_bit)) 128 return 0; 129 130 return 1; 131 } 132 133 static int clk_pfdv2_set_rate(struct clk_hw *hw, unsigned long rate, 134 unsigned long parent_rate) 135 { 136 struct clk_pfdv2 *pfd = to_clk_pfdv2(hw); 137 unsigned long flags; 138 u64 tmp = parent_rate; 139 u32 val; 140 u8 frac; 141 142 tmp = tmp * 18 + rate / 2; 143 do_div(tmp, rate); 144 frac = tmp; 145 if (frac < 12) 146 frac = 12; 147 else if (frac > 35) 148 frac = 35; 149 150 spin_lock_irqsave(&pfd_lock, flags); 151 val = readl_relaxed(pfd->reg); 152 val &= ~(CLK_PFDV2_FRAC_MASK << pfd->frac_off); 153 val |= frac << pfd->frac_off; 154 writel_relaxed(val, pfd->reg); 155 spin_unlock_irqrestore(&pfd_lock, flags); 156 157 return 0; 158 } 159 160 static const struct clk_ops clk_pfdv2_ops = { 161 .enable = clk_pfdv2_enable, 162 .disable = clk_pfdv2_disable, 163 .recalc_rate = clk_pfdv2_recalc_rate, 164 .round_rate = clk_pfdv2_round_rate, 165 .set_rate = clk_pfdv2_set_rate, 166 .is_enabled = clk_pfdv2_is_enabled, 167 }; 168 169 struct clk_hw *imx_clk_hw_pfdv2(const char *name, const char *parent_name, 170 void __iomem *reg, u8 idx) 171 { 172 struct clk_init_data init; 173 struct clk_pfdv2 *pfd; 174 struct clk_hw *hw; 175 int ret; 176 177 WARN_ON(idx > 3); 178 179 pfd = kzalloc(sizeof(*pfd), GFP_KERNEL); 180 if (!pfd) 181 return ERR_PTR(-ENOMEM); 182 183 pfd->reg = reg; 184 pfd->gate_bit = (idx + 1) * 8 - 1; 185 pfd->vld_bit = pfd->gate_bit - 1; 186 pfd->frac_off = idx * 8; 187 188 init.name = name; 189 init.ops = &clk_pfdv2_ops; 190 init.parent_names = &parent_name; 191 init.num_parents = 1; 192 init.flags = CLK_SET_RATE_GATE; 193 194 pfd->hw.init = &init; 195 196 hw = &pfd->hw; 197 ret = clk_hw_register(NULL, hw); 198 if (ret) { 199 kfree(pfd); 200 hw = ERR_PTR(ret); 201 } 202 203 return hw; 204 } 205