Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: dac: ds4422/ds4424 dac driver

This patch provides an iio device driver for DS4422/DS4424 chips that support
two/four channel 7-bit Sink/Source Current DAC.

Signed-off-by: Ismail H. Kose <Ismail.Kose@maximintegrated.com>
Signed-off-by: Ismail H. Kose <ihkose@gmail.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Ismail H. Kose and committed by
Jonathan Cameron
d632a2bd b091b9ed

+351
+9
drivers/iio/dac/Kconfig
··· 222 222 To compile this driver as a module, choose M here: the module will be 223 223 called dpot-dac. 224 224 225 + config DS4424 226 + tristate "Maxim Integrated DS4422/DS4424 DAC driver" 227 + depends on I2C 228 + help 229 + If you say yes here you get support for Maxim chips DS4422, DS4424. 230 + 231 + This driver can also be built as a module. If so, the module 232 + will be called ds4424. 233 + 225 234 config LPC18XX_DAC 226 235 tristate "NXP LPC18xx DAC driver" 227 236 depends on ARCH_LPC18XX || COMPILE_TEST
+1
drivers/iio/dac/Makefile
··· 23 23 obj-$(CONFIG_AD8801) += ad8801.o 24 24 obj-$(CONFIG_CIO_DAC) += cio-dac.o 25 25 obj-$(CONFIG_DPOT_DAC) += dpot-dac.o 26 + obj-$(CONFIG_DS4424) += ds4424.o 26 27 obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o 27 28 obj-$(CONFIG_LTC2632) += ltc2632.o 28 29 obj-$(CONFIG_M62332) += m62332.o
+341
drivers/iio/dac/ds4424.c
··· 1 + /* 2 + * Maxim Integrated 3 + * 7-bit, Multi-Channel Sink/Source Current DAC Driver 4 + * Copyright (C) 2017 Maxim Integrated 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + 11 + #include <linux/kernel.h> 12 + #include <linux/module.h> 13 + #include <linux/i2c.h> 14 + #include <linux/regulator/consumer.h> 15 + #include <linux/err.h> 16 + #include <linux/delay.h> 17 + #include <linux/iio/iio.h> 18 + #include <linux/iio/driver.h> 19 + #include <linux/iio/machine.h> 20 + #include <linux/iio/consumer.h> 21 + 22 + #define DS4422_MAX_DAC_CHANNELS 2 23 + #define DS4424_MAX_DAC_CHANNELS 4 24 + 25 + #define DS4424_DAC_ADDR(chan) ((chan) + 0xf8) 26 + #define DS4424_SOURCE_I 1 27 + #define DS4424_SINK_I 0 28 + 29 + #define DS4424_CHANNEL(chan) { \ 30 + .type = IIO_CURRENT, \ 31 + .indexed = 1, \ 32 + .output = 1, \ 33 + .channel = chan, \ 34 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 35 + } 36 + 37 + /* 38 + * DS4424 DAC control register 8 bits 39 + * [7] 0: to sink; 1: to source 40 + * [6:0] steps to sink/source 41 + * bit[7] looks like a sign bit, but the value of the register is 42 + * not a two's complement code considering the bit[6:0] is a absolute 43 + * distance from the zero point. 44 + */ 45 + union ds4424_raw_data { 46 + struct { 47 + u8 dx:7; 48 + u8 source_bit:1; 49 + }; 50 + u8 bits; 51 + }; 52 + 53 + enum ds4424_device_ids { 54 + ID_DS4422, 55 + ID_DS4424, 56 + }; 57 + 58 + struct ds4424_data { 59 + struct i2c_client *client; 60 + struct mutex lock; 61 + uint8_t save[DS4424_MAX_DAC_CHANNELS]; 62 + struct regulator *vcc_reg; 63 + uint8_t raw[DS4424_MAX_DAC_CHANNELS]; 64 + }; 65 + 66 + static const struct iio_chan_spec ds4424_channels[] = { 67 + DS4424_CHANNEL(0), 68 + DS4424_CHANNEL(1), 69 + DS4424_CHANNEL(2), 70 + DS4424_CHANNEL(3), 71 + }; 72 + 73 + static int ds4424_get_value(struct iio_dev *indio_dev, 74 + int *val, int channel) 75 + { 76 + struct ds4424_data *data = iio_priv(indio_dev); 77 + int ret; 78 + 79 + mutex_lock(&data->lock); 80 + ret = i2c_smbus_read_byte_data(data->client, DS4424_DAC_ADDR(channel)); 81 + if (ret < 0) 82 + goto fail; 83 + 84 + *val = ret; 85 + 86 + fail: 87 + mutex_unlock(&data->lock); 88 + return ret; 89 + } 90 + 91 + static int ds4424_set_value(struct iio_dev *indio_dev, 92 + int val, struct iio_chan_spec const *chan) 93 + { 94 + struct ds4424_data *data = iio_priv(indio_dev); 95 + int ret; 96 + 97 + mutex_lock(&data->lock); 98 + ret = i2c_smbus_write_byte_data(data->client, 99 + DS4424_DAC_ADDR(chan->channel), val); 100 + if (ret < 0) 101 + goto fail; 102 + 103 + data->raw[chan->channel] = val; 104 + 105 + fail: 106 + mutex_unlock(&data->lock); 107 + return ret; 108 + } 109 + 110 + static int ds4424_read_raw(struct iio_dev *indio_dev, 111 + struct iio_chan_spec const *chan, 112 + int *val, int *val2, long mask) 113 + { 114 + union ds4424_raw_data raw; 115 + int ret; 116 + 117 + switch (mask) { 118 + case IIO_CHAN_INFO_RAW: 119 + ret = ds4424_get_value(indio_dev, val, chan->channel); 120 + if (ret < 0) { 121 + pr_err("%s : ds4424_get_value returned %d\n", 122 + __func__, ret); 123 + return ret; 124 + } 125 + raw.bits = *val; 126 + *val = raw.dx; 127 + if (raw.source_bit == DS4424_SINK_I) 128 + *val = -*val; 129 + return IIO_VAL_INT; 130 + 131 + default: 132 + return -EINVAL; 133 + } 134 + } 135 + 136 + static int ds4424_write_raw(struct iio_dev *indio_dev, 137 + struct iio_chan_spec const *chan, 138 + int val, int val2, long mask) 139 + { 140 + union ds4424_raw_data raw; 141 + 142 + if (val2 != 0) 143 + return -EINVAL; 144 + 145 + switch (mask) { 146 + case IIO_CHAN_INFO_RAW: 147 + if (val < S8_MIN || val > S8_MAX) 148 + return -EINVAL; 149 + 150 + if (val > 0) { 151 + raw.source_bit = DS4424_SOURCE_I; 152 + raw.dx = val; 153 + } else { 154 + raw.source_bit = DS4424_SINK_I; 155 + raw.dx = -val; 156 + } 157 + 158 + return ds4424_set_value(indio_dev, raw.bits, chan); 159 + 160 + default: 161 + return -EINVAL; 162 + } 163 + } 164 + 165 + static int ds4424_verify_chip(struct iio_dev *indio_dev) 166 + { 167 + int ret, val; 168 + 169 + ret = ds4424_get_value(indio_dev, &val, DS4424_DAC_ADDR(0)); 170 + if (ret < 0) 171 + dev_err(&indio_dev->dev, 172 + "%s failed. ret: %d\n", __func__, ret); 173 + 174 + return ret; 175 + } 176 + 177 + static int __maybe_unused ds4424_suspend(struct device *dev) 178 + { 179 + struct i2c_client *client = to_i2c_client(dev); 180 + struct iio_dev *indio_dev = i2c_get_clientdata(client); 181 + struct ds4424_data *data = iio_priv(indio_dev); 182 + int ret = 0; 183 + int i; 184 + 185 + for (i = 0; i < indio_dev->num_channels; i++) { 186 + data->save[i] = data->raw[i]; 187 + ret = ds4424_set_value(indio_dev, 0, 188 + &indio_dev->channels[i]); 189 + if (ret < 0) 190 + return ret; 191 + } 192 + return ret; 193 + } 194 + 195 + static int __maybe_unused ds4424_resume(struct device *dev) 196 + { 197 + struct i2c_client *client = to_i2c_client(dev); 198 + struct iio_dev *indio_dev = i2c_get_clientdata(client); 199 + struct ds4424_data *data = iio_priv(indio_dev); 200 + int ret = 0; 201 + int i; 202 + 203 + for (i = 0; i < indio_dev->num_channels; i++) { 204 + ret = ds4424_set_value(indio_dev, data->save[i], 205 + &indio_dev->channels[i]); 206 + if (ret < 0) 207 + return ret; 208 + } 209 + return ret; 210 + } 211 + 212 + static SIMPLE_DEV_PM_OPS(ds4424_pm_ops, ds4424_suspend, ds4424_resume); 213 + 214 + static const struct iio_info ds4424_info = { 215 + .read_raw = ds4424_read_raw, 216 + .write_raw = ds4424_write_raw, 217 + }; 218 + 219 + static int ds4424_probe(struct i2c_client *client, 220 + const struct i2c_device_id *id) 221 + { 222 + struct ds4424_data *data; 223 + struct iio_dev *indio_dev; 224 + int ret; 225 + 226 + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 227 + if (!indio_dev) { 228 + dev_err(&client->dev, "iio dev alloc failed.\n"); 229 + return -ENOMEM; 230 + } 231 + 232 + data = iio_priv(indio_dev); 233 + i2c_set_clientdata(client, indio_dev); 234 + data->client = client; 235 + indio_dev->name = id->name; 236 + indio_dev->dev.of_node = client->dev.of_node; 237 + indio_dev->dev.parent = &client->dev; 238 + 239 + if (!client->dev.of_node) { 240 + dev_err(&client->dev, 241 + "Not found DT.\n"); 242 + return -ENODEV; 243 + } 244 + 245 + data->vcc_reg = devm_regulator_get(&client->dev, "vcc"); 246 + if (IS_ERR(data->vcc_reg)) { 247 + dev_err(&client->dev, 248 + "Failed to get vcc-supply regulator. err: %ld\n", 249 + PTR_ERR(data->vcc_reg)); 250 + return PTR_ERR(data->vcc_reg); 251 + } 252 + 253 + mutex_init(&data->lock); 254 + ret = regulator_enable(data->vcc_reg); 255 + if (ret < 0) { 256 + dev_err(&client->dev, 257 + "Unable to enable the regulator.\n"); 258 + return ret; 259 + } 260 + 261 + usleep_range(1000, 1200); 262 + ret = ds4424_verify_chip(indio_dev); 263 + if (ret < 0) 264 + goto fail; 265 + 266 + switch (id->driver_data) { 267 + case ID_DS4422: 268 + indio_dev->num_channels = DS4422_MAX_DAC_CHANNELS; 269 + break; 270 + case ID_DS4424: 271 + indio_dev->num_channels = DS4424_MAX_DAC_CHANNELS; 272 + break; 273 + default: 274 + dev_err(&client->dev, 275 + "ds4424: Invalid chip id.\n"); 276 + ret = -ENXIO; 277 + goto fail; 278 + } 279 + 280 + indio_dev->channels = ds4424_channels; 281 + indio_dev->modes = INDIO_DIRECT_MODE; 282 + indio_dev->info = &ds4424_info; 283 + 284 + ret = iio_device_register(indio_dev); 285 + if (ret < 0) { 286 + dev_err(&client->dev, 287 + "iio_device_register failed. ret: %d\n", ret); 288 + goto fail; 289 + } 290 + 291 + return ret; 292 + 293 + fail: 294 + regulator_disable(data->vcc_reg); 295 + return ret; 296 + } 297 + 298 + static int ds4424_remove(struct i2c_client *client) 299 + { 300 + struct iio_dev *indio_dev = i2c_get_clientdata(client); 301 + struct ds4424_data *data = iio_priv(indio_dev); 302 + 303 + iio_device_unregister(indio_dev); 304 + regulator_disable(data->vcc_reg); 305 + 306 + return 0; 307 + } 308 + 309 + static const struct i2c_device_id ds4424_id[] = { 310 + { "ds4422", ID_DS4422 }, 311 + { "ds4424", ID_DS4424 }, 312 + { } 313 + }; 314 + 315 + MODULE_DEVICE_TABLE(i2c, ds4424_id); 316 + 317 + static const struct of_device_id ds4424_of_match[] = { 318 + { .compatible = "maxim,ds4422" }, 319 + { .compatible = "maxim,ds4424" }, 320 + { }, 321 + }; 322 + 323 + MODULE_DEVICE_TABLE(of, ds4424_of_match); 324 + 325 + static struct i2c_driver ds4424_driver = { 326 + .driver = { 327 + .name = "ds4424", 328 + .of_match_table = ds4424_of_match, 329 + .pm = &ds4424_pm_ops, 330 + }, 331 + .probe = ds4424_probe, 332 + .remove = ds4424_remove, 333 + .id_table = ds4424_id, 334 + }; 335 + module_i2c_driver(ds4424_driver); 336 + 337 + MODULE_DESCRIPTION("Maxim DS4424 DAC Driver"); 338 + MODULE_AUTHOR("Ismail H. Kose <ismail.kose@maximintegrated.com>"); 339 + MODULE_AUTHOR("Vishal Sood <vishal.sood@maximintegrated.com>"); 340 + MODULE_AUTHOR("David Jung <david.jung@maximintegrated.com>"); 341 + MODULE_LICENSE("GPL v2");