diff options
Diffstat (limited to 'drivers/iio/dac')
31 files changed, 745 insertions, 28 deletions
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 25bed2d7d2b9..965d5c0d2468 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -60,7 +60,8 @@ config AD5446 Say yes here to build support for Analog Devices AD5300, AD5301, AD5310, AD5311, AD5320, AD5321, AD5444, AD5446, AD5450, AD5451, AD5452, AD5453, AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5602, AD5611, AD5612, - AD5620, AD5621, AD5622, AD5640, AD5641, AD5660, AD5662 DACs. + AD5620, AD5621, AD5622, AD5640, AD5641, AD5660, AD5662 DACs + as well as Texas Instruments DAC081S101, DAC101S101, DAC121S101. To compile this driver as a module, choose M here: the module will be called ad5446. @@ -221,6 +222,15 @@ config DPOT_DAC To compile this driver as a module, choose M here: the module will be called dpot-dac. +config DS4424 + tristate "Maxim Integrated DS4422/DS4424 DAC driver" + depends on I2C + help + If you say yes here you get support for Maxim chips DS4422, DS4424. + + This driver can also be built as a module. If so, the module + will be called ds4424. + config LPC18XX_DAC tristate "NXP LPC18xx DAC driver" depends on ARCH_LPC18XX || COMPILE_TEST @@ -300,6 +310,16 @@ config STM32_DAC config STM32_DAC_CORE tristate +config TI_DAC082S085 + tristate "Texas Instruments 8/10/12-bit 2/4-channel DAC driver" + depends on SPI_MASTER + help + Driver for the Texas Instruments (formerly National Semiconductor) + DAC082S085, DAC102S085, DAC122S085, DAC084S085, DAC104S085 and + DAC124S085. + + If compiled as a module, it will be called ti-dac082s085. + config VF610_DAC tristate "Vybrid vf610 DAC driver" depends on OF diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 603587cc2f07..4785858e04cc 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_AD7303) += ad7303.o obj-$(CONFIG_AD8801) += ad8801.o obj-$(CONFIG_CIO_DAC) += cio-dac.o obj-$(CONFIG_DPOT_DAC) += dpot-dac.o +obj-$(CONFIG_DS4424) += ds4424.o obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o obj-$(CONFIG_LTC2632) += ltc2632.o obj-$(CONFIG_M62332) += m62332.o @@ -32,4 +33,5 @@ obj-$(CONFIG_MCP4725) += mcp4725.o obj-$(CONFIG_MCP4922) += mcp4922.o obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o obj-$(CONFIG_STM32_DAC) += stm32-dac.o +obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o obj-$(CONFIG_VF610_DAC) += vf610_dac.o diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index 3f9399c27869..bf4fc40ec84d 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -366,7 +366,6 @@ static int ad5064_write_raw(struct iio_dev *indio_dev, static const struct iio_info ad5064_info = { .read_raw = ad5064_read_raw, .write_raw = ad5064_write_raw, - .driver_module = THIS_MODULE, }; static const struct iio_chan_spec_ext_info ad5064_ext_info[] = { diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c index 8ba0e9c50176..0209316d5566 100644 --- a/drivers/iio/dac/ad5360.c +++ b/drivers/iio/dac/ad5360.c @@ -425,7 +425,6 @@ static const struct iio_info ad5360_info = { .read_raw = ad5360_read_raw, .write_raw = ad5360_write_raw, .attrs = &ad5360_attribute_group, - .driver_module = THIS_MODULE, }; static const char * const ad5360_vref_name[] = { diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 97d2c5111f43..845fd1c0fd9d 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -237,7 +237,6 @@ static int ad5380_read_raw(struct iio_dev *indio_dev, static const struct iio_info ad5380_info = { .read_raw = ad5380_read_raw, .write_raw = ad5380_write_raw, - .driver_module = THIS_MODULE, }; static struct iio_chan_spec_ext_info ad5380_ext_info[] = { diff --git a/drivers/iio/dac/ad5421.c b/drivers/iio/dac/ad5421.c index 559061ab1982..8e9633d8de67 100644 --- a/drivers/iio/dac/ad5421.c +++ b/drivers/iio/dac/ad5421.c @@ -465,7 +465,6 @@ static const struct iio_info ad5421_info = { .read_event_config = ad5421_read_event_config, .write_event_config = ad5421_write_event_config, .read_event_value = ad5421_read_event_value, - .driver_module = THIS_MODULE, }; static int ad5421_probe(struct spi_device *spi) diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index b555552a0d80..fd26a4272fc5 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -212,7 +212,6 @@ static int ad5446_write_raw(struct iio_dev *indio_dev, static const struct iio_info ad5446_info = { .read_raw = ad5446_read_raw, .write_raw = ad5446_write_raw, - .driver_module = THIS_MODULE, }; static int ad5446_probe(struct device *dev, const char *name, @@ -461,10 +460,22 @@ static const struct spi_device_id ad5446_spi_ids[] = { {"ad5660-2500", ID_AD5660_2500}, {"ad5660-1250", ID_AD5660_1250}, {"ad5662", ID_AD5662}, + {"dac081s101", ID_AD5300}, /* compatible Texas Instruments chips */ + {"dac101s101", ID_AD5310}, + {"dac121s101", ID_AD5320}, + {"dac7512", ID_AD5320}, {} }; MODULE_DEVICE_TABLE(spi, ad5446_spi_ids); +#ifdef CONFIG_OF +static const struct of_device_id ad5446_of_ids[] = { + { .compatible = "ti,dac7512" }, + { } +}; +MODULE_DEVICE_TABLE(of, ad5446_of_ids); +#endif + static int ad5446_spi_probe(struct spi_device *spi) { const struct spi_device_id *id = spi_get_device_id(spi); @@ -481,6 +492,7 @@ static int ad5446_spi_remove(struct spi_device *spi) static struct spi_driver ad5446_spi_driver = { .driver = { .name = "ad5446", + .of_match_table = of_match_ptr(ad5446_of_ids), }, .probe = ad5446_spi_probe, .remove = ad5446_spi_remove, diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c index 5f3202339420..317a74129932 100644 --- a/drivers/iio/dac/ad5449.c +++ b/drivers/iio/dac/ad5449.c @@ -193,7 +193,6 @@ static int ad5449_write_raw(struct iio_dev *indio_dev, static const struct iio_info ad5449_info = { .read_raw = ad5449_read_raw, .write_raw = ad5449_write_raw, - .driver_module = THIS_MODULE, }; #define AD5449_CHANNEL(chan, bits) { \ diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 712d86b4be09..d9037ea59168 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -232,7 +232,6 @@ static const struct iio_info ad5504_info = { .write_raw = ad5504_write_raw, .read_raw = ad5504_read_raw, .event_attrs = &ad5504_ev_attribute_group, - .driver_module = THIS_MODULE, }; static const struct iio_chan_spec_ext_info ad5504_ext_info[] = { diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c index 69bde5909854..9234c6a09a93 100644 --- a/drivers/iio/dac/ad5592r-base.c +++ b/drivers/iio/dac/ad5592r-base.c @@ -474,7 +474,6 @@ static const struct iio_info ad5592r_info = { .read_raw = ad5592r_read_raw, .write_raw = ad5592r_write_raw, .write_raw_get_fmt = ad5592r_write_raw_get_fmt, - .driver_module = THIS_MODULE, }; static ssize_t ad5592r_show_scale_available(struct iio_dev *iio_dev, diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c index 5489ec43b95d..13fdb4dfe356 100644 --- a/drivers/iio/dac/ad5624r_spi.c +++ b/drivers/iio/dac/ad5624r_spi.c @@ -149,7 +149,6 @@ static ssize_t ad5624r_write_dac_powerdown(struct iio_dev *indio_dev, static const struct iio_info ad5624r_info = { .write_raw = ad5624r_write_raw, .read_raw = ad5624r_read_raw, - .driver_module = THIS_MODULE, }; static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = { diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index d1d8450c19f6..20254df7f9c7 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -252,7 +252,6 @@ static int ad5686_write_raw(struct iio_dev *indio_dev, static const struct iio_info ad5686_info = { .read_raw = ad5686_read_raw, .write_raw = ad5686_write_raw, - .driver_module = THIS_MODULE, }; static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index 5f7968232564..2d03cc89ba50 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -417,7 +417,6 @@ static ssize_t ad5755_write_powerdown(struct iio_dev *indio_dev, uintptr_t priv, static const struct iio_info ad5755_info = { .read_raw = ad5755_read_raw, .write_raw = ad5755_write_raw, - .driver_module = THIS_MODULE, }; static const struct iio_chan_spec_ext_info ad5755_ext_info[] = { diff --git a/drivers/iio/dac/ad5761.c b/drivers/iio/dac/ad5761.c index d6510d6928b3..05017c8bbd00 100644 --- a/drivers/iio/dac/ad5761.c +++ b/drivers/iio/dac/ad5761.c @@ -251,7 +251,6 @@ static int ad5761_write_raw(struct iio_dev *indio_dev, static const struct iio_info ad5761_info = { .read_raw = &ad5761_read_raw, .write_raw = &ad5761_write_raw, - .driver_module = THIS_MODULE, }; #define AD5761_CHAN(_bits) { \ diff --git a/drivers/iio/dac/ad5764.c b/drivers/iio/dac/ad5764.c index 9a547bbf7d2b..033f20eca616 100644 --- a/drivers/iio/dac/ad5764.c +++ b/drivers/iio/dac/ad5764.c @@ -268,7 +268,6 @@ static int ad5764_read_raw(struct iio_dev *indio_dev, static const struct iio_info ad5764_info = { .read_raw = ad5764_read_raw, .write_raw = ad5764_write_raw, - .driver_module = THIS_MODULE, }; static int ad5764_probe(struct spi_device *spi) diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index 33e4ae5c42f8..7569bf6868c2 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -340,7 +340,6 @@ static int ad5791_write_raw(struct iio_dev *indio_dev, static const struct iio_info ad5791_info = { .read_raw = &ad5791_read_raw, .write_raw = &ad5791_write_raw, - .driver_module = THIS_MODULE, }; static int ad5791_probe(struct spi_device *spi) diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index 4b0f942b8914..8f3bd19b6dc3 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -161,7 +161,6 @@ static int ad7303_write_raw(struct iio_dev *indio_dev, static const struct iio_info ad7303_info = { .read_raw = ad7303_read_raw, .write_raw = ad7303_write_raw, - .driver_module = THIS_MODULE, }; static const struct iio_chan_spec_ext_info ad7303_ext_info[] = { diff --git a/drivers/iio/dac/ad8801.c b/drivers/iio/dac/ad8801.c index f06faa1aec09..aef5808c9865 100644 --- a/drivers/iio/dac/ad8801.c +++ b/drivers/iio/dac/ad8801.c @@ -92,7 +92,6 @@ static int ad8801_read_raw(struct iio_dev *indio_dev, static const struct iio_info ad8801_info = { .read_raw = ad8801_read_raw, .write_raw = ad8801_write_raw, - .driver_module = THIS_MODULE, }; #define AD8801_CHANNEL(chan) { \ diff --git a/drivers/iio/dac/cio-dac.c b/drivers/iio/dac/cio-dac.c index a8dffd938615..6898b0c79013 100644 --- a/drivers/iio/dac/cio-dac.c +++ b/drivers/iio/dac/cio-dac.c @@ -85,7 +85,6 @@ static int cio_dac_write_raw(struct iio_dev *indio_dev, } static const struct iio_info cio_dac_info = { - .driver_module = THIS_MODULE, .read_raw = cio_dac_read_raw, .write_raw = cio_dac_write_raw }; diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c index 960a2b430480..aaa2103d7c2b 100644 --- a/drivers/iio/dac/dpot-dac.c +++ b/drivers/iio/dac/dpot-dac.c @@ -128,7 +128,6 @@ static const struct iio_info dpot_dac_info = { .read_raw = dpot_dac_read_raw, .read_avail = dpot_dac_read_avail, .write_raw = dpot_dac_write_raw, - .driver_module = THIS_MODULE, }; static int dpot_dac_channel_max_ohms(struct iio_dev *indio_dev) diff --git a/drivers/iio/dac/ds4424.c b/drivers/iio/dac/ds4424.c new file mode 100644 index 000000000000..883a47562055 --- /dev/null +++ b/drivers/iio/dac/ds4424.c @@ -0,0 +1,341 @@ +/* + * Maxim Integrated + * 7-bit, Multi-Channel Sink/Source Current DAC Driver + * Copyright (C) 2017 Maxim Integrated + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/iio/driver.h> +#include <linux/iio/machine.h> +#include <linux/iio/consumer.h> + +#define DS4422_MAX_DAC_CHANNELS 2 +#define DS4424_MAX_DAC_CHANNELS 4 + +#define DS4424_DAC_ADDR(chan) ((chan) + 0xf8) +#define DS4424_SOURCE_I 1 +#define DS4424_SINK_I 0 + +#define DS4424_CHANNEL(chan) { \ + .type = IIO_CURRENT, \ + .indexed = 1, \ + .output = 1, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ +} + +/* + * DS4424 DAC control register 8 bits + * [7] 0: to sink; 1: to source + * [6:0] steps to sink/source + * bit[7] looks like a sign bit, but the value of the register is + * not a two's complement code considering the bit[6:0] is a absolute + * distance from the zero point. + */ +union ds4424_raw_data { + struct { + u8 dx:7; + u8 source_bit:1; + }; + u8 bits; +}; + +enum ds4424_device_ids { + ID_DS4422, + ID_DS4424, +}; + +struct ds4424_data { + struct i2c_client *client; + struct mutex lock; + uint8_t save[DS4424_MAX_DAC_CHANNELS]; + struct regulator *vcc_reg; + uint8_t raw[DS4424_MAX_DAC_CHANNELS]; +}; + +static const struct iio_chan_spec ds4424_channels[] = { + DS4424_CHANNEL(0), + DS4424_CHANNEL(1), + DS4424_CHANNEL(2), + DS4424_CHANNEL(3), +}; + +static int ds4424_get_value(struct iio_dev *indio_dev, + int *val, int channel) +{ + struct ds4424_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + ret = i2c_smbus_read_byte_data(data->client, DS4424_DAC_ADDR(channel)); + if (ret < 0) + goto fail; + + *val = ret; + +fail: + mutex_unlock(&data->lock); + return ret; +} + +static int ds4424_set_value(struct iio_dev *indio_dev, + int val, struct iio_chan_spec const *chan) +{ + struct ds4424_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->lock); + ret = i2c_smbus_write_byte_data(data->client, + DS4424_DAC_ADDR(chan->channel), val); + if (ret < 0) + goto fail; + + data->raw[chan->channel] = val; + +fail: + mutex_unlock(&data->lock); + return ret; +} + +static int ds4424_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + union ds4424_raw_data raw; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = ds4424_get_value(indio_dev, val, chan->channel); + if (ret < 0) { + pr_err("%s : ds4424_get_value returned %d\n", + __func__, ret); + return ret; + } + raw.bits = *val; + *val = raw.dx; + if (raw.source_bit == DS4424_SINK_I) + *val = -*val; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int ds4424_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + union ds4424_raw_data raw; + + if (val2 != 0) + return -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (val < S8_MIN || val > S8_MAX) + return -EINVAL; + + if (val > 0) { + raw.source_bit = DS4424_SOURCE_I; + raw.dx = val; + } else { + raw.source_bit = DS4424_SINK_I; + raw.dx = -val; + } + + return ds4424_set_value(indio_dev, raw.bits, chan); + + default: + return -EINVAL; + } +} + +static int ds4424_verify_chip(struct iio_dev *indio_dev) +{ + int ret, val; + + ret = ds4424_get_value(indio_dev, &val, DS4424_DAC_ADDR(0)); + if (ret < 0) + dev_err(&indio_dev->dev, + "%s failed. ret: %d\n", __func__, ret); + + return ret; +} + +static int __maybe_unused ds4424_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ds4424_data *data = iio_priv(indio_dev); + int ret = 0; + int i; + + for (i = 0; i < indio_dev->num_channels; i++) { + data->save[i] = data->raw[i]; + ret = ds4424_set_value(indio_dev, 0, + &indio_dev->channels[i]); + if (ret < 0) + return ret; + } + return ret; +} + +static int __maybe_unused ds4424_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ds4424_data *data = iio_priv(indio_dev); + int ret = 0; + int i; + + for (i = 0; i < indio_dev->num_channels; i++) { + ret = ds4424_set_value(indio_dev, data->save[i], + &indio_dev->channels[i]); + if (ret < 0) + return ret; + } + return ret; +} + +static SIMPLE_DEV_PM_OPS(ds4424_pm_ops, ds4424_suspend, ds4424_resume); + +static const struct iio_info ds4424_info = { + .read_raw = ds4424_read_raw, + .write_raw = ds4424_write_raw, +}; + +static int ds4424_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ds4424_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) { + dev_err(&client->dev, "iio dev alloc failed.\n"); + return -ENOMEM; + } + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + indio_dev->name = id->name; + indio_dev->dev.of_node = client->dev.of_node; + indio_dev->dev.parent = &client->dev; + + if (!client->dev.of_node) { + dev_err(&client->dev, + "Not found DT.\n"); + return -ENODEV; + } + + data->vcc_reg = devm_regulator_get(&client->dev, "vcc"); + if (IS_ERR(data->vcc_reg)) { + dev_err(&client->dev, + "Failed to get vcc-supply regulator. err: %ld\n", + PTR_ERR(data->vcc_reg)); + return PTR_ERR(data->vcc_reg); + } + + mutex_init(&data->lock); + ret = regulator_enable(data->vcc_reg); + if (ret < 0) { + dev_err(&client->dev, + "Unable to enable the regulator.\n"); + return ret; + } + + usleep_range(1000, 1200); + ret = ds4424_verify_chip(indio_dev); + if (ret < 0) + goto fail; + + switch (id->driver_data) { + case ID_DS4422: + indio_dev->num_channels = DS4422_MAX_DAC_CHANNELS; + break; + case ID_DS4424: + indio_dev->num_channels = DS4424_MAX_DAC_CHANNELS; + break; + default: + dev_err(&client->dev, + "ds4424: Invalid chip id.\n"); + ret = -ENXIO; + goto fail; + } + + indio_dev->channels = ds4424_channels; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ds4424_info; + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, + "iio_device_register failed. ret: %d\n", ret); + goto fail; + } + + return ret; + +fail: + regulator_disable(data->vcc_reg); + return ret; +} + +static int ds4424_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ds4424_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + regulator_disable(data->vcc_reg); + + return 0; +} + +static const struct i2c_device_id ds4424_id[] = { + { "ds4422", ID_DS4422 }, + { "ds4424", ID_DS4424 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, ds4424_id); + +static const struct of_device_id ds4424_of_match[] = { + { .compatible = "maxim,ds4422" }, + { .compatible = "maxim,ds4424" }, + { }, +}; + +MODULE_DEVICE_TABLE(of, ds4424_of_match); + +static struct i2c_driver ds4424_driver = { + .driver = { + .name = "ds4424", + .of_match_table = ds4424_of_match, + .pm = &ds4424_pm_ops, + }, + .probe = ds4424_probe, + .remove = ds4424_remove, + .id_table = ds4424_id, +}; +module_i2c_driver(ds4424_driver); + +MODULE_DESCRIPTION("Maxim DS4424 DAC Driver"); +MODULE_AUTHOR("Ismail H. Kose <ismail.kose@maximintegrated.com>"); +MODULE_AUTHOR("Vishal Sood <vishal.sood@maximintegrated.com>"); +MODULE_AUTHOR("David Jung <david.jung@maximintegrated.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/lpc18xx_dac.c b/drivers/iio/dac/lpc18xx_dac.c index 55d1456a059d..7036f77fdf23 100644 --- a/drivers/iio/dac/lpc18xx_dac.c +++ b/drivers/iio/dac/lpc18xx_dac.c @@ -103,7 +103,6 @@ static int lpc18xx_dac_write_raw(struct iio_dev *indio_dev, static const struct iio_info lpc18xx_dac_info = { .read_raw = lpc18xx_dac_read_raw, .write_raw = lpc18xx_dac_write_raw, - .driver_module = THIS_MODULE, }; static int lpc18xx_dac_probe(struct platform_device *pdev) diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index ac5e05f6eb8b..af2ddd0dd341 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -159,7 +159,6 @@ static ssize_t ltc2632_write_dac_powerdown(struct iio_dev *indio_dev, static const struct iio_info ltc2632_info = { .write_raw = ltc2632_write_raw, .read_raw = ltc2632_read_raw, - .driver_module = THIS_MODULE, }; static const struct iio_chan_spec_ext_info ltc2632_ext_info[] = { diff --git a/drivers/iio/dac/m62332.c b/drivers/iio/dac/m62332.c index 76e8b044b979..19031943dabe 100644 --- a/drivers/iio/dac/m62332.c +++ b/drivers/iio/dac/m62332.c @@ -174,7 +174,6 @@ static SIMPLE_DEV_PM_OPS(m62332_pm_ops, m62332_suspend, m62332_resume); static const struct iio_info m62332_info = { .read_raw = m62332_read_raw, .write_raw = m62332_write_raw, - .driver_module = THIS_MODULE, }; #define M62332_CHANNEL(chan) { \ diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c index 5507b3970b4b..1d853247a205 100644 --- a/drivers/iio/dac/max517.c +++ b/drivers/iio/dac/max517.c @@ -137,7 +137,6 @@ static SIMPLE_DEV_PM_OPS(max517_pm_ops, max517_suspend, max517_resume); static const struct iio_info max517_info = { .read_raw = max517_read_raw, .write_raw = max517_write_raw, - .driver_module = THIS_MODULE, }; #define MAX517_CHANNEL(chan) { \ diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c index 193fac3059a3..d0ecc1fdd8fc 100644 --- a/drivers/iio/dac/max5821.c +++ b/drivers/iio/dac/max5821.c @@ -300,7 +300,6 @@ static SIMPLE_DEV_PM_OPS(max5821_pm_ops, max5821_suspend, max5821_resume); static const struct iio_info max5821_info = { .read_raw = max5821_read_raw, .write_raw = max5821_write_raw, - .driver_module = THIS_MODULE, }; static int max5821_probe(struct i2c_client *client, diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 6ab1f23e5a79..afa856d10c26 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -363,7 +363,6 @@ static const struct iio_info mcp4725_info = { .read_raw = mcp4725_read_raw, .write_raw = mcp4725_write_raw, .attrs = &mcp4725_attribute_group, - .driver_module = THIS_MODULE, }; #ifdef CONFIG_OF diff --git a/drivers/iio/dac/mcp4922.c b/drivers/iio/dac/mcp4922.c index 3854d201a5d6..bf9aa3fc0534 100644 --- a/drivers/iio/dac/mcp4922.c +++ b/drivers/iio/dac/mcp4922.c @@ -119,7 +119,6 @@ static const struct iio_chan_spec mcp4922_channels[3][MCP4922_NUM_CHANNELS] = { static const struct iio_info mcp4922_info = { .read_raw = &mcp4922_read_raw, .write_raw = &mcp4922_write_raw, - .driver_module = THIS_MODULE, }; static int mcp4922_probe(struct spi_device *spi) diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c index c1864e8aa851..9ffab02bf9f9 100644 --- a/drivers/iio/dac/stm32-dac.c +++ b/drivers/iio/dac/stm32-dac.c @@ -156,7 +156,6 @@ static const struct iio_info stm32_dac_iio_info = { .read_raw = stm32_dac_read_raw, .write_raw = stm32_dac_write_raw, .debugfs_reg_access = stm32_dac_debugfs_reg_access, - .driver_module = THIS_MODULE, }; static const char * const stm32_dac_powerdown_modes[] = { diff --git a/drivers/iio/dac/ti-dac082s085.c b/drivers/iio/dac/ti-dac082s085.c new file mode 100644 index 000000000000..4e1e28339c84 --- /dev/null +++ b/drivers/iio/dac/ti-dac082s085.c @@ -0,0 +1,368 @@ +/* + * ti-dac082s085.c - Texas Instruments 8/10/12-bit 2/4-channel DAC driver + * + * Copyright (C) 2017 KUNBUS GmbH + * + * http://www.ti.com/lit/ds/symlink/dac082s085.pdf + * http://www.ti.com/lit/ds/symlink/dac102s085.pdf + * http://www.ti.com/lit/ds/symlink/dac122s085.pdf + * http://www.ti.com/lit/ds/symlink/dac084s085.pdf + * http://www.ti.com/lit/ds/symlink/dac104s085.pdf + * http://www.ti.com/lit/ds/symlink/dac124s085.pdf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License (version 2) as + * published by the Free Software Foundation. + */ + +#include <linux/iio/iio.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +enum { dual_8bit, dual_10bit, dual_12bit, quad_8bit, quad_10bit, quad_12bit }; + +struct ti_dac_spec { + u8 num_channels; + u8 resolution; +}; + +static const struct ti_dac_spec ti_dac_spec[] = { + [dual_8bit] = { .num_channels = 2, .resolution = 8 }, + [dual_10bit] = { .num_channels = 2, .resolution = 10 }, + [dual_12bit] = { .num_channels = 2, .resolution = 12 }, + [quad_8bit] = { .num_channels = 4, .resolution = 8 }, + [quad_10bit] = { .num_channels = 4, .resolution = 10 }, + [quad_12bit] = { .num_channels = 4, .resolution = 12 }, +}; + +/** + * struct ti_dac_chip - TI DAC chip + * @lock: protects write sequences + * @vref: regulator generating Vref + * @mesg: SPI message to perform a write + * @xfer: SPI transfer used by @mesg + * @val: cached value of each output + * @powerdown: whether the chip is powered down + * @powerdown_mode: selected by the user + * @resolution: resolution of the chip + * @buf: buffer for @xfer + */ +struct ti_dac_chip { + struct mutex lock; + struct regulator *vref; + struct spi_message mesg; + struct spi_transfer xfer; + u16 val[4]; + bool powerdown; + u8 powerdown_mode; + u8 resolution; + u8 buf[2] ____cacheline_aligned; +}; + +#define WRITE_NOT_UPDATE(chan) (0x00 | (chan) << 6) +#define WRITE_AND_UPDATE(chan) (0x10 | (chan) << 6) +#define WRITE_ALL_UPDATE 0x20 +#define POWERDOWN(mode) (0x30 | ((mode) + 1) << 6) + +static int ti_dac_cmd(struct ti_dac_chip *ti_dac, u8 cmd, u16 val) +{ + u8 shift = 12 - ti_dac->resolution; + + ti_dac->buf[0] = cmd | (val >> (8 - shift)); + ti_dac->buf[1] = (val << shift) & 0xff; + return spi_sync(ti_dac->mesg.spi, &ti_dac->mesg); +} + +static const char * const ti_dac_powerdown_modes[] = { + "2.5kohm_to_gnd", "100kohm_to_gnd", "three_state", +}; + +static int ti_dac_get_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ti_dac_chip *ti_dac = iio_priv(indio_dev); + + return ti_dac->powerdown_mode; +} + +static int ti_dac_set_powerdown_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct ti_dac_chip *ti_dac = iio_priv(indio_dev); + int ret = 0; + + if (ti_dac->powerdown_mode == mode) + return 0; + + mutex_lock(&ti_dac->lock); + if (ti_dac->powerdown) { + ret = ti_dac_cmd(ti_dac, POWERDOWN(mode), 0); + if (ret) + goto out; + } + ti_dac->powerdown_mode = mode; + +out: + mutex_unlock(&ti_dac->lock); + return ret; +} + +static const struct iio_enum ti_dac_powerdown_mode = { + .items = ti_dac_powerdown_modes, + .num_items = ARRAY_SIZE(ti_dac_powerdown_modes), + .get = ti_dac_get_powerdown_mode, + .set = ti_dac_set_powerdown_mode, +}; + +static ssize_t ti_dac_read_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ti_dac_chip *ti_dac = iio_priv(indio_dev); + + return sprintf(buf, "%d\n", ti_dac->powerdown); +} + +static ssize_t ti_dac_write_powerdown(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct ti_dac_chip *ti_dac = iio_priv(indio_dev); + bool powerdown; + int ret; + + ret = strtobool(buf, &powerdown); + if (ret) + return ret; + + if (ti_dac->powerdown == powerdown) + return len; + + mutex_lock(&ti_dac->lock); + if (powerdown) + ret = ti_dac_cmd(ti_dac, POWERDOWN(ti_dac->powerdown_mode), 0); + else + ret = ti_dac_cmd(ti_dac, WRITE_AND_UPDATE(0), ti_dac->val[0]); + if (!ret) + ti_dac->powerdown = powerdown; + mutex_unlock(&ti_dac->lock); + + return ret ? ret : len; +} + +static const struct iio_chan_spec_ext_info ti_dac_ext_info[] = { + { + .name = "powerdown", + .read = ti_dac_read_powerdown, + .write = ti_dac_write_powerdown, + .shared = IIO_SHARED_BY_TYPE, + }, + IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), + IIO_ENUM_AVAILABLE("powerdown_mode", &ti_dac_powerdown_mode), + { }, +}; + +#define TI_DAC_CHANNEL(chan) { \ + .type = IIO_VOLTAGE, \ + .channel = (chan), \ + .address = (chan), \ + .indexed = true, \ + .output = true, \ + .datasheet_name = (const char[]){ 'A' + (chan), 0 }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .ext_info = ti_dac_ext_info, \ +} + +static const struct iio_chan_spec ti_dac_channels[] = { + TI_DAC_CHANNEL(0), + TI_DAC_CHANNEL(1), + TI_DAC_CHANNEL(2), + TI_DAC_CHANNEL(3), +}; + +static int ti_dac_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ti_dac_chip *ti_dac = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + *val = ti_dac->val[chan->channel]; + ret = IIO_VAL_INT; + break; + + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(ti_dac->vref); + if (ret < 0) + return ret; + + *val = ret / 1000; + *val2 = ti_dac->resolution; + ret = IIO_VAL_FRACTIONAL_LOG2; + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static int ti_dac_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ti_dac_chip *ti_dac = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (ti_dac->val[chan->channel] == val) + return 0; + + if (val >= (1 << ti_dac->resolution) || val < 0) + return -EINVAL; + + if (ti_dac->powerdown) + return -EBUSY; + + mutex_lock(&ti_dac->lock); + ret = ti_dac_cmd(ti_dac, WRITE_AND_UPDATE(chan->channel), val); + if (!ret) + ti_dac->val[chan->channel] = val; + mutex_unlock(&ti_dac->lock); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static int ti_dac_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, long mask) +{ + return IIO_VAL_INT; +} + +static const struct iio_info ti_dac_info = { + .read_raw = ti_dac_read_raw, + .write_raw = ti_dac_write_raw, + .write_raw_get_fmt = ti_dac_write_raw_get_fmt, +}; + +static int ti_dac_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + const struct ti_dac_spec *spec; + struct ti_dac_chip *ti_dac; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*ti_dac)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->dev.parent = dev; + indio_dev->info = &ti_dac_info; + indio_dev->name = spi->modalias; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = ti_dac_channels; + spi_set_drvdata(spi, indio_dev); + + ti_dac = iio_priv(indio_dev); + ti_dac->xfer.tx_buf = &ti_dac->buf; + ti_dac->xfer.len = sizeof(ti_dac->buf); + spi_message_init_with_transfers(&ti_dac->mesg, &ti_dac->xfer, 1); + ti_dac->mesg.spi = spi; + + spec = &ti_dac_spec[spi_get_device_id(spi)->driver_data]; + indio_dev->num_channels = spec->num_channels; + ti_dac->resolution = spec->resolution; + + ti_dac->vref = devm_regulator_get(dev, "vref"); + if (IS_ERR(ti_dac->vref)) + return PTR_ERR(ti_dac->vref); + + ret = regulator_enable(ti_dac->vref); + if (ret < 0) + return ret; + + mutex_init(&ti_dac->lock); + + ret = ti_dac_cmd(ti_dac, WRITE_ALL_UPDATE, 0); + if (ret) { + dev_err(dev, "failed to initialize outputs to 0\n"); + goto err; + } + + ret = iio_device_register(indio_dev); + if (ret) + goto err; + + return 0; + +err: + mutex_destroy(&ti_dac->lock); + regulator_disable(ti_dac->vref); + return ret; +} + +static int ti_dac_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ti_dac_chip *ti_dac = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + mutex_destroy(&ti_dac->lock); + regulator_disable(ti_dac->vref); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id ti_dac_of_id[] = { + { .compatible = "ti,dac082s085" }, + { .compatible = "ti,dac102s085" }, + { .compatible = "ti,dac122s085" }, + { .compatible = "ti,dac084s085" }, + { .compatible = "ti,dac104s085" }, + { .compatible = "ti,dac124s085" }, + { } +}; +MODULE_DEVICE_TABLE(of, ti_dac_of_id); +#endif + +static const struct spi_device_id ti_dac_spi_id[] = { + { "dac082s085", dual_8bit }, + { "dac102s085", dual_10bit }, + { "dac122s085", dual_12bit }, + { "dac084s085", quad_8bit }, + { "dac104s085", quad_10bit }, + { "dac124s085", quad_12bit }, + { } +}; +MODULE_DEVICE_TABLE(spi, ti_dac_spi_id); + +static struct spi_driver ti_dac_driver = { + .driver = { + .name = "ti-dac082s085", + .of_match_table = of_match_ptr(ti_dac_of_id), + }, + .probe = ti_dac_probe, + .remove = ti_dac_remove, + .id_table = ti_dac_spi_id, +}; +module_spi_driver(ti_dac_driver); + +MODULE_AUTHOR("Lukas Wunner <lukas@wunner.de>"); +MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 2/4-channel DAC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dac/vf610_dac.c b/drivers/iio/dac/vf610_dac.c index c4ec7779b394..5dccdd16cab3 100644 --- a/drivers/iio/dac/vf610_dac.c +++ b/drivers/iio/dac/vf610_dac.c @@ -167,7 +167,6 @@ static int vf610_write_raw(struct iio_dev *indio_dev, } static const struct iio_info vf610_dac_iio_info = { - .driver_module = THIS_MODULE, .read_raw = &vf610_read_raw, .write_raw = &vf610_write_raw, }; |