diff options
author | Nicolas Boichat <drinkcat@chromium.org> | 2017-12-13 14:44:48 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2018-01-05 00:51:29 -0800 |
commit | cfc69f6fb868b802a6033580ffe0cf73c092aca8 (patch) | |
tree | af9021ea8a120bd767d42e338cfda3b5cbb4f9e9 | |
parent | 3fecdbdcf902770036b14b93ca19a5af181c9582 (diff) | |
download | chrome-ec-cfc69f6fb868b802a6033580ffe0cf73c092aca8.tar.gz |
ec_ec_comm_master: Functions for EC-EC communication master
This adds functions required for the master in EC-EC communication,
requesting base battery static and dynamic information, and charger
control.
BRANCH=none
BUG=b:65526215
TEST=Flash lux and wand, EC-EC communication works.
Change-Id: I7a46ee3f5848d22c2c9bea7870cbd7e74141cf3d
Signed-off-by: Nicolas Boichat <drinkcat@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/839201
-rw-r--r-- | common/build.mk | 1 | ||||
-rw-r--r-- | common/ec_ec_comm_master.c | 354 | ||||
-rw-r--r-- | include/ec_ec_comm_master.h | 60 |
3 files changed, 415 insertions, 0 deletions
diff --git a/common/build.mk b/common/build.mk index c51d95be8c..589c71bf3c 100644 --- a/common/build.mk +++ b/common/build.mk @@ -46,6 +46,7 @@ common-$(CONFIG_DEDICATED_RECOVERY_BUTTON)+=button.o common-$(CONFIG_DEVICE_EVENT)+=device_event.o common-$(CONFIG_DEVICE_STATE)+=device_state.o common-$(CONFIG_DPTF)+=dptf.o +common-$(CONFIG_EC_EC_COMM_MASTER)+=ec_ec_comm_master.o common-$(CONFIG_EC_EC_COMM_SLAVE)+=ec_ec_comm_slave.o common-$(CONFIG_ESPI)+=espi.o common-$(CONFIG_EXTENSION_COMMAND)+=extension.o diff --git a/common/ec_ec_comm_master.c b/common/ec_ec_comm_master.c new file mode 100644 index 0000000000..9a742ea40b --- /dev/null +++ b/common/ec_ec_comm_master.c @@ -0,0 +1,354 @@ +/* Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * EC-EC communication, functions and definitions for master. + */ + +#include "common.h" +#include "console.h" +#include "crc8.h" +#include "ec_commands.h" +#include "ec_ec_comm_master.h" +#include "timer.h" +#include "uart.h" +#include "util.h" + +/* Console output macros */ +#define CPRINTF(format, args...) cprintf(CC_CHARGER, format, ## args) + +/* + * TODO(b:65697620): Move these to some the second position of some battery + * array, depending on a config option. + */ +struct ec_response_battery_static_info base_battery_static; +struct ec_response_battery_dynamic_info base_battery_dynamic; + +/* + * TODO(b:65697962): The packed structures below do not play well if we force EC + * host commands structures to be aligned on 32-bit boundary. There are ways to + * fix that, possibly requiring copying data around, or modifying + * uart_alt_pad_write_read API to write the actual slave response to a separate + * buffer. + */ +#ifdef CONFIG_HOSTCMD_ALIGNED +#error "Cannot define CONFIG_HOSTCMD_ALIGNED with EC-EC communication master." +#endif + +#define EC_EC_HOSTCMD_VERSION 4 + +/* Print extra debugging information */ +#undef EXTRA_DEBUG + +/* + * During early debugging, we would like to check that the error rate does + * grow out of control. + */ +#define DEBUG_EC_COMM_STATS +#ifdef DEBUG_EC_COMM_STATS +struct { + int total; + int errtimeout; + int errbusy; + int errunknown; + int errdatacrc; + int errcrc; + int errinval; +} comm_stats; + +#define INCR_COMM_STATS(var) (comm_stats.var++) +#else +#define INCR_COMM_STATS(var) +#endif + +/** + * Write a command on the EC-EC communication UART channel. + * + * @param command One of EC_CMD_*. + * @param data Packed structure with this layout: + * struct { + * struct { + * struct ec_host_request4 head; + * struct ec_params_* param; + * uint8_t crc8; + * } req; + * struct { + * struct ec_host_response4 head; + * struct ec_response_* info; + * uint8_t crc8; + * } resp; + * } __packed data; + * + * Where req is the request to be transmitted (head and crc8 are computed by + * this function), and resp is the response to be received (head integrity and + * crc8 are verified by this function). + * + * This format is required as the EC-EC UART is half-duplex, and all the + * transmitted data is received back, i.e. the master writes req, then reads + * req, followed by resp. + * + * When a command does not take parameters, param/crc8 must be omitted in + * tx structure. The same applies to rx structure if the response does not + * include a payload: info/crc8 must be omitted. + * + * @param req_len size of req.param (0 if no parameter is passed). + * @param resp_len size of resp.info (0 if no information is returned). + * @param timeout_us timeout in microseconds for the transaction to complete. + * + * @return + * - EC_SUCCESS on success. + * - EC_ERROR_TIMEOUT when remote end times out replying. + * - EC_ERROR_BUSY when UART is busy and cannot transmit currently. + * - EC_ERROR_CRC when the header or data CRC is invalid. + * - EC_ERROR_INVAL when the received header is invalid. + * - EC_ERROR_UNKNOWN on other error. + */ +static int write_command(uint16_t command, + uint8_t *data, int req_len, int resp_len, + int timeout_us) +{ + /* Sequence number. */ + static uint8_t cur_seq; + int ret; + int hascrc, response_seq; + + struct ec_host_request4 *request_header = (void *)data; + /* Request (TX) length is header + (data + crc8), response follows. */ + int tx_length = + sizeof(*request_header) + ((req_len > 0) ? (req_len + 1) : 0); + + struct ec_host_response4 *response_header = + (void *)&data[tx_length]; + /* RX length is TX length + response from slave. */ + int rx_length = tx_length + + sizeof(*request_header) + ((resp_len > 0) ? (resp_len + 1) : 0); + + /* + * Make sure there is a gap between each command, so that the slave + * can recover its state machine after each command. + * + * TODO(b:65697962): We can be much smarter than this, and record the + * last transaction time instead of just sleeping blindly. + */ + usleep(10*MSEC); + +#ifdef DEBUG_EC_COMM_STATS + if ((comm_stats.total % 128) == 0) { + CPRINTF("UART %d (T%dB%d,U%dC%dD%dI%d)\n", comm_stats.total, + comm_stats.errtimeout, comm_stats.errbusy, + comm_stats.errunknown, comm_stats.errcrc, + comm_stats.errdatacrc, comm_stats.errinval); + } +#endif + + cur_seq = (cur_seq + 1) & + (EC_PACKET4_0_SEQ_NUM_MASK >> EC_PACKET4_0_SEQ_NUM_SHIFT); + + memset(request_header, 0, sizeof(*request_header)); + /* fields0: leave seq_dup and is_response as 0. */ + request_header->fields0 = + EC_EC_HOSTCMD_VERSION | /* version */ + (cur_seq << EC_PACKET4_0_SEQ_NUM_SHIFT); /* seq_num */ + /* fields1: leave command_version as 0. */ + if (req_len > 0) + request_header->fields1 |= EC_PACKET4_1_DATA_CRC_PRESENT_MASK; + request_header->command = command; + request_header->data_len = req_len; + request_header->header_crc = + crc8((uint8_t *)request_header, sizeof(*request_header)-1); + if (req_len > 0) + data[sizeof(*request_header) + req_len] = + crc8(&data[sizeof(*request_header)], req_len); + + ret = uart_alt_pad_write_read((void *)data, tx_length, + (void *)data, rx_length, timeout_us); + + INCR_COMM_STATS(total); + +#ifdef EXTRA_DEBUG + CPRINTF("EC-EC ret=%d/%d\n", ret, rx_length); +#endif + + if (ret != rx_length) { + if (ret == -EC_ERROR_TIMEOUT) { + INCR_COMM_STATS(errtimeout); + return EC_ERROR_TIMEOUT; + } + + if (ret == -EC_ERROR_BUSY) { + INCR_COMM_STATS(errbusy); + return EC_ERROR_BUSY; + } + + INCR_COMM_STATS(errunknown); + return EC_ERROR_UNKNOWN; + } + + if (response_header->header_crc != + crc8((uint8_t *)response_header, + sizeof(*response_header)-1)) { + INCR_COMM_STATS(errcrc); + return EC_ERROR_CRC; + } + + hascrc = response_header->fields1 & EC_PACKET4_1_DATA_CRC_PRESENT_MASK; + response_seq = (response_header->fields0 & EC_PACKET4_0_SEQ_NUM_MASK) >> + EC_PACKET4_0_SEQ_NUM_SHIFT; + + /* + * Validate received header. + * Note that we _require_ data crc to be present if there is data to be + * read back, else we would not know how many bytes to read exactly. + */ + if ((response_header->fields0 & EC_PACKET4_0_STRUCT_VERSION_MASK) + != EC_EC_HOSTCMD_VERSION || + !(response_header->fields0 & + EC_PACKET4_0_IS_RESPONSE_MASK) || + response_seq != cur_seq || + (response_header->data_len > 0 && !hascrc) || + response_header->data_len != resp_len) { + INCR_COMM_STATS(errinval); + return EC_ERROR_INVAL; + } + + /* Check data CRC. */ + if (hascrc && data[rx_length - 1] != + crc8(&data[tx_length + sizeof(*request_header)], + resp_len)) { + INCR_COMM_STATS(errdatacrc); + return EC_ERROR_CRC; + } + + return EC_SUCCESS; +} + +/** + * handle error from write_command + * + * @param ret is return value from write_command + * @param request_result is data.resp.head.result (response result value) + * + * @return EC_RES_ERROR if ret is not EC_SUCCESS, else request_result. + */ +static int handle_error(const char *func, int ret, int request_result) +{ + if (ret != EC_SUCCESS) { + /* Do not print busy errors as they just spam the console. */ + if (ret != EC_ERROR_BUSY) + CPRINTF("%s: tx error %d\n", func, ret); + return EC_RES_ERROR; + } + + if (request_result != EC_RES_SUCCESS) + CPRINTF("%s: cmd error %d\n", func, ret); + + return request_result; +} + +#ifdef CONFIG_EC_EC_COMM_BATTERY +int ec_ec_master_base_get_dynamic_info(void) +{ + int ret; + struct { + struct { + struct ec_host_request4 head; + struct ec_params_battery_dynamic_info param; + uint8_t crc8; + } req; + struct { + struct ec_host_response4 head; + struct ec_response_battery_dynamic_info info; + uint8_t crc8; + } resp; + } __packed data; + + data.req.param.index = 0; + + ret = write_command(EC_CMD_BATTERY_GET_DYNAMIC, + (void *)&data, sizeof(data.req.param), + sizeof(data.resp.info), 15 * MSEC); + ret = handle_error(__func__, ret, data.resp.head.result); + if (ret != EC_RES_SUCCESS) + return ret; + +#ifdef EXTRA_DEBUG + CPRINTF("V: %d mV\n", data.resp.info.actual_voltage); + CPRINTF("I: %d mA\n", data.resp.info.actual_current); + CPRINTF("Remaining: %d mAh\n", data.resp.info.remaining_capacity); + CPRINTF("Cap-full: %d mAh\n", data.resp.info.full_capacity); + CPRINTF("Flags: %04x\n", data.resp.info.flags); + CPRINTF("V-desired: %d mV\n", data.resp.info.desired_voltage); + CPRINTF("I-desired: %d mA\n", data.resp.info.desired_current); +#endif + + memcpy(&base_battery_dynamic, &data.resp.info, + sizeof(base_battery_dynamic)); + return EC_RES_SUCCESS; +} + +int ec_ec_master_base_get_static_info(void) +{ + int ret; + struct { + struct { + struct ec_host_request4 head; + struct ec_params_battery_static_info param; + uint8_t crc8; + } req; + struct { + struct ec_host_response4 head; + struct ec_response_battery_static_info info; + uint8_t crc8; + } resp; + } __packed data; + + data.req.param.index = 0; + + ret = write_command(EC_CMD_BATTERY_GET_STATIC, + (void *)&data, sizeof(data.req.param), + sizeof(data.resp.info), 15 * MSEC); + ret = handle_error(__func__, ret, data.resp.head.result); + if (ret != EC_RES_SUCCESS) + return ret; + +#ifdef EXTRA_DEBUG + CPRINTF("Cap-design: %d mAh\n", data.resp.info.design_capacity); + CPRINTF("V-design: %d mV\n", data.resp.info.design_voltage); + CPRINTF("Manuf: %s\n", data.resp.info.manufacturer); + CPRINTF("Model: %s\n", data.resp.info.model); + CPRINTF("Serial: %s\n", data.resp.info.serial); + CPRINTF("Type: %s\n", data.resp.info.type); + CPRINTF("C-count: %d\n", data.resp.info.cycle_count); +#endif + + memcpy(&base_battery_static, &data.resp.info, + sizeof(base_battery_static)); + return EC_RES_SUCCESS; +} + +int ec_ec_master_base_charge_control(int max_current, + int otg_voltage, + int allow_charging) +{ + int ret; + struct { + struct { + struct ec_host_request4 head; + struct ec_params_charger_control ctrl; + uint8_t crc8; + } req; + struct { + struct ec_host_response4 head; + } resp; + } __packed data; + + data.req.ctrl.allow_charging = allow_charging; + data.req.ctrl.max_current = max_current; + data.req.ctrl.otg_voltage = otg_voltage; + + ret = write_command(EC_CMD_CHARGER_CONTROL, + (void *)&data, sizeof(data.req.ctrl), 0, 30 * MSEC); + + return handle_error(__func__, ret, data.resp.head.result); +} +#endif /* CONFIG_EC_EC_COMM_BATTERY */ diff --git a/include/ec_ec_comm_master.h b/include/ec_ec_comm_master.h new file mode 100644 index 0000000000..8c04795103 --- /dev/null +++ b/include/ec_ec_comm_master.h @@ -0,0 +1,60 @@ +/* Copyright 2017 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * EC-EC communication, functions for master. + */ + +#ifndef EC_EC_COMM_MASTER_H_ +#define EC_EC_COMM_MASTER_H_ + +#include <stdint.h> +#include "config.h" + +#if defined(CONFIG_EC_EC_COMM_MASTER) && defined(CONFIG_EC_EC_COMM_BATTERY) +#define CONFIG_EC_EC_COMM_BATTERY_MASTER +#endif + +/* + * TODO(b:65697620): Move these to some other C file, depending on a config + * option. + */ +extern struct ec_response_battery_static_info base_battery_static; +extern struct ec_response_battery_dynamic_info base_battery_dynamic; + +/** + * Sends EC_CMD_BATTERY_GET_DYNAMIC command to slave, and writes the + * battery dynamic information into base_battery_dynamic. + * + * Leaves base_battery_dynamic intact on error: it is the callers responsability + * to clear the data or ignore it. + + * @return EC_RES_SUCCESS on success, EC_RES_ERROR on communication error, + * else forwards the error code from the slave. + */ +int ec_ec_master_base_get_dynamic_info(void); + +/** + * Sends EC_CMD_BATTERY_GET_STATIC command to slave, and writes the + * battery static information into base_static_dynamic. + * + * Leaves base_battery_static intact on error: it is the callers responsability + * to clear the data or ignore it. + * + * @return EC_RES_SUCCESS on success, EC_RES_ERROR on communication error, + * else forwards the error code from the slave. + */ +int ec_ec_master_base_get_static_info(void); + +/** + * Sends EC_CMD_CHARGER_CONTROL command to slave, with the given parameters + * (see ec_commands.h/ec_params_charger_control for description). + * + * @return EC_RES_SUCCESS on success, EC_RES_ERROR on communication error, + * else forwards the error code from the slave. + */ +int ec_ec_master_base_charge_control(int max_current, + int otg_voltage, + int allow_charging); + +#endif /* EC_EC_COMM_MASTER_H_ */ |