rk_cru_composite.c revision 1.1 1 /* $NetBSD: rk_cru_composite.c,v 1.1 2018/06/16 00:19:04 jmcneill 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_composite.c,v 1.1 2018/06/16 00:19:04 jmcneill 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 int
40 rk_cru_composite_enable(struct rk_cru_softc *sc, struct rk_cru_clk *clk,
41 int enable)
42 {
43 struct rk_cru_composite *composite = &clk->u.composite;
44
45 KASSERT(clk->type == RK_CRU_COMPOSITE);
46
47 if (composite->gate_mask == 0)
48 return enable ? 0 : ENXIO;
49
50 const uint32_t write_mask = composite->gate_mask << 16;
51 const uint32_t write_val = __SHIFTIN(!enable, composite->gate_mask);
52
53 CRU_WRITE(sc, composite->gate_reg, write_mask | write_val);
54
55 return 0;
56 }
57
58 u_int
59 rk_cru_composite_get_rate(struct rk_cru_softc *sc,
60 struct rk_cru_clk *clk)
61 {
62 struct rk_cru_composite *composite = &clk->u.composite;
63 struct clk *clkp, *clkp_parent;
64
65 KASSERT(clk->type == RK_CRU_COMPOSITE);
66
67 clkp = &clk->base;
68 clkp_parent = clk_get_parent(clkp);
69 if (clkp_parent == NULL)
70 return 0;
71
72 const u_int prate = clk_get_rate(clkp_parent);
73 if (prate == 0)
74 return 0;
75
76 const uint32_t val = CRU_READ(sc, composite->muxdiv_reg);
77 const u_int div = __SHIFTOUT(val, composite->div_mask) + 1;
78
79 return prate / div;
80 }
81
82 int
83 rk_cru_composite_set_rate(struct rk_cru_softc *sc,
84 struct rk_cru_clk *clk, u_int rate)
85 {
86 struct rk_cru_composite *composite = &clk->u.composite;
87 u_int best_div, best_mux, best_diff;
88 struct rk_cru_clk *clk_parent;
89
90 KASSERT(clk->type == RK_CRU_COMPOSITE);
91
92 best_div = 0;
93 best_mux = 0;
94 best_diff = INT_MAX;
95 for (u_int mux = 0; mux < composite->nparents; mux++) {
96 clk_parent = rk_cru_clock_find(sc, composite->parents[mux]);
97 if (clk_parent == NULL)
98 continue;
99 const u_int prate = clk_get_rate(&clk_parent->base);
100 if (prate == 0)
101 continue;
102
103 for (u_int div = 1; div <= __SHIFTOUT_MASK(composite->div_mask) + 1; div++) {
104 const u_int cur_rate = prate / div;
105 const int diff = (int)rate - (int)cur_rate;
106 if (composite->flags & RK_COMPOSITE_ROUND_DOWN) {
107 if (diff >= 0 && diff < best_diff) {
108 best_diff = diff;
109 best_mux = mux;
110 best_div = div;
111 }
112 } else {
113 if (abs(diff) < best_diff) {
114 best_diff = abs(diff);
115 best_mux = mux;
116 best_div = div;
117 }
118 }
119 }
120 }
121 if (best_diff == INT_MAX)
122 return ERANGE;
123
124 uint32_t write_mask = composite->div_mask << 16;
125 uint32_t write_val = __SHIFTIN(best_div - 1, composite->div_mask);
126 if (composite->mux_mask) {
127 write_mask |= composite->mux_mask << 16;
128 write_val |= __SHIFTIN(best_mux, composite->mux_mask);
129 }
130
131 CRU_WRITE(sc, composite->muxdiv_reg, write_mask | write_val);
132
133 return 0;
134 }
135
136 const char *
137 rk_cru_composite_get_parent(struct rk_cru_softc *sc,
138 struct rk_cru_clk *clk)
139 {
140 struct rk_cru_composite *composite = &clk->u.composite;
141 uint32_t val;
142 u_int mux;
143
144 KASSERT(clk->type == RK_CRU_COMPOSITE);
145
146 if (composite->mux_mask) {
147 val = CRU_READ(sc, composite->muxdiv_reg);
148 mux = __SHIFTOUT(val, composite->mux_mask);
149 } else {
150 mux = 0;
151 }
152
153 return composite->parents[mux];
154 }
155
156 int
157 rk_cru_composite_set_parent(struct rk_cru_softc *sc,
158 struct rk_cru_clk *clk, const char *parent)
159 {
160 struct rk_cru_composite *composite = &clk->u.composite;
161
162 KASSERT(clk->type == RK_CRU_COMPOSITE);
163
164 if (!composite->mux_mask)
165 return EINVAL;
166
167 for (u_int mux = 0; mux < composite->nparents; mux++) {
168 if (strcmp(composite->parents[mux], parent) == 0) {
169 const uint32_t write_mask = composite->mux_mask << 16;
170 const uint32_t write_val = __SHIFTIN(mux, composite->mux_mask);
171
172 CRU_WRITE(sc, composite->muxdiv_reg, write_mask | write_val);
173 return 0;
174 }
175 }
176
177 return EINVAL;
178 }
179