/*
 * Copyright 2022 Google LLC
 * Copyright 2023 Microsoft Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#ifndef ZEPHYR_INCLUDE_DRIVERS_BATTERY_H_
#define ZEPHYR_INCLUDE_DRIVERS_BATTERY_H_

/**
 * @brief Fuel Gauge Interface
 * @defgroup fuel_gauge_interface Fuel Gauge Interface
 * @since 3.3
 * @version 0.1.0
 * @ingroup io_interfaces
 * @{
 */

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#include <zephyr/device.h>

enum fuel_gauge_prop_type {
	/** Runtime Dynamic Battery Parameters */
	/**
	 * Provide a 1 minute average of the current on the battery.
	 * Does not check for flags or whether those values are bad readings.
	 * See driver instance header for details on implementation and
	 * how the average is calculated. Units in uA negative=discharging
	 */
	FUEL_GAUGE_AVG_CURRENT = 0,

	/** Used to cutoff the battery from the system - useful for storage/shipping of devices */
	FUEL_GAUGE_BATTERY_CUTOFF,
	/** Battery current (uA); negative=discharging */
	FUEL_GAUGE_CURRENT,
	/** Whether the battery underlying the fuel-gauge is cut off from charge */
	FUEL_GAUGE_CHARGE_CUTOFF,
	/** Cycle count in 1/100ths (number of charge/discharge cycles) */
	FUEL_GAUGE_CYCLE_COUNT,
	/** Connect state of battery */
	FUEL_GAUGE_CONNECT_STATE,
	/** General Error/Runtime Flags */
	FUEL_GAUGE_FLAGS,
	/** Full Charge Capacity in uAh (might change in some implementations to determine wear) */
	FUEL_GAUGE_FULL_CHARGE_CAPACITY,
	/** Is the battery physically present */
	FUEL_GAUGE_PRESENT_STATE,
	/** Remaining capacity in uAh */
	FUEL_GAUGE_REMAINING_CAPACITY,
	/** Remaining battery life time in minutes */
	FUEL_GAUGE_RUNTIME_TO_EMPTY,
	/** Remaining time in minutes until battery reaches full charge */
	FUEL_GAUGE_RUNTIME_TO_FULL,
	/** Retrieve word from SBS1.1 ManufactuerAccess */
	FUEL_GAUGE_SBS_MFR_ACCESS,
	/** Absolute state of charge (percent, 0-100) - expressed as % of design capacity */
	FUEL_GAUGE_ABSOLUTE_STATE_OF_CHARGE,
	/** Relative state of charge (percent, 0-100) - expressed as % of full charge capacity */
	FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE,
	/** Temperature in 0.1 K */
	FUEL_GAUGE_TEMPERATURE,
	/** Battery voltage (uV) */
	FUEL_GAUGE_VOLTAGE,
	/** Battery Mode (flags) */
	FUEL_GAUGE_SBS_MODE,
	/** Battery desired Max Charging Current (uA) */
	FUEL_GAUGE_CHARGE_CURRENT,
	/** Battery desired Max Charging Voltage (uV) */
	FUEL_GAUGE_CHARGE_VOLTAGE,
	/** Alarm, Status and Error codes (flags) */
	FUEL_GAUGE_STATUS,
	/** Design Capacity (mAh or 10mWh) */
	FUEL_GAUGE_DESIGN_CAPACITY,
	/** Design Voltage (mV) */
	FUEL_GAUGE_DESIGN_VOLTAGE,
	/** AtRate (mA or 10 mW) */
	FUEL_GAUGE_SBS_ATRATE,
	/** AtRateTimeToFull (minutes) */
	FUEL_GAUGE_SBS_ATRATE_TIME_TO_FULL,
	/** AtRateTimeToEmpty (minutes) */
	FUEL_GAUGE_SBS_ATRATE_TIME_TO_EMPTY,
	/** AtRateOK (boolean) */
	FUEL_GAUGE_SBS_ATRATE_OK,
	/** Remaining Capacity Alarm (mAh or 10mWh) */
	FUEL_GAUGE_SBS_REMAINING_CAPACITY_ALARM,
	/** Remaining Time Alarm (minutes) */
	FUEL_GAUGE_SBS_REMAINING_TIME_ALARM,
	/** Manufacturer of pack (1 byte length + 20 bytes data) */
	FUEL_GAUGE_MANUFACTURER_NAME,
	/** Name of pack (1 byte length + 20 bytes data) */
	FUEL_GAUGE_DEVICE_NAME,
	/** Chemistry (1 byte length + 4 bytes data) */
	FUEL_GAUGE_DEVICE_CHEMISTRY,

	/** Reserved to demark end of common fuel gauge properties */
	FUEL_GAUGE_COMMON_COUNT,
	/**
	 * Reserved to demark downstream custom properties - use this value as the actual value may
	 * change over future versions of this API
	 */
	FUEL_GAUGE_CUSTOM_BEGIN,

	/** Reserved to demark end of valid enum properties */
	FUEL_GAUGE_PROP_MAX = UINT16_MAX,
};

typedef uint16_t fuel_gauge_prop_t;

/** Property field to value/type union */
union fuel_gauge_prop_val {
	/* Fields have the format: */
	/* FUEL_GAUGE_PROPERTY_FIELD */
	/* type property_field; */

	/* Dynamic Battery Info */
	/** FUEL_GAUGE_AVG_CURRENT */
	int avg_current;
	/** FUEL_GAUGE_CHARGE_CUTOFF */
	bool cutoff;
	/** FUEL_GAUGE_CURRENT */
	int current;
	/** FUEL_GAUGE_CYCLE_COUNT */
	uint32_t cycle_count;
	/** FUEL_GAUGE_FLAGS */
	uint32_t flags;
	/** FUEL_GAUGE_FULL_CHARGE_CAPACITY */
	uint32_t full_charge_capacity;
	/** FUEL_GAUGE_REMAINING_CAPACITY */
	uint32_t remaining_capacity;
	/** FUEL_GAUGE_RUNTIME_TO_EMPTY */
	uint32_t runtime_to_empty;
	/** FUEL_GAUGE_RUNTIME_TO_FULL */
	uint32_t runtime_to_full;
	/** FUEL_GAUGE_SBS_MFR_ACCESS */
	uint16_t sbs_mfr_access_word;
	/** FUEL_GAUGE_ABSOLUTE_STATE_OF_CHARGE */
	uint8_t absolute_state_of_charge;
	/** FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE */
	uint8_t relative_state_of_charge;
	/** FUEL_GAUGE_TEMPERATURE */
	uint16_t temperature;
	/** FUEL_GAUGE_VOLTAGE */
	int voltage;
	/** FUEL_GAUGE_SBS_MODE */
	uint16_t sbs_mode;
	/** FUEL_GAUGE_CHARGE_CURRENT */
	uint32_t chg_current;
	/** FUEL_GAUGE_CHARGE_VOLTAGE */
	uint32_t chg_voltage;
	/** FUEL_GAUGE_STATUS */
	uint16_t fg_status;
	/** FUEL_GAUGE_DESIGN_CAPACITY */
	uint16_t design_cap;
	/** FUEL_GAUGE_DESIGN_VOLTAGE */
	uint16_t design_volt;
	/** FUEL_GAUGE_SBS_ATRATE */
	int16_t sbs_at_rate;
	/** FUEL_GAUGE_SBS_ATRATE_TIME_TO_FULL */
	uint16_t sbs_at_rate_time_to_full;
	/** FUEL_GAUGE_SBS_ATRATE_TIME_TO_EMPTY	*/
	uint16_t sbs_at_rate_time_to_empty;
	/** FUEL_GAUGE_SBS_ATRATE_OK */
	bool sbs_at_rate_ok;
	/** FUEL_GAUGE_SBS_REMAINING_CAPACITY_ALARM */
	uint16_t sbs_remaining_capacity_alarm;
	/** FUEL_GAUGE_SBS_REMAINING_TIME_ALARM */
	uint16_t sbs_remaining_time_alarm;
};

/**
 * Data structures for reading SBS buffer properties
 */
#define SBS_GAUGE_MANUFACTURER_NAME_MAX_SIZE 20
#define SBS_GAUGE_DEVICE_NAME_MAX_SIZE       20
#define SBS_GAUGE_DEVICE_CHEMISTRY_MAX_SIZE  4

struct sbs_gauge_manufacturer_name {
	uint8_t manufacturer_name_length;
	char manufacturer_name[SBS_GAUGE_MANUFACTURER_NAME_MAX_SIZE];
} __packed;

struct sbs_gauge_device_name {
	uint8_t device_name_length;
	char device_name[SBS_GAUGE_DEVICE_NAME_MAX_SIZE];
} __packed;

struct sbs_gauge_device_chemistry {
	uint8_t device_chemistry_length;
	char device_chemistry[SBS_GAUGE_DEVICE_CHEMISTRY_MAX_SIZE];
} __packed;

/**
 * @typedef fuel_gauge_get_property_t
 * @brief Callback API for getting a fuel_gauge property.
 *
 * See fuel_gauge_get_property() for argument description
 */
typedef int (*fuel_gauge_get_property_t)(const struct device *dev, fuel_gauge_prop_t prop,
					 union fuel_gauge_prop_val *val);

/**
 * @typedef fuel_gauge_set_property_t
 * @brief Callback API for setting a fuel_gauge property.
 *
 * See fuel_gauge_set_property() for argument description
 */
typedef int (*fuel_gauge_set_property_t)(const struct device *dev, fuel_gauge_prop_t prop,
					 union fuel_gauge_prop_val val);

/**
 * @typedef fuel_gauge_get_buffer_property_t
 * @brief Callback API for getting a fuel_gauge buffer property.
 *
 * See fuel_gauge_get_buffer_property() for argument description
 */
typedef int (*fuel_gauge_get_buffer_property_t)(const struct device *dev,
					       fuel_gauge_prop_t prop_type,
					       void *dst, size_t dst_len);

/**
 * @typedef fuel_gauge_battery_cutoff_t
 * @brief Callback API for doing a battery cutoff.
 *
 * See fuel_gauge_battery_cutoff() for argument description
 */
typedef int (*fuel_gauge_battery_cutoff_t)(const struct device *dev);

/* Caching is entirely on the onus of the client */

__subsystem struct fuel_gauge_driver_api {
	/**
	 * Note: Historically this API allowed drivers to implement a custom multi-get/set property
	 * function, this was added so drivers could potentially optimize batch read with their
	 * specific chip. However, it was removed because of no existing concrete case upstream.
	 * If this need is demonstrated, we can add this back in as an API field.
	 */
	fuel_gauge_get_property_t get_property;
	fuel_gauge_set_property_t set_property;
	fuel_gauge_get_buffer_property_t get_buffer_property;
	fuel_gauge_battery_cutoff_t battery_cutoff;
};

/**
 * @brief Fetch a battery fuel-gauge property
 *
 * @param dev Pointer to the battery fuel-gauge device
 * @param prop Type of property to be fetched from device
 * @param val pointer to a union fuel_gauge_prop_val where the property is read into from the
 * fuel gauge device.
 * @return 0 if successful, negative errno code if failure.
 */
__syscall int fuel_gauge_get_prop(const struct device *dev, fuel_gauge_prop_t prop,
				  union fuel_gauge_prop_val *val);

static inline int z_impl_fuel_gauge_get_prop(const struct device *dev, fuel_gauge_prop_t prop,
					     union fuel_gauge_prop_val *val)
{
	const struct fuel_gauge_driver_api *api = (const struct fuel_gauge_driver_api *)dev->api;

	if (api->get_property == NULL) {
		return -ENOSYS;
	}

	return api->get_property(dev, prop, val);
}

/**
 * @brief Fetch multiple battery fuel-gauge properies. The default implementation is the same as
 * calling fuel_gauge_get_prop() multiple times. A driver may implement the `get_properties` field
 * of the fuel gauge driver APIs struct to override this implementation.
 *
 * @param dev Pointer to the battery fuel-gauge device
 * @param props Array of the type of property to be fetched from device, each index corresponds
 * to the same index of the vals input array.
 * @param vals Pointer to array of union fuel_gauge_prop_val where the property is read into from
 * the fuel gauge device. The vals array is not permuted.
 * @param len number of properties in props & vals array
 *
 * @return 0 if successful, negative errno code of first failing property
 */

__syscall int fuel_gauge_get_props(const struct device *dev, fuel_gauge_prop_t *props,
				   union fuel_gauge_prop_val *vals, size_t len);
static inline int z_impl_fuel_gauge_get_props(const struct device *dev,
					      fuel_gauge_prop_t *props,
					      union fuel_gauge_prop_val *vals, size_t len)
{
	const struct fuel_gauge_driver_api *api = dev->api;

	for (int i = 0; i < len; i++) {
		int ret = api->get_property(dev, props[i], vals + i);

		if (ret) {
			return ret;
		}
	}

	return 0;
}

/**
 * @brief Set a battery fuel-gauge property
 *
 * @param dev Pointer to the battery fuel-gauge device
 * @param prop Type of property that's being set
 * @param val Value to set associated prop property.
 *
 * @return 0 if successful, negative errno code of first failing property
 */
__syscall int fuel_gauge_set_prop(const struct device *dev, fuel_gauge_prop_t prop,
				  union fuel_gauge_prop_val val);

static inline int z_impl_fuel_gauge_set_prop(const struct device *dev, fuel_gauge_prop_t prop,
					     union fuel_gauge_prop_val val)
{
	const struct fuel_gauge_driver_api *api = dev->api;

	if (api->set_property == NULL) {
		return -ENOSYS;
	}

	return api->set_property(dev, prop, val);
}
/**
 * @brief Set a battery fuel-gauge property
 *
 * @param dev Pointer to the battery fuel-gauge device
 * @param props Array of the type of property to be set, each index corresponds
 * to the same index of the vals input array.
 * @param vals Pointer to array of union fuel_gauge_prop_val where the property is written
 * the fuel gauge device. The vals array is not permuted.
 * @param len number of properties in props array
 *
 * @return return=0 if successful. Otherwise, return array index of failing property.
 */
__syscall int fuel_gauge_set_props(const struct device *dev, fuel_gauge_prop_t *props,
				   union fuel_gauge_prop_val *vals, size_t len);

static inline int z_impl_fuel_gauge_set_props(const struct device *dev,
					      fuel_gauge_prop_t *props,
					      union fuel_gauge_prop_val *vals, size_t len)
{
	for (int i = 0; i < len; i++) {
		int ret = fuel_gauge_set_prop(dev, props[i], vals[i]);

		if (ret) {
			return ret;
		}
	}

	return 0;
}

/**
 * @brief Fetch a battery fuel-gauge buffer property
 *
 * @param dev Pointer to the battery fuel-gauge device
 * @param prop_type Type of property to be fetched from device
 * @param dst byte array or struct that will hold the buffer data that is read from the fuel gauge
 * @param dst_len the length of the destination array in bytes
 *
 * @return return=0 if successful, return < 0 if getting property failed, return 0 on success
 */

__syscall int fuel_gauge_get_buffer_prop(const struct device *dev, fuel_gauge_prop_t prop_type,
					 void *dst, size_t dst_len);

static inline int z_impl_fuel_gauge_get_buffer_prop(const struct device *dev,
						   fuel_gauge_prop_t prop_type,
						   void *dst, size_t dst_len)
{
	const struct fuel_gauge_driver_api *api = (const struct fuel_gauge_driver_api *)dev->api;

	if (api->get_buffer_property == NULL) {
		return -ENOSYS;
	}

	return api->get_buffer_property(dev, prop_type, dst, dst_len);
}

/**
 * @brief Have fuel gauge cutoff its associated battery.
 *
 * @param dev Pointer to the battery fuel-gauge device
 *
 * @return return=0 if successful and battery cutoff is now in process, return < 0 if failed to do
 * battery cutoff.
 */
__syscall int fuel_gauge_battery_cutoff(const struct device *dev);

static inline int z_impl_fuel_gauge_battery_cutoff(const struct device *dev)
{
	const struct fuel_gauge_driver_api *api = (const struct fuel_gauge_driver_api *)dev->api;

	if (api->battery_cutoff == NULL) {
		return -ENOSYS;
	}

	return api->battery_cutoff(dev);
}

/**
 * @}
 */

#ifdef __cplusplus
}
#endif /* __cplusplus */

#include <syscalls/fuel_gauge.h>

#endif /* ZEPHYR_INCLUDE_DRIVERS_BATTERY_H_ */
