12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2e442d234SBoris BREZILLON /* 3e442d234SBoris BREZILLON * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> 4e442d234SBoris BREZILLON */ 5e442d234SBoris BREZILLON 6e442d234SBoris BREZILLON #include <linux/clk-provider.h> 7e442d234SBoris BREZILLON #include <linux/clkdev.h> 8e442d234SBoris BREZILLON #include <linux/clk/at91_pmc.h> 9e442d234SBoris BREZILLON #include <linux/of.h> 101bdf0232SBoris Brezillon #include <linux/mfd/syscon.h> 111bdf0232SBoris Brezillon #include <linux/regmap.h> 12e442d234SBoris BREZILLON 13e442d234SBoris BREZILLON #include "pmc.h" 14e442d234SBoris BREZILLON 15e442d234SBoris BREZILLON #define MASTER_PRES_MASK 0x7 16e442d234SBoris BREZILLON #define MASTER_PRES_MAX MASTER_PRES_MASK 17e442d234SBoris BREZILLON #define MASTER_DIV_SHIFT 8 18*e26b3006SEugen Hristev #define MASTER_DIV_MASK 0x7 19e442d234SBoris BREZILLON 2075c88143SClaudiu Beznea #define PMC_MCR 0x30 2175c88143SClaudiu Beznea #define PMC_MCR_ID_MSK GENMASK(3, 0) 2275c88143SClaudiu Beznea #define PMC_MCR_CMD BIT(7) 2375c88143SClaudiu Beznea #define PMC_MCR_DIV GENMASK(10, 8) 2475c88143SClaudiu Beznea #define PMC_MCR_CSS GENMASK(20, 16) 2575c88143SClaudiu Beznea #define PMC_MCR_CSS_SHIFT (16) 2675c88143SClaudiu Beznea #define PMC_MCR_EN BIT(28) 2775c88143SClaudiu Beznea 2875c88143SClaudiu Beznea #define PMC_MCR_ID(x) ((x) & PMC_MCR_ID_MSK) 2975c88143SClaudiu Beznea 3075c88143SClaudiu Beznea #define MASTER_MAX_ID 4 3175c88143SClaudiu Beznea 32e442d234SBoris BREZILLON #define to_clk_master(hw) container_of(hw, struct clk_master, hw) 33e442d234SBoris BREZILLON 34e442d234SBoris BREZILLON struct clk_master { 35e442d234SBoris BREZILLON struct clk_hw hw; 361bdf0232SBoris Brezillon struct regmap *regmap; 3775c88143SClaudiu Beznea spinlock_t *lock; 38e442d234SBoris BREZILLON const struct clk_master_layout *layout; 39e442d234SBoris BREZILLON const struct clk_master_characteristics *characteristics; 4075c88143SClaudiu Beznea u32 *mux_table; 41e5be5370SAlexandre Belloni u32 mckr; 4275c88143SClaudiu Beznea int chg_pid; 4375c88143SClaudiu Beznea u8 id; 4475c88143SClaudiu Beznea u8 parent; 4575c88143SClaudiu Beznea u8 div; 46e442d234SBoris BREZILLON }; 47e442d234SBoris BREZILLON 4875c88143SClaudiu Beznea static inline bool clk_master_ready(struct clk_master *master) 491bdf0232SBoris Brezillon { 5075c88143SClaudiu Beznea unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY; 511bdf0232SBoris Brezillon unsigned int status; 521bdf0232SBoris Brezillon 5375c88143SClaudiu Beznea regmap_read(master->regmap, AT91_PMC_SR, &status); 541bdf0232SBoris Brezillon 5575c88143SClaudiu Beznea return !!(status & bit); 561bdf0232SBoris Brezillon } 571bdf0232SBoris Brezillon 58e442d234SBoris BREZILLON static int clk_master_prepare(struct clk_hw *hw) 59e442d234SBoris BREZILLON { 60e442d234SBoris BREZILLON struct clk_master *master = to_clk_master(hw); 61e442d234SBoris BREZILLON 6275c88143SClaudiu Beznea while (!clk_master_ready(master)) 6399a81706SAlexandre Belloni cpu_relax(); 64e442d234SBoris BREZILLON 65e442d234SBoris BREZILLON return 0; 66e442d234SBoris BREZILLON } 67e442d234SBoris BREZILLON 68e442d234SBoris BREZILLON static int clk_master_is_prepared(struct clk_hw *hw) 69e442d234SBoris BREZILLON { 70e442d234SBoris BREZILLON struct clk_master *master = to_clk_master(hw); 71e442d234SBoris BREZILLON 7275c88143SClaudiu Beznea return clk_master_ready(master); 73e442d234SBoris BREZILLON } 74e442d234SBoris BREZILLON 75e442d234SBoris BREZILLON static unsigned long clk_master_recalc_rate(struct clk_hw *hw, 76e442d234SBoris BREZILLON unsigned long parent_rate) 77e442d234SBoris BREZILLON { 78e442d234SBoris BREZILLON u8 pres; 79e442d234SBoris BREZILLON u8 div; 80e442d234SBoris BREZILLON unsigned long rate = parent_rate; 81e442d234SBoris BREZILLON struct clk_master *master = to_clk_master(hw); 82e442d234SBoris BREZILLON const struct clk_master_layout *layout = master->layout; 83e442d234SBoris BREZILLON const struct clk_master_characteristics *characteristics = 84e442d234SBoris BREZILLON master->characteristics; 851bdf0232SBoris Brezillon unsigned int mckr; 86e442d234SBoris BREZILLON 87e5be5370SAlexandre Belloni regmap_read(master->regmap, master->layout->offset, &mckr); 881bdf0232SBoris Brezillon mckr &= layout->mask; 89e442d234SBoris BREZILLON 901bdf0232SBoris Brezillon pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK; 911bdf0232SBoris Brezillon div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK; 92e442d234SBoris BREZILLON 93e442d234SBoris BREZILLON if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX) 94e442d234SBoris BREZILLON rate /= 3; 95e442d234SBoris BREZILLON else 96e442d234SBoris BREZILLON rate >>= pres; 97e442d234SBoris BREZILLON 98e442d234SBoris BREZILLON rate /= characteristics->divisors[div]; 99e442d234SBoris BREZILLON 100e442d234SBoris BREZILLON if (rate < characteristics->output.min) 101e442d234SBoris BREZILLON pr_warn("master clk is underclocked"); 102e442d234SBoris BREZILLON else if (rate > characteristics->output.max) 103e442d234SBoris BREZILLON pr_warn("master clk is overclocked"); 104e442d234SBoris BREZILLON 105e442d234SBoris BREZILLON return rate; 106e442d234SBoris BREZILLON } 107e442d234SBoris BREZILLON 108e442d234SBoris BREZILLON static u8 clk_master_get_parent(struct clk_hw *hw) 109e442d234SBoris BREZILLON { 110e442d234SBoris BREZILLON struct clk_master *master = to_clk_master(hw); 1111bdf0232SBoris Brezillon unsigned int mckr; 112e442d234SBoris BREZILLON 113e5be5370SAlexandre Belloni regmap_read(master->regmap, master->layout->offset, &mckr); 1141bdf0232SBoris Brezillon 1151bdf0232SBoris Brezillon return mckr & AT91_PMC_CSS; 116e442d234SBoris BREZILLON } 117e442d234SBoris BREZILLON 118e442d234SBoris BREZILLON static const struct clk_ops master_ops = { 119e442d234SBoris BREZILLON .prepare = clk_master_prepare, 120e442d234SBoris BREZILLON .is_prepared = clk_master_is_prepared, 121e442d234SBoris BREZILLON .recalc_rate = clk_master_recalc_rate, 122e442d234SBoris BREZILLON .get_parent = clk_master_get_parent, 123e442d234SBoris BREZILLON }; 124e442d234SBoris BREZILLON 125b2e39dc0SAlexandre Belloni struct clk_hw * __init 12699a81706SAlexandre Belloni at91_clk_register_master(struct regmap *regmap, 127e442d234SBoris BREZILLON const char *name, int num_parents, 128e442d234SBoris BREZILLON const char **parent_names, 129e442d234SBoris BREZILLON const struct clk_master_layout *layout, 130e442d234SBoris BREZILLON const struct clk_master_characteristics *characteristics) 131e442d234SBoris BREZILLON { 132e442d234SBoris BREZILLON struct clk_master *master; 133e442d234SBoris BREZILLON struct clk_init_data init; 134f5644f10SStephen Boyd struct clk_hw *hw; 135f5644f10SStephen Boyd int ret; 136e442d234SBoris BREZILLON 1371bdf0232SBoris Brezillon if (!name || !num_parents || !parent_names) 138e442d234SBoris BREZILLON return ERR_PTR(-EINVAL); 139e442d234SBoris BREZILLON 140e442d234SBoris BREZILLON master = kzalloc(sizeof(*master), GFP_KERNEL); 141e442d234SBoris BREZILLON if (!master) 142e442d234SBoris BREZILLON return ERR_PTR(-ENOMEM); 143e442d234SBoris BREZILLON 144e442d234SBoris BREZILLON init.name = name; 145e442d234SBoris BREZILLON init.ops = &master_ops; 146e442d234SBoris BREZILLON init.parent_names = parent_names; 147e442d234SBoris BREZILLON init.num_parents = num_parents; 148e442d234SBoris BREZILLON init.flags = 0; 149e442d234SBoris BREZILLON 150e442d234SBoris BREZILLON master->hw.init = &init; 151e442d234SBoris BREZILLON master->layout = layout; 152e442d234SBoris BREZILLON master->characteristics = characteristics; 1531bdf0232SBoris Brezillon master->regmap = regmap; 154e442d234SBoris BREZILLON 155f5644f10SStephen Boyd hw = &master->hw; 156f5644f10SStephen Boyd ret = clk_hw_register(NULL, &master->hw); 157f5644f10SStephen Boyd if (ret) { 158e442d234SBoris BREZILLON kfree(master); 159f5644f10SStephen Boyd hw = ERR_PTR(ret); 160c76a024eSDavid Dueck } 161e442d234SBoris BREZILLON 162f5644f10SStephen Boyd return hw; 163e442d234SBoris BREZILLON } 164e442d234SBoris BREZILLON 16575c88143SClaudiu Beznea static unsigned long 16675c88143SClaudiu Beznea clk_sama7g5_master_recalc_rate(struct clk_hw *hw, 16775c88143SClaudiu Beznea unsigned long parent_rate) 16875c88143SClaudiu Beznea { 16975c88143SClaudiu Beznea struct clk_master *master = to_clk_master(hw); 17075c88143SClaudiu Beznea 17175c88143SClaudiu Beznea return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div)); 17275c88143SClaudiu Beznea } 17375c88143SClaudiu Beznea 17475c88143SClaudiu Beznea static void clk_sama7g5_master_best_diff(struct clk_rate_request *req, 17575c88143SClaudiu Beznea struct clk_hw *parent, 17675c88143SClaudiu Beznea unsigned long parent_rate, 17775c88143SClaudiu Beznea long *best_rate, 17875c88143SClaudiu Beznea long *best_diff, 17975c88143SClaudiu Beznea u32 div) 18075c88143SClaudiu Beznea { 18175c88143SClaudiu Beznea unsigned long tmp_rate, tmp_diff; 18275c88143SClaudiu Beznea 18375c88143SClaudiu Beznea if (div == MASTER_PRES_MAX) 18475c88143SClaudiu Beznea tmp_rate = parent_rate / 3; 18575c88143SClaudiu Beznea else 18675c88143SClaudiu Beznea tmp_rate = parent_rate >> div; 18775c88143SClaudiu Beznea 18875c88143SClaudiu Beznea tmp_diff = abs(req->rate - tmp_rate); 18975c88143SClaudiu Beznea 19075c88143SClaudiu Beznea if (*best_diff < 0 || *best_diff >= tmp_diff) { 19175c88143SClaudiu Beznea *best_rate = tmp_rate; 19275c88143SClaudiu Beznea *best_diff = tmp_diff; 19375c88143SClaudiu Beznea req->best_parent_rate = parent_rate; 19475c88143SClaudiu Beznea req->best_parent_hw = parent; 19575c88143SClaudiu Beznea } 19675c88143SClaudiu Beznea } 19775c88143SClaudiu Beznea 19875c88143SClaudiu Beznea static int clk_sama7g5_master_determine_rate(struct clk_hw *hw, 19975c88143SClaudiu Beznea struct clk_rate_request *req) 20075c88143SClaudiu Beznea { 20175c88143SClaudiu Beznea struct clk_master *master = to_clk_master(hw); 20275c88143SClaudiu Beznea struct clk_rate_request req_parent = *req; 20375c88143SClaudiu Beznea struct clk_hw *parent; 20475c88143SClaudiu Beznea long best_rate = LONG_MIN, best_diff = LONG_MIN; 20575c88143SClaudiu Beznea unsigned long parent_rate; 20675c88143SClaudiu Beznea unsigned int div, i; 20775c88143SClaudiu Beznea 20875c88143SClaudiu Beznea /* First: check the dividers of MCR. */ 20975c88143SClaudiu Beznea for (i = 0; i < clk_hw_get_num_parents(hw); i++) { 21075c88143SClaudiu Beznea parent = clk_hw_get_parent_by_index(hw, i); 21175c88143SClaudiu Beznea if (!parent) 21275c88143SClaudiu Beznea continue; 21375c88143SClaudiu Beznea 21475c88143SClaudiu Beznea parent_rate = clk_hw_get_rate(parent); 21575c88143SClaudiu Beznea if (!parent_rate) 21675c88143SClaudiu Beznea continue; 21775c88143SClaudiu Beznea 21875c88143SClaudiu Beznea for (div = 0; div < MASTER_PRES_MAX + 1; div++) { 21975c88143SClaudiu Beznea clk_sama7g5_master_best_diff(req, parent, parent_rate, 22075c88143SClaudiu Beznea &best_rate, &best_diff, 22175c88143SClaudiu Beznea div); 22275c88143SClaudiu Beznea if (!best_diff) 22375c88143SClaudiu Beznea break; 22475c88143SClaudiu Beznea } 22575c88143SClaudiu Beznea 22675c88143SClaudiu Beznea if (!best_diff) 22775c88143SClaudiu Beznea break; 22875c88143SClaudiu Beznea } 22975c88143SClaudiu Beznea 23075c88143SClaudiu Beznea /* Second: try to request rate form changeable parent. */ 23175c88143SClaudiu Beznea if (master->chg_pid < 0) 23275c88143SClaudiu Beznea goto end; 23375c88143SClaudiu Beznea 23475c88143SClaudiu Beznea parent = clk_hw_get_parent_by_index(hw, master->chg_pid); 23575c88143SClaudiu Beznea if (!parent) 23675c88143SClaudiu Beznea goto end; 23775c88143SClaudiu Beznea 23875c88143SClaudiu Beznea for (div = 0; div < MASTER_PRES_MAX + 1; div++) { 23975c88143SClaudiu Beznea if (div == MASTER_PRES_MAX) 24075c88143SClaudiu Beznea req_parent.rate = req->rate * 3; 24175c88143SClaudiu Beznea else 24275c88143SClaudiu Beznea req_parent.rate = req->rate << div; 24375c88143SClaudiu Beznea 24475c88143SClaudiu Beznea if (__clk_determine_rate(parent, &req_parent)) 24575c88143SClaudiu Beznea continue; 24675c88143SClaudiu Beznea 24775c88143SClaudiu Beznea clk_sama7g5_master_best_diff(req, parent, req_parent.rate, 24875c88143SClaudiu Beznea &best_rate, &best_diff, div); 24975c88143SClaudiu Beznea 25075c88143SClaudiu Beznea if (!best_diff) 25175c88143SClaudiu Beznea break; 25275c88143SClaudiu Beznea } 25375c88143SClaudiu Beznea 25475c88143SClaudiu Beznea end: 25575c88143SClaudiu Beznea pr_debug("MCK: %s, best_rate = %ld, parent clk: %s @ %ld\n", 25675c88143SClaudiu Beznea __func__, best_rate, 25775c88143SClaudiu Beznea __clk_get_name((req->best_parent_hw)->clk), 25875c88143SClaudiu Beznea req->best_parent_rate); 25975c88143SClaudiu Beznea 26075c88143SClaudiu Beznea if (best_rate < 0) 26175c88143SClaudiu Beznea return -EINVAL; 26275c88143SClaudiu Beznea 26375c88143SClaudiu Beznea req->rate = best_rate; 26475c88143SClaudiu Beznea 26575c88143SClaudiu Beznea return 0; 26675c88143SClaudiu Beznea } 26775c88143SClaudiu Beznea 26875c88143SClaudiu Beznea static u8 clk_sama7g5_master_get_parent(struct clk_hw *hw) 26975c88143SClaudiu Beznea { 27075c88143SClaudiu Beznea struct clk_master *master = to_clk_master(hw); 27175c88143SClaudiu Beznea unsigned long flags; 27275c88143SClaudiu Beznea u8 index; 27375c88143SClaudiu Beznea 27475c88143SClaudiu Beznea spin_lock_irqsave(master->lock, flags); 27575c88143SClaudiu Beznea index = clk_mux_val_to_index(&master->hw, master->mux_table, 0, 27675c88143SClaudiu Beznea master->parent); 27775c88143SClaudiu Beznea spin_unlock_irqrestore(master->lock, flags); 27875c88143SClaudiu Beznea 27975c88143SClaudiu Beznea return index; 28075c88143SClaudiu Beznea } 28175c88143SClaudiu Beznea 28275c88143SClaudiu Beznea static int clk_sama7g5_master_set_parent(struct clk_hw *hw, u8 index) 28375c88143SClaudiu Beznea { 28475c88143SClaudiu Beznea struct clk_master *master = to_clk_master(hw); 28575c88143SClaudiu Beznea unsigned long flags; 28675c88143SClaudiu Beznea 28775c88143SClaudiu Beznea if (index >= clk_hw_get_num_parents(hw)) 28875c88143SClaudiu Beznea return -EINVAL; 28975c88143SClaudiu Beznea 29075c88143SClaudiu Beznea spin_lock_irqsave(master->lock, flags); 29175c88143SClaudiu Beznea master->parent = clk_mux_index_to_val(master->mux_table, 0, index); 29275c88143SClaudiu Beznea spin_unlock_irqrestore(master->lock, flags); 29375c88143SClaudiu Beznea 29475c88143SClaudiu Beznea return 0; 29575c88143SClaudiu Beznea } 29675c88143SClaudiu Beznea 29775c88143SClaudiu Beznea static int clk_sama7g5_master_enable(struct clk_hw *hw) 29875c88143SClaudiu Beznea { 29975c88143SClaudiu Beznea struct clk_master *master = to_clk_master(hw); 30075c88143SClaudiu Beznea unsigned long flags; 30175c88143SClaudiu Beznea unsigned int val, cparent; 30275c88143SClaudiu Beznea 30375c88143SClaudiu Beznea spin_lock_irqsave(master->lock, flags); 30475c88143SClaudiu Beznea 30575c88143SClaudiu Beznea regmap_write(master->regmap, PMC_MCR, PMC_MCR_ID(master->id)); 30675c88143SClaudiu Beznea regmap_read(master->regmap, PMC_MCR, &val); 30775c88143SClaudiu Beznea regmap_update_bits(master->regmap, PMC_MCR, 30875c88143SClaudiu Beznea PMC_MCR_EN | PMC_MCR_CSS | PMC_MCR_DIV | 30975c88143SClaudiu Beznea PMC_MCR_CMD | PMC_MCR_ID_MSK, 31075c88143SClaudiu Beznea PMC_MCR_EN | (master->parent << PMC_MCR_CSS_SHIFT) | 31175c88143SClaudiu Beznea (master->div << MASTER_DIV_SHIFT) | 31275c88143SClaudiu Beznea PMC_MCR_CMD | PMC_MCR_ID(master->id)); 31375c88143SClaudiu Beznea 31475c88143SClaudiu Beznea cparent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT; 31575c88143SClaudiu Beznea 31675c88143SClaudiu Beznea /* Wait here only if parent is being changed. */ 31775c88143SClaudiu Beznea while ((cparent != master->parent) && !clk_master_ready(master)) 31875c88143SClaudiu Beznea cpu_relax(); 31975c88143SClaudiu Beznea 32075c88143SClaudiu Beznea spin_unlock_irqrestore(master->lock, flags); 32175c88143SClaudiu Beznea 32275c88143SClaudiu Beznea return 0; 32375c88143SClaudiu Beznea } 32475c88143SClaudiu Beznea 32575c88143SClaudiu Beznea static void clk_sama7g5_master_disable(struct clk_hw *hw) 32675c88143SClaudiu Beznea { 32775c88143SClaudiu Beznea struct clk_master *master = to_clk_master(hw); 32875c88143SClaudiu Beznea unsigned long flags; 32975c88143SClaudiu Beznea 33075c88143SClaudiu Beznea spin_lock_irqsave(master->lock, flags); 33175c88143SClaudiu Beznea 33275c88143SClaudiu Beznea regmap_write(master->regmap, PMC_MCR, master->id); 33375c88143SClaudiu Beznea regmap_update_bits(master->regmap, PMC_MCR, 33475c88143SClaudiu Beznea PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK, 33575c88143SClaudiu Beznea PMC_MCR_CMD | PMC_MCR_ID(master->id)); 33675c88143SClaudiu Beznea 33775c88143SClaudiu Beznea spin_unlock_irqrestore(master->lock, flags); 33875c88143SClaudiu Beznea } 33975c88143SClaudiu Beznea 34075c88143SClaudiu Beznea static int clk_sama7g5_master_is_enabled(struct clk_hw *hw) 34175c88143SClaudiu Beznea { 34275c88143SClaudiu Beznea struct clk_master *master = to_clk_master(hw); 34375c88143SClaudiu Beznea unsigned long flags; 34475c88143SClaudiu Beznea unsigned int val; 34575c88143SClaudiu Beznea 34675c88143SClaudiu Beznea spin_lock_irqsave(master->lock, flags); 34775c88143SClaudiu Beznea 34875c88143SClaudiu Beznea regmap_write(master->regmap, PMC_MCR, master->id); 34975c88143SClaudiu Beznea regmap_read(master->regmap, PMC_MCR, &val); 35075c88143SClaudiu Beznea 35175c88143SClaudiu Beznea spin_unlock_irqrestore(master->lock, flags); 35275c88143SClaudiu Beznea 35375c88143SClaudiu Beznea return !!(val & PMC_MCR_EN); 35475c88143SClaudiu Beznea } 35575c88143SClaudiu Beznea 35675c88143SClaudiu Beznea static int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate, 35775c88143SClaudiu Beznea unsigned long parent_rate) 35875c88143SClaudiu Beznea { 35975c88143SClaudiu Beznea struct clk_master *master = to_clk_master(hw); 36075c88143SClaudiu Beznea unsigned long div, flags; 36175c88143SClaudiu Beznea 36275c88143SClaudiu Beznea div = DIV_ROUND_CLOSEST(parent_rate, rate); 36375c88143SClaudiu Beznea if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1))) 36475c88143SClaudiu Beznea return -EINVAL; 36575c88143SClaudiu Beznea 36675c88143SClaudiu Beznea if (div == 3) 36775c88143SClaudiu Beznea div = MASTER_PRES_MAX; 36875c88143SClaudiu Beznea else 36975c88143SClaudiu Beznea div = ffs(div) - 1; 37075c88143SClaudiu Beznea 37175c88143SClaudiu Beznea spin_lock_irqsave(master->lock, flags); 37275c88143SClaudiu Beznea master->div = div; 37375c88143SClaudiu Beznea spin_unlock_irqrestore(master->lock, flags); 37475c88143SClaudiu Beznea 37575c88143SClaudiu Beznea return 0; 37675c88143SClaudiu Beznea } 37775c88143SClaudiu Beznea 37875c88143SClaudiu Beznea static const struct clk_ops sama7g5_master_ops = { 37975c88143SClaudiu Beznea .enable = clk_sama7g5_master_enable, 38075c88143SClaudiu Beznea .disable = clk_sama7g5_master_disable, 38175c88143SClaudiu Beznea .is_enabled = clk_sama7g5_master_is_enabled, 38275c88143SClaudiu Beznea .recalc_rate = clk_sama7g5_master_recalc_rate, 38375c88143SClaudiu Beznea .determine_rate = clk_sama7g5_master_determine_rate, 38475c88143SClaudiu Beznea .set_rate = clk_sama7g5_master_set_rate, 38575c88143SClaudiu Beznea .get_parent = clk_sama7g5_master_get_parent, 38675c88143SClaudiu Beznea .set_parent = clk_sama7g5_master_set_parent, 38775c88143SClaudiu Beznea }; 38875c88143SClaudiu Beznea 38975c88143SClaudiu Beznea struct clk_hw * __init 39075c88143SClaudiu Beznea at91_clk_sama7g5_register_master(struct regmap *regmap, 39175c88143SClaudiu Beznea const char *name, int num_parents, 39275c88143SClaudiu Beznea const char **parent_names, 39375c88143SClaudiu Beznea u32 *mux_table, 39475c88143SClaudiu Beznea spinlock_t *lock, u8 id, 39575c88143SClaudiu Beznea bool critical, int chg_pid) 39675c88143SClaudiu Beznea { 39775c88143SClaudiu Beznea struct clk_master *master; 39875c88143SClaudiu Beznea struct clk_hw *hw; 39975c88143SClaudiu Beznea struct clk_init_data init; 40075c88143SClaudiu Beznea unsigned long flags; 40175c88143SClaudiu Beznea unsigned int val; 40275c88143SClaudiu Beznea int ret; 40375c88143SClaudiu Beznea 40475c88143SClaudiu Beznea if (!name || !num_parents || !parent_names || !mux_table || 40575c88143SClaudiu Beznea !lock || id > MASTER_MAX_ID) 40675c88143SClaudiu Beznea return ERR_PTR(-EINVAL); 40775c88143SClaudiu Beznea 40875c88143SClaudiu Beznea master = kzalloc(sizeof(*master), GFP_KERNEL); 40975c88143SClaudiu Beznea if (!master) 41075c88143SClaudiu Beznea return ERR_PTR(-ENOMEM); 41175c88143SClaudiu Beznea 41275c88143SClaudiu Beznea init.name = name; 41375c88143SClaudiu Beznea init.ops = &sama7g5_master_ops; 41475c88143SClaudiu Beznea init.parent_names = parent_names; 41575c88143SClaudiu Beznea init.num_parents = num_parents; 41675c88143SClaudiu Beznea init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; 41775c88143SClaudiu Beznea if (chg_pid >= 0) 41875c88143SClaudiu Beznea init.flags |= CLK_SET_RATE_PARENT; 41975c88143SClaudiu Beznea if (critical) 42075c88143SClaudiu Beznea init.flags |= CLK_IS_CRITICAL; 42175c88143SClaudiu Beznea 42275c88143SClaudiu Beznea master->hw.init = &init; 42375c88143SClaudiu Beznea master->regmap = regmap; 42475c88143SClaudiu Beznea master->id = id; 42575c88143SClaudiu Beznea master->chg_pid = chg_pid; 42675c88143SClaudiu Beznea master->lock = lock; 42775c88143SClaudiu Beznea master->mux_table = mux_table; 42875c88143SClaudiu Beznea 42975c88143SClaudiu Beznea spin_lock_irqsave(master->lock, flags); 43075c88143SClaudiu Beznea regmap_write(master->regmap, PMC_MCR, master->id); 43175c88143SClaudiu Beznea regmap_read(master->regmap, PMC_MCR, &val); 43275c88143SClaudiu Beznea master->parent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT; 43375c88143SClaudiu Beznea master->div = (val & PMC_MCR_DIV) >> MASTER_DIV_SHIFT; 43475c88143SClaudiu Beznea spin_unlock_irqrestore(master->lock, flags); 43575c88143SClaudiu Beznea 43675c88143SClaudiu Beznea hw = &master->hw; 43775c88143SClaudiu Beznea ret = clk_hw_register(NULL, &master->hw); 43875c88143SClaudiu Beznea if (ret) { 43975c88143SClaudiu Beznea kfree(master); 44075c88143SClaudiu Beznea hw = ERR_PTR(ret); 44175c88143SClaudiu Beznea } 44275c88143SClaudiu Beznea 44375c88143SClaudiu Beznea return hw; 44475c88143SClaudiu Beznea } 44575c88143SClaudiu Beznea 446b2e39dc0SAlexandre Belloni const struct clk_master_layout at91rm9200_master_layout = { 447e442d234SBoris BREZILLON .mask = 0x31F, 448e442d234SBoris BREZILLON .pres_shift = 2, 449e5be5370SAlexandre Belloni .offset = AT91_PMC_MCKR, 450e442d234SBoris BREZILLON }; 451e442d234SBoris BREZILLON 452b2e39dc0SAlexandre Belloni const struct clk_master_layout at91sam9x5_master_layout = { 453e442d234SBoris BREZILLON .mask = 0x373, 454e442d234SBoris BREZILLON .pres_shift = 4, 455e5be5370SAlexandre Belloni .offset = AT91_PMC_MCKR, 456e442d234SBoris BREZILLON }; 457