xref: /openbsd-src/sys/dev/fdt/syscon.c (revision 94673892b7b28179327a9d4cb100f53993b0bd92)
1*94673892Sjsg /*	$OpenBSD: syscon.c,v 1.8 2023/09/22 01:10:44 jsg Exp $	*/
24163ec60Skettenis /*
34163ec60Skettenis  * Copyright (c) 2017 Mark Kettenis
44163ec60Skettenis  *
54163ec60Skettenis  * Permission to use, copy, modify, and distribute this software for any
64163ec60Skettenis  * purpose with or without fee is hereby granted, provided that the above
74163ec60Skettenis  * copyright notice and this permission notice appear in all copies.
84163ec60Skettenis  *
94163ec60Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
104163ec60Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
114163ec60Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
124163ec60Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
134163ec60Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
144163ec60Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
154163ec60Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
164163ec60Skettenis  */
174163ec60Skettenis 
184163ec60Skettenis #include <sys/param.h>
194163ec60Skettenis #include <sys/systm.h>
204163ec60Skettenis #include <sys/device.h>
214163ec60Skettenis 
224163ec60Skettenis #include <machine/bus.h>
234163ec60Skettenis #include <machine/fdt.h>
244163ec60Skettenis 
254163ec60Skettenis #include <dev/ofw/openfirm.h>
264163ec60Skettenis #include <dev/ofw/ofw_misc.h>
274163ec60Skettenis #include <dev/ofw/fdt.h>
284163ec60Skettenis 
29*94673892Sjsg #include <machine/simplebusvar.h>
30c4620c40Skettenis 
314163ec60Skettenis extern void (*cpuresetfn)(void);
324163ec60Skettenis extern void (*powerdownfn)(void);
334163ec60Skettenis 
344163ec60Skettenis struct syscon_softc {
35c4620c40Skettenis 	struct simplebus_softc	sc_sbus;
365c4de186Spatrick 	bus_space_tag_t		sc_iot;
375c4de186Spatrick 	bus_space_handle_t	sc_ioh;
384163ec60Skettenis 	uint32_t		sc_regmap;
394163ec60Skettenis 	bus_size_t		sc_offset;
404163ec60Skettenis 	uint32_t		sc_mask;
41c891ae27Skettenis 	uint32_t		sc_value;
424163ec60Skettenis };
434163ec60Skettenis 
444163ec60Skettenis struct syscon_softc *syscon_reboot_sc;
454163ec60Skettenis struct syscon_softc *syscon_poweroff_sc;
464163ec60Skettenis 
474163ec60Skettenis int	syscon_match(struct device *, void *, void *);
484163ec60Skettenis void	syscon_attach(struct device *, struct device *, void *);
494163ec60Skettenis 
509fdf0c62Smpi const struct cfattach syscon_ca = {
514163ec60Skettenis 	sizeof(struct syscon_softc), syscon_match, syscon_attach
524163ec60Skettenis };
534163ec60Skettenis 
544163ec60Skettenis struct cfdriver syscon_cd = {
554163ec60Skettenis 	NULL, "syscon", DV_DULL
564163ec60Skettenis };
574163ec60Skettenis 
584163ec60Skettenis void	syscon_reset(void);
594163ec60Skettenis void	syscon_powerdown(void);
604163ec60Skettenis 
614163ec60Skettenis int
syscon_match(struct device * parent,void * match,void * aux)624163ec60Skettenis syscon_match(struct device *parent, void *match, void *aux)
634163ec60Skettenis {
644163ec60Skettenis 	struct fdt_attach_args *faa = aux;
654163ec60Skettenis 
665c4de186Spatrick 	return OF_is_compatible(faa->fa_node, "syscon") ||
675c4de186Spatrick 	    OF_is_compatible(faa->fa_node, "syscon-reboot") ||
685c4de186Spatrick 	    OF_is_compatible(faa->fa_node, "syscon-poweroff");
694163ec60Skettenis }
704163ec60Skettenis 
714163ec60Skettenis void
syscon_attach(struct device * parent,struct device * self,void * aux)724163ec60Skettenis syscon_attach(struct device *parent, struct device *self, void *aux)
734163ec60Skettenis {
744163ec60Skettenis 	struct syscon_softc *sc = (struct syscon_softc *)self;
754163ec60Skettenis 	struct fdt_attach_args *faa = aux;
76079868a5Skettenis 	char name[32];
774163ec60Skettenis 
78c4620c40Skettenis 	OF_getprop(faa->fa_node, "name", name, sizeof(name));
79c4620c40Skettenis 	name[sizeof(name) - 1] = 0;
80c4620c40Skettenis 
815c4de186Spatrick 	if (OF_is_compatible(faa->fa_node, "syscon")) {
825c4de186Spatrick 		if (faa->fa_nreg < 1) {
835c4de186Spatrick 			printf(": no registers\n");
845c4de186Spatrick 			return;
855c4de186Spatrick 		}
865c4de186Spatrick 
875c4de186Spatrick 		sc->sc_iot = faa->fa_iot;
885c4de186Spatrick 
895c4de186Spatrick 		if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
905c4de186Spatrick 		    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
915c4de186Spatrick 			printf(": can't map registers\n");
925c4de186Spatrick 			return;
935c4de186Spatrick 		}
945c4de186Spatrick 
955c4de186Spatrick 		regmap_register(faa->fa_node, sc->sc_iot, sc->sc_ioh,
965c4de186Spatrick 		    faa->fa_reg[0].size);
97079868a5Skettenis 	}
98079868a5Skettenis 
99c4620c40Skettenis 	if (OF_is_compatible(faa->fa_node, "simple-mfd"))
100c4620c40Skettenis 		simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa);
101c4620c40Skettenis 	else
102c4620c40Skettenis 		printf(": \"%s\"\n", name);
1035c4de186Spatrick 
1045c4de186Spatrick 	if (OF_is_compatible(faa->fa_node, "syscon-reboot") ||
1055c4de186Spatrick 	    OF_is_compatible(faa->fa_node, "syscon-poweroff")) {
1064163ec60Skettenis 		sc->sc_regmap = OF_getpropint(faa->fa_node, "regmap", 0);
1074163ec60Skettenis 		if (sc->sc_regmap == 0)
1084163ec60Skettenis 			return;
1094163ec60Skettenis 
110c891ae27Skettenis 		if (OF_getproplen(faa->fa_node, "offset") != sizeof(uint32_t))
111c891ae27Skettenis 			return;
112c891ae27Skettenis 
113c891ae27Skettenis 		/* At least one of "mask" and "value" should be provided. */
114c891ae27Skettenis 		if (OF_getproplen(faa->fa_node, "mask") != sizeof(uint32_t) &&
115c891ae27Skettenis 		    OF_getproplen(faa->fa_node, "value") != sizeof(uint32_t))
1164163ec60Skettenis 			return;
1174163ec60Skettenis 
1184163ec60Skettenis 		sc->sc_offset = OF_getpropint(faa->fa_node, "offset", 0);
119c891ae27Skettenis 		sc->sc_mask = OF_getpropint(faa->fa_node, "mask", 0xffffffff);
120c891ae27Skettenis 		sc->sc_value = OF_getpropint(faa->fa_node, "value", 0);
121c891ae27Skettenis 
122c891ae27Skettenis 		/*
123c891ae27Skettenis 		 * Old binding used "mask" as the value to write with
124c891ae27Skettenis 		 * an all-ones mask.  This is still supported.
125c891ae27Skettenis 		 */
126c891ae27Skettenis 		if (OF_getproplen(faa->fa_node, "value") != sizeof(uint32_t)) {
127c891ae27Skettenis 			sc->sc_value = sc->sc_mask;
128c891ae27Skettenis 			sc->sc_mask = 0xffffffff;
129c891ae27Skettenis 		}
1304163ec60Skettenis 
1314163ec60Skettenis 		if (OF_is_compatible(faa->fa_node, "syscon-reboot")) {
1324163ec60Skettenis 			syscon_reboot_sc = sc;
1334163ec60Skettenis 			cpuresetfn = syscon_reset;
1345c4de186Spatrick 		} else if (OF_is_compatible(faa->fa_node, "syscon-poweroff")) {
1354163ec60Skettenis 			syscon_poweroff_sc = sc;
1364163ec60Skettenis 			powerdownfn = syscon_powerdown;
1374163ec60Skettenis 		}
1384163ec60Skettenis 	}
1395c4de186Spatrick }
1404163ec60Skettenis 
1414163ec60Skettenis void
syscon_reset(void)1424163ec60Skettenis syscon_reset(void)
1434163ec60Skettenis {
1444163ec60Skettenis 	struct syscon_softc *sc = syscon_reboot_sc;
1454163ec60Skettenis 	struct regmap *rm;
146c891ae27Skettenis 	uint32_t value;
1474163ec60Skettenis 
1484163ec60Skettenis 	rm = regmap_byphandle(sc->sc_regmap);
1494163ec60Skettenis 	if (rm == NULL)
1504163ec60Skettenis 		return;
1514163ec60Skettenis 
152c891ae27Skettenis 	value = regmap_read_4(rm, sc->sc_offset);
153c891ae27Skettenis 	value &= ~sc->sc_mask;
154c891ae27Skettenis 	value |= sc->sc_value;
155c891ae27Skettenis 	regmap_write_4(rm, sc->sc_offset, value);
1564163ec60Skettenis 	delay(1000000);
1574163ec60Skettenis }
1584163ec60Skettenis 
1594163ec60Skettenis void
syscon_powerdown(void)1604163ec60Skettenis syscon_powerdown(void)
1614163ec60Skettenis {
1624163ec60Skettenis 	struct syscon_softc *sc = syscon_poweroff_sc;
1634163ec60Skettenis 	struct regmap *rm;
164c891ae27Skettenis 	uint32_t value;
1654163ec60Skettenis 
1664163ec60Skettenis 	rm = regmap_byphandle(sc->sc_regmap);
1674163ec60Skettenis 	if (rm == NULL)
1684163ec60Skettenis 		return;
1694163ec60Skettenis 
170c891ae27Skettenis 	value = regmap_read_4(rm, sc->sc_offset);
171c891ae27Skettenis 	value &= ~sc->sc_mask;
172c891ae27Skettenis 	value |= sc->sc_value;
173c891ae27Skettenis 	regmap_write_4(rm, sc->sc_offset, value);
1744163ec60Skettenis 	delay(1000000);
1754163ec60Skettenis }
176