cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

max5481.c (4523B)


      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");