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