root/drivers/staging/iio/adc/ad7816.c
// SPDX-License-Identifier: GPL-2.0+
/*
 * AD7816 digital temperature sensor driver supporting AD7816/7/8
 *
 * Copyright 2010 Analog Devices Inc.
 */

#include <linux/interrupt.h>
#include <linux/gpio/consumer.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/list.h>
#include <linux/spi/spi.h>
#include <linux/module.h>

#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>

/*
 * AD7816 config masks
 */
#define AD7816_FULL                     0x1
#define AD7816_PD                       0x2
#define AD7816_CS_MASK                  0x7
#define AD7816_CS_MAX                   0x4

/*
 * AD7816 temperature masks
 */
#define AD7816_VALUE_OFFSET             6
#define AD7816_BOUND_VALUE_BASE         0x8
#define AD7816_BOUND_VALUE_MIN          -95
#define AD7816_BOUND_VALUE_MAX          152
#define AD7816_TEMP_FLOAT_OFFSET        2
#define AD7816_TEMP_FLOAT_MASK          0x3

/*
 * struct ad7816_chip_info - chip specific information
 */

struct ad7816_chip_info {
        kernel_ulong_t id;
        struct spi_device *spi_dev;
        struct gpio_desc *rdwr_pin;
        struct gpio_desc *convert_pin;
        struct gpio_desc *busy_pin;
        u8  oti_data[AD7816_CS_MAX + 1];
        u8  channel_id; /* 0 always be temperature */
        u8  mode;
};

enum ad7816_type {
        ID_AD7816,
        ID_AD7817,
        ID_AD7818,
};

/*
 * ad7816 data access by SPI
 */
static int ad7816_spi_read(struct ad7816_chip_info *chip, u16 *data)
{
        struct spi_device *spi_dev = chip->spi_dev;
        int ret;
        __be16 buf;

        gpiod_set_value(chip->rdwr_pin, 1);
        gpiod_set_value(chip->rdwr_pin, 0);
        ret = spi_write(spi_dev, &chip->channel_id, sizeof(chip->channel_id));
        if (ret < 0) {
                dev_err(&spi_dev->dev, "SPI channel setting error\n");
                return ret;
        }
        gpiod_set_value(chip->rdwr_pin, 1);

        if (chip->mode == AD7816_PD) { /* operating mode 2 */
                gpiod_set_value(chip->convert_pin, 1);
                gpiod_set_value(chip->convert_pin, 0);
        } else { /* operating mode 1 */
                gpiod_set_value(chip->convert_pin, 0);
                gpiod_set_value(chip->convert_pin, 1);
        }

        if (chip->id == ID_AD7816 || chip->id == ID_AD7817) {
                while (gpiod_get_value(chip->busy_pin))
                        cpu_relax();
        }

        gpiod_set_value(chip->rdwr_pin, 0);
        gpiod_set_value(chip->rdwr_pin, 1);
        ret = spi_read(spi_dev, &buf, sizeof(*data));
        if (ret < 0) {
                dev_err(&spi_dev->dev, "SPI data read error\n");
                return ret;
        }

        *data = be16_to_cpu(buf);

        return ret;
}

static int ad7816_spi_write(struct ad7816_chip_info *chip, u8 data)
{
        struct spi_device *spi_dev = chip->spi_dev;
        int ret;

        gpiod_set_value(chip->rdwr_pin, 1);
        gpiod_set_value(chip->rdwr_pin, 0);
        ret = spi_write(spi_dev, &data, sizeof(data));
        if (ret < 0)
                dev_err(&spi_dev->dev, "SPI oti data write error\n");

        return ret;
}

static ssize_t ad7816_show_mode(struct device *dev,
                                struct device_attribute *attr,
                                char *buf)
{
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
        struct ad7816_chip_info *chip = iio_priv(indio_dev);

        if (chip->mode)
                return sprintf(buf, "power-save\n");
        return sprintf(buf, "full\n");
}

static ssize_t ad7816_store_mode(struct device *dev,
                                 struct device_attribute *attr,
                                 const char *buf,
                                 size_t len)
{
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
        struct ad7816_chip_info *chip = iio_priv(indio_dev);

        if (strcmp(buf, "full") == 0) {
                gpiod_set_value(chip->rdwr_pin, 1);
                chip->mode = AD7816_FULL;
        } else {
                gpiod_set_value(chip->rdwr_pin, 0);
                chip->mode = AD7816_PD;
        }

        return len;
}

static IIO_DEVICE_ATTR(mode, 0644,
                ad7816_show_mode,
                ad7816_store_mode,
                0);

static ssize_t ad7816_show_available_modes(struct device *dev,
                                           struct device_attribute *attr,
                                           char *buf)
{
        return sprintf(buf, "full\npower-save\n");
}

static IIO_DEVICE_ATTR(available_modes, 0444, ad7816_show_available_modes,
                        NULL, 0);

static ssize_t ad7816_show_channel(struct device *dev,
                                   struct device_attribute *attr,
                                   char *buf)
{
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
        struct ad7816_chip_info *chip = iio_priv(indio_dev);

        return sprintf(buf, "%d\n", chip->channel_id);
}

static ssize_t ad7816_store_channel(struct device *dev,
                                    struct device_attribute *attr,
                                    const char *buf,
                                    size_t len)
{
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
        struct ad7816_chip_info *chip = iio_priv(indio_dev);
        unsigned long data;
        int ret;

        ret = kstrtoul(buf, 10, &data);
        if (ret)
                return ret;

        if (data > AD7816_CS_MAX && data != AD7816_CS_MASK) {
                dev_err(&chip->spi_dev->dev, "Invalid channel id %lu for %s.\n",
                        data, indio_dev->name);
                return -EINVAL;
        } else if (strcmp(indio_dev->name, "ad7818") == 0 && data > 1) {
                dev_err(&chip->spi_dev->dev,
                        "Invalid channel id %lu for ad7818.\n", data);
                return -EINVAL;
        } else if (strcmp(indio_dev->name, "ad7816") == 0 && data > 0) {
                dev_err(&chip->spi_dev->dev,
                        "Invalid channel id %lu for ad7816.\n", data);
                return -EINVAL;
        }

        chip->channel_id = data;

        return len;
}

static IIO_DEVICE_ATTR(channel, 0644,
                ad7816_show_channel,
                ad7816_store_channel,
                0);

static ssize_t ad7816_show_value(struct device *dev,
                                 struct device_attribute *attr,
                                 char *buf)
{
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
        struct ad7816_chip_info *chip = iio_priv(indio_dev);
        u16 data;
        s8 value;
        int ret;

        ret = ad7816_spi_read(chip, &data);
        if (ret)
                return -EIO;

        data >>= AD7816_VALUE_OFFSET;

        if (chip->channel_id == 0) {
                value = (s8)((data >> AD7816_TEMP_FLOAT_OFFSET) - 103);
                data &= AD7816_TEMP_FLOAT_MASK;
                if (value < 0)
                        data = BIT(AD7816_TEMP_FLOAT_OFFSET) - data;
                return sprintf(buf, "%d.%.2d\n", value, data * 25);
        }
        return sprintf(buf, "%u\n", data);
}

static IIO_DEVICE_ATTR(value, 0444, ad7816_show_value, NULL, 0);

static struct attribute *ad7816_attributes[] = {
        &iio_dev_attr_available_modes.dev_attr.attr,
        &iio_dev_attr_mode.dev_attr.attr,
        &iio_dev_attr_channel.dev_attr.attr,
        &iio_dev_attr_value.dev_attr.attr,
        NULL,
};

static const struct attribute_group ad7816_attribute_group = {
        .attrs = ad7816_attributes,
};

/*
 * temperature bound events
 */

#define IIO_EVENT_CODE_AD7816_OTI IIO_UNMOD_EVENT_CODE(IIO_TEMP,        \
                                                       0,               \
                                                       IIO_EV_TYPE_THRESH, \
                                                       IIO_EV_DIR_FALLING)

static irqreturn_t ad7816_event_handler(int irq, void *private)
{
        iio_push_event(private, IIO_EVENT_CODE_AD7816_OTI,
                       iio_get_time_ns(private));
        return IRQ_HANDLED;
}

static ssize_t ad7816_show_oti(struct device *dev,
                               struct device_attribute *attr,
                               char *buf)
{
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
        struct ad7816_chip_info *chip = iio_priv(indio_dev);
        int value;

        if (chip->channel_id > AD7816_CS_MAX) {
                dev_err(dev, "Invalid oti channel id %d.\n", chip->channel_id);
                return -EINVAL;
        } else if (chip->channel_id == 0) {
                value = AD7816_BOUND_VALUE_MIN +
                        (chip->oti_data[chip->channel_id] -
                        AD7816_BOUND_VALUE_BASE);
                return sprintf(buf, "%d\n", value);
        }
        return sprintf(buf, "%u\n", chip->oti_data[chip->channel_id]);
}

static inline ssize_t ad7816_set_oti(struct device *dev,
                                     struct device_attribute *attr,
                                     const char *buf,
                                     size_t len)
{
        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
        struct ad7816_chip_info *chip = iio_priv(indio_dev);
        long value;
        u8 data;
        int ret;

        ret = kstrtol(buf, 10, &value);
        if (ret)
                return ret;

        if (chip->channel_id > AD7816_CS_MAX) {
                dev_err(dev, "Invalid oti channel id %d.\n", chip->channel_id);
                return -EINVAL;
        } else if (chip->channel_id == 0) {
                if (value < AD7816_BOUND_VALUE_MIN ||
                    value > AD7816_BOUND_VALUE_MAX)
                        return -EINVAL;

                data = (u8)(value - AD7816_BOUND_VALUE_MIN +
                        AD7816_BOUND_VALUE_BASE);
        } else {
                if (value < AD7816_BOUND_VALUE_BASE || value > 255)
                        return -EINVAL;

                data = (u8)value;
        }

        ret = ad7816_spi_write(chip, data);
        if (ret)
                return -EIO;

        chip->oti_data[chip->channel_id] = data;

        return len;
}

static IIO_DEVICE_ATTR(oti, 0644,
                       ad7816_show_oti, ad7816_set_oti, 0);

static struct attribute *ad7816_event_attributes[] = {
        &iio_dev_attr_oti.dev_attr.attr,
        NULL,
};

static const struct attribute_group ad7816_event_attribute_group = {
        .attrs = ad7816_event_attributes,
        .name = "events",
};

static const struct iio_info ad7816_info = {
        .attrs = &ad7816_attribute_group,
        .event_attrs = &ad7816_event_attribute_group,
};

/*
 * device probe and remove
 */

static int ad7816_probe(struct spi_device *spi_dev)
{
        struct ad7816_chip_info *chip;
        struct iio_dev *indio_dev;
        int i, ret;

        indio_dev = devm_iio_device_alloc(&spi_dev->dev, sizeof(*chip));
        if (!indio_dev)
                return -ENOMEM;
        chip = iio_priv(indio_dev);

        chip->spi_dev = spi_dev;
        for (i = 0; i <= AD7816_CS_MAX; i++)
                chip->oti_data[i] = 203;

        chip->id = spi_get_device_id(spi_dev)->driver_data;
        chip->rdwr_pin = devm_gpiod_get(&spi_dev->dev, "rdwr", GPIOD_OUT_HIGH);
        if (IS_ERR(chip->rdwr_pin)) {
                ret = PTR_ERR(chip->rdwr_pin);
                dev_err(&spi_dev->dev, "Failed to request rdwr GPIO: %d\n",
                        ret);
                return ret;
        }
        chip->convert_pin = devm_gpiod_get(&spi_dev->dev, "convert",
                                           GPIOD_OUT_HIGH);
        if (IS_ERR(chip->convert_pin)) {
                ret = PTR_ERR(chip->convert_pin);
                dev_err(&spi_dev->dev, "Failed to request convert GPIO: %d\n",
                        ret);
                return ret;
        }
        if (chip->id == ID_AD7816 || chip->id == ID_AD7817) {
                chip->busy_pin = devm_gpiod_get(&spi_dev->dev, "busy",
                                                GPIOD_IN);
                if (IS_ERR(chip->busy_pin)) {
                        ret = PTR_ERR(chip->busy_pin);
                        dev_err(&spi_dev->dev, "Failed to request busy GPIO: %d\n",
                                ret);
                        return ret;
                }
        }

        indio_dev->name = spi_get_device_id(spi_dev)->name;
        indio_dev->info = &ad7816_info;
        indio_dev->modes = INDIO_DIRECT_MODE;

        if (spi_dev->irq) {
                /* Only low trigger is supported in ad7816/7/8 */
                ret = devm_request_threaded_irq(&spi_dev->dev, spi_dev->irq,
                                                NULL,
                                                &ad7816_event_handler,
                                                IRQF_TRIGGER_LOW | IRQF_ONESHOT,
                                                indio_dev->name,
                                                indio_dev);
                if (ret)
                        return ret;
        }

        ret = devm_iio_device_register(&spi_dev->dev, indio_dev);
        if (ret)
                return ret;

        dev_info(&spi_dev->dev, "%s temperature sensor and ADC registered.\n",
                 indio_dev->name);

        return 0;
}

static const struct of_device_id ad7816_of_match[] = {
        { .compatible = "adi,ad7816", },
        { .compatible = "adi,ad7817", },
        { .compatible = "adi,ad7818", },
        { }
};
MODULE_DEVICE_TABLE(of, ad7816_of_match);

static const struct spi_device_id ad7816_id[] = {
        { "ad7816", ID_AD7816 },
        { "ad7817", ID_AD7817 },
        { "ad7818", ID_AD7818 },
        { }
};

MODULE_DEVICE_TABLE(spi, ad7816_id);

static struct spi_driver ad7816_driver = {
        .driver = {
                .name = "ad7816",
                .of_match_table = ad7816_of_match,
        },
        .probe = ad7816_probe,
        .id_table = ad7816_id,
};
module_spi_driver(ad7816_driver);

MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7816/7/8 digital temperature sensor driver");
MODULE_LICENSE("GPL v2");