1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Maxim Integrated DS1803 and similar digital potentiometer driver |
4 | * Copyright (c) 2016 Slawomir Stepien |
5 | * Copyright (c) 2022 Jagath Jog J |
6 | * |
7 | * Datasheet: https://datasheets.maximintegrated.com/en/ds/DS1803.pdf |
8 | * Datasheet: https://datasheets.maximintegrated.com/en/ds/DS3502.pdf |
9 | * |
10 | * DEVID #Wipers #Positions Resistor Opts (kOhm) i2c address |
11 | * ds1803 2 256 10, 50, 100 0101xxx |
12 | * ds3502 1 128 10 01010xx |
13 | */ |
14 | |
15 | #include <linux/err.h> |
16 | #include <linux/export.h> |
17 | #include <linux/i2c.h> |
18 | #include <linux/iio/iio.h> |
19 | #include <linux/module.h> |
20 | #include <linux/mod_devicetable.h> |
21 | #include <linux/property.h> |
22 | |
23 | #define DS1803_WIPER_0 0xA9 |
24 | #define DS1803_WIPER_1 0xAA |
25 | #define DS3502_WR_IVR 0x00 |
26 | |
27 | enum ds1803_type { |
28 | DS1803_010, |
29 | DS1803_050, |
30 | DS1803_100, |
31 | DS3502, |
32 | }; |
33 | |
34 | struct ds1803_cfg { |
35 | int wipers; |
36 | int avail[3]; |
37 | int kohms; |
38 | const struct iio_chan_spec *channels; |
39 | u8 num_channels; |
40 | int (*read)(struct iio_dev *indio_dev, |
41 | struct iio_chan_spec const *chan, int *val); |
42 | }; |
43 | |
44 | struct ds1803_data { |
45 | struct i2c_client *client; |
46 | const struct ds1803_cfg *cfg; |
47 | }; |
48 | |
49 | #define DS1803_CHANNEL(ch, addr) { \ |
50 | .type = IIO_RESISTANCE, \ |
51 | .indexed = 1, \ |
52 | .output = 1, \ |
53 | .channel = (ch), \ |
54 | .address = (addr), \ |
55 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ |
56 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ |
57 | .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_RAW), \ |
58 | } |
59 | |
60 | static const struct iio_chan_spec ds1803_channels[] = { |
61 | DS1803_CHANNEL(0, DS1803_WIPER_0), |
62 | DS1803_CHANNEL(1, DS1803_WIPER_1), |
63 | }; |
64 | |
65 | static const struct iio_chan_spec ds3502_channels[] = { |
66 | DS1803_CHANNEL(0, DS3502_WR_IVR), |
67 | }; |
68 | |
69 | static int ds1803_read(struct iio_dev *indio_dev, |
70 | struct iio_chan_spec const *chan, |
71 | int *val) |
72 | { |
73 | struct ds1803_data *data = iio_priv(indio_dev); |
74 | int ret; |
75 | u8 result[ARRAY_SIZE(ds1803_channels)]; |
76 | |
77 | ret = i2c_master_recv(client: data->client, buf: result, count: indio_dev->num_channels); |
78 | if (ret < 0) |
79 | return ret; |
80 | |
81 | *val = result[chan->channel]; |
82 | return ret; |
83 | } |
84 | |
85 | static int ds3502_read(struct iio_dev *indio_dev, |
86 | struct iio_chan_spec const *chan, |
87 | int *val) |
88 | { |
89 | struct ds1803_data *data = iio_priv(indio_dev); |
90 | int ret; |
91 | |
92 | ret = i2c_smbus_read_byte_data(client: data->client, command: chan->address); |
93 | if (ret < 0) |
94 | return ret; |
95 | |
96 | *val = ret; |
97 | return ret; |
98 | } |
99 | |
100 | static const struct ds1803_cfg ds1803_cfg[] = { |
101 | [DS1803_010] = { |
102 | .wipers = 2, |
103 | .avail = { 0, 1, 255 }, |
104 | .kohms = 10, |
105 | .channels = ds1803_channels, |
106 | .num_channels = ARRAY_SIZE(ds1803_channels), |
107 | .read = ds1803_read, |
108 | }, |
109 | [DS1803_050] = { |
110 | .wipers = 2, |
111 | .avail = { 0, 1, 255 }, |
112 | .kohms = 50, |
113 | .channels = ds1803_channels, |
114 | .num_channels = ARRAY_SIZE(ds1803_channels), |
115 | .read = ds1803_read, |
116 | }, |
117 | [DS1803_100] = { |
118 | .wipers = 2, |
119 | .avail = { 0, 1, 255 }, |
120 | .kohms = 100, |
121 | .channels = ds1803_channels, |
122 | .num_channels = ARRAY_SIZE(ds1803_channels), |
123 | .read = ds1803_read, |
124 | }, |
125 | [DS3502] = { |
126 | .wipers = 1, |
127 | .avail = { 0, 1, 127 }, |
128 | .kohms = 10, |
129 | .channels = ds3502_channels, |
130 | .num_channels = ARRAY_SIZE(ds3502_channels), |
131 | .read = ds3502_read, |
132 | }, |
133 | }; |
134 | |
135 | static int ds1803_read_raw(struct iio_dev *indio_dev, |
136 | struct iio_chan_spec const *chan, |
137 | int *val, int *val2, long mask) |
138 | { |
139 | struct ds1803_data *data = iio_priv(indio_dev); |
140 | int ret; |
141 | |
142 | switch (mask) { |
143 | case IIO_CHAN_INFO_RAW: |
144 | ret = data->cfg->read(indio_dev, chan, val); |
145 | if (ret < 0) |
146 | return ret; |
147 | |
148 | return IIO_VAL_INT; |
149 | |
150 | case IIO_CHAN_INFO_SCALE: |
151 | *val = 1000 * data->cfg->kohms; |
152 | *val2 = data->cfg->avail[2]; /* Max wiper position */ |
153 | return IIO_VAL_FRACTIONAL; |
154 | } |
155 | |
156 | return -EINVAL; |
157 | } |
158 | |
159 | static int ds1803_write_raw(struct iio_dev *indio_dev, |
160 | struct iio_chan_spec const *chan, |
161 | int val, int val2, long mask) |
162 | { |
163 | struct ds1803_data *data = iio_priv(indio_dev); |
164 | u8 addr = chan->address; |
165 | int max_pos = data->cfg->avail[2]; |
166 | |
167 | if (val2 != 0) |
168 | return -EINVAL; |
169 | |
170 | switch (mask) { |
171 | case IIO_CHAN_INFO_RAW: |
172 | if (val > max_pos || val < 0) |
173 | return -EINVAL; |
174 | break; |
175 | default: |
176 | return -EINVAL; |
177 | } |
178 | |
179 | return i2c_smbus_write_byte_data(client: data->client, command: addr, value: val); |
180 | } |
181 | |
182 | static int ds1803_read_avail(struct iio_dev *indio_dev, |
183 | struct iio_chan_spec const *chan, |
184 | const int **vals, int *type, |
185 | int *length, long mask) |
186 | { |
187 | struct ds1803_data *data = iio_priv(indio_dev); |
188 | |
189 | switch (mask) { |
190 | case IIO_CHAN_INFO_RAW: |
191 | *vals = data->cfg->avail; |
192 | *length = ARRAY_SIZE(data->cfg->avail); |
193 | *type = IIO_VAL_INT; |
194 | return IIO_AVAIL_RANGE; |
195 | } |
196 | return -EINVAL; |
197 | } |
198 | |
199 | static const struct iio_info ds1803_info = { |
200 | .read_raw = ds1803_read_raw, |
201 | .write_raw = ds1803_write_raw, |
202 | .read_avail = ds1803_read_avail, |
203 | }; |
204 | |
205 | static int ds1803_probe(struct i2c_client *client) |
206 | { |
207 | struct device *dev = &client->dev; |
208 | struct ds1803_data *data; |
209 | struct iio_dev *indio_dev; |
210 | |
211 | indio_dev = devm_iio_device_alloc(parent: dev, sizeof_priv: sizeof(*data)); |
212 | if (!indio_dev) |
213 | return -ENOMEM; |
214 | |
215 | i2c_set_clientdata(client, data: indio_dev); |
216 | |
217 | data = iio_priv(indio_dev); |
218 | data->client = client; |
219 | data->cfg = i2c_get_match_data(client); |
220 | |
221 | indio_dev->info = &ds1803_info; |
222 | indio_dev->channels = data->cfg->channels; |
223 | indio_dev->num_channels = data->cfg->num_channels; |
224 | indio_dev->name = client->name; |
225 | |
226 | return devm_iio_device_register(dev, indio_dev); |
227 | } |
228 | |
229 | static const struct of_device_id ds1803_dt_ids[] = { |
230 | { .compatible = "maxim,ds1803-010" , .data = &ds1803_cfg[DS1803_010] }, |
231 | { .compatible = "maxim,ds1803-050" , .data = &ds1803_cfg[DS1803_050] }, |
232 | { .compatible = "maxim,ds1803-100" , .data = &ds1803_cfg[DS1803_100] }, |
233 | { .compatible = "maxim,ds3502" , .data = &ds1803_cfg[DS3502] }, |
234 | {} |
235 | }; |
236 | MODULE_DEVICE_TABLE(of, ds1803_dt_ids); |
237 | |
238 | static const struct i2c_device_id ds1803_id[] = { |
239 | { "ds1803-010" , (kernel_ulong_t)&ds1803_cfg[DS1803_010] }, |
240 | { "ds1803-050" , (kernel_ulong_t)&ds1803_cfg[DS1803_050] }, |
241 | { "ds1803-100" , (kernel_ulong_t)&ds1803_cfg[DS1803_100] }, |
242 | { "ds3502" , (kernel_ulong_t)&ds1803_cfg[DS3502] }, |
243 | {} |
244 | }; |
245 | MODULE_DEVICE_TABLE(i2c, ds1803_id); |
246 | |
247 | static struct i2c_driver ds1803_driver = { |
248 | .driver = { |
249 | .name = "ds1803" , |
250 | .of_match_table = ds1803_dt_ids, |
251 | }, |
252 | .probe = ds1803_probe, |
253 | .id_table = ds1803_id, |
254 | }; |
255 | |
256 | module_i2c_driver(ds1803_driver); |
257 | |
258 | MODULE_AUTHOR("Slawomir Stepien <sst@poczta.fm>" ); |
259 | MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.com>" ); |
260 | MODULE_DESCRIPTION("DS1803 digital potentiometer" ); |
261 | MODULE_LICENSE("GPL v2" ); |
262 | |