Commit | Line | Data |
---|---|---|
c05dc2cc PR |
1 | /* |
2 | * Industrial I/O driver for Microchip digital potentiometers | |
3 | * Copyright (c) 2015 Axentia Technologies AB | |
4 | * Author: Peter Rosin <peda@axentia.se> | |
5 | * | |
6 | * Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22096b.pdf | |
7 | * | |
8 | * DEVID #Wipers #Positions Resistor Opts (kOhm) i2c address | |
9 | * mcp4531 1 129 5, 10, 50, 100 010111x | |
10 | * mcp4532 1 129 5, 10, 50, 100 01011xx | |
11 | * mcp4551 1 257 5, 10, 50, 100 010111x | |
12 | * mcp4552 1 257 5, 10, 50, 100 01011xx | |
13 | * mcp4631 2 129 5, 10, 50, 100 0101xxx | |
14 | * mcp4632 2 129 5, 10, 50, 100 01011xx | |
15 | * mcp4651 2 257 5, 10, 50, 100 0101xxx | |
16 | * mcp4652 2 257 5, 10, 50, 100 01011xx | |
17 | * | |
18 | * This program is free software; you can redistribute it and/or modify it | |
19 | * under the terms of the GNU General Public License version 2 as published by | |
20 | * the Free Software Foundation. | |
21 | */ | |
22 | ||
23 | #include <linux/module.h> | |
24 | #include <linux/i2c.h> | |
25 | #include <linux/err.h> | |
26 | ||
27 | #include <linux/iio/iio.h> | |
28 | ||
29 | struct mcp4531_cfg { | |
30 | int wipers; | |
31 | int max_pos; | |
32 | int kohms; | |
33 | }; | |
34 | ||
35 | enum mcp4531_type { | |
36 | MCP453x_502, | |
37 | MCP453x_103, | |
38 | MCP453x_503, | |
39 | MCP453x_104, | |
40 | MCP455x_502, | |
41 | MCP455x_103, | |
42 | MCP455x_503, | |
43 | MCP455x_104, | |
44 | MCP463x_502, | |
45 | MCP463x_103, | |
46 | MCP463x_503, | |
47 | MCP463x_104, | |
48 | MCP465x_502, | |
49 | MCP465x_103, | |
50 | MCP465x_503, | |
51 | MCP465x_104, | |
52 | }; | |
53 | ||
54 | static const struct mcp4531_cfg mcp4531_cfg[] = { | |
55 | [MCP453x_502] = { .wipers = 1, .max_pos = 128, .kohms = 5, }, | |
56 | [MCP453x_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, }, | |
57 | [MCP453x_503] = { .wipers = 1, .max_pos = 128, .kohms = 50, }, | |
58 | [MCP453x_104] = { .wipers = 1, .max_pos = 128, .kohms = 100, }, | |
59 | [MCP455x_502] = { .wipers = 1, .max_pos = 256, .kohms = 5, }, | |
60 | [MCP455x_103] = { .wipers = 1, .max_pos = 256, .kohms = 10, }, | |
61 | [MCP455x_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, }, | |
62 | [MCP455x_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, }, | |
63 | [MCP463x_502] = { .wipers = 2, .max_pos = 128, .kohms = 5, }, | |
64 | [MCP463x_103] = { .wipers = 2, .max_pos = 128, .kohms = 10, }, | |
65 | [MCP463x_503] = { .wipers = 2, .max_pos = 128, .kohms = 50, }, | |
66 | [MCP463x_104] = { .wipers = 2, .max_pos = 128, .kohms = 100, }, | |
67 | [MCP465x_502] = { .wipers = 2, .max_pos = 256, .kohms = 5, }, | |
68 | [MCP465x_103] = { .wipers = 2, .max_pos = 256, .kohms = 10, }, | |
69 | [MCP465x_503] = { .wipers = 2, .max_pos = 256, .kohms = 50, }, | |
70 | [MCP465x_104] = { .wipers = 2, .max_pos = 256, .kohms = 100, }, | |
71 | }; | |
72 | ||
73 | #define MCP4531_WRITE (0 << 2) | |
74 | #define MCP4531_INCR (1 << 2) | |
75 | #define MCP4531_DECR (2 << 2) | |
76 | #define MCP4531_READ (3 << 2) | |
77 | ||
78 | #define MCP4531_WIPER_SHIFT (4) | |
79 | ||
80 | struct mcp4531_data { | |
81 | struct i2c_client *client; | |
82 | unsigned long devid; | |
83 | }; | |
84 | ||
85 | #define MCP4531_CHANNEL(ch) { \ | |
86 | .type = IIO_RESISTANCE, \ | |
87 | .indexed = 1, \ | |
88 | .output = 1, \ | |
89 | .channel = (ch), \ | |
90 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ | |
91 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ | |
92 | } | |
93 | ||
94 | static const struct iio_chan_spec mcp4531_channels[] = { | |
95 | MCP4531_CHANNEL(0), | |
96 | MCP4531_CHANNEL(1), | |
97 | }; | |
98 | ||
99 | static int mcp4531_read_raw(struct iio_dev *indio_dev, | |
100 | struct iio_chan_spec const *chan, | |
101 | int *val, int *val2, long mask) | |
102 | { | |
103 | struct mcp4531_data *data = iio_priv(indio_dev); | |
104 | int address = chan->channel << MCP4531_WIPER_SHIFT; | |
105 | s32 ret; | |
106 | ||
107 | switch (mask) { | |
108 | case IIO_CHAN_INFO_RAW: | |
109 | ret = i2c_smbus_read_word_swapped(data->client, | |
110 | MCP4531_READ | address); | |
111 | if (ret < 0) | |
112 | return ret; | |
113 | *val = ret; | |
114 | return IIO_VAL_INT; | |
115 | case IIO_CHAN_INFO_SCALE: | |
116 | *val = 1000 * mcp4531_cfg[data->devid].kohms; | |
117 | *val2 = mcp4531_cfg[data->devid].max_pos; | |
118 | return IIO_VAL_FRACTIONAL; | |
119 | } | |
120 | ||
121 | return -EINVAL; | |
122 | } | |
123 | ||
124 | static int mcp4531_write_raw(struct iio_dev *indio_dev, | |
125 | struct iio_chan_spec const *chan, | |
126 | int val, int val2, long mask) | |
127 | { | |
128 | struct mcp4531_data *data = iio_priv(indio_dev); | |
129 | int address = chan->channel << MCP4531_WIPER_SHIFT; | |
130 | ||
131 | switch (mask) { | |
132 | case IIO_CHAN_INFO_RAW: | |
133 | if (val > mcp4531_cfg[data->devid].max_pos || val < 0) | |
134 | return -EINVAL; | |
135 | break; | |
136 | default: | |
137 | return -EINVAL; | |
138 | } | |
139 | ||
140 | return i2c_smbus_write_byte_data(data->client, | |
141 | MCP4531_WRITE | address | (val >> 8), | |
142 | val & 0xff); | |
143 | } | |
144 | ||
145 | static const struct iio_info mcp4531_info = { | |
146 | .read_raw = mcp4531_read_raw, | |
147 | .write_raw = mcp4531_write_raw, | |
148 | .driver_module = THIS_MODULE, | |
149 | }; | |
150 | ||
151 | static int mcp4531_probe(struct i2c_client *client, | |
152 | const struct i2c_device_id *id) | |
153 | { | |
154 | struct device *dev = &client->dev; | |
155 | unsigned long devid = id->driver_data; | |
156 | struct mcp4531_data *data; | |
157 | struct iio_dev *indio_dev; | |
158 | ||
159 | if (!i2c_check_functionality(client->adapter, | |
160 | I2C_FUNC_SMBUS_WORD_DATA)) { | |
161 | dev_err(dev, "SMBUS Word Data not supported\n"); | |
f8d9d3b4 | 162 | return -EOPNOTSUPP; |
c05dc2cc PR |
163 | } |
164 | ||
165 | indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); | |
166 | if (!indio_dev) | |
167 | return -ENOMEM; | |
168 | data = iio_priv(indio_dev); | |
169 | i2c_set_clientdata(client, indio_dev); | |
170 | data->client = client; | |
171 | data->devid = devid; | |
172 | ||
173 | indio_dev->dev.parent = dev; | |
174 | indio_dev->info = &mcp4531_info; | |
175 | indio_dev->channels = mcp4531_channels; | |
176 | indio_dev->num_channels = mcp4531_cfg[devid].wipers; | |
177 | indio_dev->name = client->name; | |
178 | ||
179 | return devm_iio_device_register(dev, indio_dev); | |
180 | } | |
181 | ||
182 | static const struct i2c_device_id mcp4531_id[] = { | |
183 | { "mcp4531-502", MCP453x_502 }, | |
184 | { "mcp4531-103", MCP453x_103 }, | |
185 | { "mcp4531-503", MCP453x_503 }, | |
186 | { "mcp4531-104", MCP453x_104 }, | |
187 | { "mcp4532-502", MCP453x_502 }, | |
188 | { "mcp4532-103", MCP453x_103 }, | |
189 | { "mcp4532-503", MCP453x_503 }, | |
190 | { "mcp4532-104", MCP453x_104 }, | |
191 | { "mcp4551-502", MCP455x_502 }, | |
192 | { "mcp4551-103", MCP455x_103 }, | |
193 | { "mcp4551-503", MCP455x_503 }, | |
194 | { "mcp4551-104", MCP455x_104 }, | |
195 | { "mcp4552-502", MCP455x_502 }, | |
196 | { "mcp4552-103", MCP455x_103 }, | |
197 | { "mcp4552-503", MCP455x_503 }, | |
198 | { "mcp4552-104", MCP455x_104 }, | |
199 | { "mcp4631-502", MCP463x_502 }, | |
200 | { "mcp4631-103", MCP463x_103 }, | |
201 | { "mcp4631-503", MCP463x_503 }, | |
202 | { "mcp4631-104", MCP463x_104 }, | |
203 | { "mcp4632-502", MCP463x_502 }, | |
204 | { "mcp4632-103", MCP463x_103 }, | |
205 | { "mcp4632-503", MCP463x_503 }, | |
206 | { "mcp4632-104", MCP463x_104 }, | |
207 | { "mcp4651-502", MCP465x_502 }, | |
208 | { "mcp4651-103", MCP465x_103 }, | |
209 | { "mcp4651-503", MCP465x_503 }, | |
210 | { "mcp4651-104", MCP465x_104 }, | |
211 | { "mcp4652-502", MCP465x_502 }, | |
212 | { "mcp4652-103", MCP465x_103 }, | |
213 | { "mcp4652-503", MCP465x_503 }, | |
214 | { "mcp4652-104", MCP465x_104 }, | |
215 | {} | |
216 | }; | |
217 | MODULE_DEVICE_TABLE(i2c, mcp4531_id); | |
218 | ||
219 | static struct i2c_driver mcp4531_driver = { | |
220 | .driver = { | |
221 | .name = "mcp4531", | |
222 | }, | |
223 | .probe = mcp4531_probe, | |
224 | .id_table = mcp4531_id, | |
225 | }; | |
226 | ||
227 | module_i2c_driver(mcp4531_driver); | |
228 | ||
229 | MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); | |
230 | MODULE_DESCRIPTION("MCP4531 digital potentiometer"); | |
231 | MODULE_LICENSE("GPL"); |