18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/bitops.h> 78c2ecf20Sopenharmony_ci#include <linux/clk-provider.h> 88c2ecf20Sopenharmony_ci#include <linux/clkdev.h> 98c2ecf20Sopenharmony_ci#include <linux/clk/at91_pmc.h> 108c2ecf20Sopenharmony_ci#include <linux/of.h> 118c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h> 128c2ecf20Sopenharmony_ci#include <linux/regmap.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "pmc.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ciDEFINE_SPINLOCK(pmc_pcr_lock); 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define PERIPHERAL_ID_MIN 2 198c2ecf20Sopenharmony_ci#define PERIPHERAL_ID_MAX 31 208c2ecf20Sopenharmony_ci#define PERIPHERAL_MASK(id) (1 << ((id) & PERIPHERAL_ID_MAX)) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define PERIPHERAL_MAX_SHIFT 3 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct clk_peripheral { 258c2ecf20Sopenharmony_ci struct clk_hw hw; 268c2ecf20Sopenharmony_ci struct regmap *regmap; 278c2ecf20Sopenharmony_ci u32 id; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define to_clk_peripheral(hw) container_of(hw, struct clk_peripheral, hw) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct clk_sam9x5_peripheral { 338c2ecf20Sopenharmony_ci struct clk_hw hw; 348c2ecf20Sopenharmony_ci struct regmap *regmap; 358c2ecf20Sopenharmony_ci struct clk_range range; 368c2ecf20Sopenharmony_ci spinlock_t *lock; 378c2ecf20Sopenharmony_ci u32 id; 388c2ecf20Sopenharmony_ci u32 div; 398c2ecf20Sopenharmony_ci const struct clk_pcr_layout *layout; 408c2ecf20Sopenharmony_ci bool auto_div; 418c2ecf20Sopenharmony_ci int chg_pid; 428c2ecf20Sopenharmony_ci}; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define to_clk_sam9x5_peripheral(hw) \ 458c2ecf20Sopenharmony_ci container_of(hw, struct clk_sam9x5_peripheral, hw) 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic int clk_peripheral_enable(struct clk_hw *hw) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct clk_peripheral *periph = to_clk_peripheral(hw); 508c2ecf20Sopenharmony_ci int offset = AT91_PMC_PCER; 518c2ecf20Sopenharmony_ci u32 id = periph->id; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci if (id < PERIPHERAL_ID_MIN) 548c2ecf20Sopenharmony_ci return 0; 558c2ecf20Sopenharmony_ci if (id > PERIPHERAL_ID_MAX) 568c2ecf20Sopenharmony_ci offset = AT91_PMC_PCER1; 578c2ecf20Sopenharmony_ci regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id)); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void clk_peripheral_disable(struct clk_hw *hw) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct clk_peripheral *periph = to_clk_peripheral(hw); 658c2ecf20Sopenharmony_ci int offset = AT91_PMC_PCDR; 668c2ecf20Sopenharmony_ci u32 id = periph->id; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci if (id < PERIPHERAL_ID_MIN) 698c2ecf20Sopenharmony_ci return; 708c2ecf20Sopenharmony_ci if (id > PERIPHERAL_ID_MAX) 718c2ecf20Sopenharmony_ci offset = AT91_PMC_PCDR1; 728c2ecf20Sopenharmony_ci regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id)); 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic int clk_peripheral_is_enabled(struct clk_hw *hw) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci struct clk_peripheral *periph = to_clk_peripheral(hw); 788c2ecf20Sopenharmony_ci int offset = AT91_PMC_PCSR; 798c2ecf20Sopenharmony_ci unsigned int status; 808c2ecf20Sopenharmony_ci u32 id = periph->id; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (id < PERIPHERAL_ID_MIN) 838c2ecf20Sopenharmony_ci return 1; 848c2ecf20Sopenharmony_ci if (id > PERIPHERAL_ID_MAX) 858c2ecf20Sopenharmony_ci offset = AT91_PMC_PCSR1; 868c2ecf20Sopenharmony_ci regmap_read(periph->regmap, offset, &status); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return status & PERIPHERAL_MASK(id) ? 1 : 0; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic const struct clk_ops peripheral_ops = { 928c2ecf20Sopenharmony_ci .enable = clk_peripheral_enable, 938c2ecf20Sopenharmony_ci .disable = clk_peripheral_disable, 948c2ecf20Sopenharmony_ci .is_enabled = clk_peripheral_is_enabled, 958c2ecf20Sopenharmony_ci}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistruct clk_hw * __init 988c2ecf20Sopenharmony_ciat91_clk_register_peripheral(struct regmap *regmap, const char *name, 998c2ecf20Sopenharmony_ci const char *parent_name, u32 id) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct clk_peripheral *periph; 1028c2ecf20Sopenharmony_ci struct clk_init_data init; 1038c2ecf20Sopenharmony_ci struct clk_hw *hw; 1048c2ecf20Sopenharmony_ci int ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (!name || !parent_name || id > PERIPHERAL_ID_MAX) 1078c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci periph = kzalloc(sizeof(*periph), GFP_KERNEL); 1108c2ecf20Sopenharmony_ci if (!periph) 1118c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci init.name = name; 1148c2ecf20Sopenharmony_ci init.ops = &peripheral_ops; 1158c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 1168c2ecf20Sopenharmony_ci init.num_parents = 1; 1178c2ecf20Sopenharmony_ci init.flags = 0; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci periph->id = id; 1208c2ecf20Sopenharmony_ci periph->hw.init = &init; 1218c2ecf20Sopenharmony_ci periph->regmap = regmap; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci hw = &periph->hw; 1248c2ecf20Sopenharmony_ci ret = clk_hw_register(NULL, &periph->hw); 1258c2ecf20Sopenharmony_ci if (ret) { 1268c2ecf20Sopenharmony_ci kfree(periph); 1278c2ecf20Sopenharmony_ci hw = ERR_PTR(ret); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci return hw; 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct clk_hw *parent; 1368c2ecf20Sopenharmony_ci unsigned long parent_rate; 1378c2ecf20Sopenharmony_ci int shift = 0; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (!periph->auto_div) 1408c2ecf20Sopenharmony_ci return; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (periph->range.max) { 1438c2ecf20Sopenharmony_ci parent = clk_hw_get_parent_by_index(&periph->hw, 0); 1448c2ecf20Sopenharmony_ci parent_rate = clk_hw_get_rate(parent); 1458c2ecf20Sopenharmony_ci if (!parent_rate) 1468c2ecf20Sopenharmony_ci return; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (; shift < PERIPHERAL_MAX_SHIFT; shift++) { 1498c2ecf20Sopenharmony_ci if (parent_rate >> shift <= periph->range.max) 1508c2ecf20Sopenharmony_ci break; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci periph->auto_div = false; 1558c2ecf20Sopenharmony_ci periph->div = shift; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int clk_sam9x5_peripheral_enable(struct clk_hw *hw) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); 1618c2ecf20Sopenharmony_ci unsigned long flags; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (periph->id < PERIPHERAL_ID_MIN) 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci spin_lock_irqsave(periph->lock, flags); 1678c2ecf20Sopenharmony_ci regmap_write(periph->regmap, periph->layout->offset, 1688c2ecf20Sopenharmony_ci (periph->id & periph->layout->pid_mask)); 1698c2ecf20Sopenharmony_ci regmap_update_bits(periph->regmap, periph->layout->offset, 1708c2ecf20Sopenharmony_ci periph->layout->div_mask | periph->layout->cmd | 1718c2ecf20Sopenharmony_ci AT91_PMC_PCR_EN, 1728c2ecf20Sopenharmony_ci field_prep(periph->layout->div_mask, periph->div) | 1738c2ecf20Sopenharmony_ci periph->layout->cmd | 1748c2ecf20Sopenharmony_ci AT91_PMC_PCR_EN); 1758c2ecf20Sopenharmony_ci spin_unlock_irqrestore(periph->lock, flags); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic void clk_sam9x5_peripheral_disable(struct clk_hw *hw) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); 1838c2ecf20Sopenharmony_ci unsigned long flags; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci if (periph->id < PERIPHERAL_ID_MIN) 1868c2ecf20Sopenharmony_ci return; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci spin_lock_irqsave(periph->lock, flags); 1898c2ecf20Sopenharmony_ci regmap_write(periph->regmap, periph->layout->offset, 1908c2ecf20Sopenharmony_ci (periph->id & periph->layout->pid_mask)); 1918c2ecf20Sopenharmony_ci regmap_update_bits(periph->regmap, periph->layout->offset, 1928c2ecf20Sopenharmony_ci AT91_PMC_PCR_EN | periph->layout->cmd, 1938c2ecf20Sopenharmony_ci periph->layout->cmd); 1948c2ecf20Sopenharmony_ci spin_unlock_irqrestore(periph->lock, flags); 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int clk_sam9x5_peripheral_is_enabled(struct clk_hw *hw) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); 2008c2ecf20Sopenharmony_ci unsigned long flags; 2018c2ecf20Sopenharmony_ci unsigned int status; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (periph->id < PERIPHERAL_ID_MIN) 2048c2ecf20Sopenharmony_ci return 1; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci spin_lock_irqsave(periph->lock, flags); 2078c2ecf20Sopenharmony_ci regmap_write(periph->regmap, periph->layout->offset, 2088c2ecf20Sopenharmony_ci (periph->id & periph->layout->pid_mask)); 2098c2ecf20Sopenharmony_ci regmap_read(periph->regmap, periph->layout->offset, &status); 2108c2ecf20Sopenharmony_ci spin_unlock_irqrestore(periph->lock, flags); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci return !!(status & AT91_PMC_PCR_EN); 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic unsigned long 2168c2ecf20Sopenharmony_ciclk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw, 2178c2ecf20Sopenharmony_ci unsigned long parent_rate) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); 2208c2ecf20Sopenharmony_ci unsigned long flags; 2218c2ecf20Sopenharmony_ci unsigned int status; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (periph->id < PERIPHERAL_ID_MIN) 2248c2ecf20Sopenharmony_ci return parent_rate; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci spin_lock_irqsave(periph->lock, flags); 2278c2ecf20Sopenharmony_ci regmap_write(periph->regmap, periph->layout->offset, 2288c2ecf20Sopenharmony_ci (periph->id & periph->layout->pid_mask)); 2298c2ecf20Sopenharmony_ci regmap_read(periph->regmap, periph->layout->offset, &status); 2308c2ecf20Sopenharmony_ci spin_unlock_irqrestore(periph->lock, flags); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (status & AT91_PMC_PCR_EN) { 2338c2ecf20Sopenharmony_ci periph->div = field_get(periph->layout->div_mask, status); 2348c2ecf20Sopenharmony_ci periph->auto_div = false; 2358c2ecf20Sopenharmony_ci } else { 2368c2ecf20Sopenharmony_ci clk_sam9x5_peripheral_autodiv(periph); 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return parent_rate >> periph->div; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic void clk_sam9x5_peripheral_best_diff(struct clk_rate_request *req, 2438c2ecf20Sopenharmony_ci struct clk_hw *parent, 2448c2ecf20Sopenharmony_ci unsigned long parent_rate, 2458c2ecf20Sopenharmony_ci u32 shift, long *best_diff, 2468c2ecf20Sopenharmony_ci long *best_rate) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci unsigned long tmp_rate = parent_rate >> shift; 2498c2ecf20Sopenharmony_ci unsigned long tmp_diff = abs(req->rate - tmp_rate); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (*best_diff < 0 || *best_diff >= tmp_diff) { 2528c2ecf20Sopenharmony_ci *best_rate = tmp_rate; 2538c2ecf20Sopenharmony_ci *best_diff = tmp_diff; 2548c2ecf20Sopenharmony_ci req->best_parent_rate = parent_rate; 2558c2ecf20Sopenharmony_ci req->best_parent_hw = parent; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic int clk_sam9x5_peripheral_determine_rate(struct clk_hw *hw, 2608c2ecf20Sopenharmony_ci struct clk_rate_request *req) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); 2638c2ecf20Sopenharmony_ci struct clk_hw *parent = clk_hw_get_parent(hw); 2648c2ecf20Sopenharmony_ci struct clk_rate_request req_parent = *req; 2658c2ecf20Sopenharmony_ci unsigned long parent_rate = clk_hw_get_rate(parent); 2668c2ecf20Sopenharmony_ci unsigned long tmp_rate; 2678c2ecf20Sopenharmony_ci long best_rate = LONG_MIN; 2688c2ecf20Sopenharmony_ci long best_diff = LONG_MIN; 2698c2ecf20Sopenharmony_ci u32 shift; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) 2728c2ecf20Sopenharmony_ci return parent_rate; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* Fist step: check the available dividers. */ 2758c2ecf20Sopenharmony_ci for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) { 2768c2ecf20Sopenharmony_ci tmp_rate = parent_rate >> shift; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci if (periph->range.max && tmp_rate > periph->range.max) 2798c2ecf20Sopenharmony_ci continue; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci clk_sam9x5_peripheral_best_diff(req, parent, parent_rate, 2828c2ecf20Sopenharmony_ci shift, &best_diff, &best_rate); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (!best_diff || best_rate <= req->rate) 2858c2ecf20Sopenharmony_ci break; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (periph->chg_pid < 0) 2898c2ecf20Sopenharmony_ci goto end; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci /* Step two: try to request rate from parent. */ 2928c2ecf20Sopenharmony_ci parent = clk_hw_get_parent_by_index(hw, periph->chg_pid); 2938c2ecf20Sopenharmony_ci if (!parent) 2948c2ecf20Sopenharmony_ci goto end; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) { 2978c2ecf20Sopenharmony_ci req_parent.rate = req->rate << shift; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci if (__clk_determine_rate(parent, &req_parent)) 3008c2ecf20Sopenharmony_ci continue; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci clk_sam9x5_peripheral_best_diff(req, parent, req_parent.rate, 3038c2ecf20Sopenharmony_ci shift, &best_diff, &best_rate); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (!best_diff) 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ciend: 3098c2ecf20Sopenharmony_ci if (best_rate < 0 || 3108c2ecf20Sopenharmony_ci (periph->range.max && best_rate > periph->range.max)) 3118c2ecf20Sopenharmony_ci return -EINVAL; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci pr_debug("PCK: %s, best_rate = %ld, parent clk: %s @ %ld\n", 3148c2ecf20Sopenharmony_ci __func__, best_rate, 3158c2ecf20Sopenharmony_ci __clk_get_name((req->best_parent_hw)->clk), 3168c2ecf20Sopenharmony_ci req->best_parent_rate); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci req->rate = best_rate; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw, 3248c2ecf20Sopenharmony_ci unsigned long rate, 3258c2ecf20Sopenharmony_ci unsigned long *parent_rate) 3268c2ecf20Sopenharmony_ci{ 3278c2ecf20Sopenharmony_ci int shift = 0; 3288c2ecf20Sopenharmony_ci unsigned long best_rate; 3298c2ecf20Sopenharmony_ci unsigned long best_diff; 3308c2ecf20Sopenharmony_ci unsigned long cur_rate = *parent_rate; 3318c2ecf20Sopenharmony_ci unsigned long cur_diff; 3328c2ecf20Sopenharmony_ci struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) 3358c2ecf20Sopenharmony_ci return *parent_rate; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci if (periph->range.max) { 3388c2ecf20Sopenharmony_ci for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) { 3398c2ecf20Sopenharmony_ci cur_rate = *parent_rate >> shift; 3408c2ecf20Sopenharmony_ci if (cur_rate <= periph->range.max) 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (rate >= cur_rate) 3468c2ecf20Sopenharmony_ci return cur_rate; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci best_diff = cur_rate - rate; 3498c2ecf20Sopenharmony_ci best_rate = cur_rate; 3508c2ecf20Sopenharmony_ci for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) { 3518c2ecf20Sopenharmony_ci cur_rate = *parent_rate >> shift; 3528c2ecf20Sopenharmony_ci if (cur_rate < rate) 3538c2ecf20Sopenharmony_ci cur_diff = rate - cur_rate; 3548c2ecf20Sopenharmony_ci else 3558c2ecf20Sopenharmony_ci cur_diff = cur_rate - rate; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (cur_diff < best_diff) { 3588c2ecf20Sopenharmony_ci best_diff = cur_diff; 3598c2ecf20Sopenharmony_ci best_rate = cur_rate; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (!best_diff || cur_rate < rate) 3638c2ecf20Sopenharmony_ci break; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return best_rate; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic int clk_sam9x5_peripheral_set_rate(struct clk_hw *hw, 3708c2ecf20Sopenharmony_ci unsigned long rate, 3718c2ecf20Sopenharmony_ci unsigned long parent_rate) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci int shift; 3748c2ecf20Sopenharmony_ci struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw); 3758c2ecf20Sopenharmony_ci if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) { 3768c2ecf20Sopenharmony_ci if (parent_rate == rate) 3778c2ecf20Sopenharmony_ci return 0; 3788c2ecf20Sopenharmony_ci else 3798c2ecf20Sopenharmony_ci return -EINVAL; 3808c2ecf20Sopenharmony_ci } 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci if (periph->range.max && rate > periph->range.max) 3838c2ecf20Sopenharmony_ci return -EINVAL; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) { 3868c2ecf20Sopenharmony_ci if (parent_rate >> shift == rate) { 3878c2ecf20Sopenharmony_ci periph->auto_div = false; 3888c2ecf20Sopenharmony_ci periph->div = shift; 3898c2ecf20Sopenharmony_ci return 0; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci return -EINVAL; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic const struct clk_ops sam9x5_peripheral_ops = { 3978c2ecf20Sopenharmony_ci .enable = clk_sam9x5_peripheral_enable, 3988c2ecf20Sopenharmony_ci .disable = clk_sam9x5_peripheral_disable, 3998c2ecf20Sopenharmony_ci .is_enabled = clk_sam9x5_peripheral_is_enabled, 4008c2ecf20Sopenharmony_ci .recalc_rate = clk_sam9x5_peripheral_recalc_rate, 4018c2ecf20Sopenharmony_ci .round_rate = clk_sam9x5_peripheral_round_rate, 4028c2ecf20Sopenharmony_ci .set_rate = clk_sam9x5_peripheral_set_rate, 4038c2ecf20Sopenharmony_ci}; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic const struct clk_ops sam9x5_peripheral_chg_ops = { 4068c2ecf20Sopenharmony_ci .enable = clk_sam9x5_peripheral_enable, 4078c2ecf20Sopenharmony_ci .disable = clk_sam9x5_peripheral_disable, 4088c2ecf20Sopenharmony_ci .is_enabled = clk_sam9x5_peripheral_is_enabled, 4098c2ecf20Sopenharmony_ci .recalc_rate = clk_sam9x5_peripheral_recalc_rate, 4108c2ecf20Sopenharmony_ci .determine_rate = clk_sam9x5_peripheral_determine_rate, 4118c2ecf20Sopenharmony_ci .set_rate = clk_sam9x5_peripheral_set_rate, 4128c2ecf20Sopenharmony_ci}; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistruct clk_hw * __init 4158c2ecf20Sopenharmony_ciat91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock, 4168c2ecf20Sopenharmony_ci const struct clk_pcr_layout *layout, 4178c2ecf20Sopenharmony_ci const char *name, const char *parent_name, 4188c2ecf20Sopenharmony_ci u32 id, const struct clk_range *range, 4198c2ecf20Sopenharmony_ci int chg_pid) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci struct clk_sam9x5_peripheral *periph; 4228c2ecf20Sopenharmony_ci struct clk_init_data init; 4238c2ecf20Sopenharmony_ci struct clk_hw *hw; 4248c2ecf20Sopenharmony_ci int ret; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (!name || !parent_name) 4278c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci periph = kzalloc(sizeof(*periph), GFP_KERNEL); 4308c2ecf20Sopenharmony_ci if (!periph) 4318c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci init.name = name; 4348c2ecf20Sopenharmony_ci init.parent_names = &parent_name; 4358c2ecf20Sopenharmony_ci init.num_parents = 1; 4368c2ecf20Sopenharmony_ci if (chg_pid < 0) { 4378c2ecf20Sopenharmony_ci init.flags = 0; 4388c2ecf20Sopenharmony_ci init.ops = &sam9x5_peripheral_ops; 4398c2ecf20Sopenharmony_ci } else { 4408c2ecf20Sopenharmony_ci init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | 4418c2ecf20Sopenharmony_ci CLK_SET_RATE_PARENT; 4428c2ecf20Sopenharmony_ci init.ops = &sam9x5_peripheral_chg_ops; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci periph->id = id; 4468c2ecf20Sopenharmony_ci periph->hw.init = &init; 4478c2ecf20Sopenharmony_ci periph->div = 0; 4488c2ecf20Sopenharmony_ci periph->regmap = regmap; 4498c2ecf20Sopenharmony_ci periph->lock = lock; 4508c2ecf20Sopenharmony_ci if (layout->div_mask) 4518c2ecf20Sopenharmony_ci periph->auto_div = true; 4528c2ecf20Sopenharmony_ci periph->layout = layout; 4538c2ecf20Sopenharmony_ci periph->range = *range; 4548c2ecf20Sopenharmony_ci periph->chg_pid = chg_pid; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci hw = &periph->hw; 4578c2ecf20Sopenharmony_ci ret = clk_hw_register(NULL, &periph->hw); 4588c2ecf20Sopenharmony_ci if (ret) { 4598c2ecf20Sopenharmony_ci kfree(periph); 4608c2ecf20Sopenharmony_ci hw = ERR_PTR(ret); 4618c2ecf20Sopenharmony_ci } else { 4628c2ecf20Sopenharmony_ci clk_sam9x5_peripheral_autodiv(periph); 4638c2ecf20Sopenharmony_ci pmc_register_id(id); 4648c2ecf20Sopenharmony_ci } 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return hw; 4678c2ecf20Sopenharmony_ci} 468