forked from Minki/linux
b290f902b8
We have a couple of changes that apply to large sets of drivers, so I have grouped those to keep this short. There are a few late breaking fixes in here that can wait for the merge window. dt yaml conversions ------------------- * adi,ad7768-1 * adi,ad7949 * aspeed,ast2400 * cosmic,10001-adc * dlg,da9150-gpadc * fsl,imx25-gcq * fsl,imx7d-adc * fsl,vf610 * holt,hi8435 * marvell,berlin2-adc * motorola,cpcap-adc * nuvoton,nau7802 * nuvoton,npcm750-adc * nxp,lpc1850-adc * nxp,lpc3220 * sprd,sc2720-adc * st,stmpe-adc * ti,adc12138 * ti,ads1015 * ti,ads7950 * ti,twl4030-madc Features -------- * adxrs290 - Add triggered buffer support and expose data ready signal as a possible trigger. Includes updating bindings. - Add debugfs hooks for register access. * mlx90632 - Add a clear user interface to override the measured ambient temperature. * vl53l0x - Add IRQ support including dt bindings. Cleanups and minor fixes ------------------------ (groups) Replace mlock with local lock: * adf4350 * exynos-adc * fls-imx25-gcq * stm32-dac devm use to simplify probe error handling and remove functions. * adis16201 * adis16203 * adis16209 * adis16240 * adis16136 * adis16260 * adis16400 * adis16460 * adis16480 * adis library - drop unused adis_setup_buffer_and_trigger() of_match_ptr removal and incorrect ACPI binding removal of_match_ptr() rarely makes sense in an IIO driver as space saving is trivial and it breaks ACPI PRP0001 based instantiation. Mostly this series is about removing examples that get copied into new drivers. * ad2s1200 * ad5272 * ad5446 * ad5592r * ad5593r * ad5703 * ak8974 * ak8975 * ams-iaq-core * as3935 * atlas-sensor * ds1803 * hdc100x * htu21 * icp10100 * lmp91000 * pulsedlight * max30102 * max5432 * max5481 * mcp4018 * mcp4131 * mcp4531 * mcp4725 * ms5611 * ms5637 * si7020 * sgp30 * ti-dac082s085 * ti-dac5571 * tmp007 * tsys01 * vz89x * zpa2326 kernel-doc fixes * iio-core * ad7303 * ad7947 * adis16080 * adis16400 * iio_dummy_evgen * sgp30 Fixes for buffer alignment when passed to iio_push_to_buffers_with_timestamp() This is a long running effort. There are a few more drivers to come. * inv_mpu6050 * itg3200 * si1145 * st_lsm6dsx * ti-adc0832 * ti-adc12138 (not driver focused) * MAINTAINERS - Consolidate Analog Device IIO entries whilst removing Beniamin Bia. - Remove Hartmut Knaack as a listed IIO maintainer as he hasn't been active for a long time and people are getting intermitted bounces. * Add __printf() markings to a few functions that were missing them. * drop some rotted documentation from staging. * rework buffer sysfs file creation (precursor to multiple buffer support) (individual drivers) * ad5592r - Fix use of true for IIO_SHARED_BY_TYPE - Tidy up locking and indentation. * ad9467 - Improve error message on chip-id missmatch. - Use more appropriate error value if chip-id not recognised. * adis-library - Simplify burst mode handling. * adxrs290 - Make sure to switch device to standby mode during remove. * as73211 - Increase measurement timeout as seems some devices are slower. * bma180 - Fix use of true fo IIO_SHARED_BY_TYPE * exynos_adc - Update binding to require second interrut with touch screen. - Update binding to not require syscon on S5Pv210 * hmc5843 - Fix use of true for IIO_SHARED_BY_TYPE * inv_mpu6050 - Use regmap_noinc_read() for fifo reading. * palmas_gpadc - Use module_platform_driver() to remove boilerplate. * meson-saradc - style consistency fixes * rockchip_saradc - Allow compile testing with !ARM. * st_lsm6dsx - Changing scaling factor to use IIO_VAL_INT_PLUS_NANO to improve precision. - Fix an issue with unchecked return value. * stm32-adc - Fix a missing return introduced in dev_err_probe() patch earlier in cycle. * sx9310 - Prefer async mode for probe as paticularly slow startup. * vcnl4000 - Add missing interrupt property to dt binding. -----BEGIN PGP SIGNATURE----- iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAl9o+P4RHGppYzIzQGtl cm5lbC5vcmcACgkQVIU0mcT0FohJyRAAjEa6RhQkoJMn4QRp5gNj+BhWoYeM4yeL kjOTI9WIfi5acuJiQK/bscqPjVoa9xkp33Za6gRYLFHtxiY0M1c7IKxK7jdDIxyT ak5JkmhbklhpAqAaXtGhngBQ/pmctunefjfJbV6ltlZP4W+7aHhGgVEW74Qiagn0 +FuT3g23pcaCelm7uf1hynxkPUSvH0HlYPUdCptuYhE1YeFSsaCxSVl8DxIK0a52 MC/rVl4Psjn01mTtVTcwD/JlcO2LnoGC3kJThYguvY52mDqNZBYCseUKwB9ribAT AUj7X9rxbAsdQAoN/RF0umD6hxoTnEePf0B29NfdXM/6sn/nhMzWMpVPEPRPRN3B /g+WDBPCdOKs5mdyHgfSKhJko0p4jQ5dhGFbzBVA75Uq0yxxRrdXLI2D4rdBjUmF 6MXgAqaaAGRMq7qg3t3Kt9scR5J0CoPTY7oQvcetu/ZItFmaLEP4zM6wpYp1YXQg 4GnUKFmwSAb1/Ah4x4XWKkgtvAgJ0RjfebTifa0u/kqi4ZBjacdP7dd4ttEY/3pH Zg7OQuMZCF70tDkksSkdAWXEdTJuCZtzaCTdck3NS7yZv0d/MYq2aqDqmON1GbEU 18yTcGQYGvCJgUq2IlMOpFmlzCzA32+FS5/d2VIiL9xPtS0/weEmIJqY7ONvkQ+A VQq0c2u9Xck= =yrII -----END PGP SIGNATURE----- Merge tag 'iio-for-5.10b-take2' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next Jonathan writes: Second set of features and cleanups for IIO in 5.10 We have a couple of changes that apply to large sets of drivers, so I have grouped those to keep this short. There are a few late breaking fixes in here that can wait for the merge window. dt yaml conversions ------------------- * adi,ad7768-1 * adi,ad7949 * aspeed,ast2400 * cosmic,10001-adc * dlg,da9150-gpadc * fsl,imx25-gcq * fsl,imx7d-adc * fsl,vf610 * holt,hi8435 * marvell,berlin2-adc * motorola,cpcap-adc * nuvoton,nau7802 * nuvoton,npcm750-adc * nxp,lpc1850-adc * nxp,lpc3220 * sprd,sc2720-adc * st,stmpe-adc * ti,adc12138 * ti,ads1015 * ti,ads7950 * ti,twl4030-madc Features -------- * adxrs290 - Add triggered buffer support and expose data ready signal as a possible trigger. Includes updating bindings. - Add debugfs hooks for register access. * mlx90632 - Add a clear user interface to override the measured ambient temperature. * vl53l0x - Add IRQ support including dt bindings. Cleanups and minor fixes ------------------------ (groups) Replace mlock with local lock: * adf4350 * exynos-adc * fls-imx25-gcq * stm32-dac devm use to simplify probe error handling and remove functions. * adis16201 * adis16203 * adis16209 * adis16240 * adis16136 * adis16260 * adis16400 * adis16460 * adis16480 * adis library - drop unused adis_setup_buffer_and_trigger() of_match_ptr removal and incorrect ACPI binding removal of_match_ptr() rarely makes sense in an IIO driver as space saving is trivial and it breaks ACPI PRP0001 based instantiation. Mostly this series is about removing examples that get copied into new drivers. * ad2s1200 * ad5272 * ad5446 * ad5592r * ad5593r * ad5703 * ak8974 * ak8975 * ams-iaq-core * as3935 * atlas-sensor * ds1803 * hdc100x * htu21 * icp10100 * lmp91000 * pulsedlight * max30102 * max5432 * max5481 * mcp4018 * mcp4131 * mcp4531 * mcp4725 * ms5611 * ms5637 * si7020 * sgp30 * ti-dac082s085 * ti-dac5571 * tmp007 * tsys01 * vz89x * zpa2326 kernel-doc fixes * iio-core * ad7303 * ad7947 * adis16080 * adis16400 * iio_dummy_evgen * sgp30 Fixes for buffer alignment when passed to iio_push_to_buffers_with_timestamp() This is a long running effort. There are a few more drivers to come. * inv_mpu6050 * itg3200 * si1145 * st_lsm6dsx * ti-adc0832 * ti-adc12138 (not driver focused) * MAINTAINERS - Consolidate Analog Device IIO entries whilst removing Beniamin Bia. - Remove Hartmut Knaack as a listed IIO maintainer as he hasn't been active for a long time and people are getting intermitted bounces. * Add __printf() markings to a few functions that were missing them. * drop some rotted documentation from staging. * rework buffer sysfs file creation (precursor to multiple buffer support) (individual drivers) * ad5592r - Fix use of true for IIO_SHARED_BY_TYPE - Tidy up locking and indentation. * ad9467 - Improve error message on chip-id missmatch. - Use more appropriate error value if chip-id not recognised. * adis-library - Simplify burst mode handling. * adxrs290 - Make sure to switch device to standby mode during remove. * as73211 - Increase measurement timeout as seems some devices are slower. * bma180 - Fix use of true fo IIO_SHARED_BY_TYPE * exynos_adc - Update binding to require second interrut with touch screen. - Update binding to not require syscon on S5Pv210 * hmc5843 - Fix use of true for IIO_SHARED_BY_TYPE * inv_mpu6050 - Use regmap_noinc_read() for fifo reading. * palmas_gpadc - Use module_platform_driver() to remove boilerplate. * meson-saradc - style consistency fixes * rockchip_saradc - Allow compile testing with !ARM. * st_lsm6dsx - Changing scaling factor to use IIO_VAL_INT_PLUS_NANO to improve precision. - Fix an issue with unchecked return value. * stm32-adc - Fix a missing return introduced in dev_err_probe() patch earlier in cycle. * sx9310 - Prefer async mode for probe as paticularly slow startup. * vcnl4000 - Add missing interrupt property to dt binding. * tag 'iio-for-5.10b-take2' of https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio: (126 commits) dt-bindings: iio: vishay,vcnl4000: add interrupts property iio:imu:inv_mpu6050: Use regmap_noinc_read for fifo reads. iio:imu:inv_mpu6050 Fix dma and ts alignment and data leak issues. iio:adc:ti-adc12138 Fix alignment issue with timestamp iio:adc:ti-adc0832 Fix alignment issue with timestamp iio:imu:st_lsm6dsx Fix alignment and data leak issues iio:light:si1145: Fix timestamp alignment and prevent data leak. iio:gyro:itg3200: Fix timestamp alignment and prevent data leak. iio:imu:st_lsm6dsx: check st_lsm6dsx_shub_read_output return iio: adc: exynos_adc: Replace indio_dev->mlock with own device lock dt-bindings:iio:adc:holt,hi8435 yaml conversion dt-bindings:iio:adc:adi,ad7768-1 yaml conversion dt-bindings:iio:adc:adi,ad7949 yaml conversion dt-bindings:iio:adc:dlg,da9150-gpadc yaml conversion dt-bindings:iio:adc:motorola,cpcap-adc yaml conversion dt-bindings:iio:adc:nxp,lpc3220-adc yaml conversion dt-bindings:iio:adc:nxp,lpc1850-adc yaml conversion dt-bindings:iio:adc:fsl,imx25-gcq yaml conversion dt-bindings:iio:adc:fsl,imx7d-adc yaml conversion dt-bindings:iio:adc:ti,ads1015 yaml conversion ...
685 lines
14 KiB
C
685 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* AD5592R Digital <-> Analog converters driver
|
|
*
|
|
* Copyright 2014-2016 Analog Devices Inc.
|
|
* Author: Paul Cercueil <paul.cercueil@analog.com>
|
|
*/
|
|
|
|
#include <linux/bitops.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/iio/iio.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/of.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/gpio/consumer.h>
|
|
#include <linux/gpio/driver.h>
|
|
#include <linux/property.h>
|
|
|
|
#include <dt-bindings/iio/adi,ad5592r.h>
|
|
|
|
#include "ad5592r-base.h"
|
|
|
|
static int ad5592r_gpio_get(struct gpio_chip *chip, unsigned offset)
|
|
{
|
|
struct ad5592r_state *st = gpiochip_get_data(chip);
|
|
int ret = 0;
|
|
u8 val;
|
|
|
|
mutex_lock(&st->gpio_lock);
|
|
|
|
if (st->gpio_out & BIT(offset))
|
|
val = st->gpio_val;
|
|
else
|
|
ret = st->ops->gpio_read(st, &val);
|
|
|
|
mutex_unlock(&st->gpio_lock);
|
|
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return !!(val & BIT(offset));
|
|
}
|
|
|
|
static void ad5592r_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
|
{
|
|
struct ad5592r_state *st = gpiochip_get_data(chip);
|
|
|
|
mutex_lock(&st->gpio_lock);
|
|
|
|
if (value)
|
|
st->gpio_val |= BIT(offset);
|
|
else
|
|
st->gpio_val &= ~BIT(offset);
|
|
|
|
st->ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val);
|
|
|
|
mutex_unlock(&st->gpio_lock);
|
|
}
|
|
|
|
static int ad5592r_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
|
{
|
|
struct ad5592r_state *st = gpiochip_get_data(chip);
|
|
int ret;
|
|
|
|
mutex_lock(&st->gpio_lock);
|
|
|
|
st->gpio_out &= ~BIT(offset);
|
|
st->gpio_in |= BIT(offset);
|
|
|
|
ret = st->ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out);
|
|
if (ret < 0)
|
|
goto err_unlock;
|
|
|
|
ret = st->ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in);
|
|
|
|
err_unlock:
|
|
mutex_unlock(&st->gpio_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ad5592r_gpio_direction_output(struct gpio_chip *chip,
|
|
unsigned offset, int value)
|
|
{
|
|
struct ad5592r_state *st = gpiochip_get_data(chip);
|
|
int ret;
|
|
|
|
mutex_lock(&st->gpio_lock);
|
|
|
|
if (value)
|
|
st->gpio_val |= BIT(offset);
|
|
else
|
|
st->gpio_val &= ~BIT(offset);
|
|
|
|
st->gpio_in &= ~BIT(offset);
|
|
st->gpio_out |= BIT(offset);
|
|
|
|
ret = st->ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val);
|
|
if (ret < 0)
|
|
goto err_unlock;
|
|
|
|
ret = st->ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out);
|
|
if (ret < 0)
|
|
goto err_unlock;
|
|
|
|
ret = st->ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in);
|
|
|
|
err_unlock:
|
|
mutex_unlock(&st->gpio_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int ad5592r_gpio_request(struct gpio_chip *chip, unsigned offset)
|
|
{
|
|
struct ad5592r_state *st = gpiochip_get_data(chip);
|
|
|
|
if (!(st->gpio_map & BIT(offset))) {
|
|
dev_err(st->dev, "GPIO %d is reserved by alternate function\n",
|
|
offset);
|
|
return -ENODEV;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ad5592r_gpio_init(struct ad5592r_state *st)
|
|
{
|
|
if (!st->gpio_map)
|
|
return 0;
|
|
|
|
st->gpiochip.label = dev_name(st->dev);
|
|
st->gpiochip.base = -1;
|
|
st->gpiochip.ngpio = 8;
|
|
st->gpiochip.parent = st->dev;
|
|
st->gpiochip.can_sleep = true;
|
|
st->gpiochip.direction_input = ad5592r_gpio_direction_input;
|
|
st->gpiochip.direction_output = ad5592r_gpio_direction_output;
|
|
st->gpiochip.get = ad5592r_gpio_get;
|
|
st->gpiochip.set = ad5592r_gpio_set;
|
|
st->gpiochip.request = ad5592r_gpio_request;
|
|
st->gpiochip.owner = THIS_MODULE;
|
|
|
|
mutex_init(&st->gpio_lock);
|
|
|
|
return gpiochip_add_data(&st->gpiochip, st);
|
|
}
|
|
|
|
static void ad5592r_gpio_cleanup(struct ad5592r_state *st)
|
|
{
|
|
if (st->gpio_map)
|
|
gpiochip_remove(&st->gpiochip);
|
|
}
|
|
|
|
static int ad5592r_reset(struct ad5592r_state *st)
|
|
{
|
|
struct gpio_desc *gpio;
|
|
|
|
gpio = devm_gpiod_get_optional(st->dev, "reset", GPIOD_OUT_LOW);
|
|
if (IS_ERR(gpio))
|
|
return PTR_ERR(gpio);
|
|
|
|
if (gpio) {
|
|
udelay(1);
|
|
gpiod_set_value(gpio, 1);
|
|
} else {
|
|
mutex_lock(&st->lock);
|
|
/* Writing this magic value resets the device */
|
|
st->ops->reg_write(st, AD5592R_REG_RESET, 0xdac);
|
|
mutex_unlock(&st->lock);
|
|
}
|
|
|
|
udelay(250);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ad5592r_get_vref(struct ad5592r_state *st)
|
|
{
|
|
int ret;
|
|
|
|
if (st->reg) {
|
|
ret = regulator_get_voltage(st->reg);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
return ret / 1000;
|
|
} else {
|
|
return 2500;
|
|
}
|
|
}
|
|
|
|
static int ad5592r_set_channel_modes(struct ad5592r_state *st)
|
|
{
|
|
const struct ad5592r_rw_ops *ops = st->ops;
|
|
int ret;
|
|
unsigned i;
|
|
u8 pulldown = 0, tristate = 0, dac = 0, adc = 0;
|
|
u16 read_back;
|
|
|
|
for (i = 0; i < st->num_channels; i++) {
|
|
switch (st->channel_modes[i]) {
|
|
case CH_MODE_DAC:
|
|
dac |= BIT(i);
|
|
break;
|
|
|
|
case CH_MODE_ADC:
|
|
adc |= BIT(i);
|
|
break;
|
|
|
|
case CH_MODE_DAC_AND_ADC:
|
|
dac |= BIT(i);
|
|
adc |= BIT(i);
|
|
break;
|
|
|
|
case CH_MODE_GPIO:
|
|
st->gpio_map |= BIT(i);
|
|
st->gpio_in |= BIT(i); /* Default to input */
|
|
break;
|
|
|
|
case CH_MODE_UNUSED:
|
|
default:
|
|
switch (st->channel_offstate[i]) {
|
|
case CH_OFFSTATE_OUT_TRISTATE:
|
|
tristate |= BIT(i);
|
|
break;
|
|
|
|
case CH_OFFSTATE_OUT_LOW:
|
|
st->gpio_out |= BIT(i);
|
|
break;
|
|
|
|
case CH_OFFSTATE_OUT_HIGH:
|
|
st->gpio_out |= BIT(i);
|
|
st->gpio_val |= BIT(i);
|
|
break;
|
|
|
|
case CH_OFFSTATE_PULLDOWN:
|
|
default:
|
|
pulldown |= BIT(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
/* Pull down unused pins to GND */
|
|
ret = ops->reg_write(st, AD5592R_REG_PULLDOWN, pulldown);
|
|
if (ret)
|
|
goto err_unlock;
|
|
|
|
ret = ops->reg_write(st, AD5592R_REG_TRISTATE, tristate);
|
|
if (ret)
|
|
goto err_unlock;
|
|
|
|
/* Configure pins that we use */
|
|
ret = ops->reg_write(st, AD5592R_REG_DAC_EN, dac);
|
|
if (ret)
|
|
goto err_unlock;
|
|
|
|
ret = ops->reg_write(st, AD5592R_REG_ADC_EN, adc);
|
|
if (ret)
|
|
goto err_unlock;
|
|
|
|
ret = ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val);
|
|
if (ret)
|
|
goto err_unlock;
|
|
|
|
ret = ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out);
|
|
if (ret)
|
|
goto err_unlock;
|
|
|
|
ret = ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in);
|
|
if (ret)
|
|
goto err_unlock;
|
|
|
|
/* Verify that we can read back at least one register */
|
|
ret = ops->reg_read(st, AD5592R_REG_ADC_EN, &read_back);
|
|
if (!ret && (read_back & 0xff) != adc)
|
|
ret = -EIO;
|
|
|
|
err_unlock:
|
|
mutex_unlock(&st->lock);
|
|
return ret;
|
|
}
|
|
|
|
static int ad5592r_reset_channel_modes(struct ad5592r_state *st)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(st->channel_modes); i++)
|
|
st->channel_modes[i] = CH_MODE_UNUSED;
|
|
|
|
return ad5592r_set_channel_modes(st);
|
|
}
|
|
|
|
static int ad5592r_write_raw(struct iio_dev *iio_dev,
|
|
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
|
{
|
|
struct ad5592r_state *st = iio_priv(iio_dev);
|
|
int ret;
|
|
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
|
|
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
|
return -EINVAL;
|
|
|
|
if (!chan->output)
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&st->lock);
|
|
ret = st->ops->write_dac(st, chan->channel, val);
|
|
if (!ret)
|
|
st->cached_dac[chan->channel] = val;
|
|
mutex_unlock(&st->lock);
|
|
return ret;
|
|
case IIO_CHAN_INFO_SCALE:
|
|
if (chan->type == IIO_VOLTAGE) {
|
|
bool gain;
|
|
|
|
if (val == st->scale_avail[0][0] &&
|
|
val2 == st->scale_avail[0][1])
|
|
gain = false;
|
|
else if (val == st->scale_avail[1][0] &&
|
|
val2 == st->scale_avail[1][1])
|
|
gain = true;
|
|
else
|
|
return -EINVAL;
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
ret = st->ops->reg_read(st, AD5592R_REG_CTRL,
|
|
&st->cached_gp_ctrl);
|
|
if (ret < 0) {
|
|
mutex_unlock(&st->lock);
|
|
return ret;
|
|
}
|
|
|
|
if (chan->output) {
|
|
if (gain)
|
|
st->cached_gp_ctrl |=
|
|
AD5592R_REG_CTRL_DAC_RANGE;
|
|
else
|
|
st->cached_gp_ctrl &=
|
|
~AD5592R_REG_CTRL_DAC_RANGE;
|
|
} else {
|
|
if (gain)
|
|
st->cached_gp_ctrl |=
|
|
AD5592R_REG_CTRL_ADC_RANGE;
|
|
else
|
|
st->cached_gp_ctrl &=
|
|
~AD5592R_REG_CTRL_ADC_RANGE;
|
|
}
|
|
|
|
ret = st->ops->reg_write(st, AD5592R_REG_CTRL,
|
|
st->cached_gp_ctrl);
|
|
mutex_unlock(&st->lock);
|
|
|
|
return ret;
|
|
}
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ad5592r_read_raw(struct iio_dev *iio_dev,
|
|
struct iio_chan_spec const *chan,
|
|
int *val, int *val2, long m)
|
|
{
|
|
struct ad5592r_state *st = iio_priv(iio_dev);
|
|
u16 read_val;
|
|
int ret, mult;
|
|
|
|
switch (m) {
|
|
case IIO_CHAN_INFO_RAW:
|
|
if (!chan->output) {
|
|
mutex_lock(&st->lock);
|
|
ret = st->ops->read_adc(st, chan->channel, &read_val);
|
|
mutex_unlock(&st->lock);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if ((read_val >> 12 & 0x7) != (chan->channel & 0x7)) {
|
|
dev_err(st->dev, "Error while reading channel %u\n",
|
|
chan->channel);
|
|
return -EIO;
|
|
}
|
|
|
|
read_val &= GENMASK(11, 0);
|
|
|
|
} else {
|
|
mutex_lock(&st->lock);
|
|
read_val = st->cached_dac[chan->channel];
|
|
mutex_unlock(&st->lock);
|
|
}
|
|
|
|
dev_dbg(st->dev, "Channel %u read: 0x%04hX\n",
|
|
chan->channel, read_val);
|
|
|
|
*val = (int) read_val;
|
|
return IIO_VAL_INT;
|
|
case IIO_CHAN_INFO_SCALE:
|
|
*val = ad5592r_get_vref(st);
|
|
|
|
if (chan->type == IIO_TEMP) {
|
|
s64 tmp = *val * (3767897513LL / 25LL);
|
|
*val = div_s64_rem(tmp, 1000000000LL, val2);
|
|
|
|
return IIO_VAL_INT_PLUS_MICRO;
|
|
}
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
if (chan->output)
|
|
mult = !!(st->cached_gp_ctrl &
|
|
AD5592R_REG_CTRL_DAC_RANGE);
|
|
else
|
|
mult = !!(st->cached_gp_ctrl &
|
|
AD5592R_REG_CTRL_ADC_RANGE);
|
|
|
|
mutex_unlock(&st->lock);
|
|
|
|
*val *= ++mult;
|
|
|
|
*val2 = chan->scan_type.realbits;
|
|
|
|
return IIO_VAL_FRACTIONAL_LOG2;
|
|
case IIO_CHAN_INFO_OFFSET:
|
|
ret = ad5592r_get_vref(st);
|
|
|
|
mutex_lock(&st->lock);
|
|
|
|
if (st->cached_gp_ctrl & AD5592R_REG_CTRL_ADC_RANGE)
|
|
*val = (-34365 * 25) / ret;
|
|
else
|
|
*val = (-75365 * 25) / ret;
|
|
|
|
mutex_unlock(&st->lock);
|
|
|
|
return IIO_VAL_INT;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int ad5592r_write_raw_get_fmt(struct iio_dev *indio_dev,
|
|
struct iio_chan_spec const *chan, long mask)
|
|
{
|
|
switch (mask) {
|
|
case IIO_CHAN_INFO_SCALE:
|
|
return IIO_VAL_INT_PLUS_NANO;
|
|
|
|
default:
|
|
return IIO_VAL_INT_PLUS_MICRO;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
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,
|
|
};
|
|
|
|
static ssize_t ad5592r_show_scale_available(struct iio_dev *iio_dev,
|
|
uintptr_t private,
|
|
const struct iio_chan_spec *chan,
|
|
char *buf)
|
|
{
|
|
struct ad5592r_state *st = iio_priv(iio_dev);
|
|
|
|
return sprintf(buf, "%d.%09u %d.%09u\n",
|
|
st->scale_avail[0][0], st->scale_avail[0][1],
|
|
st->scale_avail[1][0], st->scale_avail[1][1]);
|
|
}
|
|
|
|
static const struct iio_chan_spec_ext_info ad5592r_ext_info[] = {
|
|
{
|
|
.name = "scale_available",
|
|
.read = ad5592r_show_scale_available,
|
|
.shared = IIO_SHARED_BY_TYPE,
|
|
},
|
|
{},
|
|
};
|
|
|
|
static void ad5592r_setup_channel(struct iio_dev *iio_dev,
|
|
struct iio_chan_spec *chan, bool output, unsigned id)
|
|
{
|
|
chan->type = IIO_VOLTAGE;
|
|
chan->indexed = 1;
|
|
chan->output = output;
|
|
chan->channel = id;
|
|
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
|
chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
|
|
chan->scan_type.sign = 'u';
|
|
chan->scan_type.realbits = 12;
|
|
chan->scan_type.storagebits = 16;
|
|
chan->ext_info = ad5592r_ext_info;
|
|
}
|
|
|
|
static int ad5592r_alloc_channels(struct iio_dev *iio_dev)
|
|
{
|
|
struct ad5592r_state *st = iio_priv(iio_dev);
|
|
unsigned i, curr_channel = 0,
|
|
num_channels = st->num_channels;
|
|
struct iio_chan_spec *channels;
|
|
struct fwnode_handle *child;
|
|
u32 reg, tmp;
|
|
int ret;
|
|
|
|
device_for_each_child_node(st->dev, child) {
|
|
ret = fwnode_property_read_u32(child, "reg", ®);
|
|
if (ret || reg >= ARRAY_SIZE(st->channel_modes))
|
|
continue;
|
|
|
|
ret = fwnode_property_read_u32(child, "adi,mode", &tmp);
|
|
if (!ret)
|
|
st->channel_modes[reg] = tmp;
|
|
|
|
fwnode_property_read_u32(child, "adi,off-state", &tmp);
|
|
if (!ret)
|
|
st->channel_offstate[reg] = tmp;
|
|
}
|
|
|
|
channels = devm_kcalloc(st->dev,
|
|
1 + 2 * num_channels, sizeof(*channels),
|
|
GFP_KERNEL);
|
|
if (!channels)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < num_channels; i++) {
|
|
switch (st->channel_modes[i]) {
|
|
case CH_MODE_DAC:
|
|
ad5592r_setup_channel(iio_dev, &channels[curr_channel],
|
|
true, i);
|
|
curr_channel++;
|
|
break;
|
|
|
|
case CH_MODE_ADC:
|
|
ad5592r_setup_channel(iio_dev, &channels[curr_channel],
|
|
false, i);
|
|
curr_channel++;
|
|
break;
|
|
|
|
case CH_MODE_DAC_AND_ADC:
|
|
ad5592r_setup_channel(iio_dev, &channels[curr_channel],
|
|
true, i);
|
|
curr_channel++;
|
|
ad5592r_setup_channel(iio_dev, &channels[curr_channel],
|
|
false, i);
|
|
curr_channel++;
|
|
break;
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
}
|
|
|
|
channels[curr_channel].type = IIO_TEMP;
|
|
channels[curr_channel].channel = 8;
|
|
channels[curr_channel].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
|
BIT(IIO_CHAN_INFO_SCALE) |
|
|
BIT(IIO_CHAN_INFO_OFFSET);
|
|
curr_channel++;
|
|
|
|
iio_dev->num_channels = curr_channel;
|
|
iio_dev->channels = channels;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ad5592r_init_scales(struct ad5592r_state *st, int vref_mV)
|
|
{
|
|
s64 tmp = (s64)vref_mV * 1000000000LL >> 12;
|
|
|
|
st->scale_avail[0][0] =
|
|
div_s64_rem(tmp, 1000000000LL, &st->scale_avail[0][1]);
|
|
st->scale_avail[1][0] =
|
|
div_s64_rem(tmp * 2, 1000000000LL, &st->scale_avail[1][1]);
|
|
}
|
|
|
|
int ad5592r_probe(struct device *dev, const char *name,
|
|
const struct ad5592r_rw_ops *ops)
|
|
{
|
|
struct iio_dev *iio_dev;
|
|
struct ad5592r_state *st;
|
|
int ret;
|
|
|
|
iio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
|
if (!iio_dev)
|
|
return -ENOMEM;
|
|
|
|
st = iio_priv(iio_dev);
|
|
st->dev = dev;
|
|
st->ops = ops;
|
|
st->num_channels = 8;
|
|
dev_set_drvdata(dev, iio_dev);
|
|
|
|
st->reg = devm_regulator_get_optional(dev, "vref");
|
|
if (IS_ERR(st->reg)) {
|
|
if ((PTR_ERR(st->reg) != -ENODEV) && dev->of_node)
|
|
return PTR_ERR(st->reg);
|
|
|
|
st->reg = NULL;
|
|
} else {
|
|
ret = regulator_enable(st->reg);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
iio_dev->name = name;
|
|
iio_dev->info = &ad5592r_info;
|
|
iio_dev->modes = INDIO_DIRECT_MODE;
|
|
|
|
mutex_init(&st->lock);
|
|
|
|
ad5592r_init_scales(st, ad5592r_get_vref(st));
|
|
|
|
ret = ad5592r_reset(st);
|
|
if (ret)
|
|
goto error_disable_reg;
|
|
|
|
ret = ops->reg_write(st, AD5592R_REG_PD,
|
|
(st->reg == NULL) ? AD5592R_REG_PD_EN_REF : 0);
|
|
if (ret)
|
|
goto error_disable_reg;
|
|
|
|
ret = ad5592r_alloc_channels(iio_dev);
|
|
if (ret)
|
|
goto error_disable_reg;
|
|
|
|
ret = ad5592r_set_channel_modes(st);
|
|
if (ret)
|
|
goto error_reset_ch_modes;
|
|
|
|
ret = iio_device_register(iio_dev);
|
|
if (ret)
|
|
goto error_reset_ch_modes;
|
|
|
|
ret = ad5592r_gpio_init(st);
|
|
if (ret)
|
|
goto error_dev_unregister;
|
|
|
|
return 0;
|
|
|
|
error_dev_unregister:
|
|
iio_device_unregister(iio_dev);
|
|
|
|
error_reset_ch_modes:
|
|
ad5592r_reset_channel_modes(st);
|
|
|
|
error_disable_reg:
|
|
if (st->reg)
|
|
regulator_disable(st->reg);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ad5592r_probe);
|
|
|
|
int ad5592r_remove(struct device *dev)
|
|
{
|
|
struct iio_dev *iio_dev = dev_get_drvdata(dev);
|
|
struct ad5592r_state *st = iio_priv(iio_dev);
|
|
|
|
iio_device_unregister(iio_dev);
|
|
ad5592r_reset_channel_modes(st);
|
|
ad5592r_gpio_cleanup(st);
|
|
|
|
if (st->reg)
|
|
regulator_disable(st->reg);
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(ad5592r_remove);
|
|
|
|
MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>");
|
|
MODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters");
|
|
MODULE_LICENSE("GPL v2");
|