xref: /openbsd/sys/dev/fdt/syscon.c (revision 6f40fd34)
1 /*	$OpenBSD: syscon.c,v 1.1 2017/03/09 20:04:21 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2017 Mark Kettenis
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_misc.h>
27 #include <dev/ofw/fdt.h>
28 
29 extern void (*cpuresetfn)(void);
30 extern void (*powerdownfn)(void);
31 
32 struct syscon_softc {
33 	struct device	sc_dev;
34 	uint32_t	sc_regmap;
35 	bus_size_t	sc_offset;
36 	uint32_t	sc_mask;
37 };
38 
39 struct syscon_softc *syscon_reboot_sc;
40 struct syscon_softc *syscon_poweroff_sc;
41 
42 int	syscon_match(struct device *, void *, void *);
43 void	syscon_attach(struct device *, struct device *, void *);
44 
45 struct cfattach syscon_ca = {
46 	sizeof(struct syscon_softc), syscon_match, syscon_attach
47 };
48 
49 struct cfdriver syscon_cd = {
50 	NULL, "syscon", DV_DULL
51 };
52 
53 void	syscon_reset(void);
54 void	syscon_powerdown(void);
55 
56 int
57 syscon_match(struct device *parent, void *match, void *aux)
58 {
59 	struct fdt_attach_args *faa = aux;
60 
61 	return (OF_is_compatible(faa->fa_node, "syscon-reboot") ||
62 	    OF_is_compatible(faa->fa_node, "syscon-poweroff"));
63 }
64 
65 void
66 syscon_attach(struct device *parent, struct device *self, void *aux)
67 {
68 	struct syscon_softc *sc = (struct syscon_softc *)self;
69 	struct fdt_attach_args *faa = aux;
70 
71 	printf("\n");
72 
73 	sc->sc_regmap = OF_getpropint(faa->fa_node, "regmap", 0);
74 	if (sc->sc_regmap == 0)
75 		return;
76 
77 	if (OF_getproplen(faa->fa_node, "offset") != sizeof(uint32_t) ||
78 	    OF_getproplen(faa->fa_node, "mask") != sizeof(uint32_t))
79 		return;
80 
81 	sc->sc_offset = OF_getpropint(faa->fa_node, "offset", 0);
82 	sc->sc_mask = OF_getpropint(faa->fa_node, "mask", 0);
83 
84 	if (OF_is_compatible(faa->fa_node, "syscon-reboot")) {
85 		syscon_reboot_sc = sc;
86 		cpuresetfn = syscon_reset;
87 	} else {
88 		syscon_poweroff_sc = sc;
89 		powerdownfn = syscon_powerdown;
90 	}
91 }
92 
93 void
94 syscon_reset(void)
95 {
96 	struct syscon_softc *sc = syscon_reboot_sc;
97 	struct regmap *rm;
98 
99 	rm = regmap_byphandle(sc->sc_regmap);
100 	if (rm == NULL)
101 		return;
102 
103 	regmap_write_4(rm, sc->sc_offset, sc->sc_mask);
104 	delay(1000000);
105 }
106 
107 void
108 syscon_powerdown(void)
109 {
110 	struct syscon_softc *sc = syscon_poweroff_sc;
111 	struct regmap *rm;
112 
113 	rm = regmap_byphandle(sc->sc_regmap);
114 	if (rm == NULL)
115 		return;
116 
117 	regmap_write_4(rm, sc->sc_offset, sc->sc_mask);
118 	delay(1000000);
119 }
120