18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Analog Devices AD5272 digital potentiometer driver 48c2ecf20Sopenharmony_ci * Copyright (C) 2018 Phil Reid <preid@electromag.com.au> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/AD5272_5274.pdf 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * DEVID #Wipers #Positions Resistor Opts (kOhm) i2c address 98c2ecf20Sopenharmony_ci * ad5272 1 1024 20, 50, 100 01011xx 108c2ecf20Sopenharmony_ci * ad5274 1 256 20, 100 01011xx 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 158c2ecf20Sopenharmony_ci#include <linux/i2c.h> 168c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define AD5272_RDAC_WR 1 218c2ecf20Sopenharmony_ci#define AD5272_RDAC_RD 2 228c2ecf20Sopenharmony_ci#define AD5272_RESET 4 238c2ecf20Sopenharmony_ci#define AD5272_CTL 7 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define AD5272_RDAC_WR_EN BIT(1) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistruct ad5272_cfg { 288c2ecf20Sopenharmony_ci int max_pos; 298c2ecf20Sopenharmony_ci int kohms; 308c2ecf20Sopenharmony_ci int shift; 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cienum ad5272_type { 348c2ecf20Sopenharmony_ci AD5272_020, 358c2ecf20Sopenharmony_ci AD5272_050, 368c2ecf20Sopenharmony_ci AD5272_100, 378c2ecf20Sopenharmony_ci AD5274_020, 388c2ecf20Sopenharmony_ci AD5274_100, 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic const struct ad5272_cfg ad5272_cfg[] = { 428c2ecf20Sopenharmony_ci [AD5272_020] = { .max_pos = 1024, .kohms = 20 }, 438c2ecf20Sopenharmony_ci [AD5272_050] = { .max_pos = 1024, .kohms = 50 }, 448c2ecf20Sopenharmony_ci [AD5272_100] = { .max_pos = 1024, .kohms = 100 }, 458c2ecf20Sopenharmony_ci [AD5274_020] = { .max_pos = 256, .kohms = 20, .shift = 2 }, 468c2ecf20Sopenharmony_ci [AD5274_100] = { .max_pos = 256, .kohms = 100, .shift = 2 }, 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistruct ad5272_data { 508c2ecf20Sopenharmony_ci struct i2c_client *client; 518c2ecf20Sopenharmony_ci struct mutex lock; 528c2ecf20Sopenharmony_ci const struct ad5272_cfg *cfg; 538c2ecf20Sopenharmony_ci u8 buf[2] ____cacheline_aligned; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic const struct iio_chan_spec ad5272_channel = { 578c2ecf20Sopenharmony_ci .type = IIO_RESISTANCE, 588c2ecf20Sopenharmony_ci .output = 1, 598c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 608c2ecf20Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), 618c2ecf20Sopenharmony_ci}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int ad5272_write(struct ad5272_data *data, int reg, int val) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci int ret; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci data->buf[0] = (reg << 2) | ((val >> 8) & 0x3); 688c2ecf20Sopenharmony_ci data->buf[1] = (u8)val; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 718c2ecf20Sopenharmony_ci ret = i2c_master_send(data->client, data->buf, sizeof(data->buf)); 728c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 738c2ecf20Sopenharmony_ci return ret < 0 ? ret : 0; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int ad5272_read(struct ad5272_data *data, int reg, int *val) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci int ret; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci data->buf[0] = reg << 2; 818c2ecf20Sopenharmony_ci data->buf[1] = 0; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci mutex_lock(&data->lock); 848c2ecf20Sopenharmony_ci ret = i2c_master_send(data->client, data->buf, sizeof(data->buf)); 858c2ecf20Sopenharmony_ci if (ret < 0) 868c2ecf20Sopenharmony_ci goto error; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci ret = i2c_master_recv(data->client, data->buf, sizeof(data->buf)); 898c2ecf20Sopenharmony_ci if (ret < 0) 908c2ecf20Sopenharmony_ci goto error; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci *val = ((data->buf[0] & 0x3) << 8) | data->buf[1]; 938c2ecf20Sopenharmony_ci ret = 0; 948c2ecf20Sopenharmony_cierror: 958c2ecf20Sopenharmony_ci mutex_unlock(&data->lock); 968c2ecf20Sopenharmony_ci return ret; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int ad5272_read_raw(struct iio_dev *indio_dev, 1008c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1018c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci struct ad5272_data *data = iio_priv(indio_dev); 1048c2ecf20Sopenharmony_ci int ret; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci switch (mask) { 1078c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: { 1088c2ecf20Sopenharmony_ci ret = ad5272_read(data, AD5272_RDAC_RD, val); 1098c2ecf20Sopenharmony_ci *val = *val >> data->cfg->shift; 1108c2ecf20Sopenharmony_ci return ret ? ret : IIO_VAL_INT; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 1138c2ecf20Sopenharmony_ci *val = 1000 * data->cfg->kohms; 1148c2ecf20Sopenharmony_ci *val2 = data->cfg->max_pos; 1158c2ecf20Sopenharmony_ci return IIO_VAL_FRACTIONAL; 1168c2ecf20Sopenharmony_ci } 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci return -EINVAL; 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic int ad5272_write_raw(struct iio_dev *indio_dev, 1228c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 1238c2ecf20Sopenharmony_ci int val, int val2, long mask) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct ad5272_data *data = iio_priv(indio_dev); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (mask != IIO_CHAN_INFO_RAW) 1288c2ecf20Sopenharmony_ci return -EINVAL; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (val >= data->cfg->max_pos || val < 0 || val2) 1318c2ecf20Sopenharmony_ci return -EINVAL; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return ad5272_write(data, AD5272_RDAC_WR, val << data->cfg->shift); 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic const struct iio_info ad5272_info = { 1378c2ecf20Sopenharmony_ci .read_raw = ad5272_read_raw, 1388c2ecf20Sopenharmony_ci .write_raw = ad5272_write_raw, 1398c2ecf20Sopenharmony_ci}; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int ad5272_reset(struct ad5272_data *data) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct gpio_desc *reset_gpio; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci reset_gpio = devm_gpiod_get_optional(&data->client->dev, "reset", 1468c2ecf20Sopenharmony_ci GPIOD_OUT_LOW); 1478c2ecf20Sopenharmony_ci if (IS_ERR(reset_gpio)) 1488c2ecf20Sopenharmony_ci return PTR_ERR(reset_gpio); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (reset_gpio) { 1518c2ecf20Sopenharmony_ci udelay(1); 1528c2ecf20Sopenharmony_ci gpiod_set_value(reset_gpio, 1); 1538c2ecf20Sopenharmony_ci } else { 1548c2ecf20Sopenharmony_ci ad5272_write(data, AD5272_RESET, 0); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci usleep_range(1000, 2000); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci return 0; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic int ad5272_probe(struct i2c_client *client, 1628c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 1658c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 1668c2ecf20Sopenharmony_ci struct ad5272_data *data; 1678c2ecf20Sopenharmony_ci int ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); 1708c2ecf20Sopenharmony_ci if (!indio_dev) 1718c2ecf20Sopenharmony_ci return -ENOMEM; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 1768c2ecf20Sopenharmony_ci data->client = client; 1778c2ecf20Sopenharmony_ci mutex_init(&data->lock); 1788c2ecf20Sopenharmony_ci data->cfg = &ad5272_cfg[id->driver_data]; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci ret = ad5272_reset(data); 1818c2ecf20Sopenharmony_ci if (ret) 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci ret = ad5272_write(data, AD5272_CTL, AD5272_RDAC_WR_EN); 1858c2ecf20Sopenharmony_ci if (ret < 0) 1868c2ecf20Sopenharmony_ci return -ENODEV; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci indio_dev->info = &ad5272_info; 1898c2ecf20Sopenharmony_ci indio_dev->channels = &ad5272_channel; 1908c2ecf20Sopenharmony_ci indio_dev->num_channels = 1; 1918c2ecf20Sopenharmony_ci indio_dev->name = client->name; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return devm_iio_device_register(dev, indio_dev); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic const struct of_device_id ad5272_dt_ids[] = { 1978c2ecf20Sopenharmony_ci { .compatible = "adi,ad5272-020", .data = (void *)AD5272_020 }, 1988c2ecf20Sopenharmony_ci { .compatible = "adi,ad5272-050", .data = (void *)AD5272_050 }, 1998c2ecf20Sopenharmony_ci { .compatible = "adi,ad5272-100", .data = (void *)AD5272_100 }, 2008c2ecf20Sopenharmony_ci { .compatible = "adi,ad5274-020", .data = (void *)AD5274_020 }, 2018c2ecf20Sopenharmony_ci { .compatible = "adi,ad5274-100", .data = (void *)AD5274_100 }, 2028c2ecf20Sopenharmony_ci {} 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ad5272_dt_ids); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic const struct i2c_device_id ad5272_id[] = { 2078c2ecf20Sopenharmony_ci { "ad5272-020", AD5272_020 }, 2088c2ecf20Sopenharmony_ci { "ad5272-050", AD5272_050 }, 2098c2ecf20Sopenharmony_ci { "ad5272-100", AD5272_100 }, 2108c2ecf20Sopenharmony_ci { "ad5274-020", AD5274_020 }, 2118c2ecf20Sopenharmony_ci { "ad5274-100", AD5274_100 }, 2128c2ecf20Sopenharmony_ci {} 2138c2ecf20Sopenharmony_ci}; 2148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ad5272_id); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic struct i2c_driver ad5272_driver = { 2178c2ecf20Sopenharmony_ci .driver = { 2188c2ecf20Sopenharmony_ci .name = "ad5272", 2198c2ecf20Sopenharmony_ci .of_match_table = ad5272_dt_ids, 2208c2ecf20Sopenharmony_ci }, 2218c2ecf20Sopenharmony_ci .probe = ad5272_probe, 2228c2ecf20Sopenharmony_ci .id_table = ad5272_id, 2238c2ecf20Sopenharmony_ci}; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cimodule_i2c_driver(ad5272_driver); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ciMODULE_AUTHOR("Phil Reid <preid@eletromag.com.au>"); 2288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("AD5272 digital potentiometer"); 2298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 230