]>
git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blob - drivers/clk/sunxi-ng/ccu_mp.c
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 void ccu_mp_find_best(unsigned long parent
, unsigned long rate
,
17 unsigned int max_m
, unsigned int max_p
,
18 unsigned int *m
, unsigned int *p
)
20 unsigned long best_rate
= 0;
21 unsigned int best_m
= 0, best_p
= 0;
24 for (_p
= 1; _p
<= max_p
; _p
<<= 1) {
25 for (_m
= 1; _m
<= max_m
; _m
++) {
26 unsigned long tmp_rate
= parent
/ _p
/ _m
;
31 if ((rate
- tmp_rate
) < (rate
- best_rate
)) {
43 static unsigned long ccu_mp_round_rate(struct ccu_mux_internal
*mux
,
44 unsigned long parent_rate
,
48 struct ccu_mp
*cmp
= data
;
49 unsigned int max_m
, max_p
;
52 max_m
= cmp
->m
.max
?: 1 << cmp
->m
.width
;
53 max_p
= cmp
->p
.max
?: 1 << ((1 << cmp
->p
.width
) - 1);
55 ccu_mp_find_best(parent_rate
, rate
, max_m
, max_p
, &m
, &p
);
57 return parent_rate
/ p
/ m
;
60 static void ccu_mp_disable(struct clk_hw
*hw
)
62 struct ccu_mp
*cmp
= hw_to_ccu_mp(hw
);
64 return ccu_gate_helper_disable(&cmp
->common
, cmp
->enable
);
67 static int ccu_mp_enable(struct clk_hw
*hw
)
69 struct ccu_mp
*cmp
= hw_to_ccu_mp(hw
);
71 return ccu_gate_helper_enable(&cmp
->common
, cmp
->enable
);
74 static int ccu_mp_is_enabled(struct clk_hw
*hw
)
76 struct ccu_mp
*cmp
= hw_to_ccu_mp(hw
);
78 return ccu_gate_helper_is_enabled(&cmp
->common
, cmp
->enable
);
81 static unsigned long ccu_mp_recalc_rate(struct clk_hw
*hw
,
82 unsigned long parent_rate
)
84 struct ccu_mp
*cmp
= hw_to_ccu_mp(hw
);
88 /* Adjust parent_rate according to pre-dividers */
89 ccu_mux_helper_adjust_parent_for_prediv(&cmp
->common
, &cmp
->mux
,
92 reg
= readl(cmp
->common
.base
+ cmp
->common
.reg
);
94 m
= reg
>> cmp
->m
.shift
;
95 m
&= (1 << cmp
->m
.width
) - 1;
97 p
= reg
>> cmp
->p
.shift
;
98 p
&= (1 << cmp
->p
.width
) - 1;
100 return (parent_rate
>> p
) / (m
+ 1);
103 static int ccu_mp_determine_rate(struct clk_hw
*hw
,
104 struct clk_rate_request
*req
)
106 struct ccu_mp
*cmp
= hw_to_ccu_mp(hw
);
108 return ccu_mux_helper_determine_rate(&cmp
->common
, &cmp
->mux
,
109 req
, ccu_mp_round_rate
, cmp
);
112 static int ccu_mp_set_rate(struct clk_hw
*hw
, unsigned long rate
,
113 unsigned long parent_rate
)
115 struct ccu_mp
*cmp
= hw_to_ccu_mp(hw
);
117 unsigned int max_m
, max_p
;
121 /* Adjust parent_rate according to pre-dividers */
122 ccu_mux_helper_adjust_parent_for_prediv(&cmp
->common
, &cmp
->mux
,
125 max_m
= cmp
->m
.max
?: 1 << cmp
->m
.width
;
126 max_p
= cmp
->p
.max
?: 1 << ((1 << cmp
->p
.width
) - 1);
128 ccu_mp_find_best(parent_rate
, rate
, max_m
, max_p
, &m
, &p
);
130 spin_lock_irqsave(cmp
->common
.lock
, flags
);
132 reg
= readl(cmp
->common
.base
+ cmp
->common
.reg
);
133 reg
&= ~GENMASK(cmp
->m
.width
+ cmp
->m
.shift
- 1, cmp
->m
.shift
);
134 reg
&= ~GENMASK(cmp
->p
.width
+ cmp
->p
.shift
- 1, cmp
->p
.shift
);
136 writel(reg
| (ilog2(p
) << cmp
->p
.shift
) | ((m
- 1) << cmp
->m
.shift
),
137 cmp
->common
.base
+ cmp
->common
.reg
);
139 spin_unlock_irqrestore(cmp
->common
.lock
, flags
);
144 static u8
ccu_mp_get_parent(struct clk_hw
*hw
)
146 struct ccu_mp
*cmp
= hw_to_ccu_mp(hw
);
148 return ccu_mux_helper_get_parent(&cmp
->common
, &cmp
->mux
);
151 static int ccu_mp_set_parent(struct clk_hw
*hw
, u8 index
)
153 struct ccu_mp
*cmp
= hw_to_ccu_mp(hw
);
155 return ccu_mux_helper_set_parent(&cmp
->common
, &cmp
->mux
, index
);
158 const struct clk_ops ccu_mp_ops
= {
159 .disable
= ccu_mp_disable
,
160 .enable
= ccu_mp_enable
,
161 .is_enabled
= ccu_mp_is_enabled
,
163 .get_parent
= ccu_mp_get_parent
,
164 .set_parent
= ccu_mp_set_parent
,
166 .determine_rate
= ccu_mp_determine_rate
,
167 .recalc_rate
= ccu_mp_recalc_rate
,
168 .set_rate
= ccu_mp_set_rate
,