1 /*
2  * Copyright (c) 2024, Embeint Inc
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT zephyr_fuel_gauge_composite
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/fuel_gauge.h>
11 #include <zephyr/drivers/sensor.h>
12 #include <zephyr/drivers/sensor/battery.h>
13 #include <zephyr/pm/device_runtime.h>
14 #include <zephyr/kernel.h>
15 
16 struct composite_config {
17 	const struct device *battery_voltage;
18 	const struct device *battery_current;
19 	int32_t ocv_lookup_table[BATTERY_OCV_TABLE_LEN];
20 	uint32_t charge_capacity_microamp_hours;
21 	enum battery_chemistry chemistry;
22 };
23 
composite_read_micro(const struct device * dev,enum sensor_channel chan,int * val)24 static int composite_read_micro(const struct device *dev, enum sensor_channel chan, int *val)
25 {
26 	struct sensor_value sensor_val;
27 	int rc;
28 
29 	rc = pm_device_runtime_get(dev);
30 	if (rc < 0) {
31 		return rc;
32 	}
33 	rc = sensor_sample_fetch(dev);
34 	if (rc < 0) {
35 		return rc;
36 	}
37 	rc = sensor_channel_get(dev, chan, &sensor_val);
38 	if (rc < 0) {
39 		return rc;
40 	}
41 	rc = pm_device_runtime_put(dev);
42 	if (rc < 0) {
43 		return rc;
44 	}
45 	*val = sensor_value_to_micro(&sensor_val);
46 	return 0;
47 }
48 
composite_get_prop(const struct device * dev,fuel_gauge_prop_t prop,union fuel_gauge_prop_val * val)49 static int composite_get_prop(const struct device *dev, fuel_gauge_prop_t prop,
50 			      union fuel_gauge_prop_val *val)
51 {
52 	const struct composite_config *config = dev->config;
53 	int voltage, rc = 0;
54 
55 	switch (prop) {
56 	case FUEL_GAUGE_FULL_CHARGE_CAPACITY:
57 		if (config->charge_capacity_microamp_hours == 0) {
58 			return -ENOTSUP;
59 		}
60 		val->full_charge_capacity = config->charge_capacity_microamp_hours;
61 		break;
62 	case FUEL_GAUGE_DESIGN_CAPACITY:
63 		if (config->charge_capacity_microamp_hours == 0) {
64 			return -ENOTSUP;
65 		}
66 		val->full_charge_capacity = config->charge_capacity_microamp_hours / 1000;
67 		break;
68 	case FUEL_GAUGE_VOLTAGE:
69 		rc = composite_read_micro(config->battery_voltage, SENSOR_CHAN_VOLTAGE,
70 					  &val->voltage);
71 		break;
72 	case FUEL_GAUGE_ABSOLUTE_STATE_OF_CHARGE:
73 	case FUEL_GAUGE_RELATIVE_STATE_OF_CHARGE:
74 		if (config->ocv_lookup_table[0] == -1) {
75 			return -ENOTSUP;
76 		}
77 		rc = composite_read_micro(config->battery_voltage, SENSOR_CHAN_VOLTAGE, &voltage);
78 		val->relative_state_of_charge =
79 			battery_soc_lookup(config->ocv_lookup_table, voltage) / 1000;
80 		break;
81 	case FUEL_GAUGE_CURRENT:
82 		if (config->battery_current == NULL) {
83 			return -ENOTSUP;
84 		}
85 		rc = composite_read_micro(config->battery_current, SENSOR_CHAN_CURRENT,
86 					  &val->current);
87 		break;
88 	default:
89 		return -ENOTSUP;
90 	}
91 
92 	return 0;
93 }
94 
95 static DEVICE_API(fuel_gauge, composite_api) = {
96 	.get_property = composite_get_prop,
97 };
98 
99 #define COMPOSITE_INIT(inst)                                                                       \
100 	static const struct composite_config composite_##inst##_config = {                         \
101 		.battery_voltage = DEVICE_DT_GET(DT_INST_PROP(inst, battery_voltage)),             \
102 		.battery_current = DEVICE_DT_GET_OR_NULL(DT_INST_PROP(inst, battery_current)),     \
103 		.ocv_lookup_table =                                                                \
104 			BATTERY_OCV_TABLE_DT_GET(DT_DRV_INST(inst), ocv_capacity_table_0),         \
105 		.charge_capacity_microamp_hours =                                                  \
106 			DT_INST_PROP_OR(inst, charge_full_design_microamp_hours, 0),               \
107 		.chemistry = BATTERY_CHEMISTRY_DT_GET(inst),                                       \
108 	};                                                                                         \
109 	DEVICE_DT_INST_DEFINE(inst, NULL, NULL, NULL, &composite_##inst##_config, POST_KERNEL,     \
110 			      CONFIG_SENSOR_INIT_PRIORITY, &composite_api);
111 
112 DT_INST_FOREACH_STATUS_OKAY(COMPOSITE_INIT)
113