hc
2024-03-25 edb30157bad0c0001c32b854271ace01d3b9a16a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/** @file
  Board-specific EC commands.
 
  Copyright (c) 2021, Baruch Binyamin Doron
  Copyright (c) 2021, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent
 
**/
 
#include <Base.h>
#include <Uefi.h>
#include <Library/DebugLib.h>
#include <Library/EcLib.h>
#include <Library/IoLib.h>
 
/* Notes:
 * - ACPI "CMDB": Writing to this offset is equivalent to sending commands.
 *   The CMDx bytes contain the command parameters.
 *
 * TODO - Implement:
 *   - Commands: 0x58, 0xE1 and 0xE2
 *     - 0x51, 0x52: EC flash write?
 *   - ACPI CMDB: 0x63 and 0x64, 0xC7
 *     - 0x0B: Flash write (Boolean argument? Set in offset 0x0B?)
 *
 * Reversing vendor's protocols:
 * - Only read and write are used.
 * - Query, ACPI "CMDB" processing and command 58 are unused.
 * - Equivalent KbcPeim is an unused PPI.
 *
 * NB: Also look for potential EC library
 */
 
#define EC_INDEX_IO_PORT            0x1200
#define EC_INDEX_IO_HIGH_ADDR_PORT  EC_INDEX_IO_PORT+1
#define EC_INDEX_IO_LOW_ADDR_PORT   EC_INDEX_IO_PORT+2
#define EC_INDEX_IO_DATA_PORT       EC_INDEX_IO_PORT+3
 
/**
  Reads a byte of EC RAM.
 
  @param[in]  Address               Address to read
  @param[out] Data                  Data received
 
  @retval    EFI_SUCCESS            Command success
  @retval    EFI_INVALID_PARAMETER  Data is NULL
  @retval    EFI_DEVICE_ERROR       Command error
  @retval    EFI_TIMEOUT            Command timeout
**/
EFI_STATUS
EcCmd90Read (
  IN  UINT8                  Address,
  OUT UINT8                  *Data
  )
{
  EFI_STATUS                 Status;
 
  if (Data == NULL) {
    return EFI_INVALID_PARAMETER;
  }
 
  Status = SendEcCommand (0x90);
  if (EFI_ERROR (Status)) {
    DEBUG((DEBUG_ERROR, "%a(): SendEcCommand(0x90) failed!\n", __FUNCTION__));
    return Status;
  }
 
  Status = SendEcData (Address);
  if (EFI_ERROR (Status)) {
    DEBUG((DEBUG_ERROR, "%a(): SendEcData(Address) failed!\n", __FUNCTION__));
    return Status;
  }
 
  Status = ReceiveEcData (Data);
  if (EFI_ERROR (Status)) {
    DEBUG((DEBUG_ERROR, "%a(): ReceiveEcData(Data) failed!\n", __FUNCTION__));
    return Status;
  }
  return EFI_SUCCESS;
}
 
/**
  Writes a byte of EC RAM.
 
  @param[in] Address           Address to write
  @param[in] Data              Data to write
 
  @retval    EFI_SUCCESS       Command success
  @retval    EFI_DEVICE_ERROR  Command error
  @retval    EFI_TIMEOUT       Command timeout
**/
EFI_STATUS
EcCmd91Write (
  IN  UINT8                  Address,
  IN  UINT8                  Data
  )
{
  EFI_STATUS                 Status;
 
  Status = SendEcCommand (0x91);
  if (EFI_ERROR (Status)) {
    DEBUG((DEBUG_ERROR, "%a(): SendEcCommand(0x91) failed!\n", __FUNCTION__));
    return Status;
  }
 
  Status = SendEcData (Address);
  if (EFI_ERROR (Status)) {
    DEBUG((DEBUG_ERROR, "%a(): SendEcData(Address) failed!\n", __FUNCTION__));
    return Status;
  }
 
  Status = SendEcData (Data);
  if (EFI_ERROR (Status)) {
    DEBUG((DEBUG_ERROR, "%a(): SendEcData(Data) failed!\n", __FUNCTION__));
    return Status;
  }
  return EFI_SUCCESS;
}
 
/**
  Query the EC status.
 
  @param[out] Status                EC status byte
 
  @retval    EFI_SUCCESS            Command success
  @retval    EFI_INVALID_PARAMETER  Data is NULL
  @retval    EFI_DEVICE_ERROR       Command error
  @retval    EFI_TIMEOUT            Command timeout
**/
EFI_STATUS
EcCmd94Query (
  OUT UINT8                  *Data
  )
{
  EFI_STATUS                 Status;
 
  if (Data == NULL) {
    return EFI_INVALID_PARAMETER;
  }
 
  Status = SendEcCommand (0x94);
  if (EFI_ERROR (Status)) {
    DEBUG((DEBUG_ERROR, "%a(): SendEcCommand(0x94) failed!\n", __FUNCTION__));
    return Status;
  }
 
  Status = ReceiveEcData (Data);
  if (EFI_ERROR (Status)) {
    DEBUG((DEBUG_ERROR, "%a(): ReceiveEcData(Data) failed!\n", __FUNCTION__));
    return Status;
  }
  return EFI_SUCCESS;
}
 
/**
  Reads a byte of EC (index) RAM.
 
  @param[in]  Address               Address to read
  @param[out] Data                  Data received
**/
VOID
EcIdxRead (
  IN  UINT16                 Address,
  OUT UINT8                  *Data
  )
{
  if (Data == NULL) {
    return;
  }
 
  IoWrite8 (EC_INDEX_IO_HIGH_ADDR_PORT, (UINT8) (Address >> 8));
  IoWrite8 (EC_INDEX_IO_LOW_ADDR_PORT, (UINT8) Address);
  *Data = IoRead8 (EC_INDEX_IO_DATA_PORT);
}
 
/**
  Writes a byte of EC (index) RAM.
 
  @param[in] Address          Address to read
  @param[in] Data             Data received
**/
VOID
EcIdxWrite (
  IN  UINT16                 Address,
  IN  UINT8                  Data
  )
{
  IoWrite8 (EC_INDEX_IO_HIGH_ADDR_PORT, (UINT8) (Address >> 8));
  IoWrite8 (EC_INDEX_IO_LOW_ADDR_PORT, (UINT8) Address);
  IoWrite8 (EC_INDEX_IO_DATA_PORT, Data);
}
 
/**
  Read EC analog-digital converter.
  TODO: Check if ADC is valid.
 
  @param[in]  Adc
  @param[out] DataBuffer
**/
VOID
ReadEcAdcConverter (
  IN  UINT8        Adc,
  OUT UINT16       *DataBuffer
  )
{
  UINT8            AdcConvertersEnabled;  // Contains some ADCs and some DACs
  UINT8            IdxData;
 
  if (DataBuffer == NULL) {
    return;
  }
 
  // Backup enabled ADCs
  EcIdxRead (0xff15, &AdcConvertersEnabled);  // ADDAEN
 
  // Enable desired ADC in bitmask (not enabled by EC FW, not used by vendor FW)
  EcIdxWrite (0xff15, AdcConvertersEnabled | ((1 << Adc) & 0xf));  // ADDAEN
 
  // Sample the desired ADC in binary field; OR the start bit
  EcIdxWrite (0xff18, ((Adc << 1) & 0xf) | 1);  // ADCTRL
 
  // Read the desired ADC
  EcIdxRead (0xff19, &IdxData);  // ADCDAT
  *DataBuffer = (IdxData << 2);
  // Lower 2-bits of 10-bit ADC are in high bits of next register
  EcIdxRead (0xff1a, &IdxData);  // ECIF
  *DataBuffer |= ((IdxData & 0xc0) >> 6);
 
  // Restore enabled ADCs
  EcIdxWrite (0xff15, AdcConvertersEnabled);  // ADDAEN
}