soc/intel/ptl: Update ME specification version to 21
[coreboot.git] / src / ec / google / common / mec.c
blob51962d5ce9940587d2b2e12fba28ba644102e1f8
1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <stddef.h>
4 #include <stdint.h>
5 #include <arch/io.h>
7 #include "mec.h"
9 enum mec_access_mode {
10 /* 8-bit access */
11 ACCESS_TYPE_BYTE = 0x0,
12 /* 16-bit access */
13 ACCESS_TYPE_WORD = 0x1,
14 /* 32-bit access */
15 ACCESS_TYPE_LONG = 0x2,
17 * 32-bit access, read or write of MEC_EMI_EC_DATA_B3 causes the
18 * EC data register to be incremented.
20 ACCESS_TYPE_LONG_AUTO_INCREMENT = 0x3,
23 /* EMI registers are relative to base */
24 #define MEC_EMI_HOST_TO_EC(base) ((base) + 0)
25 #define MEC_EMI_EC_TO_HOST(base) ((base) + 1)
26 #define MEC_EMI_EC_ADDRESS_B0(base) ((base) + 2)
27 #define MEC_EMI_EC_ADDRESS_B1(base) ((base) + 3)
28 #define MEC_EMI_EC_DATA_B0(base) ((base) + 4)
29 #define MEC_EMI_EC_DATA_B1(base) ((base) + 5)
30 #define MEC_EMI_EC_DATA_B2(base) ((base) + 6)
31 #define MEC_EMI_EC_DATA_B3(base) ((base) + 7)
34 * cros_ec_lpc_mec_emi_write_address
36 * Initialize EMI read / write at a given address.
38 * @base: Starting read / write address
39 * @offset: Offset applied to base address
40 * @access_mode: Type of access, typically 32-bit auto-increment
42 static void mec_emi_write_address(uint16_t base, uint16_t offset,
43 enum mec_access_mode access_mode)
45 outb((offset & 0xfc) | access_mode, MEC_EMI_EC_ADDRESS_B0(base));
46 outb((offset >> 8) & 0x7f, MEC_EMI_EC_ADDRESS_B1(base));
49 uint8_t mec_io_bytes(enum mec_io_type type, uint16_t base,
50 uint16_t offset, void *buffer, size_t size)
52 enum mec_access_mode access_mode, new_access_mode;
53 uint8_t *buf = buffer;
54 uint8_t checksum = 0;
55 int io_addr;
56 int i = 0;
58 if (size == 0 || base == 0)
59 return 0;
62 * Long access cannot be used on misaligned data since reading B0 loads
63 * the data register and writing B3 flushes it.
65 if ((offset & 0x3) || (size < 4))
66 access_mode = ACCESS_TYPE_BYTE;
67 else
68 access_mode = ACCESS_TYPE_LONG_AUTO_INCREMENT;
70 /* Initialize I/O at desired address */
71 mec_emi_write_address(base, offset, access_mode);
73 /* Skip bytes in case of misaligned offset */
74 io_addr = MEC_EMI_EC_DATA_B0(base) + (offset & 0x3);
75 while (i < size) {
76 while (io_addr <= MEC_EMI_EC_DATA_B3(base)) {
77 if (type == MEC_IO_WRITE)
78 outb(buf[i], io_addr++);
79 else
80 buf[i] = inb(io_addr++);
82 checksum += buf[i++];
83 offset++;
85 /* Extra bounds check in case of misaligned size */
86 if (i == size)
87 return checksum;
91 * Use long auto-increment access except for misaligned write,
92 * since writing B3 triggers the flush.
94 if ((size - i) < 4 && type == MEC_IO_WRITE)
95 new_access_mode = ACCESS_TYPE_BYTE;
96 else
97 new_access_mode = ACCESS_TYPE_LONG_AUTO_INCREMENT;
98 if (new_access_mode != access_mode ||
99 access_mode != ACCESS_TYPE_LONG_AUTO_INCREMENT) {
100 access_mode = new_access_mode;
101 mec_emi_write_address(base, offset, access_mode);
104 /* Access [B0, B3] on each loop pass */
105 io_addr = MEC_EMI_EC_DATA_B0(base);
108 return checksum;