diff options
Diffstat (limited to 'drivers/iio/dac/mcp4725.c')
| -rw-r--r-- | drivers/iio/dac/mcp4725.c | 129 |
1 files changed, 67 insertions, 62 deletions
diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 6d71fd905e29..23b9e3a09ec8 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * mcp4725.c - Support for Microchip MCP4725/6 * @@ -5,10 +6,6 @@ * * Based on max517 by Roland Stigge <stigge@antcom.de> * - * This file is subject to the terms and conditions of version 2 of - * the GNU General Public License. See the file COPYING in the main - * directory of this archive for more details. - * * driver for the Microchip I2C 12-bit digital-to-analog converter (DAC) * (7-bit I2C slave address 0x60, the three LSBs can be configured in * hardware) @@ -19,23 +16,26 @@ #include <linux/err.h> #include <linux/delay.h> #include <linux/regulator/consumer.h> -#include <linux/of_device.h> -#include <linux/of.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> #include <linux/iio/dac/mcp4725.h> -#define MCP4725_DRV_NAME "mcp4725" - #define MCP472X_REF_VDD 0x00 #define MCP472X_REF_VREF_UNBUFFERED 0x02 #define MCP472X_REF_VREF_BUFFERED 0x03 +struct mcp4725_chip_info { + const struct iio_chan_spec *chan_spec; + u8 dac_reg_offset; + bool use_ext_ref_voltage; +}; + struct mcp4725_data { struct i2c_client *client; - int id; unsigned ref_mode; bool vref_buffered; u16 dac_value; @@ -45,33 +45,46 @@ struct mcp4725_data { struct regulator *vref_reg; }; -static int __maybe_unused mcp4725_suspend(struct device *dev) +static int mcp4725_suspend(struct device *dev) { struct mcp4725_data *data = iio_priv(i2c_get_clientdata( to_i2c_client(dev))); u8 outbuf[2]; + int ret; outbuf[0] = (data->powerdown_mode + 1) << 4; outbuf[1] = 0; data->powerdown = true; - return i2c_master_send(data->client, outbuf, 2); + ret = i2c_master_send(data->client, outbuf, 2); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + return 0; } -static int __maybe_unused mcp4725_resume(struct device *dev) +static int mcp4725_resume(struct device *dev) { struct mcp4725_data *data = iio_priv(i2c_get_clientdata( to_i2c_client(dev))); u8 outbuf[2]; + int ret; /* restore previous DAC value */ outbuf[0] = (data->dac_value >> 8) & 0xf; outbuf[1] = data->dac_value & 0xff; data->powerdown = false; - return i2c_master_send(data->client, outbuf, 2); + ret = i2c_master_send(data->client, outbuf, 2); + if (ret < 0) + return ret; + else if (ret != 2) + return -EIO; + return 0; } -static SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend, mcp4725_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend, + mcp4725_resume); static ssize_t mcp4725_store_eeprom(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) @@ -83,7 +96,7 @@ static ssize_t mcp4725_store_eeprom(struct device *dev, bool state; int ret; - ret = strtobool(buf, &state); + ret = kstrtobool(buf, &state); if (ret < 0) return ret; @@ -92,6 +105,7 @@ static ssize_t mcp4725_store_eeprom(struct device *dev, inoutbuf[0] = 0x60; /* write EEPROM */ inoutbuf[0] |= data->ref_mode << 3; + inoutbuf[0] |= data->powerdown ? ((data->powerdown_mode + 1) << 1) : 0; inoutbuf[1] = data->dac_value >> 4; inoutbuf[2] = (data->dac_value & 0xf) << 4; @@ -169,7 +183,7 @@ static ssize_t mcp4725_read_powerdown(struct iio_dev *indio_dev, { struct mcp4725_data *data = iio_priv(indio_dev); - return sprintf(buf, "%d\n", data->powerdown); + return sysfs_emit(buf, "%d\n", data->powerdown); } static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev, @@ -180,7 +194,7 @@ static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev, bool state; int ret; - ret = strtobool(buf, &state); + ret = kstrtobool(buf, &state); if (ret) return ret; @@ -223,9 +237,9 @@ static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp472x_powerdown_mode_enum[MCP4725]), - IIO_ENUM_AVAILABLE("powerdown_mode", - &mcp472x_powerdown_mode_enum[MCP4725]), - { }, + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &mcp472x_powerdown_mode_enum[MCP4725]), + { } }; static const struct iio_chan_spec_ext_info mcp4726_ext_info[] = { @@ -237,9 +251,9 @@ static const struct iio_chan_spec_ext_info mcp4726_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp472x_powerdown_mode_enum[MCP4726]), - IIO_ENUM_AVAILABLE("powerdown_mode", - &mcp472x_powerdown_mode_enum[MCP4726]), - { }, + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &mcp472x_powerdown_mode_enum[MCP4726]), + { } }; static const struct iio_chan_spec mcp472x_channel[] = { @@ -359,33 +373,21 @@ static const struct iio_info mcp4725_info = { .attrs = &mcp4725_attribute_group, }; -#ifdef CONFIG_OF static int mcp4725_probe_dt(struct device *dev, struct mcp4725_platform_data *pdata) { - struct device_node *np = dev->of_node; - - if (!np) - return -ENODEV; - /* check if is the vref-supply defined */ - pdata->use_vref = of_property_read_bool(np, "vref-supply"); + pdata->use_vref = device_property_present(dev, "vref-supply"); pdata->vref_buffered = - of_property_read_bool(np, "microchip,vref-buffered"); + device_property_read_bool(dev, "microchip,vref-buffered"); return 0; } -#else -static int mcp4725_probe_dt(struct device *dev, - struct mcp4725_platform_data *platform_data) -{ - return -ENODEV; -} -#endif -static int mcp4725_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int mcp4725_probe(struct i2c_client *client) { + const struct i2c_device_id *id = i2c_client_get_device_id(client); + const struct mcp4725_chip_info *info; struct mcp4725_data *data; struct iio_dev *indio_dev; struct mcp4725_platform_data *pdata, pdata_dt; @@ -400,10 +402,7 @@ static int mcp4725_probe(struct i2c_client *client, data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); data->client = client; - if (client->dev.of_node) - data->id = (enum chip_id)of_device_get_match_data(&client->dev); - else - data->id = id->driver_data; + info = i2c_get_match_data(client); pdata = dev_get_platdata(&client->dev); if (!pdata) { @@ -416,7 +415,7 @@ static int mcp4725_probe(struct i2c_client *client, pdata = &pdata_dt; } - if (data->id == MCP4725 && pdata->use_vref) { + if (info->use_ext_ref_voltage && pdata->use_vref) { dev_err(&client->dev, "external reference is unavailable on MCP4725"); return -EINVAL; @@ -455,15 +454,14 @@ static int mcp4725_probe(struct i2c_client *client, goto err_disable_vdd_reg; } - indio_dev->dev.parent = &client->dev; indio_dev->name = id->name; indio_dev->info = &mcp4725_info; - indio_dev->channels = &mcp472x_channel[id->driver_data]; + indio_dev->channels = info->chan_spec; indio_dev->num_channels = 1; indio_dev->modes = INDIO_DIRECT_MODE; /* read current DAC value and settings */ - err = i2c_master_recv(client, inbuf, data->id == MCP4725 ? 3 : 4); + err = i2c_master_recv(client, inbuf, info->dac_reg_offset); if (err < 0) { dev_err(&client->dev, "failed to read DAC value"); @@ -473,10 +471,10 @@ static int mcp4725_probe(struct i2c_client *client, data->powerdown = pd > 0; data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */ data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4); - if (data->id == MCP4726) + if (!info->use_ext_ref_voltage) ref = (inbuf[3] >> 3) & 0x3; - if (data->id == MCP4726 && ref != data->ref_mode) { + if (!info->use_ext_ref_voltage && 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); @@ -501,7 +499,7 @@ err_disable_vdd_reg: return err; } -static int mcp4725_remove(struct i2c_client *client) +static void mcp4725_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); struct mcp4725_data *data = iio_priv(indio_dev); @@ -511,37 +509,44 @@ static int mcp4725_remove(struct i2c_client *client) if (data->vref_reg) regulator_disable(data->vref_reg); regulator_disable(data->vdd_reg); - - return 0; } +static const struct mcp4725_chip_info mcp4725 = { + .chan_spec = &mcp472x_channel[MCP4725], + .dac_reg_offset = 3, + .use_ext_ref_voltage = true, +}; + +static const struct mcp4725_chip_info mcp4726 = { + .chan_spec = &mcp472x_channel[MCP4726], + .dac_reg_offset = 4, +}; + static const struct i2c_device_id mcp4725_id[] = { - { "mcp4725", MCP4725 }, - { "mcp4726", MCP4726 }, + { "mcp4725", (kernel_ulong_t)&mcp4725 }, + { "mcp4726", (kernel_ulong_t)&mcp4726 }, { } }; MODULE_DEVICE_TABLE(i2c, mcp4725_id); -#ifdef CONFIG_OF static const struct of_device_id mcp4725_of_match[] = { { .compatible = "microchip,mcp4725", - .data = (void *)MCP4725 + .data = &mcp4725 }, { .compatible = "microchip,mcp4726", - .data = (void *)MCP4726 + .data = &mcp4726 }, { } }; MODULE_DEVICE_TABLE(of, mcp4725_of_match); -#endif static struct i2c_driver mcp4725_driver = { .driver = { - .name = MCP4725_DRV_NAME, - .of_match_table = of_match_ptr(mcp4725_of_match), - .pm = &mcp4725_pm_ops, + .name = "mcp4725", + .of_match_table = mcp4725_of_match, + .pm = pm_sleep_ptr(&mcp4725_pm_ops), }, .probe = mcp4725_probe, .remove = mcp4725_remove, |
