xref: /netbsd-src/sys/arch/arm/sunxi/sunxi_ccu_nkmp.c (revision 97f6729da6e501c7d546cdc06a76dcbf7509d978)
1*97f6729dSjmcneill /* $NetBSD: sunxi_ccu_nkmp.c,v 1.8 2019/01/02 19:33:06 jmcneill Exp $ */
211d415cdSjmcneill 
311d415cdSjmcneill /*-
411d415cdSjmcneill  * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
511d415cdSjmcneill  * All rights reserved.
611d415cdSjmcneill  *
711d415cdSjmcneill  * Redistribution and use in source and binary forms, with or without
811d415cdSjmcneill  * modification, are permitted provided that the following conditions
911d415cdSjmcneill  * are met:
1011d415cdSjmcneill  * 1. Redistributions of source code must retain the above copyright
1111d415cdSjmcneill  *    notice, this list of conditions and the following disclaimer.
1211d415cdSjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
1311d415cdSjmcneill  *    notice, this list of conditions and the following disclaimer in the
1411d415cdSjmcneill  *    documentation and/or other materials provided with the distribution.
1511d415cdSjmcneill  *
1611d415cdSjmcneill  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1711d415cdSjmcneill  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1811d415cdSjmcneill  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1911d415cdSjmcneill  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2011d415cdSjmcneill  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2111d415cdSjmcneill  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2211d415cdSjmcneill  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2311d415cdSjmcneill  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2411d415cdSjmcneill  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2511d415cdSjmcneill  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2611d415cdSjmcneill  * SUCH DAMAGE.
2711d415cdSjmcneill  */
2811d415cdSjmcneill 
2911d415cdSjmcneill #include <sys/cdefs.h>
30*97f6729dSjmcneill __KERNEL_RCSID(0, "$NetBSD: sunxi_ccu_nkmp.c,v 1.8 2019/01/02 19:33:06 jmcneill Exp $");
3111d415cdSjmcneill 
3211d415cdSjmcneill #include <sys/param.h>
3311d415cdSjmcneill #include <sys/bus.h>
3411d415cdSjmcneill 
3511d415cdSjmcneill #include <dev/clk/clk_backend.h>
3611d415cdSjmcneill 
3711d415cdSjmcneill #include <arm/sunxi/sunxi_ccu.h>
3811d415cdSjmcneill 
3911d415cdSjmcneill int
sunxi_ccu_nkmp_enable(struct sunxi_ccu_softc * sc,struct sunxi_ccu_clk * clk,int enable)4011d415cdSjmcneill sunxi_ccu_nkmp_enable(struct sunxi_ccu_softc *sc, struct sunxi_ccu_clk *clk,
4111d415cdSjmcneill     int enable)
4211d415cdSjmcneill {
4311d415cdSjmcneill 	struct sunxi_ccu_nkmp *nkmp = &clk->u.nkmp;
4411d415cdSjmcneill 	uint32_t val;
45ecf61253Sjmcneill 	int retry;
4611d415cdSjmcneill 
4711d415cdSjmcneill 	KASSERT(clk->type == SUNXI_CCU_NKMP);
4811d415cdSjmcneill 
4911d415cdSjmcneill 	if (!nkmp->enable)
5011d415cdSjmcneill 		return enable ? 0 : EINVAL;
5111d415cdSjmcneill 
5211d415cdSjmcneill 	val = CCU_READ(sc, nkmp->reg);
5311d415cdSjmcneill 	if (enable)
5411d415cdSjmcneill 		val |= nkmp->enable;
5511d415cdSjmcneill 	else
5611d415cdSjmcneill 		val &= ~nkmp->enable;
5711d415cdSjmcneill 	CCU_WRITE(sc, nkmp->reg, val);
5811d415cdSjmcneill 
59ecf61253Sjmcneill 	if (enable && nkmp->lock) {
60ecf61253Sjmcneill 		for (retry = 1000; retry > 0; retry--) {
61ecf61253Sjmcneill 			val = CCU_READ(sc, nkmp->reg);
62ecf61253Sjmcneill 			if (val & nkmp->lock)
63ecf61253Sjmcneill 				break;
64ecf61253Sjmcneill 			delay(100);
65ecf61253Sjmcneill 		}
66ecf61253Sjmcneill 		if (retry == 0)
67ecf61253Sjmcneill 			return ETIMEDOUT;
68ecf61253Sjmcneill 	}
69ecf61253Sjmcneill 
7011d415cdSjmcneill 	return 0;
7111d415cdSjmcneill }
7211d415cdSjmcneill 
7311d415cdSjmcneill u_int
sunxi_ccu_nkmp_get_rate(struct sunxi_ccu_softc * sc,struct sunxi_ccu_clk * clk)7411d415cdSjmcneill sunxi_ccu_nkmp_get_rate(struct sunxi_ccu_softc *sc,
7511d415cdSjmcneill     struct sunxi_ccu_clk *clk)
7611d415cdSjmcneill {
7711d415cdSjmcneill 	struct sunxi_ccu_nkmp *nkmp = &clk->u.nkmp;
7811d415cdSjmcneill 	struct clk *clkp, *clkp_parent;
7911d415cdSjmcneill 	u_int rate, n, k, m, p;
8011d415cdSjmcneill 	uint32_t val;
8111d415cdSjmcneill 
8211d415cdSjmcneill 	KASSERT(clk->type == SUNXI_CCU_NKMP);
8311d415cdSjmcneill 
8411d415cdSjmcneill 	clkp = &clk->base;
8511d415cdSjmcneill 	clkp_parent = clk_get_parent(clkp);
8611d415cdSjmcneill 	if (clkp_parent == NULL)
8711d415cdSjmcneill 		return 0;
8811d415cdSjmcneill 
8911d415cdSjmcneill 	rate = clk_get_rate(clkp_parent);
9011d415cdSjmcneill 	if (rate == 0)
9111d415cdSjmcneill 		return 0;
9211d415cdSjmcneill 
9311d415cdSjmcneill 	val = CCU_READ(sc, nkmp->reg);
946685c529Sjmcneill 	if (nkmp->n)
9511d415cdSjmcneill 		n = __SHIFTOUT(val, nkmp->n);
966685c529Sjmcneill 	else
976685c529Sjmcneill 		n = 0;
986685c529Sjmcneill 	if (nkmp->k)
9911d415cdSjmcneill 		k = __SHIFTOUT(val, nkmp->k);
1006685c529Sjmcneill 	else
1016685c529Sjmcneill 		k = 0;
10211d415cdSjmcneill 	if (nkmp->m)
10311d415cdSjmcneill 		m = __SHIFTOUT(val, nkmp->m);
10411d415cdSjmcneill 	else
10511d415cdSjmcneill 		m = 0;
106ddb9dd9cSjmcneill 	if (nkmp->p)
10711d415cdSjmcneill 		p = __SHIFTOUT(val, nkmp->p);
108ddb9dd9cSjmcneill 	else
109ddb9dd9cSjmcneill 		p = 0;
11011d415cdSjmcneill 
11111d415cdSjmcneill 	if (nkmp->enable && !(val & nkmp->enable))
11211d415cdSjmcneill 		return 0;
11311d415cdSjmcneill 
1146685c529Sjmcneill 	if ((nkmp->flags & SUNXI_CCU_NKMP_FACTOR_N_EXACT) == 0)
11511d415cdSjmcneill 		n++;
116b3e175fcSjmcneill 
1176dec99e3Sjmcneill 	if ((nkmp->flags & SUNXI_CCU_NKMP_FACTOR_N_ZERO_IS_ONE) != 0 && n == 0)
1186dec99e3Sjmcneill 		n++;
1196dec99e3Sjmcneill 
12011d415cdSjmcneill 	k++;
121b3e175fcSjmcneill 
122b3e175fcSjmcneill 	if ((nkmp->flags & SUNXI_CCU_NKMP_FACTOR_P_POW2) != 0)
123b3e175fcSjmcneill 		p = 1 << p;
124*97f6729dSjmcneill 	else if ((nkmp->flags & SUNXI_CCU_NKMP_FACTOR_P_X4) != 0)
125*97f6729dSjmcneill 		p = p ? 4 : 1;
126b3e175fcSjmcneill 	else
12711d415cdSjmcneill 		p++;
12811d415cdSjmcneill 
129b3e175fcSjmcneill 	m++;
13068166010Sjmcneill 	if (nkmp->flags & SUNXI_CCU_NKMP_DIVIDE_BY_TWO)
13168166010Sjmcneill 		m *= 2;
13268166010Sjmcneill 
13311d415cdSjmcneill 	return (u_int)((uint64_t)rate * n * k) / (m * p);
13411d415cdSjmcneill }
13511d415cdSjmcneill 
13611d415cdSjmcneill int
sunxi_ccu_nkmp_set_rate(struct sunxi_ccu_softc * sc,struct sunxi_ccu_clk * clk,u_int rate)13711d415cdSjmcneill sunxi_ccu_nkmp_set_rate(struct sunxi_ccu_softc *sc,
13811d415cdSjmcneill     struct sunxi_ccu_clk *clk, u_int rate)
13911d415cdSjmcneill {
140ecf61253Sjmcneill 	struct sunxi_ccu_nkmp *nkmp = &clk->u.nkmp;
141ecf61253Sjmcneill 	const struct sunxi_ccu_nkmp_tbl *tab;
142ecf61253Sjmcneill 	uint32_t val;
143ecf61253Sjmcneill 
144ecf61253Sjmcneill 	KASSERT(clk->type == SUNXI_CCU_NKMP);
145ecf61253Sjmcneill 
146ecf61253Sjmcneill 	if (nkmp->table == NULL || rate == 0)
14711d415cdSjmcneill 		return EIO;
148ecf61253Sjmcneill 
149ecf61253Sjmcneill 	for (tab = nkmp->table; tab->rate > 0; tab++)
150ecf61253Sjmcneill 		if (tab->rate == rate)
151ecf61253Sjmcneill 			break;
152ecf61253Sjmcneill 	if (tab->rate == 0)
153ecf61253Sjmcneill 		return EINVAL;
154ecf61253Sjmcneill 
155ecf61253Sjmcneill 	val = CCU_READ(sc, nkmp->reg);
156b3e175fcSjmcneill 
157b3e175fcSjmcneill 	if (nkmp->flags & SUNXI_CCU_NKMP_SCALE_CLOCK) {
158b3e175fcSjmcneill 		if (nkmp->p && __SHIFTOUT(val, nkmp->p) < tab->p) {
159b3e175fcSjmcneill 			val &= ~nkmp->p;
160b3e175fcSjmcneill 			val |= __SHIFTIN(tab->p, nkmp->p);
161b3e175fcSjmcneill 			CCU_WRITE(sc, nkmp->reg, val);
162b3e175fcSjmcneill 			delay(2000);
163b3e175fcSjmcneill 		}
164b3e175fcSjmcneill 		if (nkmp->m && __SHIFTOUT(val, nkmp->m) < tab->m) {
165b3e175fcSjmcneill 			val &= ~nkmp->m;
166b3e175fcSjmcneill 			val |= __SHIFTIN(tab->m, nkmp->m);
167b3e175fcSjmcneill 			CCU_WRITE(sc, nkmp->reg, val);
168b3e175fcSjmcneill 			delay(2000);
169b3e175fcSjmcneill 		}
170b3e175fcSjmcneill 		if (nkmp->n) {
171b3e175fcSjmcneill 			val &= ~nkmp->n;
172b3e175fcSjmcneill 			val |= __SHIFTIN(tab->n, nkmp->n);
173b3e175fcSjmcneill 		}
174b3e175fcSjmcneill 		if (nkmp->k) {
175b3e175fcSjmcneill 			val &= ~nkmp->k;
176b3e175fcSjmcneill 			val |= __SHIFTIN(tab->k, nkmp->k);
177b3e175fcSjmcneill 		}
178b3e175fcSjmcneill 		CCU_WRITE(sc, nkmp->reg, val);
179b3e175fcSjmcneill 		delay(2000);
180b3e175fcSjmcneill 		if (nkmp->m && __SHIFTOUT(val, nkmp->m) > tab->m) {
181b3e175fcSjmcneill 			val &= ~nkmp->m;
182b3e175fcSjmcneill 			val |= __SHIFTIN(tab->m, nkmp->m);
183b3e175fcSjmcneill 			CCU_WRITE(sc, nkmp->reg, val);
184b3e175fcSjmcneill 			delay(2000);
185b3e175fcSjmcneill 		}
186b3e175fcSjmcneill 		if (nkmp->p && __SHIFTOUT(val, nkmp->p) > tab->p) {
187b3e175fcSjmcneill 			val &= ~nkmp->p;
188b3e175fcSjmcneill 			val |= __SHIFTIN(tab->p, nkmp->p);
189b3e175fcSjmcneill 			CCU_WRITE(sc, nkmp->reg, val);
190b3e175fcSjmcneill 			delay(2000);
191b3e175fcSjmcneill 		}
192b3e175fcSjmcneill 	} else {
193ecf61253Sjmcneill 		if (nkmp->n) {
194ecf61253Sjmcneill 			val &= ~nkmp->n;
195ecf61253Sjmcneill 			val |= __SHIFTIN(tab->n, nkmp->n);
196ecf61253Sjmcneill 		}
197ecf61253Sjmcneill 		if (nkmp->k) {
198ecf61253Sjmcneill 			val &= ~nkmp->k;
199ecf61253Sjmcneill 			val |= __SHIFTIN(tab->k, nkmp->k);
200ecf61253Sjmcneill 		}
201ecf61253Sjmcneill 		if (nkmp->m) {
202ecf61253Sjmcneill 			val &= ~nkmp->m;
203ecf61253Sjmcneill 			val |= __SHIFTIN(tab->m, nkmp->m);
204ecf61253Sjmcneill 		}
205ecf61253Sjmcneill 		if (nkmp->p) {
206ecf61253Sjmcneill 			val &= ~nkmp->p;
207ecf61253Sjmcneill 			val |= __SHIFTIN(tab->p, nkmp->p);
208ecf61253Sjmcneill 		}
209ecf61253Sjmcneill 		CCU_WRITE(sc, nkmp->reg, val);
210b3e175fcSjmcneill 	}
211ecf61253Sjmcneill 
212ecf61253Sjmcneill 	return 0;
21311d415cdSjmcneill }
21411d415cdSjmcneill 
21511d415cdSjmcneill const char *
sunxi_ccu_nkmp_get_parent(struct sunxi_ccu_softc * sc,struct sunxi_ccu_clk * clk)21611d415cdSjmcneill sunxi_ccu_nkmp_get_parent(struct sunxi_ccu_softc *sc,
21711d415cdSjmcneill     struct sunxi_ccu_clk *clk)
21811d415cdSjmcneill {
21911d415cdSjmcneill 	struct sunxi_ccu_nkmp *nkmp = &clk->u.nkmp;
22011d415cdSjmcneill 
22111d415cdSjmcneill 	KASSERT(clk->type == SUNXI_CCU_NKMP);
22211d415cdSjmcneill 
22311d415cdSjmcneill 	return nkmp->parent;
22411d415cdSjmcneill }
225