• 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 #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