Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/amdgpio/amdgpio.c
39507 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
3
*
4
* Copyright (c) 2018 Advanced Micro Devices
5
* All rights reserved.
6
* Copyright (c) 2025 The FreeBSD Foundation
7
*
8
* Portions of this software were developed by Aymeric Wibo
9
* <[email protected]> under sponsorship from the FreeBSD Foundation.
10
*
11
* Redistribution and use in source and binary forms, with or without
12
* modification, are permitted provided that the following conditions
13
* are met:
14
* 1. Redistributions of source code must retain the above copyright
15
* notice, this list of conditions and the following disclaimer.
16
* 2. Redistributions in binary form must reproduce the above copyright
17
* notice, this list of conditions and the following disclaimer in the
18
* documentation and/or other materials provided with the distribution.
19
*
20
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30
* SUCH DAMAGE.
31
*/
32
33
#include <sys/cdefs.h>
34
#include "opt_acpi.h"
35
36
#include <sys/param.h>
37
#include <sys/systm.h>
38
#include <sys/bus.h>
39
#include <sys/gpio.h>
40
#include <sys/interrupt.h>
41
#include <sys/kernel.h>
42
#include <sys/lock.h>
43
#include <sys/module.h>
44
#include <sys/mutex.h>
45
#include <sys/proc.h>
46
#include <sys/rman.h>
47
#include <sys/sysctl.h>
48
49
#include <machine/bus.h>
50
#include <machine/resource.h>
51
52
#include <contrib/dev/acpica/include/acpi.h>
53
#include <contrib/dev/acpica/include/accommon.h>
54
55
#include <dev/acpica/acpivar.h>
56
#include <dev/gpio/gpiobusvar.h>
57
58
#include "amdgpio.h"
59
60
static struct resource_spec amdgpio_spec[] = {
61
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
62
{ SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE },
63
{ -1, 0, 0 }
64
};
65
66
static inline uint32_t
67
amdgpio_read_4(struct amdgpio_softc *sc, bus_size_t off)
68
{
69
return (bus_read_4(sc->sc_res[0], off));
70
}
71
72
static inline void
73
amdgpio_write_4(struct amdgpio_softc *sc, bus_size_t off,
74
uint32_t val)
75
{
76
bus_write_4(sc->sc_res[0], off, val);
77
}
78
79
static bool
80
amdgpio_is_pin_output(struct amdgpio_softc *sc, uint32_t pin)
81
{
82
uint32_t reg, val;
83
bool ret;
84
85
/* Get the current pin state */
86
AMDGPIO_LOCK(sc);
87
88
reg = AMDGPIO_PIN_REGISTER(pin);
89
val = amdgpio_read_4(sc, reg);
90
91
if (val & BIT(OUTPUT_ENABLE_OFF))
92
ret = true;
93
else
94
ret = false;
95
96
AMDGPIO_UNLOCK(sc);
97
98
return (ret);
99
}
100
101
static device_t
102
amdgpio_get_bus(device_t dev)
103
{
104
struct amdgpio_softc *sc;
105
106
sc = device_get_softc(dev);
107
108
dprintf("busdev %p\n", sc->sc_busdev);
109
return (sc->sc_busdev);
110
}
111
112
static int
113
amdgpio_pin_max(device_t dev, int *maxpin)
114
{
115
struct amdgpio_softc *sc;
116
117
sc = device_get_softc(dev);
118
119
*maxpin = sc->sc_npins - 1;
120
dprintf("npins %d maxpin %d\n", sc->sc_npins, *maxpin);
121
122
return (0);
123
}
124
125
static bool
126
amdgpio_valid_pin(struct amdgpio_softc *sc, int pin)
127
{
128
dprintf("pin %d\n", pin);
129
if (sc->sc_res[0] == NULL)
130
return (false);
131
132
if ((sc->sc_gpio_pins[pin].gp_pin == pin) &&
133
(sc->sc_gpio_pins[pin].gp_caps != 0))
134
return (true);
135
136
return (false);
137
}
138
139
static int
140
amdgpio_pin_getname(device_t dev, uint32_t pin, char *name)
141
{
142
struct amdgpio_softc *sc;
143
144
dprintf("pin %d\n", pin);
145
sc = device_get_softc(dev);
146
147
if (!amdgpio_valid_pin(sc, pin))
148
return (EINVAL);
149
150
/* Set a very simple name */
151
snprintf(name, GPIOMAXNAME, "%s", sc->sc_gpio_pins[pin].gp_name);
152
name[GPIOMAXNAME - 1] = '\0';
153
154
dprintf("pin %d name %s\n", pin, name);
155
156
return (0);
157
}
158
159
static int
160
amdgpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
161
{
162
struct amdgpio_softc *sc;
163
164
sc = device_get_softc(dev);
165
166
dprintf("pin %d\n", pin);
167
if (!amdgpio_valid_pin(sc, pin))
168
return (EINVAL);
169
170
*caps = sc->sc_gpio_pins[pin].gp_caps;
171
172
dprintf("pin %d caps 0x%x\n", pin, *caps);
173
174
return (0);
175
}
176
177
static int
178
amdgpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
179
{
180
struct amdgpio_softc *sc;
181
182
sc = device_get_softc(dev);
183
184
dprintf("pin %d\n", pin);
185
if (!amdgpio_valid_pin(sc, pin))
186
return (EINVAL);
187
188
AMDGPIO_LOCK(sc);
189
190
*flags = sc->sc_gpio_pins[pin].gp_flags;
191
192
dprintf("pin %d flags 0x%x\n", pin, *flags);
193
194
AMDGPIO_UNLOCK(sc);
195
196
return (0);
197
}
198
199
static int
200
amdgpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
201
{
202
struct amdgpio_softc *sc;
203
uint32_t reg, val;
204
205
sc = device_get_softc(dev);
206
207
dprintf("pin %d flags 0x%x\n", pin, flags);
208
if (!amdgpio_valid_pin(sc, pin))
209
return (EINVAL);
210
211
if ((flags & ~AMDGPIO_DEFAULT_CAPS) != 0) {
212
device_printf(dev, "disallowed flags (0x%x) trying to be set "
213
"(allowed is 0x%x)\n", flags, AMDGPIO_DEFAULT_CAPS);
214
return (EINVAL);
215
}
216
217
/* Either input or output must be selected. */
218
if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == 0)
219
return (EINVAL);
220
221
/* Not both directions simultaneously. */
222
if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
223
(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT))
224
return (EINVAL);
225
226
/* Set the GPIO mode and state */
227
AMDGPIO_LOCK(sc);
228
229
reg = AMDGPIO_PIN_REGISTER(pin);
230
val = amdgpio_read_4(sc, reg);
231
232
if ((flags & GPIO_PIN_INPUT) != 0)
233
val &= ~BIT(OUTPUT_ENABLE_OFF);
234
else
235
val |= BIT(OUTPUT_ENABLE_OFF);
236
237
val &= ~(BIT(PULL_DOWN_ENABLE_OFF) | BIT(PULL_UP_ENABLE_OFF));
238
239
if ((flags & GPIO_PIN_PULLDOWN) != 0)
240
val |= BIT(PULL_DOWN_ENABLE_OFF);
241
if ((flags & GPIO_PIN_PULLUP) != 0)
242
val |= BIT(PULL_UP_ENABLE_OFF);
243
244
amdgpio_write_4(sc, reg, val);
245
246
sc->sc_gpio_pins[pin].gp_flags = flags;
247
dprintf("pin %d flags 0x%x val 0x%x gp_flags 0x%x\n",
248
pin, flags, val, sc->sc_gpio_pins[pin].gp_flags);
249
250
AMDGPIO_UNLOCK(sc);
251
252
return (0);
253
}
254
255
static int
256
amdgpio_pin_get(device_t dev, uint32_t pin, unsigned int *value)
257
{
258
struct amdgpio_softc *sc;
259
uint32_t reg, val;
260
261
sc = device_get_softc(dev);
262
263
dprintf("pin %d\n", pin);
264
if (!amdgpio_valid_pin(sc, pin))
265
return (EINVAL);
266
267
*value = 0;
268
269
AMDGPIO_LOCK(sc);
270
271
reg = AMDGPIO_PIN_REGISTER(pin);
272
val = amdgpio_read_4(sc, reg);
273
274
if ((sc->sc_gpio_pins[pin].gp_flags & GPIO_PIN_OUTPUT) != 0) {
275
if (val & BIT(OUTPUT_VALUE_OFF))
276
*value = GPIO_PIN_HIGH;
277
else
278
*value = GPIO_PIN_LOW;
279
} else {
280
if (val & BIT(PIN_STS_OFF))
281
*value = GPIO_PIN_HIGH;
282
else
283
*value = GPIO_PIN_LOW;
284
}
285
286
dprintf("pin %d value 0x%x\n", pin, *value);
287
288
AMDGPIO_UNLOCK(sc);
289
290
return (0);
291
}
292
293
static int
294
amdgpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
295
{
296
struct amdgpio_softc *sc;
297
uint32_t reg, val;
298
299
sc = device_get_softc(dev);
300
301
dprintf("pin %d value 0x%x\n", pin, value);
302
if (!amdgpio_valid_pin(sc, pin))
303
return (EINVAL);
304
305
if (!amdgpio_is_pin_output(sc, pin))
306
return (EINVAL);
307
308
AMDGPIO_LOCK(sc);
309
310
reg = AMDGPIO_PIN_REGISTER(pin);
311
val = amdgpio_read_4(sc, reg);
312
313
if (value == GPIO_PIN_LOW)
314
val &= ~BIT(OUTPUT_VALUE_OFF);
315
else
316
val |= BIT(OUTPUT_VALUE_OFF);
317
318
amdgpio_write_4(sc, reg, val);
319
320
dprintf("pin %d value 0x%x val 0x%x\n", pin, value, val);
321
322
AMDGPIO_UNLOCK(sc);
323
324
return (0);
325
}
326
327
static int
328
amdgpio_pin_toggle(device_t dev, uint32_t pin)
329
{
330
struct amdgpio_softc *sc;
331
uint32_t reg, val;
332
333
sc = device_get_softc(dev);
334
335
dprintf("pin %d\n", pin);
336
if (!amdgpio_valid_pin(sc, pin))
337
return (EINVAL);
338
339
if (!amdgpio_is_pin_output(sc, pin))
340
return (EINVAL);
341
342
/* Toggle the pin */
343
AMDGPIO_LOCK(sc);
344
345
reg = AMDGPIO_PIN_REGISTER(pin);
346
val = amdgpio_read_4(sc, reg);
347
dprintf("pin %d value before 0x%x\n", pin, val);
348
val = val ^ BIT(OUTPUT_VALUE_OFF);
349
dprintf("pin %d value after 0x%x\n", pin, val);
350
amdgpio_write_4(sc, reg, val);
351
352
AMDGPIO_UNLOCK(sc);
353
354
return (0);
355
}
356
357
static int
358
amdgpio_probe(device_t dev)
359
{
360
static char *gpio_ids[] = { "AMD0030", "AMDI0030", NULL };
361
int rv;
362
363
if (acpi_disabled("gpio"))
364
return (ENXIO);
365
rv = ACPI_ID_PROBE(device_get_parent(dev), dev, gpio_ids, NULL);
366
if (rv <= 0)
367
device_set_desc(dev, "AMD GPIO Controller");
368
369
return (rv);
370
}
371
372
static void
373
amdgpio_eoi_locked(struct amdgpio_softc *sc)
374
{
375
uint32_t master_reg = amdgpio_read_4(sc, WAKE_INT_MASTER_REG);
376
377
AMDGPIO_ASSERT_LOCKED(sc);
378
master_reg |= EOI_MASK;
379
amdgpio_write_4(sc, WAKE_INT_MASTER_REG, master_reg);
380
}
381
382
static void
383
amdgpio_eoi(struct amdgpio_softc *sc)
384
{
385
AMDGPIO_LOCK(sc);
386
amdgpio_eoi_locked(sc);
387
AMDGPIO_UNLOCK(sc);
388
}
389
390
static int
391
amdgpio_intr_filter(void *arg)
392
{
393
struct amdgpio_softc *sc = arg;
394
int off, rv = FILTER_STRAY;
395
uint32_t reg;
396
397
/* We can lock in the filter routine as it is MTX_SPIN. */
398
AMDGPIO_LOCK(sc);
399
400
/*
401
* TODO Instead of just reading the registers of all pins, we should
402
* read WAKE_INT_STATUS_REG0/1. A bit set in here denotes a group of
403
* 4 pins where at least one has an interrupt for us. Then we can just
404
* iterate over those 4 pins.
405
*
406
* See GPIO_Interrupt_Status_Index_0 in BKDG.
407
*/
408
for (size_t pin = 0; pin < AMD_GPIO_PINS_EXPOSED; pin++) {
409
off = AMDGPIO_PIN_REGISTER(pin);
410
reg = amdgpio_read_4(sc, off);
411
if ((reg & UNSERVICED_INTERRUPT_MASK) == 0)
412
continue;
413
/*
414
* Must write 1's to wake/interrupt status bits to clear them.
415
* We can do this simply by writing back to the register.
416
*/
417
amdgpio_write_4(sc, off, reg);
418
}
419
420
amdgpio_eoi_locked(sc);
421
AMDGPIO_UNLOCK(sc);
422
423
rv = FILTER_HANDLED;
424
return (rv);
425
}
426
427
static void
428
amdgpio_intr_handler(void *arg)
429
{
430
/* TODO */
431
}
432
433
static int
434
amdgpio_attach(device_t dev)
435
{
436
struct amdgpio_softc *sc;
437
int i, pin, bank, reg;
438
uint32_t flags;
439
440
sc = device_get_softc(dev);
441
sc->sc_dev = dev;
442
sc->sc_handle = acpi_get_handle(dev);
443
444
AMDGPIO_LOCK_INIT(sc);
445
446
sc->sc_nbanks = AMD_GPIO_NUM_PIN_BANK;
447
sc->sc_npins = AMD_GPIO_PINS_MAX;
448
sc->sc_bank_prefix = AMD_GPIO_PREFIX;
449
sc->sc_pin_info = kernzp_pins;
450
sc->sc_ngroups = nitems(kernzp_groups);
451
sc->sc_groups = kernzp_groups;
452
453
if (bus_alloc_resources(dev, amdgpio_spec, sc->sc_res)) {
454
device_printf(dev, "could not allocate resources\n");
455
goto err_rsrc;
456
}
457
458
sc->sc_bst = rman_get_bustag(sc->sc_res[0]);
459
sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]);
460
461
/* Set up interrupt handler. */
462
if (bus_setup_intr(dev, sc->sc_res[1], INTR_TYPE_MISC | INTR_MPSAFE,
463
amdgpio_intr_filter, amdgpio_intr_handler, sc, &sc->sc_intr_handle)
464
!= 0) {
465
device_printf(dev, "couldn't set up interrupt\n");
466
goto err_intr;
467
}
468
469
/* Initialize all possible pins to be Invalid */
470
for (i = 0; i < AMD_GPIO_PINS_MAX ; i++) {
471
snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
472
"Unexposed PIN %d", i);
473
sc->sc_gpio_pins[i].gp_pin = -1;
474
sc->sc_gpio_pins[i].gp_caps = 0;
475
sc->sc_gpio_pins[i].gp_flags = 0;
476
}
477
478
/*
479
* Initialize only driver exposed pins with appropriate capabilities.
480
*
481
* XXX Also mask and disable interrupts on all pins, since we don't
482
* support them at the moment.
483
*/
484
for (i = 0; i < AMD_GPIO_PINS_EXPOSED ; i++) {
485
pin = kernzp_pins[i].pin_num;
486
bank = pin/AMD_GPIO_PINS_PER_BANK;
487
snprintf(sc->sc_gpio_pins[pin].gp_name, GPIOMAXNAME, "%s%d_%s",
488
AMD_GPIO_PREFIX, bank, kernzp_pins[i].pin_name);
489
sc->sc_gpio_pins[pin].gp_pin = pin;
490
sc->sc_gpio_pins[pin].gp_caps = AMDGPIO_DEFAULT_CAPS;
491
sc->sc_gpio_pins[pin].gp_flags =
492
amdgpio_is_pin_output(sc, pin) ?
493
GPIO_PIN_OUTPUT : GPIO_PIN_INPUT;
494
495
reg = AMDGPIO_PIN_REGISTER(pin);
496
flags = amdgpio_read_4(sc, reg);
497
flags &= ~(1 << INTERRUPT_ENABLE_OFF);
498
flags &= ~(1 << INTERRUPT_MASK_OFF);
499
amdgpio_write_4(sc, reg, flags);
500
}
501
amdgpio_eoi(sc);
502
503
sc->sc_busdev = gpiobus_add_bus(dev);
504
if (sc->sc_busdev == NULL) {
505
device_printf(dev, "could not attach gpiobus\n");
506
goto err_bus;
507
}
508
509
bus_attach_children(dev);
510
return (0);
511
512
err_bus:
513
bus_teardown_intr(dev, sc->sc_res[1], sc->sc_intr_handle);
514
err_intr:
515
bus_release_resources(dev, amdgpio_spec, sc->sc_res);
516
err_rsrc:
517
AMDGPIO_LOCK_DESTROY(sc);
518
519
return (ENXIO);
520
}
521
522
static int
523
amdgpio_detach(device_t dev)
524
{
525
struct amdgpio_softc *sc;
526
sc = device_get_softc(dev);
527
528
if (sc->sc_busdev)
529
gpiobus_detach_bus(dev);
530
if (sc->sc_intr_handle)
531
bus_teardown_intr(dev, sc->sc_res[1], sc->sc_intr_handle);
532
bus_release_resources(dev, amdgpio_spec, sc->sc_res);
533
534
AMDGPIO_LOCK_DESTROY(sc);
535
536
return (0);
537
}
538
539
static device_method_t amdgpio_methods[] = {
540
/* Device interface */
541
DEVMETHOD(device_probe, amdgpio_probe),
542
DEVMETHOD(device_attach, amdgpio_attach),
543
DEVMETHOD(device_detach, amdgpio_detach),
544
545
/* GPIO protocol */
546
DEVMETHOD(gpio_get_bus, amdgpio_get_bus),
547
DEVMETHOD(gpio_pin_max, amdgpio_pin_max),
548
DEVMETHOD(gpio_pin_getname, amdgpio_pin_getname),
549
DEVMETHOD(gpio_pin_getcaps, amdgpio_pin_getcaps),
550
DEVMETHOD(gpio_pin_getflags, amdgpio_pin_getflags),
551
DEVMETHOD(gpio_pin_setflags, amdgpio_pin_setflags),
552
DEVMETHOD(gpio_pin_get, amdgpio_pin_get),
553
DEVMETHOD(gpio_pin_set, amdgpio_pin_set),
554
DEVMETHOD(gpio_pin_toggle, amdgpio_pin_toggle),
555
556
DEVMETHOD_END
557
};
558
559
static driver_t amdgpio_driver = {
560
"gpio",
561
amdgpio_methods,
562
sizeof(struct amdgpio_softc),
563
};
564
565
DRIVER_MODULE(amdgpio, acpi, amdgpio_driver, 0, 0);
566
MODULE_DEPEND(amdgpio, acpi, 1, 1, 1);
567
MODULE_DEPEND(amdgpio, gpiobus, 1, 1, 1);
568
MODULE_VERSION(amdgpio, 1);
569
570