| /* |
| * Copyright (c) 2023 FTP Technologies |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT current_sense_amplifier |
| |
| #include <zephyr/drivers/adc.h> |
| #include <zephyr/drivers/adc/current_sense_amplifier.h> |
| #include <zephyr/drivers/gpio.h> |
| #include <zephyr/drivers/sensor.h> |
| #include <zephyr/pm/device.h> |
| |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(current_amp, CONFIG_SENSOR_LOG_LEVEL); |
| |
| struct current_sense_amplifier_data { |
| struct adc_sequence sequence; |
| int16_t raw; |
| }; |
| |
| static int fetch(const struct device *dev, enum sensor_channel chan) |
| { |
| const struct current_sense_amplifier_dt_spec *config = dev->config; |
| struct current_sense_amplifier_data *data = dev->data; |
| int ret; |
| |
| if ((chan != SENSOR_CHAN_CURRENT) && (chan != SENSOR_CHAN_ALL)) { |
| return -ENOTSUP; |
| } |
| |
| ret = adc_read_dt(&config->port, &data->sequence); |
| if (ret != 0) { |
| LOG_ERR("adc_read: %d", ret); |
| } |
| |
| return ret; |
| } |
| |
| static int get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val) |
| { |
| const struct current_sense_amplifier_dt_spec *config = dev->config; |
| struct current_sense_amplifier_data *data = dev->data; |
| int32_t raw_val = data->raw; |
| int32_t i_ma; |
| int ret; |
| |
| __ASSERT_NO_MSG(val != NULL); |
| |
| if (chan != SENSOR_CHAN_CURRENT) { |
| return -ENOTSUP; |
| } |
| |
| ret = adc_raw_to_millivolts_dt(&config->port, &raw_val); |
| if (ret != 0) { |
| LOG_ERR("raw_to_mv: %d", ret); |
| return ret; |
| } |
| |
| i_ma = raw_val; |
| current_sense_amplifier_scale_dt(config, &i_ma); |
| |
| LOG_DBG("%d/%d, %dmV, current:%duA", data->raw, |
| (1 << data->sequence.resolution) - 1, raw_val, i_ma); |
| |
| val->val1 = i_ma / 1000; |
| val->val2 = i_ma % 1000; |
| |
| return 0; |
| } |
| |
| static const struct sensor_driver_api current_api = { |
| .sample_fetch = fetch, |
| .channel_get = get, |
| }; |
| |
| #ifdef CONFIG_PM_DEVICE |
| static int pm_action(const struct device *dev, enum pm_device_action action) |
| { |
| const struct current_sense_amplifier_dt_spec *config = dev->config; |
| int ret; |
| |
| if (config->power_gpio.port == NULL) { |
| LOG_ERR("PM not supported"); |
| return -ENOTSUP; |
| } |
| |
| switch (action) { |
| case PM_DEVICE_ACTION_RESUME: |
| ret = gpio_pin_set_dt(&config->power_gpio, 1); |
| if (ret != 0) { |
| LOG_ERR("failed to set GPIO for PM resume"); |
| return ret; |
| } |
| break; |
| case PM_DEVICE_ACTION_SUSPEND: |
| ret = gpio_pin_set_dt(&config->power_gpio, 0); |
| if (ret != 0) { |
| LOG_ERR("failed to set GPIO for PM suspend"); |
| return ret; |
| } |
| break; |
| default: |
| return -ENOTSUP; |
| } |
| |
| return 0; |
| } |
| #endif |
| |
| static int current_init(const struct device *dev) |
| { |
| const struct current_sense_amplifier_dt_spec *config = dev->config; |
| struct current_sense_amplifier_data *data = dev->data; |
| int ret; |
| |
| if (!adc_is_ready_dt(&config->port)) { |
| LOG_ERR("ADC is not ready"); |
| return -ENODEV; |
| } |
| |
| #ifdef CONFIG_PM_DEVICE |
| if (config->power_gpio.port != NULL) { |
| if (!gpio_is_ready_dt(&config->power_gpio)) { |
| LOG_ERR("Power GPIO is not ready"); |
| return -ENODEV; |
| } |
| |
| ret = gpio_pin_configure_dt(&config->power_gpio, GPIO_OUTPUT_ACTIVE); |
| if (ret != 0) { |
| LOG_ERR("failed to config GPIO: %d", ret); |
| return ret; |
| } |
| } |
| #endif |
| |
| ret = adc_channel_setup_dt(&config->port); |
| if (ret != 0) { |
| LOG_ERR("setup: %d", ret); |
| return ret; |
| } |
| |
| ret = adc_sequence_init_dt(&config->port, &data->sequence); |
| if (ret != 0) { |
| LOG_ERR("sequence init: %d", ret); |
| return ret; |
| } |
| |
| data->sequence.buffer = &data->raw; |
| data->sequence.buffer_size = sizeof(data->raw); |
| |
| return 0; |
| } |
| |
| #define CURRENT_SENSE_AMPLIFIER_INIT(inst) \ |
| static struct current_sense_amplifier_data current_amp_##inst##_data; \ |
| \ |
| static const struct current_sense_amplifier_dt_spec current_amp_##inst##_config = \ |
| CURRENT_SENSE_AMPLIFIER_DT_SPEC_GET(DT_DRV_INST(inst)); \ |
| \ |
| PM_DEVICE_DT_INST_DEFINE(inst, pm_action); \ |
| \ |
| SENSOR_DEVICE_DT_INST_DEFINE(inst, ¤t_init, PM_DEVICE_DT_INST_GET(inst), \ |
| ¤t_amp_##inst##_data, ¤t_amp_##inst##_config, \ |
| POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, ¤t_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(CURRENT_SENSE_AMPLIFIER_INIT) |