1/* $NetBSD: sunxi_ccu_nkmp.c,v 1.8 2019/01/02 19:33:06 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: sunxi_ccu_nkmp.c,v 1.8 2019/01/02 19:33:06 jmcneill Exp $");
31
32#include <sys/param.h>
33#include <sys/bus.h>
34
35#include <dev/clk/clk_backend.h>
36
37#include <arm/sunxi/sunxi_ccu.h>
38
39int
40sunxi_ccu_nkmp_enable(struct sunxi_ccu_softc *sc, struct sunxi_ccu_clk *clk,
41    int enable)
42{
43	struct sunxi_ccu_nkmp *nkmp = &clk->u.nkmp;
44	uint32_t val;
45	int retry;
46
47	KASSERT(clk->type == SUNXI_CCU_NKMP);
48
49	if (!nkmp->enable)
50		return enable ? 0 : EINVAL;
51
52	val = CCU_READ(sc, nkmp->reg);
53	if (enable)
54		val |= nkmp->enable;
55	else
56		val &= ~nkmp->enable;
57	CCU_WRITE(sc, nkmp->reg, val);
58
59	if (enable && nkmp->lock) {
60		for (retry = 1000; retry > 0; retry--) {
61			val = CCU_READ(sc, nkmp->reg);
62			if (val & nkmp->lock)
63				break;
64			delay(100);
65		}
66		if (retry == 0)
67			return ETIMEDOUT;
68	}
69
70	return 0;
71}
72
73u_int
74sunxi_ccu_nkmp_get_rate(struct sunxi_ccu_softc *sc,
75    struct sunxi_ccu_clk *clk)
76{
77	struct sunxi_ccu_nkmp *nkmp = &clk->u.nkmp;
78	struct clk *clkp, *clkp_parent;
79	u_int rate, n, k, m, p;
80	uint32_t val;
81
82	KASSERT(clk->type == SUNXI_CCU_NKMP);
83
84	clkp = &clk->base;
85	clkp_parent = clk_get_parent(clkp);
86	if (clkp_parent == NULL)
87		return 0;
88
89	rate = clk_get_rate(clkp_parent);
90	if (rate == 0)
91		return 0;
92
93	val = CCU_READ(sc, nkmp->reg);
94	if (nkmp->n)
95		n = __SHIFTOUT(val, nkmp->n);
96	else
97		n = 0;
98	if (nkmp->k)
99		k = __SHIFTOUT(val, nkmp->k);
100	else
101		k = 0;
102	if (nkmp->m)
103		m = __SHIFTOUT(val, nkmp->m);
104	else
105		m = 0;
106	if (nkmp->p)
107		p = __SHIFTOUT(val, nkmp->p);
108	else
109		p = 0;
110
111	if (nkmp->enable && !(val & nkmp->enable))
112		return 0;
113
114	if ((nkmp->flags & SUNXI_CCU_NKMP_FACTOR_N_EXACT) == 0)
115		n++;
116
117	if ((nkmp->flags & SUNXI_CCU_NKMP_FACTOR_N_ZERO_IS_ONE) != 0 && n == 0)
118		n++;
119
120	k++;
121
122	if ((nkmp->flags & SUNXI_CCU_NKMP_FACTOR_P_POW2) != 0)
123		p = 1 << p;
124	else if ((nkmp->flags & SUNXI_CCU_NKMP_FACTOR_P_X4) != 0)
125		p = p ? 4 : 1;
126	else
127		p++;
128
129	m++;
130	if (nkmp->flags & SUNXI_CCU_NKMP_DIVIDE_BY_TWO)
131		m *= 2;
132
133	return (u_int)((uint64_t)rate * n * k) / (m * p);
134}
135
136int
137sunxi_ccu_nkmp_set_rate(struct sunxi_ccu_softc *sc,
138    struct sunxi_ccu_clk *clk, u_int rate)
139{
140	struct sunxi_ccu_nkmp *nkmp = &clk->u.nkmp;
141	const struct sunxi_ccu_nkmp_tbl *tab;
142	uint32_t val;
143
144	KASSERT(clk->type == SUNXI_CCU_NKMP);
145
146	if (nkmp->table == NULL || rate == 0)
147		return EIO;
148
149	for (tab = nkmp->table; tab->rate > 0; tab++)
150		if (tab->rate == rate)
151			break;
152	if (tab->rate == 0)
153		return EINVAL;
154
155	val = CCU_READ(sc, nkmp->reg);
156
157	if (nkmp->flags & SUNXI_CCU_NKMP_SCALE_CLOCK) {
158		if (nkmp->p && __SHIFTOUT(val, nkmp->p) < tab->p) {
159			val &= ~nkmp->p;
160			val |= __SHIFTIN(tab->p, nkmp->p);
161			CCU_WRITE(sc, nkmp->reg, val);
162			delay(2000);
163		}
164		if (nkmp->m && __SHIFTOUT(val, nkmp->m) < tab->m) {
165			val &= ~nkmp->m;
166			val |= __SHIFTIN(tab->m, nkmp->m);
167			CCU_WRITE(sc, nkmp->reg, val);
168			delay(2000);
169		}
170		if (nkmp->n) {
171			val &= ~nkmp->n;
172			val |= __SHIFTIN(tab->n, nkmp->n);
173		}
174		if (nkmp->k) {
175			val &= ~nkmp->k;
176			val |= __SHIFTIN(tab->k, nkmp->k);
177		}
178		CCU_WRITE(sc, nkmp->reg, val);
179		delay(2000);
180		if (nkmp->m && __SHIFTOUT(val, nkmp->m) > tab->m) {
181			val &= ~nkmp->m;
182			val |= __SHIFTIN(tab->m, nkmp->m);
183			CCU_WRITE(sc, nkmp->reg, val);
184			delay(2000);
185		}
186		if (nkmp->p && __SHIFTOUT(val, nkmp->p) > tab->p) {
187			val &= ~nkmp->p;
188			val |= __SHIFTIN(tab->p, nkmp->p);
189			CCU_WRITE(sc, nkmp->reg, val);
190			delay(2000);
191		}
192	} else {
193		if (nkmp->n) {
194			val &= ~nkmp->n;
195			val |= __SHIFTIN(tab->n, nkmp->n);
196		}
197		if (nkmp->k) {
198			val &= ~nkmp->k;
199			val |= __SHIFTIN(tab->k, nkmp->k);
200		}
201		if (nkmp->m) {
202			val &= ~nkmp->m;
203			val |= __SHIFTIN(tab->m, nkmp->m);
204		}
205		if (nkmp->p) {
206			val &= ~nkmp->p;
207			val |= __SHIFTIN(tab->p, nkmp->p);
208		}
209		CCU_WRITE(sc, nkmp->reg, val);
210	}
211
212	return 0;
213}
214
215const char *
216sunxi_ccu_nkmp_get_parent(struct sunxi_ccu_softc *sc,
217    struct sunxi_ccu_clk *clk)
218{
219	struct sunxi_ccu_nkmp *nkmp = &clk->u.nkmp;
220
221	KASSERT(clk->type == SUNXI_CCU_NKMP);
222
223	return nkmp->parent;
224}
225