1 /* 2 * ChromeOS EC communication protocol helper functions 3 * 4 * Copyright (C) 2015 Google, Inc 5 * 6 * This software is licensed under the terms of the GNU General Public 7 * License version 2, as published by the Free Software Foundation, and 8 * may be copied, distributed, and modified under those terms. 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. 14 * 15 */ 16 17 #include <linux/mfd/cros_ec.h> 18 #include <linux/delay.h> 19 #include <linux/device.h> 20 #include <linux/module.h> 21 #include <linux/slab.h> 22 23 #define EC_COMMAND_RETRIES 50 24 25 static int prepare_packet(struct cros_ec_device *ec_dev, 26 struct cros_ec_command *msg) 27 { 28 struct ec_host_request *request; 29 u8 *out; 30 int i; 31 u8 csum = 0; 32 33 BUG_ON(ec_dev->proto_version != EC_HOST_REQUEST_VERSION); 34 BUG_ON(msg->outsize + sizeof(*request) > ec_dev->dout_size); 35 36 out = ec_dev->dout; 37 request = (struct ec_host_request *)out; 38 request->struct_version = EC_HOST_REQUEST_VERSION; 39 request->checksum = 0; 40 request->command = msg->command; 41 request->command_version = msg->version; 42 request->reserved = 0; 43 request->data_len = msg->outsize; 44 45 for (i = 0; i < sizeof(*request); i++) 46 csum += out[i]; 47 48 /* Copy data and update checksum */ 49 memcpy(out + sizeof(*request), msg->data, msg->outsize); 50 for (i = 0; i < msg->outsize; i++) 51 csum += msg->data[i]; 52 53 request->checksum = -csum; 54 55 return sizeof(*request) + msg->outsize; 56 } 57 58 static int send_command(struct cros_ec_device *ec_dev, 59 struct cros_ec_command *msg) 60 { 61 int ret; 62 63 if (ec_dev->proto_version > 2) 64 ret = ec_dev->pkt_xfer(ec_dev, msg); 65 else 66 ret = ec_dev->cmd_xfer(ec_dev, msg); 67 68 if (msg->result == EC_RES_IN_PROGRESS) { 69 int i; 70 struct cros_ec_command *status_msg; 71 struct ec_response_get_comms_status *status; 72 73 status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status), 74 GFP_KERNEL); 75 if (!status_msg) 76 return -ENOMEM; 77 78 status_msg->version = 0; 79 status_msg->command = EC_CMD_GET_COMMS_STATUS; 80 status_msg->insize = sizeof(*status); 81 status_msg->outsize = 0; 82 83 /* 84 * Query the EC's status until it's no longer busy or 85 * we encounter an error. 86 */ 87 for (i = 0; i < EC_COMMAND_RETRIES; i++) { 88 usleep_range(10000, 11000); 89 90 ret = ec_dev->cmd_xfer(ec_dev, status_msg); 91 if (ret < 0) 92 break; 93 94 msg->result = status_msg->result; 95 if (status_msg->result != EC_RES_SUCCESS) 96 break; 97 98 status = (struct ec_response_get_comms_status *) 99 status_msg->data; 100 if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) 101 break; 102 } 103 104 kfree(status_msg); 105 } 106 107 return ret; 108 } 109 110 int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, 111 struct cros_ec_command *msg) 112 { 113 u8 *out; 114 u8 csum; 115 int i; 116 117 if (ec_dev->proto_version > 2) 118 return prepare_packet(ec_dev, msg); 119 120 BUG_ON(msg->outsize > EC_PROTO2_MAX_PARAM_SIZE); 121 out = ec_dev->dout; 122 out[0] = EC_CMD_VERSION0 + msg->version; 123 out[1] = msg->command; 124 out[2] = msg->outsize; 125 csum = out[0] + out[1] + out[2]; 126 for (i = 0; i < msg->outsize; i++) 127 csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i]; 128 out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = csum; 129 130 return EC_MSG_TX_PROTO_BYTES + msg->outsize; 131 } 132 EXPORT_SYMBOL(cros_ec_prepare_tx); 133 134 int cros_ec_check_result(struct cros_ec_device *ec_dev, 135 struct cros_ec_command *msg) 136 { 137 switch (msg->result) { 138 case EC_RES_SUCCESS: 139 return 0; 140 case EC_RES_IN_PROGRESS: 141 dev_dbg(ec_dev->dev, "command 0x%02x in progress\n", 142 msg->command); 143 return -EAGAIN; 144 default: 145 dev_dbg(ec_dev->dev, "command 0x%02x returned %d\n", 146 msg->command, msg->result); 147 return 0; 148 } 149 } 150 EXPORT_SYMBOL(cros_ec_check_result); 151 152 static int cros_ec_host_command_proto_query(struct cros_ec_device *ec_dev, 153 int devidx, 154 struct cros_ec_command *msg) 155 { 156 /* 157 * Try using v3+ to query for supported protocols. If this 158 * command fails, fall back to v2. Returns the highest protocol 159 * supported by the EC. 160 * Also sets the max request/response/passthru size. 161 */ 162 int ret; 163 164 if (!ec_dev->pkt_xfer) 165 return -EPROTONOSUPPORT; 166 167 memset(msg, 0, sizeof(*msg)); 168 msg->command = EC_CMD_PASSTHRU_OFFSET(devidx) | EC_CMD_GET_PROTOCOL_INFO; 169 msg->insize = sizeof(struct ec_response_get_protocol_info); 170 171 ret = send_command(ec_dev, msg); 172 173 if (ret < 0) { 174 dev_dbg(ec_dev->dev, 175 "failed to check for EC[%d] protocol version: %d\n", 176 devidx, ret); 177 return ret; 178 } 179 180 if (devidx > 0 && msg->result == EC_RES_INVALID_COMMAND) 181 return -ENODEV; 182 else if (msg->result != EC_RES_SUCCESS) 183 return msg->result; 184 185 return 0; 186 } 187 188 static int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev) 189 { 190 struct cros_ec_command *msg; 191 struct ec_params_hello *hello_params; 192 struct ec_response_hello *hello_response; 193 int ret; 194 int len = max(sizeof(*hello_params), sizeof(*hello_response)); 195 196 msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL); 197 if (!msg) 198 return -ENOMEM; 199 200 msg->version = 0; 201 msg->command = EC_CMD_HELLO; 202 hello_params = (struct ec_params_hello *)msg->data; 203 msg->outsize = sizeof(*hello_params); 204 hello_response = (struct ec_response_hello *)msg->data; 205 msg->insize = sizeof(*hello_response); 206 207 hello_params->in_data = 0xa0b0c0d0; 208 209 ret = send_command(ec_dev, msg); 210 211 if (ret < 0) { 212 dev_dbg(ec_dev->dev, 213 "EC failed to respond to v2 hello: %d\n", 214 ret); 215 goto exit; 216 } else if (msg->result != EC_RES_SUCCESS) { 217 dev_err(ec_dev->dev, 218 "EC responded to v2 hello with error: %d\n", 219 msg->result); 220 ret = msg->result; 221 goto exit; 222 } else if (hello_response->out_data != 0xa1b2c3d4) { 223 dev_err(ec_dev->dev, 224 "EC responded to v2 hello with bad result: %u\n", 225 hello_response->out_data); 226 ret = -EBADMSG; 227 goto exit; 228 } 229 230 ret = 0; 231 232 exit: 233 kfree(msg); 234 return ret; 235 } 236 237 int cros_ec_query_all(struct cros_ec_device *ec_dev) 238 { 239 struct device *dev = ec_dev->dev; 240 struct cros_ec_command *proto_msg; 241 struct ec_response_get_protocol_info *proto_info; 242 int ret; 243 244 proto_msg = kzalloc(sizeof(*proto_msg) + sizeof(*proto_info), 245 GFP_KERNEL); 246 if (!proto_msg) 247 return -ENOMEM; 248 249 /* First try sending with proto v3. */ 250 ec_dev->proto_version = 3; 251 ret = cros_ec_host_command_proto_query(ec_dev, 0, proto_msg); 252 253 if (ret == 0) { 254 proto_info = (struct ec_response_get_protocol_info *) 255 proto_msg->data; 256 ec_dev->max_request = proto_info->max_request_packet_size - 257 sizeof(struct ec_host_request); 258 ec_dev->max_response = proto_info->max_response_packet_size - 259 sizeof(struct ec_host_response); 260 ec_dev->proto_version = 261 min(EC_HOST_REQUEST_VERSION, 262 fls(proto_info->protocol_versions) - 1); 263 dev_dbg(ec_dev->dev, 264 "using proto v%u\n", 265 ec_dev->proto_version); 266 267 ec_dev->din_size = ec_dev->max_response + 268 sizeof(struct ec_host_response) + 269 EC_MAX_RESPONSE_OVERHEAD; 270 ec_dev->dout_size = ec_dev->max_request + 271 sizeof(struct ec_host_request) + 272 EC_MAX_REQUEST_OVERHEAD; 273 274 /* 275 * Check for PD 276 */ 277 ret = cros_ec_host_command_proto_query(ec_dev, 1, proto_msg); 278 279 if (ret) { 280 dev_dbg(ec_dev->dev, "no PD chip found: %d\n", ret); 281 ec_dev->max_passthru = 0; 282 } else { 283 dev_dbg(ec_dev->dev, "found PD chip\n"); 284 ec_dev->max_passthru = 285 proto_info->max_request_packet_size - 286 sizeof(struct ec_host_request); 287 } 288 } else { 289 /* Try querying with a v2 hello message. */ 290 ec_dev->proto_version = 2; 291 ret = cros_ec_host_command_proto_query_v2(ec_dev); 292 293 if (ret == 0) { 294 /* V2 hello succeeded. */ 295 dev_dbg(ec_dev->dev, "falling back to proto v2\n"); 296 297 ec_dev->max_request = EC_PROTO2_MAX_PARAM_SIZE; 298 ec_dev->max_response = EC_PROTO2_MAX_PARAM_SIZE; 299 ec_dev->max_passthru = 0; 300 ec_dev->pkt_xfer = NULL; 301 ec_dev->din_size = EC_PROTO2_MSG_BYTES; 302 ec_dev->dout_size = EC_PROTO2_MSG_BYTES; 303 } else { 304 /* 305 * It's possible for a test to occur too early when 306 * the EC isn't listening. If this happens, we'll 307 * test later when the first command is run. 308 */ 309 ec_dev->proto_version = EC_PROTO_VERSION_UNKNOWN; 310 dev_dbg(ec_dev->dev, "EC query failed: %d\n", ret); 311 goto exit; 312 } 313 } 314 315 devm_kfree(dev, ec_dev->din); 316 devm_kfree(dev, ec_dev->dout); 317 318 ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL); 319 if (!ec_dev->din) { 320 ret = -ENOMEM; 321 goto exit; 322 } 323 324 ec_dev->dout = devm_kzalloc(dev, ec_dev->dout_size, GFP_KERNEL); 325 if (!ec_dev->dout) { 326 devm_kfree(dev, ec_dev->din); 327 ret = -ENOMEM; 328 goto exit; 329 } 330 331 exit: 332 kfree(proto_msg); 333 return ret; 334 } 335 EXPORT_SYMBOL(cros_ec_query_all); 336 337 int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, 338 struct cros_ec_command *msg) 339 { 340 int ret; 341 342 mutex_lock(&ec_dev->lock); 343 if (ec_dev->proto_version == EC_PROTO_VERSION_UNKNOWN) { 344 ret = cros_ec_query_all(ec_dev); 345 if (ret) { 346 dev_err(ec_dev->dev, 347 "EC version unknown and query failed; aborting command\n"); 348 mutex_unlock(&ec_dev->lock); 349 return ret; 350 } 351 } 352 353 if (msg->insize > ec_dev->max_response) { 354 dev_dbg(ec_dev->dev, "clamping message receive buffer\n"); 355 msg->insize = ec_dev->max_response; 356 } 357 358 if (msg->command < EC_CMD_PASSTHRU_OFFSET(1)) { 359 if (msg->outsize > ec_dev->max_request) { 360 dev_err(ec_dev->dev, 361 "request of size %u is too big (max: %u)\n", 362 msg->outsize, 363 ec_dev->max_request); 364 mutex_unlock(&ec_dev->lock); 365 return -EMSGSIZE; 366 } 367 } else { 368 if (msg->outsize > ec_dev->max_passthru) { 369 dev_err(ec_dev->dev, 370 "passthru rq of size %u is too big (max: %u)\n", 371 msg->outsize, 372 ec_dev->max_passthru); 373 mutex_unlock(&ec_dev->lock); 374 return -EMSGSIZE; 375 } 376 } 377 ret = send_command(ec_dev, msg); 378 mutex_unlock(&ec_dev->lock); 379 380 return ret; 381 } 382 EXPORT_SYMBOL(cros_ec_cmd_xfer); 383