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