xref: /linux/drivers/clk/meson/vclk.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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 	.enable = meson_vclk_gate_enable,
49 	.disable = meson_vclk_gate_disable,
50 	.is_enabled = meson_vclk_gate_is_enabled,
51 };
52 EXPORT_SYMBOL_NS_GPL(meson_vclk_gate_ops, CLK_MESON);
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_NS_GPL(meson_vclk_div_ops, CLK_MESON);
138 
139 MODULE_DESCRIPTION("Amlogic vclk clock driver");
140 MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>");
141 MODULE_LICENSE("GPL");
142 MODULE_IMPORT_NS(CLK_MESON);
143