• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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