From d2a3e0931a8f3b95b910096d022ffd98adbd075c Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Thu, 15 Oct 2015 15:10:32 +0200 Subject: iio: mma8452: support either of the available interrupt pins This change is important in order for everyone to be easily able to use the driver for one of the supported accelerometer chips! Until now, the driver blindly assumed that the INT1 interrupt line is wired on a user's board. But these devices have 2 interrupt lines and can route their interrupt sources to one of them. Now, if "INT2" is found and matches i2c_client->irq, INT2 will be used. The chip's default actually is INT2, which is why probably many boards will have it wired and can make use of this. Of course, this also falls back to assuming INT1, so for existing users nothing will break. The new functionality is described in the bindings doc. Signed-off-by: Martin Kepplinger For the binding: Acked-by: Rob Herring Signed-off-by: Jonathan Cameron diff --git a/Documentation/devicetree/bindings/iio/accel/mma8452.txt b/Documentation/devicetree/bindings/iio/accel/mma8452.txt index e3c3746..3c10e85 100644 --- a/Documentation/devicetree/bindings/iio/accel/mma8452.txt +++ b/Documentation/devicetree/bindings/iio/accel/mma8452.txt @@ -7,13 +7,18 @@ Required properties: * "fsl,mma8453" * "fsl,mma8652" * "fsl,mma8653" + - reg: the I2C address of the chip Optional properties: - interrupt-parent: should be the phandle for the interrupt controller + - interrupts: interrupt mapping for GPIO IRQ + - interrupt-names: should contain "INT1" and/or "INT2", the accelerometer's + interrupt line in use. + Example: mma8453fc@1d { @@ -21,4 +26,5 @@ Example: reg = <0x1d>; interrupt-parent = <&gpio1>; interrupts = <5 0>; + interrupt-names = "INT2"; }; diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 1eccc2d..116a6e4 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -29,6 +29,7 @@ #include #include #include +#include #define MMA8452_STATUS 0x00 #define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0)) @@ -1130,13 +1131,21 @@ static int mma8452_probe(struct i2c_client *client, MMA8452_INT_FF_MT; int enabled_interrupts = MMA8452_INT_TRANS | MMA8452_INT_FF_MT; + int irq2; - /* Assume wired to INT1 pin */ - ret = i2c_smbus_write_byte_data(client, - MMA8452_CTRL_REG5, - supported_interrupts); - if (ret < 0) - return ret; + irq2 = of_irq_get_byname(client->dev.of_node, "INT2"); + + if (irq2 == client->irq) { + dev_dbg(&client->dev, "using interrupt line INT2\n"); + } else { + ret = i2c_smbus_write_byte_data(client, + MMA8452_CTRL_REG5, + supported_interrupts); + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "using interrupt line INT1\n"); + } ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG4, -- cgit v0.10.2 From 75b6548f1793c7a79a8b063cd575df9c04dcc122 Mon Sep 17 00:00:00 2001 From: Teodora Baluta Date: Thu, 22 Oct 2015 15:44:50 +0300 Subject: iio: accel: add support for Memsic MXC6255XC sensor This patch adds a minimal implementation for the Memsic MXC6255XC orientation sensing accelerometer. The supported operations are reading raw acceleration values for X/Y axis that can be scaled using the exposed scale. Signed-off-by: Teodora Baluta Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 969428d..75ac087 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -158,6 +158,17 @@ config MXC4005 To compile this driver as a module, choose M. The module will be called mxc4005. +config MXC6255 + tristate "Memsic MXC6255 Orientation Sensing Accelerometer Driver" + depends on I2C + select REGMAP_I2C + help + Say yes here to build support for the Memsic MXC6255 Orientation + Sensing Accelerometer Driver. + + To compile this driver as a module, choose M here: the module will be + called mxc6255. + config STK8312 tristate "Sensortek STK8312 3-Axis Accelerometer Driver" depends on I2C diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 7925f16..525ed52 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_MMA9551) += mma9551.o obj-$(CONFIG_MMA9553) += mma9553.o obj-$(CONFIG_MXC4005) += mxc4005.o +obj-$(CONFIG_MXC6255) += mxc6255.o obj-$(CONFIG_STK8312) += stk8312.o obj-$(CONFIG_STK8BA50) += stk8ba50.o diff --git a/drivers/iio/accel/mxc6255.c b/drivers/iio/accel/mxc6255.c new file mode 100644 index 0000000..97ccde7 --- /dev/null +++ b/drivers/iio/accel/mxc6255.c @@ -0,0 +1,198 @@ +/* + * MXC6255 - MEMSIC orientation sensing accelerometer + * + * Copyright (c) 2015, Intel Corporation. + * + * 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. + * + * IIO driver for MXC6255 (7-bit I2C slave address 0x15). + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MXC6255_DRV_NAME "mxc6255" +#define MXC6255_REGMAP_NAME "mxc6255_regmap" + +#define MXC6255_REG_XOUT 0x00 +#define MXC6255_REG_YOUT 0x01 +#define MXC6255_REG_CHIP_ID 0x08 + +#define MXC6255_CHIP_ID 0x05 + +/* + * MXC6255 has only one measurement range: +/- 2G. + * The acceleration output is an 8-bit value. + * + * Scale is calculated as follows: + * (2 + 2) * 9.80665 / (2^8 - 1) = 0.153829 + * + * Scale value for +/- 2G measurement range + */ +#define MXC6255_SCALE 153829 + +enum mxc6255_axis { + AXIS_X, + AXIS_Y, +}; + +struct mxc6255_data { + struct i2c_client *client; + struct regmap *regmap; +}; + +static int mxc6255_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mxc6255_data *data = iio_priv(indio_dev); + unsigned int reg; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(data->regmap, chan->address, ®); + if (ret < 0) { + dev_err(&data->client->dev, + "Error reading reg %lu\n", chan->address); + return ret; + } + + *val = sign_extend32(reg, 7); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = MXC6255_SCALE; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info mxc6255_info = { + .driver_module = THIS_MODULE, + .read_raw = mxc6255_read_raw, +}; + +#define MXC6255_CHANNEL(_axis, reg) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .address = reg, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec mxc6255_channels[] = { + MXC6255_CHANNEL(X, MXC6255_REG_XOUT), + MXC6255_CHANNEL(Y, MXC6255_REG_YOUT), +}; + +static bool mxc6255_is_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MXC6255_REG_XOUT: + case MXC6255_REG_YOUT: + case MXC6255_REG_CHIP_ID: + return true; + default: + return false; + } +} + +static const struct regmap_config mxc6255_regmap_config = { + .name = MXC6255_REGMAP_NAME, + + .reg_bits = 8, + .val_bits = 8, + + .readable_reg = mxc6255_is_readable_reg, +}; + +static int mxc6255_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mxc6255_data *data; + struct iio_dev *indio_dev; + struct regmap *regmap; + unsigned int chip_id; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + regmap = devm_regmap_init_i2c(client, &mxc6255_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Error initializing regmap\n"); + return PTR_ERR(regmap); + } + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + data->regmap = regmap; + + indio_dev->name = MXC6255_DRV_NAME; + indio_dev->dev.parent = &client->dev; + indio_dev->channels = mxc6255_channels; + indio_dev->num_channels = ARRAY_SIZE(mxc6255_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mxc6255_info; + + ret = regmap_read(data->regmap, MXC6255_REG_CHIP_ID, &chip_id); + if (ret < 0) { + dev_err(&client->dev, "Error reading chip id %d\n", ret); + return ret; + } + + if (chip_id != MXC6255_CHIP_ID) { + dev_err(&client->dev, "Invalid chip id %x\n", chip_id); + return -ENODEV; + } + + dev_dbg(&client->dev, "Chip id %x\n", chip_id); + + ret = devm_iio_device_register(&client->dev, indio_dev); + if (ret < 0) { + dev_err(&client->dev, "Could not register IIO device\n"); + return ret; + } + + return 0; +} + +static const struct acpi_device_id mxc6255_acpi_match[] = { + {"MXC6255", 0}, + { } +}; +MODULE_DEVICE_TABLE(acpi, mxc6255_acpi_match); + +static const struct i2c_device_id mxc6255_id[] = { + {"mxc6255", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, mxc6255_id); + +static struct i2c_driver mxc6255_driver = { + .driver = { + .name = MXC6255_DRV_NAME, + .acpi_match_table = ACPI_PTR(mxc6255_acpi_match), + }, + .probe = mxc6255_probe, + .id_table = mxc6255_id, +}; + +module_i2c_driver(mxc6255_driver); + +MODULE_AUTHOR("Teodora Baluta "); +MODULE_DESCRIPTION("MEMSIC MXC6255 orientation sensing accelerometer driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From e08e19c331fb249e6dc86365ee80d16045c4aeb1 Mon Sep 17 00:00:00 2001 From: "H. Nikolaus Schaller" Date: Fri, 16 Oct 2015 14:53:38 +0200 Subject: iio:adc: add iio driver for Palmas (twl6035/7) gpadc This driver code was found as: https://android.googlesource.com/kernel/tegra/+/aaabb2e045f31e5a970109ffdaae900dd403d17e/drivers/staging/iio/adc Fixed various compilation issues and test this driver on omap5 evm. Signed-off-by: Pradeep Goudagunta Signed-off-by: H. Nikolaus Schaller Signed-off-by: Marek Belisko Acked-by: Laxman Dewangan Reviewed-by: Jonathan Cameron Acked-by: Lee Jones Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 7868c74..daad72e 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -275,6 +275,14 @@ config NAU7802 To compile this driver as a module, choose M here: the module will be called nau7802. +config PALMAS_GPADC + tristate "TI Palmas General Purpose ADC" + depends on MFD_PALMAS + help + Palmas series pmic chip by Texas Instruments (twl6035/6037) + is used in smartphones and tablets and supports a 16 channel + general purpose ADC. + config QCOM_SPMI_IADC tristate "Qualcomm SPMI PMIC current ADC" depends on SPMI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 99b37a9..11cfdfd 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.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_SPMI_VADC) += qcom-spmi-vadc.o obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c new file mode 100644 index 0000000..71763c5 --- /dev/null +++ b/drivers/iio/adc/palmas_gpadc.c @@ -0,0 +1,817 @@ +/* + * palmas-adc.c -- TI PALMAS GPADC. + * + * Copyright (c) 2013, NVIDIA Corporation. All rights reserved. + * + * Author: Pradeep Goudagunta + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MOD_NAME "palmas-gpadc" +#define PALMAS_ADC_CONVERSION_TIMEOUT (msecs_to_jiffies(5000)) +#define PALMAS_TO_BE_CALCULATED 0 +#define PALMAS_GPADC_TRIMINVALID -1 + +struct palmas_gpadc_info { +/* calibration codes and regs */ + int x1; /* lower ideal code */ + int x2; /* higher ideal code */ + int v1; /* expected lower volt reading */ + int v2; /* expected higher volt reading */ + u8 trim1_reg; /* register number for lower trim */ + u8 trim2_reg; /* register number for upper trim */ + int gain; /* calculated from above (after reading trim regs) */ + int offset; /* calculated from above (after reading trim regs) */ + int gain_error; /* calculated from above (after reading trim regs) */ + bool is_uncalibrated; /* if channel has calibration data */ +}; + +#define PALMAS_ADC_INFO(_chan, _x1, _x2, _v1, _v2, _t1, _t2, _is_uncalibrated) \ + [PALMAS_ADC_CH_##_chan] = { \ + .x1 = _x1, \ + .x2 = _x2, \ + .v1 = _v1, \ + .v2 = _v2, \ + .gain = PALMAS_TO_BE_CALCULATED, \ + .offset = PALMAS_TO_BE_CALCULATED, \ + .gain_error = PALMAS_TO_BE_CALCULATED, \ + .trim1_reg = PALMAS_GPADC_TRIM##_t1, \ + .trim2_reg = PALMAS_GPADC_TRIM##_t2, \ + .is_uncalibrated = _is_uncalibrated \ + } + +static struct palmas_gpadc_info palmas_gpadc_info[] = { + PALMAS_ADC_INFO(IN0, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN1, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN2, 2064, 3112, 1260, 1900, 3, 4, false), + PALMAS_ADC_INFO(IN3, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN4, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN5, 2064, 3112, 630, 950, 1, 2, false), + PALMAS_ADC_INFO(IN6, 2064, 3112, 2520, 3800, 5, 6, false), + PALMAS_ADC_INFO(IN7, 2064, 3112, 2520, 3800, 7, 8, false), + PALMAS_ADC_INFO(IN8, 2064, 3112, 3150, 4750, 9, 10, false), + PALMAS_ADC_INFO(IN9, 2064, 3112, 5670, 8550, 11, 12, false), + PALMAS_ADC_INFO(IN10, 2064, 3112, 3465, 5225, 13, 14, false), + PALMAS_ADC_INFO(IN11, 0, 0, 0, 0, INVALID, INVALID, true), + PALMAS_ADC_INFO(IN12, 0, 0, 0, 0, INVALID, INVALID, true), + PALMAS_ADC_INFO(IN13, 0, 0, 0, 0, INVALID, INVALID, true), + PALMAS_ADC_INFO(IN14, 2064, 3112, 3645, 5225, 15, 16, false), + PALMAS_ADC_INFO(IN15, 0, 0, 0, 0, INVALID, INVALID, true), +}; + +/** + * struct palmas_gpadc - the palmas_gpadc structure + * @ch0_current: channel 0 current source setting + * 0: 0 uA + * 1: 5 uA + * 2: 15 uA + * 3: 20 uA + * @ch3_current: channel 0 current source setting + * 0: 0 uA + * 1: 10 uA + * 2: 400 uA + * 3: 800 uA + * @extended_delay: enable the gpadc extended delay mode + * @auto_conversion_period: define the auto_conversion_period + * + * This is the palmas_gpadc structure to store run-time information + * and pointers for this driver instance. + */ + +struct palmas_gpadc { + struct device *dev; + struct palmas *palmas; + u8 ch0_current; + u8 ch3_current; + bool extended_delay; + int irq; + int irq_auto_0; + int irq_auto_1; + struct palmas_gpadc_info *adc_info; + struct completion conv_completion; + struct palmas_adc_wakeup_property wakeup1_data; + struct palmas_adc_wakeup_property wakeup2_data; + bool wakeup1_enable; + bool wakeup2_enable; + int auto_conversion_period; +}; + +/* + * GPADC lock issue in AUTO mode. + * Impact: In AUTO mode, GPADC conversion can be locked after disabling AUTO + * mode feature. + * Details: + * When the AUTO mode is the only conversion mode enabled, if the AUTO + * mode feature is disabled with bit GPADC_AUTO_CTRL. AUTO_CONV1_EN = 0 + * or bit GPADC_AUTO_CTRL. AUTO_CONV0_EN = 0 during a conversion, the + * conversion mechanism can be seen as locked meaning that all following + * conversion will give 0 as a result. Bit GPADC_STATUS.GPADC_AVAILABLE + * will stay at 0 meaning that GPADC is busy. An RT conversion can unlock + * the GPADC. + * + * Workaround(s): + * To avoid the lock mechanism, the workaround to follow before any stop + * conversion request is: + * Force the GPADC state machine to be ON by using the GPADC_CTRL1. + * GPADC_FORCE bit = 1 + * Shutdown the GPADC AUTO conversion using + * GPADC_AUTO_CTRL.SHUTDOWN_CONV[01] = 0. + * After 100us, force the GPADC state machine to be OFF by using the + * GPADC_CTRL1. GPADC_FORCE bit = 0 + */ + +static int palmas_disable_auto_conversion(struct palmas_gpadc *adc) +{ + int ret; + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_CTRL1, + PALMAS_GPADC_CTRL1_GPADC_FORCE, + PALMAS_GPADC_CTRL1_GPADC_FORCE); + if (ret < 0) { + dev_err(adc->dev, "GPADC_CTRL1 update failed: %d\n", ret); + return ret; + } + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_CTRL, + PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV1 | + PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV0, + 0); + if (ret < 0) { + dev_err(adc->dev, "AUTO_CTRL update failed: %d\n", ret); + return ret; + } + + udelay(100); + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_CTRL1, + PALMAS_GPADC_CTRL1_GPADC_FORCE, 0); + if (ret < 0) + dev_err(adc->dev, "GPADC_CTRL1 update failed: %d\n", ret); + + return ret; +} + +static irqreturn_t palmas_gpadc_irq(int irq, void *data) +{ + struct palmas_gpadc *adc = data; + + complete(&adc->conv_completion); + + return IRQ_HANDLED; +} + +static irqreturn_t palmas_gpadc_irq_auto(int irq, void *data) +{ + struct palmas_gpadc *adc = data; + + dev_dbg(adc->dev, "Threshold interrupt %d occurs\n", irq); + palmas_disable_auto_conversion(adc); + + return IRQ_HANDLED; +} + +static int palmas_gpadc_start_mask_interrupt(struct palmas_gpadc *adc, + bool mask) +{ + int ret; + + if (!mask) + ret = palmas_update_bits(adc->palmas, PALMAS_INTERRUPT_BASE, + PALMAS_INT3_MASK, + PALMAS_INT3_MASK_GPADC_EOC_SW, 0); + else + ret = palmas_update_bits(adc->palmas, PALMAS_INTERRUPT_BASE, + PALMAS_INT3_MASK, + PALMAS_INT3_MASK_GPADC_EOC_SW, + PALMAS_INT3_MASK_GPADC_EOC_SW); + if (ret < 0) + dev_err(adc->dev, "GPADC INT MASK update failed: %d\n", ret); + + return ret; +} + +static int palmas_gpadc_enable(struct palmas_gpadc *adc, int adc_chan, + int enable) +{ + unsigned int mask, val; + int ret; + + if (enable) { + val = (adc->extended_delay + << PALMAS_GPADC_RT_CTRL_EXTEND_DELAY_SHIFT); + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_RT_CTRL, + PALMAS_GPADC_RT_CTRL_EXTEND_DELAY, val); + if (ret < 0) { + dev_err(adc->dev, "RT_CTRL update failed: %d\n", ret); + return ret; + } + + mask = (PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_MASK | + PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_MASK | + PALMAS_GPADC_CTRL1_GPADC_FORCE); + val = (adc->ch0_current + << PALMAS_GPADC_CTRL1_CURRENT_SRC_CH0_SHIFT); + val |= (adc->ch3_current + << PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_SHIFT); + val |= PALMAS_GPADC_CTRL1_GPADC_FORCE; + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_CTRL1, mask, val); + if (ret < 0) { + dev_err(adc->dev, + "Failed to update current setting: %d\n", ret); + return ret; + } + + mask = (PALMAS_GPADC_SW_SELECT_SW_CONV0_SEL_MASK | + PALMAS_GPADC_SW_SELECT_SW_CONV_EN); + val = (adc_chan | PALMAS_GPADC_SW_SELECT_SW_CONV_EN); + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_SELECT, mask, val); + if (ret < 0) { + dev_err(adc->dev, "SW_SELECT update failed: %d\n", ret); + return ret; + } + } else { + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_SELECT, 0); + if (ret < 0) + dev_err(adc->dev, "SW_SELECT write failed: %d\n", ret); + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_CTRL1, + PALMAS_GPADC_CTRL1_GPADC_FORCE, 0); + if (ret < 0) { + dev_err(adc->dev, "CTRL1 update failed: %d\n", ret); + return ret; + } + } + + return ret; +} + +static int palmas_gpadc_read_prepare(struct palmas_gpadc *adc, int adc_chan) +{ + int ret; + + ret = palmas_gpadc_enable(adc, adc_chan, true); + if (ret < 0) + return ret; + + return palmas_gpadc_start_mask_interrupt(adc, 0); +} + +static void palmas_gpadc_read_done(struct palmas_gpadc *adc, int adc_chan) +{ + palmas_gpadc_start_mask_interrupt(adc, 1); + palmas_gpadc_enable(adc, adc_chan, false); +} + +static int palmas_gpadc_calibrate(struct palmas_gpadc *adc, int adc_chan) +{ + int k; + int d1; + int d2; + int ret; + int gain; + int x1 = adc->adc_info[adc_chan].x1; + int x2 = adc->adc_info[adc_chan].x2; + int v1 = adc->adc_info[adc_chan].v1; + int v2 = adc->adc_info[adc_chan].v2; + + ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE, + adc->adc_info[adc_chan].trim1_reg, &d1); + if (ret < 0) { + dev_err(adc->dev, "TRIM read failed: %d\n", ret); + goto scrub; + } + + ret = palmas_read(adc->palmas, PALMAS_TRIM_GPADC_BASE, + adc->adc_info[adc_chan].trim2_reg, &d2); + if (ret < 0) { + dev_err(adc->dev, "TRIM read failed: %d\n", ret); + goto scrub; + } + + /* gain error calculation */ + k = (1000 + (1000 * (d2 - d1)) / (x2 - x1)); + + /* gain calculation */ + gain = ((v2 - v1) * 1000) / (x2 - x1); + + adc->adc_info[adc_chan].gain_error = k; + adc->adc_info[adc_chan].gain = gain; + /* offset Calculation */ + adc->adc_info[adc_chan].offset = (d1 * 1000) - ((k - 1000) * x1); + +scrub: + return ret; +} + +static int palmas_gpadc_start_conversion(struct palmas_gpadc *adc, int adc_chan) +{ + unsigned int val; + int ret; + + init_completion(&adc->conv_completion); + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_SELECT, + PALMAS_GPADC_SW_SELECT_SW_START_CONV0, + PALMAS_GPADC_SW_SELECT_SW_START_CONV0); + if (ret < 0) { + dev_err(adc->dev, "SELECT_SW_START write failed: %d\n", ret); + return ret; + } + + ret = wait_for_completion_timeout(&adc->conv_completion, + PALMAS_ADC_CONVERSION_TIMEOUT); + if (ret == 0) { + dev_err(adc->dev, "conversion not completed\n"); + return -ETIMEDOUT; + } + + ret = palmas_bulk_read(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_SW_CONV0_LSB, &val, 2); + if (ret < 0) { + dev_err(adc->dev, "SW_CONV0_LSB read failed: %d\n", ret); + return ret; + } + + ret = val & 0xFFF; + + return ret; +} + +static int palmas_gpadc_get_calibrated_code(struct palmas_gpadc *adc, + int adc_chan, int val) +{ + if (!adc->adc_info[adc_chan].is_uncalibrated) + val = (val*1000 - adc->adc_info[adc_chan].offset) / + adc->adc_info[adc_chan].gain_error; + + if (val < 0) { + dev_err(adc->dev, "Mismatch with calibration\n"); + return 0; + } + + val = (val * adc->adc_info[adc_chan].gain) / 1000; + + return val; +} + +static int palmas_gpadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long mask) +{ + struct palmas_gpadc *adc = iio_priv(indio_dev); + int adc_chan = chan->channel; + int ret = 0; + + if (adc_chan > PALMAS_ADC_CH_MAX) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_PROCESSED: + ret = palmas_gpadc_read_prepare(adc, adc_chan); + if (ret < 0) + goto out; + + ret = palmas_gpadc_start_conversion(adc, adc_chan); + if (ret < 0) { + dev_err(adc->dev, + "ADC start conversion failed\n"); + goto out; + } + + if (mask == IIO_CHAN_INFO_PROCESSED) + ret = palmas_gpadc_get_calibrated_code( + adc, adc_chan, ret); + + *val = ret; + + ret = IIO_VAL_INT; + goto out; + } + + mutex_unlock(&indio_dev->mlock); + return ret; + +out: + palmas_gpadc_read_done(adc, adc_chan); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static const struct iio_info palmas_gpadc_iio_info = { + .read_raw = palmas_gpadc_read_raw, + .driver_module = THIS_MODULE, +}; + +#define PALMAS_ADC_CHAN_IIO(chan, _type, chan_info) \ +{ \ + .datasheet_name = PALMAS_DATASHEET_NAME(chan), \ + .type = _type, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(chan_info), \ + .indexed = 1, \ + .channel = PALMAS_ADC_CH_##chan, \ +} + +static const struct iio_chan_spec palmas_gpadc_iio_channel[] = { + PALMAS_ADC_CHAN_IIO(IN0, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN1, IIO_TEMP, IIO_CHAN_INFO_RAW), + PALMAS_ADC_CHAN_IIO(IN2, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN3, IIO_TEMP, IIO_CHAN_INFO_RAW), + PALMAS_ADC_CHAN_IIO(IN4, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN5, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN6, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN7, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN8, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN9, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN10, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN11, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN12, IIO_TEMP, IIO_CHAN_INFO_RAW), + PALMAS_ADC_CHAN_IIO(IN13, IIO_TEMP, IIO_CHAN_INFO_RAW), + PALMAS_ADC_CHAN_IIO(IN14, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), + PALMAS_ADC_CHAN_IIO(IN15, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), +}; + +static int palmas_gpadc_probe(struct platform_device *pdev) +{ + struct palmas_gpadc *adc; + struct palmas_platform_data *pdata; + struct palmas_gpadc_platform_data *gpadc_pdata = NULL; + struct iio_dev *indio_dev; + int ret, i; + + pdata = dev_get_platdata(pdev->dev.parent); + if (!pdata || !pdata->gpadc_pdata) { + dev_err(&pdev->dev, "No platform data\n"); + return -ENODEV; + } + + gpadc_pdata = pdata->gpadc_pdata; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); + if (!indio_dev) { + dev_err(&pdev->dev, "iio_device_alloc failed\n"); + return -ENOMEM; + } + + adc = iio_priv(indio_dev); + adc->dev = &pdev->dev; + adc->palmas = dev_get_drvdata(pdev->dev.parent); + adc->adc_info = palmas_gpadc_info; + init_completion(&adc->conv_completion); + dev_set_drvdata(&pdev->dev, indio_dev); + + adc->auto_conversion_period = gpadc_pdata->auto_conversion_period_ms; + adc->irq = palmas_irq_get_virq(adc->palmas, PALMAS_GPADC_EOC_SW_IRQ); + if (adc->irq < 0) { + dev_err(adc->dev, + "get virq failed: %d\n", adc->irq); + ret = adc->irq; + goto out; + } + ret = request_threaded_irq(adc->irq, NULL, + palmas_gpadc_irq, + IRQF_ONESHOT | IRQF_EARLY_RESUME, dev_name(adc->dev), + adc); + if (ret < 0) { + dev_err(adc->dev, + "request irq %d failed: %d\n", adc->irq, ret); + goto out; + } + + if (gpadc_pdata->adc_wakeup1_data) { + memcpy(&adc->wakeup1_data, gpadc_pdata->adc_wakeup1_data, + sizeof(adc->wakeup1_data)); + adc->wakeup1_enable = true; + adc->irq_auto_0 = platform_get_irq(pdev, 1); + ret = request_threaded_irq(adc->irq_auto_0, NULL, + palmas_gpadc_irq_auto, + IRQF_ONESHOT | IRQF_EARLY_RESUME, + "palmas-adc-auto-0", adc); + if (ret < 0) { + dev_err(adc->dev, "request auto0 irq %d failed: %d\n", + adc->irq_auto_0, ret); + goto out_irq_free; + } + } + + if (gpadc_pdata->adc_wakeup2_data) { + memcpy(&adc->wakeup2_data, gpadc_pdata->adc_wakeup2_data, + sizeof(adc->wakeup2_data)); + adc->wakeup2_enable = true; + adc->irq_auto_1 = platform_get_irq(pdev, 2); + ret = request_threaded_irq(adc->irq_auto_1, NULL, + palmas_gpadc_irq_auto, + IRQF_ONESHOT | IRQF_EARLY_RESUME, + "palmas-adc-auto-1", adc); + if (ret < 0) { + dev_err(adc->dev, "request auto1 irq %d failed: %d\n", + adc->irq_auto_1, ret); + goto out_irq_auto0_free; + } + } + + /* set the current source 0 (value 0/5/15/20 uA => 0..3) */ + if (gpadc_pdata->ch0_current <= 1) + adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_0; + else if (gpadc_pdata->ch0_current <= 5) + adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_5; + else if (gpadc_pdata->ch0_current <= 15) + adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_15; + else + adc->ch0_current = PALMAS_ADC_CH0_CURRENT_SRC_20; + + /* set the current source 3 (value 0/10/400/800 uA => 0..3) */ + if (gpadc_pdata->ch3_current <= 1) + adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_0; + else if (gpadc_pdata->ch3_current <= 10) + adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_10; + else if (gpadc_pdata->ch3_current <= 400) + adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_400; + else + adc->ch3_current = PALMAS_ADC_CH3_CURRENT_SRC_800; + + adc->extended_delay = gpadc_pdata->extended_delay; + + indio_dev->name = MOD_NAME; + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &palmas_gpadc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = palmas_gpadc_iio_channel; + indio_dev->num_channels = ARRAY_SIZE(palmas_gpadc_iio_channel); + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(adc->dev, "iio_device_register() failed: %d\n", ret); + goto out_irq_auto1_free; + } + + device_set_wakeup_capable(&pdev->dev, 1); + for (i = 0; i < PALMAS_ADC_CH_MAX; i++) { + if (!(adc->adc_info[i].is_uncalibrated)) + palmas_gpadc_calibrate(adc, i); + } + + if (adc->wakeup1_enable || adc->wakeup2_enable) + device_wakeup_enable(&pdev->dev); + + return 0; + +out_irq_auto1_free: + if (gpadc_pdata->adc_wakeup2_data) + free_irq(adc->irq_auto_1, adc); +out_irq_auto0_free: + if (gpadc_pdata->adc_wakeup1_data) + free_irq(adc->irq_auto_0, adc); +out_irq_free: + free_irq(adc->irq, adc); +out: + return ret; +} + +static int palmas_gpadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(&pdev->dev); + struct palmas_gpadc *adc = iio_priv(indio_dev); + + if (adc->wakeup1_enable || adc->wakeup2_enable) + device_wakeup_disable(&pdev->dev); + iio_device_unregister(indio_dev); + free_irq(adc->irq, adc); + if (adc->wakeup1_enable) + free_irq(adc->irq_auto_0, adc); + if (adc->wakeup2_enable) + free_irq(adc->irq_auto_1, adc); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int palmas_adc_wakeup_configure(struct palmas_gpadc *adc) +{ + int adc_period, conv; + int i; + int ch0 = 0, ch1 = 0; + int thres; + int ret; + + adc_period = adc->auto_conversion_period; + for (i = 0; i < 16; ++i) { + if (((1000 * (1 << i)) / 32) < adc_period) + continue; + } + if (i > 0) + i--; + adc_period = i; + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_CTRL, + PALMAS_GPADC_AUTO_CTRL_COUNTER_CONV_MASK, + adc_period); + if (ret < 0) { + dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret); + return ret; + } + + conv = 0; + if (adc->wakeup1_enable) { + int polarity; + + ch0 = adc->wakeup1_data.adc_channel_number; + conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN; + if (adc->wakeup1_data.adc_high_threshold > 0) { + thres = adc->wakeup1_data.adc_high_threshold; + polarity = 0; + } else { + thres = adc->wakeup1_data.adc_low_threshold; + polarity = PALMAS_GPADC_THRES_CONV0_MSB_THRES_CONV0_POL; + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_THRES_CONV0_LSB, thres & 0xFF); + if (ret < 0) { + dev_err(adc->dev, + "THRES_CONV0_LSB write failed: %d\n", ret); + return ret; + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_THRES_CONV0_MSB, + ((thres >> 8) & 0xF) | polarity); + if (ret < 0) { + dev_err(adc->dev, + "THRES_CONV0_MSB write failed: %d\n", ret); + return ret; + } + } + + if (adc->wakeup2_enable) { + int polarity; + + ch1 = adc->wakeup2_data.adc_channel_number; + conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN; + if (adc->wakeup2_data.adc_high_threshold > 0) { + thres = adc->wakeup2_data.adc_high_threshold; + polarity = 0; + } else { + thres = adc->wakeup2_data.adc_low_threshold; + polarity = PALMAS_GPADC_THRES_CONV1_MSB_THRES_CONV1_POL; + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_THRES_CONV1_LSB, thres & 0xFF); + if (ret < 0) { + dev_err(adc->dev, + "THRES_CONV1_LSB write failed: %d\n", ret); + return ret; + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_THRES_CONV1_MSB, + ((thres >> 8) & 0xF) | polarity); + if (ret < 0) { + dev_err(adc->dev, + "THRES_CONV1_MSB write failed: %d\n", ret); + return ret; + } + } + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_SELECT, (ch1 << 4) | ch0); + if (ret < 0) { + dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret); + return ret; + } + + ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_CTRL, + PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN | + PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN, conv); + if (ret < 0) + dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret); + + return ret; +} + +static int palmas_adc_wakeup_reset(struct palmas_gpadc *adc) +{ + int ret; + + ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE, + PALMAS_GPADC_AUTO_SELECT, 0); + if (ret < 0) { + dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret); + return ret; + } + + ret = palmas_disable_auto_conversion(adc); + if (ret < 0) + dev_err(adc->dev, "Disable auto conversion failed: %d\n", ret); + + return ret; +} + +static int palmas_gpadc_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct palmas_gpadc *adc = iio_priv(indio_dev); + int wakeup = adc->wakeup1_enable || adc->wakeup2_enable; + int ret; + + if (!device_may_wakeup(dev) || !wakeup) + return 0; + + ret = palmas_adc_wakeup_configure(adc); + if (ret < 0) + return ret; + + if (adc->wakeup1_enable) + enable_irq_wake(adc->irq_auto_0); + + if (adc->wakeup2_enable) + enable_irq_wake(adc->irq_auto_1); + + return 0; +} + +static int palmas_gpadc_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct palmas_gpadc *adc = iio_priv(indio_dev); + int wakeup = adc->wakeup1_enable || adc->wakeup2_enable; + int ret; + + if (!device_may_wakeup(dev) || !wakeup) + return 0; + + ret = palmas_adc_wakeup_reset(adc); + if (ret < 0) + return ret; + + if (adc->wakeup1_enable) + disable_irq_wake(adc->irq_auto_0); + + if (adc->wakeup2_enable) + disable_irq_wake(adc->irq_auto_1); + + return 0; +}; +#endif + +static const struct dev_pm_ops palmas_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(palmas_gpadc_suspend, + palmas_gpadc_resume) +}; + +static struct platform_driver palmas_gpadc_driver = { + .probe = palmas_gpadc_probe, + .remove = palmas_gpadc_remove, + .driver = { + .name = MOD_NAME, + .pm = &palmas_pm_ops, + }, +}; + +static int __init palmas_gpadc_init(void) +{ + return platform_driver_register(&palmas_gpadc_driver); +} +module_init(palmas_gpadc_init); + +static void __exit palmas_gpadc_exit(void) +{ + platform_driver_unregister(&palmas_gpadc_driver); +} +module_exit(palmas_gpadc_exit); + +MODULE_DESCRIPTION("palmas GPADC driver"); +MODULE_AUTHOR("Pradeep Goudagunta"); +MODULE_ALIAS("platform:palmas-gpadc"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index 13e1d96..c800dbc 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -134,21 +134,32 @@ struct palmas_pmic_driver_data { struct regulator_config config); }; +struct palmas_adc_wakeup_property { + int adc_channel_number; + int adc_high_threshold; + int adc_low_threshold; +}; + struct palmas_gpadc_platform_data { /* Channel 3 current source is only enabled during conversion */ - int ch3_current; + int ch3_current; /* 0: off; 1: 10uA; 2: 400uA; 3: 800 uA */ /* Channel 0 current source can be used for battery detection. * If used for battery detection this will cause a permanent current * consumption depending on current level set here. */ - int ch0_current; + int ch0_current; /* 0: off; 1: 5uA; 2: 15uA; 3: 20 uA */ + bool extended_delay; /* use extended delay for conversion */ /* default BAT_REMOVAL_DAT setting on device probe */ int bat_removal; /* Sets the START_POLARITY bit in the RT_CTRL register */ int start_polarity; + + int auto_conversion_period_ms; + struct palmas_adc_wakeup_property *adc_wakeup1_data; + struct palmas_adc_wakeup_property *adc_wakeup2_data; }; struct palmas_reg_init { @@ -405,28 +416,7 @@ struct palmas_gpadc_calibration { s32 offset_error; }; -struct palmas_gpadc { - struct device *dev; - struct palmas *palmas; - - int ch3_current; - int ch0_current; - - int gpadc_force; - - int bat_removal; - - struct mutex reading_lock; - struct completion irq_complete; - - int eoc_sw_irq; - - struct palmas_gpadc_calibration *palmas_cal_tbl; - - int conv0_channel; - int conv1_channel; - int rt_channel; -}; +#define PALMAS_DATASHEET_NAME(_name) "palmas-gpadc-chan-"#_name struct palmas_gpadc_result { s32 raw_code; @@ -520,6 +510,43 @@ enum palmas_irqs { PALMAS_NUM_IRQ, }; +/* Palmas GPADC Channels */ +enum { + PALMAS_ADC_CH_IN0, + PALMAS_ADC_CH_IN1, + PALMAS_ADC_CH_IN2, + PALMAS_ADC_CH_IN3, + PALMAS_ADC_CH_IN4, + PALMAS_ADC_CH_IN5, + PALMAS_ADC_CH_IN6, + PALMAS_ADC_CH_IN7, + PALMAS_ADC_CH_IN8, + PALMAS_ADC_CH_IN9, + PALMAS_ADC_CH_IN10, + PALMAS_ADC_CH_IN11, + PALMAS_ADC_CH_IN12, + PALMAS_ADC_CH_IN13, + PALMAS_ADC_CH_IN14, + PALMAS_ADC_CH_IN15, + PALMAS_ADC_CH_MAX, +}; + +/* Palmas GPADC Channel0 Current Source */ +enum { + PALMAS_ADC_CH0_CURRENT_SRC_0, + PALMAS_ADC_CH0_CURRENT_SRC_5, + PALMAS_ADC_CH0_CURRENT_SRC_15, + PALMAS_ADC_CH0_CURRENT_SRC_20, +}; + +/* Palmas GPADC Channel3 Current Source */ +enum { + PALMAS_ADC_CH3_CURRENT_SRC_0, + PALMAS_ADC_CH3_CURRENT_SRC_10, + PALMAS_ADC_CH3_CURRENT_SRC_400, + PALMAS_ADC_CH3_CURRENT_SRC_800, +}; + struct palmas_pmic { struct palmas *palmas; struct device *dev; -- cgit v0.10.2 From f0b1643581b376ebd97a0068cbc3d146d6abdff1 Mon Sep 17 00:00:00 2001 From: Marek Belisko Date: Fri, 16 Oct 2015 14:53:39 +0200 Subject: iio:adc:palmas: add DT support Code was found at: https://android.googlesource.com/kernel/tegra/+/a90856a6626d502d42c6e7abccbdf9d730b36270%5E%21/#F1 Signed-off-by: Laxman Dewangan Signed-off-by: Marek Belisko [Fixed minor typos + add channels list to documentation] Signed-off-by: Jonathan Cameron diff --git a/Documentation/devicetree/bindings/iio/adc/palmas-gpadc.txt b/Documentation/devicetree/bindings/iio/adc/palmas-gpadc.txt new file mode 100644 index 0000000..4bb9a86 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/palmas-gpadc.txt @@ -0,0 +1,48 @@ +* Palmas general purpose ADC IP block devicetree bindings + +Channels list: + 0 battery type + 1 battery temp NTC (optional current source) + 2 GP + 3 temp (with ext. diode, optional current source) + 4 GP + 5 GP + 6 VBAT_SENSE + 7 VCC_SENSE + 8 Backup Battery voltage + 9 external charger (VCHG) + 10 VBUS + 11 DC-DC current probe (how does this work?) + 12 internal die temp + 13 internal die temp + 14 USB ID pin voltage + 15 test network + +Required properties: +- compatible : Must be "ti,palmas-gpadc". +- #io-channel-cells: Should be set to <1>. + +Optional sub-nodes: +ti,channel0-current-microamp: Channel 0 current in uA. + Values are rounded to derive 0uA, 5uA, 15uA, 20uA. +ti,channel3-current-microamp: Channel 3 current in uA. + Values are rounded to derive 0uA, 10uA, 400uA, 800uA. +ti,enable-extended-delay: Enable extended delay. + +Example: + +pmic { + compatible = "ti,twl6035-pmic", "ti,palmas-pmic"; + ... + gpadc { + compatible = "ti,palmas-gpadc"; + interrupts = <18 0 + 16 0 + 17 0>; + #io-channel-cells = <1>; + ti,channel0-current-microamp = <5>; + ti,channel3-current-microamp = <10>; + }; + }; + ... +}; diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 71763c5..f42eb8a 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include #include #include @@ -460,6 +462,34 @@ static const struct iio_chan_spec palmas_gpadc_iio_channel[] = { PALMAS_ADC_CHAN_IIO(IN15, IIO_VOLTAGE, IIO_CHAN_INFO_PROCESSED), }; +static int palmas_gpadc_get_adc_dt_data(struct platform_device *pdev, + struct palmas_gpadc_platform_data **gpadc_pdata) +{ + struct device_node *np = pdev->dev.of_node; + struct palmas_gpadc_platform_data *gp_data; + int ret; + u32 pval; + + gp_data = devm_kzalloc(&pdev->dev, sizeof(*gp_data), GFP_KERNEL); + if (!gp_data) + return -ENOMEM; + + ret = of_property_read_u32(np, "ti,channel0-current-microamp", &pval); + if (!ret) + gp_data->ch0_current = pval; + + ret = of_property_read_u32(np, "ti,channel3-current-microamp", &pval); + if (!ret) + gp_data->ch3_current = pval; + + gp_data->extended_delay = of_property_read_bool(np, + "ti,enable-extended-delay"); + + *gpadc_pdata = gp_data; + + return 0; +} + static int palmas_gpadc_probe(struct platform_device *pdev) { struct palmas_gpadc *adc; @@ -469,12 +499,17 @@ static int palmas_gpadc_probe(struct platform_device *pdev) int ret, i; pdata = dev_get_platdata(pdev->dev.parent); - if (!pdata || !pdata->gpadc_pdata) { - dev_err(&pdev->dev, "No platform data\n"); - return -ENODEV; - } - gpadc_pdata = pdata->gpadc_pdata; + if (pdata && pdata->gpadc_pdata) + gpadc_pdata = pdata->gpadc_pdata; + + if (!gpadc_pdata && pdev->dev.of_node) { + ret = palmas_gpadc_get_adc_dt_data(pdev, &gpadc_pdata); + if (ret < 0) + return ret; + } + if (!gpadc_pdata) + return -EINVAL; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); if (!indio_dev) { @@ -790,12 +825,19 @@ static const struct dev_pm_ops palmas_pm_ops = { palmas_gpadc_resume) }; +static const struct of_device_id of_palmas_gpadc_match_tbl[] = { + { .compatible = "ti,palmas-gpadc", }, + { /* end */ } +}; +MODULE_DEVICE_TABLE(of, of_palmas_gpadc_match_tbl); + static struct platform_driver palmas_gpadc_driver = { .probe = palmas_gpadc_probe, .remove = palmas_gpadc_remove, .driver = { .name = MOD_NAME, .pm = &palmas_pm_ops, + .of_match_table = of_palmas_gpadc_match_tbl, }, }; -- cgit v0.10.2 From 415f792447572ef1949a3cef5119bbce8cc66373 Mon Sep 17 00:00:00 2001 From: Cristina Opriceana Date: Fri, 9 Oct 2015 16:31:28 +0300 Subject: iio: Move IIO Dummy Driver out of staging This patch moves the reference IIO dummy driver from drivers/staging/iio into a separate folder, drivers/iio/dummy and adds the proper Kconfig and Makefile for it. A new config menu entry called IIO dummy driver has also been added in the Industrial I/O support menu, corresponding to this driver. Signed-off-by: Cristina Opriceana Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 66792e7..6b8c77c9 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -50,6 +50,7 @@ source "drivers/iio/amplifiers/Kconfig" source "drivers/iio/chemical/Kconfig" source "drivers/iio/common/Kconfig" source "drivers/iio/dac/Kconfig" +source "drivers/iio/dummy/Kconfig" source "drivers/iio/frequency/Kconfig" source "drivers/iio/gyro/Kconfig" source "drivers/iio/humidity/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index aeca726..6769f2f 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -16,6 +16,7 @@ obj-y += buffer/ obj-y += chemical/ obj-y += common/ obj-y += dac/ +obj-y += dummy/ obj-y += gyro/ obj-y += frequency/ obj-y += humidity/ diff --git a/drivers/iio/dummy/Kconfig b/drivers/iio/dummy/Kconfig new file mode 100644 index 0000000..e8676aa --- /dev/null +++ b/drivers/iio/dummy/Kconfig @@ -0,0 +1,35 @@ +# +# Industrial I/O subsystem Dummy Driver configuration +# +menu "IIO dummy driver" + depends on IIO + +config IIO_DUMMY_EVGEN + tristate + +config IIO_SIMPLE_DUMMY + tristate "An example driver with no hardware requirements" + help + Driver intended mainly as documentation for how to write + a driver. May also be useful for testing userspace code + without hardware. + +if IIO_SIMPLE_DUMMY + +config IIO_SIMPLE_DUMMY_EVENTS + bool "Event generation support" + select IIO_DUMMY_EVGEN + help + Add some dummy events to the simple dummy driver. + +config IIO_SIMPLE_DUMMY_BUFFER + bool "Buffered capture support" + select IIO_BUFFER + select IIO_TRIGGER + select IIO_KFIFO_BUF + help + Add buffered data capture to the simple dummy driver. + +endif # IIO_SIMPLE_DUMMY + +endmenu diff --git a/drivers/iio/dummy/Makefile b/drivers/iio/dummy/Makefile new file mode 100644 index 0000000..0765e93 --- /dev/null +++ b/drivers/iio/dummy/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for the IIO Dummy Driver +# + +obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o +iio_dummy-y := iio_simple_dummy.o +iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o +iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_BUFFER) += iio_simple_dummy_buffer.o + +obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o diff --git a/drivers/iio/dummy/iio_dummy_evgen.c b/drivers/iio/dummy/iio_dummy_evgen.c new file mode 100644 index 0000000..9e83f34 --- /dev/null +++ b/drivers/iio/dummy/iio_dummy_evgen.c @@ -0,0 +1,262 @@ +/** + * Copyright (c) 2011 Jonathan Cameron + * + * 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. + * + * Companion module to the iio simple dummy example driver. + * The purpose of this is to generate 'fake' event interrupts thus + * allowing that driver's code to be as close as possible to that of + * a normal driver talking to hardware. The approach used here + * is not intended to be general and just happens to work for this + * particular use case. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "iio_dummy_evgen.h" +#include +#include +#include + +/* Fiddly bit of faking and irq without hardware */ +#define IIO_EVENTGEN_NO 10 + +/** + * struct iio_dummy_handle_irq - helper struct to simulate interrupt generation + * @work: irq_work used to run handlers from hardirq context + * @irq: fake irq line number to trigger an interrupt + */ +struct iio_dummy_handle_irq { + struct irq_work work; + int irq; +}; + +/** + * struct iio_dummy_evgen - evgen state + * @chip: irq chip we are faking + * @base: base of irq range + * @enabled: mask of which irqs are enabled + * @inuse: mask of which irqs are connected + * @regs: irq regs we are faking + * @lock: protect the evgen state + * @handler: helper for a 'hardware-like' interrupt simulation + */ +struct iio_dummy_eventgen { + struct irq_chip chip; + int base; + bool enabled[IIO_EVENTGEN_NO]; + bool inuse[IIO_EVENTGEN_NO]; + struct iio_dummy_regs regs[IIO_EVENTGEN_NO]; + struct mutex lock; + struct iio_dummy_handle_irq handler; +}; + +/* We can only ever have one instance of this 'device' */ +static struct iio_dummy_eventgen *iio_evgen; +static const char *iio_evgen_name = "iio_dummy_evgen"; + +static void iio_dummy_event_irqmask(struct irq_data *d) +{ + struct irq_chip *chip = irq_data_get_irq_chip(d); + struct iio_dummy_eventgen *evgen = + container_of(chip, struct iio_dummy_eventgen, chip); + + evgen->enabled[d->irq - evgen->base] = false; +} + +static void iio_dummy_event_irqunmask(struct irq_data *d) +{ + struct irq_chip *chip = irq_data_get_irq_chip(d); + struct iio_dummy_eventgen *evgen = + container_of(chip, struct iio_dummy_eventgen, chip); + + evgen->enabled[d->irq - evgen->base] = true; +} + +static void iio_dummy_work_handler(struct irq_work *work) +{ + struct iio_dummy_handle_irq *irq_handler; + + irq_handler = container_of(work, struct iio_dummy_handle_irq, work); + handle_simple_irq(irq_to_desc(irq_handler->irq)); +} + +static int iio_dummy_evgen_create(void) +{ + int ret, i; + + iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL); + if (!iio_evgen) + return -ENOMEM; + + iio_evgen->base = irq_alloc_descs(-1, 0, IIO_EVENTGEN_NO, 0); + if (iio_evgen->base < 0) { + ret = iio_evgen->base; + kfree(iio_evgen); + return ret; + } + iio_evgen->chip.name = iio_evgen_name; + iio_evgen->chip.irq_mask = &iio_dummy_event_irqmask; + iio_evgen->chip.irq_unmask = &iio_dummy_event_irqunmask; + for (i = 0; i < IIO_EVENTGEN_NO; i++) { + irq_set_chip(iio_evgen->base + i, &iio_evgen->chip); + irq_set_handler(iio_evgen->base + i, &handle_simple_irq); + irq_modify_status(iio_evgen->base + i, + IRQ_NOREQUEST | IRQ_NOAUTOEN, + IRQ_NOPROBE); + } + init_irq_work(&iio_evgen->handler.work, iio_dummy_work_handler); + mutex_init(&iio_evgen->lock); + return 0; +} + +/** + * iio_dummy_evgen_get_irq() - get an evgen provided irq for a device + * + * This function will give a free allocated irq to a client device. + * That irq can then be caused to 'fire' by using the associated sysfs file. + */ +int iio_dummy_evgen_get_irq(void) +{ + int i, ret = 0; + + if (!iio_evgen) + return -ENODEV; + + mutex_lock(&iio_evgen->lock); + for (i = 0; i < IIO_EVENTGEN_NO; i++) + if (!iio_evgen->inuse[i]) { + ret = iio_evgen->base + i; + iio_evgen->inuse[i] = true; + break; + } + mutex_unlock(&iio_evgen->lock); + if (i == IIO_EVENTGEN_NO) + return -ENOMEM; + return ret; +} +EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq); + +/** + * iio_dummy_evgen_release_irq() - give the irq back. + * @irq: irq being returned to the pool + * + * Used by client driver instances to give the irqs back when they disconnect + */ +void iio_dummy_evgen_release_irq(int irq) +{ + mutex_lock(&iio_evgen->lock); + iio_evgen->inuse[irq - iio_evgen->base] = false; + mutex_unlock(&iio_evgen->lock); +} +EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq); + +struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq) +{ + return &iio_evgen->regs[irq - iio_evgen->base]; +} +EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_regs); + +static void iio_dummy_evgen_free(void) +{ + irq_free_descs(iio_evgen->base, IIO_EVENTGEN_NO); + kfree(iio_evgen); +} + +static void iio_evgen_release(struct device *dev) +{ + iio_dummy_evgen_free(); +} + +static ssize_t iio_evgen_poke(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + unsigned long event; + int ret; + + ret = kstrtoul(buf, 10, &event); + if (ret) + return ret; + + iio_evgen->regs[this_attr->address].reg_id = this_attr->address; + iio_evgen->regs[this_attr->address].reg_data = event; + + iio_evgen->handler.irq = iio_evgen->base + this_attr->address; + if (iio_evgen->enabled[this_attr->address]) + irq_work_queue(&iio_evgen->handler.work); + + return len; +} + +static IIO_DEVICE_ATTR(poke_ev0, S_IWUSR, NULL, &iio_evgen_poke, 0); +static IIO_DEVICE_ATTR(poke_ev1, S_IWUSR, NULL, &iio_evgen_poke, 1); +static IIO_DEVICE_ATTR(poke_ev2, S_IWUSR, NULL, &iio_evgen_poke, 2); +static IIO_DEVICE_ATTR(poke_ev3, S_IWUSR, NULL, &iio_evgen_poke, 3); +static IIO_DEVICE_ATTR(poke_ev4, S_IWUSR, NULL, &iio_evgen_poke, 4); +static IIO_DEVICE_ATTR(poke_ev5, S_IWUSR, NULL, &iio_evgen_poke, 5); +static IIO_DEVICE_ATTR(poke_ev6, S_IWUSR, NULL, &iio_evgen_poke, 6); +static IIO_DEVICE_ATTR(poke_ev7, S_IWUSR, NULL, &iio_evgen_poke, 7); +static IIO_DEVICE_ATTR(poke_ev8, S_IWUSR, NULL, &iio_evgen_poke, 8); +static IIO_DEVICE_ATTR(poke_ev9, S_IWUSR, NULL, &iio_evgen_poke, 9); + +static struct attribute *iio_evgen_attrs[] = { + &iio_dev_attr_poke_ev0.dev_attr.attr, + &iio_dev_attr_poke_ev1.dev_attr.attr, + &iio_dev_attr_poke_ev2.dev_attr.attr, + &iio_dev_attr_poke_ev3.dev_attr.attr, + &iio_dev_attr_poke_ev4.dev_attr.attr, + &iio_dev_attr_poke_ev5.dev_attr.attr, + &iio_dev_attr_poke_ev6.dev_attr.attr, + &iio_dev_attr_poke_ev7.dev_attr.attr, + &iio_dev_attr_poke_ev8.dev_attr.attr, + &iio_dev_attr_poke_ev9.dev_attr.attr, + NULL, +}; + +static const struct attribute_group iio_evgen_group = { + .attrs = iio_evgen_attrs, +}; + +static const struct attribute_group *iio_evgen_groups[] = { + &iio_evgen_group, + NULL +}; + +static struct device iio_evgen_dev = { + .bus = &iio_bus_type, + .groups = iio_evgen_groups, + .release = &iio_evgen_release, +}; + +static __init int iio_dummy_evgen_init(void) +{ + int ret = iio_dummy_evgen_create(); + + if (ret < 0) + return ret; + device_initialize(&iio_evgen_dev); + dev_set_name(&iio_evgen_dev, "iio_evgen"); + return device_add(&iio_evgen_dev); +} +module_init(iio_dummy_evgen_init); + +static __exit void iio_dummy_evgen_exit(void) +{ + device_unregister(&iio_evgen_dev); +} +module_exit(iio_dummy_evgen_exit); + +MODULE_AUTHOR("Jonathan Cameron "); +MODULE_DESCRIPTION("IIO dummy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dummy/iio_dummy_evgen.h b/drivers/iio/dummy/iio_dummy_evgen.h new file mode 100644 index 0000000..d044b94 --- /dev/null +++ b/drivers/iio/dummy/iio_dummy_evgen.h @@ -0,0 +1,13 @@ +#ifndef _IIO_DUMMY_EVGEN_H_ +#define _IIO_DUMMY_EVGEN_H_ + +struct iio_dummy_regs { + u32 reg_id; + u32 reg_data; +}; + +struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq); +int iio_dummy_evgen_get_irq(void); +void iio_dummy_evgen_release_irq(int irq); + +#endif /* _IIO_DUMMY_EVGEN_H_ */ diff --git a/drivers/iio/dummy/iio_simple_dummy.c b/drivers/iio/dummy/iio_simple_dummy.c new file mode 100644 index 0000000..381f90f --- /dev/null +++ b/drivers/iio/dummy/iio_simple_dummy.c @@ -0,0 +1,748 @@ +/** + * Copyright (c) 2011 Jonathan Cameron + * + * 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. + * + * A reference industrial I/O driver to illustrate the functionality available. + * + * There are numerous real drivers to illustrate the finer points. + * The purpose of this driver is to provide a driver with far more comments + * and explanatory notes than any 'real' driver would have. + * Anyone starting out writing an IIO driver should first make sure they + * understand all of this driver except those bits specifically marked + * as being present to allow us to 'fake' the presence of hardware. + */ +#include +#include +#include + +#include +#include +#include +#include +#include "iio_simple_dummy.h" + +/* + * A few elements needed to fake a bus for this driver + * Note instances parameter controls how many of these + * dummy devices are registered. + */ +static unsigned instances = 1; +module_param(instances, uint, 0); + +/* Pointer array used to fake bus elements */ +static struct iio_dev **iio_dummy_devs; + +/* Fake a name for the part number, usually obtained from the id table */ +static const char *iio_dummy_part_number = "iio_dummy_part_no"; + +/** + * struct iio_dummy_accel_calibscale - realworld to register mapping + * @val: first value in read_raw - here integer part. + * @val2: second value in read_raw etc - here micro part. + * @regval: register value - magic device specific numbers. + */ +struct iio_dummy_accel_calibscale { + int val; + int val2; + int regval; /* what would be written to hardware */ +}; + +static const struct iio_dummy_accel_calibscale dummy_scales[] = { + { 0, 100, 0x8 }, /* 0.000100 */ + { 0, 133, 0x7 }, /* 0.000133 */ + { 733, 13, 0x9 }, /* 733.000013 */ +}; + +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS + +/* + * simple event - triggered when value rises above + * a threshold + */ +static const struct iio_event_spec iio_dummy_event = { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), +}; + +/* + * simple step detect event - triggered when a step is detected + */ +static const struct iio_event_spec step_detect_event = { + .type = IIO_EV_TYPE_CHANGE, + .dir = IIO_EV_DIR_NONE, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), +}; + +/* + * simple transition event - triggered when the reported running confidence + * value rises above a threshold value + */ +static const struct iio_event_spec iio_running_event = { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), +}; + +/* + * simple transition event - triggered when the reported walking confidence + * value falls under a threshold value + */ +static const struct iio_event_spec iio_walking_event = { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), +}; +#endif + +/* + * iio_dummy_channels - Description of available channels + * + * This array of structures tells the IIO core about what the device + * actually provides for a given channel. + */ +static const struct iio_chan_spec iio_dummy_channels[] = { + /* indexed ADC channel in_voltage0_raw etc */ + { + .type = IIO_VOLTAGE, + /* Channel has a numeric index of 0 */ + .indexed = 1, + .channel = 0, + /* What other information is available? */ + .info_mask_separate = + /* + * in_voltage0_raw + * Raw (unscaled no bias removal etc) measurement + * from the device. + */ + BIT(IIO_CHAN_INFO_RAW) | + /* + * in_voltage0_offset + * Offset for userspace to apply prior to scale + * when converting to standard units (microvolts) + */ + BIT(IIO_CHAN_INFO_OFFSET) | + /* + * in_voltage0_scale + * Multipler for userspace to apply post offset + * when converting to standard units (microvolts) + */ + BIT(IIO_CHAN_INFO_SCALE), + /* + * sampling_frequency + * The frequency in Hz at which the channels are sampled + */ + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), + /* The ordering of elements in the buffer via an enum */ + .scan_index = voltage0, + .scan_type = { /* Description of storage in buffer */ + .sign = 'u', /* unsigned */ + .realbits = 13, /* 13 bits */ + .storagebits = 16, /* 16 bits used for storage */ + .shift = 0, /* zero shift */ + }, +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS + .event_spec = &iio_dummy_event, + .num_event_specs = 1, +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ + }, + /* Differential ADC channel in_voltage1-voltage2_raw etc*/ + { + .type = IIO_VOLTAGE, + .differential = 1, + /* + * Indexing for differential channels uses channel + * for the positive part, channel2 for the negative. + */ + .indexed = 1, + .channel = 1, + .channel2 = 2, + /* + * in_voltage1-voltage2_raw + * Raw (unscaled no bias removal etc) measurement + * from the device. + */ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + /* + * in_voltage-voltage_scale + * Shared version of scale - shared by differential + * input channels of type IIO_VOLTAGE. + */ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + /* + * sampling_frequency + * The frequency in Hz at which the channels are sampled + */ + .scan_index = diffvoltage1m2, + .scan_type = { /* Description of storage in buffer */ + .sign = 's', /* signed */ + .realbits = 12, /* 12 bits */ + .storagebits = 16, /* 16 bits used for storage */ + .shift = 0, /* zero shift */ + }, + }, + /* Differential ADC channel in_voltage3-voltage4_raw etc*/ + { + .type = IIO_VOLTAGE, + .differential = 1, + .indexed = 1, + .channel = 3, + .channel2 = 4, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_index = diffvoltage3m4, + .scan_type = { + .sign = 's', + .realbits = 11, + .storagebits = 16, + .shift = 0, + }, + }, + /* + * 'modified' (i.e. axis specified) acceleration channel + * in_accel_z_raw + */ + { + .type = IIO_ACCEL, + .modified = 1, + /* Channel 2 is use for modifiers */ + .channel2 = IIO_MOD_X, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + /* + * Internal bias and gain correction values. Applied + * by the hardware or driver prior to userspace + * seeing the readings. Typically part of hardware + * calibration. + */ + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_index = accelx, + .scan_type = { /* Description of storage in buffer */ + .sign = 's', /* signed */ + .realbits = 16, /* 16 bits */ + .storagebits = 16, /* 16 bits used for storage */ + .shift = 0, /* zero shift */ + }, + }, + /* + * Convenience macro for timestamps. 4 is the index in + * the buffer. + */ + IIO_CHAN_SOFT_TIMESTAMP(4), + /* DAC channel out_voltage0_raw */ + { + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .scan_index = -1, /* No buffer support */ + .output = 1, + .indexed = 1, + .channel = 0, + }, + { + .type = IIO_STEPS, + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_ENABLE) | + BIT(IIO_CHAN_INFO_CALIBHEIGHT), + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = -1, /* No buffer support */ +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS + .event_spec = &step_detect_event, + .num_event_specs = 1, +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ + }, + { + .type = IIO_ACTIVITY, + .modified = 1, + .channel2 = IIO_MOD_RUNNING, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = -1, /* No buffer support */ +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS + .event_spec = &iio_running_event, + .num_event_specs = 1, +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ + }, + { + .type = IIO_ACTIVITY, + .modified = 1, + .channel2 = IIO_MOD_WALKING, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = -1, /* No buffer support */ +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS + .event_spec = &iio_walking_event, + .num_event_specs = 1, +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ + }, +}; + +/** + * iio_dummy_read_raw() - data read function. + * @indio_dev: the struct iio_dev associated with this device instance + * @chan: the channel whose data is to be read + * @val: first element of returned value (typically INT) + * @val2: second element of returned value (typically MICRO) + * @mask: what we actually want to read as per the info_mask_* + * in iio_chan_spec. + */ +static int iio_dummy_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct iio_dummy_state *st = iio_priv(indio_dev); + int ret = -EINVAL; + + mutex_lock(&st->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: /* magic value - channel value read */ + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output) { + /* Set integer part to cached value */ + *val = st->dac_val; + ret = IIO_VAL_INT; + } else if (chan->differential) { + if (chan->channel == 1) + *val = st->differential_adc_val[0]; + else + *val = st->differential_adc_val[1]; + ret = IIO_VAL_INT; + } else { + *val = st->single_ended_adc_val; + ret = IIO_VAL_INT; + } + break; + case IIO_ACCEL: + *val = st->accel_val; + ret = IIO_VAL_INT; + break; + default: + break; + } + break; + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_STEPS: + *val = st->steps; + ret = IIO_VAL_INT; + break; + case IIO_ACTIVITY: + switch (chan->channel2) { + case IIO_MOD_RUNNING: + *val = st->activity_running; + ret = IIO_VAL_INT; + break; + case IIO_MOD_WALKING: + *val = st->activity_walking; + ret = IIO_VAL_INT; + break; + default: + break; + } + break; + default: + break; + } + break; + case IIO_CHAN_INFO_OFFSET: + /* only single ended adc -> 7 */ + *val = 7; + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + switch (chan->differential) { + case 0: + /* only single ended adc -> 0.001333 */ + *val = 0; + *val2 = 1333; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case 1: + /* all differential adc channels -> + * 0.000001344 */ + *val = 0; + *val2 = 1344; + ret = IIO_VAL_INT_PLUS_NANO; + } + break; + default: + break; + } + break; + case IIO_CHAN_INFO_CALIBBIAS: + /* only the acceleration axis - read from cache */ + *val = st->accel_calibbias; + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_CALIBSCALE: + *val = st->accel_calibscale->val; + *val2 = st->accel_calibscale->val2; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = 3; + *val2 = 33; + ret = IIO_VAL_INT_PLUS_NANO; + break; + case IIO_CHAN_INFO_ENABLE: + switch (chan->type) { + case IIO_STEPS: + *val = st->steps_enabled; + ret = IIO_VAL_INT; + break; + default: + break; + } + break; + case IIO_CHAN_INFO_CALIBHEIGHT: + switch (chan->type) { + case IIO_STEPS: + *val = st->height; + ret = IIO_VAL_INT; + break; + default: + break; + } + break; + + default: + break; + } + mutex_unlock(&st->lock); + return ret; +} + +/** + * iio_dummy_write_raw() - data write function. + * @indio_dev: the struct iio_dev associated with this device instance + * @chan: the channel whose data is to be written + * @val: first element of value to set (typically INT) + * @val2: second element of value to set (typically MICRO) + * @mask: what we actually want to write as per the info_mask_* + * in iio_chan_spec. + * + * Note that all raw writes are assumed IIO_VAL_INT and info mask elements + * are assumed to be IIO_INT_PLUS_MICRO unless the callback write_raw_get_fmt + * in struct iio_info is provided by the driver. + */ +static int iio_dummy_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + int i; + int ret = 0; + struct iio_dummy_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output == 0) + return -EINVAL; + + /* Locking not required as writing single value */ + mutex_lock(&st->lock); + st->dac_val = val; + mutex_unlock(&st->lock); + return 0; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_STEPS: + mutex_lock(&st->lock); + st->steps = val; + mutex_unlock(&st->lock); + return 0; + case IIO_ACTIVITY: + if (val < 0) + val = 0; + if (val > 100) + val = 100; + switch (chan->channel2) { + case IIO_MOD_RUNNING: + st->activity_running = val; + return 0; + case IIO_MOD_WALKING: + st->activity_walking = val; + return 0; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBSCALE: + mutex_lock(&st->lock); + /* Compare against table - hard matching here */ + for (i = 0; i < ARRAY_SIZE(dummy_scales); i++) + if (val == dummy_scales[i].val && + val2 == dummy_scales[i].val2) + break; + if (i == ARRAY_SIZE(dummy_scales)) + ret = -EINVAL; + else + st->accel_calibscale = &dummy_scales[i]; + mutex_unlock(&st->lock); + return ret; + case IIO_CHAN_INFO_CALIBBIAS: + mutex_lock(&st->lock); + st->accel_calibbias = val; + mutex_unlock(&st->lock); + return 0; + case IIO_CHAN_INFO_ENABLE: + switch (chan->type) { + case IIO_STEPS: + mutex_lock(&st->lock); + st->steps_enabled = val; + mutex_unlock(&st->lock); + return 0; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBHEIGHT: + switch (chan->type) { + case IIO_STEPS: + st->height = val; + return 0; + default: + return -EINVAL; + } + + default: + return -EINVAL; + } +} + +/* + * Device type specific information. + */ +static const struct iio_info iio_dummy_info = { + .driver_module = THIS_MODULE, + .read_raw = &iio_dummy_read_raw, + .write_raw = &iio_dummy_write_raw, +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS + .read_event_config = &iio_simple_dummy_read_event_config, + .write_event_config = &iio_simple_dummy_write_event_config, + .read_event_value = &iio_simple_dummy_read_event_value, + .write_event_value = &iio_simple_dummy_write_event_value, +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ +}; + +/** + * iio_dummy_init_device() - device instance specific init + * @indio_dev: the iio device structure + * + * Most drivers have one of these to set up default values, + * reset the device to known state etc. + */ +static int iio_dummy_init_device(struct iio_dev *indio_dev) +{ + struct iio_dummy_state *st = iio_priv(indio_dev); + + st->dac_val = 0; + st->single_ended_adc_val = 73; + st->differential_adc_val[0] = 33; + st->differential_adc_val[1] = -34; + st->accel_val = 34; + st->accel_calibbias = -7; + st->accel_calibscale = &dummy_scales[0]; + st->steps = 47; + st->activity_running = 98; + st->activity_walking = 4; + + return 0; +} + +/** + * iio_dummy_probe() - device instance probe + * @index: an id number for this instance. + * + * Arguments are bus type specific. + * I2C: iio_dummy_probe(struct i2c_client *client, + * const struct i2c_device_id *id) + * SPI: iio_dummy_probe(struct spi_device *spi) + */ +static int iio_dummy_probe(int index) +{ + int ret; + struct iio_dev *indio_dev; + struct iio_dummy_state *st; + + /* + * Allocate an IIO device. + * + * This structure contains all generic state + * information about the device instance. + * It also has a region (accessed by iio_priv() + * for chip specific state information. + */ + indio_dev = iio_device_alloc(sizeof(*st)); + if (!indio_dev) { + ret = -ENOMEM; + goto error_ret; + } + + st = iio_priv(indio_dev); + mutex_init(&st->lock); + + iio_dummy_init_device(indio_dev); + /* + * With hardware: Set the parent device. + * indio_dev->dev.parent = &spi->dev; + * indio_dev->dev.parent = &client->dev; + */ + + /* + * Make the iio_dev struct available to remove function. + * Bus equivalents + * i2c_set_clientdata(client, indio_dev); + * spi_set_drvdata(spi, indio_dev); + */ + iio_dummy_devs[index] = indio_dev; + + /* + * Set the device name. + * + * This is typically a part number and obtained from the module + * id table. + * e.g. for i2c and spi: + * indio_dev->name = id->name; + * indio_dev->name = spi_get_device_id(spi)->name; + */ + indio_dev->name = iio_dummy_part_number; + + /* Provide description of available channels */ + indio_dev->channels = iio_dummy_channels; + indio_dev->num_channels = ARRAY_SIZE(iio_dummy_channels); + + /* + * Provide device type specific interface functions and + * constant data. + */ + indio_dev->info = &iio_dummy_info; + + /* Specify that device provides sysfs type interfaces */ + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_simple_dummy_events_register(indio_dev); + if (ret < 0) + goto error_free_device; + + ret = iio_simple_dummy_configure_buffer(indio_dev); + if (ret < 0) + goto error_unregister_events; + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto error_unconfigure_buffer; + + return 0; +error_unconfigure_buffer: + iio_simple_dummy_unconfigure_buffer(indio_dev); +error_unregister_events: + iio_simple_dummy_events_unregister(indio_dev); +error_free_device: + iio_device_free(indio_dev); +error_ret: + return ret; +} + +/** + * iio_dummy_remove() - device instance removal function + * @index: device index. + * + * Parameters follow those of iio_dummy_probe for buses. + */ +static void iio_dummy_remove(int index) +{ + /* + * Get a pointer to the device instance iio_dev structure + * from the bus subsystem. E.g. + * struct iio_dev *indio_dev = i2c_get_clientdata(client); + * struct iio_dev *indio_dev = spi_get_drvdata(spi); + */ + struct iio_dev *indio_dev = iio_dummy_devs[index]; + + /* Unregister the device */ + iio_device_unregister(indio_dev); + + /* Device specific code to power down etc */ + + /* Buffered capture related cleanup */ + iio_simple_dummy_unconfigure_buffer(indio_dev); + + iio_simple_dummy_events_unregister(indio_dev); + + /* Free all structures */ + iio_device_free(indio_dev); +} + +/** + * iio_dummy_init() - device driver registration + * + * Varies depending on bus type of the device. As there is no device + * here, call probe directly. For information on device registration + * i2c: + * Documentation/i2c/writing-clients + * spi: + * Documentation/spi/spi-summary + */ +static __init int iio_dummy_init(void) +{ + int i, ret; + + if (instances > 10) { + instances = 1; + return -EINVAL; + } + + /* Fake a bus */ + iio_dummy_devs = kcalloc(instances, sizeof(*iio_dummy_devs), + GFP_KERNEL); + /* Here we have no actual device so call probe */ + for (i = 0; i < instances; i++) { + ret = iio_dummy_probe(i); + if (ret < 0) + goto error_remove_devs; + } + return 0; + +error_remove_devs: + while (i--) + iio_dummy_remove(i); + + kfree(iio_dummy_devs); + return ret; +} +module_init(iio_dummy_init); + +/** + * iio_dummy_exit() - device driver removal + * + * Varies depending on bus type of the device. + * As there is no device here, call remove directly. + */ +static __exit void iio_dummy_exit(void) +{ + int i; + + for (i = 0; i < instances; i++) + iio_dummy_remove(i); + kfree(iio_dummy_devs); +} +module_exit(iio_dummy_exit); + +MODULE_AUTHOR("Jonathan Cameron "); +MODULE_DESCRIPTION("IIO dummy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/dummy/iio_simple_dummy.h b/drivers/iio/dummy/iio_simple_dummy.h new file mode 100644 index 0000000..5c2f4d0 --- /dev/null +++ b/drivers/iio/dummy/iio_simple_dummy.h @@ -0,0 +1,129 @@ +/** + * Copyright (c) 2011 Jonathan Cameron + * + * 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. + * + * Join together the various functionality of iio_simple_dummy driver + */ + +#ifndef _IIO_SIMPLE_DUMMY_H_ +#define _IIO_SIMPLE_DUMMY_H_ +#include + +struct iio_dummy_accel_calibscale; +struct iio_dummy_regs; + +/** + * struct iio_dummy_state - device instance specific state. + * @dac_val: cache for dac value + * @single_ended_adc_val: cache for single ended adc value + * @differential_adc_val: cache for differential adc value + * @accel_val: cache for acceleration value + * @accel_calibbias: cache for acceleration calibbias + * @accel_calibscale: cache for acceleration calibscale + * @lock: lock to ensure state is consistent + * @event_irq: irq number for event line (faked) + * @event_val: cache for event threshold value + * @event_en: cache of whether event is enabled + */ +struct iio_dummy_state { + int dac_val; + int single_ended_adc_val; + int differential_adc_val[2]; + int accel_val; + int accel_calibbias; + int activity_running; + int activity_walking; + const struct iio_dummy_accel_calibscale *accel_calibscale; + struct mutex lock; + struct iio_dummy_regs *regs; + int steps_enabled; + int steps; + int height; +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS + int event_irq; + int event_val; + bool event_en; + s64 event_timestamp; +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ +}; + +#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS + +struct iio_dev; + +int iio_simple_dummy_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir); + +int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state); + +int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, + int *val2); + +int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, + int val2); + +int iio_simple_dummy_events_register(struct iio_dev *indio_dev); +void iio_simple_dummy_events_unregister(struct iio_dev *indio_dev); + +#else /* Stubs for when events are disabled at compile time */ + +static inline int +iio_simple_dummy_events_register(struct iio_dev *indio_dev) +{ + return 0; +}; + +static inline void +iio_simple_dummy_events_unregister(struct iio_dev *indio_dev) +{ }; + +#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS*/ + +/** + * enum iio_simple_dummy_scan_elements - scan index enum + * @voltage0: the single ended voltage channel + * @diffvoltage1m2: first differential channel + * @diffvoltage3m4: second differenial channel + * @accelx: acceleration channel + * + * Enum provides convenient numbering for the scan index. + */ +enum iio_simple_dummy_scan_elements { + voltage0, + diffvoltage1m2, + diffvoltage3m4, + accelx, +}; + +#ifdef CONFIG_IIO_SIMPLE_DUMMY_BUFFER +int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev); +void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev); +#else +static inline int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev) +{ + return 0; +}; + +static inline +void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev) +{}; + +#endif /* CONFIG_IIO_SIMPLE_DUMMY_BUFFER */ +#endif /* _IIO_SIMPLE_DUMMY_H_ */ diff --git a/drivers/iio/dummy/iio_simple_dummy_buffer.c b/drivers/iio/dummy/iio_simple_dummy_buffer.c new file mode 100644 index 0000000..00ed774 --- /dev/null +++ b/drivers/iio/dummy/iio_simple_dummy_buffer.c @@ -0,0 +1,192 @@ +/** + * Copyright (c) 2011 Jonathan Cameron + * + * 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. + * + * Buffer handling elements of industrial I/O reference driver. + * Uses the kfifo buffer. + * + * To test without hardware use the sysfs trigger. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "iio_simple_dummy.h" + +/* Some fake data */ + +static const s16 fakedata[] = { + [voltage0] = 7, + [diffvoltage1m2] = -33, + [diffvoltage3m4] = -2, + [accelx] = 344, +}; + +/** + * iio_simple_dummy_trigger_h() - the trigger handler function + * @irq: the interrupt number + * @p: private data - always a pointer to the poll func. + * + * This is the guts of buffered capture. On a trigger event occurring, + * if the pollfunc is attached then this handler is called as a threaded + * interrupt (and hence may sleep). It is responsible for grabbing data + * from the device and pushing it into the associated buffer. + */ +static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + int len = 0; + u16 *data; + + data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); + if (!data) + goto done; + + if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)) { + /* + * Three common options here: + * hardware scans: certain combinations of channels make + * up a fast read. The capture will consist of all of them. + * Hence we just call the grab data function and fill the + * buffer without processing. + * software scans: can be considered to be random access + * so efficient reading is just a case of minimal bus + * transactions. + * software culled hardware scans: + * occasionally a driver may process the nearest hardware + * scan to avoid storing elements that are not desired. This + * is the fiddliest option by far. + * Here let's pretend we have random access. And the values are + * in the constant table fakedata. + */ + int i, j; + + for (i = 0, j = 0; + i < bitmap_weight(indio_dev->active_scan_mask, + indio_dev->masklength); + i++, j++) { + j = find_next_bit(indio_dev->active_scan_mask, + indio_dev->masklength, j); + /* random access read from the 'device' */ + data[i] = fakedata[j]; + len += 2; + } + } + + iio_push_to_buffers_with_timestamp(indio_dev, data, iio_get_time_ns()); + + kfree(data); + +done: + /* + * Tell the core we are done with this trigger and ready for the + * next one. + */ + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_buffer_setup_ops iio_simple_dummy_buffer_setup_ops = { + /* + * iio_triggered_buffer_postenable: + * Generic function that simply attaches the pollfunc to the trigger. + * Replace this to mess with hardware state before we attach the + * trigger. + */ + .postenable = &iio_triggered_buffer_postenable, + /* + * iio_triggered_buffer_predisable: + * Generic function that simple detaches the pollfunc from the trigger. + * Replace this to put hardware state back again after the trigger is + * detached but before userspace knows we have disabled the ring. + */ + .predisable = &iio_triggered_buffer_predisable, +}; + +int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev) +{ + int ret; + struct iio_buffer *buffer; + + /* Allocate a buffer to use - here a kfifo */ + buffer = iio_kfifo_allocate(); + if (!buffer) { + ret = -ENOMEM; + goto error_ret; + } + + iio_device_attach_buffer(indio_dev, buffer); + + /* Enable timestamps by default */ + buffer->scan_timestamp = true; + + /* + * Tell the core what device type specific functions should + * be run on either side of buffer capture enable / disable. + */ + indio_dev->setup_ops = &iio_simple_dummy_buffer_setup_ops; + + /* + * Configure a polling function. + * When a trigger event with this polling function connected + * occurs, this function is run. Typically this grabs data + * from the device. + * + * NULL for the bottom half. This is normally implemented only if we + * either want to ping a capture now pin (no sleeping) or grab + * a timestamp as close as possible to a data ready trigger firing. + * + * IRQF_ONESHOT ensures irqs are masked such that only one instance + * of the handler can run at a time. + * + * "iio_simple_dummy_consumer%d" formatting string for the irq 'name' + * as seen under /proc/interrupts. Remaining parameters as per printk. + */ + indio_dev->pollfunc = iio_alloc_pollfunc(NULL, + &iio_simple_dummy_trigger_h, + IRQF_ONESHOT, + indio_dev, + "iio_simple_dummy_consumer%d", + indio_dev->id); + + if (!indio_dev->pollfunc) { + ret = -ENOMEM; + goto error_free_buffer; + } + + /* + * Notify the core that this device is capable of buffered capture + * driven by a trigger. + */ + indio_dev->modes |= INDIO_BUFFER_TRIGGERED; + + return 0; + +error_free_buffer: + iio_kfifo_free(indio_dev->buffer); +error_ret: + return ret; +} + +/** + * iio_simple_dummy_unconfigure_buffer() - release buffer resources + * @indo_dev: device instance state + */ +void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev) +{ + iio_dealloc_pollfunc(indio_dev->pollfunc); + iio_kfifo_free(indio_dev->buffer); +} diff --git a/drivers/iio/dummy/iio_simple_dummy_events.c b/drivers/iio/dummy/iio_simple_dummy_events.c new file mode 100644 index 0000000..bfbf1c5 --- /dev/null +++ b/drivers/iio/dummy/iio_simple_dummy_events.c @@ -0,0 +1,276 @@ +/** + * Copyright (c) 2011 Jonathan Cameron + * + * 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. + * + * Event handling elements of industrial I/O reference driver. + */ +#include +#include +#include +#include + +#include +#include +#include +#include "iio_simple_dummy.h" + +/* Evgen 'fakes' interrupt events for this example */ +#include "iio_dummy_evgen.h" + +/** + * iio_simple_dummy_read_event_config() - is event enabled? + * @indio_dev: the device instance data + * @chan: channel for the event whose state is being queried + * @type: type of the event whose state is being queried + * @dir: direction of the vent whose state is being queried + * + * This function would normally query the relevant registers or a cache to + * discover if the event generation is enabled on the device. + */ +int iio_simple_dummy_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct iio_dummy_state *st = iio_priv(indio_dev); + + return st->event_en; +} + +/** + * iio_simple_dummy_write_event_config() - set whether event is enabled + * @indio_dev: the device instance data + * @chan: channel for the event whose state is being set + * @type: type of the event whose state is being set + * @dir: direction of the vent whose state is being set + * @state: whether to enable or disable the device. + * + * This function would normally set the relevant registers on the devices + * so that it generates the specified event. Here it just sets up a cached + * value. + */ +int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct iio_dummy_state *st = iio_priv(indio_dev); + + /* + * Deliberately over the top code splitting to illustrate + * how this is done when multiple events exist. + */ + switch (chan->type) { + case IIO_VOLTAGE: + switch (type) { + case IIO_EV_TYPE_THRESH: + if (dir == IIO_EV_DIR_RISING) + st->event_en = state; + else + return -EINVAL; + default: + return -EINVAL; + } + break; + case IIO_ACTIVITY: + switch (type) { + case IIO_EV_TYPE_THRESH: + st->event_en = state; + break; + default: + return -EINVAL; + } + break; + case IIO_STEPS: + switch (type) { + case IIO_EV_TYPE_CHANGE: + st->event_en = state; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +/** + * iio_simple_dummy_read_event_value() - get value associated with event + * @indio_dev: device instance specific data + * @chan: channel for the event whose value is being read + * @type: type of the event whose value is being read + * @dir: direction of the vent whose value is being read + * @info: info type of the event whose value is being read + * @val: value for the event code. + * + * Many devices provide a large set of events of which only a subset may + * be enabled at a time, with value registers whose meaning changes depending + * on the event enabled. This often means that the driver must cache the values + * associated with each possible events so that the right value is in place when + * the enabled event is changed. + */ +int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct iio_dummy_state *st = iio_priv(indio_dev); + + *val = st->event_val; + + return IIO_VAL_INT; +} + +/** + * iio_simple_dummy_write_event_value() - set value associate with event + * @indio_dev: device instance specific data + * @chan: channel for the event whose value is being set + * @type: type of the event whose value is being set + * @dir: direction of the vent whose value is being set + * @info: info type of the event whose value is being set + * @val: the value to be set. + */ +int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct iio_dummy_state *st = iio_priv(indio_dev); + + st->event_val = val; + + return 0; +} + +static irqreturn_t iio_simple_dummy_get_timestamp(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct iio_dummy_state *st = iio_priv(indio_dev); + + st->event_timestamp = iio_get_time_ns(); + return IRQ_HANDLED; +} + +/** + * iio_simple_dummy_event_handler() - identify and pass on event + * @irq: irq of event line + * @private: pointer to device instance state. + * + * This handler is responsible for querying the device to find out what + * event occurred and for then pushing that event towards userspace. + * Here only one event occurs so we push that directly on with locally + * grabbed timestamp. + */ +static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct iio_dummy_state *st = iio_priv(indio_dev); + + dev_dbg(&indio_dev->dev, "id %x event %x\n", + st->regs->reg_id, st->regs->reg_data); + + switch (st->regs->reg_data) { + case 0: + iio_push_event(indio_dev, + IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0, + IIO_EV_DIR_RISING, + IIO_EV_TYPE_THRESH, 0, 0, 0), + st->event_timestamp); + break; + case 1: + if (st->activity_running > st->event_val) + iio_push_event(indio_dev, + IIO_EVENT_CODE(IIO_ACTIVITY, 0, + IIO_MOD_RUNNING, + IIO_EV_DIR_RISING, + IIO_EV_TYPE_THRESH, + 0, 0, 0), + st->event_timestamp); + break; + case 2: + if (st->activity_walking < st->event_val) + iio_push_event(indio_dev, + IIO_EVENT_CODE(IIO_ACTIVITY, 0, + IIO_MOD_WALKING, + IIO_EV_DIR_FALLING, + IIO_EV_TYPE_THRESH, + 0, 0, 0), + st->event_timestamp); + break; + case 3: + iio_push_event(indio_dev, + IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD, + IIO_EV_DIR_NONE, + IIO_EV_TYPE_CHANGE, 0, 0, 0), + st->event_timestamp); + break; + default: + break; + } + + return IRQ_HANDLED; +} + +/** + * iio_simple_dummy_events_register() - setup interrupt handling for events + * @indio_dev: device instance data + * + * This function requests the threaded interrupt to handle the events. + * Normally the irq is a hardware interrupt and the number comes + * from board configuration files. Here we get it from a companion + * module that fakes the interrupt for us. Note that module in + * no way forms part of this example. Just assume that events magically + * appear via the provided interrupt. + */ +int iio_simple_dummy_events_register(struct iio_dev *indio_dev) +{ + struct iio_dummy_state *st = iio_priv(indio_dev); + int ret; + + /* Fire up event source - normally not present */ + st->event_irq = iio_dummy_evgen_get_irq(); + if (st->event_irq < 0) { + ret = st->event_irq; + goto error_ret; + } + st->regs = iio_dummy_evgen_get_regs(st->event_irq); + + ret = request_threaded_irq(st->event_irq, + &iio_simple_dummy_get_timestamp, + &iio_simple_dummy_event_handler, + IRQF_ONESHOT, + "iio_simple_event", + indio_dev); + if (ret < 0) + goto error_free_evgen; + return 0; + +error_free_evgen: + iio_dummy_evgen_release_irq(st->event_irq); +error_ret: + return ret; +} + +/** + * iio_simple_dummy_events_unregister() - tidy up interrupt handling on remove + * @indio_dev: device instance data + */ +void iio_simple_dummy_events_unregister(struct iio_dev *indio_dev) +{ + struct iio_dummy_state *st = iio_priv(indio_dev); + + free_irq(st->event_irq, indio_dev); + /* Not part of normal driver */ + iio_dummy_evgen_release_irq(st->event_irq); +} diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index 6d5b38d..85de198 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -17,32 +17,32 @@ source "drivers/staging/iio/meter/Kconfig" source "drivers/staging/iio/resolver/Kconfig" source "drivers/staging/iio/trigger/Kconfig" -config IIO_DUMMY_EVGEN - tristate - -config IIO_SIMPLE_DUMMY - tristate "An example driver with no hardware requirements" - help - Driver intended mainly as documentation for how to write - a driver. May also be useful for testing userspace code - without hardware. - -if IIO_SIMPLE_DUMMY - -config IIO_SIMPLE_DUMMY_EVENTS - bool "Event generation support" - select IIO_DUMMY_EVGEN - help - Add some dummy events to the simple dummy driver. - -config IIO_SIMPLE_DUMMY_BUFFER - bool "Buffered capture support" - select IIO_BUFFER - select IIO_TRIGGER - select IIO_KFIFO_BUF - help - Add buffered data capture to the simple dummy driver. - -endif # IIO_SIMPLE_DUMMY +#config IIO_DUMMY_EVGEN +# tristate +# +#config IIO_SIMPLE_DUMMY +# tristate "An example driver with no hardware requirements" +# help +# Driver intended mainly as documentation for how to write +# a driver. May also be useful for testing userspace code +# without hardware. + +#if IIO_SIMPLE_DUMMY + +#config IIO_SIMPLE_DUMMY_EVENTS +# bool "Event generation support" +# select IIO_DUMMY_EVGEN +# help +# Add some dummy events to the simple dummy driver. + +#config IIO_SIMPLE_DUMMY_BUFFER +# bool "Buffered capture support" +# select IIO_BUFFER +# select IIO_TRIGGER +# select IIO_KFIFO_BUF +# help +# Add buffered data capture to the simple dummy driver. + +#endif # IIO_SIMPLE_DUMMY endmenu diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index d871061..355824a 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -2,12 +2,12 @@ # Makefile for the industrial I/O core. # -obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o -iio_dummy-y := iio_simple_dummy.o -iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o -iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_BUFFER) += iio_simple_dummy_buffer.o +#obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o +#iio_dummy-y := iio_simple_dummy.o +#iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o +#iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_BUFFER) += iio_simple_dummy_buffer.o -obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o +#obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o obj-y += accel/ obj-y += adc/ diff --git a/drivers/staging/iio/iio_dummy_evgen.c b/drivers/staging/iio/iio_dummy_evgen.c deleted file mode 100644 index 9e83f34..0000000 --- a/drivers/staging/iio/iio_dummy_evgen.c +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Copyright (c) 2011 Jonathan Cameron - * - * 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. - * - * Companion module to the iio simple dummy example driver. - * The purpose of this is to generate 'fake' event interrupts thus - * allowing that driver's code to be as close as possible to that of - * a normal driver talking to hardware. The approach used here - * is not intended to be general and just happens to work for this - * particular use case. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "iio_dummy_evgen.h" -#include -#include -#include - -/* Fiddly bit of faking and irq without hardware */ -#define IIO_EVENTGEN_NO 10 - -/** - * struct iio_dummy_handle_irq - helper struct to simulate interrupt generation - * @work: irq_work used to run handlers from hardirq context - * @irq: fake irq line number to trigger an interrupt - */ -struct iio_dummy_handle_irq { - struct irq_work work; - int irq; -}; - -/** - * struct iio_dummy_evgen - evgen state - * @chip: irq chip we are faking - * @base: base of irq range - * @enabled: mask of which irqs are enabled - * @inuse: mask of which irqs are connected - * @regs: irq regs we are faking - * @lock: protect the evgen state - * @handler: helper for a 'hardware-like' interrupt simulation - */ -struct iio_dummy_eventgen { - struct irq_chip chip; - int base; - bool enabled[IIO_EVENTGEN_NO]; - bool inuse[IIO_EVENTGEN_NO]; - struct iio_dummy_regs regs[IIO_EVENTGEN_NO]; - struct mutex lock; - struct iio_dummy_handle_irq handler; -}; - -/* We can only ever have one instance of this 'device' */ -static struct iio_dummy_eventgen *iio_evgen; -static const char *iio_evgen_name = "iio_dummy_evgen"; - -static void iio_dummy_event_irqmask(struct irq_data *d) -{ - struct irq_chip *chip = irq_data_get_irq_chip(d); - struct iio_dummy_eventgen *evgen = - container_of(chip, struct iio_dummy_eventgen, chip); - - evgen->enabled[d->irq - evgen->base] = false; -} - -static void iio_dummy_event_irqunmask(struct irq_data *d) -{ - struct irq_chip *chip = irq_data_get_irq_chip(d); - struct iio_dummy_eventgen *evgen = - container_of(chip, struct iio_dummy_eventgen, chip); - - evgen->enabled[d->irq - evgen->base] = true; -} - -static void iio_dummy_work_handler(struct irq_work *work) -{ - struct iio_dummy_handle_irq *irq_handler; - - irq_handler = container_of(work, struct iio_dummy_handle_irq, work); - handle_simple_irq(irq_to_desc(irq_handler->irq)); -} - -static int iio_dummy_evgen_create(void) -{ - int ret, i; - - iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL); - if (!iio_evgen) - return -ENOMEM; - - iio_evgen->base = irq_alloc_descs(-1, 0, IIO_EVENTGEN_NO, 0); - if (iio_evgen->base < 0) { - ret = iio_evgen->base; - kfree(iio_evgen); - return ret; - } - iio_evgen->chip.name = iio_evgen_name; - iio_evgen->chip.irq_mask = &iio_dummy_event_irqmask; - iio_evgen->chip.irq_unmask = &iio_dummy_event_irqunmask; - for (i = 0; i < IIO_EVENTGEN_NO; i++) { - irq_set_chip(iio_evgen->base + i, &iio_evgen->chip); - irq_set_handler(iio_evgen->base + i, &handle_simple_irq); - irq_modify_status(iio_evgen->base + i, - IRQ_NOREQUEST | IRQ_NOAUTOEN, - IRQ_NOPROBE); - } - init_irq_work(&iio_evgen->handler.work, iio_dummy_work_handler); - mutex_init(&iio_evgen->lock); - return 0; -} - -/** - * iio_dummy_evgen_get_irq() - get an evgen provided irq for a device - * - * This function will give a free allocated irq to a client device. - * That irq can then be caused to 'fire' by using the associated sysfs file. - */ -int iio_dummy_evgen_get_irq(void) -{ - int i, ret = 0; - - if (!iio_evgen) - return -ENODEV; - - mutex_lock(&iio_evgen->lock); - for (i = 0; i < IIO_EVENTGEN_NO; i++) - if (!iio_evgen->inuse[i]) { - ret = iio_evgen->base + i; - iio_evgen->inuse[i] = true; - break; - } - mutex_unlock(&iio_evgen->lock); - if (i == IIO_EVENTGEN_NO) - return -ENOMEM; - return ret; -} -EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq); - -/** - * iio_dummy_evgen_release_irq() - give the irq back. - * @irq: irq being returned to the pool - * - * Used by client driver instances to give the irqs back when they disconnect - */ -void iio_dummy_evgen_release_irq(int irq) -{ - mutex_lock(&iio_evgen->lock); - iio_evgen->inuse[irq - iio_evgen->base] = false; - mutex_unlock(&iio_evgen->lock); -} -EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq); - -struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq) -{ - return &iio_evgen->regs[irq - iio_evgen->base]; -} -EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_regs); - -static void iio_dummy_evgen_free(void) -{ - irq_free_descs(iio_evgen->base, IIO_EVENTGEN_NO); - kfree(iio_evgen); -} - -static void iio_evgen_release(struct device *dev) -{ - iio_dummy_evgen_free(); -} - -static ssize_t iio_evgen_poke(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) -{ - struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - unsigned long event; - int ret; - - ret = kstrtoul(buf, 10, &event); - if (ret) - return ret; - - iio_evgen->regs[this_attr->address].reg_id = this_attr->address; - iio_evgen->regs[this_attr->address].reg_data = event; - - iio_evgen->handler.irq = iio_evgen->base + this_attr->address; - if (iio_evgen->enabled[this_attr->address]) - irq_work_queue(&iio_evgen->handler.work); - - return len; -} - -static IIO_DEVICE_ATTR(poke_ev0, S_IWUSR, NULL, &iio_evgen_poke, 0); -static IIO_DEVICE_ATTR(poke_ev1, S_IWUSR, NULL, &iio_evgen_poke, 1); -static IIO_DEVICE_ATTR(poke_ev2, S_IWUSR, NULL, &iio_evgen_poke, 2); -static IIO_DEVICE_ATTR(poke_ev3, S_IWUSR, NULL, &iio_evgen_poke, 3); -static IIO_DEVICE_ATTR(poke_ev4, S_IWUSR, NULL, &iio_evgen_poke, 4); -static IIO_DEVICE_ATTR(poke_ev5, S_IWUSR, NULL, &iio_evgen_poke, 5); -static IIO_DEVICE_ATTR(poke_ev6, S_IWUSR, NULL, &iio_evgen_poke, 6); -static IIO_DEVICE_ATTR(poke_ev7, S_IWUSR, NULL, &iio_evgen_poke, 7); -static IIO_DEVICE_ATTR(poke_ev8, S_IWUSR, NULL, &iio_evgen_poke, 8); -static IIO_DEVICE_ATTR(poke_ev9, S_IWUSR, NULL, &iio_evgen_poke, 9); - -static struct attribute *iio_evgen_attrs[] = { - &iio_dev_attr_poke_ev0.dev_attr.attr, - &iio_dev_attr_poke_ev1.dev_attr.attr, - &iio_dev_attr_poke_ev2.dev_attr.attr, - &iio_dev_attr_poke_ev3.dev_attr.attr, - &iio_dev_attr_poke_ev4.dev_attr.attr, - &iio_dev_attr_poke_ev5.dev_attr.attr, - &iio_dev_attr_poke_ev6.dev_attr.attr, - &iio_dev_attr_poke_ev7.dev_attr.attr, - &iio_dev_attr_poke_ev8.dev_attr.attr, - &iio_dev_attr_poke_ev9.dev_attr.attr, - NULL, -}; - -static const struct attribute_group iio_evgen_group = { - .attrs = iio_evgen_attrs, -}; - -static const struct attribute_group *iio_evgen_groups[] = { - &iio_evgen_group, - NULL -}; - -static struct device iio_evgen_dev = { - .bus = &iio_bus_type, - .groups = iio_evgen_groups, - .release = &iio_evgen_release, -}; - -static __init int iio_dummy_evgen_init(void) -{ - int ret = iio_dummy_evgen_create(); - - if (ret < 0) - return ret; - device_initialize(&iio_evgen_dev); - dev_set_name(&iio_evgen_dev, "iio_evgen"); - return device_add(&iio_evgen_dev); -} -module_init(iio_dummy_evgen_init); - -static __exit void iio_dummy_evgen_exit(void) -{ - device_unregister(&iio_evgen_dev); -} -module_exit(iio_dummy_evgen_exit); - -MODULE_AUTHOR("Jonathan Cameron "); -MODULE_DESCRIPTION("IIO dummy driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/iio_dummy_evgen.h b/drivers/staging/iio/iio_dummy_evgen.h deleted file mode 100644 index d044b94..0000000 --- a/drivers/staging/iio/iio_dummy_evgen.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _IIO_DUMMY_EVGEN_H_ -#define _IIO_DUMMY_EVGEN_H_ - -struct iio_dummy_regs { - u32 reg_id; - u32 reg_data; -}; - -struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq); -int iio_dummy_evgen_get_irq(void); -void iio_dummy_evgen_release_irq(int irq); - -#endif /* _IIO_DUMMY_EVGEN_H_ */ diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c deleted file mode 100644 index 381f90f..0000000 --- a/drivers/staging/iio/iio_simple_dummy.c +++ /dev/null @@ -1,748 +0,0 @@ -/** - * Copyright (c) 2011 Jonathan Cameron - * - * 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. - * - * A reference industrial I/O driver to illustrate the functionality available. - * - * There are numerous real drivers to illustrate the finer points. - * The purpose of this driver is to provide a driver with far more comments - * and explanatory notes than any 'real' driver would have. - * Anyone starting out writing an IIO driver should first make sure they - * understand all of this driver except those bits specifically marked - * as being present to allow us to 'fake' the presence of hardware. - */ -#include -#include -#include - -#include -#include -#include -#include -#include "iio_simple_dummy.h" - -/* - * A few elements needed to fake a bus for this driver - * Note instances parameter controls how many of these - * dummy devices are registered. - */ -static unsigned instances = 1; -module_param(instances, uint, 0); - -/* Pointer array used to fake bus elements */ -static struct iio_dev **iio_dummy_devs; - -/* Fake a name for the part number, usually obtained from the id table */ -static const char *iio_dummy_part_number = "iio_dummy_part_no"; - -/** - * struct iio_dummy_accel_calibscale - realworld to register mapping - * @val: first value in read_raw - here integer part. - * @val2: second value in read_raw etc - here micro part. - * @regval: register value - magic device specific numbers. - */ -struct iio_dummy_accel_calibscale { - int val; - int val2; - int regval; /* what would be written to hardware */ -}; - -static const struct iio_dummy_accel_calibscale dummy_scales[] = { - { 0, 100, 0x8 }, /* 0.000100 */ - { 0, 133, 0x7 }, /* 0.000133 */ - { 733, 13, 0x9 }, /* 733.000013 */ -}; - -#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS - -/* - * simple event - triggered when value rises above - * a threshold - */ -static const struct iio_event_spec iio_dummy_event = { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_RISING, - .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), -}; - -/* - * simple step detect event - triggered when a step is detected - */ -static const struct iio_event_spec step_detect_event = { - .type = IIO_EV_TYPE_CHANGE, - .dir = IIO_EV_DIR_NONE, - .mask_separate = BIT(IIO_EV_INFO_ENABLE), -}; - -/* - * simple transition event - triggered when the reported running confidence - * value rises above a threshold value - */ -static const struct iio_event_spec iio_running_event = { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_RISING, - .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), -}; - -/* - * simple transition event - triggered when the reported walking confidence - * value falls under a threshold value - */ -static const struct iio_event_spec iio_walking_event = { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_FALLING, - .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), -}; -#endif - -/* - * iio_dummy_channels - Description of available channels - * - * This array of structures tells the IIO core about what the device - * actually provides for a given channel. - */ -static const struct iio_chan_spec iio_dummy_channels[] = { - /* indexed ADC channel in_voltage0_raw etc */ - { - .type = IIO_VOLTAGE, - /* Channel has a numeric index of 0 */ - .indexed = 1, - .channel = 0, - /* What other information is available? */ - .info_mask_separate = - /* - * in_voltage0_raw - * Raw (unscaled no bias removal etc) measurement - * from the device. - */ - BIT(IIO_CHAN_INFO_RAW) | - /* - * in_voltage0_offset - * Offset for userspace to apply prior to scale - * when converting to standard units (microvolts) - */ - BIT(IIO_CHAN_INFO_OFFSET) | - /* - * in_voltage0_scale - * Multipler for userspace to apply post offset - * when converting to standard units (microvolts) - */ - BIT(IIO_CHAN_INFO_SCALE), - /* - * sampling_frequency - * The frequency in Hz at which the channels are sampled - */ - .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), - /* The ordering of elements in the buffer via an enum */ - .scan_index = voltage0, - .scan_type = { /* Description of storage in buffer */ - .sign = 'u', /* unsigned */ - .realbits = 13, /* 13 bits */ - .storagebits = 16, /* 16 bits used for storage */ - .shift = 0, /* zero shift */ - }, -#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS - .event_spec = &iio_dummy_event, - .num_event_specs = 1, -#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ - }, - /* Differential ADC channel in_voltage1-voltage2_raw etc*/ - { - .type = IIO_VOLTAGE, - .differential = 1, - /* - * Indexing for differential channels uses channel - * for the positive part, channel2 for the negative. - */ - .indexed = 1, - .channel = 1, - .channel2 = 2, - /* - * in_voltage1-voltage2_raw - * Raw (unscaled no bias removal etc) measurement - * from the device. - */ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - /* - * in_voltage-voltage_scale - * Shared version of scale - shared by differential - * input channels of type IIO_VOLTAGE. - */ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), - /* - * sampling_frequency - * The frequency in Hz at which the channels are sampled - */ - .scan_index = diffvoltage1m2, - .scan_type = { /* Description of storage in buffer */ - .sign = 's', /* signed */ - .realbits = 12, /* 12 bits */ - .storagebits = 16, /* 16 bits used for storage */ - .shift = 0, /* zero shift */ - }, - }, - /* Differential ADC channel in_voltage3-voltage4_raw etc*/ - { - .type = IIO_VOLTAGE, - .differential = 1, - .indexed = 1, - .channel = 3, - .channel2 = 4, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), - .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), - .scan_index = diffvoltage3m4, - .scan_type = { - .sign = 's', - .realbits = 11, - .storagebits = 16, - .shift = 0, - }, - }, - /* - * 'modified' (i.e. axis specified) acceleration channel - * in_accel_z_raw - */ - { - .type = IIO_ACCEL, - .modified = 1, - /* Channel 2 is use for modifiers */ - .channel2 = IIO_MOD_X, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - /* - * Internal bias and gain correction values. Applied - * by the hardware or driver prior to userspace - * seeing the readings. Typically part of hardware - * calibration. - */ - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS), - .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ), - .scan_index = accelx, - .scan_type = { /* Description of storage in buffer */ - .sign = 's', /* signed */ - .realbits = 16, /* 16 bits */ - .storagebits = 16, /* 16 bits used for storage */ - .shift = 0, /* zero shift */ - }, - }, - /* - * Convenience macro for timestamps. 4 is the index in - * the buffer. - */ - IIO_CHAN_SOFT_TIMESTAMP(4), - /* DAC channel out_voltage0_raw */ - { - .type = IIO_VOLTAGE, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .scan_index = -1, /* No buffer support */ - .output = 1, - .indexed = 1, - .channel = 0, - }, - { - .type = IIO_STEPS, - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_ENABLE) | - BIT(IIO_CHAN_INFO_CALIBHEIGHT), - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - .scan_index = -1, /* No buffer support */ -#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS - .event_spec = &step_detect_event, - .num_event_specs = 1, -#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ - }, - { - .type = IIO_ACTIVITY, - .modified = 1, - .channel2 = IIO_MOD_RUNNING, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - .scan_index = -1, /* No buffer support */ -#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS - .event_spec = &iio_running_event, - .num_event_specs = 1, -#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ - }, - { - .type = IIO_ACTIVITY, - .modified = 1, - .channel2 = IIO_MOD_WALKING, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - .scan_index = -1, /* No buffer support */ -#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS - .event_spec = &iio_walking_event, - .num_event_specs = 1, -#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ - }, -}; - -/** - * iio_dummy_read_raw() - data read function. - * @indio_dev: the struct iio_dev associated with this device instance - * @chan: the channel whose data is to be read - * @val: first element of returned value (typically INT) - * @val2: second element of returned value (typically MICRO) - * @mask: what we actually want to read as per the info_mask_* - * in iio_chan_spec. - */ -static int iio_dummy_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long mask) -{ - struct iio_dummy_state *st = iio_priv(indio_dev); - int ret = -EINVAL; - - mutex_lock(&st->lock); - switch (mask) { - case IIO_CHAN_INFO_RAW: /* magic value - channel value read */ - switch (chan->type) { - case IIO_VOLTAGE: - if (chan->output) { - /* Set integer part to cached value */ - *val = st->dac_val; - ret = IIO_VAL_INT; - } else if (chan->differential) { - if (chan->channel == 1) - *val = st->differential_adc_val[0]; - else - *val = st->differential_adc_val[1]; - ret = IIO_VAL_INT; - } else { - *val = st->single_ended_adc_val; - ret = IIO_VAL_INT; - } - break; - case IIO_ACCEL: - *val = st->accel_val; - ret = IIO_VAL_INT; - break; - default: - break; - } - break; - case IIO_CHAN_INFO_PROCESSED: - switch (chan->type) { - case IIO_STEPS: - *val = st->steps; - ret = IIO_VAL_INT; - break; - case IIO_ACTIVITY: - switch (chan->channel2) { - case IIO_MOD_RUNNING: - *val = st->activity_running; - ret = IIO_VAL_INT; - break; - case IIO_MOD_WALKING: - *val = st->activity_walking; - ret = IIO_VAL_INT; - break; - default: - break; - } - break; - default: - break; - } - break; - case IIO_CHAN_INFO_OFFSET: - /* only single ended adc -> 7 */ - *val = 7; - ret = IIO_VAL_INT; - break; - case IIO_CHAN_INFO_SCALE: - switch (chan->type) { - case IIO_VOLTAGE: - switch (chan->differential) { - case 0: - /* only single ended adc -> 0.001333 */ - *val = 0; - *val2 = 1333; - ret = IIO_VAL_INT_PLUS_MICRO; - break; - case 1: - /* all differential adc channels -> - * 0.000001344 */ - *val = 0; - *val2 = 1344; - ret = IIO_VAL_INT_PLUS_NANO; - } - break; - default: - break; - } - break; - case IIO_CHAN_INFO_CALIBBIAS: - /* only the acceleration axis - read from cache */ - *val = st->accel_calibbias; - ret = IIO_VAL_INT; - break; - case IIO_CHAN_INFO_CALIBSCALE: - *val = st->accel_calibscale->val; - *val2 = st->accel_calibscale->val2; - ret = IIO_VAL_INT_PLUS_MICRO; - break; - case IIO_CHAN_INFO_SAMP_FREQ: - *val = 3; - *val2 = 33; - ret = IIO_VAL_INT_PLUS_NANO; - break; - case IIO_CHAN_INFO_ENABLE: - switch (chan->type) { - case IIO_STEPS: - *val = st->steps_enabled; - ret = IIO_VAL_INT; - break; - default: - break; - } - break; - case IIO_CHAN_INFO_CALIBHEIGHT: - switch (chan->type) { - case IIO_STEPS: - *val = st->height; - ret = IIO_VAL_INT; - break; - default: - break; - } - break; - - default: - break; - } - mutex_unlock(&st->lock); - return ret; -} - -/** - * iio_dummy_write_raw() - data write function. - * @indio_dev: the struct iio_dev associated with this device instance - * @chan: the channel whose data is to be written - * @val: first element of value to set (typically INT) - * @val2: second element of value to set (typically MICRO) - * @mask: what we actually want to write as per the info_mask_* - * in iio_chan_spec. - * - * Note that all raw writes are assumed IIO_VAL_INT and info mask elements - * are assumed to be IIO_INT_PLUS_MICRO unless the callback write_raw_get_fmt - * in struct iio_info is provided by the driver. - */ -static int iio_dummy_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - int i; - int ret = 0; - struct iio_dummy_state *st = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_RAW: - switch (chan->type) { - case IIO_VOLTAGE: - if (chan->output == 0) - return -EINVAL; - - /* Locking not required as writing single value */ - mutex_lock(&st->lock); - st->dac_val = val; - mutex_unlock(&st->lock); - return 0; - default: - return -EINVAL; - } - case IIO_CHAN_INFO_PROCESSED: - switch (chan->type) { - case IIO_STEPS: - mutex_lock(&st->lock); - st->steps = val; - mutex_unlock(&st->lock); - return 0; - case IIO_ACTIVITY: - if (val < 0) - val = 0; - if (val > 100) - val = 100; - switch (chan->channel2) { - case IIO_MOD_RUNNING: - st->activity_running = val; - return 0; - case IIO_MOD_WALKING: - st->activity_walking = val; - return 0; - default: - return -EINVAL; - } - break; - default: - return -EINVAL; - } - case IIO_CHAN_INFO_CALIBSCALE: - mutex_lock(&st->lock); - /* Compare against table - hard matching here */ - for (i = 0; i < ARRAY_SIZE(dummy_scales); i++) - if (val == dummy_scales[i].val && - val2 == dummy_scales[i].val2) - break; - if (i == ARRAY_SIZE(dummy_scales)) - ret = -EINVAL; - else - st->accel_calibscale = &dummy_scales[i]; - mutex_unlock(&st->lock); - return ret; - case IIO_CHAN_INFO_CALIBBIAS: - mutex_lock(&st->lock); - st->accel_calibbias = val; - mutex_unlock(&st->lock); - return 0; - case IIO_CHAN_INFO_ENABLE: - switch (chan->type) { - case IIO_STEPS: - mutex_lock(&st->lock); - st->steps_enabled = val; - mutex_unlock(&st->lock); - return 0; - default: - return -EINVAL; - } - case IIO_CHAN_INFO_CALIBHEIGHT: - switch (chan->type) { - case IIO_STEPS: - st->height = val; - return 0; - default: - return -EINVAL; - } - - default: - return -EINVAL; - } -} - -/* - * Device type specific information. - */ -static const struct iio_info iio_dummy_info = { - .driver_module = THIS_MODULE, - .read_raw = &iio_dummy_read_raw, - .write_raw = &iio_dummy_write_raw, -#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS - .read_event_config = &iio_simple_dummy_read_event_config, - .write_event_config = &iio_simple_dummy_write_event_config, - .read_event_value = &iio_simple_dummy_read_event_value, - .write_event_value = &iio_simple_dummy_write_event_value, -#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ -}; - -/** - * iio_dummy_init_device() - device instance specific init - * @indio_dev: the iio device structure - * - * Most drivers have one of these to set up default values, - * reset the device to known state etc. - */ -static int iio_dummy_init_device(struct iio_dev *indio_dev) -{ - struct iio_dummy_state *st = iio_priv(indio_dev); - - st->dac_val = 0; - st->single_ended_adc_val = 73; - st->differential_adc_val[0] = 33; - st->differential_adc_val[1] = -34; - st->accel_val = 34; - st->accel_calibbias = -7; - st->accel_calibscale = &dummy_scales[0]; - st->steps = 47; - st->activity_running = 98; - st->activity_walking = 4; - - return 0; -} - -/** - * iio_dummy_probe() - device instance probe - * @index: an id number for this instance. - * - * Arguments are bus type specific. - * I2C: iio_dummy_probe(struct i2c_client *client, - * const struct i2c_device_id *id) - * SPI: iio_dummy_probe(struct spi_device *spi) - */ -static int iio_dummy_probe(int index) -{ - int ret; - struct iio_dev *indio_dev; - struct iio_dummy_state *st; - - /* - * Allocate an IIO device. - * - * This structure contains all generic state - * information about the device instance. - * It also has a region (accessed by iio_priv() - * for chip specific state information. - */ - indio_dev = iio_device_alloc(sizeof(*st)); - if (!indio_dev) { - ret = -ENOMEM; - goto error_ret; - } - - st = iio_priv(indio_dev); - mutex_init(&st->lock); - - iio_dummy_init_device(indio_dev); - /* - * With hardware: Set the parent device. - * indio_dev->dev.parent = &spi->dev; - * indio_dev->dev.parent = &client->dev; - */ - - /* - * Make the iio_dev struct available to remove function. - * Bus equivalents - * i2c_set_clientdata(client, indio_dev); - * spi_set_drvdata(spi, indio_dev); - */ - iio_dummy_devs[index] = indio_dev; - - /* - * Set the device name. - * - * This is typically a part number and obtained from the module - * id table. - * e.g. for i2c and spi: - * indio_dev->name = id->name; - * indio_dev->name = spi_get_device_id(spi)->name; - */ - indio_dev->name = iio_dummy_part_number; - - /* Provide description of available channels */ - indio_dev->channels = iio_dummy_channels; - indio_dev->num_channels = ARRAY_SIZE(iio_dummy_channels); - - /* - * Provide device type specific interface functions and - * constant data. - */ - indio_dev->info = &iio_dummy_info; - - /* Specify that device provides sysfs type interfaces */ - indio_dev->modes = INDIO_DIRECT_MODE; - - ret = iio_simple_dummy_events_register(indio_dev); - if (ret < 0) - goto error_free_device; - - ret = iio_simple_dummy_configure_buffer(indio_dev); - if (ret < 0) - goto error_unregister_events; - - ret = iio_device_register(indio_dev); - if (ret < 0) - goto error_unconfigure_buffer; - - return 0; -error_unconfigure_buffer: - iio_simple_dummy_unconfigure_buffer(indio_dev); -error_unregister_events: - iio_simple_dummy_events_unregister(indio_dev); -error_free_device: - iio_device_free(indio_dev); -error_ret: - return ret; -} - -/** - * iio_dummy_remove() - device instance removal function - * @index: device index. - * - * Parameters follow those of iio_dummy_probe for buses. - */ -static void iio_dummy_remove(int index) -{ - /* - * Get a pointer to the device instance iio_dev structure - * from the bus subsystem. E.g. - * struct iio_dev *indio_dev = i2c_get_clientdata(client); - * struct iio_dev *indio_dev = spi_get_drvdata(spi); - */ - struct iio_dev *indio_dev = iio_dummy_devs[index]; - - /* Unregister the device */ - iio_device_unregister(indio_dev); - - /* Device specific code to power down etc */ - - /* Buffered capture related cleanup */ - iio_simple_dummy_unconfigure_buffer(indio_dev); - - iio_simple_dummy_events_unregister(indio_dev); - - /* Free all structures */ - iio_device_free(indio_dev); -} - -/** - * iio_dummy_init() - device driver registration - * - * Varies depending on bus type of the device. As there is no device - * here, call probe directly. For information on device registration - * i2c: - * Documentation/i2c/writing-clients - * spi: - * Documentation/spi/spi-summary - */ -static __init int iio_dummy_init(void) -{ - int i, ret; - - if (instances > 10) { - instances = 1; - return -EINVAL; - } - - /* Fake a bus */ - iio_dummy_devs = kcalloc(instances, sizeof(*iio_dummy_devs), - GFP_KERNEL); - /* Here we have no actual device so call probe */ - for (i = 0; i < instances; i++) { - ret = iio_dummy_probe(i); - if (ret < 0) - goto error_remove_devs; - } - return 0; - -error_remove_devs: - while (i--) - iio_dummy_remove(i); - - kfree(iio_dummy_devs); - return ret; -} -module_init(iio_dummy_init); - -/** - * iio_dummy_exit() - device driver removal - * - * Varies depending on bus type of the device. - * As there is no device here, call remove directly. - */ -static __exit void iio_dummy_exit(void) -{ - int i; - - for (i = 0; i < instances; i++) - iio_dummy_remove(i); - kfree(iio_dummy_devs); -} -module_exit(iio_dummy_exit); - -MODULE_AUTHOR("Jonathan Cameron "); -MODULE_DESCRIPTION("IIO dummy driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/iio/iio_simple_dummy.h b/drivers/staging/iio/iio_simple_dummy.h deleted file mode 100644 index 5c2f4d0..0000000 --- a/drivers/staging/iio/iio_simple_dummy.h +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Copyright (c) 2011 Jonathan Cameron - * - * 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. - * - * Join together the various functionality of iio_simple_dummy driver - */ - -#ifndef _IIO_SIMPLE_DUMMY_H_ -#define _IIO_SIMPLE_DUMMY_H_ -#include - -struct iio_dummy_accel_calibscale; -struct iio_dummy_regs; - -/** - * struct iio_dummy_state - device instance specific state. - * @dac_val: cache for dac value - * @single_ended_adc_val: cache for single ended adc value - * @differential_adc_val: cache for differential adc value - * @accel_val: cache for acceleration value - * @accel_calibbias: cache for acceleration calibbias - * @accel_calibscale: cache for acceleration calibscale - * @lock: lock to ensure state is consistent - * @event_irq: irq number for event line (faked) - * @event_val: cache for event threshold value - * @event_en: cache of whether event is enabled - */ -struct iio_dummy_state { - int dac_val; - int single_ended_adc_val; - int differential_adc_val[2]; - int accel_val; - int accel_calibbias; - int activity_running; - int activity_walking; - const struct iio_dummy_accel_calibscale *accel_calibscale; - struct mutex lock; - struct iio_dummy_regs *regs; - int steps_enabled; - int steps; - int height; -#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS - int event_irq; - int event_val; - bool event_en; - s64 event_timestamp; -#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */ -}; - -#ifdef CONFIG_IIO_SIMPLE_DUMMY_EVENTS - -struct iio_dev; - -int iio_simple_dummy_read_event_config(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir); - -int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - int state); - -int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, int *val, - int *val2); - -int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, int val, - int val2); - -int iio_simple_dummy_events_register(struct iio_dev *indio_dev); -void iio_simple_dummy_events_unregister(struct iio_dev *indio_dev); - -#else /* Stubs for when events are disabled at compile time */ - -static inline int -iio_simple_dummy_events_register(struct iio_dev *indio_dev) -{ - return 0; -}; - -static inline void -iio_simple_dummy_events_unregister(struct iio_dev *indio_dev) -{ }; - -#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS*/ - -/** - * enum iio_simple_dummy_scan_elements - scan index enum - * @voltage0: the single ended voltage channel - * @diffvoltage1m2: first differential channel - * @diffvoltage3m4: second differenial channel - * @accelx: acceleration channel - * - * Enum provides convenient numbering for the scan index. - */ -enum iio_simple_dummy_scan_elements { - voltage0, - diffvoltage1m2, - diffvoltage3m4, - accelx, -}; - -#ifdef CONFIG_IIO_SIMPLE_DUMMY_BUFFER -int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev); -void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev); -#else -static inline int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev) -{ - return 0; -}; - -static inline -void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev) -{}; - -#endif /* CONFIG_IIO_SIMPLE_DUMMY_BUFFER */ -#endif /* _IIO_SIMPLE_DUMMY_H_ */ diff --git a/drivers/staging/iio/iio_simple_dummy_buffer.c b/drivers/staging/iio/iio_simple_dummy_buffer.c deleted file mode 100644 index 00ed774..0000000 --- a/drivers/staging/iio/iio_simple_dummy_buffer.c +++ /dev/null @@ -1,192 +0,0 @@ -/** - * Copyright (c) 2011 Jonathan Cameron - * - * 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. - * - * Buffer handling elements of industrial I/O reference driver. - * Uses the kfifo buffer. - * - * To test without hardware use the sysfs trigger. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "iio_simple_dummy.h" - -/* Some fake data */ - -static const s16 fakedata[] = { - [voltage0] = 7, - [diffvoltage1m2] = -33, - [diffvoltage3m4] = -2, - [accelx] = 344, -}; - -/** - * iio_simple_dummy_trigger_h() - the trigger handler function - * @irq: the interrupt number - * @p: private data - always a pointer to the poll func. - * - * This is the guts of buffered capture. On a trigger event occurring, - * if the pollfunc is attached then this handler is called as a threaded - * interrupt (and hence may sleep). It is responsible for grabbing data - * from the device and pushing it into the associated buffer. - */ -static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) -{ - struct iio_poll_func *pf = p; - struct iio_dev *indio_dev = pf->indio_dev; - int len = 0; - u16 *data; - - data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); - if (!data) - goto done; - - if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength)) { - /* - * Three common options here: - * hardware scans: certain combinations of channels make - * up a fast read. The capture will consist of all of them. - * Hence we just call the grab data function and fill the - * buffer without processing. - * software scans: can be considered to be random access - * so efficient reading is just a case of minimal bus - * transactions. - * software culled hardware scans: - * occasionally a driver may process the nearest hardware - * scan to avoid storing elements that are not desired. This - * is the fiddliest option by far. - * Here let's pretend we have random access. And the values are - * in the constant table fakedata. - */ - int i, j; - - for (i = 0, j = 0; - i < bitmap_weight(indio_dev->active_scan_mask, - indio_dev->masklength); - i++, j++) { - j = find_next_bit(indio_dev->active_scan_mask, - indio_dev->masklength, j); - /* random access read from the 'device' */ - data[i] = fakedata[j]; - len += 2; - } - } - - iio_push_to_buffers_with_timestamp(indio_dev, data, iio_get_time_ns()); - - kfree(data); - -done: - /* - * Tell the core we are done with this trigger and ready for the - * next one. - */ - iio_trigger_notify_done(indio_dev->trig); - - return IRQ_HANDLED; -} - -static const struct iio_buffer_setup_ops iio_simple_dummy_buffer_setup_ops = { - /* - * iio_triggered_buffer_postenable: - * Generic function that simply attaches the pollfunc to the trigger. - * Replace this to mess with hardware state before we attach the - * trigger. - */ - .postenable = &iio_triggered_buffer_postenable, - /* - * iio_triggered_buffer_predisable: - * Generic function that simple detaches the pollfunc from the trigger. - * Replace this to put hardware state back again after the trigger is - * detached but before userspace knows we have disabled the ring. - */ - .predisable = &iio_triggered_buffer_predisable, -}; - -int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev) -{ - int ret; - struct iio_buffer *buffer; - - /* Allocate a buffer to use - here a kfifo */ - buffer = iio_kfifo_allocate(); - if (!buffer) { - ret = -ENOMEM; - goto error_ret; - } - - iio_device_attach_buffer(indio_dev, buffer); - - /* Enable timestamps by default */ - buffer->scan_timestamp = true; - - /* - * Tell the core what device type specific functions should - * be run on either side of buffer capture enable / disable. - */ - indio_dev->setup_ops = &iio_simple_dummy_buffer_setup_ops; - - /* - * Configure a polling function. - * When a trigger event with this polling function connected - * occurs, this function is run. Typically this grabs data - * from the device. - * - * NULL for the bottom half. This is normally implemented only if we - * either want to ping a capture now pin (no sleeping) or grab - * a timestamp as close as possible to a data ready trigger firing. - * - * IRQF_ONESHOT ensures irqs are masked such that only one instance - * of the handler can run at a time. - * - * "iio_simple_dummy_consumer%d" formatting string for the irq 'name' - * as seen under /proc/interrupts. Remaining parameters as per printk. - */ - indio_dev->pollfunc = iio_alloc_pollfunc(NULL, - &iio_simple_dummy_trigger_h, - IRQF_ONESHOT, - indio_dev, - "iio_simple_dummy_consumer%d", - indio_dev->id); - - if (!indio_dev->pollfunc) { - ret = -ENOMEM; - goto error_free_buffer; - } - - /* - * Notify the core that this device is capable of buffered capture - * driven by a trigger. - */ - indio_dev->modes |= INDIO_BUFFER_TRIGGERED; - - return 0; - -error_free_buffer: - iio_kfifo_free(indio_dev->buffer); -error_ret: - return ret; -} - -/** - * iio_simple_dummy_unconfigure_buffer() - release buffer resources - * @indo_dev: device instance state - */ -void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev) -{ - iio_dealloc_pollfunc(indio_dev->pollfunc); - iio_kfifo_free(indio_dev->buffer); -} diff --git a/drivers/staging/iio/iio_simple_dummy_events.c b/drivers/staging/iio/iio_simple_dummy_events.c deleted file mode 100644 index bfbf1c5..0000000 --- a/drivers/staging/iio/iio_simple_dummy_events.c +++ /dev/null @@ -1,276 +0,0 @@ -/** - * Copyright (c) 2011 Jonathan Cameron - * - * 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. - * - * Event handling elements of industrial I/O reference driver. - */ -#include -#include -#include -#include - -#include -#include -#include -#include "iio_simple_dummy.h" - -/* Evgen 'fakes' interrupt events for this example */ -#include "iio_dummy_evgen.h" - -/** - * iio_simple_dummy_read_event_config() - is event enabled? - * @indio_dev: the device instance data - * @chan: channel for the event whose state is being queried - * @type: type of the event whose state is being queried - * @dir: direction of the vent whose state is being queried - * - * This function would normally query the relevant registers or a cache to - * discover if the event generation is enabled on the device. - */ -int iio_simple_dummy_read_event_config(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir) -{ - struct iio_dummy_state *st = iio_priv(indio_dev); - - return st->event_en; -} - -/** - * iio_simple_dummy_write_event_config() - set whether event is enabled - * @indio_dev: the device instance data - * @chan: channel for the event whose state is being set - * @type: type of the event whose state is being set - * @dir: direction of the vent whose state is being set - * @state: whether to enable or disable the device. - * - * This function would normally set the relevant registers on the devices - * so that it generates the specified event. Here it just sets up a cached - * value. - */ -int iio_simple_dummy_write_event_config(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - int state) -{ - struct iio_dummy_state *st = iio_priv(indio_dev); - - /* - * Deliberately over the top code splitting to illustrate - * how this is done when multiple events exist. - */ - switch (chan->type) { - case IIO_VOLTAGE: - switch (type) { - case IIO_EV_TYPE_THRESH: - if (dir == IIO_EV_DIR_RISING) - st->event_en = state; - else - return -EINVAL; - default: - return -EINVAL; - } - break; - case IIO_ACTIVITY: - switch (type) { - case IIO_EV_TYPE_THRESH: - st->event_en = state; - break; - default: - return -EINVAL; - } - break; - case IIO_STEPS: - switch (type) { - case IIO_EV_TYPE_CHANGE: - st->event_en = state; - break; - default: - return -EINVAL; - } - break; - default: - return -EINVAL; - } - - return 0; -} - -/** - * iio_simple_dummy_read_event_value() - get value associated with event - * @indio_dev: device instance specific data - * @chan: channel for the event whose value is being read - * @type: type of the event whose value is being read - * @dir: direction of the vent whose value is being read - * @info: info type of the event whose value is being read - * @val: value for the event code. - * - * Many devices provide a large set of events of which only a subset may - * be enabled at a time, with value registers whose meaning changes depending - * on the event enabled. This often means that the driver must cache the values - * associated with each possible events so that the right value is in place when - * the enabled event is changed. - */ -int iio_simple_dummy_read_event_value(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int *val, int *val2) -{ - struct iio_dummy_state *st = iio_priv(indio_dev); - - *val = st->event_val; - - return IIO_VAL_INT; -} - -/** - * iio_simple_dummy_write_event_value() - set value associate with event - * @indio_dev: device instance specific data - * @chan: channel for the event whose value is being set - * @type: type of the event whose value is being set - * @dir: direction of the vent whose value is being set - * @info: info type of the event whose value is being set - * @val: the value to be set. - */ -int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int val, int val2) -{ - struct iio_dummy_state *st = iio_priv(indio_dev); - - st->event_val = val; - - return 0; -} - -static irqreturn_t iio_simple_dummy_get_timestamp(int irq, void *private) -{ - struct iio_dev *indio_dev = private; - struct iio_dummy_state *st = iio_priv(indio_dev); - - st->event_timestamp = iio_get_time_ns(); - return IRQ_HANDLED; -} - -/** - * iio_simple_dummy_event_handler() - identify and pass on event - * @irq: irq of event line - * @private: pointer to device instance state. - * - * This handler is responsible for querying the device to find out what - * event occurred and for then pushing that event towards userspace. - * Here only one event occurs so we push that directly on with locally - * grabbed timestamp. - */ -static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private) -{ - struct iio_dev *indio_dev = private; - struct iio_dummy_state *st = iio_priv(indio_dev); - - dev_dbg(&indio_dev->dev, "id %x event %x\n", - st->regs->reg_id, st->regs->reg_data); - - switch (st->regs->reg_data) { - case 0: - iio_push_event(indio_dev, - IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0, - IIO_EV_DIR_RISING, - IIO_EV_TYPE_THRESH, 0, 0, 0), - st->event_timestamp); - break; - case 1: - if (st->activity_running > st->event_val) - iio_push_event(indio_dev, - IIO_EVENT_CODE(IIO_ACTIVITY, 0, - IIO_MOD_RUNNING, - IIO_EV_DIR_RISING, - IIO_EV_TYPE_THRESH, - 0, 0, 0), - st->event_timestamp); - break; - case 2: - if (st->activity_walking < st->event_val) - iio_push_event(indio_dev, - IIO_EVENT_CODE(IIO_ACTIVITY, 0, - IIO_MOD_WALKING, - IIO_EV_DIR_FALLING, - IIO_EV_TYPE_THRESH, - 0, 0, 0), - st->event_timestamp); - break; - case 3: - iio_push_event(indio_dev, - IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD, - IIO_EV_DIR_NONE, - IIO_EV_TYPE_CHANGE, 0, 0, 0), - st->event_timestamp); - break; - default: - break; - } - - return IRQ_HANDLED; -} - -/** - * iio_simple_dummy_events_register() - setup interrupt handling for events - * @indio_dev: device instance data - * - * This function requests the threaded interrupt to handle the events. - * Normally the irq is a hardware interrupt and the number comes - * from board configuration files. Here we get it from a companion - * module that fakes the interrupt for us. Note that module in - * no way forms part of this example. Just assume that events magically - * appear via the provided interrupt. - */ -int iio_simple_dummy_events_register(struct iio_dev *indio_dev) -{ - struct iio_dummy_state *st = iio_priv(indio_dev); - int ret; - - /* Fire up event source - normally not present */ - st->event_irq = iio_dummy_evgen_get_irq(); - if (st->event_irq < 0) { - ret = st->event_irq; - goto error_ret; - } - st->regs = iio_dummy_evgen_get_regs(st->event_irq); - - ret = request_threaded_irq(st->event_irq, - &iio_simple_dummy_get_timestamp, - &iio_simple_dummy_event_handler, - IRQF_ONESHOT, - "iio_simple_event", - indio_dev); - if (ret < 0) - goto error_free_evgen; - return 0; - -error_free_evgen: - iio_dummy_evgen_release_irq(st->event_irq); -error_ret: - return ret; -} - -/** - * iio_simple_dummy_events_unregister() - tidy up interrupt handling on remove - * @indio_dev: device instance data - */ -void iio_simple_dummy_events_unregister(struct iio_dev *indio_dev) -{ - struct iio_dummy_state *st = iio_priv(indio_dev); - - free_irq(st->event_irq, indio_dev); - /* Not part of normal driver */ - iio_dummy_evgen_release_irq(st->event_irq); -} -- cgit v0.10.2 From 3a872138e4b78e7f8bfce63f0da788c0cfd80e53 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 14 Oct 2015 14:54:38 +0200 Subject: iio: adc: mcp320x: Deprecated compatible strings with no vendor prefix The Microchip Analog to Digital Converter (ADC) Device Tree binding documents compatible strings with no vendor prefix. Since it should compatible strings with also a vendor, add these to the binding doc and mark the old ones as deprecated. The driver says that the device is from Microchip Technology which is listed in Documentation/devicetree/bindings/vendor-prefixes.txt so use the documented prefix. Signed-off-by: Javier Martinez Canillas Acked-by: Michael Welling Signed-off-by: Jonathan Cameron diff --git a/Documentation/devicetree/bindings/iio/adc/mcp320x.txt b/Documentation/devicetree/bindings/iio/adc/mcp320x.txt index 2a1f3af..bcd3ac8 100644 --- a/Documentation/devicetree/bindings/iio/adc/mcp320x.txt +++ b/Documentation/devicetree/bindings/iio/adc/mcp320x.txt @@ -10,16 +10,28 @@ must be specified. Required properties: - compatible: Must be one of the following, depending on the model: - "mcp3001" - "mcp3002" - "mcp3004" - "mcp3008" - "mcp3201" - "mcp3202" - "mcp3204" - "mcp3208" - "mcp3301" + "mcp3001" (DEPRECATED) + "mcp3002" (DEPRECATED) + "mcp3004" (DEPRECATED) + "mcp3008" (DEPRECATED) + "mcp3201" (DEPRECATED) + "mcp3202" (DEPRECATED) + "mcp3204" (DEPRECATED) + "mcp3208" (DEPRECATED) + "mcp3301" (DEPRECATED) + "microchip,mcp3001" + "microchip,mcp3002" + "microchip,mcp3004" + "microchip,mcp3008" + "microchip,mcp3201" + "microchip,mcp3202" + "microchip,mcp3204" + "microchip,mcp3208" + "microchip,mcp3301" + + NOTE: The use of the compatibles with no vendor prefix + is deprecated and only listed because old DT use them. Examples: spi_controller { -- cgit v0.10.2 From 0d0e53844797200aa026dfbdfe62f90ccff88300 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Wed, 14 Oct 2015 14:54:39 +0200 Subject: iio: adc: mcp320x: Add compatible with vendor prefix to OF table The driver Device Tree binding now documents compatible strings that have a vendor prefix, so add these to the OF device ID table to match and mark the old ones as deprecated explaining that should not be used anymore. Signed-off-by: Javier Martinez Canillas Acked-by: Michael Welling Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c index 41a21e9..9fcb8b6 100644 --- a/drivers/iio/adc/mcp320x.c +++ b/drivers/iio/adc/mcp320x.c @@ -354,6 +354,7 @@ static int mcp320x_remove(struct spi_device *spi) #if defined(CONFIG_OF) static const struct of_device_id mcp320x_dt_ids[] = { + /* NOTE: The use of compatibles with no vendor prefix is deprecated. */ { .compatible = "mcp3001", .data = &mcp320x_chip_infos[mcp3001], @@ -382,6 +383,33 @@ static const struct of_device_id mcp320x_dt_ids[] = { .compatible = "mcp3301", .data = &mcp320x_chip_infos[mcp3301], }, { + .compatible = "microchip,mcp3001", + .data = &mcp320x_chip_infos[mcp3001], + }, { + .compatible = "microchip,mcp3002", + .data = &mcp320x_chip_infos[mcp3002], + }, { + .compatible = "microchip,mcp3004", + .data = &mcp320x_chip_infos[mcp3004], + }, { + .compatible = "microchip,mcp3008", + .data = &mcp320x_chip_infos[mcp3008], + }, { + .compatible = "microchip,mcp3201", + .data = &mcp320x_chip_infos[mcp3201], + }, { + .compatible = "microchip,mcp3202", + .data = &mcp320x_chip_infos[mcp3202], + }, { + .compatible = "microchip,mcp3204", + .data = &mcp320x_chip_infos[mcp3204], + }, { + .compatible = "microchip,mcp3208", + .data = &mcp320x_chip_infos[mcp3208], + }, { + .compatible = "microchip,mcp3301", + .data = &mcp320x_chip_infos[mcp3301], + }, { } }; MODULE_DEVICE_TABLE(of, mcp320x_dt_ids); -- cgit v0.10.2 From f0566c0c405de543efb6fb8b8988cc7743d85ea6 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 13 Oct 2015 18:10:24 +0200 Subject: iio: Set device watermark based on watermark of all attached buffers Currently the watermark of the device is only set based on the watermark that is set for the user space buffer. This doesn't consider the watermarks set on any attached in-kernel buffers. Change this so that the watermark of the device should be the minimum of the watermarks over all attached buffers. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index d7e908a..7340922 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -610,6 +610,7 @@ static void iio_free_scan_mask(struct iio_dev *indio_dev, struct iio_device_config { unsigned int mode; + unsigned int watermark; const unsigned long *scan_mask; unsigned int scan_bytes; bool scan_timestamp; @@ -642,10 +643,14 @@ static int iio_verify_update(struct iio_dev *indio_dev, if (buffer == remove_buffer) continue; modes &= buffer->access->modes; + config->watermark = min(config->watermark, buffer->watermark); } - if (insert_buffer) + if (insert_buffer) { modes &= insert_buffer->access->modes; + config->watermark = min(config->watermark, + insert_buffer->watermark); + } /* Definitely possible for devices to support both of these. */ if ((modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) { @@ -743,6 +748,10 @@ static int iio_enable_buffers(struct iio_dev *indio_dev, } } + if (indio_dev->info->hwfifo_set_watermark) + indio_dev->info->hwfifo_set_watermark(indio_dev, + config->watermark); + indio_dev->currentmode = config->mode; if (indio_dev->setup_ops->postenable) { @@ -974,9 +983,6 @@ static ssize_t iio_buffer_store_watermark(struct device *dev, } buffer->watermark = val; - - if (indio_dev->info->hwfifo_set_watermark) - indio_dev->info->hwfifo_set_watermark(indio_dev, val); out: mutex_unlock(&indio_dev->mlock); -- cgit v0.10.2 From 4a60535726d90bfad16b5c52dcffaeede9fb84a9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 13 Oct 2015 18:10:25 +0200 Subject: iio:iio_buffer_init(): Only set watermark if not already set Only initialize the watermark field if it is still 0. This allows drivers to provide a custom default watermark value. E.g. some driver might have a fixed watermark or can only support watermarks within a certain range and the initial value for the watermark should be within this range. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 7340922..5f2c8c8 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -193,7 +193,8 @@ void iio_buffer_init(struct iio_buffer *buffer) INIT_LIST_HEAD(&buffer->buffer_list); init_waitqueue_head(&buffer->pollq); kref_init(&buffer->ref); - buffer->watermark = 1; + if (!buffer->watermark) + buffer->watermark = 1; } EXPORT_SYMBOL(iio_buffer_init); -- cgit v0.10.2 From b440655b896b2d5a2fb5f918801fb0e281a537cd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 13 Oct 2015 18:10:26 +0200 Subject: iio: Add support for indicating fixed watermarks For buffers which have a fixed wake-up watermark the watermark attribute should be read-only. Add a new FIXED_WATERMARK flag to the struct iio_buffer_access_funcs, which can be set by a buffer implementation. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 5f2c8c8..98a6447 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -998,6 +998,8 @@ static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, iio_buffer_show_enable, iio_buffer_store_enable); static DEVICE_ATTR(watermark, S_IRUGO | S_IWUSR, iio_buffer_show_watermark, iio_buffer_store_watermark); +static struct device_attribute dev_attr_watermark_ro = __ATTR(watermark, + S_IRUGO, iio_buffer_show_watermark, NULL); static struct attribute *iio_buffer_attrs[] = { &dev_attr_length.attr, @@ -1040,6 +1042,9 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) if (!buffer->access->set_length) attr[0] = &dev_attr_length_ro.attr; + if (buffer->access->flags & INDIO_BUFFER_FLAG_FIXED_WATERMARK) + attr[2] = &dev_attr_watermark_ro.attr; + if (buffer->attrs) memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs, sizeof(struct attribute *) * attrcount); diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index 1600c55..4d99a53 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -18,6 +18,12 @@ struct iio_buffer; /** + * INDIO_BUFFER_FLAG_FIXED_WATERMARK - Watermark level of the buffer can not be + * configured. It has a fixed value which will be buffer specific. + */ +#define INDIO_BUFFER_FLAG_FIXED_WATERMARK BIT(0) + +/** * struct iio_buffer_access_funcs - access functions for buffers. * @store_to: actually store stuff to the buffer * @read_first_n: try to get a specified number of bytes (must exist) @@ -30,6 +36,7 @@ struct iio_buffer; * @release: called when the last reference to the buffer is dropped, * should free all resources allocated by the buffer. * @modes: Supported operating modes by this buffer type + * @flags: A bitmask combination of INDIO_BUFFER_FLAG_* * * The purpose of this structure is to make the buffer element * modular as event for a given driver, different usecases may require @@ -54,6 +61,7 @@ struct iio_buffer_access_funcs { void (*release)(struct iio_buffer *buffer); unsigned int modes; + unsigned int flags; }; /** -- cgit v0.10.2 From e18a2ad45caeb11226e49c25068d0f2efe2adf6c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 13 Oct 2015 18:10:27 +0200 Subject: iio: Add buffer enable/disable callbacks This patch adds a enable and disable callback that is called when the buffer is enabled/disabled. This can be used by buffer implementations that need to do some setup or teardown work. E.g. a DMA based buffer can use this to start/stop the DMA transfer. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 98a6447..a4b164a 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -568,6 +568,22 @@ static void iio_buffer_deactivate_all(struct iio_dev *indio_dev) iio_buffer_deactivate(buffer); } +static int iio_buffer_enable(struct iio_buffer *buffer, + struct iio_dev *indio_dev) +{ + if (!buffer->access->enable) + return 0; + return buffer->access->enable(buffer, indio_dev); +} + +static int iio_buffer_disable(struct iio_buffer *buffer, + struct iio_dev *indio_dev) +{ + if (!buffer->access->disable) + return 0; + return buffer->access->disable(buffer, indio_dev); +} + static void iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev, struct iio_buffer *buffer) { @@ -719,6 +735,7 @@ static int iio_verify_update(struct iio_dev *indio_dev, static int iio_enable_buffers(struct iio_dev *indio_dev, struct iio_device_config *config) { + struct iio_buffer *buffer; int ret; indio_dev->active_scan_mask = config->scan_mask; @@ -753,6 +770,12 @@ static int iio_enable_buffers(struct iio_dev *indio_dev, indio_dev->info->hwfifo_set_watermark(indio_dev, config->watermark); + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) { + ret = iio_buffer_enable(buffer, indio_dev); + if (ret) + goto err_disable_buffers; + } + indio_dev->currentmode = config->mode; if (indio_dev->setup_ops->postenable) { @@ -760,12 +783,16 @@ static int iio_enable_buffers(struct iio_dev *indio_dev, if (ret) { dev_dbg(&indio_dev->dev, "Buffer not started: postenable failed (%d)\n", ret); - goto err_run_postdisable; + goto err_disable_buffers; } } return 0; +err_disable_buffers: + list_for_each_entry_continue_reverse(buffer, &indio_dev->buffer_list, + buffer_list) + iio_buffer_disable(buffer, indio_dev); err_run_postdisable: indio_dev->currentmode = INDIO_DIRECT_MODE; if (indio_dev->setup_ops->postdisable) @@ -778,6 +805,7 @@ err_undo_config: static int iio_disable_buffers(struct iio_dev *indio_dev) { + struct iio_buffer *buffer; int ret = 0; int ret2; @@ -798,6 +826,12 @@ static int iio_disable_buffers(struct iio_dev *indio_dev) ret = ret2; } + list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) { + ret2 = iio_buffer_disable(buffer, indio_dev); + if (ret2 && !ret) + ret = ret2; + } + indio_dev->currentmode = INDIO_DIRECT_MODE; if (indio_dev->setup_ops->postdisable) { diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index 4d99a53..2ec3ad5 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -33,6 +33,11 @@ struct iio_buffer; * storage. * @set_bytes_per_datum:set number of bytes per datum * @set_length: set number of datums in buffer + * @enable: called if the buffer is attached to a device and the + * device starts sampling. Calls are balanced with + * @disable. + * @disable: called if the buffer is attached to a device and the + * device stops sampling. Calles are balanced with @enable. * @release: called when the last reference to the buffer is dropped, * should free all resources allocated by the buffer. * @modes: Supported operating modes by this buffer type @@ -58,6 +63,9 @@ struct iio_buffer_access_funcs { int (*set_bytes_per_datum)(struct iio_buffer *buffer, size_t bpd); int (*set_length)(struct iio_buffer *buffer, int length); + int (*enable)(struct iio_buffer *buffer, struct iio_dev *indio_dev); + int (*disable)(struct iio_buffer *buffer, struct iio_dev *indio_dev); + void (*release)(struct iio_buffer *buffer); unsigned int modes; -- cgit v0.10.2 From 670b19ae9bfdbcb4ce2c2ffb2ec1659a7f4a2074 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 13 Oct 2015 18:10:28 +0200 Subject: iio: Add generic DMA buffer infrastructure The traditional approach used in IIO to implement buffered capture requires the generation of at least one interrupt per sample. In the interrupt handler the driver reads the sample from the device and copies it to a software buffer. This approach has a rather large per sample overhead associated with it. And while it works fine for samplerates in the range of up to 1000 samples per second it starts to consume a rather large share of the available CPU processing time once we go beyond that, this is especially true on an embedded system with limited processing power. The regular interrupt also causes increased power consumption by not allowing the hardware into deeper sleep states, which is something that becomes more and more important on mobile battery powered devices. And while the recently added watermark support mitigates some of the issues by allowing the device to generate interrupts at a rate lower than the data output rate, this still requires a storage buffer inside the device and even if it exists it is only a few 100 samples deep at most. DMA support on the other hand allows to capture multiple millions or even more samples without any CPU interaction. This allows the CPU to either go to sleep for longer periods or focus on other tasks which increases overall system performance and power consumption. In addition to that some devices might not even offer a way to read the data other than using DMA, which makes DMA mandatory to use for them. The tasks involved in implementing a DMA buffer can be divided into two categories. The first category is memory buffer management (allocation, mapping, etc.) and hooking this up the IIO buffer callbacks like read(), enable(), disable(), etc. The second category of tasks is to setup the DMA hardware and manage the DMA transfers. Tasks from the first category will be very similar for all IIO drivers supporting DMA buffers, while the tasks from the second category will be hardware specific. This patch implements a generic infrastructure that take care of the former tasks. It provides a set of functions that implement the standard IIO buffer iio_buffer_access_funcs callbacks. These can either be used as is or be overloaded and augmented with driver specific code where necessary. For the DMA buffer support infrastructure that is introduced in this series sample data is grouped by so called blocks. A block is the basic unit at which data is exchanged between the application and the hardware. The application is responsible for allocating the memory associated with the block and then passes the block to the hardware. When the hardware has captured the amount of samples equal to size of a block it will notify the application, which can then read the data from the block and process it. The block size can freely chosen (within the constraints of the hardware). This allows to make a trade-off between latency and management overhead. The larger the block size the lower the per sample overhead but the latency between when the data was captured and when the application will be able to access it increases, in a similar way smaller block sizes have a larger per sample management overhead but a lower latency. The ideal block size thus depends on system and application requirements. For the time being the infrastructure only implements a simple double buffered scheme which allocates two blocks each with half the size of the configured buffer size. This provides basic support for capturing continuous uninterrupted data over the existing file-IO ABI. Future extensions to the DMA buffer infrastructure will give applications a more fine grained control over how many blocks are allocated and the size of each block. But this requires userspace ABI additions which are intentionally not part of this patch and will be added separately. Tasks of the second category need to be implemented by a device specific driver. They can be hooked up into the generic infrastructure using two simple callbacks, submit() and abort(). The submit() callback is used to schedule DMA transfers for blocks. Once a DMA transfer has been completed it is expected that the buffer driver calls iio_dma_buffer_block_done() to notify. The abort() callback is used for stopping all pending and active DMA transfers when the buffer is disabled. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/buffer/Kconfig b/drivers/iio/buffer/Kconfig index 0a7b2fd..b2fda1a 100644 --- a/drivers/iio/buffer/Kconfig +++ b/drivers/iio/buffer/Kconfig @@ -9,6 +9,15 @@ config IIO_BUFFER_CB Should be selected by any drivers that do in-kernel push usage. That is, those where the data is pushed to the consumer. +config IIO_BUFFER_DMA + tristate + help + Provides the generic IIO DMA buffer infrastructure that can be used by + drivers for devices with DMA support to implement the IIO buffer. + + Should be selected by drivers that want to use the generic DMA buffer + infrastructure. + config IIO_KFIFO_BUF tristate "Industrial I/O buffering based on kfifo" help diff --git a/drivers/iio/buffer/Makefile b/drivers/iio/buffer/Makefile index 4d193b9..bda3f11 100644 --- a/drivers/iio/buffer/Makefile +++ b/drivers/iio/buffer/Makefile @@ -4,5 +4,6 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o +obj-$(CONFIG_IIO_BUFFER_DMA) += industrialio-buffer-dma.o obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c new file mode 100644 index 0000000..212cbed --- /dev/null +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -0,0 +1,683 @@ +/* + * Copyright 2013-2015 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * For DMA buffers the storage is sub-divided into so called blocks. Each block + * has its own memory buffer. The size of the block is the granularity at which + * memory is exchanged between the hardware and the application. Increasing the + * basic unit of data exchange from one sample to one block decreases the + * management overhead that is associated with each sample. E.g. if we say the + * management overhead for one exchange is x and the unit of exchange is one + * sample the overhead will be x for each sample. Whereas when using a block + * which contains n samples the overhead per sample is reduced to x/n. This + * allows to achieve much higher samplerates than what can be sustained with + * the one sample approach. + * + * Blocks are exchanged between the DMA controller and the application via the + * means of two queues. The incoming queue and the outgoing queue. Blocks on the + * incoming queue are waiting for the DMA controller to pick them up and fill + * them with data. Block on the outgoing queue have been filled with data and + * are waiting for the application to dequeue them and read the data. + * + * A block can be in one of the following states: + * * Owned by the application. In this state the application can read data from + * the block. + * * On the incoming list: Blocks on the incoming list are queued up to be + * processed by the DMA controller. + * * Owned by the DMA controller: The DMA controller is processing the block + * and filling it with data. + * * On the outgoing list: Blocks on the outgoing list have been successfully + * processed by the DMA controller and contain data. They can be dequeued by + * the application. + * * Dead: A block that is dead has been marked as to be freed. It might still + * be owned by either the application or the DMA controller at the moment. + * But once they are done processing it instead of going to either the + * incoming or outgoing queue the block will be freed. + * + * In addition to this blocks are reference counted and the memory associated + * with both the block structure as well as the storage memory for the block + * will be freed when the last reference to the block is dropped. This means a + * block must not be accessed without holding a reference. + * + * The iio_dma_buffer implementation provides a generic infrastructure for + * managing the blocks. + * + * A driver for a specific piece of hardware that has DMA capabilities need to + * implement the submit() callback from the iio_dma_buffer_ops structure. This + * callback is supposed to initiate the DMA transfer copying data from the + * converter to the memory region of the block. Once the DMA transfer has been + * completed the driver must call iio_dma_buffer_block_done() for the completed + * block. + * + * Prior to this it must set the bytes_used field of the block contains + * the actual number of bytes in the buffer. Typically this will be equal to the + * size of the block, but if the DMA hardware has certain alignment requirements + * for the transfer length it might choose to use less than the full size. In + * either case it is expected that bytes_used is a multiple of the bytes per + * datum, i.e. the block must not contain partial samples. + * + * The driver must call iio_dma_buffer_block_done() for each block it has + * received through its submit_block() callback, even if it does not actually + * perform a DMA transfer for the block, e.g. because the buffer was disabled + * before the block transfer was started. In this case it should set bytes_used + * to 0. + * + * In addition it is recommended that a driver implements the abort() callback. + * It will be called when the buffer is disabled and can be used to cancel + * pending and stop active transfers. + * + * The specific driver implementation should use the default callback + * implementations provided by this module for the iio_buffer_access_funcs + * struct. It may overload some callbacks with custom variants if the hardware + * has special requirements that are not handled by the generic functions. If a + * driver chooses to overload a callback it has to ensure that the generic + * callback is called from within the custom callback. + */ + +static void iio_buffer_block_release(struct kref *kref) +{ + struct iio_dma_buffer_block *block = container_of(kref, + struct iio_dma_buffer_block, kref); + + WARN_ON(block->state != IIO_BLOCK_STATE_DEAD); + + dma_free_coherent(block->queue->dev, PAGE_ALIGN(block->size), + block->vaddr, block->phys_addr); + + iio_buffer_put(&block->queue->buffer); + kfree(block); +} + +static void iio_buffer_block_get(struct iio_dma_buffer_block *block) +{ + kref_get(&block->kref); +} + +static void iio_buffer_block_put(struct iio_dma_buffer_block *block) +{ + kref_put(&block->kref, iio_buffer_block_release); +} + +/* + * dma_free_coherent can sleep, hence we need to take some special care to be + * able to drop a reference from an atomic context. + */ +static LIST_HEAD(iio_dma_buffer_dead_blocks); +static DEFINE_SPINLOCK(iio_dma_buffer_dead_blocks_lock); + +static void iio_dma_buffer_cleanup_worker(struct work_struct *work) +{ + struct iio_dma_buffer_block *block, *_block; + LIST_HEAD(block_list); + + spin_lock_irq(&iio_dma_buffer_dead_blocks_lock); + list_splice_tail_init(&iio_dma_buffer_dead_blocks, &block_list); + spin_unlock_irq(&iio_dma_buffer_dead_blocks_lock); + + list_for_each_entry_safe(block, _block, &block_list, head) + iio_buffer_block_release(&block->kref); +} +static DECLARE_WORK(iio_dma_buffer_cleanup_work, iio_dma_buffer_cleanup_worker); + +static void iio_buffer_block_release_atomic(struct kref *kref) +{ + struct iio_dma_buffer_block *block; + unsigned long flags; + + block = container_of(kref, struct iio_dma_buffer_block, kref); + + spin_lock_irqsave(&iio_dma_buffer_dead_blocks_lock, flags); + list_add_tail(&block->head, &iio_dma_buffer_dead_blocks); + spin_unlock_irqrestore(&iio_dma_buffer_dead_blocks_lock, flags); + + schedule_work(&iio_dma_buffer_cleanup_work); +} + +/* + * Version of iio_buffer_block_put() that can be called from atomic context + */ +static void iio_buffer_block_put_atomic(struct iio_dma_buffer_block *block) +{ + kref_put(&block->kref, iio_buffer_block_release_atomic); +} + +static struct iio_dma_buffer_queue *iio_buffer_to_queue(struct iio_buffer *buf) +{ + return container_of(buf, struct iio_dma_buffer_queue, buffer); +} + +static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block( + struct iio_dma_buffer_queue *queue, size_t size) +{ + struct iio_dma_buffer_block *block; + + block = kzalloc(sizeof(*block), GFP_KERNEL); + if (!block) + return NULL; + + block->vaddr = dma_alloc_coherent(queue->dev, PAGE_ALIGN(size), + &block->phys_addr, GFP_KERNEL); + if (!block->vaddr) { + kfree(block); + return NULL; + } + + block->size = size; + block->state = IIO_BLOCK_STATE_DEQUEUED; + block->queue = queue; + INIT_LIST_HEAD(&block->head); + kref_init(&block->kref); + + iio_buffer_get(&queue->buffer); + + return block; +} + +static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block) +{ + struct iio_dma_buffer_queue *queue = block->queue; + + /* + * The buffer has already been freed by the application, just drop the + * reference. + */ + if (block->state != IIO_BLOCK_STATE_DEAD) { + block->state = IIO_BLOCK_STATE_DONE; + list_add_tail(&block->head, &queue->outgoing); + } +} + +/** + * iio_dma_buffer_block_done() - Indicate that a block has been completed + * @block: The completed block + * + * Should be called when the DMA controller has finished handling the block to + * pass back ownership of the block to the queue. + */ +void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block) +{ + struct iio_dma_buffer_queue *queue = block->queue; + unsigned long flags; + + spin_lock_irqsave(&queue->list_lock, flags); + _iio_dma_buffer_block_done(block); + spin_unlock_irqrestore(&queue->list_lock, flags); + + iio_buffer_block_put_atomic(block); + wake_up_interruptible_poll(&queue->buffer.pollq, POLLIN | POLLRDNORM); +} +EXPORT_SYMBOL_GPL(iio_dma_buffer_block_done); + +/** + * iio_dma_buffer_block_list_abort() - Indicate that a list block has been + * aborted + * @queue: Queue for which to complete blocks. + * @list: List of aborted blocks. All blocks in this list must be from @queue. + * + * Typically called from the abort() callback after the DMA controller has been + * stopped. This will set bytes_used to 0 for each block in the list and then + * hand the blocks back to the queue. + */ +void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue, + struct list_head *list) +{ + struct iio_dma_buffer_block *block, *_block; + unsigned long flags; + + spin_lock_irqsave(&queue->list_lock, flags); + list_for_each_entry_safe(block, _block, list, head) { + list_del(&block->head); + block->bytes_used = 0; + _iio_dma_buffer_block_done(block); + iio_buffer_block_put_atomic(block); + } + spin_unlock_irqrestore(&queue->list_lock, flags); + + wake_up_interruptible_poll(&queue->buffer.pollq, POLLIN | POLLRDNORM); +} +EXPORT_SYMBOL_GPL(iio_dma_buffer_block_list_abort); + +static bool iio_dma_block_reusable(struct iio_dma_buffer_block *block) +{ + /* + * If the core owns the block it can be re-used. This should be the + * default case when enabling the buffer, unless the DMA controller does + * not support abort and has not given back the block yet. + */ + switch (block->state) { + case IIO_BLOCK_STATE_DEQUEUED: + case IIO_BLOCK_STATE_QUEUED: + case IIO_BLOCK_STATE_DONE: + return true; + default: + return false; + } +} + +/** + * iio_dma_buffer_request_update() - DMA buffer request_update callback + * @buffer: The buffer which to request an update + * + * Should be used as the iio_dma_buffer_request_update() callback for + * iio_buffer_access_ops struct for DMA buffers. + */ +int iio_dma_buffer_request_update(struct iio_buffer *buffer) +{ + struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer); + struct iio_dma_buffer_block *block; + bool try_reuse = false; + size_t size; + int ret = 0; + int i; + + /* + * Split the buffer into two even parts. This is used as a double + * buffering scheme with usually one block at a time being used by the + * DMA and the other one by the application. + */ + size = DIV_ROUND_UP(queue->buffer.bytes_per_datum * + queue->buffer.length, 2); + + mutex_lock(&queue->lock); + + /* Allocations are page aligned */ + if (PAGE_ALIGN(queue->fileio.block_size) == PAGE_ALIGN(size)) + try_reuse = true; + + queue->fileio.block_size = size; + queue->fileio.active_block = NULL; + + spin_lock_irq(&queue->list_lock); + for (i = 0; i < 2; i++) { + block = queue->fileio.blocks[i]; + + /* If we can't re-use it free it */ + if (block && (!iio_dma_block_reusable(block) || !try_reuse)) + block->state = IIO_BLOCK_STATE_DEAD; + } + + /* + * At this point all blocks are either owned by the core or marked as + * dead. This means we can reset the lists without having to fear + * corrution. + */ + INIT_LIST_HEAD(&queue->outgoing); + spin_unlock_irq(&queue->list_lock); + + INIT_LIST_HEAD(&queue->incoming); + + for (i = 0; i < 2; i++) { + if (queue->fileio.blocks[i]) { + block = queue->fileio.blocks[i]; + if (block->state == IIO_BLOCK_STATE_DEAD) { + /* Could not reuse it */ + iio_buffer_block_put(block); + block = NULL; + } else { + block->size = size; + } + } else { + block = NULL; + } + + if (!block) { + block = iio_dma_buffer_alloc_block(queue, size); + if (!block) { + ret = -ENOMEM; + goto out_unlock; + } + queue->fileio.blocks[i] = block; + } + + block->state = IIO_BLOCK_STATE_QUEUED; + list_add_tail(&block->head, &queue->incoming); + } + +out_unlock: + mutex_unlock(&queue->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(iio_dma_buffer_request_update); + +static void iio_dma_buffer_submit_block(struct iio_dma_buffer_queue *queue, + struct iio_dma_buffer_block *block) +{ + int ret; + + /* + * If the hardware has already been removed we put the block into + * limbo. It will neither be on the incoming nor outgoing list, nor will + * it ever complete. It will just wait to be freed eventually. + */ + if (!queue->ops) + return; + + block->state = IIO_BLOCK_STATE_ACTIVE; + iio_buffer_block_get(block); + ret = queue->ops->submit(queue, block); + if (ret) { + /* + * This is a bit of a problem and there is not much we can do + * other then wait for the buffer to be disabled and re-enabled + * and try again. But it should not really happen unless we run + * out of memory or something similar. + * + * TODO: Implement support in the IIO core to allow buffers to + * notify consumers that something went wrong and the buffer + * should be disabled. + */ + iio_buffer_block_put(block); + } +} + +/** + * iio_dma_buffer_enable() - Enable DMA buffer + * @buffer: IIO buffer to enable + * @indio_dev: IIO device the buffer is attached to + * + * Needs to be called when the device that the buffer is attached to starts + * sampling. Typically should be the iio_buffer_access_ops enable callback. + * + * This will allocate the DMA buffers and start the DMA transfers. + */ +int iio_dma_buffer_enable(struct iio_buffer *buffer, + struct iio_dev *indio_dev) +{ + struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer); + struct iio_dma_buffer_block *block, *_block; + + mutex_lock(&queue->lock); + queue->active = true; + list_for_each_entry_safe(block, _block, &queue->incoming, head) { + list_del(&block->head); + iio_dma_buffer_submit_block(queue, block); + } + mutex_unlock(&queue->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(iio_dma_buffer_enable); + +/** + * iio_dma_buffer_disable() - Disable DMA buffer + * @buffer: IIO DMA buffer to disable + * @indio_dev: IIO device the buffer is attached to + * + * Needs to be called when the device that the buffer is attached to stops + * sampling. Typically should be the iio_buffer_access_ops disable callback. + */ +int iio_dma_buffer_disable(struct iio_buffer *buffer, + struct iio_dev *indio_dev) +{ + struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer); + + mutex_lock(&queue->lock); + queue->active = false; + + if (queue->ops && queue->ops->abort) + queue->ops->abort(queue); + mutex_unlock(&queue->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(iio_dma_buffer_disable); + +static void iio_dma_buffer_enqueue(struct iio_dma_buffer_queue *queue, + struct iio_dma_buffer_block *block) +{ + if (block->state == IIO_BLOCK_STATE_DEAD) { + iio_buffer_block_put(block); + } else if (queue->active) { + iio_dma_buffer_submit_block(queue, block); + } else { + block->state = IIO_BLOCK_STATE_QUEUED; + list_add_tail(&block->head, &queue->incoming); + } +} + +static struct iio_dma_buffer_block *iio_dma_buffer_dequeue( + struct iio_dma_buffer_queue *queue) +{ + struct iio_dma_buffer_block *block; + + spin_lock_irq(&queue->list_lock); + block = list_first_entry_or_null(&queue->outgoing, struct + iio_dma_buffer_block, head); + if (block != NULL) { + list_del(&block->head); + block->state = IIO_BLOCK_STATE_DEQUEUED; + } + spin_unlock_irq(&queue->list_lock); + + return block; +} + +/** + * iio_dma_buffer_read() - DMA buffer read callback + * @buffer: Buffer to read form + * @n: Number of bytes to read + * @user_buffer: Userspace buffer to copy the data to + * + * Should be used as the read_first_n callback for iio_buffer_access_ops + * struct for DMA buffers. + */ +int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n, + char __user *user_buffer) +{ + struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer); + struct iio_dma_buffer_block *block; + int ret; + + if (n < buffer->bytes_per_datum) + return -EINVAL; + + mutex_lock(&queue->lock); + + if (!queue->fileio.active_block) { + block = iio_dma_buffer_dequeue(queue); + if (block == NULL) { + ret = 0; + goto out_unlock; + } + queue->fileio.pos = 0; + queue->fileio.active_block = block; + } else { + block = queue->fileio.active_block; + } + + n = rounddown(n, buffer->bytes_per_datum); + if (n > block->bytes_used - queue->fileio.pos) + n = block->bytes_used - queue->fileio.pos; + + if (copy_to_user(user_buffer, block->vaddr + queue->fileio.pos, n)) { + ret = -EFAULT; + goto out_unlock; + } + + queue->fileio.pos += n; + + if (queue->fileio.pos == block->bytes_used) { + queue->fileio.active_block = NULL; + iio_dma_buffer_enqueue(queue, block); + } + + ret = n; + +out_unlock: + mutex_unlock(&queue->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(iio_dma_buffer_read); + +/** + * iio_dma_buffer_data_available() - DMA buffer data_available callback + * @buf: Buffer to check for data availability + * + * Should be used as the data_available callback for iio_buffer_access_ops + * struct for DMA buffers. + */ +size_t iio_dma_buffer_data_available(struct iio_buffer *buf) +{ + struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buf); + struct iio_dma_buffer_block *block; + size_t data_available = 0; + + /* + * For counting the available bytes we'll use the size of the block not + * the number of actual bytes available in the block. Otherwise it is + * possible that we end up with a value that is lower than the watermark + * but won't increase since all blocks are in use. + */ + + mutex_lock(&queue->lock); + if (queue->fileio.active_block) + data_available += queue->fileio.active_block->size; + + spin_lock_irq(&queue->list_lock); + list_for_each_entry(block, &queue->outgoing, head) + data_available += block->size; + spin_unlock_irq(&queue->list_lock); + mutex_unlock(&queue->lock); + + return data_available; +} +EXPORT_SYMBOL_GPL(iio_dma_buffer_data_available); + +/** + * iio_dma_buffer_set_bytes_per_datum() - DMA buffer set_bytes_per_datum callback + * @buffer: Buffer to set the bytes-per-datum for + * @bpd: The new bytes-per-datum value + * + * Should be used as the set_bytes_per_datum callback for iio_buffer_access_ops + * struct for DMA buffers. + */ +int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd) +{ + buffer->bytes_per_datum = bpd; + + return 0; +} +EXPORT_SYMBOL_GPL(iio_dma_buffer_set_bytes_per_datum); + +/** + * iio_dma_buffer_set_length - DMA buffer set_length callback + * @buffer: Buffer to set the length for + * @length: The new buffer length + * + * Should be used as the set_length callback for iio_buffer_access_ops + * struct for DMA buffers. + */ +int iio_dma_buffer_set_length(struct iio_buffer *buffer, int length) +{ + /* Avoid an invalid state */ + if (length < 2) + length = 2; + buffer->length = length; + buffer->watermark = length / 2; + + return 0; +} +EXPORT_SYMBOL_GPL(iio_dma_buffer_set_length); + +/** + * iio_dma_buffer_init() - Initialize DMA buffer queue + * @queue: Buffer to initialize + * @dev: DMA device + * @ops: DMA buffer queue callback operations + * + * The DMA device will be used by the queue to do DMA memory allocations. So it + * should refer to the device that will perform the DMA to ensure that + * allocations are done from a memory region that can be accessed by the device. + */ +int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue, + struct device *dev, const struct iio_dma_buffer_ops *ops) +{ + iio_buffer_init(&queue->buffer); + queue->buffer.length = PAGE_SIZE; + queue->buffer.watermark = queue->buffer.length / 2; + queue->dev = dev; + queue->ops = ops; + + INIT_LIST_HEAD(&queue->incoming); + INIT_LIST_HEAD(&queue->outgoing); + + mutex_init(&queue->lock); + spin_lock_init(&queue->list_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(iio_dma_buffer_init); + +/** + * iio_dma_buffer_exit() - Cleanup DMA buffer queue + * @queue: Buffer to cleanup + * + * After this function has completed it is safe to free any resources that are + * associated with the buffer and are accessed inside the callback operations. + */ +void iio_dma_buffer_exit(struct iio_dma_buffer_queue *queue) +{ + unsigned int i; + + mutex_lock(&queue->lock); + + spin_lock_irq(&queue->list_lock); + for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { + if (!queue->fileio.blocks[i]) + continue; + queue->fileio.blocks[i]->state = IIO_BLOCK_STATE_DEAD; + } + INIT_LIST_HEAD(&queue->outgoing); + spin_unlock_irq(&queue->list_lock); + + INIT_LIST_HEAD(&queue->incoming); + + for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { + if (!queue->fileio.blocks[i]) + continue; + iio_buffer_block_put(queue->fileio.blocks[i]); + queue->fileio.blocks[i] = NULL; + } + queue->fileio.active_block = NULL; + queue->ops = NULL; + + mutex_unlock(&queue->lock); +} +EXPORT_SYMBOL_GPL(iio_dma_buffer_exit); + +/** + * iio_dma_buffer_release() - Release final buffer resources + * @queue: Buffer to release + * + * Frees resources that can't yet be freed in iio_dma_buffer_exit(). Should be + * called in the buffers release callback implementation right before freeing + * the memory associated with the buffer. + */ +void iio_dma_buffer_release(struct iio_dma_buffer_queue *queue) +{ + mutex_destroy(&queue->lock); +} +EXPORT_SYMBOL_GPL(iio_dma_buffer_release); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("DMA buffer for the IIO framework"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h new file mode 100644 index 0000000..767467d --- /dev/null +++ b/include/linux/iio/buffer-dma.h @@ -0,0 +1,152 @@ +/* + * Copyright 2013-2015 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + */ + +#ifndef __INDUSTRIALIO_DMA_BUFFER_H__ +#define __INDUSTRIALIO_DMA_BUFFER_H__ + +#include +#include +#include +#include +#include + +struct iio_dma_buffer_queue; +struct iio_dma_buffer_ops; +struct device; + +struct iio_buffer_block { + u32 size; + u32 bytes_used; +}; + +/** + * enum iio_block_state - State of a struct iio_dma_buffer_block + * @IIO_BLOCK_STATE_DEQUEUED: Block is not queued + * @IIO_BLOCK_STATE_QUEUED: Block is on the incoming queue + * @IIO_BLOCK_STATE_ACTIVE: Block is currently being processed by the DMA + * @IIO_BLOCK_STATE_DONE: Block is on the outgoing queue + * @IIO_BLOCK_STATE_DEAD: Block has been marked as to be freed + */ +enum iio_block_state { + IIO_BLOCK_STATE_DEQUEUED, + IIO_BLOCK_STATE_QUEUED, + IIO_BLOCK_STATE_ACTIVE, + IIO_BLOCK_STATE_DONE, + IIO_BLOCK_STATE_DEAD, +}; + +/** + * struct iio_dma_buffer_block - IIO buffer block + * @head: List head + * @size: Total size of the block in bytes + * @bytes_used: Number of bytes that contain valid data + * @vaddr: Virutal address of the blocks memory + * @phys_addr: Physical address of the blocks memory + * @queue: Parent DMA buffer queue + * @kref: kref used to manage the lifetime of block + * @state: Current state of the block + */ +struct iio_dma_buffer_block { + /* May only be accessed by the owner of the block */ + struct list_head head; + size_t bytes_used; + + /* + * Set during allocation, constant thereafter. May be accessed read-only + * by anybody holding a reference to the block. + */ + void *vaddr; + dma_addr_t phys_addr; + size_t size; + struct iio_dma_buffer_queue *queue; + + /* Must not be accessed outside the core. */ + struct kref kref; + /* + * Must not be accessed outside the core. Access needs to hold + * queue->list_lock if the block is not owned by the core. + */ + enum iio_block_state state; +}; + +/** + * struct iio_dma_buffer_queue_fileio - FileIO state for the DMA buffer + * @blocks: Buffer blocks used for fileio + * @active_block: Block being used in read() + * @pos: Read offset in the active block + * @block_size: Size of each block + */ +struct iio_dma_buffer_queue_fileio { + struct iio_dma_buffer_block *blocks[2]; + struct iio_dma_buffer_block *active_block; + size_t pos; + size_t block_size; +}; + +/** + * struct iio_dma_buffer_queue - DMA buffer base structure + * @buffer: IIO buffer base structure + * @dev: Parent device + * @ops: DMA buffer callbacks + * @lock: Protects the incoming list, active and the fields in the fileio + * substruct + * @list_lock: Protects lists that contain blocks which can be modified in + * atomic context as well as blocks on those lists. This is the outgoing queue + * list and typically also a list of active blocks in the part that handles + * the DMA controller + * @incoming: List of buffers on the incoming queue + * @outgoing: List of buffers on the outgoing queue + * @active: Whether the buffer is currently active + * @fileio: FileIO state + */ +struct iio_dma_buffer_queue { + struct iio_buffer buffer; + struct device *dev; + const struct iio_dma_buffer_ops *ops; + + struct mutex lock; + spinlock_t list_lock; + struct list_head incoming; + struct list_head outgoing; + + bool active; + + struct iio_dma_buffer_queue_fileio fileio; +}; + +/** + * struct iio_dma_buffer_ops - DMA buffer callback operations + * @submit: Called when a block is submitted to the DMA controller + * @abort: Should abort all pending transfers + */ +struct iio_dma_buffer_ops { + int (*submit)(struct iio_dma_buffer_queue *queue, + struct iio_dma_buffer_block *block); + void (*abort)(struct iio_dma_buffer_queue *queue); +}; + +void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block); +void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue, + struct list_head *list); + +int iio_dma_buffer_enable(struct iio_buffer *buffer, + struct iio_dev *indio_dev); +int iio_dma_buffer_disable(struct iio_buffer *buffer, + struct iio_dev *indio_dev); +int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n, + char __user *user_buffer); +size_t iio_dma_buffer_data_available(struct iio_buffer *buffer); +int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd); +int iio_dma_buffer_set_length(struct iio_buffer *buffer, int length); +int iio_dma_buffer_request_update(struct iio_buffer *buffer); + +int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue, + struct device *dma_dev, const struct iio_dma_buffer_ops *ops); +void iio_dma_buffer_exit(struct iio_dma_buffer_queue *queue); +void iio_dma_buffer_release(struct iio_dma_buffer_queue *queue); + +#endif -- cgit v0.10.2 From 2d6ca60f328450ff5c7802d0857d12e3711348ce Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 13 Oct 2015 18:10:29 +0200 Subject: iio: Add a DMAengine framework based buffer Add a generic fully device independent DMA buffer implementation that uses the DMAegnine framework to perform the DMA transfers. This can be used by converter drivers that whish to provide a DMA buffer for converters that are connected to a DMA core that implements the DMAengine API. Apart from allocating the buffer using iio_dmaengine_buffer_alloc() and freeing it using iio_dmaengine_buffer_free() no additional converter driver specific code is required when using this DMA buffer implementation. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/buffer/Kconfig b/drivers/iio/buffer/Kconfig index b2fda1a..4ffd3db 100644 --- a/drivers/iio/buffer/Kconfig +++ b/drivers/iio/buffer/Kconfig @@ -18,6 +18,17 @@ config IIO_BUFFER_DMA Should be selected by drivers that want to use the generic DMA buffer infrastructure. +config IIO_BUFFER_DMAENGINE + tristate + select IIO_BUFFER_DMA + help + Provides a bonding of the generic IIO DMA buffer infrastructure with the + DMAengine framework. This can be used by converter drivers with a DMA port + connected to an external DMA controller which is supported by the + DMAengine framework. + + Should be selected by drivers that want to use this functionality. + config IIO_KFIFO_BUF tristate "Industrial I/O buffering based on kfifo" help diff --git a/drivers/iio/buffer/Makefile b/drivers/iio/buffer/Makefile index bda3f11..85beaae 100644 --- a/drivers/iio/buffer/Makefile +++ b/drivers/iio/buffer/Makefile @@ -5,5 +5,6 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_IIO_BUFFER_CB) += industrialio-buffer-cb.o obj-$(CONFIG_IIO_BUFFER_DMA) += industrialio-buffer-dma.o +obj-$(CONFIG_IIO_BUFFER_DMAENGINE) += industrialio-buffer-dmaengine.o obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c new file mode 100644 index 0000000..ebdb838 --- /dev/null +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -0,0 +1,213 @@ +/* + * Copyright 2014-2015 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2 or later. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * The IIO DMAengine buffer combines the generic IIO DMA buffer infrastructure + * with the DMAengine framework. The generic IIO DMA buffer infrastructure is + * used to manage the buffer memory and implement the IIO buffer operations + * while the DMAengine framework is used to perform the DMA transfers. Combined + * this results in a device independent fully functional DMA buffer + * implementation that can be used by device drivers for peripherals which are + * connected to a DMA controller which has a DMAengine driver implementation. + */ + +struct dmaengine_buffer { + struct iio_dma_buffer_queue queue; + + struct dma_chan *chan; + struct list_head active; + + size_t align; + size_t max_size; +}; + +static struct dmaengine_buffer *iio_buffer_to_dmaengine_buffer( + struct iio_buffer *buffer) +{ + return container_of(buffer, struct dmaengine_buffer, queue.buffer); +} + +static void iio_dmaengine_buffer_block_done(void *data) +{ + struct iio_dma_buffer_block *block = data; + unsigned long flags; + + spin_lock_irqsave(&block->queue->list_lock, flags); + list_del(&block->head); + spin_unlock_irqrestore(&block->queue->list_lock, flags); + iio_dma_buffer_block_done(block); +} + +static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, + struct iio_dma_buffer_block *block) +{ + struct dmaengine_buffer *dmaengine_buffer = + iio_buffer_to_dmaengine_buffer(&queue->buffer); + struct dma_async_tx_descriptor *desc; + dma_cookie_t cookie; + + block->bytes_used = min(block->size, dmaengine_buffer->max_size); + block->bytes_used = rounddown(block->bytes_used, + dmaengine_buffer->align); + + desc = dmaengine_prep_slave_single(dmaengine_buffer->chan, + block->phys_addr, block->bytes_used, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!desc) + return -ENOMEM; + + desc->callback = iio_dmaengine_buffer_block_done; + desc->callback_param = block; + + cookie = dmaengine_submit(desc); + if (dma_submit_error(cookie)) + return dma_submit_error(cookie); + + spin_lock_irq(&dmaengine_buffer->queue.list_lock); + list_add_tail(&block->head, &dmaengine_buffer->active); + spin_unlock_irq(&dmaengine_buffer->queue.list_lock); + + dma_async_issue_pending(dmaengine_buffer->chan); + + return 0; +} + +static void iio_dmaengine_buffer_abort(struct iio_dma_buffer_queue *queue) +{ + struct dmaengine_buffer *dmaengine_buffer = + iio_buffer_to_dmaengine_buffer(&queue->buffer); + + dmaengine_terminate_all(dmaengine_buffer->chan); + /* FIXME: There is a slight chance of a race condition here. + * dmaengine_terminate_all() does not guarantee that all transfer + * callbacks have finished running. Need to introduce a + * dmaengine_terminate_all_sync(). + */ + iio_dma_buffer_block_list_abort(queue, &dmaengine_buffer->active); +} + +static void iio_dmaengine_buffer_release(struct iio_buffer *buf) +{ + struct dmaengine_buffer *dmaengine_buffer = + iio_buffer_to_dmaengine_buffer(buf); + + iio_dma_buffer_release(&dmaengine_buffer->queue); + kfree(dmaengine_buffer); +} + +static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = { + .read_first_n = iio_dma_buffer_read, + .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum, + .set_length = iio_dma_buffer_set_length, + .request_update = iio_dma_buffer_request_update, + .enable = iio_dma_buffer_enable, + .disable = iio_dma_buffer_disable, + .data_available = iio_dma_buffer_data_available, + .release = iio_dmaengine_buffer_release, + + .modes = INDIO_BUFFER_HARDWARE, + .flags = INDIO_BUFFER_FLAG_FIXED_WATERMARK, +}; + +static const struct iio_dma_buffer_ops iio_dmaengine_default_ops = { + .submit = iio_dmaengine_buffer_submit_block, + .abort = iio_dmaengine_buffer_abort, +}; + +/** + * iio_dmaengine_buffer_alloc() - Allocate new buffer which uses DMAengine + * @dev: Parent device for the buffer + * @channel: DMA channel name, typically "rx". + * + * This allocates a new IIO buffer which internally uses the DMAengine framework + * to perform its transfers. The parent device will be used to request the DMA + * channel. + * + * Once done using the buffer iio_dmaengine_buffer_free() should be used to + * release it. + */ +struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, + const char *channel) +{ + struct dmaengine_buffer *dmaengine_buffer; + unsigned int width, src_width, dest_width; + struct dma_slave_caps caps; + struct dma_chan *chan; + int ret; + + dmaengine_buffer = kzalloc(sizeof(*dmaengine_buffer), GFP_KERNEL); + if (!dmaengine_buffer) + return ERR_PTR(-ENOMEM); + + chan = dma_request_slave_channel_reason(dev, channel); + if (IS_ERR(chan)) { + ret = PTR_ERR(chan); + goto err_free; + } + + ret = dma_get_slave_caps(chan, &caps); + if (ret < 0) + goto err_free; + + /* Needs to be aligned to the maximum of the minimums */ + if (caps.src_addr_widths) + src_width = __ffs(caps.src_addr_widths); + else + src_width = 1; + if (caps.dst_addr_widths) + dest_width = __ffs(caps.dst_addr_widths); + else + dest_width = 1; + width = max(src_width, dest_width); + + INIT_LIST_HEAD(&dmaengine_buffer->active); + dmaengine_buffer->chan = chan; + dmaengine_buffer->align = width; + dmaengine_buffer->max_size = dma_get_max_seg_size(chan->device->dev); + + iio_dma_buffer_init(&dmaengine_buffer->queue, chan->device->dev, + &iio_dmaengine_default_ops); + + dmaengine_buffer->queue.buffer.access = &iio_dmaengine_buffer_ops; + + return &dmaengine_buffer->queue.buffer; + +err_free: + kfree(dmaengine_buffer); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(iio_dmaengine_buffer_alloc); + +/** + * iio_dmaengine_buffer_free() - Free dmaengine buffer + * @buffer: Buffer to free + * + * Frees a buffer previously allocated with iio_dmaengine_buffer_alloc(). + */ +void iio_dmaengine_buffer_free(struct iio_buffer *buffer) +{ + struct dmaengine_buffer *dmaengine_buffer = + iio_buffer_to_dmaengine_buffer(buffer); + + iio_dma_buffer_exit(&dmaengine_buffer->queue); + dma_release_channel(dmaengine_buffer->chan); + + iio_buffer_put(buffer); +} +EXPORT_SYMBOL_GPL(iio_dmaengine_buffer_free); diff --git a/include/linux/iio/buffer-dmaengine.h b/include/linux/iio/buffer-dmaengine.h new file mode 100644 index 0000000..5dcddf4 --- /dev/null +++ b/include/linux/iio/buffer-dmaengine.h @@ -0,0 +1,18 @@ +/* + * Copyright 2014-2015 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __IIO_DMAENGINE_H__ +#define __IIO_DMAENGINE_H__ + +struct iio_buffer; +struct device; + +struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, + const char *channel); +void iio_dmaengine_buffer_free(struct iio_buffer *buffer); + +#endif -- cgit v0.10.2 From 18fb1ab0eb81a08ecb00065f3e6c037a37d8e917 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Fri, 30 Oct 2015 16:50:39 -0700 Subject: iio: light: lm3533-als: Print error message on invalid resistance Print an error message to indicate that invalid configuration data was provided in the platform_data, rather than just aborting initialization. Signed-off-by: Bjorn Andersson Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c index 076bc46..e56937c 100644 --- a/drivers/iio/light/lm3533-als.c +++ b/drivers/iio/light/lm3533-als.c @@ -743,8 +743,10 @@ static int lm3533_als_set_resistor(struct lm3533_als *als, u8 val) { int ret; - if (val < LM3533_ALS_RESISTOR_MIN || val > LM3533_ALS_RESISTOR_MAX) + if (val < LM3533_ALS_RESISTOR_MIN || val > LM3533_ALS_RESISTOR_MAX) { + dev_err(&als->pdev->dev, "invalid resistor value\n"); return -EINVAL; + }; ret = lm3533_write(als->lm3533, LM3533_REG_ALS_RESISTOR_SELECT, val); if (ret) { -- cgit v0.10.2 From a84ef0d181d917125f1f16cffe53f84c19968969 Mon Sep 17 00:00:00 2001 From: Joachim Eastwood Date: Sat, 31 Oct 2015 13:49:16 +0100 Subject: iio: accel: add Freescale MMA7455L/MMA7456L 3-axis accelerometer driver Add support for Freescale MMA7455L/MMA7456L 3-axis in 10-bit mode for I2C and SPI bus. This rather simple driver that currently doesn't support all the hardware features of MMA7455L/MMA7456L. Tested on Embedded Artist's LPC4357 Dev Kit with MMA7455L on I2C bus. Data sheets for the two devices can be found here: http://cache.freescale.com/files/sensors/doc/data_sheet/MMA7455L.pdf http://cache.freescale.com/files/sensors/doc/data_sheet/MMA7456L.pdf Signed-off-by: Joachim Eastwood Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 75ac087..87487d3 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -107,6 +107,35 @@ config KXCJK1013 To compile this driver as a module, choose M here: the module will be called kxcjk-1013. +config MMA7455 + tristate + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + +config MMA7455_I2C + tristate "Freescale MMA7455L/MMA7456L Accelerometer I2C Driver" + depends on I2C + select MMA7455 + select REGMAP_I2C + help + Say yes here to build support for the Freescale MMA7455L and + MMA7456L 3-axis accelerometer. + + To compile this driver as a module, choose M here: the module + will be called mma7455_i2c. + +config MMA7455_SPI + tristate "Freescale MMA7455L/MMA7456L Accelerometer SPI Driver" + depends on SPI_MASTER + select MMA7455 + select REGMAP_SPI + help + Say yes here to build support for the Freescale MMA7455L and + MMA7456L 3-axis accelerometer. + + To compile this driver as a module, choose M here: the module + will be called mma7455_spi. + config MMA8452 tristate "Freescale MMA8452Q and similar Accelerometers Driver" depends on I2C diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 525ed52..71b6794 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -10,6 +10,11 @@ obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o obj-$(CONFIG_KXSD9) += kxsd9.o + +obj-$(CONFIG_MMA7455) += mma7455_core.o +obj-$(CONFIG_MMA7455_I2C) += mma7455_i2c.o +obj-$(CONFIG_MMA7455_SPI) += mma7455_spi.o + obj-$(CONFIG_MMA8452) += mma8452.o obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o diff --git a/drivers/iio/accel/mma7455.h b/drivers/iio/accel/mma7455.h new file mode 100644 index 0000000..2b1152c --- /dev/null +++ b/drivers/iio/accel/mma7455.h @@ -0,0 +1,19 @@ +/* + * IIO accel driver for Freescale MMA7455L 3-axis 10-bit accelerometer + * Copyright 2015 Joachim Eastwood + * + * 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. + */ + +#ifndef __MMA7455_H +#define __MMA7455_H + +extern const struct regmap_config mma7455_core_regmap; + +int mma7455_core_probe(struct device *dev, struct regmap *regmap, + const char *name); +int mma7455_core_remove(struct device *dev); + +#endif diff --git a/drivers/iio/accel/mma7455_core.c b/drivers/iio/accel/mma7455_core.c new file mode 100644 index 0000000..c633cc2 --- /dev/null +++ b/drivers/iio/accel/mma7455_core.c @@ -0,0 +1,311 @@ +/* + * IIO accel core driver for Freescale MMA7455L 3-axis 10-bit accelerometer + * Copyright 2015 Joachim Eastwood + * + * 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. + * + * UNSUPPORTED hardware features: + * - 8-bit mode with different scales + * - INT1/INT2 interrupts + * - Offset calibration + * - Events + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mma7455.h" + +#define MMA7455_REG_XOUTL 0x00 +#define MMA7455_REG_XOUTH 0x01 +#define MMA7455_REG_YOUTL 0x02 +#define MMA7455_REG_YOUTH 0x03 +#define MMA7455_REG_ZOUTL 0x04 +#define MMA7455_REG_ZOUTH 0x05 +#define MMA7455_REG_STATUS 0x09 +#define MMA7455_STATUS_DRDY BIT(0) +#define MMA7455_REG_WHOAMI 0x0f +#define MMA7455_WHOAMI_ID 0x55 +#define MMA7455_REG_MCTL 0x16 +#define MMA7455_MCTL_MODE_STANDBY 0x00 +#define MMA7455_MCTL_MODE_MEASURE 0x01 +#define MMA7455_REG_CTL1 0x18 +#define MMA7455_CTL1_DFBW_MASK BIT(7) +#define MMA7455_CTL1_DFBW_125HZ BIT(7) +#define MMA7455_CTL1_DFBW_62_5HZ 0 +#define MMA7455_REG_TW 0x1e + +/* + * When MMA7455 is used in 10-bit it has a fullscale of -8g + * corresponding to raw value -512. The userspace interface + * uses m/s^2 and we declare micro units. + * So scale factor is given by: + * g * 8 * 1e6 / 512 = 153228.90625, with g = 9.80665 + */ +#define MMA7455_10BIT_SCALE 153229 + +struct mma7455_data { + struct regmap *regmap; + struct device *dev; +}; + +static int mma7455_drdy(struct mma7455_data *mma7455) +{ + unsigned int reg; + int tries = 3; + int ret; + + while (tries-- > 0) { + ret = regmap_read(mma7455->regmap, MMA7455_REG_STATUS, ®); + if (ret) + return ret; + + if (reg & MMA7455_STATUS_DRDY) + return 0; + + msleep(20); + } + + dev_warn(mma7455->dev, "data not ready\n"); + + return -EIO; +} + +static irqreturn_t mma7455_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct mma7455_data *mma7455 = iio_priv(indio_dev); + u8 buf[16]; /* 3 x 16-bit channels + padding + ts */ + int ret; + + ret = mma7455_drdy(mma7455); + if (ret) + goto done; + + ret = regmap_bulk_read(mma7455->regmap, MMA7455_REG_XOUTL, buf, + sizeof(__le16) * 3); + if (ret) + goto done; + + iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); + +done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int mma7455_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mma7455_data *mma7455 = iio_priv(indio_dev); + unsigned int reg; + __le16 data; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + ret = mma7455_drdy(mma7455); + if (ret) + return ret; + + ret = regmap_bulk_read(mma7455->regmap, chan->address, &data, + sizeof(data)); + if (ret) + return ret; + + *val = sign_extend32(le16_to_cpu(data), 9); + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = MMA7455_10BIT_SCALE; + + return IIO_VAL_INT_PLUS_MICRO; + + case IIO_CHAN_INFO_SAMP_FREQ: + ret = regmap_read(mma7455->regmap, MMA7455_REG_CTL1, ®); + if (ret) + return ret; + + if (reg & MMA7455_CTL1_DFBW_MASK) + *val = 250; + else + *val = 125; + + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int mma7455_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct mma7455_data *mma7455 = iio_priv(indio_dev); + int i; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (val == 250 && val2 == 0) + i = MMA7455_CTL1_DFBW_125HZ; + else if (val == 125 && val2 == 0) + i = MMA7455_CTL1_DFBW_62_5HZ; + else + return -EINVAL; + + return regmap_update_bits(mma7455->regmap, MMA7455_REG_CTL1, + MMA7455_CTL1_DFBW_MASK, i); + + case IIO_CHAN_INFO_SCALE: + /* In 10-bit mode there is only one scale available */ + if (val == 0 && val2 == MMA7455_10BIT_SCALE) + return 0; + break; + } + + return -EINVAL; +} + +static IIO_CONST_ATTR(sampling_frequency_available, "125 250"); + +static struct attribute *mma7455_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group mma7455_group = { + .attrs = mma7455_attributes, +}; + +static const struct iio_info mma7455_info = { + .attrs = &mma7455_group, + .read_raw = mma7455_read_raw, + .write_raw = mma7455_write_raw, + .driver_module = THIS_MODULE, +}; + +#define MMA7455_CHANNEL(axis, idx) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .address = MMA7455_REG_##axis##OUTL,\ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 10, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ +} + +static const struct iio_chan_spec mma7455_channels[] = { + MMA7455_CHANNEL(X, 0), + MMA7455_CHANNEL(Y, 1), + MMA7455_CHANNEL(Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const unsigned long mma7455_scan_masks[] = {0x7, 0}; + +const struct regmap_config mma7455_core_regmap = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MMA7455_REG_TW, +}; +EXPORT_SYMBOL_GPL(mma7455_core_regmap); + +int mma7455_core_probe(struct device *dev, struct regmap *regmap, + const char *name) +{ + struct mma7455_data *mma7455; + struct iio_dev *indio_dev; + unsigned int reg; + int ret; + + ret = regmap_read(regmap, MMA7455_REG_WHOAMI, ®); + if (ret) { + dev_err(dev, "unable to read reg\n"); + return ret; + } + + if (reg != MMA7455_WHOAMI_ID) { + dev_err(dev, "device id mismatch\n"); + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(dev, sizeof(*mma7455)); + if (!indio_dev) + return -ENOMEM; + + dev_set_drvdata(dev, indio_dev); + mma7455 = iio_priv(indio_dev); + mma7455->regmap = regmap; + mma7455->dev = dev; + + indio_dev->info = &mma7455_info; + indio_dev->name = name; + indio_dev->dev.parent = dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mma7455_channels; + indio_dev->num_channels = ARRAY_SIZE(mma7455_channels); + indio_dev->available_scan_masks = mma7455_scan_masks; + + regmap_write(mma7455->regmap, MMA7455_REG_MCTL, + MMA7455_MCTL_MODE_MEASURE); + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + mma7455_trigger_handler, NULL); + if (ret) { + dev_err(dev, "unable to setup triggered buffer\n"); + return ret; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(dev, "unable to register device\n"); + iio_triggered_buffer_cleanup(indio_dev); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mma7455_core_probe); + +int mma7455_core_remove(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct mma7455_data *mma7455 = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + + regmap_write(mma7455->regmap, MMA7455_REG_MCTL, + MMA7455_MCTL_MODE_STANDBY); + + return 0; +} +EXPORT_SYMBOL_GPL(mma7455_core_remove); + +MODULE_AUTHOR("Joachim Eastwood "); +MODULE_DESCRIPTION("Freescale MMA7455L core accelerometer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/mma7455_i2c.c b/drivers/iio/accel/mma7455_i2c.c new file mode 100644 index 0000000..3cab5fb --- /dev/null +++ b/drivers/iio/accel/mma7455_i2c.c @@ -0,0 +1,56 @@ +/* + * IIO accel I2C driver for Freescale MMA7455L 3-axis 10-bit accelerometer + * Copyright 2015 Joachim Eastwood + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "mma7455.h" + +static int mma7455_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct regmap *regmap; + const char *name = NULL; + + regmap = devm_regmap_init_i2c(i2c, &mma7455_core_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + if (id) + name = id->name; + + return mma7455_core_probe(&i2c->dev, regmap, name); +} + +static int mma7455_i2c_remove(struct i2c_client *i2c) +{ + return mma7455_core_remove(&i2c->dev); +} + +static const struct i2c_device_id mma7455_i2c_ids[] = { + { "mma7455", 0 }, + { "mma7456", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mma7455_i2c_ids); + +static struct i2c_driver mma7455_i2c_driver = { + .probe = mma7455_i2c_probe, + .remove = mma7455_i2c_remove, + .id_table = mma7455_i2c_ids, + .driver = { + .name = "mma7455-i2c", + }, +}; +module_i2c_driver(mma7455_i2c_driver); + +MODULE_AUTHOR("Joachim Eastwood "); +MODULE_DESCRIPTION("Freescale MMA7455L I2C accelerometer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/mma7455_spi.c b/drivers/iio/accel/mma7455_spi.c new file mode 100644 index 0000000..79df8f2 --- /dev/null +++ b/drivers/iio/accel/mma7455_spi.c @@ -0,0 +1,52 @@ +/* + * IIO accel SPI driver for Freescale MMA7455L 3-axis 10-bit accelerometer + * Copyright 2015 Joachim Eastwood + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "mma7455.h" + +static int mma7455_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &mma7455_core_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return mma7455_core_probe(&spi->dev, regmap, id->name); +} + +static int mma7455_spi_remove(struct spi_device *spi) +{ + return mma7455_core_remove(&spi->dev); +} + +static const struct spi_device_id mma7455_spi_ids[] = { + { "mma7455", 0 }, + { "mma7456", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, mma7455_spi_ids); + +static struct spi_driver mma7455_spi_driver = { + .probe = mma7455_spi_probe, + .remove = mma7455_spi_remove, + .id_table = mma7455_spi_ids, + .driver = { + .name = "mma7455-spi", + }, +}; +module_spi_driver(mma7455_spi_driver); + +MODULE_AUTHOR("Joachim Eastwood "); +MODULE_DESCRIPTION("Freescale MMA7455L SPI accelerometer driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From 536bbca7cfc9e7a85704de552fcf4b5d1062a38e Mon Sep 17 00:00:00 2001 From: Adriana Reus Date: Fri, 6 Nov 2015 11:10:37 +0200 Subject: iio: light: pa12203001: Poweroff chip if register fails Make sure we poweroff the chip if for any reason iio_register returns an error. Signed-off-by: Adriana Reus Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/light/pa12203001.c b/drivers/iio/light/pa12203001.c index 45f7bde..76a9e12 100644 --- a/drivers/iio/light/pa12203001.c +++ b/drivers/iio/light/pa12203001.c @@ -381,17 +381,23 @@ static int pa12203001_probe(struct i2c_client *client, return ret; ret = pm_runtime_set_active(&client->dev); - if (ret < 0) { - pa12203001_power_chip(indio_dev, PA12203001_CHIP_DISABLE); - return ret; - } + if (ret < 0) + goto out_err; pm_runtime_enable(&client->dev); pm_runtime_set_autosuspend_delay(&client->dev, PA12203001_SLEEP_DELAY_MS); pm_runtime_use_autosuspend(&client->dev); - return iio_device_register(indio_dev); + ret = iio_device_register(indio_dev); + if (ret < 0) + goto out_err; + + return 0; + +out_err: + pa12203001_power_chip(indio_dev, PA12203001_CHIP_DISABLE); + return ret; } static int pa12203001_remove(struct i2c_client *client) -- cgit v0.10.2 From 7d0ead5c3f00a0652fa4436f0d2dd05e9f2de140 Mon Sep 17 00:00:00 2001 From: Adriana Reus Date: Thu, 5 Nov 2015 16:25:29 +0200 Subject: iio: Reconcile operation order between iio_register/unregister and pm functions At probe, runtime pm should be setup before registering the sysfs interface so that all the power attributes are accurate and functional when registering. Also, when removing the device we should unregister first to make sure that the interfaces that may result in wakeups are no longer available. Fix this behaviour for the following drivers: bmc150, bmg160, kmx61, kxcj-1013, mma9551, mma9553, rpr0521. Signed-off-by: Adriana Reus Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 2d33f1e..c73331f7 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -1623,24 +1623,22 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, } } - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(dev, "Unable to register iio device\n"); - goto err_trigger_unregister; - } - ret = pm_runtime_set_active(dev); if (ret) - goto err_iio_unregister; + goto err_trigger_unregister; pm_runtime_enable(dev); pm_runtime_set_autosuspend_delay(dev, BMC150_AUTO_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(dev); + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(dev, "Unable to register iio device\n"); + goto err_trigger_unregister; + } + return 0; -err_iio_unregister: - iio_device_unregister(indio_dev); err_trigger_unregister: bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1); err_buffer_cleanup: @@ -1655,12 +1653,12 @@ int bmc150_accel_core_remove(struct device *dev) struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmc150_accel_data *data = iio_priv(indio_dev); + iio_device_unregister(indio_dev); + pm_runtime_disable(data->dev); pm_runtime_set_suspended(data->dev); pm_runtime_put_noidle(data->dev); - iio_device_unregister(indio_dev); - bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1); iio_triggered_buffer_cleanup(indio_dev); diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 18c1b06..edec1d0 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1264,25 +1264,23 @@ static int kxcjk1013_probe(struct i2c_client *client, goto err_trigger_unregister; } - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "unable to register iio device\n"); - goto err_buffer_cleanup; - } - ret = pm_runtime_set_active(&client->dev); if (ret) - goto err_iio_unregister; + goto err_buffer_cleanup; pm_runtime_enable(&client->dev); pm_runtime_set_autosuspend_delay(&client->dev, KXCJK1013_SLEEP_DELAY_MS); pm_runtime_use_autosuspend(&client->dev); + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "unable to register iio device\n"); + goto err_buffer_cleanup; + } + return 0; -err_iio_unregister: - iio_device_unregister(indio_dev); err_buffer_cleanup: if (data->dready_trig) iio_triggered_buffer_cleanup(indio_dev); @@ -1302,12 +1300,12 @@ static int kxcjk1013_remove(struct i2c_client *client) struct iio_dev *indio_dev = i2c_get_clientdata(client); struct kxcjk1013_data *data = iio_priv(indio_dev); + iio_device_unregister(indio_dev); + pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); pm_runtime_put_noidle(&client->dev); - iio_device_unregister(indio_dev); - if (data->dready_trig) { iio_triggered_buffer_cleanup(indio_dev); iio_trigger_unregister(data->dready_trig); diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index 7db7cc0..d899a4d 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -495,25 +495,23 @@ static int mma9551_probe(struct i2c_client *client, if (ret < 0) goto out_poweroff; - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "unable to register iio device\n"); - goto out_poweroff; - } - ret = pm_runtime_set_active(&client->dev); if (ret < 0) - goto out_iio_unregister; + goto out_poweroff; pm_runtime_enable(&client->dev); pm_runtime_set_autosuspend_delay(&client->dev, MMA9551_AUTO_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(&client->dev); + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "unable to register iio device\n"); + goto out_poweroff; + } + return 0; -out_iio_unregister: - iio_device_unregister(indio_dev); out_poweroff: mma9551_set_device_state(client, false); @@ -525,11 +523,12 @@ static int mma9551_remove(struct i2c_client *client) struct iio_dev *indio_dev = i2c_get_clientdata(client); struct mma9551_data *data = iio_priv(indio_dev); + iio_device_unregister(indio_dev); + pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); pm_runtime_put_noidle(&client->dev); - iio_device_unregister(indio_dev); mutex_lock(&data->mutex); mma9551_set_device_state(data->client, false); mutex_unlock(&data->mutex); diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index 9408ef3..fa7d362 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -1133,27 +1133,24 @@ static int mma9553_probe(struct i2c_client *client, } } - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "unable to register iio device\n"); - goto out_poweroff; - } - ret = pm_runtime_set_active(&client->dev); if (ret < 0) - goto out_iio_unregister; + goto out_poweroff; pm_runtime_enable(&client->dev); pm_runtime_set_autosuspend_delay(&client->dev, MMA9551_AUTO_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(&client->dev); - dev_dbg(&indio_dev->dev, "Registered device %s\n", name); + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "unable to register iio device\n"); + goto out_poweroff; + } + dev_dbg(&indio_dev->dev, "Registered device %s\n", name); return 0; -out_iio_unregister: - iio_device_unregister(indio_dev); out_poweroff: mma9551_set_device_state(client, false); return ret; @@ -1164,11 +1161,12 @@ static int mma9553_remove(struct i2c_client *client) struct iio_dev *indio_dev = i2c_get_clientdata(client); struct mma9553_data *data = iio_priv(indio_dev); + iio_device_unregister(indio_dev); + pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); pm_runtime_put_noidle(&client->dev); - iio_device_unregister(indio_dev); mutex_lock(&data->mutex); mma9551_set_device_state(data->client, false); mutex_unlock(&data->mutex); diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index 02ff789..bbce3b0 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -1077,25 +1077,23 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq, goto err_trigger_unregister; } - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(dev, "unable to register iio device\n"); - goto err_buffer_cleanup; - } - ret = pm_runtime_set_active(dev); if (ret) - goto err_iio_unregister; + goto err_buffer_cleanup; pm_runtime_enable(dev); pm_runtime_set_autosuspend_delay(dev, BMG160_AUTO_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(dev); + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(dev, "unable to register iio device\n"); + goto err_buffer_cleanup; + } + return 0; -err_iio_unregister: - iio_device_unregister(indio_dev); err_buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); err_trigger_unregister: @@ -1113,11 +1111,12 @@ void bmg160_core_remove(struct device *dev) struct iio_dev *indio_dev = dev_get_drvdata(dev); struct bmg160_data *data = iio_priv(indio_dev); + iio_device_unregister(indio_dev); + pm_runtime_disable(dev); pm_runtime_set_suspended(dev); pm_runtime_put_noidle(dev); - iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); if (data->dready_trig) { diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index dbf5e99..e5306b4 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -1390,6 +1390,14 @@ static int kmx61_probe(struct i2c_client *client, } } + ret = pm_runtime_set_active(&client->dev); + if (ret < 0) + goto err_buffer_cleanup_mag; + + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, KMX61_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + ret = iio_device_register(data->acc_indio_dev); if (ret < 0) { dev_err(&client->dev, "Failed to register acc iio device\n"); @@ -1402,18 +1410,8 @@ static int kmx61_probe(struct i2c_client *client, goto err_iio_unregister_acc; } - ret = pm_runtime_set_active(&client->dev); - if (ret < 0) - goto err_iio_unregister_mag; - - pm_runtime_enable(&client->dev); - pm_runtime_set_autosuspend_delay(&client->dev, KMX61_SLEEP_DELAY_MS); - pm_runtime_use_autosuspend(&client->dev); - return 0; -err_iio_unregister_mag: - iio_device_unregister(data->mag_indio_dev); err_iio_unregister_acc: iio_device_unregister(data->acc_indio_dev); err_buffer_cleanup_mag: @@ -1437,13 +1435,13 @@ static int kmx61_remove(struct i2c_client *client) { struct kmx61_data *data = i2c_get_clientdata(client); + iio_device_unregister(data->acc_indio_dev); + iio_device_unregister(data->mag_indio_dev); + pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); pm_runtime_put_noidle(&client->dev); - iio_device_unregister(data->acc_indio_dev); - iio_device_unregister(data->mag_indio_dev); - if (client->irq > 0) { iio_triggered_buffer_cleanup(data->acc_indio_dev); iio_triggered_buffer_cleanup(data->mag_indio_dev); diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index 4b75bb0..7de0f39 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -507,34 +507,28 @@ static int rpr0521_probe(struct i2c_client *client, dev_err(&client->dev, "rpr0521 chip init failed\n"); return ret; } - ret = iio_device_register(indio_dev); - if (ret < 0) - return ret; ret = pm_runtime_set_active(&client->dev); if (ret < 0) - goto err_iio_unregister; + return ret; pm_runtime_enable(&client->dev); pm_runtime_set_autosuspend_delay(&client->dev, RPR0521_SLEEP_DELAY_MS); pm_runtime_use_autosuspend(&client->dev); - return 0; - -err_iio_unregister: - iio_device_unregister(indio_dev); - return ret; + return iio_device_register(indio_dev); } static int rpr0521_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); + iio_device_unregister(indio_dev); + pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); pm_runtime_put_noidle(&client->dev); - iio_device_unregister(indio_dev); rpr0521_poweroff(iio_priv(indio_dev)); return 0; diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index 1615b23..ffcb75e 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -928,27 +928,24 @@ static int bmc150_magn_probe(struct i2c_client *client, goto err_free_irq; } - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&client->dev, "unable to register iio device\n"); - goto err_buffer_cleanup; - } - ret = pm_runtime_set_active(&client->dev); if (ret) - goto err_iio_unregister; + goto err_buffer_cleanup; pm_runtime_enable(&client->dev); pm_runtime_set_autosuspend_delay(&client->dev, BMC150_MAGN_AUTO_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(&client->dev); - dev_dbg(&indio_dev->dev, "Registered device %s\n", name); + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "unable to register iio device\n"); + goto err_buffer_cleanup; + } + dev_dbg(&indio_dev->dev, "Registered device %s\n", name); return 0; -err_iio_unregister: - iio_device_unregister(indio_dev); err_buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); err_free_irq: @@ -967,11 +964,12 @@ static int bmc150_magn_remove(struct i2c_client *client) struct iio_dev *indio_dev = i2c_get_clientdata(client); struct bmc150_magn_data *data = iio_priv(indio_dev); + iio_device_unregister(indio_dev); + pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); pm_runtime_put_noidle(&client->dev); - iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); if (client->irq > 0) -- cgit v0.10.2 From a106b4748917ba510d083217dbc25e56299f32d4 Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Sun, 1 Nov 2015 14:58:44 +0200 Subject: iio: gyro: check sscanf return value This patch fixes the checkpatch warnings: WARNING: unchecked sscanf return value Signed-off-by: Ioana Ciornei Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index 26de876..bb09bff 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -435,7 +435,9 @@ static int adis16136_initial_setup(struct iio_dev *indio_dev) if (ret) return ret; - sscanf(indio_dev->name, "adis%u\n", &device_id); + ret = sscanf(indio_dev->name, "adis%u\n", &device_id); + if (ret != 1) + return -EINVAL; if (prod_id != device_id) dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.", -- cgit v0.10.2 From 72a868b38bdd60cbc4084a91fd7b8df3e2bb96ba Mon Sep 17 00:00:00 2001 From: Ioana Ciornei Date: Sun, 1 Nov 2015 14:58:45 +0200 Subject: iio: imu: check sscanf return value This patch fixes the following checkpatch warning: WARNING: unchecked sscanf return value Signed-off-by: Ioana Ciornei Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400_core.c index abc4c50..72bcc24 100644 --- a/drivers/iio/imu/adis16400_core.c +++ b/drivers/iio/imu/adis16400_core.c @@ -288,7 +288,11 @@ static int adis16400_initial_setup(struct iio_dev *indio_dev) if (ret) goto err_ret; - sscanf(indio_dev->name, "adis%u\n", &device_id); + ret = sscanf(indio_dev->name, "adis%u\n", &device_id); + if (ret != 1) { + ret = -EINVAL; + goto err_ret; + } if (prod_id != device_id) dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.", diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index b94bfd3..16d4304 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -765,7 +765,9 @@ static int adis16480_initial_setup(struct iio_dev *indio_dev) if (ret) return ret; - sscanf(indio_dev->name, "adis%u\n", &device_id); + ret = sscanf(indio_dev->name, "adis%u\n", &device_id); + if (ret != 1) + return -EINVAL; if (prod_id != device_id) dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.", -- cgit v0.10.2 From 34dc578d99449a83dcb0f5ef4444215590183af4 Mon Sep 17 00:00:00 2001 From: Giuseppe Barba Date: Thu, 12 Nov 2015 08:36:49 +0100 Subject: iio: st-accel: add support for lis2dh12 This commit add support for STMicroelectronics lis2dh12 accelerometer. Datasheet for this device can be found here: http://www.st.com/st-web-ui/static/active/en/resource/technical/ document/datasheet/DM00091513.pdf Signed-off-by: Giuseppe Barba Acked-by: Denis Ciocca Acked-by: Daniel Baluta Signed-off-by: Jonathan Cameron diff --git a/Documentation/devicetree/bindings/iio/st-sensors.txt b/Documentation/devicetree/bindings/iio/st-sensors.txt index d3ccdb1..d4b87cc 100644 --- a/Documentation/devicetree/bindings/iio/st-sensors.txt +++ b/Documentation/devicetree/bindings/iio/st-sensors.txt @@ -36,6 +36,7 @@ Accelerometers: - st,lsm303dlm-accel - st,lsm330-accel - st,lsm303agr-accel +- st,lis2dh12-accel Gyroscopes: - st,l3g4200d-gyro diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 87487d3..edc29b1 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -64,7 +64,7 @@ config IIO_ST_ACCEL_3AXIS help Say yes here to build support for STMicroelectronics accelerometers: LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC, - LIS331DLH, LSM303DL, LSM303DLM, LSM330. + LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12. This driver can also be built as a module. If so, these modules will be created: diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 468f21f..5d4a189 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -27,6 +27,7 @@ #define LSM303DLM_ACCEL_DEV_NAME "lsm303dlm_accel" #define LSM330_ACCEL_DEV_NAME "lsm330_accel" #define LSM303AGR_ACCEL_DEV_NAME "lsm303agr_accel" +#define LIS2DH12_ACCEL_DEV_NAME "lis2dh12_accel" /** * struct st_sensors_platform_data - default accel platform data diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index dab8b76..9d973f1 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -234,6 +234,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { [3] = LSM330DL_ACCEL_DEV_NAME, [4] = LSM330DLC_ACCEL_DEV_NAME, [5] = LSM303AGR_ACCEL_DEV_NAME, + [6] = LIS2DH12_ACCEL_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_accel_12bit_channels, .odr = { diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 8b9cc84..294a32f 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -72,6 +72,10 @@ static const struct of_device_id st_accel_of_match[] = { .compatible = "st,lsm303agr-accel", .data = LSM303AGR_ACCEL_DEV_NAME, }, + { + .compatible = "st,lis2dh12-accel", + .data = LIS2DH12_ACCEL_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_accel_of_match); @@ -121,6 +125,7 @@ static const struct i2c_device_id st_accel_id_table[] = { { LSM303DLM_ACCEL_DEV_NAME }, { LSM330_ACCEL_DEV_NAME }, { LSM303AGR_ACCEL_DEV_NAME }, + { LIS2DH12_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(i2c, st_accel_id_table); diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 54b61a3..e82bedf 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -58,6 +58,7 @@ static const struct spi_device_id st_accel_id_table[] = { { LSM303DLM_ACCEL_DEV_NAME }, { LSM330_ACCEL_DEV_NAME }, { LSM303AGR_ACCEL_DEV_NAME }, + { LIS2DH12_ACCEL_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_accel_id_table); -- cgit v0.10.2 From f47dff323088462e7b0ac52d1ba41ce953a5ce20 Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Mon, 9 Nov 2015 13:55:34 +0100 Subject: iio: core: added support for IIO_VAL_INT Added core support for IIO_VAL_INT in write_raw_get_fmt function. Signed-off-by: Sean Nyekjaer Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 208358f..d0a84fe 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -512,6 +512,12 @@ int iio_str_to_fixpoint(const char *str, int fract_mult, int i = 0, f = 0; bool integer_part = true, negative = false; + if (fract_mult == 0) { + *fract = 0; + + return kstrtoint(str, 0, integer); + } + if (str[0] == '-') { negative = true; str++; @@ -571,6 +577,9 @@ static ssize_t iio_write_channel_info(struct device *dev, if (indio_dev->info->write_raw_get_fmt) switch (indio_dev->info->write_raw_get_fmt(indio_dev, this_attr->c, this_attr->address)) { + case IIO_VAL_INT: + fract_mult = 0; + break; case IIO_VAL_INT_PLUS_MICRO: fract_mult = 100000; break; -- cgit v0.10.2 From 3e87e78383283119a7d41f8a4cab8ef0a5c9acab Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Mon, 9 Nov 2015 13:52:59 +0100 Subject: iio: adc: Add TI ADS8688 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds support for the Texas Intruments ADS8688 ADC. Signed-off-by: Sean Nyekjaer Reviewed-by: Martin Hundebøll Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index daad72e..9162dfe 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -341,6 +341,16 @@ config TI_ADC128S052 This driver can also be built as a module. If so, the module will be called ti-adc128s052. +config TI_ADS8688 + tristate "Texas Instruments ADS8688" + depends on SPI && OF + help + If you say yes here you get support for Texas Instruments ADS8684 and + and ADS8688 ADC chips + + This driver can also be built as a module. If so, the module will be + called ti-ads8688. + config TI_AM335X_ADC tristate "TI's AM335X ADC driver" depends on MFD_TI_AM335X_TSCADC diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 11cfdfd..91a65bf 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o +obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c new file mode 100644 index 0000000..03e9070 --- /dev/null +++ b/drivers/iio/adc/ti-ads8688.c @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2015 Prevas A/S + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define ADS8688_CMD_REG(x) (x << 8) +#define ADS8688_CMD_REG_NOOP 0x00 +#define ADS8688_CMD_REG_RST 0x85 +#define ADS8688_CMD_REG_MAN_CH(chan) (0xC0 | (4 * chan)) +#define ADS8688_CMD_DONT_CARE_BITS 16 + +#define ADS8688_PROG_REG(x) (x << 9) +#define ADS8688_PROG_REG_RANGE_CH(chan) (0x05 + chan) +#define ADS8688_PROG_WR_BIT BIT(8) +#define ADS8688_PROG_DONT_CARE_BITS 8 + +#define ADS8688_REG_PLUSMINUS25VREF 0 +#define ADS8688_REG_PLUSMINUS125VREF 1 +#define ADS8688_REG_PLUSMINUS0625VREF 2 +#define ADS8688_REG_PLUS25VREF 5 +#define ADS8688_REG_PLUS125VREF 6 + +#define ADS8688_VREF_MV 4096 +#define ADS8688_REALBITS 16 + +/* + * enum ads8688_range - ADS8688 reference voltage range + * @ADS8688_PLUSMINUS25VREF: Device is configured for input range ±2.5 * VREF + * @ADS8688_PLUSMINUS125VREF: Device is configured for input range ±1.25 * VREF + * @ADS8688_PLUSMINUS0625VREF: Device is configured for input range ±0.625 * VREF + * @ADS8688_PLUS25VREF: Device is configured for input range 0 - 2.5 * VREF + * @ADS8688_PLUS125VREF: Device is configured for input range 0 - 1.25 * VREF + */ +enum ads8688_range { + ADS8688_PLUSMINUS25VREF, + ADS8688_PLUSMINUS125VREF, + ADS8688_PLUSMINUS0625VREF, + ADS8688_PLUS25VREF, + ADS8688_PLUS125VREF, +}; + +struct ads8688_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct ads8688_state { + struct mutex lock; + const struct ads8688_chip_info *chip_info; + struct spi_device *spi; + struct regulator *reg; + unsigned int vref_mv; + enum ads8688_range range[8]; + union { + __be32 d32; + u8 d8[4]; + } data[2] ____cacheline_aligned; +}; + +enum ads8688_id { + ID_ADS8684, + ID_ADS8688, +}; + +struct ads8688_ranges { + enum ads8688_range range; + unsigned int scale; + int offset; + u8 reg; +}; + +static const struct ads8688_ranges ads8688_range_def[5] = { + { + .range = ADS8688_PLUSMINUS25VREF, + .scale = 76295, + .offset = -(1 << (ADS8688_REALBITS - 1)), + .reg = ADS8688_REG_PLUSMINUS25VREF, + }, { + .range = ADS8688_PLUSMINUS125VREF, + .scale = 38148, + .offset = -(1 << (ADS8688_REALBITS - 1)), + .reg = ADS8688_REG_PLUSMINUS125VREF, + }, { + .range = ADS8688_PLUSMINUS0625VREF, + .scale = 19074, + .offset = -(1 << (ADS8688_REALBITS - 1)), + .reg = ADS8688_REG_PLUSMINUS0625VREF, + }, { + .range = ADS8688_PLUS25VREF, + .scale = 38148, + .offset = 0, + .reg = ADS8688_REG_PLUS25VREF, + }, { + .range = ADS8688_PLUS125VREF, + .scale = 19074, + .offset = 0, + .reg = ADS8688_REG_PLUS125VREF, + } +}; + +static ssize_t ads8688_show_scales(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ads8688_state *st = iio_priv(dev_to_iio_dev(dev)); + + return sprintf(buf, "0.%09u 0.%09u 0.%09u\n", + ads8688_range_def[0].scale * st->vref_mv, + ads8688_range_def[1].scale * st->vref_mv, + ads8688_range_def[2].scale * st->vref_mv); +} + +static ssize_t ads8688_show_offsets(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d %d\n", ads8688_range_def[0].offset, + ads8688_range_def[3].offset); +} + +static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO, + ads8688_show_scales, NULL, 0); +static IIO_DEVICE_ATTR(in_voltage_offset_available, S_IRUGO, + ads8688_show_offsets, NULL, 0); + +static struct attribute *ads8688_attributes[] = { + &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, + &iio_dev_attr_in_voltage_offset_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ads8688_attribute_group = { + .attrs = ads8688_attributes, +}; + +#define ADS8688_CHAN(index) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET), \ +} + +static const struct iio_chan_spec ads8684_channels[] = { + ADS8688_CHAN(0), + ADS8688_CHAN(1), + ADS8688_CHAN(2), + ADS8688_CHAN(3), +}; + +static const struct iio_chan_spec ads8688_channels[] = { + ADS8688_CHAN(0), + ADS8688_CHAN(1), + ADS8688_CHAN(2), + ADS8688_CHAN(3), + ADS8688_CHAN(4), + ADS8688_CHAN(5), + ADS8688_CHAN(6), + ADS8688_CHAN(7), +}; + +static int ads8688_prog_write(struct iio_dev *indio_dev, unsigned int addr, + unsigned int val) +{ + struct ads8688_state *st = iio_priv(indio_dev); + u32 tmp; + + tmp = ADS8688_PROG_REG(addr) | ADS8688_PROG_WR_BIT | val; + tmp <<= ADS8688_PROG_DONT_CARE_BITS; + st->data[0].d32 = cpu_to_be32(tmp); + + return spi_write(st->spi, &st->data[0].d8[1], 3); +} + +static int ads8688_reset(struct iio_dev *indio_dev) +{ + struct ads8688_state *st = iio_priv(indio_dev); + u32 tmp; + + tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_RST); + tmp <<= ADS8688_CMD_DONT_CARE_BITS; + st->data[0].d32 = cpu_to_be32(tmp); + + return spi_write(st->spi, &st->data[0].d8[0], 4); +} + +static int ads8688_read(struct iio_dev *indio_dev, unsigned int chan) +{ + struct ads8688_state *st = iio_priv(indio_dev); + int ret; + u32 tmp; + struct spi_transfer t[] = { + { + .tx_buf = &st->data[0].d8[0], + .len = 4, + .cs_change = 1, + }, { + .tx_buf = &st->data[1].d8[0], + .rx_buf = &st->data[1].d8[0], + .len = 4, + }, + }; + + tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_MAN_CH(chan)); + tmp <<= ADS8688_CMD_DONT_CARE_BITS; + st->data[0].d32 = cpu_to_be32(tmp); + + tmp = ADS8688_CMD_REG(ADS8688_CMD_REG_NOOP); + tmp <<= ADS8688_CMD_DONT_CARE_BITS; + st->data[1].d32 = cpu_to_be32(tmp); + + ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t)); + if (ret < 0) + return ret; + + return be32_to_cpu(st->data[1].d32) & 0xffff; +} + +static int ads8688_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long m) +{ + int ret, offset; + unsigned long scale_mv; + + struct ads8688_state *st = iio_priv(indio_dev); + + mutex_lock(&st->lock); + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = ads8688_read(indio_dev, chan->channel); + mutex_unlock(&st->lock); + if (ret < 0) + return ret; + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + scale_mv = st->vref_mv; + scale_mv *= ads8688_range_def[st->range[chan->channel]].scale; + *val = 0; + *val2 = scale_mv; + mutex_unlock(&st->lock); + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_OFFSET: + offset = ads8688_range_def[st->range[chan->channel]].offset; + *val = offset; + mutex_unlock(&st->lock); + return IIO_VAL_INT; + } + mutex_unlock(&st->lock); + + return -EINVAL; +} + +static int ads8688_write_reg_range(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + enum ads8688_range range) +{ + unsigned int tmp; + int ret; + + tmp = ADS8688_PROG_REG_RANGE_CH(chan->channel); + ret = ads8688_prog_write(indio_dev, tmp, range); + + return ret; +} + +static int ads8688_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ads8688_state *st = iio_priv(indio_dev); + unsigned int scale = 0; + int ret = -EINVAL, i, offset = 0; + + mutex_lock(&st->lock); + switch (mask) { + case IIO_CHAN_INFO_SCALE: + /* If the offset is 0 the ±2.5 * VREF mode is not available */ + offset = ads8688_range_def[st->range[chan->channel]].offset; + if (offset == 0 && val2 == ads8688_range_def[0].scale * st->vref_mv) { + mutex_unlock(&st->lock); + return -EINVAL; + } + + /* Lookup new mode */ + for (i = 0; i < ARRAY_SIZE(ads8688_range_def); i++) + if (val2 == ads8688_range_def[i].scale * st->vref_mv && + offset == ads8688_range_def[i].offset) { + ret = ads8688_write_reg_range(indio_dev, chan, + ads8688_range_def[i].reg); + break; + } + break; + case IIO_CHAN_INFO_OFFSET: + /* + * There are only two available offsets: + * 0 and -(1 << (ADS8688_REALBITS - 1)) + */ + if (!(ads8688_range_def[0].offset == val || + ads8688_range_def[3].offset == val)) { + mutex_unlock(&st->lock); + return -EINVAL; + } + + /* + * If the device are in ±2.5 * VREF mode, it's not allowed to + * switch to a mode where the offset is 0 + */ + if (val == 0 && + st->range[chan->channel] == ADS8688_PLUSMINUS25VREF) { + mutex_unlock(&st->lock); + return -EINVAL; + } + + scale = ads8688_range_def[st->range[chan->channel]].scale; + + /* Lookup new mode */ + for (i = 0; i < ARRAY_SIZE(ads8688_range_def); i++) + if (val == ads8688_range_def[i].offset && + scale == ads8688_range_def[i].scale) { + ret = ads8688_write_reg_range(indio_dev, chan, + ads8688_range_def[i].reg); + break; + } + break; + } + + if (!ret) + st->range[chan->channel] = ads8688_range_def[i].range; + + mutex_unlock(&st->lock); + + return ret; +} + +static int ads8688_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; + case IIO_CHAN_INFO_OFFSET: + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static const struct iio_info ads8688_info = { + .read_raw = &ads8688_read_raw, + .write_raw = &ads8688_write_raw, + .write_raw_get_fmt = &ads8688_write_raw_get_fmt, + .attrs = &ads8688_attribute_group, + .driver_module = THIS_MODULE, +}; + +static const struct ads8688_chip_info ads8688_chip_info_tbl[] = { + [ID_ADS8684] = { + .channels = ads8684_channels, + .num_channels = ARRAY_SIZE(ads8684_channels), + }, + [ID_ADS8688] = { + .channels = ads8688_channels, + .num_channels = ARRAY_SIZE(ads8688_channels), + }, +}; + +static int ads8688_probe(struct spi_device *spi) +{ + struct ads8688_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->reg = devm_regulator_get_optional(&spi->dev, "vref"); + if (!IS_ERR(st->reg)) { + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = regulator_get_voltage(st->reg); + if (ret < 0) + goto error_out; + + st->vref_mv = ret / 1000; + } else { + /* Use internal reference */ + st->vref_mv = ADS8688_VREF_MV; + } + + st->chip_info = &ads8688_chip_info_tbl[spi_get_device_id(spi)->driver_data]; + + spi->mode = SPI_MODE_1; + + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channels; + indio_dev->num_channels = st->chip_info->num_channels; + indio_dev->info = &ads8688_info; + + ads8688_reset(indio_dev); + + mutex_init(&st->lock); + + ret = iio_device_register(indio_dev); + if (ret) + goto error_out; + + return 0; + +error_out: + if (!IS_ERR_OR_NULL(st->reg)) + regulator_disable(st->reg); + + return ret; +} + +static int ads8688_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct ads8688_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + if (!IS_ERR_OR_NULL(st->reg)) + regulator_disable(st->reg); + + return 0; +} + +static const struct spi_device_id ads8688_id[] = { + {"ads8684", ID_ADS8684}, + {"ads8688", ID_ADS8688}, + {} +}; +MODULE_DEVICE_TABLE(spi, ads8688_id); + +static const struct of_device_id ads8688_of_match[] = { + { .compatible = "ti,ads8684" }, + { .compatible = "ti,ads8688" }, + { } +}; +MODULE_DEVICE_TABLE(of, ads8688_of_match); + +static struct spi_driver ads8688_driver = { + .driver = { + .name = "ads8688", + .owner = THIS_MODULE, + }, + .probe = ads8688_probe, + .remove = ads8688_remove, + .id_table = ads8688_id, +}; +module_spi_driver(ads8688_driver); + +MODULE_AUTHOR("Sean Nyekjaer "); +MODULE_DESCRIPTION("Texas Instruments ADS8688 driver"); +MODULE_LICENSE("GPL v2"); -- cgit v0.10.2 From a1513641892b220bc5b7ce63e44b81658702cdae Mon Sep 17 00:00:00 2001 From: Sean Nyekjaer Date: Mon, 9 Nov 2015 13:53:00 +0100 Subject: iio: ti-ads8688: Add DT binding documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adding binding documentation for Texas Instruments ADS8688 ADC. Signed-off-by: Sean Nyekjaer Reviewed-by: Martin Hundebøll Acked-by: Rob Herring Signed-off-by: Jonathan Cameron diff --git a/Documentation/devicetree/bindings/iio/adc/ti-ads8688.txt b/Documentation/devicetree/bindings/iio/adc/ti-ads8688.txt new file mode 100644 index 0000000..a02337d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti-ads8688.txt @@ -0,0 +1,20 @@ +* Texas Instruments' ADS8684 and ADS8688 ADC chip + +Required properties: + - compatible: Should be "ti,ads8684" or "ti,ads8688" + - reg: spi chip select number for the device + +Recommended properties: + - spi-max-frequency: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt + +Optional properties: + - vref-supply: The regulator supply for ADC reference voltage + +Example: +adc@0 { + compatible = "ti,ads8688"; + reg = <0>; + vref-supply = <&vdd_supply>; + spi-max-frequency = <1000000>; +}; -- cgit v0.10.2 From e0c961bdaf2786bc9795efff6e87a15491778384 Mon Sep 17 00:00:00 2001 From: Nizam Haider Date: Mon, 9 Nov 2015 19:56:02 +0530 Subject: iio: adc: mxs-lradc: Prefer using the BIT macro Replaces bit shifting on 1 with the BIT(x) macro Signed-off-by: Nizam Haider Signed-off-by: Jonathan Cameron diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index 407d4a2..1f25027 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -324,7 +324,7 @@ struct mxs_lradc { #define LRADC_DELAY_TRIGGER(x) \ (((x) << LRADC_DELAY_TRIGGER_LRADCS_OFFSET) & \ LRADC_DELAY_TRIGGER_LRADCS_MASK) -#define LRADC_DELAY_KICK (1 << 20) +#define LRADC_DELAY_KICK BIT(20) #define LRADC_DELAY_TRIGGER_DELAYS_MASK (0xf << 16) #define LRADC_DELAY_TRIGGER_DELAYS_OFFSET 16 #define LRADC_DELAY_TRIGGER_DELAYS(x) \ -- cgit v0.10.2 From 7d173f26353df0e90ea1223b80f6834845f27435 Mon Sep 17 00:00:00 2001 From: Nizam Haider Date: Mon, 9 Nov 2015 18:20:45 +0530 Subject: iio: adc: ad7793: removed unnecessary else. Else is not generally useful after a break or return. Signed-off-by: Nizam Haider Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index b84922a..d3eeb3f 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -478,10 +478,9 @@ static int ad7793_read_raw(struct iio_dev *indio_dev, *val2 = st-> scale_avail[(st->conf >> 8) & 0x7][1]; return IIO_VAL_INT_PLUS_NANO; - } else { - /* 1170mV / 2^23 * 6 */ - scale_uv = (1170ULL * 1000000000ULL * 6ULL); } + /* 1170mV / 2^23 * 6 */ + scale_uv = (1170ULL * 1000000000ULL * 6ULL); break; case IIO_TEMP: /* 1170mV / 0.81 mV/C / 2^23 */ -- cgit v0.10.2 From 52555a5d1058eec96c6bac2928bc965b0077c60e Mon Sep 17 00:00:00 2001 From: Dragos Bogdan Date: Wed, 18 Nov 2015 12:35:34 +0200 Subject: staging:iio:ad7780: Switch to the gpio descriptor interface Use the gpiod interface for the powerdown_gpio instead of the deprecated old non-descriptor interface. The powerdown pin can be tied high, so the gpio is optional. Remove the gpio_pdrst platform_data member since the new interface is used. Signed-off-by: Dragos Bogdan Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron diff --git a/drivers/staging/iio/adc/ad7780.c b/drivers/staging/iio/adc/ad7780.c index 618b41f..98b5fc4 100644 --- a/drivers/staging/iio/adc/ad7780.c +++ b/drivers/staging/iio/adc/ad7780.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -42,7 +42,7 @@ struct ad7780_chip_info { struct ad7780_state { const struct ad7780_chip_info *chip_info; struct regulator *reg; - int powerdown_gpio; + struct gpio_desc *powerdown_gpio; unsigned int gain; u16 int_vref_mv; @@ -77,8 +77,7 @@ static int ad7780_set_mode(struct ad_sigma_delta *sigma_delta, break; } - if (gpio_is_valid(st->powerdown_gpio)) - gpio_set_value(st->powerdown_gpio, val); + gpiod_set_value(st->powerdown_gpio, val); return 0; } @@ -205,18 +204,14 @@ static int ad7780_probe(struct spi_device *spi) indio_dev->num_channels = 1; indio_dev->info = &ad7780_info; - if (pdata && gpio_is_valid(pdata->gpio_pdrst)) { - ret = devm_gpio_request_one(&spi->dev, - pdata->gpio_pdrst, - GPIOF_OUT_INIT_LOW, - "AD7780 /PDRST"); - if (ret) { - dev_err(&spi->dev, "failed to request GPIO PDRST\n"); - goto error_disable_reg; - } - st->powerdown_gpio = pdata->gpio_pdrst; - } else { - st->powerdown_gpio = -1; + st->powerdown_gpio = devm_gpiod_get_optional(&spi->dev, + "powerdown", + GPIOD_OUT_LOW); + if (IS_ERR(st->powerdown_gpio)) { + ret = PTR_ERR(st->powerdown_gpio); + dev_err(&spi->dev, "Failed to request powerdown GPIO: %d\n", + ret); + goto error_disable_reg; } ret = ad_sd_setup_buffer_and_trigger(indio_dev); diff --git a/drivers/staging/iio/adc/ad7780.h b/drivers/staging/iio/adc/ad7780.h index 67e511c..46f61c9 100644 --- a/drivers/staging/iio/adc/ad7780.h +++ b/drivers/staging/iio/adc/ad7780.h @@ -24,7 +24,6 @@ struct ad7780_platform_data { u16 vref_mv; - int gpio_pdrst; }; #endif /* IIO_ADC_AD7780_H_ */ -- cgit v0.10.2 From cef7e12585e61e2a03aea1c99331865213982f3a Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 15 Nov 2015 21:00:02 +0100 Subject: iio: adc: xilinx: constify iio_buffer_setup_ops structure The iio_buffer_setup_ops structures are never modified, so declare this one as const, like the others. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 0370624..c2b5f10 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -803,7 +803,7 @@ err: return ret; } -static struct iio_buffer_setup_ops xadc_buffer_ops = { +static const struct iio_buffer_setup_ops xadc_buffer_ops = { .preenable = &xadc_preenable, .postenable = &iio_triggered_buffer_postenable, .predisable = &iio_triggered_buffer_predisable, -- cgit v0.10.2 From 8ffedc1b128af336ab650cef4883015d1c49269d Mon Sep 17 00:00:00 2001 From: Dragos Bogdan Date: Wed, 18 Nov 2015 16:16:34 +0200 Subject: staging:iio:ad7780: Remove the ad7780_platform_data The ad7780_platform_data contains just the reference voltage information. Since the preferred way of specifying this information is using the Linux regulator framework and the ad7780 platform_data is not used by other users, it can be completely removed. Signed-off-by: Dragos Bogdan Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron diff --git a/drivers/staging/iio/adc/ad7780.c b/drivers/staging/iio/adc/ad7780.c index 98b5fc4..498e86b 100644 --- a/drivers/staging/iio/adc/ad7780.c +++ b/drivers/staging/iio/adc/ad7780.c @@ -22,8 +22,6 @@ #include #include -#include "ad7780.h" - #define AD7780_RDY BIT(7) #define AD7780_FILTER BIT(6) #define AD7780_ERR BIT(5) @@ -162,7 +160,6 @@ static const struct iio_info ad7780_info = { static int ad7780_probe(struct spi_device *spi) { - struct ad7780_platform_data *pdata = spi->dev.platform_data; struct ad7780_state *st; struct iio_dev *indio_dev; int ret, voltage_uv = 0; @@ -188,12 +185,10 @@ static int ad7780_probe(struct spi_device *spi) st->chip_info = &ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - if (pdata && pdata->vref_mv) - st->int_vref_mv = pdata->vref_mv; - else if (voltage_uv) + if (voltage_uv) st->int_vref_mv = voltage_uv / 1000; else - dev_warn(&spi->dev, "reference voltage unspecified\n"); + dev_warn(&spi->dev, "Reference voltage unspecified\n"); spi_set_drvdata(spi, indio_dev); diff --git a/drivers/staging/iio/adc/ad7780.h b/drivers/staging/iio/adc/ad7780.h deleted file mode 100644 index 46f61c9..0000000 --- a/drivers/staging/iio/adc/ad7780.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * AD7780/AD7781 SPI ADC driver - * - * Copyright 2011 Analog Devices Inc. - * - * Licensed under the GPL-2. - */ -#ifndef IIO_ADC_AD7780_H_ -#define IIO_ADC_AD7780_H_ - -/* - * TODO: struct ad7780_platform_data needs to go into include/linux/iio - */ - -/* NOTE: - * The AD7780 doesn't feature a dedicated SPI chip select, in addition it - * features a dual use data out ready DOUT/RDY output. - * In order to avoid contentions on the SPI bus, it's therefore necessary - * to use spi bus locking combined with a dedicated GPIO to control the - * power down reset signal of the AD7780. - * - * The DOUT/RDY output must also be wired to an interrupt capable GPIO. - */ - -struct ad7780_platform_data { - u16 vref_mv; -}; - -#endif /* IIO_ADC_AD7780_H_ */ -- cgit v0.10.2 From 438f13a283e776957779e9beb0503100801ea84f Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 21 Nov 2015 18:20:06 +0000 Subject: staging:iio: Delete some commented out lines in Kconfig and Makefile. These should have been removed with the driver move out of staging but instead were commented out. This was missed in reviews at the time so fixing it up now. Signed-off-by: Jonathan Cameron diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index 85de198..0e044cb 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -17,32 +17,4 @@ source "drivers/staging/iio/meter/Kconfig" source "drivers/staging/iio/resolver/Kconfig" source "drivers/staging/iio/trigger/Kconfig" -#config IIO_DUMMY_EVGEN -# tristate -# -#config IIO_SIMPLE_DUMMY -# tristate "An example driver with no hardware requirements" -# help -# Driver intended mainly as documentation for how to write -# a driver. May also be useful for testing userspace code -# without hardware. - -#if IIO_SIMPLE_DUMMY - -#config IIO_SIMPLE_DUMMY_EVENTS -# bool "Event generation support" -# select IIO_DUMMY_EVGEN -# help -# Add some dummy events to the simple dummy driver. - -#config IIO_SIMPLE_DUMMY_BUFFER -# bool "Buffered capture support" -# select IIO_BUFFER -# select IIO_TRIGGER -# select IIO_KFIFO_BUF -# help -# Add buffered data capture to the simple dummy driver. - -#endif # IIO_SIMPLE_DUMMY - endmenu diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index 355824a..3e616b4 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -2,13 +2,6 @@ # Makefile for the industrial I/O core. # -#obj-$(CONFIG_IIO_SIMPLE_DUMMY) += iio_dummy.o -#iio_dummy-y := iio_simple_dummy.o -#iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_EVENTS) += iio_simple_dummy_events.o -#iio_dummy-$(CONFIG_IIO_SIMPLE_DUMMY_BUFFER) += iio_simple_dummy_buffer.o - -#obj-$(CONFIG_IIO_DUMMY_EVGEN) += iio_dummy_evgen.o - obj-y += accel/ obj-y += adc/ obj-y += addac/ -- cgit v0.10.2 From 3fba9b5ff837ed57a993e98245378c30911ab4ee Mon Sep 17 00:00:00 2001 From: Nizam Haider Date: Mon, 16 Nov 2015 05:35:57 +0530 Subject: IIO: adc: at91_adc.c Prefer kmalloc_array over kmalloc with multiply So this patch swaps that use out for kmalloc_array instead. Signed-off-by Nizam Haider Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 7b40925..f284cd6 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -742,7 +742,7 @@ static int at91_adc_of_get_resolution(struct at91_adc_state *st, return count; } - resolutions = kmalloc(count * sizeof(*resolutions), GFP_KERNEL); + resolutions = kmalloc_array(count, sizeof(*resolutions), GFP_KERNEL); if (!resolutions) return -ENOMEM; -- cgit v0.10.2 From 4ac4e086fd8c59e6b69089e6f7605500b63a6d17 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Sun, 15 Nov 2015 17:20:05 -0800 Subject: iio: pulsedlight-lidar-lite: add runtime PM Add runtime PM support for the lidar-lite module to enable low power mode when last device requested reading is over a second. Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index 961f9f99..be8ccef 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -13,7 +13,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * TODO: runtime pm, interrupt mode, and signal strength reporting + * TODO: interrupt mode, and signal strength reporting */ #include @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,7 @@ #define LIDAR_REG_DATA_HBYTE 0x0f #define LIDAR_REG_DATA_LBYTE 0x10 +#define LIDAR_REG_PWR_CONTROL 0x65 #define LIDAR_DRV_NAME "lidar" @@ -90,6 +92,12 @@ static inline int lidar_write_control(struct lidar_data *data, int val) return i2c_smbus_write_byte_data(data->client, LIDAR_REG_CONTROL, val); } +static inline int lidar_write_power(struct lidar_data *data, int val) +{ + return i2c_smbus_write_byte_data(data->client, + LIDAR_REG_PWR_CONTROL, val); +} + static int lidar_read_measurement(struct lidar_data *data, u16 *reg) { int ret; @@ -116,6 +124,8 @@ static int lidar_get_measurement(struct lidar_data *data, u16 *reg) int tries = 10; int ret; + pm_runtime_get_sync(&client->dev); + /* start sample */ ret = lidar_write_control(data, LIDAR_REG_CONTROL_ACQUIRE); if (ret < 0) { @@ -144,6 +154,8 @@ static int lidar_get_measurement(struct lidar_data *data, u16 *reg) } ret = -EIO; } + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); return ret; } @@ -243,6 +255,17 @@ static int lidar_probe(struct i2c_client *client, if (ret) goto error_unreg_buffer; + pm_runtime_set_autosuspend_delay(&client->dev, 1000); + pm_runtime_use_autosuspend(&client->dev); + + ret = pm_runtime_set_active(&client->dev); + if (ret) + goto error_unreg_buffer; + pm_runtime_enable(&client->dev); + + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_idle(&client->dev); + return 0; error_unreg_buffer: @@ -258,6 +281,9 @@ static int lidar_remove(struct i2c_client *client) iio_device_unregister(indio_dev); iio_triggered_buffer_cleanup(indio_dev); + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + return 0; } @@ -273,10 +299,38 @@ static const struct of_device_id lidar_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, lidar_dt_ids); +#ifdef CONFIG_PM +static int lidar_pm_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct lidar_data *data = iio_priv(indio_dev); + + return lidar_write_power(data, 0x0f); +} + +static int lidar_pm_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct lidar_data *data = iio_priv(indio_dev); + int ret = lidar_write_power(data, 0); + + /* regulator and FPGA needs settling time */ + usleep_range(15000, 20000); + + return ret; +} +#endif + +static const struct dev_pm_ops lidar_pm_ops = { + SET_RUNTIME_PM_OPS(lidar_pm_runtime_suspend, + lidar_pm_runtime_resume, NULL) +}; + static struct i2c_driver lidar_driver = { .driver = { .name = LIDAR_DRV_NAME, .of_match_table = of_match_ptr(lidar_dt_ids), + .pm = &lidar_pm_ops, }, .probe = lidar_probe, .remove = lidar_remove, -- cgit v0.10.2