1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Industrial I/O driver for Microchip digital potentiometers |
4 | * Copyright (c) 2018 Axentia Technologies AB |
5 | * Author: Peter Rosin <peda@axentia.se> |
6 | * |
7 | * Datasheet: http://www.microchip.com/downloads/en/DeviceDoc/22147a.pdf |
8 | * |
9 | * DEVID #Wipers #Positions Resistor Opts (kOhm) |
10 | * mcp4017 1 128 5, 10, 50, 100 |
11 | * mcp4018 1 128 5, 10, 50, 100 |
12 | * mcp4019 1 128 5, 10, 50, 100 |
13 | */ |
14 | |
15 | #include <linux/err.h> |
16 | #include <linux/i2c.h> |
17 | #include <linux/iio/iio.h> |
18 | #include <linux/module.h> |
19 | #include <linux/mod_devicetable.h> |
20 | #include <linux/property.h> |
21 | |
22 | #define MCP4018_WIPER_MAX 127 |
23 | |
24 | struct mcp4018_cfg { |
25 | int kohms; |
26 | }; |
27 | |
28 | enum mcp4018_type { |
29 | MCP4018_502, |
30 | MCP4018_103, |
31 | MCP4018_503, |
32 | MCP4018_104, |
33 | }; |
34 | |
35 | static const struct mcp4018_cfg mcp4018_cfg[] = { |
36 | [MCP4018_502] = { .kohms = 5, }, |
37 | [MCP4018_103] = { .kohms = 10, }, |
38 | [MCP4018_503] = { .kohms = 50, }, |
39 | [MCP4018_104] = { .kohms = 100, }, |
40 | }; |
41 | |
42 | struct mcp4018_data { |
43 | struct i2c_client *client; |
44 | const struct mcp4018_cfg *cfg; |
45 | }; |
46 | |
47 | static const struct iio_chan_spec mcp4018_channel = { |
48 | .type = IIO_RESISTANCE, |
49 | .indexed = 1, |
50 | .output = 1, |
51 | .channel = 0, |
52 | .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), |
53 | .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), |
54 | }; |
55 | |
56 | static int mcp4018_read_raw(struct iio_dev *indio_dev, |
57 | struct iio_chan_spec const *chan, |
58 | int *val, int *val2, long mask) |
59 | { |
60 | struct mcp4018_data *data = iio_priv(indio_dev); |
61 | s32 ret; |
62 | |
63 | switch (mask) { |
64 | case IIO_CHAN_INFO_RAW: |
65 | ret = i2c_smbus_read_byte(client: data->client); |
66 | if (ret < 0) |
67 | return ret; |
68 | *val = ret; |
69 | return IIO_VAL_INT; |
70 | case IIO_CHAN_INFO_SCALE: |
71 | *val = 1000 * data->cfg->kohms; |
72 | *val2 = MCP4018_WIPER_MAX; |
73 | return IIO_VAL_FRACTIONAL; |
74 | } |
75 | |
76 | return -EINVAL; |
77 | } |
78 | |
79 | static int mcp4018_write_raw(struct iio_dev *indio_dev, |
80 | struct iio_chan_spec const *chan, |
81 | int val, int val2, long mask) |
82 | { |
83 | struct mcp4018_data *data = iio_priv(indio_dev); |
84 | |
85 | switch (mask) { |
86 | case IIO_CHAN_INFO_RAW: |
87 | if (val > MCP4018_WIPER_MAX || val < 0) |
88 | return -EINVAL; |
89 | break; |
90 | default: |
91 | return -EINVAL; |
92 | } |
93 | |
94 | return i2c_smbus_write_byte(client: data->client, value: val); |
95 | } |
96 | |
97 | static const struct iio_info mcp4018_info = { |
98 | .read_raw = mcp4018_read_raw, |
99 | .write_raw = mcp4018_write_raw, |
100 | }; |
101 | |
102 | #define MCP4018_ID_TABLE(_name, cfg) { \ |
103 | .name = _name, \ |
104 | .driver_data = (kernel_ulong_t)&mcp4018_cfg[cfg], \ |
105 | } |
106 | |
107 | static const struct i2c_device_id mcp4018_id[] = { |
108 | MCP4018_ID_TABLE("mcp4017-502" , MCP4018_502), |
109 | MCP4018_ID_TABLE("mcp4017-103" , MCP4018_103), |
110 | MCP4018_ID_TABLE("mcp4017-503" , MCP4018_503), |
111 | MCP4018_ID_TABLE("mcp4017-104" , MCP4018_104), |
112 | MCP4018_ID_TABLE("mcp4018-502" , MCP4018_502), |
113 | MCP4018_ID_TABLE("mcp4018-103" , MCP4018_103), |
114 | MCP4018_ID_TABLE("mcp4018-503" , MCP4018_503), |
115 | MCP4018_ID_TABLE("mcp4018-104" , MCP4018_104), |
116 | MCP4018_ID_TABLE("mcp4019-502" , MCP4018_502), |
117 | MCP4018_ID_TABLE("mcp4019-103" , MCP4018_103), |
118 | MCP4018_ID_TABLE("mcp4019-503" , MCP4018_503), |
119 | MCP4018_ID_TABLE("mcp4019-104" , MCP4018_104), |
120 | { /* sentinel */ } |
121 | }; |
122 | MODULE_DEVICE_TABLE(i2c, mcp4018_id); |
123 | |
124 | #define MCP4018_COMPATIBLE(of_compatible, cfg) { \ |
125 | .compatible = of_compatible, \ |
126 | .data = &mcp4018_cfg[cfg], \ |
127 | } |
128 | |
129 | static const struct of_device_id mcp4018_of_match[] = { |
130 | MCP4018_COMPATIBLE("microchip,mcp4017-502" , MCP4018_502), |
131 | MCP4018_COMPATIBLE("microchip,mcp4017-103" , MCP4018_103), |
132 | MCP4018_COMPATIBLE("microchip,mcp4017-503" , MCP4018_503), |
133 | MCP4018_COMPATIBLE("microchip,mcp4017-104" , MCP4018_104), |
134 | MCP4018_COMPATIBLE("microchip,mcp4018-502" , MCP4018_502), |
135 | MCP4018_COMPATIBLE("microchip,mcp4018-103" , MCP4018_103), |
136 | MCP4018_COMPATIBLE("microchip,mcp4018-503" , MCP4018_503), |
137 | MCP4018_COMPATIBLE("microchip,mcp4018-104" , MCP4018_104), |
138 | MCP4018_COMPATIBLE("microchip,mcp4019-502" , MCP4018_502), |
139 | MCP4018_COMPATIBLE("microchip,mcp4019-103" , MCP4018_103), |
140 | MCP4018_COMPATIBLE("microchip,mcp4019-503" , MCP4018_503), |
141 | MCP4018_COMPATIBLE("microchip,mcp4019-104" , MCP4018_104), |
142 | { /* sentinel */ } |
143 | }; |
144 | MODULE_DEVICE_TABLE(of, mcp4018_of_match); |
145 | |
146 | static int mcp4018_probe(struct i2c_client *client) |
147 | { |
148 | struct device *dev = &client->dev; |
149 | struct mcp4018_data *data; |
150 | struct iio_dev *indio_dev; |
151 | |
152 | if (!i2c_check_functionality(adap: client->adapter, |
153 | I2C_FUNC_SMBUS_BYTE)) { |
154 | dev_err(dev, "SMBUS Byte transfers not supported\n" ); |
155 | return -EOPNOTSUPP; |
156 | } |
157 | |
158 | indio_dev = devm_iio_device_alloc(parent: dev, sizeof_priv: sizeof(*data)); |
159 | if (!indio_dev) |
160 | return -ENOMEM; |
161 | data = iio_priv(indio_dev); |
162 | i2c_set_clientdata(client, data: indio_dev); |
163 | data->client = client; |
164 | |
165 | data->cfg = i2c_get_match_data(client); |
166 | |
167 | indio_dev->info = &mcp4018_info; |
168 | indio_dev->channels = &mcp4018_channel; |
169 | indio_dev->num_channels = 1; |
170 | indio_dev->name = client->name; |
171 | |
172 | return devm_iio_device_register(dev, indio_dev); |
173 | } |
174 | |
175 | static struct i2c_driver mcp4018_driver = { |
176 | .driver = { |
177 | .name = "mcp4018" , |
178 | .of_match_table = mcp4018_of_match, |
179 | }, |
180 | .probe = mcp4018_probe, |
181 | .id_table = mcp4018_id, |
182 | }; |
183 | |
184 | module_i2c_driver(mcp4018_driver); |
185 | |
186 | MODULE_AUTHOR("Peter Rosin <peda@axentia.se>" ); |
187 | MODULE_DESCRIPTION("MCP4018 digital potentiometer" ); |
188 | MODULE_LICENSE("GPL v2" ); |
189 | |