aboutsummaryrefslogtreecommitdiffstats
path: root/meta-rcar-gen2/recipes-kernel
diff options
context:
space:
mode:
authorGrigory Kletsko <grigory.kletsko@cogentembedded.com>2016-06-17 17:12:04 +0300
committerYannick Gicquel <yannick.gicquel@iot.bzh>2016-09-07 19:00:16 +0200
commit79640e49a585354e2f7c7c17352b13a5f7da29f6 (patch)
tree6557033156698f41fc4294745d87865f7bf7884d /meta-rcar-gen2/recipes-kernel
parenta940b05f44de841a19b44c28fc09000f75e1203d (diff)
Import-kernel-patches-for-porter-ext01-board.patch
Diffstat (limited to 'meta-rcar-gen2/recipes-kernel')
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0049-Add-lsm9ds0-acc-gyro-mag-driver.patch5433
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0051-TI-ADS111X-sigma-delta-ADC-driver.patch320
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0051a-Cleanup-TI-ADS111x-ADC-driver.patch162
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0059-crypto-aead-Add-crypto_aead_set_reqsize-helper.patch32
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter.cfg20
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0002-Porter-adapt-max9272-ov10635-driver-for-porter-exp-b.patch60
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0003-Porter-add-LVDS-camera.patch88
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0004-GPIO-CPLD-gpio-extender-driver.patch248
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0012-regmap-Implemented-default-cache-sync-operation.patch102
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0013-regmap-cache-Don-t-attempt-to-sync-non-writeable-reg.patch34
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0014-ASoC-Add-SOC_DOUBLE_STS-macro.patch35
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0015-ASoC-pcm3168a-Add-binding-document-for-pcm3168a-code.patch72
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0016-ASoC-pcm3168a-Add-driver-for-pcm3168a-codec.patch1108
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0017-ASoC-PCM3168A-add-TDM-modes.patch298
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0018-ASoC-PCM3168A-disable-16-bit-format.patch26
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0019-ASoC-PCM3168A-enable-on-power-on.patch27
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0020-ASoC-PCM3168A-merge-ADC-and-DAC-to-single-DAI.patch276
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0021-ASoC-PCM3168A-disable-PM.patch43
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0022-ASoC-fix-simple-card-do-not-respect-device-id.patch37
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0023-SPI-GPIO-get-master-fix.patch26
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0024-SPIDEV-extend-maximum-transfer-size.patch26
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0025-Radio-add-si468x-to-spidev.patch25
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0026-ASoC-add-dummy-Si468x-driver.patch122
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0027-ASoC-R-Car-initial-TDM-support.patch354
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0028-ASoC-rcar-correct-32bit-to-24-bit-sample-conv.patch45
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0029-ASoC-R-Car-fix-debug-output.patch57
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0030-R-Car-sound-disable-clock-hack.patch24
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0031-ASoC-R-car-SSI-fix-SSI-slave-mode-setup-while-TDM-an.patch48
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0032-mmc-Add-SDIO-function-devicetree-subnode-parsing.patch160
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0038-Porter-LVDS-display-LQ123K1LG03.patch48
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0060-Remove-delay-at-LVDS-camera-initialization.patch25
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0099-Porter-add-separate-dts-for-ext01-extension-board.patch805
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0100-Porter-ext01-add-dummy-regulator-to-select-48000-sou.patch53
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter_ext01.cfg46
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0014-serial-sh-sci-Break-out-default-CTS-RTS-pin-setup.patch86
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0015-serial-sh-sci-Fix-default-RTS-handling.patch50
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0016-serial-sh-sci-Expose-default-CTS-pin.patch79
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0017-serial-sh-sci-Add-SCIFA-SCIFB-CTS-RTS-pin-setup.patch199
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0018-serial-sh-sci-Expose-SCIFA-SCIFB-CTS-pin.patch65
-rw-r--r--meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb41
40 files changed, 10804 insertions, 1 deletions
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0049-Add-lsm9ds0-acc-gyro-mag-driver.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0049-Add-lsm9ds0-acc-gyro-mag-driver.patch
new file mode 100644
index 0000000..d461b3d
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0049-Add-lsm9ds0-acc-gyro-mag-driver.patch
@@ -0,0 +1,5433 @@
+From dadd0a8a2cf1fecbbb0df7713f3da81ce7c11b00 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Wed, 16 Sep 2015 14:28:22 +0300
+Subject: [PATCH 49/50] Add lsm9ds0 (acc gyro mag) driver
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/input/misc/Kconfig | 10 +
+ drivers/input/misc/Makefile | 1 +
+ drivers/input/misc/lsm9ds0_acc_mag.c | 3435 ++++++++++++++++++++++++++++++++++
+ drivers/input/misc/lsm9ds0_gyr.c | 1726 +++++++++++++++++
+ include/linux/input/lsm9ds0.h | 201 ++
+ 5 files changed, 5373 insertions(+)
+ create mode 100644 drivers/input/misc/lsm9ds0_acc_mag.c
+ create mode 100644 drivers/input/misc/lsm9ds0_gyr.c
+ create mode 100644 include/linux/input/lsm9ds0.h
+
+diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
+index bb698e1..f1eb2c8 100644
+--- a/drivers/input/misc/Kconfig
++++ b/drivers/input/misc/Kconfig
+@@ -197,6 +197,16 @@ config INPUT_MPU3050
+ To compile this driver as a module, choose M here: the
+ module will be called mpu3050.
+
++config INPUT_LSM9DS0
++ tristate "LSM9DS0 iNEMO sensor"
++ depends on I2C
++ help
++ Say Y here if you want to support 3D accelerometer,
++ 3D gyroscope, 3D magnetometer LSM9DS0
++
++ To compile this driver as a module, choose M here: the
++ module will be called lsm9ds0.
++
+ config INPUT_APANEL
+ tristate "Fujitsu Lifebook Application Panel buttons"
+ depends on X86 && I2C && LEDS_CLASS
+diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
+index d7fc17f..b726294 100644
+--- a/drivers/input/misc/Makefile
++++ b/drivers/input/misc/Makefile
+@@ -39,6 +39,7 @@ obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o
+ obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
+ obj-$(CONFIG_INPUT_MMA8450) += mma8450.o
+ obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o
++obj-$(CONFIG_INPUT_LSM9DS0) += lsm9ds0_acc_mag.o lsm9ds0_gyr.o
+ obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
+ obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
+ obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o
+diff --git a/drivers/input/misc/lsm9ds0_acc_mag.c b/drivers/input/misc/lsm9ds0_acc_mag.c
+new file mode 100644
+index 0000000..3aad517
+--- /dev/null
++++ b/drivers/input/misc/lsm9ds0_acc_mag.c
+@@ -0,0 +1,3435 @@
++/******************** (C) COPYRIGHT 2013 STMicroelectronics *******************
++*
++* File Name : lsm9ds0_acc_mag.c
++* Authors : AMS - Motion Sensors Div - Application Team
++* : Matteo Dameno (matteo.dameno@st.com)
++* : Denis Ciocca (denis.ciocca@st.com)
++* : Both authors are willing to be considered the contact
++* : and update points for the driver.
++* Version : V.1.0.5
++* Date : 2013/Oct/23
++* Description : LSM9DS0 accelerometer & magnetometer driver
++*
++*******************************************************************************
++*
++* 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.
++*
++* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
++* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
++* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
++* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
++* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
++* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
++* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
++*
++******************************************************************************/
++/******************************************************************************
++Version History.
++
++Revision 1-0-0 2012/05/04
++ first revision
++Revision 1-0-1 2012/05/07
++ New sysfs architecture
++ Support antialiasing filter
++Revision 1-0-2 2012/10/15
++ I2C address bugfix
++Revision 1-0-3 2013/01/21
++ Move CTLREG7 resume write from acc_power_on to magn_power_on
++Revision 1-0-4 2013/05/09
++ Added rotation matrix
++Revision 1-0-5 2013/10/23
++ Corrects Mag Enable bug, Corrects missing BDU enable
++******************************************************************************/
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/i2c.h>
++#include <linux/workqueue.h>
++#include <linux/input.h>
++#include <linux/kernel.h>
++#include <linux/kobject.h>
++#include <linux/gpio.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/hrtimer.h>
++#include <linux/ktime.h>
++
++#include <linux/input/lsm9ds0.h>
++/* include "lsm9ds0.h" */
++
++#define I2C_AUTO_INCREMENT (0x80)
++#define MS_TO_NS(x) (x*1000000L)
++
++#define ACC_G_MAX_POS 1495040 /** max positive value acc [ug] */
++#define ACC_G_MAX_NEG 1495770 /** max negative value acc [ug] */
++#define MAG_G_MAX_POS 983520 /** max positive value mag [ugauss] */
++#define MAG_G_MAX_NEG 983040 /** max negative value mag [ugauss] */
++
++#define FUZZ 0
++#define FLAT 0
++
++/* Address registers */
++#define REG_WHOAMI_ADDR (0x0F) /** Who am i address register */
++#define REG_CNTRL0_ADDR (0x1F) /** CNTRL0 address register */
++#define REG_CNTRL1_ADDR (0x20) /** CNTRL1 address register */
++#define REG_CNTRL2_ADDR (0x21) /** CNTRL2 address register */
++#define REG_CNTRL3_ADDR (0x22) /** CNTRL3 address register */
++#define REG_CNTRL4_ADDR (0x23) /** CNTRL4 address register */
++#define REG_CNTRL5_ADDR (0x24) /** CNTRL5 address register */
++#define REG_CNTRL6_ADDR (0x25) /** CNTRL6 address register */
++#define REG_CNTRL7_ADDR (0x26) /** CNTRL7 address register */
++
++#define REG_ACC_DATA_ADDR (0x28) /** Acc. data low address register */
++#define REG_MAG_DATA_ADDR (0x08) /** Mag. data low address register */
++#define REG_TEMP_DATA_ADDR (0x05) /** Temp. data low address register */
++
++#define REG_GEN_MAG_ADDR (0x12) /** INT_CTRL_REG_M address register */
++#define INT_SRC_REG_M_ADDR (0x13) /** INT_SRC_REG_M address register */
++#define REG_GEN_MAG_THR_ADDR (0x14) /** INT_THS_L_M address register */
++#define MIG_THRESHOLD_ADDR_H (0x15) /** INT_THS_H_M address register */
++#define REG_GEN1_AXIS_ADDR (0x30) /** INT_GEN1_REG address register */
++#define INT_GEN1_SRC_ADDR (0x31) /** INT_GEN1_SRC address register */
++#define REG_GEN1_THR_ADDR (0x32) /** INT_GEN1_THS address register */
++#define REG_GEN1_DUR_ADDR (0x33) /** INT_GEN1_DUR address register */
++#define REG_GEN2_AXIS_ADDR (0x34) /** INT_GEN2_REG address register */
++#define INT_GEN2_SRC_ADDR (0x35) /** INT_GEN2_SRC address register */
++#define REG_GEN2_THR_ADDR (0x36) /** INT_GEN2_THS address register */
++#define REG_GEN2_DUR_ADDR (0x37) /** INT_GEN2_DUR address register */
++
++/* Sensitivity */
++#define SENSITIVITY_ACC_2G 60 /** ug/LSB */
++#define SENSITIVITY_ACC_4G 120 /** ug/LSB */
++#define SENSITIVITY_ACC_8G 240 /** ug/LSB */
++#define SENSITIVITY_ACC_16G 730 /** ug/LSB */
++
++#define SENSITIVITY_MAG_2G 80 /** ugauss/LSB */
++#define SENSITIVITY_MAG_4G 160 /** ugauss/LSB */
++#define SENSITIVITY_MAG_8G 320 /** ugauss/LSB */
++#define SENSITIVITY_MAG_12G 480 /** ugauss/LSB */
++
++/* ODR */
++#define ODR_ACC_MASK (0XF0) /* Mask for odr change on acc */
++#define LSM9DS0_ACC_ODR_OFF (0x00) /* Power down */
++#define LSM9DS0_ACC_ODR3_125 (0x10) /* 3.25Hz output data rate */
++#define LSM9DS0_ACC_ODR6_25 (0x20) /* 6.25Hz output data rate */
++#define LSM9DS0_ACC_ODR12_5 (0x30) /* 12.5Hz output data rate */
++#define LSM9DS0_ACC_ODR25 (0x40) /* 25Hz output data rate */
++#define LSM9DS0_ACC_ODR50 (0x50) /* 50Hz output data rate */
++#define LSM9DS0_ACC_ODR100 (0x60) /* 100Hz output data rate */
++#define LSM9DS0_ACC_ODR200 (0x70) /* 200Hz output data rate */
++#define LSM9DS0_ACC_ODR400 (0x80) /* 400Hz output data rate */
++#define LSM9DS0_ACC_ODR800 (0x90) /* 800Hz output data rate */
++#define LSM9DS0_ACC_ODR1600 (0xA0) /* 1600Hz output data rate */
++
++#define ODR_MAG_MASK (0X1C) /* Mask for odr change on mag */
++#define LSM9DS0_MAG_ODR3_125 (0x00) /* 3.25Hz output data rate */
++#define LSM9DS0_MAG_ODR6_25 (0x04) /* 6.25Hz output data rate */
++#define LSM9DS0_MAG_ODR12_5 (0x08) /* 12.5Hz output data rate */
++#define LSM9DS0_MAG_ODR25 (0x0C) /* 25Hz output data rate */
++#define LSM9DS0_MAG_ODR50 (0x10) /* 50Hz output data rate */
++#define LSM9DS0_MAG_ODR100 (0x14) /* 100Hz output data rate */
++
++/* Magnetic sensor mode */
++#define MSMS_MASK (0x03) /* Mask magnetic sensor mode */
++#define POWEROFF_MAG (0x02) /* Power Down */
++#define CONTINUOS_CONVERSION (0x00) /* Continuos Conversion */
++
++/* Default values loaded in probe function */
++#define WHOIAM_VALUE (0x49) /** Who Am I default value */
++#define REG_DEF_CNTRL0 (0x00) /** CNTRL0 default value */
++#define REG_DEF_CNTRL1 (0x0F) /** CNTRL1 default value */
++#define REG_DEF_CNTRL2 (0x00) /** CNTRL2 default value */
++#define REG_DEF_CNTRL3 (0x00) /** CNTRL3 default value */
++#define REG_DEF_CNTRL4 (0x00) /** CNTRL4 default value */
++#define REG_DEF_CNTRL5 (0x18) /** CNTRL5 default value */
++#define REG_DEF_CNTRL6 (0x20) /** CNTRL6 default value */
++#define REG_DEF_CNTRL7 (0x02) /** CNTRL7 default value */
++
++#define REG_DEF_INT_CNTRL_MAG (0x00) /** INT_CTRL_REG_M default value */
++#define REG_DEF_INT_GEN1 (0x00) /** INT_GEN1_REG default value */
++#define REG_DEF_INT_GEN2 (0x00) /** INT_GEN2_REG default value */
++#define REG_DEF_IIG1_DURATION (0x00) /** INT_GEN1_DUR default value */
++#define REG_DEF_IIG2_DURATION (0x00) /** INT_GEN2_DUR default value */
++#define REG_DEF_IIG1_THRESHOLD (0x00) /** INT_GEN1_THS default value */
++#define REG_DEF_IIG2_THRESHOLD (0x00) /** INT_GEN2_THS default value */
++#define REG_DEF_MIG_THRESHOLD_L (0x00) /** INT_THS_L_M default value */
++#define REG_DEF_MIG_THRESHOLD_H (0x00) /** INT_THS_H_M default value */
++
++#define REG_DEF_ALL_ZEROS (0x00)
++
++/* Accelerometer Filter */
++#define LSM9DS0_ACC_FILTER_MASK (0xC0) /* Mask for filter band change on acc */
++#define FILTER_773 773 /* Anti-Aliasing 773 Hz */
++#define FILTER_362 362 /* Anti-Aliasing 362 Hz */
++#define FILTER_194 194 /* Anti-Aliasing 194 Hz */
++#define FILTER_50 50 /* Anti-Aliasing 50 Hz */
++
++/* Temperature */
++#define TEMP_MASK (0x80) /* Mask for temperature change */
++#define TEMP_ON (0x80) /* Enable temperature */
++#define TEMP_OFF (0x00) /* Disable temperature */
++#define TEMP_SENSITIVITY 8 /* Sensitivity temperature */
++#define OFFSET_TEMP 25 /* Offset temperature */
++#define NDTEMP 1000 /* Not Available temperature */
++
++/* Interrupt */
++#define GEN1_PIN1_MASK (0x20)
++#define GEN1_PIN2_MASK (0x40)
++#define GEN2_PIN1_MASK (0x10)
++#define GEN2_PIN2_MASK (0x20)
++#define GEN_MAG_PIN1_MASK (0x08)
++#define GEN_MAG_PIN2_MASK (0x10)
++#define GEN_MAG_EN_MASK (0x01)
++#define MAX_DUR_TH 127
++#define MAX_TH_MAG 131071
++#define GEN_X_HIGH_MASK (0x02)
++#define GEN_X_LOW_MASK (0x01)
++#define GEN_Y_HIGH_MASK (0x08)
++#define GEN_Y_LOW_MASK (0x04)
++#define GEN_Z_HIGH_MASK (0x20)
++#define GEN_Z_LOW_MASK (0x10)
++#define GEN_X_MAG_MASK (0x80)
++#define GEN_Y_MAG_MASK (0x40)
++#define GEN_Z_MAG_MASK (0x20)
++
++#define GEN1_AND_OR_MASK (0x80)
++#define GEN2_AND_OR_MASK (0x83)
++
++#define INT_PIN_CONF_MASK (0x10)
++#define INT_POLARITY_MASK (0x80)
++
++#define to_dev(obj) container_of(obj, struct device, kobj)
++#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
++
++static struct kobject *acc_kobj;
++static struct kobject *mag_kobj;
++
++struct workqueue_struct *lsm9ds0_workqueue = 0;
++
++struct {
++ unsigned int cutoff_us;
++ u8 value;
++} lsm9ds0_acc_odr_table[] = {
++ { 1, LSM9DS0_ACC_ODR800 },
++ { 2, LSM9DS0_ACC_ODR400 },
++ { 5, LSM9DS0_ACC_ODR200 },
++ { 10, LSM9DS0_ACC_ODR100 },
++ { 20, LSM9DS0_ACC_ODR50 },
++ { 40, LSM9DS0_ACC_ODR25 },
++ { 80, LSM9DS0_ACC_ODR12_5 },
++ { 160, LSM9DS0_ACC_ODR6_25 },
++ { 320, LSM9DS0_ACC_ODR3_125},
++};
++
++struct {
++ unsigned int cutoff_us;
++ u8 value;
++} lsm9ds0_mag_odr_table[] = {
++ { 10, LSM9DS0_MAG_ODR100 },
++ { 20, LSM9DS0_MAG_ODR50 },
++ { 40, LSM9DS0_MAG_ODR25 },
++ { 80, LSM9DS0_MAG_ODR12_5 },
++ { 160, LSM9DS0_MAG_ODR6_25 },
++ { 320, LSM9DS0_MAG_ODR3_125},
++};
++
++struct interrupt_enable {
++ atomic_t enable;
++ u8 address;
++ u8 mask;
++};
++
++struct interrupt_value {
++ int value;
++ u8 address;
++};
++
++struct lsm9ds0_interrupt {
++ struct interrupt_enable gen1_pin1;
++ struct interrupt_enable gen1_pin2;
++ struct interrupt_enable gen2_pin1;
++ struct interrupt_enable gen2_pin2;
++ struct interrupt_value gen1_threshold;
++ struct interrupt_value gen2_threshold;
++ struct interrupt_value gen1_duration;
++ struct interrupt_value gen2_duration;
++ struct interrupt_enable gen_mag_pin1;
++ struct interrupt_enable gen_mag_pin2;
++ struct interrupt_enable gen_mag;
++ struct interrupt_value gen_mag_threshold;
++ struct interrupt_enable gen1_axis[6];
++ struct interrupt_enable gen2_axis[6];
++ struct interrupt_enable gen_mag_axis[3];
++ struct interrupt_enable gen1_and_or;
++ struct interrupt_enable gen2_and_or;
++ struct interrupt_enable interrupt_pin_conf;
++ struct interrupt_enable interrupt_polarity;
++};
++
++struct lsm9ds0_status {
++ struct i2c_client *client;
++ struct lsm9ds0_acc_platform_data *pdata_acc;
++ struct lsm9ds0_mag_platform_data *pdata_mag;
++
++ struct mutex lock;
++ struct work_struct input_work_acc;
++ struct work_struct input_work_mag;
++
++ struct hrtimer hr_timer_acc;
++ ktime_t ktime_acc;
++ struct hrtimer hr_timer_mag;
++ ktime_t ktime_mag;
++
++ struct input_dev *input_dev_acc;
++ struct input_dev *input_dev_mag;
++ struct input_dev *input_dev_temp;
++
++ struct lsm9ds0_interrupt *interrupt;
++
++ int hw_initialized;
++ /* hw_working=-1 means not tested yet */
++ int hw_working;
++
++ atomic_t enabled_acc;
++ atomic_t enabled_mag;
++ atomic_t enabled_temp;
++
++ int temp_value_dec;
++ unsigned int temp_value_flo;
++
++ int on_before_suspend;
++ int use_smbus;
++
++ u16 sensitivity_acc;
++ u16 sensitivity_mag;
++
++ int irq1;
++ struct work_struct irq1_work;
++ struct workqueue_struct *irq1_work_queue;
++ int irq2;
++ struct work_struct irq2_work;
++ struct workqueue_struct *irq2_work_queue;
++};
++
++static const struct lsm9ds0_acc_platform_data default_lsm9ds0_acc_pdata = {
++ .fs_range = LSM9DS0_ACC_FS_2G,
++ .rot_matrix = {
++ {1, 0, 0},
++ {0, 1, 0},
++ {0, 0, 1},
++ },
++ .poll_interval = 100,
++ .min_interval = LSM9DS0_ACC_MIN_POLL_PERIOD_MS,
++ .aa_filter_bandwidth = ANTI_ALIASING_773,
++ .gpio_int1 = DEFAULT_INT1_GPIO,
++ .gpio_int2 = DEFAULT_INT2_GPIO,
++};
++
++static const struct lsm9ds0_mag_platform_data default_lsm9ds0_mag_pdata = {
++ .poll_interval = 100,
++ .min_interval = LSM9DS0_MAG_MIN_POLL_PERIOD_MS,
++ .fs_range = LSM9DS0_MAG_FS_2G,
++ .rot_matrix = {
++ {1, 0, 0},
++ {0, 1, 0},
++ {0, 0, 1},
++ },
++};
++
++struct reg_rw {
++ u8 address;
++ u8 default_value;
++ u8 resume_value;
++};
++
++struct reg_r {
++ u8 address;
++ u8 value;
++};
++
++static struct status_registers {
++ struct reg_r who_am_i;
++ struct reg_rw cntrl0;
++ struct reg_rw cntrl1;
++ struct reg_rw cntrl2;
++ struct reg_rw cntrl3;
++ struct reg_rw cntrl4;
++ struct reg_rw cntrl5;
++ struct reg_rw cntrl6;
++ struct reg_rw cntrl7;
++ struct reg_rw int_ctrl_reg_m;
++ struct reg_rw int_mag_threshold_low;
++ struct reg_rw int_mag_threshold_high;
++ struct reg_rw int_gen1_reg;
++ struct reg_rw int_gen2_reg;
++ struct reg_rw int_gen1_duration;
++ struct reg_rw int_gen2_duration;
++ struct reg_rw int_gen1_threshold;
++ struct reg_rw int_gen2_threshold;
++ struct reg_r int_src_reg_m;
++ struct reg_r int_gen1_src;
++ struct reg_r int_gen2_src;
++ struct reg_r int_gen_mag_src;
++} status_registers = {
++ .who_am_i.address=REG_WHOAMI_ADDR, .who_am_i.value=WHOIAM_VALUE,
++ .cntrl0.address=REG_CNTRL0_ADDR, .cntrl0.default_value=REG_DEF_CNTRL0,
++ .cntrl1.address=REG_CNTRL1_ADDR, .cntrl1.default_value=REG_DEF_CNTRL1,
++ .cntrl2.address=REG_CNTRL2_ADDR, .cntrl2.default_value=REG_DEF_CNTRL2,
++ .cntrl3.address=REG_CNTRL3_ADDR, .cntrl3.default_value=REG_DEF_CNTRL3,
++ .cntrl4.address=REG_CNTRL4_ADDR, .cntrl4.default_value=REG_DEF_CNTRL4,
++ .cntrl5.address=REG_CNTRL5_ADDR, .cntrl5.default_value=REG_DEF_CNTRL5,
++ .cntrl6.address=REG_CNTRL6_ADDR, .cntrl6.default_value=REG_DEF_CNTRL6,
++ .cntrl7.address=REG_CNTRL7_ADDR, .cntrl7.default_value=REG_DEF_CNTRL7,
++ .int_ctrl_reg_m.address=REG_GEN_MAG_ADDR,
++ .int_ctrl_reg_m.default_value=REG_DEF_INT_CNTRL_MAG,
++ .int_mag_threshold_low.address=REG_GEN_MAG_THR_ADDR,
++ .int_mag_threshold_low.default_value=REG_DEF_MIG_THRESHOLD_L,
++ .int_mag_threshold_low.address=MIG_THRESHOLD_ADDR_H,
++ .int_mag_threshold_low.default_value=REG_DEF_MIG_THRESHOLD_H,
++ .int_gen1_reg.address=REG_GEN1_AXIS_ADDR,
++ .int_gen1_reg.default_value=REG_DEF_INT_GEN1,
++ .int_gen2_reg.address=REG_GEN2_AXIS_ADDR,
++ .int_gen2_reg.default_value=REG_DEF_INT_GEN2,
++ .int_gen1_duration.address=REG_GEN1_DUR_ADDR,
++ .int_gen1_duration.default_value=REG_DEF_IIG1_DURATION,
++ .int_gen2_duration.address=REG_GEN2_DUR_ADDR,
++ .int_gen2_duration.default_value=REG_DEF_IIG2_DURATION,
++ .int_gen1_threshold.address=REG_GEN1_THR_ADDR,
++ .int_gen1_threshold.default_value=REG_DEF_IIG1_THRESHOLD,
++ .int_gen2_threshold.address=REG_GEN2_THR_ADDR,
++ .int_gen2_threshold.default_value=REG_DEF_IIG2_THRESHOLD,
++ .int_src_reg_m.address = INT_SRC_REG_M_ADDR,
++ .int_src_reg_m.value = REG_DEF_ALL_ZEROS,
++ .int_gen1_src.address = INT_GEN1_SRC_ADDR,
++ .int_gen1_src.value = REG_DEF_ALL_ZEROS,
++ .int_gen2_src.address = INT_GEN2_SRC_ADDR,
++ .int_gen2_src.value = REG_DEF_ALL_ZEROS,
++ .int_gen_mag_src.address = INT_SRC_REG_M_ADDR,
++ .int_gen_mag_src.value = REG_DEF_ALL_ZEROS,
++};
++
++static int lsm9ds0_i2c_read(struct lsm9ds0_status *stat, u8 *buf, int len)
++{
++ int ret;
++ u8 reg = buf[0];
++ u8 cmd = reg;
++#ifdef DEBUG
++ unsigned int ii;
++#endif
++
++
++ if (len > 1)
++ cmd = (I2C_AUTO_INCREMENT | reg);
++ if (stat->use_smbus) {
++ if (len == 1) {
++ ret = i2c_smbus_read_byte_data(stat->client, cmd);
++ buf[0] = ret & 0xff;
++#ifdef DEBUG
++ dev_warn(&stat->client->dev,
++ "i2c_smbus_read_byte_data: ret=0x%02x, len:%d ,"
++ "command=0x%02x, buf[0]=0x%02x\n",
++ ret, len, cmd , buf[0]);
++#endif
++ } else if (len > 1) {
++ ret = i2c_smbus_read_i2c_block_data(stat->client,
++ cmd, len, buf);
++#ifdef DEBUG
++ dev_warn(&stat->client->dev,
++ "i2c_smbus_read_i2c_block_data: ret:%d len:%d, "
++ "command=0x%02x, ",
++ ret, len, cmd);
++ for (ii = 0; ii < len; ii++)
++ printk(KERN_DEBUG "buf[%d]=0x%02x,",
++ ii, buf[ii]);
++
++ printk("\n");
++#endif
++ } else
++ ret = -1;
++
++ if (ret < 0) {
++ dev_err(&stat->client->dev,
++ "read transfer error: len:%d, command=0x%02x\n",
++ len, cmd);
++ return 0;
++ }
++ return len;
++ }
++
++ ret = i2c_master_send(stat->client, &cmd, sizeof(cmd));
++ if (ret != sizeof(cmd))
++ return ret;
++
++ return i2c_master_recv(stat->client, buf, len);
++}
++
++static int lsm9ds0_i2c_write(struct lsm9ds0_status *stat, u8 *buf, int len)
++{
++ int ret;
++ u8 reg, value;
++#ifdef DEBUG
++ unsigned int ii;
++#endif
++
++ if (len > 1)
++ buf[0] = (I2C_AUTO_INCREMENT | buf[0]);
++
++ reg = buf[0];
++ value = buf[1];
++
++ if (stat->use_smbus) {
++ if (len == 1) {
++ ret = i2c_smbus_write_byte_data(stat->client,
++ reg, value);
++#ifdef DEBUG
++ dev_warn(&stat->client->dev,
++ "i2c_smbus_write_byte_data: ret=%d, len:%d, "
++ "command=0x%02x, value=0x%02x\n",
++ ret, len, reg , value);
++#endif
++ return ret;
++ } else if (len > 1) {
++ ret = i2c_smbus_write_i2c_block_data(stat->client,
++ reg, len, buf + 1);
++#ifdef DEBUG
++ dev_warn(&stat->client->dev,
++ "i2c_smbus_write_i2c_block_data: ret=%d, "
++ "len:%d, command=0x%02x, ",
++ ret, len, reg);
++ for (ii = 0; ii < (len + 1); ii++)
++ printk(KERN_DEBUG "value[%d]=0x%02x,",
++ ii, buf[ii]);
++
++ printk("\n");
++#endif
++ return ret;
++ }
++ }
++
++ ret = i2c_master_send(stat->client, buf, len+1);
++ return (ret == len+1) ? 0 : ret;
++}
++
++static int lsm9ds0_hw_init(struct lsm9ds0_status *stat)
++{
++ int err = -1;
++ u8 buf[1];
++ int i;
++
++ pr_info("%s: hw init start\n", LSM9DS0_DEV_NAME);
++
++ buf[0] = status_registers.who_am_i.address;
++ err = lsm9ds0_i2c_read(stat, buf, 1);
++
++ if (err < 0) {
++ dev_warn(&stat->client->dev, "Error reading WHO_AM_I: is device"
++ " available/working?\n");
++ goto err_firstread;
++ } else
++ stat->hw_working = 1;
++
++ if (buf[0] != status_registers.who_am_i.value) {
++ dev_err(&stat->client->dev,
++ "device unknown. Expected: 0x%02x,"
++ " Replies: 0x%02x\n", status_registers.who_am_i.value, buf[0]);
++ err = -1;
++ goto err_unknown_device;
++ }
++
++ status_registers.cntrl1.resume_value =
++ status_registers.cntrl1.default_value;
++ status_registers.cntrl2.resume_value =
++ status_registers.cntrl2.default_value;
++ status_registers.cntrl3.resume_value =
++ status_registers.cntrl3.default_value;
++ status_registers.cntrl4.resume_value =
++ status_registers.cntrl4.default_value;
++ status_registers.cntrl5.resume_value =
++ status_registers.cntrl5.default_value;
++ status_registers.cntrl6.resume_value =
++ status_registers.cntrl6.default_value;
++ status_registers.cntrl7.resume_value =
++ status_registers.cntrl7.default_value;
++
++ status_registers.int_ctrl_reg_m.resume_value =
++ status_registers.int_ctrl_reg_m.default_value;
++ status_registers.int_mag_threshold_low.resume_value =
++ status_registers.int_mag_threshold_low.default_value;
++ status_registers.int_mag_threshold_high.resume_value =
++ status_registers.int_mag_threshold_high.default_value;
++ status_registers.int_gen1_reg.resume_value =
++ status_registers.int_gen1_reg.default_value;
++ status_registers.int_gen2_reg.resume_value =
++ status_registers.int_gen2_reg.default_value;
++ status_registers.int_gen1_duration.resume_value =
++ status_registers.int_gen1_duration.default_value;
++ status_registers.int_gen2_duration.resume_value =
++ status_registers.int_gen2_duration.default_value;
++ status_registers.int_gen1_threshold.resume_value =
++ status_registers.int_gen1_threshold.default_value;
++ status_registers.int_gen2_threshold.resume_value =
++ status_registers.int_gen2_threshold.default_value;
++
++
++ stat->temp_value_dec = NDTEMP;
++
++ if((stat->pdata_acc->gpio_int1 >= 0) ||
++ (stat->pdata_acc->gpio_int2 >= 0)) {
++
++ stat->interrupt = kmalloc(sizeof(*stat->interrupt),
++ GFP_KERNEL);
++
++ if(stat->interrupt == NULL)
++ goto error_interrupt;
++
++ stat->interrupt->gen1_pin1.address = REG_CNTRL3_ADDR;
++ stat->interrupt->gen1_pin2.address = REG_CNTRL4_ADDR;
++ stat->interrupt->gen2_pin1.address = REG_CNTRL3_ADDR;
++ stat->interrupt->gen2_pin2.address = REG_CNTRL4_ADDR;
++ stat->interrupt->gen_mag_pin1.address = REG_CNTRL3_ADDR;
++ stat->interrupt->gen_mag_pin2.address = REG_CNTRL4_ADDR;
++ stat->interrupt->gen_mag.address = REG_GEN_MAG_ADDR;
++ stat->interrupt->gen1_duration.address = REG_GEN1_DUR_ADDR;
++ stat->interrupt->gen2_duration.address = REG_GEN2_DUR_ADDR;
++ stat->interrupt->gen1_threshold.address = REG_GEN1_THR_ADDR;
++ stat->interrupt->gen2_threshold.address = REG_GEN2_THR_ADDR;
++ stat->interrupt->gen_mag_threshold.address =
++ REG_GEN_MAG_THR_ADDR;
++
++ stat->interrupt->gen1_pin1.mask = GEN1_PIN1_MASK;
++ stat->interrupt->gen1_pin2.mask = GEN1_PIN2_MASK;
++ stat->interrupt->gen2_pin1.mask = GEN2_PIN1_MASK;
++ stat->interrupt->gen2_pin2.mask = GEN2_PIN2_MASK;
++ stat->interrupt->gen_mag_pin1.mask = GEN_MAG_PIN1_MASK;
++ stat->interrupt->gen_mag_pin2.mask = GEN_MAG_PIN2_MASK;
++ stat->interrupt->gen_mag.mask = GEN_MAG_EN_MASK;
++
++ atomic_set(&stat->interrupt->gen1_pin1.enable, 0);
++ atomic_set(&stat->interrupt->gen1_pin2.enable, 0);
++ atomic_set(&stat->interrupt->gen2_pin1.enable, 0);
++ atomic_set(&stat->interrupt->gen2_pin2.enable, 0);
++ atomic_set(&stat->interrupt->gen_mag_pin1.enable, 0);
++ atomic_set(&stat->interrupt->gen_mag_pin2.enable, 0);
++ atomic_set(&stat->interrupt->gen_mag.enable, 0);
++
++ stat->interrupt->gen1_threshold.value = 0;
++ stat->interrupt->gen2_threshold.value = 0;
++ stat->interrupt->gen1_duration.value = 0;
++ stat->interrupt->gen2_duration.value = 0;
++ stat->interrupt->gen_mag_threshold.value = 0;
++
++ for(i=0; i<6; i++) {
++ stat->interrupt->gen1_axis[i].address =
++ REG_GEN1_AXIS_ADDR;
++ stat->interrupt->gen2_axis[i].address =
++ REG_GEN2_AXIS_ADDR;
++
++ atomic_set(&stat->interrupt->gen1_axis[i].enable, 0);
++ atomic_set(&stat->interrupt->gen2_axis[i].enable, 0);
++ }
++ for(i=0; i<3; i++) {
++ stat->interrupt->gen_mag_axis[i].address =
++ REG_GEN_MAG_ADDR;
++ atomic_set(&stat->interrupt->gen_mag_axis[i].enable, 0);
++ }
++
++ stat->interrupt->gen1_axis[0].mask = GEN_X_LOW_MASK;
++ stat->interrupt->gen1_axis[1].mask = GEN_Y_LOW_MASK;
++ stat->interrupt->gen1_axis[2].mask = GEN_Z_LOW_MASK;
++ stat->interrupt->gen1_axis[3].mask = GEN_X_HIGH_MASK;
++ stat->interrupt->gen1_axis[4].mask = GEN_Y_HIGH_MASK;
++ stat->interrupt->gen1_axis[5].mask = GEN_Z_HIGH_MASK;
++
++ stat->interrupt->gen2_axis[0].mask = GEN_X_LOW_MASK;
++ stat->interrupt->gen2_axis[1].mask = GEN_Y_LOW_MASK;
++ stat->interrupt->gen2_axis[2].mask = GEN_Z_LOW_MASK;
++ stat->interrupt->gen2_axis[3].mask = GEN_X_HIGH_MASK;
++ stat->interrupt->gen2_axis[4].mask = GEN_Y_HIGH_MASK;
++ stat->interrupt->gen2_axis[5].mask = GEN_Z_HIGH_MASK;
++
++ stat->interrupt->gen_mag_axis[0].mask = GEN_X_MAG_MASK;
++ stat->interrupt->gen_mag_axis[1].mask = GEN_Y_MAG_MASK;
++ stat->interrupt->gen_mag_axis[2].mask = GEN_Z_MAG_MASK;
++
++ stat->interrupt->gen1_and_or.address = REG_GEN1_AXIS_ADDR;
++ stat->interrupt->gen1_and_or.mask = GEN1_AND_OR_MASK;
++ atomic_set(&stat->interrupt->gen1_and_or.enable, 0);
++ stat->interrupt->gen2_and_or.address = REG_GEN1_DUR_ADDR;
++ stat->interrupt->gen2_and_or.mask = GEN2_AND_OR_MASK;
++ atomic_set(&stat->interrupt->gen2_and_or.enable, 0);
++
++ stat->interrupt->interrupt_pin_conf.address = REG_GEN_MAG_ADDR;
++ stat->interrupt->interrupt_pin_conf.mask = INT_PIN_CONF_MASK;
++ atomic_set(&stat->interrupt->interrupt_pin_conf.enable, 0);
++
++ stat->interrupt->interrupt_polarity.address = REG_GEN_MAG_ADDR;
++ stat->interrupt->interrupt_polarity.mask = INT_POLARITY_MASK;
++ atomic_set(&stat->interrupt->interrupt_polarity.enable, 0);
++ }
++
++ stat->hw_initialized = 1;
++ pr_info("%s: hw init done\n", LSM9DS0_DEV_NAME);
++
++ return 0;
++
++error_interrupt:
++err_unknown_device:
++err_firstread:
++ stat->hw_working = 0;
++ stat->hw_initialized = 0;
++ return err;
++}
++
++static irqreturn_t lsm9ds0_isr1(int irq, void *dev)
++{
++ struct lsm9ds0_status *stat = dev;
++
++ disable_irq_nosync(irq);
++ queue_work(stat->irq1_work_queue, &stat->irq1_work);
++ pr_debug("%s: isr1 queued\n", LSM9DS0_DEV_NAME);
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t lsm9ds0_isr2(int irq, void *dev)
++{
++ struct lsm9ds0_status *stat = dev;
++
++ disable_irq_nosync(irq);
++ queue_work(stat->irq2_work_queue, &stat->irq2_work);
++ pr_debug("%s: isr2 queued\n", LSM9DS0_DEV_NAME);
++ return IRQ_HANDLED;
++}
++
++static void lsm9ds0_interrupt_catch(struct lsm9ds0_status *stat, int pin )
++{
++ u8 buf[2];
++ u8 val;
++
++ if(atomic_read(&stat->interrupt->gen1_pin1.enable) == 1) {
++ buf[0] = status_registers.int_gen1_src.address;
++ val = lsm9ds0_i2c_read(stat, buf, 1);
++ if(val < 0)
++ return;
++ status_registers.int_gen1_src.value = buf[0];
++
++ if(((int)status_registers.int_gen1_src.value) > 64)
++ pr_info("interrupt send by accelerometer interrupt "
++ "generator 1\n");
++ }
++ if(atomic_read(&stat->interrupt->gen_mag_pin1.enable) == 1) {
++ buf[0] = status_registers.int_gen_mag_src.address;
++ val = lsm9ds0_i2c_read(stat, buf, 1);
++ if(val < 0)
++ return;
++ status_registers.int_gen_mag_src.value = buf[0];
++
++ if(((int)status_registers.int_gen_mag_src.value) > 1)
++ pr_info("interrupt send by magnetometer interrupt "
++ "generator\n");
++ }
++
++}
++
++static void lsm9ds0_irq1_work_func(struct work_struct *work)
++{
++
++ struct lsm9ds0_status *stat =
++ container_of(work, struct lsm9ds0_status, irq1_work);
++ /* TODO add interrupt service procedure.
++ ie:lsm9ds0_get_int1_source(stat); */
++
++ lsm9ds0_interrupt_catch(stat,1);
++ pr_info("%s: IRQ1 triggered\n", LSM9DS0_DEV_NAME);
++exit:
++ enable_irq(stat->irq1);
++}
++
++static void lsm9ds0_irq2_work_func(struct work_struct *work)
++{
++
++ struct lsm9ds0_status *stat =
++ container_of(work, struct lsm9ds0_status, irq2_work);
++ /* TODO add interrupt service procedure.
++ ie:lsm9ds0_get_int2_source(stat); */
++
++ lsm9ds0_interrupt_catch(stat,2);
++ pr_info("%s: IRQ2 triggered\n", LSM9DS0_DEV_NAME);
++exit:
++ enable_irq(stat->irq2);
++}
++
++static int lsm9ds0_acc_device_power_off(struct lsm9ds0_status *stat)
++{
++ int err;
++ u8 buf[2];
++
++ buf[0] = status_registers.cntrl1.address;
++ buf[1] = ((ODR_ACC_MASK & LSM9DS0_ACC_ODR_OFF) |
++ ((~ODR_ACC_MASK) & status_registers.cntrl1.resume_value));
++
++ err = lsm9ds0_i2c_write(stat, buf, 1);
++ if (err < 0)
++ dev_err(&stat->client->dev, "accelerometer soft power off "
++ "failed: %d\n", err);
++
++ if (stat->pdata_acc->power_off) {
++ stat->pdata_acc->power_off();
++ }
++
++ atomic_set(&stat->enabled_acc, 0);
++
++ return 0;
++}
++
++static int lsm9ds0_mag_device_power_off(struct lsm9ds0_status *stat)
++{
++ int err;
++ u8 buf[2];
++
++ buf[0] = status_registers.cntrl7.address;
++ buf[1] = ((MSMS_MASK & POWEROFF_MAG) |
++ ((~MSMS_MASK) & status_registers.cntrl7.resume_value));
++
++ err = lsm9ds0_i2c_write(stat, buf, 1);
++ if (err < 0)
++ dev_err(&stat->client->dev, "magnetometer soft power off "
++ "failed: %d\n", err);
++
++ if (stat->pdata_mag->power_off) {
++ stat->pdata_mag->power_off();
++ }
++
++ atomic_set(&stat->enabled_mag, 0);
++
++ return 0;
++}
++
++static int lsm9ds0_acc_device_power_on(struct lsm9ds0_status *stat)
++{
++ int err = -1;
++ u8 buf[5];
++
++ if (stat->pdata_acc->power_on) {
++ err = stat->pdata_acc->power_on();
++ if (err < 0) {
++ dev_err(&stat->client->dev,
++ "accelerometer power_on failed: %d\n", err);
++ return err;
++ }
++ }
++
++ buf[0] = status_registers.cntrl0.address;
++ buf[1] = status_registers.cntrl0.resume_value;
++ err = lsm9ds0_i2c_write(stat, buf, 1);
++ if (err < 0)
++ goto err_resume_state;
++
++ buf[0] = status_registers.cntrl1.address;
++ buf[1] = status_registers.cntrl1.resume_value;
++ buf[2] = status_registers.cntrl2.resume_value;
++ buf[3] = status_registers.cntrl3.resume_value;
++ buf[4] = status_registers.cntrl4.resume_value;
++ err = lsm9ds0_i2c_write(stat, buf, 4);
++ if (err < 0)
++ goto err_resume_state;
++
++ buf[0] = status_registers.int_gen1_reg.address;
++ buf[1] = status_registers.int_gen1_reg.resume_value;
++ err = lsm9ds0_i2c_write(stat, buf, 1);
++ if (err < 0)
++ goto err_resume_state;
++
++ buf[0] = status_registers.int_gen1_threshold.address;
++ buf[1] = status_registers.int_gen1_threshold.resume_value;
++ buf[2] = status_registers.int_gen1_duration.resume_value;
++ err = lsm9ds0_i2c_write(stat, buf, 2);
++ if (err < 0)
++ goto err_resume_state;
++
++ buf[0] = status_registers.int_gen2_reg.address;
++ buf[1] = status_registers.int_gen2_reg.resume_value;
++ err = lsm9ds0_i2c_write(stat, buf, 1);
++ if (err < 0)
++ goto err_resume_state;
++
++ buf[0] = status_registers.int_gen2_threshold.address;
++ buf[1] = status_registers.int_gen2_threshold.resume_value;
++ buf[2] = status_registers.int_gen2_duration.resume_value;
++ err = lsm9ds0_i2c_write(stat, buf, 2);
++ if (err < 0)
++ goto err_resume_state;
++
++ atomic_set(&stat->enabled_acc, 1);
++
++ return 0;
++
++err_resume_state:
++ atomic_set(&stat->enabled_acc, 0);
++ dev_err(&stat->client->dev, "accelerometer hw power on error "
++ "0x%02x,0x%02x: %d\n", buf[0], buf[1], err);
++ return err;
++}
++
++static int lsm9ds0_mag_device_power_on(struct lsm9ds0_status *stat)
++{
++ int err = -1;
++ u8 buf[6];
++
++ if (stat->pdata_mag->power_on) {
++ err = stat->pdata_mag->power_on();
++ if (err < 0) {
++ dev_err(&stat->client->dev,
++ "magnetometer power_on failed: %d\n", err);
++ return err;
++ }
++ }
++
++ buf[0] = status_registers.cntrl0.address;
++ buf[1] = status_registers.cntrl0.resume_value;
++ err = lsm9ds0_i2c_write(stat, buf, 1);
++ if (err < 0)
++ goto err_resume_state;
++
++ buf[0] = status_registers.cntrl3.address;
++ buf[1] = status_registers.cntrl3.resume_value;
++ buf[2] = status_registers.cntrl4.resume_value;
++ buf[3] = status_registers.cntrl5.resume_value;
++ buf[4] = status_registers.cntrl6.resume_value;
++
++ err = lsm9ds0_i2c_write(stat, buf, 4);
++ if (err < 0)
++ goto err_resume_state;
++
++ buf[0] = status_registers.int_ctrl_reg_m.address;
++ buf[1] = status_registers.int_ctrl_reg_m.resume_value;
++ err = lsm9ds0_i2c_write(stat, buf, 1);
++ if (err < 0)
++ goto err_resume_state;
++
++ buf[0] = status_registers.int_mag_threshold_low.address;
++ buf[1] = status_registers.int_mag_threshold_low.resume_value;
++ buf[2] = status_registers.int_mag_threshold_high.resume_value;
++ err = lsm9ds0_i2c_write(stat, buf, 2);
++ if (err < 0)
++ goto err_resume_state;
++
++ buf[0] = status_registers.cntrl7.address;
++ buf[1] = ((MSMS_MASK & CONTINUOS_CONVERSION) |
++ ((~MSMS_MASK) & status_registers.cntrl7.resume_value));
++ err = lsm9ds0_i2c_write(stat, buf, 1);
++ if (err < 0)
++ goto err_resume_state;
++
++ atomic_set(&stat->enabled_mag, 1);
++
++ return 0;
++
++err_resume_state:
++ atomic_set(&stat->enabled_mag, 0);
++ dev_err(&stat->client->dev, "magnetometer hw power on error "
++ "0x%02x,0x%02x: %d\n", buf[0], buf[1], err);
++ return err;
++}
++
++static int lsm9ds0_acc_update_filter(struct lsm9ds0_status *stat,
++ u8 new_bandwidth)
++{
++ int err=-1;
++
++ u8 updated_val;
++ u8 buf[2];
++
++ switch (new_bandwidth) {
++ case ANTI_ALIASING_50:
++ break;
++ case ANTI_ALIASING_194:
++ break;
++ case ANTI_ALIASING_362:
++ break;
++ case ANTI_ALIASING_773:
++ break;
++ default:
++ dev_err(&stat->client->dev, "invalid accelerometer "
++ "update bandwidth requested: %u\n", new_bandwidth);
++ return -EINVAL;
++ }
++
++ buf[0] = status_registers.cntrl2.address;
++ err = lsm9ds0_i2c_read(stat, buf, 1);
++ if (err < 0)
++ goto error;
++
++ status_registers.cntrl2.resume_value = buf[0];
++ updated_val = ((LSM9DS0_ACC_FILTER_MASK & new_bandwidth) |
++ ((~LSM9DS0_ACC_FILTER_MASK) & buf[0]));
++ buf[1] = updated_val;
++ buf[0] = status_registers.cntrl2.address;
++
++ err = lsm9ds0_i2c_write(stat, buf, 1);
++ if (err < 0)
++ goto error;
++ status_registers.cntrl2.resume_value = updated_val;
++
++ return err;
++
++error:
++ dev_err(&stat->client->dev, "update accelerometer fs range failed "
++ "0x%02x,0x%02x: %d\n", buf[0], buf[1], err);
++ return err;
++}
++
++static int lsm9ds0_acc_update_fs_range(struct lsm9ds0_status *stat,
++ u8 new_fs_range)
++{
++ int err=-1;
++
++ u16 sensitivity;
++ u8 updated_val;
++ u8 buf[2];
++
++ switch (new_fs_range) {
++ case LSM9DS0_ACC_FS_2G:
++ sensitivity = SENSITIVITY_ACC_2G;
++ break;
++ case LSM9DS0_ACC_FS_4G:
++ sensitivity = SENSITIVITY_ACC_4G;
++ break;
++ case LSM9DS0_ACC_FS_8G:
++ sensitivity = SENSITIVITY_ACC_8G;
++ break;
++ case LSM9DS0_ACC_FS_16G:
++ sensitivity = SENSITIVITY_ACC_16G;
++ break;
++ default:
++ dev_err(&stat->client->dev, "invalid accelerometer "
++ "fs range requested: %u\n", new_fs_range);
++ return -EINVAL;
++ }
++
++ buf[0] = status_registers.cntrl2.address;
++ err = lsm9ds0_i2c_read(stat, buf, 1);
++ if (err < 0)
++ goto error;
++
++ status_registers.cntrl2.resume_value = buf[0];
++ updated_val = ((LSM9DS0_ACC_FS_MASK & new_fs_range) |
++ ((~LSM9DS0_ACC_FS_MASK) & buf[0]));
++ buf[1] = updated_val;
++ buf[0] = status_registers.cntrl2.address;
++
++ err = lsm9ds0_i2c_write(stat, buf, 1);
++ if (err < 0)
++ goto error;
++ status_registers.cntrl2.resume_value = updated_val;
++ stat->sensitivity_acc = sensitivity;
++
++ return err;
++
++error:
++ dev_err(&stat->client->dev, "update accelerometer fs range failed "
++ "0x%02x,0x%02x: %d\n", buf[0], buf[1], err);
++ return err;
++}
++
++static int lsm9ds0_mag_update_fs_range(struct lsm9ds0_status *stat,
++ u8 new_fs_range)
++{
++ int err=-1;
++
++ u16 sensitivity;
++ u8 updated_val;
++ u8 buf[2];
++
++ switch (new_fs_range) {
++ case LSM9DS0_MAG_FS_2G:
++ sensitivity = SENSITIVITY_MAG_2G;
++ break;
++ case LSM9DS0_MAG_FS_4G:
++ sensitivity = SENSITIVITY_MAG_4G;
++ break;
++ case LSM9DS0_MAG_FS_8G:
++ sensitivity = SENSITIVITY_MAG_8G;
++ break;
++ case LSM9DS0_MAG_FS_12G:
++ sensitivity = SENSITIVITY_MAG_12G;
++ break;
++ default:
++ dev_err(&stat->client->dev, "invalid magnetometer "
++ "fs range requested: %u\n", new_fs_range);
++ return -EINVAL;
++ }
++
++ buf[0] = status_registers.cntrl6.address;
++ err = lsm9ds0_i2c_read(stat, buf, 1);
++ if (err < 0)
++ goto error;
++
++ status_registers.cntrl6.resume_value = buf[0];
++ updated_val = (LSM9DS0_MAG_FS_MASK & new_fs_range);
++ buf[1] = updated_val;
++ buf[0] = status_registers.cntrl6.address;
++
++ err = lsm9ds0_i2c_write(stat, buf, 1);
++ if (err < 0)
++ goto error;
++ status_registers.cntrl6.resume_value = updated_val;
++ stat->sensitivity_mag = sensitivity;
++
++ return err;
++
++error:
++ dev_err(&stat->client->dev, "update magnetometer fs range failed "
++ "0x%02x,0x%02x: %d\n", buf[0], buf[1], err);
++ return err;
++}
++
++static int lsm9ds0_acc_update_odr(struct lsm9ds0_status *stat,
++ unsigned int poll_interval_ms)
++{
++ int err = -1;
++ u8 config[2];
++ int i;
++
++ for (i = ARRAY_SIZE(lsm9ds0_acc_odr_table) - 1; i >= 0; i--) {
++ if ((lsm9ds0_acc_odr_table[i].cutoff_us <= poll_interval_ms)
++ || (i == 0))
++ break;
++ }
++
++ config[1] = ((ODR_ACC_MASK & lsm9ds0_acc_odr_table[i].value) |
++ ((~ODR_ACC_MASK) & status_registers.cntrl1.resume_value));
++
++ if (atomic_read(&stat->enabled_acc)) {
++ config[0] = status_registers.cntrl1.address;
++ err = lsm9ds0_i2c_write(stat, config, 1);
++ if (err < 0)
++ goto error;
++ status_registers.cntrl1.resume_value = config[1];
++ stat->ktime_acc = ktime_set(0, MS_TO_NS(poll_interval_ms));
++ }
++
++ return err;
++
++error:
++ dev_err(&stat->client->dev, "update accelerometer odr failed "
++ "0x%02x,0x%02x: %d\n", config[0], config[1], err);
++
++ return err;
++}
++
++static int lsm9ds0_mag_update_odr(struct lsm9ds0_status *stat,
++ unsigned int poll_interval_ms)
++{
++ int err = -1;
++ u8 config[2];
++ int i;
++
++ for (i = ARRAY_SIZE(lsm9ds0_mag_odr_table) - 1; i >= 0; i--) {
++ if ((lsm9ds0_mag_odr_table[i].cutoff_us <= poll_interval_ms)
++ || (i == 0))
++ break;
++ }
++
++ config[1] = ((ODR_MAG_MASK & lsm9ds0_mag_odr_table[i].value) |
++ ((~ODR_MAG_MASK) & status_registers.cntrl5.resume_value));
++
++ if (atomic_read(&stat->enabled_mag)) {
++ config[0] = status_registers.cntrl5.address;
++ err = lsm9ds0_i2c_write(stat, config, 1);
++ if (err < 0)
++ goto error;
++ status_registers.cntrl5.resume_value = config[1];
++ stat->ktime_mag = ktime_set(0, MS_TO_NS(poll_interval_ms));
++ }
++
++ return err;
++
++error:
++ dev_err(&stat->client->dev, "update magnetometer odr failed "
++ "0x%02x,0x%02x: %d\n", config[0], config[1], err);
++
++ return err;
++}
++
++static void lsm9ds0_validate_polling(unsigned int *min_interval,
++ unsigned int *poll_interval,
++ unsigned int min,
++ struct i2c_client *client)
++{
++ *min_interval = max(min, *min_interval);
++ *poll_interval = max(*poll_interval, *min_interval);
++}
++
++static int lsm9ds0_acc_validate_pdata(struct lsm9ds0_status *stat)
++{
++ int res = -EINVAL;
++
++ lsm9ds0_validate_polling(&stat->pdata_acc->min_interval,
++ &stat->pdata_acc->poll_interval,
++ (unsigned int)LSM9DS0_ACC_MIN_POLL_PERIOD_MS,
++ stat->client);
++
++ switch (stat->pdata_acc->aa_filter_bandwidth) {
++ case ANTI_ALIASING_50:
++ res = 1;
++ break;
++ case ANTI_ALIASING_194:
++ res = 1;
++ break;
++ case ANTI_ALIASING_362:
++ res = 1;
++ break;
++ case ANTI_ALIASING_773:
++ res = 1;
++ break;
++ default:
++ dev_err(&stat->client->dev, "invalid accelerometer "
++ "bandwidth selected: %u\n",
++ stat->pdata_acc->aa_filter_bandwidth);
++ }
++
++ return res;
++}
++
++static int lsm9ds0_mag_validate_pdata(struct lsm9ds0_status *stat)
++{
++ lsm9ds0_validate_polling(&stat->pdata_mag->min_interval,
++ &stat->pdata_mag->poll_interval,
++ (unsigned int)LSM9DS0_MAG_MIN_POLL_PERIOD_MS,
++ stat->client);
++
++ return 0;
++}
++
++static int lsm9ds0_acc_enable(struct lsm9ds0_status *stat)
++{
++ int err;
++
++ if (!atomic_cmpxchg(&stat->enabled_acc, 0, 1)) {
++ err = lsm9ds0_acc_device_power_on(stat);
++ if (err < 0) {
++ atomic_set(&stat->enabled_acc, 0);
++ return err;
++ }
++ hrtimer_start(&stat->hr_timer_acc, stat->ktime_acc, HRTIMER_MODE_REL);
++ if(!atomic_read(&stat->enabled_mag)) {
++ if(stat->pdata_acc->gpio_int1 >= 0)
++ enable_irq(stat->irq1);
++ if(stat->pdata_acc->gpio_int2 >= 0)
++ enable_irq(stat->irq2);
++ }
++ }
++
++ return 0;
++}
++
++static int lsm9ds0_acc_disable(struct lsm9ds0_status *stat)
++{
++ if (atomic_cmpxchg(&stat->enabled_acc, 1, 0)) {
++ cancel_work_sync(&stat->input_work_acc);
++ hrtimer_cancel(&stat->hr_timer_acc);
++ lsm9ds0_acc_device_power_off(stat);
++
++ if(!atomic_read(&stat->enabled_mag)) {
++ if(stat->pdata_acc->gpio_int1 >= 0)
++ disable_irq_nosync(stat->irq1);
++ if(stat->pdata_acc->gpio_int2 >= 0)
++ disable_irq_nosync(stat->irq2);
++ }
++ }
++
++ return 0;
++}
++
++static int lsm9ds0_mag_enable(struct lsm9ds0_status *stat)
++{
++ int err;
++
++ if (!atomic_cmpxchg(&stat->enabled_mag, 0, 1)) {
++ err = lsm9ds0_mag_device_power_on(stat);
++ if (err < 0) {
++ atomic_set(&stat->enabled_mag, 0);
++ return err;
++ }
++ if(!atomic_read(&stat->enabled_temp)) {
++ hrtimer_start(&stat->hr_timer_mag, stat->ktime_mag, HRTIMER_MODE_REL);
++ }
++ if(!atomic_read(&stat->enabled_acc)) {
++ if(stat->pdata_acc->gpio_int1 >= 0)
++ enable_irq(stat->irq1);
++ if(stat->pdata_acc->gpio_int2 >= 0)
++ enable_irq(stat->irq2);
++ }
++ }
++
++ return 0;
++}
++
++static int lsm9ds0_mag_disable(struct lsm9ds0_status *stat)
++{
++ if (atomic_cmpxchg(&stat->enabled_mag, 1, 0)) {
++ if(!atomic_read(&stat->enabled_temp)) {
++ cancel_work_sync(&stat->input_work_mag);
++ hrtimer_cancel(&stat->hr_timer_mag);
++ }
++ lsm9ds0_mag_device_power_off(stat);
++ if(!atomic_read(&stat->enabled_acc)) {
++ if(stat->pdata_acc->gpio_int1 >= 0)
++ disable_irq(stat->irq1);
++ if(stat->pdata_acc->gpio_int2 >= 0)
++ disable_irq(stat->irq2);
++ }
++ }
++
++ return 0;
++}
++
++static int lsm9ds0_temperature_enable(struct lsm9ds0_status *stat)
++{
++ int err;
++ u8 buf[2];
++ u8 updated_val;
++
++ buf[0] = status_registers.cntrl5.address;
++ err = lsm9ds0_i2c_read(stat, buf, 1);
++ if (err < 0)
++ goto error;
++
++ status_registers.cntrl5.resume_value = buf[0];
++ updated_val = ((TEMP_MASK & TEMP_ON) |
++ ((~TEMP_MASK) & buf[0]));
++ buf[1] = updated_val;
++ buf[0] = status_registers.cntrl5.address;
++
++ err = lsm9ds0_i2c_write(stat, buf, 1);
++ if (err < 0)
++ goto error;
++ status_registers.cntrl5.resume_value = updated_val;
++
++ if(!atomic_read(&stat->enabled_mag)) {
++ hrtimer_start(&stat->hr_timer_mag, stat->ktime_mag, HRTIMER_MODE_REL);
++ }
++ atomic_set(&stat->enabled_temp, 1);
++ return 0;
++
++error:
++ return -1;
++}
++
++static int lsm9ds0_temperature_disable(struct lsm9ds0_status *stat)
++{
++ int err;
++ u8 buf[2];
++ u8 updated_val;
++
++ buf[0] = status_registers.cntrl5.address;
++ err = lsm9ds0_i2c_read(stat, buf, 1);
++ if (err < 0)
++ goto error;
++
++ status_registers.cntrl5.resume_value = buf[0];
++ updated_val = ((TEMP_MASK & TEMP_OFF) |
++ ((~TEMP_MASK) & buf[0]));
++ buf[1] = updated_val;
++ buf[0] = status_registers.cntrl5.address;
++
++ err = lsm9ds0_i2c_write(stat, buf, 1);
++ if (err < 0)
++ goto error;
++ status_registers.cntrl5.resume_value = updated_val;
++
++ if(!atomic_read(&stat->enabled_mag)) {
++ cancel_work_sync(&stat->input_work_mag);
++ hrtimer_cancel(&stat->hr_timer_mag);
++ }
++ atomic_set(&stat->enabled_temp, 0);
++ stat->temp_value_dec = NDTEMP;
++ return 0;
++
++error:
++ return -1;
++}
++
++static void lsm9ds0_acc_input_cleanup(struct lsm9ds0_status *stat)
++{
++ input_unregister_device(stat->input_dev_acc);
++ input_free_device(stat->input_dev_acc);
++}
++
++static void lsm9ds0_mag_input_cleanup(struct lsm9ds0_status *stat)
++{
++ input_unregister_device(stat->input_dev_mag);
++ input_free_device(stat->input_dev_mag);
++}
++
++static ssize_t attr_get_polling_rate_acc(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ unsigned int val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ mutex_lock(&stat->lock);
++ val = stat->pdata_acc->poll_interval;
++ mutex_unlock(&stat->lock);
++ return sprintf(buf, "%u\n", val);
++}
++
++static ssize_t attr_get_polling_rate_mag(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ unsigned int val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ mutex_lock(&stat->lock);
++ val = stat->pdata_mag->poll_interval;
++ mutex_unlock(&stat->lock);
++ return sprintf(buf, "%u\n", val);
++}
++
++static ssize_t attr_set_polling_rate_acc(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ unsigned long interval_ms;
++
++ if (strict_strtoul(buf, 10, &interval_ms))
++ return -EINVAL;
++ if (!interval_ms)
++ return -EINVAL;
++ interval_ms = (unsigned int)max((unsigned int)interval_ms,
++ stat->pdata_acc->min_interval);
++ mutex_lock(&stat->lock);
++ stat->pdata_acc->poll_interval = (unsigned int)interval_ms;
++ lsm9ds0_acc_update_odr(stat, interval_ms);
++ mutex_unlock(&stat->lock);
++ return size;
++}
++
++static ssize_t attr_set_polling_rate_mag(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ unsigned long interval_ms;
++
++ if (strict_strtoul(buf, 10, &interval_ms))
++ return -EINVAL;
++ if (!interval_ms)
++ return -EINVAL;
++ interval_ms = (unsigned int)max((unsigned int)interval_ms,
++ stat->pdata_mag->min_interval);
++ mutex_lock(&stat->lock);
++ stat->pdata_mag->poll_interval = (unsigned int)interval_ms;
++ lsm9ds0_mag_update_odr(stat, interval_ms);
++ mutex_unlock(&stat->lock);
++ return size;
++}
++
++static ssize_t attr_get_enable_acc(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ int val = (int)atomic_read(&stat->enabled_acc);
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_get_enable_mag(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ int val = (int)atomic_read(&stat->enabled_mag);
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_enable_acc(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ unsigned long val;
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ if (val)
++ lsm9ds0_acc_enable(stat);
++ else
++ lsm9ds0_acc_disable(stat);
++
++ return size;
++}
++
++static ssize_t attr_set_enable_mag(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ unsigned long val;
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ if (val)
++ lsm9ds0_mag_enable(stat);
++ else
++ lsm9ds0_mag_disable(stat);
++
++ return size;
++}
++
++static ssize_t attr_get_range_acc(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ struct device *dev = to_dev(kobj->parent);
++ u8 val;
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ int range = 2;
++ mutex_lock(&stat->lock);
++ val = stat->pdata_acc->fs_range ;
++ switch (val) {
++ case LSM9DS0_ACC_FS_2G:
++ range = 2;
++ break;
++ case LSM9DS0_ACC_FS_4G:
++ range = 4;
++ break;
++ case LSM9DS0_ACC_FS_8G:
++ range = 8;
++ break;
++ case LSM9DS0_ACC_FS_16G:
++ range = 16;
++ break;
++ }
++ mutex_unlock(&stat->lock);
++ return sprintf(buf, "%d\n", range);
++}
++
++static ssize_t attr_get_range_mag(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ struct device *dev = to_dev(kobj->parent);
++ u8 val;
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ int range = 2;
++ mutex_lock(&stat->lock);
++ val = stat->pdata_mag->fs_range ;
++ switch (val) {
++ case LSM9DS0_MAG_FS_2G:
++ range = 2;
++ break;
++ case LSM9DS0_MAG_FS_4G:
++ range = 4;
++ break;
++ case LSM9DS0_MAG_FS_8G:
++ range = 8;
++ break;
++ case LSM9DS0_MAG_FS_12G:
++ range = 12;
++ break;
++ }
++ mutex_unlock(&stat->lock);
++ return sprintf(buf, "%d\n", range);
++}
++
++static ssize_t attr_set_range_acc(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ unsigned long val;
++ u8 range;
++ int err;
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++ switch (val) {
++ case 2:
++ range = LSM9DS0_ACC_FS_2G;
++ break;
++ case 4:
++ range = LSM9DS0_ACC_FS_4G;
++ break;
++ case 8:
++ range = LSM9DS0_ACC_FS_8G;
++ break;
++ case 16:
++ range = LSM9DS0_ACC_FS_16G;
++ break;
++ default:
++ dev_err(&stat->client->dev, "accelerometer invalid range "
++ "request: %lu, discarded\n", val);
++ return -EINVAL;
++ }
++ mutex_lock(&stat->lock);
++ err = lsm9ds0_acc_update_fs_range(stat, range);
++ if (err < 0) {
++ mutex_unlock(&stat->lock);
++ return err;
++ }
++ stat->pdata_acc->fs_range = range;
++ mutex_unlock(&stat->lock);
++ dev_info(&stat->client->dev, "accelerometer range set to:"
++ " %lu g\n", val);
++
++ return size;
++}
++
++static ssize_t attr_set_range_mag(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ unsigned long val;
++ u8 range;
++ int err;
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++ switch (val) {
++ case 2:
++ range = LSM9DS0_MAG_FS_2G;
++ break;
++ case 4:
++ range = LSM9DS0_MAG_FS_4G;
++ break;
++ case 8:
++ range = LSM9DS0_MAG_FS_8G;
++ break;
++ case 12:
++ range = LSM9DS0_MAG_FS_12G;
++ break;
++ default:
++ dev_err(&stat->client->dev, "magnetometer invalid range "
++ "request: %lu, discarded\n", val);
++ return -EINVAL;
++ }
++ mutex_lock(&stat->lock);
++ err = lsm9ds0_mag_update_fs_range(stat, range);
++ if (err < 0) {
++ mutex_unlock(&stat->lock);
++ return err;
++ }
++ stat->pdata_mag->fs_range = range;
++ mutex_unlock(&stat->lock);
++ dev_info(&stat->client->dev, "magnetometer range set to:"
++ " %lu g\n", val);
++
++ return size;
++}
++
++static ssize_t attr_get_aa_filter(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ struct device *dev = to_dev(kobj->parent);
++ u8 val;
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ int frequency=FILTER_773;
++ mutex_lock(&stat->lock);
++ val = stat->pdata_acc->aa_filter_bandwidth;
++ switch (val) {
++ case ANTI_ALIASING_50:
++ frequency = FILTER_50;
++ break;
++ case ANTI_ALIASING_194:
++ frequency = FILTER_194;
++ break;
++ case ANTI_ALIASING_362:
++ frequency = FILTER_362;
++ break;
++ case ANTI_ALIASING_773:
++ frequency = FILTER_773;
++ break;
++ }
++ mutex_unlock(&stat->lock);
++ return sprintf(buf, "%d\n", frequency);
++}
++
++static ssize_t attr_set_aa_filter(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ unsigned long val;
++ u8 frequency;
++ int err;
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++ switch (val) {
++ case FILTER_50:
++ frequency = ANTI_ALIASING_50;
++ break;
++ case FILTER_194:
++ frequency = ANTI_ALIASING_194;
++ break;
++ case FILTER_362:
++ frequency = ANTI_ALIASING_362;
++ break;
++ case FILTER_773:
++ frequency = ANTI_ALIASING_773;
++ break;
++ default:
++ dev_err(&stat->client->dev, "accelerometer invalid filter "
++ "request: %lu, discarded\n", val);
++ return -EINVAL;
++ }
++ mutex_lock(&stat->lock);
++ err = lsm9ds0_acc_update_filter(stat, frequency);
++ if (err < 0) {
++ mutex_unlock(&stat->lock);
++ return err;
++ }
++ stat->pdata_acc->aa_filter_bandwidth = frequency;
++ mutex_unlock(&stat->lock);
++ dev_info(&stat->client->dev, "accelerometer anti-aliasing filter "
++ "set to: %lu Hz\n", val);
++
++ return size;
++}
++
++static ssize_t attr_get_temp_enable(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ int val = (int)atomic_read(&stat->enabled_temp);
++
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_temp_enable(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t size)
++{
++ unsigned long val;
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ atomic_set(&stat->enabled_temp, (int)val);
++
++ if(val>0) {
++ lsm9ds0_temperature_enable(stat);
++ } else {
++ lsm9ds0_temperature_disable(stat);
++ }
++
++ return size;
++}
++
++static ssize_t attr_get_temp(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ int dec;
++ unsigned int flo;
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ dec = stat->temp_value_dec;
++ flo = stat->temp_value_flo;
++
++ if(dec==NDTEMP)
++ return sprintf(buf, "n.d.\n");
++
++ return sprintf(buf, "%d.%u\n", dec, flo);
++}
++
++static struct kobj_attribute poll_attr_acc =
++__ATTR(pollrate_ms, 0664, attr_get_polling_rate_acc, attr_set_polling_rate_acc);
++static struct kobj_attribute enable_attr_acc =
++__ATTR(enable_device, 0664, attr_get_enable_acc, attr_set_enable_acc);
++static struct kobj_attribute fs_attr_acc =
++__ATTR(full_scale, 0664, attr_get_range_acc, attr_set_range_acc);
++static struct kobj_attribute aa_filter_attr =
++__ATTR(anti_aliasing_frequency, 0664, attr_get_aa_filter, attr_set_aa_filter);
++static struct kobj_attribute poll_attr_mag =
++__ATTR(pollrate_ms, 0664, attr_get_polling_rate_mag, attr_set_polling_rate_mag);
++static struct kobj_attribute enable_attr_mag =
++__ATTR(enable_device, 0664, attr_get_enable_mag, attr_set_enable_mag);
++static struct kobj_attribute fs_attr_mag =
++__ATTR(full_scale, 0664, attr_get_range_mag, attr_set_range_mag);
++
++static int write_bit_on_register(struct lsm9ds0_status *stat, u8 address,
++ u8 *resume_value, u8 mask, int value)
++{
++ int err;
++ u8 updated_val;
++ u8 buf[2];
++ u8 val = 0x00;
++
++ buf[0] = address;
++ err = lsm9ds0_i2c_read(stat, buf, 1);
++ if (err < 0)
++ return -1;
++
++ if(resume_value != NULL)
++ *resume_value = buf[0];
++
++ if(mask == 0)
++ updated_val = (u8)value;
++ else {
++ if(value>0)
++ val = 0xFF;
++ updated_val = (mask & val) | ((~mask) & buf[0]);
++ }
++
++ buf[1] = updated_val;
++ buf[0] = address;
++
++ err = lsm9ds0_i2c_write(stat, buf, 1);
++ if (err < 0)
++ return -1;
++
++ if(resume_value != NULL)
++ *resume_value = updated_val;
++
++ return err;
++}
++
++static int write_gen_int(struct lsm9ds0_status *stat,
++ struct interrupt_enable *ie, int val)
++{
++ int err;
++
++ if(val>0)
++ val = 1;
++ else
++ val = 0;
++
++ err = write_bit_on_register(stat, ie->address, NULL, ie->mask, val);
++ if(err < 0)
++ return -1;
++
++ atomic_set(&ie->enable, val);
++ return err;
++}
++
++static int write_duration_threshold_int(struct lsm9ds0_status *stat,
++ struct interrupt_value *ie, int val)
++{
++ int err;
++
++ if(val<0)
++ return -1;
++
++ if(val>MAX_DUR_TH)
++ return -1;
++
++ err = write_bit_on_register(stat, ie->address, NULL, 0, val);
++ if(err<0)
++ return -1;
++
++ ie->value = val;
++
++ return err;
++}
++
++static int write_threshold_mag_int(struct lsm9ds0_status *stat,
++ struct interrupt_value *ie, int val)
++{
++ int err;
++ u8 high;
++ u8 low;
++
++ if(val<0)
++ return -1;
++
++ if(val>MAX_TH_MAG)
++ return -1;
++
++ low = (u8)(0xff & val);
++
++ err = write_bit_on_register(stat, ie->address, NULL, 0, low);
++ if(err<0)
++ return -1;
++
++ high = (u8)(0xff & (val >> 8));
++
++ err = write_bit_on_register(stat, (ie->address)+1, NULL, 0, high);
++ if(err<0)
++ return -1;
++
++ ie->value = val;
++
++ return err;
++}
++
++static ssize_t attr_get_gen1_status(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ int val = -1;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if(strcmp(attr->attr.name, "pin1_enable") == 0) {
++ val = atomic_read(&stat->interrupt->gen1_pin1.enable);
++ }
++ if(strcmp(attr->attr.name, "pin2_enable") == 0) {
++ val = atomic_read(&stat->interrupt->gen1_pin2.enable);
++ }
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_gen1_status(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err = -1;
++ unsigned long val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ if(strcmp(attr->attr.name, "pin1_enable") == 0) {
++ err = write_gen_int(stat,
++ &stat->interrupt->gen1_pin1, (int)val);
++ }
++ if(strcmp(attr->attr.name, "pin2_enable") == 0) {
++ err = write_gen_int(stat,
++ &stat->interrupt->gen1_pin2, (int)val);
++ }
++ return size;
++}
++
++static ssize_t attr_get_gen2_status(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ int val = -1;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if(strcmp(attr->attr.name, "pin1_enable") == 0) {
++ val = atomic_read(&stat->interrupt->gen2_pin1.enable);
++ }
++ if(strcmp(attr->attr.name, "pin2_enable") == 0) {
++ val = atomic_read(&stat->interrupt->gen2_pin2.enable);
++ }
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_gen2_status(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err = -1;
++ unsigned long val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ if(strcmp(attr->attr.name, "pin1_enable") == 0) {
++ err = write_gen_int(stat,
++ &stat->interrupt->gen2_pin1, (int)val);
++ }
++ if(strcmp(attr->attr.name, "pin2_enable") == 0) {
++ err = write_gen_int(stat,
++ &stat->interrupt->gen2_pin2, (int)val);
++ }
++ return size;
++}
++
++static ssize_t attr_get_gen1_duration(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ int val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ val = stat->interrupt->gen1_duration.value;
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_gen1_duration(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err = -1;
++ unsigned long val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ err = write_duration_threshold_int(stat,
++ &stat->interrupt->gen1_duration, (int)val);
++
++ return size;
++}
++
++static ssize_t attr_get_gen2_duration(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ int val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ val = stat->interrupt->gen2_duration.value;
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_gen2_duration(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err = -1;
++ unsigned long val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ err = write_duration_threshold_int(stat,
++ &stat->interrupt->gen2_duration, (int)val);
++
++ return size;
++}
++
++static ssize_t attr_get_gen1_threshold(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ int val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ val = stat->interrupt->gen1_threshold.value;
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_gen1_threshold(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err = -1;
++ unsigned long val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ err = write_duration_threshold_int(stat,
++ &stat->interrupt->gen1_threshold, (int)val);
++
++ return size;
++}
++
++static ssize_t attr_get_gen2_threshold(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ int val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ val = stat->interrupt->gen2_threshold.value;
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_gen2_threshold(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err = -1;
++ unsigned long val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ err = write_duration_threshold_int(stat,
++ &stat->interrupt->gen2_threshold, (int)val);
++
++ return size;
++}
++
++static ssize_t attr_get_gen_mag_status(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ int val = -1;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if(strcmp(attr->attr.name, "pin1_enable") == 0) {
++ val = atomic_read(&stat->interrupt->gen_mag_pin1.enable);
++ }
++ if(strcmp(attr->attr.name, "pin2_enable") == 0) {
++ val = atomic_read(&stat->interrupt->gen_mag_pin2.enable);
++ }
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_gen_mag_status(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err = -1;
++ unsigned long val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ if(strcmp(attr->attr.name, "pin1_enable") == 0) {
++ err = write_gen_int(stat,
++ &stat->interrupt->gen_mag_pin1, (int)val);
++ if(err >= 0) {
++ if((atomic_read(&stat->interrupt->gen_mag_pin2.enable))==0)
++ write_gen_int(stat,
++ &stat->interrupt->gen_mag, (int)val);
++ }
++ }
++ if(strcmp(attr->attr.name, "pin2_enable") == 0) {
++ err = write_gen_int(stat,
++ &stat->interrupt->gen_mag_pin2, (int)val);
++ if(err >= 0) {
++ if((atomic_read(&stat->interrupt->gen_mag_pin1.enable))==0)
++ write_gen_int(stat,
++ &stat->interrupt->gen_mag, (int)val);
++ }
++ }
++ return size;
++}
++
++static ssize_t attr_get_gen_mag_threshold(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ int val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ val = stat->interrupt->gen_mag_threshold.value;
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_gen_mag_threshold(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err = -1;
++ unsigned long val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ err = write_threshold_mag_int(stat,
++ &stat->interrupt->gen_mag_threshold, (int)val);
++
++ return size;
++}
++
++static int get_axis(struct lsm9ds0_status *stat,
++ int generator, const char *name) {
++
++ int val;
++ int axis;
++
++ if(strcmp(name, "x_high_enable") == 0) {
++ axis = 3;
++ }
++ if(strcmp(name, "x_low_enable") == 0) {
++ axis = 0;
++ }
++ if(strcmp(name, "y_high_enable") == 0) {
++ axis = 4;
++ }
++ if(strcmp(name, "y_low_enable") == 0) {
++ axis = 1;
++ }
++ if(strcmp(name, "z_high_enable") == 0) {
++ axis = 5;
++ }
++ if(strcmp(name, "z_low_enable") == 0) {
++ axis = 2;
++ }
++
++ if(generator == 1)
++ val = atomic_read(&stat->interrupt->gen1_axis[axis].enable);
++ else
++ val = atomic_read(&stat->interrupt->gen2_axis[axis].enable);
++
++ return val;
++}
++
++static int set_axis(struct lsm9ds0_status *stat, int generator,
++ const char *name, unsigned long value)
++{
++ int err = -1;
++ int axis;
++
++ if(strcmp(name, "x_high_enable") == 0) {
++ axis = 3;
++ }
++ if((strcmp(name, "x_low_enable") == 0) ||
++ (strcmp(name, "x_enable") == 0)) {
++ axis = 0;
++ }
++ if(strcmp(name, "y_high_enable") == 0) {
++ axis = 4;
++ }
++ if((strcmp(name, "y_low_enable") == 0) ||
++ (strcmp(name, "y_enable") == 0)) {
++ axis = 1;
++ }
++ if(strcmp(name, "z_high_enable") == 0) {
++ axis = 5;
++ }
++ if((strcmp(name, "z_low_enable") == 0) ||
++ (strcmp(name, "z_enable") == 0)) {
++ axis = 2;
++ }
++
++ if(generator == 1)
++ err = write_gen_int(stat,
++ &(stat->interrupt->gen1_axis[axis]), (int)value);
++ if(generator == 2)
++ err = write_gen_int(stat,
++ &(stat->interrupt->gen2_axis[axis]), (int)value);
++ if(generator == 3)
++ err = write_gen_int(stat,
++ &(stat->interrupt->gen_mag_axis[axis]), (int)value);
++
++ if(err < 0)
++ return -1;
++
++ return err;
++}
++
++static ssize_t attr_get_gen1_axis(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ int val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ val = get_axis(stat,1,attr->attr.name);
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_gen1_axis(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err;
++ unsigned long val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ err = set_axis(stat, 1, attr->attr.name, val);
++ if(err < 0)
++ return -1;
++
++ return size;
++}
++
++static ssize_t attr_get_gen2_axis(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ int val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ val = get_axis(stat,2,attr->attr.name);
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_gen2_axis(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err;
++ unsigned long val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ err = set_axis(stat, 2, attr->attr.name, val);
++ if(err < 0)
++ return -1;
++
++ return size;
++}
++
++static ssize_t attr_get_gen_mag_axis(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ int val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ val = get_axis(stat, 3, attr->attr.name);
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_gen_mag_axis(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err;
++ unsigned long val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ err = set_axis(stat, 3, attr->attr.name, val);
++ if(err < 0)
++ return -1;
++
++ return size;
++}
++
++static ssize_t attr_get_gen1_and_or(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ int val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ val = atomic_read(&stat->interrupt->gen1_and_or.enable);
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_gen1_and_or(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err;
++ unsigned long val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ err = write_gen_int(stat, &(stat->interrupt->gen1_and_or), (int)val);
++ if(err < 0)
++ return -1;
++
++ return size;
++}
++
++static ssize_t attr_get_gen2_and_or(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ char *buf)
++{
++ int val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ val = atomic_read(&stat->interrupt->gen2_and_or.enable);
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_gen2_and_or(struct kobject *kobj,
++ struct kobj_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err;
++ unsigned long val;
++ struct device *dev = to_dev(kobj->parent);
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ err = write_gen_int(stat, &(stat->interrupt->gen2_and_or), (int)val);
++ if(err < 0)
++ return -1;
++
++ return size;
++}
++
++static ssize_t attr_set_pin_conf(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err;
++ unsigned long val;
++
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ err = write_gen_int(stat,
++ &(stat->interrupt->interrupt_pin_conf), (int)val);
++ if(err < 0)
++ return -1;
++
++ return size;
++}
++
++static ssize_t attr_get_pin_conf(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ int val;
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ val = atomic_read(&stat->interrupt->interrupt_pin_conf.enable);
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_set_interrupt_polarity(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err;
++ unsigned long val;
++
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ err = write_gen_int(stat,
++ &(stat->interrupt->interrupt_polarity), (int)val);
++ if(err < 0)
++ return -1;
++
++ return size;
++}
++
++static ssize_t attr_get_interrupt_polarity(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ int val;
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ val = atomic_read(&stat->interrupt->interrupt_polarity.enable);
++ return sprintf(buf, "%d\n", val);
++}
++
++static struct kobj_attribute gen1_interrupt_pin1_enable =
++__ATTR(pin1_enable, 0664, attr_get_gen1_status, attr_set_gen1_status);
++static struct kobj_attribute gen1_interrupt_pin2_enable =
++__ATTR(pin2_enable, 0664, attr_get_gen1_status, attr_set_gen1_status);
++
++static struct kobj_attribute gen2_interrupt_pin1_enable =
++__ATTR(pin1_enable, 0664, attr_get_gen2_status, attr_set_gen2_status);
++static struct kobj_attribute gen2_interrupt_pin2_enable =
++__ATTR(pin2_enable, 0664, attr_get_gen2_status, attr_set_gen2_status);
++
++static struct kobj_attribute gen1_duration =
++__ATTR(duration, 0664, attr_get_gen1_duration, attr_set_gen1_duration);
++static struct kobj_attribute gen2_duration =
++__ATTR(duration, 0664, attr_get_gen2_duration, attr_set_gen2_duration);
++
++static struct kobj_attribute gen1_threshold =
++__ATTR(threshold, 0664, attr_get_gen1_threshold, attr_set_gen1_threshold);
++static struct kobj_attribute gen2_threshold =
++__ATTR(threshold, 0664, attr_get_gen2_threshold, attr_set_gen2_threshold);
++
++static struct kobj_attribute mag_gen_interrupt_pin1 =
++__ATTR(pin1_enable, 0664, attr_get_gen_mag_status, attr_set_gen_mag_status);
++static struct kobj_attribute mag_gen_interrupt_pin2 =
++__ATTR(pin2_enable, 0664, attr_get_gen_mag_status, attr_set_gen_mag_status);
++
++static struct kobj_attribute mag_gen_threshold =
++__ATTR(threshold, 0664, attr_get_gen_mag_threshold, attr_set_gen_mag_threshold);
++
++static struct kobj_attribute gen1_x_high =
++__ATTR(x_high_enable, 0664, attr_get_gen1_axis, attr_set_gen1_axis);
++static struct kobj_attribute gen1_x_low =
++__ATTR(x_low_enable, 0664, attr_get_gen1_axis, attr_set_gen1_axis);
++
++static struct kobj_attribute gen2_x_high =
++__ATTR(x_high_enable, 0664, attr_get_gen2_axis, attr_set_gen2_axis);
++static struct kobj_attribute gen2_x_low =
++__ATTR(x_low_enable, 0664, attr_get_gen2_axis, attr_set_gen2_axis);
++
++static struct kobj_attribute gen1_y_high =
++__ATTR(y_high_enable, 0664, attr_get_gen1_axis, attr_set_gen1_axis);
++static struct kobj_attribute gen1_y_low =
++__ATTR(y_low_enable, 0664, attr_get_gen1_axis, attr_set_gen1_axis);
++
++static struct kobj_attribute gen2_y_high =
++__ATTR(y_high_enable, 0664, attr_get_gen2_axis, attr_set_gen2_axis);
++static struct kobj_attribute gen2_y_low =
++__ATTR(y_low_enable, 0664, attr_get_gen2_axis, attr_set_gen2_axis);
++
++static struct kobj_attribute gen1_z_high =
++__ATTR(z_high_enable, 0664, attr_get_gen1_axis, attr_set_gen1_axis);
++static struct kobj_attribute gen1_z_low =
++__ATTR(z_low_enable, 0664, attr_get_gen1_axis, attr_set_gen1_axis);
++
++static struct kobj_attribute gen2_z_high =
++__ATTR(z_high_enable, 0664, attr_get_gen2_axis, attr_set_gen2_axis);
++static struct kobj_attribute gen2_z_low =
++__ATTR(z_low_enable, 0664, attr_get_gen2_axis, attr_set_gen2_axis);
++
++static struct kobj_attribute gen_mag_x =
++__ATTR(x_enable, 0664, attr_get_gen_mag_axis, attr_set_gen_mag_axis);
++static struct kobj_attribute gen_mag_y =
++__ATTR(y_enable, 0664, attr_get_gen_mag_axis, attr_set_gen_mag_axis);
++static struct kobj_attribute gen_mag_z =
++__ATTR(z_enable, 0664, attr_get_gen_mag_axis, attr_set_gen_mag_axis);
++
++static struct kobj_attribute gen1_and_or =
++__ATTR(and(1)_or(0)_combination, 0664, attr_get_gen1_and_or,
++ attr_set_gen1_and_or);
++static struct kobj_attribute gen2_and_or =
++__ATTR(and(1)_or(0)_combination, 0664, attr_get_gen2_and_or,
++ attr_set_gen2_and_or);
++
++
++static struct attribute *attributes_acc_interrupt1[] = {
++ &gen1_interrupt_pin1_enable.attr,
++ &gen1_interrupt_pin2_enable.attr,
++ &gen1_duration.attr,
++ &gen1_threshold.attr,
++ &gen1_x_high.attr,
++ &gen1_x_low.attr,
++ &gen1_y_high.attr,
++ &gen1_y_low.attr,
++ &gen1_z_high.attr,
++ &gen1_z_low.attr,
++ &gen1_and_or.attr,
++ NULL,
++};
++
++static struct attribute *attributes_acc_interrupt2[] = {
++ &gen2_interrupt_pin1_enable.attr,
++ &gen2_interrupt_pin2_enable.attr,
++ &gen2_duration.attr,
++ &gen2_threshold.attr,
++ &gen2_x_high.attr,
++ &gen2_x_low.attr,
++ &gen2_y_high.attr,
++ &gen2_y_low.attr,
++ &gen2_z_high.attr,
++ &gen2_z_low.attr,
++ &gen2_and_or.attr,
++ NULL,
++};
++
++static struct attribute *attributes_mag_interrupt[] = {
++ &mag_gen_interrupt_pin1.attr,
++ &mag_gen_interrupt_pin2.attr,
++ &mag_gen_threshold.attr,
++ &gen_mag_x.attr,
++ &gen_mag_y.attr,
++ &gen_mag_z.attr,
++ NULL,
++};
++
++static struct attribute *attributes_acc[] = {
++ &poll_attr_acc.attr,
++ &enable_attr_acc.attr,
++ &fs_attr_acc.attr,
++ &aa_filter_attr.attr,
++ NULL,
++};
++
++static struct attribute *attributes_mag[] = {
++ &poll_attr_mag.attr,
++ &enable_attr_mag.attr,
++ &fs_attr_mag.attr,
++ NULL,
++};
++
++static struct attribute_group attr_group_acc = {
++ .attrs = attributes_acc,
++};
++
++static struct attribute_group attr_group_mag = {
++ .attrs = attributes_mag,
++};
++
++static struct attribute_group attr_group_int1_acc = {
++ .attrs = attributes_acc_interrupt1,
++ .name = "interrupt_generator1",
++};
++
++static struct attribute_group attr_group_int2_acc = {
++ .attrs = attributes_acc_interrupt2,
++ .name = "interrupt_generator2",
++};
++
++static struct attribute_group attr_group_int_mag = {
++ .attrs = attributes_mag_interrupt,
++ .name = "interrupt_generator",
++};
++
++static struct device_attribute attributes_com[] = {
++ __ATTR(enable_temperature, 0664, attr_get_temp_enable,
++ attr_set_temp_enable),
++ __ATTR(read_temperature, 0444, attr_get_temp, NULL),
++};
++
++static struct device_attribute attributes_interrupt_com[] = {
++ __ATTR(interrupt_pin_configuration, 0664, attr_get_pin_conf,
++ attr_set_pin_conf),
++ __ATTR(interrupt_polarity, 0664, attr_get_interrupt_polarity,
++ attr_set_interrupt_polarity),
++};
++
++static int create_sysfs_interfaces(struct device *dev)
++{
++ int err;
++ int i,n;
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++
++ acc_kobj = kobject_create_and_add("accelerometer", &dev->kobj);
++ if(!acc_kobj)
++ return -ENOMEM;
++
++ mag_kobj = kobject_create_and_add("magnetometer", &dev->kobj);
++ if(!mag_kobj)
++ return -ENOMEM;
++
++ err = sysfs_create_group(acc_kobj, &attr_group_acc);
++ if (err)
++ kobject_put(acc_kobj);
++
++ err = sysfs_create_group(mag_kobj, &attr_group_mag);
++ if (err)
++ kobject_put(mag_kobj);
++
++ if((stat->pdata_acc->gpio_int1 >= 0)||
++ (stat->pdata_acc->gpio_int2 >= 0)) {
++ err = sysfs_create_group(acc_kobj, &attr_group_int1_acc);
++ if (err)
++ kobject_put(acc_kobj);
++
++ err = sysfs_create_group(acc_kobj, &attr_group_int2_acc);
++ if (err)
++ kobject_put(acc_kobj);
++
++ err = sysfs_create_group(mag_kobj, &attr_group_int_mag);
++ if (err)
++ kobject_put(mag_kobj);
++
++ for (n = 0; n < ARRAY_SIZE(attributes_interrupt_com); n++)
++ if (device_create_file(dev, attributes_interrupt_com + n))
++ goto error1;
++ }
++
++ for (i = 0; i < ARRAY_SIZE(attributes_com); i++)
++ if (device_create_file(dev, attributes_com + i))
++ goto error;
++
++
++
++ return 0;
++
++error:
++ for ( ; i >= 0; i--)
++ device_remove_file(dev, attributes_com + i);
++
++error1:
++ for ( ; n >= 0; n--)
++ device_remove_file(dev, attributes_interrupt_com + n);
++
++ dev_err(dev, "%s:Unable to create interface\n", __func__);
++ return -1;
++}
++
++static void remove_sysfs_interfaces(struct device *dev)
++{
++ int i;
++ struct lsm9ds0_status *stat = dev_get_drvdata(dev);
++ kobject_put(acc_kobj);
++ kobject_put(mag_kobj);
++ for (i = 0; i < ARRAY_SIZE(attributes_com); i++)
++ device_remove_file(dev, attributes_com + i);
++ if((stat->pdata_acc->gpio_int1 >= 0)||
++ (stat->pdata_acc->gpio_int2 >= 0)) {
++ for (i = 0; i < ARRAY_SIZE(attributes_interrupt_com); i++)
++ device_remove_file(dev, attributes_interrupt_com + i);
++ }
++}
++
++int lsm9ds0_acc_input_open(struct input_dev *input)
++{
++ struct lsm9ds0_status *stat = input_get_drvdata(input);
++
++ return lsm9ds0_acc_enable(stat);
++}
++
++void lsm9ds0_acc_input_close(struct input_dev *dev)
++{
++ struct lsm9ds0_status *stat = input_get_drvdata(dev);
++
++ lsm9ds0_acc_disable(stat);
++}
++
++int lsm9ds0_mag_input_open(struct input_dev *input)
++{
++ struct lsm9ds0_status *stat = input_get_drvdata(input);
++
++ return lsm9ds0_mag_enable(stat);
++}
++
++void lsm9ds0_mag_input_close(struct input_dev *dev)
++{
++ struct lsm9ds0_status *stat = input_get_drvdata(dev);
++
++ lsm9ds0_mag_disable(stat);
++}
++
++static int lsm9ds0_acc_get_data(struct lsm9ds0_status *stat, int *xyz)
++{
++ int i, err = -1;
++ u8 acc_data[6];
++ s32 hw_d[3] = { 0 };
++
++ acc_data[0] = (REG_ACC_DATA_ADDR);
++ err = lsm9ds0_i2c_read(stat, acc_data, 6);
++ if (err < 0)
++ return err;
++
++ hw_d[0] = ((s32)( (s16)((acc_data[1] << 8) | (acc_data[0]))));
++ hw_d[1] = ((s32)( (s16)((acc_data[3] << 8) | (acc_data[2]))));
++ hw_d[2] = ((s32)( (s16)((acc_data[5] << 8) | (acc_data[4]))));
++
++#ifdef DEBUG
++ pr_debug("%s read x=%X %X(regH regL), x=%d(dec) [ug]\n",
++ LSM9DS0_ACC_DEV_NAME, acc_data[1], acc_data[0], hw_d[0]);
++ pr_debug("%s read y=%X %X(regH regL), y=%d(dec) [ug]\n",
++ LSM9DS0_ACC_DEV_NAME, acc_data[3], acc_data[2], hw_d[1]);
++ pr_debug("%s read z=%X %X(regH regL), z=%d(dec) [ug]\n",
++ LSM9DS0_ACC_DEV_NAME, acc_data[5], acc_data[4], hw_d[2]);
++#endif
++
++ hw_d[0] = hw_d[0] * stat->sensitivity_acc;
++ hw_d[1] = hw_d[1] * stat->sensitivity_acc;
++ hw_d[2] = hw_d[2] * stat->sensitivity_acc;
++
++ for (i = 0; i < 3; i++) {
++ xyz[i] = stat->pdata_acc->rot_matrix[0][i] * hw_d[0] +
++ stat->pdata_acc->rot_matrix[1][i] * hw_d[1] +
++ stat->pdata_acc->rot_matrix[2][i] * hw_d[2];
++ }
++
++ return err;
++}
++
++static int lsm9ds0_mag_get_data(struct lsm9ds0_status *stat, int *xyz)
++{
++ int i, err = -1;
++ u8 mag_data[6];
++ s32 hw_d[3] = { 0 };
++
++ mag_data[0] = (REG_MAG_DATA_ADDR);
++ err = lsm9ds0_i2c_read(stat, mag_data, 6);
++ if (err < 0)
++ return err;
++
++ hw_d[0] = ((s32)( (s16)((mag_data[1] << 8) | (mag_data[0]))));
++ hw_d[1] = ((s32)( (s16)((mag_data[3] << 8) | (mag_data[2]))));
++ hw_d[2] = ((s32)( (s16)((mag_data[5] << 8) | (mag_data[4]))));
++
++#ifdef DEBUG
++ pr_debug("%s read x=%X %X(regH regL), x=%d(dec) [ug]\n",
++ LSM9DS0_MAG_DEV_NAME, mag_data[1], mag_data[0], hw_d[0]);
++ pr_debug("%s read x=%X %X(regH regL), x=%d(dec) [ug]\n",
++ LSM9DS0_MAG_DEV_NAME, mag_data[3], mag_data[2], hw_d[1]);
++ pr_debug("%s read x=%X %X(regH regL), x=%d(dec) [ug]\n",
++ LSM9DS0_MAG_DEV_NAME, mag_data[5], mag_data[4], hw_d[2]);
++#endif
++
++ hw_d[0] = hw_d[0] * stat->sensitivity_mag;
++ hw_d[1] = hw_d[1] * stat->sensitivity_mag;
++ hw_d[2] = hw_d[2] * stat->sensitivity_mag;
++
++ for (i = 0; i < 3; i++) {
++ xyz[i] = stat->pdata_acc->rot_matrix[0][i] * hw_d[0] +
++ stat->pdata_acc->rot_matrix[1][i] * hw_d[1] +
++ stat->pdata_acc->rot_matrix[2][i] * hw_d[2];
++ }
++
++ return err;
++}
++
++static int lsm9ds0_temp_get_data(struct lsm9ds0_status *stat,
++ int *dec, int *flo)
++{
++ int err = -1;
++ u8 temp_data[2];
++ s16 hw_d = 0;
++
++ temp_data[0] = (REG_TEMP_DATA_ADDR);
++ err = lsm9ds0_i2c_read(stat, temp_data, 2);
++ if (err < 0)
++ return err;
++
++ hw_d = (s16)((temp_data[1] << 8) | (temp_data[0]));
++
++
++#ifdef DEBUG
++ pr_debug("%s read T=%X %X(regH regL), T=%d(dec) [C]\n",
++ LSM9DS0_DEV_NAME, temp_data[1], temp_data[0], hw_d);
++#endif
++
++ *dec = (int)(hw_d/TEMP_SENSITIVITY) + OFFSET_TEMP;
++ *flo = (((unsigned int)hw_d)%TEMP_SENSITIVITY);
++
++ return err;
++}
++
++static void lsm9ds0_acc_report_values(struct lsm9ds0_status *stat, int *xyz)
++{
++ input_report_abs(stat->input_dev_acc, ABS_X, xyz[0]);
++ input_report_abs(stat->input_dev_acc, ABS_Y, xyz[1]);
++ input_report_abs(stat->input_dev_acc, ABS_Z, xyz[2]);
++ input_sync(stat->input_dev_acc);
++}
++
++static void lsm9ds0_mag_report_values(struct lsm9ds0_status *stat, int *xyz)
++{
++ input_report_abs(stat->input_dev_mag, ABS_X, xyz[0]);
++ input_report_abs(stat->input_dev_mag, ABS_Y, xyz[1]);
++ input_report_abs(stat->input_dev_mag, ABS_Z, xyz[2]);
++ input_sync(stat->input_dev_mag);
++}
++
++static int lsm9ds0_acc_input_init(struct lsm9ds0_status *stat)
++{
++ int err;
++
++ stat->input_dev_acc = input_allocate_device();
++ if (!stat->input_dev_acc) {
++ err = -ENOMEM;
++ dev_err(&stat->client->dev, "accelerometer "
++ "input device allocation failed\n");
++ goto err0;
++ }
++
++ stat->input_dev_acc->open = lsm9ds0_acc_input_open;
++ stat->input_dev_acc->close = lsm9ds0_acc_input_close;
++ stat->input_dev_acc->name = LSM9DS0_ACC_DEV_NAME;
++ stat->input_dev_acc->id.bustype = BUS_I2C;
++ stat->input_dev_acc->dev.parent = &stat->client->dev;
++
++ input_set_drvdata(stat->input_dev_acc, stat);
++
++ set_bit(EV_ABS, stat->input_dev_acc->evbit);
++
++ input_set_abs_params(stat->input_dev_acc, ABS_X,
++ -ACC_G_MAX_NEG, ACC_G_MAX_POS, FUZZ, FLAT);
++ input_set_abs_params(stat->input_dev_acc, ABS_Y,
++ -ACC_G_MAX_NEG, ACC_G_MAX_POS, FUZZ, FLAT);
++ input_set_abs_params(stat->input_dev_acc, ABS_Z,
++ -ACC_G_MAX_NEG, ACC_G_MAX_POS, FUZZ, FLAT);
++
++ err = input_register_device(stat->input_dev_acc);
++ if (err) {
++ dev_err(&stat->client->dev,
++ "unable to register accelerometer input device %s\n",
++ stat->input_dev_acc->name);
++ goto err1;
++ }
++
++ return 0;
++
++err1:
++ input_free_device(stat->input_dev_acc);
++err0:
++ return err;
++}
++
++static int lsm9ds0_mag_input_init(struct lsm9ds0_status *stat)
++{
++ int err;
++
++ stat->input_dev_mag = input_allocate_device();
++ if (!stat->input_dev_mag) {
++ err = -ENOMEM;
++ dev_err(&stat->client->dev, "magnetometer "
++ "input device allocation failed\n");
++ goto err0;
++ }
++
++ stat->input_dev_mag->open = lsm9ds0_mag_input_open;
++ stat->input_dev_mag->close = lsm9ds0_mag_input_close;
++ stat->input_dev_mag->name = LSM9DS0_MAG_DEV_NAME;
++ stat->input_dev_mag->id.bustype = BUS_I2C;
++ stat->input_dev_mag->dev.parent = &stat->client->dev;
++
++ input_set_drvdata(stat->input_dev_mag, stat);
++
++ set_bit(EV_ABS, stat->input_dev_mag->evbit);
++
++ input_set_abs_params(stat->input_dev_mag, ABS_X,
++ -MAG_G_MAX_NEG, MAG_G_MAX_POS, FUZZ, FLAT);
++ input_set_abs_params(stat->input_dev_mag, ABS_Y,
++ -MAG_G_MAX_NEG, MAG_G_MAX_POS, FUZZ, FLAT);
++ input_set_abs_params(stat->input_dev_mag, ABS_Z,
++ -MAG_G_MAX_NEG, MAG_G_MAX_POS, FUZZ, FLAT);
++
++ err = input_register_device(stat->input_dev_mag);
++ if (err) {
++ dev_err(&stat->client->dev,
++ "unable to register magnetometer input device %s\n",
++ stat->input_dev_mag->name);
++ goto err1;
++ }
++
++ return 0;
++
++err1:
++ input_free_device(stat->input_dev_mag);
++err0:
++ return err;
++}
++
++static void lsm9ds0_input_cleanup(struct lsm9ds0_status *stat)
++{
++ input_unregister_device(stat->input_dev_acc);
++ input_free_device(stat->input_dev_acc);
++
++ input_unregister_device(stat->input_dev_mag);
++ input_free_device(stat->input_dev_mag);
++}
++
++static void poll_function_work_acc(struct work_struct *input_work_acc)
++{
++ struct lsm9ds0_status *stat;
++ int xyz[3] = { 0 };
++ int err;
++
++ stat = container_of((struct work_struct *)input_work_acc,
++ struct lsm9ds0_status, input_work_acc);
++
++ mutex_lock(&stat->lock);
++ err = lsm9ds0_acc_get_data(stat, xyz);
++ if (err < 0)
++ dev_err(&stat->client->dev, "get_accelerometer_data failed\n");
++ else
++ lsm9ds0_acc_report_values(stat, xyz);
++
++ mutex_unlock(&stat->lock);
++ hrtimer_start(&stat->hr_timer_acc, stat->ktime_acc, HRTIMER_MODE_REL);
++}
++
++static void poll_function_work_mag(struct work_struct *input_work_mag)
++{
++ struct lsm9ds0_status *stat;
++ int xyz[3] = { 0 };
++ int err;
++ int dec;
++ int flo;
++
++ stat = container_of((struct work_struct *)input_work_mag,
++ struct lsm9ds0_status, input_work_mag);
++
++ mutex_lock(&stat->lock);
++
++ if(atomic_read(&stat->enabled_temp)) {
++ err = lsm9ds0_temp_get_data(stat, &dec, &flo);
++ if (err < 0)
++ dev_err(&stat->client->dev, "get_temperature_data"
++ " failed\n");
++ else {
++ stat->temp_value_dec = dec;
++ stat->temp_value_flo = flo;
++ }
++ }
++
++ if(atomic_read(&stat->enabled_mag)) {
++ err = lsm9ds0_mag_get_data(stat, xyz);
++ if (err < 0)
++ dev_err(&stat->client->dev, "get_magnetometer_data"
++ " failed\n");
++ else
++ lsm9ds0_mag_report_values(stat, xyz);
++ }
++
++ mutex_unlock(&stat->lock);
++ hrtimer_start(&stat->hr_timer_mag, stat->ktime_mag, HRTIMER_MODE_REL);
++}
++
++enum hrtimer_restart poll_function_read_acc(struct hrtimer *timer)
++{
++ struct lsm9ds0_status *stat;
++
++
++ stat = container_of((struct hrtimer *)timer,
++ struct lsm9ds0_status, hr_timer_acc);
++
++ queue_work(lsm9ds0_workqueue, &stat->input_work_acc);
++ return HRTIMER_NORESTART;
++}
++
++enum hrtimer_restart poll_function_read_mag(struct hrtimer *timer)
++{
++ struct lsm9ds0_status *stat;
++
++
++ stat = container_of((struct hrtimer *)timer,
++ struct lsm9ds0_status, hr_timer_mag);
++
++ queue_work(lsm9ds0_workqueue, &stat->input_work_mag);
++ return HRTIMER_NORESTART;
++}
++
++static int lsm9ds0_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
++{
++ struct lsm9ds0_status *stat;
++
++ u32 smbus_func = I2C_FUNC_SMBUS_BYTE_DATA |
++ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK;
++
++ int err = -1;
++ dev_info(&client->dev, "probe start.\n");
++ stat = kzalloc(sizeof(struct lsm9ds0_status), GFP_KERNEL);
++ if (stat == NULL) {
++ err = -ENOMEM;
++ dev_err(&client->dev,
++ "failed to allocate memory for module data: "
++ "%d\n", err);
++ goto exit_check_functionality_failed;
++ }
++
++ stat->use_smbus = 0;
++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
++ dev_warn(&client->dev, "client not i2c capable\n");
++ if (i2c_check_functionality(client->adapter, smbus_func)){
++ stat->use_smbus = 1;
++ dev_warn(&client->dev, "client using SMBUS\n");
++ } else {
++ err = -ENODEV;
++ dev_err(&client->dev, "client nor SMBUS capable\n");
++ goto exit_check_functionality_failed;
++ }
++ }
++
++ if(lsm9ds0_workqueue == 0)
++ lsm9ds0_workqueue = create_workqueue("lsm9ds0_workqueue");
++
++ hrtimer_init(&stat->hr_timer_acc, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
++ stat->hr_timer_acc.function = &poll_function_read_acc;
++ hrtimer_init(&stat->hr_timer_mag, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
++ stat->hr_timer_mag.function = &poll_function_read_mag;
++
++ mutex_init(&stat->lock);
++ mutex_lock(&stat->lock);
++
++ stat->client = client;
++ i2c_set_clientdata(client, stat);
++
++ stat->pdata_acc = kmalloc(sizeof(*stat->pdata_acc), GFP_KERNEL);
++ stat->pdata_mag = kmalloc(sizeof(*stat->pdata_mag), GFP_KERNEL);
++ if ((stat->pdata_acc == NULL)||(stat->pdata_mag == NULL)) {
++ err = -ENOMEM;
++ dev_err(&client->dev,
++ "failed to allocate memory for pdata: %d\n", err);
++ goto err_mutexunlock;
++ }
++
++ if (client->dev.platform_data == NULL) {
++ memcpy(stat->pdata_acc, &default_lsm9ds0_acc_pdata,
++ sizeof(*stat->pdata_acc));
++ memcpy(stat->pdata_mag, &default_lsm9ds0_mag_pdata,
++ sizeof(*stat->pdata_mag));
++ dev_info(&client->dev, "using default plaform_data for "
++ "accelerometer and magnetometer\n");
++ } else {
++ struct lsm9ds0_main_platform_data *tmp;
++ tmp = kzalloc(sizeof(struct lsm9ds0_main_platform_data),
++ GFP_KERNEL);
++ if(tmp == NULL)
++ goto exit_kfree_pdata;
++ memcpy(tmp, client->dev.platform_data, sizeof(*tmp));
++ if(tmp->pdata_acc == NULL) {
++ memcpy(stat->pdata_acc, &default_lsm9ds0_acc_pdata,
++ sizeof(*stat->pdata_acc));
++ dev_info(&client->dev, "using default plaform_data for "
++ "accelerometer\n");
++ } else {
++ memcpy(stat->pdata_acc, tmp->pdata_acc,
++ sizeof(*stat->pdata_acc));
++ }
++ if(tmp->pdata_mag == NULL) {
++ memcpy(stat->pdata_mag, &default_lsm9ds0_mag_pdata,
++ sizeof(*stat->pdata_mag));
++ dev_info(&client->dev, "using default plaform_data for "
++ "magnetometer\n");
++ } else {
++ memcpy(stat->pdata_mag, tmp->pdata_mag,
++ sizeof(*stat->pdata_mag));
++ }
++ kfree(tmp);
++ }
++
++ err = lsm9ds0_acc_validate_pdata(stat);
++ if (err < 0) {
++ dev_err(&client->dev, "failed to validate platform data for "
++ "accelerometer \n");
++ goto exit_kfree_pdata;
++ }
++
++ err = lsm9ds0_mag_validate_pdata(stat);
++ if (err < 0) {
++ dev_err(&client->dev, "failed to validate platform data for "
++ "magnetometer\n");
++ goto exit_kfree_pdata;
++ }
++
++ if (stat->pdata_acc->init) {
++ err = stat->pdata_acc->init();
++ if (err < 0) {
++ dev_err(&client->dev, "accelerometer init failed: "
++ "%d\n", err);
++ goto err_pdata_acc_init;
++ }
++ }
++ if (stat->pdata_mag->init) {
++ err = stat->pdata_mag->init();
++ if (err < 0) {
++ dev_err(&client->dev, "magnetometer init failed: "
++ "%d\n", err);
++ goto err_pdata_mag_init;
++ }
++ }
++
++ if(stat->pdata_acc->gpio_int1 >= 0) {
++ if (!gpio_is_valid(stat->pdata_acc->gpio_int1)) {
++ dev_err(&client->dev, "The requested GPIO [%d] is not "
++ "available\n", stat->pdata_acc->gpio_int1);
++ err = -EINVAL;
++ goto err_gpio1_valid;
++ }
++
++ err = gpio_request(stat->pdata_acc->gpio_int1,
++ "INTERRUPT_PIN1_LSM9DS0");
++ if(err < 0) {
++ dev_err(&client->dev, "Unable to request GPIO [%d].\n",
++ stat->pdata_acc->gpio_int1);
++ err = -EINVAL;
++ goto err_gpio1_valid;
++ }
++ gpio_direction_input(stat->pdata_acc->gpio_int1);
++ stat->irq1 = gpio_to_irq(stat->pdata_acc->gpio_int1);
++ if(stat->irq1 < 0) {
++ dev_err(&client->dev, "GPIO [%d] cannot be used as "
++ "interrupt.\n", stat->pdata_acc->gpio_int1);
++ err = -EINVAL;
++ goto err_gpio1_irq;
++ }
++ pr_info("%s: %s has set irq1 to irq: %d, mapped on gpio:%d\n",
++ LSM9DS0_DEV_NAME, __func__, stat->irq1,
++ stat->pdata_acc->gpio_int1);
++ }
++
++ if(stat->pdata_acc->gpio_int2 >= 0) {
++ if (!gpio_is_valid(stat->pdata_acc->gpio_int2)) {
++ dev_err(&client->dev, "The requested GPIO [%d] is not "
++ "available\n", stat->pdata_acc->gpio_int2);
++ err = -EINVAL;
++ goto err_gpio2_valid;
++ }
++
++ err = gpio_request(stat->pdata_acc->gpio_int2,
++ "INTERRUPT_PIN2_LSM9DS0");
++ if(err < 0) {
++ dev_err(&client->dev, "Unable to request GPIO [%d].\n",
++ stat->pdata_acc->gpio_int2);
++ err = -EINVAL;
++ goto err_gpio2_valid;
++ }
++ gpio_direction_input(stat->pdata_acc->gpio_int2);
++ stat->irq2 = gpio_to_irq(stat->pdata_acc->gpio_int2);
++ if(stat->irq2 < 0) {
++ dev_err(&client->dev, "GPIO [%d] cannot be used as "
++ "interrupt.\n", stat->pdata_acc->gpio_int2);
++ err = -EINVAL;
++ goto err_gpio2_irq;
++ }
++ pr_info("%s: %s has set irq2 to irq: %d, "
++ "mapped on gpio:%d\n",
++ LSM9DS0_DEV_NAME, __func__, stat->irq2,
++ stat->pdata_acc->gpio_int2);
++ }
++
++ err = lsm9ds0_hw_init(stat);
++ if (err < 0) {
++ dev_err(&client->dev, "hw init failed: %d\n", err);
++ goto err_hw_init;
++ }
++
++ err = lsm9ds0_acc_device_power_on(stat);
++ if (err < 0) {
++ dev_err(&client->dev, "accelerometer power on failed: "
++ "%d\n", err);
++ goto err_pdata_init;
++ }
++ err = lsm9ds0_mag_device_power_on(stat);
++ if (err < 0) {
++ dev_err(&client->dev, "magnetometer power on failed: "
++ "%d\n", err);
++ goto err_pdata_init;
++ }
++
++ err = lsm9ds0_acc_update_fs_range(stat, stat->pdata_acc->fs_range);
++ if (err < 0) {
++ dev_err(&client->dev, "update_fs_range on accelerometer "
++ "failed\n");
++ goto err_power_off_acc;
++ }
++
++ err = lsm9ds0_mag_update_fs_range(stat, stat->pdata_mag->fs_range);
++ if (err < 0) {
++ dev_err(&client->dev, "update_fs_range on magnetometer "
++ "failed\n");
++ goto err_power_off_mag;
++ }
++
++ err = lsm9ds0_acc_update_odr(stat, stat->pdata_acc->poll_interval);
++ if (err < 0) {
++ dev_err(&client->dev, "update_odr on accelerometer failed\n");
++ goto err_power_off;
++ }
++
++ err = lsm9ds0_mag_update_odr(stat, stat->pdata_mag->poll_interval);
++ if (err < 0) {
++ dev_err(&client->dev, "update_odr on magnetometer failed\n");
++ goto err_power_off;
++ }
++
++ err = lsm9ds0_acc_update_filter(stat,
++ stat->pdata_acc->aa_filter_bandwidth);
++ if (err < 0) {
++ dev_err(&client->dev, "update_filter on accelerometer "
++ "failed\n");
++ goto err_power_off;
++ }
++
++ err = lsm9ds0_acc_input_init(stat);
++ if (err < 0) {
++ dev_err(&client->dev, "accelerometer input init failed\n");
++ goto err_power_off;
++ }
++
++ err = lsm9ds0_mag_input_init(stat);
++ if (err < 0) {
++ dev_err(&client->dev, "magnetometer input init failed\n");
++ goto err_power_off;
++ }
++
++ err = create_sysfs_interfaces(&client->dev);
++ if (err < 0) {
++ dev_err(&client->dev,
++ "device LSM9DS0_DEV_NAME sysfs register failed\n");
++ goto err_input_cleanup;
++ }
++
++ lsm9ds0_acc_device_power_off(stat);
++ lsm9ds0_mag_device_power_off(stat);
++
++ if(stat->pdata_acc->gpio_int1 >= 0){
++ INIT_WORK(&stat->irq1_work, lsm9ds0_irq1_work_func);
++ stat->irq1_work_queue =
++ create_singlethread_workqueue("lsm9ds0_wq1");
++ if (!stat->irq1_work_queue) {
++ err = -ENOMEM;
++ dev_err(&client->dev,
++ "cannot create work queue1: %d\n", err);
++ goto err_remove_sysfs_int;
++ }
++ err = request_irq(stat->irq1, lsm9ds0_isr1,
++ IRQF_TRIGGER_RISING, "lsm9ds0_irq1", stat);
++ if (err < 0) {
++ dev_err(&client->dev, "request irq1 failed: %d\n", err);
++ goto err_destoyworkqueue1;
++ }
++ disable_irq_nosync(stat->irq1);
++ }
++
++ if(stat->pdata_acc->gpio_int2 >= 0){
++ INIT_WORK(&stat->irq2_work, lsm9ds0_irq2_work_func);
++ stat->irq2_work_queue =
++ create_singlethread_workqueue("lsm9ds0_wq2");
++ if (!stat->irq2_work_queue) {
++ err = -ENOMEM;
++ dev_err(&client->dev,
++ "cannot create work queue2: %d\n", err);
++ goto err_free_irq1;
++ }
++ err = request_irq(stat->irq2, lsm9ds0_isr2,
++ IRQF_TRIGGER_RISING, "lsm9ds0_irq2", stat);
++ if (err < 0) {
++ dev_err(&client->dev, "request irq2 failed: %d\n", err);
++ goto err_destoyworkqueue2;
++ }
++ disable_irq_nosync(stat->irq2);
++ }
++
++ INIT_WORK(&stat->input_work_acc, poll_function_work_acc);
++ INIT_WORK(&stat->input_work_mag, poll_function_work_mag);
++
++ mutex_unlock(&stat->lock);
++ dev_info(&client->dev, "%s: probed\n", LSM9DS0_DEV_NAME);
++ return 0;
++
++err_destoyworkqueue2:
++ destroy_workqueue(stat->irq2_work_queue);
++err_free_irq1:
++ free_irq(stat->irq1, stat);
++err_destoyworkqueue1:
++ destroy_workqueue(stat->irq1_work_queue);
++err_remove_sysfs_int:
++ remove_sysfs_interfaces(&client->dev);
++err_input_cleanup:
++ lsm9ds0_input_cleanup(stat);
++err_power_off:
++err_power_off_mag:
++ lsm9ds0_mag_device_power_off(stat);
++err_power_off_acc:
++ lsm9ds0_acc_device_power_off(stat);
++ kfree(stat->interrupt);
++err_hw_init:
++err_gpio2_irq:
++ gpio_free(stat->pdata_acc->gpio_int2);
++err_gpio2_valid:
++err_gpio1_irq:
++ gpio_free(stat->pdata_acc->gpio_int1);
++err_gpio1_valid:
++err_pdata_init:
++err_pdata_mag_init:
++ if (stat->pdata_mag->exit)
++ stat->pdata_mag->exit();
++err_pdata_acc_init:
++ if (stat->pdata_acc->exit)
++ stat->pdata_acc->exit();
++exit_kfree_pdata:
++ kfree(stat->pdata_acc);
++ kfree(stat->pdata_mag);
++err_mutexunlock:
++ mutex_unlock(&stat->lock);
++ kfree(stat);
++ if(!lsm9ds0_workqueue) {
++ flush_workqueue(lsm9ds0_workqueue);
++ destroy_workqueue(lsm9ds0_workqueue);
++ }
++exit_check_functionality_failed:
++ pr_err("%s: Driver Init failed\n", LSM9DS0_DEV_NAME);
++ return err;
++}
++
++static int lsm9ds0_remove(struct i2c_client *client)
++{
++ struct lsm9ds0_status *stat = i2c_get_clientdata(client);
++
++ lsm9ds0_acc_disable(stat);
++ lsm9ds0_mag_disable(stat);
++ lsm9ds0_temperature_disable(stat);
++
++ if(stat->pdata_acc->gpio_int1 >= 0) {
++ free_irq(stat->irq1, stat);
++ gpio_free(stat->pdata_acc->gpio_int1);
++ destroy_workqueue(stat->irq1_work_queue);
++ }
++
++ if(stat->pdata_acc->gpio_int2 >= 0) {
++ free_irq(stat->irq2, stat);
++ gpio_free(stat->pdata_acc->gpio_int2);
++ destroy_workqueue(stat->irq2_work_queue);
++ }
++
++ lsm9ds0_acc_input_cleanup(stat);
++ lsm9ds0_mag_input_cleanup(stat);
++
++ remove_sysfs_interfaces(&client->dev);
++
++ if (stat->pdata_acc->exit)
++ stat->pdata_acc->exit();
++
++ if (stat->pdata_mag->exit)
++ stat->pdata_mag->exit();
++
++ if((stat->pdata_acc->gpio_int1 >= 0)||
++ (stat->pdata_acc->gpio_int2 >= 0)) {
++ kfree(stat->interrupt);
++ }
++
++ if(!lsm9ds0_workqueue) {
++ flush_workqueue(lsm9ds0_workqueue);
++ destroy_workqueue(lsm9ds0_workqueue);
++ }
++
++ kfree(stat->pdata_acc);
++ kfree(stat->pdata_mag);
++ kfree(stat);
++ return 0;
++}
++
++static const struct i2c_device_id lsm9ds0_id[] = {
++ { LSM9DS0_DEV_NAME, 0 },
++ { },
++};
++MODULE_DEVICE_TABLE(i2c, lsm9ds0_id);
++
++static const struct of_device_id lsm9ds0_of_match[] = {
++ { .compatible = "st,"LSM9DS0_DEV_NAME, },
++ { },
++};
++MODULE_DEVICE_TABLE(of, lsm9ds0_of_match);
++
++static struct i2c_driver lsm9ds0_i2c_driver = {
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = LSM9DS0_DEV_NAME,
++ .of_match_table = lsm9ds0_of_match,
++ },
++ .probe = lsm9ds0_probe,
++ .remove = lsm9ds0_remove,
++ .id_table = lsm9ds0_id,
++};
++
++module_i2c_driver(lsm9ds0_i2c_driver);
++
++
++MODULE_DESCRIPTION("lsm9ds0 accelerometer and magnetometer driver");
++MODULE_AUTHOR("Matteo Dameno, Denis Ciocca, STMicroelectronics");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/input/misc/lsm9ds0_gyr.c b/drivers/input/misc/lsm9ds0_gyr.c
+new file mode 100644
+index 0000000..360f85c
+--- /dev/null
++++ b/drivers/input/misc/lsm9ds0_gyr.c
+@@ -0,0 +1,1726 @@
++/******************** (C) COPYRIGHT 2012 STMicroelectronics ********************
++*
++* File Name : lsm9ds0_gyr_sysfs.c
++* Authors : MEMS Motion Sensors Products Div- Application Team
++* : Matteo Dameno (matteo.dameno@st.com)
++* : Denis Ciocca (denis.ciocca@st.com)
++* : Both authors are willing to be considered the contact
++* : and update points for the driver.
++* Version : V 1.2 sysfs
++* Date : 2012/Jul/10
++* Description : LSM9DS0 digital output gyroscope sensor API
++*
++********************************************************************************
++*
++* 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.
++*
++* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
++* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
++* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
++* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
++* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
++* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
++* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
++*
++********************************************************************************
++* REVISON HISTORY
++*
++* VERSION | DATE | AUTHORS | DESCRIPTION
++* 1.0 | 2010/May/02 | Carmine Iascone | First Release
++* 1.1.3 | 2011/Jun/24 | Matteo Dameno | Corrects ODR Bug
++* 1.1.4 | 2011/Sep/02 | Matteo Dameno | SMB Bus Mng,
++* | | | forces BDU setting
++* 1.1.5 | 2011/Sep/24 | Matteo Dameno | Introduces FIFO Feat.
++* 1.1.5.2 | 2011/Nov/11 | Matteo Dameno | enable gpio_int to be
++* | | | passed as parameter at
++* | | | module loading time;
++* | | | corrects polling
++* | | | bug at end of probing;
++* 1.1.5.3 | 2011/Dec/20 | Matteo Dameno | corrects error in
++* | | | I2C SADROOT; Modifies
++* | | | resume suspend func.
++* 1.1.5.4 | 2012/Jan/09 | Matteo Dameno | moved under input/misc;
++* 1.1.5.5 | 2012/Mar/30 | Matteo Dameno | moved watermark, use_smbus,
++* | | | fifomode @ struct foo_status
++* | | | sysfs range input format
++* | | | changed to decimal
++* 1.2 | 2012/Jul/10 | Denis Ciocca | input_poll_dev removal
++* 1.2.1 | 2012/Jul/10 | Denis Ciocca | added high resolution timers
++*******************************************************************************/
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/i2c.h>
++#include <linux/mutex.h>
++#include <linux/input.h>
++#include <linux/interrupt.h>
++#include <linux/gpio.h>
++#include <linux/slab.h>
++#include <linux/moduleparam.h>
++#include <linux/kernel.h>
++#include <linux/stat.h>
++
++
++#include <linux/input/lsm9ds0.h>
++/* #include "lsm9ds0.h" */
++
++/* Maximum polled-device-reported rot speed value value in dps */
++#define FS_MAX 32768
++#define MS_TO_NS(x) (x*1000000L)
++
++/* lsm9ds0 gyroscope registers */
++#define WHO_AM_I (0x0F)
++
++#define SENSITIVITY_250 8750 /* udps/LSB */
++#define SENSITIVITY_500 17500 /* udps/LSB */
++#define SENSITIVITY_2000 70000 /* udps/LSB */
++
++#define CTRL_REG1 (0x20) /* CTRL REG1 */
++#define CTRL_REG2 (0x21) /* CTRL REG2 */
++#define CTRL_REG3 (0x22) /* CTRL_REG3 */
++#define CTRL_REG4 (0x23) /* CTRL_REG4 */
++#define CTRL_REG5 (0x24) /* CTRL_REG5 */
++#define REFERENCE (0x25) /* REFERENCE REG */
++#define FIFO_CTRL_REG (0x2E) /* FIFO CONTROL REGISTER */
++#define FIFO_SRC_REG (0x2F) /* FIFO SOURCE REGISTER */
++#define OUT_X_L (0x28) /* 1st AXIS OUT REG of 6 */
++
++#define AXISDATA_REG OUT_X_L
++
++/* CTRL_REG1 */
++#define ALL_ZEROES (0x00)
++#define PM_OFF (0x00)
++#define PM_NORMAL (0x08)
++#define ENABLE_ALL_AXES (0x07)
++#define ENABLE_NO_AXES (0x00)
++#define BW00 (0x00)
++#define BW01 (0x10)
++#define BW10 (0x20)
++#define BW11 (0x30)
++#define ODR095 (0x00) /* ODR = 95Hz */
++#define ODR190 (0x40) /* ODR = 190Hz */
++#define ODR380 (0x80) /* ODR = 380Hz */
++#define ODR760 (0xC0) /* ODR = 760Hz */
++
++/* CTRL_REG3 bits */
++#define I2_DRDY (0x08)
++#define I2_WTM (0x04)
++#define I2_OVRUN (0x02)
++#define I2_EMPTY (0x01)
++#define I2_NONE (0x00)
++#define I2_MASK (0x0F)
++
++/* CTRL_REG4 bits */
++#define FS_MASK (0x30)
++#define BDU_ENABLE (0x80)
++
++/* CTRL_REG5 bits */
++#define FIFO_ENABLE (0x40)
++#define HPF_ENALBE (0x11)
++
++/* FIFO_CTRL_REG bits */
++#define FIFO_MODE_MASK (0xE0)
++#define FIFO_MODE_BYPASS (0x00)
++#define FIFO_MODE_FIFO (0x20)
++#define FIFO_MODE_STREAM (0x40)
++#define FIFO_MODE_STR2FIFO (0x60)
++#define FIFO_MODE_BYPASS2STR (0x80)
++#define FIFO_WATERMARK_MASK (0x1F)
++
++#define FIFO_STORED_DATA_MASK (0x1F)
++
++#define I2C_AUTO_INCREMENT (0x80)
++
++/* RESUME STATE INDICES */
++#define RES_CTRL_REG1 0
++#define RES_CTRL_REG2 1
++#define RES_CTRL_REG3 2
++#define RES_CTRL_REG4 3
++#define RES_CTRL_REG5 4
++#define RES_FIFO_CTRL_REG 5
++#define RESUME_ENTRIES 6
++
++
++/* #define DEBUG 1 */
++
++/** Registers Contents */
++#define WHOAMI_LSM9DS0_GYR (0xD4) /* Expected content for WAI register*/
++
++static int int1_gpio = LSM9DS0_GYR_DEFAULT_INT1_GPIO;
++static int int2_gpio = LSM9DS0_GYR_DEFAULT_INT2_GPIO;
++/* module_param(int1_gpio, int, S_IRUGO); */
++module_param(int2_gpio, int, S_IRUGO);
++
++/*
++ * LSM9DS0 gyroscope data
++ * brief structure containing gyroscope values for yaw, pitch and roll in
++ * s32
++ */
++
++struct lsm9ds0_gyr_triple {
++ s32 x, /* x-axis angular rate data. */
++ y, /* y-axis angluar rate data. */
++ z; /* z-axis angular rate data. */
++};
++
++struct output_rate {
++ int poll_rate_ms;
++ u8 mask;
++};
++
++static const struct output_rate odr_table[] = {
++
++ { 2, ODR760|BW10},
++ { 3, ODR380|BW01},
++ { 6, ODR190|BW00},
++ { 11, ODR095|BW00},
++};
++
++static struct lsm9ds0_gyr_platform_data default_lsm9ds0_gyr_pdata = {
++ .fs_range = LSM9DS0_GYR_FS_250DPS,
++ .axis_map_x = 0,
++ .axis_map_y = 1,
++ .axis_map_z = 2,
++ .negate_x = 0,
++ .negate_y = 0,
++ .negate_z = 0,
++
++ .poll_interval = 100,
++ .min_interval = LSM9DS0_GYR_MIN_POLL_PERIOD_MS, /* 2ms */
++
++ .gpio_int1 = LSM9DS0_GYR_DEFAULT_INT1_GPIO,
++ .gpio_int2 = LSM9DS0_GYR_DEFAULT_INT2_GPIO, /* int for fifo */
++
++};
++
++struct workqueue_struct *lsm9ds0_gyr_workqueue = 0;
++
++struct lsm9ds0_gyr_status {
++ struct i2c_client *client;
++ struct lsm9ds0_gyr_platform_data *pdata;
++
++ struct mutex lock;
++
++ struct input_dev *input_dev;
++
++ int hw_initialized;
++ atomic_t enabled;
++ int use_smbus;
++
++ u8 reg_addr;
++ u8 resume_state[RESUME_ENTRIES];
++
++ u32 sensitivity;
++
++ /* interrupt related */
++ int irq2;
++ struct work_struct irq2_work;
++ struct workqueue_struct *irq2_work_queue;
++
++ bool polling_enabled;
++ /* fifo related */
++ u8 watermark;
++ u8 fifomode;
++
++ struct hrtimer hr_timer;
++ ktime_t ktime;
++ struct work_struct polling_task;
++};
++
++
++static int lsm9ds0_gyr_i2c_read(struct lsm9ds0_gyr_status *stat, u8 *buf,
++ int len)
++{
++ int ret;
++ u8 reg = buf[0];
++ u8 cmd = reg;
++
++/*
++ if (len > sizeof(buf))
++ dev_err(&stat->client->dev,
++ "read error insufficient buffer length: "
++ "len:%d, buf size=%d\n",
++ len, sizeof(buf));
++*/
++ if (len > 1)
++ cmd = (I2C_AUTO_INCREMENT | reg);
++ if (stat->use_smbus) {
++ if (len == 1) {
++ ret = i2c_smbus_read_byte_data(stat->client, cmd);
++ buf[0] = ret & 0xff;
++#ifdef DEBUG
++ dev_warn(&stat->client->dev,
++ "i2c_smbus_read_byte_data: ret=0x%02x, len:%d ,"
++ "command=0x%02x, buf[0]=0x%02x\n",
++ ret, len, cmd , buf[0]);
++#endif
++ } else if (len > 1) {
++ ret = i2c_smbus_read_i2c_block_data(stat->client,
++ cmd, len, buf);
++#ifdef DEBUG
++ dev_warn(&stat->client->dev,
++ "i2c_smbus_read_i2c_block_data: ret:%d len:%d, "
++ "command=0x%02x, ",
++ ret, len, cmd);
++ unsigned int ii;
++ for (ii = 0; ii < len; ii++)
++ printk(KERN_DEBUG "buf[%d]=0x%02x,",
++ ii, buf[ii]);
++
++ printk("\n");
++#endif
++ } else
++ ret = -1;
++
++ if (ret < 0) {
++ dev_err(&stat->client->dev,
++ "read transfer error: len:%d, command=0x%02x\n",
++ len, cmd);
++ return 0; /* failure */
++ }
++ return len; /* success */
++ }
++
++ ret = i2c_master_send(stat->client, &cmd, sizeof(cmd));
++ if (ret != sizeof(cmd))
++ return ret;
++
++ return i2c_master_recv(stat->client, buf, len);
++}
++
++static int lsm9ds0_gyr_i2c_write(struct lsm9ds0_gyr_status *stat, u8 *buf,
++ int len)
++{
++ int ret;
++ u8 reg, value;
++
++ if (len > 1)
++ buf[0] = (I2C_AUTO_INCREMENT | buf[0]);
++
++ reg = buf[0];
++ value = buf[1];
++
++ if (stat->use_smbus) {
++ if (len == 1) {
++ ret = i2c_smbus_write_byte_data(stat->client,
++ reg, value);
++#ifdef DEBUG
++ dev_warn(&stat->client->dev,
++ "i2c_smbus_write_byte_data: ret=%d, len:%d, "
++ "command=0x%02x, value=0x%02x\n",
++ ret, len, reg , value);
++#endif
++ return ret;
++ } else if (len > 1) {
++ ret = i2c_smbus_write_i2c_block_data(stat->client,
++ reg, len, buf + 1);
++#ifdef DEBUG
++ dev_warn(&stat->client->dev,
++ "i2c_smbus_write_i2c_block_data: ret=%d, "
++ "len:%d, command=0x%02x, ",
++ ret, len, reg);
++ unsigned int ii;
++ for (ii = 0; ii < (len + 1); ii++)
++ printk(KERN_DEBUG "value[%d]=0x%02x,",
++ ii, buf[ii]);
++
++ printk("\n");
++#endif
++ return ret;
++ }
++ }
++
++ ret = i2c_master_send(stat->client, buf, len+1);
++ return (ret == len+1) ? 0 : ret;
++}
++
++
++static int lsm9ds0_gyr_register_write(struct lsm9ds0_gyr_status *stat,
++ u8 *buf, u8 reg_address, u8 new_value)
++{
++ int err;
++
++ /* Sets configuration register at reg_address
++ * NOTE: this is a straight overwrite */
++ buf[0] = reg_address;
++ buf[1] = new_value;
++ err = lsm9ds0_gyr_i2c_write(stat, buf, 1);
++ if (err < 0)
++ return err;
++
++ return err;
++}
++
++static int lsm9ds0_gyr_register_read(struct lsm9ds0_gyr_status *stat,
++ u8 *buf, u8 reg_address)
++{
++
++ int err = -1;
++ buf[0] = (reg_address);
++ err = lsm9ds0_gyr_i2c_read(stat, buf, 1);
++ return err;
++}
++
++static int lsm9ds0_gyr_register_update(struct lsm9ds0_gyr_status *stat,
++ u8 *buf, u8 reg_address, u8 mask, u8 new_bit_values)
++{
++ int err = -1;
++ u8 init_val;
++ u8 updated_val;
++ err = lsm9ds0_gyr_register_read(stat, buf, reg_address);
++ if (!(err < 0)) {
++ init_val = buf[0];
++ updated_val = ((mask & new_bit_values) | ((~mask) & init_val));
++ err = lsm9ds0_gyr_register_write(stat, buf, reg_address,
++ updated_val);
++ }
++ return err;
++}
++
++
++static int lsm9ds0_gyr_update_watermark(struct lsm9ds0_gyr_status *stat,
++ u8 watermark)
++{
++ int res = 0;
++ u8 buf[2];
++ u8 new_value;
++
++ mutex_lock(&stat->lock);
++ new_value = (watermark % 0x20);
++ res = lsm9ds0_gyr_register_update(stat, buf, FIFO_CTRL_REG,
++ FIFO_WATERMARK_MASK, new_value);
++ if (res < 0) {
++ dev_err(&stat->client->dev, "failed to update watermark\n");
++ return res;
++ }
++ dev_dbg(&stat->client->dev, "%s new_value:0x%02x,watermark:0x%02x\n",
++ __func__, new_value, watermark);
++
++ stat->resume_state[RES_FIFO_CTRL_REG] =
++ ((FIFO_WATERMARK_MASK & new_value) |
++ (~FIFO_WATERMARK_MASK &
++ stat->resume_state[RES_FIFO_CTRL_REG]));
++ stat->watermark = new_value;
++ mutex_unlock(&stat->lock);
++ return res;
++}
++
++static int lsm9ds0_gyr_update_fifomode(struct lsm9ds0_gyr_status *stat,
++ u8 fifomode)
++{
++ int res;
++ u8 buf[2];
++ u8 new_value;
++
++ new_value = fifomode;
++ res = lsm9ds0_gyr_register_update(stat, buf, FIFO_CTRL_REG,
++ FIFO_MODE_MASK, new_value);
++ if (res < 0) {
++ dev_err(&stat->client->dev, "failed to update fifoMode\n");
++ return res;
++ }
++ /*
++ dev_dbg(&stat->client->dev, "new_value:0x%02x,prev fifomode:0x%02x\n",
++ __func__, new_value, stat->fifomode);
++ */
++ stat->resume_state[RES_FIFO_CTRL_REG] =
++ ((FIFO_MODE_MASK & new_value) |
++ (~FIFO_MODE_MASK &
++ stat->resume_state[RES_FIFO_CTRL_REG]));
++ stat->fifomode = new_value;
++
++ return res;
++}
++
++static int lsm9ds0_gyr_fifo_reset(struct lsm9ds0_gyr_status *stat)
++{
++ u8 oldmode;
++ int res;
++
++ oldmode = stat->fifomode;
++ res = lsm9ds0_gyr_update_fifomode(stat, FIFO_MODE_BYPASS);
++ if (res < 0)
++ return res;
++ res = lsm9ds0_gyr_update_fifomode(stat, oldmode);
++ if (res >= 0)
++ dev_dbg(&stat->client->dev, "%s fifo reset to: 0x%02x\n",
++ __func__, oldmode);
++
++ return res;
++}
++
++static int lsm9ds0_gyr_fifo_hwenable(struct lsm9ds0_gyr_status *stat,
++ u8 enable)
++{
++ int res;
++ u8 buf[2];
++ u8 set = 0x00;
++ if (enable)
++ set = FIFO_ENABLE;
++
++ res = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG5,
++ FIFO_ENABLE, set);
++ if (res < 0) {
++ dev_err(&stat->client->dev, "fifo_hw switch to:0x%02x failed\n",
++ set);
++ return res;
++ }
++ stat->resume_state[RES_CTRL_REG5] =
++ ((FIFO_ENABLE & set) |
++ (~FIFO_ENABLE & stat->resume_state[RES_CTRL_REG5]));
++ dev_dbg(&stat->client->dev, "%s set to:0x%02x\n", __func__, set);
++ return res;
++}
++
++static int lsm9ds0_gyr_manage_int2settings(struct lsm9ds0_gyr_status *stat,
++ u8 fifomode)
++{
++ int res;
++ u8 buf[2];
++ bool enable_fifo_hw;
++ bool recognized_mode = false;
++ u8 int2bits = I2_NONE;
++/*
++ if (stat->polling_enabled) {
++ fifomode = FIFO_MODE_BYPASS;
++ dbg_warn(&stat->client->dev, "in polling mode, fifo mode forced"
++ " to BYPASS mode\n");
++ }
++*/
++
++
++ switch (fifomode) {
++ case FIFO_MODE_FIFO:
++ recognized_mode = true;
++
++ if (stat->polling_enabled) {
++ int2bits = I2_NONE;
++ enable_fifo_hw = false;
++ } else {
++ int2bits = (I2_WTM | I2_OVRUN);
++ enable_fifo_hw = true;
++ }
++ res = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG3,
++ I2_MASK, int2bits);
++ if (res < 0) {
++ dev_err(&stat->client->dev, "%s : failed to update "
++ "CTRL_REG3:0x%02x\n",
++ __func__, fifomode);
++ goto err_mutex_unlock;
++ }
++ stat->resume_state[RES_CTRL_REG3] =
++ ((I2_MASK & int2bits) |
++ (~(I2_MASK) & stat->resume_state[RES_CTRL_REG3]));
++ /* enable_fifo_hw = true; */
++ break;
++
++ case FIFO_MODE_BYPASS:
++ recognized_mode = true;
++
++ if (stat->polling_enabled)
++ int2bits = I2_NONE;
++ else
++ int2bits = I2_DRDY;
++
++ res = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG3,
++ I2_MASK, int2bits);
++ if (res < 0) {
++ dev_err(&stat->client->dev, "%s : failed to update"
++ " to CTRL_REG3:0x%02x\n",
++ __func__, fifomode);
++ goto err_mutex_unlock;
++ }
++ stat->resume_state[RES_CTRL_REG3] =
++ ((I2_MASK & int2bits) |
++ (~I2_MASK & stat->resume_state[RES_CTRL_REG3]));
++ enable_fifo_hw = false;
++ break;
++
++ default:
++ recognized_mode = false;
++ res = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG3,
++ I2_MASK, I2_NONE);
++ if (res < 0) {
++ dev_err(&stat->client->dev, "%s : failed to update "
++ "CTRL_REG3:0x%02x\n",
++ __func__, fifomode);
++ goto err_mutex_unlock;
++ }
++ enable_fifo_hw = false;
++ stat->resume_state[RES_CTRL_REG3] =
++ ((I2_MASK & 0x00) |
++ (~I2_MASK & stat->resume_state[RES_CTRL_REG3]));
++ break;
++
++ }
++ if (recognized_mode) {
++ res = lsm9ds0_gyr_update_fifomode(stat, fifomode);
++ if (res < 0) {
++ dev_err(&stat->client->dev, "%s : failed to "
++ "set fifoMode\n", __func__);
++ goto err_mutex_unlock;
++ }
++ }
++ res = lsm9ds0_gyr_fifo_hwenable(stat, enable_fifo_hw);
++
++err_mutex_unlock:
++
++ return res;
++}
++
++
++static int lsm9ds0_gyr_update_fs_range(struct lsm9ds0_gyr_status *stat,
++ u8 new_fs)
++{
++ int res ;
++ u8 buf[2];
++
++ u32 sensitivity;
++
++ switch(new_fs) {
++ case LSM9DS0_GYR_FS_250DPS:
++ sensitivity = SENSITIVITY_250;
++ break;
++ case LSM9DS0_GYR_FS_500DPS:
++ sensitivity = SENSITIVITY_500;
++ break;
++ case LSM9DS0_GYR_FS_2000DPS:
++ sensitivity = SENSITIVITY_2000;
++ break;
++ default:
++ dev_err(&stat->client->dev, "invalid g range "
++ "requested: %u\n", new_fs);
++ return -EINVAL;
++ }
++
++
++ buf[0] = CTRL_REG4;
++
++ res = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG4,
++ FS_MASK, new_fs);
++
++ if (res < 0) {
++ dev_err(&stat->client->dev, "%s : failed to update fs:0x%02x\n",
++ __func__, new_fs);
++ return res;
++ }
++ stat->resume_state[RES_CTRL_REG4] =
++ ((FS_MASK & new_fs) |
++ (~FS_MASK & stat->resume_state[RES_CTRL_REG4]));
++
++ stat->sensitivity = sensitivity;
++ return res;
++}
++
++
++static int lsm9ds0_gyr_update_odr(struct lsm9ds0_gyr_status *stat,
++ unsigned int poll_interval_ms)
++{
++ int err = -1;
++ int i;
++ u8 config[2];
++
++ for (i = ARRAY_SIZE(odr_table) - 1; i >= 0; i--) {
++ if ((odr_table[i].poll_rate_ms <= poll_interval_ms) || (i == 0))
++ break;
++ }
++
++ config[1] = odr_table[i].mask;
++ config[1] |= (ENABLE_ALL_AXES + PM_NORMAL);
++
++ /* If device is currently enabled, we need to write new
++ * configuration out to it */
++ if (atomic_read(&stat->enabled)) {
++ config[0] = CTRL_REG1;
++ err = lsm9ds0_gyr_i2c_write(stat, config, 1);
++ if (err < 0)
++ return err;
++ stat->resume_state[RES_CTRL_REG1] = config[1];
++ stat->ktime = ktime_set(0, MS_TO_NS(poll_interval_ms));
++ }
++
++ return err;
++}
++
++/* gyroscope data readout */
++static int lsm9ds0_gyr_get_data(struct lsm9ds0_gyr_status *stat,
++ struct lsm9ds0_gyr_triple *data)
++{
++ int err;
++ unsigned char gyro_out[6];
++ /* y,p,r hardware data */
++ s32 hw_d[3] = { 0 };
++
++ gyro_out[0] = (AXISDATA_REG);
++
++ err = lsm9ds0_gyr_i2c_read(stat, gyro_out, 6);
++
++ if (err < 0)
++ return err;
++
++ hw_d[0] = (s32) ((s16)((gyro_out[1]) << 8) | gyro_out[0]);
++ hw_d[1] = (s32) ((s16)((gyro_out[3]) << 8) | gyro_out[2]);
++ hw_d[2] = (s32) ((s16)((gyro_out[5]) << 8) | gyro_out[4]);
++
++ //hw_d[0] = hw_d[0] * stat->sensitivity;
++ //hw_d[1] = hw_d[1] * stat->sensitivity;
++ //hw_d[2] = hw_d[2] * stat->sensitivity;
++
++ data->x = ((stat->pdata->negate_x) ? (-hw_d[stat->pdata->axis_map_x])
++ : (hw_d[stat->pdata->axis_map_x]));
++ data->y = ((stat->pdata->negate_y) ? (-hw_d[stat->pdata->axis_map_y])
++ : (hw_d[stat->pdata->axis_map_y]));
++ data->z = ((stat->pdata->negate_z) ? (-hw_d[stat->pdata->axis_map_z])
++ : (hw_d[stat->pdata->axis_map_z]));
++
++#ifdef DEBUG
++ /* dev_info(&stat->client->dev, "gyro_out: x = %d, y = %d, z = %d\n",
++ data->x, data->y, data->z); */
++#endif
++
++ return err;
++}
++
++static void lsm9ds0_gyr_report_values(struct lsm9ds0_gyr_status *stat,
++ struct lsm9ds0_gyr_triple *data)
++{
++ input_report_abs(stat->input_dev, ABS_X, data->x);
++ input_report_abs(stat->input_dev, ABS_Y, data->y);
++ input_report_abs(stat->input_dev, ABS_Z, data->z);
++ input_sync(stat->input_dev);
++}
++
++static int lsm9ds0_gyr_hw_init(struct lsm9ds0_gyr_status *stat)
++{
++ int err;
++ u8 buf[6];
++
++ dev_info(&stat->client->dev, "hw init\n");
++
++ buf[0] = (CTRL_REG1);
++ buf[1] = stat->resume_state[RES_CTRL_REG1];
++ buf[2] = stat->resume_state[RES_CTRL_REG2];
++ buf[3] = stat->resume_state[RES_CTRL_REG3];
++ buf[4] = stat->resume_state[RES_CTRL_REG4];
++ buf[5] = stat->resume_state[RES_CTRL_REG5];
++
++ err = lsm9ds0_gyr_i2c_write(stat, buf, 5);
++ if (err < 0)
++ return err;
++
++ buf[0] = (FIFO_CTRL_REG);
++ buf[1] = stat->resume_state[RES_FIFO_CTRL_REG];
++ err = lsm9ds0_gyr_i2c_write(stat, buf, 1);
++ if (err < 0)
++ return err;
++
++ stat->hw_initialized = 1;
++
++ return err;
++}
++
++static void lsm9ds0_gyr_device_power_off(struct lsm9ds0_gyr_status *stat)
++{
++ int err;
++ u8 buf[2];
++
++ dev_info(&stat->client->dev, "power off\n");
++
++ buf[0] = (CTRL_REG1);
++ buf[1] = (PM_OFF);
++ err = lsm9ds0_gyr_i2c_write(stat, buf, 1);
++ if (err < 0)
++ dev_err(&stat->client->dev, "soft power off failed\n");
++
++ if (stat->pdata->power_off) {
++ /* disable_irq_nosync(acc->irq1); */
++ disable_irq_nosync(stat->irq2);
++ stat->pdata->power_off();
++ stat->hw_initialized = 0;
++ }
++
++ if (stat->hw_initialized) {
++ /*if (stat->pdata->gpio_int1 >= 0)*/
++ /* disable_irq_nosync(stat->irq1);*/
++ if (stat->pdata->gpio_int2 >= 0) {
++ disable_irq_nosync(stat->irq2);
++ dev_info(&stat->client->dev,
++ "power off: irq2 disabled\n");
++ }
++ stat->hw_initialized = 0;
++ }
++}
++
++static int lsm9ds0_gyr_device_power_on(struct lsm9ds0_gyr_status *stat)
++{
++ int err;
++
++ if (stat->pdata->power_on) {
++ err = stat->pdata->power_on();
++ if (err < 0)
++ return err;
++ if (stat->pdata->gpio_int2 >= 0)
++ enable_irq(stat->irq2);
++ }
++
++
++ if (!stat->hw_initialized) {
++ err = lsm9ds0_gyr_hw_init(stat);
++ if (err < 0) {
++ lsm9ds0_gyr_device_power_off(stat);
++ return err;
++ }
++ }
++
++ if (stat->hw_initialized) {
++ /* if (stat->pdata->gpio_int1) {
++ enable_irq(stat->irq1);
++ dev_info(&stat->client->dev,
++ "power on: irq1 enabled\n");
++ } */
++ dev_dbg(&stat->client->dev, "stat->pdata->gpio_int2 = %d\n",
++ stat->pdata->gpio_int2);
++ if (stat->pdata->gpio_int2 >= 0) {
++ enable_irq(stat->irq2);
++ dev_info(&stat->client->dev,
++ "power on: irq2 enabled\n");
++ }
++ }
++
++ return 0;
++}
++
++static int lsm9ds0_gyr_enable(struct lsm9ds0_gyr_status *stat)
++{
++ int err;
++
++ if (!atomic_cmpxchg(&stat->enabled, 0, 1)) {
++
++ err = lsm9ds0_gyr_device_power_on(stat);
++ if (err < 0) {
++ atomic_set(&stat->enabled, 0);
++ return err;
++ }
++
++ if (stat->polling_enabled) {
++ hrtimer_start(&(stat->hr_timer), stat->ktime, HRTIMER_MODE_REL);
++ }
++
++ }
++
++ return 0;
++}
++
++static int lsm9ds0_gyr_disable(struct lsm9ds0_gyr_status *stat)
++{
++ dev_dbg(&stat->client->dev, "%s: stat->enabled = %d\n", __func__,
++ atomic_read(&stat->enabled));
++
++ if (atomic_cmpxchg(&stat->enabled, 1, 0)) {
++
++ lsm9ds0_gyr_device_power_off(stat);
++ hrtimer_cancel(&stat->hr_timer);
++ dev_dbg(&stat->client->dev, "%s: cancel_hrtimer ", __func__);
++ }
++ return 0;
++}
++
++static ssize_t attr_polling_rate_show(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ int val;
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++ mutex_lock(&stat->lock);
++ val = stat->pdata->poll_interval;
++ mutex_unlock(&stat->lock);
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_polling_rate_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t size)
++{
++ int err;
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++ unsigned long interval_ms;
++
++ if (strict_strtoul(buf, 10, &interval_ms))
++ return -EINVAL;
++ if (!interval_ms)
++ return -EINVAL;
++
++ mutex_lock(&stat->lock);
++ err = lsm9ds0_gyr_update_odr(stat, interval_ms);
++ if(err >= 0)
++ stat->pdata->poll_interval = interval_ms;
++ mutex_unlock(&stat->lock);
++ return size;
++}
++
++static ssize_t attr_range_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++ int range = 0;
++ u8 val;
++ mutex_lock(&stat->lock);
++ val = stat->pdata->fs_range;
++
++ switch (val) {
++ case LSM9DS0_GYR_FS_250DPS:
++ range = 250;
++ break;
++ case LSM9DS0_GYR_FS_500DPS:
++ range = 500;
++ break;
++ case LSM9DS0_GYR_FS_2000DPS:
++ range = 2000;
++ break;
++ }
++ mutex_unlock(&stat->lock);
++ /* return sprintf(buf, "0x%02x\n", val); */
++ return sprintf(buf, "%d\n", range);
++}
++
++static ssize_t attr_range_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t size)
++{
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++ unsigned long val;
++ u8 range;
++ int err;
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++ switch (val) {
++ case 250:
++ range = LSM9DS0_GYR_FS_250DPS;
++ break;
++ case 500:
++ range = LSM9DS0_GYR_FS_500DPS;
++ break;
++ case 2000:
++ range = LSM9DS0_GYR_FS_2000DPS;
++ break;
++ default:
++ dev_err(&stat->client->dev, "invalid range request: %lu,"
++ " discarded\n", val);
++ return -EINVAL;
++ }
++ mutex_lock(&stat->lock);
++ err = lsm9ds0_gyr_update_fs_range(stat, range);
++ if (err >= 0)
++ stat->pdata->fs_range = range;
++ mutex_unlock(&stat->lock);
++ dev_info(&stat->client->dev, "range set to: %lu dps\n", val);
++ return size;
++}
++
++static ssize_t attr_enable_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++ int val = atomic_read(&stat->enabled);
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_enable_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t size)
++{
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++ unsigned long val;
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ if (val)
++ lsm9ds0_gyr_enable(stat);
++ else
++ lsm9ds0_gyr_disable(stat);
++
++ return size;
++}
++
++static ssize_t attr_polling_mode_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ int val = 0;
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++
++ mutex_lock(&stat->lock);
++ if (stat->polling_enabled)
++ val = 1;
++ mutex_unlock(&stat->lock);
++ return sprintf(buf, "%d\n", val);
++}
++
++static ssize_t attr_polling_mode_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t size)
++{
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++ unsigned long val;
++
++ if (strict_strtoul(buf, 10, &val))
++ return -EINVAL;
++
++ mutex_lock(&stat->lock);
++ if (val) {
++ stat->polling_enabled = true;
++ lsm9ds0_gyr_manage_int2settings(stat, stat->fifomode);
++ dev_info(dev, "polling mode enabled\n");
++ if (atomic_read(&stat->enabled)) {
++ hrtimer_start(&(stat->hr_timer), stat->ktime, HRTIMER_MODE_REL);
++ }
++ } else {
++ if (stat->polling_enabled) {
++ hrtimer_cancel(&stat->hr_timer);
++ }
++ stat->polling_enabled = false;
++ lsm9ds0_gyr_manage_int2settings(stat, stat->fifomode);
++ dev_info(dev, "polling mode disabled\n");
++ }
++ mutex_unlock(&stat->lock);
++ return size;
++}
++
++static ssize_t attr_watermark_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t size)
++{
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++ unsigned long watermark;
++ int res;
++
++ if (strict_strtoul(buf, 16, &watermark))
++ return -EINVAL;
++
++ res = lsm9ds0_gyr_update_watermark(stat, watermark);
++ if (res < 0)
++ return res;
++
++ return size;
++}
++
++static ssize_t attr_watermark_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++ int val = stat->watermark;
++ return sprintf(buf, "0x%02x\n", val);
++}
++
++static ssize_t attr_fifomode_store(struct device *dev,
++ struct device_attribute *attr,
++ const char *buf, size_t size)
++{
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++ unsigned long fifomode;
++ int res;
++
++ if (strict_strtoul(buf, 16, &fifomode))
++ return -EINVAL;
++ /* if (!fifomode)
++ return -EINVAL; */
++
++ dev_dbg(dev, "%s, got value:0x%02x\n", __func__, (u8)fifomode);
++
++ mutex_lock(&stat->lock);
++ res = lsm9ds0_gyr_manage_int2settings(stat, (u8) fifomode);
++ mutex_unlock(&stat->lock);
++
++ if (res < 0)
++ return res;
++ return size;
++}
++
++static ssize_t attr_fifomode_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++ u8 val = stat->fifomode;
++ return sprintf(buf, "0x%02x\n", val);
++}
++
++#ifdef DEBUG
++static ssize_t attr_reg_set(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t size)
++{
++ int rc;
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++ u8 x[2];
++ unsigned long val;
++
++ if (strict_strtoul(buf, 16, &val))
++ return -EINVAL;
++ mutex_lock(&stat->lock);
++ x[0] = stat->reg_addr;
++ mutex_unlock(&stat->lock);
++ x[1] = val;
++ rc = lsm9ds0_gyr_i2c_write(stat, x, 1);
++ return size;
++}
++
++static ssize_t attr_reg_get(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ ssize_t ret;
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++ int rc;
++ u8 data;
++
++ mutex_lock(&stat->lock);
++ data = stat->reg_addr;
++ mutex_unlock(&stat->lock);
++ rc = lsm9ds0_gyr_i2c_read(stat, &data, 1);
++ ret = sprintf(buf, "0x%02x\n", data);
++ return ret;
++}
++
++static ssize_t attr_addr_set(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t size)
++{
++ struct lsm9ds0_gyr_status *stat = dev_get_drvdata(dev);
++ unsigned long val;
++
++ if (strict_strtoul(buf, 16, &val))
++ return -EINVAL;
++
++ mutex_lock(&stat->lock);
++
++ stat->reg_addr = val;
++
++ mutex_unlock(&stat->lock);
++
++ return size;
++}
++#endif /* DEBUG */
++
++static struct device_attribute attributes[] = {
++ __ATTR(pollrate_ms, 0666, attr_polling_rate_show,
++ attr_polling_rate_store),
++ __ATTR(range, 0666, attr_range_show, attr_range_store),
++ __ATTR(enable_device, 0666, attr_enable_show, attr_enable_store),
++ __ATTR(enable_polling, 0666, attr_polling_mode_show,
++ attr_polling_mode_store),
++ __ATTR(fifo_samples, 0666, attr_watermark_show, attr_watermark_store),
++ __ATTR(fifo_mode, 0666, attr_fifomode_show, attr_fifomode_store),
++#ifdef DEBUG
++ __ATTR(reg_value, 0600, attr_reg_get, attr_reg_set),
++ __ATTR(reg_addr, 0200, NULL, attr_addr_set),
++#endif
++};
++
++static int create_sysfs_interfaces(struct device *dev)
++{
++ int i;
++ for (i = 0; i < ARRAY_SIZE(attributes); i++)
++ if (device_create_file(dev, attributes + i))
++ goto error;
++ return 0;
++
++error:
++ for (; i >= 0; i--)
++ device_remove_file(dev, attributes + i);
++ dev_err(dev, "%s:Unable to create interface\n", __func__);
++ return -1;
++}
++
++static int remove_sysfs_interfaces(struct device *dev)
++{
++ int i;
++ for (i = 0; i < ARRAY_SIZE(attributes); i++)
++ device_remove_file(dev, attributes + i);
++ return 0;
++}
++
++static void lsm9ds0_gyr_report_triple(struct lsm9ds0_gyr_status *stat)
++{
++ int err;
++ struct lsm9ds0_gyr_triple data_out;
++
++ err = lsm9ds0_gyr_get_data(stat, &data_out);
++ if (err < 0)
++ dev_err(&stat->client->dev, "get_gyroscope_data failed\n");
++ else
++ lsm9ds0_gyr_report_values(stat, &data_out);
++}
++
++
++static void lsm9ds0_gyr_irq2_fifo(struct lsm9ds0_gyr_status *stat)
++{
++ int err;
++ u8 buf[2];
++ u8 int_source;
++ u8 samples;
++ u8 workingmode;
++ u8 stored_samples;
++
++ mutex_lock(&stat->lock);
++
++ workingmode = stat->fifomode;
++
++
++ dev_dbg(&stat->client->dev, "%s : fifomode:0x%02x\n", __func__,
++ workingmode);
++
++
++ switch (workingmode) {
++ case FIFO_MODE_BYPASS:
++ {
++ dev_dbg(&stat->client->dev, "%s : fifomode:0x%02x\n", __func__,
++ stat->fifomode);
++ lsm9ds0_gyr_report_triple(stat);
++ break;
++ }
++ case FIFO_MODE_FIFO:
++ samples = (stat->watermark)+1;
++ dev_dbg(&stat->client->dev,
++ "%s : FIFO_SRC_REG init samples:%d\n",
++ __func__, samples);
++ err = lsm9ds0_gyr_register_read(stat, buf, FIFO_SRC_REG);
++ if (err < 0)
++ dev_err(&stat->client->dev,
++ "error reading fifo source reg\n");
++
++ int_source = buf[0];
++ dev_dbg(&stat->client->dev, "%s :FIFO_SRC_REG content:0x%02x\n",
++ __func__, int_source);
++
++ stored_samples = int_source & FIFO_STORED_DATA_MASK;
++ dev_dbg(&stat->client->dev, "%s : fifomode:0x%02x\n", __func__,
++ stat->fifomode);
++
++ dev_dbg(&stat->client->dev, "%s : samples:%d stored:%d\n",
++ __func__, samples, stored_samples);
++
++ for (; samples > 0; samples--) {
++#ifdef DEBUG
++ input_report_abs(stat->input_dev, ABS_MISC, 1);
++ input_sync(stat->input_dev);
++#endif
++ dev_dbg(&stat->client->dev, "%s : current sample:%d\n",
++ __func__, samples);
++
++ lsm9ds0_gyr_report_triple(stat);
++
++#ifdef DEBUG
++ input_report_abs(stat->input_dev, ABS_MISC, 0);
++ input_sync(stat->input_dev);
++#endif
++ }
++ lsm9ds0_gyr_fifo_reset(stat);
++ break;
++ }
++#ifdef DEBUG
++ input_report_abs(stat->input_dev, ABS_MISC, 3);
++ input_sync(stat->input_dev);
++#endif
++
++ mutex_unlock(&stat->lock);
++}
++
++static irqreturn_t lsm9ds0_gyr_isr2(int irq, void *dev)
++{
++ struct lsm9ds0_gyr_status *stat = dev;
++
++ disable_irq_nosync(irq);
++#ifdef DEBUG
++ input_report_abs(stat->input_dev, ABS_MISC, 2);
++ input_sync(stat->input_dev->input);
++#endif
++ queue_work(stat->irq2_work_queue, &stat->irq2_work);
++ pr_debug("%s %s: isr2 queued\n", LSM9DS0_GYR_DEV_NAME, __func__);
++
++ return IRQ_HANDLED;
++}
++
++static void lsm9ds0_gyr_irq2_work_func(struct work_struct *work)
++{
++
++ struct lsm9ds0_gyr_status *stat =
++ container_of(work, struct lsm9ds0_gyr_status, irq2_work);
++ /* TODO add interrupt service procedure.
++ ie:lsm9ds0_gyr_irq2_XXX(stat); */
++ lsm9ds0_gyr_irq2_fifo(stat);
++ /* */
++ pr_debug("%s %s: IRQ2 served\n", LSM9DS0_GYR_DEV_NAME, __func__);
++/* exit: */
++ enable_irq(stat->irq2);
++}
++
++int lsm9ds0_gyr_input_open(struct input_dev *input)
++{
++ struct lsm9ds0_gyr_status *stat = input_get_drvdata(input);
++ dev_dbg(&stat->client->dev, "%s\n", __func__);
++ return lsm9ds0_gyr_enable(stat);
++}
++
++void lsm9ds0_gyr_input_close(struct input_dev *dev)
++{
++ struct lsm9ds0_gyr_status *stat = input_get_drvdata(dev);
++ dev_dbg(&stat->client->dev, "%s\n", __func__);
++ lsm9ds0_gyr_disable(stat);
++}
++
++static int lsm9ds0_gyr_validate_pdata(struct lsm9ds0_gyr_status *stat)
++{
++ /* checks for correctness of minimal polling period */
++ stat->pdata->min_interval =
++ max((unsigned int) LSM9DS0_GYR_MIN_POLL_PERIOD_MS,
++ stat->pdata->min_interval);
++
++ stat->pdata->poll_interval = max(stat->pdata->poll_interval,
++ stat->pdata->min_interval);
++
++ if (stat->pdata->axis_map_x > 2 ||
++ stat->pdata->axis_map_y > 2 ||
++ stat->pdata->axis_map_z > 2) {
++ dev_err(&stat->client->dev,
++ "invalid axis_map value x:%u y:%u z%u\n",
++ stat->pdata->axis_map_x,
++ stat->pdata->axis_map_y,
++ stat->pdata->axis_map_z);
++ return -EINVAL;
++ }
++
++ /* Only allow 0 and 1 for negation boolean flag */
++ if (stat->pdata->negate_x > 1 ||
++ stat->pdata->negate_y > 1 ||
++ stat->pdata->negate_z > 1) {
++ dev_err(&stat->client->dev,
++ "invalid negate value x:%u y:%u z:%u\n",
++ stat->pdata->negate_x,
++ stat->pdata->negate_y,
++ stat->pdata->negate_z);
++ return -EINVAL;
++ }
++
++ /* Enforce minimum polling interval */
++ if (stat->pdata->poll_interval < stat->pdata->min_interval) {
++ dev_err(&stat->client->dev,
++ "minimum poll interval violated\n");
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static int lsm9ds0_gyr_input_init(struct lsm9ds0_gyr_status *stat)
++{
++ int err = -1;
++
++ dev_dbg(&stat->client->dev, "%s\n", __func__);
++
++ stat->input_dev = input_allocate_device();
++ if (!stat->input_dev) {
++ err = -ENOMEM;
++ dev_err(&stat->client->dev,
++ "input device allocation failed\n");
++ goto err0;
++ }
++
++ stat->input_dev->open = lsm9ds0_gyr_input_open;
++ stat->input_dev->close = lsm9ds0_gyr_input_close;
++ stat->input_dev->name = LSM9DS0_GYR_DEV_NAME;
++
++ stat->input_dev->id.bustype = BUS_I2C;
++ stat->input_dev->dev.parent = &stat->client->dev;
++
++ input_set_drvdata(stat->input_dev, stat);
++
++ set_bit(EV_ABS, stat->input_dev->evbit);
++
++#ifdef DEBUG
++ set_bit(EV_KEY, stat->input_dev->keybit);
++ set_bit(KEY_LEFT, stat->input_dev->keybit);
++ input_set_abs_params(stat->input_dev, ABS_MISC, 0, 1, 0, 0);
++#endif
++
++ input_set_abs_params(stat->input_dev, ABS_X, -FS_MAX-1, FS_MAX, 0, 0);
++ input_set_abs_params(stat->input_dev, ABS_Y, -FS_MAX-1, FS_MAX, 0, 0);
++ input_set_abs_params(stat->input_dev, ABS_Z, -FS_MAX-1, FS_MAX, 0, 0);
++
++
++ err = input_register_device(stat->input_dev);
++ if (err) {
++ dev_err(&stat->client->dev,
++ "unable to register input polled device %s\n",
++ stat->input_dev->name);
++ goto err1;
++ }
++
++ return 0;
++
++err1:
++ input_free_device(stat->input_dev);
++err0:
++ return err;
++}
++
++static void lsm9ds0_gyr_input_cleanup(struct lsm9ds0_gyr_status *stat)
++{
++ input_unregister_device(stat->input_dev);
++ input_free_device(stat->input_dev);
++}
++
++static void poll_function_work(struct work_struct *polling_task)
++{
++ struct lsm9ds0_gyr_status *stat;
++ struct lsm9ds0_gyr_triple data_out;
++ int err;
++
++ stat = container_of((struct work_struct *)polling_task,
++ struct lsm9ds0_gyr_status, polling_task);
++
++ err = lsm9ds0_gyr_get_data(stat, &data_out);
++ if (err < 0)
++ dev_err(&stat->client->dev, "get_rotation_data failed.\n");
++ else
++ lsm9ds0_gyr_report_values(stat, &data_out);
++
++ hrtimer_start(&stat->hr_timer, stat->ktime, HRTIMER_MODE_REL);
++}
++
++enum hrtimer_restart poll_function_read(struct hrtimer *timer)
++{
++ struct lsm9ds0_gyr_status *stat;
++
++ stat = container_of((struct hrtimer *)timer,
++ struct lsm9ds0_gyr_status, hr_timer);
++
++ queue_work(lsm9ds0_gyr_workqueue, &stat->polling_task);
++ return HRTIMER_NORESTART;
++}
++
++static int lsm9ds0_gyr_probe(struct i2c_client *client,
++ const struct i2c_device_id *devid)
++{
++ struct lsm9ds0_gyr_status *stat;
++
++ u32 smbus_func = I2C_FUNC_SMBUS_BYTE_DATA |
++ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK ;
++
++ int err = -1;
++
++ dev_info(&client->dev, "probe start.\n");
++
++
++ stat = kzalloc(sizeof(*stat), GFP_KERNEL);
++ if (stat == NULL) {
++ dev_err(&client->dev,
++ "failed to allocate memory for module data\n");
++ err = -ENOMEM;
++ goto err0;
++ }
++
++
++ /* Support for both I2C and SMBUS adapter interfaces. */
++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
++ dev_warn(&client->dev, "client not i2c capable\n");
++ if (i2c_check_functionality(client->adapter, smbus_func)) {
++ stat->use_smbus = 1;
++ dev_warn(&client->dev, "client using SMBUS\n");
++ } else {
++ err = -ENODEV;
++ dev_err(&client->dev, "client nor SMBUS capable\n");
++ stat->use_smbus = 0;
++ goto err0;
++ }
++ }
++
++ if(lsm9ds0_gyr_workqueue == 0)
++ lsm9ds0_gyr_workqueue = create_workqueue("lsm9ds0_gyr_workqueue");
++
++ hrtimer_init(&stat->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
++ stat->hr_timer.function = &poll_function_read;
++
++ mutex_init(&stat->lock);
++ mutex_lock(&stat->lock);
++ stat->client = client;
++
++ stat->pdata = kmalloc(sizeof(*stat->pdata), GFP_KERNEL);
++ if (stat->pdata == NULL) {
++ dev_err(&client->dev,
++ "failed to allocate memory for pdata: %d\n", err);
++ goto err1;
++ }
++
++ if (client->dev.platform_data == NULL) {
++ default_lsm9ds0_gyr_pdata.gpio_int1 = int1_gpio;
++ default_lsm9ds0_gyr_pdata.gpio_int2 = int2_gpio;
++ memcpy(stat->pdata, &default_lsm9ds0_gyr_pdata,
++ sizeof(*stat->pdata));
++ dev_info(&client->dev, "using default plaform_data\n");
++ } else {
++ memcpy(stat->pdata, client->dev.platform_data,
++ sizeof(*stat->pdata));
++ }
++
++ err = lsm9ds0_gyr_validate_pdata(stat);
++ if (err < 0) {
++ dev_err(&client->dev, "failed to validate platform data\n");
++ goto err1_1;
++ }
++
++ i2c_set_clientdata(client, stat);
++
++ if (stat->pdata->init) {
++ err = stat->pdata->init();
++ if (err < 0) {
++ dev_err(&client->dev, "init failed: %d\n", err);
++ goto err1_1;
++ }
++ }
++
++
++ memset(stat->resume_state, 0, ARRAY_SIZE(stat->resume_state));
++
++ stat->resume_state[RES_CTRL_REG1] = ALL_ZEROES | ENABLE_ALL_AXES
++ | PM_NORMAL;
++ stat->resume_state[RES_CTRL_REG2] = ALL_ZEROES;
++ stat->resume_state[RES_CTRL_REG3] = ALL_ZEROES;
++ stat->resume_state[RES_CTRL_REG4] = ALL_ZEROES | BDU_ENABLE;
++ stat->resume_state[RES_CTRL_REG5] = ALL_ZEROES;
++ stat->resume_state[RES_FIFO_CTRL_REG] = ALL_ZEROES;
++
++ stat->polling_enabled = true;
++ dev_info(&client->dev, "polling mode enabled\n");
++
++ err = lsm9ds0_gyr_device_power_on(stat);
++ if (err < 0) {
++ dev_err(&client->dev, "power on failed: %d\n", err);
++ goto err2;
++ }
++
++ atomic_set(&stat->enabled, 1);
++
++ err = lsm9ds0_gyr_update_fs_range(stat, stat->pdata->fs_range);
++ if (err < 0) {
++ dev_err(&client->dev, "update_fs_range failed\n");
++ goto err2;
++ }
++
++ err = lsm9ds0_gyr_update_odr(stat, stat->pdata->poll_interval);
++ if (err < 0) {
++ dev_err(&client->dev, "update_odr failed\n");
++ goto err2;
++ }
++
++ err = lsm9ds0_gyr_input_init(stat);
++ if (err < 0)
++ goto err3;
++
++ err = create_sysfs_interfaces(&client->dev);
++ if (err < 0) {
++ dev_err(&client->dev,
++ "%s device register failed\n", LSM9DS0_GYR_DEV_NAME);
++ goto err4;
++ }
++
++ lsm9ds0_gyr_device_power_off(stat);
++
++ /* As default, do not report information */
++ atomic_set(&stat->enabled, 0);
++
++
++ if (stat->pdata->gpio_int2 >= 0) {
++ stat->irq2 = gpio_to_irq(stat->pdata->gpio_int2);
++ dev_info(&client->dev, "%s: %s has set irq2 to irq:"
++ " %d mapped on gpio:%d\n",
++ LSM9DS0_GYR_DEV_NAME, __func__, stat->irq2,
++ stat->pdata->gpio_int2);
++
++ INIT_WORK(&stat->irq2_work, lsm9ds0_gyr_irq2_work_func);
++ stat->irq2_work_queue =
++ create_singlethread_workqueue("lsm9ds0_gyr_irq2_wq");
++ if (!stat->irq2_work_queue) {
++ err = -ENOMEM;
++ dev_err(&client->dev, "cannot create "
++ "work queue2: %d\n", err);
++ goto err5;
++ }
++
++ err = request_irq(stat->irq2, lsm9ds0_gyr_isr2,
++ IRQF_TRIGGER_HIGH, "lsm9ds0_gyr_irq2", stat);
++
++ if (err < 0) {
++ dev_err(&client->dev, "request irq2 failed: %d\n", err);
++ goto err6;
++ }
++ disable_irq_nosync(stat->irq2);
++ }
++
++ mutex_unlock(&stat->lock);
++
++ INIT_WORK(&stat->polling_task, poll_function_work);
++ dev_info(&client->dev, "%s probed: device created successfully\n",
++ LSM9DS0_GYR_DEV_NAME);
++
++
++ return 0;
++
++/*err7:
++ free_irq(stat->irq2, stat);
++*/
++err6:
++ destroy_workqueue(stat->irq2_work_queue);
++err5:
++ lsm9ds0_gyr_device_power_off(stat);
++ remove_sysfs_interfaces(&client->dev);
++err4:
++ lsm9ds0_gyr_input_cleanup(stat);
++err3:
++ lsm9ds0_gyr_device_power_off(stat);
++err2:
++ if (stat->pdata->exit)
++ stat->pdata->exit();
++err1_1:
++ mutex_unlock(&stat->lock);
++ kfree(stat->pdata);
++err1:
++ destroy_workqueue(lsm9ds0_gyr_workqueue);
++ kfree(stat);
++err0:
++ pr_err("%s: Driver Initialization failed\n",
++ LSM9DS0_GYR_DEV_NAME);
++ return err;
++}
++
++static int lsm9ds0_gyr_remove(struct i2c_client *client)
++{
++ struct lsm9ds0_gyr_status *stat = i2c_get_clientdata(client);
++
++ dev_info(&stat->client->dev, "driver removing\n");
++
++ cancel_work_sync(&stat->polling_task);
++ if(!lsm9ds0_gyr_workqueue) {
++ flush_workqueue(lsm9ds0_gyr_workqueue);
++ destroy_workqueue(lsm9ds0_gyr_workqueue);
++ }
++ /*
++ if (stat->pdata->gpio_int1 >= 0)
++ {
++ free_irq(stat->irq1, stat);
++ gpio_free(stat->pdata->gpio_int1);
++ destroy_workqueue(stat->irq1_work_queue);
++ }
++ */
++ if (stat->pdata->gpio_int2 >= 0) {
++ free_irq(stat->irq2, stat);
++ gpio_free(stat->pdata->gpio_int2);
++ destroy_workqueue(stat->irq2_work_queue);
++ }
++
++ lsm9ds0_gyr_disable(stat);
++ lsm9ds0_gyr_input_cleanup(stat);
++
++ remove_sysfs_interfaces(&client->dev);
++
++ kfree(stat->pdata);
++ kfree(stat);
++ return 0;
++}
++
++static int lsm9ds0_gyr_suspend(struct device *dev)
++{
++ int err = 0;
++#define SLEEP
++#ifdef CONFIG_PM
++ struct i2c_client *client = to_i2c_client(dev);
++ struct lsm9ds0_gyr_status *stat = i2c_get_clientdata(client);
++ u8 buf[2];
++
++ dev_info(&client->dev, "suspend\n");
++
++ dev_dbg(&client->dev, "%s\n", __func__);
++ if (atomic_read(&stat->enabled)) {
++ mutex_lock(&stat->lock);
++ if (stat->polling_enabled) {
++ dev_info(&stat->client->dev, "polling mode disabled\n");
++ hrtimer_cancel(&stat->hr_timer);
++ }
++#ifdef SLEEP
++ err = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG1,
++ 0x0F, (ENABLE_NO_AXES | PM_NORMAL));
++#else
++ err = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG1,
++ 0x08, PM_OFF);
++#endif /*SLEEP*/
++ mutex_unlock(&stat->lock);
++ }
++#endif /*CONFIG_PM*/
++ return err;
++}
++
++static int lsm9ds0_gyr_resume(struct device *dev)
++{
++ int err = 0;
++#ifdef CONFIG_PM
++ struct i2c_client *client = to_i2c_client(dev);
++ struct lsm9ds0_gyr_status *stat = i2c_get_clientdata(client);
++ u8 buf[2];
++
++
++ dev_info(&client->dev, "resume\n");
++
++ dev_dbg(&client->dev, "%s\n", __func__);
++ if (atomic_read(&stat->enabled)) {
++ mutex_lock(&stat->lock);
++ if (stat->polling_enabled) {
++ dev_info(&stat->client->dev, "polling mode enabled\n");
++ hrtimer_init(&stat->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
++ }
++#ifdef SLEEP
++ err = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG1,
++ 0x0F, (ENABLE_ALL_AXES | PM_NORMAL));
++#else
++ err = lsm9ds0_gyr_register_update(stat, buf, CTRL_REG1,
++ 0x08, PM_NORMAL);
++#endif
++ mutex_unlock(&stat->lock);
++
++ }
++#endif /*CONFIG_PM*/
++ return err;
++}
++
++
++static const struct i2c_device_id lsm9ds0_gyr_id[] = {
++ { LSM9DS0_GYR_DEV_NAME , 0 },
++ {},
++};
++MODULE_DEVICE_TABLE(i2c, lsm9ds0_gyr_id);
++
++static const struct of_device_id lsm9ds0_gyr_of_match[] = {
++ { .compatible = "st,"LSM9DS0_GYR_DEV_NAME, },
++ { },
++};
++MODULE_DEVICE_TABLE(of, lsm9ds0_gyr_of_match);
++
++static const struct dev_pm_ops lsm9ds0_gyr_pm = {
++ .suspend = lsm9ds0_gyr_suspend,
++ .resume = lsm9ds0_gyr_resume,
++};
++
++static struct i2c_driver lsm9ds0_i2c_gyr_driver = {
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = LSM9DS0_GYR_DEV_NAME,
++ .pm = &lsm9ds0_gyr_pm,
++ .of_match_table = lsm9ds0_gyr_of_match,
++ },
++ .probe = lsm9ds0_gyr_probe,
++ .remove = lsm9ds0_gyr_remove,
++ .id_table = lsm9ds0_gyr_id,
++
++};
++
++module_i2c_driver(lsm9ds0_i2c_gyr_driver);
++
++MODULE_DESCRIPTION("lsm9ds0 gyroscope driver");
++MODULE_AUTHOR("Matteo Dameno, Denis Ciocca, STMicroelectronics");
++MODULE_LICENSE("GPL");
+diff --git a/include/linux/input/lsm9ds0.h b/include/linux/input/lsm9ds0.h
+new file mode 100644
+index 0000000..c115eb7
+--- /dev/null
++++ b/include/linux/input/lsm9ds0.h
+@@ -0,0 +1,201 @@
++/******************** (C) COPYRIGHT 2012 STMicroelectronics ********************
++*
++* File Name : lsm9ds0.h
++* Authors : MSH - C&I BU - Application Team
++* : Matteo Dameno (matteo.dameno@st.com)
++* : Denis Ciocca (denis.ciocca@st.com)
++* Version : V.1.0.2
++* Date : 2013/Oct/23
++*
++********************************************************************************
++*
++* 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.
++*
++* THE PRESENT SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
++* OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, FOR THE SOLE
++* PURPOSE TO SUPPORT YOUR APPLICATION DEVELOPMENT.
++* AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY DIRECT,
++* INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING FROM THE
++* CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE CODING
++* INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
++*
++********************************************************************************
++********************************************************************************
++* REVISON HISTORY
++* 1.0.1 | 2012/Aug/30 | Denis Ciocca | corrects gyr_get_data func
++* 1.0.2 | 2013/Oct/23 | Matteo Dameno | introduced acc_mag 1.0.5
++*******************************************************************************/
++
++#ifndef __LSM9DS0_H__
++#define __LSM9DS0_H__
++
++#define LSM9DS0_DEV_NAME "lsm9ds0_acc_mag" /* i2c system name */
++#define LSM9DS0_ACC_DEV_NAME "lsm9ds0_acc" /* Input file name */
++#define LSM9DS0_MAG_DEV_NAME "lsm9ds0_mag" /* Input file name */
++#define LSM9DS0_GYR_DEV_NAME "lsm9ds0_gyr" /* Input file name */
++
++#define LSM9DS0_SAD0L_ACC_MAG (0x02)
++#define LSM9DS0_SAD0H_ACC_MAG (0x01)
++#define LSM9DS0_SAD0L_GYR (0x00)
++#define LSM9DS0_SAD0H_GYR (0x01)
++
++/************************************************/
++/* Output data */
++/*************************************************
++accelerometer: ug
++magnetometer: ugauss
++gyroscope: udps
++*************************************************/
++
++/************************************************/
++/* sysfs data */
++/*************************************************
++accelerometer:
++ - pollrate->ms
++ - fullscale->g
++magnetometer:
++ - pollrate->ms
++ - fullscale->gauss
++gyroscope:
++ - pollrate->ms
++ - fullscale->dps
++*************************************************/
++
++#define LSM9DS0_ACC_MAG_I2C_SADROOT (0x07)
++
++/* I2C address if gyr SA0 pin to GND */
++#define LSM9DS0_ACC_MAG_I2C_SAD_L ((LSM9DS0_ACC_MAG_I2C_SADROOT<<2)| \
++ LSM9DS0_SAD0L_ACC_MAG)
++/* I2C address if gyr SA0 pin to Vdd */
++#define LSM9DS0_ACC_MAG_I2C_SAD_H ((LSM9DS0_ACC_MAG_I2C_SADROOT<<2)| \
++ LSM9DS0_SAD0H_ACC_MAG)
++
++/************************************************/
++/* Accelerometer section defines */
++/************************************************/
++
++/* Accelerometer Sensor Full Scale */
++#define LSM9DS0_ACC_FS_MASK (0x18)
++#define LSM9DS0_ACC_FS_2G (0x00) /* Full scale 2g */
++#define LSM9DS0_ACC_FS_4G (0x08) /* Full scale 4g */
++#define LSM9DS0_ACC_FS_8G (0x10) /* Full scale 8g */
++#define LSM9DS0_ACC_FS_16G (0x18) /* Full scale 16g */
++
++/* Accelerometer Anti-Aliasing Filter */
++#define ANTI_ALIASING_773 (0X00)
++#define ANTI_ALIASING_362 (0X40)
++#define ANTI_ALIASING_194 (0X80)
++#define ANTI_ALIASING_50 (0XC0)
++
++/************************************************/
++/* Magnetometer section defines */
++/************************************************/
++
++/* Magnetometer Sensor Full Scale */
++#define LSM9DS0_MAG_FS_MASK (0x60)
++#define LSM9DS0_MAG_FS_2G (0x00) /* Full scale 2 gauss */
++#define LSM9DS0_MAG_FS_4G (0x20) /* Full scale 4 gauss */
++#define LSM9DS0_MAG_FS_8G (0x40) /* Full scale 8 gauss */
++#define LSM9DS0_MAG_FS_12G (0x60) /* Full scale 12 gauss */
++
++/************************************************/
++/* Gyroscope section defines */
++/************************************************/
++
++#define LSM9DS0_GYR_I2C_SADROOT (0x35)
++
++/* I2C address if gyr SA0 pin to GND */
++#define LSM9DS0_GYR_I2C_SAD_L ((LSM9DS0_GYR_I2C_SADROOT<<1)| \
++ LSM9DS0_SAD0L_GYR)
++/* I2C address if gyr SA0 pin to Vdd */
++#define LSM9DS0_GYR_I2C_SAD_H ((LSM9DS0_GYR_I2C_SADROOT<<1)| \
++ LSM9DS0_SAD0H_GYR)
++
++#ifdef __KERNEL__
++
++/* to set gpios numb connected to gyro interrupt pins,
++ * the unused ones havew to be set to -EINVAL
++ */
++#define DEFAULT_INT1_GPIO (-EINVAL)
++#define DEFAULT_INT2_GPIO (-EINVAL)
++
++#define LSM9DS0_ACC_MIN_POLL_PERIOD_MS 1
++#define LSM9DS0_MAG_MIN_POLL_PERIOD_MS 5
++
++#define LSM9DS0_GYR_DEFAULT_INT1_GPIO (-EINVAL)
++#define LSM9DS0_GYR_DEFAULT_INT2_GPIO (-EINVAL)
++
++#define LSM9DS0_GYR_MIN_POLL_PERIOD_MS 2
++
++#define LSM9DS0_GYR_FS_250DPS (0x00)
++#define LSM9DS0_GYR_FS_500DPS (0x10)
++#define LSM9DS0_GYR_FS_2000DPS (0x30)
++
++struct lsm9ds0_acc_platform_data {
++
++ unsigned int poll_interval;
++ unsigned int min_interval;
++
++ u8 fs_range;
++
++ short rot_matrix[3][3];
++
++ u8 aa_filter_bandwidth;
++
++ int (*init)(void);
++ void (*exit)(void);
++ int (*power_on)(void);
++ int (*power_off)(void);
++
++ int gpio_int1;
++ int gpio_int2;
++};
++
++struct lsm9ds0_mag_platform_data {
++
++ unsigned int poll_interval;
++ unsigned int min_interval;
++
++ u8 fs_range;
++
++ short rot_matrix[3][3];
++
++ int (*init)(void);
++ void (*exit)(void);
++ int (*power_on)(void);
++ int (*power_off)(void);
++};
++
++struct lsm9ds0_gyr_platform_data {
++ int (*init)(void);
++ void (*exit)(void);
++ int (*power_on)(void);
++ int (*power_off)(void);
++ unsigned int poll_interval;
++ unsigned int min_interval;
++
++ u8 fs_range;
++
++ /* gpio ports for interrupt pads */
++ int gpio_int1;
++ int gpio_int2; /* int for fifo */
++
++ u8 axis_map_x;
++ u8 axis_map_y;
++ u8 axis_map_z;
++
++ u8 negate_x;
++ u8 negate_y;
++ u8 negate_z;
++};
++
++struct lsm9ds0_main_platform_data {
++
++ struct lsm9ds0_acc_platform_data *pdata_acc;
++ struct lsm9ds0_mag_platform_data *pdata_mag;
++};
++
++#endif /* __KERNEL__ */
++#endif /* __LSM9DS0_H__ */
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0051-TI-ADS111X-sigma-delta-ADC-driver.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0051-TI-ADS111X-sigma-delta-ADC-driver.patch
new file mode 100644
index 0000000..4b3238d
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0051-TI-ADS111X-sigma-delta-ADC-driver.patch
@@ -0,0 +1,320 @@
+From 623921d9a1815cb695b587088ea7909f2996b637 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Fri, 18 Sep 2015 16:19:57 +0300
+Subject: [PATCH 51/52] TI ADS111X sigma-delta ADC driver
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/iio/adc/Kconfig | 10 ++
+ drivers/iio/adc/Makefile | 1 +
+ drivers/iio/adc/ti-ads111x.c | 266 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 277 insertions(+)
+ create mode 100644 drivers/iio/adc/ti-ads111x.c
+
+diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
+index ab0767e6..2ad9d87 100644
+--- a/drivers/iio/adc/Kconfig
++++ b/drivers/iio/adc/Kconfig
+@@ -143,6 +143,16 @@ config TI_ADC081C
+ This driver can also be built as a module. If so, the module will be
+ called ti-adc081c.
+
++config TI_ADS111X
++ tristate "Texas Instruments ADS1113/4/5"
++ depends on I2C
++ help
++ If you say yes here you get support for Texas Instruments ADS1113/4/5
++ sigma-delta ADC chips.
++
++ This driver can also be built as a module. If so, the module will be
++ called ti-ads111x.
++
+ config TI_AM335X_ADC
+ tristate "TI's ADC driver"
+ depends on MFD_TI_AM335X_TSCADC
+diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
+index 0a825be..0a3c03f 100644
+--- a/drivers/iio/adc/Makefile
++++ b/drivers/iio/adc/Makefile
+@@ -15,5 +15,6 @@ obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
+ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
+ obj-$(CONFIG_MAX1363) += max1363.o
+ obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
++obj-$(CONFIG_TI_ADS111X) += ti-ads111x.o
+ obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
+ obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
+diff --git a/drivers/iio/adc/ti-ads111x.c b/drivers/iio/adc/ti-ads111x.c
+new file mode 100644
+index 0000000..98a28e3e
+--- /dev/null
++++ b/drivers/iio/adc/ti-ads111x.c
+@@ -0,0 +1,266 @@
++/*
++ * Copyright (C) 2015 CogentEmbedded Inc
++ *
++ * 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/err.h>
++#include <linux/i2c.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++
++#include <linux/iio/iio.h>
++#include <linux/regulator/consumer.h>
++
++#define REG_CONV_RES 0x00
++#define REG_CONFIG 0x01
++#define REG_LO_THRES 0x02
++#define REG_HI_THRES 0x03
++
++/* CONFIG reg bits */
++/* w: begin sigle conversion; r: operation status */
++#define CONFIG_OS (1 << 15)
++#define CONFIG_CH(n) (n << 12)
++/* ADS1114/5 only */
++#define CONFIG_PGA_6144 (0 << 9)
++#define CONFIG_PGA_4096 (1 << 9)
++#define CONFIG_PGA_2048 (2 << 9) /* def */
++#define CONFIG_PGA_1024 (3 << 9)
++#define CONFIG_PGA_0512 (4 << 9)
++#define CONFIG_PGA_0256 (5 << 9)
++#define CONFIG_MODE_CONT (0 << 8)
++#define CONFIG_MODE_SINGLE (1 << 8)
++#define CONFIG_DR_8SPS (0 << 5)
++#define CONFIG_DR_16SPS (1 << 5)
++#define CONFIG_DR_32SPS (2 << 5)
++#define CONFIG_DR_64SPS (3 << 5)
++#define CONFIG_DR_128SPS (4 << 5)
++#define CONFIG_DR_250SPS (5 << 5)
++#define CONFIG_DR_475SPS (6 << 5)
++#define CONFIG_DR_860SPS (7 << 5)
++/* ADS1114/5 only */
++#define CONFIG_COMP_HYST (0 << 4)
++#define CONFIG_COMP_WINDOW (1 << 4)
++#define CONFIG_COMP_POL_LOW (0 << 3)
++#define CONFIG_COMP_POL_HIGH (1 << 3)
++#define CONFIG_COMP_NONLATCH (0 << 2)
++#define CONFIG_COMP_LATCH (1 << 2)
++#define CONFIG_COMP_QUE_1 (0 << 0)
++#define CONFIG_COMP_QUE_2 (1 << 0)
++#define CONFIG_COMP_QUE_4 (2 << 0)
++#define CONFIG_COMP_QUE_DIS (3 << 0)
++
++struct ads111x {
++ struct i2c_client *i2c;
++ int type;
++};
++
++enum ads111x_types {
++ ads1113,
++ ads1114,
++ ads1115,
++};
++
++/* Applies to ads1115 */
++enum ads1115_modes {
++ p0n1, p0n3, p1n3, p2n3,
++ p0, p1, p2, p3
++};
++
++/* unipolar channel */
++#define ADS1115_CHAN_U(num, addr) \
++ { \
++ .type = IIO_VOLTAGE, \
++ .indexed = 1, \
++ .channel = num, \
++ .address = addr, \
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
++ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
++ .datasheet_name = "AIN"#num, \
++ .scan_type = { \
++ .sign = 'u', \
++ .realbits = 16, \
++ .storagebits = 16, \
++ .endianness = IIO_BE, \
++ }, \
++ }
++
++/* bipolar channel */
++#define ADS111X_CHAN_B(num, num2, addr) \
++ { \
++ .type = IIO_VOLTAGE, \
++ .differential = 1, \
++ .indexed = 1, \
++ .channel = num, \
++ .channel2 = num2, \
++ .address = addr, \
++ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
++ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
++ .datasheet_name = "AIN"#num"-AIN"#num2, \
++ .scan_type = { \
++ .sign = 'u', \
++ .realbits = 16, \
++ .storagebits = 16, \
++ .endianness = IIO_BE, \
++ }, \
++ }
++
++static const struct iio_chan_spec ads111x_channels[] = {
++ /* ADS1113/4/5 */
++ ADS111X_CHAN_B(0, 1, p0n1),
++ /* ADS1115 only */
++ ADS111X_CHAN_B(0, 3, p0n3),
++ ADS111X_CHAN_B(1, 3, p1n3),
++ ADS111X_CHAN_B(2, 3, p2n3),
++ ADS1115_CHAN_U(0, p0),
++ ADS1115_CHAN_U(1, p1),
++ ADS1115_CHAN_U(2, p2),
++ ADS1115_CHAN_U(3, p3),
++};
++
++static int ads111x_read_raw(struct iio_dev *iio,
++ struct iio_chan_spec const *channel, int *value,
++ int *shift, long mask)
++{
++ struct ads111x *adc = iio_priv(iio);
++ u16 config_reg;
++ int err;
++
++ switch (mask) {
++ case IIO_CHAN_INFO_RAW:
++ config_reg =
++ CONFIG_OS |
++ CONFIG_CH(channel->address) |
++ CONFIG_PGA_2048 |
++ CONFIG_MODE_SINGLE |
++ CONFIG_DR_860SPS |
++ CONFIG_COMP_QUE_DIS;
++ err = i2c_smbus_write_word_swapped(adc->i2c, REG_CONFIG, config_reg);
++ if (err < 0) {
++ dev_err(&iio->dev, "REG_CONFIG write err %d\n", err);
++ return err;
++ }
++ mdelay(1);
++ err = i2c_smbus_read_word_swapped(adc->i2c, REG_CONFIG);
++ if (err < 0) {
++ dev_err(&iio->dev, "REG_CONFIG read err %d\n", err);
++ return err;
++ }
++ if (err & CONFIG_OS) {
++ dev_err(&iio->dev, "ADC not ready 0x%04x\n", err);
++ return -EBUSY;
++ }
++ err = i2c_smbus_read_word_swapped(adc->i2c, REG_CONV_RES);
++ if (err < 0) {
++ dev_err(&iio->dev, "REG_CONV_RES read err %d\n", err);
++ return err;
++ }
++
++ *value = err;
++ return IIO_VAL_INT;
++
++ case IIO_CHAN_INFO_SCALE:
++ *value = 2048000;
++ *shift = 0;
++ return IIO_VAL_INT_PLUS_MICRO;
++
++ default:
++ break;
++ }
++
++ return -EINVAL;
++}
++
++static const struct iio_info ads111x_info = {
++ .read_raw = ads111x_read_raw,
++ .driver_module = THIS_MODULE,
++};
++
++static int ads111x_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
++{
++ struct iio_dev *iio;
++ struct ads111x *adc;
++ int err;
++
++ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
++ return -ENODEV;
++
++ iio = iio_device_alloc(sizeof(*adc));
++ if (!iio)
++ return -ENOMEM;
++
++ adc = iio_priv(iio);
++ adc->i2c = client;
++ adc->type = id->driver_data;
++
++ iio->dev.parent = &client->dev;
++ iio->name = dev_name(&client->dev);
++ iio->modes = INDIO_DIRECT_MODE;
++ iio->info = &ads111x_info;
++
++ iio->channels = ads111x_channels;
++ if (id->driver_data == ads1115)
++ iio->num_channels = ARRAY_SIZE(ads111x_channels);
++ else
++ iio->num_channels = 1;
++
++ err = iio_device_register(iio);
++ if (err < 0)
++ goto iio_free;
++
++ i2c_set_clientdata(client, iio);
++
++ return 0;
++
++iio_free:
++ iio_device_free(iio);
++
++ return err;
++}
++
++static int ads111x_remove(struct i2c_client *client)
++{
++ struct iio_dev *iio = i2c_get_clientdata(client);
++
++ iio_device_unregister(iio);
++ iio_device_free(iio);
++
++ return 0;
++}
++
++static const struct i2c_device_id ads111x_id[] = {
++ { "ads1113", ads1113 },
++ { "ads1114", ads1114 },
++ { "ads1115", ads1115 },
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, ads111x_id);
++
++#ifdef CONFIG_OF
++static const struct of_device_id ads111x_of_match[] = {
++ { .compatible = "ti,ads1113" },
++ { .compatible = "ti,ads1114" },
++ { .compatible = "ti,ads1115" },
++ { }
++};
++MODULE_DEVICE_TABLE(of, ads111x_of_match);
++#endif
++
++static struct i2c_driver ads111x_driver = {
++ .driver = {
++ .name = "ads111x",
++ .owner = THIS_MODULE,
++ .of_match_table = of_match_ptr(ads111x_of_match),
++ },
++ .probe = ads111x_probe,
++ .remove = ads111x_remove,
++ .id_table = ads111x_id,
++};
++module_i2c_driver(ads111x_driver);
++
++MODULE_AUTHOR("Andrey Gusakov <andrey.gusakov@cogentembedded.com");
++MODULE_DESCRIPTION("Texas Instruments ADS1113/4/5 driver");
++MODULE_LICENSE("GPL v2");
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0051a-Cleanup-TI-ADS111x-ADC-driver.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0051a-Cleanup-TI-ADS111x-ADC-driver.patch
new file mode 100644
index 0000000..c10e75a
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0051a-Cleanup-TI-ADS111x-ADC-driver.patch
@@ -0,0 +1,162 @@
+From f73d05506794b6f4090e4c5c701c2362a66b07ec Mon Sep 17 00:00:00 2001
+From: Petr Nechaev <petr.nechaev@cogentembedded.com>
+Date: Wed, 28 Oct 2015 16:24:28 +0300
+Subject: [PATCH 1/1] Cleanup TI ADS111x ADC driver
+
+* added locks for simultaneous multi-channel read
+* fixed error messages
+* converted result to signed 16-bit value
+* added safeguard against channel numbers > 7
+---
+ drivers/iio/adc/ti-ads111x.c | 96 ++++++++++++++++++++++++++++++--------------
+ 1 file changed, 66 insertions(+), 30 deletions(-)
+
+diff --git a/drivers/iio/adc/ti-ads111x.c b/drivers/iio/adc/ti-ads111x.c
+index 98a28e3e..bf91e04 100644
+--- a/drivers/iio/adc/ti-ads111x.c
++++ b/drivers/iio/adc/ti-ads111x.c
+@@ -12,7 +12,6 @@
+ #include <linux/delay.h>
+
+ #include <linux/iio/iio.h>
+-#include <linux/regulator/consumer.h>
+
+ #define REG_CONV_RES 0x00
+ #define REG_CONFIG 0x01
+@@ -22,7 +21,7 @@
+ /* CONFIG reg bits */
+ /* w: begin sigle conversion; r: operation status */
+ #define CONFIG_OS (1 << 15)
+-#define CONFIG_CH(n) (n << 12)
++#define CONFIG_CH(n) ((n & 0x7) << 12)
+ /* ADS1114/5 only */
+ #define CONFIG_PGA_6144 (0 << 9)
+ #define CONFIG_PGA_4096 (1 << 9)
+@@ -80,7 +79,7 @@ enum ads1115_modes {
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = "AIN"#num, \
+ .scan_type = { \
+- .sign = 'u', \
++ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+@@ -100,7 +99,7 @@ enum ads1115_modes {
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = "AIN"#num"-AIN"#num2, \
+ .scan_type = { \
+- .sign = 'u', \
++ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+@@ -120,45 +119,81 @@ static const struct iio_chan_spec ads111x_channels[] = {
+ ADS1115_CHAN_U(3, p3),
+ };
+
+-static int ads111x_read_raw(struct iio_dev *iio,
+- struct iio_chan_spec const *channel, int *value,
+- int *shift, long mask)
++static int ads111x_read_single_channel(struct iio_dev *iio,
++ struct iio_chan_spec const *channel,
++ int *value)
+ {
+- struct ads111x *adc = iio_priv(iio);
+ u16 config_reg;
+- int err;
++ int ret;
++ int iter;
++ struct ads111x *adc = iio_priv(iio);
+
+- switch (mask) {
+- case IIO_CHAN_INFO_RAW:
+- config_reg =
++ mutex_lock(&iio->mlock);
++
++ /* start the conversion */
++ config_reg =
+ CONFIG_OS |
+ CONFIG_CH(channel->address) |
+ CONFIG_PGA_2048 |
+ CONFIG_MODE_SINGLE |
+ CONFIG_DR_860SPS |
+ CONFIG_COMP_QUE_DIS;
+- err = i2c_smbus_write_word_swapped(adc->i2c, REG_CONFIG, config_reg);
+- if (err < 0) {
+- dev_err(&iio->dev, "REG_CONFIG write err %d\n", err);
+- return err;
+- }
+- mdelay(1);
+- err = i2c_smbus_read_word_swapped(adc->i2c, REG_CONFIG);
+- if (err < 0) {
+- dev_err(&iio->dev, "REG_CONFIG read err %d\n", err);
+- return err;
++ ret = i2c_smbus_write_word_swapped(adc->i2c, REG_CONFIG, config_reg);
++ if (ret < 0) {
++ dev_err(&iio->dev, "Error writing to REG_CONFIG %d\n", ret);
++ goto error_ret;
++ }
++
++ for (iter = 0; iter < 2; iter++)
++ {
++ /* wait for conversion to complete */
++ ret = msleep_interruptible(2);
++ if (ret < 0)
++ goto error_ret;
++
++ /* read status */
++ ret = i2c_smbus_read_word_swapped(adc->i2c, REG_CONFIG);
++ if (ret < 0) {
++ dev_err(&iio->dev, "Error reading REG_CONFIG %d\n", ret);
++ goto error_ret;
+ }
+- if (err & CONFIG_OS) {
+- dev_err(&iio->dev, "ADC not ready 0x%04x\n", err);
+- return -EBUSY;
++ if ((ret & CONFIG_OS) != CONFIG_OS) {
++ dev_err(&iio->dev, "ADC not ready 0x%04x\n", ret);
++ ret = -EBUSY;
++ continue;
+ }
+- err = i2c_smbus_read_word_swapped(adc->i2c, REG_CONV_RES);
+- if (err < 0) {
+- dev_err(&iio->dev, "REG_CONV_RES read err %d\n", err);
++ }
++ if (ret < 0)
++ goto error_ret;
++
++ /* read value */
++ ret = i2c_smbus_read_word_swapped(adc->i2c, REG_CONV_RES);
++ if (ret < 0) {
++ dev_err(&iio->dev, "Error reading REG_CONV_RES %d\n", ret);
++ goto error_ret;
++ }
++
++ *value = (s16) (ret & 0xFFFF);
++ ret = 1;
++
++error_ret:
++ mutex_unlock(&iio->mlock);
++ return ret;
++
++}
++
++static int ads111x_read_raw(struct iio_dev *iio,
++ struct iio_chan_spec const *channel, int *value,
++ int *shift, long mask)
++{
++ int err;
++
++ switch (mask) {
++ case IIO_CHAN_INFO_RAW:
++ err = ads111x_read_single_channel(iio, channel, value);
++ if (err < 0)
+ return err;
+- }
+
+- *value = err;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+--
+2.4.3
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0059-crypto-aead-Add-crypto_aead_set_reqsize-helper.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0059-crypto-aead-Add-crypto_aead_set_reqsize-helper.patch
new file mode 100644
index 0000000..6efee7b
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/0059-crypto-aead-Add-crypto_aead_set_reqsize-helper.patch
@@ -0,0 +1,32 @@
+From 827723430bd488d9b378ae4bfecfaa9ab198c489 Mon Sep 17 00:00:00 2001
+From: Herbert Xu <herbert@gondor.apana.org.au>
+Date: Mon, 11 May 2015 17:47:52 +0800
+Subject: [PATCH] crypto: aead - Add crypto_aead_set_reqsize helper
+
+This patch adds the helper crypto_aead_set_reqsize so that people
+don't have to directly access the aead internals to set the reqsize.
+
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ include/crypto/internal/aead.h | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/include/crypto/internal/aead.h b/include/crypto/internal/aead.h
+index 2eba340..750948c 100644
+--- a/include/crypto/internal/aead.h
++++ b/include/crypto/internal/aead.h
+@@ -78,5 +78,11 @@ static inline void aead_givcrypt_complete(struct aead_givcrypt_request *req,
+ aead_request_complete(&req->areq, err);
+ }
+
++static inline void crypto_aead_set_reqsize(struct crypto_aead *aead,
++ unsigned int reqsize)
++{
++ crypto_aead_crt(aead)->reqsize = reqsize;
++}
++
+ #endif /* _CRYPTO_INTERNAL_AEAD_H */
+
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter.cfg b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter.cfg
index 5b16fb7..74452bd 100644
--- a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter.cfg
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter.cfg
@@ -1,3 +1,21 @@
CONFIG_CAN=y
-CONFIG_CAN_VCAN=y
CONFIG_CAN_RCAR=y
+CONFIG_VIDEO_RENESAS_JPU=y
+CONFIG_RAVB=y
+CONFIG_CGROUPS=y
+CONFIG_RELAY=y
+CONFIG_PADATA=y
+CONFIG_INPUT_FF_MEMLESS=y
+CONFIG_INPUT_JOYDEV=y
+CONFIG_INPUT_MISC=y
+CONFIG_SPI_GPIO=y
+CONFIG_HID_LOGITECH=y
+CONFIG_LOGIWHEELS_FF=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_SERIAL_CONSOLE=y
+CONFIG_USB_SERIAL_GENERIC=y
+CONFIG_USB_SERIAL_PL2303=y
+CONFIG_EXT4_FS=y
+CONFIG_JBD2=y
+CONFIG_DEBUG_FS=y
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0002-Porter-adapt-max9272-ov10635-driver-for-porter-exp-b.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0002-Porter-adapt-max9272-ov10635-driver-for-porter-exp-b.patch
new file mode 100644
index 0000000..eef5751
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0002-Porter-adapt-max9272-ov10635-driver-for-porter-exp-b.patch
@@ -0,0 +1,60 @@
+From f54cc5cd6059fd65d88040805a5da72e894ad9de Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Thu, 28 Jan 2016 19:46:49 +0300
+Subject: [PATCH 02/10] Porter: adapt max9272-ov10635 driver for porter exp
+ board
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/media/i2c/soc_camera/max9272_ov10635.h | 4 ++--
+ drivers/media/i2c/soc_camera/max9272_ov10635_setup.c | 12 ++++++------
+ 2 files changed, 8 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/media/i2c/soc_camera/max9272_ov10635.h b/drivers/media/i2c/soc_camera/max9272_ov10635.h
+index de1d351..80e9af4 100644
+--- a/drivers/media/i2c/soc_camera/max9272_ov10635.h
++++ b/drivers/media/i2c/soc_camera/max9272_ov10635.h
+@@ -17,8 +17,8 @@
+ #define dev_dbg dev_info
+ #endif
+
+-#define MAX9275_PRESENT /* MAX9275 presents on I2C bus */
+-#define MAXIM_NUM 4 /* number of cameras */
++//#define MAX9275_PRESENT /* MAX9275 presents on I2C bus */
++#define MAXIM_NUM 1 /* number of cameras */
+ #define MAXIM_NUM_RETRIES 1 /* number of read/write retries */
+
+ #define MAXIM_I2C_I2C_SPEED_400KHZ (0x5 << 2) /* 339 kbps */
+diff --git a/drivers/media/i2c/soc_camera/max9272_ov10635_setup.c b/drivers/media/i2c/soc_camera/max9272_ov10635_setup.c
+index 0d4db0f..d01eb5c 100644
+--- a/drivers/media/i2c/soc_camera/max9272_ov10635_setup.c
++++ b/drivers/media/i2c/soc_camera/max9272_ov10635_setup.c
+@@ -39,6 +39,12 @@ static int maxim_probe(struct i2c_client *client,
+ }
+ }
+
++ tmp_addr = client->addr;
++
++ /* power up camera */
++ client->addr = 0x48; /* MAX9272-CAM0 I2C */
++ maxim_reg8_write(client, 0x0e, 0x00); /* GP0 low - enable power for camera */
++
+ /*
+ * Powered MCU IMI cameras need delay between power-on and R-Car access to avoid
+ * i2c bus conflicts since linux kernel does not support i2c multi-mastering,
+@@ -47,12 +53,6 @@ static int maxim_probe(struct i2c_client *client,
+ */
+ mdelay(MAXIM_IMI_MCU_DELAY);
+
+- tmp_addr = client->addr;
+-
+- /* power down cascaded MAX9272 chips */
+- client->addr = 0x48; /* MAX9272-CAM0 I2C */
+- maxim_reg8_write(client, 0x0e, 0x02); /* GP0 high */
+-
+ #ifdef MAX9275_PRESENT
+ /*
+ * SETUP MAX9275 I2C
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0003-Porter-add-LVDS-camera.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0003-Porter-add-LVDS-camera.patch
new file mode 100644
index 0000000..45a93cf
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0003-Porter-add-LVDS-camera.patch
@@ -0,0 +1,88 @@
+From 106ac216eb15205cdcbbd681662b3d018670ca54 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Mon, 30 May 2016 16:16:58 +0300
+Subject: [PATCH] Porter: add LVDS camera
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ arch/arm/mach-shmobile/board-porter-reference.c | 21 +++++++++++++++++----
+ 1 file changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/arch/arm/mach-shmobile/board-porter-reference.c b/arch/arm/mach-shmobile/board-porter-reference.c
+index a286a80..a3af264 100644
+--- a/arch/arm/mach-shmobile/board-porter-reference.c
++++ b/arch/arm/mach-shmobile/board-porter-reference.c
+@@ -148,6 +148,7 @@ static const struct clk_name clk_names[] __initconst = {
+ { "lvds0", "lvds.0", "rcar-du-r8a7791" },
+ { "hsusb", NULL, "usb_phy_rcar_gen2" },
+ { "vin0", NULL, "r8a7791-vin.0" },
++ { "vin2", NULL, "r8a7791-vin.2" },
+ { "vsps", NULL, NULL },
+ #if IS_ENABLED(CONFIG_VIDEO_RENESAS_VSP1) && \
+ !defined(CONFIG_DRM_RCAR_DU_CONNECT_VSP)
+@@ -673,6 +674,9 @@ static const struct resource vin_resources[] __initconst = {
+ /* VIN1 */
+ DEFINE_RES_MEM(0xe6ef1000, 0x1000),
+ DEFINE_RES_IRQ(gic_spi(189)),
++ /* VIN2 */
++ DEFINE_RES_MEM(0xe6ef2000, 0x1000),
++ DEFINE_RES_IRQ(gic_spi(190)),
+ };
+
+ static void __init porter_add_vin_device(unsigned idx,
+@@ -689,12 +693,12 @@ static void __init porter_add_vin_device(unsigned idx,
+ .size_data = sizeof(*pdata),
+ };
+
+- BUG_ON(idx > 1);
++ BUG_ON(idx > 2);
+
+ platform_device_register_full(&vin_info);
+ }
+
+-#define PORTER_CAMERA(idx, name, addr, pdata, flag) \
++#define PORTER_CAMERA(idx, name, bus, addr, pdata, flag) \
+ static struct i2c_board_info i2c_cam##idx##_device = { \
+ I2C_BOARD_INFO(name, addr), \
+ }; \
+@@ -706,12 +710,13 @@ static struct rcar_vin_platform_data vin##idx##_pdata = { \
+ static struct soc_camera_link cam##idx##_link = { \
+ .bus_id = idx, \
+ .board_info = &i2c_cam##idx##_device, \
+- .i2c_adapter_id = 2, \
++ .i2c_adapter_id = bus, \
+ .module_name = name, \
+ .priv = pdata, \
+ }
+
+-PORTER_CAMERA(0, "adv7180", 0x20, NULL, RCAR_VIN_BT656);
++PORTER_CAMERA(0, "adv7180", 2, 0x20, NULL, RCAR_VIN_BT656);
++PORTER_CAMERA(2, "max9272-ov10635", 0, 0x30 + 1, NULL, RCAR_VIN_BT656);
+
+ static void __init porter_add_camera0_device(void)
+ {
+@@ -720,6 +725,13 @@ static void __init porter_add_camera0_device(void)
+ porter_add_vin_device(0, &vin0_pdata);
+ }
+
++static void __init porter_add_camera2_device(void)
++{
++ platform_device_register_data(&platform_bus, "soc-camera-pdrv", 2,
++ &cam2_link, sizeof(cam2_link));
++ porter_add_vin_device(2, &vin2_pdata);
++}
++
+ /* VSP1 */
+ #if IS_ENABLED(CONFIG_VIDEO_RENESAS_VSP1) && \
+ !defined(CONFIG_DRM_RCAR_DU_CONNECT_VSP)
+@@ -911,6 +923,7 @@ static void __init porter_add_standard_devices(void)
+ porter_add_du_device();
+ porter_add_usb_devices();
+ porter_add_camera0_device();
++ porter_add_camera2_device();
+ #if IS_ENABLED(CONFIG_VIDEO_RENESAS_VSP1) && \
+ !defined(CONFIG_DRM_RCAR_DU_CONNECT_VSP)
+ porter_add_vsp1_devices();
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0004-GPIO-CPLD-gpio-extender-driver.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0004-GPIO-CPLD-gpio-extender-driver.patch
new file mode 100644
index 0000000..5efac9f
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0004-GPIO-CPLD-gpio-extender-driver.patch
@@ -0,0 +1,248 @@
+From 2a3918e3dc0cd00c7abf52a0d13dafb458f22acd Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Fri, 29 Jan 2016 15:50:00 +0300
+Subject: [PATCH 04/10] GPIO: CPLD gpio extender driver
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/gpio/Kconfig | 7 ++
+ drivers/gpio/Makefile | 1 +
+ drivers/gpio/gpio-cpld.c | 195 ++++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 203 insertions(+)
+ create mode 100644 drivers/gpio/gpio-cpld.c
+
+diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
+index 4389778..12a2f8a 100644
+--- a/drivers/gpio/Kconfig
++++ b/drivers/gpio/Kconfig
+@@ -209,6 +209,13 @@ config GPIO_RCAR
+ help
+ Say yes here to support GPIO on Renesas R-Car SoCs.
+
++config GPIO_PORTER_CPLD
++ bool "Renesas Porter extension board CPLD gpios"
++ depends on ARM
++ help
++ Say yes here to support GPIOs on Renesas Porter
++ extension board from CogentEmbedded.
++
+ config GPIO_SPEAR_SPICS
+ bool "ST SPEAr13xx SPI Chip Select as GPIO support"
+ depends on PLAT_SPEAR
+diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
+index 2636985..8e66571 100644
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -59,6 +59,7 @@ obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
+ obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
+ obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
+ obj-$(CONFIG_GPIO_RCAR) += gpio-rcar.o
++obj-$(CONFIG_GPIO_PORTER_CPLD) += gpio-cpld.o
+ obj-$(CONFIG_PLAT_SAMSUNG) += gpio-samsung.o
+ obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
+ obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
+diff --git a/drivers/gpio/gpio-cpld.c b/drivers/gpio/gpio-cpld.c
+new file mode 100644
+index 0000000..25b57a3
+--- /dev/null
++++ b/drivers/gpio/gpio-cpld.c
+@@ -0,0 +1,195 @@
++/*
++ * r8a7791-porter extension board CPLD access driver
++ *
++ * Copyright (C) 2016 Cogent Embedded, Inc.
++ *
++ * 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/module.h>
++#include <linux/of.h>
++#include <linux/of_device.h>
++#include <linux/of_gpio.h>
++#include <linux/gpio.h>
++
++struct porter_cpld_gpio_priv {
++ unsigned data_gpio; /* data gpio */
++ unsigned ptrc_gpio; /* pointer clock gpio */
++ unsigned bufc_gpio; /* buffer clock gpio */
++
++ struct gpio_chip gpio_chip;
++
++ uint16_t state; /* saved gpios state */
++};
++
++static int porter_cpld_gpio_clock_val(struct porter_cpld_gpio_priv *priv,
++ int bit, int val)
++{
++ int i;
++
++ /* reset pointer */
++ gpio_set_value(priv->data_gpio, 1);
++ gpio_set_value(priv->ptrc_gpio, 1);
++ gpio_set_value(priv->data_gpio, 0);
++ gpio_set_value(priv->ptrc_gpio, 0);
++
++ for (i = 0; i < bit; i++) {
++ gpio_set_value(priv->ptrc_gpio, 1);
++ gpio_set_value(priv->ptrc_gpio, 0);
++ }
++
++ gpio_set_value(priv->data_gpio, val);
++ gpio_set_value(priv->bufc_gpio, 1);
++ gpio_set_value(priv->bufc_gpio, 0);
++
++ if (val)
++ priv->state |= (1 << bit);
++ else
++ priv->state &= ~(1 << bit);
++
++ return 0;
++}
++
++static int porter_cpld_gpio_get_value(struct gpio_chip *gc, unsigned off)
++{
++ struct porter_cpld_gpio_priv *priv;
++
++ priv = container_of(gc, struct porter_cpld_gpio_priv, gpio_chip);
++
++ return !!(priv->state & (1 << off));
++}
++
++static void porter_cpld_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
++{
++ struct porter_cpld_gpio_priv *priv;
++
++ priv = container_of(gc, struct porter_cpld_gpio_priv, gpio_chip);
++
++ porter_cpld_gpio_clock_val(priv, off, val);
++}
++
++static int porter_cpld_gpio_direction_input(struct gpio_chip *gc, unsigned off)
++{
++ return -EPERM;
++}
++
++static int porter_cpld_gpio_direction_output(struct gpio_chip *gc, unsigned off,
++ int val)
++{
++ struct porter_cpld_gpio_priv *priv;
++
++ priv = container_of(gc, struct porter_cpld_gpio_priv, gpio_chip);
++
++ return porter_cpld_gpio_clock_val(priv, off, val);
++}
++
++static struct porter_cpld_gpio_priv *porter_cpld_gpio_parse_dt(struct device *dev)
++{
++ struct device_node *np = dev->of_node;
++ struct porter_cpld_gpio_priv *priv;
++
++ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return NULL;
++
++ priv->data_gpio = of_get_named_gpio(np, "porter-cpld-gpio,data-gpio", 0);
++ if (priv->data_gpio < 0)
++ return NULL;
++
++ priv->ptrc_gpio = of_get_named_gpio(np, "porter-cpld-gpio,ptrc-gpio", 0);
++ if (priv->ptrc_gpio < 0)
++ return NULL;
++
++ priv->bufc_gpio = of_get_named_gpio(np, "porter-cpld-gpio,bufc-gpio", 0);
++ if (priv->bufc_gpio < 0)
++ return NULL;
++
++ return priv;
++}
++
++static void porter_cpld_gpio_init(struct porter_cpld_gpio_priv *priv)
++{
++ gpio_request_one(priv->data_gpio, GPIOF_OUT_INIT_HIGH, "cpld-data");
++ gpio_request_one(priv->ptrc_gpio, GPIOF_OUT_INIT_HIGH, "cpld-ptrc");
++ gpio_request_one(priv->bufc_gpio, GPIOF_OUT_INIT_HIGH, "cpld-bufc");
++}
++
++static int porter_cpld_gpio_probe(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct porter_cpld_gpio_priv *priv;
++ struct gpio_chip *gc;
++ int ret;
++
++ priv = porter_cpld_gpio_parse_dt(dev);
++ if (!priv)
++ return -EPROBE_DEFER;
++
++ /* request gpios */
++ porter_cpld_gpio_init(priv);
++
++ priv->state = 0x0;
++ gc = &priv->gpio_chip;
++ gc->of_node = pdev->dev.of_node;
++ gc->direction_input = porter_cpld_gpio_direction_input;
++ gc->direction_output = porter_cpld_gpio_direction_output;
++ gc->get = porter_cpld_gpio_get_value;
++ gc->set = porter_cpld_gpio_set_value;
++
++ gc->base = -1;
++ gc->ngpio = 16;
++ gc->label = "porter-gpio";
++ gc->owner = THIS_MODULE;
++
++ ret = gpiochip_add(&priv->gpio_chip);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int porter_cpld_gpio_remove(struct platform_device *pdev)
++{
++ int ret;
++ struct device *dev = &pdev->dev;
++ struct porter_cpld_gpio_priv *priv = dev_get_drvdata(dev);
++
++ ret = gpiochip_remove(&priv->gpio_chip);
++ if (ret) {
++ dev_err(dev, "gpiochip_remove failed %d\n", ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++static const struct of_device_id porter_cpld_gpio_dt_ids[] = {
++ { .compatible = "porter-cpld-gpio" },
++ {},
++};
++MODULE_DEVICE_TABLE(of, porter_cpld_gpio_dt_ids);
++
++static struct platform_driver porter_cpld_gpio_driver = {
++ .driver = {
++ .name = "porter-cpld-gpio",
++ .owner = THIS_MODULE,
++ .of_match_table = of_match_ptr(porter_cpld_gpio_dt_ids),
++ },
++ .probe = porter_cpld_gpio_probe,
++ .remove = porter_cpld_gpio_remove,
++};
++
++static int __init porter_cpld_gpio_driver_init(void)
++{
++ return platform_driver_register(&porter_cpld_gpio_driver);
++}
++
++static void __exit porter_cpld_gpio_driver_exit(void)
++{
++ platform_driver_unregister(&porter_cpld_gpio_driver);
++}
++
++subsys_initcall_sync(porter_cpld_gpio_driver_init);
++module_exit(porter_cpld_gpio_driver_exit);
+\ No newline at end of file
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0012-regmap-Implemented-default-cache-sync-operation.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0012-regmap-Implemented-default-cache-sync-operation.patch
new file mode 100644
index 0000000..b00cfc8
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0012-regmap-Implemented-default-cache-sync-operation.patch
@@ -0,0 +1,102 @@
+From 2de1db4a592a5f18d1b3082fb93deb761b7477fb Mon Sep 17 00:00:00 2001
+From: Maarten ter Huurne <maarten@treewalker.org>
+Date: Mon, 3 Jun 2013 00:15:26 +0200
+Subject: [PATCH 12/16] regmap: Implemented default cache sync operation
+
+This can be used for cache types for which syncing values one by one is
+equally efficient as syncing a range, such as the flat cache.
+
+Signed-off-by: Maarten ter Huurne <maarten@treewalker.org>
+Signed-off-by: Mark Brown <broonie@linaro.org>
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/base/regmap/regcache.c | 46 ++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 42 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
+index 46283fd..ffd8db6 100644
+--- a/drivers/base/regmap/regcache.c
++++ b/drivers/base/regmap/regcache.c
+@@ -250,6 +250,38 @@ int regcache_write(struct regmap *map,
+ return 0;
+ }
+
++static int regcache_default_sync(struct regmap *map, unsigned int min,
++ unsigned int max)
++{
++ unsigned int reg;
++
++ for (reg = min; reg <= max; reg++) {
++ unsigned int val;
++ int ret;
++
++ if (regmap_volatile(map, reg))
++ continue;
++
++ ret = regcache_read(map, reg, &val);
++ if (ret)
++ return ret;
++
++ /* Is this the hardware default? If so skip. */
++ ret = regcache_lookup_reg(map, reg);
++ if (ret >= 0 && val == map->reg_defaults[ret].def)
++ continue;
++
++ map->cache_bypass = 1;
++ ret = _regmap_write(map, reg, val);
++ map->cache_bypass = 0;
++ if (ret)
++ return ret;
++ dev_dbg(map->dev, "Synced register %#x, value %#x\n", reg, val);
++ }
++
++ return 0;
++}
++
+ /**
+ * regcache_sync: Sync the register cache with the hardware.
+ *
+@@ -268,7 +300,7 @@ int regcache_sync(struct regmap *map)
+ const char *name;
+ unsigned int bypass;
+
+- BUG_ON(!map->cache_ops || !map->cache_ops->sync);
++ BUG_ON(!map->cache_ops);
+
+ map->lock(map->lock_arg);
+ /* Remember the initial bypass state */
+@@ -297,7 +329,10 @@ int regcache_sync(struct regmap *map)
+ }
+ map->cache_bypass = 0;
+
+- ret = map->cache_ops->sync(map, 0, map->max_register);
++ if (map->cache_ops->sync)
++ ret = map->cache_ops->sync(map, 0, map->max_register);
++ else
++ ret = regcache_default_sync(map, 0, map->max_register);
+
+ if (ret == 0)
+ map->cache_dirty = false;
+@@ -331,7 +366,7 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
+ const char *name;
+ unsigned int bypass;
+
+- BUG_ON(!map->cache_ops || !map->cache_ops->sync);
++ BUG_ON(!map->cache_ops);
+
+ map->lock(map->lock_arg);
+
+@@ -346,7 +381,10 @@ int regcache_sync_region(struct regmap *map, unsigned int min,
+ if (!map->cache_dirty)
+ goto out;
+
+- ret = map->cache_ops->sync(map, min, max);
++ if (map->cache_ops->sync)
++ ret = map->cache_ops->sync(map, min, max);
++ else
++ ret = regcache_default_sync(map, min, max);
+
+ out:
+ trace_regcache_sync(map->dev, name, "stop region");
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0013-regmap-cache-Don-t-attempt-to-sync-non-writeable-reg.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0013-regmap-cache-Don-t-attempt-to-sync-non-writeable-reg.patch
new file mode 100644
index 0000000..c97928d
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0013-regmap-cache-Don-t-attempt-to-sync-non-writeable-reg.patch
@@ -0,0 +1,34 @@
+From 20f963cde94f375c8f587b3866e6ef3ca6e3218c Mon Sep 17 00:00:00 2001
+From: Dylan Reid <dgreid@chromium.org>
+Date: Tue, 18 Mar 2014 13:45:09 -0700
+Subject: [PATCH 13/16] regmap: cache: Don't attempt to sync non-writeable
+ registers
+
+In the regcache_default_sync, if a register isn't writeable, then
+_regmap_write will return an error and the rest of the sync will be
+aborted. Avoid this by checking if a register is writeable before
+trying to sync it.
+
+Signed-off-by: Dylan Reid <dgreid@chromium.org>
+Signed-off-by: Mark Brown <broonie@linaro.org>
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/base/regmap/regcache.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
+index ffd8db6..3ec0bfd 100644
+--- a/drivers/base/regmap/regcache.c
++++ b/drivers/base/regmap/regcache.c
+@@ -259,7 +259,8 @@ static int regcache_default_sync(struct regmap *map, unsigned int min,
+ unsigned int val;
+ int ret;
+
+- if (regmap_volatile(map, reg))
++ if (regmap_volatile(map, reg) ||
++ !regmap_writeable(map, reg))
+ continue;
+
+ ret = regcache_read(map, reg, &val);
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0014-ASoC-Add-SOC_DOUBLE_STS-macro.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0014-ASoC-Add-SOC_DOUBLE_STS-macro.patch
new file mode 100644
index 0000000..58d8d28
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0014-ASoC-Add-SOC_DOUBLE_STS-macro.patch
@@ -0,0 +1,35 @@
+From 563ca49681d5393ed295310a7835660642f59677 Mon Sep 17 00:00:00 2001
+From: "Damien.Horsley" <Damien.Horsley@imgtec.com>
+Date: Tue, 8 Dec 2015 15:58:58 +0000
+Subject: [PATCH 14/16] ASoC: Add SOC_DOUBLE_STS macro
+
+Add SOC_DOUBLE_STS macro for read-only volatile status controls
+
+Signed-off-by: Damien.Horsley <Damien.Horsley@imgtec.com>
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ include/sound/soc.h | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/include/sound/soc.h b/include/sound/soc.h
+index 645bea2..936bddb 100644
+--- a/include/sound/soc.h
++++ b/include/sound/soc.h
+@@ -99,6 +99,14 @@
+ .put = snd_soc_put_volsw, \
+ .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \
+ max, invert) }
++#define SOC_DOUBLE_STS(xname, reg, shift_left, shift_right, max, invert) \
++{ \
++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
++ .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
++ .access = SNDRV_CTL_ELEM_ACCESS_READ | \
++ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
++ .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \
++ max, invert) }
+ #define SOC_DOUBLE_R(xname, reg_left, reg_right, xshift, xmax, xinvert) \
+ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = snd_soc_info_volsw, \
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0015-ASoC-pcm3168a-Add-binding-document-for-pcm3168a-code.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0015-ASoC-pcm3168a-Add-binding-document-for-pcm3168a-code.patch
new file mode 100644
index 0000000..f4b8e53
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0015-ASoC-pcm3168a-Add-binding-document-for-pcm3168a-code.patch
@@ -0,0 +1,72 @@
+From c4b652e226dbd995041addeb934f2a65c82b5bef Mon Sep 17 00:00:00 2001
+From: "Damien.Horsley" <Damien.Horsley@imgtec.com>
+Date: Tue, 8 Dec 2015 15:58:59 +0000
+Subject: [PATCH 15/16] ASoC: pcm3168a: Add binding document for pcm3168a
+ codec
+
+Add binding document for Texas Instruments pcm3168a codec
+
+Signed-off-by: Damien.Horsley <Damien.Horsley@imgtec.com>
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ .../devicetree/bindings/sound/ti,pcm3168a.txt | 48 ++++++++++++++++++++
+ 1 file changed, 48 insertions(+)
+ create mode 100644 Documentation/devicetree/bindings/sound/ti,pcm3168a.txt
+
+diff --git a/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt b/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt
+new file mode 100644
+index 0000000..5d9cb84
+--- /dev/null
++++ b/Documentation/devicetree/bindings/sound/ti,pcm3168a.txt
+@@ -0,0 +1,48 @@
++Texas Instruments pcm3168a DT bindings
++
++This driver supports both SPI and I2C bus access for this codec
++
++Required properties:
++
++ - compatible: "ti,pcm3168a"
++
++ - clocks : Contains an entry for each entry in clock-names
++
++ - clock-names : Includes the following entries:
++ "scki" The system clock
++
++ - VDD1-supply : Digital power supply regulator 1 (+3.3V)
++
++ - VDD2-supply : Digital power supply regulator 2 (+3.3V)
++
++ - VCCAD1-supply : ADC power supply regulator 1 (+5V)
++
++ - VCCAD2-supply : ADC power supply regulator 2 (+5V)
++
++ - VCCDA1-supply : DAC power supply regulator 1 (+5V)
++
++ - VCCDA2-supply : DAC power supply regulator 2 (+5V)
++
++For required properties on SPI/I2C, consult SPI/I2C device tree documentation
++
++Examples:
++
++i2c0: i2c0@0 {
++
++ ...
++
++ pcm3168a: audio-codec@44 {
++ compatible = "ti,pcm3168a";
++ reg = <0x44>;
++ clocks = <&clk_core CLK_AUDIO>;
++ clock-names = "scki";
++ VDD1-supply = <&supply3v3>;
++ VDD2-supply = <&supply3v3>;
++ VCCAD1-supply = <&supply5v0>;
++ VCCAD2-supply = <&supply5v0>;
++ VCCDA1-supply = <&supply5v0>;
++ VCCDA2-supply = <&supply5v0>;
++ pinctrl-names = "default";
++ pinctrl-0 = <&dac_clk_pin>;
++ };
++};
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0016-ASoC-pcm3168a-Add-driver-for-pcm3168a-codec.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0016-ASoC-pcm3168a-Add-driver-for-pcm3168a-codec.patch
new file mode 100644
index 0000000..24582bf
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0016-ASoC-pcm3168a-Add-driver-for-pcm3168a-codec.patch
@@ -0,0 +1,1108 @@
+From 3fa260164b3f5b8f3faa6ada700a3e7addd88c87 Mon Sep 17 00:00:00 2001
+From: "Damien.Horsley" <Damien.Horsley@imgtec.com>
+Date: Tue, 8 Dec 2015 15:59:00 +0000
+Subject: [PATCH 16/16] ASoC: pcm3168a: Add driver for pcm3168a codec
+
+Add driver for Texas Instruments pcm3168a codec
+
+Signed-off-by: Damien.Horsley <Damien.Horsley@imgtec.com>
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ sound/soc/codecs/Kconfig | 19 +
+ sound/soc/codecs/Makefile | 6 +
+ sound/soc/codecs/pcm3168a-i2c.c | 66 ++++
+ sound/soc/codecs/pcm3168a-spi.c | 65 ++++
+ sound/soc/codecs/pcm3168a.c | 767 +++++++++++++++++++++++++++++++++++++++
+ sound/soc/codecs/pcm3168a.h | 100 +++++
+ 6 files changed, 1023 insertions(+)
+ create mode 100644 sound/soc/codecs/pcm3168a-i2c.c
+ create mode 100644 sound/soc/codecs/pcm3168a-spi.c
+ create mode 100644 sound/soc/codecs/pcm3168a.c
+ create mode 100644 sound/soc/codecs/pcm3168a.h
+
+diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
+index 2f45f00..4f0c01a 100644
+--- a/sound/soc/codecs/Kconfig
++++ b/sound/soc/codecs/Kconfig
+@@ -55,6 +55,8 @@ config SND_SOC_ALL_CODECS
+ select SND_SOC_ML26124 if I2C
+ select SND_SOC_OMAP_HDMI_CODEC if OMAP4_DSS_HDMI
+ select SND_SOC_PCM3008
++ select SND_SOC_PCM3168A_I2C if I2C
++ select SND_SOC_PCM3168A_SPI if SPI_MASTER
+ select SND_SOC_RT5631 if I2C
+ select SND_SOC_SGTL5000 if I2C
+ select SND_SOC_SI476X if MFD_SI476X_CORE
+@@ -293,6 +295,23 @@ config SND_SOC_OMAP_HDMI_CODEC
+ config SND_SOC_PCM3008
+ tristate
+
++config SND_SOC_PCM3168A
++ tristate
++
++config SND_SOC_PCM3168A_I2C
++ tristate "Texas Instruments PCM3168A CODEC - I2C"
++ depends on I2C
++ select SND_SOC_PCM3168A
++ select REGMAP_I2C
++
++config SND_SOC_PCM3168A_SPI
++ tristate "Texas Instruments PCM3168A CODEC - SPI"
++ depends on SPI_MASTER
++ select SND_SOC_PCM3168A
++ select REGMAP_SPI
++
++ config SND_SOC_PCM512x
++ tristate
+ config SND_SOC_RT5631
+ tristate
+
+diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
+index b9e41c9..513d895 100644
+--- a/sound/soc/codecs/Makefile
++++ b/sound/soc/codecs/Makefile
+@@ -43,6 +43,9 @@ snd-soc-mc13783-objs := mc13783.o
+ snd-soc-ml26124-objs := ml26124.o
+ snd-soc-omap-hdmi-codec-objs := omap-hdmi.o
+ snd-soc-pcm3008-objs := pcm3008.o
++snd-soc-pcm3168a-objs := pcm3168a.o
++snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
++snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o
+ snd-soc-rt5631-objs := rt5631.o
+ snd-soc-sgtl5000-objs := sgtl5000.o
+ snd-soc-alc5623-objs := alc5623.o
+@@ -170,6 +173,9 @@ obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
+ obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
+ obj-$(CONFIG_SND_SOC_OMAP_HDMI_CODEC) += snd-soc-omap-hdmi-codec.o
+ obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o
++obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
++obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o
++obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o
+ obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
+ obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
+ obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
+diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c
+new file mode 100644
+index 0000000..6feb090
+--- /dev/null
++++ b/sound/soc/codecs/pcm3168a-i2c.c
+@@ -0,0 +1,66 @@
++/*
++ * PCM3168A codec i2c driver
++ *
++ * Copyright (C) 2015 Imagination Technologies Ltd.
++ *
++ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms and conditions of the GNU General Public License,
++ * version 2, as published by the Free Software Foundation.
++ */
++
++#include <linux/i2c.h>
++#include <linux/init.h>
++#include <linux/module.h>
++
++#include <sound/soc.h>
++
++#include "pcm3168a.h"
++
++static int pcm3168a_i2c_probe(struct i2c_client *i2c,
++ const struct i2c_device_id *id)
++{
++ struct regmap *regmap;
++
++ regmap = devm_regmap_init_i2c(i2c, &pcm3168a_regmap);
++ if (IS_ERR(regmap))
++ return PTR_ERR(regmap);
++
++ return pcm3168a_probe(&i2c->dev, regmap);
++}
++
++static int pcm3168a_i2c_remove(struct i2c_client *i2c)
++{
++ pcm3168a_remove(&i2c->dev);
++
++ return 0;
++}
++
++static const struct i2c_device_id pcm3168a_i2c_id[] = {
++ { "pcm3168a", },
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id);
++
++static const struct of_device_id pcm3168a_of_match[] = {
++ { .compatible = "ti,pcm3168a", },
++ { }
++};
++MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
++
++static struct i2c_driver pcm3168a_i2c_driver = {
++ .probe = pcm3168a_i2c_probe,
++ .remove = pcm3168a_i2c_remove,
++ .id_table = pcm3168a_i2c_id,
++ .driver = {
++ .name = "pcm3168a",
++ .of_match_table = pcm3168a_of_match,
++ .pm = &pcm3168a_pm_ops,
++ },
++};
++module_i2c_driver(pcm3168a_i2c_driver);
++
++MODULE_DESCRIPTION("PCM3168A I2C codec driver");
++MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
++MODULE_LICENSE("GPL v2");
+diff --git a/sound/soc/codecs/pcm3168a-spi.c b/sound/soc/codecs/pcm3168a-spi.c
+new file mode 100644
+index 0000000..03945a2
+--- /dev/null
++++ b/sound/soc/codecs/pcm3168a-spi.c
+@@ -0,0 +1,65 @@
++/*
++ * PCM3168A codec spi driver
++ *
++ * Copyright (C) 2015 Imagination Technologies Ltd.
++ *
++ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms and conditions of the GNU General Public License,
++ * version 2, as published by the Free Software Foundation.
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/spi/spi.h>
++
++#include <sound/soc.h>
++
++#include "pcm3168a.h"
++
++static int pcm3168a_spi_probe(struct spi_device *spi)
++{
++ struct regmap *regmap;
++
++ regmap = devm_regmap_init_spi(spi, &pcm3168a_regmap);
++ if (IS_ERR(regmap))
++ return PTR_ERR(regmap);
++
++ return pcm3168a_probe(&spi->dev, regmap);
++}
++
++static int pcm3168a_spi_remove(struct spi_device *spi)
++{
++ pcm3168a_remove(&spi->dev);
++
++ return 0;
++}
++
++static const struct spi_device_id pcm3168a_spi_id[] = {
++ { "pcm3168a", },
++ { },
++};
++MODULE_DEVICE_TABLE(spi, pcm3168a_spi_id);
++
++static const struct of_device_id pcm3168a_of_match[] = {
++ { .compatible = "ti,pcm3168a", },
++ { }
++};
++MODULE_DEVICE_TABLE(of, pcm3168a_of_match);
++
++static struct spi_driver pcm3168a_spi_driver = {
++ .probe = pcm3168a_spi_probe,
++ .remove = pcm3168a_spi_remove,
++ .id_table = pcm3168a_spi_id,
++ .driver = {
++ .name = "pcm3168a",
++ .of_match_table = pcm3168a_of_match,
++ .pm = &pcm3168a_pm_ops,
++ },
++};
++module_spi_driver(pcm3168a_spi_driver);
++
++MODULE_DESCRIPTION("PCM3168A SPI codec driver");
++MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
++MODULE_LICENSE("GPL v2");
+diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
+new file mode 100644
+index 0000000..0ee7ae8
+--- /dev/null
++++ b/sound/soc/codecs/pcm3168a.c
+@@ -0,0 +1,767 @@
++/*
++ * PCM3168A codec driver
++ *
++ * Copyright (C) 2015 Imagination Technologies Ltd.
++ *
++ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms and conditions of the GNU General Public License,
++ * version 2, as published by the Free Software Foundation.
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/module.h>
++#include <linux/pm_runtime.h>
++#include <linux/regulator/consumer.h>
++
++#include <sound/pcm_params.h>
++#include <sound/soc.h>
++#include <sound/tlv.h>
++
++#include "pcm3168a.h"
++
++#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
++ SNDRV_PCM_FMTBIT_S24_3LE | \
++ SNDRV_PCM_FMTBIT_S24_LE | \
++ SNDRV_PCM_FMTBIT_S32_LE)
++
++#define PCM3168A_FMT_I2S 0x0
++#define PCM3168A_FMT_LEFT_J 0x1
++#define PCM3168A_FMT_RIGHT_J 0x2
++#define PCM3168A_FMT_RIGHT_J_16 0x3
++#define PCM3168A_FMT_DSP_A 0x4
++#define PCM3168A_FMT_DSP_B 0x5
++#define PCM3168A_FMT_DSP_MASK 0x4
++
++#define PCM3168A_NUM_SUPPLIES 6
++static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
++ "VDD1",
++ "VDD2",
++ "VCCAD1",
++ "VCCAD2",
++ "VCCDA1",
++ "VCCDA2"
++};
++
++struct pcm3168a_priv {
++ struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
++ struct regmap *regmap;
++ struct clk *scki;
++ bool adc_master_mode;
++ bool dac_master_mode;
++ unsigned long sysclk;
++ unsigned int adc_fmt;
++ unsigned int dac_fmt;
++};
++
++static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" };
++
++static SOC_ENUM_SINGLE_DECL(pcm3168a_d1_roll_off, PCM3168A_DAC_OP_FLT,
++ PCM3168A_DAC_FLT_SHIFT, pcm3168a_roll_off);
++static SOC_ENUM_SINGLE_DECL(pcm3168a_d2_roll_off, PCM3168A_DAC_OP_FLT,
++ PCM3168A_DAC_FLT_SHIFT + 1, pcm3168a_roll_off);
++static SOC_ENUM_SINGLE_DECL(pcm3168a_d3_roll_off, PCM3168A_DAC_OP_FLT,
++ PCM3168A_DAC_FLT_SHIFT + 2, pcm3168a_roll_off);
++static SOC_ENUM_SINGLE_DECL(pcm3168a_d4_roll_off, PCM3168A_DAC_OP_FLT,
++ PCM3168A_DAC_FLT_SHIFT + 3, pcm3168a_roll_off);
++
++static const char *const pcm3168a_volume_type[] = {
++ "Individual", "Master + Individual" };
++
++static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_volume_type, PCM3168A_DAC_ATT_DEMP_ZF,
++ PCM3168A_DAC_ATMDDA_SHIFT, pcm3168a_volume_type);
++
++static const char *const pcm3168a_att_speed_mult[] = { "2048", "4096" };
++
++static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_att_mult, PCM3168A_DAC_ATT_DEMP_ZF,
++ PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_att_speed_mult);
++
++static const char *const pcm3168a_demp[] = {
++ "Disabled", "48khz", "44.1khz", "32khz" };
++
++static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_demp, PCM3168A_DAC_ATT_DEMP_ZF,
++ PCM3168A_DAC_DEMP_SHIFT, pcm3168a_demp);
++
++static const char *const pcm3168a_zf_func[] = {
++ "DAC 1/2/3/4 AND", "DAC 1/2/3/4 OR", "DAC 1/2/3 AND",
++ "DAC 1/2/3 OR", "DAC 4 AND", "DAC 4 OR" };
++
++static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_func, PCM3168A_DAC_ATT_DEMP_ZF,
++ PCM3168A_DAC_AZRO_SHIFT, pcm3168a_zf_func);
++
++static const char *const pcm3168a_pol[] = { "Active High", "Active Low" };
++
++static SOC_ENUM_SINGLE_DECL(pcm3168a_dac_zf_pol, PCM3168A_DAC_ATT_DEMP_ZF,
++ PCM3168A_DAC_ATSPDA_SHIFT, pcm3168a_pol);
++
++static const char *const pcm3168a_con[] = { "Differential", "Single-Ended" };
++
++static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc1_con, PCM3168A_ADC_SEAD,
++ 0, 1, pcm3168a_con);
++static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc2_con, PCM3168A_ADC_SEAD,
++ 2, 3, pcm3168a_con);
++static SOC_ENUM_DOUBLE_DECL(pcm3168a_adc3_con, PCM3168A_ADC_SEAD,
++ 4, 5, pcm3168a_con);
++
++static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_volume_type, PCM3168A_ADC_ATT_OVF,
++ PCM3168A_ADC_ATMDAD_SHIFT, pcm3168a_volume_type);
++
++static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_att_mult, PCM3168A_ADC_ATT_OVF,
++ PCM3168A_ADC_ATSPAD_SHIFT, pcm3168a_att_speed_mult);
++
++static SOC_ENUM_SINGLE_DECL(pcm3168a_adc_ov_pol, PCM3168A_ADC_ATT_OVF,
++ PCM3168A_ADC_OVFP_SHIFT, pcm3168a_pol);
++
++/* -100db to 0db, register values 0-54 cause mute */
++static const DECLARE_TLV_DB_SCALE(pcm3168a_dac_tlv, -10050, 50, 1);
++
++/* -100db to 20db, register values 0-14 cause mute */
++static const DECLARE_TLV_DB_SCALE(pcm3168a_adc_tlv, -10050, 50, 1);
++
++static const struct snd_kcontrol_new pcm3168a_snd_controls[] = {
++ SOC_SINGLE("DAC Power-Save Switch", PCM3168A_DAC_PWR_MST_FMT,
++ PCM3168A_DAC_PSMDA_SHIFT, 1, 1),
++ SOC_ENUM("DAC1 Digital Filter roll-off", pcm3168a_d1_roll_off),
++ SOC_ENUM("DAC2 Digital Filter roll-off", pcm3168a_d2_roll_off),
++ SOC_ENUM("DAC3 Digital Filter roll-off", pcm3168a_d3_roll_off),
++ SOC_ENUM("DAC4 Digital Filter roll-off", pcm3168a_d4_roll_off),
++ SOC_DOUBLE("DAC1 Invert Switch", PCM3168A_DAC_INV, 0, 1, 1, 0),
++ SOC_DOUBLE("DAC2 Invert Switch", PCM3168A_DAC_INV, 2, 3, 1, 0),
++ SOC_DOUBLE("DAC3 Invert Switch", PCM3168A_DAC_INV, 4, 5, 1, 0),
++ SOC_DOUBLE("DAC4 Invert Switch", PCM3168A_DAC_INV, 6, 7, 1, 0),
++ SOC_DOUBLE_STS("DAC1 Zero Flag", PCM3168A_DAC_ZERO, 0, 1, 1, 0),
++ SOC_DOUBLE_STS("DAC2 Zero Flag", PCM3168A_DAC_ZERO, 2, 3, 1, 0),
++ SOC_DOUBLE_STS("DAC3 Zero Flag", PCM3168A_DAC_ZERO, 4, 5, 1, 0),
++ SOC_DOUBLE_STS("DAC4 Zero Flag", PCM3168A_DAC_ZERO, 6, 7, 1, 0),
++ SOC_ENUM("DAC Volume Control Type", pcm3168a_dac_volume_type),
++ SOC_ENUM("DAC Volume Rate Multiplier", pcm3168a_dac_att_mult),
++ SOC_ENUM("DAC De-Emphasis", pcm3168a_dac_demp),
++ SOC_ENUM("DAC Zero Flag Function", pcm3168a_dac_zf_func),
++ SOC_ENUM("DAC Zero Flag Polarity", pcm3168a_dac_zf_pol),
++ SOC_SINGLE_RANGE_TLV("Master Playback Volume",
++ PCM3168A_DAC_VOL_MASTER, 0, 54, 255, 0,
++ pcm3168a_dac_tlv),
++ SOC_DOUBLE_R_RANGE_TLV("DAC1 Playback Volume",
++ PCM3168A_DAC_VOL_CHAN_START,
++ PCM3168A_DAC_VOL_CHAN_START + 1,
++ 0, 54, 255, 0, pcm3168a_dac_tlv),
++ SOC_DOUBLE_R_RANGE_TLV("DAC2 Playback Volume",
++ PCM3168A_DAC_VOL_CHAN_START + 2,
++ PCM3168A_DAC_VOL_CHAN_START + 3,
++ 0, 54, 255, 0, pcm3168a_dac_tlv),
++ SOC_DOUBLE_R_RANGE_TLV("DAC3 Playback Volume",
++ PCM3168A_DAC_VOL_CHAN_START + 4,
++ PCM3168A_DAC_VOL_CHAN_START + 5,
++ 0, 54, 255, 0, pcm3168a_dac_tlv),
++ SOC_DOUBLE_R_RANGE_TLV("DAC4 Playback Volume",
++ PCM3168A_DAC_VOL_CHAN_START + 6,
++ PCM3168A_DAC_VOL_CHAN_START + 7,
++ 0, 54, 255, 0, pcm3168a_dac_tlv),
++ SOC_SINGLE("ADC1 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
++ PCM3168A_ADC_BYP_SHIFT, 1, 1),
++ SOC_SINGLE("ADC2 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
++ PCM3168A_ADC_BYP_SHIFT + 1, 1, 1),
++ SOC_SINGLE("ADC3 High-Pass Filter Switch", PCM3168A_ADC_PWR_HPFB,
++ PCM3168A_ADC_BYP_SHIFT + 2, 1, 1),
++ SOC_ENUM("ADC1 Connection Type", pcm3168a_adc1_con),
++ SOC_ENUM("ADC2 Connection Type", pcm3168a_adc2_con),
++ SOC_ENUM("ADC3 Connection Type", pcm3168a_adc3_con),
++ SOC_DOUBLE("ADC1 Invert Switch", PCM3168A_ADC_INV, 0, 1, 1, 0),
++ SOC_DOUBLE("ADC2 Invert Switch", PCM3168A_ADC_INV, 2, 3, 1, 0),
++ SOC_DOUBLE("ADC3 Invert Switch", PCM3168A_ADC_INV, 4, 5, 1, 0),
++ SOC_DOUBLE("ADC1 Mute Switch", PCM3168A_ADC_MUTE, 0, 1, 1, 0),
++ SOC_DOUBLE("ADC2 Mute Switch", PCM3168A_ADC_MUTE, 2, 3, 1, 0),
++ SOC_DOUBLE("ADC3 Mute Switch", PCM3168A_ADC_MUTE, 4, 5, 1, 0),
++ SOC_DOUBLE_STS("ADC1 Overflow Flag", PCM3168A_ADC_OV, 0, 1, 1, 0),
++ SOC_DOUBLE_STS("ADC2 Overflow Flag", PCM3168A_ADC_OV, 2, 3, 1, 0),
++ SOC_DOUBLE_STS("ADC3 Overflow Flag", PCM3168A_ADC_OV, 4, 5, 1, 0),
++ SOC_ENUM("ADC Volume Control Type", pcm3168a_adc_volume_type),
++ SOC_ENUM("ADC Volume Rate Multiplier", pcm3168a_adc_att_mult),
++ SOC_ENUM("ADC Overflow Flag Polarity", pcm3168a_adc_ov_pol),
++ SOC_SINGLE_RANGE_TLV("Master Capture Volume",
++ PCM3168A_ADC_VOL_MASTER, 0, 14, 255, 0,
++ pcm3168a_adc_tlv),
++ SOC_DOUBLE_R_RANGE_TLV("ADC1 Capture Volume",
++ PCM3168A_ADC_VOL_CHAN_START,
++ PCM3168A_ADC_VOL_CHAN_START + 1,
++ 0, 14, 255, 0, pcm3168a_adc_tlv),
++ SOC_DOUBLE_R_RANGE_TLV("ADC2 Capture Volume",
++ PCM3168A_ADC_VOL_CHAN_START + 2,
++ PCM3168A_ADC_VOL_CHAN_START + 3,
++ 0, 14, 255, 0, pcm3168a_adc_tlv),
++ SOC_DOUBLE_R_RANGE_TLV("ADC3 Capture Volume",
++ PCM3168A_ADC_VOL_CHAN_START + 4,
++ PCM3168A_ADC_VOL_CHAN_START + 5,
++ 0, 14, 255, 0, pcm3168a_adc_tlv)
++};
++
++static const struct snd_soc_dapm_widget pcm3168a_dapm_widgets[] = {
++ SND_SOC_DAPM_DAC("DAC1", "Playback", PCM3168A_DAC_OP_FLT,
++ PCM3168A_DAC_OPEDA_SHIFT, 1),
++ SND_SOC_DAPM_DAC("DAC2", "Playback", PCM3168A_DAC_OP_FLT,
++ PCM3168A_DAC_OPEDA_SHIFT + 1, 1),
++ SND_SOC_DAPM_DAC("DAC3", "Playback", PCM3168A_DAC_OP_FLT,
++ PCM3168A_DAC_OPEDA_SHIFT + 2, 1),
++ SND_SOC_DAPM_DAC("DAC4", "Playback", PCM3168A_DAC_OP_FLT,
++ PCM3168A_DAC_OPEDA_SHIFT + 3, 1),
++
++ SND_SOC_DAPM_OUTPUT("AOUT1L"),
++ SND_SOC_DAPM_OUTPUT("AOUT1R"),
++ SND_SOC_DAPM_OUTPUT("AOUT2L"),
++ SND_SOC_DAPM_OUTPUT("AOUT2R"),
++ SND_SOC_DAPM_OUTPUT("AOUT3L"),
++ SND_SOC_DAPM_OUTPUT("AOUT3R"),
++ SND_SOC_DAPM_OUTPUT("AOUT4L"),
++ SND_SOC_DAPM_OUTPUT("AOUT4R"),
++
++ SND_SOC_DAPM_ADC("ADC1", "Capture", PCM3168A_ADC_PWR_HPFB,
++ PCM3168A_ADC_PSVAD_SHIFT, 1),
++ SND_SOC_DAPM_ADC("ADC2", "Capture", PCM3168A_ADC_PWR_HPFB,
++ PCM3168A_ADC_PSVAD_SHIFT + 1, 1),
++ SND_SOC_DAPM_ADC("ADC3", "Capture", PCM3168A_ADC_PWR_HPFB,
++ PCM3168A_ADC_PSVAD_SHIFT + 2, 1),
++
++ SND_SOC_DAPM_INPUT("AIN1L"),
++ SND_SOC_DAPM_INPUT("AIN1R"),
++ SND_SOC_DAPM_INPUT("AIN2L"),
++ SND_SOC_DAPM_INPUT("AIN2R"),
++ SND_SOC_DAPM_INPUT("AIN3L"),
++ SND_SOC_DAPM_INPUT("AIN3R")
++};
++
++static const struct snd_soc_dapm_route pcm3168a_dapm_routes[] = {
++ /* Playback */
++ { "AOUT1L", NULL, "DAC1" },
++ { "AOUT1R", NULL, "DAC1" },
++
++ { "AOUT2L", NULL, "DAC2" },
++ { "AOUT2R", NULL, "DAC2" },
++
++ { "AOUT3L", NULL, "DAC3" },
++ { "AOUT3R", NULL, "DAC3" },
++
++ { "AOUT4L", NULL, "DAC4" },
++ { "AOUT4R", NULL, "DAC4" },
++
++ /* Capture */
++ { "ADC1", NULL, "AIN1L" },
++ { "ADC1", NULL, "AIN1R" },
++
++ { "ADC2", NULL, "AIN2L" },
++ { "ADC2", NULL, "AIN2R" },
++
++ { "ADC3", NULL, "AIN3L" },
++ { "ADC3", NULL, "AIN3R" }
++};
++
++static unsigned int pcm3168a_scki_ratios[] = {
++ 768,
++ 512,
++ 384,
++ 256,
++ 192,
++ 128
++};
++
++#define PCM3168A_NUM_SCKI_RATIOS_DAC ARRAY_SIZE(pcm3168a_scki_ratios)
++#define PCM3168A_NUM_SCKI_RATIOS_ADC (ARRAY_SIZE(pcm3168a_scki_ratios) - 2)
++
++#define PCM1368A_MAX_SYSCLK 36864000
++
++static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a)
++{
++ int ret;
++
++ ret = regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE, 0);
++ if (ret)
++ return ret;
++
++ /* Internal reset is de-asserted after 3846 SCKI cycles */
++ msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk));
++
++ return regmap_write(pcm3168a->regmap, PCM3168A_RST_SMODE,
++ PCM3168A_MRST_MASK | PCM3168A_SRST_MASK);
++}
++
++static int pcm3168a_digital_mute(struct snd_soc_dai *dai, int mute)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
++
++ regmap_write(pcm3168a->regmap, PCM3168A_DAC_MUTE, mute ? 0xff : 0);
++
++ return 0;
++}
++
++static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
++ int clk_id, unsigned int freq, int dir)
++{
++ struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(dai->codec);
++
++ if (freq > PCM1368A_MAX_SYSCLK)
++ return -EINVAL;
++
++ pcm3168a->sysclk = freq;
++
++ return 0;
++}
++
++static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
++ unsigned int format, bool dac)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
++ u32 fmt, reg, mask, shift;
++ bool master_mode;
++
++ switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
++ case SND_SOC_DAIFMT_LEFT_J:
++ fmt = PCM3168A_FMT_LEFT_J;
++ break;
++ case SND_SOC_DAIFMT_I2S:
++ fmt = PCM3168A_FMT_I2S;
++ break;
++ case SND_SOC_DAIFMT_RIGHT_J:
++ fmt = PCM3168A_FMT_RIGHT_J;
++ break;
++ case SND_SOC_DAIFMT_DSP_A:
++ fmt = PCM3168A_FMT_DSP_A;
++ break;
++ case SND_SOC_DAIFMT_DSP_B:
++ fmt = PCM3168A_FMT_DSP_B;
++ break;
++ default:
++ dev_err(codec->dev, "unsupported dai format\n");
++ return -EINVAL;
++ }
++
++ switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
++ case SND_SOC_DAIFMT_CBS_CFS:
++ master_mode = false;
++ break;
++ case SND_SOC_DAIFMT_CBM_CFM:
++ master_mode = true;
++ break;
++ default:
++ dev_err(codec->dev, "unsupported master/slave mode\n");
++ return -EINVAL;
++ }
++
++ switch (format & SND_SOC_DAIFMT_INV_MASK) {
++ case SND_SOC_DAIFMT_NB_NF:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ if (dac) {
++ reg = PCM3168A_DAC_PWR_MST_FMT;
++ mask = PCM3168A_DAC_FMT_MASK;
++ shift = PCM3168A_DAC_FMT_SHIFT;
++ pcm3168a->dac_master_mode = master_mode;
++ pcm3168a->dac_fmt = fmt;
++ } else {
++ reg = PCM3168A_ADC_MST_FMT;
++ mask = PCM3168A_ADC_FMTAD_MASK;
++ shift = PCM3168A_ADC_FMTAD_SHIFT;
++ pcm3168a->adc_master_mode = master_mode;
++ pcm3168a->adc_fmt = fmt;
++ }
++
++ regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
++
++ return 0;
++}
++
++static int pcm3168a_set_dai_fmt_dac(struct snd_soc_dai *dai,
++ unsigned int format)
++{
++ return pcm3168a_set_dai_fmt(dai, format, true);
++}
++
++static int pcm3168a_set_dai_fmt_adc(struct snd_soc_dai *dai,
++ unsigned int format)
++{
++ return pcm3168a_set_dai_fmt(dai, format, false);
++}
++
++static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
++ struct snd_pcm_hw_params *params,
++ struct snd_soc_dai *dai)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
++ bool tx, master_mode;
++ u32 val, mask, shift, reg;
++ unsigned int rate, channels, fmt, ratio, max_ratio;
++ int i, min_frame_size;
++ snd_pcm_format_t format;
++
++ rate = params_rate(params);
++ format = params_format(params);
++ channels = params_channels(params);
++
++ ratio = pcm3168a->sysclk / rate;
++
++ tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
++ if (tx) {
++ max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC;
++ reg = PCM3168A_DAC_PWR_MST_FMT;
++ mask = PCM3168A_DAC_MSDA_MASK;
++ shift = PCM3168A_DAC_MSDA_SHIFT;
++ master_mode = pcm3168a->dac_master_mode;
++ fmt = pcm3168a->dac_fmt;
++ } else {
++ max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC;
++ reg = PCM3168A_ADC_MST_FMT;
++ mask = PCM3168A_ADC_MSAD_MASK;
++ shift = PCM3168A_ADC_MSAD_SHIFT;
++ master_mode = pcm3168a->adc_master_mode;
++ fmt = pcm3168a->adc_fmt;
++ }
++
++ for (i = 0; i < max_ratio; i++) {
++ if (pcm3168a_scki_ratios[i] == ratio)
++ break;
++ }
++
++ if (i == max_ratio) {
++ dev_err(codec->dev, "unsupported sysclk ratio\n");
++ return -EINVAL;
++ }
++
++ min_frame_size = snd_pcm_format_width(params_format(params)) * 2;
++ switch (min_frame_size) {
++ case 32:
++ if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) {
++ dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n");
++ return -EINVAL;
++ }
++ fmt = PCM3168A_FMT_RIGHT_J_16;
++ break;
++ case 48:
++ if (master_mode || (fmt & PCM3168A_FMT_DSP_MASK)) {
++ dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n");
++ return -EINVAL;
++ }
++ break;
++ case 64:
++ break;
++ default:
++ dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size);
++ return -EINVAL;
++ }
++
++ if (master_mode)
++ val = ((i + 1) << shift);
++ else
++ val = 0;
++
++ regmap_update_bits(pcm3168a->regmap, reg, mask, val);
++
++ if (tx) {
++ mask = PCM3168A_DAC_FMT_MASK;
++ shift = PCM3168A_DAC_FMT_SHIFT;
++ } else {
++ mask = PCM3168A_ADC_FMTAD_MASK;
++ shift = PCM3168A_ADC_FMTAD_SHIFT;
++ }
++
++ regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
++
++ return 0;
++}
++
++static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
++ .set_fmt = pcm3168a_set_dai_fmt_dac,
++ .set_sysclk = pcm3168a_set_dai_sysclk,
++ .hw_params = pcm3168a_hw_params,
++ .digital_mute = pcm3168a_digital_mute
++};
++
++static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = {
++ .set_fmt = pcm3168a_set_dai_fmt_adc,
++ .set_sysclk = pcm3168a_set_dai_sysclk,
++ .hw_params = pcm3168a_hw_params
++};
++
++static struct snd_soc_dai_driver pcm3168a_dais[] = {
++ {
++ .name = "pcm3168a-dac",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 8,
++ .rates = SNDRV_PCM_RATE_8000_192000,
++ .formats = PCM3168A_FORMATS
++ },
++ .ops = &pcm3168a_dac_dai_ops
++ },
++ {
++ .name = "pcm3168a-adc",
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 6,
++ .rates = SNDRV_PCM_RATE_8000_96000,
++ .formats = PCM3168A_FORMATS
++ },
++ .ops = &pcm3168a_adc_dai_ops
++ },
++};
++
++static const struct reg_default pcm3168a_reg_default[] = {
++ { PCM3168A_RST_SMODE, PCM3168A_MRST_MASK | PCM3168A_SRST_MASK },
++ { PCM3168A_DAC_PWR_MST_FMT, 0x00 },
++ { PCM3168A_DAC_OP_FLT, 0x00 },
++ { PCM3168A_DAC_INV, 0x00 },
++ { PCM3168A_DAC_MUTE, 0x00 },
++ { PCM3168A_DAC_ZERO, 0x00 },
++ { PCM3168A_DAC_ATT_DEMP_ZF, 0x00 },
++ { PCM3168A_DAC_VOL_MASTER, 0xff },
++ { PCM3168A_DAC_VOL_CHAN_START, 0xff },
++ { PCM3168A_DAC_VOL_CHAN_START + 1, 0xff },
++ { PCM3168A_DAC_VOL_CHAN_START + 2, 0xff },
++ { PCM3168A_DAC_VOL_CHAN_START + 3, 0xff },
++ { PCM3168A_DAC_VOL_CHAN_START + 4, 0xff },
++ { PCM3168A_DAC_VOL_CHAN_START + 5, 0xff },
++ { PCM3168A_DAC_VOL_CHAN_START + 6, 0xff },
++ { PCM3168A_DAC_VOL_CHAN_START + 7, 0xff },
++ { PCM3168A_ADC_SMODE, 0x00 },
++ { PCM3168A_ADC_MST_FMT, 0x00 },
++ { PCM3168A_ADC_PWR_HPFB, 0x00 },
++ { PCM3168A_ADC_SEAD, 0x00 },
++ { PCM3168A_ADC_INV, 0x00 },
++ { PCM3168A_ADC_MUTE, 0x00 },
++ { PCM3168A_ADC_OV, 0x00 },
++ { PCM3168A_ADC_ATT_OVF, 0x00 },
++ { PCM3168A_ADC_VOL_MASTER, 0xd3 },
++ { PCM3168A_ADC_VOL_CHAN_START, 0xd3 },
++ { PCM3168A_ADC_VOL_CHAN_START + 1, 0xd3 },
++ { PCM3168A_ADC_VOL_CHAN_START + 2, 0xd3 },
++ { PCM3168A_ADC_VOL_CHAN_START + 3, 0xd3 },
++ { PCM3168A_ADC_VOL_CHAN_START + 4, 0xd3 },
++ { PCM3168A_ADC_VOL_CHAN_START + 5, 0xd3 }
++};
++
++static bool pcm3168a_readable_register(struct device *dev, unsigned int reg)
++{
++ if (reg >= PCM3168A_RST_SMODE)
++ return true;
++ else
++ return false;
++}
++
++static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg)
++{
++ switch (reg) {
++ case PCM3168A_DAC_ZERO:
++ case PCM3168A_ADC_OV:
++ return true;
++ default:
++ return false;
++ }
++}
++
++static bool pcm3168a_writeable_register(struct device *dev, unsigned int reg)
++{
++ if (reg < PCM3168A_RST_SMODE)
++ return false;
++
++ switch (reg) {
++ case PCM3168A_DAC_ZERO:
++ case PCM3168A_ADC_OV:
++ return false;
++ default:
++ return true;
++ }
++}
++
++const struct regmap_config pcm3168a_regmap = {
++ .reg_bits = 8,
++ .val_bits = 8,
++
++ .max_register = PCM3168A_ADC_VOL_CHAN_START + 5,
++ .reg_defaults = pcm3168a_reg_default,
++ .num_reg_defaults = ARRAY_SIZE(pcm3168a_reg_default),
++ .readable_reg = pcm3168a_readable_register,
++ .volatile_reg = pcm3168a_volatile_register,
++ .writeable_reg = pcm3168a_writeable_register,
++ .cache_type = REGCACHE_FLAT
++};
++EXPORT_SYMBOL_GPL(pcm3168a_regmap);
++
++static const struct snd_soc_codec_driver pcm3168a_driver = {
++ .idle_bias_off = true,
++ .controls = pcm3168a_snd_controls,
++ .num_controls = ARRAY_SIZE(pcm3168a_snd_controls),
++ .dapm_widgets = pcm3168a_dapm_widgets,
++ .num_dapm_widgets = ARRAY_SIZE(pcm3168a_dapm_widgets),
++ .dapm_routes = pcm3168a_dapm_routes,
++ .num_dapm_routes = ARRAY_SIZE(pcm3168a_dapm_routes)
++};
++
++int pcm3168a_probe(struct device *dev, struct regmap *regmap)
++{
++ struct pcm3168a_priv *pcm3168a;
++ int ret, i;
++
++ pcm3168a = devm_kzalloc(dev, sizeof(*pcm3168a), GFP_KERNEL);
++ if (pcm3168a == NULL)
++ return -ENOMEM;
++
++ dev_set_drvdata(dev, pcm3168a);
++
++ pcm3168a->scki = devm_clk_get(dev, "scki");
++ if (IS_ERR(pcm3168a->scki)) {
++ ret = PTR_ERR(pcm3168a->scki);
++ if (ret != -EPROBE_DEFER)
++ dev_err(dev, "failed to acquire clock 'scki': %d\n", ret);
++ return ret;
++ }
++
++ ret = clk_prepare_enable(pcm3168a->scki);
++ if (ret) {
++ dev_err(dev, "Failed to enable mclk: %d\n", ret);
++ return ret;
++ }
++
++ pcm3168a->sysclk = clk_get_rate(pcm3168a->scki);
++
++ for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++)
++ pcm3168a->supplies[i].supply = pcm3168a_supply_names[i];
++
++ ret = devm_regulator_bulk_get(dev,
++ ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies);
++ if (ret) {
++ if (ret != -EPROBE_DEFER)
++ dev_err(dev, "failed to request supplies: %d\n", ret);
++ goto err_clk;
++ }
++
++ ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies),
++ pcm3168a->supplies);
++ if (ret) {
++ dev_err(dev, "failed to enable supplies: %d\n", ret);
++ goto err_clk;
++ }
++
++ pcm3168a->regmap = regmap;
++ if (IS_ERR(pcm3168a->regmap)) {
++ ret = PTR_ERR(pcm3168a->regmap);
++ dev_err(dev, "failed to allocate regmap: %d\n", ret);
++ goto err_regulator;
++ }
++
++ ret = pcm3168a_reset(pcm3168a);
++ if (ret) {
++ dev_err(dev, "Failed to reset device: %d\n", ret);
++ goto err_regulator;
++ }
++
++ pm_runtime_set_active(dev);
++ pm_runtime_enable(dev);
++ pm_runtime_idle(dev);
++
++ ret = snd_soc_register_codec(dev, &pcm3168a_driver, pcm3168a_dais,
++ ARRAY_SIZE(pcm3168a_dais));
++ if (ret) {
++ dev_err(dev, "failed to register codec: %d\n", ret);
++ goto err_regulator;
++ }
++
++ return 0;
++
++err_regulator:
++ regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
++ pcm3168a->supplies);
++err_clk:
++ clk_disable_unprepare(pcm3168a->scki);
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(pcm3168a_probe);
++
++void pcm3168a_remove(struct device *dev)
++{
++ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
++
++ snd_soc_unregister_codec(dev);
++ pm_runtime_disable(dev);
++ regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
++ pcm3168a->supplies);
++ clk_disable_unprepare(pcm3168a->scki);
++}
++EXPORT_SYMBOL_GPL(pcm3168a_remove);
++
++#ifdef CONFIG_PM
++static int pcm3168a_rt_resume(struct device *dev)
++{
++ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
++ int ret;
++
++ ret = clk_prepare_enable(pcm3168a->scki);
++ if (ret) {
++ dev_err(dev, "Failed to enable mclk: %d\n", ret);
++ return ret;
++ }
++
++ ret = regulator_bulk_enable(ARRAY_SIZE(pcm3168a->supplies),
++ pcm3168a->supplies);
++ if (ret) {
++ dev_err(dev, "Failed to enable supplies: %d\n", ret);
++ goto err_clk;
++ }
++
++ ret = pcm3168a_reset(pcm3168a);
++ if (ret) {
++ dev_err(dev, "Failed to reset device: %d\n", ret);
++ goto err_regulator;
++ }
++
++ regcache_cache_only(pcm3168a->regmap, false);
++
++ regcache_mark_dirty(pcm3168a->regmap);
++
++ ret = regcache_sync(pcm3168a->regmap);
++ if (ret) {
++ dev_err(dev, "Failed to sync regmap: %d\n", ret);
++ goto err_regulator;
++ }
++
++ return 0;
++
++err_regulator:
++ regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
++ pcm3168a->supplies);
++err_clk:
++ clk_disable_unprepare(pcm3168a->scki);
++
++ return ret;
++}
++
++static int pcm3168a_rt_suspend(struct device *dev)
++{
++ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
++
++ regcache_cache_only(pcm3168a->regmap, true);
++
++ regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
++ pcm3168a->supplies);
++
++ clk_disable_unprepare(pcm3168a->scki);
++
++ return 0;
++}
++#endif
++
++const struct dev_pm_ops pcm3168a_pm_ops = {
++ SET_RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL)
++};
++EXPORT_SYMBOL_GPL(pcm3168a_pm_ops);
++
++MODULE_DESCRIPTION("PCM3168A codec driver");
++MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
++MODULE_LICENSE("GPL v2");
+diff --git a/sound/soc/codecs/pcm3168a.h b/sound/soc/codecs/pcm3168a.h
+new file mode 100644
+index 0000000..56c8332
+--- /dev/null
++++ b/sound/soc/codecs/pcm3168a.h
+@@ -0,0 +1,100 @@
++/*
++ * PCM3168A codec driver header
++ *
++ * Copyright (C) 2015 Imagination Technologies Ltd.
++ *
++ * Author: Damien Horsley <Damien.Horsley@imgtec.com>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms and conditions of the GNU General Public License,
++ * version 2, as published by the Free Software Foundation.
++ */
++
++#ifndef __PCM3168A_H__
++#define __PCM3168A_H__
++
++extern const struct dev_pm_ops pcm3168a_pm_ops;
++extern const struct regmap_config pcm3168a_regmap;
++
++extern int pcm3168a_probe(struct device *dev, struct regmap *regmap);
++extern void pcm3168a_remove(struct device *dev);
++
++#define PCM3168A_RST_SMODE 0x40
++#define PCM3168A_MRST_MASK 0x80
++#define PCM3168A_SRST_MASK 0x40
++#define PCM3168A_DAC_SRDA_SHIFT 0
++#define PCM3168A_DAC_SRDA_MASK 0x3
++
++#define PCM3168A_DAC_PWR_MST_FMT 0x41
++#define PCM3168A_DAC_PSMDA_SHIFT 7
++#define PCM3168A_DAC_PSMDA_MASK 0x80
++#define PCM3168A_DAC_MSDA_SHIFT 4
++#define PCM3168A_DAC_MSDA_MASK 0x70
++#define PCM3168A_DAC_FMT_SHIFT 0
++#define PCM3168A_DAC_FMT_MASK 0xf
++
++#define PCM3168A_DAC_OP_FLT 0x42
++#define PCM3168A_DAC_OPEDA_SHIFT 4
++#define PCM3168A_DAC_OPEDA_MASK 0xf0
++#define PCM3168A_DAC_FLT_SHIFT 0
++#define PCM3168A_DAC_FLT_MASK 0xf
++
++#define PCM3168A_DAC_INV 0x43
++
++#define PCM3168A_DAC_MUTE 0x44
++
++#define PCM3168A_DAC_ZERO 0x45
++
++#define PCM3168A_DAC_ATT_DEMP_ZF 0x46
++#define PCM3168A_DAC_ATMDDA_MASK 0x80
++#define PCM3168A_DAC_ATMDDA_SHIFT 7
++#define PCM3168A_DAC_ATSPDA_MASK 0x40
++#define PCM3168A_DAC_ATSPDA_SHIFT 6
++#define PCM3168A_DAC_DEMP_SHIFT 4
++#define PCM3168A_DAC_DEMP_MASK 0x30
++#define PCM3168A_DAC_AZRO_SHIFT 1
++#define PCM3168A_DAC_AZRO_MASK 0xe
++#define PCM3168A_DAC_ZREV_MASK 0x1
++#define PCM3168A_DAC_ZREV_SHIFT 0
++
++#define PCM3168A_DAC_VOL_MASTER 0x47
++
++#define PCM3168A_DAC_VOL_CHAN_START 0x48
++
++#define PCM3168A_ADC_SMODE 0x50
++#define PCM3168A_ADC_SRAD_SHIFT 0
++#define PCM3168A_ADC_SRAD_MASK 0x3
++
++#define PCM3168A_ADC_MST_FMT 0x51
++#define PCM3168A_ADC_MSAD_SHIFT 4
++#define PCM3168A_ADC_MSAD_MASK 0x70
++#define PCM3168A_ADC_FMTAD_SHIFT 0
++#define PCM3168A_ADC_FMTAD_MASK 0x7
++
++#define PCM3168A_ADC_PWR_HPFB 0x52
++#define PCM3168A_ADC_PSVAD_SHIFT 4
++#define PCM3168A_ADC_PSVAD_MASK 0x70
++#define PCM3168A_ADC_BYP_SHIFT 0
++#define PCM3168A_ADC_BYP_MASK 0x7
++
++#define PCM3168A_ADC_SEAD 0x53
++
++#define PCM3168A_ADC_INV 0x54
++
++#define PCM3168A_ADC_MUTE 0x55
++
++#define PCM3168A_ADC_OV 0x56
++
++#define PCM3168A_ADC_ATT_OVF 0x57
++#define PCM3168A_ADC_ATMDAD_MASK 0x80
++#define PCM3168A_ADC_ATMDAD_SHIFT 7
++#define PCM3168A_ADC_ATSPAD_MASK 0x40
++#define PCM3168A_ADC_ATSPAD_SHIFT 6
++#define PCM3168A_ADC_OVFP_MASK 0x1
++#define PCM3168A_ADC_OVFP_SHIFT 0
++
++#define PCM3168A_ADC_VOL_MASTER 0x58
++
++#define PCM3168A_ADC_VOL_CHAN_START 0x59
++
++#endif
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0017-ASoC-PCM3168A-add-TDM-modes.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0017-ASoC-PCM3168A-add-TDM-modes.patch
new file mode 100644
index 0000000..ff91821
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0017-ASoC-PCM3168A-add-TDM-modes.patch
@@ -0,0 +1,298 @@
+From 7ca8137af6906da63479f5554d28e351218f6aff Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Wed, 13 Apr 2016 15:32:38 +0300
+Subject: [PATCH 17/21] ASoC: PCM3168A: add TDM modes
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ sound/soc/codecs/pcm3168a.c | 166 +++++++++++++++++++++++++++++++++----------
+ sound/soc/codecs/pcm3168a.h | 2 +-
+ 2 files changed, 131 insertions(+), 37 deletions(-)
+
+diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
+index 0ee7ae8..6287ad9 100644
+--- a/sound/soc/codecs/pcm3168a.c
++++ b/sound/soc/codecs/pcm3168a.c
+@@ -33,7 +33,11 @@
+ #define PCM3168A_FMT_RIGHT_J_16 0x3
+ #define PCM3168A_FMT_DSP_A 0x4
+ #define PCM3168A_FMT_DSP_B 0x5
+-#define PCM3168A_FMT_DSP_MASK 0x4
++#define PCM3168A_FMT_I2S_TDM 0x6
++#define PCM3168A_FMT_LEFT_J_TDM 0x7
++/* High speed */
++#define PCM3168A_FMT_I2S_TDMHS 0x8
++#define PCM3168A_FMT_LEFT_J_TDMHS 0x9
+
+ #define PCM3168A_NUM_SUPPLIES 6
+ static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
+@@ -45,12 +49,19 @@ static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
+ "VCCDA2"
+ };
+
++#define TDM_MODE_NONE 0
++#define TDM_MODE_NORM 1
++#define TDM_MODE_HS 2
++
+ struct pcm3168a_priv {
+ struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
+ struct regmap *regmap;
+ struct clk *scki;
+ bool adc_master_mode;
+ bool dac_master_mode;
++ unsigned int tdm;
++ unsigned int slots;
++ unsigned int slot_width;
+ unsigned long sysclk;
+ unsigned int adc_fmt;
+ unsigned int dac_fmt;
+@@ -308,32 +319,43 @@ static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
+ return 0;
+ }
+
++int format_table[3][6] = {
++ [TDM_MODE_NONE] = {
++ [0] = -1,
++ [SND_SOC_DAIFMT_I2S] = PCM3168A_FMT_I2S,
++ [SND_SOC_DAIFMT_LEFT_J] = PCM3168A_FMT_LEFT_J,
++ [SND_SOC_DAIFMT_RIGHT_J] = PCM3168A_FMT_RIGHT_J,
++ [SND_SOC_DAIFMT_DSP_A] = PCM3168A_FMT_DSP_A,
++ [SND_SOC_DAIFMT_DSP_B] = PCM3168A_FMT_DSP_B},
++ [TDM_MODE_NORM] = {
++ [0] = -1,
++ [SND_SOC_DAIFMT_I2S] = PCM3168A_FMT_I2S_TDM,
++ [SND_SOC_DAIFMT_LEFT_J] = PCM3168A_FMT_LEFT_J_TDM,
++ [SND_SOC_DAIFMT_RIGHT_J] = -1,
++ [SND_SOC_DAIFMT_DSP_A] = -1,
++ [SND_SOC_DAIFMT_DSP_B] = -1},
++ [TDM_MODE_HS] = {
++ [0] = -1,
++ [SND_SOC_DAIFMT_I2S] = PCM3168A_FMT_I2S_TDMHS,
++ [SND_SOC_DAIFMT_LEFT_J] = PCM3168A_FMT_LEFT_J_TDMHS,
++ [SND_SOC_DAIFMT_RIGHT_J] = -1,
++ [SND_SOC_DAIFMT_DSP_A] = -1,
++ [SND_SOC_DAIFMT_DSP_B] = -1},
++};
++
+ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int format, bool dac)
+ {
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
+- u32 fmt, reg, mask, shift;
++ u32 reg, mask, shift;
++ int fmt;
+ bool master_mode;
+
+- switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
+- case SND_SOC_DAIFMT_LEFT_J:
+- fmt = PCM3168A_FMT_LEFT_J;
+- break;
+- case SND_SOC_DAIFMT_I2S:
+- fmt = PCM3168A_FMT_I2S;
+- break;
+- case SND_SOC_DAIFMT_RIGHT_J:
+- fmt = PCM3168A_FMT_RIGHT_J;
+- break;
+- case SND_SOC_DAIFMT_DSP_A:
+- fmt = PCM3168A_FMT_DSP_A;
+- break;
+- case SND_SOC_DAIFMT_DSP_B:
+- fmt = PCM3168A_FMT_DSP_B;
+- break;
+- default:
+- dev_err(codec->dev, "unsupported dai format\n");
++ fmt = format_table[pcm3168a->tdm][format & SND_SOC_DAIFMT_FORMAT_MASK];
++
++ if (fmt < 0) {
++ dev_err(codec->dev, "unsupported dai format of TDM mode\n");
+ return -EINVAL;
+ }
+
+@@ -349,6 +371,16 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
+ return -EINVAL;
+ }
+
++ if ((pcm3168a->tdm == TDM_MODE_HS) && (master_mode)) {
++ dev_err(codec->dev, "TDM high speed supported only in slave mode\n");
++ return -EINVAL;
++ }
++
++ if ((pcm3168a->tdm == TDM_MODE_HS) && (!dac)) {
++ dev_err(codec->dev, "TDM high speed not supported for ADC\n");
++ return -EINVAL;
++ }
++
+ switch (format & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+@@ -391,10 +423,12 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+ {
++ int bits;
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
+ bool tx, master_mode;
+ u32 val, mask, shift, reg;
++ u32 sample_rate = 0; /* auto */
+ unsigned int rate, channels, fmt, ratio, max_ratio;
+ int i, min_frame_size;
+ snd_pcm_format_t format;
+@@ -402,6 +436,9 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
+ rate = params_rate(params);
+ format = params_format(params);
+ channels = params_channels(params);
++ bits = params->msbits;
++
++ tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+ ratio = pcm3168a->sysclk / rate;
+
+@@ -432,26 +469,49 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
+ return -EINVAL;
+ }
+
+- min_frame_size = snd_pcm_format_width(params_format(params)) * 2;
+- switch (min_frame_size) {
+- case 32:
+- if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) {
+- dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n");
++ if (pcm3168a->tdm == TDM_MODE_NONE) {
++ /* one stereo frame size */
++ min_frame_size = bits * 2;
++ switch (min_frame_size) {
++ case 32:
++ if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) {
++ dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n");
++ return -EINVAL;
++ }
++ fmt = PCM3168A_FMT_RIGHT_J_16;
++ break;
++ case 48:
++ if (master_mode ||
++ (fmt == PCM3168A_FMT_DSP_A) ||
++ (fmt == PCM3168A_FMT_DSP_B)) {
++ dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n");
++ return -EINVAL;
++ }
++ break;
++ case 64:
++ break;
++ default:
++ dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size);
+ return -EINVAL;
+ }
+- fmt = PCM3168A_FMT_RIGHT_J_16;
+- break;
+- case 48:
+- if (master_mode || (fmt & PCM3168A_FMT_DSP_MASK)) {
+- dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n");
++ }
++ if ((pcm3168a->tdm == TDM_MODE_NORM) ||
++ (pcm3168a->tdm == TDM_MODE_HS)) {
++ /* all channels over one or two line */
++ min_frame_size = bits * channels;
++
++ /* single rate */
++ sample_rate = 1;
++
++ /*
++ * 256fs for single line DIN0/DOUT0
++ * 128fs for two lines DIN01/DOU01
++ */
++ if ((min_frame_size != 256) &&
++ (min_frame_size != 128)) {
++ dev_err(codec->dev, "256/128-bit frames only supported in TDM formats\n");
+ return -EINVAL;
+ }
+- break;
+- case 64:
+- break;
+- default:
+- dev_err(codec->dev, "unsupported frame size: %d\n", min_frame_size);
+- return -EINVAL;
+ }
+
+ if (master_mode)
+@@ -460,6 +520,9 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
+ val = 0;
+
+ regmap_update_bits(pcm3168a->regmap, reg, mask, val);
++ regmap_update_bits(pcm3168a->regmap, PCM3168A_RST_SMODE,
++ PCM3168A_DAC_SRDA_MASK,
++ sample_rate << PCM3168A_DAC_SRDA_SHIFT);
+
+ if (tx) {
+ mask = PCM3168A_DAC_FMT_MASK;
+@@ -474,9 +537,31 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
+ return 0;
+ }
+
++static int pcm3168a_set_tdm_slot(struct snd_soc_dai *dai,
++ unsigned int tx_mask,
++ unsigned int rx_mask,
++ int slots,
++ int slot_width)
++{
++ struct snd_soc_codec *codec = dai->codec;
++ struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
++
++ if ((slots != 8) && (slots != 4))
++ return -EINVAL;
++
++ if ((slot_width != 32) && (slot_width != 24))
++ return -EINVAL;
++
++ pcm3168a->slots = slots;
++ pcm3168a->slot_width = slot_width;
++
++ return 0;
++}
++
+ static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
+ .set_fmt = pcm3168a_set_dai_fmt_dac,
+ .set_sysclk = pcm3168a_set_dai_sysclk,
++ .set_tdm_slot = pcm3168a_set_tdm_slot,
+ .hw_params = pcm3168a_hw_params,
+ .digital_mute = pcm3168a_digital_mute
+ };
+@@ -484,6 +569,7 @@ static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
+ static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = {
+ .set_fmt = pcm3168a_set_dai_fmt_adc,
+ .set_sysclk = pcm3168a_set_dai_sysclk,
++ .set_tdm_slot = pcm3168a_set_tdm_slot,
+ .hw_params = pcm3168a_hw_params
+ };
+
+@@ -661,6 +747,14 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
+ goto err_regulator;
+ }
+
++ /* get TDM mode */
++ if (dev->of_node) {
++ if (of_get_property(dev->of_node, "tdm", NULL))
++ pcm3168a->tdm = TDM_MODE_NORM;
++ else if (of_get_property(dev->of_node, "tdmhs", NULL))
++ pcm3168a->tdm = TDM_MODE_HS;
++ }
++
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+diff --git a/sound/soc/codecs/pcm3168a.h b/sound/soc/codecs/pcm3168a.h
+index 56c8332..658507f 100644
+--- a/sound/soc/codecs/pcm3168a.h
++++ b/sound/soc/codecs/pcm3168a.h
+@@ -69,7 +69,7 @@ extern void pcm3168a_remove(struct device *dev);
+ #define PCM3168A_ADC_MSAD_SHIFT 4
+ #define PCM3168A_ADC_MSAD_MASK 0x70
+ #define PCM3168A_ADC_FMTAD_SHIFT 0
+-#define PCM3168A_ADC_FMTAD_MASK 0x7
++#define PCM3168A_ADC_FMTAD_MASK 0xf
+
+ #define PCM3168A_ADC_PWR_HPFB 0x52
+ #define PCM3168A_ADC_PSVAD_SHIFT 4
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0018-ASoC-PCM3168A-disable-16-bit-format.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0018-ASoC-PCM3168A-disable-16-bit-format.patch
new file mode 100644
index 0000000..70c9510
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0018-ASoC-PCM3168A-disable-16-bit-format.patch
@@ -0,0 +1,26 @@
+From 4b228271e1276e67d3892aef2e86644c8f382314 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Wed, 13 Apr 2016 14:36:14 +0300
+Subject: [PATCH 18/21] ASoC: PCM3168A: disable 16 bit format
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ sound/soc/codecs/pcm3168a.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
+index 6287ad9..06b5d53 100644
+--- a/sound/soc/codecs/pcm3168a.c
++++ b/sound/soc/codecs/pcm3168a.c
+@@ -22,7 +22,7 @@
+
+ #include "pcm3168a.h"
+
+-#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
++#define PCM3168A_FORMATS (/*SNDRV_PCM_FMTBIT_S16_LE | */\
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0019-ASoC-PCM3168A-enable-on-power-on.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0019-ASoC-PCM3168A-enable-on-power-on.patch
new file mode 100644
index 0000000..0159985
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0019-ASoC-PCM3168A-enable-on-power-on.patch
@@ -0,0 +1,27 @@
+From 881838951504feac2f42e771ed2195b305cf22a1 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Wed, 13 Apr 2016 14:36:50 +0300
+Subject: [PATCH 19/21] ASoC: PCM3168A: enable on power on
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ sound/soc/codecs/pcm3168a.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
+index 06b5d53..719eca1 100644
+--- a/sound/soc/codecs/pcm3168a.c
++++ b/sound/soc/codecs/pcm3168a.c
+@@ -600,7 +600,7 @@ static struct snd_soc_dai_driver pcm3168a_dais[] = {
+
+ static const struct reg_default pcm3168a_reg_default[] = {
+ { PCM3168A_RST_SMODE, PCM3168A_MRST_MASK | PCM3168A_SRST_MASK },
+- { PCM3168A_DAC_PWR_MST_FMT, 0x00 },
++ { PCM3168A_DAC_PWR_MST_FMT, 0x80 },
+ { PCM3168A_DAC_OP_FLT, 0x00 },
+ { PCM3168A_DAC_INV, 0x00 },
+ { PCM3168A_DAC_MUTE, 0x00 },
+--
+1.7.10.4
+
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0020-ASoC-PCM3168A-merge-ADC-and-DAC-to-single-DAI.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0020-ASoC-PCM3168A-merge-ADC-and-DAC-to-single-DAI.patch
new file mode 100644
index 0000000..9565725
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0020-ASoC-PCM3168A-merge-ADC-and-DAC-to-single-DAI.patch
@@ -0,0 +1,276 @@
+From 45b88347956046b1a75c791254609ca7becdb0bf Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Wed, 20 Apr 2016 17:52:12 +0300
+Subject: [PATCH] ASoC: PCM3168A: merge ADC and DAC to single DAI
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ sound/soc/codecs/pcm3168a.c | 151 +++++++++++++++++++++----------------------
+ 1 file changed, 72 insertions(+), 79 deletions(-)
+
+diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
+index 719eca1..e1b5feb 100644
+--- a/sound/soc/codecs/pcm3168a.c
++++ b/sound/soc/codecs/pcm3168a.c
+@@ -57,8 +57,7 @@ struct pcm3168a_priv {
+ struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
+ struct regmap *regmap;
+ struct clk *scki;
+- bool adc_master_mode;
+- bool dac_master_mode;
++ bool master_mode;
+ unsigned int tdm;
+ unsigned int slots;
+ unsigned int slot_width;
+@@ -343,7 +342,7 @@ int format_table[3][6] = {
+ [SND_SOC_DAIFMT_DSP_B] = -1},
+ };
+
+-static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
++static int __pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int format, bool dac)
+ {
+ struct snd_soc_codec *codec = dai->codec;
+@@ -392,31 +391,32 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
+ reg = PCM3168A_DAC_PWR_MST_FMT;
+ mask = PCM3168A_DAC_FMT_MASK;
+ shift = PCM3168A_DAC_FMT_SHIFT;
+- pcm3168a->dac_master_mode = master_mode;
+ pcm3168a->dac_fmt = fmt;
+ } else {
+ reg = PCM3168A_ADC_MST_FMT;
+ mask = PCM3168A_ADC_FMTAD_MASK;
+ shift = PCM3168A_ADC_FMTAD_SHIFT;
+- pcm3168a->adc_master_mode = master_mode;
+ pcm3168a->adc_fmt = fmt;
+ }
+
++ pcm3168a->master_mode = master_mode;
++
+ regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
+
+ return 0;
+ }
+
+-static int pcm3168a_set_dai_fmt_dac(struct snd_soc_dai *dai,
++static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
+ unsigned int format)
+ {
+- return pcm3168a_set_dai_fmt(dai, format, true);
+-}
++ int ret;
+
+-static int pcm3168a_set_dai_fmt_adc(struct snd_soc_dai *dai,
+- unsigned int format)
+-{
+- return pcm3168a_set_dai_fmt(dai, format, false);
++ /* dac */
++ ret = __pcm3168a_set_dai_fmt(dai, format, false);
++ if (ret)
++ return ret;
++ /* adc */
++ return __pcm3168a_set_dai_fmt(dai, format, true);
+ }
+
+ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
+@@ -426,7 +426,7 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
+ int bits;
+ struct snd_soc_codec *codec = dai->codec;
+ struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
+- bool tx, master_mode;
++ bool tx;
+ u32 val, mask, shift, reg;
+ u32 sample_rate = 0; /* auto */
+ unsigned int rate, channels, fmt, ratio, max_ratio;
+@@ -440,33 +440,28 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
+
+ tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+
+- ratio = pcm3168a->sysclk / rate;
+-
+- tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+ if (tx) {
+ max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC;
+- reg = PCM3168A_DAC_PWR_MST_FMT;
+- mask = PCM3168A_DAC_MSDA_MASK;
+- shift = PCM3168A_DAC_MSDA_SHIFT;
+- master_mode = pcm3168a->dac_master_mode;
+ fmt = pcm3168a->dac_fmt;
+ } else {
+ max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC;
+- reg = PCM3168A_ADC_MST_FMT;
+- mask = PCM3168A_ADC_MSAD_MASK;
+- shift = PCM3168A_ADC_MSAD_SHIFT;
+- master_mode = pcm3168a->adc_master_mode;
+ fmt = pcm3168a->adc_fmt;
+ }
+
+- for (i = 0; i < max_ratio; i++) {
+- if (pcm3168a_scki_ratios[i] == ratio)
+- break;
+- }
++ if (pcm3168a->master_mode) {
++ ratio = pcm3168a->sysclk / rate;
++ for (i = 0; i < max_ratio; i++)
++ if (pcm3168a_scki_ratios[i] == ratio)
++ break;
+
+- if (i == max_ratio) {
+- dev_err(codec->dev, "unsupported sysclk ratio\n");
+- return -EINVAL;
++ if (i == max_ratio) {
++ dev_err(codec->dev, "unsupported sysclk ratio: %d\n", ratio);
++ return -EINVAL;
++ }
++ val = i + 1;
++ } else {
++ /* slave mode */
++ val = 0;
+ }
+
+ if (pcm3168a->tdm == TDM_MODE_NONE) {
+@@ -474,14 +469,15 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
+ min_frame_size = bits * 2;
+ switch (min_frame_size) {
+ case 32:
+- if (master_mode || (fmt != PCM3168A_FMT_RIGHT_J)) {
++ if (pcm3168a->master_mode ||
++ (fmt != PCM3168A_FMT_RIGHT_J)) {
+ dev_err(codec->dev, "32-bit frames are supported only for slave mode using right justified\n");
+ return -EINVAL;
+ }
+ fmt = PCM3168A_FMT_RIGHT_J_16;
+ break;
+ case 48:
+- if (master_mode ||
++ if (pcm3168a->master_mode ||
+ (fmt == PCM3168A_FMT_DSP_A) ||
+ (fmt == PCM3168A_FMT_DSP_B)) {
+ dev_err(codec->dev, "48-bit frames not supported in master mode, or slave mode using DSP\n");
+@@ -514,26 +510,30 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
+ }
+ }
+
+- if (master_mode)
+- val = ((i + 1) << shift);
+- else
++ /* Setup ADC in master mode, couse it drives ADC */
++ if ((pcm3168a->master_mode) || (tx)) {
++ fmt = pcm3168a->dac_fmt;
++ reg = PCM3168A_DAC_PWR_MST_FMT;
++ mask = PCM3168A_DAC_MSDA_MASK | PCM3168A_DAC_FMT_MASK;
++ shift = PCM3168A_DAC_MSDA_SHIFT;
++ /* start DAC */
++ regmap_update_bits(pcm3168a->regmap, reg, mask, (val << shift) | fmt);
++ }
++ /* Do we need also ADC? */
++ if (!tx) {
++ fmt = pcm3168a->adc_fmt;
++ reg = PCM3168A_ADC_MST_FMT;
++ mask = PCM3168A_ADC_MSAD_MASK | PCM3168A_ADC_FMTAD_MASK;
++ shift = PCM3168A_ADC_MSAD_SHIFT;
++ /* ADC slave mode only, driven by DAC or CPU DAI */
+ val = 0;
++ regmap_update_bits(pcm3168a->regmap, reg, mask, (val << shift) | fmt);
++ }
+
+- regmap_update_bits(pcm3168a->regmap, reg, mask, val);
+ regmap_update_bits(pcm3168a->regmap, PCM3168A_RST_SMODE,
+ PCM3168A_DAC_SRDA_MASK,
+ sample_rate << PCM3168A_DAC_SRDA_SHIFT);
+
+- if (tx) {
+- mask = PCM3168A_DAC_FMT_MASK;
+- shift = PCM3168A_DAC_FMT_SHIFT;
+- } else {
+- mask = PCM3168A_ADC_FMTAD_MASK;
+- shift = PCM3168A_ADC_FMTAD_SHIFT;
+- }
+-
+- regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
+-
+ return 0;
+ }
+
+@@ -558,44 +558,32 @@ static int pcm3168a_set_tdm_slot(struct snd_soc_dai *dai,
+ return 0;
+ }
+
+-static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
+- .set_fmt = pcm3168a_set_dai_fmt_dac,
++static const struct snd_soc_dai_ops pcm3168a_dai_ops = {
++ .set_fmt = pcm3168a_set_dai_fmt,
+ .set_sysclk = pcm3168a_set_dai_sysclk,
+ .set_tdm_slot = pcm3168a_set_tdm_slot,
+ .hw_params = pcm3168a_hw_params,
+ .digital_mute = pcm3168a_digital_mute
+ };
+
+-static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = {
+- .set_fmt = pcm3168a_set_dai_fmt_adc,
+- .set_sysclk = pcm3168a_set_dai_sysclk,
+- .set_tdm_slot = pcm3168a_set_tdm_slot,
+- .hw_params = pcm3168a_hw_params
+-};
+-
+-static struct snd_soc_dai_driver pcm3168a_dais[] = {
+- {
+- .name = "pcm3168a-dac",
+- .playback = {
+- .stream_name = "Playback",
+- .channels_min = 1,
+- .channels_max = 8,
+- .rates = SNDRV_PCM_RATE_8000_192000,
+- .formats = PCM3168A_FORMATS
+- },
+- .ops = &pcm3168a_dac_dai_ops
++static struct snd_soc_dai_driver pcm3168a_dai = {
++ .name = "pcm3168a",
++ .playback = {
++ .stream_name = "Playback",
++ .channels_min = 1,
++ .channels_max = 8,
++ .rates = SNDRV_PCM_RATE_8000_192000,
++ .formats = PCM3168A_FORMATS
+ },
+- {
+- .name = "pcm3168a-adc",
+- .capture = {
+- .stream_name = "Capture",
+- .channels_min = 1,
+- .channels_max = 6,
+- .rates = SNDRV_PCM_RATE_8000_96000,
+- .formats = PCM3168A_FORMATS
+- },
+- .ops = &pcm3168a_adc_dai_ops
++ .capture = {
++ .stream_name = "Capture",
++ .channels_min = 1,
++ .channels_max = 8,
++ .rates = SNDRV_PCM_RATE_8000_96000,
++ .formats = PCM3168A_FORMATS
+ },
++ .ops = &pcm3168a_dai_ops,
++ .symmetric_rates = 1,
+ };
+
+ static const struct reg_default pcm3168a_reg_default[] = {
+@@ -759,8 +747,13 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
+ pm_runtime_enable(dev);
+ pm_runtime_idle(dev);
+
+- ret = snd_soc_register_codec(dev, &pcm3168a_driver, pcm3168a_dais,
+- ARRAY_SIZE(pcm3168a_dais));
++ if (pcm3168a->tdm != TDM_MODE_NONE) {
++ pcm3168a_dai.playback.channels_min = 8;
++ pcm3168a_dai.capture.channels_min = 8;
++ }
++
++ ret = snd_soc_register_codec(dev, &pcm3168a_driver,
++ &pcm3168a_dai, 1);
+ if (ret) {
+ dev_err(dev, "failed to register codec: %d\n", ret);
+ goto err_regulator;
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0021-ASoC-PCM3168A-disable-PM.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0021-ASoC-PCM3168A-disable-PM.patch
new file mode 100644
index 0000000..13663e9
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0021-ASoC-PCM3168A-disable-PM.patch
@@ -0,0 +1,43 @@
+From 98080a2e34eaf0085c3ea34079dfce89c10d0ee1 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Mon, 30 May 2016 12:30:26 +0300
+Subject: [PATCH] ASoC: PCM3168A: disable PM
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ sound/soc/codecs/pcm3168a.c | 12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
+index 8113566..2bee442 100644
+--- a/sound/soc/codecs/pcm3168a.c
++++ b/sound/soc/codecs/pcm3168a.c
+@@ -797,7 +797,8 @@ void pcm3168a_remove(struct device *dev)
+ }
+ EXPORT_SYMBOL_GPL(pcm3168a_remove);
+
+-#ifdef CONFIG_PM
++
++#ifdef CONFIG_PM__
+ static int pcm3168a_rt_resume(struct device *dev)
+ {
+ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+@@ -856,6 +857,15 @@ static int pcm3168a_rt_suspend(struct device *dev)
+
+ return 0;
+ }
++#else
++static int pcm3168a_rt_resume(struct device *dev)
++{
++ return 0;
++}
++static int pcm3168a_rt_suspend(struct device *dev)
++{
++ return 0;
++}
+ #endif
+
+ const struct dev_pm_ops pcm3168a_pm_ops = {
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0022-ASoC-fix-simple-card-do-not-respect-device-id.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0022-ASoC-fix-simple-card-do-not-respect-device-id.patch
new file mode 100644
index 0000000..dd1dd3c
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0022-ASoC-fix-simple-card-do-not-respect-device-id.patch
@@ -0,0 +1,37 @@
+From 1bee53bce7b8bf48f70e8b22272dceb47dbfc29e Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Wed, 17 Feb 2016 17:45:17 +0300
+Subject: [PATCH 22/31] ASoC: fix simple-card do not respect device id
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ sound/soc/generic/simple-card.c | 13 -------------
+ 1 file changed, 13 deletions(-)
+
+diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
+index 9556644..0bad6f0 100644
+--- a/sound/soc/generic/simple-card.c
++++ b/sound/soc/generic/simple-card.c
+@@ -435,19 +435,6 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
+ dev_err(dev, "parse error %d\n", ret);
+ goto err;
+ }
+-
+- /*
+- * soc_bind_dai_link() will check cpu name
+- * after of_node matching if dai_link has cpu_dai_name.
+- * but, it will never match if name was created by fmt_single_name()
+- * remove cpu_dai_name to escape name matching.
+- * see
+- * fmt_single_name()
+- * fmt_multiple_name()
+- */
+- if (num_links == 1)
+- dai_link->cpu_dai_name = NULL;
+-
+ } else {
+ struct asoc_simple_card_info *cinfo;
+
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0023-SPI-GPIO-get-master-fix.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0023-SPI-GPIO-get-master-fix.patch
new file mode 100644
index 0000000..359c7ea
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0023-SPI-GPIO-get-master-fix.patch
@@ -0,0 +1,26 @@
+From 95e2424a054402d712feeebdcb3be5db4c545b5d Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Fri, 12 Feb 2016 17:52:21 +0300
+Subject: [PATCH 23/31] SPI-GPIO: get master fix
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/spi/spi-gpio.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/spi/spi-gpio.c b/drivers/spi/spi-gpio.c
+index 0021fc4..86f2ecc 100644
+--- a/drivers/spi/spi-gpio.c
++++ b/drivers/spi/spi-gpio.c
+@@ -469,7 +469,7 @@ static int spi_gpio_probe(struct platform_device *pdev)
+ }
+ #endif
+
+- spi_gpio->bitbang.master = spi_master_get(master);
++ spi_gpio->bitbang.master = master;
+ spi_gpio->bitbang.chipselect = spi_gpio_chipselect;
+
+ if ((master_flags & (SPI_MASTER_NO_TX | SPI_MASTER_NO_RX)) == 0) {
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0024-SPIDEV-extend-maximum-transfer-size.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0024-SPIDEV-extend-maximum-transfer-size.patch
new file mode 100644
index 0000000..fba1bca
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0024-SPIDEV-extend-maximum-transfer-size.patch
@@ -0,0 +1,26 @@
+From 392890512e63267a76263a5c36657a2c38bed099 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Wed, 17 Feb 2016 18:11:55 +0300
+Subject: [PATCH 24/31] SPIDEV: extend maximum transfer size
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/spi/spidev.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
+index 911e9e0..354fd4b 100644
+--- a/drivers/spi/spidev.c
++++ b/drivers/spi/spidev.c
+@@ -90,7 +90,7 @@ struct spidev_data {
+ static LIST_HEAD(device_list);
+ static DEFINE_MUTEX(device_list_lock);
+
+-static unsigned bufsiz = 4096;
++static unsigned bufsiz = 8192;
+ module_param(bufsiz, uint, S_IRUGO);
+ MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message");
+
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0025-Radio-add-si468x-to-spidev.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0025-Radio-add-si468x-to-spidev.patch
new file mode 100644
index 0000000..749fb89
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0025-Radio-add-si468x-to-spidev.patch
@@ -0,0 +1,25 @@
+From 4474e4919ab324979d736d53d05064d4f10bbd28 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Fri, 12 Feb 2016 17:55:25 +0300
+Subject: [PATCH 25/31] Radio: add si468x to spidev
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/spi/spidev.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c
+index 354fd4b..794d8d0 100644
+--- a/drivers/spi/spidev.c
++++ b/drivers/spi/spidev.c
+@@ -646,6 +646,7 @@ static int spidev_remove(struct spi_device *spi)
+
+ static const struct of_device_id spidev_dt_ids[] = {
+ { .compatible = "rohm,dh2228fv" },
++ { .compatible = "si,468x" },
+ {},
+ };
+
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0026-ASoC-add-dummy-Si468x-driver.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0026-ASoC-add-dummy-Si468x-driver.patch
new file mode 100644
index 0000000..58d2769
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0026-ASoC-add-dummy-Si468x-driver.patch
@@ -0,0 +1,122 @@
+From 56f1240b9e418ad1196085397597e2c710a73946 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Thu, 18 Feb 2016 18:54:18 +0300
+Subject: [PATCH 26/31] ASoC: add dummy Si468x driver
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ sound/soc/codecs/Kconfig | 3 +++
+ sound/soc/codecs/Makefile | 2 ++
+ sound/soc/codecs/si468x.c | 66 +++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 71 insertions(+)
+ create mode 100644 sound/soc/codecs/si468x.c
+
+diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
+index 4f0c01a..bf0f022 100644
+--- a/sound/soc/codecs/Kconfig
++++ b/sound/soc/codecs/Kconfig
+@@ -310,6 +310,9 @@ config SND_SOC_PCM3168A_SPI
+ select SND_SOC_PCM3168A
+ select REGMAP_SPI
+
++config SND_SOC_SI468X
++ tristate "Dummy sound driver for Si468x radio"
++
+ config SND_SOC_PCM512x
+ tristate
+ config SND_SOC_RT5631
+diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
+index 513d895..ba05156 100644
+--- a/sound/soc/codecs/Makefile
++++ b/sound/soc/codecs/Makefile
+@@ -51,6 +51,7 @@ snd-soc-sgtl5000-objs := sgtl5000.o
+ snd-soc-alc5623-objs := alc5623.o
+ snd-soc-alc5632-objs := alc5632.o
+ snd-soc-sigmadsp-objs := sigmadsp.o
++snd-soc-si468x-objs := si468x.o
+ snd-soc-si476x-objs := si476x.o
+ snd-soc-sn95031-objs := sn95031.o
+ snd-soc-spdif-tx-objs := spdif_transciever.o
+@@ -180,6 +181,7 @@ obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
+ obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
+ obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
+ obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o
++obj-$(CONFIG_SND_SOC_SI468X) += snd-soc-si468x.o
+ obj-$(CONFIG_SND_SOC_SN95031) +=snd-soc-sn95031.o
+ obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o
+ obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o
+diff --git a/sound/soc/codecs/si468x.c b/sound/soc/codecs/si468x.c
+new file mode 100644
+index 0000000..18b099f
+--- /dev/null
++++ b/sound/soc/codecs/si468x.c
+@@ -0,0 +1,66 @@
++/*
++ * Dummy sound driver for Si468x DAB/FM/AM chips
++ * Copyright 2016 Andrey Gusakov <andrey.gusakov@cogentembedded.com>
++ *
++ * Based on: Driver for the DFBM-CS320 bluetooth module
++ * Copyright 2011 Lars-Peter Clausen <lars@metafoo.de>
++ *
++ * 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; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/platform_device.h>
++
++#include <sound/soc.h>
++
++static struct snd_soc_dai_driver si468x_dai = {
++ .name = "si468x-pcm",
++ .capture = {
++ .channels_min = 2,
++ .channels_max = 2,
++ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_44100,
++ .formats = SNDRV_PCM_FMTBIT_S16_LE,
++ },
++};
++
++static struct snd_soc_codec_driver soc_codec_dev_si468x;
++
++static int si468x_probe(struct platform_device *pdev)
++{
++ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_si468x,
++ &si468x_dai, 1);
++}
++
++static int si468x_remove(struct platform_device *pdev)
++{
++ snd_soc_unregister_codec(&pdev->dev);
++
++ return 0;
++}
++
++static const struct of_device_id si468x_of_match[] = {
++ { .compatible = "si,si468x-pcm", },
++ { }
++};
++MODULE_DEVICE_TABLE(of, si468x_of_match);
++
++static struct platform_driver si468x_driver = {
++ .driver = {
++ .name = "si468x",
++ .of_match_table = si468x_of_match,
++ .owner = THIS_MODULE,
++ },
++ .probe = si468x_probe,
++ .remove = si468x_remove,
++};
++
++module_platform_driver(si468x_driver);
++
++MODULE_AUTHOR("Andrey Gusakov <andrey.gusakov@cogentembedded.com>");
++MODULE_DESCRIPTION("ASoC Si468x radio chip driver");
++MODULE_LICENSE("GPL");
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0027-ASoC-R-Car-initial-TDM-support.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0027-ASoC-R-Car-initial-TDM-support.patch
new file mode 100644
index 0000000..b165b44
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0027-ASoC-R-Car-initial-TDM-support.patch
@@ -0,0 +1,354 @@
+From cd5612aeb65de5468f793558d6536d1b90b3dc76 Mon Sep 17 00:00:00 2001
+From: Grigory Kletsko <grigory.kletsko@cogentembedded.com>
+Date: Wed, 15 Jun 2016 21:13:26 +0300
+Subject: [PATCH 1/5] ASoC: R-Car: initial TDM support
+
+---
+ include/sound/rcar_snd.h | 4 ++
+ sound/soc/sh/rcar/core.c | 14 ++++-
+ sound/soc/sh/rcar/src.c | 12 +++-
+ sound/soc/sh/rcar/ssi.c | 148 ++++++++++++++++++++++++++++++++++++++++++++---
+ 4 files changed, 164 insertions(+), 14 deletions(-)
+
+diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h
+index e2f1000..37229c1 100644
+--- a/include/sound/rcar_snd.h
++++ b/include/sound/rcar_snd.h
+@@ -35,6 +35,10 @@
+ */
+ #define RSND_SSI_CLK_PIN_SHARE (1 << 31)
+ #define RSND_SSI_NO_BUSIF (1 << 30) /* SSI+DMA without BUSIF */
++#define RSND_SSI_TDM_4 (1 << 29) /* use 4ch TDM on this SSI */
++#define RSND_SSI_TDM_6 (1 << 28) /* use 6ch TDM on this SSI */
++#define RSND_SSI_TDM_8 (1 << 27) /* use 8ch TDM on this SSI */
++#define RSND_SSI_TDM_MASK (0x7 << 27)
+
+ #define RSND_SSI(_dma_id, _pio_irq, _flags) \
+ { .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags }
+diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
+index ede9b5d..0b5c90e 100644
+--- a/sound/soc/sh/rcar/core.c
++++ b/sound/soc/sh/rcar/core.c
+@@ -426,6 +426,16 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
+ case 16:
+ adinr |= (8 << 16);
+ break;
++ case 18:
++ adinr |= (6 << 16);
++ break;
++ case 20:
++ adinr |= (4 << 16);
++ break;
++ case 22:
++ adinr |= (2 << 16);
++ break;
++ case 24:
+ case 32:
+ adinr |= (0 << 16);
+ break;
+@@ -1074,7 +1084,7 @@ static int rsnd_dai_probe(struct platform_device *pdev,
+ drv[i].playback.rates = RSND_RATES;
+ drv[i].playback.formats = RSND_FMTS;
+ drv[i].playback.channels_min = 2;
+- drv[i].playback.channels_max = 2;
++ drv[i].playback.channels_max = 8;
+
+ rdai[i].playback.info = &info->dai_info[i].playback;
+ rsnd_path_init(priv, &rdai[i], &rdai[i].playback);
+@@ -1083,7 +1093,7 @@ static int rsnd_dai_probe(struct platform_device *pdev,
+ drv[i].capture.rates = RSND_RATES;
+ drv[i].capture.formats = RSND_FMTS;
+ drv[i].capture.channels_min = 2;
+- drv[i].capture.channels_max = 2;
++ drv[i].capture.channels_max = 8;
+
+ rdai[i].capture.info = &info->dai_info[i].capture;
+ rsnd_path_init(priv, &rdai[i], &rdai[i].capture);
+diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
+index 426714e..bbd7fe5 100644
+--- a/sound/soc/sh/rcar/src.c
++++ b/sound/soc/sh/rcar/src.c
+@@ -158,6 +158,8 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
+ */
+ if (rsnd_ssi_is_pin_sharing(ssi_mod)) {
+ int shift = -1;
++ int sync_34 = 0;
++ int mask = 0;
+ switch (ssi_id) {
+ case 1:
+ shift = 0;
+@@ -165,16 +167,20 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
+ case 2:
+ shift = 2;
+ break;
++ case 3:
+ case 4:
+ shift = 16;
++ sync_34 = 1;
++ mask = (1 << 20);
+ break;
+ }
+
+ if (shift >= 0)
+ rsnd_mod_bset(ssi_mod, SSI_MODE1,
+- 0x3 << shift,
+- rsnd_dai_is_clk_master(rdai) ?
+- 0x2 << shift : 0x1 << shift);
++ (0x3 << shift) | mask,
++ (rsnd_dai_is_clk_master(rdai) ?
++ (0x2 << shift) : (0x1 << shift)) |
++ (sync_34 << 20));
+ }
+
+ /*
+diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
+index 49acffe..a55ac91 100644
+--- a/sound/soc/sh/rcar/ssi.c
++++ b/sound/soc/sh/rcar/ssi.c
+@@ -26,6 +26,10 @@
+ #define IIEN (1 << 25) /* Idle Mode Interrupt Enable */
+ #define DIEN (1 << 24) /* Data Interrupt Enable */
+
++#define CHNL_TDM_4 (1 << 22) /* A TDM frame consists of four system words */
++#define CHNL_TDM_6 (2 << 22) /* A TDM frame consists of six system words */
++#define CHNL_TDM_8 (3 << 22) /* A TDM frame consists of eight system words */
++
+ #define DWL_8 (0 << 19) /* Data Word Length */
+ #define DWL_16 (1 << 19) /* Data Word Length */
+ #define DWL_18 (2 << 19) /* Data Word Length */
+@@ -58,6 +62,8 @@
+ * SSIWSR
+ */
+ #define CONT (1 << 8) /* WS Continue Function */
++#define MONO (1 << 1) /* Monaural format */
++#define WS_MODE_TDM (1 << 0) /* TDM format, monaural format */
+
+ #define SSI_NAME "ssi"
+
+@@ -92,6 +98,24 @@ struct rsnd_ssi {
+ #define rsnd_ssi_mode_flags(p) ((p)->info->flags)
+ #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
+
++int rsnd_ssi_is_tdm(struct rsnd_ssi *ssi)
++{
++ return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_TDM_MASK);
++}
++
++int rsnd_ssi_channels(struct rsnd_ssi *ssi)
++{
++ if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_TDM_4)
++ return 4;
++ if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_TDM_6)
++ return 6;
++ if (rsnd_ssi_mode_flags(ssi) & RSND_SSI_TDM_8)
++ return 8;
++
++ /* no TDM */
++ return 2;
++}
++
+ static int rsnd_ssi_use_busif(struct rsnd_mod *mod)
+ {
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+@@ -152,22 +176,32 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
+ for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
+
+ /*
++ * divider = 1 not allowed in TDM
++ */
++ if ((ssi_clk_mul_table[j] == 1) &&
++ (rsnd_ssi_is_tdm(ssi)))
++ continue;
++
++ /*
+ * this driver is assuming that
+- * system word is 64fs (= 2 x 32bit)
++ * system word n_channels x 32bit
+ * see rsnd_ssi_init()
+ */
+ main_rate = rate / adg_clk_div_table[i]
+- * 32 * 2 * ssi_clk_mul_table[j];
++ * 32 * rsnd_ssi_channels(ssi) \
++ * ssi_clk_mul_table[j];
+
+ ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate);
+ if (0 == ret) {
+- ssi->rate = rate;
+- ssi->cr_clk = FORCE | SWL_32 |
+- SCKD | SWSD | CKDV(j);
+-
+ dev_dbg(dev, "ssi%d outputs %u Hz\n",
+ rsnd_mod_id(&ssi->mod), rate);
++ dev_dbg(dev, " div %d, mul %d\n",
++ adg_clk_div_table[i], ssi_clk_mul_table[j]);
+
++
++ ssi->rate = rate;
++ ssi->cr_clk = FORCE | SWL_32 |
++ SCKD | SWSD | CKDV(j);
+ return 0;
+ }
+ }
+@@ -203,10 +237,13 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
+ rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+ }
+
+- if (rsnd_ssi_clk_from_parent(ssi))
++ if (rsnd_ssi_clk_from_parent(ssi)) {
++ /* in TDM mode CKDV=0 is invalid */
++ ssi->cr_clk = CKDV(1);
+ rsnd_ssi_hw_start(ssi->parent, rdai, io);
+- else
++ } else {
+ rsnd_ssi_master_clk_start(ssi, io);
++ }
+ }
+ }
+
+@@ -318,14 +355,44 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
+ * see also rsnd_ssi_master_clk_enable()
+ */
+ cr |= SWL_32;
++ if (rsnd_ssi_is_tdm(ssi)) {
++ switch (rsnd_ssi_channels(ssi)) {
++ case 4:
++ cr |= CHNL_TDM_4;
++ break;
++ case 6:
++ cr |= CHNL_TDM_6;
++ break;
++ case 8:
++ cr |= CHNL_TDM_8;
++ break;
++ default:
++ return -EIO;
++ }
++ }
+
+ /*
+ * init clock settings for SSICR
+ */
+ switch (runtime->sample_bits) {
++ case 8:
++ cr |= DWL_8;
++ break;
+ case 16:
+ cr |= DWL_16;
+ break;
++ case 18:
++ cr |= DWL_18;
++ break;
++ case 20:
++ cr |= DWL_20;
++ break;
++ case 22:
++ cr |= DWL_22;
++ break;
++ case 24:
++ cr |= DWL_24;
++ break;
+ case 32:
+ cr |= DWL_24;
+ break;
+@@ -394,17 +461,47 @@ static int rsnd_ssi_init_irq(struct rsnd_mod *mod,
+ * see also rsnd_ssi_master_clk_enable()
+ */
+ cr |= SWL_32;
++ if (rsnd_ssi_is_tdm(ssi)) {
++ switch (rsnd_ssi_channels(ssi)) {
++ case 4:
++ cr |= CHNL_TDM_4;
++ break;
++ case 6:
++ cr |= CHNL_TDM_6;
++ break;
++ case 8:
++ cr |= CHNL_TDM_8;
++ break;
++ default:
++ return -EIO;
++ }
++ }
+
+ /*
+ * init clock settings for SSICR
+ */
+ switch (runtime->sample_bits) {
++ case 8:
++ cr |= DWL_8;
++ break;
+ case 16:
+ cr |= DWL_16;
+ break;
+- case 32:
++ case 18:
++ cr |= DWL_18;
++ break;
++ case 20:
++ cr |= DWL_20;
++ break;
++ case 22:
++ cr |= DWL_22;
++ break;
++ case 24:
+ cr |= DWL_24;
+ break;
++ case 32:
++ cr |= DWL_32;
++ break;
+ default:
+ return -EIO;
+ }
+@@ -693,9 +790,33 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
+ {
+ struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+ struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
++ struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
++ u32 wsr = 0;
++
++ /* enable DMA transfer */
++ ssi->cr_etc = DMEN;
++
++ /* enable Overflow and Underflow IRQ */
++ ssi->cr_etc |= UIEN | OIEN;
+
+ rsnd_dma_start(dma);
+
++ rsnd_ssi_hw_start(ssi, ssi->rdai, io);
++
++ rsnd_src_enable_dma_ssi_irq(mod, rdai, rsnd_ssi_use_busif(mod));
++
++ /* enable WS continue */
++ if (rsnd_dai_is_clk_master(rdai))
++ wsr |= CONT;
++
++ /* Enable TDM */
++ if (rsnd_ssi_is_tdm(ssi))
++ wsr |= WS_MODE_TDM;
++
++ rsnd_mod_write(&ssi->mod, SSIWSR, wsr);
++
++ rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
++
+ return 0;
+ }
+
+@@ -916,6 +1036,16 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
+
+ if (of_get_property(np, "no-busif", NULL))
+ ssi_info->flags |= RSND_SSI_NO_BUSIF;
++
++ /*
++ * TDM
++ */
++ if (of_get_property(np, "tdm-4ch", NULL))
++ ssi_info->flags |= RSND_SSI_TDM_4;
++ if (of_get_property(np, "tdm-6ch", NULL))
++ ssi_info->flags |= RSND_SSI_TDM_6;
++ if (of_get_property(np, "tdm-8ch", NULL))
++ ssi_info->flags |= RSND_SSI_TDM_8;
+ }
+
+ rsnd_of_parse_ssi_end:
+--
+2.5.0
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0028-ASoC-rcar-correct-32bit-to-24-bit-sample-conv.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0028-ASoC-rcar-correct-32bit-to-24-bit-sample-conv.patch
new file mode 100644
index 0000000..03bc25f
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0028-ASoC-rcar-correct-32bit-to-24-bit-sample-conv.patch
@@ -0,0 +1,45 @@
+From a6c0cc499acddaae73904ca2ae145c7d461e7f37 Mon Sep 17 00:00:00 2001
+From: Grigory Kletsko <grigory.kletsko@cogentembedded.com>
+Date: Wed, 15 Jun 2016 21:42:20 +0300
+Subject: [PATCH] ASoC: rcar: correct 32bit to 24 bit sample conv
+
+---
+ sound/soc/sh/rcar/src.c | 16 +++++++++++++++-
+ 1 file changed, 15 insertions(+), 1 deletion(-)
+
+diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
+index bbd7fe5..1fae6c2 100644
+--- a/sound/soc/sh/rcar/src.c
++++ b/sound/soc/sh/rcar/src.c
+@@ -145,6 +145,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
+ int use_busif)
+ {
+ struct rsnd_dai_stream *io = rsnd_mod_to_io(ssi_mod);
++ struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+ int ssi_id = rsnd_mod_id(ssi_mod);
+
+ /*
+@@ -192,7 +193,20 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
+ rsnd_mod_write(ssi_mod, BUSIF_DALIGN, val);
+ rsnd_mod_write(ssi_mod, SSI_BUSIF_ADINR,
+ rsnd_get_adinr(ssi_mod));
+- rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1);
++ /*
++ * Bit Length of Output Audio Data is limited to 24 bits
++ * This hack helps to convert (shift) 32 bit samples
++ * to 24 bit data on i2s bus.
++ * Without this hack sound will be too quiet.
++ * Realy not sure why this happens.
++ */
++ if ((runtime->sample_bits == 32) && (ssi_id == 3))
++ rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1 | (8 << 16));
++ else if ((runtime->sample_bits == 32) && (ssi_id == 4))
++ rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1 | (1 << 20) | (8 << 16));
++ else
++ rsnd_mod_write(ssi_mod, SSI_BUSIF_MODE, 1);
++
+ rsnd_mod_write(ssi_mod, SSI_CTRL, 0x1);
+ }
+
+--
+2.5.0
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0029-ASoC-R-Car-fix-debug-output.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0029-ASoC-R-Car-fix-debug-output.patch
new file mode 100644
index 0000000..4d94719
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0029-ASoC-R-Car-fix-debug-output.patch
@@ -0,0 +1,57 @@
+From bdd78e5d972ec91b756db6aef0eb19f44207d9f6 Mon Sep 17 00:00:00 2001
+From: Grigory Kletsko <grigory.kletsko@cogentembedded.com>
+Date: Wed, 15 Jun 2016 21:18:35 +0300
+Subject: [PATCH 3/5] ASoC: R-Car: fix debug output
+
+---
+ sound/soc/sh/rcar/adg.c | 2 +-
+ sound/soc/sh/rcar/ssi.c | 12 ++++++++----
+ 2 files changed, 9 insertions(+), 5 deletions(-)
+
+diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
+index 7d2858c..dbeb6a3 100644
+--- a/sound/soc/sh/rcar/adg.c
++++ b/sound/soc/sh/rcar/adg.c
+@@ -463,7 +463,7 @@ int rsnd_adg_probe(struct platform_device *pdev,
+ adg->clk[CLKI] = devm_clk_get(dev, "clk_i");
+
+ for_each_rsnd_clk(clk, adg, i)
+- dev_dbg(dev, "clk %d : %p\n", i, clk);
++ dev_dbg(dev, "clk %d : %ld\n", i, clk_get_rate(clk));
+
+ #ifdef QUICK_HACK
+ np_root = of_find_node_by_path("/");
+diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
+index a55ac91..a69e621 100644
+--- a/sound/soc/sh/rcar/ssi.c
++++ b/sound/soc/sh/rcar/ssi.c
+@@ -146,7 +146,8 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod,
+ udelay(50);
+ }
+
+- dev_warn(dev, "status check failed\n");
++ dev_warn(dev, "ssi%d status check failed\n",
++ rsnd_mod_id(mod));
+ }
+
+ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
+@@ -431,11 +432,14 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
+ struct device *dev = rsnd_priv_to_dev(priv);
+
+ if (ssi->err > 0)
+- dev_warn(dev, "ssi under/over flow err = %d\n", ssi->err);
++ dev_warn(dev, "ssi%d under/over flow err = %d\n",
++ rsnd_mod_id(mod), ssi->err);
+ if (ssi->err_uirq > 0)
+- dev_warn(dev, "ssi under flow err = %d\n", ssi->err_uirq);
++ dev_warn(dev, "ssi%d under flow err = %d\n",
++ rsnd_mod_id(mod), ssi->err_uirq);
+ if (ssi->err_oirq > 0)
+- dev_warn(dev, "ssi over flow err = %d\n", ssi->err_oirq);
++ dev_warn(dev, "ssi%d over flow err = %d\n",
++ rsnd_mod_id(mod), ssi->err_oirq);
+
+ ssi->rdai = NULL;
+ ssi->cr_own = 0;
+--
+2.5.0
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0030-R-Car-sound-disable-clock-hack.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0030-R-Car-sound-disable-clock-hack.patch
new file mode 100644
index 0000000..b07d7ce
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0030-R-Car-sound-disable-clock-hack.patch
@@ -0,0 +1,24 @@
+From 9f62d868ad64af881f9dc54471ca0410efe01c2e Mon Sep 17 00:00:00 2001
+From: Grigory Kletsko <grigory.kletsko@cogentembedded.com>
+Date: Wed, 15 Jun 2016 21:19:21 +0300
+Subject: [PATCH 4/5] R-Car: sound: disable clock hack
+
+---
+ sound/soc/sh/rcar/rsnd.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
+index d3b544b..1bbd5bc 100644
+--- a/sound/soc/sh/rcar/rsnd.h
++++ b/sound/soc/sh/rcar/rsnd.h
+@@ -492,7 +492,7 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
+
+ #define rsnd_dvc_nr(priv) ((priv)->dvc_nr)
+
+-#define QUICK_HACK
++/* #define QUICK_HACK */
+ #ifdef QUICK_HACK
+ void rsnd_adg_clk_set_rate(struct rsnd_mod *mod, unsigned int rate);
+ #endif
+--
+2.5.0
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0031-ASoC-R-car-SSI-fix-SSI-slave-mode-setup-while-TDM-an.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0031-ASoC-R-car-SSI-fix-SSI-slave-mode-setup-while-TDM-an.patch
new file mode 100644
index 0000000..6c469d1
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0031-ASoC-R-car-SSI-fix-SSI-slave-mode-setup-while-TDM-an.patch
@@ -0,0 +1,48 @@
+From 9467d97eaa2ede54dc67a0f83eb3cdd30cf9dc15 Mon Sep 17 00:00:00 2001
+From: Grigory Kletsko <grigory.kletsko@cogentembedded.com>
+Date: Wed, 15 Jun 2016 21:23:03 +0300
+Subject: [PATCH 5/5] ASoC: R-car: SSI fix SSI slave mode setup while TDM and
+
+---
+ sound/soc/sh/rcar/ssi.c | 22 +++++++++++++++-------
+ 1 file changed, 15 insertions(+), 7 deletions(-)
+
+diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
+index a69e621..65af590 100644
+--- a/sound/soc/sh/rcar/ssi.c
++++ b/sound/soc/sh/rcar/ssi.c
+@@ -231,17 +231,25 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
+ rsnd_mod_hw_start(&ssi->mod);
+
+ if (rsnd_dai_is_clk_master(rdai)) {
+- /* enable WS continue */
+- if (rsnd_dai_is_clk_master(rdai)) {
+- status = rsnd_mod_read(&ssi->mod, SSIWSR);
+- if (!(status & CONT))
+- rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+- }
+-
+ if (rsnd_ssi_clk_from_parent(ssi)) {
++ int wsr;
+ /* in TDM mode CKDV=0 is invalid */
+ ssi->cr_clk = CKDV(1);
++ if (ssi->parent->usrcnt == 0) {
++ ssi->parent->cr_own = ssi->cr_own;
++
++ /* enable WS continue */
++ wsr = CONT;
++
++ /* Enable TDM */
++ if (rsnd_ssi_is_tdm(ssi))
++ wsr |= WS_MODE_TDM;
++ }
+ rsnd_ssi_hw_start(ssi->parent, rdai, io);
++ /* set WSR after master mode is set in CR */
++ if (wsr)
++ rsnd_mod_write(&ssi->parent->mod, SSIWSR, wsr);
++
+ } else {
+ rsnd_ssi_master_clk_start(ssi, io);
+ }
+--
+2.5.0
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0032-mmc-Add-SDIO-function-devicetree-subnode-parsing.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0032-mmc-Add-SDIO-function-devicetree-subnode-parsing.patch
new file mode 100644
index 0000000..a95c7bc
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0032-mmc-Add-SDIO-function-devicetree-subnode-parsing.patch
@@ -0,0 +1,160 @@
+From 882ed046c6383564f09c89a7c53e28a37373fffc Mon Sep 17 00:00:00 2001
+From: Sascha Hauer <s.hauer@pengutronix.de>
+Date: Mon, 30 Jun 2014 11:07:25 +0200
+Subject: [PATCH 32/33] mmc: Add SDIO function devicetree subnode parsing
+
+This adds SDIO devicetree subnode parsing to the mmc core. While
+SDIO devices are runtime probable they sometimes need nonprobable
+additional information on embedded systems, like an additional gpio
+interrupt or a clock. This patch makes it possible to supply this
+information from the devicetree. SDIO drivers will find a pointer
+to the devicenode in their devices of_node pointer.
+
+Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
+[hdegoede@redhat.com: Misc. cleanups]
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/mmc/core/bus.c | 4 ++++
+ drivers/mmc/core/core.c | 29 +++++++++++++++++++++++++++++
+ drivers/mmc/core/core.h | 3 +++
+ drivers/mmc/core/sdio_bus.c | 11 +++++++++++
+ 4 files changed, 47 insertions(+)
+
+diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c
+index 419edfa..87049c6 100644
+--- a/drivers/mmc/core/bus.c
++++ b/drivers/mmc/core/bus.c
+@@ -16,6 +16,7 @@
+ #include <linux/err.h>
+ #include <linux/slab.h>
+ #include <linux/stat.h>
++#include <linux/of.h>
+ #include <linux/pm_runtime.h>
+
+ #include <linux/mmc/card.h>
+@@ -335,6 +336,8 @@ int mmc_add_card(struct mmc_card *card)
+ #endif
+ mmc_init_context_info(card->host);
+
++ card->dev.of_node = mmc_of_find_child_device(card->host, 0);
++
+ ret = device_add(&card->dev);
+ if (ret)
+ return ret;
+@@ -363,6 +366,7 @@ void mmc_remove_card(struct mmc_card *card)
+ mmc_hostname(card->host), card->rca);
+ }
+ device_del(&card->dev);
++ of_node_put(card->dev.of_node);
+ }
+
+ put_device(&card->dev);
+diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
+index 02a40d0..8c85f20a 100644
+--- a/drivers/mmc/core/core.c
++++ b/drivers/mmc/core/core.c
+@@ -27,6 +27,7 @@
+ #include <linux/fault-inject.h>
+ #include <linux/random.h>
+ #include <linux/slab.h>
++#include <linux/of.h>
+
+ #include <linux/mmc/card.h>
+ #include <linux/mmc/host.h>
+@@ -1165,6 +1166,34 @@ u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max)
+ }
+ EXPORT_SYMBOL(mmc_vddrange_to_ocrmask);
+
++static int mmc_of_get_func_num(struct device_node *node)
++{
++ u32 reg;
++ int ret;
++
++ ret = of_property_read_u32(node, "reg", &reg);
++ if (ret < 0)
++ return ret;
++
++ return reg;
++}
++
++struct device_node *mmc_of_find_child_device(struct mmc_host *host,
++ unsigned func_num)
++{
++ struct device_node *node;
++
++ if (!host->parent || !host->parent->of_node)
++ return NULL;
++
++ for_each_child_of_node(host->parent->of_node, node) {
++ if (mmc_of_get_func_num(node) == func_num)
++ return node;
++ }
++
++ return NULL;
++}
++
+ #ifdef CONFIG_REGULATOR
+
+ /**
+diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h
+index b9f18a2..6b01037 100644
+--- a/drivers/mmc/core/core.h
++++ b/drivers/mmc/core/core.h
+@@ -30,6 +30,9 @@ struct mmc_bus_ops {
+ void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops);
+ void mmc_detach_bus(struct mmc_host *host);
+
++struct device_node *mmc_of_find_child_device(struct mmc_host *host,
++ unsigned func_num);
++
+ void mmc_init_erase(struct mmc_card *card);
+
+ void mmc_set_chip_select(struct mmc_host *host, int mode);
+diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
+index 6d67492..b5d8af0 100644
+--- a/drivers/mmc/core/sdio_bus.c
++++ b/drivers/mmc/core/sdio_bus.c
+@@ -21,7 +21,9 @@
+ #include <linux/mmc/card.h>
+ #include <linux/mmc/host.h>
+ #include <linux/mmc/sdio_func.h>
++#include <linux/of.h>
+
++#include "core.h"
+ #include "sdio_cis.h"
+ #include "sdio_bus.h"
+
+@@ -312,6 +314,13 @@ static void sdio_acpi_set_handle(struct sdio_func *func)
+ static inline void sdio_acpi_set_handle(struct sdio_func *func) {}
+ #endif
+
++static void sdio_set_of_node(struct sdio_func *func)
++{
++ struct mmc_host *host = func->card->host;
++
++ func->dev.of_node = mmc_of_find_child_device(host, func->num);
++}
++
+ /*
+ * Register a new SDIO function with the driver model.
+ */
+@@ -321,6 +330,7 @@ int sdio_add_func(struct sdio_func *func)
+
+ dev_set_name(&func->dev, "%s:%d", mmc_card_id(func->card), func->num);
+
++ sdio_set_of_node(func);
+ sdio_acpi_set_handle(func);
+ ret = device_add(&func->dev);
+ if (ret == 0) {
+@@ -344,6 +354,7 @@ void sdio_remove_func(struct sdio_func *func)
+
+ acpi_dev_pm_detach(&func->dev, false);
+ device_del(&func->dev);
++ of_node_put(func->dev.of_node);
+ put_device(&func->dev);
+ }
+
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0038-Porter-LVDS-display-LQ123K1LG03.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0038-Porter-LVDS-display-LQ123K1LG03.patch
new file mode 100644
index 0000000..98011f8
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0038-Porter-LVDS-display-LQ123K1LG03.patch
@@ -0,0 +1,48 @@
+From b5d05aa9f2995276fd00213e6a48842292aa5889 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Sat, 21 Mar 2015 20:07:20 +0300
+Subject: [PATCH] Porter LVDS display LQ123K1LG03
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ arch/arm/mach-shmobile/board-porter-reference.c | 23 ++++++++++++-----------
+ 1 file changed, 12 insertions(+), 11 deletions(-)
+
+diff --git a/arch/arm/mach-shmobile/board-porter-reference.c b/arch/arm/mach-shmobile/board-porter-reference.c
+index d481ecd..6a1110c 100644
+--- a/arch/arm/mach-shmobile/board-porter-reference.c
++++ b/arch/arm/mach-shmobile/board-porter-reference.c
+@@ -64,18 +64,19 @@ static struct rcar_du_encoder_data porter_du_encoders[] = {
+ .type = RCAR_DU_ENCODER_NONE,
+ .output = RCAR_DU_OUTPUT_LVDS0,
+ .connector.lvds.panel = {
+- .width_mm = 229,
+- .height_mm = 149,
++ .width_mm = 291,
++ .height_mm = 109,
++ //.lvds_mode = 4,
+ .mode = {
+- .clock = 69000,
+- .hdisplay = 1280,
+- .hsync_start = 1280 + 48,
+- .hsync_end = 1280 + 48 + 32,
+- .htotal = 1280 + 48 + 32 + 80,
+- .vdisplay = 800,
+- .vsync_start = 800 + 2,
+- .vsync_end = 800 + 2 + 6,
+- .vtotal = 800 + 2 + 6 + 15,
++ .clock = 53000,
++ .hdisplay = 1280,
++ .hsync_start = 1280,
++ .hsync_end = 1688,
++ .htotal = 1688,
++ .vdisplay = 480,
++ .vsync_start = 480,
++ .vsync_end = 525,
++ .vtotal = 525,
+ .flags = 0,
+ },
+ },
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0060-Remove-delay-at-LVDS-camera-initialization.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0060-Remove-delay-at-LVDS-camera-initialization.patch
new file mode 100644
index 0000000..7f29f64
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0060-Remove-delay-at-LVDS-camera-initialization.patch
@@ -0,0 +1,25 @@
+From 4347453d644e81c81317ce256340a3193c129e6a Mon Sep 17 00:00:00 2001
+From: Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>
+Date: Wed, 13 Apr 2016 13:26:25 +0300
+Subject: [PATCH] Remove delay at LVDS camera initialization
+
+Signed-off-by: Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>
+---
+ drivers/media/i2c/soc_camera/max9272_ov10635.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/media/i2c/soc_camera/max9272_ov10635.h b/drivers/media/i2c/soc_camera/max9272_ov10635.h
+index 80e9af4..49f4d29 100644
+--- a/drivers/media/i2c/soc_camera/max9272_ov10635.h
++++ b/drivers/media/i2c/soc_camera/max9272_ov10635.h
+@@ -31,7 +31,7 @@
+ #define MAXIM_IMI_MCU_V0_DELAY 8000 /* delay for powered MCU firmware v0 */
+ #define MAXIM_IMI_MCU_V1_DELAY 3000 /* delay for powered MCU firmware v1 */
+ #define MAXIM_IMI_MCU_NO_DELAY 0 /* delay for unpowered MCU */
+-#define MAXIM_IMI_MCU_DELAY MAXIM_IMI_MCU_V0_DELAY
++#define MAXIM_IMI_MCU_DELAY MAXIM_IMI_MCU_NO_DELAY
+ //#define MAXIM_IMI_MCU_POWERED /* skip ov10635 setup for IMI powered MCU (only fw later then v1) */
+
+ /*
+--
+2.1.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0099-Porter-add-separate-dts-for-ext01-extension-board.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0099-Porter-add-separate-dts-for-ext01-extension-board.patch
new file mode 100644
index 0000000..d2cac49
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0099-Porter-add-separate-dts-for-ext01-extension-board.patch
@@ -0,0 +1,805 @@
+From ed11e08a736ac8fcdfdcc45b13d68cd688aacd73 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Mon, 23 May 2016 19:48:54 +0300
+Subject: [PATCH] Porter: add separate dts for ext01 extension board
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ arch/arm/boot/dts/Makefile | 1 +
+ arch/arm/boot/dts/r8a7791-porter-ext01.dts | 772 ++++++++++++++++++++++++++++
+ 2 files changed, 773 insertions(+)
+ create mode 100644 arch/arm/boot/dts/r8a7791-porter-ext01.dts
+
+diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
+index 5e951fb..68c86f8 100644
+--- a/arch/arm/boot/dts/Makefile
++++ b/arch/arm/boot/dts/Makefile
+@@ -231,6 +231,7 @@ dtb-$(CONFIG_ARCH_ZYNQ) += zynq-zc702.dtb \
+ # R-Car Gen2 AVB variants
+ dtb-$(CONFIG_ARCH_SHMOBILE_MULTI) += \
+ r8a7791-koelsch-eavb.dtb \
++ r8a7791-porter-ext01.dtb \
+ r8a7791-porter-eavb.dtb \
+ r8a7790-lager-eavb.dtb
+
+diff --git a/arch/arm/boot/dts/r8a7791-porter-ext01.dts b/arch/arm/boot/dts/r8a7791-porter-ext01.dts
+new file mode 100644
+index 0000000..d115b36
+--- /dev/null
++++ b/arch/arm/boot/dts/r8a7791-porter-ext01.dts
+@@ -0,0 +1,772 @@
++/*
++ * Device Tree Source for the Porter board
++ *
++ * Copyright (C) 2015 Renesas Electronics Corporation
++ * Copyright (C) 2015 Cogent Embedded, Inc.
++ *
++ * This file is licensed under the terms of the GNU General Public License
++ * version 2. This program is licensed "as is" without any warranty of any
++ * kind, whether express or implied.
++ */
++
++/*
++ * SSI-AK4642
++ *
++ * these commands are required when playback.
++ *
++ * # amixer set "LINEOUT Mixer DACL" on
++ * # amixer set "Digital" 200
++ * # amixer set "DVC Out" 50
++ */
++
++/dts-v1/;
++#include "r8a7791.dtsi"
++#include <dt-bindings/gpio/gpio.h>
++#include <dt-bindings/input/input.h>
++
++/ {
++ model = "Porter";
++ compatible = "renesas,porter", "renesas,r8a7791";
++
++ aliases {
++ serial6 = &scif0;
++ serial7 = &scifa4;
++ serial8 = &scifb2;
++ serial9 = &scif1;
++ };
++
++ chosen {
++ bootargs = "console=ttySC6,38400 ignore_loglevel ip=none rw root=/dev/mmcblk0p1 vmalloc=384M";
++ };
++
++ memory@40000000 {
++ device_type = "memory";
++ reg = <0 0x40000000 0 0x40000000>;
++ };
++
++ memory@200000000 {
++ device_type = "memory";
++ reg = <2 0x00000000 0 0x40000000>;
++ };
++
++
++ snd_clk: snd_clk {
++ compatible = "fixed-clock";
++ #clock-cells = <0>;
++ clock-frequency = <24576000>;
++ clock-output-names = "scki";
++ };
++
++ leds {
++ compatible = "gpio-leds";
++ led2 {
++ gpios = <&gpio2 19 GPIO_ACTIVE_HIGH>;
++ };
++ led3 {
++ gpios = <&gpio2 20 GPIO_ACTIVE_HIGH>;
++ };
++ led4 {
++ gpios = <&gpio2 21 GPIO_ACTIVE_HIGH>;
++ };
++ };
++
++ gpio-keys {
++ compatible = "gpio-keys";
++
++ button@1 {
++ linux,code = <KEY_1>;
++ label = "SW2-1";
++ gpio-key,wakeup;
++ debounce-interval = <20>;
++ gpios = <&gpio7 0 GPIO_ACTIVE_LOW>;
++ };
++ button@2 {
++ linux,code = <KEY_2>;
++ label = "SW2-2";
++ gpio-key,wakeup;
++ debounce-interval = <20>;
++ gpios = <&gpio7 1 GPIO_ACTIVE_LOW>;
++ };
++ };
++
++ vccq_sdhi0: regulator@1 {
++ compatible = "regulator-gpio";
++
++ regulator-name = "SDHI0 VccQ";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <3300000>;
++
++ gpios = <&gpio2 12 GPIO_ACTIVE_HIGH>;
++ gpios-states = <1>;
++ states = <3300000 1
++ 1800000 0>;
++ };
++
++ vcc_sdhi2: regulator@2 {
++ compatible = "regulator-fixed";
++
++ regulator-name = "SDHI2 Vcc";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++ regulator-always-on;
++ };
++
++ vccq_sdhi2: regulator@3 {
++ compatible = "regulator-gpio";
++
++ regulator-name = "SDHI2 VccQ";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <3300000>;
++
++ gpios = <&gpio2 26 GPIO_ACTIVE_HIGH>;
++ gpios-states = <1>;
++ states = <3300000 1
++ 1800000 0>;
++ };
++
++ codec_en_reg: regulator@4 {
++ compatible = "regulator-fixed";
++ regulator-name = "codec-en-regulator";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++
++ /* SND_RST GPIO for this board is CPLD gpio 0 */
++ gpio = <&cpld_gpio 0 0>;
++
++ /* delay - CHECK */
++ startup-delay-us = <70000>;
++ enable-active-high;
++ };
++
++ amp_en_reg: regulator@5 {
++ compatible = "regulator-fixed";
++ regulator-name = "amp-en-regulator";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++
++ /* Phones amps en GPIO for this board is CPLD gpio 1 */
++ gpio = <&cpld_gpio 1 0>;
++
++ startup-delay-us = <0>;
++ enable-active-high;
++ };
++
++ wlan_en_reg: regulator@6 {
++ compatible = "regulator-fixed";
++ regulator-name = "wlan-en-regulator";
++ regulator-min-microvolt = <1800000>;
++ regulator-max-microvolt = <1800000>;
++
++ /* WLAN_EN GPIO for this board CPLD gpio 10 */
++ gpio = <&cpld_gpio 10 0>;
++
++ /* WLAN card specific delay */
++ startup-delay-us = <70000>;
++ enable-active-high;
++ };
++
++ kim {
++ compatible = "kim";
++ nshutdown_gpio = <769>; /* FIX! CPLD pin 11 */
++ /* serial8 */
++ dev_name = "/dev/ttySC8";
++ flow_cntrl = <1>;
++ /* maximum clock on scifb@52MHz */
++ baud_rate = <3250000>;
++ };
++
++ btwilink {
++ compatible = "btwilink";
++ };
++
++ sound_ext: sound@0 {
++ compatible = "simple-audio-card";
++ pinctrl-0 = <&sound_0_pins>;
++ pinctrl-names = "default";
++
++ simple-audio-card,format = "left_j";
++ simple-audio-card,name = "pcm3168a";
++
++ simple-audio-card,bitclock-master = <&sound_ext_master>;
++ simple-audio-card,frame-master = <&sound_ext_master>;
++ sound_ext_master: simple-audio-card,cpu@0 {
++ sound-dai = <&rcar_sound 0>;
++ dai-tdm-slot-num = <8>;
++ dai-tdm-slot-width = <32>;
++ };
++
++ simple-audio-card,codec@0 {
++ sound-dai = <&pcm3168a>;
++ dai-tdm-slot-num = <8>;
++ dai-tdm-slot-width = <32>;
++ system-clock-frequency = <24576000>;
++ };
++ };
++
++ sound_onboard: sound@1 {
++ compatible = "simple-audio-card";
++ pinctrl-0 = <&sound_1_pins>;
++ pinctrl-names = "default";
++
++ simple-audio-card,format = "i2s";
++ simple-audio-card,name = "ak464x";
++
++ sndcpu_0: simple-audio-card,cpu@1 {
++ sound-dai = <&rcar_sound 1>;
++ };
++
++ sndcodec_0: simple-audio-card,codec@1 {
++ sound-dai = <&ak4642>;
++ system-clock-frequency = <11289600>;
++ };
++ };
++
++ sound_radio: sound@2 {
++ compatible = "simple-audio-card";
++ pinctrl-0 = <&sound_2_pins>;
++ pinctrl-names = "default";
++
++ simple-audio-card,format = "i2s";
++ simple-audio-card,name = "radio";
++
++ simple-audio-card,bitclock-master = <&sound_radio_master>;
++ simple-audio-card,frame-master = <&sound_radio_master>;
++ simple-audio-card,cpu@2 {
++ sound-dai = <&rcar_sound 2>;
++ };
++
++ sound_radio_master: simple-audio-card,codec@2 {
++ sound-dai = <&radio>;
++ system-clock-frequency = <12288000>;
++ };
++ };
++
++ hdmi_transmitter: adv7511 {
++ compatible = "adi,adv7511";
++ gpios = <&gpio3 29 GPIO_ACTIVE_LOW>;
++
++ adi,input-style = <0x02>;
++ adi,input-id = <0x00>;
++ adi,input-color-depth = <0x03>;
++ adi,sync-pulse = <0x03>;
++ adi,bit-justification = <0x01>;
++ adi,up-conversion = <0x00>;
++ adi,timing-generation-sequence = <0x00>;
++ adi,vsync-polarity = <0x02>;
++ adi,hsync-polarity = <0x02>;
++ adi,clock-delay = <0x03>;
++ };
++
++ usbhs_udc {
++ gpios = <&gpio5 31 GPIO_ACTIVE_HIGH>;
++ };
++
++ i2c0_gpio0: i2c@0 {
++ status = "ok";
++ clock-frequency = <400000>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ compatible = "i2c-gpio";
++ gpios = <&gpio6 9 GPIO_ACTIVE_HIGH /* sda */
++ &gpio7 2 GPIO_ACTIVE_HIGH /* scl */
++ >;
++ i2c-gpio,delay-us = <50>;
++
++ maxim_setup@10 {
++ compatible = "maxim,max9272-ov10635-setup";
++ reg = <0x10>; /* fake address */
++ gpios = <&gpio4 28 GPIO_ACTIVE_HIGH>;
++ };
++ };
++
++ cpld_gpio: cpld {
++ compatible = "porter-cpld-gpio";
++ #gpio-cells = <2>;
++
++ porter-cpld-gpio,data-gpio = <&gpio4 18 GPIO_ACTIVE_HIGH>;
++ porter-cpld-gpio,ptrc-gpio = <&gpio4 15 GPIO_ACTIVE_HIGH>;
++ porter-cpld-gpio,bufc-gpio = <&gpio7 5 GPIO_ACTIVE_HIGH>;
++ };
++
++ i2c0_gpio1: i2c@1 {
++ status = "ok";
++ clock-frequency = <400000>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++ compatible = "i2c-gpio";
++ gpios = <&gpio4 31 GPIO_ACTIVE_HIGH /* sda */
++ &gpio1 24 GPIO_ACTIVE_HIGH /* scl */
++ >;
++ i2c-gpio,delay-us = <2>; /* ~100 KHz */
++ };
++
++ spi_gpio: spi@0 {
++ compatible = "spi-gpio";
++ status = "okay";
++ gpio-sck = <&gpio4 30 0>;
++ gpio-mosi = <&gpio2 30 0>;
++ gpio-miso = <&gpio7 19 0>;
++ cs-gpios = <&gpio2 18 0>;
++ num-chipselects = <1>;
++ #address-cells = <1>;
++ #size-cells = <0>;
++
++ si4689: si4689@0 {
++ compatible = "si,468x";
++ reg = <0>;
++ spi-max-frequency = <10000000>;
++ };
++ };
++
++ radio: si468x@0 {
++ compatible = "si,si468x-pcm";
++ status = "okay";
++
++ #sound-dai-cells = <0>;
++ };
++};
++
++&extal_clk {
++ clock-frequency = <20000000>;
++};
++
++&pfc {
++ pinctrl-0 = <&du_pins &usb0_pins &usb1_pins &vin0_pins &vin2_pins>;
++ pinctrl-names = "default";
++
++ du_pins: du {
++ renesas,groups = "du_rgb666", "du_sync", "du_clk_out_0";
++ renesas,function = "du";
++ };
++
++ scif0_pins: serial6 {
++ renesas,groups = "scif0_data_d";
++ renesas,function = "scif0";
++ };
++
++ scif1_pins: serial9 {
++ renesas,groups = "scif1_data_d";
++ renesas,function = "scif1";
++ };
++
++ scifa4_pins: serial7 {
++ renesas,groups = "scifa4_data";
++ renesas,function = "scifa4";
++ };
++
++ scifb2_pins: serial8 {
++ renesas,groups = "scifb2_data", "scifb2_ctrl";
++ renesas,function = "scifb2";
++ };
++
++ i2c1_pins: i2c1 {
++ renesas,groups = "i2c1_e";
++ renesas,function = "i2c1";
++ };
++
++ i2c2_pins: i2c2 {
++ renesas,groups = "i2c2";
++ renesas,function = "i2c2";
++ };
++
++ i2c4_pins: i2c4 {
++ renesas,groups = "i2c4_c";
++ renesas,function = "i2c4";
++ };
++
++ ether_pins: ether {
++ renesas,groups = "eth_link", "eth_mdio", "eth_rmii";
++ renesas,function = "eth";
++ };
++
++ avb_pins: avb {
++ renesas,groups = "avb_mdio", "avb_gmii";
++ renesas,function = "avb";
++ };
++
++ mlb3_pins: mlp {
++ renesas,groups = "mlb3pin";
++ renesas,function = "mlb3pin";
++ };
++
++ phy1_pins: phy1 {
++ renesas,groups = "intc_irq0";
++ renesas,function = "intc";
++ };
++
++ sdhi0_pins: sd0 {
++ renesas,groups = "sdhi0_data4", "sdhi0_ctrl";
++ renesas,function = "sdhi0";
++ };
++
++ sdhi2_pins: sd2 {
++ renesas,groups = "sdhi2_data4", "sdhi2_ctrl";
++ renesas,function = "sdhi2";
++ };
++
++ qspi_pins: spi0 {
++ renesas,groups = "qspi_ctrl", "qspi_data4";
++ renesas,function = "qspi";
++ };
++
++ msiof0_pins: spi1 {
++ renesas,groups = "msiof0_clk", "msiof0_sync", "msiof0_rx",
++ "msiof0_tx";
++ renesas,function = "msiof0";
++ };
++
++ sound_clk_pins: sound_clk {
++ renesas,groups = "audio_clk_b_b";
++ renesas,function = "audio_clk";
++ };
++
++ sound_0_pins: sound1 {
++ renesas,groups = "ssi34_ctrl", "ssi3_data", "ssi4_data";
++ renesas,function = "ssi";
++ };
++
++ sound_1_pins: sound0 {
++ renesas,groups = "ssi0129_ctrl", "ssi0_data", "ssi1_data";
++ renesas,function = "ssi";
++ };
++
++ sound_2_pins: sound2 {
++ renesas,groups = "ssi5_ctrl", "ssi5_data";
++ renesas,function = "ssi";
++ };
++
++ usb0_pins: usb0 {
++ renesas,groups = "usb0";
++ renesas,function = "usb0";
++ };
++
++ usb1_pins: usb1 {
++ renesas,groups = "usb1";
++ renesas,function = "usb1";
++ };
++
++ vin0_pins: vin0 {
++ renesas,groups = "vin0_data8", "vin0_clk";
++ renesas,function = "vin0";
++ };
++
++ vin2_pins: vin2 {
++ renesas,groups = "vin2_data8", "vin2_clk";
++ renesas,function = "vin2";
++ };
++
++ can0_pins: can0 {
++ renesas,groups = "can0_data_b";
++ renesas,function = "can0";
++ };
++};
++
++&ether {
++ pinctrl-0 = <&ether_pins &phy1_pins>;
++ pinctrl-names = "default";
++
++ phy-handle = <&phy1>;
++ renesas,ether-link-active-low;
++ status = "disabled";
++
++ phy1: ethernet-phy@1 {
++ reg = <1>;
++ interrupt-parent = <&irqc0>;
++ interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
++ micrel,led-mode = <1>;
++ };
++};
++
++&can0 {
++ pinctrl-0 = <&can0_pins>;
++ pinctrl-names = "default";
++ renesas,can-clock-select = <0x0>;
++ status = "okay";
++};
++
++&avb {
++ pinctrl-0 = <&avb_pins>;
++ pinctrl-names = "default";
++
++ renesas,no-ether-link;
++ renesas,phy = <0>;
++ renesas,mii-lite-ignore-pins = <&gpio5 25 0
++ &gpio5 24 0
++ &gpio5 23 0
++ &gpio5 22 0
++ &gpio5 7 0
++ &gpio5 8 0
++ &gpio5 6 0
++ &gpio5 5 0
++ &gpio5 4 0
++ &gpio5 17 0
++ &gpio5 11 0
++ &gpio5 30 0
++ &gpio5 27 0>;
++ phy-int-gpio = <&gpio5 16 GPIO_ACTIVE_LOW>;
++ status = "okay";
++};
++
++&sata0 {
++ status = "okay";
++};
++
++&scif0 {
++ pinctrl-0 = <&scif0_pins>;
++ pinctrl-names = "default";
++
++ status = "okay";
++};
++
++&scif1 {
++ pinctrl-0 = <&scif1_pins>;
++ pinctrl-names = "default";
++
++ status = "okay";
++};
++
++&scifa4 {
++ pinctrl-0 = <&scifa4_pins>;
++ pinctrl-names = "default";
++
++ status = "okay";
++};
++
++&scifb2 {
++ pinctrl-0 = <&scifb2_pins>;
++ pinctrl-names = "default";
++ ctsrts;
++
++ status = "okay";
++};
++
++&sdhi0 {
++ pinctrl-0 = <&sdhi0_pins>;
++ pinctrl-names = "default";
++
++ vmmc-supply = <&wlan_en_reg>;
++ vqmmc-supply = <&vccq_sdhi0>;
++
++ keep-power-in-suspend;
++ enable-sdio-wakeup;
++ non-removable;
++ cap-power-off-card;
++ status = "okay";
++
++ #address-cells = <1>;
++ #size-cells = <0>;
++ wlcore: wlcore@0 {
++ compatible = "ti,wl1837";
++ reg = <2>;
++ /* GP6_12 */
++ interrupt-parent = <&gpio6>;
++ interrupts = <12 IRQ_TYPE_EDGE_RISING>;
++ platform-quirks = <1>;
++ };
++};
++
++&sdhi2 {
++ pinctrl-0 = <&sdhi2_pins>;
++ pinctrl-names = "default";
++
++ vmmc-supply = <&vcc_sdhi2>;
++ vqmmc-supply = <&vccq_sdhi2>;
++ cd-gpios = <&gpio6 22 GPIO_ACTIVE_LOW>;
++ toshiba,mmc-wrprotect-disable;
++ status = "okay";
++};
++
++&qspi {
++ pinctrl-0 = <&qspi_pins>;
++ pinctrl-names = "default";
++
++ status = "okay";
++
++ flash: flash@0 {
++ #address-cells = <1>;
++ #size-cells = <1>;
++ compatible = "spansion,s25fl512s";
++ reg = <0>;
++ spi-max-frequency = <30000000>;
++ spi-tx-bus-width = <4>;
++ spi-rx-bus-width = <4>;
++ m25p,fast-read;
++ spi-cpol;
++ spi-cpha;
++
++ partition@0 {
++ label = "loader";
++ reg = <0x00000000 0x00040000>;
++ read-only;
++ };
++ partition@40000 {
++ label = "user";
++ reg = <0x00040000 0x00400000>;
++ read-only;
++ };
++ partition@440000 {
++ label = "flash";
++ reg = <0x00440000 0x03bc0000>;
++ };
++ };
++};
++
++&msiof0 {
++ pinctrl-0 = <&msiof0_pins>;
++ pinctrl-names = "default";
++
++ status = "okay";
++};
++
++&i2c1 {
++ pinctrl-0 = <&i2c1_pins>;
++ pinctrl-names = "default";
++
++ status = "okay";
++ clock-frequency = <400000>;
++};
++
++&i2c2 {
++ pinctrl-0 = <&i2c2_pins>;
++ pinctrl-names = "default";
++
++ status = "okay";
++ clock-frequency = <400000>;
++
++ eeprom@50 {
++ compatible = "renesas,24c02";
++ reg = <0x50>;
++ pagesize = <16>;
++ };
++
++ ak4642: sound-codec@12 {
++ #sound-dai-cells = <0>;
++ compatible = "asahi-kasei,ak4642";
++ reg = <0x12>;
++ };
++};
++
++&i2c4 {
++ pinctrl-0 = <&i2c4_pins>;
++ pinctrl-names = "default";
++
++ status = "okay";
++ clock-frequency = <400000>;
++};
++
++&i2c5 {
++ status = "okay";
++ clock-frequency = <400000>;
++
++ lsm9ds0_acc_mag@1d {
++ compatible = "st,lsm9ds0_acc_mag";
++ reg = <0x1d>;
++ };
++
++ lsm9ds0_gyr@6b {
++ compatible = "st,lsm9ds0_gyr";
++ reg = <0x6b>;
++ };
++
++ pcm3168a: audio-codec@44 {
++ #sound-dai-cells = <0>;
++ compatible = "ti,pcm3168a";
++ reg = <0x44>;
++ clocks = <&snd_clk>;
++ clock-names = "scki";
++ tdm;
++ VDD1-supply = <&codec_en_reg>;
++ VDD2-supply = <&codec_en_reg>;
++ VCCAD1-supply = <&codec_en_reg>;
++ VCCAD2-supply = <&codec_en_reg>;
++ VCCDA1-supply = <&amp_en_reg>;
++ VCCDA2-supply = <&amp_en_reg>;
++ };
++};
++
++&i2c6 {
++ status = "okay";
++ clock-frequency = <100000>;
++
++ vdd_dvfs: regulator@68 {
++ compatible = "diasemi,da9210";
++ reg = <0x68>;
++
++ regulator-min-microvolt = <1000000>;
++ regulator-max-microvolt = <1000000>;
++ regulator-boot-on;
++ regulator-always-on;
++ };
++};
++
++&mlp {
++ pinctrl-names = "default";
++ pinctrl-0 = <&mlb3_pins>;
++ status = "okay";
++};
++
++&pci0 {
++ status = "okay";
++ pinctrl-0 = <&usb0_pins>;
++ pinctrl-names = "default";
++};
++
++&pci1 {
++ status = "okay";
++ pinctrl-0 = <&usb1_pins>;
++ pinctrl-names = "default";
++};
++
++&pcie_bus_clk {
++ status = "okay";
++};
++
++&pciec {
++ status = "okay";
++};
++
++&cpu0 {
++ cpu0-supply = <&vdd_dvfs>;
++};
++
++&rcar_sound {
++ #sound-dai-cells = <1>;
++ pinctrl-0 = <&sound_clk_pins>;
++ pinctrl-names = "default";
++
++ status = "okay";
++
++ rcar_sound,dai {
++ dai0 {
++ playback = <&ssi3>;
++ capture = <&ssi4>;
++ };
++
++ dai1 {
++ playback = <&ssi0 &src0 &dvc0>;
++ capture = <&ssi1 &src1 &dvc1>;
++ };
++
++ dai2 {
++ capture = <&ssi5>;
++ };
++ };
++};
++
++&ssi1 {
++ shared-pin;
++};
++
++&ssi3 {
++ tdm-8ch;
++ shared-pin;
++};
++
++&ssi4 {
++ tdm-8ch;
++ shared-pin;
++};
++
++&audio_clk_b {
++ clock-frequency = <24576000>;
++};
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0100-Porter-ext01-add-dummy-regulator-to-select-48000-sou.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0100-Porter-ext01-add-dummy-regulator-to-select-48000-sou.patch
new file mode 100644
index 0000000..36313c3
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter/0100-Porter-ext01-add-dummy-regulator-to-select-48000-sou.patch
@@ -0,0 +1,53 @@
+From f682ff4fc2efe30131e683acd2924b7f6ce72735 Mon Sep 17 00:00:00 2001
+From: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+Date: Thu, 2 Jun 2016 13:44:14 +0300
+Subject: [PATCH] Porter-ext01: add dummy regulator to select 48000 sound
+ clock
+
+
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ arch/arm/boot/dts/r8a7791-porter-ext01.dts | 21 +++++++++++++++++++--
+ 1 file changed, 19 insertions(+), 2 deletions(-)
+
+diff --git a/arch/arm/boot/dts/r8a7791-porter-ext01.dts b/arch/arm/boot/dts/r8a7791-porter-ext01.dts
+index d115b36..62c6417 100644
+--- a/arch/arm/boot/dts/r8a7791-porter-ext01.dts
++++ b/arch/arm/boot/dts/r8a7791-porter-ext01.dts
+@@ -151,7 +151,24 @@
+ enable-active-high;
+ };
+
+- wlan_en_reg: regulator@6 {
++ codec_en_reg_2: regulator@6 {
++ compatible = "regulator-fixed";
++ regulator-name = "audio-clock-set";
++ regulator-min-microvolt = <3300000>;
++ regulator-max-microvolt = <3300000>;
++
++ /*
++ * This regulator will just set clock switch to
++ * select 24.576MHz as audio_clk_b
++ * TODO: add clock switch support
++ */
++ gpio = <&cpld_gpio 7 0>;
++
++ startup-delay-us = <0>;
++ enable-active-high;
++ };
++
++ wlan_en_reg: regulator@7 {
+ compatible = "regulator-fixed";
+ regulator-name = "wlan-en-regulator";
+ regulator-min-microvolt = <1800000>;
+@@ -676,7 +693,7 @@
+ clock-names = "scki";
+ tdm;
+ VDD1-supply = <&codec_en_reg>;
+- VDD2-supply = <&codec_en_reg>;
++ VDD2-supply = <&codec_en_reg_2>;
+ VCCAD1-supply = <&codec_en_reg>;
+ VCCAD2-supply = <&codec_en_reg>;
+ VCCDA1-supply = <&amp_en_reg>;
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter_ext01.cfg b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter_ext01.cfg
new file mode 100644
index 0000000..b9a6fe7
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/porter_ext01.cfg
@@ -0,0 +1,46 @@
+CONFIG_WIRELESS_EXT=y
+CONFIG_WEXT_CORE=y
+CONFIG_WEXT_PROC=y
+CONFIG_WEXT_SPY=y
+CONFIG_WEXT_PRIV=y
+CONFIG_RFKILL=y
+CONFIG_LIB80211=y
+CONFIG_LIB80211_CRYPT_WEP=y
+CONFIG_LIB80211_CRYPT_CCMP=y
+CONFIG_LIB80211_CRYPT_TKIP=y
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_AX8817X=y
+CONFIG_USB_NET_AX88179_178A=y
+CONFIG_USB_NET_CDCETHER=y
+CONFIG_USB_NET_CDC_NCM=y
+CONFIG_USB_NET_NET1080=y
+CONFIG_USB_NET_CDC_SUBSET=y
+CONFIG_USB_BELKIN=y
+CONFIG_USB_ARMLINUX=y
+CONFIG_USB_NET_ZAURUS=y
+CONFIG_HOSTAP=y
+CONFIG_GPIO_PORTER_CPLD=y
+CONFIG_INPUT_LSM9DS0=y
+CONFIG_BT=n
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_RNG=y
+CONFIG_CRYPTO_USER=y
+CONFIG_CRYPTO_GF128MUL=y
+CONFIG_CRYPTO_NULL=y
+CONFIG_CRYPTO_PCRYPT=y
+CONFIG_CRYPTO_GCM=y
+CONFIG_CRYPTO_SEQIV=y
+CONFIG_CRYPTO_CTR=y
+CONFIG_CRYPTO_CMAC=y
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_GHASH=y
+CONFIG_CRYPTO_MICHAEL_MIC=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_SHA256=y
+CONFIG_IIO=y
+CONFIG_TI_ADS111X=y
+CONFIG_SOC_CAMERA_OV10635=y
+CONFIG_SOC_CAMERA_MAX9272_OV10635=y
+CONFIG_SND_SOC_SI468X=y
+CONFIG_SND_SOC_PCM3168A=y
+CONFIG_SND_SOC_PCM3168A_I2C=y
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0014-serial-sh-sci-Break-out-default-CTS-RTS-pin-setup.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0014-serial-sh-sci-Break-out-default-CTS-RTS-pin-setup.patch
new file mode 100644
index 0000000..6278b9a
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0014-serial-sh-sci-Break-out-default-CTS-RTS-pin-setup.patch
@@ -0,0 +1,86 @@
+From 102c206ea8ee28cf677061aa5e4ce509f776a924 Mon Sep 17 00:00:00 2001
+From: Magnus Damm <damm+renesas@opensource.se>
+Date: Thu, 19 Mar 2015 10:49:43 +0900
+Subject: [PATCH 14/18] serial: sh-sci: Break out default CTS/RTS pin setup
+
+Break out CTS/RTS pin setup for the default case. We only
+care about those pins in case SCIx_HAVE_RTSCTS is set.
+
+Signed-off-by: Magnus Damm <damm+renesas@opensource.se>
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/tty/serial/sh-sci.c | 45 +++++++++++++++++++++++++++++--------------
+ 1 file changed, 31 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
+index 526409d..8465cfd 100644
+--- a/drivers/tty/serial/sh-sci.c
++++ b/drivers/tty/serial/sh-sci.c
+@@ -522,10 +522,29 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c)
+ }
+ #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */
+
+-static void sci_init_pins(struct uart_port *port, unsigned int cflag)
++static void sci_init_ctsrts_default(struct uart_port *port, bool hwflow_enabled)
+ {
+ struct sci_port *s = to_sci_port(port);
+ struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR;
++ unsigned short status;
++
++ /* If no SCSPTR register exists then skip. Same if hardware flow
++ * control has been enabled, in such case SCFCR.MCE will be set
++ * and the SCSPTR configuration is assumed to be overridden.
++ */
++ if (!reg->size || hwflow_enabled)
++ return;
++
++ status = serial_port_in(port, SCSPTR);
++ status &= ~SCSPTR_CTSIO;
++ status |= SCSPTR_RTSIO;
++ serial_port_out(port, SCSPTR, status); /* Set RTS = 1 */
++}
++
++static void sci_init_pins(struct uart_port *port, unsigned int cflag)
++{
++ struct sci_port *s = to_sci_port(port);
++ bool hwflow_enabled = cflag & CRTSCTS;
+
+ /*
+ * Use port-specific handler if provided.
+@@ -535,22 +554,20 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag)
+ return;
+ }
+
+- /*
+- * For the generic path SCSPTR is necessary. Bail out if that's
+- * unavailable, too.
++ /* SCIF hardware with RTS/CTS support needs special setup below.
++ *
++ * Please note that if RTS/CTS is available for the hardware
++ * platform depends on the particular SCIF channel on a certain
++ * SoC, and how this channel has been hooked up on the actual board.
++ *
++ * If the RTS/CTS signals will be used or not depends on what user
++ * space requests. In case RTS/CTS is available but not requested
++ * by user space we still need to configure the pins somehow.
+ */
+- if (!reg->size)
++ if (!(s->cfg->capabilities & SCIx_HAVE_RTSCTS))
+ return;
+
+- if ((s->cfg->capabilities & SCIx_HAVE_RTSCTS) &&
+- ((!(cflag & CRTSCTS)))) {
+- unsigned short status;
+-
+- status = serial_port_in(port, SCSPTR);
+- status &= ~SCSPTR_CTSIO;
+- status |= SCSPTR_RTSIO;
+- serial_port_out(port, SCSPTR, status); /* Set RTS = 1 */
+- }
++ sci_init_ctsrts_default(port, hwflow_enabled);
+ }
+
+ static int sci_txfill(struct uart_port *port)
+--
+1.7.10.4
+
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0015-serial-sh-sci-Fix-default-RTS-handling.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0015-serial-sh-sci-Fix-default-RTS-handling.patch
new file mode 100644
index 0000000..1609d84
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0015-serial-sh-sci-Fix-default-RTS-handling.patch
@@ -0,0 +1,50 @@
+From dfe822a2848987263b7ac30b1c20e89d86f63caa Mon Sep 17 00:00:00 2001
+From: Magnus Damm <damm+renesas@opensource.se>
+Date: Thu, 19 Mar 2015 10:49:53 +0900
+Subject: [PATCH 15/18] serial: sh-sci: Fix default RTS handling
+
+Fix the default SCIF handling in case CTS/RTS is available
+on the target board but disabled by user space. Without
+this patch the RTS output value is not set.
+
+Signed-off-by: Magnus Damm <damm+renesas@opensource.se>
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/tty/serial/sh-sci.c | 7 +++++--
+ include/linux/serial_sci.h | 1 +
+ 2 files changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
+index 8465cfd..64bf5b6 100644
+--- a/drivers/tty/serial/sh-sci.c
++++ b/drivers/tty/serial/sh-sci.c
+@@ -535,10 +535,13 @@ static void sci_init_ctsrts_default(struct uart_port *port, bool hwflow_enabled)
+ if (!reg->size || hwflow_enabled)
+ return;
+
++ /* Setup CTS/RTS in the case hardware flow is disabled by user space.
++ * The CTS signal is setup as input and RTS as output with value 0.
++ */
+ status = serial_port_in(port, SCSPTR);
+- status &= ~SCSPTR_CTSIO;
++ status &= ~(SCSPTR_CTSIO | SCSPTR_RTSDT);
+ status |= SCSPTR_RTSIO;
+- serial_port_out(port, SCSPTR, status); /* Set RTS = 1 */
++ serial_port_out(port, SCSPTR, status); /* Set RTS = 0 */
+ }
+
+ static void sci_init_pins(struct uart_port *port, unsigned int cflag)
+diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h
+index e730b58..39884de 100644
+--- a/include/linux/serial_sci.h
++++ b/include/linux/serial_sci.h
+@@ -57,6 +57,7 @@
+
+ /* SCSPTR (Serial Port Register), optional */
+ #define SCSPTR_RTSIO (1 << 7) /* Serial Port RTS Pin Input/Output */
++#define SCSPTR_RTSDT (1 << 6) /* Serial Port RTS Pin Data */
+ #define SCSPTR_CTSIO (1 << 5) /* Serial Port CTS Pin Input/Output */
+ #define SCSPTR_SPB2IO (1 << 1) /* Serial Port Break Input/Output */
+ #define SCSPTR_SPB2DT (1 << 0) /* Serial Port Break Data */
+--
+1.7.10.4
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0016-serial-sh-sci-Expose-default-CTS-pin.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0016-serial-sh-sci-Expose-default-CTS-pin.patch
new file mode 100644
index 0000000..dcc6134
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0016-serial-sh-sci-Expose-default-CTS-pin.patch
@@ -0,0 +1,79 @@
+From f7d7ddb2a3ad4c20dbbd7cbabf56bca4c35b8368 Mon Sep 17 00:00:00 2001
+From: Magnus Damm <damm+renesas@opensource.se>
+Date: Thu, 19 Mar 2015 10:50:03 +0900
+Subject: [PATCH 16/18] serial: sh-sci: Expose default CTS pin
+
+Expose the CTS pin to the ->get_mctrl() callback for the
+default case. Without this patch the serial core can not
+retrieve the CTS pin state from the SCIF driver.
+
+Signed-off-by: Magnus Damm <damm+renesas@opensource.se>
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/tty/serial/sh-sci.c | 24 ++++++++++++++++++++++--
+ include/linux/serial_sci.h | 1 +
+ 2 files changed, 23 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
+index 64bf5b6..4f82fcc 100644
+--- a/drivers/tty/serial/sh-sci.c
++++ b/drivers/tty/serial/sh-sci.c
+@@ -522,6 +522,17 @@ static void sci_poll_put_char(struct uart_port *port, unsigned char c)
+ }
+ #endif /* CONFIG_CONSOLE_POLL || CONFIG_SERIAL_SH_SCI_CONSOLE */
+
++static bool sci_cts_asserted_default(struct uart_port *port)
++{
++ struct sci_port *s = to_sci_port(port);
++ struct plat_sci_reg *reg = sci_regmap[s->cfg->regtype] + SCSPTR;
++
++ if (reg->size)
++ return !(serial_port_in(port, SCSPTR) & SCSPTR_CTSDT);
++
++ return true;
++}
++
+ static void sci_init_ctsrts_default(struct uart_port *port, bool hwflow_enabled)
+ {
+ struct sci_port *s = to_sci_port(port);
+@@ -1280,14 +1291,23 @@ static void sci_set_mctrl(struct uart_port *port, unsigned int mctrl)
+
+ static unsigned int sci_get_mctrl(struct uart_port *port)
+ {
++ unsigned int mctrl;
++ struct sci_port *s = to_sci_port(port);
++ bool cts_asserted = false;
++
+ /*
+ * CTS/RTS is handled in hardware when supported, while nothing
+ * else is wired up. Keep it simple and simply assert DSR/CAR.
+ */
+- unsigned int mctrl = TIOCM_DSR | TIOCM_CAR;
++ mctrl = TIOCM_DSR | TIOCM_CAR;
++
+ if (port->type == PORT_HSCIF)
+ mctrl |= TIOCM_CTS;
+- return mctrl;
++
++ if (s->cfg->capabilities & SCIx_HAVE_RTSCTS)
++ cts_asserted = sci_cts_asserted_default(port);
++
++ return mctrl | (cts_asserted ? TIOCM_CTS : 0);
+ }
+
+ #ifdef CONFIG_SERIAL_SH_SCI_DMA
+diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h
+index 39884de..d61096e 100644
+--- a/include/linux/serial_sci.h
++++ b/include/linux/serial_sci.h
+@@ -59,6 +59,7 @@
+ #define SCSPTR_RTSIO (1 << 7) /* Serial Port RTS Pin Input/Output */
+ #define SCSPTR_RTSDT (1 << 6) /* Serial Port RTS Pin Data */
+ #define SCSPTR_CTSIO (1 << 5) /* Serial Port CTS Pin Input/Output */
++#define SCSPTR_CTSDT (1 << 4) /* Serial Port CTS Pin Data */
+ #define SCSPTR_SPB2IO (1 << 1) /* Serial Port Break Input/Output */
+ #define SCSPTR_SPB2DT (1 << 0) /* Serial Port Break Data */
+
+--
+1.7.10.4
+
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0017-serial-sh-sci-Add-SCIFA-SCIFB-CTS-RTS-pin-setup.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0017-serial-sh-sci-Add-SCIFA-SCIFB-CTS-RTS-pin-setup.patch
new file mode 100644
index 0000000..01c9bf5
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0017-serial-sh-sci-Add-SCIFA-SCIFB-CTS-RTS-pin-setup.patch
@@ -0,0 +1,199 @@
+From 8b89b0c00e23770dab98ed0153fa1c8e5c999194 Mon Sep 17 00:00:00 2001
+From: Magnus Damm <damm+renesas@opensource.se>
+Date: Thu, 19 Mar 2015 10:50:13 +0900
+Subject: [PATCH 17/18] serial: sh-sci: Add SCIFA/SCIFB CTS/RTS pin setup
+
+Add SCIFA/SCIFB pin setup code for CTS/RTS pins to handle
+both cases of hardware flow control enabled or disabled.
+
+Signed-off-by: Magnus Damm <damm+renesas@opensource.se>
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/tty/serial/sh-sci.c | 59 ++++++++++++++++++++++++++++++++++++++++++-
+ include/linux/serial_sci.h | 9 +++++++
+ 2 files changed, 67 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
+index 4f82fcc..52fcb41 100644
+--- a/drivers/tty/serial/sh-sci.c
++++ b/drivers/tty/serial/sh-sci.c
+@@ -181,6 +181,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
+ [SCSPTR] = sci_reg_invalid,
+ [SCLSR] = sci_reg_invalid,
+ [HSSRR] = sci_reg_invalid,
++ [SCPCR] = sci_reg_invalid,
++ [SCPDR] = sci_reg_invalid,
+ },
+
+ /*
+@@ -201,6 +203,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
+ [SCSPTR] = sci_reg_invalid,
+ [SCLSR] = sci_reg_invalid,
+ [HSSRR] = sci_reg_invalid,
++ [SCPCR] = sci_reg_invalid,
++ [SCPDR] = sci_reg_invalid,
+ },
+
+ /*
+@@ -220,6 +224,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
+ [SCSPTR] = sci_reg_invalid,
+ [SCLSR] = sci_reg_invalid,
+ [HSSRR] = sci_reg_invalid,
++ [SCPCR] = { 0x30, 16 },
++ [SCPDR] = { 0x34, 16 },
+ },
+
+ /*
+@@ -239,6 +245,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
+ [SCSPTR] = sci_reg_invalid,
+ [SCLSR] = sci_reg_invalid,
+ [HSSRR] = sci_reg_invalid,
++ [SCPCR] = { 0x30, 16 },
++ [SCPDR] = { 0x34, 16 },
+ },
+
+ /*
+@@ -259,6 +267,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
+ [SCSPTR] = { 0x20, 16 },
+ [SCLSR] = { 0x24, 16 },
+ [HSSRR] = sci_reg_invalid,
++ [SCPCR] = sci_reg_invalid,
++ [SCPDR] = sci_reg_invalid,
+ },
+
+ /*
+@@ -278,6 +288,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
+ [SCSPTR] = sci_reg_invalid,
+ [SCLSR] = sci_reg_invalid,
+ [HSSRR] = sci_reg_invalid,
++ [SCPCR] = sci_reg_invalid,
++ [SCPDR] = sci_reg_invalid,
+ },
+
+ /*
+@@ -297,6 +309,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
+ [SCSPTR] = { 0x20, 16 },
+ [SCLSR] = { 0x24, 16 },
+ [HSSRR] = sci_reg_invalid,
++ [SCPCR] = sci_reg_invalid,
++ [SCPDR] = sci_reg_invalid,
+ },
+
+ /*
+@@ -316,6 +330,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
+ [SCSPTR] = { 0x20, 16 },
+ [SCLSR] = { 0x24, 16 },
+ [HSSRR] = { 0x40, 16 },
++ [SCPCR] = sci_reg_invalid,
++ [SCPDR] = sci_reg_invalid,
+ },
+
+ /*
+@@ -336,6 +352,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
+ [SCSPTR] = sci_reg_invalid,
+ [SCLSR] = { 0x24, 16 },
+ [HSSRR] = sci_reg_invalid,
++ [SCPCR] = sci_reg_invalid,
++ [SCPDR] = sci_reg_invalid,
+ },
+
+ /*
+@@ -356,6 +374,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
+ [SCSPTR] = { 0x24, 16 },
+ [SCLSR] = { 0x28, 16 },
+ [HSSRR] = sci_reg_invalid,
++ [SCPCR] = sci_reg_invalid,
++ [SCPDR] = sci_reg_invalid,
+ },
+
+ /*
+@@ -376,6 +396,8 @@ static struct plat_sci_reg sci_regmap[SCIx_NR_REGTYPES][SCIx_NR_REGS] = {
+ [SCSPTR] = sci_reg_invalid,
+ [SCLSR] = sci_reg_invalid,
+ [HSSRR] = sci_reg_invalid,
++ [SCPCR] = sci_reg_invalid,
++ [SCPDR] = sci_reg_invalid,
+ },
+ };
+
+@@ -555,6 +577,34 @@ static void sci_init_ctsrts_default(struct uart_port *port, bool hwflow_enabled)
+ serial_port_out(port, SCSPTR, status); /* Set RTS = 0 */
+ }
+
++static void sci_init_ctsrts_scifab(struct uart_port *port, bool hwflow_enabled)
++{
++ unsigned short control, data;
++
++ /* SCIFA/SCIFB CTS/RTS pin configuration depends on user space.
++ *
++ * In case of CTS - (SCPDR.CTSD is always accessible):
++ * - Hardware flow control enabled: "CTS pin function"
++ * - Hardware flow control disabled: "Input port"
++ *
++ * In case of RTS:
++ * - Hardware flow control enabled: "RTS pin function"
++ * - Hardware flow control disabled: "Output port" with value 0
++ */
++ control = serial_port_in(port, SCPCR);
++ data = serial_port_in(port, SCPDR);
++
++ if (hwflow_enabled) {
++ control &= ~(SCPCR_RTSC | SCPCR_CTSC);
++ } else {
++ control |= SCPCR_RTSC | SCPCR_CTSC;
++ data &= ~SCPDR_RTSD;
++ }
++
++ serial_port_out(port, SCPDR, data);
++ serial_port_out(port, SCPCR, control);
++}
++
+ static void sci_init_pins(struct uart_port *port, unsigned int cflag)
+ {
+ struct sci_port *s = to_sci_port(port);
+@@ -581,7 +631,14 @@ static void sci_init_pins(struct uart_port *port, unsigned int cflag)
+ if (!(s->cfg->capabilities & SCIx_HAVE_RTSCTS))
+ return;
+
+- sci_init_ctsrts_default(port, hwflow_enabled);
++ switch (s->cfg->type) {
++ case PORT_SCIFA:
++ case PORT_SCIFB:
++ sci_init_ctsrts_scifab(port, hwflow_enabled);
++ break;
++ default:
++ sci_init_ctsrts_default(port, hwflow_enabled);
++ }
+ }
+
+ static int sci_txfill(struct uart_port *port)
+diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h
+index d61096e..04f0066a 100644
+--- a/include/linux/serial_sci.h
++++ b/include/linux/serial_sci.h
+@@ -66,6 +66,13 @@
+ /* HSSRR HSCIF */
+ #define HSCIF_SRE 0x8000 /* Sampling Rate Register Enable */
+
++/* SCPCR (Serial Port Control Register) */
++#define SCPCR_RTSC (1 << 4) /* Serial Port RTS Pin / Output Pin */
++#define SCPCR_CTSC (1 << 3) /* Serial Port CTS Pin / Input Pin */
++
++/* SCPDR (Serial Port Data Register) */
++#define SCPDR_RTSD (1 << 4) /* Serial Port RTS Output Pin Data */
++
+ enum {
+ SCIx_PROBE_REGTYPE,
+
+@@ -102,6 +109,8 @@ enum {
+ SCRFDR, /* Receive FIFO Data Count Register */
+ SCSPTR, /* Serial Port Register */
+ HSSRR, /* Sampling Rate Register */
++ SCPCR, /* Serial Port Control Register */
++ SCPDR, /* Serial Port Data Register */
+
+ SCIx_NR_REGS,
+ };
+--
+1.7.10.4
+
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0018-serial-sh-sci-Expose-SCIFA-SCIFB-CTS-pin.patch b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0018-serial-sh-sci-Expose-SCIFA-SCIFB-CTS-pin.patch
new file mode 100644
index 0000000..860089b
--- /dev/null
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas/sh-sci/0018-serial-sh-sci-Expose-SCIFA-SCIFB-CTS-pin.patch
@@ -0,0 +1,65 @@
+From 6979eced0a733f619708391255a92a0f3de36b5f Mon Sep 17 00:00:00 2001
+From: Magnus Damm <damm+renesas@opensource.se>
+Date: Thu, 19 Mar 2015 10:50:23 +0900
+Subject: [PATCH 18/18] serial: sh-sci: Expose SCIFA/SCIFB CTS pin
+
+Expose CTS pin to serial core for the SCIFA/SCIFB case.
+
+Signed-off-by: Magnus Damm <damm+renesas@opensource.se>
+Signed-off-by: Andrey Gusakov <andrey.gusakov@cogentembedded.com>
+---
+ drivers/tty/serial/sh-sci.c | 18 +++++++++++++++---
+ include/linux/serial_sci.h | 1 +
+ 2 files changed, 16 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
+index 52fcb41..893f2f0 100644
+--- a/drivers/tty/serial/sh-sci.c
++++ b/drivers/tty/serial/sh-sci.c
+@@ -577,6 +577,11 @@ static void sci_init_ctsrts_default(struct uart_port *port, bool hwflow_enabled)
+ serial_port_out(port, SCSPTR, status); /* Set RTS = 0 */
+ }
+
++static bool sci_cts_asserted_scifab(struct uart_port *port)
++{
++ return !(serial_port_in(port, SCPDR) & SCPDR_CTSD);
++}
++
+ static void sci_init_ctsrts_scifab(struct uart_port *port, bool hwflow_enabled)
+ {
+ unsigned short control, data;
+@@ -1360,9 +1365,16 @@ static unsigned int sci_get_mctrl(struct uart_port *port)
+
+ if (port->type == PORT_HSCIF)
+ mctrl |= TIOCM_CTS;
+-
+- if (s->cfg->capabilities & SCIx_HAVE_RTSCTS)
+- cts_asserted = sci_cts_asserted_default(port);
++ if (s->cfg->capabilities & SCIx_HAVE_RTSCTS) {
++ switch (s->cfg->type) {
++ case PORT_SCIFA:
++ case PORT_SCIFB:
++ cts_asserted = sci_cts_asserted_scifab(port);
++ break;
++ default:
++ cts_asserted = sci_cts_asserted_default(port);
++ }
++ }
+
+ return mctrl | (cts_asserted ? TIOCM_CTS : 0);
+ }
+diff --git a/include/linux/serial_sci.h b/include/linux/serial_sci.h
+index 04f0066a..54fd078 100644
+--- a/include/linux/serial_sci.h
++++ b/include/linux/serial_sci.h
+@@ -72,6 +72,7 @@
+
+ /* SCPDR (Serial Port Data Register) */
+ #define SCPDR_RTSD (1 << 4) /* Serial Port RTS Output Pin Data */
++#define SCPDR_CTSD (1 << 3) /* Serial Port CTS Input Pin Data */
+
+ enum {
+ SCIx_PROBE_REGTYPE,
+--
+1.7.10.4
+
diff --git a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb
index f26443f..bd8817f 100644
--- a/meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb
+++ b/meta-rcar-gen2/recipes-kernel/linux/linux-renesas_3.10.bb
@@ -17,9 +17,19 @@ SRC_URI = "${RENESAS_BACKPORTS_URL};protocol=git;branch=bsp/v3.10.31-ltsi/rcar-g
file://0001-arm-koelsch-Add-vmalloc-384M-to-bootargs-of-DTS.patch \
file://0001-arm-alt-Add-vmalloc-384M-to-bootargs-of-DTS.patch \
file://0001-arm-gose-Add-vmalloc-384M-to-bootargs-of-DTS.patch \
+ file://0049-Add-lsm9ds0-acc-gyro-mag-driver.patch \
+ file://0051-TI-ADS111X-sigma-delta-ADC-driver.patch \
+ file://0051a-Cleanup-TI-ADS111x-ADC-driver.patch \
+ file://0059-crypto-aead-Add-crypto_aead_set_reqsize-helper.patch \
+ file://sh-sci/0014-serial-sh-sci-Break-out-default-CTS-RTS-pin-setup.patch \
+ file://sh-sci/0015-serial-sh-sci-Fix-default-RTS-handling.patch \
+ file://sh-sci/0016-serial-sh-sci-Expose-default-CTS-pin.patch \
+ file://sh-sci/0017-serial-sh-sci-Add-SCIFA-SCIFB-CTS-RTS-pin-setup.patch \
+ file://sh-sci/0018-serial-sh-sci-Expose-SCIFA-SCIFB-CTS-pin.patch \
"
SRC_URI_append_porter = " file://porter.cfg"
+SRC_URI_append_porter = '${@ " file://porter_ext01.cfg" if 'porter-ext01' in '${MACHINE_FEATURES}' else "" }'
SRC_URI_append_porter = " \
file://0001-kernel-Silk-board-support.patch \
@@ -54,6 +64,37 @@ SRC_URI_append_porter = " \
file://0022-fanotify-fix-notification-of-groups-with-inode-mount.patch \
"
+SRC_URI_append_porter = " \
+ file://porter/0002-Porter-adapt-max9272-ov10635-driver-for-porter-exp-b.patch \
+ file://porter/0003-Porter-add-LVDS-camera.patch \
+ file://porter/0004-GPIO-CPLD-gpio-extender-driver.patch \
+ file://porter/0012-regmap-Implemented-default-cache-sync-operation.patch \
+ file://porter/0013-regmap-cache-Don-t-attempt-to-sync-non-writeable-reg.patch \
+ file://porter/0014-ASoC-Add-SOC_DOUBLE_STS-macro.patch \
+ file://porter/0015-ASoC-pcm3168a-Add-binding-document-for-pcm3168a-code.patch \
+ file://porter/0016-ASoC-pcm3168a-Add-driver-for-pcm3168a-codec.patch \
+ file://porter/0017-ASoC-PCM3168A-add-TDM-modes.patch \
+ file://porter/0018-ASoC-PCM3168A-disable-16-bit-format.patch \
+ file://porter/0019-ASoC-PCM3168A-enable-on-power-on.patch \
+ file://porter/0020-ASoC-PCM3168A-merge-ADC-and-DAC-to-single-DAI.patch \
+ file://porter/0021-ASoC-PCM3168A-disable-PM.patch \
+ file://porter/0022-ASoC-fix-simple-card-do-not-respect-device-id.patch \
+ file://porter/0023-SPI-GPIO-get-master-fix.patch \
+ file://porter/0024-SPIDEV-extend-maximum-transfer-size.patch \
+ file://porter/0025-Radio-add-si468x-to-spidev.patch \
+ file://porter/0026-ASoC-add-dummy-Si468x-driver.patch \
+ file://porter/0027-ASoC-R-Car-initial-TDM-support.patch \
+ file://porter/0028-ASoC-rcar-correct-32bit-to-24-bit-sample-conv.patch \
+ file://porter/0029-ASoC-R-Car-fix-debug-output.patch \
+ file://porter/0030-R-Car-sound-disable-clock-hack.patch \
+ file://porter/0031-ASoC-R-car-SSI-fix-SSI-slave-mode-setup-while-TDM-an.patch \
+ file://porter/0032-mmc-Add-SDIO-function-devicetree-subnode-parsing.patch \
+ file://porter/0038-Porter-LVDS-display-LQ123K1LG03.patch \
+ file://porter/0060-Remove-delay-at-LVDS-camera-initialization.patch \
+ file://porter/0099-Porter-add-separate-dts-for-ext01-extension-board.patch \
+ file://porter/0100-Porter-ext01-add-dummy-regulator-to-select-48000-sou.patch \
+"
+
SRC_URI_append_silk = " \
file://0001-kernel-Silk-board-support.patch \
file://0002-silk-reference-Add-DRM_RCAR_DU_CONNECT_VSP-configura.patch \