xref: /linux/drivers/clk/sunxi-ng/ccu_nkm.c (revision 62e59c4e69b3cdbad67e3c2d49e4df4cfe1679e3)
1df6561e6SMaxime Ripard /*
2df6561e6SMaxime Ripard  * Copyright (C) 2016 Maxime Ripard
3df6561e6SMaxime Ripard  * Maxime Ripard <maxime.ripard@free-electrons.com>
4df6561e6SMaxime Ripard  *
5df6561e6SMaxime Ripard  * This program is free software; you can redistribute it and/or
6df6561e6SMaxime Ripard  * modify it under the terms of the GNU General Public License as
7df6561e6SMaxime Ripard  * published by the Free Software Foundation; either version 2 of
8df6561e6SMaxime Ripard  * the License, or (at your option) any later version.
9df6561e6SMaxime Ripard  */
10df6561e6SMaxime Ripard 
11df6561e6SMaxime Ripard #include <linux/clk-provider.h>
12*62e59c4eSStephen Boyd #include <linux/io.h>
13df6561e6SMaxime Ripard 
14df6561e6SMaxime Ripard #include "ccu_gate.h"
15df6561e6SMaxime Ripard #include "ccu_nkm.h"
16df6561e6SMaxime Ripard 
17df6561e6SMaxime Ripard struct _ccu_nkm {
186e0d50daSMaxime Ripard 	unsigned long	n, min_n, max_n;
196e0d50daSMaxime Ripard 	unsigned long	k, min_k, max_k;
206e0d50daSMaxime Ripard 	unsigned long	m, min_m, max_m;
21df6561e6SMaxime Ripard };
22df6561e6SMaxime Ripard 
23df6561e6SMaxime Ripard static void ccu_nkm_find_best(unsigned long parent, unsigned long rate,
24df6561e6SMaxime Ripard 			      struct _ccu_nkm *nkm)
25df6561e6SMaxime Ripard {
26df6561e6SMaxime Ripard 	unsigned long best_rate = 0;
27df6561e6SMaxime Ripard 	unsigned long best_n = 0, best_k = 0, best_m = 0;
28df6561e6SMaxime Ripard 	unsigned long _n, _k, _m;
29df6561e6SMaxime Ripard 
306e0d50daSMaxime Ripard 	for (_k = nkm->min_k; _k <= nkm->max_k; _k++) {
316e0d50daSMaxime Ripard 		for (_n = nkm->min_n; _n <= nkm->max_n; _n++) {
326e0d50daSMaxime Ripard 			for (_m = nkm->min_m; _m <= nkm->max_m; _m++) {
33df6561e6SMaxime Ripard 				unsigned long tmp_rate;
34df6561e6SMaxime Ripard 
35df6561e6SMaxime Ripard 				tmp_rate = parent * _n * _k / _m;
36df6561e6SMaxime Ripard 
37df6561e6SMaxime Ripard 				if (tmp_rate > rate)
38df6561e6SMaxime Ripard 					continue;
39df6561e6SMaxime Ripard 				if ((rate - tmp_rate) < (rate - best_rate)) {
40df6561e6SMaxime Ripard 					best_rate = tmp_rate;
41df6561e6SMaxime Ripard 					best_n = _n;
42df6561e6SMaxime Ripard 					best_k = _k;
43df6561e6SMaxime Ripard 					best_m = _m;
44df6561e6SMaxime Ripard 				}
45df6561e6SMaxime Ripard 			}
46ee28648cSMaxime Ripard 		}
47ee28648cSMaxime Ripard 	}
48df6561e6SMaxime Ripard 
49df6561e6SMaxime Ripard 	nkm->n = best_n;
50df6561e6SMaxime Ripard 	nkm->k = best_k;
51df6561e6SMaxime Ripard 	nkm->m = best_m;
52df6561e6SMaxime Ripard }
53df6561e6SMaxime Ripard 
54df6561e6SMaxime Ripard static void ccu_nkm_disable(struct clk_hw *hw)
55df6561e6SMaxime Ripard {
56df6561e6SMaxime Ripard 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
57df6561e6SMaxime Ripard 
58df6561e6SMaxime Ripard 	return ccu_gate_helper_disable(&nkm->common, nkm->enable);
59df6561e6SMaxime Ripard }
60df6561e6SMaxime Ripard 
61df6561e6SMaxime Ripard static int ccu_nkm_enable(struct clk_hw *hw)
62df6561e6SMaxime Ripard {
63df6561e6SMaxime Ripard 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
64df6561e6SMaxime Ripard 
65df6561e6SMaxime Ripard 	return ccu_gate_helper_enable(&nkm->common, nkm->enable);
66df6561e6SMaxime Ripard }
67df6561e6SMaxime Ripard 
68df6561e6SMaxime Ripard static int ccu_nkm_is_enabled(struct clk_hw *hw)
69df6561e6SMaxime Ripard {
70df6561e6SMaxime Ripard 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
71df6561e6SMaxime Ripard 
72df6561e6SMaxime Ripard 	return ccu_gate_helper_is_enabled(&nkm->common, nkm->enable);
73df6561e6SMaxime Ripard }
74df6561e6SMaxime Ripard 
75df6561e6SMaxime Ripard static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw,
76df6561e6SMaxime Ripard 					unsigned long parent_rate)
77df6561e6SMaxime Ripard {
78df6561e6SMaxime Ripard 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
79a6653773SIcenowy Zheng 	unsigned long n, m, k, rate;
80df6561e6SMaxime Ripard 	u32 reg;
81df6561e6SMaxime Ripard 
82df6561e6SMaxime Ripard 	reg = readl(nkm->common.base + nkm->common.reg);
83df6561e6SMaxime Ripard 
84df6561e6SMaxime Ripard 	n = reg >> nkm->n.shift;
85df6561e6SMaxime Ripard 	n &= (1 << nkm->n.width) - 1;
86e66f81bbSMaxime Ripard 	n += nkm->n.offset;
87e66f81bbSMaxime Ripard 	if (!n)
88e66f81bbSMaxime Ripard 		n++;
89df6561e6SMaxime Ripard 
90df6561e6SMaxime Ripard 	k = reg >> nkm->k.shift;
91df6561e6SMaxime Ripard 	k &= (1 << nkm->k.width) - 1;
92e66f81bbSMaxime Ripard 	k += nkm->k.offset;
93e66f81bbSMaxime Ripard 	if (!k)
94e66f81bbSMaxime Ripard 		k++;
95df6561e6SMaxime Ripard 
96df6561e6SMaxime Ripard 	m = reg >> nkm->m.shift;
97df6561e6SMaxime Ripard 	m &= (1 << nkm->m.width) - 1;
98e66f81bbSMaxime Ripard 	m += nkm->m.offset;
99e66f81bbSMaxime Ripard 	if (!m)
100e66f81bbSMaxime Ripard 		m++;
101df6561e6SMaxime Ripard 
102a6653773SIcenowy Zheng 	rate = parent_rate * n  * k / m;
103a6653773SIcenowy Zheng 
104a6653773SIcenowy Zheng 	if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV)
105a6653773SIcenowy Zheng 		rate /= nkm->fixed_post_div;
106a6653773SIcenowy Zheng 
107a6653773SIcenowy Zheng 	return rate;
108df6561e6SMaxime Ripard }
109df6561e6SMaxime Ripard 
110a3658359SChen-Yu Tsai static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux,
11110a8d9b9SMaxime Ripard 					struct clk_hw *hw,
11210a8d9b9SMaxime Ripard 					unsigned long *parent_rate,
113a3658359SChen-Yu Tsai 					unsigned long rate,
114a3658359SChen-Yu Tsai 					void *data)
115df6561e6SMaxime Ripard {
116a3658359SChen-Yu Tsai 	struct ccu_nkm *nkm = data;
117df6561e6SMaxime Ripard 	struct _ccu_nkm _nkm;
118df6561e6SMaxime Ripard 
1194162c5ceSChen-Yu Tsai 	_nkm.min_n = nkm->n.min ?: 1;
1200c3c8e13SMaxime Ripard 	_nkm.max_n = nkm->n.max ?: 1 << nkm->n.width;
1214162c5ceSChen-Yu Tsai 	_nkm.min_k = nkm->k.min ?: 1;
1220c3c8e13SMaxime Ripard 	_nkm.max_k = nkm->k.max ?: 1 << nkm->k.width;
1236e0d50daSMaxime Ripard 	_nkm.min_m = 1;
12487ba9e59SMaxime Ripard 	_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
125df6561e6SMaxime Ripard 
126a6653773SIcenowy Zheng 	if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV)
127a6653773SIcenowy Zheng 		rate *= nkm->fixed_post_div;
128a6653773SIcenowy Zheng 
12910a8d9b9SMaxime Ripard 	ccu_nkm_find_best(*parent_rate, rate, &_nkm);
130df6561e6SMaxime Ripard 
131a6653773SIcenowy Zheng 	rate = *parent_rate * _nkm.n * _nkm.k / _nkm.m;
132a6653773SIcenowy Zheng 
133a6653773SIcenowy Zheng 	if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV)
134a6653773SIcenowy Zheng 		rate /= nkm->fixed_post_div;
135a6653773SIcenowy Zheng 
136a6653773SIcenowy Zheng 	return rate;
137a3658359SChen-Yu Tsai }
138a3658359SChen-Yu Tsai 
139a3658359SChen-Yu Tsai static int ccu_nkm_determine_rate(struct clk_hw *hw,
140a3658359SChen-Yu Tsai 				  struct clk_rate_request *req)
141a3658359SChen-Yu Tsai {
142a3658359SChen-Yu Tsai 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
143a3658359SChen-Yu Tsai 
144a3658359SChen-Yu Tsai 	return ccu_mux_helper_determine_rate(&nkm->common, &nkm->mux,
145a3658359SChen-Yu Tsai 					     req, ccu_nkm_round_rate, nkm);
146df6561e6SMaxime Ripard }
147df6561e6SMaxime Ripard 
148df6561e6SMaxime Ripard static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate,
149df6561e6SMaxime Ripard 			   unsigned long parent_rate)
150df6561e6SMaxime Ripard {
151df6561e6SMaxime Ripard 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
152df6561e6SMaxime Ripard 	struct _ccu_nkm _nkm;
153df6561e6SMaxime Ripard 	unsigned long flags;
154df6561e6SMaxime Ripard 	u32 reg;
155df6561e6SMaxime Ripard 
156a6653773SIcenowy Zheng 	if (nkm->common.features & CCU_FEATURE_FIXED_POSTDIV)
157a6653773SIcenowy Zheng 		rate *= nkm->fixed_post_div;
158a6653773SIcenowy Zheng 
1594162c5ceSChen-Yu Tsai 	_nkm.min_n = nkm->n.min ?: 1;
1600c3c8e13SMaxime Ripard 	_nkm.max_n = nkm->n.max ?: 1 << nkm->n.width;
1614162c5ceSChen-Yu Tsai 	_nkm.min_k = nkm->k.min ?: 1;
1620c3c8e13SMaxime Ripard 	_nkm.max_k = nkm->k.max ?: 1 << nkm->k.width;
1636e0d50daSMaxime Ripard 	_nkm.min_m = 1;
16487ba9e59SMaxime Ripard 	_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
165df6561e6SMaxime Ripard 
166df6561e6SMaxime Ripard 	ccu_nkm_find_best(parent_rate, rate, &_nkm);
167df6561e6SMaxime Ripard 
168df6561e6SMaxime Ripard 	spin_lock_irqsave(nkm->common.lock, flags);
169df6561e6SMaxime Ripard 
170df6561e6SMaxime Ripard 	reg = readl(nkm->common.base + nkm->common.reg);
171df6561e6SMaxime Ripard 	reg &= ~GENMASK(nkm->n.width + nkm->n.shift - 1, nkm->n.shift);
172df6561e6SMaxime Ripard 	reg &= ~GENMASK(nkm->k.width + nkm->k.shift - 1, nkm->k.shift);
173df6561e6SMaxime Ripard 	reg &= ~GENMASK(nkm->m.width + nkm->m.shift - 1, nkm->m.shift);
174df6561e6SMaxime Ripard 
175e66f81bbSMaxime Ripard 	reg |= (_nkm.n - nkm->n.offset) << nkm->n.shift;
176e66f81bbSMaxime Ripard 	reg |= (_nkm.k - nkm->k.offset) << nkm->k.shift;
177e66f81bbSMaxime Ripard 	reg |= (_nkm.m - nkm->m.offset) << nkm->m.shift;
178df6561e6SMaxime Ripard 	writel(reg, nkm->common.base + nkm->common.reg);
179df6561e6SMaxime Ripard 
180df6561e6SMaxime Ripard 	spin_unlock_irqrestore(nkm->common.lock, flags);
181df6561e6SMaxime Ripard 
182df6561e6SMaxime Ripard 	ccu_helper_wait_for_lock(&nkm->common, nkm->lock);
183df6561e6SMaxime Ripard 
184df6561e6SMaxime Ripard 	return 0;
185df6561e6SMaxime Ripard }
186df6561e6SMaxime Ripard 
187a3658359SChen-Yu Tsai static u8 ccu_nkm_get_parent(struct clk_hw *hw)
188a3658359SChen-Yu Tsai {
189a3658359SChen-Yu Tsai 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
190a3658359SChen-Yu Tsai 
191a3658359SChen-Yu Tsai 	return ccu_mux_helper_get_parent(&nkm->common, &nkm->mux);
192a3658359SChen-Yu Tsai }
193a3658359SChen-Yu Tsai 
194a3658359SChen-Yu Tsai static int ccu_nkm_set_parent(struct clk_hw *hw, u8 index)
195a3658359SChen-Yu Tsai {
196a3658359SChen-Yu Tsai 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
197a3658359SChen-Yu Tsai 
198a3658359SChen-Yu Tsai 	return ccu_mux_helper_set_parent(&nkm->common, &nkm->mux, index);
199a3658359SChen-Yu Tsai }
200a3658359SChen-Yu Tsai 
201df6561e6SMaxime Ripard const struct clk_ops ccu_nkm_ops = {
202df6561e6SMaxime Ripard 	.disable	= ccu_nkm_disable,
203df6561e6SMaxime Ripard 	.enable		= ccu_nkm_enable,
204df6561e6SMaxime Ripard 	.is_enabled	= ccu_nkm_is_enabled,
205df6561e6SMaxime Ripard 
206a3658359SChen-Yu Tsai 	.get_parent	= ccu_nkm_get_parent,
207a3658359SChen-Yu Tsai 	.set_parent	= ccu_nkm_set_parent,
208a3658359SChen-Yu Tsai 
209a3658359SChen-Yu Tsai 	.determine_rate	= ccu_nkm_determine_rate,
210df6561e6SMaxime Ripard 	.recalc_rate	= ccu_nkm_recalc_rate,
211df6561e6SMaxime Ripard 	.set_rate	= ccu_nkm_set_rate,
212df6561e6SMaxime Ripard };
213