summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Boichat <drinkcat@chromium.org>2017-12-13 14:44:48 +0800
committerchrome-bot <chrome-bot@chromium.org>2018-01-05 00:51:29 -0800
commitcfc69f6fb868b802a6033580ffe0cf73c092aca8 (patch)
treeaf9021ea8a120bd767d42e338cfda3b5cbb4f9e9
parent3fecdbdcf902770036b14b93ca19a5af181c9582 (diff)
downloadchrome-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.mk1
-rw-r--r--common/ec_ec_comm_master.c354
-rw-r--r--include/ec_ec_comm_master.h60
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_ */