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