| // SPDX-License-Identifier: GPL-2.0-only |
| /* |
| * ROHM ADC driver for BD79124 ADC/GPO device |
| * https://fscdn.rohm.com/en/products/databook/datasheet/ic/data_converter/dac/bd79124muf-c-e.pdf |
| * |
| * Copyright (c) 2025, ROHM Semiconductor. |
| */ |
| |
| #include <linux/array_size.h> |
| #include <linux/bitfield.h> |
| #include <linux/bitmap.h> |
| #include <linux/bits.h> |
| #include <linux/device.h> |
| #include <linux/delay.h> |
| #include <linux/devm-helpers.h> |
| #include <linux/err.h> |
| #include <linux/gpio/driver.h> |
| #include <linux/i2c.h> |
| #include <linux/interrupt.h> |
| #include <linux/irqreturn.h> |
| #include <linux/module.h> |
| #include <linux/mod_devicetable.h> |
| #include <linux/regmap.h> |
| #include <linux/types.h> |
| |
| #include <asm/byteorder.h> |
| |
| #include <linux/iio/events.h> |
| #include <linux/iio/iio.h> |
| #include <linux/iio/adc-helpers.h> |
| |
| #define BD79124_I2C_MULTI_READ 0x30 |
| #define BD79124_I2C_MULTI_WRITE 0x28 |
| #define BD79124_REG_MAX 0xaf |
| |
| #define BD79124_REG_SYSTEM_STATUS 0x00 |
| #define BD79124_REG_GEN_CFG 0x01 |
| #define BD79124_REG_OPMODE_CFG 0x04 |
| #define BD79124_REG_PINCFG 0x05 |
| #define BD79124_REG_GPO_VAL 0x0B |
| #define BD79124_REG_SEQ_CFG 0x10 |
| #define BD79124_REG_MANUAL_CHANNELS 0x11 |
| #define BD79124_REG_AUTO_CHANNELS 0x12 |
| #define BD79124_REG_ALERT_CH_SEL 0x14 |
| #define BD79124_REG_EVENT_FLAG 0x18 |
| #define BD79124_REG_EVENT_FLAG_HI 0x1a |
| #define BD79124_REG_EVENT_FLAG_LO 0x1c |
| #define BD79124_REG_HYSTERESIS_CH0 0x20 |
| #define BD79124_REG_EVENTCOUNT_CH0 0x22 |
| #define BD79124_REG_RECENT_CH0_LSB 0xa0 |
| #define BD79124_REG_RECENT_CH7_MSB 0xaf |
| |
| #define BD79124_ADC_BITS 12 |
| |
| /* Masks for the BD79124_REG_OPMODE_CFG */ |
| #define BD79124_MSK_CONV_MODE GENMASK(6, 5) |
| #define BD79124_CONV_MODE_MANSEQ 0 |
| #define BD79124_CONV_MODE_AUTO 1 |
| #define BD79124_MSK_AUTO_INTERVAL GENMASK(1, 0) |
| #define BD79124_INTERVAL_750_US 0 |
| |
| /* Masks for the BD79124_REG_GEN_CFG */ |
| #define BD79124_MSK_DWC_EN BIT(4) |
| #define BD79124_MSK_STATS_EN BIT(5) |
| |
| /* Masks for the BD79124_REG_SEQ_CFG */ |
| #define BD79124_MSK_SEQ_START BIT(4) |
| #define BD79124_MSK_SEQ_MODE GENMASK(1, 0) |
| #define BD79124_MSK_SEQ_MANUAL 0 |
| #define BD79124_MSK_SEQ_SEQ 1 |
| |
| #define BD79124_MSK_HYSTERESIS GENMASK(3, 0) |
| #define BD79124_LOW_LIMIT_MIN 0 |
| #define BD79124_HIGH_LIMIT_MAX GENMASK(11, 0) |
| |
| /* |
| * The high limit, low limit and last measurement result are each stored in |
| * 2 consequtive registers. 4 bits are in the high bits of the first register |
| * and 8 bits in the next register. |
| * |
| * These macros return the address of the first reg for the given channel. |
| */ |
| #define BD79124_GET_HIGH_LIMIT_REG(ch) (BD79124_REG_HYSTERESIS_CH0 + (ch) * 4) |
| #define BD79124_GET_LOW_LIMIT_REG(ch) (BD79124_REG_EVENTCOUNT_CH0 + (ch) * 4) |
| #define BD79124_GET_LIMIT_REG(ch, dir) ((dir) == IIO_EV_DIR_RISING ? \ |
| BD79124_GET_HIGH_LIMIT_REG(ch) : BD79124_GET_LOW_LIMIT_REG(ch)) |
| #define BD79124_GET_RECENT_RES_REG(ch) (BD79124_REG_RECENT_CH0_LSB + (ch) * 2) |
| |
| /* |
| * The hysteresis for a channel is stored in the same register where the |
| * 4 bits of high limit reside. |
| */ |
| #define BD79124_GET_HYSTERESIS_REG(ch) BD79124_GET_HIGH_LIMIT_REG(ch) |
| |
| #define BD79124_MAX_NUM_CHANNELS 8 |
| |
| struct bd79124_data { |
| s64 timestamp; |
| struct regmap *map; |
| struct device *dev; |
| int vmax; |
| /* |
| * Keep measurement status so read_raw() knows if the measurement needs |
| * to be started. |
| */ |
| int alarm_monitored[BD79124_MAX_NUM_CHANNELS]; |
| /* |
| * The BD79124 does not allow disabling/enabling limit separately for |
| * one direction only. Hence, we do the disabling by changing the limit |
| * to maximum/minimum measurable value. This means we need to cache |
| * the limit in order to maintain it over the time limit is disabled. |
| */ |
| u16 alarm_r_limit[BD79124_MAX_NUM_CHANNELS]; |
| u16 alarm_f_limit[BD79124_MAX_NUM_CHANNELS]; |
| /* Bitmask of disabled events (for rate limiting) for each channel. */ |
| int alarm_suppressed[BD79124_MAX_NUM_CHANNELS]; |
| /* |
| * The BD79124 is configured to run the measurements in the background. |
| * This is done for the event monitoring as well as for the read_raw(). |
| * Protect the measurement starting/stopping using a mutex. |
| */ |
| struct mutex mutex; |
| struct delayed_work alm_enable_work; |
| struct gpio_chip gc; |
| u8 gpio_valid_mask; |
| }; |
| |
| static const struct regmap_range bd79124_ro_ranges[] = { |
| { |
| .range_min = BD79124_REG_EVENT_FLAG, |
| .range_max = BD79124_REG_EVENT_FLAG, |
| }, { |
| .range_min = BD79124_REG_RECENT_CH0_LSB, |
| .range_max = BD79124_REG_RECENT_CH7_MSB, |
| }, |
| }; |
| |
| static const struct regmap_access_table bd79124_ro_regs = { |
| .no_ranges = &bd79124_ro_ranges[0], |
| .n_no_ranges = ARRAY_SIZE(bd79124_ro_ranges), |
| }; |
| |
| static const struct regmap_range bd79124_volatile_ranges[] = { |
| { |
| .range_min = BD79124_REG_RECENT_CH0_LSB, |
| .range_max = BD79124_REG_RECENT_CH7_MSB, |
| }, { |
| .range_min = BD79124_REG_EVENT_FLAG, |
| .range_max = BD79124_REG_EVENT_FLAG, |
| }, { |
| .range_min = BD79124_REG_EVENT_FLAG_HI, |
| .range_max = BD79124_REG_EVENT_FLAG_HI, |
| }, { |
| .range_min = BD79124_REG_EVENT_FLAG_LO, |
| .range_max = BD79124_REG_EVENT_FLAG_LO, |
| }, { |
| .range_min = BD79124_REG_SYSTEM_STATUS, |
| .range_max = BD79124_REG_SYSTEM_STATUS, |
| }, |
| }; |
| |
| static const struct regmap_access_table bd79124_volatile_regs = { |
| .yes_ranges = &bd79124_volatile_ranges[0], |
| .n_yes_ranges = ARRAY_SIZE(bd79124_volatile_ranges), |
| }; |
| |
| static const struct regmap_range bd79124_precious_ranges[] = { |
| { |
| .range_min = BD79124_REG_EVENT_FLAG_HI, |
| .range_max = BD79124_REG_EVENT_FLAG_HI, |
| }, { |
| .range_min = BD79124_REG_EVENT_FLAG_LO, |
| .range_max = BD79124_REG_EVENT_FLAG_LO, |
| }, |
| }; |
| |
| static const struct regmap_access_table bd79124_precious_regs = { |
| .yes_ranges = &bd79124_precious_ranges[0], |
| .n_yes_ranges = ARRAY_SIZE(bd79124_precious_ranges), |
| }; |
| |
| static const struct regmap_config bd79124_regmap = { |
| .reg_bits = 16, |
| .val_bits = 8, |
| .read_flag_mask = BD79124_I2C_MULTI_READ, |
| .write_flag_mask = BD79124_I2C_MULTI_WRITE, |
| .max_register = BD79124_REG_MAX, |
| .cache_type = REGCACHE_MAPLE, |
| .volatile_table = &bd79124_volatile_regs, |
| .wr_table = &bd79124_ro_regs, |
| .precious_table = &bd79124_precious_regs, |
| }; |
| |
| static int bd79124gpo_direction_get(struct gpio_chip *gc, unsigned int offset) |
| { |
| return GPIO_LINE_DIRECTION_OUT; |
| } |
| |
| static int bd79124gpo_set(struct gpio_chip *gc, unsigned int offset, int value) |
| { |
| struct bd79124_data *data = gpiochip_get_data(gc); |
| |
| return regmap_assign_bits(data->map, BD79124_REG_GPO_VAL, BIT(offset), |
| value); |
| } |
| |
| static int bd79124gpo_set_multiple(struct gpio_chip *gc, unsigned long *mask, |
| unsigned long *bits) |
| { |
| unsigned int all_gpos; |
| int ret; |
| struct bd79124_data *data = gpiochip_get_data(gc); |
| |
| /* |
| * Ensure all GPIOs in 'mask' are set to be GPIOs |
| * The valid_mask was not obeyed by the gpiolib in all cases prior the |
| * https://lore.kernel.org/all/cd5e067b80e1bb590027bc3bfa817e7f794f21c3.1741180097.git.mazziesaccount@gmail.com/ |
| * |
| * Keep this check here for a couple of cycles. |
| */ |
| ret = regmap_read(data->map, BD79124_REG_PINCFG, &all_gpos); |
| if (ret) |
| return ret; |
| |
| if (all_gpos ^ *mask) { |
| dev_dbg(data->dev, "Invalid mux config. Can't set value.\n"); |
| |
| return -EINVAL; |
| } |
| |
| return regmap_update_bits(data->map, BD79124_REG_GPO_VAL, *mask, *bits); |
| } |
| |
| static int bd79124_init_valid_mask(struct gpio_chip *gc, |
| unsigned long *valid_mask, |
| unsigned int ngpios) |
| { |
| struct bd79124_data *data = gpiochip_get_data(gc); |
| |
| *valid_mask = data->gpio_valid_mask; |
| |
| return 0; |
| } |
| |
| /* Template for GPIO chip */ |
| static const struct gpio_chip bd79124gpo_chip = { |
| .label = "bd79124-gpo", |
| .get_direction = bd79124gpo_direction_get, |
| .set = bd79124gpo_set, |
| .set_multiple = bd79124gpo_set_multiple, |
| .init_valid_mask = bd79124_init_valid_mask, |
| .can_sleep = true, |
| .ngpio = 8, |
| .base = -1, |
| }; |
| |
| struct bd79124_raw { |
| u8 val_bit3_0; /* Is set in high bits of the byte */ |
| u8 val_bit11_4; |
| }; |
| #define BD79124_RAW_TO_INT(r) ((r.val_bit11_4 << 4) | (r.val_bit3_0 >> 4)) |
| #define BD79124_INT_TO_RAW(val) { \ |
| .val_bit11_4 = (val) >> 4, \ |
| .val_bit3_0 = (val) << 4, \ |
| } |
| |
| /* |
| * The high and low limits as well as the recent result values are stored in |
| * the same way in 2 consequent registers. The first register contains 4 bits |
| * of the value. These bits are stored in the high bits [7:4] of register, but |
| * they represent the low bits [3:0] of the value. |
| * The value bits [11:4] are stored in the next register. |
| * |
| * Read data from register and convert to integer. |
| */ |
| static int bd79124_read_reg_to_int(struct bd79124_data *data, int reg, |
| unsigned int *val) |
| { |
| int ret; |
| struct bd79124_raw raw; |
| |
| ret = regmap_bulk_read(data->map, reg, &raw, sizeof(raw)); |
| if (ret) { |
| dev_dbg(data->dev, "bulk_read failed %d\n", ret); |
| |
| return ret; |
| } |
| |
| *val = BD79124_RAW_TO_INT(raw); |
| |
| return 0; |
| } |
| |
| /* |
| * The high and low limits as well as the recent result values are stored in |
| * the same way in 2 consequent registers. The first register contains 4 bits |
| * of the value. These bits are stored in the high bits [7:4] of register, but |
| * they represent the low bits [3:0] of the value. |
| * The value bits [11:4] are stored in the next register. |
| * |
| * Convert the integer to register format and write it using rmw cycle. |
| */ |
| static int bd79124_write_int_to_reg(struct bd79124_data *data, int reg, |
| unsigned int val) |
| { |
| struct bd79124_raw raw = BD79124_INT_TO_RAW(val); |
| unsigned int tmp; |
| int ret; |
| |
| ret = regmap_read(data->map, reg, &tmp); |
| if (ret) |
| return ret; |
| |
| raw.val_bit3_0 |= (tmp & 0xf); |
| |
| return regmap_bulk_write(data->map, reg, &raw, sizeof(raw)); |
| } |
| |
| static const struct iio_event_spec bd79124_events[] = { |
| { |
| .type = IIO_EV_TYPE_THRESH, |
| .dir = IIO_EV_DIR_RISING, |
| .mask_separate = BIT(IIO_EV_INFO_VALUE) | |
| BIT(IIO_EV_INFO_ENABLE), |
| }, |
| { |
| .type = IIO_EV_TYPE_THRESH, |
| .dir = IIO_EV_DIR_FALLING, |
| .mask_separate = BIT(IIO_EV_INFO_VALUE) | |
| BIT(IIO_EV_INFO_ENABLE), |
| }, |
| { |
| .type = IIO_EV_TYPE_THRESH, |
| .dir = IIO_EV_DIR_EITHER, |
| .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), |
| }, |
| }; |
| |
| static const struct iio_chan_spec bd79124_chan_template_noirq = { |
| .type = IIO_VOLTAGE, |
| .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
| .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), |
| .indexed = 1, |
| }; |
| |
| static const struct iio_chan_spec bd79124_chan_template = { |
| .type = IIO_VOLTAGE, |
| .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
| .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), |
| .indexed = 1, |
| .event_spec = bd79124_events, |
| .num_event_specs = ARRAY_SIZE(bd79124_events), |
| }; |
| |
| static int bd79124_read_event_value(struct iio_dev *iio_dev, |
| const struct iio_chan_spec *chan, |
| enum iio_event_type type, |
| enum iio_event_direction dir, |
| enum iio_event_info info, int *val, |
| int *val2) |
| { |
| struct bd79124_data *data = iio_priv(iio_dev); |
| int ret, reg; |
| |
| if (chan->channel >= BD79124_MAX_NUM_CHANNELS) |
| return -EINVAL; |
| |
| switch (info) { |
| case IIO_EV_INFO_VALUE: |
| if (dir == IIO_EV_DIR_RISING) |
| *val = data->alarm_r_limit[chan->channel]; |
| else if (dir == IIO_EV_DIR_FALLING) |
| *val = data->alarm_f_limit[chan->channel]; |
| else |
| return -EINVAL; |
| |
| return IIO_VAL_INT; |
| |
| case IIO_EV_INFO_HYSTERESIS: |
| reg = BD79124_GET_HYSTERESIS_REG(chan->channel); |
| ret = regmap_read(data->map, reg, val); |
| if (ret) |
| return ret; |
| |
| *val &= BD79124_MSK_HYSTERESIS; |
| /* |
| * The data-sheet says the hysteresis register value needs to be |
| * shifted left by 3. |
| */ |
| *val <<= 3; |
| |
| return IIO_VAL_INT; |
| |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int bd79124_start_measurement(struct bd79124_data *data, int chan) |
| { |
| unsigned int val, regval; |
| int ret; |
| |
| /* See if already started */ |
| ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, &val); |
| if (val & BIT(chan)) |
| return 0; |
| |
| /* |
| * The sequencer must be stopped when channels are added/removed from |
| * the list of the measured channels to ensure the new channel |
| * configuration is used. |
| */ |
| ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, |
| BD79124_MSK_SEQ_START); |
| if (ret) |
| return ret; |
| |
| ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, val | BIT(chan)); |
| if (ret) |
| return ret; |
| |
| ret = regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, |
| BD79124_MSK_SEQ_START); |
| if (ret) |
| return ret; |
| |
| /* |
| * Start the measurement at the background. Don't bother checking if |
| * it was started, regmap has cache. |
| */ |
| regval = FIELD_PREP(BD79124_MSK_CONV_MODE, BD79124_CONV_MODE_AUTO); |
| |
| return regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, |
| BD79124_MSK_CONV_MODE, regval); |
| } |
| |
| static int bd79124_stop_measurement(struct bd79124_data *data, int chan) |
| { |
| unsigned int enabled_chans; |
| int ret; |
| |
| /* See if already stopped */ |
| ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, &enabled_chans); |
| if (!(enabled_chans & BIT(chan))) |
| return 0; |
| |
| ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, |
| BD79124_MSK_SEQ_START); |
| |
| /* Clear the channel from the measured channels */ |
| enabled_chans &= ~BIT(chan); |
| ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, |
| enabled_chans); |
| if (ret) |
| return ret; |
| |
| /* |
| * Stop background conversion for power saving if it was the last |
| * channel. |
| */ |
| if (!enabled_chans) { |
| int regval = FIELD_PREP(BD79124_MSK_CONV_MODE, |
| BD79124_CONV_MODE_MANSEQ); |
| |
| ret = regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, |
| BD79124_MSK_CONV_MODE, regval); |
| if (ret) |
| return ret; |
| } |
| |
| return regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, |
| BD79124_MSK_SEQ_START); |
| } |
| |
| static int bd79124_read_event_config(struct iio_dev *iio_dev, |
| const struct iio_chan_spec *chan, |
| enum iio_event_type type, |
| enum iio_event_direction dir) |
| { |
| struct bd79124_data *data = iio_priv(iio_dev); |
| |
| if (chan->channel >= BD79124_MAX_NUM_CHANNELS) |
| return -EINVAL; |
| |
| return !!(data->alarm_monitored[chan->channel] & BIT(dir)); |
| } |
| |
| static int bd79124_disable_event(struct bd79124_data *data, |
| enum iio_event_direction dir, int channel) |
| { |
| int dir_bit = BIT(dir); |
| int reg; |
| unsigned int limit; |
| |
| guard(mutex)(&data->mutex); |
| |
| /* |
| * Set thresholds either to 0 or to 2^12 - 1 as appropriate to prevent |
| * alerts and thus disable event generation. |
| */ |
| if (dir == IIO_EV_DIR_RISING) { |
| reg = BD79124_GET_HIGH_LIMIT_REG(channel); |
| limit = BD79124_HIGH_LIMIT_MAX; |
| } else if (dir == IIO_EV_DIR_FALLING) { |
| reg = BD79124_GET_LOW_LIMIT_REG(channel); |
| limit = BD79124_LOW_LIMIT_MIN; |
| } else { |
| return -EINVAL; |
| } |
| |
| data->alarm_monitored[channel] &= ~dir_bit; |
| |
| /* |
| * Stop measurement if there is no more events to monitor. |
| * We don't bother checking the retval because the limit |
| * setting should in any case effectively disable the alarm. |
| */ |
| if (!data->alarm_monitored[channel]) { |
| bd79124_stop_measurement(data, channel); |
| regmap_clear_bits(data->map, BD79124_REG_ALERT_CH_SEL, |
| BIT(channel)); |
| } |
| |
| return bd79124_write_int_to_reg(data, reg, limit); |
| } |
| |
| static int bd79124_enable_event(struct bd79124_data *data, |
| enum iio_event_direction dir, |
| unsigned int channel) |
| { |
| int dir_bit = BIT(dir); |
| int reg, ret; |
| u16 *limit; |
| |
| guard(mutex)(&data->mutex); |
| ret = bd79124_start_measurement(data, channel); |
| if (ret) |
| return ret; |
| |
| data->alarm_monitored[channel] |= dir_bit; |
| |
| /* Add the channel to the list of monitored channels */ |
| ret = regmap_set_bits(data->map, BD79124_REG_ALERT_CH_SEL, BIT(channel)); |
| if (ret) |
| return ret; |
| |
| if (dir == IIO_EV_DIR_RISING) { |
| limit = &data->alarm_f_limit[channel]; |
| reg = BD79124_GET_HIGH_LIMIT_REG(channel); |
| } else { |
| limit = &data->alarm_f_limit[channel]; |
| reg = BD79124_GET_LOW_LIMIT_REG(channel); |
| } |
| /* |
| * Don't write the new limit to the hardware if we are in the |
| * rate-limit period. The timer which re-enables the event will set |
| * the limit. |
| */ |
| if (!(data->alarm_suppressed[channel] & dir_bit)) { |
| ret = bd79124_write_int_to_reg(data, reg, *limit); |
| if (ret) |
| return ret; |
| } |
| |
| /* |
| * Enable comparator. Trust the regmap cache, no need to check |
| * if it was already enabled. |
| * |
| * We could do this in the hw-init, but there may be users who |
| * never enable alarms and for them it makes sense to not |
| * enable the comparator at probe. |
| */ |
| return regmap_set_bits(data->map, BD79124_REG_GEN_CFG, |
| BD79124_MSK_DWC_EN); |
| } |
| |
| static int bd79124_write_event_config(struct iio_dev *iio_dev, |
| const struct iio_chan_spec *chan, |
| enum iio_event_type type, |
| enum iio_event_direction dir, bool state) |
| { |
| struct bd79124_data *data = iio_priv(iio_dev); |
| |
| if (chan->channel >= BD79124_MAX_NUM_CHANNELS) |
| return -EINVAL; |
| |
| if (state) |
| return bd79124_enable_event(data, dir, chan->channel); |
| |
| return bd79124_disable_event(data, dir, chan->channel); |
| } |
| |
| static int bd79124_write_event_value(struct iio_dev *iio_dev, |
| const struct iio_chan_spec *chan, |
| enum iio_event_type type, |
| enum iio_event_direction dir, |
| enum iio_event_info info, int val, |
| int val2) |
| { |
| struct bd79124_data *data = iio_priv(iio_dev); |
| int reg; |
| |
| if (chan->channel >= BD79124_MAX_NUM_CHANNELS) |
| return -EINVAL; |
| |
| switch (info) { |
| case IIO_EV_INFO_VALUE: |
| { |
| guard(mutex)(&data->mutex); |
| |
| if (dir == IIO_EV_DIR_RISING) { |
| data->alarm_r_limit[chan->channel] = val; |
| reg = BD79124_GET_HIGH_LIMIT_REG(chan->channel); |
| } else if (dir == IIO_EV_DIR_FALLING) { |
| data->alarm_f_limit[chan->channel] = val; |
| reg = BD79124_GET_LOW_LIMIT_REG(chan->channel); |
| } else { |
| return -EINVAL; |
| } |
| /* |
| * We don't want to enable the alarm if it is not enabled or |
| * if it is suppressed. In that case skip writing to the |
| * register. |
| */ |
| if (!(data->alarm_monitored[chan->channel] & BIT(dir)) || |
| data->alarm_suppressed[chan->channel] & BIT(dir)) |
| return 0; |
| |
| return bd79124_write_int_to_reg(data, reg, val); |
| } |
| case IIO_EV_INFO_HYSTERESIS: |
| reg = BD79124_GET_HYSTERESIS_REG(chan->channel); |
| val >>= 3; |
| |
| return regmap_update_bits(data->map, reg, BD79124_MSK_HYSTERESIS, |
| val); |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static int bd79124_single_chan_seq(struct bd79124_data *data, int chan, unsigned int *old) |
| { |
| int ret; |
| |
| ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, |
| BD79124_MSK_SEQ_START); |
| if (ret) |
| return ret; |
| |
| /* |
| * It may be we have some channels monitored for alarms so we want to |
| * cache the old config and return it when the single channel |
| * measurement has been completed. |
| */ |
| ret = regmap_read(data->map, BD79124_REG_AUTO_CHANNELS, old); |
| if (ret) |
| return ret; |
| |
| ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, BIT(chan)); |
| if (ret) |
| return ret; |
| |
| /* Restart the sequencer */ |
| return regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, |
| BD79124_MSK_SEQ_START); |
| } |
| |
| static int bd79124_single_chan_seq_end(struct bd79124_data *data, unsigned int old) |
| { |
| int ret; |
| |
| ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, |
| BD79124_MSK_SEQ_START); |
| if (ret) |
| return ret; |
| |
| ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, old); |
| if (ret) |
| return ret; |
| |
| return regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, |
| BD79124_MSK_SEQ_START); |
| } |
| |
| static int bd79124_read_raw(struct iio_dev *iio_dev, |
| struct iio_chan_spec const *chan, |
| int *val, int *val2, long m) |
| { |
| struct bd79124_data *data = iio_priv(iio_dev); |
| int ret; |
| |
| if (chan->channel >= BD79124_MAX_NUM_CHANNELS) |
| return -EINVAL; |
| |
| switch (m) { |
| case IIO_CHAN_INFO_RAW: |
| { |
| unsigned int old_chan_cfg, regval; |
| int tmp; |
| |
| guard(mutex)(&data->mutex); |
| |
| /* |
| * Start the automatic conversion. This is needed here if no |
| * events have been enabled. |
| */ |
| regval = FIELD_PREP(BD79124_MSK_CONV_MODE, |
| BD79124_CONV_MODE_AUTO); |
| ret = regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, |
| BD79124_MSK_CONV_MODE, regval); |
| if (ret) |
| return ret; |
| |
| ret = bd79124_single_chan_seq(data, chan->channel, &old_chan_cfg); |
| if (ret) |
| return ret; |
| |
| /* The maximum conversion time is 6 uS. */ |
| udelay(6); |
| |
| ret = bd79124_read_reg_to_int(data, |
| BD79124_GET_RECENT_RES_REG(chan->channel), val); |
| /* |
| * Return the old chan config even if data reading failed in |
| * order to re-enable the event monitoring. |
| */ |
| tmp = bd79124_single_chan_seq_end(data, old_chan_cfg); |
| if (tmp) |
| dev_err(data->dev, |
| "Failed to return config. Alarms may be disabled\n"); |
| |
| if (ret) |
| return ret; |
| |
| return IIO_VAL_INT; |
| } |
| case IIO_CHAN_INFO_SCALE: |
| *val = data->vmax / 1000; |
| *val2 = BD79124_ADC_BITS; |
| return IIO_VAL_FRACTIONAL_LOG2; |
| default: |
| return -EINVAL; |
| } |
| } |
| |
| static const struct iio_info bd79124_info = { |
| .read_raw = bd79124_read_raw, |
| .read_event_config = &bd79124_read_event_config, |
| .write_event_config = &bd79124_write_event_config, |
| .read_event_value = &bd79124_read_event_value, |
| .write_event_value = &bd79124_write_event_value, |
| }; |
| |
| static void bd79124_re_enable_lo(struct bd79124_data *data, unsigned int channel) |
| { |
| int ret, evbit = BIT(IIO_EV_DIR_FALLING); |
| |
| /* |
| * We should not re-enable the event if user has disabled it while |
| * rate-limiting was enabled. |
| */ |
| if (!(data->alarm_suppressed[channel] & evbit)) |
| return; |
| |
| data->alarm_suppressed[channel] &= ~evbit; |
| |
| if (!(data->alarm_monitored[channel] & evbit)) |
| return; |
| |
| ret = bd79124_write_int_to_reg(data, BD79124_GET_LOW_LIMIT_REG(channel), |
| data->alarm_f_limit[channel]); |
| if (ret) |
| dev_warn(data->dev, "Low limit enabling failed for channel%d\n", |
| channel); |
| } |
| |
| static void bd79124_re_enable_hi(struct bd79124_data *data, unsigned int channel) |
| { |
| int ret, evbit = BIT(IIO_EV_DIR_RISING); |
| |
| /* |
| * We should not re-enable the event if user has disabled it while |
| * rate-limiting was enabled. |
| */ |
| if (!(data->alarm_suppressed[channel] & evbit)) |
| return; |
| |
| data->alarm_suppressed[channel] &= ~evbit; |
| |
| if (!(data->alarm_monitored[channel] & evbit)) |
| return; |
| |
| ret = bd79124_write_int_to_reg(data, BD79124_GET_HIGH_LIMIT_REG(channel), |
| data->alarm_r_limit[channel]); |
| if (ret) |
| dev_warn(data->dev, "High limit enabling failed for channel%d\n", |
| channel); |
| } |
| |
| static void bd79124_alm_enable_worker(struct work_struct *work) |
| { |
| int i; |
| struct bd79124_data *data = container_of(work, struct bd79124_data, |
| alm_enable_work.work); |
| |
| /* Take the mutex so there is no race with user disabling the alarm */ |
| guard(mutex)(&data->mutex); |
| for (i = 0; i < BD79124_MAX_NUM_CHANNELS; i++) { |
| bd79124_re_enable_hi(data, i); |
| bd79124_re_enable_lo(data, i); |
| } |
| } |
| |
| static int __bd79124_event_ratelimit(struct bd79124_data *data, int reg, |
| unsigned int limit) |
| { |
| int ret; |
| |
| if (limit > BD79124_HIGH_LIMIT_MAX) |
| return -EINVAL; |
| |
| ret = bd79124_write_int_to_reg(data, reg, limit); |
| if (ret) |
| return ret; |
| |
| /* |
| * We use 1 sec 'grace period'. At the moment I see no reason to make |
| * this user configurable. We need an ABI for this if configuration is |
| * needed. |
| */ |
| schedule_delayed_work(&data->alm_enable_work, msecs_to_jiffies(1000)); |
| |
| return 0; |
| } |
| |
| static int bd79124_event_ratelimit_hi(struct bd79124_data *data, |
| unsigned int channel) |
| { |
| guard(mutex)(&data->mutex); |
| data->alarm_suppressed[channel] |= BIT(IIO_EV_DIR_RISING); |
| |
| return __bd79124_event_ratelimit(data, |
| BD79124_GET_HIGH_LIMIT_REG(channel), |
| BD79124_HIGH_LIMIT_MAX); |
| } |
| |
| static int bd79124_event_ratelimit_lo(struct bd79124_data *data, |
| unsigned int channel) |
| { |
| guard(mutex)(&data->mutex); |
| data->alarm_suppressed[channel] |= BIT(IIO_EV_DIR_FALLING); |
| |
| return __bd79124_event_ratelimit(data, |
| BD79124_GET_LOW_LIMIT_REG(channel), |
| BD79124_LOW_LIMIT_MIN); |
| } |
| |
| static irqreturn_t bd79124_event_handler(int irq, void *priv) |
| { |
| unsigned int i_hi, i_lo; |
| int i, ret; |
| struct iio_dev *iio_dev = priv; |
| struct bd79124_data *data = iio_priv(iio_dev); |
| |
| /* |
| * Return IRQ_NONE if bailing-out without acking. This allows the IRQ |
| * subsystem to disable the offending IRQ line if we get a hardware |
| * problem. This behaviour has saved my poor bottom a few times in the |
| * past as, instead of getting unusably unresponsive, the system has |
| * spilled out the magic words "...nobody cared". |
| */ |
| ret = regmap_read(data->map, BD79124_REG_EVENT_FLAG_HI, &i_hi); |
| if (ret) |
| return IRQ_NONE; |
| |
| ret = regmap_read(data->map, BD79124_REG_EVENT_FLAG_LO, &i_lo); |
| if (ret) |
| return IRQ_NONE; |
| |
| if (!i_lo && !i_hi) |
| return IRQ_NONE; |
| |
| for (i = 0; i < BD79124_MAX_NUM_CHANNELS; i++) { |
| u64 ecode; |
| |
| if (BIT(i) & i_hi) { |
| ecode = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, |
| IIO_EV_TYPE_THRESH, |
| IIO_EV_DIR_RISING); |
| |
| iio_push_event(iio_dev, ecode, data->timestamp); |
| /* |
| * The BD79124 keeps the IRQ asserted for as long as |
| * the voltage exceeds the threshold. It causes the IRQ |
| * to keep firing. |
| * |
| * Disable the event for the channel and schedule the |
| * re-enabling the event later to prevent storm of |
| * events. |
| */ |
| ret = bd79124_event_ratelimit_hi(data, i); |
| if (ret) |
| return IRQ_NONE; |
| } |
| if (BIT(i) & i_lo) { |
| ecode = IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i, |
| IIO_EV_TYPE_THRESH, |
| IIO_EV_DIR_FALLING); |
| |
| iio_push_event(iio_dev, ecode, data->timestamp); |
| ret = bd79124_event_ratelimit_lo(data, i); |
| if (ret) |
| return IRQ_NONE; |
| } |
| } |
| |
| ret = regmap_write(data->map, BD79124_REG_EVENT_FLAG_HI, i_hi); |
| if (ret) |
| return IRQ_NONE; |
| |
| ret = regmap_write(data->map, BD79124_REG_EVENT_FLAG_LO, i_lo); |
| if (ret) |
| return IRQ_NONE; |
| |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t bd79124_irq_handler(int irq, void *priv) |
| { |
| struct iio_dev *iio_dev = priv; |
| struct bd79124_data *data = iio_priv(iio_dev); |
| |
| data->timestamp = iio_get_time_ns(iio_dev); |
| |
| return IRQ_WAKE_THREAD; |
| } |
| |
| static int bd79124_chan_init(struct bd79124_data *data, int channel) |
| { |
| int ret; |
| |
| ret = regmap_write(data->map, BD79124_GET_HIGH_LIMIT_REG(channel), |
| BD79124_HIGH_LIMIT_MAX); |
| if (ret) |
| return ret; |
| |
| return regmap_write(data->map, BD79124_GET_LOW_LIMIT_REG(channel), |
| BD79124_LOW_LIMIT_MIN); |
| } |
| |
| static int bd79124_get_gpio_pins(const struct iio_chan_spec *cs, int num_channels) |
| { |
| int i, gpio_channels; |
| |
| /* |
| * Let's initialize the mux config to say that all 8 channels are |
| * GPIOs. Then we can just loop through the iio_chan_spec and clear the |
| * bits for found ADC channels. |
| */ |
| gpio_channels = GENMASK(7, 0); |
| for (i = 0; i < num_channels; i++) |
| gpio_channels &= ~BIT(cs[i].channel); |
| |
| return gpio_channels; |
| } |
| |
| static int bd79124_hw_init(struct bd79124_data *data) |
| { |
| unsigned int regval; |
| int ret, i; |
| |
| for (i = 0; i < BD79124_MAX_NUM_CHANNELS; i++) { |
| ret = bd79124_chan_init(data, i); |
| if (ret) |
| return ret; |
| data->alarm_r_limit[i] = BD79124_HIGH_LIMIT_MAX; |
| } |
| /* Stop auto sequencer */ |
| ret = regmap_clear_bits(data->map, BD79124_REG_SEQ_CFG, |
| BD79124_MSK_SEQ_START); |
| if (ret) |
| return ret; |
| |
| /* Enable writing the measured values to the regsters */ |
| ret = regmap_set_bits(data->map, BD79124_REG_GEN_CFG, |
| BD79124_MSK_STATS_EN); |
| if (ret) |
| return ret; |
| |
| /* Set no channels to be auto-measured */ |
| ret = regmap_write(data->map, BD79124_REG_AUTO_CHANNELS, 0x0); |
| if (ret) |
| return ret; |
| |
| /* Set no channels to be manually measured */ |
| ret = regmap_write(data->map, BD79124_REG_MANUAL_CHANNELS, 0x0); |
| if (ret) |
| return ret; |
| |
| regval = FIELD_PREP(BD79124_MSK_AUTO_INTERVAL, BD79124_INTERVAL_750_US); |
| ret = regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, |
| BD79124_MSK_AUTO_INTERVAL, regval); |
| if (ret) |
| return ret; |
| |
| /* Sequencer mode to auto */ |
| ret = regmap_set_bits(data->map, BD79124_REG_SEQ_CFG, |
| BD79124_MSK_SEQ_SEQ); |
| if (ret) |
| return ret; |
| |
| /* Don't start the measurement */ |
| regval = FIELD_PREP(BD79124_MSK_CONV_MODE, BD79124_CONV_MODE_MANSEQ); |
| return regmap_update_bits(data->map, BD79124_REG_OPMODE_CFG, |
| BD79124_MSK_CONV_MODE, regval); |
| } |
| |
| static int bd79124_probe(struct i2c_client *i2c) |
| { |
| struct bd79124_data *data; |
| struct iio_dev *iio_dev; |
| const struct iio_chan_spec *template; |
| struct iio_chan_spec *cs; |
| struct device *dev = &i2c->dev; |
| unsigned int gpio_pins; |
| int ret; |
| |
| iio_dev = devm_iio_device_alloc(dev, sizeof(*data)); |
| if (!iio_dev) |
| return -ENOMEM; |
| |
| data = iio_priv(iio_dev); |
| data->dev = dev; |
| data->map = devm_regmap_init_i2c(i2c, &bd79124_regmap); |
| if (IS_ERR(data->map)) |
| return dev_err_probe(dev, PTR_ERR(data->map), |
| "Failed to initialize Regmap\n"); |
| |
| ret = devm_regulator_get_enable_read_voltage(dev, "vdd"); |
| if (ret < 0) |
| return dev_err_probe(dev, ret, "Failed to get the Vdd\n"); |
| |
| data->vmax = ret; |
| |
| ret = devm_regulator_get_enable(dev, "iovdd"); |
| if (ret < 0) |
| return dev_err_probe(dev, ret, "Failed to enable I/O voltage\n"); |
| |
| ret = devm_delayed_work_autocancel(dev, &data->alm_enable_work, |
| bd79124_alm_enable_worker); |
| if (ret) |
| return ret; |
| |
| if (i2c->irq) { |
| template = &bd79124_chan_template; |
| } else { |
| template = &bd79124_chan_template_noirq; |
| dev_dbg(dev, "No IRQ found, events disabled\n"); |
| } |
| |
| ret = devm_mutex_init(dev, &data->mutex); |
| if (ret) |
| return ret; |
| |
| ret = devm_iio_adc_device_alloc_chaninfo_se(dev, template, |
| BD79124_MAX_NUM_CHANNELS - 1, &cs); |
| if (ret < 0) { |
| /* Register all pins as GPOs if there are no ADC channels */ |
| if (ret == -ENOENT) |
| goto register_gpios; |
| return ret; |
| } |
| iio_dev->channels = cs; |
| iio_dev->num_channels = ret; |
| iio_dev->info = &bd79124_info; |
| iio_dev->name = "bd79124"; |
| iio_dev->modes = INDIO_DIRECT_MODE; |
| |
| ret = bd79124_hw_init(data); |
| if (ret) |
| return ret; |
| |
| if (i2c->irq > 0) { |
| ret = devm_request_threaded_irq(dev, i2c->irq, |
| bd79124_irq_handler, &bd79124_event_handler, |
| IRQF_ONESHOT, "adc-thresh-alert", iio_dev); |
| if (ret) |
| return dev_err_probe(data->dev, ret, |
| "Failed to register IRQ\n"); |
| } |
| |
| ret = devm_iio_device_register(data->dev, iio_dev); |
| if (ret) |
| return dev_err_probe(data->dev, ret, "Failed to register ADC\n"); |
| |
| register_gpios: |
| gpio_pins = bd79124_get_gpio_pins(iio_dev->channels, |
| iio_dev->num_channels); |
| |
| /* |
| * The mux should default to "all ADCs", but better to not trust it. |
| * Thus we do set the mux even when we have only ADCs and no GPOs. |
| */ |
| ret = regmap_write(data->map, BD79124_REG_PINCFG, gpio_pins); |
| if (ret) |
| return ret; |
| |
| /* No GPOs if all channels are reserved for ADC, so we're done. */ |
| if (!gpio_pins) |
| return 0; |
| |
| data->gpio_valid_mask = gpio_pins; |
| data->gc = bd79124gpo_chip; |
| data->gc.parent = dev; |
| |
| return devm_gpiochip_add_data(dev, &data->gc, data); |
| } |
| |
| static const struct of_device_id bd79124_of_match[] = { |
| { .compatible = "rohm,bd79124" }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(of, bd79124_of_match); |
| |
| static const struct i2c_device_id bd79124_id[] = { |
| { "bd79124" }, |
| { } |
| }; |
| MODULE_DEVICE_TABLE(i2c, bd79124_id); |
| |
| static struct i2c_driver bd79124_driver = { |
| .driver = { |
| .name = "bd79124", |
| .of_match_table = bd79124_of_match, |
| }, |
| .probe = bd79124_probe, |
| .id_table = bd79124_id, |
| }; |
| module_i2c_driver(bd79124_driver); |
| |
| MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>"); |
| MODULE_DESCRIPTION("Driver for ROHM BD79124 ADC"); |
| MODULE_LICENSE("GPL"); |
| MODULE_IMPORT_NS("IIO_DRIVER"); |