18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * mcp4725.c - Support for Microchip MCP4725/6 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on max517 by Roland Stigge <stigge@antcom.de> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * driver for the Microchip I2C 12-bit digital-to-analog converter (DAC) 108c2ecf20Sopenharmony_ci * (7-bit I2C slave address 0x60, the three LSBs can be configured in 118c2ecf20Sopenharmony_ci * hardware) 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci#include <linux/i2c.h> 168c2ecf20Sopenharmony_ci#include <linux/err.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 198c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 208c2ecf20Sopenharmony_ci#include <linux/property.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 238c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <linux/iio/dac/mcp4725.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define MCP4725_DRV_NAME "mcp4725" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define MCP472X_REF_VDD 0x00 308c2ecf20Sopenharmony_ci#define MCP472X_REF_VREF_UNBUFFERED 0x02 318c2ecf20Sopenharmony_ci#define MCP472X_REF_VREF_BUFFERED 0x03 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct mcp4725_data { 348c2ecf20Sopenharmony_ci struct i2c_client *client; 358c2ecf20Sopenharmony_ci int id; 368c2ecf20Sopenharmony_ci unsigned ref_mode; 378c2ecf20Sopenharmony_ci bool vref_buffered; 388c2ecf20Sopenharmony_ci u16 dac_value; 398c2ecf20Sopenharmony_ci bool powerdown; 408c2ecf20Sopenharmony_ci unsigned powerdown_mode; 418c2ecf20Sopenharmony_ci struct regulator *vdd_reg; 428c2ecf20Sopenharmony_ci struct regulator *vref_reg; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int __maybe_unused mcp4725_suspend(struct device *dev) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci struct mcp4725_data *data = iio_priv(i2c_get_clientdata( 488c2ecf20Sopenharmony_ci to_i2c_client(dev))); 498c2ecf20Sopenharmony_ci u8 outbuf[2]; 508c2ecf20Sopenharmony_ci int ret; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci outbuf[0] = (data->powerdown_mode + 1) << 4; 538c2ecf20Sopenharmony_ci outbuf[1] = 0; 548c2ecf20Sopenharmony_ci data->powerdown = true; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci ret = i2c_master_send(data->client, outbuf, 2); 578c2ecf20Sopenharmony_ci if (ret < 0) 588c2ecf20Sopenharmony_ci return ret; 598c2ecf20Sopenharmony_ci else if (ret != 2) 608c2ecf20Sopenharmony_ci return -EIO; 618c2ecf20Sopenharmony_ci return 0; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int __maybe_unused mcp4725_resume(struct device *dev) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct mcp4725_data *data = iio_priv(i2c_get_clientdata( 678c2ecf20Sopenharmony_ci to_i2c_client(dev))); 688c2ecf20Sopenharmony_ci u8 outbuf[2]; 698c2ecf20Sopenharmony_ci int ret; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci /* restore previous DAC value */ 728c2ecf20Sopenharmony_ci outbuf[0] = (data->dac_value >> 8) & 0xf; 738c2ecf20Sopenharmony_ci outbuf[1] = data->dac_value & 0xff; 748c2ecf20Sopenharmony_ci data->powerdown = false; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci ret = i2c_master_send(data->client, outbuf, 2); 778c2ecf20Sopenharmony_ci if (ret < 0) 788c2ecf20Sopenharmony_ci return ret; 798c2ecf20Sopenharmony_ci else if (ret != 2) 808c2ecf20Sopenharmony_ci return -EIO; 818c2ecf20Sopenharmony_ci return 0; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend, mcp4725_resume); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic ssize_t mcp4725_store_eeprom(struct device *dev, 868c2ecf20Sopenharmony_ci struct device_attribute *attr, const char *buf, size_t len) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 898c2ecf20Sopenharmony_ci struct mcp4725_data *data = iio_priv(indio_dev); 908c2ecf20Sopenharmony_ci int tries = 20; 918c2ecf20Sopenharmony_ci u8 inoutbuf[3]; 928c2ecf20Sopenharmony_ci bool state; 938c2ecf20Sopenharmony_ci int ret; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci ret = strtobool(buf, &state); 968c2ecf20Sopenharmony_ci if (ret < 0) 978c2ecf20Sopenharmony_ci return ret; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (!state) 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci inoutbuf[0] = 0x60; /* write EEPROM */ 1038c2ecf20Sopenharmony_ci inoutbuf[0] |= data->ref_mode << 3; 1048c2ecf20Sopenharmony_ci inoutbuf[0] |= data->powerdown ? ((data->powerdown_mode + 1) << 1) : 0; 1058c2ecf20Sopenharmony_ci inoutbuf[1] = data->dac_value >> 4; 1068c2ecf20Sopenharmony_ci inoutbuf[2] = (data->dac_value & 0xf) << 4; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ret = i2c_master_send(data->client, inoutbuf, 3); 1098c2ecf20Sopenharmony_ci if (ret < 0) 1108c2ecf20Sopenharmony_ci return ret; 1118c2ecf20Sopenharmony_ci else if (ret != 3) 1128c2ecf20Sopenharmony_ci return -EIO; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* wait for write complete, takes up to 50ms */ 1158c2ecf20Sopenharmony_ci while (tries--) { 1168c2ecf20Sopenharmony_ci msleep(20); 1178c2ecf20Sopenharmony_ci ret = i2c_master_recv(data->client, inoutbuf, 3); 1188c2ecf20Sopenharmony_ci if (ret < 0) 1198c2ecf20Sopenharmony_ci return ret; 1208c2ecf20Sopenharmony_ci else if (ret != 3) 1218c2ecf20Sopenharmony_ci return -EIO; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (inoutbuf[0] & 0x80) 1248c2ecf20Sopenharmony_ci break; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci if (tries < 0) { 1288c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 1298c2ecf20Sopenharmony_ci "mcp4725_store_eeprom() failed, incomplete\n"); 1308c2ecf20Sopenharmony_ci return -EIO; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return len; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic IIO_DEVICE_ATTR(store_eeprom, S_IWUSR, NULL, mcp4725_store_eeprom, 0); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic struct attribute *mcp4725_attributes[] = { 1398c2ecf20Sopenharmony_ci &iio_dev_attr_store_eeprom.dev_attr.attr, 1408c2ecf20Sopenharmony_ci NULL, 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic const struct attribute_group mcp4725_attribute_group = { 1448c2ecf20Sopenharmony_ci .attrs = mcp4725_attributes, 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_cistatic const char * const mcp4725_powerdown_modes[] = { 1488c2ecf20Sopenharmony_ci "1kohm_to_gnd", 1498c2ecf20Sopenharmony_ci "100kohm_to_gnd", 1508c2ecf20Sopenharmony_ci "500kohm_to_gnd" 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic const char * const mcp4726_powerdown_modes[] = { 1548c2ecf20Sopenharmony_ci "1kohm_to_gnd", 1558c2ecf20Sopenharmony_ci "125kohm_to_gnd", 1568c2ecf20Sopenharmony_ci "640kohm_to_gnd" 1578c2ecf20Sopenharmony_ci}; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic int mcp4725_get_powerdown_mode(struct iio_dev *indio_dev, 1608c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct mcp4725_data *data = iio_priv(indio_dev); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return data->powerdown_mode; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int mcp4725_set_powerdown_mode(struct iio_dev *indio_dev, 1688c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, unsigned mode) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct mcp4725_data *data = iio_priv(indio_dev); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci data->powerdown_mode = mode; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic ssize_t mcp4725_read_powerdown(struct iio_dev *indio_dev, 1788c2ecf20Sopenharmony_ci uintptr_t private, const struct iio_chan_spec *chan, char *buf) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct mcp4725_data *data = iio_priv(indio_dev); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", data->powerdown); 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev, 1868c2ecf20Sopenharmony_ci uintptr_t private, const struct iio_chan_spec *chan, 1878c2ecf20Sopenharmony_ci const char *buf, size_t len) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct mcp4725_data *data = iio_priv(indio_dev); 1908c2ecf20Sopenharmony_ci bool state; 1918c2ecf20Sopenharmony_ci int ret; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci ret = strtobool(buf, &state); 1948c2ecf20Sopenharmony_ci if (ret) 1958c2ecf20Sopenharmony_ci return ret; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci if (state) 1988c2ecf20Sopenharmony_ci ret = mcp4725_suspend(&data->client->dev); 1998c2ecf20Sopenharmony_ci else 2008c2ecf20Sopenharmony_ci ret = mcp4725_resume(&data->client->dev); 2018c2ecf20Sopenharmony_ci if (ret < 0) 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci return len; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cienum chip_id { 2088c2ecf20Sopenharmony_ci MCP4725, 2098c2ecf20Sopenharmony_ci MCP4726, 2108c2ecf20Sopenharmony_ci}; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic const struct iio_enum mcp472x_powerdown_mode_enum[] = { 2138c2ecf20Sopenharmony_ci [MCP4725] = { 2148c2ecf20Sopenharmony_ci .items = mcp4725_powerdown_modes, 2158c2ecf20Sopenharmony_ci .num_items = ARRAY_SIZE(mcp4725_powerdown_modes), 2168c2ecf20Sopenharmony_ci .get = mcp4725_get_powerdown_mode, 2178c2ecf20Sopenharmony_ci .set = mcp4725_set_powerdown_mode, 2188c2ecf20Sopenharmony_ci }, 2198c2ecf20Sopenharmony_ci [MCP4726] = { 2208c2ecf20Sopenharmony_ci .items = mcp4726_powerdown_modes, 2218c2ecf20Sopenharmony_ci .num_items = ARRAY_SIZE(mcp4726_powerdown_modes), 2228c2ecf20Sopenharmony_ci .get = mcp4725_get_powerdown_mode, 2238c2ecf20Sopenharmony_ci .set = mcp4725_set_powerdown_mode, 2248c2ecf20Sopenharmony_ci }, 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_cistatic const struct iio_chan_spec_ext_info mcp4725_ext_info[] = { 2288c2ecf20Sopenharmony_ci { 2298c2ecf20Sopenharmony_ci .name = "powerdown", 2308c2ecf20Sopenharmony_ci .read = mcp4725_read_powerdown, 2318c2ecf20Sopenharmony_ci .write = mcp4725_write_powerdown, 2328c2ecf20Sopenharmony_ci .shared = IIO_SEPARATE, 2338c2ecf20Sopenharmony_ci }, 2348c2ecf20Sopenharmony_ci IIO_ENUM("powerdown_mode", IIO_SEPARATE, 2358c2ecf20Sopenharmony_ci &mcp472x_powerdown_mode_enum[MCP4725]), 2368c2ecf20Sopenharmony_ci IIO_ENUM_AVAILABLE("powerdown_mode", 2378c2ecf20Sopenharmony_ci &mcp472x_powerdown_mode_enum[MCP4725]), 2388c2ecf20Sopenharmony_ci { }, 2398c2ecf20Sopenharmony_ci}; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic const struct iio_chan_spec_ext_info mcp4726_ext_info[] = { 2428c2ecf20Sopenharmony_ci { 2438c2ecf20Sopenharmony_ci .name = "powerdown", 2448c2ecf20Sopenharmony_ci .read = mcp4725_read_powerdown, 2458c2ecf20Sopenharmony_ci .write = mcp4725_write_powerdown, 2468c2ecf20Sopenharmony_ci .shared = IIO_SEPARATE, 2478c2ecf20Sopenharmony_ci }, 2488c2ecf20Sopenharmony_ci IIO_ENUM("powerdown_mode", IIO_SEPARATE, 2498c2ecf20Sopenharmony_ci &mcp472x_powerdown_mode_enum[MCP4726]), 2508c2ecf20Sopenharmony_ci IIO_ENUM_AVAILABLE("powerdown_mode", 2518c2ecf20Sopenharmony_ci &mcp472x_powerdown_mode_enum[MCP4726]), 2528c2ecf20Sopenharmony_ci { }, 2538c2ecf20Sopenharmony_ci}; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic const struct iio_chan_spec mcp472x_channel[] = { 2568c2ecf20Sopenharmony_ci [MCP4725] = { 2578c2ecf20Sopenharmony_ci .type = IIO_VOLTAGE, 2588c2ecf20Sopenharmony_ci .indexed = 1, 2598c2ecf20Sopenharmony_ci .output = 1, 2608c2ecf20Sopenharmony_ci .channel = 0, 2618c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 2628c2ecf20Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), 2638c2ecf20Sopenharmony_ci .ext_info = mcp4725_ext_info, 2648c2ecf20Sopenharmony_ci }, 2658c2ecf20Sopenharmony_ci [MCP4726] = { 2668c2ecf20Sopenharmony_ci .type = IIO_VOLTAGE, 2678c2ecf20Sopenharmony_ci .indexed = 1, 2688c2ecf20Sopenharmony_ci .output = 1, 2698c2ecf20Sopenharmony_ci .channel = 0, 2708c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 2718c2ecf20Sopenharmony_ci .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), 2728c2ecf20Sopenharmony_ci .ext_info = mcp4726_ext_info, 2738c2ecf20Sopenharmony_ci }, 2748c2ecf20Sopenharmony_ci}; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic int mcp4725_set_value(struct iio_dev *indio_dev, int val) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci struct mcp4725_data *data = iio_priv(indio_dev); 2798c2ecf20Sopenharmony_ci u8 outbuf[2]; 2808c2ecf20Sopenharmony_ci int ret; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (val >= (1 << 12) || val < 0) 2838c2ecf20Sopenharmony_ci return -EINVAL; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci outbuf[0] = (val >> 8) & 0xf; 2868c2ecf20Sopenharmony_ci outbuf[1] = val & 0xff; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ret = i2c_master_send(data->client, outbuf, 2); 2898c2ecf20Sopenharmony_ci if (ret < 0) 2908c2ecf20Sopenharmony_ci return ret; 2918c2ecf20Sopenharmony_ci else if (ret != 2) 2928c2ecf20Sopenharmony_ci return -EIO; 2938c2ecf20Sopenharmony_ci else 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int mcp4726_set_cfg(struct iio_dev *indio_dev) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct mcp4725_data *data = iio_priv(indio_dev); 3008c2ecf20Sopenharmony_ci u8 outbuf[3]; 3018c2ecf20Sopenharmony_ci int ret; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci outbuf[0] = 0x40; 3048c2ecf20Sopenharmony_ci outbuf[0] |= data->ref_mode << 3; 3058c2ecf20Sopenharmony_ci if (data->powerdown) 3068c2ecf20Sopenharmony_ci outbuf[0] |= data->powerdown << 1; 3078c2ecf20Sopenharmony_ci outbuf[1] = data->dac_value >> 4; 3088c2ecf20Sopenharmony_ci outbuf[2] = (data->dac_value & 0xf) << 4; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci ret = i2c_master_send(data->client, outbuf, 3); 3118c2ecf20Sopenharmony_ci if (ret < 0) 3128c2ecf20Sopenharmony_ci return ret; 3138c2ecf20Sopenharmony_ci else if (ret != 3) 3148c2ecf20Sopenharmony_ci return -EIO; 3158c2ecf20Sopenharmony_ci else 3168c2ecf20Sopenharmony_ci return 0; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_cistatic int mcp4725_read_raw(struct iio_dev *indio_dev, 3208c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 3218c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci struct mcp4725_data *data = iio_priv(indio_dev); 3248c2ecf20Sopenharmony_ci int ret; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci switch (mask) { 3278c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 3288c2ecf20Sopenharmony_ci *val = data->dac_value; 3298c2ecf20Sopenharmony_ci return IIO_VAL_INT; 3308c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 3318c2ecf20Sopenharmony_ci if (data->ref_mode == MCP472X_REF_VDD) 3328c2ecf20Sopenharmony_ci ret = regulator_get_voltage(data->vdd_reg); 3338c2ecf20Sopenharmony_ci else 3348c2ecf20Sopenharmony_ci ret = regulator_get_voltage(data->vref_reg); 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (ret < 0) 3378c2ecf20Sopenharmony_ci return ret; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci *val = ret / 1000; 3408c2ecf20Sopenharmony_ci *val2 = 12; 3418c2ecf20Sopenharmony_ci return IIO_VAL_FRACTIONAL_LOG2; 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci return -EINVAL; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic int mcp4725_write_raw(struct iio_dev *indio_dev, 3478c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 3488c2ecf20Sopenharmony_ci int val, int val2, long mask) 3498c2ecf20Sopenharmony_ci{ 3508c2ecf20Sopenharmony_ci struct mcp4725_data *data = iio_priv(indio_dev); 3518c2ecf20Sopenharmony_ci int ret; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci switch (mask) { 3548c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 3558c2ecf20Sopenharmony_ci ret = mcp4725_set_value(indio_dev, val); 3568c2ecf20Sopenharmony_ci data->dac_value = val; 3578c2ecf20Sopenharmony_ci break; 3588c2ecf20Sopenharmony_ci default: 3598c2ecf20Sopenharmony_ci ret = -EINVAL; 3608c2ecf20Sopenharmony_ci break; 3618c2ecf20Sopenharmony_ci } 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return ret; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic const struct iio_info mcp4725_info = { 3678c2ecf20Sopenharmony_ci .read_raw = mcp4725_read_raw, 3688c2ecf20Sopenharmony_ci .write_raw = mcp4725_write_raw, 3698c2ecf20Sopenharmony_ci .attrs = &mcp4725_attribute_group, 3708c2ecf20Sopenharmony_ci}; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_cistatic int mcp4725_probe_dt(struct device *dev, 3738c2ecf20Sopenharmony_ci struct mcp4725_platform_data *pdata) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci /* check if is the vref-supply defined */ 3768c2ecf20Sopenharmony_ci pdata->use_vref = device_property_read_bool(dev, "vref-supply"); 3778c2ecf20Sopenharmony_ci pdata->vref_buffered = 3788c2ecf20Sopenharmony_ci device_property_read_bool(dev, "microchip,vref-buffered"); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int mcp4725_probe(struct i2c_client *client, 3848c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct mcp4725_data *data; 3878c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 3888c2ecf20Sopenharmony_ci struct mcp4725_platform_data *pdata, pdata_dt; 3898c2ecf20Sopenharmony_ci u8 inbuf[4]; 3908c2ecf20Sopenharmony_ci u8 pd; 3918c2ecf20Sopenharmony_ci u8 ref; 3928c2ecf20Sopenharmony_ci int err; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 3958c2ecf20Sopenharmony_ci if (indio_dev == NULL) 3968c2ecf20Sopenharmony_ci return -ENOMEM; 3978c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 3988c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 3998c2ecf20Sopenharmony_ci data->client = client; 4008c2ecf20Sopenharmony_ci if (dev_fwnode(&client->dev)) 4018c2ecf20Sopenharmony_ci data->id = (enum chip_id)device_get_match_data(&client->dev); 4028c2ecf20Sopenharmony_ci else 4038c2ecf20Sopenharmony_ci data->id = id->driver_data; 4048c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&client->dev); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (!pdata) { 4078c2ecf20Sopenharmony_ci err = mcp4725_probe_dt(&client->dev, &pdata_dt); 4088c2ecf20Sopenharmony_ci if (err) { 4098c2ecf20Sopenharmony_ci dev_err(&client->dev, 4108c2ecf20Sopenharmony_ci "invalid platform or devicetree data"); 4118c2ecf20Sopenharmony_ci return err; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci pdata = &pdata_dt; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (data->id == MCP4725 && pdata->use_vref) { 4178c2ecf20Sopenharmony_ci dev_err(&client->dev, 4188c2ecf20Sopenharmony_ci "external reference is unavailable on MCP4725"); 4198c2ecf20Sopenharmony_ci return -EINVAL; 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (!pdata->use_vref && pdata->vref_buffered) { 4238c2ecf20Sopenharmony_ci dev_err(&client->dev, 4248c2ecf20Sopenharmony_ci "buffering is unavailable on the internal reference"); 4258c2ecf20Sopenharmony_ci return -EINVAL; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (!pdata->use_vref) 4298c2ecf20Sopenharmony_ci data->ref_mode = MCP472X_REF_VDD; 4308c2ecf20Sopenharmony_ci else 4318c2ecf20Sopenharmony_ci data->ref_mode = pdata->vref_buffered ? 4328c2ecf20Sopenharmony_ci MCP472X_REF_VREF_BUFFERED : 4338c2ecf20Sopenharmony_ci MCP472X_REF_VREF_UNBUFFERED; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); 4368c2ecf20Sopenharmony_ci if (IS_ERR(data->vdd_reg)) 4378c2ecf20Sopenharmony_ci return PTR_ERR(data->vdd_reg); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci err = regulator_enable(data->vdd_reg); 4408c2ecf20Sopenharmony_ci if (err) 4418c2ecf20Sopenharmony_ci return err; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (pdata->use_vref) { 4448c2ecf20Sopenharmony_ci data->vref_reg = devm_regulator_get(&client->dev, "vref"); 4458c2ecf20Sopenharmony_ci if (IS_ERR(data->vref_reg)) { 4468c2ecf20Sopenharmony_ci err = PTR_ERR(data->vref_reg); 4478c2ecf20Sopenharmony_ci goto err_disable_vdd_reg; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci err = regulator_enable(data->vref_reg); 4518c2ecf20Sopenharmony_ci if (err) 4528c2ecf20Sopenharmony_ci goto err_disable_vdd_reg; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci indio_dev->name = id->name; 4568c2ecf20Sopenharmony_ci indio_dev->info = &mcp4725_info; 4578c2ecf20Sopenharmony_ci indio_dev->channels = &mcp472x_channel[id->driver_data]; 4588c2ecf20Sopenharmony_ci indio_dev->num_channels = 1; 4598c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci /* read current DAC value and settings */ 4628c2ecf20Sopenharmony_ci err = i2c_master_recv(client, inbuf, data->id == MCP4725 ? 3 : 4); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci if (err < 0) { 4658c2ecf20Sopenharmony_ci dev_err(&client->dev, "failed to read DAC value"); 4668c2ecf20Sopenharmony_ci goto err_disable_vref_reg; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci pd = (inbuf[0] >> 1) & 0x3; 4698c2ecf20Sopenharmony_ci data->powerdown = pd > 0; 4708c2ecf20Sopenharmony_ci data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */ 4718c2ecf20Sopenharmony_ci data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4); 4728c2ecf20Sopenharmony_ci if (data->id == MCP4726) 4738c2ecf20Sopenharmony_ci ref = (inbuf[3] >> 3) & 0x3; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci if (data->id == MCP4726 && ref != data->ref_mode) { 4768c2ecf20Sopenharmony_ci dev_info(&client->dev, 4778c2ecf20Sopenharmony_ci "voltage reference mode differs (conf: %u, eeprom: %u), setting %u", 4788c2ecf20Sopenharmony_ci data->ref_mode, ref, data->ref_mode); 4798c2ecf20Sopenharmony_ci err = mcp4726_set_cfg(indio_dev); 4808c2ecf20Sopenharmony_ci if (err < 0) 4818c2ecf20Sopenharmony_ci goto err_disable_vref_reg; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci err = iio_device_register(indio_dev); 4858c2ecf20Sopenharmony_ci if (err) 4868c2ecf20Sopenharmony_ci goto err_disable_vref_reg; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci return 0; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cierr_disable_vref_reg: 4918c2ecf20Sopenharmony_ci if (data->vref_reg) 4928c2ecf20Sopenharmony_ci regulator_disable(data->vref_reg); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cierr_disable_vdd_reg: 4958c2ecf20Sopenharmony_ci regulator_disable(data->vdd_reg); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return err; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int mcp4725_remove(struct i2c_client *client) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(client); 5038c2ecf20Sopenharmony_ci struct mcp4725_data *data = iio_priv(indio_dev); 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (data->vref_reg) 5088c2ecf20Sopenharmony_ci regulator_disable(data->vref_reg); 5098c2ecf20Sopenharmony_ci regulator_disable(data->vdd_reg); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return 0; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_cistatic const struct i2c_device_id mcp4725_id[] = { 5158c2ecf20Sopenharmony_ci { "mcp4725", MCP4725 }, 5168c2ecf20Sopenharmony_ci { "mcp4726", MCP4726 }, 5178c2ecf20Sopenharmony_ci { } 5188c2ecf20Sopenharmony_ci}; 5198c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mcp4725_id); 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_cistatic const struct of_device_id mcp4725_of_match[] = { 5228c2ecf20Sopenharmony_ci { 5238c2ecf20Sopenharmony_ci .compatible = "microchip,mcp4725", 5248c2ecf20Sopenharmony_ci .data = (void *)MCP4725 5258c2ecf20Sopenharmony_ci }, 5268c2ecf20Sopenharmony_ci { 5278c2ecf20Sopenharmony_ci .compatible = "microchip,mcp4726", 5288c2ecf20Sopenharmony_ci .data = (void *)MCP4726 5298c2ecf20Sopenharmony_ci }, 5308c2ecf20Sopenharmony_ci { } 5318c2ecf20Sopenharmony_ci}; 5328c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mcp4725_of_match); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_cistatic struct i2c_driver mcp4725_driver = { 5358c2ecf20Sopenharmony_ci .driver = { 5368c2ecf20Sopenharmony_ci .name = MCP4725_DRV_NAME, 5378c2ecf20Sopenharmony_ci .of_match_table = mcp4725_of_match, 5388c2ecf20Sopenharmony_ci .pm = &mcp4725_pm_ops, 5398c2ecf20Sopenharmony_ci }, 5408c2ecf20Sopenharmony_ci .probe = mcp4725_probe, 5418c2ecf20Sopenharmony_ci .remove = mcp4725_remove, 5428c2ecf20Sopenharmony_ci .id_table = mcp4725_id, 5438c2ecf20Sopenharmony_ci}; 5448c2ecf20Sopenharmony_cimodule_i2c_driver(mcp4725_driver); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 5478c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MCP4725/6 12-bit DAC"); 5488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 549