Home | History | Annotate | Line # | Download | only in sunxi
      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