xref: /openbsd/sys/dev/fdt/hiclock.c (revision 37c734d3)
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