162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Support for Microchip MCP4728 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2023 Andrea Collamati <andrea.collamati@gmail.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on mcp4725 by Peter Meerwald <pmeerw@pmeerw.net> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Driver for the Microchip I2C 12-bit digital-to-analog quad channels 1062306a36Sopenharmony_ci * converter (DAC). 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * (7-bit I2C slave address 0x60, the three LSBs can be configured in 1362306a36Sopenharmony_ci * hardware) 1462306a36Sopenharmony_ci */ 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/bitfield.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci#include <linux/err.h> 1962306a36Sopenharmony_ci#include <linux/i2c.h> 2062306a36Sopenharmony_ci#include <linux/iio/iio.h> 2162306a36Sopenharmony_ci#include <linux/iio/sysfs.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 2462306a36Sopenharmony_ci#include <linux/property.h> 2562306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define MCP4728_RESOLUTION 12 2862306a36Sopenharmony_ci#define MCP4728_N_CHANNELS 4 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define MCP4728_CMD_MASK GENMASK(7, 3) 3162306a36Sopenharmony_ci#define MCP4728_CHSEL_MASK GENMASK(2, 1) 3262306a36Sopenharmony_ci#define MCP4728_UDAC_MASK BIT(0) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define MCP4728_VREF_MASK BIT(7) 3562306a36Sopenharmony_ci#define MCP4728_PDMODE_MASK GENMASK(6, 5) 3662306a36Sopenharmony_ci#define MCP4728_GAIN_MASK BIT(4) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define MCP4728_DAC_H_MASK GENMASK(3, 0) 3962306a36Sopenharmony_ci#define MCP4728_DAC_L_MASK GENMASK(7, 0) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define MCP4728_RDY_MASK BIT(7) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define MCP4728_MW_CMD 0x08 /* Multiwrite Command */ 4462306a36Sopenharmony_ci#define MCP4728_SW_CMD 0x0A /* Sequential Write Command with EEPROM */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define MCP4728_READ_RESPONSE_LEN (MCP4728_N_CHANNELS * 3 * 2) 4762306a36Sopenharmony_ci#define MCP4728_WRITE_EEPROM_LEN (1 + MCP4728_N_CHANNELS * 2) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cienum vref_mode { 5062306a36Sopenharmony_ci MCP4728_VREF_EXTERNAL_VDD = 0, 5162306a36Sopenharmony_ci MCP4728_VREF_INTERNAL_2048mV = 1, 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cienum gain_mode { 5562306a36Sopenharmony_ci MCP4728_GAIN_X1 = 0, 5662306a36Sopenharmony_ci MCP4728_GAIN_X2 = 1, 5762306a36Sopenharmony_ci}; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cienum iio_powerdown_mode { 6062306a36Sopenharmony_ci MCP4728_IIO_1K, 6162306a36Sopenharmony_ci MCP4728_IIO_100K, 6262306a36Sopenharmony_ci MCP4728_IIO_500K, 6362306a36Sopenharmony_ci}; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistruct mcp4728_channel_data { 6662306a36Sopenharmony_ci enum vref_mode ref_mode; 6762306a36Sopenharmony_ci enum iio_powerdown_mode pd_mode; 6862306a36Sopenharmony_ci enum gain_mode g_mode; 6962306a36Sopenharmony_ci u16 dac_value; 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* MCP4728 Full Scale Ranges 7362306a36Sopenharmony_ci * the device available ranges are 7462306a36Sopenharmony_ci * - VREF = VDD FSR = from 0.0V to VDD 7562306a36Sopenharmony_ci * - VREF = Internal Gain = 1 FSR = from 0.0V to VREF 7662306a36Sopenharmony_ci * - VREF = Internal Gain = 2 FSR = from 0.0V to 2*VREF 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_cienum mcp4728_scale { 7962306a36Sopenharmony_ci MCP4728_SCALE_VDD, 8062306a36Sopenharmony_ci MCP4728_SCALE_VINT_NO_GAIN, 8162306a36Sopenharmony_ci MCP4728_SCALE_VINT_GAIN_X2, 8262306a36Sopenharmony_ci MCP4728_N_SCALES 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistruct mcp4728_data { 8662306a36Sopenharmony_ci struct i2c_client *client; 8762306a36Sopenharmony_ci struct regulator *vdd_reg; 8862306a36Sopenharmony_ci bool powerdown; 8962306a36Sopenharmony_ci int scales_avail[MCP4728_N_SCALES * 2]; 9062306a36Sopenharmony_ci struct mcp4728_channel_data chdata[MCP4728_N_CHANNELS]; 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define MCP4728_CHAN(chan) { \ 9462306a36Sopenharmony_ci .type = IIO_VOLTAGE, \ 9562306a36Sopenharmony_ci .output = 1, \ 9662306a36Sopenharmony_ci .indexed = 1, \ 9762306a36Sopenharmony_ci .channel = chan, \ 9862306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ 9962306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), \ 10062306a36Sopenharmony_ci .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \ 10162306a36Sopenharmony_ci .ext_info = mcp4728_ext_info, \ 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int mcp4728_suspend(struct device *dev); 10562306a36Sopenharmony_cistatic int mcp4728_resume(struct device *dev); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic ssize_t mcp4728_store_eeprom(struct device *dev, 10862306a36Sopenharmony_ci struct device_attribute *attr, 10962306a36Sopenharmony_ci const char *buf, size_t len) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 11262306a36Sopenharmony_ci struct mcp4728_data *data = iio_priv(indio_dev); 11362306a36Sopenharmony_ci u8 outbuf[MCP4728_WRITE_EEPROM_LEN]; 11462306a36Sopenharmony_ci int tries = 20; 11562306a36Sopenharmony_ci u8 inbuf[3]; 11662306a36Sopenharmony_ci bool state; 11762306a36Sopenharmony_ci int ret; 11862306a36Sopenharmony_ci unsigned int i; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci ret = kstrtobool(buf, &state); 12162306a36Sopenharmony_ci if (ret < 0) 12262306a36Sopenharmony_ci return ret; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci if (!state) 12562306a36Sopenharmony_ci return 0; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci outbuf[0] = FIELD_PREP(MCP4728_CMD_MASK, MCP4728_SW_CMD); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci for (i = 0; i < MCP4728_N_CHANNELS; i++) { 13062306a36Sopenharmony_ci struct mcp4728_channel_data *ch = &data->chdata[i]; 13162306a36Sopenharmony_ci int offset = 1 + i * 2; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci outbuf[offset] = FIELD_PREP(MCP4728_VREF_MASK, ch->ref_mode); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (data->powerdown) { 13662306a36Sopenharmony_ci u8 mcp4728_pd_mode = ch->pd_mode + 1; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci outbuf[offset] |= FIELD_PREP(MCP4728_PDMODE_MASK, 13962306a36Sopenharmony_ci mcp4728_pd_mode); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci outbuf[offset] |= FIELD_PREP(MCP4728_GAIN_MASK, ch->g_mode); 14362306a36Sopenharmony_ci outbuf[offset] |= 14462306a36Sopenharmony_ci FIELD_PREP(MCP4728_DAC_H_MASK, ch->dac_value >> 8); 14562306a36Sopenharmony_ci outbuf[offset + 1] = 14662306a36Sopenharmony_ci FIELD_PREP(MCP4728_DAC_L_MASK, ch->dac_value); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci ret = i2c_master_send(data->client, outbuf, MCP4728_WRITE_EEPROM_LEN); 15062306a36Sopenharmony_ci if (ret < 0) 15162306a36Sopenharmony_ci return ret; 15262306a36Sopenharmony_ci else if (ret != MCP4728_WRITE_EEPROM_LEN) 15362306a36Sopenharmony_ci return -EIO; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* wait RDY signal for write complete, takes up to 50ms */ 15662306a36Sopenharmony_ci while (tries--) { 15762306a36Sopenharmony_ci msleep(20); 15862306a36Sopenharmony_ci ret = i2c_master_recv(data->client, inbuf, 3); 15962306a36Sopenharmony_ci if (ret < 0) 16062306a36Sopenharmony_ci return ret; 16162306a36Sopenharmony_ci else if (ret != 3) 16262306a36Sopenharmony_ci return -EIO; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (FIELD_GET(MCP4728_RDY_MASK, inbuf[0])) 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (tries < 0) { 16962306a36Sopenharmony_ci dev_err(&data->client->dev, "%s failed, incomplete\n", 17062306a36Sopenharmony_ci __func__); 17162306a36Sopenharmony_ci return -EIO; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci return len; 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic IIO_DEVICE_ATTR(store_eeprom, 0200, NULL, mcp4728_store_eeprom, 0); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic struct attribute *mcp4728_attributes[] = { 17962306a36Sopenharmony_ci &iio_dev_attr_store_eeprom.dev_attr.attr, 18062306a36Sopenharmony_ci NULL, 18162306a36Sopenharmony_ci}; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic const struct attribute_group mcp4728_attribute_group = { 18462306a36Sopenharmony_ci .attrs = mcp4728_attributes, 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic int mcp4728_program_channel_cfg(int channel, struct iio_dev *indio_dev) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct mcp4728_data *data = iio_priv(indio_dev); 19062306a36Sopenharmony_ci struct mcp4728_channel_data *ch = &data->chdata[channel]; 19162306a36Sopenharmony_ci u8 outbuf[3]; 19262306a36Sopenharmony_ci int ret; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci outbuf[0] = FIELD_PREP(MCP4728_CMD_MASK, MCP4728_MW_CMD); 19562306a36Sopenharmony_ci outbuf[0] |= FIELD_PREP(MCP4728_CHSEL_MASK, channel); 19662306a36Sopenharmony_ci outbuf[0] |= FIELD_PREP(MCP4728_UDAC_MASK, 0); 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci outbuf[1] = FIELD_PREP(MCP4728_VREF_MASK, ch->ref_mode); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci if (data->powerdown) 20162306a36Sopenharmony_ci outbuf[1] |= FIELD_PREP(MCP4728_PDMODE_MASK, ch->pd_mode + 1); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci outbuf[1] |= FIELD_PREP(MCP4728_GAIN_MASK, ch->g_mode); 20462306a36Sopenharmony_ci outbuf[1] |= FIELD_PREP(MCP4728_DAC_H_MASK, ch->dac_value >> 8); 20562306a36Sopenharmony_ci outbuf[2] = FIELD_PREP(MCP4728_DAC_L_MASK, ch->dac_value); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci ret = i2c_master_send(data->client, outbuf, 3); 20862306a36Sopenharmony_ci if (ret < 0) 20962306a36Sopenharmony_ci return ret; 21062306a36Sopenharmony_ci else if (ret != 3) 21162306a36Sopenharmony_ci return -EIO; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return 0; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic const char *const mcp4728_powerdown_modes[] = { "1kohm_to_gnd", 21762306a36Sopenharmony_ci "100kohm_to_gnd", 21862306a36Sopenharmony_ci "500kohm_to_gnd" }; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic int mcp4728_get_powerdown_mode(struct iio_dev *indio_dev, 22162306a36Sopenharmony_ci const struct iio_chan_spec *chan) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct mcp4728_data *data = iio_priv(indio_dev); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci return data->chdata[chan->channel].pd_mode; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic int mcp4728_set_powerdown_mode(struct iio_dev *indio_dev, 22962306a36Sopenharmony_ci const struct iio_chan_spec *chan, 23062306a36Sopenharmony_ci unsigned int mode) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct mcp4728_data *data = iio_priv(indio_dev); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci data->chdata[chan->channel].pd_mode = mode; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic ssize_t mcp4728_read_powerdown(struct iio_dev *indio_dev, 24062306a36Sopenharmony_ci uintptr_t private, 24162306a36Sopenharmony_ci const struct iio_chan_spec *chan, 24262306a36Sopenharmony_ci char *buf) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct mcp4728_data *data = iio_priv(indio_dev); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci return sysfs_emit(buf, "%d\n", data->powerdown); 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic ssize_t mcp4728_write_powerdown(struct iio_dev *indio_dev, 25062306a36Sopenharmony_ci uintptr_t private, 25162306a36Sopenharmony_ci const struct iio_chan_spec *chan, 25262306a36Sopenharmony_ci const char *buf, size_t len) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct mcp4728_data *data = iio_priv(indio_dev); 25562306a36Sopenharmony_ci bool state; 25662306a36Sopenharmony_ci int ret; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci ret = kstrtobool(buf, &state); 25962306a36Sopenharmony_ci if (ret) 26062306a36Sopenharmony_ci return ret; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (state) 26362306a36Sopenharmony_ci ret = mcp4728_suspend(&data->client->dev); 26462306a36Sopenharmony_ci else 26562306a36Sopenharmony_ci ret = mcp4728_resume(&data->client->dev); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (ret < 0) 26862306a36Sopenharmony_ci return ret; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return len; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic const struct iio_enum mcp4728_powerdown_mode_enum = { 27462306a36Sopenharmony_ci .items = mcp4728_powerdown_modes, 27562306a36Sopenharmony_ci .num_items = ARRAY_SIZE(mcp4728_powerdown_modes), 27662306a36Sopenharmony_ci .get = mcp4728_get_powerdown_mode, 27762306a36Sopenharmony_ci .set = mcp4728_set_powerdown_mode, 27862306a36Sopenharmony_ci}; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic const struct iio_chan_spec_ext_info mcp4728_ext_info[] = { 28162306a36Sopenharmony_ci { 28262306a36Sopenharmony_ci .name = "powerdown", 28362306a36Sopenharmony_ci .read = mcp4728_read_powerdown, 28462306a36Sopenharmony_ci .write = mcp4728_write_powerdown, 28562306a36Sopenharmony_ci .shared = IIO_SEPARATE, 28662306a36Sopenharmony_ci }, 28762306a36Sopenharmony_ci IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4728_powerdown_mode_enum), 28862306a36Sopenharmony_ci IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, 28962306a36Sopenharmony_ci &mcp4728_powerdown_mode_enum), 29062306a36Sopenharmony_ci {}, 29162306a36Sopenharmony_ci}; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic const struct iio_chan_spec mcp4728_channels[MCP4728_N_CHANNELS] = { 29462306a36Sopenharmony_ci MCP4728_CHAN(0), 29562306a36Sopenharmony_ci MCP4728_CHAN(1), 29662306a36Sopenharmony_ci MCP4728_CHAN(2), 29762306a36Sopenharmony_ci MCP4728_CHAN(3), 29862306a36Sopenharmony_ci}; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic void mcp4728_get_scale_avail(enum mcp4728_scale scale, 30162306a36Sopenharmony_ci struct mcp4728_data *data, int *val, 30262306a36Sopenharmony_ci int *val2) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci *val = data->scales_avail[scale * 2]; 30562306a36Sopenharmony_ci *val2 = data->scales_avail[scale * 2 + 1]; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic void mcp4728_get_scale(int channel, struct mcp4728_data *data, int *val, 30962306a36Sopenharmony_ci int *val2) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci int ref_mode = data->chdata[channel].ref_mode; 31262306a36Sopenharmony_ci int g_mode = data->chdata[channel].g_mode; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (ref_mode == MCP4728_VREF_EXTERNAL_VDD) { 31562306a36Sopenharmony_ci mcp4728_get_scale_avail(MCP4728_SCALE_VDD, data, val, val2); 31662306a36Sopenharmony_ci } else { 31762306a36Sopenharmony_ci if (g_mode == MCP4728_GAIN_X1) { 31862306a36Sopenharmony_ci mcp4728_get_scale_avail(MCP4728_SCALE_VINT_NO_GAIN, 31962306a36Sopenharmony_ci data, val, val2); 32062306a36Sopenharmony_ci } else { 32162306a36Sopenharmony_ci mcp4728_get_scale_avail(MCP4728_SCALE_VINT_GAIN_X2, 32262306a36Sopenharmony_ci data, val, val2); 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int mcp4728_find_matching_scale(struct mcp4728_data *data, int val, 32862306a36Sopenharmony_ci int val2) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci for (int i = 0; i < MCP4728_N_SCALES; i++) { 33162306a36Sopenharmony_ci if (data->scales_avail[i * 2] == val && 33262306a36Sopenharmony_ci data->scales_avail[i * 2 + 1] == val2) 33362306a36Sopenharmony_ci return i; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci return -EINVAL; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_cistatic int mcp4728_set_scale(int channel, struct mcp4728_data *data, int val, 33962306a36Sopenharmony_ci int val2) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci int scale = mcp4728_find_matching_scale(data, val, val2); 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (scale < 0) 34462306a36Sopenharmony_ci return scale; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci switch (scale) { 34762306a36Sopenharmony_ci case MCP4728_SCALE_VDD: 34862306a36Sopenharmony_ci data->chdata[channel].ref_mode = MCP4728_VREF_EXTERNAL_VDD; 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci case MCP4728_SCALE_VINT_NO_GAIN: 35162306a36Sopenharmony_ci data->chdata[channel].ref_mode = MCP4728_VREF_INTERNAL_2048mV; 35262306a36Sopenharmony_ci data->chdata[channel].g_mode = MCP4728_GAIN_X1; 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci case MCP4728_SCALE_VINT_GAIN_X2: 35562306a36Sopenharmony_ci data->chdata[channel].ref_mode = MCP4728_VREF_INTERNAL_2048mV; 35662306a36Sopenharmony_ci data->chdata[channel].g_mode = MCP4728_GAIN_X2; 35762306a36Sopenharmony_ci return 0; 35862306a36Sopenharmony_ci default: 35962306a36Sopenharmony_ci return -EINVAL; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic int mcp4728_read_raw(struct iio_dev *indio_dev, 36462306a36Sopenharmony_ci struct iio_chan_spec const *chan, int *val, 36562306a36Sopenharmony_ci int *val2, long mask) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct mcp4728_data *data = iio_priv(indio_dev); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci switch (mask) { 37062306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 37162306a36Sopenharmony_ci *val = data->chdata[chan->channel].dac_value; 37262306a36Sopenharmony_ci return IIO_VAL_INT; 37362306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 37462306a36Sopenharmony_ci mcp4728_get_scale(chan->channel, data, val, val2); 37562306a36Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci return -EINVAL; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic int mcp4728_write_raw(struct iio_dev *indio_dev, 38162306a36Sopenharmony_ci struct iio_chan_spec const *chan, int val, 38262306a36Sopenharmony_ci int val2, long mask) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct mcp4728_data *data = iio_priv(indio_dev); 38562306a36Sopenharmony_ci int ret; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci switch (mask) { 38862306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 38962306a36Sopenharmony_ci if (val < 0 || val > GENMASK(MCP4728_RESOLUTION - 1, 0)) 39062306a36Sopenharmony_ci return -EINVAL; 39162306a36Sopenharmony_ci data->chdata[chan->channel].dac_value = val; 39262306a36Sopenharmony_ci return mcp4728_program_channel_cfg(chan->channel, indio_dev); 39362306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 39462306a36Sopenharmony_ci ret = mcp4728_set_scale(chan->channel, data, val, val2); 39562306a36Sopenharmony_ci if (ret) 39662306a36Sopenharmony_ci return ret; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci return mcp4728_program_channel_cfg(chan->channel, indio_dev); 39962306a36Sopenharmony_ci default: 40062306a36Sopenharmony_ci return -EINVAL; 40162306a36Sopenharmony_ci } 40262306a36Sopenharmony_ci} 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_cistatic void mcp4728_init_scale_avail(enum mcp4728_scale scale, int vref_mv, 40562306a36Sopenharmony_ci struct mcp4728_data *data) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci s64 tmp; 40862306a36Sopenharmony_ci int value_micro; 40962306a36Sopenharmony_ci int value_int; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci tmp = (s64)vref_mv * 1000000LL >> MCP4728_RESOLUTION; 41262306a36Sopenharmony_ci value_int = div_s64_rem(tmp, 1000000LL, &value_micro); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci data->scales_avail[scale * 2] = value_int; 41562306a36Sopenharmony_ci data->scales_avail[scale * 2 + 1] = value_micro; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic int mcp4728_init_scales_avail(struct mcp4728_data *data) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci int ret; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci ret = regulator_get_voltage(data->vdd_reg); 42362306a36Sopenharmony_ci if (ret < 0) 42462306a36Sopenharmony_ci return ret; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci mcp4728_init_scale_avail(MCP4728_SCALE_VDD, ret / 1000, data); 42762306a36Sopenharmony_ci mcp4728_init_scale_avail(MCP4728_SCALE_VINT_NO_GAIN, 2048, data); 42862306a36Sopenharmony_ci mcp4728_init_scale_avail(MCP4728_SCALE_VINT_GAIN_X2, 4096, data); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic int mcp4728_read_avail(struct iio_dev *indio_dev, 43462306a36Sopenharmony_ci struct iio_chan_spec const *chan, 43562306a36Sopenharmony_ci const int **vals, int *type, int *length, 43662306a36Sopenharmony_ci long info) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct mcp4728_data *data = iio_priv(indio_dev); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci switch (info) { 44162306a36Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 44262306a36Sopenharmony_ci *type = IIO_VAL_INT_PLUS_MICRO; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci switch (chan->type) { 44562306a36Sopenharmony_ci case IIO_VOLTAGE: 44662306a36Sopenharmony_ci *vals = data->scales_avail; 44762306a36Sopenharmony_ci *length = MCP4728_N_SCALES * 2; 44862306a36Sopenharmony_ci return IIO_AVAIL_LIST; 44962306a36Sopenharmony_ci default: 45062306a36Sopenharmony_ci return -EINVAL; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci default: 45362306a36Sopenharmony_ci return -EINVAL; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic const struct iio_info mcp4728_info = { 45862306a36Sopenharmony_ci .read_raw = mcp4728_read_raw, 45962306a36Sopenharmony_ci .write_raw = mcp4728_write_raw, 46062306a36Sopenharmony_ci .read_avail = &mcp4728_read_avail, 46162306a36Sopenharmony_ci .attrs = &mcp4728_attribute_group, 46262306a36Sopenharmony_ci}; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic int mcp4728_suspend(struct device *dev) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 46762306a36Sopenharmony_ci struct mcp4728_data *data = iio_priv(indio_dev); 46862306a36Sopenharmony_ci unsigned int i; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci data->powerdown = true; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci for (i = 0; i < MCP4728_N_CHANNELS; i++) { 47362306a36Sopenharmony_ci int err = mcp4728_program_channel_cfg(i, indio_dev); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (err) 47662306a36Sopenharmony_ci return err; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci return 0; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic int mcp4728_resume(struct device *dev) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 48462306a36Sopenharmony_ci struct mcp4728_data *data = iio_priv(indio_dev); 48562306a36Sopenharmony_ci int err = 0; 48662306a36Sopenharmony_ci unsigned int i; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci data->powerdown = false; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci for (i = 0; i < MCP4728_N_CHANNELS; i++) { 49162306a36Sopenharmony_ci int ret = mcp4728_program_channel_cfg(i, indio_dev); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (ret) 49462306a36Sopenharmony_ci err = ret; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci return err; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(mcp4728_pm_ops, mcp4728_suspend, 50062306a36Sopenharmony_ci mcp4728_resume); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_cistatic int mcp4728_init_channels_data(struct mcp4728_data *data) 50362306a36Sopenharmony_ci{ 50462306a36Sopenharmony_ci u8 inbuf[MCP4728_READ_RESPONSE_LEN]; 50562306a36Sopenharmony_ci int ret; 50662306a36Sopenharmony_ci unsigned int i; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci ret = i2c_master_recv(data->client, inbuf, MCP4728_READ_RESPONSE_LEN); 50962306a36Sopenharmony_ci if (ret < 0) { 51062306a36Sopenharmony_ci return dev_err_probe(&data->client->dev, ret, 51162306a36Sopenharmony_ci "failed to read mcp4728 conf.\n"); 51262306a36Sopenharmony_ci } else if (ret != MCP4728_READ_RESPONSE_LEN) { 51362306a36Sopenharmony_ci return dev_err_probe(&data->client->dev, -EIO, 51462306a36Sopenharmony_ci "failed to read mcp4728 conf. Wrong Response Len ret=%d\n", 51562306a36Sopenharmony_ci ret); 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci for (i = 0; i < MCP4728_N_CHANNELS; i++) { 51962306a36Sopenharmony_ci struct mcp4728_channel_data *ch = &data->chdata[i]; 52062306a36Sopenharmony_ci u8 r2 = inbuf[i * 6 + 1]; 52162306a36Sopenharmony_ci u8 r3 = inbuf[i * 6 + 2]; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci ch->dac_value = FIELD_GET(MCP4728_DAC_H_MASK, r2) << 8 | 52462306a36Sopenharmony_ci FIELD_GET(MCP4728_DAC_L_MASK, r3); 52562306a36Sopenharmony_ci ch->ref_mode = FIELD_GET(MCP4728_VREF_MASK, r2); 52662306a36Sopenharmony_ci ch->pd_mode = FIELD_GET(MCP4728_PDMODE_MASK, r2); 52762306a36Sopenharmony_ci ch->g_mode = FIELD_GET(MCP4728_GAIN_MASK, r2); 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci return 0; 53162306a36Sopenharmony_ci} 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_cistatic void mcp4728_reg_disable(void *reg) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci regulator_disable(reg); 53662306a36Sopenharmony_ci} 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic int mcp4728_probe(struct i2c_client *client) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci const struct i2c_device_id *id = i2c_client_get_device_id(client); 54162306a36Sopenharmony_ci struct mcp4728_data *data; 54262306a36Sopenharmony_ci struct iio_dev *indio_dev; 54362306a36Sopenharmony_ci int err; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 54662306a36Sopenharmony_ci if (!indio_dev) 54762306a36Sopenharmony_ci return -ENOMEM; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci data = iio_priv(indio_dev); 55062306a36Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 55162306a36Sopenharmony_ci data->client = client; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); 55462306a36Sopenharmony_ci if (IS_ERR(data->vdd_reg)) 55562306a36Sopenharmony_ci return PTR_ERR(data->vdd_reg); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci err = regulator_enable(data->vdd_reg); 55862306a36Sopenharmony_ci if (err) 55962306a36Sopenharmony_ci return err; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci err = devm_add_action_or_reset(&client->dev, mcp4728_reg_disable, 56262306a36Sopenharmony_ci data->vdd_reg); 56362306a36Sopenharmony_ci if (err) 56462306a36Sopenharmony_ci return err; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* 56762306a36Sopenharmony_ci * MCP4728 has internal EEPROM that save each channel boot 56862306a36Sopenharmony_ci * configuration. It means that device configuration is unknown to the 56962306a36Sopenharmony_ci * driver at kernel boot. mcp4728_init_channels_data() reads back DAC 57062306a36Sopenharmony_ci * settings and stores them in data structure. 57162306a36Sopenharmony_ci */ 57262306a36Sopenharmony_ci err = mcp4728_init_channels_data(data); 57362306a36Sopenharmony_ci if (err) { 57462306a36Sopenharmony_ci return dev_err_probe(&client->dev, err, 57562306a36Sopenharmony_ci "failed to read mcp4728 current configuration\n"); 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci err = mcp4728_init_scales_avail(data); 57962306a36Sopenharmony_ci if (err) { 58062306a36Sopenharmony_ci return dev_err_probe(&client->dev, err, 58162306a36Sopenharmony_ci "failed to init scales\n"); 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci indio_dev->name = id->name; 58562306a36Sopenharmony_ci indio_dev->info = &mcp4728_info; 58662306a36Sopenharmony_ci indio_dev->channels = mcp4728_channels; 58762306a36Sopenharmony_ci indio_dev->num_channels = MCP4728_N_CHANNELS; 58862306a36Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci return devm_iio_device_register(&client->dev, indio_dev); 59162306a36Sopenharmony_ci} 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_cistatic const struct i2c_device_id mcp4728_id[] = { 59462306a36Sopenharmony_ci { "mcp4728", 0 }, 59562306a36Sopenharmony_ci {} 59662306a36Sopenharmony_ci}; 59762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mcp4728_id); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic const struct of_device_id mcp4728_of_match[] = { 60062306a36Sopenharmony_ci { .compatible = "microchip,mcp4728" }, 60162306a36Sopenharmony_ci {} 60262306a36Sopenharmony_ci}; 60362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, mcp4728_of_match); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic struct i2c_driver mcp4728_driver = { 60662306a36Sopenharmony_ci .driver = { 60762306a36Sopenharmony_ci .name = "mcp4728", 60862306a36Sopenharmony_ci .of_match_table = mcp4728_of_match, 60962306a36Sopenharmony_ci .pm = pm_sleep_ptr(&mcp4728_pm_ops), 61062306a36Sopenharmony_ci }, 61162306a36Sopenharmony_ci .probe = mcp4728_probe, 61262306a36Sopenharmony_ci .id_table = mcp4728_id, 61362306a36Sopenharmony_ci}; 61462306a36Sopenharmony_cimodule_i2c_driver(mcp4728_driver); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ciMODULE_AUTHOR("Andrea Collamati <andrea.collamati@gmail.com>"); 61762306a36Sopenharmony_ciMODULE_DESCRIPTION("MCP4728 12-bit DAC"); 61862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 619