Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/clk/actions/owl-pll.c
26282 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 long owl_pll_round_rate(struct clk_hw *hw, unsigned long rate,
60
unsigned long *parent_rate)
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, rate);
69
return clkt->rate;
70
}
71
72
/* fixed frequency */
73
if (pll_hw->width == 0)
74
return pll_hw->bfreq;
75
76
mul = owl_pll_calculate_mul(pll_hw, rate);
77
78
return pll_hw->bfreq * mul;
79
}
80
81
static unsigned long owl_pll_recalc_rate(struct clk_hw *hw,
82
unsigned long parent_rate)
83
{
84
struct owl_pll *pll = hw_to_owl_pll(hw);
85
struct owl_pll_hw *pll_hw = &pll->pll_hw;
86
const struct owl_clk_common *common = &pll->common;
87
u32 val;
88
89
if (pll_hw->table) {
90
regmap_read(common->regmap, pll_hw->reg, &val);
91
92
val = val >> pll_hw->shift;
93
val &= mul_mask(pll_hw);
94
95
return _get_table_rate(pll_hw->table, val);
96
}
97
98
/* fixed frequency */
99
if (pll_hw->width == 0)
100
return pll_hw->bfreq;
101
102
regmap_read(common->regmap, pll_hw->reg, &val);
103
104
val = val >> pll_hw->shift;
105
val &= mul_mask(pll_hw);
106
107
return pll_hw->bfreq * val;
108
}
109
110
static int owl_pll_is_enabled(struct clk_hw *hw)
111
{
112
struct owl_pll *pll = hw_to_owl_pll(hw);
113
struct owl_pll_hw *pll_hw = &pll->pll_hw;
114
const struct owl_clk_common *common = &pll->common;
115
u32 reg;
116
117
regmap_read(common->regmap, pll_hw->reg, &reg);
118
119
return !!(reg & BIT(pll_hw->bit_idx));
120
}
121
122
static void owl_pll_set(const struct owl_clk_common *common,
123
const struct owl_pll_hw *pll_hw, bool enable)
124
{
125
u32 reg;
126
127
regmap_read(common->regmap, pll_hw->reg, &reg);
128
129
if (enable)
130
reg |= BIT(pll_hw->bit_idx);
131
else
132
reg &= ~BIT(pll_hw->bit_idx);
133
134
regmap_write(common->regmap, pll_hw->reg, reg);
135
}
136
137
static int owl_pll_enable(struct clk_hw *hw)
138
{
139
struct owl_pll *pll = hw_to_owl_pll(hw);
140
const struct owl_clk_common *common = &pll->common;
141
142
owl_pll_set(common, &pll->pll_hw, true);
143
144
return 0;
145
}
146
147
static void owl_pll_disable(struct clk_hw *hw)
148
{
149
struct owl_pll *pll = hw_to_owl_pll(hw);
150
const struct owl_clk_common *common = &pll->common;
151
152
owl_pll_set(common, &pll->pll_hw, false);
153
}
154
155
static int owl_pll_set_rate(struct clk_hw *hw, unsigned long rate,
156
unsigned long parent_rate)
157
{
158
struct owl_pll *pll = hw_to_owl_pll(hw);
159
struct owl_pll_hw *pll_hw = &pll->pll_hw;
160
const struct owl_clk_common *common = &pll->common;
161
const struct clk_pll_table *clkt;
162
u32 val, reg;
163
164
/* fixed frequency */
165
if (pll_hw->width == 0)
166
return 0;
167
168
if (pll_hw->table) {
169
clkt = _get_pll_table(pll_hw->table, rate);
170
val = clkt->val;
171
} else {
172
val = owl_pll_calculate_mul(pll_hw, rate);
173
}
174
175
regmap_read(common->regmap, pll_hw->reg, &reg);
176
177
reg &= ~mul_mask(pll_hw);
178
reg |= val << pll_hw->shift;
179
180
regmap_write(common->regmap, pll_hw->reg, reg);
181
182
udelay(pll_hw->delay);
183
184
return 0;
185
}
186
187
const struct clk_ops owl_pll_ops = {
188
.enable = owl_pll_enable,
189
.disable = owl_pll_disable,
190
.is_enabled = owl_pll_is_enabled,
191
.round_rate = owl_pll_round_rate,
192
.recalc_rate = owl_pll_recalc_rate,
193
.set_rate = owl_pll_set_rate,
194
};
195
196