clk-peripheral.c (42324d953b38e74cf5cb05a02c81d4922a2ddcd5) | clk-peripheral.c (b4c115c76184f2c56a295579161652fd5eb2dcc1) |
---|---|
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 4 */ 5 6#include <linux/bitops.h> 7#include <linux/clk-provider.h> 8#include <linux/clkdev.h> --- 24 unchanged lines hidden (view full) --- 33 struct clk_hw hw; 34 struct regmap *regmap; 35 struct clk_range range; 36 spinlock_t *lock; 37 u32 id; 38 u32 div; 39 const struct clk_pcr_layout *layout; 40 bool auto_div; | 1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 4 */ 5 6#include <linux/bitops.h> 7#include <linux/clk-provider.h> 8#include <linux/clkdev.h> --- 24 unchanged lines hidden (view full) --- 33 struct clk_hw hw; 34 struct regmap *regmap; 35 struct clk_range range; 36 spinlock_t *lock; 37 u32 id; 38 u32 div; 39 const struct clk_pcr_layout *layout; 40 bool auto_div; |
41 int chg_pid; |
|
41}; 42 43#define to_clk_sam9x5_peripheral(hw) \ 44 container_of(hw, struct clk_sam9x5_peripheral, hw) 45 46static int clk_peripheral_enable(struct clk_hw *hw) 47{ 48 struct clk_peripheral *periph = to_clk_peripheral(hw); --- 184 unchanged lines hidden (view full) --- 233 periph->auto_div = false; 234 } else { 235 clk_sam9x5_peripheral_autodiv(periph); 236 } 237 238 return parent_rate >> periph->div; 239} 240 | 42}; 43 44#define to_clk_sam9x5_peripheral(hw) \ 45 container_of(hw, struct clk_sam9x5_peripheral, hw) 46 47static int clk_peripheral_enable(struct clk_hw *hw) 48{ 49 struct clk_peripheral *periph = to_clk_peripheral(hw); --- 184 unchanged lines hidden (view full) --- 234 periph->auto_div = false; 235 } else { 236 clk_sam9x5_peripheral_autodiv(periph); 237 } 238 239 return parent_rate >> periph->div; 240} 241 |
242static void clk_sam9x5_peripheral_best_diff(struct clk_rate_request *req, 243 struct clk_hw *parent, 244 unsigned long parent_rate, 245 u32 shift, long *best_diff, 246 long *best_rate) 247{ 248 unsigned long tmp_rate = parent_rate >> shift; 249 unsigned long tmp_diff = abs(req->rate - tmp_rate); 250 251 if (*best_diff < 0 || *best_diff >= tmp_diff) { 252 *best_rate = tmp_rate; 253 *best_diff = tmp_diff; 254 req->best_parent_rate = parent_rate; 255 req->best_parent_hw = parent; 256 } 257} 258 259static int clk_sam9x5_peripheral_determine_rate(struct clk_hw *hw, 260 struct clk_rate_request *req) 261{ 262 struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); 263 struct clk_hw *parent = clk_hw_get_parent(hw); 264 struct clk_rate_request req_parent = *req; 265 unsigned long parent_rate = clk_hw_get_rate(parent); 266 unsigned long tmp_rate; 267 long best_rate = LONG_MIN; 268 long best_diff = LONG_MIN; 269 u32 shift; 270 271 if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) 272 return parent_rate; 273 274 /* Fist step: check the available dividers. */ 275 for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) { 276 tmp_rate = parent_rate >> shift; 277 278 if (periph->range.max && tmp_rate > periph->range.max) 279 continue; 280 281 clk_sam9x5_peripheral_best_diff(req, parent, parent_rate, 282 shift, &best_diff, &best_rate); 283 284 if (!best_diff || best_rate <= req->rate) 285 break; 286 } 287 288 if (periph->chg_pid < 0) 289 goto end; 290 291 /* Step two: try to request rate from parent. */ 292 parent = clk_hw_get_parent_by_index(hw, periph->chg_pid); 293 if (!parent) 294 goto end; 295 296 for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) { 297 req_parent.rate = req->rate << shift; 298 299 if (__clk_determine_rate(parent, &req_parent)) 300 continue; 301 302 clk_sam9x5_peripheral_best_diff(req, parent, req_parent.rate, 303 shift, &best_diff, &best_rate); 304 305 if (!best_diff) 306 break; 307 } 308end: 309 if (best_rate < 0 || 310 (periph->range.max && best_rate > periph->range.max)) 311 return -EINVAL; 312 313 pr_debug("PCK: %s, best_rate = %ld, parent clk: %s @ %ld\n", 314 __func__, best_rate, 315 __clk_get_name((req->best_parent_hw)->clk), 316 req->best_parent_rate); 317 318 req->rate = best_rate; 319 320 return 0; 321} 322 |
|
241static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw, 242 unsigned long rate, 243 unsigned long *parent_rate) 244{ 245 int shift = 0; 246 unsigned long best_rate; 247 unsigned long best_diff; 248 unsigned long cur_rate = *parent_rate; --- 66 unchanged lines hidden (view full) --- 315 .enable = clk_sam9x5_peripheral_enable, 316 .disable = clk_sam9x5_peripheral_disable, 317 .is_enabled = clk_sam9x5_peripheral_is_enabled, 318 .recalc_rate = clk_sam9x5_peripheral_recalc_rate, 319 .round_rate = clk_sam9x5_peripheral_round_rate, 320 .set_rate = clk_sam9x5_peripheral_set_rate, 321}; 322 | 323static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw, 324 unsigned long rate, 325 unsigned long *parent_rate) 326{ 327 int shift = 0; 328 unsigned long best_rate; 329 unsigned long best_diff; 330 unsigned long cur_rate = *parent_rate; --- 66 unchanged lines hidden (view full) --- 397 .enable = clk_sam9x5_peripheral_enable, 398 .disable = clk_sam9x5_peripheral_disable, 399 .is_enabled = clk_sam9x5_peripheral_is_enabled, 400 .recalc_rate = clk_sam9x5_peripheral_recalc_rate, 401 .round_rate = clk_sam9x5_peripheral_round_rate, 402 .set_rate = clk_sam9x5_peripheral_set_rate, 403}; 404 |
405static const struct clk_ops sam9x5_peripheral_chg_ops = { 406 .enable = clk_sam9x5_peripheral_enable, 407 .disable = clk_sam9x5_peripheral_disable, 408 .is_enabled = clk_sam9x5_peripheral_is_enabled, 409 .recalc_rate = clk_sam9x5_peripheral_recalc_rate, 410 .determine_rate = clk_sam9x5_peripheral_determine_rate, 411 .set_rate = clk_sam9x5_peripheral_set_rate, 412}; 413 |
|
323struct clk_hw * __init 324at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, 325 const struct clk_pcr_layout *layout, 326 const char *name, const char *parent_name, | 414struct clk_hw * __init 415at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, 416 const struct clk_pcr_layout *layout, 417 const char *name, const char *parent_name, |
327 u32 id, const struct clk_range *range) | 418 u32 id, const struct clk_range *range, 419 int chg_pid) |
328{ 329 struct clk_sam9x5_peripheral *periph; 330 struct clk_init_data init; 331 struct clk_hw *hw; 332 int ret; 333 334 if (!name || !parent_name) 335 return ERR_PTR(-EINVAL); 336 337 periph = kzalloc(sizeof(*periph), GFP_KERNEL); 338 if (!periph) 339 return ERR_PTR(-ENOMEM); 340 341 init.name = name; | 420{ 421 struct clk_sam9x5_peripheral *periph; 422 struct clk_init_data init; 423 struct clk_hw *hw; 424 int ret; 425 426 if (!name || !parent_name) 427 return ERR_PTR(-EINVAL); 428 429 periph = kzalloc(sizeof(*periph), GFP_KERNEL); 430 if (!periph) 431 return ERR_PTR(-ENOMEM); 432 433 init.name = name; |
342 init.ops = &sam9x5_peripheral_ops; 343 init.parent_names = (parent_name ? &parent_name : NULL); 344 init.num_parents = (parent_name ? 1 : 0); 345 init.flags = 0; | 434 init.parent_names = &parent_name; 435 init.num_parents = 1; 436 if (chg_pid < 0) { 437 init.flags = 0; 438 init.ops = &sam9x5_peripheral_ops; 439 } else { 440 init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | 441 CLK_SET_RATE_PARENT; 442 init.ops = &sam9x5_peripheral_chg_ops; 443 } |
346 347 periph->id = id; 348 periph->hw.init = &init; 349 periph->div = 0; 350 periph->regmap = regmap; 351 periph->lock = lock; 352 if (layout->div_mask) 353 periph->auto_div = true; 354 periph->layout = layout; 355 periph->range = *range; | 444 445 periph->id = id; 446 periph->hw.init = &init; 447 periph->div = 0; 448 periph->regmap = regmap; 449 periph->lock = lock; 450 if (layout->div_mask) 451 periph->auto_div = true; 452 periph->layout = layout; 453 periph->range = *range; |
454 periph->chg_pid = chg_pid; |
|
356 357 hw = &periph->hw; 358 ret = clk_hw_register(NULL, &periph->hw); 359 if (ret) { 360 kfree(periph); 361 hw = ERR_PTR(ret); 362 } else { 363 clk_sam9x5_peripheral_autodiv(periph); 364 pmc_register_id(id); 365 } 366 367 return hw; 368} | 455 456 hw = &periph->hw; 457 ret = clk_hw_register(NULL, &periph->hw); 458 if (ret) { 459 kfree(periph); 460 hw = ERR_PTR(ret); 461 } else { 462 clk_sam9x5_peripheral_autodiv(periph); 463 pmc_register_id(id); 464 } 465 466 return hw; 467} |