atmel-isc-clk.c (6968B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Microchip Image Sensor Controller (ISC) common clock driver setup 4 * 5 * Copyright (C) 2016 Microchip Technology, Inc. 6 * 7 * Author: Songjun Wu 8 * Author: Eugen Hristev <eugen.hristev@microchip.com> 9 * 10 */ 11#include <linux/clk.h> 12#include <linux/clkdev.h> 13#include <linux/clk-provider.h> 14#include <linux/pm_runtime.h> 15#include <linux/regmap.h> 16 17#include "atmel-isc-regs.h" 18#include "atmel-isc.h" 19 20static int isc_wait_clk_stable(struct clk_hw *hw) 21{ 22 struct isc_clk *isc_clk = to_isc_clk(hw); 23 struct regmap *regmap = isc_clk->regmap; 24 unsigned long timeout = jiffies + usecs_to_jiffies(1000); 25 unsigned int status; 26 27 while (time_before(jiffies, timeout)) { 28 regmap_read(regmap, ISC_CLKSR, &status); 29 if (!(status & ISC_CLKSR_SIP)) 30 return 0; 31 32 usleep_range(10, 250); 33 } 34 35 return -ETIMEDOUT; 36} 37 38static int isc_clk_prepare(struct clk_hw *hw) 39{ 40 struct isc_clk *isc_clk = to_isc_clk(hw); 41 int ret; 42 43 ret = pm_runtime_resume_and_get(isc_clk->dev); 44 if (ret < 0) 45 return ret; 46 47 return isc_wait_clk_stable(hw); 48} 49 50static void isc_clk_unprepare(struct clk_hw *hw) 51{ 52 struct isc_clk *isc_clk = to_isc_clk(hw); 53 54 isc_wait_clk_stable(hw); 55 56 pm_runtime_put_sync(isc_clk->dev); 57} 58 59static int isc_clk_enable(struct clk_hw *hw) 60{ 61 struct isc_clk *isc_clk = to_isc_clk(hw); 62 u32 id = isc_clk->id; 63 struct regmap *regmap = isc_clk->regmap; 64 unsigned long flags; 65 unsigned int status; 66 67 dev_dbg(isc_clk->dev, "ISC CLK: %s, id = %d, div = %d, parent id = %d\n", 68 __func__, id, isc_clk->div, isc_clk->parent_id); 69 70 spin_lock_irqsave(&isc_clk->lock, flags); 71 regmap_update_bits(regmap, ISC_CLKCFG, 72 ISC_CLKCFG_DIV_MASK(id) | ISC_CLKCFG_SEL_MASK(id), 73 (isc_clk->div << ISC_CLKCFG_DIV_SHIFT(id)) | 74 (isc_clk->parent_id << ISC_CLKCFG_SEL_SHIFT(id))); 75 76 regmap_write(regmap, ISC_CLKEN, ISC_CLK(id)); 77 spin_unlock_irqrestore(&isc_clk->lock, flags); 78 79 regmap_read(regmap, ISC_CLKSR, &status); 80 if (status & ISC_CLK(id)) 81 return 0; 82 else 83 return -EINVAL; 84} 85 86static void isc_clk_disable(struct clk_hw *hw) 87{ 88 struct isc_clk *isc_clk = to_isc_clk(hw); 89 u32 id = isc_clk->id; 90 unsigned long flags; 91 92 spin_lock_irqsave(&isc_clk->lock, flags); 93 regmap_write(isc_clk->regmap, ISC_CLKDIS, ISC_CLK(id)); 94 spin_unlock_irqrestore(&isc_clk->lock, flags); 95} 96 97static int isc_clk_is_enabled(struct clk_hw *hw) 98{ 99 struct isc_clk *isc_clk = to_isc_clk(hw); 100 u32 status; 101 int ret; 102 103 ret = pm_runtime_resume_and_get(isc_clk->dev); 104 if (ret < 0) 105 return 0; 106 107 regmap_read(isc_clk->regmap, ISC_CLKSR, &status); 108 109 pm_runtime_put_sync(isc_clk->dev); 110 111 return status & ISC_CLK(isc_clk->id) ? 1 : 0; 112} 113 114static unsigned long 115isc_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) 116{ 117 struct isc_clk *isc_clk = to_isc_clk(hw); 118 119 return DIV_ROUND_CLOSEST(parent_rate, isc_clk->div + 1); 120} 121 122static int isc_clk_determine_rate(struct clk_hw *hw, 123 struct clk_rate_request *req) 124{ 125 struct isc_clk *isc_clk = to_isc_clk(hw); 126 long best_rate = -EINVAL; 127 int best_diff = -1; 128 unsigned int i, div; 129 130 for (i = 0; i < clk_hw_get_num_parents(hw); i++) { 131 struct clk_hw *parent; 132 unsigned long parent_rate; 133 134 parent = clk_hw_get_parent_by_index(hw, i); 135 if (!parent) 136 continue; 137 138 parent_rate = clk_hw_get_rate(parent); 139 if (!parent_rate) 140 continue; 141 142 for (div = 1; div < ISC_CLK_MAX_DIV + 2; div++) { 143 unsigned long rate; 144 int diff; 145 146 rate = DIV_ROUND_CLOSEST(parent_rate, div); 147 diff = abs(req->rate - rate); 148 149 if (best_diff < 0 || best_diff > diff) { 150 best_rate = rate; 151 best_diff = diff; 152 req->best_parent_rate = parent_rate; 153 req->best_parent_hw = parent; 154 } 155 156 if (!best_diff || rate < req->rate) 157 break; 158 } 159 160 if (!best_diff) 161 break; 162 } 163 164 dev_dbg(isc_clk->dev, 165 "ISC CLK: %s, best_rate = %ld, parent clk: %s @ %ld\n", 166 __func__, best_rate, 167 __clk_get_name((req->best_parent_hw)->clk), 168 req->best_parent_rate); 169 170 if (best_rate < 0) 171 return best_rate; 172 173 req->rate = best_rate; 174 175 return 0; 176} 177 178static int isc_clk_set_parent(struct clk_hw *hw, u8 index) 179{ 180 struct isc_clk *isc_clk = to_isc_clk(hw); 181 182 if (index >= clk_hw_get_num_parents(hw)) 183 return -EINVAL; 184 185 isc_clk->parent_id = index; 186 187 return 0; 188} 189 190static u8 isc_clk_get_parent(struct clk_hw *hw) 191{ 192 struct isc_clk *isc_clk = to_isc_clk(hw); 193 194 return isc_clk->parent_id; 195} 196 197static int isc_clk_set_rate(struct clk_hw *hw, 198 unsigned long rate, 199 unsigned long parent_rate) 200{ 201 struct isc_clk *isc_clk = to_isc_clk(hw); 202 u32 div; 203 204 if (!rate) 205 return -EINVAL; 206 207 div = DIV_ROUND_CLOSEST(parent_rate, rate); 208 if (div > (ISC_CLK_MAX_DIV + 1) || !div) 209 return -EINVAL; 210 211 isc_clk->div = div - 1; 212 213 return 0; 214} 215 216static const struct clk_ops isc_clk_ops = { 217 .prepare = isc_clk_prepare, 218 .unprepare = isc_clk_unprepare, 219 .enable = isc_clk_enable, 220 .disable = isc_clk_disable, 221 .is_enabled = isc_clk_is_enabled, 222 .recalc_rate = isc_clk_recalc_rate, 223 .determine_rate = isc_clk_determine_rate, 224 .set_parent = isc_clk_set_parent, 225 .get_parent = isc_clk_get_parent, 226 .set_rate = isc_clk_set_rate, 227}; 228 229static int isc_clk_register(struct isc_device *isc, unsigned int id) 230{ 231 struct regmap *regmap = isc->regmap; 232 struct device_node *np = isc->dev->of_node; 233 struct isc_clk *isc_clk; 234 struct clk_init_data init; 235 const char *clk_name = np->name; 236 const char *parent_names[3]; 237 int num_parents; 238 239 if (id == ISC_ISPCK && !isc->ispck_required) 240 return 0; 241 242 num_parents = of_clk_get_parent_count(np); 243 if (num_parents < 1 || num_parents > 3) 244 return -EINVAL; 245 246 if (num_parents > 2 && id == ISC_ISPCK) 247 num_parents = 2; 248 249 of_clk_parent_fill(np, parent_names, num_parents); 250 251 if (id == ISC_MCK) 252 of_property_read_string(np, "clock-output-names", &clk_name); 253 else 254 clk_name = "isc-ispck"; 255 256 init.parent_names = parent_names; 257 init.num_parents = num_parents; 258 init.name = clk_name; 259 init.ops = &isc_clk_ops; 260 init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; 261 262 isc_clk = &isc->isc_clks[id]; 263 isc_clk->hw.init = &init; 264 isc_clk->regmap = regmap; 265 isc_clk->id = id; 266 isc_clk->dev = isc->dev; 267 spin_lock_init(&isc_clk->lock); 268 269 isc_clk->clk = clk_register(isc->dev, &isc_clk->hw); 270 if (IS_ERR(isc_clk->clk)) { 271 dev_err(isc->dev, "%s: clock register fail\n", clk_name); 272 return PTR_ERR(isc_clk->clk); 273 } else if (id == ISC_MCK) { 274 of_clk_add_provider(np, of_clk_src_simple_get, isc_clk->clk); 275 } 276 277 return 0; 278} 279 280int isc_clk_init(struct isc_device *isc) 281{ 282 unsigned int i; 283 int ret; 284 285 for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) 286 isc->isc_clks[i].clk = ERR_PTR(-EINVAL); 287 288 for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { 289 ret = isc_clk_register(isc, i); 290 if (ret) 291 return ret; 292 } 293 294 return 0; 295} 296EXPORT_SYMBOL_GPL(isc_clk_init); 297 298void isc_clk_cleanup(struct isc_device *isc) 299{ 300 unsigned int i; 301 302 of_clk_del_provider(isc->dev->of_node); 303 304 for (i = 0; i < ARRAY_SIZE(isc->isc_clks); i++) { 305 struct isc_clk *isc_clk = &isc->isc_clks[i]; 306 307 if (!IS_ERR(isc_clk->clk)) 308 clk_unregister(isc_clk->clk); 309 } 310} 311EXPORT_SYMBOL_GPL(isc_clk_cleanup);