1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2024 Neil Armstrong <neil.armstrong@linaro.org>
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
11static inline struct meson_vclk_gate_data *
12clk_get_meson_vclk_gate_data(struct clk_regmap *clk)
13{
14	return (struct meson_vclk_gate_data *)clk->data;
15}
16
17static 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
31static 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
39static 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
47const 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};
52EXPORT_SYMBOL_GPL(meson_vclk_gate_ops);
53
54/* The VCLK Divider has supplementary reset & enable bits */
55
56static inline struct meson_vclk_div_data *
57clk_get_meson_vclk_div_data(struct clk_regmap *clk)
58{
59	return (struct meson_vclk_div_data *)clk->data;
60}
61
62static 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
72static 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
82static 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
99static 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
111static 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
121static 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
129const 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};
137EXPORT_SYMBOL_GPL(meson_vclk_div_ops);
138
139MODULE_DESCRIPTION("Amlogic vclk clock driver");
140MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
141MODULE_LICENSE("GPL");
142