xref: /netbsd-src/sys/arch/arm/nxp/imx_ccm_composite.c (revision 8644267a503e0f573f2833152303dbf2ce4aca52)
1 /* $NetBSD: imx_ccm_composite.c,v 1.1 2020/12/23 14:42:38 skrll Exp $ */
2 
3 /*-
4  * Copyright (c) 2020 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: imx_ccm_composite.c,v 1.1 2020/12/23 14:42:38 skrll Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/bus.h>
34 
35 #include <dev/clk/clk_backend.h>
36 
37 #include <arm/nxp/imx_ccm.h>
38 
39 #include <dev/fdt/fdtvar.h>
40 
41 #define	CCM_TARGET_ROOT		0x00
42 #define	 TARGET_ROOT_ENABLE	__BIT(28)
43 #define	 TARGET_ROOT_MUX	__BITS(26,24)
44 #define	 TARGET_ROOT_PRE_PODF	__BITS(18,16)
45 #define	 TARGET_ROOT_POST_PODF	__BITS(5,0)
46 
47 int
imx_ccm_composite_enable(struct imx_ccm_softc * sc,struct imx_ccm_clk * clk,int enable)48 imx_ccm_composite_enable(struct imx_ccm_softc *sc, struct imx_ccm_clk *clk,
49     int enable)
50 {
51 	struct imx_ccm_composite *composite = &clk->u.composite;
52 	uint32_t val;
53 
54 	KASSERT(clk->type == IMX_CCM_COMPOSITE);
55 
56 	val = CCM_READ(sc, clk->regidx, composite->reg + CCM_TARGET_ROOT);
57 	if (enable)
58 		val |= TARGET_ROOT_ENABLE;
59 	else
60 		val &= ~TARGET_ROOT_ENABLE;
61 	CCM_WRITE(sc, clk->regidx, composite->reg + CCM_TARGET_ROOT, val);
62 
63 	return 0;
64 }
65 
66 u_int
imx_ccm_composite_get_rate(struct imx_ccm_softc * sc,struct imx_ccm_clk * clk)67 imx_ccm_composite_get_rate(struct imx_ccm_softc *sc,
68     struct imx_ccm_clk *clk)
69 {
70 	struct imx_ccm_composite *composite = &clk->u.composite;
71 	struct clk *clkp, *clkp_parent;
72 
73 	KASSERT(clk->type == IMX_CCM_COMPOSITE);
74 
75 	clkp = &clk->base;
76 	clkp_parent = clk_get_parent(clkp);
77 	if (clkp_parent == NULL)
78 		return 0;
79 
80 	const u_int prate = clk_get_rate(clkp_parent);
81 	if (prate == 0)
82 		return 0;
83 
84 	const uint32_t val = CCM_READ(sc, clk->regidx, composite->reg + CCM_TARGET_ROOT);
85 	const u_int pre_div = __SHIFTOUT(val, TARGET_ROOT_PRE_PODF) + 1;
86 	const u_int post_div = __SHIFTOUT(val, TARGET_ROOT_POST_PODF) + 1;
87 
88 	return prate / pre_div / post_div;
89 }
90 
91 int
imx_ccm_composite_set_rate(struct imx_ccm_softc * sc,struct imx_ccm_clk * clk,u_int rate)92 imx_ccm_composite_set_rate(struct imx_ccm_softc *sc,
93     struct imx_ccm_clk *clk, u_int rate)
94 {
95 	struct imx_ccm_composite *composite = &clk->u.composite;
96 	u_int best_prediv, best_postdiv, best_diff;
97 	struct imx_ccm_clk *rclk_parent;
98 	struct clk *clk_parent;
99 	uint32_t val;
100 
101 	KASSERT(clk->type == IMX_CCM_COMPOSITE);
102 
103 	if (composite->flags & IMX_COMPOSITE_SET_RATE_PARENT) {
104 		clk_parent = clk_get_parent(&clk->base);
105 		if (clk_parent == NULL)
106 			return ENXIO;
107 		return clk_set_rate(clk_parent, rate);
108 	}
109 
110 	best_prediv = 0;
111 	best_postdiv = 0;
112 	best_diff = INT_MAX;
113 
114 	val = CCM_READ(sc, clk->regidx, composite->reg + CCM_TARGET_ROOT);
115 	const u_int mux = __SHIFTOUT(val, TARGET_ROOT_MUX);
116 
117 	if (mux >= composite->nparents)
118 		return EIO;
119 
120 	rclk_parent = imx_ccm_clock_find(sc, composite->parents[mux]);
121 	if (rclk_parent != NULL)
122 		clk_parent = &rclk_parent->base;
123 	else
124 		clk_parent = fdtbus_clock_byname(composite->parents[mux]);
125 	if (clk_parent == NULL)
126 		return EIO;
127 
128 	const u_int prate = clk_get_rate(clk_parent);
129 	if (prate == 0)
130 		return ERANGE;
131 
132 	for (u_int prediv = 1; prediv <= __SHIFTOUT_MASK(TARGET_ROOT_PRE_PODF) + 1; prediv++) {
133 		for (u_int postdiv = 1; postdiv <= __SHIFTOUT_MASK(TARGET_ROOT_POST_PODF) + 1; postdiv++) {
134 			const u_int cur_rate = prate / prediv / postdiv;
135 			const int diff = (int)rate - (int)cur_rate;
136 			if (composite->flags & IMX_COMPOSITE_ROUND_DOWN) {
137 				if (diff >= 0 && diff < best_diff) {
138 					best_diff = diff;
139 					best_prediv = prediv;
140 					best_postdiv = postdiv;
141 				}
142 			} else {
143 				if (abs(diff) < best_diff) {
144 					best_diff = abs(diff);
145 					best_prediv = prediv;
146 					best_postdiv = postdiv;
147 				}
148 			}
149 		}
150 	}
151 	if (best_diff == INT_MAX)
152 		return ERANGE;
153 
154 	val = CCM_READ(sc, clk->regidx, composite->reg + CCM_TARGET_ROOT);
155 	val &= ~TARGET_ROOT_PRE_PODF;
156 	val |= __SHIFTIN(best_prediv - 1, TARGET_ROOT_PRE_PODF);
157 	val &= ~TARGET_ROOT_POST_PODF;
158 	val |= __SHIFTIN(best_postdiv - 1, TARGET_ROOT_POST_PODF);
159 	CCM_WRITE(sc, clk->regidx, composite->reg + CCM_TARGET_ROOT, val);
160 
161 	return 0;
162 }
163 
164 const char *
imx_ccm_composite_get_parent(struct imx_ccm_softc * sc,struct imx_ccm_clk * clk)165 imx_ccm_composite_get_parent(struct imx_ccm_softc *sc,
166     struct imx_ccm_clk *clk)
167 {
168 	struct imx_ccm_composite *composite = &clk->u.composite;
169 
170 	KASSERT(clk->type == IMX_CCM_COMPOSITE);
171 
172 	const uint32_t val = CCM_READ(sc, clk->regidx, composite->reg + CCM_TARGET_ROOT);
173 	const u_int mux = __SHIFTOUT(val, TARGET_ROOT_MUX);
174 
175 	if (mux >= composite->nparents)
176 		return NULL;
177 
178 	return composite->parents[mux];
179 }
180 
181 int
imx_ccm_composite_set_parent(struct imx_ccm_softc * sc,struct imx_ccm_clk * clk,const char * parent)182 imx_ccm_composite_set_parent(struct imx_ccm_softc *sc,
183     struct imx_ccm_clk *clk, const char *parent)
184 {
185 	struct imx_ccm_composite *composite = &clk->u.composite;
186 	uint32_t val;
187 
188 	KASSERT(clk->type == IMX_CCM_COMPOSITE);
189 
190 	for (u_int mux = 0; mux < composite->nparents; mux++) {
191 		if (strcmp(composite->parents[mux], parent) == 0) {
192 			val = CCM_READ(sc, clk->regidx, composite->reg + CCM_TARGET_ROOT);
193 			val &= ~TARGET_ROOT_MUX;
194 			val |= __SHIFTIN(mux, TARGET_ROOT_MUX);
195 			CCM_WRITE(sc, clk->regidx, composite->reg + CCM_TARGET_ROOT, val);
196 			return 0;
197 		}
198 	}
199 
200 	return EINVAL;
201 }
202