xref: /linux/drivers/clk/meson/vclk.c (revision 22c55fb9eb92395d999b8404d73e58540d11bdd8)
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 
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 	.init = clk_regmap_init,
49 	.enable = meson_vclk_gate_enable,
50 	.disable = meson_vclk_gate_disable,
51 	.is_enabled = meson_vclk_gate_is_enabled,
52 };
53 EXPORT_SYMBOL_NS_GPL(meson_vclk_gate_ops, "CLK_MESON");
54 
55 /* The VCLK Divider has supplementary reset & enable bits */
56 
57 static inline struct meson_vclk_div_data *
58 clk_get_meson_vclk_div_data(struct clk_regmap *clk)
59 {
60 	return (struct meson_vclk_div_data *)clk->data;
61 }
62 
63 static unsigned long meson_vclk_div_recalc_rate(struct clk_hw *hw,
64 						unsigned long prate)
65 {
66 	struct clk_regmap *clk = to_clk_regmap(hw);
67 	struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
68 
69 	return divider_recalc_rate(hw, prate, meson_parm_read(clk->map, &vclk->div),
70 				   vclk->table, vclk->flags, vclk->div.width);
71 }
72 
73 static int meson_vclk_div_determine_rate(struct clk_hw *hw,
74 					 struct clk_rate_request *req)
75 {
76 	struct clk_regmap *clk = to_clk_regmap(hw);
77 	struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
78 
79 	return divider_determine_rate(hw, req, vclk->table, vclk->div.width,
80 				      vclk->flags);
81 }
82 
83 static int meson_vclk_div_set_rate(struct clk_hw *hw, unsigned long rate,
84 				   unsigned long parent_rate)
85 {
86 	struct clk_regmap *clk = to_clk_regmap(hw);
87 	struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
88 	int ret;
89 
90 	ret = divider_get_val(rate, parent_rate, vclk->table, vclk->div.width,
91 			      vclk->flags);
92 	if (ret < 0)
93 		return ret;
94 
95 	meson_parm_write(clk->map, &vclk->div, ret);
96 
97 	return 0;
98 };
99 
100 static int meson_vclk_div_enable(struct clk_hw *hw)
101 {
102 	struct clk_regmap *clk = to_clk_regmap(hw);
103 	struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
104 
105 	/* Unreset the divider when ungating */
106 	meson_parm_write(clk->map, &vclk->reset, 0);
107 	meson_parm_write(clk->map, &vclk->enable, 1);
108 
109 	return 0;
110 }
111 
112 static void meson_vclk_div_disable(struct clk_hw *hw)
113 {
114 	struct clk_regmap *clk = to_clk_regmap(hw);
115 	struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
116 
117 	/* Reset the divider when gating */
118 	meson_parm_write(clk->map, &vclk->enable, 0);
119 	meson_parm_write(clk->map, &vclk->reset, 1);
120 }
121 
122 static int meson_vclk_div_is_enabled(struct clk_hw *hw)
123 {
124 	struct clk_regmap *clk = to_clk_regmap(hw);
125 	struct meson_vclk_div_data *vclk = clk_get_meson_vclk_div_data(clk);
126 
127 	return meson_parm_read(clk->map, &vclk->enable);
128 }
129 
130 const struct clk_ops meson_vclk_div_ops = {
131 	.init = clk_regmap_init,
132 	.recalc_rate = meson_vclk_div_recalc_rate,
133 	.determine_rate = meson_vclk_div_determine_rate,
134 	.set_rate = meson_vclk_div_set_rate,
135 	.enable = meson_vclk_div_enable,
136 	.disable = meson_vclk_div_disable,
137 	.is_enabled = meson_vclk_div_is_enabled,
138 };
139 EXPORT_SYMBOL_NS_GPL(meson_vclk_div_ops, "CLK_MESON");
140 
141 MODULE_DESCRIPTION("Amlogic vclk clock driver");
142 MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
143 MODULE_LICENSE("GPL");
144 MODULE_IMPORT_NS("CLK_MESON");
145