Newer
Older
/*
* Chromium OS cros_ec driver
*
* Copyright (c) 2012 The Chromium OS Authors.
*
* SPDX-License-Identifier: GPL-2.0+
* This is the interface to the Chrome OS EC. It provides keyboard functions,
* power control and battery management. Quite a few other functions are
* provided to enable the EC software to be updated, talk to the EC's I2C bus
* and store a small amount of data in a memory which persists while the EC
* is not reset.
*/
#include <common.h>
#include <command.h>
#include <i2c.h>
#include <cros_ec.h>
#include <fdtdec.h>
#include <malloc.h>
#include <spi.h>
#include <linux/errno.h>
#include <asm/io.h>
#include <asm-generic/gpio.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
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
#ifdef DEBUG_TRACE
#define debug_trace(fmt, b...) debug(fmt, #b)
#else
#define debug_trace(fmt, b...)
#endif
enum {
/* Timeout waiting for a flash erase command to complete */
CROS_EC_CMD_TIMEOUT_MS = 5000,
/* Timeout waiting for a synchronous hash to be recomputed */
CROS_EC_CMD_HASH_TIMEOUT_MS = 2000,
};
DECLARE_GLOBAL_DATA_PTR;
void cros_ec_dump_data(const char *name, int cmd, const uint8_t *data, int len)
{
#ifdef DEBUG
int i;
printf("%s: ", name);
if (cmd != -1)
printf("cmd=%#x: ", cmd);
for (i = 0; i < len; i++)
printf("%02x ", data[i]);
printf("\n");
#endif
}
/*
* Calculate a simple 8-bit checksum of a data block
*
* @param data Data block to checksum
* @param size Size of data block in bytes
* @return checksum value (0 to 255)
*/
int cros_ec_calc_checksum(const uint8_t *data, int size)
{
int csum, i;
for (i = csum = 0; i < size; i++)
csum += data[i];
return csum & 0xff;
}
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
/**
* Create a request packet for protocol version 3.
*
* The packet is stored in the device's internal output buffer.
*
* @param dev CROS-EC device
* @param cmd Command to send (EC_CMD_...)
* @param cmd_version Version of command to send (EC_VER_...)
* @param dout Output data (may be NULL If dout_len=0)
* @param dout_len Size of output data in bytes
* @return packet size in bytes, or <0 if error.
*/
static int create_proto3_request(struct cros_ec_dev *dev,
int cmd, int cmd_version,
const void *dout, int dout_len)
{
struct ec_host_request *rq = (struct ec_host_request *)dev->dout;
int out_bytes = dout_len + sizeof(*rq);
/* Fail if output size is too big */
if (out_bytes > (int)sizeof(dev->dout)) {
debug("%s: Cannot send %d bytes\n", __func__, dout_len);
return -EC_RES_REQUEST_TRUNCATED;
}
/* Fill in request packet */
rq->struct_version = EC_HOST_REQUEST_VERSION;
rq->checksum = 0;
rq->command = cmd;
rq->command_version = cmd_version;
rq->reserved = 0;
rq->data_len = dout_len;
/* Copy data after header */
memcpy(rq + 1, dout, dout_len);
/* Write checksum field so the entire packet sums to 0 */
rq->checksum = (uint8_t)(-cros_ec_calc_checksum(dev->dout, out_bytes));
cros_ec_dump_data("out", cmd, dev->dout, out_bytes);
/* Return size of request packet */
return out_bytes;
}
/**
* Prepare the device to receive a protocol version 3 response.
*
* @param dev CROS-EC device
* @param din_len Maximum size of response in bytes
* @return maximum expected number of bytes in response, or <0 if error.
*/
static int prepare_proto3_response_buffer(struct cros_ec_dev *dev, int din_len)
{
int in_bytes = din_len + sizeof(struct ec_host_response);
/* Fail if input size is too big */
if (in_bytes > (int)sizeof(dev->din)) {
debug("%s: Cannot receive %d bytes\n", __func__, din_len);
return -EC_RES_RESPONSE_TOO_BIG;
}
/* Return expected size of response packet */
return in_bytes;
}
/**
* Handle a protocol version 3 response packet.
*
* The packet must already be stored in the device's internal input buffer.
*
* @param dev CROS-EC device
* @param dinp Returns pointer to response data
* @param din_len Maximum size of response in bytes
* @return number of bytes of response data, or <0 if error. Note that error
* codes can be from errno.h or -ve EC_RES_INVALID_CHECKSUM values (and they
* overlap!)
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
*/
static int handle_proto3_response(struct cros_ec_dev *dev,
uint8_t **dinp, int din_len)
{
struct ec_host_response *rs = (struct ec_host_response *)dev->din;
int in_bytes;
int csum;
cros_ec_dump_data("in-header", -1, dev->din, sizeof(*rs));
/* Check input data */
if (rs->struct_version != EC_HOST_RESPONSE_VERSION) {
debug("%s: EC response version mismatch\n", __func__);
return -EC_RES_INVALID_RESPONSE;
}
if (rs->reserved) {
debug("%s: EC response reserved != 0\n", __func__);
return -EC_RES_INVALID_RESPONSE;
}
if (rs->data_len > din_len) {
debug("%s: EC returned too much data\n", __func__);
return -EC_RES_RESPONSE_TOO_BIG;
}
cros_ec_dump_data("in-data", -1, dev->din + sizeof(*rs), rs->data_len);
/* Update in_bytes to actual data size */
in_bytes = sizeof(*rs) + rs->data_len;
/* Verify checksum */
csum = cros_ec_calc_checksum(dev->din, in_bytes);
if (csum) {
debug("%s: EC response checksum invalid: 0x%02x\n", __func__,
csum);
return -EC_RES_INVALID_CHECKSUM;
}
/* Return error result, if any */
if (rs->result)
return -(int)rs->result;
/* If we're still here, set response data pointer and return length */
*dinp = (uint8_t *)(rs + 1);
return rs->data_len;
}
Loading
Loading full blame...