xref: /linux/drivers/clk/sunxi-ng/ccu_nkm.c (revision 0c3c8e135897eb8e896a0bb82a5aff6c9bc158cc)
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>
12df6561e6SMaxime Ripard 
13df6561e6SMaxime Ripard #include "ccu_gate.h"
14df6561e6SMaxime Ripard #include "ccu_nkm.h"
15df6561e6SMaxime Ripard 
16df6561e6SMaxime Ripard struct _ccu_nkm {
176e0d50daSMaxime Ripard 	unsigned long	n, min_n, max_n;
186e0d50daSMaxime Ripard 	unsigned long	k, min_k, max_k;
196e0d50daSMaxime Ripard 	unsigned long	m, min_m, max_m;
20df6561e6SMaxime Ripard };
21df6561e6SMaxime Ripard 
22df6561e6SMaxime Ripard static void ccu_nkm_find_best(unsigned long parent, unsigned long rate,
23df6561e6SMaxime Ripard 			      struct _ccu_nkm *nkm)
24df6561e6SMaxime Ripard {
25df6561e6SMaxime Ripard 	unsigned long best_rate = 0;
26df6561e6SMaxime Ripard 	unsigned long best_n = 0, best_k = 0, best_m = 0;
27df6561e6SMaxime Ripard 	unsigned long _n, _k, _m;
28df6561e6SMaxime Ripard 
296e0d50daSMaxime Ripard 	for (_k = nkm->min_k; _k <= nkm->max_k; _k++) {
306e0d50daSMaxime Ripard 		for (_n = nkm->min_n; _n <= nkm->max_n; _n++) {
316e0d50daSMaxime Ripard 			for (_m = nkm->min_m; _m <= nkm->max_m; _m++) {
32df6561e6SMaxime Ripard 				unsigned long tmp_rate;
33df6561e6SMaxime Ripard 
34df6561e6SMaxime Ripard 				tmp_rate = parent * _n * _k / _m;
35df6561e6SMaxime Ripard 
36df6561e6SMaxime Ripard 				if (tmp_rate > rate)
37df6561e6SMaxime Ripard 					continue;
38df6561e6SMaxime Ripard 				if ((rate - tmp_rate) < (rate - best_rate)) {
39df6561e6SMaxime Ripard 					best_rate = tmp_rate;
40df6561e6SMaxime Ripard 					best_n = _n;
41df6561e6SMaxime Ripard 					best_k = _k;
42df6561e6SMaxime Ripard 					best_m = _m;
43df6561e6SMaxime Ripard 				}
44df6561e6SMaxime Ripard 			}
45ee28648cSMaxime Ripard 		}
46ee28648cSMaxime Ripard 	}
47df6561e6SMaxime Ripard 
48df6561e6SMaxime Ripard 	nkm->n = best_n;
49df6561e6SMaxime Ripard 	nkm->k = best_k;
50df6561e6SMaxime Ripard 	nkm->m = best_m;
51df6561e6SMaxime Ripard }
52df6561e6SMaxime Ripard 
53df6561e6SMaxime Ripard static void ccu_nkm_disable(struct clk_hw *hw)
54df6561e6SMaxime Ripard {
55df6561e6SMaxime Ripard 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
56df6561e6SMaxime Ripard 
57df6561e6SMaxime Ripard 	return ccu_gate_helper_disable(&nkm->common, nkm->enable);
58df6561e6SMaxime Ripard }
59df6561e6SMaxime Ripard 
60df6561e6SMaxime Ripard static int ccu_nkm_enable(struct clk_hw *hw)
61df6561e6SMaxime Ripard {
62df6561e6SMaxime Ripard 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
63df6561e6SMaxime Ripard 
64df6561e6SMaxime Ripard 	return ccu_gate_helper_enable(&nkm->common, nkm->enable);
65df6561e6SMaxime Ripard }
66df6561e6SMaxime Ripard 
67df6561e6SMaxime Ripard static int ccu_nkm_is_enabled(struct clk_hw *hw)
68df6561e6SMaxime Ripard {
69df6561e6SMaxime Ripard 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
70df6561e6SMaxime Ripard 
71df6561e6SMaxime Ripard 	return ccu_gate_helper_is_enabled(&nkm->common, nkm->enable);
72df6561e6SMaxime Ripard }
73df6561e6SMaxime Ripard 
74df6561e6SMaxime Ripard static unsigned long ccu_nkm_recalc_rate(struct clk_hw *hw,
75df6561e6SMaxime Ripard 					unsigned long parent_rate)
76df6561e6SMaxime Ripard {
77df6561e6SMaxime Ripard 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
78df6561e6SMaxime Ripard 	unsigned long n, m, k;
79df6561e6SMaxime Ripard 	u32 reg;
80df6561e6SMaxime Ripard 
81df6561e6SMaxime Ripard 	reg = readl(nkm->common.base + nkm->common.reg);
82df6561e6SMaxime Ripard 
83df6561e6SMaxime Ripard 	n = reg >> nkm->n.shift;
84df6561e6SMaxime Ripard 	n &= (1 << nkm->n.width) - 1;
85e66f81bbSMaxime Ripard 	n += nkm->n.offset;
86e66f81bbSMaxime Ripard 	if (!n)
87e66f81bbSMaxime Ripard 		n++;
88df6561e6SMaxime Ripard 
89df6561e6SMaxime Ripard 	k = reg >> nkm->k.shift;
90df6561e6SMaxime Ripard 	k &= (1 << nkm->k.width) - 1;
91e66f81bbSMaxime Ripard 	k += nkm->k.offset;
92e66f81bbSMaxime Ripard 	if (!k)
93e66f81bbSMaxime Ripard 		k++;
94df6561e6SMaxime Ripard 
95df6561e6SMaxime Ripard 	m = reg >> nkm->m.shift;
96df6561e6SMaxime Ripard 	m &= (1 << nkm->m.width) - 1;
97e66f81bbSMaxime Ripard 	m += nkm->m.offset;
98e66f81bbSMaxime Ripard 	if (!m)
99e66f81bbSMaxime Ripard 		m++;
100df6561e6SMaxime Ripard 
101e66f81bbSMaxime Ripard 	return parent_rate * n  * k / m;
102df6561e6SMaxime Ripard }
103df6561e6SMaxime Ripard 
104a3658359SChen-Yu Tsai static unsigned long ccu_nkm_round_rate(struct ccu_mux_internal *mux,
105a3658359SChen-Yu Tsai 					unsigned long parent_rate,
106a3658359SChen-Yu Tsai 					unsigned long rate,
107a3658359SChen-Yu Tsai 					void *data)
108df6561e6SMaxime Ripard {
109a3658359SChen-Yu Tsai 	struct ccu_nkm *nkm = data;
110df6561e6SMaxime Ripard 	struct _ccu_nkm _nkm;
111df6561e6SMaxime Ripard 
1122beaa601SMaxime Ripard 	_nkm.min_n = nkm->n.min;
113*0c3c8e13SMaxime Ripard 	_nkm.max_n = nkm->n.max ?: 1 << nkm->n.width;
1142beaa601SMaxime Ripard 	_nkm.min_k = nkm->k.min;
115*0c3c8e13SMaxime Ripard 	_nkm.max_k = nkm->k.max ?: 1 << nkm->k.width;
1166e0d50daSMaxime Ripard 	_nkm.min_m = 1;
11787ba9e59SMaxime Ripard 	_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
118df6561e6SMaxime Ripard 
119a3658359SChen-Yu Tsai 	ccu_nkm_find_best(parent_rate, rate, &_nkm);
120df6561e6SMaxime Ripard 
121a3658359SChen-Yu Tsai 	return parent_rate * _nkm.n * _nkm.k / _nkm.m;
122a3658359SChen-Yu Tsai }
123a3658359SChen-Yu Tsai 
124a3658359SChen-Yu Tsai static int ccu_nkm_determine_rate(struct clk_hw *hw,
125a3658359SChen-Yu Tsai 				  struct clk_rate_request *req)
126a3658359SChen-Yu Tsai {
127a3658359SChen-Yu Tsai 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
128a3658359SChen-Yu Tsai 
129a3658359SChen-Yu Tsai 	return ccu_mux_helper_determine_rate(&nkm->common, &nkm->mux,
130a3658359SChen-Yu Tsai 					     req, ccu_nkm_round_rate, nkm);
131df6561e6SMaxime Ripard }
132df6561e6SMaxime Ripard 
133df6561e6SMaxime Ripard static int ccu_nkm_set_rate(struct clk_hw *hw, unsigned long rate,
134df6561e6SMaxime Ripard 			   unsigned long parent_rate)
135df6561e6SMaxime Ripard {
136df6561e6SMaxime Ripard 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
137df6561e6SMaxime Ripard 	struct _ccu_nkm _nkm;
138df6561e6SMaxime Ripard 	unsigned long flags;
139df6561e6SMaxime Ripard 	u32 reg;
140df6561e6SMaxime Ripard 
1412beaa601SMaxime Ripard 	_nkm.min_n = nkm->n.min;
142*0c3c8e13SMaxime Ripard 	_nkm.max_n = nkm->n.max ?: 1 << nkm->n.width;
1432beaa601SMaxime Ripard 	_nkm.min_k = nkm->k.min;
144*0c3c8e13SMaxime Ripard 	_nkm.max_k = nkm->k.max ?: 1 << nkm->k.width;
1456e0d50daSMaxime Ripard 	_nkm.min_m = 1;
14687ba9e59SMaxime Ripard 	_nkm.max_m = nkm->m.max ?: 1 << nkm->m.width;
147df6561e6SMaxime Ripard 
148df6561e6SMaxime Ripard 	ccu_nkm_find_best(parent_rate, rate, &_nkm);
149df6561e6SMaxime Ripard 
150df6561e6SMaxime Ripard 	spin_lock_irqsave(nkm->common.lock, flags);
151df6561e6SMaxime Ripard 
152df6561e6SMaxime Ripard 	reg = readl(nkm->common.base + nkm->common.reg);
153df6561e6SMaxime Ripard 	reg &= ~GENMASK(nkm->n.width + nkm->n.shift - 1, nkm->n.shift);
154df6561e6SMaxime Ripard 	reg &= ~GENMASK(nkm->k.width + nkm->k.shift - 1, nkm->k.shift);
155df6561e6SMaxime Ripard 	reg &= ~GENMASK(nkm->m.width + nkm->m.shift - 1, nkm->m.shift);
156df6561e6SMaxime Ripard 
157e66f81bbSMaxime Ripard 	reg |= (_nkm.n - nkm->n.offset) << nkm->n.shift;
158e66f81bbSMaxime Ripard 	reg |= (_nkm.k - nkm->k.offset) << nkm->k.shift;
159e66f81bbSMaxime Ripard 	reg |= (_nkm.m - nkm->m.offset) << nkm->m.shift;
160df6561e6SMaxime Ripard 	writel(reg, nkm->common.base + nkm->common.reg);
161df6561e6SMaxime Ripard 
162df6561e6SMaxime Ripard 	spin_unlock_irqrestore(nkm->common.lock, flags);
163df6561e6SMaxime Ripard 
164df6561e6SMaxime Ripard 	ccu_helper_wait_for_lock(&nkm->common, nkm->lock);
165df6561e6SMaxime Ripard 
166df6561e6SMaxime Ripard 	return 0;
167df6561e6SMaxime Ripard }
168df6561e6SMaxime Ripard 
169a3658359SChen-Yu Tsai static u8 ccu_nkm_get_parent(struct clk_hw *hw)
170a3658359SChen-Yu Tsai {
171a3658359SChen-Yu Tsai 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
172a3658359SChen-Yu Tsai 
173a3658359SChen-Yu Tsai 	return ccu_mux_helper_get_parent(&nkm->common, &nkm->mux);
174a3658359SChen-Yu Tsai }
175a3658359SChen-Yu Tsai 
176a3658359SChen-Yu Tsai static int ccu_nkm_set_parent(struct clk_hw *hw, u8 index)
177a3658359SChen-Yu Tsai {
178a3658359SChen-Yu Tsai 	struct ccu_nkm *nkm = hw_to_ccu_nkm(hw);
179a3658359SChen-Yu Tsai 
180a3658359SChen-Yu Tsai 	return ccu_mux_helper_set_parent(&nkm->common, &nkm->mux, index);
181a3658359SChen-Yu Tsai }
182a3658359SChen-Yu Tsai 
183df6561e6SMaxime Ripard const struct clk_ops ccu_nkm_ops = {
184df6561e6SMaxime Ripard 	.disable	= ccu_nkm_disable,
185df6561e6SMaxime Ripard 	.enable		= ccu_nkm_enable,
186df6561e6SMaxime Ripard 	.is_enabled	= ccu_nkm_is_enabled,
187df6561e6SMaxime Ripard 
188a3658359SChen-Yu Tsai 	.get_parent	= ccu_nkm_get_parent,
189a3658359SChen-Yu Tsai 	.set_parent	= ccu_nkm_set_parent,
190a3658359SChen-Yu Tsai 
191a3658359SChen-Yu Tsai 	.determine_rate	= ccu_nkm_determine_rate,
192df6561e6SMaxime Ripard 	.recalc_rate	= ccu_nkm_recalc_rate,
193df6561e6SMaxime Ripard 	.set_rate	= ccu_nkm_set_rate,
194df6561e6SMaxime Ripard };
195