Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/clk/actions/owl-pll.c
50712 views
1
// SPDX-License-Identifier: GPL-2.0+
2
//
3
// OWL pll clock driver
4
//
5
// Copyright (c) 2014 Actions Semi Inc.
6
// Author: David Liu <[email protected]>
7
//
8
// Copyright (c) 2018 Linaro Ltd.
9
// Author: Manivannan Sadhasivam <[email protected]>
10
11
#include <linux/clk-provider.h>
12
#include <linux/slab.h>
13
#include <linux/io.h>
14
#include <linux/delay.h>
15
16
#include "owl-pll.h"
17
18
static u32 owl_pll_calculate_mul(struct owl_pll_hw *pll_hw, unsigned long rate)
19
{
20
u32 mul;
21
22
mul = DIV_ROUND_CLOSEST(rate, pll_hw->bfreq);
23
if (mul < pll_hw->min_mul)
24
mul = pll_hw->min_mul;
25
else if (mul > pll_hw->max_mul)
26
mul = pll_hw->max_mul;
27
28
return mul & mul_mask(pll_hw);
29
}
30
31
static unsigned long _get_table_rate(const struct clk_pll_table *table,
32
unsigned int val)
33
{
34
const struct clk_pll_table *clkt;
35
36
for (clkt = table; clkt->rate; clkt++)
37
if (clkt->val == val)
38
return clkt->rate;
39
40
return 0;
41
}
42
43
static const struct clk_pll_table *_get_pll_table(
44
const struct clk_pll_table *table, unsigned long rate)
45
{
46
const struct clk_pll_table *clkt;
47
48
for (clkt = table; clkt->rate; clkt++) {
49
if (clkt->rate == rate) {
50
table = clkt;
51
break;
52
} else if (clkt->rate < rate)
53
table = clkt;
54
}
55
56
return table;
57
}
58
59
static int owl_pll_determine_rate(struct clk_hw *hw,
60
struct clk_rate_request *req)
61
{
62
struct owl_pll *pll = hw_to_owl_pll(hw);
63
struct owl_pll_hw *pll_hw = &pll->pll_hw;
64
const struct clk_pll_table *clkt;
65
u32 mul;
66
67
if (pll_hw->table) {
68
clkt = _get_pll_table(pll_hw->table, req->rate);
69
req->rate = clkt->rate;
70
71
return 0;
72
}
73
74
/* fixed frequency */
75
if (pll_hw->width == 0) {
76
req->rate = pll_hw->bfreq;
77
78
return 0;
79
}
80
81
mul = owl_pll_calculate_mul(pll_hw, req->rate);
82
83
req->rate = pll_hw->bfreq * mul;
84
85
return 0;
86
}
87
88
static unsigned long owl_pll_recalc_rate(struct clk_hw *hw,
89
unsigned long parent_rate)
90
{
91
struct owl_pll *pll = hw_to_owl_pll(hw);
92
struct owl_pll_hw *pll_hw = &pll->pll_hw;
93
const struct owl_clk_common *common = &pll->common;
94
u32 val;
95
96
if (pll_hw->table) {
97
regmap_read(common->regmap, pll_hw->reg, &val);
98
99
val = val >> pll_hw->shift;
100
val &= mul_mask(pll_hw);
101
102
return _get_table_rate(pll_hw->table, val);
103
}
104
105
/* fixed frequency */
106
if (pll_hw->width == 0)
107
return pll_hw->bfreq;
108
109
regmap_read(common->regmap, pll_hw->reg, &val);
110
111
val = val >> pll_hw->shift;
112
val &= mul_mask(pll_hw);
113
114
return pll_hw->bfreq * val;
115
}
116
117
static int owl_pll_is_enabled(struct clk_hw *hw)
118
{
119
struct owl_pll *pll = hw_to_owl_pll(hw);
120
struct owl_pll_hw *pll_hw = &pll->pll_hw;
121
const struct owl_clk_common *common = &pll->common;
122
u32 reg;
123
124
regmap_read(common->regmap, pll_hw->reg, &reg);
125
126
return !!(reg & BIT(pll_hw->bit_idx));
127
}
128
129
static void owl_pll_set(const struct owl_clk_common *common,
130
const struct owl_pll_hw *pll_hw, bool enable)
131
{
132
u32 reg;
133
134
regmap_read(common->regmap, pll_hw->reg, &reg);
135
136
if (enable)
137
reg |= BIT(pll_hw->bit_idx);
138
else
139
reg &= ~BIT(pll_hw->bit_idx);
140
141
regmap_write(common->regmap, pll_hw->reg, reg);
142
}
143
144
static int owl_pll_enable(struct clk_hw *hw)
145
{
146
struct owl_pll *pll = hw_to_owl_pll(hw);
147
const struct owl_clk_common *common = &pll->common;
148
149
owl_pll_set(common, &pll->pll_hw, true);
150
151
return 0;
152
}
153
154
static void owl_pll_disable(struct clk_hw *hw)
155
{
156
struct owl_pll *pll = hw_to_owl_pll(hw);
157
const struct owl_clk_common *common = &pll->common;
158
159
owl_pll_set(common, &pll->pll_hw, false);
160
}
161
162
static int owl_pll_set_rate(struct clk_hw *hw, unsigned long rate,
163
unsigned long parent_rate)
164
{
165
struct owl_pll *pll = hw_to_owl_pll(hw);
166
struct owl_pll_hw *pll_hw = &pll->pll_hw;
167
const struct owl_clk_common *common = &pll->common;
168
const struct clk_pll_table *clkt;
169
u32 val, reg;
170
171
/* fixed frequency */
172
if (pll_hw->width == 0)
173
return 0;
174
175
if (pll_hw->table) {
176
clkt = _get_pll_table(pll_hw->table, rate);
177
val = clkt->val;
178
} else {
179
val = owl_pll_calculate_mul(pll_hw, rate);
180
}
181
182
regmap_read(common->regmap, pll_hw->reg, &reg);
183
184
reg &= ~mul_mask(pll_hw);
185
reg |= val << pll_hw->shift;
186
187
regmap_write(common->regmap, pll_hw->reg, reg);
188
189
udelay(pll_hw->delay);
190
191
return 0;
192
}
193
194
const struct clk_ops owl_pll_ops = {
195
.enable = owl_pll_enable,
196
.disable = owl_pll_disable,
197
.is_enabled = owl_pll_is_enabled,
198
.determine_rate = owl_pll_determine_rate,
199
.recalc_rate = owl_pll_recalc_rate,
200
.set_rate = owl_pll_set_rate,
201
};
202
203