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 #define div_shift_width 16
15 #define div_odd 2
16
clk_dclk_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)17 static unsigned long clk_dclk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
18 {
19 struct clk_divider *divider = to_clk_divider(hw);
20 unsigned int val;
21
22 val = readl(divider->reg) >> divider->shift;
23 val &= div_mask(divider->width);
24
25 return DIV_ROUND_UP_ULL(((u64)parent_rate), val + 1);
26 }
27
clk_dclk_round_rate(struct clk_hw * hw,unsigned long rate,unsigned long * prate)28 static long clk_dclk_round_rate(struct clk_hw *hw, unsigned long rate, 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 % div_odd) {
35 div = __rounddown_pow_of_two(div);
36 }
37 div = div > maxdiv ? maxdiv : div;
38 *prate = div * rate;
39 return rate;
40 }
41
clk_dclk_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)42 static int clk_dclk_set_rate(struct clk_hw *hw, unsigned long rate, 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, divider->width, divider->flags);
50
51 if (divider->lock) {
52 spin_lock_irqsave(divider->lock, flags);
53 } else {
54 __acquire(divider->lock);
55 }
56
57 if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
58 val = div_mask(divider->width) << (divider->shift + div_shift_width);
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
72 return 0;
73 }
74
75 const struct clk_ops clk_dclk_divider_ops = {
76 .recalc_rate = clk_dclk_recalc_rate,
77 .round_rate = clk_dclk_round_rate,
78 .set_rate = clk_dclk_set_rate,
79 };
80 EXPORT_SYMBOL_GPL(clk_dclk_divider_ops);
81
82 /**
83 * Register a clock branch.
84 * Most clock branches have a form like
85 *
86 * src1 --|--\
87 * |M |--[GATE]-[DIV]-
88 * src2 --|--/
89 *
90 * sometimes without one of those components.
91 */
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)92 struct clk *rockchip_clk_register_dclk_branch(const char *name, const char *const *parent_names, u8 num_parents,
93 void __iomem *base, int muxdiv_offset, u8 mux_shift, u8 mux_width,
94 u8 mux_flags, int div_offset, u8 div_shift, u8 div_width, u8 div_flags,
95 struct clk_div_table *div_table, int gate_offset, u8 gate_shift,
96 u8 gate_flags, unsigned long flags, unsigned long max_prate,
97 spinlock_t *lock)
98 {
99 struct clk *clk;
100 struct clk_mux *mux = NULL;
101 struct clk_gate *gate = NULL;
102 struct clk_divider *div = NULL;
103 const struct clk_ops *mux_ops = NULL, *div_ops = NULL, *gate_ops = NULL;
104
105 if (num_parents > 1) {
106 mux = kzalloc(sizeof(*mux), GFP_KERNEL);
107 if (!mux) {
108 return ERR_PTR(-ENOMEM);
109 }
110
111 mux->reg = base + muxdiv_offset;
112 mux->shift = mux_shift;
113 mux->mask = BIT(mux_width) - 1;
114 mux->flags = mux_flags;
115 mux->lock = lock;
116 mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops : &clk_mux_ops;
117 }
118
119 if (gate_offset >= 0) {
120 gate = kzalloc(sizeof(*gate), GFP_KERNEL);
121 if (!gate) {
122 goto err_gate;
123 }
124
125 gate->flags = gate_flags;
126 gate->reg = base + gate_offset;
127 gate->bit_idx = gate_shift;
128 gate->lock = lock;
129 gate_ops = &clk_gate_ops;
130 }
131
132 if (div_width > 0) {
133 div = kzalloc(sizeof(*div), GFP_KERNEL);
134 if (!div) {
135 goto err_div;
136 }
137
138 div->flags = div_flags;
139 if (div_offset) {
140 div->reg = base + div_offset;
141 } else {
142 div->reg = base + muxdiv_offset;
143 }
144 div->shift = div_shift;
145 div->width = div_width;
146 div->lock = lock;
147 div->max_prate = max_prate;
148 div_ops = &clk_dclk_divider_ops;
149 }
150
151 clk = clk_register_composite(NULL, name, parent_names, num_parents, mux ? &mux->hw : NULL, mux_ops,
152 div ? &div->hw : NULL, div_ops, gate ? &gate->hw : NULL, gate_ops, flags);
153
154 return clk;
155 err_div:
156 kfree(gate);
157 err_gate:
158 kfree(mux);
159 return ERR_PTR(-ENOMEM);
160 }
161