1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2024 Arm Ltd 5 * Copyright (c) 2022 The FreeBSD Foundation 6 * 7 * Portions of this software were developed by Andrew Turner under sponsorship 8 * from the FreeBSD Foundation. 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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/kernel.h> 35 #include <sys/lock.h> 36 #include <sys/module.h> 37 #include <sys/mutex.h> 38 39 #include <contrib/dev/acpica/include/acpi.h> 40 #include <dev/acpica/acpivar.h> 41 42 #include <arm64/spe/arm_spe_dev.h> 43 44 static device_identify_t arm_spe_acpi_identify; 45 static device_probe_t arm_spe_acpi_probe; 46 47 static device_method_t arm_spe_acpi_methods[] = { 48 /* Device interface */ 49 DEVMETHOD(device_identify, arm_spe_acpi_identify), 50 DEVMETHOD(device_probe, arm_spe_acpi_probe), 51 52 DEVMETHOD_END, 53 }; 54 55 DEFINE_CLASS_1(spe, arm_spe_acpi_driver, arm_spe_acpi_methods, 56 sizeof(struct arm_spe_softc), arm_spe_driver); 57 58 DRIVER_MODULE(spe, acpi, arm_spe_acpi_driver, 0, 0); 59 60 struct madt_data { 61 u_int irq; 62 bool found; 63 bool valid; 64 }; 65 66 static void 67 madt_handler(ACPI_SUBTABLE_HEADER *entry, void *arg) 68 { 69 ACPI_MADT_GENERIC_INTERRUPT *intr; 70 struct madt_data *madt_data; 71 u_int irq; 72 73 madt_data = (struct madt_data *)arg; 74 75 /* Exit early if we are have decided to not attach */ 76 if (!madt_data->valid) 77 return; 78 79 switch(entry->Type) { 80 case ACPI_MADT_TYPE_GENERIC_INTERRUPT: 81 intr = (ACPI_MADT_GENERIC_INTERRUPT *)entry; 82 irq = intr->SpeInterrupt; 83 84 if (irq == 0) { 85 madt_data->valid = false; 86 } else if (!madt_data->found) { 87 madt_data->found = true; 88 madt_data->irq = irq; 89 } else if (madt_data->irq != irq) { 90 madt_data->valid = false; 91 } 92 break; 93 94 default: 95 break; 96 } 97 } 98 99 static void 100 arm_spe_acpi_identify(driver_t *driver, device_t parent) 101 { 102 struct madt_data madt_data; 103 ACPI_TABLE_MADT *madt; 104 device_t dev; 105 vm_paddr_t physaddr; 106 107 physaddr = acpi_find_table(ACPI_SIG_MADT); 108 if (physaddr == 0) 109 return; 110 111 madt = acpi_map_table(physaddr, ACPI_SIG_MADT); 112 if (madt == NULL) { 113 device_printf(parent, "spe: Unable to map the MADT\n"); 114 return; 115 } 116 117 madt_data.irq = 0; 118 madt_data.found = false; 119 madt_data.valid = true; 120 121 acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length, 122 madt_handler, &madt_data); 123 124 if (!madt_data.found || !madt_data.valid) 125 goto out; 126 127 MPASS(madt_data.irq != 0); 128 129 dev = BUS_ADD_CHILD(parent, 0, "spe", -1); 130 if (dev == NULL) { 131 device_printf(parent, "add spe child failed\n"); 132 goto out; 133 } 134 135 BUS_SET_RESOURCE(parent, dev, SYS_RES_IRQ, 0, madt_data.irq, 1); 136 137 out: 138 acpi_unmap_table(madt); 139 } 140 141 static int 142 arm_spe_acpi_probe(device_t dev) 143 { 144 device_set_desc(dev, "ARM Statistical Profiling Extension"); 145 return (BUS_PROBE_NOWILDCARD); 146 } 147