1 1.4 riastrad /* $NetBSD: apei_mapreg.c,v 1.4 2024/03/22 20:47:52 riastradh Exp $ */ 2 1.1 riastrad 3 1.1 riastrad /*- 4 1.1 riastrad * Copyright (c) 2024 The NetBSD Foundation, Inc. 5 1.1 riastrad * All rights reserved. 6 1.1 riastrad * 7 1.1 riastrad * Redistribution and use in source and binary forms, with or without 8 1.1 riastrad * modification, are permitted provided that the following conditions 9 1.1 riastrad * are met: 10 1.1 riastrad * 1. Redistributions of source code must retain the above copyright 11 1.1 riastrad * notice, this list of conditions and the following disclaimer. 12 1.1 riastrad * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 riastrad * notice, this list of conditions and the following disclaimer in the 14 1.1 riastrad * documentation and/or other materials provided with the distribution. 15 1.1 riastrad * 16 1.1 riastrad * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.1 riastrad * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 riastrad * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 riastrad * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.1 riastrad * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 riastrad * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 riastrad * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 riastrad * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 riastrad * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 riastrad * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 riastrad * POSSIBILITY OF SUCH DAMAGE. 27 1.1 riastrad */ 28 1.1 riastrad 29 1.1 riastrad /* 30 1.1 riastrad * Pre-mapped ACPI register access 31 1.1 riastrad * 32 1.1 riastrad * XXX This isn't APEI-specific -- it should be moved into the general 33 1.1 riastrad * ACPI API, and unified with the AcpiRead/AcpiWrite implementation. 34 1.1 riastrad */ 35 1.1 riastrad 36 1.1 riastrad #include <sys/cdefs.h> 37 1.4 riastrad __KERNEL_RCSID(0, "$NetBSD: apei_mapreg.c,v 1.4 2024/03/22 20:47:52 riastradh Exp $"); 38 1.1 riastrad 39 1.1 riastrad #include <sys/types.h> 40 1.1 riastrad 41 1.1 riastrad #include <sys/atomic.h> 42 1.1 riastrad 43 1.1 riastrad #include <dev/acpi/acpivar.h> 44 1.1 riastrad #include <dev/acpi/apei_mapreg.h> 45 1.1 riastrad 46 1.1 riastrad /* 47 1.1 riastrad * apei_mapreg_map(reg) 48 1.1 riastrad * 49 1.1 riastrad * Return a mapping for use with apei_mapreg_read, or NULL if it 50 1.1 riastrad * can't be mapped. 51 1.1 riastrad */ 52 1.1 riastrad struct apei_mapreg * 53 1.1 riastrad apei_mapreg_map(const ACPI_GENERIC_ADDRESS *reg) 54 1.1 riastrad { 55 1.1 riastrad 56 1.1 riastrad /* 57 1.1 riastrad * Verify the result is reasonable. 58 1.1 riastrad */ 59 1.1 riastrad switch (reg->BitWidth) { 60 1.1 riastrad case 8: 61 1.1 riastrad case 16: 62 1.1 riastrad case 32: 63 1.1 riastrad case 64: 64 1.1 riastrad break; 65 1.1 riastrad default: 66 1.1 riastrad return NULL; 67 1.1 riastrad } 68 1.1 riastrad 69 1.1 riastrad /* 70 1.1 riastrad * Verify we know how to do the access width. 71 1.1 riastrad */ 72 1.1 riastrad switch (reg->AccessWidth) { 73 1.1 riastrad case 1: /* 8-bit */ 74 1.1 riastrad case 2: /* 16-bit */ 75 1.1 riastrad case 3: /* 32-bit */ 76 1.4 riastrad break; 77 1.1 riastrad case 4: /* 64-bit */ 78 1.4 riastrad if (reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_IO) 79 1.4 riastrad return NULL; 80 1.1 riastrad break; 81 1.1 riastrad default: 82 1.1 riastrad return NULL; 83 1.1 riastrad } 84 1.1 riastrad 85 1.1 riastrad /* 86 1.1 riastrad * Verify we don't need to shift anything, because I can't 87 1.1 riastrad * figure out how the shifting is supposed to work in five 88 1.1 riastrad * minutes of looking at the spec. 89 1.1 riastrad */ 90 1.1 riastrad switch (reg->BitOffset) { 91 1.1 riastrad case 0: 92 1.1 riastrad break; 93 1.1 riastrad default: 94 1.1 riastrad return NULL; 95 1.1 riastrad } 96 1.1 riastrad 97 1.1 riastrad /* 98 1.1 riastrad * Verify the bit width is a multiple of the access width so 99 1.1 riastrad * we're not accessing more than we need. 100 1.1 riastrad */ 101 1.1 riastrad if (reg->BitWidth % (8*(1 << (reg->AccessWidth - 1)))) 102 1.1 riastrad return NULL; 103 1.1 riastrad 104 1.1 riastrad /* 105 1.1 riastrad * Dispatch on the space id. 106 1.1 riastrad */ 107 1.1 riastrad switch (reg->SpaceId) { 108 1.4 riastrad case ACPI_ADR_SPACE_SYSTEM_IO: 109 1.4 riastrad /* 110 1.4 riastrad * Just need to return something non-null -- no state 111 1.4 riastrad * to record for a mapping. 112 1.4 riastrad */ 113 1.4 riastrad return (struct apei_mapreg *)__UNCONST(reg); 114 1.1 riastrad case ACPI_ADR_SPACE_SYSTEM_MEMORY: 115 1.1 riastrad return AcpiOsMapMemory(reg->Address, 116 1.1 riastrad 1 << (reg->AccessWidth - 1)); 117 1.1 riastrad default: 118 1.1 riastrad return NULL; 119 1.1 riastrad } 120 1.1 riastrad } 121 1.1 riastrad 122 1.1 riastrad /* 123 1.1 riastrad * apei_mapreg_unmap(reg, map) 124 1.1 riastrad * 125 1.1 riastrad * Unmap a mapping previously returned by apei_mapreg_map. 126 1.1 riastrad */ 127 1.1 riastrad void 128 1.1 riastrad apei_mapreg_unmap(const ACPI_GENERIC_ADDRESS *reg, 129 1.1 riastrad struct apei_mapreg *map) 130 1.1 riastrad { 131 1.1 riastrad 132 1.4 riastrad switch (reg->SpaceId) { 133 1.4 riastrad case ACPI_ADR_SPACE_SYSTEM_IO: 134 1.4 riastrad KASSERT(map == __UNCONST(reg)); 135 1.4 riastrad break; 136 1.4 riastrad case ACPI_ADR_SPACE_SYSTEM_MEMORY: 137 1.4 riastrad AcpiOsUnmapMemory(map, 1 << (reg->AccessWidth - 1)); 138 1.4 riastrad break; 139 1.4 riastrad default: 140 1.4 riastrad panic("invalid register mapping"); 141 1.4 riastrad } 142 1.1 riastrad } 143 1.1 riastrad 144 1.1 riastrad /* 145 1.1 riastrad * apei_mapreg_read(reg, map) 146 1.1 riastrad * 147 1.1 riastrad * Read from reg via map previously obtained by apei_mapreg_map. 148 1.1 riastrad */ 149 1.1 riastrad uint64_t 150 1.1 riastrad apei_mapreg_read(const ACPI_GENERIC_ADDRESS *reg, 151 1.1 riastrad const struct apei_mapreg *map) 152 1.1 riastrad { 153 1.1 riastrad unsigned chunkbits = NBBY*(1 << (reg->AccessWidth - 1)); 154 1.3 riastrad unsigned i, n = reg->BitWidth / chunkbits; 155 1.1 riastrad uint64_t v = 0; 156 1.1 riastrad 157 1.1 riastrad for (i = 0; i < n; i++) { 158 1.1 riastrad uint64_t chunk; 159 1.1 riastrad 160 1.4 riastrad switch (reg->SpaceId) { 161 1.4 riastrad case ACPI_ADR_SPACE_SYSTEM_IO: { 162 1.4 riastrad ACPI_IO_ADDRESS addr; 163 1.4 riastrad uint32_t chunk32 = 0; 164 1.4 riastrad ACPI_STATUS rv; 165 1.4 riastrad 166 1.4 riastrad switch (reg->AccessWidth) { 167 1.4 riastrad case 1: 168 1.4 riastrad addr = reg->Address + i; 169 1.4 riastrad break; 170 1.4 riastrad case 2: 171 1.4 riastrad addr = reg->Address + 2*i; 172 1.4 riastrad break; 173 1.4 riastrad case 3: 174 1.4 riastrad addr = reg->Address + 4*i; 175 1.4 riastrad break; 176 1.4 riastrad case 4: /* no 64-bit I/O ports */ 177 1.4 riastrad default: 178 1.4 riastrad __unreachable(); 179 1.4 riastrad } 180 1.4 riastrad rv = AcpiOsReadPort(addr, &chunk32, 181 1.4 riastrad NBBY*(1 << (reg->AccessWidth - 1))); 182 1.4 riastrad KASSERTMSG(!ACPI_FAILURE(rv), "%s", 183 1.4 riastrad AcpiFormatException(rv)); 184 1.4 riastrad chunk = chunk32; 185 1.1 riastrad break; 186 1.4 riastrad } 187 1.4 riastrad case ACPI_ADR_SPACE_SYSTEM_MEMORY: 188 1.4 riastrad switch (reg->AccessWidth) { 189 1.4 riastrad case 1: 190 1.4 riastrad chunk = *((volatile const uint8_t *)map + i); 191 1.4 riastrad break; 192 1.4 riastrad case 2: 193 1.4 riastrad chunk = *((volatile const uint16_t *)map + i); 194 1.4 riastrad break; 195 1.4 riastrad case 3: 196 1.4 riastrad chunk = *((volatile const uint32_t *)map + i); 197 1.4 riastrad break; 198 1.4 riastrad case 4: 199 1.4 riastrad chunk = *((volatile const uint64_t *)map + i); 200 1.4 riastrad break; 201 1.4 riastrad default: 202 1.4 riastrad __unreachable(); 203 1.4 riastrad } 204 1.1 riastrad break; 205 1.1 riastrad default: 206 1.1 riastrad __unreachable(); 207 1.1 riastrad } 208 1.1 riastrad v |= chunk << (i*chunkbits); 209 1.1 riastrad } 210 1.1 riastrad 211 1.1 riastrad membar_acquire(); /* XXX probably not right for MMIO */ 212 1.1 riastrad return v; 213 1.1 riastrad } 214 1.1 riastrad 215 1.1 riastrad /* 216 1.1 riastrad * apei_mapreg_write(reg, map, v) 217 1.1 riastrad * 218 1.1 riastrad * Write to reg via map previously obtained by apei_mapreg_map. 219 1.1 riastrad */ 220 1.1 riastrad void 221 1.1 riastrad apei_mapreg_write(const ACPI_GENERIC_ADDRESS *reg, struct apei_mapreg *map, 222 1.1 riastrad uint64_t v) 223 1.1 riastrad { 224 1.1 riastrad unsigned chunkbits = NBBY*(1 << (reg->AccessWidth - 1)); 225 1.3 riastrad unsigned i, n = reg->BitWidth / chunkbits; 226 1.1 riastrad 227 1.1 riastrad membar_release(); /* XXX probably not right for MMIO */ 228 1.1 riastrad for (i = 0; i < n; i++) { 229 1.1 riastrad uint64_t chunk = v >> (i*chunkbits); 230 1.1 riastrad 231 1.4 riastrad switch (reg->SpaceId) { 232 1.4 riastrad case ACPI_ADR_SPACE_SYSTEM_IO: { 233 1.4 riastrad ACPI_IO_ADDRESS addr; 234 1.4 riastrad ACPI_STATUS rv; 235 1.4 riastrad 236 1.4 riastrad switch (reg->AccessWidth) { 237 1.4 riastrad case 1: 238 1.4 riastrad addr = reg->Address + i; 239 1.4 riastrad break; 240 1.4 riastrad case 2: 241 1.4 riastrad addr = reg->Address + 2*i; 242 1.4 riastrad break; 243 1.4 riastrad case 3: 244 1.4 riastrad addr = reg->Address + 4*i; 245 1.4 riastrad break; 246 1.4 riastrad case 4: /* no 64-bit I/O ports */ 247 1.4 riastrad default: 248 1.4 riastrad __unreachable(); 249 1.4 riastrad } 250 1.4 riastrad rv = AcpiOsWritePort(addr, chunk, 251 1.4 riastrad NBBY*(1 << (reg->AccessWidth - 1))); 252 1.4 riastrad KASSERTMSG(!ACPI_FAILURE(rv), "%s", 253 1.4 riastrad AcpiFormatException(rv)); 254 1.1 riastrad break; 255 1.4 riastrad } 256 1.4 riastrad case ACPI_ADR_SPACE_SYSTEM_MEMORY: 257 1.4 riastrad switch (reg->AccessWidth) { 258 1.4 riastrad case 1: 259 1.4 riastrad *((volatile uint8_t *)map + i) = chunk; 260 1.4 riastrad break; 261 1.4 riastrad case 2: 262 1.4 riastrad *((volatile uint16_t *)map + i) = chunk; 263 1.4 riastrad break; 264 1.4 riastrad case 3: 265 1.4 riastrad *((volatile uint32_t *)map + i) = chunk; 266 1.4 riastrad break; 267 1.4 riastrad case 4: 268 1.4 riastrad *((volatile uint64_t *)map + i) = chunk; 269 1.4 riastrad break; 270 1.4 riastrad default: 271 1.4 riastrad __unreachable(); 272 1.4 riastrad } 273 1.1 riastrad break; 274 1.1 riastrad default: 275 1.1 riastrad __unreachable(); 276 1.1 riastrad } 277 1.1 riastrad } 278 1.1 riastrad } 279