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

mcp4131.c (15190B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Industrial I/O driver for Microchip digital potentiometers
      4 *
      5 * Copyright (c) 2016 Slawomir Stepien
      6 * Based on: Peter Rosin's code from mcp4531.c
      7 *
      8 * Datasheet: https://ww1.microchip.com/downloads/en/DeviceDoc/22060b.pdf
      9 *
     10 * DEVID	#Wipers	#Positions	Resistor Opts (kOhm)
     11 * mcp4131	1	129		5, 10, 50, 100
     12 * mcp4132	1	129		5, 10, 50, 100
     13 * mcp4141	1	129		5, 10, 50, 100
     14 * mcp4142	1	129		5, 10, 50, 100
     15 * mcp4151	1	257		5, 10, 50, 100
     16 * mcp4152	1	257		5, 10, 50, 100
     17 * mcp4161	1	257		5, 10, 50, 100
     18 * mcp4162	1	257		5, 10, 50, 100
     19 * mcp4231	2	129		5, 10, 50, 100
     20 * mcp4232	2	129		5, 10, 50, 100
     21 * mcp4241	2	129		5, 10, 50, 100
     22 * mcp4242	2	129		5, 10, 50, 100
     23 * mcp4251	2	257		5, 10, 50, 100
     24 * mcp4252	2	257		5, 10, 50, 100
     25 * mcp4261	2	257		5, 10, 50, 100
     26 * mcp4262	2	257		5, 10, 50, 100
     27 */
     28
     29/*
     30 * TODO:
     31 * 1. Write wiper setting to EEPROM for EEPROM capable models.
     32 */
     33
     34#include <linux/cache.h>
     35#include <linux/err.h>
     36#include <linux/export.h>
     37#include <linux/iio/iio.h>
     38#include <linux/iio/types.h>
     39#include <linux/module.h>
     40#include <linux/mod_devicetable.h>
     41#include <linux/mutex.h>
     42#include <linux/property.h>
     43#include <linux/spi/spi.h>
     44
     45#define MCP4131_WRITE		(0x00 << 2)
     46#define MCP4131_READ		(0x03 << 2)
     47
     48#define MCP4131_WIPER_SHIFT	4
     49#define MCP4131_CMDERR(r)	((r[0]) & 0x02)
     50#define MCP4131_RAW(r)		((r[0]) == 0xff ? 0x100 : (r[1]))
     51
     52struct mcp4131_cfg {
     53	int wipers;
     54	int max_pos;
     55	int kohms;
     56};
     57
     58enum mcp4131_type {
     59	MCP413x_502 = 0,
     60	MCP413x_103,
     61	MCP413x_503,
     62	MCP413x_104,
     63	MCP414x_502,
     64	MCP414x_103,
     65	MCP414x_503,
     66	MCP414x_104,
     67	MCP415x_502,
     68	MCP415x_103,
     69	MCP415x_503,
     70	MCP415x_104,
     71	MCP416x_502,
     72	MCP416x_103,
     73	MCP416x_503,
     74	MCP416x_104,
     75	MCP423x_502,
     76	MCP423x_103,
     77	MCP423x_503,
     78	MCP423x_104,
     79	MCP424x_502,
     80	MCP424x_103,
     81	MCP424x_503,
     82	MCP424x_104,
     83	MCP425x_502,
     84	MCP425x_103,
     85	MCP425x_503,
     86	MCP425x_104,
     87	MCP426x_502,
     88	MCP426x_103,
     89	MCP426x_503,
     90	MCP426x_104,
     91};
     92
     93static const struct mcp4131_cfg mcp4131_cfg[] = {
     94	[MCP413x_502] = { .wipers = 1, .max_pos = 128, .kohms =   5, },
     95	[MCP413x_103] = { .wipers = 1, .max_pos = 128, .kohms =  10, },
     96	[MCP413x_503] = { .wipers = 1, .max_pos = 128, .kohms =  50, },
     97	[MCP413x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },
     98	[MCP414x_502] = { .wipers = 1, .max_pos = 128, .kohms =   5, },
     99	[MCP414x_103] = { .wipers = 1, .max_pos = 128, .kohms =  10, },
    100	[MCP414x_503] = { .wipers = 1, .max_pos = 128, .kohms =  50, },
    101	[MCP414x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, },
    102	[MCP415x_502] = { .wipers = 1, .max_pos = 256, .kohms =   5, },
    103	[MCP415x_103] = { .wipers = 1, .max_pos = 256, .kohms =  10, },
    104	[MCP415x_503] = { .wipers = 1, .max_pos = 256, .kohms =  50, },
    105	[MCP415x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
    106	[MCP416x_502] = { .wipers = 1, .max_pos = 256, .kohms =   5, },
    107	[MCP416x_103] = { .wipers = 1, .max_pos = 256, .kohms =  10, },
    108	[MCP416x_503] = { .wipers = 1, .max_pos = 256, .kohms =  50, },
    109	[MCP416x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
    110	[MCP423x_502] = { .wipers = 2, .max_pos = 128, .kohms =   5, },
    111	[MCP423x_103] = { .wipers = 2, .max_pos = 128, .kohms =  10, },
    112	[MCP423x_503] = { .wipers = 2, .max_pos = 128, .kohms =  50, },
    113	[MCP423x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },
    114	[MCP424x_502] = { .wipers = 2, .max_pos = 128, .kohms =   5, },
    115	[MCP424x_103] = { .wipers = 2, .max_pos = 128, .kohms =  10, },
    116	[MCP424x_503] = { .wipers = 2, .max_pos = 128, .kohms =  50, },
    117	[MCP424x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, },
    118	[MCP425x_502] = { .wipers = 2, .max_pos = 256, .kohms =   5, },
    119	[MCP425x_103] = { .wipers = 2, .max_pos = 256, .kohms =  10, },
    120	[MCP425x_503] = { .wipers = 2, .max_pos = 256, .kohms =  50, },
    121	[MCP425x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },
    122	[MCP426x_502] = { .wipers = 2, .max_pos = 256, .kohms =   5, },
    123	[MCP426x_103] = { .wipers = 2, .max_pos = 256, .kohms =  10, },
    124	[MCP426x_503] = { .wipers = 2, .max_pos = 256, .kohms =  50, },
    125	[MCP426x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, },
    126};
    127
    128struct mcp4131_data {
    129	struct spi_device *spi;
    130	const struct mcp4131_cfg *cfg;
    131	struct mutex lock;
    132	u8 buf[2] ____cacheline_aligned;
    133};
    134
    135#define MCP4131_CHANNEL(ch) {					\
    136	.type = IIO_RESISTANCE,					\
    137	.indexed = 1,						\
    138	.output = 1,						\
    139	.channel = (ch),					\
    140	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
    141	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
    142}
    143
    144static const struct iio_chan_spec mcp4131_channels[] = {
    145	MCP4131_CHANNEL(0),
    146	MCP4131_CHANNEL(1),
    147};
    148
    149static int mcp4131_read(struct spi_device *spi, void *buf, size_t len)
    150{
    151	struct spi_transfer t = {
    152		.tx_buf = buf, /* We need to send addr, cmd and 12 bits */
    153		.rx_buf	= buf,
    154		.len = len,
    155	};
    156	struct spi_message m;
    157
    158	spi_message_init(&m);
    159	spi_message_add_tail(&t, &m);
    160
    161	return spi_sync(spi, &m);
    162}
    163
    164static int mcp4131_read_raw(struct iio_dev *indio_dev,
    165			    struct iio_chan_spec const *chan,
    166			    int *val, int *val2, long mask)
    167{
    168	int err;
    169	struct mcp4131_data *data = iio_priv(indio_dev);
    170	int address = chan->channel;
    171
    172	switch (mask) {
    173	case IIO_CHAN_INFO_RAW:
    174		mutex_lock(&data->lock);
    175
    176		data->buf[0] = (address << MCP4131_WIPER_SHIFT) | MCP4131_READ;
    177		data->buf[1] = 0;
    178
    179		err = mcp4131_read(data->spi, data->buf, 2);
    180		if (err) {
    181			mutex_unlock(&data->lock);
    182			return err;
    183		}
    184
    185		/* Error, bad address/command combination */
    186		if (!MCP4131_CMDERR(data->buf)) {
    187			mutex_unlock(&data->lock);
    188			return -EIO;
    189		}
    190
    191		*val = MCP4131_RAW(data->buf);
    192		mutex_unlock(&data->lock);
    193
    194		return IIO_VAL_INT;
    195
    196	case IIO_CHAN_INFO_SCALE:
    197		*val = 1000 * data->cfg->kohms;
    198		*val2 = data->cfg->max_pos;
    199		return IIO_VAL_FRACTIONAL;
    200	}
    201
    202	return -EINVAL;
    203}
    204
    205static int mcp4131_write_raw(struct iio_dev *indio_dev,
    206			     struct iio_chan_spec const *chan,
    207			     int val, int val2, long mask)
    208{
    209	int err;
    210	struct mcp4131_data *data = iio_priv(indio_dev);
    211	int address = chan->channel << MCP4131_WIPER_SHIFT;
    212
    213	switch (mask) {
    214	case IIO_CHAN_INFO_RAW:
    215		if (val > data->cfg->max_pos || val < 0)
    216			return -EINVAL;
    217		break;
    218
    219	default:
    220		return -EINVAL;
    221	}
    222
    223	mutex_lock(&data->lock);
    224
    225	data->buf[0] = address << MCP4131_WIPER_SHIFT;
    226	data->buf[0] |= MCP4131_WRITE | (val >> 8);
    227	data->buf[1] = val & 0xFF; /* 8 bits here */
    228
    229	err = spi_write(data->spi, data->buf, 2);
    230	mutex_unlock(&data->lock);
    231
    232	return err;
    233}
    234
    235static const struct iio_info mcp4131_info = {
    236	.read_raw = mcp4131_read_raw,
    237	.write_raw = mcp4131_write_raw,
    238};
    239
    240static int mcp4131_probe(struct spi_device *spi)
    241{
    242	int err;
    243	struct device *dev = &spi->dev;
    244	unsigned long devid;
    245	struct mcp4131_data *data;
    246	struct iio_dev *indio_dev;
    247
    248	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
    249	if (!indio_dev)
    250		return -ENOMEM;
    251
    252	data = iio_priv(indio_dev);
    253	spi_set_drvdata(spi, indio_dev);
    254	data->spi = spi;
    255	data->cfg = device_get_match_data(&spi->dev);
    256	if (!data->cfg) {
    257		devid = spi_get_device_id(spi)->driver_data;
    258		data->cfg = &mcp4131_cfg[devid];
    259	}
    260
    261	mutex_init(&data->lock);
    262
    263	indio_dev->info = &mcp4131_info;
    264	indio_dev->channels = mcp4131_channels;
    265	indio_dev->num_channels = data->cfg->wipers;
    266	indio_dev->name = spi_get_device_id(spi)->name;
    267
    268	err = devm_iio_device_register(dev, indio_dev);
    269	if (err) {
    270		dev_info(&spi->dev, "Unable to register %s\n", indio_dev->name);
    271		return err;
    272	}
    273
    274	return 0;
    275}
    276
    277static const struct of_device_id mcp4131_dt_ids[] = {
    278	{ .compatible = "microchip,mcp4131-502",
    279		.data = &mcp4131_cfg[MCP413x_502] },
    280	{ .compatible = "microchip,mcp4131-103",
    281		.data = &mcp4131_cfg[MCP413x_103] },
    282	{ .compatible = "microchip,mcp4131-503",
    283		.data = &mcp4131_cfg[MCP413x_503] },
    284	{ .compatible = "microchip,mcp4131-104",
    285		.data = &mcp4131_cfg[MCP413x_104] },
    286	{ .compatible = "microchip,mcp4132-502",
    287		.data = &mcp4131_cfg[MCP413x_502] },
    288	{ .compatible = "microchip,mcp4132-103",
    289		.data = &mcp4131_cfg[MCP413x_103] },
    290	{ .compatible = "microchip,mcp4132-503",
    291		.data = &mcp4131_cfg[MCP413x_503] },
    292	{ .compatible = "microchip,mcp4132-104",
    293		.data = &mcp4131_cfg[MCP413x_104] },
    294	{ .compatible = "microchip,mcp4141-502",
    295		.data = &mcp4131_cfg[MCP414x_502] },
    296	{ .compatible = "microchip,mcp4141-103",
    297		.data = &mcp4131_cfg[MCP414x_103] },
    298	{ .compatible = "microchip,mcp4141-503",
    299		.data = &mcp4131_cfg[MCP414x_503] },
    300	{ .compatible = "microchip,mcp4141-104",
    301		.data = &mcp4131_cfg[MCP414x_104] },
    302	{ .compatible = "microchip,mcp4142-502",
    303		.data = &mcp4131_cfg[MCP414x_502] },
    304	{ .compatible = "microchip,mcp4142-103",
    305		.data = &mcp4131_cfg[MCP414x_103] },
    306	{ .compatible = "microchip,mcp4142-503",
    307		.data = &mcp4131_cfg[MCP414x_503] },
    308	{ .compatible = "microchip,mcp4142-104",
    309		.data = &mcp4131_cfg[MCP414x_104] },
    310	{ .compatible = "microchip,mcp4151-502",
    311		.data = &mcp4131_cfg[MCP415x_502] },
    312	{ .compatible = "microchip,mcp4151-103",
    313		.data = &mcp4131_cfg[MCP415x_103] },
    314	{ .compatible = "microchip,mcp4151-503",
    315		.data = &mcp4131_cfg[MCP415x_503] },
    316	{ .compatible = "microchip,mcp4151-104",
    317		.data = &mcp4131_cfg[MCP415x_104] },
    318	{ .compatible = "microchip,mcp4152-502",
    319		.data = &mcp4131_cfg[MCP415x_502] },
    320	{ .compatible = "microchip,mcp4152-103",
    321		.data = &mcp4131_cfg[MCP415x_103] },
    322	{ .compatible = "microchip,mcp4152-503",
    323		.data = &mcp4131_cfg[MCP415x_503] },
    324	{ .compatible = "microchip,mcp4152-104",
    325		.data = &mcp4131_cfg[MCP415x_104] },
    326	{ .compatible = "microchip,mcp4161-502",
    327		.data = &mcp4131_cfg[MCP416x_502] },
    328	{ .compatible = "microchip,mcp4161-103",
    329		.data = &mcp4131_cfg[MCP416x_103] },
    330	{ .compatible = "microchip,mcp4161-503",
    331		.data = &mcp4131_cfg[MCP416x_503] },
    332	{ .compatible = "microchip,mcp4161-104",
    333		.data = &mcp4131_cfg[MCP416x_104] },
    334	{ .compatible = "microchip,mcp4162-502",
    335		.data = &mcp4131_cfg[MCP416x_502] },
    336	{ .compatible = "microchip,mcp4162-103",
    337		.data = &mcp4131_cfg[MCP416x_103] },
    338	{ .compatible = "microchip,mcp4162-503",
    339		.data = &mcp4131_cfg[MCP416x_503] },
    340	{ .compatible = "microchip,mcp4162-104",
    341		.data = &mcp4131_cfg[MCP416x_104] },
    342	{ .compatible = "microchip,mcp4231-502",
    343		.data = &mcp4131_cfg[MCP423x_502] },
    344	{ .compatible = "microchip,mcp4231-103",
    345		.data = &mcp4131_cfg[MCP423x_103] },
    346	{ .compatible = "microchip,mcp4231-503",
    347		.data = &mcp4131_cfg[MCP423x_503] },
    348	{ .compatible = "microchip,mcp4231-104",
    349		.data = &mcp4131_cfg[MCP423x_104] },
    350	{ .compatible = "microchip,mcp4232-502",
    351		.data = &mcp4131_cfg[MCP423x_502] },
    352	{ .compatible = "microchip,mcp4232-103",
    353		.data = &mcp4131_cfg[MCP423x_103] },
    354	{ .compatible = "microchip,mcp4232-503",
    355		.data = &mcp4131_cfg[MCP423x_503] },
    356	{ .compatible = "microchip,mcp4232-104",
    357		.data = &mcp4131_cfg[MCP423x_104] },
    358	{ .compatible = "microchip,mcp4241-502",
    359		.data = &mcp4131_cfg[MCP424x_502] },
    360	{ .compatible = "microchip,mcp4241-103",
    361		.data = &mcp4131_cfg[MCP424x_103] },
    362	{ .compatible = "microchip,mcp4241-503",
    363		.data = &mcp4131_cfg[MCP424x_503] },
    364	{ .compatible = "microchip,mcp4241-104",
    365		.data = &mcp4131_cfg[MCP424x_104] },
    366	{ .compatible = "microchip,mcp4242-502",
    367		.data = &mcp4131_cfg[MCP424x_502] },
    368	{ .compatible = "microchip,mcp4242-103",
    369		.data = &mcp4131_cfg[MCP424x_103] },
    370	{ .compatible = "microchip,mcp4242-503",
    371		.data = &mcp4131_cfg[MCP424x_503] },
    372	{ .compatible = "microchip,mcp4242-104",
    373		.data = &mcp4131_cfg[MCP424x_104] },
    374	{ .compatible = "microchip,mcp4251-502",
    375		.data = &mcp4131_cfg[MCP425x_502] },
    376	{ .compatible = "microchip,mcp4251-103",
    377		.data = &mcp4131_cfg[MCP425x_103] },
    378	{ .compatible = "microchip,mcp4251-503",
    379		.data = &mcp4131_cfg[MCP425x_503] },
    380	{ .compatible = "microchip,mcp4251-104",
    381		.data = &mcp4131_cfg[MCP425x_104] },
    382	{ .compatible = "microchip,mcp4252-502",
    383		.data = &mcp4131_cfg[MCP425x_502] },
    384	{ .compatible = "microchip,mcp4252-103",
    385		.data = &mcp4131_cfg[MCP425x_103] },
    386	{ .compatible = "microchip,mcp4252-503",
    387		.data = &mcp4131_cfg[MCP425x_503] },
    388	{ .compatible = "microchip,mcp4252-104",
    389		.data = &mcp4131_cfg[MCP425x_104] },
    390	{ .compatible = "microchip,mcp4261-502",
    391		.data = &mcp4131_cfg[MCP426x_502] },
    392	{ .compatible = "microchip,mcp4261-103",
    393		.data = &mcp4131_cfg[MCP426x_103] },
    394	{ .compatible = "microchip,mcp4261-503",
    395		.data = &mcp4131_cfg[MCP426x_503] },
    396	{ .compatible = "microchip,mcp4261-104",
    397		.data = &mcp4131_cfg[MCP426x_104] },
    398	{ .compatible = "microchip,mcp4262-502",
    399		.data = &mcp4131_cfg[MCP426x_502] },
    400	{ .compatible = "microchip,mcp4262-103",
    401		.data = &mcp4131_cfg[MCP426x_103] },
    402	{ .compatible = "microchip,mcp4262-503",
    403		.data = &mcp4131_cfg[MCP426x_503] },
    404	{ .compatible = "microchip,mcp4262-104",
    405		.data = &mcp4131_cfg[MCP426x_104] },
    406	{}
    407};
    408MODULE_DEVICE_TABLE(of, mcp4131_dt_ids);
    409
    410static const struct spi_device_id mcp4131_id[] = {
    411	{ "mcp4131-502", MCP413x_502 },
    412	{ "mcp4131-103", MCP413x_103 },
    413	{ "mcp4131-503", MCP413x_503 },
    414	{ "mcp4131-104", MCP413x_104 },
    415	{ "mcp4132-502", MCP413x_502 },
    416	{ "mcp4132-103", MCP413x_103 },
    417	{ "mcp4132-503", MCP413x_503 },
    418	{ "mcp4132-104", MCP413x_104 },
    419	{ "mcp4141-502", MCP414x_502 },
    420	{ "mcp4141-103", MCP414x_103 },
    421	{ "mcp4141-503", MCP414x_503 },
    422	{ "mcp4141-104", MCP414x_104 },
    423	{ "mcp4142-502", MCP414x_502 },
    424	{ "mcp4142-103", MCP414x_103 },
    425	{ "mcp4142-503", MCP414x_503 },
    426	{ "mcp4142-104", MCP414x_104 },
    427	{ "mcp4151-502", MCP415x_502 },
    428	{ "mcp4151-103", MCP415x_103 },
    429	{ "mcp4151-503", MCP415x_503 },
    430	{ "mcp4151-104", MCP415x_104 },
    431	{ "mcp4152-502", MCP415x_502 },
    432	{ "mcp4152-103", MCP415x_103 },
    433	{ "mcp4152-503", MCP415x_503 },
    434	{ "mcp4152-104", MCP415x_104 },
    435	{ "mcp4161-502", MCP416x_502 },
    436	{ "mcp4161-103", MCP416x_103 },
    437	{ "mcp4161-503", MCP416x_503 },
    438	{ "mcp4161-104", MCP416x_104 },
    439	{ "mcp4162-502", MCP416x_502 },
    440	{ "mcp4162-103", MCP416x_103 },
    441	{ "mcp4162-503", MCP416x_503 },
    442	{ "mcp4162-104", MCP416x_104 },
    443	{ "mcp4231-502", MCP423x_502 },
    444	{ "mcp4231-103", MCP423x_103 },
    445	{ "mcp4231-503", MCP423x_503 },
    446	{ "mcp4231-104", MCP423x_104 },
    447	{ "mcp4232-502", MCP423x_502 },
    448	{ "mcp4232-103", MCP423x_103 },
    449	{ "mcp4232-503", MCP423x_503 },
    450	{ "mcp4232-104", MCP423x_104 },
    451	{ "mcp4241-502", MCP424x_502 },
    452	{ "mcp4241-103", MCP424x_103 },
    453	{ "mcp4241-503", MCP424x_503 },
    454	{ "mcp4241-104", MCP424x_104 },
    455	{ "mcp4242-502", MCP424x_502 },
    456	{ "mcp4242-103", MCP424x_103 },
    457	{ "mcp4242-503", MCP424x_503 },
    458	{ "mcp4242-104", MCP424x_104 },
    459	{ "mcp4251-502", MCP425x_502 },
    460	{ "mcp4251-103", MCP425x_103 },
    461	{ "mcp4251-503", MCP425x_503 },
    462	{ "mcp4251-104", MCP425x_104 },
    463	{ "mcp4252-502", MCP425x_502 },
    464	{ "mcp4252-103", MCP425x_103 },
    465	{ "mcp4252-503", MCP425x_503 },
    466	{ "mcp4252-104", MCP425x_104 },
    467	{ "mcp4261-502", MCP426x_502 },
    468	{ "mcp4261-103", MCP426x_103 },
    469	{ "mcp4261-503", MCP426x_503 },
    470	{ "mcp4261-104", MCP426x_104 },
    471	{ "mcp4262-502", MCP426x_502 },
    472	{ "mcp4262-103", MCP426x_103 },
    473	{ "mcp4262-503", MCP426x_503 },
    474	{ "mcp4262-104", MCP426x_104 },
    475	{}
    476};
    477MODULE_DEVICE_TABLE(spi, mcp4131_id);
    478
    479static struct spi_driver mcp4131_driver = {
    480	.driver = {
    481		.name	= "mcp4131",
    482		.of_match_table = mcp4131_dt_ids,
    483	},
    484	.probe		= mcp4131_probe,
    485	.id_table	= mcp4131_id,
    486};
    487
    488module_spi_driver(mcp4131_driver);
    489
    490MODULE_AUTHOR("Slawomir Stepien <sst@poczta.fm>");
    491MODULE_DESCRIPTION("MCP4131 digital potentiometer");
    492MODULE_LICENSE("GPL v2");