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] __aligned(IIO_DMA_MINALIGN);
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