summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomasz Michalec <tm@semihalf.com>2021-05-11 12:41:38 +0200
committerCommit Bot <commit-bot@chromium.org>2021-05-18 20:59:50 +0000
commit4698c8c20a44c5519d4ddabc3f409e0d9f9f5c68 (patch)
tree639a67053eb652b882190619a1996518504bbff5
parent59780263fcca42d1b411fddf48ebcba683d5f26b (diff)
downloadchrome-ec-4698c8c20a44c5519d4ddabc3f409e0d9f9f5c68.tar.gz
zephyr: Add Smart Battery emulator
Add Smart Battery emulator which is emulated device on i2c bus. Emulated battery properties are defined through device tree, but they can be changed in runtime through Smart Battery emulator API. It allows to set custom handlers for write and read messages to emulate more complex scenarios or malfunctioning device. BUG=b:184855975 BRANCH=none TEST=none Signed-off-by: Tomasz Michalec <tm@semihalf.com> Change-Id: Ia94a0a122123e3259882dfdc80d067c61c98379b Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2903206 Reviewed-by: Jeremy Bettis <jbettis@chromium.org> Reviewed-by: Simon Glass <sjg@chromium.org> Commit-Queue: Simon Glass <sjg@chromium.org> Tested-by: Simon Glass <sjg@chromium.org>
-rw-r--r--driver/battery/smart.c9
-rw-r--r--include/battery_smart.h23
-rw-r--r--zephyr/CMakeLists.txt1
-rw-r--r--zephyr/Kconfig1
-rw-r--r--zephyr/dts/bindings/emul/zephyr,smart-battery.yaml155
-rw-r--r--zephyr/emul/CMakeLists.txt5
-rw-r--r--zephyr/emul/Kconfig8
-rw-r--r--zephyr/emul/emul_smart_battery.c995
-rw-r--r--zephyr/include/emul/emul_smart_battery.h243
9 files changed, 1436 insertions, 4 deletions
diff --git a/driver/battery/smart.c b/driver/battery/smart.c
index 1c2dcaf4c9..fd7dbcafb9 100644
--- a/driver/battery/smart.c
+++ b/driver/battery/smart.c
@@ -294,9 +294,12 @@ test_mockable int battery_manufacture_date(int *year, int *month, int *day)
/* battery date format:
* ymd = day + month * 32 + (year - 1980) * 512
*/
- *year = (ymd >> 9) + 1980;
- *month = (ymd >> 5) & 0xf;
- *day = ymd & 0x1f;
+ *year = ((ymd & MANUFACTURE_DATE_YEAR_MASK) >>
+ MANUFACTURE_DATE_YEAR_SHIFT) + MANUFACTURE_DATE_YEAR_OFFSET;
+ *month = (ymd & MANUFACTURE_DATE_MONTH_MASK) >>
+ MANUFACTURE_DATE_MONTH_SHIFT;
+ *day = (ymd & MANUFACTURE_DATE_DAY_MASK) >>
+ MANUFACTURE_DATE_DAY_SHIFT;
return EC_SUCCESS;
}
diff --git a/include/battery_smart.h b/include/battery_smart.h
index 635ac8558a..a610092d31 100644
--- a/include/battery_smart.h
+++ b/include/battery_smart.h
@@ -92,11 +92,23 @@
#define STATUS_OVERCHARGED_ALARM BIT(15)
/* Battery Spec Info */
-#define BATTERY_SPEC_VERSION(INFO) ((INFO >> 4) & 0xF)
+#define BATTERY_SPEC_REVISION_MASK 0x000F
+#define BATTERY_SPEC_REVISION_SHIFT 0
+#define BATTERY_SPEC_VERSION_MASK 0x00F0
+#define BATTERY_SPEC_VERSION_SHIFT 4
+#define BATTERY_SPEC_VSCALE_MASK 0x0F00
+#define BATTERY_SPEC_VSCALE_SHIFT 8
+#define BATTERY_SPEC_IPSCALE_MASK 0xF000
+#define BATTERY_SPEC_IPSCALE_SHIFT 12
+
+#define BATTERY_SPEC_VERSION(INFO) ((INFO & BATTERY_SPEC_VERSION_MASK) >> \
+ BATTERY_SPEC_VERSION_SHIFT)
/* Smart battery version info */
#define BATTERY_SPEC_VER_1_0 1
#define BATTERY_SPEC_VER_1_1 2
#define BATTERY_SPEC_VER_1_1_WITH_PEC 3
+/* Smart battery revision info */
+#define BATTERY_SPEC_REVISION_1 1
/* Charger alarm warning */
#define ALARM_OVER_CHARGED 0x8000
@@ -145,6 +157,15 @@
#define BATTERY_DISCHARGING_DISABLED 0x20
#define BATTERY_CHARGING_DISABLED 0x40
+/* Battery manufacture date */
+#define MANUFACTURE_DATE_DAY_MASK 0x001F
+#define MANUFACTURE_DATE_DAY_SHIFT 0
+#define MANUFACTURE_DATE_MONTH_MASK 0x01E0
+#define MANUFACTURE_DATE_MONTH_SHIFT 5
+#define MANUFACTURE_DATE_YEAR_MASK 0xFE00
+#define MANUFACTURE_DATE_YEAR_SHIFT 9
+#define MANUFACTURE_DATE_YEAR_OFFSET 1980
+
/* Read from battery */
int sb_read(int cmd, int *param);
diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt
index b4e1b0475d..954a691a0b 100644
--- a/zephyr/CMakeLists.txt
+++ b/zephyr/CMakeLists.txt
@@ -173,6 +173,7 @@ endif()
add_subdirectory("app")
add_subdirectory("drivers")
+add_subdirectory("emul")
add_subdirectory_ifdef(CONFIG_PLATFORM_EC "shim")
# Creates a phony target all.libraries in case you only want to build the
diff --git a/zephyr/Kconfig b/zephyr/Kconfig
index 31df8475e8..356eaf262d 100644
--- a/zephyr/Kconfig
+++ b/zephyr/Kconfig
@@ -4,6 +4,7 @@
rsource "app/Kconfig"
rsource "drivers/Kconfig"
+rsource "emul/Kconfig"
if ZTEST
diff --git a/zephyr/dts/bindings/emul/zephyr,smart-battery.yaml b/zephyr/dts/bindings/emul/zephyr,smart-battery.yaml
new file mode 100644
index 0000000000..cc1d2f368d
--- /dev/null
+++ b/zephyr/dts/bindings/emul/zephyr,smart-battery.yaml
@@ -0,0 +1,155 @@
+# Copyright 2021 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.
+
+description: Zephyr Smart Battery Emulator
+
+compatible: "zephyr,smart-battery"
+
+include: base.yaml
+
+properties:
+ mf-access:
+ type: int
+ required: false
+ default: 0
+ description: Word returned on manufacturer access command.
+
+ at-rate-full-mw-support:
+ type: boolean
+ description:
+ Flag indicating if AT_RATE_TIME_TO_FULL command supports mW capacity
+ mode.
+
+ version:
+ type: string
+ required: false
+ enum:
+ - BATTERY_SPEC_VER_1_0
+ - BATTERY_SPEC_VER_1_1
+ - BATTERY_SPEC_VER_1_1_WITH_PEC
+ default: BATTERY_SPEC_VER_1_1_WITH_PEC
+ description: Version of Smart Battery.
+
+ vscale:
+ type: int
+ required: false
+ default: 0
+ description: Scaling of voltage.
+
+ ipscale:
+ type: int
+ required: false
+ default: 0
+ description: Scaling of current.
+
+ int-charge-controller:
+ type: boolean
+ description: Flag indicating if internal charge controller is supported.
+
+ primary-battery:
+ type: boolean
+ description:
+ Flag indicating if primary battery role selection is supported.
+
+ design-mv:
+ type: int
+ required: false
+ default: 5000
+ description: Design battery voltage in mV.
+
+ design-cap:
+ type: int
+ required: false
+ default: 5000
+ description: Design battery capacity in mAh.
+
+ temperature:
+ type: int
+ required: false
+ default: 2930
+ description: Battery temperature in 0.1 Kelvins.
+
+ volt:
+ type: int
+ required: false
+ default: 5000
+ description: Battery voltage in mV.
+
+ cur:
+ type: int
+ required: false
+ default: 1000
+ description: Current charging (> 0) or discharging (< 0) battery in mA.
+
+ avg-cur:
+ type: int
+ required: false
+ default: 1000
+ description: Average current from 1 minute.
+
+ max-error:
+ type: int
+ required: false
+ default: 0
+ description: Maximum error of commands return value in percent.
+
+ cap:
+ type: int
+ required: false
+ default: 2000
+ description: Capacity of the battery in mAh.
+
+ full-cap:
+ type: int
+ required: false
+ default: 4000
+ description: Full capacity of the battery in mAh.
+
+ desired-charg-cur:
+ type: int
+ required: false
+ default: 2000
+ description: Charging current requested by battery.
+
+ desired-charg-volt:
+ type: int
+ required: false
+ default: 7000
+ description: Charging voltage requested by battery.
+
+ cycle-count:
+ type: int
+ required: false
+ default: 125
+ description: Number of cycles.
+
+ serial-number:
+ type: int
+ required: false
+ default: 7
+ description: Serial number of battery.
+
+ mf-name:
+ type: string
+ required: false
+ default: "zephyr"
+ description: Manufacturer name. Length has to be smaller than 32 bytes.
+
+ dev-name:
+ type: string
+ required: false
+ default: "smartbat"
+ description: Device name. Length has to be smaller than 32 bytes.
+
+ dev-chem:
+ type: string
+ required: false
+ default: "LION"
+ description: Device chemistry. Length has to be smaller than 32 bytes.
+
+ mf-data:
+ type: string
+ required: false
+ default: "LION"
+ description: Manufacturer data. Length has to be smaller than 32 bytes.
diff --git a/zephyr/emul/CMakeLists.txt b/zephyr/emul/CMakeLists.txt
new file mode 100644
index 0000000000..d16dd068cc
--- /dev/null
+++ b/zephyr/emul/CMakeLists.txt
@@ -0,0 +1,5 @@
+# Copyright 2021 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.
+
+zephyr_library_sources_ifdef(CONFIG_EMUL_SMART_BATTERY emul_smart_battery.c)
diff --git a/zephyr/emul/Kconfig b/zephyr/emul/Kconfig
new file mode 100644
index 0000000000..c7e64b9b14
--- /dev/null
+++ b/zephyr/emul/Kconfig
@@ -0,0 +1,8 @@
+# Copyright 2021 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.
+
+config EMUL_SMART_BATTERY
+ bool "Smart Battery emulator"
+ help
+ Enable the Smart Battery emulator. This driver use emulated I2C bus.
diff --git a/zephyr/emul/emul_smart_battery.c b/zephyr/emul/emul_smart_battery.c
new file mode 100644
index 0000000000..6d23269d2a
--- /dev/null
+++ b/zephyr/emul/emul_smart_battery.c
@@ -0,0 +1,995 @@
+/* Copyright 2021 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.
+ */
+
+#define DT_DRV_COMPAT zephyr_smart_battery
+
+#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL
+#include <logging/log.h>
+LOG_MODULE_REGISTER(smart_battery);
+
+#include <device.h>
+#include <emul.h>
+#include <drivers/i2c.h>
+#include <drivers/i2c_emul.h>
+
+#include "emul/emul_smart_battery.h"
+
+#include "crc8.h"
+#include "battery_smart.h"
+
+/** Run-time data used by the emulator */
+struct sbat_emul_data {
+ /** I2C emulator detail */
+ struct i2c_emul emul;
+ /** Smart battery device being emulated */
+ const struct device *i2c;
+ /** Configuration information */
+ const struct sbat_emul_cfg *cfg;
+ /** Data required to simulate battery */
+ struct sbat_emul_bat_data bat;
+ /** Command that should be handled next */
+ int cur_cmd;
+ /** Message buffer which is used to handle smb transactions */
+ uint8_t msg_buf[MSG_BUF_LEN];
+ /** Position in msg_buf if there is on going smb write opperation */
+ int ong_write;
+ /** Position in msg_buf if there is on going smb read opperation */
+ int ong_read;
+ /** Total bytes that were generated in response to smb read operation */
+ int num_to_read;
+ /** Custom write function called on smb write opperation */
+ sbat_emul_custom_func write_custom_func;
+ /** Data passed to custom write function */
+ void *write_custom_func_data;
+ /** Custom read function called on smb read opperation */
+ sbat_emul_custom_func read_custom_func;
+ /** Data passed to custom read function */
+ void *read_custom_func_data;
+
+ /** Mutex used to control access to battery data */
+ struct k_mutex bat_mtx;
+};
+
+/** Static configuration for the emulator */
+struct sbat_emul_cfg {
+ /** Label of the I2C bus this emulator connects to */
+ const char *i2c_label;
+ /** Pointer to run-time data */
+ struct sbat_emul_data *data;
+ /** Address of smart battery on i2c bus */
+ uint16_t addr;
+};
+
+/** Check description in emul_smart_battery.h */
+struct sbat_emul_bat_data *sbat_emul_get_bat_data(struct i2c_emul *emul)
+{
+ struct sbat_emul_data *data;
+
+ data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+
+ return &data->bat;
+}
+
+/** Check description in emul_smart_battery.h */
+int sbat_emul_lock_bat_data(struct i2c_emul *emul, k_timeout_t timeout)
+{
+ struct sbat_emul_data *data;
+
+ data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+
+ return k_mutex_lock(&data->bat_mtx, timeout);
+}
+
+/** Check description in emul_smart_battery.h */
+int sbat_emul_unlock_bat_dat(struct i2c_emul *emul)
+{
+ struct sbat_emul_data *data;
+
+ data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+
+ return k_mutex_unlock(&data->bat_mtx);
+}
+
+/** Check description in emul_smart_battery.h */
+void sbat_emul_set_custom_write_func(struct i2c_emul *emul,
+ sbat_emul_custom_func func, void *data)
+{
+ struct sbat_emul_data *emul_data;
+
+ emul_data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+ emul_data->write_custom_func = func;
+ emul_data->write_custom_func_data = data;
+}
+
+/** Check description in emul_smart_battery.h */
+void sbat_emul_set_custom_read_func(struct i2c_emul *emul,
+ sbat_emul_custom_func func, void *data)
+{
+ struct sbat_emul_data *emul_data;
+
+ emul_data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+ emul_data->read_custom_func = func;
+ emul_data->read_custom_func_data = data;
+}
+
+/** Check description in emul_smart_battery.h */
+uint16_t sbat_emul_date_to_word(unsigned int day, unsigned int month,
+ unsigned int year)
+{
+ year -= MANUFACTURE_DATE_YEAR_OFFSET;
+ year <<= MANUFACTURE_DATE_YEAR_SHIFT;
+ year &= MANUFACTURE_DATE_YEAR_MASK;
+ month <<= MANUFACTURE_DATE_MONTH_SHIFT;
+ month &= MANUFACTURE_DATE_MONTH_MASK;
+ day <<= MANUFACTURE_DATE_DAY_SHIFT;
+ day &= MANUFACTURE_DATE_DAY_MASK;
+
+ return day | month | year;
+}
+
+/**
+ * @brief Compute CRC from the beginning of the message
+ *
+ * @param addr Smart battery address on SMBus
+ * @param read If message for which CRC is computed is read. For read message
+ * byte command and repeated address is added to CRC
+ * @param cmd Command used in read message
+ *
+ * @return pec CRC from first bytes of message
+ */
+static uint8_t sbat_emul_pec_head(uint8_t addr, int read, uint8_t cmd)
+{
+ uint8_t pec;
+
+ addr <<= 1;
+
+ pec = cros_crc8(&addr, 1);
+ if (!read) {
+ return pec;
+ }
+
+ pec = cros_crc8_arg(&cmd, 1, pec);
+ addr |= I2C_MSG_READ;
+ pec = cros_crc8_arg(&addr, 1, pec);
+
+ return pec;
+}
+
+/**
+ * @brief Convert from 10mW power units to mA current under given mV voltage
+ *
+ * @param mw Power in 10mW units
+ * @param mv Voltage in mV units
+ *
+ * @return Current in mA units
+ */
+static uint16_t sbat_emul_10mw_to_ma(int mw, int mv)
+{
+ /* Smart battery use 10mW units, convert to mW */
+ mw *= 10;
+ /* Multiple by 1000 to get mA instead of A */
+ return 1000 * mw/mv;
+}
+
+/**
+ * @brief Convert from mA current to 10mW power under given mV voltage
+ *
+ * @param ma Current in mA units
+ * @param mv Voltage in mV units
+ *
+ * @return Power in 10mW units
+ */
+static uint16_t sbat_emul_ma_to_10mw(int ma, int mv)
+{
+ int mw;
+ /* Divide by 1000 to get mW instead of uW */
+ mw = ma * mv / 1000;
+ /* Smart battery use 10mW units, convert to 10mW */
+ return mw / 10;
+}
+
+/**
+ * @brief Get time in minutes how long it will take to get given amount of
+ * charge at given current flow
+ *
+ * @param bat Pointer to battery data to set error code in case of
+ * over/under flow in time calculation
+ * @param rate Rate of current in mAh
+ * @param cap Required amount of charge in mA
+ * @param time Pointer to memory where calculated time will be stored
+ *
+ * @return 0 on success
+ * @return -EINVAL when over or under flow occurred
+ */
+static int sbat_emul_get_time_to_complete(struct sbat_emul_bat_data *bat,
+ int rate, int cap, uint16_t *ret_time)
+{
+ int time;
+
+ /* At negative rate process never ends, return maximum value */
+ if (rate <= 0) {
+ *ret_time = UINT16_MAX;
+
+ return 0;
+ }
+ /* Convert capacity from mAh to mAmin */
+ time = cap * 60 / rate;
+ /* Check overflow */
+ if (time >= UINT16_MAX) {
+ *ret_time = UINT16_MAX;
+ bat->error_code = STATUS_CODE_OVERUNDERFLOW;
+
+ return -EINVAL;
+ }
+ /* Check underflow */
+ if (time < 0) {
+ *ret_time = 0;
+ bat->error_code = STATUS_CODE_OVERUNDERFLOW;
+
+ return -EINVAL;
+ }
+
+ *ret_time = time;
+
+ return 0;
+}
+
+/**
+ * @brief Get time in minutes how long it will take to charge battery
+ *
+ * @param bat Pointer to battery data
+ * @param rate Rate of charging current in mAh
+ * @param time Pointer to memory where calculated time will be stored
+ *
+ * @return 0 on success
+ * @return -EINVAL when over or under flow occurred
+ */
+static int sbat_emul_time_to_full(struct sbat_emul_bat_data *bat, int rate,
+ uint16_t *time)
+{
+ int cap;
+
+ cap = bat->full_cap - bat->cap;
+ return sbat_emul_get_time_to_complete(bat, rate, cap, time);
+}
+
+/**
+ * @brief Get time in minutes how long it will take to discharge battery. Note,
+ * that rate should be negative to indicate discharging.
+ *
+ * @param bat Pointer to battery data
+ * @param rate Rate of charging current in mAh
+ * @param time Pointer to memory where calculated time will be stored
+ *
+ * @return 0 on success
+ * @return -EINVAL when over or under flow occurred
+ */
+static int sbat_emul_time_to_empty(struct sbat_emul_bat_data *bat, int rate,
+ uint16_t *time)
+{
+ int cap;
+
+ /* Reverse to have discharging rate instead of charging rate */
+ rate = -rate;
+ cap = bat->cap;
+ return sbat_emul_get_time_to_complete(bat, rate, cap, time);
+}
+
+/**
+ * @brief Check if battery can supply for 10 seconds additional power/current
+ * set in at_rate register.
+ *
+ * @param bat Pointer to battery data
+ * @param rate Rate of charging current in mAh
+ * @param ok Pointer to memory where 0 is written if battery is able to supply
+ * additional power/curent or 1 is written if battery is unable
+ * to do so.
+ *
+ * @return 0 on success
+ */
+static int sbat_emul_read_at_rate_ok(struct sbat_emul_bat_data *bat,
+ uint16_t *ok)
+{
+ int rem_time_s;
+ int rate;
+ int cap;
+
+ rate = bat->at_rate;
+ if (bat->mode & MODE_CAPACITY) {
+ rate = sbat_emul_10mw_to_ma(rate, bat->design_mv);
+ }
+
+ /* Add current battery usage */
+ rate += bat->cur;
+ if (rate >= 0) {
+ /* Battery will be charged */
+ *ok = 1;
+
+ return 0;
+ }
+ /* Reverse to have discharging rate instead of charging rate */
+ rate = -rate;
+
+ rem_time_s = bat->cap * 3600 / rate;
+ if (rem_time_s > 10) {
+ /*
+ * Battery can support 10 seconds of additional at_rate
+ * current/power
+ */
+ *ok = 1;
+ } else {
+ *ok = 0;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Get battery status. This function use emulated status register and
+ * set or clear some of the flags based on other properties of emulated
+ * smart battery. Discharge bit, capacity alarm, time alarm, fully
+ * discharged bit and error code are controlled by battery properties.
+ * Terminate charge/discharge/overcharge alarms are set only if they are
+ * set in emulated status register and battery is charging/discharging,
+ * so they are partialy controlled by emulated status register.
+ * Other bits are controlled by emulated status register
+ *
+ * @param emul Pointer to smart battery emulator
+ *
+ * @return value which equals to computed status register
+ */
+static uint16_t sbat_emul_read_status(struct i2c_emul *emul)
+{
+ uint16_t status, cap, rem_time, charge_percent;
+ struct sbat_emul_bat_data *bat;
+ struct sbat_emul_data *data;
+
+ data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+ bat = &data->bat;
+
+ status = bat->status;
+
+ /*
+ * Over charged and terminate charger alarm cannot appear when battery
+ * is not charged
+ */
+ if (bat->cur <= 0) {
+ status &= ~(STATUS_TERMINATE_CHARGE_ALARM |
+ STATUS_OVERCHARGED_ALARM);
+ status |= STATUS_DISCHARGING;
+ }
+ /* Terminate discharge alarm cannot appear when battery is charged */
+ if (bat->cur >= 0) {
+ status &= ~(STATUS_TERMINATE_DISCHARGE_ALARM |
+ STATUS_DISCHARGING);
+ }
+
+ sbat_emul_get_word_val(emul, SB_REMAINING_CAPACITY, &cap);
+ if (bat->cap_alarm && cap < bat->cap_alarm) {
+ status |= STATUS_REMAINING_CAPACITY_ALARM;
+ } else {
+ status &= ~STATUS_REMAINING_CAPACITY_ALARM;
+ }
+
+ sbat_emul_get_word_val(emul, SB_AVERAGE_TIME_TO_EMPTY, &rem_time);
+ if (bat->time_alarm && rem_time < bat->time_alarm) {
+ status |= STATUS_REMAINING_TIME_ALARM;
+ } else {
+ status &= ~STATUS_REMAINING_TIME_ALARM;
+ }
+
+ /* Unset fully discharged bit when charge is grater than 20% */
+ sbat_emul_get_word_val(emul, SB_RELATIVE_STATE_OF_CHARGE,
+ &charge_percent);
+ if (charge_percent > 20) {
+ status &= ~STATUS_FULLY_DISCHARGED;
+ } else {
+ status |= STATUS_FULLY_DISCHARGED;
+ }
+
+ status |= bat->error_code & STATUS_ERR_CODE_MASK;
+
+ return status;
+}
+
+/** Check description in emul_smart_battery.h */
+int sbat_emul_get_word_val(struct i2c_emul *emul, int cmd, uint16_t *val)
+{
+ struct sbat_emul_bat_data *bat;
+ struct sbat_emul_data *data;
+ int mode_mw;
+ int rate;
+
+ data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+ bat = &data->bat;
+ mode_mw = bat->mode & MODE_CAPACITY;
+
+ switch (cmd) {
+ case SB_MANUFACTURER_ACCESS:
+ *val = bat->mf_access;
+ return 0;
+ case SB_REMAINING_CAPACITY_ALARM:
+ *val = bat->cap_alarm;
+ return 0;
+ case SB_REMAINING_TIME_ALARM:
+ *val = bat->time_alarm;
+ return 0;
+ case SB_BATTERY_MODE:
+ *val = bat->mode;
+ return 0;
+ case SB_AT_RATE:
+ *val = bat->at_rate;
+ return 0;
+ case SB_AT_RATE_TIME_TO_FULL:
+ /* Support for reporting time to full in mW mode is optional */
+ if (mode_mw && !bat->at_rate_full_mw_support) {
+ bat->error_code = STATUS_CODE_OVERUNDERFLOW;
+ *val = UINT16_MAX;
+
+ return -EINVAL;
+ }
+
+ rate = bat->at_rate;
+ if (mode_mw) {
+ rate = sbat_emul_10mw_to_ma(rate, bat->design_mv);
+ }
+ return sbat_emul_time_to_full(bat, rate, val);
+
+ case SB_AT_RATE_TIME_TO_EMPTY:
+ rate = bat->at_rate;
+ if (mode_mw) {
+ rate = sbat_emul_10mw_to_ma(rate, bat->design_mv);
+ }
+ return sbat_emul_time_to_empty(bat, rate, val);
+
+ case SB_AT_RATE_OK:
+ return sbat_emul_read_at_rate_ok(bat, val);
+ case SB_TEMPERATURE:
+ *val = bat->temp;
+ return 0;
+ case SB_VOLTAGE:
+ *val = bat->volt;
+ return 0;
+ case SB_CURRENT:
+ *val = bat->cur;
+ return 0;
+ case SB_AVERAGE_CURRENT:
+ *val = bat->avg_cur;
+ return 0;
+ case SB_MAX_ERROR:
+ *val = bat->max_error;
+ return 0;
+ case SB_RELATIVE_STATE_OF_CHARGE:
+ /* Percent of charge according to full capacity */
+ *val = 100 * bat->cap / bat->full_cap;
+ return 0;
+ case SB_ABSOLUTE_STATE_OF_CHARGE:
+ /* Percent of charge according to design capacity */
+ *val = 100 * bat->cap / bat->design_cap;
+ return 0;
+ case SB_REMAINING_CAPACITY:
+ if (mode_mw) {
+ *val = sbat_emul_ma_to_10mw(bat->cap, bat->design_mv);
+ } else {
+ *val = bat->cap;
+ }
+ return 0;
+ case SB_FULL_CHARGE_CAPACITY:
+ if (mode_mw) {
+ *val = sbat_emul_ma_to_10mw(bat->full_cap,
+ bat->design_mv);
+ } else {
+ *val = bat->full_cap;
+ }
+ return 0;
+ case SB_RUN_TIME_TO_EMPTY:
+ rate = bat->cur;
+ return sbat_emul_time_to_empty(bat, rate, val);
+ case SB_AVERAGE_TIME_TO_EMPTY:
+ rate = bat->avg_cur;
+ return sbat_emul_time_to_empty(bat, rate, val);
+ case SB_AVERAGE_TIME_TO_FULL:
+ rate = bat->avg_cur;
+ return sbat_emul_time_to_full(bat, rate, val);
+ case SB_CHARGING_CURRENT:
+ *val = bat->desired_charg_cur;
+ return 0;
+ case SB_CHARGING_VOLTAGE:
+ *val = bat->desired_charg_volt;
+ return 0;
+ case SB_BATTERY_STATUS:
+ *val = sbat_emul_read_status(emul);
+ return 0;
+ case SB_CYCLE_COUNT:
+ *val = bat->cycle_count;
+ return 0;
+ case SB_DESIGN_CAPACITY:
+ if (mode_mw) {
+ *val = sbat_emul_ma_to_10mw(bat->design_cap,
+ bat->design_mv);
+ } else {
+ *val = bat->design_cap;
+ }
+ return 0;
+ case SB_DESIGN_VOLTAGE:
+ *val = bat->design_mv;
+ return 0;
+ case SB_SPECIFICATION_INFO:
+ *val = bat->spec_info;
+ return 0;
+ case SB_MANUFACTURE_DATE:
+ *val = bat->mf_date;
+ return 0;
+ case SB_SERIAL_NUMBER:
+ *val = bat->sn;
+ return 0;
+ default:
+ /* Unknown command or return value is not word */
+ return 1;
+ }
+}
+
+/** Check description in emul_smart_battery.h */
+int sbat_emul_get_block_data(struct i2c_emul *emul, int cmd, uint8_t **blk,
+ int *len)
+{
+ struct sbat_emul_bat_data *bat;
+ struct sbat_emul_data *data;
+
+ data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+ bat = &data->bat;
+
+ switch (cmd) {
+ case SB_MANUFACTURER_NAME:
+ *blk = bat->mf_name;
+ *len = bat->mf_name_len;
+ return 0;
+ case SB_DEVICE_NAME:
+ *blk = bat->dev_name;
+ *len = bat->dev_name_len;
+ return 0;
+ case SB_DEVICE_CHEMISTRY:
+ *blk = bat->dev_chem;
+ *len = bat->dev_chem_len;
+ return 0;
+ case SB_MANUFACTURER_DATA:
+ *blk = bat->mf_data;
+ *len = bat->mf_data_len;
+ return 0;
+ default:
+ /* Unknown command or return value is not word */
+ return 1;
+ }
+}
+
+/**
+ * @brief Append PEC to read command response if battery support it
+ *
+ * @param data Pointer to smart battery emulator data
+ */
+static void sbat_emul_append_pec(struct sbat_emul_data *data)
+{
+ uint8_t pec;
+
+ if (BATTERY_SPEC_VERSION(data->bat.spec_info) ==
+ BATTERY_SPEC_VER_1_1_WITH_PEC) {
+ pec = sbat_emul_pec_head(data->cfg->addr, 1, data->cur_cmd);
+ pec = cros_crc8_arg(data->msg_buf, data->num_to_read, pec);
+ data->msg_buf[data->num_to_read] = pec;
+ data->num_to_read++;
+ }
+}
+
+/**
+ * @brief Function which handles read messages. It expects that data->cur_cmd
+ * is set to command number which should be handled. It guarantee that
+ * data->num_to_read is set to number of bytes in data->msg_buf on
+ * successful handling read request. On error, data->num_to_read is
+ * always set to 0.
+ *
+ * @param emul Pointer to smart battery emulator
+ *
+ * @return 0 on success
+ * @return -EIO on error
+ */
+static int sbat_emul_handle_read_msg(struct i2c_emul *emul)
+{
+ struct sbat_emul_data *data;
+ uint16_t word;
+ uint8_t *blk;
+ int ret, len;
+
+ data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+
+ data->num_to_read = 0;
+ if (data->read_custom_func != NULL) {
+ ret = data->read_custom_func(emul, data->msg_buf,
+ &data->num_to_read, data->cur_cmd,
+ data->read_custom_func_data);
+ if (ret < 0) {
+ data->bat.error_code = STATUS_CODE_UNKNOWN_ERROR;
+ data->num_to_read = 0;
+
+ return -EIO;
+ } else if (ret == 0) {
+ data->bat.error_code = STATUS_CODE_OK;
+ sbat_emul_append_pec(data);
+
+ return 0;
+ }
+ }
+
+ if (data->cur_cmd == SBAT_EMUL_NO_CMD) {
+ /* Unexpected read message without preceding command select */
+ data->bat.error_code = STATUS_CODE_UNKNOWN_ERROR;
+ return -EIO;
+ }
+
+ /* Handle commands which return word */
+ ret = sbat_emul_get_word_val(emul, data->cur_cmd, &word);
+ if (ret < 0) {
+ return -EIO;
+ }
+ if (ret == 0) {
+ data->num_to_read = 2;
+ data->msg_buf[0] = word & 0xff;
+ data->msg_buf[1] = (word >> 8) & 0xff;
+ data->bat.error_code = STATUS_CODE_OK;
+ sbat_emul_append_pec(data);
+
+ return 0;
+ }
+
+ /* Handle commands which return block */
+ ret = sbat_emul_get_block_data(emul, data->cur_cmd, &blk, &len);
+ if (ret != 0) {
+ if (ret == 1) {
+ data->bat.error_code = STATUS_CODE_UNSUPPORTED;
+ LOG_ERR("Unknown read command (0x%x)", data->cur_cmd);
+ }
+
+ return -EIO;
+ }
+
+ data->num_to_read = len + 1;
+ data->msg_buf[0] = len;
+ memcpy(&data->msg_buf[1], blk, len);
+ data->bat.error_code = STATUS_CODE_OK;
+ sbat_emul_append_pec(data);
+
+ return 0;
+}
+
+/**
+ * @brief Function which finalize write messages. It expects that
+ * data->ong_write is set to number of bytes received in data->msg_buf.
+ * It guarantee that data->ong_write is set to 0.
+ *
+ * @param emul Pointer to smart battery emulator
+ *
+ * @return 0 on success
+ * @return -EIO on error
+ */
+static int sbat_emul_finalize_write_msg(struct i2c_emul *emul)
+{
+ struct sbat_emul_bat_data *bat;
+ struct sbat_emul_data *data;
+ uint16_t word;
+ uint8_t pec;
+ int ret;
+
+ data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+ bat = &data->bat;
+
+ if (data->write_custom_func != NULL) {
+ ret = data->write_custom_func(emul, data->msg_buf,
+ &data->ong_write,
+ data->msg_buf[0],
+ data->write_custom_func_data);
+ if (ret < 0) {
+ data->bat.error_code = STATUS_CODE_UNKNOWN_ERROR;
+ data->ong_write = 0;
+
+ return -EIO;
+ } else if (ret == 0) {
+ data->bat.error_code = STATUS_CODE_OK;
+ data->ong_write = 0;
+
+ return 0;
+ }
+ }
+
+ /*
+ * Fail if:
+ * - there are no bytes to handle
+ * - there are too many bytes
+ * - there is command byte and only one data byte
+ */
+ if (data->ong_write <= 0 ||
+ data->ong_write > 4 ||
+ data->ong_write == 2) {
+ data->bat.error_code = STATUS_CODE_BADSIZE;
+ data->ong_write = 0;
+ LOG_ERR("wrong write message size (%d)", data->ong_write);
+
+ return -EIO;
+ }
+
+ /* There is only command for read */
+ if (data->ong_write == 1) {
+ data->cur_cmd = data->msg_buf[0];
+ data->ong_write = 0;
+ return 0;
+ }
+
+ /* Handle PEC */
+ if (data->ong_write == 4) {
+ if (BATTERY_SPEC_VERSION(data->bat.spec_info) !=
+ BATTERY_SPEC_VER_1_1_WITH_PEC) {
+ data->bat.error_code = STATUS_CODE_BADSIZE;
+ data->ong_write = 0;
+ LOG_ERR("Unexpected PEC; No support in this version");
+
+ return -EIO;
+ }
+ pec = sbat_emul_pec_head(data->cfg->addr, 0, 0);
+ pec = cros_crc8_arg(data->msg_buf, 3, pec);
+ if (pec != data->msg_buf[3]) {
+ data->bat.error_code = STATUS_CODE_UNKNOWN_ERROR;
+ data->ong_write = 0;
+ LOG_ERR("Wrong PEC 0x%x != 0x%x",
+ pec, data->msg_buf[3]);
+
+ return -EIO;
+ }
+ }
+
+ word = ((int)data->msg_buf[2] << 8) | data->msg_buf[1];
+
+ switch (data->msg_buf[0]) {
+ case SB_MANUFACTURER_ACCESS:
+ bat->mf_access = word;
+ break;
+ case SB_REMAINING_CAPACITY_ALARM:
+ bat->cap_alarm = word;
+ break;
+ case SB_REMAINING_TIME_ALARM:
+ bat->time_alarm = word;
+ break;
+ case SB_BATTERY_MODE:
+ /* Allow to set only upper byte */
+ bat->mode &= 0xff;
+ bat->mode |= word & 0xff00;
+ break;
+ case SB_AT_RATE:
+ bat->at_rate = word;
+ break;
+ default:
+ data->ong_write = 0;
+ data->bat.error_code = STATUS_CODE_ACCESS_DENIED;
+ LOG_ERR("Unknown write command (0x%x)", data->msg_buf[0]);
+
+ return -EIO;
+ }
+
+ data->ong_write = 0;
+ data->bat.error_code = STATUS_CODE_OK;
+
+ return 0;
+}
+
+/**
+ * Emulator an I2C transfer to an smart battery
+ *
+ * This handles simple reads and writes
+ *
+ * @param emul I2C emulation information
+ * @param msgs List of messages to process. For 'read' messages, this function
+ * updates the 'buf' member with the data that was read
+ * @param num_msgs Number of messages to process
+ * @param addr Address of the I2C target device.
+ *
+ * @retval 0 If successful
+ * @retval -EIO General input / output error
+ */
+static int sbat_emul_transfer(struct i2c_emul *emul, struct i2c_msg *msgs,
+ int num_msgs, int addr)
+{
+ const struct sbat_emul_cfg *cfg;
+ struct sbat_emul_data *data;
+ unsigned int len;
+ int ret, i;
+
+ data = CONTAINER_OF(emul, struct sbat_emul_data, emul);
+ cfg = data->cfg;
+
+ if (cfg->addr != addr) {
+ LOG_ERR("Address mismatch, expected %02x, got %02x", cfg->addr,
+ addr);
+ return -EIO;
+ }
+
+ i2c_dump_msgs("emul", msgs, num_msgs, addr);
+
+ for (; num_msgs > 0; num_msgs--, msgs++) {
+ if (!(msgs->flags & I2C_MSG_READ)) {
+ /* Disscard any ongoing read */
+ data->num_to_read = 0;
+
+ /* Save incoming data to buffer */
+ for (i = 0; i < msgs->len; i++, data->ong_write++) {
+ if (data->ong_write < MSG_BUF_LEN) {
+ data->msg_buf[data->ong_write] =
+ msgs->buf[i];
+ }
+ }
+
+ /* Handle write message when we receive stop signal */
+ if (msgs->flags & I2C_MSG_STOP) {
+ k_mutex_lock(&data->bat_mtx, K_FOREVER);
+ ret = sbat_emul_finalize_write_msg(emul);
+ k_mutex_unlock(&data->bat_mtx);
+ }
+ } else {
+ /* Finalize any ongoing write message before read */
+ if (data->ong_write) {
+ k_mutex_lock(&data->bat_mtx, K_FOREVER);
+ ret = sbat_emul_finalize_write_msg(emul);
+ k_mutex_unlock(&data->bat_mtx);
+ if (ret) {
+ return -EIO;
+ }
+ }
+
+ /* Prepare read message */
+ if (!data->num_to_read) {
+ k_mutex_lock(&data->bat_mtx, K_FOREVER);
+ ret = sbat_emul_handle_read_msg(emul);
+ k_mutex_unlock(&data->bat_mtx);
+ data->cur_cmd = SBAT_EMUL_NO_CMD;
+ data->ong_read = 0;
+ }
+
+ for (i = 0; i < msgs->len; i++, data->ong_read++) {
+ if (data->ong_read >= data->num_to_read) {
+ /* We wrote everything */
+ data->num_to_read = 0;
+ break;
+ }
+ msgs->buf[i] = data->msg_buf[data->ong_read];
+ }
+
+ if (msgs->flags & I2C_MSG_STOP) {
+ /* Disscard any data that wern't read */
+ data->num_to_read = 0;
+ }
+ }
+
+ if (ret) {
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/* Device instantiation */
+
+static struct i2c_emul_api sbat_emul_api = {
+ .transfer = sbat_emul_transfer,
+};
+
+/**
+ * @brief Set up a new Smart Battery emulator
+ *
+ * This should be called for each Smart Battery device that needs to be
+ * emulated. It registers it with the I2C emulation controller.
+ *
+ * @param emul Emulation information
+ * @param parent Device to emulate
+ *
+ * @return 0 indicating success (always)
+ */
+static int sbat_emul_init(const struct emul *emul,
+ const struct device *parent)
+{
+ const struct sbat_emul_cfg *cfg = emul->cfg;
+ struct sbat_emul_data *data = cfg->data;
+ int ret;
+
+ data->emul.api = &sbat_emul_api;
+ data->emul.addr = cfg->addr;
+ data->i2c = parent;
+ data->cfg = cfg;
+ k_mutex_init(&data->bat_mtx);
+
+ ret = i2c_emul_register(parent, emul->dev_label, &data->emul);
+
+ return ret;
+}
+
+#define SMART_BATTERY_EMUL(n) \
+ static struct sbat_emul_data sbat_emul_data_##n = { \
+ .bat = { \
+ .mf_access = DT_INST_PROP(n, mf_access), \
+ .at_rate_full_mw_support = DT_INST_PROP(n, \
+ at_rate_full_mw_support), \
+ .spec_info = ((DT_ENUM_TOKEN(DT_DRV_INST(n), \
+ version) << \
+ BATTERY_SPEC_VERSION_SHIFT) & \
+ BATTERY_SPEC_VERSION_MASK) | \
+ ((DT_INST_PROP(n, vscale) << \
+ BATTERY_SPEC_VSCALE_SHIFT) & \
+ BATTERY_SPEC_VSCALE_MASK) | \
+ ((DT_INST_PROP(n, ipscale) << \
+ BATTERY_SPEC_IPSCALE_SHIFT) & \
+ BATTERY_SPEC_IPSCALE_MASK) | \
+ BATTERY_SPEC_REVISION_1, \
+ .mode = (DT_INST_PROP(n, \
+ int_charge_controller) * \
+ MODE_INTERNAL_CHARGE_CONTROLLER) | \
+ (DT_INST_PROP(n, primary_battery) * \
+ MODE_PRIMARY_BATTERY_SUPPORT), \
+ .design_mv = DT_INST_PROP(n, design_mv), \
+ .design_cap = DT_INST_PROP(n, design_cap), \
+ .temp = DT_INST_PROP(n, temperature), \
+ .volt = DT_INST_PROP(n, volt), \
+ .cur = DT_INST_PROP(n, cur), \
+ .avg_cur = DT_INST_PROP(n, avg_cur), \
+ .max_error = DT_INST_PROP(n, max_error), \
+ .cap = DT_INST_PROP(n, cap), \
+ .full_cap = DT_INST_PROP(n, full_cap), \
+ .desired_charg_cur = DT_INST_PROP(n, \
+ desired_charg_cur), \
+ .desired_charg_volt = DT_INST_PROP(n, \
+ desired_charg_volt), \
+ .cycle_count = DT_INST_PROP(n, cycle_count), \
+ .sn = DT_INST_PROP(n, serial_number), \
+ .mf_name = DT_INST_PROP(n, mf_name), \
+ .mf_name_len = sizeof( \
+ DT_INST_PROP(n, mf_name)) - 1, \
+ .mf_data = DT_INST_PROP(n, mf_data), \
+ .mf_data_len = sizeof( \
+ DT_INST_PROP(n, mf_data)) - 1, \
+ .dev_name = DT_INST_PROP(n, dev_name), \
+ .dev_name_len = sizeof( \
+ DT_INST_PROP(n, dev_name)) - 1, \
+ .dev_chem = DT_INST_PROP(n, dev_chem), \
+ .dev_chem_len = sizeof( \
+ DT_INST_PROP(n, dev_chem)) - 1, \
+ .mf_date = 0, \
+ .cap_alarm = 0, \
+ .time_alarm = 0, \
+ .at_rate = 0, \
+ .status = STATUS_INITIALIZED, \
+ .error_code = STATUS_CODE_OK, \
+ }, \
+ .cur_cmd = SBAT_EMUL_NO_CMD, \
+ .write_custom_func = NULL, \
+ .read_custom_func = NULL, \
+ }; \
+ \
+ static const struct sbat_emul_cfg sbat_emul_cfg_##n = { \
+ .i2c_label = DT_INST_BUS_LABEL(n), \
+ .data = &sbat_emul_data_##n, \
+ .addr = DT_INST_REG_ADDR(n), \
+ }; \
+ EMUL_DEFINE(sbat_emul_init, DT_DRV_INST(n), &sbat_emul_cfg_##n)
+
+DT_INST_FOREACH_STATUS_OKAY(SMART_BATTERY_EMUL)
+
+#define SMART_BATTERY_EMUL_CASE(n) \
+ case DT_INST_DEP_ORD(n): return &sbat_emul_data_##n.emul;
+
+/** Check description in emul_smart_battery.h */
+struct i2c_emul *sbat_emul_get_ptr(int ord)
+{
+ switch (ord) {
+ DT_INST_FOREACH_STATUS_OKAY(SMART_BATTERY_EMUL_CASE)
+
+ default:
+ return NULL;
+ }
+}
diff --git a/zephyr/include/emul/emul_smart_battery.h b/zephyr/include/emul/emul_smart_battery.h
new file mode 100644
index 0000000000..be5e7e4b16
--- /dev/null
+++ b/zephyr/include/emul/emul_smart_battery.h
@@ -0,0 +1,243 @@
+/* Copyright 2021 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.
+ */
+
+/**
+ * @file
+ *
+ * @brief Backend API for Smart Battery emulator
+ */
+
+#ifndef __EMUL_SMART_BATTERY_H
+#define __EMUL_SMART_BATTERY_H
+
+#include <emul.h>
+#include <drivers/i2c.h>
+#include <drivers/i2c_emul.h>
+
+/**
+ * @brief Smart Battery emulator backend API
+ * @defgroup sbat_emul Smart Battery emulator
+ * @{
+ *
+ * Smart Battery emulator handle static state of device. E.g. setting charging
+ * current will not charge battery over time. Sending periodic status messages
+ * and alarms to SMBus Host or charging voltage/current to Smart Battery Charger
+ * is not supported. Behaviour of Smart Battery emulator is application-defined.
+ * As-such, each application may
+ *
+ * - define a Device Tree overlay file to set the most of battery properties
+ * - call @ref sbat_emul_set_custom_write_func and
+ * @ref sbat_emul_set_custom_read_func to setup custom handlers for SMBus
+ * messages.
+ * - get battery properties calling @ref sbat_emul_get_bat_data Battery
+ * properties can be changed through obtained pointer. In multithread
+ * environment access to battery can be guarded by calling
+ * @ref sbat_emul_lock_bat_data and @ref sbat_emul_unlock_bat_data
+ */
+
+/* Value used to indicate that no command is selected */
+#define SBAT_EMUL_NO_CMD -1
+/* Maximum size of data that can be returned in SMBus block transaction */
+#define MAX_BLOCK_SIZE 32
+/* Maximum length of command to send is maximum size of data + len byte + PEC */
+#define MSG_BUF_LEN (MAX_BLOCK_SIZE + 2)
+
+/** @brief Emulated smart battery properties */
+struct sbat_emul_bat_data {
+ /** Battery mode - bit field configuring some battery behaviours */
+ uint16_t mode;
+ /** Word returned on manufacturer access command */
+ uint16_t mf_access;
+ /** Capacity alarm value */
+ uint16_t cap_alarm;
+ /** Remaing time alarm value */
+ uint16_t time_alarm;
+ /** Rate of charge used in some commands */
+ int16_t at_rate;
+ /**
+ * Flag indicating if AT_RATE_TIME_TO_FULL command supports mW
+ * capacity mode
+ */
+ int at_rate_full_mw_support;
+ /** Error code returned by last command */
+ uint16_t error_code;
+ /** Design battery voltage in mV */
+ uint16_t design_mv;
+ /** Battery temperature at the moment in Kelvins */
+ uint16_t temp;
+ /** Battery voltage at the moment in mV */
+ uint16_t volt;
+ /** Current charging (> 0) or discharging (< 0) battery in mA */
+ int16_t cur;
+ /** Average current from 1 minute */
+ int16_t avg_cur;
+ /** Maximum error of returned values in percent */
+ uint16_t max_error;
+ /** Capacity of the battery at the moment in mAh */
+ uint16_t cap;
+ /** Full capacity of the battery in mAh */
+ uint16_t full_cap;
+ /** Design battery capacity in mAh */
+ uint16_t design_cap;
+ /** Charging current requested by battery */
+ uint16_t desired_charg_cur;
+ /** Charging voltage requested by battery */
+ uint16_t desired_charg_volt;
+ /** Number of cycles */
+ uint16_t cycle_count;
+ /** Specification of battery */
+ uint16_t spec_info;
+ /** Status of battery */
+ uint16_t status;
+ /** Date of manufacturing */
+ uint16_t mf_date;
+ /** Serial number */
+ uint16_t sn;
+ /** Manufacturer name */
+ uint8_t mf_name[MAX_BLOCK_SIZE];
+ /** Manufacturer name length */
+ int mf_name_len;
+ /** Device name */
+ uint8_t dev_name[MAX_BLOCK_SIZE];
+ /** Device name length */
+ int dev_name_len;
+ /** Device chemistry */
+ uint8_t dev_chem[MAX_BLOCK_SIZE];
+ /** Device chemistry length */
+ int dev_chem_len;
+ /** Manufacturer data */
+ uint8_t mf_data[MAX_BLOCK_SIZE];
+ /** Manufacturer data length */
+ int mf_data_len;
+};
+
+/**
+ * @brief Get pointer to smart battery emulator using device tree order number.
+ *
+ * @param ord Device tree order number obtained from DT_DEP_ORD macro
+ *
+ * @return Pointer to smart battery emulator
+ */
+struct i2c_emul *sbat_emul_get_ptr(int ord);
+
+/**
+ * @brief Custom function type that is used as user defined callbacks in read
+ * and write SMBus messages handling.
+ *
+ * @param emul Pointer to smart battery emulator
+ * @param buf Pointer to data from write command or to be filed by read command
+ * @param len Pointer to number of bytes used for write command buffer. It may
+ * exceed MSG_BUF_LEN, indicating that some bytes from write command
+ * are not saved in @p buf. If read command is handled, than
+ * function should set how many bytes are written to @p buf
+ * @param cmd Command that was recognized
+ * @param data Pointer to custom user data
+ *
+ * @return 0 on success
+ * @return 1 continue with normal smart battery emulator handler
+ * @return negative on error
+ */
+typedef int (*sbat_emul_custom_func)(struct i2c_emul *emul, uint8_t *buf,
+ int *len, int cmd, void *data);
+
+/**
+ * @brief Function which allows to get properties of emulated smart battery
+ *
+ * @param emul Pointer to smart battery emulator
+ *
+ * @return Pointer to smart battery properties
+ */
+struct sbat_emul_bat_data *sbat_emul_get_bat_data(struct i2c_emul *emul);
+
+/**
+ * @brief Lock access to smart battery properties. After acquiring lock, user
+ * may change emulator behaviour in multi-thread setup.
+ *
+ * @param emul Pointer to smart battery emulator
+ * @param timeout Timeout in getting lock
+ *
+ * @return k_mutex_lock return code
+ */
+int sbat_emul_lock_bat_data(struct i2c_emul *emul, k_timeout_t timeout);
+
+/**
+ * @brief Unlock access to smart battery properties.
+ *
+ * @param emul Pointer to smart battery emulator
+ *
+ * @return k_mutex_unlock return code
+ */
+int sbat_emul_unlock_bat_dat(struct i2c_emul *emul);
+
+/**
+ * @brief Set custom write SMBus message handler. This function is called before
+ * generic handler.
+ *
+ * @param emul Pointer to smart battery emulator
+ * @param func Pointer to custom function
+ * @param data User data passed on call of custom function
+ */
+void sbat_emul_set_custom_write_func(struct i2c_emul *emul,
+ sbat_emul_custom_func func, void *data);
+
+/**
+ * @brief Set custom read SMBus message handler. This function is called before
+ * generic handler.
+ *
+ * @param emul Pointer to smart battery emulator
+ * @param func Pointer to custom function
+ * @param data User data passed on call of custom function
+ */
+void sbat_emul_set_custom_read_func(struct i2c_emul *emul,
+ sbat_emul_custom_func func, void *data);
+
+/**
+ * @brief Convert date to format used by smart battery
+ *
+ * @param day Day
+ * @param month Month
+ * @param year Year
+ *
+ * @return Converted date
+ */
+uint16_t sbat_emul_date_to_word(unsigned int day, unsigned int month,
+ unsigned int year);
+
+/**
+ * @brief Function which gets return value for read commands that returns word.
+ * This function may be used to obtain battery properties that are
+ * calculated e.g. time to empty/full.
+ *
+ * @param emul Pointer to smart battery emulator
+ * @param cmd Read command
+ * @param val Pointer to where word should be stored
+ *
+ * @return 0 on success
+ * @return 1 if command is unknown or return type different then word
+ * @return negative on error while reading value
+ */
+int sbat_emul_get_word_val(struct i2c_emul *emul, int cmd, uint16_t *val);
+
+/**
+ * @brief Function which gets return value for read commands that returns block
+ * data
+ *
+ * @param emul Pointer to smart battery emulator
+ * @param cmd Read command
+ * @param blk Pointer to where data pointer should be stored
+ * @param len Pointer to where data length should be stored
+ *
+ * @return 0 on success
+ * @return 1 if command is unknown or return type different then word
+ * @return negative on error while reading value
+ */
+int sbat_emul_get_block_data(struct i2c_emul *emul, int cmd, uint8_t **blk,
+ int *len);
+
+/**
+ * @}
+ */
+
+#endif /* __EMUL_SMART_BATTERY_H */