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
47 struct 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
56 int omclock_match(struct device *, void *, void *);
57 void omclock_attach(struct device *, struct device *, void *);
58
59 const struct cfattach omclock_ca = {
60 sizeof (struct omclock_softc), omclock_match, omclock_attach
61 };
62
63 struct cfdriver omclock_cd = {
64 NULL, "omclock", DV_DULL
65 };
66
67 uint32_t omclock_get_frequency(void *, uint32_t *);
68 int omclock_set_frequency(void *, uint32_t *, uint32_t);
69 void omclock_enable(void *, uint32_t *, int);
70
71 int
omclock_match(struct device * parent,void * match,void * aux)72 omclock_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
79 void
omclock_attach(struct device * parent,struct device * self,void * aux)80 omclock_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
114 uint32_t
omclock_get_frequency(void * cookie,uint32_t * cells)115 omclock_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
121 int
omclock_set_frequency(void * cookie,uint32_t * cells,uint32_t freq)122 omclock_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
128 void
omclock_enable(void * cookie,uint32_t * cells,int on)129 omclock_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