1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (c) 2019 Fuzhou Rockchip Electronics Co., Ltd
4 */
5
6 #include <linux/slab.h>
7 #include <linux/bitops.h>
8 #include <linux/regmap.h>
9 #include <linux/clk.h>
10 #include <linux/clk-provider.h>
11 #include "clk.h"
12
13 #define div_mask(width) ((1 << (width)) - 1)
14
clk_dclk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)15 static unsigned long clk_dclk_recalc_rate(struct clk_hw *hw,
16 unsigned long parent_rate)
17 {
18 struct clk_divider *divider = to_clk_divider(hw);
19 unsigned int val;
20
21 val = readl(divider->reg) >> divider->shift;
22 val &= div_mask(divider->width);
23
24 return DIV_ROUND_UP_ULL(((u64)parent_rate), val + 1);
25 }
26
clk_dclk_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * prate)27 static long clk_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
28 unsigned long *prate)
29 {
30 struct clk_divider *divider = to_clk_divider(hw);
31 int div, maxdiv = div_mask(divider->width) + 1;
32
33 div = DIV_ROUND_UP_ULL(divider->max_prate, rate);
34 if (div % 2)
35 div = __rounddown_pow_of_two(div);
36 div = div > maxdiv ? maxdiv : div;
37 *prate = div * rate;
38 return rate;
39 }
40
clk_dclk_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)41 static int clk_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
42 unsigned long parent_rate)
43 {
44 struct clk_divider *divider = to_clk_divider(hw);
45 unsigned int value;
46 unsigned long flags = 0;
47 u32 val;
48
49 value = divider_get_val(rate, parent_rate, divider->table,
50 divider->width, divider->flags);
51
52 if (divider->lock)
53 spin_lock_irqsave(divider->lock, flags);
54 else
55 __acquire(divider->lock);
56
57 if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
58 val = div_mask(divider->width) << (divider->shift + 16);
59 } else {
60 val = readl(divider->reg);
61 val &= ~(div_mask(divider->width) << divider->shift);
62 }
63 val |= value << divider->shift;
64 writel(val, divider->reg);
65
66 if (divider->lock)
67 spin_unlock_irqrestore(divider->lock, flags);
68 else
69 __release(divider->lock);
70
71 return 0;
72 }
73
74 const struct clk_ops clk_dclk_divider_ops = {
75 .recalc_rate = clk_dclk_recalc_rate,
76 .round_rate = clk_dclk_round_rate,
77 .set_rate = clk_dclk_set_rate,
78 };
79 EXPORT_SYMBOL_GPL(clk_dclk_divider_ops);
80
81 /**
82 * Register a clock branch.
83 * Most clock branches have a form like
84 *
85 * src1 --|--\
86 * |M |--[GATE]-[DIV]-
87 * src2 --|--/
88 *
89 * sometimes without one of those components.
90 */
rockchip_clk_register_dclk_branch(const char * name,const char * const * parent_names,u8 num_parents,void __iomem * base,int muxdiv_offset,u8 mux_shift,u8 mux_width,u8 mux_flags,int div_offset,u8 div_shift,u8 div_width,u8 div_flags,struct clk_div_table * div_table,int gate_offset,u8 gate_shift,u8 gate_flags,unsigned long flags,unsigned long max_prate,spinlock_t * lock)91 struct clk *rockchip_clk_register_dclk_branch(const char *name,
92 const char *const *parent_names,
93 u8 num_parents,
94 void __iomem *base,
95 int muxdiv_offset, u8 mux_shift,
96 u8 mux_width, u8 mux_flags,
97 int div_offset, u8 div_shift,
98 u8 div_width, u8 div_flags,
99 struct clk_div_table *div_table,
100 int gate_offset,
101 u8 gate_shift, u8 gate_flags,
102 unsigned long flags,
103 unsigned long max_prate,
104 spinlock_t *lock)
105 {
106 struct clk *clk;
107 struct clk_mux *mux = NULL;
108 struct clk_gate *gate = NULL;
109 struct clk_divider *div = NULL;
110 const struct clk_ops *mux_ops = NULL, *div_ops = NULL,
111 *gate_ops = NULL;
112
113 if (num_parents > 1) {
114 mux = kzalloc(sizeof(*mux), GFP_KERNEL);
115 if (!mux)
116 return ERR_PTR(-ENOMEM);
117
118 mux->reg = base + muxdiv_offset;
119 mux->shift = mux_shift;
120 mux->mask = BIT(mux_width) - 1;
121 mux->flags = mux_flags;
122 mux->lock = lock;
123 mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
124 : &clk_mux_ops;
125 }
126
127 if (gate_offset >= 0) {
128 gate = kzalloc(sizeof(*gate), GFP_KERNEL);
129 if (!gate)
130 goto err_gate;
131
132 gate->flags = gate_flags;
133 gate->reg = base + gate_offset;
134 gate->bit_idx = gate_shift;
135 gate->lock = lock;
136 gate_ops = &clk_gate_ops;
137 }
138
139 if (div_width > 0) {
140 div = kzalloc(sizeof(*div), GFP_KERNEL);
141 if (!div)
142 goto err_div;
143
144 div->flags = div_flags;
145 if (div_offset)
146 div->reg = base + div_offset;
147 else
148 div->reg = base + muxdiv_offset;
149 div->shift = div_shift;
150 div->width = div_width;
151 div->lock = lock;
152 div->max_prate = max_prate;
153 div_ops = &clk_dclk_divider_ops;
154 }
155
156 clk = clk_register_composite(NULL, name, parent_names, num_parents,
157 mux ? &mux->hw : NULL, mux_ops,
158 div ? &div->hw : NULL, div_ops,
159 gate ? &gate->hw : NULL, gate_ops,
160 flags);
161
162 return clk;
163 err_div:
164 kfree(gate);
165 err_gate:
166 kfree(mux);
167 return ERR_PTR(-ENOMEM);
168 }
169