Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/clk/at91/clk-plldiv.c
48889 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Copyright (C) 2013 Boris BREZILLON <[email protected]>
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
17
struct clk_plldiv {
18
struct clk_hw hw;
19
struct regmap *regmap;
20
};
21
22
static 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
36
static 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
65
static 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
79
static 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
85
struct clk_hw * __init
86
at91_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
}
116
117