1 /* $OpenBSD: dwgpio.c,v 1.8 2024/05/13 01:15:50 jsg Exp $ */
2 /*
3 * Copyright (c) 2020 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/malloc.h>
20 #include <sys/systm.h>
21
22 #include <dev/acpi/acpireg.h>
23 #include <dev/acpi/acpivar.h>
24 #include <dev/acpi/acpidev.h>
25 #include <dev/acpi/amltypes.h>
26 #include <dev/acpi/dsdt.h>
27
28 /* Registers. */
29 #define GPIO_SWPORTA_DR 0x0000
30 #define GPIO_SWPORTA_DDR 0x0004
31 #define GPIO_INTEN 0x0030
32 #define GPIO_INTTYPE_LEVEL 0x0034
33 #define GPIO_INT_POLARITY 0x0038
34 #define GPIO_INT_STATUS 0x003c
35 #define GPIO_PORTS_EOI 0x0040
36 #define GPIO_INTMASK 0x0044
37 #define GPIO_DEBOUNCE 0x0048
38 #define GPIO_EXT_PORTA 0x0050
39
40 #define GPIO_NUM_PORTS 4
41 #define GPIO_NUM_PINS 32
42
43 #define HREAD4(sc, reg) \
44 (bus_space_read_4((sc)->sc_memt, (sc)->sc_memh, (reg)))
45 #define HWRITE4(sc, reg, val) \
46 bus_space_write_4((sc)->sc_memt, (sc)->sc_memh, (reg), (val))
47 #define HSET4(sc, reg, bits) \
48 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
49 #define HCLR4(sc, reg, bits) \
50 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
51
52 struct dwgpio_intrhand {
53 int (*ih_func)(void *);
54 void *ih_arg;
55 };
56
57 struct dwgpio_softc {
58 struct device sc_dev;
59 struct acpi_softc *sc_acpi;
60 struct aml_node *sc_node;
61
62 bus_space_tag_t sc_memt;
63 bus_space_handle_t sc_memh;
64
65 int sc_nirq;
66 void **sc_ih;
67
68 uint32_t sc_npins;
69 struct dwgpio_intrhand *sc_pin_ih;
70
71 struct acpi_gpio sc_gpio;
72 };
73
74 int dwgpio_match(struct device *, void *, void *);
75 void dwgpio_attach(struct device *, struct device *, void *);
76
77 const struct cfattach dwgpio_ca = {
78 sizeof(struct dwgpio_softc), dwgpio_match, dwgpio_attach
79 };
80
81 struct cfdriver dwgpio_cd = {
82 NULL, "dwgpio", DV_DULL
83 };
84
85 const char *dwgpio_hids[] = {
86 "APMC0D81",
87 NULL
88 };
89
90 int dwgpio_found_hid(struct aml_node *, void *);
91 int dwgpio_read_pin(void *, int);
92 void dwgpio_write_pin(void *, int, int);
93 void dwgpio_intr_establish(void *, int, int, int (*)(void *), void *);
94 int dwgpio_intr(void *);
95
96 int
dwgpio_match(struct device * parent,void * match,void * aux)97 dwgpio_match(struct device *parent, void *match, void *aux)
98 {
99 struct acpi_attach_args *aaa = aux;
100 struct cfdata *cf = match;
101
102 if (aaa->aaa_naddr < 1 || aaa->aaa_nirq < 1)
103 return 0;
104 return acpi_matchhids(aaa, dwgpio_hids, cf->cf_driver->cd_name);
105 }
106
107 void
dwgpio_attach(struct device * parent,struct device * self,void * aux)108 dwgpio_attach(struct device *parent, struct device *self, void *aux)
109 {
110 struct acpi_attach_args *aaa = aux;
111 struct dwgpio_softc *sc = (struct dwgpio_softc *)self;
112 int i;
113
114 sc->sc_acpi = (struct acpi_softc *)parent;
115 sc->sc_node = aaa->aaa_node;
116 printf(" %s", sc->sc_node->name);
117
118 printf(" addr 0x%llx/0x%llx", aaa->aaa_addr[0], aaa->aaa_size[0]);
119
120 sc->sc_memt = aaa->aaa_bst[0];
121 if (bus_space_map(sc->sc_memt, aaa->aaa_addr[0], aaa->aaa_size[0],
122 0, &sc->sc_memh)) {
123 printf(": can't map registers\n");
124 return;
125 }
126
127 aml_find_node(sc->sc_node, "_HID", dwgpio_found_hid, sc);
128
129 if (sc->sc_npins == 0) {
130 printf(": no pins\n");
131 return;
132 }
133 if (sc->sc_npins >= GPIO_NUM_PINS) {
134 printf(": too many pins\n");
135 return;
136 }
137
138 sc->sc_pin_ih = mallocarray(sc->sc_npins, sizeof(*sc->sc_pin_ih),
139 M_DEVBUF, M_WAITOK | M_ZERO);
140
141 printf(" irq");
142
143 sc->sc_nirq = aaa->aaa_nirq;
144 sc->sc_ih = mallocarray(sc->sc_nirq, sizeof(*sc->sc_ih),
145 M_DEVBUF, M_WAITOK | M_ZERO);
146 for (i = 0; i < sc->sc_nirq; i++) {
147 printf(" %d", aaa->aaa_irq[i]);
148
149 sc->sc_ih[i] = acpi_intr_establish(aaa->aaa_irq[i],
150 aaa->aaa_irq_flags[i], IPL_BIO, dwgpio_intr,
151 sc, sc->sc_dev.dv_xname);
152 if (sc->sc_ih[i] == NULL) {
153 printf(": can't establish interrupt\n");
154 goto unmap;
155 }
156 }
157
158 sc->sc_gpio.cookie = sc;
159 sc->sc_gpio.read_pin = dwgpio_read_pin;
160 sc->sc_gpio.write_pin = dwgpio_write_pin;
161 sc->sc_gpio.intr_establish = dwgpio_intr_establish;
162 sc->sc_node->gpio = &sc->sc_gpio;
163
164 printf(", %d pins\n", sc->sc_npins);
165
166 acpi_register_gpio(sc->sc_acpi, sc->sc_node);
167 return;
168
169 unmap:
170 for (i = 0; i < sc->sc_nirq; i++) {
171 if (sc->sc_ih[i])
172 acpi_intr_disestablish(sc->sc_ih[i]);
173 }
174 free(sc->sc_ih, M_DEVBUF, sc->sc_nirq * sizeof(*sc->sc_ih));
175 free(sc->sc_pin_ih, M_DEVBUF, sc->sc_npins * sizeof(*sc->sc_pin_ih));
176 bus_space_unmap(sc->sc_memt, sc->sc_memh, aaa->aaa_size[0]);
177 }
178
179
180 int
dwgpio_found_hid(struct aml_node * child,void * arg)181 dwgpio_found_hid(struct aml_node *child, void *arg)
182 {
183 struct dwgpio_softc *sc = (struct dwgpio_softc *)arg;
184 struct aml_node *node = child->parent;
185 int reg;
186
187 /* Skip our own _HID. */
188 if (node == sc->sc_node)
189 return 0;
190
191 /* Only handle a single port for now. */
192 reg = acpi_getpropint(node, "reg", -1);
193 if (reg != 0)
194 return 0;
195
196 sc->sc_npins = acpi_getpropint(node, "snps,nr-gpios", GPIO_NUM_PINS);
197 node->attached = 1;
198 return 0;
199 }
200
201 int
dwgpio_read_pin(void * cookie,int pin)202 dwgpio_read_pin(void *cookie, int pin)
203 {
204 struct dwgpio_softc *sc = cookie;
205 uint32_t reg;
206 int val;
207
208 if (pin < 0 || pin >= sc->sc_npins)
209 return 0;
210
211 reg = HREAD4(sc, GPIO_EXT_PORTA);
212 val = (reg >> pin) & 1;
213 return val;
214 }
215
216 void
dwgpio_write_pin(void * cookie,int pin,int val)217 dwgpio_write_pin(void *cookie, int pin, int val)
218 {
219 struct dwgpio_softc *sc = cookie;
220
221 if (pin < 0 || pin >= sc->sc_npins)
222 return;
223
224 if (val)
225 HSET4(sc, GPIO_SWPORTA_DR, (1 << pin));
226 else
227 HCLR4(sc, GPIO_SWPORTA_DR, (1 << pin));
228 }
229
230 void
dwgpio_intr_establish(void * cookie,int pin,int flags,int (* func)(void *),void * arg)231 dwgpio_intr_establish(void *cookie, int pin, int flags,
232 int (*func)(void *), void *arg)
233 {
234 struct dwgpio_softc *sc = cookie;
235
236 if (pin < 0 || pin >= sc->sc_npins)
237 return;
238
239 sc->sc_pin_ih[pin].ih_func = func;
240 sc->sc_pin_ih[pin].ih_arg = arg;
241
242 if ((flags & LR_GPIO_MODE) == LR_GPIO_EDGE)
243 HSET4(sc, GPIO_INTTYPE_LEVEL, 1 << pin);
244 else
245 HCLR4(sc, GPIO_INTTYPE_LEVEL, 1 << pin);
246 if ((flags & LR_GPIO_POLARITY) == LR_GPIO_ACTHI)
247 HSET4(sc, GPIO_INT_POLARITY, 1 << pin);
248 else
249 HCLR4(sc, GPIO_INT_POLARITY, 1 << pin);
250
251 HCLR4(sc, GPIO_SWPORTA_DDR, 1 << pin);
252 HSET4(sc, GPIO_INTEN, 1 << pin);
253 HCLR4(sc, GPIO_INTMASK, 1 << pin);
254 }
255
256 int
dwgpio_intr(void * arg)257 dwgpio_intr(void *arg)
258 {
259 struct dwgpio_softc *sc = arg;
260 uint32_t status;
261 int pin, handled = 0;
262
263 status = HREAD4(sc, GPIO_INT_STATUS);
264 HWRITE4(sc, GPIO_PORTS_EOI, status);
265
266 for (pin = 0; pin < sc->sc_npins; pin++) {
267 if ((status & (1 << pin)) && sc->sc_pin_ih[pin].ih_func) {
268 sc->sc_pin_ih[pin].ih_func(sc->sc_pin_ih[pin].ih_arg);
269 handled = 1;
270 }
271 }
272
273 return handled;
274 }
275