1 /* $NetBSD: apei_mapreg.c,v 1.4 2024/03/22 20:47:52 riastradh Exp $ */
2
3 /*-
4 * Copyright (c) 2024 The NetBSD Foundation, Inc.
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 /*
30 * Pre-mapped ACPI register access
31 *
32 * XXX This isn't APEI-specific -- it should be moved into the general
33 * ACPI API, and unified with the AcpiRead/AcpiWrite implementation.
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: apei_mapreg.c,v 1.4 2024/03/22 20:47:52 riastradh Exp $");
38
39 #include <sys/types.h>
40
41 #include <sys/atomic.h>
42
43 #include <dev/acpi/acpivar.h>
44 #include <dev/acpi/apei_mapreg.h>
45
46 /*
47 * apei_mapreg_map(reg)
48 *
49 * Return a mapping for use with apei_mapreg_read, or NULL if it
50 * can't be mapped.
51 */
52 struct apei_mapreg *
apei_mapreg_map(const ACPI_GENERIC_ADDRESS * reg)53 apei_mapreg_map(const ACPI_GENERIC_ADDRESS *reg)
54 {
55
56 /*
57 * Verify the result is reasonable.
58 */
59 switch (reg->BitWidth) {
60 case 8:
61 case 16:
62 case 32:
63 case 64:
64 break;
65 default:
66 return NULL;
67 }
68
69 /*
70 * Verify we know how to do the access width.
71 */
72 switch (reg->AccessWidth) {
73 case 1: /* 8-bit */
74 case 2: /* 16-bit */
75 case 3: /* 32-bit */
76 break;
77 case 4: /* 64-bit */
78 if (reg->SpaceId == ACPI_ADR_SPACE_SYSTEM_IO)
79 return NULL;
80 break;
81 default:
82 return NULL;
83 }
84
85 /*
86 * Verify we don't need to shift anything, because I can't
87 * figure out how the shifting is supposed to work in five
88 * minutes of looking at the spec.
89 */
90 switch (reg->BitOffset) {
91 case 0:
92 break;
93 default:
94 return NULL;
95 }
96
97 /*
98 * Verify the bit width is a multiple of the access width so
99 * we're not accessing more than we need.
100 */
101 if (reg->BitWidth % (8*(1 << (reg->AccessWidth - 1))))
102 return NULL;
103
104 /*
105 * Dispatch on the space id.
106 */
107 switch (reg->SpaceId) {
108 case ACPI_ADR_SPACE_SYSTEM_IO:
109 /*
110 * Just need to return something non-null -- no state
111 * to record for a mapping.
112 */
113 return (struct apei_mapreg *)__UNCONST(reg);
114 case ACPI_ADR_SPACE_SYSTEM_MEMORY:
115 return AcpiOsMapMemory(reg->Address,
116 1 << (reg->AccessWidth - 1));
117 default:
118 return NULL;
119 }
120 }
121
122 /*
123 * apei_mapreg_unmap(reg, map)
124 *
125 * Unmap a mapping previously returned by apei_mapreg_map.
126 */
127 void
apei_mapreg_unmap(const ACPI_GENERIC_ADDRESS * reg,struct apei_mapreg * map)128 apei_mapreg_unmap(const ACPI_GENERIC_ADDRESS *reg,
129 struct apei_mapreg *map)
130 {
131
132 switch (reg->SpaceId) {
133 case ACPI_ADR_SPACE_SYSTEM_IO:
134 KASSERT(map == __UNCONST(reg));
135 break;
136 case ACPI_ADR_SPACE_SYSTEM_MEMORY:
137 AcpiOsUnmapMemory(map, 1 << (reg->AccessWidth - 1));
138 break;
139 default:
140 panic("invalid register mapping");
141 }
142 }
143
144 /*
145 * apei_mapreg_read(reg, map)
146 *
147 * Read from reg via map previously obtained by apei_mapreg_map.
148 */
149 uint64_t
apei_mapreg_read(const ACPI_GENERIC_ADDRESS * reg,const struct apei_mapreg * map)150 apei_mapreg_read(const ACPI_GENERIC_ADDRESS *reg,
151 const struct apei_mapreg *map)
152 {
153 unsigned chunkbits = NBBY*(1 << (reg->AccessWidth - 1));
154 unsigned i, n = reg->BitWidth / chunkbits;
155 uint64_t v = 0;
156
157 for (i = 0; i < n; i++) {
158 uint64_t chunk;
159
160 switch (reg->SpaceId) {
161 case ACPI_ADR_SPACE_SYSTEM_IO: {
162 ACPI_IO_ADDRESS addr;
163 uint32_t chunk32 = 0;
164 ACPI_STATUS rv;
165
166 switch (reg->AccessWidth) {
167 case 1:
168 addr = reg->Address + i;
169 break;
170 case 2:
171 addr = reg->Address + 2*i;
172 break;
173 case 3:
174 addr = reg->Address + 4*i;
175 break;
176 case 4: /* no 64-bit I/O ports */
177 default:
178 __unreachable();
179 }
180 rv = AcpiOsReadPort(addr, &chunk32,
181 NBBY*(1 << (reg->AccessWidth - 1)));
182 KASSERTMSG(!ACPI_FAILURE(rv), "%s",
183 AcpiFormatException(rv));
184 chunk = chunk32;
185 break;
186 }
187 case ACPI_ADR_SPACE_SYSTEM_MEMORY:
188 switch (reg->AccessWidth) {
189 case 1:
190 chunk = *((volatile const uint8_t *)map + i);
191 break;
192 case 2:
193 chunk = *((volatile const uint16_t *)map + i);
194 break;
195 case 3:
196 chunk = *((volatile const uint32_t *)map + i);
197 break;
198 case 4:
199 chunk = *((volatile const uint64_t *)map + i);
200 break;
201 default:
202 __unreachable();
203 }
204 break;
205 default:
206 __unreachable();
207 }
208 v |= chunk << (i*chunkbits);
209 }
210
211 membar_acquire(); /* XXX probably not right for MMIO */
212 return v;
213 }
214
215 /*
216 * apei_mapreg_write(reg, map, v)
217 *
218 * Write to reg via map previously obtained by apei_mapreg_map.
219 */
220 void
apei_mapreg_write(const ACPI_GENERIC_ADDRESS * reg,struct apei_mapreg * map,uint64_t v)221 apei_mapreg_write(const ACPI_GENERIC_ADDRESS *reg, struct apei_mapreg *map,
222 uint64_t v)
223 {
224 unsigned chunkbits = NBBY*(1 << (reg->AccessWidth - 1));
225 unsigned i, n = reg->BitWidth / chunkbits;
226
227 membar_release(); /* XXX probably not right for MMIO */
228 for (i = 0; i < n; i++) {
229 uint64_t chunk = v >> (i*chunkbits);
230
231 switch (reg->SpaceId) {
232 case ACPI_ADR_SPACE_SYSTEM_IO: {
233 ACPI_IO_ADDRESS addr;
234 ACPI_STATUS rv;
235
236 switch (reg->AccessWidth) {
237 case 1:
238 addr = reg->Address + i;
239 break;
240 case 2:
241 addr = reg->Address + 2*i;
242 break;
243 case 3:
244 addr = reg->Address + 4*i;
245 break;
246 case 4: /* no 64-bit I/O ports */
247 default:
248 __unreachable();
249 }
250 rv = AcpiOsWritePort(addr, chunk,
251 NBBY*(1 << (reg->AccessWidth - 1)));
252 KASSERTMSG(!ACPI_FAILURE(rv), "%s",
253 AcpiFormatException(rv));
254 break;
255 }
256 case ACPI_ADR_SPACE_SYSTEM_MEMORY:
257 switch (reg->AccessWidth) {
258 case 1:
259 *((volatile uint8_t *)map + i) = chunk;
260 break;
261 case 2:
262 *((volatile uint16_t *)map + i) = chunk;
263 break;
264 case 3:
265 *((volatile uint32_t *)map + i) = chunk;
266 break;
267 case 4:
268 *((volatile uint64_t *)map + i) = chunk;
269 break;
270 default:
271 __unreachable();
272 }
273 break;
274 default:
275 __unreachable();
276 }
277 }
278 }
279