Home | History | Annotate | Line # | Download | only in acpi
      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 *
     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
    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
    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
    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