diff options
Diffstat (limited to 'recipes-kernel/linux/files/0020-Quark-IIO-quark.patch')
-rw-r--r-- | recipes-kernel/linux/files/0020-Quark-IIO-quark.patch | 2742 |
1 files changed, 2742 insertions, 0 deletions
diff --git a/recipes-kernel/linux/files/0020-Quark-IIO-quark.patch b/recipes-kernel/linux/files/0020-Quark-IIO-quark.patch new file mode 100644 index 0000000..809c203 --- /dev/null +++ b/recipes-kernel/linux/files/0020-Quark-IIO-quark.patch @@ -0,0 +1,2742 @@ +From xxxx Mon Sep 17 00:00:00 2001 +From: Dan O'Donovan <dan.odonovan@emutex.com> +Date: Fri, 14 Feb 2014 14:10:33 +0000 +Subject: [PATCH 20/21] Quark IIO + +--- + drivers/iio/adc/ad7298.c | 20 +- + drivers/staging/iio/adc/Kconfig | 13 + + drivers/staging/iio/adc/Makefile | 1 + + drivers/staging/iio/adc/max78m6610_lmu.c | 2235 ++++++++++++++++++++++++ + drivers/staging/iio/trigger/Kconfig | 11 + + drivers/staging/iio/trigger/Makefile | 1 + + drivers/staging/iio/trigger/iio-trig-hrtimer.c | 288 +++ + include/linux/platform_data/ad7298.h | 5 + + include/linux/platform_data/max78m6610_lmu.h | 34 + + 9 files changed, 2603 insertions(+), 5 deletions(-) + create mode 100644 drivers/staging/iio/adc/max78m6610_lmu.c + create mode 100644 drivers/staging/iio/trigger/iio-trig-hrtimer.c + create mode 100644 include/linux/platform_data/max78m6610_lmu.h + +diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c +index b34d754..60491e4 100644 +--- a/drivers/iio/adc/ad7298.c ++++ b/drivers/iio/adc/ad7298.c +@@ -33,7 +33,6 @@ + #define AD7298_TAVG (1 << 1) /* temperature sensor averaging enable */ + #define AD7298_PDD (1 << 0) /* partial power down enable */ + +-#define AD7298_MAX_CHAN 8 + #define AD7298_BITS 12 + #define AD7298_STORAGE_BITS 16 + #define AD7298_INTREF_mV 2500 +@@ -46,6 +45,7 @@ struct ad7298_state { + struct spi_device *spi; + struct regulator *reg; + unsigned ext_ref; ++ u16 ext_vin_max[AD7298_MAX_CHAN]; + struct spi_transfer ring_xfer[10]; + struct spi_transfer scan_single_xfer[3]; + struct spi_message ring_msg; +@@ -64,7 +64,7 @@ struct ad7298_state { + .indexed = 1, \ + .channel = index, \ + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \ +- IIO_CHAN_INFO_SCALE_SHARED_BIT, \ ++ IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ +@@ -269,7 +269,10 @@ static int ad7298_read_raw(struct iio_dev *indio_dev, + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: +- *val = ad7298_get_ref_voltage(st); ++ if (st->ext_vin_max[chan->channel]) ++ *val = st->ext_vin_max[chan->channel]; ++ else ++ *val = ad7298_get_ref_voltage(st); + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: +@@ -304,8 +307,15 @@ static int ad7298_probe(struct spi_device *spi) + + st = iio_priv(indio_dev); + +- if (pdata && pdata->ext_ref) +- st->ext_ref = AD7298_EXTREF; ++ if (pdata) { ++ int i; ++ ++ if (pdata->ext_ref) ++ st->ext_ref = AD7298_EXTREF; ++ ++ for (i = 0; i < AD7298_MAX_CHAN; i++) ++ st->ext_vin_max[i] = pdata->ext_vin_max[i]; ++ } + + if (st->ext_ref) { + st->reg = regulator_get(&spi->dev, "vref"); +diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig +index fb8c239..1309fac 100644 +--- a/drivers/staging/iio/adc/Kconfig ++++ b/drivers/staging/iio/adc/Kconfig +@@ -137,4 +137,17 @@ config SPEAR_ADC + Say yes here to build support for the integrated ADC inside the + ST SPEAr SoC. Provides direct access via sysfs. + ++config MAX78M6610_LMU ++ tristate "Maxim 78M6610+LMU driver" ++ depends on SPI ++ select IIO_BUFFER ++ select IIO_TRIGGERED_BUFFER ++ help ++ Say yes here to build support for Maxim Energy Measurement Processor ++ for Load Monitoring Units. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called max78m6610_lmu. ++ ++ + endmenu +diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile +index d285596..2c4e7e1 100644 +--- a/drivers/staging/iio/adc/Makefile ++++ b/drivers/staging/iio/adc/Makefile +@@ -21,3 +21,4 @@ obj-$(CONFIG_AD7280) += ad7280a.o + obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o + obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o + obj-$(CONFIG_SPEAR_ADC) += spear_adc.o ++obj-$(CONFIG_MAX78M6610_LMU) += max78m6610_lmu.o +diff --git a/drivers/staging/iio/adc/max78m6610_lmu.c b/drivers/staging/iio/adc/max78m6610_lmu.c +new file mode 100644 +index 0000000..c427517 +--- /dev/null ++++ b/drivers/staging/iio/adc/max78m6610_lmu.c +@@ -0,0 +1,2235 @@ ++/* ++ * max78m6610+lmu SPI protocol driver ++ * ++ * Copyright(c) 2013 Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ * ++ * This SPI protocol driver is developed for the Maxim 78M6610+LMU (eADC). ++ * The driver is developed as a part of the Quark BSP where integrated into ++ * Quark Evaluation Boards Cross Hill Industrial-E. ++ * ++ * The Maxim 78M6610+LMU is an energy measurement processor (EMP) for ++ * load monitoring on single or spilt-phase AC loads. It supports varies ++ * interface configuration protocols through I/O pins. ++ * ++ * With 3 wire serial input/output interfaces provided by Quark SoC, ++ * the 78M6610+LMU can be connected directly as SPI slave device. ++ */ ++ ++#include <linux/cdev.h> ++#include <linux/delay.h> ++#include <linux/fs.h> ++#include <linux/gpio.h> ++#include <linux/iio/iio.h> ++#include <linux/iio/buffer.h> ++#include <linux/iio/types.h> ++#include <linux/iio/trigger.h> ++#include <linux/iio/trigger_consumer.h> ++#include <linux/iio/triggered_buffer.h> ++#include <linux/iio/sysfs.h> ++#include <linux/iio/events.h> ++#include <linux/platform_data/max78m6610_lmu.h> ++#include <linux/spi/spi.h> ++#include <linux/spi/spidev.h> ++#include <linux/version.h> ++ ++/* Calibration registers */ ++#define COMMAND 0x00 /* Command Register */ ++#define SAMPLES 0x03 /* High-rate samples per low-rate */ ++#define CALCYCS 0x04 /* Number of Calibration Cycles to Average */ ++#define PHASECOMP1 0x05 /* Phase compensation for S1 input */ ++#define PHASECOMP3 0x06 /* Phase compensation for S3 input */ ++#define S1_GAIN 0x07 /* Input S1 Gain Calibration */ ++#define S0_GAIN 0x08 /* Input S0 Gain Calibration */ ++#define S3_GAIN 0x09 /* Input S3 Gain Calibration */ ++#define S2_GAIN 0x0A /* Input S2 Gain Calibration */ ++#define S1_OFFSET 0x0D /* Input S1 Offset Calibration */ ++#define S0_OFFSET 0x0B /* Input S0 Offset Calibration */ ++#define S3_OFFSET 0x0E /* Input S3 Offset Calibration */ ++#define S2_OFFSET 0x0C /* Input S2 Offset Calibration */ ++#define VTARGET 0x12 /* Voltage Calibration Target */ ++#define ITARGET 0x39 /* Current Calibration Target */ ++ ++#define CALCMD_S0_GAIN 0xCA2030 /* Calibrate Voltage Gain for Input S0 */ ++#define CALCMD_S1_GAIN 0xCA0830 /* Calibrate Current Gain for Input S1 */ ++#define CALCMD_S2_GAIN 0xCA4030 /* Calibrate Voltage Gain for Input S2 */ ++#define CALCMD_S3_GAIN 0xCA1030 /* Calibrate Current Gain for Input S3 */ ++#define CALCMD_S0_OFFS 0xCA2210 /* Calibrate Voltage Offset for Input S0 */ ++#define CALCMD_S1_OFFS 0xCA0A10 /* Calibrate Current Offset for Input S1 */ ++#define CALCMD_S2_OFFS 0xCA4210 /* Calibrate Voltage Offset for Input S2 */ ++#define CALCMD_S3_OFFS 0xCA1210 /* Calibrate Current Offset for Input S3 */ ++#define FLASHSAVE_CMD 0xACC210 /* Save calibration coefficients to flash */ ++ ++/* Interrupt status registers */ ++#define MASK0 0x02 /* Status bit mask for MP0 pin */ ++#define STATUS 0x0F /* Status of device and alarms */ ++#define STATUS_RESET 0x11 /* Used to Reset Status bits */ ++#define STATUS_MASK_DRDY (1 << 23) ++#define STATUS_MASK_MMUPD (1 << 22) ++#define STATUS_MASK_VA_SAG (1 << 21) ++#define STATUS_MASK_VB_SAG (1 << 20) ++#define STATUS_MASK_SIGN_VA (1 << 19) ++#define STATUS_MASK_SIGN_VB (1 << 18) ++#define STATUS_MASK_OV_TEMP (1 << 17) ++#define STATUS_MASK_UN_TEMP (1 << 16) ++#define STATUS_MASK_OV_FREQ (1 << 15) ++#define STATUS_MASK_UN_FREQ (1 << 14) ++#define STATUS_MASK_OV_VRMSA (1 << 13) ++#define STATUS_MASK_UN_VRMSA (1 << 12) ++#define STATUS_MASK_OV_VRMSB (1 << 11) ++#define STATUS_MASK_UN_VRMSB (1 << 10) ++#define STATUS_MASK_VA_SURGE (1 << 9) ++#define STATUS_MASK_VB_SURGE (1 << 8) ++#define STATUS_MASK_OV_WATT1 (1 << 7) ++#define STATUS_MASK_OV_WATT2 (1 << 6) ++#define STATUS_MASK_OV_AMP1 (1 << 5) ++#define STATUS_MASK_OV_AMP2 (1 << 4) ++#define STATUS_MASK_XSTATE (1 << 3) ++#define STATUS_MASK_RELAY1 (1 << 2) ++#define STATUS_MASK_RELAY2 (1 << 1) ++#define STATUS_MASK_RESET (1) ++#define STATUS_MASK_STICKY (0x73FFF0) ++#define STATUS_MASK_IGNORE (0x00000F) ++ ++#define VSURG_VAL 0x13 /* Voltage surge alarm threshold */ ++#define VSAG_VAL 0x14 /* Voltage sag alarm threshold */ ++#define VRMS_MIN 0x15 /* Voltage lower alarm limit */ ++#define VRMS_MAX 0x16 /* Voltage upper alarm limit */ ++#define IRMS_MAX 0x27 /* Over Current alarm limit */ ++#define WATT_MAX 0x32 /* Power alarm limit */ ++ ++#define INSTAN_VA 0x1D /* instaneous Voltage for VA source */ ++#define INSTAN_IA 0x25 /* instaneous Current for IA source */ ++#define INSTAN_PA 0x2E /* instaneous Active Power for source A*/ ++#define INSTAN_PQA 0x30 /* instaneous Reactive Power for source A*/ ++#define VA_RMS 0x17 /* RMS voltage for VA source */ ++#define IA_RMS 0x1F /* RMS current for VA source */ ++#define WATT_A 0x28 /* Active Power for source A */ ++#define VAR_A 0x2C /* Reactive power for source A */ ++#define VA_A 0x2A /* Volt-Amperes for source A */ ++#define PFA 0x33 /* Source A Power Factor */ ++ ++#define INSTAN_VB 0x1E /* instaneous Voltage for VB source */ ++#define INSTAN_IB 0x26 /* instaneous Current for IB source */ ++#define INSTAN_PB 0x2F /* instaneous Active Power for source B*/ ++#define INSTAN_PQB 0x31 /* instaneous Voltage for VB source */ ++#define VB_RMS 0x18 /* RMS voltage for VB source */ ++#define IB_RMS 0x20 /* RMS current for VB source */ ++#define WATT_B 0x29 /* Active Power for source B */ ++#define VAR_B 0x2D /* Reactive power for source B */ ++#define VA_B 0x2B /* Volt-amperes for source B */ ++#define PFB 0x34 /* Source B Power Factor */ ++ ++/* Addr bit 6-7: ADDR6, ADDR7 */ ++#define SPI_CB_ADDR_MASK_7_6(x) (((x) & 0xC0) >> 6) ++/* Addr bit 0 - 5 */ ++#define SPI_TB_ADDR_MASK_5_0(x) ((x) & 0x3F) ++ ++#define SPI_CB_NBR_ACC 0x00 /* number register of accesss, limit to 1 */ ++#define SPI_CB_CMD 0x01 /* SPI command flag */ ++#define SPI_OP_READ 0x00 /* bit 1: Read/Write RD:0 W:1 */ ++#define SPI_OP_WRITE 0x02 /* bit 1: Read/Write RD:0 W:1 */ ++/* Positive / negative conversion */ ++#define SIGN_CONVERT 0xFFFFFFFFFFFFFFFF ++#define DATA_BIT_MASK 0x00FFFFFF ++#define SIGN_BIT_NUM 23 ++#define SPI_MSG_LEN 5 ++#define RX_OFFSET 1 ++#define SPI_BBUFFER_LEN 4096 ++/* All registers on the device are 24-bit */ ++#define REG_WIDTH 24 ++#define SAMPLE_INTERVAL_USEC 250 /* High-rate sample interval (microseconds) */ ++#define RESET_DELAY_MSEC 100 ++#define INTR_GPIO 2 ++ ++/* SPI message Control byte */ ++#define SPI_CB(x) ((SPI_CB_NBR_ACC << 4)\ ++ | (SPI_CB_ADDR_MASK_7_6(x) << 2)\ ++ | SPI_CB_CMD) ++/* SPI message Transaction byte */ ++#define SPI_TB_READ(x) ((SPI_TB_ADDR_MASK_5_0(x) << 2)\ ++ | SPI_OP_READ) ++#define SPI_TB_WRITE(x) ((SPI_TB_ADDR_MASK_5_0(x) << 2)\ ++ | SPI_OP_WRITE) ++ ++/** ++ * max78m6610_lmu_channels structure maps eADC measurement features to ++ * IIO channels on the IIO sysfs user interface ++ */ ++static const struct iio_chan_spec max78m6610_lmu_channels[] = { ++ /* IIO Channels for source A */ ++ { ++ .type = IIO_VOLTAGE, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "inst", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = INSTAN_VA, ++ .scan_index = 0, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_CURRENT, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "rms", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = IA_RMS, ++ .scan_index = 1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_POWER, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "inst_act", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = INSTAN_PA, ++ .scan_index = 2, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_POWER, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "inst_react", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = INSTAN_PQA, ++ .scan_index = 3, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_POWER, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "avg_act", ++ /* IIO_CHAN_INFO_AVERAGE_RAW is not used here, ++ * this average value is provide by HW register, ++ */ ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = WATT_A, ++ .scan_index = 4, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_POWER, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "avg_react", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = VAR_A, ++ .scan_index = 5, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_POWER, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "apparent", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = VA_A, ++ .scan_index = 6, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_POWER, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "factor", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = PFA, ++ .scan_index = 7, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, /* data type S.22 */ ++ .storagebits = 32, ++ .shift = 22, ++ }, ++ }, ++ { ++ .type = IIO_VOLTAGE, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "rms", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = VA_RMS, ++ .scan_index = 8, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ ++ /* IIO channels for source B */ ++ { ++ .type = IIO_VOLTAGE, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "inst", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = INSTAN_VB, ++ .scan_index = 9, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_CURRENT, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "rms", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = IB_RMS, ++ .scan_index = 10, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_POWER, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "inst_act", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = INSTAN_PB, ++ .scan_index = 11, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_POWER, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "inst_react", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = INSTAN_PQB, ++ .scan_index = 12, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_POWER, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "avg_act", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = WATT_B, ++ .scan_index = 13, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_POWER, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "avg_react", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = VAR_B, ++ .scan_index = 14, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_POWER, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "apparent", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = VA_B, ++ .scan_index = 15, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_POWER, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "factor", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = PFB, ++ .scan_index = 16, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 22, /* data type S.22 */ ++ }, ++ }, ++ { ++ .type = IIO_VOLTAGE, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "rms", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = VB_RMS, ++ .scan_index = 17, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_CURRENT, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "inst", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = INSTAN_IA, ++ .scan_index = 18, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_CURRENT, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "inst", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | ++ IIO_CHAN_INFO_SCALE_SHARED_BIT, ++ .address = INSTAN_IB, ++ .scan_index = 19, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ }, ++ { ++ .type = IIO_CURRENT, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "phasecomp", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = PHASECOMP1, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 21, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_CURRENT, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "phasecomp", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = PHASECOMP3, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 21, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_VOLTAGE, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "calib_target_rms", ++ .info_mask = IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_RAW), ++ .address = VTARGET, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_CURRENT, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "calib_target_rms", ++ .info_mask = IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_RAW), ++ .address = ITARGET, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_VOLTAGE, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "calib_gain", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = S0_GAIN, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 21, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_VOLTAGE, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "calib_gain", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = S2_GAIN, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 21, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_CURRENT, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "calib_gain", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = S1_GAIN, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 21, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_CURRENT, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "calib_gain", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = S3_GAIN, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 21, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_VOLTAGE, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "calib_offset", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = S0_OFFSET, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_VOLTAGE, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "calib_offset", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = S2_OFFSET, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_CURRENT, ++ .indexed = 1, ++ .channel = 0, ++ .extend_name = "calib_offset", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = S1_OFFSET, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_CURRENT, ++ .indexed = 1, ++ .channel = 1, ++ .extend_name = "calib_offset", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = S3_OFFSET, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_VOLTAGE, ++ .indexed = 0, ++ .channel = 0, ++ .extend_name = "surge_threshold", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = VSURG_VAL, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_VOLTAGE, ++ .indexed = 0, ++ .channel = 0, ++ .extend_name = "sag_threshold", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = VSAG_VAL, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_VOLTAGE, ++ .indexed = 0, ++ .channel = 0, ++ .extend_name = "rms_min_threshold", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = VRMS_MIN, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_VOLTAGE, ++ .indexed = 0, ++ .channel = 0, ++ .extend_name = "rms_max_threshold", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = VRMS_MAX, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_CURRENT, ++ .indexed = 0, ++ .channel = 0, ++ .extend_name = "rms_max_threshold", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = IRMS_MAX, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ .output = 1, ++ }, ++ { ++ .type = IIO_POWER, ++ .indexed = 0, ++ .channel = 0, ++ .extend_name = "active_max_threshold", ++ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, ++ .address = WATT_MAX, ++ .scan_index = -1, ++ .scan_type = { ++ .sign = 's', ++ .realbits = 32, ++ .storagebits = 32, ++ .shift = 23, ++ }, ++ .output = 1, ++ }, ++ ++ IIO_CHAN_SOFT_TIMESTAMP(20), ++}; ++ ++/* max number of iio channels */ ++#define MAX_CHAN_NUM ARRAY_SIZE(max78m6610_lmu_channels) ++ ++/* eADC state structure */ ++struct max78m6610_lmu_state { ++ struct spi_device *spi; ++ struct iio_dev_attr *iio_attr; ++ struct iio_trigger *trig; ++ struct spi_transfer ring_xfer[MAX_CHAN_NUM]; ++ struct spi_transfer scan_single_xfer; ++ struct spi_message ring_msg; ++ struct spi_message scan_single_msg; ++ ++ u8 tx_buf[SPI_MSG_LEN * MAX_CHAN_NUM]; ++ u8 rx_buf[SPI_MSG_LEN * MAX_CHAN_NUM + sizeof(s64)]; ++ ++ int reset_gpio; ++ ++ /* Char dev to provide ioctl interface for f/w upgrade ++ * or low-level register access */ ++ struct cdev cdev; ++ dev_t cdev_no; ++ struct class *cl; ++ u8 *bbuffer; ++}; ++ ++/** ++ * ret_fraction_log2 ++ * ++ * @param val: pointer to val ++ * @param val2: pointer to val2 ++ * @return: no returns ++ * ++ * this function to re-implement of IIO_VAL_FRACTIONAL_LOG2 marco in IIO ++ * because of the do_div() function is not correctly handle the negative ++ * input value. ++ */ ++static void ret_fraction_log2(int *val, int *val2) ++{ ++ s64 tmp; ++ ++ tmp = *val; ++ ++ if (*val < 0) { ++ /* the do_div function will return trash if the value ++ * of input is negative. We need to treat tmp as ++ * a positive number for calculation. ++ * 1. XOR tmp with 0xFFFFFFFFFFFFFFFF. ++ * 2. add on the differential ++ */ ++ tmp = (tmp ^ SIGN_CONVERT) + 1; ++ tmp = tmp * 1000000000LL >> (*val2); ++ *val2 = do_div(tmp, 1000000000LL); ++ *val = tmp; ++ /* the IIO_VAL_INT_PLUS_NANO marco is used in the later stage ++ * to return the proper format of output. ++ * The IIO use the value of val2 to determinate the sign ++ * of the output. ++ * Convert val2 from positive to negative to fool IIO to ++ * display the ++ * correct output format. ++ */ ++ *val2 = *val2 ^ SIGN_CONVERT; ++ } else { ++ ++ tmp = tmp * 1000000000LL >> (*val2); ++ *val2 = do_div(tmp, 1000000000LL); ++ *val = tmp; ++ } ++} ++ ++/** ++ * intplusnano_to_regval ++ * ++ * @param val_int: Integer part of floating point value ++ * @param val_nano: Fractional part of floating point value ++ * @param frac_bits: The number of fractional bits to produce for this register ++ * @param regval: The resulting 24-bit signed fixed-point register value ++ * @return: 0 on success, non-zero on error ++ * ++ * As the kernel doesn't allow floating point numbers, IIO ++ * will split them into separate integer and fractional parts. This function ++ * then converts them into fixed-point signed register values for the MAX78M6610 ++ */ ++static int intplusnano_to_regval(int val_int, int val_nano, ++ int fract_bits, u32 *regval) ++{ ++ int i, max_int, negative = 0; ++ ++ /* Maximum integer value must be 24 bits minus sign and fraction_bits */ ++ max_int = 1 << (REG_WIDTH - fract_bits - 1); ++ ++ if ((val_int >= max_int) || ++ (val_int < -max_int) || ++ ((val_int == -max_int) && (val_nano != 0))) { ++ pr_err("Input value exceeds maximum allowed range\n"); ++ return -EINVAL; ++ } ++ ++ *regval = abs(val_int) << fract_bits; ++ ++ /* Set the sign-bit, if input is negative */ ++ if ((val_int < 0) || (val_nano < 0)) ++ negative = 1; ++ ++ val_nano = abs(val_nano); ++ ++ /* Divide the fractional part down by negative powers of 2*/ ++ for (i = fract_bits-1; i >= 0 && val_nano; i--) { ++ val_nano = val_nano << 1; ++ if (val_nano >= 1000000000LL) { ++ *regval |= (1 << i); ++ val_nano -= 1000000000LL; ++ } ++ } ++ ++ /* Get 2s complement of number if negative */ ++ if (negative) ++ *regval = (~(*regval) + 1) & ((1 << REG_WIDTH) - 1); ++ ++ return 0; ++} ++ ++/** ++ * __max78m6610_lmu_spi_reg_read ++ * ++ * @param st: Driver state data ++ * @param regaddr: The register word address to read ++ * @param regval: The 24-bit register value obtained by the read operation ++ * @return: 0 on success, non-zero on error ++ * ++ * Issues a SPI transaction to read a single register on the device. ++ * Performs endian byte swap before returning the register data. ++ */ ++static inline ++int __max78m6610_lmu_spi_reg_read(struct max78m6610_lmu_state *st, ++ u8 regaddr, ++ u32 *regval) ++{ ++ int ret; ++ ++ st->tx_buf[0] = SPI_CB(regaddr); ++ st->tx_buf[1] = SPI_TB_READ(regaddr); ++ ret = spi_sync(st->spi, &st->scan_single_msg); ++ if (ret) { ++ pr_err("spi_sync return error: %d\n", ret); ++ return -EIO; ++ } ++ ++ *regval = (st->rx_buf[2] << 16) | (st->rx_buf[3] << 8) | st->rx_buf[4]; ++ ++ return 0; ++} ++ ++/** ++ * __max78m6610_lmu_spi_reg_write ++ * ++ * @param st: Driver state data ++ * @param regaddr: The register word address to write ++ * @param regval: The 24-bit value to write to the register ++ * @return: 0 on success, non-zero on error ++ * ++ * Issues a SPI transaction to write a single register on the device. ++ * Performs endian byte swap before writing the register data. ++ */ ++static inline ++int __max78m6610_lmu_spi_reg_write(struct max78m6610_lmu_state *st, ++ u8 regaddr, ++ u32 regval) ++{ ++ int ret; ++ ++ st->tx_buf[0] = SPI_CB(regaddr); ++ st->tx_buf[1] = SPI_TB_WRITE(regaddr); ++ st->tx_buf[2] = regval >> 16; ++ st->tx_buf[3] = regval >> 8; ++ st->tx_buf[4] = regval & 0xFF; ++ ++ ret = spi_sync(st->spi, &st->scan_single_msg); ++ if (ret) { ++ pr_err("spi_sync return non-zero value\n"); ++ ret = -EIO; ++ } ++ ++ return 0; ++} ++ ++/** ++ * max78m6610_lmu_update_scan_mode ++ * ++ * @param indio_dev: iio_dev pointer. ++ * @param active_scan_mask: pointer to scan mask. ++ * @return 0 on success or standard errnos on failure ++ * ++ * setup the spi transfer buffer for the actived scan mask ++ **/ ++static int max78m6610_lmu_update_scan_mode(struct iio_dev *indio_dev, ++ const unsigned long *active_scan_mask) ++{ ++ struct max78m6610_lmu_state *st = iio_priv(indio_dev); ++ int i, tx = 0, k = 0; ++ unsigned addr; ++ ++ spi_message_init(&st->ring_msg); ++ ++ /* scan through all the channels */ ++ for (i = 0; i < MAX_CHAN_NUM; i++) { ++ /* we build the the spi message here that support ++ * multiple register access request on the selected channel */ ++ if (test_bit(i, active_scan_mask)) { ++ addr = max78m6610_lmu_channels[i].address; ++ /* first two bytes are the contol bytes */ ++ st->tx_buf[tx] = SPI_CB(addr); ++ st->tx_buf[tx+1] = SPI_TB_READ(addr); ++ ++ st->ring_xfer[k].cs_change = 0; ++ st->ring_xfer[k].tx_buf = &st->tx_buf[tx]; ++ /* rx buffer */ ++ /* All the HW registers in the HW are designed as 24 bit ++ * size, so we skip the first byte in the rx_buf when ++ * constructing the ring_xfer. ++ */ ++ st->ring_xfer[k].rx_buf = &st->rx_buf[tx]; ++ st->ring_xfer[k].len = SPI_MSG_LEN; ++ st->ring_xfer[k].cs_change = 1; ++ ++ spi_message_add_tail(&st->ring_xfer[k], ++ &st->ring_msg); ++ /* update in bytes number */ ++ tx += SPI_MSG_LEN; ++ k++; ++ } ++ } ++ ++ return 0; ++} ++ ++/** ++ * max78m6610_lmu_trigger_handle ++ * ++ * @param irq: irq indicator ++ * @parma p: iio pull funciton pointer ++ * @return IRQ_HANDLED ++ * ++ * bh handler of trigger launched polling to ring buffer ++ * ++ **/ ++static irqreturn_t max78m6610_lmu_trigger_handler(int irq, void *p) ++{ ++ struct iio_poll_func *pf = p; ++ struct iio_dev *indio_dev = pf->indio_dev; ++ struct max78m6610_lmu_state *st = iio_priv(indio_dev); ++ ++ u32 scan_buf[((sizeof(u32)*MAX_CHAN_NUM)+sizeof(s64))/sizeof(u32)]; ++ s64 time_ns = 0; ++ int b_sent; ++ int i = 0, rx_bit = 0; ++ int scan_count; ++ ++ b_sent = spi_sync(st->spi, &st->ring_msg); ++ if (b_sent) { ++ pr_err("spi_sync failed.\n"); ++ goto done; ++ } ++ ++ scan_count = bitmap_weight(indio_dev->active_scan_mask, ++ indio_dev->masklength); ++ ++ if (indio_dev->scan_timestamp) { ++ time_ns = iio_get_time_ns(); ++ memcpy((u8 *)scan_buf + indio_dev->scan_bytes - sizeof(s64), ++ &time_ns, sizeof(time_ns)); ++ } ++ ++ for (i = 0; i < scan_count; i++) { ++ u32 *rx_buf_32 = NULL; ++ rx_bit = i*SPI_MSG_LEN + RX_OFFSET; ++ rx_buf_32 = (u32 *)&(st->rx_buf[rx_bit]); ++ *rx_buf_32 = be32_to_cpu(*rx_buf_32) & DATA_BIT_MASK; ++ scan_buf[i] = sign_extend32(*rx_buf_32, ++ SIGN_BIT_NUM); ++ } ++ ++ iio_push_to_buffers(indio_dev, (u8 *)scan_buf); ++done: ++ iio_trigger_notify_done(indio_dev->trig); ++ ++ return IRQ_HANDLED; ++} ++ ++/** ++ * max78m6610_lmu_read_raw ++ * ++ * @param indio_dev: iio_dev pointer ++ * @param chan: pointer to iio channel spec struct ++ * @param val: return value pointer ++ * @param val2: return value 2 ponter ++ * @parma m: read mask ++ * @return: IIO value type ++ * ++ * This function will be invoked when request a value form the device. ++ * Read mask specifies which value, return value will specify the type of ++ * value returned from device, val and val2 will contains the elements ++ * making up the return value. ++ */ ++static int max78m6610_lmu_read_raw(struct iio_dev *indio_dev, ++ struct iio_chan_spec const *chan, ++ int *val, ++ int *val2, ++ long m) ++{ ++ int ret; ++ u32 regval; ++ struct max78m6610_lmu_state *st = iio_priv(indio_dev); ++ ++ switch (m) { ++ ++ case IIO_CHAN_INFO_RAW: ++ mutex_lock(&indio_dev->mlock); ++ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { ++ mutex_unlock(&indio_dev->mlock); ++ return -EBUSY; ++ } ++ ++ ret = __max78m6610_lmu_spi_reg_read(st, chan->address, ®val); ++ mutex_unlock(&indio_dev->mlock); ++ if (ret) ++ return ret; ++ ++ *val = sign_extend32(regval, SIGN_BIT_NUM); ++ *val2 = chan->scan_type.shift; ++ ++ ret_fraction_log2(val, val2); ++ return IIO_VAL_INT_PLUS_NANO; ++ ++ /* the full scale units : -1.0 to 1-LSB (0x7FFFFF) ++ * As an example, if 230V-peak at the input to the voltage ++ * divider gives 250mV-peak at the chip input, one would get a ++ * full scale register reading of 1 - LSB (0x7FFFFF) for ++ * instaneous voltage. ++ * Similarly, if 30Apk at the sensor input provides 250mV-peak ++ * to the chip input, a full scale register value of 1 - LSB ++ * (0x7FFFFF) for instanteous current would correspond to ++ * 30 amps. ++ * Full scale watts correspond to the result of full scale ++ * current and voltage so, in this example, full scale watts ++ * is 230 x 30 or 6900 watts. ++ */ ++ ++ case IIO_CHAN_INFO_SCALE: ++ switch (chan->type) { ++ case IIO_CURRENT: ++ *val = 250; /* unit mV */ ++ return IIO_VAL_INT; ++ ++ case IIO_VOLTAGE: ++ *val = 250; /* unit: mV */ ++ return IIO_VAL_INT; ++ ++ case IIO_POWER: ++ *val = 250*250; /* uV */ ++ return IIO_VAL_INT; ++ ++ default: ++ return -EINVAL; ++ } ++ ++ } ++ return -EINVAL; ++} ++ ++/** ++ * max78m6610_lmu_write_raw ++ * ++ * @param indio_dev: iio_dev pointer ++ * @param chan: pointer to iio channel spec struct ++ * @param val: input value pointer ++ * @param val2: input value 2 ponter ++ * @parma m: write mask indicating IIO info type ++ * @return: status indicating success (zero) or fail (non-zero) ++ * ++ * This function will be invoked on a request to write a value to the device. ++ * Write mask specifies an IIO value type, val and val2 contain the integer and ++ * fractional elements of the floating point input value (INT+NANO). ++ */ ++static int max78m6610_lmu_write_raw(struct iio_dev *indio_dev, ++ struct iio_chan_spec const *chan, ++ int val, ++ int val2, ++ long m) ++{ ++ int ret; ++ u32 regval; ++ struct max78m6610_lmu_state *st = iio_priv(indio_dev); ++ ++ mutex_lock(&indio_dev->mlock); ++ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { ++ ret = -EBUSY; ++ goto exit_unlock; ++ } ++ ++ switch (m) { ++ ++ case IIO_CHAN_INFO_RAW: ++ ret = intplusnano_to_regval(val, val2, ++ chan->scan_type.shift, ®val); ++ if (ret) ++ goto exit_unlock; ++ ++ ret = __max78m6610_lmu_spi_reg_write(st, chan->address, regval); ++ break; ++ ++ default: ++ pr_err("Invalid channel selected for writing\n"); ++ ret = -EINVAL; ++ goto exit_unlock; ++ } ++ ++exit_unlock: ++ mutex_unlock(&indio_dev->mlock); ++ ++ return ret; ++} ++ ++/** ++ * max78m6610_lmu_write_raw_get_fmt ++ * ++ * @param indio_dev: iio_dev pointer ++ * @param chan: pointer to iio channel spec struct ++ * @param mask: specifies which value to be written ++ * @return: the format specifier for the channel value to be written ++ * ++ * IIO will query the expected format of the input value, and will then ++ * interpret and format it correctly before passing it to ++ * max78m6610_lmu_write_raw(). In all cases, we expect floating point ++ * numbers as input, which IIO will convert into integer and fractional parts ++ */ ++static int max78m6610_lmu_write_raw_get_fmt(struct iio_dev *indio_dev, ++ struct iio_chan_spec const *chan, ++ long mask) ++{ ++ return IIO_VAL_INT_PLUS_NANO; ++} ++ ++ ++/** ++ * max78m6610_lmu_reg_access ++ * ++ * @param indio_dev: iio_dev pointer ++ * @param reg: register address ++ * @param writeval: register value to write (ignored if readval is set) ++ * @parma readval: pointer to return register read result (set NULL for write) ++ * @return: status indicating success (zero) or fail (non-zero) ++ * ++ * This function allows direct read/write access MAX78M6610+LMU registers ++ * for debug only ++ */ ++static int max78m6610_lmu_reg_access(struct iio_dev *indio_dev, ++ unsigned reg, unsigned writeval, ++ unsigned *readval) ++{ ++ struct max78m6610_lmu_state *st = iio_priv(indio_dev); ++ int ret = 0; ++ ++ mutex_lock(&indio_dev->mlock); ++ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { ++ ret = -EBUSY; ++ goto exit_unlock; ++ } ++ ++ if (readval) ++ ret = __max78m6610_lmu_spi_reg_read(st, reg, readval); ++ else ++ ret = __max78m6610_lmu_spi_reg_write(st, reg, writeval); ++ ++exit_unlock: ++ mutex_unlock(&indio_dev->mlock); ++ ++ return ret; ++} ++ ++/** ++ * max78m6610_lmu_reset ++ * ++ * @param indio_dev: iio_dev pointer ++ * ++ * Executes a reset of the MAX78M6610+LMU by briefly asserting the ++ * hardware reset signal for the device. Volatile register values ++ * will revert to power-on default values. ++ */ ++static int max78m6610_lmu_reset(struct iio_dev *indio_dev) ++{ ++ struct max78m6610_lmu_state *st = iio_priv(indio_dev); ++ int ret = 0; ++ int gpio = st->reset_gpio; ++ ++ struct gpio device_reset_gpio = { ++ gpio, ++ GPIOF_OUT_INIT_HIGH, ++ "max78m6610_lmu_reset" ++ }; ++ ++ if (gpio < 0) { ++ pr_err("Reset GPIO has not been configured\n"); ++ return -ENXIO; ++ } ++ ++ mutex_lock(&indio_dev->mlock); ++ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { ++ ret = -EBUSY; ++ goto exit_unlock; ++ } ++ ++ ret = gpio_request_array(&device_reset_gpio, 1); ++ if (ret) { ++ pr_err("%s: Failed to allocate Device Reset GPIO pin\n", ++ __func__); ++ goto exit_unlock; ++ } ++ gpio_set_value(gpio, 0); ++ msleep(RESET_DELAY_MSEC); ++ gpio_set_value(gpio, 1); ++ msleep(RESET_DELAY_MSEC); ++ ++ gpio_free_array(&device_reset_gpio, 1); ++ ++exit_unlock: ++ mutex_unlock(&indio_dev->mlock); ++ ++ return ret; ++} ++ ++/** ++ * max78m6610_lmu_write_reset ++ * ++ * @param dev: device descriptor associated with sysfs attribute node ++ * @param attr: device sysfs attribute descriptor ++ * @param buf: data written by user to the attribute node ++ * @param len: length in bytes of data written by user ++ * ++ * This handles a write to this sysfs node from user-space, and invokes a ++ * reset of the MAX78M6610+LMU if an appropriate value is written. ++ * Valid input character values are 1, y and Y ++ */ ++static ssize_t max78m6610_lmu_write_reset(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct iio_dev *indio_dev = dev_to_iio_dev(dev); ++ int ret = 0; ++ ++ if (len < 1) ++ return -1; ++ switch (buf[0]) { ++ case '1': ++ case 'y': ++ case 'Y': ++ ret = max78m6610_lmu_reset(indio_dev); ++ return ret ? ret : len; ++ } ++ return -1; ++} ++ ++/** ++ * max78m6610_lmu_calib_cmd ++ * ++ * @param indio_dev: iio_dev pointer ++ * @param calib_command: 24-bit calibration command value ++ * ++ * Executes a specified calibration command on the MAX78M6610+LMU ++ * The calib_command input value is written directly to the COMMAND ++ * register on the device, to invoke a selected automatic calibration ++ * routine. The driver waits until the calibration completes, and then ++ * checks the status (depending on the specific command) ++ */ ++static int max78m6610_lmu_calib_cmd(struct iio_dev *indio_dev, ++ u32 calib_command) ++{ ++ struct max78m6610_lmu_state *st = iio_priv(indio_dev); ++ u32 samples, calcycs; ++ unsigned delay_ms; ++ int max_retries = 5; ++ int ret = 0; ++ ++ mutex_lock(&indio_dev->mlock); ++ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { ++ ret = -EBUSY; ++ goto exit_unlock; ++ } ++ ++ /* Calculate the delay required for calibration to complete */ ++ ret = __max78m6610_lmu_spi_reg_read(st, SAMPLES, &samples); ++ if (ret) ++ goto exit_unlock; ++ ret = __max78m6610_lmu_spi_reg_read(st, CALCYCS, &calcycs); ++ if (ret) ++ goto exit_unlock; ++ delay_ms = (samples * calcycs * SAMPLE_INTERVAL_USEC)/1000; ++ ++ ret = __max78m6610_lmu_spi_reg_write(st, COMMAND, calib_command); ++ if (ret) ++ goto exit_unlock; ++ ++ do { ++ /* Wait for the calibration to complete */ ++ mdelay(delay_ms); ++ ++ ret = __max78m6610_lmu_spi_reg_read(st, COMMAND, ++ &calib_command); ++ if (ret) ++ goto exit_unlock; ++ } while ((calib_command & 0xFF0000) && (max_retries--)); ++ ++ if (max_retries <= 0) { ++ pr_err("Timed out waiting for calibration to complete\n"); ++ ret = -EIO; ++ goto exit_unlock; ++ } ++ ++ /* Gain calibration commands (bit 9 unset) can be checked for failure */ ++ if ((!(calib_command & 0x000200)) && (calib_command & 0x007800)) { ++ pr_err("Calibration failed: COMMAND=0x%06X\n", calib_command); ++ ret = -EFAULT; ++ } ++ ++exit_unlock: ++ mutex_unlock(&indio_dev->mlock); ++ ++ return ret; ++} ++ ++/** ++ * max78m6610_lmu_write_calib ++ * ++ * @param dev: device descriptor associated with sysfs attribute node ++ * @param attr: device sysfs attribute descriptor ++ * @param buf: data written by user to the attribute node ++ * @param len: length in bytes of data written by user ++ * ++ * This handles a write to this sysfs node from user-space, and invokes a ++ * calibration command on the MAX78M6610+LMU if an appropriate value is written. ++ * Valid input character values are 1, y and Y. ++ * The handler is re-used for multiple calibration commands, so the command ++ * value is passed transparently via the attribute address field ++ */ ++static ssize_t max78m6610_lmu_write_calib(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); ++ struct iio_dev *indio_dev = dev_to_iio_dev(dev); ++ int ret = 0; ++ ++ if (len < 1) ++ return -1; ++ switch (buf[0]) { ++ case '1': ++ case 'y': ++ case 'Y': ++ ret = max78m6610_lmu_calib_cmd(indio_dev, this_attr->address); ++ return ret ? ret : len; ++ } ++ return -1; ++} ++ ++/** ++ * max78m6610_lmu_flash_save_cmd ++ * ++ * @param indio_dev: iio_dev pointer ++ * ++ * Executes a flash-save command on the MAX78M6610+LMU. ++ * This saves all current volatile register values to flash on the device, ++ * making them persistent across device resets or power cycles. ++ */ ++static int max78m6610_lmu_flash_save_cmd(struct iio_dev *indio_dev) ++{ ++ struct max78m6610_lmu_state *st = iio_priv(indio_dev); ++ int ret = 0; ++ ++ mutex_lock(&indio_dev->mlock); ++ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { ++ ret = -EBUSY; ++ goto exit_unlock; ++ } ++ ++ ret = __max78m6610_lmu_spi_reg_write(st, COMMAND, FLASHSAVE_CMD); ++ ++exit_unlock: ++ mutex_unlock(&indio_dev->mlock); ++ ++ return ret; ++} ++ ++/** ++ * max78m6610_lmu_write_flash ++ * ++ * @param dev: device descriptor associated with sysfs attribute node ++ * @param attr: device sysfs attribute descriptor ++ * @param buf: data written by user to the attribute node ++ * @param len: length in bytes of data written by user ++ * ++ * This handles a write to this sysfs node from user-space, and invokes a ++ * flash-save command on the MAX78M6610+LMU if an appropriate value is written. ++ * Valid input character values are 1, y and Y. ++ */ ++static ssize_t max78m6610_lmu_write_flash(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct iio_dev *indio_dev = dev_to_iio_dev(dev); ++ int ret = 0; ++ ++ if (len < 1) ++ return -1; ++ switch (buf[0]) { ++ case '1': ++ case 'y': ++ case 'Y': ++ ret = max78m6610_lmu_flash_save_cmd(indio_dev); ++ return ret ? ret : len; ++ } ++ return -1; ++} ++ ++/** ++ * max78m6610_lmu_flash_save_cmd ++ * ++ * @param indio_dev: iio_dev pointer ++ * ++ * Executes a read of the STATUS register on the MAX78M6610+LMU. ++ * Event status bits are checked, and event notifications are raised for ++ * user-space applications if any events are asserted. Event status bits are ++ * sticky and are cleared by setting the corresponding bit in the STATUS_RESET ++ * register to allow further occurances of the same event to be detected ++ */ ++static int max78m6610_lmu_status_scan(struct iio_dev *indio_dev) ++{ ++ struct max78m6610_lmu_state *st = iio_priv(indio_dev); ++ unsigned status; ++ int ret; ++ u64 timestamp_ns = iio_get_time_ns(); ++ ++ mutex_lock(&indio_dev->mlock); ++ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { ++ ret = -EBUSY; ++ goto exit_unlock; ++ } ++ ++ ret = __max78m6610_lmu_spi_reg_read(st, STATUS, &status); ++ if (ret) { ++ pr_err("Failed to read STATUS register\n"); ++ goto exit_unlock; ++ } ++ ++ status &= ~STATUS_MASK_IGNORE; ++ ++ /* Nothing more to do if no interesting status bits are set */ ++ if (!status) ++ goto exit_unlock; ++ ++ /* Not all of the event types used below are ideal, but there is a ++ * limited set available and we want to use different event types for ++ * the different events (e.g sag vs. min-threshold) to allow user ++ * applications to distinguish them ++ */ ++ if (status & STATUS_MASK_VA_SAG) { ++ iio_push_event(indio_dev, ++ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, ++ IIO_EV_TYPE_MAG, ++ IIO_EV_DIR_FALLING), ++ timestamp_ns); ++ } ++ if (status & STATUS_MASK_VB_SAG) { ++ iio_push_event(indio_dev, ++ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, ++ IIO_EV_TYPE_MAG, ++ IIO_EV_DIR_FALLING), ++ timestamp_ns); ++ } ++ if (status & STATUS_MASK_OV_VRMSA) { ++ iio_push_event(indio_dev, ++ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, ++ IIO_EV_TYPE_THRESH, ++ IIO_EV_DIR_RISING), ++ timestamp_ns); ++ } ++ if (status & STATUS_MASK_UN_VRMSA) { ++ iio_push_event(indio_dev, ++ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, ++ IIO_EV_TYPE_THRESH, ++ IIO_EV_DIR_FALLING), ++ timestamp_ns); ++ } ++ if (status & STATUS_MASK_OV_VRMSB) { ++ iio_push_event(indio_dev, ++ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, ++ IIO_EV_TYPE_THRESH, ++ IIO_EV_DIR_RISING), ++ timestamp_ns); ++ } ++ if (status & STATUS_MASK_UN_VRMSB) { ++ iio_push_event(indio_dev, ++ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, ++ IIO_EV_TYPE_THRESH, ++ IIO_EV_DIR_FALLING), ++ timestamp_ns); ++ } ++ if (status & STATUS_MASK_VA_SURGE) { ++ iio_push_event(indio_dev, ++ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 0, ++ IIO_EV_TYPE_MAG, ++ IIO_EV_DIR_RISING), ++ timestamp_ns); ++ } ++ if (status & STATUS_MASK_VB_SURGE) { ++ iio_push_event(indio_dev, ++ IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, 1, ++ IIO_EV_TYPE_MAG, ++ IIO_EV_DIR_RISING), ++ timestamp_ns); ++ } ++ if (status & STATUS_MASK_OV_WATT1) { ++ iio_push_event(indio_dev, ++ IIO_UNMOD_EVENT_CODE(IIO_POWER, 0, ++ IIO_EV_TYPE_THRESH, ++ IIO_EV_DIR_RISING), ++ timestamp_ns); ++ } ++ if (status & STATUS_MASK_OV_WATT2) { ++ iio_push_event(indio_dev, ++ IIO_UNMOD_EVENT_CODE(IIO_POWER, 1, ++ IIO_EV_TYPE_THRESH, ++ IIO_EV_DIR_RISING), ++ timestamp_ns); ++ } ++ if (status & STATUS_MASK_OV_AMP1) { ++ iio_push_event(indio_dev, ++ IIO_UNMOD_EVENT_CODE(IIO_CURRENT, 0, ++ IIO_EV_TYPE_THRESH, ++ IIO_EV_DIR_RISING), ++ timestamp_ns); ++ } ++ if (status & STATUS_MASK_OV_AMP2) { ++ iio_push_event(indio_dev, ++ IIO_UNMOD_EVENT_CODE(IIO_CURRENT, 1, ++ IIO_EV_TYPE_THRESH, ++ IIO_EV_DIR_RISING), ++ timestamp_ns); ++ } ++ ++ ret = __max78m6610_lmu_spi_reg_write(st, STATUS_RESET, ++ status & STATUS_MASK_STICKY); ++ if (ret) { ++ pr_err("Failed to write STATUS_RESET register\n"); ++ goto exit_unlock; ++ } ++ ++exit_unlock: ++ mutex_unlock(&indio_dev->mlock); ++ ++ return ret; ++} ++ ++/** ++ * max78m6610_lmu_write_status_scan ++ * ++ * @param dev: device descriptor associated with sysfs attribute node ++ * @param attr: device sysfs attribute descriptor ++ * @param buf: data written by user to the attribute node ++ * @param len: length in bytes of data written by user ++ * ++ * This handles a write to this sysfs node from user-space, and invokes a ++ * read of the status register on the MAX78M6610+LMU if an appropriate value ++ * is written. Valid input character values are 1, y and Y ++ */ ++static ssize_t max78m6610_lmu_write_status_scan(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct iio_dev *indio_dev = dev_to_iio_dev(dev); ++ int ret = 0; ++ ++ if (len < 1) ++ return -1; ++ switch (buf[0]) { ++ case '1': ++ case 'y': ++ case 'Y': ++ ret = max78m6610_lmu_status_scan(indio_dev); ++ return ret ? ret : len; ++ } ++ return -1; ++} ++ ++/** ++ * max78m6610_lmu_write_int ++ * ++ * @param dev: device descriptor associated with sysfs attribute node ++ * @param attr: device sysfs attribute descriptor ++ * @param buf: data written by user to the attribute node ++ * @param len: length in bytes of data written by user ++ * ++ * This is a generic handler to write integer values to any integer registers ++ * which are exposed as device sysfs attributes on the user interface. ++ * The attribute address field specifies which register to write. ++ */ ++ ++static IIO_DEVICE_ATTR(do_reset, S_IWUSR, NULL, ++ max78m6610_lmu_write_reset, 0); ++static IIO_DEVICE_ATTR(do_voltage0_gain_calib, S_IWUSR, NULL, ++ max78m6610_lmu_write_calib, CALCMD_S0_GAIN); ++static IIO_DEVICE_ATTR(do_current0_gain_calib, S_IWUSR, NULL, ++ max78m6610_lmu_write_calib, CALCMD_S1_GAIN); ++static IIO_DEVICE_ATTR(do_voltage1_gain_calib, S_IWUSR, NULL, ++ max78m6610_lmu_write_calib, CALCMD_S2_GAIN); ++static IIO_DEVICE_ATTR(do_current1_gain_calib, S_IWUSR, NULL, ++ max78m6610_lmu_write_calib, CALCMD_S3_GAIN); ++static IIO_DEVICE_ATTR(do_voltage0_offset_calib, S_IWUSR, NULL, ++ max78m6610_lmu_write_calib, CALCMD_S0_OFFS); ++static IIO_DEVICE_ATTR(do_current0_offset_calib, S_IWUSR, NULL, ++ max78m6610_lmu_write_calib, CALCMD_S1_OFFS); ++static IIO_DEVICE_ATTR(do_voltage1_offset_calib, S_IWUSR, NULL, ++ max78m6610_lmu_write_calib, CALCMD_S2_OFFS); ++static IIO_DEVICE_ATTR(do_current1_offset_calib, S_IWUSR, NULL, ++ max78m6610_lmu_write_calib, CALCMD_S3_OFFS); ++static IIO_DEVICE_ATTR(do_save_to_flash, S_IWUSR, NULL, ++ max78m6610_lmu_write_flash, 0); ++ ++static struct attribute *max78m6610_lmu_attributes[] = { ++ &iio_dev_attr_do_reset.dev_attr.attr, ++ &iio_dev_attr_do_voltage0_gain_calib.dev_attr.attr, ++ &iio_dev_attr_do_current0_gain_calib.dev_attr.attr, ++ &iio_dev_attr_do_voltage1_gain_calib.dev_attr.attr, ++ &iio_dev_attr_do_current1_gain_calib.dev_attr.attr, ++ &iio_dev_attr_do_voltage0_offset_calib.dev_attr.attr, ++ &iio_dev_attr_do_current0_offset_calib.dev_attr.attr, ++ &iio_dev_attr_do_voltage1_offset_calib.dev_attr.attr, ++ &iio_dev_attr_do_current1_offset_calib.dev_attr.attr, ++ &iio_dev_attr_do_save_to_flash.dev_attr.attr, ++ NULL, ++}; ++ ++static const struct attribute_group max78m6610_lmu_attribute_group = { ++ .attrs = max78m6610_lmu_attributes, ++}; ++ ++/* Provides an option to poll for events (useful if interrupts unavailable) */ ++static IIO_DEVICE_ATTR(do_status_scan, S_IWUSR, NULL, ++ max78m6610_lmu_write_status_scan, 0); ++ ++/* Need to have at least 1 event attribute to enable IIO events. ++ * Purposely not setting .event_mask for the channels because that would ++ * enable the IIO events sysfs entries which are not suitable for this driver ++ */ ++static struct attribute *max78m6610_lmu_event_attributes[] = { ++ &iio_dev_attr_do_status_scan.dev_attr.attr, ++ NULL, ++}; ++ ++static struct attribute_group max78m6610_lmu_event_attribute_group = { ++ .attrs = max78m6610_lmu_event_attributes, ++}; ++ ++ ++/* Driver specific iio info structure */ ++static const struct iio_info max78m6610_lmu_info = { ++ .read_raw = max78m6610_lmu_read_raw, ++ .write_raw = max78m6610_lmu_write_raw, ++ .write_raw_get_fmt = max78m6610_lmu_write_raw_get_fmt, ++ .debugfs_reg_access = max78m6610_lmu_reg_access, ++ .update_scan_mode = max78m6610_lmu_update_scan_mode, ++ .event_attrs = &max78m6610_lmu_event_attribute_group, ++ .attrs = &max78m6610_lmu_attribute_group, ++ .driver_module = THIS_MODULE, ++}; ++ ++/** ++ * max78m6610_lmu_open ++ * ++ * @param inode: inode descriptor associated with char device ++ * @param filp: file object pointer ++ * @return 0 on success, non-zero errno otherwise ++ * ++ * This handles an open syscall on the character device node ++ */ ++static int ++max78m6610_lmu_open(struct inode *inode, struct file *filp) ++{ ++ struct max78m6610_lmu_state *st; ++ int ret = 0; ++ ++ st = container_of(inode->i_cdev, ++ struct max78m6610_lmu_state, ++ cdev); ++ filp->private_data = st; ++ ++ if (!st->bbuffer) { ++ st->bbuffer = kmalloc(SPI_BBUFFER_LEN, GFP_KERNEL); ++ if (!st->bbuffer) { ++ dev_dbg(&st->spi->dev, "open/ENOMEM\n"); ++ ret = -ENOMEM; ++ } ++ } ++ ++ return ret; ++} ++ ++/** ++ * max78m6610_lmu_release ++ * ++ * @param inode: inode descriptor associated with char device ++ * @param filp: file object pointer ++ * @return 0 on success, non-zero errno otherwise ++ * ++ * This handles a close syscall on the character device node ++ */ ++static int ++max78m6610_lmu_release(struct inode *inode, struct file *filp) ++{ ++ struct max78m6610_lmu_state *st = ++ (struct max78m6610_lmu_state *)filp->private_data; ++ ++ kfree(st->bbuffer); ++ st->bbuffer = NULL; ++ ++ return 0; ++} ++ ++/** ++ * spidev_message ++ * ++ * @param st: driver state information ++ * @param u_xfers: spi transfer descriptor array ++ * @param n_xfers: number of spi transfer descriptors ++ * @return 0 on success, non-zero errno otherwise ++ * ++ * Translates a set of user-space SPI transfer requests to kernel-space ++ * equivalent, using bounce-buffers for the data, and invokes spi_sync() ++ * to execute the bi-directional SPI transfers ++ * ++ * The implementation below was borrowed directly from the spidev kernel driver ++ * with minor modifications to fit it in here ++ */ ++static int spidev_message(struct max78m6610_lmu_state *st, ++ struct spi_ioc_transfer *u_xfers, ++ unsigned n_xfers) ++{ ++ struct spi_message msg; ++ struct spi_transfer *k_xfers; ++ struct spi_transfer *k_tmp; ++ struct spi_ioc_transfer *u_tmp; ++ unsigned n, total; ++ u8 *buf; ++ int status = -EFAULT; ++ ++ spi_message_init(&msg); ++ k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL); ++ if (k_xfers == NULL) ++ return -ENOMEM; ++ ++ /* Construct spi_message, copying any tx data to bounce buffer. ++ * We walk the array of user-provided transfers, using each one ++ * to initialize a kernel version of the same transfer. ++ */ ++ buf = st->bbuffer; ++ total = 0; ++ for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers; ++ n; ++ n--, k_tmp++, u_tmp++) { ++ k_tmp->len = u_tmp->len; ++ ++ total += k_tmp->len; ++ if (total > SPI_BBUFFER_LEN) { ++ status = -EMSGSIZE; ++ goto done; ++ } ++ ++ if (u_tmp->rx_buf) { ++ k_tmp->rx_buf = buf; ++ if (!access_ok(VERIFY_WRITE, (u8 __user *) ++ (uintptr_t) u_tmp->rx_buf, ++ u_tmp->len)) ++ goto done; ++ } ++ if (u_tmp->tx_buf) { ++ k_tmp->tx_buf = buf; ++ if (copy_from_user(buf, (const u8 __user *) ++ (uintptr_t) u_tmp->tx_buf, ++ u_tmp->len)) ++ goto done; ++ } ++ buf += k_tmp->len; ++ ++ k_tmp->cs_change = !!u_tmp->cs_change; ++ k_tmp->bits_per_word = u_tmp->bits_per_word; ++ k_tmp->delay_usecs = u_tmp->delay_usecs; ++ k_tmp->speed_hz = u_tmp->speed_hz; ++#ifdef VERBOSE ++ dev_dbg(&st->spi->dev, ++ " xfer len %zd %s%s%s%dbits %u usec %uHz\n", ++ u_tmp->len, ++ u_tmp->rx_buf ? "rx " : "", ++ u_tmp->tx_buf ? "tx " : "", ++ u_tmp->cs_change ? "cs " : "", ++ u_tmp->bits_per_word ? : st->spi->bits_per_word, ++ u_tmp->delay_usecs, ++ u_tmp->speed_hz ? : st->spi->max_speed_hz); ++#endif ++ spi_message_add_tail(k_tmp, &msg); ++ } ++ ++ status = spi_sync(st->spi, &msg); ++ if (status < 0) ++ goto done; ++ ++ /* copy any rx data out of bounce buffer */ ++ buf = st->bbuffer; ++ for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) { ++ if (u_tmp->rx_buf) { ++ if (__copy_to_user((u8 __user *) ++ (uintptr_t) u_tmp->rx_buf, buf, ++ u_tmp->len)) { ++ status = -EFAULT; ++ goto done; ++ } ++ } ++ buf += u_tmp->len; ++ } ++ status = total; ++ ++done: ++ kfree(k_xfers); ++ return status; ++} ++ ++/** ++ * max78m6610_lmu_ioctl ++ * ++ * @param filp: file object pointer ++ * @param cmd: ioctl command ++ * @param arg: optional data argument ++ * @return 0 on success, non-zero errno otherwise ++ * ++ * This handles an ioctl syscall on the character device node. This handler ++ * supports only the SPI_IOC_MESSAGE ioctl command defined in spidev.h ++ * The implementation below was borrowed from the spidev driver, with some minor ++ * modifications to remove support for other ioctl commands not needed here ++ */ ++static long ++max78m6610_lmu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ++{ ++ struct max78m6610_lmu_state *st = filp->private_data; ++ struct iio_dev *indio_dev = spi_get_drvdata(st->spi); ++ u32 tmp; ++ unsigned n_ioc; ++ struct spi_ioc_transfer *ioc; ++ int ret = 0; ++ ++ /* Check type and command number */ ++ if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC) ++ return -ENOTTY; ++ ++ /* Check access direction once here; don't repeat below. ++ * IOC_DIR is from the user perspective, while access_ok is ++ * from the kernel perspective; so they look reversed. ++ */ ++ if (_IOC_DIR(cmd) & _IOC_READ) ++ ret = !access_ok(VERIFY_WRITE, ++ (void __user *)arg, _IOC_SIZE(cmd)); ++ if (ret == 0 && _IOC_DIR(cmd) & _IOC_WRITE) ++ ret = !access_ok(VERIFY_READ, ++ (void __user *)arg, _IOC_SIZE(cmd)); ++ if (ret) ++ return -EFAULT; ++ ++ ret = mutex_lock_interruptible(&indio_dev->mlock); ++ if (ret) ++ return ret; ++ ++ /* segmented and/or full-duplex I/O request */ ++ if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) ++ || _IOC_DIR(cmd) != _IOC_WRITE) { ++ ret = -ENOTTY; ++ goto exit; ++ } ++ ++ tmp = _IOC_SIZE(cmd); ++ if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ n_ioc = tmp / sizeof(struct spi_ioc_transfer); ++ if (n_ioc == 0) ++ goto exit; ++ ++ /* copy into scratch area */ ++ ioc = kmalloc(tmp, GFP_KERNEL); ++ if (!ioc) { ++ ret = -ENOMEM; ++ goto exit; ++ } ++ if (__copy_from_user(ioc, (void __user *)arg, tmp)) { ++ kfree(ioc); ++ ret = -EFAULT; ++ goto exit; ++ } ++ ++ /* translate to spi_message, execute */ ++ ret = spidev_message(st, ioc, n_ioc); ++ kfree(ioc); ++ ++exit: ++ mutex_unlock(&indio_dev->mlock); ++ ++ return ret; ++} ++ ++static const struct file_operations max78m6610_lmu_fops = { ++ .owner = THIS_MODULE, ++ .open = max78m6610_lmu_open, ++ .release = max78m6610_lmu_release, ++ .unlocked_ioctl = max78m6610_lmu_ioctl, ++}; ++ ++/** ++ * max78m6610_lmu_chrdev_init ++ * ++ * @param st: driver state information ++ * @return 0 on success, non-zero errno otherwise ++ * ++ * Creates a character device to implement a subset of the spidev user-interface ++ * API, namely full-duplex SPI transfers via the ioctl() interface. ++ * The intention is to provide user-space applications with direct access to the ++ * underlying SPI device if required. The user-space application is, in this ++ * mode of operation, responsible for directly constructing the SPI messages ++ * required by the MAX78M6610 and those messages are passed transparently ++ * through this driver. This is needed, for example, to facilitate binary-only ++ * firmware update applications, but may also be used by user-space applications ++ * to access any device registers which have not been exposed by this driver. ++ * ++ * The device node created will appear in the filesystem as /dev/max78m6610_lmu ++ */ ++static int ++max78m6610_lmu_chrdev_init(struct max78m6610_lmu_state *st) ++{ ++ int ret; ++ struct device *dev; ++ ++ ret = alloc_chrdev_region(&st->cdev_no, 0, 1, ++ "max78m6610_lmu"); ++ if (ret) { ++ pr_err("Failed to alloc chrdev: %d", ret); ++ return ret; ++ } ++ ++ cdev_init(&st->cdev, &max78m6610_lmu_fops); ++ ++ ret = cdev_add(&st->cdev, st->cdev_no, 1); ++ if (ret) { ++ pr_err("Failed to add cdev: %d", ret); ++ unregister_chrdev_region(st->cdev_no, 1); ++ return ret; ++ } ++ ++ st->cl = class_create(THIS_MODULE, "char"); ++ if (IS_ERR(st->cl)) { ++ pr_err("Failed to create device class: %ld", ++ PTR_ERR(st->cl)); ++ cdev_del(&st->cdev); ++ unregister_chrdev_region(st->cdev_no, 1); ++ return PTR_ERR(st->cl); ++ } ++ ++ dev = device_create(st->cl, NULL, st->cdev_no, NULL, ++ "max78m6610_lmu"); ++ if (IS_ERR(dev)) { ++ pr_err("Failed to create device: %ld", ++ PTR_ERR(st->cl)); ++ class_destroy(st->cl); ++ cdev_del(&st->cdev); ++ unregister_chrdev_region(st->cdev_no, 1); ++ return PTR_ERR(dev); ++ } ++ ++ return 0; ++} ++ ++/** ++ * max78m6610_lmu_chrdev_remove ++ * ++ * @param st: driver state information ++ * @return 0 on success, non-zero errno otherwise ++ * ++ * Remove the character device created by max78m6610_lmu_chrdev_init() ++ */ ++static int ++max78m6610_lmu_chrdev_remove(struct max78m6610_lmu_state *st) ++{ ++ device_destroy(st->cl, st->cdev_no); ++ class_destroy(st->cl); ++ cdev_del(&st->cdev); ++ unregister_chrdev_region(st->cdev_no, 1); ++ ++ return 0; ++} ++ ++/** ++ * max78m6610_lmu_probe ++ * ++ * @param spi: spi device pointer ++ * @return: return 0 or standard errorids if failure ++ * ++ * device driver probe funciton for iio_dev struct initialisation. ++ */ ++static int max78m6610_lmu_probe(struct spi_device *spi) ++{ ++ struct max78m6610_lmu_state *st; ++ struct iio_dev *indio_dev = iio_device_alloc(sizeof(*st)); ++ struct max78m6610_lmu_platform_data *pdata = spi->dev.platform_data; ++ int ret; ++ ++ if (indio_dev == NULL) ++ return -ENOMEM; ++ st = iio_priv(indio_dev); ++ ++ spi_set_drvdata(spi, indio_dev); ++ st->spi = spi; ++ ++ if (pdata) ++ st->reset_gpio = pdata->reset_gpio; ++ else ++ st->reset_gpio = -1; ++ ++ indio_dev->name = spi_get_device_id(spi)->name; ++ indio_dev->dev.parent = &spi->dev; ++ indio_dev->modes = INDIO_DIRECT_MODE; ++ indio_dev->channels = max78m6610_lmu_channels; ++ indio_dev->num_channels = ARRAY_SIZE(max78m6610_lmu_channels); ++ indio_dev->info = &max78m6610_lmu_info; ++ ++ /* Setup default message */ ++ st->scan_single_xfer.tx_buf = &st->tx_buf[0]; ++ st->scan_single_xfer.rx_buf = &st->rx_buf[0]; ++ st->scan_single_xfer.len = SPI_MSG_LEN; ++ ++ spi_message_init(&st->scan_single_msg); ++ spi_message_add_tail(&st->scan_single_xfer, &st->scan_single_msg); ++ ++ ret = iio_triggered_buffer_setup(indio_dev, NULL, ++ &max78m6610_lmu_trigger_handler, NULL); ++ if (ret) { ++ pr_err("triger buffer setup failed !\n"); ++ goto error_free; ++ } ++ ++ pr_debug("%s: alloc dev id: %d\n", __func__, indio_dev->id); ++ ret = iio_device_register(indio_dev); ++ if (ret) ++ goto error_cleanup_ring; ++ ++ ret = max78m6610_lmu_chrdev_init(st); ++ if (ret) ++ goto error_cleanup_ring; ++ ++ return 0; ++ ++error_cleanup_ring: ++ iio_triggered_buffer_cleanup(indio_dev); ++error_free: ++ iio_device_free(indio_dev); ++ ++ return ret; ++} ++ ++/** ++ * max78m6610_lmu_remove ++ * ++ * @param spi: spi device pointer ++ * @return: return 0 ++ * ++ * iio device unregister & cleanup ++ */ ++static int max78m6610_lmu_remove(struct spi_device *spi) ++{ ++ struct iio_dev *indio_dev = spi_get_drvdata(spi); ++ struct max78m6610_lmu_state *st = iio_priv(indio_dev); ++ ++ max78m6610_lmu_chrdev_remove(st); ++ iio_device_unregister(indio_dev); ++ iio_triggered_buffer_cleanup(indio_dev); ++ iio_device_free(indio_dev); ++ ++ return 0; ++} ++ ++static const struct spi_device_id max78m6610_lmu_id[] = { ++ {"max78m6610_lmu", 0}, ++ {} ++}; ++MODULE_DEVICE_TABLE(spi, max78m6610_lmu_id); ++ ++static struct spi_driver max78m6610_lmu_driver = { ++ .driver = { ++ .name = "max78m6610_lmu", ++ .owner = THIS_MODULE, ++ }, ++ .probe = max78m6610_lmu_probe, ++ .remove = max78m6610_lmu_remove, ++ .id_table = max78m6610_lmu_id, ++}; ++ ++/** ++ * max78m6610_lmu_init ++ * ++ * device driver module init ++ */ ++static __init int max78m6610_lmu_init(void) ++{ ++ int ret; ++ ++ ret = spi_register_driver(&max78m6610_lmu_driver); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++module_init(max78m6610_lmu_init); ++ ++/** ++ * max78m6610_lmu_exit ++ * ++ * device driver module exit ++ */ ++static __exit void max78m6610_lmu_exit(void) ++{ ++ spi_unregister_driver(&max78m6610_lmu_driver); ++} ++module_exit(max78m6610_lmu_exit); ++ ++ ++MODULE_AUTHOR("Kai Ji <kai.ji@emutex.com>"); ++MODULE_DESCRIPTION("Maxim 78M6610+LMU eADC"); ++MODULE_LICENSE("GPL v2"); +diff --git a/drivers/staging/iio/trigger/Kconfig b/drivers/staging/iio/trigger/Kconfig +index 7d32075..e9e837d 100644 +--- a/drivers/staging/iio/trigger/Kconfig ++++ b/drivers/staging/iio/trigger/Kconfig +@@ -29,6 +29,17 @@ config IIO_SYSFS_TRIGGER + + To compile this driver as a module, choose M here: the + module will be called iio-trig-sysfs. ++ ++config IIO_HRTIMER_TRIGGER ++ tristate "HRTIMER trigger" ++ #depends on HRTIMER ++ select IRQ_WORK ++ help ++ Provides support for using HRTIMER entries as IIO triggers. ++ If unsure, say N (but it's safe to say "Y"). ++ ++ To compile this driver as a module, choose M here: the ++ module will be called iio-trig-hrtimer. + + config IIO_BFIN_TMR_TRIGGER + tristate "Blackfin TIMER trigger" +diff --git a/drivers/staging/iio/trigger/Makefile b/drivers/staging/iio/trigger/Makefile +index b088b57..ad2f595 100644 +--- a/drivers/staging/iio/trigger/Makefile ++++ b/drivers/staging/iio/trigger/Makefile +@@ -5,4 +5,5 @@ + obj-$(CONFIG_IIO_PERIODIC_RTC_TRIGGER) += iio-trig-periodic-rtc.o + obj-$(CONFIG_IIO_GPIO_TRIGGER) += iio-trig-gpio.o + obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o ++obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o + obj-$(CONFIG_IIO_BFIN_TMR_TRIGGER) += iio-trig-bfin-timer.o +diff --git a/drivers/staging/iio/trigger/iio-trig-hrtimer.c b/drivers/staging/iio/trigger/iio-trig-hrtimer.c +new file mode 100644 +index 0000000..5dc30e5 +--- /dev/null ++++ b/drivers/staging/iio/trigger/iio-trig-hrtimer.c +@@ -0,0 +1,288 @@ ++/* ++ * Industrial I/O - hrtimer trigger support ++ * ++ * Copyright 2013 STMicroelectronics Inc. ++ * Denis Ciocca <denis.ciocca@st.com> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published by ++ * the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/hrtimer.h> ++#include <linux/ktime.h> ++#include <linux/slab.h> ++#include <linux/list.h> ++ ++#include <linux/iio/iio.h> ++#include <linux/iio/trigger.h> ++ ++struct iio_hrtimer_trigger_data { ++ struct iio_trigger *trig; ++ struct hrtimer timer; ++ struct list_head l; ++ ktime_t period; ++ u16 freq; ++ int id; ++}; ++ ++static LIST_HEAD(iio_hrtimer_trigger_list); ++static DEFINE_MUTEX(iio_hrtimer_trigger_list_mut); ++ ++static int iio_hrtimer_trigger_probe(int id); ++static int iio_hrtimer_trigger_remove(int id); ++ ++static ssize_t iio_sysfs_hrtimer_trig_add(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t len) ++{ ++ int ret; ++ unsigned long input; ++ ++ ret = kstrtoul(buf, 10, &input); ++ if (ret) ++ return ret; ++ ++ ret = iio_hrtimer_trigger_probe(input); ++ if (ret) ++ return ret; ++ ++ return len; ++} ++static DEVICE_ATTR(add_trigger, S_IWUSR, NULL, &iio_sysfs_hrtimer_trig_add); ++ ++static ssize_t iio_sysfs_hrtimer_trig_remove(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t len) ++{ ++ int ret; ++ unsigned long input; ++ ++ ret = kstrtoul(buf, 10, &input); ++ if (ret) ++ return ret; ++ ++ ret = iio_hrtimer_trigger_remove(input); ++ if (ret) ++ return ret; ++ ++ return len; ++} ++static DEVICE_ATTR(remove_trigger, S_IWUSR, ++ NULL, &iio_sysfs_hrtimer_trig_remove); ++ ++static struct attribute *iio_hrtimer_trig_attrs[] = { ++ &dev_attr_add_trigger.attr, ++ &dev_attr_remove_trigger.attr, ++ NULL, ++}; ++ ++static const struct attribute_group iio_hrtimer_trig_group = { ++ .attrs = iio_hrtimer_trig_attrs, ++}; ++ ++static const struct attribute_group *iio_hrtimer_trig_groups[] = { ++ &iio_hrtimer_trig_group, ++ NULL, ++}; ++ ++/* Nothing to actually do upon release */ ++static void iio_hrtimer_trig_release(struct device *dev) ++{ ++} ++ ++static struct device iio_hrtimer_trig_dev = { ++ .bus = &iio_bus_type, ++ .groups = iio_hrtimer_trig_groups, ++ .release = &iio_hrtimer_trig_release, ++}; ++ ++static int iio_hrtimer_trig_set_state(struct iio_trigger *trig, bool state) ++{ ++ struct iio_hrtimer_trigger_data *trig_data = ++ dev_get_drvdata(&trig->dev); ++ ++ if (trig_data->freq == 0) ++ return -EINVAL; ++ ++ if (state) ++ hrtimer_start(&trig_data->timer, ++ trig_data->period, HRTIMER_MODE_REL); ++ else ++ hrtimer_cancel(&trig_data->timer); ++ ++ return 0; ++} ++ ++static ssize_t iio_hrtimer_trigger_set_freq_value(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t len) ++{ ++ int ret; ++ u16 frequency; ++ struct iio_trigger *trig = to_iio_trigger(dev); ++ struct iio_hrtimer_trigger_data *trig_data = ++ dev_get_drvdata(&trig->dev); ++ ++ ret = kstrtou16(buf, 10, &frequency); ++ if (ret < 0) ++ return ret; ++ ++ if (frequency > NSEC_PER_SEC) ++ return -EINVAL; ++ ++ trig_data->freq = frequency; ++ ++ if (frequency) ++ trig_data->period = ++ ktime_set(0, NSEC_PER_SEC / trig_data->freq); ++ ++ return len; ++} ++ ++static ssize_t iio_hrtimer_trigger_get_freq_value(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct iio_trigger *trig = to_iio_trigger(dev); ++ struct iio_hrtimer_trigger_data *trig_data = ++ dev_get_drvdata(&trig->dev); ++ ++ return sprintf(buf, "%hu\n", trig_data->freq); ++} ++ ++static DEVICE_ATTR(frequency, S_IWUSR | S_IRUGO, ++ iio_hrtimer_trigger_get_freq_value, ++ iio_hrtimer_trigger_set_freq_value); ++ ++static struct attribute *iio_hrtimer_trigger_attrs[] = { ++ &dev_attr_frequency.attr, ++ NULL, ++}; ++ ++static const struct attribute_group iio_hrtimer_trigger_attr_group = { ++ .attrs = iio_hrtimer_trigger_attrs, ++}; ++ ++static const struct attribute_group *iio_hrtimer_trigger_attr_groups[] = { ++ &iio_hrtimer_trigger_attr_group, ++ NULL, ++}; ++ ++static const struct iio_trigger_ops iio_hrtimer_trigger_ops = { ++ .owner = THIS_MODULE, ++ .set_trigger_state = &iio_hrtimer_trig_set_state, ++}; ++ ++enum hrtimer_restart iio_hrtimer_trigger_func(struct hrtimer *timer) ++{ ++ struct iio_hrtimer_trigger_data *trig_data; ++ ++ trig_data = container_of(timer, struct iio_hrtimer_trigger_data, timer); ++ ++ hrtimer_forward_now(timer, trig_data->period); ++ iio_trigger_poll(trig_data->trig, 0); ++ ++ return HRTIMER_RESTART; ++} ++ ++static int iio_hrtimer_trigger_probe(int id) ++{ ++ int err; ++ bool foundit = false; ++ struct iio_hrtimer_trigger_data *trig_data; ++ ++ mutex_lock(&iio_hrtimer_trigger_list_mut); ++ list_for_each_entry(trig_data, &iio_hrtimer_trigger_list, l) { ++ if (id == trig_data->id) { ++ foundit = true; ++ break; ++ } ++ } ++ if (foundit) { ++ err = -EINVAL; ++ goto iio_hrtimer_mutex_unlock; ++ } ++ ++ trig_data = kmalloc(sizeof(*trig_data), GFP_KERNEL); ++ if (trig_data == NULL) { ++ err = -ENOMEM; ++ goto iio_hrtimer_mutex_unlock; ++ } ++ ++ trig_data->id = id; ++ trig_data->trig = iio_trigger_alloc("hrtimer_trig%d", id); ++ if (!trig_data->trig) { ++ err = -ENOMEM; ++ goto iio_hrtimer_free_trig_data; ++ } ++ ++ trig_data->trig->dev.groups = iio_hrtimer_trigger_attr_groups; ++ trig_data->trig->ops = &iio_hrtimer_trigger_ops; ++ trig_data->trig->dev.parent = &iio_hrtimer_trig_dev; ++ dev_set_drvdata(&trig_data->trig->dev, trig_data); ++ ++ trig_data->freq = 0; ++ hrtimer_init(&trig_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ trig_data->timer.function = &iio_hrtimer_trigger_func; ++ ++ err = iio_trigger_register(trig_data->trig); ++ if (err) ++ goto iio_hrtimer_free_trig_data; ++ ++ list_add(&trig_data->l, &iio_hrtimer_trigger_list); ++ __module_get(THIS_MODULE); ++ mutex_unlock(&iio_hrtimer_trigger_list_mut); ++ ++ return 0; ++ ++iio_hrtimer_free_trig_data: ++ kfree(trig_data); ++iio_hrtimer_mutex_unlock: ++ mutex_unlock(&iio_hrtimer_trigger_list_mut); ++ return err; ++} ++ ++static int iio_hrtimer_trigger_remove(int id) ++{ ++ bool foundit = false; ++ struct iio_hrtimer_trigger_data *trig_data; ++ ++ mutex_lock(&iio_hrtimer_trigger_list_mut); ++ list_for_each_entry(trig_data, &iio_hrtimer_trigger_list, l) { ++ if (id == trig_data->id) { ++ foundit = true; ++ break; ++ } ++ } ++ if (!foundit) { ++ mutex_unlock(&iio_hrtimer_trigger_list_mut); ++ return -EINVAL; ++ } ++ ++ iio_trigger_unregister(trig_data->trig); ++ iio_trigger_free(trig_data->trig); ++ ++ list_del(&trig_data->l); ++ kfree(trig_data); ++ module_put(THIS_MODULE); ++ mutex_unlock(&iio_hrtimer_trigger_list_mut); ++ ++ return 0; ++} ++ ++static int __init iio_hrtimer_trig_init(void) ++{ ++ device_initialize(&iio_hrtimer_trig_dev); ++ dev_set_name(&iio_hrtimer_trig_dev, "iio_hrtimer_trigger"); ++ return device_add(&iio_hrtimer_trig_dev); ++} ++module_init(iio_hrtimer_trig_init); ++ ++static void __exit iio_hrtimer_trig_exit(void) ++{ ++ device_unregister(&iio_hrtimer_trig_dev); ++} ++module_exit(iio_hrtimer_trig_exit); ++ ++MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>"); ++MODULE_DESCRIPTION("Hrtimer trigger for the iio subsystem"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/linux/platform_data/ad7298.h b/include/linux/platform_data/ad7298.h +index fbf8adf..721ed6f 100644 +--- a/include/linux/platform_data/ad7298.h ++++ b/include/linux/platform_data/ad7298.h +@@ -9,12 +9,17 @@ + #ifndef __LINUX_PLATFORM_DATA_AD7298_H__ + #define __LINUX_PLATFORM_DATA_AD7298_H__ + ++#define AD7298_MAX_CHAN 8 ++ + /** + * struct ad7298_platform_data - Platform data for the ad7298 ADC driver + * @ext_ref: Whether to use an external reference voltage. ++ * @ext_vin_max: External input voltage range for each voltage input channel ++ * (set to non-zero if platform uses external voltage dividers) + **/ + struct ad7298_platform_data { + bool ext_ref; ++ u16 ext_vin_max[AD7298_MAX_CHAN]; + }; + + #endif /* IIO_ADC_AD7298_H_ */ +diff --git a/include/linux/platform_data/max78m6610_lmu.h b/include/linux/platform_data/max78m6610_lmu.h +new file mode 100644 +index 0000000..d05d513 +--- /dev/null ++++ b/include/linux/platform_data/max78m6610_lmu.h +@@ -0,0 +1,34 @@ ++/* ++ * Platform data for max78m6610+lmu SPI protocol driver ++ * Copyright(c) 2013 Intel Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. ++ * ++ * Contact Information: ++ * Intel Corporation ++ */ ++ ++#ifndef __LINUX_PLATFORM_DATA_MAX78M6610_LMU_H__ ++#define __LINUX_PLATFORM_DATA_MAX78M6610_LMU_H__ ++ ++/** ++ * struct max78m6610_lmu_platform_data - Platform data for the max78m6610_lmu ++ * ADC driver ++ * @reset_gpio: GPIO pin number reserved for MAX78M6610+LMU device reset ++ **/ ++struct max78m6610_lmu_platform_data { ++ int reset_gpio; ++}; ++ ++#endif /* IIO_ADC_MAX78M6610_LMU_H_ */ +-- +1.7.4.1 + |