jcs's openbsd hax
openbsd
1/* $OpenBSD: syscon.c,v 1.8 2023/09/22 01:10:44 jsg 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#include <machine/simplebusvar.h>
30
31extern void (*cpuresetfn)(void);
32extern void (*powerdownfn)(void);
33
34struct syscon_softc {
35 struct simplebus_softc sc_sbus;
36 bus_space_tag_t sc_iot;
37 bus_space_handle_t sc_ioh;
38 uint32_t sc_regmap;
39 bus_size_t sc_offset;
40 uint32_t sc_mask;
41 uint32_t sc_value;
42};
43
44struct syscon_softc *syscon_reboot_sc;
45struct syscon_softc *syscon_poweroff_sc;
46
47int syscon_match(struct device *, void *, void *);
48void syscon_attach(struct device *, struct device *, void *);
49
50const struct cfattach syscon_ca = {
51 sizeof(struct syscon_softc), syscon_match, syscon_attach
52};
53
54struct cfdriver syscon_cd = {
55 NULL, "syscon", DV_DULL
56};
57
58void syscon_reset(void);
59void syscon_powerdown(void);
60
61int
62syscon_match(struct device *parent, void *match, void *aux)
63{
64 struct fdt_attach_args *faa = aux;
65
66 return OF_is_compatible(faa->fa_node, "syscon") ||
67 OF_is_compatible(faa->fa_node, "syscon-reboot") ||
68 OF_is_compatible(faa->fa_node, "syscon-poweroff");
69}
70
71void
72syscon_attach(struct device *parent, struct device *self, void *aux)
73{
74 struct syscon_softc *sc = (struct syscon_softc *)self;
75 struct fdt_attach_args *faa = aux;
76 char name[32];
77
78 OF_getprop(faa->fa_node, "name", name, sizeof(name));
79 name[sizeof(name) - 1] = 0;
80
81 if (OF_is_compatible(faa->fa_node, "syscon")) {
82 if (faa->fa_nreg < 1) {
83 printf(": no registers\n");
84 return;
85 }
86
87 sc->sc_iot = faa->fa_iot;
88
89 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
90 faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
91 printf(": can't map registers\n");
92 return;
93 }
94
95 regmap_register(faa->fa_node, sc->sc_iot, sc->sc_ioh,
96 faa->fa_reg[0].size);
97 }
98
99 if (OF_is_compatible(faa->fa_node, "simple-mfd"))
100 simplebus_attach(parent, &sc->sc_sbus.sc_dev, faa);
101 else
102 printf(": \"%s\"\n", name);
103
104 if (OF_is_compatible(faa->fa_node, "syscon-reboot") ||
105 OF_is_compatible(faa->fa_node, "syscon-poweroff")) {
106 sc->sc_regmap = OF_getpropint(faa->fa_node, "regmap", 0);
107 if (sc->sc_regmap == 0)
108 return;
109
110 if (OF_getproplen(faa->fa_node, "offset") != sizeof(uint32_t))
111 return;
112
113 /* At least one of "mask" and "value" should be provided. */
114 if (OF_getproplen(faa->fa_node, "mask") != sizeof(uint32_t) &&
115 OF_getproplen(faa->fa_node, "value") != sizeof(uint32_t))
116 return;
117
118 sc->sc_offset = OF_getpropint(faa->fa_node, "offset", 0);
119 sc->sc_mask = OF_getpropint(faa->fa_node, "mask", 0xffffffff);
120 sc->sc_value = OF_getpropint(faa->fa_node, "value", 0);
121
122 /*
123 * Old binding used "mask" as the value to write with
124 * an all-ones mask. This is still supported.
125 */
126 if (OF_getproplen(faa->fa_node, "value") != sizeof(uint32_t)) {
127 sc->sc_value = sc->sc_mask;
128 sc->sc_mask = 0xffffffff;
129 }
130
131 if (OF_is_compatible(faa->fa_node, "syscon-reboot")) {
132 syscon_reboot_sc = sc;
133 cpuresetfn = syscon_reset;
134 } else if (OF_is_compatible(faa->fa_node, "syscon-poweroff")) {
135 syscon_poweroff_sc = sc;
136 powerdownfn = syscon_powerdown;
137 }
138 }
139}
140
141void
142syscon_reset(void)
143{
144 struct syscon_softc *sc = syscon_reboot_sc;
145 struct regmap *rm;
146 uint32_t value;
147
148 rm = regmap_byphandle(sc->sc_regmap);
149 if (rm == NULL)
150 return;
151
152 value = regmap_read_4(rm, sc->sc_offset);
153 value &= ~sc->sc_mask;
154 value |= sc->sc_value;
155 regmap_write_4(rm, sc->sc_offset, value);
156 delay(1000000);
157}
158
159void
160syscon_powerdown(void)
161{
162 struct syscon_softc *sc = syscon_poweroff_sc;
163 struct regmap *rm;
164 uint32_t value;
165
166 rm = regmap_byphandle(sc->sc_regmap);
167 if (rm == NULL)
168 return;
169
170 value = regmap_read_4(rm, sc->sc_offset);
171 value &= ~sc->sc_mask;
172 value |= sc->sc_value;
173 regmap_write_4(rm, sc->sc_offset, value);
174 delay(1000000);
175}