xref: /OK3568_Linux_fs/kernel/drivers/clk/imx/clk-divider-gate.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright 2018 NXP.
4*4882a593Smuzhiyun  *   Dong Aisheng <aisheng.dong@nxp.com>
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun #include <linux/clk-provider.h>
8*4882a593Smuzhiyun #include <linux/err.h>
9*4882a593Smuzhiyun #include <linux/io.h>
10*4882a593Smuzhiyun #include <linux/slab.h>
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include "clk.h"
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun struct clk_divider_gate {
15*4882a593Smuzhiyun 	struct clk_divider divider;
16*4882a593Smuzhiyun 	u32 cached_val;
17*4882a593Smuzhiyun };
18*4882a593Smuzhiyun 
to_clk_divider_gate(struct clk_hw * hw)19*4882a593Smuzhiyun static inline struct clk_divider_gate *to_clk_divider_gate(struct clk_hw *hw)
20*4882a593Smuzhiyun {
21*4882a593Smuzhiyun 	struct clk_divider *div = to_clk_divider(hw);
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun 	return container_of(div, struct clk_divider_gate, divider);
24*4882a593Smuzhiyun }
25*4882a593Smuzhiyun 
clk_divider_gate_recalc_rate_ro(struct clk_hw * hw,unsigned long parent_rate)26*4882a593Smuzhiyun static unsigned long clk_divider_gate_recalc_rate_ro(struct clk_hw *hw,
27*4882a593Smuzhiyun 						     unsigned long parent_rate)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun 	struct clk_divider *div = to_clk_divider(hw);
30*4882a593Smuzhiyun 	unsigned int val;
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun 	val = readl(div->reg) >> div->shift;
33*4882a593Smuzhiyun 	val &= clk_div_mask(div->width);
34*4882a593Smuzhiyun 	if (!val)
35*4882a593Smuzhiyun 		return 0;
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun 	return divider_recalc_rate(hw, parent_rate, val, div->table,
38*4882a593Smuzhiyun 				   div->flags, div->width);
39*4882a593Smuzhiyun }
40*4882a593Smuzhiyun 
clk_divider_gate_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)41*4882a593Smuzhiyun static unsigned long clk_divider_gate_recalc_rate(struct clk_hw *hw,
42*4882a593Smuzhiyun 						  unsigned long parent_rate)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun 	struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
45*4882a593Smuzhiyun 	struct clk_divider *div = to_clk_divider(hw);
46*4882a593Smuzhiyun 	unsigned long flags;
47*4882a593Smuzhiyun 	unsigned int val;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	spin_lock_irqsave(div->lock, flags);
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	if (!clk_hw_is_enabled(hw)) {
52*4882a593Smuzhiyun 		val = div_gate->cached_val;
53*4882a593Smuzhiyun 	} else {
54*4882a593Smuzhiyun 		val = readl(div->reg) >> div->shift;
55*4882a593Smuzhiyun 		val &= clk_div_mask(div->width);
56*4882a593Smuzhiyun 	}
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	spin_unlock_irqrestore(div->lock, flags);
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	if (!val)
61*4882a593Smuzhiyun 		return 0;
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 	return divider_recalc_rate(hw, parent_rate, val, div->table,
64*4882a593Smuzhiyun 				   div->flags, div->width);
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun 
clk_divider_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * prate)67*4882a593Smuzhiyun static long clk_divider_round_rate(struct clk_hw *hw, unsigned long rate,
68*4882a593Smuzhiyun 				   unsigned long *prate)
69*4882a593Smuzhiyun {
70*4882a593Smuzhiyun 	return clk_divider_ops.round_rate(hw, rate, prate);
71*4882a593Smuzhiyun }
72*4882a593Smuzhiyun 
clk_divider_gate_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)73*4882a593Smuzhiyun static int clk_divider_gate_set_rate(struct clk_hw *hw, unsigned long rate,
74*4882a593Smuzhiyun 				unsigned long parent_rate)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun 	struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
77*4882a593Smuzhiyun 	struct clk_divider *div = to_clk_divider(hw);
78*4882a593Smuzhiyun 	unsigned long flags;
79*4882a593Smuzhiyun 	int value;
80*4882a593Smuzhiyun 	u32 val;
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	value = divider_get_val(rate, parent_rate, div->table,
83*4882a593Smuzhiyun 				div->width, div->flags);
84*4882a593Smuzhiyun 	if (value < 0)
85*4882a593Smuzhiyun 		return value;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	spin_lock_irqsave(div->lock, flags);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 	if (clk_hw_is_enabled(hw)) {
90*4882a593Smuzhiyun 		val = readl(div->reg);
91*4882a593Smuzhiyun 		val &= ~(clk_div_mask(div->width) << div->shift);
92*4882a593Smuzhiyun 		val |= (u32)value << div->shift;
93*4882a593Smuzhiyun 		writel(val, div->reg);
94*4882a593Smuzhiyun 	} else {
95*4882a593Smuzhiyun 		div_gate->cached_val = value;
96*4882a593Smuzhiyun 	}
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	spin_unlock_irqrestore(div->lock, flags);
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	return 0;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun 
clk_divider_enable(struct clk_hw * hw)103*4882a593Smuzhiyun static int clk_divider_enable(struct clk_hw *hw)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun 	struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
106*4882a593Smuzhiyun 	struct clk_divider *div = to_clk_divider(hw);
107*4882a593Smuzhiyun 	unsigned long flags;
108*4882a593Smuzhiyun 	u32 val;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	if (!div_gate->cached_val) {
111*4882a593Smuzhiyun 		pr_err("%s: no valid preset rate\n", clk_hw_get_name(hw));
112*4882a593Smuzhiyun 		return -EINVAL;
113*4882a593Smuzhiyun 	}
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun 	spin_lock_irqsave(div->lock, flags);
116*4882a593Smuzhiyun 	/* restore div val */
117*4882a593Smuzhiyun 	val = readl(div->reg);
118*4882a593Smuzhiyun 	val |= div_gate->cached_val << div->shift;
119*4882a593Smuzhiyun 	writel(val, div->reg);
120*4882a593Smuzhiyun 
121*4882a593Smuzhiyun 	spin_unlock_irqrestore(div->lock, flags);
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	return 0;
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun 
clk_divider_disable(struct clk_hw * hw)126*4882a593Smuzhiyun static void clk_divider_disable(struct clk_hw *hw)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun 	struct clk_divider_gate *div_gate = to_clk_divider_gate(hw);
129*4882a593Smuzhiyun 	struct clk_divider *div = to_clk_divider(hw);
130*4882a593Smuzhiyun 	unsigned long flags;
131*4882a593Smuzhiyun 	u32 val;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	spin_lock_irqsave(div->lock, flags);
134*4882a593Smuzhiyun 
135*4882a593Smuzhiyun 	/* store the current div val */
136*4882a593Smuzhiyun 	val = readl(div->reg) >> div->shift;
137*4882a593Smuzhiyun 	val &= clk_div_mask(div->width);
138*4882a593Smuzhiyun 	div_gate->cached_val = val;
139*4882a593Smuzhiyun 	writel(0, div->reg);
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	spin_unlock_irqrestore(div->lock, flags);
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun 
clk_divider_is_enabled(struct clk_hw * hw)144*4882a593Smuzhiyun static int clk_divider_is_enabled(struct clk_hw *hw)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	struct clk_divider *div = to_clk_divider(hw);
147*4882a593Smuzhiyun 	u32 val;
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	val = readl(div->reg) >> div->shift;
150*4882a593Smuzhiyun 	val &= clk_div_mask(div->width);
151*4882a593Smuzhiyun 
152*4882a593Smuzhiyun 	return val ? 1 : 0;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun static const struct clk_ops clk_divider_gate_ro_ops = {
156*4882a593Smuzhiyun 	.recalc_rate = clk_divider_gate_recalc_rate_ro,
157*4882a593Smuzhiyun 	.round_rate = clk_divider_round_rate,
158*4882a593Smuzhiyun };
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun static const struct clk_ops clk_divider_gate_ops = {
161*4882a593Smuzhiyun 	.recalc_rate = clk_divider_gate_recalc_rate,
162*4882a593Smuzhiyun 	.round_rate = clk_divider_round_rate,
163*4882a593Smuzhiyun 	.set_rate = clk_divider_gate_set_rate,
164*4882a593Smuzhiyun 	.enable = clk_divider_enable,
165*4882a593Smuzhiyun 	.disable = clk_divider_disable,
166*4882a593Smuzhiyun 	.is_enabled = clk_divider_is_enabled,
167*4882a593Smuzhiyun };
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun /*
170*4882a593Smuzhiyun  * NOTE: In order to reuse the most code from the common divider,
171*4882a593Smuzhiyun  * we also design our divider following the way that provids an extra
172*4882a593Smuzhiyun  * clk_divider_flags, however it's fixed to CLK_DIVIDER_ONE_BASED by
173*4882a593Smuzhiyun  * default as our HW is. Besides that it supports only CLK_DIVIDER_READ_ONLY
174*4882a593Smuzhiyun  * flag which can be specified by user flexibly.
175*4882a593Smuzhiyun  */
imx_clk_hw_divider_gate(const char * name,const char * parent_name,unsigned long flags,void __iomem * reg,u8 shift,u8 width,u8 clk_divider_flags,const struct clk_div_table * table,spinlock_t * lock)176*4882a593Smuzhiyun struct clk_hw *imx_clk_hw_divider_gate(const char *name, const char *parent_name,
177*4882a593Smuzhiyun 				    unsigned long flags, void __iomem *reg,
178*4882a593Smuzhiyun 				    u8 shift, u8 width, u8 clk_divider_flags,
179*4882a593Smuzhiyun 				    const struct clk_div_table *table,
180*4882a593Smuzhiyun 				    spinlock_t *lock)
181*4882a593Smuzhiyun {
182*4882a593Smuzhiyun 	struct clk_init_data init;
183*4882a593Smuzhiyun 	struct clk_divider_gate *div_gate;
184*4882a593Smuzhiyun 	struct clk_hw *hw;
185*4882a593Smuzhiyun 	u32 val;
186*4882a593Smuzhiyun 	int ret;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	div_gate  = kzalloc(sizeof(*div_gate), GFP_KERNEL);
189*4882a593Smuzhiyun 	if (!div_gate)
190*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	init.name = name;
193*4882a593Smuzhiyun 	if (clk_divider_flags & CLK_DIVIDER_READ_ONLY)
194*4882a593Smuzhiyun 		init.ops = &clk_divider_gate_ro_ops;
195*4882a593Smuzhiyun 	else
196*4882a593Smuzhiyun 		init.ops = &clk_divider_gate_ops;
197*4882a593Smuzhiyun 	init.flags = flags;
198*4882a593Smuzhiyun 	init.parent_names = parent_name ? &parent_name : NULL;
199*4882a593Smuzhiyun 	init.num_parents = parent_name ? 1 : 0;
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	div_gate->divider.reg = reg;
202*4882a593Smuzhiyun 	div_gate->divider.shift = shift;
203*4882a593Smuzhiyun 	div_gate->divider.width = width;
204*4882a593Smuzhiyun 	div_gate->divider.lock = lock;
205*4882a593Smuzhiyun 	div_gate->divider.table = table;
206*4882a593Smuzhiyun 	div_gate->divider.hw.init = &init;
207*4882a593Smuzhiyun 	div_gate->divider.flags = CLK_DIVIDER_ONE_BASED | clk_divider_flags;
208*4882a593Smuzhiyun 	/* cache gate status */
209*4882a593Smuzhiyun 	val = readl(reg) >> shift;
210*4882a593Smuzhiyun 	val &= clk_div_mask(width);
211*4882a593Smuzhiyun 	div_gate->cached_val = val;
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	hw = &div_gate->divider.hw;
214*4882a593Smuzhiyun 	ret = clk_hw_register(NULL, hw);
215*4882a593Smuzhiyun 	if (ret) {
216*4882a593Smuzhiyun 		kfree(div_gate);
217*4882a593Smuzhiyun 		hw = ERR_PTR(ret);
218*4882a593Smuzhiyun 	}
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	return hw;
221*4882a593Smuzhiyun }
222