Home | History | Annotate | Line # | Download | only in acpi
apei_mapreg.c revision 1.1
      1 /*	$NetBSD: apei_mapreg.c,v 1.1 2024/03/20 17:11:44 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.1 2024/03/20 17:11:44 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 	case 4:			/* 64-bit */
     77 		break;
     78 	default:
     79 		return NULL;
     80 	}
     81 
     82 	/*
     83 	 * Verify we don't need to shift anything, because I can't
     84 	 * figure out how the shifting is supposed to work in five
     85 	 * minutes of looking at the spec.
     86 	 */
     87 	switch (reg->BitOffset) {
     88 	case 0:
     89 		break;
     90 	default:
     91 		return NULL;
     92 	}
     93 
     94 	/*
     95 	 * Verify the bit width is a multiple of the access width so
     96 	 * we're not accessing more than we need.
     97 	 */
     98 	if (reg->BitWidth % (8*(1 << (reg->AccessWidth - 1))))
     99 		return NULL;
    100 
    101 	/*
    102 	 * Dispatch on the space id.
    103 	 *
    104 	 * Currently this only handles memory space because I/O space
    105 	 * is too painful to contemplate reimplementing here.
    106 	 */
    107 	switch (reg->SpaceId) {
    108 	case ACPI_ADR_SPACE_SYSTEM_MEMORY:
    109 		return AcpiOsMapMemory(reg->Address,
    110 		    1 << (reg->AccessWidth - 1));
    111 	default:
    112 		return NULL;
    113 	}
    114 }
    115 
    116 /*
    117  * apei_mapreg_unmap(reg, map)
    118  *
    119  *	Unmap a mapping previously returned by apei_mapreg_map.
    120  */
    121 void
    122 apei_mapreg_unmap(const ACPI_GENERIC_ADDRESS *reg,
    123     struct apei_mapreg *map)
    124 {
    125 
    126 	AcpiOsUnmapMemory(map, 1 << (reg->AccessWidth - 1));
    127 }
    128 
    129 /*
    130  * apei_mapreg_read(reg, map)
    131  *
    132  *	Read from reg via map previously obtained by apei_mapreg_map.
    133  */
    134 uint64_t
    135 apei_mapreg_read(const ACPI_GENERIC_ADDRESS *reg,
    136     const struct apei_mapreg *map)
    137 {
    138 	unsigned chunkbits = NBBY*(1 << (reg->AccessWidth - 1));
    139 	unsigned i, n = reg->BitWidth % chunkbits;
    140 	uint64_t v = 0;
    141 
    142 	for (i = 0; i < n; i++) {
    143 		uint64_t chunk;
    144 
    145 		switch (reg->AccessWidth) {
    146 		case 1:
    147 			chunk = *(volatile const uint8_t *)map;
    148 			break;
    149 		case 2:
    150 			chunk = *(volatile const uint16_t *)map;
    151 			break;
    152 		case 3:
    153 			chunk = *(volatile const uint32_t *)map;
    154 			break;
    155 		case 4:
    156 			chunk = *(volatile const uint64_t *)map;
    157 			break;
    158 		default:
    159 			__unreachable();
    160 		}
    161 		v |= chunk << (i*chunkbits);
    162 	}
    163 
    164 	membar_acquire();	/* XXX probably not right for MMIO */
    165 	return v;
    166 }
    167 
    168 /*
    169  * apei_mapreg_write(reg, map, v)
    170  *
    171  *	Write to reg via map previously obtained by apei_mapreg_map.
    172  */
    173 void
    174 apei_mapreg_write(const ACPI_GENERIC_ADDRESS *reg, struct apei_mapreg *map,
    175     uint64_t v)
    176 {
    177 	unsigned chunkbits = NBBY*(1 << (reg->AccessWidth - 1));
    178 	unsigned i, n = reg->BitWidth % chunkbits;
    179 
    180 	membar_release();	/* XXX probably not right for MMIO */
    181 	for (i = 0; i < n; i++) {
    182 		uint64_t chunk = v >> (i*chunkbits);
    183 
    184 		switch (reg->AccessWidth) {
    185 		case 1:
    186 			*(volatile uint8_t *)map = chunk;
    187 			break;
    188 		case 2:
    189 			*(volatile uint16_t *)map = chunk;
    190 			break;
    191 		case 3:
    192 			*(volatile uint32_t *)map = chunk;
    193 			break;
    194 		case 4:
    195 			*(volatile uint64_t *)map = chunk;
    196 			break;
    197 		default:
    198 			__unreachable();
    199 		}
    200 	}
    201 }
    202