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