blob: 1a8ffb93c51b8601f6ba27cca44b033222d806fe [file] [log] [blame]
Hung-Te Lin23fb9972013-06-21 20:11:47 +08001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2013 Google Inc. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
Hung-Te Lin23fb9972013-06-21 20:11:47 +080014 */
15
16#include <arch/io.h>
Aaron Durbin64031672018-04-21 14:45:32 -060017#include <compiler.h>
Hung-Te Lin23fb9972013-06-21 20:11:47 +080018#include <console/console.h>
19#include <delay.h>
20#include <stdint.h>
21#include <string.h>
22#include "ec.h"
23#include "ec_commands.h"
24#include "ec_message.h"
25
Hung-Te Line946f982013-06-22 11:18:39 +080026/* Common utilities */
Aaron Durbin64031672018-04-21 14:45:32 -060027void * __weak crosec_get_buffer(size_t size, int req)
Aaron Durbin82827272014-08-06 14:34:57 -050028{
29 printk(BIOS_DEBUG, "crosec_get_buffer() implementation required.\n");
30 return NULL;
31}
Hung-Te Line946f982013-06-22 11:18:39 +080032
33/* Dumps EC command / response data into debug output.
34 *
35 * @param name Message prefix name.
36 * @param cmd Command code, or -1 to ignore cmd message.
37 * @param data Data buffer to print.
38 * @param len Length of data.
39 */
40static void cros_ec_dump_data(const char *name, int cmd, const uint8_t *data,
41 int len)
42{
43 int i;
44
45 printk(BIOS_DEBUG, "%s: ", name);
46 if (cmd != -1)
47 printk(BIOS_DEBUG, "cmd=%#x: ", cmd);
48 for (i = 0; i < len; i++)
49 printk(BIOS_DEBUG, "%02x ", data[i]);
50 printk(BIOS_DEBUG, "\n");
51}
52
53/* Calculate a simple 8-bit checksum of a data block
54 *
55 * @param data Data block to checksum
56 * @param size Size of data block in bytes
57 * @return checksum value (0 to 255)
58 */
59static int cros_ec_calc_checksum(const uint8_t *data, int size)
60{
61 int csum, i;
62
63 for (i = csum = 0; i < size; i++)
64 csum += data[i];
65 return csum & 0xff;
66}
67
68/* Standard Chrome EC protocol, version 3 */
69
70struct ec_command_v3 {
71 struct ec_host_request header;
72 uint8_t data[MSG_BYTES];
73};
74
75struct ec_response_v3 {
76 struct ec_host_response header;
77 uint8_t data[MSG_BYTES];
78};
79
80/**
81 * Create a request packet for protocol version 3.
82 *
83 * @param cec_command Command description.
84 * @param cmd Packed command bit stream.
85 * @return packet size in bytes, or <0 if error.
86 */
87static int create_proto3_request(const struct chromeec_command *cec_command,
88 struct ec_command_v3 *cmd)
89{
90 struct ec_host_request *rq = &cmd->header;
91 int out_bytes = cec_command->cmd_size_in + sizeof(*rq);
92
93 /* Fail if output size is too big */
94 if (out_bytes > sizeof(*cmd)) {
95 printk(BIOS_ERR, "%s: Cannot send %d bytes\n", __func__,
96 cec_command->cmd_size_in);
97 return -EC_RES_REQUEST_TRUNCATED;
98 }
99
100 /* Fill in request packet */
101 rq->struct_version = EC_HOST_REQUEST_VERSION;
102 rq->checksum = 0;
103 rq->command = cec_command->cmd_code;
104 rq->command_version = cec_command->cmd_version;
105 rq->reserved = 0;
106 rq->data_len = cec_command->cmd_size_in;
107
108 /* Copy data after header */
109 memcpy(cmd->data, cec_command->cmd_data_in, cec_command->cmd_size_in);
110
111 /* Write checksum field so the entire packet sums to 0 */
112 rq->checksum = (uint8_t)(-cros_ec_calc_checksum(
113 (const uint8_t*)cmd, out_bytes));
114
115 cros_ec_dump_data("out", rq->command, (const uint8_t *)cmd, out_bytes);
116
117 /* Return size of request packet */
118 return out_bytes;
119}
120
121/**
122 * Prepare the device to receive a protocol version 3 response.
123 *
124 * @param cec_command Command description.
125 * @param resp Response buffer.
126 * @return maximum expected number of bytes in response, or <0 if error.
127 */
128static int prepare_proto3_response_buffer(
129 const struct chromeec_command *cec_command,
130 struct ec_response_v3 *resp)
131{
132 int in_bytes = cec_command->cmd_size_out + sizeof(resp->header);
133
134 /* Fail if input size is too big */
135 if (in_bytes > sizeof(*resp)) {
136 printk(BIOS_ERR, "%s: Cannot receive %d bytes\n", __func__,
137 cec_command->cmd_size_out);
138 return -EC_RES_RESPONSE_TOO_BIG;
139 }
140
141 /* Return expected size of response packet */
142 return in_bytes;
143}
144
145/**
146 * Handle a protocol version 3 response packet.
147 *
148 * The packet must already be stored in the response buffer.
149 *
150 * @param resp Response buffer.
151 * @param cec_command Command structure to receive valid response.
152 * @return number of bytes of response data, or <0 if error
153 */
154static int handle_proto3_response(struct ec_response_v3 *resp,
155 struct chromeec_command *cec_command)
156{
157 struct ec_host_response *rs = &resp->header;
158 int in_bytes;
159 int csum;
160
161 cros_ec_dump_data("in-header", -1, (const uint8_t*)rs, sizeof(*rs));
162
163 /* Check input data */
164 if (rs->struct_version != EC_HOST_RESPONSE_VERSION) {
165 printk(BIOS_ERR, "%s: EC response version mismatch\n", __func__);
166 return -EC_RES_INVALID_RESPONSE;
167 }
168
169 if (rs->reserved) {
170 printk(BIOS_ERR, "%s: EC response reserved != 0\n", __func__);
171 return -EC_RES_INVALID_RESPONSE;
172 }
173
174 if (rs->data_len > sizeof(resp->data) ||
175 rs->data_len > cec_command->cmd_size_out) {
176 printk(BIOS_ERR, "%s: EC returned too much data\n", __func__);
177 return -EC_RES_RESPONSE_TOO_BIG;
178 }
179
180 cros_ec_dump_data("in-data", -1, resp->data, rs->data_len);
181
182 /* Update in_bytes to actual data size */
183 in_bytes = sizeof(*rs) + rs->data_len;
184
185 /* Verify checksum */
186 csum = cros_ec_calc_checksum((const uint8_t *)resp, in_bytes);
187 if (csum) {
188 printk(BIOS_ERR, "%s: EC response checksum invalid: 0x%02x\n",
189 __func__, csum);
190 return -EC_RES_INVALID_CHECKSUM;
191 }
192
193 /* Return raw response. */
194 cec_command->cmd_code = rs->result;
195 cec_command->cmd_size_out = rs->data_len;
196 memcpy(cec_command->cmd_data_out, resp->data, rs->data_len);
197
198 /* Return error result, if any */
199 if (rs->result) {
200 printk(BIOS_ERR, "%s: EC response with error code: %d\n",
201 __func__, rs->result);
202 return -(int)rs->result;
203 }
204
205 return rs->data_len;
206}
207
208static int send_command_proto3(struct chromeec_command *cec_command,
209 crosec_io_t crosec_io, void *context)
210{
211 int out_bytes, in_bytes;
212 int rv;
Aaron Durbin82827272014-08-06 14:34:57 -0500213 struct ec_command_v3 *cmd;
214 struct ec_response_v3 *resp;
215
216 if ((cmd = crosec_get_buffer(sizeof(*cmd), 1)) == NULL)
217 return -EC_RES_ERROR;
218 if ((resp = crosec_get_buffer(sizeof(*resp), 0)) == NULL)
219 return -EC_RES_ERROR;
Hung-Te Line946f982013-06-22 11:18:39 +0800220
221 /* Create request packet */
Aaron Durbin82827272014-08-06 14:34:57 -0500222 out_bytes = create_proto3_request(cec_command, cmd);
Hung-Te Line946f982013-06-22 11:18:39 +0800223 if (out_bytes < 0) {
224 return out_bytes;
225 }
226
227 /* Prepare response buffer */
Aaron Durbin82827272014-08-06 14:34:57 -0500228 in_bytes = prepare_proto3_response_buffer(cec_command, resp);
Hung-Te Line946f982013-06-22 11:18:39 +0800229 if (in_bytes < 0) {
230 return in_bytes;
231 }
232
Aaron Durbin82827272014-08-06 14:34:57 -0500233 rv = crosec_io(out_bytes, in_bytes, context);
Hung-Te Line946f982013-06-22 11:18:39 +0800234 if (rv != 0) {
Julius Werner02e847b2015-02-20 13:36:11 -0800235 printk(BIOS_ERR, "%s: failed to complete I/O: Err = %#x.\n",
Hung-Te Line946f982013-06-22 11:18:39 +0800236 __func__, rv >= 0 ? rv : -rv);
237 return -EC_RES_ERROR;
238 }
239
240 /* Process the response */
Aaron Durbin82827272014-08-06 14:34:57 -0500241 return handle_proto3_response(resp, cec_command);
Hung-Te Line946f982013-06-22 11:18:39 +0800242}
243
244static int crosec_command_proto_v3(struct chromeec_command *cec_command,
245 crosec_io_t crosec_io, void *context)
246{
247 int rv = send_command_proto3(cec_command, crosec_io, context);
248 if (rv < 0) {
249 cec_command->cmd_code = rv;
250 return 1;
251 }
252 return 0;
253}
254
Hung-Te Lin23fb9972013-06-21 20:11:47 +0800255int crosec_command_proto(struct chromeec_command *cec_command,
256 crosec_io_t crosec_io, void *context)
257{
Hung-Te Line946f982013-06-22 11:18:39 +0800258 // TODO(hungte) Detect and fallback to v2 if we need.
259 return crosec_command_proto_v3(cec_command, crosec_io, context);
Hung-Te Lin23fb9972013-06-21 20:11:47 +0800260}