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