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

mcp41010.c (5196B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Industrial I/O driver for Microchip digital potentiometers
      4 *
      5 * Copyright (c) 2018 Chris Coffey <cmc@babblebit.net>
      6 * Based on: Slawomir Stepien's code from mcp4131.c
      7 *
      8 * Datasheet: https://ww1.microchip.com/downloads/en/devicedoc/11195c.pdf
      9 *
     10 * DEVID	#Wipers	#Positions	Resistance (kOhm)
     11 * mcp41010	1	256		10
     12 * mcp41050	1	256		50
     13 * mcp41100	1	256		100
     14 * mcp42010	2	256		10
     15 * mcp42050	2	256		50
     16 * mcp42100	2	256		100
     17 */
     18
     19#include <linux/cache.h>
     20#include <linux/err.h>
     21#include <linux/iio/iio.h>
     22#include <linux/iio/types.h>
     23#include <linux/module.h>
     24#include <linux/mod_devicetable.h>
     25#include <linux/mutex.h>
     26#include <linux/property.h>
     27#include <linux/spi/spi.h>
     28
     29#define MCP41010_MAX_WIPERS	2
     30#define MCP41010_WRITE		BIT(4)
     31#define MCP41010_WIPER_MAX	255
     32#define MCP41010_WIPER_CHANNEL	BIT(0)
     33
     34struct mcp41010_cfg {
     35	char name[16];
     36	int wipers;
     37	int kohms;
     38};
     39
     40enum mcp41010_type {
     41	MCP41010,
     42	MCP41050,
     43	MCP41100,
     44	MCP42010,
     45	MCP42050,
     46	MCP42100,
     47};
     48
     49static const struct mcp41010_cfg mcp41010_cfg[] = {
     50	[MCP41010] = { .name = "mcp41010", .wipers = 1, .kohms =  10, },
     51	[MCP41050] = { .name = "mcp41050", .wipers = 1, .kohms =  50, },
     52	[MCP41100] = { .name = "mcp41100", .wipers = 1, .kohms = 100, },
     53	[MCP42010] = { .name = "mcp42010", .wipers = 2, .kohms =  10, },
     54	[MCP42050] = { .name = "mcp42050", .wipers = 2, .kohms =  50, },
     55	[MCP42100] = { .name = "mcp42100", .wipers = 2, .kohms = 100, },
     56};
     57
     58struct mcp41010_data {
     59	struct spi_device *spi;
     60	const struct mcp41010_cfg *cfg;
     61	struct mutex lock; /* Protect write sequences */
     62	unsigned int value[MCP41010_MAX_WIPERS]; /* Cache wiper values */
     63	u8 buf[2] ____cacheline_aligned;
     64};
     65
     66#define MCP41010_CHANNEL(ch) {					\
     67	.type = IIO_RESISTANCE,					\
     68	.indexed = 1,						\
     69	.output = 1,						\
     70	.channel = (ch),					\
     71	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
     72	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
     73}
     74
     75static const struct iio_chan_spec mcp41010_channels[] = {
     76	MCP41010_CHANNEL(0),
     77	MCP41010_CHANNEL(1),
     78};
     79
     80static int mcp41010_read_raw(struct iio_dev *indio_dev,
     81			    struct iio_chan_spec const *chan,
     82			    int *val, int *val2, long mask)
     83{
     84	struct mcp41010_data *data = iio_priv(indio_dev);
     85	int channel = chan->channel;
     86
     87	switch (mask) {
     88	case IIO_CHAN_INFO_RAW:
     89		*val = data->value[channel];
     90		return IIO_VAL_INT;
     91
     92	case IIO_CHAN_INFO_SCALE:
     93		*val = 1000 * data->cfg->kohms;
     94		*val2 = MCP41010_WIPER_MAX;
     95		return IIO_VAL_FRACTIONAL;
     96	}
     97
     98	return -EINVAL;
     99}
    100
    101static int mcp41010_write_raw(struct iio_dev *indio_dev,
    102			     struct iio_chan_spec const *chan,
    103			     int val, int val2, long mask)
    104{
    105	int err;
    106	struct mcp41010_data *data = iio_priv(indio_dev);
    107	int channel = chan->channel;
    108
    109	if (mask != IIO_CHAN_INFO_RAW)
    110		return -EINVAL;
    111
    112	if (val > MCP41010_WIPER_MAX || val < 0)
    113		return -EINVAL;
    114
    115	mutex_lock(&data->lock);
    116
    117	data->buf[0] = MCP41010_WIPER_CHANNEL << channel;
    118	data->buf[0] |= MCP41010_WRITE;
    119	data->buf[1] = val & 0xff;
    120
    121	err = spi_write(data->spi, data->buf, sizeof(data->buf));
    122	if (!err)
    123		data->value[channel] = val;
    124
    125	mutex_unlock(&data->lock);
    126
    127	return err;
    128}
    129
    130static const struct iio_info mcp41010_info = {
    131	.read_raw = mcp41010_read_raw,
    132	.write_raw = mcp41010_write_raw,
    133};
    134
    135static int mcp41010_probe(struct spi_device *spi)
    136{
    137	int err;
    138	struct device *dev = &spi->dev;
    139	struct mcp41010_data *data;
    140	struct iio_dev *indio_dev;
    141
    142	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
    143	if (!indio_dev)
    144		return -ENOMEM;
    145
    146	data = iio_priv(indio_dev);
    147	spi_set_drvdata(spi, indio_dev);
    148	data->spi = spi;
    149	data->cfg = device_get_match_data(&spi->dev);
    150	if (!data->cfg)
    151		data->cfg = &mcp41010_cfg[spi_get_device_id(spi)->driver_data];
    152
    153	mutex_init(&data->lock);
    154
    155	indio_dev->info = &mcp41010_info;
    156	indio_dev->channels = mcp41010_channels;
    157	indio_dev->num_channels = data->cfg->wipers;
    158	indio_dev->name = data->cfg->name;
    159
    160	err = devm_iio_device_register(dev, indio_dev);
    161	if (err)
    162		dev_info(&spi->dev, "Unable to register %s\n", indio_dev->name);
    163
    164	return err;
    165}
    166
    167static const struct of_device_id mcp41010_match[] = {
    168	{ .compatible = "microchip,mcp41010", .data = &mcp41010_cfg[MCP41010] },
    169	{ .compatible = "microchip,mcp41050", .data = &mcp41010_cfg[MCP41050] },
    170	{ .compatible = "microchip,mcp41100", .data = &mcp41010_cfg[MCP41100] },
    171	{ .compatible = "microchip,mcp42010", .data = &mcp41010_cfg[MCP42010] },
    172	{ .compatible = "microchip,mcp42050", .data = &mcp41010_cfg[MCP42050] },
    173	{ .compatible = "microchip,mcp42100", .data = &mcp41010_cfg[MCP42100] },
    174	{}
    175};
    176MODULE_DEVICE_TABLE(of, mcp41010_match);
    177
    178static const struct spi_device_id mcp41010_id[] = {
    179	{ "mcp41010", MCP41010 },
    180	{ "mcp41050", MCP41050 },
    181	{ "mcp41100", MCP41100 },
    182	{ "mcp42010", MCP42010 },
    183	{ "mcp42050", MCP42050 },
    184	{ "mcp42100", MCP42100 },
    185	{}
    186};
    187MODULE_DEVICE_TABLE(spi, mcp41010_id);
    188
    189static struct spi_driver mcp41010_driver = {
    190	.driver = {
    191		.name	= "mcp41010",
    192		.of_match_table = mcp41010_match,
    193	},
    194	.probe		= mcp41010_probe,
    195	.id_table	= mcp41010_id,
    196};
    197
    198module_spi_driver(mcp41010_driver);
    199
    200MODULE_AUTHOR("Chris Coffey <cmc@babblebit.net>");
    201MODULE_DESCRIPTION("MCP41010 digital potentiometer");
    202MODULE_LICENSE("GPL v2");