2 * Copyright (C) 2016 Maxime Ripard
3 * Maxime Ripard <maxime.ripard@free-electrons.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
11 #include <linux/clk-provider.h>
16 static unsigned long ccu_div_round_rate(struct ccu_mux_internal
*mux
,
17 struct clk_hw
*parent
,
18 unsigned long *parent_rate
,
22 struct ccu_div
*cd
= data
;
24 if (cd
->common
.features
& CCU_FEATURE_FIXED_POSTDIV
)
25 rate
*= cd
->fixed_post_div
;
27 rate
= divider_round_rate_parent(&cd
->common
.hw
, parent
,
29 cd
->div
.table
, cd
->div
.width
,
32 if (cd
->common
.features
& CCU_FEATURE_FIXED_POSTDIV
)
33 rate
/= cd
->fixed_post_div
;
38 static void ccu_div_disable(struct clk_hw
*hw
)
40 struct ccu_div
*cd
= hw_to_ccu_div(hw
);
42 return ccu_gate_helper_disable(&cd
->common
, cd
->enable
);
45 static int ccu_div_enable(struct clk_hw
*hw
)
47 struct ccu_div
*cd
= hw_to_ccu_div(hw
);
49 return ccu_gate_helper_enable(&cd
->common
, cd
->enable
);
52 static int ccu_div_is_enabled(struct clk_hw
*hw
)
54 struct ccu_div
*cd
= hw_to_ccu_div(hw
);
56 return ccu_gate_helper_is_enabled(&cd
->common
, cd
->enable
);
59 static unsigned long ccu_div_recalc_rate(struct clk_hw
*hw
,
60 unsigned long parent_rate
)
62 struct ccu_div
*cd
= hw_to_ccu_div(hw
);
66 reg
= readl(cd
->common
.base
+ cd
->common
.reg
);
67 val
= reg
>> cd
->div
.shift
;
68 val
&= (1 << cd
->div
.width
) - 1;
70 parent_rate
= ccu_mux_helper_apply_prediv(&cd
->common
, &cd
->mux
, -1,
73 val
= divider_recalc_rate(hw
, parent_rate
, val
, cd
->div
.table
,
74 cd
->div
.flags
, cd
->div
.width
);
76 if (cd
->common
.features
& CCU_FEATURE_FIXED_POSTDIV
)
77 val
/= cd
->fixed_post_div
;
82 static int ccu_div_determine_rate(struct clk_hw
*hw
,
83 struct clk_rate_request
*req
)
85 struct ccu_div
*cd
= hw_to_ccu_div(hw
);
87 return ccu_mux_helper_determine_rate(&cd
->common
, &cd
->mux
,
88 req
, ccu_div_round_rate
, cd
);
91 static int ccu_div_set_rate(struct clk_hw
*hw
, unsigned long rate
,
92 unsigned long parent_rate
)
94 struct ccu_div
*cd
= hw_to_ccu_div(hw
);
99 parent_rate
= ccu_mux_helper_apply_prediv(&cd
->common
, &cd
->mux
, -1,
102 if (cd
->common
.features
& CCU_FEATURE_FIXED_POSTDIV
)
103 rate
*= cd
->fixed_post_div
;
105 val
= divider_get_val(rate
, parent_rate
, cd
->div
.table
, cd
->div
.width
,
108 spin_lock_irqsave(cd
->common
.lock
, flags
);
110 reg
= readl(cd
->common
.base
+ cd
->common
.reg
);
111 reg
&= ~GENMASK(cd
->div
.width
+ cd
->div
.shift
- 1, cd
->div
.shift
);
113 writel(reg
| (val
<< cd
->div
.shift
),
114 cd
->common
.base
+ cd
->common
.reg
);
116 spin_unlock_irqrestore(cd
->common
.lock
, flags
);
121 static u8
ccu_div_get_parent(struct clk_hw
*hw
)
123 struct ccu_div
*cd
= hw_to_ccu_div(hw
);
125 return ccu_mux_helper_get_parent(&cd
->common
, &cd
->mux
);
128 static int ccu_div_set_parent(struct clk_hw
*hw
, u8 index
)
130 struct ccu_div
*cd
= hw_to_ccu_div(hw
);
132 return ccu_mux_helper_set_parent(&cd
->common
, &cd
->mux
, index
);
135 const struct clk_ops ccu_div_ops
= {
136 .disable
= ccu_div_disable
,
137 .enable
= ccu_div_enable
,
138 .is_enabled
= ccu_div_is_enabled
,
140 .get_parent
= ccu_div_get_parent
,
141 .set_parent
= ccu_div_set_parent
,
143 .determine_rate
= ccu_div_determine_rate
,
144 .recalc_rate
= ccu_div_recalc_rate
,
145 .set_rate
= ccu_div_set_rate
,