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
43struct 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
52int hiclock_match(struct device *, void *, void *);
53void hiclock_attach(struct device *, struct device *, void *);
54
55const struct cfattach	hiclock_ca = {
56	sizeof (struct hiclock_softc), hiclock_match, hiclock_attach
57};
58
59struct cfdriver hiclock_cd = {
60	NULL, "hiclock", DV_DULL
61};
62
63struct 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
70uint32_t hiclock_get_frequency(void *, uint32_t *);
71void	hiclock_enable(void *, uint32_t *, int);
72
73uint32_t hi3670_crgctrl_get_frequency(void *, uint32_t *);
74void	hi3670_crgctrl_enable(void *, uint32_t *, int);
75uint32_t hi3670_stub_get_frequency(void *, uint32_t *);
76int	hi3670_stub_set_frequency(void *, uint32_t *, uint32_t);
77
78const 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
113int
114hiclock_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
127void
128hiclock_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
184uint32_t
185hiclock_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
193void
194hiclock_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
225uint32_t
226hi3670_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
286void
287hi3670_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
308uint32_t
309hi3670_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
330int
331hi3670_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