Fourth set of IIO new device support, features and cleanups for the 4.12 cycle

New device support
 * max1117, 1118 and 1119
   - new ADC driver
 * max9611
   - new ADC driver
 * pm8xxx hk/xoadc
   - new driver with some shared features broken out from the SPMI vadc.
 * sun4i-gpadc
   - A33 thermal sensor support (with associated rework)
 * stm32-dac
   - new driver and bindings
 * stm32 trigger
   - enable support of quadrature encoder device and counter modes
 
 Features
 * apds9960
   - use the runtime pm for normal suspend
 * stm32-adc
   - add opition to sest resolution via devicetree
 * xoadc
   - augment DT bindings to deal with some weird mux cases
 
 Cleanups
 * ad5933
   - protect direct mode using claim and release helpers
 * ade7759
   - S_IRUGO and friends to octal in two goes
 * adis16203
   - drop unnecessary brackets
 * hid-sensor
   - fix unbalanced pm_runtieme_enable error when probing after remove
 * lsm6dsx
   - use actual part numbers for device name when known
   - simplify data read pin parsing
 * mpu3050
   - avoid double reporting errors
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCAAvFiEEbilms4eEBlKRJoGxVIU0mcT0FogFAljxPf4RHGppYzIzQGtl
 cm5lbC5vcmcACgkQVIU0mcT0FojZfA//UUaFcaEiMJjoUwBQlOM49nueGcAlOOOz
 blMYe6JIIm3DYjEJnNhlA1Wn83JVitGQopQ4Tt9yrsxc3urt6GiAiRCK1Lwuyauw
 l1IrdsV9tzbPrauL8UxZYygIYHZx6ltKnpbSHCrXaA2BOTIYzgIBDGmRv+MPutwW
 iaW+dKJVW5G/5VV0aIaJc2w5wXujFcWkZoiTJgZBHhGJJ5AY6zyA3tBEOIY7K3KO
 z4EIY6V9pvVaSgvuWGVWQjtYwjqSSqp5J5Ih7ATpp4hPwi+XIudWqaiq1y2rHQKM
 Y/wqjz+W/NyAJGSFEnub7gE4wGauQtxMiOLTenrfDWkFxF0YRkunylY8Kwcgvmhh
 +TdHhazlfIHca5z/MJ0dg/4UPj75o2CtB+N1NyNMjx/K7Juu2MmcKNShLa/l68jT
 rYEuNQUyhTvNuhlYPTcYmGbL7VbyXJzgbVbk9/Dd1hcS+p/oqn18ulGsWxcj5kEI
 ME+vHOakq02GTAQEG2FLvE604fTt/dHyXdYFsDY0RCm9ZSURvRqFLYpkxyrQFe+P
 yhC9b6ouW+dPGez0RmwPZTWnH0E6eN5b13yR3ILewj/NUVA6cjlm0inhnck1IwFe
 NWVQiMz1a2DsXfk5VjBQVlsipHxW9rs8SArdWnndDpbSErQJT/KrCxantmkNciYw
 oXf1eFCo/MQ=
 =zHqs
 -----END PGP SIGNATURE-----

Merge tag 'iio-for-4.12d' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next

Jonathan writes:

Fourth set of IIO new device support, features and cleanups for the 4.12 cycle

New device support
* max1117, 1118 and 1119
  - new ADC driver
* max9611
  - new ADC driver
* pm8xxx hk/xoadc
  - new driver with some shared features broken out from the SPMI vadc.
* sun4i-gpadc
  - A33 thermal sensor support (with associated rework)
* stm32-dac
  - new driver and bindings
* stm32 trigger
  - enable support of quadrature encoder device and counter modes

Features
* apds9960
  - use the runtime pm for normal suspend
* stm32-adc
  - add opition to sest resolution via devicetree
* xoadc
  - augment DT bindings to deal with some weird mux cases

Cleanups
* ad5933
  - protect direct mode using claim and release helpers
* ade7759
  - S_IRUGO and friends to octal in two goes
* adis16203
  - drop unnecessary brackets
* hid-sensor
  - fix unbalanced pm_runtieme_enable error when probing after remove
* lsm6dsx
  - use actual part numbers for device name when known
  - simplify data read pin parsing
* mpu3050
  - avoid double reporting errors
This commit is contained in:
Greg Kroah-Hartman 2017-04-18 17:13:31 +02:00
commit d47e538235
35 changed files with 3709 additions and 444 deletions

View file

@ -379,6 +379,18 @@ config MAX11100
To compile this driver as a module, choose M here: the module will be
called max11100.
config MAX1118
tristate "Maxim max1117/max1118/max1119 ADCs driver"
depends on SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Maxim max1117/max1118/max1119
8-bit, dual-channel ADCs.
To compile this driver as a module, choose M here: the module will be
called max1118.
config MAX1363
tristate "Maxim max1363 ADC driver"
depends on I2C
@ -398,6 +410,16 @@ config MAX1363
To compile this driver as a module, choose M here: the module will be
called max1363.
config MAX9611
tristate "Maxim max9611/max9612 ADC driver"
depends on I2C
help
Say yes here to build support for Maxim max9611/max9612 current sense
amplifier with 12-bits ADC interface.
To compile this driver as a module, choose M here: the module will be
called max9611.
config MCP320X
tristate "Microchip Technology MCP3x01/02/04/08"
depends on SPI
@ -486,6 +508,20 @@ config PALMAS_GPADC
is used in smartphones and tablets and supports a 16 channel
general purpose ADC.
config QCOM_VADC_COMMON
tristate
config QCOM_PM8XXX_XOADC
tristate "Qualcomm SSBI PM8xxx PMIC XOADCs"
depends on MFD_PM8XXX
select QCOM_VADC_COMMON
help
ADC driver for the XOADC portions of the Qualcomm PM8xxx PMICs
using SSBI transport: PM8018, PM8038, PM8058, PM8921.
To compile this driver as a module, choose M here: the module
will be called qcom-pm8xxx-xoadc.
config QCOM_SPMI_IADC
tristate "Qualcomm SPMI PMIC current ADC"
depends on SPMI
@ -504,6 +540,7 @@ config QCOM_SPMI_VADC
tristate "Qualcomm SPMI PMIC voltage ADC"
depends on SPMI
select REGMAP_SPMI
select QCOM_VADC_COMMON
help
This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip.
@ -594,7 +631,7 @@ config STX104
config SUN4I_GPADC
tristate "Support for the Allwinner SoCs GPADC"
depends on IIO
depends on MFD_SUN4I_GPADC
depends on MFD_SUN4I_GPADC || MACH_SUN8I
depends on THERMAL || !THERMAL_OF
help
Say yes here to build support for Allwinner (A10, A13 and A31) SoCs

View file

@ -37,7 +37,9 @@ obj-$(CONFIG_LTC2485) += ltc2485.o
obj-$(CONFIG_LTC2497) += ltc2497.o
obj-$(CONFIG_MAX1027) += max1027.o
obj-$(CONFIG_MAX11100) += max11100.o
obj-$(CONFIG_MAX1118) += max1118.o
obj-$(CONFIG_MAX1363) += max1363.o
obj-$(CONFIG_MAX9611) += max9611.o
obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
@ -47,7 +49,9 @@ obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
obj-$(CONFIG_NAU7802) += nau7802.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o

307
drivers/iio/adc/max1118.c Normal file
View file

@ -0,0 +1,307 @@
/*
* MAX1117/MAX1118/MAX1119 8-bit, dual-channel ADCs driver
*
* Copyright (c) 2017 Akinobu Mita <akinobu.mita@gmail.com>
*
* 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.
*
* Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1117-MAX1119.pdf
*
* SPI interface connections
*
* SPI MAXIM
* Master Direction MAX1117/8/9
* ------ --------- -----------
* nCS --> CNVST
* SCK --> SCLK
* MISO <-- DOUT
* ------ --------- -----------
*/
#include <linux/module.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/regulator/consumer.h>
enum max1118_id {
max1117,
max1118,
max1119,
};
struct max1118 {
struct spi_device *spi;
struct mutex lock;
struct regulator *reg;
u8 data ____cacheline_aligned;
};
#define MAX1118_CHANNEL(ch) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = (ch), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.scan_index = ch, \
.scan_type = { \
.sign = 'u', \
.realbits = 8, \
.storagebits = 8, \
}, \
}
static const struct iio_chan_spec max1118_channels[] = {
MAX1118_CHANNEL(0),
MAX1118_CHANNEL(1),
IIO_CHAN_SOFT_TIMESTAMP(2),
};
static int max1118_read(struct spi_device *spi, int channel)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct max1118 *adc = iio_priv(indio_dev);
struct spi_transfer xfers[] = {
/*
* To select CH1 for conversion, CNVST pin must be brought high
* and low for a second time.
*/
{
.len = 0,
.delay_usecs = 1, /* > CNVST Low Time 100 ns */
.cs_change = 1,
},
/*
* The acquisition interval begins with the falling edge of
* CNVST. The total acquisition and conversion process takes
* <7.5us.
*/
{
.len = 0,
.delay_usecs = 8,
},
{
.rx_buf = &adc->data,
.len = 1,
},
};
int ret;
if (channel == 0)
ret = spi_sync_transfer(spi, xfers + 1, 2);
else
ret = spi_sync_transfer(spi, xfers, 3);
if (ret)
return ret;
return adc->data;
}
static int max1118_get_vref_mV(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct max1118 *adc = iio_priv(indio_dev);
const struct spi_device_id *id = spi_get_device_id(spi);
int vref_uV;
switch (id->driver_data) {
case max1117:
return 2048;
case max1119:
return 4096;
case max1118:
vref_uV = regulator_get_voltage(adc->reg);
if (vref_uV < 0)
return vref_uV;
return vref_uV / 1000;
}
return -ENODEV;
}
static int max1118_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct max1118 *adc = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
mutex_lock(&adc->lock);
*val = max1118_read(adc->spi, chan->channel);
mutex_unlock(&adc->lock);
if (*val < 0)
return *val;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = max1118_get_vref_mV(adc->spi);
if (*val < 0)
return *val;
*val2 = 8;
return IIO_VAL_FRACTIONAL_LOG2;
}
return -EINVAL;
}
static const struct iio_info max1118_info = {
.read_raw = max1118_read_raw,
.driver_module = THIS_MODULE,
};
static irqreturn_t max1118_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct max1118 *adc = iio_priv(indio_dev);
u8 data[16] = { }; /* 2x 8-bit ADC data + padding + 8 bytes timestamp */
int scan_index;
int i = 0;
mutex_lock(&adc->lock);
for_each_set_bit(scan_index, indio_dev->active_scan_mask,
indio_dev->masklength) {
const struct iio_chan_spec *scan_chan =
&indio_dev->channels[scan_index];
int ret = max1118_read(adc->spi, scan_chan->channel);
if (ret < 0) {
dev_warn(&adc->spi->dev,
"failed to get conversion data\n");
goto out;
}
data[i] = ret;
i++;
}
iio_push_to_buffers_with_timestamp(indio_dev, data,
iio_get_time_ns(indio_dev));
out:
mutex_unlock(&adc->lock);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
}
static int max1118_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct max1118 *adc;
const struct spi_device_id *id = spi_get_device_id(spi);
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
if (!indio_dev)
return -ENOMEM;
adc = iio_priv(indio_dev);
adc->spi = spi;
mutex_init(&adc->lock);
if (id->driver_data == max1118) {
adc->reg = devm_regulator_get(&spi->dev, "vref");
if (IS_ERR(adc->reg)) {
dev_err(&spi->dev, "failed to get vref regulator\n");
return PTR_ERR(adc->reg);
}
ret = regulator_enable(adc->reg);
if (ret)
return ret;
}
spi_set_drvdata(spi, indio_dev);
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->dev.parent = &spi->dev;
indio_dev->info = &max1118_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = max1118_channels;
indio_dev->num_channels = ARRAY_SIZE(max1118_channels);
/*
* To reinitiate a conversion on CH0, it is necessary to allow for a
* conversion to be complete and all of the data to be read out. Once
* a conversion has been completed, the MAX1117/MAX1118/MAX1119 will go
* into AutoShutdown mode until the next conversion is initiated.
*/
max1118_read(spi, 0);
ret = iio_triggered_buffer_setup(indio_dev, NULL,
max1118_trigger_handler, NULL);
if (ret)
goto err_reg_disable;
ret = iio_device_register(indio_dev);
if (ret)
goto err_buffer_cleanup;
return 0;
err_buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
err_reg_disable:
if (id->driver_data == max1118)
regulator_disable(adc->reg);
return ret;
}
static int max1118_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct max1118 *adc = iio_priv(indio_dev);
const struct spi_device_id *id = spi_get_device_id(spi);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
if (id->driver_data == max1118)
return regulator_disable(adc->reg);
return 0;
}
static const struct spi_device_id max1118_id[] = {
{ "max1117", max1117 },
{ "max1118", max1118 },
{ "max1119", max1119 },
{}
};
MODULE_DEVICE_TABLE(spi, max1118_id);
#ifdef CONFIG_OF
static const struct of_device_id max1118_dt_ids[] = {
{ .compatible = "maxim,max1117" },
{ .compatible = "maxim,max1118" },
{ .compatible = "maxim,max1119" },
{},
};
MODULE_DEVICE_TABLE(of, max1118_dt_ids);
#endif
static struct spi_driver max1118_spi_driver = {
.driver = {
.name = "max1118",
.of_match_table = of_match_ptr(max1118_dt_ids),
},
.probe = max1118_probe,
.remove = max1118_remove,
.id_table = max1118_id,
};
module_spi_driver(max1118_spi_driver);
MODULE_AUTHOR("Akinobu Mita <[email protected]>");
MODULE_DESCRIPTION("MAXIM MAX1117/MAX1118/MAX1119 ADCs driver");
MODULE_LICENSE("GPL v2");

585
drivers/iio/adc/max9611.c Normal file
View file

@ -0,0 +1,585 @@
/*
* iio/adc/max9611.c
*
* Maxim max9611/max9612 high side current sense amplifier with
* 12-bit ADC interface.
*
* Copyright (C) 2017 Jacopo Mondi
*
* 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.
*/
/*
* This driver supports input common-mode voltage, current-sense
* amplifier with programmable gains and die temperature reading from
* Maxim max9611/max9612.
*
* Op-amp, analog comparator, and watchdog functionalities are not
* supported by this driver.
*/
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/module.h>
#include <linux/of_device.h>
#define DRIVER_NAME "max9611"
/* max9611 register addresses */
#define MAX9611_REG_CSA_DATA 0x00
#define MAX9611_REG_RS_DATA 0x02
#define MAX9611_REG_TEMP_DATA 0x08
#define MAX9611_REG_CTRL1 0x0a
#define MAX9611_REG_CTRL2 0x0b
/* max9611 REG1 mux configuration options */
#define MAX9611_MUX_MASK GENMASK(3, 0)
#define MAX9611_MUX_SENSE_1x 0x00
#define MAX9611_MUX_SENSE_4x 0x01
#define MAX9611_MUX_SENSE_8x 0x02
#define MAX9611_INPUT_VOLT 0x03
#define MAX9611_MUX_TEMP 0x06
/* max9611 voltage (both csa and input) helper macros */
#define MAX9611_VOLTAGE_SHIFT 0x04
#define MAX9611_VOLTAGE_RAW(_r) ((_r) >> MAX9611_VOLTAGE_SHIFT)
/*
* max9611 current sense amplifier voltage output:
* LSB and offset values depends on selected gain (1x, 4x, 8x)
*
* GAIN LSB (nV) OFFSET (LSB steps)
* 1x 107500 1
* 4x 26880 1
* 8x 13440 3
*
* The complete formula to calculate current sense voltage is:
* (((adc_read >> 4) - offset) / ((1 / LSB) * 10^-3)
*/
#define MAX9611_CSA_1X_LSB_nV 107500
#define MAX9611_CSA_4X_LSB_nV 26880
#define MAX9611_CSA_8X_LSB_nV 13440
#define MAX9611_CSA_1X_OFFS_RAW 1
#define MAX9611_CSA_4X_OFFS_RAW 1
#define MAX9611_CSA_8X_OFFS_RAW 3
/*
* max9611 common input mode (CIM): LSB is 14mV, with 14mV offset at 25 C
*
* The complete formula to calculate input common voltage is:
* (((adc_read >> 4) * 1000) - offset) / (1 / 14 * 1000)
*/
#define MAX9611_CIM_LSB_mV 14
#define MAX9611_CIM_OFFSET_RAW 1
/*
* max9611 temperature reading: LSB is 480 milli degrees Celsius
*
* The complete formula to calculate temperature is:
* ((adc_read >> 7) * 1000) / (1 / 480 * 1000)
*/
#define MAX9611_TEMP_MAX_POS 0x7f80
#define MAX9611_TEMP_MAX_NEG 0xff80
#define MAX9611_TEMP_MIN_NEG 0xd980
#define MAX9611_TEMP_MASK GENMASK(7, 15)
#define MAX9611_TEMP_SHIFT 0x07
#define MAX9611_TEMP_RAW(_r) ((_r) >> MAX9611_TEMP_SHIFT)
#define MAX9611_TEMP_SCALE_NUM 1000000
#define MAX9611_TEMP_SCALE_DIV 2083
struct max9611_dev {
struct device *dev;
struct i2c_client *i2c_client;
struct mutex lock;
unsigned int shunt_resistor_uohm;
};
enum max9611_conf_ids {
CONF_SENSE_1x,
CONF_SENSE_4x,
CONF_SENSE_8x,
CONF_IN_VOLT,
CONF_TEMP,
};
/**
* max9611_mux_conf - associate ADC mux configuration with register address
* where data shall be read from
*/
static const unsigned int max9611_mux_conf[][2] = {
/* CONF_SENSE_1x */
{ MAX9611_MUX_SENSE_1x, MAX9611_REG_CSA_DATA },
/* CONF_SENSE_4x */
{ MAX9611_MUX_SENSE_4x, MAX9611_REG_CSA_DATA },
/* CONF_SENSE_8x */
{ MAX9611_MUX_SENSE_8x, MAX9611_REG_CSA_DATA },
/* CONF_IN_VOLT */
{ MAX9611_INPUT_VOLT, MAX9611_REG_RS_DATA },
/* CONF_TEMP */
{ MAX9611_MUX_TEMP, MAX9611_REG_TEMP_DATA },
};
enum max9611_csa_gain {
CSA_GAIN_1x,
CSA_GAIN_4x,
CSA_GAIN_8x,
};
enum max9611_csa_gain_params {
CSA_GAIN_LSB_nV,
CSA_GAIN_OFFS_RAW,
};
/**
* max9611_csa_gain_conf - associate gain multiplier with LSB and
* offset values.
*
* Group together parameters associated with configurable gain
* on current sense amplifier path to ADC interface.
* Current sense read routine adjusts gain until it gets a meaningful
* value; use this structure to retrieve the correct LSB and offset values.
*/
static const unsigned int max9611_gain_conf[][2] = {
{ /* [0] CSA_GAIN_1x */
MAX9611_CSA_1X_LSB_nV,
MAX9611_CSA_1X_OFFS_RAW,
},
{ /* [1] CSA_GAIN_4x */
MAX9611_CSA_4X_LSB_nV,
MAX9611_CSA_4X_OFFS_RAW,
},
{ /* [2] CSA_GAIN_8x */
MAX9611_CSA_8X_LSB_nV,
MAX9611_CSA_8X_OFFS_RAW,
},
};
enum max9611_chan_addrs {
MAX9611_CHAN_VOLTAGE_INPUT,
MAX9611_CHAN_VOLTAGE_SENSE,
MAX9611_CHAN_TEMPERATURE,
MAX9611_CHAN_CURRENT_LOAD,
MAX9611_CHAN_POWER_LOAD,
};
static const struct iio_chan_spec max9611_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE),
.address = MAX9611_CHAN_TEMPERATURE,
},
{
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.address = MAX9611_CHAN_VOLTAGE_SENSE,
.indexed = 1,
.channel = 0,
},
{
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.address = MAX9611_CHAN_VOLTAGE_INPUT,
.indexed = 1,
.channel = 1,
},
{
.type = IIO_CURRENT,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.address = MAX9611_CHAN_CURRENT_LOAD,
},
{
.type = IIO_POWER,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
.address = MAX9611_CHAN_POWER_LOAD
},
};
/**
* max9611_read_single() - read a single value from ADC interface
*
* Data registers are 16 bit long, spread between two 8 bit registers
* with consecutive addresses.
* Configure ADC mux first, then read register at address "reg_addr".
* The smbus_read_word routine asks for 16 bits and the ADC is kind enough
* to return values from "reg_addr" and "reg_addr + 1" consecutively.
* Data are transmitted with big-endian ordering: MSB arrives first.
*
* @max9611: max9611 device
* @selector: index for mux and register configuration
* @raw_val: the value returned from ADC
*/
static int max9611_read_single(struct max9611_dev *max9611,
enum max9611_conf_ids selector,
u16 *raw_val)
{
int ret;
u8 mux_conf = max9611_mux_conf[selector][0] & MAX9611_MUX_MASK;
u8 reg_addr = max9611_mux_conf[selector][1];
/*
* Keep mutex lock held during read-write to avoid mux register
* (CTRL1) re-configuration.
*/
mutex_lock(&max9611->lock);
ret = i2c_smbus_write_byte_data(max9611->i2c_client,
MAX9611_REG_CTRL1, mux_conf);
if (ret) {
dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
MAX9611_REG_CTRL1, mux_conf);
mutex_unlock(&max9611->lock);
return ret;
}
/*
* need a delay here to make register configuration
* stabilize. 1 msec at least, from empirical testing.
*/
usleep_range(1000, 2000);
ret = i2c_smbus_read_word_swapped(max9611->i2c_client, reg_addr);
if (ret < 0) {
dev_err(max9611->dev, "i2c read word from 0x%2x failed\n",
reg_addr);
mutex_unlock(&max9611->lock);
return ret;
}
*raw_val = ret;
mutex_unlock(&max9611->lock);
return 0;
}
/**
* max9611_read_csa_voltage() - read current sense amplifier output voltage
*
* Current sense amplifier output voltage is read through a configurable
* 1x, 4x or 8x gain.
* Start with plain 1x gain, and adjust gain control properly until a
* meaningful value is read from ADC output.
*
* @max9611: max9611 device
* @adc_raw: raw value read from ADC output
* @csa_gain: gain configuration option selector
*/
static int max9611_read_csa_voltage(struct max9611_dev *max9611,
u16 *adc_raw,
enum max9611_csa_gain *csa_gain)
{
enum max9611_conf_ids gain_selectors[] = {
CONF_SENSE_1x,
CONF_SENSE_4x,
CONF_SENSE_8x
};
unsigned int i;
int ret;
for (i = 0; i < ARRAY_SIZE(gain_selectors); ++i) {
ret = max9611_read_single(max9611, gain_selectors[i], adc_raw);
if (ret)
return ret;
if (*adc_raw > 0) {
*csa_gain = gain_selectors[i];
return 0;
}
}
return -EIO;
}
static int max9611_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct max9611_dev *dev = iio_priv(indio_dev);
enum max9611_csa_gain gain_selector;
const unsigned int *csa_gain;
u16 adc_data;
int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
switch (chan->address) {
case MAX9611_CHAN_TEMPERATURE:
ret = max9611_read_single(dev, CONF_TEMP,
&adc_data);
if (ret)
return -EINVAL;
*val = MAX9611_TEMP_RAW(adc_data);
return IIO_VAL_INT;
case MAX9611_CHAN_VOLTAGE_INPUT:
ret = max9611_read_single(dev, CONF_IN_VOLT,
&adc_data);
if (ret)
return -EINVAL;
*val = MAX9611_VOLTAGE_RAW(adc_data);
return IIO_VAL_INT;
}
break;
case IIO_CHAN_INFO_OFFSET:
/* MAX9611_CHAN_VOLTAGE_INPUT */
*val = MAX9611_CIM_OFFSET_RAW;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
switch (chan->address) {
case MAX9611_CHAN_TEMPERATURE:
*val = MAX9611_TEMP_SCALE_NUM;
*val2 = MAX9611_TEMP_SCALE_DIV;
return IIO_VAL_FRACTIONAL;
case MAX9611_CHAN_VOLTAGE_INPUT:
*val = MAX9611_CIM_LSB_mV;
return IIO_VAL_INT;
}
break;
case IIO_CHAN_INFO_PROCESSED:
switch (chan->address) {
case MAX9611_CHAN_VOLTAGE_SENSE:
/*
* processed (mV): (raw - offset) * LSB (nV) / 10^6
*
* Even if max9611 can output raw csa voltage readings,
* use a produced value as scale depends on gain.
*/
ret = max9611_read_csa_voltage(dev, &adc_data,
&gain_selector);
if (ret)
return -EINVAL;
csa_gain = max9611_gain_conf[gain_selector];
adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
*val = MAX9611_VOLTAGE_RAW(adc_data) *
csa_gain[CSA_GAIN_LSB_nV];
*val2 = 1000000;
return IIO_VAL_FRACTIONAL;
case MAX9611_CHAN_CURRENT_LOAD:
/* processed (mA): Vcsa (nV) / Rshunt (uOhm) */
ret = max9611_read_csa_voltage(dev, &adc_data,
&gain_selector);
if (ret)
return -EINVAL;
csa_gain = max9611_gain_conf[gain_selector];
adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
*val = MAX9611_VOLTAGE_RAW(adc_data) *
csa_gain[CSA_GAIN_LSB_nV];
*val2 = dev->shunt_resistor_uohm;
return IIO_VAL_FRACTIONAL;
case MAX9611_CHAN_POWER_LOAD:
/*
* processed (mW): Vin (mV) * Vcsa (uV) /
* Rshunt (uOhm)
*/
ret = max9611_read_single(dev, CONF_IN_VOLT,
&adc_data);
if (ret)
return -EINVAL;
adc_data -= MAX9611_CIM_OFFSET_RAW;
*val = MAX9611_VOLTAGE_RAW(adc_data) *
MAX9611_CIM_LSB_mV;
ret = max9611_read_csa_voltage(dev, &adc_data,
&gain_selector);
if (ret)
return -EINVAL;
csa_gain = max9611_gain_conf[gain_selector];
/* divide by 10^3 here to avoid 32bit overflow */
adc_data -= csa_gain[CSA_GAIN_OFFS_RAW];
*val *= MAX9611_VOLTAGE_RAW(adc_data) *
csa_gain[CSA_GAIN_LSB_nV] / 1000;
*val2 = dev->shunt_resistor_uohm;
return IIO_VAL_FRACTIONAL;
}
break;
}
return -EINVAL;
}
static ssize_t max9611_shunt_resistor_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct max9611_dev *max9611 = iio_priv(dev_to_iio_dev(dev));
unsigned int i, r;
i = max9611->shunt_resistor_uohm / 1000;
r = max9611->shunt_resistor_uohm % 1000;
return sprintf(buf, "%u.%03u\n", i, r);
}
static IIO_DEVICE_ATTR(in_power_shunt_resistor, 0444,
max9611_shunt_resistor_show, NULL, 0);
static IIO_DEVICE_ATTR(in_current_shunt_resistor, 0444,
max9611_shunt_resistor_show, NULL, 0);
static struct attribute *max9611_attributes[] = {
&iio_dev_attr_in_power_shunt_resistor.dev_attr.attr,
&iio_dev_attr_in_current_shunt_resistor.dev_attr.attr,
NULL,
};
static const struct attribute_group max9611_attribute_group = {
.attrs = max9611_attributes,
};
static const struct iio_info indio_info = {
.driver_module = THIS_MODULE,
.read_raw = max9611_read_raw,
.attrs = &max9611_attribute_group,
};
static int max9611_init(struct max9611_dev *max9611)
{
struct i2c_client *client = max9611->i2c_client;
u16 regval;
int ret;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WRITE_BYTE |
I2C_FUNC_SMBUS_READ_WORD_DATA)) {
dev_err(max9611->dev,
"I2c adapter does not support smbus write_byte or read_word functionalities: aborting probe.\n");
return -EINVAL;
}
/* Make sure die temperature is in range to test communications. */
ret = max9611_read_single(max9611, CONF_TEMP, &regval);
if (ret)
return ret;
regval = ret & MAX9611_TEMP_MASK;
if ((regval > MAX9611_TEMP_MAX_POS &&
regval < MAX9611_TEMP_MIN_NEG) ||
regval > MAX9611_TEMP_MAX_NEG) {
dev_err(max9611->dev,
"Invalid value received from ADC 0x%4x: aborting\n",
regval);
return -EIO;
}
/* Mux shall be zeroed back before applying other configurations */
ret = i2c_smbus_write_byte_data(max9611->i2c_client,
MAX9611_REG_CTRL1, 0);
if (ret) {
dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
MAX9611_REG_CTRL1, 0);
return ret;
}
ret = i2c_smbus_write_byte_data(max9611->i2c_client,
MAX9611_REG_CTRL2, 0);
if (ret) {
dev_err(max9611->dev, "i2c write byte failed: 0x%2x - 0x%2x\n",
MAX9611_REG_CTRL2, 0);
return ret;
}
usleep_range(1000, 2000);
return 0;
}
static const struct of_device_id max9611_of_table[] = {
{.compatible = "maxim,max9611", .data = "max9611"},
{.compatible = "maxim,max9612", .data = "max9612"},
{ },
};
MODULE_DEVICE_TABLE(of, max9611_of_table);
static int max9611_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
const char * const shunt_res_prop = "shunt-resistor-micro-ohms";
const struct device_node *of_node = client->dev.of_node;
const struct of_device_id *of_id =
of_match_device(max9611_of_table, &client->dev);
struct max9611_dev *max9611;
struct iio_dev *indio_dev;
unsigned int of_shunt;
int ret;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*max9611));
if (IS_ERR(indio_dev))
return PTR_ERR(indio_dev);
i2c_set_clientdata(client, indio_dev);
max9611 = iio_priv(indio_dev);
max9611->dev = &client->dev;
max9611->i2c_client = client;
mutex_init(&max9611->lock);
ret = of_property_read_u32(of_node, shunt_res_prop, &of_shunt);
if (ret) {
dev_err(&client->dev,
"Missing %s property for %s node\n",
shunt_res_prop, of_node->full_name);
return ret;
}
max9611->shunt_resistor_uohm = of_shunt;
ret = max9611_init(max9611);
if (ret)
return ret;
indio_dev->dev.parent = &client->dev;
indio_dev->dev.of_node = client->dev.of_node;
indio_dev->name = of_id->data;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &indio_info;
indio_dev->channels = max9611_channels;
indio_dev->num_channels = ARRAY_SIZE(max9611_channels);
return devm_iio_device_register(&client->dev, indio_dev);
}
static struct i2c_driver max9611_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = max9611_of_table,
},
.probe = max9611_probe,
};
module_i2c_driver(max9611_driver);
MODULE_AUTHOR("Jacopo Mondi <[email protected]>");
MODULE_DESCRIPTION("Maxim max9611/12 current sense amplifier with 12bit ADC");
MODULE_LICENSE("GPL v2");

File diff suppressed because it is too large Load diff

View file

@ -28,6 +28,8 @@
#include <dt-bindings/iio/qcom,spmi-vadc.h>
#include "qcom-vadc-common.h"
/* VADC register and bit definitions */
#define VADC_REVISION2 0x1
#define VADC_REVISION2_SUPPORTED_VADC 1
@ -75,83 +77,9 @@
#define VADC_DATA 0x60 /* 16 bits */
#define VADC_CONV_TIME_MIN_US 2000
#define VADC_CONV_TIME_MAX_US 2100
/* Min ADC code represents 0V */
#define VADC_MIN_ADC_CODE 0x6000
/* Max ADC code represents full-scale range of 1.8V */
#define VADC_MAX_ADC_CODE 0xa800
#define VADC_ABSOLUTE_RANGE_UV 625000
#define VADC_RATIOMETRIC_RANGE 1800
#define VADC_DEF_PRESCALING 0 /* 1:1 */
#define VADC_DEF_DECIMATION 0 /* 512 */
#define VADC_DEF_HW_SETTLE_TIME 0 /* 0 us */
#define VADC_DEF_AVG_SAMPLES 0 /* 1 sample */
#define VADC_DEF_CALIB_TYPE VADC_CALIB_ABSOLUTE
#define VADC_DECIMATION_MIN 512
#define VADC_DECIMATION_MAX 4096
#define VADC_HW_SETTLE_DELAY_MAX 10000
#define VADC_AVG_SAMPLES_MAX 512
#define KELVINMIL_CELSIUSMIL 273150
#define PMI_CHG_SCALE_1 -138890
#define PMI_CHG_SCALE_2 391750000000LL
#define VADC_CHAN_MIN VADC_USBIN
#define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
/**
* struct vadc_map_pt - Map the graph representation for ADC channel
* @x: Represent the ADC digitized code.
* @y: Represent the physical data which can be temperature, voltage,
* resistance.
*/
struct vadc_map_pt {
s32 x;
s32 y;
};
/*
* VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
* VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
* calibration.
*/
enum vadc_calibration {
VADC_CALIB_ABSOLUTE = 0,
VADC_CALIB_RATIOMETRIC
};
/**
* struct vadc_linear_graph - Represent ADC characteristics.
* @dy: numerator slope to calculate the gain.
* @dx: denominator slope to calculate the gain.
* @gnd: A/D word of the ground reference used for the channel.
*
* Each ADC device has different offset and gain parameters which are
* computed to calibrate the device.
*/
struct vadc_linear_graph {
s32 dy;
s32 dx;
s32 gnd;
};
/**
* struct vadc_prescale_ratio - Represent scaling ratio for ADC input.
* @num: the inverse numerator of the gain applied to the input channel.
* @den: the inverse denominator of the gain applied to the input channel.
*/
struct vadc_prescale_ratio {
u32 num;
u32 den;
};
/**
* struct vadc_channel_prop - VADC channel property.
* @channel: channel number, refer to the channel list.
@ -162,9 +90,8 @@ struct vadc_prescale_ratio {
* start of conversion.
* @avg_samples: ability to provide single result from the ADC
* that is an average of multiple measurements.
* @scale_fn: Represents the scaling function to convert voltage
* @scale_fn_type: Represents the scaling function to convert voltage
* physical units desired by the client for the channel.
* Referenced from enum vadc_scale_fn_type.
*/
struct vadc_channel_prop {
unsigned int channel;
@ -173,7 +100,7 @@ struct vadc_channel_prop {
unsigned int prescale;
unsigned int hw_settle_time;
unsigned int avg_samples;
unsigned int scale_fn;
enum vadc_scale_fn_type scale_fn_type;
};
/**
@ -204,35 +131,6 @@ struct vadc_priv {
struct mutex lock;
};
/**
* struct vadc_scale_fn - Scaling function prototype
* @scale: Function pointer to one of the scaling functions
* which takes the adc properties, channel properties,
* and returns the physical result.
*/
struct vadc_scale_fn {
int (*scale)(struct vadc_priv *, const struct vadc_channel_prop *,
u16, int *);
};
/**
* enum vadc_scale_fn_type - Scaling function to convert ADC code to
* physical scaled units for the channel.
* SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
* SCALE_THERM_100K_PULLUP: Returns temperature in millidegC.
* Uses a mapping table with 100K pullup.
* SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
* SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
* SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
*/
enum vadc_scale_fn_type {
SCALE_DEFAULT = 0,
SCALE_THERM_100K_PULLUP,
SCALE_PMIC_THERM,
SCALE_XOTHERM,
SCALE_PMI_CHG_TEMP,
};
static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
{.num = 1, .den = 1},
{.num = 1, .den = 3},
@ -244,44 +142,6 @@ static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
{.num = 1, .den = 10}
};
/* Voltage to temperature */
static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
{1758, -40},
{1742, -35},
{1719, -30},
{1691, -25},
{1654, -20},
{1608, -15},
{1551, -10},
{1483, -5},
{1404, 0},
{1315, 5},
{1218, 10},
{1114, 15},
{1007, 20},
{900, 25},
{795, 30},
{696, 35},
{605, 40},
{522, 45},
{448, 50},
{383, 55},
{327, 60},
{278, 65},
{237, 70},
{202, 75},
{172, 80},
{146, 85},
{125, 90},
{107, 95},
{92, 100},
{79, 105},
{68, 110},
{59, 115},
{51, 120},
{44, 125}
};
static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
{
return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
@ -553,159 +413,6 @@ err:
return ret;
}
static int vadc_map_voltage_temp(const struct vadc_map_pt *pts,
u32 tablesize, s32 input, s64 *output)
{
bool descending = 1;
u32 i = 0;
if (!pts)
return -EINVAL;
/* Check if table is descending or ascending */
if (tablesize > 1) {
if (pts[0].x < pts[1].x)
descending = 0;
}
while (i < tablesize) {
if ((descending) && (pts[i].x < input)) {
/* table entry is less than measured*/
/* value and table is descending, stop */
break;
} else if ((!descending) &&
(pts[i].x > input)) {
/* table entry is greater than measured*/
/*value and table is ascending, stop */
break;
}
i++;
}
if (i == 0) {
*output = pts[0].y;
} else if (i == tablesize) {
*output = pts[tablesize - 1].y;
} else {
/* result is between search_index and search_index-1 */
/* interpolate linearly */
*output = (((s32)((pts[i].y - pts[i - 1].y) *
(input - pts[i - 1].x)) /
(pts[i].x - pts[i - 1].x)) +
pts[i - 1].y);
}
return 0;
}
static void vadc_scale_calib(struct vadc_priv *vadc, u16 adc_code,
const struct vadc_channel_prop *prop,
s64 *scale_voltage)
{
*scale_voltage = (adc_code -
vadc->graph[prop->calibration].gnd);
*scale_voltage *= vadc->graph[prop->calibration].dx;
*scale_voltage = div64_s64(*scale_voltage,
vadc->graph[prop->calibration].dy);
if (prop->calibration == VADC_CALIB_ABSOLUTE)
*scale_voltage +=
vadc->graph[prop->calibration].dx;
if (*scale_voltage < 0)
*scale_voltage = 0;
}
static int vadc_scale_volt(struct vadc_priv *vadc,
const struct vadc_channel_prop *prop, u16 adc_code,
int *result_uv)
{
const struct vadc_prescale_ratio *prescale;
s64 voltage = 0, result = 0;
vadc_scale_calib(vadc, adc_code, prop, &voltage);
prescale = &vadc_prescale_ratios[prop->prescale];
voltage = voltage * prescale->den;
result = div64_s64(voltage, prescale->num);
*result_uv = result;
return 0;
}
static int vadc_scale_therm(struct vadc_priv *vadc,
const struct vadc_channel_prop *prop, u16 adc_code,
int *result_mdec)
{
s64 voltage = 0, result = 0;
vadc_scale_calib(vadc, adc_code, prop, &voltage);
if (prop->calibration == VADC_CALIB_ABSOLUTE)
voltage = div64_s64(voltage, 1000);
vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
voltage, &result);
result *= 1000;
*result_mdec = result;
return 0;
}
static int vadc_scale_die_temp(struct vadc_priv *vadc,
const struct vadc_channel_prop *prop,
u16 adc_code, int *result_mdec)
{
const struct vadc_prescale_ratio *prescale;
s64 voltage = 0;
u64 temp; /* Temporary variable for do_div */
vadc_scale_calib(vadc, adc_code, prop, &voltage);
if (voltage > 0) {
prescale = &vadc_prescale_ratios[prop->prescale];
temp = voltage * prescale->den;
do_div(temp, prescale->num * 2);
voltage = temp;
} else {
voltage = 0;
}
voltage -= KELVINMIL_CELSIUSMIL;
*result_mdec = voltage;
return 0;
}
static int vadc_scale_chg_temp(struct vadc_priv *vadc,
const struct vadc_channel_prop *prop,
u16 adc_code, int *result_mdec)
{
const struct vadc_prescale_ratio *prescale;
s64 voltage = 0, result = 0;
vadc_scale_calib(vadc, adc_code, prop, &voltage);
prescale = &vadc_prescale_ratios[prop->prescale];
voltage = voltage * prescale->den;
voltage = div64_s64(voltage, prescale->num);
voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
voltage = (voltage + PMI_CHG_SCALE_2);
result = div64_s64(voltage, 1000000);
*result_mdec = result;
return 0;
}
static int vadc_decimation_from_dt(u32 value)
{
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
value > VADC_DECIMATION_MAX)
return -EINVAL;
return __ffs64(value / VADC_DECIMATION_MIN);
}
static int vadc_prescaling_from_dt(u32 num, u32 den)
{
unsigned int pre;
@ -742,14 +449,6 @@ static int vadc_avg_samples_from_dt(u32 value)
return __ffs64(value);
}
static struct vadc_scale_fn scale_fn[] = {
[SCALE_DEFAULT] = {vadc_scale_volt},
[SCALE_THERM_100K_PULLUP] = {vadc_scale_therm},
[SCALE_PMIC_THERM] = {vadc_scale_die_temp},
[SCALE_XOTHERM] = {vadc_scale_therm},
[SCALE_PMI_CHG_TEMP] = {vadc_scale_chg_temp},
};
static int vadc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2,
long mask)
@ -766,7 +465,13 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
if (ret)
break;
scale_fn[prop->scale_fn].scale(vadc, prop, adc_code, val);
ret = qcom_vadc_scale(prop->scale_fn_type,
&vadc->graph[prop->calibration],
&vadc_prescale_ratios[prop->prescale],
(prop->calibration == VADC_CALIB_ABSOLUTE),
adc_code, val);
if (ret)
break;
return IIO_VAL_INT;
case IIO_CHAN_INFO_RAW:
@ -809,7 +514,7 @@ struct vadc_channels {
unsigned int prescale_index;
enum iio_chan_type type;
long info_mask;
unsigned int scale_fn;
enum vadc_scale_fn_type scale_fn_type;
};
#define VADC_CHAN(_dname, _type, _mask, _pre, _scale) \
@ -818,7 +523,7 @@ struct vadc_channels {
.prescale_index = _pre, \
.type = _type, \
.info_mask = _mask, \
.scale_fn = _scale \
.scale_fn_type = _scale \
}, \
#define VADC_NO_CHAN(_dname, _type, _mask, _pre) \
@ -976,7 +681,7 @@ static int vadc_get_dt_channel_data(struct device *dev,
ret = of_property_read_u32(node, "qcom,decimation", &value);
if (!ret) {
ret = vadc_decimation_from_dt(value);
ret = qcom_vadc_decimation_from_dt(value);
if (ret < 0) {
dev_err(dev, "%02x invalid decimation %d\n",
chan, value);
@ -1068,7 +773,7 @@ static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
return ret;
}
prop.scale_fn = vadc_chans[prop.channel].scale_fn;
prop.scale_fn_type = vadc_chans[prop.channel].scale_fn_type;
vadc->chan_props[index] = prop;
vadc_chan = &vadc_chans[prop.channel];

View file

@ -0,0 +1,230 @@
#include <linux/bug.h>
#include <linux/kernel.h>
#include <linux/bitops.h>
#include <linux/math64.h>
#include <linux/log2.h>
#include <linux/err.h>
#include "qcom-vadc-common.h"
/* Voltage to temperature */
static const struct vadc_map_pt adcmap_100k_104ef_104fb[] = {
{1758, -40},
{1742, -35},
{1719, -30},
{1691, -25},
{1654, -20},
{1608, -15},
{1551, -10},
{1483, -5},
{1404, 0},
{1315, 5},
{1218, 10},
{1114, 15},
{1007, 20},
{900, 25},
{795, 30},
{696, 35},
{605, 40},
{522, 45},
{448, 50},
{383, 55},
{327, 60},
{278, 65},
{237, 70},
{202, 75},
{172, 80},
{146, 85},
{125, 90},
{107, 95},
{92, 100},
{79, 105},
{68, 110},
{59, 115},
{51, 120},
{44, 125}
};
static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
u32 tablesize, s32 input, s64 *output)
{
bool descending = 1;
u32 i = 0;
if (!pts)
return -EINVAL;
/* Check if table is descending or ascending */
if (tablesize > 1) {
if (pts[0].x < pts[1].x)
descending = 0;
}
while (i < tablesize) {
if ((descending) && (pts[i].x < input)) {
/* table entry is less than measured*/
/* value and table is descending, stop */
break;
} else if ((!descending) &&
(pts[i].x > input)) {
/* table entry is greater than measured*/
/*value and table is ascending, stop */
break;
}
i++;
}
if (i == 0) {
*output = pts[0].y;
} else if (i == tablesize) {
*output = pts[tablesize - 1].y;
} else {
/* result is between search_index and search_index-1 */
/* interpolate linearly */
*output = (((s32)((pts[i].y - pts[i - 1].y) *
(input - pts[i - 1].x)) /
(pts[i].x - pts[i - 1].x)) +
pts[i - 1].y);
}
return 0;
}
static void qcom_vadc_scale_calib(const struct vadc_linear_graph *calib_graph,
u16 adc_code,
bool absolute,
s64 *scale_voltage)
{
*scale_voltage = (adc_code - calib_graph->gnd);
*scale_voltage *= calib_graph->dx;
*scale_voltage = div64_s64(*scale_voltage, calib_graph->dy);
if (absolute)
*scale_voltage += calib_graph->dx;
if (*scale_voltage < 0)
*scale_voltage = 0;
}
static int qcom_vadc_scale_volt(const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
bool absolute, u16 adc_code,
int *result_uv)
{
s64 voltage = 0, result = 0;
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
voltage = voltage * prescale->den;
result = div64_s64(voltage, prescale->num);
*result_uv = result;
return 0;
}
static int qcom_vadc_scale_therm(const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
bool absolute, u16 adc_code,
int *result_mdec)
{
s64 voltage = 0, result = 0;
int ret;
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
if (absolute)
voltage = div64_s64(voltage, 1000);
ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
voltage, &result);
if (ret)
return ret;
result *= 1000;
*result_mdec = result;
return 0;
}
static int qcom_vadc_scale_die_temp(const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
bool absolute,
u16 adc_code, int *result_mdec)
{
s64 voltage = 0;
u64 temp; /* Temporary variable for do_div */
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
if (voltage > 0) {
temp = voltage * prescale->den;
do_div(temp, prescale->num * 2);
voltage = temp;
} else {
voltage = 0;
}
voltage -= KELVINMIL_CELSIUSMIL;
*result_mdec = voltage;
return 0;
}
static int qcom_vadc_scale_chg_temp(const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
bool absolute,
u16 adc_code, int *result_mdec)
{
s64 voltage = 0, result = 0;
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
voltage = voltage * prescale->den;
voltage = div64_s64(voltage, prescale->num);
voltage = ((PMI_CHG_SCALE_1) * (voltage * 2));
voltage = (voltage + PMI_CHG_SCALE_2);
result = div64_s64(voltage, 1000000);
*result_mdec = result;
return 0;
}
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
bool absolute,
u16 adc_code, int *result)
{
switch (scaletype) {
case SCALE_DEFAULT:
return qcom_vadc_scale_volt(calib_graph, prescale,
absolute, adc_code,
result);
case SCALE_THERM_100K_PULLUP:
case SCALE_XOTHERM:
return qcom_vadc_scale_therm(calib_graph, prescale,
absolute, adc_code,
result);
case SCALE_PMIC_THERM:
return qcom_vadc_scale_die_temp(calib_graph, prescale,
absolute, adc_code,
result);
case SCALE_PMI_CHG_TEMP:
return qcom_vadc_scale_chg_temp(calib_graph, prescale,
absolute, adc_code,
result);
default:
return -EINVAL;
}
}
EXPORT_SYMBOL(qcom_vadc_scale);
int qcom_vadc_decimation_from_dt(u32 value)
{
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
value > VADC_DECIMATION_MAX)
return -EINVAL;
return __ffs64(value / VADC_DECIMATION_MIN);
}
EXPORT_SYMBOL(qcom_vadc_decimation_from_dt);

View file

@ -0,0 +1,108 @@
/*
* Code shared between the different Qualcomm PMIC voltage ADCs
*/
#ifndef QCOM_VADC_COMMON_H
#define QCOM_VADC_COMMON_H
#define VADC_CONV_TIME_MIN_US 2000
#define VADC_CONV_TIME_MAX_US 2100
/* Min ADC code represents 0V */
#define VADC_MIN_ADC_CODE 0x6000
/* Max ADC code represents full-scale range of 1.8V */
#define VADC_MAX_ADC_CODE 0xa800
#define VADC_ABSOLUTE_RANGE_UV 625000
#define VADC_RATIOMETRIC_RANGE 1800
#define VADC_DEF_PRESCALING 0 /* 1:1 */
#define VADC_DEF_DECIMATION 0 /* 512 */
#define VADC_DEF_HW_SETTLE_TIME 0 /* 0 us */
#define VADC_DEF_AVG_SAMPLES 0 /* 1 sample */
#define VADC_DEF_CALIB_TYPE VADC_CALIB_ABSOLUTE
#define VADC_DECIMATION_MIN 512
#define VADC_DECIMATION_MAX 4096
#define VADC_HW_SETTLE_DELAY_MAX 10000
#define VADC_AVG_SAMPLES_MAX 512
#define KELVINMIL_CELSIUSMIL 273150
#define PMI_CHG_SCALE_1 -138890
#define PMI_CHG_SCALE_2 391750000000LL
/**
* struct vadc_map_pt - Map the graph representation for ADC channel
* @x: Represent the ADC digitized code.
* @y: Represent the physical data which can be temperature, voltage,
* resistance.
*/
struct vadc_map_pt {
s32 x;
s32 y;
};
/*
* VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
* VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
* calibration.
*/
enum vadc_calibration {
VADC_CALIB_ABSOLUTE = 0,
VADC_CALIB_RATIOMETRIC
};
/**
* struct vadc_linear_graph - Represent ADC characteristics.
* @dy: numerator slope to calculate the gain.
* @dx: denominator slope to calculate the gain.
* @gnd: A/D word of the ground reference used for the channel.
*
* Each ADC device has different offset and gain parameters which are
* computed to calibrate the device.
*/
struct vadc_linear_graph {
s32 dy;
s32 dx;
s32 gnd;
};
/**
* struct vadc_prescale_ratio - Represent scaling ratio for ADC input.
* @num: the inverse numerator of the gain applied to the input channel.
* @den: the inverse denominator of the gain applied to the input channel.
*/
struct vadc_prescale_ratio {
u32 num;
u32 den;
};
/**
* enum vadc_scale_fn_type - Scaling function to convert ADC code to
* physical scaled units for the channel.
* SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
* SCALE_THERM_100K_PULLUP: Returns temperature in millidegC.
* Uses a mapping table with 100K pullup.
* SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
* SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
* SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
*/
enum vadc_scale_fn_type {
SCALE_DEFAULT = 0,
SCALE_THERM_100K_PULLUP,
SCALE_PMIC_THERM,
SCALE_XOTHERM,
SCALE_PMI_CHG_TEMP,
};
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
bool absolute,
u16 adc_code, int *result_mdec);
int qcom_vadc_decimation_from_dt(u32 value);
#endif /* QCOM_VADC_COMMON_H */

View file

@ -60,6 +60,8 @@
#define STM32F4_EOC BIT(1)
/* STM32F4_ADC_CR1 - bit fields */
#define STM32F4_RES_SHIFT 24
#define STM32F4_RES_MASK GENMASK(25, 24)
#define STM32F4_SCAN BIT(8)
#define STM32F4_EOCIE BIT(5)
@ -141,6 +143,7 @@ struct stm32_adc_regs {
* @lock: spinlock
* @bufi: data buffer index
* @num_conv: expected number of scan conversions
* @res: data resolution (e.g. RES bitfield value)
* @trigger_polarity: external trigger polarity (e.g. exten)
* @dma_chan: dma channel
* @rx_buf: dma rx buffer cpu address
@ -157,6 +160,7 @@ struct stm32_adc {
spinlock_t lock; /* interrupt lock */
unsigned int bufi;
unsigned int num_conv;
u32 res;
u32 trigger_polarity;
struct dma_chan *dma_chan;
u8 *rx_buf;
@ -196,6 +200,11 @@ static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
{ IIO_VOLTAGE, 15, "in15" },
};
static const unsigned int stm32f4_adc_resolutions[] = {
/* sorted values so the index matches RES[1:0] in STM32F4_ADC_CR1 */
12, 10, 8, 6,
};
/**
* stm32f4_sq - describe regular sequence registers
* - L: sequence len (register & bit field)
@ -302,6 +311,14 @@ static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
}
static void stm32_adc_set_res(struct stm32_adc *adc)
{
u32 val = stm32_adc_readl(adc, STM32F4_ADC_CR1);
val = (val & ~STM32F4_RES_MASK) | (adc->res << STM32F4_RES_SHIFT);
stm32_adc_writel(adc, STM32F4_ADC_CR1, val);
}
/**
* stm32_adc_start_conv() - Start conversions for regular channels.
* @adc: stm32 adc instance
@ -870,11 +887,37 @@ static const struct iio_chan_spec_ext_info stm32_adc_ext_info[] = {
{},
};
static int stm32_adc_of_get_resolution(struct iio_dev *indio_dev)
{
struct device_node *node = indio_dev->dev.of_node;
struct stm32_adc *adc = iio_priv(indio_dev);
unsigned int i;
u32 res;
if (of_property_read_u32(node, "assigned-resolution-bits", &res))
res = stm32f4_adc_resolutions[0];
for (i = 0; i < ARRAY_SIZE(stm32f4_adc_resolutions); i++)
if (res == stm32f4_adc_resolutions[i])
break;
if (i >= ARRAY_SIZE(stm32f4_adc_resolutions)) {
dev_err(&indio_dev->dev, "Bad resolution: %u bits\n", res);
return -EINVAL;
}
dev_dbg(&indio_dev->dev, "Using %u bits resolution\n", res);
adc->res = i;
return 0;
}
static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
struct iio_chan_spec *chan,
const struct stm32_adc_chan_spec *channel,
int scan_index)
{
struct stm32_adc *adc = iio_priv(indio_dev);
chan->type = channel->type;
chan->channel = channel->channel;
chan->datasheet_name = channel->name;
@ -883,7 +926,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
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.realbits = stm32f4_adc_resolutions[adc->res];
chan->scan_type.storagebits = 16;
chan->ext_info = stm32_adc_ext_info;
}
@ -1022,6 +1065,11 @@ static int stm32_adc_probe(struct platform_device *pdev)
return ret;
}
ret = stm32_adc_of_get_resolution(indio_dev);
if (ret < 0)
goto err_clk_disable;
stm32_adc_set_res(adc);
ret = stm32_adc_chan_of_init(indio_dev);
if (ret < 0)
goto err_clk_disable;

View file

@ -85,6 +85,12 @@ static const struct gpadc_data sun6i_gpadc_data = {
.adc_chan_mask = SUN6I_GPADC_CTRL1_ADC_CHAN_MASK,
};
static const struct gpadc_data sun8i_a33_gpadc_data = {
.temp_offset = -1662,
.temp_scale = 162,
.tp_mode_en = SUN8I_GPADC_CTRL1_CHOP_TEMP_EN,
};
struct sun4i_gpadc_iio {
struct iio_dev *indio_dev;
struct completion completion;
@ -96,6 +102,7 @@ struct sun4i_gpadc_iio {
unsigned int temp_data_irq;
atomic_t ignore_temp_data_irq;
const struct gpadc_data *data;
bool no_irq;
/* prevents concurrent reads of temperature and ADC */
struct mutex mutex;
};
@ -138,6 +145,23 @@ static const struct iio_chan_spec sun4i_gpadc_channels_no_temp[] = {
SUN4I_GPADC_ADC_CHANNEL(3, "adc_chan3"),
};
static const struct iio_chan_spec sun8i_a33_gpadc_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET),
.datasheet_name = "temp_adc",
},
};
static const struct regmap_config sun4i_gpadc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.fast_io = true,
};
static int sun4i_prepare_for_irq(struct iio_dev *indio_dev, int channel,
unsigned int irq)
{
@ -247,6 +271,17 @@ static int sun4i_gpadc_temp_read(struct iio_dev *indio_dev, int *val)
{
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
if (info->no_irq) {
pm_runtime_get_sync(indio_dev->dev.parent);
regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
pm_runtime_mark_last_busy(indio_dev->dev.parent);
pm_runtime_put_autosuspend(indio_dev->dev.parent);
return 0;
}
return sun4i_gpadc_read(indio_dev, 0, val, info->temp_data_irq);
}
@ -454,31 +489,69 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name,
return 0;
}
static int sun4i_gpadc_probe(struct platform_device *pdev)
static const struct of_device_id sun4i_gpadc_of_id[] = {
{
.compatible = "allwinner,sun8i-a33-ths",
.data = &sun8i_a33_gpadc_data,
},
{ /* sentinel */ }
};
static int sun4i_gpadc_probe_dt(struct platform_device *pdev,
struct iio_dev *indio_dev)
{
struct sun4i_gpadc_iio *info;
struct iio_dev *indio_dev;
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
const struct of_device_id *of_dev;
struct thermal_zone_device *tzd;
struct resource *mem;
void __iomem *base;
int ret;
struct sun4i_gpadc_dev *sun4i_gpadc_dev;
sun4i_gpadc_dev = dev_get_drvdata(pdev->dev.parent);
of_dev = of_match_device(sun4i_gpadc_of_id, &pdev->dev);
if (!of_dev)
return -ENODEV;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
if (!indio_dev)
return -ENOMEM;
info->no_irq = true;
info->data = (struct gpadc_data *)of_dev->data;
indio_dev->num_channels = ARRAY_SIZE(sun8i_a33_gpadc_channels);
indio_dev->channels = sun8i_a33_gpadc_channels;
info = iio_priv(indio_dev);
platform_set_drvdata(pdev, indio_dev);
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(base))
return PTR_ERR(base);
mutex_init(&info->mutex);
info->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&sun4i_gpadc_regmap_config);
if (IS_ERR(info->regmap)) {
ret = PTR_ERR(info->regmap);
dev_err(&pdev->dev, "failed to init regmap: %d\n", ret);
return ret;
}
if (!IS_ENABLED(CONFIG_THERMAL_OF))
return 0;
tzd = devm_thermal_zone_of_sensor_register(&pdev->dev, 0, info,
&sun4i_ts_tz_ops);
if (IS_ERR(tzd))
dev_err(&pdev->dev, "could not register thermal sensor: %ld\n",
PTR_ERR(tzd));
return PTR_ERR_OR_ZERO(tzd);
}
static int sun4i_gpadc_probe_mfd(struct platform_device *pdev,
struct iio_dev *indio_dev)
{
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
struct sun4i_gpadc_dev *sun4i_gpadc_dev =
dev_get_drvdata(pdev->dev.parent);
int ret;
info->no_irq = false;
info->regmap = sun4i_gpadc_dev->regmap;
info->indio_dev = indio_dev;
init_completion(&info->completion);
indio_dev->name = dev_name(&pdev->dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->dev.of_node = pdev->dev.of_node;
indio_dev->info = &sun4i_gpadc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->num_channels = ARRAY_SIZE(sun4i_gpadc_channels);
indio_dev->channels = sun4i_gpadc_channels;
@ -519,8 +592,7 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"could not register thermal sensor: %ld\n",
PTR_ERR(tzd));
ret = PTR_ERR(tzd);
goto err;
return PTR_ERR(tzd);
}
} else {
indio_dev->num_channels =
@ -528,36 +600,69 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
indio_dev->channels = sun4i_gpadc_channels_no_temp;
}
pm_runtime_set_autosuspend_delay(&pdev->dev,
SUN4I_GPADC_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_enable(&pdev->dev);
if (IS_ENABLED(CONFIG_THERMAL_OF)) {
ret = sun4i_irq_init(pdev, "TEMP_DATA_PENDING",
sun4i_gpadc_temp_data_irq_handler,
"temp_data", &info->temp_data_irq,
&info->ignore_temp_data_irq);
if (ret < 0)
goto err;
return ret;
}
ret = sun4i_irq_init(pdev, "FIFO_DATA_PENDING",
sun4i_gpadc_fifo_data_irq_handler, "fifo_data",
&info->fifo_data_irq, &info->ignore_fifo_data_irq);
if (ret < 0)
goto err;
return ret;
if (IS_ENABLED(CONFIG_THERMAL_OF)) {
ret = iio_map_array_register(indio_dev, sun4i_gpadc_hwmon_maps);
if (ret < 0) {
dev_err(&pdev->dev,
"failed to register iio map array\n");
goto err;
return ret;
}
}
return 0;
}
static int sun4i_gpadc_probe(struct platform_device *pdev)
{
struct sun4i_gpadc_iio *info;
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
if (!indio_dev)
return -ENOMEM;
info = iio_priv(indio_dev);
platform_set_drvdata(pdev, indio_dev);
mutex_init(&info->mutex);
info->indio_dev = indio_dev;
init_completion(&info->completion);
indio_dev->name = dev_name(&pdev->dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->dev.of_node = pdev->dev.of_node;
indio_dev->info = &sun4i_gpadc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
if (pdev->dev.of_node)
ret = sun4i_gpadc_probe_dt(pdev, indio_dev);
else
ret = sun4i_gpadc_probe_mfd(pdev, indio_dev);
if (ret)
return ret;
pm_runtime_set_autosuspend_delay(&pdev->dev,
SUN4I_GPADC_AUTOSUSPEND_DELAY);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_enable(&pdev->dev);
ret = devm_iio_device_register(&pdev->dev, indio_dev);
if (ret < 0) {
dev_err(&pdev->dev, "could not register the device\n");
@ -567,10 +672,9 @@ static int sun4i_gpadc_probe(struct platform_device *pdev)
return 0;
err_map:
if (IS_ENABLED(CONFIG_THERMAL_OF))
if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
iio_map_array_unregister(indio_dev);
err:
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@ -580,10 +684,11 @@ err:
static int sun4i_gpadc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
if (IS_ENABLED(CONFIG_THERMAL_OF))
if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
iio_map_array_unregister(indio_dev);
return 0;
@ -599,6 +704,7 @@ static const struct platform_device_id sun4i_gpadc_id[] = {
static struct platform_driver sun4i_gpadc_driver = {
.driver = {
.name = "sun4i-gpadc-iio",
.of_match_table = sun4i_gpadc_of_id,
.pm = &sun4i_gpadc_pm_ops,
},
.id_table = sun4i_gpadc_id,

View file

@ -138,6 +138,10 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
void hid_sensor_remove_trigger(struct hid_sensor_common *attrb)
{
pm_runtime_disable(&attrb->pdev->dev);
pm_runtime_set_suspended(&attrb->pdev->dev);
pm_runtime_put_noidle(&attrb->pdev->dev);
cancel_work_sync(&attrb->work);
iio_trigger_unregister(attrb->trigger);
iio_trigger_free(attrb->trigger);

View file

@ -284,6 +284,21 @@ config MCP4922
To compile this driver as a module, choose M here: the module
will be called mcp4922.
config STM32_DAC
tristate "STMicroelectronics STM32 DAC"
depends on (ARCH_STM32 && OF) || COMPILE_TEST
depends on REGULATOR
select STM32_DAC_CORE
help
Say yes here to build support for STMicroelectronics STM32 Digital
to Analog Converter (DAC).
This driver can also be built as a module. If so, the module
will be called stm32-dac.
config STM32_DAC_CORE
tristate
config VF610_DAC
tristate "Vybrid vf610 DAC driver"
depends on OF

View file

@ -30,4 +30,6 @@ obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX5821) += max5821.o
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_VF610_DAC) += vf610_dac.o

View file

@ -0,0 +1,180 @@
/*
* This file is part of STM32 DAC driver
*
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
*
* License type: GPLv2
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include "stm32-dac-core.h"
/**
* struct stm32_dac_priv - stm32 DAC core private data
* @pclk: peripheral clock common for all DACs
* @rst: peripheral reset control
* @vref: regulator reference
* @common: Common data for all DAC instances
*/
struct stm32_dac_priv {
struct clk *pclk;
struct reset_control *rst;
struct regulator *vref;
struct stm32_dac_common common;
};
static struct stm32_dac_priv *to_stm32_dac_priv(struct stm32_dac_common *com)
{
return container_of(com, struct stm32_dac_priv, common);
}
static const struct regmap_config stm32_dac_regmap_cfg = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = sizeof(u32),
.max_register = 0x3fc,
};
static int stm32_dac_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct stm32_dac_priv *priv;
struct regmap *regmap;
struct resource *res;
void __iomem *mmio;
int ret;
if (!dev->of_node)
return -ENODEV;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mmio = devm_ioremap_resource(dev, res);
if (IS_ERR(mmio))
return PTR_ERR(mmio);
regmap = devm_regmap_init_mmio(dev, mmio, &stm32_dac_regmap_cfg);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
priv->common.regmap = regmap;
priv->vref = devm_regulator_get(dev, "vref");
if (IS_ERR(priv->vref)) {
ret = PTR_ERR(priv->vref);
dev_err(dev, "vref get failed, %d\n", ret);
return ret;
}
ret = regulator_enable(priv->vref);
if (ret < 0) {
dev_err(dev, "vref enable failed\n");
return ret;
}
ret = regulator_get_voltage(priv->vref);
if (ret < 0) {
dev_err(dev, "vref get voltage failed, %d\n", ret);
goto err_vref;
}
priv->common.vref_mv = ret / 1000;
dev_dbg(dev, "vref+=%dmV\n", priv->common.vref_mv);
priv->pclk = devm_clk_get(dev, "pclk");
if (IS_ERR(priv->pclk)) {
ret = PTR_ERR(priv->pclk);
dev_err(dev, "pclk get failed\n");
goto err_vref;
}
ret = clk_prepare_enable(priv->pclk);
if (ret < 0) {
dev_err(dev, "pclk enable failed\n");
goto err_vref;
}
priv->rst = devm_reset_control_get(dev, NULL);
if (!IS_ERR(priv->rst)) {
reset_control_assert(priv->rst);
udelay(2);
reset_control_deassert(priv->rst);
}
/* When clock speed is higher than 80MHz, set HFSEL */
priv->common.hfsel = (clk_get_rate(priv->pclk) > 80000000UL);
ret = regmap_update_bits(regmap, STM32_DAC_CR, STM32H7_DAC_CR_HFSEL,
priv->common.hfsel ? STM32H7_DAC_CR_HFSEL : 0);
if (ret)
goto err_pclk;
platform_set_drvdata(pdev, &priv->common);
ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, dev);
if (ret < 0) {
dev_err(dev, "failed to populate DT children\n");
goto err_pclk;
}
return 0;
err_pclk:
clk_disable_unprepare(priv->pclk);
err_vref:
regulator_disable(priv->vref);
return ret;
}
static int stm32_dac_remove(struct platform_device *pdev)
{
struct stm32_dac_common *common = platform_get_drvdata(pdev);
struct stm32_dac_priv *priv = to_stm32_dac_priv(common);
of_platform_depopulate(&pdev->dev);
clk_disable_unprepare(priv->pclk);
regulator_disable(priv->vref);
return 0;
}
static const struct of_device_id stm32_dac_of_match[] = {
{ .compatible = "st,stm32h7-dac-core", },
{},
};
MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
static struct platform_driver stm32_dac_driver = {
.probe = stm32_dac_probe,
.remove = stm32_dac_remove,
.driver = {
.name = "stm32-dac-core",
.of_match_table = stm32_dac_of_match,
},
};
module_platform_driver(stm32_dac_driver);
MODULE_AUTHOR("Fabrice Gasnier <[email protected]>");
MODULE_DESCRIPTION("STMicroelectronics STM32 DAC core driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:stm32-dac-core");

View file

@ -0,0 +1,51 @@
/*
* This file is part of STM32 DAC driver
*
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
* Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
*
* License type: GPLv2
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __STM32_DAC_CORE_H
#define __STM32_DAC_CORE_H
#include <linux/regmap.h>
/* STM32 DAC registers */
#define STM32_DAC_CR 0x00
#define STM32_DAC_DHR12R1 0x08
#define STM32_DAC_DHR12R2 0x14
#define STM32_DAC_DOR1 0x2C
#define STM32_DAC_DOR2 0x30
/* STM32_DAC_CR bit fields */
#define STM32_DAC_CR_EN1 BIT(0)
#define STM32H7_DAC_CR_HFSEL BIT(15)
#define STM32_DAC_CR_EN2 BIT(16)
/**
* struct stm32_dac_common - stm32 DAC driver common data (for all instances)
* @regmap: DAC registers shared via regmap
* @vref_mv: reference voltage (mv)
* @hfsel: high speed bus clock selected
*/
struct stm32_dac_common {
struct regmap *regmap;
int vref_mv;
bool hfsel;
};
#endif

334
drivers/iio/dac/stm32-dac.c Normal file
View file

@ -0,0 +1,334 @@
/*
* This file is part of STM32 DAC driver
*
* Copyright (C) 2017, STMicroelectronics - All Rights Reserved
* Authors: Amelie Delaunay <amelie.delaunay@st.com>
* Fabrice Gasnier <fabrice.gasnier@st.com>
*
* License type: GPLv2
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/iio/iio.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include "stm32-dac-core.h"
#define STM32_DAC_CHANNEL_1 1
#define STM32_DAC_CHANNEL_2 2
#define STM32_DAC_IS_CHAN_1(ch) ((ch) & STM32_DAC_CHANNEL_1)
/**
* struct stm32_dac - private data of DAC driver
* @common: reference to DAC common data
*/
struct stm32_dac {
struct stm32_dac_common *common;
};
static int stm32_dac_is_enabled(struct iio_dev *indio_dev, int channel)
{
struct stm32_dac *dac = iio_priv(indio_dev);
u32 en, val;
int ret;
ret = regmap_read(dac->common->regmap, STM32_DAC_CR, &val);
if (ret < 0)
return ret;
if (STM32_DAC_IS_CHAN_1(channel))
en = FIELD_GET(STM32_DAC_CR_EN1, val);
else
en = FIELD_GET(STM32_DAC_CR_EN2, val);
return !!en;
}
static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch,
bool enable)
{
struct stm32_dac *dac = iio_priv(indio_dev);
u32 msk = STM32_DAC_IS_CHAN_1(ch) ? STM32_DAC_CR_EN1 : STM32_DAC_CR_EN2;
u32 en = enable ? msk : 0;
int ret;
ret = regmap_update_bits(dac->common->regmap, STM32_DAC_CR, msk, en);
if (ret < 0) {
dev_err(&indio_dev->dev, "%s failed\n", en ?
"Enable" : "Disable");
return ret;
}
/*
* When HFSEL is set, it is not allowed to write the DHRx register
* during 8 clock cycles after the ENx bit is set. It is not allowed
* to make software/hardware trigger during this period either.
*/
if (en && dac->common->hfsel)
udelay(1);
return 0;
}
static int stm32_dac_get_value(struct stm32_dac *dac, int channel, int *val)
{
int ret;
if (STM32_DAC_IS_CHAN_1(channel))
ret = regmap_read(dac->common->regmap, STM32_DAC_DOR1, val);
else
ret = regmap_read(dac->common->regmap, STM32_DAC_DOR2, val);
return ret ? ret : IIO_VAL_INT;
}
static int stm32_dac_set_value(struct stm32_dac *dac, int channel, int val)
{
int ret;
if (STM32_DAC_IS_CHAN_1(channel))
ret = regmap_write(dac->common->regmap, STM32_DAC_DHR12R1, val);
else
ret = regmap_write(dac->common->regmap, STM32_DAC_DHR12R2, val);
return ret;
}
static int stm32_dac_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct stm32_dac *dac = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
return stm32_dac_get_value(dac, chan->channel, val);
case IIO_CHAN_INFO_SCALE:
*val = dac->common->vref_mv;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
}
}
static int stm32_dac_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct stm32_dac *dac = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
return stm32_dac_set_value(dac, chan->channel, val);
default:
return -EINVAL;
}
}
static int stm32_dac_debugfs_reg_access(struct iio_dev *indio_dev,
unsigned reg, unsigned writeval,
unsigned *readval)
{
struct stm32_dac *dac = iio_priv(indio_dev);
if (!readval)
return regmap_write(dac->common->regmap, reg, writeval);
else
return regmap_read(dac->common->regmap, reg, readval);
}
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[] = {
"three_state",
};
static int stm32_dac_get_powerdown_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
return 0;
}
static int stm32_dac_set_powerdown_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int type)
{
return 0;
}
static ssize_t stm32_dac_read_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
int ret = stm32_dac_is_enabled(indio_dev, chan->channel);
if (ret < 0)
return ret;
return sprintf(buf, "%d\n", ret ? 0 : 1);
}
static ssize_t stm32_dac_write_powerdown(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
bool powerdown;
int ret;
ret = strtobool(buf, &powerdown);
if (ret)
return ret;
ret = stm32_dac_set_enable_state(indio_dev, chan->channel, !powerdown);
if (ret)
return ret;
return len;
}
static const struct iio_enum stm32_dac_powerdown_mode_en = {
.items = stm32_dac_powerdown_modes,
.num_items = ARRAY_SIZE(stm32_dac_powerdown_modes),
.get = stm32_dac_get_powerdown_mode,
.set = stm32_dac_set_powerdown_mode,
};
static const struct iio_chan_spec_ext_info stm32_dac_ext_info[] = {
{
.name = "powerdown",
.read = stm32_dac_read_powerdown,
.write = stm32_dac_write_powerdown,
.shared = IIO_SEPARATE,
},
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &stm32_dac_powerdown_mode_en),
IIO_ENUM_AVAILABLE("powerdown_mode", &stm32_dac_powerdown_mode_en),
{},
};
#define STM32_DAC_CHANNEL(chan, name) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.output = 1, \
.channel = chan, \
.info_mask_separate = \
BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
/* scan_index is always 0 as num_channels is 1 */ \
.scan_type = { \
.sign = 'u', \
.realbits = 12, \
.storagebits = 16, \
}, \
.datasheet_name = name, \
.ext_info = stm32_dac_ext_info \
}
static const struct iio_chan_spec stm32_dac_channels[] = {
STM32_DAC_CHANNEL(STM32_DAC_CHANNEL_1, "out1"),
STM32_DAC_CHANNEL(STM32_DAC_CHANNEL_2, "out2"),
};
static int stm32_dac_chan_of_init(struct iio_dev *indio_dev)
{
struct device_node *np = indio_dev->dev.of_node;
unsigned int i;
u32 channel;
int ret;
ret = of_property_read_u32(np, "reg", &channel);
if (ret) {
dev_err(&indio_dev->dev, "Failed to read reg property\n");
return ret;
}
for (i = 0; i < ARRAY_SIZE(stm32_dac_channels); i++) {
if (stm32_dac_channels[i].channel == channel)
break;
}
if (i >= ARRAY_SIZE(stm32_dac_channels)) {
dev_err(&indio_dev->dev, "Invalid st,dac-channel\n");
return -EINVAL;
}
indio_dev->channels = &stm32_dac_channels[i];
/*
* Expose only one channel here, as they can be used independently,
* with separate trigger. Then separate IIO devices are instantiated
* to manage this.
*/
indio_dev->num_channels = 1;
return 0;
};
static int stm32_dac_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct iio_dev *indio_dev;
struct stm32_dac *dac;
int ret;
if (!np)
return -ENODEV;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*dac));
if (!indio_dev)
return -ENOMEM;
platform_set_drvdata(pdev, indio_dev);
dac = iio_priv(indio_dev);
dac->common = dev_get_drvdata(pdev->dev.parent);
indio_dev->name = dev_name(&pdev->dev);
indio_dev->dev.parent = &pdev->dev;
indio_dev->dev.of_node = pdev->dev.of_node;
indio_dev->info = &stm32_dac_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE;
ret = stm32_dac_chan_of_init(indio_dev);
if (ret < 0)
return ret;
return devm_iio_device_register(&pdev->dev, indio_dev);
}
static const struct of_device_id stm32_dac_of_match[] = {
{ .compatible = "st,stm32-dac", },
{},
};
MODULE_DEVICE_TABLE(of, stm32_dac_of_match);
static struct platform_driver stm32_dac_driver = {
.probe = stm32_dac_probe,
.driver = {
.name = "stm32-dac",
.of_match_table = stm32_dac_of_match,
},
};
module_platform_driver(stm32_dac_driver);
MODULE_ALIAS("platform:stm32-dac");
MODULE_AUTHOR("Amelie Delaunay <[email protected]>");
MODULE_DESCRIPTION("STMicroelectronics STM32 DAC driver");
MODULE_LICENSE("GPL v2");

View file

@ -70,9 +70,8 @@ static int mpu3050_i2c_probe(struct i2c_client *client,
dev_err(&client->dev, "failed to allocate I2C mux\n");
else {
mpu3050->i2cmux->priv = mpu3050;
ret = i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0, 0);
if (ret)
dev_err(&client->dev, "failed to add I2C mux\n");
/* Ignore failure, not critical */
i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0, 0);
}
return 0;

View file

@ -71,6 +71,7 @@ enum st_lsm6dsx_fifo_mode {
/**
* struct st_lsm6dsx_sensor - ST IMU sensor instance
* @name: Sensor name.
* @id: Sensor identifier.
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
* @gain: Configured sensor sensitivity.
@ -83,6 +84,7 @@ enum st_lsm6dsx_fifo_mode {
* @ts: Latest timestamp from the interrupt handler.
*/
struct st_lsm6dsx_sensor {
char name[32];
enum st_lsm6dsx_sensor_id id;
struct st_lsm6dsx_hw *hw;
@ -133,7 +135,7 @@ struct st_lsm6dsx_hw {
#endif /* CONFIG_SPI_MASTER */
};
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
const struct st_lsm6dsx_transfer_function *tf_ops);
int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor);
int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor);

View file

@ -559,19 +559,11 @@ static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin)
{
struct device_node *np = hw->dev->of_node;
int err;
if (!np)
return -EINVAL;
err = of_property_read_u32(np, "st,drdy-int-pin", drdy_pin);
if (err == -ENODATA) {
/* if the property has not been specified use default value */
*drdy_pin = 1;
err = 0;
}
return err;
return of_property_read_u32(np, "st,drdy-int-pin", drdy_pin);
}
static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
@ -642,7 +634,8 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
}
static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
enum st_lsm6dsx_sensor_id id)
enum st_lsm6dsx_sensor_id id,
const char *name)
{
struct st_lsm6dsx_sensor *sensor;
struct iio_dev *iio_dev;
@ -666,27 +659,30 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
case ST_LSM6DSX_ID_ACC:
iio_dev->channels = st_lsm6dsx_acc_channels;
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_acc_channels);
iio_dev->name = "lsm6dsx_accel";
iio_dev->info = &st_lsm6dsx_acc_info;
sensor->decimator_mask = ST_LSM6DSX_REG_ACC_DEC_MASK;
scnprintf(sensor->name, sizeof(sensor->name), "%s_accel",
name);
break;
case ST_LSM6DSX_ID_GYRO:
iio_dev->channels = st_lsm6dsx_gyro_channels;
iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_gyro_channels);
iio_dev->name = "lsm6dsx_gyro";
iio_dev->info = &st_lsm6dsx_gyro_info;
sensor->decimator_mask = ST_LSM6DSX_REG_GYRO_DEC_MASK;
scnprintf(sensor->name, sizeof(sensor->name), "%s_gyro",
name);
break;
default:
return NULL;
}
iio_dev->name = sensor->name;
return iio_dev;
}
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
const struct st_lsm6dsx_transfer_function *tf_ops)
{
struct st_lsm6dsx_hw *hw;
@ -710,7 +706,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
return err;
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i);
hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name);
if (!hw->iio_devs[i])
return -ENOMEM;
}

View file

@ -61,7 +61,7 @@ static int st_lsm6dsx_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
return st_lsm6dsx_probe(&client->dev, client->irq,
(int)id->driver_data,
(int)id->driver_data, id->name,
&st_lsm6dsx_transfer_fn);
}

View file

@ -78,7 +78,7 @@ static int st_lsm6dsx_spi_probe(struct spi_device *spi)
const struct spi_device_id *id = spi_get_device_id(spi);
return st_lsm6dsx_probe(&spi->dev, spi->irq,
(int)id->driver_data,
(int)id->driver_data, id->name,
&st_lsm6dsx_transfer_fn);
}

View file

@ -1112,6 +1112,8 @@ static int apds9960_runtime_resume(struct device *dev)
#endif
static const struct dev_pm_ops apds9960_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(apds9960_runtime_suspend,
apds9960_runtime_resume, NULL)
};

View file

@ -15,6 +15,7 @@
#include <linux/platform_device.h>
#define MAX_TRIGGERS 6
#define MAX_VALIDS 5
/* List the triggers created by each timer */
static const void *triggers_table[][MAX_TRIGGERS] = {
@ -32,12 +33,29 @@ static const void *triggers_table[][MAX_TRIGGERS] = {
{ TIM12_TRGO, TIM12_CH1, TIM12_CH2,},
};
/* List the triggers accepted by each timer */
static const void *valids_table[][MAX_VALIDS] = {
{ TIM5_TRGO, TIM2_TRGO, TIM3_TRGO, TIM4_TRGO,},
{ TIM1_TRGO, TIM8_TRGO, TIM3_TRGO, TIM4_TRGO,},
{ TIM1_TRGO, TIM2_TRGO, TIM5_TRGO, TIM4_TRGO,},
{ TIM1_TRGO, TIM2_TRGO, TIM3_TRGO, TIM8_TRGO,},
{ TIM2_TRGO, TIM3_TRGO, TIM4_TRGO, TIM8_TRGO,},
{ }, /* timer 6 */
{ }, /* timer 7 */
{ TIM1_TRGO, TIM2_TRGO, TIM4_TRGO, TIM5_TRGO,},
{ TIM2_TRGO, TIM3_TRGO,},
{ }, /* timer 10 */
{ }, /* timer 11 */
{ TIM4_TRGO, TIM5_TRGO,},
};
struct stm32_timer_trigger {
struct device *dev;
struct regmap *regmap;
struct clk *clk;
u32 max_arr;
const void *triggers;
const void *valids;
};
static int stm32_timer_start(struct stm32_timer_trigger *priv,
@ -180,8 +198,7 @@ static ssize_t stm32_tt_show_master_mode(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
struct stm32_timer_trigger *priv = dev_get_drvdata(dev);
u32 cr2;
regmap_read(priv->regmap, TIM_CR2, &cr2);
@ -194,8 +211,7 @@ static ssize_t stm32_tt_store_master_mode(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
struct stm32_timer_trigger *priv = dev_get_drvdata(dev);
int i;
for (i = 0; i < ARRAY_SIZE(master_mode_table); i++) {
@ -275,6 +291,286 @@ static int stm32_setup_iio_triggers(struct stm32_timer_trigger *priv)
return 0;
}
static int stm32_counter_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
{
u32 cnt;
regmap_read(priv->regmap, TIM_CNT, &cnt);
*val = cnt;
return IIO_VAL_INT;
}
case IIO_CHAN_INFO_SCALE:
{
u32 smcr;
regmap_read(priv->regmap, TIM_SMCR, &smcr);
smcr &= TIM_SMCR_SMS;
*val = 1;
*val2 = 0;
/* in quadrature case scale = 0.25 */
if (smcr == 3)
*val2 = 2;
return IIO_VAL_FRACTIONAL_LOG2;
}
}
return -EINVAL;
}
static int stm32_counter_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
regmap_write(priv->regmap, TIM_CNT, val);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
/* fixed scale */
return -EINVAL;
}
return -EINVAL;
}
static const struct iio_info stm32_trigger_info = {
.driver_module = THIS_MODULE,
.read_raw = stm32_counter_read_raw,
.write_raw = stm32_counter_write_raw
};
static const char *const stm32_enable_modes[] = {
"always",
"gated",
"triggered",
};
static int stm32_enable_mode2sms(int mode)
{
switch (mode) {
case 0:
return 0;
case 1:
return 5;
case 2:
return 6;
}
return -EINVAL;
}
static int stm32_set_enable_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int mode)
{
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
int sms = stm32_enable_mode2sms(mode);
if (sms < 0)
return sms;
regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, sms);
return 0;
}
static int stm32_sms2enable_mode(int mode)
{
switch (mode) {
case 0:
return 0;
case 5:
return 1;
case 6:
return 2;
}
return -EINVAL;
}
static int stm32_get_enable_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
u32 smcr;
regmap_read(priv->regmap, TIM_SMCR, &smcr);
smcr &= TIM_SMCR_SMS;
return stm32_sms2enable_mode(smcr);
}
static const struct iio_enum stm32_enable_mode_enum = {
.items = stm32_enable_modes,
.num_items = ARRAY_SIZE(stm32_enable_modes),
.set = stm32_set_enable_mode,
.get = stm32_get_enable_mode
};
static const char *const stm32_quadrature_modes[] = {
"channel_A",
"channel_B",
"quadrature",
};
static int stm32_set_quadrature_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int mode)
{
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, mode + 1);
return 0;
}
static int stm32_get_quadrature_mode(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
u32 smcr;
regmap_read(priv->regmap, TIM_SMCR, &smcr);
smcr &= TIM_SMCR_SMS;
return smcr - 1;
}
static const struct iio_enum stm32_quadrature_mode_enum = {
.items = stm32_quadrature_modes,
.num_items = ARRAY_SIZE(stm32_quadrature_modes),
.set = stm32_set_quadrature_mode,
.get = stm32_get_quadrature_mode
};
static const char *const stm32_count_direction_states[] = {
"up",
"down"
};
static int stm32_set_count_direction(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
unsigned int mode)
{
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_DIR, mode);
return 0;
}
static int stm32_get_count_direction(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
u32 cr1;
regmap_read(priv->regmap, TIM_CR1, &cr1);
return (cr1 & TIM_CR1_DIR);
}
static const struct iio_enum stm32_count_direction_enum = {
.items = stm32_count_direction_states,
.num_items = ARRAY_SIZE(stm32_count_direction_states),
.set = stm32_set_count_direction,
.get = stm32_get_count_direction
};
static ssize_t stm32_count_get_preset(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
char *buf)
{
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
u32 arr;
regmap_read(priv->regmap, TIM_ARR, &arr);
return snprintf(buf, PAGE_SIZE, "%u\n", arr);
}
static ssize_t stm32_count_set_preset(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct stm32_timer_trigger *priv = iio_priv(indio_dev);
unsigned int preset;
int ret;
ret = kstrtouint(buf, 0, &preset);
if (ret)
return ret;
regmap_write(priv->regmap, TIM_ARR, preset);
regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_ARPE, TIM_CR1_ARPE);
return len;
}
static const struct iio_chan_spec_ext_info stm32_trigger_count_info[] = {
{
.name = "preset",
.shared = IIO_SEPARATE,
.read = stm32_count_get_preset,
.write = stm32_count_set_preset
},
IIO_ENUM("count_direction", IIO_SEPARATE, &stm32_count_direction_enum),
IIO_ENUM_AVAILABLE("count_direction", &stm32_count_direction_enum),
IIO_ENUM("quadrature_mode", IIO_SEPARATE, &stm32_quadrature_mode_enum),
IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_quadrature_mode_enum),
IIO_ENUM("enable_mode", IIO_SEPARATE, &stm32_enable_mode_enum),
IIO_ENUM_AVAILABLE("enable_mode", &stm32_enable_mode_enum),
{}
};
static const struct iio_chan_spec stm32_trigger_channel = {
.type = IIO_COUNT,
.channel = 0,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
.ext_info = stm32_trigger_count_info,
.indexed = 1
};
static struct stm32_timer_trigger *stm32_setup_counter_device(struct device *dev)
{
struct iio_dev *indio_dev;
int ret;
indio_dev = devm_iio_device_alloc(dev,
sizeof(struct stm32_timer_trigger));
if (!indio_dev)
return NULL;
indio_dev->name = dev_name(dev);
indio_dev->dev.parent = dev;
indio_dev->info = &stm32_trigger_info;
indio_dev->num_channels = 1;
indio_dev->channels = &stm32_trigger_channel;
indio_dev->dev.of_node = dev->of_node;
ret = devm_iio_device_register(dev, indio_dev);
if (ret)
return NULL;
return iio_priv(indio_dev);
}
/**
* is_stm32_timer_trigger
* @trig: trigger to be checked
@ -299,10 +595,15 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev)
if (of_property_read_u32(dev->of_node, "reg", &index))
return -EINVAL;
if (index >= ARRAY_SIZE(triggers_table))
if (index >= ARRAY_SIZE(triggers_table) ||
index >= ARRAY_SIZE(valids_table))
return -EINVAL;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
/* Create an IIO device only if we have triggers to be validated */
if (*valids_table[index])
priv = stm32_setup_counter_device(dev);
else
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@ -312,6 +613,7 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev)
priv->clk = ddata->clk;
priv->max_arr = ddata->max_arr;
priv->triggers = triggers_table[index];
priv->valids = valids_table[index];
ret = stm32_setup_iio_triggers(priv);
if (ret)