summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 */