1/* $NetBSD: rk_cru_arm.c,v 1.5 2022/08/23 05:39:06 ryo Exp $ */ 2 3/*- 4 * Copyright (c) 2018 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: rk_cru_arm.c,v 1.5 2022/08/23 05:39:06 ryo Exp $"); 31 32#include <sys/param.h> 33#include <sys/bus.h> 34 35#include <dev/clk/clk_backend.h> 36 37#include <arm/rockchip/rk_cru.h> 38 39u_int 40rk_cru_arm_get_rate(struct rk_cru_softc *sc, 41 struct rk_cru_clk *clk) 42{ 43 struct rk_cru_arm *arm = &clk->u.arm; 44 struct clk *clkp, *clkp_parent; 45 46 KASSERT(clk->type == RK_CRU_ARM); 47 48 clkp = &clk->base; 49 clkp_parent = clk_get_parent(clkp); 50 if (clkp_parent == NULL) 51 return 0; 52 53 const u_int fref = clk_get_rate(clkp_parent); 54 if (fref == 0) 55 return 0; 56 57 const uint32_t val = CRU_READ(sc, arm->divs[0].reg); 58 const u_int div = __SHIFTOUT(val, arm->divs[0].mask) + 1; 59 60 return fref / div; 61} 62 63static int 64rk_cru_arm_set_rate_rates(struct rk_cru_softc *sc, 65 struct rk_cru_clk *clk, u_int rate) 66{ 67 struct rk_cru_arm *arm = &clk->u.arm; 68 struct rk_cru_clk *main_parent, *alt_parent; 69 const struct rk_cru_arm_rate *arm_rate = NULL; 70 int error; 71 72 KASSERT(clk->type == RK_CRU_ARM); 73 74 if (arm->rates == NULL || rate == 0) 75 return EIO; 76 77 for (int i = 0; i < arm->nrates; i++) 78 if (arm->rates[i].rate == rate) { 79 arm_rate = &arm->rates[i]; 80 break; 81 } 82 if (arm_rate == NULL) 83 return EINVAL; 84 85 main_parent = rk_cru_clock_find(sc, arm->parents[arm->mux_main]); 86 alt_parent = rk_cru_clock_find(sc, arm->parents[arm->mux_alt]); 87 if (main_parent == NULL || alt_parent == NULL) { 88 device_printf(sc->sc_dev, "couldn't get clock parents\n"); 89 return ENXIO; 90 } 91 92 error = rk_cru_arm_set_parent(sc, clk, arm->parents[arm->mux_alt]); 93 if (error != 0) 94 return error; 95 96 const u_int parent_rate = arm_rate->rate / arm_rate->div; 97 98 error = clk_set_rate(&main_parent->base, parent_rate); 99 if (error != 0) 100 goto done; 101 102 for (int i = 0; i < __arraycount(arm->divs); i++) { 103 if (arm->divs[i].reg == 0 && arm->divs[i].mask == 0) 104 break; 105 106 const uint32_t write_mask = arm->divs[i].mask << 16; 107 const uint32_t write_val = __SHIFTIN(arm_rate->div - 1, 108 arm->divs[i].mask); 109 CRU_WRITE(sc, arm->divs[i].reg, write_mask | write_val); 110 } 111 112done: 113 rk_cru_arm_set_parent(sc, clk, arm->parents[arm->mux_main]); 114 return error; 115} 116 117static int 118rk_cru_arm_set_rate_cpurates(struct rk_cru_softc *sc, 119 struct rk_cru_clk *clk, u_int rate) 120{ 121 struct rk_cru_arm *arm = &clk->u.arm; 122 struct rk_cru_clk *main_parent, *alt_parent; 123 const struct rk_cru_cpu_rate *cpu_rate = NULL; 124 uint32_t write_mask, write_val; 125 int error; 126 127 KASSERT(clk->type == RK_CRU_ARM); 128 129 if (arm->cpurates == NULL || rate == 0) 130 return EIO; 131 132 for (int i = 0; i < arm->nrates; i++) 133 if (arm->cpurates[i].rate == rate) { 134 cpu_rate = &arm->cpurates[i]; 135 break; 136 } 137 if (cpu_rate == NULL) 138 return EINVAL; 139 140 main_parent = rk_cru_clock_find(sc, arm->parents[arm->mux_main]); 141 alt_parent = rk_cru_clock_find(sc, arm->parents[arm->mux_alt]); 142 if (main_parent == NULL || alt_parent == NULL) { 143 device_printf(sc->sc_dev, "couldn't get clock parents\n"); 144 return ENXIO; 145 } 146 147 /* XXX: TODO: apply cpu_rate->pre_muxs[] */ 148 149 error = rk_cru_arm_set_parent(sc, clk, arm->parents[arm->mux_alt]); 150 if (error != 0) 151 return error; 152 153 error = clk_set_rate(&main_parent->base, rate); 154 if (error != 0) 155 goto done; 156 157 for (int i = 0; i < __arraycount(cpu_rate->divs); i++) { 158 if (cpu_rate->divs[i].reg == 0 && cpu_rate->divs[i].mask == 0 && 159 cpu_rate->divs[i].val == 0) 160 break; 161 162 write_mask = cpu_rate->divs[i].mask << 16; 163 write_val = cpu_rate->divs[i].val; 164 CRU_WRITE(sc, cpu_rate->divs[i].reg, write_mask | write_val); 165 } 166 167 for (int i = 0; i < __arraycount(arm->divs); i++) { 168 if (arm->divs[i].reg == 0 && arm->divs[i].mask == 0) 169 break; 170 171 write_mask = arm->divs[i].mask << 16; 172 write_val = __SHIFTIN(0, arm->divs[i].mask); 173 CRU_WRITE(sc, arm->divs[i].reg, write_mask | write_val); 174 } 175 176 /* XXX: TODO: apply cpu_rate->post_muxs[] */ 177 178done: 179 rk_cru_arm_set_parent(sc, clk, arm->parents[arm->mux_main]); 180 return error; 181} 182 183 184int 185rk_cru_arm_set_rate(struct rk_cru_softc *sc, 186 struct rk_cru_clk *clk, u_int rate) 187{ 188 struct rk_cru_arm *arm = &clk->u.arm; 189 190 if (arm->rates) 191 return rk_cru_arm_set_rate_rates(sc, clk, rate); 192 else if (arm->cpurates) 193 return rk_cru_arm_set_rate_cpurates(sc, clk, rate); 194 else 195 return EIO; 196} 197 198const char * 199rk_cru_arm_get_parent(struct rk_cru_softc *sc, 200 struct rk_cru_clk *clk) 201{ 202 struct rk_cru_arm *arm = &clk->u.arm; 203 204 KASSERT(clk->type == RK_CRU_ARM); 205 206 const uint32_t val = CRU_READ(sc, arm->mux_reg); 207 const u_int mux = __SHIFTOUT(val, arm->mux_mask); 208 209 return arm->parents[mux]; 210} 211 212int 213rk_cru_arm_set_parent(struct rk_cru_softc *sc, 214 struct rk_cru_clk *clk, const char *parent) 215{ 216 struct rk_cru_arm *arm = &clk->u.arm; 217 218 KASSERT(clk->type == RK_CRU_ARM); 219 220 for (u_int mux = 0; mux < arm->nparents; mux++) 221 if (strcmp(arm->parents[mux], parent) == 0) { 222 const uint32_t write_mask = arm->mux_mask << 16; 223 const uint32_t write_val = __SHIFTIN(mux, arm->mux_mask); 224 225 CRU_WRITE(sc, arm->mux_reg, write_mask | write_val); 226 return 0; 227 } 228 229 return EINVAL; 230} 231