1*471aeecfSnaddy /* $OpenBSD: omclock.c,v 1.2 2022/04/06 18:59:26 naddy Exp $ */
24d149eb4Skettenis /*
34d149eb4Skettenis * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
44d149eb4Skettenis *
54d149eb4Skettenis * Permission to use, copy, modify, and distribute this software for any
64d149eb4Skettenis * purpose with or without fee is hereby granted, provided that the above
74d149eb4Skettenis * copyright notice and this permission notice appear in all copies.
84d149eb4Skettenis *
94d149eb4Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
104d149eb4Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
114d149eb4Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
124d149eb4Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
134d149eb4Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
144d149eb4Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
154d149eb4Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
164d149eb4Skettenis */
174d149eb4Skettenis
184d149eb4Skettenis #include <sys/param.h>
194d149eb4Skettenis #include <sys/systm.h>
204d149eb4Skettenis #include <sys/device.h>
214d149eb4Skettenis
224d149eb4Skettenis #include <machine/bus.h>
234d149eb4Skettenis #include <machine/fdt.h>
244d149eb4Skettenis
254d149eb4Skettenis #include <dev/ofw/openfirm.h>
264d149eb4Skettenis #include <dev/ofw/ofw_clock.h>
274d149eb4Skettenis #include <dev/ofw/fdt.h>
284d149eb4Skettenis
294d149eb4Skettenis #define IDLEST_MASK (0x3 << 16)
304d149eb4Skettenis #define IDLEST_FUNC (0x0 << 16)
314d149eb4Skettenis #define IDLEST_TRANS (0x1 << 16)
324d149eb4Skettenis #define IDLEST_IDLE (0x2 << 16)
334d149eb4Skettenis #define IDLEST_DISABLED (0x3 << 16)
344d149eb4Skettenis #define MODULEMODE_MASK (0x3 << 0)
354d149eb4Skettenis #define MODULEMODE_DISABLED (0x0 << 0)
364d149eb4Skettenis #define MODULEMODE_ENABLED (0x2 << 0)
374d149eb4Skettenis
384d149eb4Skettenis #define HREAD4(sc, reg) \
394d149eb4Skettenis (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
404d149eb4Skettenis #define HWRITE4(sc, reg, val) \
414d149eb4Skettenis bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
424d149eb4Skettenis #define HSET4(sc, reg, bits) \
434d149eb4Skettenis HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
444d149eb4Skettenis #define HCLR4(sc, reg, bits) \
454d149eb4Skettenis HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
464d149eb4Skettenis
474d149eb4Skettenis struct omclock_softc {
484d149eb4Skettenis struct device sc_dev;
494d149eb4Skettenis bus_space_tag_t sc_iot;
504d149eb4Skettenis bus_space_handle_t sc_ioh;
514d149eb4Skettenis int sc_node;
524d149eb4Skettenis
534d149eb4Skettenis struct clock_device sc_cd;
544d149eb4Skettenis };
554d149eb4Skettenis
564d149eb4Skettenis int omclock_match(struct device *, void *, void *);
574d149eb4Skettenis void omclock_attach(struct device *, struct device *, void *);
584d149eb4Skettenis
59*471aeecfSnaddy const struct cfattach omclock_ca = {
604d149eb4Skettenis sizeof (struct omclock_softc), omclock_match, omclock_attach
614d149eb4Skettenis };
624d149eb4Skettenis
634d149eb4Skettenis struct cfdriver omclock_cd = {
644d149eb4Skettenis NULL, "omclock", DV_DULL
654d149eb4Skettenis };
664d149eb4Skettenis
674d149eb4Skettenis uint32_t omclock_get_frequency(void *, uint32_t *);
684d149eb4Skettenis int omclock_set_frequency(void *, uint32_t *, uint32_t);
694d149eb4Skettenis void omclock_enable(void *, uint32_t *, int);
704d149eb4Skettenis
714d149eb4Skettenis int
omclock_match(struct device * parent,void * match,void * aux)724d149eb4Skettenis omclock_match(struct device *parent, void *match, void *aux)
734d149eb4Skettenis {
744d149eb4Skettenis struct fdt_attach_args *faa = aux;
754d149eb4Skettenis
764d149eb4Skettenis return OF_is_compatible(faa->fa_node, "ti,clkctrl");
774d149eb4Skettenis }
784d149eb4Skettenis
794d149eb4Skettenis void
omclock_attach(struct device * parent,struct device * self,void * aux)804d149eb4Skettenis omclock_attach(struct device *parent, struct device *self, void *aux)
814d149eb4Skettenis {
824d149eb4Skettenis struct omclock_softc *sc = (struct omclock_softc *)self;
834d149eb4Skettenis struct fdt_attach_args *faa = aux;
844d149eb4Skettenis char name[32];
854d149eb4Skettenis
864d149eb4Skettenis if (faa->fa_nreg < 1) {
874d149eb4Skettenis printf(": no registers\n");
884d149eb4Skettenis return;
894d149eb4Skettenis }
904d149eb4Skettenis
914d149eb4Skettenis sc->sc_iot = faa->fa_iot;
924d149eb4Skettenis if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
934d149eb4Skettenis faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
944d149eb4Skettenis printf(": can't map registers\n");
954d149eb4Skettenis return;
964d149eb4Skettenis }
974d149eb4Skettenis
984d149eb4Skettenis sc->sc_node = faa->fa_node;
994d149eb4Skettenis if (OF_getprop(sc->sc_node, "name", name, sizeof(name)) > 0) {
1004d149eb4Skettenis name[sizeof(name) - 1] = 0;
1014d149eb4Skettenis printf(": \"%s\"", name);
1024d149eb4Skettenis }
1034d149eb4Skettenis
1044d149eb4Skettenis printf("\n");
1054d149eb4Skettenis
1064d149eb4Skettenis sc->sc_cd.cd_node = faa->fa_node;
1074d149eb4Skettenis sc->sc_cd.cd_cookie = sc;
1084d149eb4Skettenis sc->sc_cd.cd_get_frequency = omclock_get_frequency;
1094d149eb4Skettenis sc->sc_cd.cd_set_frequency = omclock_set_frequency;
1104d149eb4Skettenis sc->sc_cd.cd_enable = omclock_enable;
1114d149eb4Skettenis clock_register(&sc->sc_cd);
1124d149eb4Skettenis }
1134d149eb4Skettenis
1144d149eb4Skettenis uint32_t
omclock_get_frequency(void * cookie,uint32_t * cells)1154d149eb4Skettenis omclock_get_frequency(void *cookie, uint32_t *cells)
1164d149eb4Skettenis {
1174d149eb4Skettenis printf("%s: 0x%08x 0x%08x\n", __func__, cells[0], cells[1]);
1184d149eb4Skettenis return 0;
1194d149eb4Skettenis }
1204d149eb4Skettenis
1214d149eb4Skettenis int
omclock_set_frequency(void * cookie,uint32_t * cells,uint32_t freq)1224d149eb4Skettenis omclock_set_frequency(void *cookie, uint32_t *cells, uint32_t freq)
1234d149eb4Skettenis {
1244d149eb4Skettenis printf("%s: 0x%08x 0x%08x\n", __func__, cells[0], cells[1]);
1254d149eb4Skettenis return -1;
1264d149eb4Skettenis }
1274d149eb4Skettenis
1284d149eb4Skettenis void
omclock_enable(void * cookie,uint32_t * cells,int on)1294d149eb4Skettenis omclock_enable(void *cookie, uint32_t *cells, int on)
1304d149eb4Skettenis {
1314d149eb4Skettenis struct omclock_softc *sc = cookie;
1324d149eb4Skettenis uint32_t base = cells[0];
1334d149eb4Skettenis uint32_t idx = cells[1];
1344d149eb4Skettenis uint32_t reg;
1354d149eb4Skettenis int retry;
1364d149eb4Skettenis
1374d149eb4Skettenis reg = HREAD4(sc, base);
1384d149eb4Skettenis if (idx == 0) {
1394d149eb4Skettenis reg &= ~MODULEMODE_MASK;
1404d149eb4Skettenis if (on)
1414d149eb4Skettenis reg |= MODULEMODE_ENABLED;
1424d149eb4Skettenis else
1434d149eb4Skettenis reg |= MODULEMODE_DISABLED;
1444d149eb4Skettenis } else {
1454d149eb4Skettenis if (on)
1464d149eb4Skettenis reg |= (1U << idx);
1474d149eb4Skettenis else
1484d149eb4Skettenis reg &= ~(1U << idx);
1494d149eb4Skettenis }
1504d149eb4Skettenis HWRITE4(sc, base, reg);
1514d149eb4Skettenis
1524d149eb4Skettenis if (idx == 0) {
1534d149eb4Skettenis retry = 100;
1544d149eb4Skettenis while (--retry > 0) {
1554d149eb4Skettenis if ((HREAD4(sc, base) & IDLEST_MASK) == IDLEST_FUNC)
1564d149eb4Skettenis break;
1574d149eb4Skettenis delay(10);
1584d149eb4Skettenis }
1594d149eb4Skettenis /* XXX Hope for the best if this loop times out. */
1604d149eb4Skettenis }
1614d149eb4Skettenis }
162