File Index
Files and directories
Sign in
FILE

clk-plldiv.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/at91/clk-plldiv.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
  4 */
  5
  6#include <linux/clk-provider.h>
  7#include <linux/clkdev.h>
  8#include <linux/clk/at91_pmc.h>
  9#include <linux/of.h>
 10#include <linux/mfd/syscon.h>
 11#include <linux/regmap.h>
 12
 13#include "pmc.h"
 14
 15#define to_clk_plldiv(hw) container_of(hw, struct clk_plldiv, hw)
 16
 17struct clk_plldiv {
 18	struct clk_hw hw;
 19	struct regmap *regmap;
 20};
 21
 22static unsigned long clk_plldiv_recalc_rate(struct clk_hw *hw,
 23					    unsigned long parent_rate)
 24{
 25	struct clk_plldiv *plldiv = to_clk_plldiv(hw);
 26	unsigned int mckr;
 27
 28	regmap_read(plldiv->regmap, AT91_PMC_MCKR, &mckr);
 29
 30	if (mckr & AT91_PMC_PLLADIV2)
 31		return parent_rate / 2;
 32
 33	return parent_rate;
 34}
 35
 36static int clk_plldiv_determine_rate(struct clk_hw *hw,
 37				     struct clk_rate_request *req)
 38{
 39	unsigned long div;
 40
 41	if (req->rate > req->best_parent_rate) {
 42		req->rate = req->best_parent_rate;
 43
 44		return 0;
 45	}
 46
 47	div = req->best_parent_rate / 2;
 48	if (req->rate < div) {
 49		req->rate = div;
 50
 51		return 0;
 52	}
 53
 54	if (req->rate - div < req->best_parent_rate - req->rate) {
 55		req->rate = div;
 56
 57		return 0;
 58	}
 59
 60	req->rate = req->best_parent_rate;
 61
 62	return 0;
 63}
 64
 65static int clk_plldiv_set_rate(struct clk_hw *hw, unsigned long rate,
 66			       unsigned long parent_rate)
 67{
 68	struct clk_plldiv *plldiv = to_clk_plldiv(hw);
 69
 70	if ((parent_rate != rate) && (parent_rate / 2 != rate))
 71		return -EINVAL;
 72
 73	regmap_update_bits(plldiv->regmap, AT91_PMC_MCKR, AT91_PMC_PLLADIV2,
 74			   parent_rate != rate ? AT91_PMC_PLLADIV2 : 0);
 75
 76	return 0;
 77}
 78
 79static const struct clk_ops plldiv_ops = {
 80	.recalc_rate = clk_plldiv_recalc_rate,
 81	.determine_rate = clk_plldiv_determine_rate,
 82	.set_rate = clk_plldiv_set_rate,
 83};
 84
 85struct clk_hw * __init
 86at91_clk_register_plldiv(struct regmap *regmap, const char *name,
 87			 const char *parent_name)
 88{
 89	struct clk_plldiv *plldiv;
 90	struct clk_hw *hw;
 91	struct clk_init_data init;
 92	int ret;
 93
 94	plldiv = kzalloc(sizeof(*plldiv), GFP_KERNEL);
 95	if (!plldiv)
 96		return ERR_PTR(-ENOMEM);
 97
 98	init.name = name;
 99	init.ops = &plldiv_ops;
100	init.parent_names = parent_name ? &parent_name : NULL;
101	init.num_parents = parent_name ? 1 : 0;
102	init.flags = CLK_SET_RATE_GATE;
103
104	plldiv->hw.init = &init;
105	plldiv->regmap = regmap;
106
107	hw = &plldiv->hw;
108	ret = clk_hw_register(NULL, &plldiv->hw);
109	if (ret) {
110		kfree(plldiv);
111		hw = ERR_PTR(ret);
112	}
113
114	return hw;
115}
~/ Read-only file preview.