Skip to content

Commit bb5aa08

Browse files
superna9999jbrun3t
authored andcommitted
clk: meson: add vclk driver
The VCLK and VCLK_DIV clocks have supplementary bits. The VCLK gate has a "SOFT RESET" bit to toggle after the whole VCLK sub-tree rate has been set, this is implemented in the gate enable callback. The VCLK_DIV clocks as enable and reset bits used to disable and reset the divider, associated with CLK_SET_RATE_GATE it ensures the rate is set while the divider is disabled and in reset mode. The VCLK_DIV enable bit isn't implemented as a gate since it's part of the divider logic and vendor does this exact sequence to ensure the divider is correctly set. Signed-off-by: Neil Armstrong <[email protected]> Link: https://lore.kernel.org/r/20240403-amlogic-v6-4-upstream-dsi-ccf-vim3-v12-2-99ecdfdc87fc@linaro.org Signed-off-by: Jerome Brunet <[email protected]>
1 parent 16182ac commit bb5aa08

File tree

4 files changed

+197
-0
lines changed

4 files changed

+197
-0
lines changed

drivers/clk/meson/Kconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ config COMMON_CLK_MESON_VID_PLL_DIV
3030
tristate
3131
select COMMON_CLK_MESON_REGMAP
3232

33+
config COMMON_CLK_MESON_VCLK
34+
tristate
35+
select COMMON_CLK_MESON_REGMAP
36+
3337
config COMMON_CLK_MESON_CLKC_UTILS
3438
tristate
3539

drivers/clk/meson/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ obj-$(CONFIG_COMMON_CLK_MESON_PLL) += clk-pll.o
1212
obj-$(CONFIG_COMMON_CLK_MESON_REGMAP) += clk-regmap.o
1313
obj-$(CONFIG_COMMON_CLK_MESON_SCLK_DIV) += sclk-div.o
1414
obj-$(CONFIG_COMMON_CLK_MESON_VID_PLL_DIV) += vid-pll-div.o
15+
obj-$(CONFIG_COMMON_CLK_MESON_VCLK) += vclk.o
1516

1617
# Amlogic Clock controllers
1718

drivers/clk/meson/vclk.c

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (c) 2024 Neil Armstrong <[email protected]>
4+
*/
5+
6+
#include <linux/module.h>
7+
#include "vclk.h"
8+
9+
/* The VCLK gate has a supplementary reset bit to pulse after ungating */
10+
11+
static inline struct meson_vclk_gate_data *
12+
clk_get_meson_vclk_gate_data(struct clk_regmap *clk)
13+
{
14+
return (struct meson_vclk_gate_data *)clk->data;
15+
}
16+
17+
static int meson_vclk_gate_enable(struct clk_hw *hw)
18+
{
19+
struct clk_regmap *clk = to_clk_regmap(hw);
20+
struct meson_vclk_gate_data *vclk = clk_get_meson_vclk_gate_data(clk);
21+
22+
meson_parm_write(clk->map, &vclk->enable, 1);
23+
24+
/* Do a reset pulse */
25+
meson_parm_write(clk->map, &vclk->reset, 1);
26+
meson_parm_write(clk->map, &vclk->reset, 0);
27+
28+
return 0;
29+
}
30+
31+
static void meson_vclk_gate_disable(struct clk_hw *hw)
32+
{
33+
struct clk_regmap *clk = to_clk_regmap(hw);
34+
struct meson_vclk_gate_data *vclk = clk_get_meson_vclk_gate_data(clk);
35+
36+
meson_parm_write(clk->map, &vclk->enable, 0);
37+
}
38+
39+
static int meson_vclk_gate_is_enabled(struct clk_hw *hw)
40+
{
41+
struct clk_regmap *clk = to_clk_regmap(hw);
42+
struct meson_vclk_gate_data *vclk = clk_get_meson_vclk_gate_data(clk);
43+
44+
return meson_parm_read(clk->map, &vclk->enable);
45+
}
46+
47+
const struct clk_ops meson_vclk_gate_ops = {
48+
.enable = meson_vclk_gate_enable,
49+
.disable = meson_vclk_gate_disable,
50+
.is_enabled = meson_vclk_gate_is_enabled,
51+
};
52+
EXPORT_SYMBOL_GPL(meson_vclk_gate_ops);
53+
54+
/* The VCLK Divider has supplementary reset & enable bits */
55+
56+
static inline struct meson_vclk_div_data *
57+
clk_get_meson_vclk_div_data(struct clk_regmap *clk)
58+
{
59+
return (struct meson_vclk_div_data *)clk->data;
60+
}
61+
62+
static unsigned long meson_vclk_div_recalc_rate(struct clk_hw *hw,
63+
unsigned long prate)
64+
{
65+
struct clk_regmap *clk = to_clk_regmap(hw);
66+
struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
67+
68+
return divider_recalc_rate(hw, prate, meson_parm_read(clk->map, &vclk->div),
69+
vclk->table, vclk->flags, vclk->div.width);
70+
}
71+
72+
static int meson_vclk_div_determine_rate(struct clk_hw *hw,
73+
struct clk_rate_request *req)
74+
{
75+
struct clk_regmap *clk = to_clk_regmap(hw);
76+
struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
77+
78+
return divider_determine_rate(hw, req, vclk->table, vclk->div.width,
79+
vclk->flags);
80+
}
81+
82+
static int meson_vclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
83+
unsigned long parent_rate)
84+
{
85+
struct clk_regmap *clk = to_clk_regmap(hw);
86+
struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
87+
int ret;
88+
89+
ret = divider_get_val(rate, parent_rate, vclk->table, vclk->div.width,
90+
vclk->flags);
91+
if (ret < 0)
92+
return ret;
93+
94+
meson_parm_write(clk->map, &vclk->div, ret);
95+
96+
return 0;
97+
};
98+
99+
static int meson_vclk_div_enable(struct clk_hw *hw)
100+
{
101+
struct clk_regmap *clk = to_clk_regmap(hw);
102+
struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
103+
104+
/* Unreset the divider when ungating */
105+
meson_parm_write(clk->map, &vclk->reset, 0);
106+
meson_parm_write(clk->map, &vclk->enable, 1);
107+
108+
return 0;
109+
}
110+
111+
static void meson_vclk_div_disable(struct clk_hw *hw)
112+
{
113+
struct clk_regmap *clk = to_clk_regmap(hw);
114+
struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
115+
116+
/* Reset the divider when gating */
117+
meson_parm_write(clk->map, &vclk->enable, 0);
118+
meson_parm_write(clk->map, &vclk->reset, 1);
119+
}
120+
121+
static int meson_vclk_div_is_enabled(struct clk_hw *hw)
122+
{
123+
struct clk_regmap *clk = to_clk_regmap(hw);
124+
struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
125+
126+
return meson_parm_read(clk->map, &vclk->enable);
127+
}
128+
129+
const struct clk_ops meson_vclk_div_ops = {
130+
.recalc_rate = meson_vclk_div_recalc_rate,
131+
.determine_rate = meson_vclk_div_determine_rate,
132+
.set_rate = meson_vclk_div_set_rate,
133+
.enable = meson_vclk_div_enable,
134+
.disable = meson_vclk_div_disable,
135+
.is_enabled = meson_vclk_div_is_enabled,
136+
};
137+
EXPORT_SYMBOL_GPL(meson_vclk_div_ops);
138+
139+
MODULE_DESCRIPTION("Amlogic vclk clock driver");
140+
MODULE_AUTHOR("Neil Armstrong <[email protected]>");
141+
MODULE_LICENSE("GPL v2");

drivers/clk/meson/vclk.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Copyright (c) 2024 Neil Armstrong <[email protected]>
4+
*/
5+
6+
#ifndef __VCLK_H
7+
#define __VCLK_H
8+
9+
#include "clk-regmap.h"
10+
#include "parm.h"
11+
12+
/**
13+
* struct meson_vclk_gate_data - vclk_gate regmap backed specific data
14+
*
15+
* @enable: vclk enable field
16+
* @reset: vclk reset field
17+
* @flags: hardware-specific flags
18+
*
19+
* Flags:
20+
* Same as clk_gate except CLK_GATE_HIWORD_MASK which is ignored
21+
*/
22+
struct meson_vclk_gate_data {
23+
struct parm enable;
24+
struct parm reset;
25+
u8 flags;
26+
};
27+
28+
extern const struct clk_ops meson_vclk_gate_ops;
29+
30+
/**
31+
* struct meson_vclk_div_data - vclk_div regmap back specific data
32+
*
33+
* @div: divider field
34+
* @enable: vclk divider enable field
35+
* @reset: vclk divider reset field
36+
* @table: array of value/divider pairs, last entry should have div = 0
37+
*
38+
* Flags:
39+
* Same as clk_divider except CLK_DIVIDER_HIWORD_MASK which is ignored
40+
*/
41+
struct meson_vclk_div_data {
42+
struct parm div;
43+
struct parm enable;
44+
struct parm reset;
45+
const struct clk_div_table *table;
46+
u8 flags;
47+
};
48+
49+
extern const struct clk_ops meson_vclk_div_ops;
50+
51+
#endif /* __VCLK_H */

0 commit comments

Comments
 (0)