root/drivers/iio/adc/ad7091r-base.c
// SPDX-License-Identifier: GPL-2.0
/*
 * AD7091RX Analog to Digital converter driver
 *
 * Copyright 2014-2019 Analog Devices Inc.
 */

#include <linux/bitops.h>
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>

#include "ad7091r-base.h"

const struct iio_event_spec ad7091r_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),
        },
};
EXPORT_SYMBOL_NS_GPL(ad7091r_events, "IIO_AD7091R");

static int ad7091r_set_channel(struct ad7091r_state *st, unsigned int channel)
{
        unsigned int dummy;
        int ret;

        /* AD7091R_REG_CHANNEL specified which channels to be converted */
        ret = regmap_write(st->map, AD7091R_REG_CHANNEL,
                        BIT(channel) | (BIT(channel) << 8));
        if (ret)
                return ret;

        /*
         * There is a latency of one conversion before the channel conversion
         * sequence is updated
         */
        return regmap_read(st->map, AD7091R_REG_RESULT, &dummy);
}

static int ad7091r_read_one(struct iio_dev *iio_dev,
                unsigned int channel, unsigned int *read_val)
{
        struct ad7091r_state *st = iio_priv(iio_dev);
        unsigned int val;
        int ret;

        ret = ad7091r_set_channel(st, channel);
        if (ret)
                return ret;

        ret = regmap_read(st->map, AD7091R_REG_RESULT, &val);
        if (ret)
                return ret;

        if (st->chip_info->reg_result_chan_id(val) != channel)
                return -EIO;

        *read_val = AD7091R_REG_RESULT_CONV_RESULT(val);

        return 0;
}

static int ad7091r_read_raw(struct iio_dev *iio_dev,
                           struct iio_chan_spec const *chan,
                           int *val, int *val2, long m)
{
        struct ad7091r_state *st = iio_priv(iio_dev);
        unsigned int read_val;
        int ret;

        guard(mutex)(&st->lock);

        switch (m) {
        case IIO_CHAN_INFO_RAW:
                if (st->mode != AD7091R_MODE_COMMAND)
                        return -EBUSY;

                ret = ad7091r_read_one(iio_dev, chan->channel, &read_val);
                if (ret)
                        return ret;

                *val = read_val;
                return IIO_VAL_INT;

        case IIO_CHAN_INFO_SCALE:
                if (st->vref) {
                        ret = regulator_get_voltage(st->vref);
                        if (ret < 0)
                                return ret;

                        *val = ret / 1000;
                } else {
                        *val = st->chip_info->vref_mV;
                }

                *val2 = chan->scan_type.realbits;
                return IIO_VAL_FRACTIONAL_LOG2;

        default:
                return -EINVAL;
        }
}

static int ad7091r_read_event_config(struct iio_dev *indio_dev,
                                     const struct iio_chan_spec *chan,
                                     enum iio_event_type type,
                                     enum iio_event_direction dir)
{
        struct ad7091r_state *st = iio_priv(indio_dev);
        int val, ret;

        switch (dir) {
        case IIO_EV_DIR_RISING:
                ret = regmap_read(st->map,
                                  AD7091R_REG_CH_HIGH_LIMIT(chan->channel),
                                  &val);
                if (ret)
                        return ret;
                return val != AD7091R_HIGH_LIMIT;
        case IIO_EV_DIR_FALLING:
                ret = regmap_read(st->map,
                                  AD7091R_REG_CH_LOW_LIMIT(chan->channel),
                                  &val);
                if (ret)
                        return ret;
                return val != AD7091R_LOW_LIMIT;
        default:
                return -EINVAL;
        }
}

static int ad7091r_write_event_config(struct iio_dev *indio_dev,
                                      const struct iio_chan_spec *chan,
                                      enum iio_event_type type,
                                      enum iio_event_direction dir,
                                      bool state)
{
        struct ad7091r_state *st = iio_priv(indio_dev);

        if (state) {
                return regmap_set_bits(st->map, AD7091R_REG_CONF,
                                       AD7091R_REG_CONF_ALERT_EN);
        } else {
                /*
                 * Set thresholds either to 0 or to 2^12 - 1 as appropriate to
                 * prevent alerts and thus disable event generation.
                 */
                switch (dir) {
                case IIO_EV_DIR_RISING:
                        return regmap_write(st->map,
                                            AD7091R_REG_CH_HIGH_LIMIT(chan->channel),
                                            AD7091R_HIGH_LIMIT);
                case IIO_EV_DIR_FALLING:
                        return regmap_write(st->map,
                                            AD7091R_REG_CH_LOW_LIMIT(chan->channel),
                                            AD7091R_LOW_LIMIT);
                default:
                        return -EINVAL;
                }
        }
}

static int ad7091r_read_event_value(struct iio_dev *indio_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 ad7091r_state *st = iio_priv(indio_dev);
        int ret;

        switch (info) {
        case IIO_EV_INFO_VALUE:
                switch (dir) {
                case IIO_EV_DIR_RISING:
                        ret = regmap_read(st->map,
                                          AD7091R_REG_CH_HIGH_LIMIT(chan->channel),
                                          val);
                        if (ret)
                                return ret;
                        return IIO_VAL_INT;
                case IIO_EV_DIR_FALLING:
                        ret = regmap_read(st->map,
                                          AD7091R_REG_CH_LOW_LIMIT(chan->channel),
                                          val);
                        if (ret)
                                return ret;
                        return IIO_VAL_INT;
                default:
                        return -EINVAL;
                }
        case IIO_EV_INFO_HYSTERESIS:
                ret = regmap_read(st->map,
                                  AD7091R_REG_CH_HYSTERESIS(chan->channel),
                                  val);
                if (ret)
                        return ret;
                return IIO_VAL_INT;
        default:
                return -EINVAL;
        }
}

static int ad7091r_write_event_value(struct iio_dev *indio_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 ad7091r_state *st = iio_priv(indio_dev);

        switch (info) {
        case IIO_EV_INFO_VALUE:
                switch (dir) {
                case IIO_EV_DIR_RISING:
                        return regmap_write(st->map,
                                            AD7091R_REG_CH_HIGH_LIMIT(chan->channel),
                                            val);
                case IIO_EV_DIR_FALLING:
                        return regmap_write(st->map,
                                            AD7091R_REG_CH_LOW_LIMIT(chan->channel),
                                            val);
                default:
                        return -EINVAL;
                }
        case IIO_EV_INFO_HYSTERESIS:
                return regmap_write(st->map,
                                    AD7091R_REG_CH_HYSTERESIS(chan->channel),
                                    val);
        default:
                return -EINVAL;
        }
}

static const struct iio_info ad7091r_info = {
        .read_raw = ad7091r_read_raw,
        .read_event_config = &ad7091r_read_event_config,
        .write_event_config = &ad7091r_write_event_config,
        .read_event_value = &ad7091r_read_event_value,
        .write_event_value = &ad7091r_write_event_value,
};

static irqreturn_t ad7091r_event_handler(int irq, void *private)
{
        struct iio_dev *iio_dev = private;
        struct ad7091r_state *st = iio_priv(iio_dev);
        unsigned int i, read_val;
        int ret;
        s64 timestamp = iio_get_time_ns(iio_dev);

        ret = regmap_read(st->map, AD7091R_REG_ALERT, &read_val);
        if (ret)
                return IRQ_HANDLED;

        for (i = 0; i < st->chip_info->num_channels; i++) {
                if (read_val & BIT(i * 2))
                        iio_push_event(iio_dev,
                                        IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
                                                IIO_EV_TYPE_THRESH,
                                                IIO_EV_DIR_RISING), timestamp);
                if (read_val & BIT(i * 2 + 1))
                        iio_push_event(iio_dev,
                                        IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, i,
                                                IIO_EV_TYPE_THRESH,
                                                IIO_EV_DIR_FALLING), timestamp);
        }

        return IRQ_HANDLED;
}

static void ad7091r_remove(void *data)
{
        struct ad7091r_state *st = data;

        regulator_disable(st->vref);
}

int ad7091r_probe(struct device *dev, const struct ad7091r_init_info *init_info,
                  int irq)
{
        struct iio_dev *iio_dev;
        struct ad7091r_state *st;
        int ret;

        iio_dev = devm_iio_device_alloc(dev, sizeof(*st));
        if (!iio_dev)
                return -ENOMEM;

        st = iio_priv(iio_dev);
        st->dev = dev;
        init_info->init_adc_regmap(st, init_info->regmap_config);
        if (IS_ERR(st->map))
                return dev_err_probe(st->dev, PTR_ERR(st->map),
                                     "Error initializing regmap\n");

        iio_dev->info = &ad7091r_info;
        iio_dev->modes = INDIO_DIRECT_MODE;

        if (init_info->setup) {
                ret = init_info->setup(st);
                if (ret < 0)
                        return ret;
        }

        if (irq) {
                st->chip_info = init_info->info_irq;
                ret = regmap_update_bits(st->map, AD7091R_REG_CONF,
                                         AD7091R_REG_CONF_ALERT_EN, BIT(4));
                if (ret)
                        return ret;

                ret = devm_request_threaded_irq(dev, irq, NULL,
                                                ad7091r_event_handler,
                                                IRQF_TRIGGER_FALLING |
                                                IRQF_ONESHOT,
                                                st->chip_info->name, iio_dev);
                if (ret)
                        return ret;
        } else {
                st->chip_info = init_info->info_no_irq;
        }

        iio_dev->name = st->chip_info->name;
        iio_dev->num_channels = st->chip_info->num_channels;
        iio_dev->channels = st->chip_info->channels;

        st->vref = devm_regulator_get_optional(dev, "vref");
        if (IS_ERR(st->vref)) {
                if (PTR_ERR(st->vref) == -EPROBE_DEFER)
                        return -EPROBE_DEFER;

                st->vref = NULL;
                /* Enable internal vref */
                ret = regmap_set_bits(st->map, AD7091R_REG_CONF,
                                      AD7091R_REG_CONF_INT_VREF);
                if (ret)
                        return dev_err_probe(st->dev, ret,
                                             "Error on enable internal reference\n");
        } else {
                ret = regulator_enable(st->vref);
                if (ret)
                        return ret;
                ret = devm_add_action_or_reset(dev, ad7091r_remove, st);
                if (ret)
                        return ret;
        }

        /* Use command mode by default to convert only desired channels*/
        ret = st->chip_info->set_mode(st, AD7091R_MODE_COMMAND);
        if (ret)
                return ret;

        return devm_iio_device_register(dev, iio_dev);
}
EXPORT_SYMBOL_NS_GPL(ad7091r_probe, "IIO_AD7091R");

bool ad7091r_writeable_reg(struct device *dev, unsigned int reg)
{
        switch (reg) {
        case AD7091R_REG_RESULT:
        case AD7091R_REG_ALERT:
                return false;
        default:
                return true;
        }
}
EXPORT_SYMBOL_NS_GPL(ad7091r_writeable_reg, "IIO_AD7091R");

bool ad7091r_volatile_reg(struct device *dev, unsigned int reg)
{
        /* The volatile ad7091r registers are also the only RO ones. */
        return !ad7091r_writeable_reg(dev, reg);
}
EXPORT_SYMBOL_NS_GPL(ad7091r_volatile_reg, "IIO_AD7091R");

MODULE_AUTHOR("Beniamin Bia <beniamin.bia@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7091Rx multi-channel converters");
MODULE_LICENSE("GPL v2");