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>
12 #include <linux/rational.h>
18 unsigned long n
, max_n
;
19 unsigned long k
, max_k
;
20 unsigned long m
, max_m
;
21 unsigned long p
, max_p
;
24 static void ccu_nkmp_find_best(unsigned long parent
, unsigned long rate
,
25 struct _ccu_nkmp
*nkmp
)
27 unsigned long best_rate
= 0;
28 unsigned long best_n
= 0, best_k
= 0, best_m
= 0, best_p
= 0;
29 unsigned long _n
, _k
, _m
, _p
;
31 for (_k
= 1; _k
<= nkmp
->max_k
; _k
++) {
32 for (_p
= 0; _p
<= nkmp
->max_p
; _p
++) {
33 unsigned long tmp_rate
;
35 rational_best_approximation(rate
/ _k
, parent
>> _p
,
36 nkmp
->max_n
, nkmp
->max_m
,
39 tmp_rate
= (parent
* _n
* _k
>> _p
) / _m
;
44 if ((rate
- tmp_rate
) < (rate
- best_rate
)) {
60 static void ccu_nkmp_disable(struct clk_hw
*hw
)
62 struct ccu_nkmp
*nkmp
= hw_to_ccu_nkmp(hw
);
64 return ccu_gate_helper_disable(&nkmp
->common
, nkmp
->enable
);
67 static int ccu_nkmp_enable(struct clk_hw
*hw
)
69 struct ccu_nkmp
*nkmp
= hw_to_ccu_nkmp(hw
);
71 return ccu_gate_helper_enable(&nkmp
->common
, nkmp
->enable
);
74 static int ccu_nkmp_is_enabled(struct clk_hw
*hw
)
76 struct ccu_nkmp
*nkmp
= hw_to_ccu_nkmp(hw
);
78 return ccu_gate_helper_is_enabled(&nkmp
->common
, nkmp
->enable
);
81 static unsigned long ccu_nkmp_recalc_rate(struct clk_hw
*hw
,
82 unsigned long parent_rate
)
84 struct ccu_nkmp
*nkmp
= hw_to_ccu_nkmp(hw
);
85 unsigned long n
, m
, k
, p
;
88 reg
= readl(nkmp
->common
.base
+ nkmp
->common
.reg
);
90 n
= reg
>> nkmp
->n
.shift
;
91 n
&= (1 << nkmp
->n
.width
) - 1;
93 k
= reg
>> nkmp
->k
.shift
;
94 k
&= (1 << nkmp
->k
.width
) - 1;
96 m
= reg
>> nkmp
->m
.shift
;
97 m
&= (1 << nkmp
->m
.width
) - 1;
99 p
= reg
>> nkmp
->p
.shift
;
100 p
&= (1 << nkmp
->p
.width
) - 1;
102 return (parent_rate
* (n
+ 1) * (k
+ 1) >> p
) / (m
+ 1);
105 static long ccu_nkmp_round_rate(struct clk_hw
*hw
, unsigned long rate
,
106 unsigned long *parent_rate
)
108 struct ccu_nkmp
*nkmp
= hw_to_ccu_nkmp(hw
);
109 struct _ccu_nkmp _nkmp
;
111 _nkmp
.max_n
= 1 << nkmp
->n
.width
;
112 _nkmp
.max_k
= 1 << nkmp
->k
.width
;
113 _nkmp
.max_m
= 1 << nkmp
->m
.width
;
114 _nkmp
.max_p
= (1 << nkmp
->p
.width
) - 1;
116 ccu_nkmp_find_best(*parent_rate
, rate
,
119 return (*parent_rate
* _nkmp
.n
* _nkmp
.k
>> _nkmp
.p
) / _nkmp
.m
;
122 static int ccu_nkmp_set_rate(struct clk_hw
*hw
, unsigned long rate
,
123 unsigned long parent_rate
)
125 struct ccu_nkmp
*nkmp
= hw_to_ccu_nkmp(hw
);
126 struct _ccu_nkmp _nkmp
;
130 _nkmp
.max_n
= 1 << nkmp
->n
.width
;
131 _nkmp
.max_k
= 1 << nkmp
->k
.width
;
132 _nkmp
.max_m
= 1 << nkmp
->m
.width
;
133 _nkmp
.max_p
= (1 << nkmp
->p
.width
) - 1;
135 ccu_nkmp_find_best(parent_rate
, rate
, &_nkmp
);
137 spin_lock_irqsave(nkmp
->common
.lock
, flags
);
139 reg
= readl(nkmp
->common
.base
+ nkmp
->common
.reg
);
140 reg
&= ~GENMASK(nkmp
->n
.width
+ nkmp
->n
.shift
- 1, nkmp
->n
.shift
);
141 reg
&= ~GENMASK(nkmp
->k
.width
+ nkmp
->k
.shift
- 1, nkmp
->k
.shift
);
142 reg
&= ~GENMASK(nkmp
->m
.width
+ nkmp
->m
.shift
- 1, nkmp
->m
.shift
);
143 reg
&= ~GENMASK(nkmp
->p
.width
+ nkmp
->p
.shift
- 1, nkmp
->p
.shift
);
145 reg
|= (_nkmp
.n
- 1) << nkmp
->n
.shift
;
146 reg
|= (_nkmp
.k
- 1) << nkmp
->k
.shift
;
147 reg
|= (_nkmp
.m
- 1) << nkmp
->m
.shift
;
148 reg
|= _nkmp
.p
<< nkmp
->p
.shift
;
150 writel(reg
, nkmp
->common
.base
+ nkmp
->common
.reg
);
152 spin_unlock_irqrestore(nkmp
->common
.lock
, flags
);
154 ccu_helper_wait_for_lock(&nkmp
->common
, nkmp
->lock
);
159 const struct clk_ops ccu_nkmp_ops
= {
160 .disable
= ccu_nkmp_disable
,
161 .enable
= ccu_nkmp_enable
,
162 .is_enabled
= ccu_nkmp_is_enabled
,
164 .recalc_rate
= ccu_nkmp_recalc_rate
,
165 .round_rate
= ccu_nkmp_round_rate
,
166 .set_rate
= ccu_nkmp_set_rate
,