xref: /netbsd-src/sys/dev/acpi/apei_mapreg.c (revision 5ca1c7a6c2b47986ba7129745f88dd1d8763fa92)
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