1/* $NetBSD: ipmi_acpi.c,v 1.6 2021/08/07 16:19:09 thorpej Exp $ */ 2 3/*- 4 * Copyright (c) 2018 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Michael van Elst 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33__KERNEL_RCSID(0, "$NetBSD: ipmi_acpi.c,v 1.6 2021/08/07 16:19:09 thorpej Exp $"); 34 35#include <sys/param.h> 36#include <sys/device.h> 37#include <sys/module.h> 38#include <sys/systm.h> 39 40#include <dev/acpi/acpireg.h> 41#include <dev/acpi/acpivar.h> 42 43#include <dev/ipmivar.h> 44 45#define _COMPONENT ACPI_RESOURCE_COMPONENT 46ACPI_MODULE_NAME ("ipmi_acpi") 47 48typedef struct ipmi_acpi_softc { 49 device_t sc_dev; 50 bool sc_init; 51} ipmi_acpi_softc_t; 52 53static int ipmi_acpi_match(device_t, cfdata_t, void *); 54static void ipmi_acpi_attach(device_t, device_t, void *); 55static int ipmi_acpi_detach(device_t, int); 56 57CFATTACH_DECL3_NEW(ipmi_acpi, sizeof(ipmi_acpi_softc_t), 58 ipmi_acpi_match, ipmi_acpi_attach, ipmi_acpi_detach, NULL, NULL, NULL, 59 DVF_DETACH_SHUTDOWN); 60 61static const struct device_compatible_entry compat_data[] = { 62 { .compat = "IPI0001" }, 63 DEVICE_COMPAT_EOL 64}; 65 66static int 67ipmi_acpi_match(device_t parent, cfdata_t match, void *opaque) 68{ 69 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque; 70 71 return acpi_compatible_match(aa, compat_data); 72} 73 74static void 75ipmi_acpi_attach(device_t parent, device_t self, void *opaque) 76{ 77 ipmi_acpi_softc_t *sc = device_private(self); 78 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque; 79 ACPI_STATUS rv; 80 ACPI_INTEGER itype, ivers, adr; 81 struct acpi_resources res; 82 struct acpi_io *io; 83 struct acpi_mem *mem; 84#if notyet 85 struct acpi_irq *irq; 86#endif 87 struct ipmi_attach_args IA, *ia = &IA; 88 bus_addr_t reg2; 89 uint16_t i2caddr; 90 91 sc->sc_dev = self; 92 93 aprint_naive("\n"); 94 95 rv = acpi_eval_integer(aa->aa_node->ad_handle, "_IFT", &itype); 96 if (ACPI_FAILURE(rv)) { 97 aprint_error("no _IFT\n"); 98 return; 99 } 100 101 rv = acpi_eval_integer(aa->aa_node->ad_handle, "_SRV", &ivers); 102 if (ACPI_FAILURE(rv)) { 103 aprint_error("no _SRV\n"); 104 return; 105 } 106 107 switch (itype) { 108 case IPMI_IF_KCS: 109 case IPMI_IF_SMIC: 110 case IPMI_IF_BT: 111 rv = acpi_resource_parse(self, aa->aa_node->ad_handle, "_CRS", 112 &res, &acpi_resource_parse_ops_default); 113 if (ACPI_FAILURE(rv)) { 114 aprint_normal("\n"); 115 aprint_error_dev(self, "no resources\n"); 116 return; 117 } 118 119 io = acpi_res_io(&res, 0); 120 mem = acpi_res_mem(&res, 0); 121 if (io == NULL && mem == NULL) { 122 aprint_error_dev(self, "no resources\n"); 123 return; 124 } 125 126#if notyet 127 if ((irq = acpi_res_irq(&res, 0)) != NULL) { 128 aprint_normal_dev(self, "IRQ %d type %d\n", 129 irq->ar_irq, irq->ar_type); 130 } else { 131 aprint_error_dev(self, "no interrupt\n"); 132 } 133#endif 134 ia->iaa_iot = aa->aa_iot; 135 ia->iaa_memt = aa->aa_memt; 136 137 ia->iaa_if_type = itype; 138 ia->iaa_if_rev = ivers; 139 ia->iaa_if_iotype = io ? 'i' : 'm'; 140 ia->iaa_if_iobase = io ? io->ar_base : mem->ar_base; 141 ia->iaa_if_iospacing = 1; 142 ia->iaa_if_irq = -1; 143 ia->iaa_if_irqlvl = 0; 144 145 reg2 = 0; 146 if (io) { 147 io = acpi_res_io(&res, 1); 148 if (io != NULL) 149 reg2 = (bus_addr_t)io->ar_base; 150 } else { 151 mem = acpi_res_mem(&res, 1); 152 if (mem != NULL) 153 reg2 = mem->ar_base; 154 } 155 156 if (reg2 > ia->iaa_if_iobase) 157 ia->iaa_if_iospacing = reg2 - ia->iaa_if_iobase; 158 159 config_found(self, ia, NULL, CFARGS_NONE); 160 161 break; 162 case IPMI_IF_SSIF: 163 rv = acpi_eval_integer(aa->aa_node->ad_handle, "_ADR", &adr); 164 if (ACPI_FAILURE(rv)) { 165 aprint_normal("\n"); 166 aprint_error_dev(self, "no resources\n"); 167 return; 168 } 169 if (adr > 65535) { 170 aprint_normal("\n"); 171 aprint_error_dev(self, "i2c address out of range\n"); 172 return; 173 } 174 i2caddr = adr; 175 176 aprint_normal(": i2c 0x%x\n", i2caddr); 177 break; 178 default: 179 aprint_normal("\n"); 180 aprint_error_dev(self, "unknown interface type\n"); 181 return; 182 } 183 184 sc->sc_init = true; 185 186 if (!pmf_device_register(self, NULL, NULL)) 187 aprint_error_dev(self, "couldn't establish power handler\n"); 188} 189 190static int 191ipmi_acpi_detach(device_t self, int flags) 192{ 193 struct ipmi_acpi_softc *sc = device_private(self); 194 int rc; 195 196 if (!sc->sc_init) 197 return 0; 198 199 rc = config_detach_children(self, flags); 200 if (rc) 201 return rc; 202 203 pmf_device_deregister(self); 204 return 0; 205} 206