xref: /linux/drivers/clk/sunxi-ng/ccu_div.c (revision 1fd1dc41724319406b0aff221a352a400b0ddfc5)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2016 Maxime Ripard
4  * Maxime Ripard <maxime.ripard@free-electrons.com>
5  */
6 
7 #include <linux/clk-provider.h>
8 #include <linux/io.h>
9 
10 #include "ccu_gate.h"
11 #include "ccu_div.h"
12 
13 static int ccu_div_determine_rate_helper(struct ccu_mux_internal *mux,
14 					 struct clk_rate_request *req,
15 					 void *data)
16 {
17 	struct ccu_div *cd = data;
18 	int ret;
19 
20 	if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
21 		req->rate *= cd->fixed_post_div;
22 
23 	ret = divider_determine_rate(&cd->common.hw, req, cd->div.table,
24 				     cd->div.width, cd->div.flags);
25 	if (ret)
26 		return ret;
27 
28 	if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
29 		req->rate /= cd->fixed_post_div;
30 
31 	return 0;
32 }
33 
34 static void ccu_div_disable(struct clk_hw *hw)
35 {
36 	struct ccu_div *cd = hw_to_ccu_div(hw);
37 
38 	return ccu_gate_helper_disable(&cd->common, cd->enable);
39 }
40 
41 static int ccu_div_enable(struct clk_hw *hw)
42 {
43 	struct ccu_div *cd = hw_to_ccu_div(hw);
44 
45 	return ccu_gate_helper_enable(&cd->common, cd->enable);
46 }
47 
48 static int ccu_div_is_enabled(struct clk_hw *hw)
49 {
50 	struct ccu_div *cd = hw_to_ccu_div(hw);
51 
52 	return ccu_gate_helper_is_enabled(&cd->common, cd->enable);
53 }
54 
55 static unsigned long ccu_div_recalc_rate(struct clk_hw *hw,
56 					unsigned long parent_rate)
57 {
58 	struct ccu_div *cd = hw_to_ccu_div(hw);
59 	unsigned long val;
60 	u32 reg;
61 
62 	reg = readl(cd->common.base + cd->common.reg);
63 	val = reg >> cd->div.shift;
64 	val &= (1 << cd->div.width) - 1;
65 
66 	parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
67 						  parent_rate);
68 
69 	val = divider_recalc_rate(hw, parent_rate, val, cd->div.table,
70 				  cd->div.flags, cd->div.width);
71 
72 	if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
73 		val /= cd->fixed_post_div;
74 
75 	return val;
76 }
77 
78 static int ccu_div_determine_rate(struct clk_hw *hw,
79 				struct clk_rate_request *req)
80 {
81 	struct ccu_div *cd = hw_to_ccu_div(hw);
82 
83 	return ccu_mux_helper_determine_rate(&cd->common, &cd->mux,
84 					     req, ccu_div_determine_rate_helper, cd);
85 }
86 
87 static int ccu_div_set_rate(struct clk_hw *hw, unsigned long rate,
88 			   unsigned long parent_rate)
89 {
90 	struct ccu_div *cd = hw_to_ccu_div(hw);
91 	unsigned long flags;
92 	unsigned long val;
93 	u32 reg;
94 
95 	parent_rate = ccu_mux_helper_apply_prediv(&cd->common, &cd->mux, -1,
96 						  parent_rate);
97 
98 	if (cd->common.features & CCU_FEATURE_FIXED_POSTDIV)
99 		rate *= cd->fixed_post_div;
100 
101 	val = divider_get_val(rate, parent_rate, cd->div.table, cd->div.width,
102 			      cd->div.flags);
103 
104 	spin_lock_irqsave(cd->common.lock, flags);
105 
106 	reg = readl(cd->common.base + cd->common.reg);
107 	reg &= ~GENMASK(cd->div.width + cd->div.shift - 1, cd->div.shift);
108 	if (cd->common.features & CCU_FEATURE_UPDATE_BIT)
109 		reg |= CCU_SUNXI_UPDATE_BIT;
110 
111 	writel(reg | (val << cd->div.shift),
112 	       cd->common.base + cd->common.reg);
113 
114 	spin_unlock_irqrestore(cd->common.lock, flags);
115 
116 	return 0;
117 }
118 
119 static u8 ccu_div_get_parent(struct clk_hw *hw)
120 {
121 	struct ccu_div *cd = hw_to_ccu_div(hw);
122 
123 	return ccu_mux_helper_get_parent(&cd->common, &cd->mux);
124 }
125 
126 static int ccu_div_set_parent(struct clk_hw *hw, u8 index)
127 {
128 	struct ccu_div *cd = hw_to_ccu_div(hw);
129 
130 	return ccu_mux_helper_set_parent(&cd->common, &cd->mux, index);
131 }
132 
133 const struct clk_ops ccu_div_ops = {
134 	.disable	= ccu_div_disable,
135 	.enable		= ccu_div_enable,
136 	.is_enabled	= ccu_div_is_enabled,
137 
138 	.get_parent	= ccu_div_get_parent,
139 	.set_parent	= ccu_div_set_parent,
140 
141 	.determine_rate	= ccu_div_determine_rate,
142 	.recalc_rate	= ccu_div_recalc_rate,
143 	.set_rate	= ccu_div_set_rate,
144 };
145 EXPORT_SYMBOL_NS_GPL(ccu_div_ops, "SUNXI_CCU");
146