162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/clk-provider.h> 762306a36Sopenharmony_ci#include <linux/clkdev.h> 862306a36Sopenharmony_ci#include <linux/clk.h> 962306a36Sopenharmony_ci#include <linux/clk/at91_pmc.h> 1062306a36Sopenharmony_ci#include <linux/of.h> 1162306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1262306a36Sopenharmony_ci#include <linux/regmap.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "pmc.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define MASTER_PRES_MASK 0x7 1762306a36Sopenharmony_ci#define MASTER_PRES_MAX MASTER_PRES_MASK 1862306a36Sopenharmony_ci#define MASTER_DIV_SHIFT 8 1962306a36Sopenharmony_ci#define MASTER_DIV_MASK 0x7 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define PMC_MCR_CSS_SHIFT (16) 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define MASTER_MAX_ID 4 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define to_clk_master(hw) container_of(hw, struct clk_master, hw) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistruct clk_master { 2862306a36Sopenharmony_ci struct clk_hw hw; 2962306a36Sopenharmony_ci struct regmap *regmap; 3062306a36Sopenharmony_ci spinlock_t *lock; 3162306a36Sopenharmony_ci const struct clk_master_layout *layout; 3262306a36Sopenharmony_ci const struct clk_master_characteristics *characteristics; 3362306a36Sopenharmony_ci struct at91_clk_pms pms; 3462306a36Sopenharmony_ci u32 *mux_table; 3562306a36Sopenharmony_ci u32 mckr; 3662306a36Sopenharmony_ci int chg_pid; 3762306a36Sopenharmony_ci u8 id; 3862306a36Sopenharmony_ci u8 parent; 3962306a36Sopenharmony_ci u8 div; 4062306a36Sopenharmony_ci u32 safe_div; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci/* MCK div reference to be used by notifier. */ 4462306a36Sopenharmony_cistatic struct clk_master *master_div; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic inline bool clk_master_ready(struct clk_master *master) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY; 4962306a36Sopenharmony_ci unsigned int status; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci regmap_read(master->regmap, AT91_PMC_SR, &status); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return !!(status & bit); 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int clk_master_prepare(struct clk_hw *hw) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 5962306a36Sopenharmony_ci unsigned long flags; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci while (!clk_master_ready(master)) 6462306a36Sopenharmony_ci cpu_relax(); 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci return 0; 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic int clk_master_is_prepared(struct clk_hw *hw) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 7462306a36Sopenharmony_ci unsigned long flags; 7562306a36Sopenharmony_ci bool status; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 7862306a36Sopenharmony_ci status = clk_master_ready(master); 7962306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return status; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic unsigned long clk_master_div_recalc_rate(struct clk_hw *hw, 8562306a36Sopenharmony_ci unsigned long parent_rate) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci u8 div; 8862306a36Sopenharmony_ci unsigned long flags, rate = parent_rate; 8962306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 9062306a36Sopenharmony_ci const struct clk_master_layout *layout = master->layout; 9162306a36Sopenharmony_ci const struct clk_master_characteristics *characteristics = 9262306a36Sopenharmony_ci master->characteristics; 9362306a36Sopenharmony_ci unsigned int mckr; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 9662306a36Sopenharmony_ci regmap_read(master->regmap, master->layout->offset, &mckr); 9762306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci mckr &= layout->mask; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci rate /= characteristics->divisors[div]; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (rate < characteristics->output.min) 10662306a36Sopenharmony_ci pr_warn("master clk div is underclocked"); 10762306a36Sopenharmony_ci else if (rate > characteristics->output.max) 10862306a36Sopenharmony_ci pr_warn("master clk div is overclocked"); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return rate; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic int clk_master_div_save_context(struct clk_hw *hw) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 11662306a36Sopenharmony_ci struct clk_hw *parent_hw = clk_hw_get_parent(hw); 11762306a36Sopenharmony_ci unsigned long flags; 11862306a36Sopenharmony_ci unsigned int mckr, div; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 12162306a36Sopenharmony_ci regmap_read(master->regmap, master->layout->offset, &mckr); 12262306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci mckr &= master->layout->mask; 12562306a36Sopenharmony_ci div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; 12662306a36Sopenharmony_ci div = master->characteristics->divisors[div]; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci master->pms.parent_rate = clk_hw_get_rate(parent_hw); 12962306a36Sopenharmony_ci master->pms.rate = DIV_ROUND_CLOSEST(master->pms.parent_rate, div); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return 0; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic void clk_master_div_restore_context(struct clk_hw *hw) 13562306a36Sopenharmony_ci{ 13662306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 13762306a36Sopenharmony_ci unsigned long flags; 13862306a36Sopenharmony_ci unsigned int mckr; 13962306a36Sopenharmony_ci u8 div; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 14262306a36Sopenharmony_ci regmap_read(master->regmap, master->layout->offset, &mckr); 14362306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci mckr &= master->layout->mask; 14662306a36Sopenharmony_ci div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; 14762306a36Sopenharmony_ci div = master->characteristics->divisors[div]; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (div != DIV_ROUND_CLOSEST(master->pms.parent_rate, master->pms.rate)) 15062306a36Sopenharmony_ci pr_warn("MCKR DIV not configured properly by firmware!\n"); 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_cistatic const struct clk_ops master_div_ops = { 15462306a36Sopenharmony_ci .prepare = clk_master_prepare, 15562306a36Sopenharmony_ci .is_prepared = clk_master_is_prepared, 15662306a36Sopenharmony_ci .recalc_rate = clk_master_div_recalc_rate, 15762306a36Sopenharmony_ci .save_context = clk_master_div_save_context, 15862306a36Sopenharmony_ci .restore_context = clk_master_div_restore_context, 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/* This function must be called with lock acquired. */ 16262306a36Sopenharmony_cistatic int clk_master_div_set(struct clk_master *master, 16362306a36Sopenharmony_ci unsigned long parent_rate, int div) 16462306a36Sopenharmony_ci{ 16562306a36Sopenharmony_ci const struct clk_master_characteristics *characteristics = 16662306a36Sopenharmony_ci master->characteristics; 16762306a36Sopenharmony_ci unsigned long rate = parent_rate; 16862306a36Sopenharmony_ci unsigned int max_div = 0, div_index = 0, max_div_index = 0; 16962306a36Sopenharmony_ci unsigned int i, mckr, tmp; 17062306a36Sopenharmony_ci int ret; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(characteristics->divisors); i++) { 17362306a36Sopenharmony_ci if (!characteristics->divisors[i]) 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (div == characteristics->divisors[i]) 17762306a36Sopenharmony_ci div_index = i; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (max_div < characteristics->divisors[i]) { 18062306a36Sopenharmony_ci max_div = characteristics->divisors[i]; 18162306a36Sopenharmony_ci max_div_index = i; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (div > max_div) 18662306a36Sopenharmony_ci div_index = max_div_index; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci ret = regmap_read(master->regmap, master->layout->offset, &mckr); 18962306a36Sopenharmony_ci if (ret) 19062306a36Sopenharmony_ci return ret; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci mckr &= master->layout->mask; 19362306a36Sopenharmony_ci tmp = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; 19462306a36Sopenharmony_ci if (tmp == div_index) 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci rate /= characteristics->divisors[div_index]; 19862306a36Sopenharmony_ci if (rate < characteristics->output.min) 19962306a36Sopenharmony_ci pr_warn("master clk div is underclocked"); 20062306a36Sopenharmony_ci else if (rate > characteristics->output.max) 20162306a36Sopenharmony_ci pr_warn("master clk div is overclocked"); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci mckr &= ~(MASTER_DIV_MASK << MASTER_DIV_SHIFT); 20462306a36Sopenharmony_ci mckr |= (div_index << MASTER_DIV_SHIFT); 20562306a36Sopenharmony_ci ret = regmap_write(master->regmap, master->layout->offset, mckr); 20662306a36Sopenharmony_ci if (ret) 20762306a36Sopenharmony_ci return ret; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci while (!clk_master_ready(master)) 21062306a36Sopenharmony_ci cpu_relax(); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci master->div = characteristics->divisors[div_index]; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return 0; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic unsigned long clk_master_div_recalc_rate_chg(struct clk_hw *hw, 21862306a36Sopenharmony_ci unsigned long parent_rate) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return DIV_ROUND_CLOSEST_ULL(parent_rate, master->div); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void clk_master_div_restore_context_chg(struct clk_hw *hw) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 22862306a36Sopenharmony_ci unsigned long flags; 22962306a36Sopenharmony_ci int ret; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 23262306a36Sopenharmony_ci ret = clk_master_div_set(master, master->pms.parent_rate, 23362306a36Sopenharmony_ci DIV_ROUND_CLOSEST(master->pms.parent_rate, 23462306a36Sopenharmony_ci master->pms.rate)); 23562306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 23662306a36Sopenharmony_ci if (ret) 23762306a36Sopenharmony_ci pr_warn("Failed to restore MCK DIV clock\n"); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic const struct clk_ops master_div_ops_chg = { 24162306a36Sopenharmony_ci .prepare = clk_master_prepare, 24262306a36Sopenharmony_ci .is_prepared = clk_master_is_prepared, 24362306a36Sopenharmony_ci .recalc_rate = clk_master_div_recalc_rate_chg, 24462306a36Sopenharmony_ci .save_context = clk_master_div_save_context, 24562306a36Sopenharmony_ci .restore_context = clk_master_div_restore_context_chg, 24662306a36Sopenharmony_ci}; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic int clk_master_div_notifier_fn(struct notifier_block *notifier, 24962306a36Sopenharmony_ci unsigned long code, void *data) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci const struct clk_master_characteristics *characteristics = 25262306a36Sopenharmony_ci master_div->characteristics; 25362306a36Sopenharmony_ci struct clk_notifier_data *cnd = data; 25462306a36Sopenharmony_ci unsigned long flags, new_parent_rate, new_rate; 25562306a36Sopenharmony_ci unsigned int mckr, div, new_div = 0; 25662306a36Sopenharmony_ci int ret, i; 25762306a36Sopenharmony_ci long tmp_diff; 25862306a36Sopenharmony_ci long best_diff = -1; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci spin_lock_irqsave(master_div->lock, flags); 26162306a36Sopenharmony_ci switch (code) { 26262306a36Sopenharmony_ci case PRE_RATE_CHANGE: 26362306a36Sopenharmony_ci /* 26462306a36Sopenharmony_ci * We want to avoid any overclocking of MCK DIV domain. To do 26562306a36Sopenharmony_ci * this we set a safe divider (the underclocking is not of 26662306a36Sopenharmony_ci * interest as we can go as low as 32KHz). The relation 26762306a36Sopenharmony_ci * b/w this clock and its parents are as follows: 26862306a36Sopenharmony_ci * 26962306a36Sopenharmony_ci * FRAC PLL -> DIV PLL -> MCK DIV 27062306a36Sopenharmony_ci * 27162306a36Sopenharmony_ci * With the proper safe divider we should be good even with FRAC 27262306a36Sopenharmony_ci * PLL at its maximum value. 27362306a36Sopenharmony_ci */ 27462306a36Sopenharmony_ci ret = regmap_read(master_div->regmap, master_div->layout->offset, 27562306a36Sopenharmony_ci &mckr); 27662306a36Sopenharmony_ci if (ret) { 27762306a36Sopenharmony_ci ret = NOTIFY_STOP_MASK; 27862306a36Sopenharmony_ci goto unlock; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci mckr &= master_div->layout->mask; 28262306a36Sopenharmony_ci div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* Switch to safe divider. */ 28562306a36Sopenharmony_ci clk_master_div_set(master_div, 28662306a36Sopenharmony_ci cnd->old_rate * characteristics->divisors[div], 28762306a36Sopenharmony_ci master_div->safe_div); 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci case POST_RATE_CHANGE: 29162306a36Sopenharmony_ci /* 29262306a36Sopenharmony_ci * At this point we want to restore MCK DIV domain to its maximum 29362306a36Sopenharmony_ci * allowed rate. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci ret = regmap_read(master_div->regmap, master_div->layout->offset, 29662306a36Sopenharmony_ci &mckr); 29762306a36Sopenharmony_ci if (ret) { 29862306a36Sopenharmony_ci ret = NOTIFY_STOP_MASK; 29962306a36Sopenharmony_ci goto unlock; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci mckr &= master_div->layout->mask; 30362306a36Sopenharmony_ci div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; 30462306a36Sopenharmony_ci new_parent_rate = cnd->new_rate * characteristics->divisors[div]; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(characteristics->divisors); i++) { 30762306a36Sopenharmony_ci if (!characteristics->divisors[i]) 30862306a36Sopenharmony_ci break; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci new_rate = DIV_ROUND_CLOSEST_ULL(new_parent_rate, 31162306a36Sopenharmony_ci characteristics->divisors[i]); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci tmp_diff = characteristics->output.max - new_rate; 31462306a36Sopenharmony_ci if (tmp_diff < 0) 31562306a36Sopenharmony_ci continue; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (best_diff < 0 || best_diff > tmp_diff) { 31862306a36Sopenharmony_ci new_div = characteristics->divisors[i]; 31962306a36Sopenharmony_ci best_diff = tmp_diff; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci if (!tmp_diff) 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (!new_div) { 32762306a36Sopenharmony_ci ret = NOTIFY_STOP_MASK; 32862306a36Sopenharmony_ci goto unlock; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Update the div to preserve MCK DIV clock rate. */ 33262306a36Sopenharmony_ci clk_master_div_set(master_div, new_parent_rate, 33362306a36Sopenharmony_ci new_div); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci ret = NOTIFY_OK; 33662306a36Sopenharmony_ci break; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci default: 33962306a36Sopenharmony_ci ret = NOTIFY_DONE; 34062306a36Sopenharmony_ci break; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ciunlock: 34462306a36Sopenharmony_ci spin_unlock_irqrestore(master_div->lock, flags); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci return ret; 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic struct notifier_block clk_master_div_notifier = { 35062306a36Sopenharmony_ci .notifier_call = clk_master_div_notifier_fn, 35162306a36Sopenharmony_ci}; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic void clk_sama7g5_master_best_diff(struct clk_rate_request *req, 35462306a36Sopenharmony_ci struct clk_hw *parent, 35562306a36Sopenharmony_ci unsigned long parent_rate, 35662306a36Sopenharmony_ci long *best_rate, 35762306a36Sopenharmony_ci long *best_diff, 35862306a36Sopenharmony_ci u32 div) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci unsigned long tmp_rate, tmp_diff; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (div == MASTER_PRES_MAX) 36362306a36Sopenharmony_ci tmp_rate = parent_rate / 3; 36462306a36Sopenharmony_ci else 36562306a36Sopenharmony_ci tmp_rate = parent_rate >> div; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci tmp_diff = abs(req->rate - tmp_rate); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (*best_diff < 0 || *best_diff >= tmp_diff) { 37062306a36Sopenharmony_ci *best_rate = tmp_rate; 37162306a36Sopenharmony_ci *best_diff = tmp_diff; 37262306a36Sopenharmony_ci req->best_parent_rate = parent_rate; 37362306a36Sopenharmony_ci req->best_parent_hw = parent; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic unsigned long clk_master_pres_recalc_rate(struct clk_hw *hw, 37862306a36Sopenharmony_ci unsigned long parent_rate) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 38162306a36Sopenharmony_ci const struct clk_master_characteristics *characteristics = 38262306a36Sopenharmony_ci master->characteristics; 38362306a36Sopenharmony_ci unsigned long flags; 38462306a36Sopenharmony_ci unsigned int val, pres; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 38762306a36Sopenharmony_ci regmap_read(master->regmap, master->layout->offset, &val); 38862306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci val &= master->layout->mask; 39162306a36Sopenharmony_ci pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK; 39262306a36Sopenharmony_ci if (pres == MASTER_PRES_MAX && characteristics->have_div3_pres) 39362306a36Sopenharmony_ci pres = 3; 39462306a36Sopenharmony_ci else 39562306a36Sopenharmony_ci pres = (1 << pres); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return DIV_ROUND_CLOSEST_ULL(parent_rate, pres); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic u8 clk_master_pres_get_parent(struct clk_hw *hw) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 40362306a36Sopenharmony_ci unsigned long flags; 40462306a36Sopenharmony_ci unsigned int mckr; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 40762306a36Sopenharmony_ci regmap_read(master->regmap, master->layout->offset, &mckr); 40862306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci mckr &= master->layout->mask; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci return mckr & AT91_PMC_CSS; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic int clk_master_pres_save_context(struct clk_hw *hw) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 41862306a36Sopenharmony_ci struct clk_hw *parent_hw = clk_hw_get_parent(hw); 41962306a36Sopenharmony_ci unsigned long flags; 42062306a36Sopenharmony_ci unsigned int val, pres; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 42362306a36Sopenharmony_ci regmap_read(master->regmap, master->layout->offset, &val); 42462306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci val &= master->layout->mask; 42762306a36Sopenharmony_ci pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK; 42862306a36Sopenharmony_ci if (pres == MASTER_PRES_MAX && master->characteristics->have_div3_pres) 42962306a36Sopenharmony_ci pres = 3; 43062306a36Sopenharmony_ci else 43162306a36Sopenharmony_ci pres = (1 << pres); 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci master->pms.parent = val & AT91_PMC_CSS; 43462306a36Sopenharmony_ci master->pms.parent_rate = clk_hw_get_rate(parent_hw); 43562306a36Sopenharmony_ci master->pms.rate = DIV_ROUND_CLOSEST_ULL(master->pms.parent_rate, pres); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic void clk_master_pres_restore_context(struct clk_hw *hw) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 44362306a36Sopenharmony_ci unsigned long flags; 44462306a36Sopenharmony_ci unsigned int val, pres; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 44762306a36Sopenharmony_ci regmap_read(master->regmap, master->layout->offset, &val); 44862306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci val &= master->layout->mask; 45162306a36Sopenharmony_ci pres = (val >> master->layout->pres_shift) & MASTER_PRES_MASK; 45262306a36Sopenharmony_ci if (pres == MASTER_PRES_MAX && master->characteristics->have_div3_pres) 45362306a36Sopenharmony_ci pres = 3; 45462306a36Sopenharmony_ci else 45562306a36Sopenharmony_ci pres = (1 << pres); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (master->pms.rate != 45862306a36Sopenharmony_ci DIV_ROUND_CLOSEST_ULL(master->pms.parent_rate, pres) || 45962306a36Sopenharmony_ci (master->pms.parent != (val & AT91_PMC_CSS))) 46062306a36Sopenharmony_ci pr_warn("MCKR PRES was not configured properly by firmware!\n"); 46162306a36Sopenharmony_ci} 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_cistatic const struct clk_ops master_pres_ops = { 46462306a36Sopenharmony_ci .prepare = clk_master_prepare, 46562306a36Sopenharmony_ci .is_prepared = clk_master_is_prepared, 46662306a36Sopenharmony_ci .recalc_rate = clk_master_pres_recalc_rate, 46762306a36Sopenharmony_ci .get_parent = clk_master_pres_get_parent, 46862306a36Sopenharmony_ci .save_context = clk_master_pres_save_context, 46962306a36Sopenharmony_ci .restore_context = clk_master_pres_restore_context, 47062306a36Sopenharmony_ci}; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic struct clk_hw * __init 47362306a36Sopenharmony_ciat91_clk_register_master_internal(struct regmap *regmap, 47462306a36Sopenharmony_ci const char *name, int num_parents, 47562306a36Sopenharmony_ci const char **parent_names, 47662306a36Sopenharmony_ci struct clk_hw **parent_hws, 47762306a36Sopenharmony_ci const struct clk_master_layout *layout, 47862306a36Sopenharmony_ci const struct clk_master_characteristics *characteristics, 47962306a36Sopenharmony_ci const struct clk_ops *ops, spinlock_t *lock, u32 flags) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct clk_master *master; 48262306a36Sopenharmony_ci struct clk_init_data init = {}; 48362306a36Sopenharmony_ci struct clk_hw *hw; 48462306a36Sopenharmony_ci unsigned int mckr; 48562306a36Sopenharmony_ci unsigned long irqflags; 48662306a36Sopenharmony_ci int ret; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (!name || !num_parents || !(parent_names || parent_hws) || !lock) 48962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci master = kzalloc(sizeof(*master), GFP_KERNEL); 49262306a36Sopenharmony_ci if (!master) 49362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci init.name = name; 49662306a36Sopenharmony_ci init.ops = ops; 49762306a36Sopenharmony_ci if (parent_hws) 49862306a36Sopenharmony_ci init.parent_hws = (const struct clk_hw **)parent_hws; 49962306a36Sopenharmony_ci else 50062306a36Sopenharmony_ci init.parent_names = parent_names; 50162306a36Sopenharmony_ci init.num_parents = num_parents; 50262306a36Sopenharmony_ci init.flags = flags; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci master->hw.init = &init; 50562306a36Sopenharmony_ci master->layout = layout; 50662306a36Sopenharmony_ci master->characteristics = characteristics; 50762306a36Sopenharmony_ci master->regmap = regmap; 50862306a36Sopenharmony_ci master->lock = lock; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (ops == &master_div_ops_chg) { 51162306a36Sopenharmony_ci spin_lock_irqsave(master->lock, irqflags); 51262306a36Sopenharmony_ci regmap_read(master->regmap, master->layout->offset, &mckr); 51362306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, irqflags); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci mckr &= layout->mask; 51662306a36Sopenharmony_ci mckr = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; 51762306a36Sopenharmony_ci master->div = characteristics->divisors[mckr]; 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci hw = &master->hw; 52162306a36Sopenharmony_ci ret = clk_hw_register(NULL, &master->hw); 52262306a36Sopenharmony_ci if (ret) { 52362306a36Sopenharmony_ci kfree(master); 52462306a36Sopenharmony_ci hw = ERR_PTR(ret); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci return hw; 52862306a36Sopenharmony_ci} 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_cistruct clk_hw * __init 53162306a36Sopenharmony_ciat91_clk_register_master_pres(struct regmap *regmap, 53262306a36Sopenharmony_ci const char *name, int num_parents, 53362306a36Sopenharmony_ci const char **parent_names, 53462306a36Sopenharmony_ci struct clk_hw **parent_hws, 53562306a36Sopenharmony_ci const struct clk_master_layout *layout, 53662306a36Sopenharmony_ci const struct clk_master_characteristics *characteristics, 53762306a36Sopenharmony_ci spinlock_t *lock) 53862306a36Sopenharmony_ci{ 53962306a36Sopenharmony_ci return at91_clk_register_master_internal(regmap, name, num_parents, 54062306a36Sopenharmony_ci parent_names, parent_hws, layout, 54162306a36Sopenharmony_ci characteristics, 54262306a36Sopenharmony_ci &master_pres_ops, 54362306a36Sopenharmony_ci lock, CLK_SET_RATE_GATE); 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistruct clk_hw * __init 54762306a36Sopenharmony_ciat91_clk_register_master_div(struct regmap *regmap, 54862306a36Sopenharmony_ci const char *name, const char *parent_name, 54962306a36Sopenharmony_ci struct clk_hw *parent_hw, const struct clk_master_layout *layout, 55062306a36Sopenharmony_ci const struct clk_master_characteristics *characteristics, 55162306a36Sopenharmony_ci spinlock_t *lock, u32 flags, u32 safe_div) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci const struct clk_ops *ops; 55462306a36Sopenharmony_ci struct clk_hw *hw; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (flags & CLK_SET_RATE_GATE) 55762306a36Sopenharmony_ci ops = &master_div_ops; 55862306a36Sopenharmony_ci else 55962306a36Sopenharmony_ci ops = &master_div_ops_chg; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci hw = at91_clk_register_master_internal(regmap, name, 1, 56262306a36Sopenharmony_ci parent_name ? &parent_name : NULL, 56362306a36Sopenharmony_ci parent_hw ? &parent_hw : NULL, layout, 56462306a36Sopenharmony_ci characteristics, ops, 56562306a36Sopenharmony_ci lock, flags); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci if (!IS_ERR(hw) && safe_div) { 56862306a36Sopenharmony_ci master_div = to_clk_master(hw); 56962306a36Sopenharmony_ci master_div->safe_div = safe_div; 57062306a36Sopenharmony_ci clk_notifier_register(hw->clk, 57162306a36Sopenharmony_ci &clk_master_div_notifier); 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return hw; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic unsigned long 57862306a36Sopenharmony_ciclk_sama7g5_master_recalc_rate(struct clk_hw *hw, 57962306a36Sopenharmony_ci unsigned long parent_rate) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div)); 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic int clk_sama7g5_master_determine_rate(struct clk_hw *hw, 58762306a36Sopenharmony_ci struct clk_rate_request *req) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 59062306a36Sopenharmony_ci struct clk_hw *parent; 59162306a36Sopenharmony_ci long best_rate = LONG_MIN, best_diff = LONG_MIN; 59262306a36Sopenharmony_ci unsigned long parent_rate; 59362306a36Sopenharmony_ci unsigned int div, i; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci /* First: check the dividers of MCR. */ 59662306a36Sopenharmony_ci for (i = 0; i < clk_hw_get_num_parents(hw); i++) { 59762306a36Sopenharmony_ci parent = clk_hw_get_parent_by_index(hw, i); 59862306a36Sopenharmony_ci if (!parent) 59962306a36Sopenharmony_ci continue; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci parent_rate = clk_hw_get_rate(parent); 60262306a36Sopenharmony_ci if (!parent_rate) 60362306a36Sopenharmony_ci continue; 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci for (div = 0; div < MASTER_PRES_MAX + 1; div++) { 60662306a36Sopenharmony_ci clk_sama7g5_master_best_diff(req, parent, parent_rate, 60762306a36Sopenharmony_ci &best_rate, &best_diff, 60862306a36Sopenharmony_ci div); 60962306a36Sopenharmony_ci if (!best_diff) 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci } 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci if (!best_diff) 61462306a36Sopenharmony_ci break; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci /* Second: try to request rate form changeable parent. */ 61862306a36Sopenharmony_ci if (master->chg_pid < 0) 61962306a36Sopenharmony_ci goto end; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci parent = clk_hw_get_parent_by_index(hw, master->chg_pid); 62262306a36Sopenharmony_ci if (!parent) 62362306a36Sopenharmony_ci goto end; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci for (div = 0; div < MASTER_PRES_MAX + 1; div++) { 62662306a36Sopenharmony_ci struct clk_rate_request req_parent; 62762306a36Sopenharmony_ci unsigned long req_rate; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci if (div == MASTER_PRES_MAX) 63062306a36Sopenharmony_ci req_rate = req->rate * 3; 63162306a36Sopenharmony_ci else 63262306a36Sopenharmony_ci req_rate = req->rate << div; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci clk_hw_forward_rate_request(hw, req, parent, &req_parent, req_rate); 63562306a36Sopenharmony_ci if (__clk_determine_rate(parent, &req_parent)) 63662306a36Sopenharmony_ci continue; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci clk_sama7g5_master_best_diff(req, parent, req_parent.rate, 63962306a36Sopenharmony_ci &best_rate, &best_diff, div); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (!best_diff) 64262306a36Sopenharmony_ci break; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ciend: 64662306a36Sopenharmony_ci pr_debug("MCK: %s, best_rate = %ld, parent clk: %s @ %ld\n", 64762306a36Sopenharmony_ci __func__, best_rate, 64862306a36Sopenharmony_ci __clk_get_name((req->best_parent_hw)->clk), 64962306a36Sopenharmony_ci req->best_parent_rate); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci if (best_rate < 0) 65262306a36Sopenharmony_ci return -EINVAL; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci req->rate = best_rate; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci return 0; 65762306a36Sopenharmony_ci} 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_cistatic u8 clk_sama7g5_master_get_parent(struct clk_hw *hw) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 66262306a36Sopenharmony_ci unsigned long flags; 66362306a36Sopenharmony_ci u8 index; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 66662306a36Sopenharmony_ci index = clk_mux_val_to_index(&master->hw, master->mux_table, 0, 66762306a36Sopenharmony_ci master->parent); 66862306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci return index; 67162306a36Sopenharmony_ci} 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic int clk_sama7g5_master_set_parent(struct clk_hw *hw, u8 index) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 67662306a36Sopenharmony_ci unsigned long flags; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (index >= clk_hw_get_num_parents(hw)) 67962306a36Sopenharmony_ci return -EINVAL; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 68262306a36Sopenharmony_ci master->parent = clk_mux_index_to_val(master->mux_table, 0, index); 68362306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return 0; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_cistatic void clk_sama7g5_master_set(struct clk_master *master, 68962306a36Sopenharmony_ci unsigned int status) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci unsigned long flags; 69262306a36Sopenharmony_ci unsigned int val, cparent; 69362306a36Sopenharmony_ci unsigned int enable = status ? AT91_PMC_MCR_V2_EN : 0; 69462306a36Sopenharmony_ci unsigned int parent = master->parent << PMC_MCR_CSS_SHIFT; 69562306a36Sopenharmony_ci unsigned int div = master->div << MASTER_DIV_SHIFT; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci regmap_write(master->regmap, AT91_PMC_MCR_V2, 70062306a36Sopenharmony_ci AT91_PMC_MCR_V2_ID(master->id)); 70162306a36Sopenharmony_ci regmap_read(master->regmap, AT91_PMC_MCR_V2, &val); 70262306a36Sopenharmony_ci regmap_update_bits(master->regmap, AT91_PMC_MCR_V2, 70362306a36Sopenharmony_ci enable | AT91_PMC_MCR_V2_CSS | AT91_PMC_MCR_V2_DIV | 70462306a36Sopenharmony_ci AT91_PMC_MCR_V2_CMD | AT91_PMC_MCR_V2_ID_MSK, 70562306a36Sopenharmony_ci enable | parent | div | AT91_PMC_MCR_V2_CMD | 70662306a36Sopenharmony_ci AT91_PMC_MCR_V2_ID(master->id)); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci cparent = (val & AT91_PMC_MCR_V2_CSS) >> PMC_MCR_CSS_SHIFT; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* Wait here only if parent is being changed. */ 71162306a36Sopenharmony_ci while ((cparent != master->parent) && !clk_master_ready(master)) 71262306a36Sopenharmony_ci cpu_relax(); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic int clk_sama7g5_master_enable(struct clk_hw *hw) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci clk_sama7g5_master_set(master, 1); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic void clk_sama7g5_master_disable(struct clk_hw *hw) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 72962306a36Sopenharmony_ci unsigned long flags; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id); 73462306a36Sopenharmony_ci regmap_update_bits(master->regmap, AT91_PMC_MCR_V2, 73562306a36Sopenharmony_ci AT91_PMC_MCR_V2_EN | AT91_PMC_MCR_V2_CMD | 73662306a36Sopenharmony_ci AT91_PMC_MCR_V2_ID_MSK, 73762306a36Sopenharmony_ci AT91_PMC_MCR_V2_CMD | 73862306a36Sopenharmony_ci AT91_PMC_MCR_V2_ID(master->id)); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_cistatic int clk_sama7g5_master_is_enabled(struct clk_hw *hw) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 74662306a36Sopenharmony_ci unsigned long flags; 74762306a36Sopenharmony_ci unsigned int val; 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id); 75262306a36Sopenharmony_ci regmap_read(master->regmap, AT91_PMC_MCR_V2, &val); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci return !!(val & AT91_PMC_MCR_V2_EN); 75762306a36Sopenharmony_ci} 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_cistatic int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate, 76062306a36Sopenharmony_ci unsigned long parent_rate) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 76362306a36Sopenharmony_ci unsigned long div, flags; 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci div = DIV_ROUND_CLOSEST(parent_rate, rate); 76662306a36Sopenharmony_ci if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1))) 76762306a36Sopenharmony_ci return -EINVAL; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci if (div == 3) 77062306a36Sopenharmony_ci div = MASTER_PRES_MAX; 77162306a36Sopenharmony_ci else if (div) 77262306a36Sopenharmony_ci div = ffs(div) - 1; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 77562306a36Sopenharmony_ci master->div = div; 77662306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci return 0; 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic int clk_sama7g5_master_save_context(struct clk_hw *hw) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci master->pms.status = clk_sama7g5_master_is_enabled(hw); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ci} 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic void clk_sama7g5_master_restore_context(struct clk_hw *hw) 79162306a36Sopenharmony_ci{ 79262306a36Sopenharmony_ci struct clk_master *master = to_clk_master(hw); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (master->pms.status) 79562306a36Sopenharmony_ci clk_sama7g5_master_set(master, master->pms.status); 79662306a36Sopenharmony_ci} 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cistatic const struct clk_ops sama7g5_master_ops = { 79962306a36Sopenharmony_ci .enable = clk_sama7g5_master_enable, 80062306a36Sopenharmony_ci .disable = clk_sama7g5_master_disable, 80162306a36Sopenharmony_ci .is_enabled = clk_sama7g5_master_is_enabled, 80262306a36Sopenharmony_ci .recalc_rate = clk_sama7g5_master_recalc_rate, 80362306a36Sopenharmony_ci .determine_rate = clk_sama7g5_master_determine_rate, 80462306a36Sopenharmony_ci .set_rate = clk_sama7g5_master_set_rate, 80562306a36Sopenharmony_ci .get_parent = clk_sama7g5_master_get_parent, 80662306a36Sopenharmony_ci .set_parent = clk_sama7g5_master_set_parent, 80762306a36Sopenharmony_ci .save_context = clk_sama7g5_master_save_context, 80862306a36Sopenharmony_ci .restore_context = clk_sama7g5_master_restore_context, 80962306a36Sopenharmony_ci}; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistruct clk_hw * __init 81262306a36Sopenharmony_ciat91_clk_sama7g5_register_master(struct regmap *regmap, 81362306a36Sopenharmony_ci const char *name, int num_parents, 81462306a36Sopenharmony_ci const char **parent_names, 81562306a36Sopenharmony_ci struct clk_hw **parent_hws, 81662306a36Sopenharmony_ci u32 *mux_table, 81762306a36Sopenharmony_ci spinlock_t *lock, u8 id, 81862306a36Sopenharmony_ci bool critical, int chg_pid) 81962306a36Sopenharmony_ci{ 82062306a36Sopenharmony_ci struct clk_master *master; 82162306a36Sopenharmony_ci struct clk_hw *hw; 82262306a36Sopenharmony_ci struct clk_init_data init = {}; 82362306a36Sopenharmony_ci unsigned long flags; 82462306a36Sopenharmony_ci unsigned int val; 82562306a36Sopenharmony_ci int ret; 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci if (!name || !num_parents || !(parent_names || parent_hws) || !mux_table || 82862306a36Sopenharmony_ci !lock || id > MASTER_MAX_ID) 82962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci master = kzalloc(sizeof(*master), GFP_KERNEL); 83262306a36Sopenharmony_ci if (!master) 83362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci init.name = name; 83662306a36Sopenharmony_ci init.ops = &sama7g5_master_ops; 83762306a36Sopenharmony_ci if (parent_hws) 83862306a36Sopenharmony_ci init.parent_hws = (const struct clk_hw **)parent_hws; 83962306a36Sopenharmony_ci else 84062306a36Sopenharmony_ci init.parent_names = parent_names; 84162306a36Sopenharmony_ci init.num_parents = num_parents; 84262306a36Sopenharmony_ci init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; 84362306a36Sopenharmony_ci if (chg_pid >= 0) 84462306a36Sopenharmony_ci init.flags |= CLK_SET_RATE_PARENT; 84562306a36Sopenharmony_ci if (critical) 84662306a36Sopenharmony_ci init.flags |= CLK_IS_CRITICAL; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci master->hw.init = &init; 84962306a36Sopenharmony_ci master->regmap = regmap; 85062306a36Sopenharmony_ci master->id = id; 85162306a36Sopenharmony_ci master->chg_pid = chg_pid; 85262306a36Sopenharmony_ci master->lock = lock; 85362306a36Sopenharmony_ci master->mux_table = mux_table; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci spin_lock_irqsave(master->lock, flags); 85662306a36Sopenharmony_ci regmap_write(master->regmap, AT91_PMC_MCR_V2, master->id); 85762306a36Sopenharmony_ci regmap_read(master->regmap, AT91_PMC_MCR_V2, &val); 85862306a36Sopenharmony_ci master->parent = (val & AT91_PMC_MCR_V2_CSS) >> PMC_MCR_CSS_SHIFT; 85962306a36Sopenharmony_ci master->div = (val & AT91_PMC_MCR_V2_DIV) >> MASTER_DIV_SHIFT; 86062306a36Sopenharmony_ci spin_unlock_irqrestore(master->lock, flags); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci hw = &master->hw; 86362306a36Sopenharmony_ci ret = clk_hw_register(NULL, &master->hw); 86462306a36Sopenharmony_ci if (ret) { 86562306a36Sopenharmony_ci kfree(master); 86662306a36Sopenharmony_ci hw = ERR_PTR(ret); 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci return hw; 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ciconst struct clk_master_layout at91rm9200_master_layout = { 87362306a36Sopenharmony_ci .mask = 0x31F, 87462306a36Sopenharmony_ci .pres_shift = 2, 87562306a36Sopenharmony_ci .offset = AT91_PMC_MCKR, 87662306a36Sopenharmony_ci}; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ciconst struct clk_master_layout at91sam9x5_master_layout = { 87962306a36Sopenharmony_ci .mask = 0x373, 88062306a36Sopenharmony_ci .pres_shift = 4, 88162306a36Sopenharmony_ci .offset = AT91_PMC_MCKR, 88262306a36Sopenharmony_ci}; 883