1 /* $OpenBSD: hiclock.c,v 1.4 2022/06/28 23:43:12 naddy Exp $ */
2 /*
3 * Copyright (c) 2018, 2019 Mark Kettenis <kettenis@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21
22 #include <machine/intr.h>
23 #include <machine/bus.h>
24 #include <machine/fdt.h>
25
26 #include <dev/ofw/openfirm.h>
27 #include <dev/ofw/ofw_clock.h>
28 #include <dev/ofw/ofw_misc.h>
29 #include <dev/ofw/fdt.h>
30
31 #define HREAD4(sc, reg) \
32 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
33 #define HWRITE4(sc, reg, val) \
34 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
35
36 /*
37 * This driver includes support for the preliminary device tree
38 * bindings used by the default UEFI firmware for the HiKey970 board.
39 * Support for these preliminary bindings will be dropped at some
40 * point in the future.
41 */
42
43 struct hiclock_softc {
44 struct device sc_dev;
45 bus_space_tag_t sc_iot;
46 bus_space_handle_t sc_ioh;
47 bus_space_handle_t sc_ioh_set;
48
49 struct clock_device sc_cd;
50 };
51
52 int hiclock_match(struct device *, void *, void *);
53 void hiclock_attach(struct device *, struct device *, void *);
54
55 const struct cfattach hiclock_ca = {
56 sizeof (struct hiclock_softc), hiclock_match, hiclock_attach
57 };
58
59 struct cfdriver hiclock_cd = {
60 NULL, "hiclock", DV_DULL
61 };
62
63 struct hiclock_compat {
64 const char *compat;
65 uint32_t (*get_frequency)(void *, uint32_t *);
66 int (*set_frequency)(void *, uint32_t *, uint32_t);
67 void (*enable)(void *, uint32_t *, int);
68 };
69
70 uint32_t hiclock_get_frequency(void *, uint32_t *);
71 void hiclock_enable(void *, uint32_t *, int);
72
73 uint32_t hi3670_crgctrl_get_frequency(void *, uint32_t *);
74 void hi3670_crgctrl_enable(void *, uint32_t *, int);
75 uint32_t hi3670_stub_get_frequency(void *, uint32_t *);
76 int hi3670_stub_set_frequency(void *, uint32_t *, uint32_t);
77
78 const struct hiclock_compat hiclock_compat[] = {
79 /* Official Linux device tree bindings. */
80 {
81 .compat = "hisilicon,hi3670-crgctrl",
82 .get_frequency = hi3670_crgctrl_get_frequency,
83 .enable = hi3670_crgctrl_enable,
84 },
85 { .compat = "hisilicon,hi3670-pctrl" },
86 { .compat = "hisilicon,hi3670-pmuctrl" },
87 { .compat = "hisilicon,hi3670-pmctrl" },
88 { .compat = "hisilicon,hi3670-sctrl" },
89 { .compat = "hisilicon,hi3670-iomcu" },
90 { .compat = "hisilicon,hi3670-media1-crg" },
91 { .compat = "hisilicon,hi3670-media2-crg" },
92
93 /* Preliminary device tree bindings for HiKey970. */
94 {
95 .compat = "hisilicon,kirin970-crgctrl",
96 .get_frequency = hi3670_crgctrl_get_frequency,
97 .enable = hi3670_crgctrl_enable,
98 },
99 { .compat = "hisilicon,kirin970-pctrl" },
100 { .compat = "hisilicon,kirin970-pmuctrl" },
101 { .compat = "hisilicon,kirin970-pmctrl" },
102 { .compat = "hisilicon,kirin970-sctrl" },
103 { .compat = "hisilicon,kirin970-iomcu" },
104 {
105 .compat = "hisilicon,kirin970-stub-clk",
106 .get_frequency = hi3670_stub_get_frequency,
107 .set_frequency = hi3670_stub_set_frequency,
108 },
109 { .compat = "hisilicon,media1-crg" },
110 { .compat = "hisilicon,media2-crg" },
111 };
112
113 int
hiclock_match(struct device * parent,void * match,void * aux)114 hiclock_match(struct device *parent, void *match, void *aux)
115 {
116 struct fdt_attach_args *faa = aux;
117 int i;
118
119 for (i = 0; i < nitems(hiclock_compat); i++) {
120 if (OF_is_compatible(faa->fa_node, hiclock_compat[i].compat))
121 return 10; /* Must beat syscon(4). */
122 }
123
124 return 0;
125 }
126
127 void
hiclock_attach(struct device * parent,struct device * self,void * aux)128 hiclock_attach(struct device *parent, struct device *self, void *aux)
129 {
130 struct hiclock_softc *sc = (struct hiclock_softc *)self;
131 struct fdt_attach_args *faa = aux;
132 int i;
133
134 if (faa->fa_nreg < 1) {
135 printf(": no registers\n");
136 return;
137 }
138
139 sc->sc_iot = faa->fa_iot;
140 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
141 faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
142 printf(": can't map registers\n");
143 return;
144 }
145
146 if (faa->fa_nreg > 1) {
147 if (bus_space_map(sc->sc_iot, faa->fa_reg[1].addr,
148 faa->fa_reg[1].size, 0, &sc->sc_ioh_set)) {
149 printf(": can't map registers\n");
150 bus_space_unmap(sc->sc_iot, sc->sc_ioh,
151 faa->fa_reg[0].size);
152 return;
153 }
154 }
155
156 if (OF_is_compatible(faa->fa_node, "syscon")) {
157 regmap_register(faa->fa_node, sc->sc_iot, sc->sc_ioh,
158 faa->fa_reg[0].size);
159 }
160
161 printf("\n");
162
163 sc->sc_cd.cd_node = faa->fa_node;
164 sc->sc_cd.cd_cookie = sc;
165 for (i = 0; i < nitems(hiclock_compat); i++) {
166 if (OF_is_compatible(faa->fa_node, hiclock_compat[i].compat)) {
167 sc->sc_cd.cd_get_frequency =
168 hiclock_compat[i].get_frequency;
169 sc->sc_cd.cd_set_frequency =
170 hiclock_compat[i].set_frequency;
171 sc->sc_cd.cd_enable = hiclock_compat[i].enable;
172 break;
173 }
174 }
175 if (sc->sc_cd.cd_get_frequency == NULL)
176 sc->sc_cd.cd_get_frequency = hiclock_get_frequency;
177 if (sc->sc_cd.cd_enable == NULL)
178 sc->sc_cd.cd_enable = hiclock_enable;
179 clock_register(&sc->sc_cd);
180 }
181
182 /* Generic */
183
184 uint32_t
hiclock_get_frequency(void * cookie,uint32_t * cells)185 hiclock_get_frequency(void *cookie, uint32_t *cells)
186 {
187 uint32_t idx = cells[0];
188
189 printf("%s: 0x%08x\n", __func__, idx);
190 return 0;
191 }
192
193 void
hiclock_enable(void * cookie,uint32_t * cells,int on)194 hiclock_enable(void *cookie, uint32_t *cells, int on)
195 {
196 uint32_t idx = cells[0];
197
198 printf("%s: 0x%08x\n", __func__, idx);
199 }
200
201 /* Hi3670 */
202
203 #define HI3670_CLKIN_SYS 0
204 #define HI3670_CLK_PPLL0 3
205 #define HI3670_CLK_PPLL2 5
206 #define HI3670_CLK_PPLL3 6
207
208 #define HI3670_CLK_SD_SYS 22
209 #define HI3670_CLK_SDIO_SYS 23
210 #define HI3670_CLK_GATE_ABB_USB 29
211 #define HI3670_CLK_MUX_SD_SYS 68
212 #define HI3670_CLK_MUX_SD_PLL 69
213 #define HI3670_CLK_MUX_SDIO_SYS 70
214 #define HI3670_CLK_MUX_SDIO_PLL 71
215 #define HI3670_CLK_DIV_SD 93
216 #define HI3670_CLK_DIV_SDIO 94
217 #define HI3670_HCLK_GATE_USB3OTG 147
218 #define HI3670_HCLK_GATE_USB3DVFS 148
219 #define HI3670_HCLK_GATE_SDIO 149
220 #define HI3670_CLK_GATE_SD 159
221 #define HI3670_HCLK_GATE_SD 160
222 #define HI3670_CLK_GATE_SDIO 161
223 #define HI3670_CLK_GATE_USB3OTG_REF 189
224
225 uint32_t
hi3670_crgctrl_get_frequency(void * cookie,uint32_t * cells)226 hi3670_crgctrl_get_frequency(void *cookie, uint32_t *cells)
227 {
228 struct hiclock_softc *sc = cookie;
229 uint32_t idx = cells[0];
230 uint32_t reg, freq, div;
231 int mux;
232
233 switch (idx) {
234 case HI3670_CLKIN_SYS:
235 return 19200000;
236 case HI3670_CLK_PPLL0:
237 return 1660000000;
238 case HI3670_CLK_PPLL2:
239 return 1920000000;
240 case HI3670_CLK_PPLL3:
241 return 1200000000;
242 case HI3670_CLK_SD_SYS:
243 case HI3670_CLK_SDIO_SYS:
244 idx = HI3670_CLKIN_SYS;
245 freq = hi3670_crgctrl_get_frequency(cookie, &idx);
246 return freq / 6;
247 case HI3670_CLK_MUX_SD_SYS:
248 reg = HREAD4(sc, 0x0b8);
249 mux = (reg >> 6) & 0x1;
250 idx = mux ? HI3670_CLK_DIV_SD : HI3670_CLK_SD_SYS;
251 return hi3670_crgctrl_get_frequency(cookie, &idx);
252 case HI3670_CLK_MUX_SD_PLL:
253 reg = HREAD4(sc, 0x0b8);
254 mux = (reg >> 4) & 0x3;
255 switch (mux) {
256 case 0:
257 idx = HI3670_CLK_PPLL0;
258 break;
259 case 1:
260 idx = HI3670_CLK_PPLL3;
261 break;
262 case 2:
263 case 3:
264 idx = HI3670_CLK_PPLL2;
265 break;
266 }
267 return hi3670_crgctrl_get_frequency(cookie, &idx);
268 case HI3670_CLK_DIV_SD:
269 reg = HREAD4(sc, 0x0b8);
270 div = (reg >> 0) & 0xf;
271 idx = HI3670_CLK_MUX_SD_PLL;
272 freq = hi3670_crgctrl_get_frequency(cookie, &idx);
273 return freq / (div + 1);
274 case HI3670_CLK_GATE_SD:
275 idx = HI3670_CLK_MUX_SD_SYS;
276 return hi3670_crgctrl_get_frequency(cookie, &idx);
277 case HI3670_CLK_GATE_SDIO:
278 idx = HI3670_CLK_MUX_SDIO_SYS;
279 return hi3670_crgctrl_get_frequency(cookie, &idx);
280 }
281
282 printf("%s: 0x%08x\n", __func__, idx);
283 return 0;
284 }
285
286 void
hi3670_crgctrl_enable(void * cookie,uint32_t * cells,int on)287 hi3670_crgctrl_enable(void *cookie, uint32_t *cells, int on)
288 {
289 uint32_t idx = cells[0];
290
291 switch (idx) {
292 case HI3670_CLK_GATE_ABB_USB:
293 case HI3670_HCLK_GATE_USB3OTG:
294 case HI3670_HCLK_GATE_USB3DVFS:
295 case HI3670_CLK_GATE_SD:
296 case HI3670_HCLK_GATE_SD:
297 case HI3670_CLK_GATE_USB3OTG_REF:
298 /* Enabled by default. */
299 return;
300 }
301
302 printf("%s: 0x%08x\n", __func__, idx);
303 }
304
305 #define HI3670_CLK_STUB_CLUSTER0 0
306 #define HI3670_CLK_STUB_CLUSTER1 1
307
308 uint32_t
hi3670_stub_get_frequency(void * cookie,uint32_t * cells)309 hi3670_stub_get_frequency(void *cookie, uint32_t *cells)
310 {
311 struct hiclock_softc *sc = cookie;
312 uint32_t idx = cells[0];
313 uint32_t reg;
314
315 switch (idx) {
316 case HI3670_CLK_STUB_CLUSTER0:
317 reg = HREAD4(sc, 0x070);
318 return reg * 1000000;
319 case HI3670_CLK_STUB_CLUSTER1:
320 reg = HREAD4(sc, 0x074);
321 return reg * 1000000;
322 default:
323 break;
324 }
325
326 printf("%s: 0x%08x\n", __func__, idx);
327 return 0;
328 }
329
330 int
hi3670_stub_set_frequency(void * cookie,uint32_t * cells,uint32_t freq)331 hi3670_stub_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
332 {
333 struct hiclock_softc *sc = cookie;
334 uint32_t idx = cells[0];
335 uint32_t reg;
336
337 switch (idx) {
338 case HI3670_CLK_STUB_CLUSTER0:
339 reg = freq / 16000000;
340 reg |= (0xff << 16);
341 bus_space_write_4(sc->sc_iot, sc->sc_ioh_set, 0x280, reg);
342 return 0;
343 case HI3670_CLK_STUB_CLUSTER1:
344 reg = freq / 16000000;
345 reg |= (0xff << 16);
346 bus_space_write_4(sc->sc_iot, sc->sc_ioh_set, 0x270, reg);
347 return 0;
348 default:
349 break;
350 }
351
352 printf("%s: 0x%08x\n", __func__, idx);
353 return -1;
354 }
355