Commit 29157c6d authored by Tomas Novotny's avatar Tomas Novotny Committed by Jonathan Cameron

iio: dac: mcp4725: support voltage reference selection

MCP47x6 chip supports selection of a voltage reference (VDD, VREF buffered
or unbuffered). MCP4725 doesn't have this feature thus the eventual setting
is ignored and user is warned.

The setting is stored only in the volatile memory of the chip. You need to
manually store it to the EEPROM of the chip via 'store_eeprom' sysfs entry.
Signed-off-by: default avatarTomas Novotny <tomas@novotny.cz>
Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
parent 8b0ee39f
...@@ -27,12 +27,20 @@ ...@@ -27,12 +27,20 @@
#define MCP4725_DRV_NAME "mcp4725" #define MCP4725_DRV_NAME "mcp4725"
#define MCP472X_REF_VDD 0x00
#define MCP472X_REF_VREF_UNBUFFERED 0x02
#define MCP472X_REF_VREF_BUFFERED 0x03
struct mcp4725_data { struct mcp4725_data {
struct i2c_client *client; struct i2c_client *client;
int id;
unsigned ref_mode;
bool vref_buffered;
u16 dac_value; u16 dac_value;
bool powerdown; bool powerdown;
unsigned powerdown_mode; unsigned powerdown_mode;
struct regulator *vdd_reg; struct regulator *vdd_reg;
struct regulator *vref_reg;
}; };
static int mcp4725_suspend(struct device *dev) static int mcp4725_suspend(struct device *dev)
...@@ -87,6 +95,7 @@ static ssize_t mcp4725_store_eeprom(struct device *dev, ...@@ -87,6 +95,7 @@ static ssize_t mcp4725_store_eeprom(struct device *dev,
return 0; return 0;
inoutbuf[0] = 0x60; /* write EEPROM */ inoutbuf[0] = 0x60; /* write EEPROM */
inoutbuf[0] |= data->ref_mode << 3;
inoutbuf[1] = data->dac_value >> 4; inoutbuf[1] = data->dac_value >> 4;
inoutbuf[2] = (data->dac_value & 0xf) << 4; inoutbuf[2] = (data->dac_value & 0xf) << 4;
...@@ -279,6 +288,28 @@ static int mcp4725_set_value(struct iio_dev *indio_dev, int val) ...@@ -279,6 +288,28 @@ static int mcp4725_set_value(struct iio_dev *indio_dev, int val)
return 0; return 0;
} }
static int mcp4726_set_cfg(struct iio_dev *indio_dev)
{
struct mcp4725_data *data = iio_priv(indio_dev);
u8 outbuf[3];
int ret;
outbuf[0] = 0x40;
outbuf[0] |= data->ref_mode << 3;
if (data->powerdown)
outbuf[0] |= data->powerdown << 1;
outbuf[1] = data->dac_value >> 4;
outbuf[2] = (data->dac_value & 0xf) << 4;
ret = i2c_master_send(data->client, outbuf, 3);
if (ret < 0)
return ret;
else if (ret != 3)
return -EIO;
else
return 0;
}
static int mcp4725_read_raw(struct iio_dev *indio_dev, static int mcp4725_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, struct iio_chan_spec const *chan,
int *val, int *val2, long mask) int *val, int *val2, long mask)
...@@ -291,7 +322,11 @@ static int mcp4725_read_raw(struct iio_dev *indio_dev, ...@@ -291,7 +322,11 @@ static int mcp4725_read_raw(struct iio_dev *indio_dev,
*val = data->dac_value; *val = data->dac_value;
return IIO_VAL_INT; return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE: case IIO_CHAN_INFO_SCALE:
if (data->ref_mode == MCP472X_REF_VDD)
ret = regulator_get_voltage(data->vdd_reg); ret = regulator_get_voltage(data->vdd_reg);
else
ret = regulator_get_voltage(data->vref_reg);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -335,8 +370,9 @@ static int mcp4725_probe(struct i2c_client *client, ...@@ -335,8 +370,9 @@ static int mcp4725_probe(struct i2c_client *client,
struct mcp4725_data *data; struct mcp4725_data *data;
struct iio_dev *indio_dev; struct iio_dev *indio_dev;
struct mcp4725_platform_data *pdata = dev_get_platdata(&client->dev); struct mcp4725_platform_data *pdata = dev_get_platdata(&client->dev);
u8 inbuf[3]; u8 inbuf[4];
u8 pd; u8 pd;
u8 ref;
int err; int err;
if (!pdata) { if (!pdata) {
...@@ -350,6 +386,26 @@ static int mcp4725_probe(struct i2c_client *client, ...@@ -350,6 +386,26 @@ static int mcp4725_probe(struct i2c_client *client,
data = iio_priv(indio_dev); data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev); i2c_set_clientdata(client, indio_dev);
data->client = client; data->client = client;
data->id = id->driver_data;
if (data->id == MCP4725 && pdata->use_vref) {
dev_err(&client->dev,
"external reference is unavailable on MCP4725");
return -EINVAL;
}
if (!pdata->use_vref && pdata->vref_buffered) {
dev_err(&client->dev,
"buffering is unavailable on the internal reference");
return -EINVAL;
}
if (!pdata->use_vref)
data->ref_mode = MCP472X_REF_VDD;
else
data->ref_mode = pdata->vref_buffered ?
MCP472X_REF_VREF_BUFFERED :
MCP472X_REF_VREF_UNBUFFERED;
data->vdd_reg = devm_regulator_get(&client->dev, "vdd"); data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
if (IS_ERR(data->vdd_reg)) if (IS_ERR(data->vdd_reg))
...@@ -359,6 +415,18 @@ static int mcp4725_probe(struct i2c_client *client, ...@@ -359,6 +415,18 @@ static int mcp4725_probe(struct i2c_client *client,
if (err) if (err)
return err; return err;
if (pdata->use_vref) {
data->vref_reg = devm_regulator_get(&client->dev, "vref");
if (IS_ERR(data->vref_reg)) {
err = PTR_ERR(data->vdd_reg);
goto err_disable_vdd_reg;
}
err = regulator_enable(data->vref_reg);
if (err)
goto err_disable_vdd_reg;
}
indio_dev->dev.parent = &client->dev; indio_dev->dev.parent = &client->dev;
indio_dev->name = id->name; indio_dev->name = id->name;
indio_dev->info = &mcp4725_info; indio_dev->info = &mcp4725_info;
...@@ -366,23 +434,38 @@ static int mcp4725_probe(struct i2c_client *client, ...@@ -366,23 +434,38 @@ static int mcp4725_probe(struct i2c_client *client,
indio_dev->num_channels = 1; indio_dev->num_channels = 1;
indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->modes = INDIO_DIRECT_MODE;
/* read current DAC value */ /* read current DAC value and settings */
err = i2c_master_recv(client, inbuf, 3); err = i2c_master_recv(client, inbuf, data->id == MCP4725 ? 3 : 4);
if (err < 0) { if (err < 0) {
dev_err(&client->dev, "failed to read DAC value"); dev_err(&client->dev, "failed to read DAC value");
goto err_disable_vdd_reg; goto err_disable_vref_reg;
} }
pd = (inbuf[0] >> 1) & 0x3; pd = (inbuf[0] >> 1) & 0x3;
data->powerdown = pd > 0 ? true : false; data->powerdown = pd > 0 ? true : false;
data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */ data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */
data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4); data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
if (data->id == MCP4726)
ref = (inbuf[3] >> 3) & 0x3;
if (data->id == MCP4726 && ref != data->ref_mode) {
dev_info(&client->dev,
"voltage reference mode differs (conf: %u, eeprom: %u), setting %u",
data->ref_mode, ref, data->ref_mode);
err = mcp4726_set_cfg(indio_dev);
if (err < 0)
goto err_disable_vref_reg;
}
err = iio_device_register(indio_dev); err = iio_device_register(indio_dev);
if (err) if (err)
goto err_disable_vdd_reg; goto err_disable_vref_reg;
return 0; return 0;
err_disable_vref_reg:
if (data->vref_reg)
regulator_disable(data->vref_reg);
err_disable_vdd_reg: err_disable_vdd_reg:
regulator_disable(data->vdd_reg); regulator_disable(data->vdd_reg);
...@@ -397,6 +480,8 @@ static int mcp4725_remove(struct i2c_client *client) ...@@ -397,6 +480,8 @@ static int mcp4725_remove(struct i2c_client *client)
iio_device_unregister(indio_dev); iio_device_unregister(indio_dev);
if (data->vref_reg)
regulator_disable(data->vref_reg);
regulator_disable(data->vdd_reg); regulator_disable(data->vdd_reg);
return 0; return 0;
......
...@@ -9,7 +9,18 @@ ...@@ -9,7 +9,18 @@
#ifndef IIO_DAC_MCP4725_H_ #ifndef IIO_DAC_MCP4725_H_
#define IIO_DAC_MCP4725_H_ #define IIO_DAC_MCP4725_H_
/**
* struct mcp4725_platform_data - MCP4725/6 DAC specific data.
* @use_vref: Whether an external reference voltage on Vref pin should be used.
* Additional vref-supply must be specified when used.
* @vref_buffered: Controls buffering of the external reference voltage.
*
* Vref related settings are available only on MCP4756. See
* Documentation/devicetree/bindings/iio/dac/mcp4725.txt for more information.
*/
struct mcp4725_platform_data { struct mcp4725_platform_data {
bool use_vref;
bool vref_buffered;
}; };
#endif /* IIO_DAC_MCP4725_H_ */ #endif /* IIO_DAC_MCP4725_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment