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/clk-provider.h>
78c2ecf20Sopenharmony_ci#include <linux/clkdev.h>
88c2ecf20Sopenharmony_ci#include <linux/clk/at91_pmc.h>
98c2ecf20Sopenharmony_ci#include <linux/of.h>
108c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
118c2ecf20Sopenharmony_ci#include <linux/regmap.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "pmc.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define MASTER_PRES_MASK	0x7
168c2ecf20Sopenharmony_ci#define MASTER_PRES_MAX		MASTER_PRES_MASK
178c2ecf20Sopenharmony_ci#define MASTER_DIV_SHIFT	8
188c2ecf20Sopenharmony_ci#define MASTER_DIV_MASK		0x3
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define PMC_MCR			0x30
218c2ecf20Sopenharmony_ci#define PMC_MCR_ID_MSK		GENMASK(3, 0)
228c2ecf20Sopenharmony_ci#define PMC_MCR_CMD		BIT(7)
238c2ecf20Sopenharmony_ci#define PMC_MCR_DIV		GENMASK(10, 8)
248c2ecf20Sopenharmony_ci#define PMC_MCR_CSS		GENMASK(20, 16)
258c2ecf20Sopenharmony_ci#define PMC_MCR_CSS_SHIFT	(16)
268c2ecf20Sopenharmony_ci#define PMC_MCR_EN		BIT(28)
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define PMC_MCR_ID(x)		((x) & PMC_MCR_ID_MSK)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define MASTER_MAX_ID		4
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define to_clk_master(hw) container_of(hw, struct clk_master, hw)
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistruct clk_master {
358c2ecf20Sopenharmony_ci	struct clk_hw hw;
368c2ecf20Sopenharmony_ci	struct regmap *regmap;
378c2ecf20Sopenharmony_ci	spinlock_t *lock;
388c2ecf20Sopenharmony_ci	const struct clk_master_layout *layout;
398c2ecf20Sopenharmony_ci	const struct clk_master_characteristics *characteristics;
408c2ecf20Sopenharmony_ci	u32 *mux_table;
418c2ecf20Sopenharmony_ci	u32 mckr;
428c2ecf20Sopenharmony_ci	int chg_pid;
438c2ecf20Sopenharmony_ci	u8 id;
448c2ecf20Sopenharmony_ci	u8 parent;
458c2ecf20Sopenharmony_ci	u8 div;
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic inline bool clk_master_ready(struct clk_master *master)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
518c2ecf20Sopenharmony_ci	unsigned int status;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	regmap_read(master->regmap, AT91_PMC_SR, &status);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	return !!(status & bit);
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic int clk_master_prepare(struct clk_hw *hw)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct clk_master *master = to_clk_master(hw);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	while (!clk_master_ready(master))
638c2ecf20Sopenharmony_ci		cpu_relax();
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	return 0;
668c2ecf20Sopenharmony_ci}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistatic int clk_master_is_prepared(struct clk_hw *hw)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct clk_master *master = to_clk_master(hw);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	return clk_master_ready(master);
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic unsigned long clk_master_recalc_rate(struct clk_hw *hw,
768c2ecf20Sopenharmony_ci					    unsigned long parent_rate)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	u8 pres;
798c2ecf20Sopenharmony_ci	u8 div;
808c2ecf20Sopenharmony_ci	unsigned long rate = parent_rate;
818c2ecf20Sopenharmony_ci	struct clk_master *master = to_clk_master(hw);
828c2ecf20Sopenharmony_ci	const struct clk_master_layout *layout = master->layout;
838c2ecf20Sopenharmony_ci	const struct clk_master_characteristics *characteristics =
848c2ecf20Sopenharmony_ci						master->characteristics;
858c2ecf20Sopenharmony_ci	unsigned int mckr;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	regmap_read(master->regmap, master->layout->offset, &mckr);
888c2ecf20Sopenharmony_ci	mckr &= layout->mask;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
918c2ecf20Sopenharmony_ci	div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
948c2ecf20Sopenharmony_ci		rate /= 3;
958c2ecf20Sopenharmony_ci	else
968c2ecf20Sopenharmony_ci		rate >>= pres;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	rate /= characteristics->divisors[div];
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (rate < characteristics->output.min)
1018c2ecf20Sopenharmony_ci		pr_warn("master clk is underclocked");
1028c2ecf20Sopenharmony_ci	else if (rate > characteristics->output.max)
1038c2ecf20Sopenharmony_ci		pr_warn("master clk is overclocked");
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	return rate;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistatic u8 clk_master_get_parent(struct clk_hw *hw)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct clk_master *master = to_clk_master(hw);
1118c2ecf20Sopenharmony_ci	unsigned int mckr;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	regmap_read(master->regmap, master->layout->offset, &mckr);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return mckr & AT91_PMC_CSS;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistatic const struct clk_ops master_ops = {
1198c2ecf20Sopenharmony_ci	.prepare = clk_master_prepare,
1208c2ecf20Sopenharmony_ci	.is_prepared = clk_master_is_prepared,
1218c2ecf20Sopenharmony_ci	.recalc_rate = clk_master_recalc_rate,
1228c2ecf20Sopenharmony_ci	.get_parent = clk_master_get_parent,
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cistruct clk_hw * __init
1268c2ecf20Sopenharmony_ciat91_clk_register_master(struct regmap *regmap,
1278c2ecf20Sopenharmony_ci		const char *name, int num_parents,
1288c2ecf20Sopenharmony_ci		const char **parent_names,
1298c2ecf20Sopenharmony_ci		const struct clk_master_layout *layout,
1308c2ecf20Sopenharmony_ci		const struct clk_master_characteristics *characteristics)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	struct clk_master *master;
1338c2ecf20Sopenharmony_ci	struct clk_init_data init;
1348c2ecf20Sopenharmony_ci	struct clk_hw *hw;
1358c2ecf20Sopenharmony_ci	int ret;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	if (!name || !num_parents || !parent_names)
1388c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	master = kzalloc(sizeof(*master), GFP_KERNEL);
1418c2ecf20Sopenharmony_ci	if (!master)
1428c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	init.name = name;
1458c2ecf20Sopenharmony_ci	init.ops = &master_ops;
1468c2ecf20Sopenharmony_ci	init.parent_names = parent_names;
1478c2ecf20Sopenharmony_ci	init.num_parents = num_parents;
1488c2ecf20Sopenharmony_ci	init.flags = 0;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	master->hw.init = &init;
1518c2ecf20Sopenharmony_ci	master->layout = layout;
1528c2ecf20Sopenharmony_ci	master->characteristics = characteristics;
1538c2ecf20Sopenharmony_ci	master->regmap = regmap;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	hw = &master->hw;
1568c2ecf20Sopenharmony_ci	ret = clk_hw_register(NULL, &master->hw);
1578c2ecf20Sopenharmony_ci	if (ret) {
1588c2ecf20Sopenharmony_ci		kfree(master);
1598c2ecf20Sopenharmony_ci		hw = ERR_PTR(ret);
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return hw;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic unsigned long
1668c2ecf20Sopenharmony_ciclk_sama7g5_master_recalc_rate(struct clk_hw *hw,
1678c2ecf20Sopenharmony_ci			       unsigned long parent_rate)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct clk_master *master = to_clk_master(hw);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	return DIV_ROUND_CLOSEST_ULL(parent_rate, (1 << master->div));
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic void clk_sama7g5_master_best_diff(struct clk_rate_request *req,
1758c2ecf20Sopenharmony_ci					 struct clk_hw *parent,
1768c2ecf20Sopenharmony_ci					 unsigned long parent_rate,
1778c2ecf20Sopenharmony_ci					 long *best_rate,
1788c2ecf20Sopenharmony_ci					 long *best_diff,
1798c2ecf20Sopenharmony_ci					 u32 div)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	unsigned long tmp_rate, tmp_diff;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (div == MASTER_PRES_MAX)
1848c2ecf20Sopenharmony_ci		tmp_rate = parent_rate / 3;
1858c2ecf20Sopenharmony_ci	else
1868c2ecf20Sopenharmony_ci		tmp_rate = parent_rate >> div;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	tmp_diff = abs(req->rate - tmp_rate);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (*best_diff < 0 || *best_diff >= tmp_diff) {
1918c2ecf20Sopenharmony_ci		*best_rate = tmp_rate;
1928c2ecf20Sopenharmony_ci		*best_diff = tmp_diff;
1938c2ecf20Sopenharmony_ci		req->best_parent_rate = parent_rate;
1948c2ecf20Sopenharmony_ci		req->best_parent_hw = parent;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int clk_sama7g5_master_determine_rate(struct clk_hw *hw,
1998c2ecf20Sopenharmony_ci					     struct clk_rate_request *req)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct clk_master *master = to_clk_master(hw);
2028c2ecf20Sopenharmony_ci	struct clk_rate_request req_parent = *req;
2038c2ecf20Sopenharmony_ci	struct clk_hw *parent;
2048c2ecf20Sopenharmony_ci	long best_rate = LONG_MIN, best_diff = LONG_MIN;
2058c2ecf20Sopenharmony_ci	unsigned long parent_rate;
2068c2ecf20Sopenharmony_ci	unsigned int div, i;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* First: check the dividers of MCR. */
2098c2ecf20Sopenharmony_ci	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
2108c2ecf20Sopenharmony_ci		parent = clk_hw_get_parent_by_index(hw, i);
2118c2ecf20Sopenharmony_ci		if (!parent)
2128c2ecf20Sopenharmony_ci			continue;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		parent_rate = clk_hw_get_rate(parent);
2158c2ecf20Sopenharmony_ci		if (!parent_rate)
2168c2ecf20Sopenharmony_ci			continue;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci		for (div = 0; div < MASTER_PRES_MAX + 1; div++) {
2198c2ecf20Sopenharmony_ci			clk_sama7g5_master_best_diff(req, parent, parent_rate,
2208c2ecf20Sopenharmony_ci						     &best_rate, &best_diff,
2218c2ecf20Sopenharmony_ci						     div);
2228c2ecf20Sopenharmony_ci			if (!best_diff)
2238c2ecf20Sopenharmony_ci				break;
2248c2ecf20Sopenharmony_ci		}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		if (!best_diff)
2278c2ecf20Sopenharmony_ci			break;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/* Second: try to request rate form changeable parent. */
2318c2ecf20Sopenharmony_ci	if (master->chg_pid < 0)
2328c2ecf20Sopenharmony_ci		goto end;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	parent = clk_hw_get_parent_by_index(hw, master->chg_pid);
2358c2ecf20Sopenharmony_ci	if (!parent)
2368c2ecf20Sopenharmony_ci		goto end;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	for (div = 0; div < MASTER_PRES_MAX + 1; div++) {
2398c2ecf20Sopenharmony_ci		if (div == MASTER_PRES_MAX)
2408c2ecf20Sopenharmony_ci			req_parent.rate = req->rate * 3;
2418c2ecf20Sopenharmony_ci		else
2428c2ecf20Sopenharmony_ci			req_parent.rate = req->rate << div;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci		if (__clk_determine_rate(parent, &req_parent))
2458c2ecf20Sopenharmony_ci			continue;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci		clk_sama7g5_master_best_diff(req, parent, req_parent.rate,
2488c2ecf20Sopenharmony_ci					     &best_rate, &best_diff, div);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci		if (!best_diff)
2518c2ecf20Sopenharmony_ci			break;
2528c2ecf20Sopenharmony_ci	}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ciend:
2558c2ecf20Sopenharmony_ci	pr_debug("MCK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
2568c2ecf20Sopenharmony_ci		 __func__, best_rate,
2578c2ecf20Sopenharmony_ci		 __clk_get_name((req->best_parent_hw)->clk),
2588c2ecf20Sopenharmony_ci		req->best_parent_rate);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	if (best_rate < 0)
2618c2ecf20Sopenharmony_ci		return -EINVAL;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	req->rate = best_rate;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return 0;
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic u8 clk_sama7g5_master_get_parent(struct clk_hw *hw)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	struct clk_master *master = to_clk_master(hw);
2718c2ecf20Sopenharmony_ci	unsigned long flags;
2728c2ecf20Sopenharmony_ci	u8 index;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	spin_lock_irqsave(master->lock, flags);
2758c2ecf20Sopenharmony_ci	index = clk_mux_val_to_index(&master->hw, master->mux_table, 0,
2768c2ecf20Sopenharmony_ci				     master->parent);
2778c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(master->lock, flags);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	return index;
2808c2ecf20Sopenharmony_ci}
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic int clk_sama7g5_master_set_parent(struct clk_hw *hw, u8 index)
2838c2ecf20Sopenharmony_ci{
2848c2ecf20Sopenharmony_ci	struct clk_master *master = to_clk_master(hw);
2858c2ecf20Sopenharmony_ci	unsigned long flags;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	if (index >= clk_hw_get_num_parents(hw))
2888c2ecf20Sopenharmony_ci		return -EINVAL;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	spin_lock_irqsave(master->lock, flags);
2918c2ecf20Sopenharmony_ci	master->parent = clk_mux_index_to_val(master->mux_table, 0, index);
2928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(master->lock, flags);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	return 0;
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_cistatic int clk_sama7g5_master_enable(struct clk_hw *hw)
2988c2ecf20Sopenharmony_ci{
2998c2ecf20Sopenharmony_ci	struct clk_master *master = to_clk_master(hw);
3008c2ecf20Sopenharmony_ci	unsigned long flags;
3018c2ecf20Sopenharmony_ci	unsigned int val, cparent;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	spin_lock_irqsave(master->lock, flags);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	regmap_write(master->regmap, PMC_MCR, PMC_MCR_ID(master->id));
3068c2ecf20Sopenharmony_ci	regmap_read(master->regmap, PMC_MCR, &val);
3078c2ecf20Sopenharmony_ci	regmap_update_bits(master->regmap, PMC_MCR,
3088c2ecf20Sopenharmony_ci			   PMC_MCR_EN | PMC_MCR_CSS | PMC_MCR_DIV |
3098c2ecf20Sopenharmony_ci			   PMC_MCR_CMD | PMC_MCR_ID_MSK,
3108c2ecf20Sopenharmony_ci			   PMC_MCR_EN | (master->parent << PMC_MCR_CSS_SHIFT) |
3118c2ecf20Sopenharmony_ci			   (master->div << MASTER_DIV_SHIFT) |
3128c2ecf20Sopenharmony_ci			   PMC_MCR_CMD | PMC_MCR_ID(master->id));
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	cparent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* Wait here only if parent is being changed. */
3178c2ecf20Sopenharmony_ci	while ((cparent != master->parent) && !clk_master_ready(master))
3188c2ecf20Sopenharmony_ci		cpu_relax();
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(master->lock, flags);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	return 0;
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic void clk_sama7g5_master_disable(struct clk_hw *hw)
3268c2ecf20Sopenharmony_ci{
3278c2ecf20Sopenharmony_ci	struct clk_master *master = to_clk_master(hw);
3288c2ecf20Sopenharmony_ci	unsigned long flags;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	spin_lock_irqsave(master->lock, flags);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	regmap_write(master->regmap, PMC_MCR, master->id);
3338c2ecf20Sopenharmony_ci	regmap_update_bits(master->regmap, PMC_MCR,
3348c2ecf20Sopenharmony_ci			   PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
3358c2ecf20Sopenharmony_ci			   PMC_MCR_CMD | PMC_MCR_ID(master->id));
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(master->lock, flags);
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic int clk_sama7g5_master_is_enabled(struct clk_hw *hw)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct clk_master *master = to_clk_master(hw);
3438c2ecf20Sopenharmony_ci	unsigned long flags;
3448c2ecf20Sopenharmony_ci	unsigned int val;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	spin_lock_irqsave(master->lock, flags);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	regmap_write(master->regmap, PMC_MCR, master->id);
3498c2ecf20Sopenharmony_ci	regmap_read(master->regmap, PMC_MCR, &val);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(master->lock, flags);
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	return !!(val & PMC_MCR_EN);
3548c2ecf20Sopenharmony_ci}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_cistatic int clk_sama7g5_master_set_rate(struct clk_hw *hw, unsigned long rate,
3578c2ecf20Sopenharmony_ci				       unsigned long parent_rate)
3588c2ecf20Sopenharmony_ci{
3598c2ecf20Sopenharmony_ci	struct clk_master *master = to_clk_master(hw);
3608c2ecf20Sopenharmony_ci	unsigned long div, flags;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	div = DIV_ROUND_CLOSEST(parent_rate, rate);
3638c2ecf20Sopenharmony_ci	if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1)))
3648c2ecf20Sopenharmony_ci		return -EINVAL;
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	if (div == 3)
3678c2ecf20Sopenharmony_ci		div = MASTER_PRES_MAX;
3688c2ecf20Sopenharmony_ci	else
3698c2ecf20Sopenharmony_ci		div = ffs(div) - 1;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	spin_lock_irqsave(master->lock, flags);
3728c2ecf20Sopenharmony_ci	master->div = div;
3738c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(master->lock, flags);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	return 0;
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic const struct clk_ops sama7g5_master_ops = {
3798c2ecf20Sopenharmony_ci	.enable = clk_sama7g5_master_enable,
3808c2ecf20Sopenharmony_ci	.disable = clk_sama7g5_master_disable,
3818c2ecf20Sopenharmony_ci	.is_enabled = clk_sama7g5_master_is_enabled,
3828c2ecf20Sopenharmony_ci	.recalc_rate = clk_sama7g5_master_recalc_rate,
3838c2ecf20Sopenharmony_ci	.determine_rate = clk_sama7g5_master_determine_rate,
3848c2ecf20Sopenharmony_ci	.set_rate = clk_sama7g5_master_set_rate,
3858c2ecf20Sopenharmony_ci	.get_parent = clk_sama7g5_master_get_parent,
3868c2ecf20Sopenharmony_ci	.set_parent = clk_sama7g5_master_set_parent,
3878c2ecf20Sopenharmony_ci};
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistruct clk_hw * __init
3908c2ecf20Sopenharmony_ciat91_clk_sama7g5_register_master(struct regmap *regmap,
3918c2ecf20Sopenharmony_ci				 const char *name, int num_parents,
3928c2ecf20Sopenharmony_ci				 const char **parent_names,
3938c2ecf20Sopenharmony_ci				 u32 *mux_table,
3948c2ecf20Sopenharmony_ci				 spinlock_t *lock, u8 id,
3958c2ecf20Sopenharmony_ci				 bool critical, int chg_pid)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	struct clk_master *master;
3988c2ecf20Sopenharmony_ci	struct clk_hw *hw;
3998c2ecf20Sopenharmony_ci	struct clk_init_data init;
4008c2ecf20Sopenharmony_ci	unsigned long flags;
4018c2ecf20Sopenharmony_ci	unsigned int val;
4028c2ecf20Sopenharmony_ci	int ret;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	if (!name || !num_parents || !parent_names || !mux_table ||
4058c2ecf20Sopenharmony_ci	    !lock || id > MASTER_MAX_ID)
4068c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	master = kzalloc(sizeof(*master), GFP_KERNEL);
4098c2ecf20Sopenharmony_ci	if (!master)
4108c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	init.name = name;
4138c2ecf20Sopenharmony_ci	init.ops = &sama7g5_master_ops;
4148c2ecf20Sopenharmony_ci	init.parent_names = parent_names;
4158c2ecf20Sopenharmony_ci	init.num_parents = num_parents;
4168c2ecf20Sopenharmony_ci	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
4178c2ecf20Sopenharmony_ci	if (chg_pid >= 0)
4188c2ecf20Sopenharmony_ci		init.flags |= CLK_SET_RATE_PARENT;
4198c2ecf20Sopenharmony_ci	if (critical)
4208c2ecf20Sopenharmony_ci		init.flags |= CLK_IS_CRITICAL;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	master->hw.init = &init;
4238c2ecf20Sopenharmony_ci	master->regmap = regmap;
4248c2ecf20Sopenharmony_ci	master->id = id;
4258c2ecf20Sopenharmony_ci	master->chg_pid = chg_pid;
4268c2ecf20Sopenharmony_ci	master->lock = lock;
4278c2ecf20Sopenharmony_ci	master->mux_table = mux_table;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	spin_lock_irqsave(master->lock, flags);
4308c2ecf20Sopenharmony_ci	regmap_write(master->regmap, PMC_MCR, master->id);
4318c2ecf20Sopenharmony_ci	regmap_read(master->regmap, PMC_MCR, &val);
4328c2ecf20Sopenharmony_ci	master->parent = (val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT;
4338c2ecf20Sopenharmony_ci	master->div = (val & PMC_MCR_DIV) >> MASTER_DIV_SHIFT;
4348c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(master->lock, flags);
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	hw = &master->hw;
4378c2ecf20Sopenharmony_ci	ret = clk_hw_register(NULL, &master->hw);
4388c2ecf20Sopenharmony_ci	if (ret) {
4398c2ecf20Sopenharmony_ci		kfree(master);
4408c2ecf20Sopenharmony_ci		hw = ERR_PTR(ret);
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	return hw;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ciconst struct clk_master_layout at91rm9200_master_layout = {
4478c2ecf20Sopenharmony_ci	.mask = 0x31F,
4488c2ecf20Sopenharmony_ci	.pres_shift = 2,
4498c2ecf20Sopenharmony_ci	.offset = AT91_PMC_MCKR,
4508c2ecf20Sopenharmony_ci};
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ciconst struct clk_master_layout at91sam9x5_master_layout = {
4538c2ecf20Sopenharmony_ci	.mask = 0x373,
4548c2ecf20Sopenharmony_ci	.pres_shift = 4,
4558c2ecf20Sopenharmony_ci	.offset = AT91_PMC_MCKR,
4568c2ecf20Sopenharmony_ci};
457