1/* $NetBSD: igpio_acpi.c,v 1.3 2024/02/02 22:14:05 andvar Exp $ */ 2 3/*- 4 * Copyright (c) 2021,2022 Emmanuel Dreyfus 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: igpio_acpi.c,v 1.3 2024/02/02 22:14:05 andvar Exp $"); 31 32#include <sys/param.h> 33#include <sys/kmem.h> 34#include <sys/bus.h> 35#include <sys/cpu.h> 36#include <sys/device.h> 37#include <sys/gpio.h> 38 39#include <dev/acpi/acpireg.h> 40#include <dev/acpi/acpivar.h> 41#include <dev/acpi/acpi_intr.h> 42#include <dev/acpi/acpi_event.h> 43 44#include <dev/gpio/gpiovar.h> 45#include <dev/ic/igpiovar.h> 46 47struct igpio_acpi_softc { 48 ACPI_HANDLE sc_handle; 49 struct igpio_softc sc_isc; 50 struct acpi_event * sc_event[8]; 51 int sc_pmf; 52 void *sc_intr; 53}; 54 55static int igpio_acpi_match(device_t, cfdata_t, void *); 56static void igpio_acpi_attach(device_t, device_t, void *); 57static int igpio_acpi_detach(device_t, int); 58 59static void igpio_acpi_register_event(void *, struct acpi_event *, ACPI_RESOURCE_GPIO *); 60static int igpio_acpi_intr(void *); 61 62CFATTACH_DECL_NEW(igpio_acpi, sizeof(struct igpio_acpi_softc), igpio_acpi_match, igpio_acpi_attach, igpio_acpi_detach, NULL); 63 64static const struct device_compatible_entry compat_data[] = { 65 { .compat = "INT344B" }, /* sunrisepoint */ 66 { .compat = "INT3450" }, /* cannonlake */ 67 { .compat = "INT3451" }, /* sunrisepoint */ 68 { .compat = "INT3453" }, /* geminilake */ 69 { .compat = "INT3455" }, /* icelake */ 70 { .compat = "INT345D" }, /* sunrisepoint */ 71 { .compat = "INT34BB" }, /* cannonlake */ 72 { .compat = "INT34C4" }, /* lakefield */ 73 { .compat = "INT34C5" }, /* tigerlake */ 74 { .compat = "INT34C6" }, /* tigerlake */ 75 { .compat = "INT34C8" }, /* jasperlake */ 76 { .compat = "INT3536" }, /* lewisburg */ 77 { .compat = "INTC1055" }, /* tigerlake */ 78 { .compat = "INTC1056" }, /* alderlake */ 79 { .compat = "INTC1057" }, /* tigerlake */ 80 { .compat = "INTC1071" }, /* emmitsburg */ 81 { .compat = "INTC3000" }, /* denverton */ 82 { .compat = "INTC3001" }, /* cedarfork */ 83#ifdef notyet 84 /* 85 * Complete bank setup in src/sys/dev/ic/igpioreg.h 86 * before enabling 87 */ 88 { .compat = "INT33B2" }, /* baytrail */ 89 { .compat = "INT33C7" }, /* lynxpoint */ 90 { .compat = "INT33FC" }, /* baytrail */ 91 { .compat = "INT3437" }, /* lynxpoint */ 92 { .compat = "INT3452" }, /* broxton */ 93 { .compat = "INT34D1" }, /* broxton */ 94 { .compat = "apollolake-pinctrl" }, /* broxton */ 95 { .compat = "broxton-pinctrl" }, /* broxton */ 96 { .compat = "INT33FF" }, /* cherryview */ 97#endif 98 DEVICE_COMPAT_EOL 99}; 100 101static int 102igpio_acpi_match(device_t parent, cfdata_t cf, void *aux) 103{ 104 struct acpi_attach_args *aa = aux; 105 106 return acpi_compatible_match(aa, compat_data); 107} 108 109static void 110igpio_acpi_attach(device_t parent, device_t self, void *aux) 111{ 112 struct igpio_acpi_softc * const asc = device_private(self); 113 struct acpi_attach_args *aa = aux; 114 ACPI_DEVICE_INFO *ad = aa->aa_node->ad_devinfo; 115 struct acpi_resources res; 116 struct acpi_mem *mem; 117 struct acpi_irq *irq; 118 int nbar; 119 ACPI_STATUS rv; 120 int i; 121 122 asc->sc_handle = aa->aa_node->ad_handle; 123 124 rv = acpi_resource_parse(self, aa->aa_node->ad_handle, "_CRS", 125 &res, &acpi_resource_parse_ops_default); 126 if (ACPI_FAILURE(rv)) 127 return; 128 129 irq = acpi_res_irq(&res, 0); 130 if (irq == NULL) { 131 aprint_error_dev(self, "couldn't find irq resource\n"); 132 goto done; 133 } 134 135 asc->sc_isc.sc_dev = self; 136 137 asc->sc_isc.sc_bst = aa->aa_memt; 138 for (nbar = 0; acpi_res_mem(&res, nbar); nbar++); 139 asc->sc_isc.sc_nbar = nbar; 140 asc->sc_isc.sc_base = 141 kmem_zalloc(sizeof(*asc->sc_isc.sc_base) * nbar, KM_SLEEP); 142 asc->sc_isc.sc_length = 143 kmem_zalloc(sizeof(*asc->sc_isc.sc_length) * nbar, KM_SLEEP); 144 asc->sc_isc.sc_bsh = 145 kmem_zalloc(sizeof(*asc->sc_isc.sc_bsh) * nbar, KM_SLEEP); 146 147 asc->sc_isc.sc_acpi_hid = ad->HardwareId.String; 148 149 for (i = 0; i < nbar; i++) { 150 mem = acpi_res_mem(&res, i); 151 if (mem == NULL) { 152 aprint_error_dev(self, "couldn't find mem resource\n"); 153 goto done; 154 } 155 156 asc->sc_isc.sc_base[i] = mem->ar_base; 157 asc->sc_isc.sc_length[i] = mem->ar_length; 158 } 159 160 igpio_attach(&asc->sc_isc); 161 162 /* If attachment failed */ 163 if (asc->sc_isc.sc_banks == NULL) { 164 igpio_acpi_detach(self, 0); 165 goto done; 166 } 167 168 rv = acpi_event_create_gpio(self, asc->sc_handle, igpio_acpi_register_event, asc); 169 if (ACPI_FAILURE(rv)) { 170 if (rv != AE_NOT_FOUND) 171 aprint_error_dev(self, "failed to create events: %s\n", AcpiFormatException(rv)); 172 goto done; 173 } 174 175 asc->sc_intr = acpi_intr_establish(self, 176 (uint64_t)(uintptr_t)asc->sc_handle, 177 IPL_VM, false, igpio_acpi_intr, asc, device_xname(self)); 178 if (asc->sc_intr == NULL) 179 aprint_error_dev(self, "couldn't establish interrupt\n"); 180 181done: 182 acpi_resource_cleanup(&res); 183 (void)pmf_device_register(self, NULL, NULL); 184 asc->sc_pmf = 1; 185} 186 187static int 188igpio_acpi_detach(device_t self, int flags) 189{ 190 struct igpio_acpi_softc * const asc = device_private(self); 191 struct igpio_softc * const isc = &asc->sc_isc; 192 int nbar = isc->sc_nbar; 193 194 acpi_intr_disestablish(asc->sc_intr); 195 196 igpio_detach(&asc->sc_isc); 197 198 if (isc->sc_base != NULL) { 199 kmem_free(isc->sc_base, sizeof(*isc->sc_base) * nbar); 200 isc->sc_base = NULL; 201 } 202 203 if (isc->sc_length != NULL) { 204 kmem_free(isc->sc_length, sizeof(*isc->sc_length) * nbar); 205 isc->sc_length = NULL; 206 } 207 208 if (isc->sc_bsh != NULL) { 209 kmem_free(isc->sc_bsh, sizeof(*isc->sc_bsh) * nbar); 210 isc->sc_length = NULL; 211 } 212 213 if (asc->sc_pmf) { 214 pmf_device_deregister(self); 215 asc->sc_pmf = 0; 216 } 217 218 return 0; 219} 220 221static void 222igpio_acpi_register_event(void *priv, struct acpi_event *ev, ACPI_RESOURCE_GPIO *gpio) 223{ 224 return; 225} 226 227static int 228igpio_acpi_intr(void *priv) 229{ 230 struct igpio_acpi_softc *asc = priv; 231 struct igpio_softc * const isc = &asc->sc_isc; 232 int ret; 233 234 ret = igpio_intr(isc); 235 236 return ret; 237} 238