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; | |
91307cbe | 82 | const struct mcp4531_cfg *cfg; |
c05dc2cc PR |
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: | |
91307cbe SS |
116 | *val = 1000 * data->cfg->kohms; |
117 | *val2 = data->cfg->max_pos; | |
c05dc2cc PR |
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: | |
91307cbe | 133 | if (val > data->cfg->max_pos || val < 0) |
c05dc2cc PR |
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; | |
c05dc2cc PR |
155 | struct mcp4531_data *data; |
156 | struct iio_dev *indio_dev; | |
157 | ||
158 | if (!i2c_check_functionality(client->adapter, | |
159 | I2C_FUNC_SMBUS_WORD_DATA)) { | |
160 | dev_err(dev, "SMBUS Word Data not supported\n"); | |
f8d9d3b4 | 161 | return -EOPNOTSUPP; |
c05dc2cc PR |
162 | } |
163 | ||
164 | indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); | |
165 | if (!indio_dev) | |
166 | return -ENOMEM; | |
167 | data = iio_priv(indio_dev); | |
168 | i2c_set_clientdata(client, indio_dev); | |
169 | data->client = client; | |
91307cbe | 170 | data->cfg = &mcp4531_cfg[id->driver_data]; |
c05dc2cc PR |
171 | |
172 | indio_dev->dev.parent = dev; | |
173 | indio_dev->info = &mcp4531_info; | |
174 | indio_dev->channels = mcp4531_channels; | |
91307cbe | 175 | indio_dev->num_channels = data->cfg->wipers; |
c05dc2cc PR |
176 | indio_dev->name = client->name; |
177 | ||
178 | return devm_iio_device_register(dev, indio_dev); | |
179 | } | |
180 | ||
181 | static const struct i2c_device_id mcp4531_id[] = { | |
182 | { "mcp4531-502", MCP453x_502 }, | |
183 | { "mcp4531-103", MCP453x_103 }, | |
184 | { "mcp4531-503", MCP453x_503 }, | |
185 | { "mcp4531-104", MCP453x_104 }, | |
186 | { "mcp4532-502", MCP453x_502 }, | |
187 | { "mcp4532-103", MCP453x_103 }, | |
188 | { "mcp4532-503", MCP453x_503 }, | |
189 | { "mcp4532-104", MCP453x_104 }, | |
190 | { "mcp4551-502", MCP455x_502 }, | |
191 | { "mcp4551-103", MCP455x_103 }, | |
192 | { "mcp4551-503", MCP455x_503 }, | |
193 | { "mcp4551-104", MCP455x_104 }, | |
194 | { "mcp4552-502", MCP455x_502 }, | |
195 | { "mcp4552-103", MCP455x_103 }, | |
196 | { "mcp4552-503", MCP455x_503 }, | |
197 | { "mcp4552-104", MCP455x_104 }, | |
198 | { "mcp4631-502", MCP463x_502 }, | |
199 | { "mcp4631-103", MCP463x_103 }, | |
200 | { "mcp4631-503", MCP463x_503 }, | |
201 | { "mcp4631-104", MCP463x_104 }, | |
202 | { "mcp4632-502", MCP463x_502 }, | |
203 | { "mcp4632-103", MCP463x_103 }, | |
204 | { "mcp4632-503", MCP463x_503 }, | |
205 | { "mcp4632-104", MCP463x_104 }, | |
206 | { "mcp4651-502", MCP465x_502 }, | |
207 | { "mcp4651-103", MCP465x_103 }, | |
208 | { "mcp4651-503", MCP465x_503 }, | |
209 | { "mcp4651-104", MCP465x_104 }, | |
210 | { "mcp4652-502", MCP465x_502 }, | |
211 | { "mcp4652-103", MCP465x_103 }, | |
212 | { "mcp4652-503", MCP465x_503 }, | |
213 | { "mcp4652-104", MCP465x_104 }, | |
214 | {} | |
215 | }; | |
216 | MODULE_DEVICE_TABLE(i2c, mcp4531_id); | |
217 | ||
218 | static struct i2c_driver mcp4531_driver = { | |
219 | .driver = { | |
220 | .name = "mcp4531", | |
221 | }, | |
222 | .probe = mcp4531_probe, | |
223 | .id_table = mcp4531_id, | |
224 | }; | |
225 | ||
226 | module_i2c_driver(mcp4531_driver); | |
227 | ||
228 | MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); | |
229 | MODULE_DESCRIPTION("MCP4531 digital potentiometer"); | |
230 | MODULE_LICENSE("GPL"); |