1 /*
2  * Copyright (c) 2023 FTP Technologies
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT current_sense_amplifier
8 
9 #include <stdlib.h>
10 
11 #include <zephyr/drivers/adc.h>
12 #include <zephyr/drivers/adc/current_sense_amplifier.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <zephyr/drivers/sensor.h>
15 #include <zephyr/pm/device.h>
16 #include <zephyr/sys/__assert.h>
17 
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(current_amp, CONFIG_SENSOR_LOG_LEVEL);
20 
21 struct current_sense_amplifier_data {
22 	struct adc_sequence sequence;
23 	int16_t raw;
24 };
25 
fetch(const struct device * dev,enum sensor_channel chan)26 static int fetch(const struct device *dev, enum sensor_channel chan)
27 {
28 	const struct current_sense_amplifier_dt_spec *config = dev->config;
29 	struct current_sense_amplifier_data *data = dev->data;
30 	int ret;
31 
32 	if ((chan != SENSOR_CHAN_CURRENT) && (chan != SENSOR_CHAN_ALL)) {
33 		return -ENOTSUP;
34 	}
35 
36 	ret = adc_read_dt(&config->port, &data->sequence);
37 	if (ret != 0) {
38 		LOG_ERR("adc_read: %d", ret);
39 	}
40 
41 	return ret;
42 }
43 
get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)44 static int get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val)
45 {
46 	const struct current_sense_amplifier_dt_spec *config = dev->config;
47 	struct current_sense_amplifier_data *data = dev->data;
48 	int32_t raw_val = data->raw;
49 	int32_t i_ma;
50 	int ret;
51 
52 	__ASSERT_NO_MSG(val != NULL);
53 
54 	if (chan != SENSOR_CHAN_CURRENT) {
55 		return -ENOTSUP;
56 	}
57 
58 	if (abs(raw_val) < config->noise_threshold) {
59 		return sensor_value_from_micro(val, 0);
60 	}
61 
62 	ret = adc_raw_to_millivolts_dt(&config->port, &raw_val);
63 	if (ret != 0) {
64 		LOG_ERR("raw_to_mv: %d", ret);
65 		return ret;
66 	}
67 
68 	i_ma = raw_val;
69 	current_sense_amplifier_scale_dt(config, &i_ma);
70 
71 	LOG_DBG("%d/%d, %dmV, current:%dmA", data->raw,
72 		(1 << data->sequence.resolution) - 1, raw_val, i_ma);
73 
74 	val->val1 = i_ma / 1000;
75 	val->val2 = (i_ma % 1000) * 1000;
76 
77 	return 0;
78 }
79 
80 static DEVICE_API(sensor, current_api) = {
81 	.sample_fetch = fetch,
82 	.channel_get = get,
83 };
84 
85 #ifdef CONFIG_PM_DEVICE
pm_action(const struct device * dev,enum pm_device_action action)86 static int pm_action(const struct device *dev, enum pm_device_action action)
87 {
88 	const struct current_sense_amplifier_dt_spec *config = dev->config;
89 	int ret;
90 
91 	if (config->power_gpio.port == NULL) {
92 		LOG_ERR("PM not supported");
93 		return -ENOTSUP;
94 	}
95 
96 	switch (action) {
97 	case PM_DEVICE_ACTION_RESUME:
98 		ret = gpio_pin_set_dt(&config->power_gpio, 1);
99 		if (ret != 0) {
100 			LOG_ERR("failed to set GPIO for PM resume");
101 			return ret;
102 		}
103 		break;
104 	case PM_DEVICE_ACTION_SUSPEND:
105 		ret = gpio_pin_set_dt(&config->power_gpio, 0);
106 		if (ret != 0) {
107 			LOG_ERR("failed to set GPIO for PM suspend");
108 			return ret;
109 		}
110 		break;
111 	default:
112 		return -ENOTSUP;
113 	}
114 
115 	return 0;
116 }
117 #endif
118 
current_init(const struct device * dev)119 static int current_init(const struct device *dev)
120 {
121 	const struct current_sense_amplifier_dt_spec *config = dev->config;
122 	struct current_sense_amplifier_data *data = dev->data;
123 	int ret;
124 
125 	__ASSERT(config->sense_milli_ohms != 0, "Milli-ohms must not be 0");
126 
127 	if (!adc_is_ready_dt(&config->port)) {
128 		LOG_ERR("ADC is not ready");
129 		return -ENODEV;
130 	}
131 
132 #ifdef CONFIG_PM_DEVICE
133 	if (config->power_gpio.port != NULL) {
134 		if (!gpio_is_ready_dt(&config->power_gpio)) {
135 			LOG_ERR("Power GPIO is not ready");
136 			return -ENODEV;
137 		}
138 
139 		ret = gpio_pin_configure_dt(&config->power_gpio, GPIO_OUTPUT_ACTIVE);
140 		if (ret != 0) {
141 			LOG_ERR("failed to config GPIO: %d", ret);
142 			return ret;
143 		}
144 	}
145 #endif
146 
147 	ret = adc_channel_setup_dt(&config->port);
148 	if (ret != 0) {
149 		LOG_ERR("setup: %d", ret);
150 		return ret;
151 	}
152 
153 	ret = adc_sequence_init_dt(&config->port, &data->sequence);
154 	if (ret != 0) {
155 		LOG_ERR("sequence init: %d", ret);
156 		return ret;
157 	}
158 
159 	data->sequence.buffer = &data->raw;
160 	data->sequence.buffer_size = sizeof(data->raw);
161 	data->sequence.calibrate = config->enable_calibration;
162 
163 	return 0;
164 }
165 
166 #define CURRENT_SENSE_AMPLIFIER_INIT(inst)                                                         \
167 	static struct current_sense_amplifier_data current_amp_##inst##_data;                      \
168                                                                                                    \
169 	static const struct current_sense_amplifier_dt_spec current_amp_##inst##_config =          \
170 		CURRENT_SENSE_AMPLIFIER_DT_SPEC_GET(DT_DRV_INST(inst));                            \
171                                                                                                    \
172 	PM_DEVICE_DT_INST_DEFINE(inst, pm_action);                                                 \
173                                                                                                    \
174 	SENSOR_DEVICE_DT_INST_DEFINE(inst, &current_init, PM_DEVICE_DT_INST_GET(inst),             \
175 				     &current_amp_##inst##_data, &current_amp_##inst##_config,     \
176 				     POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, &current_api);      \
177                                                                                                    \
178 	BUILD_ASSERT((DT_INST_PROP(inst, zero_current_voltage_mv) == 0) ||                         \
179 			     (DT_INST_PROP(inst, sense_resistor_milli_ohms) == 1),                 \
180 		     "zero_current_voltage_mv requires sense_resistor_milli_ohms == 1");
181 
182 DT_INST_FOREACH_STATUS_OKAY(CURRENT_SENSE_AMPLIFIER_INIT)
183