xref: /netbsd-src/sys/arch/arm/sunxi/sunxi_ccu_nkmp.c (revision 97f6729da6e501c7d546cdc06a76dcbf7509d978)
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 
39 int
sunxi_ccu_nkmp_enable(struct sunxi_ccu_softc * sc,struct sunxi_ccu_clk * clk,int enable)40 sunxi_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 
73 u_int
sunxi_ccu_nkmp_get_rate(struct sunxi_ccu_softc * sc,struct sunxi_ccu_clk * clk)74 sunxi_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 
136 int
sunxi_ccu_nkmp_set_rate(struct sunxi_ccu_softc * sc,struct sunxi_ccu_clk * clk,u_int rate)137 sunxi_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 
215 const char *
sunxi_ccu_nkmp_get_parent(struct sunxi_ccu_softc * sc,struct sunxi_ccu_clk * clk)216 sunxi_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