|
1 |
| -/* $NetBSD: acpi_gpio.c,v 1.2 2024/12/09 22:10:25 jmcneill Exp $ */ |
| 1 | +/* $NetBSD: acpi_gpio.c,v 1.3 2024/12/11 01:00:02 jmcneill Exp $ */ |
2 | 2 |
|
3 | 3 | /*-
|
4 | 4 | * Copyright (c) 2024 The NetBSD Foundation, Inc.
|
|
34 | 34 | */
|
35 | 35 |
|
36 | 36 | #include <sys/cdefs.h>
|
37 |
| -__KERNEL_RCSID(0, "$NetBSD: acpi_gpio.c,v 1.2 2024/12/09 22:10:25 jmcneill Exp $"); |
| 37 | +__KERNEL_RCSID(0, "$NetBSD: acpi_gpio.c,v 1.3 2024/12/11 01:00:02 jmcneill Exp $"); |
38 | 38 |
|
39 | 39 | #include <sys/param.h>
|
| 40 | +#include <sys/kmem.h> |
40 | 41 | #include <sys/gpio.h>
|
41 | 42 |
|
| 43 | +#include <dev/gpio/gpiovar.h> |
| 44 | + |
42 | 45 | #include <dev/acpi/acpireg.h>
|
43 | 46 | #include <dev/acpi/acpivar.h>
|
44 | 47 | #include <dev/acpi/acpi_gpio.h>
|
45 | 48 |
|
46 |
| -int |
| 49 | +struct acpi_gpio_address_space_context { |
| 50 | + ACPI_CONNECTION_INFO conn_info; /* must be first */ |
| 51 | + struct acpi_devnode *ad; |
| 52 | +}; |
| 53 | + |
| 54 | +static ACPI_STATUS |
| 55 | +acpi_gpio_address_space_init(ACPI_HANDLE region_hdl, UINT32 function, |
| 56 | + void *handler_ctx, void **region_ctx) |
| 57 | +{ |
| 58 | + if (function == ACPI_REGION_DEACTIVATE) { |
| 59 | + *region_ctx = NULL; |
| 60 | + } else { |
| 61 | + *region_ctx = region_hdl; |
| 62 | + } |
| 63 | + return AE_OK; |
| 64 | +} |
| 65 | + |
| 66 | +static ACPI_STATUS |
| 67 | +acpi_gpio_address_space_handler(UINT32 function, |
| 68 | + ACPI_PHYSICAL_ADDRESS address, UINT32 bit_width, UINT64 *value, |
| 69 | + void *handler_ctx, void *region_ctx) |
| 70 | +{ |
| 71 | + ACPI_OPERAND_OBJECT *region_obj = region_ctx; |
| 72 | + struct acpi_gpio_address_space_context *context = handler_ctx; |
| 73 | + ACPI_CONNECTION_INFO *conn_info = &context->conn_info; |
| 74 | + struct acpi_devnode *ad = context->ad; |
| 75 | + ACPI_RESOURCE *res; |
| 76 | + ACPI_STATUS rv; |
| 77 | + struct gpio_pinmap pinmap; |
| 78 | + int pins[1]; |
| 79 | + void *gpiop; |
| 80 | + int pin; |
| 81 | + |
| 82 | + if (region_obj->Region.Type != ACPI_TYPE_REGION) { |
| 83 | + return AE_OK; |
| 84 | + } |
| 85 | + |
| 86 | + if (ad->ad_gpiodev == NULL) { |
| 87 | + return AE_NO_HANDLER; |
| 88 | + } |
| 89 | + |
| 90 | + rv = AcpiBufferToResource(conn_info->Connection, |
| 91 | + conn_info->Length, &res); |
| 92 | + if (ACPI_FAILURE(rv)) { |
| 93 | + return rv; |
| 94 | + } |
| 95 | + |
| 96 | + if (res->Data.Gpio.PinTableLength != 1) { |
| 97 | + /* TODO */ |
| 98 | + aprint_debug_dev(ad->ad_gpiodev, |
| 99 | + "Pin table length %u not implemented\n", |
| 100 | + res->Data.Gpio.PinTableLength); |
| 101 | + rv = AE_NOT_IMPLEMENTED; |
| 102 | + goto done; |
| 103 | + } |
| 104 | + |
| 105 | + pin = ad->ad_gpio_translate(ad->ad_gpio_priv, |
| 106 | + &res->Data.Gpio, &gpiop); |
| 107 | + if (pin == -1) { |
| 108 | + /* Pin could not be translated. */ |
| 109 | + rv = AE_SUPPORT; |
| 110 | + goto done; |
| 111 | + } |
| 112 | + |
| 113 | + pinmap.pm_map = pins; |
| 114 | + if (gpio_pin_map(gpiop, pin, 1, &pinmap) != 0) { |
| 115 | + rv = AE_NOT_ACQUIRED; |
| 116 | + goto done; |
| 117 | + } |
| 118 | + if (function & ACPI_IO_MASK) { |
| 119 | + gpio_pin_write(gpiop, &pinmap, 0, *value & 1); |
| 120 | + } else { |
| 121 | + *value = gpio_pin_read(gpiop, &pinmap, 0); |
| 122 | + } |
| 123 | + gpio_pin_unmap(gpiop, &pinmap); |
| 124 | + |
| 125 | +done: |
| 126 | + ACPI_FREE(res); |
| 127 | + |
| 128 | + return rv; |
| 129 | +} |
| 130 | + |
| 131 | +ACPI_STATUS |
47 | 132 | acpi_gpio_register(struct acpi_devnode *ad, device_t dev,
|
48 | 133 | int (*translate)(void *, ACPI_RESOURCE_GPIO *, void **), void *priv)
|
49 | 134 | {
|
| 135 | + struct acpi_gpio_address_space_context *context; |
| 136 | + ACPI_STATUS rv; |
| 137 | + |
50 | 138 | if (ad->ad_gpiodev != NULL) {
|
51 | 139 | device_printf(dev, "%s already registered\n",
|
52 | 140 | device_xname(ad->ad_gpiodev));
|
53 |
| - return EBUSY; |
| 141 | + return AE_ALREADY_EXISTS; |
| 142 | + } |
| 143 | + |
| 144 | + context = kmem_zalloc(sizeof(*context), KM_SLEEP); |
| 145 | + context->ad = ad; |
| 146 | + |
| 147 | + rv = AcpiInstallAddressSpaceHandler(ad->ad_handle, |
| 148 | + ACPI_ADR_SPACE_GPIO, |
| 149 | + acpi_gpio_address_space_handler, |
| 150 | + acpi_gpio_address_space_init, |
| 151 | + context); |
| 152 | + if (ACPI_FAILURE(rv)) { |
| 153 | + aprint_error_dev(dev, |
| 154 | + "couldn't install address space handler: %s", |
| 155 | + AcpiFormatException(rv)); |
| 156 | + return rv; |
54 | 157 | }
|
55 | 158 |
|
56 | 159 | ad->ad_gpiodev = dev;
|
57 | 160 | ad->ad_gpio_translate = translate;
|
58 | 161 | ad->ad_gpio_priv = priv;
|
59 | 162 |
|
60 |
| - return 0; |
| 163 | + return AE_OK; |
61 | 164 | }
|
62 | 165 |
|
63 | 166 | static ACPI_STATUS
|
@@ -98,7 +201,7 @@ acpi_gpio_translate(ACPI_RESOURCE_GPIO *res, void **gpiop, int *pin)
|
98 | 201 | res, gpiop);
|
99 | 202 | if (xpin == -1) {
|
100 | 203 | /* Pin could not be translated. */
|
101 |
| - return AE_NOT_IMPLEMENTED; |
| 204 | + return AE_SUPPORT; |
102 | 205 | }
|
103 | 206 |
|
104 | 207 | *pin = xpin;
|
|
0 commit comments