| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Copyright 2022 Google LLC | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; either version 2 of the License, or | |
| * (at your option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| */ | |
| #include <linux/configfs.h> | |
| #include <linux/module.h> | |
| #include <linux/init.h> | |
| #include <linux/timer.h> | |
| #include <linux/alarmtimer.h> | |
| #include <linux/kmod.h> | |
| #include <linux/slab.h> | |
| #include <linux/platform_device.h> | |
| #include <linux/i2c.h> | |
| #include <linux/iio/iio.h> | |
| #include <linux/iio/configfs.h> | |
| #include <linux/iio/sysfs.h> | |
| #include <linux/of_device.h> | |
| #include <linux/pinctrl/consumer.h> | |
| #include <soc/google/odpm.h> | |
| #include <linux/mfd/samsung/s2mpg15.h> | |
| #define ODPM_PRINT_ESTIMATED_CLOCK_SKEW 0 | |
| /* Cache accumulated values to prevent too frequent updates, allow a refresh | |
| * only every X ms. | |
| * | |
| * In order to prevent quantization error, we need a minimum count per time | |
| * interval to accurately estimate the frequency of the ADC polling. If we | |
| * could rely on the accuracy of the PMIC clock, then we would assume the | |
| * configured frequency, and this wouldn't be a consideration. | |
| * | |
| * At 20 counts, the quantization error may be +-(1/20)/2 = +-0.025 = +-2.5%. | |
| * At max frequency (1 KHz), the minimum refresh time is 20 ms. | |
| * At min frequency (7.628125 Hz), the minimum refresh time is ~2.62 s. | |
| * | |
| * Note 1: The ODPM will be refreshed regardless of minimum interval if an | |
| * instantaneous refresh is called (odpm_take_snapshot_instant_locked) | |
| * Note 2: s2mpg1x chips can take on average between 1-2 ms for a refresh. | |
| */ | |
| #define ODPM_MIN_INTERVAL_ACC_COUNT 20 /* accumulator counts */ | |
| #define UHZ_PER_HZ 1000000 | |
| #define PICO_PER_MILLI 1000000000 | |
| #define str(val) #val | |
| #define xstr(s) str(s) | |
| #define ODPM_RAIL_NAME_STR_LEN_MAX 49 | |
| #define ODPM_FREQ_DECIMAL_UHZ_STR_LEN_MAX 6 | |
| #define ODPM_SAMPLING_FREQ_CHAR_LEN_MAX 20 | |
| #define _SWITCH_METER_FUNC(infop, ret, func, args...) \ | |
| do { \ | |
| switch ((infop)->chip.hw_id) { \ | |
| case ID_S2MPG14: \ | |
| ret = s2mpg14_##func(args); \ | |
| break; \ | |
| case ID_S2MPG15: \ | |
| ret = s2mpg15_##func(args); \ | |
| break; \ | |
| case ID_COUNT: \ | |
| break; \ | |
| } \ | |
| } while (0) | |
| #define SWITCH_METER_FUNC(info, ret, func, args...) \ | |
| _SWITCH_METER_FUNC(info, ret, func, info->meter, args) | |
| /** | |
| * IIO driver specific channel configurations | |
| */ | |
| #define ODPM_ACC_CHANNEL(_index) \ | |
| { \ | |
| .type = IIO_ENERGY, .indexed = 1, .channel = (_index), \ | |
| .info_mask_separate = BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ | |
| .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| } | |
| #define ODPM_CURRENT_BIT_RES_CHANNEL(_index) \ | |
| { \ | |
| .type = IIO_CURRENT, .indexed = 1, .channel = (_index), \ | |
| .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), \ | |
| .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| } | |
| #define ODPM_POWER_BIT_RES_CHANNEL(_index) \ | |
| { \ | |
| .type = IIO_POWER, .indexed = 1, .channel = (_index), \ | |
| .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE), \ | |
| .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ | |
| } | |
| static const struct iio_chan_spec s2mpg1415_single_channel[ODPM_CHANNEL_MAX * 3] = { | |
| ODPM_ACC_CHANNEL(0), ODPM_ACC_CHANNEL(1), | |
| ODPM_ACC_CHANNEL(2), ODPM_ACC_CHANNEL(3), | |
| ODPM_ACC_CHANNEL(4), ODPM_ACC_CHANNEL(5), | |
| ODPM_ACC_CHANNEL(6), ODPM_ACC_CHANNEL(7), | |
| ODPM_ACC_CHANNEL(8), ODPM_ACC_CHANNEL(9), | |
| ODPM_ACC_CHANNEL(10), ODPM_ACC_CHANNEL(11), | |
| ODPM_CURRENT_BIT_RES_CHANNEL(0), ODPM_CURRENT_BIT_RES_CHANNEL(1), | |
| ODPM_CURRENT_BIT_RES_CHANNEL(2), ODPM_CURRENT_BIT_RES_CHANNEL(3), | |
| ODPM_CURRENT_BIT_RES_CHANNEL(4), ODPM_CURRENT_BIT_RES_CHANNEL(5), | |
| ODPM_CURRENT_BIT_RES_CHANNEL(6), ODPM_CURRENT_BIT_RES_CHANNEL(7), | |
| ODPM_CURRENT_BIT_RES_CHANNEL(8), ODPM_CURRENT_BIT_RES_CHANNEL(9), | |
| ODPM_CURRENT_BIT_RES_CHANNEL(10), ODPM_CURRENT_BIT_RES_CHANNEL(11), | |
| ODPM_POWER_BIT_RES_CHANNEL(0), ODPM_POWER_BIT_RES_CHANNEL(1), | |
| ODPM_POWER_BIT_RES_CHANNEL(2), ODPM_POWER_BIT_RES_CHANNEL(3), | |
| ODPM_POWER_BIT_RES_CHANNEL(4), ODPM_POWER_BIT_RES_CHANNEL(5), | |
| ODPM_POWER_BIT_RES_CHANNEL(6), ODPM_POWER_BIT_RES_CHANNEL(7), | |
| ODPM_POWER_BIT_RES_CHANNEL(8), ODPM_POWER_BIT_RES_CHANNEL(9), | |
| ODPM_POWER_BIT_RES_CHANNEL(10), ODPM_POWER_BIT_RES_CHANNEL(11), | |
| }; | |
| static const u32 s2mpg1415_int_sample_rate_uhz[INT_FREQ_COUNT] = { | |
| 7812500, | |
| 15625000, | |
| 31250000, | |
| 62500000, | |
| 125000000, | |
| 250000000, | |
| 1000000000, | |
| }; | |
| static const u32 s2mpg1415_ext_sample_rate_uhz[EXT_FREQ_COUNT] = { | |
| 7812500, | |
| 15625000, | |
| 31250000, | |
| 62500000, | |
| 125000000, | |
| }; | |
| static int odpm_take_snapshot(struct odpm_info *info); | |
| static int odpm_take_snapshot_locked(struct odpm_info *info); | |
| static int odpm_take_snapshot_instant_locked(struct odpm_info *info, | |
| bool resume); | |
| static void odpm_print_new_sampling_rate(struct odpm_info *info, int ret, | |
| enum odpm_sampling_rate_type type); | |
| static u64 to_ms(u64 ns) | |
| { | |
| return ns / NSEC_PER_MSEC; | |
| } | |
| static int odpm_io_set_channel(struct odpm_info *info, int channel) | |
| { | |
| int ret = -1; | |
| int rail_i = info->channels[channel].rail_i; | |
| pr_info("odpm: %s: CH%d=%s\n", info->chip.name, channel, | |
| info->chip.rails[rail_i].schematic_name); | |
| SWITCH_METER_FUNC(info, ret, meter_set_muxsel, channel, | |
| info->chip.rails[rail_i].mux_select); | |
| return ret; | |
| } | |
| static int odpm_io_set_int_sampling_rate(struct odpm_info *info, | |
| enum s2mpg1415_int_samp_rate samp_rate_sel) | |
| { | |
| if (samp_rate_sel >= INT_FREQ_COUNT) | |
| return -1; | |
| info->chip.int_sampling_rate_i = samp_rate_sel; | |
| return s2mpg1415_meter_set_int_samp_rate(info->chip.hw_id, info->i2c, samp_rate_sel); | |
| } | |
| static int odpm_io_set_ext_sampling_rate(struct odpm_info *info, | |
| enum s2mpg1415_ext_samp_rate samp_rate_sel) | |
| { | |
| if (samp_rate_sel >= EXT_FREQ_COUNT) | |
| return -1; | |
| info->chip.ext_sampling_rate_i = samp_rate_sel; | |
| return s2mpg1415_meter_set_ext_samp_rate(info->chip.hw_id, info->i2c, samp_rate_sel); | |
| } | |
| static int odpm_io_set_meter_on(struct odpm_info *info, bool is_on) | |
| { | |
| int ret = -1; | |
| SWITCH_METER_FUNC(info, ret, meter_onoff, is_on); | |
| return ret; | |
| } | |
| static int odpm_io_set_ext_meter_on(struct odpm_info *info, bool is_on) | |
| { | |
| int ret = -1; | |
| SWITCH_METER_FUNC(info, ret, ext_meter_onoff, is_on); | |
| return ret; | |
| } | |
| static int odpm_io_set_ext_channels_en(struct odpm_info *info, u8 channels) | |
| { | |
| return s2mpg1415_meter_set_ext_channel_en(info->chip.hw_id, info->i2c, | |
| channels); | |
| } | |
| static int odpm_io_set_buck_channels_en(struct odpm_info *info, u8 *channels, | |
| int num_bytes) | |
| { | |
| return s2mpg1415_meter_set_buck_channel_en(info->chip.hw_id, info->i2c, | |
| channels, num_bytes); | |
| } | |
| static int odpm_io_send_blank_async(struct odpm_info *info, | |
| u64 *timestamp_capture) | |
| { | |
| return s2mpg1415_meter_set_async_blocking(info->chip.hw_id, info->i2c, | |
| timestamp_capture, | |
| info->chip.int_sampling_rate_i); | |
| } | |
| static int odpm_io_update_ext_enable_bits(struct odpm_info *info) | |
| { | |
| /* As of b/181251561, we permanently enable all 3 external rail bits */ | |
| /* This must be done while the ext meter en bit is set to 0 */ | |
| return odpm_io_set_ext_channels_en(info, EXT_METER_CHANNEL_EN_ALL); | |
| } | |
| static int odpm_io_update_bucken_enable_bits(struct odpm_info *info, | |
| bool on_sleep) | |
| { | |
| u8 buck_channels_en[ODPM_BUCK_EN_BYTES] = { 0 }; | |
| int ch; | |
| for (ch = 0; ch < ODPM_CHANNEL_MAX; ch++) { | |
| int rail_i = info->channels[ch].rail_i; | |
| struct odpm_rail_data *rail = &info->chip.rails[rail_i]; | |
| u8 channel_en_i = rail->channel_en_index; | |
| int channel_offset_byte = rail->channel_en_byte_offset; | |
| if (!info->channels[ch].enabled) | |
| continue; | |
| /* Skip the enable bit if bucks should be disabled in sleep */ | |
| if (on_sleep && rail->disable_in_sleep) | |
| continue; | |
| if (rail->type == ODPM_RAIL_TYPE_REGULATOR_BUCK && | |
| channel_offset_byte < ODPM_BUCK_EN_BYTES) | |
| buck_channels_en[channel_offset_byte] |= channel_en_i; | |
| } | |
| return odpm_io_set_buck_channels_en(info, buck_channels_en, | |
| ODPM_BUCK_EN_BYTES); | |
| } | |
| static void odpm_io_set_lpf_mode(struct odpm_info *info, enum s2mpg1415_meter_mode mode) | |
| { | |
| s2mpg1415_meter_set_lpf_mode(info->chip.hw_id, info->i2c, mode); | |
| } | |
| static void odpm_io_get_lpf_data(struct odpm_info *info, u32 *data) | |
| { | |
| s2mpg1415_meter_read_lpf_data_reg(info->chip.hw_id, info->i2c, data); | |
| } | |
| static int odpm_io_write_lpf_reg(struct odpm_info *info, | |
| int ch, u8 data) | |
| { | |
| return s2mpg1415_meter_set_lpf_coefficient(info->chip.hw_id, info->i2c, ch, data); | |
| } | |
| int odpm_configure_chip(struct odpm_info *info) | |
| { | |
| int ch; | |
| int ret; | |
| /* TODO(stayfan): b/156107234 | |
| * error conditions | |
| */ | |
| pr_info("odpm: Configuring %s...\n", info->chip.name); | |
| ret = odpm_io_set_int_sampling_rate(info, | |
| info->chip.int_config_sampling_rate_i); | |
| odpm_print_new_sampling_rate(info, ret, ODPM_SAMPLING_RATE_INTERNAL); | |
| ret = odpm_io_set_ext_sampling_rate(info, | |
| info->chip.ext_config_sampling_rate_i); | |
| odpm_print_new_sampling_rate(info, ret, ODPM_SAMPLING_RATE_EXTERNAL); | |
| for (ch = 0; ch < ODPM_CHANNEL_MAX; ch++) { | |
| if (info->channels[ch].enabled) | |
| odpm_io_set_channel(info, ch); | |
| } | |
| odpm_io_update_bucken_enable_bits(info, false /* on_sleep */); | |
| odpm_io_set_ext_meter_on(info, false); | |
| odpm_io_update_ext_enable_bits(info); | |
| odpm_io_set_meter_on(info, true); | |
| odpm_io_set_ext_meter_on(info, true); | |
| pr_info("odpm: Configuring %s...done\n", info->chip.name); | |
| return 0; | |
| } | |
| int odpm_meter_sw_reset(struct odpm_info *info) | |
| { | |
| int ret = 0; | |
| int hw_id = info->chip.hw_id; | |
| pr_info("odpm: %s: meter reset\n", info->chip.name); | |
| // b/322027909: disable NTC LPF before meter reset | |
| if (hw_id == ID_S2MPG15 && s2mpg15_spmic_thermal_ready()) { | |
| ret = s2mpg15_spmic_set_hw_lpf(false); | |
| if (ret) { | |
| pr_err("odpm: %s: LPF disable fail\n", info->chip.name); | |
| goto err; | |
| } | |
| } | |
| ret = s2mpg1415_meter_sw_reset(hw_id, info->i2c); | |
| // enable NTC LPF after sensors are ready | |
| if (hw_id == ID_S2MPG15 && s2mpg15_spmic_thermal_ready()) { | |
| ret = s2mpg15_spmic_set_hw_lpf(true); | |
| if (ret) { | |
| pr_err("odpm: %s: LPF enable fail\n", info->chip.name); | |
| goto err; | |
| } | |
| } | |
| err: | |
| return ret; | |
| } | |
| int odpm_configure_start_measurement(struct odpm_info *info) | |
| { | |
| u64 timestamp_capture_ns = 0; | |
| int ch; | |
| /* For s2mpg1x chips, clear ACC registers */ | |
| int ret = odpm_io_send_blank_async(info, ×tamp_capture_ns); | |
| info->last_poll_ktime_boot_ns = timestamp_capture_ns; | |
| pr_info("odpm: %s: Starting at timestamp (ms): %llu\n", | |
| info->chip.name, | |
| to_ms(timestamp_capture_ns)); | |
| /* Initialize boot measurement time to 0. This means that there will be | |
| * some amount of time from boot where power measurements are not | |
| * accounted for, because the driver needs to load and wait for the | |
| * HAL to initialize (and send "CONFIG_COMPLETE"). Therefore, the true | |
| * measurement duration will be incorrect at boot (duration = boot | |
| * timestamp - start time), since we don't start truly measuring power | |
| * until the HAL starts. | |
| * | |
| * The approximate unaccounted time was measured to be ~ 15s when this | |
| * commit was created. | |
| */ | |
| for (ch = 0; ch < ODPM_CHANNEL_MAX; ch++) { | |
| if (info->channels[ch].enabled) | |
| info->channels[ch].measurement_start_ms = 0; | |
| } | |
| return ret; | |
| } | |
| static enum alarmtimer_restart odpm_alarm_handler(struct alarm *alarm, ktime_t time) | |
| { | |
| struct odpm_info *info = | |
| container_of(alarm, struct odpm_info, alarmtimer_refresh); | |
| __pm_stay_awake(info->ws); | |
| /* schedule the periodic reading from the chip */ | |
| queue_work(info->work_queue, &info->work_refresh); | |
| alarm_start_relative(&info->alarmtimer_refresh, | |
| ms_to_ktime(info->chip.max_refresh_time_ms)); | |
| return ALARMTIMER_NORESTART; | |
| } | |
| static void odpm_periodic_refresh_work(struct work_struct *work) | |
| { | |
| struct odpm_info *info = | |
| container_of(work, struct odpm_info, work_refresh); | |
| if (odpm_take_snapshot(info) < 0) | |
| pr_err("odpm: Cannot refresh %s registers periodically!\n", | |
| info->chip.name); | |
| else | |
| pr_info("odpm: Refreshed %s registers!\n", info->chip.name); | |
| __pm_relax(info->ws); | |
| } | |
| static void odpm_periodic_refresh_setup(struct odpm_info *info) | |
| { | |
| info->work_queue = create_workqueue("wq_odpm"); | |
| INIT_WORK(&info->work_refresh, odpm_periodic_refresh_work); | |
| /* setup the latest moment for reading the regs before saturation */ | |
| /* register the timer */ | |
| alarm_init(&info->alarmtimer_refresh, ALARM_BOOTTIME, odpm_alarm_handler); | |
| alarm_start_relative(&info->alarmtimer_refresh, | |
| ms_to_ktime(info->chip.max_refresh_time_ms)); | |
| } | |
| static bool odpm_match_int_sampling_rate(struct odpm_info *info, | |
| u32 sampling_rate, int *index) | |
| { | |
| bool success = false; | |
| int i; | |
| for (i = 0; i < info->chip.sampling_rate_int_count; i++) { | |
| if (sampling_rate == info->chip.sampling_rate_int_uhz[i]) { | |
| *index = i; | |
| success = true; | |
| break; | |
| } | |
| } | |
| return success; | |
| } | |
| static bool odpm_match_ext_sampling_rate(struct odpm_info *info, | |
| u32 sampling_rate, int *index) | |
| { | |
| bool success = false; | |
| int i; | |
| for (i = 0; i < info->chip.sampling_rate_ext_count; i++) { | |
| if (sampling_rate == info->chip.sampling_rate_ext_uhz[i]) { | |
| *index = i; | |
| success = true; | |
| break; | |
| } | |
| } | |
| return success; | |
| } | |
| static int odpm_parse_dt_rail(struct odpm_rail_data *rail_data, | |
| struct device_node *node) | |
| { | |
| if (!node->name) { | |
| pr_err("odpm: cannot read node name\n"); | |
| return -EINVAL; | |
| } | |
| rail_data->name = node->name; | |
| /* If the read fails, the pointers are not assigned; thus the pointers | |
| * are left NULL if the values don't exist in the DT | |
| */ | |
| of_property_read_string(node, "schematic-name", | |
| &rail_data->schematic_name); | |
| of_property_read_string(node, "subsys-name", &rail_data->subsys_name); | |
| if (of_property_read_bool(node, "external_rail")) | |
| rail_data->type = ODPM_RAIL_TYPE_SHUNT; | |
| else if (of_property_read_bool(node, "buck_rail")) | |
| rail_data->type = ODPM_RAIL_TYPE_REGULATOR_BUCK; | |
| else | |
| rail_data->type = ODPM_RAIL_TYPE_REGULATOR_LDO; | |
| if (of_property_read_u32(node, "channel-mux-selection", | |
| &rail_data->mux_select)) { | |
| pr_err("odpm: cannot read channel-mux-selection\n"); | |
| return -EINVAL; | |
| } | |
| rail_data->disable_in_sleep = | |
| of_property_read_bool(node, "odpm_disable_in_sleep"); | |
| switch (rail_data->type) { | |
| case ODPM_RAIL_TYPE_SHUNT: { | |
| u32 channel_en_index; | |
| if (of_property_read_u32(node, "shunt-res-uohms", | |
| &rail_data->shunt_uohms)) { | |
| pr_err("odpm: cannot read shunt-res-uohms\n"); | |
| return -EINVAL; | |
| } | |
| if (of_property_read_u32(node, "channel-en-index", | |
| &channel_en_index)) { | |
| pr_err("odpm: cannot read channel-en-index\n"); | |
| return -EINVAL; | |
| } | |
| rail_data->channel_en_index = channel_en_index; | |
| } break; | |
| case ODPM_RAIL_TYPE_REGULATOR_BUCK: { | |
| u32 channel_en_index; | |
| int channel_en_byte_offset; | |
| if (of_property_read_u32(node, "channel-en-index", | |
| &channel_en_index)) { | |
| pr_err("odpm: cannot read channel-en-index\n"); | |
| return -EINVAL; | |
| } | |
| rail_data->channel_en_index = channel_en_index; | |
| if (of_property_read_u32(node, "channel-en-byte-offset", | |
| &channel_en_byte_offset)) { | |
| pr_err("odpm: cannot read channel-en-byte-offset\n"); | |
| return -EINVAL; | |
| } | |
| rail_data->channel_en_byte_offset = channel_en_byte_offset; | |
| } break; | |
| case ODPM_RAIL_TYPE_REGULATOR_LDO: | |
| default: | |
| break; | |
| } | |
| return 0; | |
| } | |
| static int odpm_parse_dt_rails(struct device *dev, struct odpm_info *info, | |
| struct device_node *pmic_np) | |
| { | |
| struct device_node *iter_np, *regulators_np; | |
| bool use_regulators_as_rails = false; | |
| struct odpm_rail_data *rail_data; | |
| int rail_i = 0, num_rails = 0; | |
| struct device_node *rails_np = of_find_node_by_name(pmic_np, "rails"); | |
| if (!rails_np) { | |
| pr_err("odpm: cannot find rails DT node!\n"); | |
| return -EINVAL; | |
| } | |
| /* Count rails */ | |
| use_regulators_as_rails = | |
| of_property_read_bool(rails_np, "use-regulators-as-rails"); | |
| if (use_regulators_as_rails) { | |
| regulators_np = of_find_node_by_name(pmic_np, "regulators"); | |
| if (!regulators_np) { | |
| pr_err("odpm: Could not find regulators sub-node\n"); | |
| return -EINVAL; | |
| } | |
| num_rails += of_get_child_count(regulators_np); | |
| } | |
| num_rails += of_get_child_count(rails_np); | |
| if (num_rails <= 0) { | |
| pr_err("odpm: Could not find any rails\n"); | |
| return -EINVAL; | |
| } | |
| info->chip.num_rails = num_rails; | |
| /* Allocate/Initialize rails */ | |
| rail_data = | |
| devm_kzalloc(dev, sizeof(*rail_data) * num_rails, GFP_KERNEL); | |
| if (!rail_data) { | |
| dev_err(dev, "odpm: could not allocate memory for rail data\n"); | |
| return -ENOMEM; | |
| } | |
| info->chip.rails = rail_data; | |
| /* Populate rail data */ | |
| if (use_regulators_as_rails) { | |
| for_each_child_of_node(regulators_np, iter_np) { | |
| int ret = | |
| odpm_parse_dt_rail(&rail_data[rail_i], iter_np); | |
| if (ret != 0) | |
| return ret; | |
| rail_i++; | |
| } | |
| } | |
| for_each_child_of_node(rails_np, iter_np) { | |
| int ret = odpm_parse_dt_rail(&rail_data[rail_i], iter_np); | |
| if (ret != 0) | |
| return ret; | |
| rail_i++; | |
| } | |
| /* Confidence check rail count */ | |
| if (rail_i != num_rails) { | |
| pr_err("odpm: expected %d rails, got %d\n", num_rails, rail_i); | |
| return -EINVAL; | |
| } | |
| return 0; | |
| } | |
| static int odpm_parse_dt_channels(struct odpm_info *info, | |
| struct device_node *channels_np) | |
| { | |
| int rail_i = 0, channel_i = 0; | |
| int num_channels = of_get_child_count(channels_np); | |
| struct device_node *iter_np; | |
| /* Check channel count */ | |
| if (num_channels != ODPM_CHANNEL_MAX) { | |
| pr_err("odpm: expected %d channels, got %d\n", ODPM_CHANNEL_MAX, | |
| num_channels); | |
| return -EINVAL; | |
| } | |
| /* Parse channels */ | |
| for_each_child_of_node(channels_np, iter_np) { | |
| const char *rail_name; | |
| /* Explicitly set enabled to false until we find the | |
| * associated rail | |
| */ | |
| info->channels[channel_i].enabled = false; | |
| /* Read rail name */ | |
| if (of_property_read_string(iter_np, "rail-name", &rail_name)) { | |
| pr_err("odpm: invalid rail-name value on %s\n", | |
| iter_np->full_name); | |
| return -EINVAL; | |
| } | |
| /* Match rail name */ | |
| for (rail_i = 0; rail_i < info->chip.num_rails; rail_i++) { | |
| if (!strcmp(info->chip.rails[rail_i].name, rail_name)) { | |
| info->channels[channel_i].rail_i = rail_i; | |
| break; | |
| } | |
| } | |
| if (rail_i == info->chip.num_rails) { | |
| pr_err("odpm: Could not find rail-name %s\n", | |
| rail_name); | |
| return -EINVAL; | |
| } | |
| /* Check if the channel is enabled or not */ | |
| info->channels[channel_i].enabled = | |
| of_property_read_bool(iter_np, "channel_enabled"); | |
| channel_i++; | |
| } | |
| return 0; | |
| } | |
| static int odpm_parse_dt(struct device *dev, struct odpm_info *info) | |
| { | |
| struct device_node *pmic_np = dev->parent->parent->of_node; | |
| struct device_node *odpm_np, *channels_np; | |
| u32 sampling_rate; | |
| int sampling_rate_i; | |
| int ret; | |
| if (!pmic_np) { | |
| pr_err("odpm: cannot find parent DT node!\n"); | |
| return -EINVAL; | |
| } | |
| odpm_np = of_find_node_by_name(pmic_np, "odpm"); | |
| if (!odpm_np) { | |
| pr_err("odpm: cannot find main DT node!\n"); | |
| return -EINVAL; | |
| } | |
| channels_np = of_find_node_by_name(pmic_np, "channels"); | |
| if (!channels_np) { | |
| pr_err("odpm: cannot find channels DT node!\n"); | |
| return -EINVAL; | |
| } | |
| /* Get main properties; sample-rate, chip-name, etc. */ | |
| if (of_property_read_string(odpm_np, "chip-name", &info->chip.name)) { | |
| pr_err("odpm: cannot read sample rate value\n"); | |
| return -EINVAL; | |
| } | |
| if (of_property_read_u32(odpm_np, "sample-rate-uhz", &sampling_rate)) { | |
| pr_err("odpm: cannot read sample rate value\n"); | |
| return -EINVAL; | |
| } | |
| if (!odpm_match_int_sampling_rate(info, sampling_rate, | |
| &sampling_rate_i)) { | |
| pr_err("odpm: cannot parse sample rate value %d\n", | |
| sampling_rate); | |
| return -EINVAL; | |
| } | |
| info->chip.int_config_sampling_rate_i = sampling_rate_i; | |
| info->chip.int_sampling_rate_i = sampling_rate_i; | |
| if (of_property_read_u32(odpm_np, "sample-rate-external-uhz", | |
| &sampling_rate)) { | |
| pr_err("odpm: cannot read external sample rate value\n"); | |
| return -EINVAL; | |
| } | |
| if (!odpm_match_ext_sampling_rate(info, sampling_rate, | |
| &sampling_rate_i)) { | |
| pr_err("odpm: cannot parse external sample rate value %d\n", | |
| sampling_rate); | |
| return -EINVAL; | |
| } | |
| info->chip.ext_config_sampling_rate_i = sampling_rate_i; | |
| info->chip.ext_sampling_rate_i = sampling_rate_i; | |
| if (of_property_read_u32(odpm_np, "max-refresh-time-ms", | |
| &info->chip.max_refresh_time_ms)) { | |
| pr_err("odpm: cannot read max refresh time value\n"); | |
| return -EINVAL; | |
| } | |
| ret = odpm_parse_dt_rails(dev, info, pmic_np); | |
| if (ret != 0) | |
| return ret; | |
| return odpm_parse_dt_channels(info, channels_np); | |
| } | |
| /** | |
| * @brief Return the specific rail's milli unit/bit resolution | |
| * | |
| * @return u32 - the rail resolution in iq30 is in milli unit/bit. Will return 0 if | |
| * the resolution doesn't exist. | |
| */ | |
| static u32 odpm_get_resolution_milli_iq30(struct odpm_info *info, int rail_i, | |
| enum s2mpg1415_meter_mode mode) | |
| { | |
| u32 ret = 0; | |
| switch (info->chip.rails[rail_i].type) { | |
| case ODPM_RAIL_TYPE_REGULATOR_BUCK: | |
| case ODPM_RAIL_TYPE_REGULATOR_LDO: | |
| default: | |
| if (mode == S2MPG1415_METER_CURRENT) | |
| _SWITCH_METER_FUNC(info, ret, | |
| muxsel_to_current_resolution, | |
| info->chip.rails[rail_i].mux_select); | |
| else | |
| _SWITCH_METER_FUNC(info, ret, | |
| muxsel_to_power_resolution, | |
| info->chip.rails[rail_i].mux_select); | |
| break; | |
| case ODPM_RAIL_TYPE_SHUNT: | |
| { | |
| u64 raw_unit_iq60; | |
| /* Losing a fraction of resolution performing u64 divisions, | |
| * as there is no support for 128 bit divisions | |
| */ | |
| if (mode == S2MPG1415_METER_CURRENT) { | |
| raw_unit_iq60 = 10000 * ((u64)EXTERNAL_RESOLUTION_VSHUNT) / | |
| info->chip.rails[rail_i].shunt_uohms; | |
| ret = raw_unit_iq60; | |
| } else { | |
| raw_unit_iq60 = ((u64)EXTERNAL_RESOLUTION_VRAIL * | |
| (u64)EXTERNAL_RESOLUTION_VSHUNT * | |
| (u64)EXTERNAL_RESOLUTION_TRIM) / | |
| info->chip.rails[rail_i].shunt_uohms; | |
| /* Scale back to iq30 */ | |
| ret = _IQ30_to_int(raw_unit_iq60 * 10); | |
| } | |
| } | |
| break; | |
| } | |
| return ret; | |
| } | |
| static u32 odpm_get_resolution_mW_iq30(struct odpm_info *info, int rail_i) | |
| { | |
| return odpm_get_resolution_milli_iq30(info, rail_i, S2MPG1415_METER_POWER); | |
| } | |
| static u64 odpm_calculate_uW_sec(struct odpm_info *info, int rail_i, | |
| u64 acc_data, u32 int_sampling_frequency_uhz) | |
| { | |
| u64 sampling_period_ms_iq30; | |
| u32 sampling_period_ms_iq22; | |
| u32 resolution_mW_iq30 = INVALID_RESOLUTION; | |
| __uint128_t power_acc_mW_iq30; | |
| __uint128_t power_acc_uW_s_iq52; | |
| resolution_mW_iq30 = odpm_get_resolution_mW_iq30(info, rail_i); | |
| /* Maintain as much precision as possible computing period in ms */ | |
| sampling_period_ms_iq30 = | |
| _IQ30(u64, (u64)(MSEC_PER_SEC * UHZ_PER_HZ)) / | |
| int_sampling_frequency_uhz; | |
| /* Allocate 10-bits in u32 for sample rate in ms (max: 1023 ms) */ | |
| sampling_period_ms_iq22 = _IQ30_to_IQ22(sampling_period_ms_iq30); | |
| /* Use 128 bit to prevent overflow on multiplied iq values */ | |
| power_acc_mW_iq30 = (__uint128_t)acc_data * resolution_mW_iq30; | |
| power_acc_uW_s_iq52 = power_acc_mW_iq30 * sampling_period_ms_iq22; | |
| /* Scale back u128 from iq to value */ | |
| return _IQ22_to_int(_IQ30_to_int(power_acc_uW_s_iq52)); | |
| } | |
| #if ODPM_PRINT_ESTIMATED_CLOCK_SKEW | |
| static void odpm_print_clock_skew(struct odpm_info *info, u64 elapsed_ms, | |
| u32 acc_count) | |
| { | |
| u64 uHz_estimated = ((u64)acc_count * (MSEC_PER_SEC * UHZ_PER_HZ)) / | |
| elapsed_ms; | |
| int i = info->chip.int_sampling_rate_i; | |
| u64 uHz = info->chip.sampling_rate_int_uhz[i]; | |
| u64 ratio_u = (1000000 * uHz_estimated) / uHz; | |
| s64 pct_u = (((s64)ratio_u - (1 * 1000000)) * 100); | |
| pr_info("odpm: %s: elapsed_ms: %lld, acc_count: %d\n", | |
| info->chip.name, elapsed_ms, acc_count); | |
| pr_info("odpm: %s: internal clock skew: %lld.%06lld %%\n", | |
| info->chip.name, pct_u / 1000000, abs(pct_u) % 1000000); | |
| } | |
| #endif | |
| static u32 odpm_estimate_sampling_frequency(struct odpm_info *info, | |
| unsigned int elapsed_ms, | |
| unsigned int elapsed_refresh_ms, | |
| u32 acc_count) | |
| { | |
| /* b/156680376 | |
| * Instead of using the configured sampling rate, we want to approximate | |
| * the sampling rate based on acc_count | |
| */ | |
| u64 sampling_frequency_estimated_uhz; | |
| int i = info->chip.int_sampling_rate_i; | |
| u64 sampling_frequency_table_uhz = info->chip.sampling_rate_int_uhz[i]; | |
| u64 freq_lower_bound; | |
| u64 freq_upper_bound; | |
| /* Don't bother calculating frequency when there aren't enough samples, | |
| * otherwise we'd accumulate large quantization error. This may occur | |
| * nominally if two instantaneous requests are followed back-to-back. | |
| */ | |
| if (acc_count < ODPM_MIN_INTERVAL_ACC_COUNT) | |
| return sampling_frequency_table_uhz; | |
| if (elapsed_ms == 0) { | |
| pr_err("odpm: %s: elapsed time is 0 ms\n", info->chip.name); | |
| /* Fall back to configured frequency */ | |
| return sampling_frequency_table_uhz; | |
| } | |
| #if ODPM_PRINT_ESTIMATED_CLOCK_SKEW | |
| odpm_print_clock_skew(info, elapsed_ms, acc_count); | |
| #endif | |
| /* Estimate sampling rate based on acc_count */ | |
| sampling_frequency_estimated_uhz = | |
| ((u64)acc_count * (MSEC_PER_SEC * UHZ_PER_HZ)) / elapsed_ms; | |
| /** | |
| * 100 ms check on register refresh... | |
| * We want to verify that the transaction time isn't too long, as the | |
| * process may have been pre-empted between the command to refresh | |
| * registers and when the timestamp was captured. | |
| */ | |
| if (elapsed_refresh_ms >= 100) { | |
| pr_err("odpm: %s: refresh registers took too long; %u ms\n", | |
| info->chip.name, elapsed_refresh_ms); | |
| /* Fall back to configured frequency */ | |
| return sampling_frequency_table_uhz; | |
| } | |
| /* +-12.5% error bounds check, per the datasheets, to ensure that our | |
| * calculations are no worse than is expected from the configured | |
| * frequency. This also ensures the new frequency fits in a u32 (max | |
| * 1000 000 000 uHz * 1.125). | |
| */ | |
| freq_lower_bound = sampling_frequency_table_uhz * 7 / 8; | |
| freq_upper_bound = sampling_frequency_table_uhz * 9 / 8; | |
| if (sampling_frequency_estimated_uhz < freq_lower_bound || | |
| sampling_frequency_estimated_uhz > freq_upper_bound) { | |
| pr_err("odpm: %s: clock error too large! fsel: %llu, fest: %llu, elapsed_ms: %d, acc_count: %d\n", | |
| info->chip.name, sampling_frequency_table_uhz, | |
| sampling_frequency_estimated_uhz, | |
| elapsed_ms, acc_count); | |
| if (sampling_frequency_estimated_uhz > freq_upper_bound) { | |
| if (odpm_meter_sw_reset(info) != 0) | |
| pr_err("odpm: meter_sw_reset failed\n"); | |
| } | |
| /* Fall back to configured frequency */ | |
| return sampling_frequency_table_uhz; | |
| } | |
| return sampling_frequency_estimated_uhz; | |
| } | |
| static int odpm_refresh_registers(struct odpm_info *info, bool resume) | |
| { | |
| int ch; | |
| int ret = 0; | |
| u64 acc_data[ODPM_CHANNEL_MAX]; | |
| u32 acc_count = 0; | |
| u32 sampling_frequency_uhz; | |
| u64 timestamp_previous_sample = info->last_poll_ktime_boot_ns; | |
| u64 timestamp_before_async; | |
| u64 timestamp_after_async; | |
| unsigned int elapsed_ms; | |
| unsigned int elapsed_refresh_ms; | |
| timestamp_before_async = ktime_get_boottime_ns(); | |
| ret = s2mpg1415_meter_measure_acc(info->chip.hw_id, | |
| info->i2c, | |
| info->meter_lock, | |
| S2MPG1415_METER_POWER, | |
| acc_data, | |
| &acc_count, | |
| ×tamp_after_async, | |
| info->chip.int_sampling_rate_i); | |
| if (ret < 0) { | |
| pr_err("odpm: %s: i2c error; count not measure interval\n", | |
| info->chip.name); | |
| goto exit_refresh; | |
| } | |
| /* Store timestamps - the rest of the function will succeed */ | |
| info->last_poll_ktime_boot_ns = timestamp_after_async; | |
| info->chip.acc_timestamp_ms = to_ms(timestamp_after_async); | |
| elapsed_ms = to_ms(timestamp_after_async - timestamp_previous_sample); | |
| elapsed_refresh_ms = | |
| to_ms(timestamp_after_async - timestamp_before_async); | |
| sampling_frequency_uhz = | |
| odpm_estimate_sampling_frequency(info, | |
| elapsed_ms, | |
| elapsed_refresh_ms, | |
| acc_count); | |
| for (ch = 0; ch < ODPM_CHANNEL_MAX; ch++) { | |
| const int rail_i = info->channels[ch].rail_i; | |
| struct odpm_rail_data *rail = &info->chip.rails[rail_i]; | |
| u64 uW_sec; | |
| /* Do not add energy on rails that were disabled during sleep */ | |
| if (resume && rail->disable_in_sleep) | |
| continue; | |
| uW_sec = odpm_calculate_uW_sec(info, rail_i, acc_data[ch], | |
| (u32)sampling_frequency_uhz); | |
| info->channels[ch].acc_power_uW_sec += uW_sec; | |
| } | |
| exit_refresh: | |
| return ret; | |
| } | |
| static int odpm_reset_timer(struct odpm_info *info) | |
| { | |
| int ret = alarm_cancel(&info->alarmtimer_refresh); | |
| if (ret < 0) { | |
| pr_err("odpm: cannot reset the refresh timer\n"); | |
| return ret; | |
| } else { | |
| alarm_init(&info->alarmtimer_refresh, ALARM_BOOTTIME, odpm_alarm_handler); | |
| alarm_start_relative(&info->alarmtimer_refresh, | |
| ms_to_ktime(info->chip.max_refresh_time_ms)); | |
| } | |
| return ret; | |
| } | |
| static int odpm_take_snapshot(struct odpm_info *info) | |
| { | |
| int ret; | |
| mutex_lock(&info->lock); | |
| ret = odpm_take_snapshot_locked(info); | |
| mutex_unlock(&info->lock); | |
| return ret; | |
| } | |
| static int odpm_take_snapshot_locked(struct odpm_info *info) | |
| { | |
| /* check if the minimal elapsed time has passed and if so, | |
| * re-read the chip, otherwise the cached info is just fine | |
| */ | |
| int i = info->chip.int_sampling_rate_i; | |
| u32 freq = info->chip.sampling_rate_int_uhz[i]; | |
| u64 min_time_ns = (u64)ODPM_MIN_INTERVAL_ACC_COUNT * NSEC_PER_SEC * | |
| UHZ_PER_HZ / freq; | |
| u64 now_ns = ktime_get_boottime_ns(); | |
| if (now_ns > info->last_poll_ktime_boot_ns + min_time_ns) | |
| return odpm_take_snapshot_instant_locked(info, false); | |
| return 0; /* Refresh skipped for min elapsed time */ | |
| } | |
| static int odpm_take_snapshot_instant_locked(struct odpm_info *info, | |
| bool resume) | |
| { | |
| int ret_refresh; | |
| int ret_timer = odpm_reset_timer(info); | |
| if (info->sleeping) { | |
| pr_err("odpm: tried to refresh registers while sleeping!\n"); | |
| return -1; | |
| } | |
| /* we need to re-read the chip values */ | |
| ret_refresh = odpm_refresh_registers(info, resume); | |
| if (ret_refresh < 0) { | |
| pr_err("odpm: could not refresh registers!\n"); | |
| return ret_refresh; | |
| } | |
| return ret_timer; | |
| } | |
| static void odpm_suspend_resume_operations(struct odpm_info *info, bool suspend) | |
| { | |
| int ch; | |
| int ret; | |
| mutex_lock(&info->lock); | |
| if (suspend) { | |
| /* Send a refresh and store values */ | |
| if (odpm_take_snapshot_instant_locked(info, false) < 0) { | |
| pr_err("odpm: cannot capture snapshot for suspend\n"); | |
| goto suspend_resume_exit; | |
| } | |
| /* In case it saves on power, we set the muxsels for these bucks | |
| * to 0x00. | |
| */ | |
| for (ch = 0; ch < ODPM_CHANNEL_MAX; ch++) { | |
| const int rail_i = info->channels[ch].rail_i; | |
| struct odpm_rail_data *rail = &info->chip.rails[rail_i]; | |
| if (info->channels[ch].enabled && | |
| rail->disable_in_sleep) | |
| SWITCH_METER_FUNC(info, ret, meter_set_muxsel, | |
| ch, MUXSEL_NONE); | |
| } | |
| /* Disable the powermeter for the bucks with disable_in_sleep */ | |
| odpm_io_update_bucken_enable_bits(info, true /* on_sleep */); | |
| /* Prevents any external device from refreshing registers */ | |
| info->sleeping = true; | |
| } else { /* resume: opposite operations */ | |
| info->sleeping = false; | |
| /* Re-enable the bucks */ | |
| odpm_io_update_bucken_enable_bits(info, false /* on_sleep */); | |
| /* Re-select the muxsel for the bucks */ | |
| for (ch = 0; ch < ODPM_CHANNEL_MAX; ch++) { | |
| const int rail_i = info->channels[ch].rail_i; | |
| struct odpm_rail_data *rail = &info->chip.rails[rail_i]; | |
| if (info->channels[ch].enabled && | |
| rail->disable_in_sleep) | |
| SWITCH_METER_FUNC(info, ret, meter_set_muxsel, | |
| ch, rail->mux_select); | |
| } | |
| /* This refresh will erase the last data interval for specific | |
| * bucks that have disable_in_sleep. | |
| */ | |
| if (odpm_take_snapshot_instant_locked(info, true /* resume */) | |
| < 0) { | |
| pr_err("odpm: cannot capture snapshot for resume\n"); | |
| goto suspend_resume_exit; | |
| } | |
| } | |
| suspend_resume_exit: | |
| mutex_unlock(&info->lock); | |
| } | |
| /** | |
| * Verify and return sampling rate | |
| * | |
| * @return Sampling rate if >= 0, else error number | |
| */ | |
| static int odpm_sampling_rate_verify(const char *buf) | |
| { | |
| int hz; | |
| int i; | |
| char decimal_str[ODPM_FREQ_DECIMAL_UHZ_STR_LEN_MAX + 1]; | |
| int decimal_len = 0; | |
| int decimal_uHz = 0; | |
| decimal_str[0] = 0; | |
| if (sscanf(buf, "%d.%" xstr(ODPM_FREQ_DECIMAL_UHZ_STR_LEN_MAX) "s", &hz, | |
| decimal_str) != 2) { | |
| decimal_str[0] = 0; | |
| if (kstrtoint(buf, 10, &hz)) { | |
| pr_err("odpm: sampling rate is not a number\n"); | |
| return -EINVAL; | |
| } | |
| } | |
| decimal_len = strlen(decimal_str); | |
| /* Got a decimal value */ | |
| if (decimal_len > 0) { | |
| const int power_of_10 = | |
| (ODPM_FREQ_DECIMAL_UHZ_STR_LEN_MAX - decimal_len); | |
| /* Parse integer */ | |
| if (kstrtoint(decimal_str, 10, &decimal_uHz)) { | |
| pr_err("odpm: sampling rate decimal is not a number\n"); | |
| return -EINVAL; | |
| } | |
| /* Multiply by powers of 10 */ | |
| for (i = 0; i < power_of_10; i++) | |
| decimal_uHz *= 10; | |
| } | |
| return UHZ_PER_HZ * hz + decimal_uHz; | |
| } | |
| static ssize_t scnprintf_sampling_rate(char *buf, size_t size, u32 freq) | |
| { | |
| return scnprintf(buf, size, "%d.%06d\n", freq / UHZ_PER_HZ, | |
| freq % UHZ_PER_HZ); | |
| } | |
| static void odpm_print_new_sampling_rate(struct odpm_info *info, int ret, | |
| enum odpm_sampling_rate_type type) | |
| { | |
| char buf[ODPM_SAMPLING_FREQ_CHAR_LEN_MAX]; | |
| u32 freq = 0; | |
| if (ret < 0) { | |
| pr_warn("odpm: cannot apply sampling frequency type: %d\n", | |
| type); | |
| return; | |
| } | |
| if (type == ODPM_SAMPLING_RATE_INTERNAL) { | |
| int i = info->chip.int_sampling_rate_i; | |
| freq = info->chip.sampling_rate_int_uhz[i]; | |
| } else if (type == ODPM_SAMPLING_RATE_EXTERNAL) { | |
| int i = info->chip.ext_sampling_rate_i; | |
| freq = info->chip.sampling_rate_ext_uhz[i]; | |
| } else { | |
| pr_warn("odpm: unsupported frequency type: %d\n", type); | |
| return; | |
| } | |
| /* Returns with a new line */ | |
| scnprintf_sampling_rate(buf, ODPM_SAMPLING_FREQ_CHAR_LEN_MAX, freq); | |
| pr_info("odpm: %s: Applied new sampling frequency (type %d) in Hz: %s", | |
| info->chip.name, type, buf); | |
| } | |
| static void odpm_set_sampling_rate(struct odpm_info *info, | |
| enum odpm_sampling_rate_type type, | |
| enum s2mpg1415_int_samp_rate int_samp_rate_sel, | |
| enum s2mpg1415_ext_samp_rate ext_samp_rate_sel) | |
| { | |
| int ret = 0; | |
| mutex_lock(&info->lock); | |
| /* Send a refresh and store values */ | |
| if (odpm_take_snapshot_instant_locked(info, false) < 0) { | |
| pr_err("odpm: cannot refresh to apply new sampling rate\n"); | |
| goto sampling_rate_store_exit; | |
| } | |
| if (type == ODPM_SAMPLING_RATE_INTERNAL || | |
| type == ODPM_SAMPLING_RATE_ALL) { | |
| ret = odpm_io_set_int_sampling_rate(info, int_samp_rate_sel); | |
| odpm_print_new_sampling_rate(info, ret, | |
| ODPM_SAMPLING_RATE_INTERNAL); | |
| } | |
| if (type == ODPM_SAMPLING_RATE_EXTERNAL || | |
| type == ODPM_SAMPLING_RATE_ALL) { | |
| ret = odpm_io_set_ext_sampling_rate(info, ext_samp_rate_sel); | |
| odpm_print_new_sampling_rate(info, ret, | |
| ODPM_SAMPLING_RATE_EXTERNAL); | |
| } | |
| if (odpm_meter_sw_reset(info) != 0) { | |
| pr_err("odpm: meter_sw_reset failed\n"); | |
| } | |
| /* Send blank ASYNC, ignoring the latest set of data */ | |
| if (odpm_io_send_blank_async(info, &info->last_poll_ktime_boot_ns) < 0) | |
| pr_err("odpm: Could not send blank async when applying sampling rate\n"); | |
| sampling_rate_store_exit: | |
| odpm_reset_timer(info); | |
| mutex_unlock(&info->lock); | |
| } | |
| static ssize_t sampling_rate_show(struct device *dev, | |
| struct device_attribute *attr, char *buf) | |
| { | |
| struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| int i = info->chip.int_sampling_rate_i; | |
| u32 freq = info->chip.sampling_rate_int_uhz[i]; | |
| return scnprintf_sampling_rate(buf, PAGE_SIZE, freq); | |
| } | |
| static ssize_t sampling_rate_store(struct device *dev, | |
| struct device_attribute *attr, | |
| const char *buf, size_t count) | |
| { | |
| struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| int new_sampling_rate_i; | |
| int new_sampling_rate; | |
| int ret = odpm_sampling_rate_verify(buf); | |
| if (ret < 0) | |
| return ret; | |
| new_sampling_rate = ret; | |
| if (!odpm_match_int_sampling_rate(info, new_sampling_rate, | |
| &new_sampling_rate_i)) { | |
| pr_err("odpm: cannot match new sampling rate value; %d uHz\n", | |
| new_sampling_rate); | |
| return -EINVAL; | |
| } | |
| odpm_set_sampling_rate(info, ODPM_SAMPLING_RATE_INTERNAL, | |
| new_sampling_rate_i, EXT_FREQ_NONE); | |
| return count; | |
| } | |
| static ssize_t ext_sampling_rate_show(struct device *dev, | |
| struct device_attribute *attr, char *buf) | |
| { | |
| struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| int i = info->chip.ext_sampling_rate_i; | |
| u32 freq = info->chip.sampling_rate_ext_uhz[i]; | |
| return scnprintf_sampling_rate(buf, PAGE_SIZE, freq); | |
| } | |
| static ssize_t ext_sampling_rate_store(struct device *dev, | |
| struct device_attribute *attr, | |
| const char *buf, size_t count) | |
| { | |
| struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| int new_sampling_rate_i; | |
| int new_sampling_rate; | |
| int ret = odpm_sampling_rate_verify(buf); | |
| if (ret < 0) | |
| return ret; | |
| new_sampling_rate = ret; | |
| if (!odpm_match_ext_sampling_rate(info, new_sampling_rate, | |
| &new_sampling_rate_i)) { | |
| pr_err("odpm: cannot match new sampling rate value; %d uHz\n", | |
| new_sampling_rate); | |
| return -EINVAL; | |
| } | |
| odpm_set_sampling_rate(info, ODPM_SAMPLING_RATE_EXTERNAL, | |
| INT_FREQ_NONE, new_sampling_rate_i); | |
| return count; | |
| } | |
| static ssize_t energy_value_show(struct device *dev, | |
| struct device_attribute *attr, char *buf) | |
| { | |
| struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| ssize_t count = 0; | |
| int ch; | |
| /* take snapshot */ | |
| mutex_lock(&info->lock); | |
| if (odpm_take_snapshot_locked(info) < 0) { | |
| pr_err("odpm: cannot retrieve energy values\n"); | |
| goto energy_value_show_exit; | |
| } | |
| /** | |
| * Output format: | |
| * t=<Measurement timestamp, ms> | |
| * CH<N>(T=<Duration, ms>)[<Schematic name>], <Accumulated Energy, uWs> | |
| */ | |
| count += scnprintf(buf + count, PAGE_SIZE - count, "t=%llu\n", | |
| info->chip.acc_timestamp_ms); | |
| for (ch = 0; ch < ODPM_CHANNEL_MAX; ch++) { | |
| int rail_i = info->channels[ch].rail_i; | |
| u64 start_ms = info->channels[ch].measurement_start_ms; | |
| u64 duration_ms = 0; | |
| if (info->chip.acc_timestamp_ms >= start_ms) | |
| duration_ms = info->chip.acc_timestamp_ms - start_ms; | |
| count += scnprintf(buf + count, PAGE_SIZE - count, | |
| "CH%d(T=%llu)[%s], %llu\n", ch, | |
| duration_ms, | |
| info->chip.rails[rail_i].schematic_name, | |
| info->channels[ch].acc_power_uW_sec); | |
| } | |
| energy_value_show_exit: | |
| mutex_unlock(&info->lock); | |
| return count; | |
| } | |
| static ssize_t available_rails_show(struct device *dev, | |
| struct device_attribute *attr, char *buf) | |
| { | |
| struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| int rail_i; | |
| ssize_t count = 0; | |
| /* No locking needed for rail specific information */ | |
| for (rail_i = 0; rail_i < info->chip.num_rails; rail_i++) { | |
| struct odpm_rail_data *rail = &info->chip.rails[rail_i]; | |
| count += scnprintf(buf + count, PAGE_SIZE - count, | |
| "%s(%s):%s\n", rail->name, | |
| rail->schematic_name, rail->subsys_name); | |
| } | |
| return count; | |
| } | |
| static ssize_t enabled_rails_show(struct device *dev, | |
| struct device_attribute *attr, char *buf) | |
| { | |
| struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| ssize_t count = 0; | |
| int ch; | |
| mutex_lock(&info->lock); | |
| for (ch = 0; ch < ODPM_CHANNEL_MAX; ch++) { | |
| if (info->channels[ch].enabled) { | |
| size_t rail_i = info->channels[ch].rail_i; | |
| struct odpm_rail_data *rail = &info->chip.rails[rail_i]; | |
| count += scnprintf(buf + count, PAGE_SIZE - count, | |
| "CH%d[%s]:%s\n", ch, | |
| rail->schematic_name, | |
| rail->subsys_name); | |
| } | |
| } | |
| mutex_unlock(&info->lock); | |
| return count; | |
| } | |
| static ssize_t enabled_rails_store(struct device *dev, | |
| struct device_attribute *attr, | |
| const char *buf, size_t count) | |
| { | |
| struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| char rail_name[ODPM_RAIL_NAME_STR_LEN_MAX + 1]; | |
| int channel = -1; | |
| bool channel_valid = false; | |
| int rail_i; | |
| int scan_result; | |
| int current_rail; | |
| int new_rail; | |
| u64 timestamp_ns; | |
| if (strcmp(buf, "CONFIG_COMPLETE") == 0) { | |
| /** | |
| * External configuration applied, so reset the timestamps and | |
| * measurements. This can only be done once from boot. | |
| */ | |
| mutex_lock(&info->lock); | |
| if (!info->chip.rx_ext_config_confirmation) { | |
| info->chip.rx_ext_config_confirmation = true; | |
| odpm_reset_timer(info); | |
| if (odpm_meter_sw_reset(info) != 0) { | |
| pr_err("odpm: meter_sw_reset failed\n"); | |
| } | |
| if (odpm_configure_start_measurement(info)) | |
| pr_err("odpm: Failed to start measurement\n"); | |
| else | |
| pr_info("odpm: Boot config complete!\n"); | |
| } else { | |
| pr_err("odpm: Boot config already applied\n"); | |
| } | |
| mutex_unlock(&info->lock); | |
| return count; | |
| } | |
| scan_result = sscanf(buf, "CH%d=%" xstr(ODPM_RAIL_NAME_STR_LEN_MAX) "s", | |
| &channel, rail_name); | |
| if (scan_result == 2 && channel >= 0 && channel < ODPM_CHANNEL_MAX) { | |
| for (rail_i = 0; rail_i < info->chip.num_rails; rail_i++) { | |
| if (strcmp(info->chip.rails[rail_i].name, rail_name) == | |
| 0) { | |
| channel_valid = true; | |
| break; | |
| } | |
| } | |
| } | |
| if (!channel_valid) | |
| return -EINVAL; /* The buffer syntax was invalid */ | |
| mutex_lock(&info->lock); | |
| new_rail = rail_i; | |
| current_rail = info->channels[channel].rail_i; | |
| if (new_rail == current_rail) { | |
| /* Do not apply rail selection if the same rail is being | |
| * replaced. | |
| */ | |
| goto enabled_rails_store_exit; | |
| } | |
| /* Send a refresh and store values for old rails */ | |
| if (odpm_take_snapshot_locked(info) < 0) { | |
| pr_err("odpm: cannot refresh values to swap rails\n"); | |
| goto enabled_rails_store_exit; | |
| } | |
| odpm_reset_timer(info); | |
| /* Capture measurement time for current rail */ | |
| info->chip.rails[current_rail].measurement_start_ms_cached = | |
| info->channels[channel].measurement_start_ms; | |
| info->chip.rails[current_rail].measurement_stop_ms = | |
| info->chip.acc_timestamp_ms; | |
| /* Reset stored energy for channel */ | |
| info->chip.rails[current_rail].acc_power_uW_sec_cached = | |
| info->channels[channel].acc_power_uW_sec; | |
| info->channels[channel].acc_power_uW_sec = 0; | |
| /* Assign new rail to channel */ | |
| info->channels[channel].rail_i = new_rail; | |
| /* Rail is swapped, update en bits */ | |
| odpm_io_update_bucken_enable_bits(info, false /* on_sleep */); | |
| /* Update rail muxsel */ | |
| odpm_io_set_channel(info, channel); | |
| /* Send blank ASYNC, ignoring the last data set, if we've already init. | |
| * If we haven't init, there's a high probability that this is boot-time | |
| * rail selection, which will clear with CONFIG_COMPLETE. | |
| */ | |
| if (!info->chip.rx_ext_config_confirmation) { | |
| odpm_io_send_blank_async(info, ×tamp_ns); | |
| info->last_poll_ktime_boot_ns = timestamp_ns; | |
| } else { | |
| timestamp_ns = ktime_get_boottime_ns(); | |
| } | |
| /* Record measurement start time / reset stop time */ | |
| info->channels[channel].measurement_start_ms = to_ms(timestamp_ns); | |
| info->chip.rails[new_rail].measurement_stop_ms = 0; | |
| enabled_rails_store_exit: | |
| mutex_unlock(&info->lock); | |
| return count; | |
| } | |
| static ssize_t measurement_start_show(struct device *dev, | |
| struct device_attribute *attr, char *buf) | |
| { | |
| struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| int ch; | |
| ssize_t count = 0; | |
| mutex_lock(&info->lock); | |
| for (ch = 0; ch < ODPM_CHANNEL_MAX; ch++) { | |
| int rail_i = info->channels[ch].rail_i; | |
| if (info->channels[ch].enabled) { | |
| count += scnprintf(buf + count, PAGE_SIZE - count, | |
| "CH%d[%s], %llu\n", ch, | |
| info->chip.rails[rail_i].schematic_name, | |
| info->channels[ch].measurement_start_ms); | |
| } | |
| } | |
| mutex_unlock(&info->lock); | |
| return count; | |
| } | |
| static ssize_t measurement_stop_show(struct device *dev, | |
| struct device_attribute *attr, char *buf) | |
| { | |
| struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| int rail_i; | |
| ssize_t count = 0; | |
| mutex_lock(&info->lock); | |
| for (rail_i = 0; rail_i < info->chip.num_rails; rail_i++) { | |
| struct odpm_rail_data *rail = &info->chip.rails[rail_i]; | |
| if (rail->measurement_stop_ms != 0) { | |
| count += scnprintf(buf + count, PAGE_SIZE - count, | |
| "%s(%s), %llu, %llu, %llu\n", | |
| rail->name, rail->schematic_name, | |
| rail->measurement_start_ms_cached, | |
| rail->measurement_stop_ms, | |
| rail->acc_power_uW_sec_cached); | |
| } | |
| } | |
| mutex_unlock(&info->lock); | |
| return count; | |
| } | |
| void odpm_get_raw_lpf_values(struct odpm_info *info, enum s2mpg1415_meter_mode mode, | |
| u32 micro_unit[ODPM_CHANNEL_MAX]) | |
| { | |
| const int samp_rate_sel = info->chip.int_sampling_rate_i; | |
| u32 acquisition_time_us = s2mpg1415_meter_get_acquisition_time_us(samp_rate_sel); | |
| odpm_io_set_lpf_mode(info, mode); | |
| usleep_range(acquisition_time_us, acquisition_time_us + 100); | |
| odpm_io_get_lpf_data(info, micro_unit); | |
| } | |
| EXPORT_SYMBOL_GPL(odpm_get_raw_lpf_values); | |
| static void odpm_get_lpf_values(struct odpm_info *info, enum s2mpg1415_meter_mode mode, | |
| u64 micro_unit[ODPM_CHANNEL_MAX]) | |
| { | |
| int ch; | |
| u32 data[ODPM_CHANNEL_MAX]; | |
| odpm_get_raw_lpf_values(info, mode, data); | |
| for (ch = 0; ch < ODPM_CHANNEL_MAX; ch++) { | |
| const int rail_i = info->channels[ch].rail_i; | |
| u32 milli_res_iq30 = odpm_get_resolution_milli_iq30(info, rail_i, mode); | |
| u32 rail_data = data[ch]; /* 21-bits */ | |
| u64 milli_unit_iq30 = (u64)rail_data * milli_res_iq30; | |
| /* | |
| * As the data is max 21 bits, we can convert to micro without | |
| * possible overflow (2^(32-21) = 2048, 2048 > 1000). | |
| */ | |
| u64 micro_unit_iq30 = milli_unit_iq30 * 1000; | |
| micro_unit[ch] = (u32)_IQ30_to_int(micro_unit_iq30); | |
| } | |
| } | |
| static ssize_t odpm_show_lpf_values(struct device *dev, | |
| struct device_attribute *attr, char *buf, | |
| enum s2mpg1415_meter_mode mode) | |
| { | |
| struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| ssize_t count = 0; | |
| int ch; | |
| u64 micro_unit[ODPM_CHANNEL_MAX]; | |
| mutex_lock(&info->lock); | |
| odpm_get_lpf_values(info, mode, micro_unit); | |
| count += scnprintf(buf + count, PAGE_SIZE - count, "t=%lld\n", | |
| to_ms(ktime_get_boottime_ns())); | |
| for (ch = 0; ch < ODPM_CHANNEL_MAX; ch++) { | |
| int rail_i = info->channels[ch].rail_i; | |
| count += scnprintf(buf + count, PAGE_SIZE - count, | |
| "CH%d[%s], %lld\n", ch, | |
| info->chip.rails[rail_i].schematic_name, | |
| micro_unit[ch]); | |
| } | |
| mutex_unlock(&info->lock); | |
| return count; | |
| } | |
| /* | |
| * Output format: | |
| * t=<Measurement timestamp, ms> | |
| * CH<N>[<Schematic name>], <LPF power, uW> | |
| */ | |
| static ssize_t lpf_power_show(struct device *dev, | |
| struct device_attribute *attr, char *buf) | |
| { | |
| return odpm_show_lpf_values(dev, attr, buf, S2MPG1415_METER_POWER); | |
| } | |
| static ssize_t lpf_power_store(struct device *dev, | |
| struct device_attribute *attr, | |
| const char *buf, size_t count) | |
| { | |
| struct iio_dev *indio_dev = dev_to_iio_dev(dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| int channel = -1; | |
| int scan_result; | |
| u32 coefficient_scan; | |
| u8 coefficient; | |
| scan_result = sscanf(buf, "CH%d=%x", &channel, &coefficient_scan); | |
| if (!(scan_result == 2 && channel >= 0 && channel < ODPM_CHANNEL_MAX)) | |
| return -EINVAL; /* The buffer syntax was invalid */ | |
| coefficient = (u8)coefficient_scan; | |
| mutex_lock(&info->lock); | |
| odpm_io_write_lpf_reg(info, channel, coefficient); | |
| mutex_unlock(&info->lock); | |
| return count; | |
| } | |
| /* | |
| * Output format: | |
| * t=<Measurement timestamp, ms> | |
| * CH<N>[<Schematic name>], <LPF current, uA> | |
| */ | |
| static ssize_t lpf_current_show(struct device *dev, | |
| struct device_attribute *attr, char *buf) | |
| { | |
| return odpm_show_lpf_values(dev, attr, buf, S2MPG1415_METER_CURRENT); | |
| } | |
| static ssize_t lpf_current_store(struct device *dev, | |
| struct device_attribute *attr, | |
| const char *buf, size_t count) | |
| { | |
| return lpf_power_store(dev, attr, buf, count); | |
| } | |
| static IIO_DEVICE_ATTR_RW(ext_sampling_rate, 0); | |
| static IIO_DEVICE_ATTR_RW(sampling_rate, 0); | |
| static IIO_DEVICE_ATTR_RO(energy_value, 0); | |
| static IIO_DEVICE_ATTR_RO(available_rails, 0); | |
| static IIO_DEVICE_ATTR_RW(enabled_rails, 0); | |
| static IIO_DEVICE_ATTR_RO(measurement_start, 0); | |
| static IIO_DEVICE_ATTR_RO(measurement_stop, 0); | |
| static IIO_DEVICE_ATTR_RW(lpf_power, 0); | |
| static IIO_DEVICE_ATTR_RW(lpf_current, 0); | |
| /** | |
| * TODO(stayfan): b/156109194 | |
| * Add attributes that allow you to dynamically configure channels | |
| * You should be able to configure all 8 channels and initiate the config change | |
| * from a sysfs file, and the previous values from the static device tree config | |
| * should be buffered while the dynamic config is in place. There should also be | |
| * a sysfs file to revert back to the device tree config with the previous | |
| * accumulated values. | |
| */ | |
| #define ODPM_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) | |
| static struct attribute *odpm_custom_attributes[] = { | |
| ODPM_DEV_ATTR(sampling_rate), ODPM_DEV_ATTR(ext_sampling_rate), | |
| ODPM_DEV_ATTR(energy_value), ODPM_DEV_ATTR(available_rails), | |
| ODPM_DEV_ATTR(enabled_rails), ODPM_DEV_ATTR(measurement_start), | |
| ODPM_DEV_ATTR(measurement_stop), ODPM_DEV_ATTR(lpf_power), | |
| ODPM_DEV_ATTR(lpf_current), | |
| NULL | |
| }; | |
| static const struct attribute_group odpm_group = { | |
| .attrs = odpm_custom_attributes, | |
| }; | |
| /* TODO(stayfan): b/156108825 | |
| * Understand the requirements for read_raw/write_raw | |
| * Populate if these functions are useful to userland | |
| */ | |
| static int odpm_read_raw(struct iio_dev *indio_dev, | |
| struct iio_chan_spec const *chan, int *val, int *val2, | |
| long mask) | |
| { | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| const int rail_i = info->channels[chan->channel].rail_i; | |
| int ret = 0; | |
| u64 milli_res_iq30; | |
| switch (mask) { | |
| case IIO_CHAN_INFO_AVERAGE_RAW: | |
| switch (chan->type) { | |
| case IIO_ENERGY: | |
| break; | |
| default: | |
| break; | |
| } | |
| break; | |
| case IIO_CHAN_INFO_SCALE: | |
| switch (chan->type) { | |
| case IIO_CURRENT: | |
| mutex_lock(&info->lock); | |
| milli_res_iq30 = odpm_get_resolution_milli_iq30(info, | |
| rail_i, | |
| S2MPG1415_METER_CURRENT); | |
| mutex_unlock(&info->lock); | |
| *val = (u32)_IQ30_to_int(milli_res_iq30); | |
| *val2 = (u32)_IQ30_to_int(milli_res_iq30 * PICO_PER_MILLI) % PICO_PER_MILLI; | |
| return IIO_VAL_INT_PLUS_NANO; | |
| case IIO_POWER: | |
| mutex_lock(&info->lock); | |
| milli_res_iq30 = odpm_get_resolution_milli_iq30(info, | |
| rail_i, | |
| S2MPG1415_METER_POWER); | |
| mutex_unlock(&info->lock); | |
| *val = (u32)_IQ30_to_int(milli_res_iq30); | |
| *val2 = (u32)_IQ30_to_int(milli_res_iq30 * PICO_PER_MILLI) % PICO_PER_MILLI; | |
| return IIO_VAL_INT_PLUS_NANO; | |
| default: | |
| break; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| return ret; | |
| } | |
| static int odpm_write_raw(struct iio_dev *indio_dev, | |
| struct iio_chan_spec const *chan, int val, int val2, | |
| long mask) | |
| { | |
| int ret = -EINVAL; | |
| if (iio_buffer_enabled(indio_dev)) | |
| return -EBUSY; | |
| return ret; | |
| } | |
| static const struct iio_info odpm_iio_info = { | |
| .attrs = &odpm_group, | |
| .read_raw = odpm_read_raw, | |
| .write_raw = odpm_write_raw, | |
| }; | |
| static int odpm_remove(struct platform_device *pdev) | |
| { | |
| struct iio_dev *indio_dev = dev_get_drvdata(&pdev->dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| int ret = 0; | |
| if (info->work_queue) { | |
| ret = alarm_cancel(&info->alarmtimer_refresh); | |
| if (ret < 0) { | |
| pr_err("odpm: cannot delete the refresh timer\n"); | |
| ret = -EINVAL; | |
| } | |
| cancel_work_sync(&info->work_refresh); | |
| flush_workqueue(info->work_queue); | |
| destroy_workqueue(info->work_queue); | |
| } | |
| /* free the channel attributes memory */ | |
| if (indio_dev->channels) | |
| indio_dev->channels = NULL; | |
| if (info->ws) | |
| wakeup_source_unregister(info->ws); | |
| return ret; | |
| } | |
| static void odpm_probe_init_device_specific(struct odpm_info *info, int id) | |
| { | |
| info->chip.hw_id = id; | |
| info->chip.sampling_rate_int_uhz = s2mpg1415_int_sample_rate_uhz; | |
| info->chip.sampling_rate_int_count = INT_FREQ_COUNT; | |
| info->chip.sampling_rate_ext_uhz = s2mpg1415_ext_sample_rate_uhz; | |
| info->chip.sampling_rate_ext_count = EXT_FREQ_COUNT; | |
| switch (id) { | |
| case ID_S2MPG14: { | |
| struct s2mpg14_meter *meter = info->meter; | |
| struct s2mpg14_dev *pmic = dev_get_drvdata(meter->dev->parent); | |
| struct s2mpg14_platform_data *pdata = dev_get_platdata(pmic->dev); | |
| pdata->meter = info; | |
| info->chip.hw_rev = pmic->pmic_rev; | |
| info->i2c = meter->i2c; | |
| info->meter_lock = &meter->meter_lock; | |
| } break; | |
| case ID_S2MPG15: { | |
| struct s2mpg15_meter *meter = info->meter; | |
| struct s2mpg15_dev *pmic = dev_get_drvdata(meter->dev->parent); | |
| struct s2mpg15_platform_data *pdata = dev_get_platdata(pmic->dev); | |
| pdata->meter = info; | |
| info->chip.hw_rev = pmic->pmic_rev; | |
| info->i2c = meter->i2c; | |
| info->meter_lock = &meter->meter_lock; | |
| } break; | |
| } | |
| info->chip.rx_ext_config_confirmation = false; | |
| } | |
| static int odpm_probe(struct platform_device *pdev) | |
| { | |
| struct odpm_info *odpm_info; | |
| struct iio_dev *indio_dev; | |
| int ret; | |
| void *iodev; | |
| pr_info("odpm: %s: init\n", pdev->name); | |
| /* Allocate the memory for our private structure | |
| * related to the chip info structure | |
| */ | |
| indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*odpm_info)); | |
| if (!indio_dev) { | |
| pr_err("odpm: Could not allocate device!\n"); | |
| return -ENOMEM; | |
| } | |
| platform_set_drvdata(pdev, indio_dev); | |
| /* Point our chip info structure towards | |
| * the address freshly allocated | |
| */ | |
| odpm_info = iio_priv(indio_dev); | |
| /* Make the link between the parent driver and the IIO */ | |
| iodev = dev_get_drvdata(pdev->dev.parent); | |
| if (!iodev) { | |
| pr_err("odpm: Could not get parent data!\n"); | |
| return -ENODEV; | |
| } | |
| odpm_info->meter = iodev; | |
| if (!pdev->id_entry) { | |
| pr_err("odpm: Could not find id_entry!\n"); | |
| odpm_remove(pdev); | |
| return -ENODEV; | |
| } | |
| if (pdev->id_entry->driver_data >= ID_COUNT) { | |
| pr_err("odpm: Could not identify driver!\n"); | |
| odpm_remove(pdev); | |
| return -ENODEV; | |
| } | |
| /* Init device-specific data */ | |
| odpm_probe_init_device_specific(odpm_info, pdev->id_entry->driver_data); | |
| /* Read device tree data */ | |
| if (!pdev->dev.parent->parent || | |
| (!of_get_next_child(pdev->dev.parent->parent->of_node, NULL))) { | |
| pr_err("odpm: DT does not exist!\n"); | |
| odpm_remove(pdev); | |
| return -EINVAL; | |
| } | |
| /* Note: This function will call devm_kzalloc() in order to | |
| * dynamically allocate memory for the rails | |
| */ | |
| ret = odpm_parse_dt(&pdev->dev, odpm_info); | |
| if (ret != 0) { | |
| pr_err("odpm: DT parsing error!\n"); | |
| odpm_remove(pdev); | |
| return ret; | |
| } | |
| /* Initialize other data in odpm_info */ | |
| /* Configure ODPM channels based on device tree input */ | |
| ret = odpm_configure_chip(odpm_info); | |
| if (ret < 0) { | |
| odpm_remove(pdev); | |
| return ret; | |
| } | |
| /* Start measurement of default rails */ | |
| if (odpm_configure_start_measurement(odpm_info)) | |
| pr_err("odpm: Failed to start measurement at probe\n"); | |
| /* Configure work to kick off every XHz */ | |
| odpm_periodic_refresh_setup(odpm_info); | |
| /* Setup IIO */ | |
| indio_dev->channels = s2mpg1415_single_channel; | |
| indio_dev->num_channels = ARRAY_SIZE(s2mpg1415_single_channel); | |
| indio_dev->info = &odpm_iio_info; | |
| indio_dev->name = pdev->name; | |
| indio_dev->dev.parent = &pdev->dev; | |
| indio_dev->modes = INDIO_DIRECT_MODE; | |
| ret = devm_iio_device_register(&pdev->dev, indio_dev); | |
| if (ret < 0) { | |
| odpm_remove(pdev); | |
| return ret; | |
| } | |
| device_enable_async_suspend(&pdev->dev); | |
| odpm_info->ws = wakeup_source_register(&pdev->dev, odpm_info->chip.name); | |
| if (odpm_info->ws == NULL) { | |
| pr_err("odpm: wakelock register fail\n"); | |
| } | |
| mutex_init(&odpm_info->lock); | |
| pr_info("odpm: %s: init completed\n", pdev->name); | |
| smp_store_release(&odpm_info->ready, true); | |
| return ret; | |
| } | |
| static int odpm_suspend(struct platform_device *pdev, pm_message_t state) | |
| { | |
| struct iio_dev *indio_dev = dev_get_drvdata(&pdev->dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| odpm_suspend_resume_operations(info, true /* suspend */); | |
| return 0; | |
| } | |
| static int odpm_resume(struct platform_device *pdev) | |
| { | |
| struct iio_dev *indio_dev = dev_get_drvdata(&pdev->dev); | |
| struct odpm_info *info = iio_priv(indio_dev); | |
| odpm_suspend_resume_operations(info, false /* suspend */); | |
| return 0; | |
| } | |
| static const struct platform_device_id odpm_id[] = { | |
| { "s2mpg14-odpm", ID_S2MPG14 }, | |
| { "s2mpg15-odpm", ID_S2MPG15 }, | |
| {}, | |
| }; | |
| MODULE_DEVICE_TABLE(platform, odpm_id); | |
| static struct platform_driver odpm_driver = { | |
| .driver = { | |
| .name = "odpm", | |
| .owner = THIS_MODULE, | |
| }, | |
| .probe = odpm_probe, | |
| .remove = odpm_remove, | |
| .suspend = odpm_suspend, | |
| .resume = odpm_resume, | |
| .id_table = odpm_id, | |
| }; | |
| static int __init odpm_init(void) | |
| { | |
| return platform_driver_register(&odpm_driver); | |
| } | |
| subsys_initcall(odpm_init); | |
| static void __exit odpm_exit(void) | |
| { | |
| platform_driver_unregister(&odpm_driver); | |
| } | |
| module_exit(odpm_exit); | |
| MODULE_DESCRIPTION("IIO ODPM Driver"); | |
| MODULE_AUTHOR("Stephane Lee <[email protected]>"); | |
| MODULE_LICENSE("GPL"); |