1/*	$OpenBSD: omclock.c,v 1.2 2022/04/06 18:59:26 naddy Exp $	*/
2/*
3 * Copyright (c) 2020 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/bus.h>
23#include <machine/fdt.h>
24
25#include <dev/ofw/openfirm.h>
26#include <dev/ofw/ofw_clock.h>
27#include <dev/ofw/fdt.h>
28
29#define IDLEST_MASK		(0x3 << 16)
30#define IDLEST_FUNC		(0x0 << 16)
31#define IDLEST_TRANS		(0x1 << 16)
32#define IDLEST_IDLE		(0x2 << 16)
33#define IDLEST_DISABLED		(0x3 << 16)
34#define MODULEMODE_MASK		(0x3 << 0)
35#define MODULEMODE_DISABLED	(0x0 << 0)
36#define MODULEMODE_ENABLED	(0x2 << 0)
37
38#define HREAD4(sc, reg)							\
39	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
40#define HWRITE4(sc, reg, val)						\
41	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
42#define HSET4(sc, reg, bits)						\
43	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
44#define HCLR4(sc, reg, bits)						\
45	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
46
47struct omclock_softc {
48	struct device		sc_dev;
49	bus_space_tag_t		sc_iot;
50	bus_space_handle_t	sc_ioh;
51	int			sc_node;
52
53	struct clock_device	sc_cd;
54};
55
56int	omclock_match(struct device *, void *, void *);
57void	omclock_attach(struct device *, struct device *, void *);
58
59const struct cfattach omclock_ca = {
60	sizeof (struct omclock_softc), omclock_match, omclock_attach
61};
62
63struct cfdriver omclock_cd = {
64	NULL, "omclock", DV_DULL
65};
66
67uint32_t omclock_get_frequency(void *, uint32_t *);
68int	omclock_set_frequency(void *, uint32_t *, uint32_t);
69void	omclock_enable(void *, uint32_t *, int);
70
71int
72omclock_match(struct device *parent, void *match, void *aux)
73{
74	struct fdt_attach_args *faa = aux;
75
76	return OF_is_compatible(faa->fa_node, "ti,clkctrl");
77}
78
79void
80omclock_attach(struct device *parent, struct device *self, void *aux)
81{
82	struct omclock_softc *sc = (struct omclock_softc *)self;
83	struct fdt_attach_args *faa = aux;
84	char name[32];
85
86	if (faa->fa_nreg < 1) {
87		printf(": no registers\n");
88		return;
89	}
90
91	sc->sc_iot = faa->fa_iot;
92	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
93	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
94		printf(": can't map registers\n");
95		return;
96	}
97
98	sc->sc_node = faa->fa_node;
99	if (OF_getprop(sc->sc_node, "name", name, sizeof(name)) > 0) {
100		name[sizeof(name) - 1] = 0;
101		printf(": \"%s\"", name);
102	}
103
104	printf("\n");
105
106	sc->sc_cd.cd_node = faa->fa_node;
107	sc->sc_cd.cd_cookie = sc;
108	sc->sc_cd.cd_get_frequency = omclock_get_frequency;
109	sc->sc_cd.cd_set_frequency = omclock_set_frequency;
110	sc->sc_cd.cd_enable = omclock_enable;
111	clock_register(&sc->sc_cd);
112}
113
114uint32_t
115omclock_get_frequency(void *cookie, uint32_t *cells)
116{
117	printf("%s: 0x%08x 0x%08x\n", __func__, cells[0], cells[1]);
118	return 0;
119}
120
121int
122omclock_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
123{
124	printf("%s: 0x%08x 0x%08x\n", __func__, cells[0], cells[1]);
125	return -1;
126}
127
128void
129omclock_enable(void *cookie, uint32_t *cells, int on)
130{
131	struct omclock_softc *sc = cookie;
132	uint32_t base = cells[0];
133	uint32_t idx = cells[1];
134	uint32_t reg;
135	int retry;
136
137	reg = HREAD4(sc, base);
138	if (idx == 0) {
139		reg &= ~MODULEMODE_MASK;
140		if (on)
141			reg |= MODULEMODE_ENABLED;
142		else
143			reg |= MODULEMODE_DISABLED;
144	} else {
145		if (on)
146			reg |= (1U << idx);
147		else
148			reg &= ~(1U << idx);
149	}
150	HWRITE4(sc, base, reg);
151
152	if (idx == 0) {
153		retry = 100;
154		while (--retry > 0) {
155			if ((HREAD4(sc, base) & IDLEST_MASK) == IDLEST_FUNC)
156				break;
157			delay(10);
158		}
159		/* XXX Hope for the best if this loop times out. */
160	}
161}
162