1 /* $NetBSD: sunxi_ccu_nkmp.c,v 1.10 2026/10/04 07:45:07 rxg Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill (at) 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.10 2026/10/04 07:45:07 rxg 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 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 46 KASSERT(clk->type == SUNXI_CCU_NKMP); 47 48 if (!nkmp->enable) 49 return enable ? 0 : EINVAL; 50 51 val = CCU_READ(sc, nkmp->reg); 52 if (enable) 53 val |= nkmp->enable; 54 else 55 val &= ~nkmp->enable; 56 CCU_WRITE(sc, nkmp->reg, val); 57 58 return 0; 59 } 60 61 u_int 62 sunxi_ccu_nkmp_get_rate(struct sunxi_ccu_softc *sc, 63 struct sunxi_ccu_clk *clk) 64 { 65 struct sunxi_ccu_nkmp *nkmp = &clk->u.nkmp; 66 const struct sunxi_ccu_nkmp_tbl *tab; 67 struct clk *clkp, *clkp_parent; 68 u_int rate, n, k, m, p; 69 uint32_t val; 70 71 KASSERT(clk->type == SUNXI_CCU_NKMP); 72 73 clkp = &clk->base; 74 clkp_parent = clk_get_parent(clkp); 75 if (clkp_parent == NULL) 76 return 0; 77 78 rate = clk_get_rate(clkp_parent); 79 if (rate == 0) 80 return 0; 81 82 val = CCU_READ(sc, nkmp->reg); 83 if (nkmp->n) 84 n = __SHIFTOUT(val, nkmp->n); 85 else 86 n = 0; 87 if (nkmp->k) 88 k = __SHIFTOUT(val, nkmp->k); 89 else 90 k = 0; 91 if (nkmp->m) 92 m = __SHIFTOUT(val, nkmp->m); 93 else 94 m = 0; 95 if (nkmp->p) 96 p = __SHIFTOUT(val, nkmp->p); 97 else 98 p = 0; 99 100 if (nkmp->enable && !(val & nkmp->enable)) 101 return 0; 102 103 if (nkmp->sdm_en && (val & nkmp->sdm_en) && nkmp->pat != NULL) { 104 const uint32_t pat_val = CCU_READ(sc, nkmp->pat->reg); 105 for (tab = nkmp->table; tab->rate > 0; tab++) 106 if (tab->pat == pat_val) 107 break; 108 if (tab->rate == 0) 109 return 0; 110 if (tab->n != n || tab->k != k || tab->m != m || tab->p != p) 111 return 0; 112 return tab->rate; 113 } 114 115 if ((nkmp->flags & SUNXI_CCU_NKMP_FACTOR_N_EXACT) == 0) 116 n++; 117 118 if ((nkmp->flags & SUNXI_CCU_NKMP_FACTOR_N_ZERO_IS_ONE) != 0 && n == 0) 119 n++; 120 121 k++; 122 123 if ((nkmp->flags & SUNXI_CCU_NKMP_FACTOR_P_POW2) != 0) 124 p = 1 << p; 125 else if ((nkmp->flags & SUNXI_CCU_NKMP_FACTOR_P_X4) != 0) 126 p = p ? 4 : 1; 127 else 128 p++; 129 130 m++; 131 if (nkmp->flags & SUNXI_CCU_NKMP_DIVIDE_BY_TWO) 132 m *= 2; 133 134 return (u_int)((uint64_t)rate * n * k) / (m * p); 135 } 136 137 int 138 sunxi_ccu_nkmp_set_rate(struct sunxi_ccu_softc *sc, 139 struct sunxi_ccu_clk *clk, u_int rate) 140 { 141 struct sunxi_ccu_nkmp *nkmp = &clk->u.nkmp; 142 const struct sunxi_ccu_nkmp_tbl *tab; 143 uint32_t val; 144 int retry; 145 146 KASSERT(clk->type == SUNXI_CCU_NKMP); 147 148 if (nkmp->table == NULL || rate == 0) 149 return EIO; 150 151 for (tab = nkmp->table; tab->rate > 0; tab++) 152 if (tab->rate == rate) 153 break; 154 if (tab->rate == 0) 155 return EINVAL; 156 157 val = CCU_READ(sc, nkmp->reg); 158 if (nkmp->sdm_en) 159 val |= nkmp->sdm_en; 160 161 if (nkmp->flags & SUNXI_CCU_NKMP_SCALE_CLOCK) { 162 if (nkmp->p && __SHIFTOUT(val, nkmp->p) < tab->p) { 163 val &= ~nkmp->p; 164 val |= __SHIFTIN(tab->p, nkmp->p); 165 CCU_WRITE(sc, nkmp->reg, val); 166 delay(2000); 167 } 168 if (nkmp->m && __SHIFTOUT(val, nkmp->m) < tab->m) { 169 val &= ~nkmp->m; 170 val |= __SHIFTIN(tab->m, nkmp->m); 171 CCU_WRITE(sc, nkmp->reg, val); 172 delay(2000); 173 } 174 if (nkmp->n) { 175 val &= ~nkmp->n; 176 val |= __SHIFTIN(tab->n, nkmp->n); 177 } 178 if (nkmp->k) { 179 val &= ~nkmp->k; 180 val |= __SHIFTIN(tab->k, nkmp->k); 181 } 182 CCU_WRITE(sc, nkmp->reg, val); 183 delay(2000); 184 if (nkmp->m && __SHIFTOUT(val, nkmp->m) > tab->m) { 185 val &= ~nkmp->m; 186 val |= __SHIFTIN(tab->m, nkmp->m); 187 CCU_WRITE(sc, nkmp->reg, val); 188 delay(2000); 189 } 190 if (nkmp->p && __SHIFTOUT(val, nkmp->p) > tab->p) { 191 val &= ~nkmp->p; 192 val |= __SHIFTIN(tab->p, nkmp->p); 193 CCU_WRITE(sc, nkmp->reg, val); 194 delay(2000); 195 } 196 } else { 197 if (nkmp->n) { 198 val &= ~nkmp->n; 199 val |= __SHIFTIN(tab->n, nkmp->n); 200 } 201 if (nkmp->k) { 202 val &= ~nkmp->k; 203 val |= __SHIFTIN(tab->k, nkmp->k); 204 } 205 if (nkmp->m) { 206 val &= ~nkmp->m; 207 val |= __SHIFTIN(tab->m, nkmp->m); 208 } 209 if (nkmp->p) { 210 val &= ~nkmp->p; 211 val |= __SHIFTIN(tab->p, nkmp->p); 212 } 213 CCU_WRITE(sc, nkmp->reg, val); 214 } 215 216 if (nkmp->pat != NULL) 217 CCU_WRITE(sc, nkmp->pat->reg, tab->pat); 218 219 if (nkmp->lock) { 220 for (retry = 1000; retry > 0; retry--) { 221 val = CCU_READ(sc, nkmp->reg); 222 if (val & nkmp->lock) 223 break; 224 delay(100); 225 } 226 if (retry == 0) 227 return ETIMEDOUT; 228 } 229 230 return 0; 231 } 232 233 const char * 234 sunxi_ccu_nkmp_get_parent(struct sunxi_ccu_softc *sc, 235 struct sunxi_ccu_clk *clk) 236 { 237 struct sunxi_ccu_nkmp *nkmp = &clk->u.nkmp; 238 239 KASSERT(clk->type == SUNXI_CCU_NKMP); 240 241 return nkmp->parent; 242 } 243