| // SPDX-License-Identifier: GPL-2.0-only | 
 | /* | 
 |  * Support for Microchip MCP4728 | 
 |  * | 
 |  * Copyright (C) 2023 Andrea Collamati <andrea.collamati@gmail.com> | 
 |  * | 
 |  * Based on mcp4725 by Peter Meerwald <pmeerw@pmeerw.net> | 
 |  * | 
 |  * Driver for the Microchip I2C 12-bit digital-to-analog quad channels | 
 |  * converter (DAC). | 
 |  * | 
 |  * (7-bit I2C slave address 0x60, the three LSBs can be configured in | 
 |  * hardware) | 
 |  */ | 
 |  | 
 | #include <linux/bitfield.h> | 
 | #include <linux/delay.h> | 
 | #include <linux/err.h> | 
 | #include <linux/i2c.h> | 
 | #include <linux/iio/iio.h> | 
 | #include <linux/iio/sysfs.h> | 
 | #include <linux/module.h> | 
 | #include <linux/mod_devicetable.h> | 
 | #include <linux/property.h> | 
 | #include <linux/regulator/consumer.h> | 
 |  | 
 | #define MCP4728_RESOLUTION	  12 | 
 | #define MCP4728_N_CHANNELS	  4 | 
 |  | 
 | #define MCP4728_CMD_MASK	  GENMASK(7, 3) | 
 | #define MCP4728_CHSEL_MASK	  GENMASK(2, 1) | 
 | #define MCP4728_UDAC_MASK	  BIT(0) | 
 |  | 
 | #define MCP4728_VREF_MASK	  BIT(7) | 
 | #define MCP4728_PDMODE_MASK	  GENMASK(6, 5) | 
 | #define MCP4728_GAIN_MASK	  BIT(4) | 
 |  | 
 | #define MCP4728_DAC_H_MASK	  GENMASK(3, 0) | 
 | #define MCP4728_DAC_L_MASK	  GENMASK(7, 0) | 
 |  | 
 | #define MCP4728_RDY_MASK	  BIT(7) | 
 |  | 
 | #define MCP4728_MW_CMD		  0x08 /* Multiwrite Command */ | 
 | #define MCP4728_SW_CMD		  0x0A /* Sequential Write Command with EEPROM */ | 
 |  | 
 | #define MCP4728_READ_RESPONSE_LEN (MCP4728_N_CHANNELS * 3 * 2) | 
 | #define MCP4728_WRITE_EEPROM_LEN  (1 + MCP4728_N_CHANNELS * 2) | 
 |  | 
 | enum vref_mode { | 
 | 	MCP4728_VREF_EXTERNAL_VDD    = 0, | 
 | 	MCP4728_VREF_INTERNAL_2048mV = 1, | 
 | }; | 
 |  | 
 | enum gain_mode { | 
 | 	MCP4728_GAIN_X1 = 0, | 
 | 	MCP4728_GAIN_X2 = 1, | 
 | }; | 
 |  | 
 | enum iio_powerdown_mode { | 
 | 	MCP4728_IIO_1K, | 
 | 	MCP4728_IIO_100K, | 
 | 	MCP4728_IIO_500K, | 
 | }; | 
 |  | 
 | struct mcp4728_channel_data { | 
 | 	enum vref_mode ref_mode; | 
 | 	enum iio_powerdown_mode pd_mode; | 
 | 	enum gain_mode g_mode; | 
 | 	u16 dac_value; | 
 | }; | 
 |  | 
 | /* MCP4728 Full Scale Ranges | 
 |  * the device available ranges are | 
 |  * - VREF = VDD				FSR = from 0.0V to VDD | 
 |  * - VREF = Internal	Gain = 1	FSR = from 0.0V to VREF | 
 |  * - VREF = Internal	Gain = 2	FSR = from 0.0V to 2*VREF | 
 |  */ | 
 | enum mcp4728_scale { | 
 | 	MCP4728_SCALE_VDD, | 
 | 	MCP4728_SCALE_VINT_NO_GAIN, | 
 | 	MCP4728_SCALE_VINT_GAIN_X2, | 
 | 	MCP4728_N_SCALES | 
 | }; | 
 |  | 
 | struct mcp4728_data { | 
 | 	struct i2c_client *client; | 
 | 	struct regulator *vdd_reg; | 
 | 	bool powerdown; | 
 | 	int scales_avail[MCP4728_N_SCALES * 2]; | 
 | 	struct mcp4728_channel_data chdata[MCP4728_N_CHANNELS]; | 
 | }; | 
 |  | 
 | #define MCP4728_CHAN(chan) {						\ | 
 | 	.type = IIO_VOLTAGE,						\ | 
 | 	.output = 1,							\ | 
 | 	.indexed = 1,							\ | 
 | 	.channel = chan,						\ | 
 | 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW)	|		\ | 
 | 			      BIT(IIO_CHAN_INFO_SCALE),			\ | 
 | 	.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),	\ | 
 | 	.ext_info = mcp4728_ext_info,					\ | 
 | } | 
 |  | 
 | static int mcp4728_suspend(struct device *dev); | 
 | static int mcp4728_resume(struct device *dev); | 
 |  | 
 | static ssize_t mcp4728_store_eeprom(struct device *dev, | 
 | 				    struct device_attribute *attr, | 
 | 				    const char *buf, size_t len) | 
 | { | 
 | 	struct iio_dev *indio_dev = dev_to_iio_dev(dev); | 
 | 	struct mcp4728_data *data = iio_priv(indio_dev); | 
 | 	u8 outbuf[MCP4728_WRITE_EEPROM_LEN]; | 
 | 	int tries = 20; | 
 | 	u8 inbuf[3]; | 
 | 	bool state; | 
 | 	int ret; | 
 | 	unsigned int i; | 
 |  | 
 | 	ret = kstrtobool(buf, &state); | 
 | 	if (ret < 0) | 
 | 		return ret; | 
 |  | 
 | 	if (!state) | 
 | 		return 0; | 
 |  | 
 | 	outbuf[0] = FIELD_PREP(MCP4728_CMD_MASK, MCP4728_SW_CMD); | 
 |  | 
 | 	for (i = 0; i < MCP4728_N_CHANNELS; i++) { | 
 | 		struct mcp4728_channel_data *ch = &data->chdata[i]; | 
 | 		int offset			= 1 + i * 2; | 
 |  | 
 | 		outbuf[offset] = FIELD_PREP(MCP4728_VREF_MASK, ch->ref_mode); | 
 |  | 
 | 		if (data->powerdown) { | 
 | 			u8 mcp4728_pd_mode = ch->pd_mode + 1; | 
 |  | 
 | 			outbuf[offset] |= FIELD_PREP(MCP4728_PDMODE_MASK, | 
 | 						     mcp4728_pd_mode); | 
 | 		} | 
 |  | 
 | 		outbuf[offset] |= FIELD_PREP(MCP4728_GAIN_MASK, ch->g_mode); | 
 | 		outbuf[offset] |= | 
 | 			FIELD_PREP(MCP4728_DAC_H_MASK, ch->dac_value >> 8); | 
 | 		outbuf[offset + 1] = | 
 | 			FIELD_PREP(MCP4728_DAC_L_MASK, ch->dac_value); | 
 | 	} | 
 |  | 
 | 	ret = i2c_master_send(data->client, outbuf, MCP4728_WRITE_EEPROM_LEN); | 
 | 	if (ret < 0) | 
 | 		return ret; | 
 | 	else if (ret != MCP4728_WRITE_EEPROM_LEN) | 
 | 		return -EIO; | 
 |  | 
 | 	/* wait RDY signal for write complete, takes up to 50ms */ | 
 | 	while (tries--) { | 
 | 		msleep(20); | 
 | 		ret = i2c_master_recv(data->client, inbuf, 3); | 
 | 		if (ret < 0) | 
 | 			return ret; | 
 | 		else if (ret != 3) | 
 | 			return -EIO; | 
 |  | 
 | 		if (FIELD_GET(MCP4728_RDY_MASK, inbuf[0])) | 
 | 			break; | 
 | 	} | 
 |  | 
 | 	if (tries < 0) { | 
 | 		dev_err(&data->client->dev, "%s failed, incomplete\n", | 
 | 			__func__); | 
 | 		return -EIO; | 
 | 	} | 
 | 	return len; | 
 | } | 
 |  | 
 | static IIO_DEVICE_ATTR(store_eeprom, 0200, NULL, mcp4728_store_eeprom, 0); | 
 |  | 
 | static struct attribute *mcp4728_attributes[] = { | 
 | 	&iio_dev_attr_store_eeprom.dev_attr.attr, | 
 | 	NULL, | 
 | }; | 
 |  | 
 | static const struct attribute_group mcp4728_attribute_group = { | 
 | 	.attrs = mcp4728_attributes, | 
 | }; | 
 |  | 
 | static int mcp4728_program_channel_cfg(int channel, struct iio_dev *indio_dev) | 
 | { | 
 | 	struct mcp4728_data *data	= iio_priv(indio_dev); | 
 | 	struct mcp4728_channel_data *ch = &data->chdata[channel]; | 
 | 	u8 outbuf[3]; | 
 | 	int ret; | 
 |  | 
 | 	outbuf[0] = FIELD_PREP(MCP4728_CMD_MASK, MCP4728_MW_CMD); | 
 | 	outbuf[0] |= FIELD_PREP(MCP4728_CHSEL_MASK, channel); | 
 | 	outbuf[0] |= FIELD_PREP(MCP4728_UDAC_MASK, 0); | 
 |  | 
 | 	outbuf[1] = FIELD_PREP(MCP4728_VREF_MASK, ch->ref_mode); | 
 |  | 
 | 	if (data->powerdown) | 
 | 		outbuf[1] |= FIELD_PREP(MCP4728_PDMODE_MASK, ch->pd_mode + 1); | 
 |  | 
 | 	outbuf[1] |= FIELD_PREP(MCP4728_GAIN_MASK, ch->g_mode); | 
 | 	outbuf[1] |= FIELD_PREP(MCP4728_DAC_H_MASK, ch->dac_value >> 8); | 
 | 	outbuf[2] = FIELD_PREP(MCP4728_DAC_L_MASK, ch->dac_value); | 
 |  | 
 | 	ret = i2c_master_send(data->client, outbuf, 3); | 
 | 	if (ret < 0) | 
 | 		return ret; | 
 | 	else if (ret != 3) | 
 | 		return -EIO; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static const char *const mcp4728_powerdown_modes[] = { "1kohm_to_gnd", | 
 | 						       "100kohm_to_gnd", | 
 | 						       "500kohm_to_gnd" }; | 
 |  | 
 | static int mcp4728_get_powerdown_mode(struct iio_dev *indio_dev, | 
 | 				      const struct iio_chan_spec *chan) | 
 | { | 
 | 	struct mcp4728_data *data = iio_priv(indio_dev); | 
 |  | 
 | 	return data->chdata[chan->channel].pd_mode; | 
 | } | 
 |  | 
 | static int mcp4728_set_powerdown_mode(struct iio_dev *indio_dev, | 
 | 				      const struct iio_chan_spec *chan, | 
 | 				      unsigned int mode) | 
 | { | 
 | 	struct mcp4728_data *data = iio_priv(indio_dev); | 
 |  | 
 | 	data->chdata[chan->channel].pd_mode = mode; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static ssize_t mcp4728_read_powerdown(struct iio_dev *indio_dev, | 
 | 				      uintptr_t private, | 
 | 				      const struct iio_chan_spec *chan, | 
 | 				      char *buf) | 
 | { | 
 | 	struct mcp4728_data *data = iio_priv(indio_dev); | 
 |  | 
 | 	return sysfs_emit(buf, "%d\n", data->powerdown); | 
 | } | 
 |  | 
 | static ssize_t mcp4728_write_powerdown(struct iio_dev *indio_dev, | 
 | 				       uintptr_t private, | 
 | 				       const struct iio_chan_spec *chan, | 
 | 				       const char *buf, size_t len) | 
 | { | 
 | 	struct mcp4728_data *data = iio_priv(indio_dev); | 
 | 	bool state; | 
 | 	int ret; | 
 |  | 
 | 	ret = kstrtobool(buf, &state); | 
 | 	if (ret) | 
 | 		return ret; | 
 |  | 
 | 	if (state) | 
 | 		ret = mcp4728_suspend(&data->client->dev); | 
 | 	else | 
 | 		ret = mcp4728_resume(&data->client->dev); | 
 |  | 
 | 	if (ret < 0) | 
 | 		return ret; | 
 |  | 
 | 	return len; | 
 | } | 
 |  | 
 | static const struct iio_enum mcp4728_powerdown_mode_enum = { | 
 | 	.items	   = mcp4728_powerdown_modes, | 
 | 	.num_items = ARRAY_SIZE(mcp4728_powerdown_modes), | 
 | 	.get	   = mcp4728_get_powerdown_mode, | 
 | 	.set	   = mcp4728_set_powerdown_mode, | 
 | }; | 
 |  | 
 | static const struct iio_chan_spec_ext_info mcp4728_ext_info[] = { | 
 | 	{ | 
 | 		.name	= "powerdown", | 
 | 		.read	= mcp4728_read_powerdown, | 
 | 		.write	= mcp4728_write_powerdown, | 
 | 		.shared = IIO_SEPARATE, | 
 | 	}, | 
 | 	IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4728_powerdown_mode_enum), | 
 | 	IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, | 
 | 			   &mcp4728_powerdown_mode_enum), | 
 | 	{}, | 
 | }; | 
 |  | 
 | static const struct iio_chan_spec mcp4728_channels[MCP4728_N_CHANNELS] = { | 
 | 	MCP4728_CHAN(0), | 
 | 	MCP4728_CHAN(1), | 
 | 	MCP4728_CHAN(2), | 
 | 	MCP4728_CHAN(3), | 
 | }; | 
 |  | 
 | static void mcp4728_get_scale_avail(enum mcp4728_scale scale, | 
 | 				    struct mcp4728_data *data, int *val, | 
 | 				    int *val2) | 
 | { | 
 | 	*val  = data->scales_avail[scale * 2]; | 
 | 	*val2 = data->scales_avail[scale * 2 + 1]; | 
 | } | 
 |  | 
 | static void mcp4728_get_scale(int channel, struct mcp4728_data *data, int *val, | 
 | 			      int *val2) | 
 | { | 
 | 	int ref_mode = data->chdata[channel].ref_mode; | 
 | 	int g_mode   = data->chdata[channel].g_mode; | 
 |  | 
 | 	if (ref_mode == MCP4728_VREF_EXTERNAL_VDD) { | 
 | 		mcp4728_get_scale_avail(MCP4728_SCALE_VDD, data, val, val2); | 
 | 	} else { | 
 | 		if (g_mode == MCP4728_GAIN_X1) { | 
 | 			mcp4728_get_scale_avail(MCP4728_SCALE_VINT_NO_GAIN, | 
 | 						data, val, val2); | 
 | 		} else { | 
 | 			mcp4728_get_scale_avail(MCP4728_SCALE_VINT_GAIN_X2, | 
 | 						data, val, val2); | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | static int mcp4728_find_matching_scale(struct mcp4728_data *data, int val, | 
 | 				       int val2) | 
 | { | 
 | 	for (int i = 0; i < MCP4728_N_SCALES; i++) { | 
 | 		if (data->scales_avail[i * 2] == val && | 
 | 		    data->scales_avail[i * 2 + 1] == val2) | 
 | 			return i; | 
 | 	} | 
 | 	return -EINVAL; | 
 | } | 
 |  | 
 | static int mcp4728_set_scale(int channel, struct mcp4728_data *data, int val, | 
 | 			     int val2) | 
 | { | 
 | 	int scale = mcp4728_find_matching_scale(data, val, val2); | 
 |  | 
 | 	if (scale < 0) | 
 | 		return scale; | 
 |  | 
 | 	switch (scale) { | 
 | 	case MCP4728_SCALE_VDD: | 
 | 		data->chdata[channel].ref_mode = MCP4728_VREF_EXTERNAL_VDD; | 
 | 		return 0; | 
 | 	case MCP4728_SCALE_VINT_NO_GAIN: | 
 | 		data->chdata[channel].ref_mode = MCP4728_VREF_INTERNAL_2048mV; | 
 | 		data->chdata[channel].g_mode   = MCP4728_GAIN_X1; | 
 | 		return 0; | 
 | 	case MCP4728_SCALE_VINT_GAIN_X2: | 
 | 		data->chdata[channel].ref_mode = MCP4728_VREF_INTERNAL_2048mV; | 
 | 		data->chdata[channel].g_mode   = MCP4728_GAIN_X2; | 
 | 		return 0; | 
 | 	default: | 
 | 		return -EINVAL; | 
 | 	} | 
 | } | 
 |  | 
 | static int mcp4728_read_raw(struct iio_dev *indio_dev, | 
 | 			    struct iio_chan_spec const *chan, int *val, | 
 | 			    int *val2, long mask) | 
 | { | 
 | 	struct mcp4728_data *data = iio_priv(indio_dev); | 
 |  | 
 | 	switch (mask) { | 
 | 	case IIO_CHAN_INFO_RAW: | 
 | 		*val = data->chdata[chan->channel].dac_value; | 
 | 		return IIO_VAL_INT; | 
 | 	case IIO_CHAN_INFO_SCALE: | 
 | 		mcp4728_get_scale(chan->channel, data, val, val2); | 
 | 		return IIO_VAL_INT_PLUS_MICRO; | 
 | 	} | 
 | 	return -EINVAL; | 
 | } | 
 |  | 
 | static int mcp4728_write_raw(struct iio_dev *indio_dev, | 
 | 			     struct iio_chan_spec const *chan, int val, | 
 | 			     int val2, long mask) | 
 | { | 
 | 	struct mcp4728_data *data = iio_priv(indio_dev); | 
 | 	int ret; | 
 |  | 
 | 	switch (mask) { | 
 | 	case IIO_CHAN_INFO_RAW: | 
 | 		if (val < 0 || val > GENMASK(MCP4728_RESOLUTION - 1, 0)) | 
 | 			return -EINVAL; | 
 | 		data->chdata[chan->channel].dac_value = val; | 
 | 		return mcp4728_program_channel_cfg(chan->channel, indio_dev); | 
 | 	case IIO_CHAN_INFO_SCALE: | 
 | 		ret = mcp4728_set_scale(chan->channel, data, val, val2); | 
 | 		if (ret) | 
 | 			return ret; | 
 |  | 
 | 		return mcp4728_program_channel_cfg(chan->channel, indio_dev); | 
 | 	default: | 
 | 		return -EINVAL; | 
 | 	} | 
 | } | 
 |  | 
 | static void mcp4728_init_scale_avail(enum mcp4728_scale scale, int vref_mv, | 
 | 				     struct mcp4728_data *data) | 
 | { | 
 | 	s64 tmp; | 
 | 	int value_micro; | 
 | 	int value_int; | 
 |  | 
 | 	tmp	  = (s64)vref_mv * 1000000LL >> MCP4728_RESOLUTION; | 
 | 	value_int = div_s64_rem(tmp, 1000000LL, &value_micro); | 
 |  | 
 | 	data->scales_avail[scale * 2]	  = value_int; | 
 | 	data->scales_avail[scale * 2 + 1] = value_micro; | 
 | } | 
 |  | 
 | static int mcp4728_init_scales_avail(struct mcp4728_data *data) | 
 | { | 
 | 	int ret; | 
 |  | 
 | 	ret = regulator_get_voltage(data->vdd_reg); | 
 | 	if (ret < 0) | 
 | 		return ret; | 
 |  | 
 | 	mcp4728_init_scale_avail(MCP4728_SCALE_VDD, ret / 1000, data); | 
 | 	mcp4728_init_scale_avail(MCP4728_SCALE_VINT_NO_GAIN, 2048, data); | 
 | 	mcp4728_init_scale_avail(MCP4728_SCALE_VINT_GAIN_X2, 4096, data); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int mcp4728_read_avail(struct iio_dev *indio_dev, | 
 | 			      struct iio_chan_spec const *chan, | 
 | 			      const int **vals, int *type, int *length, | 
 | 			      long info) | 
 | { | 
 | 	struct mcp4728_data *data = iio_priv(indio_dev); | 
 |  | 
 | 	switch (info) { | 
 | 	case IIO_CHAN_INFO_SCALE: | 
 | 		*type = IIO_VAL_INT_PLUS_MICRO; | 
 |  | 
 | 		switch (chan->type) { | 
 | 		case IIO_VOLTAGE: | 
 | 			*vals	= data->scales_avail; | 
 | 			*length = MCP4728_N_SCALES * 2; | 
 | 			return IIO_AVAIL_LIST; | 
 | 		default: | 
 | 			return -EINVAL; | 
 | 		} | 
 | 	default: | 
 | 		return -EINVAL; | 
 | 	} | 
 | } | 
 |  | 
 | static const struct iio_info mcp4728_info = { | 
 | 	.read_raw   = mcp4728_read_raw, | 
 | 	.write_raw  = mcp4728_write_raw, | 
 | 	.read_avail = &mcp4728_read_avail, | 
 | 	.attrs	    = &mcp4728_attribute_group, | 
 | }; | 
 |  | 
 | static int mcp4728_suspend(struct device *dev) | 
 | { | 
 | 	struct iio_dev *indio_dev = dev_get_drvdata(dev); | 
 | 	struct mcp4728_data *data = iio_priv(indio_dev); | 
 | 	unsigned int i; | 
 |  | 
 | 	data->powerdown = true; | 
 |  | 
 | 	for (i = 0; i < MCP4728_N_CHANNELS; i++) { | 
 | 		int err = mcp4728_program_channel_cfg(i, indio_dev); | 
 |  | 
 | 		if (err) | 
 | 			return err; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int mcp4728_resume(struct device *dev) | 
 | { | 
 | 	struct iio_dev *indio_dev = dev_get_drvdata(dev); | 
 | 	struct mcp4728_data *data = iio_priv(indio_dev); | 
 | 	int err			  = 0; | 
 | 	unsigned int i; | 
 |  | 
 | 	data->powerdown = false; | 
 |  | 
 | 	for (i = 0; i < MCP4728_N_CHANNELS; i++) { | 
 | 		int ret = mcp4728_program_channel_cfg(i, indio_dev); | 
 |  | 
 | 		if (ret) | 
 | 			err = ret; | 
 | 	} | 
 | 	return err; | 
 | } | 
 |  | 
 | static DEFINE_SIMPLE_DEV_PM_OPS(mcp4728_pm_ops, mcp4728_suspend, | 
 | 				mcp4728_resume); | 
 |  | 
 | static int mcp4728_init_channels_data(struct mcp4728_data *data) | 
 | { | 
 | 	u8 inbuf[MCP4728_READ_RESPONSE_LEN]; | 
 | 	int ret; | 
 | 	unsigned int i; | 
 |  | 
 | 	ret = i2c_master_recv(data->client, inbuf, MCP4728_READ_RESPONSE_LEN); | 
 | 	if (ret < 0) { | 
 | 		return dev_err_probe(&data->client->dev, ret, | 
 | 				     "failed to read mcp4728 conf.\n"); | 
 | 	} else if (ret != MCP4728_READ_RESPONSE_LEN) { | 
 | 		return dev_err_probe(&data->client->dev, -EIO, | 
 | 			"failed to read mcp4728 conf. Wrong Response Len ret=%d\n", | 
 | 			ret); | 
 | 	} | 
 |  | 
 | 	for (i = 0; i < MCP4728_N_CHANNELS; i++) { | 
 | 		struct mcp4728_channel_data *ch = &data->chdata[i]; | 
 | 		u8 r2				= inbuf[i * 6 + 1]; | 
 | 		u8 r3				= inbuf[i * 6 + 2]; | 
 |  | 
 | 		ch->dac_value = FIELD_GET(MCP4728_DAC_H_MASK, r2) << 8 | | 
 | 				FIELD_GET(MCP4728_DAC_L_MASK, r3); | 
 | 		ch->ref_mode = FIELD_GET(MCP4728_VREF_MASK, r2); | 
 | 		ch->pd_mode  = FIELD_GET(MCP4728_PDMODE_MASK, r2); | 
 | 		ch->g_mode   = FIELD_GET(MCP4728_GAIN_MASK, r2); | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void mcp4728_reg_disable(void *reg) | 
 | { | 
 | 	regulator_disable(reg); | 
 | } | 
 |  | 
 | static int mcp4728_probe(struct i2c_client *client) | 
 | { | 
 | 	const struct i2c_device_id *id = i2c_client_get_device_id(client); | 
 | 	struct mcp4728_data *data; | 
 | 	struct iio_dev *indio_dev; | 
 | 	int err; | 
 |  | 
 | 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); | 
 | 	if (!indio_dev) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	data = iio_priv(indio_dev); | 
 | 	i2c_set_clientdata(client, indio_dev); | 
 | 	data->client = client; | 
 |  | 
 | 	data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); | 
 | 	if (IS_ERR(data->vdd_reg)) | 
 | 		return PTR_ERR(data->vdd_reg); | 
 |  | 
 | 	err = regulator_enable(data->vdd_reg); | 
 | 	if (err) | 
 | 		return err; | 
 |  | 
 | 	err = devm_add_action_or_reset(&client->dev, mcp4728_reg_disable, | 
 | 				       data->vdd_reg); | 
 | 	if (err) | 
 | 		return err; | 
 |  | 
 | 	/* | 
 | 	 * MCP4728 has internal EEPROM that save each channel boot | 
 | 	 * configuration. It means that device configuration is unknown to the | 
 | 	 * driver at kernel boot. mcp4728_init_channels_data() reads back DAC | 
 | 	 * settings and stores them in data structure. | 
 | 	 */ | 
 | 	err = mcp4728_init_channels_data(data); | 
 | 	if (err) { | 
 | 		return dev_err_probe(&client->dev, err, | 
 | 			"failed to read mcp4728 current configuration\n"); | 
 | 	} | 
 |  | 
 | 	err = mcp4728_init_scales_avail(data); | 
 | 	if (err) { | 
 | 		return dev_err_probe(&client->dev, err, | 
 | 				     "failed to init scales\n"); | 
 | 	} | 
 |  | 
 | 	indio_dev->name		= id->name; | 
 | 	indio_dev->info		= &mcp4728_info; | 
 | 	indio_dev->channels	= mcp4728_channels; | 
 | 	indio_dev->num_channels = MCP4728_N_CHANNELS; | 
 | 	indio_dev->modes	= INDIO_DIRECT_MODE; | 
 |  | 
 | 	return devm_iio_device_register(&client->dev, indio_dev); | 
 | } | 
 |  | 
 | static const struct i2c_device_id mcp4728_id[] = { | 
 | 	{ "mcp4728", 0 }, | 
 | 	{} | 
 | }; | 
 | MODULE_DEVICE_TABLE(i2c, mcp4728_id); | 
 |  | 
 | static const struct of_device_id mcp4728_of_match[] = { | 
 | 	{ .compatible = "microchip,mcp4728" }, | 
 | 	{} | 
 | }; | 
 | MODULE_DEVICE_TABLE(of, mcp4728_of_match); | 
 |  | 
 | static struct i2c_driver mcp4728_driver = { | 
 | 	.driver = { | 
 | 		.name = "mcp4728", | 
 | 		.of_match_table = mcp4728_of_match, | 
 | 		.pm = pm_sleep_ptr(&mcp4728_pm_ops), | 
 | 	}, | 
 | 	.probe = mcp4728_probe, | 
 | 	.id_table = mcp4728_id, | 
 | }; | 
 | module_i2c_driver(mcp4728_driver); | 
 |  | 
 | MODULE_AUTHOR("Andrea Collamati <andrea.collamati@gmail.com>"); | 
 | MODULE_DESCRIPTION("MCP4728 12-bit DAC"); | 
 | MODULE_LICENSE("GPL"); |