mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

The new GPIO callbacks 'set_rv' and 'set_multiple_rv' allow returning a success code to indicate failures when setting GPIO status. Use them to allow callers to know when things go south. Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com> Link: https://patch.msgid.link/Z_N_J52IZ2IaWawl@mva-rohm Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
1146 lines
30 KiB
C
1146 lines
30 KiB
C
// 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_rv = bd79124gpo_set,
|
|
.set_multiple_rv = 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");
|