linux/drivers/iio/potentiometer/max5481.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Maxim Integrated MAX5481-MAX5484 digital potentiometer driver
   4 * Copyright 2016 Rockwell Collins
   5 *
   6 * Datasheet:
   7 * https://datasheets.maximintegrated.com/en/ds/MAX5481-MAX5484.pdf
   8 */
   9
  10#include <linux/iio/iio.h>
  11#include <linux/iio/sysfs.h>
  12#include <linux/module.h>
  13#include <linux/mod_devicetable.h>
  14#include <linux/property.h>
  15#include <linux/spi/spi.h>
  16
  17/* write wiper reg */
  18#define MAX5481_WRITE_WIPER (0 << 4)
  19/* copy wiper reg to NV reg */
  20#define MAX5481_COPY_AB_TO_NV (2 << 4)
  21/* copy NV reg to wiper reg */
  22#define MAX5481_COPY_NV_TO_AB (3 << 4)
  23
  24#define MAX5481_MAX_POS    1023
  25
  26enum max5481_variant {
  27        max5481,
  28        max5482,
  29        max5483,
  30        max5484,
  31};
  32
  33struct max5481_cfg {
  34        int kohms;
  35};
  36
  37static const struct max5481_cfg max5481_cfg[] = {
  38        [max5481] = { .kohms =  10, },
  39        [max5482] = { .kohms =  50, },
  40        [max5483] = { .kohms =  10, },
  41        [max5484] = { .kohms =  50, },
  42};
  43
  44struct max5481_data {
  45        struct spi_device *spi;
  46        const struct max5481_cfg *cfg;
  47        u8 msg[3] ____cacheline_aligned;
  48};
  49
  50#define MAX5481_CHANNEL {                                       \
  51        .type = IIO_RESISTANCE,                                 \
  52        .indexed = 1,                                           \
  53        .output = 1,                                            \
  54        .channel = 0,                                           \
  55        .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
  56        .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
  57}
  58
  59static const struct iio_chan_spec max5481_channels[] = {
  60        MAX5481_CHANNEL,
  61};
  62
  63static int max5481_write_cmd(struct max5481_data *data, u8 cmd, u16 val)
  64{
  65        struct spi_device *spi = data->spi;
  66
  67        data->msg[0] = cmd;
  68
  69        switch (cmd) {
  70        case MAX5481_WRITE_WIPER:
  71                data->msg[1] = val >> 2;
  72                data->msg[2] = (val & 0x3) << 6;
  73                return spi_write(spi, data->msg, 3);
  74
  75        case MAX5481_COPY_AB_TO_NV:
  76        case MAX5481_COPY_NV_TO_AB:
  77                return spi_write(spi, data->msg, 1);
  78
  79        default:
  80                return -EIO;
  81        }
  82}
  83
  84static int max5481_read_raw(struct iio_dev *indio_dev,
  85                struct iio_chan_spec const *chan,
  86                int *val, int *val2, long mask)
  87{
  88        struct max5481_data *data = iio_priv(indio_dev);
  89
  90        if (mask != IIO_CHAN_INFO_SCALE)
  91                return -EINVAL;
  92
  93        *val = 1000 * data->cfg->kohms;
  94        *val2 = MAX5481_MAX_POS;
  95
  96        return IIO_VAL_FRACTIONAL;
  97}
  98
  99static int max5481_write_raw(struct iio_dev *indio_dev,
 100                struct iio_chan_spec const *chan,
 101                int val, int val2, long mask)
 102{
 103        struct max5481_data *data = iio_priv(indio_dev);
 104
 105        if (mask != IIO_CHAN_INFO_RAW)
 106                return -EINVAL;
 107
 108        if (val < 0 || val > MAX5481_MAX_POS)
 109                return -EINVAL;
 110
 111        return max5481_write_cmd(data, MAX5481_WRITE_WIPER, val);
 112}
 113
 114static const struct iio_info max5481_info = {
 115        .read_raw = max5481_read_raw,
 116        .write_raw = max5481_write_raw,
 117};
 118
 119static const struct of_device_id max5481_match[] = {
 120        { .compatible = "maxim,max5481", .data = &max5481_cfg[max5481] },
 121        { .compatible = "maxim,max5482", .data = &max5481_cfg[max5482] },
 122        { .compatible = "maxim,max5483", .data = &max5481_cfg[max5483] },
 123        { .compatible = "maxim,max5484", .data = &max5481_cfg[max5484] },
 124        { }
 125};
 126MODULE_DEVICE_TABLE(of, max5481_match);
 127
 128static void max5481_wiper_save(void *data)
 129{
 130        max5481_write_cmd(data, MAX5481_COPY_AB_TO_NV, 0);
 131}
 132
 133static int max5481_probe(struct spi_device *spi)
 134{
 135        struct iio_dev *indio_dev;
 136        struct max5481_data *data;
 137        const struct spi_device_id *id = spi_get_device_id(spi);
 138        int ret;
 139
 140        indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
 141        if (!indio_dev)
 142                return -ENOMEM;
 143
 144        data = iio_priv(indio_dev);
 145
 146        data->spi = spi;
 147
 148        data->cfg = device_get_match_data(&spi->dev);
 149        if (!data->cfg)
 150                data->cfg = &max5481_cfg[id->driver_data];
 151
 152        indio_dev->name = id->name;
 153        indio_dev->modes = INDIO_DIRECT_MODE;
 154
 155        /* variant specific configuration */
 156        indio_dev->info = &max5481_info;
 157        indio_dev->channels = max5481_channels;
 158        indio_dev->num_channels = ARRAY_SIZE(max5481_channels);
 159
 160        /* restore wiper from NV */
 161        ret = max5481_write_cmd(data, MAX5481_COPY_NV_TO_AB, 0);
 162        if (ret < 0)
 163                return ret;
 164
 165        ret = devm_add_action(&spi->dev, max5481_wiper_save, data);
 166        if (ret < 0)
 167                return ret;
 168
 169        return devm_iio_device_register(&spi->dev, indio_dev);
 170}
 171
 172static const struct spi_device_id max5481_id_table[] = {
 173        { "max5481", max5481 },
 174        { "max5482", max5482 },
 175        { "max5483", max5483 },
 176        { "max5484", max5484 },
 177        { }
 178};
 179MODULE_DEVICE_TABLE(spi, max5481_id_table);
 180
 181static struct spi_driver max5481_driver = {
 182        .driver = {
 183                .name  = "max5481",
 184                .of_match_table = max5481_match,
 185        },
 186        .probe = max5481_probe,
 187        .id_table = max5481_id_table,
 188};
 189
 190module_spi_driver(max5481_driver);
 191
 192MODULE_AUTHOR("Maury Anderson <maury.anderson@rockwellcollins.com>");
 193MODULE_DESCRIPTION("max5481 SPI driver");
 194MODULE_LICENSE("GPL v2");
 195