Home | History | Annotate | Line # | Download | only in rockchip
rk_cru_arm.c revision 1.3
      1 /* $NetBSD: rk_cru_arm.c,v 1.3 2022/08/23 05:32:18 ryo Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2018 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: rk_cru_arm.c,v 1.3 2022/08/23 05:32:18 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 
     39 u_int
     40 rk_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->reg);
     58 	const u_int div = __SHIFTOUT(val, arm->div_mask) + 1;
     59 
     60 	return fref / div;
     61 }
     62 
     63 static int
     64 rk_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 	const uint32_t write_mask = arm->div_mask << 16;
    103 	const uint32_t write_val = __SHIFTIN(arm_rate->div - 1, arm->div_mask);
    104 
    105 	CRU_WRITE(sc, arm->reg, write_mask | write_val);
    106 
    107 done:
    108 	rk_cru_arm_set_parent(sc, clk, arm->parents[arm->mux_main]);
    109 	return error;
    110 }
    111 
    112 static int
    113 rk_cru_arm_set_rate_cpurates(struct rk_cru_softc *sc,
    114     struct rk_cru_clk *clk, u_int rate)
    115 {
    116 	struct rk_cru_arm *arm = &clk->u.arm;
    117 	struct rk_cru_clk *main_parent, *alt_parent;
    118 	const struct rk_cru_cpu_rate *cpu_rate = NULL;
    119 	uint32_t write_mask, write_val;
    120 	int error;
    121 
    122 	KASSERT(clk->type == RK_CRU_ARM);
    123 
    124 	if (arm->cpurates == NULL || rate == 0)
    125 		return EIO;
    126 
    127 	for (int i = 0; i < arm->nrates; i++)
    128 		if (arm->cpurates[i].rate == rate) {
    129 			cpu_rate = &arm->cpurates[i];
    130 			break;
    131 		}
    132 	if (cpu_rate == NULL)
    133 		return EINVAL;
    134 
    135 	main_parent = rk_cru_clock_find(sc, arm->parents[arm->mux_main]);
    136 	alt_parent = rk_cru_clock_find(sc, arm->parents[arm->mux_alt]);
    137 	if (main_parent == NULL || alt_parent == NULL) {
    138 		device_printf(sc->sc_dev, "couldn't get clock parents\n");
    139 		return ENXIO;
    140 	}
    141 
    142 	error = rk_cru_arm_set_parent(sc, clk, arm->parents[arm->mux_alt]);
    143 	if (error != 0)
    144 		return error;
    145 
    146 	error = clk_set_rate(&main_parent->base, rate);
    147 	if (error != 0)
    148 		goto done;
    149 
    150 	for (int i = 0; i < __arraycount(cpu_rate->divs); i++) {
    151 		write_mask = cpu_rate->divs[i].mask << 16;
    152 		write_val = cpu_rate->divs[i].val;
    153 		CRU_WRITE(sc, cpu_rate->divs[i].reg, write_mask | write_val);
    154 	}
    155 
    156 	write_mask = arm->div_mask << 16;
    157 	write_val = __SHIFTIN(0, arm->div_mask);
    158 	CRU_WRITE(sc, arm->reg, write_mask | write_val);
    159 
    160 done:
    161 	rk_cru_arm_set_parent(sc, clk, arm->parents[arm->mux_main]);
    162 	return error;
    163 }
    164 
    165 
    166 int
    167 rk_cru_arm_set_rate(struct rk_cru_softc *sc,
    168     struct rk_cru_clk *clk, u_int rate)
    169 {
    170 	struct rk_cru_arm *arm = &clk->u.arm;
    171 
    172 	if (arm->rates)
    173 		return rk_cru_arm_set_rate_rates(sc, clk, rate);
    174 	else if (arm->cpurates)
    175 		return rk_cru_arm_set_rate_cpurates(sc, clk, rate);
    176 	else
    177 		return EIO;
    178 }
    179 
    180 const char *
    181 rk_cru_arm_get_parent(struct rk_cru_softc *sc,
    182     struct rk_cru_clk *clk)
    183 {
    184 	struct rk_cru_arm *arm = &clk->u.arm;
    185 
    186 	KASSERT(clk->type == RK_CRU_ARM);
    187 
    188 	const uint32_t val = CRU_READ(sc, arm->reg);
    189 	const u_int mux = __SHIFTOUT(val, arm->mux_mask);
    190 
    191 	return arm->parents[mux];
    192 }
    193 
    194 int
    195 rk_cru_arm_set_parent(struct rk_cru_softc *sc,
    196     struct rk_cru_clk *clk, const char *parent)
    197 {
    198 	struct rk_cru_arm *arm = &clk->u.arm;
    199 
    200 	KASSERT(clk->type == RK_CRU_ARM);
    201 
    202 	for (u_int mux = 0; mux < arm->nparents; mux++)
    203 		if (strcmp(arm->parents[mux], parent) == 0) {
    204 			const uint32_t write_mask = arm->mux_mask << 16;
    205 			const uint32_t write_val = __SHIFTIN(mux, arm->mux_mask);
    206 
    207 			CRU_WRITE(sc, arm->reg, write_mask | write_val);
    208 			return 0;
    209 		}
    210 
    211 	return EINVAL;
    212 }
    213