summaryrefslogtreecommitdiff
path: root/drivers/input/misc
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/input/misc
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/input/misc')
-rw-r--r--drivers/input/misc/88pm80x_onkey.c168
-rw-r--r--drivers/input/misc/88pm860x_onkey.c170
-rw-r--r--drivers/input/misc/Kconfig776
-rw-r--r--drivers/input/misc/Makefile81
-rw-r--r--drivers/input/misc/ab8500-ponkey.c155
-rw-r--r--drivers/input/misc/ad714x-i2c.c123
-rw-r--r--drivers/input/misc/ad714x-spi.c130
-rw-r--r--drivers/input/misc/ad714x.c1261
-rw-r--r--drivers/input/misc/ad714x.h55
-rw-r--r--drivers/input/misc/adxl34x-i2c.c155
-rw-r--r--drivers/input/misc/adxl34x-spi.c136
-rw-r--r--drivers/input/misc/adxl34x.c914
-rw-r--r--drivers/input/misc/adxl34x.h30
-rw-r--r--drivers/input/misc/al3006_pls.c838
-rw-r--r--drivers/input/misc/apanel.c350
-rw-r--r--drivers/input/misc/arizona-haptics.c252
-rw-r--r--drivers/input/misc/ati_remote2.c1016
-rw-r--r--drivers/input/misc/atlas_btns.c159
-rw-r--r--drivers/input/misc/bfin_rotary.c272
-rw-r--r--drivers/input/misc/bma150.c668
-rw-r--r--drivers/input/misc/bma2x2.c7564
-rw-r--r--drivers/input/misc/bs_log.h167
-rw-r--r--drivers/input/misc/bstclass.c234
-rw-r--r--drivers/input/misc/bstclass.h74
-rw-r--r--drivers/input/misc/cm109.c918
-rw-r--r--drivers/input/misc/cma3000_d0x.c399
-rw-r--r--drivers/input/misc/cma3000_d0x.h42
-rw-r--r--drivers/input/misc/cma3000_d0x_i2c.c132
-rw-r--r--drivers/input/misc/cobalt_btns.c166
-rw-r--r--drivers/input/misc/da9052_onkey.c158
-rw-r--r--drivers/input/misc/da9055_onkey.c171
-rw-r--r--drivers/input/misc/dm355evm_keys.c273
-rw-r--r--drivers/input/misc/epl2182_pls_v2.c1496
-rw-r--r--drivers/input/misc/epl259x.c4645
-rw-r--r--drivers/input/misc/ewtsa.c2763
-rw-r--r--drivers/input/misc/gp2ap002a00f.c288
-rw-r--r--drivers/input/misc/gpio_axis.c192
-rw-r--r--drivers/input/misc/gpio_event.c228
-rw-r--r--drivers/input/misc/gpio_input.c390
-rw-r--r--drivers/input/misc/gpio_matrix.c441
-rw-r--r--drivers/input/misc/gpio_output.c97
-rw-r--r--drivers/input/misc/gpio_tilt_polled.c213
-rw-r--r--drivers/input/misc/headset.c462
-rw-r--r--drivers/input/misc/headset_sprd.c2377
-rw-r--r--drivers/input/misc/headset_sprd_sc2723.c1920
-rw-r--r--drivers/input/misc/hp_sdc_rtc.c730
-rw-r--r--drivers/input/misc/ims-pcu.c1901
-rw-r--r--drivers/input/misc/ixp4xx-beeper.c172
-rw-r--r--drivers/input/misc/keychord.c391
-rw-r--r--drivers/input/misc/keyspan_remote.c597
-rw-r--r--drivers/input/misc/kxtj9.c674
-rw-r--r--drivers/input/misc/lis3dh.c1707
-rw-r--r--drivers/input/misc/lis3dh_interrupt.c2261
-rw-r--r--drivers/input/misc/lis3dh_zt.c1729
-rw-r--r--drivers/input/misc/ltr_558als.c1236
-rw-r--r--drivers/input/misc/m68kspkr.c151
-rw-r--r--drivers/input/misc/max8925_onkey.c201
-rw-r--r--drivers/input/misc/max8997_haptic.c407
-rw-r--r--drivers/input/misc/mc13783-pwrbutton.c272
-rw-r--r--drivers/input/misc/mma8450.c254
-rw-r--r--drivers/input/misc/mpu3050.c482
-rw-r--r--drivers/input/misc/pcap_keys.c133
-rw-r--r--drivers/input/misc/pcf50633-input.c121
-rw-r--r--drivers/input/misc/pcf8574_keypad.c223
-rw-r--r--drivers/input/misc/pcspkr.c138
-rw-r--r--drivers/input/misc/pm8xxx-vibrator.c285
-rw-r--r--drivers/input/misc/pmic8xxx-pwrkey.c221
-rw-r--r--drivers/input/misc/powermate.c453
-rw-r--r--drivers/input/misc/pwm-beeper.c200
-rw-r--r--drivers/input/misc/rb532_button.c108
-rw-r--r--drivers/input/misc/retu-pwrbutton.c99
-rw-r--r--drivers/input/misc/rotary_encoder.c339
-rw-r--r--drivers/input/misc/sgi_btns.c169
-rw-r--r--drivers/input/misc/sparcspkr.c372
-rw-r--r--drivers/input/misc/stk3x1x.c4262
-rw-r--r--drivers/input/misc/stk3x1x.h31
-rw-r--r--drivers/input/misc/tmd2771_pls.c1591
-rw-r--r--drivers/input/misc/twl4030-pwrbutton.c125
-rw-r--r--drivers/input/misc/twl4030-vibra.c263
-rw-r--r--drivers/input/misc/twl6040-vibra.c431
-rw-r--r--drivers/input/misc/uinput.c883
-rw-r--r--drivers/input/misc/wistron_btns.c1389
-rw-r--r--drivers/input/misc/wm831x-on.c151
-rw-r--r--drivers/input/misc/xen-kbdfront.c396
-rw-r--r--drivers/input/misc/yealink.c1008
-rw-r--r--drivers/input/misc/yealink.h220
86 files changed, 60295 insertions, 0 deletions
diff --git a/drivers/input/misc/88pm80x_onkey.c b/drivers/input/misc/88pm80x_onkey.c
new file mode 100644
index 00000000..ee43e5b7
--- /dev/null
+++ b/drivers/input/misc/88pm80x_onkey.c
@@ -0,0 +1,168 @@
+/*
+ * Marvell 88PM80x ONKEY driver
+ *
+ * Copyright (C) 2012 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ * Qiao Zhou <zhouqiao@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/mfd/88pm80x.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define PM800_LONG_ONKEY_EN (1 << 0)
+#define PM800_LONG_KEY_DELAY (8) /* 1 .. 16 seconds */
+#define PM800_LONKEY_PRESS_TIME ((PM800_LONG_KEY_DELAY-1) << 4)
+#define PM800_LONKEY_PRESS_TIME_MASK (0xF0)
+#define PM800_SW_PDOWN (1 << 5)
+
+struct pm80x_onkey_info {
+ struct input_dev *idev;
+ struct pm80x_chip *pm80x;
+ struct regmap *map;
+ int irq;
+};
+
+/* 88PM80x gives us an interrupt when ONKEY is held */
+static irqreturn_t pm80x_onkey_handler(int irq, void *data)
+{
+ struct pm80x_onkey_info *info = data;
+ int ret = 0;
+ unsigned int val;
+
+ ret = regmap_read(info->map, PM800_STATUS_1, &val);
+ if (ret < 0) {
+ dev_err(info->idev->dev.parent, "failed to read status: %d\n", ret);
+ return IRQ_NONE;
+ }
+ val &= PM800_ONKEY_STS1;
+
+ input_report_key(info->idev, KEY_POWER, val);
+ input_sync(info->idev);
+
+ return IRQ_HANDLED;
+}
+
+static SIMPLE_DEV_PM_OPS(pm80x_onkey_pm_ops, pm80x_dev_suspend,
+ pm80x_dev_resume);
+
+static int pm80x_onkey_probe(struct platform_device *pdev)
+{
+
+ struct pm80x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct pm80x_onkey_info *info;
+ int err;
+
+ info = kzalloc(sizeof(struct pm80x_onkey_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->pm80x = chip;
+
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource!\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ info->map = info->pm80x->regmap;
+ if (!info->map) {
+ dev_err(&pdev->dev, "no regmap!\n");
+ err = -EINVAL;
+ goto out;
+ }
+
+ info->idev = input_allocate_device();
+ if (!info->idev) {
+ dev_err(&pdev->dev, "Failed to allocate input dev\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ info->idev->name = "88pm80x_on";
+ info->idev->phys = "88pm80x_on/input0";
+ info->idev->id.bustype = BUS_I2C;
+ info->idev->dev.parent = &pdev->dev;
+ info->idev->evbit[0] = BIT_MASK(EV_KEY);
+ __set_bit(KEY_POWER, info->idev->keybit);
+
+ err = pm80x_request_irq(info->pm80x, info->irq, pm80x_onkey_handler,
+ IRQF_ONESHOT, "onkey", info);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Failed to request IRQ: #%d: %d\n",
+ info->irq, err);
+ goto out_reg;
+ }
+
+ err = input_register_device(info->idev);
+ if (err) {
+ dev_err(&pdev->dev, "Can't register input device: %d\n", err);
+ goto out_irq;
+ }
+
+ platform_set_drvdata(pdev, info);
+
+ /* Enable long onkey detection */
+ regmap_update_bits(info->map, PM800_RTC_MISC4, PM800_LONG_ONKEY_EN,
+ PM800_LONG_ONKEY_EN);
+ /* Set 8-second interval */
+ regmap_update_bits(info->map, PM800_RTC_MISC3,
+ PM800_LONKEY_PRESS_TIME_MASK,
+ PM800_LONKEY_PRESS_TIME);
+
+ device_init_wakeup(&pdev->dev, 1);
+ return 0;
+
+out_irq:
+ pm80x_free_irq(info->pm80x, info->irq, info);
+out_reg:
+ input_free_device(info->idev);
+out:
+ kfree(info);
+ return err;
+}
+
+static int pm80x_onkey_remove(struct platform_device *pdev)
+{
+ struct pm80x_onkey_info *info = platform_get_drvdata(pdev);
+
+ device_init_wakeup(&pdev->dev, 0);
+ pm80x_free_irq(info->pm80x, info->irq, info);
+ input_unregister_device(info->idev);
+ kfree(info);
+ return 0;
+}
+
+static struct platform_driver pm80x_onkey_driver = {
+ .driver = {
+ .name = "88pm80x-onkey",
+ .owner = THIS_MODULE,
+ .pm = &pm80x_onkey_pm_ops,
+ },
+ .probe = pm80x_onkey_probe,
+ .remove = pm80x_onkey_remove,
+};
+
+module_platform_driver(pm80x_onkey_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Marvell 88PM80x ONKEY driver");
+MODULE_AUTHOR("Qiao Zhou <zhouqiao@marvell.com>");
+MODULE_ALIAS("platform:88pm80x-onkey");
diff --git a/drivers/input/misc/88pm860x_onkey.c b/drivers/input/misc/88pm860x_onkey.c
new file mode 100644
index 00000000..abd8453e
--- /dev/null
+++ b/drivers/input/misc/88pm860x_onkey.c
@@ -0,0 +1,170 @@
+/*
+ * 88pm860x_onkey.c - Marvell 88PM860x ONKEY driver
+ *
+ * Copyright (C) 2009-2010 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/88pm860x.h>
+#include <linux/slab.h>
+
+#define PM8607_WAKEUP 0x0b
+
+#define LONG_ONKEY_EN (1 << 1)
+#define ONKEY_STATUS (1 << 0)
+
+struct pm860x_onkey_info {
+ struct input_dev *idev;
+ struct pm860x_chip *chip;
+ struct i2c_client *i2c;
+ struct device *dev;
+ int irq;
+};
+
+/* 88PM860x gives us an interrupt when ONKEY is held */
+static irqreturn_t pm860x_onkey_handler(int irq, void *data)
+{
+ struct pm860x_onkey_info *info = data;
+ int ret;
+
+ ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2);
+ ret &= ONKEY_STATUS;
+ input_report_key(info->idev, KEY_POWER, ret);
+ input_sync(info->idev);
+
+ /* Enable 8-second long onkey detection */
+ pm860x_set_bits(info->i2c, PM8607_WAKEUP, 3, LONG_ONKEY_EN);
+ return IRQ_HANDLED;
+}
+
+static int pm860x_onkey_probe(struct platform_device *pdev)
+{
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct pm860x_onkey_info *info;
+ int irq, ret;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "No IRQ resource!\n");
+ return -EINVAL;
+ }
+
+ info = kzalloc(sizeof(struct pm860x_onkey_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ info->chip = chip;
+ info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion;
+ info->dev = &pdev->dev;
+ info->irq = irq;
+
+ info->idev = input_allocate_device();
+ if (!info->idev) {
+ dev_err(chip->dev, "Failed to allocate input dev\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ info->idev->name = "88pm860x_on";
+ info->idev->phys = "88pm860x_on/input0";
+ info->idev->id.bustype = BUS_I2C;
+ info->idev->dev.parent = &pdev->dev;
+ info->idev->evbit[0] = BIT_MASK(EV_KEY);
+ info->idev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+
+ ret = input_register_device(info->idev);
+ if (ret) {
+ dev_err(chip->dev, "Can't register input device: %d\n", ret);
+ goto out_reg;
+ }
+
+ ret = request_threaded_irq(info->irq, NULL, pm860x_onkey_handler,
+ IRQF_ONESHOT, "onkey", info);
+ if (ret < 0) {
+ dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+ info->irq, ret);
+ goto out_irq;
+ }
+
+ platform_set_drvdata(pdev, info);
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+
+out_irq:
+ input_unregister_device(info->idev);
+ kfree(info);
+ return ret;
+
+out_reg:
+ input_free_device(info->idev);
+out:
+ kfree(info);
+ return ret;
+}
+
+static int pm860x_onkey_remove(struct platform_device *pdev)
+{
+ struct pm860x_onkey_info *info = platform_get_drvdata(pdev);
+
+ free_irq(info->irq, info);
+ input_unregister_device(info->idev);
+ kfree(info);
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pm860x_onkey_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ if (device_may_wakeup(dev))
+ chip->wakeup_flag |= 1 << PM8607_IRQ_ONKEY;
+ return 0;
+}
+static int pm860x_onkey_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ if (device_may_wakeup(dev))
+ chip->wakeup_flag &= ~(1 << PM8607_IRQ_ONKEY);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm860x_onkey_pm_ops, pm860x_onkey_suspend, pm860x_onkey_resume);
+
+static struct platform_driver pm860x_onkey_driver = {
+ .driver = {
+ .name = "88pm860x-onkey",
+ .owner = THIS_MODULE,
+ .pm = &pm860x_onkey_pm_ops,
+ },
+ .probe = pm860x_onkey_probe,
+ .remove = pm860x_onkey_remove,
+};
+module_platform_driver(pm860x_onkey_driver);
+
+MODULE_DESCRIPTION("Marvell 88PM860x ONKEY driver");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
new file mode 100644
index 00000000..08e1faf2
--- /dev/null
+++ b/drivers/input/misc/Kconfig
@@ -0,0 +1,776 @@
+#
+# Input misc drivers configuration
+#
+menuconfig INPUT_MISC
+ bool "Miscellaneous devices"
+ help
+ Say Y here, and a list of miscellaneous input drivers will be displayed.
+ Everything that didn't fit into the other categories is here. This option
+ doesn't affect the kernel.
+
+ If unsure, say Y.
+
+if INPUT_MISC
+
+config INPUT_88PM860X_ONKEY
+ tristate "88PM860x ONKEY support"
+ depends on MFD_88PM860X
+ help
+ Support the ONKEY of Marvell 88PM860x PMICs as an input device
+ reporting power button status.
+
+ To compile this driver as a module, choose M here: the module
+ will be called 88pm860x_onkey.
+
+config INPUT_88PM80X_ONKEY
+ tristate "88PM80x ONKEY support"
+ depends on MFD_88PM800
+ help
+ Support the ONKEY of Marvell 88PM80x PMICs as an input device
+ reporting power button status.
+
+ To compile this driver as a module, choose M here: the module
+ will be called 88pm80x_onkey.
+
+config INPUT_AB8500_PONKEY
+ tristate "AB8500 Pon (PowerOn) Key"
+ depends on AB8500_CORE
+ help
+ Say Y here to use the PowerOn Key for ST-Ericsson's AB8500
+ Mix-Sig PMIC.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ab8500-ponkey.
+
+config INPUT_AD714X
+ tristate "Analog Devices AD714x Capacitance Touch Sensor"
+ help
+ Say Y here if you want to support an AD7142/3/7/8/7A touch sensor.
+
+ You should select a bus connection too.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad714x.
+
+config INPUT_AD714X_I2C
+ tristate "support I2C bus connection"
+ depends on INPUT_AD714X && I2C
+ default y
+ help
+ Say Y here if you have AD7142/AD7147 hooked to an I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad714x-i2c.
+
+config INPUT_AD714X_SPI
+ tristate "support SPI bus connection"
+ depends on INPUT_AD714X && SPI
+ default y
+ help
+ Say Y here if you have AD7142/AD7147 hooked to a SPI bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad714x-spi.
+
+config INPUT_ARIZONA_HAPTICS
+ tristate "Arizona haptics support"
+ depends on MFD_ARIZONA && SND_SOC
+ select INPUT_FF_MEMLESS
+ help
+ Say Y to enable support for the haptics module in Arizona CODECs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called arizona-haptics.
+
+config INPUT_BMA150
+ tristate "BMA150/SMB380 acceleration sensor support"
+ depends on I2C
+ select INPUT_POLLDEV
+ help
+ Say Y here if you have Bosch Sensortec's BMA150 or SMB380
+ acceleration sensor hooked to an I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bma150.
+
+config INPUT_PCSPKR
+ tristate "PC Speaker support"
+ depends on PCSPKR_PLATFORM
+ help
+ Say Y here if you want the standard PC Speaker to be used for
+ bells and whistles.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pcspkr.
+
+config INPUT_PM8XXX_VIBRATOR
+ tristate "Qualcomm PM8XXX vibrator support"
+ depends on MFD_PM8XXX
+ select INPUT_FF_MEMLESS
+ help
+ This option enables device driver support for the vibrator
+ on Qualcomm PM8xxx chip. This driver supports ff-memless interface
+ from input framework.
+
+ To compile this driver as module, choose M here: the
+ module will be called pm8xxx-vibrator.
+
+config INPUT_PMIC8XXX_PWRKEY
+ tristate "PMIC8XXX power key support"
+ depends on MFD_PM8XXX
+ help
+ Say Y here if you want support for the PMIC8XXX power key.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pmic8xxx-pwrkey.
+
+config INPUT_SPARCSPKR
+ tristate "SPARC Speaker support"
+ depends on PCI && SPARC64
+ help
+ Say Y here if you want the standard Speaker on Sparc PCI systems
+ to be used for bells and whistles.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sparcspkr.
+
+config INPUT_M68K_BEEP
+ tristate "M68k Beeper support"
+ depends on M68K
+
+config INPUT_MAX8925_ONKEY
+ tristate "MAX8925 ONKEY support"
+ depends on MFD_MAX8925
+ help
+ Support the ONKEY of MAX8925 PMICs as an input device
+ reporting power button status.
+
+ To compile this driver as a module, choose M here: the module
+ will be called max8925_onkey.
+
+config INPUT_MAX8997_HAPTIC
+ tristate "MAXIM MAX8997 haptic controller support"
+ depends on HAVE_PWM && MFD_MAX8997
+ select INPUT_FF_MEMLESS
+ help
+ This option enables device driver support for the haptic controller
+ on MAXIM MAX8997 chip. This driver supports ff-memless interface
+ from input framework.
+
+ To compile this driver as module, choose M here: the
+ module will be called max8997-haptic.
+
+config INPUT_MC13783_PWRBUTTON
+ tristate "MC13783 ON buttons"
+ depends on MFD_MC13783
+ help
+ Support the ON buttons of MC13783 PMIC as an input device
+ reporting power button status.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mc13783-pwrbutton.
+
+config INPUT_MMA8450
+ tristate "MMA8450 - Freescale's 3-Axis, 8/12-bit Digital Accelerometer"
+ depends on I2C
+ select INPUT_POLLDEV
+ help
+ Say Y here if you want to support Freescale's MMA8450 Accelerometer
+ through I2C interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mma8450.
+
+config INPUT_MPU3050
+ tristate "MPU3050 Triaxial gyroscope sensor"
+ depends on I2C
+ help
+ Say Y here if you want to support InvenSense MPU3050
+ connected via an I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mpu3050.
+
+config INPUT_APANEL
+ tristate "Fujitsu Lifebook Application Panel buttons"
+ depends on X86 && I2C && LEDS_CLASS
+ select INPUT_POLLDEV
+ select CHECK_SIGNATURE
+ help
+ Say Y here for support of the Application Panel buttons, used on
+ Fujitsu Lifebook. These are attached to the mainboard through
+ an SMBus interface managed by the I2C Intel ICH (i801) driver,
+ which you should also build for this kernel.
+
+ To compile this driver as a module, choose M here: the module will
+ be called apanel.
+
+config INPUT_GP2A
+ tristate "Sharp GP2AP002A00F I2C Proximity/Opto sensor driver"
+ depends on I2C
+ depends on GPIOLIB
+ help
+ Say Y here if you have a Sharp GP2AP002A00F proximity/als combo-chip
+ hooked to an I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gp2ap002a00f.
+
+config INPUT_GPIO_TILT_POLLED
+ tristate "Polled GPIO tilt switch"
+ depends on GPIOLIB
+ select INPUT_POLLDEV
+ help
+ This driver implements support for tilt switches connected
+ to GPIO pins that are not capable of generating interrupts.
+
+ The list of gpios to use and the mapping of their states
+ to specific angles is done via platform data.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gpio_tilt_polled.
+
+config INPUT_IXP4XX_BEEPER
+ tristate "IXP4XX Beeper support"
+ depends on ARCH_IXP4XX
+ help
+ If you say yes here, you can connect a beeper to the
+ ixp4xx gpio pins. This is used by the LinkSys NSLU2.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ixp4xx-beeper.
+
+config INPUT_COBALT_BTNS
+ tristate "Cobalt button interface"
+ depends on MIPS_COBALT
+ select INPUT_POLLDEV
+ help
+ Say Y here if you want to support MIPS Cobalt button interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cobalt_btns.
+
+config INPUT_WISTRON_BTNS
+ tristate "x86 Wistron laptop button interface"
+ depends on X86 && !X86_64
+ select INPUT_POLLDEV
+ select INPUT_SPARSEKMAP
+ select NEW_LEDS
+ select LEDS_CLASS
+ select CHECK_SIGNATURE
+ help
+ Say Y here for support of Wistron laptop button interfaces, used on
+ laptops of various brands, including Acer and Fujitsu-Siemens. If
+ available, mail and wifi LEDs will be controllable via /sys/class/leds.
+
+ To compile this driver as a module, choose M here: the module will
+ be called wistron_btns.
+
+config INPUT_ATLAS_BTNS
+ tristate "x86 Atlas button interface"
+ depends on X86 && ACPI
+ help
+ Say Y here for support of Atlas wallmount touchscreen buttons.
+ The events will show up as scancodes F1 through F9 via evdev.
+
+ To compile this driver as a module, choose M here: the module will
+ be called atlas_btns.
+
+config INPUT_ATI_REMOTE2
+ tristate "ATI / Philips USB RF remote control"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use an ATI or Philips USB RF remote control.
+ These are RF remotes with USB receivers.
+ ATI Remote Wonder II comes with some ATI's All-In-Wonder video cards
+ and is also available as a separate product.
+ This driver provides mouse pointer, left and right mouse buttons,
+ and maps all the other remote buttons to keypress events.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ati_remote2.
+
+config INPUT_KEYCHORD
+ tristate "Key chord input driver support"
+ help
+ Say Y here if you want to enable the key chord driver
+ accessible at /dev/keychord. This driver can be used
+ for receiving notifications when client specified key
+ combinations are pressed.
+
+ To compile this driver as a module, choose M here: the
+ module will be called keychord.
+
+config INPUT_KEYSPAN_REMOTE
+ tristate "Keyspan DMR USB remote control"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use a Keyspan DMR USB remote control.
+ Currently only the UIA-11 type of receiver has been tested. The tag
+ on the receiver that connects to the USB port should have a P/N that
+ will tell you what type of DMR you have. The UIA-10 type is not
+ supported at this time. This driver maps all buttons to keypress
+ events.
+
+ To compile this driver as a module, choose M here: the module will
+ be called keyspan_remote.
+
+config INPUT_KXTJ9
+ tristate "Kionix KXTJ9 tri-axis digital accelerometer"
+ depends on I2C
+ help
+ Say Y here to enable support for the Kionix KXTJ9 digital tri-axis
+ accelerometer.
+
+ To compile this driver as a module, choose M here: the module will
+ be called kxtj9.
+
+config INPUT_KXTJ9_POLLED_MODE
+ bool "Enable polling mode support"
+ depends on INPUT_KXTJ9
+ select INPUT_POLLDEV
+ help
+ Say Y here if you need accelerometer to work in polling mode.
+
+config INPUT_POWERMATE
+ tristate "Griffin PowerMate and Contour Jog support"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to use Griffin PowerMate or Contour Jog devices.
+ These are aluminum dials which can measure clockwise and anticlockwise
+ rotation. The dial also acts as a pushbutton. The base contains an LED
+ which can be instructed to pulse or to switch to a particular intensity.
+
+ You can download userspace tools from
+ <http://sowerbutts.com/powermate/>.
+
+ To compile this driver as a module, choose M here: the
+ module will be called powermate.
+
+config INPUT_YEALINK
+ tristate "Yealink usb-p1k voip phone"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to enable keyboard and LCD functions of the
+ Yealink usb-p1k usb phones. The audio part is enabled by the generic
+ usb sound driver, so you might want to enable that as well.
+
+ For information about how to use these additional functions, see
+ <file:Documentation/input/yealink.txt>.
+
+ To compile this driver as a module, choose M here: the module will be
+ called yealink.
+
+config INPUT_CM109
+ tristate "C-Media CM109 USB I/O Controller"
+ depends on USB_ARCH_HAS_HCD
+ select USB
+ help
+ Say Y here if you want to enable keyboard and buzzer functions of the
+ C-Media CM109 usb phones. The audio part is enabled by the generic
+ usb sound driver, so you might want to enable that as well.
+
+ To compile this driver as a module, choose M here: the module will be
+ called cm109.
+
+config INPUT_RETU_PWRBUTTON
+ tristate "Retu Power button Driver"
+ depends on MFD_RETU
+ help
+ Say Y here if you want to enable power key reporting via the
+ Retu chips found in Nokia Internet Tablets (770, N800, N810).
+
+ To compile this driver as a module, choose M here. The module will
+ be called retu-pwrbutton.
+
+config INPUT_TWL4030_PWRBUTTON
+ tristate "TWL4030 Power button Driver"
+ depends on TWL4030_CORE
+ help
+ Say Y here if you want to enable power key reporting via the
+ TWL4030 family of chips.
+
+ To compile this driver as a module, choose M here. The module will
+ be called twl4030_pwrbutton.
+
+config INPUT_TWL4030_VIBRA
+ tristate "Support for TWL4030 Vibrator"
+ depends on TWL4030_CORE
+ select MFD_TWL4030_AUDIO
+ select INPUT_FF_MEMLESS
+ help
+ This option enables support for TWL4030 Vibrator Driver.
+
+ To compile this driver as a module, choose M here. The module will
+ be called twl4030_vibra.
+
+config INPUT_TWL6040_VIBRA
+ tristate "Support for TWL6040 Vibrator"
+ depends on TWL6040_CORE
+ select INPUT_FF_MEMLESS
+ help
+ This option enables support for TWL6040 Vibrator Driver.
+
+ To compile this driver as a module, choose M here. The module will
+ be called twl6040_vibra.
+
+config INPUT_UINPUT
+ tristate "User level driver support"
+ help
+ Say Y here if you want to support user level drivers for input
+ subsystem accessible under char device 10:223 - /dev/input/uinput.
+
+ To compile this driver as a module, choose M here: the
+ module will be called uinput.
+
+config INPUT_SGI_BTNS
+ tristate "SGI Indy/O2 volume button interface"
+ depends on SGI_IP22 || SGI_IP32
+ select INPUT_POLLDEV
+ help
+ Say Y here if you want to support SGI Indy/O2 volume button interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sgi_btns.
+
+config INPUT_GPIO
+ tristate "GPIO driver support"
+ help
+ Say Y here if you want to support gpio based keys, wheels etc...
+
+config HP_SDC_RTC
+ tristate "HP SDC Real Time Clock"
+ depends on (GSC || HP300) && SERIO
+ select HP_SDC
+ help
+ Say Y here if you want to support the built-in real time clock
+ of the HP SDC controller.
+
+config INPUT_PCF50633_PMU
+ tristate "PCF50633 PMU events"
+ depends on MFD_PCF50633
+ help
+ Say Y to include support for delivering PMU events via input
+ layer on NXP PCF50633.
+
+config INPUT_PCF8574
+ tristate "PCF8574 Keypad input device"
+ depends on I2C
+ help
+ Say Y here if you want to support a keypad connected via I2C
+ with a PCF8574.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pcf8574_keypad.
+
+config INPUT_PWM_BEEPER
+ tristate "PWM beeper support"
+ depends on HAVE_PWM || PWM
+ help
+ Say Y here to get support for PWM based beeper devices.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the module will be
+ called pwm-beeper.
+
+config INPUT_GPIO_ROTARY_ENCODER
+ tristate "Rotary encoders connected to GPIO pins"
+ depends on GPIOLIB
+ help
+ Say Y here to add support for rotary encoders connected to GPIO lines.
+ Check file:Documentation/input/rotary-encoder.txt for more
+ information.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rotary_encoder.
+
+config INPUT_RB532_BUTTON
+ tristate "Mikrotik Routerboard 532 button interface"
+ depends on MIKROTIK_RB532
+ depends on GPIOLIB
+ select INPUT_POLLDEV
+ help
+ Say Y here if you want support for the S1 button built into
+ Mikrotik's Routerboard 532.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rb532_button.
+
+config INPUT_DA9052_ONKEY
+ tristate "Dialog DA9052/DA9053 Onkey"
+ depends on PMIC_DA9052
+ help
+ Support the ONKEY of Dialog DA9052 PMICs as an input device
+ reporting power button status.
+
+ To compile this driver as a module, choose M here: the
+ module will be called da9052_onkey.
+
+config INPUT_DA9055_ONKEY
+ tristate "Dialog Semiconductor DA9055 ONKEY"
+ depends on MFD_DA9055
+ help
+ Support the ONKEY of DA9055 PMICs as an input device
+ reporting power button status.
+
+ To compile this driver as a module, choose M here: the module
+ will be called da9055_onkey.
+
+config INPUT_DM355EVM
+ tristate "TI DaVinci DM355 EVM Keypad and IR Remote"
+ depends on MFD_DM355EVM_MSP
+ select INPUT_SPARSEKMAP
+ help
+ Supports the pushbuttons and IR remote used with
+ the DM355 EVM board.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dm355evm_keys.
+
+config INPUT_BFIN_ROTARY
+ tristate "Blackfin Rotary support"
+ depends on BF54x || BF52x
+ help
+ Say Y here if you want to use the Blackfin Rotary.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bfin-rotary.
+
+config INPUT_WM831X_ON
+ tristate "WM831X ON pin"
+ depends on MFD_WM831X
+ help
+ Support the ON pin of WM831X PMICs as an input device
+ reporting power button status.
+
+ To compile this driver as a module, choose M here: the module
+ will be called wm831x_on.
+
+config INPUT_PCAP
+ tristate "Motorola EZX PCAP misc input events"
+ depends on EZX_PCAP
+ help
+ Say Y here if you want to use Power key and Headphone button
+ on Motorola EZX phones.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pcap_keys.
+
+config INPUT_ADXL34X
+ tristate "Analog Devices ADXL34x Three-Axis Digital Accelerometer"
+ default n
+ help
+ Say Y here if you have a Accelerometer interface using the
+ ADXL345/6 controller, and your board-specific initialization
+ code includes that in its table of devices.
+
+ This driver can use either I2C or SPI communication to the
+ ADXL345/6 controller. Select the appropriate method for
+ your system.
+
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called adxl34x.
+
+config INPUT_ADXL34X_I2C
+ tristate "support I2C bus connection"
+ depends on INPUT_ADXL34X && I2C
+ default y
+ help
+ Say Y here if you have ADXL345/6 hooked to an I2C bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adxl34x-i2c.
+
+config INPUT_ADXL34X_SPI
+ tristate "support SPI bus connection"
+ depends on INPUT_ADXL34X && SPI
+ default y
+ help
+ Say Y here if you have ADXL345/6 hooked to a SPI bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adxl34x-spi.
+
+config INPUT_IMS_PCU
+ tristate "IMS Passenger Control Unit driver"
+ depends on USB
+ depends on LEDS_CLASS
+ help
+ Say Y here if you have system with IMS Rave Passenger Control Unit.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ims_pcu.
+
+config INPUT_CMA3000
+ tristate "VTI CMA3000 Tri-axis accelerometer"
+ help
+ Say Y here if you want to use VTI CMA3000_D0x Accelerometer
+ driver
+
+ This driver currently only supports I2C interface to the
+ controller. Also select the I2C method.
+
+ If unsure, say N
+
+ To compile this driver as a module, choose M here: the
+ module will be called cma3000_d0x.
+
+config INPUT_CMA3000_I2C
+ tristate "Support I2C bus connection"
+ depends on INPUT_CMA3000 && I2C
+ help
+ Say Y here if you want to use VTI CMA3000_D0x Accelerometer
+ through I2C interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cma3000_d0x_i2c.
+
+config INPUT_XEN_KBDDEV_FRONTEND
+ tristate "Xen virtual keyboard and mouse support"
+ depends on XEN
+ default y
+ select XEN_XENBUS_FRONTEND
+ help
+ This driver implements the front-end of the Xen virtual
+ keyboard and mouse device driver. It communicates with a back-end
+ in another domain.
+
+ To compile this driver as a module, choose M here: the
+ module will be called xen-kbdfront.
+
+config INPUT_HEADSET_SPRD_SC2723
+ tristate "SPRD SC2723 builtin headset & button detect driver"
+ default n
+ help
+ Say Y here if you are using SPRD platform.
+
+ To compile this driver as a module, choose M here: the
+ module will be called headset.
+
+config INPUT_SPRD_HEADSET_SHARK
+ tristate "SPRD platform builtin headset & button detect driver"
+ help
+ Say Y here if you are using SPRD platform.
+
+ To compile this driver as a module, choose M here: the
+ module will be called headset.
+
+config HEADSET_KEY_VOLUME_SUPPORTED
+ bool "HEADSET_KEY_VOLUME_SUPPORTED"
+ depends on INPUT_SPRD_HEADSET_SHARK
+ help
+ Say Y to enable HEADSET_KEY_VOLUME_SUPPORT.
+
+config HEADSET_EXTERNAL_HEADMICBIAS_POWER_SUPPORTED
+ bool "HEADSET_EXTERNAL_HEADMICBIAS_POWER_SUPPORTED"
+ depends on INPUT_SPRD_HEADSET_SHARK
+ help
+ Say Y to enable HEADSET_EXTERNAL_HEADMICBIAS_POWER_SUPPORT.
+
+config HEADSET_STS_POLLING_DISABLED
+ bool "HEADSET_STS_POLLING_DISABLED"
+ depends on INPUT_SPRD_HEADSET_SHARK
+ help
+ Say Y to disable HEADSET_STS_POLLING.
+
+config HEADSET_AUXADC_CAL_SUPPORTED
+ bool "HEADSET_AUXADC_CAL_SUPPORTED"
+ depends on INPUT_SPRD_HEADSET_SHARK
+ help
+ Say Y to enable HEADSET_AUXADC_CAL_SUPPORTED.
+
+#add by lgd
+config CAMERA_POLE_SUPPORT
+ bool "CAMERA_POLE_SUPPORT"
+ depends on INPUT_SPRD_HEADSET_SHARK
+ help
+ Say Y to enable camera pole support.
+
+config INPUT_LIS3DH_I2C
+ tristate "ST lid3dh 3-Axid accelerometer device with I2C bus"
+ depends on I2C
+ default n
+ help
+ Say Y here if you have a lis3dh device on the board and use I2C
+ communication, else say N.
+
+ To compile this driver as a module, choose M here.
+
+config INPUT_LIS3DH_I2C_INTERRUPT
+ tristate "ST lid3dh 3-Axid accelerometer device with I2C bus for interrupt mode"
+ depends on I2C
+ default n
+ help
+ Say Y here if you have a lis3dh device on the board and use I2C
+ communication, else say N.
+
+ To compile this driver as a module, choose M here.
+
+config INPUT_LIS3DH_ZT_I2C
+ tristate "ST lid3dh_zt 3-Axid accelerometer device with I2C bus"
+ depends on I2C
+ default n
+ help
+ Say Y here if you have a lis3dh_zt device on the board and use I2C
+ communication, else say N.
+
+ To compile this driver as a module, choose M here.
+
+config INPUT_LTR558_I2C
+ tristate "LTR558 proximity and light sensor device with I2C bus"
+ depends on I2C
+ default n
+ help
+ Say Y here if you have a LTR558 device on the board and use I2C
+ communication, else say N.
+
+config INPUT_STK3X1X_I2C
+ tristate "STK3X1X proximity and light sensor device with I2C bus"
+ depends on I2C
+ default n
+ help
+ Say Y here if you have a LTR558 device on the board and use I2C
+ communication, else say N.
+
+ To compile this driver as a module, choose M here.
+config INPUT_SENSORS_BMA2X2
+ tristate "BMA2x2 acceleration sensor support"
+ depends on I2C
+ help
+ To compile this driver as a module, choose M here.
+
+config INPUT_EPL2182_I2C
+ tristate "epl2182 proximity and light sensor device with I2C bus"
+ depends on I2C
+ default n
+ help
+ Say Y here if you have a epl2182 device on the board and use I2C
+ communication, else say N.
+
+ To compile this driver as a module, choose M here.
+
+config INPUT_EPL259X_I2C
+ tristate "epl259x proximity and light sensor device with I2C bus"
+ depends on I2C
+ default n
+ help
+ Say Y here if you have a epl259x device on the board and use I2C
+ communication, else say N.
+
+ To compile this driver as a module, choose M here.
+endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
new file mode 100644
index 00000000..201efeac
--- /dev/null
+++ b/drivers/input/misc/Makefile
@@ -0,0 +1,81 @@
+#
+# Makefile for the input misc drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
+obj-$(CONFIG_INPUT_88PM80X_ONKEY) += 88pm80x_onkey.o
+obj-$(CONFIG_INPUT_AB8500_PONKEY) += ab8500-ponkey.o
+obj-$(CONFIG_INPUT_AD714X) += ad714x.o
+obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o
+obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o
+obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o
+obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o
+obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o
+obj-$(CONFIG_INPUT_APANEL) += apanel.o
+obj-$(CONFIG_INPUT_ARIZONA_HAPTICS) += arizona-haptics.o
+obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
+obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
+obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o
+obj-$(CONFIG_INPUT_BMA150) += bma150.o
+obj-$(CONFIG_INPUT_CM109) += cm109.o
+obj-$(CONFIG_INPUT_CMA3000) += cma3000_d0x.o
+obj-$(CONFIG_INPUT_CMA3000_I2C) += cma3000_d0x_i2c.o
+obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
+obj-$(CONFIG_INPUT_DA9052_ONKEY) += da9052_onkey.o
+obj-$(CONFIG_INPUT_DA9055_ONKEY) += da9055_onkey.o
+obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
+obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
+obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o
+obj-$(CONFIG_INPUT_GPIO) += gpio_event.o gpio_matrix.o gpio_input.o gpio_output.o gpio_axis.o
+obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
+obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o
+obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
+obj-$(CONFIG_INPUT_KEYCHORD) += keychord.o
+obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
+obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
+obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
+obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
+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_PCAP) += pcap_keys.o
+obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
+obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o
+obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
+obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o
+obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
+obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
+obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o
+obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
+obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o
+obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
+obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
+obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
+obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
+obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o
+obj-$(CONFIG_INPUT_TWL6040_VIBRA) += twl6040-vibra.o
+obj-$(CONFIG_INPUT_UINPUT) += uinput.o
+obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
+obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
+obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
+obj-$(CONFIG_INPUT_YEALINK) += yealink.o
+obj-$(CONFIG_INPUT_LTR558_I2C) += ltr_558als.o
+obj-$(CONFIG_INPUT_EPL2182_I2C) += epl2182_pls_v2.o
+obj-$(CONFIG_INPUT_EPL259X_I2C) += epl259x.o
+obj-$(CONFIG_INPUT_LIS3DH_I2C) += lis3dh.o
+obj-$(CONFIG_INPUT_LIS3DH_I2C_INTERRUPT)+= lis3dh_interrupt.o
+obj-$(CONFIG_INPUT_LIS3DH_ZT_I2C) += lis3dh_zt.o
+obj-$(CONFIG_INPUT_SPRD_HEADSET_SHARK) += headset_sprd.o
+obj-$(CONFIG_INPUT_HEADSET_SPRD_SC2723) += headset_sprd_sc2723.o
+obj-$(CONFIG_INPUT_STK3X1X_I2C) += stk3x1x.o
+obj-$(CONFIG_INPUT_SENSORS_BMA2X2) += bstclass.o bma2x2.o
+ifeq ($(CONFIG_SENSORS_BMA2X2_ENABLE_INT1),y)
+ EXTRA_CFLAGS += -DBMA2X2_ENABLE_INT1
+endif
+
+ifeq ($(CONFIG_BOSCH_BMA2X2_ENABLE_INT2),y)
+ EXTRA_CFLAGS += -DBMA2X2_ENABLE_INT2
+endif
diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c
new file mode 100644
index 00000000..2f090b46
--- /dev/null
+++ b/drivers/input/misc/ab8500-ponkey.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ *
+ * License Terms: GNU General Public License v2
+ * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
+ *
+ * AB8500 Power-On Key handler
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/abx500/ab8500.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+
+/**
+ * struct ab8500_ponkey - ab8500 ponkey information
+ * @input_dev: pointer to input device
+ * @ab8500: ab8500 parent
+ * @irq_dbf: irq number for falling transition
+ * @irq_dbr: irq number for rising transition
+ */
+struct ab8500_ponkey {
+ struct input_dev *idev;
+ struct ab8500 *ab8500;
+ int irq_dbf;
+ int irq_dbr;
+};
+
+/* AB8500 gives us an interrupt when ONKEY is held */
+static irqreturn_t ab8500_ponkey_handler(int irq, void *data)
+{
+ struct ab8500_ponkey *ponkey = data;
+
+ if (irq == ponkey->irq_dbf)
+ input_report_key(ponkey->idev, KEY_POWER, true);
+ else if (irq == ponkey->irq_dbr)
+ input_report_key(ponkey->idev, KEY_POWER, false);
+
+ input_sync(ponkey->idev);
+
+ return IRQ_HANDLED;
+}
+
+static int ab8500_ponkey_probe(struct platform_device *pdev)
+{
+ struct ab8500 *ab8500 = dev_get_drvdata(pdev->dev.parent);
+ struct ab8500_ponkey *ponkey;
+ struct input_dev *input;
+ int irq_dbf, irq_dbr;
+ int error;
+
+ irq_dbf = platform_get_irq_byname(pdev, "ONKEY_DBF");
+ if (irq_dbf < 0) {
+ dev_err(&pdev->dev, "No IRQ for ONKEY_DBF, error=%d\n", irq_dbf);
+ return irq_dbf;
+ }
+
+ irq_dbr = platform_get_irq_byname(pdev, "ONKEY_DBR");
+ if (irq_dbr < 0) {
+ dev_err(&pdev->dev, "No IRQ for ONKEY_DBR, error=%d\n", irq_dbr);
+ return irq_dbr;
+ }
+
+ ponkey = kzalloc(sizeof(struct ab8500_ponkey), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!ponkey || !input) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ponkey->idev = input;
+ ponkey->ab8500 = ab8500;
+ ponkey->irq_dbf = irq_dbf;
+ ponkey->irq_dbr = irq_dbr;
+
+ input->name = "AB8500 POn(PowerOn) Key";
+ input->dev.parent = &pdev->dev;
+
+ input_set_capability(input, EV_KEY, KEY_POWER);
+
+ error = request_any_context_irq(ponkey->irq_dbf, ab8500_ponkey_handler,
+ 0, "ab8500-ponkey-dbf", ponkey);
+ if (error < 0) {
+ dev_err(ab8500->dev, "Failed to request dbf IRQ#%d: %d\n",
+ ponkey->irq_dbf, error);
+ goto err_free_mem;
+ }
+
+ error = request_any_context_irq(ponkey->irq_dbr, ab8500_ponkey_handler,
+ 0, "ab8500-ponkey-dbr", ponkey);
+ if (error < 0) {
+ dev_err(ab8500->dev, "Failed to request dbr IRQ#%d: %d\n",
+ ponkey->irq_dbr, error);
+ goto err_free_dbf_irq;
+ }
+
+ error = input_register_device(ponkey->idev);
+ if (error) {
+ dev_err(ab8500->dev, "Can't register input device: %d\n", error);
+ goto err_free_dbr_irq;
+ }
+
+ platform_set_drvdata(pdev, ponkey);
+ return 0;
+
+err_free_dbr_irq:
+ free_irq(ponkey->irq_dbr, ponkey);
+err_free_dbf_irq:
+ free_irq(ponkey->irq_dbf, ponkey);
+err_free_mem:
+ input_free_device(input);
+ kfree(ponkey);
+
+ return error;
+}
+
+static int ab8500_ponkey_remove(struct platform_device *pdev)
+{
+ struct ab8500_ponkey *ponkey = platform_get_drvdata(pdev);
+
+ free_irq(ponkey->irq_dbf, ponkey);
+ free_irq(ponkey->irq_dbr, ponkey);
+ input_unregister_device(ponkey->idev);
+ kfree(ponkey);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ab8500_ponkey_match[] = {
+ { .compatible = "stericsson,ab8500-ponkey", },
+ {}
+};
+#endif
+
+static struct platform_driver ab8500_ponkey_driver = {
+ .driver = {
+ .name = "ab8500-poweron-key",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ab8500_ponkey_match),
+ },
+ .probe = ab8500_ponkey_probe,
+ .remove = ab8500_ponkey_remove,
+};
+module_platform_driver(ab8500_ponkey_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Sundar Iyer <sundar.iyer@stericsson.com>");
+MODULE_DESCRIPTION("ST-Ericsson AB8500 Power-ON(Pon) Key driver");
diff --git a/drivers/input/misc/ad714x-i2c.c b/drivers/input/misc/ad714x-i2c.c
new file mode 100644
index 00000000..e0f52251
--- /dev/null
+++ b/drivers/input/misc/ad714x-i2c.c
@@ -0,0 +1,123 @@
+/*
+ * AD714X CapTouch Programmable Controller driver (I2C bus)
+ *
+ * Copyright 2009-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h> /* BUS_I2C */
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/pm.h>
+#include "ad714x.h"
+
+#ifdef CONFIG_PM_SLEEP
+static int ad714x_i2c_suspend(struct device *dev)
+{
+ return ad714x_disable(i2c_get_clientdata(to_i2c_client(dev)));
+}
+
+static int ad714x_i2c_resume(struct device *dev)
+{
+ return ad714x_enable(i2c_get_clientdata(to_i2c_client(dev)));
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ad714x_i2c_pm, ad714x_i2c_suspend, ad714x_i2c_resume);
+
+static int ad714x_i2c_write(struct ad714x_chip *chip,
+ unsigned short reg, unsigned short data)
+{
+ struct i2c_client *client = to_i2c_client(chip->dev);
+ int error;
+
+ chip->xfer_buf[0] = cpu_to_be16(reg);
+ chip->xfer_buf[1] = cpu_to_be16(data);
+
+ error = i2c_master_send(client, (u8 *)chip->xfer_buf,
+ 2 * sizeof(*chip->xfer_buf));
+ if (unlikely(error < 0)) {
+ dev_err(&client->dev, "I2C write error: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int ad714x_i2c_read(struct ad714x_chip *chip,
+ unsigned short reg, unsigned short *data, size_t len)
+{
+ struct i2c_client *client = to_i2c_client(chip->dev);
+ int i;
+ int error;
+
+ chip->xfer_buf[0] = cpu_to_be16(reg);
+
+ error = i2c_master_send(client, (u8 *)chip->xfer_buf,
+ sizeof(*chip->xfer_buf));
+ if (error >= 0)
+ error = i2c_master_recv(client, (u8 *)chip->xfer_buf,
+ len * sizeof(*chip->xfer_buf));
+
+ if (unlikely(error < 0)) {
+ dev_err(&client->dev, "I2C read error: %d\n", error);
+ return error;
+ }
+
+ for (i = 0; i < len; i++)
+ data[i] = be16_to_cpu(chip->xfer_buf[i]);
+
+ return 0;
+}
+
+static int ad714x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ad714x_chip *chip;
+
+ chip = ad714x_probe(&client->dev, BUS_I2C, client->irq,
+ ad714x_i2c_read, ad714x_i2c_write);
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ i2c_set_clientdata(client, chip);
+
+ return 0;
+}
+
+static int ad714x_i2c_remove(struct i2c_client *client)
+{
+ struct ad714x_chip *chip = i2c_get_clientdata(client);
+
+ ad714x_remove(chip);
+
+ return 0;
+}
+
+static const struct i2c_device_id ad714x_id[] = {
+ { "ad7142_captouch", 0 },
+ { "ad7143_captouch", 0 },
+ { "ad7147_captouch", 0 },
+ { "ad7147a_captouch", 0 },
+ { "ad7148_captouch", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ad714x_id);
+
+static struct i2c_driver ad714x_i2c_driver = {
+ .driver = {
+ .name = "ad714x_captouch",
+ .pm = &ad714x_i2c_pm,
+ },
+ .probe = ad714x_i2c_probe,
+ .remove = ad714x_i2c_remove,
+ .id_table = ad714x_id,
+};
+
+module_i2c_driver(ad714x_i2c_driver);
+
+MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor I2C Bus Driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ad714x-spi.c b/drivers/input/misc/ad714x-spi.c
new file mode 100644
index 00000000..61891486
--- /dev/null
+++ b/drivers/input/misc/ad714x-spi.c
@@ -0,0 +1,130 @@
+/*
+ * AD714X CapTouch Programmable Controller driver (SPI bus)
+ *
+ * Copyright 2009-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h> /* BUS_SPI */
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/pm.h>
+#include <linux/types.h>
+#include "ad714x.h"
+
+#define AD714x_SPI_CMD_PREFIX 0xE000 /* bits 15:11 */
+#define AD714x_SPI_READ BIT(10)
+
+#ifdef CONFIG_PM_SLEEP
+static int ad714x_spi_suspend(struct device *dev)
+{
+ return ad714x_disable(spi_get_drvdata(to_spi_device(dev)));
+}
+
+static int ad714x_spi_resume(struct device *dev)
+{
+ return ad714x_enable(spi_get_drvdata(to_spi_device(dev)));
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(ad714x_spi_pm, ad714x_spi_suspend, ad714x_spi_resume);
+
+static int ad714x_spi_read(struct ad714x_chip *chip,
+ unsigned short reg, unsigned short *data, size_t len)
+{
+ struct spi_device *spi = to_spi_device(chip->dev);
+ struct spi_message message;
+ struct spi_transfer xfer[2];
+ int i;
+ int error;
+
+ spi_message_init(&message);
+ memset(xfer, 0, sizeof(xfer));
+
+ chip->xfer_buf[0] = cpu_to_be16(AD714x_SPI_CMD_PREFIX |
+ AD714x_SPI_READ | reg);
+ xfer[0].tx_buf = &chip->xfer_buf[0];
+ xfer[0].len = sizeof(chip->xfer_buf[0]);
+ spi_message_add_tail(&xfer[0], &message);
+
+ xfer[1].rx_buf = &chip->xfer_buf[1];
+ xfer[1].len = sizeof(chip->xfer_buf[1]) * len;
+ spi_message_add_tail(&xfer[1], &message);
+
+ error = spi_sync(spi, &message);
+ if (unlikely(error)) {
+ dev_err(chip->dev, "SPI read error: %d\n", error);
+ return error;
+ }
+
+ for (i = 0; i < len; i++)
+ data[i] = be16_to_cpu(chip->xfer_buf[i + 1]);
+
+ return 0;
+}
+
+static int ad714x_spi_write(struct ad714x_chip *chip,
+ unsigned short reg, unsigned short data)
+{
+ struct spi_device *spi = to_spi_device(chip->dev);
+ int error;
+
+ chip->xfer_buf[0] = cpu_to_be16(AD714x_SPI_CMD_PREFIX | reg);
+ chip->xfer_buf[1] = cpu_to_be16(data);
+
+ error = spi_write(spi, (u8 *)chip->xfer_buf,
+ 2 * sizeof(*chip->xfer_buf));
+ if (unlikely(error)) {
+ dev_err(chip->dev, "SPI write error: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int ad714x_spi_probe(struct spi_device *spi)
+{
+ struct ad714x_chip *chip;
+ int err;
+
+ spi->bits_per_word = 8;
+ err = spi_setup(spi);
+ if (err < 0)
+ return err;
+
+ chip = ad714x_probe(&spi->dev, BUS_SPI, spi->irq,
+ ad714x_spi_read, ad714x_spi_write);
+ if (IS_ERR(chip))
+ return PTR_ERR(chip);
+
+ spi_set_drvdata(spi, chip);
+
+ return 0;
+}
+
+static int ad714x_spi_remove(struct spi_device *spi)
+{
+ struct ad714x_chip *chip = spi_get_drvdata(spi);
+
+ ad714x_remove(chip);
+ spi_set_drvdata(spi, NULL);
+
+ return 0;
+}
+
+static struct spi_driver ad714x_spi_driver = {
+ .driver = {
+ .name = "ad714x_captouch",
+ .owner = THIS_MODULE,
+ .pm = &ad714x_spi_pm,
+ },
+ .probe = ad714x_spi_probe,
+ .remove = ad714x_spi_remove,
+};
+
+module_spi_driver(ad714x_spi_driver);
+
+MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor SPI Bus Driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ad714x.c b/drivers/input/misc/ad714x.c
new file mode 100644
index 00000000..2e5d5e1d
--- /dev/null
+++ b/drivers/input/misc/ad714x.c
@@ -0,0 +1,1261 @@
+/*
+ * AD714X CapTouch Programmable Controller driver supporting AD7142/3/7/8/7A
+ *
+ * Copyright 2009-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input/ad714x.h>
+#include <linux/module.h>
+#include "ad714x.h"
+
+#define AD714X_PWR_CTRL 0x0
+#define AD714X_STG_CAL_EN_REG 0x1
+#define AD714X_AMB_COMP_CTRL0_REG 0x2
+#define AD714X_PARTID_REG 0x17
+#define AD7142_PARTID 0xE620
+#define AD7143_PARTID 0xE630
+#define AD7147_PARTID 0x1470
+#define AD7148_PARTID 0x1480
+#define AD714X_STAGECFG_REG 0x80
+#define AD714X_SYSCFG_REG 0x0
+
+#define STG_LOW_INT_EN_REG 0x5
+#define STG_HIGH_INT_EN_REG 0x6
+#define STG_COM_INT_EN_REG 0x7
+#define STG_LOW_INT_STA_REG 0x8
+#define STG_HIGH_INT_STA_REG 0x9
+#define STG_COM_INT_STA_REG 0xA
+
+#define CDC_RESULT_S0 0xB
+#define CDC_RESULT_S1 0xC
+#define CDC_RESULT_S2 0xD
+#define CDC_RESULT_S3 0xE
+#define CDC_RESULT_S4 0xF
+#define CDC_RESULT_S5 0x10
+#define CDC_RESULT_S6 0x11
+#define CDC_RESULT_S7 0x12
+#define CDC_RESULT_S8 0x13
+#define CDC_RESULT_S9 0x14
+#define CDC_RESULT_S10 0x15
+#define CDC_RESULT_S11 0x16
+
+#define STAGE0_AMBIENT 0xF1
+#define STAGE1_AMBIENT 0x115
+#define STAGE2_AMBIENT 0x139
+#define STAGE3_AMBIENT 0x15D
+#define STAGE4_AMBIENT 0x181
+#define STAGE5_AMBIENT 0x1A5
+#define STAGE6_AMBIENT 0x1C9
+#define STAGE7_AMBIENT 0x1ED
+#define STAGE8_AMBIENT 0x211
+#define STAGE9_AMBIENT 0x234
+#define STAGE10_AMBIENT 0x259
+#define STAGE11_AMBIENT 0x27D
+
+#define PER_STAGE_REG_NUM 36
+#define STAGE_CFGREG_NUM 8
+#define SYS_CFGREG_NUM 8
+
+/*
+ * driver information which will be used to maintain the software flow
+ */
+enum ad714x_device_state { IDLE, JITTER, ACTIVE, SPACE };
+
+struct ad714x_slider_drv {
+ int highest_stage;
+ int abs_pos;
+ int flt_pos;
+ enum ad714x_device_state state;
+ struct input_dev *input;
+};
+
+struct ad714x_wheel_drv {
+ int abs_pos;
+ int flt_pos;
+ int pre_highest_stage;
+ int highest_stage;
+ enum ad714x_device_state state;
+ struct input_dev *input;
+};
+
+struct ad714x_touchpad_drv {
+ int x_highest_stage;
+ int x_flt_pos;
+ int x_abs_pos;
+ int y_highest_stage;
+ int y_flt_pos;
+ int y_abs_pos;
+ int left_ep;
+ int left_ep_val;
+ int right_ep;
+ int right_ep_val;
+ int top_ep;
+ int top_ep_val;
+ int bottom_ep;
+ int bottom_ep_val;
+ enum ad714x_device_state state;
+ struct input_dev *input;
+};
+
+struct ad714x_button_drv {
+ enum ad714x_device_state state;
+ /*
+ * Unlike slider/wheel/touchpad, all buttons point to
+ * same input_dev instance
+ */
+ struct input_dev *input;
+};
+
+struct ad714x_driver_data {
+ struct ad714x_slider_drv *slider;
+ struct ad714x_wheel_drv *wheel;
+ struct ad714x_touchpad_drv *touchpad;
+ struct ad714x_button_drv *button;
+};
+
+/*
+ * information to integrate all things which will be private data
+ * of spi/i2c device
+ */
+
+static void ad714x_use_com_int(struct ad714x_chip *ad714x,
+ int start_stage, int end_stage)
+{
+ unsigned short data;
+ unsigned short mask;
+
+ mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1);
+
+ ad714x->read(ad714x, STG_COM_INT_EN_REG, &data, 1);
+ data |= 1 << end_stage;
+ ad714x->write(ad714x, STG_COM_INT_EN_REG, data);
+
+ ad714x->read(ad714x, STG_HIGH_INT_EN_REG, &data, 1);
+ data &= ~mask;
+ ad714x->write(ad714x, STG_HIGH_INT_EN_REG, data);
+}
+
+static void ad714x_use_thr_int(struct ad714x_chip *ad714x,
+ int start_stage, int end_stage)
+{
+ unsigned short data;
+ unsigned short mask;
+
+ mask = ((1 << (end_stage + 1)) - 1) - ((1 << start_stage) - 1);
+
+ ad714x->read(ad714x, STG_COM_INT_EN_REG, &data, 1);
+ data &= ~(1 << end_stage);
+ ad714x->write(ad714x, STG_COM_INT_EN_REG, data);
+
+ ad714x->read(ad714x, STG_HIGH_INT_EN_REG, &data, 1);
+ data |= mask;
+ ad714x->write(ad714x, STG_HIGH_INT_EN_REG, data);
+}
+
+static int ad714x_cal_highest_stage(struct ad714x_chip *ad714x,
+ int start_stage, int end_stage)
+{
+ int max_res = 0;
+ int max_idx = 0;
+ int i;
+
+ for (i = start_stage; i <= end_stage; i++) {
+ if (ad714x->sensor_val[i] > max_res) {
+ max_res = ad714x->sensor_val[i];
+ max_idx = i;
+ }
+ }
+
+ return max_idx;
+}
+
+static int ad714x_cal_abs_pos(struct ad714x_chip *ad714x,
+ int start_stage, int end_stage,
+ int highest_stage, int max_coord)
+{
+ int a_param, b_param;
+
+ if (highest_stage == start_stage) {
+ a_param = ad714x->sensor_val[start_stage + 1];
+ b_param = ad714x->sensor_val[start_stage] +
+ ad714x->sensor_val[start_stage + 1];
+ } else if (highest_stage == end_stage) {
+ a_param = ad714x->sensor_val[end_stage] *
+ (end_stage - start_stage) +
+ ad714x->sensor_val[end_stage - 1] *
+ (end_stage - start_stage - 1);
+ b_param = ad714x->sensor_val[end_stage] +
+ ad714x->sensor_val[end_stage - 1];
+ } else {
+ a_param = ad714x->sensor_val[highest_stage] *
+ (highest_stage - start_stage) +
+ ad714x->sensor_val[highest_stage - 1] *
+ (highest_stage - start_stage - 1) +
+ ad714x->sensor_val[highest_stage + 1] *
+ (highest_stage - start_stage + 1);
+ b_param = ad714x->sensor_val[highest_stage] +
+ ad714x->sensor_val[highest_stage - 1] +
+ ad714x->sensor_val[highest_stage + 1];
+ }
+
+ return (max_coord / (end_stage - start_stage)) * a_param / b_param;
+}
+
+/*
+ * One button can connect to multi positive and negative of CDCs
+ * Multi-buttons can connect to same positive/negative of one CDC
+ */
+static void ad714x_button_state_machine(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_button_plat *hw = &ad714x->hw->button[idx];
+ struct ad714x_button_drv *sw = &ad714x->sw->button[idx];
+
+ switch (sw->state) {
+ case IDLE:
+ if (((ad714x->h_state & hw->h_mask) == hw->h_mask) &&
+ ((ad714x->l_state & hw->l_mask) == hw->l_mask)) {
+ dev_dbg(ad714x->dev, "button %d touched\n", idx);
+ input_report_key(sw->input, hw->keycode, 1);
+ input_sync(sw->input);
+ sw->state = ACTIVE;
+ }
+ break;
+
+ case ACTIVE:
+ if (((ad714x->h_state & hw->h_mask) != hw->h_mask) ||
+ ((ad714x->l_state & hw->l_mask) != hw->l_mask)) {
+ dev_dbg(ad714x->dev, "button %d released\n", idx);
+ input_report_key(sw->input, hw->keycode, 0);
+ input_sync(sw->input);
+ sw->state = IDLE;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * The response of a sensor is defined by the absolute number of codes
+ * between the current CDC value and the ambient value.
+ */
+static void ad714x_slider_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
+ int i;
+
+ ad714x->read(ad714x, CDC_RESULT_S0 + hw->start_stage,
+ &ad714x->adc_reg[hw->start_stage],
+ hw->end_stage - hw->start_stage + 1);
+
+ for (i = hw->start_stage; i <= hw->end_stage; i++) {
+ ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
+ &ad714x->amb_reg[i], 1);
+
+ ad714x->sensor_val[i] =
+ abs(ad714x->adc_reg[i] - ad714x->amb_reg[i]);
+ }
+}
+
+static void ad714x_slider_cal_highest_stage(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
+ struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
+
+ sw->highest_stage = ad714x_cal_highest_stage(ad714x, hw->start_stage,
+ hw->end_stage);
+
+ dev_dbg(ad714x->dev, "slider %d highest_stage:%d\n", idx,
+ sw->highest_stage);
+}
+
+/*
+ * The formulae are very straight forward. It uses the sensor with the
+ * highest response and the 2 adjacent ones.
+ * When Sensor 0 has the highest response, only sensor 0 and sensor 1
+ * are used in the calculations. Similarly when the last sensor has the
+ * highest response, only the last sensor and the second last sensors
+ * are used in the calculations.
+ *
+ * For i= idx_of_peak_Sensor-1 to i= idx_of_peak_Sensor+1
+ * v += Sensor response(i)*i
+ * w += Sensor response(i)
+ * POS=(Number_of_Positions_Wanted/(Number_of_Sensors_Used-1)) *(v/w)
+ */
+static void ad714x_slider_cal_abs_pos(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
+ struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
+
+ sw->abs_pos = ad714x_cal_abs_pos(ad714x, hw->start_stage, hw->end_stage,
+ sw->highest_stage, hw->max_coord);
+
+ dev_dbg(ad714x->dev, "slider %d absolute position:%d\n", idx,
+ sw->abs_pos);
+}
+
+/*
+ * To minimise the Impact of the noise on the algorithm, ADI developed a
+ * routine that filters the CDC results after they have been read by the
+ * host processor.
+ * The filter used is an Infinite Input Response(IIR) filter implemented
+ * in firmware and attenuates the noise on the CDC results after they've
+ * been read by the host processor.
+ * Filtered_CDC_result = (Filtered_CDC_result * (10 - Coefficient) +
+ * Latest_CDC_result * Coefficient)/10
+ */
+static void ad714x_slider_cal_flt_pos(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
+
+ sw->flt_pos = (sw->flt_pos * (10 - 4) +
+ sw->abs_pos * 4)/10;
+
+ dev_dbg(ad714x->dev, "slider %d filter position:%d\n", idx,
+ sw->flt_pos);
+}
+
+static void ad714x_slider_use_com_int(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
+
+ ad714x_use_com_int(ad714x, hw->start_stage, hw->end_stage);
+}
+
+static void ad714x_slider_use_thr_int(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
+
+ ad714x_use_thr_int(ad714x, hw->start_stage, hw->end_stage);
+}
+
+static void ad714x_slider_state_machine(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_slider_plat *hw = &ad714x->hw->slider[idx];
+ struct ad714x_slider_drv *sw = &ad714x->sw->slider[idx];
+ unsigned short h_state, c_state;
+ unsigned short mask;
+
+ mask = ((1 << (hw->end_stage + 1)) - 1) - ((1 << hw->start_stage) - 1);
+
+ h_state = ad714x->h_state & mask;
+ c_state = ad714x->c_state & mask;
+
+ switch (sw->state) {
+ case IDLE:
+ if (h_state) {
+ sw->state = JITTER;
+ /* In End of Conversion interrupt mode, the AD714X
+ * continuously generates hardware interrupts.
+ */
+ ad714x_slider_use_com_int(ad714x, idx);
+ dev_dbg(ad714x->dev, "slider %d touched\n", idx);
+ }
+ break;
+
+ case JITTER:
+ if (c_state == mask) {
+ ad714x_slider_cal_sensor_val(ad714x, idx);
+ ad714x_slider_cal_highest_stage(ad714x, idx);
+ ad714x_slider_cal_abs_pos(ad714x, idx);
+ sw->flt_pos = sw->abs_pos;
+ sw->state = ACTIVE;
+ }
+ break;
+
+ case ACTIVE:
+ if (c_state == mask) {
+ if (h_state) {
+ ad714x_slider_cal_sensor_val(ad714x, idx);
+ ad714x_slider_cal_highest_stage(ad714x, idx);
+ ad714x_slider_cal_abs_pos(ad714x, idx);
+ ad714x_slider_cal_flt_pos(ad714x, idx);
+ input_report_abs(sw->input, ABS_X, sw->flt_pos);
+ input_report_key(sw->input, BTN_TOUCH, 1);
+ } else {
+ /* When the user lifts off the sensor, configure
+ * the AD714X back to threshold interrupt mode.
+ */
+ ad714x_slider_use_thr_int(ad714x, idx);
+ sw->state = IDLE;
+ input_report_key(sw->input, BTN_TOUCH, 0);
+ dev_dbg(ad714x->dev, "slider %d released\n",
+ idx);
+ }
+ input_sync(sw->input);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * When the scroll wheel is activated, we compute the absolute position based
+ * on the sensor values. To calculate the position, we first determine the
+ * sensor that has the greatest response among the 8 sensors that constitutes
+ * the scrollwheel. Then we determined the 2 sensors on either sides of the
+ * sensor with the highest response and we apply weights to these sensors.
+ */
+static void ad714x_wheel_cal_highest_stage(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
+ struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
+
+ sw->pre_highest_stage = sw->highest_stage;
+ sw->highest_stage = ad714x_cal_highest_stage(ad714x, hw->start_stage,
+ hw->end_stage);
+
+ dev_dbg(ad714x->dev, "wheel %d highest_stage:%d\n", idx,
+ sw->highest_stage);
+}
+
+static void ad714x_wheel_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
+ int i;
+
+ ad714x->read(ad714x, CDC_RESULT_S0 + hw->start_stage,
+ &ad714x->adc_reg[hw->start_stage],
+ hw->end_stage - hw->start_stage + 1);
+
+ for (i = hw->start_stage; i <= hw->end_stage; i++) {
+ ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
+ &ad714x->amb_reg[i], 1);
+ if (ad714x->adc_reg[i] > ad714x->amb_reg[i])
+ ad714x->sensor_val[i] =
+ ad714x->adc_reg[i] - ad714x->amb_reg[i];
+ else
+ ad714x->sensor_val[i] = 0;
+ }
+}
+
+/*
+ * When the scroll wheel is activated, we compute the absolute position based
+ * on the sensor values. To calculate the position, we first determine the
+ * sensor that has the greatest response among the sensors that constitutes
+ * the scrollwheel. Then we determined the sensors on either sides of the
+ * sensor with the highest response and we apply weights to these sensors. The
+ * result of this computation gives us the mean value.
+ */
+
+static void ad714x_wheel_cal_abs_pos(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
+ struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
+ int stage_num = hw->end_stage - hw->start_stage + 1;
+ int first_before, highest, first_after;
+ int a_param, b_param;
+
+ first_before = (sw->highest_stage + stage_num - 1) % stage_num;
+ highest = sw->highest_stage;
+ first_after = (sw->highest_stage + stage_num + 1) % stage_num;
+
+ a_param = ad714x->sensor_val[highest] *
+ (highest - hw->start_stage) +
+ ad714x->sensor_val[first_before] *
+ (highest - hw->start_stage - 1) +
+ ad714x->sensor_val[first_after] *
+ (highest - hw->start_stage + 1);
+ b_param = ad714x->sensor_val[highest] +
+ ad714x->sensor_val[first_before] +
+ ad714x->sensor_val[first_after];
+
+ sw->abs_pos = ((hw->max_coord / (hw->end_stage - hw->start_stage)) *
+ a_param) / b_param;
+
+ if (sw->abs_pos > hw->max_coord)
+ sw->abs_pos = hw->max_coord;
+ else if (sw->abs_pos < 0)
+ sw->abs_pos = 0;
+}
+
+static void ad714x_wheel_cal_flt_pos(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
+ struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
+ if (((sw->pre_highest_stage == hw->end_stage) &&
+ (sw->highest_stage == hw->start_stage)) ||
+ ((sw->pre_highest_stage == hw->start_stage) &&
+ (sw->highest_stage == hw->end_stage)))
+ sw->flt_pos = sw->abs_pos;
+ else
+ sw->flt_pos = ((sw->flt_pos * 30) + (sw->abs_pos * 71)) / 100;
+
+ if (sw->flt_pos > hw->max_coord)
+ sw->flt_pos = hw->max_coord;
+}
+
+static void ad714x_wheel_use_com_int(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
+
+ ad714x_use_com_int(ad714x, hw->start_stage, hw->end_stage);
+}
+
+static void ad714x_wheel_use_thr_int(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
+
+ ad714x_use_thr_int(ad714x, hw->start_stage, hw->end_stage);
+}
+
+static void ad714x_wheel_state_machine(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_wheel_plat *hw = &ad714x->hw->wheel[idx];
+ struct ad714x_wheel_drv *sw = &ad714x->sw->wheel[idx];
+ unsigned short h_state, c_state;
+ unsigned short mask;
+
+ mask = ((1 << (hw->end_stage + 1)) - 1) - ((1 << hw->start_stage) - 1);
+
+ h_state = ad714x->h_state & mask;
+ c_state = ad714x->c_state & mask;
+
+ switch (sw->state) {
+ case IDLE:
+ if (h_state) {
+ sw->state = JITTER;
+ /* In End of Conversion interrupt mode, the AD714X
+ * continuously generates hardware interrupts.
+ */
+ ad714x_wheel_use_com_int(ad714x, idx);
+ dev_dbg(ad714x->dev, "wheel %d touched\n", idx);
+ }
+ break;
+
+ case JITTER:
+ if (c_state == mask) {
+ ad714x_wheel_cal_sensor_val(ad714x, idx);
+ ad714x_wheel_cal_highest_stage(ad714x, idx);
+ ad714x_wheel_cal_abs_pos(ad714x, idx);
+ sw->flt_pos = sw->abs_pos;
+ sw->state = ACTIVE;
+ }
+ break;
+
+ case ACTIVE:
+ if (c_state == mask) {
+ if (h_state) {
+ ad714x_wheel_cal_sensor_val(ad714x, idx);
+ ad714x_wheel_cal_highest_stage(ad714x, idx);
+ ad714x_wheel_cal_abs_pos(ad714x, idx);
+ ad714x_wheel_cal_flt_pos(ad714x, idx);
+ input_report_abs(sw->input, ABS_WHEEL,
+ sw->flt_pos);
+ input_report_key(sw->input, BTN_TOUCH, 1);
+ } else {
+ /* When the user lifts off the sensor, configure
+ * the AD714X back to threshold interrupt mode.
+ */
+ ad714x_wheel_use_thr_int(ad714x, idx);
+ sw->state = IDLE;
+ input_report_key(sw->input, BTN_TOUCH, 0);
+
+ dev_dbg(ad714x->dev, "wheel %d released\n",
+ idx);
+ }
+ input_sync(sw->input);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void touchpad_cal_sensor_val(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+ int i;
+
+ ad714x->read(ad714x, CDC_RESULT_S0 + hw->x_start_stage,
+ &ad714x->adc_reg[hw->x_start_stage],
+ hw->x_end_stage - hw->x_start_stage + 1);
+
+ for (i = hw->x_start_stage; i <= hw->x_end_stage; i++) {
+ ad714x->read(ad714x, STAGE0_AMBIENT + i * PER_STAGE_REG_NUM,
+ &ad714x->amb_reg[i], 1);
+ if (ad714x->adc_reg[i] > ad714x->amb_reg[i])
+ ad714x->sensor_val[i] =
+ ad714x->adc_reg[i] - ad714x->amb_reg[i];
+ else
+ ad714x->sensor_val[i] = 0;
+ }
+}
+
+static void touchpad_cal_highest_stage(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+ struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
+
+ sw->x_highest_stage = ad714x_cal_highest_stage(ad714x,
+ hw->x_start_stage, hw->x_end_stage);
+ sw->y_highest_stage = ad714x_cal_highest_stage(ad714x,
+ hw->y_start_stage, hw->y_end_stage);
+
+ dev_dbg(ad714x->dev,
+ "touchpad %d x_highest_stage:%d, y_highest_stage:%d\n",
+ idx, sw->x_highest_stage, sw->y_highest_stage);
+}
+
+/*
+ * If 2 fingers are touching the sensor then 2 peaks can be observed in the
+ * distribution.
+ * The arithmetic doesn't support to get absolute coordinates for multi-touch
+ * yet.
+ */
+static int touchpad_check_second_peak(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+ struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
+ int i;
+
+ for (i = hw->x_start_stage; i < sw->x_highest_stage; i++) {
+ if ((ad714x->sensor_val[i] - ad714x->sensor_val[i + 1])
+ > (ad714x->sensor_val[i + 1] / 10))
+ return 1;
+ }
+
+ for (i = sw->x_highest_stage; i < hw->x_end_stage; i++) {
+ if ((ad714x->sensor_val[i + 1] - ad714x->sensor_val[i])
+ > (ad714x->sensor_val[i] / 10))
+ return 1;
+ }
+
+ for (i = hw->y_start_stage; i < sw->y_highest_stage; i++) {
+ if ((ad714x->sensor_val[i] - ad714x->sensor_val[i + 1])
+ > (ad714x->sensor_val[i + 1] / 10))
+ return 1;
+ }
+
+ for (i = sw->y_highest_stage; i < hw->y_end_stage; i++) {
+ if ((ad714x->sensor_val[i + 1] - ad714x->sensor_val[i])
+ > (ad714x->sensor_val[i] / 10))
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * If only one finger is used to activate the touch pad then only 1 peak will be
+ * registered in the distribution. This peak and the 2 adjacent sensors will be
+ * used in the calculation of the absolute position. This will prevent hand
+ * shadows to affect the absolute position calculation.
+ */
+static void touchpad_cal_abs_pos(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+ struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
+
+ sw->x_abs_pos = ad714x_cal_abs_pos(ad714x, hw->x_start_stage,
+ hw->x_end_stage, sw->x_highest_stage, hw->x_max_coord);
+ sw->y_abs_pos = ad714x_cal_abs_pos(ad714x, hw->y_start_stage,
+ hw->y_end_stage, sw->y_highest_stage, hw->y_max_coord);
+
+ dev_dbg(ad714x->dev, "touchpad %d absolute position:(%d, %d)\n", idx,
+ sw->x_abs_pos, sw->y_abs_pos);
+}
+
+static void touchpad_cal_flt_pos(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
+
+ sw->x_flt_pos = (sw->x_flt_pos * (10 - 4) +
+ sw->x_abs_pos * 4)/10;
+ sw->y_flt_pos = (sw->y_flt_pos * (10 - 4) +
+ sw->y_abs_pos * 4)/10;
+
+ dev_dbg(ad714x->dev, "touchpad %d filter position:(%d, %d)\n",
+ idx, sw->x_flt_pos, sw->y_flt_pos);
+}
+
+/*
+ * To prevent distortion from showing in the absolute position, it is
+ * necessary to detect the end points. When endpoints are detected, the
+ * driver stops updating the status variables with absolute positions.
+ * End points are detected on the 4 edges of the touchpad sensor. The
+ * method to detect them is the same for all 4.
+ * To detect the end points, the firmware computes the difference in
+ * percent between the sensor on the edge and the adjacent one. The
+ * difference is calculated in percent in order to make the end point
+ * detection independent of the pressure.
+ */
+
+#define LEFT_END_POINT_DETECTION_LEVEL 550
+#define RIGHT_END_POINT_DETECTION_LEVEL 750
+#define LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL 850
+#define TOP_END_POINT_DETECTION_LEVEL 550
+#define BOTTOM_END_POINT_DETECTION_LEVEL 950
+#define TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL 700
+static int touchpad_check_endpoint(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+ struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
+ int percent_sensor_diff;
+
+ /* left endpoint detect */
+ percent_sensor_diff = (ad714x->sensor_val[hw->x_start_stage] -
+ ad714x->sensor_val[hw->x_start_stage + 1]) * 100 /
+ ad714x->sensor_val[hw->x_start_stage + 1];
+ if (!sw->left_ep) {
+ if (percent_sensor_diff >= LEFT_END_POINT_DETECTION_LEVEL) {
+ sw->left_ep = 1;
+ sw->left_ep_val =
+ ad714x->sensor_val[hw->x_start_stage + 1];
+ }
+ } else {
+ if ((percent_sensor_diff < LEFT_END_POINT_DETECTION_LEVEL) &&
+ (ad714x->sensor_val[hw->x_start_stage + 1] >
+ LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw->left_ep_val))
+ sw->left_ep = 0;
+ }
+
+ /* right endpoint detect */
+ percent_sensor_diff = (ad714x->sensor_val[hw->x_end_stage] -
+ ad714x->sensor_val[hw->x_end_stage - 1]) * 100 /
+ ad714x->sensor_val[hw->x_end_stage - 1];
+ if (!sw->right_ep) {
+ if (percent_sensor_diff >= RIGHT_END_POINT_DETECTION_LEVEL) {
+ sw->right_ep = 1;
+ sw->right_ep_val =
+ ad714x->sensor_val[hw->x_end_stage - 1];
+ }
+ } else {
+ if ((percent_sensor_diff < RIGHT_END_POINT_DETECTION_LEVEL) &&
+ (ad714x->sensor_val[hw->x_end_stage - 1] >
+ LEFT_RIGHT_END_POINT_DEAVTIVALION_LEVEL + sw->right_ep_val))
+ sw->right_ep = 0;
+ }
+
+ /* top endpoint detect */
+ percent_sensor_diff = (ad714x->sensor_val[hw->y_start_stage] -
+ ad714x->sensor_val[hw->y_start_stage + 1]) * 100 /
+ ad714x->sensor_val[hw->y_start_stage + 1];
+ if (!sw->top_ep) {
+ if (percent_sensor_diff >= TOP_END_POINT_DETECTION_LEVEL) {
+ sw->top_ep = 1;
+ sw->top_ep_val =
+ ad714x->sensor_val[hw->y_start_stage + 1];
+ }
+ } else {
+ if ((percent_sensor_diff < TOP_END_POINT_DETECTION_LEVEL) &&
+ (ad714x->sensor_val[hw->y_start_stage + 1] >
+ TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw->top_ep_val))
+ sw->top_ep = 0;
+ }
+
+ /* bottom endpoint detect */
+ percent_sensor_diff = (ad714x->sensor_val[hw->y_end_stage] -
+ ad714x->sensor_val[hw->y_end_stage - 1]) * 100 /
+ ad714x->sensor_val[hw->y_end_stage - 1];
+ if (!sw->bottom_ep) {
+ if (percent_sensor_diff >= BOTTOM_END_POINT_DETECTION_LEVEL) {
+ sw->bottom_ep = 1;
+ sw->bottom_ep_val =
+ ad714x->sensor_val[hw->y_end_stage - 1];
+ }
+ } else {
+ if ((percent_sensor_diff < BOTTOM_END_POINT_DETECTION_LEVEL) &&
+ (ad714x->sensor_val[hw->y_end_stage - 1] >
+ TOP_BOTTOM_END_POINT_DEAVTIVALION_LEVEL + sw->bottom_ep_val))
+ sw->bottom_ep = 0;
+ }
+
+ return sw->left_ep || sw->right_ep || sw->top_ep || sw->bottom_ep;
+}
+
+static void touchpad_use_com_int(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+
+ ad714x_use_com_int(ad714x, hw->x_start_stage, hw->x_end_stage);
+}
+
+static void touchpad_use_thr_int(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+
+ ad714x_use_thr_int(ad714x, hw->x_start_stage, hw->x_end_stage);
+ ad714x_use_thr_int(ad714x, hw->y_start_stage, hw->y_end_stage);
+}
+
+static void ad714x_touchpad_state_machine(struct ad714x_chip *ad714x, int idx)
+{
+ struct ad714x_touchpad_plat *hw = &ad714x->hw->touchpad[idx];
+ struct ad714x_touchpad_drv *sw = &ad714x->sw->touchpad[idx];
+ unsigned short h_state, c_state;
+ unsigned short mask;
+
+ mask = (((1 << (hw->x_end_stage + 1)) - 1) -
+ ((1 << hw->x_start_stage) - 1)) +
+ (((1 << (hw->y_end_stage + 1)) - 1) -
+ ((1 << hw->y_start_stage) - 1));
+
+ h_state = ad714x->h_state & mask;
+ c_state = ad714x->c_state & mask;
+
+ switch (sw->state) {
+ case IDLE:
+ if (h_state) {
+ sw->state = JITTER;
+ /* In End of Conversion interrupt mode, the AD714X
+ * continuously generates hardware interrupts.
+ */
+ touchpad_use_com_int(ad714x, idx);
+ dev_dbg(ad714x->dev, "touchpad %d touched\n", idx);
+ }
+ break;
+
+ case JITTER:
+ if (c_state == mask) {
+ touchpad_cal_sensor_val(ad714x, idx);
+ touchpad_cal_highest_stage(ad714x, idx);
+ if ((!touchpad_check_second_peak(ad714x, idx)) &&
+ (!touchpad_check_endpoint(ad714x, idx))) {
+ dev_dbg(ad714x->dev,
+ "touchpad%d, 2 fingers or endpoint\n",
+ idx);
+ touchpad_cal_abs_pos(ad714x, idx);
+ sw->x_flt_pos = sw->x_abs_pos;
+ sw->y_flt_pos = sw->y_abs_pos;
+ sw->state = ACTIVE;
+ }
+ }
+ break;
+
+ case ACTIVE:
+ if (c_state == mask) {
+ if (h_state) {
+ touchpad_cal_sensor_val(ad714x, idx);
+ touchpad_cal_highest_stage(ad714x, idx);
+ if ((!touchpad_check_second_peak(ad714x, idx))
+ && (!touchpad_check_endpoint(ad714x, idx))) {
+ touchpad_cal_abs_pos(ad714x, idx);
+ touchpad_cal_flt_pos(ad714x, idx);
+ input_report_abs(sw->input, ABS_X,
+ sw->x_flt_pos);
+ input_report_abs(sw->input, ABS_Y,
+ sw->y_flt_pos);
+ input_report_key(sw->input, BTN_TOUCH,
+ 1);
+ }
+ } else {
+ /* When the user lifts off the sensor, configure
+ * the AD714X back to threshold interrupt mode.
+ */
+ touchpad_use_thr_int(ad714x, idx);
+ sw->state = IDLE;
+ input_report_key(sw->input, BTN_TOUCH, 0);
+ dev_dbg(ad714x->dev, "touchpad %d released\n",
+ idx);
+ }
+ input_sync(sw->input);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int ad714x_hw_detect(struct ad714x_chip *ad714x)
+{
+ unsigned short data;
+
+ ad714x->read(ad714x, AD714X_PARTID_REG, &data, 1);
+ switch (data & 0xFFF0) {
+ case AD7142_PARTID:
+ ad714x->product = 0x7142;
+ ad714x->version = data & 0xF;
+ dev_info(ad714x->dev, "found AD7142 captouch, rev:%d\n",
+ ad714x->version);
+ return 0;
+
+ case AD7143_PARTID:
+ ad714x->product = 0x7143;
+ ad714x->version = data & 0xF;
+ dev_info(ad714x->dev, "found AD7143 captouch, rev:%d\n",
+ ad714x->version);
+ return 0;
+
+ case AD7147_PARTID:
+ ad714x->product = 0x7147;
+ ad714x->version = data & 0xF;
+ dev_info(ad714x->dev, "found AD7147(A) captouch, rev:%d\n",
+ ad714x->version);
+ return 0;
+
+ case AD7148_PARTID:
+ ad714x->product = 0x7148;
+ ad714x->version = data & 0xF;
+ dev_info(ad714x->dev, "found AD7148 captouch, rev:%d\n",
+ ad714x->version);
+ return 0;
+
+ default:
+ dev_err(ad714x->dev,
+ "fail to detect AD714X captouch, read ID is %04x\n",
+ data);
+ return -ENODEV;
+ }
+}
+
+static void ad714x_hw_init(struct ad714x_chip *ad714x)
+{
+ int i, j;
+ unsigned short reg_base;
+ unsigned short data;
+
+ /* configuration CDC and interrupts */
+
+ for (i = 0; i < STAGE_NUM; i++) {
+ reg_base = AD714X_STAGECFG_REG + i * STAGE_CFGREG_NUM;
+ for (j = 0; j < STAGE_CFGREG_NUM; j++)
+ ad714x->write(ad714x, reg_base + j,
+ ad714x->hw->stage_cfg_reg[i][j]);
+ }
+
+ for (i = 0; i < SYS_CFGREG_NUM; i++)
+ ad714x->write(ad714x, AD714X_SYSCFG_REG + i,
+ ad714x->hw->sys_cfg_reg[i]);
+ for (i = 0; i < SYS_CFGREG_NUM; i++)
+ ad714x->read(ad714x, AD714X_SYSCFG_REG + i, &data, 1);
+
+ ad714x->write(ad714x, AD714X_STG_CAL_EN_REG, 0xFFF);
+
+ /* clear all interrupts */
+ ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3);
+}
+
+static irqreturn_t ad714x_interrupt_thread(int irq, void *data)
+{
+ struct ad714x_chip *ad714x = data;
+ int i;
+
+ mutex_lock(&ad714x->mutex);
+
+ ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3);
+
+ for (i = 0; i < ad714x->hw->button_num; i++)
+ ad714x_button_state_machine(ad714x, i);
+ for (i = 0; i < ad714x->hw->slider_num; i++)
+ ad714x_slider_state_machine(ad714x, i);
+ for (i = 0; i < ad714x->hw->wheel_num; i++)
+ ad714x_wheel_state_machine(ad714x, i);
+ for (i = 0; i < ad714x->hw->touchpad_num; i++)
+ ad714x_touchpad_state_machine(ad714x, i);
+
+ mutex_unlock(&ad714x->mutex);
+
+ return IRQ_HANDLED;
+}
+
+#define MAX_DEVICE_NUM 8
+struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
+ ad714x_read_t read, ad714x_write_t write)
+{
+ int i, alloc_idx;
+ int error;
+ struct input_dev *input[MAX_DEVICE_NUM];
+
+ struct ad714x_platform_data *plat_data = dev->platform_data;
+ struct ad714x_chip *ad714x;
+ void *drv_mem;
+ unsigned long irqflags;
+
+ struct ad714x_button_drv *bt_drv;
+ struct ad714x_slider_drv *sd_drv;
+ struct ad714x_wheel_drv *wl_drv;
+ struct ad714x_touchpad_drv *tp_drv;
+
+
+ if (irq <= 0) {
+ dev_err(dev, "IRQ not configured!\n");
+ error = -EINVAL;
+ goto err_out;
+ }
+
+ if (dev->platform_data == NULL) {
+ dev_err(dev, "platform data for ad714x doesn't exist\n");
+ error = -EINVAL;
+ goto err_out;
+ }
+
+ ad714x = kzalloc(sizeof(*ad714x) + sizeof(*ad714x->sw) +
+ sizeof(*sd_drv) * plat_data->slider_num +
+ sizeof(*wl_drv) * plat_data->wheel_num +
+ sizeof(*tp_drv) * plat_data->touchpad_num +
+ sizeof(*bt_drv) * plat_data->button_num, GFP_KERNEL);
+ if (!ad714x) {
+ error = -ENOMEM;
+ goto err_out;
+ }
+
+ ad714x->hw = plat_data;
+
+ drv_mem = ad714x + 1;
+ ad714x->sw = drv_mem;
+ drv_mem += sizeof(*ad714x->sw);
+ ad714x->sw->slider = sd_drv = drv_mem;
+ drv_mem += sizeof(*sd_drv) * ad714x->hw->slider_num;
+ ad714x->sw->wheel = wl_drv = drv_mem;
+ drv_mem += sizeof(*wl_drv) * ad714x->hw->wheel_num;
+ ad714x->sw->touchpad = tp_drv = drv_mem;
+ drv_mem += sizeof(*tp_drv) * ad714x->hw->touchpad_num;
+ ad714x->sw->button = bt_drv = drv_mem;
+ drv_mem += sizeof(*bt_drv) * ad714x->hw->button_num;
+
+ ad714x->read = read;
+ ad714x->write = write;
+ ad714x->irq = irq;
+ ad714x->dev = dev;
+
+ error = ad714x_hw_detect(ad714x);
+ if (error)
+ goto err_free_mem;
+
+ /* initialize and request sw/hw resources */
+
+ ad714x_hw_init(ad714x);
+ mutex_init(&ad714x->mutex);
+
+ /*
+ * Allocate and register AD714X input device
+ */
+ alloc_idx = 0;
+
+ /* a slider uses one input_dev instance */
+ if (ad714x->hw->slider_num > 0) {
+ struct ad714x_slider_plat *sd_plat = ad714x->hw->slider;
+
+ for (i = 0; i < ad714x->hw->slider_num; i++) {
+ sd_drv[i].input = input[alloc_idx] = input_allocate_device();
+ if (!input[alloc_idx]) {
+ error = -ENOMEM;
+ goto err_free_dev;
+ }
+
+ __set_bit(EV_ABS, input[alloc_idx]->evbit);
+ __set_bit(EV_KEY, input[alloc_idx]->evbit);
+ __set_bit(ABS_X, input[alloc_idx]->absbit);
+ __set_bit(BTN_TOUCH, input[alloc_idx]->keybit);
+ input_set_abs_params(input[alloc_idx],
+ ABS_X, 0, sd_plat->max_coord, 0, 0);
+
+ input[alloc_idx]->id.bustype = bus_type;
+ input[alloc_idx]->id.product = ad714x->product;
+ input[alloc_idx]->id.version = ad714x->version;
+ input[alloc_idx]->name = "ad714x_captouch_slider";
+ input[alloc_idx]->dev.parent = dev;
+
+ error = input_register_device(input[alloc_idx]);
+ if (error)
+ goto err_free_dev;
+
+ alloc_idx++;
+ }
+ }
+
+ /* a wheel uses one input_dev instance */
+ if (ad714x->hw->wheel_num > 0) {
+ struct ad714x_wheel_plat *wl_plat = ad714x->hw->wheel;
+
+ for (i = 0; i < ad714x->hw->wheel_num; i++) {
+ wl_drv[i].input = input[alloc_idx] = input_allocate_device();
+ if (!input[alloc_idx]) {
+ error = -ENOMEM;
+ goto err_free_dev;
+ }
+
+ __set_bit(EV_KEY, input[alloc_idx]->evbit);
+ __set_bit(EV_ABS, input[alloc_idx]->evbit);
+ __set_bit(ABS_WHEEL, input[alloc_idx]->absbit);
+ __set_bit(BTN_TOUCH, input[alloc_idx]->keybit);
+ input_set_abs_params(input[alloc_idx],
+ ABS_WHEEL, 0, wl_plat->max_coord, 0, 0);
+
+ input[alloc_idx]->id.bustype = bus_type;
+ input[alloc_idx]->id.product = ad714x->product;
+ input[alloc_idx]->id.version = ad714x->version;
+ input[alloc_idx]->name = "ad714x_captouch_wheel";
+ input[alloc_idx]->dev.parent = dev;
+
+ error = input_register_device(input[alloc_idx]);
+ if (error)
+ goto err_free_dev;
+
+ alloc_idx++;
+ }
+ }
+
+ /* a touchpad uses one input_dev instance */
+ if (ad714x->hw->touchpad_num > 0) {
+ struct ad714x_touchpad_plat *tp_plat = ad714x->hw->touchpad;
+
+ for (i = 0; i < ad714x->hw->touchpad_num; i++) {
+ tp_drv[i].input = input[alloc_idx] = input_allocate_device();
+ if (!input[alloc_idx]) {
+ error = -ENOMEM;
+ goto err_free_dev;
+ }
+
+ __set_bit(EV_ABS, input[alloc_idx]->evbit);
+ __set_bit(EV_KEY, input[alloc_idx]->evbit);
+ __set_bit(ABS_X, input[alloc_idx]->absbit);
+ __set_bit(ABS_Y, input[alloc_idx]->absbit);
+ __set_bit(BTN_TOUCH, input[alloc_idx]->keybit);
+ input_set_abs_params(input[alloc_idx],
+ ABS_X, 0, tp_plat->x_max_coord, 0, 0);
+ input_set_abs_params(input[alloc_idx],
+ ABS_Y, 0, tp_plat->y_max_coord, 0, 0);
+
+ input[alloc_idx]->id.bustype = bus_type;
+ input[alloc_idx]->id.product = ad714x->product;
+ input[alloc_idx]->id.version = ad714x->version;
+ input[alloc_idx]->name = "ad714x_captouch_pad";
+ input[alloc_idx]->dev.parent = dev;
+
+ error = input_register_device(input[alloc_idx]);
+ if (error)
+ goto err_free_dev;
+
+ alloc_idx++;
+ }
+ }
+
+ /* all buttons use one input node */
+ if (ad714x->hw->button_num > 0) {
+ struct ad714x_button_plat *bt_plat = ad714x->hw->button;
+
+ input[alloc_idx] = input_allocate_device();
+ if (!input[alloc_idx]) {
+ error = -ENOMEM;
+ goto err_free_dev;
+ }
+
+ __set_bit(EV_KEY, input[alloc_idx]->evbit);
+ for (i = 0; i < ad714x->hw->button_num; i++) {
+ bt_drv[i].input = input[alloc_idx];
+ __set_bit(bt_plat[i].keycode, input[alloc_idx]->keybit);
+ }
+
+ input[alloc_idx]->id.bustype = bus_type;
+ input[alloc_idx]->id.product = ad714x->product;
+ input[alloc_idx]->id.version = ad714x->version;
+ input[alloc_idx]->name = "ad714x_captouch_button";
+ input[alloc_idx]->dev.parent = dev;
+
+ error = input_register_device(input[alloc_idx]);
+ if (error)
+ goto err_free_dev;
+
+ alloc_idx++;
+ }
+
+ irqflags = plat_data->irqflags ?: IRQF_TRIGGER_FALLING;
+ irqflags |= IRQF_ONESHOT;
+
+ error = request_threaded_irq(ad714x->irq, NULL, ad714x_interrupt_thread,
+ irqflags, "ad714x_captouch", ad714x);
+ if (error) {
+ dev_err(dev, "can't allocate irq %d\n", ad714x->irq);
+ goto err_unreg_dev;
+ }
+
+ return ad714x;
+
+ err_free_dev:
+ dev_err(dev, "failed to setup AD714x input device %i\n", alloc_idx);
+ input_free_device(input[alloc_idx]);
+ err_unreg_dev:
+ while (--alloc_idx >= 0)
+ input_unregister_device(input[alloc_idx]);
+ err_free_mem:
+ kfree(ad714x);
+ err_out:
+ return ERR_PTR(error);
+}
+EXPORT_SYMBOL(ad714x_probe);
+
+void ad714x_remove(struct ad714x_chip *ad714x)
+{
+ struct ad714x_platform_data *hw = ad714x->hw;
+ struct ad714x_driver_data *sw = ad714x->sw;
+ int i;
+
+ free_irq(ad714x->irq, ad714x);
+
+ /* unregister and free all input devices */
+
+ for (i = 0; i < hw->slider_num; i++)
+ input_unregister_device(sw->slider[i].input);
+
+ for (i = 0; i < hw->wheel_num; i++)
+ input_unregister_device(sw->wheel[i].input);
+
+ for (i = 0; i < hw->touchpad_num; i++)
+ input_unregister_device(sw->touchpad[i].input);
+
+ if (hw->button_num)
+ input_unregister_device(sw->button[0].input);
+
+ kfree(ad714x);
+}
+EXPORT_SYMBOL(ad714x_remove);
+
+#ifdef CONFIG_PM
+int ad714x_disable(struct ad714x_chip *ad714x)
+{
+ unsigned short data;
+
+ dev_dbg(ad714x->dev, "%s enter\n", __func__);
+
+ mutex_lock(&ad714x->mutex);
+
+ data = ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL] | 0x3;
+ ad714x->write(ad714x, AD714X_PWR_CTRL, data);
+
+ mutex_unlock(&ad714x->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ad714x_disable);
+
+int ad714x_enable(struct ad714x_chip *ad714x)
+{
+ dev_dbg(ad714x->dev, "%s enter\n", __func__);
+
+ mutex_lock(&ad714x->mutex);
+
+ /* resume to non-shutdown mode */
+
+ ad714x->write(ad714x, AD714X_PWR_CTRL,
+ ad714x->hw->sys_cfg_reg[AD714X_PWR_CTRL]);
+
+ /* make sure the interrupt output line is not low level after resume,
+ * otherwise we will get no chance to enter falling-edge irq again
+ */
+
+ ad714x->read(ad714x, STG_LOW_INT_STA_REG, &ad714x->l_state, 3);
+
+ mutex_unlock(&ad714x->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL(ad714x_enable);
+#endif
+
+MODULE_DESCRIPTION("Analog Devices AD714X Capacitance Touch Sensor Driver");
+MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ad714x.h b/drivers/input/misc/ad714x.h
new file mode 100644
index 00000000..3c85455a
--- /dev/null
+++ b/drivers/input/misc/ad714x.h
@@ -0,0 +1,55 @@
+/*
+ * AD714X CapTouch Programmable Controller driver (bus interfaces)
+ *
+ * Copyright 2009-2011 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _AD714X_H_
+#define _AD714X_H_
+
+#include <linux/types.h>
+
+#define STAGE_NUM 12
+
+struct device;
+struct ad714x_platform_data;
+struct ad714x_driver_data;
+struct ad714x_chip;
+
+typedef int (*ad714x_read_t)(struct ad714x_chip *, unsigned short, unsigned short *, size_t);
+typedef int (*ad714x_write_t)(struct ad714x_chip *, unsigned short, unsigned short);
+
+struct ad714x_chip {
+ unsigned short l_state;
+ unsigned short h_state;
+ unsigned short c_state;
+ unsigned short adc_reg[STAGE_NUM];
+ unsigned short amb_reg[STAGE_NUM];
+ unsigned short sensor_val[STAGE_NUM];
+
+ struct ad714x_platform_data *hw;
+ struct ad714x_driver_data *sw;
+
+ int irq;
+ struct device *dev;
+ ad714x_read_t read;
+ ad714x_write_t write;
+
+ struct mutex mutex;
+
+ unsigned product;
+ unsigned version;
+
+ __be16 xfer_buf[16] ____cacheline_aligned;
+
+};
+
+int ad714x_disable(struct ad714x_chip *ad714x);
+int ad714x_enable(struct ad714x_chip *ad714x);
+struct ad714x_chip *ad714x_probe(struct device *dev, u16 bus_type, int irq,
+ ad714x_read_t read, ad714x_write_t write);
+void ad714x_remove(struct ad714x_chip *ad714x);
+
+#endif
diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c
new file mode 100644
index 00000000..416f47dd
--- /dev/null
+++ b/drivers/input/misc/adxl34x-i2c.c
@@ -0,0 +1,155 @@
+/*
+ * ADLX345/346 Three-Axis Digital Accelerometers (I2C Interface)
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h> /* BUS_I2C */
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/pm.h>
+#include "adxl34x.h"
+
+static int adxl34x_smbus_read(struct device *dev, unsigned char reg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int adxl34x_smbus_write(struct device *dev,
+ unsigned char reg, unsigned char val)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static int adxl34x_smbus_read_block(struct device *dev,
+ unsigned char reg, int count,
+ void *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ return i2c_smbus_read_i2c_block_data(client, reg, count, buf);
+}
+
+static int adxl34x_i2c_read_block(struct device *dev,
+ unsigned char reg, int count,
+ void *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+
+ ret = i2c_master_send(client, &reg, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_master_recv(client, buf, count);
+ if (ret < 0)
+ return ret;
+
+ if (ret != count)
+ return -EIO;
+
+ return 0;
+}
+
+static const struct adxl34x_bus_ops adxl34x_smbus_bops = {
+ .bustype = BUS_I2C,
+ .write = adxl34x_smbus_write,
+ .read = adxl34x_smbus_read,
+ .read_block = adxl34x_smbus_read_block,
+};
+
+static const struct adxl34x_bus_ops adxl34x_i2c_bops = {
+ .bustype = BUS_I2C,
+ .write = adxl34x_smbus_write,
+ .read = adxl34x_smbus_read,
+ .read_block = adxl34x_i2c_read_block,
+};
+
+static int adxl34x_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adxl34x *ac;
+ int error;
+
+ error = i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA);
+ if (!error) {
+ dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
+ return -EIO;
+ }
+
+ ac = adxl34x_probe(&client->dev, client->irq, false,
+ i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK) ?
+ &adxl34x_smbus_bops : &adxl34x_i2c_bops);
+ if (IS_ERR(ac))
+ return PTR_ERR(ac);
+
+ i2c_set_clientdata(client, ac);
+
+ return 0;
+}
+
+static int adxl34x_i2c_remove(struct i2c_client *client)
+{
+ struct adxl34x *ac = i2c_get_clientdata(client);
+
+ return adxl34x_remove(ac);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int adxl34x_i2c_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adxl34x *ac = i2c_get_clientdata(client);
+
+ adxl34x_suspend(ac);
+
+ return 0;
+}
+
+static int adxl34x_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adxl34x *ac = i2c_get_clientdata(client);
+
+ adxl34x_resume(ac);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(adxl34x_i2c_pm, adxl34x_i2c_suspend,
+ adxl34x_i2c_resume);
+
+static const struct i2c_device_id adxl34x_id[] = {
+ { "adxl34x", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, adxl34x_id);
+
+static struct i2c_driver adxl34x_driver = {
+ .driver = {
+ .name = "adxl34x",
+ .owner = THIS_MODULE,
+ .pm = &adxl34x_i2c_pm,
+ },
+ .probe = adxl34x_i2c_probe,
+ .remove = adxl34x_i2c_remove,
+ .id_table = adxl34x_id,
+};
+
+module_i2c_driver(adxl34x_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer I2C Bus Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c
new file mode 100644
index 00000000..76dc0679
--- /dev/null
+++ b/drivers/input/misc/adxl34x-spi.c
@@ -0,0 +1,136 @@
+/*
+ * ADLX345/346 Three-Axis Digital Accelerometers (SPI Interface)
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/input.h> /* BUS_SPI */
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/pm.h>
+#include <linux/types.h>
+#include "adxl34x.h"
+
+#define MAX_SPI_FREQ_HZ 5000000
+#define MAX_FREQ_NO_FIFODELAY 1500000
+#define ADXL34X_CMD_MULTB (1 << 6)
+#define ADXL34X_CMD_READ (1 << 7)
+#define ADXL34X_WRITECMD(reg) (reg & 0x3F)
+#define ADXL34X_READCMD(reg) (ADXL34X_CMD_READ | (reg & 0x3F))
+#define ADXL34X_READMB_CMD(reg) (ADXL34X_CMD_READ | ADXL34X_CMD_MULTB \
+ | (reg & 0x3F))
+
+static int adxl34x_spi_read(struct device *dev, unsigned char reg)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char cmd;
+
+ cmd = ADXL34X_READCMD(reg);
+
+ return spi_w8r8(spi, cmd);
+}
+
+static int adxl34x_spi_write(struct device *dev,
+ unsigned char reg, unsigned char val)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ unsigned char buf[2];
+
+ buf[0] = ADXL34X_WRITECMD(reg);
+ buf[1] = val;
+
+ return spi_write(spi, buf, sizeof(buf));
+}
+
+static int adxl34x_spi_read_block(struct device *dev,
+ unsigned char reg, int count,
+ void *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ ssize_t status;
+
+ reg = ADXL34X_READMB_CMD(reg);
+ status = spi_write_then_read(spi, &reg, 1, buf, count);
+
+ return (status < 0) ? status : 0;
+}
+
+static const struct adxl34x_bus_ops adxl34x_spi_bops = {
+ .bustype = BUS_SPI,
+ .write = adxl34x_spi_write,
+ .read = adxl34x_spi_read,
+ .read_block = adxl34x_spi_read_block,
+};
+
+static int adxl34x_spi_probe(struct spi_device *spi)
+{
+ struct adxl34x *ac;
+
+ /* don't exceed max specified SPI CLK frequency */
+ if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
+ dev_err(&spi->dev, "SPI CLK %d Hz too fast\n", spi->max_speed_hz);
+ return -EINVAL;
+ }
+
+ ac = adxl34x_probe(&spi->dev, spi->irq,
+ spi->max_speed_hz > MAX_FREQ_NO_FIFODELAY,
+ &adxl34x_spi_bops);
+
+ if (IS_ERR(ac))
+ return PTR_ERR(ac);
+
+ spi_set_drvdata(spi, ac);
+
+ return 0;
+}
+
+static int adxl34x_spi_remove(struct spi_device *spi)
+{
+ struct adxl34x *ac = spi_get_drvdata(spi);
+
+ return adxl34x_remove(ac);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int adxl34x_spi_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct adxl34x *ac = spi_get_drvdata(spi);
+
+ adxl34x_suspend(ac);
+
+ return 0;
+}
+
+static int adxl34x_spi_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct adxl34x *ac = spi_get_drvdata(spi);
+
+ adxl34x_resume(ac);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(adxl34x_spi_pm, adxl34x_spi_suspend,
+ adxl34x_spi_resume);
+
+static struct spi_driver adxl34x_driver = {
+ .driver = {
+ .name = "adxl34x",
+ .owner = THIS_MODULE,
+ .pm = &adxl34x_spi_pm,
+ },
+ .probe = adxl34x_spi_probe,
+ .remove = adxl34x_spi_remove,
+};
+
+module_spi_driver(adxl34x_driver);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer SPI Bus Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c
new file mode 100644
index 00000000..0735de3a
--- /dev/null
+++ b/drivers/input/misc/adxl34x.c
@@ -0,0 +1,914 @@
+/*
+ * ADXL345/346 Three-Axis Digital Accelerometers
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/input/adxl34x.h>
+#include <linux/module.h>
+
+#include "adxl34x.h"
+
+/* ADXL345/6 Register Map */
+#define DEVID 0x00 /* R Device ID */
+#define THRESH_TAP 0x1D /* R/W Tap threshold */
+#define OFSX 0x1E /* R/W X-axis offset */
+#define OFSY 0x1F /* R/W Y-axis offset */
+#define OFSZ 0x20 /* R/W Z-axis offset */
+#define DUR 0x21 /* R/W Tap duration */
+#define LATENT 0x22 /* R/W Tap latency */
+#define WINDOW 0x23 /* R/W Tap window */
+#define THRESH_ACT 0x24 /* R/W Activity threshold */
+#define THRESH_INACT 0x25 /* R/W Inactivity threshold */
+#define TIME_INACT 0x26 /* R/W Inactivity time */
+#define ACT_INACT_CTL 0x27 /* R/W Axis enable control for activity and */
+ /* inactivity detection */
+#define THRESH_FF 0x28 /* R/W Free-fall threshold */
+#define TIME_FF 0x29 /* R/W Free-fall time */
+#define TAP_AXES 0x2A /* R/W Axis control for tap/double tap */
+#define ACT_TAP_STATUS 0x2B /* R Source of tap/double tap */
+#define BW_RATE 0x2C /* R/W Data rate and power mode control */
+#define POWER_CTL 0x2D /* R/W Power saving features control */
+#define INT_ENABLE 0x2E /* R/W Interrupt enable control */
+#define INT_MAP 0x2F /* R/W Interrupt mapping control */
+#define INT_SOURCE 0x30 /* R Source of interrupts */
+#define DATA_FORMAT 0x31 /* R/W Data format control */
+#define DATAX0 0x32 /* R X-Axis Data 0 */
+#define DATAX1 0x33 /* R X-Axis Data 1 */
+#define DATAY0 0x34 /* R Y-Axis Data 0 */
+#define DATAY1 0x35 /* R Y-Axis Data 1 */
+#define DATAZ0 0x36 /* R Z-Axis Data 0 */
+#define DATAZ1 0x37 /* R Z-Axis Data 1 */
+#define FIFO_CTL 0x38 /* R/W FIFO control */
+#define FIFO_STATUS 0x39 /* R FIFO status */
+#define TAP_SIGN 0x3A /* R Sign and source for tap/double tap */
+/* Orientation ADXL346 only */
+#define ORIENT_CONF 0x3B /* R/W Orientation configuration */
+#define ORIENT 0x3C /* R Orientation status */
+
+/* DEVIDs */
+#define ID_ADXL345 0xE5
+#define ID_ADXL346 0xE6
+
+/* INT_ENABLE/INT_MAP/INT_SOURCE Bits */
+#define DATA_READY (1 << 7)
+#define SINGLE_TAP (1 << 6)
+#define DOUBLE_TAP (1 << 5)
+#define ACTIVITY (1 << 4)
+#define INACTIVITY (1 << 3)
+#define FREE_FALL (1 << 2)
+#define WATERMARK (1 << 1)
+#define OVERRUN (1 << 0)
+
+/* ACT_INACT_CONTROL Bits */
+#define ACT_ACDC (1 << 7)
+#define ACT_X_EN (1 << 6)
+#define ACT_Y_EN (1 << 5)
+#define ACT_Z_EN (1 << 4)
+#define INACT_ACDC (1 << 3)
+#define INACT_X_EN (1 << 2)
+#define INACT_Y_EN (1 << 1)
+#define INACT_Z_EN (1 << 0)
+
+/* TAP_AXES Bits */
+#define SUPPRESS (1 << 3)
+#define TAP_X_EN (1 << 2)
+#define TAP_Y_EN (1 << 1)
+#define TAP_Z_EN (1 << 0)
+
+/* ACT_TAP_STATUS Bits */
+#define ACT_X_SRC (1 << 6)
+#define ACT_Y_SRC (1 << 5)
+#define ACT_Z_SRC (1 << 4)
+#define ASLEEP (1 << 3)
+#define TAP_X_SRC (1 << 2)
+#define TAP_Y_SRC (1 << 1)
+#define TAP_Z_SRC (1 << 0)
+
+/* BW_RATE Bits */
+#define LOW_POWER (1 << 4)
+#define RATE(x) ((x) & 0xF)
+
+/* POWER_CTL Bits */
+#define PCTL_LINK (1 << 5)
+#define PCTL_AUTO_SLEEP (1 << 4)
+#define PCTL_MEASURE (1 << 3)
+#define PCTL_SLEEP (1 << 2)
+#define PCTL_WAKEUP(x) ((x) & 0x3)
+
+/* DATA_FORMAT Bits */
+#define SELF_TEST (1 << 7)
+#define SPI (1 << 6)
+#define INT_INVERT (1 << 5)
+#define FULL_RES (1 << 3)
+#define JUSTIFY (1 << 2)
+#define RANGE(x) ((x) & 0x3)
+#define RANGE_PM_2g 0
+#define RANGE_PM_4g 1
+#define RANGE_PM_8g 2
+#define RANGE_PM_16g 3
+
+/*
+ * Maximum value our axis may get in full res mode for the input device
+ * (signed 13 bits)
+ */
+#define ADXL_FULLRES_MAX_VAL 4096
+
+/*
+ * Maximum value our axis may get in fixed res mode for the input device
+ * (signed 10 bits)
+ */
+#define ADXL_FIXEDRES_MAX_VAL 512
+
+/* FIFO_CTL Bits */
+#define FIFO_MODE(x) (((x) & 0x3) << 6)
+#define FIFO_BYPASS 0
+#define FIFO_FIFO 1
+#define FIFO_STREAM 2
+#define FIFO_TRIGGER 3
+#define TRIGGER (1 << 5)
+#define SAMPLES(x) ((x) & 0x1F)
+
+/* FIFO_STATUS Bits */
+#define FIFO_TRIG (1 << 7)
+#define ENTRIES(x) ((x) & 0x3F)
+
+/* TAP_SIGN Bits ADXL346 only */
+#define XSIGN (1 << 6)
+#define YSIGN (1 << 5)
+#define ZSIGN (1 << 4)
+#define XTAP (1 << 3)
+#define YTAP (1 << 2)
+#define ZTAP (1 << 1)
+
+/* ORIENT_CONF ADXL346 only */
+#define ORIENT_DEADZONE(x) (((x) & 0x7) << 4)
+#define ORIENT_DIVISOR(x) ((x) & 0x7)
+
+/* ORIENT ADXL346 only */
+#define ADXL346_2D_VALID (1 << 6)
+#define ADXL346_2D_ORIENT(x) (((x) & 0x3) >> 4)
+#define ADXL346_3D_VALID (1 << 3)
+#define ADXL346_3D_ORIENT(x) ((x) & 0x7)
+#define ADXL346_2D_PORTRAIT_POS 0 /* +X */
+#define ADXL346_2D_PORTRAIT_NEG 1 /* -X */
+#define ADXL346_2D_LANDSCAPE_POS 2 /* +Y */
+#define ADXL346_2D_LANDSCAPE_NEG 3 /* -Y */
+
+#define ADXL346_3D_FRONT 3 /* +X */
+#define ADXL346_3D_BACK 4 /* -X */
+#define ADXL346_3D_RIGHT 2 /* +Y */
+#define ADXL346_3D_LEFT 5 /* -Y */
+#define ADXL346_3D_TOP 1 /* +Z */
+#define ADXL346_3D_BOTTOM 6 /* -Z */
+
+#undef ADXL_DEBUG
+
+#define ADXL_X_AXIS 0
+#define ADXL_Y_AXIS 1
+#define ADXL_Z_AXIS 2
+
+#define AC_READ(ac, reg) ((ac)->bops->read((ac)->dev, reg))
+#define AC_WRITE(ac, reg, val) ((ac)->bops->write((ac)->dev, reg, val))
+
+struct axis_triple {
+ int x;
+ int y;
+ int z;
+};
+
+struct adxl34x {
+ struct device *dev;
+ struct input_dev *input;
+ struct mutex mutex; /* reentrant protection for struct */
+ struct adxl34x_platform_data pdata;
+ struct axis_triple swcal;
+ struct axis_triple hwcal;
+ struct axis_triple saved;
+ char phys[32];
+ unsigned orient2d_saved;
+ unsigned orient3d_saved;
+ bool disabled; /* P: mutex */
+ bool opened; /* P: mutex */
+ bool suspended; /* P: mutex */
+ bool fifo_delay;
+ int irq;
+ unsigned model;
+ unsigned int_mask;
+
+ const struct adxl34x_bus_ops *bops;
+};
+
+static const struct adxl34x_platform_data adxl34x_default_init = {
+ .tap_threshold = 35,
+ .tap_duration = 3,
+ .tap_latency = 20,
+ .tap_window = 20,
+ .tap_axis_control = ADXL_TAP_X_EN | ADXL_TAP_Y_EN | ADXL_TAP_Z_EN,
+ .act_axis_control = 0xFF,
+ .activity_threshold = 6,
+ .inactivity_threshold = 4,
+ .inactivity_time = 3,
+ .free_fall_threshold = 8,
+ .free_fall_time = 0x20,
+ .data_rate = 8,
+ .data_range = ADXL_FULL_RES,
+
+ .ev_type = EV_ABS,
+ .ev_code_x = ABS_X, /* EV_REL */
+ .ev_code_y = ABS_Y, /* EV_REL */
+ .ev_code_z = ABS_Z, /* EV_REL */
+
+ .ev_code_tap = {BTN_TOUCH, BTN_TOUCH, BTN_TOUCH}, /* EV_KEY {x,y,z} */
+ .power_mode = ADXL_AUTO_SLEEP | ADXL_LINK,
+ .fifo_mode = ADXL_FIFO_STREAM,
+ .watermark = 0,
+};
+
+static void adxl34x_get_triple(struct adxl34x *ac, struct axis_triple *axis)
+{
+ short buf[3];
+
+ ac->bops->read_block(ac->dev, DATAX0, DATAZ1 - DATAX0 + 1, buf);
+
+ mutex_lock(&ac->mutex);
+ ac->saved.x = (s16) le16_to_cpu(buf[0]);
+ axis->x = ac->saved.x;
+
+ ac->saved.y = (s16) le16_to_cpu(buf[1]);
+ axis->y = ac->saved.y;
+
+ ac->saved.z = (s16) le16_to_cpu(buf[2]);
+ axis->z = ac->saved.z;
+ mutex_unlock(&ac->mutex);
+}
+
+static void adxl34x_service_ev_fifo(struct adxl34x *ac)
+{
+ struct adxl34x_platform_data *pdata = &ac->pdata;
+ struct axis_triple axis;
+
+ adxl34x_get_triple(ac, &axis);
+
+ input_event(ac->input, pdata->ev_type, pdata->ev_code_x,
+ axis.x - ac->swcal.x);
+ input_event(ac->input, pdata->ev_type, pdata->ev_code_y,
+ axis.y - ac->swcal.y);
+ input_event(ac->input, pdata->ev_type, pdata->ev_code_z,
+ axis.z - ac->swcal.z);
+}
+
+static void adxl34x_report_key_single(struct input_dev *input, int key)
+{
+ input_report_key(input, key, true);
+ input_sync(input);
+ input_report_key(input, key, false);
+}
+
+static void adxl34x_send_key_events(struct adxl34x *ac,
+ struct adxl34x_platform_data *pdata, int status, int press)
+{
+ int i;
+
+ for (i = ADXL_X_AXIS; i <= ADXL_Z_AXIS; i++) {
+ if (status & (1 << (ADXL_Z_AXIS - i)))
+ input_report_key(ac->input,
+ pdata->ev_code_tap[i], press);
+ }
+}
+
+static void adxl34x_do_tap(struct adxl34x *ac,
+ struct adxl34x_platform_data *pdata, int status)
+{
+ adxl34x_send_key_events(ac, pdata, status, true);
+ input_sync(ac->input);
+ adxl34x_send_key_events(ac, pdata, status, false);
+}
+
+static irqreturn_t adxl34x_irq(int irq, void *handle)
+{
+ struct adxl34x *ac = handle;
+ struct adxl34x_platform_data *pdata = &ac->pdata;
+ int int_stat, tap_stat, samples, orient, orient_code;
+
+ /*
+ * ACT_TAP_STATUS should be read before clearing the interrupt
+ * Avoid reading ACT_TAP_STATUS in case TAP detection is disabled
+ */
+
+ if (pdata->tap_axis_control & (TAP_X_EN | TAP_Y_EN | TAP_Z_EN))
+ tap_stat = AC_READ(ac, ACT_TAP_STATUS);
+ else
+ tap_stat = 0;
+
+ int_stat = AC_READ(ac, INT_SOURCE);
+
+ if (int_stat & FREE_FALL)
+ adxl34x_report_key_single(ac->input, pdata->ev_code_ff);
+
+ if (int_stat & OVERRUN)
+ dev_dbg(ac->dev, "OVERRUN\n");
+
+ if (int_stat & (SINGLE_TAP | DOUBLE_TAP)) {
+ adxl34x_do_tap(ac, pdata, tap_stat);
+
+ if (int_stat & DOUBLE_TAP)
+ adxl34x_do_tap(ac, pdata, tap_stat);
+ }
+
+ if (pdata->ev_code_act_inactivity) {
+ if (int_stat & ACTIVITY)
+ input_report_key(ac->input,
+ pdata->ev_code_act_inactivity, 1);
+ if (int_stat & INACTIVITY)
+ input_report_key(ac->input,
+ pdata->ev_code_act_inactivity, 0);
+ }
+
+ /*
+ * ORIENTATION SENSING ADXL346 only
+ */
+ if (pdata->orientation_enable) {
+ orient = AC_READ(ac, ORIENT);
+ if ((pdata->orientation_enable & ADXL_EN_ORIENTATION_2D) &&
+ (orient & ADXL346_2D_VALID)) {
+
+ orient_code = ADXL346_2D_ORIENT(orient);
+ /* Report orientation only when it changes */
+ if (ac->orient2d_saved != orient_code) {
+ ac->orient2d_saved = orient_code;
+ adxl34x_report_key_single(ac->input,
+ pdata->ev_codes_orient_2d[orient_code]);
+ }
+ }
+
+ if ((pdata->orientation_enable & ADXL_EN_ORIENTATION_3D) &&
+ (orient & ADXL346_3D_VALID)) {
+
+ orient_code = ADXL346_3D_ORIENT(orient) - 1;
+ /* Report orientation only when it changes */
+ if (ac->orient3d_saved != orient_code) {
+ ac->orient3d_saved = orient_code;
+ adxl34x_report_key_single(ac->input,
+ pdata->ev_codes_orient_3d[orient_code]);
+ }
+ }
+ }
+
+ if (int_stat & (DATA_READY | WATERMARK)) {
+
+ if (pdata->fifo_mode)
+ samples = ENTRIES(AC_READ(ac, FIFO_STATUS)) + 1;
+ else
+ samples = 1;
+
+ for (; samples > 0; samples--) {
+ adxl34x_service_ev_fifo(ac);
+ /*
+ * To ensure that the FIFO has
+ * completely popped, there must be at least 5 us between
+ * the end of reading the data registers, signified by the
+ * transition to register 0x38 from 0x37 or the CS pin
+ * going high, and the start of new reads of the FIFO or
+ * reading the FIFO_STATUS register. For SPI operation at
+ * 1.5 MHz or lower, the register addressing portion of the
+ * transmission is sufficient delay to ensure the FIFO has
+ * completely popped. It is necessary for SPI operation
+ * greater than 1.5 MHz to de-assert the CS pin to ensure a
+ * total of 5 us, which is at most 3.4 us at 5 MHz
+ * operation.
+ */
+ if (ac->fifo_delay && (samples > 1))
+ udelay(3);
+ }
+ }
+
+ input_sync(ac->input);
+
+ return IRQ_HANDLED;
+}
+
+static void __adxl34x_disable(struct adxl34x *ac)
+{
+ /*
+ * A '0' places the ADXL34x into standby mode
+ * with minimum power consumption.
+ */
+ AC_WRITE(ac, POWER_CTL, 0);
+}
+
+static void __adxl34x_enable(struct adxl34x *ac)
+{
+ AC_WRITE(ac, POWER_CTL, ac->pdata.power_mode | PCTL_MEASURE);
+}
+
+void adxl34x_suspend(struct adxl34x *ac)
+{
+ mutex_lock(&ac->mutex);
+
+ if (!ac->suspended && !ac->disabled && ac->opened)
+ __adxl34x_disable(ac);
+
+ ac->suspended = true;
+
+ mutex_unlock(&ac->mutex);
+}
+EXPORT_SYMBOL_GPL(adxl34x_suspend);
+
+void adxl34x_resume(struct adxl34x *ac)
+{
+ mutex_lock(&ac->mutex);
+
+ if (ac->suspended && !ac->disabled && ac->opened)
+ __adxl34x_enable(ac);
+
+ ac->suspended = false;
+
+ mutex_unlock(&ac->mutex);
+}
+EXPORT_SYMBOL_GPL(adxl34x_resume);
+
+static ssize_t adxl34x_disable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ac->disabled);
+}
+
+static ssize_t adxl34x_disable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ac->mutex);
+
+ if (!ac->suspended && ac->opened) {
+ if (val) {
+ if (!ac->disabled)
+ __adxl34x_disable(ac);
+ } else {
+ if (ac->disabled)
+ __adxl34x_enable(ac);
+ }
+ }
+
+ ac->disabled = !!val;
+
+ mutex_unlock(&ac->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(disable, 0664, adxl34x_disable_show, adxl34x_disable_store);
+
+static ssize_t adxl34x_calibrate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+ ssize_t count;
+
+ mutex_lock(&ac->mutex);
+ count = sprintf(buf, "%d,%d,%d\n",
+ ac->hwcal.x * 4 + ac->swcal.x,
+ ac->hwcal.y * 4 + ac->swcal.y,
+ ac->hwcal.z * 4 + ac->swcal.z);
+ mutex_unlock(&ac->mutex);
+
+ return count;
+}
+
+static ssize_t adxl34x_calibrate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+
+ /*
+ * Hardware offset calibration has a resolution of 15.6 mg/LSB.
+ * We use HW calibration and handle the remaining bits in SW. (4mg/LSB)
+ */
+
+ mutex_lock(&ac->mutex);
+ ac->hwcal.x -= (ac->saved.x / 4);
+ ac->swcal.x = ac->saved.x % 4;
+
+ ac->hwcal.y -= (ac->saved.y / 4);
+ ac->swcal.y = ac->saved.y % 4;
+
+ ac->hwcal.z -= (ac->saved.z / 4);
+ ac->swcal.z = ac->saved.z % 4;
+
+ AC_WRITE(ac, OFSX, (s8) ac->hwcal.x);
+ AC_WRITE(ac, OFSY, (s8) ac->hwcal.y);
+ AC_WRITE(ac, OFSZ, (s8) ac->hwcal.z);
+ mutex_unlock(&ac->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(calibrate, 0664,
+ adxl34x_calibrate_show, adxl34x_calibrate_store);
+
+static ssize_t adxl34x_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", RATE(ac->pdata.data_rate));
+}
+
+static ssize_t adxl34x_rate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+ unsigned char val;
+ int error;
+
+ error = kstrtou8(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ac->mutex);
+
+ ac->pdata.data_rate = RATE(val);
+ AC_WRITE(ac, BW_RATE,
+ ac->pdata.data_rate |
+ (ac->pdata.low_power_mode ? LOW_POWER : 0));
+
+ mutex_unlock(&ac->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(rate, 0664, adxl34x_rate_show, adxl34x_rate_store);
+
+static ssize_t adxl34x_autosleep_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n",
+ ac->pdata.power_mode & (PCTL_AUTO_SLEEP | PCTL_LINK) ? 1 : 0);
+}
+
+static ssize_t adxl34x_autosleep_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ac->mutex);
+
+ if (val)
+ ac->pdata.power_mode |= (PCTL_AUTO_SLEEP | PCTL_LINK);
+ else
+ ac->pdata.power_mode &= ~(PCTL_AUTO_SLEEP | PCTL_LINK);
+
+ if (!ac->disabled && !ac->suspended && ac->opened)
+ AC_WRITE(ac, POWER_CTL, ac->pdata.power_mode | PCTL_MEASURE);
+
+ mutex_unlock(&ac->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(autosleep, 0664,
+ adxl34x_autosleep_show, adxl34x_autosleep_store);
+
+static ssize_t adxl34x_position_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+ ssize_t count;
+
+ mutex_lock(&ac->mutex);
+ count = sprintf(buf, "(%d, %d, %d)\n",
+ ac->saved.x, ac->saved.y, ac->saved.z);
+ mutex_unlock(&ac->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(position, S_IRUGO, adxl34x_position_show, NULL);
+
+#ifdef ADXL_DEBUG
+static ssize_t adxl34x_write_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct adxl34x *ac = dev_get_drvdata(dev);
+ unsigned int val;
+ int error;
+
+ /*
+ * This allows basic ADXL register write access for debug purposes.
+ */
+ error = kstrtouint(buf, 16, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&ac->mutex);
+ AC_WRITE(ac, val >> 8, val & 0xFF);
+ mutex_unlock(&ac->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(write, 0664, NULL, adxl34x_write_store);
+#endif
+
+static struct attribute *adxl34x_attributes[] = {
+ &dev_attr_disable.attr,
+ &dev_attr_calibrate.attr,
+ &dev_attr_rate.attr,
+ &dev_attr_autosleep.attr,
+ &dev_attr_position.attr,
+#ifdef ADXL_DEBUG
+ &dev_attr_write.attr,
+#endif
+ NULL
+};
+
+static const struct attribute_group adxl34x_attr_group = {
+ .attrs = adxl34x_attributes,
+};
+
+static int adxl34x_input_open(struct input_dev *input)
+{
+ struct adxl34x *ac = input_get_drvdata(input);
+
+ mutex_lock(&ac->mutex);
+
+ if (!ac->suspended && !ac->disabled)
+ __adxl34x_enable(ac);
+
+ ac->opened = true;
+
+ mutex_unlock(&ac->mutex);
+
+ return 0;
+}
+
+static void adxl34x_input_close(struct input_dev *input)
+{
+ struct adxl34x *ac = input_get_drvdata(input);
+
+ mutex_lock(&ac->mutex);
+
+ if (!ac->suspended && !ac->disabled)
+ __adxl34x_disable(ac);
+
+ ac->opened = false;
+
+ mutex_unlock(&ac->mutex);
+}
+
+struct adxl34x *adxl34x_probe(struct device *dev, int irq,
+ bool fifo_delay_default,
+ const struct adxl34x_bus_ops *bops)
+{
+ struct adxl34x *ac;
+ struct input_dev *input_dev;
+ const struct adxl34x_platform_data *pdata;
+ int err, range, i;
+ unsigned char revid;
+
+ if (!irq) {
+ dev_err(dev, "no IRQ?\n");
+ err = -ENODEV;
+ goto err_out;
+ }
+
+ ac = kzalloc(sizeof(*ac), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!ac || !input_dev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ ac->fifo_delay = fifo_delay_default;
+
+ pdata = dev->platform_data;
+ if (!pdata) {
+ dev_dbg(dev,
+ "No platform data: Using default initialization\n");
+ pdata = &adxl34x_default_init;
+ }
+
+ ac->pdata = *pdata;
+ pdata = &ac->pdata;
+
+ ac->input = input_dev;
+ ac->dev = dev;
+ ac->irq = irq;
+ ac->bops = bops;
+
+ mutex_init(&ac->mutex);
+
+ input_dev->name = "ADXL34x accelerometer";
+ revid = AC_READ(ac, DEVID);
+
+ switch (revid) {
+ case ID_ADXL345:
+ ac->model = 345;
+ break;
+ case ID_ADXL346:
+ ac->model = 346;
+ break;
+ default:
+ dev_err(dev, "Failed to probe %s\n", input_dev->name);
+ err = -ENODEV;
+ goto err_free_mem;
+ }
+
+ snprintf(ac->phys, sizeof(ac->phys), "%s/input0", dev_name(dev));
+
+ input_dev->phys = ac->phys;
+ input_dev->dev.parent = dev;
+ input_dev->id.product = ac->model;
+ input_dev->id.bustype = bops->bustype;
+ input_dev->open = adxl34x_input_open;
+ input_dev->close = adxl34x_input_close;
+
+ input_set_drvdata(input_dev, ac);
+
+ __set_bit(ac->pdata.ev_type, input_dev->evbit);
+
+ if (ac->pdata.ev_type == EV_REL) {
+ __set_bit(REL_X, input_dev->relbit);
+ __set_bit(REL_Y, input_dev->relbit);
+ __set_bit(REL_Z, input_dev->relbit);
+ } else {
+ /* EV_ABS */
+ __set_bit(ABS_X, input_dev->absbit);
+ __set_bit(ABS_Y, input_dev->absbit);
+ __set_bit(ABS_Z, input_dev->absbit);
+
+ if (pdata->data_range & FULL_RES)
+ range = ADXL_FULLRES_MAX_VAL; /* Signed 13-bit */
+ else
+ range = ADXL_FIXEDRES_MAX_VAL; /* Signed 10-bit */
+
+ input_set_abs_params(input_dev, ABS_X, -range, range, 3, 3);
+ input_set_abs_params(input_dev, ABS_Y, -range, range, 3, 3);
+ input_set_abs_params(input_dev, ABS_Z, -range, range, 3, 3);
+ }
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(pdata->ev_code_tap[ADXL_X_AXIS], input_dev->keybit);
+ __set_bit(pdata->ev_code_tap[ADXL_Y_AXIS], input_dev->keybit);
+ __set_bit(pdata->ev_code_tap[ADXL_Z_AXIS], input_dev->keybit);
+
+ if (pdata->ev_code_ff) {
+ ac->int_mask = FREE_FALL;
+ __set_bit(pdata->ev_code_ff, input_dev->keybit);
+ }
+
+ if (pdata->ev_code_act_inactivity)
+ __set_bit(pdata->ev_code_act_inactivity, input_dev->keybit);
+
+ ac->int_mask |= ACTIVITY | INACTIVITY;
+
+ if (pdata->watermark) {
+ ac->int_mask |= WATERMARK;
+ if (!FIFO_MODE(pdata->fifo_mode))
+ ac->pdata.fifo_mode |= FIFO_STREAM;
+ } else {
+ ac->int_mask |= DATA_READY;
+ }
+
+ if (pdata->tap_axis_control & (TAP_X_EN | TAP_Y_EN | TAP_Z_EN))
+ ac->int_mask |= SINGLE_TAP | DOUBLE_TAP;
+
+ if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS)
+ ac->fifo_delay = false;
+
+ AC_WRITE(ac, POWER_CTL, 0);
+
+ err = request_threaded_irq(ac->irq, NULL, adxl34x_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ dev_name(dev), ac);
+ if (err) {
+ dev_err(dev, "irq %d busy?\n", ac->irq);
+ goto err_free_mem;
+ }
+
+ err = sysfs_create_group(&dev->kobj, &adxl34x_attr_group);
+ if (err)
+ goto err_free_irq;
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_remove_attr;
+
+ AC_WRITE(ac, OFSX, pdata->x_axis_offset);
+ ac->hwcal.x = pdata->x_axis_offset;
+ AC_WRITE(ac, OFSY, pdata->y_axis_offset);
+ ac->hwcal.y = pdata->y_axis_offset;
+ AC_WRITE(ac, OFSZ, pdata->z_axis_offset);
+ ac->hwcal.z = pdata->z_axis_offset;
+ AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold);
+ AC_WRITE(ac, DUR, pdata->tap_duration);
+ AC_WRITE(ac, LATENT, pdata->tap_latency);
+ AC_WRITE(ac, WINDOW, pdata->tap_window);
+ AC_WRITE(ac, THRESH_ACT, pdata->activity_threshold);
+ AC_WRITE(ac, THRESH_INACT, pdata->inactivity_threshold);
+ AC_WRITE(ac, TIME_INACT, pdata->inactivity_time);
+ AC_WRITE(ac, THRESH_FF, pdata->free_fall_threshold);
+ AC_WRITE(ac, TIME_FF, pdata->free_fall_time);
+ AC_WRITE(ac, TAP_AXES, pdata->tap_axis_control);
+ AC_WRITE(ac, ACT_INACT_CTL, pdata->act_axis_control);
+ AC_WRITE(ac, BW_RATE, RATE(ac->pdata.data_rate) |
+ (pdata->low_power_mode ? LOW_POWER : 0));
+ AC_WRITE(ac, DATA_FORMAT, pdata->data_range);
+ AC_WRITE(ac, FIFO_CTL, FIFO_MODE(pdata->fifo_mode) |
+ SAMPLES(pdata->watermark));
+
+ if (pdata->use_int2) {
+ /* Map all INTs to INT2 */
+ AC_WRITE(ac, INT_MAP, ac->int_mask | OVERRUN);
+ } else {
+ /* Map all INTs to INT1 */
+ AC_WRITE(ac, INT_MAP, 0);
+ }
+
+ if (ac->model == 346 && ac->pdata.orientation_enable) {
+ AC_WRITE(ac, ORIENT_CONF,
+ ORIENT_DEADZONE(ac->pdata.deadzone_angle) |
+ ORIENT_DIVISOR(ac->pdata.divisor_length));
+
+ ac->orient2d_saved = 1234;
+ ac->orient3d_saved = 1234;
+
+ if (pdata->orientation_enable & ADXL_EN_ORIENTATION_3D)
+ for (i = 0; i < ARRAY_SIZE(pdata->ev_codes_orient_3d); i++)
+ __set_bit(pdata->ev_codes_orient_3d[i],
+ input_dev->keybit);
+
+ if (pdata->orientation_enable & ADXL_EN_ORIENTATION_2D)
+ for (i = 0; i < ARRAY_SIZE(pdata->ev_codes_orient_2d); i++)
+ __set_bit(pdata->ev_codes_orient_2d[i],
+ input_dev->keybit);
+ } else {
+ ac->pdata.orientation_enable = 0;
+ }
+
+ AC_WRITE(ac, INT_ENABLE, ac->int_mask | OVERRUN);
+
+ ac->pdata.power_mode &= (PCTL_AUTO_SLEEP | PCTL_LINK);
+
+ return ac;
+
+ err_remove_attr:
+ sysfs_remove_group(&dev->kobj, &adxl34x_attr_group);
+ err_free_irq:
+ free_irq(ac->irq, ac);
+ err_free_mem:
+ input_free_device(input_dev);
+ kfree(ac);
+ err_out:
+ return ERR_PTR(err);
+}
+EXPORT_SYMBOL_GPL(adxl34x_probe);
+
+int adxl34x_remove(struct adxl34x *ac)
+{
+ sysfs_remove_group(&ac->dev->kobj, &adxl34x_attr_group);
+ free_irq(ac->irq, ac);
+ input_unregister_device(ac->input);
+ dev_dbg(ac->dev, "unregistered accelerometer\n");
+ kfree(ac);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adxl34x_remove);
+
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/adxl34x.h b/drivers/input/misc/adxl34x.h
new file mode 100644
index 00000000..bbbc80fd
--- /dev/null
+++ b/drivers/input/misc/adxl34x.h
@@ -0,0 +1,30 @@
+/*
+ * ADXL345/346 Three-Axis Digital Accelerometers (I2C/SPI Interface)
+ *
+ * Enter bugs at http://blackfin.uclinux.org/
+ *
+ * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#ifndef _ADXL34X_H_
+#define _ADXL34X_H_
+
+struct device;
+struct adxl34x;
+
+struct adxl34x_bus_ops {
+ u16 bustype;
+ int (*read)(struct device *, unsigned char);
+ int (*read_block)(struct device *, unsigned char, int, void *);
+ int (*write)(struct device *, unsigned char, unsigned char);
+};
+
+void adxl34x_suspend(struct adxl34x *ac);
+void adxl34x_resume(struct adxl34x *ac);
+struct adxl34x *adxl34x_probe(struct device *dev, int irq,
+ bool fifo_delay_default,
+ const struct adxl34x_bus_ops *bops);
+int adxl34x_remove(struct adxl34x *ac);
+
+#endif
diff --git a/drivers/input/misc/al3006_pls.c b/drivers/input/misc/al3006_pls.c
new file mode 100644
index 00000000..8d72a055
--- /dev/null
+++ b/drivers/input/misc/al3006_pls.c
@@ -0,0 +1,838 @@
+/*
+ * File: al3006_pls.c
+ * Based on:
+ * Author: Yunlong Wang <Yunlong.Wang @spreadtrum.com>
+ *
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+#include <linux/i2c/al3006_pls.h>
+#include <linux/miscdevice.h>
+
+static struct i2c_client *this_client;
+static atomic_t p_flag;
+static atomic_t l_flag;
+static int al3006_pls_opened=0;
+
+/*0: sleep out; 1: sleep in*/
+static unsigned char suspend_flag=0;
+
+
+static ssize_t al3006_pls_show_suspend(struct device* cd,struct device_attribute *attr, char* buf);
+static ssize_t al3006_pls_store_suspend(struct device* cd, struct device_attribute *attr,const char* buf, size_t len);
+static ssize_t al3006_pls_show_version(struct device* cd,struct device_attribute *attr, char* buf);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void al3006_pls_early_suspend(struct early_suspend *handler);
+static void al3006_pls_early_resume(struct early_suspend *handler);
+#endif
+static int al3006_pls_write_data(unsigned char addr, unsigned char data);
+static int al3006_pls_read_data(unsigned char addr, unsigned char *data);
+static int al3006_pls_enable(SENSOR_TYPE type);
+static int al3006_pls_disable(SENSOR_TYPE type);
+static void al3006_pls_report_init(void);
+
+static int al3006_pls_adc2lux[64]={
+ 1, 1, 1, 2, 2, 2, 3, 4,
+ 4, 5, 6, 7, 9, 11, 13, 16,
+ 19, 22, 27, 32, 39, 46, 56, 67,
+ 80, 96, 116, 139, 167, 200, 240, 289,
+ 346, 416, 499, 599, 720, 864, 1037, 1245,
+ 1495, 1795, 2154, 2586, 3105, 3728, 4475, 5372,
+ 6449, 7743, 9295, 11159, 13396, 16082, 19307, 23178,
+ 27862, 33405, 40103, 48144, 57797, 69386, 83298, 100000
+};
+
+
+static DEVICE_ATTR(suspend, S_IRUGO | S_IWUSR, al3006_pls_show_suspend, al3006_pls_store_suspend);
+static DEVICE_ATTR(version, S_IRUGO | S_IWUSR, al3006_pls_show_version, NULL);
+
+static ssize_t al3006_pls_show_suspend(struct device* cd,
+ struct device_attribute *attr, char* buf)
+{
+ ssize_t ret = 0;
+
+ if(suspend_flag==1)
+ sprintf(buf, "AL3006 Resume\n");
+ else
+ sprintf(buf, "AL3006 Suspend\n");
+
+ ret = strlen(buf) + 1;
+
+ return ret;
+}
+
+static ssize_t al3006_pls_store_suspend(struct device* cd, struct device_attribute *attr,
+ const char* buf, size_t len)
+{
+ unsigned long on_off = simple_strtoul(buf, NULL, 10);
+ suspend_flag = on_off;
+
+ if(on_off==1)
+ {
+ printk(KERN_INFO "AL3006 Entry Resume\n");
+ al3006_pls_enable(AL3006_PLS_BOTH);
+ }
+ else
+ {
+ printk(KERN_INFO "AL3006 Entry Suspend\n");
+ al3006_pls_disable(AL3006_PLS_BOTH);
+ }
+
+ return len;
+}
+
+static ssize_t al3006_pls_show_version(struct device* cd,
+ struct device_attribute *attr, char* buf)
+{
+ ssize_t ret = 0;
+
+ sprintf(buf, "AL3006");
+ ret = strlen(buf) + 1;
+
+ return ret;
+}
+
+static int al3006_pls_create_sysfs(struct i2c_client *client)
+{
+ int err;
+ struct device *dev = &(client->dev);
+
+ PLS_DBG("%s", __func__);
+
+ err = device_create_file(dev, &dev_attr_suspend);
+ err = device_create_file(dev, &dev_attr_version);
+
+ return err;
+}
+
+/*******************************************************************************
+* Function : al3006_pls_enable
+* Description : type: proximity / light
+*******************************************************************************/
+static int al3006_pls_enable(SENSOR_TYPE type)
+{
+ unsigned char config;
+
+ al3006_pls_read_data(AL3006_PLS_REG_CONFIG, &config);
+
+ PLS_DBG("%s: type=%d; config=0x%x", __func__, type, config);
+
+ switch(type) {
+ case AL3006_PLS_ALPS:
+ if(config == AL3006_PLS_PXY_ACTIVE)
+ type = AL3006_PLS_BOTH;
+ break;
+ case AL3006_PLS_PXY:
+ if(config == AL3006_PLS_ALPS_ACTIVE)
+ type = AL3006_PLS_BOTH;
+ break;
+ case AL3006_PLS_BOTH:
+ break;
+ }
+
+ PLS_DBG("%s: after type=%d\n",__func__, type);
+
+ switch(type) {
+ case AL3006_PLS_ALPS:
+ al3006_pls_write_data(AL3006_PLS_REG_CONFIG, AL3006_PLS_ALPS_ACTIVE);
+ break;
+ case AL3006_PLS_PXY:
+ al3006_pls_write_data(AL3006_PLS_REG_CONFIG, AL3006_PLS_PXY_ACTIVE);
+ al3006_pls_report_init();
+ break;
+ case AL3006_PLS_BOTH:
+ al3006_pls_write_data(AL3006_PLS_REG_CONFIG, AL3006_PLS_BOTH_ACTIVE);
+ al3006_pls_report_init();
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/*******************************************************************************
+* Function : al3006_pls_disable
+* Description : type: proximity / light
+*******************************************************************************/
+static int al3006_pls_disable(SENSOR_TYPE type)
+{
+ int pxy_flag, apls_flag;
+
+ PLS_DBG("%s: type=%d", __func__, type);
+
+ switch(type) {
+ case AL3006_PLS_ALPS:
+ pxy_flag = atomic_read(&p_flag);
+ /*check proximiy sensor status*/
+ if(pxy_flag == 0) {
+ PLS_DBG("%s: Disable both sensor for pxy_flag=%d", __func__, pxy_flag);
+ al3006_pls_write_data(AL3006_PLS_REG_CONFIG, AL3006_PLS_BOTH_DEACTIVE);
+ } else {
+ PLS_DBG("%s: Only Proximity Sensor alive", __func__);
+ al3006_pls_write_data(AL3006_PLS_REG_CONFIG, AL3006_PLS_PXY_ACTIVE);
+ }
+
+ break;
+ case AL3006_PLS_PXY:
+ apls_flag = atomic_read(&l_flag);
+ /*check light sensor status*/
+ if(apls_flag == 0) {
+ PLS_DBG("%s: Disable both sensor for apls_flag=%d", __func__, apls_flag);
+ al3006_pls_write_data(AL3006_PLS_REG_CONFIG, AL3006_PLS_BOTH_DEACTIVE);
+ } else {
+ PLS_DBG("%s: Only Light Sensor alive", __func__);
+ al3006_pls_write_data(AL3006_PLS_REG_CONFIG, AL3006_PLS_ALPS_ACTIVE);
+ }
+ break;
+ case AL3006_PLS_BOTH:
+ PLS_DBG("%s: Disable both sensors", __func__);
+ al3006_pls_write_data(AL3006_PLS_REG_CONFIG, AL3006_PLS_BOTH_DEACTIVE);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int al3006_pls_open(struct inode *inode, struct file *file)
+{
+ PLS_DBG("%s\n", __func__);
+ if (al3006_pls_opened)
+ return -EBUSY;
+ al3006_pls_opened = 1;
+ return 0;
+}
+
+static int al3006_pls_release(struct inode *inode, struct file *file)
+{
+ PLS_DBG("%s", __func__);
+ al3006_pls_opened = 0;
+ return al3006_pls_disable(AL3006_PLS_BOTH);
+}
+
+
+/*******************************************************************************
+* Function : al3006_pls_ioctl
+* Description : ioctl interface to control the sensor
+*******************************************************************************/
+static long al3006_pls_ioctl(struct file *file, unsigned int cmd,unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int flag;
+ unsigned char data;
+
+ PLS_DBG("%s: cmd %d", __func__, _IOC_NR(cmd));
+
+ /*get ioctl parameter*/
+ switch (cmd) {
+ case LTR_IOCTL_SET_PFLAG:
+ case LTR_IOCTL_SET_LFLAG:
+ if (copy_from_user(&flag, argp, sizeof(flag))) {
+ return -EFAULT;
+ }
+ if (flag < 0 || flag > 1) {
+ return -EINVAL;
+ }
+ PLS_DBG("%s: set flag=%d", __func__, flag);
+ break;
+ default:
+ break;
+ }
+
+ /*handle ioctl*/
+ switch (cmd) {
+ case LTR_IOCTL_GET_PFLAG:
+ flag = atomic_read(&p_flag);
+ break;
+
+ case LTR_IOCTL_GET_LFLAG:
+ flag = atomic_read(&l_flag);
+ break;
+
+ case LTR_IOCTL_GET_DATA:
+ break;
+
+ case LTR_IOCTL_SET_PFLAG:
+ atomic_set(&p_flag, flag);
+ if(flag==1){
+ al3006_pls_enable(AL3006_PLS_PXY);
+ }
+ else if(flag==0) {
+ al3006_pls_disable(AL3006_PLS_PXY);
+ }
+ break;
+
+ case LTR_IOCTL_SET_LFLAG:
+ atomic_set(&l_flag, flag);
+ if(flag==1){
+ al3006_pls_enable(AL3006_PLS_ALPS);
+ }
+ else if(flag==0) {
+ al3006_pls_disable(AL3006_PLS_ALPS);
+ }
+ break;
+
+ default:
+ pr_err("%s: invalid cmd %d\n", __func__, _IOC_NR(cmd));
+ return -EINVAL;
+ }
+
+ /*report ioctl*/
+ switch (cmd) {
+ case LTR_IOCTL_GET_PFLAG:
+ case LTR_IOCTL_GET_LFLAG:
+ if (copy_to_user(argp, &flag, sizeof(flag))) {
+ return -EFAULT;
+ }
+ PLS_DBG("%s: get flag=%d", __func__, flag);
+ break;
+
+ case LTR_IOCTL_GET_DATA:
+ al3006_pls_read_data(AL3006_PLS_REG_DATA,&data);
+ if (copy_to_user(argp, &data, sizeof(data))) {
+ return -EFAULT;
+ }
+ PLS_DBG("%s: get data=%d", __func__, flag);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+
+}
+
+
+static struct file_operations al3006_pls_fops = {
+ .owner = THIS_MODULE,
+ .open = al3006_pls_open,
+ .release = al3006_pls_release,
+ .unlocked_ioctl = al3006_pls_ioctl,
+};
+
+static struct miscdevice al3006_pls_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = AL3006_PLS_DEVICE,
+ .fops = &al3006_pls_fops,
+};
+
+/*******************************************************************************
+* Function : al3006_pls_rx_data
+* Description : read data from i2c
+* Parameters : buf: content, len: length
+*******************************************************************************/
+static int al3006_pls_rx_data(char *buf, int len)
+{
+ int ret;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = buf,
+ },
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(this_client->adapter, msgs, 2);
+ if (ret < 0)
+ printk(KERN_ERR "%s i2c read error: %d\n", __func__, ret);
+
+
+ return ret;
+}
+
+/*******************************************************************************
+* Function : al3006_pls_tx_data
+* Description : send data to i2c
+* Parameters : buf: content, len: length
+*******************************************************************************/
+static int al3006_pls_tx_data(char *buf, int len)
+{
+ int ret;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = this_client->addr,
+ .flags = 0,
+ .len = len,
+ .buf = buf,
+ }
+ };
+
+ ret = i2c_transfer(this_client->adapter, msg, 1);
+ if (ret < 0)
+ printk(KERN_ERR "%s i2c write error: %d\n", __func__, ret);
+
+ return ret;
+}
+
+/*******************************************************************************
+* Function : al3006_pls_write_data
+* Description : write data to IC
+* Parameters : addr: register address, data: register data
+*******************************************************************************/
+static int al3006_pls_write_data(unsigned char addr, unsigned char data)
+{
+ unsigned char buf[2];
+ buf[0]=addr;
+ buf[1]=data;
+ return al3006_pls_tx_data(buf, 2);
+}
+
+/*******************************************************************************
+* Function : al3006_pls_read_data
+* Description : read data from IC
+* Parameters : addr: register address, data: read data
+*******************************************************************************/
+static int al3006_pls_read_data(unsigned char addr, unsigned char *data)
+{
+ int ret;
+ unsigned char buf;
+
+ buf=addr;
+ ret = al3006_pls_rx_data(&buf, 1);
+ *data = buf;
+
+ return ret;;
+}
+
+#if PLS_DEBUG
+static void al3006_pls_reg_dump(void)
+{
+ unsigned char config,time_ctrl,dls_ctrl,int_status,dps_ctrl,data,dls_win;
+ al3006_pls_read_data(AL3006_PLS_REG_CONFIG,&config);
+ al3006_pls_read_data(AL3006_PLS_REG_TIME_CTRL,&time_ctrl);
+ al3006_pls_read_data(AL3006_PLS_REG_DLS_CTRL,&dls_ctrl);
+ al3006_pls_read_data(AL3006_PLS_REG_INT_STATUS,&int_status);
+ al3006_pls_read_data(AL3006_PLS_REG_DPS_CTRL,&dps_ctrl);
+ al3006_pls_read_data(AL3006_PLS_REG_DATA,&data);
+ al3006_pls_read_data(AL3006_PLS_REG_DLS_WIN,&dls_win);
+
+ PLS_DBG("%s: config=0x%x,time_ctrl=0x%x,dls_ctrl=0x%x,int_status=0x%x,dps_ctrl=0x%x,data=0x%x,dls_win=0x%x", \
+ __func__, config,time_ctrl,dls_ctrl,int_status,dps_ctrl,data,dls_win);
+}
+#endif
+
+/*******************************************************************************
+* Function : al3006_pls_reg_init
+* Description : set al3006 registers
+* Parameters : none
+* Return : void
+*******************************************************************************/
+static int al3006_pls_reg_init(void)
+{
+ int ret=0;
+
+ /*set window loss*/
+ if(al3006_pls_write_data(AL3006_PLS_REG_DLS_WIN,0x0D)<0) {
+ printk(KERN_ERR "%s: I2C Write Reg %x Failed\n", __func__, AL3006_PLS_REG_DLS_WIN);
+ ret = -1;
+ }
+
+ /*set time control*/
+ if(al3006_pls_write_data(AL3006_PLS_REG_TIME_CTRL,0x1)<0) {
+ printk(KERN_ERR "%s: I2C Write Reg %x Failed\n", __func__, AL3006_PLS_REG_TIME_CTRL);
+ ret = -1;
+ }
+
+ /*set alps level*/
+#if defined(AL3006_PLS_ADC_LEVEL64)
+ al3006_pls_write_data(AL3006_PLS_REG_DLS_CTRL,0xA0);
+#elif defined(AL3006_PLS_ADC_LEVEL9)
+ al3006_pls_write_data(AL3006_PLS_REG_DLS_CTRL,0x40);
+#elif defined(AL3006_PLS_ADC_LEVEL5)
+ al3006_pls_write_data(AL3006_PLS_REG_DLS_CTRL,0x20);
+#endif
+
+ /*set dps distance*/
+ if(al3006_pls_write_data(AL3006_PLS_REG_DPS_CTRL,0x10)<0) {
+ printk(KERN_ERR "%s: I2C Write Reg %x Failed\n", __func__, AL3006_PLS_REG_DPS_CTRL);
+ ret = -1;
+ }
+
+ /*enter power down mode*/
+ if(al3006_pls_write_data(AL3006_PLS_REG_CONFIG,AL3006_PLS_BOTH_DEACTIVE)<0) {
+ printk(KERN_ERR "%s: I2C Write Reg %x Failed\n", __func__, AL3006_PLS_BOTH_DEACTIVE);
+ ret = -1;
+ }
+
+#if PLS_DEBUG
+ al3006_pls_reg_dump();
+#endif
+
+ return ret;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/*******************************************************************************
+* Function : al3006_pls_early_suspend
+* Description : cancel the delayed work and put ts to shutdown mode
+* Parameters : handler
+* Return : none
+*******************************************************************************/
+static void al3006_pls_early_suspend(struct early_suspend *handler)
+{
+ PLS_DBG("%s\n", __func__);
+#if 0
+ al3006_pls_disable(AL3006_PLS_BOTH);
+#endif
+}
+
+/*******************************************************************************
+* Function : al3006_pls_early_resume
+* Description : ts re-entry the normal mode and schedule the work, there need to be a litte time
+ for ts ready
+* Parameters : handler
+* Return : none
+*******************************************************************************/
+static void al3006_pls_early_resume(struct early_suspend *handler)
+{
+ PLS_DBG("%s\n", __func__);
+#if 0
+ al3006_pls_enable(AL3006_PLS_BOTH);
+#endif
+}
+#endif
+
+static void al3006_pls_report_init(void)
+{
+ al3006_pls_struct *al3006_pls = (al3006_pls_struct *)i2c_get_clientdata(this_client);
+ PLS_DBG("%s\n",__func__);
+ /*report far*/
+ input_report_abs(al3006_pls->input, ABS_DISTANCE, 1);
+ input_sync(al3006_pls->input);
+}
+
+/*******************************************************************************
+* Function : al3006_pls_report_dps
+* Description : reg data,input dev
+* Parameters : report proximity sensor data to input system
+* Return : none
+*******************************************************************************/
+static void al3006_pls_report_dps(unsigned char data, struct input_dev *input)
+{
+ unsigned char dps_data = (data&0x80)>>7;
+
+ dps_data = (dps_data==1) ? 0: 1;
+ PLS_DBG("%s: proximity=%d", __func__, dps_data);
+
+ input_report_abs(input, ABS_DISTANCE, dps_data);
+ input_sync(input);
+}
+
+/*******************************************************************************
+* Function : al3006_pls_report_dls
+* Description : reg data,input dev
+* Parameters : report light sensor data to input system
+* Return : none
+*******************************************************************************/
+static void al3006_pls_report_dls(unsigned char data, struct input_dev *input)
+{
+ int lux;
+ unsigned char adc = data&0x3f;
+
+#if defined(AL3006_PLS_ADC_LEVEL9)
+ adc = adc<<3;
+#elif defined(AL3006_PLS_ADC_LEVEL5)
+ adc = adc<<4;
+#endif
+
+ lux = al3006_pls_adc2lux[adc];
+ PLS_DBG("%s: adc=%d, lux=%d", __func__, adc, lux);
+ input_report_abs(input, ABS_MISC, lux);
+ input_sync(input);
+}
+
+/*******************************************************************************
+* Function : al3006_pls_work
+* Description : handler current data and report coordinate
+* Parameters : work
+* Return : none
+*******************************************************************************/
+
+static void al3006_pls_work(struct work_struct *work)
+{
+ unsigned char int_status, data, enable;
+ al3006_pls_struct *pls = container_of(work, al3006_pls_struct, work);
+
+ al3006_pls_read_data(AL3006_PLS_REG_CONFIG,&enable);
+ al3006_pls_read_data(AL3006_PLS_REG_INT_STATUS,&int_status);
+ al3006_pls_read_data(AL3006_PLS_REG_DATA,&data);
+
+ PLS_DBG("\033[33;1m %s: enable=0x%x; int_status=0x%x; data=0x%x \033[m",\
+ __func__, enable, int_status, data);
+
+ switch(int_status & AL3006_PLS_INT_MASK) {
+ case AL3006_PLS_DPS_INT:
+ al3006_pls_report_dps(data, pls->input);
+ break;
+
+ case AL3006_PLS_DLS_INT:
+ al3006_pls_report_dls(data, pls->input);
+ break;
+
+ case AL3006_PLS_INT_MASK:
+ al3006_pls_report_dps(data, pls->input);
+ al3006_pls_report_dls(data, pls->input);
+ break;
+
+ default:
+ break;
+ }
+
+ enable_irq(pls->client->irq);
+
+}
+
+
+/*******************************************************************************
+* Function : al3006_pls_irq_handler
+* Description : handle ts irq
+* Parameters : handler
+* Return : none
+*******************************************************************************/
+static irqreturn_t al3006_pls_irq_handler(int irq, void *dev_id)
+{
+ al3006_pls_struct *pls = (al3006_pls_struct *)dev_id;
+
+ disable_irq_nosync(pls->client->irq);
+ queue_work(pls->ltr_work_queue,&pls->work);
+ return IRQ_HANDLED;
+}
+
+static void al3006_pls_pininit(int irq_pin)
+{
+ printk(KERN_INFO "%s [irq=%d]\n",__func__,irq_pin);
+ gpio_request(irq_pin, AL3006_PLS_IRQ_PIN);
+}
+
+
+static int al3006_pls_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+
+ int err = 0;
+ struct al3006_pls_platform_data *pdata = client->dev.platform_data;
+ struct input_dev *input_dev;
+ al3006_pls_struct *al3006_pls;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ printk("%s: functionality check failed\n", __func__);
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ al3006_pls = kzalloc(sizeof(al3006_pls_struct), GFP_KERNEL);
+ if (!al3006_pls)
+ {
+ printk("%s: request memory failed\n", __func__);
+ err= -ENOMEM;
+ goto exit_request_memory_failed;
+ }
+
+ this_client = client;
+ al3006_pls->client = client;
+ i2c_set_clientdata(client, al3006_pls);
+
+ /*pin init*/
+ al3006_pls_pininit(pdata->irq_gpio_number);
+
+ /*get irq*/
+ client->irq = gpio_to_irq(pdata->irq_gpio_number);
+ al3006_pls->irq = client->irq;
+
+ PLS_DBG("I2C name=%s, addr=0x%x, gpio=%d, irq=%d",client->name,client->addr, \
+ pdata->irq_gpio_number, client->irq);
+
+ /*init AL3006_PLS*/
+ if(al3006_pls_reg_init()<0)
+ {
+ printk(KERN_ERR "%s: device init failed\n", __func__);
+ err=-1;
+ goto exit_device_init_failed;
+ }
+
+ /*register device*/
+ err = misc_register(&al3006_pls_device);
+ if (err) {
+ printk(KERN_ERR "%s: al3006_pls_device register failed\n", __func__);
+ goto exit_device_register_failed;
+ }
+
+
+ /* register input device*/
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ {
+ printk(KERN_ERR "%s: input allocate device failed\n", __func__);
+ err = -ENOMEM;
+ goto exit_input_dev_allocate_failed;
+ }
+
+ al3006_pls->input = input_dev;
+
+ input_dev->name = AL3006_PLS_INPUT_DEV;
+ input_dev->phys = AL3006_PLS_INPUT_DEV;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0010;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ /*for proximity*/
+ input_set_abs_params(input_dev, ABS_DISTANCE, 0, 1, 0, 0);
+ /*for lightsensor*/
+ input_set_abs_params(input_dev, ABS_MISC, 0, 100001, 0, 0);
+
+ err = input_register_device(input_dev);
+ if (err < 0)
+ {
+ printk(KERN_ERR "%s: input device regist failed\n", __func__);
+ goto exit_input_register_failed;
+ }
+
+ /*create work queue*/
+ INIT_WORK(&al3006_pls->work, al3006_pls_work);
+ al3006_pls->ltr_work_queue= create_singlethread_workqueue(AL3006_PLS_DEVICE);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ /*register early suspend*/
+ al3006_pls->ltr_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ al3006_pls->ltr_early_suspend.suspend = al3006_pls_early_suspend;
+ al3006_pls->ltr_early_suspend.resume = al3006_pls_early_resume;
+ register_early_suspend(&al3006_pls->ltr_early_suspend);
+#endif
+
+ if(client->irq > 0)
+ {
+ err = request_irq(client->irq, al3006_pls_irq_handler, IRQ_TYPE_LEVEL_LOW, client->name,al3006_pls);
+ if (err <0)
+ {
+ printk(KERN_ERR "%s: IRQ setup failed %d\n", __func__, err);
+ goto irq_request_err;
+ }
+ }
+
+ /*create attribute files*/
+ al3006_pls_create_sysfs(client);
+
+
+ printk(KERN_INFO "%s: Probe Success!\n",__func__);
+
+ return 0;
+
+irq_request_err:
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&al3006_pls->ltr_early_suspend);
+#endif
+ destroy_workqueue(al3006_pls->ltr_work_queue);
+exit_input_register_failed:
+ input_free_device(input_dev);
+ misc_deregister(&al3006_pls_device);
+exit_device_register_failed:
+exit_input_dev_allocate_failed:
+exit_device_init_failed:
+ kfree(al3006_pls);
+exit_request_memory_failed:
+exit_check_functionality_failed:
+ printk(KERN_ERR "%s: Probe Fail!\n",__func__);
+ return err;
+
+}
+
+static int al3006_pls_remove(struct i2c_client *client)
+{
+ al3006_pls_struct *al3006_pls = i2c_get_clientdata(client);
+
+ PLS_DBG("%s", __func__);
+
+ /*remove queue*/
+ flush_workqueue(al3006_pls->ltr_work_queue);
+ destroy_workqueue(al3006_pls->ltr_work_queue);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ /*free suspend*/
+ unregister_early_suspend(&al3006_pls->ltr_early_suspend);
+#endif
+ misc_deregister(&al3006_pls_device);
+ /*free input*/
+ input_unregister_device(al3006_pls->input);
+ input_free_device(al3006_pls->input);
+ /*free irq*/
+ free_irq(al3006_pls->client->irq, al3006_pls);
+ /*free malloc*/
+ kfree(al3006_pls);
+
+ return 0;
+}
+
+static const struct i2c_device_id al3006_pls_id[] = {
+ { AL3006_PLS_DEVICE, 0 },
+ { }
+};
+/*----------------------------------------------------------------------------*/
+static struct i2c_driver al3006_pls_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = AL3006_PLS_DEVICE,
+ },
+ .probe = al3006_pls_probe,
+ .remove = al3006_pls_remove,
+ .id_table = al3006_pls_id,
+};
+/*----------------------------------------------------------------------------*/
+
+static int __init al3006_pls_init(void)
+{
+ PLS_DBG("%s", __func__);
+ return i2c_add_driver(&al3006_pls_driver);
+}
+
+static void __exit al3006_pls_exit(void)
+{
+ PLS_DBG("%s", __func__);
+ i2c_del_driver(&al3006_pls_driver);
+}
+
+module_init(al3006_pls_init);
+module_exit(al3006_pls_exit);
+
+
+MODULE_AUTHOR("Yunlong Wang <Yunlong.Wang@spreadtrum.com>");
+MODULE_DESCRIPTION("Proximity&Light Sensor AL3006 DRIVER");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/misc/apanel.c b/drivers/input/misc/apanel.c
new file mode 100644
index 00000000..a8d2b8db
--- /dev/null
+++ b/drivers/input/misc/apanel.c
@@ -0,0 +1,350 @@
+/*
+ * Fujitsu Lifebook Application Panel button drive
+ *
+ * Copyright (C) 2007 Stephen Hemminger <shemminger@linux-foundation.org>
+ * Copyright (C) 2001-2003 Jochen Eisinger <jochen@penguin-breeder.org>
+ *
+ * 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.
+ *
+ * Many Fujitsu Lifebook laptops have a small panel of buttons that are
+ * accessible via the i2c/smbus interface. This driver polls those
+ * buttons and generates input events.
+ *
+ * For more details see:
+ * http://apanel.sourceforge.net/tech.php
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/input-polldev.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/leds.h>
+
+#define APANEL_NAME "Fujitsu Application Panel"
+#define APANEL_VERSION "1.3.1"
+#define APANEL "apanel"
+
+/* How often we poll keys - msecs */
+#define POLL_INTERVAL_DEFAULT 1000
+
+/* Magic constants in BIOS that tell about buttons */
+enum apanel_devid {
+ APANEL_DEV_NONE = 0,
+ APANEL_DEV_APPBTN = 1,
+ APANEL_DEV_CDBTN = 2,
+ APANEL_DEV_LCD = 3,
+ APANEL_DEV_LED = 4,
+
+ APANEL_DEV_MAX,
+};
+
+enum apanel_chip {
+ CHIP_NONE = 0,
+ CHIP_OZ992C = 1,
+ CHIP_OZ163T = 2,
+ CHIP_OZ711M3 = 4,
+};
+
+/* Result of BIOS snooping/probing -- what features are supported */
+static enum apanel_chip device_chip[APANEL_DEV_MAX];
+
+#define MAX_PANEL_KEYS 12
+
+struct apanel {
+ struct input_polled_dev *ipdev;
+ struct i2c_client *client;
+ unsigned short keymap[MAX_PANEL_KEYS];
+ u16 nkeys;
+ u16 led_bits;
+ struct work_struct led_work;
+ struct led_classdev mail_led;
+};
+
+
+static int apanel_probe(struct i2c_client *, const struct i2c_device_id *);
+
+static void report_key(struct input_dev *input, unsigned keycode)
+{
+ pr_debug(APANEL ": report key %#x\n", keycode);
+ input_report_key(input, keycode, 1);
+ input_sync(input);
+
+ input_report_key(input, keycode, 0);
+ input_sync(input);
+}
+
+/* Poll for key changes
+ *
+ * Read Application keys via SMI
+ * A (0x4), B (0x8), Internet (0x2), Email (0x1).
+ *
+ * CD keys:
+ * Forward (0x100), Rewind (0x200), Stop (0x400), Pause (0x800)
+ */
+static void apanel_poll(struct input_polled_dev *ipdev)
+{
+ struct apanel *ap = ipdev->private;
+ struct input_dev *idev = ipdev->input;
+ u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+ s32 data;
+ int i;
+
+ data = i2c_smbus_read_word_data(ap->client, cmd);
+ if (data < 0)
+ return; /* ignore errors (due to ACPI??) */
+
+ /* write back to clear latch */
+ i2c_smbus_write_word_data(ap->client, cmd, 0);
+
+ if (!data)
+ return;
+
+ dev_dbg(&idev->dev, APANEL ": data %#x\n", data);
+ for (i = 0; i < idev->keycodemax; i++)
+ if ((1u << i) & data)
+ report_key(idev, ap->keymap[i]);
+}
+
+/* Track state changes of LED */
+static void led_update(struct work_struct *work)
+{
+ struct apanel *ap = container_of(work, struct apanel, led_work);
+
+ i2c_smbus_write_word_data(ap->client, 0x10, ap->led_bits);
+}
+
+static void mail_led_set(struct led_classdev *led,
+ enum led_brightness value)
+{
+ struct apanel *ap = container_of(led, struct apanel, mail_led);
+
+ if (value != LED_OFF)
+ ap->led_bits |= 0x8000;
+ else
+ ap->led_bits &= ~0x8000;
+
+ schedule_work(&ap->led_work);
+}
+
+static int apanel_remove(struct i2c_client *client)
+{
+ struct apanel *ap = i2c_get_clientdata(client);
+
+ if (device_chip[APANEL_DEV_LED] != CHIP_NONE)
+ led_classdev_unregister(&ap->mail_led);
+
+ input_unregister_polled_device(ap->ipdev);
+ input_free_polled_device(ap->ipdev);
+
+ return 0;
+}
+
+static void apanel_shutdown(struct i2c_client *client)
+{
+ apanel_remove(client);
+}
+
+static const struct i2c_device_id apanel_id[] = {
+ { "fujitsu_apanel", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, apanel_id);
+
+static struct i2c_driver apanel_driver = {
+ .driver = {
+ .name = APANEL,
+ },
+ .probe = &apanel_probe,
+ .remove = &apanel_remove,
+ .shutdown = &apanel_shutdown,
+ .id_table = apanel_id,
+};
+
+static struct apanel apanel = {
+ .keymap = {
+ [0] = KEY_MAIL,
+ [1] = KEY_WWW,
+ [2] = KEY_PROG2,
+ [3] = KEY_PROG1,
+
+ [8] = KEY_FORWARD,
+ [9] = KEY_REWIND,
+ [10] = KEY_STOPCD,
+ [11] = KEY_PLAYPAUSE,
+
+ },
+ .mail_led = {
+ .name = "mail:blue",
+ .brightness_set = mail_led_set,
+ },
+};
+
+/* NB: Only one panel on the i2c. */
+static int apanel_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct apanel *ap;
+ struct input_polled_dev *ipdev;
+ struct input_dev *idev;
+ u8 cmd = device_chip[APANEL_DEV_APPBTN] == CHIP_OZ992C ? 0 : 8;
+ int i, err = -ENOMEM;
+
+ ap = &apanel;
+
+ ipdev = input_allocate_polled_device();
+ if (!ipdev)
+ goto out1;
+
+ ap->ipdev = ipdev;
+ ap->client = client;
+
+ i2c_set_clientdata(client, ap);
+
+ err = i2c_smbus_write_word_data(client, cmd, 0);
+ if (err) {
+ dev_warn(&client->dev, APANEL ": smbus write error %d\n",
+ err);
+ goto out3;
+ }
+
+ ipdev->poll = apanel_poll;
+ ipdev->poll_interval = POLL_INTERVAL_DEFAULT;
+ ipdev->private = ap;
+
+ idev = ipdev->input;
+ idev->name = APANEL_NAME " buttons";
+ idev->phys = "apanel/input0";
+ idev->id.bustype = BUS_HOST;
+ idev->dev.parent = &client->dev;
+
+ set_bit(EV_KEY, idev->evbit);
+
+ idev->keycode = ap->keymap;
+ idev->keycodesize = sizeof(ap->keymap[0]);
+ idev->keycodemax = (device_chip[APANEL_DEV_CDBTN] != CHIP_NONE) ? 12 : 4;
+
+ for (i = 0; i < idev->keycodemax; i++)
+ if (ap->keymap[i])
+ set_bit(ap->keymap[i], idev->keybit);
+
+ err = input_register_polled_device(ipdev);
+ if (err)
+ goto out3;
+
+ INIT_WORK(&ap->led_work, led_update);
+ if (device_chip[APANEL_DEV_LED] != CHIP_NONE) {
+ err = led_classdev_register(&client->dev, &ap->mail_led);
+ if (err)
+ goto out4;
+ }
+
+ return 0;
+out4:
+ input_unregister_polled_device(ipdev);
+out3:
+ input_free_polled_device(ipdev);
+out1:
+ return err;
+}
+
+/* Scan the system ROM for the signature "FJKEYINF" */
+static __init const void __iomem *bios_signature(const void __iomem *bios)
+{
+ ssize_t offset;
+ const unsigned char signature[] = "FJKEYINF";
+
+ for (offset = 0; offset < 0x10000; offset += 0x10) {
+ if (check_signature(bios + offset, signature,
+ sizeof(signature)-1))
+ return bios + offset;
+ }
+ pr_notice(APANEL ": Fujitsu BIOS signature '%s' not found...\n",
+ signature);
+ return NULL;
+}
+
+static int __init apanel_init(void)
+{
+ void __iomem *bios;
+ const void __iomem *p;
+ u8 devno;
+ unsigned char i2c_addr;
+ int found = 0;
+
+ bios = ioremap(0xF0000, 0x10000); /* Can't fail */
+
+ p = bios_signature(bios);
+ if (!p) {
+ iounmap(bios);
+ return -ENODEV;
+ }
+
+ /* just use the first address */
+ p += 8;
+ i2c_addr = readb(p + 3) >> 1;
+
+ for ( ; (devno = readb(p)) & 0x7f; p += 4) {
+ unsigned char method, slave, chip;
+
+ method = readb(p + 1);
+ chip = readb(p + 2);
+ slave = readb(p + 3) >> 1;
+
+ if (slave != i2c_addr) {
+ pr_notice(APANEL ": only one SMBus slave "
+ "address supported, skiping device...\n");
+ continue;
+ }
+
+ /* translate alternative device numbers */
+ switch (devno) {
+ case 6:
+ devno = APANEL_DEV_APPBTN;
+ break;
+ case 7:
+ devno = APANEL_DEV_LED;
+ break;
+ }
+
+ if (devno >= APANEL_DEV_MAX)
+ pr_notice(APANEL ": unknown device %u found\n", devno);
+ else if (device_chip[devno] != CHIP_NONE)
+ pr_warning(APANEL ": duplicate entry for devno %u\n", devno);
+
+ else if (method != 1 && method != 2 && method != 4) {
+ pr_notice(APANEL ": unknown method %u for devno %u\n",
+ method, devno);
+ } else {
+ device_chip[devno] = (enum apanel_chip) chip;
+ ++found;
+ }
+ }
+ iounmap(bios);
+
+ if (found == 0) {
+ pr_info(APANEL ": no input devices reported by BIOS\n");
+ return -EIO;
+ }
+
+ return i2c_add_driver(&apanel_driver);
+}
+module_init(apanel_init);
+
+static void __exit apanel_cleanup(void)
+{
+ i2c_del_driver(&apanel_driver);
+}
+module_exit(apanel_cleanup);
+
+MODULE_AUTHOR("Stephen Hemminger <shemminger@linux-foundation.org>");
+MODULE_DESCRIPTION(APANEL_NAME " driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(APANEL_VERSION);
+
+MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifeBook*:pvr*:rvnFUJITSU:*");
+MODULE_ALIAS("dmi:*:svnFUJITSU:pnLifebook*:pvr*:rvnFUJITSU:*");
diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c
new file mode 100644
index 00000000..e7e12a5f
--- /dev/null
+++ b/drivers/input/misc/arizona-haptics.c
@@ -0,0 +1,252 @@
+/*
+ * Arizona haptics driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/pdata.h>
+#include <linux/mfd/arizona/registers.h>
+
+struct arizona_haptics {
+ struct arizona *arizona;
+ struct input_dev *input_dev;
+ struct work_struct work;
+
+ struct mutex mutex;
+ u8 intensity;
+};
+
+static void arizona_haptics_work(struct work_struct *work)
+{
+ struct arizona_haptics *haptics = container_of(work,
+ struct arizona_haptics,
+ work);
+ struct arizona *arizona = haptics->arizona;
+ struct mutex *dapm_mutex = &arizona->dapm->card->dapm_mutex;
+ int ret;
+
+ if (!haptics->arizona->dapm) {
+ dev_err(arizona->dev, "No DAPM context\n");
+ return;
+ }
+
+ if (haptics->intensity) {
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_HAPTICS_PHASE_2_INTENSITY,
+ ARIZONA_PHASE2_INTENSITY_MASK,
+ haptics->intensity);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to set intensity: %d\n",
+ ret);
+ return;
+ }
+
+ /* This enable sequence will be a noop if already enabled */
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_HAPTICS_CONTROL_1,
+ ARIZONA_HAP_CTRL_MASK,
+ 1 << ARIZONA_HAP_CTRL_SHIFT);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to start haptics: %d\n",
+ ret);
+ return;
+ }
+
+ mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ ret = snd_soc_dapm_enable_pin(arizona->dapm, "HAPTICS");
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to start HAPTICS: %d\n",
+ ret);
+ mutex_unlock(dapm_mutex);
+ return;
+ }
+
+ mutex_unlock(dapm_mutex);
+
+ ret = snd_soc_dapm_sync(arizona->dapm);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
+ ret);
+ return;
+ }
+ } else {
+ /* This disable sequence will be a noop if already enabled */
+ mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ ret = snd_soc_dapm_disable_pin(arizona->dapm, "HAPTICS");
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to disable HAPTICS: %d\n",
+ ret);
+ mutex_unlock(dapm_mutex);
+ return;
+ }
+
+ mutex_unlock(dapm_mutex);
+
+ ret = snd_soc_dapm_sync(arizona->dapm);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to sync DAPM: %d\n",
+ ret);
+ return;
+ }
+
+ ret = regmap_update_bits(arizona->regmap,
+ ARIZONA_HAPTICS_CONTROL_1,
+ ARIZONA_HAP_CTRL_MASK,
+ 1 << ARIZONA_HAP_CTRL_SHIFT);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to stop haptics: %d\n",
+ ret);
+ return;
+ }
+ }
+}
+
+static int arizona_haptics_play(struct input_dev *input, void *data,
+ struct ff_effect *effect)
+{
+ struct arizona_haptics *haptics = input_get_drvdata(input);
+ struct arizona *arizona = haptics->arizona;
+
+ if (!arizona->dapm) {
+ dev_err(arizona->dev, "No DAPM context\n");
+ return -EBUSY;
+ }
+
+ if (effect->u.rumble.strong_magnitude) {
+ /* Scale the magnitude into the range the device supports */
+ if (arizona->pdata.hap_act) {
+ haptics->intensity =
+ effect->u.rumble.strong_magnitude >> 9;
+ if (effect->direction < 0x8000)
+ haptics->intensity += 0x7f;
+ } else {
+ haptics->intensity =
+ effect->u.rumble.strong_magnitude >> 8;
+ }
+ } else {
+ haptics->intensity = 0;
+ }
+
+ schedule_work(&haptics->work);
+
+ return 0;
+}
+
+static void arizona_haptics_close(struct input_dev *input)
+{
+ struct arizona_haptics *haptics = input_get_drvdata(input);
+ struct mutex *dapm_mutex = &haptics->arizona->dapm->card->dapm_mutex;
+
+ cancel_work_sync(&haptics->work);
+
+ mutex_lock_nested(dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+ if (haptics->arizona->dapm)
+ snd_soc_dapm_disable_pin(haptics->arizona->dapm, "HAPTICS");
+
+ mutex_unlock(dapm_mutex);
+}
+
+static int arizona_haptics_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct arizona_haptics *haptics;
+ int ret;
+
+ haptics = devm_kzalloc(&pdev->dev, sizeof(*haptics), GFP_KERNEL);
+ if (!haptics)
+ return -ENOMEM;
+
+ haptics->arizona = arizona;
+
+ ret = regmap_update_bits(arizona->regmap, ARIZONA_HAPTICS_CONTROL_1,
+ ARIZONA_HAP_ACT, arizona->pdata.hap_act);
+ if (ret != 0) {
+ dev_err(arizona->dev, "Failed to set haptics actuator: %d\n",
+ ret);
+ return ret;
+ }
+
+ INIT_WORK(&haptics->work, arizona_haptics_work);
+
+ haptics->input_dev = input_allocate_device();
+ if (haptics->input_dev == NULL) {
+ dev_err(arizona->dev, "Failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ input_set_drvdata(haptics->input_dev, haptics);
+
+ haptics->input_dev->name = "arizona:haptics";
+ haptics->input_dev->dev.parent = pdev->dev.parent;
+ haptics->input_dev->close = arizona_haptics_close;
+ __set_bit(FF_RUMBLE, haptics->input_dev->ffbit);
+
+ ret = input_ff_create_memless(haptics->input_dev, NULL,
+ arizona_haptics_play);
+ if (ret < 0) {
+ dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n",
+ ret);
+ goto err_ialloc;
+ }
+
+ ret = input_register_device(haptics->input_dev);
+ if (ret < 0) {
+ dev_err(arizona->dev, "couldn't register input device: %d\n",
+ ret);
+ goto err_iff;
+ }
+
+ platform_set_drvdata(pdev, haptics);
+
+ return 0;
+
+err_iff:
+ if (haptics->input_dev)
+ input_ff_destroy(haptics->input_dev);
+err_ialloc:
+ input_free_device(haptics->input_dev);
+
+ return ret;
+}
+
+static int arizona_haptics_remove(struct platform_device *pdev)
+{
+ struct arizona_haptics *haptics = platform_get_drvdata(pdev);
+
+ input_unregister_device(haptics->input_dev);
+
+ return 0;
+}
+
+static struct platform_driver arizona_haptics_driver = {
+ .probe = arizona_haptics_probe,
+ .remove = arizona_haptics_remove,
+ .driver = {
+ .name = "arizona-haptics",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(arizona_haptics_driver);
+
+MODULE_ALIAS("platform:arizona-haptics");
+MODULE_DESCRIPTION("Arizona haptics driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
diff --git a/drivers/input/misc/ati_remote2.c b/drivers/input/misc/ati_remote2.c
new file mode 100644
index 00000000..f63341f2
--- /dev/null
+++ b/drivers/input/misc/ati_remote2.c
@@ -0,0 +1,1016 @@
+/*
+ * ati_remote2 - ATI/Philips USB RF remote driver
+ *
+ * Copyright (C) 2005-2008 Ville Syrjala <syrjala@sci.fi>
+ * Copyright (C) 2007-2008 Peter Stokes <linux@dadeos.co.uk>
+ *
+ * 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/usb/input.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#define DRIVER_DESC "ATI/Philips USB RF remote driver"
+#define DRIVER_VERSION "0.3"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_AUTHOR("Ville Syrjala <syrjala@sci.fi>");
+MODULE_LICENSE("GPL");
+
+/*
+ * ATI Remote Wonder II Channel Configuration
+ *
+ * The remote control can by assigned one of sixteen "channels" in order to facilitate
+ * the use of multiple remote controls within range of each other.
+ * A remote's "channel" may be altered by pressing and holding the "PC" button for
+ * approximately 3 seconds, after which the button will slowly flash the count of the
+ * currently configured "channel", using the numeric keypad enter a number between 1 and
+ * 16 and then press the "PC" button again, the button will slowly flash the count of the
+ * newly configured "channel".
+ */
+
+enum {
+ ATI_REMOTE2_MAX_CHANNEL_MASK = 0xFFFF,
+ ATI_REMOTE2_MAX_MODE_MASK = 0x1F,
+};
+
+static int ati_remote2_set_mask(const char *val,
+ const struct kernel_param *kp,
+ unsigned int max)
+{
+ unsigned int mask;
+ int ret;
+
+ if (!val)
+ return -EINVAL;
+
+ ret = kstrtouint(val, 0, &mask);
+ if (ret)
+ return ret;
+
+ if (mask & ~max)
+ return -EINVAL;
+
+ *(unsigned int *)kp->arg = mask;
+
+ return 0;
+}
+
+static int ati_remote2_set_channel_mask(const char *val,
+ const struct kernel_param *kp)
+{
+ pr_debug("%s()\n", __func__);
+
+ return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_CHANNEL_MASK);
+}
+
+static int ati_remote2_get_channel_mask(char *buffer,
+ const struct kernel_param *kp)
+{
+ pr_debug("%s()\n", __func__);
+
+ return sprintf(buffer, "0x%04x", *(unsigned int *)kp->arg);
+}
+
+static int ati_remote2_set_mode_mask(const char *val,
+ const struct kernel_param *kp)
+{
+ pr_debug("%s()\n", __func__);
+
+ return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_MODE_MASK);
+}
+
+static int ati_remote2_get_mode_mask(char *buffer,
+ const struct kernel_param *kp)
+{
+ pr_debug("%s()\n", __func__);
+
+ return sprintf(buffer, "0x%02x", *(unsigned int *)kp->arg);
+}
+
+static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK;
+#define param_check_channel_mask(name, p) __param_check(name, p, unsigned int)
+static struct kernel_param_ops param_ops_channel_mask = {
+ .set = ati_remote2_set_channel_mask,
+ .get = ati_remote2_get_channel_mask,
+};
+module_param(channel_mask, channel_mask, 0644);
+MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>");
+
+static unsigned int mode_mask = ATI_REMOTE2_MAX_MODE_MASK;
+#define param_check_mode_mask(name, p) __param_check(name, p, unsigned int)
+static struct kernel_param_ops param_ops_mode_mask = {
+ .set = ati_remote2_set_mode_mask,
+ .get = ati_remote2_get_mode_mask,
+};
+module_param(mode_mask, mode_mask, 0644);
+MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>");
+
+static struct usb_device_id ati_remote2_id_table[] = {
+ { USB_DEVICE(0x0471, 0x0602) }, /* ATI Remote Wonder II */
+ { }
+};
+MODULE_DEVICE_TABLE(usb, ati_remote2_id_table);
+
+static DEFINE_MUTEX(ati_remote2_mutex);
+
+enum {
+ ATI_REMOTE2_OPENED = 0x1,
+ ATI_REMOTE2_SUSPENDED = 0x2,
+};
+
+enum {
+ ATI_REMOTE2_AUX1,
+ ATI_REMOTE2_AUX2,
+ ATI_REMOTE2_AUX3,
+ ATI_REMOTE2_AUX4,
+ ATI_REMOTE2_PC,
+ ATI_REMOTE2_MODES,
+};
+
+static const struct {
+ u8 hw_code;
+ u16 keycode;
+} ati_remote2_key_table[] = {
+ { 0x00, KEY_0 },
+ { 0x01, KEY_1 },
+ { 0x02, KEY_2 },
+ { 0x03, KEY_3 },
+ { 0x04, KEY_4 },
+ { 0x05, KEY_5 },
+ { 0x06, KEY_6 },
+ { 0x07, KEY_7 },
+ { 0x08, KEY_8 },
+ { 0x09, KEY_9 },
+ { 0x0c, KEY_POWER },
+ { 0x0d, KEY_MUTE },
+ { 0x10, KEY_VOLUMEUP },
+ { 0x11, KEY_VOLUMEDOWN },
+ { 0x20, KEY_CHANNELUP },
+ { 0x21, KEY_CHANNELDOWN },
+ { 0x28, KEY_FORWARD },
+ { 0x29, KEY_REWIND },
+ { 0x2c, KEY_PLAY },
+ { 0x30, KEY_PAUSE },
+ { 0x31, KEY_STOP },
+ { 0x37, KEY_RECORD },
+ { 0x38, KEY_DVD },
+ { 0x39, KEY_TV },
+ { 0x3f, KEY_PROG1 }, /* AUX1-AUX4 and PC */
+ { 0x54, KEY_MENU },
+ { 0x58, KEY_UP },
+ { 0x59, KEY_DOWN },
+ { 0x5a, KEY_LEFT },
+ { 0x5b, KEY_RIGHT },
+ { 0x5c, KEY_OK },
+ { 0x78, KEY_A },
+ { 0x79, KEY_B },
+ { 0x7a, KEY_C },
+ { 0x7b, KEY_D },
+ { 0x7c, KEY_E },
+ { 0x7d, KEY_F },
+ { 0x82, KEY_ENTER },
+ { 0x8e, KEY_VENDOR },
+ { 0x96, KEY_COFFEE },
+ { 0xa9, BTN_LEFT },
+ { 0xaa, BTN_RIGHT },
+ { 0xbe, KEY_QUESTION },
+ { 0xd0, KEY_EDIT },
+ { 0xd5, KEY_FRONT },
+ { 0xf9, KEY_INFO },
+};
+
+struct ati_remote2 {
+ struct input_dev *idev;
+ struct usb_device *udev;
+
+ struct usb_interface *intf[2];
+ struct usb_endpoint_descriptor *ep[2];
+ struct urb *urb[2];
+ void *buf[2];
+ dma_addr_t buf_dma[2];
+
+ unsigned long jiffies;
+ int mode;
+
+ char name[64];
+ char phys[64];
+
+ /* Each mode (AUX1-AUX4 and PC) can have an independent keymap. */
+ u16 keycode[ATI_REMOTE2_MODES][ARRAY_SIZE(ati_remote2_key_table)];
+
+ unsigned int flags;
+
+ unsigned int channel_mask;
+ unsigned int mode_mask;
+};
+
+static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id);
+static void ati_remote2_disconnect(struct usb_interface *interface);
+static int ati_remote2_suspend(struct usb_interface *interface, pm_message_t message);
+static int ati_remote2_resume(struct usb_interface *interface);
+static int ati_remote2_reset_resume(struct usb_interface *interface);
+static int ati_remote2_pre_reset(struct usb_interface *interface);
+static int ati_remote2_post_reset(struct usb_interface *interface);
+
+static struct usb_driver ati_remote2_driver = {
+ .name = "ati_remote2",
+ .probe = ati_remote2_probe,
+ .disconnect = ati_remote2_disconnect,
+ .id_table = ati_remote2_id_table,
+ .suspend = ati_remote2_suspend,
+ .resume = ati_remote2_resume,
+ .reset_resume = ati_remote2_reset_resume,
+ .pre_reset = ati_remote2_pre_reset,
+ .post_reset = ati_remote2_post_reset,
+ .supports_autosuspend = 1,
+};
+
+static int ati_remote2_submit_urbs(struct ati_remote2 *ar2)
+{
+ int r;
+
+ r = usb_submit_urb(ar2->urb[0], GFP_KERNEL);
+ if (r) {
+ dev_err(&ar2->intf[0]->dev,
+ "%s(): usb_submit_urb() = %d\n", __func__, r);
+ return r;
+ }
+ r = usb_submit_urb(ar2->urb[1], GFP_KERNEL);
+ if (r) {
+ usb_kill_urb(ar2->urb[0]);
+ dev_err(&ar2->intf[1]->dev,
+ "%s(): usb_submit_urb() = %d\n", __func__, r);
+ return r;
+ }
+
+ return 0;
+}
+
+static void ati_remote2_kill_urbs(struct ati_remote2 *ar2)
+{
+ usb_kill_urb(ar2->urb[1]);
+ usb_kill_urb(ar2->urb[0]);
+}
+
+static int ati_remote2_open(struct input_dev *idev)
+{
+ struct ati_remote2 *ar2 = input_get_drvdata(idev);
+ int r;
+
+ dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+ r = usb_autopm_get_interface(ar2->intf[0]);
+ if (r) {
+ dev_err(&ar2->intf[0]->dev,
+ "%s(): usb_autopm_get_interface() = %d\n", __func__, r);
+ goto fail1;
+ }
+
+ mutex_lock(&ati_remote2_mutex);
+
+ if (!(ar2->flags & ATI_REMOTE2_SUSPENDED)) {
+ r = ati_remote2_submit_urbs(ar2);
+ if (r)
+ goto fail2;
+ }
+
+ ar2->flags |= ATI_REMOTE2_OPENED;
+
+ mutex_unlock(&ati_remote2_mutex);
+
+ usb_autopm_put_interface(ar2->intf[0]);
+
+ return 0;
+
+ fail2:
+ mutex_unlock(&ati_remote2_mutex);
+ usb_autopm_put_interface(ar2->intf[0]);
+ fail1:
+ return r;
+}
+
+static void ati_remote2_close(struct input_dev *idev)
+{
+ struct ati_remote2 *ar2 = input_get_drvdata(idev);
+
+ dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+ mutex_lock(&ati_remote2_mutex);
+
+ if (!(ar2->flags & ATI_REMOTE2_SUSPENDED))
+ ati_remote2_kill_urbs(ar2);
+
+ ar2->flags &= ~ATI_REMOTE2_OPENED;
+
+ mutex_unlock(&ati_remote2_mutex);
+}
+
+static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
+{
+ struct input_dev *idev = ar2->idev;
+ u8 *data = ar2->buf[0];
+ int channel, mode;
+
+ channel = data[0] >> 4;
+
+ if (!((1 << channel) & ar2->channel_mask))
+ return;
+
+ mode = data[0] & 0x0F;
+
+ if (mode > ATI_REMOTE2_PC) {
+ dev_err(&ar2->intf[0]->dev,
+ "Unknown mode byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ if (!((1 << mode) & ar2->mode_mask))
+ return;
+
+ input_event(idev, EV_REL, REL_X, (s8) data[1]);
+ input_event(idev, EV_REL, REL_Y, (s8) data[2]);
+ input_sync(idev);
+}
+
+static int ati_remote2_lookup(unsigned int hw_code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ati_remote2_key_table); i++)
+ if (ati_remote2_key_table[i].hw_code == hw_code)
+ return i;
+
+ return -1;
+}
+
+static void ati_remote2_input_key(struct ati_remote2 *ar2)
+{
+ struct input_dev *idev = ar2->idev;
+ u8 *data = ar2->buf[1];
+ int channel, mode, hw_code, index;
+
+ channel = data[0] >> 4;
+
+ if (!((1 << channel) & ar2->channel_mask))
+ return;
+
+ mode = data[0] & 0x0F;
+
+ if (mode > ATI_REMOTE2_PC) {
+ dev_err(&ar2->intf[1]->dev,
+ "Unknown mode byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ hw_code = data[2];
+ if (hw_code == 0x3f) {
+ /*
+ * For some incomprehensible reason the mouse pad generates
+ * events which look identical to the events from the last
+ * pressed mode key. Naturally we don't want to generate key
+ * events for the mouse pad so we filter out any subsequent
+ * events from the same mode key.
+ */
+ if (ar2->mode == mode)
+ return;
+
+ if (data[1] == 0)
+ ar2->mode = mode;
+ }
+
+ if (!((1 << mode) & ar2->mode_mask))
+ return;
+
+ index = ati_remote2_lookup(hw_code);
+ if (index < 0) {
+ dev_err(&ar2->intf[1]->dev,
+ "Unknown code byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ switch (data[1]) {
+ case 0: /* release */
+ break;
+ case 1: /* press */
+ ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_DELAY]);
+ break;
+ case 2: /* repeat */
+
+ /* No repeat for mouse buttons. */
+ if (ar2->keycode[mode][index] == BTN_LEFT ||
+ ar2->keycode[mode][index] == BTN_RIGHT)
+ return;
+
+ if (!time_after_eq(jiffies, ar2->jiffies))
+ return;
+
+ ar2->jiffies = jiffies + msecs_to_jiffies(idev->rep[REP_PERIOD]);
+ break;
+ default:
+ dev_err(&ar2->intf[1]->dev,
+ "Unknown state byte (%02x %02x %02x %02x)\n",
+ data[3], data[2], data[1], data[0]);
+ return;
+ }
+
+ input_event(idev, EV_KEY, ar2->keycode[mode][index], data[1]);
+ input_sync(idev);
+}
+
+static void ati_remote2_complete_mouse(struct urb *urb)
+{
+ struct ati_remote2 *ar2 = urb->context;
+ int r;
+
+ switch (urb->status) {
+ case 0:
+ usb_mark_last_busy(ar2->udev);
+ ati_remote2_input_mouse(ar2);
+ break;
+ case -ENOENT:
+ case -EILSEQ:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ dev_dbg(&ar2->intf[0]->dev,
+ "%s(): urb status = %d\n", __func__, urb->status);
+ return;
+ default:
+ usb_mark_last_busy(ar2->udev);
+ dev_err(&ar2->intf[0]->dev,
+ "%s(): urb status = %d\n", __func__, urb->status);
+ }
+
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r)
+ dev_err(&ar2->intf[0]->dev,
+ "%s(): usb_submit_urb() = %d\n", __func__, r);
+}
+
+static void ati_remote2_complete_key(struct urb *urb)
+{
+ struct ati_remote2 *ar2 = urb->context;
+ int r;
+
+ switch (urb->status) {
+ case 0:
+ usb_mark_last_busy(ar2->udev);
+ ati_remote2_input_key(ar2);
+ break;
+ case -ENOENT:
+ case -EILSEQ:
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ dev_dbg(&ar2->intf[1]->dev,
+ "%s(): urb status = %d\n", __func__, urb->status);
+ return;
+ default:
+ usb_mark_last_busy(ar2->udev);
+ dev_err(&ar2->intf[1]->dev,
+ "%s(): urb status = %d\n", __func__, urb->status);
+ }
+
+ r = usb_submit_urb(urb, GFP_ATOMIC);
+ if (r)
+ dev_err(&ar2->intf[1]->dev,
+ "%s(): usb_submit_urb() = %d\n", __func__, r);
+}
+
+static int ati_remote2_getkeycode(struct input_dev *idev,
+ struct input_keymap_entry *ke)
+{
+ struct ati_remote2 *ar2 = input_get_drvdata(idev);
+ unsigned int mode;
+ int offset;
+ unsigned int index;
+ unsigned int scancode;
+
+ if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+ index = ke->index;
+ if (index >= ATI_REMOTE2_MODES *
+ ARRAY_SIZE(ati_remote2_key_table))
+ return -EINVAL;
+
+ mode = ke->index / ARRAY_SIZE(ati_remote2_key_table);
+ offset = ke->index % ARRAY_SIZE(ati_remote2_key_table);
+ scancode = (mode << 8) + ati_remote2_key_table[offset].hw_code;
+ } else {
+ if (input_scancode_to_scalar(ke, &scancode))
+ return -EINVAL;
+
+ mode = scancode >> 8;
+ if (mode > ATI_REMOTE2_PC)
+ return -EINVAL;
+
+ offset = ati_remote2_lookup(scancode & 0xff);
+ if (offset < 0)
+ return -EINVAL;
+
+ index = mode * ARRAY_SIZE(ati_remote2_key_table) + offset;
+ }
+
+ ke->keycode = ar2->keycode[mode][offset];
+ ke->len = sizeof(scancode);
+ memcpy(&ke->scancode, &scancode, sizeof(scancode));
+ ke->index = index;
+
+ return 0;
+}
+
+static int ati_remote2_setkeycode(struct input_dev *idev,
+ const struct input_keymap_entry *ke,
+ unsigned int *old_keycode)
+{
+ struct ati_remote2 *ar2 = input_get_drvdata(idev);
+ unsigned int mode;
+ int offset;
+ unsigned int index;
+ unsigned int scancode;
+
+ if (ke->flags & INPUT_KEYMAP_BY_INDEX) {
+ if (ke->index >= ATI_REMOTE2_MODES *
+ ARRAY_SIZE(ati_remote2_key_table))
+ return -EINVAL;
+
+ mode = ke->index / ARRAY_SIZE(ati_remote2_key_table);
+ offset = ke->index % ARRAY_SIZE(ati_remote2_key_table);
+ } else {
+ if (input_scancode_to_scalar(ke, &scancode))
+ return -EINVAL;
+
+ mode = scancode >> 8;
+ if (mode > ATI_REMOTE2_PC)
+ return -EINVAL;
+
+ offset = ati_remote2_lookup(scancode & 0xff);
+ if (offset < 0)
+ return -EINVAL;
+ }
+
+ *old_keycode = ar2->keycode[mode][offset];
+ ar2->keycode[mode][offset] = ke->keycode;
+ __set_bit(ke->keycode, idev->keybit);
+
+ for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
+ for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
+ if (ar2->keycode[mode][index] == *old_keycode)
+ return 0;
+ }
+ }
+
+ __clear_bit(*old_keycode, idev->keybit);
+
+ return 0;
+}
+
+static int ati_remote2_input_init(struct ati_remote2 *ar2)
+{
+ struct input_dev *idev;
+ int index, mode, retval;
+
+ idev = input_allocate_device();
+ if (!idev)
+ return -ENOMEM;
+
+ ar2->idev = idev;
+ input_set_drvdata(idev, ar2);
+
+ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | BIT_MASK(EV_REL);
+ idev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+ BIT_MASK(BTN_RIGHT);
+ idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+
+ for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
+ for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
+ ar2->keycode[mode][index] = ati_remote2_key_table[index].keycode;
+ __set_bit(ar2->keycode[mode][index], idev->keybit);
+ }
+ }
+
+ /* AUX1-AUX4 and PC generate the same scancode. */
+ index = ati_remote2_lookup(0x3f);
+ ar2->keycode[ATI_REMOTE2_AUX1][index] = KEY_PROG1;
+ ar2->keycode[ATI_REMOTE2_AUX2][index] = KEY_PROG2;
+ ar2->keycode[ATI_REMOTE2_AUX3][index] = KEY_PROG3;
+ ar2->keycode[ATI_REMOTE2_AUX4][index] = KEY_PROG4;
+ ar2->keycode[ATI_REMOTE2_PC][index] = KEY_PC;
+ __set_bit(KEY_PROG1, idev->keybit);
+ __set_bit(KEY_PROG2, idev->keybit);
+ __set_bit(KEY_PROG3, idev->keybit);
+ __set_bit(KEY_PROG4, idev->keybit);
+ __set_bit(KEY_PC, idev->keybit);
+
+ idev->rep[REP_DELAY] = 250;
+ idev->rep[REP_PERIOD] = 33;
+
+ idev->open = ati_remote2_open;
+ idev->close = ati_remote2_close;
+
+ idev->getkeycode = ati_remote2_getkeycode;
+ idev->setkeycode = ati_remote2_setkeycode;
+
+ idev->name = ar2->name;
+ idev->phys = ar2->phys;
+
+ usb_to_input_id(ar2->udev, &idev->id);
+ idev->dev.parent = &ar2->udev->dev;
+
+ retval = input_register_device(idev);
+ if (retval)
+ input_free_device(idev);
+
+ return retval;
+}
+
+static int ati_remote2_urb_init(struct ati_remote2 *ar2)
+{
+ struct usb_device *udev = ar2->udev;
+ int i, pipe, maxp;
+
+ for (i = 0; i < 2; i++) {
+ ar2->buf[i] = usb_alloc_coherent(udev, 4, GFP_KERNEL, &ar2->buf_dma[i]);
+ if (!ar2->buf[i])
+ return -ENOMEM;
+
+ ar2->urb[i] = usb_alloc_urb(0, GFP_KERNEL);
+ if (!ar2->urb[i])
+ return -ENOMEM;
+
+ pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ maxp = maxp > 4 ? 4 : maxp;
+
+ usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp,
+ i ? ati_remote2_complete_key : ati_remote2_complete_mouse,
+ ar2, ar2->ep[i]->bInterval);
+ ar2->urb[i]->transfer_dma = ar2->buf_dma[i];
+ ar2->urb[i]->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ }
+
+ return 0;
+}
+
+static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ usb_free_urb(ar2->urb[i]);
+ usb_free_coherent(ar2->udev, 4, ar2->buf[i], ar2->buf_dma[i]);
+ }
+}
+
+static int ati_remote2_setup(struct ati_remote2 *ar2, unsigned int ch_mask)
+{
+ int r, i, channel;
+
+ /*
+ * Configure receiver to only accept input from remote "channel"
+ * channel == 0 -> Accept input from any remote channel
+ * channel == 1 -> Only accept input from remote channel 1
+ * channel == 2 -> Only accept input from remote channel 2
+ * ...
+ * channel == 16 -> Only accept input from remote channel 16
+ */
+
+ channel = 0;
+ for (i = 0; i < 16; i++) {
+ if ((1 << i) & ch_mask) {
+ if (!(~(1 << i) & ch_mask))
+ channel = i + 1;
+ break;
+ }
+ }
+
+ r = usb_control_msg(ar2->udev, usb_sndctrlpipe(ar2->udev, 0),
+ 0x20,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
+ channel, 0x0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (r) {
+ dev_err(&ar2->udev->dev, "%s - failed to set channel due to error: %d\n",
+ __func__, r);
+ return r;
+ }
+
+ return 0;
+}
+
+static ssize_t ati_remote2_show_channel_mask(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+ struct ati_remote2 *ar2 = usb_get_intfdata(intf);
+
+ return sprintf(buf, "0x%04x\n", ar2->channel_mask);
+}
+
+static ssize_t ati_remote2_store_channel_mask(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+ struct ati_remote2 *ar2 = usb_get_intfdata(intf);
+ unsigned int mask;
+ int r;
+
+ r = kstrtouint(buf, 0, &mask);
+ if (r)
+ return r;
+
+ if (mask & ~ATI_REMOTE2_MAX_CHANNEL_MASK)
+ return -EINVAL;
+
+ r = usb_autopm_get_interface(ar2->intf[0]);
+ if (r) {
+ dev_err(&ar2->intf[0]->dev,
+ "%s(): usb_autopm_get_interface() = %d\n", __func__, r);
+ return r;
+ }
+
+ mutex_lock(&ati_remote2_mutex);
+
+ if (mask != ar2->channel_mask) {
+ r = ati_remote2_setup(ar2, mask);
+ if (!r)
+ ar2->channel_mask = mask;
+ }
+
+ mutex_unlock(&ati_remote2_mutex);
+
+ usb_autopm_put_interface(ar2->intf[0]);
+
+ return r ? r : count;
+}
+
+static ssize_t ati_remote2_show_mode_mask(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+ struct ati_remote2 *ar2 = usb_get_intfdata(intf);
+
+ return sprintf(buf, "0x%02x\n", ar2->mode_mask);
+}
+
+static ssize_t ati_remote2_store_mode_mask(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_device *udev = to_usb_device(dev);
+ struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
+ struct ati_remote2 *ar2 = usb_get_intfdata(intf);
+ unsigned int mask;
+ int err;
+
+ err = kstrtouint(buf, 0, &mask);
+ if (err)
+ return err;
+
+ if (mask & ~ATI_REMOTE2_MAX_MODE_MASK)
+ return -EINVAL;
+
+ ar2->mode_mask = mask;
+
+ return count;
+}
+
+static DEVICE_ATTR(channel_mask, 0644, ati_remote2_show_channel_mask,
+ ati_remote2_store_channel_mask);
+
+static DEVICE_ATTR(mode_mask, 0644, ati_remote2_show_mode_mask,
+ ati_remote2_store_mode_mask);
+
+static struct attribute *ati_remote2_attrs[] = {
+ &dev_attr_channel_mask.attr,
+ &dev_attr_mode_mask.attr,
+ NULL,
+};
+
+static struct attribute_group ati_remote2_attr_group = {
+ .attrs = ati_remote2_attrs,
+};
+
+static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_host_interface *alt = interface->cur_altsetting;
+ struct ati_remote2 *ar2;
+ int r;
+
+ if (alt->desc.bInterfaceNumber)
+ return -ENODEV;
+
+ ar2 = kzalloc(sizeof (struct ati_remote2), GFP_KERNEL);
+ if (!ar2)
+ return -ENOMEM;
+
+ ar2->udev = udev;
+
+ ar2->intf[0] = interface;
+ ar2->ep[0] = &alt->endpoint[0].desc;
+
+ ar2->intf[1] = usb_ifnum_to_if(udev, 1);
+ r = usb_driver_claim_interface(&ati_remote2_driver, ar2->intf[1], ar2);
+ if (r)
+ goto fail1;
+ alt = ar2->intf[1]->cur_altsetting;
+ ar2->ep[1] = &alt->endpoint[0].desc;
+
+ r = ati_remote2_urb_init(ar2);
+ if (r)
+ goto fail2;
+
+ ar2->channel_mask = channel_mask;
+ ar2->mode_mask = mode_mask;
+
+ r = ati_remote2_setup(ar2, ar2->channel_mask);
+ if (r)
+ goto fail2;
+
+ usb_make_path(udev, ar2->phys, sizeof(ar2->phys));
+ strlcat(ar2->phys, "/input0", sizeof(ar2->phys));
+
+ strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name));
+
+ r = sysfs_create_group(&udev->dev.kobj, &ati_remote2_attr_group);
+ if (r)
+ goto fail2;
+
+ r = ati_remote2_input_init(ar2);
+ if (r)
+ goto fail3;
+
+ usb_set_intfdata(interface, ar2);
+
+ interface->needs_remote_wakeup = 1;
+
+ return 0;
+
+ fail3:
+ sysfs_remove_group(&udev->dev.kobj, &ati_remote2_attr_group);
+ fail2:
+ ati_remote2_urb_cleanup(ar2);
+ usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
+ fail1:
+ kfree(ar2);
+
+ return r;
+}
+
+static void ati_remote2_disconnect(struct usb_interface *interface)
+{
+ struct ati_remote2 *ar2;
+ struct usb_host_interface *alt = interface->cur_altsetting;
+
+ if (alt->desc.bInterfaceNumber)
+ return;
+
+ ar2 = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ input_unregister_device(ar2->idev);
+
+ sysfs_remove_group(&ar2->udev->dev.kobj, &ati_remote2_attr_group);
+
+ ati_remote2_urb_cleanup(ar2);
+
+ usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
+
+ kfree(ar2);
+}
+
+static int ati_remote2_suspend(struct usb_interface *interface,
+ pm_message_t message)
+{
+ struct ati_remote2 *ar2;
+ struct usb_host_interface *alt = interface->cur_altsetting;
+
+ if (alt->desc.bInterfaceNumber)
+ return 0;
+
+ ar2 = usb_get_intfdata(interface);
+
+ dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+ mutex_lock(&ati_remote2_mutex);
+
+ if (ar2->flags & ATI_REMOTE2_OPENED)
+ ati_remote2_kill_urbs(ar2);
+
+ ar2->flags |= ATI_REMOTE2_SUSPENDED;
+
+ mutex_unlock(&ati_remote2_mutex);
+
+ return 0;
+}
+
+static int ati_remote2_resume(struct usb_interface *interface)
+{
+ struct ati_remote2 *ar2;
+ struct usb_host_interface *alt = interface->cur_altsetting;
+ int r = 0;
+
+ if (alt->desc.bInterfaceNumber)
+ return 0;
+
+ ar2 = usb_get_intfdata(interface);
+
+ dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+ mutex_lock(&ati_remote2_mutex);
+
+ if (ar2->flags & ATI_REMOTE2_OPENED)
+ r = ati_remote2_submit_urbs(ar2);
+
+ if (!r)
+ ar2->flags &= ~ATI_REMOTE2_SUSPENDED;
+
+ mutex_unlock(&ati_remote2_mutex);
+
+ return r;
+}
+
+static int ati_remote2_reset_resume(struct usb_interface *interface)
+{
+ struct ati_remote2 *ar2;
+ struct usb_host_interface *alt = interface->cur_altsetting;
+ int r = 0;
+
+ if (alt->desc.bInterfaceNumber)
+ return 0;
+
+ ar2 = usb_get_intfdata(interface);
+
+ dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+ mutex_lock(&ati_remote2_mutex);
+
+ r = ati_remote2_setup(ar2, ar2->channel_mask);
+ if (r)
+ goto out;
+
+ if (ar2->flags & ATI_REMOTE2_OPENED)
+ r = ati_remote2_submit_urbs(ar2);
+
+ if (!r)
+ ar2->flags &= ~ATI_REMOTE2_SUSPENDED;
+
+ out:
+ mutex_unlock(&ati_remote2_mutex);
+
+ return r;
+}
+
+static int ati_remote2_pre_reset(struct usb_interface *interface)
+{
+ struct ati_remote2 *ar2;
+ struct usb_host_interface *alt = interface->cur_altsetting;
+
+ if (alt->desc.bInterfaceNumber)
+ return 0;
+
+ ar2 = usb_get_intfdata(interface);
+
+ dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+ mutex_lock(&ati_remote2_mutex);
+
+ if (ar2->flags == ATI_REMOTE2_OPENED)
+ ati_remote2_kill_urbs(ar2);
+
+ return 0;
+}
+
+static int ati_remote2_post_reset(struct usb_interface *interface)
+{
+ struct ati_remote2 *ar2;
+ struct usb_host_interface *alt = interface->cur_altsetting;
+ int r = 0;
+
+ if (alt->desc.bInterfaceNumber)
+ return 0;
+
+ ar2 = usb_get_intfdata(interface);
+
+ dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
+
+ if (ar2->flags == ATI_REMOTE2_OPENED)
+ r = ati_remote2_submit_urbs(ar2);
+
+ mutex_unlock(&ati_remote2_mutex);
+
+ return r;
+}
+
+module_usb_driver(ati_remote2_driver);
diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c
new file mode 100644
index 00000000..5d440236
--- /dev/null
+++ b/drivers/input/misc/atlas_btns.c
@@ -0,0 +1,159 @@
+/*
+ * atlas_btns.c - Atlas Wallmount Touchscreen ACPI Extras
+ *
+ * Copyright (C) 2006 Jaya Kumar
+ * Based on Toshiba ACPI by John Belmonte and ASUS ACPI
+ * This work was sponsored by CIS(M) Sdn Bhd.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/types.h>
+#include <asm/uaccess.h>
+#include <acpi/acpi_drivers.h>
+
+#define ACPI_ATLAS_NAME "Atlas ACPI"
+#define ACPI_ATLAS_CLASS "Atlas"
+
+static unsigned short atlas_keymap[16];
+static struct input_dev *input_dev;
+
+/* button handling code */
+static acpi_status acpi_atlas_button_setup(acpi_handle region_handle,
+ u32 function, void *handler_context, void **return_context)
+{
+ *return_context =
+ (function != ACPI_REGION_DEACTIVATE) ? handler_context : NULL;
+
+ return AE_OK;
+}
+
+static acpi_status acpi_atlas_button_handler(u32 function,
+ acpi_physical_address address,
+ u32 bit_width, u64 *value,
+ void *handler_context, void *region_context)
+{
+ acpi_status status;
+
+ if (function == ACPI_WRITE) {
+ int code = address & 0x0f;
+ int key_down = !(address & 0x10);
+
+ input_event(input_dev, EV_MSC, MSC_SCAN, code);
+ input_report_key(input_dev, atlas_keymap[code], key_down);
+ input_sync(input_dev);
+
+ status = AE_OK;
+ } else {
+ pr_warn("shrugged on unexpected function: function=%x,address=%lx,value=%x\n",
+ function, (unsigned long)address, (u32)*value);
+ status = AE_BAD_PARAMETER;
+ }
+
+ return status;
+}
+
+static int atlas_acpi_button_add(struct acpi_device *device)
+{
+ acpi_status status;
+ int i;
+ int err;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ pr_err("unable to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ input_dev->name = "Atlas ACPI button driver";
+ input_dev->phys = "ASIM0000/atlas/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->keycode = atlas_keymap;
+ input_dev->keycodesize = sizeof(unsigned short);
+ input_dev->keycodemax = ARRAY_SIZE(atlas_keymap);
+
+ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+ __set_bit(EV_KEY, input_dev->evbit);
+ for (i = 0; i < ARRAY_SIZE(atlas_keymap); i++) {
+ if (i < 9) {
+ atlas_keymap[i] = KEY_F1 + i;
+ __set_bit(KEY_F1 + i, input_dev->keybit);
+ } else
+ atlas_keymap[i] = KEY_RESERVED;
+ }
+
+ err = input_register_device(input_dev);
+ if (err) {
+ pr_err("couldn't register input device\n");
+ input_free_device(input_dev);
+ return err;
+ }
+
+ /* hookup button handler */
+ status = acpi_install_address_space_handler(device->handle,
+ 0x81, &acpi_atlas_button_handler,
+ &acpi_atlas_button_setup, device);
+ if (ACPI_FAILURE(status)) {
+ pr_err("error installing addr spc handler\n");
+ input_unregister_device(input_dev);
+ err = -EINVAL;
+ }
+
+ return err;
+}
+
+static int atlas_acpi_button_remove(struct acpi_device *device)
+{
+ acpi_status status;
+
+ status = acpi_remove_address_space_handler(device->handle,
+ 0x81, &acpi_atlas_button_handler);
+ if (ACPI_FAILURE(status))
+ pr_err("error removing addr spc handler\n");
+
+ input_unregister_device(input_dev);
+
+ return 0;
+}
+
+static const struct acpi_device_id atlas_device_ids[] = {
+ {"ASIM0000", 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, atlas_device_ids);
+
+static struct acpi_driver atlas_acpi_driver = {
+ .name = ACPI_ATLAS_NAME,
+ .class = ACPI_ATLAS_CLASS,
+ .owner = THIS_MODULE,
+ .ids = atlas_device_ids,
+ .ops = {
+ .add = atlas_acpi_button_add,
+ .remove = atlas_acpi_button_remove,
+ },
+};
+module_acpi_driver(atlas_acpi_driver);
+
+MODULE_AUTHOR("Jaya Kumar");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Atlas button driver");
+
diff --git a/drivers/input/misc/bfin_rotary.c b/drivers/input/misc/bfin_rotary.c
new file mode 100644
index 00000000..a6666e14
--- /dev/null
+++ b/drivers/input/misc/bfin_rotary.c
@@ -0,0 +1,272 @@
+/*
+ * Rotary counter driver for Analog Devices Blackfin Processors
+ *
+ * Copyright 2008-2009 Analog Devices Inc.
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include <asm/portmux.h>
+#include <asm/bfin_rotary.h>
+
+static const u16 per_cnt[] = {
+ P_CNT_CUD,
+ P_CNT_CDG,
+ P_CNT_CZM,
+ 0
+};
+
+struct bfin_rot {
+ struct input_dev *input;
+ int irq;
+ unsigned int up_key;
+ unsigned int down_key;
+ unsigned int button_key;
+ unsigned int rel_code;
+ unsigned short cnt_config;
+ unsigned short cnt_imask;
+ unsigned short cnt_debounce;
+};
+
+static void report_key_event(struct input_dev *input, int keycode)
+{
+ /* simulate a press-n-release */
+ input_report_key(input, keycode, 1);
+ input_sync(input);
+ input_report_key(input, keycode, 0);
+ input_sync(input);
+}
+
+static void report_rotary_event(struct bfin_rot *rotary, int delta)
+{
+ struct input_dev *input = rotary->input;
+
+ if (rotary->up_key) {
+ report_key_event(input,
+ delta > 0 ? rotary->up_key : rotary->down_key);
+ } else {
+ input_report_rel(input, rotary->rel_code, delta);
+ input_sync(input);
+ }
+}
+
+static irqreturn_t bfin_rotary_isr(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct bfin_rot *rotary = platform_get_drvdata(pdev);
+ int delta;
+
+ switch (bfin_read_CNT_STATUS()) {
+
+ case ICII:
+ break;
+
+ case UCII:
+ case DCII:
+ delta = bfin_read_CNT_COUNTER();
+ if (delta)
+ report_rotary_event(rotary, delta);
+ break;
+
+ case CZMII:
+ report_key_event(rotary->input, rotary->button_key);
+ break;
+
+ default:
+ break;
+ }
+
+ bfin_write_CNT_COMMAND(W1LCNT_ZERO); /* Clear COUNTER */
+ bfin_write_CNT_STATUS(-1); /* Clear STATUS */
+
+ return IRQ_HANDLED;
+}
+
+static int bfin_rotary_probe(struct platform_device *pdev)
+{
+ struct bfin_rotary_platform_data *pdata = pdev->dev.platform_data;
+ struct bfin_rot *rotary;
+ struct input_dev *input;
+ int error;
+
+ /* Basic validation */
+ if ((pdata->rotary_up_key && !pdata->rotary_down_key) ||
+ (!pdata->rotary_up_key && pdata->rotary_down_key)) {
+ return -EINVAL;
+ }
+
+ error = peripheral_request_list(per_cnt, dev_name(&pdev->dev));
+ if (error) {
+ dev_err(&pdev->dev, "requesting peripherals failed\n");
+ return error;
+ }
+
+ rotary = kzalloc(sizeof(struct bfin_rot), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!rotary || !input) {
+ error = -ENOMEM;
+ goto out1;
+ }
+
+ rotary->input = input;
+
+ rotary->up_key = pdata->rotary_up_key;
+ rotary->down_key = pdata->rotary_down_key;
+ rotary->button_key = pdata->rotary_button_key;
+ rotary->rel_code = pdata->rotary_rel_code;
+
+ error = rotary->irq = platform_get_irq(pdev, 0);
+ if (error < 0)
+ goto out1;
+
+ input->name = pdev->name;
+ input->phys = "bfin-rotary/input0";
+ input->dev.parent = &pdev->dev;
+
+ input_set_drvdata(input, rotary);
+
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0001;
+ input->id.version = 0x0100;
+
+ if (rotary->up_key) {
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(rotary->up_key, input->keybit);
+ __set_bit(rotary->down_key, input->keybit);
+ } else {
+ __set_bit(EV_REL, input->evbit);
+ __set_bit(rotary->rel_code, input->relbit);
+ }
+
+ if (rotary->button_key) {
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(rotary->button_key, input->keybit);
+ }
+
+ error = request_irq(rotary->irq, bfin_rotary_isr,
+ 0, dev_name(&pdev->dev), pdev);
+ if (error) {
+ dev_err(&pdev->dev,
+ "unable to claim irq %d; error %d\n",
+ rotary->irq, error);
+ goto out1;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&pdev->dev,
+ "unable to register input device (%d)\n", error);
+ goto out2;
+ }
+
+ if (pdata->rotary_button_key)
+ bfin_write_CNT_IMASK(CZMIE);
+
+ if (pdata->mode & ROT_DEBE)
+ bfin_write_CNT_DEBOUNCE(pdata->debounce & DPRESCALE);
+
+ if (pdata->mode)
+ bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() |
+ (pdata->mode & ~CNTE));
+
+ bfin_write_CNT_IMASK(bfin_read_CNT_IMASK() | UCIE | DCIE);
+ bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() | CNTE);
+
+ platform_set_drvdata(pdev, rotary);
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+
+out2:
+ free_irq(rotary->irq, pdev);
+out1:
+ input_free_device(input);
+ kfree(rotary);
+ peripheral_free_list(per_cnt);
+
+ return error;
+}
+
+static int bfin_rotary_remove(struct platform_device *pdev)
+{
+ struct bfin_rot *rotary = platform_get_drvdata(pdev);
+
+ bfin_write_CNT_CONFIG(0);
+ bfin_write_CNT_IMASK(0);
+
+ free_irq(rotary->irq, pdev);
+ input_unregister_device(rotary->input);
+ peripheral_free_list(per_cnt);
+
+ kfree(rotary);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int bfin_rotary_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bfin_rot *rotary = platform_get_drvdata(pdev);
+
+ rotary->cnt_config = bfin_read_CNT_CONFIG();
+ rotary->cnt_imask = bfin_read_CNT_IMASK();
+ rotary->cnt_debounce = bfin_read_CNT_DEBOUNCE();
+
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(rotary->irq);
+
+ return 0;
+}
+
+static int bfin_rotary_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct bfin_rot *rotary = platform_get_drvdata(pdev);
+
+ bfin_write_CNT_DEBOUNCE(rotary->cnt_debounce);
+ bfin_write_CNT_IMASK(rotary->cnt_imask);
+ bfin_write_CNT_CONFIG(rotary->cnt_config & ~CNTE);
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(rotary->irq);
+
+ if (rotary->cnt_config & CNTE)
+ bfin_write_CNT_CONFIG(rotary->cnt_config);
+
+ return 0;
+}
+
+static const struct dev_pm_ops bfin_rotary_pm_ops = {
+ .suspend = bfin_rotary_suspend,
+ .resume = bfin_rotary_resume,
+};
+#endif
+
+static struct platform_driver bfin_rotary_device_driver = {
+ .probe = bfin_rotary_probe,
+ .remove = bfin_rotary_remove,
+ .driver = {
+ .name = "bfin-rotary",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &bfin_rotary_pm_ops,
+#endif
+ },
+};
+module_platform_driver(bfin_rotary_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Rotary Counter driver for Blackfin Processors");
+MODULE_ALIAS("platform:bfin-rotary");
diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c
new file mode 100644
index 00000000..865c2f9d
--- /dev/null
+++ b/drivers/input/misc/bma150.c
@@ -0,0 +1,668 @@
+/*
+ * Copyright (c) 2011 Bosch Sensortec GmbH
+ * Copyright (c) 2011 Unixphere
+ *
+ * This driver adds support for Bosch Sensortec's digital acceleration
+ * sensors BMA150 and SMB380.
+ * The SMB380 is fully compatible with BMA150 and only differs in packaging.
+ *
+ * The datasheet for the BMA150 chip can be found here:
+ * http://www.bosch-sensortec.com/content/language1/downloads/BST-BMA150-DS000-07.pdf
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/bma150.h>
+
+#define ABSMAX_ACC_VAL 0x01FF
+#define ABSMIN_ACC_VAL -(ABSMAX_ACC_VAL)
+
+/* Each axis is represented by a 2-byte data word */
+#define BMA150_XYZ_DATA_SIZE 6
+
+/* Input poll interval in milliseconds */
+#define BMA150_POLL_INTERVAL 10
+#define BMA150_POLL_MAX 200
+#define BMA150_POLL_MIN 0
+
+#define BMA150_MODE_NORMAL 0
+#define BMA150_MODE_SLEEP 2
+#define BMA150_MODE_WAKE_UP 3
+
+/* Data register addresses */
+#define BMA150_DATA_0_REG 0x00
+#define BMA150_DATA_1_REG 0x01
+#define BMA150_DATA_2_REG 0x02
+
+/* Control register addresses */
+#define BMA150_CTRL_0_REG 0x0A
+#define BMA150_CTRL_1_REG 0x0B
+#define BMA150_CTRL_2_REG 0x14
+#define BMA150_CTRL_3_REG 0x15
+
+/* Configuration/Setting register addresses */
+#define BMA150_CFG_0_REG 0x0C
+#define BMA150_CFG_1_REG 0x0D
+#define BMA150_CFG_2_REG 0x0E
+#define BMA150_CFG_3_REG 0x0F
+#define BMA150_CFG_4_REG 0x10
+#define BMA150_CFG_5_REG 0x11
+
+#define BMA150_CHIP_ID 2
+#define BMA150_CHIP_ID_REG BMA150_DATA_0_REG
+
+#define BMA150_ACC_X_LSB_REG BMA150_DATA_2_REG
+
+#define BMA150_SLEEP_POS 0
+#define BMA150_SLEEP_MSK 0x01
+#define BMA150_SLEEP_REG BMA150_CTRL_0_REG
+
+#define BMA150_BANDWIDTH_POS 0
+#define BMA150_BANDWIDTH_MSK 0x07
+#define BMA150_BANDWIDTH_REG BMA150_CTRL_2_REG
+
+#define BMA150_RANGE_POS 3
+#define BMA150_RANGE_MSK 0x18
+#define BMA150_RANGE_REG BMA150_CTRL_2_REG
+
+#define BMA150_WAKE_UP_POS 0
+#define BMA150_WAKE_UP_MSK 0x01
+#define BMA150_WAKE_UP_REG BMA150_CTRL_3_REG
+
+#define BMA150_SW_RES_POS 1
+#define BMA150_SW_RES_MSK 0x02
+#define BMA150_SW_RES_REG BMA150_CTRL_0_REG
+
+/* Any-motion interrupt register fields */
+#define BMA150_ANY_MOTION_EN_POS 6
+#define BMA150_ANY_MOTION_EN_MSK 0x40
+#define BMA150_ANY_MOTION_EN_REG BMA150_CTRL_1_REG
+
+#define BMA150_ANY_MOTION_DUR_POS 6
+#define BMA150_ANY_MOTION_DUR_MSK 0xC0
+#define BMA150_ANY_MOTION_DUR_REG BMA150_CFG_5_REG
+
+#define BMA150_ANY_MOTION_THRES_REG BMA150_CFG_4_REG
+
+/* Advanced interrupt register fields */
+#define BMA150_ADV_INT_EN_POS 6
+#define BMA150_ADV_INT_EN_MSK 0x40
+#define BMA150_ADV_INT_EN_REG BMA150_CTRL_3_REG
+
+/* High-G interrupt register fields */
+#define BMA150_HIGH_G_EN_POS 1
+#define BMA150_HIGH_G_EN_MSK 0x02
+#define BMA150_HIGH_G_EN_REG BMA150_CTRL_1_REG
+
+#define BMA150_HIGH_G_HYST_POS 3
+#define BMA150_HIGH_G_HYST_MSK 0x38
+#define BMA150_HIGH_G_HYST_REG BMA150_CFG_5_REG
+
+#define BMA150_HIGH_G_DUR_REG BMA150_CFG_3_REG
+#define BMA150_HIGH_G_THRES_REG BMA150_CFG_2_REG
+
+/* Low-G interrupt register fields */
+#define BMA150_LOW_G_EN_POS 0
+#define BMA150_LOW_G_EN_MSK 0x01
+#define BMA150_LOW_G_EN_REG BMA150_CTRL_1_REG
+
+#define BMA150_LOW_G_HYST_POS 0
+#define BMA150_LOW_G_HYST_MSK 0x07
+#define BMA150_LOW_G_HYST_REG BMA150_CFG_5_REG
+
+#define BMA150_LOW_G_DUR_REG BMA150_CFG_1_REG
+#define BMA150_LOW_G_THRES_REG BMA150_CFG_0_REG
+
+struct bma150_data {
+ struct i2c_client *client;
+ struct input_polled_dev *input_polled;
+ struct input_dev *input;
+ u8 mode;
+};
+
+/*
+ * The settings for the given range, bandwidth and interrupt features
+ * are stated and verified by Bosch Sensortec where they are configured
+ * to provide a generic sensitivity performance.
+ */
+static struct bma150_cfg default_cfg = {
+ .any_motion_int = 1,
+ .hg_int = 1,
+ .lg_int = 1,
+ .any_motion_dur = 0,
+ .any_motion_thres = 0,
+ .hg_hyst = 0,
+ .hg_dur = 150,
+ .hg_thres = 160,
+ .lg_hyst = 0,
+ .lg_dur = 150,
+ .lg_thres = 20,
+ .range = BMA150_RANGE_2G,
+ .bandwidth = BMA150_BW_50HZ
+};
+
+static int bma150_write_byte(struct i2c_client *client, u8 reg, u8 val)
+{
+ s32 ret;
+
+ /* As per specification, disable irq in between register writes */
+ if (client->irq)
+ disable_irq_nosync(client->irq);
+
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+
+ if (client->irq)
+ enable_irq(client->irq);
+
+ return ret;
+}
+
+static int bma150_set_reg_bits(struct i2c_client *client,
+ int val, int shift, u8 mask, u8 reg)
+{
+ int data;
+
+ data = i2c_smbus_read_byte_data(client, reg);
+ if (data < 0)
+ return data;
+
+ data = (data & ~mask) | ((val << shift) & mask);
+ return bma150_write_byte(client, reg, data);
+}
+
+static int bma150_set_mode(struct bma150_data *bma150, u8 mode)
+{
+ int error;
+
+ error = bma150_set_reg_bits(bma150->client, mode, BMA150_WAKE_UP_POS,
+ BMA150_WAKE_UP_MSK, BMA150_WAKE_UP_REG);
+ if (error)
+ return error;
+
+ error = bma150_set_reg_bits(bma150->client, mode, BMA150_SLEEP_POS,
+ BMA150_SLEEP_MSK, BMA150_SLEEP_REG);
+ if (error)
+ return error;
+
+ if (mode == BMA150_MODE_NORMAL)
+ msleep(2);
+
+ bma150->mode = mode;
+ return 0;
+}
+
+static int bma150_soft_reset(struct bma150_data *bma150)
+{
+ int error;
+
+ error = bma150_set_reg_bits(bma150->client, 1, BMA150_SW_RES_POS,
+ BMA150_SW_RES_MSK, BMA150_SW_RES_REG);
+ if (error)
+ return error;
+
+ msleep(2);
+ return 0;
+}
+
+static int bma150_set_range(struct bma150_data *bma150, u8 range)
+{
+ return bma150_set_reg_bits(bma150->client, range, BMA150_RANGE_POS,
+ BMA150_RANGE_MSK, BMA150_RANGE_REG);
+}
+
+static int bma150_set_bandwidth(struct bma150_data *bma150, u8 bw)
+{
+ return bma150_set_reg_bits(bma150->client, bw, BMA150_BANDWIDTH_POS,
+ BMA150_BANDWIDTH_MSK, BMA150_BANDWIDTH_REG);
+}
+
+static int bma150_set_low_g_interrupt(struct bma150_data *bma150,
+ u8 enable, u8 hyst, u8 dur, u8 thres)
+{
+ int error;
+
+ error = bma150_set_reg_bits(bma150->client, hyst,
+ BMA150_LOW_G_HYST_POS, BMA150_LOW_G_HYST_MSK,
+ BMA150_LOW_G_HYST_REG);
+ if (error)
+ return error;
+
+ error = bma150_write_byte(bma150->client, BMA150_LOW_G_DUR_REG, dur);
+ if (error)
+ return error;
+
+ error = bma150_write_byte(bma150->client, BMA150_LOW_G_THRES_REG, thres);
+ if (error)
+ return error;
+
+ return bma150_set_reg_bits(bma150->client, !!enable,
+ BMA150_LOW_G_EN_POS, BMA150_LOW_G_EN_MSK,
+ BMA150_LOW_G_EN_REG);
+}
+
+static int bma150_set_high_g_interrupt(struct bma150_data *bma150,
+ u8 enable, u8 hyst, u8 dur, u8 thres)
+{
+ int error;
+
+ error = bma150_set_reg_bits(bma150->client, hyst,
+ BMA150_HIGH_G_HYST_POS, BMA150_HIGH_G_HYST_MSK,
+ BMA150_HIGH_G_HYST_REG);
+ if (error)
+ return error;
+
+ error = bma150_write_byte(bma150->client,
+ BMA150_HIGH_G_DUR_REG, dur);
+ if (error)
+ return error;
+
+ error = bma150_write_byte(bma150->client,
+ BMA150_HIGH_G_THRES_REG, thres);
+ if (error)
+ return error;
+
+ return bma150_set_reg_bits(bma150->client, !!enable,
+ BMA150_HIGH_G_EN_POS, BMA150_HIGH_G_EN_MSK,
+ BMA150_HIGH_G_EN_REG);
+}
+
+
+static int bma150_set_any_motion_interrupt(struct bma150_data *bma150,
+ u8 enable, u8 dur, u8 thres)
+{
+ int error;
+
+ error = bma150_set_reg_bits(bma150->client, dur,
+ BMA150_ANY_MOTION_DUR_POS,
+ BMA150_ANY_MOTION_DUR_MSK,
+ BMA150_ANY_MOTION_DUR_REG);
+ if (error)
+ return error;
+
+ error = bma150_write_byte(bma150->client,
+ BMA150_ANY_MOTION_THRES_REG, thres);
+ if (error)
+ return error;
+
+ error = bma150_set_reg_bits(bma150->client, !!enable,
+ BMA150_ADV_INT_EN_POS, BMA150_ADV_INT_EN_MSK,
+ BMA150_ADV_INT_EN_REG);
+ if (error)
+ return error;
+
+ return bma150_set_reg_bits(bma150->client, !!enable,
+ BMA150_ANY_MOTION_EN_POS,
+ BMA150_ANY_MOTION_EN_MSK,
+ BMA150_ANY_MOTION_EN_REG);
+}
+
+static void bma150_report_xyz(struct bma150_data *bma150)
+{
+ u8 data[BMA150_XYZ_DATA_SIZE];
+ s16 x, y, z;
+ s32 ret;
+
+ ret = i2c_smbus_read_i2c_block_data(bma150->client,
+ BMA150_ACC_X_LSB_REG, BMA150_XYZ_DATA_SIZE, data);
+ if (ret != BMA150_XYZ_DATA_SIZE)
+ return;
+
+ x = ((0xc0 & data[0]) >> 6) | (data[1] << 2);
+ y = ((0xc0 & data[2]) >> 6) | (data[3] << 2);
+ z = ((0xc0 & data[4]) >> 6) | (data[5] << 2);
+
+ /* sign extension */
+ x = (s16) (x << 6) >> 6;
+ y = (s16) (y << 6) >> 6;
+ z = (s16) (z << 6) >> 6;
+
+ input_report_abs(bma150->input, ABS_X, x);
+ input_report_abs(bma150->input, ABS_Y, y);
+ input_report_abs(bma150->input, ABS_Z, z);
+ input_sync(bma150->input);
+}
+
+static irqreturn_t bma150_irq_thread(int irq, void *dev)
+{
+ bma150_report_xyz(dev);
+
+ return IRQ_HANDLED;
+}
+
+static void bma150_poll(struct input_polled_dev *dev)
+{
+ bma150_report_xyz(dev->private);
+}
+
+static int bma150_open(struct bma150_data *bma150)
+{
+ int error;
+
+ error = pm_runtime_get_sync(&bma150->client->dev);
+ if (error < 0 && error != -ENOSYS)
+ return error;
+
+ /*
+ * See if runtime PM woke up the device. If runtime PM
+ * is disabled we need to do it ourselves.
+ */
+ if (bma150->mode != BMA150_MODE_NORMAL) {
+ error = bma150_set_mode(bma150, BMA150_MODE_NORMAL);
+ if (error)
+ return error;
+ }
+
+ return 0;
+}
+
+static void bma150_close(struct bma150_data *bma150)
+{
+ pm_runtime_put_sync(&bma150->client->dev);
+
+ if (bma150->mode != BMA150_MODE_SLEEP)
+ bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+}
+
+static int bma150_irq_open(struct input_dev *input)
+{
+ struct bma150_data *bma150 = input_get_drvdata(input);
+
+ return bma150_open(bma150);
+}
+
+static void bma150_irq_close(struct input_dev *input)
+{
+ struct bma150_data *bma150 = input_get_drvdata(input);
+
+ bma150_close(bma150);
+}
+
+static void bma150_poll_open(struct input_polled_dev *ipoll_dev)
+{
+ struct bma150_data *bma150 = ipoll_dev->private;
+
+ bma150_open(bma150);
+}
+
+static void bma150_poll_close(struct input_polled_dev *ipoll_dev)
+{
+ struct bma150_data *bma150 = ipoll_dev->private;
+
+ bma150_close(bma150);
+}
+
+static int bma150_initialize(struct bma150_data *bma150,
+ const struct bma150_cfg *cfg)
+{
+ int error;
+
+ error = bma150_soft_reset(bma150);
+ if (error)
+ return error;
+
+ error = bma150_set_bandwidth(bma150, cfg->bandwidth);
+ if (error)
+ return error;
+
+ error = bma150_set_range(bma150, cfg->range);
+ if (error)
+ return error;
+
+ if (bma150->client->irq) {
+ error = bma150_set_any_motion_interrupt(bma150,
+ cfg->any_motion_int,
+ cfg->any_motion_dur,
+ cfg->any_motion_thres);
+ if (error)
+ return error;
+
+ error = bma150_set_high_g_interrupt(bma150,
+ cfg->hg_int, cfg->hg_hyst,
+ cfg->hg_dur, cfg->hg_thres);
+ if (error)
+ return error;
+
+ error = bma150_set_low_g_interrupt(bma150,
+ cfg->lg_int, cfg->lg_hyst,
+ cfg->lg_dur, cfg->lg_thres);
+ if (error)
+ return error;
+ }
+
+ return bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+}
+
+static void bma150_init_input_device(struct bma150_data *bma150,
+ struct input_dev *idev)
+{
+ idev->name = BMA150_DRIVER;
+ idev->phys = BMA150_DRIVER "/input0";
+ idev->id.bustype = BUS_I2C;
+ idev->dev.parent = &bma150->client->dev;
+
+ idev->evbit[0] = BIT_MASK(EV_ABS);
+ input_set_abs_params(idev, ABS_X, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+ input_set_abs_params(idev, ABS_Y, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+ input_set_abs_params(idev, ABS_Z, ABSMIN_ACC_VAL, ABSMAX_ACC_VAL, 0, 0);
+}
+
+static int bma150_register_input_device(struct bma150_data *bma150)
+{
+ struct input_dev *idev;
+ int error;
+
+ idev = input_allocate_device();
+ if (!idev)
+ return -ENOMEM;
+
+ bma150_init_input_device(bma150, idev);
+
+ idev->open = bma150_irq_open;
+ idev->close = bma150_irq_close;
+ input_set_drvdata(idev, bma150);
+
+ error = input_register_device(idev);
+ if (error) {
+ input_free_device(idev);
+ return error;
+ }
+
+ bma150->input = idev;
+ return 0;
+}
+
+static int bma150_register_polled_device(struct bma150_data *bma150)
+{
+ struct input_polled_dev *ipoll_dev;
+ int error;
+
+ ipoll_dev = input_allocate_polled_device();
+ if (!ipoll_dev)
+ return -ENOMEM;
+
+ ipoll_dev->private = bma150;
+ ipoll_dev->open = bma150_poll_open;
+ ipoll_dev->close = bma150_poll_close;
+ ipoll_dev->poll = bma150_poll;
+ ipoll_dev->poll_interval = BMA150_POLL_INTERVAL;
+ ipoll_dev->poll_interval_min = BMA150_POLL_MIN;
+ ipoll_dev->poll_interval_max = BMA150_POLL_MAX;
+
+ bma150_init_input_device(bma150, ipoll_dev->input);
+
+ error = input_register_polled_device(ipoll_dev);
+ if (error) {
+ input_free_polled_device(ipoll_dev);
+ return error;
+ }
+
+ bma150->input_polled = ipoll_dev;
+ bma150->input = ipoll_dev->input;
+
+ return 0;
+}
+
+static int bma150_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct bma150_platform_data *pdata = client->dev.platform_data;
+ const struct bma150_cfg *cfg;
+ struct bma150_data *bma150;
+ int chip_id;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "i2c_check_functionality error\n");
+ return -EIO;
+ }
+
+ chip_id = i2c_smbus_read_byte_data(client, BMA150_CHIP_ID_REG);
+ if (chip_id != BMA150_CHIP_ID) {
+ dev_err(&client->dev, "BMA150 chip id error: %d\n", chip_id);
+ return -EINVAL;
+ }
+
+ bma150 = kzalloc(sizeof(struct bma150_data), GFP_KERNEL);
+ if (!bma150)
+ return -ENOMEM;
+
+ bma150->client = client;
+
+ if (pdata) {
+ if (pdata->irq_gpio_cfg) {
+ error = pdata->irq_gpio_cfg();
+ if (error) {
+ dev_err(&client->dev,
+ "IRQ GPIO conf. error %d, error %d\n",
+ client->irq, error);
+ goto err_free_mem;
+ }
+ }
+ cfg = &pdata->cfg;
+ } else {
+ cfg = &default_cfg;
+ }
+
+ error = bma150_initialize(bma150, cfg);
+ if (error)
+ goto err_free_mem;
+
+ if (client->irq > 0) {
+ error = bma150_register_input_device(bma150);
+ if (error)
+ goto err_free_mem;
+
+ error = request_threaded_irq(client->irq,
+ NULL, bma150_irq_thread,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ BMA150_DRIVER, bma150);
+ if (error) {
+ dev_err(&client->dev,
+ "irq request failed %d, error %d\n",
+ client->irq, error);
+ input_unregister_device(bma150->input);
+ goto err_free_mem;
+ }
+ } else {
+ error = bma150_register_polled_device(bma150);
+ if (error)
+ goto err_free_mem;
+ }
+
+ i2c_set_clientdata(client, bma150);
+
+ pm_runtime_enable(&client->dev);
+
+ return 0;
+
+err_free_mem:
+ kfree(bma150);
+ return error;
+}
+
+static int bma150_remove(struct i2c_client *client)
+{
+ struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+ pm_runtime_disable(&client->dev);
+
+ if (client->irq > 0) {
+ free_irq(client->irq, bma150);
+ input_unregister_device(bma150->input);
+ } else {
+ input_unregister_polled_device(bma150->input_polled);
+ input_free_polled_device(bma150->input_polled);
+ }
+
+ kfree(bma150);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int bma150_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+ return bma150_set_mode(bma150, BMA150_MODE_SLEEP);
+}
+
+static int bma150_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+ return bma150_set_mode(bma150, BMA150_MODE_NORMAL);
+}
+#endif
+
+static UNIVERSAL_DEV_PM_OPS(bma150_pm, bma150_suspend, bma150_resume, NULL);
+
+static const struct i2c_device_id bma150_id[] = {
+ { "bma150", 0 },
+ { "smb380", 0 },
+ { "bma023", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, bma150_id);
+
+static struct i2c_driver bma150_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = BMA150_DRIVER,
+ .pm = &bma150_pm,
+ },
+ .class = I2C_CLASS_HWMON,
+ .id_table = bma150_id,
+ .probe = bma150_probe,
+ .remove = bma150_remove,
+};
+
+module_i2c_driver(bma150_driver);
+
+MODULE_AUTHOR("Albert Zhang <xu.zhang@bosch-sensortec.com>");
+MODULE_DESCRIPTION("BMA150 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/bma2x2.c b/drivers/input/misc/bma2x2.c
new file mode 100644
index 00000000..28ead0b5
--- /dev/null
+++ b/drivers/input/misc/bma2x2.c
@@ -0,0 +1,7564 @@
+/*!
+ * @section LICENSE
+ * $license$
+ *
+ * @filename $filename$
+ * @date 2015/07/13 15:32
+ * @id $id$
+ * @version 2.1.3
+ *
+ * @brief
+ * This file contains all function implementations for the BMA2X2 in linux
+*/
+
+#ifdef CONFIG_SIG_MOTION
+#undef CONFIG_HAS_EARLYSUSPEND
+#endif
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/irq.h>
+#include <asm/mach/irq.h>
+#include <linux/hrtimer.h>
+#include <linux/timer.h>
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#ifdef __KERNEL__
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/unistd.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#else
+#include <unistd.h>
+#include <sys/types.h>
+#include <string.h>
+#endif
+
+
+#include <linux/init.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/fcntl.h>
+#include <asm/uaccess.h>
+
+
+#include "bstclass.h"
+#include "bs_log.h"
+
+#define ACC_NAME "ACC"
+/*#define CONFIG_BMA_ENABLE_NEWDATA_INT 1*/
+
+#define SENSOR_NAME "bma2x2"
+#define ABSMIN -512
+#define ABSMAX 512
+#define SLOPE_THRESHOLD_VALUE 32
+#define SLOPE_DURATION_VALUE 1
+#define INTERRUPT_LATCH_MODE 13
+#define INTERRUPT_ENABLE 1
+#define INTERRUPT_DISABLE 0
+#define MAP_SLOPE_INTERRUPT 2
+#define SLOPE_X_INDEX 5
+#define SLOPE_Y_INDEX 6
+#define SLOPE_Z_INDEX 7
+#define BMA2X2_MAX_DELAY 200
+#define BMA2X2_RANGE_SET 3 /* +/- 2G */
+#define BMA2X2_BW_SET 12 /* 125HZ */
+
+#define LOW_G_INTERRUPT REL_Z
+#define HIGH_G_INTERRUPT REL_HWHEEL
+#define SLOP_INTERRUPT REL_DIAL
+#define DOUBLE_TAP_INTERRUPT REL_WHEEL
+#define SINGLE_TAP_INTERRUPT REL_MISC
+#define ORIENT_INTERRUPT ABS_PRESSURE
+#define FLAT_INTERRUPT ABS_DISTANCE
+#define SLOW_NO_MOTION_INTERRUPT REL_Y
+
+#define HIGH_G_INTERRUPT_X_HAPPENED 1
+#define HIGH_G_INTERRUPT_Y_HAPPENED 2
+#define HIGH_G_INTERRUPT_Z_HAPPENED 3
+#define HIGH_G_INTERRUPT_X_NEGATIVE_HAPPENED 4
+#define HIGH_G_INTERRUPT_Y_NEGATIVE_HAPPENED 5
+#define HIGH_G_INTERRUPT_Z_NEGATIVE_HAPPENED 6
+#define SLOPE_INTERRUPT_X_HAPPENED 7
+#define SLOPE_INTERRUPT_Y_HAPPENED 8
+#define SLOPE_INTERRUPT_Z_HAPPENED 9
+#define SLOPE_INTERRUPT_X_NEGATIVE_HAPPENED 10
+#define SLOPE_INTERRUPT_Y_NEGATIVE_HAPPENED 11
+#define SLOPE_INTERRUPT_Z_NEGATIVE_HAPPENED 12
+#define DOUBLE_TAP_INTERRUPT_HAPPENED 13
+#define SINGLE_TAP_INTERRUPT_HAPPENED 14
+#define UPWARD_PORTRAIT_UP_INTERRUPT_HAPPENED 15
+#define UPWARD_PORTRAIT_DOWN_INTERRUPT_HAPPENED 16
+#define UPWARD_LANDSCAPE_LEFT_INTERRUPT_HAPPENED 17
+#define UPWARD_LANDSCAPE_RIGHT_INTERRUPT_HAPPENED 18
+#define DOWNWARD_PORTRAIT_UP_INTERRUPT_HAPPENED 19
+#define DOWNWARD_PORTRAIT_DOWN_INTERRUPT_HAPPENED 20
+#define DOWNWARD_LANDSCAPE_LEFT_INTERRUPT_HAPPENED 21
+#define DOWNWARD_LANDSCAPE_RIGHT_INTERRUPT_HAPPENED 22
+#define FLAT_INTERRUPT_TURE_HAPPENED 23
+#define FLAT_INTERRUPT_FALSE_HAPPENED 24
+#define LOW_G_INTERRUPT_HAPPENED 25
+#define SLOW_NO_MOTION_INTERRUPT_HAPPENED 26
+
+#define PAD_LOWG 0
+#define PAD_HIGHG 1
+#define PAD_SLOP 2
+#define PAD_DOUBLE_TAP 3
+#define PAD_SINGLE_TAP 4
+#define PAD_ORIENT 5
+#define PAD_FLAT 6
+#define PAD_SLOW_NO_MOTION 7
+
+#define BMA2X2_EEP_OFFSET 0x16
+#define BMA2X2_IMAGE_BASE 0x38
+#define BMA2X2_IMAGE_LEN 22
+
+#define BMA2X2_CHIP_ID_REG 0x00
+#define BMA2X2_VERSION_REG 0x01
+#define BMA2X2_X_AXIS_LSB_REG 0x02
+#define BMA2X2_X_AXIS_MSB_REG 0x03
+#define BMA2X2_Y_AXIS_LSB_REG 0x04
+#define BMA2X2_Y_AXIS_MSB_REG 0x05
+#define BMA2X2_Z_AXIS_LSB_REG 0x06
+#define BMA2X2_Z_AXIS_MSB_REG 0x07
+#define BMA2X2_TEMPERATURE_REG 0x08
+#define BMA2X2_STATUS1_REG 0x09
+#define BMA2X2_STATUS2_REG 0x0A
+#define BMA2X2_STATUS_TAP_SLOPE_REG 0x0B
+#define BMA2X2_STATUS_ORIENT_HIGH_REG 0x0C
+#define BMA2X2_STATUS_FIFO_REG 0x0E
+#define BMA2X2_RANGE_SEL_REG 0x0F
+#define BMA2X2_BW_SEL_REG 0x10
+#define BMA2X2_MODE_CTRL_REG 0x11
+#define BMA2X2_LOW_NOISE_CTRL_REG 0x12
+#define BMA2X2_DATA_CTRL_REG 0x13
+#define BMA2X2_RESET_REG 0x14
+#define BMA2X2_INT_ENABLE1_REG 0x16
+#define BMA2X2_INT_ENABLE2_REG 0x17
+#define BMA2X2_INT_SLO_NO_MOT_REG 0x18
+#define BMA2X2_INT1_PAD_SEL_REG 0x19
+#define BMA2X2_INT_DATA_SEL_REG 0x1A
+#define BMA2X2_INT2_PAD_SEL_REG 0x1B
+#define BMA2X2_INT_SRC_REG 0x1E
+#define BMA2X2_INT_SET_REG 0x20
+#define BMA2X2_INT_CTRL_REG 0x21
+#define BMA2X2_LOW_DURN_REG 0x22
+#define BMA2X2_LOW_THRES_REG 0x23
+#define BMA2X2_LOW_HIGH_HYST_REG 0x24
+#define BMA2X2_HIGH_DURN_REG 0x25
+#define BMA2X2_HIGH_THRES_REG 0x26
+#define BMA2X2_SLOPE_DURN_REG 0x27
+#define BMA2X2_SLOPE_THRES_REG 0x28
+#define BMA2X2_SLO_NO_MOT_THRES_REG 0x29
+#define BMA2X2_TAP_PARAM_REG 0x2A
+#define BMA2X2_TAP_THRES_REG 0x2B
+#define BMA2X2_ORIENT_PARAM_REG 0x2C
+#define BMA2X2_THETA_BLOCK_REG 0x2D
+#define BMA2X2_THETA_FLAT_REG 0x2E
+#define BMA2X2_FLAT_HOLD_TIME_REG 0x2F
+#define BMA2X2_FIFO_WML_TRIG 0x30
+#define BMA2X2_SELF_TEST_REG 0x32
+#define BMA2X2_EEPROM_CTRL_REG 0x33
+#define BMA2X2_SERIAL_CTRL_REG 0x34
+#define BMA2X2_EXTMODE_CTRL_REG 0x35
+#define BMA2X2_OFFSET_CTRL_REG 0x36
+#define BMA2X2_OFFSET_PARAMS_REG 0x37
+#define BMA2X2_OFFSET_X_AXIS_REG 0x38
+#define BMA2X2_OFFSET_Y_AXIS_REG 0x39
+#define BMA2X2_OFFSET_Z_AXIS_REG 0x3A
+#define BMA2X2_GP0_REG 0x3B
+#define BMA2X2_GP1_REG 0x3C
+#define BMA2X2_FIFO_MODE_REG 0x3E
+#define BMA2X2_FIFO_DATA_OUTPUT_REG 0x3F
+
+#define BMA2X2_CHIP_ID__POS 0
+#define BMA2X2_CHIP_ID__MSK 0xFF
+#define BMA2X2_CHIP_ID__LEN 8
+#define BMA2X2_CHIP_ID__REG BMA2X2_CHIP_ID_REG
+
+#define BMA2X2_VERSION__POS 0
+#define BMA2X2_VERSION__LEN 8
+#define BMA2X2_VERSION__MSK 0xFF
+#define BMA2X2_VERSION__REG BMA2X2_VERSION_REG
+
+#define BMA2x2_SLO_NO_MOT_DUR__POS 2
+#define BMA2x2_SLO_NO_MOT_DUR__LEN 6
+#define BMA2x2_SLO_NO_MOT_DUR__MSK 0xFC
+#define BMA2x2_SLO_NO_MOT_DUR__REG BMA2X2_SLOPE_DURN_REG
+
+#define BMA2X2_NEW_DATA_X__POS 0
+#define BMA2X2_NEW_DATA_X__LEN 1
+#define BMA2X2_NEW_DATA_X__MSK 0x01
+#define BMA2X2_NEW_DATA_X__REG BMA2X2_X_AXIS_LSB_REG
+
+#define BMA2X2_ACC_X14_LSB__POS 2
+#define BMA2X2_ACC_X14_LSB__LEN 6
+#define BMA2X2_ACC_X14_LSB__MSK 0xFC
+#define BMA2X2_ACC_X14_LSB__REG BMA2X2_X_AXIS_LSB_REG
+
+#define BMA2X2_ACC_X12_LSB__POS 4
+#define BMA2X2_ACC_X12_LSB__LEN 4
+#define BMA2X2_ACC_X12_LSB__MSK 0xF0
+#define BMA2X2_ACC_X12_LSB__REG BMA2X2_X_AXIS_LSB_REG
+
+#define BMA2X2_ACC_X10_LSB__POS 6
+#define BMA2X2_ACC_X10_LSB__LEN 2
+#define BMA2X2_ACC_X10_LSB__MSK 0xC0
+#define BMA2X2_ACC_X10_LSB__REG BMA2X2_X_AXIS_LSB_REG
+
+#define BMA2X2_ACC_X8_LSB__POS 0
+#define BMA2X2_ACC_X8_LSB__LEN 0
+#define BMA2X2_ACC_X8_LSB__MSK 0x00
+#define BMA2X2_ACC_X8_LSB__REG BMA2X2_X_AXIS_LSB_REG
+
+#define BMA2X2_ACC_X_MSB__POS 0
+#define BMA2X2_ACC_X_MSB__LEN 8
+#define BMA2X2_ACC_X_MSB__MSK 0xFF
+#define BMA2X2_ACC_X_MSB__REG BMA2X2_X_AXIS_MSB_REG
+
+#define BMA2X2_NEW_DATA_Y__POS 0
+#define BMA2X2_NEW_DATA_Y__LEN 1
+#define BMA2X2_NEW_DATA_Y__MSK 0x01
+#define BMA2X2_NEW_DATA_Y__REG BMA2X2_Y_AXIS_LSB_REG
+
+#define BMA2X2_ACC_Y14_LSB__POS 2
+#define BMA2X2_ACC_Y14_LSB__LEN 6
+#define BMA2X2_ACC_Y14_LSB__MSK 0xFC
+#define BMA2X2_ACC_Y14_LSB__REG BMA2X2_Y_AXIS_LSB_REG
+
+#define BMA2X2_ACC_Y12_LSB__POS 4
+#define BMA2X2_ACC_Y12_LSB__LEN 4
+#define BMA2X2_ACC_Y12_LSB__MSK 0xF0
+#define BMA2X2_ACC_Y12_LSB__REG BMA2X2_Y_AXIS_LSB_REG
+
+#define BMA2X2_ACC_Y10_LSB__POS 6
+#define BMA2X2_ACC_Y10_LSB__LEN 2
+#define BMA2X2_ACC_Y10_LSB__MSK 0xC0
+#define BMA2X2_ACC_Y10_LSB__REG BMA2X2_Y_AXIS_LSB_REG
+
+#define BMA2X2_ACC_Y8_LSB__POS 0
+#define BMA2X2_ACC_Y8_LSB__LEN 0
+#define BMA2X2_ACC_Y8_LSB__MSK 0x00
+#define BMA2X2_ACC_Y8_LSB__REG BMA2X2_Y_AXIS_LSB_REG
+
+#define BMA2X2_ACC_Y_MSB__POS 0
+#define BMA2X2_ACC_Y_MSB__LEN 8
+#define BMA2X2_ACC_Y_MSB__MSK 0xFF
+#define BMA2X2_ACC_Y_MSB__REG BMA2X2_Y_AXIS_MSB_REG
+
+#define BMA2X2_NEW_DATA_Z__POS 0
+#define BMA2X2_NEW_DATA_Z__LEN 1
+#define BMA2X2_NEW_DATA_Z__MSK 0x01
+#define BMA2X2_NEW_DATA_Z__REG BMA2X2_Z_AXIS_LSB_REG
+
+#define BMA2X2_ACC_Z14_LSB__POS 2
+#define BMA2X2_ACC_Z14_LSB__LEN 6
+#define BMA2X2_ACC_Z14_LSB__MSK 0xFC
+#define BMA2X2_ACC_Z14_LSB__REG BMA2X2_Z_AXIS_LSB_REG
+
+#define BMA2X2_ACC_Z12_LSB__POS 4
+#define BMA2X2_ACC_Z12_LSB__LEN 4
+#define BMA2X2_ACC_Z12_LSB__MSK 0xF0
+#define BMA2X2_ACC_Z12_LSB__REG BMA2X2_Z_AXIS_LSB_REG
+
+#define BMA2X2_ACC_Z10_LSB__POS 6
+#define BMA2X2_ACC_Z10_LSB__LEN 2
+#define BMA2X2_ACC_Z10_LSB__MSK 0xC0
+#define BMA2X2_ACC_Z10_LSB__REG BMA2X2_Z_AXIS_LSB_REG
+
+#define BMA2X2_ACC_Z8_LSB__POS 0
+#define BMA2X2_ACC_Z8_LSB__LEN 0
+#define BMA2X2_ACC_Z8_LSB__MSK 0x00
+#define BMA2X2_ACC_Z8_LSB__REG BMA2X2_Z_AXIS_LSB_REG
+
+#define BMA2X2_ACC_Z_MSB__POS 0
+#define BMA2X2_ACC_Z_MSB__LEN 8
+#define BMA2X2_ACC_Z_MSB__MSK 0xFF
+#define BMA2X2_ACC_Z_MSB__REG BMA2X2_Z_AXIS_MSB_REG
+
+#define BMA2X2_TEMPERATURE__POS 0
+#define BMA2X2_TEMPERATURE__LEN 8
+#define BMA2X2_TEMPERATURE__MSK 0xFF
+#define BMA2X2_TEMPERATURE__REG BMA2X2_TEMP_RD_REG
+
+#define BMA2X2_LOWG_INT_S__POS 0
+#define BMA2X2_LOWG_INT_S__LEN 1
+#define BMA2X2_LOWG_INT_S__MSK 0x01
+#define BMA2X2_LOWG_INT_S__REG BMA2X2_STATUS1_REG
+
+#define BMA2X2_HIGHG_INT_S__POS 1
+#define BMA2X2_HIGHG_INT_S__LEN 1
+#define BMA2X2_HIGHG_INT_S__MSK 0x02
+#define BMA2X2_HIGHG_INT_S__REG BMA2X2_STATUS1_REG
+
+#define BMA2X2_SLOPE_INT_S__POS 2
+#define BMA2X2_SLOPE_INT_S__LEN 1
+#define BMA2X2_SLOPE_INT_S__MSK 0x04
+#define BMA2X2_SLOPE_INT_S__REG BMA2X2_STATUS1_REG
+
+
+#define BMA2X2_SLO_NO_MOT_INT_S__POS 3
+#define BMA2X2_SLO_NO_MOT_INT_S__LEN 1
+#define BMA2X2_SLO_NO_MOT_INT_S__MSK 0x08
+#define BMA2X2_SLO_NO_MOT_INT_S__REG BMA2X2_STATUS1_REG
+
+#define BMA2X2_DOUBLE_TAP_INT_S__POS 4
+#define BMA2X2_DOUBLE_TAP_INT_S__LEN 1
+#define BMA2X2_DOUBLE_TAP_INT_S__MSK 0x10
+#define BMA2X2_DOUBLE_TAP_INT_S__REG BMA2X2_STATUS1_REG
+
+#define BMA2X2_SINGLE_TAP_INT_S__POS 5
+#define BMA2X2_SINGLE_TAP_INT_S__LEN 1
+#define BMA2X2_SINGLE_TAP_INT_S__MSK 0x20
+#define BMA2X2_SINGLE_TAP_INT_S__REG BMA2X2_STATUS1_REG
+
+#define BMA2X2_ORIENT_INT_S__POS 6
+#define BMA2X2_ORIENT_INT_S__LEN 1
+#define BMA2X2_ORIENT_INT_S__MSK 0x40
+#define BMA2X2_ORIENT_INT_S__REG BMA2X2_STATUS1_REG
+
+#define BMA2X2_FLAT_INT_S__POS 7
+#define BMA2X2_FLAT_INT_S__LEN 1
+#define BMA2X2_FLAT_INT_S__MSK 0x80
+#define BMA2X2_FLAT_INT_S__REG BMA2X2_STATUS1_REG
+
+#define BMA2X2_FIFO_FULL_INT_S__POS 5
+#define BMA2X2_FIFO_FULL_INT_S__LEN 1
+#define BMA2X2_FIFO_FULL_INT_S__MSK 0x20
+#define BMA2X2_FIFO_FULL_INT_S__REG BMA2X2_STATUS2_REG
+
+#define BMA2X2_FIFO_WM_INT_S__POS 6
+#define BMA2X2_FIFO_WM_INT_S__LEN 1
+#define BMA2X2_FIFO_WM_INT_S__MSK 0x40
+#define BMA2X2_FIFO_WM_INT_S__REG BMA2X2_STATUS2_REG
+
+#define BMA2X2_DATA_INT_S__POS 7
+#define BMA2X2_DATA_INT_S__LEN 1
+#define BMA2X2_DATA_INT_S__MSK 0x80
+#define BMA2X2_DATA_INT_S__REG BMA2X2_STATUS2_REG
+
+#define BMA2X2_SLOPE_FIRST_X__POS 0
+#define BMA2X2_SLOPE_FIRST_X__LEN 1
+#define BMA2X2_SLOPE_FIRST_X__MSK 0x01
+#define BMA2X2_SLOPE_FIRST_X__REG BMA2X2_STATUS_TAP_SLOPE_REG
+
+#define BMA2X2_SLOPE_FIRST_Y__POS 1
+#define BMA2X2_SLOPE_FIRST_Y__LEN 1
+#define BMA2X2_SLOPE_FIRST_Y__MSK 0x02
+#define BMA2X2_SLOPE_FIRST_Y__REG BMA2X2_STATUS_TAP_SLOPE_REG
+
+#define BMA2X2_SLOPE_FIRST_Z__POS 2
+#define BMA2X2_SLOPE_FIRST_Z__LEN 1
+#define BMA2X2_SLOPE_FIRST_Z__MSK 0x04
+#define BMA2X2_SLOPE_FIRST_Z__REG BMA2X2_STATUS_TAP_SLOPE_REG
+
+#define BMA2X2_SLOPE_SIGN_S__POS 3
+#define BMA2X2_SLOPE_SIGN_S__LEN 1
+#define BMA2X2_SLOPE_SIGN_S__MSK 0x08
+#define BMA2X2_SLOPE_SIGN_S__REG BMA2X2_STATUS_TAP_SLOPE_REG
+
+#define BMA2X2_TAP_FIRST_X__POS 4
+#define BMA2X2_TAP_FIRST_X__LEN 1
+#define BMA2X2_TAP_FIRST_X__MSK 0x10
+#define BMA2X2_TAP_FIRST_X__REG BMA2X2_STATUS_TAP_SLOPE_REG
+
+#define BMA2X2_TAP_FIRST_Y__POS 5
+#define BMA2X2_TAP_FIRST_Y__LEN 1
+#define BMA2X2_TAP_FIRST_Y__MSK 0x20
+#define BMA2X2_TAP_FIRST_Y__REG BMA2X2_STATUS_TAP_SLOPE_REG
+
+#define BMA2X2_TAP_FIRST_Z__POS 6
+#define BMA2X2_TAP_FIRST_Z__LEN 1
+#define BMA2X2_TAP_FIRST_Z__MSK 0x40
+#define BMA2X2_TAP_FIRST_Z__REG BMA2X2_STATUS_TAP_SLOPE_REG
+
+#define BMA2X2_TAP_SIGN_S__POS 7
+#define BMA2X2_TAP_SIGN_S__LEN 1
+#define BMA2X2_TAP_SIGN_S__MSK 0x80
+#define BMA2X2_TAP_SIGN_S__REG BMA2X2_STATUS_TAP_SLOPE_REG
+
+#define BMA2X2_HIGHG_FIRST_X__POS 0
+#define BMA2X2_HIGHG_FIRST_X__LEN 1
+#define BMA2X2_HIGHG_FIRST_X__MSK 0x01
+#define BMA2X2_HIGHG_FIRST_X__REG BMA2X2_STATUS_ORIENT_HIGH_REG
+
+#define BMA2X2_HIGHG_FIRST_Y__POS 1
+#define BMA2X2_HIGHG_FIRST_Y__LEN 1
+#define BMA2X2_HIGHG_FIRST_Y__MSK 0x02
+#define BMA2X2_HIGHG_FIRST_Y__REG BMA2X2_STATUS_ORIENT_HIGH_REG
+
+#define BMA2X2_HIGHG_FIRST_Z__POS 2
+#define BMA2X2_HIGHG_FIRST_Z__LEN 1
+#define BMA2X2_HIGHG_FIRST_Z__MSK 0x04
+#define BMA2X2_HIGHG_FIRST_Z__REG BMA2X2_STATUS_ORIENT_HIGH_REG
+
+#define BMA2X2_HIGHG_SIGN_S__POS 3
+#define BMA2X2_HIGHG_SIGN_S__LEN 1
+#define BMA2X2_HIGHG_SIGN_S__MSK 0x08
+#define BMA2X2_HIGHG_SIGN_S__REG BMA2X2_STATUS_ORIENT_HIGH_REG
+
+#define BMA2X2_ORIENT_S__POS 4
+#define BMA2X2_ORIENT_S__LEN 3
+#define BMA2X2_ORIENT_S__MSK 0x70
+#define BMA2X2_ORIENT_S__REG BMA2X2_STATUS_ORIENT_HIGH_REG
+
+#define BMA2X2_FLAT_S__POS 7
+#define BMA2X2_FLAT_S__LEN 1
+#define BMA2X2_FLAT_S__MSK 0x80
+#define BMA2X2_FLAT_S__REG BMA2X2_STATUS_ORIENT_HIGH_REG
+
+#define BMA2X2_FIFO_FRAME_COUNTER_S__POS 0
+#define BMA2X2_FIFO_FRAME_COUNTER_S__LEN 7
+#define BMA2X2_FIFO_FRAME_COUNTER_S__MSK 0x7F
+#define BMA2X2_FIFO_FRAME_COUNTER_S__REG BMA2X2_STATUS_FIFO_REG
+#define BMA2X2_FIFO_OVERRUN_S__POS 7
+#define BMA2X2_FIFO_OVERRUN_S__LEN 1
+#define BMA2X2_FIFO_OVERRUN_S__MSK 0x80
+#define BMA2X2_FIFO_OVERRUN_S__REG BMA2X2_STATUS_FIFO_REG
+
+#define BMA2X2_RANGE_SEL__POS 0
+#define BMA2X2_RANGE_SEL__LEN 4
+#define BMA2X2_RANGE_SEL__MSK 0x0F
+#define BMA2X2_RANGE_SEL__REG BMA2X2_RANGE_SEL_REG
+
+#define BMA2X2_BANDWIDTH__POS 0
+#define BMA2X2_BANDWIDTH__LEN 5
+#define BMA2X2_BANDWIDTH__MSK 0x1F
+#define BMA2X2_BANDWIDTH__REG BMA2X2_BW_SEL_REG
+
+#define BMA2X2_SLEEP_DUR__POS 1
+#define BMA2X2_SLEEP_DUR__LEN 4
+#define BMA2X2_SLEEP_DUR__MSK 0x1E
+#define BMA2X2_SLEEP_DUR__REG BMA2X2_MODE_CTRL_REG
+
+#define BMA2X2_MODE_CTRL__POS 5
+#define BMA2X2_MODE_CTRL__LEN 3
+#define BMA2X2_MODE_CTRL__MSK 0xE0
+#define BMA2X2_MODE_CTRL__REG BMA2X2_MODE_CTRL_REG
+
+#define BMA2X2_DEEP_SUSPEND__POS 5
+#define BMA2X2_DEEP_SUSPEND__LEN 1
+#define BMA2X2_DEEP_SUSPEND__MSK 0x20
+#define BMA2X2_DEEP_SUSPEND__REG BMA2X2_MODE_CTRL_REG
+
+#define BMA2X2_EN_LOW_POWER__POS 6
+#define BMA2X2_EN_LOW_POWER__LEN 1
+#define BMA2X2_EN_LOW_POWER__MSK 0x40
+#define BMA2X2_EN_LOW_POWER__REG BMA2X2_MODE_CTRL_REG
+
+#define BMA2X2_EN_SUSPEND__POS 7
+#define BMA2X2_EN_SUSPEND__LEN 1
+#define BMA2X2_EN_SUSPEND__MSK 0x80
+#define BMA2X2_EN_SUSPEND__REG BMA2X2_MODE_CTRL_REG
+
+#define BMA2X2_SLEEP_TIMER__POS 5
+#define BMA2X2_SLEEP_TIMER__LEN 1
+#define BMA2X2_SLEEP_TIMER__MSK 0x20
+#define BMA2X2_SLEEP_TIMER__REG BMA2X2_LOW_NOISE_CTRL_REG
+
+#define BMA2X2_LOW_POWER_MODE__POS 6
+#define BMA2X2_LOW_POWER_MODE__LEN 1
+#define BMA2X2_LOW_POWER_MODE__MSK 0x40
+#define BMA2X2_LOW_POWER_MODE__REG BMA2X2_LOW_NOISE_CTRL_REG
+
+#define BMA2X2_EN_LOW_NOISE__POS 7
+#define BMA2X2_EN_LOW_NOISE__LEN 1
+#define BMA2X2_EN_LOW_NOISE__MSK 0x80
+#define BMA2X2_EN_LOW_NOISE__REG BMA2X2_LOW_NOISE_CTRL_REG
+
+#define BMA2X2_DIS_SHADOW_PROC__POS 6
+#define BMA2X2_DIS_SHADOW_PROC__LEN 1
+#define BMA2X2_DIS_SHADOW_PROC__MSK 0x40
+#define BMA2X2_DIS_SHADOW_PROC__REG BMA2X2_DATA_CTRL_REG
+
+#define BMA2X2_EN_DATA_HIGH_BW__POS 7
+#define BMA2X2_EN_DATA_HIGH_BW__LEN 1
+#define BMA2X2_EN_DATA_HIGH_BW__MSK 0x80
+#define BMA2X2_EN_DATA_HIGH_BW__REG BMA2X2_DATA_CTRL_REG
+
+#define BMA2X2_EN_SOFT_RESET__POS 0
+#define BMA2X2_EN_SOFT_RESET__LEN 8
+#define BMA2X2_EN_SOFT_RESET__MSK 0xFF
+#define BMA2X2_EN_SOFT_RESET__REG BMA2X2_RESET_REG
+
+#define BMA2X2_EN_SOFT_RESET_VALUE 0xB6
+
+#define BMA2X2_EN_SLOPE_X_INT__POS 0
+#define BMA2X2_EN_SLOPE_X_INT__LEN 1
+#define BMA2X2_EN_SLOPE_X_INT__MSK 0x01
+#define BMA2X2_EN_SLOPE_X_INT__REG BMA2X2_INT_ENABLE1_REG
+
+#define BMA2X2_EN_SLOPE_Y_INT__POS 1
+#define BMA2X2_EN_SLOPE_Y_INT__LEN 1
+#define BMA2X2_EN_SLOPE_Y_INT__MSK 0x02
+#define BMA2X2_EN_SLOPE_Y_INT__REG BMA2X2_INT_ENABLE1_REG
+
+#define BMA2X2_EN_SLOPE_Z_INT__POS 2
+#define BMA2X2_EN_SLOPE_Z_INT__LEN 1
+#define BMA2X2_EN_SLOPE_Z_INT__MSK 0x04
+#define BMA2X2_EN_SLOPE_Z_INT__REG BMA2X2_INT_ENABLE1_REG
+
+#define BMA2X2_EN_DOUBLE_TAP_INT__POS 4
+#define BMA2X2_EN_DOUBLE_TAP_INT__LEN 1
+#define BMA2X2_EN_DOUBLE_TAP_INT__MSK 0x10
+#define BMA2X2_EN_DOUBLE_TAP_INT__REG BMA2X2_INT_ENABLE1_REG
+
+#define BMA2X2_EN_SINGLE_TAP_INT__POS 5
+#define BMA2X2_EN_SINGLE_TAP_INT__LEN 1
+#define BMA2X2_EN_SINGLE_TAP_INT__MSK 0x20
+#define BMA2X2_EN_SINGLE_TAP_INT__REG BMA2X2_INT_ENABLE1_REG
+
+#define BMA2X2_EN_ORIENT_INT__POS 6
+#define BMA2X2_EN_ORIENT_INT__LEN 1
+#define BMA2X2_EN_ORIENT_INT__MSK 0x40
+#define BMA2X2_EN_ORIENT_INT__REG BMA2X2_INT_ENABLE1_REG
+
+#define BMA2X2_EN_FLAT_INT__POS 7
+#define BMA2X2_EN_FLAT_INT__LEN 1
+#define BMA2X2_EN_FLAT_INT__MSK 0x80
+#define BMA2X2_EN_FLAT_INT__REG BMA2X2_INT_ENABLE1_REG
+
+#define BMA2X2_EN_HIGHG_X_INT__POS 0
+#define BMA2X2_EN_HIGHG_X_INT__LEN 1
+#define BMA2X2_EN_HIGHG_X_INT__MSK 0x01
+#define BMA2X2_EN_HIGHG_X_INT__REG BMA2X2_INT_ENABLE2_REG
+
+#define BMA2X2_EN_HIGHG_Y_INT__POS 1
+#define BMA2X2_EN_HIGHG_Y_INT__LEN 1
+#define BMA2X2_EN_HIGHG_Y_INT__MSK 0x02
+#define BMA2X2_EN_HIGHG_Y_INT__REG BMA2X2_INT_ENABLE2_REG
+
+#define BMA2X2_EN_HIGHG_Z_INT__POS 2
+#define BMA2X2_EN_HIGHG_Z_INT__LEN 1
+#define BMA2X2_EN_HIGHG_Z_INT__MSK 0x04
+#define BMA2X2_EN_HIGHG_Z_INT__REG BMA2X2_INT_ENABLE2_REG
+
+#define BMA2X2_EN_LOWG_INT__POS 3
+#define BMA2X2_EN_LOWG_INT__LEN 1
+#define BMA2X2_EN_LOWG_INT__MSK 0x08
+#define BMA2X2_EN_LOWG_INT__REG BMA2X2_INT_ENABLE2_REG
+
+#define BMA2X2_EN_NEW_DATA_INT__POS 4
+#define BMA2X2_EN_NEW_DATA_INT__LEN 1
+#define BMA2X2_EN_NEW_DATA_INT__MSK 0x10
+#define BMA2X2_EN_NEW_DATA_INT__REG BMA2X2_INT_ENABLE2_REG
+
+#define BMA2X2_INT_FFULL_EN_INT__POS 5
+#define BMA2X2_INT_FFULL_EN_INT__LEN 1
+#define BMA2X2_INT_FFULL_EN_INT__MSK 0x20
+#define BMA2X2_INT_FFULL_EN_INT__REG BMA2X2_INT_ENABLE2_REG
+
+#define BMA2X2_INT_FWM_EN_INT__POS 6
+#define BMA2X2_INT_FWM_EN_INT__LEN 1
+#define BMA2X2_INT_FWM_EN_INT__MSK 0x40
+#define BMA2X2_INT_FWM_EN_INT__REG BMA2X2_INT_ENABLE2_REG
+
+#define BMA2X2_INT_SLO_NO_MOT_EN_X_INT__POS 0
+#define BMA2X2_INT_SLO_NO_MOT_EN_X_INT__LEN 1
+#define BMA2X2_INT_SLO_NO_MOT_EN_X_INT__MSK 0x01
+#define BMA2X2_INT_SLO_NO_MOT_EN_X_INT__REG BMA2X2_INT_SLO_NO_MOT_REG
+
+#define BMA2X2_INT_SLO_NO_MOT_EN_Y_INT__POS 1
+#define BMA2X2_INT_SLO_NO_MOT_EN_Y_INT__LEN 1
+#define BMA2X2_INT_SLO_NO_MOT_EN_Y_INT__MSK 0x02
+#define BMA2X2_INT_SLO_NO_MOT_EN_Y_INT__REG BMA2X2_INT_SLO_NO_MOT_REG
+
+#define BMA2X2_INT_SLO_NO_MOT_EN_Z_INT__POS 2
+#define BMA2X2_INT_SLO_NO_MOT_EN_Z_INT__LEN 1
+#define BMA2X2_INT_SLO_NO_MOT_EN_Z_INT__MSK 0x04
+#define BMA2X2_INT_SLO_NO_MOT_EN_Z_INT__REG BMA2X2_INT_SLO_NO_MOT_REG
+
+#define BMA2X2_INT_SLO_NO_MOT_EN_SEL_INT__POS 3
+#define BMA2X2_INT_SLO_NO_MOT_EN_SEL_INT__LEN 1
+#define BMA2X2_INT_SLO_NO_MOT_EN_SEL_INT__MSK 0x08
+#define BMA2X2_INT_SLO_NO_MOT_EN_SEL_INT__REG BMA2X2_INT_SLO_NO_MOT_REG
+
+#define BMA2X2_EN_INT1_PAD_LOWG__POS 0
+#define BMA2X2_EN_INT1_PAD_LOWG__LEN 1
+#define BMA2X2_EN_INT1_PAD_LOWG__MSK 0x01
+#define BMA2X2_EN_INT1_PAD_LOWG__REG BMA2X2_INT1_PAD_SEL_REG
+
+#define BMA2X2_EN_INT1_PAD_HIGHG__POS 1
+#define BMA2X2_EN_INT1_PAD_HIGHG__LEN 1
+#define BMA2X2_EN_INT1_PAD_HIGHG__MSK 0x02
+#define BMA2X2_EN_INT1_PAD_HIGHG__REG BMA2X2_INT1_PAD_SEL_REG
+
+#define BMA2X2_EN_INT1_PAD_SLOPE__POS 2
+#define BMA2X2_EN_INT1_PAD_SLOPE__LEN 1
+#define BMA2X2_EN_INT1_PAD_SLOPE__MSK 0x04
+#define BMA2X2_EN_INT1_PAD_SLOPE__REG BMA2X2_INT1_PAD_SEL_REG
+
+#define BMA2X2_EN_INT1_PAD_SLO_NO_MOT__POS 3
+#define BMA2X2_EN_INT1_PAD_SLO_NO_MOT__LEN 1
+#define BMA2X2_EN_INT1_PAD_SLO_NO_MOT__MSK 0x08
+#define BMA2X2_EN_INT1_PAD_SLO_NO_MOT__REG BMA2X2_INT1_PAD_SEL_REG
+
+#define BMA2X2_EN_INT1_PAD_DB_TAP__POS 4
+#define BMA2X2_EN_INT1_PAD_DB_TAP__LEN 1
+#define BMA2X2_EN_INT1_PAD_DB_TAP__MSK 0x10
+#define BMA2X2_EN_INT1_PAD_DB_TAP__REG BMA2X2_INT1_PAD_SEL_REG
+
+#define BMA2X2_EN_INT1_PAD_SNG_TAP__POS 5
+#define BMA2X2_EN_INT1_PAD_SNG_TAP__LEN 1
+#define BMA2X2_EN_INT1_PAD_SNG_TAP__MSK 0x20
+#define BMA2X2_EN_INT1_PAD_SNG_TAP__REG BMA2X2_INT1_PAD_SEL_REG
+
+#define BMA2X2_EN_INT1_PAD_ORIENT__POS 6
+#define BMA2X2_EN_INT1_PAD_ORIENT__LEN 1
+#define BMA2X2_EN_INT1_PAD_ORIENT__MSK 0x40
+#define BMA2X2_EN_INT1_PAD_ORIENT__REG BMA2X2_INT1_PAD_SEL_REG
+
+#define BMA2X2_EN_INT1_PAD_FLAT__POS 7
+#define BMA2X2_EN_INT1_PAD_FLAT__LEN 1
+#define BMA2X2_EN_INT1_PAD_FLAT__MSK 0x80
+#define BMA2X2_EN_INT1_PAD_FLAT__REG BMA2X2_INT1_PAD_SEL_REG
+
+#define BMA2X2_EN_INT2_PAD_LOWG__POS 0
+#define BMA2X2_EN_INT2_PAD_LOWG__LEN 1
+#define BMA2X2_EN_INT2_PAD_LOWG__MSK 0x01
+#define BMA2X2_EN_INT2_PAD_LOWG__REG BMA2X2_INT2_PAD_SEL_REG
+
+#define BMA2X2_EN_INT2_PAD_HIGHG__POS 1
+#define BMA2X2_EN_INT2_PAD_HIGHG__LEN 1
+#define BMA2X2_EN_INT2_PAD_HIGHG__MSK 0x02
+#define BMA2X2_EN_INT2_PAD_HIGHG__REG BMA2X2_INT2_PAD_SEL_REG
+
+#define BMA2X2_EN_INT2_PAD_SLOPE__POS 2
+#define BMA2X2_EN_INT2_PAD_SLOPE__LEN 1
+#define BMA2X2_EN_INT2_PAD_SLOPE__MSK 0x04
+#define BMA2X2_EN_INT2_PAD_SLOPE__REG BMA2X2_INT2_PAD_SEL_REG
+
+#define BMA2X2_EN_INT2_PAD_SLO_NO_MOT__POS 3
+#define BMA2X2_EN_INT2_PAD_SLO_NO_MOT__LEN 1
+#define BMA2X2_EN_INT2_PAD_SLO_NO_MOT__MSK 0x08
+#define BMA2X2_EN_INT2_PAD_SLO_NO_MOT__REG BMA2X2_INT2_PAD_SEL_REG
+
+#define BMA2X2_EN_INT2_PAD_DB_TAP__POS 4
+#define BMA2X2_EN_INT2_PAD_DB_TAP__LEN 1
+#define BMA2X2_EN_INT2_PAD_DB_TAP__MSK 0x10
+#define BMA2X2_EN_INT2_PAD_DB_TAP__REG BMA2X2_INT2_PAD_SEL_REG
+
+#define BMA2X2_EN_INT2_PAD_SNG_TAP__POS 5
+#define BMA2X2_EN_INT2_PAD_SNG_TAP__LEN 1
+#define BMA2X2_EN_INT2_PAD_SNG_TAP__MSK 0x20
+#define BMA2X2_EN_INT2_PAD_SNG_TAP__REG BMA2X2_INT2_PAD_SEL_REG
+
+#define BMA2X2_EN_INT2_PAD_ORIENT__POS 6
+#define BMA2X2_EN_INT2_PAD_ORIENT__LEN 1
+#define BMA2X2_EN_INT2_PAD_ORIENT__MSK 0x40
+#define BMA2X2_EN_INT2_PAD_ORIENT__REG BMA2X2_INT2_PAD_SEL_REG
+
+#define BMA2X2_EN_INT2_PAD_FLAT__POS 7
+#define BMA2X2_EN_INT2_PAD_FLAT__LEN 1
+#define BMA2X2_EN_INT2_PAD_FLAT__MSK 0x80
+#define BMA2X2_EN_INT2_PAD_FLAT__REG BMA2X2_INT2_PAD_SEL_REG
+
+#define BMA2X2_EN_INT1_PAD_NEWDATA__POS 0
+#define BMA2X2_EN_INT1_PAD_NEWDATA__LEN 1
+#define BMA2X2_EN_INT1_PAD_NEWDATA__MSK 0x01
+#define BMA2X2_EN_INT1_PAD_NEWDATA__REG BMA2X2_INT_DATA_SEL_REG
+
+#define BMA2X2_EN_INT1_PAD_FWM__POS 1
+#define BMA2X2_EN_INT1_PAD_FWM__LEN 1
+#define BMA2X2_EN_INT1_PAD_FWM__MSK 0x02
+#define BMA2X2_EN_INT1_PAD_FWM__REG BMA2X2_INT_DATA_SEL_REG
+
+#define BMA2X2_EN_INT1_PAD_FFULL__POS 2
+#define BMA2X2_EN_INT1_PAD_FFULL__LEN 1
+#define BMA2X2_EN_INT1_PAD_FFULL__MSK 0x04
+#define BMA2X2_EN_INT1_PAD_FFULL__REG BMA2X2_INT_DATA_SEL_REG
+
+#define BMA2X2_EN_INT2_PAD_FFULL__POS 5
+#define BMA2X2_EN_INT2_PAD_FFULL__LEN 1
+#define BMA2X2_EN_INT2_PAD_FFULL__MSK 0x20
+#define BMA2X2_EN_INT2_PAD_FFULL__REG BMA2X2_INT_DATA_SEL_REG
+
+#define BMA2X2_EN_INT2_PAD_FWM__POS 6
+#define BMA2X2_EN_INT2_PAD_FWM__LEN 1
+#define BMA2X2_EN_INT2_PAD_FWM__MSK 0x40
+#define BMA2X2_EN_INT2_PAD_FWM__REG BMA2X2_INT_DATA_SEL_REG
+
+#define BMA2X2_EN_INT2_PAD_NEWDATA__POS 7
+#define BMA2X2_EN_INT2_PAD_NEWDATA__LEN 1
+#define BMA2X2_EN_INT2_PAD_NEWDATA__MSK 0x80
+#define BMA2X2_EN_INT2_PAD_NEWDATA__REG BMA2X2_INT_DATA_SEL_REG
+
+#define BMA2X2_UNFILT_INT_SRC_LOWG__POS 0
+#define BMA2X2_UNFILT_INT_SRC_LOWG__LEN 1
+#define BMA2X2_UNFILT_INT_SRC_LOWG__MSK 0x01
+#define BMA2X2_UNFILT_INT_SRC_LOWG__REG BMA2X2_INT_SRC_REG
+
+#define BMA2X2_UNFILT_INT_SRC_HIGHG__POS 1
+#define BMA2X2_UNFILT_INT_SRC_HIGHG__LEN 1
+#define BMA2X2_UNFILT_INT_SRC_HIGHG__MSK 0x02
+#define BMA2X2_UNFILT_INT_SRC_HIGHG__REG BMA2X2_INT_SRC_REG
+
+#define BMA2X2_UNFILT_INT_SRC_SLOPE__POS 2
+#define BMA2X2_UNFILT_INT_SRC_SLOPE__LEN 1
+#define BMA2X2_UNFILT_INT_SRC_SLOPE__MSK 0x04
+#define BMA2X2_UNFILT_INT_SRC_SLOPE__REG BMA2X2_INT_SRC_REG
+
+#define BMA2X2_UNFILT_INT_SRC_SLO_NO_MOT__POS 3
+#define BMA2X2_UNFILT_INT_SRC_SLO_NO_MOT__LEN 1
+#define BMA2X2_UNFILT_INT_SRC_SLO_NO_MOT__MSK 0x08
+#define BMA2X2_UNFILT_INT_SRC_SLO_NO_MOT__REG BMA2X2_INT_SRC_REG
+
+#define BMA2X2_UNFILT_INT_SRC_TAP__POS 4
+#define BMA2X2_UNFILT_INT_SRC_TAP__LEN 1
+#define BMA2X2_UNFILT_INT_SRC_TAP__MSK 0x10
+#define BMA2X2_UNFILT_INT_SRC_TAP__REG BMA2X2_INT_SRC_REG
+
+#define BMA2X2_UNFILT_INT_SRC_DATA__POS 5
+#define BMA2X2_UNFILT_INT_SRC_DATA__LEN 1
+#define BMA2X2_UNFILT_INT_SRC_DATA__MSK 0x20
+#define BMA2X2_UNFILT_INT_SRC_DATA__REG BMA2X2_INT_SRC_REG
+
+#define BMA2X2_INT1_PAD_ACTIVE_LEVEL__POS 0
+#define BMA2X2_INT1_PAD_ACTIVE_LEVEL__LEN 1
+#define BMA2X2_INT1_PAD_ACTIVE_LEVEL__MSK 0x01
+#define BMA2X2_INT1_PAD_ACTIVE_LEVEL__REG BMA2X2_INT_SET_REG
+
+#define BMA2X2_INT2_PAD_ACTIVE_LEVEL__POS 2
+#define BMA2X2_INT2_PAD_ACTIVE_LEVEL__LEN 1
+#define BMA2X2_INT2_PAD_ACTIVE_LEVEL__MSK 0x04
+#define BMA2X2_INT2_PAD_ACTIVE_LEVEL__REG BMA2X2_INT_SET_REG
+
+#define BMA2X2_INT1_PAD_OUTPUT_TYPE__POS 1
+#define BMA2X2_INT1_PAD_OUTPUT_TYPE__LEN 1
+#define BMA2X2_INT1_PAD_OUTPUT_TYPE__MSK 0x02
+#define BMA2X2_INT1_PAD_OUTPUT_TYPE__REG BMA2X2_INT_SET_REG
+
+#define BMA2X2_INT2_PAD_OUTPUT_TYPE__POS 3
+#define BMA2X2_INT2_PAD_OUTPUT_TYPE__LEN 1
+#define BMA2X2_INT2_PAD_OUTPUT_TYPE__MSK 0x08
+#define BMA2X2_INT2_PAD_OUTPUT_TYPE__REG BMA2X2_INT_SET_REG
+
+#define BMA2X2_INT_MODE_SEL__POS 0
+#define BMA2X2_INT_MODE_SEL__LEN 4
+#define BMA2X2_INT_MODE_SEL__MSK 0x0F
+#define BMA2X2_INT_MODE_SEL__REG BMA2X2_INT_CTRL_REG
+
+#define BMA2X2_RESET_INT__POS 7
+#define BMA2X2_RESET_INT__LEN 1
+#define BMA2X2_RESET_INT__MSK 0x80
+#define BMA2X2_RESET_INT__REG BMA2X2_INT_CTRL_REG
+
+#define BMA2X2_LOWG_DUR__POS 0
+#define BMA2X2_LOWG_DUR__LEN 8
+#define BMA2X2_LOWG_DUR__MSK 0xFF
+#define BMA2X2_LOWG_DUR__REG BMA2X2_LOW_DURN_REG
+
+#define BMA2X2_LOWG_THRES__POS 0
+#define BMA2X2_LOWG_THRES__LEN 8
+#define BMA2X2_LOWG_THRES__MSK 0xFF
+#define BMA2X2_LOWG_THRES__REG BMA2X2_LOW_THRES_REG
+
+#define BMA2X2_LOWG_HYST__POS 0
+#define BMA2X2_LOWG_HYST__LEN 2
+#define BMA2X2_LOWG_HYST__MSK 0x03
+#define BMA2X2_LOWG_HYST__REG BMA2X2_LOW_HIGH_HYST_REG
+
+#define BMA2X2_LOWG_INT_MODE__POS 2
+#define BMA2X2_LOWG_INT_MODE__LEN 1
+#define BMA2X2_LOWG_INT_MODE__MSK 0x04
+#define BMA2X2_LOWG_INT_MODE__REG BMA2X2_LOW_HIGH_HYST_REG
+
+#define BMA2X2_HIGHG_DUR__POS 0
+#define BMA2X2_HIGHG_DUR__LEN 8
+#define BMA2X2_HIGHG_DUR__MSK 0xFF
+#define BMA2X2_HIGHG_DUR__REG BMA2X2_HIGH_DURN_REG
+
+#define BMA2X2_HIGHG_THRES__POS 0
+#define BMA2X2_HIGHG_THRES__LEN 8
+#define BMA2X2_HIGHG_THRES__MSK 0xFF
+#define BMA2X2_HIGHG_THRES__REG BMA2X2_HIGH_THRES_REG
+
+#define BMA2X2_HIGHG_HYST__POS 6
+#define BMA2X2_HIGHG_HYST__LEN 2
+#define BMA2X2_HIGHG_HYST__MSK 0xC0
+#define BMA2X2_HIGHG_HYST__REG BMA2X2_LOW_HIGH_HYST_REG
+
+#define BMA2X2_SLOPE_DUR__POS 0
+#define BMA2X2_SLOPE_DUR__LEN 2
+#define BMA2X2_SLOPE_DUR__MSK 0x03
+#define BMA2X2_SLOPE_DUR__REG BMA2X2_SLOPE_DURN_REG
+
+#define BMA2X2_SLO_NO_MOT_DUR__POS 2
+#define BMA2X2_SLO_NO_MOT_DUR__LEN 6
+#define BMA2X2_SLO_NO_MOT_DUR__MSK 0xFC
+#define BMA2X2_SLO_NO_MOT_DUR__REG BMA2X2_SLOPE_DURN_REG
+
+#define BMA2X2_SLOPE_THRES__POS 0
+#define BMA2X2_SLOPE_THRES__LEN 8
+#define BMA2X2_SLOPE_THRES__MSK 0xFF
+#define BMA2X2_SLOPE_THRES__REG BMA2X2_SLOPE_THRES_REG
+
+#define BMA2X2_SLO_NO_MOT_THRES__POS 0
+#define BMA2X2_SLO_NO_MOT_THRES__LEN 8
+#define BMA2X2_SLO_NO_MOT_THRES__MSK 0xFF
+#define BMA2X2_SLO_NO_MOT_THRES__REG BMA2X2_SLO_NO_MOT_THRES_REG
+
+#define BMA2X2_TAP_DUR__POS 0
+#define BMA2X2_TAP_DUR__LEN 3
+#define BMA2X2_TAP_DUR__MSK 0x07
+#define BMA2X2_TAP_DUR__REG BMA2X2_TAP_PARAM_REG
+
+#define BMA2X2_TAP_SHOCK_DURN__POS 6
+#define BMA2X2_TAP_SHOCK_DURN__LEN 1
+#define BMA2X2_TAP_SHOCK_DURN__MSK 0x40
+#define BMA2X2_TAP_SHOCK_DURN__REG BMA2X2_TAP_PARAM_REG
+
+#define BMA2X2_ADV_TAP_INT__POS 5
+#define BMA2X2_ADV_TAP_INT__LEN 1
+#define BMA2X2_ADV_TAP_INT__MSK 0x20
+#define BMA2X2_ADV_TAP_INT__REG BMA2X2_TAP_PARAM_REG
+
+#define BMA2X2_TAP_QUIET_DURN__POS 7
+#define BMA2X2_TAP_QUIET_DURN__LEN 1
+#define BMA2X2_TAP_QUIET_DURN__MSK 0x80
+#define BMA2X2_TAP_QUIET_DURN__REG BMA2X2_TAP_PARAM_REG
+
+#define BMA2X2_TAP_THRES__POS 0
+#define BMA2X2_TAP_THRES__LEN 5
+#define BMA2X2_TAP_THRES__MSK 0x1F
+#define BMA2X2_TAP_THRES__REG BMA2X2_TAP_THRES_REG
+
+#define BMA2X2_TAP_SAMPLES__POS 6
+#define BMA2X2_TAP_SAMPLES__LEN 2
+#define BMA2X2_TAP_SAMPLES__MSK 0xC0
+#define BMA2X2_TAP_SAMPLES__REG BMA2X2_TAP_THRES_REG
+
+#define BMA2X2_ORIENT_MODE__POS 0
+#define BMA2X2_ORIENT_MODE__LEN 2
+#define BMA2X2_ORIENT_MODE__MSK 0x03
+#define BMA2X2_ORIENT_MODE__REG BMA2X2_ORIENT_PARAM_REG
+
+#define BMA2X2_ORIENT_BLOCK__POS 2
+#define BMA2X2_ORIENT_BLOCK__LEN 2
+#define BMA2X2_ORIENT_BLOCK__MSK 0x0C
+#define BMA2X2_ORIENT_BLOCK__REG BMA2X2_ORIENT_PARAM_REG
+
+#define BMA2X2_ORIENT_HYST__POS 4
+#define BMA2X2_ORIENT_HYST__LEN 3
+#define BMA2X2_ORIENT_HYST__MSK 0x70
+#define BMA2X2_ORIENT_HYST__REG BMA2X2_ORIENT_PARAM_REG
+
+#define BMA2X2_ORIENT_AXIS__POS 7
+#define BMA2X2_ORIENT_AXIS__LEN 1
+#define BMA2X2_ORIENT_AXIS__MSK 0x80
+#define BMA2X2_ORIENT_AXIS__REG BMA2X2_THETA_BLOCK_REG
+
+#define BMA2X2_ORIENT_UD_EN__POS 6
+#define BMA2X2_ORIENT_UD_EN__LEN 1
+#define BMA2X2_ORIENT_UD_EN__MSK 0x40
+#define BMA2X2_ORIENT_UD_EN__REG BMA2X2_THETA_BLOCK_REG
+
+#define BMA2X2_THETA_BLOCK__POS 0
+#define BMA2X2_THETA_BLOCK__LEN 6
+#define BMA2X2_THETA_BLOCK__MSK 0x3F
+#define BMA2X2_THETA_BLOCK__REG BMA2X2_THETA_BLOCK_REG
+
+#define BMA2X2_THETA_FLAT__POS 0
+#define BMA2X2_THETA_FLAT__LEN 6
+#define BMA2X2_THETA_FLAT__MSK 0x3F
+#define BMA2X2_THETA_FLAT__REG BMA2X2_THETA_FLAT_REG
+
+#define BMA2X2_FLAT_HOLD_TIME__POS 4
+#define BMA2X2_FLAT_HOLD_TIME__LEN 2
+#define BMA2X2_FLAT_HOLD_TIME__MSK 0x30
+#define BMA2X2_FLAT_HOLD_TIME__REG BMA2X2_FLAT_HOLD_TIME_REG
+
+#define BMA2X2_FLAT_HYS__POS 0
+#define BMA2X2_FLAT_HYS__LEN 3
+#define BMA2X2_FLAT_HYS__MSK 0x07
+#define BMA2X2_FLAT_HYS__REG BMA2X2_FLAT_HOLD_TIME_REG
+
+#define BMA2X2_FIFO_WML_TRIG_RETAIN__POS 0
+#define BMA2X2_FIFO_WML_TRIG_RETAIN__LEN 6
+#define BMA2X2_FIFO_WML_TRIG_RETAIN__MSK 0x3F
+#define BMA2X2_FIFO_WML_TRIG_RETAIN__REG BMA2X2_FIFO_WML_TRIG
+
+#define BMA2X2_EN_SELF_TEST__POS 0
+#define BMA2X2_EN_SELF_TEST__LEN 2
+#define BMA2X2_EN_SELF_TEST__MSK 0x03
+#define BMA2X2_EN_SELF_TEST__REG BMA2X2_SELF_TEST_REG
+
+#define BMA2X2_NEG_SELF_TEST__POS 2
+#define BMA2X2_NEG_SELF_TEST__LEN 1
+#define BMA2X2_NEG_SELF_TEST__MSK 0x04
+#define BMA2X2_NEG_SELF_TEST__REG BMA2X2_SELF_TEST_REG
+
+#define BMA2X2_SELF_TEST_AMP__POS 4
+#define BMA2X2_SELF_TEST_AMP__LEN 1
+#define BMA2X2_SELF_TEST_AMP__MSK 0x10
+#define BMA2X2_SELF_TEST_AMP__REG BMA2X2_SELF_TEST_REG
+
+
+#define BMA2X2_UNLOCK_EE_PROG_MODE__POS 0
+#define BMA2X2_UNLOCK_EE_PROG_MODE__LEN 1
+#define BMA2X2_UNLOCK_EE_PROG_MODE__MSK 0x01
+#define BMA2X2_UNLOCK_EE_PROG_MODE__REG BMA2X2_EEPROM_CTRL_REG
+
+#define BMA2X2_START_EE_PROG_TRIG__POS 1
+#define BMA2X2_START_EE_PROG_TRIG__LEN 1
+#define BMA2X2_START_EE_PROG_TRIG__MSK 0x02
+#define BMA2X2_START_EE_PROG_TRIG__REG BMA2X2_EEPROM_CTRL_REG
+
+#define BMA2X2_EE_PROG_READY__POS 2
+#define BMA2X2_EE_PROG_READY__LEN 1
+#define BMA2X2_EE_PROG_READY__MSK 0x04
+#define BMA2X2_EE_PROG_READY__REG BMA2X2_EEPROM_CTRL_REG
+
+#define BMA2X2_UPDATE_IMAGE__POS 3
+#define BMA2X2_UPDATE_IMAGE__LEN 1
+#define BMA2X2_UPDATE_IMAGE__MSK 0x08
+#define BMA2X2_UPDATE_IMAGE__REG BMA2X2_EEPROM_CTRL_REG
+
+#define BMA2X2_EE_REMAIN__POS 4
+#define BMA2X2_EE_REMAIN__LEN 4
+#define BMA2X2_EE_REMAIN__MSK 0xF0
+#define BMA2X2_EE_REMAIN__REG BMA2X2_EEPROM_CTRL_REG
+
+#define BMA2X2_EN_SPI_MODE_3__POS 0
+#define BMA2X2_EN_SPI_MODE_3__LEN 1
+#define BMA2X2_EN_SPI_MODE_3__MSK 0x01
+#define BMA2X2_EN_SPI_MODE_3__REG BMA2X2_SERIAL_CTRL_REG
+
+#define BMA2X2_I2C_WATCHDOG_PERIOD__POS 1
+#define BMA2X2_I2C_WATCHDOG_PERIOD__LEN 1
+#define BMA2X2_I2C_WATCHDOG_PERIOD__MSK 0x02
+#define BMA2X2_I2C_WATCHDOG_PERIOD__REG BMA2X2_SERIAL_CTRL_REG
+
+#define BMA2X2_EN_I2C_WATCHDOG__POS 2
+#define BMA2X2_EN_I2C_WATCHDOG__LEN 1
+#define BMA2X2_EN_I2C_WATCHDOG__MSK 0x04
+#define BMA2X2_EN_I2C_WATCHDOG__REG BMA2X2_SERIAL_CTRL_REG
+
+#define BMA2X2_EXT_MODE__POS 7
+#define BMA2X2_EXT_MODE__LEN 1
+#define BMA2X2_EXT_MODE__MSK 0x80
+#define BMA2X2_EXT_MODE__REG BMA2X2_EXTMODE_CTRL_REG
+
+#define BMA2X2_ALLOW_UPPER__POS 6
+#define BMA2X2_ALLOW_UPPER__LEN 1
+#define BMA2X2_ALLOW_UPPER__MSK 0x40
+#define BMA2X2_ALLOW_UPPER__REG BMA2X2_EXTMODE_CTRL_REG
+
+#define BMA2X2_MAP_2_LOWER__POS 5
+#define BMA2X2_MAP_2_LOWER__LEN 1
+#define BMA2X2_MAP_2_LOWER__MSK 0x20
+#define BMA2X2_MAP_2_LOWER__REG BMA2X2_EXTMODE_CTRL_REG
+
+#define BMA2X2_MAGIC_NUMBER__POS 0
+#define BMA2X2_MAGIC_NUMBER__LEN 5
+#define BMA2X2_MAGIC_NUMBER__MSK 0x1F
+#define BMA2X2_MAGIC_NUMBER__REG BMA2X2_EXTMODE_CTRL_REG
+
+#define BMA2X2_UNLOCK_EE_WRITE_TRIM__POS 4
+#define BMA2X2_UNLOCK_EE_WRITE_TRIM__LEN 4
+#define BMA2X2_UNLOCK_EE_WRITE_TRIM__MSK 0xF0
+#define BMA2X2_UNLOCK_EE_WRITE_TRIM__REG BMA2X2_CTRL_UNLOCK_REG
+
+#define BMA2X2_EN_SLOW_COMP_X__POS 0
+#define BMA2X2_EN_SLOW_COMP_X__LEN 1
+#define BMA2X2_EN_SLOW_COMP_X__MSK 0x01
+#define BMA2X2_EN_SLOW_COMP_X__REG BMA2X2_OFFSET_CTRL_REG
+
+#define BMA2X2_EN_SLOW_COMP_Y__POS 1
+#define BMA2X2_EN_SLOW_COMP_Y__LEN 1
+#define BMA2X2_EN_SLOW_COMP_Y__MSK 0x02
+#define BMA2X2_EN_SLOW_COMP_Y__REG BMA2X2_OFFSET_CTRL_REG
+
+#define BMA2X2_EN_SLOW_COMP_Z__POS 2
+#define BMA2X2_EN_SLOW_COMP_Z__LEN 1
+#define BMA2X2_EN_SLOW_COMP_Z__MSK 0x04
+#define BMA2X2_EN_SLOW_COMP_Z__REG BMA2X2_OFFSET_CTRL_REG
+
+#define BMA2X2_FAST_CAL_RDY_S__POS 4
+#define BMA2X2_FAST_CAL_RDY_S__LEN 1
+#define BMA2X2_FAST_CAL_RDY_S__MSK 0x10
+#define BMA2X2_FAST_CAL_RDY_S__REG BMA2X2_OFFSET_CTRL_REG
+
+#define BMA2X2_CAL_TRIGGER__POS 5
+#define BMA2X2_CAL_TRIGGER__LEN 2
+#define BMA2X2_CAL_TRIGGER__MSK 0x60
+#define BMA2X2_CAL_TRIGGER__REG BMA2X2_OFFSET_CTRL_REG
+
+#define BMA2X2_RESET_OFFSET_REGS__POS 7
+#define BMA2X2_RESET_OFFSET_REGS__LEN 1
+#define BMA2X2_RESET_OFFSET_REGS__MSK 0x80
+#define BMA2X2_RESET_OFFSET_REGS__REG BMA2X2_OFFSET_CTRL_REG
+
+#define BMA2X2_COMP_CUTOFF__POS 0
+#define BMA2X2_COMP_CUTOFF__LEN 1
+#define BMA2X2_COMP_CUTOFF__MSK 0x01
+#define BMA2X2_COMP_CUTOFF__REG BMA2X2_OFFSET_PARAMS_REG
+
+#define BMA2X2_COMP_TARGET_OFFSET_X__POS 1
+#define BMA2X2_COMP_TARGET_OFFSET_X__LEN 2
+#define BMA2X2_COMP_TARGET_OFFSET_X__MSK 0x06
+#define BMA2X2_COMP_TARGET_OFFSET_X__REG BMA2X2_OFFSET_PARAMS_REG
+
+#define BMA2X2_COMP_TARGET_OFFSET_Y__POS 3
+#define BMA2X2_COMP_TARGET_OFFSET_Y__LEN 2
+#define BMA2X2_COMP_TARGET_OFFSET_Y__MSK 0x18
+#define BMA2X2_COMP_TARGET_OFFSET_Y__REG BMA2X2_OFFSET_PARAMS_REG
+
+#define BMA2X2_COMP_TARGET_OFFSET_Z__POS 5
+#define BMA2X2_COMP_TARGET_OFFSET_Z__LEN 2
+#define BMA2X2_COMP_TARGET_OFFSET_Z__MSK 0x60
+#define BMA2X2_COMP_TARGET_OFFSET_Z__REG BMA2X2_OFFSET_PARAMS_REG
+
+#define BMA2X2_FIFO_DATA_SELECT__POS 0
+#define BMA2X2_FIFO_DATA_SELECT__LEN 2
+#define BMA2X2_FIFO_DATA_SELECT__MSK 0x03
+#define BMA2X2_FIFO_DATA_SELECT__REG BMA2X2_FIFO_MODE_REG
+
+#define BMA2X2_FIFO_TRIGGER_SOURCE__POS 2
+#define BMA2X2_FIFO_TRIGGER_SOURCE__LEN 2
+#define BMA2X2_FIFO_TRIGGER_SOURCE__MSK 0x0C
+#define BMA2X2_FIFO_TRIGGER_SOURCE__REG BMA2X2_FIFO_MODE_REG
+
+#define BMA2X2_FIFO_TRIGGER_ACTION__POS 4
+#define BMA2X2_FIFO_TRIGGER_ACTION__LEN 2
+#define BMA2X2_FIFO_TRIGGER_ACTION__MSK 0x30
+#define BMA2X2_FIFO_TRIGGER_ACTION__REG BMA2X2_FIFO_MODE_REG
+
+#define BMA2X2_FIFO_MODE__POS 6
+#define BMA2X2_FIFO_MODE__LEN 2
+#define BMA2X2_FIFO_MODE__MSK 0xC0
+#define BMA2X2_FIFO_MODE__REG BMA2X2_FIFO_MODE_REG
+
+
+#define BMA2X2_STATUS1 0
+#define BMA2X2_STATUS2 1
+#define BMA2X2_STATUS3 2
+#define BMA2X2_STATUS4 3
+#define BMA2X2_STATUS5 4
+
+
+#define BMA2X2_RANGE_2G 3
+#define BMA2X2_RANGE_4G 5
+#define BMA2X2_RANGE_8G 8
+#define BMA2X2_RANGE_16G 12
+
+
+#define BMA2X2_BW_7_81HZ 0x08
+#define BMA2X2_BW_15_63HZ 0x09
+#define BMA2X2_BW_31_25HZ 0x0A
+#define BMA2X2_BW_62_50HZ 0x0B
+#define BMA2X2_BW_125HZ 0x0C
+#define BMA2X2_BW_250HZ 0x0D
+#define BMA2X2_BW_500HZ 0x0E
+#define BMA2X2_BW_1000HZ 0x0F
+
+#define BMA2X2_SLEEP_DUR_0_5MS 0x05
+#define BMA2X2_SLEEP_DUR_1MS 0x06
+#define BMA2X2_SLEEP_DUR_2MS 0x07
+#define BMA2X2_SLEEP_DUR_4MS 0x08
+#define BMA2X2_SLEEP_DUR_6MS 0x09
+#define BMA2X2_SLEEP_DUR_10MS 0x0A
+#define BMA2X2_SLEEP_DUR_25MS 0x0B
+#define BMA2X2_SLEEP_DUR_50MS 0x0C
+#define BMA2X2_SLEEP_DUR_100MS 0x0D
+#define BMA2X2_SLEEP_DUR_500MS 0x0E
+#define BMA2X2_SLEEP_DUR_1S 0x0F
+
+#define BMA2X2_LATCH_DUR_NON_LATCH 0x00
+#define BMA2X2_LATCH_DUR_250MS 0x01
+#define BMA2X2_LATCH_DUR_500MS 0x02
+#define BMA2X2_LATCH_DUR_1S 0x03
+#define BMA2X2_LATCH_DUR_2S 0x04
+#define BMA2X2_LATCH_DUR_4S 0x05
+#define BMA2X2_LATCH_DUR_8S 0x06
+#define BMA2X2_LATCH_DUR_LATCH 0x07
+#define BMA2X2_LATCH_DUR_NON_LATCH1 0x08
+#define BMA2X2_LATCH_DUR_250US 0x09
+#define BMA2X2_LATCH_DUR_500US 0x0A
+#define BMA2X2_LATCH_DUR_1MS 0x0B
+#define BMA2X2_LATCH_DUR_12_5MS 0x0C
+#define BMA2X2_LATCH_DUR_25MS 0x0D
+#define BMA2X2_LATCH_DUR_50MS 0x0E
+#define BMA2X2_LATCH_DUR_LATCH1 0x0F
+
+#define BMA2X2_MODE_NORMAL 0
+#define BMA2X2_MODE_LOWPOWER1 1
+#define BMA2X2_MODE_SUSPEND 2
+#define BMA2X2_MODE_DEEP_SUSPEND 3
+#define BMA2X2_MODE_LOWPOWER2 4
+#define BMA2X2_MODE_STANDBY 5
+
+#define BMA2X2_X_AXIS 0
+#define BMA2X2_Y_AXIS 1
+#define BMA2X2_Z_AXIS 2
+
+#define BMA2X2_Low_G_Interrupt 0
+#define BMA2X2_High_G_X_Interrupt 1
+#define BMA2X2_High_G_Y_Interrupt 2
+#define BMA2X2_High_G_Z_Interrupt 3
+#define BMA2X2_DATA_EN 4
+#define BMA2X2_Slope_X_Interrupt 5
+#define BMA2X2_Slope_Y_Interrupt 6
+#define BMA2X2_Slope_Z_Interrupt 7
+#define BMA2X2_Single_Tap_Interrupt 8
+#define BMA2X2_Double_Tap_Interrupt 9
+#define BMA2X2_Orient_Interrupt 10
+#define BMA2X2_Flat_Interrupt 11
+#define BMA2X2_FFULL_INTERRUPT 12
+#define BMA2X2_FWM_INTERRUPT 13
+
+#define BMA2X2_INT1_LOWG 0
+#define BMA2X2_INT2_LOWG 1
+#define BMA2X2_INT1_HIGHG 0
+#define BMA2X2_INT2_HIGHG 1
+#define BMA2X2_INT1_SLOPE 0
+#define BMA2X2_INT2_SLOPE 1
+#define BMA2X2_INT1_SLO_NO_MOT 0
+#define BMA2X2_INT2_SLO_NO_MOT 1
+#define BMA2X2_INT1_DTAP 0
+#define BMA2X2_INT2_DTAP 1
+#define BMA2X2_INT1_STAP 0
+#define BMA2X2_INT2_STAP 1
+#define BMA2X2_INT1_ORIENT 0
+#define BMA2X2_INT2_ORIENT 1
+#define BMA2X2_INT1_FLAT 0
+#define BMA2X2_INT2_FLAT 1
+#define BMA2X2_INT1_NDATA 0
+#define BMA2X2_INT2_NDATA 1
+#define BMA2X2_INT1_FWM 0
+#define BMA2X2_INT2_FWM 1
+#define BMA2X2_INT1_FFULL 0
+#define BMA2X2_INT2_FFULL 1
+
+#define BMA2X2_SRC_LOWG 0
+#define BMA2X2_SRC_HIGHG 1
+#define BMA2X2_SRC_SLOPE 2
+#define BMA2X2_SRC_SLO_NO_MOT 3
+#define BMA2X2_SRC_TAP 4
+#define BMA2X2_SRC_DATA 5
+
+#define BMA2X2_INT1_OUTPUT 0
+#define BMA2X2_INT2_OUTPUT 1
+#define BMA2X2_INT1_LEVEL 0
+#define BMA2X2_INT2_LEVEL 1
+
+#define BMA2X2_LOW_DURATION 0
+#define BMA2X2_HIGH_DURATION 1
+#define BMA2X2_SLOPE_DURATION 2
+#define BMA2X2_SLO_NO_MOT_DURATION 3
+
+#define BMA2X2_LOW_THRESHOLD 0
+#define BMA2X2_HIGH_THRESHOLD 1
+#define BMA2X2_SLOPE_THRESHOLD 2
+#define BMA2X2_SLO_NO_MOT_THRESHOLD 3
+
+
+#define BMA2X2_LOWG_HYST 0
+#define BMA2X2_HIGHG_HYST 1
+
+#define BMA2X2_ORIENT_THETA 0
+#define BMA2X2_FLAT_THETA 1
+
+#define BMA2X2_I2C_SELECT 0
+#define BMA2X2_I2C_EN 1
+
+#define BMA2X2_SLOW_COMP_X 0
+#define BMA2X2_SLOW_COMP_Y 1
+#define BMA2X2_SLOW_COMP_Z 2
+
+#define BMA2X2_CUT_OFF 0
+#define BMA2X2_OFFSET_TRIGGER_X 1
+#define BMA2X2_OFFSET_TRIGGER_Y 2
+#define BMA2X2_OFFSET_TRIGGER_Z 3
+
+#define BMA2X2_GP0 0
+#define BMA2X2_GP1 1
+
+#define BMA2X2_SLO_NO_MOT_EN_X 0
+#define BMA2X2_SLO_NO_MOT_EN_Y 1
+#define BMA2X2_SLO_NO_MOT_EN_Z 2
+#define BMA2X2_SLO_NO_MOT_EN_SEL 3
+
+#define BMA2X2_WAKE_UP_DUR_20MS 0
+#define BMA2X2_WAKE_UP_DUR_80MS 1
+#define BMA2X2_WAKE_UP_DUR_320MS 2
+#define BMA2X2_WAKE_UP_DUR_2560MS 3
+
+#define BMA2X2_SELF_TEST0_ON 1
+#define BMA2X2_SELF_TEST1_ON 2
+
+#define BMA2X2_EE_W_OFF 0
+#define BMA2X2_EE_W_ON 1
+
+#define BMA2X2_LOW_TH_IN_G(gthres, range) ((256 * gthres) / range)
+
+
+#define BMA2X2_HIGH_TH_IN_G(gthres, range) ((256 * gthres) / range)
+
+
+#define BMA2X2_LOW_HY_IN_G(ghyst, range) ((32 * ghyst) / range)
+
+
+#define BMA2X2_HIGH_HY_IN_G(ghyst, range) ((32 * ghyst) / range)
+
+
+#define BMA2X2_SLOPE_TH_IN_G(gthres, range) ((128 * gthres) / range)
+
+
+#define BMA2X2_GET_BITSLICE(regvar, bitname)\
+ ((regvar & bitname##__MSK) >> bitname##__POS)
+
+
+#define BMA2X2_SET_BITSLICE(regvar, bitname, val)\
+ ((regvar & ~bitname##__MSK) | ((val<<bitname##__POS)&bitname##__MSK))
+
+#define CHECK_CHIP_ID_TIME_MAX 5
+#define BMA255_CHIP_ID 0XFA
+#define BMA250E_CHIP_ID 0XF9
+#define BMA222E_CHIP_ID 0XF8
+#define BMA280_CHIP_ID 0XFB
+#define BMA355_CHIP_ID 0XEA
+
+#define BMA255_TYPE 0
+#define BMA250E_TYPE 1
+#define BMA222E_TYPE 2
+#define BMA280_TYPE 3
+
+#define MAX_FIFO_F_LEVEL 32
+#define MAX_FIFO_F_BYTES 6
+#define BMA_MAX_RETRY_I2C_XFER (100)
+
+#ifdef CONFIG_DOUBLE_TAP
+#define DEFAULT_TAP_JUDGE_PERIOD 1000 /* default judge in 1 second */
+#endif
+
+/*! Bosch sensor unknown place*/
+#define BOSCH_SENSOR_PLACE_UNKNOWN (-1)
+
+#define BOSCH_SENSOR_PLACE (4)
+
+/*! Bosch sensor remapping table size P0~P7*/
+#define MAX_AXIS_REMAP_TAB_SZ 8
+
+/* How was BMA enabled(set to operation mode) */
+#define BMA_ENABLED_ALL 0
+#define BMA_ENABLED_SGM 1
+#define BMA_ENABLED_DTAP 2
+#define BMA_ENABLED_INPUT 3
+#define BMA_ENABLED_BSX 4
+
+#define BOSCH_SENSOR_BAD_ADDR -1
+
+
+/*!
+ * @brief:BMI058 feature
+ * macro definition
+*/
+
+#define BMA2X2_FIFO_DAT_SEL_X 1
+#define BMA2X2_FIFO_DAT_SEL_Y 2
+#define BMA2X2_FIFO_DAT_SEL_Z 3
+
+#ifdef CONFIG_SENSORS_BMI058
+#define C_BMI058_One_U8X 1
+#define C_BMI058_Two_U8X 2
+#define BMI058_OFFSET_TRIGGER_X BMA2X2_OFFSET_TRIGGER_Y
+#define BMI058_OFFSET_TRIGGER_Y BMA2X2_OFFSET_TRIGGER_X
+
+/*! BMI058 X AXIS OFFSET REG definition*/
+#define BMI058_OFFSET_X_AXIS_REG BMA2X2_OFFSET_Y_AXIS_REG
+/*! BMI058 Y AXIS OFFSET REG definition*/
+#define BMI058_OFFSET_Y_AXIS_REG BMA2X2_OFFSET_X_AXIS_REG
+
+#define BMI058_FIFO_DAT_SEL_X BMA2X2_FIFO_DAT_SEL_Y
+#define BMI058_FIFO_DAT_SEL_Y BMA2X2_FIFO_DAT_SEL_X
+
+/*! BMA2x2 common slow no motion X interrupt type definition*/
+#define BMA2X2_SLOW_NO_MOT_X_INT 12
+/*! BMA2x2 common slow no motion Y interrupt type definition*/
+#define BMA2X2_SLOW_NO_MOT_Y_INT 13
+/*! BMA2x2 common High G X interrupt type definition*/
+#define BMA2X2_HIGHG_X_INT 1
+/*! BMA2x2 common High G Y interrupt type definition*/
+#define BMA2X2_HIGHG_Y_INT 2
+/*! BMA2x2 common slope X interrupt type definition*/
+#define BMA2X2_SLOPE_X_INT 5
+/*! BMA2x2 common slope Y interrupt type definition*/
+#define BMA2X2_SLOPE_Y_INT 6
+
+/*! this structure holds some interrupt types difference
+**between BMA2x2 and BMI058.
+*/
+struct interrupt_map_t {
+ int x;
+ int y;
+};
+/*!*Need to use BMA2x2 Common interrupt type definition to
+* instead of Some of BMI058 reversed Interrupt type
+* because of HW Register.
+* The reversed Interrupt types contain:
+* slow_no_mot_x_int && slow_not_mot_y_int
+* highg_x_int && highg_y_int
+* slope_x_int && slope_y_int
+**/
+static const struct interrupt_map_t int_map[] = {
+ {BMA2X2_SLOW_NO_MOT_X_INT, BMA2X2_SLOW_NO_MOT_Y_INT},
+ {BMA2X2_HIGHG_X_INT, BMA2X2_HIGHG_Y_INT},
+ {BMA2X2_SLOPE_X_INT, BMA2X2_SLOPE_Y_INT}
+};
+
+/*! high g or slope interrupt type definition for BMI058*/
+/*! High G interrupt of x, y, z axis happened */
+#define HIGH_G_INTERRUPT_X HIGH_G_INTERRUPT_Y_HAPPENED
+#define HIGH_G_INTERRUPT_Y HIGH_G_INTERRUPT_X_HAPPENED
+#define HIGH_G_INTERRUPT_Z HIGH_G_INTERRUPT_Z_HAPPENED
+/*! High G interrupt of x, y, z negative axis happened */
+#define HIGH_G_INTERRUPT_X_N HIGH_G_INTERRUPT_Y_NEGATIVE_HAPPENED
+#define HIGH_G_INTERRUPT_Y_N HIGH_G_INTERRUPT_X_NEGATIVE_HAPPENED
+#define HIGH_G_INTERRUPT_Z_N HIGH_G_INTERRUPT_Z_NEGATIVE_HAPPENED
+/*! Slope interrupt of x, y, z axis happened */
+#define SLOPE_INTERRUPT_X SLOPE_INTERRUPT_Y_HAPPENED
+#define SLOPE_INTERRUPT_Y SLOPE_INTERRUPT_X_HAPPENED
+#define SLOPE_INTERRUPT_Z SLOPE_INTERRUPT_Z_HAPPENED
+/*! Slope interrupt of x, y, z negative axis happened */
+#define SLOPE_INTERRUPT_X_N SLOPE_INTERRUPT_Y_NEGATIVE_HAPPENED
+#define SLOPE_INTERRUPT_Y_N SLOPE_INTERRUPT_X_NEGATIVE_HAPPENED
+#define SLOPE_INTERRUPT_Z_N SLOPE_INTERRUPT_Z_NEGATIVE_HAPPENED
+
+
+#else
+
+/*! high g or slope interrupt type definition*/
+/*! High G interrupt of x, y, z axis happened */
+#define HIGH_G_INTERRUPT_X HIGH_G_INTERRUPT_X_HAPPENED
+#define HIGH_G_INTERRUPT_Y HIGH_G_INTERRUPT_Y_HAPPENED
+#define HIGH_G_INTERRUPT_Z HIGH_G_INTERRUPT_Z_HAPPENED
+/*! High G interrupt of x, y, z negative axis happened */
+#define HIGH_G_INTERRUPT_X_N HIGH_G_INTERRUPT_X_NEGATIVE_HAPPENED
+#define HIGH_G_INTERRUPT_Y_N HIGH_G_INTERRUPT_Y_NEGATIVE_HAPPENED
+#define HIGH_G_INTERRUPT_Z_N HIGH_G_INTERRUPT_Z_NEGATIVE_HAPPENED
+/*! Slope interrupt of x, y, z axis happened */
+#define SLOPE_INTERRUPT_X SLOPE_INTERRUPT_X_HAPPENED
+#define SLOPE_INTERRUPT_Y SLOPE_INTERRUPT_Y_HAPPENED
+#define SLOPE_INTERRUPT_Z SLOPE_INTERRUPT_Z_HAPPENED
+/*! Slope interrupt of x, y, z negative axis happened */
+#define SLOPE_INTERRUPT_X_N SLOPE_INTERRUPT_X_NEGATIVE_HAPPENED
+#define SLOPE_INTERRUPT_Y_N SLOPE_INTERRUPT_Y_NEGATIVE_HAPPENED
+#define SLOPE_INTERRUPT_Z_N SLOPE_INTERRUPT_Z_NEGATIVE_HAPPENED
+
+
+#endif/*End of CONFIG_SENSORS_BMI058*/
+
+
+struct bma2x2_type_map_t {
+
+ /*! bma2x2 sensor chip id */
+ uint16_t chip_id;
+
+ /*! bma2x2 sensor type */
+ uint16_t sensor_type;
+
+ /*! bma2x2 sensor name */
+ const char *sensor_name;
+};
+
+static const struct bma2x2_type_map_t sensor_type_map[] = {
+
+ {BMA255_CHIP_ID, BMA255_TYPE, "BMA255/254"},
+ {BMA355_CHIP_ID, BMA255_TYPE, "BMA355"},
+ {BMA250E_CHIP_ID, BMA250E_TYPE, "BMA250E"},
+ {BMA222E_CHIP_ID, BMA222E_TYPE, "BMA222E"},
+ {BMA280_CHIP_ID, BMA280_TYPE, "BMA280"},
+
+};
+
+/*!
+* Bst sensor common definition,
+* please give parameters in BSP file.
+*/
+struct bosch_sensor_specific {
+ char *name;
+ /* 0 to 7 */
+ int place;
+ int irq;
+ int (*irq_gpio_cfg)(void);
+};
+
+
+/*!
+ * we use a typedef to hide the detail,
+ * because this type might be changed
+ */
+struct bosch_sensor_axis_remap {
+ /* src means which source will be mapped to target x, y, z axis */
+ /* if an target OS axis is remapped from (-)x,
+ * src is 0, sign_* is (-)1 */
+ /* if an target OS axis is remapped from (-)y,
+ * src is 1, sign_* is (-)1 */
+ /* if an target OS axis is remapped from (-)z,
+ * src is 2, sign_* is (-)1 */
+ int src_x:3;
+ int src_y:3;
+ int src_z:3;
+
+ int sign_x:2;
+ int sign_y:2;
+ int sign_z:2;
+};
+
+struct bosch_sensor_data {
+ union {
+ int16_t v[3];
+ struct {
+ int16_t x;
+ int16_t y;
+ int16_t z;
+ };
+ };
+};
+
+struct bma2x2acc {
+ s16 x;
+ s16 y;
+ s16 z;
+};
+
+struct bma2x2_data {
+ struct i2c_client *bma2x2_client;
+ atomic_t delay;
+ atomic_t enable;
+ atomic_t selftest_result;
+ unsigned int chip_id;
+ unsigned int fifo_count;
+ unsigned char fifo_datasel;
+ unsigned char mode;
+ signed char sensor_type;
+ struct input_dev *input;
+
+ struct bst_dev *bst_acc;
+
+ struct bma2x2acc value;
+ struct mutex value_mutex;
+ struct mutex enable_mutex;
+ struct mutex mode_mutex;
+// struct delayed_work work;
+ struct work_struct work;
+ struct hrtimer timer;
+ struct workqueue_struct *wq;
+ struct work_struct irq_work;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+ int IRQ;
+ struct bosch_sensor_specific *bst_pd;
+
+ int bma_mode_enabled;
+ struct input_dev *dev_interrupt;
+
+#ifdef CONFIG_SIG_MOTION
+ struct class *g_sensor_class;
+ struct device *g_sensor_dev;
+
+ /*struct bma250_platform_data *pdata;*/
+ atomic_t en_sig_motion;
+#endif
+
+#ifdef CONFIG_DOUBLE_TAP
+ struct class *g_sensor_class_doubletap;
+ struct device *g_sensor_dev_doubletap;
+ atomic_t en_double_tap;
+ unsigned char tap_times;
+ struct mutex tap_mutex;
+ struct timer_list tap_timer;
+ int tap_time_period;
+#endif
+};
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void bma2x2_early_suspend(struct early_suspend *h);
+static void bma2x2_late_resume(struct early_suspend *h);
+#endif
+
+static int bma2x2_set_mode(struct i2c_client *client,
+ u8 mode, u8 enabled_mode);
+static int bma2x2_get_mode(struct i2c_client *client, u8 *mode);
+static int bma2x2_get_fifo_mode(struct i2c_client *client, u8 *fifo_mode);
+static int bma2x2_set_fifo_mode(struct i2c_client *client, u8 fifo_mode);
+static int bma2x2_normal_to_suspend(struct bma2x2_data *bma2x2,
+ unsigned char data1, unsigned char data2);
+
+
+/*Remapping for BMA2X2*/
+
+static const struct bosch_sensor_axis_remap
+bst_axis_remap_tab_dft[MAX_AXIS_REMAP_TAB_SZ] = {
+ /* src_x src_y src_z sign_x sign_y sign_z */
+ { 0, 1, 2, 1, 1, 1 }, /* P0 */
+ { 1, 0, 2, 1, -1, 1 }, /* P1 */
+ { 0, 1, 2, -1, -1, 1 }, /* P2 */
+ { 1, 0, 2, -1, 1, 1 }, /* P3 */
+
+ { 0, 1, 2, -1, 1, -1 }, /* P4 */
+ { 1, 0, 2, -1, -1, -1 }, /* P5 */
+ { 0, 1, 2, 1, -1, -1 }, /* P6 */
+ { 1, 0, 2, 1, 1, -1 }, /* P7 */
+};
+
+static unsigned int read_file(char *filename)
+{
+ struct file *filp;
+ char bufs[100];
+ int ret;
+ unsigned int data_val=0;
+
+ // strcpy(bufs, "hello my world");
+
+ /* kernel memory access setting */
+ mm_segment_t old_fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ /* open a file */
+ filp = filp_open(filename, O_RDWR, 0664);
+ if (IS_ERR(filp)) {
+ printk(KERN_ERR"open error\n");
+ return -1;
+ }
+ else {
+ printk(KERN_ERR"open success\n");
+ }
+
+ /* write example */
+ /// printk("filp->f_pos = %d\n", (int)filp->f_pos);
+ // vfs_write(filp, bufs, strlen(bufs), &filp->f_pos);
+ // printk("filp->f_pos = %d\n", (int)filp->f_pos);
+
+ /* read example */
+ printk(KERN_DEBUG);
+
+ // int pos = (int)filp->f_pos;
+ printk(KERN_ERR"filp->f_pos=%d \n: ", (int)filp->f_pos);
+ memset(bufs,'\n',60);
+ ret = vfs_read(filp, bufs, 60, &filp->f_pos);
+ printk(KERN_ERR"char_cnt =%d \n: ", ret);
+
+ if (ret != 0) {
+ printk(KERN_ERR"filp->f_pos=%d\n", (int)filp->f_pos);
+ }
+ printk("\n");
+
+ ret= sscanf(bufs,"%10d\n",&data_val);
+ printk(KERN_ERR"data_val=%d\n", data_val);
+ filp_close(filp, NULL); /* filp_close(filp, current->files) ? */
+ /* restore kernel memory setting */
+
+ set_fs(old_fs);
+ return data_val;
+}
+
+static void bst_remap_sensor_data(struct bosch_sensor_data *data,
+ const struct bosch_sensor_axis_remap *remap)
+{
+ struct bosch_sensor_data tmp;
+
+ tmp.x = data->v[remap->src_x] * remap->sign_x;
+ tmp.y = data->v[remap->src_y] * remap->sign_y;
+ tmp.z = data->v[remap->src_z] * remap->sign_z;
+
+ memcpy(data, &tmp, sizeof(*data));
+}
+
+static void bst_remap_sensor_data_dft_tab(struct bosch_sensor_data *data,
+ int place)
+{
+ /* sensor with place 0 needs not to be remapped */
+ if ((place <= 0) || (place >= MAX_AXIS_REMAP_TAB_SZ))
+ return;
+
+ bst_remap_sensor_data(data, &bst_axis_remap_tab_dft[place]);
+}
+
+static void bma2x2_remap_sensor_data(struct bma2x2acc *val,
+ struct bma2x2_data *client_data)
+{
+ struct bosch_sensor_data bsd;
+ int place;
+
+ if ((NULL == client_data->bst_pd) || (BOSCH_SENSOR_PLACE_UNKNOWN
+ == client_data->bst_pd->place))
+ place = BOSCH_SENSOR_PLACE_UNKNOWN;
+ else
+ place = client_data->bst_pd->place;
+
+#ifdef CONFIG_SENSORS_BMI058
+/*x,y need to be invesed becase of HW Register for BMI058*/
+ bsd.y = val->x;
+ bsd.x = val->y;
+ bsd.z = val->z;
+#else
+ bsd.x = val->x;
+ bsd.y = val->y;
+ bsd.z = val->z;
+#endif
+
+ bst_remap_sensor_data_dft_tab(&bsd, place);
+
+ val->x = bsd.x;
+ val->y = bsd.y;
+ val->z = bsd.z;
+
+}
+
+
+static int bma2x2_smbus_read_byte(struct i2c_client *client,
+ unsigned char reg_addr, unsigned char *data)
+{
+ s32 dummy;
+ dummy = i2c_smbus_read_byte_data(client, reg_addr);
+ if (dummy < 0)
+ return -1;
+ *data = dummy & 0x000000ff;
+
+ return 0;
+}
+
+static int bma2x2_smbus_write_byte(struct i2c_client *client,
+ unsigned char reg_addr, unsigned char *data)
+{
+ s32 dummy;
+
+ dummy = i2c_smbus_write_byte_data(client, reg_addr, *data);
+ if (dummy < 0)
+ return -1;
+ udelay(2);
+ return 0;
+}
+
+static int bma2x2_smbus_read_byte_block(struct i2c_client *client,
+ unsigned char reg_addr, unsigned char *data, unsigned char len)
+{
+ s32 dummy;
+ dummy = i2c_smbus_read_i2c_block_data(client, reg_addr, len, data);
+ if (dummy < 0)
+ return -1;
+ return 0;
+}
+
+static int bma_i2c_burst_read(struct i2c_client *client, u8 reg_addr,
+ u8 *data, u16 len)
+{
+ int retry;
+
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &reg_addr,
+ },
+
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = len,
+ .buf = data,
+ },
+ };
+
+ for (retry = 0; retry < BMA_MAX_RETRY_I2C_XFER; retry++) {
+ if (i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)) > 0)
+ break;
+ else
+ mdelay(1);
+ }
+
+ if (BMA_MAX_RETRY_I2C_XFER <= retry) {
+ PINFO("I2C xfer error");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int bma2x2_check_chip_id(struct i2c_client *client,
+ struct bma2x2_data *data)
+{
+ int i = 0;
+ int err = 0;
+ unsigned char chip_id = 0;
+ unsigned char read_count = 0;
+ unsigned char bma2x2_sensor_type_count = 0;
+
+ bma2x2_sensor_type_count =
+ sizeof(sensor_type_map) / sizeof(struct bma2x2_type_map_t);
+
+ while (read_count++ < CHECK_CHIP_ID_TIME_MAX) {
+ if (bma2x2_smbus_read_byte(client, BMA2X2_CHIP_ID_REG,
+ &chip_id) < 0) {
+ PERR("Bosch Sensortec Device not found"
+ "i2c bus read error, read chip_id:%d\n", chip_id);
+ continue;
+ } else {
+ for (i = 0; i < bma2x2_sensor_type_count; i++) {
+ if (sensor_type_map[i].chip_id == chip_id) {
+ data->sensor_type =
+ sensor_type_map[i].sensor_type;
+ data->chip_id = chip_id;
+ PINFO("Bosch Sensortec Device detected,"
+ " HW IC name: %s\n",
+ sensor_type_map[i].sensor_name);
+ return err;
+ }
+ }
+ if (i < bma2x2_sensor_type_count)
+ return err;
+ else {
+ if (read_count == CHECK_CHIP_ID_TIME_MAX) {
+ PERR("Failed! Bosch Sensortec Device"
+ " not found, mismatch chip_id:%d\n",
+ chip_id);
+ err = -ENODEV;
+ return err;
+ }
+ }
+ mdelay(1);
+ }
+ }
+ return err;
+}
+
+#ifdef CONFIG_BMA_ENABLE_NEWDATA_INT
+static int bma2x2_set_newdata(struct i2c_client *client,
+ unsigned char channel, unsigned char int_newdata)
+{
+
+ unsigned char data = 0;
+ int comres = 0;
+
+ switch (channel) {
+ case BMA2X2_INT1_NDATA:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT1_PAD_NEWDATA__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data,
+ BMA2X2_EN_INT1_PAD_NEWDATA, int_newdata);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT1_PAD_NEWDATA__REG, &data);
+ break;
+ case BMA2X2_INT2_NDATA:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT2_PAD_NEWDATA__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data,
+ BMA2X2_EN_INT2_PAD_NEWDATA, int_newdata);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT2_PAD_NEWDATA__REG, &data);
+ break;
+ default:
+ comres = -1;
+ break;
+ }
+
+ return comres;
+
+}
+#endif /* CONFIG_BMA_ENABLE_NEWDATA_INT */
+
+#ifdef BMA2X2_ENABLE_INT1
+static int bma2x2_set_int1_pad_sel(struct i2c_client *client, unsigned char
+ int1sel)
+{
+ int comres = 0;
+ unsigned char data = 0;
+ unsigned char state;
+ state = 0x01;
+
+ switch (int1sel) {
+ case 0:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT1_PAD_LOWG__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_LOWG,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT1_PAD_LOWG__REG, &data);
+ break;
+ case 1:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT1_PAD_HIGHG__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_HIGHG,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT1_PAD_HIGHG__REG, &data);
+ break;
+ case 2:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT1_PAD_SLOPE__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_SLOPE,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT1_PAD_SLOPE__REG, &data);
+ break;
+ case 3:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT1_PAD_DB_TAP__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_DB_TAP,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT1_PAD_DB_TAP__REG, &data);
+ break;
+ case 4:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT1_PAD_SNG_TAP__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_SNG_TAP,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT1_PAD_SNG_TAP__REG, &data);
+ break;
+ case 5:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT1_PAD_ORIENT__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_ORIENT,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT1_PAD_ORIENT__REG, &data);
+ break;
+ case 6:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT1_PAD_FLAT__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_FLAT,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT1_PAD_FLAT__REG, &data);
+ break;
+ case 7:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT1_PAD_SLO_NO_MOT__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT1_PAD_SLO_NO_MOT,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT1_PAD_SLO_NO_MOT__REG, &data);
+ break;
+
+ default:
+ break;
+ }
+
+ return comres;
+}
+#endif /* BMA2X2_ENABLE_INT1 */
+
+#ifdef BMA2X2_ENABLE_INT2
+static int bma2x2_set_int2_pad_sel(struct i2c_client *client, unsigned char
+ int2sel)
+{
+ int comres = 0;
+ unsigned char data = 0;
+ unsigned char state;
+ state = 0x01;
+
+
+ switch (int2sel) {
+ case 0:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT2_PAD_LOWG__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_LOWG,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT2_PAD_LOWG__REG, &data);
+ break;
+ case 1:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT2_PAD_HIGHG__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_HIGHG,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT2_PAD_HIGHG__REG, &data);
+ break;
+ case 2:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT2_PAD_SLOPE__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_SLOPE,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT2_PAD_SLOPE__REG, &data);
+ break;
+ case 3:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT2_PAD_DB_TAP__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_DB_TAP,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT2_PAD_DB_TAP__REG, &data);
+ break;
+ case 4:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT2_PAD_SNG_TAP__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_SNG_TAP,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT2_PAD_SNG_TAP__REG, &data);
+ break;
+ case 5:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT2_PAD_ORIENT__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_ORIENT,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT2_PAD_ORIENT__REG, &data);
+ break;
+ case 6:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT2_PAD_FLAT__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_FLAT,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT2_PAD_FLAT__REG, &data);
+ break;
+ case 7:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_EN_INT2_PAD_SLO_NO_MOT__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_INT2_PAD_SLO_NO_MOT,
+ state);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_EN_INT2_PAD_SLO_NO_MOT__REG, &data);
+ break;
+ default:
+ break;
+ }
+
+ return comres;
+}
+#endif /* BMA2X2_ENABLE_INT2 */
+
+static int bma2x2_set_Int_Enable(struct i2c_client *client, unsigned char
+ InterruptType , unsigned char value)
+{
+ int comres = 0;
+ unsigned char data1 = 0;
+ unsigned char data2 = 0;
+
+ if ((11 < InterruptType) && (InterruptType < 16)) {
+ switch (InterruptType) {
+ case 12:
+ /* slow/no motion X Interrupt */
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_INT_SLO_NO_MOT_EN_X_INT__REG, &data1);
+ data1 = BMA2X2_SET_BITSLICE(data1,
+ BMA2X2_INT_SLO_NO_MOT_EN_X_INT, value);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_INT_SLO_NO_MOT_EN_X_INT__REG, &data1);
+ break;
+ case 13:
+ /* slow/no motion Y Interrupt */
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_INT_SLO_NO_MOT_EN_Y_INT__REG, &data1);
+ data1 = BMA2X2_SET_BITSLICE(data1,
+ BMA2X2_INT_SLO_NO_MOT_EN_Y_INT, value);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_INT_SLO_NO_MOT_EN_Y_INT__REG, &data1);
+ break;
+ case 14:
+ /* slow/no motion Z Interrupt */
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_INT_SLO_NO_MOT_EN_Z_INT__REG, &data1);
+ data1 = BMA2X2_SET_BITSLICE(data1,
+ BMA2X2_INT_SLO_NO_MOT_EN_Z_INT, value);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_INT_SLO_NO_MOT_EN_Z_INT__REG, &data1);
+ break;
+ case 15:
+ /* slow / no motion Interrupt select */
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_INT_SLO_NO_MOT_EN_SEL_INT__REG, &data1);
+ data1 = BMA2X2_SET_BITSLICE(data1,
+ BMA2X2_INT_SLO_NO_MOT_EN_SEL_INT, value);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_INT_SLO_NO_MOT_EN_SEL_INT__REG, &data1);
+ }
+
+ return comres;
+ }
+
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_INT_ENABLE1_REG, &data1);
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_INT_ENABLE2_REG, &data2);
+
+ value = value & 1;
+ switch (InterruptType) {
+ case 0:
+ /* Low G Interrupt */
+ data2 = BMA2X2_SET_BITSLICE(data2, BMA2X2_EN_LOWG_INT, value);
+ break;
+
+ case 1:
+ /* High G X Interrupt */
+ data2 = BMA2X2_SET_BITSLICE(data2, BMA2X2_EN_HIGHG_X_INT,
+ value);
+ break;
+
+ case 2:
+ /* High G Y Interrupt */
+ data2 = BMA2X2_SET_BITSLICE(data2, BMA2X2_EN_HIGHG_Y_INT,
+ value);
+ break;
+
+ case 3:
+ /* High G Z Interrupt */
+ data2 = BMA2X2_SET_BITSLICE(data2, BMA2X2_EN_HIGHG_Z_INT,
+ value);
+ break;
+
+ case 4:
+ /* New Data Interrupt */
+ data2 = BMA2X2_SET_BITSLICE(data2, BMA2X2_EN_NEW_DATA_INT,
+ value);
+ break;
+
+ case 5:
+ /* Slope X Interrupt */
+ data1 = BMA2X2_SET_BITSLICE(data1, BMA2X2_EN_SLOPE_X_INT,
+ value);
+ break;
+
+ case 6:
+ /* Slope Y Interrupt */
+ data1 = BMA2X2_SET_BITSLICE(data1, BMA2X2_EN_SLOPE_Y_INT,
+ value);
+ break;
+
+ case 7:
+ /* Slope Z Interrupt */
+ data1 = BMA2X2_SET_BITSLICE(data1, BMA2X2_EN_SLOPE_Z_INT,
+ value);
+ break;
+
+ case 8:
+ /* Single Tap Interrupt */
+ data1 = BMA2X2_SET_BITSLICE(data1, BMA2X2_EN_SINGLE_TAP_INT,
+ value);
+ break;
+
+ case 9:
+ /* Double Tap Interrupt */
+ data1 = BMA2X2_SET_BITSLICE(data1, BMA2X2_EN_DOUBLE_TAP_INT,
+ value);
+ break;
+
+ case 10:
+ /* Orient Interrupt */
+ data1 = BMA2X2_SET_BITSLICE(data1, BMA2X2_EN_ORIENT_INT, value);
+ break;
+
+ case 11:
+ /* Flat Interrupt */
+ data1 = BMA2X2_SET_BITSLICE(data1, BMA2X2_EN_FLAT_INT, value);
+ break;
+
+ default:
+ break;
+ }
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_INT_ENABLE1_REG,
+ &data1);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_INT_ENABLE2_REG,
+ &data2);
+
+ return comres;
+}
+
+
+#if defined(BMA2X2_ENABLE_INT1) || defined(BMA2X2_ENABLE_INT2)
+static int bma2x2_get_interruptstatus1(struct i2c_client *client, unsigned char
+ *intstatus)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS1_REG, &data);
+ *intstatus = data;
+
+ return comres;
+}
+
+#ifdef CONFIG_BMA_ENABLE_NEWDATA_INT
+static int bma2x2_get_interruptstatus2(struct i2c_client *client, unsigned char
+ *intstatus)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS2_REG, &data);
+ *intstatus = data;
+
+ return comres;
+}
+#endif
+
+static int bma2x2_get_HIGH_first(struct i2c_client *client, unsigned char
+ param, unsigned char *intstatus)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ switch (param) {
+ case 0:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_STATUS_ORIENT_HIGH_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_HIGHG_FIRST_X);
+ *intstatus = data;
+ break;
+ case 1:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_STATUS_ORIENT_HIGH_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_HIGHG_FIRST_Y);
+ *intstatus = data;
+ break;
+ case 2:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_STATUS_ORIENT_HIGH_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_HIGHG_FIRST_Z);
+ *intstatus = data;
+ break;
+ default:
+ break;
+ }
+
+ return comres;
+}
+
+static int bma2x2_get_HIGH_sign(struct i2c_client *client, unsigned char
+ *intstatus)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS_ORIENT_HIGH_REG,
+ &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_HIGHG_SIGN_S);
+ *intstatus = data;
+
+ return comres;
+}
+
+#ifndef CONFIG_SIG_MOTION
+static int bma2x2_get_slope_first(struct i2c_client *client, unsigned char
+ param, unsigned char *intstatus)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ switch (param) {
+ case 0:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_STATUS_TAP_SLOPE_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_SLOPE_FIRST_X);
+ *intstatus = data;
+ break;
+ case 1:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_STATUS_TAP_SLOPE_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_SLOPE_FIRST_Y);
+ *intstatus = data;
+ break;
+ case 2:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_STATUS_TAP_SLOPE_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_SLOPE_FIRST_Z);
+ *intstatus = data;
+ break;
+ default:
+ break;
+ }
+
+ return comres;
+}
+
+static int bma2x2_get_slope_sign(struct i2c_client *client, unsigned char
+ *intstatus)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS_TAP_SLOPE_REG,
+ &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_SLOPE_SIGN_S);
+ *intstatus = data;
+
+ return comres;
+}
+#endif
+
+static int bma2x2_get_orient_status(struct i2c_client *client, unsigned char
+ *intstatus)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS_ORIENT_HIGH_REG,
+ &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_ORIENT_S);
+ *intstatus = data;
+
+ return comres;
+}
+
+static int bma2x2_get_orient_flat_status(struct i2c_client *client, unsigned
+ char *intstatus)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_STATUS_ORIENT_HIGH_REG,
+ &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_FLAT_S);
+ *intstatus = data;
+
+ return comres;
+}
+#endif /* defined(BMA2X2_ENABLE_INT1)||defined(BMA2X2_ENABLE_INT2) */
+
+static int bma2x2_set_Int_Mode(struct i2c_client *client, unsigned char Mode)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_INT_MODE_SEL__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_INT_MODE_SEL, Mode);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_INT_MODE_SEL__REG, &data);
+
+
+ return comres;
+}
+
+static int bma2x2_get_Int_Mode(struct i2c_client *client, unsigned char *Mode)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_INT_MODE_SEL__REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_INT_MODE_SEL);
+ *Mode = data;
+
+
+ return comres;
+}
+static int bma2x2_set_slope_duration(struct i2c_client *client, unsigned char
+ duration)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_SLOPE_DUR__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_SLOPE_DUR, duration);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_SLOPE_DUR__REG, &data);
+
+ return comres;
+}
+
+static int bma2x2_get_slope_duration(struct i2c_client *client, unsigned char
+ *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_SLOPE_DURN_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_SLOPE_DUR);
+ *status = data;
+
+
+ return comres;
+}
+
+static int bma2x2_set_slope_no_mot_duration(struct i2c_client *client,
+ unsigned char duration)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2x2_SLO_NO_MOT_DUR__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2x2_SLO_NO_MOT_DUR, duration);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2x2_SLO_NO_MOT_DUR__REG, &data);
+
+
+ return comres;
+}
+
+static int bma2x2_get_slope_no_mot_duration(struct i2c_client *client,
+ unsigned char *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2x2_SLO_NO_MOT_DUR__REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2x2_SLO_NO_MOT_DUR);
+ *status = data;
+
+
+ return comres;
+}
+
+static int bma2x2_set_slope_threshold(struct i2c_client *client,
+ unsigned char threshold)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ data = threshold;
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_SLOPE_THRES__REG, &data);
+
+ return comres;
+}
+
+static int bma2x2_get_slope_threshold(struct i2c_client *client,
+ unsigned char *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_SLOPE_THRES_REG, &data);
+ *status = data;
+
+ return comres;
+}
+
+static int bma2x2_set_slope_no_mot_threshold(struct i2c_client *client,
+ unsigned char threshold)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ data = threshold;
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_SLO_NO_MOT_THRES_REG, &data);
+
+ return comres;
+}
+
+static int bma2x2_get_slope_no_mot_threshold(struct i2c_client *client,
+ unsigned char *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_SLO_NO_MOT_THRES_REG, &data);
+ *status = data;
+
+ return comres;
+}
+
+
+static int bma2x2_set_low_g_duration(struct i2c_client *client, unsigned char
+ duration)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_LOWG_DUR__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_LOWG_DUR, duration);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_LOWG_DUR__REG, &data);
+
+ return comres;
+}
+
+static int bma2x2_get_low_g_duration(struct i2c_client *client, unsigned char
+ *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_LOW_DURN_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_LOWG_DUR);
+ *status = data;
+
+ return comres;
+}
+
+static int bma2x2_set_low_g_threshold(struct i2c_client *client, unsigned char
+ threshold)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_LOWG_THRES__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_LOWG_THRES, threshold);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_LOWG_THRES__REG, &data);
+
+ return comres;
+}
+
+static int bma2x2_get_low_g_threshold(struct i2c_client *client, unsigned char
+ *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_LOW_THRES_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_LOWG_THRES);
+ *status = data;
+
+ return comres;
+}
+
+static int bma2x2_set_high_g_duration(struct i2c_client *client, unsigned char
+ duration)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_HIGHG_DUR__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_HIGHG_DUR, duration);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_HIGHG_DUR__REG, &data);
+
+ return comres;
+}
+
+static int bma2x2_get_high_g_duration(struct i2c_client *client, unsigned char
+ *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_HIGH_DURN_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_HIGHG_DUR);
+ *status = data;
+
+ return comres;
+}
+
+static int bma2x2_set_high_g_threshold(struct i2c_client *client, unsigned char
+ threshold)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_HIGHG_THRES__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_HIGHG_THRES, threshold);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_HIGHG_THRES__REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_get_high_g_threshold(struct i2c_client *client, unsigned char
+ *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_HIGH_THRES_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_HIGHG_THRES);
+ *status = data;
+
+ return comres;
+}
+
+
+static int bma2x2_set_tap_duration(struct i2c_client *client, unsigned char
+ duration)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_DUR__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_TAP_DUR, duration);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_TAP_DUR__REG, &data);
+
+ return comres;
+}
+
+static int bma2x2_get_tap_duration(struct i2c_client *client, unsigned char
+ *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_PARAM_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_DUR);
+ *status = data;
+
+ return comres;
+}
+
+static int bma2x2_set_tap_shock(struct i2c_client *client, unsigned char setval)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_SHOCK_DURN__REG,
+ &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_TAP_SHOCK_DURN, setval);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_TAP_SHOCK_DURN__REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_get_tap_shock(struct i2c_client *client, unsigned char
+ *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_PARAM_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_SHOCK_DURN);
+ *status = data;
+
+ return comres;
+}
+
+static int bma2x2_set_tap_quiet(struct i2c_client *client, unsigned char
+ duration)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_QUIET_DURN__REG,
+ &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_TAP_QUIET_DURN, duration);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_TAP_QUIET_DURN__REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_get_tap_quiet(struct i2c_client *client, unsigned char
+ *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_PARAM_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_QUIET_DURN);
+ *status = data;
+
+ return comres;
+}
+
+static int bma2x2_set_tap_threshold(struct i2c_client *client, unsigned char
+ threshold)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_THRES__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_TAP_THRES, threshold);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_TAP_THRES__REG, &data);
+
+ return comres;
+}
+
+static int bma2x2_get_tap_threshold(struct i2c_client *client, unsigned char
+ *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_THRES_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_THRES);
+ *status = data;
+
+ return comres;
+}
+
+static int bma2x2_set_tap_samp(struct i2c_client *client, unsigned char samp)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_SAMPLES__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_TAP_SAMPLES, samp);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_TAP_SAMPLES__REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_get_tap_samp(struct i2c_client *client, unsigned char *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_TAP_THRES_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_TAP_SAMPLES);
+ *status = data;
+
+ return comres;
+}
+
+static int bma2x2_set_orient_mode(struct i2c_client *client, unsigned char mode)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_MODE__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_ORIENT_MODE, mode);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_ORIENT_MODE__REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_get_orient_mode(struct i2c_client *client, unsigned char
+ *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_PARAM_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_ORIENT_MODE);
+ *status = data;
+
+ return comres;
+}
+
+static int bma2x2_set_orient_blocking(struct i2c_client *client, unsigned char
+ samp)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_BLOCK__REG,
+ &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_ORIENT_BLOCK, samp);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_ORIENT_BLOCK__REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_get_orient_blocking(struct i2c_client *client, unsigned char
+ *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_PARAM_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_ORIENT_BLOCK);
+ *status = data;
+
+ return comres;
+}
+
+static int bma2x2_set_orient_hyst(struct i2c_client *client, unsigned char
+ orienthyst)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_HYST__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_ORIENT_HYST, orienthyst);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_ORIENT_HYST__REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_get_orient_hyst(struct i2c_client *client, unsigned char
+ *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_ORIENT_PARAM_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_ORIENT_HYST);
+ *status = data;
+
+ return comres;
+}
+static int bma2x2_set_theta_blocking(struct i2c_client *client, unsigned char
+ thetablk)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_THETA_BLOCK__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_THETA_BLOCK, thetablk);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_THETA_BLOCK__REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_get_theta_blocking(struct i2c_client *client, unsigned char
+ *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_THETA_BLOCK_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_THETA_BLOCK);
+ *status = data;
+
+ return comres;
+}
+
+static int bma2x2_set_theta_flat(struct i2c_client *client, unsigned char
+ thetaflat)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_THETA_FLAT__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_THETA_FLAT, thetaflat);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_THETA_FLAT__REG, &data);
+
+ return comres;
+}
+
+static int bma2x2_get_theta_flat(struct i2c_client *client, unsigned char
+ *status)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_THETA_FLAT_REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_THETA_FLAT);
+ *status = data;
+
+ return comres;
+}
+
+static int bma2x2_set_flat_hold_time(struct i2c_client *client, unsigned char
+ holdtime)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_FLAT_HOLD_TIME__REG,
+ &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_FLAT_HOLD_TIME, holdtime);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_FLAT_HOLD_TIME__REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_get_flat_hold_time(struct i2c_client *client, unsigned char
+ *holdtime)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_FLAT_HOLD_TIME_REG,
+ &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_FLAT_HOLD_TIME);
+ *holdtime = data;
+
+ return comres;
+}
+
+/*!
+ * brief: bma2x2 switch from normal to suspend mode
+ * @param[i] bma2x2
+ * @param[i] data1, write to PMU_LPW
+ * @param[i] data2, write to PMU_LOW_NOSIE
+ *
+ * @return zero success, none-zero failed
+ */
+static int bma2x2_normal_to_suspend(struct bma2x2_data *bma2x2,
+ unsigned char data1, unsigned char data2)
+{
+ unsigned char current_fifo_mode;
+ unsigned char current_op_mode;
+ if (bma2x2 == NULL)
+ return -1;
+ /* get current op mode from mode register */
+ if (bma2x2_get_mode(bma2x2->bma2x2_client, &current_op_mode) < 0)
+ return -1;
+ /* only aimed at operatiom mode chang from normal/lpw1 mode
+ * to suspend state.
+ */
+ if (current_op_mode == BMA2X2_MODE_NORMAL ||
+ current_op_mode == BMA2X2_MODE_LOWPOWER1) {
+ /* get current fifo mode from fifo config register */
+ if (bma2x2_get_fifo_mode(bma2x2->bma2x2_client,
+ &current_fifo_mode) < 0)
+ return -1;
+ else {
+ bma2x2_smbus_write_byte(bma2x2->bma2x2_client,
+ BMA2X2_LOW_NOISE_CTRL_REG, &data2);
+ bma2x2_smbus_write_byte(bma2x2->bma2x2_client,
+ BMA2X2_MODE_CTRL_REG, &data1);
+ bma2x2_smbus_write_byte(bma2x2->bma2x2_client,
+ BMA2X2_FIFO_MODE__REG, &current_fifo_mode);
+ mdelay(3);
+ return 0;
+ }
+ } else {
+ bma2x2_smbus_write_byte(bma2x2->bma2x2_client,
+ BMA2X2_LOW_NOISE_CTRL_REG, &data2);
+ bma2x2_smbus_write_byte(bma2x2->bma2x2_client,
+ BMA2X2_MODE_CTRL_REG, &data1);
+ mdelay(3);
+ return 0;
+ }
+
+}
+
+static int bma2x2_set_mode(struct i2c_client *client, unsigned char mode,
+ unsigned char enabled_mode)
+{
+ int comres = 0;
+ unsigned char data1 = 0;
+ unsigned char data2 = 0;
+ int ret = 0;
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ mutex_lock(&bma2x2->mode_mutex);
+ if (BMA2X2_MODE_SUSPEND == mode) {
+ if (enabled_mode != BMA_ENABLED_ALL) {
+ if ((bma2x2->bma_mode_enabled &
+ (1<<enabled_mode)) == 0) {
+ /* sensor is already closed in this mode */
+ mutex_unlock(&bma2x2->mode_mutex);
+ return 0;
+ } else {
+ bma2x2->bma_mode_enabled &= ~(1<<enabled_mode);
+ }
+ } else {
+ /* shut down, close all and force do it*/
+ bma2x2->bma_mode_enabled = 0;
+ }
+ } else if (BMA2X2_MODE_NORMAL == mode) {
+ if ((bma2x2->bma_mode_enabled & (1<<enabled_mode)) != 0) {
+ /* sensor is already enabled in this mode */
+ mutex_unlock(&bma2x2->mode_mutex);
+ return 0;
+ } else {
+ bma2x2->bma_mode_enabled |= (1<<enabled_mode);
+ }
+ } else {
+ /* other mode, close all and force do it*/
+ bma2x2->bma_mode_enabled = 0;
+ }
+ mutex_unlock(&bma2x2->mode_mutex);
+
+ if (mode < 6) {
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_MODE_CTRL_REG,
+ &data1);
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_LOW_NOISE_CTRL_REG,
+ &data2);
+ switch (mode) {
+ case BMA2X2_MODE_NORMAL:
+ data1 = BMA2X2_SET_BITSLICE(data1,
+ BMA2X2_MODE_CTRL, 0);
+ data2 = BMA2X2_SET_BITSLICE(data2,
+ BMA2X2_LOW_POWER_MODE, 0);
+ bma2x2_smbus_write_byte(client,
+ BMA2X2_MODE_CTRL_REG, &data1);
+ mdelay(3);
+ bma2x2_smbus_write_byte(client,
+ BMA2X2_LOW_NOISE_CTRL_REG, &data2);
+ break;
+ case BMA2X2_MODE_LOWPOWER1:
+ data1 = BMA2X2_SET_BITSLICE(data1,
+ BMA2X2_MODE_CTRL, 2);
+ data2 = BMA2X2_SET_BITSLICE(data2,
+ BMA2X2_LOW_POWER_MODE, 0);
+ bma2x2_smbus_write_byte(client,
+ BMA2X2_MODE_CTRL_REG, &data1);
+ mdelay(3);
+ bma2x2_smbus_write_byte(client,
+ BMA2X2_LOW_NOISE_CTRL_REG, &data2);
+ break;
+ case BMA2X2_MODE_SUSPEND:
+ if (bma2x2->bma_mode_enabled != 0) {
+ PERR("bma still working");
+ return 0;
+ }
+ data1 = BMA2X2_SET_BITSLICE(data1,
+ BMA2X2_MODE_CTRL, 4);
+ data2 = BMA2X2_SET_BITSLICE(data2,
+ BMA2X2_LOW_POWER_MODE, 0);
+ /*aimed at anomaly resolution when switch to suspend*/
+ ret = bma2x2_normal_to_suspend(bma2x2, data1, data2);
+ if (ret < 0)
+ PERR("Error switching to suspend");
+ break;
+ case BMA2X2_MODE_DEEP_SUSPEND:
+ if (bma2x2->bma_mode_enabled != 0) {
+ PERR("bma still working");
+ return 0;
+ }
+ data1 = BMA2X2_SET_BITSLICE(data1,
+ BMA2X2_MODE_CTRL, 1);
+ data2 = BMA2X2_SET_BITSLICE(data2,
+ BMA2X2_LOW_POWER_MODE, 1);
+ bma2x2_smbus_write_byte(client,
+ BMA2X2_MODE_CTRL_REG, &data1);
+ mdelay(3);
+ bma2x2_smbus_write_byte(client,
+ BMA2X2_LOW_NOISE_CTRL_REG, &data2);
+ break;
+ case BMA2X2_MODE_LOWPOWER2:
+ data1 = BMA2X2_SET_BITSLICE(data1,
+ BMA2X2_MODE_CTRL, 2);
+ data2 = BMA2X2_SET_BITSLICE(data2,
+ BMA2X2_LOW_POWER_MODE, 1);
+ bma2x2_smbus_write_byte(client,
+ BMA2X2_MODE_CTRL_REG, &data1);
+ mdelay(3);
+ bma2x2_smbus_write_byte(client,
+ BMA2X2_LOW_NOISE_CTRL_REG, &data2);
+ break;
+ case BMA2X2_MODE_STANDBY:
+ data1 = BMA2X2_SET_BITSLICE(data1,
+ BMA2X2_MODE_CTRL, 4);
+ data2 = BMA2X2_SET_BITSLICE(data2,
+ BMA2X2_LOW_POWER_MODE, 1);
+ bma2x2_smbus_write_byte(client,
+ BMA2X2_LOW_NOISE_CTRL_REG, &data2);
+ mdelay(3);
+ bma2x2_smbus_write_byte(client,
+ BMA2X2_MODE_CTRL_REG, &data1);
+ break;
+ }
+ } else {
+ comres = -1;
+ }
+
+ return comres;
+}
+
+
+static int bma2x2_get_mode(struct i2c_client *client, unsigned char *mode)
+{
+ int comres = 0;
+ unsigned char data1 = 0;
+ unsigned char data2 = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_MODE_CTRL_REG, &data1);
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_LOW_NOISE_CTRL_REG,
+ &data2);
+
+ data1 = (data1 & 0xE0) >> 5;
+ data2 = (data2 & 0x40) >> 6;
+
+
+ if ((data1 == 0x00) && (data2 == 0x00)) {
+ *mode = BMA2X2_MODE_NORMAL;
+ } else {
+ if ((data1 == 0x02) && (data2 == 0x00)) {
+ *mode = BMA2X2_MODE_LOWPOWER1;
+ } else {
+ if ((data1 == 0x04 || data1 == 0x06) &&
+ (data2 == 0x00)) {
+ *mode = BMA2X2_MODE_SUSPEND;
+ } else {
+ if (((data1 & 0x01) == 0x01)) {
+ *mode = BMA2X2_MODE_DEEP_SUSPEND;
+ } else {
+ if ((data1 == 0x02) &&
+ (data2 == 0x01)) {
+ *mode = BMA2X2_MODE_LOWPOWER2;
+ } else {
+ if ((data1 == 0x04) && (data2 ==
+ 0x01)) {
+ *mode =
+ BMA2X2_MODE_STANDBY;
+ } else {
+ *mode =
+ BMA2X2_MODE_DEEP_SUSPEND;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return comres;
+}
+
+static int bma2x2_set_range(struct i2c_client *client, unsigned char Range)
+{
+ int comres = 0;
+ unsigned char data1 = 0;
+
+ if ((Range == 3) || (Range == 5) || (Range == 8) || (Range == 12)) {
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_RANGE_SEL_REG,
+ &data1);
+ switch (Range) {
+ case BMA2X2_RANGE_2G:
+ data1 = BMA2X2_SET_BITSLICE(data1,
+ BMA2X2_RANGE_SEL, 3);
+ break;
+ case BMA2X2_RANGE_4G:
+ data1 = BMA2X2_SET_BITSLICE(data1,
+ BMA2X2_RANGE_SEL, 5);
+ break;
+ case BMA2X2_RANGE_8G:
+ data1 = BMA2X2_SET_BITSLICE(data1,
+ BMA2X2_RANGE_SEL, 8);
+ break;
+ case BMA2X2_RANGE_16G:
+ data1 = BMA2X2_SET_BITSLICE(data1,
+ BMA2X2_RANGE_SEL, 12);
+ break;
+ default:
+ break;
+ }
+ comres += bma2x2_smbus_write_byte(client, BMA2X2_RANGE_SEL_REG,
+ &data1);
+ } else {
+ comres = -1;
+ }
+
+ return comres;
+}
+
+static int bma2x2_get_range(struct i2c_client *client, unsigned char *Range)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_RANGE_SEL__REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_RANGE_SEL);
+ *Range = data;
+
+ return comres;
+}
+
+
+static int bma2x2_set_bandwidth(struct i2c_client *client, unsigned char BW)
+{
+ int comres = 0;
+ unsigned char data = 0;
+ int Bandwidth = 0;
+
+ if (BW > 7 && BW < 16) {
+ switch (BW) {
+ case BMA2X2_BW_7_81HZ:
+ Bandwidth = BMA2X2_BW_7_81HZ;
+
+ /* 7.81 Hz 64000 uS */
+ break;
+ case BMA2X2_BW_15_63HZ:
+ Bandwidth = BMA2X2_BW_15_63HZ;
+
+ /* 15.63 Hz 32000 uS */
+ break;
+ case BMA2X2_BW_31_25HZ:
+ Bandwidth = BMA2X2_BW_31_25HZ;
+
+ /* 31.25 Hz 16000 uS */
+ break;
+ case BMA2X2_BW_62_50HZ:
+ Bandwidth = BMA2X2_BW_62_50HZ;
+
+ /* 62.50 Hz 8000 uS */
+ break;
+ case BMA2X2_BW_125HZ:
+ Bandwidth = BMA2X2_BW_125HZ;
+
+ /* 125 Hz 4000 uS */
+ break;
+ case BMA2X2_BW_250HZ:
+ Bandwidth = BMA2X2_BW_250HZ;
+
+ /* 250 Hz 2000 uS */
+ break;
+ case BMA2X2_BW_500HZ:
+ Bandwidth = BMA2X2_BW_500HZ;
+
+ /* 500 Hz 1000 uS */
+ break;
+ case BMA2X2_BW_1000HZ:
+ Bandwidth = BMA2X2_BW_1000HZ;
+
+ /* 1000 Hz 500 uS */
+ break;
+ default:
+ break;
+ }
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_BANDWIDTH__REG,
+ &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_BANDWIDTH, Bandwidth);
+ comres += bma2x2_smbus_write_byte(client, BMA2X2_BANDWIDTH__REG,
+ &data);
+ } else {
+ comres = -1;
+ }
+
+ return comres;
+}
+
+static int bma2x2_get_bandwidth(struct i2c_client *client, unsigned char *BW)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_BANDWIDTH__REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_BANDWIDTH);
+ *BW = data;
+
+ return comres;
+}
+
+int bma2x2_get_sleep_duration(struct i2c_client *client, unsigned char
+ *sleep_dur)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_SLEEP_DUR__REG, &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_SLEEP_DUR);
+ *sleep_dur = data;
+
+ return comres;
+}
+
+int bma2x2_set_sleep_duration(struct i2c_client *client, unsigned char
+ sleep_dur)
+{
+ int comres = 0;
+ unsigned char data = 0;
+ int sleep_duration = 0;
+
+ if (sleep_dur > 4 && sleep_dur < 16) {
+ switch (sleep_dur) {
+ case BMA2X2_SLEEP_DUR_0_5MS:
+ sleep_duration = BMA2X2_SLEEP_DUR_0_5MS;
+
+ /* 0.5 MS */
+ break;
+ case BMA2X2_SLEEP_DUR_1MS:
+ sleep_duration = BMA2X2_SLEEP_DUR_1MS;
+
+ /* 1 MS */
+ break;
+ case BMA2X2_SLEEP_DUR_2MS:
+ sleep_duration = BMA2X2_SLEEP_DUR_2MS;
+
+ /* 2 MS */
+ break;
+ case BMA2X2_SLEEP_DUR_4MS:
+ sleep_duration = BMA2X2_SLEEP_DUR_4MS;
+
+ /* 4 MS */
+ break;
+ case BMA2X2_SLEEP_DUR_6MS:
+ sleep_duration = BMA2X2_SLEEP_DUR_6MS;
+
+ /* 6 MS */
+ break;
+ case BMA2X2_SLEEP_DUR_10MS:
+ sleep_duration = BMA2X2_SLEEP_DUR_10MS;
+
+ /* 10 MS */
+ break;
+ case BMA2X2_SLEEP_DUR_25MS:
+ sleep_duration = BMA2X2_SLEEP_DUR_25MS;
+
+ /* 25 MS */
+ break;
+ case BMA2X2_SLEEP_DUR_50MS:
+ sleep_duration = BMA2X2_SLEEP_DUR_50MS;
+
+ /* 50 MS */
+ break;
+ case BMA2X2_SLEEP_DUR_100MS:
+ sleep_duration = BMA2X2_SLEEP_DUR_100MS;
+
+ /* 100 MS */
+ break;
+ case BMA2X2_SLEEP_DUR_500MS:
+ sleep_duration = BMA2X2_SLEEP_DUR_500MS;
+
+ /* 500 MS */
+ break;
+ case BMA2X2_SLEEP_DUR_1S:
+ sleep_duration = BMA2X2_SLEEP_DUR_1S;
+
+ /* 1 SECS */
+ break;
+ default:
+ break;
+ }
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_SLEEP_DUR__REG,
+ &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_SLEEP_DUR,
+ sleep_duration);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_SLEEP_DUR__REG,
+ &data);
+ } else {
+ comres = -1;
+ }
+
+
+ return comres;
+}
+
+static int bma2x2_get_fifo_mode(struct i2c_client *client, unsigned char
+ *fifo_mode)
+{
+ int comres;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_FIFO_MODE__REG, &data);
+ *fifo_mode = BMA2X2_GET_BITSLICE(data, BMA2X2_FIFO_MODE);
+
+ return comres;
+}
+
+static int bma2x2_set_fifo_mode(struct i2c_client *client, unsigned char
+ fifo_mode)
+{
+ unsigned char data = 0;
+ int comres = 0;
+
+ if (fifo_mode < 4) {
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_FIFO_MODE__REG,
+ &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_FIFO_MODE, fifo_mode);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_FIFO_MODE__REG,
+ &data);
+ } else {
+ comres = -1;
+ }
+
+ return comres;
+}
+
+static int bma2x2_get_fifo_trig(struct i2c_client *client, unsigned char
+ *fifo_trig)
+{
+ int comres;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_FIFO_TRIGGER_ACTION__REG, &data);
+ *fifo_trig = BMA2X2_GET_BITSLICE(data, BMA2X2_FIFO_TRIGGER_ACTION);
+
+ return comres;
+}
+
+static int bma2x2_set_fifo_trig(struct i2c_client *client, unsigned char
+ fifo_trig)
+{
+ unsigned char data = 0;
+ int comres = 0;
+
+ if (fifo_trig < 4) {
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_FIFO_TRIGGER_ACTION__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_FIFO_TRIGGER_ACTION,
+ fifo_trig);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_FIFO_TRIGGER_ACTION__REG, &data);
+ } else {
+ comres = -1;
+ }
+
+ return comres;
+}
+
+static int bma2x2_get_fifo_trig_src(struct i2c_client *client, unsigned char
+ *trig_src)
+{
+ int comres;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_FIFO_TRIGGER_SOURCE__REG, &data);
+ *trig_src = BMA2X2_GET_BITSLICE(data, BMA2X2_FIFO_TRIGGER_SOURCE);
+
+ return comres;
+}
+
+static int bma2x2_set_fifo_trig_src(struct i2c_client *client, unsigned char
+ trig_src)
+{
+ unsigned char data = 0;
+ int comres = 0;
+
+ if (trig_src < 4) {
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_FIFO_TRIGGER_SOURCE__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_FIFO_TRIGGER_SOURCE,
+ trig_src);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_FIFO_TRIGGER_SOURCE__REG, &data);
+ } else {
+ comres = -1;
+ }
+
+ return comres;
+}
+
+static int bma2x2_get_fifo_framecount(struct i2c_client *client, unsigned char
+ *framecount)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_FIFO_FRAME_COUNTER_S__REG, &data);
+ *framecount = BMA2X2_GET_BITSLICE(data, BMA2X2_FIFO_FRAME_COUNTER_S);
+
+ return comres;
+}
+
+static int bma2x2_get_fifo_data_sel(struct i2c_client *client, unsigned char
+ *data_sel)
+{
+ int comres;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_FIFO_DATA_SELECT__REG, &data);
+ *data_sel = BMA2X2_GET_BITSLICE(data, BMA2X2_FIFO_DATA_SELECT);
+
+ return comres;
+}
+
+static int bma2x2_set_fifo_data_sel(struct i2c_client *client, unsigned char
+ data_sel)
+{
+ unsigned char data = 0;
+ int comres = 0;
+
+ if (data_sel < 4) {
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_FIFO_DATA_SELECT__REG,
+ &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_FIFO_DATA_SELECT,
+ data_sel);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_FIFO_DATA_SELECT__REG,
+ &data);
+ } else {
+ comres = -1;
+ }
+
+ return comres;
+}
+
+
+static int bma2x2_get_offset_target(struct i2c_client *client, unsigned char
+ channel, unsigned char *offset)
+{
+ unsigned char data = 0;
+ int comres = 0;
+
+ switch (channel) {
+ case BMA2X2_CUT_OFF:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_COMP_CUTOFF__REG, &data);
+ *offset = BMA2X2_GET_BITSLICE(data, BMA2X2_COMP_CUTOFF);
+ break;
+ case BMA2X2_OFFSET_TRIGGER_X:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_COMP_TARGET_OFFSET_X__REG, &data);
+ *offset = BMA2X2_GET_BITSLICE(data,
+ BMA2X2_COMP_TARGET_OFFSET_X);
+ break;
+ case BMA2X2_OFFSET_TRIGGER_Y:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_COMP_TARGET_OFFSET_Y__REG, &data);
+ *offset = BMA2X2_GET_BITSLICE(data,
+ BMA2X2_COMP_TARGET_OFFSET_Y);
+ break;
+ case BMA2X2_OFFSET_TRIGGER_Z:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_COMP_TARGET_OFFSET_Z__REG, &data);
+ *offset = BMA2X2_GET_BITSLICE(data,
+ BMA2X2_COMP_TARGET_OFFSET_Z);
+ break;
+ default:
+ comres = -1;
+ break;
+ }
+
+ return comres;
+}
+
+static int bma2x2_set_offset_target(struct i2c_client *client, unsigned char
+ channel, unsigned char offset)
+{
+ unsigned char data = 0;
+ int comres = 0;
+
+ switch (channel) {
+ case BMA2X2_CUT_OFF:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_COMP_CUTOFF__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_COMP_CUTOFF,
+ offset);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_COMP_CUTOFF__REG, &data);
+ break;
+ case BMA2X2_OFFSET_TRIGGER_X:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_COMP_TARGET_OFFSET_X__REG,
+ &data);
+ data = BMA2X2_SET_BITSLICE(data,
+ BMA2X2_COMP_TARGET_OFFSET_X,
+ offset);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_COMP_TARGET_OFFSET_X__REG,
+ &data);
+ break;
+ case BMA2X2_OFFSET_TRIGGER_Y:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_COMP_TARGET_OFFSET_Y__REG,
+ &data);
+ data = BMA2X2_SET_BITSLICE(data,
+ BMA2X2_COMP_TARGET_OFFSET_Y,
+ offset);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_COMP_TARGET_OFFSET_Y__REG,
+ &data);
+ break;
+ case BMA2X2_OFFSET_TRIGGER_Z:
+ comres = bma2x2_smbus_read_byte(client,
+ BMA2X2_COMP_TARGET_OFFSET_Z__REG,
+ &data);
+ data = BMA2X2_SET_BITSLICE(data,
+ BMA2X2_COMP_TARGET_OFFSET_Z,
+ offset);
+ comres = bma2x2_smbus_write_byte(client,
+ BMA2X2_COMP_TARGET_OFFSET_Z__REG,
+ &data);
+ break;
+ default:
+ comres = -1;
+ break;
+ }
+
+ return comres;
+}
+
+static int bma2x2_get_cal_ready(struct i2c_client *client,
+ unsigned char *calrdy)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_FAST_CAL_RDY_S__REG,
+ &data);
+ data = BMA2X2_GET_BITSLICE(data, BMA2X2_FAST_CAL_RDY_S);
+ *calrdy = data;
+
+ return comres;
+}
+
+static int bma2x2_set_cal_trigger(struct i2c_client *client, unsigned char
+ caltrigger)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_CAL_TRIGGER__REG, &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_CAL_TRIGGER, caltrigger);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_CAL_TRIGGER__REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_write_reg(struct i2c_client *client, unsigned char addr,
+ unsigned char *data)
+{
+ int comres = 0;
+ comres = bma2x2_smbus_write_byte(client, addr, data);
+
+ return comres;
+}
+
+
+static int bma2x2_set_offset_x(struct i2c_client *client, unsigned char
+ offsetfilt)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ data = offsetfilt;
+
+#ifdef CONFIG_SENSORS_BMI058
+ comres = bma2x2_smbus_write_byte(client, BMI058_OFFSET_X_AXIS_REG,
+ &data);
+#else
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_OFFSET_X_AXIS_REG,
+ &data);
+#endif
+
+ return comres;
+}
+
+
+static int bma2x2_get_offset_x(struct i2c_client *client, unsigned char
+ *offsetfilt)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+#ifdef CONFIG_SENSORS_BMI058
+ comres = bma2x2_smbus_read_byte(client, BMI058_OFFSET_X_AXIS_REG,
+ &data);
+#else
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_OFFSET_X_AXIS_REG,
+ &data);
+#endif
+ *offsetfilt = data;
+
+ return comres;
+}
+
+static int bma2x2_set_offset_y(struct i2c_client *client, unsigned char
+ offsetfilt)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ data = offsetfilt;
+
+#ifdef CONFIG_SENSORS_BMI058
+ comres = bma2x2_smbus_write_byte(client, BMI058_OFFSET_Y_AXIS_REG,
+ &data);
+#else
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_OFFSET_Y_AXIS_REG,
+ &data);
+#endif
+ return comres;
+}
+
+static int bma2x2_get_offset_y(struct i2c_client *client, unsigned char
+ *offsetfilt)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+#ifdef CONFIG_SENSORS_BMI058
+ comres = bma2x2_smbus_read_byte(client, BMI058_OFFSET_Y_AXIS_REG,
+ &data);
+#else
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_OFFSET_Y_AXIS_REG,
+ &data);
+#endif
+ *offsetfilt = data;
+
+ return comres;
+}
+
+static int bma2x2_set_offset_z(struct i2c_client *client, unsigned char
+ offsetfilt)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ data = offsetfilt;
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_OFFSET_Z_AXIS_REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_get_offset_z(struct i2c_client *client, unsigned char
+ *offsetfilt)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_OFFSET_Z_AXIS_REG,
+ &data);
+ *offsetfilt = data;
+
+ return comres;
+}
+
+
+static int bma2x2_set_selftest_st(struct i2c_client *client, unsigned char
+ selftest)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_EN_SELF_TEST__REG,
+ &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_EN_SELF_TEST, selftest);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_EN_SELF_TEST__REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_set_selftest_stn(struct i2c_client *client, unsigned char stn)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_NEG_SELF_TEST__REG,
+ &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_NEG_SELF_TEST, stn);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_NEG_SELF_TEST__REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_set_selftest_amp(struct i2c_client *client, unsigned char amp)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_SELF_TEST_AMP__REG,
+ &data);
+ data = BMA2X2_SET_BITSLICE(data, BMA2X2_SELF_TEST_AMP, amp);
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_SELF_TEST_AMP__REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_read_accel_x(struct i2c_client *client,
+ signed char sensor_type, short *a_x)
+{
+ int comres = 0;
+ unsigned char data[2];
+
+ switch (sensor_type) {
+ case 0:
+ comres = bma2x2_smbus_read_byte_block(client,
+ BMA2X2_ACC_X12_LSB__REG, data, 2);
+ *a_x = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_X12_LSB)|
+ (BMA2X2_GET_BITSLICE(data[1],
+ BMA2X2_ACC_X_MSB)<<(BMA2X2_ACC_X12_LSB__LEN));
+ *a_x = *a_x << (sizeof(short)*8-(BMA2X2_ACC_X12_LSB__LEN
+ + BMA2X2_ACC_X_MSB__LEN));
+ *a_x = *a_x >> (sizeof(short)*8-(BMA2X2_ACC_X12_LSB__LEN
+ + BMA2X2_ACC_X_MSB__LEN));
+ break;
+ case 1:
+ comres = bma2x2_smbus_read_byte_block(client,
+ BMA2X2_ACC_X10_LSB__REG, data, 2);
+ *a_x = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_X10_LSB)|
+ (BMA2X2_GET_BITSLICE(data[1],
+ BMA2X2_ACC_X_MSB)<<(BMA2X2_ACC_X10_LSB__LEN));
+ *a_x = *a_x << (sizeof(short)*8-(BMA2X2_ACC_X10_LSB__LEN
+ + BMA2X2_ACC_X_MSB__LEN));
+ *a_x = *a_x >> (sizeof(short)*8-(BMA2X2_ACC_X10_LSB__LEN
+ + BMA2X2_ACC_X_MSB__LEN));
+ break;
+ case 2:
+ comres = bma2x2_smbus_read_byte_block(client,
+ BMA2X2_ACC_X8_LSB__REG, data, 2);
+ *a_x = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_X8_LSB)|
+ (BMA2X2_GET_BITSLICE(data[1],
+ BMA2X2_ACC_X_MSB)<<(BMA2X2_ACC_X8_LSB__LEN));
+ *a_x = *a_x << (sizeof(short)*8-(BMA2X2_ACC_X8_LSB__LEN
+ + BMA2X2_ACC_X_MSB__LEN));
+ *a_x = *a_x >> (sizeof(short)*8-(BMA2X2_ACC_X8_LSB__LEN
+ + BMA2X2_ACC_X_MSB__LEN));
+ break;
+ case 3:
+ comres = bma2x2_smbus_read_byte_block(client,
+ BMA2X2_ACC_X14_LSB__REG, data, 2);
+ *a_x = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_X14_LSB)|
+ (BMA2X2_GET_BITSLICE(data[1],
+ BMA2X2_ACC_X_MSB)<<(BMA2X2_ACC_X14_LSB__LEN));
+ *a_x = *a_x << (sizeof(short)*8-(BMA2X2_ACC_X14_LSB__LEN
+ + BMA2X2_ACC_X_MSB__LEN));
+ *a_x = *a_x >> (sizeof(short)*8-(BMA2X2_ACC_X14_LSB__LEN
+ + BMA2X2_ACC_X_MSB__LEN));
+ break;
+ default:
+ break;
+ }
+
+ return comres;
+}
+
+static int bma2x2_soft_reset(struct i2c_client *client)
+{
+ int comres = 0;
+ unsigned char data = BMA2X2_EN_SOFT_RESET_VALUE;
+
+ comres = bma2x2_smbus_write_byte(client, BMA2X2_EN_SOFT_RESET__REG,
+ &data);
+
+ return comres;
+}
+
+static int bma2x2_read_accel_y(struct i2c_client *client,
+ signed char sensor_type, short *a_y)
+{
+ int comres = 0;
+ unsigned char data[2];
+
+ switch (sensor_type) {
+ case 0:
+ comres = bma2x2_smbus_read_byte_block(client,
+ BMA2X2_ACC_Y12_LSB__REG, data, 2);
+ *a_y = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Y12_LSB)|
+ (BMA2X2_GET_BITSLICE(data[1],
+ BMA2X2_ACC_Y_MSB)<<(BMA2X2_ACC_Y12_LSB__LEN));
+ *a_y = *a_y << (sizeof(short)*8-(BMA2X2_ACC_Y12_LSB__LEN
+ + BMA2X2_ACC_Y_MSB__LEN));
+ *a_y = *a_y >> (sizeof(short)*8-(BMA2X2_ACC_Y12_LSB__LEN
+ + BMA2X2_ACC_Y_MSB__LEN));
+ break;
+ case 1:
+ comres = bma2x2_smbus_read_byte_block(client,
+ BMA2X2_ACC_Y10_LSB__REG, data, 2);
+ *a_y = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Y10_LSB)|
+ (BMA2X2_GET_BITSLICE(data[1],
+ BMA2X2_ACC_Y_MSB)<<(BMA2X2_ACC_Y10_LSB__LEN));
+ *a_y = *a_y << (sizeof(short)*8-(BMA2X2_ACC_Y10_LSB__LEN
+ + BMA2X2_ACC_Y_MSB__LEN));
+ *a_y = *a_y >> (sizeof(short)*8-(BMA2X2_ACC_Y10_LSB__LEN
+ + BMA2X2_ACC_Y_MSB__LEN));
+ break;
+ case 2:
+ comres = bma2x2_smbus_read_byte_block(client,
+ BMA2X2_ACC_Y8_LSB__REG, data, 2);
+ *a_y = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Y8_LSB)|
+ (BMA2X2_GET_BITSLICE(data[1],
+ BMA2X2_ACC_Y_MSB)<<(BMA2X2_ACC_Y8_LSB__LEN));
+ *a_y = *a_y << (sizeof(short)*8-(BMA2X2_ACC_Y8_LSB__LEN
+ + BMA2X2_ACC_Y_MSB__LEN));
+ *a_y = *a_y >> (sizeof(short)*8-(BMA2X2_ACC_Y8_LSB__LEN
+ + BMA2X2_ACC_Y_MSB__LEN));
+ break;
+ case 3:
+ comres = bma2x2_smbus_read_byte_block(client,
+ BMA2X2_ACC_Y14_LSB__REG, data, 2);
+ *a_y = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Y14_LSB)|
+ (BMA2X2_GET_BITSLICE(data[1],
+ BMA2X2_ACC_Y_MSB)<<(BMA2X2_ACC_Y14_LSB__LEN));
+ *a_y = *a_y << (sizeof(short)*8-(BMA2X2_ACC_Y14_LSB__LEN
+ + BMA2X2_ACC_Y_MSB__LEN));
+ *a_y = *a_y >> (sizeof(short)*8-(BMA2X2_ACC_Y14_LSB__LEN
+ + BMA2X2_ACC_Y_MSB__LEN));
+ break;
+ default:
+ break;
+ }
+
+ return comres;
+}
+
+static int bma2x2_read_accel_z(struct i2c_client *client,
+ signed char sensor_type, short *a_z)
+{
+ int comres = 0;
+ unsigned char data[2];
+
+ switch (sensor_type) {
+ case 0:
+ comres = bma2x2_smbus_read_byte_block(client,
+ BMA2X2_ACC_Z12_LSB__REG, data, 2);
+ *a_z = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Z12_LSB)|
+ (BMA2X2_GET_BITSLICE(data[1],
+ BMA2X2_ACC_Z_MSB)<<(BMA2X2_ACC_Z12_LSB__LEN));
+ *a_z = *a_z << (sizeof(short)*8-(BMA2X2_ACC_Z12_LSB__LEN
+ + BMA2X2_ACC_Z_MSB__LEN));
+ *a_z = *a_z >> (sizeof(short)*8-(BMA2X2_ACC_Z12_LSB__LEN
+ + BMA2X2_ACC_Z_MSB__LEN));
+ break;
+ case 1:
+ comres = bma2x2_smbus_read_byte_block(client,
+ BMA2X2_ACC_Z10_LSB__REG, data, 2);
+ *a_z = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Z10_LSB)|
+ (BMA2X2_GET_BITSLICE(data[1],
+ BMA2X2_ACC_Z_MSB)<<(BMA2X2_ACC_Z10_LSB__LEN));
+ *a_z = *a_z << (sizeof(short)*8-(BMA2X2_ACC_Z10_LSB__LEN
+ + BMA2X2_ACC_Z_MSB__LEN));
+ *a_z = *a_z >> (sizeof(short)*8-(BMA2X2_ACC_Z10_LSB__LEN
+ + BMA2X2_ACC_Z_MSB__LEN));
+ break;
+ case 2:
+ comres = bma2x2_smbus_read_byte_block(client,
+ BMA2X2_ACC_Z8_LSB__REG, data, 2);
+ *a_z = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Z8_LSB)|
+ (BMA2X2_GET_BITSLICE(data[1],
+ BMA2X2_ACC_Z_MSB)<<(BMA2X2_ACC_Z8_LSB__LEN));
+ *a_z = *a_z << (sizeof(short)*8-(BMA2X2_ACC_Z8_LSB__LEN
+ + BMA2X2_ACC_Z_MSB__LEN));
+ *a_z = *a_z >> (sizeof(short)*8-(BMA2X2_ACC_Z8_LSB__LEN
+ + BMA2X2_ACC_Z_MSB__LEN));
+ break;
+ case 3:
+ comres = bma2x2_smbus_read_byte_block(client,
+ BMA2X2_ACC_Z14_LSB__REG, data, 2);
+ *a_z = BMA2X2_GET_BITSLICE(data[0], BMA2X2_ACC_Z14_LSB)|
+ (BMA2X2_GET_BITSLICE(data[1],
+ BMA2X2_ACC_Z_MSB)<<(BMA2X2_ACC_Z14_LSB__LEN));
+ *a_z = *a_z << (sizeof(short)*8-(BMA2X2_ACC_Z14_LSB__LEN
+ + BMA2X2_ACC_Z_MSB__LEN));
+ *a_z = *a_z >> (sizeof(short)*8-(BMA2X2_ACC_Z14_LSB__LEN
+ + BMA2X2_ACC_Z_MSB__LEN));
+ break;
+ default:
+ break;
+ }
+
+ return comres;
+}
+
+
+static int bma2x2_read_temperature(struct i2c_client *client,
+ signed char *temperature)
+{
+ unsigned char data = 0;
+ int comres = 0;
+
+ comres = bma2x2_smbus_read_byte(client, BMA2X2_TEMPERATURE_REG, &data);
+ *temperature = (signed char)data;
+
+ return comres;
+}
+
+static ssize_t bma2x2_enable_int_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int type, value;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+#ifdef CONFIG_SENSORS_BMI058
+ int i;
+#endif
+
+ sscanf(buf, "%3d %3d", &type, &value);
+
+#ifdef CONFIG_SENSORS_BMI058
+ for (i = 0; i < sizeof(int_map) / sizeof(struct interrupt_map_t); i++) {
+ if (int_map[i].x == type) {
+ type = int_map[i].y;
+ break;
+ }
+ if (int_map[i].y == type) {
+ type = int_map[i].x;
+ break;
+ }
+ }
+#endif
+
+ if (bma2x2_set_Int_Enable(bma2x2->bma2x2_client, type, value) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+
+static ssize_t bma2x2_int_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_Int_Mode(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t bma2x2_int_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_Int_Mode(bma2x2->bma2x2_client, (unsigned char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+static ssize_t bma2x2_slope_duration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_slope_duration(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_slope_duration_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_slope_duration(bma2x2->bma2x2_client, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_slope_no_mot_duration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_slope_no_mot_duration(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_slope_no_mot_duration_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_slope_no_mot_duration(bma2x2->bma2x2_client, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+
+static ssize_t bma2x2_slope_threshold_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_slope_threshold(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_slope_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (bma2x2_set_slope_threshold(bma2x2->bma2x2_client, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_slope_no_mot_threshold_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_slope_no_mot_threshold(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_slope_no_mot_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (bma2x2_set_slope_no_mot_threshold(bma2x2->bma2x2_client, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_high_g_duration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_high_g_duration(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_high_g_duration_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_high_g_duration(bma2x2->bma2x2_client, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_high_g_threshold_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_high_g_threshold(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_high_g_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (bma2x2_set_high_g_threshold(bma2x2->bma2x2_client, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_low_g_duration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_low_g_duration(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_low_g_duration_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_low_g_duration(bma2x2->bma2x2_client, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_low_g_threshold_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_low_g_threshold(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_low_g_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (bma2x2_set_low_g_threshold(bma2x2->bma2x2_client, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+static ssize_t bma2x2_tap_threshold_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_tap_threshold(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_tap_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (bma2x2_set_tap_threshold(bma2x2->bma2x2_client, (unsigned char)data)
+ < 0)
+ return -EINVAL;
+
+ return count;
+}
+static ssize_t bma2x2_tap_duration_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_tap_duration(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_tap_duration_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_tap_duration(bma2x2->bma2x2_client, (unsigned char)data)
+ < 0)
+ return -EINVAL;
+
+ return count;
+}
+static ssize_t bma2x2_tap_quiet_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_tap_quiet(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_tap_quiet_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_tap_quiet(bma2x2->bma2x2_client, (unsigned char)data) <
+ 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_tap_shock_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_tap_shock(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_tap_shock_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_tap_shock(bma2x2->bma2x2_client, (unsigned char)data) <
+ 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_tap_samp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_tap_samp(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_tap_samp_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_tap_samp(bma2x2->bma2x2_client, (unsigned char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_orient_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_orient_mode(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_orient_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_orient_mode(bma2x2->bma2x2_client, (unsigned char)data) <
+ 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_orient_blocking_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_orient_blocking(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_orient_blocking_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_orient_blocking(bma2x2->bma2x2_client, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+static ssize_t bma2x2_orient_hyst_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_orient_hyst(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_orient_hyst_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_orient_hyst(bma2x2->bma2x2_client, (unsigned char)data) <
+ 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_orient_theta_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_theta_blocking(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_orient_theta_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_theta_blocking(bma2x2->bma2x2_client, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_flat_theta_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_theta_flat(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_flat_theta_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_theta_flat(bma2x2->bma2x2_client, (unsigned char)data) <
+ 0)
+ return -EINVAL;
+
+ return count;
+}
+static ssize_t bma2x2_flat_hold_time_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_get_flat_hold_time(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+static ssize_t bma2x2_selftest_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%d\n", atomic_read(&bma2x2->selftest_result));
+
+}
+
+static ssize_t bma2x2_softreset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if (bma2x2_soft_reset(bma2x2->bma2x2_client) < 0)
+ return -EINVAL;
+
+ return count;
+}
+static ssize_t bma2x2_selftest_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+
+ unsigned long data;
+ unsigned char clear_value = 0;
+ int error;
+ short value1 = 0;
+ short value2 = 0;
+ short diff = 0;
+ unsigned long result = 0;
+ unsigned char test_result_branch = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ bma2x2_soft_reset(bma2x2->bma2x2_client);
+ mdelay(5);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (data != 1)
+ return -EINVAL;
+
+ bma2x2_write_reg(bma2x2->bma2x2_client, 0x32, &clear_value);
+
+ if ((bma2x2->sensor_type == BMA280_TYPE) ||
+ (bma2x2->sensor_type == BMA255_TYPE)) {
+#ifdef CONFIG_SENSORS_BMI058
+ /*set self test amp */
+ if (bma2x2_set_selftest_amp(bma2x2->bma2x2_client, 1) < 0)
+ return -EINVAL;
+ /* set to 8 G range */
+ if (bma2x2_set_range(bma2x2->bma2x2_client,
+ BMA2X2_RANGE_8G) < 0)
+ return -EINVAL;
+#else
+ /* set to 4 G range */
+ if (bma2x2_set_range(bma2x2->bma2x2_client,
+ BMA2X2_RANGE_4G) < 0)
+ return -EINVAL;
+#endif
+ }
+
+ if ((bma2x2->sensor_type == BMA250E_TYPE) ||
+ (bma2x2->sensor_type == BMA222E_TYPE)) {
+ /* set to 8 G range */
+ if (bma2x2_set_range(bma2x2->bma2x2_client, 8) < 0)
+ return -EINVAL;
+ if (bma2x2_set_selftest_amp(bma2x2->bma2x2_client, 1) < 0)
+ return -EINVAL;
+ }
+
+ /* 1 for x-axis(but BMI058 is 1 for y-axis )*/
+ bma2x2_set_selftest_st(bma2x2->bma2x2_client, 1);
+ bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 0);
+ mdelay(10);
+ bma2x2_read_accel_x(bma2x2->bma2x2_client,
+ bma2x2->sensor_type, &value1);
+ bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 1);
+ mdelay(10);
+ bma2x2_read_accel_x(bma2x2->bma2x2_client,
+ bma2x2->sensor_type, &value2);
+ diff = value1-value2;
+
+#ifdef CONFIG_SENSORS_BMI058
+ PINFO("diff y is %d,value1 is %d, value2 is %d\n", diff,
+ value1, value2);
+ test_result_branch = 2;
+#else
+ PINFO("diff x is %d,value1 is %d, value2 is %d\n", diff,
+ value1, value2);
+ test_result_branch = 1;
+#endif
+
+ if (bma2x2->sensor_type == BMA280_TYPE) {
+#ifdef CONFIG_SENSORS_BMI058
+ if (abs(diff) < 819)
+ result |= test_result_branch;
+#else
+ if (abs(diff) < 1638)
+ result |= test_result_branch;
+#endif
+ }
+ if (bma2x2->sensor_type == BMA255_TYPE) {
+ if (abs(diff) < 409)
+ result |= 1;
+ }
+ if (bma2x2->sensor_type == BMA250E_TYPE) {
+ if (abs(diff) < 51)
+ result |= 1;
+ }
+ if (bma2x2->sensor_type == BMA222E_TYPE) {
+ if (abs(diff) < 12)
+ result |= 1;
+ }
+
+ /* 2 for y-axis but BMI058 is 1*/
+ bma2x2_set_selftest_st(bma2x2->bma2x2_client, 2);
+ bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 0);
+ mdelay(10);
+ bma2x2_read_accel_y(bma2x2->bma2x2_client,
+ bma2x2->sensor_type, &value1);
+ bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 1);
+ mdelay(10);
+ bma2x2_read_accel_y(bma2x2->bma2x2_client,
+ bma2x2->sensor_type, &value2);
+ diff = value1-value2;
+
+#ifdef CONFIG_SENSORS_BMI058
+ PINFO("diff x is %d,value1 is %d, value2 is %d\n", diff,
+ value1, value2);
+ test_result_branch = 1;
+#else
+ PINFO("diff y is %d,value1 is %d, value2 is %d\n", diff,
+ value1, value2);
+ test_result_branch = 2;
+#endif
+
+ if (bma2x2->sensor_type == BMA280_TYPE) {
+#ifdef CONFIG_SENSORS_BMI058
+ if (abs(diff) < 819)
+ result |= test_result_branch;
+#else
+ if (abs(diff) < 1638)
+ result |= test_result_branch;
+#endif
+ }
+ if (bma2x2->sensor_type == BMA255_TYPE) {
+ if (abs(diff) < 409)
+ result |= test_result_branch;
+ }
+ if (bma2x2->sensor_type == BMA250E_TYPE) {
+ if (abs(diff) < 51)
+ result |= test_result_branch;
+ }
+ if (bma2x2->sensor_type == BMA222E_TYPE) {
+ if (abs(diff) < 12)
+ result |= test_result_branch;
+ }
+
+
+ bma2x2_set_selftest_st(bma2x2->bma2x2_client, 3); /* 3 for z-axis*/
+ bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 0);
+ mdelay(10);
+ bma2x2_read_accel_z(bma2x2->bma2x2_client,
+ bma2x2->sensor_type, &value1);
+ bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 1);
+ mdelay(10);
+ bma2x2_read_accel_z(bma2x2->bma2x2_client,
+ bma2x2->sensor_type, &value2);
+ diff = value1-value2;
+
+ PINFO("diff z is %d,value1 is %d, value2 is %d\n", diff,
+ value1, value2);
+
+ if (bma2x2->sensor_type == BMA280_TYPE) {
+#ifdef CONFIG_SENSORS_BMI058
+ if (abs(diff) < 409)
+ result |= 4;
+#else
+ if (abs(diff) < 819)
+ result |= 4;
+#endif
+ }
+ if (bma2x2->sensor_type == BMA255_TYPE) {
+ if (abs(diff) < 204)
+ result |= 4;
+ }
+ if (bma2x2->sensor_type == BMA250E_TYPE) {
+ if (abs(diff) < 25)
+ result |= 4;
+ }
+ if (bma2x2->sensor_type == BMA222E_TYPE) {
+ if (abs(diff) < 6)
+ result |= 4;
+ }
+
+ /* self test for bma254 */
+ if ((bma2x2->sensor_type == BMA255_TYPE) && (result > 0)) {
+ result = 0;
+ bma2x2_soft_reset(bma2x2->bma2x2_client);
+ mdelay(5);
+ bma2x2_write_reg(bma2x2->bma2x2_client, 0x32, &clear_value);
+ /* set to 8 G range */
+ if (bma2x2_set_range(bma2x2->bma2x2_client, 8) < 0)
+ return -EINVAL;
+ if (bma2x2_set_selftest_amp(bma2x2->bma2x2_client, 1) < 0)
+ return -EINVAL;
+
+ bma2x2_set_selftest_st(bma2x2->bma2x2_client, 1); /* 1
+ for x-axis*/
+ bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 0); /*
+ positive direction*/
+ mdelay(10);
+ bma2x2_read_accel_x(bma2x2->bma2x2_client,
+ bma2x2->sensor_type, &value1);
+ bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 1); /*
+ negative direction*/
+ mdelay(10);
+ bma2x2_read_accel_x(bma2x2->bma2x2_client,
+ bma2x2->sensor_type, &value2);
+ diff = value1-value2;
+
+ PINFO("diff x is %d,value1 is %d, value2 is %d\n",
+ diff, value1, value2);
+ if (abs(diff) < 204)
+ result |= 1;
+
+ bma2x2_set_selftest_st(bma2x2->bma2x2_client, 2); /* 2
+ for y-axis*/
+ bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 0); /*
+ positive direction*/
+ mdelay(10);
+ bma2x2_read_accel_y(bma2x2->bma2x2_client,
+ bma2x2->sensor_type, &value1);
+ bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 1); /*
+ negative direction*/
+ mdelay(10);
+ bma2x2_read_accel_y(bma2x2->bma2x2_client,
+ bma2x2->sensor_type, &value2);
+ diff = value1-value2;
+ PINFO("diff y is %d,value1 is %d, value2 is %d\n",
+ diff, value1, value2);
+
+ if (abs(diff) < 204)
+ result |= 2;
+
+ bma2x2_set_selftest_st(bma2x2->bma2x2_client, 3); /* 3
+ for z-axis*/
+ bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 0); /*
+ positive direction*/
+ mdelay(10);
+ bma2x2_read_accel_z(bma2x2->bma2x2_client,
+ bma2x2->sensor_type, &value1);
+ bma2x2_set_selftest_stn(bma2x2->bma2x2_client, 1); /*
+ negative direction*/
+ mdelay(10);
+ bma2x2_read_accel_z(bma2x2->bma2x2_client,
+ bma2x2->sensor_type, &value2);
+ diff = value1-value2;
+
+ PINFO("diff z is %d,value1 is %d, value2 is %d\n",
+ diff, value1, value2);
+ if (abs(diff) < 102)
+ result |= 4;
+ }
+
+ atomic_set(&bma2x2->selftest_result, (unsigned int)result);
+
+ bma2x2_soft_reset(bma2x2->bma2x2_client);
+ mdelay(5);
+ PINFO("self test finished\n");
+
+ return count;
+}
+
+
+
+static ssize_t bma2x2_flat_hold_time_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_flat_hold_time(bma2x2->bma2x2_client, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+const int bma2x2_sensor_bitwidth[] = {
+ 12, 10, 8, 14
+};
+
+static int bma2x2_read_accel_xyz(struct i2c_client *client,
+ signed char sensor_type, struct bma2x2acc *acc)
+{
+ int comres = 0;
+ unsigned char data[6];
+ struct bma2x2_data *client_data = i2c_get_clientdata(client);
+#ifndef BMA2X2_SENSOR_IDENTIFICATION_ENABLE
+ int bitwidth;
+#endif
+
+ if(!client_data){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ comres = bma2x2_smbus_read_byte_block(client,
+ BMA2X2_ACC_X12_LSB__REG, data, 6);
+ if (sensor_type >= 4)
+ return -EINVAL;
+
+ acc->x = (data[1]<<8)|data[0];
+ acc->y = (data[3]<<8)|data[2];
+ acc->z = (data[5]<<8)|data[4];
+
+#ifndef BMA2X2_SENSOR_IDENTIFICATION_ENABLE
+ bitwidth = bma2x2_sensor_bitwidth[sensor_type];
+
+ acc->x = (acc->x >> (16 - bitwidth));
+ acc->y = (acc->y >> (16 - bitwidth));
+ acc->z = (acc->z >> (16 - bitwidth));
+#endif
+
+ bma2x2_remap_sensor_data(acc, client_data);
+ return comres;
+}
+
+
+static int bma_cali_flag=0;
+
+int bma2x2_real_cali(struct bma2x2_data *bma2x2);
+
+
+#ifndef CONFIG_BMA_ENABLE_NEWDATA_INT
+
+static void bma2x2_work_func(struct work_struct *work)
+{
+// struct bma2x2_data *bma2x2 = container_of((struct delayed_work *)work,
+ struct bma2x2_data *bma2x2 = container_of((struct work_struct *)work,
+ struct bma2x2_data, work);
+ static struct bma2x2acc acc;
+ int ret=0;
+// unsigned long delay = msecs_to_jiffies(atomic_read(&bma2x2->delay));
+
+ if(bma_cali_flag==0)
+ {
+ ret = bma2x2_real_cali(bma2x2);
+ if(ret >= 0)
+ {
+ bma_cali_flag = 1;
+ }
+ }
+ bma2x2_read_accel_xyz(bma2x2->bma2x2_client, bma2x2->sensor_type, &acc);
+#if 0
+ input_report_abs(bma2x2->input, ABS_X, acc.x);
+ input_report_abs(bma2x2->input, ABS_Y, acc.y);
+ input_report_abs(bma2x2->input, ABS_Z, acc.z);
+#else
+ input_event(bma2x2->input, EV_MSC, MSC_RAW, acc.x);
+ input_event(bma2x2->input, EV_MSC, MSC_SCAN, acc.y);
+ input_event(bma2x2->input, EV_MSC, MSC_GESTURE, acc.z);
+#endif
+ input_sync(bma2x2->input);
+ mutex_lock(&bma2x2->value_mutex);
+ bma2x2->value = acc;
+ mutex_unlock(&bma2x2->value_mutex);
+// schedule_delayed_work(&bma2x2->work, delay);
+}
+#endif
+
+static ssize_t bma2x2_register_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int address, value;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ sscanf(buf, "%3d %3d", &address, &value);
+ if (bma2x2_write_reg(bma2x2->bma2x2_client, (unsigned char)address,
+ (unsigned char *)&value) < 0)
+ return -EINVAL;
+ return count;
+}
+static ssize_t bma2x2_register_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ size_t count = 0;
+ u8 reg[0x40];
+ int i;
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ for (i = 0; i < 0x40; i++) {
+ bma2x2_smbus_read_byte(bma2x2->bma2x2_client, i, reg+i);
+
+ count += sprintf(&buf[count], "0x%x: %d\n", i, reg[i]);
+ }
+ return count;
+
+
+}
+
+static ssize_t bma2x2_range_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2_get_range(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t bma2x2_range_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (bma2x2_set_range(bma2x2->bma2x2_client, (unsigned char) data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_bandwidth_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+ if (bma2x2_get_bandwidth(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_bandwidth_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2->sensor_type == BMA280_TYPE)
+ if ((unsigned char) data > 14)
+ return -EINVAL;
+
+ if (bma2x2_set_bandwidth(bma2x2->bma2x2_client,
+ (unsigned char) data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2_get_mode(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d %d\n", data, bma2x2->bma_mode_enabled);
+}
+
+static ssize_t bma2x2_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (bma2x2_set_mode(bma2x2->bma2x2_client,
+ (unsigned char) data, BMA_ENABLED_BSX) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_value_cache_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_dev *input = to_input_dev(dev);
+ struct bma2x2_data *bma2x2 = input_get_drvdata(input);
+ struct bma2x2acc acc_value;
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ mutex_lock(&bma2x2->value_mutex);
+ acc_value = bma2x2->value;
+ mutex_unlock(&bma2x2->value_mutex);
+
+ return sprintf(buf, "%d %d %d\n", acc_value.x, acc_value.y,
+ acc_value.z);
+}
+
+static ssize_t bma2x2_value_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_dev *input = to_input_dev(dev);
+ struct bma2x2_data *bma2x2 = input_get_drvdata(input);
+ struct bma2x2acc acc_value;
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ bma2x2_read_accel_xyz(bma2x2->bma2x2_client, bma2x2->sensor_type,
+ &acc_value);
+
+ return sprintf(buf, "%d %d %d\n", acc_value.x, acc_value.y,
+ acc_value.z);
+}
+
+static ssize_t bma2x2_delay_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ return sprintf(buf, "%d\n", atomic_read(&bma2x2->delay));
+
+}
+
+static ssize_t bma2x2_chip_id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ return sprintf(buf, "%u\n", bma2x2->chip_id);
+
+}
+
+
+static ssize_t bma2x2_place_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+ int place = BOSCH_SENSOR_PLACE_UNKNOWN;
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (NULL != bma2x2->bst_pd)
+ place = bma2x2->bst_pd->place;
+
+ return sprintf(buf, "%d\n", place);
+}
+
+
+static ssize_t bma2x2_delay_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (data > BMA2X2_MAX_DELAY)
+ data = BMA2X2_MAX_DELAY;
+ atomic_set(&bma2x2->delay, (unsigned int) data);
+
+ return count;
+}
+
+int bma2x2_real_cali(struct bma2x2_data *bma2x2)
+{
+ int cali_xyz=0;
+ unsigned int flag=0;
+ unsigned char bst_offset_x=0;
+ unsigned char bst_offset_y=0;
+ unsigned char bst_offset_z=0;
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ cali_xyz = read_file("/productinfo/gsensor_calibration.ini");
+
+ if(cali_xyz<0){
+ return -EINVAL;
+ }
+
+ flag=(cali_xyz>>24)&0xff;
+
+ if(flag==0x2)
+ {
+ bst_offset_x = cali_xyz & 0xff;
+ bst_offset_y = (cali_xyz>>8)&0xff;
+ bst_offset_z = (cali_xyz>>16)&0xff;
+ }
+ else
+ {
+ bst_offset_x = 0;
+ bst_offset_y = 0;
+ bst_offset_z = 0;
+ }
+
+ if (bma2x2_set_offset_x(bma2x2->bma2x2_client, bst_offset_x) < 0)
+ return -EINVAL;
+
+ if (bma2x2_set_offset_y(bma2x2->bma2x2_client, bst_offset_y) < 0)
+ return -EINVAL;
+
+ if (bma2x2_set_offset_z(bma2x2->bma2x2_client, bst_offset_z) < 0)
+ return -EINVAL;
+
+ return 0;
+}
+
+static ssize_t bma2x2_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ return sprintf(buf, "%d\n", atomic_read(&bma2x2->enable));
+
+}
+
+static void bma2x2_set_enable(struct device *dev, int enable)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+ int pre_enable;
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ pre_enable = atomic_read(&bma2x2->enable);
+
+ mutex_lock(&bma2x2->enable_mutex);
+
+ if (enable) {
+
+ if (pre_enable == 0) {
+ bma2x2_set_mode(bma2x2->bma2x2_client,
+ BMA2X2_MODE_NORMAL, BMA_ENABLED_INPUT);
+
+#if 0
+#ifndef CONFIG_BMA_ENABLE_NEWDATA_INT
+ schedule_delayed_work(&bma2x2->work,
+ msecs_to_jiffies(atomic_read(&bma2x2->delay)));
+#endif
+#endif
+ hrtimer_start(&bma2x2->timer, ktime_set(0,
+ atomic_read(&bma2x2->delay) * 1000000), HRTIMER_MODE_REL);
+ atomic_set(&bma2x2->enable, 1);
+ }
+
+ } else {
+ if (pre_enable == 1) {
+ bma2x2_set_mode(bma2x2->bma2x2_client,
+ BMA2X2_MODE_SUSPEND, BMA_ENABLED_INPUT);
+#if 0
+#ifndef CONFIG_BMA_ENABLE_NEWDATA_INT
+ cancel_delayed_work_sync(&bma2x2->work);
+#endif
+#endif
+ hrtimer_cancel(&bma2x2->timer);
+ atomic_set(&bma2x2->enable, 0);
+ }
+ }
+ mutex_unlock(&bma2x2->enable_mutex);
+
+}
+
+static ssize_t bma2x2_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if ((data == 0) || (data == 1))
+ bma2x2_set_enable(dev, data);
+
+ return count;
+}
+static ssize_t bma2x2_fast_calibration_x_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+#ifdef CONFIG_SENSORS_BMI058
+ if (bma2x2_get_offset_target(bma2x2->bma2x2_client,
+ BMI058_OFFSET_TRIGGER_X, &data) < 0)
+ return -EINVAL;
+#else
+ if (bma2x2_get_offset_target(bma2x2->bma2x2_client,
+ BMA2X2_OFFSET_TRIGGER_X, &data) < 0)
+ return -EINVAL;
+#endif
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_fast_calibration_x_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ signed char tmp;
+ unsigned char timeout = 0;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+#ifdef CONFIG_SENSORS_BMI058
+ if (bma2x2_set_offset_target(bma2x2->bma2x2_client,
+ BMI058_OFFSET_TRIGGER_X, (unsigned char)data) < 0)
+ return -EINVAL;
+#else
+ if (bma2x2_set_offset_target(bma2x2->bma2x2_client,
+ BMA2X2_OFFSET_TRIGGER_X, (unsigned char)data) < 0)
+ return -EINVAL;
+#endif
+
+ if (bma2x2_set_cal_trigger(bma2x2->bma2x2_client, 1) < 0)
+ return -EINVAL;
+
+ do {
+ mdelay(2);
+ bma2x2_get_cal_ready(bma2x2->bma2x2_client, &tmp);
+
+ /*PINFO("wait 2ms cal ready flag is %d\n", tmp); */
+ timeout++;
+ if (timeout == 50) {
+ PINFO("get fast calibration ready error\n");
+ return -EINVAL;
+ };
+
+ } while (tmp == 0);
+
+ PINFO("x axis fast calibration finished\n");
+ return count;
+}
+
+
+static int bma2x2_fast_calibration_x(struct device *dev, unsigned char data)
+{
+ signed char tmp;
+ unsigned char timeout = 0;
+
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+#ifdef CONFIG_SENSORS_BMI058
+ if (bma2x2_set_offset_target(bma2x2->bma2x2_client,
+ BMI058_OFFSET_TRIGGER_X, (unsigned char)data) < 0)
+ return -EINVAL;
+#else
+ if (bma2x2_set_offset_target(bma2x2->bma2x2_client,
+ BMA2X2_OFFSET_TRIGGER_X, (unsigned char)data) < 0)
+ return -EINVAL;
+#endif
+
+ if (bma2x2_set_cal_trigger(bma2x2->bma2x2_client, 1) < 0)
+ return -EINVAL;
+
+ do {
+ mdelay(2);
+ bma2x2_get_cal_ready(bma2x2->bma2x2_client, &tmp);
+
+ /*PINFO("wait 2ms cal ready flag is %d\n", tmp); */
+ timeout++;
+ if (timeout == 50) {
+ PINFO("get fast calibration ready error\n");
+ return -EINVAL;
+ };
+
+ } while (tmp == 0);
+
+ PINFO("x axis fast calibration finished\n");
+ return 0;
+}
+
+
+static ssize_t bma2x2_fast_calibration_y_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+#ifdef CONFIG_SENSORS_BMI058
+ if (bma2x2_get_offset_target(bma2x2->bma2x2_client,
+ BMI058_OFFSET_TRIGGER_Y, &data) < 0)
+ return -EINVAL;
+#else
+ if (bma2x2_get_offset_target(bma2x2->bma2x2_client,
+ BMA2X2_OFFSET_TRIGGER_Y, &data) < 0)
+ return -EINVAL;
+#endif
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_fast_calibration_y_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ signed char tmp;
+ unsigned char timeout = 0;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+#ifdef CONFIG_SENSORS_BMI058
+ if (bma2x2_set_offset_target(bma2x2->bma2x2_client,
+ BMI058_OFFSET_TRIGGER_Y, (unsigned char)data) < 0)
+ return -EINVAL;
+#else
+ if (bma2x2_set_offset_target(bma2x2->bma2x2_client,
+ BMA2X2_OFFSET_TRIGGER_Y, (unsigned char)data) < 0)
+ return -EINVAL;
+#endif
+
+ if (bma2x2_set_cal_trigger(bma2x2->bma2x2_client, 2) < 0)
+ return -EINVAL;
+
+ do {
+ mdelay(2);
+ bma2x2_get_cal_ready(bma2x2->bma2x2_client, &tmp);
+
+ /*PINFO("wait 2ms cal ready flag is %d\n", tmp);*/
+ timeout++;
+ if (timeout == 50) {
+ PINFO("get fast calibration ready error\n");
+ return -EINVAL;
+ };
+
+ } while (tmp == 0);
+
+ PINFO("y axis fast calibration finished\n");
+ return count;
+}
+
+
+static int bma2x2_fast_calibration_y(struct device *dev, unsigned char data)
+{
+ signed char tmp;
+ unsigned char timeout = 0;
+
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+#ifdef CONFIG_SENSORS_BMI058
+ if (bma2x2_set_offset_target(bma2x2->bma2x2_client,
+ BMI058_OFFSET_TRIGGER_Y, (unsigned char)data) < 0)
+ return -EINVAL;
+#else
+ if (bma2x2_set_offset_target(bma2x2->bma2x2_client,
+ BMA2X2_OFFSET_TRIGGER_Y, (unsigned char)data) < 0)
+ return -EINVAL;
+#endif
+
+ if (bma2x2_set_cal_trigger(bma2x2->bma2x2_client, 2) < 0)
+ return -EINVAL;
+
+ do {
+ mdelay(2);
+ bma2x2_get_cal_ready(bma2x2->bma2x2_client, &tmp);
+
+ /*PINFO("wait 2ms cal ready flag is %d\n", tmp);*/
+ timeout++;
+ if (timeout == 50) {
+ PINFO("get fast calibration ready error\n");
+ return -EINVAL;
+ };
+
+ } while (tmp == 0);
+
+ PINFO("y axis fast calibration finished\n");
+ return 0;
+}
+
+
+static ssize_t bma2x2_fast_calibration_z_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2_get_offset_target(bma2x2->bma2x2_client, 3, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_fast_calibration_z_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ signed char tmp;
+ unsigned char timeout = 0;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_offset_target(bma2x2->bma2x2_client, 3, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ if (bma2x2_set_cal_trigger(bma2x2->bma2x2_client, 3) < 0)
+ return -EINVAL;
+
+ do {
+ mdelay(2);
+ bma2x2_get_cal_ready(bma2x2->bma2x2_client, &tmp);
+
+ /*PINFO("wait 2ms cal ready flag is %d\n", tmp);*/
+ timeout++;
+ if (timeout == 50) {
+ PINFO("get fast calibration ready error\n");
+ return -EINVAL;
+ };
+
+ } while (tmp == 0);
+
+ PINFO("z axis fast calibration finished\n");
+ return count;
+}
+
+
+static ssize_t bma2x2_fast_calibration_z(struct device *dev,unsigned char data)
+{
+ signed char tmp;
+ unsigned char timeout = 0;
+
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2_set_offset_target(bma2x2->bma2x2_client, 3, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ if (bma2x2_set_cal_trigger(bma2x2->bma2x2_client, 3) < 0)
+ return -EINVAL;
+
+ do {
+ mdelay(2);
+ bma2x2_get_cal_ready(bma2x2->bma2x2_client, &tmp);
+
+ /*PINFO("wait 2ms cal ready flag is %d\n", tmp);*/
+ timeout++;
+ if (timeout == 50) {
+ PINFO("get fast calibration ready error\n");
+ return -EINVAL;
+ };
+
+ } while (tmp == 0);
+
+ PINFO("z axis fast calibration finished\n");
+ return 0;
+}
+
+
+static ssize_t bma2x2_enable_cali_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char datax = 0;
+ unsigned char datay = 0;
+ unsigned char dataz = 0;
+ unsigned int data=0;
+
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ bma2x2_fast_calibration_x(dev,3);
+ bma2x2_fast_calibration_y(dev,3);
+ bma2x2_fast_calibration_z(dev,2);
+
+ if (bma2x2_get_offset_x(bma2x2->bma2x2_client, &datax) < 0)
+ return -1;
+ if (bma2x2_get_offset_y(bma2x2->bma2x2_client, &datay) < 0)
+ return -1;
+ if (bma2x2_get_offset_z(bma2x2->bma2x2_client, &dataz) < 0)
+ return -1;
+
+ data = dataz;
+ data = (data<<8)|datay;
+ data = (data<<8)|datax;
+
+ return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t bma2x2_SleepDur_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2_get_sleep_duration(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t bma2x2_SleepDur_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (bma2x2_set_sleep_duration(bma2x2->bma2x2_client,
+ (unsigned char) data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_fifo_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2_get_fifo_mode(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_fifo_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (bma2x2_set_fifo_mode(bma2x2->bma2x2_client,
+ (unsigned char) data) < 0)
+ return -EINVAL;
+ return count;
+}
+
+static ssize_t bma2x2_fifo_trig_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2_get_fifo_trig(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_fifo_trig_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (bma2x2_set_fifo_trig(bma2x2->bma2x2_client,
+ (unsigned char) data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_fifo_trig_src_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2_get_fifo_trig_src(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_fifo_trig_src_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (bma2x2_set_fifo_trig_src(bma2x2->bma2x2_client,
+ (unsigned char) data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+
+/*!
+ * @brief show fifo_data_sel axis definition(Android definition, not sensor HW reg).
+ * 0--> x, y, z axis fifo data for every frame
+ * 1--> only x axis fifo data for every frame
+ * 2--> only y axis fifo data for every frame
+ * 3--> only z axis fifo data for every frame
+ */
+static ssize_t bma2x2_fifo_data_sel_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+ signed char place = BOSCH_SENSOR_PLACE_UNKNOWN;
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2_get_fifo_data_sel(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+#ifdef CONFIG_SENSORS_BMI058
+/*Update BMI058 fifo_data_sel to the BMA2x2 common definition*/
+ if (BMI058_FIFO_DAT_SEL_X == data)
+ data = BMA2X2_FIFO_DAT_SEL_X;
+ else if (BMI058_FIFO_DAT_SEL_Y == data)
+ data = BMA2X2_FIFO_DAT_SEL_Y;
+#endif
+
+ /*remaping fifo_dat_sel if define virtual place in BSP files*/
+ if ((NULL != bma2x2->bst_pd) &&
+ (BOSCH_SENSOR_PLACE_UNKNOWN != bma2x2->bst_pd->place)) {
+ place = bma2x2->bst_pd->place;
+ /* sensor with place 0 needs not to be remapped */
+ if ((place > 0) && (place < MAX_AXIS_REMAP_TAB_SZ)) {
+ /* BMA2X2_FIFO_DAT_SEL_X: 1, Y:2, Z:3;
+ * but bst_axis_remap_tab_dft[i].src_x:0, y:1, z:2
+ * so we need to +1*/
+ if (BMA2X2_FIFO_DAT_SEL_X == data)
+ data = bst_axis_remap_tab_dft[place].src_x + 1;
+ else if (BMA2X2_FIFO_DAT_SEL_Y == data)
+ data = bst_axis_remap_tab_dft[place].src_y + 1;
+ }
+ }
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_fifo_framecount_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ unsigned char mode;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2_get_fifo_framecount(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ if (data > MAX_FIFO_F_LEVEL) {
+
+ if (bma2x2_get_mode(bma2x2->bma2x2_client, &mode) < 0)
+ return -EINVAL;
+
+ if (BMA2X2_MODE_NORMAL == mode) {
+ PERR("bma2x2 fifo_count: %d abnormal, op_mode: %d",
+ data, mode);
+ data = MAX_FIFO_F_LEVEL;
+ } else {
+ /*chip already suspend or shutdown*/
+ data = 0;
+ }
+ }
+
+ return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t bma2x2_fifo_framecount_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ bma2x2->fifo_count = (unsigned int) data;
+
+ return count;
+}
+
+static ssize_t bma2x2_temperature_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2_read_temperature(bma2x2->bma2x2_client, &data) < 0)
+ return -EINVAL;
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+/*!
+ * @brief store fifo_data_sel axis definition(Android definition, not sensor HW reg).
+ * 0--> x, y, z axis fifo data for every frame
+ * 1--> only x axis fifo data for every frame
+ * 2--> only y axis fifo data for every frame
+ * 3--> only z axis fifo data for every frame
+ */
+static ssize_t bma2x2_fifo_data_sel_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+ signed char place;
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+ /*save fifo_data_sel(android definition)*/
+ bma2x2->fifo_datasel = (unsigned char) data;
+
+ /*remaping fifo_dat_sel if define virtual place*/
+ if ((NULL != bma2x2->bst_pd) &&
+ (BOSCH_SENSOR_PLACE_UNKNOWN != bma2x2->bst_pd->place)) {
+ place = bma2x2->bst_pd->place;
+ /* sensor with place 0 needs not to be remapped */
+ if ((place > 0) && (place < MAX_AXIS_REMAP_TAB_SZ)) {
+ /*Need X Y axis revesal sensor place: P1, P3, P5, P7 */
+ /* BMA2X2_FIFO_DAT_SEL_X: 1, Y:2, Z:3;
+ * but bst_axis_remap_tab_dft[i].src_x:0, y:1, z:2
+ * so we need to +1*/
+ if (BMA2X2_FIFO_DAT_SEL_X == data)
+ data = bst_axis_remap_tab_dft[place].src_x + 1;
+ else if (BMA2X2_FIFO_DAT_SEL_Y == data)
+ data = bst_axis_remap_tab_dft[place].src_y + 1;
+ }
+ }
+#ifdef CONFIG_SENSORS_BMI058
+ /*Update BMI058 fifo_data_sel to the BMA2x2 common definition*/
+ if (BMA2X2_FIFO_DAT_SEL_X == data)
+ data = BMI058_FIFO_DAT_SEL_X;
+ else if (BMA2X2_FIFO_DAT_SEL_Y == data)
+ data = BMI058_FIFO_DAT_SEL_Y;
+
+#endif
+ if (bma2x2_set_fifo_data_sel(bma2x2->bma2x2_client,
+ (unsigned char) data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+
+/*!
+ * brief: bma2x2 single axis data remaping
+ * @param[i] fifo_datasel fifo axis data select setting
+ * @param[i/o] remap_dir remapping direction
+ * @param[i] client_data to transfer sensor place
+ *
+ * @return none
+ */
+static void bma2x2_single_axis_remaping(unsigned char fifo_datasel,
+ unsigned char *remap_dir, struct bma2x2_data *client_data)
+{
+ if ((NULL == client_data->bst_pd) ||
+ (BOSCH_SENSOR_PLACE_UNKNOWN
+ == client_data->bst_pd->place))
+ return;
+ else {
+ signed char place = client_data->bst_pd->place;
+ /* sensor with place 0 needs not to be remapped */
+ if ((place <= 0) || (place >= MAX_AXIS_REMAP_TAB_SZ))
+ return;
+
+ if (fifo_datasel < 1 || fifo_datasel > 3)
+ return;
+ else {
+ switch (fifo_datasel) {
+ /*P2, P3, P4, P5 X axis(andorid) need to reverse*/
+ case BMA2X2_FIFO_DAT_SEL_X:
+ if (-1 == bst_axis_remap_tab_dft[place].sign_x)
+ *remap_dir = 1;
+ else
+ *remap_dir = 0;
+ break;
+ /*P1, P2, P5, P6 Y axis(andorid) need to reverse*/
+ case BMA2X2_FIFO_DAT_SEL_Y:
+ if (-1 == bst_axis_remap_tab_dft[place].sign_y)
+ *remap_dir = 1;
+ else
+ *remap_dir = 0;
+ break;
+ case BMA2X2_FIFO_DAT_SEL_Z:
+ /*P4, P5, P6, P7 Z axis(andorid) need to reverse*/
+ if (-1 == bst_axis_remap_tab_dft[place].sign_z)
+ *remap_dir = 1;
+ else
+ *remap_dir = 0;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return;
+}
+
+static ssize_t bma2x2_fifo_data_out_frame_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int err, i, len;
+ signed char fifo_data_out[MAX_FIFO_F_LEVEL * MAX_FIFO_F_BYTES] = {0};
+ unsigned char f_len = 0;
+ s16 value;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+ struct bma2x2acc acc_lsb;
+ unsigned char axis_dir_remap = 0;
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2->fifo_datasel) {
+ /*Select one axis data output for every fifo frame*/
+ f_len = 2;
+ } else {
+ /*Select X Y Z axis data output for every fifo frame*/
+ f_len = 6;
+ }
+
+ if (bma2x2->fifo_count == 0)
+ return -EINVAL;
+
+ if (bma2x2->bma_mode_enabled == 0) {
+ PERR("bma2x2_fifo_data, bma_mode_enabled 0");
+ /*return -EINVAL;*/
+ }
+
+ if (bma_i2c_burst_read(bma2x2->bma2x2_client,
+ BMA2X2_FIFO_DATA_OUTPUT_REG, fifo_data_out,
+ bma2x2->fifo_count * f_len) < 0)
+ return -EINVAL;
+
+ err = 0;
+
+/* please give attation for the fifo output data format*/
+ if (f_len == 6) {
+ /* Select X Y Z axis data output for every frame */
+ for (i = 0; i < bma2x2->fifo_count; i++) {
+ acc_lsb.x =
+ ((unsigned char)fifo_data_out[i * f_len + 1] << 8 |
+ (unsigned char)fifo_data_out[i * f_len + 0]);
+ acc_lsb.y =
+ ((unsigned char)fifo_data_out[i * f_len + 3] << 8 |
+ (unsigned char)fifo_data_out[i * f_len + 2]);
+ acc_lsb.z =
+ ((unsigned char)fifo_data_out[i * f_len + 5] << 8 |
+ (unsigned char)fifo_data_out[i * f_len + 4]);
+#ifndef BMA2X2_SENSOR_IDENTIFICATION_ENABLE
+ acc_lsb.x >>=
+ (16 - bma2x2_sensor_bitwidth[bma2x2->sensor_type]);
+ acc_lsb.y >>=
+ (16 - bma2x2_sensor_bitwidth[bma2x2->sensor_type]);
+ acc_lsb.z >>=
+ (16 - bma2x2_sensor_bitwidth[bma2x2->sensor_type]);
+#endif
+ bma2x2_remap_sensor_data(&acc_lsb, bma2x2);
+ len = sprintf(buf, "%d %d %d ",
+ acc_lsb.x, acc_lsb.y, acc_lsb.z);
+ buf += len;
+ err += len;
+ }
+ } else {
+ /* single axis data output for every frame */
+ bma2x2_single_axis_remaping(bma2x2->fifo_datasel,
+ &axis_dir_remap, bma2x2);
+ for (i = 0; i < bma2x2->fifo_count * f_len / 2; i++) {
+ value = ((unsigned char)fifo_data_out[2 * i + 1] << 8 |
+ (unsigned char)fifo_data_out[2 * i]);
+#ifndef BMA2X2_SENSOR_IDENTIFICATION_ENABLE
+ value >>=
+ (16 - bma2x2_sensor_bitwidth[bma2x2->sensor_type]);
+#endif
+ if (axis_dir_remap)
+ value = 0 - value;
+ len = sprintf(buf, "%d ", value);
+ buf += len;
+ err += len;
+ }
+ }
+
+ return err;
+}
+
+static ssize_t bma2x2_offset_x_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2_get_offset_x(bma2x2->bma2x2_client, &data) < 0)
+ return sprintf(buf, "Read error\n");
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_offset_x_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_offset_x(bma2x2->bma2x2_client, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_offset_y_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2_get_offset_y(bma2x2->bma2x2_client, &data) < 0)
+ return sprintf(buf, "Read error\n");
+
+ return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t bma2x2_offset_y_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_offset_y(bma2x2->bma2x2_client, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma2x2_offset_z_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data = 0;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (bma2x2_get_offset_z(bma2x2->bma2x2_client, &data) < 0)
+ return sprintf(buf, "Read error\n");
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma2x2_offset_z_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if (bma2x2_set_offset_z(bma2x2->bma2x2_client, (unsigned
+ char)data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+#ifdef CONFIG_SIG_MOTION
+static int bma2x2_set_en_slope_int(struct bma2x2_data *bma2x2,
+ int en)
+{
+ int err;
+ struct i2c_client *client = bma2x2->bma2x2_client;
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (en) {
+ /* Set the related parameters which needs to be fine tuned by
+ * interfaces: slope_threshold and slope_duration
+ */
+ /*dur: 192 samples ~= 3s*/
+ err = bma2x2_set_slope_duration(client, 0x0);
+ err += bma2x2_set_slope_threshold(client, 0x16);
+
+ /*Enable the interrupts*/
+ err += bma2x2_set_Int_Enable(client, 5, 1);/*Slope X*/
+ err += bma2x2_set_Int_Enable(client, 6, 1);/*Slope Y*/
+ err += bma2x2_set_Int_Enable(client, 7, 1);/*Slope Z*/
+ #ifdef BMA2X2_ENABLE_INT1
+ /* TODO: SLOPE can now only be routed to INT1 pin*/
+ err += bma2x2_set_int1_pad_sel(client, PAD_SLOP);
+ #else
+ /* err += bma2x2_set_int2_pad_sel(client, PAD_SLOP); */
+ #endif
+ } else {
+ err = bma2x2_set_Int_Enable(client, 5, 0);/*Slope X*/
+ err += bma2x2_set_Int_Enable(client, 6, 0);/*Slope Y*/
+ err += bma2x2_set_Int_Enable(client, 7, 0);/*Slope Z*/
+ }
+ return err;
+}
+
+static ssize_t bma2x2_en_sig_motion_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ return sprintf(buf, "%d\n", atomic_read(&bma2x2->en_sig_motion));
+}
+
+static int bma2x2_set_en_sig_motion(struct bma2x2_data *bma2x2,
+ int en)
+{
+ int err = 0;
+
+ en = (en >= 1) ? 1 : 0; /* set sig motion sensor status */
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ if (atomic_read(&bma2x2->en_sig_motion) != en) {
+ if (en) {
+ err = bma2x2_set_mode(bma2x2->bma2x2_client,
+ BMA2X2_MODE_NORMAL, BMA_ENABLED_SGM);
+ err = bma2x2_set_en_slope_int(bma2x2, en);
+ enable_irq_wake(bma2x2->IRQ);
+ } else {
+ disable_irq_wake(bma2x2->IRQ);
+ err = bma2x2_set_en_slope_int(bma2x2, en);
+ err = bma2x2_set_mode(bma2x2->bma2x2_client,
+ BMA2X2_MODE_SUSPEND, BMA_ENABLED_SGM);
+ }
+ atomic_set(&bma2x2->en_sig_motion, en);
+ }
+ return err;
+}
+
+static ssize_t bma2x2_en_sig_motion_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ if(!bma2x2){
+ return BOSCH_SENSOR_BAD_ADDR;
+ }
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if ((data == 0) || (data == 1))
+ bma2x2_set_en_sig_motion(bma2x2, data);
+
+ return count;
+}
+#endif
+
+#ifdef CONFIG_DOUBLE_TAP
+static int bma2x2_set_en_single_tap_int(struct bma2x2_data *bma2x2, int en)
+{
+ int err;
+ struct i2c_client *client = bma2x2->bma2x2_client;
+
+ if (en) {
+ /* set tap interruption parameter here if needed.
+ bma2x2_set_tap_duration(client, 0xc0);
+ bma2x2_set_tap_threshold(client, 0x16);
+ */
+
+ /*Enable the single tap interrupts*/
+ err = bma2x2_set_Int_Enable(client, 8, 1);
+ #ifdef BMA2X2_ENABLE_INT1
+ err += bma2x2_set_int1_pad_sel(client, PAD_SINGLE_TAP);
+ #else
+ err += bma2x2_set_int2_pad_sel(client, PAD_SINGLE_TAP);
+ #endif
+ } else {
+ err = bma2x2_set_Int_Enable(client, 8, 0);
+ }
+ return err;
+}
+
+static ssize_t bma2x2_tap_time_period_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%d\n", bma2x2->tap_time_period);
+}
+
+static ssize_t bma2x2_tap_time_period_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ bma2x2->tap_time_period = data;
+
+ return count;
+}
+
+static ssize_t bma2x2_en_double_tap_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%d\n", atomic_read(&bma2x2->en_double_tap));
+}
+
+static int bma2x2_set_en_double_tap(struct bma2x2_data *bma2x2,
+ int en)
+{
+ int err = 0;
+
+ en = (en >= 1) ? 1 : 0;
+
+ if (atomic_read(&bma2x2->en_double_tap) != en) {
+ if (en) {
+ err = bma2x2_set_mode(bma2x2->bma2x2_client,
+ BMA2X2_MODE_NORMAL, BMA_ENABLED_DTAP);
+ err = bma2x2_set_en_single_tap_int(bma2x2, en);
+ } else {
+ err = bma2x2_set_en_single_tap_int(bma2x2, en);
+ err = bma2x2_set_mode(bma2x2->bma2x2_client,
+ BMA2X2_MODE_SUSPEND, BMA_ENABLED_DTAP);
+ }
+ atomic_set(&bma2x2->en_double_tap, en);
+ }
+ return err;
+}
+
+static ssize_t bma2x2_en_double_tap_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long data;
+ int error;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma2x2_data *bma2x2 = i2c_get_clientdata(client);
+
+ error = kstrtoul(buf, 10, &data);
+ if (error)
+ return error;
+
+ if ((data == 0) || (data == 1))
+ bma2x2_set_en_double_tap(bma2x2, data);
+
+ return count;
+}
+
+static void bma2x2_tap_timeout_handle(unsigned long data)
+{
+ struct bma2x2_data *bma2x2 = (struct bma2x2_data *)data;
+
+ PINFO("tap interrupt handle, timeout\n");
+ mutex_lock(&bma2x2->tap_mutex);
+ bma2x2->tap_times = 0;
+ mutex_unlock(&bma2x2->tap_mutex);
+
+ /* if a single tap need to report, open the define */
+#ifdef REPORT_SINGLE_TAP_WHEN_DOUBLE_TAP_SENSOR_ENABLED
+ input_report_rel(bma2x2->dev_interrupt,
+ SINGLE_TAP_INTERRUPT,
+ SINGLE_TAP_INTERRUPT_HAPPENED);
+ input_sync(bma2x2->dev_interrupt);
+#endif
+
+}
+#endif
+
+static DEVICE_ATTR(range, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_range_show, bma2x2_range_store);
+static DEVICE_ATTR(bandwidth, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_bandwidth_show, bma2x2_bandwidth_store);
+static DEVICE_ATTR(op_mode, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_mode_show, bma2x2_mode_store);
+static DEVICE_ATTR(value, S_IRUGO,
+ bma2x2_value_show, NULL);
+static DEVICE_ATTR(value_cache, S_IRUGO,
+ bma2x2_value_cache_show, NULL);
+static DEVICE_ATTR(delay, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_delay_show, bma2x2_delay_store);
+static DEVICE_ATTR(enable, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_enable_show, bma2x2_enable_store);
+static DEVICE_ATTR(SleepDur, S_IRUGO|S_IWUSR|S_IWGRP,
+ bma2x2_SleepDur_show, bma2x2_SleepDur_store);
+static DEVICE_ATTR(fast_calibration_x, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_fast_calibration_x_show,
+ bma2x2_fast_calibration_x_store);
+static DEVICE_ATTR(fast_calibration_y, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_fast_calibration_y_show,
+ bma2x2_fast_calibration_y_store);
+static DEVICE_ATTR(fast_calibration_z, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_fast_calibration_z_show,
+ bma2x2_fast_calibration_z_store);
+static DEVICE_ATTR(enable_cali, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_enable_cali_show,
+ NULL);
+static DEVICE_ATTR(fifo_mode, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_fifo_mode_show, bma2x2_fifo_mode_store);
+static DEVICE_ATTR(fifo_framecount, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_fifo_framecount_show, bma2x2_fifo_framecount_store);
+static DEVICE_ATTR(fifo_trig, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_fifo_trig_show, bma2x2_fifo_trig_store);
+static DEVICE_ATTR(fifo_trig_src, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_fifo_trig_src_show, bma2x2_fifo_trig_src_store);
+static DEVICE_ATTR(fifo_data_sel, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_fifo_data_sel_show, bma2x2_fifo_data_sel_store);
+static DEVICE_ATTR(fifo_data_frame, S_IRUGO,
+ bma2x2_fifo_data_out_frame_show, NULL);
+static DEVICE_ATTR(reg, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_register_show, bma2x2_register_store);
+static DEVICE_ATTR(chip_id, S_IRUGO,
+ bma2x2_chip_id_show, NULL);
+static DEVICE_ATTR(offset_x, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_offset_x_show,
+ bma2x2_offset_x_store);
+static DEVICE_ATTR(offset_y, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_offset_y_show,
+ bma2x2_offset_y_store);
+static DEVICE_ATTR(offset_z, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_offset_z_show,
+ bma2x2_offset_z_store);
+static DEVICE_ATTR(enable_int, S_IWUSR|S_IWGRP|S_IWOTH,
+ NULL, bma2x2_enable_int_store);
+static DEVICE_ATTR(int_mode, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_int_mode_show, bma2x2_int_mode_store);
+static DEVICE_ATTR(slope_duration, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_slope_duration_show, bma2x2_slope_duration_store);
+static DEVICE_ATTR(slope_threshold, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_slope_threshold_show, bma2x2_slope_threshold_store);
+static DEVICE_ATTR(slope_no_mot_duration, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_slope_no_mot_duration_show,
+ bma2x2_slope_no_mot_duration_store);
+static DEVICE_ATTR(slope_no_mot_threshold, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_slope_no_mot_threshold_show,
+ bma2x2_slope_no_mot_threshold_store);
+static DEVICE_ATTR(high_g_duration, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_high_g_duration_show, bma2x2_high_g_duration_store);
+static DEVICE_ATTR(high_g_threshold, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_high_g_threshold_show, bma2x2_high_g_threshold_store);
+static DEVICE_ATTR(low_g_duration, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_low_g_duration_show, bma2x2_low_g_duration_store);
+static DEVICE_ATTR(low_g_threshold, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_low_g_threshold_show, bma2x2_low_g_threshold_store);
+static DEVICE_ATTR(tap_duration, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_tap_duration_show, bma2x2_tap_duration_store);
+static DEVICE_ATTR(tap_threshold, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_tap_threshold_show, bma2x2_tap_threshold_store);
+static DEVICE_ATTR(tap_quiet, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_tap_quiet_show, bma2x2_tap_quiet_store);
+static DEVICE_ATTR(tap_shock, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_tap_shock_show, bma2x2_tap_shock_store);
+static DEVICE_ATTR(tap_samp, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_tap_samp_show, bma2x2_tap_samp_store);
+static DEVICE_ATTR(orient_mode, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_orient_mode_show, bma2x2_orient_mode_store);
+static DEVICE_ATTR(orient_blocking, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_orient_blocking_show, bma2x2_orient_blocking_store);
+static DEVICE_ATTR(orient_hyst, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_orient_hyst_show, bma2x2_orient_hyst_store);
+static DEVICE_ATTR(orient_theta, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_orient_theta_show, bma2x2_orient_theta_store);
+static DEVICE_ATTR(flat_theta, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_flat_theta_show, bma2x2_flat_theta_store);
+static DEVICE_ATTR(flat_hold_time, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_flat_hold_time_show, bma2x2_flat_hold_time_store);
+static DEVICE_ATTR(selftest, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_selftest_show, bma2x2_selftest_store);
+static DEVICE_ATTR(softreset, S_IWUSR|S_IWGRP|S_IWOTH,
+ NULL, bma2x2_softreset_store);
+static DEVICE_ATTR(temperature, S_IRUGO,
+ bma2x2_temperature_show, NULL);
+static DEVICE_ATTR(place, S_IRUGO,
+ bma2x2_place_show, NULL);
+#ifdef CONFIG_SIG_MOTION
+static DEVICE_ATTR(en_sig_motion, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_en_sig_motion_show, bma2x2_en_sig_motion_store);
+#endif
+#ifdef CONFIG_DOUBLE_TAP
+static DEVICE_ATTR(tap_time_period, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_tap_time_period_show, bma2x2_tap_time_period_store);
+static DEVICE_ATTR(en_double_tap, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma2x2_en_double_tap_show, bma2x2_en_double_tap_store);
+#endif
+
+static struct attribute *bma2x2_attributes[] = {
+ &dev_attr_range.attr,
+ &dev_attr_bandwidth.attr,
+ &dev_attr_op_mode.attr,
+ &dev_attr_value.attr,
+ &dev_attr_value_cache.attr,
+ &dev_attr_delay.attr,
+ &dev_attr_enable.attr,
+ &dev_attr_SleepDur.attr,
+ &dev_attr_reg.attr,
+ &dev_attr_fast_calibration_x.attr,
+ &dev_attr_fast_calibration_y.attr,
+ &dev_attr_fast_calibration_z.attr,
+ &dev_attr_enable_cali.attr,
+ &dev_attr_fifo_mode.attr,
+ &dev_attr_fifo_framecount.attr,
+ &dev_attr_fifo_trig.attr,
+ &dev_attr_fifo_trig_src.attr,
+ &dev_attr_fifo_data_sel.attr,
+ &dev_attr_fifo_data_frame.attr,
+ &dev_attr_chip_id.attr,
+ &dev_attr_offset_x.attr,
+ &dev_attr_offset_y.attr,
+ &dev_attr_offset_z.attr,
+ &dev_attr_enable_int.attr,
+ &dev_attr_int_mode.attr,
+ &dev_attr_slope_duration.attr,
+ &dev_attr_slope_threshold.attr,
+ &dev_attr_slope_no_mot_duration.attr,
+ &dev_attr_slope_no_mot_threshold.attr,
+ &dev_attr_high_g_duration.attr,
+ &dev_attr_high_g_threshold.attr,
+ &dev_attr_low_g_duration.attr,
+ &dev_attr_low_g_threshold.attr,
+ &dev_attr_tap_threshold.attr,
+ &dev_attr_tap_duration.attr,
+ &dev_attr_tap_quiet.attr,
+ &dev_attr_tap_shock.attr,
+ &dev_attr_tap_samp.attr,
+ &dev_attr_orient_mode.attr,
+ &dev_attr_orient_blocking.attr,
+ &dev_attr_orient_hyst.attr,
+ &dev_attr_orient_theta.attr,
+ &dev_attr_flat_theta.attr,
+ &dev_attr_flat_hold_time.attr,
+ &dev_attr_selftest.attr,
+ &dev_attr_softreset.attr,
+ &dev_attr_temperature.attr,
+ &dev_attr_place.attr,
+#ifdef CONFIG_SIG_MOTION
+ &dev_attr_en_sig_motion.attr,
+#endif
+#ifdef CONFIG_DOUBLE_TAP
+ &dev_attr_en_double_tap.attr,
+#endif
+
+ NULL
+};
+
+static struct attribute_group bma2x2_attribute_group = {
+ .attrs = bma2x2_attributes
+};
+
+#ifdef CONFIG_SIG_MOTION
+static struct attribute *bma2x2_sig_motion_attributes[] = {
+ &dev_attr_slope_duration.attr,
+ &dev_attr_slope_threshold.attr,
+ &dev_attr_en_sig_motion.attr,
+ NULL
+};
+static struct attribute_group bma2x2_sig_motion_attribute_group = {
+ .attrs = bma2x2_sig_motion_attributes
+};
+#endif
+
+#ifdef CONFIG_DOUBLE_TAP
+static struct attribute *bma2x2_double_tap_attributes[] = {
+ &dev_attr_tap_threshold.attr,
+ &dev_attr_tap_duration.attr,
+ &dev_attr_tap_quiet.attr,
+ &dev_attr_tap_shock.attr,
+ &dev_attr_tap_samp.attr,
+ &dev_attr_tap_time_period.attr,
+ &dev_attr_en_double_tap.attr,
+ NULL
+};
+static struct attribute_group bma2x2_double_tap_attribute_group = {
+ .attrs = bma2x2_double_tap_attributes
+};
+#endif
+
+
+#if defined(BMA2X2_ENABLE_INT1) || defined(BMA2X2_ENABLE_INT2)
+unsigned char *orient[] = {"upward looking portrait upright",
+ "upward looking portrait upside-down",
+ "upward looking landscape left",
+ "upward looking landscape right",
+ "downward looking portrait upright",
+ "downward looking portrait upside-down",
+ "downward looking landscape left",
+ "downward looking landscape right"};
+
+
+static void bma2x2_high_g_interrupt_handle(struct bma2x2_data *bma2x2)
+{
+ unsigned char first_value = 0;
+ unsigned char sign_value = 0;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ bma2x2_get_HIGH_first(bma2x2->bma2x2_client, i, &first_value);
+ if (first_value == 1) {
+ bma2x2_get_HIGH_sign(bma2x2->bma2x2_client,
+ &sign_value);
+ if (sign_value == 1) {
+ if (i == 0)
+ input_report_rel(bma2x2->dev_interrupt,
+ HIGH_G_INTERRUPT,
+ HIGH_G_INTERRUPT_X_N);
+ if (i == 1)
+ input_report_rel(bma2x2->dev_interrupt,
+ HIGH_G_INTERRUPT,
+ HIGH_G_INTERRUPT_Y_N);
+ if (i == 2)
+ input_report_rel(bma2x2->dev_interrupt,
+ HIGH_G_INTERRUPT,
+ HIGH_G_INTERRUPT_Z_N);
+ } else {
+ if (i == 0)
+ input_report_rel(bma2x2->dev_interrupt,
+ HIGH_G_INTERRUPT,
+ HIGH_G_INTERRUPT_X);
+ if (i == 1)
+ input_report_rel(bma2x2->dev_interrupt,
+ HIGH_G_INTERRUPT,
+ HIGH_G_INTERRUPT_Y);
+ if (i == 2)
+ input_report_rel(bma2x2->dev_interrupt,
+ HIGH_G_INTERRUPT,
+ HIGH_G_INTERRUPT_Z);
+ }
+ }
+
+ PINFO("High G interrupt happened,exis is %d,"
+ "first is %d,sign is %d\n", i,
+ first_value, sign_value);
+ }
+
+
+}
+
+#ifndef CONFIG_SIG_MOTION
+static void bma2x2_slope_interrupt_handle(struct bma2x2_data *bma2x2)
+{
+ unsigned char first_value = 0;
+ unsigned char sign_value = 0;
+ int i;
+ for (i = 0; i < 3; i++) {
+ bma2x2_get_slope_first(bma2x2->bma2x2_client, i, &first_value);
+ if (first_value == 1) {
+ bma2x2_get_slope_sign(bma2x2->bma2x2_client,
+ &sign_value);
+ if (sign_value == 1) {
+ if (i == 0)
+ input_report_rel(bma2x2->dev_interrupt,
+ SLOP_INTERRUPT,
+ SLOPE_INTERRUPT_X_N);
+ if (i == 1)
+ input_report_rel(bma2x2->dev_interrupt,
+ SLOP_INTERRUPT,
+ SLOPE_INTERRUPT_Y_N);
+ if (i == 2)
+ input_report_rel(bma2x2->dev_interrupt,
+ SLOP_INTERRUPT,
+ SLOPE_INTERRUPT_Z_N);
+ } else {
+ if (i == 0)
+ input_report_rel(bma2x2->dev_interrupt,
+ SLOP_INTERRUPT,
+ SLOPE_INTERRUPT_X);
+ if (i == 1)
+ input_report_rel(bma2x2->dev_interrupt,
+ SLOP_INTERRUPT,
+ SLOPE_INTERRUPT_Y);
+ if (i == 2)
+ input_report_rel(bma2x2->dev_interrupt,
+ SLOP_INTERRUPT,
+ SLOPE_INTERRUPT_Z);
+
+ }
+ }
+
+ PINFO("Slop interrupt happened,exis is %d,"
+ "first is %d,sign is %d\n", i,
+ first_value, sign_value);
+ }
+}
+#endif
+
+static void bma2x2_irq_work_func(struct work_struct *work)
+{
+ struct bma2x2_data *bma2x2 = container_of((struct work_struct *)work,
+ struct bma2x2_data, irq_work);
+#ifdef CONFIG_DOUBLE_TAP
+ struct i2c_client *client = bma2x2->bma2x2_client;
+#endif
+
+ unsigned char status = 0;
+ unsigned char first_value = 0;
+ unsigned char sign_value = 0;
+
+#ifdef CONFIG_BMA_ENABLE_NEWDATA_INT
+ static struct bma2x2acc acc;
+
+ bma2x2_get_interruptstatus2(bma2x2->bma2x2_client, &status);
+
+ if ((status&0x80) == 0x80) {
+ /* PINFO("New data interrupt happened\n");*/
+ bma2x2_read_accel_xyz(bma2x2->bma2x2_client,
+ bma2x2->sensor_type, &acc);
+ input_report_abs(bma2x2->input, ABS_X, acc.x);
+ input_report_abs(bma2x2->input, ABS_Y, acc.y);
+ input_report_abs(bma2x2->input, ABS_Z, acc.z);
+ input_sync(bma2x2->input);
+ mutex_lock(&bma2x2->value_mutex);
+ bma2x2->value = acc;
+ mutex_unlock(&bma2x2->value_mutex);
+ return;
+ }
+#endif
+
+ bma2x2_get_interruptstatus1(bma2x2->bma2x2_client, &status);
+ PINFO("bma2x2_irq_work_func, status = 0x%x\n", status);
+
+#ifdef CONFIG_SIG_MOTION
+ if (status & 0x04) {
+ if (atomic_read(&bma2x2->en_sig_motion) == 1) {
+ PINFO("Significant motion interrupt happened\n");
+ /* close sig sensor,
+ it will be open again if APP wants */
+ bma2x2_set_en_sig_motion(bma2x2, 0);
+
+ input_report_rel(bma2x2->dev_interrupt,
+ SLOP_INTERRUPT, 1);
+ input_sync(bma2x2->dev_interrupt);
+ }
+ }
+#endif
+
+#ifdef CONFIG_DOUBLE_TAP
+ if (status & 0x20) {
+ if (atomic_read(&bma2x2->en_double_tap) == 1) {
+ PINFO("single tap interrupt happened\n");
+ bma2x2_set_Int_Enable(client, 8, 0);
+ if (bma2x2->tap_times == 0) {
+ mod_timer(&bma2x2->tap_timer, jiffies +
+ msecs_to_jiffies(bma2x2->tap_time_period));
+ bma2x2->tap_times = 1;
+ } else {
+ /* only double tap is judged */
+ PINFO("double tap\n");
+ mutex_lock(&bma2x2->tap_mutex);
+ bma2x2->tap_times = 0;
+ del_timer(&bma2x2->tap_timer);
+ mutex_unlock(&bma2x2->tap_mutex);
+ input_report_rel(bma2x2->dev_interrupt,
+ DOUBLE_TAP_INTERRUPT,
+ DOUBLE_TAP_INTERRUPT_HAPPENED);
+ input_sync(bma2x2->dev_interrupt);
+ }
+ bma2x2_set_Int_Enable(client, 8, 1);
+ }
+ }
+#endif
+
+ switch (status) {
+
+ case 0x01:
+ PINFO("Low G interrupt happened\n");
+ input_report_rel(bma2x2->dev_interrupt, LOW_G_INTERRUPT,
+ LOW_G_INTERRUPT_HAPPENED);
+ break;
+
+ case 0x02:
+ bma2x2_high_g_interrupt_handle(bma2x2);
+ break;
+
+#ifndef CONFIG_SIG_MOTION
+ case 0x04:
+ bma2x2_slope_interrupt_handle(bma2x2);
+ break;
+#endif
+
+ case 0x08:
+ PINFO("slow/ no motion interrupt happened\n");
+ input_report_rel(bma2x2->dev_interrupt,
+ SLOW_NO_MOTION_INTERRUPT,
+ SLOW_NO_MOTION_INTERRUPT_HAPPENED);
+ break;
+
+#ifndef CONFIG_DOUBLE_TAP
+ case 0x10:
+ PINFO("double tap interrupt happened\n");
+ input_report_rel(bma2x2->dev_interrupt,
+ DOUBLE_TAP_INTERRUPT,
+ DOUBLE_TAP_INTERRUPT_HAPPENED);
+ break;
+ case 0x20:
+ PINFO("single tap interrupt happened\n");
+ input_report_rel(bma2x2->dev_interrupt,
+ SINGLE_TAP_INTERRUPT,
+ SINGLE_TAP_INTERRUPT_HAPPENED);
+ break;
+#endif
+
+ case 0x40:
+ bma2x2_get_orient_status(bma2x2->bma2x2_client,
+ &first_value);
+ PINFO("orient interrupt happened,%s\n",
+ orient[first_value]);
+ if (first_value == 0)
+ input_report_abs(bma2x2->dev_interrupt,
+ ORIENT_INTERRUPT,
+ UPWARD_PORTRAIT_UP_INTERRUPT_HAPPENED);
+ else if (first_value == 1)
+ input_report_abs(bma2x2->dev_interrupt,
+ ORIENT_INTERRUPT,
+ UPWARD_PORTRAIT_DOWN_INTERRUPT_HAPPENED);
+ else if (first_value == 2)
+ input_report_abs(bma2x2->dev_interrupt,
+ ORIENT_INTERRUPT,
+ UPWARD_LANDSCAPE_LEFT_INTERRUPT_HAPPENED);
+ else if (first_value == 3)
+ input_report_abs(bma2x2->dev_interrupt,
+ ORIENT_INTERRUPT,
+ UPWARD_LANDSCAPE_RIGHT_INTERRUPT_HAPPENED);
+ else if (first_value == 4)
+ input_report_abs(bma2x2->dev_interrupt,
+ ORIENT_INTERRUPT,
+ DOWNWARD_PORTRAIT_UP_INTERRUPT_HAPPENED);
+ else if (first_value == 5)
+ input_report_abs(bma2x2->dev_interrupt,
+ ORIENT_INTERRUPT,
+ DOWNWARD_PORTRAIT_DOWN_INTERRUPT_HAPPENED);
+ else if (first_value == 6)
+ input_report_abs(bma2x2->dev_interrupt,
+ ORIENT_INTERRUPT,
+ DOWNWARD_LANDSCAPE_LEFT_INTERRUPT_HAPPENED);
+ else if (first_value == 7)
+ input_report_abs(bma2x2->dev_interrupt,
+ ORIENT_INTERRUPT,
+ DOWNWARD_LANDSCAPE_RIGHT_INTERRUPT_HAPPENED);
+ break;
+ case 0x80:
+ bma2x2_get_orient_flat_status(bma2x2->bma2x2_client,
+ &sign_value);
+ PINFO("flat interrupt happened,flat status is %d\n",
+ sign_value);
+ if (sign_value == 1) {
+ input_report_abs(bma2x2->dev_interrupt,
+ FLAT_INTERRUPT,
+ FLAT_INTERRUPT_TURE_HAPPENED);
+ } else {
+ input_report_abs(bma2x2->dev_interrupt,
+ FLAT_INTERRUPT,
+ FLAT_INTERRUPT_FALSE_HAPPENED);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static irqreturn_t bma2x2_irq_handler(int irq, void *handle)
+{
+ struct bma2x2_data *data = handle;
+
+ if (data == NULL)
+ return IRQ_HANDLED;
+ if (data->bma2x2_client == NULL)
+ return IRQ_HANDLED;
+
+ schedule_work(&data->irq_work);
+
+ return IRQ_HANDLED;
+}
+#endif /* defined(BMA2X2_ENABLE_INT1)||defined(BMA2X2_ENABLE_INT2) */
+
+static enum hrtimer_restart timer_handler(struct hrtimer *handle)
+{
+ struct bma2x2_data *client_data;
+
+ client_data = container_of(handle, struct bma2x2_data, timer);
+ queue_work(client_data->wq, &client_data->work);
+
+ hrtimer_forward_now(&client_data->timer, ktime_set(0, atomic_read(&client_data->delay) * 1000000));
+
+ return HRTIMER_RESTART;
+}
+
+static int bma2x2_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err = 0;
+ struct bma2x2_data *data;
+ struct input_dev *dev;
+ struct bst_dev *dev_acc;
+#if defined(BMA2X2_ENABLE_INT1) || defined(BMA2X2_ENABLE_INT2)
+ struct bosch_sensor_specific *pdata;
+#endif
+ struct input_dev *dev_interrupt;
+
+ PINFO("bma2x2_probe start\n");
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ PERR("i2c_check_functionality error\n");
+ err = -EIO;
+ goto exit;
+ }
+ data = kzalloc(sizeof(struct bma2x2_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ /* do soft reset */
+ mdelay(5);
+ if (bma2x2_soft_reset(client) < 0) {
+ PERR("i2c bus write error, pls check HW connection\n");
+ err = -EINVAL;
+ goto kfree_exit;
+ }
+ mdelay(5);
+ /* read and check chip id */
+ if (bma2x2_check_chip_id(client, data) < 0) {
+ err = -EINVAL;
+ goto kfree_exit;
+ }
+
+ i2c_set_clientdata(client, data);
+ data->bma2x2_client = client;
+ mutex_init(&data->value_mutex);
+ mutex_init(&data->mode_mutex);
+ mutex_init(&data->enable_mutex);
+ bma2x2_set_bandwidth(client, BMA2X2_BW_SET);
+ bma2x2_set_range(client, BMA2X2_RANGE_SET);
+
+
+#if defined(BMA2X2_ENABLE_INT1) || defined(BMA2X2_ENABLE_INT2)
+
+ pdata = client->dev.platform_data;
+ if (pdata) {
+ if (pdata->irq_gpio_cfg && (pdata->irq_gpio_cfg() < 0)) {
+ PERR("IRQ GPIO conf. error %d\n",
+ client->irq);
+ }
+ }
+
+#ifdef BMA2X2_ENABLE_INT1
+ /* maps interrupt to INT1 pin */
+ bma2x2_set_int1_pad_sel(client, PAD_LOWG);
+ bma2x2_set_int1_pad_sel(client, PAD_HIGHG);
+ bma2x2_set_int1_pad_sel(client, PAD_SLOP);
+ bma2x2_set_int1_pad_sel(client, PAD_DOUBLE_TAP);
+ bma2x2_set_int1_pad_sel(client, PAD_SINGLE_TAP);
+ bma2x2_set_int1_pad_sel(client, PAD_ORIENT);
+ bma2x2_set_int1_pad_sel(client, PAD_FLAT);
+ bma2x2_set_int1_pad_sel(client, PAD_SLOW_NO_MOTION);
+#ifdef CONFIG_BMA_ENABLE_NEWDATA_INT
+ bma2x2_set_newdata(client, BMA2X2_INT1_NDATA, 1);
+ bma2x2_set_newdata(client, BMA2X2_INT2_NDATA, 0);
+#endif
+#endif
+
+#ifdef BMA2X2_ENABLE_INT2
+ /* maps interrupt to INT2 pin */
+ bma2x2_set_int2_pad_sel(client, PAD_LOWG);
+ bma2x2_set_int2_pad_sel(client, PAD_HIGHG);
+ bma2x2_set_int2_pad_sel(client, PAD_SLOP);
+ bma2x2_set_int2_pad_sel(client, PAD_DOUBLE_TAP);
+ bma2x2_set_int2_pad_sel(client, PAD_SINGLE_TAP);
+ bma2x2_set_int2_pad_sel(client, PAD_ORIENT);
+ bma2x2_set_int2_pad_sel(client, PAD_FLAT);
+ bma2x2_set_int2_pad_sel(client, PAD_SLOW_NO_MOTION);
+#ifdef CONFIG_BMA_ENABLE_NEWDATA_INT
+ bma2x2_set_newdata(client, BMA2X2_INT1_NDATA, 0);
+ bma2x2_set_newdata(client, BMA2X2_INT2_NDATA, 1);
+#endif
+#endif
+
+ bma2x2_set_Int_Mode(client, 1);/*latch interrupt 250ms*/
+
+ /* do not open any interrupt here */
+ /*10,orient
+ 11,flat*/
+ /* bma2x2_set_Int_Enable(client, 10, 1); */
+ /* bma2x2_set_Int_Enable(client, 11, 1); */
+
+#ifdef CONFIG_BMA_ENABLE_NEWDATA_INT
+ /* enable new data interrupt */
+ bma2x2_set_Int_Enable(client, 4, 1);
+#endif
+
+
+ data->IRQ = client->irq;
+ err = request_irq(data->IRQ, bma2x2_irq_handler, IRQF_TRIGGER_RISING,
+ "bma2x2", data);
+#ifdef CONFIG_SIG_MOTION
+ enable_irq_wake(data->IRQ);
+#endif
+ if (err)
+ PERR("could not request irq\n");
+
+ INIT_WORK(&data->irq_work, bma2x2_irq_work_func);
+#endif
+
+#ifndef CONFIG_BMA_ENABLE_NEWDATA_INT
+ //INIT_DELAYED_WORK(&data->work, bma2x2_work_func);
+ INIT_WORK(&data->work, bma2x2_work_func);
+ data->wq = create_workqueue("bma_wq");
+ hrtimer_init(&data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ data->timer.function = timer_handler;
+#endif
+ atomic_set(&data->delay, BMA2X2_MAX_DELAY);
+ atomic_set(&data->enable, 0);
+
+ dev = input_allocate_device();
+ if (!dev)
+ return -ENOMEM;
+
+ dev_interrupt = input_allocate_device();
+ if (!dev_interrupt) {
+ kfree(data);
+ input_free_device(dev); /*free the successful dev and return*/
+ return -ENOMEM;
+ }
+
+ /* only value events reported */
+ dev->name = SENSOR_NAME;
+ dev->id.bustype = BUS_I2C;
+#if 0
+ input_set_capability(dev, EV_ABS, ABS_MISC);
+ input_set_abs_params(dev, ABS_X, ABSMIN, ABSMAX, 0, 0);
+ input_set_abs_params(dev, ABS_Y, ABSMIN, ABSMAX, 0, 0);
+ input_set_abs_params(dev, ABS_Z, ABSMIN, ABSMAX, 0, 0);
+#else
+ input_set_capability(dev, EV_MSC, MSC_RAW);
+ input_set_capability(dev, EV_MSC, MSC_SCAN);
+ input_set_capability(dev, EV_MSC, MSC_GESTURE);
+#endif
+
+ input_set_drvdata(dev, data);
+ err = input_register_device(dev);
+ if (err < 0)
+ goto err_register_input_device;
+
+ /* all interrupt generated events are moved to interrupt input devices*/
+ dev_interrupt->name = "bma_interrupt";
+ dev_interrupt->id.bustype = BUS_I2C;
+ input_set_capability(dev_interrupt, EV_REL,
+ SLOW_NO_MOTION_INTERRUPT);
+ input_set_capability(dev_interrupt, EV_REL,
+ LOW_G_INTERRUPT);
+ input_set_capability(dev_interrupt, EV_REL,
+ HIGH_G_INTERRUPT);
+ input_set_capability(dev_interrupt, EV_REL,
+ SLOP_INTERRUPT);
+ input_set_capability(dev_interrupt, EV_REL,
+ DOUBLE_TAP_INTERRUPT);
+ input_set_capability(dev_interrupt, EV_REL,
+ SINGLE_TAP_INTERRUPT);
+ input_set_capability(dev_interrupt, EV_ABS,
+ ORIENT_INTERRUPT);
+ input_set_capability(dev_interrupt, EV_ABS,
+ FLAT_INTERRUPT);
+ input_set_drvdata(dev_interrupt, data);
+
+ err = input_register_device(dev_interrupt);
+ if (err < 0)
+ goto err_register_input_device_interrupt;
+
+ data->dev_interrupt = dev_interrupt;
+ data->input = dev;
+
+#ifdef CONFIG_SIG_MOTION
+ data->g_sensor_class = class_create(THIS_MODULE, "sig_sensor");
+ if (IS_ERR(data->g_sensor_class)) {
+ err = PTR_ERR(data->g_sensor_class);
+ data->g_sensor_class = NULL;
+ PERR("could not allocate g_sensor_class\n");
+ goto err_create_class;
+ }
+
+ data->g_sensor_dev = device_create(data->g_sensor_class,
+ NULL, 0, "%s", "g_sensor");
+ if (unlikely(IS_ERR(data->g_sensor_dev))) {
+ err = PTR_ERR(data->g_sensor_dev);
+ data->g_sensor_dev = NULL;
+
+ PERR("could not allocate g_sensor_dev\n");
+ goto err_create_g_sensor_device;
+ }
+
+ dev_set_drvdata(data->g_sensor_dev, data);
+
+ err = sysfs_create_group(&data->g_sensor_dev->kobj,
+ &bma2x2_sig_motion_attribute_group);
+ if (err < 0)
+ goto error_sysfs;
+#endif
+
+
+#ifdef CONFIG_DOUBLE_TAP
+ data->g_sensor_class_doubletap =
+ class_create(THIS_MODULE, "dtap_sensor");
+ if (IS_ERR(data->g_sensor_class_doubletap)) {
+ err = PTR_ERR(data->g_sensor_class_doubletap);
+ data->g_sensor_class_doubletap = NULL;
+ PERR("could not allocate g_sensor_class_doubletap\n");
+ goto err_create_class;
+ }
+
+
+ data->g_sensor_dev_doubletap = device_create(
+ data->g_sensor_class_doubletap,
+ NULL, 0, "%s", "g_sensor");
+ if (unlikely(IS_ERR(data->g_sensor_dev_doubletap))) {
+ err = PTR_ERR(data->g_sensor_dev_doubletap);
+ data->g_sensor_dev_doubletap = NULL;
+
+ PERR("could not allocate g_sensor_dev_doubletap\n");
+ goto err_create_g_sensor_device_double_tap;
+ }
+
+
+ dev_set_drvdata(data->g_sensor_dev_doubletap, data);
+
+ err = sysfs_create_group(&data->g_sensor_dev_doubletap->kobj,
+ &bma2x2_double_tap_attribute_group);
+ if (err < 0)
+ goto error_sysfs;
+#endif
+
+
+ err = sysfs_create_group(&data->input->dev.kobj,
+ &bma2x2_attribute_group);
+ if (err < 0)
+ goto error_sysfs;
+
+ dev_acc = bst_allocate_device();
+ if (!dev_acc) {
+ err = -ENOMEM;
+ goto error_sysfs;
+ }
+ dev_acc->name = ACC_NAME;
+
+ bst_set_drvdata(dev_acc, data);
+
+ err = bst_register_device(dev_acc);
+ if (err < 0)
+ goto bst_free_acc_exit;
+
+ data->bst_acc = dev_acc;
+ err = sysfs_create_group(&data->bst_acc->dev.kobj,
+ &bma2x2_attribute_group);
+
+ if (err < 0)
+ goto bst_free_exit;
+
+#if 0
+ if (NULL != client->dev.platform_data) {
+ data->bst_pd = kzalloc(sizeof(*data->bst_pd),
+ GFP_KERNEL);
+
+ if (NULL != data->bst_pd) {
+ memcpy(data->bst_pd, client->dev.platform_data,
+ sizeof(*data->bst_pd));
+ PINFO("%s sensor driver set place: p%d",
+ data->bst_pd->name, data->bst_pd->place);
+ }
+ }
+#endif
+
+ data->bst_pd = kzalloc(sizeof(*data->bst_pd),
+ GFP_KERNEL);
+
+ if (NULL != data->bst_pd) {
+ data->bst_pd->place=BOSCH_SENSOR_PLACE;
+ }
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ data->early_suspend.suspend = bma2x2_early_suspend;
+ data->early_suspend.resume = bma2x2_late_resume;
+ register_early_suspend(&data->early_suspend);
+#endif
+
+ data->bma_mode_enabled = 0;
+ data->fifo_datasel = 0;
+ data->fifo_count = 0;
+
+
+#ifdef CONFIG_SIG_MOTION
+ atomic_set(&data->en_sig_motion, 0);
+#endif
+#ifdef CONFIG_DOUBLE_TAP
+ atomic_set(&data->en_double_tap, 0);
+ data->tap_times = 0;
+ data->tap_time_period = DEFAULT_TAP_JUDGE_PERIOD;
+ mutex_init(&data->tap_mutex);
+ setup_timer(&data->tap_timer, bma2x2_tap_timeout_handle,
+ (unsigned long)data);
+#endif
+
+ if (bma2x2_set_mode(client, BMA2X2_MODE_SUSPEND, BMA_ENABLED_ALL) < 0)
+ return -EINVAL;
+ PINFO("BMA2x2 driver probe successfully");
+
+ return 0;
+
+bst_free_exit:
+ bst_unregister_device(dev_acc);
+
+bst_free_acc_exit:
+ bst_free_device(dev_acc);
+
+error_sysfs:
+ input_unregister_device(data->input);
+
+#ifdef CONFIG_DOUBLE_TAP
+err_create_g_sensor_device_double_tap:
+ class_destroy(data->g_sensor_class_doubletap);
+#endif
+
+#ifdef CONFIG_SIG_MOTION
+err_create_g_sensor_device:
+ class_destroy(data->g_sensor_class);
+#endif
+
+#if defined(CONFIG_SIG_MOTION) || defined(CONFIG_DOUBLE_TAP)
+err_create_class:
+ input_unregister_device(data->dev_interrupt);
+#endif
+
+err_register_input_device_interrupt:
+ input_free_device(dev_interrupt);
+ input_unregister_device(data->input);
+
+err_register_input_device:
+ input_free_device(dev);
+
+kfree_exit:
+ if ((NULL != data) && (NULL != data->bst_pd)) {
+ kfree(data->bst_pd);
+ data->bst_pd = NULL;
+ }
+ kfree(data);
+exit:
+ return err;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void bma2x2_early_suspend(struct early_suspend *h)
+{
+ struct bma2x2_data *data =
+ container_of(h, struct bma2x2_data, early_suspend);
+
+ mutex_lock(&data->enable_mutex);
+ if (atomic_read(&data->enable) == 1) {
+ bma2x2_set_mode(data->bma2x2_client,
+ BMA2X2_MODE_SUSPEND, BMA_ENABLED_INPUT);
+#ifndef CONFIG_BMA_ENABLE_NEWDATA_INT
+// cancel_delayed_work_sync(&data->work);
+ cancel_work_sync(&data->work);
+#endif
+ }
+ mutex_unlock(&data->enable_mutex);
+}
+
+static void bma2x2_late_resume(struct early_suspend *h)
+{
+ struct bma2x2_data *data =
+ container_of(h, struct bma2x2_data, early_suspend);
+ if (NULL == data)
+ return;
+
+ mutex_lock(&data->enable_mutex);
+ if (atomic_read(&data->enable) == 1) {
+ bma2x2_set_mode(data->bma2x2_client,
+ BMA2X2_MODE_NORMAL, BMA_ENABLED_INPUT);
+#ifndef CONFIG_BMA_ENABLE_NEWDATA_INT
+// schedule_delayed_work(&data->work,
+// msecs_to_jiffies(atomic_read(&data->delay)));
+ schedule_work(&data->work);
+#endif
+ }
+ mutex_unlock(&data->enable_mutex);
+}
+#endif
+
+static int bma2x2_remove(struct i2c_client *client)
+{
+ struct bma2x2_data *data = i2c_get_clientdata(client);
+
+ if (NULL == data)
+ return 0;
+
+ bma2x2_set_enable(&client->dev, 0);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&data->early_suspend);
+#endif
+ sysfs_remove_group(&data->input->dev.kobj, &bma2x2_attribute_group);
+ input_unregister_device(data->input);
+
+ if (NULL != data->bst_pd) {
+ kfree(data->bst_pd);
+ data->bst_pd = NULL;
+ }
+
+ kfree(data);
+ return 0;
+}
+
+void bma2x2_shutdown(struct i2c_client *client)
+{
+ struct bma2x2_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->enable_mutex);
+ bma2x2_set_mode(data->bma2x2_client,
+ BMA2X2_MODE_DEEP_SUSPEND, BMA_ENABLED_ALL);
+ mutex_unlock(&data->enable_mutex);
+}
+
+#ifdef CONFIG_PM
+static int bma2x2_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct bma2x2_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->enable_mutex);
+ if (atomic_read(&data->enable) == 1) {
+ bma2x2_set_mode(data->bma2x2_client,
+ BMA2X2_MODE_SUSPEND, BMA_ENABLED_INPUT);
+#ifndef CONFIG_BMA_ENABLE_NEWDATA_INT
+// cancel_delayed_work_sync(&data->work);
+ cancel_work_sync(&data->work);
+
+#endif
+ }
+ mutex_unlock(&data->enable_mutex);
+
+ return 0;
+}
+
+static int bma2x2_resume(struct i2c_client *client)
+{
+ struct bma2x2_data *data = i2c_get_clientdata(client);
+
+ mutex_lock(&data->enable_mutex);
+ if (atomic_read(&data->enable) == 1) {
+ bma2x2_set_mode(data->bma2x2_client,
+ BMA2X2_MODE_NORMAL, BMA_ENABLED_INPUT);
+#ifndef CONFIG_BMA_ENABLE_NEWDATA_INT
+// schedule_delayed_work(&data->work,
+// msecs_to_jiffies(atomic_read(&data->delay)));
+ schedule_work(&data->work);
+#endif
+ }
+ mutex_unlock(&data->enable_mutex);
+
+ return 0;
+}
+
+#else
+
+#define bma2x2_suspend NULL
+#define bma2x2_resume NULL
+
+#endif /* CONFIG_PM */
+
+static const struct i2c_device_id bma2x2_id[] = {
+ { SENSOR_NAME, 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, bma2x2_id);
+
+
+static struct of_device_id bst_match_table[] = {
+ { .compatible = "bst,bma2x2", },
+ { },
+};
+
+static struct i2c_driver bma2x2_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = SENSOR_NAME,
+#ifdef CONFIG_OF
+ .of_match_table = bst_match_table,
+#endif
+ },
+ .suspend = bma2x2_suspend,
+ .resume = bma2x2_resume,
+ .id_table = bma2x2_id,
+ .probe = bma2x2_probe,
+ .remove = bma2x2_remove,
+ .shutdown = bma2x2_shutdown,
+};
+
+static struct bosch_sensor_specific __initdata bma2x2_pdata = {
+ .name = "bma2x2",
+ .place = 0,
+};
+
+static struct i2c_board_info __initdata sprd_i2c_bst = {
+ I2C_BOARD_INFO("bma2x2", 0x18),
+ .platform_data = &bma2x2_pdata,
+};
+
+static int __init BMA2X2_init(void)
+{
+ return i2c_add_driver(&bma2x2_driver);
+}
+
+static void __exit BMA2X2_exit(void)
+{
+ i2c_del_driver(&bma2x2_driver);
+}
+
+MODULE_AUTHOR("contact@bosch-sensortec.com");
+MODULE_DESCRIPTION("BMA2X2 ACCELEROMETER SENSOR DRIVER");
+MODULE_LICENSE("GPL v2");
+
+//module_init(BMA2X2_init);
+late_initcall(BMA2X2_init);
+module_exit(BMA2X2_exit);
+
diff --git a/drivers/input/misc/bs_log.h b/drivers/input/misc/bs_log.h
new file mode 100644
index 00000000..0d3af001
--- /dev/null
+++ b/drivers/input/misc/bs_log.h
@@ -0,0 +1,167 @@
+/*!
+ * @section LICENSE
+ * $license$
+ *
+ * @filename $filename$
+ * @date $date$
+ * @id $id$
+ *
+ * @brief
+ * The head file of BOSCH SENSOR LOG
+*/
+
+#ifndef __BS_LOG_H
+#define __BS_LOG_H
+
+#include <linux/kernel.h>
+
+/*! @ trace functions
+ @{*/
+/*! ERROR LOG LEVEL */
+#define LOG_LEVEL_E 3
+/*! NOTICE LOG LEVEL */
+#define LOG_LEVEL_N 5
+/*! INFORMATION LOG LEVEL */
+#define LOG_LEVEL_I 6
+/*! DEBUG LOG LEVEL */
+#define LOG_LEVEL_D 7
+/*! DEBUG_FWDL LOG LEVEL */
+#define LOG_LEVEL_DF 10
+/*! DEBUG_DATA LOG LEVEL */
+#define LOG_LEVEL_DA 15
+/*! ALL LOG LEVEL */
+#define LOG_LEVEL_A 20
+
+#ifndef MODULE_TAG
+/*! MODULE TAG DEFINATION */
+#define MODULE_TAG "<BS_LOG>"
+#endif
+
+#ifndef LOG_LEVEL
+/*! LOG LEVEL DEFINATION */
+#define LOG_LEVEL LOG_LEVEL_I
+#endif
+
+#ifdef BOSCH_DRIVER_LOG_FUNC
+ #ifdef BSLOG_VAR_DEF
+ uint8_t debug_log_level = LOG_LEVEL;
+ #else
+ extern uint8_t debug_log_level;
+ #endif
+
+ /*! print error message */
+ #define PERR(fmt, args...) do\
+ {\
+ if (debug_log_level >= LOG_LEVEL_E)\
+ printk(KERN_INFO "\n" "[E]" KERN_ERR MODULE_TAG \
+ "<%s><%d>" fmt "\n", __func__, __LINE__, ##args);\
+ } while (0)
+
+ /*! print notice message */
+ #define PNOTICE(fmt, args...) do\
+ {\
+ if (debug_log_level >= LOG_LEVEL_N)\
+ printk(KERN_INFO "\n" "[N]" KERN_NOTICE MODULE_TAG \
+ "<%s><%d>" fmt "\n", __func__, __LINE__, ##args);\
+ } while (0)
+
+ /*! print information message */
+ #define PINFO(fmt, args...) do\
+ {\
+ if (debug_log_level >= LOG_LEVEL_I)\
+ printk(KERN_INFO "\n" "[I]" KERN_INFO MODULE_TAG \
+ "<%s><%d>" fmt "\n", __func__, __LINE__, ##args);\
+ } while (0)
+
+ /*! print debug message */
+ #define PDEBUG(fmt, args...) do\
+ {\
+ if (debug_log_level >= LOG_LEVEL_D)\
+ printk(KERN_INFO "\n" "[D]" KERN_DEBUG MODULE_TAG \
+ "<%s><%d>" fmt "\n", __func__, __LINE__, ##args);\
+ } while (0)
+
+ /*! print debug fw download message */
+ #define PDEBUG_FWDL(fmt, args...) do\
+ {\
+ if (debug_log_level >= LOG_LEVEL_DF)\
+ printk(KERN_INFO "\n" "[DF]" KERN_DEBUG MODULE_TAG \
+ "<%s><%d>" fmt "\n", __func__, __LINE__, ##args);\
+ } while (0)
+
+ /*! print debug data log message */
+ #define PDEBUG_DLOG(fmt, args...) do\
+ {\
+ if (debug_log_level >= LOG_LEVEL_DA)\
+ printk(KERN_INFO "\n" "[DA]" KERN_DEBUG MODULE_TAG \
+ "<%s><%d>" fmt "\n", __func__, __LINE__, ##args);\
+ } while (0)
+
+ void set_debug_log_level(uint8_t level);
+ uint8_t get_debug_log_level(void);
+
+#else
+
+ #if (LOG_LEVEL >= LOG_LEVEL_E)
+ /*! print error message */
+ #define PERR(fmt, args...) \
+ printk(KERN_INFO "\n" "[E]" KERN_ERR MODULE_TAG \
+ "<%s><%d>" fmt "\n", __func__, __LINE__, ##args)
+ #else
+ /*! invalid message */
+ #define PERR(fmt, args...)
+ #endif
+
+ #if (LOG_LEVEL >= LOG_LEVEL_N)
+ /*! print notice message */
+ #define PNOTICE(fmt, args...) \
+ printk(KERN_INFO "\n" "[N]" KERN_NOTICE MODULE_TAG \
+ "<%s><%d>" fmt "\n", __func__, __LINE__, ##args)
+ #else
+ /*! invalid message */
+ #define PNOTICE(fmt, args...)
+ #endif
+
+ #if (LOG_LEVEL >= LOG_LEVEL_I)
+ /*! print information message */
+ #define PINFO(fmt, args...) printk(KERN_INFO "\n" "[I]" KERN_INFO MODULE_TAG \
+ "<%s><%d>" fmt "\n", __func__, __LINE__, ##args)
+ #else
+ /*! invalid message */
+ #define PINFO(fmt, args...)
+ #endif
+
+ #if (LOG_LEVEL >= LOG_LEVEL_D)
+ /*! print debug message */
+ #define PDEBUG(fmt, args...) printk(KERN_INFO "\n" "[D]" KERN_DEBUG MODULE_TAG \
+ "<%s><%d>" fmt "\n", __func__, __LINE__, ##args)
+ #else
+ /*! invalid message */
+ #define PDEBUG(fmt, args...)
+ #endif
+
+ #if (LOG_LEVEL >= LOG_LEVEL_DF)
+ /*! print debug fw download message */
+ #define PDEBUG_FWDL(fmt, args...) printk(KERN_INFO "\n" "[DF]" KERN_DEBUG MODULE_TAG \
+ "<%s><%d>" fmt "\n", __func__, __LINE__, ##args)
+ #else
+ /*! invalid message */
+ #define PDEBUG_FWDL(fmt, args...)
+ #endif
+
+ #if (LOG_LEVEL >= LOG_LEVEL_DA)
+ /*! print debug data log message */
+ #define PDEBUG_DLOG(fmt, args...) printk(KERN_INFO "\n" "[DA]" KERN_DEBUG MODULE_TAG \
+ "<%s><%d>" fmt "\n", __func__, __LINE__, ##args)
+ #else
+ /*! invalid message */
+ #define PDEBUG_DLOG(fmt, args...)
+ #endif
+
+ #define set_debug_log_level(level) {}
+ #define get_debug_log_level() (LOG_LEVEL)
+
+#endif
+
+#endif/*__BS_LOG_H*/
+/*@}*/
diff --git a/drivers/input/misc/bstclass.c b/drivers/input/misc/bstclass.c
new file mode 100644
index 00000000..11c3c39d
--- /dev/null
+++ b/drivers/input/misc/bstclass.c
@@ -0,0 +1,234 @@
+/*!
+ * @section LICENSE
+ * $license$
+ *
+ * @filename $filename$
+ * @date $date$
+ * @id $id$
+ *
+ * @brief
+ * The core code of bst device driver
+*/
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/poll.h>
+#include <linux/mutex.h>
+#include <linux/rcupdate.h>
+#include <linux/compiler.h>
+#include <linux/compat.h>
+#include "bstclass.h"
+#include "bs_log.h"
+
+static LIST_HEAD(bst_dev_list);
+
+/*
+ * bst_mutex protects access to both bst_dev_list and input_handler_list.
+ * This also causes bst_[un]register_device and bst_[un]register_handler
+ * be mutually exclusive which simplifies locking in drivers implementing
+ * input handlers.
+ */
+static DEFINE_MUTEX(bst_mutex);
+
+
+static void bst_dev_release(struct device *device)
+{
+ struct bst_dev *dev = to_bst_dev(device);
+ if (NULL != dev)
+ kfree(dev);
+ module_put(THIS_MODULE);
+}
+
+
+#ifdef CONFIG_PM
+static int bst_dev_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int bst_dev_resume(struct device *dev)
+{
+ return 0;
+}
+
+static const struct dev_pm_ops bst_dev_pm_ops = {
+ .suspend = bst_dev_suspend,
+ .resume = bst_dev_resume,
+ .poweroff = bst_dev_suspend,
+ .restore = bst_dev_resume,
+};
+#endif /* CONFIG_PM */
+
+static const struct attribute_group *bst_dev_attr_groups[] = {
+ NULL
+};
+
+static struct device_type bst_dev_type = {
+ .groups = bst_dev_attr_groups,
+ .release = bst_dev_release,
+#ifdef CONFIG_PM
+ .pm = &bst_dev_pm_ops,
+#endif
+};
+
+
+
+static char *bst_devnode(struct device *dev, mode_t *mode)
+{
+ return kasprintf(GFP_KERNEL, "%s", dev_name(dev));
+}
+
+struct class bst_class = {
+ .name = "bst",
+ .owner = THIS_MODULE,
+ .devnode = bst_devnode,
+ .dev_release = bst_dev_release,
+};
+EXPORT_SYMBOL_GPL(bst_class);
+
+/**
+ * bst_allocate_device - allocate memory for new input device
+ *
+ * Returns prepared struct bst_dev or NULL.
+ *
+ * NOTE: Use bst_free_device() to free devices that have not been
+ * registered; bst_unregister_device() should be used for already
+ * registered devices.
+ */
+struct bst_dev *bst_allocate_device(void)
+{
+ struct bst_dev *dev;
+
+ dev = kzalloc(sizeof(struct bst_dev), GFP_KERNEL);
+ if (dev) {
+ dev->dev.type = &bst_dev_type;
+ dev->dev.class = &bst_class;
+ device_initialize(&dev->dev);
+ mutex_init(&dev->mutex);
+ INIT_LIST_HEAD(&dev->node);
+ __module_get(THIS_MODULE);
+ }
+ return dev;
+}
+EXPORT_SYMBOL(bst_allocate_device);
+
+
+
+/**
+ * bst_free_device - free memory occupied by bst_dev structure
+ * @dev: input device to free
+ *
+ * This function should only be used if bst_register_device()
+ * was not called yet or if it failed. Once device was registered
+ * use bst_unregister_device() and memory will be freed once last
+ * reference to the device is dropped.
+ *
+ * Device should be allocated by bst_allocate_device().
+ *
+ * NOTE: If there are references to the input device then memory
+ * will not be freed until last reference is dropped.
+ */
+void bst_free_device(struct bst_dev *dev)
+{
+ if (dev)
+ bst_put_device(dev);
+}
+EXPORT_SYMBOL(bst_free_device);
+
+/**
+ * bst_register_device - register device with input core
+ * @dev: device to be registered
+ *
+ * This function registers device with input core. The device must be
+ * allocated with bst_allocate_device() and all it's capabilities
+ * set up before registering.
+ * If function fails the device must be freed with bst_free_device().
+ * Once device has been successfully registered it can be unregistered
+ * with bst_unregister_device(); bst_free_device() should not be
+ * called in this case.
+ */
+int bst_register_device(struct bst_dev *dev)
+{
+ const char *path;
+ int error;
+
+
+ /*
+ * If delay and period are pre-set by the driver, then autorepeating
+ * is handled by the driver itself and we don't do it in input.c.
+ */
+ dev_set_name(&dev->dev, dev->name);
+
+ error = device_add(&dev->dev);
+ if (error)
+ return error;
+
+ path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
+ PINFO("%s as %s\n",
+ dev->name ? dev->name : "Unspecified device",
+ path ? path : "N/A");
+ kfree(path);
+ error = mutex_lock_interruptible(&bst_mutex);
+ if (error) {
+ device_del(&dev->dev);
+ return error;
+ }
+
+ list_add_tail(&dev->node, &bst_dev_list);
+
+ mutex_unlock(&bst_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(bst_register_device);
+
+/**
+ * bst_unregister_device - unregister previously registered device
+ * @dev: device to be unregistered
+ *
+ * This function unregisters an input device. Once device is unregistered
+ * the caller should not try to access it as it may get freed at any moment.
+ */
+void bst_unregister_device(struct bst_dev *dev)
+{
+ int ret = 0;
+ ret = mutex_lock_interruptible(&bst_mutex);
+ if(ret){
+ return;
+ }
+
+ list_del_init(&dev->node);
+ mutex_unlock(&bst_mutex);
+ device_unregister(&dev->dev);
+}
+EXPORT_SYMBOL(bst_unregister_device);
+
+static int __init bst_init(void)
+{
+ int err;
+ /*bst class register*/
+ err = class_register(&bst_class);
+ if (err) {
+ pr_err("unable to register bst_dev class\n");
+ return err;
+ }
+ return err;
+}
+
+static void __exit bst_exit(void)
+{
+ /*bst class*/
+ class_unregister(&bst_class);
+}
+
+/*subsys_initcall(bst_init);*/
+
+MODULE_AUTHOR("contact@bosch-sensortec.com");
+MODULE_DESCRIPTION("BST CLASS CORE");
+MODULE_LICENSE("GPL V2");
+
+module_init(bst_init);
+module_exit(bst_exit);
diff --git a/drivers/input/misc/bstclass.h b/drivers/input/misc/bstclass.h
new file mode 100644
index 00000000..2291cca0
--- /dev/null
+++ b/drivers/input/misc/bstclass.h
@@ -0,0 +1,74 @@
+/*!
+ * @section LICENSE
+ * $license$
+ *
+ * @filename $filename$
+ * @date $date$
+ * @id $id$
+ *
+ * @brief
+ * The core code of bst device driver
+*/
+
+#ifndef _BSTCLASS_H
+#define _BSTCLASS_H
+
+#ifdef __KERNEL__
+#include <linux/time.h>
+#include <linux/list.h>
+#else
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <linux/types.h>
+#endif
+
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/mod_devicetable.h>
+
+struct bst_dev {
+ const char *name;
+
+ int (*open)(struct bst_dev *dev);
+ void (*close)(struct bst_dev *dev);
+ struct mutex mutex;
+ struct device dev;
+ struct list_head node;
+};
+
+#define to_bst_dev(d) container_of(d, struct bst_dev, dev)
+
+struct bst_dev *bst_allocate_device(void);
+void bst_free_device(struct bst_dev *dev);
+
+static inline struct bst_dev *bst_get_device(struct bst_dev *dev)
+{
+ return dev ? to_bst_dev(get_device(&dev->dev)) : NULL;
+}
+
+static inline void bst_put_device(struct bst_dev *dev)
+{
+ if (dev)
+ put_device(&dev->dev);
+}
+
+static inline void *bst_get_drvdata(struct bst_dev *dev)
+{
+ return dev_get_drvdata(&dev->dev);
+}
+
+static inline void bst_set_drvdata(struct bst_dev *dev, void *data)
+{
+ dev_set_drvdata(&dev->dev, data);
+}
+
+int __must_check bst_register_device(struct bst_dev *);
+void bst_unregister_device(struct bst_dev *);
+
+void bst_reset_device(struct bst_dev *);
+
+
+extern struct class bst_class;
+
+#endif
diff --git a/drivers/input/misc/cm109.c b/drivers/input/misc/cm109.c
new file mode 100644
index 00000000..082684e7
--- /dev/null
+++ b/drivers/input/misc/cm109.c
@@ -0,0 +1,918 @@
+/*
+ * Driver for the VoIP USB phones with CM109 chipsets.
+ *
+ * Copyright (C) 2007 - 2008 Alfred E. Heggestad <aeh@db.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ */
+
+/*
+ * Tested devices:
+ * - Komunikate KIP1000
+ * - Genius G-talk
+ * - Allied-Telesis Corega USBPH01
+ * - ...
+ *
+ * This driver is based on the yealink.c driver
+ *
+ * Thanks to:
+ * - Authors of yealink.c
+ * - Thomas Reitmayr
+ * - Oliver Neukum for good review comments and code
+ * - Shaun Jackman <sjackman@gmail.com> for Genius G-talk keymap
+ * - Dmitry Torokhov for valuable input and review
+ *
+ * Todo:
+ * - Read/write EEPROM
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/rwsem.h>
+#include <linux/usb/input.h>
+
+#define DRIVER_VERSION "20080805"
+#define DRIVER_AUTHOR "Alfred E. Heggestad"
+#define DRIVER_DESC "CM109 phone driver"
+
+static char *phone = "kip1000";
+module_param(phone, charp, S_IRUSR);
+MODULE_PARM_DESC(phone, "Phone name {kip1000, gtalk, usbph01, atcom}");
+
+enum {
+ /* HID Registers */
+ HID_IR0 = 0x00, /* Record/Playback-mute button, Volume up/down */
+ HID_IR1 = 0x01, /* GPI, generic registers or EEPROM_DATA0 */
+ HID_IR2 = 0x02, /* Generic registers or EEPROM_DATA1 */
+ HID_IR3 = 0x03, /* Generic registers or EEPROM_CTRL */
+ HID_OR0 = 0x00, /* Mapping control, buzzer, SPDIF (offset 0x04) */
+ HID_OR1 = 0x01, /* GPO - General Purpose Output */
+ HID_OR2 = 0x02, /* Set GPIO to input/output mode */
+ HID_OR3 = 0x03, /* SPDIF status channel or EEPROM_CTRL */
+
+ /* HID_IR0 */
+ RECORD_MUTE = 1 << 3,
+ PLAYBACK_MUTE = 1 << 2,
+ VOLUME_DOWN = 1 << 1,
+ VOLUME_UP = 1 << 0,
+
+ /* HID_OR0 */
+ /* bits 7-6
+ 0: HID_OR1-2 are used for GPO; HID_OR0, 3 are used for buzzer
+ and SPDIF
+ 1: HID_OR0-3 are used as generic HID registers
+ 2: Values written to HID_OR0-3 are also mapped to MCU_CTRL,
+ EEPROM_DATA0-1, EEPROM_CTRL (see Note)
+ 3: Reserved
+ */
+ HID_OR_GPO_BUZ_SPDIF = 0 << 6,
+ HID_OR_GENERIC_HID_REG = 1 << 6,
+ HID_OR_MAP_MCU_EEPROM = 2 << 6,
+
+ BUZZER_ON = 1 << 5,
+
+ /* up to 256 normal keys, up to 16 special keys */
+ KEYMAP_SIZE = 256 + 16,
+};
+
+/* CM109 protocol packet */
+struct cm109_ctl_packet {
+ u8 byte[4];
+} __attribute__ ((packed));
+
+enum { USB_PKT_LEN = sizeof(struct cm109_ctl_packet) };
+
+/* CM109 device structure */
+struct cm109_dev {
+ struct input_dev *idev; /* input device */
+ struct usb_device *udev; /* usb device */
+ struct usb_interface *intf;
+
+ /* irq input channel */
+ struct cm109_ctl_packet *irq_data;
+ dma_addr_t irq_dma;
+ struct urb *urb_irq;
+
+ /* control output channel */
+ struct cm109_ctl_packet *ctl_data;
+ dma_addr_t ctl_dma;
+ struct usb_ctrlrequest *ctl_req;
+ struct urb *urb_ctl;
+ /*
+ * The 3 bitfields below are protected by ctl_submit_lock.
+ * They have to be separate since they are accessed from IRQ
+ * context.
+ */
+ unsigned irq_urb_pending:1; /* irq_urb is in flight */
+ unsigned ctl_urb_pending:1; /* ctl_urb is in flight */
+ unsigned buzzer_pending:1; /* need to issue buzz command */
+ spinlock_t ctl_submit_lock;
+
+ unsigned char buzzer_state; /* on/off */
+
+ /* flags */
+ unsigned open:1;
+ unsigned resetting:1;
+ unsigned shutdown:1;
+
+ /* This mutex protects writes to the above flags */
+ struct mutex pm_mutex;
+
+ unsigned short keymap[KEYMAP_SIZE];
+
+ char phys[64]; /* physical device path */
+ int key_code; /* last reported key */
+ int keybit; /* 0=new scan 1,2,4,8=scan columns */
+ u8 gpi; /* Cached value of GPI (high nibble) */
+};
+
+/******************************************************************************
+ * CM109 key interface
+ *****************************************************************************/
+
+static unsigned short special_keymap(int code)
+{
+ if (code > 0xff) {
+ switch (code - 0xff) {
+ case RECORD_MUTE: return KEY_MUTE;
+ case PLAYBACK_MUTE: return KEY_MUTE;
+ case VOLUME_DOWN: return KEY_VOLUMEDOWN;
+ case VOLUME_UP: return KEY_VOLUMEUP;
+ }
+ }
+ return KEY_RESERVED;
+}
+
+/* Map device buttons to internal key events.
+ *
+ * The "up" and "down" keys, are symbolised by arrows on the button.
+ * The "pickup" and "hangup" keys are symbolised by a green and red phone
+ * on the button.
+
+ Komunikate KIP1000 Keyboard Matrix
+
+ -> -- 1 -- 2 -- 3 --> GPI pin 4 (0x10)
+ | | | |
+ <- -- 4 -- 5 -- 6 --> GPI pin 5 (0x20)
+ | | | |
+ END - 7 -- 8 -- 9 --> GPI pin 6 (0x40)
+ | | | |
+ OK -- * -- 0 -- # --> GPI pin 7 (0x80)
+ | | | |
+
+ /|\ /|\ /|\ /|\
+ | | | |
+GPO
+pin: 3 2 1 0
+ 0x8 0x4 0x2 0x1
+
+ */
+static unsigned short keymap_kip1000(int scancode)
+{
+ switch (scancode) { /* phone key: */
+ case 0x82: return KEY_NUMERIC_0; /* 0 */
+ case 0x14: return KEY_NUMERIC_1; /* 1 */
+ case 0x12: return KEY_NUMERIC_2; /* 2 */
+ case 0x11: return KEY_NUMERIC_3; /* 3 */
+ case 0x24: return KEY_NUMERIC_4; /* 4 */
+ case 0x22: return KEY_NUMERIC_5; /* 5 */
+ case 0x21: return KEY_NUMERIC_6; /* 6 */
+ case 0x44: return KEY_NUMERIC_7; /* 7 */
+ case 0x42: return KEY_NUMERIC_8; /* 8 */
+ case 0x41: return KEY_NUMERIC_9; /* 9 */
+ case 0x81: return KEY_NUMERIC_POUND; /* # */
+ case 0x84: return KEY_NUMERIC_STAR; /* * */
+ case 0x88: return KEY_ENTER; /* pickup */
+ case 0x48: return KEY_ESC; /* hangup */
+ case 0x28: return KEY_LEFT; /* IN */
+ case 0x18: return KEY_RIGHT; /* OUT */
+ default: return special_keymap(scancode);
+ }
+}
+
+/*
+ Contributed by Shaun Jackman <sjackman@gmail.com>
+
+ Genius G-Talk keyboard matrix
+ 0 1 2 3
+ 4: 0 4 8 Talk
+ 5: 1 5 9 End
+ 6: 2 6 # Up
+ 7: 3 7 * Down
+*/
+static unsigned short keymap_gtalk(int scancode)
+{
+ switch (scancode) {
+ case 0x11: return KEY_NUMERIC_0;
+ case 0x21: return KEY_NUMERIC_1;
+ case 0x41: return KEY_NUMERIC_2;
+ case 0x81: return KEY_NUMERIC_3;
+ case 0x12: return KEY_NUMERIC_4;
+ case 0x22: return KEY_NUMERIC_5;
+ case 0x42: return KEY_NUMERIC_6;
+ case 0x82: return KEY_NUMERIC_7;
+ case 0x14: return KEY_NUMERIC_8;
+ case 0x24: return KEY_NUMERIC_9;
+ case 0x44: return KEY_NUMERIC_POUND; /* # */
+ case 0x84: return KEY_NUMERIC_STAR; /* * */
+ case 0x18: return KEY_ENTER; /* Talk (green handset) */
+ case 0x28: return KEY_ESC; /* End (red handset) */
+ case 0x48: return KEY_UP; /* Menu up (rocker switch) */
+ case 0x88: return KEY_DOWN; /* Menu down (rocker switch) */
+ default: return special_keymap(scancode);
+ }
+}
+
+/*
+ * Keymap for Allied-Telesis Corega USBPH01
+ * http://www.alliedtelesis-corega.com/2/1344/1437/1360/chprd.html
+ *
+ * Contributed by july@nat.bg
+ */
+static unsigned short keymap_usbph01(int scancode)
+{
+ switch (scancode) {
+ case 0x11: return KEY_NUMERIC_0; /* 0 */
+ case 0x21: return KEY_NUMERIC_1; /* 1 */
+ case 0x41: return KEY_NUMERIC_2; /* 2 */
+ case 0x81: return KEY_NUMERIC_3; /* 3 */
+ case 0x12: return KEY_NUMERIC_4; /* 4 */
+ case 0x22: return KEY_NUMERIC_5; /* 5 */
+ case 0x42: return KEY_NUMERIC_6; /* 6 */
+ case 0x82: return KEY_NUMERIC_7; /* 7 */
+ case 0x14: return KEY_NUMERIC_8; /* 8 */
+ case 0x24: return KEY_NUMERIC_9; /* 9 */
+ case 0x44: return KEY_NUMERIC_POUND; /* # */
+ case 0x84: return KEY_NUMERIC_STAR; /* * */
+ case 0x18: return KEY_ENTER; /* pickup */
+ case 0x28: return KEY_ESC; /* hangup */
+ case 0x48: return KEY_LEFT; /* IN */
+ case 0x88: return KEY_RIGHT; /* OUT */
+ default: return special_keymap(scancode);
+ }
+}
+
+/*
+ * Keymap for ATCom AU-100
+ * http://www.atcom.cn/products.html
+ * http://www.packetizer.com/products/au100/
+ * http://www.voip-info.org/wiki/view/AU-100
+ *
+ * Contributed by daniel@gimpelevich.san-francisco.ca.us
+ */
+static unsigned short keymap_atcom(int scancode)
+{
+ switch (scancode) { /* phone key: */
+ case 0x82: return KEY_NUMERIC_0; /* 0 */
+ case 0x11: return KEY_NUMERIC_1; /* 1 */
+ case 0x12: return KEY_NUMERIC_2; /* 2 */
+ case 0x14: return KEY_NUMERIC_3; /* 3 */
+ case 0x21: return KEY_NUMERIC_4; /* 4 */
+ case 0x22: return KEY_NUMERIC_5; /* 5 */
+ case 0x24: return KEY_NUMERIC_6; /* 6 */
+ case 0x41: return KEY_NUMERIC_7; /* 7 */
+ case 0x42: return KEY_NUMERIC_8; /* 8 */
+ case 0x44: return KEY_NUMERIC_9; /* 9 */
+ case 0x84: return KEY_NUMERIC_POUND; /* # */
+ case 0x81: return KEY_NUMERIC_STAR; /* * */
+ case 0x18: return KEY_ENTER; /* pickup */
+ case 0x28: return KEY_ESC; /* hangup */
+ case 0x48: return KEY_LEFT; /* left arrow */
+ case 0x88: return KEY_RIGHT; /* right arrow */
+ default: return special_keymap(scancode);
+ }
+}
+
+static unsigned short (*keymap)(int) = keymap_kip1000;
+
+/*
+ * Completes a request by converting the data into events for the
+ * input subsystem.
+ */
+static void report_key(struct cm109_dev *dev, int key)
+{
+ struct input_dev *idev = dev->idev;
+
+ if (dev->key_code >= 0) {
+ /* old key up */
+ input_report_key(idev, dev->key_code, 0);
+ }
+
+ dev->key_code = key;
+ if (key >= 0) {
+ /* new valid key */
+ input_report_key(idev, key, 1);
+ }
+
+ input_sync(idev);
+}
+
+/******************************************************************************
+ * CM109 usb communication interface
+ *****************************************************************************/
+
+static void cm109_submit_buzz_toggle(struct cm109_dev *dev)
+{
+ int error;
+
+ if (dev->buzzer_state)
+ dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+ else
+ dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+ error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+ if (error)
+ dev_err(&dev->intf->dev,
+ "%s: usb_submit_urb (urb_ctl) failed %d\n",
+ __func__, error);
+}
+
+/*
+ * IRQ handler
+ */
+static void cm109_urb_irq_callback(struct urb *urb)
+{
+ struct cm109_dev *dev = urb->context;
+ const int status = urb->status;
+ int error;
+
+ dev_dbg(&dev->intf->dev, "### URB IRQ: [0x%02x 0x%02x 0x%02x 0x%02x] keybit=0x%02x\n",
+ dev->irq_data->byte[0],
+ dev->irq_data->byte[1],
+ dev->irq_data->byte[2],
+ dev->irq_data->byte[3],
+ dev->keybit);
+
+ if (status) {
+ if (status == -ESHUTDOWN)
+ return;
+ dev_err(&dev->intf->dev, "%s: urb status %d\n", __func__, status);
+ }
+
+ /* Special keys */
+ if (dev->irq_data->byte[HID_IR0] & 0x0f) {
+ const int code = (dev->irq_data->byte[HID_IR0] & 0x0f);
+ report_key(dev, dev->keymap[0xff + code]);
+ }
+
+ /* Scan key column */
+ if (dev->keybit == 0xf) {
+
+ /* Any changes ? */
+ if ((dev->gpi & 0xf0) == (dev->irq_data->byte[HID_IR1] & 0xf0))
+ goto out;
+
+ dev->gpi = dev->irq_data->byte[HID_IR1] & 0xf0;
+ dev->keybit = 0x1;
+ } else {
+ report_key(dev, dev->keymap[dev->irq_data->byte[HID_IR1]]);
+
+ dev->keybit <<= 1;
+ if (dev->keybit > 0x8)
+ dev->keybit = 0xf;
+ }
+
+ out:
+
+ spin_lock(&dev->ctl_submit_lock);
+
+ dev->irq_urb_pending = 0;
+
+ if (likely(!dev->shutdown)) {
+
+ if (dev->buzzer_state)
+ dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+ else
+ dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+
+ dev->buzzer_pending = 0;
+ dev->ctl_urb_pending = 1;
+
+ error = usb_submit_urb(dev->urb_ctl, GFP_ATOMIC);
+ if (error)
+ dev_err(&dev->intf->dev,
+ "%s: usb_submit_urb (urb_ctl) failed %d\n",
+ __func__, error);
+ }
+
+ spin_unlock(&dev->ctl_submit_lock);
+}
+
+static void cm109_urb_ctl_callback(struct urb *urb)
+{
+ struct cm109_dev *dev = urb->context;
+ const int status = urb->status;
+ int error;
+
+ dev_dbg(&dev->intf->dev, "### URB CTL: [0x%02x 0x%02x 0x%02x 0x%02x]\n",
+ dev->ctl_data->byte[0],
+ dev->ctl_data->byte[1],
+ dev->ctl_data->byte[2],
+ dev->ctl_data->byte[3]);
+
+ if (status)
+ dev_err(&dev->intf->dev, "%s: urb status %d\n", __func__, status);
+
+ spin_lock(&dev->ctl_submit_lock);
+
+ dev->ctl_urb_pending = 0;
+
+ if (likely(!dev->shutdown)) {
+
+ if (dev->buzzer_pending) {
+ dev->buzzer_pending = 0;
+ dev->ctl_urb_pending = 1;
+ cm109_submit_buzz_toggle(dev);
+ } else if (likely(!dev->irq_urb_pending)) {
+ /* ask for key data */
+ dev->irq_urb_pending = 1;
+ error = usb_submit_urb(dev->urb_irq, GFP_ATOMIC);
+ if (error)
+ dev_err(&dev->intf->dev,
+ "%s: usb_submit_urb (urb_irq) failed %d\n",
+ __func__, error);
+ }
+ }
+
+ spin_unlock(&dev->ctl_submit_lock);
+}
+
+static void cm109_toggle_buzzer_async(struct cm109_dev *dev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->ctl_submit_lock, flags);
+
+ if (dev->ctl_urb_pending) {
+ /* URB completion will resubmit */
+ dev->buzzer_pending = 1;
+ } else {
+ dev->ctl_urb_pending = 1;
+ cm109_submit_buzz_toggle(dev);
+ }
+
+ spin_unlock_irqrestore(&dev->ctl_submit_lock, flags);
+}
+
+static void cm109_toggle_buzzer_sync(struct cm109_dev *dev, int on)
+{
+ int error;
+
+ if (on)
+ dev->ctl_data->byte[HID_OR0] |= BUZZER_ON;
+ else
+ dev->ctl_data->byte[HID_OR0] &= ~BUZZER_ON;
+
+ error = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ dev->ctl_req->bRequest,
+ dev->ctl_req->bRequestType,
+ le16_to_cpu(dev->ctl_req->wValue),
+ le16_to_cpu(dev->ctl_req->wIndex),
+ dev->ctl_data,
+ USB_PKT_LEN, USB_CTRL_SET_TIMEOUT);
+ if (error < 0 && error != -EINTR)
+ dev_err(&dev->intf->dev, "%s: usb_control_msg() failed %d\n",
+ __func__, error);
+}
+
+static void cm109_stop_traffic(struct cm109_dev *dev)
+{
+ dev->shutdown = 1;
+ /*
+ * Make sure other CPUs see this
+ */
+ smp_wmb();
+
+ usb_kill_urb(dev->urb_ctl);
+ usb_kill_urb(dev->urb_irq);
+
+ cm109_toggle_buzzer_sync(dev, 0);
+
+ dev->shutdown = 0;
+ smp_wmb();
+}
+
+static void cm109_restore_state(struct cm109_dev *dev)
+{
+ if (dev->open) {
+ /*
+ * Restore buzzer state.
+ * This will also kick regular URB submission
+ */
+ cm109_toggle_buzzer_async(dev);
+ }
+}
+
+/******************************************************************************
+ * input event interface
+ *****************************************************************************/
+
+static int cm109_input_open(struct input_dev *idev)
+{
+ struct cm109_dev *dev = input_get_drvdata(idev);
+ int error;
+
+ error = usb_autopm_get_interface(dev->intf);
+ if (error < 0) {
+ dev_err(&idev->dev, "%s - cannot autoresume, result %d\n",
+ __func__, error);
+ return error;
+ }
+
+ mutex_lock(&dev->pm_mutex);
+
+ dev->buzzer_state = 0;
+ dev->key_code = -1; /* no keys pressed */
+ dev->keybit = 0xf;
+
+ /* issue INIT */
+ dev->ctl_data->byte[HID_OR0] = HID_OR_GPO_BUZ_SPDIF;
+ dev->ctl_data->byte[HID_OR1] = dev->keybit;
+ dev->ctl_data->byte[HID_OR2] = dev->keybit;
+ dev->ctl_data->byte[HID_OR3] = 0x00;
+
+ error = usb_submit_urb(dev->urb_ctl, GFP_KERNEL);
+ if (error)
+ dev_err(&dev->intf->dev, "%s: usb_submit_urb (urb_ctl) failed %d\n",
+ __func__, error);
+ else
+ dev->open = 1;
+
+ mutex_unlock(&dev->pm_mutex);
+
+ if (error)
+ usb_autopm_put_interface(dev->intf);
+
+ return error;
+}
+
+static void cm109_input_close(struct input_dev *idev)
+{
+ struct cm109_dev *dev = input_get_drvdata(idev);
+
+ mutex_lock(&dev->pm_mutex);
+
+ /*
+ * Once we are here event delivery is stopped so we
+ * don't need to worry about someone starting buzzer
+ * again
+ */
+ cm109_stop_traffic(dev);
+ dev->open = 0;
+
+ mutex_unlock(&dev->pm_mutex);
+
+ usb_autopm_put_interface(dev->intf);
+}
+
+static int cm109_input_ev(struct input_dev *idev, unsigned int type,
+ unsigned int code, int value)
+{
+ struct cm109_dev *dev = input_get_drvdata(idev);
+
+ dev_dbg(&dev->intf->dev,
+ "input_ev: type=%u code=%u value=%d\n", type, code, value);
+
+ if (type != EV_SND)
+ return -EINVAL;
+
+ switch (code) {
+ case SND_TONE:
+ case SND_BELL:
+ dev->buzzer_state = !!value;
+ if (!dev->resetting)
+ cm109_toggle_buzzer_async(dev);
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/******************************************************************************
+ * Linux interface and usb initialisation
+ *****************************************************************************/
+
+struct driver_info {
+ char *name;
+};
+
+static const struct driver_info info_cm109 = {
+ .name = "CM109 USB driver",
+};
+
+enum {
+ VENDOR_ID = 0x0d8c, /* C-Media Electronics */
+ PRODUCT_ID_CM109 = 0x000e, /* CM109 defines range 0x0008 - 0x000f */
+};
+
+/* table of devices that work with this driver */
+static const struct usb_device_id cm109_usb_table[] = {
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+ USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = VENDOR_ID,
+ .idProduct = PRODUCT_ID_CM109,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .driver_info = (kernel_ulong_t) &info_cm109
+ },
+ /* you can add more devices here with product ID 0x0008 - 0x000f */
+ { }
+};
+
+static void cm109_usb_cleanup(struct cm109_dev *dev)
+{
+ kfree(dev->ctl_req);
+ if (dev->ctl_data)
+ usb_free_coherent(dev->udev, USB_PKT_LEN,
+ dev->ctl_data, dev->ctl_dma);
+ if (dev->irq_data)
+ usb_free_coherent(dev->udev, USB_PKT_LEN,
+ dev->irq_data, dev->irq_dma);
+
+ usb_free_urb(dev->urb_irq); /* parameter validation in core/urb */
+ usb_free_urb(dev->urb_ctl); /* parameter validation in core/urb */
+ kfree(dev);
+}
+
+static void cm109_usb_disconnect(struct usb_interface *interface)
+{
+ struct cm109_dev *dev = usb_get_intfdata(interface);
+
+ usb_set_intfdata(interface, NULL);
+ input_unregister_device(dev->idev);
+ cm109_usb_cleanup(dev);
+}
+
+static int cm109_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct driver_info *nfo = (struct driver_info *)id->driver_info;
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct cm109_dev *dev;
+ struct input_dev *input_dev = NULL;
+ int ret, pipe, i;
+ int error = -ENOMEM;
+
+ interface = intf->cur_altsetting;
+ endpoint = &interface->endpoint[0].desc;
+
+ if (!usb_endpoint_is_int_in(endpoint))
+ return -ENODEV;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ spin_lock_init(&dev->ctl_submit_lock);
+ mutex_init(&dev->pm_mutex);
+
+ dev->udev = udev;
+ dev->intf = intf;
+
+ dev->idev = input_dev = input_allocate_device();
+ if (!input_dev)
+ goto err_out;
+
+ /* allocate usb buffers */
+ dev->irq_data = usb_alloc_coherent(udev, USB_PKT_LEN,
+ GFP_KERNEL, &dev->irq_dma);
+ if (!dev->irq_data)
+ goto err_out;
+
+ dev->ctl_data = usb_alloc_coherent(udev, USB_PKT_LEN,
+ GFP_KERNEL, &dev->ctl_dma);
+ if (!dev->ctl_data)
+ goto err_out;
+
+ dev->ctl_req = kmalloc(sizeof(*(dev->ctl_req)), GFP_KERNEL);
+ if (!dev->ctl_req)
+ goto err_out;
+
+ /* allocate urb structures */
+ dev->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->urb_irq)
+ goto err_out;
+
+ dev->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->urb_ctl)
+ goto err_out;
+
+ /* get a handle to the interrupt data pipe */
+ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+ ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ if (ret != USB_PKT_LEN)
+ dev_err(&intf->dev, "invalid payload size %d, expected %d\n",
+ ret, USB_PKT_LEN);
+
+ /* initialise irq urb */
+ usb_fill_int_urb(dev->urb_irq, udev, pipe, dev->irq_data,
+ USB_PKT_LEN,
+ cm109_urb_irq_callback, dev, endpoint->bInterval);
+ dev->urb_irq->transfer_dma = dev->irq_dma;
+ dev->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ dev->urb_irq->dev = udev;
+
+ /* initialise ctl urb */
+ dev->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+ USB_DIR_OUT;
+ dev->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
+ dev->ctl_req->wValue = cpu_to_le16(0x200);
+ dev->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
+ dev->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
+
+ usb_fill_control_urb(dev->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
+ (void *)dev->ctl_req, dev->ctl_data, USB_PKT_LEN,
+ cm109_urb_ctl_callback, dev);
+ dev->urb_ctl->transfer_dma = dev->ctl_dma;
+ dev->urb_ctl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ dev->urb_ctl->dev = udev;
+
+ /* find out the physical bus location */
+ usb_make_path(udev, dev->phys, sizeof(dev->phys));
+ strlcat(dev->phys, "/input0", sizeof(dev->phys));
+
+ /* register settings for the input device */
+ input_dev->name = nfo->name;
+ input_dev->phys = dev->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, dev);
+ input_dev->open = cm109_input_open;
+ input_dev->close = cm109_input_close;
+ input_dev->event = cm109_input_ev;
+
+ input_dev->keycode = dev->keymap;
+ input_dev->keycodesize = sizeof(unsigned char);
+ input_dev->keycodemax = ARRAY_SIZE(dev->keymap);
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_SND);
+ input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+
+ /* register available key events */
+ for (i = 0; i < KEYMAP_SIZE; i++) {
+ unsigned short k = keymap(i);
+ dev->keymap[i] = k;
+ __set_bit(k, input_dev->keybit);
+ }
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
+
+ error = input_register_device(dev->idev);
+ if (error)
+ goto err_out;
+
+ usb_set_intfdata(intf, dev);
+
+ return 0;
+
+ err_out:
+ input_free_device(input_dev);
+ cm109_usb_cleanup(dev);
+ return error;
+}
+
+static int cm109_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct cm109_dev *dev = usb_get_intfdata(intf);
+
+ dev_info(&intf->dev, "cm109: usb_suspend (event=%d)\n", message.event);
+
+ mutex_lock(&dev->pm_mutex);
+ cm109_stop_traffic(dev);
+ mutex_unlock(&dev->pm_mutex);
+
+ return 0;
+}
+
+static int cm109_usb_resume(struct usb_interface *intf)
+{
+ struct cm109_dev *dev = usb_get_intfdata(intf);
+
+ dev_info(&intf->dev, "cm109: usb_resume\n");
+
+ mutex_lock(&dev->pm_mutex);
+ cm109_restore_state(dev);
+ mutex_unlock(&dev->pm_mutex);
+
+ return 0;
+}
+
+static int cm109_usb_pre_reset(struct usb_interface *intf)
+{
+ struct cm109_dev *dev = usb_get_intfdata(intf);
+
+ mutex_lock(&dev->pm_mutex);
+
+ /*
+ * Make sure input events don't try to toggle buzzer
+ * while we are resetting
+ */
+ dev->resetting = 1;
+ smp_wmb();
+
+ cm109_stop_traffic(dev);
+
+ return 0;
+}
+
+static int cm109_usb_post_reset(struct usb_interface *intf)
+{
+ struct cm109_dev *dev = usb_get_intfdata(intf);
+
+ dev->resetting = 0;
+ smp_wmb();
+
+ cm109_restore_state(dev);
+
+ mutex_unlock(&dev->pm_mutex);
+
+ return 0;
+}
+
+static struct usb_driver cm109_driver = {
+ .name = "cm109",
+ .probe = cm109_usb_probe,
+ .disconnect = cm109_usb_disconnect,
+ .suspend = cm109_usb_suspend,
+ .resume = cm109_usb_resume,
+ .reset_resume = cm109_usb_resume,
+ .pre_reset = cm109_usb_pre_reset,
+ .post_reset = cm109_usb_post_reset,
+ .id_table = cm109_usb_table,
+ .supports_autosuspend = 1,
+};
+
+static int __init cm109_select_keymap(void)
+{
+ /* Load the phone keymap */
+ if (!strcasecmp(phone, "kip1000")) {
+ keymap = keymap_kip1000;
+ printk(KERN_INFO KBUILD_MODNAME ": "
+ "Keymap for Komunikate KIP1000 phone loaded\n");
+ } else if (!strcasecmp(phone, "gtalk")) {
+ keymap = keymap_gtalk;
+ printk(KERN_INFO KBUILD_MODNAME ": "
+ "Keymap for Genius G-talk phone loaded\n");
+ } else if (!strcasecmp(phone, "usbph01")) {
+ keymap = keymap_usbph01;
+ printk(KERN_INFO KBUILD_MODNAME ": "
+ "Keymap for Allied-Telesis Corega USBPH01 phone loaded\n");
+ } else if (!strcasecmp(phone, "atcom")) {
+ keymap = keymap_atcom;
+ printk(KERN_INFO KBUILD_MODNAME ": "
+ "Keymap for ATCom AU-100 phone loaded\n");
+ } else {
+ printk(KERN_ERR KBUILD_MODNAME ": "
+ "Unsupported phone: %s\n", phone);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int __init cm109_init(void)
+{
+ int err;
+
+ err = cm109_select_keymap();
+ if (err)
+ return err;
+
+ err = usb_register(&cm109_driver);
+ if (err)
+ return err;
+
+ printk(KERN_INFO KBUILD_MODNAME ": "
+ DRIVER_DESC ": " DRIVER_VERSION " (C) " DRIVER_AUTHOR "\n");
+
+ return 0;
+}
+
+static void __exit cm109_exit(void)
+{
+ usb_deregister(&cm109_driver);
+}
+
+module_init(cm109_init);
+module_exit(cm109_exit);
+
+MODULE_DEVICE_TABLE(usb, cm109_usb_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/cma3000_d0x.c b/drivers/input/misc/cma3000_d0x.c
new file mode 100644
index 00000000..df9b7565
--- /dev/null
+++ b/drivers/input/misc/cma3000_d0x.c
@@ -0,0 +1,399 @@
+/*
+ * VTI CMA3000_D0x Accelerometer driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Hemanth V <hemanthv@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/cma3000.h>
+#include <linux/module.h>
+
+#include "cma3000_d0x.h"
+
+#define CMA3000_WHOAMI 0x00
+#define CMA3000_REVID 0x01
+#define CMA3000_CTRL 0x02
+#define CMA3000_STATUS 0x03
+#define CMA3000_RSTR 0x04
+#define CMA3000_INTSTATUS 0x05
+#define CMA3000_DOUTX 0x06
+#define CMA3000_DOUTY 0x07
+#define CMA3000_DOUTZ 0x08
+#define CMA3000_MDTHR 0x09
+#define CMA3000_MDFFTMR 0x0A
+#define CMA3000_FFTHR 0x0B
+
+#define CMA3000_RANGE2G (1 << 7)
+#define CMA3000_RANGE8G (0 << 7)
+#define CMA3000_BUSI2C (0 << 4)
+#define CMA3000_MODEMASK (7 << 1)
+#define CMA3000_GRANGEMASK (1 << 7)
+
+#define CMA3000_STATUS_PERR 1
+#define CMA3000_INTSTATUS_FFDET (1 << 2)
+
+/* Settling time delay in ms */
+#define CMA3000_SETDELAY 30
+
+/* Delay for clearing interrupt in us */
+#define CMA3000_INTDELAY 44
+
+
+/*
+ * Bit weights in mg for bit 0, other bits need
+ * multiply factor 2^n. Eight bit is the sign bit.
+ */
+#define BIT_TO_2G 18
+#define BIT_TO_8G 71
+
+struct cma3000_accl_data {
+ const struct cma3000_bus_ops *bus_ops;
+ const struct cma3000_platform_data *pdata;
+
+ struct device *dev;
+ struct input_dev *input_dev;
+
+ int bit_to_mg;
+ int irq;
+
+ int g_range;
+ u8 mode;
+
+ struct mutex mutex;
+ bool opened;
+ bool suspended;
+};
+
+#define CMA3000_READ(data, reg, msg) \
+ (data->bus_ops->read(data->dev, reg, msg))
+#define CMA3000_SET(data, reg, val, msg) \
+ ((data)->bus_ops->write(data->dev, reg, val, msg))
+
+/*
+ * Conversion for each of the eight modes to g, depending
+ * on G range i.e 2G or 8G. Some modes always operate in
+ * 8G.
+ */
+
+static int mode_to_mg[8][2] = {
+ { 0, 0 },
+ { BIT_TO_8G, BIT_TO_2G },
+ { BIT_TO_8G, BIT_TO_2G },
+ { BIT_TO_8G, BIT_TO_8G },
+ { BIT_TO_8G, BIT_TO_8G },
+ { BIT_TO_8G, BIT_TO_2G },
+ { BIT_TO_8G, BIT_TO_2G },
+ { 0, 0},
+};
+
+static void decode_mg(struct cma3000_accl_data *data, int *datax,
+ int *datay, int *dataz)
+{
+ /* Data in 2's complement, convert to mg */
+ *datax = ((s8)*datax) * data->bit_to_mg;
+ *datay = ((s8)*datay) * data->bit_to_mg;
+ *dataz = ((s8)*dataz) * data->bit_to_mg;
+}
+
+static irqreturn_t cma3000_thread_irq(int irq, void *dev_id)
+{
+ struct cma3000_accl_data *data = dev_id;
+ int datax, datay, dataz, intr_status;
+ u8 ctrl, mode, range;
+
+ intr_status = CMA3000_READ(data, CMA3000_INTSTATUS, "interrupt status");
+ if (intr_status < 0)
+ return IRQ_NONE;
+
+ /* Check if free fall is detected, report immediately */
+ if (intr_status & CMA3000_INTSTATUS_FFDET) {
+ input_report_abs(data->input_dev, ABS_MISC, 1);
+ input_sync(data->input_dev);
+ } else {
+ input_report_abs(data->input_dev, ABS_MISC, 0);
+ }
+
+ datax = CMA3000_READ(data, CMA3000_DOUTX, "X");
+ datay = CMA3000_READ(data, CMA3000_DOUTY, "Y");
+ dataz = CMA3000_READ(data, CMA3000_DOUTZ, "Z");
+
+ ctrl = CMA3000_READ(data, CMA3000_CTRL, "ctrl");
+ mode = (ctrl & CMA3000_MODEMASK) >> 1;
+ range = (ctrl & CMA3000_GRANGEMASK) >> 7;
+
+ data->bit_to_mg = mode_to_mg[mode][range];
+
+ /* Interrupt not for this device */
+ if (data->bit_to_mg == 0)
+ return IRQ_NONE;
+
+ /* Decode register values to milli g */
+ decode_mg(data, &datax, &datay, &dataz);
+
+ input_report_abs(data->input_dev, ABS_X, datax);
+ input_report_abs(data->input_dev, ABS_Y, datay);
+ input_report_abs(data->input_dev, ABS_Z, dataz);
+ input_sync(data->input_dev);
+
+ return IRQ_HANDLED;
+}
+
+static int cma3000_reset(struct cma3000_accl_data *data)
+{
+ int val;
+
+ /* Reset sequence */
+ CMA3000_SET(data, CMA3000_RSTR, 0x02, "Reset");
+ CMA3000_SET(data, CMA3000_RSTR, 0x0A, "Reset");
+ CMA3000_SET(data, CMA3000_RSTR, 0x04, "Reset");
+
+ /* Settling time delay */
+ mdelay(10);
+
+ val = CMA3000_READ(data, CMA3000_STATUS, "Status");
+ if (val < 0) {
+ dev_err(data->dev, "Reset failed\n");
+ return val;
+ }
+
+ if (val & CMA3000_STATUS_PERR) {
+ dev_err(data->dev, "Parity Error\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int cma3000_poweron(struct cma3000_accl_data *data)
+{
+ const struct cma3000_platform_data *pdata = data->pdata;
+ u8 ctrl = 0;
+ int ret;
+
+ if (data->g_range == CMARANGE_2G) {
+ ctrl = (data->mode << 1) | CMA3000_RANGE2G;
+ } else if (data->g_range == CMARANGE_8G) {
+ ctrl = (data->mode << 1) | CMA3000_RANGE8G;
+ } else {
+ dev_info(data->dev,
+ "Invalid G range specified, assuming 8G\n");
+ ctrl = (data->mode << 1) | CMA3000_RANGE8G;
+ }
+
+ ctrl |= data->bus_ops->ctrl_mod;
+
+ CMA3000_SET(data, CMA3000_MDTHR, pdata->mdthr,
+ "Motion Detect Threshold");
+ CMA3000_SET(data, CMA3000_MDFFTMR, pdata->mdfftmr,
+ "Time register");
+ CMA3000_SET(data, CMA3000_FFTHR, pdata->ffthr,
+ "Free fall threshold");
+ ret = CMA3000_SET(data, CMA3000_CTRL, ctrl, "Mode setting");
+ if (ret < 0)
+ return -EIO;
+
+ msleep(CMA3000_SETDELAY);
+
+ return 0;
+}
+
+static int cma3000_poweroff(struct cma3000_accl_data *data)
+{
+ int ret;
+
+ ret = CMA3000_SET(data, CMA3000_CTRL, CMAMODE_POFF, "Mode setting");
+ msleep(CMA3000_SETDELAY);
+
+ return ret;
+}
+
+static int cma3000_open(struct input_dev *input_dev)
+{
+ struct cma3000_accl_data *data = input_get_drvdata(input_dev);
+
+ mutex_lock(&data->mutex);
+
+ if (!data->suspended)
+ cma3000_poweron(data);
+
+ data->opened = true;
+
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+static void cma3000_close(struct input_dev *input_dev)
+{
+ struct cma3000_accl_data *data = input_get_drvdata(input_dev);
+
+ mutex_lock(&data->mutex);
+
+ if (!data->suspended)
+ cma3000_poweroff(data);
+
+ data->opened = false;
+
+ mutex_unlock(&data->mutex);
+}
+
+void cma3000_suspend(struct cma3000_accl_data *data)
+{
+ mutex_lock(&data->mutex);
+
+ if (!data->suspended && data->opened)
+ cma3000_poweroff(data);
+
+ data->suspended = true;
+
+ mutex_unlock(&data->mutex);
+}
+EXPORT_SYMBOL(cma3000_suspend);
+
+
+void cma3000_resume(struct cma3000_accl_data *data)
+{
+ mutex_lock(&data->mutex);
+
+ if (data->suspended && data->opened)
+ cma3000_poweron(data);
+
+ data->suspended = false;
+
+ mutex_unlock(&data->mutex);
+}
+EXPORT_SYMBOL(cma3000_resume);
+
+struct cma3000_accl_data *cma3000_init(struct device *dev, int irq,
+ const struct cma3000_bus_ops *bops)
+{
+ const struct cma3000_platform_data *pdata = dev->platform_data;
+ struct cma3000_accl_data *data;
+ struct input_dev *input_dev;
+ int rev;
+ int error;
+
+ if (!pdata) {
+ dev_err(dev, "platform data not found\n");
+ error = -EINVAL;
+ goto err_out;
+ }
+
+
+ /* if no IRQ return error */
+ if (irq == 0) {
+ error = -EINVAL;
+ goto err_out;
+ }
+
+ data = kzalloc(sizeof(struct cma3000_accl_data), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!data || !input_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ data->dev = dev;
+ data->input_dev = input_dev;
+ data->bus_ops = bops;
+ data->pdata = pdata;
+ data->irq = irq;
+ mutex_init(&data->mutex);
+
+ data->mode = pdata->mode;
+ if (data->mode > CMAMODE_POFF) {
+ data->mode = CMAMODE_MOTDET;
+ dev_warn(dev,
+ "Invalid mode specified, assuming Motion Detect\n");
+ }
+
+ data->g_range = pdata->g_range;
+ if (data->g_range != CMARANGE_2G && data->g_range != CMARANGE_8G) {
+ dev_info(dev,
+ "Invalid G range specified, assuming 8G\n");
+ data->g_range = CMARANGE_8G;
+ }
+
+ input_dev->name = "cma3000-accelerometer";
+ input_dev->id.bustype = bops->bustype;
+ input_dev->open = cma3000_open;
+ input_dev->close = cma3000_close;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+
+ input_set_abs_params(input_dev, ABS_X,
+ -data->g_range, data->g_range, pdata->fuzz_x, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ -data->g_range, data->g_range, pdata->fuzz_y, 0);
+ input_set_abs_params(input_dev, ABS_Z,
+ -data->g_range, data->g_range, pdata->fuzz_z, 0);
+ input_set_abs_params(input_dev, ABS_MISC, 0, 1, 0, 0);
+
+ input_set_drvdata(input_dev, data);
+
+ error = cma3000_reset(data);
+ if (error)
+ goto err_free_mem;
+
+ rev = CMA3000_READ(data, CMA3000_REVID, "Revid");
+ if (rev < 0) {
+ error = rev;
+ goto err_free_mem;
+ }
+
+ pr_info("CMA3000 Accelerometer: Revision %x\n", rev);
+
+ error = request_threaded_irq(irq, NULL, cma3000_thread_irq,
+ pdata->irqflags | IRQF_ONESHOT,
+ "cma3000_d0x", data);
+ if (error) {
+ dev_err(dev, "request_threaded_irq failed\n");
+ goto err_free_mem;
+ }
+
+ error = input_register_device(data->input_dev);
+ if (error) {
+ dev_err(dev, "Unable to register input device\n");
+ goto err_free_irq;
+ }
+
+ return data;
+
+err_free_irq:
+ free_irq(irq, data);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(data);
+err_out:
+ return ERR_PTR(error);
+}
+EXPORT_SYMBOL(cma3000_init);
+
+void cma3000_exit(struct cma3000_accl_data *data)
+{
+ free_irq(data->irq, data);
+ input_unregister_device(data->input_dev);
+ kfree(data);
+}
+EXPORT_SYMBOL(cma3000_exit);
+
+MODULE_DESCRIPTION("CMA3000-D0x Accelerometer Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>");
diff --git a/drivers/input/misc/cma3000_d0x.h b/drivers/input/misc/cma3000_d0x.h
new file mode 100644
index 00000000..2304ce30
--- /dev/null
+++ b/drivers/input/misc/cma3000_d0x.h
@@ -0,0 +1,42 @@
+/*
+ * VTI CMA3000_D0x Accelerometer driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Hemanth V <hemanthv@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _INPUT_CMA3000_H
+#define _INPUT_CMA3000_H
+
+#include <linux/types.h>
+#include <linux/input.h>
+
+struct device;
+struct cma3000_accl_data;
+
+struct cma3000_bus_ops {
+ u16 bustype;
+ u8 ctrl_mod;
+ int (*read)(struct device *, u8, char *);
+ int (*write)(struct device *, u8, u8, char *);
+};
+
+struct cma3000_accl_data *cma3000_init(struct device *dev, int irq,
+ const struct cma3000_bus_ops *bops);
+void cma3000_exit(struct cma3000_accl_data *);
+void cma3000_suspend(struct cma3000_accl_data *);
+void cma3000_resume(struct cma3000_accl_data *);
+
+#endif
diff --git a/drivers/input/misc/cma3000_d0x_i2c.c b/drivers/input/misc/cma3000_d0x_i2c.c
new file mode 100644
index 00000000..4fdef98c
--- /dev/null
+++ b/drivers/input/misc/cma3000_d0x_i2c.c
@@ -0,0 +1,132 @@
+/*
+ * Implements I2C interface for VTI CMA300_D0x Accelerometer driver
+ *
+ * Copyright (C) 2010 Texas Instruments
+ * Author: Hemanth V <hemanthv@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input/cma3000.h>
+#include "cma3000_d0x.h"
+
+static int cma3000_i2c_set(struct device *dev,
+ u8 reg, u8 val, char *msg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ if (ret < 0)
+ dev_err(&client->dev,
+ "%s failed (%s, %d)\n", __func__, msg, ret);
+ return ret;
+}
+
+static int cma3000_i2c_read(struct device *dev, u8 reg, char *msg)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0)
+ dev_err(&client->dev,
+ "%s failed (%s, %d)\n", __func__, msg, ret);
+ return ret;
+}
+
+static const struct cma3000_bus_ops cma3000_i2c_bops = {
+ .bustype = BUS_I2C,
+#define CMA3000_BUSI2C (0 << 4)
+ .ctrl_mod = CMA3000_BUSI2C,
+ .read = cma3000_i2c_read,
+ .write = cma3000_i2c_set,
+};
+
+static int cma3000_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cma3000_accl_data *data;
+
+ data = cma3000_init(&client->dev, client->irq, &cma3000_i2c_bops);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ i2c_set_clientdata(client, data);
+
+ return 0;
+}
+
+static int cma3000_i2c_remove(struct i2c_client *client)
+{
+ struct cma3000_accl_data *data = i2c_get_clientdata(client);
+
+ cma3000_exit(data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cma3000_i2c_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cma3000_accl_data *data = i2c_get_clientdata(client);
+
+ cma3000_suspend(data);
+
+ return 0;
+}
+
+static int cma3000_i2c_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct cma3000_accl_data *data = i2c_get_clientdata(client);
+
+ cma3000_resume(data);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cma3000_i2c_pm_ops = {
+ .suspend = cma3000_i2c_suspend,
+ .resume = cma3000_i2c_resume,
+};
+#endif
+
+static const struct i2c_device_id cma3000_i2c_id[] = {
+ { "cma3000_d01", 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, cma3000_i2c_id);
+
+static struct i2c_driver cma3000_i2c_driver = {
+ .probe = cma3000_i2c_probe,
+ .remove = cma3000_i2c_remove,
+ .id_table = cma3000_i2c_id,
+ .driver = {
+ .name = "cma3000_i2c_accl",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &cma3000_i2c_pm_ops,
+#endif
+ },
+};
+
+module_i2c_driver(cma3000_i2c_driver);
+
+MODULE_DESCRIPTION("CMA3000-D0x Accelerometer I2C Driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Hemanth V <hemanthv@ti.com>");
diff --git a/drivers/input/misc/cobalt_btns.c b/drivers/input/misc/cobalt_btns.c
new file mode 100644
index 00000000..4f77f878
--- /dev/null
+++ b/drivers/input/misc/cobalt_btns.c
@@ -0,0 +1,166 @@
+/*
+ * Cobalt button interface driver.
+ *
+ * Copyright (C) 2007-2008 Yoichi Yuasa <yuasa@linux-mips.org>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <linux/init.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define BUTTONS_POLL_INTERVAL 30 /* msec */
+#define BUTTONS_COUNT_THRESHOLD 3
+#define BUTTONS_STATUS_MASK 0xfe000000
+
+static const unsigned short cobalt_map[] = {
+ KEY_RESERVED,
+ KEY_RESTART,
+ KEY_LEFT,
+ KEY_UP,
+ KEY_DOWN,
+ KEY_RIGHT,
+ KEY_ENTER,
+ KEY_SELECT
+};
+
+struct buttons_dev {
+ struct input_polled_dev *poll_dev;
+ unsigned short keymap[ARRAY_SIZE(cobalt_map)];
+ int count[ARRAY_SIZE(cobalt_map)];
+ void __iomem *reg;
+};
+
+static void handle_buttons(struct input_polled_dev *dev)
+{
+ struct buttons_dev *bdev = dev->private;
+ struct input_dev *input = dev->input;
+ uint32_t status;
+ int i;
+
+ status = ~readl(bdev->reg) >> 24;
+
+ for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
+ if (status & (1U << i)) {
+ if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
+ input_event(input, EV_MSC, MSC_SCAN, i);
+ input_report_key(input, bdev->keymap[i], 1);
+ input_sync(input);
+ }
+ } else {
+ if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
+ input_event(input, EV_MSC, MSC_SCAN, i);
+ input_report_key(input, bdev->keymap[i], 0);
+ input_sync(input);
+ }
+ bdev->count[i] = 0;
+ }
+ }
+}
+
+static int cobalt_buttons_probe(struct platform_device *pdev)
+{
+ struct buttons_dev *bdev;
+ struct input_polled_dev *poll_dev;
+ struct input_dev *input;
+ struct resource *res;
+ int error, i;
+
+ bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
+ poll_dev = input_allocate_polled_device();
+ if (!bdev || !poll_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ memcpy(bdev->keymap, cobalt_map, sizeof(bdev->keymap));
+
+ poll_dev->private = bdev;
+ poll_dev->poll = handle_buttons;
+ poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
+
+ input = poll_dev->input;
+ input->name = "Cobalt buttons";
+ input->phys = "cobalt/input0";
+ input->id.bustype = BUS_HOST;
+ input->dev.parent = &pdev->dev;
+
+ input->keycode = bdev->keymap;
+ input->keycodemax = ARRAY_SIZE(bdev->keymap);
+ input->keycodesize = sizeof(unsigned short);
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+ __set_bit(EV_KEY, input->evbit);
+ for (i = 0; i < ARRAY_SIZE(cobalt_map); i++)
+ __set_bit(bdev->keymap[i], input->keybit);
+ __clear_bit(KEY_RESERVED, input->keybit);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ error = -EBUSY;
+ goto err_free_mem;
+ }
+
+ bdev->poll_dev = poll_dev;
+ bdev->reg = ioremap(res->start, resource_size(res));
+ dev_set_drvdata(&pdev->dev, bdev);
+
+ error = input_register_polled_device(poll_dev);
+ if (error)
+ goto err_iounmap;
+
+ return 0;
+
+ err_iounmap:
+ iounmap(bdev->reg);
+ err_free_mem:
+ input_free_polled_device(poll_dev);
+ kfree(bdev);
+ dev_set_drvdata(&pdev->dev, NULL);
+ return error;
+}
+
+static int cobalt_buttons_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct buttons_dev *bdev = dev_get_drvdata(dev);
+
+ input_unregister_polled_device(bdev->poll_dev);
+ input_free_polled_device(bdev->poll_dev);
+ iounmap(bdev->reg);
+ kfree(bdev);
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
+MODULE_DESCRIPTION("Cobalt button interface driver");
+MODULE_LICENSE("GPL");
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:Cobalt buttons");
+
+static struct platform_driver cobalt_buttons_driver = {
+ .probe = cobalt_buttons_probe,
+ .remove = cobalt_buttons_remove,
+ .driver = {
+ .name = "Cobalt buttons",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(cobalt_buttons_driver);
diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c
new file mode 100644
index 00000000..020569a4
--- /dev/null
+++ b/drivers/input/misc/da9052_onkey.c
@@ -0,0 +1,158 @@
+/*
+ * ON pin driver for Dialog DA9052 PMICs
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * 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/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/reg.h>
+
+struct da9052_onkey {
+ struct da9052 *da9052;
+ struct input_dev *input;
+ struct delayed_work work;
+};
+
+static void da9052_onkey_query(struct da9052_onkey *onkey)
+{
+ int key_stat;
+
+ key_stat = da9052_reg_read(onkey->da9052, DA9052_EVENT_B_REG);
+ if (key_stat < 0) {
+ dev_err(onkey->da9052->dev,
+ "Failed to read onkey event %d\n", key_stat);
+ } else {
+ /*
+ * Since interrupt for deassertion of ONKEY pin is not
+ * generated, onkey event state determines the onkey
+ * button state.
+ */
+ key_stat &= DA9052_EVENTB_ENONKEY;
+ input_report_key(onkey->input, KEY_POWER, key_stat);
+ input_sync(onkey->input);
+ }
+
+ /*
+ * Interrupt is generated only when the ONKEY pin is asserted.
+ * Hence the deassertion of the pin is simulated through work queue.
+ */
+ if (key_stat)
+ schedule_delayed_work(&onkey->work, msecs_to_jiffies(50));
+}
+
+static void da9052_onkey_work(struct work_struct *work)
+{
+ struct da9052_onkey *onkey = container_of(work, struct da9052_onkey,
+ work.work);
+
+ da9052_onkey_query(onkey);
+}
+
+static irqreturn_t da9052_onkey_irq(int irq, void *data)
+{
+ struct da9052_onkey *onkey = data;
+
+ da9052_onkey_query(onkey);
+
+ return IRQ_HANDLED;
+}
+
+static int da9052_onkey_probe(struct platform_device *pdev)
+{
+ struct da9052 *da9052 = dev_get_drvdata(pdev->dev.parent);
+ struct da9052_onkey *onkey;
+ struct input_dev *input_dev;
+ int error;
+
+ if (!da9052) {
+ dev_err(&pdev->dev, "Failed to get the driver's data\n");
+ return -EINVAL;
+ }
+
+ onkey = kzalloc(sizeof(*onkey), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!onkey || !input_dev) {
+ dev_err(&pdev->dev, "Failed to allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ onkey->input = input_dev;
+ onkey->da9052 = da9052;
+ INIT_DELAYED_WORK(&onkey->work, da9052_onkey_work);
+
+ input_dev->name = "da9052-onkey";
+ input_dev->phys = "da9052-onkey/input0";
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY);
+ __set_bit(KEY_POWER, input_dev->keybit);
+
+ error = da9052_request_irq(onkey->da9052, DA9052_IRQ_NONKEY, "ONKEY",
+ da9052_onkey_irq, onkey);
+ if (error < 0) {
+ dev_err(onkey->da9052->dev,
+ "Failed to register ONKEY IRQ: %d\n", error);
+ goto err_free_mem;
+ }
+
+ error = input_register_device(onkey->input);
+ if (error) {
+ dev_err(&pdev->dev, "Unable to register input device, %d\n",
+ error);
+ goto err_free_irq;
+ }
+
+ platform_set_drvdata(pdev, onkey);
+ return 0;
+
+err_free_irq:
+ da9052_free_irq(onkey->da9052, DA9052_IRQ_NONKEY, onkey);
+ cancel_delayed_work_sync(&onkey->work);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(onkey);
+
+ return error;
+}
+
+static int da9052_onkey_remove(struct platform_device *pdev)
+{
+ struct da9052_onkey *onkey = platform_get_drvdata(pdev);
+
+ da9052_free_irq(onkey->da9052, DA9052_IRQ_NONKEY, onkey);
+ cancel_delayed_work_sync(&onkey->work);
+
+ input_unregister_device(onkey->input);
+ kfree(onkey);
+
+ return 0;
+}
+
+static struct platform_driver da9052_onkey_driver = {
+ .probe = da9052_onkey_probe,
+ .remove = da9052_onkey_remove,
+ .driver = {
+ .name = "da9052-onkey",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(da9052_onkey_driver);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("Onkey driver for DA9052");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-onkey");
diff --git a/drivers/input/misc/da9055_onkey.c b/drivers/input/misc/da9055_onkey.c
new file mode 100644
index 00000000..ee6ae3a0
--- /dev/null
+++ b/drivers/input/misc/da9055_onkey.c
@@ -0,0 +1,171 @@
+/*
+ * ON pin driver for Dialog DA9055 PMICs
+ *
+ * Copyright(c) 2012 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * 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/input.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <linux/mfd/da9055/core.h>
+#include <linux/mfd/da9055/reg.h>
+
+struct da9055_onkey {
+ struct da9055 *da9055;
+ struct input_dev *input;
+ struct delayed_work work;
+};
+
+static void da9055_onkey_query(struct da9055_onkey *onkey)
+{
+ int key_stat;
+
+ key_stat = da9055_reg_read(onkey->da9055, DA9055_REG_STATUS_A);
+ if (key_stat < 0) {
+ dev_err(onkey->da9055->dev,
+ "Failed to read onkey event %d\n", key_stat);
+ } else {
+ key_stat &= DA9055_NOKEY_STS;
+ /*
+ * Onkey status bit is cleared when onkey button is relased.
+ */
+ if (!key_stat) {
+ input_report_key(onkey->input, KEY_POWER, 0);
+ input_sync(onkey->input);
+ }
+ }
+
+ /*
+ * Interrupt is generated only when the ONKEY pin is asserted.
+ * Hence the deassertion of the pin is simulated through work queue.
+ */
+ if (key_stat)
+ schedule_delayed_work(&onkey->work, msecs_to_jiffies(10));
+
+}
+
+static void da9055_onkey_work(struct work_struct *work)
+{
+ struct da9055_onkey *onkey = container_of(work, struct da9055_onkey,
+ work.work);
+
+ da9055_onkey_query(onkey);
+}
+
+static irqreturn_t da9055_onkey_irq(int irq, void *data)
+{
+ struct da9055_onkey *onkey = data;
+
+ input_report_key(onkey->input, KEY_POWER, 1);
+ input_sync(onkey->input);
+
+ da9055_onkey_query(onkey);
+
+ return IRQ_HANDLED;
+}
+
+static int da9055_onkey_probe(struct platform_device *pdev)
+{
+ struct da9055 *da9055 = dev_get_drvdata(pdev->dev.parent);
+ struct da9055_onkey *onkey;
+ struct input_dev *input_dev;
+ int irq, err;
+
+ irq = platform_get_irq_byname(pdev, "ONKEY");
+ if (irq < 0) {
+ dev_err(&pdev->dev,
+ "Failed to get an IRQ for input device, %d\n", irq);
+ return -EINVAL;
+ }
+
+ onkey = devm_kzalloc(&pdev->dev, sizeof(*onkey), GFP_KERNEL);
+ if (!onkey) {
+ dev_err(&pdev->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(&pdev->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ onkey->input = input_dev;
+ onkey->da9055 = da9055;
+ input_dev->name = "da9055-onkey";
+ input_dev->phys = "da9055-onkey/input0";
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY);
+ __set_bit(KEY_POWER, input_dev->keybit);
+
+ INIT_DELAYED_WORK(&onkey->work, da9055_onkey_work);
+
+ irq = regmap_irq_get_virq(da9055->irq_data, irq);
+ err = request_threaded_irq(irq, NULL, da9055_onkey_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "ONKEY", onkey);
+ if (err < 0) {
+ dev_err(&pdev->dev,
+ "Failed to register ONKEY IRQ %d, error = %d\n",
+ irq, err);
+ goto err_free_input;
+ }
+
+ err = input_register_device(input_dev);
+ if (err) {
+ dev_err(&pdev->dev, "Unable to register input device, %d\n",
+ err);
+ goto err_free_irq;
+ }
+
+ platform_set_drvdata(pdev, onkey);
+
+ return 0;
+
+err_free_irq:
+ free_irq(irq, onkey);
+ cancel_delayed_work_sync(&onkey->work);
+err_free_input:
+ input_free_device(input_dev);
+
+ return err;
+}
+
+static int da9055_onkey_remove(struct platform_device *pdev)
+{
+ struct da9055_onkey *onkey = platform_get_drvdata(pdev);
+ int irq = platform_get_irq_byname(pdev, "ONKEY");
+
+ irq = regmap_irq_get_virq(onkey->da9055->irq_data, irq);
+ free_irq(irq, onkey);
+ cancel_delayed_work_sync(&onkey->work);
+ input_unregister_device(onkey->input);
+
+ return 0;
+}
+
+static struct platform_driver da9055_onkey_driver = {
+ .probe = da9055_onkey_probe,
+ .remove = da9055_onkey_remove,
+ .driver = {
+ .name = "da9055-onkey",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(da9055_onkey_driver);
+
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_DESCRIPTION("Onkey driver for DA9055");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9055-onkey");
diff --git a/drivers/input/misc/dm355evm_keys.c b/drivers/input/misc/dm355evm_keys.c
new file mode 100644
index 00000000..a309a5c0
--- /dev/null
+++ b/drivers/input/misc/dm355evm_keys.c
@@ -0,0 +1,273 @@
+/*
+ * dm355evm_keys.c - support buttons and IR remote on DM355 EVM board
+ *
+ * Copyright (c) 2008 by David Brownell
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+
+#include <linux/i2c/dm355evm_msp.h>
+#include <linux/module.h>
+
+
+/*
+ * The MSP430 firmware on the DM355 EVM monitors on-board pushbuttons
+ * and an IR receptor used for the remote control. When any key is
+ * pressed, or its autorepeat kicks in, an event is sent. This driver
+ * read those events from the small (32 event) queue and reports them.
+ *
+ * Note that physically there can only be one of these devices.
+ *
+ * This driver was tested with firmware revision A4.
+ */
+struct dm355evm_keys {
+ struct input_dev *input;
+ struct device *dev;
+ int irq;
+};
+
+/* These initial keycodes can be remapped */
+static const struct key_entry dm355evm_keys[] = {
+ /*
+ * Pushbuttons on the EVM board ... note that the labels for these
+ * are SW10/SW11/etc on the PC board. The left/right orientation
+ * comes only from the firmware's documentation, and presumes the
+ * power connector is immediately in front of you and the IR sensor
+ * is to the right. (That is, rotate the board counter-clockwise
+ * by 90 degrees from the SW10/etc and "DM355 EVM" labels.)
+ */
+ { KE_KEY, 0x00d8, { KEY_OK } }, /* SW12 */
+ { KE_KEY, 0x00b8, { KEY_UP } }, /* SW13 */
+ { KE_KEY, 0x00e8, { KEY_DOWN } }, /* SW11 */
+ { KE_KEY, 0x0078, { KEY_LEFT } }, /* SW14 */
+ { KE_KEY, 0x00f0, { KEY_RIGHT } }, /* SW10 */
+
+ /*
+ * IR buttons ... codes assigned to match the universal remote
+ * provided with the EVM (Philips PM4S) using DVD code 0020.
+ *
+ * These event codes match firmware documentation, but other
+ * remote controls could easily send more RC5-encoded events.
+ * The PM4S manual was used in several cases to help select
+ * a keycode reflecting the intended usage.
+ *
+ * RC5 codes are 14 bits, with two start bits (0x3 prefix)
+ * and a toggle bit (masked out below).
+ */
+ { KE_KEY, 0x300c, { KEY_POWER } }, /* NOTE: docs omit this */
+ { KE_KEY, 0x3000, { KEY_NUMERIC_0 } },
+ { KE_KEY, 0x3001, { KEY_NUMERIC_1 } },
+ { KE_KEY, 0x3002, { KEY_NUMERIC_2 } },
+ { KE_KEY, 0x3003, { KEY_NUMERIC_3 } },
+ { KE_KEY, 0x3004, { KEY_NUMERIC_4 } },
+ { KE_KEY, 0x3005, { KEY_NUMERIC_5 } },
+ { KE_KEY, 0x3006, { KEY_NUMERIC_6 } },
+ { KE_KEY, 0x3007, { KEY_NUMERIC_7 } },
+ { KE_KEY, 0x3008, { KEY_NUMERIC_8 } },
+ { KE_KEY, 0x3009, { KEY_NUMERIC_9 } },
+ { KE_KEY, 0x3022, { KEY_ENTER } },
+ { KE_KEY, 0x30ec, { KEY_MODE } }, /* "tv/vcr/..." */
+ { KE_KEY, 0x300f, { KEY_SELECT } }, /* "info" */
+ { KE_KEY, 0x3020, { KEY_CHANNELUP } }, /* "up" */
+ { KE_KEY, 0x302e, { KEY_MENU } }, /* "in/out" */
+ { KE_KEY, 0x3011, { KEY_VOLUMEDOWN } }, /* "left" */
+ { KE_KEY, 0x300d, { KEY_MUTE } }, /* "ok" */
+ { KE_KEY, 0x3010, { KEY_VOLUMEUP } }, /* "right" */
+ { KE_KEY, 0x301e, { KEY_SUBTITLE } }, /* "cc" */
+ { KE_KEY, 0x3021, { KEY_CHANNELDOWN } },/* "down" */
+ { KE_KEY, 0x3022, { KEY_PREVIOUS } },
+ { KE_KEY, 0x3026, { KEY_SLEEP } },
+ { KE_KEY, 0x3172, { KEY_REWIND } }, /* NOTE: docs wrongly say 0x30ca */
+ { KE_KEY, 0x3175, { KEY_PLAY } },
+ { KE_KEY, 0x3174, { KEY_FASTFORWARD } },
+ { KE_KEY, 0x3177, { KEY_RECORD } },
+ { KE_KEY, 0x3176, { KEY_STOP } },
+ { KE_KEY, 0x3169, { KEY_PAUSE } },
+};
+
+/*
+ * Because we communicate with the MSP430 using I2C, and all I2C calls
+ * in Linux sleep, we use a threaded IRQ handler. The IRQ itself is
+ * active low, but we go through the GPIO controller so we can trigger
+ * on falling edges and not worry about enabling/disabling the IRQ in
+ * the keypress handling path.
+ */
+static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
+{
+ static u16 last_event;
+ struct dm355evm_keys *keys = _keys;
+ const struct key_entry *ke;
+ unsigned int keycode;
+ int status;
+ u16 event;
+
+ /* For simplicity we ignore INPUT_COUNT and just read
+ * events until we get the "queue empty" indicator.
+ * Reading INPUT_LOW decrements the count.
+ */
+ for (;;) {
+ status = dm355evm_msp_read(DM355EVM_MSP_INPUT_HIGH);
+ if (status < 0) {
+ dev_dbg(keys->dev, "input high err %d\n",
+ status);
+ break;
+ }
+ event = status << 8;
+
+ status = dm355evm_msp_read(DM355EVM_MSP_INPUT_LOW);
+ if (status < 0) {
+ dev_dbg(keys->dev, "input low err %d\n",
+ status);
+ break;
+ }
+ event |= status;
+ if (event == 0xdead)
+ break;
+
+ /* Press and release a button: two events, same code.
+ * Press and hold (autorepeat), then release: N events
+ * (N > 2), same code. For RC5 buttons the toggle bits
+ * distinguish (for example) "1-autorepeat" from "1 1";
+ * but PCB buttons don't support that bit.
+ *
+ * So we must synthesize release events. We do that by
+ * mapping events to a press/release event pair; then
+ * to avoid adding extra events, skip the second event
+ * of each pair.
+ */
+ if (event == last_event) {
+ last_event = 0;
+ continue;
+ }
+ last_event = event;
+
+ /* ignore the RC5 toggle bit */
+ event &= ~0x0800;
+
+ /* find the key, or report it as unknown */
+ ke = sparse_keymap_entry_from_scancode(keys->input, event);
+ keycode = ke ? ke->keycode : KEY_UNKNOWN;
+ dev_dbg(keys->dev,
+ "input event 0x%04x--> keycode %d\n",
+ event, keycode);
+
+ /* report press + release */
+ input_report_key(keys->input, keycode, 1);
+ input_sync(keys->input);
+ input_report_key(keys->input, keycode, 0);
+ input_sync(keys->input);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*----------------------------------------------------------------------*/
+
+static int dm355evm_keys_probe(struct platform_device *pdev)
+{
+ struct dm355evm_keys *keys;
+ struct input_dev *input;
+ int status;
+
+ /* allocate instance struct and input dev */
+ keys = kzalloc(sizeof *keys, GFP_KERNEL);
+ input = input_allocate_device();
+ if (!keys || !input) {
+ status = -ENOMEM;
+ goto fail1;
+ }
+
+ keys->dev = &pdev->dev;
+ keys->input = input;
+
+ /* set up "threaded IRQ handler" */
+ status = platform_get_irq(pdev, 0);
+ if (status < 0)
+ goto fail1;
+ keys->irq = status;
+
+ input_set_drvdata(input, keys);
+
+ input->name = "DM355 EVM Controls";
+ input->phys = "dm355evm/input0";
+ input->dev.parent = &pdev->dev;
+
+ input->id.bustype = BUS_I2C;
+ input->id.product = 0x0355;
+ input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
+
+ status = sparse_keymap_setup(input, dm355evm_keys, NULL);
+ if (status)
+ goto fail1;
+
+ /* REVISIT: flush the event queue? */
+
+ status = request_threaded_irq(keys->irq, NULL, dm355evm_keys_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(&pdev->dev), keys);
+ if (status < 0)
+ goto fail2;
+
+ /* register */
+ status = input_register_device(input);
+ if (status < 0)
+ goto fail3;
+
+ platform_set_drvdata(pdev, keys);
+
+ return 0;
+
+fail3:
+ free_irq(keys->irq, keys);
+fail2:
+ sparse_keymap_free(input);
+fail1:
+ input_free_device(input);
+ kfree(keys);
+ dev_err(&pdev->dev, "can't register, err %d\n", status);
+
+ return status;
+}
+
+static int dm355evm_keys_remove(struct platform_device *pdev)
+{
+ struct dm355evm_keys *keys = platform_get_drvdata(pdev);
+
+ free_irq(keys->irq, keys);
+ sparse_keymap_free(keys->input);
+ input_unregister_device(keys->input);
+ kfree(keys);
+
+ return 0;
+}
+
+/* REVISIT: add suspend/resume when DaVinci supports it. The IRQ should
+ * be able to wake up the system. When device_may_wakeup(&pdev->dev), call
+ * enable_irq_wake() on suspend, and disable_irq_wake() on resume.
+ */
+
+/*
+ * I2C is used to talk to the MSP430, but this platform device is
+ * exposed by an MFD driver that manages I2C communications.
+ */
+static struct platform_driver dm355evm_keys_driver = {
+ .probe = dm355evm_keys_probe,
+ .remove = dm355evm_keys_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "dm355evm_keys",
+ },
+};
+module_platform_driver(dm355evm_keys_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/epl2182_pls_v2.c b/drivers/input/misc/epl2182_pls_v2.c
new file mode 100644
index 00000000..6d14770c
--- /dev/null
+++ b/drivers/input/misc/epl2182_pls_v2.c
@@ -0,0 +1,1496 @@
+/* drivers/i2c/chips/epl2182.c - light and proxmity sensors driver
+ * Copyright (C) 2011 ELAN Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/hrtimer.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+//#include <asm/mach-types.h>
+#include <asm/setup.h>
+#include <linux/wakelock.h>
+#include <linux/jiffies.h>
+#include <linux/regulator/consumer.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/i2c/epl2182_pls_v2.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+
+/******************************************************************************
+ * configuration
+*******************************************************************************/
+#define PS_INTERRUPT_MODE 1 // 0 is polling mode, 1 is interrupt mode
+#define PLSENSOR_ADAPTIVE
+#ifdef PLSENSOR_ADAPTIVE
+/*ensure the initial value can enable Psensor interrupt,the ps_state of the initial value will be 0*/
+static u16 P_SENSOR_LTHD = 0x7fff; //init
+static u16 P_SENSOR_HTHD = 0x0; //init
+#else
+#define P_SENSOR_LTHD 1900//120 //100
+#define P_SENSOR_HTHD 2000//170 //500
+#endif
+
+#define PS_POLLING_RATE 100
+#define ALS_POLLING_RATE 1000
+
+#define LUX_PER_COUNT 440 // 660 = 1.1*0.6*1000
+#ifdef PLSENSOR_ADAPTIVE
+#define DEBOUNCE 30
+#define MIN_SPACING 250
+#define DIVEDE 4
+static bool first_do_irq = true;
+static u16 ps_min = 0;
+static u16 ps_max = 0;
+static u16 ps_threshold = 0;
+#endif
+/******************************************************************************
+ * configuration
+*******************************************************************************/
+
+typedef enum
+{
+ CMC_MODE_ALS = 0x00,
+ CMC_MODE_PS = 0x10,
+} CMC_MODE;
+
+#define TXBYTES 2
+#define RXBYTES 2
+
+#define PS_DELAY 50
+#define ALS_DELAY 55
+
+#define PACKAGE_SIZE 2
+#define I2C_RETRY_COUNT 10
+#define P_INTT 1
+
+#define PS_INTT 4
+#define ALS_INTT 6 //5-8
+
+static int set_psensor_intr_threshold(uint16_t low_thd, uint16_t high_thd);
+
+#if PS_INTERRUPT_MODE
+static void epl_sensor_irq_do_work(struct work_struct *work);
+static DECLARE_WORK(epl_sensor_irq_work, epl_sensor_irq_do_work);
+#endif
+
+static void report_polling_do_work(struct work_struct *work);
+static DECLARE_DELAYED_WORK(report_polling_work, report_polling_do_work);
+static void polling_do_work(struct work_struct *work);
+static DECLARE_DELAYED_WORK(polling_work, polling_do_work);
+
+/* primitive raw data from I2C */
+typedef struct _epl_raw_data
+{
+ u8 raw_bytes[PACKAGE_SIZE];
+ u16 ps_state;
+ u16 ps_int_state;
+ u16 ps_ch1_raw;
+ u16 als_ch1_raw;
+} epl_raw_data;
+
+struct elan_epl_data
+{
+ struct i2c_client *client;
+ //struct input_dev *als_input_dev;
+ struct input_dev *ps_input_dev;
+ struct workqueue_struct *epl_wq;
+ struct early_suspend early_suspend;
+
+ int intr_pin;
+ //int (*power)(int on);
+
+ int ps_opened;
+ int als_opened;
+
+ int enable_pflag;
+ int enable_lflag;
+ int read_flag;
+ int irq;
+} ;
+
+static DECLARE_WAIT_QUEUE_HEAD(ps_waitqueue);
+static DECLARE_WAIT_QUEUE_HEAD(ls_waitqueue);
+
+
+static int ps_data_changed;
+static int ls_data_changed;
+static struct i2c_client *this_client = NULL;
+
+//static struct wake_lock g_ps_wlock;
+struct elan_epl_data *epl_data;
+static epl_raw_data gRawData;
+static int dual_count;
+
+static const char ElanPsensorName[] = "proximity";
+static const char ElanALsensorName[] = "lightsensor-level";
+
+static int psensor_mode_suspend = 0;
+
+#define LOG_TAG "[EPL2182] "
+#define LOG_FUN(f) printk(KERN_INFO LOG_TAG"%s\n", __FUNCTION__)
+#define LOG_INFO(fmt, args...) printk(KERN_INFO LOG_TAG fmt, ##args)
+#define LOG_ERR(fmt, args...) printk(KERN_ERR LOG_TAG"%s %d : "fmt, __FUNCTION__, __LINE__, ##args)
+
+/*
+//====================I2C write operation===============//
+//regaddr: ELAN Register Address.
+//bytecount: How many bytes to be written to register via i2c bus.
+//txbyte: I2C bus transmit byte(s). Single byte(0X01) transmit only slave address.
+//data: setting value.
+//
+// Example: If you want to write single byte to 0x1D register address, show below
+// elan_sensor_I2C_Write(client,0x1D,0x01,0X02,0xff);
+//
+*/
+static int elan_sensor_I2C_Write(struct i2c_client *client, uint8_t regaddr, uint8_t bytecount, uint8_t txbyte, uint8_t data)
+{
+ uint8_t buffer[2];
+ int ret = 0;
+ int retry,val;
+ struct elan_epl_data *epld = epl_data;
+
+ buffer[0] = (regaddr<<3) | bytecount ;
+ buffer[1] = data;
+
+ for(retry = 0; retry < I2C_RETRY_COUNT; retry++)
+ {
+ ret = i2c_master_send(client, buffer, txbyte);
+
+ if (ret == txbyte)
+ {
+ break;
+ }
+
+ val = gpio_get_value(epld->intr_pin);
+
+ LOG_INFO("INTERRUPT GPIO val = %d\n", val);
+
+ msleep(10);
+ }
+
+ if(retry>=I2C_RETRY_COUNT)
+ {
+ LOG_ERR(KERN_ERR "i2c write retry over %d\n", I2C_RETRY_COUNT);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int elan_sensor_I2C_Read(struct i2c_client *client)
+{
+ uint8_t buffer[RXBYTES];
+ int ret = 0, i =0;
+ int retry,val;
+ struct elan_epl_data *epld = epl_data;
+
+ for(retry = 0; retry < I2C_RETRY_COUNT; retry++)
+ {
+ ret = i2c_master_recv(client, buffer, RXBYTES);
+
+ if (ret == RXBYTES)
+ break;
+
+ val = gpio_get_value(epld->intr_pin);
+
+ LOG_INFO("INTERRUPT GPIO val = %d\n", val);
+
+ msleep(10);
+ }
+
+ if(retry>=I2C_RETRY_COUNT)
+ {
+ LOG_ERR("i2c read retry over %d\n", I2C_RETRY_COUNT);
+ return -EINVAL;
+ }
+
+ for(i=0; i<PACKAGE_SIZE; i++)
+ {
+ gRawData.raw_bytes[i] = buffer[i];
+ }
+
+ return ret;
+}
+
+static void elan_sensor_restart_work(void)
+{
+ struct elan_epl_data *epld = epl_data;
+ cancel_delayed_work(&polling_work);
+ cancel_delayed_work(&report_polling_work);
+ queue_delayed_work(epld->epl_wq, &polling_work,msecs_to_jiffies(10));
+}
+
+static int elan_sensor_psensor_enable(struct elan_epl_data *epld)
+{
+ int ret;
+ uint8_t regdata = 0;
+ struct i2c_client *client = epld->client;
+
+ LOG_INFO("--- Proximity sensor Enable --- \n");
+
+ //disable_irq(epld->irq);
+ ret = elan_sensor_I2C_Write(client,REG_9,W_SINGLE_BYTE,0x02,EPL_INT_DISABLE);
+
+ regdata = EPL_SENSING_8_TIME | EPL_PS_MODE | EPL_L_GAIN ;
+ regdata = regdata | (PS_INTERRUPT_MODE ? EPL_C_SENSING_MODE : EPL_S_SENSING_MODE);
+ ret = elan_sensor_I2C_Write(client,REG_0,W_SINGLE_BYTE,0X02,regdata);
+
+ regdata = PS_INTT<<4 | EPL_PST_1_TIME | EPL_10BIT_ADC;
+ ret = elan_sensor_I2C_Write(client,REG_1,W_SINGLE_BYTE,0X02,regdata);
+
+ set_psensor_intr_threshold(P_SENSOR_LTHD ,P_SENSOR_HTHD);
+#ifdef PLSENSOR_ADAPTIVE
+ if (true == first_do_irq) {
+ msleep(PS_DELAY);
+ elan_sensor_I2C_Write(client,REG_13,R_SINGLE_BYTE,0x01,0);
+ elan_sensor_I2C_Read(client);
+ gRawData.ps_state= !((gRawData.raw_bytes[0]&0x04)>>2);
+ elan_sensor_I2C_Write(client,REG_16,R_TWO_BYTE,0x01,0x00);
+ elan_sensor_I2C_Read(client);
+ gRawData.ps_ch1_raw = (gRawData.raw_bytes[1]<<8) | gRawData.raw_bytes[0];
+ P_SENSOR_LTHD = gRawData.ps_ch1_raw + MIN_SPACING - DEBOUNCE;
+ P_SENSOR_HTHD = gRawData.ps_ch1_raw + MIN_SPACING - DEBOUNCE;
+ set_psensor_intr_threshold(P_SENSOR_LTHD ,P_SENSOR_HTHD);
+ }
+#endif
+
+ ret = elan_sensor_I2C_Write(client,REG_7,W_SINGLE_BYTE,0X02,EPL_C_RESET);
+ ret = elan_sensor_I2C_Write(client,REG_7,W_SINGLE_BYTE,0x02,EPL_C_START_RUN);
+
+#if PS_INTERRUPT_MODE
+ if(epld->enable_lflag)
+ {
+ msleep(PS_DELAY);
+ elan_sensor_I2C_Write(client,REG_13,R_SINGLE_BYTE,0x01,0);
+ elan_sensor_I2C_Read(client);
+ gRawData.ps_state= !((gRawData.raw_bytes[0]&0x04)>>2);
+
+ if(gRawData.ps_state!= gRawData.ps_int_state)
+ {
+ elan_sensor_I2C_Write(client,REG_9,W_SINGLE_BYTE,0x02,EPL_INT_FRAME_ENABLE);
+ }
+ else
+ {
+ elan_sensor_I2C_Write(client,REG_9,W_SINGLE_BYTE,0x02,EPL_INT_ACTIVE_LOW);
+ }
+
+ }
+ else
+ {
+ elan_sensor_I2C_Write(client,REG_9,W_SINGLE_BYTE,0x02,EPL_INT_ACTIVE_LOW);
+ }
+ //enable_irq(epld->irq);
+#endif
+
+ if (ret != 0x02)
+ {
+ LOG_INFO("P-sensor i2c err\n");
+ }
+
+ return ret;
+}
+
+static int elan_sensor_lsensor_enable(struct elan_epl_data *epld)
+{
+ int ret;
+ uint8_t regdata = 0;
+ struct i2c_client *client = epld->client;
+
+ LOG_INFO("--- ALS sensor Enable --- \n");
+
+ //disable_irq(epld->irq);
+ regdata = EPL_INT_DISABLE;
+ ret = elan_sensor_I2C_Write(client,REG_9,W_SINGLE_BYTE,0x02, regdata);
+
+ regdata = EPL_S_SENSING_MODE | EPL_SENSING_8_TIME | EPL_ALS_MODE | EPL_AUTO_GAIN;
+ ret = elan_sensor_I2C_Write(client,REG_0,W_SINGLE_BYTE,0X02,regdata);
+
+ regdata = ALS_INTT<<4 | EPL_PST_1_TIME | EPL_10BIT_ADC;
+ ret = elan_sensor_I2C_Write(client,REG_1,W_SINGLE_BYTE,0X02,regdata);
+
+ ret = elan_sensor_I2C_Write(client,REG_10,W_SINGLE_BYTE,0x02, EPL_GO_MID);
+ ret = elan_sensor_I2C_Write(client,REG_11,W_SINGLE_BYTE,0x02, EPL_GO_LOW);
+
+ ret = elan_sensor_I2C_Write(client,REG_7,W_SINGLE_BYTE,0X02,EPL_C_RESET);
+ ret = elan_sensor_I2C_Write(client,REG_7,W_SINGLE_BYTE,0x02,EPL_C_START_RUN);
+
+ if(ret != 0x02)
+ {
+ LOG_INFO(" ALS-sensor i2c err\n");
+ }
+
+ return ret;
+
+}
+
+/*
+//====================elan_epl_ps_poll_rawdata===============//
+//polling method for proximity sensor detect. Report proximity sensor raw data.
+//Report "ABS_DISTANCE" event to HAL layer.
+//Variable "value" 0 and 1 to represent which distance from psensor to target(human's face..etc).
+//value: 0 represent near.
+//value: 1 represent far.
+*/
+static int elan_epl_ps_poll_rawdata(void)
+{
+ struct elan_epl_data *epld = epl_data;
+ struct i2c_client *client = epld->client;
+ u16 value = 0;
+
+ elan_sensor_I2C_Write(epld->client,REG_7,W_SINGLE_BYTE,0x02,EPL_DATA_LOCK);
+
+ elan_sensor_I2C_Write(client,REG_13,R_SINGLE_BYTE,0x01,0);
+ elan_sensor_I2C_Read(client);
+ gRawData.ps_state= !((gRawData.raw_bytes[0]&0x04)>>2);
+
+ elan_sensor_I2C_Write(client,REG_16,R_TWO_BYTE,0x01,0x00);
+ elan_sensor_I2C_Read(client);
+ gRawData.ps_ch1_raw = (gRawData.raw_bytes[1]<<8) | gRawData.raw_bytes[0];
+
+#ifdef PLSENSOR_ADAPTIVE
+ value = gRawData.ps_ch1_raw;
+ /*threshold detect process*/
+ if(0 == ps_max || 0 == ps_min) {
+ ps_min = value;
+ ps_max = value;
+ ps_threshold = ps_min + MIN_SPACING;
+ }
+ if(value > ps_max) {
+ ps_max = value;
+ ps_threshold = ((ps_max - ps_min) / DIVEDE) + ps_min;
+ if(ps_threshold - ps_min < MIN_SPACING)
+ ps_threshold = ps_min + MIN_SPACING;
+ } else if(value < ps_min) {
+ ps_min = value;
+ ps_threshold = ((ps_max - ps_min) / DIVEDE) + ps_min;
+ if(ps_threshold - ps_min < MIN_SPACING)
+ ps_threshold = ps_min + MIN_SPACING;
+ }
+ P_SENSOR_LTHD = ps_threshold - DEBOUNCE;
+ P_SENSOR_HTHD = ps_threshold + DEBOUNCE;
+ set_psensor_intr_threshold(P_SENSOR_LTHD ,P_SENSOR_HTHD);
+ /*threshold detect process end*/
+ LOG_INFO("ps_min (%4d), ps_max (%4d), ps_threshold (%4d)\n", ps_min, ps_max, ps_threshold);
+ if (true == first_do_irq)
+ first_do_irq = false;
+#endif
+ elan_sensor_I2C_Write(epld->client,REG_7,W_SINGLE_BYTE,0x02,EPL_DATA_UNLOCK);
+ LOG_INFO("### ps_ch1_raw_data (%d), value(%d) ###\n", gRawData.ps_ch1_raw, gRawData.ps_state);
+ ps_data_changed = 1;
+
+ input_report_abs(epld->ps_input_dev, ABS_DISTANCE, gRawData.ps_state);
+ input_sync(epld->ps_input_dev);
+ return 0;
+}
+
+static void elan_epl_als_rawdata(void)
+{
+ struct elan_epl_data *epld = epl_data;
+ struct i2c_client *client = epld->client;
+ uint32_t lux;
+
+ elan_sensor_I2C_Write(client,REG_16,R_TWO_BYTE,0x01,0x00);
+ elan_sensor_I2C_Read(client);
+ gRawData.als_ch1_raw = (gRawData.raw_bytes[1]<<8) | gRawData.raw_bytes[0];
+
+ lux = (gRawData.als_ch1_raw* LUX_PER_COUNT) / 1000 * 15 / 100;
+ if(lux>20000)
+ lux=20000;
+
+ LOG_INFO("------------------- ALS raw = %d, lux = %d\n\n", gRawData.als_ch1_raw, lux);
+ ls_data_changed = 1;
+#if 1
+ input_report_abs(epld->ps_input_dev, ABS_MISC, lux);
+ input_sync(epld->ps_input_dev);
+#else
+ input_report_abs(epld->als_input_dev, ABS_MISC, lux);
+ input_sync(epld->als_input_dev);
+#endif
+}
+
+/*
+//====================set_psensor_intr_threshold===============//
+//low_thd: The value is psensor interrupt low threshold.
+//high_thd: The value is psensor interrupt hihg threshold.
+//When psensor rawdata > hihg_threshold, interrupt pin will be pulled low.
+//After interrupt occur, psensor rawdata < low_threshold, interrupt pin will be pulled high.
+*/
+static int set_psensor_intr_threshold(uint16_t low_thd, uint16_t high_thd)
+{
+ int ret = 0;
+ struct elan_epl_data *epld = epl_data;
+ struct i2c_client *client = epld->client;
+
+ uint8_t high_msb ,high_lsb, low_msb, low_lsb;
+
+ high_msb = (uint8_t) (high_thd >> 8);
+ high_lsb = (uint8_t) (high_thd & 0x00ff);
+ low_msb = (uint8_t) (low_thd >> 8);
+ low_lsb = (uint8_t) (low_thd & 0x00ff);
+
+ elan_sensor_I2C_Write(client,REG_2,W_SINGLE_BYTE,0x02,high_lsb);
+ elan_sensor_I2C_Write(client,REG_3,W_SINGLE_BYTE,0x02,high_msb);
+ elan_sensor_I2C_Write(client,REG_4,W_SINGLE_BYTE,0x02,low_lsb);
+ elan_sensor_I2C_Write(client,REG_5,W_SINGLE_BYTE,0x02,low_msb);
+
+ return ret;
+}
+
+#if PS_INTERRUPT_MODE
+static void epl_sensor_irq_do_work(struct work_struct *work)
+{
+ struct elan_epl_data *epld = epl_data;
+ struct i2c_client *client = epld->client;
+ int mode = 0;
+ LOG_FUN();
+
+ elan_sensor_I2C_Write(epld->client,REG_7,W_SINGLE_BYTE,0x02,EPL_DATA_LOCK);
+
+ elan_sensor_I2C_Write(client,REG_13,R_SINGLE_BYTE,0x01,0);
+ elan_sensor_I2C_Read(client);
+ mode = gRawData.raw_bytes[0]&(3<<4);
+
+ // 0x10 is ps mode
+ if(mode==CMC_MODE_PS && epld->enable_pflag)
+ {
+ gRawData.ps_int_state= !((gRawData.raw_bytes[0]&0x04)>>2);
+ elan_epl_ps_poll_rawdata();
+ }
+ else
+ {
+ LOG_INFO("interrupt in als\n");
+ }
+ elan_sensor_I2C_Write(client,REG_9,W_SINGLE_BYTE,0x02,EPL_INT_ACTIVE_LOW);
+ elan_sensor_I2C_Write(client,REG_7,W_SINGLE_BYTE,0x02,EPL_DATA_UNLOCK);
+ enable_irq(epld->irq);
+}
+
+static irqreturn_t elan_sensor_irq_handler(int irqNo, void *handle)
+{
+ struct elan_epl_data *epld = (struct elan_epl_data*)handle;
+
+ disable_irq_nosync(epld->irq);
+ queue_work(epld->epl_wq, &epl_sensor_irq_work);
+
+ return IRQ_HANDLED;
+}
+#endif
+
+static void report_polling_do_work(struct work_struct *work)
+{
+ struct elan_epl_data *epld = epl_data;
+
+ if(dual_count==CMC_MODE_PS)
+ {
+ elan_epl_ps_poll_rawdata();
+ }
+ else if(dual_count==CMC_MODE_ALS)
+ {
+ elan_epl_als_rawdata();
+ }
+
+ if(epld->enable_pflag)
+ {
+ elan_sensor_psensor_enable(epld);
+
+ if(PS_INTERRUPT_MODE==0)
+ {
+ dual_count=CMC_MODE_PS; // ps mode
+ queue_delayed_work(epld->epl_wq, &report_polling_work,msecs_to_jiffies(PS_POLLING_RATE));
+ }
+ }
+}
+
+static void polling_do_work(struct work_struct *work)
+{
+ struct elan_epl_data *epld = epl_data;
+ struct i2c_client *client = epld->client;
+
+ bool isInterleaving = epld->enable_pflag==1 && epld->enable_lflag==1;
+ bool isAlsOnly = epld->enable_pflag==0 && epld->enable_lflag==1;
+ bool isPsOnly = epld->enable_pflag==1 && epld->enable_lflag==0;
+
+ cancel_delayed_work(&polling_work);
+ cancel_delayed_work(&report_polling_work);
+
+ LOG_INFO("enable_pflag = %d, enable_lflag = %d\n", epld->enable_pflag, epld->enable_lflag);
+
+ if(isAlsOnly || isInterleaving)
+ {
+ elan_sensor_lsensor_enable(epld);
+ dual_count = CMC_MODE_ALS;
+
+ queue_delayed_work(epld->epl_wq, &report_polling_work,msecs_to_jiffies(ALS_DELAY));
+ queue_delayed_work(epld->epl_wq, &polling_work,msecs_to_jiffies(ALS_POLLING_RATE));
+ }
+ else if(isPsOnly)
+ {
+ elan_sensor_psensor_enable(epld);
+ dual_count = CMC_MODE_PS;
+
+ if(PS_INTERRUPT_MODE)
+ {
+ // do nothing
+ }
+ else
+ {
+ queue_delayed_work(epld->epl_wq, &report_polling_work,msecs_to_jiffies(PS_DELAY));
+ queue_delayed_work(epld->epl_wq, &polling_work,msecs_to_jiffies(PS_POLLING_RATE));
+ }
+ }
+ else
+ {
+ elan_sensor_I2C_Write(client,REG_9,W_SINGLE_BYTE,0x02,EPL_INT_DISABLE);
+ elan_sensor_I2C_Write(client,REG_0,W_SINGLE_BYTE,0X02,EPL_S_SENSING_MODE);
+ }
+}
+
+#if 0
+static ssize_t elan_ls_operationmode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ uint16_t mode=0;
+ struct elan_epl_data *epld = epl_data;
+ LOG_FUN();
+
+ sscanf(buf, "%hu",&mode);
+ LOG_INFO("==>[operation mode]=%d\n", mode);
+
+ if(mode == 0)
+ {
+ epld->enable_lflag = 0;
+ epld->enable_pflag = 0;
+ }
+ else if(mode == 1)
+ {
+ epld->enable_lflag = 1;
+ epld->enable_pflag = 0;
+ }
+ else if(mode == 2)
+ {
+ epld->enable_lflag = 0;
+ epld->enable_pflag = 1;
+ }
+ else if(mode == 3)
+ {
+ epld->enable_lflag = 1;
+ epld->enable_pflag = 1;
+ }
+ else
+ {
+ LOG_INFO("0: none\n1: als only\n2: ps only\n3: interleaving");
+ }
+
+ elan_sensor_restart_work();
+ return count;
+}
+
+static ssize_t elan_ls_operationmode_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct elan_epl_data *epld = epl_data;
+ long *tmp = (long*)buf;
+ uint16_t mode =0;
+ LOG_FUN();
+
+ if( epld->enable_pflag==0 && epld->enable_lflag==0)
+ {
+ mode = 0;
+ }
+ else if( epld->enable_pflag==0 && epld->enable_lflag==1)
+ {
+ mode = 1;
+ }
+ else if( epld->enable_pflag==1 && epld->enable_lflag==0)
+ {
+ mode = 2;
+ }
+ else if( epld->enable_pflag==1 && epld->enable_lflag==1)
+ {
+ mode = 3;
+ }
+ tmp[0] = mode;
+ return sprintf(buf, "%d \n", mode);
+}
+
+static ssize_t elan_ls_status_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct elan_epl_data *epld = epl_data;
+ u16 ch1;
+
+ if(!epl_data)
+ {
+ LOG_INFO("epl_data is null!!\n");
+ return 0;
+ }
+ elan_sensor_I2C_Write(epld->client,REG_7,W_SINGLE_BYTE,0x02,EPL_DATA_LOCK);
+
+ elan_sensor_I2C_Write(epld->client,REG_16,R_TWO_BYTE,0x01,0x00);
+ elan_sensor_I2C_Read(epld->client);
+ ch1 = (gRawData.raw_bytes[1]<<8) | gRawData.raw_bytes[0];
+ LOG_INFO("ch1 raw_data = %d\n", ch1);
+
+ elan_sensor_I2C_Write(epld->client,REG_7,W_SINGLE_BYTE,0x02,EPL_DATA_UNLOCK);
+
+ return sprintf(buf, "%d\n", ch1);
+}
+
+static DEVICE_ATTR(elan_ls_operationmode, S_IROTH|S_IWOTH, elan_ls_operationmode_show,elan_ls_operationmode_store);
+static DEVICE_ATTR(elan_ls_status, S_IROTH|S_IWOTH, elan_ls_status_show,NULL);
+
+static struct attribute *ets_attributes[] =
+{
+ &dev_attr_elan_ls_operationmode.attr,
+ &dev_attr_elan_ls_status.attr,
+ NULL,
+};
+
+static struct attribute_group ets_attr_group =
+{
+ .attrs = ets_attributes,
+};
+#endif
+
+#if 0 //ices add
+static int elan_als_open(struct inode *inode, struct file *file)
+{
+ struct elan_epl_data *epld = epl_data;
+
+ LOG_FUN();
+
+ if (epld->als_opened)
+ {
+ return -EBUSY;
+ }
+ epld->als_opened = 1;
+
+ return 0;
+}
+
+static int elan_als_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct elan_epl_data *epld = epl_data;
+ int buf[1];
+ if(epld->read_flag ==1)
+ {
+ buf[0] = gRawData.als_ch1_raw;
+ if(copy_to_user(buffer, &buf , sizeof(buf)))
+ return 0;
+ epld->read_flag = 0;
+ return 12;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+static int elan_als_release(struct inode *inode, struct file *file)
+{
+ struct elan_epl_data *epld = epl_data;
+
+ LOG_FUN();
+
+ epld->als_opened = 0;
+
+ return 0;
+}
+#if 1
+static long elan_als_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int flag;
+ unsigned long buf[1];
+ struct elan_epl_data *epld = epl_data;
+
+ void __user *argp = (void __user *)arg;
+
+ LOG_INFO("als io ctrl cmd %d\n", _IOC_NR(cmd));
+
+ switch(cmd)
+ {
+ case ELAN_EPL6800_IOCTL_GET_LFLAG:
+
+ LOG_INFO("elan ambient-light IOCTL Sensor get lflag \n");
+ flag = epld->enable_lflag;
+ if (copy_to_user(argp, &flag, sizeof(flag)))
+ return -EFAULT;
+
+ LOG_INFO("elan ambient-light Sensor get lflag %d\n",flag);
+ break;
+
+ case ELAN_EPL6800_IOCTL_ENABLE_LFLAG:
+
+ LOG_INFO("elan ambient-light IOCTL Sensor set lflag \n");
+ if (copy_from_user(&flag, argp, 1))
+ return -EFAULT;
+ LOG_INFO("elan ambient-light Sensor set lflag %d\n",flag);
+ if(flag) {
+ flag = 1;
+ }else {
+ flag = 0;
+ }
+ //if (flag < 0 || flag > 1)
+ // return -EINVAL;
+
+ epld->enable_lflag = flag;
+ elan_sensor_restart_work();
+
+ //LOG_INFO("elan ambient-light Sensor set lflag %d\n",flag);
+ break;
+
+ case ELAN_EPL6800_IOCTL_GETDATA:
+ buf[0] = (unsigned long)gRawData.als_ch1_raw;
+ if(copy_to_user(argp, &buf , 2))
+ return -EFAULT;
+
+ break;
+
+ default:
+ LOG_ERR("invalid cmd %d\n", _IOC_NR(cmd));
+ return -EINVAL;
+ }
+
+ return 0;
+
+}
+#endif
+static unsigned int elan_als_poll(struct file *fp, poll_table * wait)
+{
+ if(ls_data_changed){
+ //pr_info("%s, ls_data_changed 1st, ls_data = 0x%x\n", __func__, ls_data);
+ ls_data_changed = 0;
+ //mutex_unlock(&stk3x1x_mutex);
+ return POLLIN | POLLRDNORM;
+ }
+ poll_wait(fp, &ls_waitqueue, wait);
+ //mutex_unlock(&stk3x1x_mutex);
+ return 0;
+}
+
+static struct file_operations elan_als_fops =
+{
+ .owner = THIS_MODULE,
+ .open = elan_als_open,
+ .read = elan_als_read,
+ .release = elan_als_release,
+ .unlocked_ioctl = elan_als_ioctl,
+ .poll = elan_als_poll,
+};
+
+static struct miscdevice elan_als_device =
+{
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "elan_als",
+ .fops = &elan_als_fops
+};
+#endif //ices end
+
+static int elan_ps_open(struct inode *inode, struct file *file)
+{
+ struct elan_epl_data *epld = epl_data;
+
+ LOG_FUN();
+
+ if (epld->ps_opened)
+ return -EBUSY;
+
+ epld->ps_opened = 1;
+
+ return 0;
+}
+
+static int elan_ps_release(struct inode *inode, struct file *file)
+{
+ struct elan_epl_data *epld = epl_data;
+
+ LOG_FUN();
+
+ epld->ps_opened = 0;
+
+ psensor_mode_suspend = 0;
+
+ return 0;
+}
+
+static int epl2182_read_chip_info(struct i2c_client *client, char *buf)
+{
+ if(NULL == buf) {
+ return -1;
+ }
+ if(NULL == client) {
+ *buf = 0;
+ return -2;
+ }
+
+ sprintf(buf, "EPL2182");
+ printk("[EPL2182] epl2182_read_chip_info %s\n",buf);
+ return 0;
+}
+
+static long elan_ps_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int flag, err;
+ unsigned long buf;
+ char strbuf[256];
+ struct elan_epl_data *epld = epl_data;
+ void __user *argp = (void __user *)arg;
+
+ LOG_INFO("ps io ctrl cmd %d\n", _IOC_NR(cmd));
+ //ioctl message handle must define by android sensor library (case by case)
+ switch(cmd)
+ {
+ case ELAN_EPL6800_IOCTL_GET_PFLAG:
+ flag = epld->enable_pflag;
+ if (copy_to_user(argp, &flag, sizeof(flag)))
+ return -EFAULT;
+ LOG_INFO("elan Proximity Sensor get pflag %d\n",flag);
+ break;
+
+ case ELAN_EPL6800_IOCTL_ENABLE_PFLAG:
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ LOG_INFO("elan Proximity Sensor set pflag %d\n",flag);
+ if(flag) {
+ flag = 1;
+ }else {
+ flag = 0;
+ }
+ epld->enable_pflag = flag;
+ elan_sensor_restart_work();
+ break;
+
+ case ELAN_EPL6800_IOCTL_GET_CHIPINFO:
+ err = epl2182_read_chip_info(this_client, strbuf);
+ if(err < 0)
+ return -EFAULT;
+ if(copy_to_user(argp, strbuf, strlen(strbuf)+1))
+ return -EFAULT;
+ break;
+
+ case ELAN_EPL6800_IOCTL_GET_LFLAG:
+ flag = epld->enable_lflag;
+ if (copy_to_user(argp, &flag, sizeof(flag)))
+ return -EFAULT;
+ LOG_INFO("elan ambient-light Sensor get lflag %d\n",flag);
+ break;
+
+ case ELAN_EPL6800_IOCTL_ENABLE_LFLAG:
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ LOG_INFO("elan ambient-light Sensor set lflag %d\n",flag);
+ if(flag) {
+ flag = 1;
+ }else {
+ flag = 0;
+ }
+ epld->enable_lflag = flag;
+ elan_sensor_restart_work();
+ break;
+
+ case ELAN_EPL6800_IOCTL_GETDATA:
+ buf = (unsigned long)gRawData.als_ch1_raw;
+ if(copy_to_user(argp, &buf , sizeof(buf)))
+ return -EFAULT;
+ LOG_INFO("elan als Sensor get data (%lu)\n",buf);
+ break;
+
+ default:
+ LOG_ERR("invalid cmd %d\n", _IOC_NR(cmd));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static unsigned int elan_ps_poll(struct file *fp, poll_table * wait)
+{
+ if(ps_data_changed) {
+ ps_data_changed = 0;
+ return POLLIN | POLLRDNORM;
+ }
+ poll_wait(fp, &ps_waitqueue, wait);
+ return 0;
+}
+
+
+static struct file_operations elan_ps_fops =
+{
+ .owner = THIS_MODULE,
+ .open = elan_ps_open,
+ .release = elan_ps_release,
+ .unlocked_ioctl = elan_ps_ioctl,
+ .poll = elan_ps_poll,
+};
+
+static struct miscdevice elan_ps_device =
+{
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = EPL2182_PLS_DEVICE,
+ .fops = &elan_ps_fops
+};
+
+static int initial_sensor(struct elan_epl_data *epld)
+{
+ struct i2c_client *client = epld->client;
+
+ int ret = 0;
+
+ LOG_INFO("initial_sensor enter!\n");
+
+ ret = elan_sensor_I2C_Read(client);
+
+ if(ret < 0)
+ return -EINVAL;
+
+ elan_sensor_I2C_Write(client,REG_0,W_SINGLE_BYTE,0x02, EPL_S_SENSING_MODE);
+ elan_sensor_I2C_Write(client,REG_9,W_SINGLE_BYTE,0x02,EPL_INT_DISABLE);
+ set_psensor_intr_threshold(P_SENSOR_LTHD , P_SENSOR_HTHD);
+
+ msleep(2);
+
+ epld->enable_lflag = 0;
+ epld->enable_pflag = 0;
+
+ return ret;
+}
+#if 0 //ices add
+/*----------------------------------------------------------------------------*/
+static ssize_t light_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct elan_epl_data *epld = epl_data;
+ printk("%s: ALS_status=%d\n", __func__, epld->enable_lflag);
+ return sprintf(buf, "%d\n", epld->enable_lflag);
+}
+
+static ssize_t light_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct elan_epl_data *epld = epl_data;
+
+ LOG_INFO("light_enable_store: enable=%s \n", buf);
+
+ if (sysfs_streq(buf, "1"))
+ epld->enable_lflag= 1;
+ else if (sysfs_streq(buf, "0"))
+ epld->enable_lflag= 0;
+ else {
+ pr_err("%s: invalid value %d\n", __func__, *buf);
+ return 0;
+ }
+
+ elan_sensor_restart_work();
+ return size;
+}
+
+/*----------------------------------------------------------------------------*/
+static struct device_attribute dev_attr_light_enable =
+__ATTR(enable, S_IRWXUGO,
+ light_enable_show, light_enable_store);
+
+static struct attribute *light_sysfs_attrs[] = {
+ &dev_attr_light_enable.attr,
+ NULL
+};
+
+static struct attribute_group light_attribute_group = {
+ .attrs = light_sysfs_attrs,
+};
+/*----------------------------------------------------------------------------*/
+
+static int lightsensor_setup(struct elan_epl_data *epld)
+{
+ int err = 0;
+ LOG_INFO("lightsensor_setup enter.\n");
+
+ epld->als_input_dev = input_allocate_device();
+ if (!epld->als_input_dev)
+ {
+ LOG_ERR( "could not allocate ls input device\n");
+ return -ENOMEM;
+ }
+ epld->als_input_dev->name = ElanALsensorName;
+ set_bit(EV_ABS, epld->als_input_dev->evbit);
+ input_set_abs_params(epld->als_input_dev, ABS_MISC, 0, 9, 0, 0);
+
+ err = input_register_device(epld->als_input_dev);
+ if (err < 0)
+ {
+ LOG_ERR("can not register ls input device\n");
+ goto err_free_ls_input_device;
+ }
+#if 0 //ices add
+ err = misc_register(&elan_als_device);
+ if (err < 0)
+ {
+ LOG_ERR("can not register ls misc device\n");
+ goto err_unregister_ls_input_device;
+ }
+#endif //ices end
+ err = sysfs_create_group(&epld->als_input_dev->dev.kobj, &light_attribute_group);
+
+ if (err) {
+ pr_err("%s: could not create sysfs group\n", __func__);
+ goto err_free_ls_input_device;
+ }
+
+ return err;
+
+err_unregister_ls_input_device:
+ input_unregister_device(epld->als_input_dev);
+err_free_ls_input_device:
+ input_free_device(epld->als_input_dev);
+ return err;
+}
+#endif
+/*----------------------------------------------------------------------------*/
+static ssize_t proximity_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct elan_epl_data *epld = epl_data;
+ printk("%s: PS status=%d\n", __func__, epld->enable_pflag);
+ LOG_INFO("epl2182_setup_psensor enter.\n");
+ return sprintf(buf, "%d\n", epld->enable_pflag);
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static ssize_t proximity_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct elan_epl_data *epld = epl_data;
+
+ LOG_INFO("proximity_enable_store: enable=%s \n", buf);
+ if (sysfs_streq(buf, "1"))
+ epld->enable_pflag =1;
+ else if (sysfs_streq(buf, "0"))
+ epld->enable_pflag =0;
+ else {
+ pr_err("%s: invalid value %d\n", __func__, *buf);
+ return 0;
+ }
+
+ elan_sensor_restart_work();
+ return size;
+}
+/*----------------------------------------------------------------------------*/
+static struct device_attribute dev_attr_ps_enable =
+__ATTR(enable, S_IRWXUGO,
+ proximity_enable_show, proximity_enable_store);
+
+static struct attribute *proximity_sysfs_attrs[] = {
+ &dev_attr_ps_enable.attr,
+ NULL
+};
+
+static struct attribute_group proximity_attribute_group = {
+ .attrs = proximity_sysfs_attrs,
+};
+/*----------------------------------------------------------------------------*/
+
+static int psensor_setup(struct elan_epl_data *epld)
+{
+ int err = 0;
+ LOG_INFO("psensor_setup enter.\n");
+
+ epld->ps_input_dev = input_allocate_device();
+ if (!epld->ps_input_dev)
+ {
+ LOG_ERR("could not allocate ps input device\n");
+ return -ENOMEM;
+ }
+ epld->ps_input_dev->name = ElanPsensorName;
+
+ set_bit(EV_ABS, epld->ps_input_dev->evbit);
+ input_set_abs_params(epld->ps_input_dev, ABS_DISTANCE, 0, 1, 0, 0);
+#if 1
+ set_bit(EV_ABS, epld->ps_input_dev->evbit);
+ input_set_abs_params(epld->ps_input_dev, ABS_MISC, 0, 9, 0, 0);
+#endif
+
+ err = input_register_device(epld->ps_input_dev);
+ if (err < 0)
+ {
+ LOG_ERR("could not register ps input device\n");
+ goto err_free_ps_input_device;
+ }
+
+ err = misc_register(&elan_ps_device);
+ if (err < 0)
+ {
+ LOG_ERR("could not register ps misc device\n");
+ goto err_unregister_ps_input_device;
+ }
+
+ err = sysfs_create_group(&epld->ps_input_dev->dev.kobj, &proximity_attribute_group);
+ if (err) {
+ pr_err("%s: PS could not create sysfs group\n", __func__);
+ goto err_free_ps_input_device;
+ }
+
+ return err;
+
+err_unregister_ps_input_device:
+ input_unregister_device(epld->ps_input_dev);
+err_free_ps_input_device:
+ input_free_device(epld->ps_input_dev);
+ return err;
+}
+
+#if PS_INTERRUPT_MODE
+static int setup_interrupt(struct elan_epl_data *epld)
+{
+ struct i2c_client *client = epld->client;
+ struct elan_epl_platform_data *pdata = client->dev.platform_data;
+
+ int err = 0;
+ msleep(5);
+#if 1
+ err = gpio_request(pdata->irq_gpio_number, "Elan EPL IRQ");
+ if (err)
+ {
+ LOG_ERR("gpio pin request fail (%d)\n", err);
+ goto initial_fail;
+ }
+ else
+ {
+
+ gpio_direction_input(pdata->irq_gpio_number);
+
+ /*get irq*/
+ client->irq = gpio_to_irq(pdata->irq_gpio_number);
+ epld->irq = client->irq;
+
+ printk("IRQ number is %d\n", client->irq);
+
+ }
+#endif
+ err = request_irq(epld->irq,elan_sensor_irq_handler, IRQF_TRIGGER_FALLING | IRQF_NO_SUSPEND ,
+ client->dev.driver->name, epld);
+ if(err <0)
+ {
+ LOG_ERR("request irq pin %d fail for gpio\n",err);
+ goto fail_free_intr_pin;
+ }
+
+ return err;
+
+initial_fail:
+fail_free_intr_pin:
+ gpio_free(epld->intr_pin);
+ return err;
+}
+#endif
+
+#ifdef CONFIG_SUSPEND
+static int elan_sensor_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ LOG_FUN();
+
+ if(epl_data->enable_pflag==0 )
+ {
+ elan_sensor_I2C_Write(client,REG_7, W_SINGLE_BYTE, 0x02, EPL_C_P_DOWN);
+ cancel_delayed_work(&polling_work);
+ }
+ return 0;
+}
+
+static int elan_sensor_resume(struct i2c_client *client)
+{
+ struct elan_epl_data *epld = epl_data;
+ LOG_FUN();
+
+ if(epld->enable_pflag | epld->enable_lflag)
+ {
+ elan_sensor_I2C_Write(client,REG_7, W_SINGLE_BYTE, 0x02, EPL_C_P_UP);
+ }
+
+ if(epld->enable_pflag)
+ {
+ elan_sensor_restart_work();
+ }
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void elan_sensor_early_suspend(struct early_suspend *h)
+{
+ struct elan_epl_data *epld = epl_data;
+ struct i2c_client *client = epld->client;
+ LOG_FUN();
+
+ if( epld->enable_pflag==0)
+ {
+ elan_sensor_I2C_Write(client,REG_7, W_SINGLE_BYTE, 0x02, EPL_C_P_DOWN);
+ cancel_delayed_work(&polling_work);
+ }
+
+ psensor_mode_suspend = 1;
+}
+
+static void elan_sensor_late_resume(struct early_suspend *h)
+{
+ struct elan_epl_data *epld = epl_data;
+ struct i2c_client *client = epld->client;
+
+ LOG_FUN();
+
+ if(epld->enable_pflag | epld->enable_lflag)
+ {
+ elan_sensor_I2C_Write(client,REG_7, W_SINGLE_BYTE, 0x02, EPL_C_P_UP);
+ }
+
+ if(epld->enable_pflag || epld->enable_lflag)
+ {
+ elan_sensor_restart_work();
+ }
+ psensor_mode_suspend = 0;
+}
+#endif
+
+static int elan_sensor_probe(struct i2c_client *client,const struct i2c_device_id *id)
+{
+ int err = 0;
+ int chip_id = 0;
+ struct elan_epl_data *epld ;
+ struct elan_epl_platform_data *pdata = client->dev.platform_data;
+ //static struct platform_device *sensor_dev;
+ struct device_node *np = client->dev.of_node;
+ LOG_INFO("elan sensor probe enter.\n");
+#ifdef CONFIG_OF
+ if (np && !pdata){
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev, "Could not allocate struct elan_epl_platform_data");
+ goto exit_allocate_pdata_failed;
+ }
+ pdata->irq_gpio_number = of_get_gpio(np, 0);
+ if(pdata->irq_gpio_number < 0){
+ dev_err(&client->dev, "fail to get irq_gpio_number\n");
+ kfree(pdata);
+ goto exit_irq_gpio_read_fail;
+ }
+ client->dev.platform_data = pdata;
+ }
+#endif
+
+ epld = kzalloc(sizeof(struct elan_epl_data), GFP_KERNEL);
+ if (!epld) {
+ err = -ENOMEM;
+ LOG_ERR("kzalloc elan_epl_data failed!\n");
+ goto exit_kzalloc_epld_failed;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ {
+ dev_err(&client->dev,"No supported i2c func what we need?!!\n");
+ err = -ENOTSUPP;
+ goto i2c_fail;
+ }
+ chip_id = i2c_smbus_read_byte_data(client, 0x00);
+ if (chip_id < 0)
+ {
+ dev_err(&client->dev,"read chip id REG 0x00 failed\n");
+ err = -ENODEV;
+ goto exit_read_chipid_failed;
+ }
+ LOG_INFO("chip id REG 0x00 value = %8x\n", i2c_smbus_read_byte_data(client, 0x00));
+ LOG_INFO("chip id REG 0x01 value = %8x\n", i2c_smbus_read_byte_data(client, 0x08));
+ LOG_INFO("chip id REG 0x02 value = %8x\n", i2c_smbus_read_byte_data(client, 0x10));
+ LOG_INFO("chip id REG 0x03 value = %8x\n", i2c_smbus_read_byte_data(client, 0x18));
+ LOG_INFO("chip id REG 0x04 value = %8x\n", i2c_smbus_read_byte_data(client, 0x20));
+ LOG_INFO("chip id REG 0x05 value = %8x\n", i2c_smbus_read_byte_data(client, 0x28));
+ LOG_INFO("chip id REG 0x06 value = %8x\n", i2c_smbus_read_byte_data(client, 0x30));
+ LOG_INFO("chip id REG 0x07 value = %8x\n", i2c_smbus_read_byte_data(client, 0x38));
+ LOG_INFO("chip id REG 0x09 value = %8x\n", i2c_smbus_read_byte_data(client, 0x48));
+ LOG_INFO("chip id REG 0x0D value = %8x\n", i2c_smbus_read_byte_data(client, 0x68));
+ LOG_INFO("chip id REG 0x0E value = %8x\n", i2c_smbus_read_byte_data(client, 0x70));
+ LOG_INFO("chip id REG 0x0F value = %8x\n", i2c_smbus_read_byte_data(client, 0x71));
+ LOG_INFO("chip id REG 0x10 value = %8x\n", i2c_smbus_read_byte_data(client, 0x80));
+ LOG_INFO("chip id REG 0x11 value = %8x\n", i2c_smbus_read_byte_data(client, 0x88));
+ LOG_INFO("chip id REG 0x13 value = %8x\n", i2c_smbus_read_byte_data(client, 0x98));
+
+ epld->client = client;
+ this_client = client;
+ epld->irq = client->irq;
+ i2c_set_clientdata(client, epld);
+
+ epl_data = epld;
+
+ epld->epl_wq = create_singlethread_workqueue(EPL2182_PLS_DEVICE);
+ if (!epld->epl_wq)
+ {
+ LOG_ERR("can't create workqueue\n");
+ err = -ENOMEM;
+ goto err_create_singlethread_workqueue;
+ }
+#if 0 //ices add
+ err = lightsensor_setup(epld);
+ if (err < 0)
+ {
+ LOG_ERR("lightsensor_setup error!!\n");
+ goto err_lightsensor_setup;
+ }
+#endif
+ err = psensor_setup(epld);
+ if (err < 0)
+ {
+ LOG_ERR("psensor_setup error!!\n");
+ goto err_psensor_setup;
+ }
+
+ err = initial_sensor(epld);
+ if (err < 0)
+ {
+ LOG_ERR("fail to initial sensor (%d)\n", err);
+ goto err_sensor_setup;
+ }
+
+#if PS_INTERRUPT_MODE
+ err = setup_interrupt(epld);
+ if (err < 0)
+ {
+ LOG_ERR("setup error!\n");
+ goto err_sensor_setup;
+ }
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ epld->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ epld->early_suspend.suspend = elan_sensor_early_suspend;
+ epld->early_suspend.resume = elan_sensor_late_resume;
+ register_early_suspend(&epld->early_suspend);
+#endif
+
+#if 0
+ wake_lock_init(&g_ps_wlock, WAKE_LOCK_SUSPEND, "ps_wakelock");
+
+ sensor_dev = platform_device_register_simple("elan_alsps", -1, NULL, 0);
+ if (IS_ERR(sensor_dev))
+ {
+ printk ("sensor_dev_init: error\n");
+ goto err_fail;
+ }
+
+ err = sysfs_create_group(&sensor_dev->dev.kobj, &ets_attr_group);
+ if (err !=0)
+ {
+ dev_err(&client->dev,"%s:create sysfs group error", __func__);
+ goto err_fail;
+ }
+#endif
+
+ LOG_INFO("sensor probe success.\n");
+
+ return err;
+
+err_fail:
+ //input_unregister_device(epld->als_input_dev);
+ input_unregister_device(epld->ps_input_dev);
+ //input_free_device(epld->als_input_dev);
+ input_free_device(epld->ps_input_dev);
+//err_lightsensor_setup:
+err_psensor_setup:
+err_sensor_setup:
+ destroy_workqueue(epld->epl_wq);
+ misc_deregister(&elan_ps_device);
+// misc_deregister(&elan_als_device); //ices add
+err_create_singlethread_workqueue:
+exit_read_chipid_failed:
+i2c_fail:
+//err_platform_data_null:
+ kfree(epld);
+exit_kzalloc_epld_failed:
+#ifdef CONFIG_OF
+exit_irq_gpio_read_fail:
+exit_allocate_pdata_failed:
+#endif
+ return err;
+}
+
+static int elan_sensor_remove(struct i2c_client *client)
+{
+ struct elan_epl_data *epld = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "%s: enter.\n", __func__);
+
+ unregister_early_suspend(&epld->early_suspend);
+ //input_unregister_device(epld->als_input_dev);
+ input_unregister_device(epld->ps_input_dev);
+ //input_free_device(epld->als_input_dev);
+ input_free_device(epld->ps_input_dev);
+ misc_deregister(&elan_ps_device);
+// misc_deregister(&elan_als_device); //ices add
+ free_irq(epld->irq,epld);
+ destroy_workqueue(epld->epl_wq);
+ kfree(epld);
+ return 0;
+}
+
+static const struct i2c_device_id elan_sensor_id[] =
+{
+ { EPL2182_PLS_DEVICE, 0 },
+ { }
+};
+
+static const struct of_device_id epl2182_of_match[] = {
+ { .compatible = "ELAN,epl2182_pls", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, epl2182_of_match);
+
+static struct i2c_driver elan_sensor_driver =
+{
+ .probe = elan_sensor_probe,
+ .remove = elan_sensor_remove,
+ .id_table = elan_sensor_id,
+ .driver = {
+ .name = EPL2182_PLS_DEVICE,
+ .owner = THIS_MODULE,
+ .of_match_table = epl2182_of_match,
+ },
+#ifdef CONFIG_SUSPEND
+ .suspend = elan_sensor_suspend,
+ .resume = elan_sensor_resume,
+#endif
+};
+
+static int __init elan_sensor_init(void)
+{
+ return i2c_add_driver(&elan_sensor_driver);
+}
+
+static void __exit elan_sensor_exit(void)
+{
+ i2c_del_driver(&elan_sensor_driver);
+}
+
+module_init(elan_sensor_init);
+module_exit(elan_sensor_exit);
+
+MODULE_AUTHOR("Renato Pan <renato.pan@eminent-tek.com>");
+MODULE_DESCRIPTION("ELAN epl2182 driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/misc/epl259x.c b/drivers/input/misc/epl259x.c
new file mode 100644
index 00000000..81bcd79a
--- /dev/null
+++ b/drivers/input/misc/epl259x.c
@@ -0,0 +1,4645 @@
+/* drivers/i2c/chips/epl259x.c - light and proxmity sensors driver
+ * Copyright (C) 2014 ELAN Corporation.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+#include <linux/hrtimer.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <asm/uaccess.h>
+//#include <asm/mach-types.h>
+#include <asm/setup.h>
+#include <linux/wakelock.h>
+#include <linux/jiffies.h>
+#include <linux/i2c/epl259x.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/platform_device.h>
+
+/******************************************************************************
+* configuration
+*******************************************************************************/
+#define ALS_POLLING_MODE 1 // 1 is polling mode, 0 is interrupt mode
+#define PS_POLLING_MODE 0 // 1 is polling mode, 0 is interrupt mode
+
+#define ALS_LOW_THRESHOLD 1000
+#define ALS_HIGH_THRESHOLD 3000
+
+#define PS_LOW_THRESHOLD 3000
+#define PS_HIGH_THRESHOLD 4000
+
+#define LUX_PER_COUNT 700
+
+//platform select
+#define S5PV210 0
+#define SPREAD 1
+#define QCOM 0
+#define LEADCORE 0
+#define MARVELL 0
+
+//#define ELAN_INT_PIN 140 /*Interrupt pin setting*/
+
+//debug message
+#define COMMON_DEBUG 0
+#define ALS_DEBUG 0
+#define PS_DEBUG 1
+#define SHOW_DBG 0
+
+#define ALS_DYN_INTT 0
+#define PS_DYN_K 0
+#define PS_DYN_K_STR 0
+#define HS_ENABLE 0
+#define PS_GES 0
+
+#define ALS_LEVEL 16
+static int polling_time = 200;
+static struct i2c_client *this_client = NULL;
+static int als_level[] = {20, 45, 70, 90, 150, 300, 500, 700, 1150, 2250, 4500, 8000, 15000, 30000, 50000};
+static int als_value[] = {10, 30, 60, 80, 100, 200, 400, 600, 800, 1500, 3000, 6000, 10000, 20000, 40000, 60000};
+
+#if ALS_DYN_INTT
+//Dynamic INTT
+int dynamic_intt_idx;
+int dynamic_intt_init_idx = 1; //initial dynamic_intt_idx
+int c_gain;
+int dynamic_intt_lux = 0;
+
+uint16_t dynamic_intt_high_thr;
+uint16_t dynamic_intt_low_thr;
+uint32_t dynamic_intt_max_lux = 12000;
+uint32_t dynamic_intt_min_lux = 0;
+uint32_t dynamic_intt_min_unit = 1000;
+
+static int als_dynamic_intt_intt[] = {EPL_ALS_INTT_8192, EPL_ALS_INTT_64};
+static int als_dynamic_intt_value[] = {8192, 64};
+static int als_dynamic_intt_gain[] = {EPL_GAIN_MID, EPL_GAIN_MID};
+static int als_dynamic_intt_high_thr[] = {60000, 53000};
+static int als_dynamic_intt_low_thr[] = {200, 300};
+static int als_dynamic_intt_intt_num = sizeof(als_dynamic_intt_value)/sizeof(int);
+#endif
+
+#if ALS_DYN_INTT
+typedef enum
+{
+ CMC_BIT_LSRC_NON = 0x0,
+ CMC_BIT_LSRC_SCALE = 0x1,
+ CMC_BIT_LSRC_SLOPE = 0x2,
+ CMC_BIT_LSRC_BOTH = 0x3,
+} CMC_LSRC_REPORT_TYPE;
+#endif
+
+#if PS_DYN_K
+static int dynk_polling_delay = 200;
+int dynk_min_ps_raw_data;
+int dynk_max_ir_data;
+
+u32 dynk_thd_low = 0;
+u32 dynk_thd_high = 0;
+
+int dynk_low_offset;
+int dynk_high_offset;
+
+bool dynk_change_flag = false;
+u16 dynk_change_thd_max;
+u16 dynk_thd_offset;
+
+u8 dynk_last_status = 0;
+
+#if PS_DYN_K_STR
+bool dynk_enhance_flag = true;
+u8 dynk_enhance_integration_time;
+u8 dynk_enhance_gain;
+u8 dynk_enhance_adc;
+u16 dynk_enhance_ch0;
+u16 dynk_enhance_ch1;
+u16 dynk_enhance_max_ch0;
+#endif
+
+#endif
+
+#if HS_ENABLE
+static struct mutex hs_sensor_mutex;
+static bool hs_enable_flag = false;
+#endif
+
+#if PS_GES
+static bool ps_ges_enable_flag = false;
+u16 ges_threshold_low = 1000;
+u16 ges_threshold_high = 1500;
+#define KEYCODE_LEFT KEY_LEFTALT
+bool ps_ges_suspend_flag = false;
+#endif
+
+bool polling_flag = true;
+bool eint_flag = true;
+
+//ps calibration file location
+static const char ps_cal_file[]="/data/data/com.eminent.ps.calibration/ps.dat";
+
+//als calibration file location
+static const char als_cal_file[]="/data/data/com.eminent.ps.calibration/als.dat";
+
+static int PS_h_offset = 2000;
+static int PS_l_offset = 1000;
+static int PS_MAX_XTALK = 30000;
+
+int als_frame_time = 0;
+int ps_frame_time = 0;
+/******************************************************************************
+*******************************************************************************/
+
+#define TXBYTES 2
+#define RXBYTES 2
+
+#define PACKAGE_SIZE 8
+#define I2C_RETRY_COUNT 10
+#define EPL_DEV_NAME "epl2182_pls"
+#define DRIVER_VERSION "1.0.3"
+//struct timeval ges_enable_time;
+
+//int ges_frame_count;
+
+typedef enum
+{
+ CMC_BIT_RAW = 0x0,
+ CMC_BIT_PRE_COUNT = 0x1,
+ CMC_BIT_DYN_INT = 0x2,
+ CMC_BIT_DEF_LIGHT = 0x4,
+ CMC_BIT_TABLE = 0x8,
+} CMC_ALS_REPORT_TYPE;
+
+typedef struct _epl_raw_data
+{
+ u8 raw_bytes[PACKAGE_SIZE];
+ u16 renvo;
+} epl_raw_data;
+
+struct epl_sensor_priv
+{
+ struct i2c_client *client;
+ struct input_dev *als_input_dev;
+ struct input_dev *ps_input_dev;
+ struct workqueue_struct *epl_wq;
+ struct delayed_work eint_work;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+ struct early_suspend early_suspend;
+#endif
+ int intr_pin;
+ int (*power)(int on);
+
+ int ps_opened;
+ int als_opened;
+
+ int als_suspend;
+ int ps_suspend;
+
+ int lux_per_count;
+
+ int enable_pflag;
+ int enable_lflag;
+#if HS_ENABLE
+ int enable_hflag;
+ int hs_suspend;
+#endif
+#if PS_GES
+ struct input_dev *gs_input_dev;
+ int enable_gflag;
+ int ges_suspend;
+#endif
+ int read_flag;
+ int irq;
+ spinlock_t lock;
+
+#if ALS_DYN_INTT
+ uint32_t ratio;
+ uint32_t last_ratio;
+ int c_gain_h; // fluorescent (C1)
+ int c_gain_l; // incandescent (C2)
+ uint32_t lsource_thd_high; //different light source boundary (N) fluorescent (C1)
+ uint32_t lsource_thd_low; //different light source boundary (N) incandescent (C2)
+#endif
+
+ /*data*/
+ u16 als_level_num;
+ u16 als_value_num;
+ u32 als_level[ALS_LEVEL-1];
+ u32 als_value[ALS_LEVEL];
+} ;
+
+static struct platform_device *sensor_dev;
+struct epl_sensor_priv *epl_sensor_obj;
+static epl_optical_sensor epl_sensor;
+int i2c_max_count=8;
+
+static epl_raw_data gRawData;
+
+static struct wake_lock ps_lock;
+static struct mutex sensor_mutex;
+
+#if S5PV210
+static const char ElanPsensorName[]="proximity";
+static const char ElanALsensorName[]="lightsensor-level";
+#elif SPREAD
+static const char ElanPsensorName[] = "proximity";
+#elif QCOM || LEADCORE
+static const char ElanPsensorName[] = "proximity";
+static const char ElanALsensorName[] = "light";
+#elif MARVELL
+static const char ElanPsensorName[] = "alps_pxy";
+#endif
+
+#define LOG_TAG "[EPL259x] "
+#define LOG_FUN(f) printk(KERN_INFO LOG_TAG"%s\n", __FUNCTION__)
+#define LOG_INFO(fmt, args...) printk(KERN_INFO LOG_TAG fmt, ##args)
+#define LOG_ERR(fmt, args...) printk(KERN_ERR LOG_TAG"%s %d : "fmt, __FUNCTION__, __LINE__, ##args)
+
+
+void epl_sensor_update_mode(struct i2c_client *client);
+int epl_sensor_read_als_status(struct i2c_client *client);
+static int epl_sensor_setup_interrupt(struct epl_sensor_priv *epld);
+static int ps_sensing_time(int intt, int adc, int cycle);
+static int als_sensing_time(int intt, int adc, int cycle);
+
+static void epl_sensor_eint_work(struct work_struct *work);
+//static DECLARE_WORK(epl_sensor_irq_work, epl_sensor_eint_work);
+
+static void epl_sensor_polling_work(struct work_struct *work);
+static DECLARE_DELAYED_WORK(polling_work, epl_sensor_polling_work);
+
+#if PS_DYN_K
+void epl_sensor_dynk_thd_polling_work(struct work_struct *work);
+void epl_sensor_restart_dynk_polling(void);
+static DECLARE_DELAYED_WORK(dynk_thd_polling_work, epl_sensor_dynk_thd_polling_work);
+#endif
+
+
+
+
+/*
+//====================I2C write operation===============//
+*/
+static int epl_sensor_I2C_Write_Cmd(struct i2c_client *client, uint8_t regaddr, uint8_t data, uint8_t txbyte)
+{
+ uint8_t buffer[2];
+ int ret = 0;
+ int retry;
+
+ buffer[0] = regaddr ;
+ buffer[1] = data;
+
+ for(retry = 0; retry < I2C_RETRY_COUNT; retry++)
+ {
+ ret = i2c_master_send(client, buffer, txbyte);
+
+ if (ret == txbyte)
+ {
+ break;
+ }
+
+ LOG_ERR("i2c write error,TXBYTES %d\n",ret);
+ mdelay(10);
+ }
+
+
+ if(retry>=I2C_RETRY_COUNT)
+ {
+ LOG_ERR("i2c write retry over %d\n", I2C_RETRY_COUNT);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int epl_sensor_I2C_Write(struct i2c_client *client, uint8_t regaddr, uint8_t data)
+{
+ int ret = 0;
+ ret = epl_sensor_I2C_Write_Cmd(client, regaddr, data, 0x02);
+ return ret;
+ return 0;
+}
+/*----------------------------------------------------------------------------*/
+static int epl_sensor_I2C_Read(struct i2c_client *client, uint8_t regaddr, uint8_t bytecount)
+{
+
+ int ret = 0;
+
+
+ int retry;
+ int read_count=0, rx_count=0;
+
+ while(bytecount>0)
+ {
+ epl_sensor_I2C_Write_Cmd(client, regaddr+read_count, 0x00, 0x01);
+
+ for(retry = 0; retry < I2C_RETRY_COUNT; retry++)
+ {
+ rx_count = bytecount>i2c_max_count?i2c_max_count:bytecount;
+ ret = i2c_master_recv(client, &gRawData.raw_bytes[read_count], rx_count);
+
+ if (ret == rx_count)
+ break;
+
+ LOG_ERR("i2c read error,RXBYTES %d\r\n",ret);
+ mdelay(10);
+ }
+
+ if(retry>=I2C_RETRY_COUNT)
+ {
+ LOG_ERR("i2c read retry over %d\n", I2C_RETRY_COUNT);
+ return -EINVAL;
+ }
+ bytecount-=rx_count;
+ read_count+=rx_count;
+ }
+
+ return ret;
+}
+
+/*----------------------------------------------------------------------------*/
+static void epl_sensor_restart_polling(void)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+ cancel_delayed_work(&polling_work);
+ queue_delayed_work(epld->epl_wq, &polling_work,msecs_to_jiffies(polling_time));
+
+}
+/*----------------------------------------------------------------------------*/
+
+#if PS_GES
+static void epl_sensor_notify_event(void)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ struct input_dev *idev = epld->gs_input_dev;
+
+ LOG_INFO(" --> LEFT\n\n");
+ input_report_key(idev, KEYCODE_LEFT, 1);
+ input_report_key(idev, KEYCODE_LEFT, 0);
+ input_sync(idev);
+}
+#endif
+
+/*----------------------------------------------------------------------------*/
+static void epl_sensor_report_lux(int repott_lux)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+ LOG_INFO("------------------- ALS raw = %d, lux = %d\n\n",epl_sensor.als.data.channels[1], repott_lux);
+#if SPREAD || MARVELL
+ input_report_abs(epld->ps_input_dev, ABS_MISC, repott_lux);
+ input_sync(epld->ps_input_dev);
+#else
+ input_report_abs(epld->als_input_dev, ABS_MISC, repott_lux);
+ input_sync(epld->als_input_dev);
+#endif
+}
+/*----------------------------------------------------------------------------*/
+#if ALS_DYN_INTT
+long raw_convert_to_lux(u16 raw_data)
+{
+ long lux = 0;
+
+ lux = raw_data * (c_gain / als_dynamic_intt_value[dynamic_intt_idx]);
+#if ALS_DEBUG
+ LOG_INFO("[%s]:raw_data=%d, lux=%ld\r\n", __func__, raw_data, lux);
+#endif
+
+ if(lux >= (dynamic_intt_max_lux * dynamic_intt_min_unit)){
+#if ALS_DEBUG
+ LOG_INFO("[%s]:raw_convert_to_lux: change max lux\r\n", __func__);
+#endif
+ lux = dynamic_intt_max_lux * dynamic_intt_min_unit;
+ }
+ else if(lux <= (dynamic_intt_min_lux*dynamic_intt_min_unit)){
+#if ALS_DEBUG
+ LOG_INFO("[%s]:raw_convert_to_lux: change min lux\r\n", __func__);
+#endif
+ lux = dynamic_intt_min_lux * dynamic_intt_min_unit;
+ }
+
+ return lux;
+}
+#endif
+/*----------------------------------------------------------------------------*/
+static int epl_sensor_get_als_value(struct epl_sensor_priv *obj, u16 als)
+{
+ int idx;
+ int invalid = 0;
+#if ALS_DYN_INTT
+ long now_lux=0, lux_tmp=0;
+ bool change_flag = false;
+#endif
+ switch(epl_sensor.als.report_type)
+ {
+ case CMC_BIT_RAW:
+ return als;
+ break;
+
+ case CMC_BIT_PRE_COUNT:
+ return (als * epl_sensor.als.factory.lux_per_count)/1000;
+ break;
+
+ case CMC_BIT_TABLE:
+ for(idx = 0; idx < obj->als_level_num; idx++)
+ {
+ if(als < als_level[idx])
+ {
+ break;
+ }
+ }
+
+ if(idx >= obj->als_value_num)
+ {
+ LOG_ERR("exceed range\n");
+ idx = obj->als_value_num - 1;
+ }
+
+ if(!invalid)
+ {
+ LOG_INFO("ALS: %05d => %05d\n", als, als_value[idx]);
+ return als_value[idx];
+ }
+ else
+ {
+ LOG_ERR("ALS: %05d => %05d (-1)\n", als, als_value[idx]);
+ return als;
+ }
+ break;
+#if ALS_DYN_INTT
+ case CMC_BIT_DYN_INT:
+
+ if(epl_sensor.als.lsrc_type != CMC_BIT_LSRC_NON)
+ {
+ long luxratio = 0;
+ epl_sensor_read_als_status(obj->client);
+
+ if (epl_sensor.als.data.channels[0] == 0)
+ {
+ epl_sensor.als.data.channels[0] = 1;
+ LOG_ERR("[%s]:read ch0 data is 0 \r\n", __func__);
+ }
+
+ luxratio = (long)((als*dynamic_intt_min_unit) / epl_sensor.als.data.channels[0]); //lux ratio (A=CH1/CH0)
+
+ obj->ratio = luxratio;
+ if((epl_sensor.als.saturation >> 5) == 0)
+ {
+ if(epl_sensor.als.lsrc_type == CMC_BIT_LSRC_SCALE || epl_sensor.als.lsrc_type == CMC_BIT_LSRC_BOTH)
+ {
+ if(obj->ratio == 0){
+ obj->last_ratio = luxratio;
+ }
+ else{
+ obj->last_ratio = (luxratio + obj->last_ratio*9) / 10;
+ }
+
+ if (obj->last_ratio >= obj->lsource_thd_high) //fluorescent (C1)
+ {
+ c_gain = obj->c_gain_h;
+ }
+ else if (obj->last_ratio <= obj->lsource_thd_low) //incandescent (C2)
+ {
+ c_gain = obj->c_gain_l;
+ }
+ else if(epl_sensor.als.lsrc_type == CMC_BIT_LSRC_BOTH)
+ {
+ int a = 0, b = 0, c = 0;
+ a = (obj->c_gain_h - obj->c_gain_l) * dynamic_intt_min_unit / (obj->lsource_thd_high - obj->lsource_thd_low);
+ b = (obj->c_gain_h) - ((a * obj->lsource_thd_high)/dynamic_intt_min_unit );
+ c = ((a * obj->last_ratio)/dynamic_intt_min_unit) + b;
+ if(c > obj->c_gain_h)
+ c_gain = obj->c_gain_h;
+ else if (c < obj->c_gain_l)
+ c_gain = obj->c_gain_l;
+ else
+ c_gain = c;
+ }
+ }
+ else if(epl_sensor.als.lsrc_type == CMC_BIT_LSRC_SLOPE)
+ {
+ if (luxratio >= obj->lsource_thd_high) //fluorescent (C1)
+ {
+ c_gain = obj->c_gain_h;
+ }
+ else if (luxratio <= obj->lsource_thd_low) //incandescent (C2)
+ {
+ c_gain = obj->c_gain_l;
+ }
+ else{ /*mix*/
+ int a = 0, b = 0, c = 0;
+ a = (obj->c_gain_h - obj->c_gain_l) * dynamic_intt_min_unit / (obj->lsource_thd_high - obj->lsource_thd_low);
+ b = (obj->c_gain_h) - ((a * obj->lsource_thd_high)/dynamic_intt_min_unit );
+ c = ((a * luxratio)/dynamic_intt_min_unit) + b;
+ if(c > obj->c_gain_h)
+ c_gain = obj->c_gain_h;
+ else if (c < obj->c_gain_l)
+ c_gain = obj->c_gain_l;
+ else
+ c_gain = c;
+ }
+ }
+ LOG_INFO("[%s]:ch0=%d, ch1=%d, c_gain=%d, obj->ratio=%d, obj->last_ratio=%d \r\n\n",
+ __func__,epl_sensor.als.data.channels[0], als, c_gain, obj->ratio, obj->last_ratio);
+ }
+ else
+ {
+ LOG_INFO("[%s]: ALS saturation(%d) \r\n", __func__, (epl_sensor.als.saturation >> 5));
+ }
+ }
+
+#if ALS_DEBUG
+ LOG_INFO("[%s]: dynamic_intt_idx=%d, als_dynamic_intt_value=%d, dynamic_intt_gain=%d, als=%d \r\n",
+ __func__, dynamic_intt_idx, als_dynamic_intt_value[dynamic_intt_idx], als_dynamic_intt_gain[dynamic_intt_idx], als);
+#endif
+
+ if(als > dynamic_intt_high_thr)
+ {
+ if(dynamic_intt_idx == (als_dynamic_intt_intt_num - 1)){
+ als = dynamic_intt_high_thr;
+ lux_tmp = raw_convert_to_lux(als);
+#if ALS_DEBUG
+ LOG_INFO(">>>>>>>>>>>>>>>>>>>>>>>> INTT_MAX_LUX\r\n");
+#endif
+ }
+ else{
+ change_flag = true;
+ als = dynamic_intt_high_thr;
+ lux_tmp = raw_convert_to_lux(als);
+ dynamic_intt_idx++;
+#if ALS_DEBUG
+ LOG_INFO(">>>>>>>>>>>>>>>>>>>>>>>>change INTT high: %d, raw: %d \r\n", dynamic_intt_idx, als);
+#endif
+ }
+ }
+ else if(als < dynamic_intt_low_thr)
+ {
+ if(dynamic_intt_idx == 0){
+ //als = dynamic_intt_low_thr;
+ lux_tmp = raw_convert_to_lux(als);
+#if ALS_DEBUG
+ LOG_INFO(">>>>>>>>>>>>>>>>>>>>>>>> INTT_MIN_LUX\r\n");
+#endif
+ }
+ else{
+ change_flag = true;
+ als = dynamic_intt_low_thr;
+ lux_tmp = raw_convert_to_lux(als);
+ dynamic_intt_idx--;
+#if ALS_DEBUG
+ LOG_INFO(">>>>>>>>>>>>>>>>>>>>>>>>change INTT low: %d, raw: %d \r\n", dynamic_intt_idx, als);
+#endif
+ }
+ }
+ else
+ {
+ lux_tmp = raw_convert_to_lux(als);
+ }
+
+ now_lux = lux_tmp;
+ dynamic_intt_lux = now_lux/dynamic_intt_min_unit;
+
+ epl_sensor_report_lux(dynamic_intt_lux);
+
+ if(change_flag == true)
+ {
+ epl_sensor.als.integration_time = als_dynamic_intt_intt[dynamic_intt_idx];
+ epl_sensor.als.gain = als_dynamic_intt_gain[dynamic_intt_idx];
+ dynamic_intt_high_thr = als_dynamic_intt_high_thr[dynamic_intt_idx];
+ dynamic_intt_low_thr = als_dynamic_intt_low_thr[dynamic_intt_idx];
+ epl_sensor_update_mode(obj->client);
+ change_flag = false;
+ }
+ return dynamic_intt_lux;
+
+ break;
+#endif
+
+ }
+ return 0;
+}
+/*----------------------------------------------------------------------------*/
+
+int epl_sensor_read_als(struct i2c_client *client)
+{
+ struct epl_sensor_priv *epld = i2c_get_clientdata(client);
+
+ if(client == NULL)
+ {
+ LOG_ERR("CLIENT CANN'T EQUL NULL\n");
+ return -1;
+ }
+
+ epl_sensor_I2C_Read(epld->client, 0x13, 4);
+#if PS_DYN_K && PS_DYN_K_STR
+ if(dynk_enhance_flag == false)
+ {
+ dynk_enhance_ch0 = (gRawData.raw_bytes[1]<<8) | gRawData.raw_bytes[0];
+ dynk_enhance_ch1 = (gRawData.raw_bytes[3]<<8) | gRawData.raw_bytes[2];
+
+ LOG_INFO("read dynk_enhance_ch0 = %d\n", dynk_enhance_ch0);
+ LOG_INFO("read dynk_enhance_ch1 = %d\n", dynk_enhance_ch1);
+ }
+ else
+#endif
+ {
+ epl_sensor.als.data.channels[0] = (gRawData.raw_bytes[1]<<8) | gRawData.raw_bytes[0];
+ epl_sensor.als.data.channels[1] = (gRawData.raw_bytes[3]<<8) | gRawData.raw_bytes[2];
+
+#if ALS_DEBUG
+ LOG_INFO("read als channel 0 = %d\n", epl_sensor.als.data.channels[0]);
+ LOG_INFO("read als channel 1 = %d\n", epl_sensor.als.data.channels[1]);
+#endif
+ }
+
+
+ if(epl_sensor.wait == EPL_WAIT_SINGLE)
+ epl_sensor_I2C_Write(epld->client,0x11, epl_sensor.power | epl_sensor.reset);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void epl_sensor_report_ps_status(void)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+
+ LOG_INFO("------------------- epl_sensor.ps.data.data=%d, value=%d \n\n", epl_sensor.ps.data.data, epl_sensor.ps.compare_low >> 3);
+
+ input_report_abs(epld->ps_input_dev, ABS_DISTANCE, epl_sensor.ps.compare_low >> 3);
+ input_sync(epld->ps_input_dev);
+}
+
+int epl_sensor_read_ps(struct i2c_client *client)
+{
+ struct epl_sensor_priv *epld = i2c_get_clientdata(client);
+ if(client == NULL)
+ {
+ LOG_ERR("CLIENT CANN'T EQUL NULL\n");
+ return -1;
+ }
+
+ epl_sensor_I2C_Read(epld->client,0x1c, 4);
+
+ epl_sensor.ps.data.ir_data = (gRawData.raw_bytes[1]<<8) | gRawData.raw_bytes[0];
+ epl_sensor.ps.data.data = (gRawData.raw_bytes[3]<<8) | gRawData.raw_bytes[2];
+
+ LOG_INFO("[%s] data = %d\n", __FUNCTION__, epl_sensor.ps.data.data);
+ LOG_INFO("[%s] ir data = %d\n", __FUNCTION__, epl_sensor.ps.data.ir_data);
+
+ if(epl_sensor.wait == EPL_WAIT_SINGLE)
+ epl_sensor_I2C_Write(epld->client, 0x11, epl_sensor.power | epl_sensor.reset);
+
+ return 0;
+}
+
+int epl_sensor_read_ps_status(struct i2c_client *client)
+{
+ u8 buf;
+#if PS_GES
+ struct epl_sensor_priv *obj = epl_sensor_obj;
+ u8 new_ps_state;
+ u8 ges_saturation;
+ bool enable_ges = obj->enable_gflag==1 && obj->ges_suspend==0;
+#endif
+ if(client == NULL)
+ {
+ LOG_ERR("CLIENT CANN'T EQUL NULL\n");
+ return -1;
+ }
+
+ epl_sensor_I2C_Read(client, 0x1b, 1);
+
+ buf = gRawData.raw_bytes[0];
+
+#if PS_GES
+ ges_saturation = (buf & 0x20);
+ new_ps_state = (buf & 0x08) >> 3;
+ LOG_INFO("[%s]:new_ps_state=%d, ps_ges_suspend_flag=%d \r\n", __func__, new_ps_state, ps_ges_suspend_flag);
+ if(enable_ges == 1 && epl_sensor.ges.polling_mode == 1)
+ {
+ if( new_ps_state == 0 && (epl_sensor.ps.compare_low>>3 == 1) && ps_ges_suspend_flag == false && ges_saturation == 0)
+ epl_sensor_notify_event();
+ }
+ else if(enable_ges == 1 && epl_sensor.ges.polling_mode == 0 && ps_ges_suspend_flag == false && ges_saturation == 0)
+ {
+ if(new_ps_state == 0)
+ epl_sensor_notify_event();
+ }
+#endif
+
+ epl_sensor.ps.saturation = (buf & 0x20);
+ epl_sensor.ps.compare_high = (buf & 0x10);
+ epl_sensor.ps.compare_low = (buf & 0x08);
+ epl_sensor.ps.interrupt_flag = (buf & 0x04);
+ epl_sensor.ps.compare_reset = (buf & 0x02);
+ epl_sensor.ps.lock= (buf & 0x01);
+#if PS_DEBUG
+ LOG_INFO("ps: ~~~~ PS ~~~~~ \n");
+ LOG_INFO("ps: buf = 0x%x\n", buf);
+ LOG_INFO("ps: sat = 0x%x\n", epl_sensor.ps.saturation);
+ LOG_INFO("ps: cmp h = 0x%x, l = 0x%x\n", epl_sensor.ps.compare_high, epl_sensor.ps.compare_low);
+ LOG_INFO("ps: int_flag = 0x%x\n",epl_sensor.ps.interrupt_flag);
+ LOG_INFO("ps: cmp_rstn = 0x%x, lock = %x\n", epl_sensor.ps.compare_reset, epl_sensor.ps.lock);
+#endif
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int set_psensor_intr_threshold(uint16_t low_thd, uint16_t high_thd)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ struct i2c_client *client = epld->client;
+ uint8_t high_msb ,high_lsb, low_msb, low_lsb;
+
+
+ high_msb = (uint8_t) (high_thd >> 8);
+ high_lsb = (uint8_t) (high_thd & 0x00ff);
+ low_msb = (uint8_t) (low_thd >> 8);
+ low_lsb = (uint8_t) (low_thd & 0x00ff);
+
+ LOG_INFO("%s: low_thd = %d, high_thd = %d \n",__FUNCTION__, low_thd, high_thd);
+
+ epl_sensor_I2C_Write(client,0x0c,low_lsb);
+ epl_sensor_I2C_Write(client,0x0d,low_msb);
+ epl_sensor_I2C_Write(client,0x0e,high_lsb);
+ epl_sensor_I2C_Write(client,0x0f,high_msb);
+
+ return 0;
+}
+
+static int set_lsensor_intr_threshold(uint16_t low_thd, uint16_t high_thd)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ struct i2c_client *client = epld->client;
+ uint8_t high_msb ,high_lsb, low_msb, low_lsb;
+
+ high_msb = (uint8_t) (high_thd >> 8);
+ high_lsb = (uint8_t) (high_thd & 0x00ff);
+ low_msb = (uint8_t) (low_thd >> 8);
+ low_lsb = (uint8_t) (low_thd & 0x00ff);
+
+ epl_sensor_I2C_Write(client,0x08,low_lsb);
+ epl_sensor_I2C_Write(client,0x09,low_msb);
+ epl_sensor_I2C_Write(client,0x0a,high_lsb);
+ epl_sensor_I2C_Write(client,0x0b,high_msb);
+
+ LOG_INFO("%s: low_thd = %d, high_thd = %d \n",__FUNCTION__, low_thd, high_thd);
+
+ return 0;
+}
+
+int epl_sensor_read_als_status(struct i2c_client *client)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ u8 buf;
+
+ if(client == NULL)
+ {
+ LOG_ERR("CLIENT CANN'T EQUL NULL\n");
+ return -1;
+ }
+
+ epl_sensor_I2C_Read(epld->client, 0x12, 1);
+
+ buf = gRawData.raw_bytes[0];
+
+ epl_sensor.als.saturation = (buf & 0x20);
+ epl_sensor.als.compare_high = (buf & 0x10);
+ epl_sensor.als.compare_low = (buf & 0x08);
+ epl_sensor.als.interrupt_flag = (buf & 0x04);
+ epl_sensor.als.compare_reset = (buf & 0x02);
+ epl_sensor.als.lock= (buf & 0x01);
+#if ALS_DEBUG
+ LOG_INFO("als: ~~~~ ALS ~~~~~ \n");
+ LOG_INFO("als: buf = 0x%x\n", buf);
+ LOG_INFO("als: sat = 0x%x\n", epl_sensor.als.saturation);
+ LOG_INFO("als: cmp h = 0x%x, l = 0x%x\n", epl_sensor.als.compare_high, epl_sensor.als.compare_low);
+ LOG_INFO("als: int_flag = 0x%x\n",epl_sensor.als.interrupt_flag);
+ LOG_INFO("als: cmp_rstn = 0x%x, lock = 0x%x\n", epl_sensor.als.compare_reset, epl_sensor.als.lock);
+#endif
+ return 0;
+}
+
+static int write_factory_calibration(struct epl_sensor_priv *epl_data, char* ps_data, int ps_cal_len)
+{
+ struct file *fp_cal;
+
+ mm_segment_t fs;
+ loff_t pos;
+
+ LOG_FUN();
+ pos = 0;
+
+ fp_cal = filp_open(ps_cal_file, O_CREAT|O_RDWR|O_TRUNC, 0755/*S_IRWXU*/);
+ if (IS_ERR(fp_cal))
+ {
+ LOG_ERR("[ELAN]create file_h error\n");
+ return -1;
+ }
+
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ vfs_write(fp_cal, ps_data, ps_cal_len, &pos);
+
+ filp_close(fp_cal, NULL);
+
+ set_fs(fs);
+
+ return 0;
+}
+
+static bool read_factory_calibration(void)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ struct file *fp;
+ mm_segment_t fs;
+ loff_t pos;
+ char buffer[100]= {0};
+ if(epl_sensor.ps.factory.calibration_enable && !epl_sensor.ps.factory.calibrated)
+ {
+ fp = filp_open(ps_cal_file, O_RDWR, S_IRUSR);
+
+ if (IS_ERR(fp))
+ {
+ LOG_ERR("NO PS calibration file(%d)\n", (int)IS_ERR(fp));
+ epl_sensor.ps.factory.calibration_enable = false;
+ }
+ else
+ {
+ int ps_cancelation = 0, ps_hthr = 0, ps_lthr = 0;
+ pos = 0;
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ vfs_read(fp, buffer, sizeof(buffer), &pos);
+ filp_close(fp, NULL);
+
+ sscanf(buffer, "%d,%d,%d", &ps_cancelation, &ps_hthr, &ps_lthr);
+ epl_sensor.ps.factory.cancelation = ps_cancelation;
+ epl_sensor.ps.factory.high_threshold = ps_hthr;
+ epl_sensor.ps.factory.low_threshold = ps_lthr;
+ set_fs(fs);
+
+ epl_sensor.ps.high_threshold = epl_sensor.ps.factory.high_threshold;
+ epl_sensor.ps.low_threshold = epl_sensor.ps.factory.low_threshold;
+ epl_sensor.ps.cancelation = epl_sensor.ps.factory.cancelation;
+ }
+
+ epl_sensor_I2C_Write(epld->client,0x22, (u8)(epl_sensor.ps.cancelation& 0xff));
+ epl_sensor_I2C_Write(epld->client,0x23, (u8)((epl_sensor.ps.cancelation & 0xff00) >> 8));
+ set_psensor_intr_threshold(epl_sensor.ps.low_threshold, epl_sensor.ps.high_threshold);
+
+ epl_sensor.ps.factory.calibrated = true;
+ }
+
+ if(epl_sensor.als.factory.calibration_enable && !epl_sensor.als.factory.calibrated)
+ {
+ fp = filp_open(als_cal_file, O_RDONLY, S_IRUSR);
+ if (IS_ERR(fp))
+ {
+ LOG_ERR("NO ALS calibration file(%d)\n", (int)IS_ERR(fp));
+ epl_sensor.als.factory.calibration_enable = false;
+ }
+ else
+ {
+ int als_lux_per_count = 0;
+ pos = 0;
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+ vfs_read(fp, buffer, sizeof(buffer), &pos);
+ filp_close(fp, NULL);
+
+ sscanf(buffer, "%d", &als_lux_per_count);
+ epl_sensor.als.factory.lux_per_count = als_lux_per_count;
+ set_fs(fs);
+ }
+ epl_sensor.als.factory.calibrated = true;
+ }
+ return true;
+}
+
+static int epl_run_ps_calibration(struct epl_sensor_priv *epl_data)
+{
+ struct epl_sensor_priv *epld = epl_data;
+ bool enable_ps = epld->enable_pflag==1 && epld->ps_suspend==0;
+ u16 ch1=0;
+ u32 ch1_all=0;
+ int count =5, i;
+ int ps_hthr=0, ps_lthr=0, ps_cancelation=0, ps_cal_len = 0;
+ char ps_calibration[20];
+
+
+ if(PS_MAX_XTALK < 0)
+ {
+ LOG_ERR("[%s]:Failed: PS_MAX_XTALK < 0 \r\n", __func__);
+ return -EINVAL;
+ }
+
+ if(enable_ps == 0)
+ {
+ epld->enable_pflag = 1;
+ epl_sensor_update_mode(epld->client);
+ }
+
+ polling_flag = false;
+
+ for(i=0; i<count; i++)
+ {
+ msleep(50);
+ switch(epl_sensor.mode)
+ {
+ case EPL_MODE_PS:
+ case EPL_MODE_ALS_PS:
+ if(enable_ps == true && polling_flag == true && eint_flag == true && epl_sensor.ps.polling_mode == 0)
+ epl_sensor_read_ps(epld->client);
+ ch1 = epl_sensor.ps.data.data;
+ break;
+ }
+
+ ch1_all = ch1_all + ch1;
+ if(epl_sensor.wait == EPL_WAIT_SINGLE)
+ epl_sensor_I2C_Write(epld->client,0x11, epl_sensor.power | epl_sensor.reset);
+ }
+
+
+ ch1 = (u16)(ch1_all/count);
+
+ if(ch1 > PS_MAX_XTALK)
+ {
+ LOG_ERR("[%s]:Failed: ch1 > max_xtalk(%d) \r\n", __func__, ch1);
+ return -EINVAL;
+ }
+ else if(ch1 <= 0)
+ {
+ LOG_ERR("[%s]:Failed: ch1 = 0\r\n", __func__);
+ return -EINVAL;
+ }
+
+ ps_hthr = ch1 + PS_h_offset;
+ ps_lthr = ch1 + PS_l_offset;
+
+ ps_cal_len = sprintf(ps_calibration, "%d,%d,%d", ps_cancelation, ps_hthr, ps_lthr);
+
+ if(write_factory_calibration(epld, ps_calibration, ps_cal_len) < 0)
+ {
+ LOG_ERR("[%s] create file error \n", __func__);
+ return -EINVAL;
+ }
+
+ epl_sensor.ps.low_threshold = ps_lthr;
+ epl_sensor.ps.high_threshold = ps_hthr;
+ set_psensor_intr_threshold(epl_sensor.ps.low_threshold, epl_sensor.ps.high_threshold);
+
+ LOG_INFO("[%s]: ch1 = %d\n", __func__, ch1);
+
+ polling_flag = true;
+ epl_sensor_restart_polling();
+ return ch1;
+}
+
+/*
+//====================write global variable===============//
+*/
+static void write_global_variable(struct i2c_client *client)
+{
+ u8 buf;
+#if HS_ENABLE
+ struct epl_sensor_priv *obj = epl_sensor_obj;
+ bool enable_hs = obj->enable_hflag==1 && obj->hs_suspend==0;
+#endif
+#if PS_GES
+ bool enable_ges = obj->enable_gflag==1 && obj->ges_suspend==0;
+#endif
+ //wake up chip
+ buf = epl_sensor.reset | epl_sensor.power;
+ epl_sensor_I2C_Write(client,0x11, buf);
+
+ /* read revno*/
+ epl_sensor_I2C_Read(client, 0x20, 2);
+ epl_sensor.revno = gRawData.raw_bytes[0] | gRawData.raw_bytes[1] << 8;
+
+ /*chip refrash*/
+ epl_sensor_I2C_Write(client, 0xfd, 0x8e);
+ epl_sensor_I2C_Write(client, 0xfe, 0x22);
+ epl_sensor_I2C_Write(client, 0xfe, 0x02);
+ epl_sensor_I2C_Write(client, 0xfd, 0x00);
+
+ epl_sensor_I2C_Write(client, 0xfc, EPL_A_D | EPL_NORMAL| EPL_GFIN_ENABLE | EPL_VOS_ENABLE | EPL_DOC_ON);
+
+#if HS_ENABLE
+ if(enable_hs)
+ {
+ epl_sensor.mode = EPL_MODE_PS;
+ epl_sensor_I2C_Write(obj->client, 0x00, epl_sensor.wait | EPL_MODE_IDLE);
+
+ /*hs setting*/
+ buf = epl_sensor.hs.integration_time | epl_sensor.hs.gain;
+ epl_sensor_I2C_Write(client, 0x03, buf);
+
+ buf = epl_sensor.hs.adc | epl_sensor.hs.cycle;
+ epl_sensor_I2C_Write(client, 0x04, buf);
+
+ buf = epl_sensor.hs.ir_on_control | epl_sensor.hs.ir_mode | epl_sensor.hs.ir_driver;
+ epl_sensor_I2C_Write(client, 0x05, buf);
+
+ buf = epl_sensor.hs.compare_reset | epl_sensor.hs.lock;
+
+ epl_sensor_I2C_Write(client, 0x1b, buf);
+ }
+#if !PS_GES /*PS_GES*/
+ else
+#elif PS_GES
+ else if(enable_ges)
+#endif /*PS_GES*/
+#endif
+
+#if PS_GES
+
+#if !HS_ENABLE /*HS_ENABLE*/
+ if(enable_ges)
+#endif /*HS_ENABLE*/
+ {
+ /*ges setting*/
+ buf = epl_sensor.ges.integration_time | epl_sensor.ges.gain;
+ epl_sensor_I2C_Write(client, 0x03, buf);
+
+ buf = epl_sensor.ges.adc | epl_sensor.ges.cycle;
+ epl_sensor_I2C_Write(client, 0x04, buf);
+
+ buf = epl_sensor.ges.ir_on_control | epl_sensor.ges.ir_mode | epl_sensor.ges.ir_drive;
+ epl_sensor_I2C_Write(client, 0x05, buf);
+
+ buf = epl_sensor.interrupt_control | epl_sensor.ges.persist |epl_sensor.ges.interrupt_type;
+ epl_sensor_I2C_Write(client, 0x06, buf);
+
+ buf = epl_sensor.ges.compare_reset | epl_sensor.ges.lock;
+ epl_sensor_I2C_Write(client, 0x1b, buf);
+
+ epl_sensor_I2C_Write(client, 0x22, (u8)(epl_sensor.ges.cancelation& 0xff));
+ epl_sensor_I2C_Write(client, 0x23, (u8)((epl_sensor.ges.cancelation & 0xff00) >> 8));
+ set_psensor_intr_threshold(epl_sensor.ges.low_threshold, epl_sensor.ges.high_threshold);
+ }
+ else
+#endif
+ {
+ /*ps setting*/
+ buf = epl_sensor.ps.integration_time | epl_sensor.ps.gain;
+ epl_sensor_I2C_Write(client,0x03, buf);
+
+ buf = epl_sensor.ps.adc | epl_sensor.ps.cycle;
+ epl_sensor_I2C_Write(client,0x04, buf);
+
+ buf = epl_sensor.ps.ir_on_control | epl_sensor.ps.ir_mode | epl_sensor.ps.ir_drive;
+ epl_sensor_I2C_Write(client,0x05, buf);
+
+ buf = epl_sensor.interrupt_control | epl_sensor.ps.persist |epl_sensor.ps.interrupt_type;
+ epl_sensor_I2C_Write(client,0x06, buf);
+
+ buf = epl_sensor.ps.compare_reset | epl_sensor.ps.lock;
+ epl_sensor_I2C_Write(client,0x1b, buf);
+
+ epl_sensor_I2C_Write(client,0x22, (u8)(epl_sensor.ps.cancelation& 0xff));
+ epl_sensor_I2C_Write(client,0x23, (u8)((epl_sensor.ps.cancelation & 0xff0) >> 8));
+ set_psensor_intr_threshold(epl_sensor.ps.low_threshold, epl_sensor.ps.high_threshold);
+
+ /*als setting*/
+ buf = epl_sensor.als.integration_time | epl_sensor.als.gain;
+ epl_sensor_I2C_Write(client,0x01, buf);
+
+ buf = epl_sensor.als.adc | epl_sensor.als.cycle;
+ epl_sensor_I2C_Write(client,0x02, buf);
+
+ buf = epl_sensor.als.interrupt_channel_select | epl_sensor.als.persist | epl_sensor.als.interrupt_type;
+ epl_sensor_I2C_Write(client,0x07, buf);
+
+ buf = epl_sensor.als.compare_reset | epl_sensor.als.lock;
+ epl_sensor_I2C_Write(client,0x12, buf);
+
+ set_lsensor_intr_threshold(epl_sensor.als.low_threshold, epl_sensor.als.high_threshold);
+ }
+ //set mode and wait
+ buf = epl_sensor.wait | epl_sensor.mode;
+ epl_sensor_I2C_Write(client,0x00, buf);
+}
+
+static void set_als_ps_intr_type(struct i2c_client *client, bool ps_polling, bool als_polling)
+{
+
+ //set als / ps interrupt control mode and trigger type
+ switch((ps_polling << 1) | als_polling)
+ {
+ case 0: // ps and als interrupt
+ epl_sensor.interrupt_control = EPL_INT_CTRL_ALS_OR_PS;
+ epl_sensor.als.interrupt_type = EPL_INTTY_ACTIVE;
+ epl_sensor.ps.interrupt_type = EPL_INTTY_ACTIVE;
+ break;
+
+ case 1: //ps interrupt and als polling
+ epl_sensor.interrupt_control = EPL_INT_CTRL_PS;
+ epl_sensor.als.interrupt_type = EPL_INTTY_DISABLE;
+ epl_sensor.ps.interrupt_type = EPL_INTTY_ACTIVE;
+ break;
+
+ case 2: // ps polling and als interrupt
+ epl_sensor.interrupt_control = EPL_INT_CTRL_ALS;
+ epl_sensor.als.interrupt_type = EPL_INTTY_ACTIVE;
+ epl_sensor.ps.interrupt_type = EPL_INTTY_DISABLE;
+ break;
+
+ case 3: //ps and als polling
+ epl_sensor.interrupt_control = EPL_INT_CTRL_ALS_OR_PS;
+ epl_sensor.als.interrupt_type = EPL_INTTY_DISABLE;
+ epl_sensor.ps.interrupt_type = EPL_INTTY_DISABLE;
+ break;
+ }
+}
+
+//====================initial global variable===============//
+static void initial_global_variable(struct i2c_client *client, struct epl_sensor_priv *obj)
+{
+
+ //general setting
+ epl_sensor.power = EPL_POWER_ON;
+ epl_sensor.reset = EPL_RESETN_RUN;
+ epl_sensor.mode = EPL_MODE_IDLE;
+ epl_sensor.wait = EPL_WAIT_20_MS;
+ epl_sensor.osc_sel = EPL_OSC_SEL_1MHZ;
+
+ //als setting
+ epl_sensor.als.polling_mode = ALS_POLLING_MODE;
+ epl_sensor.als.integration_time = EPL_ALS_INTT_64;
+ epl_sensor.als.gain = EPL_GAIN_MID;
+ epl_sensor.als.adc = EPL_PSALS_ADC_13;
+ epl_sensor.als.cycle = EPL_CYCLE_16;
+ epl_sensor.als.interrupt_channel_select = EPL_ALS_INT_CHSEL_1;
+ epl_sensor.als.persist = EPL_PERIST_1;
+ epl_sensor.als.compare_reset = EPL_CMP_RESET;
+ epl_sensor.als.lock = EPL_UN_LOCK;
+ epl_sensor.als.report_type = CMC_BIT_RAW; //CMC_BIT_DYN_INT
+ epl_sensor.als.high_threshold = ALS_HIGH_THRESHOLD;
+ epl_sensor.als.low_threshold = ALS_LOW_THRESHOLD;
+ //als factory
+ epl_sensor.als.factory.calibration_enable = false;
+ epl_sensor.als.factory.calibrated = false;
+ epl_sensor.als.factory.lux_per_count = LUX_PER_COUNT;
+
+#if ALS_DYN_INTT
+
+ epl_sensor.als.lsrc_type = CMC_BIT_LSRC_NON;
+
+ if(epl_sensor.als.report_type == CMC_BIT_DYN_INT)
+ {
+ dynamic_intt_idx = dynamic_intt_init_idx;
+ epl_sensor.als.integration_time = als_dynamic_intt_intt[dynamic_intt_idx];
+ epl_sensor.als.gain = als_dynamic_intt_gain[dynamic_intt_idx];
+ dynamic_intt_high_thr = als_dynamic_intt_high_thr[dynamic_intt_idx];
+ dynamic_intt_low_thr = als_dynamic_intt_low_thr[dynamic_intt_idx];
+ }
+
+ c_gain = 21000; // 21000/1000=21
+
+ obj->lsource_thd_high = 1900; //different light source boundary (N) (1.9)
+ obj->lsource_thd_low = 1500; //1.5
+ obj->c_gain_h = 21000; //fluorescent (C1) (21)
+ obj->c_gain_l = 15000; //incandescent (C2) (15)
+
+#endif
+ //ps setting
+ epl_sensor.ps.polling_mode = PS_POLLING_MODE;
+ epl_sensor.ps.integration_time = EPL_PS_INTT_80;
+ epl_sensor.ps.gain = EPL_GAIN_MID;
+#if PS_DYN_K
+ dynk_min_ps_raw_data = 0xffff; //min raw data
+ dynk_max_ir_data = 50000; //max ch0
+ dynk_low_offset = 500; //low thd offset
+ dynk_high_offset = 800; //high thd offset
+ dynk_change_thd_max = 30000; //change thd condition
+ dynk_thd_offset = 500; //change thd offset
+#if PS_DYN_K_STR
+ dynk_enhance_max_ch0 = 50000; //enhance max ch0
+ dynk_enhance_integration_time = EPL_ALS_INTT_64;
+ dynk_enhance_gain = EPL_GAIN_LOW;
+ dynk_enhance_adc = EPL_PSALS_ADC_12;
+#endif
+#endif
+ epl_sensor.ps.adc = EPL_PSALS_ADC_12;
+ epl_sensor.ps.cycle = EPL_CYCLE_32;
+ epl_sensor.ps.persist = EPL_PERIST_1;
+ epl_sensor.ps.ir_on_control = EPL_IR_ON_CTRL_ON;
+ epl_sensor.ps.ir_mode = EPL_IR_MODE_CURRENT;
+ epl_sensor.ps.ir_drive = EPL_IR_DRIVE_100;
+ epl_sensor.ps.compare_reset = EPL_CMP_RESET;
+ epl_sensor.ps.lock = EPL_UN_LOCK;
+ epl_sensor.ps.high_threshold = PS_HIGH_THRESHOLD;
+ epl_sensor.ps.low_threshold = PS_LOW_THRESHOLD;
+ //ps factory
+ epl_sensor.ps.factory.calibration_enable = false;
+ epl_sensor.ps.factory.calibrated = false;
+ epl_sensor.ps.factory.cancelation= 0;
+
+#if HS_ENABLE
+ //hs setting
+ epl_sensor.hs.integration_time = EPL_PS_INTT_80;
+ epl_sensor.hs.integration_time_max = EPL_PS_INTT_272;
+ epl_sensor.hs.integration_time_min = EPL_PS_INTT_32;
+ epl_sensor.hs.gain = EPL_GAIN_LOW;
+ epl_sensor.hs.adc = EPL_PSALS_ADC_11;
+ epl_sensor.hs.cycle = EPL_CYCLE_4;
+ epl_sensor.hs.ir_on_control = EPL_IR_ON_CTRL_ON;
+ epl_sensor.hs.ir_mode = EPL_IR_MODE_CURRENT;
+ epl_sensor.hs.ir_driver = EPL_IR_DRIVE_200;
+ epl_sensor.hs.compare_reset = EPL_CMP_RESET;
+ epl_sensor.hs.lock = EPL_UN_LOCK;
+ epl_sensor.hs.low_threshold = 6400;
+ epl_sensor.hs.mid_threshold = 25600;
+ epl_sensor.hs.high_threshold = 60800;
+#endif
+
+#if PS_GES
+ //ps setting
+ epl_sensor.ges.polling_mode = PS_POLLING_MODE;
+ epl_sensor.ges.integration_time = EPL_PS_INTT_80;
+ epl_sensor.ges.gain = EPL_GAIN_LOW;
+ epl_sensor.ges.adc = EPL_PSALS_ADC_12;
+ epl_sensor.ges.cycle = EPL_CYCLE_2;
+ epl_sensor.ges.persist = EPL_PERIST_1;
+ epl_sensor.ges.ir_on_control = EPL_IR_ON_CTRL_ON;
+ epl_sensor.ges.ir_mode = EPL_IR_MODE_CURRENT;
+ epl_sensor.ges.ir_drive = EPL_IR_DRIVE_200;
+ epl_sensor.ges.compare_reset = EPL_CMP_RESET;
+ epl_sensor.ges.lock = EPL_UN_LOCK;
+ epl_sensor.ges.high_threshold = ges_threshold_high;
+ epl_sensor.ges.low_threshold = ges_threshold_low;
+#endif
+
+ set_als_ps_intr_type(client, epl_sensor.ps.polling_mode, epl_sensor.als.polling_mode);
+ //write setting to sensor
+ write_global_variable(client);
+}
+
+#if HS_ENABLE
+int epl_sensor_read_hs(struct i2c_client *client)
+{
+ u8 buf;
+
+ if(client == NULL)
+ {
+ LOG_ERR("CLIENT CANN'T EQUL NULL\n");
+ return -1;
+ }
+
+ mutex_lock(&hs_sensor_mutex);
+ epl_sensor_I2C_Read(client,0x1e, 2);
+ epl_sensor.hs.raw = (gRawData.raw_bytes[1]<<8) | gRawData.raw_bytes[0];
+ LOG_INFO("epl_sensor.hs.raw=%d \r\n", epl_sensor.hs.raw);
+ if(epl_sensor.hs.dynamic_intt == true && epl_sensor.hs.raw>epl_sensor.hs.high_threshold && epl_sensor.hs.integration_time > epl_sensor.hs.integration_time_min)
+ {
+ epl_sensor.hs.integration_time -= 4;
+ buf = epl_sensor.hs.integration_time | epl_sensor.hs.gain;
+ epl_sensor_I2C_Write(client, 0x03, buf);
+ }
+ else if(epl_sensor.hs.dynamic_intt == true && epl_sensor.hs.raw>epl_sensor.hs.low_threshold && epl_sensor.hs.raw <epl_sensor.hs.mid_threshold && epl_sensor.hs.integration_time < epl_sensor.hs.integration_time_max)
+ {
+ epl_sensor.hs.integration_time += 4;
+ buf = epl_sensor.hs.integration_time | epl_sensor.hs.gain;
+ epl_sensor_I2C_Write(client, 0x03, buf);
+ }
+
+ mutex_unlock(&hs_sensor_mutex);
+
+ if(epl_sensor.hs.raws_count<200)
+ {
+ epl_sensor.hs.raws[epl_sensor.hs.raws_count] = epl_sensor.hs.raw;
+ epl_sensor.hs.raws_count++;
+ }
+
+ return 0;
+}
+#endif
+
+#if PS_DYN_K
+void epl_sensor_reset_dynk_thd(u8 last_status, u8 now_status)
+{
+ if(last_status==0 && now_status==1)
+ {
+#if PS_DYN_K_STR
+ if( (epl_sensor.ps.saturation == 0) && (epl_sensor.ps.data.ir_data < dynk_max_ir_data) && (dynk_enhance_ch0 < dynk_enhance_max_ch0) )
+#else
+ if( (epl_sensor.ps.saturation == 0)&&(epl_sensor.ps.data.ir_data < dynk_max_ir_data) )
+#endif
+ {
+ dynk_min_ps_raw_data = epl_sensor.ps.data.data;
+ }
+
+ dynk_thd_low = dynk_min_ps_raw_data + dynk_low_offset;
+ dynk_thd_high = dynk_min_ps_raw_data + dynk_high_offset;
+
+ if(dynk_thd_low>65534)
+ dynk_thd_low = 65534;
+ if(dynk_thd_high>65535)
+ dynk_thd_high = 65535;
+#if PS_DEBUG
+ LOG_INFO("[%s]:restart dynk ps raw = %d, min = %d, ir_data = %d\n", __func__, epl_sensor.ps.data.data, dynk_min_ps_raw_data, epl_sensor.ps.data.ir_data);
+ LOG_INFO("[%s]:restart dynk thre_l = %ld, thre_h = %ld\n", __func__, (long)dynk_thd_low, (long)dynk_thd_high);
+#endif
+ eint_flag = false;
+ set_psensor_intr_threshold((u16)dynk_thd_low, (u16)dynk_thd_high);
+ eint_flag = true;
+ }
+ else if(last_status==1 && now_status==0)
+ {
+ dynk_change_flag = true;
+ }
+}
+#if PS_DYN_K_STR
+void epl_sensor_enhance_enable(struct i2c_client *client, bool enable)
+{
+ bool cmp_flag = false;
+ u8 buf, now_cmp_l;
+ int enh_time=0, ps_time=0, total_time=0;
+ ps_time = ps_sensing_time(epl_sensor.ps.integration_time, epl_sensor.ps.adc, epl_sensor.ps.cycle);
+
+
+ //mutex_lock(&sensor_mutex);
+ polling_flag = false;
+
+
+ epl_sensor_I2C_Read(client, 0x1b, 1);
+ buf = gRawData.raw_bytes[0];
+ epl_sensor.ps.compare_high = (buf & 0x10);
+ now_cmp_l = (buf & 0x08);
+
+ if(now_cmp_l != epl_sensor.ps.compare_low)
+ {
+ LOG_INFO("[%s]: buf=0x%x, now_cmp_l=0x%x, epl_sensor.ps.compare_low=0x%x \r\n", __func__, buf, now_cmp_l, epl_sensor.ps.compare_low);
+ //PS unlock and reset
+ epl_sensor.ps.compare_reset = EPL_CMP_RESET;
+ epl_sensor.ps.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(client, 0x1b, epl_sensor.ps.compare_reset |epl_sensor.ps.lock);
+
+ cmp_flag = true;
+ }
+
+ epl_sensor.ps.interrupt_flag = EPL_INT_CLEAR;
+ epl_sensor.als.interrupt_flag = EPL_INT_CLEAR;
+
+ epl_sensor_I2C_Write(client, 0x00, epl_sensor.wait | EPL_MODE_IDLE);
+
+ if(enable == true){
+ enh_time = als_sensing_time(dynk_enhance_integration_time, dynk_enhance_adc, epl_sensor.als.cycle) / 2;
+ epl_sensor_I2C_Write(client, 0x01, dynk_enhance_integration_time | dynk_enhance_gain);
+ epl_sensor_I2C_Write(client, 0x02, dynk_enhance_adc | epl_sensor.als.cycle);
+ epl_sensor_I2C_Write(client, 0xfc, EPL_A_D | EPL_NORMAL| EPL_GFIN_ENABLE | EPL_VOS_ENABLE | EPL_DOC_OFF);
+ }
+ else
+ {
+ enh_time = als_sensing_time(epl_sensor.als.integration_time, epl_sensor.als.adc, epl_sensor.als.cycle);
+ epl_sensor_I2C_Write(client, 0x01, epl_sensor.als.integration_time | epl_sensor.als.gain);
+ epl_sensor_I2C_Write(client, 0x02, epl_sensor.als.adc | epl_sensor.als.cycle);
+ epl_sensor_I2C_Write(client, 0xfc, EPL_A_D | EPL_NORMAL| EPL_GFIN_ENABLE | EPL_VOS_ENABLE | EPL_DOC_ON);
+ }
+
+ als_frame_time = enh_time;
+ ps_frame_time = ps_time;
+
+ epl_sensor_I2C_Write(client, 0x00, epl_sensor.wait | epl_sensor.mode);
+ if(cmp_flag == true)
+ {
+ //PS unlock and run
+ epl_sensor.ps.compare_reset = EPL_CMP_RUN;
+ epl_sensor.ps.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(client, 0x1b, epl_sensor.ps.compare_reset |epl_sensor.ps.lock);
+
+ cmp_flag = false;
+ }
+ //mutex_unlock(&sensor_mutex);
+
+ total_time = ps_time+enh_time+wait_value[epl_sensor.wait>>4];
+ if((2*total_time) >= dynk_polling_delay)
+ {
+ dynk_polling_delay = 2*total_time+50;
+ LOG_INFO("[%s]: dynk_polling_delay=%d \r\n", __func__, dynk_polling_delay);
+ }
+
+ msleep(total_time);
+ LOG_INFO("[%s] PS+ALS(%dms)\r\n", __func__, total_time);
+
+ //mutex_lock(&sensor_mutex);
+ if(enable == true)
+ {
+ epl_sensor_read_als(client);
+ }
+
+ if(epl_sensor.ps.interrupt_flag == EPL_INT_TRIGGER)
+ {
+ //PS unlock and run
+ epl_sensor.ps.compare_reset = EPL_CMP_RUN;
+ epl_sensor.ps.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(client, 0x1b, epl_sensor.ps.compare_reset |epl_sensor.ps.lock);
+ }
+ polling_flag = true;
+ //mutex_unlock(&sensor_mutex);
+
+}
+#endif
+
+void epl_sensor_restart_dynk_polling(void)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ cancel_delayed_work(&dynk_thd_polling_work);
+ queue_delayed_work(epld->epl_wq, &dynk_thd_polling_work,msecs_to_jiffies(dynk_polling_delay));
+}
+
+void epl_sensor_dynk_thd_polling_work(struct work_struct *work)
+{
+ struct epl_sensor_priv *obj = epl_sensor_obj;
+
+ bool enable_ps = obj->enable_pflag==1 && obj->ps_suspend==0;
+#if PS_DEBUG
+ bool enable_als = obj->enable_lflag==1 && obj->als_suspend==0;
+ LOG_INFO("[%s]:als / ps enable: %d / %d\n", __func__,enable_als, enable_ps);
+#endif
+
+ if(enable_ps == true)
+ {
+#if PS_DYN_K_STR
+ dynk_enhance_flag = false;
+ if(polling_flag == true && eint_flag == true)
+ {
+ epl_sensor_enhance_enable(obj->client, true);
+ }
+#endif
+ if(polling_flag == true && eint_flag == true && epl_sensor.ps.polling_mode == 0)
+ {
+ mutex_lock(&sensor_mutex);
+ epl_sensor_read_ps_status(obj->client);
+ epl_sensor_read_ps(obj->client);
+ mutex_unlock(&sensor_mutex);
+ }
+#if PS_DYN_K_STR
+ if( (dynk_min_ps_raw_data > epl_sensor.ps.data.data)
+ && (epl_sensor.ps.saturation == 0)
+ && (epl_sensor.ps.data.ir_data < dynk_max_ir_data)
+ && (dynk_enhance_ch0 < dynk_enhance_max_ch0) )
+#else
+ if( (dynk_min_ps_raw_data > epl_sensor.ps.data.data)
+ && (epl_sensor.ps.saturation == 0)
+ && (epl_sensor.ps.data.ir_data < dynk_max_ir_data) )
+#endif
+ {
+ dynk_min_ps_raw_data = epl_sensor.ps.data.data;
+ dynk_thd_low = dynk_min_ps_raw_data + dynk_low_offset;
+ dynk_thd_high = dynk_min_ps_raw_data + dynk_high_offset;
+
+ if(dynk_thd_low>65534)
+ dynk_thd_low = 65534;
+ if(dynk_thd_high>65535)
+ dynk_thd_high = 65535;
+#if PS_DEBUG
+ LOG_INFO("[%s]:dyn ps raw = %d, min = %d, ir_data = %d\n", __func__, epl_sensor.ps.data.data, dynk_min_ps_raw_data, epl_sensor.ps.data.ir_data);
+#endif
+ eint_flag = false;
+ mutex_lock(&sensor_mutex);
+ set_psensor_intr_threshold((u16)dynk_thd_low, (u16)dynk_thd_high);
+ mutex_unlock(&sensor_mutex);
+ eint_flag = true;
+#if PS_DEBUG
+ LOG_INFO("[%s]:dyn k thre_l = %ld, thre_h = %ld\n", __func__, (long)dynk_thd_low, (long)dynk_thd_high);
+#endif
+ }
+ else if(dynk_change_flag==true && (epl_sensor.ps.data.data>dynk_change_thd_max) && ( (epl_sensor.ps.compare_low >> 3)==0 ))
+ {
+ dynk_change_flag = false;
+ dynk_thd_low += dynk_thd_offset;
+ dynk_thd_high += dynk_thd_offset;
+
+ if(dynk_thd_low>65534)
+ dynk_thd_low = 65534;
+ if(dynk_thd_high>65535)
+ dynk_thd_high = 65535;
+
+ eint_flag = false;
+ mutex_lock(&sensor_mutex);
+ set_psensor_intr_threshold((u16)dynk_thd_low, (u16)dynk_thd_high);
+ mutex_unlock(&sensor_mutex);
+ eint_flag = true;
+#if PS_DEBUG
+ LOG_INFO("[%s]: epl_sensor.ps.data.data=%d, L/H=%ld/%ld \r\n", __func__, epl_sensor.ps.data.data, (long)dynk_thd_low, (long)dynk_thd_high);
+#endif
+ }
+#if PS_DYN_K_STR
+ if(polling_flag == true && eint_flag == true)
+ {
+ epl_sensor_enhance_enable(obj->client, false);
+ }
+ dynk_enhance_flag = true;
+#endif
+ queue_delayed_work(obj->epl_wq, &dynk_thd_polling_work,msecs_to_jiffies(dynk_polling_delay));
+ }
+}
+#endif
+
+/************************************************************************/
+//for 3637
+static int als_sensing_time(int intt, int adc, int cycle)
+{
+ long sensing_us_time;
+ int sensing_ms_time;
+ int als_intt, als_adc, als_cycle;
+
+ als_intt = als_intt_value[intt>>2];
+ als_adc = adc_value[adc>>3];
+ als_cycle = cycle_value[cycle];
+#if COMMON_DEBUG
+ LOG_INFO("ALS: INTT=%d, ADC=%d, Cycle=%d \r\n", als_intt, als_adc, als_cycle);
+#endif
+
+ sensing_us_time = (als_intt + als_adc*2*2) * 2 * als_cycle;
+ sensing_ms_time = sensing_us_time / 1000;
+
+#if COMMON_DEBUG
+ LOG_INFO("[%s]: sensing=%d ms \r\n", __func__, sensing_ms_time);
+#endif
+ return (sensing_ms_time + 5);
+}
+
+static int ps_sensing_time(int intt, int adc, int cycle)
+{
+ long sensing_us_time;
+ int sensing_ms_time;
+ int ps_intt, ps_adc, ps_cycle;
+
+ ps_intt = ps_intt_value[intt>>2];
+ ps_adc = adc_value[adc>>3];
+ ps_cycle = cycle_value[cycle];
+#if COMMON_DEBUG
+ LOG_INFO("PS: INTT=%d, ADC=%d, Cycle=%d \r\n", ps_intt, ps_adc, ps_cycle);
+#endif
+
+ sensing_us_time = (ps_intt*3 + ps_adc*2*3) * ps_cycle;
+ sensing_ms_time = sensing_us_time / 1000;
+#if COMMON_DEBUG
+ LOG_INFO("[%s]: sensing=%d ms\r\n", __func__, sensing_ms_time);
+#endif
+
+
+ return (sensing_ms_time + 5);
+}
+
+static int epl_sensor_get_wait_time(int ps_time, int als_time)
+{
+ int wait_idx = 0;
+ int wait_time = 0;
+
+ wait_time = als_time - ps_time;
+ if(wait_time < 0){
+ wait_time = 0;
+ }
+#if COMMON_DEBUG
+ LOG_INFO("[%s]: wait_len = %d \r\n", __func__, wait_len);
+#endif
+ for(wait_idx = 0; wait_idx < wait_len; wait_idx++)
+ {
+ if(wait_time < wait_value[wait_idx])
+ {
+ break;
+ }
+ }
+ if(wait_idx >= wait_len){
+ wait_idx = wait_len - 1;
+ }
+
+#if COMMON_DEBUG
+ LOG_INFO("[%s]: wait_idx = %d, wait = %dms \r\n", __func__, wait_idx, wait_value[wait_idx]);
+#endif
+ return (wait_idx << 4);
+}
+/************************************************************************/
+
+void epl_sensor_update_mode(struct i2c_client *client)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ int als_time = 0, ps_time = 0;
+
+ bool enable_ps = epld->enable_pflag==1 && epld->ps_suspend==0;
+ bool enable_als = epld->enable_lflag==1 && epld->als_suspend==0;
+#if HS_ENABLE
+ bool enable_hs = epld->enable_hflag==1 && epld->hs_suspend==0;
+#endif
+#if PS_GES
+ bool enable_ges = epld->enable_gflag==1 && epld->ges_suspend==0;
+#endif
+ polling_flag = false;
+ als_time = als_sensing_time(epl_sensor.als.integration_time, epl_sensor.als.adc, epl_sensor.als.cycle);
+ ps_time = ps_sensing_time(epl_sensor.ps.integration_time, epl_sensor.ps.adc, epl_sensor.ps.cycle);
+
+ als_frame_time = als_time;
+ ps_frame_time = ps_time;
+
+#if HS_ENABLE
+ if(enable_hs)
+ {
+ LOG_INFO("[%s]: HS mode \r\n", __func__);
+ epl_sensor_restart_polling();
+ }
+ else
+#endif
+ {
+ //PS unlock and reset
+ epl_sensor.ps.compare_reset = EPL_CMP_RESET;
+ epl_sensor.ps.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client, 0x1b, epl_sensor.ps.compare_reset |epl_sensor.ps.lock);
+
+ //ALS unlock and reset
+ epl_sensor.als.compare_reset = EPL_CMP_RESET;
+ epl_sensor.als.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client, 0x12, epl_sensor.als.compare_reset | epl_sensor.als.lock);
+
+
+ //epl_sensor_I2C_Write(epld->client, 0x11, EPL_RESETN_RESET | EPL_POWER_OFF);
+ epl_sensor.ps.interrupt_flag = EPL_INT_CLEAR;
+ epl_sensor.als.interrupt_flag = EPL_INT_CLEAR;
+
+#if PS_GES
+ LOG_INFO("mode selection =0x%x\n", (enable_ps|enable_ges) | (enable_als << 1));
+#else
+ LOG_INFO("mode selection =0x%x\n", enable_ps | (enable_als << 1));
+#endif
+
+#if PS_GES
+ //**** mode selection ****
+ switch((enable_als << 1) | (enable_ps|enable_ges))
+#else
+ //**** mode selection ****
+ switch((enable_als << 1) | enable_ps)
+#endif
+ {
+ case 0: //disable all
+ epl_sensor.mode = EPL_MODE_IDLE;
+ break;
+
+ case 1: //als = 0, ps = 1
+#if PS_DYN_K && PS_DYN_K_STR
+ epl_sensor.mode = EPL_MODE_ALS_PS;
+#else
+ epl_sensor.mode = EPL_MODE_PS;
+#endif
+ break;
+
+ case 2: //als = 1, ps = 0
+ epl_sensor.mode = EPL_MODE_ALS;
+ break;
+
+ case 3: //als = 1, ps = 1
+ epl_sensor.mode = EPL_MODE_ALS_PS;
+ break;
+ }
+
+ //**** write setting ****
+ // step 1. set sensor at idle mode
+ // step 2. set sensor at operation mode
+ // step 3. delay sensing time
+ // step 4. unlock and run als / ps status
+ epl_sensor_I2C_Write(epld->client, 0x00, epl_sensor.wait | EPL_MODE_IDLE);
+ // initial factory calibration variable
+ read_factory_calibration();
+
+ epl_sensor_I2C_Write(epld->client, 0x02, epl_sensor.als.adc | epl_sensor.als.cycle);
+ set_als_ps_intr_type(epld->client, epl_sensor.ps.polling_mode, epl_sensor.als.polling_mode);
+
+ epl_sensor_I2C_Write(epld->client, 0x06, epl_sensor.interrupt_control | epl_sensor.ps.persist |epl_sensor.ps.interrupt_type);
+
+ epl_sensor_I2C_Write(epld->client, 0x07, epl_sensor.als.interrupt_channel_select | epl_sensor.als.persist | epl_sensor.als.interrupt_type);
+
+#if ALS_DYN_INTT
+ if(epl_sensor.als.report_type == CMC_BIT_DYN_INT){
+ epl_sensor_I2C_Write(client, 0x01, epl_sensor.als.integration_time | epl_sensor.als.gain);
+ }
+#endif
+
+ if(epl_sensor.mode == EPL_MODE_ALS_PS && epl_sensor.als.polling_mode == 0 && epl_sensor.ps.polling_mode == 0){
+ int wait = 0;
+ wait = epl_sensor_get_wait_time(ps_time, als_time);
+ epl_sensor_I2C_Write(epld->client, 0x00, wait | epl_sensor.mode);
+ }
+ else{
+#if PS_GES
+ if(enable_ges == 1 && enable_ps == 0)
+ {
+ set_psensor_intr_threshold(epl_sensor.ges.low_threshold, epl_sensor.ges.high_threshold);
+ epl_sensor_I2C_Write(client, 0x00, EPL_WAIT_2_MS | epl_sensor.mode);
+ }
+ else
+#endif
+ epl_sensor_I2C_Write(epld->client, 0x00, epl_sensor.wait | epl_sensor.mode);
+ }
+
+ //ALS unlock and run
+ epl_sensor.als.compare_reset = EPL_CMP_RUN;
+ epl_sensor.als.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client, 0x12, epl_sensor.als.compare_reset | epl_sensor.als.lock);
+
+ //PS unlock and run
+ epl_sensor.ps.compare_reset = EPL_CMP_RUN;
+ epl_sensor.ps.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client, 0x1b, epl_sensor.ps.compare_reset |epl_sensor.ps.lock);
+
+ //epl_sensor_I2C_Write(epld->client, 0x11, epl_sensor.reset | epl_sensor.power);
+
+#if COMMON_DEBUG
+ //**** check setting ****
+ if(enable_ps == 1)
+ {
+ LOG_INFO("[%s] PS:low_thd = %d, high_thd = %d \n",__func__, epl_sensor.ps.low_threshold, epl_sensor.ps.high_threshold);
+ }
+
+ if(enable_als == 1 && epl_sensor.als.polling_mode == 0)
+ {
+ LOG_INFO("[%s] ALS:low_thd = %d, high_thd = %d \n",__func__, epl_sensor.als.low_threshold, epl_sensor.als.high_threshold);
+ }
+#if PS_GES
+ if(enable_ges)
+ {
+ LOG_INFO("[%s] GES:low_thd = %d, high_thd = %d \n",__func__, epl_sensor.ges.low_threshold, epl_sensor.ges.high_threshold);
+
+ }
+#endif
+
+ LOG_INFO("[%s] reg0x00= 0x%x \n", __func__, epl_sensor.wait | epl_sensor.mode);
+ LOG_INFO("[%s] reg0x07= 0x%x \n", __func__, epl_sensor.als.interrupt_channel_select | epl_sensor.als.persist | epl_sensor.als.interrupt_type);
+ LOG_INFO("[%s] reg0x06= 0x%x \n", __func__, epl_sensor.interrupt_control | epl_sensor.ps.persist |epl_sensor.ps.interrupt_type);
+ LOG_INFO("[%s] reg0x11= 0x%x \n", __func__, epl_sensor.power | epl_sensor.reset);
+ LOG_INFO("[%s] reg0x12= 0x%x \n", __func__, epl_sensor.als.compare_reset | epl_sensor.als.lock);
+ LOG_INFO("[%s] reg0x1b= 0x%x \n", __func__, epl_sensor.ps.compare_reset | epl_sensor.ps.lock);
+#endif
+
+ if(epl_sensor.mode == EPL_MODE_PS)
+ {
+ msleep(ps_time);
+ LOG_INFO("[%s] PS only(%dms)\r\n", __func__, ps_time);
+ }
+ else if (epl_sensor.mode == EPL_MODE_ALS)
+ {
+ msleep(als_time);
+ LOG_INFO("[%s] ALS only(%dms)\r\n", __func__, als_time);
+ }
+ else if (epl_sensor.mode == EPL_MODE_ALS_PS)
+ {
+ msleep(ps_time+als_time+wait_value[epl_sensor.wait>>4]);
+ LOG_INFO("[%s] PS+ALS(%dms)\r\n", __func__, ps_time+als_time+wait_value[epl_sensor.wait>>4]);
+ }
+
+ if(epl_sensor.ps.interrupt_flag == EPL_INT_TRIGGER)
+ {
+ //PS unlock and run
+ epl_sensor.ps.compare_reset = EPL_CMP_RUN;
+ epl_sensor.ps.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client, 0x1b, epl_sensor.ps.compare_reset |epl_sensor.ps.lock);
+ }
+
+ if(epl_sensor.als.interrupt_flag == EPL_INT_TRIGGER)
+ {
+ //ALS unlock and run
+ epl_sensor.als.compare_reset = EPL_CMP_RUN;
+ epl_sensor.als.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client, 0x12, epl_sensor.als.compare_reset | epl_sensor.als.lock);
+ }
+
+#if PS_GES
+ if((enable_als==1 && epl_sensor.als.polling_mode==1) || (enable_ps==1 && epl_sensor.ps.polling_mode==1) || (enable_ges == 1 && epl_sensor.ges.polling_mode==1))
+#else
+ if((enable_als==1 && epl_sensor.als.polling_mode==1) || (enable_ps==1 && epl_sensor.ps.polling_mode==1))
+#endif
+ {
+ epl_sensor_restart_polling();
+ }
+
+#if PS_DYN_K
+ if(enable_ps == 1)
+ {
+ epl_sensor_restart_dynk_polling();
+ }
+#endif
+ }
+ polling_flag = true;
+
+}
+
+/*----------------------------------------------------------------------------*/
+static void epl_sensor_polling_work(struct work_struct *work)
+{
+
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ struct i2c_client *client = epld->client;
+
+ bool enable_ps = epld->enable_pflag==1 && epld->ps_suspend==0;
+ bool enable_als = epld->enable_lflag==1 && epld->als_suspend==0;
+#if HS_ENABLE
+ bool enable_hs = epld->enable_hflag==1 && epld->hs_suspend==0;
+
+#endif
+#if PS_GES
+ bool enable_ges = epld->enable_gflag==1 && epld->ges_suspend==0;
+#endif
+
+#if COMMON_DEBUG
+#if HS_ENABLE && PS_GES
+ LOG_INFO("enable_pflag=%d, enable_lflag=%d, enable_hs=%d, enable_ges=%d \n", enable_ps, enable_als, enable_hs, enable_ges);
+#elif HS_ENABLE
+ LOG_INFO("enable_pflag=%d, enable_lflag=%d, enable_hs=%d\n", enable_ps, enable_als, enable_hs);
+#elif PS_GES
+ LOG_INFO("enable_pflag=%d, enable_lflag=%d, enable_ges=%d\n", enable_ps, enable_als, enable_ges);
+#else
+ LOG_INFO("enable_pflag = %d, enable_lflag = %d \n", enable_ps, enable_als);
+#endif
+#endif
+
+#if PS_DYN_K && PS_DYN_K_STR
+ if(polling_flag == false && eint_flag == false && dynk_enhance_flag == false)
+#else
+ if(polling_flag == false && eint_flag == false)
+#endif
+ {
+#if PS_DYN_K && PS_DYN_K_STR
+ LOG_INFO("[%s]: epl_sensor_update_mode(%d) or eint_work(%d) is running, dynk_enhance_flag=%d !\r\n", __func__, polling_flag, eint_flag, dynk_enhance_flag);
+#else
+ LOG_INFO("[%s]: epl_sensor_update_mode(%d) or eint_work(%d) is running !\r\n", __func__, polling_flag, eint_flag);
+#endif
+ return;
+ }
+
+ cancel_delayed_work(&polling_work);
+
+ if( (enable_als && epl_sensor.als.polling_mode == 1) || (enable_ps && epl_sensor.ps.polling_mode == 1) )
+ {
+ queue_delayed_work(epld->epl_wq, &polling_work,msecs_to_jiffies(polling_time));
+ }
+
+#if HS_ENABLE
+ if (enable_hs)
+ {
+ queue_delayed_work(epld->epl_wq, &polling_work,msecs_to_jiffies(20));
+ epl_sensor_read_hs(client);
+ }
+#if PS_GES /*PS_GES*/
+ else if (enable_ges && epl_sensor.ges.polling_mode==1)
+#endif /*PS_GES*/
+#endif
+
+#if PS_GES
+#if !HS_ENABLE /*HS_ENABLE*/
+ if (enable_ges && epl_sensor.ges.polling_mode==1)
+#endif /*HS_ENABLE*/
+ {
+ queue_delayed_work(epld->epl_wq, &polling_work, msecs_to_jiffies(polling_time));
+ epl_sensor_read_ps(epld->client);
+ epl_sensor_read_ps_status(epld->client);
+ }
+#endif
+
+ if(enable_als && epl_sensor.als.polling_mode == 1)
+ {
+ int report_lux = 0;
+#if PS_DYN_K && PS_DYN_K_STR
+ if(polling_flag == true && eint_flag == true && dynk_enhance_flag == true)
+#else
+ if(polling_flag == true && eint_flag == true)
+#endif
+ {
+ mutex_lock(&sensor_mutex);
+ //epl_sensor_read_als_status(client); // for test
+ epl_sensor_read_als(client);
+ mutex_unlock(&sensor_mutex);
+ report_lux = epl_sensor_get_als_value(epld, epl_sensor.als.data.channels[1]);
+ if(epl_sensor.als.report_type != CMC_BIT_DYN_INT){
+ epl_sensor_report_lux(report_lux);
+ }
+ }
+ }
+
+ if(enable_ps && epl_sensor.ps.polling_mode == 1)
+ {
+#if PS_DYN_K && PS_DYN_K_STR
+ if(polling_flag == true && eint_flag == true && dynk_enhance_flag == true)
+#else
+ if(polling_flag == true && eint_flag == true)
+#endif
+ {
+ mutex_lock(&sensor_mutex);
+ epl_sensor_read_ps_status(client);
+ epl_sensor_read_ps(client);
+ mutex_unlock(&sensor_mutex);
+#if PS_DYN_K
+ epl_sensor_reset_dynk_thd(dynk_last_status, (epl_sensor.ps.compare_low >> 3));
+ dynk_last_status = (epl_sensor.ps.compare_low >> 3);
+#endif
+ epl_sensor_report_ps_status();
+ }
+ }
+#if HS_ENABLE && PS_GES
+ if(enable_als==false && enable_ps==false && enable_hs==false && enable_ges==false)
+#elif HS_ENABLE
+ if(enable_als==false && enable_ps==false && enable_hs==false)
+#elif PS_GES
+ if(enable_als==false && enable_ps==false && enable_ges==false)
+#else
+ if(enable_als==false && enable_ps==false)
+#endif
+ {
+ cancel_delayed_work(&polling_work);
+ LOG_INFO("disable sensor\n");
+ }
+
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static irqreturn_t epl_sensor_eint_func(int irqNo, void *handle)
+{
+ struct epl_sensor_priv *epld = (struct epl_sensor_priv*)handle;
+ disable_irq_nosync(epld->irq);
+ //queue_work(epld->epl_wq, &epl_sensor_irq_work);
+ schedule_delayed_work(&epld->eint_work, 0);
+
+ return IRQ_HANDLED;
+}
+/*----------------------------------------------------------------------------*/
+static void epl_sensor_intr_als_report_lux(void)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+
+ int report_lux = 0;
+ epl_sensor_read_als(epld->client);
+
+ report_lux = epl_sensor_get_als_value(epld, epl_sensor.als.data.channels[1]);
+ epl_sensor_report_lux(report_lux);
+
+ epl_sensor.als.compare_reset = EPL_CMP_RESET;
+ epl_sensor.als.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client,0x12, epl_sensor.als.compare_reset | epl_sensor.als.lock);
+
+ //set dynamic threshold
+ if(epl_sensor.als.compare_high >> 4)
+ {
+ epl_sensor.als.high_threshold = epl_sensor.als.high_threshold + 250;
+ epl_sensor.als.low_threshold = epl_sensor.als.low_threshold + 250;
+
+ if (epl_sensor.als.high_threshold > 60000)
+ {
+ epl_sensor.als.high_threshold = epl_sensor.als.high_threshold -250;
+ epl_sensor.als.low_threshold = epl_sensor.als.low_threshold - 250;
+ }
+ }
+ if(epl_sensor.als.compare_low>> 3)
+ {
+ epl_sensor.als.high_threshold = epl_sensor.als.high_threshold -250;
+ epl_sensor.als.low_threshold = epl_sensor.als.low_threshold - 250;
+
+ if (epl_sensor.als.high_threshold < 250)
+ {
+ epl_sensor.als.high_threshold = epl_sensor.als.high_threshold + 250;
+ epl_sensor.als.low_threshold = epl_sensor.als.low_threshold + 250;
+ }
+ }
+
+ if(epl_sensor.als.high_threshold < epl_sensor.als.low_threshold)
+ {
+ LOG_INFO("[%s]:recover default setting \r\n", __FUNCTION__);
+ epl_sensor.als.high_threshold = ALS_HIGH_THRESHOLD;
+ epl_sensor.als.low_threshold = ALS_LOW_THRESHOLD;
+ }
+
+ //write new threshold
+ set_lsensor_intr_threshold(epl_sensor.als.low_threshold, epl_sensor.als.high_threshold);
+}
+/*----------------------------------------------------------------------------*/
+static void epl_sensor_eint_work(struct work_struct *work)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ LOG_FUN();
+
+ bool enable_ps = epld->enable_pflag==1 && epld->ps_suspend==0;
+ bool enable_als = epld->enable_lflag==1 && epld->als_suspend==0;
+#if PS_GES
+ bool enable_ges = epld->enable_gflag==1 && epld->ges_suspend==0;
+#endif
+ eint_flag = false;
+ mutex_lock(&sensor_mutex);
+
+#if PS_GES
+ if(enable_ges && epl_sensor.ges.polling_mode == 0)
+ {
+ epl_sensor_read_ps_status(epld->client);
+ if(polling_flag == true)
+ {
+ //GES unlock and run
+ epl_sensor.ges.compare_reset = EPL_CMP_RUN;
+ epl_sensor.ges.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client,0x1b, epl_sensor.ges.compare_reset | epl_sensor.ges.lock);
+ }
+ }
+#endif
+ if(enable_ps && epl_sensor.ps.polling_mode == 0)
+ {
+ epl_sensor_read_ps_status(epld->client);
+ if(epl_sensor.ps.interrupt_flag == EPL_INT_TRIGGER)
+ {
+#if PS_DEBUG || PS_DYN_K
+ epl_sensor_read_ps(epld->client);
+#endif
+
+#if PS_DYN_K
+ epl_sensor_reset_dynk_thd(dynk_last_status, (epl_sensor.ps.compare_low >> 3));
+ dynk_last_status = (epl_sensor.ps.compare_low >> 3);
+#endif
+ epl_sensor_report_ps_status();
+
+ if(polling_flag == true)
+ {
+ //PS unlock and run
+ epl_sensor.ps.compare_reset = EPL_CMP_RUN;
+ epl_sensor.ps.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client,0x1b, epl_sensor.ps.compare_reset |epl_sensor.ps.lock);
+ }
+
+ }
+ }
+ if(enable_als && epl_sensor.als.polling_mode == 0)
+ {
+ epl_sensor_read_als_status(epld->client);
+ if(epl_sensor.als.interrupt_flag == EPL_INT_TRIGGER)
+ {
+ epl_sensor_intr_als_report_lux();
+ if(polling_flag == true)
+ {
+ //ALS unlock and run
+ epl_sensor.als.compare_reset = EPL_CMP_RUN;
+ epl_sensor.als.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client,0x12, epl_sensor.als.compare_reset | epl_sensor.als.lock);
+ }
+ }
+ }
+
+ //enable_irq(epld->irq);
+ mutex_unlock(&sensor_mutex);
+ eint_flag = true;
+ enable_irq(epld->irq);
+}
+/*----------------------------------------------------------------------------*/
+static int epl_sensor_setup_interrupt(struct epl_sensor_priv *epld)
+{
+ struct i2c_client *client = epld->client;
+ int err = 0;
+#if QCOM
+ unsigned int irq_gpio;
+ unsigned int irq_gpio_flags;
+ struct device_node *np = client->dev.of_node;
+#endif
+ msleep(5);
+#if S5PV210
+ err = gpio_request(S5PV210_GPH0(1), "Elan EPL IRQ");
+ if (err)
+ {
+ LOG_ERR("gpio pin request fail (%d)\n", err);
+ goto initial_fail;
+ }
+ else
+ {
+ LOG_INFO("----- Samsung gpio config success -----\n");
+ s3c_gpio_cfgpin(S5PV210_GPH0(1),S3C_GPIO_SFN(0x0F)/*(S5PV210_GPH0_1_EXT_INT30_1) */);
+ s3c_gpio_setpull(S5PV210_GPH0(1),S3C_GPIO_PULL_UP);
+
+ }
+#elif SPREAD
+ //epld->intr_pin = ELAN_INT_PIN; /*need setting*/
+ err = gpio_request(epld->intr_pin, "Elan EPL IRQ");
+ if (err)
+ {
+ LOG_ERR("gpio pin request fail (%d)\n", err);
+ goto initial_fail;
+ }else{
+ gpio_direction_input(epld->intr_pin);
+ /*get irq*/
+ client->irq = gpio_to_irq(epld->intr_pin);
+ epld->irq = client->irq;
+ LOG_INFO("IRQ number is %d\n", client->irq);
+ }
+#elif QCOM
+ irq_gpio = of_get_named_gpio_flags(np, "epl,irq-gpio", 0, &irq_gpio_flags);
+ //irq_gpio = ELAN_INT_PIN;
+ epld->intr_pin = irq_gpio;
+ if (epld->intr_pin < 0) {
+ goto initial_fail;
+ }
+
+ if (gpio_is_valid(epld->intr_pin)) {
+ err = gpio_request(epld->intr_pin, "epl_irq_gpio");
+ if (err) {
+ LOG_ERR( "irq gpio request failed");
+ goto initial_fail;
+ }
+
+ err = gpio_direction_input(epld->intr_pin);
+ if (err) {
+ LOG_ERR("set_direction for irq gpio failed\n");
+ goto initial_fail;
+ }
+ }
+#elif LEADCORE
+ //epld->intr_pin = ELAN_INT_PIN; /*need setting*/
+ epld->intr_pin = irq_to_gpio(client->irq); /*need confirm*/
+
+ err = gpio_request(epld->intr_pin, "epl irq"); /*ep12182 irq*/
+ //err = gpio_request(epld->intr_pin, "ep12182 irq");
+ if (err < 0){
+ LOG_ERR("%s:Gpio request failed! \r\n", __func__);
+ goto initial_fail;
+ }
+ gpio_direction_input(epld->intr_pin);
+ client->irq = gpio_to_irq(epld->intr_pin);
+#elif MARVELL
+
+ epld->intr_pin = ELAN_INT_PIN; /*need setting*/
+ if(client->irq <= 0)
+ {
+ LOG_ERR("client->irq(%d) Failed \r\n", client->irq);
+ goto initial_fail;
+ }
+#endif
+ err = request_irq(epld->irq,epl_sensor_eint_func, IRQF_TRIGGER_FALLING,
+ "epl2182_pls", epld);
+ if(err <0)
+ {
+ LOG_ERR("request irq pin %d fail for gpio\n",err);
+ goto fail_free_intr_pin;
+ }
+
+ return err;
+
+initial_fail:
+fail_free_intr_pin:
+ gpio_free(epld->intr_pin);
+ //free_irq(epld->irq, epld);
+ return err;
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+
+static ssize_t epl_sensor_show_reg(struct device *dev, struct device_attribute *attr, char *buf)
+{
+
+ ssize_t len = 0;
+ struct i2c_client *client = epl_sensor_obj->client;
+
+ if(!epl_sensor_obj)
+ {
+ LOG_ERR("epl_obj is null!!\n");
+ return 0;
+ }
+
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x00 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x00));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x01 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x01));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x02 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x02));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x03 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x03));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x04 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x04));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x05 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x05));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x06 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x06));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x07 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x07));
+ if(epl_sensor.als.polling_mode == 0)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x08 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x08));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x09 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x09));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x0A value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x0A));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x0B value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x0B));
+ }
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x0C value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x0C));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x0D value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x0D));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x0E value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x0E));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x0F value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x0F));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x11 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x11));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x12 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x12));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x1B value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x1B));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x22 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x22));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x23 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x23));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x24 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x24));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0x25 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x25));
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip id REG 0xFC value = 0x%x\n", i2c_smbus_read_byte_data(client, 0xFC));
+
+ return len;
+
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_show_status(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ ssize_t len = 0;
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ bool enable_ps = epld->enable_pflag==1 && epld->ps_suspend==0;
+ bool enable_als = epld->enable_lflag==1 && epld->als_suspend==0;
+#if HS_ENABLE
+ bool enable_hs = epld->enable_hflag==1 && epld->hs_suspend==0;
+#endif
+
+ if(!epl_sensor_obj)
+ {
+ LOG_ERR("epl_sensor_obj is null!!\n");
+ return 0;
+ }
+
+ len += snprintf(buf+len, PAGE_SIZE-len, "chip is %s, ver is %s \n", EPL_DEV_NAME, DRIVER_VERSION);
+ len += snprintf(buf+len, PAGE_SIZE-len, "als/ps polling is %d-%d\n", epl_sensor.als.polling_mode, epl_sensor.ps.polling_mode);
+ len += snprintf(buf+len, PAGE_SIZE-len, "wait = %d, mode = %d\n",epl_sensor.wait >> 4, epl_sensor.mode);
+ len += snprintf(buf+len, PAGE_SIZE-len, "interrupt control = %d\n", epl_sensor.interrupt_control >> 4);
+ len += snprintf(buf+len, PAGE_SIZE-len, "frame time ps=%dms, als=%dms\n", ps_frame_time, als_frame_time);
+#if HS_ENABLE
+ if(enable_hs)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "hs adc= %d\n", epl_sensor.hs.adc>>3);
+ len += snprintf(buf+len, PAGE_SIZE-len, "hs int_time= %d\n", epl_sensor.hs.integration_time>>2);
+ len += snprintf(buf+len, PAGE_SIZE-len, "hs cycle= %d\n", epl_sensor.hs.cycle);
+ len += snprintf(buf+len, PAGE_SIZE-len, "hs gain= %d\n", epl_sensor.hs.gain);
+ len += snprintf(buf+len, PAGE_SIZE-len, "hs ch1 raw= %d\n", epl_sensor.hs.raw);
+ }
+#endif
+ if(enable_ps)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "PS: \n");
+ len += snprintf(buf+len, PAGE_SIZE-len, "INTEG = %d, gain = %d\n", epl_sensor.ps.integration_time >> 2, epl_sensor.ps.gain);
+ len += snprintf(buf+len, PAGE_SIZE-len, "ADC = %d, cycle = %d, ir drive = %d\n", epl_sensor.ps.adc >> 3, epl_sensor.ps.cycle, epl_sensor.ps.ir_drive);
+ len += snprintf(buf+len, PAGE_SIZE-len, "saturation = %d, int flag = %d\n", epl_sensor.ps.saturation >> 5, epl_sensor.ps.interrupt_flag >> 2);
+ len += snprintf(buf+len, PAGE_SIZE-len, "Thr(L/H) = (%d/%d)\n", epl_sensor.ps.low_threshold, epl_sensor.ps.high_threshold);
+#if PS_DYN_K
+#if PS_DYN_K_STR
+ len += snprintf(buf+len, PAGE_SIZE-len, "Dyn enhance ch0 = %d \n", dynk_enhance_ch0);
+#endif
+ len += snprintf(buf+len, PAGE_SIZE-len, "Dyn thr(L/H) = (%ld/%ld)\n", (long)dynk_thd_low, (long)dynk_thd_high);
+#endif
+ len += snprintf(buf+len, PAGE_SIZE-len, "pals data = %d, data = %d\n", epl_sensor.ps.data.ir_data, epl_sensor.ps.data.data);
+ }
+ if(enable_als)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "ALS: \n");
+ len += snprintf(buf+len, PAGE_SIZE-len, "INTEG = %d, gain = %d\n", epl_sensor.als.integration_time >> 2, epl_sensor.als.gain);
+ len += snprintf(buf+len, PAGE_SIZE-len, "ADC = %d, cycle = %d\n", epl_sensor.als.adc >> 3, epl_sensor.als.cycle);
+#if ALS_DYN_INTT
+ if(epl_sensor.als.lsrc_type != CMC_BIT_LSRC_NON)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "lsource_thd_low=%d, lsource_thd_high=%d \n", epld->lsource_thd_low, epld->lsource_thd_high);
+ len += snprintf(buf+len, PAGE_SIZE-len, "saturation = %d\n", epl_sensor.als.saturation >> 5);
+
+ if(epl_sensor.als.lsrc_type == CMC_BIT_LSRC_SCALE || epl_sensor.als.lsrc_type == CMC_BIT_LSRC_BOTH)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "real_ratio = %d\n", epld->ratio);
+ len += snprintf(buf+len, PAGE_SIZE-len, "use_ratio = %d\n", epld->last_ratio);
+ }
+ else if(epl_sensor.als.lsrc_type == CMC_BIT_LSRC_SLOPE)
+ {
+ len += snprintf(buf+len, PAGE_SIZE-len, "ratio = %d\n", epld->ratio);
+ }
+ }
+
+ len += snprintf(buf+len, PAGE_SIZE-len, "c_gain = %d\n", c_gain);
+ len += snprintf(buf+len, PAGE_SIZE-len, "dynamic_intt_lux = %d\n", dynamic_intt_lux);
+#endif
+ if(epl_sensor.als.polling_mode == 0)
+ len += snprintf(buf+len, PAGE_SIZE-len, "Thr(L/H) = (%d/%d)\n", epl_sensor.als.low_threshold, epl_sensor.als.high_threshold);
+ len += snprintf(buf+len, PAGE_SIZE-len, "ch0 = %d, ch1 = %d\n", epl_sensor.als.data.channels[0], epl_sensor.als.data.channels[1]);
+ }
+
+ return len;
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_als_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ uint16_t mode=0;
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ LOG_FUN();
+
+
+ sscanf(buf, "%hu",&mode);
+ if(epld->enable_lflag != mode)
+ {
+#if ALS_DYN_INTT
+ if(epl_sensor.als.report_type == CMC_BIT_DYN_INT)
+ {
+ dynamic_intt_idx = dynamic_intt_init_idx;
+ epl_sensor.als.integration_time = als_dynamic_intt_intt[dynamic_intt_idx];
+ epl_sensor.als.gain = als_dynamic_intt_gain[dynamic_intt_idx];
+ dynamic_intt_high_thr = als_dynamic_intt_high_thr[dynamic_intt_idx];
+ dynamic_intt_low_thr = als_dynamic_intt_low_thr[dynamic_intt_idx];
+ }
+#endif
+ epld->enable_lflag = mode;
+
+ epl_sensor_update_mode(epld->client);
+ }
+
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_ps_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ uint16_t mode=0;
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+#if HS_ENABLE
+ bool enable_hs = epld->enable_hflag==1 && epld->hs_suspend==0;
+#endif
+#if PS_GES
+ bool enable_ges = epld->enable_gflag==1 && epld->ges_suspend==0;
+#endif
+ LOG_FUN();
+
+ sscanf(buf, "%hu",&mode);
+#if HS_ENABLE
+ if(enable_hs == 1 && mode == 1)
+ {
+ epld->enable_hflag = 0;
+ if(hs_enable_flag == true)
+ {
+ epld->enable_lflag = 1;
+ hs_enable_flag = false;
+ }
+ write_global_variable(epld->client);
+ LOG_INFO("[%s] Disable HS and recover ps setting \r\n", __func__);
+ }
+#endif
+#if PS_GES
+ if(enable_ges == 1 && mode == 1)
+ {
+ epld->enable_gflag = 0;
+ write_global_variable(epld->client);
+ ps_ges_enable_flag = true;
+ LOG_INFO("[%s] Disable GES and recover ps setting \r\n", __func__);
+ }
+ else if (ps_ges_enable_flag == true && mode == 0)
+ {
+ epld->enable_gflag = 1;
+ write_global_variable(epld->client);
+ ps_ges_enable_flag = false;
+ LOG_INFO("[%s] enable GES and recover ges setting \r\n", __func__);
+ }
+#endif
+ if(epld->enable_pflag != mode)
+ {
+ epld->enable_pflag = mode;
+ if(mode)
+ {
+ wake_lock(&ps_lock);
+#if PS_DYN_K
+ dynk_min_ps_raw_data = 0xffff;
+ dynk_change_flag = false;
+#if PS_DYN_K_STR
+ dynk_enhance_flag = false;
+#endif
+#endif
+ }
+ else
+ {
+#if PS_DYN_K
+ cancel_delayed_work(&dynk_thd_polling_work);
+#endif
+ wake_unlock(&ps_lock);
+ }
+
+ epl_sensor_update_mode(epld->client);
+ }
+
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_show_cal_raw(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ u16 ch1 = 0;
+ u32 ch1_all=0;
+ int count =5;
+ int i;
+ ssize_t len = 0;
+#if !PS_DYN_K
+ bool enable_ps = epld->enable_pflag==1 && epld->ps_suspend==0;
+#endif
+
+ if(!epl_sensor_obj)
+ {
+ LOG_ERR("epl_sensor_obj is null!!\n");
+ return 0;
+ }
+
+
+
+ for(i=0; i<count; i++)
+ {
+ switch(epl_sensor.mode)
+ {
+ msleep(50);
+#if PS_DYN_K_STR
+ case EPL_MODE_ALS_PS:
+#else
+ case EPL_MODE_PS:
+#endif
+#if !PS_DYN_K
+ if(enable_ps == true && polling_flag == true && eint_flag == true && epl_sensor.ps.polling_mode == 0)
+ epl_sensor_read_ps(epld->client);
+#endif
+ ch1 = epl_sensor.ps.data.data;
+ break;
+
+ case EPL_MODE_ALS:
+ if(epl_sensor.als.polling_mode == 0)
+ epl_sensor_read_als(epld->client);
+ ch1 = epl_sensor.als.data.channels[1];
+ break;
+ }
+
+ ch1_all = ch1_all + ch1;
+ if(epl_sensor.wait == EPL_WAIT_SINGLE)
+ epl_sensor_I2C_Write(epld->client,0x11, epl_sensor.power | epl_sensor.reset);
+ }
+
+ ch1 = (u16)(ch1_all/count);
+
+ LOG_INFO("cal_raw = %d \r\n" , ch1);
+
+ len += snprintf(buf + len, PAGE_SIZE - len, "%d \r\n", ch1);
+
+ return len;
+}
+
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_threshold(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ int hthr = 0, lthr = 0;
+ if(!epld)
+ {
+ LOG_ERR("epl_sensor_obj is null!!\n");
+ return 0;
+ }
+
+ switch(epl_sensor.mode)
+ {
+#if PS_DYN_K_STR
+ case EPL_MODE_ALS_PS:
+#else
+ case EPL_MODE_PS:
+#endif
+ sscanf(buf, "%d,%d", &lthr, &hthr);
+ epl_sensor.ps.low_threshold = lthr;
+ epl_sensor.ps.high_threshold = hthr;
+ set_psensor_intr_threshold(epl_sensor.ps.low_threshold, epl_sensor.ps.high_threshold);
+ break;
+
+ case EPL_MODE_ALS:
+ sscanf(buf, "%d,%d", &lthr, &hthr);
+ epl_sensor.als.low_threshold = lthr;
+ epl_sensor.als.high_threshold = hthr;
+ set_lsensor_intr_threshold(epl_sensor.als.low_threshold, epl_sensor.als.high_threshold);
+ break;
+
+ }
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_wait_time(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int val;
+ sscanf(buf, "%d",&val);
+
+ epl_sensor.wait = (val & 0xf) << 4;
+
+ return count;
+}
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_gain(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ int value = 0;
+ LOG_FUN();
+
+ sscanf(buf, "%d", &value);
+
+ value = value & 0x03;
+
+ switch (epl_sensor.mode)
+ {
+#if PS_DYN_K_STR
+ case EPL_MODE_ALS_PS:
+#else
+ case EPL_MODE_PS: //ps
+#endif
+ epl_sensor.ps.gain = value;
+ epl_sensor_I2C_Write(epld->client, 0x03, epl_sensor.ps.integration_time | epl_sensor.ps.gain);
+ break;
+
+ case EPL_MODE_ALS: //als
+ epl_sensor.als.gain = value;
+ epl_sensor_I2C_Write(epld->client, 0x01, epl_sensor.als.integration_time | epl_sensor.als.gain);
+ break;
+ }
+
+ epl_sensor_update_mode(epld->client);
+
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ int value=0;
+
+ LOG_FUN();
+
+ epld->enable_pflag = 0;
+ epld->enable_lflag = 0;
+
+ sscanf(buf, "%d",&value);
+
+ switch (value)
+ {
+ case 0:
+ epl_sensor.mode = EPL_MODE_IDLE;
+ break;
+
+ case 1:
+ epld->enable_lflag = 1;
+ epl_sensor.mode = EPL_MODE_ALS;
+ break;
+
+ case 2:
+ epld->enable_pflag = 1;
+ epl_sensor.mode = EPL_MODE_PS;
+ break;
+
+ case 3:
+ epld->enable_lflag = 1;
+ epld->enable_pflag = 1;
+ epl_sensor.mode = EPL_MODE_ALS_PS;
+ break;
+ }
+
+ epl_sensor_update_mode(epld->client);
+
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_ir_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value=0;
+
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+ LOG_FUN();
+
+ sscanf(buf, "%d", &value);
+
+ switch (epl_sensor.mode)
+ {
+#if PS_DYN_K_STR
+ case EPL_MODE_ALS_PS:
+#else
+ case EPL_MODE_PS: //ps
+#endif
+ switch(value)
+ {
+ case 0:
+ epl_sensor.ps.ir_mode = EPL_IR_MODE_CURRENT;
+ break;
+
+ case 1:
+ epl_sensor.ps.ir_mode = EPL_IR_MODE_VOLTAGE;
+ break;
+ }
+
+ epl_sensor_I2C_Write(epld->client,0x05, epl_sensor.ps.ir_on_control | epl_sensor.ps.ir_mode | epl_sensor.ps.ir_drive);
+ break;
+ }
+
+ epl_sensor_I2C_Write(epld->client,0x00,epl_sensor.wait | epl_sensor.mode);
+
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_ir_contrl(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value=0;
+ uint8_t data;
+
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+ LOG_FUN();
+
+ sscanf(buf, "%d",&value);
+
+ switch (epl_sensor.mode)
+ {
+#if PS_DYN_K_STR
+ case EPL_MODE_ALS_PS:
+#else
+ case EPL_MODE_PS: //ps
+#endif
+ switch(value)
+ {
+ case 0:
+ epl_sensor.ps.ir_on_control = EPL_IR_ON_CTRL_OFF;
+ break;
+
+ case 1:
+ epl_sensor.ps.ir_on_control = EPL_IR_ON_CTRL_ON;
+ break;
+ }
+
+ data = epl_sensor.ps.ir_on_control | epl_sensor.ps.ir_mode | epl_sensor.ps.ir_drive;
+ LOG_INFO("[%s]: 0x05 = 0x%x\n", __FUNCTION__, data);
+
+ epl_sensor_I2C_Write(epld->client,0x05, epl_sensor.ps.ir_on_control | epl_sensor.ps.ir_mode | epl_sensor.ps.ir_drive);
+ break;
+ }
+
+ epl_sensor_I2C_Write(epld->client,0x00,epl_sensor.wait | epl_sensor.mode);
+
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_ir_drive(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value=0;
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+ LOG_FUN();
+
+ sscanf(buf, "%d", &value);
+
+ switch(epl_sensor.mode)
+ {
+#if PS_DYN_K_STR
+ case EPL_MODE_ALS_PS:
+#else
+ case EPL_MODE_PS:
+#endif
+ epl_sensor.ps.ir_drive = (value & 0x03);
+ epl_sensor_I2C_Write(epld->client,0x05, epl_sensor.ps.ir_on_control | epl_sensor.ps.ir_mode | epl_sensor.ps.ir_drive);
+ break;
+ }
+
+ epl_sensor_I2C_Write(epld->client,0x00,epl_sensor.wait | epl_sensor.mode);
+
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_interrupt_type(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value=0;
+
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+ LOG_FUN();
+
+ sscanf(buf, "%d",&value);
+
+ switch (epl_sensor.mode)
+ {
+#if PS_DYN_K_STR
+ case EPL_MODE_ALS_PS:
+#else
+ case EPL_MODE_PS: //ps
+#endif
+ if(!epl_sensor.ps.polling_mode)
+ {
+ epl_sensor.ps.interrupt_type = value & 0x03;
+ epl_sensor_I2C_Write(epld->client,0x06, epl_sensor.interrupt_control | epl_sensor.ps.persist |epl_sensor.ps.interrupt_type);
+ LOG_INFO("[%s]: 0x06 = 0x%x\n", __FUNCTION__, epl_sensor.interrupt_control | epl_sensor.ps.persist |epl_sensor.ps.interrupt_type);
+ }
+ break;
+
+ case EPL_MODE_ALS: //als
+ if(!epl_sensor.als.polling_mode)
+ {
+ epl_sensor.als.interrupt_type = value & 0x03;
+ epl_sensor_I2C_Write(epld->client,0x07, epl_sensor.als.interrupt_channel_select | epl_sensor.als.persist | epl_sensor.als.interrupt_type);
+ LOG_INFO("[%s]: 0x07 = 0x%x\n", __FUNCTION__, epl_sensor.als.interrupt_channel_select | epl_sensor.als.persist | epl_sensor.als.interrupt_type);
+ }
+ break;
+ }
+
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static ssize_t epl_sensor_store_ps_polling_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ int polling_mode = 0;
+
+
+ sscanf(buf, "%d",&polling_mode);
+ epl_sensor.ps.polling_mode = polling_mode;
+#if PS_GES
+ epl_sensor.ges.polling_mode = polling_mode;
+#endif
+
+ epl_sensor_update_mode(epld->client);
+
+ return count;
+}
+
+static ssize_t epl_sensor_store_als_polling_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ int polling_mode = 0;
+
+ sscanf(buf, "%d",&polling_mode);
+ epl_sensor.als.polling_mode = polling_mode;
+
+ epl_sensor_update_mode(epld->client);
+
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_integration(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value=0;
+
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+ LOG_FUN();
+
+ sscanf(buf, "%d",&value);
+
+ switch (epl_sensor.mode)
+ {
+#if PS_DYN_K_STR
+ case EPL_MODE_ALS_PS:
+#else
+ case EPL_MODE_PS: //ps
+#endif
+ epl_sensor.ps.integration_time = (value & 0xf) << 2;
+ epl_sensor_I2C_Write(epld->client,0x03, epl_sensor.ps.integration_time | epl_sensor.ps.gain);
+ epl_sensor_I2C_Read(epld->client, 0x03, 1);
+ LOG_INFO("[%s]: 0x03 = 0x%x (0x%x)\n", __FUNCTION__, epl_sensor.ps.integration_time | epl_sensor.ps.gain, gRawData.raw_bytes[0]);
+ break;
+
+ case EPL_MODE_ALS: //als
+ epl_sensor.als.integration_time = (value & 0xf) << 2;
+ epl_sensor_I2C_Write(epld->client,0x01, epl_sensor.als.integration_time | epl_sensor.als.gain);
+ epl_sensor_I2C_Read(epld->client, 0x01, 1);
+ LOG_INFO("[%s]: 0x01 = 0x%x (0x%x)\n", __FUNCTION__, epl_sensor.als.integration_time | epl_sensor.als.gain, gRawData.raw_bytes[0]);
+ break;
+ }
+
+ epl_sensor_update_mode(epld->client);
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_adc(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value=0;
+
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+ LOG_FUN();
+
+ sscanf(buf, "%d",&value);
+
+ switch (epl_sensor.mode)
+ {
+#if PS_DYN_K_STR
+ case EPL_MODE_ALS_PS:
+#else
+ case EPL_MODE_PS: //ps
+#endif
+ epl_sensor.ps.adc = (value & 0x3) << 3;
+ epl_sensor_I2C_Write(epld->client,0x04, epl_sensor.ps.adc | epl_sensor.ps.cycle);
+ epl_sensor_I2C_Read(epld->client, 0x04, 1);
+ LOG_INFO("[%s]:0x04 = 0x%x (0x%x)\n", __FUNCTION__, epl_sensor.ps.adc | epl_sensor.ps.cycle, gRawData.raw_bytes[0]);
+ break;
+
+ case EPL_MODE_ALS: //als
+ epl_sensor.als.adc = (value & 0x3) << 3;
+ epl_sensor_I2C_Write(epld->client,0x02, epl_sensor.als.adc | epl_sensor.als.cycle);
+ epl_sensor_I2C_Read(epld->client, 0x02, 1);
+ LOG_INFO("[%s]:0x02 = 0x%x (0x%x)\n", __FUNCTION__, epl_sensor.als.adc | epl_sensor.als.cycle, gRawData.raw_bytes[0]);
+ break;
+ }
+
+ epl_sensor_update_mode(epld->client);
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_cycle(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value=0;
+
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+ LOG_FUN();
+
+ sscanf(buf, "%d",&value);
+
+ switch (epl_sensor.mode)
+ {
+#if PS_DYN_K_STR
+ case EPL_MODE_ALS_PS:
+#else
+ case EPL_MODE_PS: //ps
+#endif
+ epl_sensor.ps.cycle = (value & 0x7);
+ epl_sensor_I2C_Write(epld->client,0x04, epl_sensor.ps.adc | epl_sensor.ps.cycle);
+ LOG_INFO("[%s]:0x04 = 0x%x (0x%x)\n", __FUNCTION__, epl_sensor.ps.adc | epl_sensor.ps.cycle, gRawData.raw_bytes[0]);
+ break;
+
+ case EPL_MODE_ALS: //als
+ epl_sensor.als.cycle = (value & 0x7);
+ epl_sensor_I2C_Write(epld->client,0x02, epl_sensor.als.adc | epl_sensor.als.cycle);
+ LOG_INFO("[%s]:0x02 = 0x%x (0x%x)\n", __FUNCTION__, epl_sensor.als.adc | epl_sensor.als.cycle, gRawData.raw_bytes[0]);
+ break;
+ }
+
+ epl_sensor_update_mode(epld->client);
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_als_report_type(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int value=0;
+
+ LOG_FUN();
+
+ sscanf(buf, "%d", &value);
+ epl_sensor.als.report_type = value & 0xf;
+
+ return count;
+}
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_store_ps_w_calfile(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ int ps_hthr=0, ps_lthr=0, ps_cancelation=0;
+ int ps_cal_len = 0;
+ char ps_calibration[20];
+ LOG_FUN();
+
+ if(!epl_sensor_obj)
+ {
+ LOG_ERR("epl_obj is null!!\n");
+ return 0;
+ }
+ sscanf(buf, "%d,%d,%d",&ps_cancelation, &ps_hthr, &ps_lthr);
+
+ ps_cal_len = sprintf(ps_calibration, "%d,%d,%d", ps_cancelation, ps_hthr, ps_lthr);
+
+ write_factory_calibration(epld, ps_calibration, ps_cal_len);
+ return count;
+}
+/*----------------------------------------------------------------------------*/
+
+static ssize_t epl_sensor_store_reg_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ int reg;
+ int data;
+ LOG_FUN();
+
+ sscanf(buf, "%x,%x",&reg, &data);
+
+ LOG_INFO("[%s]: reg=0x%x, data=0x%x", __func__, reg, data);
+ epl_sensor_I2C_Write(epld->client, reg, data);
+
+ return count;
+}
+
+static ssize_t epl_sensor_store_unlock(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ int mode;
+ LOG_FUN();
+
+ sscanf(buf, "%d",&mode);
+
+ LOG_INFO("mode = %d \r\n", mode);
+ switch (mode)
+ {
+ case 0: //ps
+ //PS unlock and run
+ epl_sensor.ps.compare_reset = EPL_CMP_RUN;
+ epl_sensor.ps.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client,0x1b, epl_sensor.ps.compare_reset |epl_sensor.ps.lock);
+ break;
+
+ case 1: //als
+ //ALS unlock and run
+ epl_sensor.als.compare_reset = EPL_CMP_RUN;
+ epl_sensor.als.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client,0x12, epl_sensor.als.compare_reset | epl_sensor.als.lock);
+ break;
+
+ case 2: //als unlock and reset
+ epl_sensor.als.compare_reset = EPL_CMP_RESET;
+ epl_sensor.als.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client,0x12, epl_sensor.als.compare_reset | epl_sensor.als.lock);
+ break;
+
+ case 3: //ps+als
+ //PS unlock and run
+ epl_sensor.ps.compare_reset = EPL_CMP_RUN;
+ epl_sensor.ps.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client,0x1b, epl_sensor.ps.compare_reset |epl_sensor.ps.lock);
+
+ //ALS unlock and run
+ epl_sensor.als.compare_reset = EPL_CMP_RUN;
+ epl_sensor.als.lock = EPL_UN_LOCK;
+ epl_sensor_I2C_Write(epld->client,0x12, epl_sensor.als.compare_reset | epl_sensor.als.lock);
+ break;
+ }
+ /*double check PS or ALS lock*/
+
+
+ return count;
+}
+
+static ssize_t epl_sensor_store_als_ch_sel(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ int ch_sel;
+ LOG_FUN();
+
+ sscanf(buf, "%d",&ch_sel);
+
+ LOG_INFO("channel selection = %d \r\n", ch_sel);
+ switch (ch_sel)
+ {
+ case 0: //ch0
+ epl_sensor.als.interrupt_channel_select = EPL_ALS_INT_CHSEL_0;
+ break;
+
+ case 1: //ch1
+ epl_sensor.als.interrupt_channel_select = EPL_ALS_INT_CHSEL_1;
+ break;
+ }
+ epl_sensor_I2C_Write(epld->client,0x07, epl_sensor.als.interrupt_channel_select | epl_sensor.als.persist | epl_sensor.als.interrupt_type);
+
+ epl_sensor_update_mode(epld->client);
+
+ return count;
+}
+
+static ssize_t epl_sensor_store_ps_cancelation(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ int cancelation;
+ LOG_FUN();
+
+ sscanf(buf, "%d",&cancelation);
+
+ epl_sensor.ps.cancelation = cancelation;
+
+ LOG_INFO("epl_sensor.ps.cancelation = %d \r\n", epl_sensor.ps.cancelation);
+
+ epl_sensor_I2C_Write(epld->client,0x22, (u8)(epl_sensor.ps.cancelation& 0xff));
+ epl_sensor_I2C_Write(epld->client,0x23, (u8)((epl_sensor.ps.cancelation & 0xff00) >> 8));
+
+ return count;
+}
+
+static ssize_t epl_sensor_show_ps_polling(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ u16 *tmp = (u16*)buf;
+ tmp[0]= epl_sensor.ps.polling_mode;
+ return 2;
+}
+
+static ssize_t epl_sensor_show_als_polling(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ u16 *tmp = (u16*)buf;
+ tmp[0]= epl_sensor.als.polling_mode;
+ return 2;
+}
+
+static ssize_t epl_sensor_show_ps_run_cali(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ ssize_t len = 0;
+ int ret;
+
+ LOG_FUN();
+
+ ret = epl_run_ps_calibration(epld);
+
+ len += snprintf(buf+len, PAGE_SIZE-len, "ret = %d\r\n", ret);
+
+ return len;
+}
+
+static ssize_t epl_sensor_show_pdata(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ ssize_t len = 0;
+ bool enable_ps = epld->enable_pflag==1 && epld->ps_suspend==0;
+ LOG_FUN();
+
+ if(enable_ps == true && polling_flag == true && eint_flag == true && epl_sensor.ps.polling_mode == 0)
+ {
+ mutex_lock(&sensor_mutex);
+ epl_sensor_read_ps(epld->client);
+ mutex_unlock(&sensor_mutex);
+ }
+ LOG_INFO("[%s]: epl_sensor.ps.data.data = %d \r\n", __func__, epl_sensor.ps.data.data);
+ len += snprintf(buf + len, PAGE_SIZE - len, "%d", epl_sensor.ps.data.data);
+ return len;
+
+}
+
+static ssize_t epl_sensor_show_als_data(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ ssize_t len = 0;
+ bool enable_als = epld->enable_lflag==1 && epld->als_suspend==0;
+ LOG_FUN();
+
+#if ALS_DYN_INTT
+ if(epl_sensor.als.report_type == CMC_BIT_DYN_INT)
+ {
+ LOG_INFO("[%s]: dynamic_intt_lux = %d \r\n", __func__, dynamic_intt_lux);
+ len += snprintf(buf + len, PAGE_SIZE - len, "%d", dynamic_intt_lux);
+ }
+ else
+#endif
+ {
+ if(enable_als == true && polling_flag == true && eint_flag == true && epl_sensor.als.polling_mode == 0)
+ {
+ mutex_lock(&sensor_mutex);
+ epl_sensor_read_als(epld->client);
+ mutex_unlock(&sensor_mutex);
+ }
+ len += snprintf(buf + len, PAGE_SIZE - len, "%d", epl_sensor.als.data.channels[1]);
+ }
+
+ return len;
+}
+
+#if ALS_DYN_INTT
+static ssize_t epl_sensor_store_c_gain(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ int c_h,c_l;
+ LOG_FUN();
+
+ if(epl_sensor.als.lsrc_type == CMC_BIT_LSRC_NON)
+ {
+ sscanf(buf, "%d",&c_h);
+ c_gain = c_h;
+ LOG_INFO("c_gain = %d \r\n", c_gain);
+ }
+ else
+ {
+ sscanf(buf, "%d,%d",&c_l, &c_h);
+ epld->c_gain_h = c_h;
+ epld->c_gain_l = c_l;
+ }
+
+ return count;
+}
+
+static ssize_t epl_sensor_store_lsrc_type(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int type;
+ LOG_FUN();
+
+ sscanf(buf, "%d",&type);
+
+ epl_sensor.als.lsrc_type = type;
+
+ LOG_INFO("epl_sensor.als.lsrc_type = %d \r\n", epl_sensor.als.lsrc_type);
+
+ return count;
+}
+
+static ssize_t epl_sensor_store_lsrc_thd(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int lsrc_thrl, lsrc_thrh;
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ LOG_FUN();
+
+ sscanf(buf, "%d,%d",&lsrc_thrl, &lsrc_thrh);
+
+ epld->lsource_thd_low = lsrc_thrl;
+ epld->lsource_thd_high = lsrc_thrh;
+
+ LOG_INFO("lsource_thd=(%d,%d) \r\n", epld->lsource_thd_low, epld->lsource_thd_high);
+
+ return count;
+}
+
+#endif
+
+#if PS_DYN_K
+static ssize_t epl_sensor_store_dyn_offset(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int dyn_h,dyn_l;
+ LOG_FUN();
+
+ sscanf(buf, "%d,%d",&dyn_l, &dyn_h);
+
+ dynk_low_offset = dyn_l;
+ dynk_high_offset = dyn_h;
+
+ return count;
+}
+
+static ssize_t epl_sensor_store_dyn_thd_offset(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int thd_offset;
+ LOG_FUN();
+
+ sscanf(buf, "%d",&thd_offset);
+ dynk_thd_offset = thd_offset;
+
+ return count;
+}
+
+static ssize_t epl_sensor_store_dyn_change_thd_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int thd_max;
+ LOG_FUN();
+
+ sscanf(buf, "%d",&thd_max);
+ dynk_change_thd_max = thd_max;
+
+ return count;
+}
+
+static ssize_t epl_sensor_store_dyn_max_ir_data(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int max_ir_data;
+ LOG_FUN();
+
+ sscanf(buf, "%d",&max_ir_data);
+ dynk_max_ir_data = max_ir_data;
+
+ return count;
+}
+
+#if PS_DYN_K_STR
+static ssize_t epl_sensor_store_dyn_enhance_max_ch0(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ //struct epl_sensor_priv *epld = epl_sensor_obj;
+ int max_ch0;
+ LOG_FUN();
+
+ sscanf(buf, "%d",&max_ch0);
+ dynk_enhance_max_ch0 = max_ch0;
+
+ return count;
+}
+
+static ssize_t epl_sensor_store_dyn_enhance_gain_intt(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ //struct epl_sensor_priv *epld = epl_sensor_obj;
+ int gain, intt;
+ LOG_FUN();
+
+ sscanf(buf, "%d,%d",&gain, &intt);
+
+ dynk_enhance_integration_time = intt;
+ dynk_enhance_gain = gain;
+
+ return count;
+}
+#endif
+
+#endif
+
+#if HS_ENABLE
+static ssize_t epl_sensor_show_renvo(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ ssize_t len = 0;
+
+ LOG_FUN();
+ LOG_INFO("gRawData.renvo=0x%x \r\n", epl_sensor.revno);
+
+ len += snprintf(buf+len, PAGE_SIZE-len, "%x", epl_sensor.revno);
+
+ return len;
+}
+
+static ssize_t epl_sensor_store_hs_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ uint16_t mode=0;
+ struct epl_sensor_priv *obj = epl_sensor_obj;
+ bool enable_ps = obj->enable_pflag==1 && obj->ps_suspend==0;
+ bool enable_als = obj->enable_lflag==1 && obj->als_suspend==0;
+ LOG_FUN();
+
+ sscanf(buf, "%hu",&mode);
+
+ if(enable_ps == 0)
+ {
+ if(mode > 0)
+ {
+ if(enable_als == 1)
+ {
+ obj->enable_lflag = 0;
+ hs_enable_flag = true;
+ }
+ epl_sensor.hs.integration_time = epl_sensor.hs.integration_time_max;
+ epl_sensor.hs.raws_count=0;
+ obj->enable_hflag = 1;
+
+ if(mode == 2)
+ {
+ epl_sensor.hs.dynamic_intt = false;
+ }
+ else
+ {
+ epl_sensor.hs.dynamic_intt = true;
+ }
+ }
+ else
+ {
+ obj->enable_hflag = 0;
+ if(hs_enable_flag == true)
+ {
+ obj->enable_lflag = 1;
+ hs_enable_flag = false;
+ }
+
+ }
+ write_global_variable(obj->client);
+ epl_sensor_update_mode(obj->client);
+ }
+
+ return count;
+}
+
+static ssize_t epl_sensor_store_hs_int_time(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct epl_sensor_priv *obj = epl_sensor_obj;
+ int value;
+ u8 intt_buf;
+
+ sscanf(buf, "%d",&value);
+
+ mutex_lock(&hs_sensor_mutex);
+ epl_sensor.hs.integration_time = value<<2;
+ intt_buf = epl_sensor.hs.integration_time | epl_sensor.hs.gain;
+ epl_sensor_I2C_Write(obj->client, 0x03, intt_buf);
+ mutex_unlock(&hs_sensor_mutex);
+
+ return count;
+}
+
+/*----------------------------------------------------------------------------*/
+static ssize_t epl_sensor_show_hs_raws(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ u16 *tmp = (u16*)buf;
+ int byte_count=2+epl_sensor.hs.raws_count*2;
+ int i=0;
+ LOG_FUN();
+ mutex_lock(&hs_sensor_mutex);
+ tmp[0]= epl_sensor.hs.raws_count;
+
+ for(i=0; i<epl_sensor.hs.raws_count; i++){
+ tmp[i+1] = epl_sensor.hs.raws[i];
+ }
+
+ epl_sensor.hs.raws_count=0;
+ mutex_unlock(&hs_sensor_mutex);
+
+ return byte_count;
+}
+#endif
+
+#if PS_GES
+static ssize_t epl_sensor_store_ges_enable(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ges_enable=0;
+ struct epl_sensor_priv *obj = epl_sensor_obj;
+ struct i2c_client *client = obj->client;
+ bool enable_ps = obj->enable_pflag==1 && obj->ps_suspend==0;
+ LOG_FUN();
+
+ sscanf(buf, "%d",&ges_enable);
+ LOG_INFO("[%s]: enable_ps=%d, ges_enable=%d \r\n", __func__, enable_ps, ges_enable);
+
+ if(enable_ps == 0)
+ {
+ if(ges_enable == 1)
+ {
+ obj->enable_gflag = 1;
+ }
+ else
+ {
+ obj->enable_gflag = 0;
+ }
+ }
+
+ write_global_variable(obj->client);
+ epl_sensor_update_mode(client);
+ return count;
+}
+
+static ssize_t epl_sensor_store_ges_polling_mode(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ges_mode=0;
+ struct epl_sensor_priv *obj = epl_sensor_obj;
+ struct i2c_client *client = obj->client;
+
+ LOG_FUN();
+ sscanf(buf, "%d",&ges_mode);
+ LOG_INFO("[%s]: ges_mode=%d \r\n", __func__, ges_mode);
+
+ epl_sensor.ges.polling_mode = ges_mode;
+ epl_sensor.ps.polling_mode = ges_mode;
+
+ epl_sensor_update_mode(client);
+ return count;
+}
+
+static ssize_t epl_sensor_store_ges_thd(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ int ges_l=0, ges_h=0;
+ struct epl_sensor_priv *obj = epl_sensor_obj;
+ struct i2c_client *client = obj->client;
+
+ LOG_FUN();
+ sscanf(buf, "%d,%d",&ges_l, &ges_h);
+ epl_sensor.ges.low_threshold = ges_l;
+ epl_sensor.ges.high_threshold = ges_h;
+ LOG_INFO("[%s]: ges_thd=%d,%d \r\n", __func__, epl_sensor.ges.low_threshold, epl_sensor.ges.high_threshold);
+
+ write_global_variable(obj->client);
+ epl_sensor_update_mode(client);
+ return count;
+}
+
+#endif
+/*----------------------------------------------------------------------------*/
+
+static DEVICE_ATTR(elan_status, S_IROTH | S_IWOTH, epl_sensor_show_status, NULL );
+static DEVICE_ATTR(elan_reg, S_IROTH | S_IWOTH, epl_sensor_show_reg, NULL );
+static DEVICE_ATTR(mode, S_IROTH | S_IWOTH, NULL, epl_sensor_store_mode );
+static DEVICE_ATTR(wait_time, S_IROTH | S_IWOTH, NULL, epl_sensor_store_wait_time );
+static DEVICE_ATTR(set_threshold, S_IROTH | S_IWOTH, NULL, epl_sensor_store_threshold );
+static DEVICE_ATTR(cal_raw, S_IROTH | S_IWOTH, epl_sensor_show_cal_raw, NULL );
+static DEVICE_ATTR(als_enable, S_IROTH | S_IWOTH, NULL, epl_sensor_store_als_enable );
+static DEVICE_ATTR(als_report_type, S_IROTH | S_IWOTH, NULL, epl_sensor_store_als_report_type );
+static DEVICE_ATTR(ps_enable, S_IROTH | S_IWOTH, NULL, epl_sensor_store_ps_enable );
+static DEVICE_ATTR(ps_polling_mode, S_IROTH | S_IWOTH, epl_sensor_show_ps_polling, epl_sensor_store_ps_polling_mode );
+static DEVICE_ATTR(als_polling_mode, S_IROTH | S_IWOTH, epl_sensor_show_als_polling, epl_sensor_store_als_polling_mode );
+static DEVICE_ATTR(gain, S_IROTH | S_IWOTH, NULL, epl_sensor_store_gain );
+static DEVICE_ATTR(ir_mode, S_IROTH | S_IWOTH, NULL, epl_sensor_store_ir_mode );
+static DEVICE_ATTR(ir_drive, S_IROTH | S_IWOTH, NULL, epl_sensor_store_ir_drive );
+static DEVICE_ATTR(ir_on, S_IROTH | S_IWOTH, NULL, epl_sensor_store_ir_contrl );
+static DEVICE_ATTR(interrupt_type, S_IROTH | S_IWOTH, NULL, epl_sensor_store_interrupt_type );
+static DEVICE_ATTR(integration, S_IROTH | S_IWOTH, NULL, epl_sensor_store_integration );
+static DEVICE_ATTR(adc, S_IROTH | S_IWOTH, NULL, epl_sensor_store_adc );
+static DEVICE_ATTR(cycle, S_IROTH | S_IWOTH, NULL, epl_sensor_store_cycle );
+static DEVICE_ATTR(ps_w_calfile, S_IROTH | S_IWOTH, NULL, epl_sensor_store_ps_w_calfile );
+static DEVICE_ATTR(i2c_w, S_IROTH | S_IWOTH, NULL, epl_sensor_store_reg_write );
+static DEVICE_ATTR(unlock, S_IROTH | S_IWOTH, NULL, epl_sensor_store_unlock );
+static DEVICE_ATTR(als_ch, S_IROTH | S_IWOTH, NULL, epl_sensor_store_als_ch_sel );
+static DEVICE_ATTR(ps_cancel, S_IROTH | S_IWOTH, NULL, epl_sensor_store_ps_cancelation );
+static DEVICE_ATTR(run_ps_cali, S_IROTH | S_IWOTH, epl_sensor_show_ps_run_cali, NULL );
+static DEVICE_ATTR(pdata, S_IROTH | S_IWOTH, epl_sensor_show_pdata, NULL );
+static DEVICE_ATTR(als_data, S_IROTH | S_IWOTH, epl_sensor_show_als_data, NULL );
+#if ALS_DYN_INTT
+static DEVICE_ATTR(als_dyn_c_gain, S_IROTH | S_IWOTH, NULL, epl_sensor_store_c_gain);
+static DEVICE_ATTR(als_dyn_lsrc_type, S_IROTH | S_IWOTH, NULL, epl_sensor_store_lsrc_type);
+static DEVICE_ATTR(als_dyn_lsrc_thd, S_IROTH | S_IWOTH, NULL, epl_sensor_store_lsrc_thd);
+#endif
+#if PS_DYN_K
+static DEVICE_ATTR(dyn_offset, S_IROTH | S_IWOTH, NULL, epl_sensor_store_dyn_offset);
+static DEVICE_ATTR(dyn_thd_offset, S_IROTH | S_IWOTH, NULL, epl_sensor_store_dyn_thd_offset);
+static DEVICE_ATTR(dyn_change_max, S_IROTH | S_IWOTH, NULL, epl_sensor_store_dyn_change_thd_max);
+static DEVICE_ATTR(dyn_max_ir_data, S_IROTH | S_IWOTH, NULL, epl_sensor_store_dyn_max_ir_data );
+#if PS_DYN_K_STR
+static DEVICE_ATTR(dyn_enh_max_ch0, S_IROTH | S_IWOTH, NULL, epl_sensor_store_dyn_enhance_max_ch0 );
+static DEVICE_ATTR(dyn_enh_gain_intt, S_IROTH | S_IWOTH, NULL, epl_sensor_store_dyn_enhance_gain_intt );
+#endif
+#endif
+#if HS_ENABLE
+static DEVICE_ATTR(elan_renvo, S_IROTH | S_IWOTH, epl_sensor_show_renvo, NULL );
+static DEVICE_ATTR(hs_enable, S_IROTH | S_IWOTH, NULL, epl_sensor_store_hs_enable );
+static DEVICE_ATTR(hs_int_time, S_IROTH | S_IWOTH, NULL, epl_sensor_store_hs_int_time );
+static DEVICE_ATTR(hs_raws, S_IROTH | S_IWOTH, epl_sensor_show_hs_raws, NULL );
+#endif
+#if PS_GES
+static DEVICE_ATTR(ges_enable, S_IROTH | S_IWOTH, NULL, epl_sensor_store_ges_enable );
+static DEVICE_ATTR(ges_polling_mode, S_IROTH | S_IWOTH, NULL, epl_sensor_store_ges_polling_mode );
+static DEVICE_ATTR(ges_thd, S_IROTH | S_IWOTH, NULL, epl_sensor_store_ges_thd );
+#endif
+/*----------------------------------------------------------------------------*/
+static struct attribute *epl_sensor_attr_list[] =
+{
+ &dev_attr_elan_status.attr,
+ &dev_attr_elan_reg.attr,
+ &dev_attr_als_enable.attr,
+ &dev_attr_ps_enable.attr,
+ &dev_attr_cal_raw.attr,
+ &dev_attr_set_threshold.attr,
+ &dev_attr_wait_time.attr,
+ &dev_attr_gain.attr,
+ &dev_attr_mode.attr,
+ &dev_attr_ir_mode.attr,
+ &dev_attr_ir_drive.attr,
+ &dev_attr_ir_on.attr,
+ &dev_attr_interrupt_type.attr,
+ &dev_attr_integration.attr,
+ &dev_attr_adc.attr,
+ &dev_attr_cycle.attr,
+ &dev_attr_als_report_type.attr,
+ &dev_attr_ps_polling_mode.attr,
+ &dev_attr_als_polling_mode.attr,
+ &dev_attr_ps_w_calfile.attr,
+ &dev_attr_i2c_w.attr,
+ &dev_attr_unlock.attr,
+ &dev_attr_als_ch.attr,
+ &dev_attr_ps_cancel.attr,
+ &dev_attr_run_ps_cali.attr,
+ &dev_attr_pdata.attr,
+ &dev_attr_als_data.attr,
+#if ALS_DYN_INTT
+ &dev_attr_als_dyn_c_gain.attr,
+ &dev_attr_als_dyn_lsrc_type.attr,
+ &dev_attr_als_dyn_lsrc_thd.attr,
+#endif
+#if PS_DYN_K
+ &dev_attr_dyn_offset.attr,
+ &dev_attr_dyn_thd_offset.attr,
+ &dev_attr_dyn_change_max.attr,
+ &dev_attr_dyn_max_ir_data.attr,
+#if PS_DYN_K_STR
+ &dev_attr_dyn_enh_max_ch0.attr,
+ &dev_attr_dyn_enh_gain_intt.attr,
+#endif
+#endif
+#if HS_ENABLE
+ &dev_attr_elan_renvo.attr,
+ &dev_attr_hs_enable.attr,
+ &dev_attr_hs_int_time.attr,
+ &dev_attr_hs_raws.attr,
+#endif
+#if PS_GES
+ &dev_attr_ges_enable.attr,
+ &dev_attr_ges_polling_mode.attr,
+ &dev_attr_ges_thd.attr,
+#endif
+};
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static struct attribute_group epl_sensor_attr_group =
+{
+ .attrs = epl_sensor_attr_list,
+};
+/*----------------------------------------------------------------------------*/
+#if !(SPREAD || MARVELL)/*SPREAD MARVELL start.....*/
+/*----------------------------------------------------------------------------*/
+static int epl_sensor_als_open(struct inode *inode, struct file *file)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+ LOG_FUN();
+
+ if (epld->als_opened)
+ {
+ return -EBUSY;
+ }
+ epld->als_opened = 1;
+
+ return 0;
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static int epl_sensor_als_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ int buf[1];
+ if(epld->read_flag ==1)
+ {
+ buf[0] = epl_sensor.als.data.channels[1];
+ if(copy_to_user(buffer, &buf , sizeof(buf)))
+ return 0;
+ epld->read_flag = 0;
+ return 12;
+ }
+ else
+ {
+ return 0;
+ }
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static int epl_sensor_als_release(struct inode *inode, struct file *file)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+ LOG_FUN();
+
+ epld->als_opened = 0;
+
+ return 0;
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static long epl_sensor_als_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int flag;
+ unsigned long buf[1];
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ bool enable_als = epld->enable_lflag==1 && epld->als_suspend==0;
+ void __user *argp = (void __user *)arg;
+
+ LOG_INFO("als io ctrl cmd %d\n", _IOC_NR(cmd));
+
+ switch(cmd)
+ {
+ case ELAN_EPL8800_IOCTL_GET_LFLAG:
+
+ LOG_INFO("elan ambient-light IOCTL Sensor get lflag \n");
+ flag = epld->enable_lflag;
+ if (copy_to_user(argp, &flag, sizeof(flag)))
+ return -EFAULT;
+
+ LOG_INFO("elan ambient-light Sensor get lflag %d\n",flag);
+ break;
+
+ case ELAN_EPL8800_IOCTL_ENABLE_LFLAG:
+#if LEADCORE
+ case LIGHT_SET_ENALBE:
+#endif
+ LOG_INFO("elan ambient-light IOCTL Sensor set lflag \n");
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ if (flag < 0 || flag > 1)
+ return -EINVAL;
+
+ if(epld->enable_lflag != flag)
+ {
+#if ALS_DYN_INTT
+ if(epl_sensor.als.report_type == CMC_BIT_DYN_INT)
+ {
+ dynamic_intt_idx = dynamic_intt_init_idx;
+ epl_sensor.als.integration_time = als_dynamic_intt_intt[dynamic_intt_idx];
+ epl_sensor.als.gain = als_dynamic_intt_gain[dynamic_intt_idx];
+ dynamic_intt_high_thr = als_dynamic_intt_high_thr[dynamic_intt_idx];
+ dynamic_intt_low_thr = als_dynamic_intt_low_thr[dynamic_intt_idx];
+ }
+#endif
+ epld->enable_lflag = flag;
+
+ epl_sensor_update_mode(epld->client);
+ }
+
+ LOG_INFO("elan ambient-light Sensor set lflag %d\n",flag);
+ break;
+
+ case ELAN_EPL8800_IOCTL_GETDATA:
+ if(enable_als == 0)
+ {
+ epld->enable_lflag = 1;
+ epl_sensor_update_mode(epld->client);
+ msleep(30);
+ }
+
+ if(enable_als == true && polling_flag == true && eint_flag == true && epl_sensor.als.polling_mode == 0)
+ {
+ mutex_lock(&sensor_mutex);
+ epl_sensor_read_als(epld->client);
+ mutex_unlock(&sensor_mutex);
+ }
+#if ALS_DYN_INTT
+ if(epl_sensor.als.report_type == CMC_BIT_DYN_INT)
+ {
+ buf[0] = dynamic_intt_lux;
+ LOG_INFO("[%s]: als dynamic_intt_lux = %d \r\n", __func__, dynamic_intt_lux);
+ }
+ else
+#else
+ {
+ buf[0] = epl_sensor.als.data.channels[1];
+ LOG_INFO("[%s]: epl_sensor.als.data.channels[1] = %d \r\n", __func__, epl_sensor.als.data.channels[1]);
+ }
+#endif
+
+ if(copy_to_user(argp, &buf , sizeof(buf)))
+ return -EFAULT;
+
+ break;
+#if LEADCORE
+ case LIGHT_SET_DELAY:
+
+ if (arg > LIGHT_MAX_DELAY)
+ arg = LIGHT_MAX_DELAY;
+ else if (arg < LIGHT_MIN_DELAY)
+ arg = LIGHT_MIN_DELAY;
+ LOG_INFO("LIGHT_SET_DELAY--%d\r\n",(int)arg);
+ polling_time = arg;
+ break;
+#endif
+ default:
+ LOG_ERR("invalid cmd %d\n", _IOC_NR(cmd));
+ return -EINVAL;
+ }
+
+ return 0;
+
+
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static struct file_operations epl_sensor_als_fops =
+{
+ .owner = THIS_MODULE,
+ .open = epl_sensor_als_open,
+ .read = epl_sensor_als_read,
+ .release = epl_sensor_als_release,
+ .unlocked_ioctl = epl_sensor_als_ioctl
+};
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static struct miscdevice epl_sensor_als_device =
+{
+ .minor = MISC_DYNAMIC_MINOR,
+#if LEADCORE
+ .name = "light",
+#else
+ .name = "elan_als",
+#endif
+ .fops = &epl_sensor_als_fops
+};
+/*----------------------------------------------------------------------------*/
+#endif /*SPREAD MARVELL end.........*/
+
+/*----------------------------------------------------------------------------*/
+static int epl_sensor_ps_open(struct inode *inode, struct file *file)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+ LOG_FUN();
+
+ if (epld->ps_opened)
+ return -EBUSY;
+
+ epld->ps_opened = 1;
+
+ return 0;
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static int epl_sensor_ps_release(struct inode *inode, struct file *file)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+
+ LOG_FUN();
+
+ epld->ps_opened = 0;
+
+ return 0;
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+
+static int epl259x_read_chip_info(struct i2c_client *client, char *buf)
+{
+ if(NULL == buf) {
+ return -1;
+ }
+ if(NULL == client) {
+ *buf = 0;
+ return -2;
+ }
+
+ sprintf(buf, "EPL2182");
+ printk("[EPL259x] epl259x_read_chip_info %s\n",buf);
+ return 0;
+}
+
+static long epl_sensor_ps_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int value;
+ int flag,err =0;
+ char strbuf[100];
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ bool enable_ps = epld->enable_pflag==1 && epld->ps_suspend==0;
+#if HS_ENABLE
+ bool enable_hs = epld->enable_hflag==1 && epld->hs_suspend==0;
+#endif
+#if PS_GES
+ bool enable_ges = epld->enable_gflag==1 && epld->ges_suspend==0;
+#endif
+ void __user *argp = (void __user *)arg;
+
+ LOG_INFO("ps io ctrl cmd %d\n", _IOC_NR(cmd));
+
+ //ioctl message handle must define by android sensor library (case by case)
+ switch(cmd)
+ {
+ case ELAN_EPL8800_IOCTL_GET_PFLAG:
+#if MARVELL
+ case LTR_IOCTL_GET_PFLAG:
+#endif
+ LOG_INFO("elan Proximity Sensor IOCTL get pflag \n");
+ flag = epld->enable_pflag;
+ if (copy_to_user(argp, &flag, sizeof(flag)))
+ return -EFAULT;
+
+ LOG_INFO("elan Proximity Sensor get pflag %d\n",flag);
+ break;
+
+ case ELAN_EPL8800_IOCTL_ENABLE_PFLAG:
+#if LEADCORE
+ case PROXIMITY_SET_ENALBE:
+#elif MARVELL
+ case LTR_IOCTL_SET_PFLAG:
+#endif
+ LOG_INFO("elan Proximity IOCTL Sensor set pflag \n");
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ if (flag < 0 || flag > 1)
+ return -EINVAL;
+#if HS_ENABLE
+ if(enable_hs == 1 && flag == 1)
+ {
+ epld->enable_hflag = 0;
+ if(hs_enable_flag == true)
+ {
+ epld->enable_lflag = 1;
+ hs_enable_flag = false;
+ }
+ write_global_variable(epld->client);
+ LOG_INFO("[%s] Disable HS and recover ps setting \r\n", __func__);
+ }
+#endif
+#if PS_GES
+ if(enable_ges == 1 && flag == 1)
+ {
+ epld->enable_gflag = 0;
+ write_global_variable(epld->client);
+ ps_ges_enable_flag = true;
+ LOG_INFO("[%s] Disable GES and recover ps setting \r\n", __func__);
+ }
+ else if (ps_ges_enable_flag == true && flag == 0)
+ {
+ epld->enable_gflag = 1;
+ write_global_variable(epld->client);
+ ps_ges_enable_flag = false;
+ LOG_INFO("[%s] enable GES and recover ges setting \r\n", __func__);
+ }
+#endif
+ if(epld->enable_pflag != flag)
+ {
+ epld->enable_pflag = flag;
+ if(flag)
+ {
+ wake_lock(&ps_lock);
+#if PS_DYN_K
+ dynk_min_ps_raw_data = 0xffff;
+ dynk_change_flag = false;
+#if PS_DYN_K_STR
+ dynk_enhance_flag = false;
+#endif
+#endif
+ }else{
+#if PS_DYN_K
+ cancel_delayed_work(&dynk_thd_polling_work);
+#endif
+ wake_unlock(&ps_lock);
+ }
+ epl_sensor_update_mode(epld->client);
+ }
+
+ LOG_INFO("elan Proximity Sensor set pflag %d\n",flag);
+ break;
+
+ case ELAN_EPL8800_IOCTL_GETDATA:
+ if(enable_ps == 0)
+ {
+ epld->enable_pflag = 1;
+ epl_sensor_update_mode(epld->client);
+ msleep(30);
+ }
+
+ if(enable_ps == true && polling_flag == true && eint_flag == true && epl_sensor.ps.polling_mode == 0)
+ {
+ mutex_lock(&sensor_mutex);
+ epl_sensor_read_ps(epld->client);
+ mutex_unlock(&sensor_mutex);
+ }
+
+ LOG_INFO("[%s]: epl_sensor.ps.data.data = %d \r\n", __func__, epl_sensor.ps.data.data);
+
+ value = epl_sensor.ps.data.data;
+ if(copy_to_user(argp, &value , sizeof(value)))
+ return -EFAULT;
+
+ LOG_INFO("elan proximity Sensor get data (%d) \n",value);
+ break;
+#if LEADCORE
+ case PROXIMITY_SET_DELAY:
+ if (arg > PROXIMITY_MAX_DELAY)
+ arg = PROXIMITY_MAX_DELAY;
+ else if (arg < PROXIMITY_MIN_DELAY)
+ arg = PROXIMITY_MIN_DELAY;
+ LOG_INFO("PROXIMITY_SET_DELAY--%d\r\n",(int)arg);
+ polling_time = arg;
+ break;
+#endif
+
+#if SPREAD || MARVELL /*SPREAD MARVELL start.....*/
+ case ELAN_EPL8800_IOCTL_GET_LFLAG:
+#if MARVELL
+ case LTR_IOCTL_GET_LFLAG:
+#endif
+ LOG_INFO("elan ambient-light IOCTL Sensor get lflag \n");
+ flag = epld->enable_lflag;
+ if (copy_to_user(argp, &flag, sizeof(flag)))
+ return -EFAULT;
+
+ LOG_INFO("elan ambient-light Sensor get lflag %d\n",flag);
+ break;
+
+ case ELAN_EPL8800_IOCTL_GET_CHIPINFO:
+ err = epl259x_read_chip_info(this_client, strbuf);
+ if(err < 0)
+ return -EFAULT;
+ if(copy_to_user(argp, strbuf, strlen(strbuf)+1))
+ return -EFAULT;
+ break;
+
+ case ELAN_EPL8800_IOCTL_ENABLE_LFLAG:
+#if MARVELL
+ case LTR_IOCTL_SET_LFLAG:
+#endif
+ LOG_INFO("elan ambient-light IOCTL Sensor set lflag \n");
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ if (flag < 0 || flag > 1)
+ return -EINVAL;
+
+ if(epld->enable_lflag != flag)
+ {
+#if ALS_DYN_INTT
+ if(epl_sensor.als.report_type == CMC_BIT_DYN_INT)
+ {
+ dynamic_intt_idx = dynamic_intt_init_idx;
+ epl_sensor.als.integration_time = als_dynamic_intt_intt[dynamic_intt_idx];
+ epl_sensor.als.gain = als_dynamic_intt_gain[dynamic_intt_idx];
+ dynamic_intt_high_thr = als_dynamic_intt_high_thr[dynamic_intt_idx];
+ dynamic_intt_low_thr = als_dynamic_intt_low_thr[dynamic_intt_idx];
+ }
+#endif
+ epld->enable_lflag = flag;
+
+ epl_sensor_update_mode(epld->client);
+ }
+
+ LOG_INFO("elan ambient-light Sensor set lflag %d\n",flag);
+ break;
+#if 0
+ case ELAN_EPL8800_IOCTL_GETDATA:
+ if(enable_als == 0)
+ {
+ epld->enable_lflag = 1;
+ epl_sensor_update_mode(epld->client);
+ msleep(30);
+ }
+
+ if(enable_als == true && polling_flag == true && eint_flag == true && epl_sensor.als.polling_mode == 0)
+ {
+ mutex_lock(&sensor_mutex);
+ epl_sensor_read_als(epld->client);
+ mutex_unlock(&sensor_mutex);
+ }
+#if ALS_DYN_INTT
+ if(epl_sensor.als.report_type == CMC_BIT_DYN_INT)
+ {
+ buf[0] = dynamic_intt_lux;
+ LOG_INFO("[%s]: als dynamic_intt_lux = %d \r\n", __func__, dynamic_intt_lux);
+ }
+ else
+#else
+ {
+ buf[0] = epl_sensor.als.data.channels[1];
+ LOG_INFO("[%s]: epl_sensor.als.data.channels[1] = %d \r\n", __func__, epl_sensor.als.data.channels[1]);
+ }
+#endif
+
+ if(copy_to_user(argp, &buf , sizeof(buf)))
+ return -EFAULT;
+
+ break;
+#endif
+#endif /*SPREAD MARVELL end......*/
+ default:
+ LOG_ERR("invalid cmd %d\n", _IOC_NR(cmd));
+ return -EINVAL;
+ }
+
+ return 0;
+
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static struct file_operations epl_sensor_ps_fops =
+{
+ .owner = THIS_MODULE,
+ .open = epl_sensor_ps_open,
+ .release = epl_sensor_ps_release,
+ .unlocked_ioctl = epl_sensor_ps_ioctl
+};
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static struct miscdevice epl_sensor_ps_device =
+{
+ .minor = MISC_DYNAMIC_MINOR,
+#if LEADCORE
+ .name = "proximity",
+#elif MARVELL
+ .name = "alps_pxy",
+#elif SPREAD
+ .name = "epl2182_pls",
+#endif
+ .fops = &epl_sensor_ps_fops
+};
+
+#if !(SPREAD || MARVELL) /*SPREAD MARVELL start.....*/
+/*----------------------------------------------------------------------------*/
+static ssize_t light_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ LOG_INFO("%s: ALS_status=%d\n", __func__, epld->enable_lflag);
+ return sprintf(buf, "%d\n", epld->enable_lflag);
+}
+
+static ssize_t light_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ uint16_t als_enable = 0;
+ LOG_INFO("light_enable_store: enable=%s \n", buf);
+
+ sscanf(buf, "%hu",&als_enable);
+
+ if(epld->enable_lflag != als_enable)
+ {
+ epld->enable_lflag = als_enable;
+
+ epl_sensor_update_mode(epld->client);
+ }
+
+ return size;
+}
+/*----------------------------------------------------------------------------*/
+static struct device_attribute dev_attr_light_enable =
+__ATTR(enable, S_IRWXUGO,
+ light_enable_show, light_enable_store);
+
+static struct attribute *light_sysfs_attrs[] = {
+ &dev_attr_light_enable.attr,
+ NULL
+};
+
+static struct attribute_group light_attribute_group = {
+ .attrs = light_sysfs_attrs,
+};
+/*----------------------------------------------------------------------------*/
+static int epl_sensor_setup_lsensor(struct epl_sensor_priv *epld)
+{
+ int err = 0;
+ LOG_INFO("epl_sensor_setup_lsensor enter.\n");
+
+ epld->als_input_dev = input_allocate_device();
+ if (!epld->als_input_dev)
+ {
+ LOG_ERR( "could not allocate ls input device\n");
+ return -ENOMEM;
+ }
+ epld->als_input_dev->name = ElanALsensorName;
+ set_bit(EV_ABS, epld->als_input_dev->evbit);
+ input_set_abs_params(epld->als_input_dev, ABS_MISC, 0, 9, 0, 0);
+
+ err = input_register_device(epld->als_input_dev);
+ if (err < 0)
+ {
+ LOG_ERR("can not register ls input device\n");
+ goto err_free_ls_input_device;
+ }
+
+ err = misc_register(&epl_sensor_als_device);
+ if (err < 0)
+ {
+ LOG_ERR("can not register ls misc device\n");
+ goto err_unregister_ls_input_device;
+ }
+
+ err = sysfs_create_group(&epld->als_input_dev->dev.kobj, &light_attribute_group);
+
+ if (err) {
+ pr_err("%s: could not create sysfs group\n", __func__);
+ goto err_free_ls_input_device;
+ }
+ return err;
+
+
+err_unregister_ls_input_device:
+ input_unregister_device(epld->als_input_dev);
+err_free_ls_input_device:
+ input_free_device(epld->als_input_dev);
+ return err;
+}
+#endif /*SPREAD MARVELL end.....*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static ssize_t proximity_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ LOG_INFO("%s: PS status=%d\n", __func__, epld->enable_pflag);
+ return sprintf(buf, "%d\n", epld->enable_pflag);
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static ssize_t proximity_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ uint16_t ps_enable = 0;
+#if HS_ENABLE
+ bool enable_hs = epld->enable_hflag==1 && epld->hs_suspend==0;
+#endif
+#if PS_GES
+ bool enable_ges = epld->enable_gflag==1 && epld->ges_suspend==0;
+#endif
+ LOG_INFO("proximity_enable_store: enable=%s \n", buf);
+
+ sscanf(buf, "%hu",&ps_enable);
+
+#if HS_ENABLE
+ if(enable_hs == 1 && ps_enable == 1)
+ {
+ epld->enable_hflag = 0;
+ if(hs_enable_flag == true)
+ {
+ epld->enable_lflag = 1;
+ hs_enable_flag = false;
+ }
+ write_global_variable(epld->client);
+ LOG_INFO("[%s] Disable HS and recover ps setting \r\n", __func__);
+ }
+#endif
+#if PS_GES
+ if(enable_ges == 1 && ps_enable == 1)
+ {
+ epld->enable_gflag = 0;
+ write_global_variable(epld->client);
+ ps_ges_enable_flag = true;
+ LOG_INFO("[%s] Disable GES and recover ps setting \r\n", __func__);
+ }
+ else if (ps_ges_enable_flag == true && ps_enable == 0)
+ {
+ epld->enable_gflag = 1;
+ write_global_variable(epld->client);
+ ps_ges_enable_flag = false;
+ LOG_INFO("[%s] enable GES and recover ges setting \r\n", __func__);
+ }
+#endif
+ if(epld->enable_pflag != ps_enable)
+ {
+ epld->enable_pflag = ps_enable;
+ if(ps_enable)
+ {
+ wake_lock(&ps_lock);
+#if PS_DYN_K
+ dynk_min_ps_raw_data = 0xffff;
+ dynk_change_flag = false;
+#if PS_DYN_K_STR
+ dynk_enhance_flag = false;
+#endif
+#endif
+ }
+ else
+ {
+#if PS_DYN_K
+ cancel_delayed_work(&dynk_thd_polling_work);
+#endif
+ wake_unlock(&ps_lock);
+ }
+
+ epl_sensor_update_mode(epld->client);
+ }
+
+ return size;
+}
+/*----------------------------------------------------------------------------*/
+static struct device_attribute dev_attr_psensor_enable =
+__ATTR(enable, S_IRWXUGO,
+ proximity_enable_show, proximity_enable_store);
+
+static struct attribute *proximity_sysfs_attrs[] = {
+ &dev_attr_psensor_enable.attr,
+ NULL
+};
+
+static struct attribute_group proximity_attribute_group = {
+ .attrs = proximity_sysfs_attrs,
+};
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static int epl_sensor_setup_psensor(struct epl_sensor_priv *epld)
+{
+ int err = 0;
+ LOG_INFO("epl_sensor_setup_psensor enter.\n");
+
+
+ epld->ps_input_dev = input_allocate_device();
+ if (!epld->ps_input_dev)
+ {
+ LOG_ERR("could not allocate ps input device\n");
+ return -ENOMEM;
+ }
+ epld->ps_input_dev->name = ElanPsensorName;
+
+ set_bit(EV_ABS, epld->ps_input_dev->evbit);
+ input_set_abs_params(epld->ps_input_dev, ABS_DISTANCE, 0, 1, 0, 0);
+#if 1
+ set_bit(EV_ABS, epld->ps_input_dev->evbit);
+ input_set_abs_params(epld->ps_input_dev, ABS_MISC, 0, 9, 0, 0);
+#endif
+
+ err = input_register_device(epld->ps_input_dev);
+ if (err < 0)
+ {
+ LOG_ERR("could not register ps input device\n");
+ goto err_free_ps_input_device;
+ }
+
+ err = misc_register(&epl_sensor_ps_device);
+ if (err < 0)
+ {
+ LOG_ERR("could not register ps misc device\n");
+ goto err_unregister_ps_input_device;
+ }
+
+ err = sysfs_create_group(&epld->ps_input_dev->dev.kobj, &proximity_attribute_group);
+
+ if (err) {
+ pr_err("%s: PS could not create sysfs group\n", __func__);
+ goto err_free_ps_input_device;
+ }
+
+ return err;
+
+err_unregister_ps_input_device:
+ input_unregister_device(epld->ps_input_dev);
+err_free_ps_input_device:
+ input_free_device(epld->ps_input_dev);
+ return err;
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+#ifdef CONFIG_SUSPEND
+static int epl_sensor_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ LOG_FUN();
+
+ epld->als_suspend=1;
+#if HS_ENABLE
+ epld->hs_suspend=1;
+#endif
+#if PS_GES
+ epld->ges_suspend = 1;
+ ps_ges_suspend_flag = true;
+#endif
+ if(epld->enable_pflag == 1){
+ //epld->ps_suspend=0;
+ LOG_INFO("[%s]: ps enable \r\n", __func__);
+ }
+ else{
+ //epld->ps_suspend=1;
+ LOG_INFO("[%s]: ps disable \r\n", __func__);
+ epl_sensor_update_mode(epld->client);
+ }
+
+ return 0;
+}
+
+static int epl_sensor_resume(struct i2c_client *client)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ LOG_FUN();
+
+ epld->als_suspend=0;
+ epld->ps_suspend=0;
+#if HS_ENABLE
+ epld->hs_suspend=0;
+#endif
+#if PS_GES
+ epld->ges_suspend = 0;
+#endif
+ if(epld->enable_pflag == 1){
+ //epld->ps_suspend=0;
+ LOG_INFO("[%s]: ps enable \r\n", __func__);
+ epl_sensor_restart_polling();
+ }
+ else{
+ //epld->ps_suspend=1;
+ LOG_INFO("[%s]: ps disable \r\n", __func__);
+ epl_sensor_update_mode(epld->client);
+ }
+
+#if !defined(CONFIG_HAS_EARLYSUSPEND) && PS_GES
+ ps_ges_suspend_flag = false;
+#endif
+ return 0;
+}
+#endif
+
+/*----------------------------------------------------------------------------*/
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+static void epl_sensor_early_suspend(struct early_suspend *h)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ LOG_FUN();
+
+ epld->als_suspend=1;
+#if HS_ENABLE
+ epld->hs_suspend=1;
+#endif
+#if PS_GES
+ epld->ges_suspend = 1;
+ ps_ges_suspend_flag = true;
+#endif
+ if(epld->enable_pflag == 1)
+ {
+ //epld->ps_suspend=0;
+ LOG_INFO("[%s]: ps enable \r\n", __func__);
+ }
+ else
+ {
+ //epld->ps_suspend=1;
+ LOG_INFO("[%s]: ps disable \r\n", __func__);
+ epl_sensor_update_mode(epld->client);
+ }
+
+}
+
+static void epl_sensor_late_resume(struct early_suspend *h)
+{
+ struct epl_sensor_priv *epld = epl_sensor_obj;
+ LOG_FUN();
+
+ epld->als_suspend=0;
+ epld->ps_suspend=0;
+#if HS_ENABLE
+ epld->hs_suspend=0;
+#endif
+#if PS_GES
+ epld->ges_suspend = 0;
+#endif
+ if(epld->enable_pflag == 1){
+ //epld->ps_suspend=0;
+ LOG_INFO("[%s]: ps enable \r\n", __func__);
+ epl_sensor_restart_polling();
+ }
+ else{
+ //epld->ps_suspend=1;
+ LOG_INFO("[%s]: ps disable \r\n", __func__);
+ epl_sensor_update_mode(epld->client);
+ }
+#if PS_GES
+ ps_ges_suspend_flag = false;
+#endif
+}
+#endif
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static int epl_sensor_probe(struct i2c_client *client,const struct i2c_device_id *id)
+{
+ int err = 0;
+ struct epl_sensor_priv *epld ;
+ struct elan_epl_platform_data *pdata = client->dev.platform_data;
+ struct device_node *np = client->dev.of_node;
+ LOG_INFO("elan sensor probe enter.\n");
+
+#ifdef CONFIG_OF
+ if (np && !pdata){
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev, "Could not allocate struct elan_epl_platform_data");
+ goto exit_allocate_pdata_failed;
+ }
+ pdata->irq_gpio_number = of_get_gpio(np, 0);
+ if(pdata->irq_gpio_number < 0){
+ dev_err(&client->dev, "fail to get irq_gpio_number\n");
+ kfree(pdata);
+ goto exit_irq_gpio_read_fail;
+ }
+ client->dev.platform_data = pdata;
+ }
+#endif
+
+ epld = kzalloc(sizeof(struct epl_sensor_priv), GFP_KERNEL);
+ if (!epld)
+ return -ENOMEM;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ {
+ dev_err(&client->dev,"No supported i2c func what we need?!!\n");
+ err = -ENOTSUPP;
+ goto i2c_fail;
+ }
+ LOG_INFO("chip id REG 0x00 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x00));
+ LOG_INFO("chip id REG 0x01 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x01));
+ LOG_INFO("chip id REG 0x02 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x02));
+ LOG_INFO("chip id REG 0x03 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x03));
+ LOG_INFO("chip id REG 0x04 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x04));
+ LOG_INFO("chip id REG 0x05 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x05));
+ LOG_INFO("chip id REG 0x06 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x06));
+ LOG_INFO("chip id REG 0x07 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x07));
+ LOG_INFO("chip id REG 0x11 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x11));
+ LOG_INFO("chip id REG 0x12 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x12));
+ LOG_INFO("chip id REG 0x1B value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x1B));
+ LOG_INFO("chip id REG 0x20 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x20));
+ LOG_INFO("chip id REG 0x21 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x21));
+ LOG_INFO("chip id REG 0x24 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x24));
+ LOG_INFO("chip id REG 0x25 value = 0x%x\n", i2c_smbus_read_byte_data(client, 0x25));
+
+ if((i2c_smbus_read_byte_data(client, 0x21)) != 0x81){
+ LOG_INFO("elan ALS/PS sensor is failed. \n");
+ goto i2c_fail;
+ }
+
+ epld->als_level_num = sizeof(epld->als_level)/sizeof(epld->als_level[0]);
+ epld->als_value_num = sizeof(epld->als_value)/sizeof(epld->als_value[0]);
+ BUG_ON(sizeof(epld->als_level) != sizeof(als_level));
+ memcpy(epld->als_level, als_level, sizeof(epld->als_level));
+ BUG_ON(sizeof(epld->als_value) != sizeof(als_value));
+ memcpy(epld->als_value, als_value, sizeof(epld->als_value));
+
+ epld->client = client;
+ this_client = client;
+ epld->intr_pin= pdata->irq_gpio_number;
+
+#if HS_ENABLE
+ epld->hs_suspend = 0;
+ mutex_init(&hs_sensor_mutex);
+#endif
+#if PS_GES
+ epld->ges_suspend = 0;
+ epld->gs_input_dev = input_allocate_device();
+ set_bit(EV_KEY, epld->gs_input_dev->evbit);
+ set_bit(EV_REL, epld->gs_input_dev->evbit);
+ set_bit(EV_ABS, epld->gs_input_dev->evbit);
+ epld->gs_input_dev->evbit[0] |= BIT_MASK(EV_REP);
+ epld->gs_input_dev->keycodemax = 500;
+ epld->gs_input_dev->name ="elan_gesture";
+ epld->gs_input_dev->keybit[BIT_WORD(KEYCODE_LEFT)] |= BIT_MASK(KEYCODE_LEFT);
+ if (input_register_device(epld->gs_input_dev))
+ LOG_ERR("register input error\n");
+#endif
+ i2c_set_clientdata(client, epld);
+
+ epl_sensor_obj = epld;
+
+ INIT_DELAYED_WORK(&epld->eint_work, epl_sensor_eint_work);
+
+ epld->epl_wq = create_workqueue("epl2182_pls"); //create_singlethread_workqueue
+ if (!epld->epl_wq)
+ {
+ LOG_ERR("can't create workqueue\n");
+ err = -ENOMEM;
+ goto err_create_singlethread_workqueue;
+ }
+
+ mutex_init(&sensor_mutex);
+
+ //initial global variable and write to senosr
+ initial_global_variable(client, epld);
+#if !(SPREAD || MARVELL)
+ err = epl_sensor_setup_lsensor(epld);
+ if (err < 0)
+ {
+ LOG_ERR("epl_sensor_setup_lsensor error!!\n");
+ goto err_lightsensor_setup;
+ }
+#endif
+
+ err = epl_sensor_setup_psensor(epld);
+ if (err < 0)
+ {
+ LOG_ERR("epl_sensor_setup_psensor error!!\n");
+ goto err_psensor_setup;
+ }
+
+
+ if (epl_sensor.als.polling_mode==0 || epl_sensor.ps.polling_mode==0)
+ {
+ err = epl_sensor_setup_interrupt(epld);
+ if (err < 0)
+ {
+ LOG_ERR("setup error!\n");
+ goto err_sensor_setup;
+ }
+ }
+ //disable_irq(epld->irq); //ices add
+
+#ifdef CONFIG_SUSPEND
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+ epld->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ epld->early_suspend.suspend = epl_sensor_early_suspend;
+ epld->early_suspend.resume = epl_sensor_late_resume;
+ register_early_suspend(&epld->early_suspend);
+#endif
+
+#endif
+
+ wake_lock_init(&ps_lock, WAKE_LOCK_SUSPEND, "ps wakelock");
+#if 0
+ sensor_dev = platform_device_register_simple("elan_alsps", -1, NULL, 0);
+ if (IS_ERR(sensor_dev))
+ {
+ printk ("sensor_dev_init: error\n");
+ goto err_fail;
+ }
+
+
+ err = sysfs_create_group(&sensor_dev->dev.kobj, &epl_sensor_attr_group);
+ if (err !=0)
+ {
+ dev_err(&client->dev,"%s:create sysfs group error", __func__);
+ goto err_fail;
+ }
+#endif
+ LOG_INFO("sensor probe success.\n");
+ return err;
+
+err_fail:
+ input_unregister_device(epld->als_input_dev);
+ input_unregister_device(epld->ps_input_dev);
+ input_free_device(epld->als_input_dev);
+ input_free_device(epld->ps_input_dev);
+#if !(SPREAD || MARVELL)
+err_lightsensor_setup:
+#endif
+err_psensor_setup:
+err_sensor_setup:
+ destroy_workqueue(epld->epl_wq);
+ misc_deregister(&epl_sensor_ps_device);
+#if !(SPREAD || MARVELL)
+ misc_deregister(&epl_sensor_als_device);
+#endif
+err_create_singlethread_workqueue:
+i2c_fail:
+ kfree(epld);
+#ifdef CONFIG_OF
+exit_irq_gpio_read_fail:
+exit_allocate_pdata_failed:
+#endif
+
+ return err;
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static int epl_sensor_remove(struct i2c_client *client)
+{
+ struct epl_sensor_priv *epld = i2c_get_clientdata(client);
+
+ dev_dbg(&client->dev, "%s: enter.\n", __func__);
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+ unregister_early_suspend(&epld->early_suspend);
+#endif
+ sysfs_remove_group(&sensor_dev->dev.kobj, &epl_sensor_attr_group);
+ platform_device_unregister(sensor_dev);
+ input_unregister_device(epld->als_input_dev);
+ input_unregister_device(epld->ps_input_dev);
+#if !(SPREAD || MARVELL)
+ input_free_device(epld->als_input_dev);
+#endif
+ input_free_device(epld->ps_input_dev);
+ misc_deregister(&epl_sensor_ps_device);
+#if !(SPREAD || MARVELL)
+ misc_deregister(&epl_sensor_als_device);
+#endif
+ free_irq(epld->irq,epld);
+ destroy_workqueue(epld->epl_wq);
+ kfree(epld);
+ return 0;
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static const struct i2c_device_id epl_sensor_id[] =
+{
+ { EPL_DEV_NAME, 0 },
+ {}
+};
+
+#if SPREAD
+static struct of_device_id epl_match_table[] = {
+ { .compatible = "ELAN,epl259x_pls", },
+ {}
+};
+#endif
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+static struct i2c_driver epl_sensor_driver =
+{
+ .probe = epl_sensor_probe,
+ .remove = epl_sensor_remove,
+ .id_table = epl_sensor_id,
+ .driver = {
+ .name = EPL_DEV_NAME,
+ .owner = THIS_MODULE,
+#if SPREAD
+ .of_match_table =epl_match_table,
+#endif
+ },
+#ifdef CONFIG_SUSPEND
+ //.suspend = epl_sensor_suspend,
+ //.resume = epl_sensor_resume,
+#endif
+};
+
+static int __init epl_sensor_init(void)
+{
+ return i2c_add_driver(&epl_sensor_driver);
+}
+
+static void __exit epl_sensor_exit(void)
+{
+ i2c_del_driver(&epl_sensor_driver);
+}
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+module_init(epl_sensor_init);
+module_exit(epl_sensor_exit);
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+MODULE_AUTHOR("Renato Pan <renato.pan@eminent-tek.com>");
+MODULE_DESCRIPTION("ELAN epl259x driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ewtsa.c b/drivers/input/misc/ewtsa.c
new file mode 100644
index 00000000..bdb0b973
--- /dev/null
+++ b/drivers/input/misc/ewtsa.c
@@ -0,0 +1,2763 @@
+/*
+ * linux/drivers/input/misc/ewtsa.c
+ *
+ * Copyright 2010-2011 Panasonic Electronic Devices. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*include files*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/regulator/consumer.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <mach/hardware.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+
+/* configurable */
+#define GYRO_MOUNT_SWAP_XY 0 /* swap X, Y */
+#define GYRO_MOUNT_REVERSE_X 0 /* reverse X */
+#define GYRO_MOUNT_REVERSE_Y 0 /* reverse Y */
+#define GYRO_MOUNT_REVERSE_Z 0 /* reverse Z */
+#define GYRO_VER3_ES1 0
+#define GYRO_VER3_ES3 0
+#define GYRO_VER3_ES4 1
+#define GYRO_REGISTER_MAP2 0
+#define GYRO_SPI_INTERFACE 0
+
+#define USE_GPIO_DRDY_PIN 0
+#define USE_GPIO_DIAG_PIN 0
+#define USE_GPIO_WINDOW_PIN 0
+
+/* macro defines */
+#define CHIP_ID 0x68
+#define DEVICE_NAME "ewtsa"
+#define EWTSA_ON 1
+#define EWTSA_OFF 0
+#define WINDOW_PIN 14
+#define DRDY_PIN 12
+#define DIAG_PIN 11
+#define DIAG_ENABLE 0
+#define DIAG_DISABLE 1
+#define MAX_VALUE 32768
+
+/* ewtsa_delay parameter */
+#define DELAY_THRES_MIN 1
+#define DELAY_THRES_1 4
+#define DELAY_THRES_2 9 /* msec x 90% */
+#define DELAY_THRES_3 18
+#define DELAY_THRES_4 45
+#define DELAY_THRES_5 90
+#define DELAY_THRES_6 128
+#define DELAY_THRES_MAX 255
+#define DELAY_DLPF_0 0
+#define DELAY_DLPF_1 1
+#define DELAY_DLPF_2 2
+#define DELAY_DLPF_3 3
+#define DELAY_DLPF_4 4
+#define DELAY_DLPF_5 5
+#define DELAY_DLPF_6 6
+#define DELAY_INTMIN_THRES 9
+#define DELAY_INT8K_THRES 79
+#define DELAY_THRES_FIFO 150
+
+/* ewtsa_range parameter */
+#define DYNRANGE_250 0x00
+#define DYNRANGE_500 0x01
+#define DYNRANGE_1000 0x02
+#define DYNRANGE_2000 0x03
+
+#define DATA_RATE_1 0x01
+#define DATA_RATE_5 0x05
+#define DATA_RATE_8 0x08
+#define DATA_RATE_10 0x0A
+
+/* ewtsa_sleep parameter */
+#define SLEEP_OFF 0
+#define SLEEP_ON 1
+#define SLEEP_ALLRESET 2
+
+/* ewtsa_calib parameter */
+#define BOOST_TIME_500MS 0
+#define BOOST_TIME_300MS 1
+#define BOOST_TIME_200MS 2
+#define BOOST_TIME_100MS 3
+#define BOOST_FS_48KHZ 0
+#define BOOST_FS_16KHZ 1
+#define HPF_001HZ 0
+#define HPF_002HZ 1
+#define MASK_CLIP 0
+#define MASK_NON_CLIP 1
+#define THRESHOLD_ON 0
+#define THRESHOLD_OFF 1
+#define LAG_NON 0
+#define LAG_5SMP 1
+#define LAG_10SMP 2
+#define LAG_20SMP 3
+#define W1_THRES_LEVEL_2DPS 0
+#define W1_THRES_LEVEL_5DPS 1
+#define W1_THRES_LEVEL_10DPS 2
+#define W1_THRES_LEVEL_15DPS 3
+#define REFERENCE_WAIT 16
+#define W1_THRES_CENTER_COEF 25000
+#define CALIB_FS_62_5HZ 0
+#define CALIB_FS_31_25HZ 1
+
+/* ewtsa_configuration parameter */
+#define BYPASS_MODE 0
+#define FIFO_MODE 1
+#define FIFO_MAX 32
+#define FIFO_MIN_DELAY 5
+#define MAP_NAME_SIZE 4
+#define INTERFACE_NAME_SIZE 3
+#define CMD_SIZE_3 3
+#define CMD_SIZE_4 4
+#define THRES_CENTER_MIN 0
+#define THRES_CENTER_MAX 359
+#define THRES_CENTER_INIT_X 160
+#define THRES_CENTER_INIT_Y 160
+#define THRES_CENTER_INIT_Z 160
+
+/* EWTSA_system_restart parameter */
+#define RST_REG_INIT_OFF 0x00
+#define RST_REG_INIT_RANGE 0x01
+#define RST_REG_INIT_DELAY 0x02
+#define RST_REG_INIT_ALL 0xFF
+
+/* event mode */
+#define EWTSA_POLLING_MODE 0
+#define EWTSA_INTERUPT_MODE 1
+
+/* ewtsa register address */
+#define REG_SLAD 0x00
+#define REG_FIFO_DRDY 0x10
+#define REG_FIFO_CTRL1 0x13
+#define REG_FIFO_CTRL2 0x14
+#define REG_DATA_RATE 0x15
+#define REG_FS_DLPF_CTRL 0x16
+#define REG_INT_CTRL 0x17
+#define REG_CTRL_REG2 0x18
+#define REG_CTRL_REG3 0x19
+#define REG_INT_STAT 0x1A
+#define REG_TEMP_H 0x1B
+#define REG_TEMP_L 0x1C
+#define REG_OUTX_H 0x1D
+#define REG_OUTX_L 0x1E
+#define REG_OUTY_H 0x1F
+#define REG_OUTY_L 0x20
+#define REG_OUTZ_H 0x21
+#define REG_OUTZ_L 0x22
+#define REG_CTRL_REG5 0x24
+#define REG_REFERENCE 0x25
+#define REG_STATUS_REG 0x27
+#define REG_DIGITAL_CAL 0x29
+#define REG_DIAG_CTRL 0x2B
+#define REG_FIFO_DM 0x2F
+#define REG_DIAG_MONI 0x39
+#define REG_SLEEP_CTRL 0x3E
+#define REG_W1THX_H 0x46
+#define REG_W1THX_L 0x47
+#define REG_W1THY_H 0x48
+#define REG_W1THY_L 0x49
+#define REG_W1THZ_H 0x4A
+#define REG_W1THZ_L 0x4B
+#define REG_SOC_CNT1 0x4C
+#define REG_SOC_CNT2 0x4D
+#define REG_SLFSLP_CNT 0x4F
+#define REG_SPITYPE 0x60
+#define REG_MBURST_ALL 0xFF
+
+/* ewtsa register map2 address */
+#define REG2_CTRL_REG1 0x20
+
+/* ewtsa register param */
+#define INT_CTRL_WTMON_ENABLE 0x0D
+#define INT_CTRL_WTMON_DISABLE 0x00
+#define DIGITAL_CAL_ENABLE 0x00
+#define DIGITAL_CAL_DISABLE 0x01
+#define SLEEP_CTRL_ACTIVATE 0x44
+#define SLEEP_CTRL_SLEEP 0x04
+#define SLEEP_CTRL_ALLRSTSLEEP 0x00
+#define SLEEP_CTRL_LOGIC_RST 0x80
+#define SOC_CNT1_BOOST_ON 0x01
+#define SOC_CNT1_WIN_DIS 0x70
+#define SOC_CNT2_SOCCLK_ON 0x40
+#define SOC_CNT2_SOCCLK_OFF 0x00
+#define INT_CTRL_INT_ENABLE 0x01
+#define INT_CTRL_INT_DISABLE 0x00
+#define INT_CTRL_INT_WTMON_EN 0x08
+#define CTRL_REG2_REF_MODE 0x10
+#define CTRL_REG2_NOR_MODE 0x60
+#define CTRL_REG2_HPCF 0x09
+#define CTRL_REG5_FIFO_EN 0x53
+#define CTRL_REG5_INIT 0x13
+#define FIFO_DRDY_DITH_EN 0x02
+#define FIFO_DRDY_INIT 0x17
+#define CTRL_REG3_WTM_DIS 0x00
+#define CTRL_REG3_WTM_EN 0x04
+#define STATUS_REG_OVERRUN 0xF0
+#define FIFO_CTRL1_CLEAR 0x02
+
+/* ewtsa register map2 param */
+#define CTRL_REG1_SLEEP 0x07
+#define CTRL_REG1_ACTIVATE 0x0F
+
+/* ewtsa interrupt control */
+#define EWSTA_INT_CLEAR 0x00
+#define EWSTA_INT_SKIP 0x01
+
+/* EWTSA_set_calibration param */
+#define EWTSA_RANGE_0 250
+#define EWTSA_RANGE_1 500
+#define EWTSA_RANGE_2 1000
+#define EWTSA_RANGE_3 2000
+#define EWTSA_BOOST_TIME_0 500
+#define EWTSA_BOOST_TIME_1 300
+#define EWTSA_BOOST_TIME_2 200
+#define EWTSA_BOOST_TIME_3 100
+#define EWTSA_W1_THRES_LV_0 2
+#define EWTSA_W1_THRES_LV_1 5
+#define EWTSA_W1_THRES_LV_2 10
+#define EWTSA_W1_THRES_LV_3 15
+
+/* init param */
+#define SLEEP_INIT_VAL SLEEP_ALLRESET
+#define FIFO_TYPE_INIT_VAL BYPASS_MODE
+#define DELAY_INIT_VAL DELAY_THRES_1
+#define RANGE_INIT_VAL DYNRANGE_1000
+#define DLPF_INIT_VAL DELAY_DLPF_2
+#define CALIB_FUNC_INIT_VAL EWTSA_ON
+#if GYRO_VER3_ES4
+#define CALIB_BTIM_INIT_VAL BOOST_TIME_100MS
+#else
+#define CALIB_BTIM_INIT_VAL BOOST_TIME_500MS
+#endif
+#define CALIB_FS_INIT_VAL CALIB_FS_62_5HZ
+#define CALIB_RST_INIT_VAL EWTSA_ON
+#define CALIB_BFS_INIT_VAL BOOST_FS_48KHZ
+#define CALIB_CLK_INIT_VAL EWTSA_ON
+#define CALIB_HPF_INIT_VAL HPF_002HZ
+#if GYRO_VER3_ES4
+#define CALIB_LAG_INIT_VAL LAG_5SMP
+#else
+#define CALIB_LAG_INIT_VAL LAG_10SMP
+#endif
+#define W1_FUNC_INIT_VAL THRESHOLD_ON
+#define W2_FUNC_INIT_VAL THRESHOLD_OFF
+#if GYRO_VER3_ES4
+#define W1_LV_INIT_VAL W1_THRES_LEVEL_5DPS
+#define W1_MASK_INIT_VAL MASK_CLIP
+#else
+#define W1_LV_INIT_VAL W1_THRES_LEVEL_15DPS
+#define W1_MASK_INIT_VAL MASK_NON_CLIP
+#endif
+
+/* declarations */
+struct ewtsa_t {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct workqueue_struct *workqueue;
+ struct delayed_work input_work;
+ struct mutex mlock;
+ spinlock_t slock;
+ unsigned long delay;
+ signed short fifo_lastdata[4];
+ unsigned short w1_thres_x;
+ unsigned short w1_thres_y;
+ unsigned short w1_thres_z;
+ unsigned char event;
+ unsigned char range;
+ unsigned char sleep;
+ unsigned char dlpf;
+ unsigned char calib;
+ unsigned char calib_fs;
+ unsigned char calib_rst;
+ unsigned char calib_boost_tm;
+ unsigned char calib_boost_fs;
+ unsigned char fifo_mode;
+ unsigned char fifo_num;
+ unsigned char fifo_data_flag;
+ unsigned char reset_param;
+ unsigned char int_skip;
+ unsigned char calib_alway;
+ unsigned char hpf;
+ unsigned char w1_datamask;
+ unsigned char w1_thres_en;
+ unsigned char w2_thres_en;
+ unsigned char offset_lag;
+ unsigned char w1_thres_level;
+ unsigned char reserved;
+};
+
+static int __init init_ewtsa(void);
+static void __exit exit_ewtsa(void);
+static ssize_t ewtsa_sleep_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ewtsa_sleep_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t ewtsa_delay_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ewtsa_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t ewtsa_range_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ewtsa_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t ewtsa_calib_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ewtsa_calib_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static ssize_t ewtsa_config_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t ewtsa_config_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
+static int EWTSA_probe(struct i2c_client *client, const struct i2c_device_id *id);
+static int EWTSA_remove(struct i2c_client *client);
+static int EWTSA_suspend(struct i2c_client *client, pm_message_t mesg);
+static int EWTSA_resume(struct i2c_client *client);
+static void EWTSA_report_abs(struct ewtsa_t *ewt);
+static void EWTSA_dev_poll(struct work_struct *work);
+static void EWTSA_input_work_func(struct work_struct *work);
+#if USE_GPIO_DRDY_PIN
+static irqreturn_t EWTSA_interrupt(int irq, void *dev_id);
+#endif
+static void EWTSA_system_restart(struct ewtsa_t *ewtsa);
+static void EWTSA_set_calibration(struct ewtsa_t *ewtsa);
+
+static struct device_attribute ewtsa_sleep_attr = {
+ .attr = {
+ .name = "ewtsa_sleep",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .show = ewtsa_sleep_show,
+ .store = ewtsa_sleep_store,
+};
+
+static struct device_attribute ewtsa_delay_attr = {
+ .attr = {
+ .name = "ewtsa_delay",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .show = ewtsa_delay_show,
+ .store = ewtsa_delay_store,
+};
+
+static struct device_attribute ewtsa_range_attr = {
+ .attr = {
+ .name = "ewtsa_range",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .show = ewtsa_range_show,
+ .store = ewtsa_range_store,
+};
+
+static struct device_attribute ewtsa_calib_attr = {
+ .attr = {
+ .name = "ewtsa_calib",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .show = ewtsa_calib_show,
+ .store = ewtsa_calib_store,
+};
+
+static struct device_attribute ewtsa_config_attr = {
+ .attr = {
+ .name = "ewtsa_configuration",
+ .mode = S_IRUGO | S_IWUSR,
+ },
+ .show = ewtsa_config_show,
+ .store = ewtsa_config_store,
+};
+
+static const struct i2c_device_id ewtsa_id[] = {
+ {"ewtsa", 0},
+ {{0}, 0}
+};
+
+MODULE_DEVICE_TABLE(i2c, ewtsa_id);
+
+static struct i2c_driver i2c_ewtsa_driver = {
+ .driver = {
+ .name = "ewtsa",
+ },
+ .probe = EWTSA_probe,
+ .remove = EWTSA_remove,
+ .suspend = EWTSA_suspend,
+ .resume = EWTSA_resume,
+ .id_table = ewtsa_id,
+};
+
+/* show the sleep mode */
+static ssize_t ewtsa_sleep_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client;
+ struct ewtsa_t *ewtsa;
+#if !GYRO_VER3_ES1
+ int ret;
+ unsigned char val;
+#endif /* GYRO_VER3_ES1 */
+
+ if (dev == NULL) {
+ printk(KERN_INFO "struct device null pointer\n");
+ return -EIO;
+ }
+ client = to_i2c_client(dev);
+
+ if (client == NULL) {
+ printk(KERN_INFO "struct i2c_client null pointer\n");
+ return -EIO;
+ }
+ ewtsa = i2c_get_clientdata(client);
+
+ /* output buffer check */
+ if (buf == NULL) {
+ dev_err(&client->dev, "buf pointer null!\n");
+ return -EIO;
+ }
+
+#if GYRO_VER3_ES1
+ return sprintf(buf, "%d\n", ewtsa->sleep);
+#else /* GYRO_VER3_ES1 */
+ ret = i2c_smbus_read_byte_data(ewtsa->client, REG_SLEEP_CTRL);
+ if (ret < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SLEEP_CTRL read err(ret = %d)\n", ret);
+ return ret;
+ }
+ val = (unsigned char)ret;
+
+ /* REG_SLEEP_CTRL register value check */
+ if ((val & SLEEP_CTRL_ACTIVATE) == SLEEP_CTRL_ACTIVATE) {
+ val = SLEEP_OFF;
+ }
+ else if ((val & SLEEP_CTRL_SLEEP) == SLEEP_CTRL_SLEEP) {
+ val = SLEEP_ON;
+ }
+ else {
+ val = SLEEP_ALLRESET;
+ }
+
+ return sprintf(buf, "%d\n", val);
+#endif /* GYRO_VER3_ES1 */
+}
+
+/* Set the sleep mode */
+static ssize_t ewtsa_sleep_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ u8 reg;
+ struct i2c_client *client;
+ struct ewtsa_t *ewtsa;
+ unsigned char val;
+ unsigned char before_sleep;
+ s32 ret;
+#if GYRO_VER3_ES1 || USE_GPIO_DRDY_PIN
+ int err;
+#endif /* GYRO_VER3_ES1 */
+ long chk_param;
+ unsigned long l_flags = 0;
+#if GYRO_VER3_ES1
+ unsigned char map2_flag = EWTSA_OFF;
+#endif /* GYRO_VER3_ES1 */
+
+ if (dev == NULL) {
+ printk(KERN_INFO "struct device null pointer\n");
+ return -EIO;
+ }
+ client = to_i2c_client(dev);
+
+ if (client == NULL) {
+ printk(KERN_INFO "struct i2c_client null pointer\n");
+ return -EIO;
+ }
+ ewtsa = i2c_get_clientdata(client);
+
+ /* input buffer check */
+ if (buf == NULL) {
+ dev_err(&client->dev, "buf pointer null!\n");
+ return -EIO;
+ }
+
+ chk_param = simple_strtol(buf, NULL, 10);
+ if ((chk_param < SLEEP_OFF) || (chk_param >SLEEP_ALLRESET)) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", chk_param);
+ return -EINVAL;
+ }
+ val = (unsigned char)chk_param;
+
+ dev_info(&ewtsa->client->dev, "set SLEEP = %d\n", val);
+
+#if GYRO_VER3_ES1
+ /* if current status is same */
+ if (ewtsa->sleep == val) {
+ return count;
+ }
+#else /* GYRO_VER3_ES1 */
+ ret = i2c_smbus_read_byte_data(ewtsa->client, REG_SLEEP_CTRL);
+ if (ret < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SLEEP_CTRL read err(ret = %d)\n", ret);
+ return ret;
+ }
+ reg = (u8)ret;
+
+ /* REG_SLEEP_CTRL register value check */
+ if ((reg & SLEEP_CTRL_ACTIVATE) == SLEEP_CTRL_ACTIVATE) {
+ reg = SLEEP_OFF;
+ }
+ else if ((reg & SLEEP_CTRL_SLEEP) == SLEEP_CTRL_SLEEP) {
+ reg = SLEEP_ON;
+ }
+ else {
+ reg = SLEEP_ALLRESET;
+ }
+
+ /* if current status is same */
+ if (reg == val) {
+ return count;
+ }
+#endif /* GYRO_VER3_ES1 */
+
+ before_sleep = ewtsa->sleep;
+ switch(val)
+ {
+ case SLEEP_OFF:
+#if GYRO_VER3_ES1
+ if (ewtsa->sleep == SLEEP_ON) {
+ reg = CTRL_REG1_ACTIVATE;
+ map2_flag = EWTSA_ON;
+ }
+ else {
+ reg = SLEEP_CTRL_ACTIVATE;
+ }
+#else /* GYRO_VER3_ES1 */
+ reg = SLEEP_CTRL_ACTIVATE;
+#endif /* GYRO_VER3_ES1 */
+ break;
+ case SLEEP_ON:
+ if (ewtsa->sleep == SLEEP_ALLRESET) {
+ return count;
+ }
+ reg = SLEEP_CTRL_SLEEP;
+ ewtsa->calib_rst = EWTSA_ON;
+ break;
+ default: /* SLEEP_ALLRESET */
+ reg = SLEEP_CTRL_ALLRSTSLEEP;
+ ewtsa->calib_rst = EWTSA_ON;
+ ewtsa->reset_param = RST_REG_INIT_ALL;
+ break;
+ }
+
+ if (ewtsa->event != EWTSA_POLLING_MODE) {
+ /* disable interrupt */
+ ret = i2c_smbus_write_byte_data(ewtsa->client, REG_INT_CTRL, INT_CTRL_INT_DISABLE);
+ if (ret < 0)
+ {
+ dev_err(&ewtsa->client->dev, "REG_INT_CTRL write err(data = 0x%02x, ret = %d)\n", INT_CTRL_INT_DISABLE, ret);
+ return ret;
+ }
+ }
+
+ dev_info(&ewtsa->client->dev, "set reg = 0x%02x\n", reg);
+#if GYRO_VER3_ES1
+ if (map2_flag == EWTSA_ON) {
+ ret = i2c_smbus_write_byte_data(ewtsa->client, REG2_CTRL_REG1, reg);
+ }
+ else {
+ ret = i2c_smbus_write_byte_data(ewtsa->client, REG_SLEEP_CTRL, reg);
+ }
+#else /* GYRO_VER3_ES1 */
+ ret = i2c_smbus_write_byte_data(ewtsa->client, REG_SLEEP_CTRL, reg);
+#endif /* GYRO_VER3_ES1 */
+ if (ret < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SLEEP_CTRL write err(data = 0x%02x, ret = %d)\n", reg, ret);
+ return ret;
+ }
+ ewtsa->sleep = val;
+
+ if (val != SLEEP_OFF) {
+ if (ewtsa->event == EWTSA_POLLING_MODE) {
+ cancel_delayed_work(&ewtsa->input_work);
+ }
+ }
+ else {
+ ret = i2c_smbus_write_byte_data(ewtsa->client, REG_DIAG_CTRL, DIAG_ENABLE);
+ if (ret < 0) {
+ dev_err(&ewtsa->client->dev, "REG_DIAG_CTRL write err(data = 0x%02x, ret = %d)\n", DIAG_ENABLE, ret);
+ return ret;
+ }
+ if (ewtsa->event == EWTSA_POLLING_MODE) {
+ ret = i2c_smbus_write_byte_data(ewtsa->client, REG_INT_CTRL, INT_CTRL_WTMON_DISABLE); /* No INT */
+ if (ret < 0) {
+ dev_err(&ewtsa->client->dev, "REG_INT_CTRL write err(data = 0x%02x, ret = %d)\n", INT_CTRL_WTMON_DISABLE, ret);
+ return ret;
+ }
+ }
+
+ if (ewtsa->calib_rst == EWTSA_ON) {
+ EWTSA_system_restart(ewtsa);
+ }
+ if (ewtsa->event == EWTSA_POLLING_MODE) {
+ /* polling timer start */
+ queue_delayed_work(ewtsa->workqueue,
+ &ewtsa->input_work,
+ msecs_to_jiffies(ewtsa->delay));
+ }
+ }
+
+ if (ewtsa->event == EWTSA_POLLING_MODE) {
+ mutex_lock(&ewtsa->mlock);
+ input_report_abs(ewtsa->input_dev, ABS_RY, -1);
+ input_sync(ewtsa->input_dev);
+ mutex_unlock(&ewtsa->mlock);
+ }
+ else {
+ spin_lock_irqsave(&ewtsa->slock, l_flags);
+ input_report_abs(ewtsa->input_dev, ABS_RY, -1);
+ input_sync(ewtsa->input_dev);
+ spin_unlock_irqrestore(&ewtsa->slock, l_flags);
+
+ if (val == SLEEP_OFF) {
+ /* enable interrupt */
+#if USE_GPIO_DRDY_PIN
+ err = gpio_request(DRDY_PIN, "DRDY");
+ err += gpio_direction_input(DRDY_PIN);
+ err += request_threaded_irq(gpio_to_irq(DRDY_PIN), NULL, EWTSA_interrupt, IRQF_TRIGGER_RISING, DEVICE_NAME, ewtsa);
+ if (err != 0) {
+ dev_err(&ewtsa->client->dev, "request_irq err(%d)\n", err);
+ return err;
+ }
+#endif
+
+ ewtsa->fifo_data_flag = EWTSA_OFF;
+ memset(ewtsa->fifo_lastdata, 0x00, sizeof(ewtsa->fifo_lastdata));
+ ewtsa->int_skip = EWSTA_INT_SKIP;
+ if (ewtsa->fifo_mode == BYPASS_MODE) {
+ reg = (u8)INT_CTRL_INT_ENABLE;
+ }
+ else {
+ reg = (u8)(INT_CTRL_INT_ENABLE | INT_CTRL_INT_WTMON_EN);
+ }
+ ret = i2c_smbus_write_byte_data(ewtsa->client, REG_INT_CTRL, reg);
+ if (ret < 0) {
+ dev_err(&ewtsa->client->dev, "REG_INT_CTRL write err(data = 0x%02x, ret = %d)\n", reg, ret);
+ return ret;
+ }
+ }
+ else
+ {
+ if (before_sleep == SLEEP_OFF) {
+#if USE_GPIO_DRDY_PIN
+ free_irq(gpio_to_irq(DRDY_PIN), ewtsa);
+ gpio_free(DRDY_PIN);
+#endif
+ }
+ }
+ }
+
+ return count;
+}
+
+/* Show the LPF settings */
+static ssize_t ewtsa_delay_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client;
+ struct ewtsa_t *ewtsa;
+
+ if (dev == NULL) {
+ printk(KERN_INFO "struct device null pointer\n");
+ return -EIO;
+ }
+ client = to_i2c_client(dev);
+
+ if (client == NULL) {
+ printk(KERN_INFO "struct i2c_client null pointer\n");
+ return -EIO;
+ }
+ ewtsa = i2c_get_clientdata(client);
+
+ /* output buffer check */
+ if (buf == NULL) {
+ dev_err(&client->dev, "buf pointer null!\n");
+ return -EIO;
+ }
+
+ return sprintf(buf, "%ld\n", ewtsa->delay);
+}
+
+/* Set the LPF settings*/
+static ssize_t ewtsa_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client;
+ struct ewtsa_t *ewtsa;
+ unsigned long delay;
+ unsigned char val;
+ unsigned char number;
+ u8 smpl;
+ int err;
+ unsigned char reg;
+ unsigned char fifo_type;
+
+ if (dev == NULL) {
+ printk(KERN_INFO "struct device null pointer\n");
+ return -EIO;
+ }
+ client = to_i2c_client(dev);
+
+ if (client == NULL) {
+ printk(KERN_INFO "struct i2c_client null pointer\n");
+ return -EIO;
+ }
+ ewtsa = i2c_get_clientdata(client);
+
+ /* input buffer check */
+ if (buf == NULL) {
+ dev_err(&client->dev, "buf pointer null!\n");
+ return -EIO;
+ }
+ err = strict_strtoul(buf, 0, &delay);
+ if (err != 0) {
+ dev_err(&ewtsa->client->dev, "strict_strtoul err(ret = %d)\n", err);
+ return err;
+ }
+
+ if (delay == ewtsa->delay) {
+ return count;
+ }
+
+ if ((delay < DELAY_THRES_MIN) || (delay > DELAY_THRES_MAX)) {
+ dev_err(&ewtsa->client->dev, "param err(ret = %ld)\n", delay);
+ return -EINVAL;
+ }
+
+ dev_info(&ewtsa->client->dev, "Delay = %ld\n", delay);
+
+ if (ewtsa->sleep == SLEEP_OFF) {
+ if (ewtsa->event == EWTSA_POLLING_MODE) {
+ cancel_delayed_work(&ewtsa->input_work);
+ }
+ else {
+ /* disable interrupt */
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_INT_CTRL, INT_CTRL_INT_DISABLE);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_INT_CTRL write err(data = 0x%02x, ret = %d)\n", INT_CTRL_INT_DISABLE, err);
+ return err;
+ }
+ }
+
+ if (ewtsa->event == EWTSA_POLLING_MODE) {
+ fifo_type = BYPASS_MODE;
+ }
+ else {
+ fifo_type = ewtsa->fifo_mode;
+ }
+
+ if (fifo_type == BYPASS_MODE) {
+ if (delay < DELAY_THRES_4) {
+ if (delay >= DELAY_THRES_3) {
+ val = DELAY_DLPF_3;
+ } else {
+ val = DELAY_DLPF_2;
+ }
+ } else {
+ if (delay >= DELAY_THRES_6) {
+ val = DELAY_DLPF_6;
+ } else if (delay >= DELAY_THRES_5) {
+ val = DELAY_DLPF_5;
+ } else {
+ val = DELAY_DLPF_4;
+ }
+ }
+ }
+ else {
+ if (delay <= DELAY_THRES_FIFO) {
+ val = DELAY_DLPF_1;
+ }
+ else {
+ val = DELAY_DLPF_2;
+ }
+ }
+
+ reg = (unsigned char)((unsigned char)(ewtsa->range << 3) | val );
+ dev_info(&ewtsa->client->dev, "FS_DLPF = %2X\n", (unsigned int)reg);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_FS_DLPF_CTRL, reg);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_FS_DLPF_CTRL write err(data = 0x%02x, ret = %d)\n", reg, err);
+ return err;
+ }
+ ewtsa->dlpf = val;
+
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_CTRL_REG5, CTRL_REG5_INIT);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_CTRL_REG5 write err(data = 0x%02x, ret = %d)\n", CTRL_REG5_INIT, err);
+ return err;
+ }
+
+ if (fifo_type != BYPASS_MODE) {
+ reg = FIFO_DRDY_DITH_EN;
+ }
+ else {
+ dev_info(&ewtsa->client->dev, "FIFO_CTR = %2X\n", fifo_type);
+ reg = (unsigned char)(fifo_type << 5);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_FIFO_CTRL2, reg);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_FIFO_CTRL2 write err(data = 0x%02x, ret = %d)\n", reg, err);
+ return err;
+ }
+ reg = FIFO_DRDY_INIT;
+ }
+
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_FIFO_DRDY, reg);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_FIFO_DRDY write err(data = 0x%02x, ret = %d)\n", reg, err);
+ return err;
+ }
+
+ if (ewtsa->event == EWTSA_POLLING_MODE) {
+ smpl = DATA_RATE_1 - 1; /* SMPL = 0 */
+ dev_info(&ewtsa->client->dev, "SMPL+1 = %2X\n", smpl);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_DATA_RATE, smpl);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_DATA_RATE write err(data = 0x%02x, ret = %d)\n", smpl, err);
+ return err;
+ }
+ ewtsa->delay = delay;
+ ewtsa->reset_param &= (unsigned char)~RST_REG_INIT_DELAY;
+ EWTSA_system_restart(ewtsa);
+
+ queue_delayed_work(ewtsa->workqueue,
+ &ewtsa->input_work,
+ msecs_to_jiffies(ewtsa->delay));
+ }
+ else {
+ if (fifo_type == BYPASS_MODE) {
+ if (delay <= DELAY_THRES_2) {
+ smpl = DELAY_INTMIN_THRES;
+ }
+ else {
+ smpl = (unsigned char)(delay - 1);
+ }
+ ewtsa->fifo_num = 1;
+ }
+ else {
+ if (delay <= DELAY_THRES_FIFO) {
+ if (delay > FIFO_MIN_DELAY) {
+ number = (unsigned char)(delay / DATA_RATE_5);
+ if ((delay % DATA_RATE_5) != 0) {
+ number++;
+ }
+ }
+ else {
+ number = 2;
+ }
+ smpl = DATA_RATE_5 - 1; /* SMPL = 4 */
+ }
+ else {
+ number = (unsigned char)(delay / DATA_RATE_10);
+ if ((delay % DATA_RATE_10) != 0) {
+ number++;
+ }
+ smpl = DATA_RATE_10 - 1; /* SMPL = 9 */
+ }
+
+ reg = (unsigned char)(fifo_type << 5);
+ reg |= number;
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_FIFO_CTRL2, reg);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_FIFO_CTRL2 write err(data = 0x%02x, ret = %d)\n", reg, err);
+ return err;
+ }
+ ewtsa->fifo_num = number;
+
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_CTRL_REG3, CTRL_REG3_WTM_EN);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_CTRL_REG3 write err(data = 0x%02x, ret = %d)\n", CTRL_REG3_WTM_EN, err);
+ return err;
+ }
+
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_CTRL_REG5, CTRL_REG5_FIFO_EN);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_CTRL_REG5 write err(data = 0x%02x, ret = %d)\n", CTRL_REG5_FIFO_EN, err);
+ return err;
+ }
+ }
+ dev_info(&ewtsa->client->dev, "SMPL+1 = %2X\n", smpl);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_DATA_RATE, smpl);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_DATA_RATE write err(data = 0x%02x, ret = %d)\n", smpl, err);
+ return err;
+ }
+ ewtsa->delay = delay;
+ ewtsa->reset_param &= (unsigned char)~RST_REG_INIT_DELAY;
+ EWTSA_system_restart(ewtsa);
+
+ /* enable interrupt */
+ ewtsa->fifo_data_flag = EWTSA_OFF;
+ memset(ewtsa->fifo_lastdata, 0x00, sizeof(ewtsa->fifo_lastdata));
+ ewtsa->int_skip = EWSTA_INT_SKIP;
+ if (fifo_type == BYPASS_MODE) {
+ reg = (u8)INT_CTRL_INT_ENABLE;
+ }
+ else {
+ reg = (u8)(INT_CTRL_INT_ENABLE | INT_CTRL_INT_WTMON_EN);
+ }
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_INT_CTRL, reg);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_INT_CTRL write err(data = 0x%02x, ret = %d)\n", reg, err);
+ return err;
+ }
+ }
+ }
+ else {
+ ewtsa->delay = delay;
+ ewtsa->calib_rst = EWTSA_ON;
+ ewtsa->reset_param |= RST_REG_INIT_DELAY;
+ }
+
+ return count;
+}
+
+/* Show the FS settings */
+static ssize_t ewtsa_range_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client;
+ struct ewtsa_t *ewtsa ;
+
+ if (dev == NULL) {
+ printk(KERN_INFO "struct device null pointer\n");
+ return -EIO;
+ }
+ client = to_i2c_client(dev);
+
+ if (client == NULL) {
+ printk(KERN_INFO "struct i2c_client null pointer\n");
+ return -EIO;
+ }
+ ewtsa = i2c_get_clientdata(client);
+
+ /* output buffer check */
+ if (buf == NULL) {
+ dev_err(&client->dev, "buf pointer null!\n");
+ return -EIO;
+ }
+
+ return sprintf(buf, "%d\n", ewtsa->range);
+}
+
+/* Set the FS settings*/
+static ssize_t ewtsa_range_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client;
+ struct ewtsa_t *ewtsa;
+ unsigned char val;
+ s32 err;
+ unsigned char rng;
+ u8 reg;
+ long chk_param;
+
+ if (dev == NULL) {
+ printk(KERN_INFO "struct device null pointer\n");
+ return -EIO;
+ }
+ client = to_i2c_client(dev);
+
+ if (client == NULL) {
+ printk(KERN_INFO "struct i2c_client null pointer\n");
+ return -EIO;
+ }
+ ewtsa = i2c_get_clientdata(client);
+
+ /* input buffer check */
+ if (buf == NULL) {
+ dev_err(&client->dev, "buf pointer null!\n");
+ return -EIO;
+ }
+ chk_param = simple_strtol(buf, NULL, 10);
+ if ((chk_param < DYNRANGE_250) || (chk_param > DYNRANGE_2000)) {
+ dev_info(&ewtsa->client->dev, "invalid value = %ld\n", chk_param);
+ return -EINVAL;
+ }
+ val = (unsigned char)chk_param;
+
+ if (val == ewtsa->range) {
+ return count;
+ }
+
+ dev_info(&ewtsa->client->dev, "FS = %d\n", (int)val);
+ if (ewtsa->sleep == SLEEP_OFF) {
+ if (ewtsa->event != EWTSA_POLLING_MODE) {
+ /* disable interrupt */
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_INT_CTRL, INT_CTRL_INT_DISABLE);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_INT_CTRL write err(data = 0x%02x, ret = %d)\n", INT_CTRL_INT_DISABLE, err);
+ return err;
+ }
+ }
+ rng = (unsigned char)((unsigned char)(val << 3) | ewtsa->dlpf);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_FS_DLPF_CTRL, rng);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_FS_DLPF_CTRL write err(data = 0x%02x, ret = %d)\n", rng, err);
+ return err;
+ }
+ ewtsa->range = val;
+ ewtsa->reset_param &= (unsigned char)~RST_REG_INIT_RANGE;
+ if (ewtsa->event == EWTSA_POLLING_MODE) {
+ cancel_delayed_work(&ewtsa->input_work);
+ EWTSA_system_restart(ewtsa);
+ queue_delayed_work(ewtsa->workqueue,
+ &ewtsa->input_work,
+ msecs_to_jiffies(ewtsa->delay));
+ }
+ else {
+ EWTSA_system_restart(ewtsa);
+
+ /* enable interrupt */
+ ewtsa->fifo_data_flag = EWTSA_OFF;
+ memset(ewtsa->fifo_lastdata, 0x00, sizeof(ewtsa->fifo_lastdata));
+ ewtsa->int_skip = EWSTA_INT_SKIP;
+ if (ewtsa->fifo_mode == BYPASS_MODE) {
+ reg = (u8)INT_CTRL_INT_ENABLE;
+ }
+ else {
+ reg = (u8)(INT_CTRL_INT_ENABLE | INT_CTRL_INT_WTMON_EN);
+ }
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_INT_CTRL, reg);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_INT_CTRL write err(data = 0x%02x, ret = %d)\n", reg, err);
+ return err;
+ }
+ }
+ }
+ else {
+ ewtsa->range = val;
+ ewtsa->calib_rst = EWTSA_ON;
+ ewtsa->reset_param |= RST_REG_INIT_RANGE;
+ }
+
+ return count;
+}
+
+/* Show the SELF O C settings */
+static ssize_t ewtsa_calib_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client;
+ struct ewtsa_t *ewtsa;
+
+ if (dev == NULL) {
+ printk(KERN_INFO "struct device null pointer\n");
+ return -EIO;
+ }
+ client = to_i2c_client(dev);
+
+ if (client == NULL) {
+ printk(KERN_INFO "struct i2c_client null pointer\n");
+ return -EIO;
+ }
+ ewtsa = i2c_get_clientdata(client);
+
+ /* output buffer check */
+ if (buf == NULL) {
+ dev_err(&client->dev, "buf pointer null!\n");
+ return -EIO;
+ }
+
+ return sprintf(buf, "%d\n", ewtsa->calib);
+}
+
+/* Set the SELF O C settings*/
+static ssize_t ewtsa_calib_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client;
+ struct ewtsa_t *ewtsa;
+ unsigned char val;
+ s32 err;
+ u8 reg;
+ long chk_param;
+
+ if (dev == NULL) {
+ printk(KERN_INFO "struct device null pointer\n");
+ return -EIO;
+ }
+ client = to_i2c_client(dev);
+
+ if (client == NULL) {
+ printk(KERN_INFO "struct i2c_client null pointer\n");
+ return -EIO;
+ }
+ ewtsa = i2c_get_clientdata(client);
+
+ /* input buffer check */
+ if (buf == NULL) {
+ dev_err(&client->dev, "buf pointer null!\n");
+ return -EIO;
+ }
+ chk_param = simple_strtol(buf, NULL, 10);
+ if ((chk_param != EWTSA_ON) && (chk_param != EWTSA_OFF)) {
+ dev_info(&ewtsa->client->dev, "invalid value = %ld\n", chk_param);
+ return -EINVAL;
+ }
+ val = (unsigned char)chk_param;
+
+ dev_info(&ewtsa->client->dev, "SELF_O_C = %d(now = %d)\n", (int)val, ewtsa->calib);
+
+ if (ewtsa->calib == val) {
+ return count;
+ }
+
+ ewtsa->calib = val;
+ if (ewtsa->sleep == SLEEP_OFF) {
+ if (ewtsa->event == EWTSA_POLLING_MODE) {
+ cancel_delayed_work(&ewtsa->input_work);
+ EWTSA_system_restart(ewtsa);
+ queue_delayed_work(ewtsa->workqueue,
+ &ewtsa->input_work,
+ msecs_to_jiffies(ewtsa->delay));
+ }
+ else {
+ /* disable interrupt */
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_INT_CTRL, INT_CTRL_INT_DISABLE);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_INT_CTRL write err(data = 0x%02x, ret = %d)\n", INT_CTRL_INT_DISABLE, err);
+ return err;
+ }
+
+ EWTSA_system_restart(ewtsa);
+
+ /* enable interrupt */
+ ewtsa->fifo_data_flag = EWTSA_OFF;
+ memset(ewtsa->fifo_lastdata, 0x00, sizeof(ewtsa->fifo_lastdata));
+ ewtsa->int_skip = EWSTA_INT_SKIP;
+ if (ewtsa->fifo_mode == BYPASS_MODE) {
+ reg = (u8)INT_CTRL_INT_ENABLE;
+ }
+ else {
+ reg = (u8)(INT_CTRL_INT_ENABLE | INT_CTRL_INT_WTMON_EN);
+ }
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_INT_CTRL, reg);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_INT_CTRL write err(data = 0x%02x, ret = %d)\n", reg, err);
+ return err;
+ }
+ }
+ }
+ else {
+ ewtsa->calib_rst = EWTSA_ON;
+ }
+
+ return count;
+}
+
+/* Show the gyro configurations */
+static ssize_t ewtsa_config_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *l_client;
+ struct ewtsa_t *ewtsa;
+ int ret;
+ ssize_t ret_len = 0;
+
+ if (dev == NULL) {
+ printk(KERN_INFO "struct device null pointer\n");
+ return -EIO;
+ }
+ l_client = to_i2c_client(dev);
+
+ if (l_client == NULL) {
+ printk(KERN_INFO "struct i2c_client null pointer\n");
+ return -EIO;
+ }
+ ewtsa = i2c_get_clientdata(l_client);
+
+ /* output buffer check */
+ if (buf == NULL) {
+ dev_err(&ewtsa->client->dev, "buf pointer null!\n");
+ return -EIO;
+ }
+
+#if GYRO_REGISTER_MAP2
+ ret = sprintf(buf+ret_len, "MAP2;");
+#else /* GYRO_REGISTER_MAP2 */
+ ret = sprintf(buf+ret_len, "MAP1;");
+#endif /* GYRO_REGISTER_MAP2 */
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+#if GYRO_SPI_INTERFACE
+ ret = sprintf(buf+ret_len, "SPI;");
+#else /* GYRO_SPI_INTERFACE */
+ ret = sprintf(buf+ret_len, "I2C;");
+#endif /* GYRO_SPI_INTERFACE */
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "SLP=%d;", ewtsa->sleep);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "DLY=%ld;", ewtsa->delay);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "RNG=%d;", ewtsa->range);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "SOC=%d;", ewtsa->calib);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "SBT=%d;", ewtsa->calib_boost_tm);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "HPF=%d;", ewtsa->hpf);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "SFR=%d;", ewtsa->calib_fs);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "EVE=%d;", ewtsa->event);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "FIFO=%d;", ewtsa->fifo_mode);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "SAA=%d;", ewtsa->calib_alway);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "W1CX=%d;", ewtsa->w1_thres_x);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "W1CY=%d;", ewtsa->w1_thres_y);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "W1CZ=%d;", ewtsa->w1_thres_z);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "BFR=%d;", ewtsa->calib_boost_fs);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "OTL=%d;", ewtsa->offset_lag);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "W1L=%d;", ewtsa->w1_thres_level);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ ret = sprintf(buf+ret_len, "W1DM=%d;", ewtsa->w1_datamask);
+ if (ret >= 0) {
+ ret_len += ret;
+ }
+
+ return ret_len;
+}
+
+/* Set the gyro configurations */
+static ssize_t ewtsa_config_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *l_client;
+ struct ewtsa_t *ewtsa;
+ char val_char[4] = {0};
+ char sleep_val[4] = {0};
+ unsigned char cmd[5] = {0};
+ unsigned char cmd_size = 0;
+ unsigned char val_size = 0;
+ unsigned char sleep_flag = EWTSA_OFF;
+ unsigned char sleep_mode = SLEEP_ALLRESET;
+ unsigned char fifo_tmp;
+ unsigned char event_tmp;
+ int loop;
+ int size_err;
+ unsigned long val = 0;
+ size_t chk_cnt = 0;
+ ssize_t sleep_ret;
+
+ if (dev == NULL) {
+ printk(KERN_INFO "struct device null pointer\n");
+ return -EIO;
+ }
+ l_client = to_i2c_client(dev);
+
+ if (l_client == NULL) {
+ printk(KERN_INFO "struct i2c_client null pointer\n");
+ return -EIO;
+ }
+ ewtsa = i2c_get_clientdata(l_client);
+
+ /* input buffer check */
+ if (buf == NULL) {
+ dev_err(&ewtsa->client->dev, "buf pointer null!\n");
+ return -EIO;
+ }
+
+ fifo_tmp = ewtsa->fifo_mode;
+ event_tmp = ewtsa->event;
+
+ if (strncmp(buf, "MAP", 3) == 0) {
+ /* get register map information */
+#if GYRO_REGISTER_MAP2
+ if (strncmp(buf, "MAP2", MAP_NAME_SIZE) != 0) {
+ dev_err(&ewtsa->client->dev, "register map unmatch!\n");
+ return -EINVAL;
+ }
+#else /* GYRO_REGISTER_MAP2 */
+ if (strncmp(buf, "MAP1", MAP_NAME_SIZE) != 0) {
+ dev_err(&ewtsa->client->dev, "register map unmatch!\n");
+ return -EINVAL;
+ }
+#endif /* GYRO_REGISTER_MAP2 */
+ buf += MAP_NAME_SIZE + 1;
+ chk_cnt += MAP_NAME_SIZE + 1;
+
+ /* get communication interface */
+#if GYRO_SPI_INTERFACE
+ if (strncmp(buf, "SPI", INTERFACE_NAME_SIZE) != 0) {
+ dev_err(&ewtsa->client->dev, "communication interface unmatch!\n");
+ return -EINVAL;
+ }
+#else /* GYRO_SPI_INTERFACE */
+ if (strncmp(buf, "I2C", INTERFACE_NAME_SIZE) != 0) {
+ dev_err(&ewtsa->client->dev, "communication interface unmatch!\n");
+ return -EINVAL;
+ }
+#endif /* GYRO_SPI_INTERFACE */
+ buf += INTERFACE_NAME_SIZE + 1;
+ chk_cnt += INTERFACE_NAME_SIZE + 1;
+ }
+
+ while (chk_cnt < count) {
+ memset(cmd, 0x00, sizeof(cmd));
+ memset(val_char, 0x00, sizeof(val_char));
+ size_err = 0;
+
+ for (loop = 0; (chk_cnt + loop) < count; loop++) {
+ if (*(buf + loop) == '=') {
+ break;
+ }
+
+ if (*(buf + loop) == ';') {
+ dev_err(&ewtsa->client->dev, "command format err\n");
+ return -EINVAL;
+ }
+ }
+
+ if ((chk_cnt + loop) >= count) {
+ break;
+ }
+
+ cmd_size = (unsigned char)loop;
+ if (cmd_size == 0) {
+ dev_err(&ewtsa->client->dev, "command nothing\n");
+ return -EINVAL;
+ }
+
+ /* command size over? */
+ if (loop > (sizeof(cmd) - 1)) {
+ size_err = EWTSA_ON;
+ }
+ else {
+ memcpy(cmd, buf, cmd_size);
+ }
+ buf += cmd_size + 1;
+ chk_cnt += cmd_size + 1;
+
+ for (loop = 0; (chk_cnt + loop) < count; loop++) {
+ if (*(buf + loop) == ';') {
+ break;
+ }
+ }
+
+ if ((chk_cnt + loop) >= count) {
+ break;
+ }
+
+ val_size = (unsigned char)loop;
+ if (val_size == 0) {
+ dev_err(&ewtsa->client->dev, "value nothing(cmd = %s)\n", cmd);
+ return -EINVAL;
+ }
+
+ if (size_err != 0) {
+ /* if command size is over, value ignore and next command check. */
+ buf += val_size + 1;
+ chk_cnt += val_size + 1;
+ continue;
+ }
+ else if (loop > (sizeof(val_char) - 1)) {
+ /* if value size is over, return format err. */
+ size_err = EWTSA_ON;
+ }
+ else {
+ memcpy(val_char, buf, val_size);
+ size_err = strict_strtoul(val_char, 10, &val);
+ }
+
+ if (size_err != 0) {
+ dev_err(&ewtsa->client->dev, "value format err(cmd = %s)\n", cmd);
+ return -EINVAL;
+ }
+ buf += val_size + 1;
+ chk_cnt += val_size + 1;
+
+ if (cmd_size == CMD_SIZE_3) {
+ if (strncmp((char*)cmd, "SLP", CMD_SIZE_3) == 0) {
+ sleep_flag = EWTSA_ON;
+ memcpy(sleep_val, val_char, sizeof(sleep_val));
+ if (val == 0) {
+ sleep_mode = SLEEP_OFF;
+ }
+ }
+ else if (strncmp((char*)cmd, "DLY", CMD_SIZE_3) == 0) {
+ if ((val < DELAY_THRES_MIN) || (val > DELAY_THRES_MAX)) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+
+ ewtsa->delay = val;
+ ewtsa->calib_rst = EWTSA_ON;
+ ewtsa->reset_param |= RST_REG_INIT_DELAY;
+ }
+ else if (strncmp((char*)cmd, "RNG", CMD_SIZE_3) == 0) {
+ if (val > DYNRANGE_2000) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+ ewtsa->range = (unsigned char)val;
+ ewtsa->calib_rst = EWTSA_ON;
+ ewtsa->reset_param |= RST_REG_INIT_RANGE;
+ }
+ else if (strncmp((char*)cmd, "SOC", CMD_SIZE_3) == 0) {
+ if ((val != EWTSA_ON) && (val != EWTSA_OFF)) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+ ewtsa->calib = (unsigned char)val;
+ ewtsa->calib_rst = EWTSA_ON;
+ }
+ else if (strncmp((char*)cmd, "SBT", CMD_SIZE_3) == 0) {
+ if (val > BOOST_TIME_100MS) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+ ewtsa->calib_boost_tm = (unsigned char)val;
+ }
+ else if (strncmp((char*)cmd, "HPF", CMD_SIZE_3) == 0) {
+ if ((val != HPF_001HZ) && (val != HPF_002HZ)) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+ ewtsa->hpf = (unsigned char)val;
+ ewtsa->calib_rst = EWTSA_ON;
+ }
+ else if (strncmp((char*)cmd, "SFR", CMD_SIZE_3) == 0) {
+ if ((val != CALIB_FS_62_5HZ) && (val != CALIB_FS_31_25HZ)) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+ ewtsa->calib_fs = (unsigned char)val;
+ ewtsa->calib_rst = EWTSA_ON;
+ }
+ else if (strncmp((char*)cmd, "EVE", CMD_SIZE_3) == 0) {
+ if ((val != EWTSA_POLLING_MODE) && (val != EWTSA_INTERUPT_MODE)) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+#if USE_GPIO_DRDY_PIN
+ event_tmp = (unsigned char)val;
+#else
+ event_tmp = (unsigned char)EWTSA_POLLING_MODE;
+#endif
+ ewtsa->calib_rst = EWTSA_ON;
+ ewtsa->reset_param = RST_REG_INIT_ALL;
+ }
+ else if (strncmp((char*)cmd, "SAA", CMD_SIZE_3) == 0) {
+ if ((val != EWTSA_ON) && (val != EWTSA_OFF)) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+ ewtsa->calib_alway = (unsigned char)val;
+ ewtsa->calib_rst = EWTSA_ON;
+ }
+ else if (strncmp((char*)cmd, "BFR", CMD_SIZE_3) == 0) {
+ if ((val != BOOST_FS_48KHZ) && (val != BOOST_FS_16KHZ)) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+ ewtsa->calib_boost_fs = (unsigned char)val;
+ }
+ else if (strncmp((char*)cmd, "OTL", CMD_SIZE_3) == 0) {
+ if (val > LAG_20SMP) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+ ewtsa->offset_lag = (unsigned char)val;
+ ewtsa->calib_rst = EWTSA_ON;
+ }
+ else if (strncmp((char*)cmd, "W1L", CMD_SIZE_3) == 0) {
+ if (val > W1_THRES_LEVEL_15DPS) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+ ewtsa->w1_thres_level = (unsigned char)val;
+ ewtsa->calib_rst = EWTSA_ON;
+ }
+ else {
+ dev_info(&ewtsa->client->dev, "unknown command(%s)\n", cmd);
+ }
+ }
+ else if (cmd_size == CMD_SIZE_4) {
+ if (strncmp((char*)cmd, "FIFO", CMD_SIZE_4) == 0) {
+ if ((val != BYPASS_MODE) && (val != FIFO_MODE)) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+ fifo_tmp = (unsigned char)val;
+ ewtsa->calib_rst = EWTSA_ON;
+ ewtsa->reset_param = RST_REG_INIT_ALL;
+ }
+ else if (strncmp((char*)cmd, "W1CX", CMD_SIZE_4) == 0) {
+ if (val > THRES_CENTER_MAX) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+ ewtsa->w1_thres_x = (unsigned short)val;
+ ewtsa->calib_rst = EWTSA_ON;
+ }
+ else if (strncmp((char*)cmd, "W1CY", CMD_SIZE_4) == 0) {
+ if (val > THRES_CENTER_MAX) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+ ewtsa->w1_thres_y = (unsigned short)val;
+ ewtsa->calib_rst = EWTSA_ON;
+ }
+ else if (strncmp((char*)cmd, "W1CZ", CMD_SIZE_4) == 0) {
+ if (val > THRES_CENTER_MAX) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+ ewtsa->w1_thres_z = (unsigned short)val;
+ ewtsa->calib_rst = EWTSA_ON;
+ }
+ else if (strncmp((char*)cmd, "W1DM", CMD_SIZE_4) == 0) {
+ if ((val != MASK_CLIP) && (val != MASK_NON_CLIP)) {
+ dev_err(&ewtsa->client->dev, "invalid value = %ld\n", val);
+ return -EINVAL;
+ }
+ ewtsa->w1_datamask = (unsigned char)val;
+ ewtsa->calib_rst = EWTSA_ON;
+ }
+ else {
+ dev_info(&ewtsa->client->dev, "unknown command(%s)\n", cmd);
+ }
+ }
+ else {
+ dev_info(&ewtsa->client->dev, "unknown command(%s)\n", cmd);
+ }
+ }
+
+ if (sleep_flag == EWTSA_ON) {
+ if (sleep_mode == SLEEP_OFF) {
+ /* all reset sleep mode */
+ val_char[0] = '2';
+ val_char[1] = '\0';
+ sleep_ret = ewtsa_sleep_store(dev, attr, val_char, 1);
+ if (sleep_ret != 1) {
+ dev_err(&ewtsa->client->dev, "ewtsa_sleep_store err(%d)\n", sleep_ret);
+ return sleep_ret;
+ }
+ ewtsa->event = event_tmp;
+ ewtsa->fifo_mode = fifo_tmp;
+ }
+ sleep_ret = ewtsa_sleep_store(dev, attr, sleep_val, sizeof(sleep_val));
+ if (sleep_ret != sizeof(sleep_val)) {
+ dev_err(&ewtsa->client->dev, "ewtsa_sleep_store err(%d)\n", sleep_ret);
+ return sleep_ret;
+ }
+ ewtsa->event = event_tmp;
+ ewtsa->fifo_mode = fifo_tmp;
+ }
+ else {
+ if (ewtsa->calib_rst == EWTSA_ON) {
+ sleep_mode = ewtsa->sleep;
+
+ /* all reset sleep mode */
+ val_char[0] = '2';
+ val_char[1] = '\0';
+ sleep_ret = ewtsa_sleep_store(dev, attr, val_char, 1);
+ if (sleep_ret != 1) {
+ dev_err(&ewtsa->client->dev, "ewtsa_sleep_store err(%d)\n", sleep_ret);
+ return sleep_ret;
+ }
+
+ ewtsa->event = event_tmp;
+ ewtsa->fifo_mode = fifo_tmp;
+
+ if (sleep_mode == SLEEP_OFF) {
+ /* sleep off mode */
+ val_char[0] = '0';
+ val_char[1] = '\0';
+ sleep_ret = ewtsa_sleep_store(dev, attr, val_char, 1);
+ if (sleep_ret != 1) {
+ dev_err(&ewtsa->client->dev, "ewtsa_sleep_store err(%d)\n", sleep_ret);
+ return sleep_ret;
+ }
+ }
+ }
+ }
+
+ return count;
+}
+
+/* calibration & register settings */
+static void EWTSA_system_restart(struct ewtsa_t *ewtsa)
+{
+ unsigned char val;
+ unsigned char smpl;
+ unsigned char number;
+ unsigned char fifo_type;
+ s32 err;
+#if GYRO_VER3_ES1
+ signed long ret;
+ signed long timeout;
+#endif
+
+ if (ewtsa == NULL) {
+ printk(KERN_INFO "EWTSA_system_restart:ewtsa null pointer\n");
+ return;
+ }
+
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_DIGITAL_CAL, DIGITAL_CAL_DISABLE);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_DIGITAL_CAL write err(data = 0x%02x, ret = %d)\n", DIGITAL_CAL_DISABLE, err);
+ return;
+ }
+
+#if GYRO_VER3_ES1
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_REFERENCE, 0x00);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_REFERENCE write err(data = 0x00, ret = %d)\n", err);
+ return;
+ }
+
+ val = (unsigned char)(CTRL_REG2_REF_MODE | CTRL_REG2_HPCF);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_CTRL_REG2, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_CTRL_REG2 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ err = i2c_smbus_read_byte_data(ewtsa->client, REG_REFERENCE);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_REFERENCE read err(ret = %d)\n", err);
+ return;
+ }
+
+ val = SOC_CNT2_SOCCLK_ON;
+ val |= (unsigned char)(CALIB_FS_62_5HZ << 7);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_SOC_CNT2, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SOC_CNT2 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ /* Wait reference mode setting */
+ ret = (signed long)((REFERENCE_WAIT * HZ)/1000);
+ ret += (signed long)(1000 / HZ);
+ while (ret > 0) {
+ timeout = ret;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ ret = schedule_timeout(timeout);
+ }
+
+ val = SOC_CNT2_SOCCLK_OFF;
+ val |= (unsigned char)(CALIB_FS_62_5HZ << 7);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_SOC_CNT2, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SOC_CNT2 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ val = (unsigned char)(CTRL_REG2_NOR_MODE | CTRL_REG2_HPCF);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_CTRL_REG2, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_CTRL_REG2 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+#endif /* GYRO_VER3_ES1 */
+
+ if (ewtsa->event == EWTSA_POLLING_MODE) {
+ fifo_type = BYPASS_MODE;
+ }
+ else {
+ fifo_type = ewtsa->fifo_mode;
+ }
+
+ if ((ewtsa->reset_param & RST_REG_INIT_ALL) != RST_REG_INIT_OFF) {
+ if (fifo_type == BYPASS_MODE) {
+ if (ewtsa->delay < DELAY_THRES_4) {
+ if (ewtsa->delay >= DELAY_THRES_3) {
+ ewtsa->dlpf = DELAY_DLPF_3;
+ } else {
+ ewtsa->dlpf = DELAY_DLPF_2;
+ }
+ } else {
+ if (ewtsa->delay >= DELAY_THRES_6) {
+ ewtsa->dlpf = DELAY_DLPF_6;
+ } else if (ewtsa->delay >= DELAY_THRES_5) {
+ ewtsa->dlpf = DELAY_DLPF_5;
+ } else {
+ ewtsa->dlpf = DELAY_DLPF_4;
+ }
+ }
+ }
+ else {
+ if (ewtsa->delay <= DELAY_THRES_FIFO) {
+ ewtsa->dlpf = DELAY_DLPF_1;
+ }
+ else {
+ ewtsa->dlpf = DELAY_DLPF_2;
+ }
+ }
+ val = (unsigned char)((unsigned char)(ewtsa->range << 3) | ewtsa->dlpf);
+
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_FS_DLPF_CTRL, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_FS_DLPF_CTRL write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+ }
+
+ if ((ewtsa->reset_param & RST_REG_INIT_DELAY) != RST_REG_INIT_OFF) {
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_CTRL_REG5, CTRL_REG5_INIT);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_CTRL_REG5 write err(data = 0x%02x, ret = %d)\n", CTRL_REG5_INIT, err);
+ return;
+ }
+
+ if (fifo_type != BYPASS_MODE) {
+ val = FIFO_DRDY_DITH_EN;
+ }
+ else {
+ val = (unsigned char)(fifo_type << 5);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_FIFO_CTRL2, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_FIFO_CTRL2 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+ val = FIFO_DRDY_INIT;
+ }
+
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_FIFO_DRDY, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_FIFO_DRDY write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ if (fifo_type == BYPASS_MODE) {
+ if (ewtsa->event == EWTSA_POLLING_MODE) {
+ smpl = DATA_RATE_1 - 1; /* SMPL = 0 */
+ }
+ else {
+ if (ewtsa->delay <= DELAY_THRES_2) {
+ smpl = DELAY_INTMIN_THRES;
+ }
+ else {
+ smpl = (unsigned char)(ewtsa->delay - 1);
+ }
+ }
+ ewtsa->fifo_num = 1;
+ }
+ else {
+ if (ewtsa->delay <= DELAY_THRES_FIFO) {
+ if (ewtsa->delay > FIFO_MIN_DELAY) {
+ number = (unsigned char)(ewtsa->delay / DATA_RATE_5);
+ if ((ewtsa->delay % DATA_RATE_5) != 0) {
+ number++;
+ }
+ }
+ else {
+ number = 2;
+ }
+ smpl = DATA_RATE_5 - 1; /* SMPL = 4 */
+ }
+ else {
+ number = (unsigned char)(ewtsa->delay / DATA_RATE_10);
+ if ((ewtsa->delay % DATA_RATE_10) != 0) {
+ number++;
+ }
+ smpl = DATA_RATE_10 - 1; /* SMPL = 9 */
+ }
+
+ val = (unsigned char)(fifo_type << 5);
+ val |= number;
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_FIFO_CTRL2, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_FIFO_CTRL2 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+ ewtsa->fifo_num = number;
+
+ val = CTRL_REG3_WTM_EN;
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_CTRL_REG3, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_CTRL_REG3 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_CTRL_REG5, CTRL_REG5_FIFO_EN);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_CTRL_REG5 write err(data = 0x%02x, ret = %d)\n", CTRL_REG5_FIFO_EN, err);
+ return;
+ }
+ }
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_DATA_RATE, smpl);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_DATA_RATE write err(data = 0x%02x, ret = %d)\n", smpl, err);
+ return;
+ }
+ }
+ ewtsa->reset_param = RST_REG_INIT_OFF;
+
+ if (ewtsa->calib == EWTSA_ON) {
+ EWTSA_set_calibration(ewtsa);
+ }
+ else {
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_SLEEP_CTRL, SLEEP_CTRL_SLEEP);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SLEEP_CTRL write err(data = 0x%02x, ret = %d)\n", SLEEP_CTRL_SLEEP, err);
+ return;
+ }
+
+#if GYRO_VER3_ES1
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG2_CTRL_REG1, CTRL_REG1_ACTIVATE);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG2_CTRL_REG1 write err(data = 0x%02x, ret = %d)\n", CTRL_REG1_ACTIVATE, err);
+ return;
+ }
+#else /* GYRO_VER3_ES1 */
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_SLEEP_CTRL, SLEEP_CTRL_ACTIVATE);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SLEEP_CTRL write err(data = 0x%02x, ret = %d)\n", SLEEP_CTRL_ACTIVATE, err);
+ return;
+ }
+#endif /* GYRO_VER3_ES1 */
+
+ /* FIFO clear */
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_FIFO_CTRL1, FIFO_CTRL1_CLEAR);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_FIFO_CTRL1 write err(data = 0x02, ret = %d)\n", err);
+ return;
+ }
+ }
+
+ ewtsa->calib_rst = EWTSA_OFF;
+}
+
+static void EWTSA_set_calibration(struct ewtsa_t *ewtsa)
+{
+ unsigned char val;
+ s32 err;
+ signed long ret;
+ signed long l_timeout;
+ unsigned long center;
+ static const unsigned short ewtsa_boost_time[4] = {
+ EWTSA_BOOST_TIME_0, EWTSA_BOOST_TIME_1, EWTSA_BOOST_TIME_2, EWTSA_BOOST_TIME_3
+ };
+ static const unsigned char ewtsa_w1_threshold_level[4] = {
+ EWTSA_W1_THRES_LV_0, EWTSA_W1_THRES_LV_1, EWTSA_W1_THRES_LV_2, EWTSA_W1_THRES_LV_3
+ };
+ static const unsigned short ewtsa_dynamic_range[4] = {
+ EWTSA_RANGE_0, EWTSA_RANGE_1, EWTSA_RANGE_2, EWTSA_RANGE_3
+ };
+
+ if (ewtsa == NULL) {
+ printk(KERN_INFO "EWTSA_system_restart:ewtsa null pointer\n");
+ return;
+ }
+
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_SOC_CNT1, SOC_CNT1_WIN_DIS | SOC_CNT1_BOOST_ON);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SOC_CNT1 write err(data = 0x%02x, ret = %d)\n", SOC_CNT1_WIN_DIS | SOC_CNT1_BOOST_ON, err);
+ return;
+ }
+
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_SLEEP_CTRL, SLEEP_CTRL_SLEEP);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SLEEP_CTRL write err(data = 0x%02x, ret = %d)\n", SLEEP_CTRL_SLEEP, err);
+ return;
+ }
+
+#if GYRO_VER3_ES1
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG2_CTRL_REG1, CTRL_REG1_ACTIVATE);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG2_CTRL_REG1 write err(data = 0x%02x, ret = %d)\n", CTRL_REG1_ACTIVATE, err);
+ return;
+ }
+#else /* GYRO_VER3_ES1 */
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_SLEEP_CTRL, SLEEP_CTRL_ACTIVATE);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SLEEP_CTRL write err(data = 0x%02x, ret = %d)\n", SLEEP_CTRL_ACTIVATE, err);
+ return;
+ }
+#endif /* GYRO_VER3_ES1 */
+
+ if (ewtsa_w1_threshold_level[ewtsa->w1_thres_level] > ewtsa->w1_thres_x) {
+ center = ewtsa_w1_threshold_level[ewtsa->w1_thres_level] + 1;
+ }
+ else {
+ center = ewtsa->w1_thres_x;
+ }
+ center *= W1_THRES_CENTER_COEF;
+ center /= ewtsa_dynamic_range[ewtsa->range];
+ val = (unsigned char)((0xFF00 & center) >> 8);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_REFERENCE, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_REFERENCE write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ val = (unsigned char)(CTRL_REG2_REF_MODE | CTRL_REG2_HPCF);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_CTRL_REG2, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_CTRL_REG2 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ val = SOC_CNT2_SOCCLK_ON;
+ val |= (unsigned char)(CALIB_FS_62_5HZ << 7);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_SOC_CNT2, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SOC_CNT2 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ /* Wait reference mode setting */
+ ret = (signed long)((REFERENCE_WAIT * HZ)/1000);
+ ret += (signed long)(1000 / HZ);
+ while (ret > 0) {
+ l_timeout = ret;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ ret = schedule_timeout(l_timeout);
+ }
+
+ val = SOC_CNT2_SOCCLK_OFF;
+ val |= (unsigned char)(CALIB_FS_62_5HZ << 7);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_SOC_CNT2, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SOC_CNT2 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ val = (unsigned char)(CTRL_REG2_NOR_MODE | CTRL_REG2_HPCF);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_CTRL_REG2, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_CTRL_REG2 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+#if GYRO_VER3_ES3 || GYRO_VER3_ES4 //2012.01.25 for ES3 & ES4
+ val = (unsigned char)(0x40 | SOC_CNT1_BOOST_ON);
+#else
+ val = (unsigned char)((unsigned char)(ewtsa->w1_datamask << 6) | SOC_CNT1_BOOST_ON);
+#endif
+ val |= (unsigned char)(ewtsa->w2_thres_en << 5);
+ val |= (unsigned char)(ewtsa->w1_thres_en << 4);
+ val |= (unsigned char)(ewtsa->calib_boost_tm << 2);
+ val |= (unsigned char)(ewtsa->calib_boost_fs << 1);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_SOC_CNT1, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SOC_CNT1 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ val = (unsigned char)(SOC_CNT2_SOCCLK_ON | ewtsa->w1_thres_level);
+ val |= (unsigned char)(ewtsa->calib_fs << 7);
+ val |= (unsigned char)(ewtsa->offset_lag << 4);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_SOC_CNT2, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SOC_CNT2 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ val = (unsigned char)(ewtsa->hpf << 7);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_SLFSLP_CNT, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SLFSLP_CNT write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ if (ewtsa_w1_threshold_level[ewtsa->w1_thres_level] > ewtsa->w1_thres_x) {
+ center = ewtsa_w1_threshold_level[ewtsa->w1_thres_level] + 1;
+ }
+ else {
+ center = ewtsa->w1_thres_x;
+ }
+ center *= W1_THRES_CENTER_COEF;
+ center /= ewtsa_dynamic_range[ewtsa->range];
+ val = (unsigned char)((0xFF00 & center) >> 8);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_W1THX_H, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_W1THX_H write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ val = (unsigned char)(0xFF & center);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_W1THX_L, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_W1THX_L write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ if (ewtsa_w1_threshold_level[ewtsa->w1_thres_level] > ewtsa->w1_thres_y) {
+ center = ewtsa_w1_threshold_level[ewtsa->w1_thres_level] + 1;
+ }
+ else {
+ center = ewtsa->w1_thres_y;
+ }
+ center *= W1_THRES_CENTER_COEF;
+ center /= ewtsa_dynamic_range[ewtsa->range];
+ val = (unsigned char)((0xFF00 & center) >> 8);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_W1THY_H, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_W1THY_H write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ val = (unsigned char)(0xFF & center);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_W1THY_L, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_W1THY_L write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ if (ewtsa_w1_threshold_level[ewtsa->w1_thres_level] > ewtsa->w1_thres_z) {
+ center = ewtsa_w1_threshold_level[ewtsa->w1_thres_level] + 1;
+ }
+ else {
+ center = ewtsa->w1_thres_z;
+ }
+ center *= W1_THRES_CENTER_COEF;
+ center /= ewtsa_dynamic_range[ewtsa->range];
+ val = (unsigned char)((0xFF00 & center) >> 8);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_W1THZ_H, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_W1THZ_H write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ val = (unsigned char)(0xFF & center);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_W1THZ_L, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_W1THZ_L write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_DIGITAL_CAL, DIGITAL_CAL_ENABLE);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_DIGITAL_CAL write err(data = 0x%02x, ret = %d)\n", DIGITAL_CAL_ENABLE, err);
+ return;
+ }
+
+ /* Wait calibration boost */
+ ret = (signed long)((ewtsa_boost_time[ewtsa->calib_boost_tm] * HZ)/1000);
+ ret += (signed long)(1000 / HZ);
+ while (ret > 0) {
+ l_timeout = ret;
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ ret = schedule_timeout(l_timeout);
+ }
+
+ /* boost stop */
+ val = (unsigned char)(ewtsa->w1_datamask << 6);
+ val |= (unsigned char)(ewtsa->w2_thres_en << 5);
+ val |= (unsigned char)(ewtsa->w1_thres_en << 4);
+ val |= (unsigned char)(ewtsa->calib_boost_tm << 2);
+ val |= (unsigned char)(ewtsa->calib_boost_fs << 1);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_SOC_CNT1, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SOC_CNT1 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+
+ if ((ewtsa->calib_alway != EWTSA_ON) ||
+ ((ewtsa->w1_thres_en == THRESHOLD_OFF) && (ewtsa->w2_thres_en == THRESHOLD_OFF))) {
+ val = (unsigned char)(SOC_CNT2_SOCCLK_OFF | ewtsa->w1_thres_level);
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_SOC_CNT2, val);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SOC_CNT2 write err(data = 0x%02x, ret = %d)\n", val, err);
+ return;
+ }
+ }
+
+ /* FIFO clear */
+ err = i2c_smbus_write_byte_data(ewtsa->client, REG_FIFO_CTRL1, FIFO_CTRL1_CLEAR);
+ if (err < 0) {
+ dev_err(&ewtsa->client->dev, "REG_FIFO_CTRL1 write err(data = 0x02, ret = %d)\n", err);
+ return;
+ }
+ return;
+}
+
+/* Read the angular data from the registers */
+static void EWTSA_report_abs(struct ewtsa_t *ewt)
+{
+ unsigned short x = 0, y = 0, z = 0, temp = 0;
+ s16 send_x, send_y, send_z, send_temp;
+ unsigned char reg = REG_MBURST_ALL;
+ unsigned char reg2[32][8];
+ int err;
+ struct i2c_msg msg[2];
+#if GYRO_MOUNT_SWAP_XY
+ s16 tmp;
+#endif /* GYRO_MOUNT_SWAP_XY */
+ unsigned char diag_stat = 0;
+ unsigned char loop;
+ unsigned long l_flags = 0;
+ signed long cal_tmp[4] = {0};
+ unsigned char get_num = 0;
+ unsigned char read_num = 0;
+
+ if (ewt == NULL) {
+ printk(KERN_INFO "struct ewtsa_t null pointer\n");
+ return;
+ }
+
+ if (ewt->event == EWTSA_POLLING_MODE) {
+ mutex_lock(&ewt->mlock);
+ }
+ else {
+ if (ewt->fifo_mode == FIFO_MODE) {
+ err = i2c_smbus_read_byte_data(ewt->client, REG_FIFO_DM);
+ if (err < 0) {
+ dev_err(&ewt->client->dev, "REG_FIFO_DM read err(%d)\n", err);
+ err = 0;
+ }
+ read_num = (unsigned char)err;
+ if (((unsigned char)(read_num & 0x20) >> 5) == 1) {
+ /* data nothing */
+ err = i2c_smbus_read_byte_data(ewt->client, REG_INT_STAT);
+ if (err < 0) {
+ dev_err(&ewt->client->dev, "REG_INT_STAT read err(%d)\n", err);
+ }
+ return;
+ }
+ read_num &= 0x1F;
+
+ if (ewt->fifo_num > read_num) {
+ err = i2c_smbus_read_byte_data(ewt->client, REG_INT_STAT);
+ if (err < 0) {
+ dev_err(&ewt->client->dev, "REG_INT_STAT read err(%d)\n", err);
+ }
+ return;
+ }
+ }
+ }
+
+ msg[0].addr = ewt->client->addr;
+ msg[0].flags = 0;
+ msg[0].len = 1;
+ msg[0].buf = &reg;
+
+ msg[1].addr = ewt->client->addr;
+ msg[1].flags = I2C_M_RD;
+ msg[1].len = (unsigned short)(8 * ewt->fifo_num);
+ msg[1].buf = &reg2[0][0];
+
+ err = i2c_transfer(ewt->client->adapter, msg, 2);
+ if (err < 0) {
+ dev_err(&ewt->client->dev, "REG_MBURST_ALL read err(%d)\n", err);
+ memset(reg2, 0x00, sizeof(reg2));
+ }
+
+ for (loop = 0; loop < ewt->fifo_num; loop++) {
+ x = (unsigned short)(0xFF & reg2[loop][3]);
+ x |= (unsigned short)(0xFF00 & (unsigned short)(reg2[loop][2] << 8));
+ y = (unsigned short)(0xFF & reg2[loop][5]);
+ y |= (unsigned short)(0xFF00 & (unsigned short)(reg2[loop][4] << 8));
+ z = (unsigned short)(0xFF & reg2[loop][7]);
+ z |= (unsigned short)(0xFF00 & (unsigned short)(reg2[loop][6] << 8));
+ temp = (unsigned short)(0xFF & reg2[loop][1]);
+ temp |= (unsigned short)(0xFF00 & (unsigned short)(reg2[loop][0] << 8));
+
+ cal_tmp[0] += (signed short)x;
+ cal_tmp[1] += (signed short)y;
+ cal_tmp[2] += (signed short)z;
+ cal_tmp[3] += (signed short)temp;
+ get_num++;
+ }
+
+ err = i2c_smbus_read_byte_data(ewt->client, REG_STATUS_REG);
+ if (err < 0) {
+ dev_err(&ewt->client->dev, "REG_STATUS_REG read err(%d)\n", err);
+ err = 0;
+ }
+ reg = (unsigned char)err;
+ if ((reg & STATUS_REG_OVERRUN) != 0) {
+ err = i2c_smbus_write_byte_data(ewt->client, REG_FIFO_CTRL1, FIFO_CTRL1_CLEAR);
+ if (err < 0) {
+ dev_err(&ewt->client->dev, "REG_FIFO_CTRL1 read err(%d)\n", err);
+ }
+ }
+
+ if (ewt->int_skip == EWSTA_INT_SKIP) {
+ ewt->int_skip = EWSTA_INT_CLEAR;
+ err = i2c_smbus_read_byte_data(ewt->client, REG_INT_STAT);
+ if (err < 0) {
+ dev_err(&ewt->client->dev, "REG_INT_STAT read err(%d)\n", err);
+ }
+ return;
+ }
+
+ if (get_num != 0) {
+ if (ewt->fifo_data_flag == EWTSA_ON) {
+ cal_tmp[0] += ewt->fifo_lastdata[0];
+ cal_tmp[1] += ewt->fifo_lastdata[1];
+ cal_tmp[2] += ewt->fifo_lastdata[2];
+ cal_tmp[3] += ewt->fifo_lastdata[3];
+ get_num++;
+ }
+
+ /* fifo mode only */
+ if (get_num > 1) {
+ ewt->fifo_lastdata[0] = (signed short)x;
+ ewt->fifo_lastdata[1] = (signed short)y;
+ ewt->fifo_lastdata[2] = (signed short)z;
+ ewt->fifo_lastdata[3] = (signed short)temp;
+ ewt->fifo_data_flag = EWTSA_ON;
+ }
+
+ send_x = (signed short)(cal_tmp[0] / get_num);
+ send_y = (signed short)(cal_tmp[1] / get_num);
+ send_z = (signed short)(cal_tmp[2] / get_num);
+ send_temp = (signed short)(cal_tmp[3] / get_num);
+ }
+ else {
+ ewt->fifo_lastdata[0] = 0;
+ ewt->fifo_lastdata[1] = 0;
+ ewt->fifo_lastdata[2] = 0;
+ ewt->fifo_lastdata[3] = 0;
+ ewt->fifo_data_flag = EWTSA_OFF;
+
+ send_x = 0;
+ send_y = 0;
+ send_z = 0;
+ send_temp = 0;
+ err = i2c_smbus_write_byte_data(ewt->client, REG_FIFO_CTRL1, FIFO_CTRL1_CLEAR);
+ if (err < 0) {
+ dev_err(&ewt->client->dev, "REG_FIFO_CTRL1 read err(%d)\n", err);
+ }
+ }
+
+#if GYRO_MOUNT_SWAP_XY
+ tmp = send_x;
+ send_x = send_y;
+ send_y = tmp;
+#endif /* GYRO_MOUNT_SWAP_XY */
+#if GYRO_MOUNT_REVERSE_X
+ send_x *= -1;
+#endif /* GYRO_MOUNT_REVERSE_X */
+#if GYRO_MOUNT_REVERSE_Y
+ send_y *= -1;
+#endif /* GYRO_MOUNT_REVERSE_Y */
+#if GYRO_MOUNT_REVERSE_Z
+ send_z *= -1;
+#endif /* GYRO_MOUNT_REVERSE_Z */
+
+ if (ewt->event != EWTSA_POLLING_MODE) {
+ err = i2c_smbus_read_byte_data(ewt->client, REG_INT_STAT);
+ if (err < 0) {
+ dev_err(&ewt->client->dev, "REG_INT_STAT read err(%d)\n", err);
+ err = 0;
+ }
+ diag_stat = (unsigned char)err;
+ spin_lock_irqsave(&ewt->slock, l_flags);
+ }
+
+ input_report_abs(ewt->input_dev, ABS_X, send_x);
+ input_report_abs(ewt->input_dev, ABS_Y, send_y);
+ input_report_abs(ewt->input_dev, ABS_Z, send_z);
+ input_report_abs(ewt->input_dev, ABS_RX, send_temp);
+
+#if USE_GPIO_DIAG_PIN
+ if (ewt->event == EWTSA_POLLING_MODE) {
+ input_report_abs(ewt->input_dev, ABS_RY, gpio_get_value(DIAG_PIN));
+ }
+ else {
+ diag_stat = (unsigned char)((unsigned char)(diag_stat & 0x04) >> 2);
+ input_report_abs(ewt->input_dev, ABS_RY, diag_stat);
+ }
+#else
+ diag_stat = (unsigned char)((unsigned char)(diag_stat & 0x04) >> 2);
+ input_report_abs(ewt->input_dev, ABS_RY, diag_stat);
+#endif
+ input_sync(ewt->input_dev);
+
+ if (ewt->event == EWTSA_POLLING_MODE) {
+ mutex_unlock(&ewt->mlock);
+ }
+ else {
+ spin_unlock_irqrestore(&ewt->slock, l_flags);
+ }
+}
+
+static void EWTSA_dev_poll(struct work_struct *work)
+{
+ struct ewtsa_t *ewt;
+
+ if (work == NULL) {
+ printk(KERN_INFO "struct work_struct null pointer\n");
+ return;
+ }
+ ewt = container_of(work, struct ewtsa_t, input_work.work);
+
+ if (ewt->sleep == SLEEP_OFF) {
+ EWTSA_report_abs(ewt);
+ }
+}
+
+/* Interrupt handler */
+#if USE_GPIO_DRDY_PIN
+static irqreturn_t EWTSA_interrupt(int irq, void *dev_id)
+{
+ struct ewtsa_t *ewtsa = dev_id;
+
+ if (ewtsa == NULL) {
+ printk(KERN_INFO "struct ewtsa_t null pointer\n");
+ return IRQ_RETVAL(1);
+ }
+
+ if (irq == gpio_to_irq(DRDY_PIN)) {
+ if (ewtsa->sleep != SLEEP_OFF) {
+ return IRQ_RETVAL(1);
+ }
+ EWTSA_report_abs(ewtsa);
+ }
+ return IRQ_RETVAL(1);
+}
+#endif
+
+static void EWTSA_input_work_func(struct work_struct *work)
+{
+ struct ewtsa_t *ewt;
+ volatile unsigned long start_jiff = jiffies, delay_jiff, next_jiff;
+
+ if (work == NULL) {
+ printk(KERN_INFO "struct work_struct null pointer\n");
+ return;
+ }
+ ewt = container_of(work, struct ewtsa_t, input_work.work);
+
+ if (ewt->sleep == SLEEP_OFF) {
+ EWTSA_dev_poll(work);
+
+ /* adjust polling interval */
+ delay_jiff = jiffies;
+ if (start_jiff > delay_jiff) { /* jiffies overflow */
+ delay_jiff += 0xFFFFFFFFUL - start_jiff;
+ }
+ else {
+ delay_jiff -= start_jiff;
+ }
+ next_jiff = msecs_to_jiffies(ewt->delay);
+ /* for delay_jiff > next_jiff */
+ delay_jiff %= next_jiff;
+ next_jiff -= delay_jiff;
+
+ queue_delayed_work(ewt->workqueue,
+ &ewt->input_work,
+ next_jiff);
+ }
+}
+
+/* Suspend function to register device */
+static int EWTSA_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct ewtsa_t *ewt;
+ s32 ret;
+
+ if (client == NULL) {
+ printk(KERN_INFO "struct i2c_client null pointer\n");
+ return -EIO;
+ }
+ ewt = i2c_get_clientdata(client);
+
+ if (ewt->event == EWTSA_POLLING_MODE) {
+ cancel_delayed_work(&ewt->input_work);
+ }
+ else {
+ /* disable interrupt */
+ ret = i2c_smbus_write_byte_data(ewt->client, REG_INT_CTRL, INT_CTRL_INT_DISABLE);
+ if (ret < 0) {
+ dev_err(&ewt->client->dev, "REG_INT_CTRL write err(ret = %d)\n", ret);
+ }
+
+ if (ewt->sleep == SLEEP_OFF) {
+#if USE_GPIO_DRDY_PIN
+ free_irq(gpio_to_irq(DRDY_PIN), ewt);
+ /* keep sleep status for resume */
+#endif
+ }
+ else {
+ ewt->sleep = SLEEP_ALLRESET;
+ }
+ }
+#if USE_GPIO_WINDOW_PIN
+ gpio_free(WINDOW_PIN);
+#endif
+#if USE_GPIO_DRDY_PIN
+ gpio_free(DRDY_PIN);
+#endif
+#if USE_GPIO_DIAG_PIN
+ gpio_free(DIAG_PIN);
+#endif
+ ewt->calib_rst = EWTSA_ON;
+ ewt->reset_param = RST_REG_INIT_ALL;
+ ret = i2c_smbus_write_byte_data(ewt->client, REG_SLEEP_CTRL, SLEEP_CTRL_ALLRSTSLEEP);
+ if (ret < 0) {
+ dev_err(&ewt->client->dev, "REG_SLEEP_CTRL write err(ret = %d)\n", ret);
+ }
+ return 0;
+}
+
+/* Resume function to register device */
+static int EWTSA_resume(struct i2c_client *client)
+{
+ struct ewtsa_t *ewt;
+ int ret;
+
+ if (client == NULL) {
+ printk(KERN_INFO "struct i2c_client null pointer\n");
+ return -EIO;
+ }
+ ewt = i2c_get_clientdata(client);
+
+#if USE_GPIO_WINDOW_PIN
+ ret = gpio_request(WINDOW_PIN, "WINDOW");
+ ret += gpio_direction_input(WINDOW_PIN);
+#else
+ ret = 0;
+#endif
+#if USE_GPIO_DIAG_PIN
+ ret += gpio_request(DIAG_PIN, "DIAG");
+ ret += gpio_direction_input(DIAG_PIN);
+#endif
+ if ((ewt->sleep != SLEEP_OFF) || (ret != 0)) {
+ return 0;
+ }
+
+#if GYRO_VER3_ES1
+ ret = i2c_smbus_write_byte_data(ewt->client, REG2_CTRL_REG1, CTRL_REG1_ACTIVATE);
+#else /* GYRO_VER3_ES1 */
+ ret = i2c_smbus_write_byte_data(ewt->client, REG_SLEEP_CTRL, SLEEP_CTRL_ACTIVATE);
+#endif /* GYRO_VER3_ES1 */
+
+ if (ret >= 0) {
+ EWTSA_system_restart(ewt);
+
+ if (ewt->event == EWTSA_POLLING_MODE) {
+ queue_delayed_work(ewt->workqueue,
+ &ewt->input_work,
+ msecs_to_jiffies(ewt->delay));
+ }
+ else {
+ /* enable interrupt */
+#if USE_GPIO_DRDY_PIN
+ ret = gpio_request(DRDY_PIN, "DRDY");
+ ret += gpio_direction_input(DRDY_PIN);
+ ret += request_threaded_irq(gpio_to_irq(DRDY_PIN), NULL, EWTSA_interrupt, IRQF_TRIGGER_RISING, DEVICE_NAME, ewt);
+
+ if (ret == 0) {
+ ewt->int_skip = EWSTA_INT_SKIP;
+ if (ewt->fifo_mode == BYPASS_MODE) {
+ ret = i2c_smbus_write_byte_data(ewt->client, REG_INT_CTRL, INT_CTRL_INT_ENABLE);
+ }
+ else {
+ ret = i2c_smbus_write_byte_data(ewt->client, REG_INT_CTRL, INT_CTRL_INT_ENABLE | INT_CTRL_INT_WTMON_EN);
+ }
+ if (ret < 0) {
+ dev_err(&ewt->client->dev, "REG_INT_CTRL write err(ret = %d)\n", ret);
+ }
+ }
+#endif
+ }
+ }
+ else {
+ dev_err(&ewt->client->dev, "REG_SLEEP_CTRL write err(ret = %d)\n", ret);
+ }
+
+ return 0;
+}
+
+/* Probe function to register device */
+static int EWTSA_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int ret;
+ struct ewtsa_t *ewtsa;
+
+ if (client == NULL) {
+ printk(KERN_INFO "EWTSA_probe:client null pointer\n");
+ return -EIO;
+ }
+
+ ewtsa = kzalloc(sizeof(*ewtsa), GFP_KERNEL);
+ if (ewtsa == NULL) {
+ dev_err(&client->dev, "unable to allocate memory!\n");
+ return -ENOMEM;
+ }
+
+ ewtsa->workqueue = create_workqueue("ewtsa_workqueue");
+ if (ewtsa->workqueue == NULL) {
+ dev_err(&client->dev, "Unable to create workqueue!\n");
+ kfree(ewtsa);
+ return -ENOMEM;
+ }
+
+ ewtsa->client = client;
+ i2c_set_clientdata(client, ewtsa);
+
+ ewtsa->sleep = SLEEP_INIT_VAL; /* sleep mode */
+ ewtsa->delay = DELAY_INIT_VAL; /* information interval */
+ ewtsa->range = RANGE_INIT_VAL; /* dynamic range */
+ ewtsa->dlpf = DLPF_INIT_VAL; /* dlpf */
+ ewtsa->calib = CALIB_FUNC_INIT_VAL; /* calibration function */
+ ewtsa->calib_boost_tm = CALIB_BTIM_INIT_VAL; /* boost time[ms] */
+ ewtsa->calib_fs = CALIB_FS_INIT_VAL; /* offset renewal rate */
+ ewtsa->calib_rst = CALIB_RST_INIT_VAL; /* calib restart flag */
+ ewtsa->fifo_mode = FIFO_TYPE_INIT_VAL; /* fifo type */
+ ewtsa->reset_param = RST_REG_INIT_ALL; /* register setting flag */
+#if USE_GPIO_DRDY_PIN
+ ewtsa->event = EWTSA_INTERUPT_MODE; /* data detect method */
+#else
+ ewtsa->event = EWTSA_POLLING_MODE; /* data detect method */
+#endif
+ ewtsa->calib_boost_fs = CALIB_BFS_INIT_VAL; /* calibration boost fs */
+ ewtsa->int_skip = EWSTA_INT_CLEAR; /* first data flag */
+ ewtsa->calib_alway = CALIB_CLK_INIT_VAL; /* socclk flag */
+ ewtsa->hpf = CALIB_HPF_INIT_VAL; /* hpf */
+ ewtsa->w1_thres_x = THRES_CENTER_INIT_X; /* threshold center val */
+ ewtsa->w1_thres_y = THRES_CENTER_INIT_Y; /* threshold center val */
+ ewtsa->w1_thres_z = THRES_CENTER_INIT_Z; /* threshold center val */
+ ewtsa->w1_datamask = W1_MASK_INIT_VAL; /* window1 datamask set */
+ ewtsa->w1_thres_en = W1_FUNC_INIT_VAL; /* window1 function flag */
+ ewtsa->w2_thres_en = W2_FUNC_INIT_VAL; /* window2 function flag */
+ ewtsa->offset_lag = CALIB_LAG_INIT_VAL; /* offset renewal lag */
+ ewtsa->w1_thres_level = W1_LV_INIT_VAL; /* window1 threshold lv */
+ ewtsa->fifo_data_flag = EWTSA_OFF; /* last data flag */
+ ewtsa->fifo_num = 0; /* fifo number */
+ memset(ewtsa->fifo_lastdata, 0x00, sizeof(ewtsa->fifo_lastdata));
+
+ /*create device file in sysfs as user interface */
+ ret = device_create_file(&client->dev, &ewtsa_sleep_attr);
+ if (ret != 0) {
+ dev_err(&client->dev, "create device(sleep_attr) file failed!\n");
+ destroy_workqueue(ewtsa->workqueue);
+ kfree(ewtsa);
+ return ret;
+ }
+ ret = device_create_file(&client->dev, &ewtsa_delay_attr);
+ if (ret != 0) {
+ dev_err(&client->dev, "create device(delay_attr) file failed!\n");
+ device_remove_file(&client->dev, &ewtsa_sleep_attr);
+ destroy_workqueue(ewtsa->workqueue);
+ kfree(ewtsa);
+ return ret;
+ }
+ ret = device_create_file(&client->dev, &ewtsa_range_attr);
+ if (ret != 0) {
+ dev_err(&client->dev, "create device(range_attr) file failed!\n");
+ device_remove_file(&client->dev, &ewtsa_delay_attr);
+ device_remove_file(&client->dev, &ewtsa_sleep_attr);
+ destroy_workqueue(ewtsa->workqueue);
+ kfree(ewtsa);
+ return ret;
+ }
+ ret = device_create_file(&client->dev, &ewtsa_calib_attr);
+ if (ret != 0) {
+ dev_err(&client->dev, "create device(calib_attr) file failed!\n");
+ device_remove_file(&client->dev, &ewtsa_range_attr);
+ device_remove_file(&client->dev, &ewtsa_delay_attr);
+ device_remove_file(&client->dev, &ewtsa_sleep_attr);
+ destroy_workqueue(ewtsa->workqueue);
+ kfree(ewtsa);
+ return ret;
+ }
+ ret = device_create_file(&client->dev, &ewtsa_config_attr);
+ if (ret != 0) {
+ dev_err(&client->dev, "create device(calib_attr) file failed!\n");
+ device_remove_file(&client->dev, &ewtsa_calib_attr);
+ device_remove_file(&client->dev, &ewtsa_range_attr);
+ device_remove_file(&client->dev, &ewtsa_delay_attr);
+ device_remove_file(&client->dev, &ewtsa_sleep_attr);
+ destroy_workqueue(ewtsa->workqueue);
+ kfree(ewtsa);
+ return ret;
+ }
+
+ /*input poll device register */
+ ewtsa->input_dev = input_allocate_device();
+ if (ewtsa->input_dev == NULL) {
+ dev_err(&client->dev, "alloc input device failed!\n");
+ device_remove_file(&client->dev, &ewtsa_config_attr);
+ device_remove_file(&client->dev, &ewtsa_calib_attr);
+ device_remove_file(&client->dev, &ewtsa_range_attr);
+ device_remove_file(&client->dev, &ewtsa_delay_attr);
+ device_remove_file(&client->dev, &ewtsa_sleep_attr);
+ destroy_workqueue(ewtsa->workqueue);
+ kfree(ewtsa);
+ return -ENOMEM;
+ }
+ ewtsa->input_dev->name = DEVICE_NAME;
+ ewtsa->input_dev->id.bustype = BUS_I2C;
+ ewtsa->input_dev->evbit[0] = BIT_MASK(EV_ABS);
+
+ mutex_init(&ewtsa->mlock);
+ spin_lock_init(&ewtsa->slock);
+
+ input_set_abs_params(ewtsa->input_dev, ABS_X, -MAX_VALUE, MAX_VALUE, 0, 0);
+ input_set_abs_params(ewtsa->input_dev, ABS_Y, -MAX_VALUE, MAX_VALUE, 0, 0);
+ input_set_abs_params(ewtsa->input_dev, ABS_Z, -MAX_VALUE, MAX_VALUE, 0, 0);
+ input_set_abs_params(ewtsa->input_dev, ABS_RX, -200, 200, 0, 0);
+ input_set_abs_params(ewtsa->input_dev, ABS_RY, -200, 200, 0, 0);
+
+ ret = input_register_device(ewtsa->input_dev);
+ if (ret != 0) {
+ dev_err(&client->dev, "register poll device failed!\n");
+ input_free_device(ewtsa->input_dev);
+ device_remove_file(&client->dev, &ewtsa_config_attr);
+ device_remove_file(&client->dev, &ewtsa_calib_attr);
+ device_remove_file(&client->dev, &ewtsa_range_attr);
+ device_remove_file(&client->dev, &ewtsa_delay_attr);
+ device_remove_file(&client->dev, &ewtsa_sleep_attr);
+ destroy_workqueue(ewtsa->workqueue);
+ kfree(ewtsa);
+ return ret;
+ }
+
+ /* GPIO configuration */
+ ret = i2c_smbus_write_byte_data(ewtsa->client, REG_SLEEP_CTRL, SLEEP_CTRL_ALLRSTSLEEP);
+ if (ret < 0) {
+ dev_err(&ewtsa->client->dev, "REG_SLEEP_CTRL write err(data = 0x00, ret = %d)\n", ret);
+ }
+#if USE_GPIO_WINDOW_PIN
+ ret = gpio_request(WINDOW_PIN, "WINDOW");
+ ret += gpio_direction_input(WINDOW_PIN);
+#else
+ ret = 0;
+#endif
+#if USE_GPIO_DIAG_PIN
+ ret += gpio_request(DIAG_PIN, "DIAG");
+ ret += gpio_direction_input(DIAG_PIN);
+#endif
+ if (ret != 0) {
+ dev_err(&client->dev, "gpio_request failed!\n");
+#if USE_GPIO_WINDOW_PIN
+ gpio_free(WINDOW_PIN);
+#endif
+#if USE_GPIO_DIAG_PIN
+ gpio_free(DIAG_PIN);
+#endif
+ input_unregister_device(ewtsa->input_dev);
+ input_free_device(ewtsa->input_dev);
+ device_remove_file(&client->dev, &ewtsa_config_attr);
+ device_remove_file(&client->dev, &ewtsa_calib_attr);
+ device_remove_file(&client->dev, &ewtsa_range_attr);
+ device_remove_file(&client->dev, &ewtsa_delay_attr);
+ device_remove_file(&client->dev, &ewtsa_sleep_attr);
+ destroy_workqueue(ewtsa->workqueue);
+ kfree(ewtsa);
+ return ret;
+ }
+
+ INIT_DELAYED_WORK(&ewtsa->input_work, EWTSA_input_work_func);
+
+ dev_info(&client->dev, "ewtsa device is probed successfully.\n");
+ return 0;
+}
+
+/* Remove function to unregister the device */
+static int EWTSA_remove(struct i2c_client *client)
+{
+ struct ewtsa_t *ewtsa;
+
+ if (client == NULL) {
+ printk(KERN_INFO "client pointer null!\n");
+ return -EIO;
+ }
+ ewtsa = i2c_get_clientdata(client);
+
+ i2c_smbus_write_byte_data(ewtsa->client, REG_SLEEP_CTRL, SLEEP_CTRL_ALLRSTSLEEP);
+#if USE_GPIO_DRDY_PIN
+ if (ewtsa->sleep == SLEEP_OFF) {
+ free_irq(gpio_to_irq(DRDY_PIN), ewtsa);
+ gpio_free(DRDY_PIN);
+ }
+#endif
+#if USE_GPIO_WINDOW_PIN
+ gpio_free(WINDOW_PIN);
+#endif
+#if USE_GPIO_DIAG_PIN
+ gpio_free(DIAG_PIN);
+#endif
+
+ input_unregister_device(ewtsa->input_dev);
+ input_free_device(ewtsa->input_dev);
+ destroy_workqueue(ewtsa->workqueue);
+ device_remove_file(&client->dev, &ewtsa_sleep_attr);
+ device_remove_file(&client->dev, &ewtsa_delay_attr);
+ device_remove_file(&client->dev, &ewtsa_range_attr);
+ device_remove_file(&client->dev, &ewtsa_calib_attr);
+ device_remove_file(&client->dev, &ewtsa_config_attr);
+ kfree(ewtsa);
+ dev_info(&client->dev, "ewtsa device is removed successfully.\n");
+ return 0;
+}
+
+
+static int __init init_ewtsa(void)
+{
+ return i2c_add_driver(&i2c_ewtsa_driver);
+}
+
+static void __exit exit_ewtsa(void)
+{
+ i2c_del_driver(&i2c_ewtsa_driver);
+}
+
+module_init(init_ewtsa);
+module_exit(exit_ewtsa);
+
+MODULE_AUTHOR("Panasonic Electronic Devices");
+MODULE_DESCRIPTION("Gyro sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/gp2ap002a00f.c b/drivers/input/misc/gp2ap002a00f.c
new file mode 100644
index 00000000..fe30bd0f
--- /dev/null
+++ b/drivers/input/misc/gp2ap002a00f.c
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2011 Sony Ericsson Mobile Communications Inc.
+ *
+ * Author: Courtney Cavin <courtney.cavin@sonyericsson.com>
+ * Prepared for up-stream by: Oskar Andero <oskar.andero@sonyericsson.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/input/gp2ap002a00f.h>
+
+struct gp2a_data {
+ struct input_dev *input;
+ const struct gp2a_platform_data *pdata;
+ struct i2c_client *i2c_client;
+};
+
+enum gp2a_addr {
+ GP2A_ADDR_PROX = 0x0,
+ GP2A_ADDR_GAIN = 0x1,
+ GP2A_ADDR_HYS = 0x2,
+ GP2A_ADDR_CYCLE = 0x3,
+ GP2A_ADDR_OPMOD = 0x4,
+ GP2A_ADDR_CON = 0x6
+};
+
+enum gp2a_controls {
+ /* Software Shutdown control: 0 = shutdown, 1 = normal operation */
+ GP2A_CTRL_SSD = 0x01
+};
+
+static int gp2a_report(struct gp2a_data *dt)
+{
+ int vo = gpio_get_value(dt->pdata->vout_gpio);
+
+ input_report_switch(dt->input, SW_FRONT_PROXIMITY, !vo);
+ input_sync(dt->input);
+
+ return 0;
+}
+
+static irqreturn_t gp2a_irq(int irq, void *handle)
+{
+ struct gp2a_data *dt = handle;
+
+ gp2a_report(dt);
+
+ return IRQ_HANDLED;
+}
+
+static int gp2a_enable(struct gp2a_data *dt)
+{
+ return i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_OPMOD,
+ GP2A_CTRL_SSD);
+}
+
+static int gp2a_disable(struct gp2a_data *dt)
+{
+ return i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_OPMOD,
+ 0x00);
+}
+
+static int gp2a_device_open(struct input_dev *dev)
+{
+ struct gp2a_data *dt = input_get_drvdata(dev);
+ int error;
+
+ error = gp2a_enable(dt);
+ if (error < 0) {
+ dev_err(&dt->i2c_client->dev,
+ "unable to activate, err %d\n", error);
+ return error;
+ }
+
+ gp2a_report(dt);
+
+ return 0;
+}
+
+static void gp2a_device_close(struct input_dev *dev)
+{
+ struct gp2a_data *dt = input_get_drvdata(dev);
+ int error;
+
+ error = gp2a_disable(dt);
+ if (error < 0)
+ dev_err(&dt->i2c_client->dev,
+ "unable to deactivate, err %d\n", error);
+}
+
+static int gp2a_initialize(struct gp2a_data *dt)
+{
+ int error;
+
+ error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_GAIN,
+ 0x08);
+ if (error < 0)
+ return error;
+
+ error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_HYS,
+ 0xc2);
+ if (error < 0)
+ return error;
+
+ error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_CYCLE,
+ 0x04);
+ if (error < 0)
+ return error;
+
+ error = gp2a_disable(dt);
+
+ return error;
+}
+
+static int gp2a_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct gp2a_platform_data *pdata = client->dev.platform_data;
+ struct gp2a_data *dt;
+ int error;
+
+ if (!pdata)
+ return -EINVAL;
+
+ if (pdata->hw_setup) {
+ error = pdata->hw_setup(client);
+ if (error < 0)
+ return error;
+ }
+
+ error = gpio_request_one(pdata->vout_gpio, GPIOF_IN, GP2A_I2C_NAME);
+ if (error)
+ goto err_hw_shutdown;
+
+ dt = kzalloc(sizeof(struct gp2a_data), GFP_KERNEL);
+ if (!dt) {
+ error = -ENOMEM;
+ goto err_free_gpio;
+ }
+
+ dt->pdata = pdata;
+ dt->i2c_client = client;
+
+ error = gp2a_initialize(dt);
+ if (error < 0)
+ goto err_free_mem;
+
+ dt->input = input_allocate_device();
+ if (!dt->input) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ input_set_drvdata(dt->input, dt);
+
+ dt->input->open = gp2a_device_open;
+ dt->input->close = gp2a_device_close;
+ dt->input->name = GP2A_I2C_NAME;
+ dt->input->id.bustype = BUS_I2C;
+ dt->input->dev.parent = &client->dev;
+
+ input_set_capability(dt->input, EV_SW, SW_FRONT_PROXIMITY);
+
+ error = request_threaded_irq(client->irq, NULL, gp2a_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ GP2A_I2C_NAME, dt);
+ if (error) {
+ dev_err(&client->dev, "irq request failed\n");
+ goto err_free_input_dev;
+ }
+
+ error = input_register_device(dt->input);
+ if (error) {
+ dev_err(&client->dev, "device registration failed\n");
+ goto err_free_irq;
+ }
+
+ device_init_wakeup(&client->dev, pdata->wakeup);
+ i2c_set_clientdata(client, dt);
+
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, dt);
+err_free_input_dev:
+ input_free_device(dt->input);
+err_free_mem:
+ kfree(dt);
+err_free_gpio:
+ gpio_free(pdata->vout_gpio);
+err_hw_shutdown:
+ if (pdata->hw_shutdown)
+ pdata->hw_shutdown(client);
+ return error;
+}
+
+static int gp2a_remove(struct i2c_client *client)
+{
+ struct gp2a_data *dt = i2c_get_clientdata(client);
+ const struct gp2a_platform_data *pdata = dt->pdata;
+
+ device_init_wakeup(&client->dev, false);
+
+ free_irq(client->irq, dt);
+
+ input_unregister_device(dt->input);
+ kfree(dt);
+
+ gpio_free(pdata->vout_gpio);
+
+ if (pdata->hw_shutdown)
+ pdata->hw_shutdown(client);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int gp2a_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct gp2a_data *dt = i2c_get_clientdata(client);
+ int retval = 0;
+
+ if (device_may_wakeup(&client->dev)) {
+ enable_irq_wake(client->irq);
+ } else {
+ mutex_lock(&dt->input->mutex);
+ if (dt->input->users)
+ retval = gp2a_disable(dt);
+ mutex_unlock(&dt->input->mutex);
+ }
+
+ return retval;
+}
+
+static int gp2a_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct gp2a_data *dt = i2c_get_clientdata(client);
+ int retval = 0;
+
+ if (device_may_wakeup(&client->dev)) {
+ disable_irq_wake(client->irq);
+ } else {
+ mutex_lock(&dt->input->mutex);
+ if (dt->input->users)
+ retval = gp2a_enable(dt);
+ mutex_unlock(&dt->input->mutex);
+ }
+
+ return retval;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(gp2a_pm, gp2a_suspend, gp2a_resume);
+
+static const struct i2c_device_id gp2a_i2c_id[] = {
+ { GP2A_I2C_NAME, 0 },
+ { }
+};
+
+static struct i2c_driver gp2a_i2c_driver = {
+ .driver = {
+ .name = GP2A_I2C_NAME,
+ .owner = THIS_MODULE,
+ .pm = &gp2a_pm,
+ },
+ .probe = gp2a_probe,
+ .remove = gp2a_remove,
+ .id_table = gp2a_i2c_id,
+};
+
+module_i2c_driver(gp2a_i2c_driver);
+
+MODULE_AUTHOR("Courtney Cavin <courtney.cavin@sonyericsson.com>");
+MODULE_DESCRIPTION("Sharp GP2AP002A00F I2C Proximity/Opto sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/gpio_axis.c b/drivers/input/misc/gpio_axis.c
new file mode 100644
index 00000000..0acf4a57
--- /dev/null
+++ b/drivers/input/misc/gpio_axis.c
@@ -0,0 +1,192 @@
+/* drivers/input/misc/gpio_axis.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+struct gpio_axis_state {
+ struct gpio_event_input_devs *input_devs;
+ struct gpio_event_axis_info *info;
+ uint32_t pos;
+};
+
+uint16_t gpio_axis_4bit_gray_map_table[] = {
+ [0x0] = 0x0, [0x1] = 0x1, /* 0000 0001 */
+ [0x3] = 0x2, [0x2] = 0x3, /* 0011 0010 */
+ [0x6] = 0x4, [0x7] = 0x5, /* 0110 0111 */
+ [0x5] = 0x6, [0x4] = 0x7, /* 0101 0100 */
+ [0xc] = 0x8, [0xd] = 0x9, /* 1100 1101 */
+ [0xf] = 0xa, [0xe] = 0xb, /* 1111 1110 */
+ [0xa] = 0xc, [0xb] = 0xd, /* 1010 1011 */
+ [0x9] = 0xe, [0x8] = 0xf, /* 1001 1000 */
+};
+uint16_t gpio_axis_4bit_gray_map(struct gpio_event_axis_info *info, uint16_t in)
+{
+ return gpio_axis_4bit_gray_map_table[in];
+}
+
+uint16_t gpio_axis_5bit_singletrack_map_table[] = {
+ [0x10] = 0x00, [0x14] = 0x01, [0x1c] = 0x02, /* 10000 10100 11100 */
+ [0x1e] = 0x03, [0x1a] = 0x04, [0x18] = 0x05, /* 11110 11010 11000 */
+ [0x08] = 0x06, [0x0a] = 0x07, [0x0e] = 0x08, /* 01000 01010 01110 */
+ [0x0f] = 0x09, [0x0d] = 0x0a, [0x0c] = 0x0b, /* 01111 01101 01100 */
+ [0x04] = 0x0c, [0x05] = 0x0d, [0x07] = 0x0e, /* 00100 00101 00111 */
+ [0x17] = 0x0f, [0x16] = 0x10, [0x06] = 0x11, /* 10111 10110 00110 */
+ [0x02] = 0x12, [0x12] = 0x13, [0x13] = 0x14, /* 00010 10010 10011 */
+ [0x1b] = 0x15, [0x0b] = 0x16, [0x03] = 0x17, /* 11011 01011 00011 */
+ [0x01] = 0x18, [0x09] = 0x19, [0x19] = 0x1a, /* 00001 01001 11001 */
+ [0x1d] = 0x1b, [0x15] = 0x1c, [0x11] = 0x1d, /* 11101 10101 10001 */
+};
+uint16_t gpio_axis_5bit_singletrack_map(
+ struct gpio_event_axis_info *info, uint16_t in)
+{
+ return gpio_axis_5bit_singletrack_map_table[in];
+}
+
+static void gpio_event_update_axis(struct gpio_axis_state *as, int report)
+{
+ struct gpio_event_axis_info *ai = as->info;
+ int i;
+ int change;
+ uint16_t state = 0;
+ uint16_t pos;
+ uint16_t old_pos = as->pos;
+ for (i = ai->count - 1; i >= 0; i--)
+ state = (state << 1) | gpio_get_value(ai->gpio[i]);
+ pos = ai->map(ai, state);
+ if (ai->flags & GPIOEAF_PRINT_RAW)
+ pr_info("axis %d-%d raw %x, pos %d -> %d\n",
+ ai->type, ai->code, state, old_pos, pos);
+ if (report && pos != old_pos) {
+ if (ai->type == EV_REL) {
+ change = (ai->decoded_size + pos - old_pos) %
+ ai->decoded_size;
+ if (change > ai->decoded_size / 2)
+ change -= ai->decoded_size;
+ if (change == ai->decoded_size / 2) {
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d unknown direction, "
+ "pos %d -> %d\n", ai->type,
+ ai->code, old_pos, pos);
+ change = 0; /* no closest direction */
+ }
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d change %d\n",
+ ai->type, ai->code, change);
+ input_report_rel(as->input_devs->dev[ai->dev],
+ ai->code, change);
+ } else {
+ if (ai->flags & GPIOEAF_PRINT_EVENT)
+ pr_info("axis %d-%d now %d\n",
+ ai->type, ai->code, pos);
+ input_event(as->input_devs->dev[ai->dev],
+ ai->type, ai->code, pos);
+ }
+ input_sync(as->input_devs->dev[ai->dev]);
+ }
+ as->pos = pos;
+}
+
+static irqreturn_t gpio_axis_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_axis_state *as = dev_id;
+ gpio_event_update_axis(as, 1);
+ return IRQ_HANDLED;
+}
+
+int gpio_event_axis_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int ret;
+ int i;
+ int irq;
+ struct gpio_event_axis_info *ai;
+ struct gpio_axis_state *as;
+
+ ai = container_of(info, struct gpio_event_axis_info, info);
+ if (func == GPIO_EVENT_FUNC_SUSPEND) {
+ for (i = 0; i < ai->count; i++)
+ disable_irq(gpio_to_irq(ai->gpio[i]));
+ return 0;
+ }
+ if (func == GPIO_EVENT_FUNC_RESUME) {
+ for (i = 0; i < ai->count; i++)
+ enable_irq(gpio_to_irq(ai->gpio[i]));
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ *data = as = kmalloc(sizeof(*as), GFP_KERNEL);
+ if (as == NULL) {
+ ret = -ENOMEM;
+ goto err_alloc_axis_state_failed;
+ }
+ as->input_devs = input_devs;
+ as->info = ai;
+ if (ai->dev >= input_devs->count) {
+ pr_err("gpio_event_axis: bad device index %d >= %d "
+ "for %d:%d\n", ai->dev, input_devs->count,
+ ai->type, ai->code);
+ ret = -EINVAL;
+ goto err_bad_device_index;
+ }
+
+ input_set_capability(input_devs->dev[ai->dev],
+ ai->type, ai->code);
+ if (ai->type == EV_ABS) {
+ input_set_abs_params(input_devs->dev[ai->dev], ai->code,
+ 0, ai->decoded_size - 1, 0, 0);
+ }
+ for (i = 0; i < ai->count; i++) {
+ ret = gpio_request(ai->gpio[i], "gpio_event_axis");
+ if (ret < 0)
+ goto err_request_gpio_failed;
+ ret = gpio_direction_input(ai->gpio[i]);
+ if (ret < 0)
+ goto err_gpio_direction_input_failed;
+ ret = irq = gpio_to_irq(ai->gpio[i]);
+ if (ret < 0)
+ goto err_get_irq_num_failed;
+ ret = request_irq(irq, gpio_axis_irq_handler,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ "gpio_event_axis", as);
+ if (ret < 0)
+ goto err_request_irq_failed;
+ }
+ gpio_event_update_axis(as, 0);
+ return 0;
+ }
+
+ ret = 0;
+ as = *data;
+ for (i = ai->count - 1; i >= 0; i--) {
+ free_irq(gpio_to_irq(ai->gpio[i]), as);
+err_request_irq_failed:
+err_get_irq_num_failed:
+err_gpio_direction_input_failed:
+ gpio_free(ai->gpio[i]);
+err_request_gpio_failed:
+ ;
+ }
+err_bad_device_index:
+ kfree(as);
+ *data = NULL;
+err_alloc_axis_state_failed:
+ return ret;
+}
diff --git a/drivers/input/misc/gpio_event.c b/drivers/input/misc/gpio_event.c
new file mode 100644
index 00000000..90f07eba
--- /dev/null
+++ b/drivers/input/misc/gpio_event.c
@@ -0,0 +1,228 @@
+/* drivers/input/misc/gpio_event.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+struct gpio_event {
+ struct gpio_event_input_devs *input_devs;
+ const struct gpio_event_platform_data *info;
+ void *state[0];
+};
+
+static int gpio_input_event(
+ struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ int i;
+ int devnr;
+ int ret = 0;
+ int tmp_ret;
+ struct gpio_event_info **ii;
+ struct gpio_event *ip = input_get_drvdata(dev);
+
+ for (devnr = 0; devnr < ip->input_devs->count; devnr++)
+ if (ip->input_devs->dev[devnr] == dev)
+ break;
+ if (devnr == ip->input_devs->count) {
+ pr_err("gpio_input_event: unknown device %p\n", dev);
+ return -EIO;
+ }
+
+ for (i = 0, ii = ip->info->info; i < ip->info->info_count; i++, ii++) {
+ if ((*ii)->event) {
+ tmp_ret = (*ii)->event(ip->input_devs, *ii,
+ &ip->state[i],
+ devnr, type, code, value);
+ if (tmp_ret)
+ ret = tmp_ret;
+ }
+ }
+ return ret;
+}
+
+static int gpio_event_call_all_func(struct gpio_event *ip, int func)
+{
+ int i;
+ int ret;
+ struct gpio_event_info **ii;
+
+ if (func == GPIO_EVENT_FUNC_INIT || func == GPIO_EVENT_FUNC_RESUME) {
+ ii = ip->info->info;
+ for (i = 0; i < ip->info->info_count; i++, ii++) {
+ if ((*ii)->func == NULL) {
+ ret = -ENODEV;
+ pr_err("gpio_event_probe: Incomplete pdata, "
+ "no function\n");
+ goto err_no_func;
+ }
+ if (func == GPIO_EVENT_FUNC_RESUME && (*ii)->no_suspend)
+ continue;
+ ret = (*ii)->func(ip->input_devs, *ii, &ip->state[i],
+ func);
+ if (ret) {
+ pr_err("gpio_event_probe: function failed\n");
+ goto err_func_failed;
+ }
+ }
+ return 0;
+ }
+
+ ret = 0;
+ i = ip->info->info_count;
+ ii = ip->info->info + i;
+ while (i > 0) {
+ i--;
+ ii--;
+ if ((func & ~1) == GPIO_EVENT_FUNC_SUSPEND && (*ii)->no_suspend)
+ continue;
+ (*ii)->func(ip->input_devs, *ii, &ip->state[i], func & ~1);
+err_func_failed:
+err_no_func:
+ ;
+ }
+ return ret;
+}
+
+static void __maybe_unused gpio_event_suspend(struct gpio_event *ip)
+{
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_SUSPEND);
+ if (ip->info->power)
+ ip->info->power(ip->info, 0);
+}
+
+static void __maybe_unused gpio_event_resume(struct gpio_event *ip)
+{
+ if (ip->info->power)
+ ip->info->power(ip->info, 1);
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_RESUME);
+}
+
+static int gpio_event_probe(struct platform_device *pdev)
+{
+ int err;
+ struct gpio_event *ip;
+ struct gpio_event_platform_data *event_info;
+ int dev_count = 1;
+ int i;
+ int registered = 0;
+
+ event_info = pdev->dev.platform_data;
+ if (event_info == NULL) {
+ pr_err("gpio_event_probe: No pdata\n");
+ return -ENODEV;
+ }
+ if ((!event_info->name && !event_info->names[0]) ||
+ !event_info->info || !event_info->info_count) {
+ pr_err("gpio_event_probe: Incomplete pdata\n");
+ return -ENODEV;
+ }
+ if (!event_info->name)
+ while (event_info->names[dev_count])
+ dev_count++;
+ ip = kzalloc(sizeof(*ip) +
+ sizeof(ip->state[0]) * event_info->info_count +
+ sizeof(*ip->input_devs) +
+ sizeof(ip->input_devs->dev[0]) * dev_count, GFP_KERNEL);
+ if (ip == NULL) {
+ err = -ENOMEM;
+ pr_err("gpio_event_probe: Failed to allocate private data\n");
+ goto err_kp_alloc_failed;
+ }
+ ip->input_devs = (void*)&ip->state[event_info->info_count];
+ platform_set_drvdata(pdev, ip);
+
+ for (i = 0; i < dev_count; i++) {
+ struct input_dev *input_dev = input_allocate_device();
+ if (input_dev == NULL) {
+ err = -ENOMEM;
+ pr_err("gpio_event_probe: "
+ "Failed to allocate input device\n");
+ goto err_input_dev_alloc_failed;
+ }
+ input_set_drvdata(input_dev, ip);
+ input_dev->name = event_info->name ?
+ event_info->name : event_info->names[i];
+ input_dev->event = gpio_input_event;
+ ip->input_devs->dev[i] = input_dev;
+ }
+ ip->input_devs->count = dev_count;
+ ip->info = event_info;
+ if (event_info->power)
+ ip->info->power(ip->info, 1);
+
+ err = gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_INIT);
+ if (err)
+ goto err_call_all_func_failed;
+
+ for (i = 0; i < dev_count; i++) {
+ err = input_register_device(ip->input_devs->dev[i]);
+ if (err) {
+ pr_err("gpio_event_probe: Unable to register %s "
+ "input device\n", ip->input_devs->dev[i]->name);
+ goto err_input_register_device_failed;
+ }
+ registered++;
+ }
+
+ return 0;
+
+err_input_register_device_failed:
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
+err_call_all_func_failed:
+ if (event_info->power)
+ ip->info->power(ip->info, 0);
+ for (i = 0; i < registered; i++)
+ input_unregister_device(ip->input_devs->dev[i]);
+ for (i = dev_count - 1; i >= registered; i--) {
+ input_free_device(ip->input_devs->dev[i]);
+err_input_dev_alloc_failed:
+ ;
+ }
+ kfree(ip);
+err_kp_alloc_failed:
+ return err;
+}
+
+static int gpio_event_remove(struct platform_device *pdev)
+{
+ struct gpio_event *ip = platform_get_drvdata(pdev);
+ int i;
+
+ gpio_event_call_all_func(ip, GPIO_EVENT_FUNC_UNINIT);
+ if (ip->info->power)
+ ip->info->power(ip->info, 0);
+ for (i = 0; i < ip->input_devs->count; i++)
+ input_unregister_device(ip->input_devs->dev[i]);
+ kfree(ip);
+ return 0;
+}
+
+static struct platform_driver gpio_event_driver = {
+ .probe = gpio_event_probe,
+ .remove = gpio_event_remove,
+ .driver = {
+ .name = GPIO_EVENT_DEV_NAME,
+ },
+};
+
+module_platform_driver(gpio_event_driver);
+
+MODULE_DESCRIPTION("GPIO Event Driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c
new file mode 100644
index 00000000..eefd0272
--- /dev/null
+++ b/drivers/input/misc/gpio_input.c
@@ -0,0 +1,390 @@
+/* drivers/input/misc/gpio_input.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/pm_wakeup.h>
+
+enum {
+ DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */
+ DEBOUNCE_PRESSED = BIT(1),
+ DEBOUNCE_NOTPRESSED = BIT(2),
+ DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */
+ DEBOUNCE_POLL = BIT(4), /* Stable polling state */
+
+ DEBOUNCE_UNKNOWN =
+ DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED,
+};
+
+struct gpio_key_state {
+ struct gpio_input_state *ds;
+ uint8_t debounce;
+};
+
+struct gpio_input_state {
+ struct gpio_event_input_devs *input_devs;
+ const struct gpio_event_input_info *info;
+ struct hrtimer timer;
+ int use_irq;
+ int debounce_count;
+ spinlock_t irq_lock;
+ struct wakeup_source *ws;
+ struct gpio_key_state key_state[0];
+};
+
+static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer)
+{
+ int i;
+ int pressed;
+ struct gpio_input_state *ds =
+ container_of(timer, struct gpio_input_state, timer);
+ unsigned gpio_flags = ds->info->flags;
+ unsigned npolarity;
+ int nkeys = ds->info->keymap_size;
+ const struct gpio_event_direct_entry *key_entry;
+ struct gpio_key_state *key_state;
+ unsigned long irqflags;
+ uint8_t debounce;
+ bool sync_needed;
+
+#if 0
+ key_entry = kp->keys_info->keymap;
+ key_state = kp->key_state;
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++)
+ pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio,
+ gpio_read_detect_status(key_entry->gpio));
+#endif
+ key_entry = ds->info->keymap;
+ key_state = ds->key_state;
+ sync_needed = false;
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++) {
+ debounce = key_state->debounce;
+ if (debounce & DEBOUNCE_WAIT_IRQ)
+ continue;
+ if (key_state->debounce & DEBOUNCE_UNSTABLE) {
+ debounce = key_state->debounce = DEBOUNCE_UNKNOWN;
+ enable_irq(gpio_to_irq(key_entry->gpio));
+ if (gpio_flags & GPIOEDF_PRINT_KEY_UNSTABLE)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) continue debounce\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ }
+ npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH);
+ pressed = gpio_get_value(key_entry->gpio) ^ npolarity;
+ if (debounce & DEBOUNCE_POLL) {
+ if (pressed == !(debounce & DEBOUNCE_PRESSED)) {
+ ds->debounce_count++;
+ key_state->debounce = DEBOUNCE_UNKNOWN;
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-"
+ "%x, %d (%d) start debounce\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ }
+ continue;
+ }
+ if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) {
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) debounce pressed 1\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ key_state->debounce = DEBOUNCE_PRESSED;
+ continue;
+ }
+ if (!pressed && (debounce & DEBOUNCE_PRESSED)) {
+ if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d "
+ "(%d) debounce pressed 0\n",
+ ds->info->type, key_entry->code,
+ i, key_entry->gpio);
+ key_state->debounce = DEBOUNCE_NOTPRESSED;
+ continue;
+ }
+ /* key is stable */
+ ds->debounce_count--;
+ if (ds->use_irq)
+ key_state->debounce |= DEBOUNCE_WAIT_IRQ;
+ else
+ key_state->debounce |= DEBOUNCE_POLL;
+ if (gpio_flags & GPIOEDF_PRINT_KEYS)
+ pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) "
+ "changed to %d\n", ds->info->type,
+ key_entry->code, i, key_entry->gpio, pressed);
+ input_event(ds->input_devs->dev[key_entry->dev], ds->info->type,
+ key_entry->code, pressed);
+ sync_needed = true;
+ }
+ if (sync_needed) {
+ for (i = 0; i < ds->input_devs->count; i++)
+ input_sync(ds->input_devs->dev[i]);
+ }
+
+#if 0
+ key_entry = kp->keys_info->keymap;
+ key_state = kp->key_state;
+ for (i = 0; i < nkeys; i++, key_entry++, key_state++) {
+ pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio,
+ gpio_read_detect_status(key_entry->gpio));
+ }
+#endif
+
+ if (ds->debounce_count)
+ hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL);
+ else if (!ds->use_irq)
+ hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL);
+ else
+ __pm_relax(ds->ws);
+
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id)
+{
+ struct gpio_key_state *ks = dev_id;
+ struct gpio_input_state *ds = ks->ds;
+ int keymap_index = ks - ds->key_state;
+ const struct gpio_event_direct_entry *key_entry;
+ unsigned long irqflags;
+ int pressed;
+
+ if (!ds->use_irq)
+ return IRQ_HANDLED;
+
+ key_entry = &ds->info->keymap[keymap_index];
+
+ if (ds->info->debounce_time.tv64) {
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ if (ks->debounce & DEBOUNCE_WAIT_IRQ) {
+ ks->debounce = DEBOUNCE_UNKNOWN;
+ if (ds->debounce_count++ == 0) {
+ __pm_stay_awake(ds->ws);
+ hrtimer_start(
+ &ds->timer, ds->info->debounce_time,
+ HRTIMER_MODE_REL);
+ }
+ if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE)
+ pr_info("gpio_event_input_irq_handler: "
+ "key %x-%x, %d (%d) start debounce\n",
+ ds->info->type, key_entry->code,
+ keymap_index, key_entry->gpio);
+ } else {
+ disable_irq_nosync(irq);
+ ks->debounce = DEBOUNCE_UNSTABLE;
+ }
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ } else {
+ pressed = gpio_get_value(key_entry->gpio) ^
+ !(ds->info->flags & GPIOEDF_ACTIVE_HIGH);
+ if (ds->info->flags & GPIOEDF_PRINT_KEYS)
+ pr_info("gpio_event_input_irq_handler: key %x-%x, %d "
+ "(%d) changed to %d\n",
+ ds->info->type, key_entry->code, keymap_index,
+ key_entry->gpio, pressed);
+ input_event(ds->input_devs->dev[key_entry->dev], ds->info->type,
+ key_entry->code, pressed);
+ input_sync(ds->input_devs->dev[key_entry->dev]);
+ }
+ return IRQ_HANDLED;
+}
+
+static int gpio_event_input_request_irqs(struct gpio_input_state *ds)
+{
+ int i;
+ int err;
+ unsigned int irq;
+ unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+
+ for (i = 0; i < ds->info->keymap_size; i++) {
+ err = irq = gpio_to_irq(ds->info->keymap[i].gpio);
+ if (err < 0)
+ goto err_gpio_get_irq_num_failed;
+ err = request_irq(irq, gpio_event_input_irq_handler,
+ req_flags, "gpio_keys", &ds->key_state[i]);
+ if (err) {
+ pr_err("gpio_event_input_request_irqs: request_irq "
+ "failed for input %d, irq %d\n",
+ ds->info->keymap[i].gpio, irq);
+ goto err_request_irq_failed;
+ }
+ if (ds->info->info.no_suspend) {
+ err = enable_irq_wake(irq);
+ if (err) {
+ pr_err("gpio_event_input_request_irqs: "
+ "enable_irq_wake failed for input %d, "
+ "irq %d\n",
+ ds->info->keymap[i].gpio, irq);
+ goto err_enable_irq_wake_failed;
+ }
+ }
+ }
+ return 0;
+
+ for (i = ds->info->keymap_size - 1; i >= 0; i--) {
+ irq = gpio_to_irq(ds->info->keymap[i].gpio);
+ if (ds->info->info.no_suspend)
+ disable_irq_wake(irq);
+err_enable_irq_wake_failed:
+ free_irq(irq, &ds->key_state[i]);
+err_request_irq_failed:
+err_gpio_get_irq_num_failed:
+ ;
+ }
+ return err;
+}
+
+int gpio_event_input_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int ret;
+ int i;
+ unsigned long irqflags;
+ struct gpio_event_input_info *di;
+ struct gpio_input_state *ds = *data;
+ char *wlname;
+
+ di = container_of(info, struct gpio_event_input_info, info);
+
+ if (func == GPIO_EVENT_FUNC_SUSPEND) {
+ if (ds->use_irq)
+ for (i = 0; i < di->keymap_size; i++)
+ disable_irq(gpio_to_irq(di->keymap[i].gpio));
+ hrtimer_cancel(&ds->timer);
+ return 0;
+ }
+ if (func == GPIO_EVENT_FUNC_RESUME) {
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ if (ds->use_irq)
+ for (i = 0; i < di->keymap_size; i++)
+ enable_irq(gpio_to_irq(di->keymap[i].gpio));
+ hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ if (ktime_to_ns(di->poll_time) <= 0)
+ di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC);
+
+ *data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) *
+ di->keymap_size, GFP_KERNEL);
+ if (ds == NULL) {
+ ret = -ENOMEM;
+ pr_err("gpio_event_input_func: "
+ "Failed to allocate private data\n");
+ goto err_ds_alloc_failed;
+ }
+ ds->debounce_count = di->keymap_size;
+ ds->input_devs = input_devs;
+ ds->info = di;
+ wlname = kasprintf(GFP_KERNEL, "gpio_input:%s%s",
+ input_devs->dev[0]->name,
+ (input_devs->count > 1) ? "..." : "");
+
+ ds->ws = wakeup_source_register(wlname);
+ kfree(wlname);
+ if (!ds->ws) {
+ ret = -ENOMEM;
+ pr_err("gpio_event_input_func: "
+ "Failed to allocate wakeup source\n");
+ goto err_ws_failed;
+ }
+
+ spin_lock_init(&ds->irq_lock);
+
+ for (i = 0; i < di->keymap_size; i++) {
+ int dev = di->keymap[i].dev;
+ if (dev >= input_devs->count) {
+ pr_err("gpio_event_input_func: bad device "
+ "index %d >= %d for key code %d\n",
+ dev, input_devs->count,
+ di->keymap[i].code);
+ ret = -EINVAL;
+ goto err_bad_keymap;
+ }
+ input_set_capability(input_devs->dev[dev], di->type,
+ di->keymap[i].code);
+ ds->key_state[i].ds = ds;
+ ds->key_state[i].debounce = DEBOUNCE_UNKNOWN;
+ }
+
+ for (i = 0; i < di->keymap_size; i++) {
+ ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in");
+ if (ret) {
+ pr_err("gpio_event_input_func: gpio_request "
+ "failed for %d\n", di->keymap[i].gpio);
+ goto err_gpio_request_failed;
+ }
+ ret = gpio_direction_input(di->keymap[i].gpio);
+ if (ret) {
+ pr_err("gpio_event_input_func: "
+ "gpio_direction_input failed for %d\n",
+ di->keymap[i].gpio);
+ goto err_gpio_configure_failed;
+ }
+ }
+
+ ret = gpio_event_input_request_irqs(ds);
+
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ ds->use_irq = ret == 0;
+
+ pr_info("GPIO Input Driver: Start gpio inputs for %s%s in %s "
+ "mode\n", input_devs->dev[0]->name,
+ (input_devs->count > 1) ? "..." : "",
+ ret == 0 ? "interrupt" : "polling");
+
+ hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ds->timer.function = gpio_event_input_timer_func;
+ hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+ return 0;
+ }
+
+ ret = 0;
+ spin_lock_irqsave(&ds->irq_lock, irqflags);
+ hrtimer_cancel(&ds->timer);
+ if (ds->use_irq) {
+ for (i = di->keymap_size - 1; i >= 0; i--) {
+ int irq = gpio_to_irq(di->keymap[i].gpio);
+ if (ds->info->info.no_suspend)
+ disable_irq_wake(irq);
+ free_irq(irq, &ds->key_state[i]);
+ }
+ }
+ spin_unlock_irqrestore(&ds->irq_lock, irqflags);
+
+ for (i = di->keymap_size - 1; i >= 0; i--) {
+err_gpio_configure_failed:
+ gpio_free(di->keymap[i].gpio);
+err_gpio_request_failed:
+ ;
+ }
+err_bad_keymap:
+ wakeup_source_unregister(ds->ws);
+err_ws_failed:
+ kfree(ds);
+err_ds_alloc_failed:
+ return ret;
+}
diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c
new file mode 100644
index 00000000..eaa9e89d
--- /dev/null
+++ b/drivers/input/misc/gpio_matrix.c
@@ -0,0 +1,441 @@
+/* drivers/input/misc/gpio_matrix.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/wakelock.h>
+
+struct gpio_kp {
+ struct gpio_event_input_devs *input_devs;
+ struct gpio_event_matrix_info *keypad_info;
+ struct hrtimer timer;
+ struct wake_lock wake_lock;
+ int current_output;
+ unsigned int use_irq:1;
+ unsigned int key_state_changed:1;
+ unsigned int last_key_state_changed:1;
+ unsigned int some_keys_pressed:2;
+ unsigned int disabled_irq:1;
+ unsigned long keys_pressed[0];
+};
+
+static void clear_phantom_key(struct gpio_kp *kp, int out, int in)
+{
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ int key_index = out * mi->ninputs + in;
+ unsigned short keyentry = mi->keymap[key_index];
+ unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+ unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+
+ if (!test_bit(keycode, kp->input_devs->dev[dev]->key)) {
+ if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
+ pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
+ "cleared\n", keycode, out, in,
+ mi->output_gpios[out], mi->input_gpios[in]);
+ __clear_bit(key_index, kp->keys_pressed);
+ } else {
+ if (mi->flags & GPIOKPF_PRINT_PHANTOM_KEYS)
+ pr_info("gpiomatrix: phantom key %x, %d-%d (%d-%d) "
+ "not cleared\n", keycode, out, in,
+ mi->output_gpios[out], mi->input_gpios[in]);
+ }
+}
+
+static int restore_keys_for_input(struct gpio_kp *kp, int out, int in)
+{
+ int rv = 0;
+ int key_index;
+
+ key_index = out * kp->keypad_info->ninputs + in;
+ while (out < kp->keypad_info->noutputs) {
+ if (test_bit(key_index, kp->keys_pressed)) {
+ rv = 1;
+ clear_phantom_key(kp, out, in);
+ }
+ key_index += kp->keypad_info->ninputs;
+ out++;
+ }
+ return rv;
+}
+
+static void remove_phantom_keys(struct gpio_kp *kp)
+{
+ int out, in, inp;
+ int key_index;
+
+ if (kp->some_keys_pressed < 3)
+ return;
+
+ for (out = 0; out < kp->keypad_info->noutputs; out++) {
+ inp = -1;
+ key_index = out * kp->keypad_info->ninputs;
+ for (in = 0; in < kp->keypad_info->ninputs; in++, key_index++) {
+ if (test_bit(key_index, kp->keys_pressed)) {
+ if (inp == -1) {
+ inp = in;
+ continue;
+ }
+ if (inp >= 0) {
+ if (!restore_keys_for_input(kp, out + 1,
+ inp))
+ break;
+ clear_phantom_key(kp, out, inp);
+ inp = -2;
+ }
+ restore_keys_for_input(kp, out, in);
+ }
+ }
+ }
+}
+
+static void report_key(struct gpio_kp *kp, int key_index, int out, int in)
+{
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ int pressed = test_bit(key_index, kp->keys_pressed);
+ unsigned short keyentry = mi->keymap[key_index];
+ unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+ unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+
+ if (pressed != test_bit(keycode, kp->input_devs->dev[dev]->key)) {
+ if (keycode == KEY_RESERVED) {
+ if (mi->flags & GPIOKPF_PRINT_UNMAPPED_KEYS)
+ pr_info("gpiomatrix: unmapped key, %d-%d "
+ "(%d-%d) changed to %d\n",
+ out, in, mi->output_gpios[out],
+ mi->input_gpios[in], pressed);
+ } else {
+ if (mi->flags & GPIOKPF_PRINT_MAPPED_KEYS)
+ pr_info("gpiomatrix: key %x, %d-%d (%d-%d) "
+ "changed to %d\n", keycode,
+ out, in, mi->output_gpios[out],
+ mi->input_gpios[in], pressed);
+ input_report_key(kp->input_devs->dev[dev], keycode, pressed);
+ }
+ }
+}
+
+static void report_sync(struct gpio_kp *kp)
+{
+ int i;
+
+ for (i = 0; i < kp->input_devs->count; i++)
+ input_sync(kp->input_devs->dev[i]);
+}
+
+static enum hrtimer_restart gpio_keypad_timer_func(struct hrtimer *timer)
+{
+ int out, in;
+ int key_index;
+ int gpio;
+ struct gpio_kp *kp = container_of(timer, struct gpio_kp, timer);
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ unsigned gpio_keypad_flags = mi->flags;
+ unsigned polarity = !!(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH);
+
+ out = kp->current_output;
+ if (out == mi->noutputs) {
+ out = 0;
+ kp->last_key_state_changed = kp->key_state_changed;
+ kp->key_state_changed = 0;
+ kp->some_keys_pressed = 0;
+ } else {
+ key_index = out * mi->ninputs;
+ for (in = 0; in < mi->ninputs; in++, key_index++) {
+ gpio = mi->input_gpios[in];
+ if (gpio_get_value(gpio) ^ !polarity) {
+ if (kp->some_keys_pressed < 3)
+ kp->some_keys_pressed++;
+ kp->key_state_changed |= !__test_and_set_bit(
+ key_index, kp->keys_pressed);
+ } else
+ kp->key_state_changed |= __test_and_clear_bit(
+ key_index, kp->keys_pressed);
+ }
+ gpio = mi->output_gpios[out];
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(gpio, !polarity);
+ else
+ gpio_direction_input(gpio);
+ out++;
+ }
+ kp->current_output = out;
+ if (out < mi->noutputs) {
+ gpio = mi->output_gpios[out];
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(gpio, polarity);
+ else
+ gpio_direction_output(gpio, polarity);
+ hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+ if (gpio_keypad_flags & GPIOKPF_DEBOUNCE) {
+ if (kp->key_state_changed) {
+ hrtimer_start(&kp->timer, mi->debounce_delay,
+ HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+ kp->key_state_changed = kp->last_key_state_changed;
+ }
+ if (kp->key_state_changed) {
+ if (gpio_keypad_flags & GPIOKPF_REMOVE_SOME_PHANTOM_KEYS)
+ remove_phantom_keys(kp);
+ key_index = 0;
+ for (out = 0; out < mi->noutputs; out++)
+ for (in = 0; in < mi->ninputs; in++, key_index++)
+ report_key(kp, key_index, out, in);
+ report_sync(kp);
+ }
+ if (!kp->use_irq || kp->some_keys_pressed) {
+ hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+ }
+
+ /* No keys are pressed, reenable interrupt */
+ for (out = 0; out < mi->noutputs; out++) {
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(mi->output_gpios[out], polarity);
+ else
+ gpio_direction_output(mi->output_gpios[out], polarity);
+ }
+ for (in = 0; in < mi->ninputs; in++)
+ enable_irq(gpio_to_irq(mi->input_gpios[in]));
+ wake_unlock(&kp->wake_lock);
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t gpio_keypad_irq_handler(int irq_in, void *dev_id)
+{
+ int i;
+ struct gpio_kp *kp = dev_id;
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+ unsigned gpio_keypad_flags = mi->flags;
+
+ if (!kp->use_irq) {
+ /* ignore interrupt while registering the handler */
+ kp->disabled_irq = 1;
+ disable_irq_nosync(irq_in);
+ return IRQ_HANDLED;
+ }
+
+ for (i = 0; i < mi->ninputs; i++)
+ disable_irq_nosync(gpio_to_irq(mi->input_gpios[i]));
+ for (i = 0; i < mi->noutputs; i++) {
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set_value(mi->output_gpios[i],
+ !(gpio_keypad_flags & GPIOKPF_ACTIVE_HIGH));
+ else
+ gpio_direction_input(mi->output_gpios[i]);
+ }
+ wake_lock(&kp->wake_lock);
+ hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+ return IRQ_HANDLED;
+}
+
+static int gpio_keypad_request_irqs(struct gpio_kp *kp)
+{
+ int i;
+ int err;
+ unsigned int irq;
+ unsigned long request_flags;
+ struct gpio_event_matrix_info *mi = kp->keypad_info;
+
+ switch (mi->flags & (GPIOKPF_ACTIVE_HIGH|GPIOKPF_LEVEL_TRIGGERED_IRQ)) {
+ default:
+ request_flags = IRQF_TRIGGER_FALLING;
+ break;
+ case GPIOKPF_ACTIVE_HIGH:
+ request_flags = IRQF_TRIGGER_RISING;
+ break;
+ case GPIOKPF_LEVEL_TRIGGERED_IRQ:
+ request_flags = IRQF_TRIGGER_LOW;
+ break;
+ case GPIOKPF_LEVEL_TRIGGERED_IRQ | GPIOKPF_ACTIVE_HIGH:
+ request_flags = IRQF_TRIGGER_HIGH;
+ break;
+ }
+
+ for (i = 0; i < mi->ninputs; i++) {
+ err = irq = gpio_to_irq(mi->input_gpios[i]);
+ if (err < 0)
+ goto err_gpio_get_irq_num_failed;
+ err = request_irq(irq, gpio_keypad_irq_handler, request_flags,
+ "gpio_kp", kp);
+ if (err) {
+ pr_err("gpiomatrix: request_irq failed for input %d, "
+ "irq %d\n", mi->input_gpios[i], irq);
+ goto err_request_irq_failed;
+ }
+ err = enable_irq_wake(irq);
+ if (err) {
+ pr_err("gpiomatrix: set_irq_wake failed for input %d, "
+ "irq %d\n", mi->input_gpios[i], irq);
+ }
+ disable_irq(irq);
+ if (kp->disabled_irq) {
+ kp->disabled_irq = 0;
+ enable_irq(irq);
+ }
+ }
+ return 0;
+
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+ free_irq(gpio_to_irq(mi->input_gpios[i]), kp);
+err_request_irq_failed:
+err_gpio_get_irq_num_failed:
+ ;
+ }
+ return err;
+}
+
+int gpio_event_matrix_func(struct gpio_event_input_devs *input_devs,
+ struct gpio_event_info *info, void **data, int func)
+{
+ int i;
+ int err;
+ int key_count;
+ struct gpio_kp *kp;
+ struct gpio_event_matrix_info *mi;
+
+ mi = container_of(info, struct gpio_event_matrix_info, info);
+ if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME) {
+ /* TODO: disable scanning */
+ return 0;
+ }
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ if (mi->keymap == NULL ||
+ mi->input_gpios == NULL ||
+ mi->output_gpios == NULL) {
+ err = -ENODEV;
+ pr_err("gpiomatrix: Incomplete pdata\n");
+ goto err_invalid_platform_data;
+ }
+ key_count = mi->ninputs * mi->noutputs;
+
+ *data = kp = kzalloc(sizeof(*kp) + sizeof(kp->keys_pressed[0]) *
+ BITS_TO_LONGS(key_count), GFP_KERNEL);
+ if (kp == NULL) {
+ err = -ENOMEM;
+ pr_err("gpiomatrix: Failed to allocate private data\n");
+ goto err_kp_alloc_failed;
+ }
+ kp->input_devs = input_devs;
+ kp->keypad_info = mi;
+ for (i = 0; i < key_count; i++) {
+ unsigned short keyentry = mi->keymap[i];
+ unsigned short keycode = keyentry & MATRIX_KEY_MASK;
+ unsigned short dev = keyentry >> MATRIX_CODE_BITS;
+ if (dev >= input_devs->count) {
+ pr_err("gpiomatrix: bad device index %d >= "
+ "%d for key code %d\n",
+ dev, input_devs->count, keycode);
+ err = -EINVAL;
+ goto err_bad_keymap;
+ }
+ if (keycode && keycode <= KEY_MAX)
+ input_set_capability(input_devs->dev[dev],
+ EV_KEY, keycode);
+ }
+
+ for (i = 0; i < mi->noutputs; i++) {
+ err = gpio_request(mi->output_gpios[i], "gpio_kp_out");
+ if (err) {
+ pr_err("gpiomatrix: gpio_request failed for "
+ "output %d\n", mi->output_gpios[i]);
+ goto err_request_output_gpio_failed;
+ }
+ if (gpio_cansleep(mi->output_gpios[i])) {
+ pr_err("gpiomatrix: unsupported output gpio %d,"
+ " can sleep\n", mi->output_gpios[i]);
+ err = -EINVAL;
+ goto err_output_gpio_configure_failed;
+ }
+ if (mi->flags & GPIOKPF_DRIVE_INACTIVE)
+ err = gpio_direction_output(mi->output_gpios[i],
+ !(mi->flags & GPIOKPF_ACTIVE_HIGH));
+ else
+ err = gpio_direction_input(mi->output_gpios[i]);
+ if (err) {
+ pr_err("gpiomatrix: gpio_configure failed for "
+ "output %d\n", mi->output_gpios[i]);
+ goto err_output_gpio_configure_failed;
+ }
+ }
+ for (i = 0; i < mi->ninputs; i++) {
+ err = gpio_request(mi->input_gpios[i], "gpio_kp_in");
+ if (err) {
+ pr_err("gpiomatrix: gpio_request failed for "
+ "input %d\n", mi->input_gpios[i]);
+ goto err_request_input_gpio_failed;
+ }
+ err = gpio_direction_input(mi->input_gpios[i]);
+ if (err) {
+ pr_err("gpiomatrix: gpio_direction_input failed"
+ " for input %d\n", mi->input_gpios[i]);
+ goto err_gpio_direction_input_failed;
+ }
+ }
+ kp->current_output = mi->noutputs;
+ kp->key_state_changed = 1;
+
+ hrtimer_init(&kp->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ kp->timer.function = gpio_keypad_timer_func;
+ wake_lock_init(&kp->wake_lock, WAKE_LOCK_SUSPEND, "gpio_kp");
+ err = gpio_keypad_request_irqs(kp);
+ kp->use_irq = err == 0;
+
+ pr_info("GPIO Matrix Keypad Driver: Start keypad matrix for "
+ "%s%s in %s mode\n", input_devs->dev[0]->name,
+ (input_devs->count > 1) ? "..." : "",
+ kp->use_irq ? "interrupt" : "polling");
+
+ if (kp->use_irq)
+ wake_lock(&kp->wake_lock);
+ hrtimer_start(&kp->timer, ktime_set(0, 0), HRTIMER_MODE_REL);
+
+ return 0;
+ }
+
+ err = 0;
+ kp = *data;
+
+ if (kp->use_irq)
+ for (i = mi->noutputs - 1; i >= 0; i--)
+ free_irq(gpio_to_irq(mi->input_gpios[i]), kp);
+
+ hrtimer_cancel(&kp->timer);
+ wake_lock_destroy(&kp->wake_lock);
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+err_gpio_direction_input_failed:
+ gpio_free(mi->input_gpios[i]);
+err_request_input_gpio_failed:
+ ;
+ }
+ for (i = mi->noutputs - 1; i >= 0; i--) {
+err_output_gpio_configure_failed:
+ gpio_free(mi->output_gpios[i]);
+err_request_output_gpio_failed:
+ ;
+ }
+err_bad_keymap:
+ kfree(kp);
+err_kp_alloc_failed:
+err_invalid_platform_data:
+ return err;
+}
diff --git a/drivers/input/misc/gpio_output.c b/drivers/input/misc/gpio_output.c
new file mode 100644
index 00000000..2aac2fad
--- /dev/null
+++ b/drivers/input/misc/gpio_output.c
@@ -0,0 +1,97 @@
+/* drivers/input/misc/gpio_output.c
+ *
+ * Copyright (C) 2007 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+
+int gpio_event_output_event(
+ struct gpio_event_input_devs *input_devs, struct gpio_event_info *info,
+ void **data, unsigned int dev, unsigned int type,
+ unsigned int code, int value)
+{
+ int i;
+ struct gpio_event_output_info *oi;
+ oi = container_of(info, struct gpio_event_output_info, info);
+ if (type != oi->type)
+ return 0;
+ if (!(oi->flags & GPIOEDF_ACTIVE_HIGH))
+ value = !value;
+ for (i = 0; i < oi->keymap_size; i++)
+ if (dev == oi->keymap[i].dev && code == oi->keymap[i].code)
+ gpio_set_value(oi->keymap[i].gpio, value);
+ return 0;
+}
+
+int gpio_event_output_func(
+ struct gpio_event_input_devs *input_devs, struct gpio_event_info *info,
+ void **data, int func)
+{
+ int ret;
+ int i;
+ struct gpio_event_output_info *oi;
+ oi = container_of(info, struct gpio_event_output_info, info);
+
+ if (func == GPIO_EVENT_FUNC_SUSPEND || func == GPIO_EVENT_FUNC_RESUME)
+ return 0;
+
+ if (func == GPIO_EVENT_FUNC_INIT) {
+ int output_level = !(oi->flags & GPIOEDF_ACTIVE_HIGH);
+
+ for (i = 0; i < oi->keymap_size; i++) {
+ int dev = oi->keymap[i].dev;
+ if (dev >= input_devs->count) {
+ pr_err("gpio_event_output_func: bad device "
+ "index %d >= %d for key code %d\n",
+ dev, input_devs->count,
+ oi->keymap[i].code);
+ ret = -EINVAL;
+ goto err_bad_keymap;
+ }
+ input_set_capability(input_devs->dev[dev], oi->type,
+ oi->keymap[i].code);
+ }
+
+ for (i = 0; i < oi->keymap_size; i++) {
+ ret = gpio_request(oi->keymap[i].gpio,
+ "gpio_event_output");
+ if (ret) {
+ pr_err("gpio_event_output_func: gpio_request "
+ "failed for %d\n", oi->keymap[i].gpio);
+ goto err_gpio_request_failed;
+ }
+ ret = gpio_direction_output(oi->keymap[i].gpio,
+ output_level);
+ if (ret) {
+ pr_err("gpio_event_output_func: "
+ "gpio_direction_output failed for %d\n",
+ oi->keymap[i].gpio);
+ goto err_gpio_direction_output_failed;
+ }
+ }
+ return 0;
+ }
+
+ ret = 0;
+ for (i = oi->keymap_size - 1; i >= 0; i--) {
+err_gpio_direction_output_failed:
+ gpio_free(oi->keymap[i].gpio);
+err_gpio_request_failed:
+ ;
+ }
+err_bad_keymap:
+ return ret;
+}
+
diff --git a/drivers/input/misc/gpio_tilt_polled.c b/drivers/input/misc/gpio_tilt_polled.c
new file mode 100644
index 00000000..da05cca8
--- /dev/null
+++ b/drivers/input/misc/gpio_tilt_polled.c
@@ -0,0 +1,213 @@
+/*
+ * Driver for tilt switches connected via GPIO lines
+ * not capable of generating interrupts
+ *
+ * Copyright (C) 2011 Heiko Stuebner <heiko@sntech.de>
+ *
+ * based on: drivers/input/keyboard/gpio_keys_polled.c
+ *
+ * Copyright (C) 2007-2010 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2010 Nuno Goncalves <nunojpg@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/input/gpio_tilt.h>
+
+#define DRV_NAME "gpio-tilt-polled"
+
+struct gpio_tilt_polled_dev {
+ struct input_polled_dev *poll_dev;
+ struct device *dev;
+ const struct gpio_tilt_platform_data *pdata;
+
+ int last_state;
+
+ int threshold;
+ int count;
+};
+
+static void gpio_tilt_polled_poll(struct input_polled_dev *dev)
+{
+ struct gpio_tilt_polled_dev *tdev = dev->private;
+ const struct gpio_tilt_platform_data *pdata = tdev->pdata;
+ struct input_dev *input = dev->input;
+ struct gpio_tilt_state *tilt_state = NULL;
+ int state, i;
+
+ if (tdev->count < tdev->threshold) {
+ tdev->count++;
+ } else {
+ state = 0;
+ for (i = 0; i < pdata->nr_gpios; i++)
+ state |= (!!gpio_get_value(pdata->gpios[i].gpio) << i);
+
+ if (state != tdev->last_state) {
+ for (i = 0; i < pdata->nr_states; i++)
+ if (pdata->states[i].gpios == state)
+ tilt_state = &pdata->states[i];
+
+ if (tilt_state) {
+ for (i = 0; i < pdata->nr_axes; i++)
+ input_report_abs(input,
+ pdata->axes[i].axis,
+ tilt_state->axes[i]);
+
+ input_sync(input);
+ }
+
+ tdev->count = 0;
+ tdev->last_state = state;
+ }
+ }
+}
+
+static void gpio_tilt_polled_open(struct input_polled_dev *dev)
+{
+ struct gpio_tilt_polled_dev *tdev = dev->private;
+ const struct gpio_tilt_platform_data *pdata = tdev->pdata;
+
+ if (pdata->enable)
+ pdata->enable(tdev->dev);
+
+ /* report initial state of the axes */
+ tdev->last_state = -1;
+ tdev->count = tdev->threshold;
+ gpio_tilt_polled_poll(tdev->poll_dev);
+}
+
+static void gpio_tilt_polled_close(struct input_polled_dev *dev)
+{
+ struct gpio_tilt_polled_dev *tdev = dev->private;
+ const struct gpio_tilt_platform_data *pdata = tdev->pdata;
+
+ if (pdata->disable)
+ pdata->disable(tdev->dev);
+}
+
+static int gpio_tilt_polled_probe(struct platform_device *pdev)
+{
+ const struct gpio_tilt_platform_data *pdata = pdev->dev.platform_data;
+ struct device *dev = &pdev->dev;
+ struct gpio_tilt_polled_dev *tdev;
+ struct input_polled_dev *poll_dev;
+ struct input_dev *input;
+ int error, i;
+
+ if (!pdata || !pdata->poll_interval)
+ return -EINVAL;
+
+ tdev = kzalloc(sizeof(struct gpio_tilt_polled_dev), GFP_KERNEL);
+ if (!tdev) {
+ dev_err(dev, "no memory for private data\n");
+ return -ENOMEM;
+ }
+
+ error = gpio_request_array(pdata->gpios, pdata->nr_gpios);
+ if (error) {
+ dev_err(dev,
+ "Could not request tilt GPIOs: %d\n", error);
+ goto err_free_tdev;
+ }
+
+ poll_dev = input_allocate_polled_device();
+ if (!poll_dev) {
+ dev_err(dev, "no memory for polled device\n");
+ error = -ENOMEM;
+ goto err_free_gpios;
+ }
+
+ poll_dev->private = tdev;
+ poll_dev->poll = gpio_tilt_polled_poll;
+ poll_dev->poll_interval = pdata->poll_interval;
+ poll_dev->open = gpio_tilt_polled_open;
+ poll_dev->close = gpio_tilt_polled_close;
+
+ input = poll_dev->input;
+
+ input->name = pdev->name;
+ input->phys = DRV_NAME"/input0";
+ input->dev.parent = &pdev->dev;
+
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0001;
+ input->id.version = 0x0100;
+
+ __set_bit(EV_ABS, input->evbit);
+ for (i = 0; i < pdata->nr_axes; i++)
+ input_set_abs_params(input, pdata->axes[i].axis,
+ pdata->axes[i].min, pdata->axes[i].max,
+ pdata->axes[i].fuzz, pdata->axes[i].flat);
+
+ tdev->threshold = DIV_ROUND_UP(pdata->debounce_interval,
+ pdata->poll_interval);
+
+ tdev->poll_dev = poll_dev;
+ tdev->dev = dev;
+ tdev->pdata = pdata;
+
+ error = input_register_polled_device(poll_dev);
+ if (error) {
+ dev_err(dev, "unable to register polled device, err=%d\n",
+ error);
+ goto err_free_polldev;
+ }
+
+ platform_set_drvdata(pdev, tdev);
+
+ return 0;
+
+err_free_polldev:
+ input_free_polled_device(poll_dev);
+err_free_gpios:
+ gpio_free_array(pdata->gpios, pdata->nr_gpios);
+err_free_tdev:
+ kfree(tdev);
+
+ return error;
+}
+
+static int gpio_tilt_polled_remove(struct platform_device *pdev)
+{
+ struct gpio_tilt_polled_dev *tdev = platform_get_drvdata(pdev);
+ const struct gpio_tilt_platform_data *pdata = tdev->pdata;
+
+ platform_set_drvdata(pdev, NULL);
+
+ input_unregister_polled_device(tdev->poll_dev);
+ input_free_polled_device(tdev->poll_dev);
+
+ gpio_free_array(pdata->gpios, pdata->nr_gpios);
+
+ kfree(tdev);
+
+ return 0;
+}
+
+static struct platform_driver gpio_tilt_polled_driver = {
+ .probe = gpio_tilt_polled_probe,
+ .remove = gpio_tilt_polled_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(gpio_tilt_polled_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("Polled GPIO tilt driver");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/input/misc/headset.c b/drivers/input/misc/headset.c
new file mode 100644
index 00000000..9a46670e
--- /dev/null
+++ b/drivers/input/misc/headset.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2012 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <mach/gpio.h>
+#include <linux/headset.h>
+#include <mach/board.h>
+
+#ifndef HEADSET_DETECT_GPIO
+#define HEADSET_DETECT_GPIO 165
+#endif
+#ifndef HEADSET_BUTTON_GPIO
+#define HEADSET_BUTTON_GPIO 164
+#endif
+
+#ifndef HEADSET_DETECT_GPIO_ACTIVE_LOW
+#define HEADSET_DETECT_GPIO_ACTIVE_LOW 1
+#endif
+#ifndef HEADSET_BUTTON_GPIO_ACTIVE_LOW
+#define HEADSET_BUTTON_GPIO_ACTIVE_LOW 0
+#endif
+
+#ifndef HEADSET_DETECT_GPIO_DEBOUNCE_SW
+#define HEADSET_DETECT_GPIO_DEBOUNCE_SW 1000
+#endif
+#ifndef HEADSET_BUTTON_GPIO_DEBOUNCE_SW
+#define HEADSET_BUTTON_GPIO_DEBOUNCE_SW 100
+#endif
+
+static enum hrtimer_restart report_headset_button_status(int active, struct _headset_gpio *hgp);
+static enum hrtimer_restart report_headset_detect_status(int active, struct _headset_gpio *hgp);
+static struct _headset headset = {
+ .sdev = {
+ .name = "h2w",
+ },
+ .detect = {
+ .desc = "headset detect",
+ .active_low = HEADSET_DETECT_GPIO_ACTIVE_LOW,
+ .gpio = HEADSET_DETECT_GPIO,
+ .debounce = 0,
+ .debounce_sw = HEADSET_DETECT_GPIO_DEBOUNCE_SW,
+ .irq_enabled = 1,
+ .callback = report_headset_detect_status,
+ },
+ .button = {
+ .desc = "headset button",
+ .active_low = HEADSET_BUTTON_GPIO_ACTIVE_LOW,
+ .gpio = HEADSET_BUTTON_GPIO,
+ .debounce = 0,
+ .debounce_sw = HEADSET_BUTTON_GPIO_DEBOUNCE_SW,
+ .irq_enabled = 1,
+ .callback = report_headset_button_status,
+ .timeout_ms = 800, /* 800ms for long button down */
+ },
+};
+
+
+#ifndef headset_gpio_init
+#define headset_gpio_init(gpio, desc) \
+ do { \
+ gpio_request(gpio, desc); \
+ gpio_direction_input(gpio); \
+ } while (0)
+#endif
+
+#ifndef headset_gpio_free
+#define headset_gpio_free(gpio) \
+ gpio_free(gpio)
+#endif
+
+#ifndef headset_gpio2irq_free
+#define headset_gpio2irq_free(irq, args) { }
+#endif
+
+#ifndef headset_gpio2irq
+#define headset_gpio2irq(gpio) \
+ gpio_to_irq(gpio)
+#endif
+
+#ifndef headset_gpio_set_irq_type
+#define headset_gpio_set_irq_type(irq, type) \
+ irq_set_irq_type(irq, type)
+#endif
+
+#ifndef headset_gpio_get_value
+#define headset_gpio_get_value(gpio) \
+ gpio_get_value(gpio)
+#endif
+
+#ifndef headset_gpio_debounce
+#define headset_gpio_debounce(gpio, ms) \
+ gpio_set_debounce(gpio, ms)
+#endif
+
+#ifndef headset_hook_detect
+#define headset_hook_detect(status) { }
+#endif
+
+#define HEADSET_DEBOUNCE_ROUND_UP(dw) \
+ dw = (((dw ? dw : 1) + HEADSET_GPIO_DEBOUNCE_SW_SAMPLE_PERIOD - 1) / \
+ HEADSET_GPIO_DEBOUNCE_SW_SAMPLE_PERIOD) * HEADSET_GPIO_DEBOUNCE_SW_SAMPLE_PERIOD;
+
+static struct _headset_keycap headset_key_capability[20] = {
+ { EV_KEY, KEY_MEDIA },
+ { EV_KEY, KEY_END },
+ { EV_KEY, KEY_RESERVED },
+};
+
+static unsigned int (*headset_get_button_code_board_method)(int v);
+static unsigned int (*headset_map_code2push_code_board_method)(unsigned int code, int push_type);
+static __devinit int headset_button_probe(struct platform_device *pdev)
+{
+ struct _headset_button *headset_button = platform_get_drvdata(pdev);
+ headset_get_button_code_board_method = headset_button->headset_get_button_code_board_method;
+ headset_map_code2push_code_board_method = headset_button->headset_map_code2push_code_board_method;
+ memcpy(headset_key_capability, headset_button->cap, sizeof headset_button->cap);
+ return 0;
+}
+
+static struct platform_driver headset_button_driver = {
+ .driver = {
+ .name = "headset-button",
+ .owner = THIS_MODULE,
+ },
+ .probe = headset_button_probe,
+};
+
+static unsigned int headset_get_button_code(int v)
+{
+ unsigned int code;
+ if (headset_get_button_code_board_method)
+ code = headset_get_button_code_board_method(v);
+ else
+ code = KEY_MEDIA;
+ return code;
+}
+
+static unsigned int headset_map_code2key_type(unsigned int code)
+{
+ unsigned int key_type = EV_KEY;
+ int i;
+ for(i = 0; headset_key_capability[i].key != KEY_RESERVED &&
+ headset_key_capability[i].key != code && i < ARRY_SIZE(headset_key_capability); i++);
+ if (i < ARRY_SIZE(headset_key_capability) &&
+ headset_key_capability[i].key == code)
+ key_type = headset_key_capability[i].type;
+ else
+ pr_err("headset not find code [0x%x]'s maping type\n", code);
+ return key_type;
+}
+
+static unsigned int headset_map_code2push_code(unsigned int code, int push_type)
+{
+ if (headset_map_code2push_code_board_method)
+ return headset_map_code2push_code_board_method(code, push_type);
+
+ switch (push_type) {
+ case HEADSET_BUTTON_DOWN_SHORT:
+ code = KEY_MEDIA;
+ break;
+ case HEADSET_BUTTON_DOWN_LONG:
+ code = KEY_END;
+ break;
+ }
+
+ return code;
+}
+
+static void headset_gpio_irq_enable(int enable, struct _headset_gpio *hgp);
+#define HEADSET_GPIO_DEBOUNCE_SW_SAMPLE_PERIOD 50 /* 10 */
+static enum hrtimer_restart report_headset_button_status(int active, struct _headset_gpio *hgp)
+{
+ enum hrtimer_restart restart;
+ int button_push_type = HEADSET_BUTTON_DOWN_INVALID;
+ unsigned int key_type;
+ static int pre_code = KEY_RESERVED;
+ static int step = 0;
+
+ if (active < 0) {
+ step = 0;
+ pre_code = KEY_RESERVED;
+ return HRTIMER_NORESTART;
+ }
+ if (active) {
+ restart = HRTIMER_RESTART;
+ if (++step > 3)
+ step = 0;
+ switch (step) {
+ case 1:
+ pre_code = headset_get_button_code(0);
+ break;
+ case 2:
+ button_push_type = HEADSET_BUTTON_DOWN_LONG;
+ break;
+ }
+ } else {
+ restart = HRTIMER_NORESTART;
+ if (step == 1)
+ button_push_type = HEADSET_BUTTON_DOWN_SHORT;
+ step = 0;
+ }
+
+ if (button_push_type != HEADSET_BUTTON_DOWN_INVALID) {
+ unsigned int code = headset_map_code2push_code(pre_code, button_push_type);
+ key_type = headset_map_code2key_type(code);
+ switch (key_type) {
+ case EV_KEY:
+ input_event(hgp->parent->input, key_type, code, 1);
+ input_sync(hgp->parent->input);
+ input_event(hgp->parent->input, key_type, code, 0);
+ input_sync(hgp->parent->input);
+ pr_info("headset button-%d[%dms]\n", code, hgp->holded);
+ break;
+ default:
+ pr_err("headset not support key type [%d]\n", key_type);
+ }
+ }
+ return restart;
+}
+
+static enum hrtimer_restart report_headset_detect_status(int active, struct _headset_gpio *hgp)
+{
+ struct _headset * ht = hgp->parent;
+
+ if (active) {
+ headset_hook_detect(1);
+ ht->headphone = 0;
+ /* ht->headphone = ht->button.active_low ^ headset_gpio_get_value(ht->button.gpio); */
+ if (ht->headphone) {
+ ht->type = BIT_HEADSET_NO_MIC;
+ queue_work(ht->switch_workqueue, &ht->switch_work);
+ pr_info("headphone plug in\n");
+ } else {
+ ht->type = BIT_HEADSET_MIC;
+ queue_work(ht->switch_workqueue, &ht->switch_work);
+ pr_info("headset plug in\n");
+ headset_gpio_set_irq_type(ht->button.irq, ht->button.irq_type_active);
+ headset_gpio_irq_enable(1, &ht->button);
+ }
+ } else {
+ headset_gpio_irq_enable(0, &ht->button);
+ ht->button.callback(-1, &ht->button);
+ headset_hook_detect(0);
+ if (ht->headphone)
+ pr_info("headphone plug out\n");
+ else {
+ pr_info("headset plug out\n");
+ }
+ ht->type = BIT_HEADSET_OUT;
+ queue_work(ht->switch_workqueue, &ht->switch_work);
+ }
+ /* use below code only when gpio irq misses state, because of the dithering */
+ headset_gpio_set_irq_type(hgp->irq, active ? hgp->irq_type_inactive : hgp->irq_type_active);
+ return HRTIMER_NORESTART;
+}
+
+static enum hrtimer_restart headset_gpio_timer_func(struct hrtimer *timer)
+{
+ enum hrtimer_restart restart = HRTIMER_RESTART;
+ struct _headset_gpio *hgp =
+ container_of(timer, struct _headset_gpio, timer);
+ int active = hgp->active_low ^ headset_gpio_get_value(hgp->gpio); /* hgp->active */
+ int green_ch = (!active && &hgp->parent->detect == hgp);
+ if (active != hgp->active) {
+ pr_info("The value %s mismatch [%d:%d] at %dms!\n",
+ hgp->desc, active, hgp->active, hgp->holded);
+ hgp->holded = 0;
+ }
+ pr_debug("%s : %s %s green_ch[%d], holed=%d, debounce_sw=%d\n", __func__,
+ hgp->desc, active ? "active" : "inactive", green_ch, hgp->holded, hgp->debounce_sw);
+ hgp->holded += HEADSET_GPIO_DEBOUNCE_SW_SAMPLE_PERIOD;
+ if (hgp->holded >= hgp->debounce_sw || green_ch) {
+ if (hgp->holded == hgp->debounce_sw || \
+ hgp->holded == hgp->timeout_ms || \
+ green_ch) {
+ pr_debug("call headset gpio handler\n");
+ restart = hgp->callback(active, hgp);
+ } else
+ pr_debug("gpio <%d> has kept active for %d ms\n", hgp->gpio, hgp->holded);
+ }
+ if (restart == HRTIMER_RESTART)
+ hrtimer_forward_now(timer,
+ ktime_set(HEADSET_GPIO_DEBOUNCE_SW_SAMPLE_PERIOD / 1000,
+ (HEADSET_GPIO_DEBOUNCE_SW_SAMPLE_PERIOD % 1000) * 1000000)); /* repeat timer */
+ return restart;
+}
+
+static irqreturn_t headset_gpio_irq_handler(int irq, void *dev)
+{
+ struct _headset_gpio *hgp = dev;
+ hrtimer_cancel(&hgp->timer);
+ hgp->active = hgp->active_low ^ headset_gpio_get_value(hgp->gpio);
+ headset_gpio_set_irq_type(hgp->irq, hgp->active ? hgp->irq_type_inactive : hgp->irq_type_active);
+ pr_debug("%s : %s %s\n", __func__, hgp->desc, hgp->active ? "active" : "inactive");
+ hgp->holded = 0;
+ hrtimer_start(&hgp->timer,
+ ktime_set(HEADSET_GPIO_DEBOUNCE_SW_SAMPLE_PERIOD / 1000,
+ (HEADSET_GPIO_DEBOUNCE_SW_SAMPLE_PERIOD % 1000) * 1000000),
+ HRTIMER_MODE_REL);
+ return IRQ_HANDLED;
+}
+
+static void headset_gpio_irq_enable(int enable, struct _headset_gpio *hgp)
+{
+ int action = 0;
+ if (enable) {
+ if (!hgp->irq_enabled) {
+ hrtimer_cancel(&hgp->timer);
+ hgp->irq_enabled = 1;
+ action = 1;
+ hgp->holded = 0;
+ enable_irq(hgp->irq);
+ }
+ } else {
+ if (hgp->irq_enabled) {
+ disable_irq(hgp->irq);
+ hrtimer_cancel(&hgp->timer);
+ hgp->irq_enabled = 0;
+ action = 1;
+ hgp->holded = 0;
+ }
+ }
+ pr_info("%s [ irq=%d ] --- %saction %s\n", __func__, hgp->irq_enabled, action ? "do " : "no ", hgp->desc);
+}
+
+static void headset_switch_state(struct work_struct *work)
+{
+ struct _headset *ht;
+ int type;
+
+ ht = container_of(work, struct _headset, switch_work);
+ type = ht->type;
+ switch_set_state(&headset.sdev, type);
+ pr_info("set headset state to %d\n", type);
+}
+
+static int __init headset_init(void)
+{
+ int ret, i;
+ struct _headset *ht = &headset;
+
+ ret = switch_dev_register(&ht->sdev);
+ if (ret < 0) {
+ pr_err("switch_dev_register failed!\n");
+ return ret;
+ }
+ platform_driver_register(&headset_button_driver);
+ ht->input = input_allocate_device();
+ if (ht->input == NULL) {
+ pr_err("switch_dev_register failed!\n");
+ goto _switch_dev_register;
+ }
+ ht->input->name = "headset-keyboard";
+ ht->input->id.bustype = BUS_HOST;
+ ht->input->id.vendor = 0x0001;
+ ht->input->id.product = 0x0001;
+ ht->input->id.version = 0x0100;
+
+ for(i = 0; headset_key_capability[i].key != KEY_RESERVED; i++) {
+ __set_bit(headset_key_capability[i].type, ht->input->evbit);
+ input_set_capability(ht->input, headset_key_capability[i].type, headset_key_capability[i].key);
+ }
+
+ if (input_register_device(ht->input))
+ goto _switch_dev_register;
+
+ headset_gpio_init(ht->detect.gpio, ht->detect.desc);
+ headset_gpio_init(ht->button.gpio, ht->button.desc);
+
+ headset_gpio_debounce(ht->detect.gpio, ht->detect.debounce * 1000);
+ headset_gpio_debounce(ht->button.gpio, ht->button.debounce * 1000);
+
+ hrtimer_init(&ht->button.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ht->button.timer.function = headset_gpio_timer_func;
+ HEADSET_DEBOUNCE_ROUND_UP(ht->button.debounce_sw);
+ HEADSET_DEBOUNCE_ROUND_UP(ht->button.timeout_ms);
+ ht->button.parent = ht;
+ ht->button.irq = headset_gpio2irq(ht->button.gpio);
+ ht->button.irq_type_active = ht->button.active_low ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH;
+ ht->button.irq_type_inactive = ht->button.active_low ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW;
+ ret = request_irq(ht->button.irq, headset_gpio_irq_handler,
+ ht->button.irq_type_active, ht->button.desc, &ht->button);
+ if (ret) {
+ pr_err("request_irq gpio %d's irq failed!\n", ht->button.gpio);
+ goto _gpio_request;
+ }
+ headset_gpio_irq_enable(0, &ht->button);
+
+ hrtimer_init(&ht->detect.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ht->detect.timer.function = headset_gpio_timer_func;
+ HEADSET_DEBOUNCE_ROUND_UP(ht->detect.debounce_sw);
+ ht->detect.parent = ht;
+ ht->detect.irq = headset_gpio2irq(ht->detect.gpio);
+ ht->detect.irq_type_active = ht->detect.active_low ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH;
+ ht->detect.irq_type_inactive = ht->detect.active_low ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW;
+ ret = request_irq(ht->detect.irq, headset_gpio_irq_handler,
+ ht->detect.irq_type_active, ht->detect.desc, &ht->detect);
+ if (ret) {
+ pr_err("request_irq gpio %d's irq failed!\n", ht->detect.gpio);
+ goto _headset_button_gpio_irq_handler;
+ }
+
+ INIT_WORK(&ht->switch_work, headset_switch_state);
+ ht->switch_workqueue = create_singlethread_workqueue("headset_switch");
+
+ if (ht->switch_workqueue == NULL) {
+ pr_err("can't create headset switch workqueue\n");
+ ret = -ENOMEM;
+ goto _headset_button_gpio_irq_handler;
+ }
+
+ return 0;
+
+ destroy_workqueue(ht->switch_workqueue);
+_headset_button_gpio_irq_handler:
+ free_irq(ht->button.irq, &ht->button);
+ headset_gpio2irq_free(ht->button.irq, &ht->button);
+_gpio_request:
+ headset_gpio_free(ht->detect.gpio);
+ headset_gpio_free(ht->button.gpio);
+ input_free_device(ht->input);
+_switch_dev_register:
+ platform_driver_unregister(&headset_button_driver);
+ switch_dev_unregister(&ht->sdev);
+ return ret;
+}
+module_init(headset_init);
+
+static void __exit headset_exit(void)
+{
+ struct _headset *ht = &headset;
+ destroy_workqueue(ht->switch_workqueue);
+ headset_gpio_irq_enable(0, &ht->button);
+ headset_gpio_irq_enable(0, &ht->detect);
+ free_irq(ht->detect.irq, &ht->detect);
+ headset_gpio2irq_free(ht->detect.irq, &ht->detect);
+ free_irq(ht->button.irq, &ht->button);
+ headset_gpio2irq_free(ht->button.irq, &ht->button);
+ headset_gpio_free(ht->detect.gpio);
+ headset_gpio_free(ht->button.gpio);
+ input_free_device(ht->input);
+ platform_driver_unregister(&headset_button_driver);
+ switch_dev_unregister(&ht->sdev);
+}
+module_exit(headset_exit);
+
+MODULE_DESCRIPTION("headset & button detect driver");
+MODULE_AUTHOR("Luther Ge <luther.ge@spreadtrum.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/headset_sprd.c b/drivers/input/misc/headset_sprd.c
new file mode 100644
index 00000000..db6d1f7d
--- /dev/null
+++ b/drivers/input/misc/headset_sprd.c
@@ -0,0 +1,2377 @@
+/*
+ * Copyright (C) 2013 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <mach/gpio.h>
+#include <linux/headset_sprd.h>
+#include <soc/sprd/board.h>
+#include <linux/input.h>
+
+#include <soc/sprd/adc.h>
+#include <asm/io.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/adi.h>
+#include <linux/module.h>
+#include <linux/wakelock.h>
+
+#include <linux/regulator/consumer.h>
+#include <soc/sprd/regulator.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/arch_misc.h>
+#include <linux/notifier.h>
+#ifdef CONFIG_OF
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#endif
+
+//==================== debug ====================
+#define ENTER \
+do{ if(debug_level >= 1) printk(KERN_INFO "[SPRD_HEADSET_DBG][%d] func: %s line: %04d\n", adie_type, __func__, __LINE__); }while(0)
+
+#define PRINT_DBG(format,x...) \
+do{ if(debug_level >= 1) printk(KERN_INFO "[SPRD_HEADSET_DBG][%d] " format, adie_type, ## x); }while(0)
+
+#define PRINT_INFO(format,x...) \
+do{ printk(KERN_INFO "[SPRD_HEADSET_INFO][%d] " format, adie_type, ## x); }while(0)
+
+#define PRINT_WARN(format,x...) \
+do{ printk(KERN_INFO "[SPRD_HEADSET_WARN][%d] " format, adie_type, ## x); }while(0)
+
+#define PRINT_ERR(format,x...) \
+do{ printk(KERN_ERR "[SPRD_HEADSET_ERR][%d] func: %s line: %04d info: " format, adie_type, __func__, __LINE__, ## x); }while(0)
+//==================== debug ====================
+
+/**********************************************
+ * The micro ADPGAR_BYP_SELECT is used for avoiding the Bug#298417,
+ * please refer to Bug#298417 to confirm your configuration.
+ **********************************************/
+#define ADPGAR_BYP_SELECT
+
+#ifdef CONFIG_HEADSET_STS_POLLING_DISABLED
+#undef SPRD_STS_POLLING_EN
+#else
+#define SPRD_STS_POLLING_EN
+#endif
+
+#define PLUG_CONFIRM_COUNT (2)
+#define NO_MIC_RETRY_COUNT (0)
+#define ADC_READ_COUNT (5)
+#define ADC_READ_LOOP (2)
+#define ADC_GND (100)
+#define SCI_ADC_GET_VALUE_COUNT (50)
+
+#define ARM_MF_REG (0x0110)
+#define HEAD_INSERT2_EIC_EN (BIT(15))
+#define HEAD_INSERT_EIC_EN (BIT(14))
+
+#define EIC3_DBNC_CTRL (0x4C)
+#define DBNC_CNT3_VALUE (30)
+#define DBNC_CNT3_SHIFT (0)
+#define DBNC_CNT3_MASK (0xFFF << DBNC_CNT3_SHIFT)
+
+#define EIC5_DBNC_CTRL (0x54)
+#define DBNC_CNT5_VALUE (100)
+#define DBNC_CNT5_SHIFT (0)
+#define DBNC_CNT5_MASK (0xFFF << DBNC_CNT5_SHIFT)
+
+#if (defined(CONFIG_ARCH_SCX15))
+ #define ADIE_CHID_LOW 0x0134
+ #define ADIE_CHID_HIGH 0x0138
+#else
+ #if (defined(CONFIG_ARCH_SCX35))
+ #define ADIE_CHID_LOW 0x0108
+ #define ADIE_CHID_HIGH 0x010C
+ #endif
+#endif
+
+#if (defined(CONFIG_ARCH_SCX15))
+ #define CLK_AUD_HID_EN (BIT(4))
+ #define CLK_AUD_HBD_EN (BIT(3))
+#else
+ #if (defined(CONFIG_ARCH_SCX35))
+ #define CLK_AUD_HID_EN (BIT(5))
+ #define CLK_AUD_HBD_EN (BIT(4))
+ #endif
+#endif
+
+#define HEADMIC_DETECT_BASE (ANA_AUDCFGA_INT_BASE)
+#define HEADMIC_DETECT_REG(X) (HEADMIC_DETECT_BASE + (X))
+
+#define HDT_EN (BIT(5))
+#define AUD_EN (BIT(4))
+
+#define HEADMIC_BUTTON_BASE (ANA_HDT_INT_BASE)
+#define HEADMIC_BUTTON_REG(X) (HEADMIC_BUTTON_BASE + (X))
+#define HID_CFG0 (0x0080)
+#define HID_CFG2 (0x0088)
+#define HID_CFG3 (0x008C)
+#define HID_CFG4 (0x0090)
+
+#define ANA_CFG0 (0x0040)
+#define ANA_CFG1 (0x0044)
+#define ANA_CFG2 (0x0048)
+#define ANA_CFG5 (0x0064)
+#define ANA_CFG20 (0x00A0)
+#define ANA_STS0 (0x00C0)
+
+#define HEADMIC_DETECT_GLB_REG(X) (ANA_REGS_GLB_BASE + (X))
+
+#define HID_TMR_T1T2_STEP_SHIFT (5)
+#define HID_TMR_T0_SHIFT (0)
+#define HID_TMR_T1_SHIFT (0)
+#define HID_TMR_T2_SHIFT (0)
+
+#define HID_TMR_T1T2_STEP_MASK (0x1F << HID_TMR_T1T2_STEP_SHIFT)
+#define HID_TMR_T0_MASK (0x1F << HID_TMR_T0_SHIFT)
+#define HID_TMR_T1_MASK (0xFFFF << HID_TMR_T1_SHIFT)
+#define HID_TMR_T2_MASK (0xFFFF << HID_TMR_T2_SHIFT)
+
+#define HID_TMR_T1T2_STEP_VAL (0x1)
+#define HID_TMR_T0_VAL (0xF)
+#define HID_TMR_T1_VAL (0xF)
+#define HID_TMR_T2_VAL (0xF)
+
+#define AUDIO_ICM_PLUS_EN (BIT(2))
+
+#define AUDIO_HEADMIC_SLEEP_EN (BIT(1))
+#define AUDIO_MICBIAS_HV_EN (BIT(2))
+#define AUDIO_MICBIAS_V_SHIFT (3)
+#define AUDIO_MICBIAS_V_MASK (0x3 << AUDIO_MICBIAS_V_SHIFT)
+#define AUDIO_MICBIAS_V_1P7_OR_2P2 (0)
+#define AUDIO_MICBIAS_V_1P9_OR_2P4 (1)
+#define AUDIO_MICBIAS_V_2P1_OR_2P7 (2)
+#define AUDIO_MICBIAS_V_2P3_OR_3P0 (3)
+
+#define AUDIO_ADPGAR_BYP_SHIFT (10)
+#define AUDIO_ADPGAR_BYP_MASK (0x3 << AUDIO_ADPGAR_BYP_SHIFT)
+#define AUDIO_ADPGAR_BYP_NORMAL (0)
+#define AUDIO_ADPGAR_BYP_ADC_PGAR1_2_ADCR (1)
+#define AUDIO_ADPGAR_BYP_HEADMIC_2_ADCR (2)
+#define AUDIO_ADPGAR_BYP_ALL_DISCONNECTED (3)
+
+#if (defined(CONFIG_ARCH_SCX15))
+ #define AUDIO_HEAD_SDET_SHIFT (4)
+#else
+ #if (defined(CONFIG_ARCH_SCX35))
+ #define AUDIO_HEAD_SDET_SHIFT (5)
+ #endif
+#endif
+#define AUDIO_HEAD_SDET_MASK (0x3 << AUDIO_HEAD_SDET_SHIFT)
+#define AUDIO_HEAD_SDET_2P1_OR_1P4 (3)
+#define AUDIO_HEAD_SDET_2P3_OR_1P5 (2)
+#define AUDIO_HEAD_SDET_2P5_OR_1P6 (1)
+#define AUDIO_HEAD_SDET_2P7_OR_1P7 (0)
+
+#if (defined(CONFIG_ARCH_SCX15))
+ #define AUDIO_HEAD_INS_VREF_SHIFT (7)
+#else
+ #if (defined(CONFIG_ARCH_SCX35))
+ #define AUDIO_HEAD_INS_VREF_SHIFT (8)
+ #endif
+#endif
+#define AUDIO_HEAD_INS_VREF_MASK (0x3 << AUDIO_HEAD_INS_VREF_SHIFT)
+#define AUDIO_HEAD_INS_VREF_2P1_OR_1P4 (3)
+#define AUDIO_HEAD_INS_VREF_2P3_OR_1P5 (2)
+#define AUDIO_HEAD_INS_VREF_2P5_OR_1P6 (1)
+#define AUDIO_HEAD_INS_VREF_2P7_OR_1P7 (0)
+
+#if (defined(CONFIG_ARCH_SCX15))
+ #define AUDIO_HEAD_SBUT_SHIFT (0)
+#else
+ #if (defined(CONFIG_ARCH_SCX35))
+ #define AUDIO_HEAD_SBUT_SHIFT (1)
+ #endif
+#endif
+#define AUDIO_HEAD_SBUT_MASK (0xF << AUDIO_HEAD_SBUT_SHIFT)
+
+#define AUDIO_HEADMIC_ADC_SEL (BIT(13))
+#define AUDIO_HEAD_INS_HMBIAS_EN (BIT(11))
+
+#if (defined(CONFIG_ARCH_SCX15))
+ #define AUDIO_V2ADC_EN (BIT(15))
+ #define AUDIO_HEAD_BUF_EN (BIT(14))
+ #define AUDIO_HEAD2ADC_SEL_SHIFT (12)
+ #define AUDIO_HEAD2ADC_SEL_MASK (0x3 << AUDIO_HEAD2ADC_SEL_SHIFT)
+ #define AUDIO_HEAD2ADC_SEL_DISABLE (0)
+ #define AUDIO_HEAD2ADC_SEL_MIC_IN (1)
+ #define AUDIO_HEAD2ADC_SEL_L_INT (2)
+ #define AUDIO_HEAD2ADC_SEL_DRO_L (3)
+#else
+ #if (defined(CONFIG_ARCH_SCX35))
+ #define AUDIO_V2ADC_EN (BIT(14))
+ #define AUDIO_HEAD_BUF_EN (BIT(15))
+ #define AUDIO_HEAD2ADC_SEL_SHIFT (13)
+ #define AUDIO_HEAD2ADC_SEL_MASK (0x1 << AUDIO_HEAD2ADC_SEL_SHIFT)
+ #define AUDIO_HEAD2ADC_SEL_DISABLE (0)
+ #define AUDIO_HEAD2ADC_SEL_MIC_IN (1)
+ #define AUDIO_HEAD2ADC_SEL_L_INT (0)
+ #define AUDIO_HEAD2ADC_SEL_DRO_L (0)
+ #endif
+#endif
+
+#define AUDIO_HEAD_BUTTON (BIT(7))
+#define AUDIO_HEAD_INSERT (BIT(6))
+#define AUDIO_HEAD_INSERT_2 (BIT(5))
+
+#define ABS(x) (((x) < (0)) ? (-(x)) : (x))
+#define MAX(x,y) (((x) > (y)) ? (x) : (y))
+
+#define headset_reg_read(addr) \
+ do { \
+ sci_adi_read(addr); \
+} while(0)
+
+#define headset_reg_set_val(addr, val, mask, shift) \
+ do { \
+ uint32_t temp; \
+ temp = sci_adi_read(addr); \
+ temp = (temp & (~mask)) | ((val) << (shift)); \
+ sci_adi_raw_write(addr, temp); \
+ } while(0)
+
+#define headset_reg_clr_bit(addr, bit) \
+ do { \
+ uint32_t temp; \
+ temp = sci_adi_read(addr); \
+ temp = temp & (~bit); \
+ sci_adi_raw_write(addr, temp); \
+ } while(0)
+
+#define headset_reg_set_bit(addr, bit) \
+ do { \
+ uint32_t temp; \
+ temp = sci_adi_read(addr); \
+ temp = temp | bit; \
+ sci_adi_raw_write(addr, temp); \
+ } while(0)
+
+static inline uint32_t headset_reg_get_bit(uint32_t addr, uint32_t bit)
+{
+ uint32_t temp;
+ temp = sci_adi_read(addr);
+ temp = temp & bit;
+ return temp;
+}
+
+typedef enum sprd_headset_type {
+ HEADSET_NORMAL,
+ HEADSET_NO_MIC,
+ HEADSET_NORTH_AMERICA,
+ HEADSET_APPLE,
+ HEADSET_CAMERA_POLE, //add by lgd
+ HEADSET_TYPE_ERR = -1,
+} SPRD_HEADSET_TYPE;
+
+static struct delayed_work reg_dump_work;
+static struct workqueue_struct *reg_dump_work_queue;
+
+/* < add by lgd */
+#ifdef CONFIG_CAMERA_POLE_SUPPORT
+static struct delayed_work camera_pole_work;
+static struct workqueue_struct *camera_pole_work_queue;
+static int headset_ctl = 0;
+#define CAMERA_POLE_CODE KEY_CAMERA
+#endif
+/* add by lgd > */
+
+/***polling ana_sts0 to avoid the hardware defect***/
+#ifdef SPRD_STS_POLLING_EN
+static struct delayed_work sts_check_work;
+static struct workqueue_struct *sts_check_work_queue;
+static int plug_state_class_g_on = 0;
+static int sts_check_work_need_to_cancel = 1;
+#endif
+/***polling ana_sts0 to avoid the hardware defect***/
+
+#ifdef ADPGAR_BYP_SELECT
+static struct delayed_work adpgar_byp_select_work;
+static struct workqueue_struct *adpgar_byp_select_work_queue;
+#endif
+
+static DEFINE_SPINLOCK(irq_button_lock);
+static DEFINE_SPINLOCK(irq_detect_lock);
+static int detect_err_count = 0;
+static int debug_level = 0;
+static int adie_type = 100;
+static int gpio_detect_value_last = 0;
+static int gpio_button_value_last = 0;
+static volatile int button_state_last = 0; //0==released, 1==pressed
+static int current_key_code = KEY_RESERVED;
+static int plug_state_last = 0; //if the hardware detected the headset is plug in, set plug_state_last = 1
+static struct wake_lock headset_detect_wakelock;
+static struct wake_lock headset_button_wakelock;
+static struct semaphore headset_sem;
+
+
+static struct sprd_headset headset = {
+ .sdev = {
+ .name = "h2w",
+ },
+};
+
+#ifdef CONFIG_HEADSET_AUXADC_CAL_SUPPORTED
+static int adc_get_ideal(int adc_mic);
+#endif
+//======================== audio codec ========================
+static struct sprd_headset_power {
+ struct regulator *head_mic;
+ struct regulator *vcom_buf;
+ struct regulator *vbo;
+} sprd_hts_power;
+
+static int sprd_headset_power_get(struct device *dev,
+ struct regulator **regu, const char *id)
+{
+ if (!*regu) {
+ *regu = regulator_get(dev, id);
+ if (IS_ERR(*regu)) {
+ pr_err("ERR:Failed to request %ld: %s\n",
+ PTR_ERR(*regu), id);
+ *regu = 0;
+ return PTR_ERR(*regu);
+ }
+ }
+ return 0;
+}
+
+static int sprd_headset_power_init(struct device *dev)
+{
+ int ret = 0;
+ ret =
+ sprd_headset_power_get(dev, &sprd_hts_power.head_mic,
+ "HEADMICBIAS");
+ if (ret || (sprd_hts_power.head_mic == NULL)) {
+ sprd_hts_power.head_mic = 0;
+ return ret;
+ }
+ regulator_set_voltage(sprd_hts_power.head_mic, 950000, 950000);
+
+ ret = sprd_headset_power_get(dev, &sprd_hts_power.vcom_buf, "VCOM_BUF");
+ if (ret) {
+ sprd_hts_power.vcom_buf = 0;
+ goto __err1;
+ }
+
+ ret = sprd_headset_power_get(dev, &sprd_hts_power.vbo, "VBO");
+ if (ret) {
+ sprd_hts_power.vbo = 0;
+ goto __err2;
+ }
+ if (sprd_hts_power.vbo != NULL)
+ regulator_enable(sprd_hts_power.vbo);
+ goto __ok;
+__err2:
+ regulator_put(sprd_hts_power.vcom_buf);
+__err1:
+ regulator_put(sprd_hts_power.head_mic);
+__ok:
+ return ret;
+}
+
+static void sprd_headset_power_deinit(void)
+{
+ regulator_put(sprd_hts_power.head_mic);
+ regulator_put(sprd_hts_power.vcom_buf);
+ regulator_put(sprd_hts_power.vbo);
+}
+
+static int sprd_headset_audio_block_is_running(struct device *dev)
+{
+ return regulator_is_enabled(sprd_hts_power.vcom_buf)
+ || regulator_is_enabled(sprd_hts_power.vbo);
+}
+
+static int sprd_headset_audio_headmic_sleep_disable(struct device *dev, int on)
+{
+ int ret = 0;
+ if (!sprd_hts_power.head_mic) {
+ return -1;
+ }
+
+ if (on) {
+ ret = regulator_enable(sprd_hts_power.vbo);
+ ret =
+ regulator_set_mode(sprd_hts_power.head_mic,
+ REGULATOR_MODE_NORMAL);
+ } else {
+ ret = regulator_disable(sprd_hts_power.vbo);
+ if (sprd_headset_audio_block_is_running(dev)) {
+ ret =
+ regulator_set_mode(sprd_hts_power.head_mic,
+ REGULATOR_MODE_NORMAL);
+ } else {
+ ret =
+ regulator_set_mode(sprd_hts_power.head_mic,
+ REGULATOR_MODE_STANDBY);
+ }
+ }
+ return ret;
+}
+
+static int sprd_headset_headmic_bias_control(struct device *dev, int on)
+{
+ int ret = 0;
+ if (!sprd_hts_power.head_mic) {
+ return -1;
+ }
+ if (on) {
+ ret = regulator_enable(sprd_hts_power.head_mic);
+ } else {
+ ret = regulator_disable(sprd_hts_power.head_mic);
+ }
+ if (!ret) {
+ /* Set HEADMIC_SLEEP when audio block closed */
+ if (sprd_headset_audio_block_is_running(dev)) {
+ ret =
+ regulator_set_mode(sprd_hts_power.head_mic,
+ REGULATOR_MODE_NORMAL);
+ } else {
+ ret =
+ regulator_set_mode(sprd_hts_power.head_mic,
+ REGULATOR_MODE_STANDBY);
+ }
+ }
+ return ret;
+}
+
+static BLOCKING_NOTIFIER_HEAD(hp_chain_list);
+int hp_register_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&hp_chain_list, nb);
+}
+EXPORT_SYMBOL(hp_register_notifier);
+
+int hp_unregister_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&hp_chain_list, nb);
+}
+EXPORT_SYMBOL(hp_unregister_notifier);
+
+static int hp_notifier_call_chain(unsigned long val)
+{
+ return (blocking_notifier_call_chain(&hp_chain_list, val, NULL)
+ == NOTIFY_BAD) ? -EINVAL : 0;
+}
+
+int get_hp_plug_state(void)
+{
+ return !!plug_state_last;
+}
+EXPORT_SYMBOL(get_hp_plug_state);
+//======================== audio codec ========================
+
+static int wrap_sci_adc_get_value(unsigned int channel, int scale)
+{
+ int count = 0;
+ int average = 0;
+
+ while(count < SCI_ADC_GET_VALUE_COUNT) {
+ average += sci_adc_get_value(channel, scale);
+ count++;
+ }
+ average /= SCI_ADC_GET_VALUE_COUNT;
+
+ return average;
+}
+
+/* on = 0: open headmic detect circuit */
+static void headset_detect_circuit(unsigned on)
+{
+ if (on) {
+ headset_reg_clr_bit(HEADMIC_DETECT_REG(ANA_CFG20), AUDIO_HEAD_INS_HMBIAS_EN);
+ } else {
+ headset_reg_set_bit(HEADMIC_DETECT_REG(ANA_CFG20), AUDIO_HEAD_INS_HMBIAS_EN);
+ }
+}
+
+static void headset_detect_clk_en(void)
+{
+ //address:0x4003_8800+0x00
+ headset_reg_set_bit(HEADMIC_DETECT_GLB_REG(0x00), (HDT_EN | AUD_EN));
+ //address:0x4003_8800+0x04
+ headset_reg_set_bit(HEADMIC_DETECT_GLB_REG(0x04), (CLK_AUD_HID_EN | CLK_AUD_HBD_EN));
+}
+
+static void headset_detect_init(void)
+{
+ headset_detect_clk_en();
+ /* set headset detect voltage */
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CFG20), AUDIO_HEAD_SDET_2P5_OR_1P6, AUDIO_HEAD_SDET_MASK, AUDIO_HEAD_SDET_SHIFT);
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CFG20), AUDIO_HEAD_INS_VREF_2P1_OR_1P4, AUDIO_HEAD_INS_VREF_MASK, AUDIO_HEAD_INS_VREF_SHIFT);
+ /*set headmicbias voltage*/
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CFG0), AUDIO_MICBIAS_V_2P3_OR_3P0, AUDIO_MICBIAS_V_MASK, AUDIO_MICBIAS_V_SHIFT);
+ headset_reg_set_bit(HEADMIC_DETECT_REG(ANA_CFG0), AUDIO_MICBIAS_HV_EN);
+}
+
+static void headmic_sleep_disable(struct device *dev, int on);
+
+static void headset_adc_en(int en)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+
+ if (en) {
+ headset_reg_set_bit(HEADMIC_DETECT_REG(ANA_CFG20), AUDIO_HEAD_BUF_EN);
+ headset_reg_set_bit(HEADMIC_DETECT_REG(ANA_CFG20), AUDIO_V2ADC_EN);
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CFG20), AUDIO_HEAD2ADC_SEL_MIC_IN, AUDIO_HEAD2ADC_SEL_MASK, AUDIO_HEAD2ADC_SEL_SHIFT);
+ headset_reg_set_bit(HEADMIC_DETECT_REG(ANA_CFG1), AUDIO_ICM_PLUS_EN);
+ headmic_sleep_disable(&ht->this_pdev, 1);
+ } else {
+ if(debug_level <= 2) {
+ headset_reg_clr_bit(HEADMIC_DETECT_REG(ANA_CFG20), AUDIO_HEAD_BUF_EN);
+ headset_reg_clr_bit(HEADMIC_DETECT_REG(ANA_CFG20), AUDIO_V2ADC_EN);
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CFG20), AUDIO_HEAD2ADC_SEL_DISABLE, AUDIO_HEAD2ADC_SEL_MASK, AUDIO_HEAD2ADC_SEL_SHIFT);
+ }
+ headmic_sleep_disable(&ht->this_pdev, 0);
+ }
+}
+
+/* is_set = 1, headset_mic to AUXADC */
+static void set_adc_to_headmic(unsigned is_set)
+{
+ headset_adc_en(1);
+
+ if (is_set) {
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CFG20), AUDIO_HEAD2ADC_SEL_MIC_IN, AUDIO_HEAD2ADC_SEL_MASK, AUDIO_HEAD2ADC_SEL_SHIFT);
+ } else {
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CFG20), AUDIO_HEAD2ADC_SEL_L_INT, AUDIO_HEAD2ADC_SEL_MASK, AUDIO_HEAD2ADC_SEL_SHIFT);
+ }
+}
+
+static void headset_button_irq_threshold(int enable)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ int audio_head_sbut = 0;
+
+ audio_head_sbut = pdata->irq_threshold_buttont;
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ if(enable) {
+ if(HEADSET_CAMERA_POLE == ht->raw_type)
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CFG20), 11, AUDIO_HEAD_SBUT_MASK, AUDIO_HEAD_SBUT_SHIFT);
+ else
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CFG20), audio_head_sbut, AUDIO_HEAD_SBUT_MASK, AUDIO_HEAD_SBUT_SHIFT);
+ }
+ else
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CFG20), 0xF, AUDIO_HEAD_SBUT_MASK, AUDIO_HEAD_SBUT_SHIFT);
+ #else
+
+ if (enable)
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CFG20), audio_head_sbut, AUDIO_HEAD_SBUT_MASK, AUDIO_HEAD_SBUT_SHIFT);
+ else
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CFG20), 0xF, AUDIO_HEAD_SBUT_MASK, AUDIO_HEAD_SBUT_SHIFT);
+ #endif
+ /* add by lgd > */
+}
+
+static void headset_micbias_polling_en(int en)
+{
+ if(en) {
+ //step 1: enable clk for register accessable
+ headset_detect_clk_en();
+ //step 2: start polling & set timer
+ headset_reg_set_val(HEADMIC_BUTTON_REG(HID_CFG2), HID_TMR_T1T2_STEP_VAL, HID_TMR_T1T2_STEP_MASK, HID_TMR_T1T2_STEP_SHIFT);//step for T1 & T2 [9:5]
+ headset_reg_set_val(HEADMIC_BUTTON_REG(HID_CFG2), HID_TMR_T0_VAL, HID_TMR_T0_MASK, HID_TMR_T0_SHIFT);//T0 timer count [4:0]
+ headset_reg_set_val(HEADMIC_BUTTON_REG(HID_CFG3), HID_TMR_T1_VAL, HID_TMR_T1_MASK, HID_TMR_T1_SHIFT);//T1 timer count [15:0]
+ headset_reg_set_val(HEADMIC_BUTTON_REG(HID_CFG4), HID_TMR_T2_VAL, HID_TMR_T2_MASK, HID_TMR_T2_SHIFT);//T2 timer count [15:0]
+ //step 3: disable headmicbias
+ headset_reg_clr_bit(HEADMIC_DETECT_REG(ANA_CFG0), BIT(5));
+ //headset_reg_set_bit(HEADMIC_DETECT_REG(ANA_CFG0), BIT(1));
+ PRINT_INFO("headmicbias polling enable\n");
+ PRINT_DBG("ANA_CFG0(0x%08X) HID_CFG0(0x%08X) HID_CFG2(0x%08X) HID_CFG3(0x%08X) HID_CFG4(0x%08X)\n",
+ sci_adi_read(HEADMIC_DETECT_REG(ANA_CFG0)),
+ sci_adi_read(HEADMIC_BUTTON_REG(HID_CFG0)),
+ sci_adi_read(HEADMIC_BUTTON_REG(HID_CFG2)),
+ sci_adi_read(HEADMIC_BUTTON_REG(HID_CFG3)),
+ sci_adi_read(HEADMIC_BUTTON_REG(HID_CFG4)));
+ } else {
+ //step 1: enable headmicbias
+ //headset_reg_clr_bit(HEADMIC_DETECT_REG(ANA_CFG0), BIT(1));
+ headset_reg_set_bit(HEADMIC_DETECT_REG(ANA_CFG0), BIT(5));
+ //step 2: stop polling
+ PRINT_INFO("headmicbias polling disable\n");
+ PRINT_DBG("ANA_CFG0(0x%08X) HID_CFG0(0x%08X) HID_CFG2(0x%08X) HID_CFG3(0x%08X) HID_CFG4(0x%08X)\n",
+ sci_adi_read(HEADMIC_DETECT_REG(ANA_CFG0)),
+ sci_adi_read(HEADMIC_BUTTON_REG(HID_CFG0)),
+ sci_adi_read(HEADMIC_BUTTON_REG(HID_CFG2)),
+ sci_adi_read(HEADMIC_BUTTON_REG(HID_CFG3)),
+ sci_adi_read(HEADMIC_BUTTON_REG(HID_CFG4)));
+ }
+}
+
+static void headset_irq_button_enable(int enable, unsigned int irq)
+{
+ PRINT_INFO("headset_irq_button_enable irq = %d\n",irq);
+ unsigned long spin_lock_flags;
+ static int current_irq_state = 1;//irq is enabled after request_irq()
+ spin_lock_irqsave(&irq_button_lock, spin_lock_flags);
+ if (1 == enable) {
+ if (0 == current_irq_state) {
+ enable_irq(irq);
+ current_irq_state = 1;
+ }
+ } else {
+ if (1 == current_irq_state) {
+ disable_irq_nosync(irq);
+ current_irq_state = 0;
+ }
+ }
+ spin_unlock_irqrestore(&irq_button_lock, spin_lock_flags);
+ return;
+}
+
+static void headset_irq_detect_enable(int enable, unsigned int irq)
+{
+ PRINT_INFO("headset_irq_detect_enable irq = %d\n",irq);
+ unsigned long spin_lock_flags;
+ static int current_irq_state = 1;//irq is enabled after request_irq()
+ spin_lock_irqsave(&irq_detect_lock, spin_lock_flags);
+ if (1 == enable) {
+ if (0 == current_irq_state) {
+ enable_irq(irq);
+ current_irq_state = 1;
+ }
+ } else {
+ if (1 == current_irq_state) {
+ disable_irq_nosync(irq);
+ current_irq_state = 0;
+ }
+ }
+ spin_unlock_irqrestore(&irq_detect_lock, spin_lock_flags);
+ return;
+}
+
+static void headmic_sleep_disable(struct device *dev, int on)
+{
+ static int current_power_state = 0;
+
+ if (1 == on) {
+ if (0 == current_power_state) {
+ sprd_headset_audio_headmic_sleep_disable(dev, 1);
+ current_power_state = 1;
+ }
+ } else {
+ if (1 == current_power_state) {
+ sprd_headset_audio_headmic_sleep_disable(dev, 0);
+ current_power_state = 0;
+ }
+ }
+
+ return;
+}
+
+static void headmicbias_power_on(struct device *dev, int on)
+{
+ static int current_power_state = 0;
+ struct sprd_headset *ht = &headset;
+
+ if (1 == on) {
+ if (0 == current_power_state) {
+ if(NULL != ht->platform_data->external_headmicbias_power_on)
+ ht->platform_data->external_headmicbias_power_on(1);
+ sprd_headset_headmic_bias_control(dev, 1);
+ current_power_state = 1;
+ }
+ } else {
+ if (1 == current_power_state) {
+ if(NULL != ht->platform_data->external_headmicbias_power_on)
+ ht->platform_data->external_headmicbias_power_on(0);
+ sprd_headset_headmic_bias_control(dev, 0);
+ current_power_state = 0;
+ }
+ }
+
+ return;
+}
+
+static int ana_sts0_confirm(void)
+{
+ if(0 == headset_reg_get_bit(HEADMIC_DETECT_REG(ANA_STS0), AUDIO_HEAD_INSERT))
+ return 0;
+ else
+ return 1;
+}
+
+static int adc_compare(int x1, int x2)
+{
+ int delta = 0;
+ int max = 0;
+
+ if((x1<100) && (x2<100))
+ return 1;
+
+ x1 = ((0==x1) ? 1 : (x1*100));
+ x2 = ((0==x2) ? 1 : (x2*100));
+
+ delta = ABS(x1-x2);
+ max = MAX(x1, x2);
+
+ if(delta < ((max*10)/100))
+ return 1;
+ else
+ return 0;
+}
+
+static int adc_get_average(int gpio_num, int gpio_value)
+{
+ int i = 0;
+ int j = 0;
+ int k = 0;
+ int adc_average = 0;
+ int success = 1;
+ int adc[ADC_READ_COUNT] = {0};
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+
+ //================= debug =====================
+ if(debug_level >= 3) {
+ int count = 0;
+ int adc_val = 0;
+ int gpio_button = 0;
+ int ana_cfg0 = 0;
+ int ana_cfg1 = 0;
+ int ana_cfg20 = 0;
+ int ana_sts0 = 0;
+
+ while(count < 20)
+ {
+ count++;
+ adc_val = wrap_sci_adc_get_value(ADC_CHANNEL_HEADMIC, 0);
+ gpio_button = gpio_get_value(pdata->gpio_button);
+ ana_cfg0 = sci_adi_read(HEADMIC_DETECT_REG(ANA_CFG0));
+ ana_cfg1 = sci_adi_read(HEADMIC_DETECT_REG(ANA_CFG1));
+ ana_cfg20 = sci_adi_read(HEADMIC_DETECT_REG(ANA_CFG20));
+ ana_sts0 = sci_adi_read(HEADMIC_DETECT_REG(ANA_STS0));
+
+ PRINT_INFO("%2d: gpio_button=%d adc=%4d ana_cfg0=0x%08X ana_cfg1=0x%08X ana_cfg20=0x%08X ana_sts0=0x%08X\n",
+ count, gpio_button, adc_val, ana_cfg0, ana_cfg1, ana_cfg20, ana_sts0);
+ msleep(1);
+ }
+ }
+ //================= debug =====================
+
+ msleep(1);
+ if(0 == headset_reg_get_bit(HEADMIC_DETECT_REG(ANA_CFG0), AUDIO_HEADMIC_SLEEP_EN))
+ PRINT_INFO("AUDIO_HEADMIC_SLEEP_EN is disabled\n");
+
+ for(i=0; i< ADC_READ_LOOP; i++) {
+ if(gpio_get_value(gpio_num) != gpio_value) {
+ PRINT_INFO("gpio value changed!!! the adc read operation aborted (step1)\n");
+ return -1;
+ }
+ if(0 == ana_sts0_confirm()) {
+ PRINT_INFO("ana_sts0_confirm failed!!! the adc read operation aborted (step2)\n");
+ return -1;
+ }
+ adc[0] = wrap_sci_adc_get_value(ADC_CHANNEL_HEADMIC, 0);
+ for(j=0; j<ADC_READ_COUNT-1; j++) {
+ udelay(100);
+ if(gpio_get_value(gpio_num) != gpio_value) {
+ PRINT_INFO("gpio value changed!!! the adc read operation aborted (step3)\n");
+ return -1;
+ }
+ if(0 == ana_sts0_confirm()) {
+ PRINT_INFO("ana_sts0_confirm failed!!! the adc read operation aborted (step4)\n");
+ return -1;
+ }
+ adc[j+1] = wrap_sci_adc_get_value(ADC_CHANNEL_HEADMIC, 0);
+ if(0 == adc_compare(adc[j], adc[j+1])) {
+ success = 0;
+ for(k=0; k<=j+1; k++) {
+ PRINT_DBG("adc[%d] = %d\n", k, adc[k]);
+ }
+ break;
+ }
+ }
+ if(1 == success) {
+ msleep(DBNC_CNT3_VALUE);
+ if(gpio_get_value(gpio_num) != gpio_value) {
+ PRINT_INFO("gpio value changed!!! the adc read operation aborted (step5)\n");
+ return -1;
+ }
+ if(0 == ana_sts0_confirm()) {
+ PRINT_INFO("ana_sts0_confirm failed!!! the adc read operation aborted (step6)\n");
+ return -1;
+ }
+ for(i=0; i<ADC_READ_COUNT; i++) {
+ adc_average += adc[i];
+ PRINT_DBG("adc[%d] = %d\n", i, adc[i]);
+ }
+ adc_average = adc_average / ADC_READ_COUNT;
+ PRINT_DBG("adc_get_average success, adc_average = %d\n", adc_average);
+ return adc_average;
+ }
+ else if(i+1< ADC_READ_LOOP) {
+ PRINT_INFO("adc_get_average failed, retrying count = %d\n", i+1);
+ msleep(1);
+ }
+ }
+
+ return -1;
+}
+
+static int headset_irq_set_irq_type(unsigned int irq, unsigned int type)
+{
+ struct sprd_headset *ht = &headset;
+ struct irq_desc *irq_desc = NULL;
+ unsigned int irq_flags = 0;
+ int ret = -1;
+
+ ret = irq_set_irq_type(irq, type);
+ irq_desc = irq_to_desc(irq);
+ irq_flags = irq_desc->action->flags;
+
+ if(irq == ht->irq_button) {
+ if(IRQF_TRIGGER_HIGH == type) {
+ PRINT_DBG("IRQF_TRIGGER_HIGH is set for irq_button(%d). irq_flags = 0x%08X, ret = %d\n",
+ ht->irq_button, irq_flags, ret);
+ }
+ else if(IRQF_TRIGGER_LOW == type) {
+ PRINT_DBG("IRQF_TRIGGER_LOW is set for irq_button(%d). irq_flags = 0x%08X, ret = %d\n",
+ ht->irq_button, irq_flags, ret);
+ }
+ }
+ else if(irq == ht->irq_detect) {
+ if(IRQF_TRIGGER_HIGH == type) {
+ PRINT_DBG("IRQF_TRIGGER_HIGH is set for irq_detect(%d). irq_flags = 0x%08X, ret = %d\n",
+ ht->irq_detect, irq_flags, ret);
+ }
+ else if(IRQF_TRIGGER_LOW == type) {
+ PRINT_DBG("IRQF_TRIGGER_LOW is set for irq_detect(%d). irq_flags = 0x%08X, ret = %d\n",
+ ht->irq_detect, irq_flags, ret);
+ }
+ }
+ return 0;
+}
+
+static int headset_button_valid(int gpio_detect_value_current)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ int button_is_valid = 0;
+
+ if(1 == pdata->irq_trigger_level_detect) {
+ if(1 == gpio_detect_value_current)
+ button_is_valid = 1;
+ else
+ button_is_valid = 0;
+ } else {
+ if(0 == gpio_detect_value_current)
+ button_is_valid = 1;
+ else
+ button_is_valid = 0;
+ }
+
+ return button_is_valid;
+}
+
+static int headset_gpio_2_button_state(int gpio_button_value_current)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ int button_state_current = 0;
+
+ if(1 == pdata->irq_trigger_level_button) {
+ if(1 == gpio_button_value_current)
+ button_state_current = 1;
+ else
+ button_state_current = 0;
+ } else {
+ if(0 == gpio_button_value_current)
+ button_state_current = 1;
+ else
+ button_state_current = 0;
+ }
+
+ return button_state_current; //0==released, 1==pressed
+}
+
+static int headset_plug_confirm_by_adc(int last_gpio_detect_value)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ int adc_last = 0;
+ int adc_current = 0;
+ int count = 0;
+ int adc_read_interval = 10;
+
+ msleep(adc_read_interval);
+ adc_last = adc_get_average(pdata->gpio_detect, last_gpio_detect_value);
+ if(-1 == adc_last) {
+ PRINT_INFO("headset_plug_confirm_by_adc failed!!!\n");
+ return -1;
+ }
+
+ while(count < PLUG_CONFIRM_COUNT) {
+ adc_current = adc_get_average(pdata->gpio_detect, last_gpio_detect_value);
+ if(-1 == adc_current) {
+ PRINT_INFO("headset_plug_confirm_by_adc failed!!!\n");
+ return -1;
+ }
+ if(0 == adc_compare(adc_last, adc_current)) {
+ PRINT_INFO("headset_plug_confirm_by_adc failed!!!\n");
+ return -1;
+ }
+ adc_last = adc_current;
+ count++;
+ msleep(adc_read_interval);
+ }
+ PRINT_INFO("headset_plug_confirm_by_adc success!!!\n");
+ return adc_current;
+}
+
+static SPRD_HEADSET_TYPE headset_type_detect(int last_gpio_detect_value)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ int adc_mic_average = 0;
+ int adc_left_average = 0;
+#ifdef CONFIG_HEADSET_AUXADC_CAL_SUPPORTED
+ int adc_mic_ideal = 0;
+ int adc_left_ideal = 0;
+#endif
+ int no_mic_retry_count = NO_MIC_RETRY_COUNT;
+ unsigned int head_insert_2;
+
+ ENTER;
+
+ if(0 != pdata->gpio_switch)
+ gpio_direction_output(pdata->gpio_switch, 0);
+ else
+ PRINT_INFO("automatic type switch is unsupported\n");
+
+ headset_button_irq_threshold(1);
+ headset_detect_init();
+ headset_detect_circuit(1);
+
+no_mic_retry:
+
+ //get adc value of left
+ if(EIC_AUD_HEAD_INST2 == pdata->gpio_detect) {
+ PRINT_INFO("HEADSET_DETECT_GPIO=%d, matched the reference phone, now getting adc value of left\n", pdata->gpio_detect);
+ set_adc_to_headmic(0);
+ msleep(50);
+ adc_left_average = headset_plug_confirm_by_adc(last_gpio_detect_value);
+ if(-1 == adc_left_average)
+ return HEADSET_TYPE_ERR;
+ } else {
+ PRINT_INFO("HEADSET_DETECT_GPIO=%d, NOT matched the reference phone(GPIO_%d), set adc_left_average to 0\n",
+ pdata->gpio_detect, EIC_AUD_HEAD_INST2);
+ adc_left_average = 0;
+ }
+
+ //get adc value of mic
+ set_adc_to_headmic(1);
+ msleep(50);
+ adc_mic_average = headset_plug_confirm_by_adc(last_gpio_detect_value);
+ if(-1 == adc_mic_average)
+ return HEADSET_TYPE_ERR;
+#ifdef CONFIG_HEADSET_AUXADC_CAL_SUPPORTED
+ adc_mic_ideal = adc_get_ideal(adc_mic_average);
+ adc_left_ideal = adc_get_ideal(adc_left_average);
+
+ PRINT_INFO("adc_mic_average=%d, adc_mic_ideal=%d\n", adc_mic_average, adc_mic_ideal);
+ PRINT_INFO("adc_left_average=%d, adc_left_ideal=%d\n", adc_left_average, adc_left_ideal);
+
+ if(adc_mic_ideal >= 0 && adc_left_ideal >= 0) {
+ adc_mic_average = adc_mic_ideal;
+ adc_left_average = adc_left_ideal;
+ }
+#else
+ PRINT_INFO("adc_mic_average = %d\n", adc_mic_average);
+ PRINT_INFO("adc_left_average = %d\n", adc_left_average);
+#endif
+ if((gpio_get_value(pdata->gpio_detect)) != last_gpio_detect_value) {
+ PRINT_INFO("software debance (gpio check)!!!(headset_type_detect)\n");
+ return HEADSET_TYPE_ERR;
+ }
+
+ if(0 == ana_sts0_confirm()) {
+ PRINT_INFO("software debance (ana_sts0_confirm)!!!(headset_type_detect)\n");
+ return HEADSET_TYPE_ERR;
+ }
+
+ head_insert_2 = headset_reg_get_bit(HEADMIC_DETECT_REG(ANA_STS0), AUDIO_HEAD_INSERT_2);
+ if((adc_mic_average < pdata->adc_threshold_3pole_detect)) {
+ if(0 != no_mic_retry_count) {
+ PRINT_INFO("no_mic_retry\n");
+ no_mic_retry_count--;
+ goto no_mic_retry;
+ }
+ return HEADSET_NO_MIC;
+ }
+ else if((adc_left_average < ADC_GND) && (adc_mic_average > ADC_GND))
+ return HEADSET_NORMAL;
+/* < add by lgd */
+#ifdef CONFIG_CAMERA_POLE_SUPPORT
+ else if((adc_left_average > ADC_GND) && (adc_mic_average > ADC_GND)) {
+ if (ABS(adc_mic_average - adc_left_average) < ADC_GND && adc_left_average < 2300)
+ return HEADSET_NORTH_AMERICA;
+ else
+ return HEADSET_CAMERA_POLE;
+ }
+#else
+ else if((adc_left_average > ADC_GND) && (adc_mic_average > ADC_GND)
+ && (ABS(adc_mic_average - adc_left_average) < ADC_GND))
+ return HEADSET_NORTH_AMERICA;
+#endif
+/* add by lgd > */
+ else {
+ PRINT_INFO("head_insert_2 = %d\n", head_insert_2);
+ return HEADSET_TYPE_ERR;
+ }
+
+}
+
+static void button_release_verify(void)
+{
+ struct sprd_headset *ht = &headset;
+
+ if(1 == button_state_last) {
+ input_event(ht->input_dev, EV_KEY, current_key_code, 0);
+ input_sync(ht->input_dev);
+ button_state_last = 0;
+ PRINT_INFO("headset button released by force!!! current_key_code = %d(0x%04X)\n", current_key_code, current_key_code);
+
+ if (1 == ht->platform_data->irq_trigger_level_button)
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_HIGH);
+ else
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_LOW);
+ }
+}
+
+static void headset_button_work_func(struct work_struct *work)
+{
+ PRINT_INFO("headset_button_work_func enter into\n");
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ int gpio_button_value_current = 0;
+ int button_state_current = 0;
+ int adc_mic_average = 0;
+#ifdef CONFIG_HEADSET_AUXADC_CAL_SUPPORTED
+ int adc_ideal = 0;
+#endif
+ int i = 0;
+
+ down(&headset_sem);
+ set_adc_to_headmic(1);
+
+ ENTER;
+
+ gpio_button_value_current = gpio_get_value(pdata->gpio_button);
+ if(gpio_button_value_current != gpio_button_value_last) {
+ PRINT_INFO("software debance (step 1: gpio check)!!!(headset_button_work_func)\n");
+ #ifdef ADPGAR_BYP_SELECT
+ queue_delayed_work(adpgar_byp_select_work_queue, &adpgar_byp_select_work, msecs_to_jiffies(0));
+ #endif
+ goto out;
+ }
+
+ button_state_current = headset_gpio_2_button_state(gpio_button_value_current);
+
+ if(1 == button_state_current) {//pressed!
+ if(ht->platform_data->nbuttons > 0) {
+ adc_mic_average = adc_get_average(pdata->gpio_button, gpio_button_value_last);
+ if(-1 == adc_mic_average) {
+ PRINT_INFO("software debance (step 3: adc check)!!!(headset_button_work_func)(pressed)\n");
+ #ifdef ADPGAR_BYP_SELECT
+ queue_delayed_work(adpgar_byp_select_work_queue, &adpgar_byp_select_work, msecs_to_jiffies(0));
+ #endif
+ goto out;
+ }
+#ifdef CONFIG_HEADSET_AUXADC_CAL_SUPPORTED
+ adc_ideal = adc_get_ideal(adc_mic_average);
+ PRINT_INFO("adc_mic_average=%d, adc_ideal=%d\n", adc_mic_average, adc_ideal);
+
+ if (adc_ideal >= 0)
+ adc_mic_average = adc_ideal;
+#else
+ PRINT_INFO("adc_mic_average = %d\n", adc_mic_average);
+#endif
+ for (i = 0; i < ht->platform_data->nbuttons; i++) {
+ PRINT_DBG("adc_min=%d, adc_max=%d, code = %d\n",
+ ht->platform_data->headset_buttons[i].adc_min,
+ ht->platform_data->headset_buttons[i].adc_max,
+ ht->platform_data->headset_buttons[i].code);
+ if (adc_mic_average >= ht->platform_data->headset_buttons[i].adc_min &&
+ adc_mic_average < ht->platform_data->headset_buttons[i].adc_max) {
+ current_key_code = ht->platform_data->headset_buttons[i].code;
+ break;
+ }
+ current_key_code = KEY_RESERVED;
+ }
+ }
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ if(HEADSET_CAMERA_POLE == ht->raw_type) {
+ current_key_code = CAMERA_POLE_CODE;
+ }
+ #endif
+ /* add by lgd > */
+ if(0 == button_state_last) {
+ input_event(ht->input_dev, EV_KEY, current_key_code, 1);
+ input_sync(ht->input_dev);
+ button_state_last = 1;
+ PRINT_INFO("headset button pressed! current_key_code = %d(0x%04X)\n", current_key_code, current_key_code);
+ } else {
+ PRINT_ERR("headset button pressed already! current_key_code = %d(0x%04X)\n", current_key_code, current_key_code);
+ #ifdef ADPGAR_BYP_SELECT
+ queue_delayed_work(adpgar_byp_select_work_queue, &adpgar_byp_select_work, msecs_to_jiffies(0));
+ #endif
+ }
+
+ if (1 == ht->platform_data->irq_trigger_level_button)
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_LOW);
+ else
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_HIGH);
+
+ headset_irq_button_enable(1, ht->irq_button);
+ } else { //released!
+ if(1 == button_state_last) {
+ input_event(ht->input_dev, EV_KEY, current_key_code, 0);
+ input_sync(ht->input_dev);
+ button_state_last = 0;
+ PRINT_INFO("headset button released! current_key_code = %d(0x%04X)\n", current_key_code, current_key_code);
+ } else
+ PRINT_ERR("headset button released already! current_key_code = %d(0x%04X)\n", current_key_code, current_key_code);
+
+ if (1 == ht->platform_data->irq_trigger_level_button)
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_HIGH);
+ else
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_LOW);
+
+ headset_irq_button_enable(1, ht->irq_button);
+ }
+out:
+ headset_adc_en(0);
+ headset_irq_button_enable(1, ht->irq_button);
+ //wake_unlock(&headset_button_wakelock);
+ up(&headset_sem);
+ PRINT_INFO("headset_button_work_func go out\n");
+ return;
+}
+static void headset_detect_work_func(struct work_struct *work)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ SPRD_HEADSET_TYPE headset_type;
+ int plug_state_current = 0;
+ int gpio_detect_value_current = 0;
+
+ down(&headset_sem);
+ ENTER;
+ if (sprd_hts_power.head_mic == NULL) {
+ sprd_headset_power_init(&ht->this_pdev);
+ }
+ if (sprd_hts_power.head_mic == NULL ) {
+ PRINT_INFO("sprd_headset_power_init fail \n");
+ goto out;
+ }
+
+ if(0 == plug_state_last) {
+ if(adie_type >= 3) {
+ headmicbias_power_on(&ht->this_pdev, 1);
+ }
+ }
+ if(0 == plug_state_last) {
+ gpio_detect_value_current = gpio_get_value(pdata->gpio_detect);
+ PRINT_INFO("gpio_detect_value_current = %d, gpio_detect_value_last = %d, plug_state_last = %d\n",
+ gpio_detect_value_current, gpio_detect_value_last, plug_state_last);
+
+ if(gpio_detect_value_current != gpio_detect_value_last) {
+ PRINT_INFO("software debance (step 1)!!!(headset_detect_work_func)\n");
+ goto out;
+ }
+
+ if(1 == pdata->irq_trigger_level_detect) {
+ if(1 == gpio_detect_value_current)
+ plug_state_current = 1;
+ else
+ plug_state_current = 0;
+ } else {
+ if(0 == gpio_detect_value_current)
+ plug_state_current = 1;
+ else
+ plug_state_current = 0;
+ }
+ }
+ else
+ plug_state_current = 0;//no debounce for plug out!!!
+
+ if(1 == plug_state_current && 0 == plug_state_last) {
+ headset_type = headset_type_detect(gpio_detect_value_last);
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ ht->raw_type = headset_type;
+ #endif
+ /* add by lgd > */
+ headset_adc_en(0);
+ switch (headset_type) {
+ case HEADSET_TYPE_ERR:
+ detect_err_count ++;
+ PRINT_INFO("headset_type = %d detect_err_count = %d(HEADSET_TYPE_ERR)\n", headset_type,detect_err_count);
+ goto out;
+ case HEADSET_NORTH_AMERICA:
+ PRINT_INFO("headset_type = %d (HEADSET_NORTH_AMERICA)\n", headset_type);
+ if(0 != pdata->gpio_switch)
+ gpio_direction_output(pdata->gpio_switch, 1);
+ break;
+ case HEADSET_NORMAL:
+ PRINT_INFO("headset_type = %d (HEADSET_NORMAL)\n", headset_type);
+ if(0 != pdata->gpio_switch)
+ gpio_direction_output(pdata->gpio_switch, 0);
+ break;
+ case HEADSET_NO_MIC:
+ PRINT_INFO("headset_type = %d (HEADSET_NO_MIC)\n", headset_type);
+ if(0 != pdata->gpio_switch)
+ gpio_direction_output(pdata->gpio_switch, 0);
+ break;
+ case HEADSET_APPLE:
+ PRINT_INFO("headset_type = %d (HEADSET_APPLE)\n", headset_type);
+ PRINT_INFO("we have not yet implemented this in the code\n");
+ break;
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ case HEADSET_CAMERA_POLE:
+ PRINT_INFO("headset_type = %d (HEADSET_CAMERA_POLE)\n", headset_type);
+ break;
+ #endif
+ /* add by lgd > */
+ default:
+ PRINT_INFO("headset_type = %d (HEADSET_UNKNOWN)\n", headset_type);
+ break;
+ }
+ detect_err_count = 0;
+ if(headset_type == HEADSET_NO_MIC)
+ ht->headphone = 1;
+ else
+ ht->headphone = 0;
+
+ if (ht->headphone) {
+ headset_button_irq_threshold(0);
+ headset_irq_button_enable(0, ht->irq_button);
+
+ ht->type = BIT_HEADSET_NO_MIC;
+ hp_notifier_call_chain(ht->type);
+ switch_set_state(&ht->sdev, ht->type);
+ PRINT_INFO("headphone plug in (headset_detect_work_func)\n");
+ } else {
+ headset_button_irq_threshold(1);
+
+ if((HEADSET_NORTH_AMERICA == headset_type) && (0 == pdata->gpio_switch)) {
+ headset_irq_button_enable(0, ht->irq_button);
+ PRINT_INFO("HEADSET_NORTH_AMERICA is not supported by your hardware! so disable the button irq!\n");
+ } else {
+ if (1 == ht->platform_data->irq_trigger_level_button)
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_HIGH);
+ else
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_LOW);
+
+ headset_irq_button_enable(1, ht->irq_button);
+ }
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ if(HEADSET_CAMERA_POLE != ht->raw_type) {
+ #endif
+ /* add by lgd > */
+ ht->type = BIT_HEADSET_MIC;
+ hp_notifier_call_chain(ht->type);
+ switch_set_state(&ht->sdev, ht->type);
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ }
+ #endif
+ /* add by lgd > */
+ PRINT_INFO("headset plug in (headset_detect_work_func)\n");
+ #ifdef ADPGAR_BYP_SELECT
+ queue_delayed_work(adpgar_byp_select_work_queue, &adpgar_byp_select_work, msecs_to_jiffies(500));
+ #endif
+ }
+
+ /***polling ana_sts0 to avoid the hardware defect***/
+#ifdef SPRD_STS_POLLING_EN
+ if ((1 != adie_type) && (adie_type < 5)) {
+ plug_state_class_g_on = 1;
+ sts_check_work_need_to_cancel = 0;
+ queue_delayed_work(sts_check_work_queue, &sts_check_work, msecs_to_jiffies(1000));
+ PRINT_INFO("start sts_check_work_queue\n");
+ }
+ else
+ PRINT_INFO("sts_check_work_queue is no need to start\n");
+#else
+ PRINT_INFO("sts_check_work_queue is not used\n");
+#endif
+ /***polling ana_sts0 to avoid the hardware defect***/
+
+ plug_state_last = 1;
+
+ if(1 == pdata->irq_trigger_level_detect)
+ headset_irq_set_irq_type(ht->irq_detect, IRQF_TRIGGER_LOW);
+ else
+ headset_irq_set_irq_type(ht->irq_detect, IRQF_TRIGGER_HIGH);
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ if(HEADSET_CAMERA_POLE != ht->raw_type)
+ #endif
+ /* add by lgd > */
+ if (adie_type >= 5) {
+ headset_reg_set_bit(ANA_CTL_GLB_BASE + ARM_MF_REG, HEAD_INSERT_EIC_EN);
+ headset_reg_clr_bit(ANA_CTL_GLB_BASE + ARM_MF_REG, HEAD_INSERT2_EIC_EN);
+ PRINT_INFO("disable HEAD_INSERT2_EIC_EN, enable HEAD_INSERT_EIC_EN\n");
+ }
+
+ headset_irq_detect_enable(1, ht->irq_detect);
+ } else if(0 == plug_state_current && 1 == plug_state_last) {
+
+ headset_irq_button_enable(0, ht->irq_button);
+ button_release_verify();
+
+ /***polling ana_sts0 to avoid the hardware defect***/
+#ifdef SPRD_STS_POLLING_EN
+ if ((1 != adie_type) && (adie_type < 5)) {
+ sts_check_work_need_to_cancel = 1;
+ if(0 == plug_state_class_g_on) {
+ PRINT_INFO("plug out already!!!\n");
+ goto plug_out_already;
+ }
+ plug_state_class_g_on = 0;
+ }
+#endif
+ /***polling ana_sts0 to avoid the hardware defect***/
+
+ if (ht->headphone) {
+ PRINT_INFO("headphone plug out (headset_detect_work_func)\n");
+ } else {
+ PRINT_INFO("headset plug out (headset_detect_work_func)\n");
+ }
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ if(HEADSET_CAMERA_POLE != ht->raw_type) {
+ #endif
+ /* add by lgd > */
+ ht->type = BIT_HEADSET_OUT;
+ hp_notifier_call_chain(ht->type);
+ switch_set_state(&ht->sdev, ht->type);
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ }
+ #endif
+ /* add by lgd > */
+plug_out_already:
+ plug_state_last = 0;
+
+ if(1 == pdata->irq_trigger_level_detect)
+ headset_irq_set_irq_type(ht->irq_detect, IRQF_TRIGGER_HIGH);
+ else
+ headset_irq_set_irq_type(ht->irq_detect, IRQF_TRIGGER_LOW);
+
+ if (adie_type >= 5) {
+ headset_reg_set_bit(ANA_CTL_GLB_BASE + ARM_MF_REG, HEAD_INSERT2_EIC_EN);
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ if(0 == headset_ctl) {
+ #endif
+ /* add by lgd > */
+ headset_reg_clr_bit(ANA_CTL_GLB_BASE + ARM_MF_REG, HEAD_INSERT_EIC_EN);
+ PRINT_INFO("disable HEAD_INSERT_EIC_EN, enable HEAD_INSERT2_EIC_EN\n");
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ }
+ #endif
+ /* add by lgd > */
+ }
+
+ headset_irq_detect_enable(1, ht->irq_detect);
+ } else {
+ PRINT_INFO("irq_detect must be enabled anyway!!!\n");
+ goto out;
+ }
+out:
+
+ if(0 == plug_state_last) {
+ if(adie_type >= 3) {
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ if(0 == headset_ctl)
+ #endif
+ /* add by lgd > */
+ headmicbias_power_on(&ht->this_pdev, 0);
+ }
+ }
+ if(detect_err_count < 500) {
+ headset_irq_detect_enable(1, ht->irq_detect);
+ }
+ //wake_unlock(&headset_detect_wakelock);
+ up(&headset_sem);
+ return;
+}
+
+/***polling ana_sts0 to avoid the hardware defect***/
+#ifdef SPRD_STS_POLLING_EN
+static void headset_sts_check_func(struct work_struct *work)
+{
+ int ana_sts0 = 0;
+ int ana_sts0_debance = 0;
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ SPRD_HEADSET_TYPE headset_type;
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ if(HEADSET_CAMERA_POLE == ht->raw_type) {
+ PRINT_INFO("headset_sts_check_func is not implemented for camera pole\n");
+ return;
+ }
+ #endif
+ /* add by lgd > */
+ down(&headset_sem);
+
+ ENTER;
+
+ if(1 == sts_check_work_need_to_cancel)
+ goto out;
+
+ ana_sts0_debance = sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_STS0);//arm base address:0x40038600
+ msleep(100);
+ ana_sts0 = sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_STS0);//arm base address:0x40038600
+ if((0x00000060 & ana_sts0) != (0x00000060 & ana_sts0_debance)) {
+ PRINT_INFO("software debance!!!(headset_sts_check_func)\n");
+ goto out;
+ }
+
+ if(((0x00000060 & ana_sts0) != 0x00000060) && (1 == plug_state_class_g_on)) {
+
+ headset_irq_button_enable(0, ht->irq_button);
+ button_release_verify();
+
+ if (ht->headphone) {
+ PRINT_INFO("headphone plug out (headset_sts_check_func)\n");
+ } else {
+ PRINT_INFO("headset plug out (headset_sts_check_func)\n");
+ }
+ ht->type = BIT_HEADSET_OUT;
+ hp_notifier_call_chain(ht->type);
+ switch_set_state(&ht->sdev, ht->type);
+ plug_state_class_g_on = 0;
+ }
+ if(((0x00000060 & ana_sts0) == 0x00000060) && (0 == plug_state_class_g_on)) {
+ headset_type = headset_type_detect(gpio_get_value(pdata->gpio_detect));
+ headset_adc_en(0);
+ switch (headset_type) {
+ case HEADSET_TYPE_ERR:
+ PRINT_INFO("headset_type = %d (HEADSET_TYPE_ERR)\n", headset_type);
+ goto out;
+ case HEADSET_NORTH_AMERICA:
+ PRINT_INFO("headset_type = %d (HEADSET_NORTH_AMERICA)\n", headset_type);
+ if(0 != pdata->gpio_switch)
+ gpio_direction_output(pdata->gpio_switch, 1);
+ break;
+ case HEADSET_NORMAL:
+ PRINT_INFO("headset_type = %d (HEADSET_NORMAL)\n", headset_type);
+ if(0 != pdata->gpio_switch)
+ gpio_direction_output(pdata->gpio_switch, 0);
+ break;
+ case HEADSET_NO_MIC:
+ PRINT_INFO("headset_type = %d (HEADSET_NO_MIC)\n", headset_type);
+ if(0 != pdata->gpio_switch)
+ gpio_direction_output(pdata->gpio_switch, 0);
+ break;
+ case HEADSET_APPLE:
+ PRINT_INFO("headset_type = %d (HEADSET_APPLE)\n", headset_type);
+ PRINT_INFO("we have not yet implemented this in the code\n");
+ break;
+ default:
+ PRINT_INFO("headset_type = %d (HEADSET_UNKNOWN)\n", headset_type);
+ break;
+ }
+
+ if(headset_type == HEADSET_NO_MIC)
+ ht->headphone = 1;
+ else
+ ht->headphone = 0;
+
+ if (ht->headphone) {
+ headset_button_irq_threshold(0);
+ headset_irq_button_enable(0, ht->irq_button);
+
+ ht->type = BIT_HEADSET_NO_MIC;
+ hp_notifier_call_chain(ht->type);
+ switch_set_state(&ht->sdev, ht->type);
+ PRINT_INFO("headphone plug in (headset_sts_check_func)\n");
+ } else {
+ headset_button_irq_threshold(1);
+
+ if((HEADSET_NORTH_AMERICA == headset_type) && (0 == pdata->gpio_switch)) {
+ headset_irq_button_enable(0, ht->irq_button);
+ PRINT_INFO("HEADSET_NORTH_AMERICA is not supported by your hardware! so disable the button irq!\n");
+ } else {
+ if (1 == ht->platform_data->irq_trigger_level_button)
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_HIGH);
+ else
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_LOW);
+
+ headset_irq_button_enable(1, ht->irq_button);
+ }
+
+ ht->type = BIT_HEADSET_MIC;
+ hp_notifier_call_chain(ht->type);
+ switch_set_state(&ht->sdev, ht->type);
+ PRINT_INFO("headset plug in (headset_sts_check_func)\n");
+ }
+ plug_state_class_g_on = 1;
+ }
+out:
+ if(0 == sts_check_work_need_to_cancel)
+ queue_delayed_work(sts_check_work_queue, &sts_check_work, msecs_to_jiffies(1000));
+ else
+ PRINT_INFO("sts_check_work cancelled\n");
+
+ up(&headset_sem);
+ return;
+}
+#endif
+/***polling ana_sts0 to avoid the hardware defect***/
+
+#ifdef ADPGAR_BYP_SELECT
+static void adpgar_byp_select_func(struct work_struct *work)
+{
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CFG5), AUDIO_ADPGAR_BYP_HEADMIC_2_ADCR, AUDIO_ADPGAR_BYP_MASK, AUDIO_ADPGAR_BYP_SHIFT);
+ PRINT_INFO("ANA_CFG5 = 0x%08X\n", sci_adi_read(HEADMIC_DETECT_REG(ANA_CFG5)));
+ msleep(100);
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CFG5), AUDIO_ADPGAR_BYP_NORMAL, AUDIO_ADPGAR_BYP_MASK, AUDIO_ADPGAR_BYP_SHIFT);
+ PRINT_INFO("ANA_CFG5 = 0x%08X\n", sci_adi_read(HEADMIC_DETECT_REG(ANA_CFG5)));
+ PRINT_INFO("adpgar_byp_select_func\n");
+}
+#endif
+
+static void reg_dump_func(struct work_struct *work)
+{
+ int adc_mic = 0;
+
+ int gpio_detect = 0;
+ int gpio_button = 0;
+
+ int ana_sts0 = 0;
+ int ana_cfg0 = 0;
+ int ana_cfg1 = 0;
+ int ana_cfg20 = 0;
+
+ int hid_cfg0 = 0;
+ int hid_cfg2 = 0;
+ int hid_cfg3 = 0;
+ int hid_cfg4 = 0;
+
+ int arm_module_en = 0;
+ int arm_clk_en = 0;
+
+ adc_mic = wrap_sci_adc_get_value(ADC_CHANNEL_HEADMIC, 0);
+
+ gpio_detect = gpio_get_value(headset.platform_data->gpio_detect);
+ gpio_button = gpio_get_value(headset.platform_data->gpio_button);
+
+ sci_adi_write(ANA_REG_GLB_ARM_MODULE_EN, BIT_ANA_AUD_EN, BIT_ANA_AUD_EN);//arm base address:0x40038800 for register accessable
+ ana_cfg0 = sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_CFG0);//arm base address:0x40038600
+ ana_cfg1 = sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_CFG1);//arm base address:0x40038600
+ ana_cfg20 = sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_CFG20);//arm base address:0x40038600
+ ana_sts0 = sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_STS0);//arm base address:0x40038600
+
+ sci_adi_write(ANA_REG_GLB_ARM_MODULE_EN, BIT_ANA_HDT_EN, BIT_ANA_HDT_EN);//arm base address:0x40038800 for register accessable
+ sci_adi_write(ANA_REG_GLB_ARM_CLK_EN, BIT_CLK_AUD_HID_EN, BIT_CLK_AUD_HID_EN);//arm base address:0x40038800 for register accessable
+
+ hid_cfg2 = sci_adi_read(ANA_HDT_INT_BASE+HID_CFG2);//arm base address:0x40038700
+ hid_cfg3 = sci_adi_read(ANA_HDT_INT_BASE+HID_CFG3);//arm base address:0x40038700
+ hid_cfg4 = sci_adi_read(ANA_HDT_INT_BASE+HID_CFG4);//arm base address:0x40038700
+ hid_cfg0 = sci_adi_read(ANA_HDT_INT_BASE+HID_CFG0);//arm base address:0x40038700
+
+ arm_module_en = sci_adi_read(ANA_REG_GLB_ARM_MODULE_EN);
+ arm_clk_en = sci_adi_read(ANA_REG_GLB_ARM_CLK_EN);
+
+ PRINT_INFO("GPIO_%03d(det)=%d GPIO_%03d(but)=%d adc_mic=%d\n",
+ headset.platform_data->gpio_detect, gpio_detect,
+ headset.platform_data->gpio_button, gpio_button,
+ adc_mic);
+ PRINT_INFO("arm_module_en|arm_clk_en|ana_cfg0 |ana_cfg1 |ana_cfg20 |ana_sts0 |hid_cfg2 |hid_cfg3 |hid_cfg4 |hid_cfg0\n");
+ PRINT_INFO("0x%08X |0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X\n",
+ arm_module_en, arm_clk_en, ana_cfg0, ana_cfg1, ana_cfg20, ana_sts0, hid_cfg2, hid_cfg3, hid_cfg4, hid_cfg0);
+#if 0
+ int i = 0;
+ int hbd[20] = {0};
+
+ for(i=0; i<20; i++)
+ hbd[i] = sci_adi_read(ANA_HDT_INT_BASE + (i*4));
+
+ PRINT_INFO(" hbd_cfg0 | hbd_cfg1 | hbd_cfg2 | hbd_cfg3 | hbd_cfg4 | hbd_cfg5 | hbd_cfg6 | hbd_cfg7 | hbd_cfg8 | hbd_cfg9\n");
+ PRINT_INFO("0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X\n",
+ hbd[0], hbd[1], hbd[2], hbd[3], hbd[4], hbd[5], hbd[6], hbd[7], hbd[8], hbd[9]);
+
+ PRINT_INFO("hbd_cfg10 |hbd_cfg11 |hbd_cfg12 |hbd_cfg13 |hbd_cfg14 |hbd_cfg15 |hbd_cfg16 | hbd_sts0 | hbd_sts1 | hbd_sts2\n");
+ PRINT_INFO("0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X\n",
+ hbd[10], hbd[11], hbd[12], hbd[13], hbd[14], hbd[15], hbd[16], hbd[17], hbd[18], hbd[19]);
+#endif
+
+ if (debug_level >= 2)
+ queue_delayed_work(reg_dump_work_queue, &reg_dump_work, msecs_to_jiffies(500));
+ return;
+}
+
+/* < add by lgd */
+#ifdef CONFIG_CAMERA_POLE_SUPPORT
+static void camera_pole_func(struct work_struct *work)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+
+ down(&headset_sem);
+
+ if(1 == headset_ctl) {
+ headmicbias_power_on(&ht->this_pdev, 1);
+ msleep(5);
+ headset_detect_circuit(1);
+ msleep(5);
+ headset_reg_set_bit(ANA_CTL_GLB_BASE + ARM_MF_REG, HEAD_INSERT_EIC_EN);
+ PRINT_INFO("enable headmic_in insert detect\n");
+ } else {
+ if((HEADSET_CAMERA_POLE == ht->raw_type) || (0 == plug_state_last)) {
+ headset_reg_clr_bit(ANA_CTL_GLB_BASE + ARM_MF_REG, HEAD_INSERT_EIC_EN);
+ headmicbias_power_on(&ht->this_pdev, 0);
+ msleep(5);
+ headset_detect_circuit(0);
+ msleep(5);
+ PRINT_INFO("disable headmic_in insert detect\n");
+ }
+ }
+
+ up(&headset_sem);
+}
+#endif
+/* add by lgd > */
+
+/**
+ * some phone boards, when no headset plugin, the button headset_button_valid()
+ * will always return 0, so we need to disable headset_button irq in here
+ * otherwise button irq will always happen.
+ */
+volatile int headset_button_hardware_bug_fix_stage = 1;
+static void headset_button_hardware_bug_fix(struct sprd_headset *ht)
+{
+ if (headset_button_hardware_bug_fix_stage) {
+ headset_irq_button_enable(0, ht->irq_button); // disable button irq before headset detected
+ pr_err("This phone board headset button circuit has bug, please fix the hardware\n");
+ }
+}
+
+static irqreturn_t headset_button_irq_handler(int irq, void *dev)
+{
+ PRINT_INFO("headset_button_irq_handler enter into\n");
+ struct sprd_headset *ht = dev;
+ int button_state_current = 0;
+ int gpio_button_value_current = 0;
+
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ if(HEADSET_CAMERA_POLE != ht->raw_type)
+ #endif
+ /* add by lgd > */
+
+ if(0 == headset_button_valid(gpio_get_value(ht->platform_data->gpio_detect))) {
+ PRINT_INFO("headset_button_irq_handler: button is invalid!!! IRQ_%d(GPIO_%d) = %d, ANA_STS0 = 0x%08X\n",
+ ht->irq_button, ht->platform_data->gpio_button, gpio_button_value_last,
+ sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_STS0));
+ headset_button_hardware_bug_fix(ht);
+
+ if(0 == plug_state_last)
+ headset_irq_button_enable(0, ht->irq_button);
+
+ return IRQ_HANDLED;
+ }
+
+ gpio_button_value_current = gpio_get_value(ht->platform_data->gpio_button);
+ button_state_current = headset_gpio_2_button_state(gpio_button_value_current);
+ #ifdef ADPGAR_BYP_SELECT
+ if(0 == button_state_current) {
+ queue_delayed_work(adpgar_byp_select_work_queue, &adpgar_byp_select_work, msecs_to_jiffies(0));
+ }
+ #endif
+ if(button_state_current == button_state_last) {
+ PRINT_INFO("button state check failed!!! maybe the release is too quick. button_state_current=%d, button_state_last=%d\n",
+ button_state_current, button_state_last);
+ return IRQ_HANDLED;
+ }
+
+ headset_irq_button_enable(0, ht->irq_button);
+ wake_lock_timeout(&headset_button_wakelock, msecs_to_jiffies(2000));
+ gpio_button_value_last = gpio_button_value_current;
+ PRINT_DBG("headset_button_irq_handler: IRQ_%d(GPIO_%d) = %d, ANA_STS0 = 0x%08X\n",
+ ht->irq_button, ht->platform_data->gpio_button, gpio_button_value_last,
+ sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_STS0));
+ queue_work(ht->button_work_queue, &ht->work_button);
+ PRINT_INFO("headset_button_irq_handler go out\n");
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t headset_detect_irq_handler(int irq, void *dev)
+{
+ struct sprd_headset *ht = dev;
+ headset_irq_button_enable(0, ht->irq_button);
+ headset_irq_detect_enable(0, ht->irq_detect);
+ wake_lock_timeout(&headset_detect_wakelock, msecs_to_jiffies(2000));
+ gpio_detect_value_last = gpio_get_value(ht->platform_data->gpio_detect);
+ PRINT_DBG("headset_detect_irq_handler: IRQ_%d(GPIO_%d) = %d, ANA_STS0 = 0x%08X\n",
+ ht->irq_detect, ht->platform_data->gpio_detect, gpio_detect_value_last,
+ sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_STS0));
+ queue_delayed_work(ht->detect_work_queue, &ht->work_detect, msecs_to_jiffies(0));
+ return IRQ_HANDLED;
+}
+
+//================= create sys fs for debug ===================
+//============= /sys/kernel/headset/debug_level ===============
+
+static ssize_t headset_debug_level_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff)
+{
+ PRINT_INFO("debug_level = %d\n", debug_level);
+ return sprintf(buff, "%d\n", debug_level);
+}
+
+static ssize_t headset_debug_level_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buff, size_t len)
+{
+ unsigned long level = simple_strtoul(buff, NULL, 10);
+ debug_level = level;
+ PRINT_INFO("debug_level = %d\n", debug_level);
+ if(debug_level >= 2)
+ queue_delayed_work(reg_dump_work_queue, &reg_dump_work, msecs_to_jiffies(500));
+ return len;
+}
+
+static struct kobject *headset_debug_kobj = NULL;
+static struct kobj_attribute headset_debug_attr =
+ __ATTR(debug_level, 0644, headset_debug_level_show, headset_debug_level_store);
+
+
+/* < add by lgd */
+#ifdef CONFIG_CAMERA_POLE_SUPPORT
+static ssize_t headset_ctl_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff)
+{
+ PRINT_INFO("headset_ctl = %d\n", headset_ctl);
+ return sprintf(buff, "%d\n", headset_ctl);
+}
+
+static ssize_t headset_ctl_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buff, size_t len)
+{
+ unsigned long ctl = simple_strtoul(buff, NULL, 10);
+ if (headset_ctl == ctl)
+ return len;
+ headset_ctl = ctl;
+ PRINT_INFO("headset_ctl = %d\n", headset_ctl);
+ queue_delayed_work(camera_pole_work_queue, &camera_pole_work, msecs_to_jiffies(100));
+ return len;
+}
+
+static struct kobj_attribute headset_ctl_attr =
+ __ATTR(headset_ctl, 0666, headset_ctl_show, headset_ctl_store);
+#endif
+/* add by lgd > */
+
+static int headset_debug_sysfs_init(void)
+{
+ int ret = -1;
+
+ headset_debug_kobj = kobject_create_and_add("headset", kernel_kobj);
+ if (headset_debug_kobj == NULL) {
+ ret = -ENOMEM;
+ PRINT_ERR("register sysfs failed. ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = sysfs_create_file(headset_debug_kobj, &headset_debug_attr.attr);
+ if (ret) {
+ PRINT_ERR("create sysfs failed. ret = %d\n", ret);
+ return ret;
+ }
+ /* < add by lgd */
+#ifdef CONFIG_CAMERA_POLE_SUPPORT
+ ret = sysfs_create_file(headset_debug_kobj, &headset_ctl_attr.attr);
+ if (ret) {
+ PRINT_ERR("create sysfs failed 2. ret = %d\n", ret);
+ return ret;
+ }
+#endif
+ /* add by lgd > */
+
+ PRINT_INFO("headset_debug_sysfs_init success\n");
+ return ret;
+}
+//================= create sys fs for debug ===================
+
+#ifdef CONFIG_OF
+static struct sprd_headset_platform_data *headset_detect_parse_dt(
+ struct device *dev)
+{
+ struct sprd_headset_platform_data *pdata;
+ struct device_node *np = dev->of_node,*buttons_np = NULL;
+ int ret;
+ struct headset_buttons *buttons_data;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "could not allocate memory for platform data\n");
+ return NULL;
+ }
+ ret = of_property_read_u32(np, "gpio_switch", &pdata->gpio_switch);
+ if(ret){
+ dev_err(dev, "fail to get gpio_switch\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "gpio_detect", &pdata->gpio_detect);
+ if(ret){
+ dev_err(dev, "fail to get gpio_detect\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "gpio_button", &pdata->gpio_button);
+ if(ret){
+ dev_err(dev, "fail to get gpio_button\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "irq_trigger_level_detect", &pdata->irq_trigger_level_detect);
+ if(ret){
+ dev_err(dev, "fail to get irq_trigger_level_detect\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "irq_trigger_level_button", &pdata->irq_trigger_level_button);
+ if(ret){
+ dev_err(dev, "fail to get irq_trigger_level_button\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "adc_threshold_3pole_detect", &pdata->adc_threshold_3pole_detect);
+ if(ret){
+ dev_err(dev, "fail to get adc_threshold_3pole_detect\n");
+ goto fail;
+ }
+
+ ret = of_property_read_u32(np, "adc_threshold_4pole_detect", &pdata->adc_threshold_4pole_detect);
+ if(ret){
+ dev_err(dev, "fail to get adc_threshold_4pole_detect\n");
+ goto fail;
+ }
+
+ ret = of_property_read_u32(np, "irq_threshold_buttont", &pdata->irq_threshold_buttont);
+ if(ret){
+ dev_err(dev, "fail to get irq_threshold_buttont\n");
+ goto fail;
+ }
+
+ ret = of_property_read_u32(np, "voltage_headmicbias", &pdata->voltage_headmicbias);
+ if(ret){
+ dev_err(dev, "fail to get voltage_headmicbias\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "nbuttons", &pdata->nbuttons);
+ if(ret){
+ dev_err(dev, "fail to get nbuttons\n");
+ goto fail;
+ }
+
+ buttons_data = kzalloc(pdata->nbuttons*sizeof(*buttons_data),GFP_KERNEL);
+ if (!buttons_data) {
+ dev_err(dev, "could not allocate memory for headset_buttons\n");
+ goto fail;
+ }
+ pdata->headset_buttons = buttons_data;
+
+ for_each_child_of_node(np,buttons_np){
+
+ ret = of_property_read_u32(buttons_np, "adc_min", &buttons_data->adc_min);
+ if (ret) {
+ dev_err(dev, "fail to get adc_min\n");
+ goto fail_buttons_data;
+ }
+ ret = of_property_read_u32(buttons_np, "adc_max", &buttons_data->adc_max);
+ if (ret) {
+ dev_err(dev, "fail to get adc_max\n");
+ goto fail_buttons_data;
+ }
+ ret = of_property_read_u32(buttons_np, "code", &buttons_data->code);
+ if (ret) {
+ dev_err(dev, "fail to get code\n");
+ goto fail_buttons_data;
+ }
+ ret = of_property_read_u32(buttons_np, "type", &buttons_data->type);
+ if (ret) {
+ dev_err(dev, "fail to get type\n");
+ goto fail_buttons_data;
+ }
+ printk("device tree data: adc_min = %d adc_max = %d code = %d type = %d \n", buttons_data->adc_min,
+ buttons_data->adc_max, buttons_data->code, buttons_data->type);
+ buttons_data++;
+ };
+
+ return pdata;
+
+fail_buttons_data:
+ kfree(buttons_data);
+ pdata->headset_buttons = NULL;
+fail:
+ kfree(pdata);
+ return NULL;
+}
+#endif
+
+static int headset_detect_probe(struct platform_device *pdev)
+{
+ struct sprd_headset_platform_data *pdata = pdev->dev.platform_data;
+ struct sprd_headset *ht = &headset;
+ unsigned long irqflags = 0;
+ struct input_dev *input_dev = NULL;
+ int i = 0;
+ int ret = -1;
+ int ana_sts0 = 0;
+ int adie_chip_id_low = 0;
+ int adie_chip_id_high = 0;
+
+#ifdef CONFIG_OF
+ struct device_node *np = pdev->dev.of_node;
+ if (pdev->dev.of_node && !pdata){
+ pdata = headset_detect_parse_dt(&pdev->dev);
+ if(pdata)
+ pdev->dev.platform_data = pdata;
+ }
+ if (!pdata) {
+ printk(KERN_WARNING "headset_detect_probe get platform_data NULL\n");
+ ret = -EINVAL;
+ goto fail_to_get_platform_data;
+ }
+#endif
+
+ adie_chip_id_low = sci_adi_read(ANA_CTL_GLB_BASE+ADIE_CHID_LOW);//A-die chip id LOW
+ adie_chip_id_high = sci_adi_read(ANA_CTL_GLB_BASE+ADIE_CHID_HIGH);//A-die chip id HIGH
+
+ sci_adi_write(ANA_REG_GLB_ARM_MODULE_EN, BIT_ANA_AUD_EN, BIT_ANA_AUD_EN);//arm base address:0x40038800 for register accessable
+ ana_sts0 = sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_STS0);//arm base address:0x40038600
+
+ switch (adie_chip_id_high) {
+ case 0x2713: {//chip is 2713
+ switch (adie_chip_id_low) {
+ case 0xA000: {
+ adie_type = 1; //AC
+ break;
+ }
+ case 0xA001: {
+ if ((0x00000040 & ana_sts0) == 0x00000040) {
+ adie_type = 2; //BA
+ break;
+ } else {
+ adie_type = 3; //BB
+ break;
+ }
+ break;
+ }
+ case 0xCA00: {
+ adie_type = 5; //CA
+ break;
+ }
+ default: {
+ adie_type = 100; //unknow
+ PRINT_ERR("unknow 2713 chip ID!!!\n");
+ break;
+ }
+ }
+ PRINT_INFO("adie chip is 2713, type = %d\n", adie_type);
+ break;//break from 2713
+ }
+
+ case 0x2711: {//chip is 2711
+ adie_type = 4; //Dolphin
+ PRINT_INFO("adie chip is 2711, type = %d\n", adie_type);
+ break; //break from 2711
+ }
+
+ default: {
+ adie_type = 100; //unknow
+ PRINT_ERR("unknow adie chip !!!\n");
+ break;
+ }
+ }
+
+ ht->platform_data = pdata;
+ ht->this_pdev = pdev;
+ headset_detect_init();
+
+ ret = sprd_headset_power_init(&pdev->dev);
+ if (ret)
+ goto fail_to_sprd_headset_power_init;
+
+ if(adie_type < 3) {
+ headmicbias_power_on(&pdev->dev, 1);
+ msleep(5);//this time delay is necessary here
+ }
+
+ PRINT_INFO("====================================================================\n");
+ PRINT_INFO(" write 0~3 to \"/sys/kernel/headset/debug_level\" for headset debug\n");
+ PRINT_INFO("====================================================================\n");
+ PRINT_INFO("D-die chip id = 0x%08X\n", __raw_readl(REG_AON_APB_CHIP_ID));
+ PRINT_INFO("A-die chip id HIGH = 0x%08X\n", adie_chip_id_high);
+ PRINT_INFO("A-die chip id LOW = 0x%08X\n", adie_chip_id_low);
+
+ if (1 == adie_type) {
+ pdata->gpio_detect -= 1;
+ PRINT_WARN("use EIC_AUD_HEAD_INST (EIC4, GPIO_%d) for insert detecting\n", pdata->gpio_detect);
+ } else {
+ PRINT_INFO("use GPIO_%d for insert detecting\n", pdata->gpio_detect);
+ }
+
+ if(0 != pdata->gpio_switch) {
+ ret = gpio_request(pdata->gpio_switch, "headset_switch");
+ if (ret < 0) {
+ PRINT_ERR("failed to request GPIO_%d(headset_switch)\n", pdata->gpio_switch);
+ goto failed_to_request_gpio_switch;
+ }
+ }
+ else
+ PRINT_INFO("automatic type switch is unsupported\n");
+
+ ret = gpio_request(pdata->gpio_detect, "headset_detect");
+ if (ret < 0) {
+ PRINT_ERR("failed to request GPIO_%d(headset_detect)\n", pdata->gpio_detect);
+ goto failed_to_request_gpio_detect;
+ }
+
+ ret = gpio_request(pdata->gpio_button, "headset_button");
+ if (ret < 0) {
+ PRINT_ERR("failed to request GPIO_%d(headset_button)\n", pdata->gpio_button);
+ goto failed_to_request_gpio_button;
+ }
+
+ if(0 != pdata->gpio_switch)
+ gpio_direction_output(pdata->gpio_switch, 0);
+ gpio_direction_input(pdata->gpio_detect);
+ gpio_direction_input(pdata->gpio_button);
+ ht->irq_detect = gpio_to_irq(pdata->gpio_detect);
+ ht->irq_button = gpio_to_irq(pdata->gpio_button);
+
+ ret = switch_dev_register(&ht->sdev);
+ if (ret < 0) {
+ PRINT_ERR("switch_dev_register failed!\n");
+ goto failed_to_register_switch_dev;
+ }
+
+ input_dev = input_allocate_device();
+ if ( !input_dev) {
+ PRINT_ERR("input_allocate_device for headset_button failed!\n");
+ ret = -ENOMEM;
+ goto failed_to_allocate_input_device;
+ }
+
+ input_dev->name = "headset-keyboard";
+ input_dev->id.bustype = BUS_HOST;
+ ht->input_dev = input_dev;
+
+ for (i = 0; i < pdata->nbuttons; i++) {
+ struct headset_buttons *buttons = &pdata->headset_buttons[i];
+ unsigned int type = buttons->type ?: EV_KEY;
+ input_set_capability(input_dev, type, buttons->code);
+ }
+ /* < add by lgd */
+ #ifdef CONFIG_CAMERA_POLE_SUPPORT
+ input_set_capability(input_dev, EV_KEY, CAMERA_POLE_CODE);
+ #endif
+ /* add by lgd > */
+ ret = input_register_device(input_dev);
+ if (ret) {
+ PRINT_ERR("input_register_device for headset_button failed!\n");
+ goto failed_to_register_input_device;
+ }
+
+ sema_init(&headset_sem, 1);
+
+ INIT_WORK(&ht->work_button, headset_button_work_func);
+ ht->button_work_queue = create_singlethread_workqueue("headset_button");
+ if(ht->button_work_queue == NULL) {
+ PRINT_ERR("create_singlethread_workqueue for headset_button failed!\n");
+ goto failed_to_create_singlethread_workqueue_for_headset_button;
+ }
+
+ INIT_DELAYED_WORK(&ht->work_detect, headset_detect_work_func);
+ ht->detect_work_queue = create_singlethread_workqueue("headset_detect");
+ if(ht->detect_work_queue == NULL) {
+ PRINT_ERR("create_singlethread_workqueue for headset_detect failed!\n");
+ goto failed_to_create_singlethread_workqueue_for_headset_detect;
+ }
+
+ /***polling ana_sts0 to avoid the hardware defect***/
+#ifdef SPRD_STS_POLLING_EN
+ INIT_DELAYED_WORK(&sts_check_work, headset_sts_check_func);
+ sts_check_work_queue = create_singlethread_workqueue("headset_sts_check");
+ if(sts_check_work_queue == NULL) {
+ PRINT_ERR("create_singlethread_workqueue for headset_sts_check failed!\n");
+ goto failed_to_create_singlethread_workqueue_for_headset_sts_check;
+ }
+#endif
+ /***polling ana_sts0 to avoid the hardware defect***/
+
+ INIT_DELAYED_WORK(&reg_dump_work, reg_dump_func);
+ reg_dump_work_queue = create_singlethread_workqueue("headset_reg_dump");
+ if(reg_dump_work_queue == NULL) {
+ PRINT_ERR("create_singlethread_workqueue for headset_reg_dump failed!\n");
+ goto failed_to_create_singlethread_workqueue_for_headset_reg_dump;
+ }
+ if (debug_level >= 2)
+ queue_delayed_work(reg_dump_work_queue, &reg_dump_work, msecs_to_jiffies(500));
+ /* < add by lgd */
+#ifdef CONFIG_CAMERA_POLE_SUPPORT
+ INIT_DELAYED_WORK(&camera_pole_work, camera_pole_func);
+ camera_pole_work_queue = create_singlethread_workqueue("camera_pole");
+ if(NULL == camera_pole_work_queue) {
+ PRINT_ERR("create_singlethread_workqueue for camera_pole failed!\n");
+ goto failed_to_create_singlethread_workqueue_for_camera_pole;
+ }
+#endif
+ /* add by lgd > */
+#ifdef ADPGAR_BYP_SELECT
+ //work queue for Bug#298417
+ INIT_DELAYED_WORK(&adpgar_byp_select_work, adpgar_byp_select_func);
+ adpgar_byp_select_work_queue = create_singlethread_workqueue("adpgar_byp_select");
+ if(adpgar_byp_select_work_queue == NULL) {
+ PRINT_ERR("create_singlethread_workqueue for adpgar_byp_select failed!\n");
+ goto failed_to_create_singlethread_workqueue_for_adpgar_byp_select;
+ }
+ PRINT_INFO("ADPGAR_BYP_SELECT is enabled!\n");
+#endif
+
+ wake_lock_init(&headset_detect_wakelock, WAKE_LOCK_SUSPEND, "headset_detect_wakelock");
+ wake_lock_init(&headset_button_wakelock, WAKE_LOCK_SUSPEND, "headset_button_wakelock");
+
+ //set EIC3 de-bounce time for button irq
+ headset_reg_set_val((ANA_EIC_BASE+EIC3_DBNC_CTRL), DBNC_CNT3_VALUE, DBNC_CNT3_MASK, DBNC_CNT3_SHIFT);
+ //set EIC5 de-bounce time for inser irq
+ headset_reg_set_val((ANA_EIC_BASE+EIC5_DBNC_CTRL), DBNC_CNT5_VALUE, DBNC_CNT5_MASK, DBNC_CNT5_SHIFT);
+
+ irqflags = pdata->irq_trigger_level_button ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW;
+ ret = request_irq(ht->irq_button, headset_button_irq_handler, irqflags | IRQF_NO_SUSPEND, "headset_button", ht);
+ if (ret) {
+ PRINT_ERR("failed to request IRQ_%d(GPIO_%d)\n", ht->irq_button, pdata->gpio_button);
+ goto failed_to_request_irq_headset_button;
+ }
+ headset_irq_button_enable(0, ht->irq_button);//disable button irq before headset detected
+ headset_button_hardware_bug_fix_stage = 0;
+
+ irqflags = pdata->irq_trigger_level_detect ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW;
+ ret = request_irq(ht->irq_detect, headset_detect_irq_handler, irqflags | IRQF_NO_SUSPEND, "headset_detect", ht);
+ if (ret < 0) {
+ PRINT_ERR("failed to request IRQ_%d(GPIO_%d)\n", ht->irq_detect, pdata->gpio_detect);
+ goto failed_to_request_irq_headset_detect;
+ }
+
+ ret = headset_debug_sysfs_init();
+
+ PRINT_INFO("headset_detect_probe success\n");
+ return ret;
+
+failed_to_request_irq_headset_detect:
+ free_irq(ht->irq_button, ht);
+failed_to_request_irq_headset_button:
+
+#ifdef ADPGAR_BYP_SELECT
+ cancel_delayed_work_sync(&adpgar_byp_select_work);
+ destroy_workqueue(adpgar_byp_select_work_queue);
+failed_to_create_singlethread_workqueue_for_adpgar_byp_select:
+#endif
+
+/* < add by lgd */
+#ifdef CONFIG_CAMERA_POLE_SUPPORT
+ cancel_delayed_work_sync(&camera_pole_work);
+ destroy_workqueue(camera_pole_work_queue);
+failed_to_create_singlethread_workqueue_for_camera_pole:
+#endif
+/* add by lgd > */
+
+ cancel_delayed_work_sync(&reg_dump_work);
+ destroy_workqueue(reg_dump_work_queue);
+failed_to_create_singlethread_workqueue_for_headset_reg_dump:
+
+#ifdef SPRD_STS_POLLING_EN
+ destroy_workqueue(sts_check_work_queue);
+failed_to_create_singlethread_workqueue_for_headset_sts_check:
+#endif
+
+ destroy_workqueue(ht->detect_work_queue);
+failed_to_create_singlethread_workqueue_for_headset_detect:
+ destroy_workqueue(ht->button_work_queue);
+failed_to_create_singlethread_workqueue_for_headset_button:
+ input_unregister_device(input_dev);
+failed_to_register_input_device:
+ input_free_device(input_dev);
+failed_to_allocate_input_device:
+ switch_dev_unregister(&ht->sdev);
+failed_to_register_switch_dev:
+ gpio_free(pdata->gpio_button);
+failed_to_request_gpio_button:
+ gpio_free(pdata->gpio_detect);
+failed_to_request_gpio_detect:
+ if(0 != pdata->gpio_switch)
+ gpio_free(pdata->gpio_switch);
+failed_to_request_gpio_switch:
+ headmicbias_power_on(&pdev->dev, 0);
+fail_to_sprd_headset_power_init:
+fail_to_get_platform_data:
+ PRINT_ERR("headset_detect_probe failed\n");
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int headset_suspend(struct platform_device *dev, pm_message_t state)
+{
+ PRINT_INFO("suspend (det_irq = %d, but_irq = %d, plug_state_last = %d)\n",
+ headset.irq_detect, headset.irq_button, plug_state_last);
+ return 0;
+}
+
+static int headset_resume(struct platform_device *dev)
+{
+ PRINT_INFO("resume (det_irq = %d, but_irq = %d, plug_state_last = %d)\n",
+ headset.irq_detect, headset.irq_button, plug_state_last);
+ return 0;
+}
+#else
+#define headset_suspend NULL
+#define headset_resume NULL
+#endif
+
+#ifdef CONFIG_HEADSET_AUXADC_CAL_SUPPORTED
+#define SPRD_HEADSET_AUXADC_CAL_NO 0
+#define SPRD_HEADSET_AUXADC_CAL_NV 1
+#define SPRD_HEADSET_AUXADC_CAL_CHIP 2
+
+struct sprd_headset_auxadc_cal {
+ uint16_t p0_vol; //900mV
+ uint16_t p0_adc;
+ uint16_t p1_vol; //300mV
+ uint16_t p1_adc;
+ uint16_t cal_type;
+};
+
+static struct sprd_headset_auxadc_cal adc_cal = {
+ 0, 0,
+ 0, 0,
+ SPRD_HEADSET_AUXADC_CAL_NO,
+};
+
+static void adc_cal_from_efuse(void);
+extern int sci_efuse_get_cal_v3(unsigned int * pdata, int num);
+
+static int adc_get_ideal(int adc_mic)
+{
+ int32_t A = 0;
+ int32_t B = 0;
+ int32_t temp1 = 0;
+ int32_t temp2 = 0;
+ int32_t temp3 = 0;
+ int32_t adc_ideal = -1;
+ PRINT_INFO("adc_get_ideal headset enter into \n");
+ if (SPRD_HEADSET_AUXADC_CAL_CHIP == adc_cal.cal_type) {
+ A = adc_cal.p1_adc;
+ B = adc_cal.p0_adc;
+
+ temp1 = 18 * adc_mic - 27 * A + 9 * B;
+ temp2 = B - A;
+ temp3 = 109 * temp1;
+ adc_ideal = temp3 / temp2;
+
+ adc_ideal = (adc_ideal <= 0) ? 0 : adc_ideal;
+
+ PRINT_INFO("Calibration data from efuse, adc_mic=%d, adc_ideal=%d\n", adc_mic, adc_ideal);
+ } else {
+ PRINT_ERR("The chip and NV do not calibrate ADC!\n");
+ }
+
+ return adc_ideal;
+}
+
+static void adc_cal_from_efuse(void)
+{
+ int ret;
+ unsigned int efuse_cal_data[2] = {0};
+
+ PRINT_INFO("getting calibration data from efuse ...\n");
+
+ if (adc_cal.cal_type != SPRD_HEADSET_AUXADC_CAL_CHIP) {
+ if (!(sci_efuse_get_cal_v3(efuse_cal_data,2))) {
+ if(((efuse_cal_data[0] >> 16) & 0xffff - (efuse_cal_data[1] >> 16) & 0xffff == 0)) {
+ PRINT_ERR("ADC is equal to two points, So calibrate from efuse error!\n");
+ return;
+ }
+
+ adc_cal.p0_vol = efuse_cal_data[0] & 0xffff;
+ adc_cal.p0_adc = (efuse_cal_data[0] >> 16) & 0xffff;
+ adc_cal.p1_vol = efuse_cal_data[1] & 0xffff;
+ adc_cal.p1_adc = (efuse_cal_data[1] >> 16) & 0xffff;
+
+ adc_cal.cal_type = SPRD_HEADSET_AUXADC_CAL_CHIP;
+
+ PRINT_INFO("efuse_cal_data[0]=0x%08x\n", efuse_cal_data[0]);
+ PRINT_INFO("efuse_cal_data[1]=0x%08x\n", efuse_cal_data[1]);
+
+ PRINT_INFO("========auxadc cal from efuse=========\n");
+ PRINT_INFO(" ADC VOL\n");
+ PRINT_INFO("p0: %4d %4d\n", adc_cal.p0_adc, adc_cal.p0_vol);
+ PRINT_INFO("p1: %4d %4d\n", adc_cal.p1_adc, adc_cal.p1_vol);
+ PRINT_INFO("========auxadc cal from efuse=========\n");
+
+ PRINT_INFO("calibrate from efuse success!\n");
+ } else {
+ PRINT_ERR("calibrate from efuse failed!\n");
+ }
+ } else {
+ PRINT_INFO("no need to calibrate from efuse!\n");
+ }
+}
+
+#endif
+
+static const struct of_device_id headset_detect_of_match[] = {
+ {.compatible = "sprd,headset-detect",},
+ { }
+};
+static struct platform_driver headset_detect_driver = {
+ .driver = {
+ .name = "headset-detect",
+ .owner = THIS_MODULE,
+ .of_match_table = headset_detect_of_match,
+ },
+ .probe = headset_detect_probe,
+ .suspend = headset_suspend,
+ .resume = headset_resume,
+};
+
+static int __init headset_init(void)
+{
+ int ret;
+#ifdef CONFIG_HEADSET_AUXADC_CAL_SUPPORTED
+ adc_cal_from_efuse();
+#endif
+ ret = platform_driver_register(&headset_detect_driver);
+ return ret;
+}
+
+static void __exit headset_exit(void)
+{
+ sprd_headset_power_deinit();
+ platform_driver_unregister(&headset_detect_driver);
+}
+
+late_initcall(headset_init);
+module_exit(headset_exit);
+
+MODULE_DESCRIPTION("headset & button detect driver");
+MODULE_AUTHOR("Yaochuan Li <yaochuan.li@spreadtrum.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/headset_sprd_sc2723.c b/drivers/input/misc/headset_sprd_sc2723.c
new file mode 100644
index 00000000..3c210d31
--- /dev/null
+++ b/drivers/input/misc/headset_sprd_sc2723.c
@@ -0,0 +1,1920 @@
+/*
+ * Copyright (C) 2013 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <soc/sprd/gpio.h>
+#include <linux/headset_sprd_sc2723.h>
+#include <soc/sprd/board.h>
+#include <linux/input.h>
+
+#include <soc/sprd/adc.h>
+#include <asm/io.h>
+#include <soc/sprd/hardware.h>
+#include <soc/sprd/adi.h>
+#include <linux/module.h>
+#include <linux/wakelock.h>
+
+#include <linux/regulator/consumer.h>
+#include <soc/sprd/regulator.h>
+#include <soc/sprd/sci_glb_regs.h>
+#include <soc/sprd/arch_misc.h>
+#include <linux/notifier.h>
+#ifdef CONFIG_OF
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#endif
+#include <asm/div64.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+
+//==================== debug ====================
+#define ENTER \
+do{ if(debug_level >= 1) printk(KERN_INFO "[SPRD_HEADSET_DBG][%d] func: %s line: %04d\n", adie_type, __func__, __LINE__); }while(0)
+
+#define PRINT_DBG(format,x...) \
+do{ if(debug_level >= 1) printk(KERN_INFO "[SPRD_HEADSET_DBG][%d] " format, adie_type, ## x); }while(0)
+
+#define PRINT_INFO(format,x...) \
+do{ printk(KERN_INFO "[SPRD_HEADSET_INFO][%d] " format, adie_type, ## x); }while(0)
+
+#define PRINT_WARN(format,x...) \
+do{ printk(KERN_INFO "[SPRD_HEADSET_WARN][%d] " format, adie_type, ## x); }while(0)
+
+#define PRINT_ERR(format,x...) \
+do{ printk(KERN_ERR "[SPRD_HEADSET_ERR][%d] func: %s line: %04d info: " format, adie_type, __func__, __LINE__, ## x); }while(0)
+//==================== debug ====================
+
+/**********************************************
+ * The micro ADPGAR_BYP_SELECT is used for avoiding the Bug#298417,
+ * please refer to Bug#298417 to confirm your configuration.
+ **********************************************/
+#define ADPGAR_BYP_SELECT
+
+#define PLUG_CONFIRM_COUNT (1)
+#define NO_MIC_RETRY_COUNT (0)
+#define ADC_READ_COUNT (5)
+#define ADC_READ_LOOP (2)
+#define ADC_GND (300)
+#define SCI_ADC_GET_VALUE_COUNT (50)
+
+#define EIC3_DBNC_CTRL (0x4C)
+#define SW_DBNC_VALUE (10)
+#define DBNC_CNT3_VALUE (30)
+#define DBNC_CNT3_SHIFT (0)
+#define DBNC_CNT3_MASK (0xFFF << DBNC_CNT3_SHIFT)
+
+#define EIC5_DBNC_CTRL (0x54)
+#define DBNC_CNT5_VALUE (25)
+#define DBNC_CNT5_SHIFT (0)
+#define DBNC_CNT5_MASK (0xFFF << DBNC_CNT5_SHIFT)
+
+
+#define EIC8_DBNC_CTRL (0x60)
+#define DBNC_CNT8_VALUE (25)
+#define DBNC_CNT8_SHIFT (0)
+#define DBNC_CNT8_MASK (0xFFF << DBNC_CNT8_SHIFT)
+
+#define CLK_AUD_HID_EN (BIT(4))
+#define CLK_AUD_HBD_EN (BIT(3))
+
+#define HEADMIC_DETECT_BASE (ANA_AUDCFGA_INT_BASE)
+#define HEADMIC_DETECT_REG(X) (HEADMIC_DETECT_BASE + (X))
+
+#define HEADMIC_DETECT_GLB_REG(X) (ANA_REGS_GLB_BASE + (X))
+#define HDT_EN (BIT(5))
+#define AUD_EN (BIT(4))
+
+//no use
+#define HEADMIC_BUTTON_BASE (ANA_HDT_INT_BASE)
+#define HEADMIC_BUTTON_REG(X) (HEADMIC_BUTTON_BASE + (X))
+#define HID_CFG0 (0x0080)
+#define HID_CFG2 (0x0088)
+#define HID_CFG3 (0x008C)
+#define HID_CFG4 (0x0090)
+//no use
+
+#define ANA_PMU0 (0x0000)
+#define ANA_CDC1 (0x0020)
+#define ANA_HDT0 (0x0068)
+#define ANA_STS0 (0x0074)
+
+#define AUDIO_HEADMIC_SLEEP_EN (BIT(6))
+
+#define AUDIO_ADPGAR_BYP_SHIFT (10)
+#define AUDIO_ADPGAR_BYP_MASK (0x3 << AUDIO_ADPGAR_BYP_SHIFT)
+#define AUDIO_ADPGAR_BYP_NORMAL (0)
+#define AUDIO_ADPGAR_BYP_ADC_PGAR1_2_ADCR (1)
+#define AUDIO_ADPGAR_BYP_HEADMIC_2_ADCR (2)
+#define AUDIO_ADPGAR_BYP_ALL_DISCONNECTED (3)
+
+#define AUDIO_HEAD_SDET_SHIFT (5)
+#define AUDIO_HEAD_SDET_MASK (0x3 << AUDIO_HEAD_SDET_SHIFT)
+#define AUDIO_HEAD_SDET_2P1 (3)
+#define AUDIO_HEAD_SDET_2P3 (2)
+#define AUDIO_HEAD_SDET_2P5 (1)
+#define AUDIO_HEAD_SDET_2P7 (0)
+
+#define AUDIO_HEAD_INS_VREF_SHIFT (8)
+#define AUDIO_HEAD_INS_VREF_MASK (0x3 << AUDIO_HEAD_INS_VREF_SHIFT)
+#define AUDIO_HEAD_INS_VREF_2P1 (3)
+#define AUDIO_HEAD_INS_VREF_2P3 (2)
+#define AUDIO_HEAD_INS_VREF_2P5 (1)
+#define AUDIO_HEAD_INS_VREF_2P7 (0)
+
+#define AUDIO_HEAD_SBUT_SHIFT (1)
+#define AUDIO_HEAD_SBUT_MASK (0xF << AUDIO_HEAD_SBUT_SHIFT)
+
+#define AUDIO_HEAD_INS_PD (BIT(10))
+#define AUDIO_HEADDETECT_PD (BIT(7))
+
+#define AUDIO_V2ADC_EN (BIT(15))
+#define AUDIO_HEAD2ADC_SEL_SHIFT (11)
+#define AUDIO_HEAD2ADC_SEL_MASK (0xF << AUDIO_HEAD2ADC_SEL_SHIFT)
+#define AUDIO_HEAD2ADC_SEL_DISABLE (0)
+#define AUDIO_HEAD2ADC_SEL_HEADMIC_IN (1)
+#define AUDIO_HEAD2ADC_SEL_HEADSET_L_INT (3)
+#define AUDIO_HEAD2ADC_SEL_HEAD_DRO_L (4)
+
+#define AUDIO_HEAD_BUTTON (BIT(11))
+#define AUDIO_HEAD_INSERT (BIT(10))
+#define AUDIO_HEAD_INSERT_2 (BIT(9))
+
+#define AUDIO_PMUR1 (BIT(0))
+#define ANA_PMU1 (0x0004)
+
+#define ABS(x) (((x) < (0)) ? (-(x)) : (x))
+#define MAX(x,y) (((x) > (y)) ? (x) : (y))
+
+#define headset_reg_read(addr) \
+ do { \
+ sci_adi_read(addr); \
+} while(0)
+
+#define headset_reg_set_val(addr, val, mask, shift) \
+ do { \
+ uint32_t temp; \
+ temp = sci_adi_read(addr); \
+ temp = (temp & (~mask)) | ((val) << (shift)); \
+ sci_adi_raw_write(addr, temp); \
+ } while(0)
+
+#define headset_reg_clr_bit(addr, bit) \
+ do { \
+ uint32_t temp; \
+ temp = sci_adi_read(addr); \
+ temp = temp & (~bit); \
+ sci_adi_raw_write(addr, temp); \
+ } while(0)
+
+#define headset_reg_set_bit(addr, bit) \
+ do { \
+ uint32_t temp; \
+ temp = sci_adi_read(addr); \
+ temp = temp | bit; \
+ sci_adi_raw_write(addr, temp); \
+ } while(0)
+
+static inline uint32_t headset_reg_get_bit(uint32_t addr, uint32_t bit)
+{
+ uint32_t temp;
+ temp = sci_adi_read(addr);
+ temp = temp & bit;
+ return temp;
+}
+
+typedef enum sprd_headset_type {
+ HEADSET_4POLE_NORMAL,
+ HEADSET_NO_MIC,
+ HEADSET_4POLE_NOT_NORMAL,
+ HEADSET_APPLE,
+ HEADSET_TYPE_ERR = -1,
+} SPRD_HEADSET_TYPE;
+
+static struct delayed_work reg_dump_work;
+static struct workqueue_struct *reg_dump_work_queue;
+
+#ifdef ADPGAR_BYP_SELECT
+static struct delayed_work adpgar_byp_select_work;
+static struct workqueue_struct *adpgar_byp_select_work_queue;
+#endif
+
+struct sprd_headset_auxadc_cal_l {
+ u32 A;
+ u32 B;
+ u32 E;
+ u32 cal_type;
+};
+
+#define SPRD_HEADSET_AUXADC_CAL_NO 0
+#define SPRD_HEADSET_AUXADC_CAL_DO 1
+
+static struct sprd_headset_auxadc_cal_l adc_cal_headset = {
+ 0, 0,0,SPRD_HEADSET_AUXADC_CAL_NO,
+};
+
+
+
+static DEFINE_SPINLOCK(irq_button_lock);
+static DEFINE_SPINLOCK(irq_detect_lock);
+static DEFINE_SPINLOCK(irq_detect_mic_lock);
+
+static int detect_err_count = 0;
+static int debug_level = 0;
+static int adie_type = -1;
+static int gpio_detect_value_last = 0;
+static int gpio_button_value_last = 0;
+static volatile int button_state_last = 0; //0==released, 1==pressed
+static int current_key_code = KEY_RESERVED;
+static int plug_state_last = 0; //if the hardware detected the headset is plug in, set plug_state_last = 1
+static struct wake_lock headset_detect_wakelock;
+static struct wake_lock headset_button_wakelock;
+static struct semaphore headset_sem;
+static struct platform_device *this_pdev = NULL;
+static struct sprd_headset headset = {
+ .sdev = {
+ .name = "h2w",
+ },
+};
+
+//======================== audio codec ========================
+static struct sprd_headset_power {
+ struct regulator *head_mic;
+ struct regulator *vcom_buf;
+ struct regulator *vbo;
+} sprd_hts_power;
+
+static int sprd_headset_power_get(struct device *dev,
+ struct regulator **regu, const char *id)
+{
+ if (!*regu) {
+ *regu = regulator_get(dev, id);
+ if (IS_ERR(*regu)) {
+ pr_err("ERR:Failed to request %ld: %s\n", PTR_ERR(*regu), id);
+ *regu = 0;
+ return PTR_ERR(*regu);
+ }
+ }
+ return 0;
+}
+
+static int sprd_headset_power_init(struct device *dev)
+{
+ int ret = 0;
+ ret = sprd_headset_power_get(dev, &sprd_hts_power.head_mic, "HEADMICBIAS");
+ if (ret || (sprd_hts_power.head_mic == NULL)) {
+ sprd_hts_power.head_mic = 0;
+ return ret;
+ }
+ regulator_set_voltage(sprd_hts_power.head_mic, 950000, 950000);
+
+ ret = sprd_headset_power_get(dev, &sprd_hts_power.vcom_buf, "VCOM_BUF");
+ if (ret) {
+ sprd_hts_power.vcom_buf = 0;
+ goto __err1;
+ }
+
+ ret = sprd_headset_power_get(dev, &sprd_hts_power.vbo, "VBO");
+ if (ret) {
+ sprd_hts_power.vbo = 0;
+ goto __err2;
+ }
+
+ goto __ok;
+__err2:
+ regulator_put(sprd_hts_power.vcom_buf);
+__err1:
+ regulator_put(sprd_hts_power.head_mic);
+__ok:
+ return ret;
+}
+
+static void sprd_headset_power_deinit(void)
+{
+ regulator_put(sprd_hts_power.head_mic);
+ regulator_put(sprd_hts_power.vcom_buf);
+ regulator_put(sprd_hts_power.vbo);
+}
+
+static int sprd_headset_audio_block_is_running(struct device *dev)
+{
+ return regulator_is_enabled(sprd_hts_power.vcom_buf)
+ || regulator_is_enabled(sprd_hts_power.vbo);
+}
+
+static int sprd_headset_audio_headmic_sleep_disable(struct device *dev, int on)
+{
+ int ret = 0;
+ if (!sprd_hts_power.head_mic) {
+ return -1;
+ }
+
+ if (on) {
+ ret = regulator_enable(sprd_hts_power.vbo);
+ ret = regulator_set_mode(sprd_hts_power.head_mic, REGULATOR_MODE_NORMAL);
+ } else {
+ ret = regulator_disable(sprd_hts_power.vbo);
+ if (sprd_headset_audio_block_is_running(dev)) {
+ ret = regulator_set_mode(sprd_hts_power.head_mic, REGULATOR_MODE_NORMAL);
+ } else {
+ ret = regulator_set_mode(sprd_hts_power.head_mic, REGULATOR_MODE_STANDBY);
+ }
+ }
+ return ret;
+}
+
+static int sprd_headset_headmic_bias_control(struct device *dev, int on)
+{
+ int ret = 0;
+ if (!sprd_hts_power.head_mic) {
+ return -1;
+ }
+ if (on) {
+ ret = regulator_enable(sprd_hts_power.head_mic);
+ } else {
+ ret = regulator_disable(sprd_hts_power.head_mic);
+ }
+ if (!ret) {
+ /* Set HEADMIC_SLEEP when audio block closed */
+ if (sprd_headset_audio_block_is_running(dev)) {
+ ret = regulator_set_mode(sprd_hts_power.head_mic, REGULATOR_MODE_NORMAL);
+ } else {
+ ret = regulator_set_mode(sprd_hts_power.head_mic, REGULATOR_MODE_STANDBY);
+ }
+ }
+ return ret;
+}
+
+static BLOCKING_NOTIFIER_HEAD(hp_chain_list);
+int hp_register_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_register(&hp_chain_list, nb);
+}
+EXPORT_SYMBOL(hp_register_notifier);
+
+int hp_unregister_notifier(struct notifier_block *nb)
+{
+ return blocking_notifier_chain_unregister(&hp_chain_list, nb);
+}
+EXPORT_SYMBOL(hp_unregister_notifier);
+
+#if 0
+static int hp_notifier_call_chain(unsigned long val)
+{
+ return (blocking_notifier_call_chain(&hp_chain_list, val, NULL)
+ == NOTIFY_BAD) ? -EINVAL : 0;
+}
+#endif
+
+int get_hp_plug_state(void)
+{
+ return !!plug_state_last;
+}
+EXPORT_SYMBOL(get_hp_plug_state);
+//======================== audio codec ========================
+
+static int wrap_sci_adc_get_value(unsigned int channel, int scale)
+{
+ int count = 0;
+ int average = 0;
+
+ while(count < SCI_ADC_GET_VALUE_COUNT) {
+ average += sci_adc_get_value(channel, scale);
+ count++;
+ }
+ average /= SCI_ADC_GET_VALUE_COUNT;
+
+ return average;
+}
+
+/* on = 0: open headmic detect circuit */
+static void headset_detect_circuit(unsigned on)
+{
+ if (on) {
+ headset_reg_clr_bit(HEADMIC_DETECT_REG(ANA_HDT0), AUDIO_HEAD_INS_PD);
+ headset_reg_clr_bit(HEADMIC_DETECT_REG(ANA_HDT0), AUDIO_HEADDETECT_PD);
+ } else {
+ headset_reg_set_bit(HEADMIC_DETECT_REG(ANA_HDT0), AUDIO_HEAD_INS_PD);
+ headset_reg_set_bit(HEADMIC_DETECT_REG(ANA_HDT0), AUDIO_HEADDETECT_PD);
+ }
+}
+
+static void headset_detect_clk_en(void)
+{
+ //address:0x4003_8800+0x00
+ headset_reg_set_bit(HEADMIC_DETECT_GLB_REG(0x00), (HDT_EN | AUD_EN));
+ //address:0x4003_8800+0x04
+ headset_reg_set_bit(HEADMIC_DETECT_GLB_REG(0x04), (CLK_AUD_HID_EN | CLK_AUD_HBD_EN));
+}
+
+static void headset_detect_init(void)
+{
+ headset_detect_clk_en();
+ /* set headset detect voltage */
+ headset_reg_set_bit(HEADMIC_DETECT_REG(ANA_PMU1), AUDIO_PMUR1);
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_HDT0), AUDIO_HEAD_SDET_2P5, AUDIO_HEAD_SDET_MASK, AUDIO_HEAD_SDET_SHIFT);
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_HDT0), AUDIO_HEAD_INS_VREF_2P1, AUDIO_HEAD_INS_VREF_MASK, AUDIO_HEAD_INS_VREF_SHIFT);
+}
+
+static void headmic_sleep_disable(struct device *dev, int on);
+
+static void headset_adc_en(int en)
+{
+ if (en) {
+ headset_reg_set_bit(HEADMIC_DETECT_REG(ANA_HDT0), AUDIO_V2ADC_EN);
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_HDT0), AUDIO_HEAD2ADC_SEL_HEADMIC_IN, AUDIO_HEAD2ADC_SEL_MASK, AUDIO_HEAD2ADC_SEL_SHIFT);
+ headmic_sleep_disable(&this_pdev->dev, 1);
+ } else {
+ if(debug_level <= 2) {
+ headset_reg_clr_bit(HEADMIC_DETECT_REG(ANA_HDT0), AUDIO_V2ADC_EN);
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_HDT0), AUDIO_HEAD2ADC_SEL_DISABLE, AUDIO_HEAD2ADC_SEL_MASK, AUDIO_HEAD2ADC_SEL_SHIFT);
+ }
+ headmic_sleep_disable(&this_pdev->dev, 0);
+ }
+}
+
+/* is_set = 1, headset_mic to AUXADC */
+static void set_adc_to_headmic(unsigned is_set)
+{
+ headset_adc_en(1);
+
+ if (is_set) {
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_HDT0), AUDIO_HEAD2ADC_SEL_HEADMIC_IN, AUDIO_HEAD2ADC_SEL_MASK, AUDIO_HEAD2ADC_SEL_SHIFT);
+ } else {
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_HDT0), AUDIO_HEAD2ADC_SEL_HEADSET_L_INT, AUDIO_HEAD2ADC_SEL_MASK, AUDIO_HEAD2ADC_SEL_SHIFT);
+ }
+}
+
+static void headset_button_irq_threshold(int enable)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ int audio_head_sbut = 0;
+
+ audio_head_sbut = pdata->irq_threshold_buttont;
+
+ if (enable)
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_HDT0), audio_head_sbut, AUDIO_HEAD_SBUT_MASK, AUDIO_HEAD_SBUT_SHIFT);
+ else
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_HDT0), 0xF, AUDIO_HEAD_SBUT_MASK, AUDIO_HEAD_SBUT_SHIFT);
+}
+
+static void headset_irq_button_enable(int enable, unsigned int irq)
+{
+ unsigned long spin_lock_flags;
+ static int current_irq_state = 1;//irq is enabled after request_irq()
+ spin_lock_irqsave(&irq_button_lock, spin_lock_flags);
+ if (1 == enable) {
+ if (0 == current_irq_state) {
+ enable_irq(irq);
+ current_irq_state = 1;
+ }
+ } else {
+ if (1 == current_irq_state) {
+ disable_irq_nosync(irq);
+ current_irq_state = 0;
+ }
+ }
+ spin_unlock_irqrestore(&irq_button_lock, spin_lock_flags);
+ return;
+}
+
+static void headset_irq_detect_enable(int enable, unsigned int irq)
+{
+ unsigned long spin_lock_flags;
+ static int current_irq_state = 1;//irq is enabled after request_irq()
+ spin_lock_irqsave(&irq_detect_lock, spin_lock_flags);
+ if (1 == enable) {
+ if (0 == current_irq_state) {
+ enable_irq(irq);
+ current_irq_state = 1;
+ }
+ } else {
+ if (1 == current_irq_state) {
+ disable_irq_nosync(irq);
+ current_irq_state = 0;
+ }
+ }
+ spin_unlock_irqrestore(&irq_detect_lock, spin_lock_flags);
+ return;
+}
+
+static void headset_irq_detect_mic_enable(int enable, unsigned int irq)
+{
+ unsigned long spin_lock_flags;
+ static int current_irq_state = 1;
+ spin_lock_irqsave(&irq_detect_mic_lock, spin_lock_flags);
+ if (1 == enable) {
+ if (0 == current_irq_state) {
+ enable_irq(irq);
+ current_irq_state = 1;
+ }
+ } else {
+ if (1 == current_irq_state) {
+ disable_irq_nosync(irq);
+ current_irq_state = 0;
+ }
+ }
+ spin_unlock_irqrestore(&irq_detect_mic_lock, spin_lock_flags);
+ return;
+}
+
+static void headmic_sleep_disable(struct device *dev, int on)
+{
+ static int current_power_state = 0;
+
+ if (1 == on) {
+ if (0 == current_power_state) {
+ sprd_headset_audio_headmic_sleep_disable(dev, 1);
+ current_power_state = 1;
+ }
+ } else {
+ if (1 == current_power_state) {
+ sprd_headset_audio_headmic_sleep_disable(dev, 0);
+ current_power_state = 0;
+ }
+ }
+
+ return;
+}
+
+static void headmicbias_power_on(struct device *dev, int on)
+{
+ static int current_power_state = 0;
+ struct sprd_headset *ht = &headset;
+
+ if (1 == on) {
+ if (0 == current_power_state) {
+ if(NULL != ht->platform_data->external_headmicbias_power_on)
+ ht->platform_data->external_headmicbias_power_on(1);
+ sprd_headset_headmic_bias_control(dev, 1);
+ current_power_state = 1;
+ }
+ } else {
+ if (1 == current_power_state) {
+ if(NULL != ht->platform_data->external_headmicbias_power_on)
+ ht->platform_data->external_headmicbias_power_on(0);
+ sprd_headset_headmic_bias_control(dev, 0);
+ current_power_state = 0;
+ }
+ }
+
+ return;
+}
+
+static int ana_sts0_confirm(void)
+{
+ if (0 == headset_reg_get_bit(HEADMIC_DETECT_REG(ANA_STS0), AUDIO_HEAD_INSERT))
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ return 0;
+#else
+ return 1;
+#endif
+ else
+ return 1;
+}
+
+static int adc_compare(int x1, int x2)
+{
+ int delta = 0;
+ int max = 0;
+
+ if((x1<300) && (x2<300))
+ return 1;
+
+ x1 = ((0==x1) ? 1 : (x1*100));
+ x2 = ((0==x2) ? 1 : (x2*100));
+
+ delta = ABS(x1-x2);
+ max = MAX(x1, x2);
+
+ if(delta < ((max*10)/100))
+ return 1;
+ else
+ return 0;
+}
+
+static int adc_get_average(int gpio_num, int gpio_value)
+{
+ int i = 0;
+ int j = 0;
+ int k = 0;
+ int adc_average = 0;
+ int success = 1;
+ int adc[ADC_READ_COUNT] = {0};
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+
+ //================= debug =====================
+ if(debug_level >= 3) {
+ int count = 0;
+ int adc_val = 0;
+ int gpio_button = 0;
+ int ana_pmu0 = 0;
+ int ana_cfg1 = 0;
+ int ana_hdt0 = 0;
+ int ana_sts0 = 0;
+
+ while(count < 20) {
+ count++;
+ adc_val = wrap_sci_adc_get_value(ADC_CHANNEL_HEADMIC, 0);
+ gpio_button = gpio_get_value(pdata->gpio_button);
+ ana_pmu0 = sci_adi_read(HEADMIC_DETECT_REG(ANA_PMU0));
+ ana_hdt0 = sci_adi_read(HEADMIC_DETECT_REG(ANA_HDT0));
+ ana_sts0 = sci_adi_read(HEADMIC_DETECT_REG(ANA_STS0));
+
+ PRINT_INFO("%2d: gpio_button=%d adc=%4d ana_pmu0=0x%08X ana_cfg1=0x%08X ana_hdt0=0x%08X ana_sts0=0x%08X\n",
+ count, gpio_button, adc_val, ana_pmu0, ana_cfg1, ana_hdt0, ana_sts0);
+ msleep(1);
+ }
+ }
+ //================= debug =====================
+
+ msleep(1);
+ if(0 == headset_reg_get_bit(HEADMIC_DETECT_REG(ANA_PMU0), AUDIO_HEADMIC_SLEEP_EN))
+ PRINT_INFO("AUDIO_HEADMIC_SLEEP_EN is disabled\n");
+
+ for(i=0; i< ADC_READ_LOOP; i++) {
+ if(gpio_get_value(gpio_num) != gpio_value) {
+ PRINT_INFO("gpio value changed!!! the adc read operation aborted (step1)\n");
+ return -1;
+ }
+ if(0 == ana_sts0_confirm()) {
+ PRINT_INFO("ana_sts0_confirm failed!!! the adc read operation aborted (step2)\n");
+ return -1;
+ }
+ adc[0] = wrap_sci_adc_get_value(ADC_CHANNEL_HEADMIC, 0);
+
+ for(j=0; j<ADC_READ_COUNT-1; j++) {
+ udelay(100);
+ if(gpio_get_value(gpio_num) != gpio_value) {
+ PRINT_INFO("gpio value changed!!! the adc read operation aborted (step3)\n");
+ return -1;
+ }
+ if(0 == ana_sts0_confirm()) {
+ PRINT_INFO("ana_sts0_confirm failed!!! the adc read operation aborted (step4)\n");
+ return -1;
+ }
+ adc[j+1] = wrap_sci_adc_get_value(ADC_CHANNEL_HEADMIC, 0);
+
+ if(0 == adc_compare(adc[j], adc[j+1])) {
+ success = 0;
+ for(k=0; k<=j+1; k++) {
+ PRINT_DBG("adc[%d] = %d\n", k, adc[k]);
+ }
+ break;
+ }
+ }
+ if(1 == success) {
+ msleep(3);
+ if(gpio_get_value(gpio_num) != gpio_value) {
+ PRINT_INFO("gpio value changed!!! the adc read operation aborted (step5)\n");
+ return -1;
+ }
+ if(0 == ana_sts0_confirm()) {
+ PRINT_INFO("ana_sts0_confirm failed!!! the adc read operation aborted (step6)\n");
+ return -1;
+ }
+ for(i=0; i<ADC_READ_COUNT; i++) {
+ adc_average += adc[i];
+ PRINT_DBG("adc[%d] = %d\n", i, adc[i]);
+ }
+ adc_average = adc_average / ADC_READ_COUNT;
+ PRINT_DBG("adc_get_average success, adc_average = %d\n", adc_average);
+ return adc_average;
+ } else if(i+1< ADC_READ_LOOP) {
+ PRINT_INFO("adc_get_average failed, retrying count = %d\n", i+1);
+ msleep(1);
+ }
+ }
+ PRINT_INFO("adc_get_average out \n");
+ return -1;
+}
+
+static int headset_irq_set_irq_type(unsigned int irq, unsigned int type)
+{
+ struct sprd_headset *ht = &headset;
+ struct irq_desc *irq_desc = NULL;
+ unsigned int irq_flags = 0;
+ int ret = -1;
+
+ ret = irq_set_irq_type(irq, type);
+ irq_desc = irq_to_desc(irq);
+ irq_flags = irq_desc->action->flags;
+
+ if(irq == ht->irq_button) {
+ if(IRQF_TRIGGER_HIGH == type) {
+ PRINT_DBG("IRQF_TRIGGER_HIGH is set for irq_button(%d). irq_flags = 0x%08X, ret = %d\n",
+ ht->irq_button, irq_flags, ret);
+ } else if(IRQF_TRIGGER_LOW == type) {
+ PRINT_DBG("IRQF_TRIGGER_LOW is set for irq_button(%d). irq_flags = 0x%08X, ret = %d\n",
+ ht->irq_button, irq_flags, ret);
+ }
+ } else if(irq == ht->irq_detect) {
+ if(IRQF_TRIGGER_HIGH == type) {
+ PRINT_DBG("IRQF_TRIGGER_HIGH is set for irq_detect(%d). irq_flags = 0x%08X, ret = %d\n",
+ ht->irq_detect, irq_flags, ret);
+ } else if(IRQF_TRIGGER_LOW == type) {
+ PRINT_DBG("IRQF_TRIGGER_LOW is set for irq_detect(%d). irq_flags = 0x%08X, ret = %d\n",
+ ht->irq_detect, irq_flags, ret);
+ }
+ }
+ return 0;
+}
+
+static int headset_button_valid(int gpio_detect_value_current)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ int button_is_valid = 0;
+
+ if(1 == pdata->irq_trigger_level_detect) {
+ if(1 == gpio_detect_value_current)
+ button_is_valid = 1;
+ else
+ button_is_valid = 0;
+ } else {
+ if(0 == gpio_detect_value_current)
+ button_is_valid = 1;
+ else
+ button_is_valid = 0;
+ }
+
+ return button_is_valid;
+}
+
+static int headset_gpio_2_button_state(int gpio_button_value_current)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ int button_state_current = 0;
+
+ if(1 == pdata->irq_trigger_level_button) {
+ if(1 == gpio_button_value_current)
+ button_state_current = 1;
+ else
+ button_state_current = 0;
+ } else {
+ if(0 == gpio_button_value_current)
+ button_state_current = 1;
+ else
+ button_state_current = 0;
+ }
+
+ return button_state_current; //0==released, 1==pressed
+}
+
+static int headset_plug_confirm_by_adc(int last_gpio_detect_value)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ int adc_last = 0;
+ int adc_current = 0;
+ int count = 0;
+ int adc_read_interval = 10;
+
+ adc_last = adc_get_average(pdata->gpio_detect, last_gpio_detect_value);
+ if(-1 == adc_last) {
+ PRINT_INFO("headset_plug_confirm_by_adc failed!!!\n");
+ return -1;
+ }
+
+ while(count < PLUG_CONFIRM_COUNT) {
+ msleep(adc_read_interval);
+ adc_current = adc_get_average(pdata->gpio_detect, last_gpio_detect_value);
+ if(-1 == adc_current) {
+ PRINT_INFO("headset_plug_confirm_by_adc failed!!!\n");
+ return -1;
+ }
+ if(0 == adc_compare(adc_last, adc_current)) {
+ PRINT_INFO("headset_plug_confirm_by_adc failed!!!\n");
+ return -1;
+ }
+ adc_last = adc_current;
+ count++;
+ }
+ PRINT_INFO("headset_plug_confirm_by_adc success!!!\n");
+ return adc_current;
+}
+static int adc_get_ideal(u32 adc_mic);
+
+static SPRD_HEADSET_TYPE headset_type_detect(int last_gpio_detect_value)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ int adc_mic_average = 0;
+ int adc_left_average = 0;
+ int adc_mic_ideal = 0;
+ int no_mic_retry_count = NO_MIC_RETRY_COUNT;
+
+ ENTER;
+
+ if(0 != pdata->gpio_switch)
+ gpio_direction_output(pdata->gpio_switch, 0);
+ else
+ PRINT_INFO("automatic type switch is unsupported\n");
+
+ headset_button_irq_threshold(1);
+ headset_detect_init();
+ headset_detect_circuit(1);
+
+no_mic_retry:
+
+ //get adc value of left
+ if(EIC_AUD_HEAD_INST2 == pdata->gpio_detect) {
+ PRINT_INFO("HEADSET_DETECT_GPIO=%d, matched the reference phone, now getting adc value of left\n", pdata->gpio_detect);
+ set_adc_to_headmic(0);
+ msleep(100);
+ adc_left_average = headset_plug_confirm_by_adc(last_gpio_detect_value);
+ if(-1 == adc_left_average)
+ return HEADSET_TYPE_ERR;
+ } else {
+ PRINT_INFO("HEADSET_DETECT_GPIO=%d, NOT matched the reference phone(GPIO_%d), set adc_left_average to 0\n",
+ pdata->gpio_detect, EIC_AUD_HEAD_INST2);
+ adc_left_average = 0;
+ }
+
+ //get adc value of mic
+ set_adc_to_headmic(1);
+ msleep(50);
+ adc_mic_average = headset_plug_confirm_by_adc(last_gpio_detect_value);
+ if(-1 == adc_mic_average)
+ return HEADSET_TYPE_ERR;
+ adc_mic_ideal = adc_get_ideal(adc_mic_average);
+ PRINT_INFO("adc_mic_average=%d, adc_mic_ideal=%d\n", adc_mic_average, adc_mic_ideal);
+ if(adc_mic_ideal >= 0) {
+ adc_mic_average = adc_mic_ideal;
+ }
+ PRINT_INFO("adc_mic_average = %d\n", adc_mic_average);
+ PRINT_INFO("adc_left_average = %d\n", adc_left_average);
+
+ if((gpio_get_value(pdata->gpio_detect)) != last_gpio_detect_value) {
+ PRINT_INFO("software debance (gpio check)!!!(headset_type_detect)\n");
+ return HEADSET_TYPE_ERR;
+ }
+
+ if(0 == ana_sts0_confirm()) {
+ PRINT_INFO("software debance (ana_sts0_confirm)!!!(headset_type_detect)\n");
+ return HEADSET_TYPE_ERR;
+ }
+
+ if((adc_mic_average < pdata->adc_threshold_3pole_detect)) {
+ if(0 != no_mic_retry_count) {
+ PRINT_INFO("no_mic_retry\n");
+ no_mic_retry_count--;
+ goto no_mic_retry;
+ }
+ return HEADSET_NO_MIC;
+ } else if((adc_left_average < ADC_GND) && (adc_mic_average > ADC_GND))
+ return HEADSET_4POLE_NORMAL;
+ else if((adc_left_average > ADC_GND) && (adc_mic_average > ADC_GND)
+ && (ABS(adc_mic_average - adc_left_average) < ADC_GND))
+ return HEADSET_4POLE_NOT_NORMAL;
+ else
+ return HEADSET_TYPE_ERR;
+
+ return HEADSET_TYPE_ERR;
+}
+
+static void button_release_verify(void)
+{
+ struct sprd_headset *ht = &headset;
+
+ if(1 == button_state_last) {
+ input_event(ht->input_dev, EV_KEY, current_key_code, 0);
+ input_sync(ht->input_dev);
+ button_state_last = 0;
+ PRINT_INFO("headset button released by force!!! current_key_code = %d(0x%04X)\n", current_key_code, current_key_code);
+
+ if (1 == ht->platform_data->irq_trigger_level_button)
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_HIGH);
+ else
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_LOW);
+ }
+}
+
+static void headset_button_work_func(struct work_struct *work)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ int gpio_button_value_current = 0;
+ int button_state_current = 0;
+ int adc_mic_average = 0;
+ int i = 0;
+ int adc_ideal = 0;
+
+ down(&headset_sem);
+ set_adc_to_headmic(1);
+
+ ENTER;
+
+ gpio_button_value_current = gpio_get_value(pdata->gpio_button);
+ if(gpio_button_value_current != gpio_button_value_last) {
+ PRINT_INFO("software debance (step 1: gpio check)!!!(headset_button_work_func)\n");
+#ifdef ADPGAR_BYP_SELECT
+ queue_delayed_work(adpgar_byp_select_work_queue, &adpgar_byp_select_work, msecs_to_jiffies(0));
+#endif
+ goto out;
+ }
+
+ button_state_current = headset_gpio_2_button_state(gpio_button_value_current);
+
+ if(1 == button_state_current) {//pressed!
+ if(ht->platform_data->nbuttons > 0) {
+ adc_mic_average = adc_get_average(pdata->gpio_button, gpio_button_value_last);
+ if(-1 == adc_mic_average) {
+ PRINT_INFO("software debance (step 3: adc check)!!!(headset_button_work_func)(pressed)\n");
+#ifdef ADPGAR_BYP_SELECT
+ queue_delayed_work(adpgar_byp_select_work_queue, &adpgar_byp_select_work, msecs_to_jiffies(0));
+#endif
+ goto out;
+ }
+ adc_ideal = adc_get_ideal(adc_mic_average);
+ PRINT_INFO("adc_mic_average=%d, adc_ideal=%d\n", adc_mic_average, adc_ideal);
+ if (adc_ideal >= 0)
+ adc_mic_average = adc_ideal;
+ PRINT_INFO("adc_mic_average = %d\n", adc_mic_average);
+ for (i = 0; i < ht->platform_data->nbuttons; i++) {
+ if (adc_mic_average >= ht->platform_data->headset_buttons[i].adc_min &&
+ adc_mic_average < ht->platform_data->headset_buttons[i].adc_max) {
+ current_key_code = ht->platform_data->headset_buttons[i].code;
+ break;
+ }
+ current_key_code = KEY_RESERVED;
+ }
+ }
+
+ if(0 == button_state_last) {
+ input_event(ht->input_dev, EV_KEY, current_key_code, 1);
+ input_sync(ht->input_dev);
+ button_state_last = 1;
+ PRINT_INFO("headset button pressed! current_key_code = %d(0x%04X)\n", current_key_code, current_key_code);
+ } else {
+ PRINT_ERR("headset button pressed already! current_key_code = %d(0x%04X)\n", current_key_code, current_key_code);
+#ifdef ADPGAR_BYP_SELECT
+ queue_delayed_work(adpgar_byp_select_work_queue, &adpgar_byp_select_work, msecs_to_jiffies(0));
+#endif
+ }
+
+ if (1 == ht->platform_data->irq_trigger_level_button)
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_LOW);
+ else
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_HIGH);
+
+ headset_irq_button_enable(1, ht->irq_button);
+ } else { //released!
+ if(1 == button_state_last) {
+ input_event(ht->input_dev, EV_KEY, current_key_code, 0);
+ input_sync(ht->input_dev);
+ button_state_last = 0;
+ PRINT_INFO("headset button released! current_key_code = %d(0x%04X)\n", current_key_code, current_key_code);
+ } else
+ PRINT_ERR("headset button released already! current_key_code = %d(0x%04X)\n", current_key_code, current_key_code);
+
+ if (1 == ht->platform_data->irq_trigger_level_button)
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_HIGH);
+ else
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_LOW);
+
+ headset_irq_button_enable(1, ht->irq_button);
+ }
+out:
+ headset_adc_en(0);
+ headset_irq_button_enable(1, ht->irq_button);
+ //wake_unlock(&headset_button_wakelock);
+ up(&headset_sem);
+ return;
+}
+
+static void headset_detect_work_func(struct work_struct *work)
+{
+ struct sprd_headset *ht = &headset;
+ struct sprd_headset_platform_data *pdata = ht->platform_data;
+ SPRD_HEADSET_TYPE headset_type;
+ int plug_state_current = 0;
+ int gpio_detect_value_current = 0;
+ down(&headset_sem);
+
+ ENTER;
+ if (sprd_hts_power.head_mic == NULL) {
+ sprd_headset_power_init(&this_pdev->dev);
+ }
+ if (sprd_hts_power.head_mic == NULL ) {
+ PRINT_INFO("sprd_headset_power_init fail 0 \n");
+ goto out;
+ }
+
+ if(0 == plug_state_last) {
+ //schedule_timeout_uninterruptible(msecs_to_jiffies(10));
+ headmicbias_power_on(&this_pdev->dev, 1);
+ }
+
+ if(0 == plug_state_last) {
+ gpio_detect_value_current = gpio_get_value(pdata->gpio_detect);
+ PRINT_INFO("gpio_detect_value_current = %d, gpio_detect_value_last = %d, plug_state_last = %d\n",
+ gpio_detect_value_current, gpio_detect_value_last, plug_state_last);
+
+ if(gpio_detect_value_current != gpio_detect_value_last) {
+ PRINT_INFO("software debance (step 1)!!!(headset_detect_work_func)\n");
+ goto out;
+ }
+
+ if(1 == pdata->irq_trigger_level_detect) {
+ if(1 == gpio_detect_value_current)
+ plug_state_current = 1;
+ else
+ plug_state_current = 0;
+ } else {
+ if(0 == gpio_detect_value_current)
+ plug_state_current = 1;
+ else
+ plug_state_current = 0;
+ }
+ } else
+ plug_state_current = 0;//no debounce for plug out!!!
+
+ if(1 == plug_state_current && 0 == plug_state_last) {
+ headset_type = headset_type_detect(gpio_detect_value_last);
+ headset_adc_en(0);
+ switch (headset_type) {
+ case HEADSET_TYPE_ERR:
+ detect_err_count ++;
+ PRINT_INFO("headset_type = %d detect_err_count = %d(HEADSET_TYPE_ERR)\n", headset_type,detect_err_count);
+ goto out;
+ case HEADSET_4POLE_NOT_NORMAL:
+ PRINT_INFO("headset_type = %d (HEADSET_4POLE_NOT_NORMAL)\n", headset_type);
+ if(0 != pdata->gpio_switch)
+ gpio_direction_output(pdata->gpio_switch, 1);
+ break;
+ case HEADSET_4POLE_NORMAL:
+ PRINT_INFO("headset_type = %d (HEADSET_4POLE_NORMAL)\n", headset_type);
+ if(0 != pdata->gpio_switch)
+ gpio_direction_output(pdata->gpio_switch, 0);
+ break;
+ case HEADSET_NO_MIC:
+ PRINT_INFO("headset_type = %d (HEADSET_NO_MIC)\n", headset_type);
+ if(0 != pdata->gpio_switch)
+ gpio_direction_output(pdata->gpio_switch, 0);
+ break;
+ case HEADSET_APPLE:
+ PRINT_INFO("headset_type = %d (HEADSET_APPLE)\n", headset_type);
+ PRINT_INFO("we have not yet implemented this in the code\n");
+ break;
+ default:
+ PRINT_INFO("headset_type = %d (HEADSET_UNKNOWN)\n", headset_type);
+ break;
+ }
+
+ detect_err_count = 0;
+ if(headset_type == HEADSET_NO_MIC)
+ ht->headphone = 1;
+ else
+ ht->headphone = 0;
+
+ if (ht->headphone) {
+ headset_button_irq_threshold(0);
+ headset_irq_button_enable(0, ht->irq_button);
+
+ ht->type = BIT_HEADSET_NO_MIC;
+ //hp_notifier_call_chain(ht->type);
+ switch_set_state(&ht->sdev, ht->type);
+ PRINT_INFO("headphone plug in (headset_detect_work_func)\n");
+ } else {
+ headset_button_irq_threshold(1);
+
+ if((HEADSET_4POLE_NOT_NORMAL == headset_type) && (0 == pdata->gpio_switch)) {
+ headset_irq_button_enable(0, ht->irq_button);
+ PRINT_INFO("HEADSET_4POLE_NOT_NORMAL is not supported by your hardware! so disable the button irq!\n");
+ } else {
+ if (1 == ht->platform_data->irq_trigger_level_button)
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_HIGH);
+ else
+ headset_irq_set_irq_type(ht->irq_button, IRQF_TRIGGER_LOW);
+
+ headset_irq_button_enable(1, ht->irq_button);
+ }
+
+ ht->type = BIT_HEADSET_MIC;
+ //hp_notifier_call_chain(ht->type);
+ switch_set_state(&ht->sdev, ht->type);
+ PRINT_INFO("headset plug in (headset_detect_work_func)\n");
+#ifdef ADPGAR_BYP_SELECT
+ queue_delayed_work(adpgar_byp_select_work_queue, &adpgar_byp_select_work, msecs_to_jiffies(500));
+#endif
+ }
+
+ plug_state_last = 1;
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ headset_irq_detect_enable(0, ht->irq_detect);
+ headset_irq_detect_mic_enable(1, ht->irq_detect_mic);
+#else
+ if(1 == pdata->irq_trigger_level_detect)
+ headset_irq_set_irq_type(ht->irq_detect, IRQF_TRIGGER_LOW);
+ else
+ headset_irq_set_irq_type(ht->irq_detect, IRQF_TRIGGER_HIGH);
+
+ headset_irq_detect_enable(1, ht->irq_detect);
+#endif
+ } else if(0 == plug_state_current && 1 == plug_state_last) {
+
+ headset_irq_button_enable(0, ht->irq_button);
+ button_release_verify();
+
+ if (ht->headphone) {
+ PRINT_INFO("headphone plug out (headset_detect_work_func)\n");
+ } else {
+ PRINT_INFO("headset plug out (headset_detect_work_func)\n");
+ }
+ ht->type = BIT_HEADSET_OUT;
+ //hp_notifier_call_chain(ht->type);
+ switch_set_state(&ht->sdev, ht->type);
+ plug_state_last = 0;
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ headset_irq_detect_mic_enable(0, ht->irq_detect_mic);
+#else
+ if(1 == pdata->irq_trigger_level_detect)
+ headset_irq_set_irq_type(ht->irq_detect, IRQF_TRIGGER_HIGH);
+ else
+ headset_irq_set_irq_type(ht->irq_detect, IRQF_TRIGGER_LOW);
+#endif
+ headset_irq_detect_enable(1, ht->irq_detect);
+ } else {
+ PRINT_INFO("irq_detect must be enabled anyway!!!\n");
+ goto out;
+ }
+out:
+
+ if(0 == plug_state_last) {
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ headset_irq_detect_mic_enable(0, ht->irq_detect_mic);
+ if (detect_err_count < 500) {
+ headset_irq_detect_enable(1, ht->irq_detect);
+ }
+#endif
+ headmicbias_power_on(&this_pdev->dev, 0);
+ //headset_detect_clk_en();
+ //if (sprd_hts_power.head_mic) {
+ //regulator_set_mode(sprd_hts_power.head_mic, REGULATOR_MODE_NORMAL);
+ //}
+ }
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ if (1 == plug_state_last) {
+ headset_irq_detect_mic_enable(1, ht->irq_detect_mic);
+ headset_irq_detect_enable(0, ht->irq_detect);
+ }
+#else
+ headset_irq_detect_enable(1, ht->irq_detect);
+#endif
+ //wake_unlock(&headset_detect_wakelock);
+ up(&headset_sem);
+ return;
+}
+
+#ifdef ADPGAR_BYP_SELECT
+static void adpgar_byp_select_func(struct work_struct *work)
+{
+ if ((0x2723 == (sci_get_ana_chip_id() >> 16))
+ && (SC2723ES == sci_get_ana_chip_ver()
+ || SC2723TS == sci_get_ana_chip_ver())) {
+ return;
+ }
+
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CDC1), AUDIO_ADPGAR_BYP_HEADMIC_2_ADCR, AUDIO_ADPGAR_BYP_MASK, AUDIO_ADPGAR_BYP_SHIFT);
+ PRINT_INFO("ANA_CDC1 = 0x%08X\n", sci_adi_read(HEADMIC_DETECT_REG(ANA_CDC1)));
+ msleep(100);
+ headset_reg_set_val(HEADMIC_DETECT_REG(ANA_CDC1), AUDIO_ADPGAR_BYP_NORMAL, AUDIO_ADPGAR_BYP_MASK, AUDIO_ADPGAR_BYP_SHIFT);
+ PRINT_INFO("ANA_CDC1 = 0x%08X\n", sci_adi_read(HEADMIC_DETECT_REG(ANA_CDC1)));
+ PRINT_INFO("adpgar_byp_select_func\n");
+}
+#endif
+
+static void reg_dump_func(struct work_struct *work)
+{
+ int adc_mic = 0;
+
+ int gpio_detect = 0;
+ int gpio_button = 0;
+ int gpio_detect_mic = 0;
+
+ int ana_sts0 = 0;
+ int ana_pmu0 = 0;
+ int ana_cfg1 = 0;
+ int ana_hdt0 = 0;
+
+ int hid_cfg0 = 0;
+ int hid_cfg2 = 0;
+ int hid_cfg3 = 0;
+ int hid_cfg4 = 0;
+
+ int arm_module_en = 0;
+ int arm_clk_en = 0;
+
+ adc_mic = wrap_sci_adc_get_value(ADC_CHANNEL_HEADMIC, 1);
+
+ gpio_detect = gpio_get_value(headset.platform_data->gpio_detect);
+ gpio_button = gpio_get_value(headset.platform_data->gpio_button);
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ gpio_detect_mic = gpio_get_value(headset.platform_data->gpio_detect_mic);
+#endif
+
+ sci_adi_write(ANA_REG_GLB_ARM_MODULE_EN, BIT_ANA_AUD_EN, BIT_ANA_AUD_EN);//arm base address:0x40038800 for register accessable
+ ana_pmu0 = sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_PMU0);//arm base address:0x40038600
+ ana_hdt0 = sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_HDT0);//arm base address:0x40038600
+ ana_sts0 = sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_STS0);//arm base address:0x40038600
+
+ sci_adi_write(ANA_REG_GLB_ARM_MODULE_EN, BIT_ANA_HDT_EN, BIT_ANA_HDT_EN);//arm base address:0x40038800 for register accessable
+ sci_adi_write(ANA_REG_GLB_ARM_CLK_EN, BIT_CLK_AUD_HID_EN, BIT_CLK_AUD_HID_EN);//arm base address:0x40038800 for register accessable
+
+ hid_cfg2 = sci_adi_read(ANA_HDT_INT_BASE+HID_CFG2);//arm base address:0x40038700
+ hid_cfg3 = sci_adi_read(ANA_HDT_INT_BASE+HID_CFG3);//arm base address:0x40038700
+ hid_cfg4 = sci_adi_read(ANA_HDT_INT_BASE+HID_CFG4);//arm base address:0x40038700
+ hid_cfg0 = sci_adi_read(ANA_HDT_INT_BASE+HID_CFG0);//arm base address:0x40038700
+
+ arm_module_en = sci_adi_read(ANA_REG_GLB_ARM_MODULE_EN);
+ arm_clk_en = sci_adi_read(ANA_REG_GLB_ARM_CLK_EN);
+
+ PRINT_INFO("GPIO_%03d(det)=%d GPIO_%03d(but)=%d GPIO_%03d(det_mic)=%d adc_mic=%d\n",
+ headset.platform_data->gpio_detect, gpio_detect,
+ headset.platform_data->gpio_button, gpio_button,
+ headset.platform_data->gpio_detect_mic, gpio_detect_mic,
+ adc_mic);
+ PRINT_INFO("arm_module_en|arm_clk_en|ana_pmu0 |ana_cfg1 |ana_hdt0 |ana_sts0 |hid_cfg2 |hid_cfg3 |hid_cfg4 |hid_cfg0\n");
+ PRINT_INFO("0x%08X |0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X\n",
+ arm_module_en, arm_clk_en, ana_pmu0, ana_cfg1, ana_hdt0, ana_sts0, hid_cfg2, hid_cfg3, hid_cfg4, hid_cfg0);
+#if 0
+ int i = 0;
+ int hbd[20] = {0};
+
+ for(i=0; i<20; i++)
+ hbd[i] = sci_adi_read(ANA_HDT_INT_BASE + (i*4));
+
+ PRINT_INFO(" hbd_cfg0 | hbd_cfg1 | hbd_cfg2 | hbd_cfg3 | hbd_cfg4 | hbd_cfg5 | hbd_cfg6 | hbd_cfg7 | hbd_cfg8 | hbd_cfg9\n");
+ PRINT_INFO("0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X\n",
+ hbd[0], hbd[1], hbd[2], hbd[3], hbd[4], hbd[5], hbd[6], hbd[7], hbd[8], hbd[9]);
+
+ PRINT_INFO("hbd_cfg10 |hbd_cfg11 |hbd_cfg12 |hbd_cfg13 |hbd_cfg14 |hbd_cfg15 |hbd_cfg16 | hbd_sts0 | hbd_sts1 | hbd_sts2\n");
+ PRINT_INFO("0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X|0x%08X\n",
+ hbd[10], hbd[11], hbd[12], hbd[13], hbd[14], hbd[15], hbd[16], hbd[17], hbd[18], hbd[19]);
+#endif
+
+ if (debug_level >= 2)
+ queue_delayed_work(reg_dump_work_queue, &reg_dump_work, msecs_to_jiffies(500));
+ return;
+}
+
+/**
+ * some phone boards, when no headset plugin, the button headset_button_valid()
+ * will always return 0, so we need to disable headset_button irq in here
+ * otherwise button irq will always happen.
+ */
+volatile int headset_button_hardware_bug_fix_stage = 1;
+static void headset_button_hardware_bug_fix(struct sprd_headset *ht)
+{
+ if (headset_button_hardware_bug_fix_stage) {
+ headset_irq_button_enable(0, ht->irq_button); // disable button irq before headset detected
+ pr_err("This phone board headset button circuit has bug, please fix the hardware\n");
+ }
+}
+
+static irqreturn_t headset_button_irq_handler(int irq, void *dev)
+{
+ struct sprd_headset *ht = dev;
+ int button_state_current = 0;
+ int gpio_button_value_current = 0;
+
+ if(0 == headset_button_valid(gpio_get_value(ht->platform_data->gpio_detect))) {
+ PRINT_INFO("headset_button_irq_handler: button is invalid!!! IRQ_%d(GPIO_%d) = %d, ANA_STS0 = 0x%08X\n",
+ ht->irq_button, ht->platform_data->gpio_button, gpio_button_value_last,
+ sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_STS0));
+ headset_button_hardware_bug_fix(ht);
+
+ if(0 == plug_state_last)
+ headset_irq_button_enable(0, ht->irq_button);
+
+ return IRQ_HANDLED;
+ }
+
+ gpio_button_value_current = gpio_get_value(ht->platform_data->gpio_button);
+ button_state_current = headset_gpio_2_button_state(gpio_button_value_current);
+#ifdef ADPGAR_BYP_SELECT
+ if(0 == button_state_current) {
+ queue_delayed_work(adpgar_byp_select_work_queue, &adpgar_byp_select_work, msecs_to_jiffies(0));
+ }
+#endif
+ if(button_state_current == button_state_last) {
+ PRINT_INFO("button state check failed!!! maybe the release is too quick. button_state_current=%d, button_state_last=%d\n",
+ button_state_current, button_state_last);
+ return IRQ_HANDLED;
+ }
+
+ headset_irq_button_enable(0, ht->irq_button);
+ wake_lock_timeout(&headset_button_wakelock, msecs_to_jiffies(2000));
+ gpio_button_value_last = gpio_button_value_current;
+ PRINT_DBG("headset_button_irq_handler: IRQ_%d(GPIO_%d) = %d, ANA_STS0 = 0x%08X\n",
+ ht->irq_button, ht->platform_data->gpio_button, gpio_button_value_last,
+ sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_STS0));
+ queue_work(ht->button_work_queue, &ht->work_button);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t headset_detect_irq_handler(int irq, void *dev)
+{
+ struct sprd_headset *ht = dev;
+ PRINT_INFO("headset_detect_irq_handler in \n");
+ headset_irq_button_enable(0, ht->irq_button);
+ headset_irq_detect_enable(0, ht->irq_detect);
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ headset_irq_detect_mic_enable(0, ht->irq_detect_mic);
+#endif
+ wake_lock_timeout(&headset_detect_wakelock, msecs_to_jiffies(2000));
+ gpio_detect_value_last = gpio_get_value(ht->platform_data->gpio_detect);
+ PRINT_DBG("headset_detect_irq_handler: IRQ_%d(GPIO_%d) = %d, ANA_STS0 = 0x%08X\n",
+ ht->irq_detect, ht->platform_data->gpio_detect, gpio_detect_value_last,
+ sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_STS0));
+ queue_delayed_work(ht->detect_work_queue, &ht->work_detect, msecs_to_jiffies(0));
+ PRINT_INFO("headset_detect_irq_handler out \n");
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t headset_detect_mic_irq_handler(int irq, void *dev)
+{
+ struct sprd_headset *ht = dev;
+ PRINT_INFO("headset_detect_mic_irq_handler in \n");
+ if (plug_state_last != 1) {
+ headset_irq_detect_mic_enable(0, ht->irq_detect_mic);
+ PRINT_INFO("headset_detect_mic_irq_handler out0 \n");
+ return IRQ_HANDLED;
+ }
+ headset_irq_detect_mic_enable(0, ht->irq_detect_mic);
+ headset_irq_detect_enable(0, ht->irq_detect);
+ wake_lock_timeout(&headset_detect_wakelock, msecs_to_jiffies(2000));
+ queue_delayed_work(ht->detect_work_queue, &ht->work_detect, msecs_to_jiffies(0));
+ PRINT_INFO("headset_detect_mic_irq_handler out1 \n");
+ return IRQ_HANDLED;
+}
+
+//================= create sys fs for debug ===================
+//============= /sys/kernel/headset/debug_level ===============
+
+static ssize_t headset_debug_level_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff)
+{
+ PRINT_INFO("debug_level = %d\n", debug_level);
+ return sprintf(buff, "%d\n", debug_level);
+}
+
+static ssize_t headset_debug_level_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buff, size_t len)
+{
+ unsigned long level = simple_strtoul(buff, NULL, 10);
+ debug_level = level;
+ PRINT_INFO("debug_level = %d\n", debug_level);
+ if(debug_level >= 2)
+ queue_delayed_work(reg_dump_work_queue, &reg_dump_work, msecs_to_jiffies(500));
+ return len;
+}
+
+static struct kobject *headset_debug_kobj = NULL;
+static struct kobj_attribute headset_debug_attr =
+ __ATTR(debug_level, 0644, headset_debug_level_show, headset_debug_level_store);
+
+static int headset_debug_sysfs_init(void)
+{
+ int ret = -1;
+
+ headset_debug_kobj = kobject_create_and_add("headset", kernel_kobj);
+ if (headset_debug_kobj == NULL) {
+ ret = -ENOMEM;
+ PRINT_ERR("register sysfs failed. ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = sysfs_create_file(headset_debug_kobj, &headset_debug_attr.attr);
+ if (ret) {
+ PRINT_ERR("create sysfs failed. ret = %d\n", ret);
+ return ret;
+ }
+
+ PRINT_INFO("headset_debug_sysfs_init success\n");
+ return ret;
+}
+//================= create sys fs for debug ===================
+
+#ifdef CONFIG_OF
+static struct sprd_headset_platform_data *headset_detect_parse_dt(struct device *dev)
+{
+ struct sprd_headset_platform_data *pdata;
+ struct device_node *np = dev->of_node,*buttons_np = NULL;
+ int ret;
+ struct headset_buttons *buttons_data;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "could not allocate memory for platform data\n");
+ return NULL;
+ }
+ ret = of_property_read_u32(np, "gpio_switch", &pdata->gpio_switch);
+ if(ret) {
+ dev_err(dev, "fail to get gpio_switch\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "gpio_detect", &pdata->gpio_detect);
+ if(ret) {
+ dev_err(dev, "fail to get gpio_detect\n");
+ goto fail;
+ }
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ ret = of_property_read_u32(np, "gpio_detect_mic", &pdata->gpio_detect_mic);
+ if (ret) {
+ dev_err(dev, "fail to get gpio_detect_mic\n");
+ goto fail;
+ }
+#endif
+ ret = of_property_read_u32(np, "gpio_button", &pdata->gpio_button);
+ if(ret) {
+ dev_err(dev, "fail to get gpio_button\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "irq_trigger_level_detect", &pdata->irq_trigger_level_detect);
+ if(ret) {
+ dev_err(dev, "fail to get irq_trigger_level_detect\n");
+ goto fail;
+ }
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ ret = of_property_read_u32(np, "irq_trigger_level_detect_mic", &pdata->irq_trigger_level_detect_mic);
+ if (ret) {
+ dev_err(dev, "fail to get irq_trigger_level_detect_mic\n");
+ goto fail;
+ }
+#endif
+ ret = of_property_read_u32(np, "irq_trigger_level_button", &pdata->irq_trigger_level_button);
+ if(ret) {
+ dev_err(dev, "fail to get irq_trigger_level_button\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "adc_threshold_3pole_detect", &pdata->adc_threshold_3pole_detect);
+ if(ret) {
+ dev_err(dev, "fail to get adc_threshold_3pole_detect\n");
+ goto fail;
+ }
+
+ ret = of_property_read_u32(np, "adc_threshold_4pole_detect", &pdata->adc_threshold_4pole_detect);
+ if(ret) {
+ dev_err(dev, "fail to get adc_threshold_4pole_detect\n");
+ goto fail;
+ }
+
+ ret = of_property_read_u32(np, "irq_threshold_buttont", &pdata->irq_threshold_buttont);
+ if(ret) {
+ dev_err(dev, "fail to get irq_threshold_buttont\n");
+ goto fail;
+ }
+
+ ret = of_property_read_u32(np, "voltage_headmicbias", &pdata->voltage_headmicbias);
+ if(ret) {
+ dev_err(dev, "fail to get voltage_headmicbias\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "nbuttons", &pdata->nbuttons);
+ if(ret) {
+ dev_err(dev, "fail to get nbuttons\n");
+ goto fail;
+ }
+
+ buttons_data = kzalloc(pdata->nbuttons*sizeof(*buttons_data),GFP_KERNEL);
+ if (!buttons_data) {
+ dev_err(dev, "could not allocate memory for headset_buttons\n");
+ goto fail;
+ }
+ pdata->headset_buttons = buttons_data;
+
+ for_each_child_of_node(np,buttons_np) {
+
+ ret = of_property_read_u32(buttons_np, "adc_min", &buttons_data->adc_min);
+ if (ret) {
+ dev_err(dev, "fail to get adc_min\n");
+ goto fail_buttons_data;
+ }
+ ret = of_property_read_u32(buttons_np, "adc_max", &buttons_data->adc_max);
+ if (ret) {
+ dev_err(dev, "fail to get adc_max\n");
+ goto fail_buttons_data;
+ }
+ ret = of_property_read_u32(buttons_np, "code", &buttons_data->code);
+ if (ret) {
+ dev_err(dev, "fail to get code\n");
+ goto fail_buttons_data;
+ }
+ ret = of_property_read_u32(buttons_np, "type", &buttons_data->type);
+ if (ret) {
+ dev_err(dev, "fail to get type\n");
+ goto fail_buttons_data;
+ }
+ PRINT_INFO("device tree data: adc_min = %d adc_max = %d code = %d type = %d \n", buttons_data->adc_min,
+ buttons_data->adc_max, buttons_data->code, buttons_data->type);
+ buttons_data++;
+ };
+
+ return pdata;
+
+fail_buttons_data:
+ kfree(buttons_data);
+ pdata->headset_buttons = NULL;
+fail:
+ kfree(pdata);
+ return NULL;
+}
+#endif
+static void adc_cal_from_efuse(void);
+
+static int headset_detect_probe(struct platform_device *pdev)
+{
+ struct sprd_headset_platform_data *pdata = pdev->dev.platform_data;
+ struct sprd_headset *ht = &headset;
+ unsigned long irqflags = 0;
+ struct input_dev *input_dev = NULL;
+ int i = 0;
+ int ret = -1;
+ int ana_sts0 = 0;
+ int adie_chip_id_low = 0;
+ int adie_chip_id_high = 0;
+ unsigned int adie_chip_id = 0;
+ unsigned int ddie_chip_id = 0;
+
+#ifdef CONFIG_OF
+ struct device_node *np = NULL;
+ np = pdev->dev.of_node;
+
+ if (pdev->dev.of_node && !pdata) {
+ pdata = headset_detect_parse_dt(&pdev->dev);
+ if(pdata)
+ pdev->dev.platform_data = pdata;
+ }
+ if (!pdata) {
+ PRINT_ERR("headset_detect_probe get platform_data NULL\n");
+ ret = -EINVAL;
+ goto fail_to_get_platform_data;
+ }
+#endif
+
+ this_pdev = pdev;
+
+ sci_adi_write(ANA_REG_GLB_ARM_MODULE_EN, BIT_ANA_AUD_EN, BIT_ANA_AUD_EN);//arm base address:0x40038800 for register accessable
+ ana_sts0 = sci_adi_read(ANA_AUDCFGA_INT_BASE+ANA_STS0);//arm base address:0x40038600
+
+ ddie_chip_id = sci_get_chip_id();
+ PRINT_INFO("ddie_chip_id=0x%08X\n", ddie_chip_id);
+ adie_chip_id = sci_get_ana_chip_id();
+ PRINT_INFO("adie_chip_id=0x%08X\n", adie_chip_id);
+
+ adie_chip_id_high = (adie_chip_id >> 16) & 0xFFFF;
+ adie_chip_id_low = adie_chip_id & 0xFFFF;
+
+ switch (adie_chip_id_high) {
+ case 0x2723: {//chip is 2723
+ adie_type = 2723;
+ PRINT_INFO("adie chip is 2723, type = %d\n", adie_type);
+ break; //break from 2723
+ }
+
+ default: {
+ adie_type = -1; //unknow
+ PRINT_ERR("unknow adie chip !!!\n");
+ break;
+ }
+ }
+
+ ht->platform_data = pdata;
+ headset_detect_init();
+
+ ret = sprd_headset_power_init(&pdev->dev);
+ if (ret)
+ goto fail_to_sprd_headset_power_init;
+
+ PRINT_INFO("====================== SPRD HEADSET DETECT v2 ======================\n");
+ PRINT_INFO(" write 0~3 to \"/sys/kernel/headset/debug_level\" for headset debug\n");
+ PRINT_INFO("====================================================================\n");
+ PRINT_INFO("use GPIO_%d for insert detecting\n", pdata->gpio_detect);
+
+ if(0 != pdata->gpio_switch) {
+ ret = gpio_request(pdata->gpio_switch, "headset_switch");
+ if (ret < 0) {
+ PRINT_ERR("failed to request GPIO_%d(headset_switch)\n", pdata->gpio_switch);
+ goto failed_to_request_gpio_switch;
+ }
+ } else
+ PRINT_INFO("automatic type switch is unsupported\n");
+
+ ret = gpio_request(pdata->gpio_detect, "headset_detect");
+ if (ret < 0) {
+ PRINT_ERR("failed to request GPIO_%d(headset_detect)\n", pdata->gpio_detect);
+ goto failed_to_request_gpio_detect;
+ }
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ ret = gpio_request(pdata->gpio_detect_mic, "headset_detect_mic");
+ if (ret < 0) {
+ PRINT_ERR("failed to request GPIO_%d(headset_detect_mic)\n", pdata->gpio_detect_mic);
+ goto failed_to_request_gpio_detect_mic;
+ }
+#endif
+ ret = gpio_request(pdata->gpio_button, "headset_button");
+ if (ret < 0) {
+ PRINT_ERR("failed to request GPIO_%d(headset_button)\n", pdata->gpio_button);
+ goto failed_to_request_gpio_button;
+ }
+
+ if(0 != pdata->gpio_switch)
+ gpio_direction_output(pdata->gpio_switch, 0);
+ gpio_direction_input(pdata->gpio_detect);
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ gpio_direction_input(pdata->gpio_detect_mic);
+#endif
+ gpio_direction_input(pdata->gpio_button);
+ ht->irq_detect = gpio_to_irq(pdata->gpio_detect);
+ ht->irq_button = gpio_to_irq(pdata->gpio_button);
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ ht->irq_detect_mic = gpio_to_irq(pdata->gpio_detect_mic);
+#endif
+ ret = switch_dev_register(&ht->sdev);
+ if (ret < 0) {
+ PRINT_ERR("switch_dev_register failed!\n");
+ goto failed_to_register_switch_dev;
+ }
+
+ input_dev = input_allocate_device();
+ if ( !input_dev) {
+ PRINT_ERR("input_allocate_device for headset_button failed!\n");
+ ret = -ENOMEM;
+ goto failed_to_allocate_input_device;
+ }
+
+ input_dev->name = "headset-keyboard";
+ input_dev->id.bustype = BUS_HOST;
+ ht->input_dev = input_dev;
+
+ for (i = 0; i < pdata->nbuttons; i++) {
+ struct headset_buttons *buttons = &pdata->headset_buttons[i];
+ unsigned int type = buttons->type ?: EV_KEY;
+ input_set_capability(input_dev, type, buttons->code);
+ }
+
+ ret = input_register_device(input_dev);
+ if (ret) {
+ PRINT_ERR("input_register_device for headset_button failed!\n");
+ goto failed_to_register_input_device;
+ }
+
+ sema_init(&headset_sem, 1);
+
+ INIT_WORK(&ht->work_button, headset_button_work_func);
+ ht->button_work_queue = create_singlethread_workqueue("headset_button");
+ if(ht->button_work_queue == NULL) {
+ PRINT_ERR("create_singlethread_workqueue for headset_button failed!\n");
+ goto failed_to_create_singlethread_workqueue_for_headset_button;
+ }
+
+ INIT_DELAYED_WORK(&ht->work_detect, headset_detect_work_func);
+ ht->detect_work_queue = create_singlethread_workqueue("headset_detect");
+ if(ht->detect_work_queue == NULL) {
+ PRINT_ERR("create_singlethread_workqueue for headset_detect failed!\n");
+ goto failed_to_create_singlethread_workqueue_for_headset_detect;
+ }
+
+ INIT_DELAYED_WORK(&reg_dump_work, reg_dump_func);
+ reg_dump_work_queue = create_singlethread_workqueue("headset_reg_dump");
+ if(reg_dump_work_queue == NULL) {
+ PRINT_ERR("create_singlethread_workqueue for headset_reg_dump failed!\n");
+ goto failed_to_create_singlethread_workqueue_for_headset_reg_dump;
+ }
+ if (debug_level >= 2)
+ queue_delayed_work(reg_dump_work_queue, &reg_dump_work, msecs_to_jiffies(500));
+
+#ifdef ADPGAR_BYP_SELECT
+ //work queue for Bug#298417
+ INIT_DELAYED_WORK(&adpgar_byp_select_work, adpgar_byp_select_func);
+ adpgar_byp_select_work_queue = create_singlethread_workqueue("adpgar_byp_select");
+ if(adpgar_byp_select_work_queue == NULL) {
+ PRINT_ERR("create_singlethread_workqueue for adpgar_byp_select failed!\n");
+ goto failed_to_create_singlethread_workqueue_for_adpgar_byp_select;
+ }
+ PRINT_INFO("ADPGAR_BYP_SELECT is enabled!\n");
+#endif
+
+ wake_lock_init(&headset_detect_wakelock, WAKE_LOCK_SUSPEND, "headset_detect_wakelock");
+ wake_lock_init(&headset_button_wakelock, WAKE_LOCK_SUSPEND, "headset_button_wakelock");
+
+ //set EIC3 de-bounce time for button irq
+ headset_reg_set_val((ANA_EIC_BASE+EIC3_DBNC_CTRL), DBNC_CNT3_VALUE, DBNC_CNT3_MASK, DBNC_CNT3_SHIFT);
+ //set EIC5 de-bounce time for inser irq
+ headset_reg_set_val((ANA_EIC_BASE+EIC8_DBNC_CTRL), DBNC_CNT8_VALUE, DBNC_CNT8_MASK, DBNC_CNT8_SHIFT);
+
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ /* set EIC5 de-bounce time for ins_mic irq */
+ headset_reg_set_val((ANA_EIC_BASE+EIC5_DBNC_CTRL), DBNC_CNT5_VALUE, DBNC_CNT5_MASK, DBNC_CNT5_SHIFT);
+#endif
+ irqflags = pdata->irq_trigger_level_button ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW;
+ ret = request_irq(ht->irq_button, headset_button_irq_handler, irqflags | IRQF_NO_SUSPEND, "headset_button", ht);
+ if (ret) {
+ PRINT_ERR("failed to request IRQ_%d(GPIO_%d)\n", ht->irq_button, pdata->gpio_button);
+ goto failed_to_request_irq_headset_button;
+ }
+ headset_irq_button_enable(0, ht->irq_button);//disable button irq before headset detected
+ headset_button_hardware_bug_fix_stage = 0;
+
+ irqflags = pdata->irq_trigger_level_detect ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW;
+ ret = request_irq(ht->irq_detect, headset_detect_irq_handler, irqflags | IRQF_NO_SUSPEND, "headset_detect", ht);
+ if (ret < 0) {
+ PRINT_ERR("failed to request IRQ_%d(GPIO_%d)\n", ht->irq_detect, pdata->gpio_detect);
+ goto failed_to_request_irq_headset_detect;
+ }
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+ irqflags = pdata->irq_trigger_level_detect_mic ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW;
+ ret = request_irq(ht->irq_detect_mic, headset_detect_mic_irq_handler, irqflags | IRQF_NO_SUSPEND, "headset_detect_mic", ht);
+ if (ret < 0) {
+ PRINT_ERR("failed to request IRQ_%d(GPIO_%d)\n", ht->irq_detect_mic, pdata->gpio_detect_mic);
+ goto failed_to_request_irq_mic_headset_detect;
+ }
+#endif
+ ret = headset_debug_sysfs_init();
+ adc_cal_from_efuse();
+ PRINT_INFO("headset_detect_probe success\n");
+ return ret;
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+failed_to_request_irq_mic_headset_detect:
+ free_irq(ht->irq_detect_mic, ht);
+#endif
+failed_to_request_irq_headset_detect:
+ free_irq(ht->irq_button, ht);
+failed_to_request_irq_headset_button:
+
+#ifdef ADPGAR_BYP_SELECT
+ cancel_delayed_work_sync(&adpgar_byp_select_work);
+ destroy_workqueue(adpgar_byp_select_work_queue);
+failed_to_create_singlethread_workqueue_for_adpgar_byp_select:
+#endif
+
+ cancel_delayed_work_sync(&reg_dump_work);
+ destroy_workqueue(reg_dump_work_queue);
+failed_to_create_singlethread_workqueue_for_headset_reg_dump:
+ destroy_workqueue(ht->detect_work_queue);
+failed_to_create_singlethread_workqueue_for_headset_detect:
+ destroy_workqueue(ht->button_work_queue);
+failed_to_create_singlethread_workqueue_for_headset_button:
+ input_unregister_device(input_dev);
+failed_to_register_input_device:
+ input_free_device(input_dev);
+failed_to_allocate_input_device:
+ switch_dev_unregister(&ht->sdev);
+failed_to_register_switch_dev:
+ gpio_free(pdata->gpio_button);
+failed_to_request_gpio_button:
+ gpio_free(pdata->gpio_detect);
+#ifdef CONFIG_SND_SOC_SPRD_USE_EAR_JACK_TYPE13
+failed_to_request_gpio_detect_mic:
+ gpio_free(pdata->gpio_detect_mic);
+#endif
+failed_to_request_gpio_detect:
+ if(0 != pdata->gpio_switch)
+ gpio_free(pdata->gpio_switch);
+failed_to_request_gpio_switch:
+ headmicbias_power_on(&pdev->dev, 0);
+fail_to_sprd_headset_power_init:
+fail_to_get_platform_data:
+ PRINT_ERR("headset_detect_probe failed\n");
+ return ret;
+}
+
+#ifdef CONFIG_PM
+static int headset_suspend(struct platform_device *dev, pm_message_t state)
+{
+ PRINT_INFO("suspend (det_irq = %d, but_irq = %d, plug_state_last = %d)\n",
+ headset.irq_detect, headset.irq_button, plug_state_last);
+ return 0;
+}
+
+static int headset_resume(struct platform_device *dev)
+{
+ PRINT_INFO("resume (det_irq = %d, but_irq = %d, plug_state_last = %d)\n",
+ headset.irq_detect, headset.irq_button, plug_state_last);
+ return 0;
+}
+#else
+#define headset_suspend NULL
+#define headset_resume NULL
+#endif
+
+static int adc_get_ideal(u32 adc_mic)
+{
+ u64 numerator = 0;
+ u32 denominator = 0;
+ u32 adc_ideal = 0;
+ if (adc_cal_headset.cal_type == SPRD_HEADSET_AUXADC_CAL_DO){
+ PRINT_INFO("wangzuo adc_get_ideal in\n");
+ u32 a = adc_cal_headset.A;
+ u32 b = adc_cal_headset.B;
+ u32 e = adc_cal_headset.E;
+
+ if (9*adc_mic + b < 10*a){
+ return adc_mic;
+ }
+ denominator = e*(b-a);
+ numerator = 917280*(9*(u64)adc_mic+(u64)b-10*(u64)a);
+ PRINT_INFO("denominator=%u, numerator=%llu\n", denominator, numerator);
+ do_div(numerator,denominator);
+ adc_ideal = (u32)numerator;
+ PRINT_INFO("adc_mic=%d, adc_ideal=%d\n", adc_mic, adc_ideal);
+ return adc_ideal;
+ } else {
+ return adc_mic;
+ }
+}
+extern u32 __adie_efuse_read_bits(int bit_index, int length);
+
+#define DELTA1_BLOCK9 9
+#define DELTA2_BLOCK10 10
+#define DELTA3_BLOCK11 11
+#define BITCOUNT 8
+#define BLK_WIDTH 8
+static void adc_cal_from_efuse(void)
+{
+ u32 delta[3] = {0};
+ u32 block0_bit7 = 128;
+ u8 test[3] = {0};
+ PRINT_INFO("to get calibration data from efuse ...\n");
+
+ if (adc_cal_headset.cal_type == SPRD_HEADSET_AUXADC_CAL_NO) {
+
+ delta[0] = __adie_efuse_read_bits(DELTA1_BLOCK9*BLK_WIDTH,BITCOUNT);
+ delta[1] = __adie_efuse_read_bits(DELTA2_BLOCK10*BLK_WIDTH,BITCOUNT);
+ delta[2] = __adie_efuse_read_bits(DELTA3_BLOCK11*BLK_WIDTH,BITCOUNT);
+
+ test[0] = delta[0]&0xff;
+ test[1] = delta[1]&0xff;
+ test[2] = delta[2]&0xff;
+ PRINT_INFO("test[0] 0x%x %d, test[1] 0x%x %d, test[2] 0x%x %d \n",test[0],test[0],test[1],test[1],test[2],test[2]);
+
+ block0_bit7 = __adie_efuse_read_bits(0,BITCOUNT);
+ PRINT_INFO("block_7 0x%08x \n",block0_bit7);
+ if(block0_bit7&(1<<7)){
+ PRINT_INFO("block 0 bit 7 set 1 no efuse data\n");
+ return;
+ } else {
+ adc_cal_headset.cal_type = SPRD_HEADSET_AUXADC_CAL_DO;
+ PRINT_INFO("block 0 bit 7 set 0 have efuse data\n");
+ }
+
+ PRINT_INFO("test[0] %d, test[1] %d, test[2] %d \n",test[0],test[1],test[2]);
+
+ adc_cal_headset.A = (test[0]*4)-184;
+ adc_cal_headset.B = (test[1]*4)+2764;
+ adc_cal_headset.E = (test[2]*2)+2500;
+ PRINT_INFO("adc_cal_headset.A %d, adc_cal_headset.B %d, adc_cal_headset.E %d \n",adc_cal_headset.A,adc_cal_headset.B,adc_cal_headset.E);
+ } else {
+ PRINT_INFO("efuse A,B,E has been calculated! \n");
+ }
+
+}
+
+static const struct of_device_id headset_detect_of_match[] = {
+ {.compatible = "sprd,headset_sprd_sc2723",},
+ { }
+};
+static struct platform_driver headset_detect_driver = {
+ .driver = {
+ .name = "headset_sprd_sc2723",
+ .owner = THIS_MODULE,
+ .of_match_table = headset_detect_of_match,
+ },
+ .probe = headset_detect_probe,
+ //.suspend = headset_suspend,
+ //.resume = headset_resume,
+};
+
+static int __init headset_init(void)
+{
+ int ret;
+ ret = platform_driver_register(&headset_detect_driver);
+ return ret;
+}
+
+static void __exit headset_exit(void)
+{
+ sprd_headset_power_deinit();
+ platform_driver_unregister(&headset_detect_driver);
+}
+
+late_initcall(headset_init);
+module_exit(headset_exit);
+
+MODULE_DESCRIPTION("headset & button detect driver v2");
+MODULE_AUTHOR("Yaochuan Li <yaochuan.li@spreadtrum.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/hp_sdc_rtc.c b/drivers/input/misc/hp_sdc_rtc.c
new file mode 100644
index 00000000..86b82280
--- /dev/null
+++ b/drivers/input/misc/hp_sdc_rtc.c
@@ -0,0 +1,730 @@
+/*
+ * HP i8042 SDC + MSM-58321 BBRTC driver.
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *
+ * References:
+ * System Device Controller Microprocessor Firmware Theory of Operation
+ * for Part Number 1820-4784 Revision B. Dwg No. A-1820-4784-2
+ * efirtc.c by Stephane Eranian/Hewlett Packard
+ *
+ */
+
+#include <linux/hp_sdc.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/miscdevice.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/poll.h>
+#include <linux/rtc.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION("HP i8042 SDC + MSM-58321 RTC Driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define RTC_VERSION "1.10d"
+
+static DEFINE_MUTEX(hp_sdc_rtc_mutex);
+static unsigned long epoch = 2000;
+
+static struct semaphore i8042tregs;
+
+static hp_sdc_irqhook hp_sdc_rtc_isr;
+
+static struct fasync_struct *hp_sdc_rtc_async_queue;
+
+static DECLARE_WAIT_QUEUE_HEAD(hp_sdc_rtc_wait);
+
+static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos);
+
+static long hp_sdc_rtc_unlocked_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg);
+
+static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait);
+
+static int hp_sdc_rtc_open(struct inode *inode, struct file *file);
+static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on);
+
+static void hp_sdc_rtc_isr (int irq, void *dev_id,
+ uint8_t status, uint8_t data)
+{
+ return;
+}
+
+static int hp_sdc_rtc_do_read_bbrtc (struct rtc_time *rtctm)
+{
+ struct semaphore tsem;
+ hp_sdc_transaction t;
+ uint8_t tseq[91];
+ int i;
+
+ i = 0;
+ while (i < 91) {
+ tseq[i++] = HP_SDC_ACT_DATAREG |
+ HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN;
+ tseq[i++] = 0x01; /* write i8042[0x70] */
+ tseq[i] = i / 7; /* BBRTC reg address */
+ i++;
+ tseq[i++] = HP_SDC_CMD_DO_RTCR; /* Trigger command */
+ tseq[i++] = 2; /* expect 1 stat/dat pair back. */
+ i++; i++; /* buffer for stat/dat pair */
+ }
+ tseq[84] |= HP_SDC_ACT_SEMAPHORE;
+ t.endidx = 91;
+ t.seq = tseq;
+ t.act.semaphore = &tsem;
+ sema_init(&tsem, 0);
+
+ if (hp_sdc_enqueue_transaction(&t)) return -1;
+
+ /* Put ourselves to sleep for results. */
+ if (WARN_ON(down_interruptible(&tsem)))
+ return -1;
+
+ /* Check for nonpresence of BBRTC */
+ if (!((tseq[83] | tseq[90] | tseq[69] | tseq[76] |
+ tseq[55] | tseq[62] | tseq[34] | tseq[41] |
+ tseq[20] | tseq[27] | tseq[6] | tseq[13]) & 0x0f))
+ return -1;
+
+ memset(rtctm, 0, sizeof(struct rtc_time));
+ rtctm->tm_year = (tseq[83] & 0x0f) + (tseq[90] & 0x0f) * 10;
+ rtctm->tm_mon = (tseq[69] & 0x0f) + (tseq[76] & 0x0f) * 10;
+ rtctm->tm_mday = (tseq[55] & 0x0f) + (tseq[62] & 0x0f) * 10;
+ rtctm->tm_wday = (tseq[48] & 0x0f);
+ rtctm->tm_hour = (tseq[34] & 0x0f) + (tseq[41] & 0x0f) * 10;
+ rtctm->tm_min = (tseq[20] & 0x0f) + (tseq[27] & 0x0f) * 10;
+ rtctm->tm_sec = (tseq[6] & 0x0f) + (tseq[13] & 0x0f) * 10;
+
+ return 0;
+}
+
+static int hp_sdc_rtc_read_bbrtc (struct rtc_time *rtctm)
+{
+ struct rtc_time tm, tm_last;
+ int i = 0;
+
+ /* MSM-58321 has no read latch, so must read twice and compare. */
+
+ if (hp_sdc_rtc_do_read_bbrtc(&tm_last)) return -1;
+ if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
+
+ while (memcmp(&tm, &tm_last, sizeof(struct rtc_time))) {
+ if (i++ > 4) return -1;
+ memcpy(&tm_last, &tm, sizeof(struct rtc_time));
+ if (hp_sdc_rtc_do_read_bbrtc(&tm)) return -1;
+ }
+
+ memcpy(rtctm, &tm, sizeof(struct rtc_time));
+
+ return 0;
+}
+
+
+static int64_t hp_sdc_rtc_read_i8042timer (uint8_t loadcmd, int numreg)
+{
+ hp_sdc_transaction t;
+ uint8_t tseq[26] = {
+ HP_SDC_ACT_PRECMD | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
+ 0,
+ HP_SDC_CMD_READ_T1, 2, 0, 0,
+ HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
+ HP_SDC_CMD_READ_T2, 2, 0, 0,
+ HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
+ HP_SDC_CMD_READ_T3, 2, 0, 0,
+ HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
+ HP_SDC_CMD_READ_T4, 2, 0, 0,
+ HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN,
+ HP_SDC_CMD_READ_T5, 2, 0, 0
+ };
+
+ t.endidx = numreg * 5;
+
+ tseq[1] = loadcmd;
+ tseq[t.endidx - 4] |= HP_SDC_ACT_SEMAPHORE; /* numreg assumed > 1 */
+
+ t.seq = tseq;
+ t.act.semaphore = &i8042tregs;
+
+ /* Sleep if output regs in use. */
+ if (WARN_ON(down_interruptible(&i8042tregs)))
+ return -1;
+
+ if (hp_sdc_enqueue_transaction(&t)) return -1;
+
+ /* Sleep until results come back. */
+ if (WARN_ON(down_interruptible(&i8042tregs)))
+ return -1;
+
+ up(&i8042tregs);
+
+ return (tseq[5] |
+ ((uint64_t)(tseq[10]) << 8) | ((uint64_t)(tseq[15]) << 16) |
+ ((uint64_t)(tseq[20]) << 24) | ((uint64_t)(tseq[25]) << 32));
+}
+
+
+/* Read the i8042 real-time clock */
+static inline int hp_sdc_rtc_read_rt(struct timeval *res) {
+ int64_t raw;
+ uint32_t tenms;
+ unsigned int days;
+
+ raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_RT, 5);
+ if (raw < 0) return -1;
+
+ tenms = (uint32_t)raw & 0xffffff;
+ days = (unsigned int)(raw >> 24) & 0xffff;
+
+ res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+ res->tv_sec = (time_t)(tenms / 100) + days * 86400;
+
+ return 0;
+}
+
+
+/* Read the i8042 fast handshake timer */
+static inline int hp_sdc_rtc_read_fhs(struct timeval *res) {
+ int64_t raw;
+ unsigned int tenms;
+
+ raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_FHS, 2);
+ if (raw < 0) return -1;
+
+ tenms = (unsigned int)raw & 0xffff;
+
+ res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+ res->tv_sec = (time_t)(tenms / 100);
+
+ return 0;
+}
+
+
+/* Read the i8042 match timer (a.k.a. alarm) */
+static inline int hp_sdc_rtc_read_mt(struct timeval *res) {
+ int64_t raw;
+ uint32_t tenms;
+
+ raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_MT, 3);
+ if (raw < 0) return -1;
+
+ tenms = (uint32_t)raw & 0xffffff;
+
+ res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+ res->tv_sec = (time_t)(tenms / 100);
+
+ return 0;
+}
+
+
+/* Read the i8042 delay timer */
+static inline int hp_sdc_rtc_read_dt(struct timeval *res) {
+ int64_t raw;
+ uint32_t tenms;
+
+ raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_DT, 3);
+ if (raw < 0) return -1;
+
+ tenms = (uint32_t)raw & 0xffffff;
+
+ res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+ res->tv_sec = (time_t)(tenms / 100);
+
+ return 0;
+}
+
+
+/* Read the i8042 cycle timer (a.k.a. periodic) */
+static inline int hp_sdc_rtc_read_ct(struct timeval *res) {
+ int64_t raw;
+ uint32_t tenms;
+
+ raw = hp_sdc_rtc_read_i8042timer(HP_SDC_CMD_LOAD_CT, 3);
+ if (raw < 0) return -1;
+
+ tenms = (uint32_t)raw & 0xffffff;
+
+ res->tv_usec = (suseconds_t)(tenms % 100) * 10000;
+ res->tv_sec = (time_t)(tenms / 100);
+
+ return 0;
+}
+
+
+#if 0 /* not used yet */
+/* Set the i8042 real-time clock */
+static int hp_sdc_rtc_set_rt (struct timeval *setto)
+{
+ uint32_t tenms;
+ unsigned int days;
+ hp_sdc_transaction t;
+ uint8_t tseq[11] = {
+ HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+ HP_SDC_CMD_SET_RTMS, 3, 0, 0, 0,
+ HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+ HP_SDC_CMD_SET_RTD, 2, 0, 0
+ };
+
+ t.endidx = 10;
+
+ if (0xffff < setto->tv_sec / 86400) return -1;
+ days = setto->tv_sec / 86400;
+ if (0xffff < setto->tv_usec / 1000000 / 86400) return -1;
+ days += ((setto->tv_sec % 86400) + setto->tv_usec / 1000000) / 86400;
+ if (days > 0xffff) return -1;
+
+ if (0xffffff < setto->tv_sec) return -1;
+ tenms = setto->tv_sec * 100;
+ if (0xffffff < setto->tv_usec / 10000) return -1;
+ tenms += setto->tv_usec / 10000;
+ if (tenms > 0xffffff) return -1;
+
+ tseq[3] = (uint8_t)(tenms & 0xff);
+ tseq[4] = (uint8_t)((tenms >> 8) & 0xff);
+ tseq[5] = (uint8_t)((tenms >> 16) & 0xff);
+
+ tseq[9] = (uint8_t)(days & 0xff);
+ tseq[10] = (uint8_t)((days >> 8) & 0xff);
+
+ t.seq = tseq;
+
+ if (hp_sdc_enqueue_transaction(&t)) return -1;
+ return 0;
+}
+
+/* Set the i8042 fast handshake timer */
+static int hp_sdc_rtc_set_fhs (struct timeval *setto)
+{
+ uint32_t tenms;
+ hp_sdc_transaction t;
+ uint8_t tseq[5] = {
+ HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+ HP_SDC_CMD_SET_FHS, 2, 0, 0
+ };
+
+ t.endidx = 4;
+
+ if (0xffff < setto->tv_sec) return -1;
+ tenms = setto->tv_sec * 100;
+ if (0xffff < setto->tv_usec / 10000) return -1;
+ tenms += setto->tv_usec / 10000;
+ if (tenms > 0xffff) return -1;
+
+ tseq[3] = (uint8_t)(tenms & 0xff);
+ tseq[4] = (uint8_t)((tenms >> 8) & 0xff);
+
+ t.seq = tseq;
+
+ if (hp_sdc_enqueue_transaction(&t)) return -1;
+ return 0;
+}
+
+
+/* Set the i8042 match timer (a.k.a. alarm) */
+#define hp_sdc_rtc_set_mt (setto) \
+ hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_MT)
+
+/* Set the i8042 delay timer */
+#define hp_sdc_rtc_set_dt (setto) \
+ hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_DT)
+
+/* Set the i8042 cycle timer (a.k.a. periodic) */
+#define hp_sdc_rtc_set_ct (setto) \
+ hp_sdc_rtc_set_i8042timer(setto, HP_SDC_CMD_SET_CT)
+
+/* Set one of the i8042 3-byte wide timers */
+static int hp_sdc_rtc_set_i8042timer (struct timeval *setto, uint8_t setcmd)
+{
+ uint32_t tenms;
+ hp_sdc_transaction t;
+ uint8_t tseq[6] = {
+ HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT,
+ 0, 3, 0, 0, 0
+ };
+
+ t.endidx = 6;
+
+ if (0xffffff < setto->tv_sec) return -1;
+ tenms = setto->tv_sec * 100;
+ if (0xffffff < setto->tv_usec / 10000) return -1;
+ tenms += setto->tv_usec / 10000;
+ if (tenms > 0xffffff) return -1;
+
+ tseq[1] = setcmd;
+ tseq[3] = (uint8_t)(tenms & 0xff);
+ tseq[4] = (uint8_t)((tenms >> 8) & 0xff);
+ tseq[5] = (uint8_t)((tenms >> 16) & 0xff);
+
+ t.seq = tseq;
+
+ if (hp_sdc_enqueue_transaction(&t)) {
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+static ssize_t hp_sdc_rtc_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos) {
+ ssize_t retval;
+
+ if (count < sizeof(unsigned long))
+ return -EINVAL;
+
+ retval = put_user(68, (unsigned long __user *)buf);
+ return retval;
+}
+
+static unsigned int hp_sdc_rtc_poll(struct file *file, poll_table *wait)
+{
+ unsigned long l;
+
+ l = 0;
+ if (l != 0)
+ return POLLIN | POLLRDNORM;
+ return 0;
+}
+
+static int hp_sdc_rtc_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static int hp_sdc_rtc_fasync (int fd, struct file *filp, int on)
+{
+ return fasync_helper (fd, filp, on, &hp_sdc_rtc_async_queue);
+}
+
+static int hp_sdc_rtc_proc_show(struct seq_file *m, void *v)
+{
+#define YN(bit) ("no")
+#define NY(bit) ("yes")
+ struct rtc_time tm;
+ struct timeval tv;
+
+ memset(&tm, 0, sizeof(struct rtc_time));
+
+ if (hp_sdc_rtc_read_bbrtc(&tm)) {
+ seq_puts(m, "BBRTC\t\t: READ FAILED!\n");
+ } else {
+ seq_printf(m,
+ "rtc_time\t: %02d:%02d:%02d\n"
+ "rtc_date\t: %04d-%02d-%02d\n"
+ "rtc_epoch\t: %04lu\n",
+ tm.tm_hour, tm.tm_min, tm.tm_sec,
+ tm.tm_year + 1900, tm.tm_mon + 1,
+ tm.tm_mday, epoch);
+ }
+
+ if (hp_sdc_rtc_read_rt(&tv)) {
+ seq_puts(m, "i8042 rtc\t: READ FAILED!\n");
+ } else {
+ seq_printf(m, "i8042 rtc\t: %ld.%02d seconds\n",
+ tv.tv_sec, (int)tv.tv_usec/1000);
+ }
+
+ if (hp_sdc_rtc_read_fhs(&tv)) {
+ seq_puts(m, "handshake\t: READ FAILED!\n");
+ } else {
+ seq_printf(m, "handshake\t: %ld.%02d seconds\n",
+ tv.tv_sec, (int)tv.tv_usec/1000);
+ }
+
+ if (hp_sdc_rtc_read_mt(&tv)) {
+ seq_puts(m, "alarm\t\t: READ FAILED!\n");
+ } else {
+ seq_printf(m, "alarm\t\t: %ld.%02d seconds\n",
+ tv.tv_sec, (int)tv.tv_usec/1000);
+ }
+
+ if (hp_sdc_rtc_read_dt(&tv)) {
+ seq_puts(m, "delay\t\t: READ FAILED!\n");
+ } else {
+ seq_printf(m, "delay\t\t: %ld.%02d seconds\n",
+ tv.tv_sec, (int)tv.tv_usec/1000);
+ }
+
+ if (hp_sdc_rtc_read_ct(&tv)) {
+ seq_puts(m, "periodic\t: READ FAILED!\n");
+ } else {
+ seq_printf(m, "periodic\t: %ld.%02d seconds\n",
+ tv.tv_sec, (int)tv.tv_usec/1000);
+ }
+
+ seq_printf(m,
+ "DST_enable\t: %s\n"
+ "BCD\t\t: %s\n"
+ "24hr\t\t: %s\n"
+ "square_wave\t: %s\n"
+ "alarm_IRQ\t: %s\n"
+ "update_IRQ\t: %s\n"
+ "periodic_IRQ\t: %s\n"
+ "periodic_freq\t: %ld\n"
+ "batt_status\t: %s\n",
+ YN(RTC_DST_EN),
+ NY(RTC_DM_BINARY),
+ YN(RTC_24H),
+ YN(RTC_SQWE),
+ YN(RTC_AIE),
+ YN(RTC_UIE),
+ YN(RTC_PIE),
+ 1UL,
+ 1 ? "okay" : "dead");
+
+ return 0;
+#undef YN
+#undef NY
+}
+
+static int hp_sdc_rtc_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hp_sdc_rtc_proc_show, NULL);
+}
+
+static const struct file_operations hp_sdc_rtc_proc_fops = {
+ .open = hp_sdc_rtc_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int hp_sdc_rtc_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+#if 1
+ return -EINVAL;
+#else
+
+ struct rtc_time wtime;
+ struct timeval ttime;
+ int use_wtime = 0;
+
+ /* This needs major work. */
+
+ switch (cmd) {
+
+ case RTC_AIE_OFF: /* Mask alarm int. enab. bit */
+ case RTC_AIE_ON: /* Allow alarm interrupts. */
+ case RTC_PIE_OFF: /* Mask periodic int. enab. bit */
+ case RTC_PIE_ON: /* Allow periodic ints */
+ case RTC_UIE_ON: /* Allow ints for RTC updates. */
+ case RTC_UIE_OFF: /* Allow ints for RTC updates. */
+ {
+ /* We cannot mask individual user timers and we
+ cannot tell them apart when they occur, so it
+ would be disingenuous to succeed these IOCTLs */
+ return -EINVAL;
+ }
+ case RTC_ALM_READ: /* Read the present alarm time */
+ {
+ if (hp_sdc_rtc_read_mt(&ttime)) return -EFAULT;
+ if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
+
+ wtime.tm_hour = ttime.tv_sec / 3600; ttime.tv_sec %= 3600;
+ wtime.tm_min = ttime.tv_sec / 60; ttime.tv_sec %= 60;
+ wtime.tm_sec = ttime.tv_sec;
+
+ break;
+ }
+ case RTC_IRQP_READ: /* Read the periodic IRQ rate. */
+ {
+ return put_user(hp_sdc_rtc_freq, (unsigned long *)arg);
+ }
+ case RTC_IRQP_SET: /* Set periodic IRQ rate. */
+ {
+ /*
+ * The max we can do is 100Hz.
+ */
+
+ if ((arg < 1) || (arg > 100)) return -EINVAL;
+ ttime.tv_sec = 0;
+ ttime.tv_usec = 1000000 / arg;
+ if (hp_sdc_rtc_set_ct(&ttime)) return -EFAULT;
+ hp_sdc_rtc_freq = arg;
+ return 0;
+ }
+ case RTC_ALM_SET: /* Store a time into the alarm */
+ {
+ /*
+ * This expects a struct hp_sdc_rtc_time. Writing 0xff means
+ * "don't care" or "match all" for PC timers. The HP SDC
+ * does not support that perk, but it could be emulated fairly
+ * easily. Only the tm_hour, tm_min and tm_sec are used.
+ * We could do it with 10ms accuracy with the HP SDC, if the
+ * rtc interface left us a way to do that.
+ */
+ struct hp_sdc_rtc_time alm_tm;
+
+ if (copy_from_user(&alm_tm, (struct hp_sdc_rtc_time*)arg,
+ sizeof(struct hp_sdc_rtc_time)))
+ return -EFAULT;
+
+ if (alm_tm.tm_hour > 23) return -EINVAL;
+ if (alm_tm.tm_min > 59) return -EINVAL;
+ if (alm_tm.tm_sec > 59) return -EINVAL;
+
+ ttime.sec = alm_tm.tm_hour * 3600 +
+ alm_tm.tm_min * 60 + alm_tm.tm_sec;
+ ttime.usec = 0;
+ if (hp_sdc_rtc_set_mt(&ttime)) return -EFAULT;
+ return 0;
+ }
+ case RTC_RD_TIME: /* Read the time/date from RTC */
+ {
+ if (hp_sdc_rtc_read_bbrtc(&wtime)) return -EFAULT;
+ break;
+ }
+ case RTC_SET_TIME: /* Set the RTC */
+ {
+ struct rtc_time hp_sdc_rtc_tm;
+ unsigned char mon, day, hrs, min, sec, leap_yr;
+ unsigned int yrs;
+
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+ if (copy_from_user(&hp_sdc_rtc_tm, (struct rtc_time *)arg,
+ sizeof(struct rtc_time)))
+ return -EFAULT;
+
+ yrs = hp_sdc_rtc_tm.tm_year + 1900;
+ mon = hp_sdc_rtc_tm.tm_mon + 1; /* tm_mon starts at zero */
+ day = hp_sdc_rtc_tm.tm_mday;
+ hrs = hp_sdc_rtc_tm.tm_hour;
+ min = hp_sdc_rtc_tm.tm_min;
+ sec = hp_sdc_rtc_tm.tm_sec;
+
+ if (yrs < 1970)
+ return -EINVAL;
+
+ leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400));
+
+ if ((mon > 12) || (day == 0))
+ return -EINVAL;
+ if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr)))
+ return -EINVAL;
+ if ((hrs >= 24) || (min >= 60) || (sec >= 60))
+ return -EINVAL;
+
+ if ((yrs -= eH) > 255) /* They are unsigned */
+ return -EINVAL;
+
+
+ return 0;
+ }
+ case RTC_EPOCH_READ: /* Read the epoch. */
+ {
+ return put_user (epoch, (unsigned long *)arg);
+ }
+ case RTC_EPOCH_SET: /* Set the epoch. */
+ {
+ /*
+ * There were no RTC clocks before 1900.
+ */
+ if (arg < 1900)
+ return -EINVAL;
+ if (!capable(CAP_SYS_TIME))
+ return -EACCES;
+
+ epoch = arg;
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
+ return copy_to_user((void *)arg, &wtime, sizeof wtime) ? -EFAULT : 0;
+#endif
+}
+
+static long hp_sdc_rtc_unlocked_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret;
+
+ mutex_lock(&hp_sdc_rtc_mutex);
+ ret = hp_sdc_rtc_ioctl(file, cmd, arg);
+ mutex_unlock(&hp_sdc_rtc_mutex);
+
+ return ret;
+}
+
+
+static const struct file_operations hp_sdc_rtc_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = hp_sdc_rtc_read,
+ .poll = hp_sdc_rtc_poll,
+ .unlocked_ioctl = hp_sdc_rtc_unlocked_ioctl,
+ .open = hp_sdc_rtc_open,
+ .fasync = hp_sdc_rtc_fasync,
+};
+
+static struct miscdevice hp_sdc_rtc_dev = {
+ .minor = RTC_MINOR,
+ .name = "rtc_HIL",
+ .fops = &hp_sdc_rtc_fops
+};
+
+static int __init hp_sdc_rtc_init(void)
+{
+ int ret;
+
+#ifdef __mc68000__
+ if (!MACH_IS_HP300)
+ return -ENODEV;
+#endif
+
+ sema_init(&i8042tregs, 1);
+
+ if ((ret = hp_sdc_request_timer_irq(&hp_sdc_rtc_isr)))
+ return ret;
+ if (misc_register(&hp_sdc_rtc_dev) != 0)
+ printk(KERN_INFO "Could not register misc. dev for i8042 rtc\n");
+
+ proc_create("driver/rtc", 0, NULL, &hp_sdc_rtc_proc_fops);
+
+ printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support loaded "
+ "(RTC v " RTC_VERSION ")\n");
+
+ return 0;
+}
+
+static void __exit hp_sdc_rtc_exit(void)
+{
+ remove_proc_entry ("driver/rtc", NULL);
+ misc_deregister(&hp_sdc_rtc_dev);
+ hp_sdc_release_timer_irq(hp_sdc_rtc_isr);
+ printk(KERN_INFO "HP i8042 SDC + MSM-58321 RTC support unloaded\n");
+}
+
+module_init(hp_sdc_rtc_init);
+module_exit(hp_sdc_rtc_exit);
diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c
new file mode 100644
index 00000000..e204f26b
--- /dev/null
+++ b/drivers/input/misc/ims-pcu.c
@@ -0,0 +1,1901 @@
+/*
+ * Driver for IMS Passenger Control Unit Devices
+ *
+ * Copyright (C) 2013 The IMS Company
+ *
+ * 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/completion.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/usb/input.h>
+#include <linux/usb/cdc.h>
+#include <asm/unaligned.h>
+
+#define IMS_PCU_KEYMAP_LEN 32
+
+struct ims_pcu_buttons {
+ struct input_dev *input;
+ char name[32];
+ char phys[32];
+ unsigned short keymap[IMS_PCU_KEYMAP_LEN];
+};
+
+struct ims_pcu_gamepad {
+ struct input_dev *input;
+ char name[32];
+ char phys[32];
+};
+
+struct ims_pcu_backlight {
+ struct led_classdev cdev;
+ struct work_struct work;
+ enum led_brightness desired_brightness;
+ char name[32];
+};
+
+#define IMS_PCU_PART_NUMBER_LEN 15
+#define IMS_PCU_SERIAL_NUMBER_LEN 8
+#define IMS_PCU_DOM_LEN 8
+#define IMS_PCU_FW_VERSION_LEN (9 + 1)
+#define IMS_PCU_BL_VERSION_LEN (9 + 1)
+#define IMS_PCU_BL_RESET_REASON_LEN (2 + 1)
+
+#define IMS_PCU_BUF_SIZE 128
+
+struct ims_pcu {
+ struct usb_device *udev;
+ struct device *dev; /* control interface's device, used for logging */
+
+ unsigned int device_no;
+
+ bool bootloader_mode;
+
+ char part_number[IMS_PCU_PART_NUMBER_LEN];
+ char serial_number[IMS_PCU_SERIAL_NUMBER_LEN];
+ char date_of_manufacturing[IMS_PCU_DOM_LEN];
+ char fw_version[IMS_PCU_FW_VERSION_LEN];
+ char bl_version[IMS_PCU_BL_VERSION_LEN];
+ char reset_reason[IMS_PCU_BL_RESET_REASON_LEN];
+ int update_firmware_status;
+
+ struct usb_interface *ctrl_intf;
+
+ struct usb_endpoint_descriptor *ep_ctrl;
+ struct urb *urb_ctrl;
+ u8 *urb_ctrl_buf;
+ dma_addr_t ctrl_dma;
+ size_t max_ctrl_size;
+
+ struct usb_interface *data_intf;
+
+ struct usb_endpoint_descriptor *ep_in;
+ struct urb *urb_in;
+ u8 *urb_in_buf;
+ dma_addr_t read_dma;
+ size_t max_in_size;
+
+ struct usb_endpoint_descriptor *ep_out;
+ u8 *urb_out_buf;
+ size_t max_out_size;
+
+ u8 read_buf[IMS_PCU_BUF_SIZE];
+ u8 read_pos;
+ u8 check_sum;
+ bool have_stx;
+ bool have_dle;
+
+ u8 cmd_buf[IMS_PCU_BUF_SIZE];
+ u8 ack_id;
+ u8 expected_response;
+ u8 cmd_buf_len;
+ struct completion cmd_done;
+ struct mutex cmd_mutex;
+
+ u32 fw_start_addr;
+ u32 fw_end_addr;
+ struct completion async_firmware_done;
+
+ struct ims_pcu_buttons buttons;
+ struct ims_pcu_gamepad *gamepad;
+ struct ims_pcu_backlight backlight;
+
+ bool setup_complete; /* Input and LED devices have been created */
+};
+
+
+/*********************************************************************
+ * Buttons Input device support *
+ *********************************************************************/
+
+static const unsigned short ims_pcu_keymap_1[] = {
+ [1] = KEY_ATTENDANT_OFF,
+ [2] = KEY_ATTENDANT_ON,
+ [3] = KEY_LIGHTS_TOGGLE,
+ [4] = KEY_VOLUMEUP,
+ [5] = KEY_VOLUMEDOWN,
+ [6] = KEY_INFO,
+};
+
+static const unsigned short ims_pcu_keymap_2[] = {
+ [4] = KEY_VOLUMEUP,
+ [5] = KEY_VOLUMEDOWN,
+ [6] = KEY_INFO,
+};
+
+static const unsigned short ims_pcu_keymap_3[] = {
+ [1] = KEY_HOMEPAGE,
+ [2] = KEY_ATTENDANT_TOGGLE,
+ [3] = KEY_LIGHTS_TOGGLE,
+ [4] = KEY_VOLUMEUP,
+ [5] = KEY_VOLUMEDOWN,
+ [6] = KEY_DISPLAYTOGGLE,
+ [18] = KEY_PLAYPAUSE,
+};
+
+static const unsigned short ims_pcu_keymap_4[] = {
+ [1] = KEY_ATTENDANT_OFF,
+ [2] = KEY_ATTENDANT_ON,
+ [3] = KEY_LIGHTS_TOGGLE,
+ [4] = KEY_VOLUMEUP,
+ [5] = KEY_VOLUMEDOWN,
+ [6] = KEY_INFO,
+ [18] = KEY_PLAYPAUSE,
+};
+
+static const unsigned short ims_pcu_keymap_5[] = {
+ [1] = KEY_ATTENDANT_OFF,
+ [2] = KEY_ATTENDANT_ON,
+ [3] = KEY_LIGHTS_TOGGLE,
+};
+
+struct ims_pcu_device_info {
+ const unsigned short *keymap;
+ size_t keymap_len;
+ bool has_gamepad;
+};
+
+#define IMS_PCU_DEVINFO(_n, _gamepad) \
+ [_n] = { \
+ .keymap = ims_pcu_keymap_##_n, \
+ .keymap_len = ARRAY_SIZE(ims_pcu_keymap_##_n), \
+ .has_gamepad = _gamepad, \
+ }
+
+static const struct ims_pcu_device_info ims_pcu_device_info[] = {
+ IMS_PCU_DEVINFO(1, true),
+ IMS_PCU_DEVINFO(2, true),
+ IMS_PCU_DEVINFO(3, true),
+ IMS_PCU_DEVINFO(4, true),
+ IMS_PCU_DEVINFO(5, false),
+};
+
+static void ims_pcu_buttons_report(struct ims_pcu *pcu, u32 data)
+{
+ struct ims_pcu_buttons *buttons = &pcu->buttons;
+ struct input_dev *input = buttons->input;
+ int i;
+
+ for (i = 0; i < 32; i++) {
+ unsigned short keycode = buttons->keymap[i];
+
+ if (keycode != KEY_RESERVED)
+ input_report_key(input, keycode, data & (1UL << i));
+ }
+
+ input_sync(input);
+}
+
+static int ims_pcu_setup_buttons(struct ims_pcu *pcu,
+ const unsigned short *keymap,
+ size_t keymap_len)
+{
+ struct ims_pcu_buttons *buttons = &pcu->buttons;
+ struct input_dev *input;
+ int i;
+ int error;
+
+ input = input_allocate_device();
+ if (!input) {
+ dev_err(pcu->dev,
+ "Not enough memory for input input device\n");
+ return -ENOMEM;
+ }
+
+ snprintf(buttons->name, sizeof(buttons->name),
+ "IMS PCU#%d Button Interface", pcu->device_no);
+
+ usb_make_path(pcu->udev, buttons->phys, sizeof(buttons->phys));
+ strlcat(buttons->phys, "/input0", sizeof(buttons->phys));
+
+ memcpy(buttons->keymap, keymap, sizeof(*keymap) * keymap_len);
+
+ input->name = buttons->name;
+ input->phys = buttons->phys;
+ usb_to_input_id(pcu->udev, &input->id);
+ input->dev.parent = &pcu->ctrl_intf->dev;
+
+ input->keycode = buttons->keymap;
+ input->keycodemax = ARRAY_SIZE(buttons->keymap);
+ input->keycodesize = sizeof(buttons->keymap[0]);
+
+ __set_bit(EV_KEY, input->evbit);
+ for (i = 0; i < IMS_PCU_KEYMAP_LEN; i++)
+ __set_bit(buttons->keymap[i], input->keybit);
+ __clear_bit(KEY_RESERVED, input->keybit);
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(pcu->dev,
+ "Failed to register buttons input device: %d\n",
+ error);
+ input_free_device(input);
+ return error;
+ }
+
+ buttons->input = input;
+ return 0;
+}
+
+static void ims_pcu_destroy_buttons(struct ims_pcu *pcu)
+{
+ struct ims_pcu_buttons *buttons = &pcu->buttons;
+
+ input_unregister_device(buttons->input);
+}
+
+
+/*********************************************************************
+ * Gamepad Input device support *
+ *********************************************************************/
+
+static void ims_pcu_gamepad_report(struct ims_pcu *pcu, u32 data)
+{
+ struct ims_pcu_gamepad *gamepad = pcu->gamepad;
+ struct input_dev *input = gamepad->input;
+ int x, y;
+
+ x = !!(data & (1 << 14)) - !!(data & (1 << 13));
+ y = !!(data & (1 << 12)) - !!(data & (1 << 11));
+
+ input_report_abs(input, ABS_X, x);
+ input_report_abs(input, ABS_Y, y);
+
+ input_report_key(input, BTN_A, data & (1 << 7));
+ input_report_key(input, BTN_B, data & (1 << 8));
+ input_report_key(input, BTN_X, data & (1 << 9));
+ input_report_key(input, BTN_Y, data & (1 << 10));
+ input_report_key(input, BTN_START, data & (1 << 15));
+ input_report_key(input, BTN_SELECT, data & (1 << 16));
+
+ input_sync(input);
+}
+
+static int ims_pcu_setup_gamepad(struct ims_pcu *pcu)
+{
+ struct ims_pcu_gamepad *gamepad;
+ struct input_dev *input;
+ int error;
+
+ gamepad = kzalloc(sizeof(struct ims_pcu_gamepad), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!gamepad || !input) {
+ dev_err(pcu->dev,
+ "Not enough memory for gamepad device\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ gamepad->input = input;
+
+ snprintf(gamepad->name, sizeof(gamepad->name),
+ "IMS PCU#%d Gamepad Interface", pcu->device_no);
+
+ usb_make_path(pcu->udev, gamepad->phys, sizeof(gamepad->phys));
+ strlcat(gamepad->phys, "/input1", sizeof(gamepad->phys));
+
+ input->name = gamepad->name;
+ input->phys = gamepad->phys;
+ usb_to_input_id(pcu->udev, &input->id);
+ input->dev.parent = &pcu->ctrl_intf->dev;
+
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(BTN_A, input->keybit);
+ __set_bit(BTN_B, input->keybit);
+ __set_bit(BTN_X, input->keybit);
+ __set_bit(BTN_Y, input->keybit);
+ __set_bit(BTN_START, input->keybit);
+ __set_bit(BTN_SELECT, input->keybit);
+
+ __set_bit(EV_ABS, input->evbit);
+ input_set_abs_params(input, ABS_X, -1, 1, 0, 0);
+ input_set_abs_params(input, ABS_Y, -1, 1, 0, 0);
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(pcu->dev,
+ "Failed to register gamepad input device: %d\n",
+ error);
+ goto err_free_mem;
+ }
+
+ pcu->gamepad = gamepad;
+ return 0;
+
+err_free_mem:
+ input_free_device(input);
+ kfree(gamepad);
+ return -ENOMEM;
+}
+
+static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu)
+{
+ struct ims_pcu_gamepad *gamepad = pcu->gamepad;
+
+ input_unregister_device(gamepad->input);
+ kfree(gamepad);
+}
+
+
+/*********************************************************************
+ * PCU Communication protocol handling *
+ *********************************************************************/
+
+#define IMS_PCU_PROTOCOL_STX 0x02
+#define IMS_PCU_PROTOCOL_ETX 0x03
+#define IMS_PCU_PROTOCOL_DLE 0x10
+
+/* PCU commands */
+#define IMS_PCU_CMD_STATUS 0xa0
+#define IMS_PCU_CMD_PCU_RESET 0xa1
+#define IMS_PCU_CMD_RESET_REASON 0xa2
+#define IMS_PCU_CMD_SEND_BUTTONS 0xa3
+#define IMS_PCU_CMD_JUMP_TO_BTLDR 0xa4
+#define IMS_PCU_CMD_GET_INFO 0xa5
+#define IMS_PCU_CMD_SET_BRIGHTNESS 0xa6
+#define IMS_PCU_CMD_EEPROM 0xa7
+#define IMS_PCU_CMD_GET_FW_VERSION 0xa8
+#define IMS_PCU_CMD_GET_BL_VERSION 0xa9
+#define IMS_PCU_CMD_SET_INFO 0xab
+#define IMS_PCU_CMD_GET_BRIGHTNESS 0xac
+#define IMS_PCU_CMD_GET_DEVICE_ID 0xae
+#define IMS_PCU_CMD_SPECIAL_INFO 0xb0
+#define IMS_PCU_CMD_BOOTLOADER 0xb1 /* Pass data to bootloader */
+
+/* PCU responses */
+#define IMS_PCU_RSP_STATUS 0xc0
+#define IMS_PCU_RSP_PCU_RESET 0 /* Originally 0xc1 */
+#define IMS_PCU_RSP_RESET_REASON 0xc2
+#define IMS_PCU_RSP_SEND_BUTTONS 0xc3
+#define IMS_PCU_RSP_JUMP_TO_BTLDR 0 /* Originally 0xc4 */
+#define IMS_PCU_RSP_GET_INFO 0xc5
+#define IMS_PCU_RSP_SET_BRIGHTNESS 0xc6
+#define IMS_PCU_RSP_EEPROM 0xc7
+#define IMS_PCU_RSP_GET_FW_VERSION 0xc8
+#define IMS_PCU_RSP_GET_BL_VERSION 0xc9
+#define IMS_PCU_RSP_SET_INFO 0xcb
+#define IMS_PCU_RSP_GET_BRIGHTNESS 0xcc
+#define IMS_PCU_RSP_CMD_INVALID 0xcd
+#define IMS_PCU_RSP_GET_DEVICE_ID 0xce
+#define IMS_PCU_RSP_SPECIAL_INFO 0xd0
+#define IMS_PCU_RSP_BOOTLOADER 0xd1 /* Bootloader response */
+
+#define IMS_PCU_RSP_EVNT_BUTTONS 0xe0 /* Unsolicited, button state */
+#define IMS_PCU_GAMEPAD_MASK 0x0001ff80UL /* Bits 7 through 16 */
+
+
+#define IMS_PCU_MIN_PACKET_LEN 3
+#define IMS_PCU_DATA_OFFSET 2
+
+#define IMS_PCU_CMD_WRITE_TIMEOUT 100 /* msec */
+#define IMS_PCU_CMD_RESPONSE_TIMEOUT 500 /* msec */
+
+static void ims_pcu_report_events(struct ims_pcu *pcu)
+{
+ u32 data = get_unaligned_be32(&pcu->read_buf[3]);
+
+ ims_pcu_buttons_report(pcu, data & ~IMS_PCU_GAMEPAD_MASK);
+ if (pcu->gamepad)
+ ims_pcu_gamepad_report(pcu, data);
+}
+
+static void ims_pcu_handle_response(struct ims_pcu *pcu)
+{
+ switch (pcu->read_buf[0]) {
+ case IMS_PCU_RSP_EVNT_BUTTONS:
+ if (likely(pcu->setup_complete))
+ ims_pcu_report_events(pcu);
+ break;
+
+ default:
+ /*
+ * See if we got command completion.
+ * If both the sequence and response code match save
+ * the data and signal completion.
+ */
+ if (pcu->read_buf[0] == pcu->expected_response &&
+ pcu->read_buf[1] == pcu->ack_id - 1) {
+
+ memcpy(pcu->cmd_buf, pcu->read_buf, pcu->read_pos);
+ pcu->cmd_buf_len = pcu->read_pos;
+ complete(&pcu->cmd_done);
+ }
+ break;
+ }
+}
+
+static void ims_pcu_process_data(struct ims_pcu *pcu, struct urb *urb)
+{
+ int i;
+
+ for (i = 0; i < urb->actual_length; i++) {
+ u8 data = pcu->urb_in_buf[i];
+
+ /* Skip everything until we get Start Xmit */
+ if (!pcu->have_stx && data != IMS_PCU_PROTOCOL_STX)
+ continue;
+
+ if (pcu->have_dle) {
+ pcu->have_dle = false;
+ pcu->read_buf[pcu->read_pos++] = data;
+ pcu->check_sum += data;
+ continue;
+ }
+
+ switch (data) {
+ case IMS_PCU_PROTOCOL_STX:
+ if (pcu->have_stx)
+ dev_warn(pcu->dev,
+ "Unexpected STX at byte %d, discarding old data\n",
+ pcu->read_pos);
+ pcu->have_stx = true;
+ pcu->have_dle = false;
+ pcu->read_pos = 0;
+ pcu->check_sum = 0;
+ break;
+
+ case IMS_PCU_PROTOCOL_DLE:
+ pcu->have_dle = true;
+ break;
+
+ case IMS_PCU_PROTOCOL_ETX:
+ if (pcu->read_pos < IMS_PCU_MIN_PACKET_LEN) {
+ dev_warn(pcu->dev,
+ "Short packet received (%d bytes), ignoring\n",
+ pcu->read_pos);
+ } else if (pcu->check_sum != 0) {
+ dev_warn(pcu->dev,
+ "Invalid checksum in packet (%d bytes), ignoring\n",
+ pcu->read_pos);
+ } else {
+ ims_pcu_handle_response(pcu);
+ }
+
+ pcu->have_stx = false;
+ pcu->have_dle = false;
+ pcu->read_pos = 0;
+ break;
+
+ default:
+ pcu->read_buf[pcu->read_pos++] = data;
+ pcu->check_sum += data;
+ break;
+ }
+ }
+}
+
+static bool ims_pcu_byte_needs_escape(u8 byte)
+{
+ return byte == IMS_PCU_PROTOCOL_STX ||
+ byte == IMS_PCU_PROTOCOL_ETX ||
+ byte == IMS_PCU_PROTOCOL_DLE;
+}
+
+static int ims_pcu_send_cmd_chunk(struct ims_pcu *pcu,
+ u8 command, int chunk, int len)
+{
+ int error;
+
+ error = usb_bulk_msg(pcu->udev,
+ usb_sndbulkpipe(pcu->udev,
+ pcu->ep_out->bEndpointAddress),
+ pcu->urb_out_buf, len,
+ NULL, IMS_PCU_CMD_WRITE_TIMEOUT);
+ if (error < 0) {
+ dev_dbg(pcu->dev,
+ "Sending 0x%02x command failed at chunk %d: %d\n",
+ command, chunk, error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int ims_pcu_send_command(struct ims_pcu *pcu,
+ u8 command, const u8 *data, int len)
+{
+ int count = 0;
+ int chunk = 0;
+ int delta;
+ int i;
+ int error;
+ u8 csum = 0;
+ u8 ack_id;
+
+ pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_STX;
+
+ /* We know the command need not be escaped */
+ pcu->urb_out_buf[count++] = command;
+ csum += command;
+
+ ack_id = pcu->ack_id++;
+ if (ack_id == 0xff)
+ ack_id = pcu->ack_id++;
+
+ if (ims_pcu_byte_needs_escape(ack_id))
+ pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE;
+
+ pcu->urb_out_buf[count++] = ack_id;
+ csum += ack_id;
+
+ for (i = 0; i < len; i++) {
+
+ delta = ims_pcu_byte_needs_escape(data[i]) ? 2 : 1;
+ if (count + delta >= pcu->max_out_size) {
+ error = ims_pcu_send_cmd_chunk(pcu, command,
+ ++chunk, count);
+ if (error)
+ return error;
+
+ count = 0;
+ }
+
+ if (delta == 2)
+ pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE;
+
+ pcu->urb_out_buf[count++] = data[i];
+ csum += data[i];
+ }
+
+ csum = 1 + ~csum;
+
+ delta = ims_pcu_byte_needs_escape(csum) ? 3 : 2;
+ if (count + delta >= pcu->max_out_size) {
+ error = ims_pcu_send_cmd_chunk(pcu, command, ++chunk, count);
+ if (error)
+ return error;
+
+ count = 0;
+ }
+
+ if (delta == 3)
+ pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_DLE;
+
+ pcu->urb_out_buf[count++] = csum;
+ pcu->urb_out_buf[count++] = IMS_PCU_PROTOCOL_ETX;
+
+ return ims_pcu_send_cmd_chunk(pcu, command, ++chunk, count);
+}
+
+static int __ims_pcu_execute_command(struct ims_pcu *pcu,
+ u8 command, const void *data, size_t len,
+ u8 expected_response, int response_time)
+{
+ int error;
+
+ pcu->expected_response = expected_response;
+ init_completion(&pcu->cmd_done);
+
+ error = ims_pcu_send_command(pcu, command, data, len);
+ if (error)
+ return error;
+
+ if (expected_response &&
+ !wait_for_completion_timeout(&pcu->cmd_done,
+ msecs_to_jiffies(response_time))) {
+ dev_dbg(pcu->dev, "Command 0x%02x timed out\n", command);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+#define ims_pcu_execute_command(pcu, code, data, len) \
+ __ims_pcu_execute_command(pcu, \
+ IMS_PCU_CMD_##code, data, len, \
+ IMS_PCU_RSP_##code, \
+ IMS_PCU_CMD_RESPONSE_TIMEOUT)
+
+#define ims_pcu_execute_query(pcu, code) \
+ ims_pcu_execute_command(pcu, code, NULL, 0)
+
+/* Bootloader commands */
+#define IMS_PCU_BL_CMD_QUERY_DEVICE 0xa1
+#define IMS_PCU_BL_CMD_UNLOCK_CONFIG 0xa2
+#define IMS_PCU_BL_CMD_ERASE_APP 0xa3
+#define IMS_PCU_BL_CMD_PROGRAM_DEVICE 0xa4
+#define IMS_PCU_BL_CMD_PROGRAM_COMPLETE 0xa5
+#define IMS_PCU_BL_CMD_READ_APP 0xa6
+#define IMS_PCU_BL_CMD_RESET_DEVICE 0xa7
+#define IMS_PCU_BL_CMD_LAUNCH_APP 0xa8
+
+/* Bootloader commands */
+#define IMS_PCU_BL_RSP_QUERY_DEVICE 0xc1
+#define IMS_PCU_BL_RSP_UNLOCK_CONFIG 0xc2
+#define IMS_PCU_BL_RSP_ERASE_APP 0xc3
+#define IMS_PCU_BL_RSP_PROGRAM_DEVICE 0xc4
+#define IMS_PCU_BL_RSP_PROGRAM_COMPLETE 0xc5
+#define IMS_PCU_BL_RSP_READ_APP 0xc6
+#define IMS_PCU_BL_RSP_RESET_DEVICE 0 /* originally 0xa7 */
+#define IMS_PCU_BL_RSP_LAUNCH_APP 0 /* originally 0xa8 */
+
+#define IMS_PCU_BL_DATA_OFFSET 3
+
+static int __ims_pcu_execute_bl_command(struct ims_pcu *pcu,
+ u8 command, const void *data, size_t len,
+ u8 expected_response, int response_time)
+{
+ int error;
+
+ pcu->cmd_buf[0] = command;
+ if (data)
+ memcpy(&pcu->cmd_buf[1], data, len);
+
+ error = __ims_pcu_execute_command(pcu,
+ IMS_PCU_CMD_BOOTLOADER, pcu->cmd_buf, len + 1,
+ expected_response ? IMS_PCU_RSP_BOOTLOADER : 0,
+ response_time);
+ if (error) {
+ dev_err(pcu->dev,
+ "Failure when sending 0x%02x command to bootloader, error: %d\n",
+ pcu->cmd_buf[0], error);
+ return error;
+ }
+
+ if (expected_response && pcu->cmd_buf[2] != expected_response) {
+ dev_err(pcu->dev,
+ "Unexpected response from bootloader: 0x%02x, wanted 0x%02x\n",
+ pcu->cmd_buf[2], expected_response);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+#define ims_pcu_execute_bl_command(pcu, code, data, len, timeout) \
+ __ims_pcu_execute_bl_command(pcu, \
+ IMS_PCU_BL_CMD_##code, data, len, \
+ IMS_PCU_BL_RSP_##code, timeout) \
+
+#define IMS_PCU_INFO_PART_OFFSET 2
+#define IMS_PCU_INFO_DOM_OFFSET 17
+#define IMS_PCU_INFO_SERIAL_OFFSET 25
+
+#define IMS_PCU_SET_INFO_SIZE 31
+
+static int ims_pcu_get_info(struct ims_pcu *pcu)
+{
+ int error;
+
+ error = ims_pcu_execute_query(pcu, GET_INFO);
+ if (error) {
+ dev_err(pcu->dev,
+ "GET_INFO command failed, error: %d\n", error);
+ return error;
+ }
+
+ memcpy(pcu->part_number,
+ &pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET],
+ sizeof(pcu->part_number));
+ memcpy(pcu->date_of_manufacturing,
+ &pcu->cmd_buf[IMS_PCU_INFO_DOM_OFFSET],
+ sizeof(pcu->date_of_manufacturing));
+ memcpy(pcu->serial_number,
+ &pcu->cmd_buf[IMS_PCU_INFO_SERIAL_OFFSET],
+ sizeof(pcu->serial_number));
+
+ return 0;
+}
+
+static int ims_pcu_set_info(struct ims_pcu *pcu)
+{
+ int error;
+
+ memcpy(&pcu->cmd_buf[IMS_PCU_INFO_PART_OFFSET],
+ pcu->part_number, sizeof(pcu->part_number));
+ memcpy(&pcu->cmd_buf[IMS_PCU_INFO_DOM_OFFSET],
+ pcu->date_of_manufacturing, sizeof(pcu->date_of_manufacturing));
+ memcpy(&pcu->cmd_buf[IMS_PCU_INFO_SERIAL_OFFSET],
+ pcu->serial_number, sizeof(pcu->serial_number));
+
+ error = ims_pcu_execute_command(pcu, SET_INFO,
+ &pcu->cmd_buf[IMS_PCU_DATA_OFFSET],
+ IMS_PCU_SET_INFO_SIZE);
+ if (error) {
+ dev_err(pcu->dev,
+ "Failed to update device information, error: %d\n",
+ error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int ims_pcu_switch_to_bootloader(struct ims_pcu *pcu)
+{
+ int error;
+
+ /* Execute jump to the bootoloader */
+ error = ims_pcu_execute_command(pcu, JUMP_TO_BTLDR, NULL, 0);
+ if (error) {
+ dev_err(pcu->dev,
+ "Failure when sending JUMP TO BOOLTLOADER command, error: %d\n",
+ error);
+ return error;
+ }
+
+ return 0;
+}
+
+/*********************************************************************
+ * Firmware Update handling *
+ *********************************************************************/
+
+#define IMS_PCU_FIRMWARE_NAME "imspcu.fw"
+
+struct ims_pcu_flash_fmt {
+ __le32 addr;
+ u8 len;
+ u8 data[];
+};
+
+static unsigned int ims_pcu_count_fw_records(const struct firmware *fw)
+{
+ const struct ihex_binrec *rec = (const struct ihex_binrec *)fw->data;
+ unsigned int count = 0;
+
+ while (rec) {
+ count++;
+ rec = ihex_next_binrec(rec);
+ }
+
+ return count;
+}
+
+static int ims_pcu_verify_block(struct ims_pcu *pcu,
+ u32 addr, u8 len, const u8 *data)
+{
+ struct ims_pcu_flash_fmt *fragment;
+ int error;
+
+ fragment = (void *)&pcu->cmd_buf[1];
+ put_unaligned_le32(addr, &fragment->addr);
+ fragment->len = len;
+
+ error = ims_pcu_execute_bl_command(pcu, READ_APP, NULL, 5,
+ IMS_PCU_CMD_RESPONSE_TIMEOUT);
+ if (error) {
+ dev_err(pcu->dev,
+ "Failed to retrieve block at 0x%08x, len %d, error: %d\n",
+ addr, len, error);
+ return error;
+ }
+
+ fragment = (void *)&pcu->cmd_buf[IMS_PCU_BL_DATA_OFFSET];
+ if (get_unaligned_le32(&fragment->addr) != addr ||
+ fragment->len != len) {
+ dev_err(pcu->dev,
+ "Wrong block when retrieving 0x%08x (0x%08x), len %d (%d)\n",
+ addr, get_unaligned_le32(&fragment->addr),
+ len, fragment->len);
+ return -EINVAL;
+ }
+
+ if (memcmp(fragment->data, data, len)) {
+ dev_err(pcu->dev,
+ "Mismatch in block at 0x%08x, len %d\n",
+ addr, len);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ims_pcu_flash_firmware(struct ims_pcu *pcu,
+ const struct firmware *fw,
+ unsigned int n_fw_records)
+{
+ const struct ihex_binrec *rec = (const struct ihex_binrec *)fw->data;
+ struct ims_pcu_flash_fmt *fragment;
+ unsigned int count = 0;
+ u32 addr;
+ u8 len;
+ int error;
+
+ error = ims_pcu_execute_bl_command(pcu, ERASE_APP, NULL, 0, 2000);
+ if (error) {
+ dev_err(pcu->dev,
+ "Failed to erase application image, error: %d\n",
+ error);
+ return error;
+ }
+
+ while (rec) {
+ /*
+ * The firmware format is messed up for some reason.
+ * The address twice that of what is needed for some
+ * reason and we end up overwriting half of the data
+ * with the next record.
+ */
+ addr = be32_to_cpu(rec->addr) / 2;
+ len = be16_to_cpu(rec->len);
+
+ fragment = (void *)&pcu->cmd_buf[1];
+ put_unaligned_le32(addr, &fragment->addr);
+ fragment->len = len;
+ memcpy(fragment->data, rec->data, len);
+
+ error = ims_pcu_execute_bl_command(pcu, PROGRAM_DEVICE,
+ NULL, len + 5,
+ IMS_PCU_CMD_RESPONSE_TIMEOUT);
+ if (error) {
+ dev_err(pcu->dev,
+ "Failed to write block at 0x%08x, len %d, error: %d\n",
+ addr, len, error);
+ return error;
+ }
+
+ if (addr >= pcu->fw_start_addr && addr < pcu->fw_end_addr) {
+ error = ims_pcu_verify_block(pcu, addr, len, rec->data);
+ if (error)
+ return error;
+ }
+
+ count++;
+ pcu->update_firmware_status = (count * 100) / n_fw_records;
+
+ rec = ihex_next_binrec(rec);
+ }
+
+ error = ims_pcu_execute_bl_command(pcu, PROGRAM_COMPLETE,
+ NULL, 0, 2000);
+ if (error)
+ dev_err(pcu->dev,
+ "Failed to send PROGRAM_COMPLETE, error: %d\n",
+ error);
+
+ return 0;
+}
+
+static int ims_pcu_handle_firmware_update(struct ims_pcu *pcu,
+ const struct firmware *fw)
+{
+ unsigned int n_fw_records;
+ int retval;
+
+ dev_info(pcu->dev, "Updating firmware %s, size: %zu\n",
+ IMS_PCU_FIRMWARE_NAME, fw->size);
+
+ n_fw_records = ims_pcu_count_fw_records(fw);
+
+ retval = ims_pcu_flash_firmware(pcu, fw, n_fw_records);
+ if (retval)
+ goto out;
+
+ retval = ims_pcu_execute_bl_command(pcu, LAUNCH_APP, NULL, 0, 0);
+ if (retval)
+ dev_err(pcu->dev,
+ "Failed to start application image, error: %d\n",
+ retval);
+
+out:
+ pcu->update_firmware_status = retval;
+ sysfs_notify(&pcu->dev->kobj, NULL, "update_firmware_status");
+ return retval;
+}
+
+static void ims_pcu_process_async_firmware(const struct firmware *fw,
+ void *context)
+{
+ struct ims_pcu *pcu = context;
+ int error;
+
+ if (!fw) {
+ dev_err(pcu->dev, "Failed to get firmware %s\n",
+ IMS_PCU_FIRMWARE_NAME);
+ goto out;
+ }
+
+ error = ihex_validate_fw(fw);
+ if (error) {
+ dev_err(pcu->dev, "Firmware %s is invalid\n",
+ IMS_PCU_FIRMWARE_NAME);
+ goto out;
+ }
+
+ mutex_lock(&pcu->cmd_mutex);
+ ims_pcu_handle_firmware_update(pcu, fw);
+ mutex_unlock(&pcu->cmd_mutex);
+
+ release_firmware(fw);
+
+out:
+ complete(&pcu->async_firmware_done);
+}
+
+/*********************************************************************
+ * Backlight LED device support *
+ *********************************************************************/
+
+#define IMS_PCU_MAX_BRIGHTNESS 31998
+
+static void ims_pcu_backlight_work(struct work_struct *work)
+{
+ struct ims_pcu_backlight *backlight =
+ container_of(work, struct ims_pcu_backlight, work);
+ struct ims_pcu *pcu =
+ container_of(backlight, struct ims_pcu, backlight);
+ int desired_brightness = backlight->desired_brightness;
+ __le16 br_val = cpu_to_le16(desired_brightness);
+ int error;
+
+ mutex_lock(&pcu->cmd_mutex);
+
+ error = ims_pcu_execute_command(pcu, SET_BRIGHTNESS,
+ &br_val, sizeof(br_val));
+ if (error && error != -ENODEV)
+ dev_warn(pcu->dev,
+ "Failed to set desired brightness %u, error: %d\n",
+ desired_brightness, error);
+
+ mutex_unlock(&pcu->cmd_mutex);
+}
+
+static void ims_pcu_backlight_set_brightness(struct led_classdev *cdev,
+ enum led_brightness value)
+{
+ struct ims_pcu_backlight *backlight =
+ container_of(cdev, struct ims_pcu_backlight, cdev);
+
+ backlight->desired_brightness = value;
+ schedule_work(&backlight->work);
+}
+
+static enum led_brightness
+ims_pcu_backlight_get_brightness(struct led_classdev *cdev)
+{
+ struct ims_pcu_backlight *backlight =
+ container_of(cdev, struct ims_pcu_backlight, cdev);
+ struct ims_pcu *pcu =
+ container_of(backlight, struct ims_pcu, backlight);
+ int brightness;
+ int error;
+
+ mutex_lock(&pcu->cmd_mutex);
+
+ error = ims_pcu_execute_query(pcu, GET_BRIGHTNESS);
+ if (error) {
+ dev_warn(pcu->dev,
+ "Failed to get current brightness, error: %d\n",
+ error);
+ /* Assume the LED is OFF */
+ brightness = LED_OFF;
+ } else {
+ brightness =
+ get_unaligned_le16(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET]);
+ }
+
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return brightness;
+}
+
+static int ims_pcu_setup_backlight(struct ims_pcu *pcu)
+{
+ struct ims_pcu_backlight *backlight = &pcu->backlight;
+ int error;
+
+ INIT_WORK(&backlight->work, ims_pcu_backlight_work);
+ snprintf(backlight->name, sizeof(backlight->name),
+ "pcu%d::kbd_backlight", pcu->device_no);
+
+ backlight->cdev.name = backlight->name;
+ backlight->cdev.max_brightness = IMS_PCU_MAX_BRIGHTNESS;
+ backlight->cdev.brightness_get = ims_pcu_backlight_get_brightness;
+ backlight->cdev.brightness_set = ims_pcu_backlight_set_brightness;
+
+ error = led_classdev_register(pcu->dev, &backlight->cdev);
+ if (error) {
+ dev_err(pcu->dev,
+ "Failed to register backlight LED device, error: %d\n",
+ error);
+ return error;
+ }
+
+ return 0;
+}
+
+static void ims_pcu_destroy_backlight(struct ims_pcu *pcu)
+{
+ struct ims_pcu_backlight *backlight = &pcu->backlight;
+
+ led_classdev_unregister(&backlight->cdev);
+ cancel_work_sync(&backlight->work);
+}
+
+
+/*********************************************************************
+ * Sysfs attributes handling *
+ *********************************************************************/
+
+struct ims_pcu_attribute {
+ struct device_attribute dattr;
+ size_t field_offset;
+ int field_length;
+};
+
+static ssize_t ims_pcu_attribute_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ struct ims_pcu_attribute *attr =
+ container_of(dattr, struct ims_pcu_attribute, dattr);
+ char *field = (char *)pcu + attr->field_offset;
+
+ return scnprintf(buf, PAGE_SIZE, "%.*s\n", attr->field_length, field);
+}
+
+static ssize_t ims_pcu_attribute_store(struct device *dev,
+ struct device_attribute *dattr,
+ const char *buf, size_t count)
+{
+
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ struct ims_pcu_attribute *attr =
+ container_of(dattr, struct ims_pcu_attribute, dattr);
+ char *field = (char *)pcu + attr->field_offset;
+ size_t data_len;
+ int error;
+
+ if (count > attr->field_length)
+ return -EINVAL;
+
+ data_len = strnlen(buf, attr->field_length);
+ if (data_len > attr->field_length)
+ return -EINVAL;
+
+ error = mutex_lock_interruptible(&pcu->cmd_mutex);
+ if (error)
+ return error;
+
+ memset(field, 0, attr->field_length);
+ memcpy(field, buf, data_len);
+
+ error = ims_pcu_set_info(pcu);
+
+ /*
+ * Even if update failed, let's fetch the info again as we just
+ * clobbered one of the fields.
+ */
+ ims_pcu_get_info(pcu);
+
+ mutex_unlock(&pcu->cmd_mutex);
+
+ return error < 0 ? error : count;
+}
+
+#define IMS_PCU_ATTR(_field, _mode) \
+struct ims_pcu_attribute ims_pcu_attr_##_field = { \
+ .dattr = __ATTR(_field, _mode, \
+ ims_pcu_attribute_show, \
+ ims_pcu_attribute_store), \
+ .field_offset = offsetof(struct ims_pcu, _field), \
+ .field_length = sizeof(((struct ims_pcu *)NULL)->_field), \
+}
+
+#define IMS_PCU_RO_ATTR(_field) \
+ IMS_PCU_ATTR(_field, S_IRUGO)
+#define IMS_PCU_RW_ATTR(_field) \
+ IMS_PCU_ATTR(_field, S_IRUGO | S_IWUSR)
+
+static IMS_PCU_RW_ATTR(part_number);
+static IMS_PCU_RW_ATTR(serial_number);
+static IMS_PCU_RW_ATTR(date_of_manufacturing);
+
+static IMS_PCU_RO_ATTR(fw_version);
+static IMS_PCU_RO_ATTR(bl_version);
+static IMS_PCU_RO_ATTR(reset_reason);
+
+static ssize_t ims_pcu_reset_device(struct device *dev,
+ struct device_attribute *dattr,
+ const char *buf, size_t count)
+{
+ static const u8 reset_byte = 1;
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ int value;
+ int error;
+
+ error = kstrtoint(buf, 0, &value);
+ if (error)
+ return error;
+
+ if (value != 1)
+ return -EINVAL;
+
+ dev_info(pcu->dev, "Attempting to reset device\n");
+
+ error = ims_pcu_execute_command(pcu, PCU_RESET, &reset_byte, 1);
+ if (error) {
+ dev_info(pcu->dev,
+ "Failed to reset device, error: %d\n",
+ error);
+ return error;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(reset_device, S_IWUSR, NULL, ims_pcu_reset_device);
+
+static ssize_t ims_pcu_update_firmware_store(struct device *dev,
+ struct device_attribute *dattr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ const struct firmware *fw = NULL;
+ int value;
+ int error;
+
+ error = kstrtoint(buf, 0, &value);
+ if (error)
+ return error;
+
+ if (value != 1)
+ return -EINVAL;
+
+ error = mutex_lock_interruptible(&pcu->cmd_mutex);
+ if (error)
+ return error;
+
+ error = request_ihex_firmware(&fw, IMS_PCU_FIRMWARE_NAME, pcu->dev);
+ if (error) {
+ dev_err(pcu->dev, "Failed to request firmware %s, error: %d\n",
+ IMS_PCU_FIRMWARE_NAME, error);
+ goto out;
+ }
+
+ /*
+ * If we are already in bootloader mode we can proceed with
+ * flashing the firmware.
+ *
+ * If we are in application mode, then we need to switch into
+ * bootloader mode, which will cause the device to disconnect
+ * and reconnect as different device.
+ */
+ if (pcu->bootloader_mode)
+ error = ims_pcu_handle_firmware_update(pcu, fw);
+ else
+ error = ims_pcu_switch_to_bootloader(pcu);
+
+ release_firmware(fw);
+
+out:
+ mutex_unlock(&pcu->cmd_mutex);
+ return error ?: count;
+}
+
+static DEVICE_ATTR(update_firmware, S_IWUSR,
+ NULL, ims_pcu_update_firmware_store);
+
+static ssize_t
+ims_pcu_update_firmware_status_show(struct device *dev,
+ struct device_attribute *dattr,
+ char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", pcu->update_firmware_status);
+}
+
+static DEVICE_ATTR(update_firmware_status, S_IRUGO,
+ ims_pcu_update_firmware_status_show, NULL);
+
+static struct attribute *ims_pcu_attrs[] = {
+ &ims_pcu_attr_part_number.dattr.attr,
+ &ims_pcu_attr_serial_number.dattr.attr,
+ &ims_pcu_attr_date_of_manufacturing.dattr.attr,
+ &ims_pcu_attr_fw_version.dattr.attr,
+ &ims_pcu_attr_bl_version.dattr.attr,
+ &ims_pcu_attr_reset_reason.dattr.attr,
+ &dev_attr_reset_device.attr,
+ &dev_attr_update_firmware.attr,
+ &dev_attr_update_firmware_status.attr,
+ NULL
+};
+
+static umode_t ims_pcu_is_attr_visible(struct kobject *kobj,
+ struct attribute *attr, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ umode_t mode = attr->mode;
+
+ if (pcu->bootloader_mode) {
+ if (attr != &dev_attr_update_firmware_status.attr &&
+ attr != &dev_attr_update_firmware.attr &&
+ attr != &dev_attr_reset_device.attr) {
+ mode = 0;
+ }
+ } else {
+ if (attr == &dev_attr_update_firmware_status.attr)
+ mode = 0;
+ }
+
+ return mode;
+}
+
+static struct attribute_group ims_pcu_attr_group = {
+ .is_visible = ims_pcu_is_attr_visible,
+ .attrs = ims_pcu_attrs,
+};
+
+static void ims_pcu_irq(struct urb *urb)
+{
+ struct ims_pcu *pcu = urb->context;
+ int retval, status;
+
+ status = urb->status;
+
+ switch (status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dev_dbg(pcu->dev, "%s - urb shutting down with status: %d\n",
+ __func__, status);
+ return;
+ default:
+ dev_dbg(pcu->dev, "%s - nonzero urb status received: %d\n",
+ __func__, status);
+ goto exit;
+ }
+
+ dev_dbg(pcu->dev, "%s: received %d: %*ph\n", __func__,
+ urb->actual_length, urb->actual_length, pcu->urb_in_buf);
+
+ if (urb == pcu->urb_in)
+ ims_pcu_process_data(pcu, urb);
+
+exit:
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval && retval != -ENODEV)
+ dev_err(pcu->dev, "%s - usb_submit_urb failed with result %d\n",
+ __func__, retval);
+}
+
+static int ims_pcu_buffers_alloc(struct ims_pcu *pcu)
+{
+ int error;
+
+ pcu->urb_in_buf = usb_alloc_coherent(pcu->udev, pcu->max_in_size,
+ GFP_KERNEL, &pcu->read_dma);
+ if (!pcu->urb_in_buf) {
+ dev_err(pcu->dev,
+ "Failed to allocate memory for read buffer\n");
+ return -ENOMEM;
+ }
+
+ pcu->urb_in = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pcu->urb_in) {
+ dev_err(pcu->dev, "Failed to allocate input URB\n");
+ error = -ENOMEM;
+ goto err_free_urb_in_buf;
+ }
+
+ pcu->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ pcu->urb_in->transfer_dma = pcu->read_dma;
+
+ usb_fill_bulk_urb(pcu->urb_in, pcu->udev,
+ usb_rcvbulkpipe(pcu->udev,
+ pcu->ep_in->bEndpointAddress),
+ pcu->urb_in_buf, pcu->max_in_size,
+ ims_pcu_irq, pcu);
+
+ /*
+ * We are using usb_bulk_msg() for sending so there is no point
+ * in allocating memory with usb_alloc_coherent().
+ */
+ pcu->urb_out_buf = kmalloc(pcu->max_out_size, GFP_KERNEL);
+ if (!pcu->urb_out_buf) {
+ dev_err(pcu->dev, "Failed to allocate memory for write buffer\n");
+ error = -ENOMEM;
+ goto err_free_in_urb;
+ }
+
+ pcu->urb_ctrl_buf = usb_alloc_coherent(pcu->udev, pcu->max_ctrl_size,
+ GFP_KERNEL, &pcu->ctrl_dma);
+ if (!pcu->urb_ctrl_buf) {
+ dev_err(pcu->dev,
+ "Failed to allocate memory for read buffer\n");
+ goto err_free_urb_out_buf;
+ }
+
+ pcu->urb_ctrl = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pcu->urb_ctrl) {
+ dev_err(pcu->dev, "Failed to allocate input URB\n");
+ error = -ENOMEM;
+ goto err_free_urb_ctrl_buf;
+ }
+
+ pcu->urb_ctrl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ pcu->urb_ctrl->transfer_dma = pcu->ctrl_dma;
+
+ usb_fill_int_urb(pcu->urb_ctrl, pcu->udev,
+ usb_rcvintpipe(pcu->udev,
+ pcu->ep_ctrl->bEndpointAddress),
+ pcu->urb_ctrl_buf, pcu->max_ctrl_size,
+ ims_pcu_irq, pcu, pcu->ep_ctrl->bInterval);
+
+ return 0;
+
+err_free_urb_ctrl_buf:
+ usb_free_coherent(pcu->udev, pcu->max_ctrl_size,
+ pcu->urb_ctrl_buf, pcu->ctrl_dma);
+err_free_urb_out_buf:
+ kfree(pcu->urb_out_buf);
+err_free_in_urb:
+ usb_free_urb(pcu->urb_in);
+err_free_urb_in_buf:
+ usb_free_coherent(pcu->udev, pcu->max_in_size,
+ pcu->urb_in_buf, pcu->read_dma);
+ return error;
+}
+
+static void ims_pcu_buffers_free(struct ims_pcu *pcu)
+{
+ usb_kill_urb(pcu->urb_in);
+ usb_free_urb(pcu->urb_in);
+
+ usb_free_coherent(pcu->udev, pcu->max_out_size,
+ pcu->urb_in_buf, pcu->read_dma);
+
+ kfree(pcu->urb_out_buf);
+
+ usb_kill_urb(pcu->urb_ctrl);
+ usb_free_urb(pcu->urb_ctrl);
+
+ usb_free_coherent(pcu->udev, pcu->max_ctrl_size,
+ pcu->urb_ctrl_buf, pcu->ctrl_dma);
+}
+
+static const struct usb_cdc_union_desc *
+ims_pcu_get_cdc_union_desc(struct usb_interface *intf)
+{
+ const void *buf = intf->altsetting->extra;
+ size_t buflen = intf->altsetting->extralen;
+ struct usb_cdc_union_desc *union_desc;
+
+ if (!buf) {
+ dev_err(&intf->dev, "Missing descriptor data\n");
+ return NULL;
+ }
+
+ if (!buflen) {
+ dev_err(&intf->dev, "Zero length descriptor\n");
+ return NULL;
+ }
+
+ while (buflen > 0) {
+ union_desc = (struct usb_cdc_union_desc *)buf;
+
+ if (union_desc->bDescriptorType == USB_DT_CS_INTERFACE &&
+ union_desc->bDescriptorSubType == USB_CDC_UNION_TYPE) {
+ dev_dbg(&intf->dev, "Found union header\n");
+ return union_desc;
+ }
+
+ buflen -= union_desc->bLength;
+ buf += union_desc->bLength;
+ }
+
+ dev_err(&intf->dev, "Missing CDC union descriptor\n");
+ return NULL;
+}
+
+static int ims_pcu_parse_cdc_data(struct usb_interface *intf, struct ims_pcu *pcu)
+{
+ const struct usb_cdc_union_desc *union_desc;
+ struct usb_host_interface *alt;
+
+ union_desc = ims_pcu_get_cdc_union_desc(intf);
+ if (!union_desc)
+ return -EINVAL;
+
+ pcu->ctrl_intf = usb_ifnum_to_if(pcu->udev,
+ union_desc->bMasterInterface0);
+
+ alt = pcu->ctrl_intf->cur_altsetting;
+ pcu->ep_ctrl = &alt->endpoint[0].desc;
+ pcu->max_ctrl_size = usb_endpoint_maxp(pcu->ep_ctrl);
+
+ pcu->data_intf = usb_ifnum_to_if(pcu->udev,
+ union_desc->bSlaveInterface0);
+
+ alt = pcu->data_intf->cur_altsetting;
+ if (alt->desc.bNumEndpoints != 2) {
+ dev_err(pcu->dev,
+ "Incorrect number of endpoints on data interface (%d)\n",
+ alt->desc.bNumEndpoints);
+ return -EINVAL;
+ }
+
+ pcu->ep_out = &alt->endpoint[0].desc;
+ if (!usb_endpoint_is_bulk_out(pcu->ep_out)) {
+ dev_err(pcu->dev,
+ "First endpoint on data interface is not BULK OUT\n");
+ return -EINVAL;
+ }
+
+ pcu->max_out_size = usb_endpoint_maxp(pcu->ep_out);
+ if (pcu->max_out_size < 8) {
+ dev_err(pcu->dev,
+ "Max OUT packet size is too small (%zd)\n",
+ pcu->max_out_size);
+ return -EINVAL;
+ }
+
+ pcu->ep_in = &alt->endpoint[1].desc;
+ if (!usb_endpoint_is_bulk_in(pcu->ep_in)) {
+ dev_err(pcu->dev,
+ "Second endpoint on data interface is not BULK IN\n");
+ return -EINVAL;
+ }
+
+ pcu->max_in_size = usb_endpoint_maxp(pcu->ep_in);
+ if (pcu->max_in_size < 8) {
+ dev_err(pcu->dev,
+ "Max IN packet size is too small (%zd)\n",
+ pcu->max_in_size);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ims_pcu_start_io(struct ims_pcu *pcu)
+{
+ int error;
+
+ error = usb_submit_urb(pcu->urb_ctrl, GFP_KERNEL);
+ if (error) {
+ dev_err(pcu->dev,
+ "Failed to start control IO - usb_submit_urb failed with result: %d\n",
+ error);
+ return -EIO;
+ }
+
+ error = usb_submit_urb(pcu->urb_in, GFP_KERNEL);
+ if (error) {
+ dev_err(pcu->dev,
+ "Failed to start IO - usb_submit_urb failed with result: %d\n",
+ error);
+ usb_kill_urb(pcu->urb_ctrl);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void ims_pcu_stop_io(struct ims_pcu *pcu)
+{
+ usb_kill_urb(pcu->urb_in);
+ usb_kill_urb(pcu->urb_ctrl);
+}
+
+static int ims_pcu_line_setup(struct ims_pcu *pcu)
+{
+ struct usb_host_interface *interface = pcu->ctrl_intf->cur_altsetting;
+ struct usb_cdc_line_coding *line = (void *)pcu->cmd_buf;
+ int error;
+
+ memset(line, 0, sizeof(*line));
+ line->dwDTERate = cpu_to_le32(57600);
+ line->bDataBits = 8;
+
+ error = usb_control_msg(pcu->udev, usb_sndctrlpipe(pcu->udev, 0),
+ USB_CDC_REQ_SET_LINE_CODING,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, interface->desc.bInterfaceNumber,
+ line, sizeof(struct usb_cdc_line_coding),
+ 5000);
+ if (error < 0) {
+ dev_err(pcu->dev, "Failed to set line coding, error: %d\n",
+ error);
+ return error;
+ }
+
+ error = usb_control_msg(pcu->udev, usb_sndctrlpipe(pcu->udev, 0),
+ USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0x03, interface->desc.bInterfaceNumber,
+ NULL, 0, 5000);
+ if (error < 0) {
+ dev_err(pcu->dev, "Failed to set line state, error: %d\n",
+ error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int ims_pcu_get_device_info(struct ims_pcu *pcu)
+{
+ int error;
+
+ error = ims_pcu_get_info(pcu);
+ if (error)
+ return error;
+
+ error = ims_pcu_execute_query(pcu, GET_FW_VERSION);
+ if (error) {
+ dev_err(pcu->dev,
+ "GET_FW_VERSION command failed, error: %d\n", error);
+ return error;
+ }
+
+ snprintf(pcu->fw_version, sizeof(pcu->fw_version),
+ "%02d%02d%02d%02d.%c%c",
+ pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5],
+ pcu->cmd_buf[6], pcu->cmd_buf[7]);
+
+ error = ims_pcu_execute_query(pcu, GET_BL_VERSION);
+ if (error) {
+ dev_err(pcu->dev,
+ "GET_BL_VERSION command failed, error: %d\n", error);
+ return error;
+ }
+
+ snprintf(pcu->bl_version, sizeof(pcu->bl_version),
+ "%02d%02d%02d%02d.%c%c",
+ pcu->cmd_buf[2], pcu->cmd_buf[3], pcu->cmd_buf[4], pcu->cmd_buf[5],
+ pcu->cmd_buf[6], pcu->cmd_buf[7]);
+
+ error = ims_pcu_execute_query(pcu, RESET_REASON);
+ if (error) {
+ dev_err(pcu->dev,
+ "RESET_REASON command failed, error: %d\n", error);
+ return error;
+ }
+
+ snprintf(pcu->reset_reason, sizeof(pcu->reset_reason),
+ "%02x", pcu->cmd_buf[IMS_PCU_DATA_OFFSET]);
+
+ dev_dbg(pcu->dev,
+ "P/N: %s, MD: %s, S/N: %s, FW: %s, BL: %s, RR: %s\n",
+ pcu->part_number,
+ pcu->date_of_manufacturing,
+ pcu->serial_number,
+ pcu->fw_version,
+ pcu->bl_version,
+ pcu->reset_reason);
+
+ return 0;
+}
+
+static int ims_pcu_identify_type(struct ims_pcu *pcu, u8 *device_id)
+{
+ int error;
+
+ error = ims_pcu_execute_query(pcu, GET_DEVICE_ID);
+ if (error) {
+ dev_err(pcu->dev,
+ "GET_DEVICE_ID command failed, error: %d\n", error);
+ return error;
+ }
+
+ *device_id = pcu->cmd_buf[IMS_PCU_DATA_OFFSET];
+ dev_dbg(pcu->dev, "Detected device ID: %d\n", *device_id);
+
+ return 0;
+}
+
+static int ims_pcu_init_application_mode(struct ims_pcu *pcu)
+{
+ static atomic_t device_no = ATOMIC_INIT(0);
+
+ const struct ims_pcu_device_info *info;
+ u8 device_id;
+ int error;
+
+ error = ims_pcu_get_device_info(pcu);
+ if (error) {
+ /* Device does not respond to basic queries, hopeless */
+ return error;
+ }
+
+ error = ims_pcu_identify_type(pcu, &device_id);
+ if (error) {
+ dev_err(pcu->dev,
+ "Failed to identify device, error: %d\n", error);
+ /*
+ * Do not signal error, but do not create input nor
+ * backlight devices either, let userspace figure this
+ * out (flash a new firmware?).
+ */
+ return 0;
+ }
+
+ if (device_id >= ARRAY_SIZE(ims_pcu_device_info) ||
+ !ims_pcu_device_info[device_id].keymap) {
+ dev_err(pcu->dev, "Device ID %d is not valid\n", device_id);
+ /* Same as above, punt to userspace */
+ return 0;
+ }
+
+ /* Device appears to be operable, complete initialization */
+ pcu->device_no = atomic_inc_return(&device_no) - 1;
+
+ error = ims_pcu_setup_backlight(pcu);
+ if (error)
+ return error;
+
+ info = &ims_pcu_device_info[device_id];
+ error = ims_pcu_setup_buttons(pcu, info->keymap, info->keymap_len);
+ if (error)
+ goto err_destroy_backlight;
+
+ if (info->has_gamepad) {
+ error = ims_pcu_setup_gamepad(pcu);
+ if (error)
+ goto err_destroy_buttons;
+ }
+
+ pcu->setup_complete = true;
+
+ return 0;
+
+err_destroy_backlight:
+ ims_pcu_destroy_backlight(pcu);
+err_destroy_buttons:
+ ims_pcu_destroy_buttons(pcu);
+ return error;
+}
+
+static void ims_pcu_destroy_application_mode(struct ims_pcu *pcu)
+{
+ if (pcu->setup_complete) {
+ pcu->setup_complete = false;
+ mb(); /* make sure flag setting is not reordered */
+
+ if (pcu->gamepad)
+ ims_pcu_destroy_gamepad(pcu);
+ ims_pcu_destroy_buttons(pcu);
+ ims_pcu_destroy_backlight(pcu);
+ }
+}
+
+static int ims_pcu_init_bootloader_mode(struct ims_pcu *pcu)
+{
+ int error;
+
+ error = ims_pcu_execute_bl_command(pcu, QUERY_DEVICE, NULL, 0,
+ IMS_PCU_CMD_RESPONSE_TIMEOUT);
+ if (error) {
+ dev_err(pcu->dev, "Bootloader does not respond, aborting\n");
+ return error;
+ }
+
+ pcu->fw_start_addr =
+ get_unaligned_le32(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 11]);
+ pcu->fw_end_addr =
+ get_unaligned_le32(&pcu->cmd_buf[IMS_PCU_DATA_OFFSET + 15]);
+
+ dev_info(pcu->dev,
+ "Device is in bootloader mode (addr 0x%08x-0x%08x), requesting firmware\n",
+ pcu->fw_start_addr, pcu->fw_end_addr);
+
+ error = request_firmware_nowait(THIS_MODULE, true,
+ IMS_PCU_FIRMWARE_NAME,
+ pcu->dev, GFP_KERNEL, pcu,
+ ims_pcu_process_async_firmware);
+ if (error) {
+ /* This error is not fatal, let userspace have another chance */
+ complete(&pcu->async_firmware_done);
+ }
+
+ return 0;
+}
+
+static void ims_pcu_destroy_bootloader_mode(struct ims_pcu *pcu)
+{
+ /* Make sure our initial firmware request has completed */
+ wait_for_completion(&pcu->async_firmware_done);
+}
+
+#define IMS_PCU_APPLICATION_MODE 0
+#define IMS_PCU_BOOTLOADER_MODE 1
+
+static struct usb_driver ims_pcu_driver;
+
+static int ims_pcu_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ struct ims_pcu *pcu;
+ int error;
+
+ pcu = kzalloc(sizeof(struct ims_pcu), GFP_KERNEL);
+ if (!pcu)
+ return -ENOMEM;
+
+ pcu->dev = &intf->dev;
+ pcu->udev = udev;
+ pcu->bootloader_mode = id->driver_info == IMS_PCU_BOOTLOADER_MODE;
+ mutex_init(&pcu->cmd_mutex);
+ init_completion(&pcu->cmd_done);
+ init_completion(&pcu->async_firmware_done);
+
+ error = ims_pcu_parse_cdc_data(intf, pcu);
+ if (error)
+ goto err_free_mem;
+
+ error = usb_driver_claim_interface(&ims_pcu_driver,
+ pcu->data_intf, pcu);
+ if (error) {
+ dev_err(&intf->dev,
+ "Unable to claim corresponding data interface: %d\n",
+ error);
+ goto err_free_mem;
+ }
+
+ usb_set_intfdata(pcu->ctrl_intf, pcu);
+ usb_set_intfdata(pcu->data_intf, pcu);
+
+ error = ims_pcu_buffers_alloc(pcu);
+ if (error)
+ goto err_unclaim_intf;
+
+ error = ims_pcu_start_io(pcu);
+ if (error)
+ goto err_free_buffers;
+
+ error = ims_pcu_line_setup(pcu);
+ if (error)
+ goto err_stop_io;
+
+ error = sysfs_create_group(&intf->dev.kobj, &ims_pcu_attr_group);
+ if (error)
+ goto err_stop_io;
+
+ error = pcu->bootloader_mode ?
+ ims_pcu_init_bootloader_mode(pcu) :
+ ims_pcu_init_application_mode(pcu);
+ if (error)
+ goto err_remove_sysfs;
+
+ return 0;
+
+err_remove_sysfs:
+ sysfs_remove_group(&intf->dev.kobj, &ims_pcu_attr_group);
+err_stop_io:
+ ims_pcu_stop_io(pcu);
+err_free_buffers:
+ ims_pcu_buffers_free(pcu);
+err_unclaim_intf:
+ usb_driver_release_interface(&ims_pcu_driver, pcu->data_intf);
+err_free_mem:
+ kfree(pcu);
+ return error;
+}
+
+static void ims_pcu_disconnect(struct usb_interface *intf)
+{
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ struct usb_host_interface *alt = intf->cur_altsetting;
+
+ usb_set_intfdata(intf, NULL);
+
+ /*
+ * See if we are dealing with control or data interface. The cleanup
+ * happens when we unbind primary (control) interface.
+ */
+ if (alt->desc.bInterfaceClass != USB_CLASS_COMM)
+ return;
+
+ sysfs_remove_group(&intf->dev.kobj, &ims_pcu_attr_group);
+
+ ims_pcu_stop_io(pcu);
+
+ if (pcu->bootloader_mode)
+ ims_pcu_destroy_bootloader_mode(pcu);
+ else
+ ims_pcu_destroy_application_mode(pcu);
+
+ ims_pcu_buffers_free(pcu);
+ kfree(pcu);
+}
+
+#ifdef CONFIG_PM
+static int ims_pcu_suspend(struct usb_interface *intf,
+ pm_message_t message)
+{
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ struct usb_host_interface *alt = intf->cur_altsetting;
+
+ if (alt->desc.bInterfaceClass == USB_CLASS_COMM)
+ ims_pcu_stop_io(pcu);
+
+ return 0;
+}
+
+static int ims_pcu_resume(struct usb_interface *intf)
+{
+ struct ims_pcu *pcu = usb_get_intfdata(intf);
+ struct usb_host_interface *alt = intf->cur_altsetting;
+ int retval = 0;
+
+ if (alt->desc.bInterfaceClass == USB_CLASS_COMM) {
+ retval = ims_pcu_start_io(pcu);
+ if (retval == 0)
+ retval = ims_pcu_line_setup(pcu);
+ }
+
+ return retval;
+}
+#endif
+
+static const struct usb_device_id ims_pcu_id_table[] = {
+ {
+ USB_DEVICE_AND_INTERFACE_INFO(0x04d8, 0x0082,
+ USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ACM,
+ USB_CDC_ACM_PROTO_AT_V25TER),
+ .driver_info = IMS_PCU_APPLICATION_MODE,
+ },
+ {
+ USB_DEVICE_AND_INTERFACE_INFO(0x04d8, 0x0083,
+ USB_CLASS_COMM,
+ USB_CDC_SUBCLASS_ACM,
+ USB_CDC_ACM_PROTO_AT_V25TER),
+ .driver_info = IMS_PCU_BOOTLOADER_MODE,
+ },
+ { }
+};
+
+static struct usb_driver ims_pcu_driver = {
+ .name = "ims_pcu",
+ .id_table = ims_pcu_id_table,
+ .probe = ims_pcu_probe,
+ .disconnect = ims_pcu_disconnect,
+#ifdef CONFIG_PM
+ .suspend = ims_pcu_suspend,
+ .resume = ims_pcu_resume,
+ .reset_resume = ims_pcu_resume,
+#endif
+};
+
+module_usb_driver(ims_pcu_driver);
+
+MODULE_DESCRIPTION("IMS Passenger Control Unit driver");
+MODULE_AUTHOR("Dmitry Torokhov <dmitry.torokhov@gmail.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ixp4xx-beeper.c b/drivers/input/misc/ixp4xx-beeper.c
new file mode 100644
index 00000000..6ab3decc
--- /dev/null
+++ b/drivers/input/misc/ixp4xx-beeper.c
@@ -0,0 +1,172 @@
+/*
+ * Generic IXP4xx beeper driver
+ *
+ * Copyright (C) 2005 Tower Technologies
+ *
+ * based on nslu2-io.c
+ * Copyright (C) 2004 Karen Spearel
+ *
+ * Author: Alessandro Zummo <a.zummo@towertech.it>
+ * Maintainers: http://www.nslu2-linux.org/
+ *
+ * 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/input.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <mach/hardware.h>
+
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
+MODULE_DESCRIPTION("ixp4xx beeper driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ixp4xx-beeper");
+
+static DEFINE_SPINLOCK(beep_lock);
+
+static void ixp4xx_spkr_control(unsigned int pin, unsigned int count)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&beep_lock, flags);
+
+ if (count) {
+ gpio_line_config(pin, IXP4XX_GPIO_OUT);
+ gpio_line_set(pin, IXP4XX_GPIO_LOW);
+
+ *IXP4XX_OSRT2 = (count & ~IXP4XX_OST_RELOAD_MASK) | IXP4XX_OST_ENABLE;
+ } else {
+ gpio_line_config(pin, IXP4XX_GPIO_IN);
+ gpio_line_set(pin, IXP4XX_GPIO_HIGH);
+
+ *IXP4XX_OSRT2 = 0;
+ }
+
+ spin_unlock_irqrestore(&beep_lock, flags);
+}
+
+static int ixp4xx_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ unsigned int pin = (unsigned int) input_get_drvdata(dev);
+ unsigned int count = 0;
+
+ if (type != EV_SND)
+ return -1;
+
+ switch (code) {
+ case SND_BELL:
+ if (value)
+ value = 1000;
+ case SND_TONE:
+ break;
+ default:
+ return -1;
+ }
+
+ if (value > 20 && value < 32767)
+ count = (IXP4XX_TIMER_FREQ / (value * 4)) - 1;
+
+ ixp4xx_spkr_control(pin, count);
+
+ return 0;
+}
+
+static irqreturn_t ixp4xx_spkr_interrupt(int irq, void *dev_id)
+{
+ /* clear interrupt */
+ *IXP4XX_OSST = IXP4XX_OSST_TIMER_2_PEND;
+
+ /* flip the beeper output */
+ *IXP4XX_GPIO_GPOUTR ^= (1 << (unsigned int) dev_id);
+
+ return IRQ_HANDLED;
+}
+
+static int ixp4xx_spkr_probe(struct platform_device *dev)
+{
+ struct input_dev *input_dev;
+ int err;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_set_drvdata(input_dev, (void *) dev->id);
+
+ input_dev->name = "ixp4xx beeper",
+ input_dev->phys = "ixp4xx/gpio";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x001f;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &dev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_SND);
+ input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+ input_dev->event = ixp4xx_spkr_event;
+
+ err = request_irq(IRQ_IXP4XX_TIMER2, &ixp4xx_spkr_interrupt,
+ IRQF_NO_SUSPEND, "ixp4xx-beeper",
+ (void *) dev->id);
+ if (err)
+ goto err_free_device;
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_free_irq;
+
+ platform_set_drvdata(dev, input_dev);
+
+ return 0;
+
+ err_free_irq:
+ free_irq(IRQ_IXP4XX_TIMER2, dev);
+ err_free_device:
+ input_free_device(input_dev);
+
+ return err;
+}
+
+static int ixp4xx_spkr_remove(struct platform_device *dev)
+{
+ struct input_dev *input_dev = platform_get_drvdata(dev);
+ unsigned int pin = (unsigned int) input_get_drvdata(input_dev);
+
+ input_unregister_device(input_dev);
+ platform_set_drvdata(dev, NULL);
+
+ /* turn the speaker off */
+ disable_irq(IRQ_IXP4XX_TIMER2);
+ ixp4xx_spkr_control(pin, 0);
+
+ free_irq(IRQ_IXP4XX_TIMER2, dev);
+
+ return 0;
+}
+
+static void ixp4xx_spkr_shutdown(struct platform_device *dev)
+{
+ struct input_dev *input_dev = platform_get_drvdata(dev);
+ unsigned int pin = (unsigned int) input_get_drvdata(input_dev);
+
+ /* turn off the speaker */
+ disable_irq(IRQ_IXP4XX_TIMER2);
+ ixp4xx_spkr_control(pin, 0);
+}
+
+static struct platform_driver ixp4xx_spkr_platform_driver = {
+ .driver = {
+ .name = "ixp4xx-beeper",
+ .owner = THIS_MODULE,
+ },
+ .probe = ixp4xx_spkr_probe,
+ .remove = ixp4xx_spkr_remove,
+ .shutdown = ixp4xx_spkr_shutdown,
+};
+module_platform_driver(ixp4xx_spkr_platform_driver);
+
diff --git a/drivers/input/misc/keychord.c b/drivers/input/misc/keychord.c
new file mode 100644
index 00000000..a5ea27ad
--- /dev/null
+++ b/drivers/input/misc/keychord.c
@@ -0,0 +1,391 @@
+/*
+ * drivers/input/misc/keychord.c
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+*/
+
+#include <linux/poll.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/keychord.h>
+#include <linux/sched.h>
+
+#define KEYCHORD_NAME "keychord"
+#define BUFFER_SIZE 16
+
+MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
+MODULE_DESCRIPTION("Key chord input driver");
+MODULE_SUPPORTED_DEVICE("keychord");
+MODULE_LICENSE("GPL");
+
+#define NEXT_KEYCHORD(kc) ((struct input_keychord *) \
+ ((char *)kc + sizeof(struct input_keychord) + \
+ kc->count * sizeof(kc->keycodes[0])))
+
+struct keychord_device {
+ struct input_handler input_handler;
+ int registered;
+
+ /* list of keychords to monitor */
+ struct input_keychord *keychords;
+ int keychord_count;
+
+ /* bitmask of keys contained in our keychords */
+ unsigned long keybit[BITS_TO_LONGS(KEY_CNT)];
+ /* current state of the keys */
+ unsigned long keystate[BITS_TO_LONGS(KEY_CNT)];
+ /* number of keys that are currently pressed */
+ int key_down;
+
+ /* second input_device_id is needed for null termination */
+ struct input_device_id device_ids[2];
+
+ spinlock_t lock;
+ wait_queue_head_t waitq;
+ unsigned char head;
+ unsigned char tail;
+ __u16 buff[BUFFER_SIZE];
+};
+
+static int check_keychord(struct keychord_device *kdev,
+ struct input_keychord *keychord)
+{
+ int i;
+
+ if (keychord->count != kdev->key_down)
+ return 0;
+
+ for (i = 0; i < keychord->count; i++) {
+ if (!test_bit(keychord->keycodes[i], kdev->keystate))
+ return 0;
+ }
+
+ /* we have a match */
+ return 1;
+}
+
+static void keychord_event(struct input_handle *handle, unsigned int type,
+ unsigned int code, int value)
+{
+ struct keychord_device *kdev = handle->private;
+ struct input_keychord *keychord;
+ unsigned long flags;
+ int i, got_chord = 0;
+
+ if (type != EV_KEY || code >= KEY_MAX)
+ return;
+
+ spin_lock_irqsave(&kdev->lock, flags);
+ /* do nothing if key state did not change */
+ if (!test_bit(code, kdev->keystate) == !value)
+ goto done;
+ __change_bit(code, kdev->keystate);
+ if (value)
+ kdev->key_down++;
+ else
+ kdev->key_down--;
+
+ /* don't notify on key up */
+ if (!value)
+ goto done;
+ /* ignore this event if it is not one of the keys we are monitoring */
+ if (!test_bit(code, kdev->keybit))
+ goto done;
+
+ keychord = kdev->keychords;
+ if (!keychord)
+ goto done;
+
+ /* check to see if the keyboard state matches any keychords */
+ for (i = 0; i < kdev->keychord_count; i++) {
+ if (check_keychord(kdev, keychord)) {
+ kdev->buff[kdev->head] = keychord->id;
+ kdev->head = (kdev->head + 1) % BUFFER_SIZE;
+ got_chord = 1;
+ break;
+ }
+ /* skip to next keychord */
+ keychord = NEXT_KEYCHORD(keychord);
+ }
+
+done:
+ spin_unlock_irqrestore(&kdev->lock, flags);
+
+ if (got_chord) {
+ pr_info("keychord: got keychord id %d. Any tasks: %d\n",
+ keychord->id,
+ !list_empty_careful(&kdev->waitq.task_list));
+ wake_up_interruptible(&kdev->waitq);
+ }
+}
+
+static int keychord_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ int i, ret;
+ struct input_handle *handle;
+ struct keychord_device *kdev =
+ container_of(handler, struct keychord_device, input_handler);
+
+ /*
+ * ignore this input device if it does not contain any keycodes
+ * that we are monitoring
+ */
+ for (i = 0; i < KEY_MAX; i++) {
+ if (test_bit(i, kdev->keybit) && test_bit(i, dev->keybit))
+ break;
+ }
+ if (i == KEY_MAX)
+ return -ENODEV;
+
+ handle = kzalloc(sizeof(*handle), GFP_KERNEL);
+ if (!handle)
+ return -ENOMEM;
+
+ handle->dev = dev;
+ handle->handler = handler;
+ handle->name = KEYCHORD_NAME;
+ handle->private = kdev;
+
+ ret = input_register_handle(handle);
+ if (ret)
+ goto err_input_register_handle;
+
+ ret = input_open_device(handle);
+ if (ret)
+ goto err_input_open_device;
+
+ pr_info("keychord: using input dev %s for fevent\n", dev->name);
+
+ return 0;
+
+err_input_open_device:
+ input_unregister_handle(handle);
+err_input_register_handle:
+ kfree(handle);
+ return ret;
+}
+
+static void keychord_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+ kfree(handle);
+}
+
+/*
+ * keychord_read is used to read keychord events from the driver
+ */
+static ssize_t keychord_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct keychord_device *kdev = file->private_data;
+ __u16 id;
+ int retval;
+ unsigned long flags;
+
+ if (count < sizeof(id))
+ return -EINVAL;
+ count = sizeof(id);
+
+ if (kdev->head == kdev->tail && (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
+
+ retval = wait_event_interruptible(kdev->waitq,
+ kdev->head != kdev->tail);
+ if (retval)
+ return retval;
+
+ spin_lock_irqsave(&kdev->lock, flags);
+ /* pop a keychord ID off the queue */
+ id = kdev->buff[kdev->tail];
+ kdev->tail = (kdev->tail + 1) % BUFFER_SIZE;
+ spin_unlock_irqrestore(&kdev->lock, flags);
+
+ if (copy_to_user(buffer, &id, count))
+ return -EFAULT;
+
+ return count;
+}
+
+/*
+ * keychord_write is used to configure the driver
+ */
+static ssize_t keychord_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct keychord_device *kdev = file->private_data;
+ struct input_keychord *keychords = 0;
+ struct input_keychord *keychord, *next, *end;
+ int ret, i, key;
+ unsigned long flags;
+
+ if (count < sizeof(struct input_keychord))
+ return -EINVAL;
+ keychords = kzalloc(count, GFP_KERNEL);
+ if (!keychords)
+ return -ENOMEM;
+
+ /* read list of keychords from userspace */
+ if (copy_from_user(keychords, buffer, count)) {
+ kfree(keychords);
+ return -EFAULT;
+ }
+
+ /* unregister handler before changing configuration */
+ if (kdev->registered) {
+ input_unregister_handler(&kdev->input_handler);
+ kdev->registered = 0;
+ }
+
+ spin_lock_irqsave(&kdev->lock, flags);
+ /* clear any existing configuration */
+ kfree(kdev->keychords);
+ kdev->keychords = 0;
+ kdev->keychord_count = 0;
+ kdev->key_down = 0;
+ memset(kdev->keybit, 0, sizeof(kdev->keybit));
+ memset(kdev->keystate, 0, sizeof(kdev->keystate));
+ kdev->head = kdev->tail = 0;
+
+ keychord = keychords;
+ end = (struct input_keychord *)((char *)keychord + count);
+
+ while (keychord < end) {
+ next = NEXT_KEYCHORD(keychord);
+ if (keychord->count <= 0 || next > end) {
+ pr_err("keychord: invalid keycode count %d\n",
+ keychord->count);
+ goto err_unlock_return;
+ }
+ if (keychord->version != KEYCHORD_VERSION) {
+ pr_err("keychord: unsupported version %d\n",
+ keychord->version);
+ goto err_unlock_return;
+ }
+
+ /* keep track of the keys we are monitoring in keybit */
+ for (i = 0; i < keychord->count; i++) {
+ key = keychord->keycodes[i];
+ if (key < 0 || key >= KEY_CNT) {
+ pr_err("keychord: keycode %d out of range\n",
+ key);
+ goto err_unlock_return;
+ }
+ __set_bit(key, kdev->keybit);
+ }
+
+ kdev->keychord_count++;
+ keychord = next;
+ }
+
+ kdev->keychords = keychords;
+ spin_unlock_irqrestore(&kdev->lock, flags);
+
+ ret = input_register_handler(&kdev->input_handler);
+ if (ret) {
+ kfree(keychords);
+ kdev->keychords = 0;
+ return ret;
+ }
+ kdev->registered = 1;
+
+ return count;
+
+err_unlock_return:
+ spin_unlock_irqrestore(&kdev->lock, flags);
+ kfree(keychords);
+ return -EINVAL;
+}
+
+static unsigned int keychord_poll(struct file *file, poll_table *wait)
+{
+ struct keychord_device *kdev = file->private_data;
+
+ poll_wait(file, &kdev->waitq, wait);
+
+ if (kdev->head != kdev->tail)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static int keychord_open(struct inode *inode, struct file *file)
+{
+ struct keychord_device *kdev;
+
+ kdev = kzalloc(sizeof(struct keychord_device), GFP_KERNEL);
+ if (!kdev)
+ return -ENOMEM;
+
+ spin_lock_init(&kdev->lock);
+ init_waitqueue_head(&kdev->waitq);
+
+ kdev->input_handler.event = keychord_event;
+ kdev->input_handler.connect = keychord_connect;
+ kdev->input_handler.disconnect = keychord_disconnect;
+ kdev->input_handler.name = KEYCHORD_NAME;
+ kdev->input_handler.id_table = kdev->device_ids;
+
+ kdev->device_ids[0].flags = INPUT_DEVICE_ID_MATCH_EVBIT;
+ __set_bit(EV_KEY, kdev->device_ids[0].evbit);
+
+ file->private_data = kdev;
+
+ return 0;
+}
+
+static int keychord_release(struct inode *inode, struct file *file)
+{
+ struct keychord_device *kdev = file->private_data;
+
+ if (kdev->registered)
+ input_unregister_handler(&kdev->input_handler);
+ kfree(kdev);
+
+ return 0;
+}
+
+static const struct file_operations keychord_fops = {
+ .owner = THIS_MODULE,
+ .open = keychord_open,
+ .release = keychord_release,
+ .read = keychord_read,
+ .write = keychord_write,
+ .poll = keychord_poll,
+};
+
+static struct miscdevice keychord_misc = {
+ .fops = &keychord_fops,
+ .name = KEYCHORD_NAME,
+ .minor = MISC_DYNAMIC_MINOR,
+};
+
+static int __init keychord_init(void)
+{
+ return misc_register(&keychord_misc);
+}
+
+static void __exit keychord_exit(void)
+{
+ misc_deregister(&keychord_misc);
+}
+
+module_init(keychord_init);
+module_exit(keychord_exit);
diff --git a/drivers/input/misc/keyspan_remote.c b/drivers/input/misc/keyspan_remote.c
new file mode 100644
index 00000000..290fa5f9
--- /dev/null
+++ b/drivers/input/misc/keyspan_remote.c
@@ -0,0 +1,597 @@
+/*
+ * keyspan_remote: USB driver for the Keyspan DMR
+ *
+ * Copyright (C) 2005 Zymeta Corporation - Michael Downey (downey@zymeta.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2.
+ *
+ * This driver has been put together with the support of Innosys, Inc.
+ * and Keyspan, Inc the manufacturers of the Keyspan USB DMR product.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb/input.h>
+
+#define DRIVER_VERSION "v0.1"
+#define DRIVER_AUTHOR "Michael Downey <downey@zymeta.com>"
+#define DRIVER_DESC "Driver for the USB Keyspan remote control."
+#define DRIVER_LICENSE "GPL"
+
+/* Parameters that can be passed to the driver. */
+static int debug;
+module_param(debug, int, 0444);
+MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
+
+/* Vendor and product ids */
+#define USB_KEYSPAN_VENDOR_ID 0x06CD
+#define USB_KEYSPAN_PRODUCT_UIA11 0x0202
+
+/* Defines for converting the data from the remote. */
+#define ZERO 0x18
+#define ZERO_MASK 0x1F /* 5 bits for a 0 */
+#define ONE 0x3C
+#define ONE_MASK 0x3F /* 6 bits for a 1 */
+#define SYNC 0x3F80
+#define SYNC_MASK 0x3FFF /* 14 bits for a SYNC sequence */
+#define STOP 0x00
+#define STOP_MASK 0x1F /* 5 bits for the STOP sequence */
+#define GAP 0xFF
+
+#define RECV_SIZE 8 /* The UIA-11 type have a 8 byte limit. */
+
+/*
+ * Table that maps the 31 possible keycodes to input keys.
+ * Currently there are 15 and 17 button models so RESERVED codes
+ * are blank areas in the mapping.
+ */
+static const unsigned short keyspan_key_table[] = {
+ KEY_RESERVED, /* 0 is just a place holder. */
+ KEY_RESERVED,
+ KEY_STOP,
+ KEY_PLAYCD,
+ KEY_RESERVED,
+ KEY_PREVIOUSSONG,
+ KEY_REWIND,
+ KEY_FORWARD,
+ KEY_NEXTSONG,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_PAUSE,
+ KEY_VOLUMEUP,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_VOLUMEDOWN,
+ KEY_RESERVED,
+ KEY_UP,
+ KEY_RESERVED,
+ KEY_MUTE,
+ KEY_LEFT,
+ KEY_ENTER,
+ KEY_RIGHT,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_DOWN,
+ KEY_RESERVED,
+ KEY_KPASTERISK,
+ KEY_RESERVED,
+ KEY_MENU
+};
+
+/* table of devices that work with this driver */
+static struct usb_device_id keyspan_table[] = {
+ { USB_DEVICE(USB_KEYSPAN_VENDOR_ID, USB_KEYSPAN_PRODUCT_UIA11) },
+ { } /* Terminating entry */
+};
+
+/* Structure to store all the real stuff that a remote sends to us. */
+struct keyspan_message {
+ u16 system;
+ u8 button;
+ u8 toggle;
+};
+
+/* Structure used for all the bit testing magic needed to be done. */
+struct bit_tester {
+ u32 tester;
+ int len;
+ int pos;
+ int bits_left;
+ u8 buffer[32];
+};
+
+/* Structure to hold all of our driver specific stuff */
+struct usb_keyspan {
+ char name[128];
+ char phys[64];
+ unsigned short keymap[ARRAY_SIZE(keyspan_key_table)];
+ struct usb_device *udev;
+ struct input_dev *input;
+ struct usb_interface *interface;
+ struct usb_endpoint_descriptor *in_endpoint;
+ struct urb* irq_urb;
+ int open;
+ dma_addr_t in_dma;
+ unsigned char *in_buffer;
+
+ /* variables used to parse messages from remote. */
+ struct bit_tester data;
+ int stage;
+ int toggle;
+};
+
+static struct usb_driver keyspan_driver;
+
+/*
+ * Debug routine that prints out what we've received from the remote.
+ */
+static void keyspan_print(struct usb_keyspan* dev) /*unsigned char* data)*/
+{
+ char codes[4 * RECV_SIZE];
+ int i;
+
+ for (i = 0; i < RECV_SIZE; i++)
+ snprintf(codes + i * 3, 4, "%02x ", dev->in_buffer[i]);
+
+ dev_info(&dev->udev->dev, "%s\n", codes);
+}
+
+/*
+ * Routine that manages the bit_tester structure. It makes sure that there are
+ * at least bits_needed bits loaded into the tester.
+ */
+static int keyspan_load_tester(struct usb_keyspan* dev, int bits_needed)
+{
+ if (dev->data.bits_left >= bits_needed)
+ return 0;
+
+ /*
+ * Somehow we've missed the last message. The message will be repeated
+ * though so it's not too big a deal
+ */
+ if (dev->data.pos >= dev->data.len) {
+ dev_dbg(&dev->interface->dev,
+ "%s - Error ran out of data. pos: %d, len: %d\n",
+ __func__, dev->data.pos, dev->data.len);
+ return -1;
+ }
+
+ /* Load as much as we can into the tester. */
+ while ((dev->data.bits_left + 7 < (sizeof(dev->data.tester) * 8)) &&
+ (dev->data.pos < dev->data.len)) {
+ dev->data.tester += (dev->data.buffer[dev->data.pos++] << dev->data.bits_left);
+ dev->data.bits_left += 8;
+ }
+
+ return 0;
+}
+
+static void keyspan_report_button(struct usb_keyspan *remote, int button, int press)
+{
+ struct input_dev *input = remote->input;
+
+ input_event(input, EV_MSC, MSC_SCAN, button);
+ input_report_key(input, remote->keymap[button], press);
+ input_sync(input);
+}
+
+/*
+ * Routine that handles all the logic needed to parse out the message from the remote.
+ */
+static void keyspan_check_data(struct usb_keyspan *remote)
+{
+ int i;
+ int found = 0;
+ struct keyspan_message message;
+
+ switch(remote->stage) {
+ case 0:
+ /*
+ * In stage 0 we want to find the start of a message. The remote sends a 0xFF as filler.
+ * So the first byte that isn't a FF should be the start of a new message.
+ */
+ for (i = 0; i < RECV_SIZE && remote->in_buffer[i] == GAP; ++i);
+
+ if (i < RECV_SIZE) {
+ memcpy(remote->data.buffer, remote->in_buffer, RECV_SIZE);
+ remote->data.len = RECV_SIZE;
+ remote->data.pos = 0;
+ remote->data.tester = 0;
+ remote->data.bits_left = 0;
+ remote->stage = 1;
+ }
+ break;
+
+ case 1:
+ /*
+ * Stage 1 we should have 16 bytes and should be able to detect a
+ * SYNC. The SYNC is 14 bits, 7 0's and then 7 1's.
+ */
+ memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
+ remote->data.len += RECV_SIZE;
+
+ found = 0;
+ while ((remote->data.bits_left >= 14 || remote->data.pos < remote->data.len) && !found) {
+ for (i = 0; i < 8; ++i) {
+ if (keyspan_load_tester(remote, 14) != 0) {
+ remote->stage = 0;
+ return;
+ }
+
+ if ((remote->data.tester & SYNC_MASK) == SYNC) {
+ remote->data.tester = remote->data.tester >> 14;
+ remote->data.bits_left -= 14;
+ found = 1;
+ break;
+ } else {
+ remote->data.tester = remote->data.tester >> 1;
+ --remote->data.bits_left;
+ }
+ }
+ }
+
+ if (!found) {
+ remote->stage = 0;
+ remote->data.len = 0;
+ } else {
+ remote->stage = 2;
+ }
+ break;
+
+ case 2:
+ /*
+ * Stage 2 we should have 24 bytes which will be enough for a full
+ * message. We need to parse out the system code, button code,
+ * toggle code, and stop.
+ */
+ memcpy(remote->data.buffer + remote->data.len, remote->in_buffer, RECV_SIZE);
+ remote->data.len += RECV_SIZE;
+
+ message.system = 0;
+ for (i = 0; i < 9; i++) {
+ keyspan_load_tester(remote, 6);
+
+ if ((remote->data.tester & ZERO_MASK) == ZERO) {
+ message.system = message.system << 1;
+ remote->data.tester = remote->data.tester >> 5;
+ remote->data.bits_left -= 5;
+ } else if ((remote->data.tester & ONE_MASK) == ONE) {
+ message.system = (message.system << 1) + 1;
+ remote->data.tester = remote->data.tester >> 6;
+ remote->data.bits_left -= 6;
+ } else {
+ dev_err(&remote->interface->dev,
+ "%s - Unknown sequence found in system data.\n",
+ __func__);
+ remote->stage = 0;
+ return;
+ }
+ }
+
+ message.button = 0;
+ for (i = 0; i < 5; i++) {
+ keyspan_load_tester(remote, 6);
+
+ if ((remote->data.tester & ZERO_MASK) == ZERO) {
+ message.button = message.button << 1;
+ remote->data.tester = remote->data.tester >> 5;
+ remote->data.bits_left -= 5;
+ } else if ((remote->data.tester & ONE_MASK) == ONE) {
+ message.button = (message.button << 1) + 1;
+ remote->data.tester = remote->data.tester >> 6;
+ remote->data.bits_left -= 6;
+ } else {
+ dev_err(&remote->interface->dev,
+ "%s - Unknown sequence found in button data.\n",
+ __func__);
+ remote->stage = 0;
+ return;
+ }
+ }
+
+ keyspan_load_tester(remote, 6);
+ if ((remote->data.tester & ZERO_MASK) == ZERO) {
+ message.toggle = 0;
+ remote->data.tester = remote->data.tester >> 5;
+ remote->data.bits_left -= 5;
+ } else if ((remote->data.tester & ONE_MASK) == ONE) {
+ message.toggle = 1;
+ remote->data.tester = remote->data.tester >> 6;
+ remote->data.bits_left -= 6;
+ } else {
+ dev_err(&remote->interface->dev,
+ "%s - Error in message, invalid toggle.\n",
+ __func__);
+ remote->stage = 0;
+ return;
+ }
+
+ keyspan_load_tester(remote, 5);
+ if ((remote->data.tester & STOP_MASK) == STOP) {
+ remote->data.tester = remote->data.tester >> 5;
+ remote->data.bits_left -= 5;
+ } else {
+ dev_err(&remote->interface->dev,
+ "Bad message received, no stop bit found.\n");
+ }
+
+ dev_dbg(&remote->interface->dev,
+ "%s found valid message: system: %d, button: %d, toggle: %d\n",
+ __func__, message.system, message.button, message.toggle);
+
+ if (message.toggle != remote->toggle) {
+ keyspan_report_button(remote, message.button, 1);
+ keyspan_report_button(remote, message.button, 0);
+ remote->toggle = message.toggle;
+ }
+
+ remote->stage = 0;
+ break;
+ }
+}
+
+/*
+ * Routine for sending all the initialization messages to the remote.
+ */
+static int keyspan_setup(struct usb_device* dev)
+{
+ int retval = 0;
+
+ retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x11, 0x40, 0x5601, 0x0, NULL, 0, 0);
+ if (retval) {
+ dev_dbg(&dev->dev, "%s - failed to set bit rate due to error: %d\n",
+ __func__, retval);
+ return(retval);
+ }
+
+ retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x44, 0x40, 0x0, 0x0, NULL, 0, 0);
+ if (retval) {
+ dev_dbg(&dev->dev, "%s - failed to set resume sensitivity due to error: %d\n",
+ __func__, retval);
+ return(retval);
+ }
+
+ retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x22, 0x40, 0x0, 0x0, NULL, 0, 0);
+ if (retval) {
+ dev_dbg(&dev->dev, "%s - failed to turn receive on due to error: %d\n",
+ __func__, retval);
+ return(retval);
+ }
+
+ dev_dbg(&dev->dev, "%s - Setup complete.\n", __func__);
+ return(retval);
+}
+
+/*
+ * Routine used to handle a new message that has come in.
+ */
+static void keyspan_irq_recv(struct urb *urb)
+{
+ struct usb_keyspan *dev = urb->context;
+ int retval;
+
+ /* Check our status in case we need to bail out early. */
+ switch (urb->status) {
+ case 0:
+ break;
+
+ /* Device went away so don't keep trying to read from it. */
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+
+ default:
+ goto resubmit;
+ break;
+ }
+
+ if (debug)
+ keyspan_print(dev);
+
+ keyspan_check_data(dev);
+
+resubmit:
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(&dev->interface->dev,
+ "%s - usb_submit_urb failed with result: %d\n",
+ __func__, retval);
+}
+
+static int keyspan_open(struct input_dev *dev)
+{
+ struct usb_keyspan *remote = input_get_drvdata(dev);
+
+ remote->irq_urb->dev = remote->udev;
+ if (usb_submit_urb(remote->irq_urb, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+static void keyspan_close(struct input_dev *dev)
+{
+ struct usb_keyspan *remote = input_get_drvdata(dev);
+
+ usb_kill_urb(remote->irq_urb);
+}
+
+static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_interface *iface)
+{
+
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+
+ for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
+ endpoint = &iface->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(endpoint)) {
+ /* we found our interrupt in endpoint */
+ return endpoint;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Routine that sets up the driver to handle a specific USB device detected on the bus.
+ */
+static int keyspan_probe(struct usb_interface *interface, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct usb_endpoint_descriptor *endpoint;
+ struct usb_keyspan *remote;
+ struct input_dev *input_dev;
+ int i, error;
+
+ endpoint = keyspan_get_in_endpoint(interface->cur_altsetting);
+ if (!endpoint)
+ return -ENODEV;
+
+ remote = kzalloc(sizeof(*remote), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!remote || !input_dev) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ remote->udev = udev;
+ remote->input = input_dev;
+ remote->interface = interface;
+ remote->in_endpoint = endpoint;
+ remote->toggle = -1; /* Set to -1 so we will always not match the toggle from the first remote message. */
+
+ remote->in_buffer = usb_alloc_coherent(udev, RECV_SIZE, GFP_ATOMIC, &remote->in_dma);
+ if (!remote->in_buffer) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ remote->irq_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!remote->irq_urb) {
+ error = -ENOMEM;
+ goto fail2;
+ }
+
+ error = keyspan_setup(udev);
+ if (error) {
+ error = -ENODEV;
+ goto fail3;
+ }
+
+ if (udev->manufacturer)
+ strlcpy(remote->name, udev->manufacturer, sizeof(remote->name));
+
+ if (udev->product) {
+ if (udev->manufacturer)
+ strlcat(remote->name, " ", sizeof(remote->name));
+ strlcat(remote->name, udev->product, sizeof(remote->name));
+ }
+
+ if (!strlen(remote->name))
+ snprintf(remote->name, sizeof(remote->name),
+ "USB Keyspan Remote %04x:%04x",
+ le16_to_cpu(udev->descriptor.idVendor),
+ le16_to_cpu(udev->descriptor.idProduct));
+
+ usb_make_path(udev, remote->phys, sizeof(remote->phys));
+ strlcat(remote->phys, "/input0", sizeof(remote->phys));
+ memcpy(remote->keymap, keyspan_key_table, sizeof(remote->keymap));
+
+ input_dev->name = remote->name;
+ input_dev->phys = remote->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &interface->dev;
+ input_dev->keycode = remote->keymap;
+ input_dev->keycodesize = sizeof(unsigned short);
+ input_dev->keycodemax = ARRAY_SIZE(remote->keymap);
+
+ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+ __set_bit(EV_KEY, input_dev->evbit);
+ for (i = 0; i < ARRAY_SIZE(keyspan_key_table); i++)
+ __set_bit(keyspan_key_table[i], input_dev->keybit);
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
+
+ input_set_drvdata(input_dev, remote);
+
+ input_dev->open = keyspan_open;
+ input_dev->close = keyspan_close;
+
+ /*
+ * Initialize the URB to access the device.
+ * The urb gets sent to the device in keyspan_open()
+ */
+ usb_fill_int_urb(remote->irq_urb,
+ remote->udev,
+ usb_rcvintpipe(remote->udev, endpoint->bEndpointAddress),
+ remote->in_buffer, RECV_SIZE, keyspan_irq_recv, remote,
+ endpoint->bInterval);
+ remote->irq_urb->transfer_dma = remote->in_dma;
+ remote->irq_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* we can register the device now, as it is ready */
+ error = input_register_device(remote->input);
+ if (error)
+ goto fail3;
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, remote);
+
+ return 0;
+
+ fail3: usb_free_urb(remote->irq_urb);
+ fail2: usb_free_coherent(udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
+ fail1: kfree(remote);
+ input_free_device(input_dev);
+
+ return error;
+}
+
+/*
+ * Routine called when a device is disconnected from the USB.
+ */
+static void keyspan_disconnect(struct usb_interface *interface)
+{
+ struct usb_keyspan *remote;
+
+ remote = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ if (remote) { /* We have a valid driver structure so clean up everything we allocated. */
+ input_unregister_device(remote->input);
+ usb_kill_urb(remote->irq_urb);
+ usb_free_urb(remote->irq_urb);
+ usb_free_coherent(remote->udev, RECV_SIZE, remote->in_buffer, remote->in_dma);
+ kfree(remote);
+ }
+}
+
+/*
+ * Standard driver set up sections
+ */
+static struct usb_driver keyspan_driver =
+{
+ .name = "keyspan_remote",
+ .probe = keyspan_probe,
+ .disconnect = keyspan_disconnect,
+ .id_table = keyspan_table
+};
+
+module_usb_driver(keyspan_driver);
+
+MODULE_DEVICE_TABLE(usb, keyspan_table);
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c
new file mode 100644
index 00000000..a993b67a
--- /dev/null
+++ b/drivers/input/misc/kxtj9.c
@@ -0,0 +1,674 @@
+/*
+ * Copyright (C) 2011 Kionix, Inc.
+ * Written by Chris Hudson <chudson@kionix.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input/kxtj9.h>
+#include <linux/input-polldev.h>
+
+#define NAME "kxtj9"
+#define G_MAX 8000
+/* OUTPUT REGISTERS */
+#define XOUT_L 0x06
+#define WHO_AM_I 0x0F
+/* CONTROL REGISTERS */
+#define INT_REL 0x1A
+#define CTRL_REG1 0x1B
+#define INT_CTRL1 0x1E
+#define DATA_CTRL 0x21
+/* CONTROL REGISTER 1 BITS */
+#define PC1_OFF 0x7F
+#define PC1_ON (1 << 7)
+/* Data ready funtion enable bit: set during probe if using irq mode */
+#define DRDYE (1 << 5)
+/* DATA CONTROL REGISTER BITS */
+#define ODR12_5F 0
+#define ODR25F 1
+#define ODR50F 2
+#define ODR100F 3
+#define ODR200F 4
+#define ODR400F 5
+#define ODR800F 6
+/* INTERRUPT CONTROL REGISTER 1 BITS */
+/* Set these during probe if using irq mode */
+#define KXTJ9_IEL (1 << 3)
+#define KXTJ9_IEA (1 << 4)
+#define KXTJ9_IEN (1 << 5)
+/* INPUT_ABS CONSTANTS */
+#define FUZZ 3
+#define FLAT 3
+/* RESUME STATE INDICES */
+#define RES_DATA_CTRL 0
+#define RES_CTRL_REG1 1
+#define RES_INT_CTRL1 2
+#define RESUME_ENTRIES 3
+
+/*
+ * The following table lists the maximum appropriate poll interval for each
+ * available output data rate.
+ */
+static const struct {
+ unsigned int cutoff;
+ u8 mask;
+} kxtj9_odr_table[] = {
+ { 3, ODR800F },
+ { 5, ODR400F },
+ { 10, ODR200F },
+ { 20, ODR100F },
+ { 40, ODR50F },
+ { 80, ODR25F },
+ { 0, ODR12_5F},
+};
+
+struct kxtj9_data {
+ struct i2c_client *client;
+ struct kxtj9_platform_data pdata;
+ struct input_dev *input_dev;
+#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
+ struct input_polled_dev *poll_dev;
+#endif
+ unsigned int last_poll_interval;
+ u8 shift;
+ u8 ctrl_reg1;
+ u8 data_ctrl;
+ u8 int_ctrl;
+};
+
+static int kxtj9_i2c_read(struct kxtj9_data *tj9, u8 addr, u8 *data, int len)
+{
+ struct i2c_msg msgs[] = {
+ {
+ .addr = tj9->client->addr,
+ .flags = tj9->client->flags,
+ .len = 1,
+ .buf = &addr,
+ },
+ {
+ .addr = tj9->client->addr,
+ .flags = tj9->client->flags | I2C_M_RD,
+ .len = len,
+ .buf = data,
+ },
+ };
+
+ return i2c_transfer(tj9->client->adapter, msgs, 2);
+}
+
+static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9)
+{
+ s16 acc_data[3]; /* Data bytes from hardware xL, xH, yL, yH, zL, zH */
+ s16 x, y, z;
+ int err;
+
+ err = kxtj9_i2c_read(tj9, XOUT_L, (u8 *)acc_data, 6);
+ if (err < 0)
+ dev_err(&tj9->client->dev, "accelerometer data read failed\n");
+
+ x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]);
+ y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]);
+ z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]);
+
+ x >>= tj9->shift;
+ y >>= tj9->shift;
+ z >>= tj9->shift;
+
+ input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x);
+ input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y);
+ input_report_abs(tj9->input_dev, ABS_Z, tj9->pdata.negate_z ? -z : z);
+ input_sync(tj9->input_dev);
+}
+
+static irqreturn_t kxtj9_isr(int irq, void *dev)
+{
+ struct kxtj9_data *tj9 = dev;
+ int err;
+
+ /* data ready is the only possible interrupt type */
+ kxtj9_report_acceleration_data(tj9);
+
+ err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
+ if (err < 0)
+ dev_err(&tj9->client->dev,
+ "error clearing interrupt status: %d\n", err);
+
+ return IRQ_HANDLED;
+}
+
+static int kxtj9_update_g_range(struct kxtj9_data *tj9, u8 new_g_range)
+{
+ switch (new_g_range) {
+ case KXTJ9_G_2G:
+ tj9->shift = 4;
+ break;
+ case KXTJ9_G_4G:
+ tj9->shift = 3;
+ break;
+ case KXTJ9_G_8G:
+ tj9->shift = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tj9->ctrl_reg1 &= 0xe7;
+ tj9->ctrl_reg1 |= new_g_range;
+
+ return 0;
+}
+
+static int kxtj9_update_odr(struct kxtj9_data *tj9, unsigned int poll_interval)
+{
+ int err;
+ int i;
+
+ /* Use the lowest ODR that can support the requested poll interval */
+ for (i = 0; i < ARRAY_SIZE(kxtj9_odr_table); i++) {
+ tj9->data_ctrl = kxtj9_odr_table[i].mask;
+ if (poll_interval < kxtj9_odr_table[i].cutoff)
+ break;
+ }
+
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0);
+ if (err < 0)
+ return err;
+
+ err = i2c_smbus_write_byte_data(tj9->client, DATA_CTRL, tj9->data_ctrl);
+ if (err < 0)
+ return err;
+
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int kxtj9_device_power_on(struct kxtj9_data *tj9)
+{
+ if (tj9->pdata.power_on)
+ return tj9->pdata.power_on();
+
+ return 0;
+}
+
+static void kxtj9_device_power_off(struct kxtj9_data *tj9)
+{
+ int err;
+
+ tj9->ctrl_reg1 &= PC1_OFF;
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+ if (err < 0)
+ dev_err(&tj9->client->dev, "soft power off failed\n");
+
+ if (tj9->pdata.power_off)
+ tj9->pdata.power_off();
+}
+
+static int kxtj9_enable(struct kxtj9_data *tj9)
+{
+ int err;
+
+ err = kxtj9_device_power_on(tj9);
+ if (err < 0)
+ return err;
+
+ /* ensure that PC1 is cleared before updating control registers */
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, 0);
+ if (err < 0)
+ return err;
+
+ /* only write INT_CTRL_REG1 if in irq mode */
+ if (tj9->client->irq) {
+ err = i2c_smbus_write_byte_data(tj9->client,
+ INT_CTRL1, tj9->int_ctrl);
+ if (err < 0)
+ return err;
+ }
+
+ err = kxtj9_update_g_range(tj9, tj9->pdata.g_range);
+ if (err < 0)
+ return err;
+
+ /* turn on outputs */
+ tj9->ctrl_reg1 |= PC1_ON;
+ err = i2c_smbus_write_byte_data(tj9->client, CTRL_REG1, tj9->ctrl_reg1);
+ if (err < 0)
+ return err;
+
+ err = kxtj9_update_odr(tj9, tj9->last_poll_interval);
+ if (err < 0)
+ return err;
+
+ /* clear initial interrupt if in irq mode */
+ if (tj9->client->irq) {
+ err = i2c_smbus_read_byte_data(tj9->client, INT_REL);
+ if (err < 0) {
+ dev_err(&tj9->client->dev,
+ "error clearing interrupt: %d\n", err);
+ goto fail;
+ }
+ }
+
+ return 0;
+
+fail:
+ kxtj9_device_power_off(tj9);
+ return err;
+}
+
+static void kxtj9_disable(struct kxtj9_data *tj9)
+{
+ kxtj9_device_power_off(tj9);
+}
+
+static int kxtj9_input_open(struct input_dev *input)
+{
+ struct kxtj9_data *tj9 = input_get_drvdata(input);
+
+ return kxtj9_enable(tj9);
+}
+
+static void kxtj9_input_close(struct input_dev *dev)
+{
+ struct kxtj9_data *tj9 = input_get_drvdata(dev);
+
+ kxtj9_disable(tj9);
+}
+
+static void kxtj9_init_input_device(struct kxtj9_data *tj9,
+ struct input_dev *input_dev)
+{
+ __set_bit(EV_ABS, input_dev->evbit);
+ input_set_abs_params(input_dev, ABS_X, -G_MAX, G_MAX, FUZZ, FLAT);
+ input_set_abs_params(input_dev, ABS_Y, -G_MAX, G_MAX, FUZZ, FLAT);
+ input_set_abs_params(input_dev, ABS_Z, -G_MAX, G_MAX, FUZZ, FLAT);
+
+ input_dev->name = "kxtj9_accel";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &tj9->client->dev;
+}
+
+static int kxtj9_setup_input_device(struct kxtj9_data *tj9)
+{
+ struct input_dev *input_dev;
+ int err;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(&tj9->client->dev, "input device allocate failed\n");
+ return -ENOMEM;
+ }
+
+ tj9->input_dev = input_dev;
+
+ input_dev->open = kxtj9_input_open;
+ input_dev->close = kxtj9_input_close;
+ input_set_drvdata(input_dev, tj9);
+
+ kxtj9_init_input_device(tj9, input_dev);
+
+ err = input_register_device(tj9->input_dev);
+ if (err) {
+ dev_err(&tj9->client->dev,
+ "unable to register input polled device %s: %d\n",
+ tj9->input_dev->name, err);
+ input_free_device(tj9->input_dev);
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * When IRQ mode is selected, we need to provide an interface to allow the user
+ * to change the output data rate of the part. For consistency, we are using
+ * the set_poll method, which accepts a poll interval in milliseconds, and then
+ * calls update_odr() while passing this value as an argument. In IRQ mode, the
+ * data outputs will not be read AT the requested poll interval, rather, the
+ * lowest ODR that can support the requested interval. The client application
+ * will be responsible for retrieving data from the input node at the desired
+ * interval.
+ */
+
+/* Returns currently selected poll interval (in ms) */
+static ssize_t kxtj9_get_poll(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%d\n", tj9->last_poll_interval);
+}
+
+/* Allow users to select a new poll interval (in ms) */
+static ssize_t kxtj9_set_poll(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+ struct input_dev *input_dev = tj9->input_dev;
+ unsigned int interval;
+ int error;
+
+ error = kstrtouint(buf, 10, &interval);
+ if (error < 0)
+ return error;
+
+ /* Lock the device to prevent races with open/close (and itself) */
+ mutex_lock(&input_dev->mutex);
+
+ disable_irq(client->irq);
+
+ /*
+ * Set current interval to the greater of the minimum interval or
+ * the requested interval
+ */
+ tj9->last_poll_interval = max(interval, tj9->pdata.min_interval);
+
+ kxtj9_update_odr(tj9, tj9->last_poll_interval);
+
+ enable_irq(client->irq);
+ mutex_unlock(&input_dev->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(poll, S_IRUGO|S_IWUSR, kxtj9_get_poll, kxtj9_set_poll);
+
+static struct attribute *kxtj9_attributes[] = {
+ &dev_attr_poll.attr,
+ NULL
+};
+
+static struct attribute_group kxtj9_attribute_group = {
+ .attrs = kxtj9_attributes
+};
+
+
+#ifdef CONFIG_INPUT_KXTJ9_POLLED_MODE
+static void kxtj9_poll(struct input_polled_dev *dev)
+{
+ struct kxtj9_data *tj9 = dev->private;
+ unsigned int poll_interval = dev->poll_interval;
+
+ kxtj9_report_acceleration_data(tj9);
+
+ if (poll_interval != tj9->last_poll_interval) {
+ kxtj9_update_odr(tj9, poll_interval);
+ tj9->last_poll_interval = poll_interval;
+ }
+}
+
+static void kxtj9_polled_input_open(struct input_polled_dev *dev)
+{
+ struct kxtj9_data *tj9 = dev->private;
+
+ kxtj9_enable(tj9);
+}
+
+static void kxtj9_polled_input_close(struct input_polled_dev *dev)
+{
+ struct kxtj9_data *tj9 = dev->private;
+
+ kxtj9_disable(tj9);
+}
+
+static int kxtj9_setup_polled_device(struct kxtj9_data *tj9)
+{
+ int err;
+ struct input_polled_dev *poll_dev;
+ poll_dev = input_allocate_polled_device();
+
+ if (!poll_dev) {
+ dev_err(&tj9->client->dev,
+ "Failed to allocate polled device\n");
+ return -ENOMEM;
+ }
+
+ tj9->poll_dev = poll_dev;
+ tj9->input_dev = poll_dev->input;
+
+ poll_dev->private = tj9;
+ poll_dev->poll = kxtj9_poll;
+ poll_dev->open = kxtj9_polled_input_open;
+ poll_dev->close = kxtj9_polled_input_close;
+
+ kxtj9_init_input_device(tj9, poll_dev->input);
+
+ err = input_register_polled_device(poll_dev);
+ if (err) {
+ dev_err(&tj9->client->dev,
+ "Unable to register polled device, err=%d\n", err);
+ input_free_polled_device(poll_dev);
+ return err;
+ }
+
+ return 0;
+}
+
+static void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
+{
+ input_unregister_polled_device(tj9->poll_dev);
+ input_free_polled_device(tj9->poll_dev);
+}
+
+#else
+
+static inline int kxtj9_setup_polled_device(struct kxtj9_data *tj9)
+{
+ return -ENOSYS;
+}
+
+static inline void kxtj9_teardown_polled_device(struct kxtj9_data *tj9)
+{
+}
+
+#endif
+
+static int kxtj9_verify(struct kxtj9_data *tj9)
+{
+ int retval;
+
+ retval = kxtj9_device_power_on(tj9);
+ if (retval < 0)
+ return retval;
+
+ retval = i2c_smbus_read_byte_data(tj9->client, WHO_AM_I);
+ if (retval < 0) {
+ dev_err(&tj9->client->dev, "read err int source\n");
+ goto out;
+ }
+
+ retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0;
+
+out:
+ kxtj9_device_power_off(tj9);
+ return retval;
+}
+
+static int kxtj9_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct kxtj9_platform_data *pdata = client->dev.platform_data;
+ struct kxtj9_data *tj9;
+ int err;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA)) {
+ dev_err(&client->dev, "client is not i2c capable\n");
+ return -ENXIO;
+ }
+
+ if (!pdata) {
+ dev_err(&client->dev, "platform data is NULL; exiting\n");
+ return -EINVAL;
+ }
+
+ tj9 = kzalloc(sizeof(*tj9), GFP_KERNEL);
+ if (!tj9) {
+ dev_err(&client->dev,
+ "failed to allocate memory for module data\n");
+ return -ENOMEM;
+ }
+
+ tj9->client = client;
+ tj9->pdata = *pdata;
+
+ if (pdata->init) {
+ err = pdata->init();
+ if (err < 0)
+ goto err_free_mem;
+ }
+
+ err = kxtj9_verify(tj9);
+ if (err < 0) {
+ dev_err(&client->dev, "device not recognized\n");
+ goto err_pdata_exit;
+ }
+
+ i2c_set_clientdata(client, tj9);
+
+ tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range;
+ tj9->last_poll_interval = tj9->pdata.init_interval;
+
+ if (client->irq) {
+ /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */
+ tj9->int_ctrl |= KXTJ9_IEN | KXTJ9_IEA | KXTJ9_IEL;
+ tj9->ctrl_reg1 |= DRDYE;
+
+ err = kxtj9_setup_input_device(tj9);
+ if (err)
+ goto err_pdata_exit;
+
+ err = request_threaded_irq(client->irq, NULL, kxtj9_isr,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "kxtj9-irq", tj9);
+ if (err) {
+ dev_err(&client->dev, "request irq failed: %d\n", err);
+ goto err_destroy_input;
+ }
+
+ err = sysfs_create_group(&client->dev.kobj, &kxtj9_attribute_group);
+ if (err) {
+ dev_err(&client->dev, "sysfs create failed: %d\n", err);
+ goto err_free_irq;
+ }
+
+ } else {
+ err = kxtj9_setup_polled_device(tj9);
+ if (err)
+ goto err_pdata_exit;
+ }
+
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, tj9);
+err_destroy_input:
+ input_unregister_device(tj9->input_dev);
+err_pdata_exit:
+ if (tj9->pdata.exit)
+ tj9->pdata.exit();
+err_free_mem:
+ kfree(tj9);
+ return err;
+}
+
+static int kxtj9_remove(struct i2c_client *client)
+{
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+
+ if (client->irq) {
+ sysfs_remove_group(&client->dev.kobj, &kxtj9_attribute_group);
+ free_irq(client->irq, tj9);
+ input_unregister_device(tj9->input_dev);
+ } else {
+ kxtj9_teardown_polled_device(tj9);
+ }
+
+ if (tj9->pdata.exit)
+ tj9->pdata.exit();
+
+ kfree(tj9);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int kxtj9_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+ struct input_dev *input_dev = tj9->input_dev;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ kxtj9_disable(tj9);
+
+ mutex_unlock(&input_dev->mutex);
+ return 0;
+}
+
+static int kxtj9_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct kxtj9_data *tj9 = i2c_get_clientdata(client);
+ struct input_dev *input_dev = tj9->input_dev;
+ int retval = 0;
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users)
+ kxtj9_enable(tj9);
+
+ mutex_unlock(&input_dev->mutex);
+ return retval;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(kxtj9_pm_ops, kxtj9_suspend, kxtj9_resume);
+
+static const struct i2c_device_id kxtj9_id[] = {
+ { NAME, 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, kxtj9_id);
+
+static struct i2c_driver kxtj9_driver = {
+ .driver = {
+ .name = NAME,
+ .owner = THIS_MODULE,
+ .pm = &kxtj9_pm_ops,
+ },
+ .probe = kxtj9_probe,
+ .remove = kxtj9_remove,
+ .id_table = kxtj9_id,
+};
+
+module_i2c_driver(kxtj9_driver);
+
+MODULE_DESCRIPTION("KXTJ9 accelerometer driver");
+MODULE_AUTHOR("Chris Hudson <chudson@kionix.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/lis3dh.c b/drivers/input/misc/lis3dh.c
new file mode 100644
index 00000000..947c3bbb
--- /dev/null
+++ b/drivers/input/misc/lis3dh.c
@@ -0,0 +1,1707 @@
+/******************** (C) COPYRIGHT 2010 STMicroelectronics ********************
+ *
+ * File Name : lis3dh_acc.c
+ * Authors : MSH - Motion Mems BU - Application Team
+ * : Carmine Iascone (carmine.iascone@st.com)
+ * : Matteo Dameno (matteo.dameno@st.com)
+ * Version : V.1.0.5
+ * Date : 16/08/2010
+ * Description : LIS3DH accelerometer 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.
+ *
+ * THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS.
+ *
+ ******************************************************************************
+ Revision 1.0.0 05/11/09
+ First Release
+ Revision 1.0.3 22/01/2010
+ Linux K&R Compliant Release;
+ Revision 1.0.5 16/08/2010
+ modified _get_acceleration_data function
+ modified _update_odr function
+ manages 2 interrupts
+
+ ******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include <linux/i2c/lis3dh.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+
+#define INTERRUPT_MANAGEMENT 1
+
+#define G_MAX 16000 /** Maximum polled-device-reported g value */
+
+/*
+#define SHIFT_ADJ_2G 4
+#define SHIFT_ADJ_4G 3
+#define SHIFT_ADJ_8G 2
+#define SHIFT_ADJ_16G 1
+*/
+
+#define SENSITIVITY_2G 1 /** mg/LSB */
+#define SENSITIVITY_4G 2 /** mg/LSB */
+#define SENSITIVITY_8G 4 /** mg/LSB */
+#define SENSITIVITY_16G 12 /** mg/LSB */
+
+#define HIGH_RESOLUTION 0x08
+
+#define AXISDATA_REG 0x28
+#define WHOAMI_LIS3DH_ACC 0x33 /* Expctd content for WAI */
+
+/* CONTROL REGISTERS */
+#define WHO_AM_I 0x0F /* WhoAmI register */
+#define TEMP_CFG_REG 0x1F /* temper sens control reg */
+/* ctrl 1: ODR3 ODR2 ODR ODR0 LPen Zenable Yenable Zenable */
+#define CTRL_REG1 0x20 /* control reg 1 */
+#define CTRL_REG2 0x21 /* control reg 2 */
+#define CTRL_REG3 0x22 /* control reg 3 */
+#define CTRL_REG4 0x23 /* control reg 4 */
+#define CTRL_REG5 0x24 /* control reg 5 */
+#define CTRL_REG6 0x25 /* control reg 6 */
+
+#define FIFO_CTRL_REG 0x2E /* FiFo control reg */
+
+#define INT_CFG1 0x30 /* interrupt 1 config */
+#define INT_SRC1 0x31 /* interrupt 1 source */
+#define INT_THS1 0x32 /* interrupt 1 threshold */
+#define INT_DUR1 0x33 /* interrupt 1 duration */
+
+#define INT_CFG2 0x34 /* interrupt 2 config */
+#define INT_SRC2 0x35 /* interrupt 2 source */
+#define INT_THS2 0x36 /* interrupt 2 threshold */
+#define INT_DUR2 0x37 /* interrupt 2 duration */
+
+#define TT_CFG 0x38 /* tap config */
+#define TT_SRC 0x39 /* tap source */
+#define TT_THS 0x3A /* tap threshold */
+#define TT_LIM 0x3B /* tap time limit */
+#define TT_TLAT 0x3C /* tap time latency */
+#define TT_TW 0x3D /* tap time window */
+/* end CONTROL REGISTRES */
+
+#define ENABLE_HIGH_RESOLUTION 1
+
+#define LIS3DH_ACC_PM_OFF 0x00
+#define LIS3DH_ACC_ENABLE_ALL_AXES 0x07
+
+#define PMODE_MASK 0x08
+#define ODR_MASK 0XF0
+
+#define ODR1 0x10 /* 1Hz output data rate */
+#define ODR10 0x20 /* 10Hz output data rate */
+#define ODR25 0x30 /* 25Hz output data rate */
+#define ODR50 0x40 /* 50Hz output data rate */
+#define ODR100 0x50 /* 100Hz output data rate */
+#define ODR200 0x60 /* 200Hz output data rate */
+#define ODR400 0x70 /* 400Hz output data rate */
+#define ODR1250 0x90 /* 1250Hz output data rate */
+
+#define IA 0x40
+#define ZH 0x20
+#define ZL 0x10
+#define YH 0x08
+#define YL 0x04
+#define XH 0x02
+#define XL 0x01
+/* */
+/* CTRL REG BITS*/
+#define CTRL_REG3_I1_AOI1 0x40
+#define CTRL_REG6_I2_TAPEN 0x80
+#define CTRL_REG6_HLACTIVE 0x02
+/* */
+
+/* TAP_SOURCE_REG BIT */
+#define DTAP 0x20
+#define STAP 0x10
+#define SIGNTAP 0x08
+#define ZTAP 0x04
+#define YTAP 0x02
+#define XTAZ 0x01
+
+#define FUZZ 32
+#define FLAT 32
+#define I2C_RETRY_DELAY 5
+#define I2C_RETRIES 5
+#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_CTRL_REG6 5
+
+#define RES_INT_CFG1 6
+#define RES_INT_THS1 7
+#define RES_INT_DUR1 8
+#define RES_INT_CFG2 9
+#define RES_INT_THS2 10
+#define RES_INT_DUR2 11
+
+#define RES_TT_CFG 12
+#define RES_TT_THS 13
+#define RES_TT_LIM 14
+#define RES_TT_TLAT 15
+#define RES_TT_TW 16
+
+#define RES_TEMP_CFG_REG 17
+#define RES_REFERENCE_REG 18
+#define RES_FIFO_CTRL_REG 19
+
+#define RESUME_ENTRIES 20
+#define DEVICE_INFO "ST, LIS3DH"
+#define DEVICE_INFO_LEN 32
+/* end RESUME STATE INDICES */
+
+#define GSENSOR_GINT1_GPI 0
+#define GSENSOR_GINT2_GPI 1
+
+struct {
+ unsigned int cutoff_ms;
+ unsigned int mask;
+} lis3dh_acc_odr_table[] = {
+ {
+ 1, ODR1250}, {
+ 3, ODR400}, {
+ 5, ODR200}, {
+ 10, ODR100}, {
+ 20, ODR50}, {
+ 40, ODR25}, {
+ 100, ODR10}, {
+1000, ODR1},};
+
+struct lis3dh_acc_data {
+ struct i2c_client *client;
+ struct lis3dh_acc_platform_data *pdata;
+
+ struct mutex lock;
+ struct delayed_work input_work;
+
+ struct input_dev *input_dev;
+
+ int hw_initialized;
+ /* hw_working=-1 means not tested yet */
+ int hw_working;
+ atomic_t enabled;
+ int on_before_suspend;
+
+ u8 sensitivity;
+
+ u8 resume_state[RESUME_ENTRIES];
+
+ 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;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+};
+
+/*
+ * Because misc devices can not carry a pointer from driver register to
+ * open, we keep this global. This limits the driver to a single instance.
+ */
+struct lis3dh_acc_data *lis3dh_acc_misc_data;
+struct i2c_client *lis3dh_i2c_client;
+
+static int lis3dh_acc_i2c_read(struct lis3dh_acc_data *acc, u8 * buf, int len)
+{
+ int err;
+ int tries = 0;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = acc->client->addr,
+ .flags = acc->client->flags & I2C_M_TEN,
+ .len = 1,
+ .buf = buf,},
+ {
+ .addr = acc->client->addr,
+ .flags = (acc->client->flags & I2C_M_TEN) | I2C_M_RD,
+ .len = len,
+ .buf = buf,},
+ };
+
+ do {
+ err = i2c_transfer(acc->client->adapter, msgs, 2);
+ if (err != 2)
+ msleep_interruptible(I2C_RETRY_DELAY);
+ } while ((err != 2) && (++tries < I2C_RETRIES));
+
+ if (err != 2) {
+ dev_err(&acc->client->dev, "read transfer error\n");
+ err = -EIO;
+ } else {
+ err = 0;
+ }
+
+ return err;
+}
+
+static int lis3dh_acc_i2c_write(struct lis3dh_acc_data *acc, u8 * buf, int len)
+{
+ int err;
+ int tries = 0;
+
+ struct i2c_msg msgs[] = { {.addr = acc->client->addr,
+ .flags = acc->client->flags & I2C_M_TEN,
+ .len = len + 1,.buf = buf,},
+ };
+ do {
+ err = i2c_transfer(acc->client->adapter, msgs, 1);
+ if (err != 1)
+ msleep_interruptible(I2C_RETRY_DELAY);
+ } while ((err != 1) && (++tries < I2C_RETRIES));
+
+ if (err != 1) {
+ dev_err(&acc->client->dev, "write transfer error\n");
+ err = -EIO;
+ } else {
+ err = 0;
+ }
+
+ return err;
+}
+
+static int lis3dh_acc_hw_init(struct lis3dh_acc_data *acc)
+{
+ int err = -1;
+ u8 buf[7];
+
+ pr_debug("%s: hw init start\n", LIS3DH_ACC_DEV_NAME);
+
+ buf[0] = WHO_AM_I;
+ err = lis3dh_acc_i2c_read(acc, buf, 1);
+ if (err < 0)
+ goto error_firstread;
+ else
+ acc->hw_working = 1;
+ if (buf[0] != WHOAMI_LIS3DH_ACC) {
+ err = -1; /* choose the right coded error */
+ goto error_unknown_device;
+ }
+
+ buf[0] = CTRL_REG1;
+ buf[1] = acc->resume_state[RES_CTRL_REG1];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = TEMP_CFG_REG;
+ buf[1] = acc->resume_state[RES_TEMP_CFG_REG];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = FIFO_CTRL_REG;
+ buf[1] = acc->resume_state[RES_FIFO_CTRL_REG];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = (I2C_AUTO_INCREMENT | TT_THS);
+ buf[1] = acc->resume_state[RES_TT_THS];
+ buf[2] = acc->resume_state[RES_TT_LIM];
+ buf[3] = acc->resume_state[RES_TT_TLAT];
+ buf[4] = acc->resume_state[RES_TT_TW];
+ err = lis3dh_acc_i2c_write(acc, buf, 4);
+ if (err < 0)
+ goto error1;
+ buf[0] = TT_CFG;
+ buf[1] = acc->resume_state[RES_TT_CFG];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = (I2C_AUTO_INCREMENT | INT_THS1);
+ buf[1] = acc->resume_state[RES_INT_THS1];
+ buf[2] = acc->resume_state[RES_INT_DUR1];
+ err = lis3dh_acc_i2c_write(acc, buf, 2);
+ if (err < 0)
+ goto error1;
+ buf[0] = INT_CFG1;
+ buf[1] = acc->resume_state[RES_INT_CFG1];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = (I2C_AUTO_INCREMENT | INT_THS2);
+ buf[1] = acc->resume_state[RES_INT_THS2];
+ buf[2] = acc->resume_state[RES_INT_DUR2];
+ err = lis3dh_acc_i2c_write(acc, buf, 2);
+ if (err < 0)
+ goto error1;
+ buf[0] = INT_CFG2;
+ buf[1] = acc->resume_state[RES_INT_CFG2];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = (I2C_AUTO_INCREMENT | CTRL_REG2);
+ buf[1] = acc->resume_state[RES_CTRL_REG2];
+ buf[2] = acc->resume_state[RES_CTRL_REG3];
+ buf[3] = acc->resume_state[RES_CTRL_REG4];
+ buf[4] = acc->resume_state[RES_CTRL_REG5];
+ buf[5] = acc->resume_state[RES_CTRL_REG6];
+ err = lis3dh_acc_i2c_write(acc, buf, 5);
+ if (err < 0)
+ goto error1;
+
+ acc->hw_initialized = 1;
+ pr_debug("%s: hw init done\n", LIS3DH_ACC_DEV_NAME);
+ return 0;
+
+error_firstread:
+ acc->hw_working = 0;
+ dev_warn(&acc->client->dev, "Error reading WHO_AM_I: is device "
+ "available/working?\n");
+ goto error1;
+error_unknown_device:
+ dev_err(&acc->client->dev,
+ "device unknown. Expected: 0x%x,"
+ " Replies: 0x%x\n", WHOAMI_LIS3DH_ACC, buf[0]);
+error1:
+ acc->hw_initialized = 0;
+ dev_err(&acc->client->dev, "hw init error 0x%x,0x%x: %d\n", buf[0],
+ buf[1], err);
+ return err;
+}
+
+static void lis3dh_acc_device_power_off(struct lis3dh_acc_data *acc)
+{
+ int err;
+ u8 buf[2] = { CTRL_REG1, LIS3DH_ACC_PM_OFF };
+
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ dev_err(&acc->client->dev, "soft power off failed: %d\n", err);
+
+ if (acc->pdata->power_off) {
+ if (acc->irq1 != 0)
+ disable_irq_nosync(acc->irq1);
+ if (acc->irq2 != 0)
+ disable_irq_nosync(acc->irq2);
+ acc->pdata->power_off();
+ acc->hw_initialized = 0;
+ }
+ if (acc->hw_initialized) {
+ if (acc->irq1 != 0)
+ disable_irq_nosync(acc->irq1);
+ if (acc->irq2 != 0)
+ disable_irq_nosync(acc->irq2);
+ acc->hw_initialized = 0;
+ }
+}
+
+static int lis3dh_acc_device_power_on(struct lis3dh_acc_data *acc)
+{
+ int err = -1;
+
+ if (acc->pdata->power_on) {
+ err = acc->pdata->power_on();
+ if (err < 0) {
+ dev_err(&acc->client->dev,
+ "power_on failed: %d\n", err);
+ return err;
+ }
+ }
+
+ if (!acc->hw_initialized) {
+ err = lis3dh_acc_hw_init(acc);
+ if (acc->hw_working == 1 && err < 0) {
+ lis3dh_acc_device_power_off(acc);
+ return err;
+ }
+ }
+ return 0;
+}
+
+static irqreturn_t lis3dh_acc_isr1(int irq, void *dev)
+{
+ struct lis3dh_acc_data *acc = dev;
+
+ disable_irq_nosync(irq);
+ queue_work(acc->irq1_work_queue, &acc->irq1_work);
+
+ pr_debug("%s: isr1 queued\n", LIS3DH_ACC_DEV_NAME);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t lis3dh_acc_isr2(int irq, void *dev)
+{
+ struct lis3dh_acc_data *acc = dev;
+
+ disable_irq_nosync(irq);
+ queue_work(acc->irq2_work_queue, &acc->irq2_work);
+
+ pr_debug("%s: isr2 queued\n", LIS3DH_ACC_DEV_NAME);
+
+ return IRQ_HANDLED;
+}
+
+static void lis3dh_acc_irq1_work_func(struct work_struct *work)
+{
+
+ /*struct lis3dh_acc_data *acc =
+ container_of(work, struct lis3dh_acc_data, irq1_work);
+ */
+ /* TODO add interrupt service procedure.
+ ie:lis3dh_acc_get_int1_source(acc); */
+ ;
+ /* */
+ pr_debug("%s: IRQ1 triggered\n", LIS3DH_ACC_DEV_NAME);
+}
+
+static void lis3dh_acc_irq2_work_func(struct work_struct *work)
+{
+
+ /*struct lis3dh_acc_data *acc =
+ container_of(work, struct lis3dh_acc_data, irq2_work);
+ */
+ /* TODO add interrupt service procedure.
+ ie:lis3dh_acc_get_tap_source(acc); */
+ ;
+ /* */
+
+ pr_debug("%s: IRQ2 triggered\n", LIS3DH_ACC_DEV_NAME);
+}
+
+int lis3dh_acc_update_g_range(struct lis3dh_acc_data *acc, u8 new_g_range)
+{
+ int err;
+
+ u8 sensitivity;
+ u8 buf[2];
+ u8 updated_val;
+ u8 init_val;
+ u8 new_val;
+ u8 mask = LIS3DH_ACC_FS_MASK | HIGH_RESOLUTION;
+
+ pr_debug("%s\n", __func__);
+
+ switch (new_g_range) {
+ case LIS3DH_ACC_G_2G:
+
+ sensitivity = SENSITIVITY_2G;
+ break;
+ case LIS3DH_ACC_G_4G:
+
+ sensitivity = SENSITIVITY_4G;
+ break;
+ case LIS3DH_ACC_G_8G:
+
+ sensitivity = SENSITIVITY_8G;
+ break;
+ case LIS3DH_ACC_G_16G:
+
+ sensitivity = SENSITIVITY_16G;
+ break;
+ default:
+ dev_err(&acc->client->dev, "invalid g range requested: %u\n",
+ new_g_range);
+ return -EINVAL;
+ }
+
+ if (atomic_read(&acc->enabled)) {
+ /* Set configuration register 4, which contains g range setting
+ * NOTE: this is a straight overwrite because this driver does
+ * not use any of the other configuration bits in this
+ * register. Should this become untrue, we will have to read
+ * out the value and only change the relevant bits --XX----
+ * (marked by X) */
+ buf[0] = CTRL_REG4;
+ err = lis3dh_acc_i2c_read(acc, buf, 1);
+ if (err < 0)
+ goto error;
+ init_val = buf[0];
+ acc->resume_state[RES_CTRL_REG4] = init_val;
+ new_val = new_g_range | HIGH_RESOLUTION;
+ updated_val = ((mask & new_val) | ((~mask) & init_val));
+ buf[1] = updated_val;
+ buf[0] = CTRL_REG4;
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error;
+ acc->resume_state[RES_CTRL_REG4] = updated_val;
+ acc->sensitivity = sensitivity;
+
+ pr_debug("%s sensitivity %d g-range %d\n", __func__, sensitivity,new_g_range);
+ }
+
+ return 0;
+error:
+ dev_err(&acc->client->dev, "update g range failed 0x%x,0x%x: %d\n",
+ buf[0], buf[1], err);
+
+ return err;
+}
+
+int lis3dh_acc_update_odr(struct lis3dh_acc_data *acc, int poll_interval_ms)
+{
+ int err = -1;
+ int i;
+ u8 config[2];
+
+ /* Convert the poll interval into an output data rate configuration
+ * that is as low as possible. The ordering of these checks must be
+ * maintained due to the cascading cut off values - poll intervals are
+ * checked from shortest to longest. At each check, if the next lower
+ * ODR cannot support the current poll interval, we stop searching */
+ for (i = ARRAY_SIZE(lis3dh_acc_odr_table) - 1; i >= 0; i--) {
+ if (lis3dh_acc_odr_table[i].cutoff_ms <= poll_interval_ms)
+ break;
+ }
+ config[1] = lis3dh_acc_odr_table[i].mask;
+
+ config[1] |= LIS3DH_ACC_ENABLE_ALL_AXES;
+
+ /* If device is currently enabled, we need to write new
+ * configuration out to it */
+ if (atomic_read(&acc->enabled)) {
+ config[0] = CTRL_REG1;
+ err = lis3dh_acc_i2c_write(acc, config, 1);
+ if (err < 0)
+ goto error;
+ acc->resume_state[RES_CTRL_REG1] = config[1];
+ }
+
+ return 0;
+
+error:
+ dev_err(&acc->client->dev, "update odr failed 0x%x,0x%x: %d\n",
+ config[0], config[1], err);
+
+ return err;
+}
+
+/* */
+
+static int lis3dh_acc_register_write(struct lis3dh_acc_data *acc, u8 * buf,
+ u8 reg_address, u8 new_value)
+{
+ int err = -1;
+
+ if (atomic_read(&acc->enabled)) {
+ /* Sets configuration register at reg_address
+ * NOTE: this is a straight overwrite */
+ buf[0] = reg_address;
+ buf[1] = new_value;
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ return err;
+ }
+ return err;
+}
+
+static int lis3dh_acc_register_read(struct lis3dh_acc_data *acc, u8 * buf,
+ u8 reg_address)
+{
+
+ int err = -1;
+ buf[0] = (reg_address);
+ err = lis3dh_acc_i2c_read(acc, buf, 1);
+ return err;
+}
+
+static int lis3dh_acc_register_update(struct lis3dh_acc_data *acc, u8 * buf,
+ u8 reg_address, u8 mask,
+ u8 new_bit_values)
+{
+ int err = -1;
+ u8 init_val;
+ u8 updated_val;
+ err = lis3dh_acc_register_read(acc, buf, reg_address);
+ if (!(err < 0)) {
+ init_val = buf[1];
+ updated_val = ((mask & new_bit_values) | ((~mask) & init_val));
+ err = lis3dh_acc_register_write(acc, buf, reg_address,
+ updated_val);
+ }
+ return err;
+}
+
+/* */
+
+static int lis3dh_acc_get_acceleration_data(struct lis3dh_acc_data *acc,
+ int *xyz)
+{
+ int err = -1;
+ /* Data bytes from hardware xL, xH, yL, yH, zL, zH */
+ u8 acc_data[6];
+ /* x,y,z hardware data */
+ s16 hw_d[3] = { 0 };
+
+ acc_data[0] = (I2C_AUTO_INCREMENT | AXISDATA_REG);
+ err = lis3dh_acc_i2c_read(acc, acc_data, 6);
+
+ if (err < 0) {
+ pr_debug("%s I2C read error %d\n", LIS3DH_ACC_I2C_NAME,
+ err);
+ return err;
+ }
+
+ hw_d[0] = (((s16) ((acc_data[1] << 8) | acc_data[0])) >> 4);
+ hw_d[1] = (((s16) ((acc_data[3] << 8) | acc_data[2])) >> 4);
+ hw_d[2] = (((s16) ((acc_data[5] << 8) | acc_data[4])) >> 4);
+
+ hw_d[0] = hw_d[0] * acc->sensitivity;
+ hw_d[1] = hw_d[1] * acc->sensitivity;
+ hw_d[2] = hw_d[2] * acc->sensitivity;
+
+ xyz[0] = ((acc->pdata->negate_x) ? (-hw_d[acc->pdata->axis_map_x])
+ : (hw_d[acc->pdata->axis_map_x]));
+ xyz[1] = ((acc->pdata->negate_y) ? (-hw_d[acc->pdata->axis_map_y])
+ : (hw_d[acc->pdata->axis_map_y]));
+ xyz[2] = ((acc->pdata->negate_z) ? (-hw_d[acc->pdata->axis_map_z])
+ : (hw_d[acc->pdata->axis_map_z]));
+
+ pr_debug("%s read x=%d, y=%d, z=%d\n",
+ LIS3DH_ACC_DEV_NAME, xyz[0], xyz[1], xyz[2]);
+
+ return err;
+}
+
+static void lis3dh_acc_report_values(struct lis3dh_acc_data *acc, int *xyz)
+{
+ input_report_abs(acc->input_dev, ABS_X, xyz[0]);
+ input_report_abs(acc->input_dev, ABS_Y, xyz[1]);
+ input_report_abs(acc->input_dev, ABS_Z, xyz[2]);
+ input_sync(acc->input_dev);
+}
+
+static int lis3dh_acc_enable(struct lis3dh_acc_data *acc)
+{
+ int err;
+ printk("sprd-gsensor: -- %s -- !\n",__func__);
+ if (!atomic_cmpxchg(&acc->enabled, 0, 1)) {
+ err = lis3dh_acc_device_power_on(acc);
+ if (err < 0) {
+ atomic_set(&acc->enabled, 0);
+ return err;
+ }
+
+ if (acc->hw_initialized) {
+ if (acc->irq1 != 0)
+ enable_irq(acc->irq1);
+ if (acc->irq2 != 0)
+ enable_irq(acc->irq2);
+ pr_debug("%s: power on: irq enabled\n",
+ LIS3DH_ACC_DEV_NAME);
+ }
+
+ schedule_delayed_work(&acc->input_work,
+ msecs_to_jiffies(acc->pdata->
+ poll_interval));
+ }
+ printk("sprd-gsensor: -- %s -- success!\n",__func__);
+ return 0;
+}
+
+static int lis3dh_acc_disable(struct lis3dh_acc_data *acc)
+{
+ printk("sprd-gsensor: -- %s -- \n",__func__);
+ if (atomic_cmpxchg(&acc->enabled, 1, 0)) {
+ cancel_delayed_work_sync(&acc->input_work);
+ lis3dh_acc_device_power_off(acc);
+ }
+
+ return 0;
+}
+
+static int lis3dh_acc_misc_open(struct inode *inode, struct file *file)
+{
+ int err;
+ err = nonseekable_open(inode, file);
+ if (err < 0)
+ return err;
+
+ file->private_data = lis3dh_acc_misc_data;
+
+ return 0;
+}
+
+static long lis3dh_acc_misc_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ u8 buf[4];
+ u8 mask;
+ u8 reg_address;
+ u8 bit_values;
+ int err;
+ int interval;
+ int xyz[3] = { 0 };
+ struct lis3dh_acc_data *acc = file->private_data;
+
+ switch (cmd) {
+ case LIS3DH_ACC_IOCTL_GET_DELAY:
+ interval = acc->pdata->poll_interval;
+ if (copy_to_user(argp, &interval, sizeof(interval)))
+ return -EFAULT;
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_DELAY:
+ if (copy_from_user(&interval, argp, sizeof(interval)))
+ return -EFAULT;
+ if (interval < 0 || interval > 1000)
+ return -EINVAL;
+
+ acc->pdata->poll_interval = max(interval,
+ acc->pdata->min_interval);
+err = lis3dh_acc_update_odr(acc, 2);
+ /* TODO: if update fails poll is still set */
+ if (err < 0)
+ return err;
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_ENABLE:
+ if (copy_from_user(&interval, argp, sizeof(interval)))
+ return -EFAULT;
+ if (interval > 1)
+ return -EINVAL;
+ if (interval)
+ err = lis3dh_acc_enable(acc);
+ else
+ err = lis3dh_acc_disable(acc);
+ return err;
+ break;
+
+ case LIS3DH_ACC_IOCTL_GET_ENABLE:
+ interval = atomic_read(&acc->enabled);
+ if (copy_to_user(argp, &interval, sizeof(interval)))
+ return -EINVAL;
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_G_RANGE:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ bit_values = buf[0];
+ err = lis3dh_acc_update_g_range(acc, bit_values);
+ if (err < 0)
+ return err;
+ break;
+
+#ifdef INTERRUPT_MANAGEMENT
+ case LIS3DH_ACC_IOCTL_SET_CTRL_REG3:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = CTRL_REG3;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_CTRL_REG3] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_CTRL_REG3]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_CTRL_REG6:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = CTRL_REG6;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_CTRL_REG6] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_CTRL_REG6]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_DURATION1:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ reg_address = INT_DUR1;
+ mask = 0x7F;
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_DUR1] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_DUR1]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_THRESHOLD1:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ reg_address = INT_THS1;
+ mask = 0x7F;
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_THS1] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_THS1]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_CONFIG1:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = INT_CFG1;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_CFG1] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_CFG1]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_GET_SOURCE1:
+ err = lis3dh_acc_register_read(acc, buf, INT_SRC1);
+ if (err < 0)
+ return err;
+
+ pr_debug("INT1_SRC content: %d , 0x%x\n", buf[0], buf[0]);
+
+ if (copy_to_user(argp, buf, 1))
+ return -EINVAL;
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_DURATION2:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ reg_address = INT_DUR2;
+ mask = 0x7F;
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_DUR2] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_DUR2]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_THRESHOLD2:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ reg_address = INT_THS2;
+ mask = 0x7F;
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_THS2] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_THS2]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_CONFIG2:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = INT_CFG2;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_CFG2] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_CFG2]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_GET_SOURCE2:
+ err = lis3dh_acc_register_read(acc, buf, INT_SRC2);
+ if (err < 0)
+ return err;
+
+ pr_debug("INT2_SRC content: %d , 0x%x\n", buf[0], buf[0]);
+
+ if (copy_to_user(argp, buf, 1))
+ return -EINVAL;
+ break;
+
+ case LIS3DH_ACC_IOCTL_GET_TAP_SOURCE:
+ err = lis3dh_acc_register_read(acc, buf, TT_SRC);
+ if (err < 0)
+ return err;
+ pr_debug("TT_SRC content: %d , 0x%x\n", buf[0], buf[0]);
+
+ if (copy_to_user(argp, buf, 1)) {
+ pr_err("%s: %s error in copy_to_user \n",
+ LIS3DH_ACC_DEV_NAME, __func__);
+ return -EINVAL;
+ }
+ break;
+ case LIS3DH_ACC_IOCTL_GET_COOR_XYZ:
+ err = lis3dh_acc_get_acceleration_data(acc, xyz);
+ if (err < 0)
+ return err;
+
+ if (copy_to_user(argp, xyz, sizeof(xyz))) {
+ pr_err(" %s %d error in copy_to_user \n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+ case LIS3DH_ACC_IOCTL_SET_TAP_CFG:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_CFG;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_CFG] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_CFG]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_TAP_TLIM:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_LIM;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_LIM] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_LIM]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_TAP_THS:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_THS;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_THS] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_THS]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_TAP_TLAT:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_TLAT;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_TLAT] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_TLAT]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_TAP_TW:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_TW;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_TW] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_TW]));
+ break;
+
+#endif /* INTERRUPT_MANAGEMENT */
+ case LIS3DH_ACC_IOCTL_GET_CHIP_ID:
+ {
+ u8 devid = 0;
+ u8 devinfo[DEVICE_INFO_LEN] = {0};
+ err = lis3dh_acc_register_read(acc, &devid, WHO_AM_I);
+ if (err < 0) {
+ printk("__func__, error read register WHO_AM_I\n", __func__);
+ return -EAGAIN;
+ }
+ sprintf(devinfo, "%s, %#x", DEVICE_INFO, devid);
+
+ if (copy_to_user(argp, devinfo, sizeof(devinfo))) {
+ printk("%s error in copy_to_user(IOCTL_GET_CHIP_ID)\n", __func__);
+ return -EINVAL;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct file_operations lis3dh_acc_misc_fops = {
+ .owner = THIS_MODULE,
+ .open = lis3dh_acc_misc_open,
+ .unlocked_ioctl = lis3dh_acc_misc_ioctl,
+};
+
+static struct miscdevice lis3dh_acc_misc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = LIS3DH_ACC_DEV_NAME,
+ .fops = &lis3dh_acc_misc_fops,
+};
+
+static void lis3dh_acc_input_work_func(struct work_struct *work)
+{
+ struct lis3dh_acc_data *acc;
+
+ int xyz[3] = { 0 };
+ int err;
+
+ acc = container_of((struct delayed_work *)work,
+ struct lis3dh_acc_data, input_work);
+
+ mutex_lock(&acc->lock);
+ err = lis3dh_acc_get_acceleration_data(acc, xyz);
+ if (err < 0)
+ dev_err(&acc->client->dev, "get_acceleration_data failed\n");
+ else
+ lis3dh_acc_report_values(acc, xyz);
+
+ schedule_delayed_work(&acc->input_work,
+ msecs_to_jiffies(acc->pdata->poll_interval));
+ mutex_unlock(&acc->lock);
+}
+
+#ifdef LIS3DH_OPEN_ENABLE
+int lis3dh_acc_input_open(struct input_dev *input)
+{
+ struct lis3dh_acc_data *acc = input_get_drvdata(input);
+
+ return lis3dh_acc_enable(acc);
+}
+
+void lis3dh_acc_input_close(struct input_dev *dev)
+{
+ struct lis3dh_acc_data *acc = input_get_drvdata(dev);
+
+ lis3dh_acc_disable(acc);
+}
+#endif
+
+static int lis3dh_acc_validate_pdata(struct lis3dh_acc_data *acc)
+{
+ acc->pdata->poll_interval = max(acc->pdata->poll_interval,
+ acc->pdata->min_interval);
+
+ if (acc->pdata->axis_map_x > 2 || acc->pdata->axis_map_y > 2
+ || acc->pdata->axis_map_z > 2) {
+ dev_err(&acc->client->dev, "invalid axis_map value "
+ "x:%u y:%u z%u\n", acc->pdata->axis_map_x,
+ acc->pdata->axis_map_y, acc->pdata->axis_map_z);
+ return -EINVAL;
+ }
+
+ /* Only allow 0 and 1 for negation boolean flag */
+ if (acc->pdata->negate_x > 1 || acc->pdata->negate_y > 1
+ || acc->pdata->negate_z > 1) {
+ dev_err(&acc->client->dev, "invalid negate value "
+ "x:%u y:%u z:%u\n", acc->pdata->negate_x,
+ acc->pdata->negate_y, acc->pdata->negate_z);
+ return -EINVAL;
+ }
+
+ /* Enforce minimum polling interval */
+ if (acc->pdata->poll_interval < acc->pdata->min_interval) {
+ dev_err(&acc->client->dev, "minimum poll interval violated\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int lis3dh_acc_input_init(struct lis3dh_acc_data *acc)
+{
+ int err;
+ /* Polling rx data when the interrupt is not used.*/
+ if (1 /*acc->irq1 == 0 && acc->irq1 == 0 */ ) {
+ INIT_DELAYED_WORK(&acc->input_work, lis3dh_acc_input_work_func);
+ }
+
+ acc->input_dev = input_allocate_device();
+ if (!acc->input_dev) {
+ err = -ENOMEM;
+ dev_err(&acc->client->dev, "input device allocate failed\n");
+ goto err0;
+ }
+#ifdef LIS3DH_ACC_OPEN_ENABLE
+ acc->input_dev->open = lis3dh_acc_input_open;
+ acc->input_dev->close = lis3dh_acc_input_close;
+#endif
+
+ input_set_drvdata(acc->input_dev, acc);
+
+ set_bit(EV_ABS, acc->input_dev->evbit);
+ /* next is used for interruptA sources data if the case */
+ set_bit(ABS_MISC, acc->input_dev->absbit);
+ /* next is used for interruptB sources data if the case */
+ set_bit(ABS_WHEEL, acc->input_dev->absbit);
+
+ input_set_abs_params(acc->input_dev, ABS_X, -G_MAX, G_MAX, 0, 0);
+ input_set_abs_params(acc->input_dev, ABS_Y, -G_MAX, G_MAX, 0, 0);
+ input_set_abs_params(acc->input_dev, ABS_Z, -G_MAX, G_MAX, 0, 0);
+ /* next is used for interruptA sources data if the case */
+ input_set_abs_params(acc->input_dev, ABS_MISC, INT_MIN, INT_MAX, 0, 0);
+ /* next is used for interruptB sources data if the case */
+ input_set_abs_params(acc->input_dev, ABS_WHEEL, INT_MIN, INT_MAX, 0, 0);
+
+ acc->input_dev->name = "accelerometer";
+
+ err = input_register_device(acc->input_dev);
+ if (err) {
+ dev_err(&acc->client->dev,
+ "unable to register input polled device %s\n",
+ acc->input_dev->name);
+ goto err1;
+ }
+
+ return 0;
+
+err1:
+ input_free_device(acc->input_dev);
+err0:
+ return err;
+}
+
+static void lis3dh_acc_input_cleanup(struct lis3dh_acc_data *acc)
+{
+ input_unregister_device(acc->input_dev);
+ input_free_device(acc->input_dev);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void lis3dh_early_suspend(struct early_suspend *es);
+static void lis3dh_early_resume(struct early_suspend *es);
+#endif
+
+#ifdef CONFIG_OF
+static struct lis3dh_acc_platform_data *lis3dh_acc_parse_dt(struct device *dev)
+{
+ struct lis3dh_acc_platform_data *pdata;
+ struct device_node *np = dev->of_node;
+ int ret;
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "Could not allocate struct lis3dh_acc_platform_data");
+ return NULL;
+ }
+ ret = of_property_read_u32(np, "poll_interval", &pdata->poll_interval);
+ if(ret){
+ dev_err(dev, "fail to get poll_interval\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "min_interval", &pdata->min_interval);
+ if(ret){
+ dev_err(dev, "fail to get min_interval\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "g_range", &pdata->g_range);
+ if(ret){
+ dev_err(dev, "fail to get g_range\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "axis_map_x", &pdata->axis_map_x);
+ if(ret){
+ dev_err(dev, "fail to get axis_map_x\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "axis_map_y", &pdata->axis_map_y);
+ if(ret){
+ dev_err(dev, "fail to get axis_map_y\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "axis_map_z", &pdata->axis_map_z);
+ if(ret){
+ dev_err(dev, "fail to get axis_map_z\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "negate_x", &pdata->negate_x);
+ if(ret){
+ dev_err(dev, "fail to get negate_x\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "negate_y", &pdata->negate_y);
+ if(ret){
+ dev_err(dev, "fail to get negate_y\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "negate_z", &pdata->negate_z);
+ if(ret){
+ dev_err(dev, "fail to get negate_z\n");
+ goto fail;
+ }
+ return pdata;
+fail:
+ kfree(pdata);
+ return NULL;
+}
+#endif
+static int lis3dh_acc_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+
+ struct lis3dh_acc_data *acc;
+ struct lis3dh_acc_platform_data *pdata = client->dev.platform_data;
+
+ int err = -1;
+ int tempvalue;
+ printk("sprd-gsensor: -- %s -- start !\n",__func__);
+ pr_debug("%s: probe start.\n", LIS3DH_ACC_DEV_NAME);
+
+#ifdef CONFIG_OF
+ struct device_node *np = client->dev.of_node;
+ if (np && !pdata){
+ pdata = lis3dh_acc_parse_dt(&client->dev);
+ if(pdata){
+ client->dev.platform_data = pdata;
+ }
+ if(!pdata){
+ err = -ENOMEM;
+ goto exit_alloc_platform_data_failed;
+ }
+ }
+#endif
+ /*
+ if (client->dev.platform_data == NULL) {
+ dev_err(&client->dev, "platform data is NULL. exiting.\n");
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+ */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "client not i2c capable\n");
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_err(&client->dev, "client not smb-i2c capable:2\n");
+ err = -EIO;
+ goto exit_check_functionality_failed;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ dev_err(&client->dev, "client not smb-i2c capable:3\n");
+ err = -EIO;
+ goto exit_check_functionality_failed;
+ }
+ /*
+ * OK. From now, we presume we have a valid client. We now create the
+ * client structure, even though we cannot fill it completely yet.
+ */
+
+ acc = kzalloc(sizeof(struct lis3dh_acc_data), GFP_KERNEL);
+ if (acc == NULL) {
+ err = -ENOMEM;
+ dev_err(&client->dev,
+ "failed to allocate memory for module data: "
+ "%d\n", err);
+ goto exit_alloc_data_failed;
+ }
+
+ mutex_init(&acc->lock);
+ mutex_lock(&acc->lock);
+
+ acc->client = client;
+ lis3dh_i2c_client = client;
+ i2c_set_clientdata(client, acc);
+
+ pr_debug("%s: %s has set irq1 to irq: %d\n",
+ LIS3DH_ACC_DEV_NAME, __func__, acc->irq1);
+ pr_debug("%s: %s has set irq2 to irq: %d\n",
+ LIS3DH_ACC_DEV_NAME, __func__, acc->irq2);
+
+ gpio_request(GSENSOR_GINT1_GPI, "GSENSOR_INT1");
+ gpio_request(GSENSOR_GINT2_GPI, "GSENSOR_INT2");
+ acc->irq1 = 0; /* gpio_to_irq(GSENSOR_GINT1_GPI); */
+ acc->irq2 = 0; /* gpio_to_irq(GSENSOR_GINT2_GPI); */
+
+ if (acc->irq1 != 0) {
+ pr_debug("%s request irq1\n", __func__);
+ err =
+ request_irq(acc->irq1, lis3dh_acc_isr1, IRQF_TRIGGER_RISING,
+ "lis3dh_acc_irq1", acc);
+ if (err < 0) {
+ dev_err(&client->dev, "request irq1 failed: %d\n", err);
+ goto err_mutexunlockfreedata;
+ }
+ disable_irq_nosync(acc->irq1);
+
+ INIT_WORK(&acc->irq1_work, lis3dh_acc_irq1_work_func);
+ acc->irq1_work_queue =
+ create_singlethread_workqueue("lis3dh_acc_wq1");
+ if (!acc->irq1_work_queue) {
+ err = -ENOMEM;
+ dev_err(&client->dev, "cannot create work queue1: %d\n",
+ err);
+ goto err_free_irq1;
+ }
+ }
+
+ if (acc->irq2 != 0) {
+ err =
+ request_irq(acc->irq2, lis3dh_acc_isr2, IRQF_TRIGGER_RISING,
+ "lis3dh_acc_irq2", acc);
+ if (err < 0) {
+ dev_err(&client->dev, "request irq2 failed: %d\n", err);
+ goto err_destoyworkqueue1;
+ }
+ disable_irq_nosync(acc->irq2);
+
+/* Create workqueue for IRQ.*/
+
+ INIT_WORK(&acc->irq2_work, lis3dh_acc_irq2_work_func);
+ acc->irq2_work_queue =
+ create_singlethread_workqueue("lis3dh_acc_wq2");
+ if (!acc->irq2_work_queue) {
+ err = -ENOMEM;
+ dev_err(&client->dev, "cannot create work queue2: %d\n",
+ err);
+ goto err_free_irq2;
+ }
+ }
+
+ acc->pdata = kmalloc(sizeof(*acc->pdata), GFP_KERNEL);
+ if (acc->pdata == NULL) {
+ err = -ENOMEM;
+ dev_err(&client->dev,
+ "failed to allocate memory for pdata: %d\n", err);
+ goto err_destoyworkqueue2;
+ }
+
+ memcpy(acc->pdata, client->dev.platform_data, sizeof(*acc->pdata));
+
+ err = lis3dh_acc_validate_pdata(acc);
+ if (err < 0) {
+ dev_err(&client->dev, "failed to validate platform data\n");
+ goto exit_kfree_pdata;
+ }
+
+ err = lis3dh_acc_device_power_on(acc);
+ if (err < 0) {
+ dev_err(&client->dev, "power on failed: %d\n", err);
+ goto err2;
+ }
+
+#if 1
+ if (i2c_smbus_read_byte(client) < 0) {
+ pr_err("i2c_smbus_read_byte error!!\n");
+ goto err_destoyworkqueue2;
+ } else {
+ pr_debug("%s Device detected!\n", LIS3DH_ACC_DEV_NAME);
+ }
+#endif
+ /* read chip id */
+
+ tempvalue = i2c_smbus_read_word_data(client, WHO_AM_I);
+
+ if ((tempvalue & 0x00FF) == WHOAMI_LIS3DH_ACC) {
+ pr_debug("%s I2C driver registered!\n",
+ LIS3DH_ACC_DEV_NAME);
+ } else {
+ acc->client = NULL;
+ pr_debug("I2C driver not registered!"
+ " Device unknown 0x%x\n", tempvalue);
+ goto err_destoyworkqueue2;
+ }
+
+ i2c_set_clientdata(client, acc);
+
+ if (acc->pdata->init) {
+ err = acc->pdata->init();
+ if (err < 0) {
+ dev_err(&client->dev, "init failed: %d\n", err);
+ goto err2;
+ }
+ }
+
+ memset(acc->resume_state, 0, ARRAY_SIZE(acc->resume_state));
+
+ acc->resume_state[RES_CTRL_REG1] = LIS3DH_ACC_ENABLE_ALL_AXES;
+ acc->resume_state[RES_CTRL_REG2] = 0x00;
+ acc->resume_state[RES_CTRL_REG3] = 0x00;
+ acc->resume_state[RES_CTRL_REG4] = 0x00;
+ acc->resume_state[RES_CTRL_REG5] = 0x00;
+ acc->resume_state[RES_CTRL_REG6] = 0x00;
+
+ acc->resume_state[RES_TEMP_CFG_REG] = 0x00;
+ acc->resume_state[RES_FIFO_CTRL_REG] = 0x00;
+ acc->resume_state[RES_INT_CFG1] = 0x00;
+ acc->resume_state[RES_INT_THS1] = 0x00;
+ acc->resume_state[RES_INT_DUR1] = 0x00;
+ acc->resume_state[RES_INT_CFG2] = 0x00;
+ acc->resume_state[RES_INT_THS2] = 0x00;
+ acc->resume_state[RES_INT_DUR2] = 0x00;
+
+ acc->resume_state[RES_TT_CFG] = 0x00;
+ acc->resume_state[RES_TT_THS] = 0x00;
+ acc->resume_state[RES_TT_LIM] = 0x00;
+ acc->resume_state[RES_TT_TLAT] = 0x00;
+ acc->resume_state[RES_TT_TW] = 0x00;
+
+
+ atomic_set(&acc->enabled, 1);
+
+ err = lis3dh_acc_update_g_range(acc, acc->pdata->g_range);
+ if (err < 0) {
+ dev_err(&client->dev, "update_g_range failed\n");
+ goto err_power_off;
+ }
+
+ err = lis3dh_acc_update_odr(acc, acc->pdata->poll_interval);
+ if (err < 0) {
+ dev_err(&client->dev, "update_odr failed\n");
+ goto err_power_off;
+ }
+
+ err = lis3dh_acc_input_init(acc);
+ if (err < 0) {
+ dev_err(&client->dev, "input init failed\n");
+ goto err_power_off;
+ }
+ lis3dh_acc_misc_data = acc;
+
+ err = misc_register(&lis3dh_acc_misc_device);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "misc LIS3DH_ACC_DEV_NAME register failed\n");
+ goto err_input_cleanup;
+ }
+
+ lis3dh_acc_device_power_off(acc);
+
+ /* As default, do not report information */
+ atomic_set(&acc->enabled, 0);
+/*
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ acc->early_suspend.suspend = lis3dh_early_suspend;
+ acc->early_suspend.resume = lis3dh_early_resume;
+ acc->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
+ register_early_suspend(&acc->early_suspend);
+#endif
+*/
+ mutex_unlock(&acc->lock);
+ dev_info(&client->dev, "###%s###\n", __func__);
+ printk("sprd-gsensor: -- %s -- success !\n",__func__);
+ return 0;
+
+err_input_cleanup:
+ lis3dh_acc_input_cleanup(acc);
+err_power_off:
+ lis3dh_acc_device_power_off(acc);
+err2:
+ if (acc->pdata->exit)
+ acc->pdata->exit();
+exit_kfree_pdata:
+ kfree(acc->pdata);
+err_destoyworkqueue2:
+ if (acc->irq2_work_queue)
+ destroy_workqueue(acc->irq2_work_queue);
+err_free_irq2:
+ if (acc->irq2) {
+ free_irq(acc->irq2, acc);
+ gpio_free(GSENSOR_GINT2_GPI);
+ }
+err_destoyworkqueue1:
+ if (acc->irq1_work_queue)
+ destroy_workqueue(acc->irq1_work_queue);
+err_free_irq1:
+ if (acc->irq1) {
+ free_irq(acc->irq1, acc);
+ gpio_free(GSENSOR_GINT1_GPI);
+ }
+err_mutexunlockfreedata:
+ i2c_set_clientdata(client, NULL);
+ mutex_unlock(&acc->lock);
+ kfree(acc);
+ lis3dh_acc_misc_data = NULL;
+exit_alloc_data_failed:
+exit_check_functionality_failed:
+ pr_err("%s: Driver Init failed\n", LIS3DH_ACC_DEV_NAME);
+exit_alloc_platform_data_failed:
+ return err;
+}
+
+static int lis3dh_acc_remove(struct i2c_client *client)
+{
+ /* TODO: revisit ordering here once _probe order is finalized */
+ struct lis3dh_acc_data *acc = i2c_get_clientdata(client);
+ if (acc != NULL) {
+ if (acc->irq1) {
+ free_irq(acc->irq1, acc);
+ gpio_free(GSENSOR_GINT1_GPI);
+ }
+ if (acc->irq2) {
+ free_irq(acc->irq2, acc);
+ gpio_free(GSENSOR_GINT2_GPI);
+ }
+
+ if (acc->irq1_work_queue)
+ destroy_workqueue(acc->irq1_work_queue);
+ if (acc->irq2_work_queue)
+ destroy_workqueue(acc->irq2_work_queue);
+ misc_deregister(&lis3dh_acc_misc_device);
+ lis3dh_acc_input_cleanup(acc);
+ lis3dh_acc_device_power_off(acc);
+ if (acc->pdata->exit)
+ acc->pdata->exit();
+ kfree(acc->pdata);
+ kfree(acc);
+ }
+
+ return 0;
+}
+
+static int lis3dh_acc_resume(struct i2c_client *client)
+{
+ struct lis3dh_acc_data *acc = i2c_get_clientdata(client);
+ printk("sprd-gsensor: -- %s -- !\n",__func__);
+ dev_dbg(&client->dev, "###%s###\n", __func__);
+
+ if (acc != NULL && acc->on_before_suspend)
+ return lis3dh_acc_enable(acc);
+
+ return 0;
+}
+
+static int lis3dh_acc_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct lis3dh_acc_data *acc = i2c_get_clientdata(client);
+ printk("sprd-gsensor: -- %s -- !\n",__func__);
+ dev_dbg(&client->dev, "###%s###\n", __func__);
+
+ if (acc != NULL) {
+ acc->on_before_suspend = atomic_read(&acc->enabled);
+ return lis3dh_acc_disable(acc);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+
+static void lis3dh_early_suspend(struct early_suspend *es)
+{
+ lis3dh_acc_suspend(lis3dh_i2c_client, (pm_message_t) {
+ .event = 0});
+}
+
+static void lis3dh_early_resume(struct early_suspend *es)
+{
+ lis3dh_acc_resume(lis3dh_i2c_client);
+}
+
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+
+static const struct i2c_device_id lis3dh_acc_id[]
+= { {LIS3DH_ACC_DEV_NAME, 0}, {}, };
+
+MODULE_DEVICE_TABLE(i2c, lis3dh_acc_id);
+
+static const struct of_device_id lis3dh_acc_of_match[] = {
+ { .compatible = "ST,lis3dh_acc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lis3dh_acc_of_match);
+static struct i2c_driver lis3dh_acc_driver = {
+ .driver = {
+ .name = LIS3DH_ACC_I2C_NAME,
+ .of_match_table = lis3dh_acc_of_match,
+ },
+ .probe = lis3dh_acc_probe,
+ .remove = lis3dh_acc_remove,
+ .resume = lis3dh_acc_resume,
+ .suspend = lis3dh_acc_suspend,
+ .id_table = lis3dh_acc_id,
+};
+
+
+static int __init lis3dh_acc_init(void)
+{
+ pr_debug("%s accelerometer driver: init\n", LIS3DH_ACC_I2C_NAME);
+
+ return i2c_add_driver(&lis3dh_acc_driver);
+}
+
+static void __exit lis3dh_acc_exit(void)
+{
+ pr_debug("%s accelerometer driver exit\n", LIS3DH_ACC_DEV_NAME);
+
+ i2c_del_driver(&lis3dh_acc_driver);
+ return;
+}
+
+module_init(lis3dh_acc_init);
+module_exit(lis3dh_acc_exit);
+
+MODULE_DESCRIPTION("lis3dh accelerometer misc driver");
+MODULE_AUTHOR("STMicroelectronics");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/lis3dh_interrupt.c b/drivers/input/misc/lis3dh_interrupt.c
new file mode 100644
index 00000000..ba801834
--- /dev/null
+++ b/drivers/input/misc/lis3dh_interrupt.c
@@ -0,0 +1,2261 @@
+/******************** (C) COPYRIGHT 2010 STMicroelectronics ********************
+ *
+ * File Name : lis3dh_acc.c
+ * Authors : MSH - Motion Mems BU - Application Team
+ * : Carmine Iascone (carmine.iascone@st.com)
+ * : Matteo Dameno (matteo.dameno@st.com)
+ * Version : V.1.0.5
+ * Date : 16/08/2010
+ * Description : LIS3DH accelerometer 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.
+ *
+ * THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS.
+ *
+ ******************************************************************************
+ Revision 1.0.0 05/11/09
+ First Release
+ Revision 1.0.3 22/01/2010
+ Linux K&R Compliant Release;
+ Revision 1.0.5 16/08/2010
+ modified _get_acceleration_data function
+ modified _update_odr function
+ manages 2 interrupts
+
+ ******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include <linux/i2c/lis3dh.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+
+#include <soc/sprd/i2c-sprd.h>
+
+#define INTERRUPT_MANAGEMENT 1
+
+#define G_MAX 16000 /** Maximum polled-device-reported g value */
+
+/*
+#define SHIFT_ADJ_2G 4
+#define SHIFT_ADJ_4G 3
+#define SHIFT_ADJ_8G 2
+#define SHIFT_ADJ_16G 1
+*/
+#define GSENSOR_INTERRUPT_MODE
+
+#define SENSITIVITY_2G 1 /** mg/LSB */
+#define SENSITIVITY_4G 2 /** mg/LSB */
+#define SENSITIVITY_8G 4 /** mg/LSB */
+#define SENSITIVITY_16G 12 /** mg/LSB */
+
+#define HIGH_RESOLUTION 0x08
+
+#define AXISDATA_REG 0x28
+#define WHOAMI_LIS3DH_ACC 0x33 /* Expctd content for WAI */
+
+/* CONTROL REGISTERS */
+#define WHO_AM_I 0x0F /* WhoAmI register */
+#define TEMP_CFG_REG 0x1F /* temper sens control reg */
+/* ctrl 1: ODR3 ODR2 ODR ODR0 LPen Zenable Yenable Zenable */
+#define CTRL_REG1 0x20 /* control reg 1 */
+#define CTRL_REG2 0x21 /* control reg 2 */
+#define CTRL_REG3 0x22 /* control reg 3 */
+#define CTRL_REG4 0x23 /* control reg 4 */
+#define CTRL_REG5 0x24 /* control reg 5 */
+#define CTRL_REG6 0x25 /* control reg 6 */
+
+#define FIFO_CTRL_REG 0x2E /* FiFo control reg */
+
+#define INT_CFG1 0x30 /* interrupt 1 config */
+#define INT_SRC1 0x31 /* interrupt 1 source */
+#define INT_THS1 0x32 /* interrupt 1 threshold */
+#define INT_DUR1 0x33 /* interrupt 1 duration */
+
+#define INT_CFG2 0x34 /* interrupt 2 config */
+#define INT_SRC2 0x35 /* interrupt 2 source */
+#define INT_THS2 0x36 /* interrupt 2 threshold */
+#define INT_DUR2 0x37 /* interrupt 2 duration */
+
+#define TT_CFG 0x38 /* tap config */
+#define TT_SRC 0x39 /* tap source */
+#define TT_THS 0x3A /* tap threshold */
+#define TT_LIM 0x3B /* tap time limit */
+#define TT_TLAT 0x3C /* tap time latency */
+#define TT_TW 0x3D /* tap time window */
+/* end CONTROL REGISTRES */
+
+#define ENABLE_HIGH_RESOLUTION 1
+
+#define LIS3DH_ACC_PM_OFF 0x00
+#define LIS3DH_ACC_ENABLE_ALL_AXES 0x07
+
+#define PMODE_MASK 0x08
+#define ODR_MASK 0XF0
+
+#define ODR1 0x10 /* 1Hz output data rate */
+#define ODR10 0x20 /* 10Hz output data rate */
+#define ODR25 0x30 /* 25Hz output data rate */
+#define ODR50 0x40 /* 50Hz output data rate */
+#define ODR100 0x50 /* 100Hz output data rate */
+#define ODR200 0x60 /* 200Hz output data rate */
+#define ODR400 0x70 /* 400Hz output data rate */
+#define ODR1250 0x90 /* 1250Hz output data rate : test actually 650hz*/
+
+#define IA 0x40
+#define ZH 0x20
+#define ZL 0x10
+#define YH 0x08
+#define YL 0x04
+#define XH 0x02
+#define XL 0x01
+/* */
+/* CTRL REG BITS*/
+#define CTRL_REG3_I1_AOI1 0x40
+#define CTRL_REG6_I2_TAPEN 0x80
+/*
+CTRL_REG3 (22h) µÄI1_DRDY1 λ enable
+CTRL_REG5 (24h) LIR_INT1 1 Ϊ ´¥·¢ÖÐ¶Ï ºó Öжϱ£³Ö£¬0 Ϊ Âö³å
+CTRL_REG6 (25h) 0 ΪÖÐ¶Ï ¸ßÓÐЧ£¬ 1 ΪÖжϵÍÓÐЧ
+********must read data then will interrupter,******
+*/
+#define CTRL_REG3_I1_DRDY1 0x10 //DRDY1 interrupt on INT1
+#define CTRL_REG5_LIR_INT1 0x08 //latch interrupt
+//#define CTRL_REG6_HLACTIVE 0x02 //low
+#define CTRL_REG6_HLACTIVE 0x00 //high
+
+
+/* */
+
+/* TAP_SOURCE_REG BIT */
+#define DTAP 0x20
+#define STAP 0x10
+#define SIGNTAP 0x08
+#define ZTAP 0x04
+#define YTAP 0x02
+#define XTAZ 0x01
+
+#define FUZZ 32
+#define FLAT 32
+#define I2C_RETRY_DELAY 5
+#define I2C_RETRIES 5
+#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_CTRL_REG6 5
+
+#define RES_INT_CFG1 6
+#define RES_INT_THS1 7
+#define RES_INT_DUR1 8
+#define RES_INT_CFG2 9
+#define RES_INT_THS2 10
+#define RES_INT_DUR2 11
+
+#define RES_TT_CFG 12
+#define RES_TT_THS 13
+#define RES_TT_LIM 14
+#define RES_TT_TLAT 15
+#define RES_TT_TW 16
+
+#define RES_TEMP_CFG_REG 17
+#define RES_REFERENCE_REG 18
+#define RES_FIFO_CTRL_REG 19
+
+#define RESUME_ENTRIES 20
+#define DEVICE_INFO "ST, LIS3DH"
+#define DEVICE_INFO_LEN 32
+/* end RESUME STATE INDICES */
+
+//#define GSENSOR_GINT1_GPI 238
+#define GSENSOR_GINT2_GPI 1
+
+#define LOG_TAG "[LIS3DH] "
+#define LOG_FUN( ) printk(KERN_INFO LOG_TAG"%s\n", __FUNCTION__)
+#define LOG_INFO(fmt, args...) printk(KERN_INFO LOG_TAG fmt, ##args)
+#define LOG_ERR(fmt, args...) printk(KERN_ERR LOG_TAG"%s %d : "fmt, __FUNCTION__, __LINE__, ##args)
+
+//#define LIS3DH_DBG
+#define GSENSOR_INTERRUPT_MODE
+#define USE_WAIT_QUEUE 1
+#if USE_WAIT_QUEUE
+static struct task_struct *thread = NULL;
+static DECLARE_WAIT_QUEUE_HEAD(waiter);
+static int gsensor_flag = 0;
+#endif
+
+struct {
+ unsigned int cutoff_ms;
+ unsigned int mask;
+} lis3dh_acc_odr_table[] = {
+ {
+ 1, ODR1250}, {
+ 2, ODR400}, {
+ 5, ODR200}, {
+ 10, ODR100}, {
+ 20, ODR50}, {
+ 40, ODR25}, {
+ 100, ODR10}, {
+ 1000, ODR1},};
+
+struct lis3dh_acc_data {
+ struct i2c_client *client;
+ struct lis3dh_acc_platform_data *pdata;
+
+ struct mutex lock;
+ struct delayed_work input_work;
+
+ struct input_dev *input_dev;
+
+ int hw_initialized;
+ /* hw_working=-1 means not tested yet */
+ int hw_working;
+ atomic_t enabled;
+ int on_before_suspend;
+
+ u8 sensitivity;
+
+ u8 resume_state[RESUME_ENTRIES];
+
+// int irq1_gpio_number;
+ 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;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+#ifdef LIS3DH_DBG
+struct {
+ u8 diag_reg_addr_wr;
+ u8 diag_reg_val_wr;
+ } debug;
+ u64 data_num;
+#endif
+};
+
+/*
+ * Because misc devices can not carry a pointer from driver register to
+ * open, we keep this global. This limits the driver to a single instance.
+ */
+struct lis3dh_acc_data *lis3dh_acc_misc_data;
+struct i2c_client *lis3dh_i2c_client;
+
+#ifndef GSENSOR_INTERRUPT_MODE
+static struct workqueue_struct *_gSensorWork_workqueue = NULL;
+#endif
+
+static int lis3dh_acc_get_acceleration_data(struct lis3dh_acc_data *acc,int *xyz);
+static void lis3dh_acc_report_values(struct lis3dh_acc_data *acc, int *xyz);
+
+static int lis3dh_acc_i2c_read(struct lis3dh_acc_data *acc, u8 * buf, int len)
+{
+ int err;
+ int tries = 0;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = acc->client->addr,
+ .flags = acc->client->flags & I2C_M_TEN,
+ .len = 1,
+ .buf = buf,},
+ {
+ .addr = acc->client->addr,
+ .flags = (acc->client->flags & I2C_M_TEN) | I2C_M_RD,
+ .len = len,
+ .buf = buf,},
+ };
+
+ do {
+ err = i2c_transfer(acc->client->adapter, msgs, 2);
+ if (err != 2){
+ LOG_ERR("************* exception read transfer error,tries=%d********\n",tries);
+ msleep_interruptible(I2C_RETRY_DELAY);
+ }
+ } while ((err != 2) && (++tries < I2C_RETRIES));
+
+ if (err != 2) {
+ LOG_ERR(" exception read transfer error\n");
+ err = -EIO;
+ } else {
+ err = 0;
+ }
+
+ return err;
+}
+
+static int lis3dh_acc_i2c_write(struct lis3dh_acc_data *acc, u8 * buf, int len)
+{
+ int err;
+ int tries = 0;
+
+ struct i2c_msg msgs[] = { {.addr = acc->client->addr,
+ .flags = acc->client->flags & I2C_M_TEN,
+ .len = len + 1,.buf = buf,},
+ };
+ do {
+ err = i2c_transfer(acc->client->adapter, msgs, 1);
+ if (err != 1)
+ msleep_interruptible(I2C_RETRY_DELAY);
+ } while ((err != 1) && (++tries < I2C_RETRIES));
+
+ if (err != 1) {
+ dev_err(&acc->client->dev, "write transfer error\n");
+ err = -EIO;
+ } else {
+ err = 0;
+ }
+
+ return err;
+}
+
+
+static int lis3dh_acc_register_read(struct lis3dh_acc_data *acc, u8 * buf,
+ u8 reg_address)
+{
+ int err = -1;
+ buf[0] = (reg_address);
+ err = lis3dh_acc_i2c_read(acc, buf, 1);
+ return err;
+}
+
+static void lis3dh_read_interrupt_reg(struct lis3dh_acc_data *acc)
+{
+ u8 value = 0;
+ int err;
+
+ err = lis3dh_acc_register_read(acc, &value, CTRL_REG3);
+ LOG_INFO("REG content: =======================>\n");
+ if (err >= 0)
+ LOG_INFO("REG content: [CTRL_REG3:0x%x]= 0x%02x\n", CTRL_REG3, value);
+
+ err = lis3dh_acc_register_read(acc, &value, CTRL_REG5);
+ if (err >= 0)
+ LOG_INFO("REG content: [CTRL_REG5:0x%x]= 0x%02x\n", CTRL_REG5, value);
+ err = lis3dh_acc_register_read(acc, &value, CTRL_REG6);
+ if (err >= 0)
+ LOG_INFO("REG content: [CTRL_REG6:0x%x]= 0x%02x\n", CTRL_REG6, value);
+
+ LOG_INFO("REG content:< =======================\n");
+
+}
+
+
+
+/* if CTM want to turn off vdd & vddio , we recommend to call this function to retrive register setting */
+
+static int lis3dh_acc_hw_init(struct lis3dh_acc_data *acc)
+{
+ int err = -1;
+ u8 buf[7];
+
+ pr_debug("%s: hw init start\n", LIS3DH_ACC_DEV_NAME);
+ LOG_INFO("sprd-gsensor: -- %s entry :acc-> pdata ->gpio_int1=%d-- !\n",__func__,gpio_get_value(acc-> pdata ->gpio_int1));
+
+ buf[0] = WHO_AM_I;
+ err = lis3dh_acc_i2c_read(acc, buf, 1);
+ if (err < 0)
+ goto error_firstread;
+ else
+ acc->hw_working = 1;
+ if (buf[0] != WHOAMI_LIS3DH_ACC) {
+ err = -1; /* choose the right coded error */
+ goto error_unknown_device;
+ }
+
+ buf[0] = CTRL_REG1;
+ buf[1] = acc->resume_state[RES_CTRL_REG1];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = TEMP_CFG_REG;
+ buf[1] = acc->resume_state[RES_TEMP_CFG_REG];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = FIFO_CTRL_REG;
+ buf[1] = acc->resume_state[RES_FIFO_CTRL_REG];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = (I2C_AUTO_INCREMENT | TT_THS);
+ buf[1] = acc->resume_state[RES_TT_THS];
+ buf[2] = acc->resume_state[RES_TT_LIM];
+ buf[3] = acc->resume_state[RES_TT_TLAT];
+ buf[4] = acc->resume_state[RES_TT_TW];
+ err = lis3dh_acc_i2c_write(acc, buf, 4);
+ if (err < 0)
+ goto error1;
+ buf[0] = TT_CFG;
+ buf[1] = acc->resume_state[RES_TT_CFG];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = (I2C_AUTO_INCREMENT | INT_THS1);
+ buf[1] = acc->resume_state[RES_INT_THS1];
+ buf[2] = acc->resume_state[RES_INT_DUR1];
+ err = lis3dh_acc_i2c_write(acc, buf, 2);
+ if (err < 0)
+ goto error1;
+ buf[0] = INT_CFG1;
+ buf[1] = acc->resume_state[RES_INT_CFG1];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = (I2C_AUTO_INCREMENT | INT_THS2);
+ buf[1] = acc->resume_state[RES_INT_THS2];
+ buf[2] = acc->resume_state[RES_INT_DUR2];
+ err = lis3dh_acc_i2c_write(acc, buf, 2);
+ if (err < 0)
+ goto error1;
+ buf[0] = INT_CFG2;
+ buf[1] = acc->resume_state[RES_INT_CFG2];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = (I2C_AUTO_INCREMENT | CTRL_REG2);
+ buf[1] = acc->resume_state[RES_CTRL_REG2];
+ buf[2] = acc->resume_state[RES_CTRL_REG3];
+ buf[3] = acc->resume_state[RES_CTRL_REG4];
+ buf[4] = acc->resume_state[RES_CTRL_REG5];
+ buf[5] = acc->resume_state[RES_CTRL_REG6];
+ err = lis3dh_acc_i2c_write(acc, buf, 5);
+ if (err < 0)
+ goto error1;
+
+ acc->hw_initialized = 1;
+
+ LOG_INFO("sprd-gsensor: -- %s exit :acc-> pdata ->gpio_int1=%d-- !\n",__func__,gpio_get_value(acc-> pdata ->gpio_int1));
+
+ pr_debug("%s: hw init done\n", LIS3DH_ACC_DEV_NAME);
+ return 0;
+
+error_firstread:
+ acc->hw_working = 0;
+ dev_warn(&acc->client->dev, "Error reading WHO_AM_I: is device "
+ "available/working?\n");
+ goto error1;
+error_unknown_device:
+ dev_err(&acc->client->dev,
+ "device unknown. Expected: 0x%x,"
+ " Replies: 0x%x\n", WHOAMI_LIS3DH_ACC, buf[0]);
+error1:
+ acc->hw_initialized = 0;
+ dev_err(&acc->client->dev, "hw init error 0x%x,0x%x: %d\n", buf[0],
+ buf[1], err);
+ return err;
+}
+
+static void lis3dh_acc_device_power_off(struct lis3dh_acc_data *acc)
+{
+ int err;
+ u8 buf[2] = { CTRL_REG1, LIS3DH_ACC_PM_OFF };
+ int xyz[3] = { 0 };
+
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ dev_err(&acc->client->dev, "soft power off failed: %d\n", err);
+#ifdef GSENSOR_INTERRUPT_MODE
+/*
+read data:clear interrupter
+*/
+ err = lis3dh_acc_get_acceleration_data(acc, xyz);
+#endif
+ LOG_INFO("sprd-gsensor: -- %s acc-> pdata ->gpio_int1=%d acc->hw_initialized=%d-- !\n",__func__,gpio_get_value(acc-> pdata ->gpio_int1),acc->hw_initialized);
+
+}
+
+static int lis3dh_acc_device_power_on(struct lis3dh_acc_data *acc)
+{
+ int err = -1;
+ u8 buf[2];
+
+
+ if (acc->pdata->power_on) {
+ err = acc->pdata->power_on();
+ if (err < 0) {
+ dev_err(&acc->client->dev,
+ "power_on failed: %d\n", err);
+ return err;
+ }
+ }
+
+ LOG_INFO("sprd-gsensor: -- %s acc->irq1_gpio_number=%d acc->hw_initialized=%d-- !\n",__func__,gpio_get_value(acc-> pdata ->gpio_int1),acc->hw_initialized);
+ buf[0] = CTRL_REG1;
+ buf[1] = acc->resume_state[RES_CTRL_REG1];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ return err;
+ LOG_INFO("sprd-gsensor: -- %s acc->resume_state[RES_CTRL_REG1]=0x%02x-- !\n",__func__, acc->resume_state[RES_CTRL_REG1]);
+
+
+ if (!acc->hw_initialized) {
+ err = lis3dh_acc_hw_init(acc);
+ lis3dh_read_interrupt_reg(acc);
+ if (acc->hw_working == 1 && err < 0) {
+ lis3dh_acc_device_power_off(acc);
+ return err;
+ }
+ }
+ return 0;
+}
+
+static irqreturn_t lis3dh_acc_isr1(int irq, void *dev)
+{
+#ifndef USE_WAIT_QUEUE
+ struct lis3dh_acc_data *acc = dev;
+#endif
+
+#ifdef LIS3DH_DBG
+ static int count_intr = 0;
+ static s64 interrupt_last = 0;
+ static s64 interrupt_cur = 0;
+ static int poll_interval=0;
+ ktime_t time_mono;
+
+ time_mono = ktime_get();
+ if (poll_interval !=lis3dh_acc_misc_data->pdata->poll_interval) {
+ count_intr=1;
+ poll_interval = lis3dh_acc_misc_data->pdata->poll_interval;
+ interrupt_last = ktime_to_ns(time_mono);
+ }
+ interrupt_cur = ktime_to_ns(time_mono);
+ int interrupt_interval = interrupt_cur - interrupt_last;
+ interrupt_last = interrupt_cur;
+
+ if(interrupt_interval > (lis3dh_acc_misc_data->pdata->poll_interval*1000000) +(lis3dh_acc_misc_data->pdata->poll_interval*1000000) /4)
+ LOG_INFO("***sprd-gsensor ISR exception:count_intr=%d,interrupt_interval=%d,poll_interva=%d********\n",count_intr++,interrupt_interval,lis3dh_acc_misc_data->pdata->poll_interval);
+ LOG_INFO("sprd-gsensor: -- %s entry : interrupt_interval=%d,poll_interval=%d,acc->irq1_gpio_number=%d-- >>>>>>>>>>>>>>>>>!\n",__func__,interrupt_interval,poll_interval,gpio_get_value(lis3dh_acc_misc_data->pdata->gpio_int1));
+#endif
+
+ disable_irq_nosync(irq);
+#if USE_WAIT_QUEUE
+ gsensor_flag = 1;
+ wake_up_interruptible(&waiter);
+#else
+ queue_work(acc->irq1_work_queue, &acc->irq1_work);
+#endif
+ pr_debug("%s: isr1 queued\n", LIS3DH_ACC_DEV_NAME);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t lis3dh_acc_isr2(int irq, void *dev)
+{
+ struct lis3dh_acc_data *acc = dev;
+
+// disable_irq_nosync(irq);
+ queue_work(acc->irq2_work_queue, &acc->irq2_work);
+
+ pr_debug("%s: isr2 queued\n", LIS3DH_ACC_DEV_NAME);
+
+ return IRQ_HANDLED;
+}
+//static int lis3dh_acc_get_acceleration_data(struct lis3dh_acc_data *acc,int *xyz);
+static void lis3dh_acc_irq1_work_func(struct work_struct *work)
+{
+ int xyz[3] = { 0 };
+ int err;
+ u8 value;
+ struct lis3dh_acc_data *acc =
+ container_of(work, struct lis3dh_acc_data, irq1_work);
+
+ /* TODO add interrupt service procedure.
+ ie:lis3dh_acc_get_int1_source(acc); */
+ ;
+ /* */
+ LOG_INFO("%s: isr1 queued=========>>>>>>>>>>>>>>>>>\n", __func__);
+ LOG_INFO("sprd-gsensor: -- %s entry :acc->irq1_gpio_number=%d-- !\n",__func__,gpio_get_value(acc->pdata->gpio_int1));
+
+ err = lis3dh_acc_register_read(acc, &value, INT_SRC1);
+
+ if (err < 0) {
+ LOG_INFO("%s, error read register INT_SRC1\n", __func__);
+ }
+ LOG_INFO("%s: IRQ1 triggered INT_SRC1=0x%02x\n",__func__,value);
+
+ err = lis3dh_acc_get_acceleration_data(acc, xyz);
+ if (err < 0)
+ dev_err(&acc->client->dev, "get_acceleration_data failed\n");
+ else
+ lis3dh_acc_report_values(acc, xyz);
+
+
+ LOG_INFO("sprd-gsensor: -- %s exit :acc->irq1_gpio_number=%d-- !\n",__func__,gpio_get_value(acc->pdata->gpio_int1));
+ enable_irq(acc->irq1);
+}
+
+
+#if USE_WAIT_QUEUE
+
+static void lis3dh_acc_update_data_func(void)
+{
+ int xyz[3] = { 0 };
+ int err;
+ struct lis3dh_acc_data *acc =lis3dh_acc_misc_data;
+
+// LOG_INFO("sprd-gsensor: -- %s entry :acc->irq1_gpio_number=%d-- !\n",__func__,gpio_get_value(acc->pdata->gpio_int1));
+ err = lis3dh_acc_get_acceleration_data(acc, xyz);
+ if (err < 0)
+ LOG_ERR("get_acceleration_data failed*******************************\n");
+ else
+ lis3dh_acc_report_values(acc, xyz);
+#ifdef LIS3DH_DBG
+ LOG_INFO("sprd-gsensor: -- %s exit :acc->irq1_gpio_number=%d-->>>>>>>>>>>>>>>>> !\n\n",__func__,gpio_get_value(acc->pdata->gpio_int1));
+ LOG_INFO("\n");
+ #endif
+ enable_irq(acc->irq1);
+}
+
+static int gsensor_event_handler(void *unused)
+{
+#ifdef LIS3DH_DBG
+ static int count_intr = 0;
+ static s64 interrupt_last = 0;
+ static s64 interrupt_read = 0;
+ static s64 interrupt_cur = 0;
+ static int poll_interval=0;
+ ktime_t time_mono;
+#endif
+ struct sched_param param = { .sched_priority = 5};
+
+ sched_setscheduler(current, SCHED_RR, &param);
+ LOG_INFO("sprd-gsensor: -- %s entry :acc->irq1_gpio_number=%d-- !\n",__func__,gpio_get_value(lis3dh_acc_misc_data->pdata->gpio_int1));
+
+ do {
+#ifdef LIS3DH_DBG
+ LOG_INFO("sprd-gsensor: -- %s 1 : gsensor_flag=%d,acc->irq1_gpio_number=%d-- !\n",__func__,gsensor_flag,gpio_get_value(lis3dh_acc_misc_data->pdata->gpio_int1));
+#endif
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ wait_event_interruptible(waiter, (0 != gsensor_flag));
+//
+#ifdef LIS3DH_DBG
+ time_mono = ktime_get();
+ if (poll_interval !=lis3dh_acc_misc_data->pdata->poll_interval) {
+ count_intr=1;
+ poll_interval = lis3dh_acc_misc_data->pdata->poll_interval;
+ interrupt_last = ktime_to_ns(time_mono);
+ }
+ interrupt_cur = ktime_to_ns(time_mono);
+ int interrupt_interval = interrupt_cur - interrupt_last;
+ interrupt_last = interrupt_cur;
+
+ if(interrupt_interval > (lis3dh_acc_misc_data->pdata->poll_interval*1000000) +(lis3dh_acc_misc_data->pdata->poll_interval*1000000) /4)
+ LOG_INFO("***sprd-gsensor SHED exception:count_intr=%d,interrupt_interval=%d,poll_interva=%d********\n",count_intr++,interrupt_interval,lis3dh_acc_misc_data->pdata->poll_interval);
+
+ LOG_INFO("sprd-gsensor: -- %s 2: interrupt_interval=%d,poll_interval=%d,gsensor_flag=%d,acc->irq1_gpio_number=%d-- !\n",__func__,interrupt_interval,poll_interval,gsensor_flag,gpio_get_value(lis3dh_acc_misc_data->pdata->gpio_int1));
+#endif
+ gsensor_flag = 0;
+ set_current_state(TASK_RUNNING);
+ lis3dh_acc_update_data_func();
+#ifdef LIS3DH_DBG
+ time_mono = ktime_get();
+ interrupt_read=ktime_to_ns(time_mono)-interrupt_cur;
+ if(interrupt_interval > (lis3dh_acc_misc_data->pdata->poll_interval*1000000) +(lis3dh_acc_misc_data->pdata->poll_interval*1000000) /4)
+ LOG_INFO("***sprd-gsensor SHED exception:count_intr=%d,interrupt_interval=%d,poll_interva=%d, interrupt_read=%lld********\n",count_intr++,interrupt_interval,lis3dh_acc_misc_data->pdata->poll_interval,interrupt_read);
+
+ LOG_INFO("sprd-gsensor: -- %s 3: interrupt_read=%lld,poll_interval=%d,gsensor_flag=%d,acc->irq1_gpio_number=%d-- !\n",__func__,interrupt_read,poll_interval,gsensor_flag,gpio_get_value(lis3dh_acc_misc_data->pdata->gpio_int1));
+#endif
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+#endif
+
+
+static void lis3dh_acc_irq2_work_func(struct work_struct *work)
+{
+
+ /*struct lis3dh_acc_data *acc =
+ container_of(work, struct lis3dh_acc_data, irq2_work);
+ */
+ /* TODO add interrupt service procedure.
+ ie:lis3dh_acc_get_tap_source(acc); */
+ ;
+ /* */
+
+ LOG_INFO("%s: IRQ2 triggered\n", LIS3DH_ACC_DEV_NAME);
+}
+
+int lis3dh_acc_update_g_range(struct lis3dh_acc_data *acc, u8 new_g_range)
+{
+ int err;
+
+ u8 sensitivity;
+ u8 buf[2];
+ u8 updated_val;
+ u8 init_val;
+ u8 new_val;
+ u8 mask = LIS3DH_ACC_FS_MASK | HIGH_RESOLUTION;
+
+ pr_debug("%s\n", __func__);
+
+ switch (new_g_range) {
+ case LIS3DH_ACC_G_2G:
+
+ sensitivity = SENSITIVITY_2G;
+ break;
+ case LIS3DH_ACC_G_4G:
+
+ sensitivity = SENSITIVITY_4G;
+ break;
+ case LIS3DH_ACC_G_8G:
+
+ sensitivity = SENSITIVITY_8G;
+ break;
+ case LIS3DH_ACC_G_16G:
+
+ sensitivity = SENSITIVITY_16G;
+ break;
+ default:
+ dev_err(&acc->client->dev, "invalid g range requested: %u\n",
+ new_g_range);
+ return -EINVAL;
+ }
+
+ if (atomic_read(&acc->enabled)) {
+ /* Set configuration register 4, which contains g range setting
+ * NOTE: this is a straight overwrite because this driver does
+ * not use any of the other configuration bits in this
+ * register. Should this become untrue, we will have to read
+ * out the value and only change the relevant bits --XX----
+ * (marked by X) */
+ buf[0] = CTRL_REG4;
+ err = lis3dh_acc_i2c_read(acc, buf, 1);
+ if (err < 0)
+ goto error;
+ init_val = buf[0];
+ acc->resume_state[RES_CTRL_REG4] = init_val;
+ new_val = new_g_range | HIGH_RESOLUTION;
+ updated_val = ((mask & new_val) | ((~mask) & init_val));
+ buf[1] = updated_val;
+ buf[0] = CTRL_REG4;
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error;
+ acc->resume_state[RES_CTRL_REG4] = updated_val;
+ acc->sensitivity = sensitivity;
+
+ pr_debug("%s sensitivity %d g-range %d\n", __func__, sensitivity,new_g_range);
+ }
+
+ return 0;
+error:
+ dev_err(&acc->client->dev, "update g range failed 0x%x,0x%x: %d\n",
+ buf[0], buf[1], err);
+
+ return err;
+}
+
+int lis3dh_acc_update_odr(struct lis3dh_acc_data *acc, int poll_interval_ms)
+{
+ int err = -1;
+ int i;
+ u8 config[2];
+
+ /* Convert the poll interval into an output data rate configuration
+ * that is as low as possible. The ordering of these checks must be
+ * maintained due to the cascading cut off values - poll intervals are
+ * checked from shortest to longest. At each check, if the next lower
+ * ODR cannot support the current poll interval, we stop searching */
+ for (i = ARRAY_SIZE(lis3dh_acc_odr_table) - 1; i >= 0; i--) {
+ if (lis3dh_acc_odr_table[i].cutoff_ms <= poll_interval_ms)
+ break;
+ }
+ config[1] = lis3dh_acc_odr_table[i].mask;
+
+ LOG_INFO("%s :ODR poll_interval_ms=%d, config[1]=0x%02x,cutoff_ms %d",__func__,poll_interval_ms,config[1],lis3dh_acc_odr_table[i].cutoff_ms );
+
+ config[1] |= LIS3DH_ACC_ENABLE_ALL_AXES;
+
+ /* If device is currently enabled, we need to write new
+ * configuration out to it */
+ if (atomic_read(&acc->enabled)) {
+ config[0] = CTRL_REG1;
+ err = lis3dh_acc_i2c_write(acc, config, 1);
+ if (err < 0)
+ goto error;
+ acc->resume_state[RES_CTRL_REG1] = config[1];
+ }
+
+ return 0;
+
+error:
+ dev_err(&acc->client->dev, "update odr failed 0x%x,0x%x: %d\n",
+ config[0], config[1], err);
+
+ return err;
+}
+
+/* */
+
+static int lis3dh_acc_register_write(struct lis3dh_acc_data *acc, u8 * buf,
+ u8 reg_address, u8 new_value)
+{
+ int err = -1;
+
+ if (atomic_read(&acc->enabled)) {
+ /* Sets configuration register at reg_address
+ * NOTE: this is a straight overwrite */
+ buf[0] = reg_address;
+ buf[1] = new_value;
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ return err;
+ }
+ return err;
+}
+
+static int lis3dh_acc_register_update(struct lis3dh_acc_data *acc, u8 * buf,
+ u8 reg_address, u8 mask,
+ u8 new_bit_values)
+{
+ int err = -1;
+ u8 init_val;
+ u8 updated_val;
+ err = lis3dh_acc_register_read(acc, buf, reg_address);
+ if (!(err < 0)) {
+ init_val = buf[1];
+ updated_val = ((mask & new_bit_values) | ((~mask) & init_val));
+ err = lis3dh_acc_register_write(acc, buf, reg_address,
+ updated_val);
+ }
+ return err;
+}
+
+/* */
+
+static int lis3dh_acc_get_acceleration_data(struct lis3dh_acc_data *acc,
+ int *xyz)
+{
+ int err = -1;
+ /* Data bytes from hardware xL, xH, yL, yH, zL, zH */
+ u8 acc_data[6];
+ /* x,y,z hardware data */
+ s16 hw_d[3] = { 0 };
+
+ acc_data[0] = (I2C_AUTO_INCREMENT | AXISDATA_REG);
+ err = lis3dh_acc_i2c_read(acc, acc_data, 6);
+
+ if (err < 0) {
+ LOG_ERR("%s exception ssssI2C read error %d\n", LIS3DH_ACC_I2C_NAME,
+ err);
+ return err;
+ }
+
+ hw_d[0] = (((s16) ((acc_data[1] << 8) | acc_data[0])) >> 4);
+ hw_d[1] = (((s16) ((acc_data[3] << 8) | acc_data[2])) >> 4);
+ hw_d[2] = (((s16) ((acc_data[5] << 8) | acc_data[4])) >> 4);
+
+ hw_d[0] = hw_d[0] * acc->sensitivity;
+ hw_d[1] = hw_d[1] * acc->sensitivity;
+ hw_d[2] = hw_d[2] * acc->sensitivity;
+
+ xyz[0] = ((acc->pdata->negate_x) ? (-hw_d[acc->pdata->axis_map_x])
+ : (hw_d[acc->pdata->axis_map_x]));
+ xyz[1] = ((acc->pdata->negate_y) ? (-hw_d[acc->pdata->axis_map_y])
+ : (hw_d[acc->pdata->axis_map_y]));
+ xyz[2] = ((acc->pdata->negate_z) ? (-hw_d[acc->pdata->axis_map_z])
+ : (hw_d[acc->pdata->axis_map_z]));
+#ifdef LIS3DH_DBG
+ LOG_INFO("%s read x=%d, y=%d, z=%d\n",
+ LIS3DH_ACC_DEV_NAME, xyz[0], xyz[1], xyz[2]);
+#endif
+
+ return err;
+}
+
+static void lis3dh_acc_report_values(struct lis3dh_acc_data *acc, int *xyz)
+{
+ input_report_abs(acc->input_dev, ABS_X, xyz[0]);
+ input_report_abs(acc->input_dev, ABS_Y, xyz[1]);
+ input_report_abs(acc->input_dev, ABS_Z, xyz[2]);
+ input_sync(acc->input_dev);
+ #ifdef LIS3DH_DBG
+ acc->data_num++;
+ #endif
+}
+
+static int lis3dh_acc_enable(struct lis3dh_acc_data *acc)
+{
+ int err;
+
+ pr_debug("%s: pr_debug PRINT \n", __func__);
+
+ LOG_INFO("sprd-gsensor: -- %s entry :acc->irq1_gpio_number=%d-- !\n",__func__,gpio_get_value(acc->pdata->gpio_int1));
+
+ if (!atomic_cmpxchg(&acc->enabled, 0, 1)) {
+ err = lis3dh_acc_device_power_on(acc);
+ LOG_INFO("sprd-gsensor: -- %s err=%d-- !\n",__func__,err);
+ if (err < 0) {
+ atomic_set(&acc->enabled, 0);
+ return err;
+ }
+
+ LOG_INFO("sprd-gsensor: -- %s acc->hw_initialized=%d acc->irq1=%d-- !\n",__func__,acc->hw_initialized,acc->irq1);
+ if (acc->hw_initialized) {
+ if (acc->irq1 != 0){
+ LOG_INFO("sprd-gsensor: -- %s after init : acc->irq1_gpio_number=%d-- !\n",__func__,gpio_get_value(acc->pdata->gpio_int1));
+ enable_irq(acc->irq1);
+ }
+ if (acc->irq2 != 0)
+ enable_irq(acc->irq2);
+ LOG_INFO("%s: power on: irq enabled\n",
+ LIS3DH_ACC_DEV_NAME);
+ }
+
+ #ifdef LIS3DH_DBG
+ acc->data_num = 0;
+ #endif
+
+ /*
+ schedule_delayed_work(&acc->input_work,
+ msecs_to_jiffies(acc->pdata->
+ poll_interval));
+ */
+#ifndef GSENSOR_INTERRUPT_MODE
+ queue_delayed_work(_gSensorWork_workqueue, &acc->input_work, msecs_to_jiffies(acc->pdata->poll_interval));
+#endif
+ }
+ LOG_INFO("sprd-gsensor: -- %s exit :acc->irq1_gpio_number=%d-- !\n",__func__,gpio_get_value(acc->pdata->gpio_int1));
+ LOG_INFO("sprd-gsensor: -- %s -- success!\n",__func__);
+ return 0;
+}
+
+static int lis3dh_acc_disable(struct lis3dh_acc_data *acc)
+{
+ LOG_INFO("sprd-gsensor: -- %s --acc->hw_initialized=%d\n",__func__,acc->hw_initialized);
+ if (atomic_cmpxchg(&acc->enabled, 1, 0)) {
+ cancel_delayed_work_sync(&acc->input_work);
+
+ if (acc->pdata->power_off) {
+ if (acc->irq1 != 0)
+ disable_irq_nosync(acc->irq1);
+ if (acc->irq2 != 0)
+ disable_irq_nosync(acc->irq2);
+ acc->pdata->power_off();
+ acc->hw_initialized = 0;
+ }
+ if (acc->hw_initialized) {
+ if (acc->irq1 != 0)
+ disable_irq_nosync(acc->irq1);
+ if (acc->irq2 != 0)
+ disable_irq_nosync(acc->irq2);
+ //only init once during probe, the sensor is ALWAYS power supply
+ // acc->hw_initialized = 0;
+ }
+ lis3dh_acc_device_power_off(acc);
+ }
+#ifdef LIS3DH_DBG
+ LOG_INFO("******sprd-gsensor: -- %s --acc->data_num =%lld ,acc->pdata->poll_interval =%d****\n",__func__,acc->data_num,acc->pdata->poll_interval);
+ acc->data_num = 0;
+#endif
+ return 0;
+}
+
+static int lis3dh_acc_misc_open(struct inode *inode, struct file *file)
+{
+ int err;
+ err = nonseekable_open(inode, file);
+ if (err < 0)
+ return err;
+
+ file->private_data = lis3dh_acc_misc_data;
+
+ return 0;
+}
+
+static long lis3dh_acc_misc_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ u8 buf[4];
+ u8 mask;
+ u8 reg_address;
+ u8 bit_values;
+ int err;
+ int interval;
+ int xyz[3] = { 0 };
+ struct lis3dh_acc_data *acc = file->private_data;
+
+
+ LOG_INFO("%s :ioctl cmd %d\n",__func__, _IOC_NR(cmd));
+ switch (cmd) {
+ case LIS3DH_ACC_IOCTL_GET_DELAY:
+ interval = acc->pdata->poll_interval;
+ if (copy_to_user(argp, &interval, sizeof(interval)))
+ return -EFAULT;
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_DELAY:
+ if (copy_from_user(&interval, argp, sizeof(interval)))
+ return -EFAULT;
+ if (interval < 0 || interval > 1000)
+ return -EINVAL;
+ interval = interval/2;
+ acc->pdata->poll_interval = max(interval,acc->pdata->min_interval); // max the frequecy of g sensor
+ LOG_INFO(" %s,acc->pdata->poll_interval =%d,interval=%d,acc->pdata->min_interval=%d\n", __func__,acc->pdata->poll_interval,interval,acc->pdata->min_interval );
+ err = lis3dh_acc_update_odr(acc, acc->pdata->poll_interval);
+ /* TODO: if update fails poll is still set */
+ if (err < 0)
+ return err;
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_ENABLE:
+ if (copy_from_user(&interval, argp, sizeof(interval)))
+ return -EFAULT;
+ if (interval > 1)
+ return -EINVAL;
+ if (interval)
+ err = lis3dh_acc_enable(acc);
+ else
+ err = lis3dh_acc_disable(acc);
+ return err;
+ break;
+
+ case LIS3DH_ACC_IOCTL_GET_ENABLE:
+ interval = atomic_read(&acc->enabled);
+ if (copy_to_user(argp, &interval, sizeof(interval)))
+ return -EINVAL;
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_G_RANGE:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ bit_values = buf[0];
+ err = lis3dh_acc_update_g_range(acc, bit_values);
+ if (err < 0)
+ return err;
+ break;
+
+#ifdef INTERRUPT_MANAGEMENT
+ case LIS3DH_ACC_IOCTL_SET_CTRL_REG3:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = CTRL_REG3;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_CTRL_REG3] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_CTRL_REG3]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_CTRL_REG6:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = CTRL_REG6;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_CTRL_REG6] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_CTRL_REG6]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_DURATION1:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ reg_address = INT_DUR1;
+ mask = 0x7F;
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_DUR1] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_DUR1]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_THRESHOLD1:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ reg_address = INT_THS1;
+ mask = 0x7F;
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_THS1] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_THS1]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_CONFIG1:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = INT_CFG1;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_CFG1] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_CFG1]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_GET_SOURCE1:
+ err = lis3dh_acc_register_read(acc, buf, INT_SRC1);
+ if (err < 0)
+ return err;
+
+ pr_debug("INT1_SRC content: %d , 0x%x\n", buf[0], buf[0]);
+
+ if (copy_to_user(argp, buf, 1))
+ return -EINVAL;
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_DURATION2:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ reg_address = INT_DUR2;
+ mask = 0x7F;
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_DUR2] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_DUR2]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_THRESHOLD2:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ reg_address = INT_THS2;
+ mask = 0x7F;
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_THS2] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_THS2]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_CONFIG2:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = INT_CFG2;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_CFG2] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_CFG2]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_GET_SOURCE2:
+ err = lis3dh_acc_register_read(acc, buf, INT_SRC2);
+ if (err < 0)
+ return err;
+
+ pr_debug("INT2_SRC content: %d , 0x%x\n", buf[0], buf[0]);
+
+ if (copy_to_user(argp, buf, 1))
+ return -EINVAL;
+ break;
+
+ case LIS3DH_ACC_IOCTL_GET_TAP_SOURCE:
+ err = lis3dh_acc_register_read(acc, buf, TT_SRC);
+ if (err < 0)
+ return err;
+ pr_debug("TT_SRC content: %d , 0x%x\n", buf[0], buf[0]);
+
+ if (copy_to_user(argp, buf, 1)) {
+ pr_err("%s: %s error in copy_to_user \n",
+ LIS3DH_ACC_DEV_NAME, __func__);
+ return -EINVAL;
+ }
+ break;
+ case LIS3DH_ACC_IOCTL_GET_COOR_XYZ:
+ err = lis3dh_acc_get_acceleration_data(acc, xyz);
+ if (err < 0)
+ return err;
+
+ if (copy_to_user(argp, xyz, sizeof(xyz))) {
+ pr_err(" %s %d error in copy_to_user \n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+ case LIS3DH_ACC_IOCTL_SET_TAP_CFG:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_CFG;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_CFG] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_CFG]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_TAP_TLIM:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_LIM;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_LIM] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_LIM]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_TAP_THS:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_THS;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_THS] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_THS]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_TAP_TLAT:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_TLAT;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_TLAT] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_TLAT]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_TAP_TW:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_TW;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_TW] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_TW]));
+ break;
+
+#endif /* INTERRUPT_MANAGEMENT */
+ case LIS3DH_ACC_IOCTL_GET_CHIP_ID:
+ {
+ u8 devid = 0;
+ u8 devinfo[DEVICE_INFO_LEN] = {0};
+ err = lis3dh_acc_register_read(acc, &devid, WHO_AM_I);
+ if (err < 0) {
+ LOG_INFO("%s, error read register WHO_AM_I\n", __func__);
+ return -EAGAIN;
+ }
+ sprintf(devinfo, "%s, %#x", DEVICE_INFO, devid);
+
+ if (copy_to_user(argp, devinfo, sizeof(devinfo))) {
+ printk("%s error in copy_to_user(IOCTL_GET_CHIP_ID)\n", __func__);
+ return -EINVAL;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct file_operations lis3dh_acc_misc_fops = {
+ .owner = THIS_MODULE,
+ .open = lis3dh_acc_misc_open,
+ .unlocked_ioctl = lis3dh_acc_misc_ioctl,
+};
+
+static struct miscdevice lis3dh_acc_misc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = LIS3DH_ACC_DEV_NAME,
+ .fops = &lis3dh_acc_misc_fops,
+};
+
+
+#ifndef GSENSOR_INTERRUPT_MODE
+
+static void lis3dh_acc_input_work_func(struct work_struct *work)
+{
+ struct lis3dh_acc_data *acc;
+
+ int xyz[3] = { 0 };
+ int err;
+ static int count_intr = 0;
+ static s64 interrupt_last = 0;
+ static s64 interrupt_cur = 0;
+ ktime_t time_mono;
+ time_mono = ktime_get();
+ interrupt_cur = ktime_to_ns(time_mono);
+ int interrupt_interval = interrupt_cur - interrupt_last;
+ interrupt_last = interrupt_cur;
+
+// LOG_INFO("ENTRY----->%s\n",__func__);
+ acc = container_of((struct delayed_work *)work,
+ struct lis3dh_acc_data, input_work);
+
+ mutex_lock(&acc->lock);
+ err = lis3dh_acc_get_acceleration_data(acc, xyz);
+ if (err < 0)
+ dev_err(&acc->client->dev, "get_acceleration_data failed\n");
+ else
+ lis3dh_acc_report_values(acc, xyz);
+// LOG_INFO(KERN_ERR "lihai: sprd-gsensor count_intr=%8d interrupt_interval=%8d, poll_interval=%d, err=%d \n", count_intr++, interrupt_interval, acc->pdata->poll_interval, err);
+#if 0
+ schedule_delayed_work(&acc->input_work,
+ msecs_to_jiffies(acc->pdata->poll_interval));
+#else
+ queue_delayed_work(_gSensorWork_workqueue, &acc->input_work, msecs_to_jiffies(acc->pdata->poll_interval));
+#endif
+ mutex_unlock(&acc->lock);
+
+// LOG_INFO("EXIT----->%s\n\n",__func__);
+}
+#endif
+
+#ifdef LIS3DH_OPEN_ENABLE
+int lis3dh_acc_input_open(struct input_dev *input)
+{
+ struct lis3dh_acc_data *acc = input_get_drvdata(input);
+
+ return lis3dh_acc_enable(acc);
+}
+
+void lis3dh_acc_input_close(struct input_dev *dev)
+{
+ struct lis3dh_acc_data *acc = input_get_drvdata(dev);
+
+ lis3dh_acc_disable(acc);
+}
+#endif
+
+static int lis3dh_acc_validate_pdata(struct lis3dh_acc_data *acc)
+{
+ acc->pdata->poll_interval = max(acc->pdata->poll_interval,
+ acc->pdata->min_interval);
+
+ if (acc->pdata->axis_map_x > 2 || acc->pdata->axis_map_y > 2
+ || acc->pdata->axis_map_z > 2) {
+ dev_err(&acc->client->dev, "invalid axis_map value "
+ "x:%u y:%u z%u\n", acc->pdata->axis_map_x,
+ acc->pdata->axis_map_y, acc->pdata->axis_map_z);
+ return -EINVAL;
+ }
+
+ /* Only allow 0 and 1 for negation boolean flag */
+ if (acc->pdata->negate_x > 1 || acc->pdata->negate_y > 1
+ || acc->pdata->negate_z > 1) {
+ dev_err(&acc->client->dev, "invalid negate value "
+ "x:%u y:%u z:%u\n", acc->pdata->negate_x,
+ acc->pdata->negate_y, acc->pdata->negate_z);
+ return -EINVAL;
+ }
+
+ /* Enforce minimum polling interval */
+ if (acc->pdata->poll_interval < acc->pdata->min_interval) {
+ dev_err(&acc->client->dev, "minimum poll interval violated\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int lis3dh_acc_input_init(struct lis3dh_acc_data *acc)
+{
+ int err;
+
+#ifndef GSENSOR_INTERRUPT_MODE
+ /* Polling rx data when the interrupt is not used.*/
+ if (1 /*acc->irq1 == 0 && acc->irq1 == 0 */ ) {
+ INIT_DELAYED_WORK(&acc->input_work, lis3dh_acc_input_work_func);
+ _gSensorWork_workqueue = create_singlethread_workqueue("acc_singlethread");
+ if (!_gSensorWork_workqueue) {
+ err = -ESRCH;
+ printk(KERN_ERR "acc: create_singlethread_workqueue\n");
+ return err;
+ }
+ }
+#endif
+
+ acc->input_dev = input_allocate_device();
+ if (!acc->input_dev) {
+ err = -ENOMEM;
+ dev_err(&acc->client->dev, "input device allocate failed\n");
+ goto err0;
+ }
+#ifdef LIS3DH_ACC_OPEN_ENABLE
+ acc->input_dev->open = lis3dh_acc_input_open;
+ acc->input_dev->close = lis3dh_acc_input_close;
+#endif
+
+ input_set_drvdata(acc->input_dev, acc);
+
+ set_bit(EV_ABS, acc->input_dev->evbit);
+ /* next is used for interruptA sources data if the case */
+ set_bit(ABS_MISC, acc->input_dev->absbit);
+ /* next is used for interruptB sources data if the case */
+ set_bit(ABS_WHEEL, acc->input_dev->absbit);
+
+ input_set_abs_params(acc->input_dev, ABS_X, -G_MAX, G_MAX, 0, 0);
+ input_set_abs_params(acc->input_dev, ABS_Y, -G_MAX, G_MAX, 0, 0);
+ input_set_abs_params(acc->input_dev, ABS_Z, -G_MAX, G_MAX, 0, 0);
+ /* next is used for interruptA sources data if the case */
+ input_set_abs_params(acc->input_dev, ABS_MISC, INT_MIN, INT_MAX, 0, 0);
+ /* next is used for interruptB sources data if the case */
+ input_set_abs_params(acc->input_dev, ABS_WHEEL, INT_MIN, INT_MAX, 0, 0);
+
+ acc->input_dev->name = "accelerometer";
+
+ err = input_register_device(acc->input_dev);
+ if (err) {
+ dev_err(&acc->client->dev,
+ "unable to register input polled device %s\n",
+ acc->input_dev->name);
+ goto err1;
+ }
+
+ return 0;
+
+err1:
+ input_free_device(acc->input_dev);
+err0:
+ return err;
+}
+
+static void lis3dh_acc_input_cleanup(struct lis3dh_acc_data *acc)
+{
+ input_unregister_device(acc->input_dev);
+ input_free_device(acc->input_dev);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void lis3dh_early_suspend(struct early_suspend *es);
+static void lis3dh_early_resume(struct early_suspend *es);
+#endif
+
+#ifdef CONFIG_OF
+static struct lis3dh_acc_platform_data *lis3dh_acc_parse_dt(struct device *dev)
+{
+ struct lis3dh_acc_platform_data *pdata;
+ struct device_node *np = dev->of_node;
+ int ret;
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "Could not allocate struct lis3dh_acc_platform_data");
+ return NULL;
+ }
+
+ pdata->gpio_int1 = of_get_gpio(np, 0);
+ LOG_INFO(" %s:get gpio_int1 %d\n",__func__,pdata->gpio_int1);
+ if(pdata->gpio_int1 < 0){
+ dev_err(dev, "fail to get irq_gpio_number\n");
+ LOG_INFO(" get irq1_gpio_number fail\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "poll_interval", &pdata->poll_interval);
+ LOG_INFO("%s: get pdata->poll_interval %d\n",__func__,pdata->poll_interval);
+ if(ret){
+ dev_err(dev, "fail to get poll_interval\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "min_interval", &pdata->min_interval);
+ if(ret){
+ dev_err(dev, "fail to get min_interval\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "g_range", &pdata->g_range);
+ if(ret){
+ dev_err(dev, "fail to get g_range\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "axis_map_x", &pdata->axis_map_x);
+ if(ret){
+ dev_err(dev, "fail to get axis_map_x\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "axis_map_y", &pdata->axis_map_y);
+ if(ret){
+ dev_err(dev, "fail to get axis_map_y\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "axis_map_z", &pdata->axis_map_z);
+ if(ret){
+ dev_err(dev, "fail to get axis_map_z\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "negate_x", &pdata->negate_x);
+ if(ret){
+ dev_err(dev, "fail to get negate_x\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "negate_y", &pdata->negate_y);
+ if(ret){
+ dev_err(dev, "fail to get negate_y\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "negate_z", &pdata->negate_z);
+ if(ret){
+ dev_err(dev, "fail to get negate_z\n");
+ goto fail;
+ }
+ return pdata;
+fail:
+ kfree(pdata);
+ return NULL;
+}
+#endif
+
+
+
+
+#ifdef LIS3DH_DBG
+static int str2int(char *p, char *end)
+{
+ int result = 0;
+ int len = end - p + 1;
+ int c;
+
+ for(; len > 0; len--, p++) {
+ if(p > end)
+ return -1;
+ c = *p - '0';
+ if((unsigned int)c >= 10)
+ return -1;
+ result = result * 10 + c;
+ }
+ return result;
+}
+
+/*parse a int from the string*/
+static int get_int(char *p, u8 max_len)
+{
+ char *start = p;
+ char *end =NULL;
+
+ if(max_len > 60) {
+ LOG_ERR("CMD ERROR!the max length of the cmd should less than 60\n");
+ return -1;
+ }
+ for(; max_len > 0; max_len--, p++) {
+ if(*p >= '0' && *p <= '9') {
+ start = p;
+ break;
+ }
+ }
+ if(0 == max_len)
+ return -1;
+
+ end = start;
+
+ for(; max_len > 0; max_len--, p++) {
+ if(*p >= '0' && *p <= '9') {
+ end = p;
+ continue;
+ }
+ break;
+ }
+
+ return str2int(start, end);
+}
+
+static ssize_t lis3dh_val_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff)
+{
+ LOG_FUN( );
+
+ return sprintf(buff, "acc->pdata->poll_interva=%d\n",lis3dh_acc_misc_data->pdata->poll_interval);
+}
+// adjust poll interval dynamiclly,eg echo 5>val_show ,means poll interval is 5ms
+static ssize_t lis3dh_val_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buff, size_t n)
+{
+ LOG_FUN( );
+ char *buff_temp = NULL;
+ int interval;
+ int err;
+
+ buff_temp = kmalloc(n, GFP_KERNEL);
+ if(NULL == buff_temp){
+ LOG_ERR("kmalloc err\n");
+ return n;
+ }
+
+ memcpy(buff_temp, buff, n);
+ interval = get_int(buff_temp, n);
+
+ if (interval < 0 || interval > 1000)
+ return -EINVAL;
+
+ lis3dh_acc_misc_data->pdata->poll_interval = max(interval,lis3dh_acc_misc_data->pdata->min_interval); // max the frequecy of g sensor
+ LOG_INFO("yuebao %s,acc->pdata->poll_interval =%d,interval=%d,acc->pdata->min_interval=%d\n", __func__,lis3dh_acc_misc_data->pdata->poll_interval,interval,lis3dh_acc_misc_data->pdata->min_interval );
+ err = lis3dh_acc_update_odr(lis3dh_acc_misc_data, lis3dh_acc_misc_data->pdata->poll_interval );
+ /* TODO: if update fails poll is still set */
+ // if (err < 0)
+ // return err;
+ if(buff_temp != NULL)
+ kfree(buff_temp);
+
+ return 0;
+}
+
+/*
+1 should first write ,eg echo 0x20=0x97>reg
+2 then read ,eg cat reg:Addr: 0x20 Val: 0x97
+*/
+static ssize_t sensor_reg_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff)
+{
+ int err;
+ u8 value = 0;
+
+ LOG_FUN( );
+
+ err = lis3dh_acc_register_read(lis3dh_acc_misc_data, &value, lis3dh_acc_misc_data->debug.diag_reg_addr_wr);
+ return sprintf(buff, "Addr: 0x%x Val: 0x%x\n",
+ lis3dh_acc_misc_data->debug.diag_reg_addr_wr, value);
+}
+
+static ssize_t sensor_reg_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buff, size_t n)
+{
+ char buf[32];
+ char *sptr, *token;
+ unsigned int len = 0;
+ u8 reg_addr, reg_val;
+ int err = -1;
+
+ LOG_INFO("%s, user echo is:%s\n",__func__,buff);
+ len = min(n, sizeof(buf) - 1);
+ if (!memcpy(buf, buff, len)){
+ LOG_INFO("%s, %s\n",__func__,buf);
+ return -EFAULT;
+ }
+
+ buf[len] = '\0';
+ sptr = buf;
+
+ token = strsep(&sptr, "=");
+ LOG_INFO("%s, token=%s\n",__func__,token);
+
+ if (!token)
+ return -EINVAL;
+
+ LOG_INFO("%s,now token=%s,sptr=%s\n",__func__,token,sptr);
+
+ if (kstrtou8(token, 0, &reg_addr)){
+ LOG_INFO("%s,now reg_addr=0x%x\n",__func__,reg_addr);
+ return -EINVAL;
+ }
+// if (!ath6kl_dbg_is_diag_reg_valid(reg_addr))
+// return -EINVAL;
+
+ if (kstrtou8(sptr, 0, &reg_val)){
+ LOG_INFO("%s,now reg_val=0x%x\n",__func__,reg_val);
+ return -EINVAL;
+ }
+ LOG_INFO("%s,now reg_addr=0x%x,reg_val=0x%x\n",__func__,reg_addr,reg_val);
+
+ lis3dh_acc_misc_data->debug.diag_reg_addr_wr = reg_addr;
+ lis3dh_acc_misc_data->debug.diag_reg_val_wr = reg_val;
+
+ buf[0] = reg_addr;
+ buf[1] = reg_val;
+
+ err = lis3dh_acc_i2c_write(lis3dh_acc_misc_data, buf, 1);
+ if (err < 0){
+ LOG_ERR("%s: %d\n",__func__,err);
+ return -EIO;
+ }
+ //resume_state[index] index is not reg addr, so ONLY CAN config CTRL_REG1~CTRL_REG6
+ lis3dh_acc_misc_data->resume_state[reg_addr-0x20] = reg_val;
+ LOG_INFO("%s,now n=%d\n",__func__,n);
+ return n;
+}
+static struct kobject *lis3dh_kobj;
+static struct kobj_attribute lis3dh_val_attr =
+ __ATTR(show_val, 0644, lis3dh_val_show, lis3dh_val_store);
+static struct kobj_attribute sensor_reg_attr =
+ __ATTR(reg, 0644, sensor_reg_show, sensor_reg_store);
+
+
+static struct attribute *sensor_sysfs_attrs[] = {
+ &lis3dh_val_attr.attr,
+ &sensor_reg_attr.attr,
+
+ NULL
+};
+
+static struct attribute_group sensor_attribute_group = {
+ .attrs = sensor_sysfs_attrs,
+};
+
+
+static int lis3dh_sysfs_init(struct lis3dh_acc_data *acc)
+{
+ int ret = -1;
+
+ lis3dh_kobj = kobject_create_and_add("lis3dh", &(acc->input_dev->dev.kobj));
+ if (lis3dh_kobj == NULL) {
+ ret = -ENOMEM;
+ LOG_ERR("register sysfs failed. ret = %d\n", ret);
+ return ret;
+ }
+ ret = sysfs_create_group(lis3dh_kobj, &sensor_attribute_group);
+ if (ret) {
+ LOG_ERR("create sysfs failed. ret = %d\n", ret);
+ return ret;
+ }
+ return ret;
+}
+
+#endif
+
+static int lis3dh_acc_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+
+ struct lis3dh_acc_data *acc;
+ struct lis3dh_acc_platform_data *pdata = client->dev.platform_data;
+
+ int err = -1;
+ int tempvalue;
+ LOG_INFO("sprd-gsensor: -- %s -- start !\n",__func__);
+ pr_debug("%s: probe start.\n", LIS3DH_ACC_DEV_NAME);
+
+
+#ifdef CONFIG_OF
+ struct device_node *np = client->dev.of_node;
+ if (np && !pdata){
+ pdata = lis3dh_acc_parse_dt(&client->dev);
+ if(pdata){
+ client->dev.platform_data = pdata;
+ }
+ if(!pdata){
+ err = -ENOMEM;
+ goto exit_alloc_platform_data_failed;
+ }
+ }
+#endif
+ /*
+ if (client->dev.platform_data == NULL) {
+ dev_err(&client->dev, "platform data is NULL. exiting.\n");
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+ */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "client not i2c capable\n");
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_err(&client->dev, "client not smb-i2c capable:2\n");
+ err = -EIO;
+ goto exit_check_functionality_failed;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ dev_err(&client->dev, "client not smb-i2c capable:3\n");
+ err = -EIO;
+ goto exit_check_functionality_failed;
+ }
+ /*
+ * OK. From now, we presume we have a valid client. We now create the
+ * client structure, even though we cannot fill it completely yet.
+ */
+
+ acc = kzalloc(sizeof(struct lis3dh_acc_data), GFP_KERNEL);
+ if (acc == NULL) {
+ err = -ENOMEM;
+ dev_err(&client->dev,
+ "failed to allocate memory for module data: "
+ "%d\n", err);
+ goto exit_alloc_data_failed;
+ }
+
+ mutex_init(&acc->lock);
+ mutex_lock(&acc->lock);
+
+ acc->client = client;
+ lis3dh_i2c_client = client;
+ i2c_set_clientdata(client, acc);
+
+ LOG_INFO("gpio pin request (%d)\n", pdata->gpio_int1);
+ err=gpio_request(pdata->gpio_int1, "GSENSOR_INT1");
+
+ if (err) {
+ LOG_ERR("gpio pin request fail (%d)\n", err);
+ goto err_free_irq1;
+ } else {
+ gpio_direction_input(pdata->gpio_int1);
+ LOG_INFO("sprd-gsensor: -- %s acc->irq1_gpio_number status is [%d]-- !\n",__func__,gpio_get_value(pdata->gpio_int1));
+ /*get irq*/
+ acc->irq1 = gpio_to_irq(pdata->gpio_int1);
+ LOG_INFO("IRQ1 number is %d\n", acc->irq1 );
+ }
+ if (acc->irq1 != 0) {
+ pr_debug("%s request irq1\n", __func__);
+ err =
+ request_irq(acc->irq1, lis3dh_acc_isr1, IRQF_TRIGGER_HIGH| IRQF_NO_SUSPEND ,
+ "lis3dh_acc_irq1", acc);
+ if (err < 0) {
+ dev_err(&client->dev, "request irq1 failed: %d\n", err);
+ goto err_mutexunlockfreedata;
+ }
+ disable_irq_nosync(acc->irq1);
+
+ INIT_WORK(&acc->irq1_work, lis3dh_acc_irq1_work_func);
+ acc->irq1_work_queue =
+ create_singlethread_workqueue("lis3dh_acc_wq1");
+ if (!acc->irq1_work_queue) {
+ err = -ENOMEM;
+ dev_err(&client->dev, "cannot create work queue1: %d\n",
+ err);
+ goto err_free_irq1;
+ }
+ }
+
+
+// gpio_request(GSENSOR_GINT2_GPI, "GSENSOR_INT2");
+ acc->irq2 = 0; /* gpio_to_irq(GSENSOR_GINT2_GPI); */
+ if (acc->irq2 != 0) {
+ err =
+ request_irq(acc->irq2, lis3dh_acc_isr2, IRQF_TRIGGER_RISING,
+ "lis3dh_acc_irq2", acc);
+ if (err < 0) {
+ dev_err(&client->dev, "request irq2 failed: %d\n", err);
+ goto err_destoyworkqueue1;
+ }
+ disable_irq_nosync(acc->irq2);
+
+/* Create workqueue for IRQ.*/
+
+ INIT_WORK(&acc->irq2_work, lis3dh_acc_irq2_work_func);
+ acc->irq2_work_queue =
+ create_singlethread_workqueue("lis3dh_acc_wq2");
+ if (!acc->irq2_work_queue) {
+ err = -ENOMEM;
+ dev_err(&client->dev, "cannot create work queue2: %d\n",
+ err);
+ goto err_free_irq2;
+ }
+ }
+
+ acc->pdata = kmalloc(sizeof(*acc->pdata), GFP_KERNEL);
+ if (acc->pdata == NULL) {
+ err = -ENOMEM;
+ dev_err(&client->dev,
+ "failed to allocate memory for pdata: %d\n", err);
+ goto err_destoyworkqueue2;
+ }
+
+ memcpy(acc->pdata, client->dev.platform_data, sizeof(*acc->pdata));
+
+ err = lis3dh_acc_validate_pdata(acc);
+ if (err < 0) {
+ dev_err(&client->dev, "failed to validate platform data\n");
+ goto exit_kfree_pdata;
+ }
+
+#ifdef GSENSOR_INTERRUPT_MODE
+memset(acc->resume_state, 0, ARRAY_SIZE(acc->resume_state));
+
+ acc->resume_state[RES_CTRL_REG1] = LIS3DH_ACC_ENABLE_ALL_AXES;
+ acc->resume_state[RES_CTRL_REG2] = 0x00;
+ acc->resume_state[RES_CTRL_REG3] = CTRL_REG3_I1_DRDY1;//0x00;
+ acc->resume_state[RES_CTRL_REG4] = 0x00;
+ acc->resume_state[RES_CTRL_REG5] = CTRL_REG5_LIR_INT1;//0x00;
+ acc->resume_state[RES_CTRL_REG6] = CTRL_REG6_HLACTIVE;//0x00;
+
+ acc->resume_state[RES_TEMP_CFG_REG] = 0x00;
+ acc->resume_state[RES_FIFO_CTRL_REG] = 0x00;
+ acc->resume_state[RES_INT_CFG1] = 0x00;
+ acc->resume_state[RES_INT_THS1] = 0x00;
+ acc->resume_state[RES_INT_DUR1] = 0x00;
+ acc->resume_state[RES_INT_CFG2] = 0x00;
+ acc->resume_state[RES_INT_THS2] = 0x00;
+ acc->resume_state[RES_INT_DUR2] = 0x00;
+
+ acc->resume_state[RES_TT_CFG] = 0x00;
+ acc->resume_state[RES_TT_THS] = 0x00;
+ acc->resume_state[RES_TT_LIM] = 0x00;
+ acc->resume_state[RES_TT_TLAT] = 0x00;
+ acc->resume_state[RES_TT_TW] = 0x00;
+
+#endif
+ sprd_i2c_ctl_chg_clk(client->adapter->nr, 400000);
+
+
+
+
+ err = lis3dh_acc_device_power_on(acc);
+ if (err < 0) {
+ dev_err(&client->dev, "power on failed: %d\n", err);
+ goto err2;
+ }
+
+#if 1
+ if (i2c_smbus_read_byte(client) < 0) {
+ pr_err("i2c_smbus_read_byte error!!\n");
+ goto err_destoyworkqueue2;
+ } else {
+ pr_debug("%s Device detected!\n", LIS3DH_ACC_DEV_NAME);
+ }
+#endif
+ /* read chip id */
+
+ tempvalue = i2c_smbus_read_word_data(client, WHO_AM_I);
+
+ if ((tempvalue & 0x00FF) == WHOAMI_LIS3DH_ACC) {
+ pr_debug("%s I2C driver registered!\n",
+ LIS3DH_ACC_DEV_NAME);
+ } else {
+ acc->client = NULL;
+ pr_debug("I2C driver not registered!"
+ " Device unknown 0x%x\n", tempvalue);
+ goto err_destoyworkqueue2;
+ }
+
+ i2c_set_clientdata(client, acc);
+
+ if (acc->pdata->init) {
+ err = acc->pdata->init();
+ if (err < 0) {
+ dev_err(&client->dev, "init failed: %d\n", err);
+ goto err2;
+ }
+ }
+#ifndef GSENSOR_INTERRUPT_MODE
+ memset(acc->resume_state, 0, ARRAY_SIZE(acc->resume_state));
+
+ acc->resume_state[RES_CTRL_REG1] = LIS3DH_ACC_ENABLE_ALL_AXES;
+ acc->resume_state[RES_CTRL_REG2] = 0x00;
+ acc->resume_state[RES_CTRL_REG3] = CTRL_REG3_I1_DRDY1;//0x00;
+ acc->resume_state[RES_CTRL_REG4] = 0x00;
+ acc->resume_state[RES_CTRL_REG5] = 0x00;//CTRL_REG5_LIR_INT1;//0x04;
+ acc->resume_state[RES_CTRL_REG6] = CTRL_REG6_HLACTIVE;//0x00;
+
+ acc->resume_state[RES_TEMP_CFG_REG] = 0x00;
+ acc->resume_state[RES_FIFO_CTRL_REG] = 0x00;
+ acc->resume_state[RES_INT_CFG1] = 0x00;
+ acc->resume_state[RES_INT_THS1] = 0x00;
+ acc->resume_state[RES_INT_DUR1] = 0x00;
+ acc->resume_state[RES_INT_CFG2] = 0x00;
+ acc->resume_state[RES_INT_THS2] = 0x00;
+ acc->resume_state[RES_INT_DUR2] = 0x00;
+
+ acc->resume_state[RES_TT_CFG] = 0x00;
+ acc->resume_state[RES_TT_THS] = 0x00;
+ acc->resume_state[RES_TT_LIM] = 0x00;
+ acc->resume_state[RES_TT_TLAT] = 0x00;
+ acc->resume_state[RES_TT_TW] = 0x00;
+
+#endif
+ atomic_set(&acc->enabled, 1);
+
+ err = lis3dh_acc_update_g_range(acc, acc->pdata->g_range);
+ if (err < 0) {
+ dev_err(&client->dev, "update_g_range failed\n");
+ goto err_power_off;
+ }
+
+ err = lis3dh_acc_update_odr(acc, acc->pdata->poll_interval);
+ if (err < 0) {
+ dev_err(&client->dev, "update_odr failed\n");
+ goto err_power_off;
+ }
+
+ err = lis3dh_acc_input_init(acc);
+ if (err < 0) {
+ dev_err(&client->dev, "input init failed\n");
+ goto err_power_off;
+ }
+ lis3dh_acc_misc_data = acc;
+
+ err = misc_register(&lis3dh_acc_misc_device);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "misc LIS3DH_ACC_DEV_NAME register failed\n");
+ goto err_input_cleanup;
+ }
+
+ lis3dh_acc_device_power_off(acc);
+
+ /* As default, do not report information */
+ atomic_set(&acc->enabled, 0);
+/*
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ acc->early_suspend.suspend = lis3dh_early_suspend;
+ acc->early_suspend.resume = lis3dh_early_resume;
+ acc->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
+ register_early_suspend(&acc->early_suspend);
+#endif
+*/
+#ifdef LIS3DH_DBG
+ err = lis3dh_sysfs_init(acc);
+ if(err) {
+ LOG_ERR("lis3dh_sysfs_init failed!\n");
+ goto exit_lis3dh_sysfs_init_failed;
+ }
+#endif
+#if USE_WAIT_QUEUE
+ thread = kthread_run(gsensor_event_handler, 0, "gsensor-wait");
+ if (IS_ERR(thread))
+ {
+ err = PTR_ERR(thread);
+ LOG_ERR("failed to create kernel thread: %d\n", err);
+ }
+#endif
+ mutex_unlock(&acc->lock);
+ dev_info(&client->dev, "###%s###\n", __func__);
+ LOG_INFO("sprd-gsensor: -- %s exit :acc->irq1_gpio_number status is [%d]-- !\n",__func__,gpio_get_value(acc->pdata->gpio_int1));
+ LOG_INFO("sprd-gsensor: -- %s -- success !\n",__func__);
+ return 0;
+
+#ifdef LIS3DH_DBG
+exit_lis3dh_sysfs_init_failed:
+#endif
+
+err_input_cleanup:
+ lis3dh_acc_input_cleanup(acc);
+err_power_off:
+ lis3dh_acc_device_power_off(acc);
+err2:
+ if (acc->pdata->exit)
+ acc->pdata->exit();
+exit_kfree_pdata:
+ kfree(acc->pdata);
+err_destoyworkqueue2:
+ if (acc->irq2_work_queue)
+ destroy_workqueue(acc->irq2_work_queue);
+err_free_irq2:
+ if (acc->irq2) {
+ free_irq(acc->irq2, acc);
+ gpio_free(GSENSOR_GINT2_GPI);
+ }
+err_destoyworkqueue1:
+ if (acc->irq1_work_queue)
+ destroy_workqueue(acc->irq1_work_queue);
+err_free_irq1:
+ if (acc->irq1) {
+ free_irq(acc->irq1, acc);
+ gpio_free(acc->pdata->gpio_int1);
+ }
+err_mutexunlockfreedata:
+ i2c_set_clientdata(client, NULL);
+ mutex_unlock(&acc->lock);
+ kfree(acc);
+ lis3dh_acc_misc_data = NULL;
+exit_alloc_data_failed:
+exit_check_functionality_failed:
+ pr_err("%s: Driver Init failed\n", LIS3DH_ACC_DEV_NAME);
+exit_alloc_platform_data_failed:
+ return err;
+}
+
+static int lis3dh_acc_remove(struct i2c_client *client)
+{
+
+ /* TODO: revisit ordering here once _probe order is finalized */
+ struct lis3dh_acc_data *acc = i2c_get_clientdata(client);
+ if (acc != NULL) {
+ if (acc->irq1) {
+ free_irq(acc->irq1, acc);
+ gpio_free(acc->pdata->gpio_int1);
+ }
+ if (acc->irq2) {
+ free_irq(acc->irq2, acc);
+ gpio_free(GSENSOR_GINT2_GPI);
+ }
+
+ if (acc->irq1_work_queue)
+ destroy_workqueue(acc->irq1_work_queue);
+ if (acc->irq2_work_queue)
+ destroy_workqueue(acc->irq2_work_queue);
+ misc_deregister(&lis3dh_acc_misc_device);
+ lis3dh_acc_input_cleanup(acc);
+ lis3dh_acc_device_power_off(acc);
+ if (acc->pdata->exit)
+ acc->pdata->exit();
+ kfree(acc->pdata);
+ kfree(acc);
+ }
+
+ return 0;
+}
+
+static int lis3dh_acc_resume(struct i2c_client *client)
+{
+ struct lis3dh_acc_data *acc = i2c_get_clientdata(client);
+
+ LOG_INFO("sprd-gsensor: -- %s -- !\n",__func__);
+ dev_dbg(&client->dev, "###%s###\n", __func__);
+ if (acc != NULL && acc->on_before_suspend)
+ return lis3dh_acc_enable(acc);
+
+ return 0;
+}
+
+static int lis3dh_acc_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct lis3dh_acc_data *acc = i2c_get_clientdata(client);
+
+ LOG_INFO("sprd-gsensor: -- %s -- !\n",__func__);
+ dev_dbg(&client->dev, "###%s###\n", __func__);
+ if (acc != NULL) {
+ acc->on_before_suspend = atomic_read(&acc->enabled);
+ return lis3dh_acc_disable(acc);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+
+static void lis3dh_early_suspend(struct early_suspend *es)
+{
+ lis3dh_acc_suspend(lis3dh_i2c_client, (pm_message_t) {
+ .event = 0});
+}
+
+static void lis3dh_early_resume(struct early_suspend *es)
+{
+ lis3dh_acc_resume(lis3dh_i2c_client);
+}
+
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+
+static const struct i2c_device_id lis3dh_acc_id[]
+= { {LIS3DH_ACC_DEV_NAME, 0}, {}, };
+
+MODULE_DEVICE_TABLE(i2c, lis3dh_acc_id);
+
+static const struct of_device_id lis3dh_acc_of_match[] = {
+ { .compatible = "ST,lis3dh_acc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lis3dh_acc_of_match);
+static struct i2c_driver lis3dh_acc_driver = {
+ .driver = {
+ .name = LIS3DH_ACC_I2C_NAME,
+ .of_match_table = lis3dh_acc_of_match,
+ },
+ .probe = lis3dh_acc_probe,
+ .remove = lis3dh_acc_remove,
+ .resume = lis3dh_acc_resume,
+ .suspend = lis3dh_acc_suspend,
+ .id_table = lis3dh_acc_id,
+};
+
+
+static int __init lis3dh_acc_init(void)
+{
+ pr_debug("%s accelerometer driver: init\n", LIS3DH_ACC_I2C_NAME);
+
+ return i2c_add_driver(&lis3dh_acc_driver);
+}
+
+static void __exit lis3dh_acc_exit(void)
+{
+ pr_debug("%s accelerometer driver exit\n", LIS3DH_ACC_DEV_NAME);
+
+ i2c_del_driver(&lis3dh_acc_driver);
+ return;
+}
+
+module_init(lis3dh_acc_init);
+module_exit(lis3dh_acc_exit);
+
+MODULE_DESCRIPTION("lis3dh accelerometer misc driver");
+MODULE_AUTHOR("STMicroelectronics");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/lis3dh_zt.c b/drivers/input/misc/lis3dh_zt.c
new file mode 100644
index 00000000..c0494cb3
--- /dev/null
+++ b/drivers/input/misc/lis3dh_zt.c
@@ -0,0 +1,1729 @@
+/******************** (C) COPYRIGHT 2010 STMicroelectronics ********************
+ *
+ * File Name : lis3dh_acc.c
+ * Authors : MSH - Motion Mems BU - Application Team
+ * : Carmine Iascone (carmine.iascone@st.com)
+ * : Matteo Dameno (matteo.dameno@st.com)
+ * Version : V.1.0.5
+ * Date : 16/08/2010
+ * Description : LIS3DH accelerometer 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.
+ *
+ * THIS SOFTWARE IS SPECIFICALLY DESIGNED FOR EXCLUSIVE USE WITH ST PARTS.
+ *
+ ******************************************************************************
+ Revision 1.0.0 05/11/09
+ First Release
+ Revision 1.0.3 22/01/2010
+ Linux K&R Compliant Release;
+ Revision 1.0.5 16/08/2010
+ modified _get_acceleration_data function
+ modified _update_odr function
+ manages 2 interrupts
+
+ ******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#include <linux/i2c/lis3dh_zt.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+
+#define INTERRUPT_MANAGEMENT 1
+
+#define G_MAX 16000 /** Maximum polled-device-reported g value */
+
+/*
+#define SHIFT_ADJ_2G 4
+#define SHIFT_ADJ_4G 3
+#define SHIFT_ADJ_8G 2
+#define SHIFT_ADJ_16G 1
+*/
+
+//#define SENSITIVITY_2G 1 /** mg/LSB */
+#define SENSITIVITY_2G 15.6 /** mg/LSB */
+//#define SENSITIVITY_4G 2 /** mg/LSB */
+#define SENSITIVITY_4G 31.2 /** mg/LSB */
+//#define SENSITIVITY_8G 4 /** mg/LSB */
+#define SENSITIVITY_8G 62.5 /** mg/LSB */
+//#define SENSITIVITY_16G 12 /** mg/LSB */
+#define SENSITIVITY_16G 187.5 /** mg/LSB */
+
+#define HIGH_RESOLUTION 0x08
+
+#define AXISDATA_REG 0x28
+#define WHOAMI_LIS3DH_ACC 0x33 /* Expctd content for WAI */
+
+/* CONTROL REGISTERS */
+#define WHO_AM_I 0x0F /* WhoAmI register */
+#define TEMP_CFG_REG 0x1F /* temper sens control reg */
+/* ctrl 1: ODR3 ODR2 ODR ODR0 LPen Zenable Yenable Zenable */
+#define CTRL_REG1 0x20 /* control reg 1 */
+#define CTRL_REG2 0x21 /* control reg 2 */
+#define CTRL_REG3 0x22 /* control reg 3 */
+#define CTRL_REG4 0x23 /* control reg 4 */
+#define CTRL_REG5 0x24 /* control reg 5 */
+#define CTRL_REG6 0x25 /* control reg 6 */
+
+#define FIFO_CTRL_REG 0x2E /* FiFo control reg */
+
+#define INT_CFG1 0x30 /* interrupt 1 config */
+#define INT_SRC1 0x31 /* interrupt 1 source */
+#define INT_THS1 0x32 /* interrupt 1 threshold */
+#define INT_DUR1 0x33 /* interrupt 1 duration */
+
+#define INT_CFG2 0x34 /* interrupt 2 config */
+#define INT_SRC2 0x35 /* interrupt 2 source */
+#define INT_THS2 0x36 /* interrupt 2 threshold */
+#define INT_DUR2 0x37 /* interrupt 2 duration */
+
+#define TT_CFG 0x38 /* tap config */
+#define TT_SRC 0x39 /* tap source */
+#define TT_THS 0x3A /* tap threshold */
+#define TT_LIM 0x3B /* tap time limit */
+#define TT_TLAT 0x3C /* tap time latency */
+#define TT_TW 0x3D /* tap time window */
+/* end CONTROL REGISTRES */
+
+#define ENABLE_HIGH_RESOLUTION 1
+
+#define LIS3DH_ACC_PM_OFF 0x00
+#define LIS3DH_ACC_ENABLE_ALL_AXES 0x07
+
+#define PMODE_MASK 0x08
+#define ODR_MASK 0XF0
+
+#define ODR1 0x10 /* 1Hz output data rate */
+#define ODR10 0x20 /* 10Hz output data rate */
+#define ODR25 0x30 /* 25Hz output data rate */
+#define ODR50 0x40 /* 50Hz output data rate */
+#define ODR100 0x50 /* 100Hz output data rate */
+#define ODR200 0x60 /* 200Hz output data rate */
+#define ODR400 0x70 /* 400Hz output data rate */
+#define ODR1250 0x90 /* 1250Hz output data rate */
+
+#define IA 0x40
+#define ZH 0x20
+#define ZL 0x10
+#define YH 0x08
+#define YL 0x04
+#define XH 0x02
+#define XL 0x01
+/* */
+/* CTRL REG BITS*/
+#define CTRL_REG3_I1_AOI1 0x40
+#define CTRL_REG6_I2_TAPEN 0x80
+#define CTRL_REG6_HLACTIVE 0x02
+/* */
+
+/* TAP_SOURCE_REG BIT */
+#define DTAP 0x20
+#define STAP 0x10
+#define SIGNTAP 0x08
+#define ZTAP 0x04
+#define YTAP 0x02
+#define XTAZ 0x01
+
+#define FUZZ 32
+#define FLAT 32
+#define I2C_RETRY_DELAY 5
+#define I2C_RETRIES 5
+#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_CTRL_REG6 5
+
+#define RES_INT_CFG1 6
+#define RES_INT_THS1 7
+#define RES_INT_DUR1 8
+#define RES_INT_CFG2 9
+#define RES_INT_THS2 10
+#define RES_INT_DUR2 11
+
+#define RES_TT_CFG 12
+#define RES_TT_THS 13
+#define RES_TT_LIM 14
+#define RES_TT_TLAT 15
+#define RES_TT_TW 16
+
+#define RES_TEMP_CFG_REG 17
+#define RES_REFERENCE_REG 18
+#define RES_FIFO_CTRL_REG 19
+
+#define RESUME_ENTRIES 20
+#define DEVICE_INFO "ST, LIS3DH"
+#define DEVICE_INFO_LEN 32
+/* end RESUME STATE INDICES */
+
+#define GSENSOR_GINT1_GPI 0
+#define GSENSOR_GINT2_GPI 1
+
+struct {
+ unsigned int cutoff_ms;
+ unsigned int mask;
+} lis3dh_acc_odr_table[] = {
+ {
+ 1, ODR1250}, {
+ 3, ODR400}, {
+ 5, ODR200}, {
+ 10, ODR100}, {
+ 20, ODR50}, {
+ 40, ODR25}, {
+ 100, ODR10}, {
+1000, ODR1},};
+
+struct lis3dh_acc_data {
+ struct i2c_client *client;
+ struct lis3dh_acc_platform_data *pdata;
+
+ struct mutex lock;
+ struct delayed_work input_work;
+
+ struct input_dev *input_dev;
+
+ int hw_initialized;
+ /* hw_working=-1 means not tested yet */
+ int hw_working;
+ atomic_t enabled;
+ int on_before_suspend;
+
+ u8 sensitivity;
+
+ u8 resume_state[RESUME_ENTRIES];
+
+ 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;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend;
+#endif
+};
+
+/*
+ * Because misc devices can not carry a pointer from driver register to
+ * open, we keep this global. This limits the driver to a single instance.
+ */
+struct lis3dh_acc_data *lis3dh_acc_misc_data;
+struct i2c_client *lis3dh_i2c_client;
+
+static int lis3dh_acc_i2c_read(struct lis3dh_acc_data *acc, u8 * buf, int len)
+{
+ int err;
+ int tries = 0;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = acc->client->addr,
+ .flags = acc->client->flags & I2C_M_TEN,
+ .len = 1,
+ .buf = buf,},
+ {
+ .addr = acc->client->addr,
+ .flags = (acc->client->flags & I2C_M_TEN) | I2C_M_RD,
+ .len = len,
+ .buf = buf,},
+ };
+
+ do {
+ err = i2c_transfer(acc->client->adapter, msgs, 2);
+ if (err != 2)
+ msleep_interruptible(I2C_RETRY_DELAY);
+ } while ((err != 2) && (++tries < I2C_RETRIES));
+
+ if (err != 2) {
+ dev_err(&acc->client->dev, "read transfer error\n");
+ err = -EIO;
+ } else {
+ err = 0;
+ }
+
+ return err;
+}
+
+static int lis3dh_acc_i2c_write(struct lis3dh_acc_data *acc, u8 * buf, int len)
+{
+ int err;
+ int tries = 0;
+
+ struct i2c_msg msgs[] = { {.addr = acc->client->addr,
+ .flags = acc->client->flags & I2C_M_TEN,
+ .len = len + 1,.buf = buf,},
+ };
+ do {
+ err = i2c_transfer(acc->client->adapter, msgs, 1);
+ if (err != 1)
+ msleep_interruptible(I2C_RETRY_DELAY);
+ } while ((err != 1) && (++tries < I2C_RETRIES));
+
+ if (err != 1) {
+ dev_err(&acc->client->dev, "write transfer error\n");
+ err = -EIO;
+ } else {
+ err = 0;
+ }
+
+ return err;
+}
+
+static int lis3dh_acc_hw_init(struct lis3dh_acc_data *acc)
+{
+ int err = -1;
+ u8 buf[7];
+
+ pr_debug("%s: hw init start\n", LIS3DH_ACC_DEV_NAME);
+
+ buf[0] = WHO_AM_I;
+ err = lis3dh_acc_i2c_read(acc, buf, 1);
+ if (err < 0)
+ goto error_firstread;
+ else
+ acc->hw_working = 1;
+ if (buf[0] != WHOAMI_LIS3DH_ACC) {
+ err = -1; /* choose the right coded error */
+ goto error_unknown_device;
+ }
+
+ buf[0] = CTRL_REG1;
+ buf[1] = acc->resume_state[RES_CTRL_REG1];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = TEMP_CFG_REG;
+ buf[1] = acc->resume_state[RES_TEMP_CFG_REG];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = FIFO_CTRL_REG;
+ buf[1] = acc->resume_state[RES_FIFO_CTRL_REG];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = (I2C_AUTO_INCREMENT | TT_THS);
+ buf[1] = acc->resume_state[RES_TT_THS];
+ buf[2] = acc->resume_state[RES_TT_LIM];
+ buf[3] = acc->resume_state[RES_TT_TLAT];
+ buf[4] = acc->resume_state[RES_TT_TW];
+ err = lis3dh_acc_i2c_write(acc, buf, 4);
+ if (err < 0)
+ goto error1;
+ buf[0] = TT_CFG;
+ buf[1] = acc->resume_state[RES_TT_CFG];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = (I2C_AUTO_INCREMENT | INT_THS1);
+ buf[1] = acc->resume_state[RES_INT_THS1];
+ buf[2] = acc->resume_state[RES_INT_DUR1];
+ err = lis3dh_acc_i2c_write(acc, buf, 2);
+ if (err < 0)
+ goto error1;
+ buf[0] = INT_CFG1;
+ buf[1] = acc->resume_state[RES_INT_CFG1];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = (I2C_AUTO_INCREMENT | INT_THS2);
+ buf[1] = acc->resume_state[RES_INT_THS2];
+ buf[2] = acc->resume_state[RES_INT_DUR2];
+ err = lis3dh_acc_i2c_write(acc, buf, 2);
+ if (err < 0)
+ goto error1;
+ buf[0] = INT_CFG2;
+ buf[1] = acc->resume_state[RES_INT_CFG2];
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error1;
+
+ buf[0] = (I2C_AUTO_INCREMENT | CTRL_REG2);
+ buf[1] = acc->resume_state[RES_CTRL_REG2];
+ buf[2] = acc->resume_state[RES_CTRL_REG3];
+ buf[3] = acc->resume_state[RES_CTRL_REG4];
+ buf[4] = acc->resume_state[RES_CTRL_REG5];
+ buf[5] = acc->resume_state[RES_CTRL_REG6];
+ err = lis3dh_acc_i2c_write(acc, buf, 5);
+ if (err < 0)
+ goto error1;
+
+ acc->hw_initialized = 1;
+ pr_debug("%s: hw init done\n", LIS3DH_ACC_DEV_NAME);
+ return 0;
+
+error_firstread:
+ acc->hw_working = 0;
+ dev_warn(&acc->client->dev, "Error reading WHO_AM_I: is device "
+ "available/working?\n");
+ goto error1;
+error_unknown_device:
+ dev_err(&acc->client->dev,
+ "device unknown. Expected: 0x%x,"
+ " Replies: 0x%x\n", WHOAMI_LIS3DH_ACC, buf[0]);
+error1:
+ acc->hw_initialized = 0;
+ dev_err(&acc->client->dev, "hw init error 0x%x,0x%x: %d\n", buf[0],
+ buf[1], err);
+ return err;
+}
+
+static void lis3dh_acc_device_power_off(struct lis3dh_acc_data *acc)
+{
+ int err;
+ u8 buf[2] = { CTRL_REG1, LIS3DH_ACC_PM_OFF };
+
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ dev_err(&acc->client->dev, "soft power off failed: %d\n", err);
+
+ if (acc->pdata->power_off) {
+ if (acc->irq1 != 0)
+ disable_irq_nosync(acc->irq1);
+ if (acc->irq2 != 0)
+ disable_irq_nosync(acc->irq2);
+ acc->pdata->power_off();
+ acc->hw_initialized = 0;
+ }
+ if (acc->hw_initialized) {
+ if (acc->irq1 != 0)
+ disable_irq_nosync(acc->irq1);
+ if (acc->irq2 != 0)
+ disable_irq_nosync(acc->irq2);
+ acc->hw_initialized = 0;
+ }
+}
+
+static int lis3dh_acc_device_power_on(struct lis3dh_acc_data *acc)
+{
+ int err = -1;
+
+ if (acc->pdata->power_on) {
+ err = acc->pdata->power_on();
+ if (err < 0) {
+ dev_err(&acc->client->dev,
+ "power_on failed: %d\n", err);
+ return err;
+ }
+ }
+
+ if (!acc->hw_initialized) {
+ err = lis3dh_acc_hw_init(acc);
+ if (acc->hw_working == 1 && err < 0) {
+ lis3dh_acc_device_power_off(acc);
+ return err;
+ }
+ }
+ return 0;
+}
+
+static irqreturn_t lis3dh_acc_isr1(int irq, void *dev)
+{
+ struct lis3dh_acc_data *acc = dev;
+
+ disable_irq_nosync(irq);
+ queue_work(acc->irq1_work_queue, &acc->irq1_work);
+
+ pr_debug("%s: isr1 queued\n", LIS3DH_ACC_DEV_NAME);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t lis3dh_acc_isr2(int irq, void *dev)
+{
+ struct lis3dh_acc_data *acc = dev;
+
+ disable_irq_nosync(irq);
+ queue_work(acc->irq2_work_queue, &acc->irq2_work);
+
+ pr_debug("%s: isr2 queued\n", LIS3DH_ACC_DEV_NAME);
+
+ return IRQ_HANDLED;
+}
+
+static void lis3dh_acc_irq1_work_func(struct work_struct *work)
+{
+
+ struct lis3dh_acc_data *acc =
+ container_of(work, struct lis3dh_acc_data, irq1_work);
+
+ /* TODO add interrupt service procedure.
+ ie:lis3dh_acc_get_int1_source(acc); */
+ ;
+ /* */
+ pr_debug("%s: IRQ1 triggered\n", LIS3DH_ACC_DEV_NAME);
+}
+
+static void lis3dh_acc_irq2_work_func(struct work_struct *work)
+{
+
+ /*struct lis3dh_acc_data *acc =
+ container_of(work, struct lis3dh_acc_data, irq2_work);
+ */
+ /* TODO add interrupt service procedure.
+ ie:lis3dh_acc_get_tap_source(acc); */
+ ;
+ /* */
+
+ pr_debug("%s: IRQ2 triggered\n", LIS3DH_ACC_DEV_NAME);
+}
+
+int lis3dh_acc_update_g_range(struct lis3dh_acc_data *acc, u8 new_g_range)
+{
+ int err;
+
+ u8 sensitivity;
+ u8 buf[2];
+ u8 updated_val;
+ u8 init_val;
+ u8 new_val;
+ u8 mask = LIS3DH_ACC_FS_MASK | HIGH_RESOLUTION;
+
+ pr_debug("%s\n", __func__);
+
+ switch (new_g_range) {
+ case LIS3DH_ACC_G_2G:
+
+ sensitivity = SENSITIVITY_2G;
+ break;
+ case LIS3DH_ACC_G_4G:
+
+ sensitivity = SENSITIVITY_4G;
+ break;
+ case LIS3DH_ACC_G_8G:
+
+ sensitivity = SENSITIVITY_8G;
+ break;
+ case LIS3DH_ACC_G_16G:
+
+ sensitivity = SENSITIVITY_16G;
+ break;
+ default:
+ dev_err(&acc->client->dev, "invalid g range requested: %u\n",
+ new_g_range);
+ return -EINVAL;
+ }
+
+ if (atomic_read(&acc->enabled)) {
+ /* Set configuration register 4, which contains g range setting
+ * NOTE: this is a straight overwrite because this driver does
+ * not use any of the other configuration bits in this
+ * register. Should this become untrue, we will have to read
+ * out the value and only change the relevant bits --XX----
+ * (marked by X) */
+ buf[0] = CTRL_REG4;
+ err = lis3dh_acc_i2c_read(acc, buf, 1);
+ if (err < 0)
+ goto error;
+ init_val = buf[0];
+ acc->resume_state[RES_CTRL_REG4] = init_val;
+ new_val = new_g_range | HIGH_RESOLUTION;
+ updated_val = ((mask & new_val) | ((~mask) & init_val));
+ buf[1] = updated_val;
+ buf[0] = CTRL_REG4;
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ goto error;
+ acc->resume_state[RES_CTRL_REG4] = updated_val;
+ acc->sensitivity = sensitivity;
+
+ pr_debug("%s sensitivity %d g-range %d\n", __func__, sensitivity,new_g_range);
+ }
+
+ return 0;
+error:
+ dev_err(&acc->client->dev, "update g range failed 0x%x,0x%x: %d\n",
+ buf[0], buf[1], err);
+
+ return err;
+}
+
+int lis3dh_acc_update_odr(struct lis3dh_acc_data *acc, int poll_interval_ms)
+{
+ int err = -1;
+ int i;
+ u8 config[2];
+
+ /* Convert the poll interval into an output data rate configuration
+ * that is as low as possible. The ordering of these checks must be
+ * maintained due to the cascading cut off values - poll intervals are
+ * checked from shortest to longest. At each check, if the next lower
+ * ODR cannot support the current poll interval, we stop searching */
+ for (i = ARRAY_SIZE(lis3dh_acc_odr_table) - 1; i >= 0; i--) {
+ if (lis3dh_acc_odr_table[i].cutoff_ms <= poll_interval_ms)
+ break;
+ }
+ config[1] = lis3dh_acc_odr_table[i].mask;
+
+ config[1] |= LIS3DH_ACC_ENABLE_ALL_AXES;
+
+ /* If device is currently enabled, we need to write new
+ * configuration out to it */
+ if (atomic_read(&acc->enabled)) {
+ config[0] = CTRL_REG1;
+ err = lis3dh_acc_i2c_write(acc, config, 1);
+ if (err < 0)
+ goto error;
+ acc->resume_state[RES_CTRL_REG1] = config[1];
+ }
+
+ return 0;
+
+error:
+ dev_err(&acc->client->dev, "update odr failed 0x%x,0x%x: %d\n",
+ config[0], config[1], err);
+
+ return err;
+}
+
+/* */
+
+static int lis3dh_acc_register_write(struct lis3dh_acc_data *acc, u8 * buf,
+ u8 reg_address, u8 new_value)
+{
+ int err = -1;
+
+ if (atomic_read(&acc->enabled)) {
+ /* Sets configuration register at reg_address
+ * NOTE: this is a straight overwrite */
+ buf[0] = reg_address;
+ buf[1] = new_value;
+ err = lis3dh_acc_i2c_write(acc, buf, 1);
+ if (err < 0)
+ return err;
+ }
+ return err;
+}
+
+static int lis3dh_acc_register_read(struct lis3dh_acc_data *acc, u8 * buf,
+ u8 reg_address)
+{
+
+ int err = -1;
+ buf[0] = (reg_address);
+ err = lis3dh_acc_i2c_read(acc, buf, 1);
+ return err;
+}
+
+static int lis3dh_acc_register_update(struct lis3dh_acc_data *acc, u8 * buf,
+ u8 reg_address, u8 mask,
+ u8 new_bit_values)
+{
+ int err = -1;
+ u8 init_val;
+ u8 updated_val;
+ err = lis3dh_acc_register_read(acc, buf, reg_address);
+ if (!(err < 0)) {
+ init_val = buf[1];
+ updated_val = ((mask & new_bit_values) | ((~mask) & init_val));
+ err = lis3dh_acc_register_write(acc, buf, reg_address,
+ updated_val);
+ }
+ return err;
+}
+
+/* */
+
+static int lis3dh_acc_get_acceleration_data(struct lis3dh_acc_data *acc,
+ int *xyz)
+{
+ int err = -1;
+ /* Data bytes from hardware xL, xH, yL, yH, zL, zH */
+ u8 acc_data[6];
+ /* x,y,z hardware data */
+ s16 hw_d[3] = { 0 };
+ s8 acc_data_term[3]={0};
+
+ acc_data[0] = (I2C_AUTO_INCREMENT | AXISDATA_REG);
+ err = lis3dh_acc_i2c_read(acc, acc_data, 6);
+
+ if (err < 0) {
+ pr_debug("%s I2C read error %d\n", LIS3DH_ACC_I2C_NAME,
+ err);
+ return err;
+ }
+
+ //hw_d[0] = (((s16) ((acc_data[1] << 8) | acc_data[0])) >> 4);
+ //hw_d[1] = (((s16) ((acc_data[3] << 8) | acc_data[2])) >> 4);
+ //hw_d[2] = (((s16) ((acc_data[5] << 8) | acc_data[4])) >> 4);
+ acc_data_term[0]=acc_data[1];
+ acc_data_term[1]=acc_data[3];
+ acc_data_term[2]=acc_data[5];
+
+ //printk("%s read 000 x=%d, y=%d, z=%d\n",
+ // LIS3DH_ACC_DEV_NAME, acc_data_term[0], acc_data_term[1], acc_data_term[2]);
+ //hw_d[0] = hw_d[0] * acc->sensitivity;
+ hw_d[0] = acc_data_term[0] * acc->sensitivity;
+ //hw_d[1] = hw_d[1] * acc->sensitivity;
+ hw_d[1] = acc_data_term[1] * acc->sensitivity;
+ //hw_d[2] = hw_d[2] * acc->sensitivity;
+ hw_d[2] = acc_data_term[2] * acc->sensitivity;
+
+ acc->pdata->negate_x = -acc->pdata->negate_x;
+ acc->pdata->negate_y = -acc->pdata->negate_y;
+
+ xyz[0] = ((acc->pdata->negate_x) ? (-hw_d[acc->pdata->axis_map_x])
+ : (hw_d[acc->pdata->axis_map_x]));
+ xyz[1] = ((acc->pdata->negate_y) ? (-hw_d[acc->pdata->axis_map_y])
+ : (hw_d[acc->pdata->axis_map_y]));
+ xyz[2] = ((acc->pdata->negate_z) ? (-hw_d[acc->pdata->axis_map_z])
+ : (hw_d[acc->pdata->axis_map_z]));
+
+ pr_debug("%s read x=%d, y=%d, z=%d\n",
+ LIS3DH_ACC_DEV_NAME, xyz[0], xyz[1], xyz[2]);
+
+ return err;
+}
+
+static void lis3dh_acc_report_values(struct lis3dh_acc_data *acc, int *xyz)
+{
+ input_report_abs(acc->input_dev, ABS_X, xyz[0]);
+ input_report_abs(acc->input_dev, ABS_Y, xyz[1]);
+ input_report_abs(acc->input_dev, ABS_Z, xyz[2]);
+ input_sync(acc->input_dev);
+}
+
+static int lis3dh_acc_enable(struct lis3dh_acc_data *acc)
+{
+ int err;
+ printk("sprd-gsensor: -- %s -- !\n",__func__);
+ if (!atomic_cmpxchg(&acc->enabled, 0, 1)) {
+ err = lis3dh_acc_device_power_on(acc);
+ if (err < 0) {
+ atomic_set(&acc->enabled, 0);
+ return err;
+ }
+
+ if (acc->hw_initialized) {
+ if (acc->irq1 != 0)
+ enable_irq(acc->irq1);
+ if (acc->irq2 != 0)
+ enable_irq(acc->irq2);
+ pr_debug("%s: power on: irq enabled\n",
+ LIS3DH_ACC_DEV_NAME);
+ }
+
+ schedule_delayed_work(&acc->input_work,
+ msecs_to_jiffies(acc->pdata->
+ poll_interval));
+ }
+ printk("sprd-gsensor: -- %s -- success!\n",__func__);
+ return 0;
+}
+
+static int lis3dh_acc_disable(struct lis3dh_acc_data *acc)
+{
+ printk("sprd-gsensor: -- %s -- \n",__func__);
+ if (atomic_cmpxchg(&acc->enabled, 1, 0)) {
+ cancel_delayed_work_sync(&acc->input_work);
+ lis3dh_acc_device_power_off(acc);
+ }
+
+ return 0;
+}
+
+static int lis3dh_acc_misc_open(struct inode *inode, struct file *file)
+{
+ int err;
+ err = nonseekable_open(inode, file);
+ if (err < 0)
+ return err;
+
+ file->private_data = lis3dh_acc_misc_data;
+
+ return 0;
+}
+
+static long lis3dh_acc_misc_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ u8 buf[4];
+ u8 mask;
+ u8 reg_address;
+ u8 bit_values;
+ int err;
+ int interval;
+ int xyz[3] = { 0 };
+ struct lis3dh_acc_data *acc = file->private_data;
+
+ switch (cmd) {
+ case LIS3DH_ACC_IOCTL_GET_DELAY:
+ interval = acc->pdata->poll_interval;
+ if (copy_to_user(argp, &interval, sizeof(interval)))
+ return -EFAULT;
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_DELAY:
+ if (copy_from_user(&interval, argp, sizeof(interval)))
+ return -EFAULT;
+ if (interval < 0 || interval > 1000)
+ return -EINVAL;
+
+ acc->pdata->poll_interval = max(interval,
+ acc->pdata->min_interval);
+ err = lis3dh_acc_update_odr(acc, acc->pdata->poll_interval);
+ /* TODO: if update fails poll is still set */
+ if (err < 0)
+ return err;
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_ENABLE:
+ if (copy_from_user(&interval, argp, sizeof(interval)))
+ return -EFAULT;
+ if (interval > 1)
+ return -EINVAL;
+ if (interval)
+ err = lis3dh_acc_enable(acc);
+ else
+ err = lis3dh_acc_disable(acc);
+ return err;
+ break;
+
+ case LIS3DH_ACC_IOCTL_GET_ENABLE:
+ interval = atomic_read(&acc->enabled);
+ if (copy_to_user(argp, &interval, sizeof(interval)))
+ return -EINVAL;
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_G_RANGE:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ bit_values = buf[0];
+ err = lis3dh_acc_update_g_range(acc, bit_values);
+ if (err < 0)
+ return err;
+ break;
+
+#ifdef INTERRUPT_MANAGEMENT
+ case LIS3DH_ACC_IOCTL_SET_CTRL_REG3:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = CTRL_REG3;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_CTRL_REG3] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_CTRL_REG3]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_CTRL_REG6:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = CTRL_REG6;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_CTRL_REG6] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_CTRL_REG6]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_DURATION1:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ reg_address = INT_DUR1;
+ mask = 0x7F;
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_DUR1] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_DUR1]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_THRESHOLD1:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ reg_address = INT_THS1;
+ mask = 0x7F;
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_THS1] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_THS1]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_CONFIG1:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = INT_CFG1;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_CFG1] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_CFG1]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_GET_SOURCE1:
+ err = lis3dh_acc_register_read(acc, buf, INT_SRC1);
+ if (err < 0)
+ return err;
+
+ pr_debug("INT1_SRC content: %d , 0x%x\n", buf[0], buf[0]);
+
+ if (copy_to_user(argp, buf, 1))
+ return -EINVAL;
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_DURATION2:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ reg_address = INT_DUR2;
+ mask = 0x7F;
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_DUR2] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_DUR2]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_THRESHOLD2:
+ if (copy_from_user(buf, argp, 1))
+ return -EFAULT;
+ reg_address = INT_THS2;
+ mask = 0x7F;
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_THS2] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_THS2]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_CONFIG2:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = INT_CFG2;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_INT_CFG2] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state
+ [RES_INT_CFG2]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_GET_SOURCE2:
+ err = lis3dh_acc_register_read(acc, buf, INT_SRC2);
+ if (err < 0)
+ return err;
+
+ pr_debug("INT2_SRC content: %d , 0x%x\n", buf[0], buf[0]);
+
+ if (copy_to_user(argp, buf, 1))
+ return -EINVAL;
+ break;
+
+ case LIS3DH_ACC_IOCTL_GET_TAP_SOURCE:
+ err = lis3dh_acc_register_read(acc, buf, TT_SRC);
+ if (err < 0)
+ return err;
+ pr_debug("TT_SRC content: %d , 0x%x\n", buf[0], buf[0]);
+
+ if (copy_to_user(argp, buf, 1)) {
+ pr_err("%s: %s error in copy_to_user \n",
+ LIS3DH_ACC_DEV_NAME, __func__);
+ return -EINVAL;
+ }
+ break;
+ case LIS3DH_ACC_IOCTL_GET_COOR_XYZ:
+ err = lis3dh_acc_get_acceleration_data(acc, xyz);
+ if (err < 0)
+ return err;
+
+ if (copy_to_user(argp, xyz, sizeof(xyz))) {
+ pr_err(" %s %d error in copy_to_user \n",
+ __func__, __LINE__);
+ return -EINVAL;
+ }
+ break;
+ case LIS3DH_ACC_IOCTL_SET_TAP_CFG:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_CFG;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_CFG] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_CFG]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_TAP_TLIM:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_LIM;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_LIM] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_LIM]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_TAP_THS:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_THS;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_THS] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_THS]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_TAP_TLAT:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_TLAT;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_TLAT] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_TLAT]));
+ break;
+
+ case LIS3DH_ACC_IOCTL_SET_TAP_TW:
+ if (copy_from_user(buf, argp, 2))
+ return -EFAULT;
+ reg_address = TT_TW;
+ mask = buf[1];
+ bit_values = buf[0];
+ err = lis3dh_acc_register_update(acc, (u8 *) arg, reg_address,
+ mask, bit_values);
+ if (err < 0)
+ return err;
+ acc->resume_state[RES_TT_TW] = ((mask & bit_values) |
+ (~mask & acc->
+ resume_state[RES_TT_TW]));
+ break;
+
+#endif /* INTERRUPT_MANAGEMENT */
+ case LIS3DH_ACC_IOCTL_GET_CHIP_ID:
+ {
+ u8 devid = 0;
+ u8 devinfo[DEVICE_INFO_LEN] = {0};
+ err = lis3dh_acc_register_read(acc, &devid, WHO_AM_I);
+ if (err < 0) {
+ printk("__func__, error read register WHO_AM_I\n", __func__);
+ return -EAGAIN;
+ }
+ sprintf(devinfo, "%s, %#x", DEVICE_INFO, devid);
+
+ if (copy_to_user(argp, devinfo, sizeof(devinfo))) {
+ printk("%s error in copy_to_user(IOCTL_GET_CHIP_ID)\n", __func__);
+ return -EINVAL;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct file_operations lis3dh_acc_misc_fops = {
+ .owner = THIS_MODULE,
+ .open = lis3dh_acc_misc_open,
+ .unlocked_ioctl = lis3dh_acc_misc_ioctl,
+};
+
+static struct miscdevice lis3dh_acc_misc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = LIS3DH_ACC_DEV_NAME,
+ .fops = &lis3dh_acc_misc_fops,
+};
+
+static void lis3dh_acc_input_work_func(struct work_struct *work)
+{
+ struct lis3dh_acc_data *acc;
+
+ int xyz[3] = { 0 };
+ int err;
+
+ acc = container_of((struct delayed_work *)work,
+ struct lis3dh_acc_data, input_work);
+
+ mutex_lock(&acc->lock);
+ err = lis3dh_acc_get_acceleration_data(acc, xyz);
+ if (err < 0)
+ dev_err(&acc->client->dev, "get_acceleration_data failed\n");
+ else
+ lis3dh_acc_report_values(acc, xyz);
+
+ schedule_delayed_work(&acc->input_work,
+ msecs_to_jiffies(acc->pdata->poll_interval));
+ mutex_unlock(&acc->lock);
+}
+
+#ifdef LIS3DH_OPEN_ENABLE
+int lis3dh_acc_input_open(struct input_dev *input)
+{
+ struct lis3dh_acc_data *acc = input_get_drvdata(input);
+
+ return lis3dh_acc_enable(acc);
+}
+
+void lis3dh_acc_input_close(struct input_dev *dev)
+{
+ struct lis3dh_acc_data *acc = input_get_drvdata(dev);
+
+ lis3dh_acc_disable(acc);
+}
+#endif
+
+static int lis3dh_acc_validate_pdata(struct lis3dh_acc_data *acc)
+{
+ acc->pdata->poll_interval = max(acc->pdata->poll_interval,
+ acc->pdata->min_interval);
+
+ if (acc->pdata->axis_map_x > 2 || acc->pdata->axis_map_y > 2
+ || acc->pdata->axis_map_z > 2) {
+ dev_err(&acc->client->dev, "invalid axis_map value "
+ "x:%u y:%u z%u\n", acc->pdata->axis_map_x,
+ acc->pdata->axis_map_y, acc->pdata->axis_map_z);
+ return -EINVAL;
+ }
+
+ /* Only allow 0 and 1 for negation boolean flag */
+ if (acc->pdata->negate_x > 1 || acc->pdata->negate_y > 1
+ || acc->pdata->negate_z > 1) {
+ dev_err(&acc->client->dev, "invalid negate value "
+ "x:%u y:%u z:%u\n", acc->pdata->negate_x,
+ acc->pdata->negate_y, acc->pdata->negate_z);
+ return -EINVAL;
+ }
+
+ /* Enforce minimum polling interval */
+ if (acc->pdata->poll_interval < acc->pdata->min_interval) {
+ dev_err(&acc->client->dev, "minimum poll interval violated\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int lis3dh_acc_input_init(struct lis3dh_acc_data *acc)
+{
+ int err;
+ /* Polling rx data when the interrupt is not used.*/
+ if (1 /*acc->irq1 == 0 && acc->irq1 == 0 */ ) {
+ INIT_DELAYED_WORK(&acc->input_work, lis3dh_acc_input_work_func);
+ }
+
+ acc->input_dev = input_allocate_device();
+ if (!acc->input_dev) {
+ err = -ENOMEM;
+ dev_err(&acc->client->dev, "input device allocate failed\n");
+ goto err0;
+ }
+#ifdef LIS3DH_ACC_OPEN_ENABLE
+ acc->input_dev->open = lis3dh_acc_input_open;
+ acc->input_dev->close = lis3dh_acc_input_close;
+#endif
+
+ input_set_drvdata(acc->input_dev, acc);
+
+ set_bit(EV_ABS, acc->input_dev->evbit);
+ /* next is used for interruptA sources data if the case */
+ set_bit(ABS_MISC, acc->input_dev->absbit);
+ /* next is used for interruptB sources data if the case */
+ set_bit(ABS_WHEEL, acc->input_dev->absbit);
+
+ input_set_abs_params(acc->input_dev, ABS_X, -G_MAX, G_MAX, 0, 0);
+ input_set_abs_params(acc->input_dev, ABS_Y, -G_MAX, G_MAX, 0, 0);
+ input_set_abs_params(acc->input_dev, ABS_Z, -G_MAX, G_MAX, 0, 0);
+ /* next is used for interruptA sources data if the case */
+ input_set_abs_params(acc->input_dev, ABS_MISC, INT_MIN, INT_MAX, 0, 0);
+ /* next is used for interruptB sources data if the case */
+ input_set_abs_params(acc->input_dev, ABS_WHEEL, INT_MIN, INT_MAX, 0, 0);
+
+ acc->input_dev->name = "accelerometer";
+
+ err = input_register_device(acc->input_dev);
+ if (err) {
+ dev_err(&acc->client->dev,
+ "unable to register input polled device %s\n",
+ acc->input_dev->name);
+ goto err1;
+ }
+
+ return 0;
+
+err1:
+ input_free_device(acc->input_dev);
+err0:
+ return err;
+}
+
+static void lis3dh_acc_input_cleanup(struct lis3dh_acc_data *acc)
+{
+ input_unregister_device(acc->input_dev);
+ input_free_device(acc->input_dev);
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void lis3dh_early_suspend(struct early_suspend *es);
+static void lis3dh_early_resume(struct early_suspend *es);
+#endif
+
+#ifdef CONFIG_OF
+static struct lis3dh_acc_platform_data *lis3dh_acc_parse_dt(struct device *dev)
+{
+ struct lis3dh_acc_platform_data *pdata;
+ struct device_node *np = dev->of_node;
+ int ret;
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(dev, "Could not allocate struct lis3dh_acc_platform_data");
+ return NULL;
+ }
+ ret = of_property_read_u32(np, "poll_interval", &pdata->poll_interval);
+ if(ret){
+ dev_err(dev, "fail to get poll_interval\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "min_interval", &pdata->min_interval);
+ if(ret){
+ dev_err(dev, "fail to get min_interval\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "g_range", &pdata->g_range);
+ if(ret){
+ dev_err(dev, "fail to get g_range\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "axis_map_x", &pdata->axis_map_x);
+ if(ret){
+ dev_err(dev, "fail to get axis_map_x\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "axis_map_y", &pdata->axis_map_y);
+ if(ret){
+ dev_err(dev, "fail to get axis_map_y\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "axis_map_z", &pdata->axis_map_z);
+ if(ret){
+ dev_err(dev, "fail to get axis_map_z\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "negate_x", &pdata->negate_x);
+ if(ret){
+ dev_err(dev, "fail to get negate_x\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "negate_y", &pdata->negate_y);
+ if(ret){
+ dev_err(dev, "fail to get negate_y\n");
+ goto fail;
+ }
+ ret = of_property_read_u32(np, "negate_z", &pdata->negate_z);
+ if(ret){
+ dev_err(dev, "fail to get negate_z\n");
+ goto fail;
+ }
+ return pdata;
+fail:
+ kfree(pdata);
+ return NULL;
+}
+#endif
+static int lis3dh_acc_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+
+ struct lis3dh_acc_data *acc;
+ struct lis3dh_acc_platform_data *pdata = client->dev.platform_data;
+ int xyz[3] = { 0 };
+
+ int err = -1;
+ int tempvalue;
+ printk("sprd-gsensor: -- %s -- start !\n",__func__);
+ pr_debug("%s: probe start.\n", LIS3DH_ACC_DEV_NAME);
+
+#ifdef CONFIG_OF
+ struct device_node *np = client->dev.of_node;
+ if (np && !pdata){
+ pdata = lis3dh_acc_parse_dt(&client->dev);
+ if(pdata){
+ client->dev.platform_data = pdata;
+ }
+ if(!pdata){
+ err = -ENOMEM;
+ goto exit_alloc_platform_data_failed;
+ }
+ }
+#endif
+ /*
+ if (client->dev.platform_data == NULL) {
+ dev_err(&client->dev, "platform data is NULL. exiting.\n");
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+ */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "client not i2c capable\n");
+ err = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA)) {
+ dev_err(&client->dev, "client not smb-i2c capable:2\n");
+ err = -EIO;
+ goto exit_check_functionality_failed;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) {
+ dev_err(&client->dev, "client not smb-i2c capable:3\n");
+ err = -EIO;
+ goto exit_check_functionality_failed;
+ }
+ /*
+ * OK. From now, we presume we have a valid client. We now create the
+ * client structure, even though we cannot fill it completely yet.
+ */
+
+ acc = kzalloc(sizeof(struct lis3dh_acc_data), GFP_KERNEL);
+ if (acc == NULL) {
+ err = -ENOMEM;
+ dev_err(&client->dev,
+ "failed to allocate memory for module data: "
+ "%d\n", err);
+ goto exit_alloc_data_failed;
+ }
+
+ mutex_init(&acc->lock);
+ mutex_lock(&acc->lock);
+
+ acc->client = client;
+ lis3dh_i2c_client = client;
+ i2c_set_clientdata(client, acc);
+
+ pr_debug("%s: %s has set irq1 to irq: %d\n",
+ LIS3DH_ACC_DEV_NAME, __func__, acc->irq1);
+ pr_debug("%s: %s has set irq2 to irq: %d\n",
+ LIS3DH_ACC_DEV_NAME, __func__, acc->irq2);
+
+ gpio_request(GSENSOR_GINT1_GPI, "GSENSOR_INT1");
+ gpio_request(GSENSOR_GINT2_GPI, "GSENSOR_INT2");
+ acc->irq1 = 0; /* gpio_to_irq(GSENSOR_GINT1_GPI); */
+ acc->irq2 = 0; /* gpio_to_irq(GSENSOR_GINT2_GPI); */
+
+ if (acc->irq1 != 0) {
+ pr_debug("%s request irq1\n", __func__);
+ err =
+ request_irq(acc->irq1, lis3dh_acc_isr1, IRQF_TRIGGER_RISING,
+ "lis3dh_acc_irq1", acc);
+ if (err < 0) {
+ dev_err(&client->dev, "request irq1 failed: %d\n", err);
+ goto err_mutexunlockfreedata;
+ }
+ disable_irq_nosync(acc->irq1);
+
+ INIT_WORK(&acc->irq1_work, lis3dh_acc_irq1_work_func);
+ acc->irq1_work_queue =
+ create_singlethread_workqueue("lis3dh_acc_wq1");
+ if (!acc->irq1_work_queue) {
+ err = -ENOMEM;
+ dev_err(&client->dev, "cannot create work queue1: %d\n",
+ err);
+ goto err_free_irq1;
+ }
+ }
+
+ if (acc->irq2 != 0) {
+ err =
+ request_irq(acc->irq2, lis3dh_acc_isr2, IRQF_TRIGGER_RISING,
+ "lis3dh_acc_irq2", acc);
+ if (err < 0) {
+ dev_err(&client->dev, "request irq2 failed: %d\n", err);
+ goto err_destoyworkqueue1;
+ }
+ disable_irq_nosync(acc->irq2);
+
+/* Create workqueue for IRQ.*/
+
+ INIT_WORK(&acc->irq2_work, lis3dh_acc_irq2_work_func);
+ acc->irq2_work_queue =
+ create_singlethread_workqueue("lis3dh_acc_wq2");
+ if (!acc->irq2_work_queue) {
+ err = -ENOMEM;
+ dev_err(&client->dev, "cannot create work queue2: %d\n",
+ err);
+ goto err_free_irq2;
+ }
+ }
+
+ acc->pdata = kmalloc(sizeof(*acc->pdata), GFP_KERNEL);
+ if (acc->pdata == NULL) {
+ err = -ENOMEM;
+ dev_err(&client->dev,
+ "failed to allocate memory for pdata: %d\n", err);
+ goto err_destoyworkqueue2;
+ }
+
+ memcpy(acc->pdata, client->dev.platform_data, sizeof(*acc->pdata));
+
+ err = lis3dh_acc_validate_pdata(acc);
+ if (err < 0) {
+ dev_err(&client->dev, "failed to validate platform data\n");
+ goto exit_kfree_pdata;
+ }
+
+ err = lis3dh_acc_device_power_on(acc);
+ if (err < 0) {
+ dev_err(&client->dev, "power on failed: %d\n", err);
+ goto err2;
+ }
+
+#if 1
+ if (i2c_smbus_read_byte(client) < 0) {
+ pr_err("i2c_smbus_read_byte error!!\n");
+ goto err_destoyworkqueue2;
+ } else {
+ pr_debug("%s Device detected!\n", LIS3DH_ACC_DEV_NAME);
+ }
+#endif
+ /* read chip id */
+
+ tempvalue = i2c_smbus_read_word_data(client, WHO_AM_I);
+
+ if ((tempvalue & 0x00FF) == WHOAMI_LIS3DH_ACC) {
+ pr_debug("%s I2C driver registered!\n",
+ LIS3DH_ACC_DEV_NAME);
+ } else {
+ acc->client = NULL;
+ pr_debug("I2C driver not registered!"
+ " Device unknown 0x%x\n", tempvalue);
+ goto err_destoyworkqueue2;
+ }
+
+ i2c_set_clientdata(client, acc);
+
+ if (acc->pdata->init) {
+ err = acc->pdata->init();
+ if (err < 0) {
+ dev_err(&client->dev, "init failed: %d\n", err);
+ goto err2;
+ }
+ }
+
+ memset(acc->resume_state, 0, ARRAY_SIZE(acc->resume_state));
+
+ acc->resume_state[RES_CTRL_REG1] = LIS3DH_ACC_ENABLE_ALL_AXES;
+ acc->resume_state[RES_CTRL_REG2] = 0x00;
+ acc->resume_state[RES_CTRL_REG3] = 0x00;
+ acc->resume_state[RES_CTRL_REG4] = 0x00;
+ acc->resume_state[RES_CTRL_REG5] = 0x00;
+ acc->resume_state[RES_CTRL_REG6] = 0x00;
+
+ acc->resume_state[RES_TEMP_CFG_REG] = 0x00;
+ acc->resume_state[RES_FIFO_CTRL_REG] = 0x00;
+ acc->resume_state[RES_INT_CFG1] = 0x00;
+ acc->resume_state[RES_INT_THS1] = 0x00;
+ acc->resume_state[RES_INT_DUR1] = 0x00;
+ acc->resume_state[RES_INT_CFG2] = 0x00;
+ acc->resume_state[RES_INT_THS2] = 0x00;
+ acc->resume_state[RES_INT_DUR2] = 0x00;
+
+ acc->resume_state[RES_TT_CFG] = 0x00;
+ acc->resume_state[RES_TT_THS] = 0x00;
+ acc->resume_state[RES_TT_LIM] = 0x00;
+ acc->resume_state[RES_TT_TLAT] = 0x00;
+ acc->resume_state[RES_TT_TW] = 0x00;
+
+
+ atomic_set(&acc->enabled, 1);
+
+ err = lis3dh_acc_update_g_range(acc, acc->pdata->g_range);
+ if (err < 0) {
+ dev_err(&client->dev, "update_g_range failed\n");
+ goto err_power_off;
+ }
+
+ err = lis3dh_acc_update_odr(acc, acc->pdata->poll_interval);
+ if (err < 0) {
+ dev_err(&client->dev, "update_odr failed\n");
+ goto err_power_off;
+ }
+
+ err = lis3dh_acc_input_init(acc);
+ if (err < 0) {
+ dev_err(&client->dev, "input init failed\n");
+ goto err_power_off;
+ }
+ lis3dh_acc_misc_data = acc;
+
+ err = misc_register(&lis3dh_acc_misc_device);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "misc LIS3DH_ACC_DEV_NAME register failed\n");
+ goto err_input_cleanup;
+ }
+
+ lis3dh_acc_device_power_off(acc);
+
+ /* As default, do not report information */
+ atomic_set(&acc->enabled, 0);
+/*
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ acc->early_suspend.suspend = lis3dh_early_suspend;
+ acc->early_suspend.resume = lis3dh_early_resume;
+ acc->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;
+ register_early_suspend(&acc->early_suspend);
+#endif
+*/
+ mutex_unlock(&acc->lock);
+ dev_info(&client->dev, "###%s###\n", __func__);
+ printk("sprd-gsensor: -- %s -- success !\n",__func__);
+
+
+ err = lis3dh_acc_get_acceleration_data(acc, xyz);
+
+
+ return 0;
+
+err_input_cleanup:
+ lis3dh_acc_input_cleanup(acc);
+err_power_off:
+ lis3dh_acc_device_power_off(acc);
+err2:
+ if (acc->pdata->exit)
+ acc->pdata->exit();
+exit_kfree_pdata:
+ kfree(acc->pdata);
+err_destoyworkqueue2:
+ if (acc->irq2_work_queue)
+ destroy_workqueue(acc->irq2_work_queue);
+err_free_irq2:
+ if (acc->irq2) {
+ free_irq(acc->irq2, acc);
+ gpio_free(GSENSOR_GINT2_GPI);
+ }
+err_destoyworkqueue1:
+ if (acc->irq1_work_queue)
+ destroy_workqueue(acc->irq1_work_queue);
+err_free_irq1:
+ if (acc->irq1) {
+ free_irq(acc->irq1, acc);
+ gpio_free(GSENSOR_GINT1_GPI);
+ }
+err_mutexunlockfreedata:
+ i2c_set_clientdata(client, NULL);
+ mutex_unlock(&acc->lock);
+ kfree(acc);
+ lis3dh_acc_misc_data = NULL;
+exit_alloc_data_failed:
+exit_check_functionality_failed:
+ pr_err("%s: Driver Init failed\n", LIS3DH_ACC_DEV_NAME);
+exit_alloc_platform_data_failed:
+ return err;
+}
+
+static int lis3dh_acc_remove(struct i2c_client *client)
+{
+ /* TODO: revisit ordering here once _probe order is finalized */
+ struct lis3dh_acc_data *acc = i2c_get_clientdata(client);
+ if (acc != NULL) {
+ if (acc->irq1) {
+ free_irq(acc->irq1, acc);
+ gpio_free(GSENSOR_GINT1_GPI);
+ }
+ if (acc->irq2) {
+ free_irq(acc->irq2, acc);
+ gpio_free(GSENSOR_GINT2_GPI);
+ }
+
+ if (acc->irq1_work_queue)
+ destroy_workqueue(acc->irq1_work_queue);
+ if (acc->irq2_work_queue)
+ destroy_workqueue(acc->irq2_work_queue);
+ misc_deregister(&lis3dh_acc_misc_device);
+ lis3dh_acc_input_cleanup(acc);
+ lis3dh_acc_device_power_off(acc);
+ if (acc->pdata->exit)
+ acc->pdata->exit();
+ kfree(acc->pdata);
+ kfree(acc);
+ }
+
+ return 0;
+}
+
+static int lis3dh_acc_resume(struct i2c_client *client)
+{
+ struct lis3dh_acc_data *acc = i2c_get_clientdata(client);
+ printk("sprd-gsensor: -- %s -- !\n",__func__);
+ dev_dbg(&client->dev, "###%s###\n", __func__);
+
+ if (acc != NULL && acc->on_before_suspend)
+ return lis3dh_acc_enable(acc);
+
+ return 0;
+}
+
+static int lis3dh_acc_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct lis3dh_acc_data *acc = i2c_get_clientdata(client);
+ printk("sprd-gsensor: -- %s -- !\n",__func__);
+ dev_dbg(&client->dev, "###%s###\n", __func__);
+
+ if (acc != NULL) {
+ acc->on_before_suspend = atomic_read(&acc->enabled);
+ return lis3dh_acc_disable(acc);
+ }
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+
+static void lis3dh_early_suspend(struct early_suspend *es)
+{
+ lis3dh_acc_suspend(lis3dh_i2c_client, (pm_message_t) {
+ .event = 0});
+}
+
+static void lis3dh_early_resume(struct early_suspend *es)
+{
+ lis3dh_acc_resume(lis3dh_i2c_client);
+}
+
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+
+static const struct i2c_device_id lis3dh_acc_id[]
+= { {LIS3DH_ACC_DEV_NAME, 0}, {}, };
+
+MODULE_DEVICE_TABLE(i2c, lis3dh_acc_id);
+
+static const struct of_device_id lis3dh_acc_of_match[] = {
+ { .compatible = "ST,lis3dh_acc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, lis3dh_acc_of_match);
+static struct i2c_driver lis3dh_acc_driver = {
+ .driver = {
+ .name = LIS3DH_ACC_I2C_NAME,
+ .of_match_table = lis3dh_acc_of_match,
+ },
+ .probe = lis3dh_acc_probe,
+ .remove = lis3dh_acc_remove,
+ .resume = lis3dh_acc_resume,
+ .suspend = lis3dh_acc_suspend,
+ .id_table = lis3dh_acc_id,
+};
+
+
+static int __init lis3dh_acc_init(void)
+{
+ pr_debug("%s accelerometer driver: init\n", LIS3DH_ACC_I2C_NAME);
+
+ return i2c_add_driver(&lis3dh_acc_driver);
+}
+
+static void __exit lis3dh_acc_exit(void)
+{
+ pr_debug("%s accelerometer driver exit\n", LIS3DH_ACC_DEV_NAME);
+
+ i2c_del_driver(&lis3dh_acc_driver);
+ return;
+}
+
+module_init(lis3dh_acc_init);
+module_exit(lis3dh_acc_exit);
+
+MODULE_DESCRIPTION("lis3dh accelerometer misc driver");
+MODULE_AUTHOR("STMicroelectronics");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/ltr_558als.c b/drivers/input/misc/ltr_558als.c
new file mode 100644
index 00000000..0251cdb9
--- /dev/null
+++ b/drivers/input/misc/ltr_558als.c
@@ -0,0 +1,1236 @@
+/*
+ * File:ltr_558als.c
+ * Author:Yaochuan Li <yaochuan.li@spreadtrum.com>
+ * Created:2013-03-18
+ * Description:LTR-558ALS Driver
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/sysctl.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/earlysuspend.h>
+#include <linux/platform_device.h>
+#include <asm/uaccess.h>
+#include <linux/wakelock.h>
+#include <linux/i2c/ltr_558als.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+
+#define LTR588_DBG
+#define LTR558_ADAPTIVE
+#ifdef LTR588_DBG
+#define ENTER printk(KERN_INFO "[LTR588_DBG] func: %s line: %04d ", __func__, __LINE__)
+#define PRINT_DBG(x...) printk(KERN_INFO "[LTR588_DBG] " x)
+#define PRINT_INFO(x...) printk(KERN_INFO "[LTR588_INFO] " x)
+#define PRINT_WARN(x...) printk(KERN_INFO "[LTR588_WARN] " x)
+#define PRINT_ERR(format,x...) printk(KERN_ERR "[LTR588_ERR] func: %s line: %04d info: " format, __func__, __LINE__, ## x)
+#else
+#define ENTER
+#define PRINT_DBG(x...)
+#define PRINT_INFO(x...) printk(KERN_INFO "[LTR588_INFO] " x)
+#define PRINT_WARN(x...) printk(KERN_INFO "[LTR588_WARN] " x)
+#define PRINT_ERR(format,x...) printk(KERN_ERR "[LTR588_ERR] func: %s line: %04d info: " format, __func__, __LINE__, ## x)
+#endif
+
+typedef struct tag_ltr558 {
+ struct input_dev *input;
+ struct i2c_client *client;
+ struct work_struct work;
+ struct workqueue_struct *ltr_work_queue;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend ltr_early_suspend;
+#endif
+} ltr558_t, *ltr558_p;
+
+static int ps_threshold = 0;
+static int ps_threshold_high = 600;
+static int ps_threshold_low = 500;
+static int dyna_cali = 2047;
+
+static int p_flag = 0;
+static int l_flag = 0;
+static u8 p_gainrange = PS_RANGE4;
+static u8 l_gainrange = ALS_RANGE1_320;
+//static int ps_threshold = 0;
+static struct i2c_client *this_client = NULL;
+static int LTR_PLS_MODE = 0;
+
+#ifdef LTR558_ADAPTIVE
+#define DEBOUNCE 10
+#define MIN_SPACING 120
+#define DIVEDE 10
+static int ps_max_filter[5] = {0};
+static int ps_min_filter[5] = {0};
+static int max_index = 0;
+static int min_index = 0;
+static int ps_min = 0;
+static int ps_max = 0;
+#endif
+
+static int ltr558_reg_init(void);
+
+static int ltr558_i2c_read_bytes(u8 index, u8 *rx_buff, u8 length)
+{
+ int ret = -1;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = this_client->addr,//chip address, 7bit
+ .flags = 0,//write
+ .len = 1,
+ .buf = &index,
+ },
+ {
+ .addr = this_client->addr,
+ .flags = I2C_M_RD,//read
+ .len = length,
+ .buf = rx_buff,
+ },
+ };
+
+ ret = i2c_transfer(this_client->adapter, msgs, 2);
+ if(ret != 2) {
+ PRINT_ERR("READ ERROR!ret=%d\n", ret);
+ }
+ return ret;
+}
+
+static int ltr558_i2c_write_bytes(u8 *tx_buff, u8 length)
+{
+ int ret = -1;
+ struct i2c_msg msgs[1];
+
+ msgs[0].addr = this_client->addr;
+ msgs[0].flags = 0;
+ msgs[0].len = length;
+ msgs[0].buf = tx_buff;
+
+ ret = i2c_transfer(this_client->adapter, msgs, 1);
+ if(ret != 1) {
+ PRINT_ERR("WRITE ERROR!ret=%d\n", ret);
+ }
+ return ret;
+}
+
+static int ltr558_i2c_read_2_bytes(u8 reg)
+{
+ int ret = 0;
+ u8 data[2] = {0};
+
+ ret = ltr558_i2c_read_bytes(reg, data, 2);
+ if(ret != 2) {
+ PRINT_ERR("READ ERROR!ret=%d\n", ret);
+ return -1;
+ }
+
+ ret = data[1];
+ ret = ( ret<<8) |data[0];
+
+ return ret;
+}
+
+static int ltr558_i2c_read_1_byte(u8 reg)
+{
+ int ret = 0;
+ u8 data[1] = {0};
+
+ ret = ltr558_i2c_read_bytes(reg, data, 1);
+ if(ret != 2) {
+ PRINT_ERR("READ ERROR!ret=%d\n", ret);
+ return -1;
+ }
+
+ ret = data[0];
+ return ret;
+}
+
+static int ltr558_i2c_write_2_bytes(u8 reg, u16 value)
+{
+ int ret = 0;
+ u8 data[3] = {0};
+
+ data[0] = reg;
+ data[1] = value & 0x00FF;
+ data[2] = value >> 8;
+
+ ret = ltr558_i2c_write_bytes(data, 3);
+ if(ret != 1) {
+ PRINT_ERR("WRITE ERROR!ret=%d\n", ret);
+ return -1;
+ }
+ return 1;
+}
+
+static int ltr558_i2c_write_1_byte(u8 reg, u8 value)
+{
+ int ret = 0;
+ u8 data[2] = {0};
+
+ data[0] = reg;
+ data[1] = value;
+
+ ret = ltr558_i2c_write_bytes(data, 2);
+ if(ret != 1) {
+ PRINT_ERR("WRITE ERROR!ret=%d\n", ret);
+ return -1;
+ }
+ return 1;
+}
+
+static int ltr_read_chip_info(struct i2c_client *client, char *buf)
+{
+ if(NULL == buf) {
+ return -1;
+ }
+ if(NULL == client) {
+ *buf = 0;
+ return -2;
+ }
+
+ if( LTR_PLS_553 == LTR_PLS_MODE)
+ {
+ sprintf(buf, "LTR558ALS");
+ printk("[LTR553] ltr_read_chip_info LTR553ALS\n");
+ }
+ if(LTR_PLS_558 == LTR_PLS_MODE)
+ {
+ sprintf(buf, "LTR558ALS");
+ printk("[LTR558] ltr_read_chip_info LTR558ALS\n");
+ }
+ return 0;
+}
+
+static int dynamic_cali(void)
+{
+ int i=0;
+ int j=0;
+ int val=0;
+ int data_total=0;
+ int noise=0;
+
+ for (i = 0; i < 3; i++)
+ {
+ msleep(10);
+ val = ltr558_i2c_read_2_bytes(LTR558_PS_DATA_0);
+ data_total += val;
+ }
+ noise = data_total/3;
+
+ if(noise < (dyna_cali + 500))
+ {
+ if(noise < 1500)
+ {
+ ps_threshold_high = noise + 250;
+ ps_threshold_low = noise + 220;
+ }else{
+ ps_threshold_high = 1800;
+ ps_threshold_low = 1600;
+ }
+ }
+ PRINT_INFO("ltr558_ps_enable, ps_threshold_high=%d, ps_threshold_low=%d\n", ps_threshold_high, ps_threshold_low);
+}
+
+static int ltr558_ps_enable(u8 gainrange)
+{
+ int ret = -1;
+ u8 setgain;
+ ltr558_t *ltr_558als = (ltr558_t *)i2c_get_clientdata(this_client);
+
+ if(LTR_PLS_553 == LTR_PLS_MODE)
+ {
+ switch (gainrange)
+ {
+ case PS_553_RANGE16:
+ setgain = MODE_PS_553_ON_Gain16;
+ break;
+ case PS_553_RANGE32:
+ setgain = MODE_PS_553_ON_Gain32;
+ break;
+ case PS_553_RANGE64:
+ setgain = MODE_PS_553_ON_Gain64;
+ break;
+ default:
+ setgain = MODE_PS_553_ON_Gain16;
+ break;
+ }
+ }
+ else
+ {
+ switch (gainrange) {
+ case PS_558_RANGE1:
+ setgain = MODE_PS_558_ON_Gain1;
+ break;
+ case PS_558_RANGE2:
+ setgain = MODE_PS_558_ON_Gain4;
+ break;
+ case PS_558_RANGE4:
+ setgain = MODE_PS_558_ON_Gain8;
+ break;
+ case PS_558_RANGE8:
+ setgain = MODE_PS_558_ON_Gain16;
+ break;
+ default:
+ setgain = MODE_PS_558_ON_Gain8;
+ break;
+ }
+ }
+
+ ret = ltr558_i2c_write_1_byte(LTR558_PS_CONTR, setgain);
+ mdelay(WAKEUP_DELAY);
+
+ if(setgain != ltr558_i2c_read_1_byte(LTR558_PS_CONTR))
+ {
+ ret = ltr558_i2c_write_1_byte(LTR558_PS_CONTR, setgain);
+ mdelay(WAKEUP_DELAY);
+ }
+
+ /*input report init*/
+ input_report_abs(ltr_558als->input, ABS_DISTANCE, 1);
+ input_sync(ltr_558als->input);
+
+ dynamic_cali();
+
+ PRINT_INFO("ltr558_ps_enable, gainrange=%d, ret=%d\n", gainrange, ret);
+ if(ret >= 0)
+ return 0;
+ else
+ return ret;
+}
+
+static int ltr558_ps_disable(void)
+{
+ int ret = -1;
+ ltr558_t *ltr_558als = (ltr558_t *)i2c_get_clientdata(this_client);
+ ret = ltr558_i2c_write_1_byte(LTR558_PS_CONTR, MODE_PS_STANDBY);
+
+ /*input report init*/
+ input_report_abs(ltr_558als->input, ABS_DISTANCE, 1);
+ input_sync(ltr_558als->input);
+
+ PRINT_INFO("ltr558_ps_disable, ret=%d\n", ret);
+ if(ret >= 0)
+ return 0;
+ else
+ return ret;
+}
+
+static int ltr558_als_enable(u8 gainrange)
+{
+ int ret = -1;
+ u8 setgain;
+
+ if(LTR_PLS_553 == LTR_PLS_MODE)
+ {
+ switch (gainrange)
+ {
+ case ALS_553_RANGE1_64K:
+ setgain = MODE_ALS_553_ON_Range1;
+ break;
+
+ case ALS_553_RANGE2_32K:
+ setgain = MODE_ALS_553_ON_Range2;
+ break;
+
+ case ALS_553_RANGE4_16K:
+ setgain = MODE_ALS_553_ON_Range4;
+ break;
+
+ case ALS_553_RANGE8_8K:
+ setgain = MODE_ALS_553_ON_Range8;
+ break;
+
+ case ALS_553_RANGE48_1K3:
+ setgain = MODE_ALS_553_ON_Range48;
+ break;
+
+ case ALS_553_RANGE96_600:
+ setgain = MODE_ALS_553_ON_Range96;
+ break;
+
+ default:
+ setgain = MODE_ALS_553_ON_Range1;
+ break;
+ }
+ }
+ else
+ {
+ switch (gainrange)
+ {
+ case ALS_558_RANGE1_320:
+ setgain = MODE_ALS_558_ON_Range1;
+ break;
+
+ case ALS_558_RANGE2_64K:
+ setgain = MODE_ALS_558_ON_Range2;
+ break;
+
+ default:
+ setgain = MODE_ALS_558_ON_Range1;
+ break;
+ }
+ }
+
+ ret = ltr558_i2c_write_1_byte(LTR558_ALS_CONTR, setgain);
+ mdelay(WAKEUP_DELAY);
+
+ if(setgain != ltr558_i2c_read_1_byte(LTR558_ALS_CONTR))
+ {
+ ret = ltr558_i2c_write_1_byte(LTR558_ALS_CONTR, setgain);
+ mdelay(WAKEUP_DELAY);
+ }
+
+ ltr558_i2c_read_1_byte(LTR558_ALS_PS_STATUS);
+ ltr558_i2c_read_2_bytes(LTR558_PS_DATA_0);
+ ltr558_i2c_read_2_bytes(LTR558_ALS_DATA_CH1);
+ ltr558_i2c_read_2_bytes(LTR558_ALS_DATA_CH0);
+
+ PRINT_INFO("ltr558_als_enable, gainrange=%d, ret = %d\n", gainrange, ret);
+ if(ret >= 0)
+ return 0;
+ else
+ return ret;
+}
+
+// Put ALS into Standby mode
+static int ltr558_als_disable(void)
+{
+ int ret = -1;
+ ret = ltr558_i2c_write_1_byte(LTR558_ALS_CONTR, MODE_ALS_STANDBY);
+ PRINT_INFO("ltr558_als_disable, ret = %d \n", ret);
+ if(ret >= 0)
+ return 0;
+ else
+ return ret;
+}
+
+static int ltr558_als_read(int gainrange)
+{
+ int luxdata_int;
+ int luxdata_flt, ratio;
+ int ch0, ch1;
+
+ /*IMPORTANT!CH1 MUST BE READ FIRST!*/
+ ch1 = ltr558_i2c_read_2_bytes(LTR558_ALS_DATA_CH1);
+ ch0 = ltr558_i2c_read_2_bytes(LTR558_ALS_DATA_CH0);
+
+ PRINT_DBG("ch0=%d, ch1=%d\n", ch0, ch1);
+ if (0 == ch0)
+ ratio = 100;
+ else
+ ratio = (ch1 * 100) / ch0;
+
+ // Compute Lux data from ALS data (ch0 and ch1)
+ // For Ratio < 0.69:
+ // 1.3618*CH0 – 1.5*CH1
+ // For 0.69 <= Ratio < 1:
+ // 0.57*CH0 – 0.345*CH1
+ // For high gain, divide the calculated lux by 150.
+ if (ratio < 69) {
+ luxdata_flt = (13618 * ch0) - (15000 * ch1);
+ luxdata_flt = luxdata_flt / 10000;
+ } else if ((ratio >= 69) && (ratio < 100)) {
+ luxdata_flt = (5700 * ch0) - (3450 * ch1);
+ luxdata_flt = luxdata_flt / 10000;
+ } else {
+ luxdata_flt = 0;
+ }
+
+ // For Range1
+ //if (gainrange == ALS_RANGE1_320)
+ // luxdata_flt = luxdata_flt / 150;
+
+ luxdata_int = luxdata_flt / 80;
+ return luxdata_int;
+}
+
+static int ltr558_open(struct inode *inode, struct file *file)
+{
+ PRINT_INFO("ltr558_open\n");
+ return 0;
+}
+
+static int ltr558_release(struct inode *inode, struct file *file)
+{
+ PRINT_INFO("ltr558_release\n");
+ return 0;
+}
+
+static long ltr558_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int flag, err;
+ char strbuf[256];
+ PRINT_INFO("cmd = %d, %d\n", _IOC_NR(cmd), cmd);
+ switch (cmd) {
+ case LTR_IOCTL_SET_PFLAG: {
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ PRINT_INFO("LTR_IOCTL_SET_PFLAG = %d\n", flag);
+ if (1 == flag) {
+ ltr558_reg_init();
+ msleep(20);
+ if (ltr558_ps_enable(p_gainrange))
+ return -EIO;
+ } else if (0 == flag) {
+ if (ltr558_ps_disable())
+ return -EIO;
+ } else {
+ return -EINVAL;
+ }
+ p_flag = flag;
+ }
+ break;
+ case LTR_IOCTL_SET_LFLAG: {
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ PRINT_INFO("LTR_IOCTL_SET_LFLAG = %d\n", flag);
+ if (1 == flag) {
+ ltr558_reg_init();
+ msleep(20);
+ if (ltr558_als_enable(l_gainrange))
+ return -EIO;
+ } else if (0 == flag) {
+ if (ltr558_als_disable())
+ return -EIO;
+ } else {
+ return -EINVAL;
+ }
+ l_flag = flag;
+ }
+ break;
+ case LTR_IOCTL_GET_CHIPINFO: {
+ err = ltr_read_chip_info(this_client, strbuf);
+ if(err < 0)
+ return -EFAULT;
+ if(copy_to_user(argp, strbuf, strlen(strbuf)+1))
+ return -EFAULT;
+ }
+ break;
+ case LTR_IOCTL_GET_PFLAG: {
+ flag = p_flag;
+ PRINT_INFO("LTR_IOCTL_GET_PFLAG = %d\n", flag);
+ if (copy_to_user(argp, &flag, sizeof(flag)))
+ return -EFAULT;
+ }
+ break;
+ case LTR_IOCTL_GET_LFLAG: {
+ flag = l_flag;
+ PRINT_INFO("LTR_IOCTL_GET_LFLAG = %d\n", flag);
+ if (copy_to_user(argp, &flag, sizeof(flag)))
+ return -EFAULT;
+ }
+ break;
+ default:
+ PRINT_ERR("unknown command: 0x%08X (%d)\n", cmd, cmd);
+ break;
+ }
+ return 0;
+}
+
+static struct file_operations ltr558_fops = {
+ .owner = THIS_MODULE,
+ .open = ltr558_open,
+ .release = ltr558_release,
+ .unlocked_ioctl = ltr558_ioctl,
+};
+
+static struct miscdevice ltr558_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = LTR558_I2C_NAME,
+ .fops = &ltr558_fops,
+};
+
+static int get_min_value(int* array, int size)
+{
+ int ret = 0;
+ int i = 0;
+ ret = array[0];
+ for(i=1; i<size; i++) {
+ if(ret > array[i])
+ ret = array[i];
+ }
+ return ret;
+}
+
+static int get_max_value(int* array, int size)
+{
+ int ret = 0;
+ int i = 0;
+ ret = array[0];
+ for(i=1; i<size; i++) {
+ if(ret < array[i])
+ ret = array[i];
+ }
+ return ret;
+}
+
+static void ltr558_work(struct work_struct *work)
+{
+ int status = 0;
+ int value = 0;
+ ltr558_t *pls = container_of(work, ltr558_t, work);
+
+ status = ltr558_i2c_read_1_byte(LTR558_ALS_PS_STATUS);
+ PRINT_DBG("LTR558_ALS_PS_STATUS = 0x%02X\n", status);
+
+ if ((0x03 == (status & 0x03)) && (LTR_PLS_MODE == LTR_PLS_558)) {/*is 558 PS*/
+ value = ltr558_i2c_read_2_bytes(LTR558_PS_DATA_0);
+ PRINT_DBG("LTR_PLS_MODE is pls 558, LTR558_PS_DATA_0 = %d\n", value);
+ if (value >= ps_threshold_high) { // 3cm //high
+ /*ltr558_i2c_write_1_byte(LTR558_PS_THRES_UP_0, 0xff);
+ ltr558_i2c_write_1_byte(LTR558_PS_THRES_UP_1, 0x07);
+ ltr558_i2c_write_1_byte(LTR558_PS_THRES_LOW_0, 0xDC);
+ ltr558_i2c_write_1_byte(LTR558_PS_THRES_LOW_1, 0x05);
+ */
+
+ ltr558_i2c_write_2_bytes(LTR558_PS_THRES_UP, 0x07FF);
+ ltr558_i2c_write_2_bytes(LTR558_PS_THRES_LOW, ps_threshold_low);
+
+ input_report_abs(pls->input, ABS_DISTANCE, 0);
+ input_sync(pls->input);
+ } else if (value <= ps_threshold_low) { // 5cm //low
+ /*ltr558_i2c_write_1_byte(LTR558_PS_THRES_UP_0, 0x0E);
+ ltr558_i2c_write_1_byte(LTR558_PS_THRES_UP_1, 0x06);
+ ltr558_i2c_write_1_byte(LTR558_PS_THRES_LOW_0, 0x00);
+ ltr558_i2c_write_1_byte(LTR558_PS_THRES_LOW_1, 0x00);
+ */
+
+ if(dyna_cali > 20 && value <(dyna_cali - 100))
+ {
+ if(value < 1500)
+ {
+ ps_threshold_high = value + 250;
+ ps_threshold_low = value + 220;
+ }else{
+ ps_threshold_high = 1800;
+ ps_threshold_low = 1600;
+ }
+ }
+
+ ltr558_i2c_write_2_bytes(LTR558_PS_THRES_UP, ps_threshold_high);
+ ltr558_i2c_write_2_bytes(LTR558_PS_THRES_LOW, 0x0000);
+
+ input_report_abs(pls->input, ABS_DISTANCE, 1);
+ input_sync(pls->input);
+ }
+ }
+
+ if ((0x03 == (status & 0x03)) && (LTR_PLS_MODE == LTR_PLS_553)) {/*is 553 PS*/
+ value = ltr558_i2c_read_2_bytes(LTR558_PS_DATA_0);
+ PRINT_DBG("LTR_PLS_MODE is pls 553 \n");
+#ifdef LTR558_ADAPTIVE
+ /*threshold detect process*/
+ if(0 == ps_max || 0 == ps_min) {
+ ps_min = value;
+ ps_max = value;
+ ps_threshold = ps_min + MIN_SPACING;
+ }
+ if(value > ps_max) {
+ ps_max_filter[max_index++] = value;
+ if(ARRAY_SIZE(ps_max_filter) == max_index) {
+ ps_max = get_min_value(ps_max_filter, ARRAY_SIZE(ps_max_filter));
+ ps_threshold = ((ps_max - ps_min) / DIVEDE) + ps_min;
+ if(ps_threshold - ps_min < MIN_SPACING)
+ ps_threshold = ps_min + MIN_SPACING;
+ max_index = 0;
+ }
+ min_index = 0;
+ } else if(value < ps_min) {
+ ps_min_filter[min_index++] = value;
+ if(ARRAY_SIZE(ps_min_filter) == min_index) {
+ ps_min = get_max_value(ps_min_filter, ARRAY_SIZE(ps_min_filter));
+ ps_threshold = ((ps_max - ps_min) / DIVEDE) + ps_min;
+ if(ps_threshold - ps_min < MIN_SPACING)
+ ps_threshold = ps_min + MIN_SPACING;
+ min_index = 0;
+ }
+ max_index = 0;
+ } else {
+ max_index = 0;
+ min_index = 0;
+ }
+ /*threshold detect process end*/
+
+ if (value > (ps_threshold + DEBOUNCE)) {
+ input_report_abs(pls->input, ABS_DISTANCE, 0);
+ input_sync(pls->input);
+ } else if (value < (ps_threshold - DEBOUNCE)) {
+ input_report_abs(pls->input, ABS_DISTANCE, 1);
+ input_sync(pls->input);
+ }
+ PRINT_DBG("PS INT: PS_DATA_VAL = 0x%04X(%4d) ps_min=%4d ps_max=%4d ps_threshold=%4d\n", \
+ value, value, ps_min, ps_max, ps_threshold);
+#else
+ if (value >= ps_threshold) {
+ ltr558_i2c_write_2_bytes(LTR558_PS_THRES_UP, 0x07FF);
+ ltr558_i2c_write_2_bytes(LTR558_PS_THRES_LOW, ps_threshold);
+ input_report_abs(pls->input, ABS_DISTANCE, 0);
+ input_sync(pls->input);
+ } else if (value <= ps_threshold) {
+ ltr558_i2c_write_2_bytes(LTR558_PS_THRES_UP, ps_threshold);
+ ltr558_i2c_write_2_bytes(LTR558_PS_THRES_LOW, 0x0000);
+ input_report_abs(pls->input, ABS_DISTANCE, 1);
+ input_sync(pls->input);
+ }
+ PRINT_DBG("PS INT: PS_DATA_VAL = 0x%04X ( %d )\n", value, value);
+#endif
+ }
+ if (0x0c == (status & 0x0c)) {/*is ALS*/
+ value = ltr558_als_read(l_gainrange);
+ PRINT_DBG("ALS INT: ALS_DATA_VAL = 0x%04X(%4d)\n", value, value);
+ input_report_abs(pls->input, ABS_MISC, value);
+ input_sync(pls->input);
+ }
+
+ enable_irq(pls->client->irq);
+}
+
+static irqreturn_t ltr558_irq_handler(int irq, void *dev_id)
+{
+ ltr558_t *pls = (ltr558_t *) dev_id;
+
+ disable_irq_nosync(pls->client->irq);
+ queue_work(pls->ltr_work_queue, &pls->work);
+ return IRQ_HANDLED;
+}
+
+static int ltr558_sw_reset(void)
+{
+ int ret = 0;
+ ret = ltr558_i2c_write_1_byte(LTR558_ALS_CONTR, 0x04);
+ if(1 == ret)
+ PRINT_INFO("ltr558_sw_reset success\n");
+ else
+ PRINT_ERR("ltr558_sw_reset failed! ret = %d\n", ret);
+ return ret;
+}
+
+static int ltr558_reg_init(void)
+{
+ int ret = 0;
+// ret = ltr558_sw_reset();
+// if(1 != ret) {
+// PRINT_ERR("ltr558_reg_init failed! ret = %d\n", ret);
+// return ret;
+// }
+// mdelay(PON_DELAY);
+
+ if(LTR_PLS_558 == LTR_PLS_MODE){
+ //set: LED Pulse Frequency=60KHz,LED Duty Cycle=100%,LED Peak Current=50mA
+ ltr558_i2c_write_1_byte(LTR558_PS_LED, 0x7B);
+ //set: LED Pulse Count=15
+ ltr558_i2c_write_1_byte(LTR558_PS_N_PULSES , 0x1f);
+ //set: PS Measurement Repeat Rate: 0x00=50ms 0x02=100ms
+ ltr558_i2c_write_1_byte(LTR558_PS_MEAS_RATE, 0x00);
+ //set: ALS Integration Time=100ms, ALS Measurement Repeat Rate=100ms
+ ltr558_i2c_write_1_byte(LTR558_ALS_MEAS_RATE, 0x13);
+ ltr558_i2c_write_1_byte(LTR558_INTERRUPT_PERSIST,0x02);
+ //set: INT MODE=updated after every measurement,
+ //=active low level,
+ //=both PS & ALS measurement can trigger interrupt
+ ltr558_i2c_write_1_byte(LTR558_INTERRUPT, 0x0B);
+ }else{
+ //set: LED Pulse Frequency=60KHz,LED Duty Cycle=100%,LED Peak Current=50mA
+ ltr558_i2c_write_1_byte(LTR558_PS_LED, 0x7B);
+ //set: LED Pulse Count=15
+ ltr558_i2c_write_1_byte(LTR558_PS_N_PULSES , 0x08);
+ //set: PS Measurement Repeat Rate: 0x00=50ms 0x02=100ms
+ ltr558_i2c_write_1_byte(LTR558_PS_MEAS_RATE, 0x00);
+ //set: ALS Integration Time=100ms, ALS Measurement Repeat Rate=100ms
+ ltr558_i2c_write_1_byte(LTR558_ALS_MEAS_RATE, 0x03);
+ ltr558_i2c_write_1_byte(LTR558_INTERRUPT_PERSIST,0x12);
+ //set: INT MODE=updated after every measurement,
+ //=active low level,
+ //=both PS & ALS measurement can trigger interrupt
+ ltr558_i2c_write_1_byte(LTR558_INTERRUPT, 0x03);
+ }
+ //set: PS Persist=2, ALS Persist=2
+ //ltr558_i2c_write_1_byte(LTR558_INTERRUPT_PERSIST, 0x22);
+ //set: PS Threshold
+ //ltr558_i2c_write_2_bytes(LTR558_PS_THRES_UP, 0x0000);
+ //ltr558_i2c_write_2_bytes(LTR558_PS_THRES_LOW, 0x0000);
+ //set: ALS Threshold
+ //ltr558_i2c_write_2_bytes(LTR558_ALS_THRES_UP, 0x0000);
+ //ltr558_i2c_write_2_bytes(LTR558_ALS_THRES_LOW, 0x0001);/*DO NOT set:0x0000*/
+
+ // ps
+ ltr558_i2c_write_1_byte(LTR558_PS_THRES_UP_0, 0x00);
+ ltr558_i2c_write_1_byte(LTR558_PS_THRES_UP_1, 0x03);
+ ltr558_i2c_write_1_byte(LTR558_PS_THRES_LOW_0, 0xf0);
+ ltr558_i2c_write_1_byte(LTR558_PS_THRES_LOW_1, 0x02);
+
+ // als
+ ltr558_i2c_write_1_byte(LTR558_ALS_THRES_UP_0, 0x00);
+ ltr558_i2c_write_1_byte(LTR558_ALS_THRES_UP_1, 0x00);
+ ltr558_i2c_write_1_byte(LTR558_ALS_THRES_LOW_0, 0x01);
+ ltr558_i2c_write_1_byte(LTR558_ALS_THRES_LOW_1, 0x00);
+
+ msleep(WAKEUP_DELAY);
+
+ PRINT_INFO("ltr558_reg_init success!\n");
+ return ret;
+}
+
+static int ltr558_version_check(void)
+{
+ int part_id = -1;
+ int manufacturer_id = -1;
+ part_id = ltr558_i2c_read_1_byte(LTR558_PART_NUMBER_ID);
+ manufacturer_id = ltr558_i2c_read_1_byte(LTR558_MANUFACTURER_ID);
+ PRINT_INFO("PART_ID: 0x%02X MANUFACTURER_ID: 0x%02X\n", part_id, manufacturer_id);
+
+ if(part_id == 0x80 && manufacturer_id == 0x05) {
+ PRINT_INFO("I'm LTR558, and I'm working now\n");
+ return 0;
+ }else if(part_id == 0x92 && manufacturer_id == 0x05)
+ {
+ PRINT_INFO("I'm LTR553, and I'm working now\n");
+ return 0;
+ }
+ else if(part_id < 0 || manufacturer_id < 0) {
+ PRINT_ERR("can't read who am I\n");
+ return -1;
+ } else {
+ PRINT_ERR("I'm working, but I'm NOT LTR558\n");
+ return -1;
+ }
+}
+
+static ssize_t ltr_558als_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff)
+{
+ return sprintf(buff, "ps_min=%4d ps_max=%4d ps_threshold=%4d\n", ps_min, ps_max, ps_threshold);
+}
+
+#ifdef LTR588_DBG
+static int str2int(char *p, char *end)
+{
+ int result = 0;
+ int len = end - p + 1;
+ int c;
+
+ for(; len > 0; len--, p++) {
+ if(p > end)
+ return -1;
+ c = *p - '0';
+ if((unsigned int)c >= 10)
+ return -1;
+ result = result * 10 + c;
+ }
+ return result;
+}
+
+/*parse a int from the string*/
+static int get_int(char *p, u8 max_len)
+{
+ char *start = p;
+ char *end =NULL;
+
+ if(max_len > 60) {
+ PRINT_ERR("CMD ERROR!the max length of the cmd should less than 60\n");
+ return -1;
+ }
+ for(; max_len > 0; max_len--, p++) {
+ if(*p >= '0' && *p <= '9') {
+ start = p;
+ break;
+ }
+ }
+ if(0 == max_len)
+ return -1;
+
+ end = start;
+
+ for(; max_len > 0; max_len--, p++) {
+ if(*p >= '0' && *p <= '9') {
+ end = p;
+ continue;
+ }
+ break;
+ }
+
+ return str2int(start, end);
+}
+
+static ssize_t ltr_558als_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buff, size_t n)
+{
+ char *buff_temp = NULL;
+
+ buff_temp = kmalloc(n, GFP_KERNEL);
+ if(NULL == buff_temp){
+ PRINT_ERR("kmalloc err\n");
+ return n;
+ }
+
+ memcpy(buff_temp, buff, n);
+
+ ps_threshold = get_int(buff_temp, n);
+ if(0x07FF < ps_threshold) {
+ ps_threshold = LTR558_PS_THRESHOLD;
+ PRINT_WARN("ps_threshold value over range, replace with the default value\n");
+ }
+ PRINT_INFO("set ps_threshold = 0x%04X ( %d )\n", ps_threshold, ps_threshold);
+
+ ltr558_i2c_write_2_bytes(LTR558_PS_THRES_UP, 0x0000);
+ ltr558_i2c_write_2_bytes(LTR558_PS_THRES_LOW, 0x0000);
+
+ if(buff_temp != NULL)
+ kfree(buff_temp);
+
+ return n;
+}
+
+static int break_loop = 0;
+static ssize_t ltr_558als_val_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff)
+{
+ int measured_val = 0;
+ ltr558_t *pls = (ltr558_t *)i2c_get_clientdata(this_client);
+
+ disable_irq_nosync(pls->client->irq);
+ ltr558_als_enable(l_gainrange);
+ ltr558_ps_enable(p_gainrange);
+
+ break_loop = 0;
+ while(1) {
+ measured_val = ltr558_i2c_read_2_bytes(LTR558_PS_DATA_0);
+ PRINT_DBG("PS_DATA_VAL = 0x%04X ( %d )\n", measured_val, measured_val);
+ input_report_abs(pls->input, ABS_DISTANCE, measured_val);
+ input_sync(pls->input);
+
+ measured_val = ltr558_als_read(l_gainrange);
+ PRINT_DBG("ALS_DATA_VAL = 0x%04X ( %d )\n", measured_val, measured_val);
+ input_report_abs(pls->input, ABS_MISC, measured_val);
+ input_sync(pls->input);
+
+ if(1 == break_loop)
+ break;
+ msleep(200);
+ }
+ ltr558_ps_disable();
+ ltr558_als_disable();
+ enable_irq(pls->client->irq);
+
+ return 0;
+}
+
+static ssize_t ltr_558als_val_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buff, size_t n)
+{
+ break_loop = 1;
+ PRINT_INFO("stop show value\n");
+ ltr558_i2c_write_2_bytes(LTR558_PS_THRES_UP, 0x0000);
+ ltr558_i2c_write_2_bytes(LTR558_PS_THRES_LOW, 0x0000);
+ return n;
+}
+#endif
+
+static struct kobject *ltr_558als_kobj;
+static struct kobj_attribute ltr_558als_attr =
+ __ATTR(ps_threshold, 0644, ltr_558als_show, NULL);
+#ifdef LTR588_DBG
+static struct kobj_attribute ltr_558als_val_attr =
+ __ATTR(show_val, 0644, ltr_558als_val_show, ltr_558als_val_store);
+#endif
+
+static int ltr_558als_sysfs_init(struct input_dev *input_dev)
+{
+ int ret = -1;
+
+ ltr_558als_kobj = kobject_create_and_add("ltr_558als", &(input_dev->dev.kobj));
+ if (ltr_558als_kobj == NULL) {
+ ret = -ENOMEM;
+ PRINT_ERR("register sysfs failed. ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = sysfs_create_file(ltr_558als_kobj, &ltr_558als_attr.attr);
+ if (ret) {
+ PRINT_ERR("create sysfs failed. ret = %d\n", ret);
+ return ret;
+ }
+#ifdef LTR588_DBG
+ ret = sysfs_create_file(ltr_558als_kobj, &ltr_558als_val_attr.attr);
+ if (ret) {
+ PRINT_ERR("create sysfs failed. ret = %d\n", ret);
+ return ret;
+ }
+#endif
+ return ret;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void ltr558_early_suspend(struct early_suspend *handler)
+{
+ PRINT_INFO("ltr558_early_suspend\n");
+}
+
+static void ltr558_late_resume(struct early_suspend *handler)
+{
+ PRINT_INFO("ltr558_late_resume\n");
+}
+#endif
+
+static int ltr558_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int ret = 0;
+ ltr558_t *ltr_558als = NULL;
+ struct input_dev *input_dev = NULL;
+ struct ltr558_pls_platform_data *pdata = client->dev.platform_data;
+ int chip_id = 0;
+#ifdef CONFIG_OF
+ struct device_node *np = client->dev.of_node;
+ if (np && !pdata){
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev, "Could not allocate struct ltr558_pls_platform_data");
+ goto exit_allocate_pdata_failed;
+ }
+ pdata->irq_gpio_number = of_get_gpio(np, 0);
+ if(pdata->irq_gpio_number < 0){
+ dev_err(&client->dev, "fail to get irq_gpio_number\n");
+ kfree(pdata);
+ goto exit_irq_gpio_read_fail;
+ }
+ client->dev.platform_data = pdata;
+ }
+#endif
+ ret = gpio_request(pdata->irq_gpio_number, LTR558_PLS_IRQ_PIN);
+ if(ret) {
+ PRINT_ERR("gpio_request failed!\n");
+ goto exit_gpio_request_failed;
+ }
+ gpio_direction_input(pdata->irq_gpio_number);
+ client->irq = gpio_to_irq(pdata->irq_gpio_number);
+ PRINT_INFO("client->irq = %d\n", client->irq);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ PRINT_ERR("i2c_check_functionality failed!\n");
+ ret = -ENODEV;
+ goto exit_i2c_check_functionality_failed;
+ }
+
+ ltr_558als = kzalloc(sizeof(ltr558_t), GFP_KERNEL);
+ if (!ltr_558als) {
+ PRINT_ERR("kzalloc failed!\n");
+ ret = -ENOMEM;
+ goto exit_kzalloc_failed;
+ }
+
+ i2c_set_clientdata(client, ltr_558als);
+ ltr_558als->client = client;
+ this_client = client;
+
+ chip_id = ltr558_i2c_read_1_byte(LTR558_PART_ID);
+ if(chip_id < 0)
+ {
+ PRINT_ERR("ltr558 or ltr553 read chip_id failed!\n");
+ ret = -ENOMEM;
+ goto exit_read_chip_id_failed;
+ }
+ if(LTR_553_PART_ID == chip_id)
+ {
+ LTR_PLS_MODE = LTR_PLS_553;
+ p_gainrange = PS_553_RANGE16;
+ l_gainrange = ALS_553_RANGE1_64K;
+ }
+ else
+ {
+ LTR_PLS_MODE = LTR_PLS_558;
+ p_gainrange = PS_558_RANGE4;
+ l_gainrange = ALS_558_RANGE1_320;
+ }
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ PRINT_ERR("input_allocate_device failed!\n");
+ ret = -ENOMEM;
+ goto exit_input_allocate_device_failed;
+ }
+
+ input_dev->name = LTR558_INPUT_DEV;
+ input_dev->phys = LTR558_INPUT_DEV;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0010;
+ ltr_558als->input = input_dev;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ input_set_abs_params(input_dev, ABS_DISTANCE, 0, 1, 0, 0);
+ input_set_abs_params(input_dev, ABS_MISC, 0, 100001, 0, 0);
+
+ ret = input_register_device(input_dev);
+ if (ret < 0) {
+ PRINT_ERR("input_register_device failed!\n");
+ input_free_device(input_dev);
+ input_dev = NULL;
+ goto exit_input_register_device_failed;
+ }
+
+ ret = misc_register(&ltr558_device);
+ if (ret) {
+ PRINT_ERR("misc_register failed!\n");
+ goto exit_misc_register_failed;
+ }
+
+ if (ltr558_reg_init() < 0) {
+ PRINT_ERR("ltr558_reg_init failed!\n");
+ ret = -1;
+ goto exit_ltr558_reg_init_failed;
+ }
+
+ ret = ltr558_version_check();
+ if(ret) {
+ PRINT_ERR("ltr558_version_check failed!\n");
+ goto exit_ltr558_version_check_failed;
+ }
+
+ INIT_WORK(&ltr_558als->work, ltr558_work);
+ ltr_558als->ltr_work_queue = create_singlethread_workqueue(LTR558_I2C_NAME);
+ if (!ltr_558als->ltr_work_queue) {
+ PRINT_ERR("create_singlethread_workqueue failed!\n");
+ goto exit_create_singlethread_workqueue_failed;
+ }
+
+ if (client->irq > 0) {
+ ret = request_irq(client->irq, ltr558_irq_handler, IRQ_TYPE_LEVEL_LOW | IRQF_NO_SUSPEND, client->name, ltr_558als);
+ if (ret < 0) {
+ PRINT_ERR("request_irq failed!\n");
+ goto exit_request_irq_failed;
+ }
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ltr_558als->ltr_early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB + 25;
+ ltr_558als->ltr_early_suspend.suspend = ltr558_early_suspend;
+ ltr_558als->ltr_early_suspend.resume = ltr558_late_resume;
+ register_early_suspend(&ltr_558als->ltr_early_suspend);
+#endif
+
+ ret = ltr_558als_sysfs_init(input_dev);
+ if(ret) {
+ PRINT_ERR("ltr_558als_sysfs_init failed!\n");
+ goto exit_ltr_558als_sysfs_init_failed;
+ }
+
+ PRINT_INFO("probe success!\n");
+ return 0;
+
+exit_ltr_558als_sysfs_init_failed:
+ free_irq(ltr_558als->client->irq, ltr_558als);
+exit_request_irq_failed:
+ destroy_workqueue(ltr_558als->ltr_work_queue);
+ ltr_558als->ltr_work_queue = NULL;
+exit_create_singlethread_workqueue_failed:
+exit_ltr558_version_check_failed:
+exit_ltr558_reg_init_failed:
+ misc_deregister(&ltr558_device);
+exit_misc_register_failed:
+ input_unregister_device(input_dev);
+exit_input_register_device_failed:
+exit_input_allocate_device_failed:
+exit_read_chip_id_failed:
+ kfree(ltr_558als);
+ ltr_558als = NULL;
+exit_kzalloc_failed:
+exit_i2c_check_functionality_failed:
+ gpio_free(pdata->irq_gpio_number);
+exit_gpio_request_failed:
+exit_irq_gpio_read_fail:
+exit_allocate_pdata_failed:
+ PRINT_ERR("probe failed!\n");
+ return ret;
+}
+
+static int ltr558_remove(struct i2c_client *client)
+{
+ ltr558_t *ltr_558als = i2c_get_clientdata(client);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ unregister_early_suspend(&ltr_558als->ltr_early_suspend);
+#endif
+
+ flush_workqueue(ltr_558als->ltr_work_queue);
+ destroy_workqueue(ltr_558als->ltr_work_queue);
+ ltr_558als->ltr_work_queue = NULL;
+
+ misc_deregister(&ltr558_device);
+ input_unregister_device(ltr_558als->input);
+ input_free_device(ltr_558als->input);
+ ltr_558als->input = NULL;
+ free_irq(ltr_558als->client->irq, ltr_558als);
+ gpio_free(irq_to_gpio(ltr_558als->client->irq));
+ kfree(ltr_558als);
+ ltr_558als = NULL;
+ this_client = NULL;
+
+ PRINT_INFO("ltr558_remove\n");
+ return 0;
+}
+
+static const struct i2c_device_id ltr558_id[] = {
+ {LTR558_I2C_NAME, 0},
+ {}
+};
+
+static const struct of_device_id ltr558_of_match[] = {
+ { .compatible = "LITEON,ltr_558als", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ltr558_of_match);
+static struct i2c_driver ltr558_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = LTR558_I2C_NAME,
+ .of_match_table = ltr558_of_match,
+ },
+ .probe = ltr558_probe,
+ .remove = ltr558_remove,
+ .id_table = ltr558_id,
+};
+
+static int __init ltr558_init(void)
+{
+ int ret = -1;
+ ret = i2c_add_driver(&ltr558_driver);
+ if(ret) {
+ PRINT_ERR("i2c_add_driver failed!\n");
+ return ret;
+ }
+ return ret;
+}
+
+static void __exit ltr558_exit(void)
+{
+ i2c_del_driver(&ltr558_driver);
+}
+
+late_initcall(ltr558_init);
+module_exit(ltr558_exit);
+
+MODULE_AUTHOR("Yaochuan Li <yaochuan.li@spreadtrum.com>");
+MODULE_DESCRIPTION("Proximity&Light Sensor LTR558ALS DRIVER");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/m68kspkr.c b/drivers/input/misc/m68kspkr.c
new file mode 100644
index 00000000..b40ee4b4
--- /dev/null
+++ b/drivers/input/misc/m68kspkr.c
@@ -0,0 +1,151 @@
+/*
+ * m68k beeper driver for Linux
+ *
+ * Copyright (c) 2002 Richard Zidlicky
+ * Copyright (c) 2002 Vojtech Pavlik
+ * Copyright (c) 1992 Orest Zborowski
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <asm/machdep.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Richard Zidlicky <rz@linux-m68k.org>");
+MODULE_DESCRIPTION("m68k beeper driver");
+MODULE_LICENSE("GPL");
+
+static struct platform_device *m68kspkr_platform_device;
+
+static int m68kspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ unsigned int count = 0;
+
+ if (type != EV_SND)
+ return -1;
+
+ switch (code) {
+ case SND_BELL: if (value) value = 1000;
+ case SND_TONE: break;
+ default: return -1;
+ }
+
+ if (value > 20 && value < 32767)
+ count = 1193182 / value;
+
+ mach_beep(count, -1);
+
+ return 0;
+}
+
+static int m68kspkr_probe(struct platform_device *dev)
+{
+ struct input_dev *input_dev;
+ int err;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_dev->name = "m68k beeper";
+ input_dev->phys = "m68k/generic";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x001f;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &dev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_SND);
+ input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+ input_dev->event = m68kspkr_event;
+
+ err = input_register_device(input_dev);
+ if (err) {
+ input_free_device(input_dev);
+ return err;
+ }
+
+ platform_set_drvdata(dev, input_dev);
+
+ return 0;
+}
+
+static int m68kspkr_remove(struct platform_device *dev)
+{
+ struct input_dev *input_dev = platform_get_drvdata(dev);
+
+ input_unregister_device(input_dev);
+ platform_set_drvdata(dev, NULL);
+ /* turn off the speaker */
+ m68kspkr_event(NULL, EV_SND, SND_BELL, 0);
+
+ return 0;
+}
+
+static void m68kspkr_shutdown(struct platform_device *dev)
+{
+ /* turn off the speaker */
+ m68kspkr_event(NULL, EV_SND, SND_BELL, 0);
+}
+
+static struct platform_driver m68kspkr_platform_driver = {
+ .driver = {
+ .name = "m68kspkr",
+ .owner = THIS_MODULE,
+ },
+ .probe = m68kspkr_probe,
+ .remove = m68kspkr_remove,
+ .shutdown = m68kspkr_shutdown,
+};
+
+static int __init m68kspkr_init(void)
+{
+ int err;
+
+ if (!mach_beep) {
+ printk(KERN_INFO "m68kspkr: no lowlevel beep support\n");
+ return -ENODEV;
+ }
+
+ err = platform_driver_register(&m68kspkr_platform_driver);
+ if (err)
+ return err;
+
+ m68kspkr_platform_device = platform_device_alloc("m68kspkr", -1);
+ if (!m68kspkr_platform_device) {
+ err = -ENOMEM;
+ goto err_unregister_driver;
+ }
+
+ err = platform_device_add(m68kspkr_platform_device);
+ if (err)
+ goto err_free_device;
+
+ return 0;
+
+ err_free_device:
+ platform_device_put(m68kspkr_platform_device);
+ err_unregister_driver:
+ platform_driver_unregister(&m68kspkr_platform_driver);
+
+ return err;
+}
+
+static void __exit m68kspkr_exit(void)
+{
+ platform_device_unregister(m68kspkr_platform_device);
+ platform_driver_unregister(&m68kspkr_platform_driver);
+}
+
+module_init(m68kspkr_init);
+module_exit(m68kspkr_exit);
diff --git a/drivers/input/misc/max8925_onkey.c b/drivers/input/misc/max8925_onkey.c
new file mode 100644
index 00000000..f9179b25
--- /dev/null
+++ b/drivers/input/misc/max8925_onkey.c
@@ -0,0 +1,201 @@
+/**
+ * MAX8925 ONKEY driver
+ *
+ * Copyright (C) 2009 Marvell International Ltd.
+ * Haojian Zhuang <haojian.zhuang@marvell.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/max8925.h>
+#include <linux/slab.h>
+
+#define SW_INPUT (1 << 7) /* 0/1 -- up/down */
+#define HARDRESET_EN (1 << 7)
+#define PWREN_EN (1 << 7)
+
+struct max8925_onkey_info {
+ struct input_dev *idev;
+ struct i2c_client *i2c;
+ struct device *dev;
+ unsigned int irq[2];
+};
+
+/*
+ * MAX8925 gives us an interrupt when ONKEY is pressed or released.
+ * max8925_set_bits() operates I2C bus and may sleep. So implement
+ * it in thread IRQ handler.
+ */
+static irqreturn_t max8925_onkey_handler(int irq, void *data)
+{
+ struct max8925_onkey_info *info = data;
+ int state;
+
+ state = max8925_reg_read(info->i2c, MAX8925_ON_OFF_STATUS);
+
+ input_report_key(info->idev, KEY_POWER, state & SW_INPUT);
+ input_sync(info->idev);
+
+ dev_dbg(info->dev, "onkey state:%d\n", state);
+
+ /* Enable hardreset to halt if system isn't shutdown on time */
+ max8925_set_bits(info->i2c, MAX8925_SYSENSEL,
+ HARDRESET_EN, HARDRESET_EN);
+
+ return IRQ_HANDLED;
+}
+
+static int max8925_onkey_probe(struct platform_device *pdev)
+{
+ struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+ struct max8925_onkey_info *info;
+ struct input_dev *input;
+ int irq[2], error;
+
+ irq[0] = platform_get_irq(pdev, 0);
+ if (irq[0] < 0) {
+ dev_err(&pdev->dev, "No IRQ resource!\n");
+ return -EINVAL;
+ }
+
+ irq[1] = platform_get_irq(pdev, 1);
+ if (irq[1] < 0) {
+ dev_err(&pdev->dev, "No IRQ resource!\n");
+ return -EINVAL;
+ }
+
+ info = kzalloc(sizeof(struct max8925_onkey_info), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!info || !input) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ info->idev = input;
+ info->i2c = chip->i2c;
+ info->dev = &pdev->dev;
+ info->irq[0] = irq[0];
+ info->irq[1] = irq[1];
+
+ input->name = "max8925_on";
+ input->phys = "max8925_on/input0";
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &pdev->dev;
+ input_set_capability(input, EV_KEY, KEY_POWER);
+
+ error = request_threaded_irq(irq[0], NULL, max8925_onkey_handler,
+ IRQF_ONESHOT, "onkey-down", info);
+ if (error < 0) {
+ dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+ irq[0], error);
+ goto err_free_mem;
+ }
+
+ error = request_threaded_irq(irq[1], NULL, max8925_onkey_handler,
+ IRQF_ONESHOT, "onkey-up", info);
+ if (error < 0) {
+ dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
+ irq[1], error);
+ goto err_free_irq0;
+ }
+
+ error = input_register_device(info->idev);
+ if (error) {
+ dev_err(chip->dev, "Can't register input device: %d\n", error);
+ goto err_free_irq1;
+ }
+
+ platform_set_drvdata(pdev, info);
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+
+err_free_irq1:
+ free_irq(irq[1], info);
+err_free_irq0:
+ free_irq(irq[0], info);
+err_free_mem:
+ input_free_device(input);
+ kfree(info);
+
+ return error;
+}
+
+static int max8925_onkey_remove(struct platform_device *pdev)
+{
+ struct max8925_onkey_info *info = platform_get_drvdata(pdev);
+ struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ free_irq(info->irq[0] + chip->irq_base, info);
+ free_irq(info->irq[1] + chip->irq_base, info);
+ input_unregister_device(info->idev);
+ kfree(info);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max8925_onkey_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct max8925_onkey_info *info = platform_get_drvdata(pdev);
+ struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ if (device_may_wakeup(dev)) {
+ chip->wakeup_flag |= 1 << info->irq[0];
+ chip->wakeup_flag |= 1 << info->irq[1];
+ }
+
+ return 0;
+}
+
+static int max8925_onkey_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct max8925_onkey_info *info = platform_get_drvdata(pdev);
+ struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent);
+
+ if (device_may_wakeup(dev)) {
+ chip->wakeup_flag &= ~(1 << info->irq[0]);
+ chip->wakeup_flag &= ~(1 << info->irq[1]);
+ }
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max8925_onkey_pm_ops, max8925_onkey_suspend, max8925_onkey_resume);
+
+static struct platform_driver max8925_onkey_driver = {
+ .driver = {
+ .name = "max8925-onkey",
+ .owner = THIS_MODULE,
+ .pm = &max8925_onkey_pm_ops,
+ },
+ .probe = max8925_onkey_probe,
+ .remove = max8925_onkey_remove,
+};
+module_platform_driver(max8925_onkey_driver);
+
+MODULE_DESCRIPTION("Maxim MAX8925 ONKEY driver");
+MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c
new file mode 100644
index 00000000..e9731332
--- /dev/null
+++ b/drivers/input/misc/max8997_haptic.c
@@ -0,0 +1,407 @@
+/*
+ * MAX8997-haptic controller driver
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * Donggeun Kim <dg77.kim@samsung.com>
+ *
+ * This program is not provided / owned by Maxim Integrated Products.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/pwm.h>
+#include <linux/input.h>
+#include <linux/mfd/max8997-private.h>
+#include <linux/mfd/max8997.h>
+#include <linux/regulator/consumer.h>
+
+/* Haptic configuration 2 register */
+#define MAX8997_MOTOR_TYPE_SHIFT 7
+#define MAX8997_ENABLE_SHIFT 6
+#define MAX8997_MODE_SHIFT 5
+
+/* Haptic driver configuration register */
+#define MAX8997_CYCLE_SHIFT 6
+#define MAX8997_SIG_PERIOD_SHIFT 4
+#define MAX8997_SIG_DUTY_SHIFT 2
+#define MAX8997_PWM_DUTY_SHIFT 0
+
+struct max8997_haptic {
+ struct device *dev;
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ struct regulator *regulator;
+
+ struct work_struct work;
+ struct mutex mutex;
+
+ bool enabled;
+ unsigned int level;
+
+ struct pwm_device *pwm;
+ unsigned int pwm_period;
+ enum max8997_haptic_pwm_divisor pwm_divisor;
+
+ enum max8997_haptic_motor_type type;
+ enum max8997_haptic_pulse_mode mode;
+
+ unsigned int internal_mode_pattern;
+ unsigned int pattern_cycle;
+ unsigned int pattern_signal_period;
+};
+
+static int max8997_haptic_set_duty_cycle(struct max8997_haptic *chip)
+{
+ int ret = 0;
+
+ if (chip->mode == MAX8997_EXTERNAL_MODE) {
+ unsigned int duty = chip->pwm_period * chip->level / 100;
+ ret = pwm_config(chip->pwm, duty, chip->pwm_period);
+ } else {
+ int i;
+ u8 duty_index = 0;
+
+ for (i = 0; i <= 64; i++) {
+ if (chip->level <= i * 100 / 64) {
+ duty_index = i;
+ break;
+ }
+ }
+ switch (chip->internal_mode_pattern) {
+ case 0:
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGPWMDC1, duty_index);
+ break;
+ case 1:
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGPWMDC2, duty_index);
+ break;
+ case 2:
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGPWMDC3, duty_index);
+ break;
+ case 3:
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGPWMDC4, duty_index);
+ break;
+ default:
+ break;
+ }
+ }
+ return ret;
+}
+
+static void max8997_haptic_configure(struct max8997_haptic *chip)
+{
+ u8 value;
+
+ value = chip->type << MAX8997_MOTOR_TYPE_SHIFT |
+ chip->enabled << MAX8997_ENABLE_SHIFT |
+ chip->mode << MAX8997_MODE_SHIFT | chip->pwm_divisor;
+ max8997_write_reg(chip->client, MAX8997_HAPTIC_REG_CONF2, value);
+
+ if (chip->mode == MAX8997_INTERNAL_MODE && chip->enabled) {
+ value = chip->internal_mode_pattern << MAX8997_CYCLE_SHIFT |
+ chip->internal_mode_pattern << MAX8997_SIG_PERIOD_SHIFT |
+ chip->internal_mode_pattern << MAX8997_SIG_DUTY_SHIFT |
+ chip->internal_mode_pattern << MAX8997_PWM_DUTY_SHIFT;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_DRVCONF, value);
+
+ switch (chip->internal_mode_pattern) {
+ case 0:
+ value = chip->pattern_cycle << 4;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_CYCLECONF1, value);
+ value = chip->pattern_signal_period;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGCONF1, value);
+ break;
+
+ case 1:
+ value = chip->pattern_cycle;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_CYCLECONF1, value);
+ value = chip->pattern_signal_period;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGCONF2, value);
+ break;
+
+ case 2:
+ value = chip->pattern_cycle << 4;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_CYCLECONF2, value);
+ value = chip->pattern_signal_period;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGCONF3, value);
+ break;
+
+ case 3:
+ value = chip->pattern_cycle;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_CYCLECONF2, value);
+ value = chip->pattern_signal_period;
+ max8997_write_reg(chip->client,
+ MAX8997_HAPTIC_REG_SIGCONF4, value);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static void max8997_haptic_enable(struct max8997_haptic *chip)
+{
+ int error;
+
+ mutex_lock(&chip->mutex);
+
+ error = max8997_haptic_set_duty_cycle(chip);
+ if (error) {
+ dev_err(chip->dev, "set_pwm_cycle failed, error: %d\n", error);
+ goto out;
+ }
+
+ if (!chip->enabled) {
+ chip->enabled = true;
+ regulator_enable(chip->regulator);
+ max8997_haptic_configure(chip);
+ if (chip->mode == MAX8997_EXTERNAL_MODE)
+ pwm_enable(chip->pwm);
+ }
+
+out:
+ mutex_unlock(&chip->mutex);
+}
+
+static void max8997_haptic_disable(struct max8997_haptic *chip)
+{
+ mutex_lock(&chip->mutex);
+
+ if (chip->enabled) {
+ chip->enabled = false;
+ max8997_haptic_configure(chip);
+ if (chip->mode == MAX8997_EXTERNAL_MODE)
+ pwm_disable(chip->pwm);
+ regulator_disable(chip->regulator);
+ }
+
+ mutex_unlock(&chip->mutex);
+}
+
+static void max8997_haptic_play_effect_work(struct work_struct *work)
+{
+ struct max8997_haptic *chip =
+ container_of(work, struct max8997_haptic, work);
+
+ if (chip->level)
+ max8997_haptic_enable(chip);
+ else
+ max8997_haptic_disable(chip);
+}
+
+static int max8997_haptic_play_effect(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct max8997_haptic *chip = input_get_drvdata(dev);
+
+ chip->level = effect->u.rumble.strong_magnitude;
+ if (!chip->level)
+ chip->level = effect->u.rumble.weak_magnitude;
+
+ schedule_work(&chip->work);
+
+ return 0;
+}
+
+static void max8997_haptic_close(struct input_dev *dev)
+{
+ struct max8997_haptic *chip = input_get_drvdata(dev);
+
+ cancel_work_sync(&chip->work);
+ max8997_haptic_disable(chip);
+}
+
+static int max8997_haptic_probe(struct platform_device *pdev)
+{
+ struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ const struct max8997_platform_data *pdata =
+ dev_get_platdata(iodev->dev);
+ const struct max8997_haptic_platform_data *haptic_pdata =
+ pdata->haptic_pdata;
+ struct max8997_haptic *chip;
+ struct input_dev *input_dev;
+ int error;
+
+ if (!haptic_pdata) {
+ dev_err(&pdev->dev, "no haptic platform data\n");
+ return -EINVAL;
+ }
+
+ chip = kzalloc(sizeof(struct max8997_haptic), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!chip || !input_dev) {
+ dev_err(&pdev->dev, "unable to allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ INIT_WORK(&chip->work, max8997_haptic_play_effect_work);
+ mutex_init(&chip->mutex);
+
+ chip->client = iodev->haptic;
+ chip->dev = &pdev->dev;
+ chip->input_dev = input_dev;
+ chip->pwm_period = haptic_pdata->pwm_period;
+ chip->type = haptic_pdata->type;
+ chip->mode = haptic_pdata->mode;
+ chip->pwm_divisor = haptic_pdata->pwm_divisor;
+
+ switch (chip->mode) {
+ case MAX8997_INTERNAL_MODE:
+ chip->internal_mode_pattern =
+ haptic_pdata->internal_mode_pattern;
+ chip->pattern_cycle = haptic_pdata->pattern_cycle;
+ chip->pattern_signal_period =
+ haptic_pdata->pattern_signal_period;
+ break;
+
+ case MAX8997_EXTERNAL_MODE:
+ chip->pwm = pwm_request(haptic_pdata->pwm_channel_id,
+ "max8997-haptic");
+ if (IS_ERR(chip->pwm)) {
+ error = PTR_ERR(chip->pwm);
+ dev_err(&pdev->dev,
+ "unable to request PWM for haptic, error: %d\n",
+ error);
+ goto err_free_mem;
+ }
+ break;
+
+ default:
+ dev_err(&pdev->dev,
+ "Invalid chip mode specified (%d)\n", chip->mode);
+ error = -EINVAL;
+ goto err_free_mem;
+ }
+
+ chip->regulator = regulator_get(&pdev->dev, "inmotor");
+ if (IS_ERR(chip->regulator)) {
+ error = PTR_ERR(chip->regulator);
+ dev_err(&pdev->dev,
+ "unable to get regulator, error: %d\n",
+ error);
+ goto err_free_pwm;
+ }
+
+ input_dev->name = "max8997-haptic";
+ input_dev->id.version = 1;
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->close = max8997_haptic_close;
+ input_set_drvdata(input_dev, chip);
+ input_set_capability(input_dev, EV_FF, FF_RUMBLE);
+
+ error = input_ff_create_memless(input_dev, NULL,
+ max8997_haptic_play_effect);
+ if (error) {
+ dev_err(&pdev->dev,
+ "unable to create FF device, error: %d\n",
+ error);
+ goto err_put_regulator;
+ }
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&pdev->dev,
+ "unable to register input device, error: %d\n",
+ error);
+ goto err_destroy_ff;
+ }
+
+ platform_set_drvdata(pdev, chip);
+ return 0;
+
+err_destroy_ff:
+ input_ff_destroy(input_dev);
+err_put_regulator:
+ regulator_put(chip->regulator);
+err_free_pwm:
+ if (chip->mode == MAX8997_EXTERNAL_MODE)
+ pwm_free(chip->pwm);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(chip);
+
+ return error;
+}
+
+static int max8997_haptic_remove(struct platform_device *pdev)
+{
+ struct max8997_haptic *chip = platform_get_drvdata(pdev);
+
+ input_unregister_device(chip->input_dev);
+ regulator_put(chip->regulator);
+
+ if (chip->mode == MAX8997_EXTERNAL_MODE)
+ pwm_free(chip->pwm);
+
+ kfree(chip);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int max8997_haptic_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct max8997_haptic *chip = platform_get_drvdata(pdev);
+
+ max8997_haptic_disable(chip);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(max8997_haptic_pm_ops, max8997_haptic_suspend, NULL);
+
+static const struct platform_device_id max8997_haptic_id[] = {
+ { "max8997-haptic", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, max8997_haptic_id);
+
+static struct platform_driver max8997_haptic_driver = {
+ .driver = {
+ .name = "max8997-haptic",
+ .owner = THIS_MODULE,
+ .pm = &max8997_haptic_pm_ops,
+ },
+ .probe = max8997_haptic_probe,
+ .remove = max8997_haptic_remove,
+ .id_table = max8997_haptic_id,
+};
+module_platform_driver(max8997_haptic_driver);
+
+MODULE_ALIAS("platform:max8997-haptic");
+MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
+MODULE_DESCRIPTION("max8997_haptic driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/mc13783-pwrbutton.c b/drivers/input/misc/mc13783-pwrbutton.c
new file mode 100644
index 00000000..0906ca59
--- /dev/null
+++ b/drivers/input/misc/mc13783-pwrbutton.c
@@ -0,0 +1,272 @@
+/**
+ * Copyright (C) 2011 Philippe Rétornaz
+ *
+ * Based on twl4030-pwrbutton driver by:
+ * Peter De Schrijver <peter.de-schrijver@nokia.com>
+ * Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/mc13783.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+struct mc13783_pwrb {
+ struct input_dev *pwr;
+ struct mc13xxx *mc13783;
+#define MC13783_PWRB_B1_POL_INVERT (1 << 0)
+#define MC13783_PWRB_B2_POL_INVERT (1 << 1)
+#define MC13783_PWRB_B3_POL_INVERT (1 << 2)
+ int flags;
+ unsigned short keymap[3];
+};
+
+#define MC13783_REG_INTERRUPT_SENSE_1 5
+#define MC13783_IRQSENSE1_ONOFD1S (1 << 3)
+#define MC13783_IRQSENSE1_ONOFD2S (1 << 4)
+#define MC13783_IRQSENSE1_ONOFD3S (1 << 5)
+
+#define MC13783_REG_POWER_CONTROL_2 15
+#define MC13783_POWER_CONTROL_2_ON1BDBNC 4
+#define MC13783_POWER_CONTROL_2_ON2BDBNC 6
+#define MC13783_POWER_CONTROL_2_ON3BDBNC 8
+#define MC13783_POWER_CONTROL_2_ON1BRSTEN (1 << 1)
+#define MC13783_POWER_CONTROL_2_ON2BRSTEN (1 << 2)
+#define MC13783_POWER_CONTROL_2_ON3BRSTEN (1 << 3)
+
+static irqreturn_t button_irq(int irq, void *_priv)
+{
+ struct mc13783_pwrb *priv = _priv;
+ int val;
+
+ mc13xxx_irq_ack(priv->mc13783, irq);
+ mc13xxx_reg_read(priv->mc13783, MC13783_REG_INTERRUPT_SENSE_1, &val);
+
+ switch (irq) {
+ case MC13783_IRQ_ONOFD1:
+ val = val & MC13783_IRQSENSE1_ONOFD1S ? 1 : 0;
+ if (priv->flags & MC13783_PWRB_B1_POL_INVERT)
+ val ^= 1;
+ input_report_key(priv->pwr, priv->keymap[0], val);
+ break;
+
+ case MC13783_IRQ_ONOFD2:
+ val = val & MC13783_IRQSENSE1_ONOFD2S ? 1 : 0;
+ if (priv->flags & MC13783_PWRB_B2_POL_INVERT)
+ val ^= 1;
+ input_report_key(priv->pwr, priv->keymap[1], val);
+ break;
+
+ case MC13783_IRQ_ONOFD3:
+ val = val & MC13783_IRQSENSE1_ONOFD3S ? 1 : 0;
+ if (priv->flags & MC13783_PWRB_B3_POL_INVERT)
+ val ^= 1;
+ input_report_key(priv->pwr, priv->keymap[2], val);
+ break;
+ }
+
+ input_sync(priv->pwr);
+
+ return IRQ_HANDLED;
+}
+
+static int mc13783_pwrbutton_probe(struct platform_device *pdev)
+{
+ const struct mc13xxx_buttons_platform_data *pdata;
+ struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent);
+ struct input_dev *pwr;
+ struct mc13783_pwrb *priv;
+ int err = 0;
+ int reg = 0;
+
+ pdata = dev_get_platdata(&pdev->dev);
+ if (!pdata) {
+ dev_err(&pdev->dev, "missing platform data\n");
+ return -ENODEV;
+ }
+
+ pwr = input_allocate_device();
+ if (!pwr) {
+ dev_dbg(&pdev->dev, "Can't allocate power button\n");
+ return -ENOMEM;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ err = -ENOMEM;
+ dev_dbg(&pdev->dev, "Can't allocate power button\n");
+ goto free_input_dev;
+ }
+
+ reg |= (pdata->b1on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON1BDBNC;
+ reg |= (pdata->b2on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON2BDBNC;
+ reg |= (pdata->b3on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON3BDBNC;
+
+ priv->pwr = pwr;
+ priv->mc13783 = mc13783;
+
+ mc13xxx_lock(mc13783);
+
+ if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) {
+ priv->keymap[0] = pdata->b1on_key;
+ if (pdata->b1on_key != KEY_RESERVED)
+ __set_bit(pdata->b1on_key, pwr->keybit);
+
+ if (pdata->b1on_flags & MC13783_BUTTON_POL_INVERT)
+ priv->flags |= MC13783_PWRB_B1_POL_INVERT;
+
+ if (pdata->b1on_flags & MC13783_BUTTON_RESET_EN)
+ reg |= MC13783_POWER_CONTROL_2_ON1BRSTEN;
+
+ err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD1,
+ button_irq, "b1on", priv);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't request irq\n");
+ goto free_priv;
+ }
+ }
+
+ if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) {
+ priv->keymap[1] = pdata->b2on_key;
+ if (pdata->b2on_key != KEY_RESERVED)
+ __set_bit(pdata->b2on_key, pwr->keybit);
+
+ if (pdata->b2on_flags & MC13783_BUTTON_POL_INVERT)
+ priv->flags |= MC13783_PWRB_B2_POL_INVERT;
+
+ if (pdata->b2on_flags & MC13783_BUTTON_RESET_EN)
+ reg |= MC13783_POWER_CONTROL_2_ON2BRSTEN;
+
+ err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD2,
+ button_irq, "b2on", priv);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't request irq\n");
+ goto free_irq_b1;
+ }
+ }
+
+ if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) {
+ priv->keymap[2] = pdata->b3on_key;
+ if (pdata->b3on_key != KEY_RESERVED)
+ __set_bit(pdata->b3on_key, pwr->keybit);
+
+ if (pdata->b3on_flags & MC13783_BUTTON_POL_INVERT)
+ priv->flags |= MC13783_PWRB_B3_POL_INVERT;
+
+ if (pdata->b3on_flags & MC13783_BUTTON_RESET_EN)
+ reg |= MC13783_POWER_CONTROL_2_ON3BRSTEN;
+
+ err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD3,
+ button_irq, "b3on", priv);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't request irq: %d\n", err);
+ goto free_irq_b2;
+ }
+ }
+
+ mc13xxx_reg_rmw(mc13783, MC13783_REG_POWER_CONTROL_2, 0x3FE, reg);
+
+ mc13xxx_unlock(mc13783);
+
+ pwr->name = "mc13783_pwrbutton";
+ pwr->phys = "mc13783_pwrbutton/input0";
+ pwr->dev.parent = &pdev->dev;
+
+ pwr->keycode = priv->keymap;
+ pwr->keycodemax = ARRAY_SIZE(priv->keymap);
+ pwr->keycodesize = sizeof(priv->keymap[0]);
+ __set_bit(EV_KEY, pwr->evbit);
+
+ err = input_register_device(pwr);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't register power button: %d\n", err);
+ goto free_irq;
+ }
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+
+free_irq:
+ mc13xxx_lock(mc13783);
+
+ if (pdata->b3on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD3, priv);
+
+free_irq_b2:
+ if (pdata->b2on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD2, priv);
+
+free_irq_b1:
+ if (pdata->b1on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD1, priv);
+
+free_priv:
+ mc13xxx_unlock(mc13783);
+ kfree(priv);
+
+free_input_dev:
+ input_free_device(pwr);
+
+ return err;
+}
+
+static int mc13783_pwrbutton_remove(struct platform_device *pdev)
+{
+ struct mc13783_pwrb *priv = platform_get_drvdata(pdev);
+ const struct mc13xxx_buttons_platform_data *pdata;
+
+ pdata = dev_get_platdata(&pdev->dev);
+
+ mc13xxx_lock(priv->mc13783);
+
+ if (pdata->b3on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD3, priv);
+ if (pdata->b2on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD2, priv);
+ if (pdata->b1on_flags & MC13783_BUTTON_ENABLE)
+ mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD1, priv);
+
+ mc13xxx_unlock(priv->mc13783);
+
+ input_unregister_device(priv->pwr);
+ kfree(priv);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver mc13783_pwrbutton_driver = {
+ .probe = mc13783_pwrbutton_probe,
+ .remove = mc13783_pwrbutton_remove,
+ .driver = {
+ .name = "mc13783-pwrbutton",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(mc13783_pwrbutton_driver);
+
+MODULE_ALIAS("platform:mc13783-pwrbutton");
+MODULE_DESCRIPTION("MC13783 Power Button");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Philippe Retornaz");
diff --git a/drivers/input/misc/mma8450.c b/drivers/input/misc/mma8450.c
new file mode 100644
index 00000000..f3309696
--- /dev/null
+++ b/drivers/input/misc/mma8450.c
@@ -0,0 +1,254 @@
+/*
+ * Driver for Freescale's 3-Axis Accelerometer MMA8450
+ *
+ * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input-polldev.h>
+#include <linux/of_device.h>
+
+#define MMA8450_DRV_NAME "mma8450"
+
+#define MODE_CHANGE_DELAY_MS 100
+#define POLL_INTERVAL 100
+#define POLL_INTERVAL_MAX 500
+
+/* register definitions */
+#define MMA8450_STATUS 0x00
+#define MMA8450_STATUS_ZXYDR 0x08
+
+#define MMA8450_OUT_X8 0x01
+#define MMA8450_OUT_Y8 0x02
+#define MMA8450_OUT_Z8 0x03
+
+#define MMA8450_OUT_X_LSB 0x05
+#define MMA8450_OUT_X_MSB 0x06
+#define MMA8450_OUT_Y_LSB 0x07
+#define MMA8450_OUT_Y_MSB 0x08
+#define MMA8450_OUT_Z_LSB 0x09
+#define MMA8450_OUT_Z_MSB 0x0a
+
+#define MMA8450_XYZ_DATA_CFG 0x16
+
+#define MMA8450_CTRL_REG1 0x38
+#define MMA8450_CTRL_REG2 0x39
+
+/* mma8450 status */
+struct mma8450 {
+ struct i2c_client *client;
+ struct input_polled_dev *idev;
+};
+
+static int mma8450_read(struct mma8450 *m, unsigned off)
+{
+ struct i2c_client *c = m->client;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(c, off);
+ if (ret < 0)
+ dev_err(&c->dev,
+ "failed to read register 0x%02x, error %d\n",
+ off, ret);
+
+ return ret;
+}
+
+static int mma8450_write(struct mma8450 *m, unsigned off, u8 v)
+{
+ struct i2c_client *c = m->client;
+ int error;
+
+ error = i2c_smbus_write_byte_data(c, off, v);
+ if (error < 0) {
+ dev_err(&c->dev,
+ "failed to write to register 0x%02x, error %d\n",
+ off, error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int mma8450_read_block(struct mma8450 *m, unsigned off,
+ u8 *buf, size_t size)
+{
+ struct i2c_client *c = m->client;
+ int err;
+
+ err = i2c_smbus_read_i2c_block_data(c, off, size, buf);
+ if (err < 0) {
+ dev_err(&c->dev,
+ "failed to read block data at 0x%02x, error %d\n",
+ MMA8450_OUT_X_LSB, err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void mma8450_poll(struct input_polled_dev *dev)
+{
+ struct mma8450 *m = dev->private;
+ int x, y, z;
+ int ret;
+ u8 buf[6];
+
+ ret = mma8450_read(m, MMA8450_STATUS);
+ if (ret < 0)
+ return;
+
+ if (!(ret & MMA8450_STATUS_ZXYDR))
+ return;
+
+ ret = mma8450_read_block(m, MMA8450_OUT_X_LSB, buf, sizeof(buf));
+ if (ret < 0)
+ return;
+
+ x = ((int)(s8)buf[1] << 4) | (buf[0] & 0xf);
+ y = ((int)(s8)buf[3] << 4) | (buf[2] & 0xf);
+ z = ((int)(s8)buf[5] << 4) | (buf[4] & 0xf);
+
+ input_report_abs(dev->input, ABS_X, x);
+ input_report_abs(dev->input, ABS_Y, y);
+ input_report_abs(dev->input, ABS_Z, z);
+ input_sync(dev->input);
+}
+
+/* Initialize the MMA8450 chip */
+static void mma8450_open(struct input_polled_dev *dev)
+{
+ struct mma8450 *m = dev->private;
+ int err;
+
+ /* enable all events from X/Y/Z, no FIFO */
+ err = mma8450_write(m, MMA8450_XYZ_DATA_CFG, 0x07);
+ if (err)
+ return;
+
+ /*
+ * Sleep mode poll rate - 50Hz
+ * System output data rate - 400Hz
+ * Full scale selection - Active, +/- 2G
+ */
+ err = mma8450_write(m, MMA8450_CTRL_REG1, 0x01);
+ if (err < 0)
+ return;
+
+ msleep(MODE_CHANGE_DELAY_MS);
+}
+
+static void mma8450_close(struct input_polled_dev *dev)
+{
+ struct mma8450 *m = dev->private;
+
+ mma8450_write(m, MMA8450_CTRL_REG1, 0x00);
+ mma8450_write(m, MMA8450_CTRL_REG2, 0x01);
+}
+
+/*
+ * I2C init/probing/exit functions
+ */
+static int mma8450_probe(struct i2c_client *c,
+ const struct i2c_device_id *id)
+{
+ struct input_polled_dev *idev;
+ struct mma8450 *m;
+ int err;
+
+ m = kzalloc(sizeof(struct mma8450), GFP_KERNEL);
+ idev = input_allocate_polled_device();
+ if (!m || !idev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ m->client = c;
+ m->idev = idev;
+
+ idev->private = m;
+ idev->input->name = MMA8450_DRV_NAME;
+ idev->input->id.bustype = BUS_I2C;
+ idev->poll = mma8450_poll;
+ idev->poll_interval = POLL_INTERVAL;
+ idev->poll_interval_max = POLL_INTERVAL_MAX;
+ idev->open = mma8450_open;
+ idev->close = mma8450_close;
+
+ __set_bit(EV_ABS, idev->input->evbit);
+ input_set_abs_params(idev->input, ABS_X, -2048, 2047, 32, 32);
+ input_set_abs_params(idev->input, ABS_Y, -2048, 2047, 32, 32);
+ input_set_abs_params(idev->input, ABS_Z, -2048, 2047, 32, 32);
+
+ err = input_register_polled_device(idev);
+ if (err) {
+ dev_err(&c->dev, "failed to register polled input device\n");
+ goto err_free_mem;
+ }
+
+ return 0;
+
+err_free_mem:
+ input_free_polled_device(idev);
+ kfree(m);
+ return err;
+}
+
+static int mma8450_remove(struct i2c_client *c)
+{
+ struct mma8450 *m = i2c_get_clientdata(c);
+ struct input_polled_dev *idev = m->idev;
+
+ input_unregister_polled_device(idev);
+ input_free_polled_device(idev);
+ kfree(m);
+
+ return 0;
+}
+
+static const struct i2c_device_id mma8450_id[] = {
+ { MMA8450_DRV_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, mma8450_id);
+
+static const struct of_device_id mma8450_dt_ids[] = {
+ { .compatible = "fsl,mma8450", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mma8450_dt_ids);
+
+static struct i2c_driver mma8450_driver = {
+ .driver = {
+ .name = MMA8450_DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mma8450_dt_ids,
+ },
+ .probe = mma8450_probe,
+ .remove = mma8450_remove,
+ .id_table = mma8450_id,
+};
+
+module_i2c_driver(mma8450_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MMA8450 3-Axis Accelerometer Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
new file mode 100644
index 00000000..dce0d959
--- /dev/null
+++ b/drivers/input/misc/mpu3050.c
@@ -0,0 +1,482 @@
+/*
+ * MPU3050 Tri-axis gyroscope driver
+ *
+ * Copyright (C) 2011 Wistron Co.Ltd
+ * Joseph Lai <joseph_lai@wistron.com>
+ *
+ * Trimmed down by Alan Cox <alan@linux.intel.com> to produce this version
+ *
+ * This is a 'lite' version of the driver, while we consider the right way
+ * to present the other features to user space. In particular it requires the
+ * device has an IRQ, and it only provides an input interface, so is not much
+ * use for device orientation. A fuller version is available from the Meego
+ * tree.
+ *
+ * This program is based on bma023.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+
+#define MPU3050_CHIP_ID 0x69
+
+#define MPU3050_AUTO_DELAY 1000
+
+#define MPU3050_MIN_VALUE -32768
+#define MPU3050_MAX_VALUE 32767
+
+#define MPU3050_DEFAULT_POLL_INTERVAL 200
+#define MPU3050_DEFAULT_FS_RANGE 3
+
+/* Register map */
+#define MPU3050_CHIP_ID_REG 0x00
+#define MPU3050_SMPLRT_DIV 0x15
+#define MPU3050_DLPF_FS_SYNC 0x16
+#define MPU3050_INT_CFG 0x17
+#define MPU3050_XOUT_H 0x1D
+#define MPU3050_PWR_MGM 0x3E
+#define MPU3050_PWR_MGM_POS 6
+
+/* Register bits */
+
+/* DLPF_FS_SYNC */
+#define MPU3050_EXT_SYNC_NONE 0x00
+#define MPU3050_EXT_SYNC_TEMP 0x20
+#define MPU3050_EXT_SYNC_GYROX 0x40
+#define MPU3050_EXT_SYNC_GYROY 0x60
+#define MPU3050_EXT_SYNC_GYROZ 0x80
+#define MPU3050_EXT_SYNC_ACCELX 0xA0
+#define MPU3050_EXT_SYNC_ACCELY 0xC0
+#define MPU3050_EXT_SYNC_ACCELZ 0xE0
+#define MPU3050_EXT_SYNC_MASK 0xE0
+#define MPU3050_FS_250DPS 0x00
+#define MPU3050_FS_500DPS 0x08
+#define MPU3050_FS_1000DPS 0x10
+#define MPU3050_FS_2000DPS 0x18
+#define MPU3050_FS_MASK 0x18
+#define MPU3050_DLPF_CFG_256HZ_NOLPF2 0x00
+#define MPU3050_DLPF_CFG_188HZ 0x01
+#define MPU3050_DLPF_CFG_98HZ 0x02
+#define MPU3050_DLPF_CFG_42HZ 0x03
+#define MPU3050_DLPF_CFG_20HZ 0x04
+#define MPU3050_DLPF_CFG_10HZ 0x05
+#define MPU3050_DLPF_CFG_5HZ 0x06
+#define MPU3050_DLPF_CFG_2100HZ_NOLPF 0x07
+#define MPU3050_DLPF_CFG_MASK 0x07
+/* INT_CFG */
+#define MPU3050_RAW_RDY_EN 0x01
+#define MPU3050_MPU_RDY_EN 0x02
+#define MPU3050_LATCH_INT_EN 0x04
+/* PWR_MGM */
+#define MPU3050_PWR_MGM_PLL_X 0x01
+#define MPU3050_PWR_MGM_PLL_Y 0x02
+#define MPU3050_PWR_MGM_PLL_Z 0x03
+#define MPU3050_PWR_MGM_CLKSEL 0x07
+#define MPU3050_PWR_MGM_STBY_ZG 0x08
+#define MPU3050_PWR_MGM_STBY_YG 0x10
+#define MPU3050_PWR_MGM_STBY_XG 0x20
+#define MPU3050_PWR_MGM_SLEEP 0x40
+#define MPU3050_PWR_MGM_RESET 0x80
+#define MPU3050_PWR_MGM_MASK 0x40
+
+struct axis_data {
+ s16 x;
+ s16 y;
+ s16 z;
+};
+
+struct mpu3050_sensor {
+ struct i2c_client *client;
+ struct device *dev;
+ struct input_dev *idev;
+};
+
+/**
+ * mpu3050_xyz_read_reg - read the axes values
+ * @buffer: provide register addr and get register
+ * @length: length of register
+ *
+ * Reads the register values in one transaction or returns a negative
+ * error code on failure.
+ */
+static int mpu3050_xyz_read_reg(struct i2c_client *client,
+ u8 *buffer, int length)
+{
+ /*
+ * Annoying we can't make this const because the i2c layer doesn't
+ * declare input buffers const.
+ */
+ char cmd = MPU3050_XOUT_H;
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &cmd,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = buffer,
+ },
+ };
+
+ return i2c_transfer(client->adapter, msg, 2);
+}
+
+/**
+ * mpu3050_read_xyz - get co-ordinates from device
+ * @client: i2c address of sensor
+ * @coords: co-ordinates to update
+ *
+ * Return the converted X Y and Z co-ordinates from the sensor device
+ */
+static void mpu3050_read_xyz(struct i2c_client *client,
+ struct axis_data *coords)
+{
+ u16 buffer[3];
+
+ mpu3050_xyz_read_reg(client, (u8 *)buffer, 6);
+ coords->x = be16_to_cpu(buffer[0]);
+ coords->y = be16_to_cpu(buffer[1]);
+ coords->z = be16_to_cpu(buffer[2]);
+ dev_dbg(&client->dev, "%s: x %d, y %d, z %d\n", __func__,
+ coords->x, coords->y, coords->z);
+}
+
+/**
+ * mpu3050_set_power_mode - set the power mode
+ * @client: i2c client for the sensor
+ * @val: value to switch on/off of power, 1: normal power, 0: low power
+ *
+ * Put device to normal-power mode or low-power mode.
+ */
+static void mpu3050_set_power_mode(struct i2c_client *client, u8 val)
+{
+ u8 value;
+
+ value = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);
+ value = (value & ~MPU3050_PWR_MGM_MASK) |
+ (((val << MPU3050_PWR_MGM_POS) & MPU3050_PWR_MGM_MASK) ^
+ MPU3050_PWR_MGM_MASK);
+ i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, value);
+}
+
+/**
+ * mpu3050_input_open - called on input event open
+ * @input: input dev of opened device
+ *
+ * The input layer calls this function when input event is opened. The
+ * function will push the device to resume. Then, the device is ready
+ * to provide data.
+ */
+static int mpu3050_input_open(struct input_dev *input)
+{
+ struct mpu3050_sensor *sensor = input_get_drvdata(input);
+ int error;
+
+ pm_runtime_get(sensor->dev);
+
+ /* Enable interrupts */
+ error = i2c_smbus_write_byte_data(sensor->client, MPU3050_INT_CFG,
+ MPU3050_LATCH_INT_EN |
+ MPU3050_RAW_RDY_EN |
+ MPU3050_MPU_RDY_EN);
+ if (error < 0) {
+ pm_runtime_put(sensor->dev);
+ return error;
+ }
+
+ return 0;
+}
+
+/**
+ * mpu3050_input_close - called on input event close
+ * @input: input dev of closed device
+ *
+ * The input layer calls this function when input event is closed. The
+ * function will push the device to suspend.
+ */
+static void mpu3050_input_close(struct input_dev *input)
+{
+ struct mpu3050_sensor *sensor = input_get_drvdata(input);
+
+ pm_runtime_put(sensor->dev);
+}
+
+/**
+ * mpu3050_interrupt_thread - handle an IRQ
+ * @irq: interrupt numner
+ * @data: the sensor
+ *
+ * Called by the kernel single threaded after an interrupt occurs. Read
+ * the sensor data and generate an input event for it.
+ */
+static irqreturn_t mpu3050_interrupt_thread(int irq, void *data)
+{
+ struct mpu3050_sensor *sensor = data;
+ struct axis_data axis;
+
+ mpu3050_read_xyz(sensor->client, &axis);
+
+ input_report_abs(sensor->idev, ABS_X, axis.x);
+ input_report_abs(sensor->idev, ABS_Y, axis.y);
+ input_report_abs(sensor->idev, ABS_Z, axis.z);
+ input_sync(sensor->idev);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * mpu3050_hw_init - initialize hardware
+ * @sensor: the sensor
+ *
+ * Called during device probe; configures the sampling method.
+ */
+static int mpu3050_hw_init(struct mpu3050_sensor *sensor)
+{
+ struct i2c_client *client = sensor->client;
+ int ret;
+ u8 reg;
+
+ /* Reset */
+ ret = i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM,
+ MPU3050_PWR_MGM_RESET);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);
+ if (ret < 0)
+ return ret;
+
+ ret &= ~MPU3050_PWR_MGM_CLKSEL;
+ ret |= MPU3050_PWR_MGM_PLL_Z;
+ ret = i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, ret);
+ if (ret < 0)
+ return ret;
+
+ /* Output frequency divider. The poll interval */
+ ret = i2c_smbus_write_byte_data(client, MPU3050_SMPLRT_DIV,
+ MPU3050_DEFAULT_POLL_INTERVAL - 1);
+ if (ret < 0)
+ return ret;
+
+ /* Set low pass filter and full scale */
+ reg = MPU3050_DEFAULT_FS_RANGE;
+ reg |= MPU3050_DLPF_CFG_42HZ << 3;
+ reg |= MPU3050_EXT_SYNC_NONE << 5;
+ ret = i2c_smbus_write_byte_data(client, MPU3050_DLPF_FS_SYNC, reg);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * mpu3050_probe - device detection callback
+ * @client: i2c client of found device
+ * @id: id match information
+ *
+ * The I2C layer calls us when it believes a sensor is present at this
+ * address. Probe to see if this is correct and to validate the device.
+ *
+ * If present install the relevant sysfs interfaces and input device.
+ */
+static int mpu3050_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mpu3050_sensor *sensor;
+ struct input_dev *idev;
+ int ret;
+ int error;
+
+ sensor = kzalloc(sizeof(struct mpu3050_sensor), GFP_KERNEL);
+ idev = input_allocate_device();
+ if (!sensor || !idev) {
+ dev_err(&client->dev, "failed to allocate driver data\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ sensor->client = client;
+ sensor->dev = &client->dev;
+ sensor->idev = idev;
+
+ mpu3050_set_power_mode(client, 1);
+ msleep(10);
+
+ ret = i2c_smbus_read_byte_data(client, MPU3050_CHIP_ID_REG);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to detect device\n");
+ error = -ENXIO;
+ goto err_free_mem;
+ }
+
+ if (ret != MPU3050_CHIP_ID) {
+ dev_err(&client->dev, "unsupported chip id\n");
+ error = -ENXIO;
+ goto err_free_mem;
+ }
+
+ idev->name = "MPU3050";
+ idev->id.bustype = BUS_I2C;
+ idev->dev.parent = &client->dev;
+
+ idev->open = mpu3050_input_open;
+ idev->close = mpu3050_input_close;
+
+ __set_bit(EV_ABS, idev->evbit);
+ input_set_abs_params(idev, ABS_X,
+ MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+ input_set_abs_params(idev, ABS_Y,
+ MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+ input_set_abs_params(idev, ABS_Z,
+ MPU3050_MIN_VALUE, MPU3050_MAX_VALUE, 0, 0);
+
+ input_set_drvdata(idev, sensor);
+
+ pm_runtime_set_active(&client->dev);
+
+ error = mpu3050_hw_init(sensor);
+ if (error)
+ goto err_pm_set_suspended;
+
+ error = request_threaded_irq(client->irq,
+ NULL, mpu3050_interrupt_thread,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "mpu3050", sensor);
+ if (error) {
+ dev_err(&client->dev,
+ "can't get IRQ %d, error %d\n", client->irq, error);
+ goto err_pm_set_suspended;
+ }
+
+ error = input_register_device(idev);
+ if (error) {
+ dev_err(&client->dev, "failed to register input device\n");
+ goto err_free_irq;
+ }
+
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev, MPU3050_AUTO_DELAY);
+
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, sensor);
+err_pm_set_suspended:
+ pm_runtime_set_suspended(&client->dev);
+err_free_mem:
+ input_free_device(idev);
+ kfree(sensor);
+ return error;
+}
+
+/**
+ * mpu3050_remove - remove a sensor
+ * @client: i2c client of sensor being removed
+ *
+ * Our sensor is going away, clean up the resources.
+ */
+static int mpu3050_remove(struct i2c_client *client)
+{
+ struct mpu3050_sensor *sensor = i2c_get_clientdata(client);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ free_irq(client->irq, sensor);
+ input_unregister_device(sensor->idev);
+ kfree(sensor);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+/**
+ * mpu3050_suspend - called on device suspend
+ * @dev: device being suspended
+ *
+ * Put the device into sleep mode before we suspend the machine.
+ */
+static int mpu3050_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ mpu3050_set_power_mode(client, 0);
+
+ return 0;
+}
+
+/**
+ * mpu3050_resume - called on device resume
+ * @dev: device being resumed
+ *
+ * Put the device into powered mode on resume.
+ */
+static int mpu3050_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ mpu3050_set_power_mode(client, 1);
+ msleep(100); /* wait for gyro chip resume */
+
+ return 0;
+}
+#endif
+
+static UNIVERSAL_DEV_PM_OPS(mpu3050_pm, mpu3050_suspend, mpu3050_resume, NULL);
+
+static const struct i2c_device_id mpu3050_ids[] = {
+ { "mpu3050", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mpu3050_ids);
+
+static const struct of_device_id mpu3050_of_match[] = {
+ { .compatible = "invn,mpu3050", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mpu3050_of_match);
+
+static struct i2c_driver mpu3050_i2c_driver = {
+ .driver = {
+ .name = "mpu3050",
+ .owner = THIS_MODULE,
+ .pm = &mpu3050_pm,
+ .of_match_table = mpu3050_of_match,
+ },
+ .probe = mpu3050_probe,
+ .remove = mpu3050_remove,
+ .id_table = mpu3050_ids,
+};
+
+module_i2c_driver(mpu3050_i2c_driver);
+
+MODULE_AUTHOR("Wistron Corp.");
+MODULE_DESCRIPTION("MPU3050 Tri-axis gyroscope driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/pcap_keys.c b/drivers/input/misc/pcap_keys.c
new file mode 100644
index 00000000..40ac9a5a
--- /dev/null
+++ b/drivers/input/misc/pcap_keys.c
@@ -0,0 +1,133 @@
+/*
+ * Input driver for PCAP events:
+ * * Power key
+ * * Headphone button
+ *
+ * Copyright (c) 2008,2009 Ilya Petrov <ilya.muromec@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/mfd/ezx-pcap.h>
+#include <linux/slab.h>
+
+struct pcap_keys {
+ struct pcap_chip *pcap;
+ struct input_dev *input;
+};
+
+/* PCAP2 interrupts us on keypress */
+static irqreturn_t pcap_keys_handler(int irq, void *_pcap_keys)
+{
+ struct pcap_keys *pcap_keys = _pcap_keys;
+ int pirq = irq_to_pcap(pcap_keys->pcap, irq);
+ u32 pstat;
+
+ ezx_pcap_read(pcap_keys->pcap, PCAP_REG_PSTAT, &pstat);
+ pstat &= 1 << pirq;
+
+ switch (pirq) {
+ case PCAP_IRQ_ONOFF:
+ input_report_key(pcap_keys->input, KEY_POWER, !pstat);
+ break;
+ case PCAP_IRQ_MIC:
+ input_report_key(pcap_keys->input, KEY_HP, !pstat);
+ break;
+ }
+
+ input_sync(pcap_keys->input);
+
+ return IRQ_HANDLED;
+}
+
+static int pcap_keys_probe(struct platform_device *pdev)
+{
+ int err = -ENOMEM;
+ struct pcap_keys *pcap_keys;
+ struct input_dev *input_dev;
+
+ pcap_keys = kmalloc(sizeof(struct pcap_keys), GFP_KERNEL);
+ if (!pcap_keys)
+ return err;
+
+ pcap_keys->pcap = dev_get_drvdata(pdev->dev.parent);
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ goto fail;
+
+ pcap_keys->input = input_dev;
+
+ platform_set_drvdata(pdev, pcap_keys);
+ input_dev->name = pdev->name;
+ input_dev->phys = "pcap-keys/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->dev.parent = &pdev->dev;
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(KEY_POWER, input_dev->keybit);
+ __set_bit(KEY_HP, input_dev->keybit);
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto fail_allocate;
+
+ err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF),
+ pcap_keys_handler, 0, "Power key", pcap_keys);
+ if (err)
+ goto fail_register;
+
+ err = request_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC),
+ pcap_keys_handler, 0, "Headphone button", pcap_keys);
+ if (err)
+ goto fail_pwrkey;
+
+ return 0;
+
+fail_pwrkey:
+ free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys);
+fail_register:
+ input_unregister_device(input_dev);
+ goto fail;
+fail_allocate:
+ input_free_device(input_dev);
+fail:
+ kfree(pcap_keys);
+ return err;
+}
+
+static int pcap_keys_remove(struct platform_device *pdev)
+{
+ struct pcap_keys *pcap_keys = platform_get_drvdata(pdev);
+
+ free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_ONOFF), pcap_keys);
+ free_irq(pcap_to_irq(pcap_keys->pcap, PCAP_IRQ_MIC), pcap_keys);
+
+ input_unregister_device(pcap_keys->input);
+ kfree(pcap_keys);
+
+ return 0;
+}
+
+static struct platform_driver pcap_keys_device_driver = {
+ .probe = pcap_keys_probe,
+ .remove = pcap_keys_remove,
+ .driver = {
+ .name = "pcap-keys",
+ .owner = THIS_MODULE,
+ }
+};
+module_platform_driver(pcap_keys_device_driver);
+
+MODULE_DESCRIPTION("Motorola PCAP2 input events driver");
+MODULE_AUTHOR("Ilya Petrov <ilya.muromec@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcap_keys");
diff --git a/drivers/input/misc/pcf50633-input.c b/drivers/input/misc/pcf50633-input.c
new file mode 100644
index 00000000..73b13eba
--- /dev/null
+++ b/drivers/input/misc/pcf50633-input.c
@@ -0,0 +1,121 @@
+/* NXP PCF50633 Input Driver
+ *
+ * (C) 2006-2008 by Openmoko, Inc.
+ * Author: Balaji Rao <balajirrao@openmoko.org>
+ * All rights reserved.
+ *
+ * Broken down from monstrous PCF50633 driver mainly by
+ * Harald Welte, Andy Green and Werner Almesberger
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include <linux/mfd/pcf50633/core.h>
+
+#define PCF50633_OOCSTAT_ONKEY 0x01
+#define PCF50633_REG_OOCSTAT 0x12
+#define PCF50633_REG_OOCMODE 0x10
+
+struct pcf50633_input {
+ struct pcf50633 *pcf;
+ struct input_dev *input_dev;
+};
+
+static void
+pcf50633_input_irq(int irq, void *data)
+{
+ struct pcf50633_input *input;
+ int onkey_released;
+
+ input = data;
+
+ /* We report only one event depending on the key press status */
+ onkey_released = pcf50633_reg_read(input->pcf, PCF50633_REG_OOCSTAT)
+ & PCF50633_OOCSTAT_ONKEY;
+
+ if (irq == PCF50633_IRQ_ONKEYF && !onkey_released)
+ input_report_key(input->input_dev, KEY_POWER, 1);
+ else if (irq == PCF50633_IRQ_ONKEYR && onkey_released)
+ input_report_key(input->input_dev, KEY_POWER, 0);
+
+ input_sync(input->input_dev);
+}
+
+static int pcf50633_input_probe(struct platform_device *pdev)
+{
+ struct pcf50633_input *input;
+ struct input_dev *input_dev;
+ int ret;
+
+
+ input = kzalloc(sizeof(*input), GFP_KERNEL);
+ if (!input)
+ return -ENOMEM;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ kfree(input);
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, input);
+ input->pcf = dev_to_pcf50633(pdev->dev.parent);
+ input->input_dev = input_dev;
+
+ input_dev->name = "PCF50633 PMU events";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_PWR);
+ set_bit(KEY_POWER, input_dev->keybit);
+
+ ret = input_register_device(input_dev);
+ if (ret) {
+ input_free_device(input_dev);
+ kfree(input);
+ return ret;
+ }
+ pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYR,
+ pcf50633_input_irq, input);
+ pcf50633_register_irq(input->pcf, PCF50633_IRQ_ONKEYF,
+ pcf50633_input_irq, input);
+
+ return 0;
+}
+
+static int pcf50633_input_remove(struct platform_device *pdev)
+{
+ struct pcf50633_input *input = platform_get_drvdata(pdev);
+
+ pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYR);
+ pcf50633_free_irq(input->pcf, PCF50633_IRQ_ONKEYF);
+
+ input_unregister_device(input->input_dev);
+ kfree(input);
+
+ return 0;
+}
+
+static struct platform_driver pcf50633_input_driver = {
+ .driver = {
+ .name = "pcf50633-input",
+ },
+ .probe = pcf50633_input_probe,
+ .remove = pcf50633_input_remove,
+};
+module_platform_driver(pcf50633_input_driver);
+
+MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
+MODULE_DESCRIPTION("PCF50633 input driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcf50633-input");
diff --git a/drivers/input/misc/pcf8574_keypad.c b/drivers/input/misc/pcf8574_keypad.c
new file mode 100644
index 00000000..e3739297
--- /dev/null
+++ b/drivers/input/misc/pcf8574_keypad.c
@@ -0,0 +1,223 @@
+/*
+ * Driver for a keypad w/16 buttons connected to a PCF8574 I2C I/O expander
+ *
+ * Copyright 2005-2008 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#define DRV_NAME "pcf8574_keypad"
+
+static const unsigned char pcf8574_kp_btncode[] = {
+ [0] = KEY_RESERVED,
+ [1] = KEY_ENTER,
+ [2] = KEY_BACKSLASH,
+ [3] = KEY_0,
+ [4] = KEY_RIGHTBRACE,
+ [5] = KEY_C,
+ [6] = KEY_9,
+ [7] = KEY_8,
+ [8] = KEY_7,
+ [9] = KEY_B,
+ [10] = KEY_6,
+ [11] = KEY_5,
+ [12] = KEY_4,
+ [13] = KEY_A,
+ [14] = KEY_3,
+ [15] = KEY_2,
+ [16] = KEY_1
+};
+
+struct kp_data {
+ unsigned short btncode[ARRAY_SIZE(pcf8574_kp_btncode)];
+ struct input_dev *idev;
+ struct i2c_client *client;
+ char name[64];
+ char phys[32];
+ unsigned char laststate;
+};
+
+static short read_state(struct kp_data *lp)
+{
+ unsigned char x, y, a, b;
+
+ i2c_smbus_write_byte(lp->client, 240);
+ x = 0xF & (~(i2c_smbus_read_byte(lp->client) >> 4));
+
+ i2c_smbus_write_byte(lp->client, 15);
+ y = 0xF & (~i2c_smbus_read_byte(lp->client));
+
+ for (a = 0; x > 0; a++)
+ x = x >> 1;
+ for (b = 0; y > 0; b++)
+ y = y >> 1;
+
+ return ((a - 1) * 4) + b;
+}
+
+static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id)
+{
+ struct kp_data *lp = dev_id;
+ unsigned char nextstate = read_state(lp);
+
+ if (lp->laststate != nextstate) {
+ int key_down = nextstate < ARRAY_SIZE(lp->btncode);
+ unsigned short keycode = key_down ?
+ lp->btncode[nextstate] : lp->btncode[lp->laststate];
+
+ input_report_key(lp->idev, keycode, key_down);
+ input_sync(lp->idev);
+
+ lp->laststate = nextstate;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ int i, ret;
+ struct input_dev *idev;
+ struct kp_data *lp;
+
+ if (i2c_smbus_write_byte(client, 240) < 0) {
+ dev_err(&client->dev, "probe: write fail\n");
+ return -ENODEV;
+ }
+
+ lp = kzalloc(sizeof(*lp), GFP_KERNEL);
+ if (!lp)
+ return -ENOMEM;
+
+ idev = input_allocate_device();
+ if (!idev) {
+ dev_err(&client->dev, "Can't allocate input device\n");
+ ret = -ENOMEM;
+ goto fail_allocate;
+ }
+
+ lp->idev = idev;
+ lp->client = client;
+
+ idev->evbit[0] = BIT_MASK(EV_KEY);
+ idev->keycode = lp->btncode;
+ idev->keycodesize = sizeof(lp->btncode[0]);
+ idev->keycodemax = ARRAY_SIZE(lp->btncode);
+
+ for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) {
+ lp->btncode[i] = pcf8574_kp_btncode[i];
+ __set_bit(lp->btncode[i] & KEY_MAX, idev->keybit);
+ }
+
+ sprintf(lp->name, DRV_NAME);
+ sprintf(lp->phys, "kp_data/input0");
+
+ idev->name = lp->name;
+ idev->phys = lp->phys;
+ idev->id.bustype = BUS_I2C;
+ idev->id.vendor = 0x0001;
+ idev->id.product = 0x0001;
+ idev->id.version = 0x0100;
+
+ lp->laststate = read_state(lp);
+
+ ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ DRV_NAME, lp);
+ if (ret) {
+ dev_err(&client->dev, "IRQ %d is not free\n", client->irq);
+ goto fail_free_device;
+ }
+
+ ret = input_register_device(idev);
+ if (ret) {
+ dev_err(&client->dev, "input_register_device() failed\n");
+ goto fail_free_irq;
+ }
+
+ i2c_set_clientdata(client, lp);
+ return 0;
+
+ fail_free_irq:
+ free_irq(client->irq, lp);
+ fail_free_device:
+ input_free_device(idev);
+ fail_allocate:
+ kfree(lp);
+
+ return ret;
+}
+
+static int pcf8574_kp_remove(struct i2c_client *client)
+{
+ struct kp_data *lp = i2c_get_clientdata(client);
+
+ free_irq(client->irq, lp);
+
+ input_unregister_device(lp->idev);
+ kfree(lp);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pcf8574_kp_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static int pcf8574_kp_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+
+ disable_irq(client->irq);
+
+ return 0;
+}
+
+static const struct dev_pm_ops pcf8574_kp_pm_ops = {
+ .suspend = pcf8574_kp_suspend,
+ .resume = pcf8574_kp_resume,
+};
+
+#else
+# define pcf8574_kp_resume NULL
+# define pcf8574_kp_suspend NULL
+#endif
+
+static const struct i2c_device_id pcf8574_kp_id[] = {
+ { DRV_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
+
+static struct i2c_driver pcf8574_kp_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &pcf8574_kp_pm_ops,
+#endif
+ },
+ .probe = pcf8574_kp_probe,
+ .remove = pcf8574_kp_remove,
+ .id_table = pcf8574_kp_id,
+};
+
+module_i2c_driver(pcf8574_kp_driver);
+
+MODULE_AUTHOR("Michael Hennerich");
+MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/pcspkr.c b/drivers/input/misc/pcspkr.c
new file mode 100644
index 00000000..199db78a
--- /dev/null
+++ b/drivers/input/misc/pcspkr.c
@@ -0,0 +1,138 @@
+/*
+ * PC Speaker beeper driver for Linux
+ *
+ * Copyright (c) 2002 Vojtech Pavlik
+ * Copyright (c) 1992 Orest Zborowski
+ *
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i8253.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/timex.h>
+#include <asm/io.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("PC Speaker beeper driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pcspkr");
+
+static int pcspkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ unsigned int count = 0;
+ unsigned long flags;
+
+ if (type != EV_SND)
+ return -1;
+
+ switch (code) {
+ case SND_BELL: if (value) value = 1000;
+ case SND_TONE: break;
+ default: return -1;
+ }
+
+ if (value > 20 && value < 32767)
+ count = PIT_TICK_RATE / value;
+
+ raw_spin_lock_irqsave(&i8253_lock, flags);
+
+ if (count) {
+ /* set command for counter 2, 2 byte write */
+ outb_p(0xB6, 0x43);
+ /* select desired HZ */
+ outb_p(count & 0xff, 0x42);
+ outb((count >> 8) & 0xff, 0x42);
+ /* enable counter 2 */
+ outb_p(inb_p(0x61) | 3, 0x61);
+ } else {
+ /* disable counter 2 */
+ outb(inb_p(0x61) & 0xFC, 0x61);
+ }
+
+ raw_spin_unlock_irqrestore(&i8253_lock, flags);
+
+ return 0;
+}
+
+static int pcspkr_probe(struct platform_device *dev)
+{
+ struct input_dev *pcspkr_dev;
+ int err;
+
+ pcspkr_dev = input_allocate_device();
+ if (!pcspkr_dev)
+ return -ENOMEM;
+
+ pcspkr_dev->name = "PC Speaker";
+ pcspkr_dev->phys = "isa0061/input0";
+ pcspkr_dev->id.bustype = BUS_ISA;
+ pcspkr_dev->id.vendor = 0x001f;
+ pcspkr_dev->id.product = 0x0001;
+ pcspkr_dev->id.version = 0x0100;
+ pcspkr_dev->dev.parent = &dev->dev;
+
+ pcspkr_dev->evbit[0] = BIT_MASK(EV_SND);
+ pcspkr_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+ pcspkr_dev->event = pcspkr_event;
+
+ err = input_register_device(pcspkr_dev);
+ if (err) {
+ input_free_device(pcspkr_dev);
+ return err;
+ }
+
+ platform_set_drvdata(dev, pcspkr_dev);
+
+ return 0;
+}
+
+static int pcspkr_remove(struct platform_device *dev)
+{
+ struct input_dev *pcspkr_dev = platform_get_drvdata(dev);
+
+ input_unregister_device(pcspkr_dev);
+ platform_set_drvdata(dev, NULL);
+ /* turn off the speaker */
+ pcspkr_event(NULL, EV_SND, SND_BELL, 0);
+
+ return 0;
+}
+
+static int pcspkr_suspend(struct device *dev)
+{
+ pcspkr_event(NULL, EV_SND, SND_BELL, 0);
+
+ return 0;
+}
+
+static void pcspkr_shutdown(struct platform_device *dev)
+{
+ /* turn off the speaker */
+ pcspkr_event(NULL, EV_SND, SND_BELL, 0);
+}
+
+static const struct dev_pm_ops pcspkr_pm_ops = {
+ .suspend = pcspkr_suspend,
+};
+
+static struct platform_driver pcspkr_platform_driver = {
+ .driver = {
+ .name = "pcspkr",
+ .owner = THIS_MODULE,
+ .pm = &pcspkr_pm_ops,
+ },
+ .probe = pcspkr_probe,
+ .remove = pcspkr_remove,
+ .shutdown = pcspkr_shutdown,
+};
+module_platform_driver(pcspkr_platform_driver);
+
diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c
new file mode 100644
index 00000000..a9da65e4
--- /dev/null
+++ b/drivers/input/misc/pm8xxx-vibrator.c
@@ -0,0 +1,285 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/mfd/pm8xxx/core.h>
+
+#define VIB_DRV 0x4A
+
+#define VIB_DRV_SEL_MASK 0xf8
+#define VIB_DRV_SEL_SHIFT 0x03
+#define VIB_DRV_EN_MANUAL_MASK 0xfc
+
+#define VIB_MAX_LEVEL_mV (3100)
+#define VIB_MIN_LEVEL_mV (1200)
+#define VIB_MAX_LEVELS (VIB_MAX_LEVEL_mV - VIB_MIN_LEVEL_mV)
+
+#define MAX_FF_SPEED 0xff
+
+/**
+ * struct pm8xxx_vib - structure to hold vibrator data
+ * @vib_input_dev: input device supporting force feedback
+ * @work: work structure to set the vibration parameters
+ * @dev: device supporting force feedback
+ * @speed: speed of vibration set from userland
+ * @active: state of vibrator
+ * @level: level of vibration to set in the chip
+ * @reg_vib_drv: VIB_DRV register value
+ */
+struct pm8xxx_vib {
+ struct input_dev *vib_input_dev;
+ struct work_struct work;
+ struct device *dev;
+ int speed;
+ int level;
+ bool active;
+ u8 reg_vib_drv;
+};
+
+/**
+ * pm8xxx_vib_read_u8 - helper to read a byte from pmic chip
+ * @vib: pointer to vibrator structure
+ * @data: placeholder for data to be read
+ * @reg: register address
+ */
+static int pm8xxx_vib_read_u8(struct pm8xxx_vib *vib,
+ u8 *data, u16 reg)
+{
+ int rc;
+
+ rc = pm8xxx_readb(vib->dev->parent, reg, data);
+ if (rc < 0)
+ dev_warn(vib->dev, "Error reading pm8xxx reg 0x%x(0x%x)\n",
+ reg, rc);
+ return rc;
+}
+
+/**
+ * pm8xxx_vib_write_u8 - helper to write a byte to pmic chip
+ * @vib: pointer to vibrator structure
+ * @data: data to write
+ * @reg: register address
+ */
+static int pm8xxx_vib_write_u8(struct pm8xxx_vib *vib,
+ u8 data, u16 reg)
+{
+ int rc;
+
+ rc = pm8xxx_writeb(vib->dev->parent, reg, data);
+ if (rc < 0)
+ dev_warn(vib->dev, "Error writing pm8xxx reg 0x%x(0x%x)\n",
+ reg, rc);
+ return rc;
+}
+
+/**
+ * pm8xxx_vib_set - handler to start/stop vibration
+ * @vib: pointer to vibrator structure
+ * @on: state to set
+ */
+static int pm8xxx_vib_set(struct pm8xxx_vib *vib, bool on)
+{
+ int rc;
+ u8 val = vib->reg_vib_drv;
+
+ if (on)
+ val |= ((vib->level << VIB_DRV_SEL_SHIFT) & VIB_DRV_SEL_MASK);
+ else
+ val &= ~VIB_DRV_SEL_MASK;
+
+ rc = pm8xxx_vib_write_u8(vib, val, VIB_DRV);
+ if (rc < 0)
+ return rc;
+
+ vib->reg_vib_drv = val;
+ return 0;
+}
+
+/**
+ * pm8xxx_work_handler - worker to set vibration level
+ * @work: pointer to work_struct
+ */
+static void pm8xxx_work_handler(struct work_struct *work)
+{
+ struct pm8xxx_vib *vib = container_of(work, struct pm8xxx_vib, work);
+ int rc;
+ u8 val;
+
+ rc = pm8xxx_vib_read_u8(vib, &val, VIB_DRV);
+ if (rc < 0)
+ return;
+
+ /*
+ * pmic vibrator supports voltage ranges from 1.2 to 3.1V, so
+ * scale the level to fit into these ranges.
+ */
+ if (vib->speed) {
+ vib->active = true;
+ vib->level = ((VIB_MAX_LEVELS * vib->speed) / MAX_FF_SPEED) +
+ VIB_MIN_LEVEL_mV;
+ vib->level /= 100;
+ } else {
+ vib->active = false;
+ vib->level = VIB_MIN_LEVEL_mV / 100;
+ }
+
+ pm8xxx_vib_set(vib, vib->active);
+}
+
+/**
+ * pm8xxx_vib_close - callback of input close callback
+ * @dev: input device pointer
+ *
+ * Turns off the vibrator.
+ */
+static void pm8xxx_vib_close(struct input_dev *dev)
+{
+ struct pm8xxx_vib *vib = input_get_drvdata(dev);
+
+ cancel_work_sync(&vib->work);
+ if (vib->active)
+ pm8xxx_vib_set(vib, false);
+}
+
+/**
+ * pm8xxx_vib_play_effect - function to handle vib effects.
+ * @dev: input device pointer
+ * @data: data of effect
+ * @effect: effect to play
+ *
+ * Currently this driver supports only rumble effects.
+ */
+static int pm8xxx_vib_play_effect(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct pm8xxx_vib *vib = input_get_drvdata(dev);
+
+ vib->speed = effect->u.rumble.strong_magnitude >> 8;
+ if (!vib->speed)
+ vib->speed = effect->u.rumble.weak_magnitude >> 9;
+
+ schedule_work(&vib->work);
+
+ return 0;
+}
+
+static int pm8xxx_vib_probe(struct platform_device *pdev)
+
+{
+ struct pm8xxx_vib *vib;
+ struct input_dev *input_dev;
+ int error;
+ u8 val;
+
+ vib = kzalloc(sizeof(*vib), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!vib || !input_dev) {
+ dev_err(&pdev->dev, "couldn't allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ INIT_WORK(&vib->work, pm8xxx_work_handler);
+ vib->dev = &pdev->dev;
+ vib->vib_input_dev = input_dev;
+
+ /* operate in manual mode */
+ error = pm8xxx_vib_read_u8(vib, &val, VIB_DRV);
+ if (error < 0)
+ goto err_free_mem;
+ val &= ~VIB_DRV_EN_MANUAL_MASK;
+ error = pm8xxx_vib_write_u8(vib, val, VIB_DRV);
+ if (error < 0)
+ goto err_free_mem;
+
+ vib->reg_vib_drv = val;
+
+ input_dev->name = "pm8xxx_vib_ffmemless";
+ input_dev->id.version = 1;
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->close = pm8xxx_vib_close;
+ input_set_drvdata(input_dev, vib);
+ input_set_capability(vib->vib_input_dev, EV_FF, FF_RUMBLE);
+
+ error = input_ff_create_memless(input_dev, NULL,
+ pm8xxx_vib_play_effect);
+ if (error) {
+ dev_err(&pdev->dev,
+ "couldn't register vibrator as FF device\n");
+ goto err_free_mem;
+ }
+
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&pdev->dev, "couldn't register input device\n");
+ goto err_destroy_memless;
+ }
+
+ platform_set_drvdata(pdev, vib);
+ return 0;
+
+err_destroy_memless:
+ input_ff_destroy(input_dev);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(vib);
+
+ return error;
+}
+
+static int pm8xxx_vib_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_vib *vib = platform_get_drvdata(pdev);
+
+ input_unregister_device(vib->vib_input_dev);
+ kfree(vib);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pm8xxx_vib_suspend(struct device *dev)
+{
+ struct pm8xxx_vib *vib = dev_get_drvdata(dev);
+
+ /* Turn off the vibrator */
+ pm8xxx_vib_set(vib, false);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm8xxx_vib_pm_ops, pm8xxx_vib_suspend, NULL);
+
+static struct platform_driver pm8xxx_vib_driver = {
+ .probe = pm8xxx_vib_probe,
+ .remove = pm8xxx_vib_remove,
+ .driver = {
+ .name = "pm8xxx-vib",
+ .owner = THIS_MODULE,
+ .pm = &pm8xxx_vib_pm_ops,
+ },
+};
+module_platform_driver(pm8xxx_vib_driver);
+
+MODULE_ALIAS("platform:pm8xxx_vib");
+MODULE_DESCRIPTION("PMIC8xxx vibrator driver based on ff-memless framework");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Amy Maloche <amaloche@codeaurora.org>");
diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
new file mode 100644
index 00000000..4b811be7
--- /dev/null
+++ b/drivers/input/misc/pmic8xxx-pwrkey.c
@@ -0,0 +1,221 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/log2.h>
+
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/input/pmic8xxx-pwrkey.h>
+
+#define PON_CNTL_1 0x1C
+#define PON_CNTL_PULL_UP BIT(7)
+#define PON_CNTL_TRIG_DELAY_MASK (0x7)
+
+/**
+ * struct pmic8xxx_pwrkey - pmic8xxx pwrkey information
+ * @key_press_irq: key press irq number
+ */
+struct pmic8xxx_pwrkey {
+ struct input_dev *pwr;
+ int key_press_irq;
+};
+
+static irqreturn_t pwrkey_press_irq(int irq, void *_pwrkey)
+{
+ struct pmic8xxx_pwrkey *pwrkey = _pwrkey;
+
+ input_report_key(pwrkey->pwr, KEY_POWER, 1);
+ input_sync(pwrkey->pwr);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t pwrkey_release_irq(int irq, void *_pwrkey)
+{
+ struct pmic8xxx_pwrkey *pwrkey = _pwrkey;
+
+ input_report_key(pwrkey->pwr, KEY_POWER, 0);
+ input_sync(pwrkey->pwr);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pmic8xxx_pwrkey_suspend(struct device *dev)
+{
+ struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(pwrkey->key_press_irq);
+
+ return 0;
+}
+
+static int pmic8xxx_pwrkey_resume(struct device *dev)
+{
+ struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(pwrkey->key_press_irq);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops,
+ pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume);
+
+static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
+{
+ struct input_dev *pwr;
+ int key_release_irq = platform_get_irq(pdev, 0);
+ int key_press_irq = platform_get_irq(pdev, 1);
+ int err;
+ unsigned int delay;
+ u8 pon_cntl;
+ struct pmic8xxx_pwrkey *pwrkey;
+ const struct pm8xxx_pwrkey_platform_data *pdata =
+ dev_get_platdata(&pdev->dev);
+
+ if (!pdata) {
+ dev_err(&pdev->dev, "power key platform data not supplied\n");
+ return -EINVAL;
+ }
+
+ if (pdata->kpd_trigger_delay_us > 62500) {
+ dev_err(&pdev->dev, "invalid power key trigger delay\n");
+ return -EINVAL;
+ }
+
+ pwrkey = kzalloc(sizeof(*pwrkey), GFP_KERNEL);
+ if (!pwrkey)
+ return -ENOMEM;
+
+ pwr = input_allocate_device();
+ if (!pwr) {
+ dev_dbg(&pdev->dev, "Can't allocate power button\n");
+ err = -ENOMEM;
+ goto free_pwrkey;
+ }
+
+ input_set_capability(pwr, EV_KEY, KEY_POWER);
+
+ pwr->name = "pmic8xxx_pwrkey";
+ pwr->phys = "pmic8xxx_pwrkey/input0";
+ pwr->dev.parent = &pdev->dev;
+
+ delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC;
+ delay = 1 + ilog2(delay);
+
+ err = pm8xxx_readb(pdev->dev.parent, PON_CNTL_1, &pon_cntl);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed reading PON_CNTL_1 err=%d\n", err);
+ goto free_input_dev;
+ }
+
+ pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK;
+ pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK);
+ if (pdata->pull_up)
+ pon_cntl |= PON_CNTL_PULL_UP;
+ else
+ pon_cntl &= ~PON_CNTL_PULL_UP;
+
+ err = pm8xxx_writeb(pdev->dev.parent, PON_CNTL_1, pon_cntl);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err);
+ goto free_input_dev;
+ }
+
+ err = input_register_device(pwr);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't register power key: %d\n", err);
+ goto free_input_dev;
+ }
+
+ pwrkey->key_press_irq = key_press_irq;
+ pwrkey->pwr = pwr;
+
+ platform_set_drvdata(pdev, pwrkey);
+
+ err = request_irq(key_press_irq, pwrkey_press_irq,
+ IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwrkey);
+ if (err < 0) {
+ dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",
+ key_press_irq, err);
+ goto unreg_input_dev;
+ }
+
+ err = request_irq(key_release_irq, pwrkey_release_irq,
+ IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_release", pwrkey);
+ if (err < 0) {
+ dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n",
+ key_release_irq, err);
+
+ goto free_press_irq;
+ }
+
+ device_init_wakeup(&pdev->dev, pdata->wakeup);
+
+ return 0;
+
+free_press_irq:
+ free_irq(key_press_irq, NULL);
+unreg_input_dev:
+ platform_set_drvdata(pdev, NULL);
+ input_unregister_device(pwr);
+ pwr = NULL;
+free_input_dev:
+ input_free_device(pwr);
+free_pwrkey:
+ kfree(pwrkey);
+ return err;
+}
+
+static int pmic8xxx_pwrkey_remove(struct platform_device *pdev)
+{
+ struct pmic8xxx_pwrkey *pwrkey = platform_get_drvdata(pdev);
+ int key_release_irq = platform_get_irq(pdev, 0);
+ int key_press_irq = platform_get_irq(pdev, 1);
+
+ device_init_wakeup(&pdev->dev, 0);
+
+ free_irq(key_press_irq, pwrkey);
+ free_irq(key_release_irq, pwrkey);
+ input_unregister_device(pwrkey->pwr);
+ platform_set_drvdata(pdev, NULL);
+ kfree(pwrkey);
+
+ return 0;
+}
+
+static struct platform_driver pmic8xxx_pwrkey_driver = {
+ .probe = pmic8xxx_pwrkey_probe,
+ .remove = pmic8xxx_pwrkey_remove,
+ .driver = {
+ .name = PM8XXX_PWRKEY_DEV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &pm8xxx_pwr_key_pm_ops,
+ },
+};
+module_platform_driver(pmic8xxx_pwrkey_driver);
+
+MODULE_ALIAS("platform:pmic8xxx_pwrkey");
+MODULE_DESCRIPTION("PMIC8XXX Power Key driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Trilok Soni <tsoni@codeaurora.org>");
diff --git a/drivers/input/misc/powermate.c b/drivers/input/misc/powermate.c
new file mode 100644
index 00000000..49c0c3eb
--- /dev/null
+++ b/drivers/input/misc/powermate.c
@@ -0,0 +1,453 @@
+/*
+ * A driver for the Griffin Technology, Inc. "PowerMate" USB controller dial.
+ *
+ * v1.1, (c)2002 William R Sowerbutts <will@sowerbutts.com>
+ *
+ * This device is a anodised aluminium knob which connects over USB. It can measure
+ * clockwise and anticlockwise rotation. The dial also acts as a pushbutton with
+ * a spring for automatic release. The base contains a pair of LEDs which illuminate
+ * the translucent base. It rotates without limit and reports its relative rotation
+ * back to the host when polled by the USB controller.
+ *
+ * Testing with the knob I have has shown that it measures approximately 94 "clicks"
+ * for one full rotation. Testing with my High Speed Rotation Actuator (ok, it was
+ * a variable speed cordless electric drill) has shown that the device can measure
+ * speeds of up to 7 clicks either clockwise or anticlockwise between pollings from
+ * the host. If it counts more than 7 clicks before it is polled, it will wrap back
+ * to zero and start counting again. This was at quite high speed, however, almost
+ * certainly faster than the human hand could turn it. Griffin say that it loses a
+ * pulse or two on a direction change; the granularity is so fine that I never
+ * noticed this in practice.
+ *
+ * The device's microcontroller can be programmed to set the LED to either a constant
+ * intensity, or to a rhythmic pulsing. Several patterns and speeds are available.
+ *
+ * Griffin were very happy to provide documentation and free hardware for development.
+ *
+ * Some userspace tools are available on the web: http://sowerbutts.com/powermate/
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/usb/input.h>
+
+#define POWERMATE_VENDOR 0x077d /* Griffin Technology, Inc. */
+#define POWERMATE_PRODUCT_NEW 0x0410 /* Griffin PowerMate */
+#define POWERMATE_PRODUCT_OLD 0x04AA /* Griffin soundKnob */
+
+#define CONTOUR_VENDOR 0x05f3 /* Contour Design, Inc. */
+#define CONTOUR_JOG 0x0240 /* Jog and Shuttle */
+
+/* these are the command codes we send to the device */
+#define SET_STATIC_BRIGHTNESS 0x01
+#define SET_PULSE_ASLEEP 0x02
+#define SET_PULSE_AWAKE 0x03
+#define SET_PULSE_MODE 0x04
+
+/* these refer to bits in the powermate_device's requires_update field. */
+#define UPDATE_STATIC_BRIGHTNESS (1<<0)
+#define UPDATE_PULSE_ASLEEP (1<<1)
+#define UPDATE_PULSE_AWAKE (1<<2)
+#define UPDATE_PULSE_MODE (1<<3)
+
+/* at least two versions of the hardware exist, with differing payload
+ sizes. the first three bytes always contain the "interesting" data in
+ the relevant format. */
+#define POWERMATE_PAYLOAD_SIZE_MAX 6
+#define POWERMATE_PAYLOAD_SIZE_MIN 3
+struct powermate_device {
+ signed char *data;
+ dma_addr_t data_dma;
+ struct urb *irq, *config;
+ struct usb_ctrlrequest *configcr;
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct input_dev *input;
+ spinlock_t lock;
+ int static_brightness;
+ int pulse_speed;
+ int pulse_table;
+ int pulse_asleep;
+ int pulse_awake;
+ int requires_update; // physical settings which are out of sync
+ char phys[64];
+};
+
+static char pm_name_powermate[] = "Griffin PowerMate";
+static char pm_name_soundknob[] = "Griffin SoundKnob";
+
+static void powermate_config_complete(struct urb *urb);
+
+/* Callback for data arriving from the PowerMate over the USB interrupt pipe */
+static void powermate_irq(struct urb *urb)
+{
+ struct powermate_device *pm = urb->context;
+ struct device *dev = &pm->intf->dev;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dev_dbg(dev, "%s - urb shutting down with status: %d\n",
+ __func__, urb->status);
+ return;
+ default:
+ dev_dbg(dev, "%s - nonzero urb status received: %d\n",
+ __func__, urb->status);
+ goto exit;
+ }
+
+ /* handle updates to device state */
+ input_report_key(pm->input, BTN_0, pm->data[0] & 0x01);
+ input_report_rel(pm->input, REL_DIAL, pm->data[1]);
+ input_sync(pm->input);
+
+exit:
+ retval = usb_submit_urb (urb, GFP_ATOMIC);
+ if (retval)
+ dev_err(dev, "%s - usb_submit_urb failed with result: %d\n",
+ __func__, retval);
+}
+
+/* Decide if we need to issue a control message and do so. Must be called with pm->lock taken */
+static void powermate_sync_state(struct powermate_device *pm)
+{
+ if (pm->requires_update == 0)
+ return; /* no updates are required */
+ if (pm->config->status == -EINPROGRESS)
+ return; /* an update is already in progress; it'll issue this update when it completes */
+
+ if (pm->requires_update & UPDATE_PULSE_ASLEEP){
+ pm->configcr->wValue = cpu_to_le16( SET_PULSE_ASLEEP );
+ pm->configcr->wIndex = cpu_to_le16( pm->pulse_asleep ? 1 : 0 );
+ pm->requires_update &= ~UPDATE_PULSE_ASLEEP;
+ }else if (pm->requires_update & UPDATE_PULSE_AWAKE){
+ pm->configcr->wValue = cpu_to_le16( SET_PULSE_AWAKE );
+ pm->configcr->wIndex = cpu_to_le16( pm->pulse_awake ? 1 : 0 );
+ pm->requires_update &= ~UPDATE_PULSE_AWAKE;
+ }else if (pm->requires_update & UPDATE_PULSE_MODE){
+ int op, arg;
+ /* the powermate takes an operation and an argument for its pulse algorithm.
+ the operation can be:
+ 0: divide the speed
+ 1: pulse at normal speed
+ 2: multiply the speed
+ the argument only has an effect for operations 0 and 2, and ranges between
+ 1 (least effect) to 255 (maximum effect).
+
+ thus, several states are equivalent and are coalesced into one state.
+
+ we map this onto a range from 0 to 510, with:
+ 0 -- 254 -- use divide (0 = slowest)
+ 255 -- use normal speed
+ 256 -- 510 -- use multiple (510 = fastest).
+
+ Only values of 'arg' quite close to 255 are particularly useful/spectacular.
+ */
+ if (pm->pulse_speed < 255) {
+ op = 0; // divide
+ arg = 255 - pm->pulse_speed;
+ } else if (pm->pulse_speed > 255) {
+ op = 2; // multiply
+ arg = pm->pulse_speed - 255;
+ } else {
+ op = 1; // normal speed
+ arg = 0; // can be any value
+ }
+ pm->configcr->wValue = cpu_to_le16( (pm->pulse_table << 8) | SET_PULSE_MODE );
+ pm->configcr->wIndex = cpu_to_le16( (arg << 8) | op );
+ pm->requires_update &= ~UPDATE_PULSE_MODE;
+ } else if (pm->requires_update & UPDATE_STATIC_BRIGHTNESS) {
+ pm->configcr->wValue = cpu_to_le16( SET_STATIC_BRIGHTNESS );
+ pm->configcr->wIndex = cpu_to_le16( pm->static_brightness );
+ pm->requires_update &= ~UPDATE_STATIC_BRIGHTNESS;
+ } else {
+ printk(KERN_ERR "powermate: unknown update required");
+ pm->requires_update = 0; /* fudge the bug */
+ return;
+ }
+
+/* printk("powermate: %04x %04x\n", pm->configcr->wValue, pm->configcr->wIndex); */
+
+ pm->configcr->bRequestType = 0x41; /* vendor request */
+ pm->configcr->bRequest = 0x01;
+ pm->configcr->wLength = 0;
+
+ usb_fill_control_urb(pm->config, pm->udev, usb_sndctrlpipe(pm->udev, 0),
+ (void *) pm->configcr, NULL, 0,
+ powermate_config_complete, pm);
+
+ if (usb_submit_urb(pm->config, GFP_ATOMIC))
+ printk(KERN_ERR "powermate: usb_submit_urb(config) failed");
+}
+
+/* Called when our asynchronous control message completes. We may need to issue another immediately */
+static void powermate_config_complete(struct urb *urb)
+{
+ struct powermate_device *pm = urb->context;
+ unsigned long flags;
+
+ if (urb->status)
+ printk(KERN_ERR "powermate: config urb returned %d\n", urb->status);
+
+ spin_lock_irqsave(&pm->lock, flags);
+ powermate_sync_state(pm);
+ spin_unlock_irqrestore(&pm->lock, flags);
+}
+
+/* Set the LED up as described and begin the sync with the hardware if required */
+static void powermate_pulse_led(struct powermate_device *pm, int static_brightness, int pulse_speed,
+ int pulse_table, int pulse_asleep, int pulse_awake)
+{
+ unsigned long flags;
+
+ if (pulse_speed < 0)
+ pulse_speed = 0;
+ if (pulse_table < 0)
+ pulse_table = 0;
+ if (pulse_speed > 510)
+ pulse_speed = 510;
+ if (pulse_table > 2)
+ pulse_table = 2;
+
+ pulse_asleep = !!pulse_asleep;
+ pulse_awake = !!pulse_awake;
+
+
+ spin_lock_irqsave(&pm->lock, flags);
+
+ /* mark state updates which are required */
+ if (static_brightness != pm->static_brightness) {
+ pm->static_brightness = static_brightness;
+ pm->requires_update |= UPDATE_STATIC_BRIGHTNESS;
+ }
+ if (pulse_asleep != pm->pulse_asleep) {
+ pm->pulse_asleep = pulse_asleep;
+ pm->requires_update |= (UPDATE_PULSE_ASLEEP | UPDATE_STATIC_BRIGHTNESS);
+ }
+ if (pulse_awake != pm->pulse_awake) {
+ pm->pulse_awake = pulse_awake;
+ pm->requires_update |= (UPDATE_PULSE_AWAKE | UPDATE_STATIC_BRIGHTNESS);
+ }
+ if (pulse_speed != pm->pulse_speed || pulse_table != pm->pulse_table) {
+ pm->pulse_speed = pulse_speed;
+ pm->pulse_table = pulse_table;
+ pm->requires_update |= UPDATE_PULSE_MODE;
+ }
+
+ powermate_sync_state(pm);
+
+ spin_unlock_irqrestore(&pm->lock, flags);
+}
+
+/* Callback from the Input layer when an event arrives from userspace to configure the LED */
+static int powermate_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int _value)
+{
+ unsigned int command = (unsigned int)_value;
+ struct powermate_device *pm = input_get_drvdata(dev);
+
+ if (type == EV_MSC && code == MSC_PULSELED){
+ /*
+ bits 0- 7: 8 bits: LED brightness
+ bits 8-16: 9 bits: pulsing speed modifier (0 ... 510); 0-254 = slower, 255 = standard, 256-510 = faster.
+ bits 17-18: 2 bits: pulse table (0, 1, 2 valid)
+ bit 19: 1 bit : pulse whilst asleep?
+ bit 20: 1 bit : pulse constantly?
+ */
+ int static_brightness = command & 0xFF; // bits 0-7
+ int pulse_speed = (command >> 8) & 0x1FF; // bits 8-16
+ int pulse_table = (command >> 17) & 0x3; // bits 17-18
+ int pulse_asleep = (command >> 19) & 0x1; // bit 19
+ int pulse_awake = (command >> 20) & 0x1; // bit 20
+
+ powermate_pulse_led(pm, static_brightness, pulse_speed, pulse_table, pulse_asleep, pulse_awake);
+ }
+
+ return 0;
+}
+
+static int powermate_alloc_buffers(struct usb_device *udev, struct powermate_device *pm)
+{
+ pm->data = usb_alloc_coherent(udev, POWERMATE_PAYLOAD_SIZE_MAX,
+ GFP_ATOMIC, &pm->data_dma);
+ if (!pm->data)
+ return -1;
+
+ pm->configcr = kmalloc(sizeof(*(pm->configcr)), GFP_KERNEL);
+ if (!pm->configcr)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void powermate_free_buffers(struct usb_device *udev, struct powermate_device *pm)
+{
+ usb_free_coherent(udev, POWERMATE_PAYLOAD_SIZE_MAX,
+ pm->data, pm->data_dma);
+ kfree(pm->configcr);
+}
+
+/* Called whenever a USB device matching one in our supported devices table is connected */
+static int powermate_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev (intf);
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct powermate_device *pm;
+ struct input_dev *input_dev;
+ int pipe, maxp;
+ int error = -ENOMEM;
+
+ interface = intf->cur_altsetting;
+ endpoint = &interface->endpoint[0].desc;
+ if (!usb_endpoint_is_int_in(endpoint))
+ return -EIO;
+
+ usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x0a, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, interface->desc.bInterfaceNumber, NULL, 0,
+ USB_CTRL_SET_TIMEOUT);
+
+ pm = kzalloc(sizeof(struct powermate_device), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!pm || !input_dev)
+ goto fail1;
+
+ if (powermate_alloc_buffers(udev, pm))
+ goto fail2;
+
+ pm->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pm->irq)
+ goto fail2;
+
+ pm->config = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pm->config)
+ goto fail3;
+
+ pm->udev = udev;
+ pm->intf = intf;
+ pm->input = input_dev;
+
+ usb_make_path(udev, pm->phys, sizeof(pm->phys));
+ strlcat(pm->phys, "/input0", sizeof(pm->phys));
+
+ spin_lock_init(&pm->lock);
+
+ switch (le16_to_cpu(udev->descriptor.idProduct)) {
+ case POWERMATE_PRODUCT_NEW:
+ input_dev->name = pm_name_powermate;
+ break;
+ case POWERMATE_PRODUCT_OLD:
+ input_dev->name = pm_name_soundknob;
+ break;
+ default:
+ input_dev->name = pm_name_soundknob;
+ printk(KERN_WARNING "powermate: unknown product id %04x\n",
+ le16_to_cpu(udev->descriptor.idProduct));
+ }
+
+ input_dev->phys = pm->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, pm);
+
+ input_dev->event = powermate_input_event;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL) |
+ BIT_MASK(EV_MSC);
+ input_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
+ input_dev->relbit[BIT_WORD(REL_DIAL)] = BIT_MASK(REL_DIAL);
+ input_dev->mscbit[BIT_WORD(MSC_PULSELED)] = BIT_MASK(MSC_PULSELED);
+
+ /* get a handle to the interrupt data pipe */
+ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+
+ if (maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX) {
+ printk(KERN_WARNING "powermate: Expected payload of %d--%d bytes, found %d bytes!\n",
+ POWERMATE_PAYLOAD_SIZE_MIN, POWERMATE_PAYLOAD_SIZE_MAX, maxp);
+ maxp = POWERMATE_PAYLOAD_SIZE_MAX;
+ }
+
+ usb_fill_int_urb(pm->irq, udev, pipe, pm->data,
+ maxp, powermate_irq,
+ pm, endpoint->bInterval);
+ pm->irq->transfer_dma = pm->data_dma;
+ pm->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ /* register our interrupt URB with the USB system */
+ if (usb_submit_urb(pm->irq, GFP_KERNEL)) {
+ error = -EIO;
+ goto fail4;
+ }
+
+ error = input_register_device(pm->input);
+ if (error)
+ goto fail5;
+
+
+ /* force an update of everything */
+ pm->requires_update = UPDATE_PULSE_ASLEEP | UPDATE_PULSE_AWAKE | UPDATE_PULSE_MODE | UPDATE_STATIC_BRIGHTNESS;
+ powermate_pulse_led(pm, 0x80, 255, 0, 1, 0); // set default pulse parameters
+
+ usb_set_intfdata(intf, pm);
+ return 0;
+
+ fail5: usb_kill_urb(pm->irq);
+ fail4: usb_free_urb(pm->config);
+ fail3: usb_free_urb(pm->irq);
+ fail2: powermate_free_buffers(udev, pm);
+ fail1: input_free_device(input_dev);
+ kfree(pm);
+ return error;
+}
+
+/* Called when a USB device we've accepted ownership of is removed */
+static void powermate_disconnect(struct usb_interface *intf)
+{
+ struct powermate_device *pm = usb_get_intfdata (intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (pm) {
+ pm->requires_update = 0;
+ usb_kill_urb(pm->irq);
+ input_unregister_device(pm->input);
+ usb_free_urb(pm->irq);
+ usb_free_urb(pm->config);
+ powermate_free_buffers(interface_to_usbdev(intf), pm);
+
+ kfree(pm);
+ }
+}
+
+static struct usb_device_id powermate_devices [] = {
+ { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_NEW) },
+ { USB_DEVICE(POWERMATE_VENDOR, POWERMATE_PRODUCT_OLD) },
+ { USB_DEVICE(CONTOUR_VENDOR, CONTOUR_JOG) },
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, powermate_devices);
+
+static struct usb_driver powermate_driver = {
+ .name = "powermate",
+ .probe = powermate_probe,
+ .disconnect = powermate_disconnect,
+ .id_table = powermate_devices,
+};
+
+module_usb_driver(powermate_driver);
+
+MODULE_AUTHOR( "William R Sowerbutts" );
+MODULE_DESCRIPTION( "Griffin Technology, Inc PowerMate driver" );
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c
new file mode 100644
index 00000000..08088684
--- /dev/null
+++ b/drivers/input/misc/pwm-beeper.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ * PWM beeper driver
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/pwm.h>
+#include <linux/slab.h>
+
+struct pwm_beeper {
+ struct input_dev *input;
+ struct pwm_device *pwm;
+ unsigned long period;
+};
+
+#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x))
+
+static int pwm_beeper_event(struct input_dev *input,
+ unsigned int type, unsigned int code, int value)
+{
+ int ret = 0;
+ struct pwm_beeper *beeper = input_get_drvdata(input);
+ unsigned long period;
+
+ if (type != EV_SND || value < 0)
+ return -EINVAL;
+
+ switch (code) {
+ case SND_BELL:
+ value = value ? 1000 : 0;
+ break;
+ case SND_TONE:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (value == 0) {
+ pwm_config(beeper->pwm, 0, 0);
+ pwm_disable(beeper->pwm);
+ } else {
+ period = HZ_TO_NANOSECONDS(value);
+ ret = pwm_config(beeper->pwm, period / 2, period);
+ if (ret)
+ return ret;
+ ret = pwm_enable(beeper->pwm);
+ if (ret)
+ return ret;
+ beeper->period = period;
+ }
+
+ return 0;
+}
+
+static int pwm_beeper_probe(struct platform_device *pdev)
+{
+ unsigned long pwm_id = (unsigned long)pdev->dev.platform_data;
+ struct pwm_beeper *beeper;
+ int error;
+
+ beeper = kzalloc(sizeof(*beeper), GFP_KERNEL);
+ if (!beeper)
+ return -ENOMEM;
+
+ beeper->pwm = pwm_get(&pdev->dev, NULL);
+ if (IS_ERR(beeper->pwm)) {
+ dev_dbg(&pdev->dev, "unable to request PWM, trying legacy API\n");
+ beeper->pwm = pwm_request(pwm_id, "pwm beeper");
+ }
+
+ if (IS_ERR(beeper->pwm)) {
+ error = PTR_ERR(beeper->pwm);
+ dev_err(&pdev->dev, "Failed to request pwm device: %d\n", error);
+ goto err_free;
+ }
+
+ beeper->input = input_allocate_device();
+ if (!beeper->input) {
+ dev_err(&pdev->dev, "Failed to allocate input device\n");
+ error = -ENOMEM;
+ goto err_pwm_free;
+ }
+ beeper->input->dev.parent = &pdev->dev;
+
+ beeper->input->name = "pwm-beeper";
+ beeper->input->phys = "pwm/input0";
+ beeper->input->id.bustype = BUS_HOST;
+ beeper->input->id.vendor = 0x001f;
+ beeper->input->id.product = 0x0001;
+ beeper->input->id.version = 0x0100;
+
+ beeper->input->evbit[0] = BIT(EV_SND);
+ beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL);
+
+ beeper->input->event = pwm_beeper_event;
+
+ input_set_drvdata(beeper->input, beeper);
+
+ error = input_register_device(beeper->input);
+ if (error) {
+ dev_err(&pdev->dev, "Failed to register input device: %d\n", error);
+ goto err_input_free;
+ }
+
+ platform_set_drvdata(pdev, beeper);
+
+ return 0;
+
+err_input_free:
+ input_free_device(beeper->input);
+err_pwm_free:
+ pwm_free(beeper->pwm);
+err_free:
+ kfree(beeper);
+
+ return error;
+}
+
+static int pwm_beeper_remove(struct platform_device *pdev)
+{
+ struct pwm_beeper *beeper = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+ input_unregister_device(beeper->input);
+
+ pwm_disable(beeper->pwm);
+ pwm_free(beeper->pwm);
+
+ kfree(beeper);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pwm_beeper_suspend(struct device *dev)
+{
+ struct pwm_beeper *beeper = dev_get_drvdata(dev);
+
+ if (beeper->period)
+ pwm_disable(beeper->pwm);
+
+ return 0;
+}
+
+static int pwm_beeper_resume(struct device *dev)
+{
+ struct pwm_beeper *beeper = dev_get_drvdata(dev);
+
+ if (beeper->period) {
+ pwm_config(beeper->pwm, beeper->period / 2, beeper->period);
+ pwm_enable(beeper->pwm);
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops,
+ pwm_beeper_suspend, pwm_beeper_resume);
+
+#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops)
+#else
+#define PWM_BEEPER_PM_OPS NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id pwm_beeper_match[] = {
+ { .compatible = "pwm-beeper", },
+ { },
+};
+#endif
+
+static struct platform_driver pwm_beeper_driver = {
+ .probe = pwm_beeper_probe,
+ .remove = pwm_beeper_remove,
+ .driver = {
+ .name = "pwm-beeper",
+ .owner = THIS_MODULE,
+ .pm = PWM_BEEPER_PM_OPS,
+ .of_match_table = of_match_ptr(pwm_beeper_match),
+ },
+};
+module_platform_driver(pwm_beeper_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("PWM beeper driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pwm-beeper");
diff --git a/drivers/input/misc/rb532_button.c b/drivers/input/misc/rb532_button.c
new file mode 100644
index 00000000..fb4f8ac3
--- /dev/null
+++ b/drivers/input/misc/rb532_button.c
@@ -0,0 +1,108 @@
+/*
+ * Support for the S1 button on Routerboard 532
+ *
+ * Copyright (C) 2009 Phil Sutter <n0-1@freewrt.org>
+ */
+
+#include <linux/input-polldev.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-rc32434/gpio.h>
+#include <asm/mach-rc32434/rb.h>
+
+#define DRV_NAME "rb532-button"
+
+#define RB532_BTN_RATE 100 /* msec */
+#define RB532_BTN_KSYM BTN_0
+
+/* The S1 button state is provided by GPIO pin 1. But as this
+ * pin is also used for uart input as alternate function, the
+ * operational modes must be switched first:
+ * 1) disable uart using set_latch_u5()
+ * 2) turn off alternate function implicitly through
+ * gpio_direction_input()
+ * 3) read the GPIO's current value
+ * 4) undo step 2 by enabling alternate function (in this
+ * mode the GPIO direction is fixed, so no change needed)
+ * 5) turn on uart again
+ * The GPIO value occurs to be inverted, so pin high means
+ * button is not pressed.
+ */
+static bool rb532_button_pressed(void)
+{
+ int val;
+
+ set_latch_u5(0, LO_FOFF);
+ gpio_direction_input(GPIO_BTN_S1);
+
+ val = gpio_get_value(GPIO_BTN_S1);
+
+ rb532_gpio_set_func(GPIO_BTN_S1);
+ set_latch_u5(LO_FOFF, 0);
+
+ return !val;
+}
+
+static void rb532_button_poll(struct input_polled_dev *poll_dev)
+{
+ input_report_key(poll_dev->input, RB532_BTN_KSYM,
+ rb532_button_pressed());
+ input_sync(poll_dev->input);
+}
+
+static int rb532_button_probe(struct platform_device *pdev)
+{
+ struct input_polled_dev *poll_dev;
+ int error;
+
+ poll_dev = input_allocate_polled_device();
+ if (!poll_dev)
+ return -ENOMEM;
+
+ poll_dev->poll = rb532_button_poll;
+ poll_dev->poll_interval = RB532_BTN_RATE;
+
+ poll_dev->input->name = "rb532 button";
+ poll_dev->input->phys = "rb532/button0";
+ poll_dev->input->id.bustype = BUS_HOST;
+ poll_dev->input->dev.parent = &pdev->dev;
+
+ dev_set_drvdata(&pdev->dev, poll_dev);
+
+ input_set_capability(poll_dev->input, EV_KEY, RB532_BTN_KSYM);
+
+ error = input_register_polled_device(poll_dev);
+ if (error) {
+ input_free_polled_device(poll_dev);
+ return error;
+ }
+
+ return 0;
+}
+
+static int rb532_button_remove(struct platform_device *pdev)
+{
+ struct input_polled_dev *poll_dev = dev_get_drvdata(&pdev->dev);
+
+ input_unregister_polled_device(poll_dev);
+ input_free_polled_device(poll_dev);
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver rb532_button_driver = {
+ .probe = rb532_button_probe,
+ .remove = rb532_button_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(rb532_button_driver);
+
+MODULE_AUTHOR("Phil Sutter <n0-1@freewrt.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Support for S1 button on Routerboard 532");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/input/misc/retu-pwrbutton.c b/drivers/input/misc/retu-pwrbutton.c
new file mode 100644
index 00000000..7ca09baa
--- /dev/null
+++ b/drivers/input/misc/retu-pwrbutton.c
@@ -0,0 +1,99 @@
+/*
+ * Retu power button driver.
+ *
+ * Copyright (C) 2004-2010 Nokia Corporation
+ *
+ * Original code written by Ari Saastamoinen, Juha Yrjölä and Felipe Balbi.
+ * Rewritten by Aaro Koskinen.
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/retu.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+
+#define RETU_STATUS_PWRONX (1 << 5)
+
+static irqreturn_t retu_pwrbutton_irq(int irq, void *_pwr)
+{
+ struct input_dev *idev = _pwr;
+ struct retu_dev *rdev = input_get_drvdata(idev);
+ bool state;
+
+ state = !(retu_read(rdev, RETU_REG_STATUS) & RETU_STATUS_PWRONX);
+ input_report_key(idev, KEY_POWER, state);
+ input_sync(idev);
+
+ return IRQ_HANDLED;
+}
+
+static int retu_pwrbutton_probe(struct platform_device *pdev)
+{
+ struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
+ struct input_dev *idev;
+ int irq;
+ int error;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ idev = devm_input_allocate_device(&pdev->dev);
+ if (!idev)
+ return -ENOMEM;
+
+ idev->name = "retu-pwrbutton";
+ idev->dev.parent = &pdev->dev;
+
+ input_set_capability(idev, EV_KEY, KEY_POWER);
+ input_set_drvdata(idev, rdev);
+
+ error = devm_request_threaded_irq(&pdev->dev, irq,
+ NULL, retu_pwrbutton_irq, 0,
+ "retu-pwrbutton", idev);
+ if (error)
+ return error;
+
+ error = input_register_device(idev);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static int retu_pwrbutton_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver retu_pwrbutton_driver = {
+ .probe = retu_pwrbutton_probe,
+ .remove = retu_pwrbutton_remove,
+ .driver = {
+ .name = "retu-pwrbutton",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(retu_pwrbutton_driver);
+
+MODULE_ALIAS("platform:retu-pwrbutton");
+MODULE_DESCRIPTION("Retu Power Button");
+MODULE_AUTHOR("Ari Saastamoinen");
+MODULE_AUTHOR("Felipe Balbi");
+MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c
new file mode 100644
index 00000000..aff47b2c
--- /dev/null
+++ b/drivers/input/misc/rotary_encoder.c
@@ -0,0 +1,339 @@
+/*
+ * rotary_encoder.c
+ *
+ * (c) 2009 Daniel Mack <daniel@caiaq.de>
+ * Copyright (C) 2011 Johan Hovold <jhovold@gmail.com>
+ *
+ * state machine code inspired by code from Tim Ruetz
+ *
+ * A generic driver for rotary encoders connected to GPIO lines.
+ * See file:Documentation/input/rotary-encoder.txt for more information
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/rotary_encoder.h>
+#include <linux/slab.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+
+#define DRV_NAME "rotary-encoder"
+
+struct rotary_encoder {
+ struct input_dev *input;
+ const struct rotary_encoder_platform_data *pdata;
+
+ unsigned int axis;
+ unsigned int pos;
+
+ unsigned int irq_a;
+ unsigned int irq_b;
+
+ bool armed;
+ unsigned char dir; /* 0 - clockwise, 1 - CCW */
+
+ char last_stable;
+};
+
+static int rotary_encoder_get_state(const struct rotary_encoder_platform_data *pdata)
+{
+ int a = !!gpio_get_value(pdata->gpio_a);
+ int b = !!gpio_get_value(pdata->gpio_b);
+
+ a ^= pdata->inverted_a;
+ b ^= pdata->inverted_b;
+
+ return ((a << 1) | b);
+}
+
+static void rotary_encoder_report_event(struct rotary_encoder *encoder)
+{
+ const struct rotary_encoder_platform_data *pdata = encoder->pdata;
+
+ if (pdata->relative_axis) {
+ input_report_rel(encoder->input,
+ pdata->axis, encoder->dir ? -1 : 1);
+ } else {
+ unsigned int pos = encoder->pos;
+
+ if (encoder->dir) {
+ /* turning counter-clockwise */
+ if (pdata->rollover)
+ pos += pdata->steps;
+ if (pos)
+ pos--;
+ } else {
+ /* turning clockwise */
+ if (pdata->rollover || pos < pdata->steps)
+ pos++;
+ }
+
+ if (pdata->rollover)
+ pos %= pdata->steps;
+
+ encoder->pos = pos;
+ input_report_abs(encoder->input, pdata->axis, encoder->pos);
+ }
+
+ input_sync(encoder->input);
+}
+
+static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
+{
+ struct rotary_encoder *encoder = dev_id;
+ int state;
+
+ state = rotary_encoder_get_state(encoder->pdata);
+
+ switch (state) {
+ case 0x0:
+ if (encoder->armed) {
+ rotary_encoder_report_event(encoder);
+ encoder->armed = false;
+ }
+ break;
+
+ case 0x1:
+ case 0x2:
+ if (encoder->armed)
+ encoder->dir = state - 1;
+ break;
+
+ case 0x3:
+ encoder->armed = true;
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
+{
+ struct rotary_encoder *encoder = dev_id;
+ int state;
+
+ state = rotary_encoder_get_state(encoder->pdata);
+
+ switch (state) {
+ case 0x00:
+ case 0x03:
+ if (state != encoder->last_stable) {
+ rotary_encoder_report_event(encoder);
+ encoder->last_stable = state;
+ }
+ break;
+
+ case 0x01:
+ case 0x02:
+ encoder->dir = (encoder->last_stable + state) & 0x01;
+ break;
+ }
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id rotary_encoder_of_match[] = {
+ { .compatible = "rotary-encoder", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, rotary_encoder_of_match);
+
+static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct device *dev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(rotary_encoder_of_match, dev);
+ struct device_node *np = dev->of_node;
+ struct rotary_encoder_platform_data *pdata;
+ enum of_gpio_flags flags;
+
+ if (!of_id || !np)
+ return NULL;
+
+ pdata = kzalloc(sizeof(struct rotary_encoder_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ of_property_read_u32(np, "rotary-encoder,steps", &pdata->steps);
+ of_property_read_u32(np, "linux,axis", &pdata->axis);
+
+ pdata->gpio_a = of_get_gpio_flags(np, 0, &flags);
+ pdata->inverted_a = flags & OF_GPIO_ACTIVE_LOW;
+
+ pdata->gpio_b = of_get_gpio_flags(np, 1, &flags);
+ pdata->inverted_b = flags & OF_GPIO_ACTIVE_LOW;
+
+ pdata->relative_axis = !!of_get_property(np,
+ "rotary-encoder,relative-axis", NULL);
+ pdata->rollover = !!of_get_property(np,
+ "rotary-encoder,rollover", NULL);
+ pdata->half_period = !!of_get_property(np,
+ "rotary-encoder,half-period", NULL);
+
+ return pdata;
+}
+#else
+static inline struct rotary_encoder_platform_data *
+rotary_encoder_parse_dt(struct device *dev)
+{
+ return NULL;
+}
+#endif
+
+static int rotary_encoder_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ const struct rotary_encoder_platform_data *pdata = dev_get_platdata(dev);
+ struct rotary_encoder *encoder;
+ struct input_dev *input;
+ irq_handler_t handler;
+ int err;
+
+ if (!pdata) {
+ pdata = rotary_encoder_parse_dt(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+
+ if (!pdata) {
+ dev_err(dev, "missing platform data\n");
+ return -EINVAL;
+ }
+ }
+
+ encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL);
+ input = input_allocate_device();
+ if (!encoder || !input) {
+ err = -ENOMEM;
+ goto exit_free_mem;
+ }
+
+ encoder->input = input;
+ encoder->pdata = pdata;
+
+ input->name = pdev->name;
+ input->id.bustype = BUS_HOST;
+ input->dev.parent = dev;
+
+ if (pdata->relative_axis) {
+ input->evbit[0] = BIT_MASK(EV_REL);
+ input->relbit[0] = BIT_MASK(pdata->axis);
+ } else {
+ input->evbit[0] = BIT_MASK(EV_ABS);
+ input_set_abs_params(encoder->input,
+ pdata->axis, 0, pdata->steps, 0, 1);
+ }
+
+ /* request the GPIOs */
+ err = gpio_request_one(pdata->gpio_a, GPIOF_IN, dev_name(dev));
+ if (err) {
+ dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_a);
+ goto exit_free_mem;
+ }
+
+ err = gpio_request_one(pdata->gpio_b, GPIOF_IN, dev_name(dev));
+ if (err) {
+ dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_b);
+ goto exit_free_gpio_a;
+ }
+
+ encoder->irq_a = gpio_to_irq(pdata->gpio_a);
+ encoder->irq_b = gpio_to_irq(pdata->gpio_b);
+
+ /* request the IRQs */
+ if (pdata->half_period) {
+ handler = &rotary_encoder_half_period_irq;
+ encoder->last_stable = rotary_encoder_get_state(pdata);
+ } else {
+ handler = &rotary_encoder_irq;
+ }
+
+ err = request_irq(encoder->irq_a, handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ DRV_NAME, encoder);
+ if (err) {
+ dev_err(dev, "unable to request IRQ %d\n", encoder->irq_a);
+ goto exit_free_gpio_b;
+ }
+
+ err = request_irq(encoder->irq_b, handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ DRV_NAME, encoder);
+ if (err) {
+ dev_err(dev, "unable to request IRQ %d\n", encoder->irq_b);
+ goto exit_free_irq_a;
+ }
+
+ err = input_register_device(input);
+ if (err) {
+ dev_err(dev, "failed to register input device\n");
+ goto exit_free_irq_b;
+ }
+
+ platform_set_drvdata(pdev, encoder);
+
+ return 0;
+
+exit_free_irq_b:
+ free_irq(encoder->irq_b, encoder);
+exit_free_irq_a:
+ free_irq(encoder->irq_a, encoder);
+exit_free_gpio_b:
+ gpio_free(pdata->gpio_b);
+exit_free_gpio_a:
+ gpio_free(pdata->gpio_a);
+exit_free_mem:
+ input_free_device(input);
+ kfree(encoder);
+ if (!dev_get_platdata(&pdev->dev))
+ kfree(pdata);
+
+ return err;
+}
+
+static int rotary_encoder_remove(struct platform_device *pdev)
+{
+ struct rotary_encoder *encoder = platform_get_drvdata(pdev);
+ const struct rotary_encoder_platform_data *pdata = encoder->pdata;
+
+ free_irq(encoder->irq_a, encoder);
+ free_irq(encoder->irq_b, encoder);
+ gpio_free(pdata->gpio_a);
+ gpio_free(pdata->gpio_b);
+
+ input_unregister_device(encoder->input);
+ kfree(encoder);
+
+ if (!dev_get_platdata(&pdev->dev))
+ kfree(pdata);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver rotary_encoder_driver = {
+ .probe = rotary_encoder_probe,
+ .remove = rotary_encoder_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(rotary_encoder_of_match),
+ }
+};
+module_platform_driver(rotary_encoder_driver);
+
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DESCRIPTION("GPIO rotary encoder driver");
+MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>, Johan Hovold");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/sgi_btns.c b/drivers/input/misc/sgi_btns.c
new file mode 100644
index 00000000..ad6415ce
--- /dev/null
+++ b/drivers/input/misc/sgi_btns.c
@@ -0,0 +1,169 @@
+/*
+ * SGI Volume Button interface driver
+ *
+ * Copyright (C) 2008 Thomas Bogendoerfer <tsbogend@alpha.franken.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <linux/init.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#ifdef CONFIG_SGI_IP22
+#include <asm/sgi/ioc.h>
+
+static inline u8 button_status(void)
+{
+ u8 status;
+
+ status = readb(&sgioc->panel) ^ 0xa0;
+ return ((status & 0x80) >> 6) | ((status & 0x20) >> 5);
+}
+#endif
+
+#ifdef CONFIG_SGI_IP32
+#include <asm/ip32/mace.h>
+
+static inline u8 button_status(void)
+{
+ u64 status;
+
+ status = readq(&mace->perif.audio.control);
+ writeq(status & ~(3U << 23), &mace->perif.audio.control);
+
+ return (status >> 23) & 3;
+}
+#endif
+
+#define BUTTONS_POLL_INTERVAL 30 /* msec */
+#define BUTTONS_COUNT_THRESHOLD 3
+
+static const unsigned short sgi_map[] = {
+ KEY_VOLUMEDOWN,
+ KEY_VOLUMEUP
+};
+
+struct buttons_dev {
+ struct input_polled_dev *poll_dev;
+ unsigned short keymap[ARRAY_SIZE(sgi_map)];
+ int count[ARRAY_SIZE(sgi_map)];
+};
+
+static void handle_buttons(struct input_polled_dev *dev)
+{
+ struct buttons_dev *bdev = dev->private;
+ struct input_dev *input = dev->input;
+ u8 status;
+ int i;
+
+ status = button_status();
+
+ for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
+ if (status & (1U << i)) {
+ if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
+ input_event(input, EV_MSC, MSC_SCAN, i);
+ input_report_key(input, bdev->keymap[i], 1);
+ input_sync(input);
+ }
+ } else {
+ if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
+ input_event(input, EV_MSC, MSC_SCAN, i);
+ input_report_key(input, bdev->keymap[i], 0);
+ input_sync(input);
+ }
+ bdev->count[i] = 0;
+ }
+ }
+}
+
+static int sgi_buttons_probe(struct platform_device *pdev)
+{
+ struct buttons_dev *bdev;
+ struct input_polled_dev *poll_dev;
+ struct input_dev *input;
+ int error, i;
+
+ bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
+ poll_dev = input_allocate_polled_device();
+ if (!bdev || !poll_dev) {
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ memcpy(bdev->keymap, sgi_map, sizeof(bdev->keymap));
+
+ poll_dev->private = bdev;
+ poll_dev->poll = handle_buttons;
+ poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
+
+ input = poll_dev->input;
+ input->name = "SGI buttons";
+ input->phys = "sgi/input0";
+ input->id.bustype = BUS_HOST;
+ input->dev.parent = &pdev->dev;
+
+ input->keycode = bdev->keymap;
+ input->keycodemax = ARRAY_SIZE(bdev->keymap);
+ input->keycodesize = sizeof(unsigned short);
+
+ input_set_capability(input, EV_MSC, MSC_SCAN);
+ __set_bit(EV_KEY, input->evbit);
+ for (i = 0; i < ARRAY_SIZE(sgi_map); i++)
+ __set_bit(bdev->keymap[i], input->keybit);
+ __clear_bit(KEY_RESERVED, input->keybit);
+
+ bdev->poll_dev = poll_dev;
+ dev_set_drvdata(&pdev->dev, bdev);
+
+ error = input_register_polled_device(poll_dev);
+ if (error)
+ goto err_free_mem;
+
+ return 0;
+
+ err_free_mem:
+ input_free_polled_device(poll_dev);
+ kfree(bdev);
+ dev_set_drvdata(&pdev->dev, NULL);
+ return error;
+}
+
+static int sgi_buttons_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct buttons_dev *bdev = dev_get_drvdata(dev);
+
+ input_unregister_polled_device(bdev->poll_dev);
+ input_free_polled_device(bdev->poll_dev);
+ kfree(bdev);
+ dev_set_drvdata(dev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver sgi_buttons_driver = {
+ .probe = sgi_buttons_probe,
+ .remove = sgi_buttons_remove,
+ .driver = {
+ .name = "sgibtns",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(sgi_buttons_driver);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/sparcspkr.c b/drivers/input/misc/sparcspkr.c
new file mode 100644
index 00000000..a53586a7
--- /dev/null
+++ b/drivers/input/misc/sparcspkr.c
@@ -0,0 +1,372 @@
+/*
+ * Driver for PC-speaker like devices found on various Sparc systems.
+ *
+ * Copyright (c) 2002 Vojtech Pavlik
+ * Copyright (c) 2002, 2006, 2008 David S. Miller (davem@davemloft.net)
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+
+MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");
+MODULE_DESCRIPTION("Sparc Speaker beeper driver");
+MODULE_LICENSE("GPL");
+
+struct grover_beep_info {
+ void __iomem *freq_regs;
+ void __iomem *enable_reg;
+};
+
+struct bbc_beep_info {
+ u32 clock_freq;
+ void __iomem *regs;
+};
+
+struct sparcspkr_state {
+ const char *name;
+ int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
+ spinlock_t lock;
+ struct input_dev *input_dev;
+ union {
+ struct grover_beep_info grover;
+ struct bbc_beep_info bbc;
+ } u;
+};
+
+static u32 bbc_count_to_reg(struct bbc_beep_info *info, unsigned int count)
+{
+ u32 val, clock_freq = info->clock_freq;
+ int i;
+
+ if (!count)
+ return 0;
+
+ if (count <= clock_freq >> 20)
+ return 1 << 18;
+
+ if (count >= clock_freq >> 12)
+ return 1 << 10;
+
+ val = 1 << 18;
+ for (i = 19; i >= 11; i--) {
+ val >>= 1;
+ if (count <= clock_freq >> i)
+ break;
+ }
+
+ return val;
+}
+
+static int bbc_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent);
+ struct bbc_beep_info *info = &state->u.bbc;
+ unsigned int count = 0;
+ unsigned long flags;
+
+ if (type != EV_SND)
+ return -1;
+
+ switch (code) {
+ case SND_BELL: if (value) value = 1000;
+ case SND_TONE: break;
+ default: return -1;
+ }
+
+ if (value > 20 && value < 32767)
+ count = 1193182 / value;
+
+ count = bbc_count_to_reg(info, count);
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ if (count) {
+ outb(0x01, info->regs + 0);
+ outb(0x00, info->regs + 2);
+ outb((count >> 16) & 0xff, info->regs + 3);
+ outb((count >> 8) & 0xff, info->regs + 4);
+ outb(0x00, info->regs + 5);
+ } else {
+ outb(0x00, info->regs + 0);
+ }
+
+ spin_unlock_irqrestore(&state->lock, flags);
+
+ return 0;
+}
+
+static int grover_spkr_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct sparcspkr_state *state = dev_get_drvdata(dev->dev.parent);
+ struct grover_beep_info *info = &state->u.grover;
+ unsigned int count = 0;
+ unsigned long flags;
+
+ if (type != EV_SND)
+ return -1;
+
+ switch (code) {
+ case SND_BELL: if (value) value = 1000;
+ case SND_TONE: break;
+ default: return -1;
+ }
+
+ if (value > 20 && value < 32767)
+ count = 1193182 / value;
+
+ spin_lock_irqsave(&state->lock, flags);
+
+ if (count) {
+ /* enable counter 2 */
+ outb(inb(info->enable_reg) | 3, info->enable_reg);
+ /* set command for counter 2, 2 byte write */
+ outb(0xB6, info->freq_regs + 1);
+ /* select desired HZ */
+ outb(count & 0xff, info->freq_regs + 0);
+ outb((count >> 8) & 0xff, info->freq_regs + 0);
+ } else {
+ /* disable counter 2 */
+ outb(inb_p(info->enable_reg) & 0xFC, info->enable_reg);
+ }
+
+ spin_unlock_irqrestore(&state->lock, flags);
+
+ return 0;
+}
+
+static int sparcspkr_probe(struct device *dev)
+{
+ struct sparcspkr_state *state = dev_get_drvdata(dev);
+ struct input_dev *input_dev;
+ int error;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+ return -ENOMEM;
+
+ input_dev->name = state->name;
+ input_dev->phys = "sparc/input0";
+ input_dev->id.bustype = BUS_ISA;
+ input_dev->id.vendor = 0x001f;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_SND);
+ input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE);
+
+ input_dev->event = state->event;
+
+ error = input_register_device(input_dev);
+ if (error) {
+ input_free_device(input_dev);
+ return error;
+ }
+
+ state->input_dev = input_dev;
+
+ return 0;
+}
+
+static void sparcspkr_shutdown(struct platform_device *dev)
+{
+ struct sparcspkr_state *state = dev_get_drvdata(&dev->dev);
+ struct input_dev *input_dev = state->input_dev;
+
+ /* turn off the speaker */
+ state->event(input_dev, EV_SND, SND_BELL, 0);
+}
+
+static int bbc_beep_probe(struct platform_device *op)
+{
+ struct sparcspkr_state *state;
+ struct bbc_beep_info *info;
+ struct device_node *dp;
+ int err = -ENOMEM;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ goto out_err;
+
+ state->name = "Sparc BBC Speaker";
+ state->event = bbc_spkr_event;
+ spin_lock_init(&state->lock);
+
+ dp = of_find_node_by_path("/");
+ err = -ENODEV;
+ if (!dp)
+ goto out_free;
+
+ info = &state->u.bbc;
+ info->clock_freq = of_getintprop_default(dp, "clock-frequency", 0);
+ if (!info->clock_freq)
+ goto out_free;
+
+ info->regs = of_ioremap(&op->resource[0], 0, 6, "bbc beep");
+ if (!info->regs)
+ goto out_free;
+
+ dev_set_drvdata(&op->dev, state);
+
+ err = sparcspkr_probe(&op->dev);
+ if (err)
+ goto out_clear_drvdata;
+
+ return 0;
+
+out_clear_drvdata:
+ dev_set_drvdata(&op->dev, NULL);
+ of_iounmap(&op->resource[0], info->regs, 6);
+
+out_free:
+ kfree(state);
+out_err:
+ return err;
+}
+
+static int bbc_remove(struct platform_device *op)
+{
+ struct sparcspkr_state *state = dev_get_drvdata(&op->dev);
+ struct input_dev *input_dev = state->input_dev;
+ struct bbc_beep_info *info = &state->u.bbc;
+
+ /* turn off the speaker */
+ state->event(input_dev, EV_SND, SND_BELL, 0);
+
+ input_unregister_device(input_dev);
+
+ of_iounmap(&op->resource[0], info->regs, 6);
+
+ dev_set_drvdata(&op->dev, NULL);
+ kfree(state);
+
+ return 0;
+}
+
+static const struct of_device_id bbc_beep_match[] = {
+ {
+ .name = "beep",
+ .compatible = "SUNW,bbc-beep",
+ },
+ {},
+};
+
+static struct platform_driver bbc_beep_driver = {
+ .driver = {
+ .name = "bbcbeep",
+ .owner = THIS_MODULE,
+ .of_match_table = bbc_beep_match,
+ },
+ .probe = bbc_beep_probe,
+ .remove = bbc_remove,
+ .shutdown = sparcspkr_shutdown,
+};
+
+static int grover_beep_probe(struct platform_device *op)
+{
+ struct sparcspkr_state *state;
+ struct grover_beep_info *info;
+ int err = -ENOMEM;
+
+ state = kzalloc(sizeof(*state), GFP_KERNEL);
+ if (!state)
+ goto out_err;
+
+ state->name = "Sparc Grover Speaker";
+ state->event = grover_spkr_event;
+ spin_lock_init(&state->lock);
+
+ info = &state->u.grover;
+ info->freq_regs = of_ioremap(&op->resource[2], 0, 2, "grover beep freq");
+ if (!info->freq_regs)
+ goto out_free;
+
+ info->enable_reg = of_ioremap(&op->resource[3], 0, 1, "grover beep enable");
+ if (!info->enable_reg)
+ goto out_unmap_freq_regs;
+
+ dev_set_drvdata(&op->dev, state);
+
+ err = sparcspkr_probe(&op->dev);
+ if (err)
+ goto out_clear_drvdata;
+
+ return 0;
+
+out_clear_drvdata:
+ dev_set_drvdata(&op->dev, NULL);
+ of_iounmap(&op->resource[3], info->enable_reg, 1);
+
+out_unmap_freq_regs:
+ of_iounmap(&op->resource[2], info->freq_regs, 2);
+out_free:
+ kfree(state);
+out_err:
+ return err;
+}
+
+static int grover_remove(struct platform_device *op)
+{
+ struct sparcspkr_state *state = dev_get_drvdata(&op->dev);
+ struct grover_beep_info *info = &state->u.grover;
+ struct input_dev *input_dev = state->input_dev;
+
+ /* turn off the speaker */
+ state->event(input_dev, EV_SND, SND_BELL, 0);
+
+ input_unregister_device(input_dev);
+
+ of_iounmap(&op->resource[3], info->enable_reg, 1);
+ of_iounmap(&op->resource[2], info->freq_regs, 2);
+
+ dev_set_drvdata(&op->dev, NULL);
+ kfree(state);
+
+ return 0;
+}
+
+static const struct of_device_id grover_beep_match[] = {
+ {
+ .name = "beep",
+ .compatible = "SUNW,smbus-beep",
+ },
+ {},
+};
+
+static struct platform_driver grover_beep_driver = {
+ .driver = {
+ .name = "groverbeep",
+ .owner = THIS_MODULE,
+ .of_match_table = grover_beep_match,
+ },
+ .probe = grover_beep_probe,
+ .remove = grover_remove,
+ .shutdown = sparcspkr_shutdown,
+};
+
+static int __init sparcspkr_init(void)
+{
+ int err = platform_driver_register(&bbc_beep_driver);
+
+ if (!err) {
+ err = platform_driver_register(&grover_beep_driver);
+ if (err)
+ platform_driver_unregister(&bbc_beep_driver);
+ }
+
+ return err;
+}
+
+static void __exit sparcspkr_exit(void)
+{
+ platform_driver_unregister(&bbc_beep_driver);
+ platform_driver_unregister(&grover_beep_driver);
+}
+
+module_init(sparcspkr_init);
+module_exit(sparcspkr_exit);
diff --git a/drivers/input/misc/stk3x1x.c b/drivers/input/misc/stk3x1x.c
new file mode 100644
index 00000000..a6ed55fb
--- /dev/null
+++ b/drivers/input/misc/stk3x1x.c
@@ -0,0 +1,4262 @@
+/*
+ * stk3x1x.c - Linux kernel modules for sensortek stk301x, stk321x, stk331x
+ * , and stk3410 proximity/ambient light sensor
+ *
+ * Copyright (C) 2012~2015 Lex Hsieh / sensortek <lex_hsieh@sensortek.com.tw>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/input.h>
+#include <linux/gpio.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#include <linux/fs.h>
+#include <asm/uaccess.h>
+//#include <linux/i2c/stk3x1x.h>
+#include <linux/miscdevice.h>
+
+#include <linux/wakelock.h>
+
+#define DRIVER_VERSION "3.10.1 20151217"
+
+/* Driver Settings */
+#define CONFIG_STK_PS_ALS_USE_CHANGE_THRESHOLD
+#ifdef CONFIG_STK_PS_ALS_USE_CHANGE_THRESHOLD
+#define STK_ALS_CHANGE_THD 5 //10 /* The threshold to trigger ALS interrupt, unit: lux */
+#endif /* #ifdef CONFIG_STK_PS_ALS_USE_CHANGE_THRESHOLD */
+#define STK_INT_PS_MODE 1 /* 1, 2, or 3 */
+//#define STK_POLL_PS
+#define STK_POLL_ALS /* ALS interrupt is valid only when STK_INT_PS_MODE = 1 or 4*/
+#define STK_TUNE0
+#define CALI_PS_EVERY_TIME
+//#define STK_DEBUG_PRINTF
+#define SPREADTRUM_PLATFORM
+#define STK_ALS_FIR
+//#define STK_IRS
+//#define STK_CHK_REG
+//#define STK_GES
+//#define STK_QUALCOMM_POWER_CTRL
+//#define QUALCOMM_PLATFORM
+
+#if 0
+#ifdef SPREADTRUM_PLATFORM
+#include <linux/i2c/stk3x1x.h>
+#else
+ #include "stk3x1x.h"
+#endif
+#endif
+#include "stk3x1x.h"
+
+/* Define Register Map */
+#define STK_STATE_REG 0x00
+#define STK_PSCTRL_REG 0x01
+#define STK_ALSCTRL_REG 0x02
+#define STK_LEDCTRL_REG 0x03
+#define STK_INT_REG 0x04
+#define STK_WAIT_REG 0x05
+#define STK_THDH1_PS_REG 0x06
+#define STK_THDH2_PS_REG 0x07
+#define STK_THDL1_PS_REG 0x08
+#define STK_THDL2_PS_REG 0x09
+#define STK_THDH1_ALS_REG 0x0A
+#define STK_THDH2_ALS_REG 0x0B
+#define STK_THDL1_ALS_REG 0x0C
+#define STK_THDL2_ALS_REG 0x0D
+#define STK_FLAG_REG 0x10
+#define STK_DATA1_PS_REG 0x11
+#define STK_DATA2_PS_REG 0x12
+#define STK_DATA1_ALS_REG 0x13
+#define STK_DATA2_ALS_REG 0x14
+#define STK_DATA1_OFFSET_REG 0x15
+#define STK_DATA2_OFFSET_REG 0x16
+#define STK_DATA1_IR_REG 0x17
+#define STK_DATA2_IR_REG 0x18
+#define STK_PDT_ID_REG 0x3E
+#define STK_RSRVD_REG 0x3F
+#define STK_SW_RESET_REG 0x80
+
+#define STK_GSCTRL_REG 0x1A
+#define STK_FLAG2_REG 0x1C
+
+/* Define state reg */
+#define STK_STATE_EN_IRS_SHIFT 7
+#define STK_STATE_EN_AK_SHIFT 6
+#define STK_STATE_EN_ASO_SHIFT 5
+#define STK_STATE_EN_IRO_SHIFT 4
+#define STK_STATE_EN_WAIT_SHIFT 2
+#define STK_STATE_EN_ALS_SHIFT 1
+#define STK_STATE_EN_PS_SHIFT 0
+
+#define STK_STATE_EN_IRS_MASK 0x80
+#define STK_STATE_EN_AK_MASK 0x40
+#define STK_STATE_EN_ASO_MASK 0x20
+#define STK_STATE_EN_IRO_MASK 0x10
+#define STK_STATE_EN_WAIT_MASK 0x04
+#define STK_STATE_EN_ALS_MASK 0x02
+#define STK_STATE_EN_PS_MASK 0x01
+
+/* Define PS ctrl reg */
+#define STK_PS_PRS_SHIFT 6
+#define STK_PS_GAIN_SHIFT 4
+#define STK_PS_IT_SHIFT 0
+
+#define STK_PS_PRS_MASK 0xC0
+#define STK_PS_GAIN_MASK 0x30
+#define STK_PS_IT_MASK 0x0F
+
+/* Define ALS ctrl reg */
+#define STK_ALS_PRS_SHIFT 6
+#define STK_ALS_GAIN_SHIFT 4
+#define STK_ALS_IT_SHIFT 0
+
+#define STK_ALS_PRS_MASK 0xC0
+#define STK_ALS_GAIN_MASK 0x30
+#define STK_ALS_IT_MASK 0x0F
+
+/* Define LED ctrl reg */
+#define STK_LED_IRDR_SHIFT 6
+#define STK_LED_DT_SHIFT 0
+
+#define STK_LED_IRDR_MASK 0xC0
+#define STK_LED_DT_MASK 0x3F
+
+/* Define interrupt reg */
+#define STK_INT_CTRL_SHIFT 7
+#define STK_INT_OUI_SHIFT 4
+#define STK_INT_ALS_SHIFT 3
+#define STK_INT_PS_SHIFT 0
+
+#define STK_INT_CTRL_MASK 0x80
+#define STK_INT_OUI_MASK 0x10
+#define STK_INT_ALS_MASK 0x08
+#define STK_INT_PS_MASK 0x07
+
+#define STK_INT_ALS 0x08
+
+/* Define flag reg */
+#define STK_FLG_ALSDR_SHIFT 7
+#define STK_FLG_PSDR_SHIFT 6
+#define STK_FLG_ALSINT_SHIFT 5
+#define STK_FLG_PSINT_SHIFT 4
+#define STK_FLG_OUI_SHIFT 2
+#define STK_FLG_IR_RDY_SHIFT 1
+#define STK_FLG_NF_SHIFT 0
+
+#define STK_FLG_ALSDR_MASK 0x80
+#define STK_FLG_PSDR_MASK 0x40
+#define STK_FLG_ALSINT_MASK 0x20
+#define STK_FLG_PSINT_MASK 0x10
+#define STK_FLG_OUI_MASK 0x04
+#define STK_FLG_IR_RDY_MASK 0x02
+#define STK_FLG_NF_MASK 0x01
+
+/* Define flag2 reg */
+#define STK_FLG2_INT_GS_SHIFT 6
+#define STK_FLG2_GS10_SHIFT 5
+#define STK_FLG2_GS01_SHIFT 4
+
+#define STK_FLG2_INT_GS_MASK 0x40
+#define STK_FLG2_GS10_MASK 0x20
+#define STK_FLG2_GS01_MASK 0x10
+
+
+/* misc define */
+#define MIN_ALS_POLL_DELAY_NS 60000000
+
+
+#ifdef STK_TUNE0
+ #define STK_MAX_MIN_DIFF 150 //200
+ #define STK_LT_N_CT 50 //100
+ #define STK_HT_N_CT 80 //150
+#endif /* #ifdef STK_TUNE0 */
+
+#define STK_IRC_MAX_ALS_CODE 20000
+#define STK_IRC_MIN_ALS_CODE 25
+#define STK_IRC_MIN_IR_CODE 50
+#define STK_IRC_ALS_DENOMI 2
+#define STK_IRC_ALS_NUMERA 5
+#define STK_IRC_ALS_CORREC 850
+
+#define STK_IRS_IT_REDUCE 2
+#define STK_ALS_READ_IRS_IT_REDUCE 5
+#define STK_ALS_THRESHOLD 30
+
+#define DEVICE_NAME "stk_ps"
+#define ALS_NAME "lightsensor-level"
+#define PS_NAME "proximity"
+
+#ifdef STK_QUALCOMM_POWER_CTRL
+ /* POWER SUPPLY VOLTAGE RANGE */
+ #define STK3X1X_VDD_MIN_UV 2000000
+ #define STK3X1X_VDD_MAX_UV 3300000
+ #define STK3X1X_VIO_MIN_UV 1750000
+ #define STK3X1X_VIO_MAX_UV 1950000
+#endif
+
+#define STK3310SA_PID 0x17
+#define STK3311SA_PID 0x1E
+#define STK3311WV_PID 0x1D
+
+#ifdef SPREADTRUM_PLATFORM
+extern int sprd_3rdparty_gpio_pls_irq;
+
+static struct stk3x1x_platform_data stk3x1x_pfdata={
+ .state_reg = 0x0, /* disable all */
+ .psctrl_reg = 0x31, /* ps_persistance=1, ps_gain=64X, PS_IT=0.391ms */
+ .alsctrl_reg = 0x39, /* als_persistance=1, als_gain=64X, ALS_IT=100ms */
+ .ledctrl_reg = 0xFF, /* 100mA IRDR, 64/64 LED duty */
+ .wait_reg = 0xF, /* 100 ms */
+ .ps_thd_h =1700,
+ .ps_thd_l = 1500,
+ .int_pin = 216,//GPIO_PROX_INT//sprd_3rdparty_gpio_pls_irq
+ .transmittance = 3100, //500
+};
+#endif
+
+#ifdef STK_ALS_FIR
+ #define STK_FIR_LEN 4 //6 //8
+ #define MAX_FIR_LEN 32
+struct data_filter {
+ u16 raw[MAX_FIR_LEN];
+ int sum;
+ int number;
+ int idx;
+};
+#endif
+
+#ifdef STK_GES
+union stk_ges_operation{
+ uint8_t ops[4];
+ struct {
+ uint8_t rw_len_retry;
+ uint8_t reg;
+ uint8_t reg_value_retry_crit;
+ uint8_t sleep_10ns;
+ }action;
+};
+
+union stk_ges_operation stk_ges_op[10] =
+{
+ {.ops={0xc1, 0x24, 0, 0}},
+ {.ops={0, 0, 0, 0}},
+ {.ops={0, 0, 0, 0}},
+ {.ops={0, 0, 0, 0}},
+ {.ops={0, 0, 0, 0}},
+ {.ops={0, 0, 0, 0}},
+ {.ops={0, 0, 0, 0}},
+ {.ops={0, 0, 0, 0}},
+ {.ops={0, 0, 0, 0}},
+ {.ops={0, 0, 0, 0}}
+};
+#endif
+
+struct stk3x1x_data {
+ struct i2c_client *client;
+ struct stk3x1x_platform_data *pdata;
+#if (!defined(STK_POLL_PS) || !defined(STK_POLL_ALS))
+ int32_t irq;
+ struct work_struct stk_work;
+ struct workqueue_struct *stk_wq;
+#endif
+ uint16_t ir_code;
+ uint16_t als_correct_factor;
+ uint8_t alsctrl_reg;
+ uint8_t psctrl_reg;
+ uint8_t ledctrl_reg;
+ uint8_t state_reg;
+ int int_pin;
+ uint8_t wait_reg;
+ uint8_t int_reg;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ //struct early_suspend stk_early_suspend;
+#endif
+ uint16_t ps_thd_h;
+ uint16_t ps_thd_l;
+#ifdef CALI_PS_EVERY_TIME
+ uint16_t ps_high_thd_boot;
+ uint16_t ps_low_thd_boot;
+#endif
+ struct mutex io_lock;
+ struct input_dev *ps_input_dev;
+ int32_t ps_distance_last;
+ bool ps_enabled;
+ bool re_enable_ps;
+ struct wake_lock ps_wakelock;
+#ifdef STK_POLL_PS
+ struct hrtimer ps_timer;
+ struct work_struct stk_ps_work;
+ struct workqueue_struct *stk_ps_wq;
+ struct wake_lock ps_nosuspend_wl;
+#endif
+ struct input_dev *als_input_dev;
+ int32_t als_lux_last;
+ uint32_t als_transmittance;
+ bool als_enabled;
+ bool re_enable_als;
+ ktime_t ps_poll_delay;
+ ktime_t als_poll_delay;
+#ifdef STK_POLL_ALS
+ struct work_struct stk_als_work;
+ struct hrtimer als_timer;
+ struct workqueue_struct *stk_als_wq;
+#endif
+ bool first_boot;
+#ifdef STK_TUNE0
+ uint16_t psa;
+ uint16_t psi;
+ uint16_t psi_set;
+ struct hrtimer ps_tune0_timer;
+ struct workqueue_struct *stk_ps_tune0_wq;
+ struct work_struct stk_ps_tune0_work;
+ ktime_t ps_tune0_delay;
+ bool tune_zero_init_proc;
+ uint32_t ps_stat_data[3];
+ int data_count;
+ int stk_max_min_diff;
+ int stk_lt_n_ct;
+ int stk_ht_n_ct;
+#endif
+#ifdef STK_ALS_FIR
+ struct data_filter fir;
+ atomic_t firlength;
+#endif
+ atomic_t recv_reg;
+
+#ifdef STK_GES
+ struct input_dev *ges_input_dev;
+ int ges_enabled;
+ int re_enable_ges;
+ atomic_t gesture2;
+#endif
+#ifdef STK_IRS
+ int als_data_index;
+#endif
+#ifdef STK_QUALCOMM_POWER_CTRL
+ struct regulator *vdd;
+ struct regulator *vio;
+ bool power_enabled;
+#endif
+ uint8_t pid;
+ uint8_t p_wv_r_bd_with_co;
+ uint32_t als_code_last;
+};
+
+#if( !defined(CONFIG_STK_PS_ALS_USE_CHANGE_THRESHOLD))
+static uint32_t lux_threshold_table[] =
+{
+ 3,
+ 10,
+ 40,
+ 65,
+ 145,
+ 300,
+ 550,
+ 930,
+ 1250,
+ 1700,
+};
+
+#define LUX_THD_TABLE_SIZE (sizeof(lux_threshold_table)/sizeof(uint32_t)+1)
+static uint16_t code_threshold_table[LUX_THD_TABLE_SIZE+1];
+#endif
+
+static int32_t stk3x1x_enable_ps(struct stk3x1x_data *ps_data, uint8_t enable, uint8_t validate_reg);
+static int32_t stk3x1x_enable_als(struct stk3x1x_data *ps_data, uint8_t enable);
+static int32_t stk3x1x_set_ps_thd_l(struct stk3x1x_data *ps_data, uint16_t thd_l);
+static int32_t stk3x1x_set_ps_thd_h(struct stk3x1x_data *ps_data, uint16_t thd_h);
+static int32_t stk3x1x_set_als_thd_l(struct stk3x1x_data *ps_data, uint16_t thd_l);
+static int32_t stk3x1x_set_als_thd_h(struct stk3x1x_data *ps_data, uint16_t thd_h);
+static int32_t stk3x1x_get_ir_reading(struct stk3x1x_data *ps_data, int32_t als_it_reduce);
+#ifdef STK_TUNE0
+static int stk_ps_tune_zero_func_fae(struct stk3x1x_data *ps_data);
+#endif
+#ifdef STK_CHK_REG
+static int stk3x1x_validate_n_handle(struct i2c_client *client);
+#endif
+static int stk_ps_val(struct stk3x1x_data *ps_data);
+#ifdef STK_QUALCOMM_POWER_CTRL
+static int stk3x1x_device_ctl(struct stk3x1x_data *ps_data, bool enable);
+#endif
+
+static int stk3x1x_i2c_read_data(struct i2c_client *client, unsigned char command, int length, unsigned char *values)
+{
+ uint8_t retry;
+ int err;
+ struct i2c_msg msgs[] =
+ {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .len = 1,
+ .buf = &command,
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = length,
+ .buf = values,
+ },
+ };
+
+ for (retry = 0; retry < 5; retry++)
+ {
+ err = i2c_transfer(client->adapter, msgs, 2);
+ if (err == 2)
+ break;
+ else
+ mdelay(5);
+ }
+ //printk("stk3x1x_i2c_read_data");
+
+ if (retry >= 5)
+ {
+ printk(KERN_ERR "%s: i2c read fail, err=%d\n", __func__, err);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int stk3x1x_i2c_write_data(struct i2c_client *client, unsigned char command, int length, unsigned char *values)
+{
+ int retry;
+ int err;
+ unsigned char data[11];
+ struct i2c_msg msg;
+ int index;
+ //printk("stk3x1x_i2c_write_data");
+
+ if (!client)
+ return -EINVAL;
+ else if (length >= 10)
+ {
+ printk(KERN_ERR "%s:length %d exceeds 10\n", __func__, length);
+ return -EINVAL;
+ }
+
+ data[0] = command;
+ for (index=1;index<=length;index++)
+ data[index] = values[index-1];
+
+ msg.addr = client->addr;
+ msg.flags = 0;
+ msg.len = length+1;
+ msg.buf = data;
+
+ for (retry = 0; retry < 5; retry++)
+ {
+ err = i2c_transfer(client->adapter, &msg, 1);
+ if (err == 1)
+ break;
+ else
+ mdelay(5);
+ }
+
+ if (retry >= 5)
+ {
+ printk(KERN_ERR "%s: i2c write fail, err=%d\n", __func__, err);
+ return -EIO;
+ }
+ return 0;
+}
+
+static int stk3x1x_i2c_smbus_read_byte_data(struct i2c_client *client, unsigned char command)
+{
+ unsigned char value;
+ int err;
+ err = stk3x1x_i2c_read_data(client, command, 1, &value);
+ if(err < 0)
+ return err;
+ return value;
+}
+
+static int stk3x1x_i2c_smbus_write_byte_data(struct i2c_client *client, unsigned char command, unsigned char value)
+{
+ int err;
+ err = stk3x1x_i2c_write_data(client, command, 1, &value);
+ return err;
+}
+
+uint32_t stk_alscode2lux(struct stk3x1x_data *ps_data, uint32_t alscode)
+{
+ alscode += ((alscode<<7)+(alscode<<3)+(alscode>>1));
+ alscode<<=3;
+ alscode/=ps_data->als_transmittance;
+ return alscode;
+}
+
+uint32_t stk_lux2alscode(struct stk3x1x_data *ps_data, uint32_t lux)
+{
+ lux*=ps_data->als_transmittance;
+ lux/=1100;
+ if (unlikely(lux>=(1<<16)))
+ lux = (1<<16) -1;
+ return lux;
+}
+
+#ifndef CONFIG_STK_PS_ALS_USE_CHANGE_THRESHOLD
+static void stk_init_code_threshold_table(struct stk3x1x_data *ps_data)
+{
+ uint32_t i,j;
+ uint32_t alscode;
+
+ code_threshold_table[0] = 0;
+#ifdef STK_DEBUG_PRINTF
+ printk(KERN_INFO "alscode[0]=%d\n",0);
+#endif
+ for (i=1,j=0;i<LUX_THD_TABLE_SIZE;i++,j++)
+ {
+ alscode = stk_lux2alscode(ps_data, lux_threshold_table[j]);
+ printk(KERN_INFO "alscode[%d]=%d\n",i,alscode);
+ code_threshold_table[i] = (uint16_t)(alscode);
+ }
+ code_threshold_table[i] = 0xffff;
+ printk(KERN_INFO "alscode[%d]=%d\n",i,alscode);
+}
+
+static uint32_t stk_get_lux_interval_index(uint16_t alscode)
+{
+ uint32_t i;
+ for (i=1;i<=LUX_THD_TABLE_SIZE;i++)
+ {
+ if ((alscode>=code_threshold_table[i-1])&&(alscode<code_threshold_table[i]))
+ {
+ return i;
+ }
+ }
+ return LUX_THD_TABLE_SIZE;
+}
+#else
+void stk_als_set_new_thd(struct stk3x1x_data *ps_data, uint16_t alscode)
+{
+ int32_t high_thd,low_thd;
+ high_thd = alscode + stk_lux2alscode(ps_data, STK_ALS_CHANGE_THD);
+ low_thd = alscode - stk_lux2alscode(ps_data, STK_ALS_CHANGE_THD);
+ if (high_thd >= (1<<16))
+ high_thd = (1<<16) -1;
+ if (low_thd <0)
+ low_thd = 0;
+ stk3x1x_set_als_thd_h(ps_data, (uint16_t)high_thd);
+ stk3x1x_set_als_thd_l(ps_data, (uint16_t)low_thd);
+}
+#endif // CONFIG_STK_PS_ALS_USE_CHANGE_THRESHOLD
+
+
+static void stk3x1x_proc_plat_data(struct stk3x1x_data *ps_data, struct stk3x1x_platform_data *plat_data)
+{
+ uint8_t w_reg;
+
+ ps_data->state_reg = plat_data->state_reg;
+ ps_data->psctrl_reg = plat_data->psctrl_reg;
+#ifdef STK_POLL_PS
+ ps_data->psctrl_reg &= 0x3F;
+#endif
+ ps_data->alsctrl_reg = plat_data->alsctrl_reg;
+ ps_data->ledctrl_reg = plat_data->ledctrl_reg;
+ if(ps_data->pid == STK3310SA_PID || ps_data->pid == STK3311SA_PID)
+ ps_data->ledctrl_reg &= 0x3F;
+
+ ps_data->wait_reg = plat_data->wait_reg;
+ if(ps_data->wait_reg < 2)
+ {
+ printk(KERN_WARNING "%s: wait_reg should be larger than 2, force to write 2\n", __func__);
+ ps_data->wait_reg = 2;
+ }
+ else if (ps_data->wait_reg > 0xFF)
+ {
+ printk(KERN_WARNING "%s: wait_reg should be less than 0xFF, force to write 0xFF\n", __func__);
+ ps_data->wait_reg = 0xFF;
+ }
+//#ifndef STK_TUNE0
+ if(ps_data->ps_thd_h == 0 && ps_data->ps_thd_l == 0)
+ {
+ ps_data->ps_thd_h = plat_data->ps_thd_h;
+ ps_data->ps_thd_l = plat_data->ps_thd_l;
+ }
+//#endif
+#ifdef CALI_PS_EVERY_TIME
+ ps_data->ps_high_thd_boot = plat_data->ps_thd_h;
+ ps_data->ps_low_thd_boot = plat_data->ps_thd_l;
+#endif
+ w_reg = 0;
+#ifndef STK_POLL_PS
+ w_reg |= STK_INT_PS_MODE;
+#else
+ w_reg |= 0x01;
+#endif
+
+#if (!defined(STK_POLL_ALS) && (STK_INT_PS_MODE != 0x02) && (STK_INT_PS_MODE != 0x03))
+ w_reg |= STK_INT_ALS;
+#endif
+ ps_data->int_reg = w_reg;
+ return;
+}
+
+static int32_t stk3x1x_init_all_reg(struct stk3x1x_data *ps_data)
+{
+ int32_t ret;
+
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_STATE_REG, ps_data->state_reg);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_PSCTRL_REG, ps_data->psctrl_reg);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_ALSCTRL_REG, ps_data->alsctrl_reg);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_LEDCTRL_REG, ps_data->ledctrl_reg);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_WAIT_REG, ps_data->wait_reg);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+#ifdef STK_TUNE0
+ ps_data->psa = 0x0;
+ ps_data->psi = 0xFFFF;
+#endif
+//#else
+ stk3x1x_set_ps_thd_h(ps_data, ps_data->ps_thd_h);
+ stk3x1x_set_ps_thd_l(ps_data, ps_data->ps_thd_l);
+//#endif
+
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_INT_REG, ps_data->int_reg);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+
+ /*
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, 0x87, 0x60);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+ */
+
+ return 0;
+}
+
+static int32_t stk3x1x_read_otp25(struct stk3x1x_data *ps_data)
+{
+ int32_t ret, otp25;
+
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, 0x0, 0x2);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, 0x90, 0x25);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, 0x92, 0x82);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+ usleep_range(1000, 5000);
+
+ ret = stk3x1x_i2c_smbus_read_byte_data(ps_data->client,0x91);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, ret);
+ return ret;
+ }
+ otp25 = ret;
+
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, 0x0, 0x0);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+ printk(KERN_INFO "%s: otp25=0x%x\n", __func__, otp25);
+ if(otp25 & 0x80)
+ return 1;
+ return 0;
+}
+
+static int32_t stk3x1x_check_pid(struct stk3x1x_data *ps_data)
+{
+ unsigned char value[2], pid_msb;
+ int err;
+
+ ps_data->p_wv_r_bd_with_co = 0;
+
+ err = stk3x1x_i2c_read_data(ps_data->client, STK_PDT_ID_REG, 2, &value[0]);
+ if(err < 0)
+ {
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, err);
+ return err;
+ }
+
+ printk(KERN_INFO "%s: PID=0x%x, RID=0x%x\n", __func__, value[0], value[1]);
+ ps_data->pid = value[0];
+
+ if(value[0] == STK3311WV_PID)
+ ps_data->p_wv_r_bd_with_co |= 0b100;
+ if(value[1] == 0xC3)
+ ps_data->p_wv_r_bd_with_co |= 0b010;
+
+ if(stk3x1x_read_otp25(ps_data) == 1)
+ {
+ ps_data->p_wv_r_bd_with_co |= 0b001;
+ }
+ printk(KERN_INFO "%s: p_wv_r_bd_with_co = 0x%x\n", __func__, ps_data->p_wv_r_bd_with_co);
+
+ if(value[0] == 0)
+ {
+ printk(KERN_ERR "PID=0x0, please make sure the chip is stk3x1x!\n");
+ return -2;
+ }
+
+ pid_msb = value[0] & 0xF0;
+ switch(pid_msb)
+ {
+ case 0x10:
+ case 0x20:
+ case 0x30:
+ return 0;
+ default:
+ printk(KERN_ERR "%s: invalid PID(%#x)\n", __func__, value[0]);
+ return -1;
+ }
+ return 0;
+}
+
+
+static int32_t stk3x1x_software_reset(struct stk3x1x_data *ps_data)
+{
+ int32_t r;
+ uint8_t w_reg;
+
+ w_reg = 0x7F;
+ r = stk3x1x_i2c_smbus_write_byte_data(ps_data->client,STK_WAIT_REG,w_reg);
+ if (r<0)
+ {
+ printk(KERN_ERR "%s: software reset: write i2c error, ret=%d\n", __func__, r);
+ return r;
+ }
+ r = stk3x1x_i2c_smbus_read_byte_data(ps_data->client,STK_WAIT_REG);
+ if (w_reg != r)
+ {
+ printk(KERN_ERR "%s: software reset: read-back value is not the same\n", __func__);
+ return -1;
+ }
+
+ r = stk3x1x_i2c_smbus_write_byte_data(ps_data->client,STK_SW_RESET_REG,0);
+ if (r<0)
+ {
+ printk(KERN_ERR "%s: software reset: read error after reset\n", __func__);
+ return r;
+ }
+ usleep_range(13000, 15000);
+ return 0;
+}
+
+
+static int32_t stk3x1x_set_als_thd_l(struct stk3x1x_data *ps_data, uint16_t thd_l)
+{
+ unsigned char val[2];
+ int ret;
+ val[0] = (thd_l & 0xFF00) >> 8;
+ val[1] = thd_l & 0x00FF;
+ ret = stk3x1x_i2c_write_data(ps_data->client, STK_THDL1_ALS_REG, 2, val);
+ if(ret < 0)
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, ret);
+ return ret;
+}
+static int32_t stk3x1x_set_als_thd_h(struct stk3x1x_data *ps_data, uint16_t thd_h)
+{
+ unsigned char val[2];
+ int ret;
+ val[0] = (thd_h & 0xFF00) >> 8;
+ val[1] = thd_h & 0x00FF;
+ ret = stk3x1x_i2c_write_data(ps_data->client, STK_THDH1_ALS_REG, 2, val);
+ if(ret < 0)
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static int32_t stk3x1x_set_ps_thd_l(struct stk3x1x_data *ps_data, uint16_t thd_l)
+{
+ unsigned char val[2];
+ int ret;
+ val[0] = (thd_l & 0xFF00) >> 8;
+ val[1] = thd_l & 0x00FF;
+ ret = stk3x1x_i2c_write_data(ps_data->client, STK_THDL1_PS_REG, 2, val);
+ if(ret < 0)
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, ret);
+ return ret;
+}
+static int32_t stk3x1x_set_ps_thd_h(struct stk3x1x_data *ps_data, uint16_t thd_h)
+{
+ unsigned char val[2];
+ int ret;
+ val[0] = (thd_h & 0xFF00) >> 8;
+ val[1] = thd_h & 0x00FF;
+ ret = stk3x1x_i2c_write_data(ps_data->client, STK_THDH1_PS_REG, 2, val);
+ if(ret < 0)
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static uint32_t stk3x1x_get_ps_reading(struct stk3x1x_data *ps_data)
+{
+ unsigned char value[2];
+ int err;
+ err = stk3x1x_i2c_read_data(ps_data->client, STK_DATA1_PS_REG, 2, &value[0]);
+ if(err < 0)
+ {
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, err);
+ return err;
+ }
+ return ((value[0]<<8) | value[1]);
+}
+
+
+static int32_t stk3x1x_set_flag(struct stk3x1x_data *ps_data, uint8_t org_flag_reg, uint8_t clr)
+{
+ uint8_t w_flag;
+ int ret;
+
+ w_flag = org_flag_reg | (STK_FLG_ALSINT_MASK | STK_FLG_PSINT_MASK | STK_FLG_OUI_MASK | STK_FLG_IR_RDY_MASK);
+ w_flag &= (~clr);
+ //printk(KERN_INFO "%s: org_flag_reg=0x%x, w_flag = 0x%x\n", __func__, org_flag_reg, w_flag);
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client,STK_FLAG_REG, w_flag);
+ if(ret < 0)
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static int32_t stk3x1x_get_flag(struct stk3x1x_data *ps_data)
+{
+ int ret;
+ ret = stk3x1x_i2c_smbus_read_byte_data(ps_data->client,STK_FLAG_REG);
+ if(ret < 0)
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+#ifdef STK_GES
+static int32_t stk3x1x_set_flag2(struct stk3x1x_data *ps_data, uint8_t org_flag2_reg, uint8_t clr)
+{
+ uint8_t w_flag2;
+ int ret;
+
+ w_flag2 = org_flag2_reg | 0x72;
+ w_flag2 &= (~clr);
+ //printk(KERN_INFO "%s: org_flag2_reg=0x%x, w_flag2 = 0x%x\n", __func__, org_flag2_reg, w_flag2);
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client,STK_FLAG2_REG, w_flag2);
+ if(ret < 0)
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static int32_t stk3x1x_get_flag2(struct stk3x1x_data *ps_data)
+{
+ int ret;
+ ret = stk3x1x_i2c_smbus_read_byte_data(ps_data->client,STK_FLAG2_REG);
+ if(ret < 0)
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, ret);
+ return ret;
+}
+#endif
+
+static int32_t stk3x1x_set_state(struct stk3x1x_data *ps_data, uint8_t state)
+{
+ int ret;
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client,STK_STATE_REG, state);
+ if(ret < 0)
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static int32_t stk3x1x_get_state(struct stk3x1x_data *ps_data)
+{
+ int ret;
+ ret = stk3x1x_i2c_smbus_read_byte_data(ps_data->client,STK_STATE_REG);
+ if(ret < 0)
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+#ifdef STK_GES
+static int32_t stk3x1x_set_gsctrl(struct stk3x1x_data *ps_data, uint8_t state)
+{
+ int ret;
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client,STK_GSCTRL_REG, state);
+ if(ret < 0)
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+static int32_t stk3x1x_get_gsctrl(struct stk3x1x_data *ps_data)
+{
+ int ret;
+ ret = stk3x1x_i2c_smbus_read_byte_data(ps_data->client,STK_GSCTRL_REG);
+ if(ret < 0)
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, ret);
+ return ret;
+}
+
+#if 0
+static uint32_t stk3x1x_get_ges_reading(struct stk3x1x_data *ps_data, unsigned int *ges0,
+ unsigned int *ges1, unsigned int *ges2)
+{
+ int retry, len, no = 0;
+ uint8_t reg_val_crit;
+ int err;
+ unsigned char value[10];
+
+ while(stk_ges_op[no].ops[1] != 0)
+ {
+ retry = stk_ges_op[no].action.rw_len_retry & 0x0F;
+ len = (stk_ges_op[no].action.rw_len_retry & 0x70) >> 4;
+ reg_val_crit = stk_ges_op[no].action.reg_value_retry_crit;
+ if(stk_ges_op[no].action.rw_len_retry & 0x80)
+ {
+ while(retry != 0)
+ {
+ err = stk3x1x_i2c_read_data(ps_data->client,
+ stk_ges_op[no].action.reg, len, value);
+ if(err < 0)
+ {
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, err);
+ return err;
+ }
+ if(reg_val_crit)
+ {
+ if(value[0] & reg_val_crit)
+ break;
+ }
+ if(stk_ges_op[no].action.sleep_10ns != 0)
+ usleep_range(stk_ges_op[no].action.sleep_10ns*10,
+ stk_ges_op[no].action.sleep_10ns*10+300);
+ retry--;
+ }
+ }
+ else
+ {
+ while(retry != 0)
+ {
+ err = stk3x1x_i2c_write_data(ps_data->client, stk_ges_op[no].action.reg,
+ len, &reg_val_crit);
+ if(err < 0)
+ {
+ printk(KERN_ERR "%s: fail, err=%d\n", __func__, err);
+ return err;
+ }
+
+ if(stk_ges_op[no].action.sleep_10ns != 0)
+ usleep_range(stk_ges_op[no].action.sleep_10ns*10,
+ stk_ges_op[no].action.sleep_10ns*10+300);
+ retry--;
+ }
+ }
+
+ if(stk_ges_op[no].action.reg == 0x24)
+ {
+ *ges0 = (value[0]<<8) | value[1];
+ *ges1 = (value[2]<<8) | value[3];
+ }
+ else if(stk_ges_op[no].action.reg == stk_ges_op[9].ops[0])
+ {
+ *ges2 = (value[0]<<8) | value[1];
+ }
+
+ no++;
+ }
+ return 0;
+}
+
+#else
+static uint32_t stk3x1x_get_ges_reading(struct stk3x1x_data *ps_data, unsigned int *ges0,unsigned int *ges1,unsigned int *ges2)
+{
+ unsigned char value[4];
+ int err, retry = 10;
+
+ do {
+ err = stk3x1x_get_flag(ps_data);
+ if(err < 0)
+ return err;
+ if(err & STK_FLG_PSDR_MASK)
+ break;
+ //printk(KERN_INFO "ges: not ready, %d\n", retry);
+ retry--;
+ usleep_range(350, 1000);
+ } while(retry > 0);
+ err = stk3x1x_i2c_read_data(ps_data->client, STK_DATA1_PS_REG, 2, &value[0]);
+
+ err = stk3x1x_i2c_read_data(ps_data->client, 0x24, 4, &value[0]);
+ if(err < 0)
+ {
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, err);
+ return err;
+ }
+ *ges0 = (value[0]<<8) | value[1];
+ *ges1 = (value[2]<<8) | value[3];
+ //printk(KERN_INFO "%s: ges=%d,%d\n",__func__, *ges0, *ges1);
+ return 0;
+}
+#endif
+
+static int32_t stk3x1x_enable_ges(struct stk3x1x_data *ps_data, uint8_t enable, uint8_t mode)
+{
+ int32_t ret;
+ uint8_t w_state_reg, gsctrl_reg;
+ uint8_t org_mode = 0;
+
+ if(ps_data->ps_enabled)
+ {
+ printk(KERN_INFO "%s: since PS is enabled, ges is disabled\n", __func__);
+ ps_data->re_enable_ges = enable;
+ return 0;
+ }
+
+ if(enable == ps_data->ges_enabled)
+ return 0;
+
+ if(enable)
+ {
+#ifdef STK_QUALCOMM_POWER_CTRL
+ ret = stk3x1x_device_ctl(ps_data, enable);
+ if (ret)
+ return ret;
+#endif
+ if(ps_data->als_enabled)
+ {
+ printk(KERN_INFO "%s: force disable ALS\n", __func__);
+ stk3x1x_enable_als(ps_data, 0);
+ ps_data->re_enable_als = true;
+ }
+
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_WAIT_REG, 0);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_INT_REG, 0);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+
+ w_state_reg = STK_STATE_EN_WAIT_MASK | STK_STATE_EN_PS_MASK;
+ ret = stk3x1x_set_state(ps_data, w_state_reg);
+ if(ret < 0)
+ return ret;
+ ps_data->state_reg = w_state_reg;
+
+ if(mode == 2)
+ {
+ ret = stk3x1x_get_gsctrl(ps_data);
+ if(ret < 0)
+ return ret;
+ gsctrl_reg = ret & 0xF3;
+#ifdef STK_POLL_PS
+ gsctrl_reg |= 0x04;
+#else
+ gsctrl_reg |= 0x0C;
+#endif
+ ret = stk3x1x_set_gsctrl(ps_data, gsctrl_reg);
+ if(ret < 0)
+ return ret;
+#ifdef STK_POLL_PS
+ hrtimer_start(&ps_data->ps_timer, ps_data->ps_poll_delay, HRTIMER_MODE_REL);
+#else
+ enable_irq(ps_data->irq);
+#endif
+ }
+ ps_data->ges_enabled = mode;
+ }
+ else
+ {
+ org_mode = ps_data->ges_enabled;
+ if(org_mode == 2)
+ {
+#ifdef STK_POLL_PS
+ hrtimer_cancel(&ps_data->ps_timer);
+ cancel_work_sync(&ps_data->stk_ps_work);
+#else
+ disable_irq(ps_data->irq);
+#endif
+ }
+
+ ret = stk3x1x_set_state(ps_data, 0);
+ if(ret < 0)
+ return ret;
+ ps_data->state_reg = 0;
+
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_WAIT_REG, ps_data->wait_reg);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_INT_REG, ps_data->int_reg);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+ if(org_mode == 2)
+ {
+ ret = stk3x1x_get_gsctrl(ps_data);
+ if(ret < 0)
+ return ret;
+ gsctrl_reg = ret & (~0x0C);
+ ret = stk3x1x_set_gsctrl(ps_data, gsctrl_reg);
+ if(ret < 0)
+ return ret;
+ }
+ ps_data->ges_enabled = 0;
+ if(ps_data->re_enable_als)
+ {
+ printk(KERN_INFO "%s: re-enable ALS\n", __func__);
+ stk3x1x_enable_als(ps_data, 1);
+ ps_data->re_enable_als = false;
+ }
+#ifdef STK_QUALCOMM_POWER_CTRL
+ ret = stk3x1x_device_ctl(ps_data, enable);
+ if (ret)
+ return ret;
+#endif
+ }
+
+ return 0;
+}
+#endif /* #ifdef STK_GES */
+
+static int32_t stk3x1x_enable_ps(struct stk3x1x_data *ps_data, uint8_t enable, uint8_t validate_reg)
+{
+ int32_t ret;
+ uint8_t w_state_reg;
+ uint8_t curr_ps_enable;
+ uint32_t reading;
+ int32_t near_far_state;
+
+#ifdef STK_QUALCOMM_POWER_CTRL
+ if (enable) {
+ ret = stk3x1x_device_ctl(ps_data, enable);
+ if (ret)
+ return ret;
+ }
+#endif
+
+#ifdef STK_CHK_REG
+ if(validate_reg)
+ {
+ ret = stk3x1x_validate_n_handle(ps_data->client);
+ if(ret < 0)
+ printk(KERN_ERR "stk3x1x_validate_n_handle fail: %d\n", ret);
+ }
+#endif /* #ifdef STK_CHK_REG */
+
+#ifdef STK_GES
+ if(ps_data->ges_enabled && enable)
+ {
+ printk(KERN_INFO "%s: force disable ges\n", __func__);
+ stk3x1x_enable_ges(ps_data, 0, 1);
+ ps_data->re_enable_ges = 1;
+ }
+#endif
+ curr_ps_enable = ps_data->ps_enabled?1:0;
+ if(curr_ps_enable == enable)
+ return 0;
+
+#ifdef STK_TUNE0
+ if (!(ps_data->psi_set) && !enable)
+ {
+ hrtimer_cancel(&ps_data->ps_tune0_timer);
+ cancel_work_sync(&ps_data->stk_ps_tune0_work);
+ }
+#endif
+ if(ps_data->first_boot == true)
+ {
+ ps_data->first_boot = false;
+ }
+
+ ret = stk3x1x_get_state(ps_data);
+ if(ret < 0)
+ return ret;
+ w_state_reg = ret;
+
+
+ w_state_reg &= ~(STK_STATE_EN_PS_MASK | STK_STATE_EN_WAIT_MASK | STK_STATE_EN_AK_MASK);
+ if(enable)
+ {
+ w_state_reg |= STK_STATE_EN_PS_MASK;
+ if(!(ps_data->als_enabled))
+ w_state_reg |= STK_STATE_EN_WAIT_MASK;
+ }
+ ret = stk3x1x_set_state(ps_data, w_state_reg);
+ if(ret < 0)
+ return ret;
+ ps_data->state_reg = w_state_reg;
+
+ if(enable)
+ {
+#ifdef STK_TUNE0
+ #ifdef CALI_PS_EVERY_TIME
+ ps_data->psi_set = 0;
+ ps_data->psa = 0;
+ ps_data->psi = 0xFFFF;
+ #ifndef QUALCOMM_PLATFORM
+ ps_data->ps_thd_h = ps_data->ps_high_thd_boot;
+ ps_data->ps_thd_l = ps_data->ps_low_thd_boot;
+ #endif
+ hrtimer_start(&ps_data->ps_tune0_timer, ps_data->ps_tune0_delay, HRTIMER_MODE_REL);
+ #else
+
+ if (!(ps_data->psi_set))
+ hrtimer_start(&ps_data->ps_tune0_timer, ps_data->ps_tune0_delay, HRTIMER_MODE_REL);
+ #endif /* #ifdef CALI_PS_EVERY_TIME */
+ stk3x1x_set_ps_thd_h(ps_data, ps_data->ps_thd_h);
+ stk3x1x_set_ps_thd_l(ps_data, ps_data->ps_thd_l);
+#endif
+ printk(KERN_INFO "%s: HT=%d,LT=%d\n", __func__, ps_data->ps_thd_h, ps_data->ps_thd_l);
+#ifdef STK_POLL_PS
+ hrtimer_start(&ps_data->ps_timer, ps_data->ps_poll_delay, HRTIMER_MODE_REL);
+ ps_data->ps_distance_last = -1;
+#endif
+#ifndef STK_POLL_PS
+ #ifndef STK_POLL_ALS
+ if(!(ps_data->als_enabled))
+ #endif /* #ifndef STK_POLL_ALS */
+ enable_irq(ps_data->irq);
+#endif /* #ifndef STK_POLL_PS */
+ ps_data->ps_enabled = true;
+#ifdef STK_CHK_REG
+ if(!validate_reg)
+ {
+ ps_data->ps_distance_last = 1;
+ input_report_abs(ps_data->ps_input_dev, ABS_DISTANCE, 1);
+ input_sync(ps_data->ps_input_dev);
+ wake_lock_timeout(&ps_data->ps_wakelock, 3*HZ);
+ reading = stk3x1x_get_ps_reading(ps_data);
+ printk(KERN_INFO "%s: force report ps input event=1, ps code = %d\n",__func__, reading);
+ }
+ else
+#endif /* #ifdef STK_CHK_REG */
+ {
+ usleep_range(4000, 5000);
+ ret = stk3x1x_get_flag(ps_data);
+ if (ret < 0)
+ return ret;
+ near_far_state = ret & STK_FLG_NF_MASK;
+ ps_data->ps_distance_last = near_far_state;
+ input_report_abs(ps_data->ps_input_dev, ABS_DISTANCE, near_far_state);
+ input_sync(ps_data->ps_input_dev);
+ wake_lock_timeout(&ps_data->ps_wakelock, 3*HZ);
+ reading = stk3x1x_get_ps_reading(ps_data);
+ printk(KERN_INFO "%s: ps input event=%d, ps code = %d\n",__func__, near_far_state, reading);
+ }
+ }
+ else
+ {
+#ifdef STK_POLL_PS
+ hrtimer_cancel(&ps_data->ps_timer);
+ cancel_work_sync(&ps_data->stk_ps_work);
+#else
+#ifndef STK_POLL_ALS
+ if(!(ps_data->als_enabled))
+#endif
+ disable_irq(ps_data->irq);
+#endif
+ ps_data->ps_enabled = false;
+#ifdef STK_GES
+ if(ps_data->re_enable_ges)
+ {
+ printk(KERN_INFO "%s: re-enable ges\n", __func__);
+ stk3x1x_enable_ges(ps_data, 1, 1);
+ ps_data->re_enable_ges = 0;
+ }
+#endif
+#ifdef STK_QUALCOMM_POWER_CTRL
+ ret = stk3x1x_device_ctl(ps_data, enable);
+ if (ret)
+ return ret;
+#endif
+ }
+ return ret;
+}
+
+static int32_t stk3x1x_enable_als(struct stk3x1x_data *ps_data, uint8_t enable)
+{
+ int32_t ret;
+ uint8_t w_state_reg;
+ uint8_t curr_als_enable = (ps_data->als_enabled)?1:0;
+
+#ifdef STK_GES
+ if(ps_data->ges_enabled)
+ {
+ printk(KERN_INFO "%s: since ges is enabled, ALS is disabled\n", __func__);
+ ps_data->re_enable_als = enable ? true : false;
+ return 0;
+ }
+#endif /* #ifdef STK_GES */
+ if(curr_als_enable == enable)
+ return 0;
+#ifdef STK_QUALCOMM_POWER_CTRL
+ if (enable) {
+ ret = stk3x1x_device_ctl(ps_data, enable);
+ if (ret)
+ return ret;
+ }
+#endif
+#ifndef STK_POLL_ALS
+ #ifdef STK_IRS
+ if(enable && !(ps_data->ps_enabled))
+ {
+ ret = stk3x1x_get_ir_reading(ps_data, STK_IRS_IT_REDUCE );
+ if(ret > 0)
+ ps_data->ir_code = ret;
+ }
+ #endif
+
+ if (enable)
+ {
+ stk3x1x_set_als_thd_h(ps_data, 0x0000);
+ stk3x1x_set_als_thd_l(ps_data, 0xFFFF);
+ }
+#endif
+
+ ret = stk3x1x_get_state(ps_data);
+ if(ret < 0)
+ return ret;
+
+ w_state_reg = (uint8_t)(ret & (~(STK_STATE_EN_ALS_MASK | STK_STATE_EN_WAIT_MASK)));
+ if(enable)
+ w_state_reg |= STK_STATE_EN_ALS_MASK;
+ else if (ps_data->ps_enabled)
+ w_state_reg |= STK_STATE_EN_WAIT_MASK;
+
+ ret = stk3x1x_set_state(ps_data, w_state_reg);
+ if(ret < 0)
+ return ret;
+ ps_data->state_reg = w_state_reg;
+
+ if (enable)
+ {
+ ps_data->als_enabled = true;
+#ifdef STK_POLL_ALS
+ hrtimer_start(&ps_data->als_timer, ps_data->als_poll_delay, HRTIMER_MODE_REL);
+#else
+#ifndef STK_POLL_PS
+ if(!(ps_data->ps_enabled))
+#endif
+ enable_irq(ps_data->irq);
+#endif
+#ifdef STK_IRS
+ ps_data->als_data_index = 0;
+#endif
+ }
+ else
+ {
+ ps_data->als_enabled = false;
+#ifdef STK_POLL_ALS
+ hrtimer_cancel(&ps_data->als_timer);
+ cancel_work_sync(&ps_data->stk_als_work);
+#else
+#ifndef STK_POLL_PS
+ if(!(ps_data->ps_enabled))
+#endif
+ disable_irq(ps_data->irq);
+#endif
+#ifdef STK_QUALCOMM_POWER_CTRL
+ ret = stk3x1x_device_ctl(ps_data, enable);
+ if (ret)
+ return ret;
+#endif
+ }
+ return ret;
+}
+
+static int32_t stk3x1x_get_als_reading(struct stk3x1x_data *ps_data)
+{
+ int32_t als_data, ir_data = 0;
+#ifdef STK_ALS_FIR
+ int index;
+ int firlen = atomic_read(&ps_data->firlength);
+#endif
+ unsigned char value[2];
+ int ret;
+ const int ir_enlarge = 1 << (STK_ALS_READ_IRS_IT_REDUCE - STK_IRS_IT_REDUCE);
+
+ ret = stk3x1x_i2c_read_data(ps_data->client, STK_DATA1_ALS_REG, 2, &value[0]);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s fail, ret=0x%x", __func__, ret);
+ return ret;
+ }
+ als_data = (value[0]<<8) | value[1];
+ // printk("%s: raw als_data=%d\n", __func__, als_data);
+ if(ps_data->p_wv_r_bd_with_co & 0b010)
+ {
+ if(als_data < STK_ALS_THRESHOLD && ps_data->als_code_last > 10000)
+ {
+ ir_data = stk3x1x_get_ir_reading(ps_data, STK_ALS_READ_IRS_IT_REDUCE);
+#ifdef STK_IRS
+ if(ir_data > 0)
+ ps_data->ir_code = ir_data * ir_enlarge;
+#endif
+ // printk(KERN_INFO "%s: als_data=%d, als_code_last=%d,ir_data=%d\n",
+ // __func__, als_data, ps_data->als_code_last, ir_data);
+ if(ir_data > (STK_ALS_THRESHOLD*3))
+ {
+ als_data = ps_data->als_code_last;
+ }
+ }
+#ifdef STK_IRS
+ else
+ {
+ ps_data->ir_code = 0;
+ }
+#endif
+ }
+
+ ps_data->als_code_last = als_data;
+
+#ifdef STK_ALS_FIR
+ if(ps_data->fir.number < firlen)
+ {
+ ps_data->fir.raw[ps_data->fir.number] = als_data;
+ ps_data->fir.sum += als_data;
+ ps_data->fir.number++;
+ ps_data->fir.idx++;
+ }
+ else
+ {
+ index = ps_data->fir.idx % firlen;
+ ps_data->fir.sum -= ps_data->fir.raw[index];
+ ps_data->fir.raw[index] = als_data;
+ ps_data->fir.sum += als_data;
+ ps_data->fir.idx++;
+ als_data = ps_data->fir.sum/firlen;
+ }
+#endif
+
+ return als_data;
+}
+
+
+#if (defined(STK_IRS) && defined(STK_POLL_ALS))
+static int stk_als_ir_skip_als(struct stk3x1x_data *ps_data)
+{
+ int ret;
+ unsigned char value[2];
+
+ if(ps_data->als_data_index < 60000)
+ ps_data->als_data_index++;
+ else
+ ps_data->als_data_index = 0;
+
+ if( ps_data->als_data_index % 10 == 1)
+ {
+ ret = stk3x1x_i2c_read_data(ps_data->client, STK_DATA1_ALS_REG, 2, &value[0]);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s fail, ret=0x%x", __func__, ret);
+ return ret;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+static void stk_als_ir_get_corr(struct stk3x1x_data *ps_data, int32_t als)
+{
+ int32_t als_comperator;
+
+ if(ps_data->ir_code)
+ {
+ ps_data->als_correct_factor = 1000;
+ if(als < STK_IRC_MAX_ALS_CODE && als > STK_IRC_MIN_ALS_CODE &&
+ ps_data->ir_code > STK_IRC_MIN_IR_CODE)
+ {
+ als_comperator = als * STK_IRC_ALS_NUMERA / STK_IRC_ALS_DENOMI;
+ if(ps_data->ir_code > als_comperator)
+ ps_data->als_correct_factor = STK_IRC_ALS_CORREC;
+ }
+#ifdef STK_DEBUG_PRINTF
+ printk(KERN_INFO "%s: als=%d, ir=%d, als_correct_factor=%d", __func__,
+ als, ps_data->ir_code, ps_data->als_correct_factor);
+#endif
+ ps_data->ir_code = 0;
+ }
+ return;
+}
+
+static int stk_als_ir_run(struct stk3x1x_data *ps_data)
+{
+ int ret;
+
+ if( ps_data->als_data_index % 10 == 0)
+ {
+ if(ps_data->ps_distance_last != 0 && ps_data->ir_code == 0)
+ {
+ ret = stk3x1x_get_ir_reading(ps_data, STK_IRS_IT_REDUCE);
+ if(ret > 0)
+ ps_data->ir_code = ret;
+ }
+ return ret;
+ }
+ return 0;
+}
+#endif /* #if (defined(STK_IRS) && defined(STK_POLL_ALS)) */
+
+
+static int32_t stk3x1x_set_irs_it_slp(struct stk3x1x_data *ps_data, uint16_t *slp_time, int32_t ials_it_reduce)
+{
+ uint8_t irs_alsctrl;
+ int32_t ret;
+
+ irs_alsctrl = (ps_data->alsctrl_reg & 0x0F) - ials_it_reduce;
+ switch(irs_alsctrl)
+ {
+ case 2:
+ *slp_time = 1;
+ break;
+ case 3:
+ *slp_time = 2;
+ break;
+ case 4:
+ *slp_time = 3;
+ break;
+ case 5:
+ *slp_time = 6;
+ break;
+ case 6:
+ *slp_time = 12;
+ break;
+ case 7:
+ *slp_time = 24;
+ break;
+ case 8:
+ *slp_time = 48;
+ break;
+ case 9:
+ *slp_time = 96;
+ break;
+ case 10:
+ *slp_time = 192;
+ break;
+ default:
+ printk(KERN_ERR "%s: unknown ALS IT=0x%x\n", __func__, irs_alsctrl);
+ ret = -EINVAL;
+ return ret;
+ }
+ irs_alsctrl |= (ps_data->alsctrl_reg & 0xF0);
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_ALSCTRL_REG, irs_alsctrl);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+ return 0;
+}
+
+static int32_t stk3x1x_get_ir_reading(struct stk3x1x_data *ps_data, int32_t als_it_reduce)
+{
+ int32_t word_data, ret;
+ uint8_t w_reg, retry = 0;
+ uint16_t irs_slp_time = 100;
+ unsigned char value[2];
+
+ ret = stk3x1x_set_irs_it_slp(ps_data, &irs_slp_time, als_it_reduce);
+ if(ret < 0)
+ goto irs_err_i2c_rw;
+
+ ret = stk3x1x_get_state(ps_data);
+ if(ret < 0)
+ goto irs_err_i2c_rw;
+
+ w_reg = ret | STK_STATE_EN_IRS_MASK;
+ ret = stk3x1x_set_state(ps_data, w_reg);
+ if(ret < 0)
+ goto irs_err_i2c_rw;
+ msleep(irs_slp_time);
+
+ do
+ {
+ usleep_range(3000, 4000);
+ ret = stk3x1x_get_flag(ps_data);
+ if (ret < 0)
+ goto irs_err_i2c_rw;
+ retry++;
+ }while(retry < 10 && ((ret&STK_FLG_IR_RDY_MASK) == 0));
+
+ if(retry == 10)
+ {
+ printk(KERN_ERR "%s: ir data is not ready for a long time\n", __func__);
+ ret = -EINVAL;
+ goto irs_err_i2c_rw;
+ }
+
+ ret = stk3x1x_set_flag(ps_data, ret, STK_FLG_IR_RDY_MASK);
+ if (ret < 0)
+ goto irs_err_i2c_rw;
+
+ ret = stk3x1x_i2c_read_data(ps_data->client, STK_DATA1_IR_REG, 2, &value[0]);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s fail, ret=0x%x", __func__, ret);
+ goto irs_err_i2c_rw;
+ }
+ word_data = ((value[0]<<8) | value[1]);
+ //printk(KERN_INFO "%s: ir=%d\n", __func__, word_data);
+
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_ALSCTRL_REG, ps_data->alsctrl_reg );
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ goto irs_err_i2c_rw;
+ }
+
+ return word_data;
+
+irs_err_i2c_rw:
+ return ret;
+}
+
+#ifdef STK_CHK_REG
+static int stk3x1x_chk_reg_valid(struct stk3x1x_data *ps_data)
+{
+ unsigned char value[9];
+ int err;
+ /*
+ uint8_t cnt;
+
+ for(cnt=0;cnt<9;cnt++)
+ {
+ value[cnt] = stk3x1x_i2c_smbus_read_byte_data(ps_data->client, (cnt+1));
+ if(value[cnt] < 0)
+ {
+ printk(KERN_ERR "%s fail, ret=%d", __func__, value[cnt]);
+ return value[cnt];
+ }
+ }
+ */
+ err = stk3x1x_i2c_read_data(ps_data->client, STK_PSCTRL_REG, 9, &value[0]);
+ if(err < 0)
+ {
+ printk(KERN_ERR "%s: fail, ret=%d\n", __func__, err);
+ return err;
+ }
+
+ if(value[0] != ps_data->psctrl_reg)
+ {
+ printk(KERN_ERR "%s: invalid reg 0x01=0x%2x\n", __func__, value[0]);
+ return 0xFF;
+ }
+#ifdef STK_IRS
+ if((value[1] != ps_data->alsctrl_reg) && (value[1] != (ps_data->alsctrl_reg - STK_IRS_IT_REDUCE))
+ && (value[1] != (ps_data->alsctrl_reg - STK_ALS_READ_IRS_IT_REDUCE)))
+#else
+ if((value[1] != ps_data->alsctrl_reg) && (value[1] != (ps_data->alsctrl_reg - STK_ALS_READ_IRS_IT_REDUCE)))
+#endif
+ {
+ printk(KERN_ERR "%s: invalid reg 0x02=0x%2x\n", __func__, value[1]);
+ return 0xFF;
+ }
+ if(value[2] != ps_data->ledctrl_reg)
+ {
+ printk(KERN_ERR "%s: invalid reg 0x03=0x%2x\n", __func__, value[2]);
+ return 0xFF;
+ }
+ if(value[3] != ps_data->int_reg)
+ {
+ printk(KERN_ERR "%s: invalid reg 0x04=0x%2x\n", __func__, value[3]);
+ return 0xFF;
+ }
+ if(value[4] != ps_data->wait_reg)
+ {
+ printk(KERN_ERR "%s: invalid reg 0x05=0x%2x\n", __func__, value[4]);
+ return 0xFF;
+ }
+ if(value[5] != ((ps_data->ps_thd_h & 0xFF00) >> 8))
+ {
+ printk(KERN_ERR "%s: invalid reg 0x06=0x%2x\n", __func__, value[5]);
+ return 0xFF;
+ }
+ if(value[6] != (ps_data->ps_thd_h & 0x00FF))
+ {
+ printk(KERN_ERR "%s: invalid reg 0x07=0x%2x\n", __func__, value[6]);
+ return 0xFF;
+ }
+ if(value[7] != ((ps_data->ps_thd_l & 0xFF00) >> 8))
+ {
+ printk(KERN_ERR "%s: invalid reg 0x08=0x%2x\n", __func__, value[7]);
+ return 0xFF;
+ }
+ if(value[8] != (ps_data->ps_thd_l & 0x00FF))
+ {
+ printk(KERN_ERR "%s: invalid reg 0x09=0x%2x\n", __func__, value[8]);
+ return 0xFF;
+ }
+
+ return 0;
+}
+
+static int stk3x1x_validate_n_handle(struct i2c_client *client)
+{
+ struct stk3x1x_data *ps_data = i2c_get_clientdata(client);
+ int err;
+
+ err = stk3x1x_chk_reg_valid(ps_data);
+ if(err < 0)
+ {
+ printk(KERN_ERR "stk3x1x_chk_reg_valid fail: %d\n", err);
+ return err;
+ }
+
+ if(err == 0xFF)
+ {
+ printk(KERN_ERR "%s: Re-init chip\n", __func__);
+ err = stk3x1x_software_reset(ps_data);
+ if(err < 0)
+ return err;
+ err = stk3x1x_init_all_reg(ps_data);
+ if(err < 0)
+ return err;
+
+ //ps_data->psa = 0;
+ //ps_data->psi = 0xFFFF;
+ stk3x1x_set_ps_thd_h(ps_data, ps_data->ps_thd_h);
+ stk3x1x_set_ps_thd_l(ps_data, ps_data->ps_thd_l);
+#ifdef STK_ALS_FIR
+ memset(&ps_data->fir, 0x00, sizeof(ps_data->fir));
+#endif
+ return 0xFF;
+ }
+ return 0;
+}
+#endif /* #ifdef STK_CHK_REG */
+
+static ssize_t stk_als_code_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ int32_t reading;
+#ifdef STK_POLL_ALS
+ reading = ps_data->als_code_last;
+#else
+ unsigned char value[2];
+ int ret;
+ ret = stk3x1x_i2c_read_data(ps_data->client, STK_DATA1_ALS_REG, 2, &value[0]);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s fail, ret=0x%x", __func__, ret);
+ return ret;
+ }
+ reading = (value[0]<<8) | value[1];
+#endif
+ return scnprintf(buf, PAGE_SIZE, "%d\n", reading);
+}
+
+
+static ssize_t stk_als_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ int32_t ret;
+
+ ret = stk3x1x_get_state(ps_data);
+ if(ret < 0)
+ return ret;
+ ret = (ret & STK_STATE_EN_ALS_MASK)?1:0;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+static ssize_t stk_als_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ uint8_t en;
+ if (sysfs_streq(buf, "1"))
+ en = 1;
+ else if (sysfs_streq(buf, "0"))
+ en = 0;
+ else
+ {
+ printk(KERN_ERR "%s, invalid value %d\n", __func__, *buf);
+ return -EINVAL;
+ }
+ printk(KERN_INFO "%s: Enable ALS : %d\n", __func__, en);
+ mutex_lock(&ps_data->io_lock);
+ stk3x1x_enable_als(ps_data, en);
+ mutex_unlock(&ps_data->io_lock);
+ return size;
+}
+
+static ssize_t stk_als_lux_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ int32_t als_reading;
+ uint32_t als_lux;
+ als_reading = stk3x1x_get_als_reading(ps_data);
+ als_lux = stk_alscode2lux(ps_data, als_reading);
+ return scnprintf(buf, PAGE_SIZE, "%d lux\n", als_lux);
+}
+
+static ssize_t stk_als_lux_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ unsigned long value = 0;
+ int ret;
+ ret = kstrtoul(buf, 16, &value);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+ ps_data->als_lux_last = value;
+ input_report_abs(ps_data->als_input_dev, ABS_MISC, value);
+ input_sync(ps_data->als_input_dev);
+ printk(KERN_INFO "%s: als input event %ld lux\n",__func__, value);
+
+ return size;
+}
+
+
+static ssize_t stk_als_transmittance_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ int32_t transmittance;
+ transmittance = ps_data->als_transmittance;
+ return scnprintf(buf, PAGE_SIZE, "%d\n", transmittance);
+}
+
+
+static ssize_t stk_als_transmittance_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ unsigned long value = 0;
+ int ret;
+ ret = kstrtoul(buf, 10, &value);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+ ps_data->als_transmittance = value;
+ return size;
+}
+
+static ssize_t stk_als_delay_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ int64_t delay;
+ mutex_lock(&ps_data->io_lock);
+ delay = ktime_to_ns(ps_data->als_poll_delay);
+ mutex_unlock(&ps_data->io_lock);
+ return scnprintf(buf, PAGE_SIZE, "%lld\n", delay);
+}
+
+
+static ssize_t stk_als_delay_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ uint64_t value = 0;
+ int ret;
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ ret = kstrtoull(buf, 10, &value);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoull failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+#ifdef STK_DEBUG_PRINTF
+ printk(KERN_INFO "%s: set als poll delay=%lld\n", __func__, value);
+#endif
+ if(value < MIN_ALS_POLL_DELAY_NS)
+ {
+ printk(KERN_ERR "%s: delay is too small\n", __func__);
+ value = MIN_ALS_POLL_DELAY_NS;
+ }
+ mutex_lock(&ps_data->io_lock);
+ if(value != ktime_to_ns(ps_data->als_poll_delay))
+ ps_data->als_poll_delay = ns_to_ktime(value);
+#ifdef STK_ALS_FIR
+ ps_data->fir.number = 0;
+ ps_data->fir.idx = 0;
+ ps_data->fir.sum = 0;
+#endif
+ mutex_unlock(&ps_data->io_lock);
+ return size;
+}
+
+static ssize_t stk_als_ir_code_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ int32_t reading;
+ reading = stk3x1x_get_ir_reading(ps_data, STK_IRS_IT_REDUCE);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", reading);
+}
+
+#ifdef STK_ALS_FIR
+static ssize_t stk_als_firlen_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ int len = atomic_read(&ps_data->firlength);
+
+ printk(KERN_INFO "%s: len = %2d, idx = %2d\n", __func__, len, ps_data->fir.idx);
+ printk(KERN_INFO "%s: sum = %5d, ave = %5d\n", __func__, ps_data->fir.sum, ps_data->fir.sum/len);
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", len);
+}
+
+
+static ssize_t stk_als_firlen_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ uint64_t value = 0;
+ int ret;
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ ret = kstrtoull(buf, 10, &value);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoull failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+
+ if(value > MAX_FIR_LEN)
+ {
+ printk(KERN_ERR "%s: firlen exceed maximum filter length\n", __func__);
+ }
+ else if (value < 1)
+ {
+ atomic_set(&ps_data->firlength, 1);
+ memset(&ps_data->fir, 0x00, sizeof(ps_data->fir));
+ }
+ else
+ {
+ atomic_set(&ps_data->firlength, value);
+ memset(&ps_data->fir, 0x00, sizeof(ps_data->fir));
+ }
+ return size;
+}
+#endif /* #ifdef STK_ALS_FIR */
+
+#ifdef STK_GES
+static ssize_t stk_ges_code_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ int ret;
+ unsigned int gest0 = 0, gest1 = 0, gest2 = 0;
+
+ if(!ps_data->ges_enabled)
+ return 0;
+
+ ret = stk3x1x_get_ges_reading(ps_data, &gest0, &gest1, &gest2);
+ if(ret < 0)
+ return ret;
+ else if(ret == 0xFFFF)
+ atomic_set(&ps_data->gesture2, 0);
+
+ return scnprintf(buf, PAGE_SIZE, "%5d,%5d,%5d\n", gest0, gest1, atomic_read(&ps_data->gesture2));
+}
+
+static ssize_t stk_ges_code_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ uint8_t ges;
+ unsigned long value = 0;
+ int ret;
+
+ ret = kstrtoul(buf, 16, &value);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=%d\n", __func__, ret);
+ return ret;
+ }
+
+ switch(value)
+ {
+ case 3:
+ //printk(KERN_INFO "%s: ges input event, not detected\n",__func__);
+ case 0:
+ return size;
+ case 1:
+ ges = KEY_PAGEUP;
+ atomic_set(&ps_data->gesture2, 0);
+ printk(KERN_INFO "%s: ges input event >>>\n",__func__);
+ break;
+ case 2:
+ ges = KEY_PAGEDOWN;
+ atomic_set(&ps_data->gesture2, 0);
+ printk(KERN_INFO "%s: ges input event <<<\n",__func__);
+ break;
+ case 32:
+ ges = KEY_VOLUMEDOWN;
+ printk(KERN_INFO "%s: ges input event near\n",__func__);
+ break;
+ case 48:
+ ges = KEY_VOLUMEUP;
+ printk(KERN_INFO "%s: ges input event far\n",__func__);
+ break;
+ default:
+ printk(KERN_ERR "%s, invalid value %d\n", __func__, (int)value);
+ return -EINVAL;
+ }
+
+ input_report_key(ps_data->ges_input_dev, ges, 1);
+ input_report_key(ps_data->ges_input_dev, ges, 0);
+ input_sync(ps_data->ges_input_dev);
+ return size;
+}
+
+static ssize_t stk_ges_poll_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int len = 0, ii = 0, jj = 0;
+
+ while(stk_ges_op[ii].ops[0] != 0)
+ {
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%x ", ii);
+ for(jj=0;jj<4;jj++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%x ", stk_ges_op[ii].ops[jj]);
+ len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
+ ii++;
+ }
+ return len;
+}
+
+
+static ssize_t stk_ges_poll_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ int32_t ret, i = 0, index = 0;
+ char *token;
+ unsigned long value = 0;
+
+ while(buf != '\0')
+ {
+ token = strsep((char **)&buf, " ");
+ if((ret = kstrtoul(token, 16, &value)) < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+
+ if(i == 0)
+ {
+ if(value >= 10)
+ {
+ memset(stk_ges_op, 0, sizeof(stk_ges_op));
+ break;
+ }
+ else
+ index = value;
+ }
+ else
+ {
+ stk_ges_op[index].ops[i-1] = value;
+ }
+ i++;
+ if(i == 5)
+ break;
+ }
+ if(i != 5)
+ {
+ printk(KERN_ERR "%s: invalid length(%d)\n", __func__, i);
+ memset(&(stk_ges_op[index]), 0, sizeof(union stk_ges_operation));
+ }
+ return size;
+}
+
+static ssize_t stk_ges_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ unsigned long value = 0;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &value);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=%d\n", __func__, ret);
+ return ret;
+ }
+ printk(KERN_INFO "%s: Enable GES : %d\n", __func__, (int)value);
+
+ switch(value)
+ {
+ case 0:
+ mutex_lock(&ps_data->io_lock);
+ if(ps_data->ges_enabled == 1)
+ stk3x1x_enable_ges(ps_data, 0, 1);
+ else
+ stk3x1x_enable_ges(ps_data, 0, 2);
+ mutex_unlock(&ps_data->io_lock);
+ break;
+ case 1:
+ mutex_lock(&ps_data->io_lock);
+ stk3x1x_enable_ges(ps_data, 1, 1);
+ mutex_unlock(&ps_data->io_lock);
+ break;
+ case 2:
+ mutex_lock(&ps_data->io_lock);
+ stk3x1x_enable_ges(ps_data, 1, 2);
+ mutex_unlock(&ps_data->io_lock);
+ break;
+ default:
+ printk(KERN_ERR "%s, invalid value %d\n", __func__, *buf);
+ return -EINVAL;
+ break;
+ }
+
+ return size;
+}
+
+static ssize_t stk_ges_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", ps_data->ges_enabled);
+}
+
+#endif /* #ifdef STK_GES */
+
+static ssize_t stk_ps_code_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ uint32_t reading;
+ reading = stk3x1x_get_ps_reading(ps_data);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", reading);
+}
+
+static ssize_t stk_ps_enable_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int32_t ret;
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+
+ ret = stk3x1x_get_state(ps_data);
+ if(ret < 0)
+ return ret;
+ ret = (ret & STK_STATE_EN_PS_MASK)?1:0;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+static ssize_t stk_ps_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ uint8_t en;
+ if (sysfs_streq(buf, "1"))
+ en = 1;
+ else if (sysfs_streq(buf, "0"))
+ en = 0;
+ else
+ {
+ printk(KERN_ERR "%s, invalid value %d\n", __func__, *buf);
+ return -EINVAL;
+ }
+ printk(KERN_INFO "%s: Enable PS : %d\n", __func__, en);
+ mutex_lock(&ps_data->io_lock);
+ stk3x1x_enable_ps(ps_data, en, 1);
+ mutex_unlock(&ps_data->io_lock);
+ return size;
+}
+
+static ssize_t stk_ps_enable_aso_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int32_t ret;
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+
+ ret = stk3x1x_i2c_smbus_read_byte_data(ps_data->client,STK_STATE_REG);
+ ret = (ret & STK_STATE_EN_ASO_MASK)?1:0;
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", ret);
+}
+
+static ssize_t stk_ps_enable_aso_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ uint8_t en;
+ int32_t ret;
+ uint8_t w_state_reg;
+
+ if (sysfs_streq(buf, "1"))
+ en = 1;
+ else if (sysfs_streq(buf, "0"))
+ en = 0;
+ else
+ {
+ printk(KERN_ERR "%s, invalid value %d\n", __func__, *buf);
+ return -EINVAL;
+ }
+ printk(KERN_INFO "%s: Enable PS ASO : %d\n", __func__, en);
+
+ ret = stk3x1x_i2c_smbus_read_byte_data(ps_data->client, STK_STATE_REG);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+ w_state_reg = (uint8_t)(ret & (~STK_STATE_EN_ASO_MASK));
+ if(en)
+ w_state_reg |= STK_STATE_EN_ASO_MASK;
+
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_STATE_REG, w_state_reg);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+
+ return size;
+}
+
+
+static ssize_t stk_ps_offset_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ int32_t word_data;
+ unsigned char value[2];
+ int ret;
+
+ ret = stk3x1x_i2c_read_data(ps_data->client, STK_DATA1_OFFSET_REG, 2, &value[0]);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s fail, ret=0x%x", __func__, ret);
+ return ret;
+ }
+ word_data = (value[0]<<8) | value[1];
+
+ return scnprintf(buf, PAGE_SIZE, "%d\n", word_data);
+}
+
+static ssize_t stk_ps_offset_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ unsigned long offset = 0;
+ int ret;
+ unsigned char val[2];
+
+ ret = kstrtoul(buf, 10, &offset);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+ if(offset > 65535)
+ {
+ printk(KERN_ERR "%s: invalid value, offset=%ld\n", __func__, offset);
+ return -EINVAL;
+ }
+
+ val[0] = (offset & 0xFF00) >> 8;
+ val[1] = offset & 0x00FF;
+ ret = stk3x1x_i2c_write_data(ps_data->client, STK_DATA1_OFFSET_REG, 2, val);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+
+ return size;
+}
+
+
+static ssize_t stk_ps_distance_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ int32_t dist=1, ret;
+
+ ret = stk3x1x_get_flag(ps_data);
+ if(ret < 0)
+ return ret;
+ dist = (ret & STK_FLG_NF_MASK)?1:0;
+
+ ps_data->ps_distance_last = dist;
+ input_report_abs(ps_data->ps_input_dev, ABS_DISTANCE, dist);
+ input_sync(ps_data->ps_input_dev);
+ wake_lock_timeout(&ps_data->ps_wakelock, 3*HZ);
+ printk(KERN_INFO "%s: ps input event %d cm\n",__func__, dist);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", dist);
+}
+
+
+static ssize_t stk_ps_distance_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ unsigned long value = 0;
+ int ret;
+ ret = kstrtoul(buf, 10, &value);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+ ps_data->ps_distance_last = value;
+ input_report_abs(ps_data->ps_input_dev, ABS_DISTANCE, value);
+ input_sync(ps_data->ps_input_dev);
+ wake_lock_timeout(&ps_data->ps_wakelock, 3*HZ);
+ printk(KERN_INFO "%s: ps input event %ld cm\n",__func__, value);
+ return size;
+}
+
+
+static ssize_t stk_ps_code_thd_l_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int32_t ps_thd_l1_reg, ps_thd_l2_reg;
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ ps_thd_l1_reg = stk3x1x_i2c_smbus_read_byte_data(ps_data->client,STK_THDL1_PS_REG);
+ if(ps_thd_l1_reg < 0)
+ {
+ printk(KERN_ERR "%s fail, err=0x%x", __func__, ps_thd_l1_reg);
+ return -EINVAL;
+ }
+ ps_thd_l2_reg = stk3x1x_i2c_smbus_read_byte_data(ps_data->client,STK_THDL2_PS_REG);
+ if(ps_thd_l2_reg < 0)
+ {
+ printk(KERN_ERR "%s fail, err=0x%x", __func__, ps_thd_l2_reg);
+ return -EINVAL;
+ }
+ ps_thd_l1_reg = ps_thd_l1_reg<<8 | ps_thd_l2_reg;
+ return scnprintf(buf, PAGE_SIZE, "%d\n", ps_thd_l1_reg);
+}
+
+
+static ssize_t stk_ps_code_thd_l_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ unsigned long value = 0;
+ int ret;
+ ret = kstrtoul(buf, 10, &value);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+ stk3x1x_set_ps_thd_l(ps_data, value);
+ return size;
+}
+
+static ssize_t stk_ps_code_thd_h_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int32_t ps_thd_h1_reg, ps_thd_h2_reg;
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ ps_thd_h1_reg = stk3x1x_i2c_smbus_read_byte_data(ps_data->client,STK_THDH1_PS_REG);
+ if(ps_thd_h1_reg < 0)
+ {
+ printk(KERN_ERR "%s fail, err=0x%x", __func__, ps_thd_h1_reg);
+ return -EINVAL;
+ }
+ ps_thd_h2_reg = stk3x1x_i2c_smbus_read_byte_data(ps_data->client,STK_THDH2_PS_REG);
+ if(ps_thd_h2_reg < 0)
+ {
+ printk(KERN_ERR "%s fail, err=0x%x", __func__, ps_thd_h2_reg);
+ return -EINVAL;
+ }
+ ps_thd_h1_reg = ps_thd_h1_reg<<8 | ps_thd_h2_reg;
+ return scnprintf(buf, PAGE_SIZE, "%d\n", ps_thd_h1_reg);
+}
+
+
+static ssize_t stk_ps_code_thd_h_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ unsigned long value = 0;
+ int ret;
+ ret = kstrtoul(buf, 10, &value);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+ stk3x1x_set_ps_thd_h(ps_data, value);
+ return size;
+}
+
+
+static ssize_t stk_all_reg_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int32_t ps_reg[0x22];
+ uint8_t cnt;
+ int len = 0;
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+
+ for(cnt=0;cnt<0x20;cnt++)
+ {
+ ps_reg[cnt] = stk3x1x_i2c_smbus_read_byte_data(ps_data->client, (cnt));
+ if(ps_reg[cnt] < 0)
+ {
+ printk(KERN_ERR "%s fail, ret=%d", __func__, ps_reg[cnt]);
+ return -EINVAL;
+ }
+ else
+ {
+ printk(KERN_INFO "reg[0x%2X]=0x%2X\n", cnt, ps_reg[cnt]);
+ len += scnprintf(buf+len, PAGE_SIZE-len, "[%2X]%2X,", cnt, ps_reg[cnt]);
+ }
+ }
+ ps_reg[cnt] = stk3x1x_i2c_smbus_read_byte_data(ps_data->client, STK_PDT_ID_REG);
+ if(ps_reg[cnt] < 0)
+ {
+ printk( KERN_ERR "%s fail, ret=%d", __func__, ps_reg[cnt]);
+ return -EINVAL;
+ }
+ printk( KERN_INFO "reg[0x%x]=0x%2X\n", STK_PDT_ID_REG, ps_reg[cnt]);
+ cnt++;
+ ps_reg[cnt] = stk3x1x_i2c_smbus_read_byte_data(ps_data->client, STK_RSRVD_REG);
+ if(ps_reg[cnt] < 0)
+ {
+ printk( KERN_ERR "%s fail, ret=%d", __func__, ps_reg[cnt]);
+ return -EINVAL;
+ }
+ printk( KERN_INFO "reg[0x%x]=0x%2X\n", STK_RSRVD_REG, ps_reg[cnt]);
+ len += scnprintf(buf+len, PAGE_SIZE-len, "[%2X]%2X,[%2X]%2X\n", cnt-1, ps_reg[cnt-1], cnt, ps_reg[cnt]);
+ return len;
+/*
+ return scnprintf(buf, PAGE_SIZE, "[0]%2X [1]%2X [2]%2X [3]%2X [4]%2X [5]%2X [6/7 HTHD]%2X,%2X [8/9 LTHD]%2X, %2X [A]%2X [B]%2X [C]%2X [D]%2X [E/F Aoff]%2X,%2X,[10]%2X [11/12 PS]%2X,%2X [13]%2X [14]%2X [15/16 Foff]%2X,%2X [17]%2X [18]%2X [3E]%2X [3F]%2X\n",
+ ps_reg[0], ps_reg[1], ps_reg[2], ps_reg[3], ps_reg[4], ps_reg[5], ps_reg[6], ps_reg[7], ps_reg[8],
+ ps_reg[9], ps_reg[10], ps_reg[11], ps_reg[12], ps_reg[13], ps_reg[14], ps_reg[15], ps_reg[16], ps_reg[17],
+ ps_reg[18], ps_reg[19], ps_reg[20], ps_reg[21], ps_reg[22], ps_reg[23], ps_reg[24], ps_reg[25], ps_reg[26]);
+ */
+}
+
+static ssize_t stk_status_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ int32_t ps_reg[27];
+ uint8_t cnt;
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ for(cnt=0;cnt<25;cnt++)
+ {
+ ps_reg[cnt] = stk3x1x_i2c_smbus_read_byte_data(ps_data->client, (cnt));
+ if(ps_reg[cnt] < 0)
+ {
+ printk(KERN_ERR "%s fail, ret=%d", __func__, ps_reg[cnt]);
+ return -EINVAL;
+ }
+ else
+ {
+ printk(KERN_INFO "reg[0x%2X]=0x%2X\n", cnt, ps_reg[cnt]);
+ }
+ }
+ ps_reg[cnt] = stk3x1x_i2c_smbus_read_byte_data(ps_data->client, STK_PDT_ID_REG);
+ if(ps_reg[cnt] < 0)
+ {
+ printk( KERN_ERR "%s fail, ret=%d", __func__, ps_reg[cnt]);
+ return -EINVAL;
+ }
+ printk( KERN_INFO "reg[0x%x]=0x%2X\n", STK_PDT_ID_REG, ps_reg[cnt]);
+ cnt++;
+ ps_reg[cnt] = stk3x1x_i2c_smbus_read_byte_data(ps_data->client, STK_RSRVD_REG);
+ if(ps_reg[cnt] < 0)
+ {
+ printk( KERN_ERR "%s fail, ret=%d", __func__, ps_reg[cnt]);
+ return -EINVAL;
+ }
+ printk( KERN_INFO "reg[0x%x]=0x%2X\n", STK_RSRVD_REG, ps_reg[cnt]);
+
+ return scnprintf(buf, PAGE_SIZE, "[PS=%2X] [ALS=%2X] [WAIT=0x%4Xms] [EN_ASO=%2X] [EN_AK=%2X] [NEAR/FAR=%2X] [FLAG_OUI=%2X] [FLAG_PSINT=%2X] [FLAG_ALSINT=%2X]\n",
+ ps_reg[0]&0x01,(ps_reg[0]&0x02)>>1,((ps_reg[0]&0x04)>>2)*ps_reg[5]*6,(ps_reg[0]&0x20)>>5,
+ (ps_reg[0]&0x40)>>6,ps_reg[16]&0x01,(ps_reg[16]&0x04)>>2,(ps_reg[16]&0x10)>>4,(ps_reg[16]&0x20)>>5);
+}
+
+static ssize_t stk_recv_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ return scnprintf(buf, PAGE_SIZE, "0x%04X\n", atomic_read(&ps_data->recv_reg));
+}
+
+
+static ssize_t stk_recv_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ unsigned long value = 0;
+ int ret;
+ int32_t recv_data;
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+
+ if((ret = kstrtoul(buf, 16, &value)) < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+ recv_data = stk3x1x_i2c_smbus_read_byte_data(ps_data->client,value);
+// printk("%s: reg 0x%x=0x%x\n", __func__, (int)value, recv_data);
+ atomic_set(&ps_data->recv_reg, recv_data);
+ return size;
+}
+
+
+static ssize_t stk_send_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return 0;
+}
+
+
+static ssize_t stk_send_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ int addr, cmd;
+ int32_t ret, i;
+ char *token[10];
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+
+ for (i = 0; i < 2; i++)
+ token[i] = strsep((char **)&buf, " ");
+ if((ret = kstrtoul(token[0], 16, (unsigned long *)&(addr))) < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+ if((ret = kstrtoul(token[1], 16, (unsigned long *)&(cmd))) < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+ printk(KERN_INFO "%s: write reg 0x%x=0x%x\n", __func__, addr, cmd);
+
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, (unsigned char)addr, (unsigned char)cmd);
+ if (0 != ret)
+ {
+ printk(KERN_ERR "%s: stk3x1x_i2c_smbus_write_byte_data fail\n", __func__);
+ return ret;
+ }
+
+ return size;
+}
+
+#ifdef STK_TUNE0
+
+static ssize_t stk_ps_cali_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ int32_t word_data;
+ unsigned char value[2];
+ int ret;
+
+ ret = stk3x1x_i2c_read_data(ps_data->client, 0x20, 2, &value[0]);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s fail, ret=0x%x", __func__, ret);
+ return ret;
+ }
+ word_data = (value[0]<<8) | value[1];
+
+ ret = stk3x1x_i2c_read_data(ps_data->client, 0x22, 2, &value[0]);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s fail, ret=0x%x", __func__, ret);
+ return ret;
+ }
+ word_data += ((value[0]<<8) | value[1]);
+
+ printk("%s: psi_set=%d, psa=%d,psi=%d, word_data=%d\n", __func__,
+ ps_data->psi_set, ps_data->psa, ps_data->psi, word_data);
+#ifdef CALI_PS_EVERY_TIME
+ printk("%s: boot HT=%d, LT=%d\n", __func__, ps_data->ps_high_thd_boot, ps_data->ps_low_thd_boot);
+#endif
+ return 0;
+}
+
+static ssize_t stk_ps_maxdiff_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ unsigned long value = 0;
+ int ret;
+
+ if((ret = kstrtoul(buf, 10, &value)) < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+ ps_data->stk_max_min_diff = (int) value;
+ return size;
+}
+
+
+static ssize_t stk_ps_maxdiff_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", ps_data->stk_max_min_diff);
+}
+
+static ssize_t stk_ps_ltnct_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ unsigned long value = 0;
+ int ret;
+
+ if((ret = kstrtoul(buf, 10, &value)) < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+ ps_data->stk_lt_n_ct = (int) value;
+ return size;
+}
+
+static ssize_t stk_ps_ltnct_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", ps_data->stk_lt_n_ct);
+}
+
+static ssize_t stk_ps_htnct_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ unsigned long value = 0;
+ int ret;
+
+ if((ret = kstrtoul(buf, 10, &value)) < 0)
+ {
+ printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret);
+ return ret;
+ }
+ ps_data->stk_ht_n_ct = (int) value;
+ return size;
+}
+
+static ssize_t stk_ps_htnct_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+ return scnprintf(buf, PAGE_SIZE, "%d\n", ps_data->stk_ht_n_ct);
+}
+#endif /* #ifdef STK_TUNE0 */
+
+static struct device_attribute als_enable_attribute = __ATTR(enable,0666,stk_als_enable_show,stk_als_enable_store);
+static struct device_attribute als_lux_attribute = __ATTR(lux,0664,stk_als_lux_show,stk_als_lux_store);
+static struct device_attribute als_code_attribute = __ATTR(code, 0444, stk_als_code_show, NULL);
+static struct device_attribute als_transmittance_attribute = __ATTR(transmittance,0664,stk_als_transmittance_show,stk_als_transmittance_store);
+static struct device_attribute als_poll_delay_attribute = __ATTR(delay,0664,stk_als_delay_show,stk_als_delay_store);
+static struct device_attribute als_ir_code_attribute = __ATTR(ircode,0444,stk_als_ir_code_show,NULL);
+#ifdef STK_ALS_FIR
+static struct device_attribute als_firlen_attribute = __ATTR(firlen,0664,stk_als_firlen_show,stk_als_firlen_store);
+#endif
+
+static struct attribute *stk_als_attrs [] =
+{
+ &als_enable_attribute.attr,
+ &als_lux_attribute.attr,
+ &als_code_attribute.attr,
+ &als_transmittance_attribute.attr,
+ &als_poll_delay_attribute.attr,
+ &als_ir_code_attribute.attr,
+#ifdef STK_ALS_FIR
+ &als_firlen_attribute.attr,
+#endif
+ NULL
+};
+
+static struct attribute_group stk_als_attribute_group = {
+#ifndef QUALCOMM_PLATFORM
+ .name = "driver",
+#endif
+ .attrs = stk_als_attrs,
+};
+
+#ifdef STK_GES
+static struct device_attribute ges_enable_attribute = __ATTR(enable,0666,stk_ges_enable_show,stk_ges_enable_store);
+static struct device_attribute ges_code_attribute = __ATTR(code, 0664, stk_ges_code_show, stk_ges_code_store);
+static struct device_attribute ges_poll_attribute = __ATTR(poll, 0664, stk_ges_poll_show, stk_ges_poll_store);
+static struct device_attribute ges_recv_attribute = __ATTR(recv,0664,stk_recv_show,stk_recv_store);
+static struct device_attribute ges_send_attribute = __ATTR(send,0664,stk_send_show, stk_send_store);
+
+static struct attribute *stk_ges_attrs [] =
+{
+ &ges_enable_attribute.attr,
+ &ges_code_attribute.attr,
+ &ges_poll_attribute.attr,
+ &ges_recv_attribute.attr,
+ &ges_send_attribute.attr,
+ NULL
+};
+
+static struct attribute_group stk_ges_attribute_group =
+{
+ .name = "driver",
+ .attrs = stk_ges_attrs,
+};
+#endif /* #ifdef STK_GES */
+
+static struct device_attribute ps_enable_attribute = __ATTR(enable,0666,stk_ps_enable_show,stk_ps_enable_store);
+static struct device_attribute ps_enable_aso_attribute = __ATTR(enableaso,0664,stk_ps_enable_aso_show,stk_ps_enable_aso_store);
+static struct device_attribute ps_distance_attribute = __ATTR(distance,0664,stk_ps_distance_show, stk_ps_distance_store);
+static struct device_attribute ps_offset_attribute = __ATTR(offset,0664,stk_ps_offset_show, stk_ps_offset_store);
+static struct device_attribute ps_code_attribute = __ATTR(code, 0444, stk_ps_code_show, NULL);
+static struct device_attribute ps_code_thd_l_attribute = __ATTR(codethdl,0664,stk_ps_code_thd_l_show,stk_ps_code_thd_l_store);
+static struct device_attribute ps_code_thd_h_attribute = __ATTR(codethdh,0664,stk_ps_code_thd_h_show,stk_ps_code_thd_h_store);
+static struct device_attribute ps_recv_attribute = __ATTR(recv,0664,stk_recv_show,stk_recv_store);
+static struct device_attribute ps_send_attribute = __ATTR(send,0664,stk_send_show, stk_send_store);
+static struct device_attribute all_reg_attribute = __ATTR(allreg, 0444, stk_all_reg_show, NULL);
+static struct device_attribute status_attribute = __ATTR(status, 0444, stk_status_show, NULL);
+#ifdef STK_TUNE0
+static struct device_attribute ps_cali_attribute = __ATTR(cali,0444,stk_ps_cali_show, NULL);
+static struct device_attribute ps_maxdiff_attribute = __ATTR(maxdiff,0664,stk_ps_maxdiff_show, stk_ps_maxdiff_store);
+static struct device_attribute ps_ltnct_attribute = __ATTR(ltnct,0664,stk_ps_ltnct_show, stk_ps_ltnct_store);
+static struct device_attribute ps_htnct_attribute = __ATTR(htnct,0664,stk_ps_htnct_show, stk_ps_htnct_store);
+#endif
+
+static struct attribute *stk_ps_attrs [] =
+{
+ &ps_enable_attribute.attr,
+ &ps_enable_aso_attribute.attr,
+ &ps_distance_attribute.attr,
+ &ps_offset_attribute.attr,
+ &ps_code_attribute.attr,
+ &ps_code_thd_l_attribute.attr,
+ &ps_code_thd_h_attribute.attr,
+ &ps_recv_attribute.attr,
+ &ps_send_attribute.attr,
+ &all_reg_attribute.attr,
+ &status_attribute.attr,
+#ifdef STK_TUNE0
+ &ps_cali_attribute.attr,
+ &ps_maxdiff_attribute.attr,
+ &ps_ltnct_attribute.attr,
+ &ps_htnct_attribute.attr,
+#endif
+ NULL
+};
+
+static struct attribute_group stk_ps_attribute_group = {
+#ifndef QUALCOMM_PLATFORM
+ .name = "driver",
+#endif
+ .attrs = stk_ps_attrs,
+};
+
+static int stk_ps_val(struct stk3x1x_data *ps_data)
+{
+ int mode;
+ int32_t word_data, lii;
+ unsigned char value[4];
+ int ret;
+
+ ret = stk3x1x_i2c_read_data(ps_data->client, 0x20, 4, value);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s fail, ret=0x%x", __func__, ret);
+ return ret;
+ }
+ word_data = (value[0]<<8) | value[1];
+ word_data += ((value[2]<<8) | value[3]);
+
+ mode = (ps_data->psctrl_reg) & 0x3F;
+ if(mode == 0x30)
+ lii = 100;
+ else if (mode == 0x31)
+ lii = 200;
+ else if (mode == 0x32)
+ lii = 400;
+ else if (mode == 0x33)
+ lii = 800;
+ else
+ {
+ printk(KERN_ERR "%s: unsupported PS_IT(0x%x)\n", __func__, mode);
+ return -1;
+ }
+
+ if(word_data > lii)
+ {
+ printk(KERN_INFO "%s: word_data=%d, lii=%d\n", __func__, word_data, lii);
+ return 0xFFFF;
+ }
+ return 0;
+}
+
+#ifdef STK_TUNE0
+
+static int stk_ps_tune_zero_final(struct stk3x1x_data *ps_data)
+{
+ int ret;
+
+ ps_data->tune_zero_init_proc = false;
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_INT_REG, ps_data->int_reg);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_STATE_REG, 0);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+
+ if(ps_data->data_count == -1)
+ {
+ printk(KERN_INFO "%s: exceed limit\n", __func__);
+ hrtimer_cancel(&ps_data->ps_tune0_timer);
+ return 0;
+ }
+
+ ps_data->psa = ps_data->ps_stat_data[0];
+ ps_data->psi = ps_data->ps_stat_data[2];
+
+#ifdef CALI_PS_EVERY_TIME
+ ps_data->ps_high_thd_boot = ps_data->ps_stat_data[1] + ps_data->stk_ht_n_ct*3;
+ ps_data->ps_low_thd_boot = ps_data->ps_stat_data[1] + ps_data->stk_lt_n_ct*3;
+ ps_data->ps_thd_h = ps_data->ps_high_thd_boot ;
+ ps_data->ps_thd_l = ps_data->ps_low_thd_boot ;
+#else
+ ps_data->ps_thd_h = ps_data->ps_stat_data[1] + ps_data->stk_ht_n_ct;
+ ps_data->ps_thd_l = ps_data->ps_stat_data[1] + ps_data->stk_lt_n_ct;
+#endif
+ stk3x1x_set_ps_thd_h(ps_data, ps_data->ps_thd_h);
+ stk3x1x_set_ps_thd_l(ps_data, ps_data->ps_thd_l);
+ printk(KERN_INFO "%s: set HT=%d,LT=%d\n", __func__, ps_data->ps_thd_h, ps_data->ps_thd_l);
+ hrtimer_cancel(&ps_data->ps_tune0_timer);
+ return 0;
+}
+
+static int32_t stk_tune_zero_get_ps_data(struct stk3x1x_data *ps_data)
+{
+ uint32_t ps_adc;
+ int ret;
+
+ ret = stk_ps_val(ps_data);
+ if(ret == 0xFFFF)
+ {
+ ps_data->data_count = -1;
+ stk_ps_tune_zero_final(ps_data);
+ return 0;
+ }
+
+ ps_adc = stk3x1x_get_ps_reading(ps_data);
+ printk(KERN_INFO "%s: ps_adc #%d=%d\n", __func__, ps_data->data_count, ps_adc);
+ if(ps_adc < 0)
+ return ps_adc;
+
+ ps_data->ps_stat_data[1] += ps_adc;
+ if(ps_adc > ps_data->ps_stat_data[0])
+ ps_data->ps_stat_data[0] = ps_adc;
+ if(ps_adc < ps_data->ps_stat_data[2])
+ ps_data->ps_stat_data[2] = ps_adc;
+ ps_data->data_count++;
+
+ if(ps_data->data_count == 5)
+ {
+ ps_data->ps_stat_data[1] /= ps_data->data_count;
+ stk_ps_tune_zero_final(ps_data);
+ }
+
+ return 0;
+}
+static int stk_ps_tune_zero_init(struct stk3x1x_data *ps_data)
+{
+ int32_t ret = 0;
+ uint8_t w_state_reg;
+
+ ps_data->psi_set = 0;
+ ps_data->ps_stat_data[0] = 0;
+ ps_data->ps_stat_data[2] = 9999;
+ ps_data->ps_stat_data[1] = 0;
+ ps_data->data_count = 0;
+
+ ps_data->tune_zero_init_proc = true;
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_INT_REG, 0);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+
+ w_state_reg = (STK_STATE_EN_PS_MASK | STK_STATE_EN_WAIT_MASK);
+ ret = stk3x1x_i2c_smbus_write_byte_data(ps_data->client, STK_STATE_REG, w_state_reg);
+ if (ret < 0)
+ {
+ printk(KERN_ERR "%s: write i2c error\n", __func__);
+ return ret;
+ }
+ hrtimer_start(&ps_data->ps_tune0_timer, ps_data->ps_tune0_delay, HRTIMER_MODE_REL);
+
+ return 0;
+}
+static int stk_ps_tune_zero_func_fae(struct stk3x1x_data *ps_data)
+{
+ int32_t word_data;
+ int ret, diff;
+ unsigned char value[2];
+
+#ifdef CALI_PS_EVERY_TIME
+ if(!(ps_data->ps_enabled))
+#else
+ if(ps_data->psi_set || !(ps_data->ps_enabled))
+#endif
+ {
+ return 0;
+ }
+
+ ret = stk3x1x_get_flag(ps_data);
+ if(ret < 0)
+ return ret;
+ if(!(ret&STK_FLG_PSDR_MASK))
+ {
+ //printk(KERN_INFO "%s: ps data is not ready yet\n", __func__);
+ return 0;
+ }
+
+ ret = stk_ps_val(ps_data);
+ if(ret == 0)
+ {
+ ret = stk3x1x_i2c_read_data(ps_data->client, 0x11, 2, &value[0]);
+ if(ret < 0)
+ {
+ printk(KERN_ERR "%s fail, ret=0x%x", __func__, ret);
+ return ret;
+ }
+ word_data = (value[0]<<8) | value[1];
+ //printk(KERN_INFO "%s: word_data=%d\n", __func__, word_data);
+
+ if(word_data == 0)
+ {
+ //printk(KERN_ERR "%s: incorrect word data (0)\n", __func__);
+ return 0xFFFF;
+ }
+
+ if(word_data > ps_data->psa)
+ {
+ ps_data->psa = word_data;
+ printk(KERN_INFO "%s: update psa: psa=%d,psi=%d\n", __func__, ps_data->psa, ps_data->psi);
+ }
+ if(word_data < ps_data->psi)
+ {
+ ps_data->psi = word_data;
+ printk(KERN_INFO "%s: update psi: psa=%d,psi=%d\n", __func__, ps_data->psa, ps_data->psi);
+ }
+ }
+ diff = ps_data->psa - ps_data->psi;
+ if(diff > ps_data->stk_max_min_diff)
+ {
+ ps_data->psi_set = ps_data->psi;
+ ps_data->ps_thd_h = ps_data->psi + ps_data->stk_ht_n_ct;
+ ps_data->ps_thd_l = ps_data->psi + ps_data->stk_lt_n_ct;
+
+
+#ifdef CALI_PS_EVERY_TIME
+ if(ps_data->ps_thd_h > ps_data->ps_high_thd_boot)
+ {
+ ps_data->ps_high_thd_boot = ps_data->ps_thd_h;
+ ps_data->ps_low_thd_boot = ps_data->ps_thd_l;
+ printk(KERN_INFO "%s: update boot HT=%d, LT=%d\n", __func__, ps_data->ps_high_thd_boot, ps_data->ps_low_thd_boot);
+ }
+#endif
+
+ stk3x1x_set_ps_thd_h(ps_data, ps_data->ps_thd_h);
+ stk3x1x_set_ps_thd_l(ps_data, ps_data->ps_thd_l);
+#ifdef STK_DEBUG_PRINTF
+ printk(KERN_INFO "%s: FAE tune0 psa-psi(%d) > STK_DIFF found\n", __func__, diff);
+#endif
+ hrtimer_cancel(&ps_data->ps_tune0_timer);
+ }
+
+ return 0;
+}
+
+static void stk_ps_tune0_work_func(struct work_struct *work)
+{
+ struct stk3x1x_data *ps_data = container_of(work, struct stk3x1x_data, stk_ps_tune0_work);
+ if(ps_data->tune_zero_init_proc)
+ stk_tune_zero_get_ps_data(ps_data);
+ else
+ stk_ps_tune_zero_func_fae(ps_data);
+ return;
+}
+
+
+static enum hrtimer_restart stk_ps_tune0_timer_func(struct hrtimer *timer)
+{
+ struct stk3x1x_data *ps_data = container_of(timer, struct stk3x1x_data, ps_tune0_timer);
+ queue_work(ps_data->stk_ps_tune0_wq, &ps_data->stk_ps_tune0_work);
+ hrtimer_forward_now(&ps_data->ps_tune0_timer, ps_data->ps_tune0_delay);
+ return HRTIMER_RESTART;
+}
+#endif
+
+#ifdef STK_POLL_ALS
+static enum hrtimer_restart stk_als_timer_func(struct hrtimer *timer)
+{
+ struct stk3x1x_data *ps_data = container_of(timer, struct stk3x1x_data, als_timer);
+ queue_work(ps_data->stk_als_wq, &ps_data->stk_als_work);
+ hrtimer_forward_now(&ps_data->als_timer, ps_data->als_poll_delay);
+ return HRTIMER_RESTART;
+
+}
+
+static void stk_als_poll_work_func(struct work_struct *work)
+{
+ struct stk3x1x_data *ps_data = container_of(work, struct stk3x1x_data, stk_als_work);
+ int32_t reading = 0, reading_lux, flag_reg;
+#ifdef STK_IRS
+ int ret;
+#endif
+#ifdef STK_GES
+ if(ps_data->ges_enabled)
+ {
+ input_report_abs(ps_data->als_input_dev, ABS_MISC, ps_data->als_lux_last);
+ input_sync(ps_data->als_input_dev);
+#ifdef STK_DEBUG_PRINTF
+ printk(KERN_INFO "%s: ges_enabled=1, als input event %d lux\n",__func__, ps_data->als_lux_last);
+ return;
+#endif
+ }
+#endif
+
+ flag_reg = stk3x1x_get_flag(ps_data);
+ if(flag_reg < 0)
+ return;
+ if(!(flag_reg&STK_FLG_ALSDR_MASK))
+ {
+ //printk(KERN_INFO "%s: als is not ready\n", __func__);
+ return;
+ }
+
+#ifdef STK_IRS
+ ret = stk_als_ir_skip_als(ps_data);
+ if(ret == 1)
+ return;
+#endif
+
+ reading = stk3x1x_get_als_reading(ps_data);
+ if(reading < 0)
+ return;
+ // printk("%s: als_data_index=%d, als_data=%d\n", __func__, ps_data->als_data_index, reading);
+#ifdef STK_IRS
+ stk_als_ir_get_corr(ps_data, reading);
+ reading = reading * ps_data->als_correct_factor / 1000;
+#endif
+ reading_lux = stk_alscode2lux(ps_data, reading);
+ if(abs(ps_data->als_lux_last - reading_lux) >= STK_ALS_CHANGE_THD)
+ {
+ ps_data->als_lux_last = reading_lux;
+ input_report_abs(ps_data->als_input_dev, ABS_MISC, reading_lux);
+ input_sync(ps_data->als_input_dev);
+#ifdef STK_DEBUG_PRINTF
+ printk(KERN_INFO "%s: als input event %d lux\n",__func__, reading_lux);
+#endif
+ }
+
+#ifdef STK_IRS
+ stk_als_ir_run(ps_data);
+#endif
+ return;
+}
+#endif /* #ifdef STK_POLL_ALS */
+
+
+#ifdef STK_POLL_PS
+static enum hrtimer_restart stk_ps_timer_func(struct hrtimer *timer)
+{
+ struct stk3x1x_data *ps_data = container_of(timer, struct stk3x1x_data, ps_timer);
+ queue_work(ps_data->stk_ps_wq, &ps_data->stk_ps_work);
+ hrtimer_forward_now(&ps_data->ps_timer, ps_data->ps_poll_delay);
+ return HRTIMER_RESTART;
+}
+
+static void stk_ps_poll_work_func(struct work_struct *work)
+{
+ struct stk3x1x_data *ps_data = container_of(work, struct stk3x1x_data, stk_ps_work);
+ uint32_t reading;
+ int32_t near_far_state;
+ uint8_t org_flag_reg;
+#ifdef STK_GES
+ int32_t ret;
+ //uint8_t disable_flag = 0;
+ uint8_t disable_flag2 = 0, org_flag2_reg;
+ if(ps_data->ges_enabled == 2)
+ {
+ ret = stk3x1x_get_flag2(ps_data);
+ if(ret < 0)
+ goto err_i2c_rw;
+ org_flag2_reg = ret;
+
+ disable_flag2 = org_flag2_reg & (STK_FLG2_INT_GS_MASK |
+ STK_FLG2_GS10_MASK | STK_FLG2_GS01_MASK);
+ if(org_flag2_reg & STK_FLG2_GS10_MASK)
+ {
+ printk(KERN_INFO "%s: >>>>>>>>>>>>\n", __func__);
+ }
+ if(org_flag2_reg & STK_FLG2_GS01_MASK)
+ {
+ printk(KERN_INFO "%s: <<<<<<<<<<<<\n", __func__);
+ }
+ atomic_set(&ps_data->gesture2, (disable_flag2 &
+ (STK_FLG2_GS10_MASK | STK_FLG2_GS01_MASK)));
+
+ if(disable_flag2)
+ {
+ ret = stk3x1x_set_flag2(ps_data, org_flag2_reg, disable_flag2);
+ if(ret < 0)
+ goto err_i2c_rw;
+ }
+ }
+#endif
+
+ if(ps_data->ps_enabled)
+ {
+#ifdef STK_TUNE0
+ // if(!(ps_data->psi_set))
+ // return;
+#endif
+ org_flag_reg = stk3x1x_get_flag(ps_data);
+ if(org_flag_reg < 0)
+ goto err_i2c_rw;
+
+ if(!(org_flag_reg&STK_FLG_PSDR_MASK))
+ {
+ //printk(KERN_INFO "%s: ps is not ready\n", __func__);
+ return;
+ }
+
+ near_far_state = (org_flag_reg & STK_FLG_NF_MASK)?1:0;
+ reading = stk3x1x_get_ps_reading(ps_data);
+ if(ps_data->ps_distance_last != near_far_state)
+ {
+ ps_data->ps_distance_last = near_far_state;
+ input_report_abs(ps_data->ps_input_dev, ABS_DISTANCE, near_far_state);
+ input_sync(ps_data->ps_input_dev);
+ wake_lock_timeout(&ps_data->ps_wakelock, 3*HZ);
+#ifdef STK_DEBUG_PRINTF
+ printk(KERN_INFO "%s: ps input event %d cm, ps code = %d\n",__func__, near_far_state, reading);
+#endif
+ }
+ // ret = stk3x1x_set_flag(ps_data, org_flag_reg, disable_flag);
+ // if(ret < 0)
+ // goto err_i2c_rw;
+ return;
+ }
+
+err_i2c_rw:
+ msleep(30);
+ return;
+}
+#endif
+
+#if (!defined(STK_POLL_PS) || !defined(STK_POLL_ALS))
+static void stk_work_func(struct work_struct *work)
+{
+ uint32_t reading;
+#if ((STK_INT_PS_MODE != 0x03) && (STK_INT_PS_MODE != 0x02))
+ int32_t ret;
+ uint8_t disable_flag = 0;
+ int32_t org_flag_reg;
+#endif /* #if ((STK_INT_PS_MODE != 0x03) && (STK_INT_PS_MODE != 0x02)) */
+
+#ifndef CONFIG_STK_PS_ALS_USE_CHANGE_THRESHOLD
+ uint32_t nLuxIndex;
+#endif
+ struct stk3x1x_data *ps_data = container_of(work, struct stk3x1x_data, stk_work);
+ int32_t near_far_state;
+ int32_t als_comperator;
+#ifdef STK_GES
+ uint8_t disable_flag2 = 0, org_flag2_reg;
+#endif
+
+#if (STK_INT_PS_MODE == 0x03)
+ near_far_state = gpio_get_value(ps_data->int_pin);
+#elif (STK_INT_PS_MODE == 0x02)
+ near_far_state = !(gpio_get_value(ps_data->int_pin));
+#endif
+
+#if ((STK_INT_PS_MODE == 0x03) || (STK_INT_PS_MODE == 0x02))
+ ps_data->ps_distance_last = near_far_state;
+ input_report_abs(ps_data->ps_input_dev, ABS_DISTANCE, near_far_state);
+ input_sync(ps_data->ps_input_dev);
+ wake_lock_timeout(&ps_data->ps_wakelock, 3*HZ);
+ reading = stk3x1x_get_ps_reading(ps_data);
+#ifdef STK_DEBUG_PRINTF
+ printk(KERN_INFO "%s: ps input event %d cm, ps code = %d\n",__func__, near_far_state, reading);
+#endif
+#else
+ /* mode 0x01 or 0x04 */
+ org_flag_reg = stk3x1x_get_flag(ps_data);
+ if(org_flag_reg < 0)
+ goto err_i2c_rw;
+
+ if (org_flag_reg & STK_FLG_ALSINT_MASK)
+ {
+ disable_flag |= STK_FLG_ALSINT_MASK;
+ reading = stk3x1x_get_als_reading(ps_data);
+ if(reading < 0)
+ {
+ printk(KERN_ERR "%s: stk3x1x_get_als_reading fail, ret=%d", __func__, reading);
+ goto err_i2c_rw;
+ }
+#ifndef CONFIG_STK_PS_ALS_USE_CHANGE_THRESHOLD
+ nLuxIndex = stk_get_lux_interval_index(reading);
+ stk3x1x_set_als_thd_h(ps_data, code_threshold_table[nLuxIndex]);
+ stk3x1x_set_als_thd_l(ps_data, code_threshold_table[nLuxIndex-1]);
+#else
+ stk_als_set_new_thd(ps_data, reading);
+#endif //CONFIG_STK_PS_ALS_USE_CHANGE_THRESHOLD
+
+ if(ps_data->ir_code)
+ {
+ if(reading < STK_IRC_MAX_ALS_CODE && reading > STK_IRC_MIN_ALS_CODE &&
+ ps_data->ir_code > STK_IRC_MIN_IR_CODE)
+ {
+ als_comperator = reading * STK_IRC_ALS_NUMERA / STK_IRC_ALS_DENOMI;
+ if(ps_data->ir_code > als_comperator)
+ ps_data->als_correct_factor = STK_IRC_ALS_CORREC;
+ else
+ ps_data->als_correct_factor = 1000;
+ }
+ printk(KERN_INFO "%s: als=%d, ir=%d, als_correct_factor=%d", __func__, reading, ps_data->ir_code, ps_data->als_correct_factor);
+ ps_data->ir_code = 0;
+ }
+
+ reading = reading * ps_data->als_correct_factor / 1000;
+
+ ps_data->als_lux_last = stk_alscode2lux(ps_data, reading);
+ input_report_abs(ps_data->als_input_dev, ABS_MISC, ps_data->als_lux_last);
+ input_sync(ps_data->als_input_dev);
+#ifdef STK_DEBUG_PRINTF
+ printk(KERN_INFO "%s: als input event %d lux\n",__func__, ps_data->als_lux_last);
+#endif
+ }
+ if (org_flag_reg & STK_FLG_PSINT_MASK)
+ {
+ disable_flag |= STK_FLG_PSINT_MASK;
+ near_far_state = (org_flag_reg & STK_FLG_NF_MASK)?1:0;
+
+ ps_data->ps_distance_last = near_far_state;
+ input_report_abs(ps_data->ps_input_dev, ABS_DISTANCE, near_far_state);
+ input_sync(ps_data->ps_input_dev);
+ wake_lock_timeout(&ps_data->ps_wakelock, 3*HZ);
+ reading = stk3x1x_get_ps_reading(ps_data);
+#ifdef STK_DEBUG_PRINTF
+ printk(KERN_INFO "%s: ps input event=%d, ps code = %d\n",__func__, near_far_state, reading);
+#endif
+ }
+
+ if(disable_flag)
+ {
+ ret = stk3x1x_set_flag(ps_data, org_flag_reg, disable_flag);
+ if(ret < 0)
+ goto err_i2c_rw;
+ }
+#endif
+ usleep_range(1000, 2000);
+ //msleep(1);
+ enable_irq(ps_data->irq);
+ return;
+
+err_i2c_rw:
+ msleep(30);
+ enable_irq(ps_data->irq);
+ return;
+}
+
+static irqreturn_t stk_oss_irq_handler(int irq, void *data)
+{
+ struct stk3x1x_data *pData = data;
+ disable_irq_nosync(irq);
+ queue_work(pData->stk_wq,&pData->stk_work);
+ return IRQ_HANDLED;
+}
+#endif /* #if (!defined(STK_POLL_PS) || !defined(STK_POLL_ALS)) */
+
+#ifdef STK_POLL_ALS
+static void stk3x1x_als_set_poll_delay(struct stk3x1x_data *ps_data)
+{
+ uint8_t als_it = ps_data->alsctrl_reg & 0x0F;
+
+ if(als_it == 0x8)
+ {
+ ps_data->als_poll_delay = ns_to_ktime(60 * NSEC_PER_MSEC);
+ }
+ else if(als_it == 0x9)
+ {
+ ps_data->als_poll_delay = ns_to_ktime(110 * NSEC_PER_MSEC);
+ }
+ else if(als_it == 0xA)
+ {
+ ps_data->als_poll_delay = ns_to_ktime(220 * NSEC_PER_MSEC);
+ }
+ else if(als_it == 0xB)
+ {
+ ps_data->als_poll_delay = ns_to_ktime(440 * NSEC_PER_MSEC);
+ }
+ else if(als_it == 0xC)
+ {
+ ps_data->als_poll_delay = ns_to_ktime(880 * NSEC_PER_MSEC);
+ }
+ else
+ {
+ ps_data->als_poll_delay = ns_to_ktime(110 * NSEC_PER_MSEC);
+ printk(KERN_INFO "%s: unknown ALS_IT=%d, set als_poll_delay=110ms\n", __func__, als_it);
+ }
+}
+#endif
+
+static int32_t stk3x1x_init_all_setting(struct i2c_client *client, struct stk3x1x_platform_data *plat_data)
+{
+ int32_t ret;
+ struct stk3x1x_data *ps_data = i2c_get_clientdata(client);
+
+ ret = stk3x1x_software_reset(ps_data);
+ if(ret < 0)
+ return ret;
+
+ ret = stk3x1x_check_pid(ps_data);
+ if(ret < 0)
+ return ret;
+ stk3x1x_proc_plat_data(ps_data, plat_data);
+ ret = stk3x1x_init_all_reg(ps_data);
+ if(ret < 0)
+ return ret;
+#ifdef STK_POLL_ALS
+ stk3x1x_als_set_poll_delay(ps_data);
+#endif
+ ps_data->als_enabled = false;
+ ps_data->ps_enabled = false;
+ ps_data->re_enable_als = false;
+ ps_data->re_enable_ps = false;
+ ps_data->ir_code = 0;
+ ps_data->als_correct_factor = 1000;
+ ps_data->first_boot = true;
+#ifndef CONFIG_STK_PS_ALS_USE_CHANGE_THRESHOLD
+ stk_init_code_threshold_table(ps_data);
+#endif
+#ifdef STK_TUNE0
+ #ifdef QUALCOMM_PLATFORM
+ ps_data->tune_zero_init_proc = false;
+ ps_data->psi_set = 0;
+ #else
+ stk_ps_tune_zero_init(ps_data);
+ #endif
+#endif
+#ifdef STK_ALS_FIR
+ memset(&ps_data->fir, 0x00, sizeof(ps_data->fir));
+ atomic_set(&ps_data->firlength, STK_FIR_LEN);
+#endif
+ atomic_set(&ps_data->recv_reg, 0);
+#ifdef STK_GES
+ ps_data->re_enable_ges = 0;
+ atomic_set(&ps_data->gesture2, 0);
+ //memset(stk_ges_op, 0, sizeof(stk_ges_op));
+#endif
+#ifdef STK_IRS
+ ps_data->als_data_index = 0;
+#endif
+ ps_data->ps_distance_last = 1;
+ ps_data->als_code_last = 0;
+ return 0;
+}
+
+#if (!defined(STK_POLL_PS) || !defined(STK_POLL_ALS))
+static int stk3x1x_setup_irq(struct i2c_client *client)
+{
+ int irq, err = -EIO;
+ struct stk3x1x_data *ps_data = i2c_get_clientdata(client);
+
+//#ifdef SPREADTRUM_PLATFORM
+// irq = sprd_alloc_gpio_irq(ps_data->int_pin);
+//#else
+ irq = gpio_to_irq(ps_data->int_pin);
+//#endif
+#ifdef STK_DEBUG_PRINTF
+ printk(KERN_INFO "%s: int pin #=%d, irq=%d\n",__func__, ps_data->int_pin, irq);
+#endif
+ if (irq <= 0)
+ {
+ printk(KERN_ERR "irq number is not specified, irq # = %d, int pin=%d\n",irq, ps_data->int_pin);
+ return irq;
+ }
+ ps_data->irq = irq;
+ err = gpio_request(ps_data->int_pin,"stk-int");
+ if(err < 0)
+ {
+ printk(KERN_ERR "%s: gpio_request, err=%d", __func__, err);
+ return err;
+ }
+ err = gpio_direction_input(ps_data->int_pin);
+ if(err < 0)
+ {
+ printk(KERN_ERR "%s: gpio_direction_input, err=%d", __func__, err);
+ return err;
+ }
+#if ((STK_INT_PS_MODE == 0x03) || (STK_INT_PS_MODE == 0x02))
+ err = request_any_context_irq(irq, stk_oss_irq_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, DEVICE_NAME, ps_data);
+#else
+ err = request_any_context_irq(irq, stk_oss_irq_handler, IRQF_TRIGGER_LOW|IRQF_NO_SUSPEND|IRQF_ONESHOT, DEVICE_NAME, ps_data);
+#endif
+ if (err < 0)
+ {
+ printk(KERN_WARNING "%s: request_any_context_irq(%d) failed for (%d)\n", __func__, irq, err);
+ goto err_request_any_context_irq;
+ }
+ disable_irq(irq);
+
+ return 0;
+err_request_any_context_irq:
+//#ifdef SPREADTRUM_PLATFORM
+// sprd_free_gpio_irq(ps_data->int_pin);
+//#else
+ gpio_free(ps_data->int_pin);
+//#endif
+ return err;
+}
+#endif
+
+
+static int stk3x1x_suspend(struct device *dev)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+#if (defined(STK_CHK_REG) || !defined(STK_POLL_PS))
+ int err;
+#endif
+
+#ifndef STK_POLL_PS
+ struct i2c_client *client = to_i2c_client(dev);
+#endif
+
+ printk(KERN_INFO "%s", __func__);
+#ifndef SPREADTRUM_PLATFORM
+ mutex_lock(&ps_data->io_lock);
+#endif
+#ifdef STK_CHK_REG
+ err = stk3x1x_validate_n_handle(ps_data->client);
+ if(err < 0)
+ {
+ printk(KERN_ERR "stk3x1x_validate_n_handle fail: %d\n", err);
+ }
+ else if (err == 0xFF)
+ {
+ if(ps_data->ps_enabled)
+ stk3x1x_enable_ps(ps_data, 1, 0);
+ }
+#endif /* #ifdef STK_CHK_REG */
+#ifdef STK_GES
+ if(ps_data->ges_enabled == 1)
+ {
+ ps_data->re_enable_ges = ps_data->ges_enabled;
+ stk3x1x_enable_ges(ps_data, 0, 1);
+ }
+ else if(ps_data->ges_enabled == 2)
+ {
+ ps_data->re_enable_ges = ps_data->ges_enabled;
+ stk3x1x_enable_ges(ps_data, 0, 2);
+ }
+#endif
+#ifndef SPREADTRUM_PLATFORM
+ if(ps_data->als_enabled)
+ {
+ printk(KERN_INFO "%s: Enable ALS : 0\n", __func__);
+ stk3x1x_enable_als(ps_data, 0);
+ ps_data->re_enable_als = true;
+ }
+#endif
+ if(ps_data->ps_enabled)
+ {
+#ifdef STK_POLL_PS
+ wake_lock(&ps_data->ps_nosuspend_wl);
+#else
+ if(device_may_wakeup(&client->dev))
+ {
+ err = enable_irq_wake(ps_data->irq);
+ if (err)
+ printk(KERN_WARNING "%s: set_irq_wake(%d) failed, err=(%d)\n", __func__, ps_data->irq, err);
+ }
+ else
+ {
+ printk(KERN_ERR "%s: not support wakeup source", __func__);
+ }
+#endif
+ }
+#ifndef SPREADTRUM_PLATFORM
+ mutex_unlock(&ps_data->io_lock);
+#endif
+ return 0;
+}
+
+static int stk3x1x_resume(struct device *dev)
+{
+ struct stk3x1x_data *ps_data = dev_get_drvdata(dev);
+#if (defined(STK_CHK_REG) || !defined(STK_POLL_PS))
+ int err;
+#endif
+
+#ifndef STK_POLL_PS
+ struct i2c_client *client = to_i2c_client(dev);
+#endif
+ printk("%s called\n", __func__);
+ printk(KERN_INFO "%s", __func__);
+#ifndef SPREADTRUM_PLATFORM
+ mutex_lock(&ps_data->io_lock);
+#endif
+#ifdef STK_CHK_REG
+ err = stk3x1x_validate_n_handle(ps_data->client);
+ if(err < 0)
+ {
+ printk(KERN_ERR "stk3x1x_validate_n_handle fail: %d\n", err);
+ }
+ else if (err == 0xFF)
+ {
+ if(ps_data->ps_enabled)
+ stk3x1x_enable_ps(ps_data, 1, 0);
+ }
+#endif /* #ifdef STK_CHK_REG */
+#ifdef STK_GES
+ if(ps_data->re_enable_ges == 1)
+ {
+ stk3x1x_enable_ges(ps_data, 1, 1);
+ ps_data->re_enable_ges = 0;
+ }
+ else if(ps_data->re_enable_ges == 2)
+ {
+ stk3x1x_enable_ges(ps_data, 1, 2);
+ ps_data->re_enable_ges = 0;
+ }
+#endif
+#ifndef SPREADTRUM_PLATFORM
+ if(ps_data->re_enable_als)
+ {
+ printk(KERN_INFO "%s: Enable ALS : 1\n", __func__);
+ stk3x1x_enable_als(ps_data, 1);
+ ps_data->re_enable_als = false;
+ }
+#endif
+ if(ps_data->ps_enabled)
+ {
+#ifdef STK_POLL_PS
+ wake_unlock(&ps_data->ps_nosuspend_wl);
+#else
+ if(device_may_wakeup(&client->dev))
+ {
+ err = disable_irq_wake(ps_data->irq);
+ if (err)
+ printk(KERN_WARNING "%s: disable_irq_wake(%d) failed, err=(%d)\n", __func__, ps_data->irq, err);
+ }
+#endif
+ }
+#ifndef SPREADTRUM_PLATFORM
+ mutex_unlock(&ps_data->io_lock);
+#endif
+ return 0;
+}
+
+static const struct dev_pm_ops stk3x1x_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stk3x1x_suspend, stk3x1x_resume)
+};
+
+//#ifdef CONFIG_HAS_EARLYSUSPEND
+#if 0
+static void stk3x1x_early_suspend(struct early_suspend *h)
+{
+
+}
+
+static void stk3x1x_late_resume(struct early_suspend *h)
+{
+
+}
+#endif //#ifdef CONFIG_HAS_EARLYSUSPEND
+
+
+#ifdef STK_QUALCOMM_POWER_CTRL
+static int stk3x1x_power_ctl(struct stk3x1x_data *data, bool on)
+{
+ int ret = 0;
+
+ if (!on && data->power_enabled) {
+ ret = regulator_disable(data->vdd);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "Regulator vdd disable failed ret=%d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_disable(data->vio);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "Regulator vio disable failed ret=%d\n", ret);
+ regulator_enable(data->vdd);
+ return ret;
+ }
+ data->power_enabled = on;
+ printk(KERN_INFO "%s: disable stk3x1x power", __func__);
+ dev_dbg(&data->client->dev, "stk3x1x_power_ctl on=%d\n",
+ on);
+ } else if (on && !data->power_enabled) {
+ ret = regulator_enable(data->vdd);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "Regulator vdd enable failed ret=%d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(data->vio);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "Regulator vio enable failed ret=%d\n", ret);
+ regulator_disable(data->vdd);
+ return ret;
+ }
+ data->power_enabled = on;
+ printk(KERN_INFO "%s: enable stk3x1x power", __func__);
+ dev_dbg(&data->client->dev, "stk3x1x_power_ctl on=%d\n",
+ on);
+ } else {
+ dev_warn(&data->client->dev,
+ "Power on=%d. enabled=%d\n",
+ on, data->power_enabled);
+ }
+
+ return ret;
+}
+
+static int stk3x1x_power_init(struct stk3x1x_data *data, bool on)
+{
+ int ret;
+
+ if (!on) {
+ if (regulator_count_voltages(data->vdd) > 0)
+ regulator_set_voltage(data->vdd,
+ 0, STK3X1X_VDD_MAX_UV);
+
+ regulator_put(data->vdd);
+
+ if (regulator_count_voltages(data->vio) > 0)
+ regulator_set_voltage(data->vio,
+ 0, STK3X1X_VIO_MAX_UV);
+
+ regulator_put(data->vio);
+ } else {
+ data->vdd = regulator_get(&data->client->dev, "vdd");
+ if (IS_ERR(data->vdd)) {
+ ret = PTR_ERR(data->vdd);
+ dev_err(&data->client->dev,
+ "Regulator get failed vdd ret=%d\n", ret);
+ return ret;
+ }
+
+ if (regulator_count_voltages(data->vdd) > 0) {
+ ret = regulator_set_voltage(data->vdd,
+ STK3X1X_VDD_MIN_UV,
+ STK3X1X_VDD_MAX_UV);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "Regulator set failed vdd ret=%d\n",
+ ret);
+ goto reg_vdd_put;
+ }
+ }
+
+ data->vio = regulator_get(&data->client->dev, "vio");
+ if (IS_ERR(data->vio)) {
+ ret = PTR_ERR(data->vio);
+ dev_err(&data->client->dev,
+ "Regulator get failed vio ret=%d\n", ret);
+ goto reg_vdd_set;
+ }
+
+ if (regulator_count_voltages(data->vio) > 0) {
+ ret = regulator_set_voltage(data->vio,
+ STK3X1X_VIO_MIN_UV,
+ STK3X1X_VIO_MAX_UV);
+ if (ret) {
+ dev_err(&data->client->dev,
+ "Regulator set failed vio ret=%d\n", ret);
+ goto reg_vio_put;
+ }
+ }
+ }
+
+ return 0;
+
+reg_vio_put:
+ regulator_put(data->vio);
+reg_vdd_set:
+ if (regulator_count_voltages(data->vdd) > 0)
+ regulator_set_voltage(data->vdd, 0, STK3X1X_VDD_MAX_UV);
+reg_vdd_put:
+ regulator_put(data->vdd);
+ return ret;
+}
+
+static int stk3x1x_device_ctl(struct stk3x1x_data *ps_data, bool enable)
+{
+ int ret;
+ struct device *dev = &ps_data->client->dev;
+
+ if (enable && !ps_data->power_enabled) {
+ ret = stk3x1x_power_ctl(ps_data, true);
+ if (ret) {
+ dev_err(dev, "Failed to enable device power\n");
+ goto err_exit;
+ }
+ ret = stk3x1x_init_all_setting(ps_data->client, ps_data->pdata);
+ if (ret < 0) {
+ stk3x1x_power_ctl(ps_data, false);
+ dev_err(dev, "Failed to re-init device setting\n");
+ goto err_exit;
+ }
+ } else if (!enable && ps_data->power_enabled) {
+ #ifdef STK_GES
+ if (!ps_data->als_enabled && !ps_data->ps_enabled && !ps_data->ges_enabled) {
+ #else
+ if (!ps_data->als_enabled && !ps_data->ps_enabled) {
+ #endif
+ ret = stk3x1x_power_ctl(ps_data, false);
+ if (ret) {
+ dev_err(dev, "Failed to disable device power\n");
+ goto err_exit;
+ }
+ } else {
+ dev_dbg(dev, "device control: als_enabled=%d, ps_enabled=%d\n",
+ ps_data->als_enabled, ps_data->ps_enabled);
+ }
+ } else {
+ dev_dbg(dev, "device control: enable=%d, power_enabled=%d\n",
+ enable, ps_data->power_enabled);
+ }
+ return 0;
+
+err_exit:
+ return ret;
+}
+#endif /* #ifdef STK_QUALCOMM_POWER_CTRL */
+
+#ifdef CONFIG_OF
+static int stk3x1x_parse_dt(struct device *dev,
+ struct stk3x1x_platform_data *pdata)
+{
+#if 0
+ int rc;
+ struct device_node *np = dev->of_node;
+ u32 temp_val;
+
+ pdata->int_pin = of_get_named_gpio_flags(np, "stk,irq-gpio",
+ 0, &pdata->int_flags);
+ if (pdata->int_pin < 0) {
+ dev_err(dev, "Unable to read irq-gpio\n");
+ return pdata->int_pin;
+ }
+
+ rc = of_property_read_u32(np, "stk,transmittance", &temp_val);
+ if (!rc)
+ pdata->transmittance = temp_val;
+ else {
+ dev_err(dev, "Unable to read transmittance\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(np, "stk,state-reg", &temp_val);
+ if (!rc)
+ pdata->state_reg = temp_val;
+ else {
+ dev_err(dev, "Unable to read state-reg\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(np, "stk,psctrl-reg", &temp_val);
+ if (!rc)
+ pdata->psctrl_reg = (u8)temp_val;
+ else {
+ dev_err(dev, "Unable to read psctrl-reg\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(np, "stk,alsctrl-reg", &temp_val);
+ if (!rc)
+ pdata->alsctrl_reg = (u8)temp_val;
+ else {
+ dev_err(dev, "Unable to read alsctrl-reg\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(np, "stk,ledctrl-reg", &temp_val);
+ if (!rc)
+ pdata->ledctrl_reg = (u8)temp_val;
+ else {
+ dev_err(dev, "Unable to read ledctrl-reg\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(np, "stk,wait-reg", &temp_val);
+ if (!rc)
+ pdata->wait_reg = (u8)temp_val;
+ else {
+ dev_err(dev, "Unable to read wait-reg\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(np, "stk,ps-thdh", &temp_val);
+ if (!rc)
+ pdata->ps_thd_h = (u16)temp_val;
+ else {
+ dev_err(dev, "Unable to read ps-thdh\n");
+ return rc;
+ }
+
+ rc = of_property_read_u32(np, "stk,ps-thdl", &temp_val);
+ if (!rc)
+ pdata->ps_thd_l = (u16)temp_val;
+ else {
+ dev_err(dev, "Unable to read ps-thdl\n");
+ return rc;
+ }
+
+ //pdata->use_fir = of_property_read_bool(np, "stk,use-fir");
+#endif
+
+ return 0;
+}
+#else
+static int stk3x1x_parse_dt(struct device *dev,
+ struct stk3x1x_platform_data *pdata)
+{
+ return -ENODEV;
+}
+#endif /* !CONFIG_OF */
+
+static int stk3x1x_set_wq(struct stk3x1x_data *ps_data)
+{
+#ifdef STK_POLL_ALS
+ ps_data->stk_als_wq = create_singlethread_workqueue("stk_als_wq");
+ INIT_WORK(&ps_data->stk_als_work, stk_als_poll_work_func);
+ hrtimer_init(&ps_data->als_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ps_data->als_poll_delay = ns_to_ktime(110 * NSEC_PER_MSEC);
+ ps_data->als_timer.function = stk_als_timer_func;
+#endif
+
+#ifdef STK_POLL_PS
+ ps_data->stk_ps_wq = create_singlethread_workqueue("stk_ps_wq");
+ INIT_WORK(&ps_data->stk_ps_work, stk_ps_poll_work_func);
+ hrtimer_init(&ps_data->ps_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ps_data->ps_poll_delay = ns_to_ktime(60 * NSEC_PER_MSEC);
+ ps_data->ps_timer.function = stk_ps_timer_func;
+#endif
+
+#ifdef STK_TUNE0
+ ps_data->stk_ps_tune0_wq = create_singlethread_workqueue("stk_ps_tune0_wq");
+ INIT_WORK(&ps_data->stk_ps_tune0_work, stk_ps_tune0_work_func);
+ hrtimer_init(&ps_data->ps_tune0_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ps_data->ps_tune0_delay = ns_to_ktime(60 * NSEC_PER_MSEC);
+ ps_data->ps_tune0_timer.function = stk_ps_tune0_timer_func;
+#endif
+
+#if (!defined(STK_POLL_ALS) || !defined(STK_POLL_PS))
+ ps_data->stk_wq = create_singlethread_workqueue("stk_wq");
+ INIT_WORK(&ps_data->stk_work, stk_work_func);
+#endif
+ return 0;
+}
+
+
+static int stk3x1x_set_input_devices(struct stk3x1x_data *ps_data)
+{
+ int err;
+
+ ps_data->als_input_dev = input_allocate_device();
+ if (ps_data->als_input_dev==NULL)
+ {
+ printk(KERN_ERR "%s: could not allocate als device\n", __func__);
+ err = -ENOMEM;
+ return err;
+ }
+ ps_data->ps_input_dev = input_allocate_device();
+ if (ps_data->ps_input_dev==NULL)
+ {
+ printk(KERN_ERR "%s: could not allocate ps device\n", __func__);
+ err = -ENOMEM;
+ return err;
+ }
+ ps_data->als_input_dev->name = ALS_NAME;
+ ps_data->ps_input_dev->name = PS_NAME;
+ set_bit(EV_ABS, ps_data->als_input_dev->evbit);
+ set_bit(EV_ABS, ps_data->ps_input_dev->evbit);
+ input_set_abs_params(ps_data->als_input_dev, ABS_MISC, 0, stk_alscode2lux(ps_data, (1<<16)-1), 0, 0);
+ input_set_abs_params(ps_data->ps_input_dev, ABS_DISTANCE, 0,1, 0, 0);
+ err = input_register_device(ps_data->als_input_dev);
+ if (err<0)
+ {
+ printk(KERN_ERR "%s: can not register als input device\n", __func__);
+ return err;
+ }
+ err = input_register_device(ps_data->ps_input_dev);
+ if (err<0)
+ {
+ printk(KERN_ERR "%s: can not register ps input device\n", __func__);
+ return err;
+ }
+
+ err = sysfs_create_group(&ps_data->als_input_dev->dev.kobj, &stk_als_attribute_group);
+ if (err < 0)
+ {
+ printk(KERN_ERR "%s:could not create sysfs group for als\n", __func__);
+ return err;
+ }
+ err = sysfs_create_group(&ps_data->ps_input_dev->dev.kobj, &stk_ps_attribute_group);
+ if (err < 0)
+ {
+ printk(KERN_ERR "%s:could not create sysfs group for ps\n", __func__);
+ return err;
+ }
+ input_set_drvdata(ps_data->als_input_dev, ps_data);
+ input_set_drvdata(ps_data->ps_input_dev, ps_data);
+
+#ifdef STK_GES
+ ps_data->ges_input_dev = input_allocate_device();
+ if (ps_data->ges_input_dev==NULL)
+ {
+ printk(KERN_ERR "%s: could not allocate ps device\n", __func__);
+ err = -ENOMEM;
+ return err;
+ }
+ ps_data->ges_input_dev->name = "stk_ges";
+ ps_data->ges_input_dev->evbit[0] = BIT_MASK(EV_KEY);
+ set_bit(KEY_PAGEUP, ps_data->ges_input_dev->keybit);
+ set_bit(KEY_PAGEDOWN, ps_data->ges_input_dev->keybit);
+ set_bit(KEY_VOLUMEUP, ps_data->ges_input_dev->keybit);
+ set_bit(KEY_VOLUMEDOWN, ps_data->ges_input_dev->keybit);
+ /*
+ set_bit(KEY_LEFT, ps_data->ges_input_dev->keybit);
+ set_bit(KEY_RIGHT, ps_data->ges_input_dev->keybit);
+ set_bit(KEY_UP, ps_data->ges_input_dev->keybit);
+ set_bit(KEY_DOWN, ps_data->ges_input_dev->keybit);
+ */
+ err = input_register_device(ps_data->ges_input_dev);
+ if (err<0)
+ {
+ printk(KERN_ERR "%s: can not register ps input device\n", __func__);
+ return err;
+ }
+
+ err = sysfs_create_group(&ps_data->ges_input_dev->dev.kobj, &stk_ges_attribute_group);
+ if (err < 0)
+ {
+ printk(KERN_ERR "%s:could not create sysfs group for ps\n", __func__);
+ return err;
+ }
+ input_set_drvdata(ps_data->ges_input_dev, ps_data);
+#endif
+
+ return 0;
+}
+
+static int stk3x1x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err = -ENODEV;
+ struct stk3x1x_data *ps_data;
+ struct stk3x1x_platform_data *plat_data;
+ printk(KERN_INFO "%s: driver version = %s\n", __func__, DRIVER_VERSION);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ {
+ printk(KERN_ERR "%s: No Support for I2C_FUNC_I2C\n", __func__);
+ return -ENODEV;
+ }
+
+ ps_data = kzalloc(sizeof(struct stk3x1x_data),GFP_KERNEL);
+ if(!ps_data)
+ {
+ printk(KERN_ERR "%s: failed to allocate stk3x1x_data\n", __func__);
+ return -ENOMEM;
+ }
+ ps_data->client = client;
+ i2c_set_clientdata(client,ps_data);
+ mutex_init(&ps_data->io_lock);
+ wake_lock_init(&ps_data->ps_wakelock,WAKE_LOCK_SUSPEND, "stk_input_wakelock");
+
+#ifdef STK_POLL_PS
+ wake_lock_init(&ps_data->ps_nosuspend_wl,WAKE_LOCK_SUSPEND, "stk_nosuspend_wakelock");
+#endif
+
+ if (client->dev.of_node) {
+ printk(KERN_INFO "%s: probe with device tree\n", __func__);
+ plat_data = devm_kzalloc(&client->dev,
+ sizeof(struct stk3x1x_platform_data), GFP_KERNEL);
+ if (!plat_data) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ err = stk3x1x_parse_dt(&client->dev, plat_data);
+//huanggq
+ plat_data->state_reg = stk3x1x_pfdata.state_reg;
+ plat_data->psctrl_reg = stk3x1x_pfdata.psctrl_reg;
+ plat_data->alsctrl_reg = stk3x1x_pfdata.alsctrl_reg;
+ plat_data->ledctrl_reg = stk3x1x_pfdata.ledctrl_reg;
+ plat_data->wait_reg = stk3x1x_pfdata.wait_reg;
+ plat_data->ps_thd_h = stk3x1x_pfdata.ps_thd_h;
+ plat_data->ps_thd_l = stk3x1x_pfdata.ps_thd_l;
+ plat_data->int_pin = stk3x1x_pfdata.int_pin;
+ plat_data->transmittance = stk3x1x_pfdata.transmittance;
+//end
+ dev_err(&client->dev,
+ "%s: stk3x1x_parse_dt ret=%d\n", __func__, err);
+ if (err)
+ return err;
+ } else {
+ printk(KERN_INFO "%s: probe with platform data\n", __func__);
+#ifdef SPREADTRUM_PLATFORM
+ plat_data = &stk3x1x_pfdata;
+#else
+ plat_data = client->dev.platform_data;
+#endif
+ }
+ if (!plat_data) {
+ dev_err(&client->dev,
+ "%s: no stk3x1x platform data!\n", __func__);
+ goto err_als_input_allocate;
+ }
+ ps_data->als_transmittance = plat_data->transmittance;
+ ps_data->int_pin = plat_data->int_pin;
+ ps_data->pdata = plat_data;
+
+ if (ps_data->als_transmittance == 0) {
+ dev_err(&client->dev,
+ "%s: Please set als_transmittance\n", __func__);
+ goto err_als_input_allocate;
+ }
+
+ stk3x1x_set_wq(ps_data);
+#ifdef QUALCOMM_PLATFORM
+ ps_data->ps_thd_h = 0;
+ ps_data->ps_thd_l = 0;
+#endif
+ ps_data->stk_max_min_diff = STK_MAX_MIN_DIFF;
+ ps_data->stk_lt_n_ct = STK_LT_N_CT;
+ ps_data->stk_ht_n_ct = STK_HT_N_CT;
+
+#ifdef STK_QUALCOMM_POWER_CTRL
+ err = stk3x1x_power_init(ps_data, true);
+ if (err)
+ goto err_init_all_setting;
+
+ err = stk3x1x_power_ctl(ps_data, true);
+ if (err)
+ goto err_power_on;
+
+ ps_data->als_enabled = false;
+ ps_data->ps_enabled = false;
+#endif
+
+ err = stk3x1x_init_all_setting(client, plat_data);
+ if(err < 0)
+ goto err_init_all_setting;
+
+ err = stk3x1x_set_input_devices(ps_data);
+ if(err < 0)
+ goto err_setup_input_device;
+
+#if (!defined(STK_POLL_ALS) || !defined(STK_POLL_PS))
+ err = stk3x1x_setup_irq(client);
+ if(err < 0)
+ goto err_stk3x1x_setup_irq;
+#endif
+ device_init_wakeup(&client->dev, true);
+
+//#ifdef CONFIG_HAS_EARLYSUSPEND
+#if 0
+ ps_data->stk_early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ps_data->stk_early_suspend.suspend = stk3x1x_early_suspend;
+ ps_data->stk_early_suspend.resume = stk3x1x_late_resume;
+ register_early_suspend(&ps_data->stk_early_suspend);
+#endif
+
+#ifdef STK_QUALCOMM_POWER_CTRL
+ /* enable device power only when it is enabled */
+ err = stk3x1x_power_ctl(ps_data, false);
+ if (err)
+ goto err_stk3x1x_setup_irq;
+#endif
+ printk(KERN_INFO "%s: probe successfully", __func__);
+ return 0;
+
+ //device_init_wakeup(&client->dev, false);
+err_stk3x1x_setup_irq:
+#if (!defined(STK_POLL_ALS) || !defined(STK_POLL_PS))
+ free_irq(ps_data->irq, ps_data);
+ //#ifdef SPREADTRUM_PLATFORM
+ // sprd_free_gpio_irq(ps_data->int_pin);
+ //#else
+ gpio_free(ps_data->int_pin);
+ //#endif
+#endif
+err_setup_input_device:
+#ifdef STK_GES
+ sysfs_remove_group(&ps_data->ges_input_dev->dev.kobj, &stk_ges_attribute_group);
+ input_unregister_device(ps_data->ges_input_dev);
+ input_free_device(ps_data->ges_input_dev);
+#endif
+ sysfs_remove_group(&ps_data->ps_input_dev->dev.kobj, &stk_ps_attribute_group);
+ sysfs_remove_group(&ps_data->als_input_dev->dev.kobj, &stk_als_attribute_group);
+ input_unregister_device(ps_data->ps_input_dev);
+ input_unregister_device(ps_data->als_input_dev);
+ input_free_device(ps_data->ps_input_dev);
+ input_free_device(ps_data->als_input_dev);
+#ifdef STK_QUALCOMM_POWER_CTRL
+ stk3x1x_power_ctl(ps_data, false);
+err_power_on:
+ stk3x1x_power_init(ps_data, false);
+#endif
+err_init_all_setting:
+#ifdef STK_POLL_ALS
+ hrtimer_try_to_cancel(&ps_data->als_timer);
+ destroy_workqueue(ps_data->stk_als_wq);
+#endif
+#ifdef STK_TUNE0
+ destroy_workqueue(ps_data->stk_ps_tune0_wq);
+#endif
+#ifdef STK_POLL_PS
+ hrtimer_try_to_cancel(&ps_data->ps_timer);
+ destroy_workqueue(ps_data->stk_ps_wq);
+#endif
+#if (!defined(STK_POLL_ALS) || !defined(STK_POLL_PS))
+ destroy_workqueue(ps_data->stk_wq);
+#endif
+err_als_input_allocate:
+#ifdef STK_POLL_PS
+ wake_lock_destroy(&ps_data->ps_nosuspend_wl);
+#endif
+ wake_lock_destroy(&ps_data->ps_wakelock);
+ mutex_destroy(&ps_data->io_lock);
+ kfree(ps_data);
+ return err;
+}
+
+
+static int stk3x1x_remove(struct i2c_client *client)
+{
+ struct stk3x1x_data *ps_data = i2c_get_clientdata(client);
+ device_init_wakeup(&client->dev, false);
+#if (!defined(STK_POLL_ALS) || !defined(STK_POLL_PS))
+ free_irq(ps_data->irq, ps_data);
+ //#ifdef SPREADTRUM_PLATFORM
+ // sprd_free_gpio_irq(ps_data->int_pin);
+ //#else
+ gpio_free(ps_data->int_pin);
+ //#endif
+#endif /* #if (!defined(STK_POLL_ALS) || !defined(STK_POLL_PS)) */
+
+
+#ifdef STK_GES
+ sysfs_remove_group(&ps_data->ges_input_dev->dev.kobj, &stk_ges_attribute_group);
+ input_unregister_device(ps_data->ges_input_dev);
+ input_free_device(ps_data->ges_input_dev);
+#endif
+#if (!defined(STK_POLL_ALS) || !defined(STK_POLL_PS))
+ destroy_workqueue(ps_data->stk_wq);
+#endif
+ sysfs_remove_group(&ps_data->ps_input_dev->dev.kobj, &stk_ps_attribute_group);
+ sysfs_remove_group(&ps_data->als_input_dev->dev.kobj, &stk_als_attribute_group);
+ input_unregister_device(ps_data->ps_input_dev);
+ input_unregister_device(ps_data->als_input_dev);
+ input_free_device(ps_data->ps_input_dev);
+ input_free_device(ps_data->als_input_dev);
+#ifdef STK_QUALCOMM_POWER_CTRL
+ stk3x1x_power_ctl(ps_data, false);
+ stk3x1x_power_init(ps_data, false);
+#endif
+#ifdef STK_POLL_ALS
+ hrtimer_try_to_cancel(&ps_data->als_timer);
+ destroy_workqueue(ps_data->stk_als_wq);
+#endif
+#ifdef STK_TUNE0
+ destroy_workqueue(ps_data->stk_ps_tune0_wq);
+#endif
+#ifdef STK_POLL_PS
+ hrtimer_try_to_cancel(&ps_data->ps_timer);
+ destroy_workqueue(ps_data->stk_ps_wq);
+ wake_lock_destroy(&ps_data->ps_nosuspend_wl);
+#endif
+ wake_lock_destroy(&ps_data->ps_wakelock);
+ mutex_destroy(&ps_data->io_lock);
+ kfree(ps_data);
+
+ return 0;
+}
+
+static const struct i2c_device_id stk_ps_id[] =
+{
+ { "stk_ps", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, stk_ps_id);
+
+static struct of_device_id stk_match_table[] = {
+ { .compatible = "stk,stk3x1x", },
+ { },
+};
+
+static struct i2c_driver stk_ps_driver =
+{
+ .driver = {
+ .name = DEVICE_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = stk_match_table,
+#endif
+ .pm = &stk3x1x_pm_ops,
+ },
+ .probe = stk3x1x_probe,
+ .remove = stk3x1x_remove,
+ .id_table = stk_ps_id,
+};
+
+static int __init stk3x1x_init(void)
+{
+ int ret;
+
+ printk("stk3x1x_init");
+
+ ret = i2c_add_driver(&stk_ps_driver);
+ if (ret)
+ {
+ i2c_del_driver(&stk_ps_driver);
+ return ret;
+ }
+ return 0;
+}
+
+static void __exit stk3x1x_exit(void)
+{
+ i2c_del_driver(&stk_ps_driver);
+}
+
+module_init(stk3x1x_init);
+module_exit(stk3x1x_exit);
+MODULE_AUTHOR("Lex Hsieh <lex_hsieh@sensortek.com.tw>");
+MODULE_DESCRIPTION("Sensortek stk3x1x Proximity Sensor driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION); \ No newline at end of file
diff --git a/drivers/input/misc/stk3x1x.h b/drivers/input/misc/stk3x1x.h
new file mode 100644
index 00000000..2d3cc79b
--- /dev/null
+++ b/drivers/input/misc/stk3x1x.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * $Id: stk3x1x.h
+ *
+ * Copyright (C) 2012~2013 Lex Hsieh <lex_hsieh@sensortek.com.tw>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ */
+#ifndef __STK3X1X_H__
+#define __STK3X1X_H__
+
+/* platform data */
+struct stk3x1x_platform_data
+{
+ uint8_t state_reg;
+ uint8_t psctrl_reg;
+ uint8_t alsctrl_reg;
+ uint8_t ledctrl_reg;
+ uint8_t wait_reg;
+ uint16_t ps_thd_h;
+ uint16_t ps_thd_l;
+ int int_pin;
+ uint32_t transmittance;
+ uint32_t int_flags;
+};
+
+
+#endif // __STK3X1X_H__ \ No newline at end of file
diff --git a/drivers/input/misc/tmd2771_pls.c b/drivers/input/misc/tmd2771_pls.c
new file mode 100644
index 00000000..e3cc465e
--- /dev/null
+++ b/drivers/input/misc/tmd2771_pls.c
@@ -0,0 +1,1591 @@
+/*******************************************************************************
+* *
+* File Name: taos.c *
+* Description: Linux device driver for Taos ambient light and *
+* proximity sensors. *
+* Author: John Koshi *
+* History: 09/16/2009 - Initial creation *
+* 10/09/2009 - Triton version *
+* 12/21/2009 - Probe/remove mode *
+* 02/07/2010 - Add proximity *
+* *
+********************************************************************************
+* Proprietary to Taos Inc., 1001 Klein Road #300, Plano, TX 75074 *
+*******************************************************************************/
+// includes
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/timer.h>
+#include <asm/uaccess.h>
+#include <asm/errno.h>
+#include <asm/delay.h>
+#include <linux/i2c/tmd2771_pls.h>
+#include <linux/delay.h>
+//iVIZM
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <mach/gpio.h>
+#include <linux/poll.h>
+#include <linux/wakelock.h>
+#include <linux/input.h>
+#include <linux/regulator/consumer.h>
+#include <mach/regulator.h>
+
+#include <linux/miscdevice.h>
+
+// device name/id/address/counts
+#define TAOS_DEVICE_NAME "tmd2771_pls"
+#define TAOS_DEVICE_ID "tritonFN"
+#define TAOS_ID_NAME_SIZE 10
+#define TAOS_TRITON_CHIPIDVAL 0x00
+#define TAOS_TRITON_MAXREGS 32
+#define TAOS_DEVICE_ADDR1 0x29
+#define TAOS_DEVICE_ADDR2 0x39
+#define TAOS_DEVICE_ADDR3 0x49
+#define TAOS_MAX_NUM_DEVICES 3
+#define TAOS_MAX_DEVICE_REGS 32
+#define I2C_MAX_ADAPTERS 8
+
+// TRITON register offsets
+#define TAOS_TRITON_CNTRL 0x00
+#define TAOS_TRITON_ALS_TIME 0X01
+#define TAOS_TRITON_PRX_TIME 0x02
+#define TAOS_TRITON_WAIT_TIME 0x03
+#define TAOS_TRITON_ALS_MINTHRESHLO 0X04
+#define TAOS_TRITON_ALS_MINTHRESHHI 0X05
+#define TAOS_TRITON_ALS_MAXTHRESHLO 0X06
+#define TAOS_TRITON_ALS_MAXTHRESHHI 0X07
+#define TAOS_TRITON_PRX_MINTHRESHLO 0X08
+#define TAOS_TRITON_PRX_MINTHRESHHI 0X09
+#define TAOS_TRITON_PRX_MAXTHRESHLO 0X0A
+#define TAOS_TRITON_PRX_MAXTHRESHHI 0X0B
+#define TAOS_TRITON_INTERRUPT 0x0C
+#define TAOS_TRITON_PRX_CFG 0x0D
+#define TAOS_TRITON_PRX_COUNT 0x0E
+#define TAOS_TRITON_GAIN 0x0F
+#define TAOS_TRITON_REVID 0x11
+#define TAOS_TRITON_CHIPID 0x12
+#define TAOS_TRITON_STATUS 0x13
+#define TAOS_TRITON_ALS_CHAN0LO 0x14
+#define TAOS_TRITON_ALS_CHAN0HI 0x15
+#define TAOS_TRITON_ALS_CHAN1LO 0x16
+#define TAOS_TRITON_ALS_CHAN1HI 0x17
+#define TAOS_TRITON_PRX_LO 0x18
+#define TAOS_TRITON_PRX_HI 0x19
+#define TAOS_TRITON_TEST_STATUS 0x1F
+
+// Triton cmd reg masks
+#define TAOS_TRITON_CMD_REG 0X80
+#define TAOS_TRITON_CMD_AUTO 0x10 //iVIZM
+#define TAOS_TRITON_CMD_BYTE_RW 0x00
+#define TAOS_TRITON_CMD_WORD_BLK_RW 0x20
+#define TAOS_TRITON_CMD_SPL_FN 0x60
+#define TAOS_TRITON_CMD_PROX_INTCLR 0X05
+#define TAOS_TRITON_CMD_ALS_INTCLR 0X06
+#define TAOS_TRITON_CMD_PROXALS_INTCLR 0X07
+#define TAOS_TRITON_CMD_TST_REG 0X08
+#define TAOS_TRITON_CMD_USER_REG 0X09
+
+// Triton cntrl reg masks
+#define TAOS_TRITON_CNTL_PROX_INT_ENBL 0X20
+#define TAOS_TRITON_CNTL_ALS_INT_ENBL 0X10
+#define TAOS_TRITON_CNTL_WAIT_TMR_ENBL 0X08
+#define TAOS_TRITON_CNTL_PROX_DET_ENBL 0X04
+#define TAOS_TRITON_CNTL_ADC_ENBL 0x02
+#define TAOS_TRITON_CNTL_PWRON 0x01
+
+// Triton status reg masks
+#define TAOS_TRITON_STATUS_ADCVALID 0x01
+#define TAOS_TRITON_STATUS_PRXVALID 0x02
+#define TAOS_TRITON_STATUS_ADCINTR 0x10
+#define TAOS_TRITON_STATUS_PRXINTR 0x20
+
+// lux constants
+#define TAOS_MAX_LUX 10000
+#define TAOS_SCALE_MILLILUX 3
+#define TAOS_FILTER_DEPTH 3
+#define CHIP_ID 0x3d
+
+#define TAOS_INPUT_NAME "light sensor"
+// forward declarations
+static int taos_probe(struct i2c_client *clientp, const struct i2c_device_id *idp);
+static int taos_remove(struct i2c_client *client);
+static int taos_open(struct inode *inode, struct file *file);
+static int taos_release(struct inode *inode, struct file *file);
+static int taos_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+static int taos_get_lux(void);
+//static int taos_lux_filter(int raw_lux);
+static int taos_device_name(unsigned char *bufp, char **device_name);
+static int taos_prox_poll(struct taos_prox_info *prxp);
+static void taos_prox_poll_timer_func(unsigned long param);
+static void taos_prox_poll_timer_start(void);
+//iVIZM
+static int taos_als_threshold_set(void);
+static int taos_prox_threshold_set(void);
+static int taos_als_get_data(void);
+static int taos_interrupts_clear(void);
+//static unsigned int taos_poll(struct file *filp, poll_table *wait);
+//static int taos_resume(struct i2c_client *client);
+//static int taos_suspend(struct i2c_client *client,pm_message_t mesg);
+
+
+DECLARE_WAIT_QUEUE_HEAD(waitqueue_read);//iVIZM
+
+#define ALS_PROX_DEBUG //iVIZM
+
+// first device number
+static dev_t taos_dev_number;
+
+// workqueue struct
+//static struct workqueue_struct *taos_wq; //iVIZM
+
+// class structure for this device
+struct class *taos_class;
+
+// module device table
+static struct i2c_device_id taos_idtable[] = {
+ {TAOS_DEVICE_NAME, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, taos_idtable);
+
+// board and address info iVIZM
+struct i2c_board_info taos_board_info[] = {
+ {I2C_BOARD_INFO(TAOS_DEVICE_ID, TAOS_DEVICE_ADDR2),},
+};
+
+unsigned short const taos_addr_list[2] = {TAOS_DEVICE_ADDR2, I2C_CLIENT_END};//iVIZM
+
+// client and device
+struct i2c_client *my_clientp;
+struct i2c_client *bad_clientp[TAOS_MAX_NUM_DEVICES];
+static int num_bad = 0;
+static int device_found = 0;
+//iVIZM
+static char pro_buf[4]; //iVIZM
+static int mcount = 0; //iVIZM
+static char als_buf[4]; //iVIZM
+u16 status = 0;
+static int ALS_ON;
+
+// driver definition
+static struct i2c_driver taos_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = TAOS_DEVICE_NAME,
+ },
+ .id_table = taos_idtable,
+ .probe = taos_probe,
+ //.resume = taos_resume,//iVIZM
+ //.suspend = taos_suspend,//iVIZM
+ .remove = __devexit_p(taos_remove),
+};
+
+// per-device data
+struct taos_data {
+ struct i2c_client *client;
+ struct cdev cdev;
+ unsigned int addr;
+ struct input_dev *input_dev;//iVIZM
+ struct work_struct work;//iVIZM
+ struct wake_lock taos_wake_lock;//iVIZM
+ char taos_id;
+ char taos_name[TAOS_ID_NAME_SIZE];
+ struct semaphore update_lock;
+ char valid;
+ //int working;
+ unsigned long last_updated;
+ struct regulator *reg_vdd;
+} *taos_datap;
+
+// file operations
+static struct file_operations taos_fops = {
+ .owner = THIS_MODULE,
+ .open = taos_open,
+ .release = taos_release,
+ .unlocked_ioctl = taos_ioctl,
+};
+
+// device configuration
+struct taos_cfg *taos_cfgp;
+
+#if 1 //zwj add
+static struct file_operations taos_fops_2 = {
+ .owner = THIS_MODULE,
+ .open = taos_open,
+ .release = taos_release,
+ .unlocked_ioctl = taos_ioctl,
+};
+
+static struct miscdevice taos_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = TAOS_DEVICE_NAME,
+ .fops = &taos_fops_2,
+};
+#endif
+
+
+static u32 calibrate_target_param = 300000;
+static u16 als_time_param = 200;
+static u16 scale_factor_param = 1;
+static u16 gain_trim_param = 512;
+static u8 filter_history_param = 3;
+static u8 filter_count_param = 1;
+static u8 gain_param = 2;
+static u16 prox_threshold_hi_param = 560; //120;
+static u16 prox_threshold_lo_param = 500; //80;
+static u16 als_threshold_hi_param = 3000;//iVIZM
+static u16 als_threshold_lo_param = 10;//iVIZM
+static u8 prox_int_time_param = 0xEE;//50ms
+static u8 prox_adc_time_param = 0xFF;
+static u8 prox_wait_time_param = 0xEE;
+static u8 prox_intr_filter_param = 0x23;
+static u8 prox_config_param = 0x00;
+static u8 prox_pulse_cnt_param = 0x04; //0x01;
+static u8 prox_gain_param = 0xA2; //0x22;
+
+// prox info
+struct taos_prox_info prox_cal_info[20];
+struct taos_prox_info prox_cur_info;
+struct taos_prox_info *prox_cur_infop = &prox_cur_info;
+static u8 prox_history_hi = 0;
+static u8 prox_history_lo = 0;
+static struct timer_list prox_poll_timer;
+static int prox_on = 0;
+static int device_released = 0;
+static u16 sat_als = 0;
+static u16 sat_prox = 0;
+
+// device reg init values
+u8 taos_triton_reg_init[16] = {0x00,0xFF,0XFF,0XFF,0X00,0X00,0XFF,0XFF,0X00,0X00,0XFF,0XFF,0X00,0X00,0X00,0X00};
+
+// lux time scale
+struct time_scale_factor {
+ u16 numerator;
+ u16 denominator;
+ u16 saturation;
+};
+struct time_scale_factor TritonTime = {1, 0, 0};
+struct time_scale_factor *lux_timep = &TritonTime;
+
+// gain table
+u8 taos_triton_gain_table[] = {1, 8, 16, 120};
+
+// lux data
+
+struct lux_data {
+ u16 ratio;
+ u16 clear;
+ u16 ir;
+
+};
+struct lux_data TritonFN_lux_data[] = {
+ { 9830, 8320, 15360 },
+ { 12452, 10554, 22797 },
+ { 14746, 6234, 11430 },
+ { 17695, 3968, 6400 },
+ { 0, 0, 0 }
+};
+struct lux_data *lux_tablep = TritonFN_lux_data;
+static int lux_history[TAOS_FILTER_DEPTH] = {-ENODATA, -ENODATA, -ENODATA};//iVIZM
+
+static int tmd2711_pls_irq;
+
+
+
+static irqreturn_t taos_irq_handler(int irq, void *dev_id) //iVIZM
+{
+ //disable_irq_nosync(tmd2711_pls_irq);
+ //queue_work(taos_wq,&taos_datap->work);
+ //printk("^^^^^^^^^^^^^^^^^^ taos_irq_handler ^^^^^^^^^^^^^^^^^^^^\n");
+ schedule_work(&taos_datap->work);
+ //enable_irq(tmd2711_pls_irq);
+
+ return IRQ_HANDLED;
+}
+
+static int taos_get_data(void)//iVIZM
+{
+ int ret = 0;
+
+ //printk("^^^^^^^^^^^^^^^^^^ taos_get_data ^^^^^^^^^^^^^^^^^^^^\n");
+
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | 0x13)))) < 0) {
+ //printk(KERN_ERR "TAOS: i2c_smbus_write_byte(1) failed in taos_work_func()\n");
+ return (ret);
+ }
+ status = i2c_smbus_read_byte(taos_datap->client);
+ // if (down_interruptible(&taos_datap->update_lock))
+ // return -ERESTARTSYS;
+ printk(" kl# %s, line=%d, status=%x\n", __func__, __LINE__, status);
+ // status : 5:4 = pint:aint //It has a little error here
+ // 1:1 = pint:aint
+ // 1:0 = pint
+ // 0:1 = aint
+ if((status & 0x20) == 0x20) { // ps
+ ret = taos_prox_threshold_set();
+ } else if((status & 0x10) == 0x10) { // as
+ taos_als_threshold_set();
+ taos_als_get_data();
+ }
+ //up(&taos_datap->update_lock);
+ return ret;
+}
+
+static int taos_interrupts_clear(void)//iVIZM
+{
+ int ret = 0;
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG|TAOS_TRITON_CMD_SPL_FN|0x07)))) < 0) {
+ //printk(KERN_ERR "TAOS: i2c_smbus_write_byte(2) failed in taos_work_func()\n");
+ return (ret);
+ }
+ return ret;
+}
+static void taos_work_func(struct work_struct * work) //iVIZM
+{
+ wake_lock(&taos_datap->taos_wake_lock);
+ printk("^^^^^^^^^^^^^^^^^^ taos_work_func ^^^^^^^^^^^^^^^^^^^^\n");
+ taos_get_data();
+ taos_interrupts_clear();
+ wake_unlock(&taos_datap->taos_wake_lock);
+}
+
+static int taos_als_get_data(void)//iVIZM
+{
+ int ret = 0;
+ u8 reg_val;
+ int lux_val = 0;
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ //printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_data\n");
+ return (ret);
+ }
+ reg_val = i2c_smbus_read_byte(taos_datap->client);
+ if ((reg_val & (TAOS_TRITON_CNTL_ADC_ENBL | TAOS_TRITON_CNTL_PWRON)) != (TAOS_TRITON_CNTL_ADC_ENBL | TAOS_TRITON_CNTL_PWRON))
+ return -ENODATA;
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_STATUS)))) < 0) {
+ //printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_data\n");
+ return (ret);
+ }
+ reg_val = i2c_smbus_read_byte(taos_datap->client);
+ if ((reg_val & TAOS_TRITON_STATUS_ADCVALID) != TAOS_TRITON_STATUS_ADCVALID)
+ return -ENODATA;
+ if ((lux_val = taos_get_lux()) < 0)
+ printk(KERN_ERR "TAOS: call to taos_get_lux() returned error %d in ioctl als_data\n", lux_val);
+ printk("********** before taos_als_get_data ********* lux_val = %d \n",lux_val);
+ input_report_abs(taos_datap->input_dev,ABS_MISC,lux_val);
+ input_sync(taos_datap->input_dev); // zwj add test
+ return ret;
+}
+
+
+static int taos_als_threshold_set(void)//iVIZM
+{
+ int i,ret = 0;
+ u8 chdata[2];
+ u16 ch0;
+
+ printk("kl# %s, line=%d \n", __func__, __LINE__);
+
+ for (i = 0; i < 2; i++) {
+ chdata[i] = (i2c_smbus_read_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CMD_WORD_BLK_RW | (TAOS_TRITON_ALS_CHAN0LO + i))));
+ }
+ ch0 = chdata[0] + chdata[1]*256;
+ als_threshold_hi_param = (12*ch0)/10;
+ if (als_threshold_hi_param >= 65535)
+ als_threshold_hi_param = 65535;
+ als_threshold_lo_param = (8*ch0)/10;
+ als_buf[0] = als_threshold_lo_param & 0x0ff;
+ als_buf[1] = als_threshold_lo_param >> 8;
+ als_buf[2] = als_threshold_hi_param & 0x0ff;
+ als_buf[3] = als_threshold_hi_param >> 8;
+
+ for( mcount=0; mcount<4; mcount++ ) {
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x04) + mcount, als_buf[mcount]))) < 0) {
+ //printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in taos als threshold set\n");
+ return (ret);
+ }
+ }
+ return ret;
+}
+
+static int taos_prox_threshold_set(void)//iVIZM
+{
+ int i,ret = 0;
+ u8 chdata[6];
+ u16 proxdata = 0;
+ u16 cleardata = 0;
+ int data = 0;
+
+ // int ch0,ch1;
+// printk("kl# %s, line=%d \n", __func__, __LINE__);
+
+ for (i = 0; i < 6; i++) {
+ chdata[i] = (i2c_smbus_read_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CMD_WORD_BLK_RW| (TAOS_TRITON_ALS_CHAN0LO + i))));
+ }
+ cleardata = chdata[0] + chdata[1]*256;
+ proxdata = chdata[4] + chdata[5]*256;
+ printk("kl## = proxdata = %d, cleardata=%d\n",proxdata, cleardata);
+
+ //ch0= i2c_smbus_read_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | 0x18 ));
+ //ch1= i2c_smbus_read_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | 0x19 ));
+
+ //printk("================= proxdatatest = %d\n",ch1<<8 |ch0);
+
+ if(prox_on || (proxdata < (taos_cfgp->prox_threshold_lo )))
+ {
+ pro_buf[0] = 0x0;
+ pro_buf[1] = 0x0;
+ pro_buf[2] = taos_cfgp->prox_threshold_hi & 0x0ff;
+ pro_buf[3] = taos_cfgp->prox_threshold_hi >> 8;
+ data = 1;
+ printk(KERN_ERR "report pls data 1\n");
+ input_report_abs(taos_datap->input_dev,ABS_DISTANCE,data);
+ input_sync(taos_datap->input_dev);
+ }
+ else if(proxdata > (taos_cfgp->prox_threshold_hi) )
+ {
+ if (cleardata > ((sat_als*80)/100)){
+ data = 0;
+ }
+ else{
+ pro_buf[0] = taos_cfgp->prox_threshold_lo & 0x0ff;
+ pro_buf[1] = taos_cfgp->prox_threshold_lo >> 8;
+ pro_buf[2] = 0xff;
+ pro_buf[3] = 0xff;
+ data = 0;
+ }
+ input_report_abs(taos_datap->input_dev,ABS_DISTANCE,data);
+
+ input_sync(taos_datap->input_dev);
+ }
+
+ // printk("prox_threshold_hi=%d, prox_threshold_lo= %d\n",pro_buf[3]<<8 |pro_buf[2], pro_buf[1]<<8 |pro_buf[0]);
+
+ for( mcount=0; mcount<4; mcount++ ) {
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x08) + mcount, pro_buf[mcount]))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in taos prox threshold set\n");
+ return (ret);
+ }
+ }
+
+ prox_on = 0;
+ return ret;
+}
+
+static int taos_reg_init(struct i2c_client *clientp)
+{
+ int ret = 0;
+ int i = 0;
+ unsigned char buf[TAOS_MAX_DEVICE_REGS];
+ char *device_name;
+ int chip_id;
+
+ for (i = 0; i < TAOS_MAX_DEVICE_REGS; i++) {
+ if ((ret = (i2c_smbus_write_byte(clientp, (TAOS_TRITON_CMD_REG | (TAOS_TRITON_CNTRL + i))))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte() to control reg failed in taos_probe()\n");
+ return(ret);
+ }
+ buf[i] = i2c_smbus_read_byte(clientp);
+ }
+ if ((ret = taos_device_name(buf, &device_name)) == 0) {
+ printk(KERN_ERR "TAOS: chip id that was read found mismatched by taos_device_name(), in taos_probe()\n");
+ return -ENODEV;
+ }
+ if (strcmp(device_name, TAOS_DEVICE_ID)) {
+ printk(KERN_ERR "TAOS: chip id that was read does not match expected id in taos_probe()\n");
+ return -ENODEV;
+ }
+ else {
+ printk(KERN_ERR "TAOS: chip id of %s that was read matches expected id in taos_probe()\n", device_name);
+ device_found = 1;
+ }
+ if ((ret = (i2c_smbus_write_byte(clientp, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte() to control reg failed in taos_probe()\n");
+ return(ret);
+ }
+ strlcpy(clientp->name, TAOS_DEVICE_ID, I2C_NAME_SIZE);
+ strlcpy(taos_datap->taos_name, TAOS_DEVICE_ID, TAOS_ID_NAME_SIZE);
+ taos_datap->valid = 0;
+ if (!(taos_cfgp = kmalloc(sizeof(struct taos_cfg), GFP_KERNEL))) {
+ printk(KERN_ERR "TAOS: kmalloc for struct taos_cfg failed in taos_probe()\n");
+ return -ENOMEM;
+ }
+ taos_cfgp->calibrate_target = calibrate_target_param;
+ taos_cfgp->als_time = als_time_param;
+ taos_cfgp->scale_factor = scale_factor_param;
+ taos_cfgp->gain_trim = gain_trim_param;
+ taos_cfgp->filter_history = filter_history_param;
+ taos_cfgp->filter_count = filter_count_param;
+ taos_cfgp->gain = gain_param;
+ taos_cfgp->als_threshold_hi = als_threshold_hi_param;//iVIZM
+ taos_cfgp->als_threshold_lo = als_threshold_lo_param;//iVIZM
+ taos_cfgp->prox_threshold_hi = prox_threshold_hi_param;
+ taos_cfgp->prox_threshold_lo = prox_threshold_lo_param;
+ taos_cfgp->prox_int_time = prox_int_time_param;
+ taos_cfgp->prox_adc_time = prox_adc_time_param;
+ taos_cfgp->prox_wait_time = prox_wait_time_param;
+ taos_cfgp->prox_intr_filter = prox_intr_filter_param;
+ taos_cfgp->prox_config = prox_config_param;
+ taos_cfgp->prox_pulse_cnt = prox_pulse_cnt_param;
+ taos_cfgp->prox_gain = prox_gain_param;
+ sat_als = (256 - taos_cfgp->prox_int_time) << 10;
+ sat_prox = (256 - taos_cfgp->prox_adc_time) << 10;
+
+ /*dmobile ::power down for init ,Rambo liu*/
+ printk("Rambo::light sensor will pwr down \n");
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x00), 0x00))) < 0) {
+ printk(KERN_ERR "TAOS:Rambo, i2c_smbus_write_byte_data failed in power down\n");
+ return (ret);
+ }
+}
+
+static void tmd2771_pls_pininit(int irq_pin)
+{
+ printk(KERN_INFO "%s [irq=%d]\n",__func__,irq_pin);
+ gpio_request(irq_pin, "tmd2771_pls irq");
+ gpio_direction_input(irq_pin);
+}
+
+
+// client probe
+static int taos_probe(struct i2c_client *clientp, const struct i2c_device_id *idp) {
+ int ret = 0;
+ int i = 0;
+ int chip_id;
+ int ch0,ch1,ch2,ch3,ch4,ch5;
+ struct regulator *reg_vdd;
+ //unsigned char buf[TAOS_MAX_DEVICE_REGS];
+ //char *device_name;
+ struct i2c_adapter *adapter = to_i2c_adapter(clientp->dev.parent);
+ struct tmd2771_pls_platform_data *pdata = clientp->dev.platform_data;
+
+ printk("kl# tmd2771 probe!!!!line=%d\n", __LINE__);
+
+ //reg_vdd = regulator_get(NULL,REGU_NAMES_VDD28);
+ //regulator_enable(reg_vdd);
+
+ if (!i2c_check_functionality(clientp->adapter, I2C_FUNC_I2C)) {
+ printk("%s: functionality check failed\n", __func__);
+ ret = -ENODEV;
+ goto exit_check_functionality_failed;
+ }
+
+ taos_datap = kmalloc(sizeof(struct taos_data), GFP_KERNEL);
+ if (!taos_datap) {
+ printk(KERN_ERR "TAOS: kmalloc for struct taos_data failed in taos_init()\n");
+ //return -ENOMEM;
+ ret = -ENOMEM;
+ goto exit_request_memory_failed;
+ }
+ memset(taos_datap, 0, sizeof(struct taos_data));
+ taos_datap->reg_vdd = reg_vdd;
+
+ // get chip id
+ chip_id = i2c_smbus_read_byte_data(clientp, (TAOS_TRITON_CMD_REG | (TAOS_TRITON_CNTRL + 0x12))); //iVIZM
+ printk(" toas1 chip_id = %d\n",chip_id);
+ if(chip_id < 0) {
+ ret = chip_id;
+ goto exit_request_memory_failed;
+ }
+
+ tmd2771_pls_pininit(pdata->irq_gpio_number);
+
+
+ /*get irq*/
+ clientp->irq = gpio_to_irq(pdata->irq_gpio_number);
+
+ tmd2711_pls_irq = clientp->irq;
+
+
+ taos_datap->client = clientp;
+ i2c_set_clientdata(clientp, taos_datap);
+ INIT_WORK(&(taos_datap->work),taos_work_func);
+ mutex_init(&taos_datap->update_lock);
+
+ // input devices
+ taos_datap->input_dev = input_allocate_device();//iVIZM
+ if (taos_datap->input_dev == NULL) {
+ ret = -ENOMEM;
+ goto exit_input_dev_allocate_failed;
+ }
+
+ taos_datap->input_dev->name = TAOS_INPUT_NAME;
+ taos_datap->input_dev->id.bustype = BUS_I2C;
+ set_bit(EV_ABS,taos_datap->input_dev->evbit);
+ set_bit(ABS_MISC, taos_datap->input_dev->absbit);
+ set_bit(ABS_DISTANCE, taos_datap->input_dev->absbit);
+ /*for proximity*/
+ input_set_abs_params(taos_datap->input_dev, ABS_DISTANCE, 0, 1, 0, 0);
+ /*for lightsensor*/
+ input_set_abs_params(taos_datap->input_dev, ABS_MISC, 0, 100001, 0, 0);
+
+ ret = input_register_device(taos_datap->input_dev);
+ if (ret < 0) {
+ printk("%s: input device regist failed\n", __func__);
+ goto exit_input_register_failed;
+ }
+ // chip init
+ if(taos_reg_init(clientp)<0)
+ {
+ printk("%s: device init failed\n", __func__);
+ ret=-1;
+ goto exit_device_init_failed;
+ }
+
+ // create char device
+ if ((ret = (alloc_chrdev_region(&taos_dev_number, 0, TAOS_MAX_NUM_DEVICES, TAOS_DEVICE_NAME))) < 0) {
+ printk(KERN_ERR "TAOS: alloc_chrdev_region() failed in taos_init()\n");
+ goto exit_chrdev_region_failed;
+ }
+ taos_class = class_create(THIS_MODULE, TAOS_DEVICE_NAME);
+ cdev_init(&taos_datap->cdev, &taos_fops);
+ taos_datap->cdev.owner = THIS_MODULE;
+ if ((ret = (cdev_add(&taos_datap->cdev, taos_dev_number, 1))) < 0) {
+ printk(KERN_ERR "TAOS: cdev_add() failed in taos_init()\n");
+ goto exit_chrdev_add_failed;
+ }
+ wake_lock_init(&taos_datap->taos_wake_lock, WAKE_LOCK_SUSPEND, "taos-wake-lock");
+ device_create(taos_class, NULL, MKDEV(MAJOR(taos_dev_number), 0), &taos_driver ,"taos");
+
+
+
+ // interrupt
+ ret = request_irq(tmd2711_pls_irq,taos_irq_handler, IRQ_TYPE_EDGE_FALLING, "taos_irq",taos_datap);//iVIZM
+ if (ret != 0) {
+ goto irq_request_err;
+ }
+ disable_irq(tmd2711_pls_irq);//iVIZM
+ for(i = 0; i < 50; i++)
+ taos_als_get_data();
+
+ printk("%s probe success!\n", __func__);
+ return (ret);
+
+irq_request_err:
+ device_destroy(taos_class, MKDEV(MAJOR(taos_dev_number), 0));
+ gpio_free(pdata->irq_gpio_number);
+exit_chrdev_add_failed:
+ cdev_del(&taos_datap->cdev);
+ class_destroy(taos_class);
+exit_chrdev_region_failed:
+ unregister_chrdev_region(taos_dev_number, TAOS_MAX_NUM_DEVICES);
+exit_device_init_failed:
+exit_input_register_failed:
+ input_free_device(taos_datap->input_dev);
+exit_input_dev_allocate_failed:
+exit_device_register_failed:
+ kfree(taos_datap);
+exit_request_memory_failed:
+exit_check_functionality_failed:
+ //wake_lock_destroy(&pls_delayed_work_wake_lock);
+ printk("kl %s: Probe Fail!\n",__func__);
+ return ret;
+}
+# if 0
+//resume iVIZM
+static int taos_resume(struct i2c_client *client) {
+ u8 reg_val = 0,reg_cntrl = 0;
+ int ret = -1;
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in taos_resume\n");
+ return (ret);
+ }
+ reg_val = i2c_smbus_read_byte(taos_datap->client);
+ if ( taos_datap->working == 1) {
+ taos_datap->working = 0;
+ reg_cntrl = reg_val | TAOS_TRITON_CNTL_PWRON;
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL), reg_cntrl))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl als_off\n");
+ return (ret);
+ }
+ }
+ enable_irq(tmd2711_pls_irq);
+ return ret;
+}
+
+//suspend iVIZM
+static int taos_suspend(struct i2c_client *client, pm_message_t mesg) {
+ u8 reg_val = 0,reg_cntrl = 0;
+ int ret = -1;
+ disable_irq(tmd2711_pls_irq);
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in taos_resume\n");
+ return (ret);
+ }
+ reg_val = i2c_smbus_read_byte(taos_datap->client);
+ if (reg_val & TAOS_TRITON_CNTL_PWRON) {
+ taos_datap->working = 1;
+ reg_cntrl = reg_val & (~TAOS_TRITON_CNTL_PWRON);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL), reg_cntrl))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in taos_suspend\n");
+ return (ret);
+ }
+ }
+ return ret;
+}
+#endif
+// client remove
+static int __devexit taos_remove(struct i2c_client *client) {
+ int ret = 0;
+
+#if 1 // zwj mv from taos_exit
+ unregister_chrdev_region(taos_dev_number, TAOS_MAX_NUM_DEVICES);
+ device_destroy(taos_class, MKDEV(MAJOR(taos_dev_number), 0));
+ cdev_del(&taos_datap->cdev);
+ class_destroy(taos_class);
+ disable_irq(tmd2711_pls_irq);//iVIZM
+ //if (taos_wq)
+ // destroy_workqueue(taos_wq);//iVIZM
+ kfree(taos_datap);
+#endif
+
+ return (ret);
+}
+
+// open
+static int taos_open(struct inode *inode, struct file *file) {
+ struct taos_data *taos_datap;
+ int ret = 0;
+
+ device_released = 0;
+ taos_datap = container_of(inode->i_cdev, struct taos_data, cdev);
+ if (strcmp(taos_datap->taos_name, TAOS_DEVICE_ID) != 0) {
+ printk(KERN_ERR "TAOS: device name incorrect during taos_open(), get %s\n", taos_datap->taos_name);
+ ret = -ENODEV;
+ }
+
+ enable_irq(tmd2711_pls_irq);//iVIZM
+ return (ret);
+}
+
+// release
+static int taos_release(struct inode *inode, struct file *file) {
+ struct taos_data *taos_datap;
+ int ret = 0;
+
+ device_released = 1;
+ prox_on = 0;
+ prox_history_hi = 0;
+ prox_history_lo = 0;
+ taos_datap = container_of(inode->i_cdev, struct taos_data, cdev);
+ if (strcmp(taos_datap->taos_name, TAOS_DEVICE_ID) != 0) {
+ printk(KERN_ERR "TAOS: device name incorrect during taos_release(), get %s\n", taos_datap->taos_name);
+ ret = -ENODEV;
+ }
+ disable_irq(tmd2711_pls_irq);
+ return (ret);
+}
+
+static int taos_sensors_als_on(void) {
+ int ret = 0, i = 0;
+ u8 itime = 0, reg_val = 0, reg_cntrl = 0;
+ //int lux_val = 0, ret = 0, i = 0, tmp = 0;
+
+// printk("kl# %s, line=%d \n", __func__, __LINE__);
+
+ for (i = 0; i < TAOS_FILTER_DEPTH; i++)
+ lux_history[i] = -ENODATA;
+ //taos_als_threshold_set();//iVIZM
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG|TAOS_TRITON_CMD_SPL_FN|TAOS_TRITON_CMD_ALS_INTCLR)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_on\n");
+ return (ret);
+ }
+ itime = (((taos_cfgp->als_time/50) * 18) - 1);
+ itime = (~itime);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_ALS_TIME), itime))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl als_on\n");
+ return (ret);
+ }
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|TAOS_TRITON_INTERRUPT), taos_cfgp->prox_intr_filter))) < 0) {//golden
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl als_on\n");
+ return (ret);
+ }
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_GAIN)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_on\n");
+ return (ret);
+ }
+ reg_val = i2c_smbus_read_byte(taos_datap->client);
+ reg_val = reg_val & 0xFC;
+ reg_val = reg_val | (taos_cfgp->gain & 0x03);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_GAIN), reg_val))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl als_on\n");
+ return (ret);
+ }
+ //if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ // printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_calibrate\n");
+ // return (ret);
+ //}
+ //reg_val = i2c_smbus_read_byte(taos_datap->client);
+ //reg_cntrl = reg_val | (TAOS_TRITON_CNTL_ADC_ENBL | TAOS_TRITON_CNTL_PWRON | TAOS_TRITON_CNTL_ALS_INT_ENBL);
+ reg_cntrl = (TAOS_TRITON_CNTL_ADC_ENBL | TAOS_TRITON_CNTL_PWRON | TAOS_TRITON_CNTL_ALS_INT_ENBL);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL), reg_cntrl))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl als_on\n");
+ return (ret);
+ }
+
+ mdelay(100);
+
+ taos_als_threshold_set();//iVIZM
+ return ret;
+}
+
+// ioctls
+//static int taos_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) {
+static int taos_ioctl(struct file *file, unsigned int cmd, unsigned long arg) {
+ int prox_sum = 0, prox_mean = 0, prox_max = 0;
+ int lux_val = 0, ret = 0, i = 0, tmp = 0;
+ u16 gain_trim_val = 0;
+ u8 reg_val = 0, reg_cntrl = 0;
+ int ret_check=0;
+ int ret_m=0;
+ u8 reg_val_temp=0;
+
+ printk("k#### %s, line=%d \n", __func__, __LINE__);
+
+ switch (cmd) {
+ case TAOS_IOCTL_SENSOR_CHECK:
+ reg_val_temp=0;
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_CHECK failed\n");
+ return (ret);
+ }
+ reg_val_temp = i2c_smbus_read_byte(taos_datap->client);
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_CHECK,prox_adc_time,%d~\n",reg_val_temp);
+ if ((reg_val_temp & 0xFF) == 0xF)
+ return -ENODATA;
+
+ break;
+
+ case TAOS_IOCTL_SENSOR_CONFIG:
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_CONFIG,test01~\n");
+ ret = copy_from_user(taos_cfgp, (struct taos_cfg *)arg, sizeof(struct taos_cfg));
+ if (ret) {
+ printk(KERN_ERR "TAOS: copy_from_user failed in ioctl config_set\n");
+ return -ENODATA;
+ }
+
+ break;
+
+ case TAOS_IOCTL_SENSOR_ON:
+ ret=0;
+ reg_val=0;
+ ret_m=0;
+
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, test01~\n");
+
+#if 0
+ /*decide the numbers of sensors*/
+ ret_m=taos_sensor_mark();
+ if(ret_m <0){
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, error~~\n");
+ return -1;
+ }
+ if(ret_m ==0){
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, num >0 ~~\n");
+ return 1;
+ }
+#endif
+#if 1
+ /*Register init and turn off */
+ for (i = 0; i < TAOS_FILTER_DEPTH; i++){
+ /*Rambo ??*/
+ lux_history[i] = -ENODATA;
+ }
+#endif
+
+#if 0
+ for (i = 0; i < sizeof(taos_triton_reg_init); i++){
+ if(i !=11){
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|(TAOS_TRITON_CNTRL +i)), taos_triton_reg_init[i]))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl als_on\n");
+ return (ret);
+ }
+ }
+ }
+#endif
+ /*ALS interrupt clear*/
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG|TAOS_TRITON_CMD_SPL_FN|TAOS_TRITON_CMD_ALS_INTCLR)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_on\n");
+ return (ret);
+ }
+
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, test02~\n");
+ /*Register setting*/
+#if 0
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x00), 0x00))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+#endif
+
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, test03,prox_int_time,%d~\n",taos_cfgp->prox_int_time);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|TAOS_TRITON_ALS_TIME), taos_cfgp->prox_int_time))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, test04,prox_adc_time,%d~\n",taos_cfgp->prox_adc_time);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|TAOS_TRITON_PRX_TIME), taos_cfgp->prox_adc_time))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, test05,prox_wait_time,%d~\n", taos_cfgp->prox_wait_time);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|TAOS_TRITON_WAIT_TIME), taos_cfgp->prox_wait_time))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, test05-1,prox_intr_filter,%d~\n", taos_cfgp->prox_intr_filter);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|TAOS_TRITON_INTERRUPT), taos_cfgp->prox_intr_filter))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, test06,prox_config,%d~\n",taos_cfgp->prox_config);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|TAOS_TRITON_PRX_CFG), taos_cfgp->prox_config))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, test06,prox_pulse_cnt,%d~\n",taos_cfgp->prox_pulse_cnt);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|TAOS_TRITON_PRX_COUNT), taos_cfgp->prox_pulse_cnt))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+ /*gain*/
+#if 0
+ reg_val_temp=0;
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_GAIN)))) < 0) {
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON failed in ioctl als_calibrate\n");
+ return (ret);
+ }
+ reg_val_temp = i2c_smbus_read_byte(taos_datap->client);
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, test07-1,gain_init&0xC,%d~\n",reg_val_temp&0xC);
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, test07-2,prox_gain&0xFC,%d~\n",(taos_cfgp->prox_gain&0xFC));
+ reg_val_temp=(taos_cfgp->prox_gain&0xFC)|(reg_val_temp&0xC);
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, test07,%d~\n",reg_val_temp);
+#endif
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|TAOS_TRITON_GAIN), taos_cfgp->prox_gain))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+
+ /*turn on*/
+#if 0
+ reg_val_temp=0;
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON failed in ioctl als_calibrate\n");
+ return (ret);
+ }
+ reg_val_temp = i2c_smbus_read_byte(taos_datap->client);
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, test08-1,%d~\n",reg_val_temp);
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, test08-2,%d~\n",(reg_val_temp&0x40));
+ reg_val_temp=(0xF&0x3F)|(reg_val_temp&0x40);
+
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, test08,%d~\n",reg_val_temp);
+#endif
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|TAOS_TRITON_CNTRL), 0xF))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+
+ break;
+
+
+ case TAOS_IOCTL_SENSOR_OFF:
+ ret=0;
+ reg_val=0;
+ ret_check=0;
+ ret_m=0;
+
+#if 0
+ /*chech the num of used sensor*/
+ ret_check=taos_sensor_check();
+
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_OFF,test01~\n");
+ if(ret_check <0){
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_OFF, error~~\n");
+ return -1;
+ }
+ if(ret_check ==0){
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_ON, num >1 ~~\n");
+ return 1;
+ }
+#endif
+ /*turn off*/
+ printk(KERN_ERR "TAOS: TAOS_IOCTL_SENSOR_OFF,test02~\n");
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x00), 0x00))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_off\n");
+ return (ret);
+ }
+
+ break;
+ case TAOS_IOCTL_ALS_ON:
+ printk("####################################\n");
+ printk("######## TAOS IOCTL ALS ON #########\n");
+ printk("####################################\n");
+
+
+ printk("taos==== TAOS IOCTL ALS ON ===00==ALS_ON=%d prox_on=%d\n",ALS_ON,prox_on);
+
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_calibrate\n");
+ return (ret);
+ }
+ reg_val = i2c_smbus_read_byte(taos_datap->client);
+
+ if ((reg_val & TAOS_TRITON_CNTL_PROX_DET_ENBL) == 0x0) {
+ printk("taos==== TAOS_IOCTL_ALS_ON ===TAOS_IOCTL_PROX_OFF\n");
+ taos_sensors_als_on();
+ }
+
+ /* for (i = 0; i < TAOS_FILTER_DEPTH; i++)
+ lux_history[i] = -ENODATA;
+ //taos_als_threshold_set();//iVIZM
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG|TAOS_TRITON_CMD_SPL_FN|TAOS_TRITON_CMD_ALS_INTCLR)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_on\n");
+ return (ret);
+ }
+ itime = (((taos_cfgp->als_time/50) * 18) - 1);
+ itime = (~itime);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_ALS_TIME), itime))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl als_on\n");
+ return (ret);
+ }
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|TAOS_TRITON_INTERRUPT), taos_cfgp->prox_intr_filter))) < 0) {//golden
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl als_on\n");
+ return (ret);
+ }
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_GAIN)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_on\n");
+ return (ret);
+ }
+ reg_val = i2c_smbus_read_byte(taos_datap->client);
+ reg_val = reg_val & 0xFC;
+ reg_val = reg_val | (taos_cfgp->gain & 0x03);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_GAIN), reg_val))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl als_on\n");
+ return (ret);
+ }
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_calibrate\n");
+ return (ret);
+ }
+ reg_val = i2c_smbus_read_byte(taos_datap->client);
+ reg_cntrl = reg_val | (TAOS_TRITON_CNTL_ADC_ENBL | TAOS_TRITON_CNTL_PWRON | TAOS_TRITON_CNTL_ALS_INT_ENBL);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL), reg_cntrl))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl als_on\n");
+ return (ret);
+ }
+ taos_als_threshold_set();//iVIZM
+ */
+ ALS_ON = 1;
+ printk("taos==== TAOS IOCTL ALS ON ===11==ALS_ON=%d prox_on=%d\n",ALS_ON,prox_on);
+ return (ret);
+ break;
+ case TAOS_IOCTL_ALS_OFF:
+ printk("#####################################\n");
+ printk("######## TAOS IOCTL ALS OFF #########\n");
+ printk("#####################################\n");
+
+ printk("taos==== TAOS IOCTL ALS OFF ===00==ALS_ON=%d prox_on=%d\n",ALS_ON,prox_on);
+
+ for (i = 0; i < TAOS_FILTER_DEPTH; i++)
+ lux_history[i] = -ENODATA;
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_calibrate\n");
+ return (ret);
+ }
+ reg_val = i2c_smbus_read_byte(taos_datap->client);
+ if ((reg_val & TAOS_TRITON_CNTL_PROX_DET_ENBL) == 0x0) {
+
+ printk("taos==== TAOS_IOCTL_ALS_OFF ===TAOS_IOCTL_PROX_OFF\n");
+
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL), 0x00))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl als_off\n");
+ return (ret);
+ }
+ cancel_work_sync(&taos_datap->work);//golden
+ // reg_cntrl = reg_val & (~TAOS_TRITON_CNTL_ALS_INT_ENBL);
+ // } else {
+ // reg_cntrl = reg_val & (~(TAOS_TRITON_CNTL_ADC_ENBL | TAOS_TRITON_CNTL_PWRON | TAOS_TRITON_CNTL_ALS_INT_ENBL));
+ // printk("===========cancel_work_sync======als====\n");
+ // cancel_work_sync(&taos_datap->work);//golden
+ // }
+ // if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL), reg_cntrl))) < 0) {
+ // printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl als_off\n");
+ // return (ret);
+ }
+ ALS_ON = 0;
+ printk("taos==== TAOS IOCTL ALS OFF ===11==ALS_ON=%d prox_on=%d\n",ALS_ON,prox_on);
+ return (ret);
+ break;
+ case TAOS_IOCTL_ALS_DATA:
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_data\n");
+ return (ret);
+ }
+ reg_val = i2c_smbus_read_byte(taos_datap->client);
+ if ((reg_val & (TAOS_TRITON_CNTL_ADC_ENBL | TAOS_TRITON_CNTL_PWRON)) != (TAOS_TRITON_CNTL_ADC_ENBL | TAOS_TRITON_CNTL_PWRON))
+ return -ENODATA;
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_STATUS)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_data\n");
+ return (ret);
+ }
+ reg_val = i2c_smbus_read_byte(taos_datap->client);
+ if ((reg_val & TAOS_TRITON_STATUS_ADCVALID) != TAOS_TRITON_STATUS_ADCVALID)
+ return -ENODATA;
+ if ((lux_val = taos_get_lux()) < 0)
+ printk(KERN_ERR "TAOS: call to taos_get_lux() returned error %d in ioctl als_data\n", lux_val);
+ //lux_val = taos_lux_filter(lux_val);
+ return (lux_val);
+ break;
+ case TAOS_IOCTL_ALS_CALIBRATE:
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_calibrate\n");
+ return (ret);
+ }
+ reg_val = i2c_smbus_read_byte(taos_datap->client);
+ if ((reg_val & 0x07) != 0x07)
+ return -ENODATA;
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_STATUS)))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_calibrate\n");
+
+ return (ret);
+ }
+ reg_val = i2c_smbus_read_byte(taos_datap->client);
+ if ((reg_val & 0x01) != 0x01)
+ return -ENODATA;
+ if ((lux_val = taos_get_lux()) < 0) {
+ printk(KERN_ERR "TAOS: call to lux_val() returned error %d in ioctl als_data\n", lux_val);
+ return (lux_val);
+ }
+ gain_trim_val = (u16)(((taos_cfgp->calibrate_target) * 512)/lux_val);
+ taos_cfgp->gain_trim = (int)gain_trim_val;
+ return ((int)gain_trim_val);
+ break;
+ case TAOS_IOCTL_CONFIG_GET:
+ ret = copy_to_user((struct taos_cfg *)arg, taos_cfgp, sizeof(struct taos_cfg));
+ if (ret) {
+ printk(KERN_ERR "TAOS: copy_to_user failed in ioctl config_get\n");
+ return -ENODATA;
+ }
+ return (ret);
+ break;
+ case TAOS_IOCTL_CONFIG_SET:
+ printk("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
+ printk("^^^^^^^^^ TAOS INCTL CONFIG SET ^^^^^^^\n");
+ printk("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
+ ret = copy_from_user(taos_cfgp, (struct taos_cfg *)arg, sizeof(struct taos_cfg));
+ if (ret) {
+ printk(KERN_ERR "TAOS: copy_from_user failed in ioctl config_set\n");
+ return -ENODATA;
+ }
+ if(taos_cfgp->als_time < 50)
+ taos_cfgp->als_time = 50;
+ if(taos_cfgp->als_time > 650)
+ taos_cfgp->als_time = 650;
+ tmp = (taos_cfgp->als_time + 25)/50;
+ taos_cfgp->als_time = tmp*50;
+ sat_als = (256 - taos_cfgp->prox_int_time) << 10;
+ sat_prox = (256 - taos_cfgp->prox_adc_time) << 10;
+ break;
+ case TAOS_IOCTL_PROX_ON:
+ printk("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
+ printk("^^^^^^^^^ TAOS IOCTL PROX ON ^^^^^^^\n");
+ printk("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
+
+ printk("taos==== TAOS_IOCTL_PROX_ON ===00==ALS_ON=%d prox_on=%d\n",ALS_ON,prox_on);
+
+ prox_on = 1;
+
+
+ reg_cntrl = TAOS_TRITON_CNTL_PROX_DET_ENBL | TAOS_TRITON_CNTL_PWRON | TAOS_TRITON_CNTL_PROX_INT_ENBL |
+ TAOS_TRITON_CNTL_ADC_ENBL | TAOS_TRITON_CNTL_WAIT_TMR_ENBL ;
+
+ printk("taos==== TAOS_IOCTL_PROX_ON ===00==reg_cntrl=%x\n",reg_cntrl);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL), reg_cntrl))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x01), taos_cfgp->prox_int_time))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x02), taos_cfgp->prox_adc_time))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x03), taos_cfgp->prox_wait_time))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x0C), taos_cfgp->prox_intr_filter))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x0D), taos_cfgp->prox_config))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x0E), taos_cfgp->prox_pulse_cnt))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x0F), taos_cfgp->prox_gain))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+ //if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ // printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl prox_on\n");
+ // return (ret);
+ //}
+ //reg_val = i2c_smbus_read_byte(taos_datap->client);
+ //reg_cntrl = reg_val | (TAOS_TRITON_CNTL_PROX_DET_ENBL | TAOS_TRITON_CNTL_PWRON | TAOS_TRITON_CNTL_PROX_INT_ENBL | TAOS_TRITON_CNTL_ADC_ENBL);
+
+ mdelay(100);
+
+ taos_prox_threshold_set();//iVIZM
+
+ printk("taos==== TAOS_IOCTL_PROX_ON ===11==ALS_ON=%d prox_on=%d\n",ALS_ON,prox_on);
+ break;
+ case TAOS_IOCTL_PROX_OFF:
+ printk("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
+ printk("^^^^^^^^^ TAOS IOCTL PROX OFF ^^^^^^^\n");
+ printk("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n");
+
+
+ printk("taos==== TAOS_IOCTL_PROX_OFF ===00==ALS_ON=%d prox_on=%d\n",ALS_ON,prox_on);
+ //if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ // printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl als_calibrate\n");
+ // return (ret);
+ //}
+ //reg_val = i2c_smbus_read_byte(taos_datap->client);
+ //if (reg_val & TAOS_TRITON_CNTL_ALS_INT_ENBL) {
+ // reg_cntrl = reg_val & (~(TAOS_TRITON_CNTL_PROX_DET_ENBL | TAOS_TRITON_CNTL_PROX_INT_ENBL));
+ //} else {
+ // reg_cntrl = reg_val & (~(TAOS_TRITON_CNTL_PROX_DET_ENBL | TAOS_TRITON_CNTL_PWRON | TAOS_TRITON_CNTL_PROX_INT_ENBL));
+ //reg_cntrl = reg_val & (~(TAOS_TRITON_CNTL_PROX_DET_ENBL | TAOS_TRITON_CNTL_PWRON | TAOS_TRITON_CNTL_PROX_INT_ENBL));
+ // printk("===========cancel_work_sync=====prox=====\n");
+ // cancel_work_sync(&taos_datap->work);//golden
+ //}
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL), 0x00))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl als_off\n");
+ return (ret);
+ }
+ if (ALS_ON == 1) {
+ taos_sensors_als_on();
+ } else {
+ cancel_work_sync(&taos_datap->work);//golden
+ }
+ prox_on = 0;
+ printk("taos==== TAOS_IOCTL_PROX_OFF ===11==ALS_ON=%d prox_on=%d\n",ALS_ON,prox_on);
+ break;
+ case TAOS_IOCTL_PROX_DATA:
+ //Rambo
+#if 1
+ if ((ret = taos_prox_poll(prox_cur_infop)) < 0) {
+ printk(KERN_ERR "TAOS: call to prox_poll failed in taos_prox_poll_timer_func()\n");
+ return ret;
+ }
+#endif
+ ret = copy_to_user((struct taos_prox_info *)arg, prox_cur_infop, sizeof(struct taos_prox_info));
+ if (ret) {
+ printk(KERN_ERR "TAOS: copy_to_user failed in ioctl prox_data\n");
+ return -ENODATA;
+ }
+ return (ret);
+ break;
+ case TAOS_IOCTL_PROX_EVENT:
+ if ((ret = taos_prox_poll(prox_cur_infop)) < 0) {
+ printk(KERN_ERR "TAOS: call to prox_poll failed in taos_prox_poll_timer_func()\n");
+ return ret;
+ }
+
+ return (prox_cur_infop->prox_event);
+ break;
+ case TAOS_IOCTL_PROX_CALIBRATE:
+ //if (prox_on) golden
+ //del_timer(&prox_poll_timer);
+ //else {
+ //printk(KERN_ERR "TAOS: ioctl prox_calibrate was called before ioctl prox_on was called\n");
+ //return -EPERM;
+ //}
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x01), taos_cfgp->prox_int_time))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x02), taos_cfgp->prox_adc_time))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x03), taos_cfgp->prox_wait_time))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x0D), taos_cfgp->prox_config))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x0E), taos_cfgp->prox_pulse_cnt))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|0x0F), taos_cfgp->prox_gain))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+ //if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL)))) < 0) {
+ // printk(KERN_ERR "TAOS: i2c_smbus_write_byte failed in ioctl prox_on\n");
+ // return (ret);
+ //}
+ //reg_val = i2c_smbus_read_byte(taos_datap->client);
+ reg_cntrl = reg_val | (TAOS_TRITON_CNTL_PROX_DET_ENBL | TAOS_TRITON_CNTL_PWRON | TAOS_TRITON_CNTL_ADC_ENBL);
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CNTRL), reg_cntrl))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl prox_on\n");
+ return (ret);
+ }
+
+ prox_sum = 0;
+ prox_max = 0;
+ for (i = 0; i < 20; i++) {
+ if ((ret = taos_prox_poll(&prox_cal_info[i])) < 0) {
+ printk(KERN_ERR "TAOS: call to prox_poll failed in ioctl prox_calibrate\n");
+ return (ret);
+ }
+ prox_sum += prox_cal_info[i].prox_data;
+ if (prox_cal_info[i].prox_data > prox_max)
+ prox_max = prox_cal_info[i].prox_data;
+ mdelay(100);
+ }
+ prox_mean = prox_sum/20;
+ taos_cfgp->prox_threshold_hi = ((((prox_max - prox_mean) * 200) + 50)/100) + prox_mean;
+ taos_cfgp->prox_threshold_lo = ((((prox_max - prox_mean) * 170) + 50)/100) + prox_mean;
+ printk("TAOS:------------ taos_cfgp->prox_threshold_hi = %d\n",taos_cfgp->prox_threshold_hi );
+ printk("TAOS:------------ taos_cfgp->prox_threshold_lo = %d\n",taos_cfgp->prox_threshold_lo );
+ // if (taos_cfgp->prox_threshold_lo < ((sat_prox*12)/100)) {
+ // taos_cfgp->prox_threshold_lo = ((sat_prox*12)/100);
+ // taos_cfgp->prox_threshold_hi = ((sat_prox*15)/100);
+ // }
+ for (i = 0; i < sizeof(taos_triton_reg_init); i++){
+ if(i !=11){
+ if ((ret = (i2c_smbus_write_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG|(TAOS_TRITON_CNTRL +i)), taos_triton_reg_init[i]))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte_data failed in ioctl als_on\n");
+ return (ret);
+ }
+ }
+ }
+
+ //taos_prox_poll_timer_start();//Rambo
+ break;
+ default:
+ return -EINVAL;
+ break;
+ }
+ return (ret);
+}
+
+// read/calculate lux value
+static int taos_get_lux(void) {
+ u16 raw_clear = 0, raw_ir = 0, raw_lux = 0;
+ u32 lux = 0;
+ u32 ratio = 0;
+ u8 dev_gain = 0;
+ u16 Tint = 0;
+ struct lux_data *p;
+ int ret = 0;
+ u8 chdata[4];
+ int tmp = 0, i = 0;
+
+ for (i = 0; i < 4; i++) {
+ if ((ret = (i2c_smbus_write_byte(taos_datap->client, (TAOS_TRITON_CMD_REG | (TAOS_TRITON_ALS_CHAN0LO + i))))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_smbus_write_byte() to chan0/1/lo/hi reg failed in taos_get_lux()\n");
+ return (ret);
+ }
+ chdata[i] = i2c_smbus_read_byte(taos_datap->client);
+ printk("ch(%d),data=%d\n",i,chdata[i]);
+ }
+ printk("ch0=%d\n",chdata[0]+chdata[1]*256);
+ printk("ch1=%d\n",chdata[2]+chdata[3]*256);
+
+ tmp = (taos_cfgp->als_time + 25)/50; //if atime =100 tmp = (atime+25)/50=2.5 tine = 2.7*(256-atime)= 412.5
+ TritonTime.numerator = 1;
+ TritonTime.denominator = tmp;
+
+ tmp = 300 * taos_cfgp->als_time; //tmp = 300*atime 400
+ if(tmp > 65535)
+ tmp = 65535;
+ TritonTime.saturation = tmp;
+ raw_clear = chdata[1];
+ raw_clear <<= 8;
+ raw_clear |= chdata[0];
+ raw_ir = chdata[3];
+ raw_ir <<= 8;
+ raw_ir |= chdata[2];
+
+ raw_clear *= (taos_cfgp->scale_factor *11 );
+ raw_ir *= (taos_cfgp->scale_factor *3 );
+
+ if(raw_ir > raw_clear) {
+ raw_lux = raw_ir;
+ raw_ir = raw_clear;
+ raw_clear = raw_lux;
+ }
+ dev_gain = taos_triton_gain_table[taos_cfgp->gain & 0x3];
+ if(raw_clear >= lux_timep->saturation)
+ return(TAOS_MAX_LUX);
+ if(raw_ir >= lux_timep->saturation)
+ return(TAOS_MAX_LUX);
+ if(raw_clear == 0)
+ return(0);
+ if(dev_gain == 0 || dev_gain > 127) {
+ printk(KERN_ERR "TAOS: dev_gain = 0 or > 127 in taos_get_lux()\n");
+ return -1;
+ }
+ if(lux_timep->denominator == 0) {
+ printk(KERN_ERR "TAOS: lux_timep->denominator = 0 in taos_get_lux()\n");
+ return -1;
+ }
+ ratio = (raw_ir<<15)/raw_clear;
+ for (p = lux_tablep; p->ratio && p->ratio < ratio; p++);
+ if(!p->ratio) {//iVIZM
+ if(lux_history[0] < 0)
+ return 0;
+ else
+ return lux_history[0];
+ }
+ Tint = taos_cfgp->als_time;
+ raw_clear = ((raw_clear*400 + (dev_gain>>1))/dev_gain + (Tint>>1))/Tint;
+ raw_ir = ((raw_ir*400 +(dev_gain>>1))/dev_gain + (Tint>>1))/Tint;
+ lux = ((raw_clear*(p->clear)) - (raw_ir*(p->ir)));
+ lux = (lux + 32768)/65536;
+ if(lux > TAOS_MAX_LUX) {
+ lux = TAOS_MAX_LUX;
+ }
+ //return(lux)*taos_cfgp->filter_count;
+ return(lux);
+}
+
+/*static int taos_lux_filter(int lux)
+{
+ static u8 middle[] = {1,0,2,0,0,2,0,1};
+ int index;
+
+ lux_history[2] = lux_history[1];
+ lux_history[1] = lux_history[0];
+ lux_history[0] = lux;
+
+ if(lux_history[2] < 0) { //iVIZM
+ if(lux_history[1] > 0)
+ return lux_history[1];
+ else
+ return lux_history[0];
+ }
+ index = 0;
+ if( lux_history[0] > lux_history[1] )
+ index += 4;
+ if( lux_history[1] > lux_history[2] )
+ index += 2;
+ if( lux_history[0] > lux_history[2] )
+ index++;
+ return(lux_history[middle[index]]);
+}*/
+
+
+// verify device
+static int taos_device_name(unsigned char *bufp, char **device_name) {
+ if( (bufp[0x12]&0x00)!=0x00)
+ return(0);
+ //if(bufp[0x10]|bufp[0x1a]|bufp[0x1b]|bufp[0x1c]|bufp[0x1d]|bufp[0x1e])
+ // return(0);
+ if(bufp[0x13]&0x0c)
+ return(0);
+ *device_name="tritonFN";
+ return(1);
+}
+
+// proximity poll
+static int taos_prox_poll(struct taos_prox_info *prxp) {
+ //static int event = 0;
+ //u16 status = 0;
+ int i = 0, ret = 0; //wait_count = 0;
+ u8 chdata[6];
+
+ for (i = 0; i < 6; i++) {
+ chdata[i] = (i2c_smbus_read_byte_data(taos_datap->client, (TAOS_TRITON_CMD_REG | TAOS_TRITON_CMD_AUTO | (TAOS_TRITON_ALS_CHAN0LO + i))));
+ }
+ prxp->prox_clear = chdata[1];
+ prxp->prox_clear <<= 8;
+ prxp->prox_clear |= chdata[0];
+ if (prxp->prox_clear > ((sat_als*80)/100))
+ return -ENODATA;
+ prxp->prox_data = chdata[5];
+ prxp->prox_data <<= 8;
+ prxp->prox_data |= chdata[4];
+
+ return (ret);
+}
+
+// prox poll timer function
+static void taos_prox_poll_timer_func(unsigned long param) {
+ int ret = 0;
+
+ if (!device_released) {
+ if ((ret = taos_prox_poll(prox_cur_infop)) < 0) {
+ printk(KERN_ERR "TAOS: call to prox_poll failed in taos_prox_poll_timer_func()\n");
+ return;
+ }
+ taos_prox_poll_timer_start();
+ }
+ return;
+}
+
+// start prox poll timer
+static void taos_prox_poll_timer_start(void) {
+ init_timer(&prox_poll_timer);
+ prox_poll_timer.expires = jiffies + (HZ/10);
+ prox_poll_timer.function = taos_prox_poll_timer_func;
+ add_timer(&prox_poll_timer);
+ return;
+}
+
+// driver init
+static int __init taos_init(void) {
+ int ret = 0, i = 0, k = 0;
+ //struct i2c_adapter *my_adap;
+
+ //tmd2711_pls_config_pins();
+ //LDO_TurnOnLDO(LDO_LDO_VDD28);
+ //printk(KERN_ERR "TAOS:init()\n");
+
+ if ((ret = (i2c_add_driver(&taos_driver))) < 0) {
+ printk(KERN_ERR "TAOS: i2c_add_driver() failed in taos_init()\n");
+ return (ret);
+ }
+ //printk(KERN_ERR "TAOS:end(ret [%d])\n ",ret);
+ return (ret);
+}
+
+// driver exit
+static void __exit taos_exit(void) {
+ i2c_del_driver(&taos_driver);
+}
+
+MODULE_AUTHOR("John Koshi - Surya Software");
+MODULE_DESCRIPTION("TAOS ambient light and proximity sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(taos_init);
+module_exit(taos_exit);
+
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
new file mode 100644
index 00000000..b9a05fda
--- /dev/null
+++ b/drivers/input/misc/twl4030-pwrbutton.c
@@ -0,0 +1,125 @@
+/**
+ * twl4030-pwrbutton.c - TWL4030 Power Button Input Driver
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Written by Peter De Schrijver <peter.de-schrijver@nokia.com>
+ * Several fixes by Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl.h>
+
+#define PWR_PWRON_IRQ (1 << 0)
+
+#define STS_HW_CONDITIONS 0xf
+
+static irqreturn_t powerbutton_irq(int irq, void *_pwr)
+{
+ struct input_dev *pwr = _pwr;
+ int err;
+ u8 value;
+
+ err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &value, STS_HW_CONDITIONS);
+ if (!err) {
+ pm_wakeup_event(pwr->dev.parent, 0);
+ input_report_key(pwr, KEY_POWER, value & PWR_PWRON_IRQ);
+ input_sync(pwr);
+ } else {
+ dev_err(pwr->dev.parent, "twl4030: i2c error %d while reading"
+ " TWL4030 PM_MASTER STS_HW_CONDITIONS register\n", err);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __init twl4030_pwrbutton_probe(struct platform_device *pdev)
+{
+ struct input_dev *pwr;
+ int irq = platform_get_irq(pdev, 0);
+ int err;
+
+ pwr = input_allocate_device();
+ if (!pwr) {
+ dev_dbg(&pdev->dev, "Can't allocate power button\n");
+ return -ENOMEM;
+ }
+
+ pwr->evbit[0] = BIT_MASK(EV_KEY);
+ pwr->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+ pwr->name = "twl4030_pwrbutton";
+ pwr->phys = "twl4030_pwrbutton/input0";
+ pwr->dev.parent = &pdev->dev;
+
+ err = request_threaded_irq(irq, NULL, powerbutton_irq,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "twl4030_pwrbutton", pwr);
+ if (err < 0) {
+ dev_dbg(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err);
+ goto free_input_dev;
+ }
+
+ err = input_register_device(pwr);
+ if (err) {
+ dev_dbg(&pdev->dev, "Can't register power button: %d\n", err);
+ goto free_irq;
+ }
+
+ platform_set_drvdata(pdev, pwr);
+
+ return 0;
+
+free_irq:
+ free_irq(irq, pwr);
+free_input_dev:
+ input_free_device(pwr);
+ return err;
+}
+
+static int __exit twl4030_pwrbutton_remove(struct platform_device *pdev)
+{
+ struct input_dev *pwr = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ free_irq(irq, pwr);
+ input_unregister_device(pwr);
+
+ return 0;
+}
+
+static struct platform_driver twl4030_pwrbutton_driver = {
+ .remove = __exit_p(twl4030_pwrbutton_remove),
+ .driver = {
+ .name = "twl4030_pwrbutton",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver_probe(twl4030_pwrbutton_driver,
+ twl4030_pwrbutton_probe);
+
+MODULE_ALIAS("platform:twl4030_pwrbutton");
+MODULE_DESCRIPTION("Triton2 Power Button");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter De Schrijver <peter.de-schrijver@nokia.com>");
+MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");
+
diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c
new file mode 100644
index 00000000..68a5f331
--- /dev/null
+++ b/drivers/input/misc/twl4030-vibra.c
@@ -0,0 +1,263 @@
+/*
+ * twl4030-vibra.c - TWL4030 Vibrator driver
+ *
+ * Copyright (C) 2008-2010 Nokia Corporation
+ *
+ * Written by Henrik Saari <henrik.saari@nokia.com>
+ * Updates by Felipe Balbi <felipe.balbi@nokia.com>
+ * Input by Jari Vanhala <ext-jari.vanhala@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/workqueue.h>
+#include <linux/i2c/twl.h>
+#include <linux/mfd/twl4030-audio.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+/* MODULE ID2 */
+#define LEDEN 0x00
+
+/* ForceFeedback */
+#define EFFECT_DIR_180_DEG 0x8000 /* range is 0 - 0xFFFF */
+
+struct vibra_info {
+ struct device *dev;
+ struct input_dev *input_dev;
+
+ struct work_struct play_work;
+
+ bool enabled;
+ int speed;
+ int direction;
+
+ bool coexist;
+};
+
+static void vibra_disable_leds(void)
+{
+ u8 reg;
+
+ /* Disable LEDA & LEDB, cannot be used with vibra (PWM) */
+ twl_i2c_read_u8(TWL4030_MODULE_LED, &reg, LEDEN);
+ reg &= ~0x03;
+ twl_i2c_write_u8(TWL4030_MODULE_LED, LEDEN, reg);
+}
+
+/* Powers H-Bridge and enables audio clk */
+static void vibra_enable(struct vibra_info *info)
+{
+ u8 reg;
+
+ twl4030_audio_enable_resource(TWL4030_AUDIO_RES_POWER);
+
+ /* turn H-Bridge on */
+ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
+ &reg, TWL4030_REG_VIBRA_CTL);
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ (reg | TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
+
+ twl4030_audio_enable_resource(TWL4030_AUDIO_RES_APLL);
+
+ info->enabled = true;
+}
+
+static void vibra_disable(struct vibra_info *info)
+{
+ u8 reg;
+
+ /* Power down H-Bridge */
+ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
+ &reg, TWL4030_REG_VIBRA_CTL);
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ (reg & ~TWL4030_VIBRA_EN), TWL4030_REG_VIBRA_CTL);
+
+ twl4030_audio_disable_resource(TWL4030_AUDIO_RES_APLL);
+ twl4030_audio_disable_resource(TWL4030_AUDIO_RES_POWER);
+
+ info->enabled = false;
+}
+
+static void vibra_play_work(struct work_struct *work)
+{
+ struct vibra_info *info = container_of(work,
+ struct vibra_info, play_work);
+ int dir;
+ int pwm;
+ u8 reg;
+
+ dir = info->direction;
+ pwm = info->speed;
+
+ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
+ &reg, TWL4030_REG_VIBRA_CTL);
+ if (pwm && (!info->coexist || !(reg & TWL4030_VIBRA_SEL))) {
+
+ if (!info->enabled)
+ vibra_enable(info);
+
+ /* set vibra rotation direction */
+ twl_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE,
+ &reg, TWL4030_REG_VIBRA_CTL);
+ reg = (dir) ? (reg | TWL4030_VIBRA_DIR) :
+ (reg & ~TWL4030_VIBRA_DIR);
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ reg, TWL4030_REG_VIBRA_CTL);
+
+ /* set PWM, 1 = max, 255 = min */
+ twl_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
+ 256 - pwm, TWL4030_REG_VIBRA_SET);
+ } else {
+ if (info->enabled)
+ vibra_disable(info);
+ }
+}
+
+/*** Input/ForceFeedback ***/
+
+static int vibra_play(struct input_dev *input, void *data,
+ struct ff_effect *effect)
+{
+ struct vibra_info *info = input_get_drvdata(input);
+
+ info->speed = effect->u.rumble.strong_magnitude >> 8;
+ if (!info->speed)
+ info->speed = effect->u.rumble.weak_magnitude >> 9;
+ info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1;
+ schedule_work(&info->play_work);
+ return 0;
+}
+
+static void twl4030_vibra_close(struct input_dev *input)
+{
+ struct vibra_info *info = input_get_drvdata(input);
+
+ cancel_work_sync(&info->play_work);
+
+ if (info->enabled)
+ vibra_disable(info);
+}
+
+/*** Module ***/
+#ifdef CONFIG_PM_SLEEP
+static int twl4030_vibra_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct vibra_info *info = platform_get_drvdata(pdev);
+
+ if (info->enabled)
+ vibra_disable(info);
+
+ return 0;
+}
+
+static int twl4030_vibra_resume(struct device *dev)
+{
+ vibra_disable_leds();
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(twl4030_vibra_pm_ops,
+ twl4030_vibra_suspend, twl4030_vibra_resume);
+
+static bool twl4030_vibra_check_coexist(struct twl4030_vibra_data *pdata,
+ struct device_node *node)
+{
+ if (pdata && pdata->coexist)
+ return true;
+
+ if (of_find_node_by_name(node, "codec"))
+ return true;
+
+ return false;
+}
+
+static int twl4030_vibra_probe(struct platform_device *pdev)
+{
+ struct twl4030_vibra_data *pdata = pdev->dev.platform_data;
+ struct device_node *twl4030_core_node = pdev->dev.parent->of_node;
+ struct vibra_info *info;
+ int ret;
+
+ if (!pdata && !twl4030_core_node) {
+ dev_dbg(&pdev->dev, "platform_data not available\n");
+ return -EINVAL;
+ }
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ info->dev = &pdev->dev;
+ info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node);
+ INIT_WORK(&info->play_work, vibra_play_work);
+
+ info->input_dev = devm_input_allocate_device(&pdev->dev);
+ if (info->input_dev == NULL) {
+ dev_err(&pdev->dev, "couldn't allocate input device\n");
+ return -ENOMEM;
+ }
+
+ input_set_drvdata(info->input_dev, info);
+
+ info->input_dev->name = "twl4030:vibrator";
+ info->input_dev->id.version = 1;
+ info->input_dev->dev.parent = pdev->dev.parent;
+ info->input_dev->close = twl4030_vibra_close;
+ __set_bit(FF_RUMBLE, info->input_dev->ffbit);
+
+ ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
+ return ret;
+ }
+
+ ret = input_register_device(info->input_dev);
+ if (ret < 0) {
+ dev_dbg(&pdev->dev, "couldn't register input device\n");
+ goto err_iff;
+ }
+
+ vibra_disable_leds();
+
+ platform_set_drvdata(pdev, info);
+ return 0;
+
+err_iff:
+ input_ff_destroy(info->input_dev);
+ return ret;
+}
+
+static struct platform_driver twl4030_vibra_driver = {
+ .probe = twl4030_vibra_probe,
+ .driver = {
+ .name = "twl4030-vibra",
+ .owner = THIS_MODULE,
+ .pm = &twl4030_vibra_pm_ops,
+ },
+};
+module_platform_driver(twl4030_vibra_driver);
+
+MODULE_ALIAS("platform:twl4030-vibra");
+MODULE_DESCRIPTION("TWL4030 Vibra driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nokia Corporation");
diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c
new file mode 100644
index 00000000..0c2dfc8e
--- /dev/null
+++ b/drivers/input/misc/twl6040-vibra.c
@@ -0,0 +1,431 @@
+/*
+ * twl6040-vibra.c - TWL6040 Vibrator driver
+ *
+ * Author: Jorge Eduardo Candelaria <jorge.candelaria@ti.com>
+ * Author: Misael Lopez Cruz <misael.lopez@ti.com>
+ *
+ * Copyright: (C) 2011 Texas Instruments, Inc.
+ *
+ * Based on twl4030-vibra.c by Henrik Saari <henrik.saari@nokia.com>
+ * Felipe Balbi <felipe.balbi@nokia.com>
+ * Jari Vanhala <ext-javi.vanhala@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/workqueue.h>
+#include <linux/input.h>
+#include <linux/mfd/twl6040.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+#define EFFECT_DIR_180_DEG 0x8000
+
+/* Recommended modulation index 85% */
+#define TWL6040_VIBRA_MOD 85
+
+#define TWL6040_NUM_SUPPLIES 2
+
+struct vibra_info {
+ struct device *dev;
+ struct input_dev *input_dev;
+ struct workqueue_struct *workqueue;
+ struct work_struct play_work;
+ struct mutex mutex;
+ int irq;
+
+ bool enabled;
+ int weak_speed;
+ int strong_speed;
+ int direction;
+
+ unsigned int vibldrv_res;
+ unsigned int vibrdrv_res;
+ unsigned int viblmotor_res;
+ unsigned int vibrmotor_res;
+
+ struct regulator_bulk_data supplies[TWL6040_NUM_SUPPLIES];
+
+ struct twl6040 *twl6040;
+};
+
+static irqreturn_t twl6040_vib_irq_handler(int irq, void *data)
+{
+ struct vibra_info *info = data;
+ struct twl6040 *twl6040 = info->twl6040;
+ u8 status;
+
+ status = twl6040_reg_read(twl6040, TWL6040_REG_STATUS);
+ if (status & TWL6040_VIBLOCDET) {
+ dev_warn(info->dev, "Left Vibrator overcurrent detected\n");
+ twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLL,
+ TWL6040_VIBENA);
+ }
+ if (status & TWL6040_VIBROCDET) {
+ dev_warn(info->dev, "Right Vibrator overcurrent detected\n");
+ twl6040_clear_bits(twl6040, TWL6040_REG_VIBCTLR,
+ TWL6040_VIBENA);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void twl6040_vibra_enable(struct vibra_info *info)
+{
+ struct twl6040 *twl6040 = info->twl6040;
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(info->supplies), info->supplies);
+ if (ret) {
+ dev_err(info->dev, "failed to enable regulators %d\n", ret);
+ return;
+ }
+
+ twl6040_power(info->twl6040, 1);
+ if (twl6040_get_revid(twl6040) <= TWL6040_REV_ES1_1) {
+ /*
+ * ERRATA: Disable overcurrent protection for at least
+ * 3ms when enabling vibrator drivers to avoid false
+ * overcurrent detection
+ */
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL,
+ TWL6040_VIBENA | TWL6040_VIBCTRL);
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR,
+ TWL6040_VIBENA | TWL6040_VIBCTRL);
+ usleep_range(3000, 3500);
+ }
+
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL,
+ TWL6040_VIBENA);
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR,
+ TWL6040_VIBENA);
+
+ info->enabled = true;
+}
+
+static void twl6040_vibra_disable(struct vibra_info *info)
+{
+ struct twl6040 *twl6040 = info->twl6040;
+
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLL, 0x00);
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBCTLR, 0x00);
+ twl6040_power(info->twl6040, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(info->supplies), info->supplies);
+
+ info->enabled = false;
+}
+
+static u8 twl6040_vibra_code(int vddvib, int vibdrv_res, int motor_res,
+ int speed, int direction)
+{
+ int vpk, max_code;
+ u8 vibdat;
+
+ /* output swing */
+ vpk = (vddvib * motor_res * TWL6040_VIBRA_MOD) /
+ (100 * (vibdrv_res + motor_res));
+
+ /* 50mV per VIBDAT code step */
+ max_code = vpk / 50;
+ if (max_code > TWL6040_VIBDAT_MAX)
+ max_code = TWL6040_VIBDAT_MAX;
+
+ /* scale speed to max allowed code */
+ vibdat = (u8)((speed * max_code) / USHRT_MAX);
+
+ /* 2's complement for direction > 180 degrees */
+ vibdat *= direction;
+
+ return vibdat;
+}
+
+static void twl6040_vibra_set_effect(struct vibra_info *info)
+{
+ struct twl6040 *twl6040 = info->twl6040;
+ u8 vibdatl, vibdatr;
+ int volt;
+
+ /* weak motor */
+ volt = regulator_get_voltage(info->supplies[0].consumer) / 1000;
+ vibdatl = twl6040_vibra_code(volt, info->vibldrv_res,
+ info->viblmotor_res,
+ info->weak_speed, info->direction);
+
+ /* strong motor */
+ volt = regulator_get_voltage(info->supplies[1].consumer) / 1000;
+ vibdatr = twl6040_vibra_code(volt, info->vibrdrv_res,
+ info->vibrmotor_res,
+ info->strong_speed, info->direction);
+
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBDATL, vibdatl);
+ twl6040_reg_write(twl6040, TWL6040_REG_VIBDATR, vibdatr);
+}
+
+static void vibra_play_work(struct work_struct *work)
+{
+ struct vibra_info *info = container_of(work,
+ struct vibra_info, play_work);
+
+ mutex_lock(&info->mutex);
+
+ if (info->weak_speed || info->strong_speed) {
+ if (!info->enabled)
+ twl6040_vibra_enable(info);
+
+ twl6040_vibra_set_effect(info);
+ } else if (info->enabled)
+ twl6040_vibra_disable(info);
+
+ mutex_unlock(&info->mutex);
+}
+
+static int vibra_play(struct input_dev *input, void *data,
+ struct ff_effect *effect)
+{
+ struct vibra_info *info = input_get_drvdata(input);
+ int ret;
+
+ /* Do not allow effect, while the routing is set to use audio */
+ ret = twl6040_get_vibralr_status(info->twl6040);
+ if (ret & TWL6040_VIBSEL) {
+ dev_info(&input->dev, "Vibra is configured for audio\n");
+ return -EBUSY;
+ }
+
+ info->weak_speed = effect->u.rumble.weak_magnitude;
+ info->strong_speed = effect->u.rumble.strong_magnitude;
+ info->direction = effect->direction < EFFECT_DIR_180_DEG ? 1 : -1;
+
+ ret = queue_work(info->workqueue, &info->play_work);
+ if (!ret) {
+ dev_info(&input->dev, "work is already on queue\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void twl6040_vibra_close(struct input_dev *input)
+{
+ struct vibra_info *info = input_get_drvdata(input);
+
+ cancel_work_sync(&info->play_work);
+
+ mutex_lock(&info->mutex);
+
+ if (info->enabled)
+ twl6040_vibra_disable(info);
+
+ mutex_unlock(&info->mutex);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int twl6040_vibra_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct vibra_info *info = platform_get_drvdata(pdev);
+
+ mutex_lock(&info->mutex);
+
+ if (info->enabled)
+ twl6040_vibra_disable(info);
+
+ mutex_unlock(&info->mutex);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops, twl6040_vibra_suspend, NULL);
+
+static int twl6040_vibra_probe(struct platform_device *pdev)
+{
+ struct twl6040_vibra_data *pdata = pdev->dev.platform_data;
+ struct device *twl6040_core_dev = pdev->dev.parent;
+ struct device_node *twl6040_core_node = NULL;
+ struct vibra_info *info;
+ int vddvibl_uV = 0;
+ int vddvibr_uV = 0;
+ int ret;
+
+#ifdef CONFIG_OF
+ twl6040_core_node = of_find_node_by_name(twl6040_core_dev->of_node,
+ "vibra");
+#endif
+
+ if (!pdata && !twl6040_core_node) {
+ dev_err(&pdev->dev, "platform_data not available\n");
+ return -EINVAL;
+ }
+
+ info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ dev_err(&pdev->dev, "couldn't allocate memory\n");
+ return -ENOMEM;
+ }
+
+ info->dev = &pdev->dev;
+
+ info->twl6040 = dev_get_drvdata(pdev->dev.parent);
+ if (pdata) {
+ info->vibldrv_res = pdata->vibldrv_res;
+ info->vibrdrv_res = pdata->vibrdrv_res;
+ info->viblmotor_res = pdata->viblmotor_res;
+ info->vibrmotor_res = pdata->vibrmotor_res;
+ vddvibl_uV = pdata->vddvibl_uV;
+ vddvibr_uV = pdata->vddvibr_uV;
+ } else {
+ of_property_read_u32(twl6040_core_node, "ti,vibldrv-res",
+ &info->vibldrv_res);
+ of_property_read_u32(twl6040_core_node, "ti,vibrdrv-res",
+ &info->vibrdrv_res);
+ of_property_read_u32(twl6040_core_node, "ti,viblmotor-res",
+ &info->viblmotor_res);
+ of_property_read_u32(twl6040_core_node, "ti,vibrmotor-res",
+ &info->vibrmotor_res);
+ of_property_read_u32(twl6040_core_node, "ti,vddvibl-uV",
+ &vddvibl_uV);
+ of_property_read_u32(twl6040_core_node, "ti,vddvibr-uV",
+ &vddvibr_uV);
+ }
+
+ if ((!info->vibldrv_res && !info->viblmotor_res) ||
+ (!info->vibrdrv_res && !info->vibrmotor_res)) {
+ dev_err(info->dev, "invalid vibra driver/motor resistance\n");
+ return -EINVAL;
+ }
+
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ dev_err(info->dev, "invalid irq\n");
+ return -EINVAL;
+ }
+
+ mutex_init(&info->mutex);
+
+ ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
+ twl6040_vib_irq_handler, 0,
+ "twl6040_irq_vib", info);
+ if (ret) {
+ dev_err(info->dev, "VIB IRQ request failed: %d\n", ret);
+ return ret;
+ }
+
+ info->supplies[0].supply = "vddvibl";
+ info->supplies[1].supply = "vddvibr";
+ /*
+ * When booted with Device tree the regulators are attached to the
+ * parent device (twl6040 MFD core)
+ */
+ ret = regulator_bulk_get(pdata ? info->dev : twl6040_core_dev,
+ ARRAY_SIZE(info->supplies), info->supplies);
+ if (ret) {
+ dev_err(info->dev, "couldn't get regulators %d\n", ret);
+ return ret;
+ }
+
+ if (vddvibl_uV) {
+ ret = regulator_set_voltage(info->supplies[0].consumer,
+ vddvibl_uV, vddvibl_uV);
+ if (ret) {
+ dev_err(info->dev, "failed to set VDDVIBL volt %d\n",
+ ret);
+ goto err_regulator;
+ }
+ }
+
+ if (vddvibr_uV) {
+ ret = regulator_set_voltage(info->supplies[1].consumer,
+ vddvibr_uV, vddvibr_uV);
+ if (ret) {
+ dev_err(info->dev, "failed to set VDDVIBR volt %d\n",
+ ret);
+ goto err_regulator;
+ }
+ }
+
+ INIT_WORK(&info->play_work, vibra_play_work);
+
+ info->input_dev = input_allocate_device();
+ if (info->input_dev == NULL) {
+ dev_err(info->dev, "couldn't allocate input device\n");
+ ret = -ENOMEM;
+ goto err_regulator;
+ }
+
+ input_set_drvdata(info->input_dev, info);
+
+ info->input_dev->name = "twl6040:vibrator";
+ info->input_dev->id.version = 1;
+ info->input_dev->dev.parent = pdev->dev.parent;
+ info->input_dev->close = twl6040_vibra_close;
+ __set_bit(FF_RUMBLE, info->input_dev->ffbit);
+
+ ret = input_ff_create_memless(info->input_dev, NULL, vibra_play);
+ if (ret < 0) {
+ dev_err(info->dev, "couldn't register vibrator to FF\n");
+ goto err_ialloc;
+ }
+
+ ret = input_register_device(info->input_dev);
+ if (ret < 0) {
+ dev_err(info->dev, "couldn't register input device\n");
+ goto err_iff;
+ }
+
+ platform_set_drvdata(pdev, info);
+
+ return 0;
+
+err_iff:
+ input_ff_destroy(info->input_dev);
+err_ialloc:
+ input_free_device(info->input_dev);
+err_regulator:
+ regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
+ return ret;
+}
+
+static int twl6040_vibra_remove(struct platform_device *pdev)
+{
+ struct vibra_info *info = platform_get_drvdata(pdev);
+
+ input_unregister_device(info->input_dev);
+ regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies);
+
+ return 0;
+}
+
+static struct platform_driver twl6040_vibra_driver = {
+ .probe = twl6040_vibra_probe,
+ .remove = twl6040_vibra_remove,
+ .driver = {
+ .name = "twl6040-vibra",
+ .owner = THIS_MODULE,
+ .pm = &twl6040_vibra_pm_ops,
+ },
+};
+module_platform_driver(twl6040_vibra_driver);
+
+MODULE_ALIAS("platform:twl6040-vibra");
+MODULE_DESCRIPTION("TWL6040 Vibra driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jorge Eduardo Candelaria <jorge.candelaria@ti.com>");
+MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
new file mode 100644
index 00000000..a0a4bbae
--- /dev/null
+++ b/drivers/input/misc/uinput.c
@@ -0,0 +1,883 @@
+/*
+ * User level driver support for input subsystem
+ *
+ * Heavily based on evdev.c by Vojtech Pavlik
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
+ *
+ * Changes/Revisions:
+ * 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>)
+ * - updated ff support for the changes in kernel interface
+ * - added MODULE_VERSION
+ * 0.2 16/10/2004 (Micah Dowty <micah@navi.cx>)
+ * - added force feedback support
+ * - added UI_SET_PHYS
+ * 0.1 20/06/2002
+ * - first public version
+ */
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uinput.h>
+#include <linux/input/mt.h>
+#include "../input-compat.h"
+
+static int uinput_dev_event(struct input_dev *dev,
+ unsigned int type, unsigned int code, int value)
+{
+ struct uinput_device *udev = input_get_drvdata(dev);
+
+ udev->buff[udev->head].type = type;
+ udev->buff[udev->head].code = code;
+ udev->buff[udev->head].value = value;
+ do_gettimeofday(&udev->buff[udev->head].time);
+ udev->head = (udev->head + 1) % UINPUT_BUFFER_SIZE;
+
+ wake_up_interruptible(&udev->waitq);
+
+ return 0;
+}
+
+/* Atomically allocate an ID for the given request. Returns 0 on success. */
+static bool uinput_request_alloc_id(struct uinput_device *udev,
+ struct uinput_request *request)
+{
+ unsigned int id;
+ bool reserved = false;
+
+ spin_lock(&udev->requests_lock);
+
+ for (id = 0; id < UINPUT_NUM_REQUESTS; id++) {
+ if (!udev->requests[id]) {
+ request->id = id;
+ udev->requests[id] = request;
+ reserved = true;
+ break;
+ }
+ }
+
+ spin_unlock(&udev->requests_lock);
+ return reserved;
+}
+
+static struct uinput_request *uinput_request_find(struct uinput_device *udev,
+ unsigned int id)
+{
+ /* Find an input request, by ID. Returns NULL if the ID isn't valid. */
+ if (id >= UINPUT_NUM_REQUESTS)
+ return NULL;
+
+ return udev->requests[id];
+}
+
+static int uinput_request_reserve_slot(struct uinput_device *udev,
+ struct uinput_request *request)
+{
+ /* Allocate slot. If none are available right away, wait. */
+ return wait_event_interruptible(udev->requests_waitq,
+ uinput_request_alloc_id(udev, request));
+}
+
+static void uinput_request_done(struct uinput_device *udev,
+ struct uinput_request *request)
+{
+ /* Mark slot as available */
+ udev->requests[request->id] = NULL;
+ wake_up(&udev->requests_waitq);
+
+ complete(&request->done);
+}
+
+static int uinput_request_send(struct uinput_device *udev,
+ struct uinput_request *request)
+{
+ int retval;
+
+ retval = mutex_lock_interruptible(&udev->mutex);
+ if (retval)
+ return retval;
+
+ if (udev->state != UIST_CREATED) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ init_completion(&request->done);
+
+ /*
+ * Tell our userspace application about this new request
+ * by queueing an input event.
+ */
+ uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id);
+
+ out:
+ mutex_unlock(&udev->mutex);
+ return retval;
+}
+
+static int uinput_request_submit(struct uinput_device *udev,
+ struct uinput_request *request)
+{
+ int error;
+
+ error = uinput_request_reserve_slot(udev, request);
+ if (error)
+ return error;
+
+ error = uinput_request_send(udev, request);
+ if (error) {
+ uinput_request_done(udev, request);
+ return error;
+ }
+
+ wait_for_completion(&request->done);
+ return request->retval;
+}
+
+/*
+ * Fail all outstanding requests so handlers don't wait for the userspace
+ * to finish processing them.
+ */
+static void uinput_flush_requests(struct uinput_device *udev)
+{
+ struct uinput_request *request;
+ int i;
+
+ spin_lock(&udev->requests_lock);
+
+ for (i = 0; i < UINPUT_NUM_REQUESTS; i++) {
+ request = udev->requests[i];
+ if (request) {
+ request->retval = -ENODEV;
+ uinput_request_done(udev, request);
+ }
+ }
+
+ spin_unlock(&udev->requests_lock);
+}
+
+static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
+{
+ uinput_dev_event(dev, EV_FF, FF_GAIN, gain);
+}
+
+static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+ uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude);
+}
+
+static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value)
+{
+ return uinput_dev_event(dev, EV_FF, effect_id, value);
+}
+
+static int uinput_dev_upload_effect(struct input_dev *dev,
+ struct ff_effect *effect,
+ struct ff_effect *old)
+{
+ struct uinput_device *udev = input_get_drvdata(dev);
+ struct uinput_request request;
+
+ /*
+ * uinput driver does not currently support periodic effects with
+ * custom waveform since it does not have a way to pass buffer of
+ * samples (custom_data) to userspace. If ever there is a device
+ * supporting custom waveforms we would need to define an additional
+ * ioctl (UI_UPLOAD_SAMPLES) but for now we just bail out.
+ */
+ if (effect->type == FF_PERIODIC &&
+ effect->u.periodic.waveform == FF_CUSTOM)
+ return -EINVAL;
+
+ request.code = UI_FF_UPLOAD;
+ request.u.upload.effect = effect;
+ request.u.upload.old = old;
+
+ return uinput_request_submit(udev, &request);
+}
+
+static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
+{
+ struct uinput_device *udev = input_get_drvdata(dev);
+ struct uinput_request request;
+
+ if (!test_bit(EV_FF, dev->evbit))
+ return -ENOSYS;
+
+ request.code = UI_FF_ERASE;
+ request.u.effect_id = effect_id;
+
+ return uinput_request_submit(udev, &request);
+}
+
+static void uinput_destroy_device(struct uinput_device *udev)
+{
+ const char *name, *phys;
+ struct input_dev *dev = udev->dev;
+ enum uinput_state old_state = udev->state;
+
+ udev->state = UIST_NEW_DEVICE;
+
+ if (dev) {
+ name = dev->name;
+ phys = dev->phys;
+ if (old_state == UIST_CREATED) {
+ uinput_flush_requests(udev);
+ input_unregister_device(dev);
+ } else {
+ input_free_device(dev);
+ }
+ kfree(name);
+ kfree(phys);
+ udev->dev = NULL;
+ }
+}
+
+static int uinput_create_device(struct uinput_device *udev)
+{
+ struct input_dev *dev = udev->dev;
+ int error;
+
+ if (udev->state != UIST_SETUP_COMPLETE) {
+ printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
+ return -EINVAL;
+ }
+
+ if (udev->ff_effects_max) {
+ error = input_ff_create(dev, udev->ff_effects_max);
+ if (error)
+ goto fail1;
+
+ dev->ff->upload = uinput_dev_upload_effect;
+ dev->ff->erase = uinput_dev_erase_effect;
+ dev->ff->playback = uinput_dev_playback;
+ dev->ff->set_gain = uinput_dev_set_gain;
+ dev->ff->set_autocenter = uinput_dev_set_autocenter;
+ }
+
+ error = input_register_device(udev->dev);
+ if (error)
+ goto fail2;
+
+ udev->state = UIST_CREATED;
+
+ return 0;
+
+ fail2: input_ff_destroy(dev);
+ fail1: uinput_destroy_device(udev);
+ return error;
+}
+
+static int uinput_open(struct inode *inode, struct file *file)
+{
+ struct uinput_device *newdev;
+
+ newdev = kzalloc(sizeof(struct uinput_device), GFP_KERNEL);
+ if (!newdev)
+ return -ENOMEM;
+
+ mutex_init(&newdev->mutex);
+ spin_lock_init(&newdev->requests_lock);
+ init_waitqueue_head(&newdev->requests_waitq);
+ init_waitqueue_head(&newdev->waitq);
+ newdev->state = UIST_NEW_DEVICE;
+
+ file->private_data = newdev;
+ nonseekable_open(inode, file);
+
+ return 0;
+}
+
+static int uinput_validate_absbits(struct input_dev *dev)
+{
+ unsigned int cnt;
+ int retval = 0;
+
+ for (cnt = 0; cnt < ABS_CNT; cnt++) {
+ int min, max;
+ if (!test_bit(cnt, dev->absbit))
+ continue;
+
+ min = input_abs_get_min(dev, cnt);
+ max = input_abs_get_max(dev, cnt);
+
+ if ((min != 0 || max != 0) && max <= min) {
+ printk(KERN_DEBUG
+ "%s: invalid abs[%02x] min:%d max:%d\n",
+ UINPUT_NAME, cnt,
+ input_abs_get_min(dev, cnt),
+ input_abs_get_max(dev, cnt));
+ retval = -EINVAL;
+ break;
+ }
+
+ if (input_abs_get_flat(dev, cnt) >
+ input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) {
+ printk(KERN_DEBUG
+ "%s: abs_flat #%02x out of range: %d "
+ "(min:%d/max:%d)\n",
+ UINPUT_NAME, cnt,
+ input_abs_get_flat(dev, cnt),
+ input_abs_get_min(dev, cnt),
+ input_abs_get_max(dev, cnt));
+ retval = -EINVAL;
+ break;
+ }
+ }
+ return retval;
+}
+
+static int uinput_allocate_device(struct uinput_device *udev)
+{
+ udev->dev = input_allocate_device();
+ if (!udev->dev)
+ return -ENOMEM;
+
+ udev->dev->event = uinput_dev_event;
+ input_set_drvdata(udev->dev, udev);
+
+ return 0;
+}
+
+static int uinput_setup_device(struct uinput_device *udev,
+ const char __user *buffer, size_t count)
+{
+ struct uinput_user_dev *user_dev;
+ struct input_dev *dev;
+ int i;
+ int retval;
+
+ if (count != sizeof(struct uinput_user_dev))
+ return -EINVAL;
+
+ if (!udev->dev) {
+ retval = uinput_allocate_device(udev);
+ if (retval)
+ return retval;
+ }
+
+ dev = udev->dev;
+
+ user_dev = memdup_user(buffer, sizeof(struct uinput_user_dev));
+ if (IS_ERR(user_dev))
+ return PTR_ERR(user_dev);
+
+ udev->ff_effects_max = user_dev->ff_effects_max;
+
+ /* Ensure name is filled in */
+ if (!user_dev->name[0]) {
+ retval = -EINVAL;
+ goto exit;
+ }
+
+ kfree(dev->name);
+ dev->name = kstrndup(user_dev->name, UINPUT_MAX_NAME_SIZE,
+ GFP_KERNEL);
+ if (!dev->name) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ dev->id.bustype = user_dev->id.bustype;
+ dev->id.vendor = user_dev->id.vendor;
+ dev->id.product = user_dev->id.product;
+ dev->id.version = user_dev->id.version;
+
+ for (i = 0; i < ABS_CNT; i++) {
+ input_abs_set_max(dev, i, user_dev->absmax[i]);
+ input_abs_set_min(dev, i, user_dev->absmin[i]);
+ input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]);
+ input_abs_set_flat(dev, i, user_dev->absflat[i]);
+ }
+
+ /* check if absmin/absmax/absfuzz/absflat are filled as
+ * told in Documentation/input/input-programming.txt */
+ if (test_bit(EV_ABS, dev->evbit)) {
+ retval = uinput_validate_absbits(dev);
+ if (retval < 0)
+ goto exit;
+ if (test_bit(ABS_MT_SLOT, dev->absbit)) {
+ int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
+ input_mt_init_slots(dev, nslot, 0);
+ } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
+ input_set_events_per_packet(dev, 60);
+ }
+ }
+
+ udev->state = UIST_SETUP_COMPLETE;
+ retval = count;
+
+ exit:
+ kfree(user_dev);
+ return retval;
+}
+
+static ssize_t uinput_inject_event(struct uinput_device *udev,
+ const char __user *buffer, size_t count)
+{
+ struct input_event ev;
+
+ if (count < input_event_size())
+ return -EINVAL;
+
+ if (input_event_from_user(buffer, &ev))
+ return -EFAULT;
+
+ input_event(udev->dev, ev.type, ev.code, ev.value);
+
+ return input_event_size();
+}
+
+static ssize_t uinput_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct uinput_device *udev = file->private_data;
+ int retval;
+
+ if (count == 0)
+ return 0;
+
+ retval = mutex_lock_interruptible(&udev->mutex);
+ if (retval)
+ return retval;
+
+ retval = udev->state == UIST_CREATED ?
+ uinput_inject_event(udev, buffer, count) :
+ uinput_setup_device(udev, buffer, count);
+
+ mutex_unlock(&udev->mutex);
+
+ return retval;
+}
+
+static bool uinput_fetch_next_event(struct uinput_device *udev,
+ struct input_event *event)
+{
+ bool have_event;
+
+ spin_lock_irq(&udev->dev->event_lock);
+
+ have_event = udev->head != udev->tail;
+ if (have_event) {
+ *event = udev->buff[udev->tail];
+ udev->tail = (udev->tail + 1) % UINPUT_BUFFER_SIZE;
+ }
+
+ spin_unlock_irq(&udev->dev->event_lock);
+
+ return have_event;
+}
+
+static ssize_t uinput_events_to_user(struct uinput_device *udev,
+ char __user *buffer, size_t count)
+{
+ struct input_event event;
+ size_t read = 0;
+
+ while (read + input_event_size() <= count &&
+ uinput_fetch_next_event(udev, &event)) {
+
+ if (input_event_to_user(buffer + read, &event))
+ return -EFAULT;
+
+ read += input_event_size();
+ }
+
+ return read;
+}
+
+static ssize_t uinput_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct uinput_device *udev = file->private_data;
+ ssize_t retval;
+
+ if (count != 0 && count < input_event_size())
+ return -EINVAL;
+
+ do {
+ retval = mutex_lock_interruptible(&udev->mutex);
+ if (retval)
+ return retval;
+
+ if (udev->state != UIST_CREATED)
+ retval = -ENODEV;
+ else if (udev->head == udev->tail &&
+ (file->f_flags & O_NONBLOCK))
+ retval = -EAGAIN;
+ else
+ retval = uinput_events_to_user(udev, buffer, count);
+
+ mutex_unlock(&udev->mutex);
+
+ if (retval || count == 0)
+ break;
+
+ if (!(file->f_flags & O_NONBLOCK))
+ retval = wait_event_interruptible(udev->waitq,
+ udev->head != udev->tail ||
+ udev->state != UIST_CREATED);
+ } while (retval == 0);
+
+ return retval;
+}
+
+static unsigned int uinput_poll(struct file *file, poll_table *wait)
+{
+ struct uinput_device *udev = file->private_data;
+
+ poll_wait(file, &udev->waitq, wait);
+
+ if (udev->head != udev->tail)
+ return POLLIN | POLLRDNORM;
+
+ return 0;
+}
+
+static int uinput_release(struct inode *inode, struct file *file)
+{
+ struct uinput_device *udev = file->private_data;
+
+ uinput_destroy_device(udev);
+ kfree(udev);
+
+ return 0;
+}
+
+#ifdef CONFIG_COMPAT
+struct uinput_ff_upload_compat {
+ __u32 request_id;
+ __s32 retval;
+ struct ff_effect_compat effect;
+ struct ff_effect_compat old;
+};
+
+static int uinput_ff_upload_to_user(char __user *buffer,
+ const struct uinput_ff_upload *ff_up)
+{
+ if (INPUT_COMPAT_TEST) {
+ struct uinput_ff_upload_compat ff_up_compat;
+
+ ff_up_compat.request_id = ff_up->request_id;
+ ff_up_compat.retval = ff_up->retval;
+ /*
+ * It so happens that the pointer that gives us the trouble
+ * is the last field in the structure. Since we don't support
+ * custom waveforms in uinput anyway we can just copy the whole
+ * thing (to the compat size) and ignore the pointer.
+ */
+ memcpy(&ff_up_compat.effect, &ff_up->effect,
+ sizeof(struct ff_effect_compat));
+ memcpy(&ff_up_compat.old, &ff_up->old,
+ sizeof(struct ff_effect_compat));
+
+ if (copy_to_user(buffer, &ff_up_compat,
+ sizeof(struct uinput_ff_upload_compat)))
+ return -EFAULT;
+ } else {
+ if (copy_to_user(buffer, ff_up,
+ sizeof(struct uinput_ff_upload)))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int uinput_ff_upload_from_user(const char __user *buffer,
+ struct uinput_ff_upload *ff_up)
+{
+ if (INPUT_COMPAT_TEST) {
+ struct uinput_ff_upload_compat ff_up_compat;
+
+ if (copy_from_user(&ff_up_compat, buffer,
+ sizeof(struct uinput_ff_upload_compat)))
+ return -EFAULT;
+
+ ff_up->request_id = ff_up_compat.request_id;
+ ff_up->retval = ff_up_compat.retval;
+ memcpy(&ff_up->effect, &ff_up_compat.effect,
+ sizeof(struct ff_effect_compat));
+ memcpy(&ff_up->old, &ff_up_compat.old,
+ sizeof(struct ff_effect_compat));
+
+ } else {
+ if (copy_from_user(ff_up, buffer,
+ sizeof(struct uinput_ff_upload)))
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+#else
+
+static int uinput_ff_upload_to_user(char __user *buffer,
+ const struct uinput_ff_upload *ff_up)
+{
+ if (copy_to_user(buffer, ff_up, sizeof(struct uinput_ff_upload)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int uinput_ff_upload_from_user(const char __user *buffer,
+ struct uinput_ff_upload *ff_up)
+{
+ if (copy_from_user(ff_up, buffer, sizeof(struct uinput_ff_upload)))
+ return -EFAULT;
+
+ return 0;
+}
+
+#endif
+
+#define uinput_set_bit(_arg, _bit, _max) \
+({ \
+ int __ret = 0; \
+ if (udev->state == UIST_CREATED) \
+ __ret = -EINVAL; \
+ else if ((_arg) > (_max)) \
+ __ret = -EINVAL; \
+ else set_bit((_arg), udev->dev->_bit); \
+ __ret; \
+})
+
+static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
+ unsigned long arg, void __user *p)
+{
+ int retval;
+ struct uinput_device *udev = file->private_data;
+ struct uinput_ff_upload ff_up;
+ struct uinput_ff_erase ff_erase;
+ struct uinput_request *req;
+ char *phys;
+
+ retval = mutex_lock_interruptible(&udev->mutex);
+ if (retval)
+ return retval;
+
+ if (!udev->dev) {
+ retval = uinput_allocate_device(udev);
+ if (retval)
+ goto out;
+ }
+
+ switch (cmd) {
+ case UI_DEV_CREATE:
+ retval = uinput_create_device(udev);
+ break;
+
+ case UI_DEV_DESTROY:
+ uinput_destroy_device(udev);
+ break;
+
+ case UI_SET_EVBIT:
+ retval = uinput_set_bit(arg, evbit, EV_MAX);
+ break;
+
+ case UI_SET_KEYBIT:
+ retval = uinput_set_bit(arg, keybit, KEY_MAX);
+ break;
+
+ case UI_SET_RELBIT:
+ retval = uinput_set_bit(arg, relbit, REL_MAX);
+ break;
+
+ case UI_SET_ABSBIT:
+ retval = uinput_set_bit(arg, absbit, ABS_MAX);
+ break;
+
+ case UI_SET_MSCBIT:
+ retval = uinput_set_bit(arg, mscbit, MSC_MAX);
+ break;
+
+ case UI_SET_LEDBIT:
+ retval = uinput_set_bit(arg, ledbit, LED_MAX);
+ break;
+
+ case UI_SET_SNDBIT:
+ retval = uinput_set_bit(arg, sndbit, SND_MAX);
+ break;
+
+ case UI_SET_FFBIT:
+ retval = uinput_set_bit(arg, ffbit, FF_MAX);
+ break;
+
+ case UI_SET_SWBIT:
+ retval = uinput_set_bit(arg, swbit, SW_MAX);
+ break;
+
+ case UI_SET_PROPBIT:
+ retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX);
+ break;
+
+ case UI_SET_PHYS:
+ if (udev->state == UIST_CREATED) {
+ retval = -EINVAL;
+ goto out;
+ }
+
+ phys = strndup_user(p, 1024);
+ if (IS_ERR(phys)) {
+ retval = PTR_ERR(phys);
+ goto out;
+ }
+
+ kfree(udev->dev->phys);
+ udev->dev->phys = phys;
+ break;
+
+ case UI_BEGIN_FF_UPLOAD:
+ retval = uinput_ff_upload_from_user(p, &ff_up);
+ if (retval)
+ break;
+
+ req = uinput_request_find(udev, ff_up.request_id);
+ if (!req || req->code != UI_FF_UPLOAD ||
+ !req->u.upload.effect) {
+ retval = -EINVAL;
+ break;
+ }
+
+ ff_up.retval = 0;
+ ff_up.effect = *req->u.upload.effect;
+ if (req->u.upload.old)
+ ff_up.old = *req->u.upload.old;
+ else
+ memset(&ff_up.old, 0, sizeof(struct ff_effect));
+
+ retval = uinput_ff_upload_to_user(p, &ff_up);
+ break;
+
+ case UI_BEGIN_FF_ERASE:
+ if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ req = uinput_request_find(udev, ff_erase.request_id);
+ if (!req || req->code != UI_FF_ERASE) {
+ retval = -EINVAL;
+ break;
+ }
+
+ ff_erase.retval = 0;
+ ff_erase.effect_id = req->u.effect_id;
+ if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ break;
+
+ case UI_END_FF_UPLOAD:
+ retval = uinput_ff_upload_from_user(p, &ff_up);
+ if (retval)
+ break;
+
+ req = uinput_request_find(udev, ff_up.request_id);
+ if (!req || req->code != UI_FF_UPLOAD ||
+ !req->u.upload.effect) {
+ retval = -EINVAL;
+ break;
+ }
+
+ req->retval = ff_up.retval;
+ uinput_request_done(udev, req);
+ break;
+
+ case UI_END_FF_ERASE:
+ if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) {
+ retval = -EFAULT;
+ break;
+ }
+
+ req = uinput_request_find(udev, ff_erase.request_id);
+ if (!req || req->code != UI_FF_ERASE) {
+ retval = -EINVAL;
+ break;
+ }
+
+ req->retval = ff_erase.retval;
+ uinput_request_done(udev, req);
+ break;
+
+ default:
+ retval = -EINVAL;
+ }
+
+ out:
+ mutex_unlock(&udev->mutex);
+ return retval;
+}
+
+static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return uinput_ioctl_handler(file, cmd, arg, (void __user *)arg);
+}
+
+#ifdef CONFIG_COMPAT
+static long uinput_compat_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg));
+}
+#endif
+
+static const struct file_operations uinput_fops = {
+ .owner = THIS_MODULE,
+ .open = uinput_open,
+ .release = uinput_release,
+ .read = uinput_read,
+ .write = uinput_write,
+ .poll = uinput_poll,
+ .unlocked_ioctl = uinput_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = uinput_compat_ioctl,
+#endif
+ .llseek = no_llseek,
+};
+
+static struct miscdevice uinput_misc = {
+ .fops = &uinput_fops,
+ .minor = UINPUT_MINOR,
+ .name = UINPUT_NAME,
+};
+MODULE_ALIAS_MISCDEV(UINPUT_MINOR);
+MODULE_ALIAS("devname:" UINPUT_NAME);
+
+static int __init uinput_init(void)
+{
+ return misc_register(&uinput_misc);
+}
+
+static void __exit uinput_exit(void)
+{
+ misc_deregister(&uinput_misc);
+}
+
+MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
+MODULE_DESCRIPTION("User level driver support for input subsystem");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.3");
+
+module_init(uinput_init);
+module_exit(uinput_exit);
diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c
new file mode 100644
index 00000000..56536f4b
--- /dev/null
+++ b/drivers/input/misc/wistron_btns.c
@@ -0,0 +1,1389 @@
+/*
+ * Wistron laptop button driver
+ * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
+ * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
+ * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
+ *
+ * You can redistribute and/or modify this program under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place Suite 330, Boston, MA 02111-1307, USA.
+ */
+#include <linux/io.h>
+#include <linux/dmi.h>
+#include <linux/init.h>
+#include <linux/input-polldev.h>
+#include <linux/input/sparse-keymap.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/mc146818rtc.h>
+#include <linux/module.h>
+#include <linux/preempt.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+
+/* How often we poll keys - msecs */
+#define POLL_INTERVAL_DEFAULT 500 /* when idle */
+#define POLL_INTERVAL_BURST 100 /* when a key was recently pressed */
+
+/* BIOS subsystem IDs */
+#define WIFI 0x35
+#define BLUETOOTH 0x34
+#define MAIL_LED 0x31
+
+MODULE_AUTHOR("Miloslav Trmac <mitr@volny.cz>");
+MODULE_DESCRIPTION("Wistron laptop button driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.3");
+
+static bool force; /* = 0; */
+module_param(force, bool, 0);
+MODULE_PARM_DESC(force, "Load even if computer is not in database");
+
+static char *keymap_name; /* = NULL; */
+module_param_named(keymap, keymap_name, charp, 0);
+MODULE_PARM_DESC(keymap, "Keymap name, if it can't be autodetected [generic, 1557/MS2141]");
+
+static struct platform_device *wistron_device;
+
+ /* BIOS interface implementation */
+
+static void __iomem *bios_entry_point; /* BIOS routine entry point */
+static void __iomem *bios_code_map_base;
+static void __iomem *bios_data_map_base;
+
+static u8 cmos_address;
+
+struct regs {
+ u32 eax, ebx, ecx;
+};
+
+static void call_bios(struct regs *regs)
+{
+ unsigned long flags;
+
+ preempt_disable();
+ local_irq_save(flags);
+ asm volatile ("pushl %%ebp;"
+ "movl %7, %%ebp;"
+ "call *%6;"
+ "popl %%ebp"
+ : "=a" (regs->eax), "=b" (regs->ebx), "=c" (regs->ecx)
+ : "0" (regs->eax), "1" (regs->ebx), "2" (regs->ecx),
+ "m" (bios_entry_point), "m" (bios_data_map_base)
+ : "edx", "edi", "esi", "memory");
+ local_irq_restore(flags);
+ preempt_enable();
+}
+
+static ssize_t __init locate_wistron_bios(void __iomem *base)
+{
+ static unsigned char __initdata signature[] =
+ { 0x42, 0x21, 0x55, 0x30 };
+ ssize_t offset;
+
+ for (offset = 0; offset < 0x10000; offset += 0x10) {
+ if (check_signature(base + offset, signature,
+ sizeof(signature)) != 0)
+ return offset;
+ }
+ return -1;
+}
+
+static int __init map_bios(void)
+{
+ void __iomem *base;
+ ssize_t offset;
+ u32 entry_point;
+
+ base = ioremap(0xF0000, 0x10000); /* Can't fail */
+ offset = locate_wistron_bios(base);
+ if (offset < 0) {
+ printk(KERN_ERR "wistron_btns: BIOS entry point not found\n");
+ iounmap(base);
+ return -ENODEV;
+ }
+
+ entry_point = readl(base + offset + 5);
+ printk(KERN_DEBUG
+ "wistron_btns: BIOS signature found at %p, entry point %08X\n",
+ base + offset, entry_point);
+
+ if (entry_point >= 0xF0000) {
+ bios_code_map_base = base;
+ bios_entry_point = bios_code_map_base + (entry_point & 0xFFFF);
+ } else {
+ iounmap(base);
+ bios_code_map_base = ioremap(entry_point & ~0x3FFF, 0x4000);
+ if (bios_code_map_base == NULL) {
+ printk(KERN_ERR
+ "wistron_btns: Can't map BIOS code at %08X\n",
+ entry_point & ~0x3FFF);
+ goto err;
+ }
+ bios_entry_point = bios_code_map_base + (entry_point & 0x3FFF);
+ }
+ /* The Windows driver maps 0x10000 bytes, we keep only one page... */
+ bios_data_map_base = ioremap(0x400, 0xc00);
+ if (bios_data_map_base == NULL) {
+ printk(KERN_ERR "wistron_btns: Can't map BIOS data\n");
+ goto err_code;
+ }
+ return 0;
+
+err_code:
+ iounmap(bios_code_map_base);
+err:
+ return -ENOMEM;
+}
+
+static inline void unmap_bios(void)
+{
+ iounmap(bios_code_map_base);
+ iounmap(bios_data_map_base);
+}
+
+ /* BIOS calls */
+
+static u16 bios_pop_queue(void)
+{
+ struct regs regs;
+
+ memset(&regs, 0, sizeof (regs));
+ regs.eax = 0x9610;
+ regs.ebx = 0x061C;
+ regs.ecx = 0x0000;
+ call_bios(&regs);
+
+ return regs.eax;
+}
+
+static void bios_attach(void)
+{
+ struct regs regs;
+
+ memset(&regs, 0, sizeof (regs));
+ regs.eax = 0x9610;
+ regs.ebx = 0x012E;
+ call_bios(&regs);
+}
+
+static void bios_detach(void)
+{
+ struct regs regs;
+
+ memset(&regs, 0, sizeof (regs));
+ regs.eax = 0x9610;
+ regs.ebx = 0x002E;
+ call_bios(&regs);
+}
+
+static u8 bios_get_cmos_address(void)
+{
+ struct regs regs;
+
+ memset(&regs, 0, sizeof (regs));
+ regs.eax = 0x9610;
+ regs.ebx = 0x051C;
+ call_bios(&regs);
+
+ return regs.ecx;
+}
+
+static u16 bios_get_default_setting(u8 subsys)
+{
+ struct regs regs;
+
+ memset(&regs, 0, sizeof (regs));
+ regs.eax = 0x9610;
+ regs.ebx = 0x0200 | subsys;
+ call_bios(&regs);
+
+ return regs.eax;
+}
+
+static void bios_set_state(u8 subsys, int enable)
+{
+ struct regs regs;
+
+ memset(&regs, 0, sizeof (regs));
+ regs.eax = 0x9610;
+ regs.ebx = (enable ? 0x0100 : 0x0000) | subsys;
+ call_bios(&regs);
+}
+
+/* Hardware database */
+
+#define KE_WIFI (KE_LAST + 1)
+#define KE_BLUETOOTH (KE_LAST + 2)
+
+#define FE_MAIL_LED 0x01
+#define FE_WIFI_LED 0x02
+#define FE_UNTESTED 0x80
+
+static struct key_entry *keymap; /* = NULL; Current key map */
+static bool have_wifi;
+static bool have_bluetooth;
+static int leds_present; /* bitmask of leds present */
+
+static int __init dmi_matched(const struct dmi_system_id *dmi)
+{
+ const struct key_entry *key;
+
+ keymap = dmi->driver_data;
+ for (key = keymap; key->type != KE_END; key++) {
+ if (key->type == KE_WIFI)
+ have_wifi = true;
+ else if (key->type == KE_BLUETOOTH)
+ have_bluetooth = true;
+ }
+ leds_present = key->code & (FE_MAIL_LED | FE_WIFI_LED);
+
+ return 1;
+}
+
+static struct key_entry keymap_empty[] __initdata = {
+ { KE_END, 0 }
+};
+
+static struct key_entry keymap_fs_amilo_pro_v2000[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, 0 }
+};
+
+static struct key_entry keymap_fs_amilo_pro_v3505[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} }, /* Fn+F1 */
+ { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Fn+F4 */
+ { KE_BLUETOOTH, 0x30 }, /* Fn+F10 */
+ { KE_KEY, 0x31, {KEY_MAIL} }, /* mail button */
+ { KE_KEY, 0x36, {KEY_WWW} }, /* www button */
+ { KE_WIFI, 0x78 }, /* satellite dish button */
+ { KE_END, 0 }
+};
+
+static struct key_entry keymap_fujitsu_n3510[] __initdata = {
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x71, {KEY_STOPCD} },
+ { KE_KEY, 0x72, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x74, {KEY_REWIND} },
+ { KE_KEY, 0x78, {KEY_FORWARD} },
+ { KE_END, 0 }
+};
+
+static struct key_entry keymap_wistron_ms2111[] __initdata = {
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, FE_MAIL_LED }
+};
+
+static struct key_entry keymap_wistron_md40100[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x37, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+ { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_wistron_ms2141[] __initdata = {
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x22, {KEY_REWIND} },
+ { KE_KEY, 0x23, {KEY_FORWARD} },
+ { KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x25, {KEY_STOPCD} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, 0 }
+};
+
+static struct key_entry keymap_acer_aspire_1500[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x49, {KEY_CONFIG} },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_aspire_1600[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x49, {KEY_CONFIG} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+/* 3020 has been tested */
+static struct key_entry keymap_acer_aspire_5020[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x6a, {KEY_CONFIG} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_2410[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x6d, {KEY_POWER} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x6a, {KEY_CONFIG} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_110[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_SW, 0x4a, {.sw = {SW_LID, 1}} }, /* lid close */
+ { KE_SW, 0x4b, {.sw = {SW_LID, 0}} }, /* lid open */
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_300[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_380[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} }, /* not 370 */
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+/* unusual map */
+static struct key_entry keymap_acer_travelmate_220[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_MAIL} },
+ { KE_KEY, 0x12, {KEY_WWW} },
+ { KE_KEY, 0x13, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_PROG1} },
+ { KE_END, FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_230[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_240[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_350[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_MAIL} },
+ { KE_KEY, 0x14, {KEY_PROG3} },
+ { KE_KEY, 0x15, {KEY_WWW} },
+ { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_acer_travelmate_360[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_MAIL} },
+ { KE_KEY, 0x14, {KEY_PROG3} },
+ { KE_KEY, 0x15, {KEY_WWW} },
+ { KE_KEY, 0x40, {KEY_WLAN} },
+ { KE_END, FE_WIFI_LED | FE_UNTESTED } /* no mail led */
+};
+
+/* Wifi subsystem only activates the led. Therefore we need to pass
+ * wifi event as a normal key, then userspace can really change the wifi state.
+ * TODO we need to export led state to userspace (wifi and mail) */
+static struct key_entry keymap_acer_travelmate_610[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x14, {KEY_MAIL} },
+ { KE_KEY, 0x15, {KEY_WWW} },
+ { KE_KEY, 0x40, {KEY_WLAN} },
+ { KE_END, FE_MAIL_LED | FE_WIFI_LED }
+};
+
+static struct key_entry keymap_acer_travelmate_630[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x08, {KEY_MUTE} }, /* not 620 */
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_aopen_1559as[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x06, {KEY_PROG3} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, 0 },
+};
+
+static struct key_entry keymap_fs_amilo_d88x0[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_END, FE_MAIL_LED | FE_WIFI_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_wistron_md2900[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_END, FE_MAIL_LED | FE_UNTESTED }
+};
+
+static struct key_entry keymap_wistron_md96500[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */
+ { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x22, {KEY_REWIND} },
+ { KE_KEY, 0x23, {KEY_FORWARD} },
+ { KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x25, {KEY_STOPCD} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, FE_UNTESTED }
+};
+
+static struct key_entry keymap_wistron_generic[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x02, {KEY_CONFIG} },
+ { KE_KEY, 0x03, {KEY_POWER} },
+ { KE_KEY, 0x05, {KEY_SWITCHVIDEOMODE} }, /* Display selection */
+ { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+ { KE_KEY, 0x08, {KEY_MUTE} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_KEY, 0x13, {KEY_PROG3} },
+ { KE_KEY, 0x14, {KEY_MAIL} },
+ { KE_KEY, 0x15, {KEY_WWW} },
+ { KE_KEY, 0x20, {KEY_VOLUMEUP} },
+ { KE_KEY, 0x21, {KEY_VOLUMEDOWN} },
+ { KE_KEY, 0x22, {KEY_REWIND} },
+ { KE_KEY, 0x23, {KEY_FORWARD} },
+ { KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x25, {KEY_STOPCD} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_KEY, 0x37, {KEY_DISPLAYTOGGLE} }, /* Display on/off */
+ { KE_KEY, 0x40, {KEY_WLAN} },
+ { KE_KEY, 0x49, {KEY_CONFIG} },
+ { KE_SW, 0x4a, {.sw = {SW_LID, 1}} }, /* lid close */
+ { KE_SW, 0x4b, {.sw = {SW_LID, 0}} }, /* lid open */
+ { KE_KEY, 0x6a, {KEY_CONFIG} },
+ { KE_KEY, 0x6d, {KEY_POWER} },
+ { KE_KEY, 0x71, {KEY_STOPCD} },
+ { KE_KEY, 0x72, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x74, {KEY_REWIND} },
+ { KE_KEY, 0x78, {KEY_FORWARD} },
+ { KE_WIFI, 0x30 },
+ { KE_BLUETOOTH, 0x44 },
+ { KE_END, 0 }
+};
+
+static struct key_entry keymap_aopen_1557[] __initdata = {
+ { KE_KEY, 0x01, {KEY_HELP} },
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x22, {KEY_REWIND} },
+ { KE_KEY, 0x23, {KEY_FORWARD} },
+ { KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x25, {KEY_STOPCD} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, 0 }
+};
+
+static struct key_entry keymap_prestigio[] __initdata = {
+ { KE_KEY, 0x11, {KEY_PROG1} },
+ { KE_KEY, 0x12, {KEY_PROG2} },
+ { KE_WIFI, 0x30 },
+ { KE_KEY, 0x22, {KEY_REWIND} },
+ { KE_KEY, 0x23, {KEY_FORWARD} },
+ { KE_KEY, 0x24, {KEY_PLAYPAUSE} },
+ { KE_KEY, 0x25, {KEY_STOPCD} },
+ { KE_KEY, 0x31, {KEY_MAIL} },
+ { KE_KEY, 0x36, {KEY_WWW} },
+ { KE_END, 0 }
+};
+
+
+/*
+ * If your machine is not here (which is currently rather likely), please send
+ * a list of buttons and their key codes (reported when loading this module
+ * with force=1) and the output of dmidecode to $MODULE_AUTHOR.
+ */
+static const struct dmi_system_id __initconst dmi_ids[] = {
+ {
+ /* Fujitsu-Siemens Amilo Pro V2000 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2000"),
+ },
+ .driver_data = keymap_fs_amilo_pro_v2000
+ },
+ {
+ /* Fujitsu-Siemens Amilo Pro Edition V3505 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Edition V3505"),
+ },
+ .driver_data = keymap_fs_amilo_pro_v3505
+ },
+ {
+ /* Fujitsu-Siemens Amilo M7400 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO M "),
+ },
+ .driver_data = keymap_fs_amilo_pro_v2000
+ },
+ {
+ /* Maxdata Pro 7000 DX */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MAXDATA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Pro 7000"),
+ },
+ .driver_data = keymap_fs_amilo_pro_v2000
+ },
+ {
+ /* Fujitsu N3510 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "N3510"),
+ },
+ .driver_data = keymap_fujitsu_n3510
+ },
+ {
+ /* Acer Aspire 1500 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1500"),
+ },
+ .driver_data = keymap_acer_aspire_1500
+ },
+ {
+ /* Acer Aspire 1600 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1600"),
+ },
+ .driver_data = keymap_acer_aspire_1600
+ },
+ {
+ /* Acer Aspire 3020 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 3020"),
+ },
+ .driver_data = keymap_acer_aspire_5020
+ },
+ {
+ /* Acer Aspire 5020 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5020"),
+ },
+ .driver_data = keymap_acer_aspire_5020
+ },
+ {
+ /* Acer TravelMate 2100 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2100"),
+ },
+ .driver_data = keymap_acer_aspire_5020
+ },
+ {
+ /* Acer TravelMate 2410 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2410"),
+ },
+ .driver_data = keymap_acer_travelmate_2410
+ },
+ {
+ /* Acer TravelMate C300 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C300"),
+ },
+ .driver_data = keymap_acer_travelmate_300
+ },
+ {
+ /* Acer TravelMate C100 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C100"),
+ },
+ .driver_data = keymap_acer_travelmate_300
+ },
+ {
+ /* Acer TravelMate C110 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate C110"),
+ },
+ .driver_data = keymap_acer_travelmate_110
+ },
+ {
+ /* Acer TravelMate 380 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 380"),
+ },
+ .driver_data = keymap_acer_travelmate_380
+ },
+ {
+ /* Acer TravelMate 370 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 370"),
+ },
+ .driver_data = keymap_acer_travelmate_380 /* keyboard minus 1 key */
+ },
+ {
+ /* Acer TravelMate 220 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 220"),
+ },
+ .driver_data = keymap_acer_travelmate_220
+ },
+ {
+ /* Acer TravelMate 260 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 260"),
+ },
+ .driver_data = keymap_acer_travelmate_220
+ },
+ {
+ /* Acer TravelMate 230 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 230"),
+ /* acerhk looks for "TravelMate F4..." ?! */
+ },
+ .driver_data = keymap_acer_travelmate_230
+ },
+ {
+ /* Acer TravelMate 280 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 280"),
+ },
+ .driver_data = keymap_acer_travelmate_230
+ },
+ {
+ /* Acer TravelMate 240 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 240"),
+ },
+ .driver_data = keymap_acer_travelmate_240
+ },
+ {
+ /* Acer TravelMate 250 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 250"),
+ },
+ .driver_data = keymap_acer_travelmate_240
+ },
+ {
+ /* Acer TravelMate 2424NWXCi */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2420"),
+ },
+ .driver_data = keymap_acer_travelmate_240
+ },
+ {
+ /* Acer TravelMate 350 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 350"),
+ },
+ .driver_data = keymap_acer_travelmate_350
+ },
+ {
+ /* Acer TravelMate 360 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 360"),
+ },
+ .driver_data = keymap_acer_travelmate_360
+ },
+ {
+ /* Acer TravelMate 610 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ACER"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 610"),
+ },
+ .driver_data = keymap_acer_travelmate_610
+ },
+ {
+ /* Acer TravelMate 620 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 620"),
+ },
+ .driver_data = keymap_acer_travelmate_630
+ },
+ {
+ /* Acer TravelMate 630 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 630"),
+ },
+ .driver_data = keymap_acer_travelmate_630
+ },
+ {
+ /* AOpen 1559AS */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "E2U"),
+ DMI_MATCH(DMI_BOARD_NAME, "E2U"),
+ },
+ .driver_data = keymap_aopen_1559as
+ },
+ {
+ /* Medion MD 9783 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MD 9783"),
+ },
+ .driver_data = keymap_wistron_ms2111
+ },
+ {
+ /* Medion MD 40100 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WID2000"),
+ },
+ .driver_data = keymap_wistron_md40100
+ },
+ {
+ /* Medion MD 2900 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2000"),
+ },
+ .driver_data = keymap_wistron_md2900
+ },
+ {
+ /* Medion MD 42200 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Medion"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2030"),
+ },
+ .driver_data = keymap_fs_amilo_pro_v2000
+ },
+ {
+ /* Medion MD 96500 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2040"),
+ },
+ .driver_data = keymap_wistron_md96500
+ },
+ {
+ /* Medion MD 95400 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONPC"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "WIM 2050"),
+ },
+ .driver_data = keymap_wistron_md96500
+ },
+ {
+ /* Fujitsu Siemens Amilo D7820 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), /* not sure */
+ DMI_MATCH(DMI_PRODUCT_NAME, "Amilo D"),
+ },
+ .driver_data = keymap_fs_amilo_d88x0
+ },
+ {
+ /* Fujitsu Siemens Amilo D88x0 */
+ .callback = dmi_matched,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO D"),
+ },
+ .driver_data = keymap_fs_amilo_d88x0
+ },
+ { NULL, }
+};
+
+/* Copy the good keymap, as the original ones are free'd */
+static int __init copy_keymap(void)
+{
+ const struct key_entry *key;
+ struct key_entry *new_keymap;
+ unsigned int length = 1;
+
+ for (key = keymap; key->type != KE_END; key++)
+ length++;
+
+ new_keymap = kmemdup(keymap, length * sizeof(struct key_entry),
+ GFP_KERNEL);
+ if (!new_keymap)
+ return -ENOMEM;
+
+ keymap = new_keymap;
+
+ return 0;
+}
+
+static int __init select_keymap(void)
+{
+ dmi_check_system(dmi_ids);
+ if (keymap_name != NULL) {
+ if (strcmp (keymap_name, "1557/MS2141") == 0)
+ keymap = keymap_wistron_ms2141;
+ else if (strcmp (keymap_name, "aopen1557") == 0)
+ keymap = keymap_aopen_1557;
+ else if (strcmp (keymap_name, "prestigio") == 0)
+ keymap = keymap_prestigio;
+ else if (strcmp (keymap_name, "generic") == 0)
+ keymap = keymap_wistron_generic;
+ else {
+ printk(KERN_ERR "wistron_btns: Keymap unknown\n");
+ return -EINVAL;
+ }
+ }
+ if (keymap == NULL) {
+ if (!force) {
+ printk(KERN_ERR "wistron_btns: System unknown\n");
+ return -ENODEV;
+ }
+ keymap = keymap_empty;
+ }
+
+ return copy_keymap();
+}
+
+ /* Input layer interface */
+
+static struct input_polled_dev *wistron_idev;
+static unsigned long jiffies_last_press;
+static bool wifi_enabled;
+static bool bluetooth_enabled;
+
+ /* led management */
+static void wistron_mail_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ bios_set_state(MAIL_LED, (value != LED_OFF) ? 1 : 0);
+}
+
+/* same as setting up wifi card, but for laptops on which the led is managed */
+static void wistron_wifi_led_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ bios_set_state(WIFI, (value != LED_OFF) ? 1 : 0);
+}
+
+static struct led_classdev wistron_mail_led = {
+ .name = "wistron:green:mail",
+ .brightness_set = wistron_mail_led_set,
+};
+
+static struct led_classdev wistron_wifi_led = {
+ .name = "wistron:red:wifi",
+ .brightness_set = wistron_wifi_led_set,
+};
+
+static void wistron_led_init(struct device *parent)
+{
+ if (leds_present & FE_WIFI_LED) {
+ u16 wifi = bios_get_default_setting(WIFI);
+ if (wifi & 1) {
+ wistron_wifi_led.brightness = (wifi & 2) ? LED_FULL : LED_OFF;
+ if (led_classdev_register(parent, &wistron_wifi_led))
+ leds_present &= ~FE_WIFI_LED;
+ else
+ bios_set_state(WIFI, wistron_wifi_led.brightness);
+
+ } else
+ leds_present &= ~FE_WIFI_LED;
+ }
+
+ if (leds_present & FE_MAIL_LED) {
+ /* bios_get_default_setting(MAIL) always retuns 0, so just turn the led off */
+ wistron_mail_led.brightness = LED_OFF;
+ if (led_classdev_register(parent, &wistron_mail_led))
+ leds_present &= ~FE_MAIL_LED;
+ else
+ bios_set_state(MAIL_LED, wistron_mail_led.brightness);
+ }
+}
+
+static void wistron_led_remove(void)
+{
+ if (leds_present & FE_MAIL_LED)
+ led_classdev_unregister(&wistron_mail_led);
+
+ if (leds_present & FE_WIFI_LED)
+ led_classdev_unregister(&wistron_wifi_led);
+}
+
+static inline void wistron_led_suspend(void)
+{
+ if (leds_present & FE_MAIL_LED)
+ led_classdev_suspend(&wistron_mail_led);
+
+ if (leds_present & FE_WIFI_LED)
+ led_classdev_suspend(&wistron_wifi_led);
+}
+
+static inline void wistron_led_resume(void)
+{
+ if (leds_present & FE_MAIL_LED)
+ led_classdev_resume(&wistron_mail_led);
+
+ if (leds_present & FE_WIFI_LED)
+ led_classdev_resume(&wistron_wifi_led);
+}
+
+static void handle_key(u8 code)
+{
+ const struct key_entry *key =
+ sparse_keymap_entry_from_scancode(wistron_idev->input, code);
+
+ if (key) {
+ switch (key->type) {
+ case KE_WIFI:
+ if (have_wifi) {
+ wifi_enabled = !wifi_enabled;
+ bios_set_state(WIFI, wifi_enabled);
+ }
+ break;
+
+ case KE_BLUETOOTH:
+ if (have_bluetooth) {
+ bluetooth_enabled = !bluetooth_enabled;
+ bios_set_state(BLUETOOTH, bluetooth_enabled);
+ }
+ break;
+
+ default:
+ sparse_keymap_report_entry(wistron_idev->input,
+ key, 1, true);
+ break;
+ }
+ jiffies_last_press = jiffies;
+ } else
+ printk(KERN_NOTICE
+ "wistron_btns: Unknown key code %02X\n", code);
+}
+
+static void poll_bios(bool discard)
+{
+ u8 qlen;
+ u16 val;
+
+ for (;;) {
+ qlen = CMOS_READ(cmos_address);
+ if (qlen == 0)
+ break;
+ val = bios_pop_queue();
+ if (val != 0 && !discard)
+ handle_key((u8)val);
+ }
+}
+
+static void wistron_flush(struct input_polled_dev *dev)
+{
+ /* Flush stale event queue */
+ poll_bios(true);
+}
+
+static void wistron_poll(struct input_polled_dev *dev)
+{
+ poll_bios(false);
+
+ /* Increase poll frequency if user is currently pressing keys (< 2s ago) */
+ if (time_before(jiffies, jiffies_last_press + 2 * HZ))
+ dev->poll_interval = POLL_INTERVAL_BURST;
+ else
+ dev->poll_interval = POLL_INTERVAL_DEFAULT;
+}
+
+static int wistron_setup_keymap(struct input_dev *dev,
+ struct key_entry *entry)
+{
+ switch (entry->type) {
+
+ /* if wifi or bluetooth are not available, create normal keys */
+ case KE_WIFI:
+ if (!have_wifi) {
+ entry->type = KE_KEY;
+ entry->keycode = KEY_WLAN;
+ }
+ break;
+
+ case KE_BLUETOOTH:
+ if (!have_bluetooth) {
+ entry->type = KE_KEY;
+ entry->keycode = KEY_BLUETOOTH;
+ }
+ break;
+
+ case KE_END:
+ if (entry->code & FE_UNTESTED)
+ printk(KERN_WARNING "Untested laptop multimedia keys, "
+ "please report success or failure to "
+ "eric.piel@tremplin-utc.net\n");
+ break;
+ }
+
+ return 0;
+}
+
+static int setup_input_dev(void)
+{
+ struct input_dev *input_dev;
+ int error;
+
+ wistron_idev = input_allocate_polled_device();
+ if (!wistron_idev)
+ return -ENOMEM;
+
+ wistron_idev->open = wistron_flush;
+ wistron_idev->poll = wistron_poll;
+ wistron_idev->poll_interval = POLL_INTERVAL_DEFAULT;
+
+ input_dev = wistron_idev->input;
+ input_dev->name = "Wistron laptop buttons";
+ input_dev->phys = "wistron/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->dev.parent = &wistron_device->dev;
+
+ error = sparse_keymap_setup(input_dev, keymap, wistron_setup_keymap);
+ if (error)
+ goto err_free_dev;
+
+ error = input_register_polled_device(wistron_idev);
+ if (error)
+ goto err_free_keymap;
+
+ return 0;
+
+ err_free_keymap:
+ sparse_keymap_free(input_dev);
+ err_free_dev:
+ input_free_polled_device(wistron_idev);
+ return error;
+}
+
+/* Driver core */
+
+static int wistron_probe(struct platform_device *dev)
+{
+ int err;
+
+ bios_attach();
+ cmos_address = bios_get_cmos_address();
+
+ if (have_wifi) {
+ u16 wifi = bios_get_default_setting(WIFI);
+ if (wifi & 1)
+ wifi_enabled = wifi & 2;
+ else
+ have_wifi = 0;
+
+ if (have_wifi)
+ bios_set_state(WIFI, wifi_enabled);
+ }
+
+ if (have_bluetooth) {
+ u16 bt = bios_get_default_setting(BLUETOOTH);
+ if (bt & 1)
+ bluetooth_enabled = bt & 2;
+ else
+ have_bluetooth = false;
+
+ if (have_bluetooth)
+ bios_set_state(BLUETOOTH, bluetooth_enabled);
+ }
+
+ wistron_led_init(&dev->dev);
+
+ err = setup_input_dev();
+ if (err) {
+ bios_detach();
+ return err;
+ }
+
+ return 0;
+}
+
+static int wistron_remove(struct platform_device *dev)
+{
+ wistron_led_remove();
+ input_unregister_polled_device(wistron_idev);
+ sparse_keymap_free(wistron_idev->input);
+ input_free_polled_device(wistron_idev);
+ bios_detach();
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int wistron_suspend(struct device *dev)
+{
+ if (have_wifi)
+ bios_set_state(WIFI, 0);
+
+ if (have_bluetooth)
+ bios_set_state(BLUETOOTH, 0);
+
+ wistron_led_suspend();
+
+ return 0;
+}
+
+static int wistron_resume(struct device *dev)
+{
+ if (have_wifi)
+ bios_set_state(WIFI, wifi_enabled);
+
+ if (have_bluetooth)
+ bios_set_state(BLUETOOTH, bluetooth_enabled);
+
+ wistron_led_resume();
+
+ poll_bios(true);
+
+ return 0;
+}
+
+static const struct dev_pm_ops wistron_pm_ops = {
+ .suspend = wistron_suspend,
+ .resume = wistron_resume,
+ .poweroff = wistron_suspend,
+ .restore = wistron_resume,
+};
+#endif
+
+static struct platform_driver wistron_driver = {
+ .driver = {
+ .name = "wistron-bios",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &wistron_pm_ops,
+#endif
+ },
+ .probe = wistron_probe,
+ .remove = wistron_remove,
+};
+
+static int __init wb_module_init(void)
+{
+ int err;
+
+ err = select_keymap();
+ if (err)
+ return err;
+
+ err = map_bios();
+ if (err)
+ goto err_free_keymap;
+
+ err = platform_driver_register(&wistron_driver);
+ if (err)
+ goto err_unmap_bios;
+
+ wistron_device = platform_device_alloc("wistron-bios", -1);
+ if (!wistron_device) {
+ err = -ENOMEM;
+ goto err_unregister_driver;
+ }
+
+ err = platform_device_add(wistron_device);
+ if (err)
+ goto err_free_device;
+
+ return 0;
+
+ err_free_device:
+ platform_device_put(wistron_device);
+ err_unregister_driver:
+ platform_driver_unregister(&wistron_driver);
+ err_unmap_bios:
+ unmap_bios();
+ err_free_keymap:
+ kfree(keymap);
+
+ return err;
+}
+
+static void __exit wb_module_exit(void)
+{
+ platform_device_unregister(wistron_device);
+ platform_driver_unregister(&wistron_driver);
+ unmap_bios();
+ kfree(keymap);
+}
+
+module_init(wb_module_init);
+module_exit(wb_module_exit);
diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c
new file mode 100644
index 00000000..caa2c406
--- /dev/null
+++ b/drivers/input/misc/wm831x-on.c
@@ -0,0 +1,151 @@
+/**
+ * wm831x-on.c - WM831X ON pin driver
+ *
+ * Copyright (C) 2009 Wolfson Microelectronics plc
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/mfd/wm831x/core.h>
+
+struct wm831x_on {
+ struct input_dev *dev;
+ struct delayed_work work;
+ struct wm831x *wm831x;
+};
+
+/*
+ * The chip gives us an interrupt when the ON pin is asserted but we
+ * then need to poll to see when the pin is deasserted.
+ */
+static void wm831x_poll_on(struct work_struct *work)
+{
+ struct wm831x_on *wm831x_on = container_of(work, struct wm831x_on,
+ work.work);
+ struct wm831x *wm831x = wm831x_on->wm831x;
+ int poll, ret;
+
+ ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL);
+ if (ret >= 0) {
+ poll = !(ret & WM831X_ON_PIN_STS);
+
+ input_report_key(wm831x_on->dev, KEY_POWER, poll);
+ input_sync(wm831x_on->dev);
+ } else {
+ dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret);
+ poll = 1;
+ }
+
+ if (poll)
+ schedule_delayed_work(&wm831x_on->work, 100);
+}
+
+static irqreturn_t wm831x_on_irq(int irq, void *data)
+{
+ struct wm831x_on *wm831x_on = data;
+
+ schedule_delayed_work(&wm831x_on->work, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int wm831x_on_probe(struct platform_device *pdev)
+{
+ struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_on *wm831x_on;
+ int irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0));
+ int ret;
+
+ wm831x_on = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_on),
+ GFP_KERNEL);
+ if (!wm831x_on) {
+ dev_err(&pdev->dev, "Can't allocate data\n");
+ return -ENOMEM;
+ }
+
+ wm831x_on->wm831x = wm831x;
+ INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on);
+
+ wm831x_on->dev = devm_input_allocate_device(&pdev->dev);
+ if (!wm831x_on->dev) {
+ dev_err(&pdev->dev, "Can't allocate input dev\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ wm831x_on->dev->evbit[0] = BIT_MASK(EV_KEY);
+ wm831x_on->dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+ wm831x_on->dev->name = "wm831x_on";
+ wm831x_on->dev->phys = "wm831x_on/input0";
+ wm831x_on->dev->dev.parent = &pdev->dev;
+
+ ret = request_threaded_irq(irq, NULL, wm831x_on_irq,
+ IRQF_TRIGGER_RISING, "wm831x_on",
+ wm831x_on);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret);
+ goto err_input_dev;
+ }
+ ret = input_register_device(wm831x_on->dev);
+ if (ret) {
+ dev_dbg(&pdev->dev, "Can't register input device: %d\n", ret);
+ goto err_irq;
+ }
+
+ platform_set_drvdata(pdev, wm831x_on);
+
+ return 0;
+
+err_irq:
+ free_irq(irq, wm831x_on);
+err_input_dev:
+err:
+ return ret;
+}
+
+static int wm831x_on_remove(struct platform_device *pdev)
+{
+ struct wm831x_on *wm831x_on = platform_get_drvdata(pdev);
+ int irq = platform_get_irq(pdev, 0);
+
+ free_irq(irq, wm831x_on);
+ cancel_delayed_work_sync(&wm831x_on->work);
+
+ return 0;
+}
+
+static struct platform_driver wm831x_on_driver = {
+ .probe = wm831x_on_probe,
+ .remove = wm831x_on_remove,
+ .driver = {
+ .name = "wm831x-on",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(wm831x_on_driver);
+
+MODULE_ALIAS("platform:wm831x-on");
+MODULE_DESCRIPTION("WM831x ON pin");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
+
diff --git a/drivers/input/misc/xen-kbdfront.c b/drivers/input/misc/xen-kbdfront.c
new file mode 100644
index 00000000..e21c1816
--- /dev/null
+++ b/drivers/input/misc/xen-kbdfront.c
@@ -0,0 +1,396 @@
+/*
+ * Xen para-virtual input device
+ *
+ * Copyright (C) 2005 Anthony Liguori <aliguori@us.ibm.com>
+ * Copyright (C) 2006-2008 Red Hat, Inc., Markus Armbruster <armbru@redhat.com>
+ *
+ * Based on linux/drivers/input/mouse/sermouse.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#include <asm/xen/hypervisor.h>
+
+#include <xen/xen.h>
+#include <xen/events.h>
+#include <xen/page.h>
+#include <xen/grant_table.h>
+#include <xen/interface/grant_table.h>
+#include <xen/interface/io/fbif.h>
+#include <xen/interface/io/kbdif.h>
+#include <xen/xenbus.h>
+
+struct xenkbd_info {
+ struct input_dev *kbd;
+ struct input_dev *ptr;
+ struct xenkbd_page *page;
+ int gref;
+ int irq;
+ struct xenbus_device *xbdev;
+ char phys[32];
+};
+
+static int xenkbd_remove(struct xenbus_device *);
+static int xenkbd_connect_backend(struct xenbus_device *, struct xenkbd_info *);
+static void xenkbd_disconnect_backend(struct xenkbd_info *);
+
+/*
+ * Note: if you need to send out events, see xenfb_do_update() for how
+ * to do that.
+ */
+
+static irqreturn_t input_handler(int rq, void *dev_id)
+{
+ struct xenkbd_info *info = dev_id;
+ struct xenkbd_page *page = info->page;
+ __u32 cons, prod;
+
+ prod = page->in_prod;
+ if (prod == page->in_cons)
+ return IRQ_HANDLED;
+ rmb(); /* ensure we see ring contents up to prod */
+ for (cons = page->in_cons; cons != prod; cons++) {
+ union xenkbd_in_event *event;
+ struct input_dev *dev;
+ event = &XENKBD_IN_RING_REF(page, cons);
+
+ dev = info->ptr;
+ switch (event->type) {
+ case XENKBD_TYPE_MOTION:
+ input_report_rel(dev, REL_X, event->motion.rel_x);
+ input_report_rel(dev, REL_Y, event->motion.rel_y);
+ if (event->motion.rel_z)
+ input_report_rel(dev, REL_WHEEL,
+ -event->motion.rel_z);
+ break;
+ case XENKBD_TYPE_KEY:
+ dev = NULL;
+ if (test_bit(event->key.keycode, info->kbd->keybit))
+ dev = info->kbd;
+ if (test_bit(event->key.keycode, info->ptr->keybit))
+ dev = info->ptr;
+ if (dev)
+ input_report_key(dev, event->key.keycode,
+ event->key.pressed);
+ else
+ pr_warning("unhandled keycode 0x%x\n",
+ event->key.keycode);
+ break;
+ case XENKBD_TYPE_POS:
+ input_report_abs(dev, ABS_X, event->pos.abs_x);
+ input_report_abs(dev, ABS_Y, event->pos.abs_y);
+ if (event->pos.rel_z)
+ input_report_rel(dev, REL_WHEEL,
+ -event->pos.rel_z);
+ break;
+ }
+ if (dev)
+ input_sync(dev);
+ }
+ mb(); /* ensure we got ring contents */
+ page->in_cons = cons;
+ notify_remote_via_irq(info->irq);
+
+ return IRQ_HANDLED;
+}
+
+static int xenkbd_probe(struct xenbus_device *dev,
+ const struct xenbus_device_id *id)
+{
+ int ret, i, abs;
+ struct xenkbd_info *info;
+ struct input_dev *kbd, *ptr;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ xenbus_dev_fatal(dev, -ENOMEM, "allocating info structure");
+ return -ENOMEM;
+ }
+ dev_set_drvdata(&dev->dev, info);
+ info->xbdev = dev;
+ info->irq = -1;
+ info->gref = -1;
+ snprintf(info->phys, sizeof(info->phys), "xenbus/%s", dev->nodename);
+
+ info->page = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO);
+ if (!info->page)
+ goto error_nomem;
+
+ if (xenbus_scanf(XBT_NIL, dev->otherend, "feature-abs-pointer", "%d", &abs) < 0)
+ abs = 0;
+ if (abs)
+ xenbus_printf(XBT_NIL, dev->nodename, "request-abs-pointer", "1");
+
+ /* keyboard */
+ kbd = input_allocate_device();
+ if (!kbd)
+ goto error_nomem;
+ kbd->name = "Xen Virtual Keyboard";
+ kbd->phys = info->phys;
+ kbd->id.bustype = BUS_PCI;
+ kbd->id.vendor = 0x5853;
+ kbd->id.product = 0xffff;
+
+ __set_bit(EV_KEY, kbd->evbit);
+ for (i = KEY_ESC; i < KEY_UNKNOWN; i++)
+ __set_bit(i, kbd->keybit);
+ for (i = KEY_OK; i < KEY_MAX; i++)
+ __set_bit(i, kbd->keybit);
+
+ ret = input_register_device(kbd);
+ if (ret) {
+ input_free_device(kbd);
+ xenbus_dev_fatal(dev, ret, "input_register_device(kbd)");
+ goto error;
+ }
+ info->kbd = kbd;
+
+ /* pointing device */
+ ptr = input_allocate_device();
+ if (!ptr)
+ goto error_nomem;
+ ptr->name = "Xen Virtual Pointer";
+ ptr->phys = info->phys;
+ ptr->id.bustype = BUS_PCI;
+ ptr->id.vendor = 0x5853;
+ ptr->id.product = 0xfffe;
+
+ if (abs) {
+ __set_bit(EV_ABS, ptr->evbit);
+ input_set_abs_params(ptr, ABS_X, 0, XENFB_WIDTH, 0, 0);
+ input_set_abs_params(ptr, ABS_Y, 0, XENFB_HEIGHT, 0, 0);
+ } else {
+ input_set_capability(ptr, EV_REL, REL_X);
+ input_set_capability(ptr, EV_REL, REL_Y);
+ }
+ input_set_capability(ptr, EV_REL, REL_WHEEL);
+
+ __set_bit(EV_KEY, ptr->evbit);
+ for (i = BTN_LEFT; i <= BTN_TASK; i++)
+ __set_bit(i, ptr->keybit);
+
+ ret = input_register_device(ptr);
+ if (ret) {
+ input_free_device(ptr);
+ xenbus_dev_fatal(dev, ret, "input_register_device(ptr)");
+ goto error;
+ }
+ info->ptr = ptr;
+
+ ret = xenkbd_connect_backend(dev, info);
+ if (ret < 0)
+ goto error;
+
+ return 0;
+
+ error_nomem:
+ ret = -ENOMEM;
+ xenbus_dev_fatal(dev, ret, "allocating device memory");
+ error:
+ xenkbd_remove(dev);
+ return ret;
+}
+
+static int xenkbd_resume(struct xenbus_device *dev)
+{
+ struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
+
+ xenkbd_disconnect_backend(info);
+ memset(info->page, 0, PAGE_SIZE);
+ return xenkbd_connect_backend(dev, info);
+}
+
+static int xenkbd_remove(struct xenbus_device *dev)
+{
+ struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
+
+ xenkbd_disconnect_backend(info);
+ if (info->kbd)
+ input_unregister_device(info->kbd);
+ if (info->ptr)
+ input_unregister_device(info->ptr);
+ free_page((unsigned long)info->page);
+ kfree(info);
+ return 0;
+}
+
+static int xenkbd_connect_backend(struct xenbus_device *dev,
+ struct xenkbd_info *info)
+{
+ int ret, evtchn;
+ struct xenbus_transaction xbt;
+
+ ret = gnttab_grant_foreign_access(dev->otherend_id,
+ virt_to_mfn(info->page), 0);
+ if (ret < 0)
+ return ret;
+ info->gref = ret;
+
+ ret = xenbus_alloc_evtchn(dev, &evtchn);
+ if (ret)
+ goto error_grant;
+ ret = bind_evtchn_to_irqhandler(evtchn, input_handler,
+ 0, dev->devicetype, info);
+ if (ret < 0) {
+ xenbus_dev_fatal(dev, ret, "bind_evtchn_to_irqhandler");
+ goto error_evtchan;
+ }
+ info->irq = ret;
+
+ again:
+ ret = xenbus_transaction_start(&xbt);
+ if (ret) {
+ xenbus_dev_fatal(dev, ret, "starting transaction");
+ goto error_irqh;
+ }
+ ret = xenbus_printf(xbt, dev->nodename, "page-ref", "%lu",
+ virt_to_mfn(info->page));
+ if (ret)
+ goto error_xenbus;
+ ret = xenbus_printf(xbt, dev->nodename, "page-gref", "%u", info->gref);
+ if (ret)
+ goto error_xenbus;
+ ret = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
+ evtchn);
+ if (ret)
+ goto error_xenbus;
+ ret = xenbus_transaction_end(xbt, 0);
+ if (ret) {
+ if (ret == -EAGAIN)
+ goto again;
+ xenbus_dev_fatal(dev, ret, "completing transaction");
+ goto error_irqh;
+ }
+
+ xenbus_switch_state(dev, XenbusStateInitialised);
+ return 0;
+
+ error_xenbus:
+ xenbus_transaction_end(xbt, 1);
+ xenbus_dev_fatal(dev, ret, "writing xenstore");
+ error_irqh:
+ unbind_from_irqhandler(info->irq, info);
+ info->irq = -1;
+ error_evtchan:
+ xenbus_free_evtchn(dev, evtchn);
+ error_grant:
+ gnttab_end_foreign_access_ref(info->gref, 0);
+ info->gref = -1;
+ return ret;
+}
+
+static void xenkbd_disconnect_backend(struct xenkbd_info *info)
+{
+ if (info->irq >= 0)
+ unbind_from_irqhandler(info->irq, info);
+ info->irq = -1;
+ if (info->gref >= 0)
+ gnttab_end_foreign_access_ref(info->gref, 0);
+ info->gref = -1;
+}
+
+static void xenkbd_backend_changed(struct xenbus_device *dev,
+ enum xenbus_state backend_state)
+{
+ struct xenkbd_info *info = dev_get_drvdata(&dev->dev);
+ int ret, val;
+
+ switch (backend_state) {
+ case XenbusStateInitialising:
+ case XenbusStateInitialised:
+ case XenbusStateReconfiguring:
+ case XenbusStateReconfigured:
+ case XenbusStateUnknown:
+ break;
+
+ case XenbusStateInitWait:
+InitWait:
+ ret = xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+ "feature-abs-pointer", "%d", &val);
+ if (ret < 0)
+ val = 0;
+ if (val) {
+ ret = xenbus_printf(XBT_NIL, info->xbdev->nodename,
+ "request-abs-pointer", "1");
+ if (ret)
+ pr_warning("xenkbd: can't request abs-pointer");
+ }
+
+ xenbus_switch_state(dev, XenbusStateConnected);
+ break;
+
+ case XenbusStateConnected:
+ /*
+ * Work around xenbus race condition: If backend goes
+ * through InitWait to Connected fast enough, we can
+ * get Connected twice here.
+ */
+ if (dev->state != XenbusStateConnected)
+ goto InitWait; /* no InitWait seen yet, fudge it */
+
+ /* Set input abs params to match backend screen res */
+ if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+ "width", "%d", &val) > 0)
+ input_set_abs_params(info->ptr, ABS_X, 0, val, 0, 0);
+
+ if (xenbus_scanf(XBT_NIL, info->xbdev->otherend,
+ "height", "%d", &val) > 0)
+ input_set_abs_params(info->ptr, ABS_Y, 0, val, 0, 0);
+
+ break;
+
+ case XenbusStateClosed:
+ if (dev->state == XenbusStateClosed)
+ break;
+ /* Missed the backend's CLOSING state -- fallthrough */
+ case XenbusStateClosing:
+ xenbus_frontend_closed(dev);
+ break;
+ }
+}
+
+static const struct xenbus_device_id xenkbd_ids[] = {
+ { "vkbd" },
+ { "" }
+};
+
+static DEFINE_XENBUS_DRIVER(xenkbd, ,
+ .probe = xenkbd_probe,
+ .remove = xenkbd_remove,
+ .resume = xenkbd_resume,
+ .otherend_changed = xenkbd_backend_changed,
+);
+
+static int __init xenkbd_init(void)
+{
+ if (!xen_domain())
+ return -ENODEV;
+
+ /* Nothing to do if running in dom0. */
+ if (xen_initial_domain())
+ return -ENODEV;
+
+ return xenbus_register_frontend(&xenkbd_driver);
+}
+
+static void __exit xenkbd_cleanup(void)
+{
+ xenbus_unregister_driver(&xenkbd_driver);
+}
+
+module_init(xenkbd_init);
+module_exit(xenkbd_cleanup);
+
+MODULE_DESCRIPTION("Xen virtual keyboard/pointer device frontend");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("xen:vkbd");
diff --git a/drivers/input/misc/yealink.c b/drivers/input/misc/yealink.c
new file mode 100644
index 00000000..285a5bd6
--- /dev/null
+++ b/drivers/input/misc/yealink.c
@@ -0,0 +1,1008 @@
+/*
+ * drivers/usb/input/yealink.c
+ *
+ * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * Description:
+ * Driver for the USB-P1K voip usb phone.
+ * This device is produced by Yealink Network Technology Co Ltd
+ * but may be branded under several names:
+ * - Yealink usb-p1k
+ * - Tiptel 115
+ * - ...
+ *
+ * This driver is based on:
+ * - the usbb2k-api http://savannah.nongnu.org/projects/usbb2k-api/
+ * - information from http://memeteau.free.fr/usbb2k
+ * - the xpad-driver drivers/input/joystick/xpad.c
+ *
+ * Thanks to:
+ * - Olivier Vandorpe, for providing the usbb2k-api.
+ * - Martin Diehl, for spotting my memory allocation bug.
+ *
+ * History:
+ * 20050527 henk First version, functional keyboard. Keyboard events
+ * will pop-up on the ../input/eventX bus.
+ * 20050531 henk Added led, LCD, dialtone and sysfs interface.
+ * 20050610 henk Cleanups, make it ready for public consumption.
+ * 20050630 henk Cleanups, fixes in response to comments.
+ * 20050701 henk sysfs write serialisation, fix potential unload races
+ * 20050801 henk Added ringtone, restructure USB
+ * 20050816 henk Merge 2.6.13-rc6
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/rwsem.h>
+#include <linux/usb/input.h>
+#include <linux/map_to_7segment.h>
+
+#include "yealink.h"
+
+#define DRIVER_VERSION "yld-20051230"
+#define DRIVER_AUTHOR "Henk Vergonet"
+#define DRIVER_DESC "Yealink phone driver"
+
+#define YEALINK_POLLING_FREQUENCY 10 /* in [Hz] */
+
+struct yld_status {
+ u8 lcd[24];
+ u8 led;
+ u8 dialtone;
+ u8 ringtone;
+ u8 keynum;
+} __attribute__ ((packed));
+
+/*
+ * Register the LCD segment and icon map
+ */
+#define _LOC(k,l) { .a = (k), .m = (l) }
+#define _SEG(t, a, am, b, bm, c, cm, d, dm, e, em, f, fm, g, gm) \
+ { .type = (t), \
+ .u = { .s = { _LOC(a, am), _LOC(b, bm), _LOC(c, cm), \
+ _LOC(d, dm), _LOC(e, em), _LOC(g, gm), \
+ _LOC(f, fm) } } }
+#define _PIC(t, h, hm, n) \
+ { .type = (t), \
+ .u = { .p = { .name = (n), .a = (h), .m = (hm) } } }
+
+static const struct lcd_segment_map {
+ char type;
+ union {
+ struct pictogram_map {
+ u8 a,m;
+ char name[10];
+ } p;
+ struct segment_map {
+ u8 a,m;
+ } s[7];
+ } u;
+} lcdMap[] = {
+#include "yealink.h"
+};
+
+struct yealink_dev {
+ struct input_dev *idev; /* input device */
+ struct usb_device *udev; /* usb device */
+ struct usb_interface *intf; /* usb interface */
+
+ /* irq input channel */
+ struct yld_ctl_packet *irq_data;
+ dma_addr_t irq_dma;
+ struct urb *urb_irq;
+
+ /* control output channel */
+ struct yld_ctl_packet *ctl_data;
+ dma_addr_t ctl_dma;
+ struct usb_ctrlrequest *ctl_req;
+ struct urb *urb_ctl;
+
+ char phys[64]; /* physical device path */
+
+ u8 lcdMap[ARRAY_SIZE(lcdMap)]; /* state of LCD, LED ... */
+ int key_code; /* last reported key */
+
+ unsigned int shutdown:1;
+
+ int stat_ix;
+ union {
+ struct yld_status s;
+ u8 b[sizeof(struct yld_status)];
+ } master, copy;
+};
+
+
+/*******************************************************************************
+ * Yealink lcd interface
+ ******************************************************************************/
+
+/*
+ * Register a default 7 segment character set
+ */
+static SEG7_DEFAULT_MAP(map_seg7);
+
+ /* Display a char,
+ * char '\9' and '\n' are placeholders and do not overwrite the original text.
+ * A space will always hide an icon.
+ */
+static int setChar(struct yealink_dev *yld, int el, int chr)
+{
+ int i, a, m, val;
+
+ if (el >= ARRAY_SIZE(lcdMap))
+ return -EINVAL;
+
+ if (chr == '\t' || chr == '\n')
+ return 0;
+
+ yld->lcdMap[el] = chr;
+
+ if (lcdMap[el].type == '.') {
+ a = lcdMap[el].u.p.a;
+ m = lcdMap[el].u.p.m;
+ if (chr != ' ')
+ yld->master.b[a] |= m;
+ else
+ yld->master.b[a] &= ~m;
+ return 0;
+ }
+
+ val = map_to_seg7(&map_seg7, chr);
+ for (i = 0; i < ARRAY_SIZE(lcdMap[0].u.s); i++) {
+ m = lcdMap[el].u.s[i].m;
+
+ if (m == 0)
+ continue;
+
+ a = lcdMap[el].u.s[i].a;
+ if (val & 1)
+ yld->master.b[a] |= m;
+ else
+ yld->master.b[a] &= ~m;
+ val = val >> 1;
+ }
+ return 0;
+};
+
+/*******************************************************************************
+ * Yealink key interface
+ ******************************************************************************/
+
+/* Map device buttons to internal key events.
+ *
+ * USB-P1K button layout:
+ *
+ * up
+ * IN OUT
+ * down
+ *
+ * pickup C hangup
+ * 1 2 3
+ * 4 5 6
+ * 7 8 9
+ * * 0 #
+ *
+ * The "up" and "down" keys, are symbolised by arrows on the button.
+ * The "pickup" and "hangup" keys are symbolised by a green and red phone
+ * on the button.
+ */
+static int map_p1k_to_key(int scancode)
+{
+ switch(scancode) { /* phone key: */
+ case 0x23: return KEY_LEFT; /* IN */
+ case 0x33: return KEY_UP; /* up */
+ case 0x04: return KEY_RIGHT; /* OUT */
+ case 0x24: return KEY_DOWN; /* down */
+ case 0x03: return KEY_ENTER; /* pickup */
+ case 0x14: return KEY_BACKSPACE; /* C */
+ case 0x13: return KEY_ESC; /* hangup */
+ case 0x00: return KEY_1; /* 1 */
+ case 0x01: return KEY_2; /* 2 */
+ case 0x02: return KEY_3; /* 3 */
+ case 0x10: return KEY_4; /* 4 */
+ case 0x11: return KEY_5; /* 5 */
+ case 0x12: return KEY_6; /* 6 */
+ case 0x20: return KEY_7; /* 7 */
+ case 0x21: return KEY_8; /* 8 */
+ case 0x22: return KEY_9; /* 9 */
+ case 0x30: return KEY_KPASTERISK; /* * */
+ case 0x31: return KEY_0; /* 0 */
+ case 0x32: return KEY_LEFTSHIFT |
+ KEY_3 << 8; /* # */
+ }
+ return -EINVAL;
+}
+
+/* Completes a request by converting the data into events for the
+ * input subsystem.
+ *
+ * The key parameter can be cascaded: key2 << 8 | key1
+ */
+static void report_key(struct yealink_dev *yld, int key)
+{
+ struct input_dev *idev = yld->idev;
+
+ if (yld->key_code >= 0) {
+ /* old key up */
+ input_report_key(idev, yld->key_code & 0xff, 0);
+ if (yld->key_code >> 8)
+ input_report_key(idev, yld->key_code >> 8, 0);
+ }
+
+ yld->key_code = key;
+ if (key >= 0) {
+ /* new valid key */
+ input_report_key(idev, key & 0xff, 1);
+ if (key >> 8)
+ input_report_key(idev, key >> 8, 1);
+ }
+ input_sync(idev);
+}
+
+/*******************************************************************************
+ * Yealink usb communication interface
+ ******************************************************************************/
+
+static int yealink_cmd(struct yealink_dev *yld, struct yld_ctl_packet *p)
+{
+ u8 *buf = (u8 *)p;
+ int i;
+ u8 sum = 0;
+
+ for(i=0; i<USB_PKT_LEN-1; i++)
+ sum -= buf[i];
+ p->sum = sum;
+ return usb_control_msg(yld->udev,
+ usb_sndctrlpipe(yld->udev, 0),
+ USB_REQ_SET_CONFIGURATION,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
+ 0x200, 3,
+ p, sizeof(*p),
+ USB_CTRL_SET_TIMEOUT);
+}
+
+static u8 default_ringtone[] = {
+ 0xEF, /* volume [0-255] */
+ 0xFB, 0x1E, 0x00, 0x0C, /* 1250 [hz], 12/100 [s] */
+ 0xFC, 0x18, 0x00, 0x0C, /* 1000 [hz], 12/100 [s] */
+ 0xFB, 0x1E, 0x00, 0x0C,
+ 0xFC, 0x18, 0x00, 0x0C,
+ 0xFB, 0x1E, 0x00, 0x0C,
+ 0xFC, 0x18, 0x00, 0x0C,
+ 0xFB, 0x1E, 0x00, 0x0C,
+ 0xFC, 0x18, 0x00, 0x0C,
+ 0xFF, 0xFF, 0x01, 0x90, /* silent, 400/100 [s] */
+ 0x00, 0x00 /* end of sequence */
+};
+
+static int yealink_set_ringtone(struct yealink_dev *yld, u8 *buf, size_t size)
+{
+ struct yld_ctl_packet *p = yld->ctl_data;
+ int ix, len;
+
+ if (size <= 0)
+ return -EINVAL;
+
+ /* Set the ringtone volume */
+ memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
+ yld->ctl_data->cmd = CMD_RING_VOLUME;
+ yld->ctl_data->size = 1;
+ yld->ctl_data->data[0] = buf[0];
+ yealink_cmd(yld, p);
+
+ buf++;
+ size--;
+
+ p->cmd = CMD_RING_NOTE;
+ ix = 0;
+ while (size != ix) {
+ len = size - ix;
+ if (len > sizeof(p->data))
+ len = sizeof(p->data);
+ p->size = len;
+ p->offset = cpu_to_be16(ix);
+ memcpy(p->data, &buf[ix], len);
+ yealink_cmd(yld, p);
+ ix += len;
+ }
+ return 0;
+}
+
+/* keep stat_master & stat_copy in sync.
+ */
+static int yealink_do_idle_tasks(struct yealink_dev *yld)
+{
+ u8 val;
+ int i, ix, len;
+
+ ix = yld->stat_ix;
+
+ memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
+ yld->ctl_data->cmd = CMD_KEYPRESS;
+ yld->ctl_data->size = 1;
+ yld->ctl_data->sum = 0xff - CMD_KEYPRESS;
+
+ /* If state update pointer wraps do a KEYPRESS first. */
+ if (ix >= sizeof(yld->master)) {
+ yld->stat_ix = 0;
+ return 0;
+ }
+
+ /* find update candidates: copy != master */
+ do {
+ val = yld->master.b[ix];
+ if (val != yld->copy.b[ix])
+ goto send_update;
+ } while (++ix < sizeof(yld->master));
+
+ /* nothing todo, wait a bit and poll for a KEYPRESS */
+ yld->stat_ix = 0;
+ /* TODO how can we wait abit. ??
+ * msleep_interruptible(1000 / YEALINK_POLLING_FREQUENCY);
+ */
+ return 0;
+
+send_update:
+
+ /* Setup an appropriate update request */
+ yld->copy.b[ix] = val;
+ yld->ctl_data->data[0] = val;
+
+ switch(ix) {
+ case offsetof(struct yld_status, led):
+ yld->ctl_data->cmd = CMD_LED;
+ yld->ctl_data->sum = -1 - CMD_LED - val;
+ break;
+ case offsetof(struct yld_status, dialtone):
+ yld->ctl_data->cmd = CMD_DIALTONE;
+ yld->ctl_data->sum = -1 - CMD_DIALTONE - val;
+ break;
+ case offsetof(struct yld_status, ringtone):
+ yld->ctl_data->cmd = CMD_RINGTONE;
+ yld->ctl_data->sum = -1 - CMD_RINGTONE - val;
+ break;
+ case offsetof(struct yld_status, keynum):
+ val--;
+ val &= 0x1f;
+ yld->ctl_data->cmd = CMD_SCANCODE;
+ yld->ctl_data->offset = cpu_to_be16(val);
+ yld->ctl_data->data[0] = 0;
+ yld->ctl_data->sum = -1 - CMD_SCANCODE - val;
+ break;
+ default:
+ len = sizeof(yld->master.s.lcd) - ix;
+ if (len > sizeof(yld->ctl_data->data))
+ len = sizeof(yld->ctl_data->data);
+
+ /* Combine up to <len> consecutive LCD bytes in a singe request
+ */
+ yld->ctl_data->cmd = CMD_LCD;
+ yld->ctl_data->offset = cpu_to_be16(ix);
+ yld->ctl_data->size = len;
+ yld->ctl_data->sum = -CMD_LCD - ix - val - len;
+ for(i=1; i<len; i++) {
+ ix++;
+ val = yld->master.b[ix];
+ yld->copy.b[ix] = val;
+ yld->ctl_data->data[i] = val;
+ yld->ctl_data->sum -= val;
+ }
+ }
+ yld->stat_ix = ix + 1;
+ return 1;
+}
+
+/* Decide on how to handle responses
+ *
+ * The state transition diagram is somethhing like:
+ *
+ * syncState<--+
+ * | |
+ * | idle
+ * \|/ |
+ * init --ok--> waitForKey --ok--> getKey
+ * ^ ^ |
+ * | +-------ok-------+
+ * error,start
+ *
+ */
+static void urb_irq_callback(struct urb *urb)
+{
+ struct yealink_dev *yld = urb->context;
+ int ret, status = urb->status;
+
+ if (status)
+ dev_err(&yld->intf->dev, "%s - urb status %d\n",
+ __func__, status);
+
+ switch (yld->irq_data->cmd) {
+ case CMD_KEYPRESS:
+
+ yld->master.s.keynum = yld->irq_data->data[0];
+ break;
+
+ case CMD_SCANCODE:
+ dev_dbg(&yld->intf->dev, "get scancode %x\n",
+ yld->irq_data->data[0]);
+
+ report_key(yld, map_p1k_to_key(yld->irq_data->data[0]));
+ break;
+
+ default:
+ dev_err(&yld->intf->dev, "unexpected response %x\n",
+ yld->irq_data->cmd);
+ }
+
+ yealink_do_idle_tasks(yld);
+
+ if (!yld->shutdown) {
+ ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
+ if (ret && ret != -EPERM)
+ dev_err(&yld->intf->dev,
+ "%s - usb_submit_urb failed %d\n",
+ __func__, ret);
+ }
+}
+
+static void urb_ctl_callback(struct urb *urb)
+{
+ struct yealink_dev *yld = urb->context;
+ int ret = 0, status = urb->status;
+
+ if (status)
+ dev_err(&yld->intf->dev, "%s - urb status %d\n",
+ __func__, status);
+
+ switch (yld->ctl_data->cmd) {
+ case CMD_KEYPRESS:
+ case CMD_SCANCODE:
+ /* ask for a response */
+ if (!yld->shutdown)
+ ret = usb_submit_urb(yld->urb_irq, GFP_ATOMIC);
+ break;
+ default:
+ /* send new command */
+ yealink_do_idle_tasks(yld);
+ if (!yld->shutdown)
+ ret = usb_submit_urb(yld->urb_ctl, GFP_ATOMIC);
+ break;
+ }
+
+ if (ret && ret != -EPERM)
+ dev_err(&yld->intf->dev, "%s - usb_submit_urb failed %d\n",
+ __func__, ret);
+}
+
+/*******************************************************************************
+ * input event interface
+ ******************************************************************************/
+
+/* TODO should we issue a ringtone on a SND_BELL event?
+static int input_ev(struct input_dev *dev, unsigned int type,
+ unsigned int code, int value)
+{
+
+ if (type != EV_SND)
+ return -EINVAL;
+
+ switch (code) {
+ case SND_BELL:
+ case SND_TONE:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+*/
+
+static int input_open(struct input_dev *dev)
+{
+ struct yealink_dev *yld = input_get_drvdata(dev);
+ int i, ret;
+
+ dev_dbg(&yld->intf->dev, "%s\n", __func__);
+
+ /* force updates to device */
+ for (i = 0; i<sizeof(yld->master); i++)
+ yld->copy.b[i] = ~yld->master.b[i];
+ yld->key_code = -1; /* no keys pressed */
+
+ yealink_set_ringtone(yld, default_ringtone, sizeof(default_ringtone));
+
+ /* issue INIT */
+ memset(yld->ctl_data, 0, sizeof(*(yld->ctl_data)));
+ yld->ctl_data->cmd = CMD_INIT;
+ yld->ctl_data->size = 10;
+ yld->ctl_data->sum = 0x100-CMD_INIT-10;
+ if ((ret = usb_submit_urb(yld->urb_ctl, GFP_KERNEL)) != 0) {
+ dev_dbg(&yld->intf->dev,
+ "%s - usb_submit_urb failed with result %d\n",
+ __func__, ret);
+ return ret;
+ }
+ return 0;
+}
+
+static void input_close(struct input_dev *dev)
+{
+ struct yealink_dev *yld = input_get_drvdata(dev);
+
+ yld->shutdown = 1;
+ /*
+ * Make sure the flag is seen by other CPUs before we start
+ * killing URBs so new URBs won't be submitted
+ */
+ smp_wmb();
+
+ usb_kill_urb(yld->urb_ctl);
+ usb_kill_urb(yld->urb_irq);
+
+ yld->shutdown = 0;
+ smp_wmb();
+}
+
+/*******************************************************************************
+ * sysfs interface
+ ******************************************************************************/
+
+static DECLARE_RWSEM(sysfs_rwsema);
+
+/* Interface to the 7-segments translation table aka. char set.
+ */
+static ssize_t show_map(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ memcpy(buf, &map_seg7, sizeof(map_seg7));
+ return sizeof(map_seg7);
+}
+
+static ssize_t store_map(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t cnt)
+{
+ if (cnt != sizeof(map_seg7))
+ return -EINVAL;
+ memcpy(&map_seg7, buf, sizeof(map_seg7));
+ return sizeof(map_seg7);
+}
+
+/* Interface to the LCD.
+ */
+
+/* Reading /sys/../lineX will return the format string with its settings:
+ *
+ * Example:
+ * cat ./line3
+ * 888888888888
+ * Linux Rocks!
+ */
+static ssize_t show_line(struct device *dev, char *buf, int a, int b)
+{
+ struct yealink_dev *yld;
+ int i;
+
+ down_read(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_read(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ for (i = a; i < b; i++)
+ *buf++ = lcdMap[i].type;
+ *buf++ = '\n';
+ for (i = a; i < b; i++)
+ *buf++ = yld->lcdMap[i];
+ *buf++ = '\n';
+ *buf = 0;
+
+ up_read(&sysfs_rwsema);
+ return 3 + ((b - a) << 1);
+}
+
+static ssize_t show_line1(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return show_line(dev, buf, LCD_LINE1_OFFSET, LCD_LINE2_OFFSET);
+}
+
+static ssize_t show_line2(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return show_line(dev, buf, LCD_LINE2_OFFSET, LCD_LINE3_OFFSET);
+}
+
+static ssize_t show_line3(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return show_line(dev, buf, LCD_LINE3_OFFSET, LCD_LINE4_OFFSET);
+}
+
+/* Writing to /sys/../lineX will set the coresponding LCD line.
+ * - Excess characters are ignored.
+ * - If less characters are written than allowed, the remaining digits are
+ * unchanged.
+ * - The '\n' or '\t' char is a placeholder, it does not overwrite the
+ * original content.
+ */
+static ssize_t store_line(struct device *dev, const char *buf, size_t count,
+ int el, size_t len)
+{
+ struct yealink_dev *yld;
+ int i;
+
+ down_write(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_write(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ if (len > count)
+ len = count;
+ for (i = 0; i < len; i++)
+ setChar(yld, el++, buf[i]);
+
+ up_write(&sysfs_rwsema);
+ return count;
+}
+
+static ssize_t store_line1(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return store_line(dev, buf, count, LCD_LINE1_OFFSET, LCD_LINE1_SIZE);
+}
+
+static ssize_t store_line2(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return store_line(dev, buf, count, LCD_LINE2_OFFSET, LCD_LINE2_SIZE);
+}
+
+static ssize_t store_line3(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return store_line(dev, buf, count, LCD_LINE3_OFFSET, LCD_LINE3_SIZE);
+}
+
+/* Interface to visible and audible "icons", these include:
+ * pictures on the LCD, the LED, and the dialtone signal.
+ */
+
+/* Get a list of "switchable elements" with their current state. */
+static ssize_t get_icons(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct yealink_dev *yld;
+ int i, ret = 1;
+
+ down_read(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_read(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
+ if (lcdMap[i].type != '.')
+ continue;
+ ret += sprintf(&buf[ret], "%s %s\n",
+ yld->lcdMap[i] == ' ' ? " " : "on",
+ lcdMap[i].u.p.name);
+ }
+ up_read(&sysfs_rwsema);
+ return ret;
+}
+
+/* Change the visibility of a particular element. */
+static ssize_t set_icon(struct device *dev, const char *buf, size_t count,
+ int chr)
+{
+ struct yealink_dev *yld;
+ int i;
+
+ down_write(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_write(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lcdMap); i++) {
+ if (lcdMap[i].type != '.')
+ continue;
+ if (strncmp(buf, lcdMap[i].u.p.name, count) == 0) {
+ setChar(yld, i, chr);
+ break;
+ }
+ }
+
+ up_write(&sysfs_rwsema);
+ return count;
+}
+
+static ssize_t show_icon(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return set_icon(dev, buf, count, buf[0]);
+}
+
+static ssize_t hide_icon(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ return set_icon(dev, buf, count, ' ');
+}
+
+/* Upload a ringtone to the device.
+ */
+
+/* Stores raw ringtone data in the phone */
+static ssize_t store_ringtone(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct yealink_dev *yld;
+
+ down_write(&sysfs_rwsema);
+ yld = dev_get_drvdata(dev);
+ if (yld == NULL) {
+ up_write(&sysfs_rwsema);
+ return -ENODEV;
+ }
+
+ /* TODO locking with async usb control interface??? */
+ yealink_set_ringtone(yld, (char *)buf, count);
+ up_write(&sysfs_rwsema);
+ return count;
+}
+
+#define _M444 S_IRUGO
+#define _M664 S_IRUGO|S_IWUSR|S_IWGRP
+#define _M220 S_IWUSR|S_IWGRP
+
+static DEVICE_ATTR(map_seg7 , _M664, show_map , store_map );
+static DEVICE_ATTR(line1 , _M664, show_line1 , store_line1 );
+static DEVICE_ATTR(line2 , _M664, show_line2 , store_line2 );
+static DEVICE_ATTR(line3 , _M664, show_line3 , store_line3 );
+static DEVICE_ATTR(get_icons , _M444, get_icons , NULL );
+static DEVICE_ATTR(show_icon , _M220, NULL , show_icon );
+static DEVICE_ATTR(hide_icon , _M220, NULL , hide_icon );
+static DEVICE_ATTR(ringtone , _M220, NULL , store_ringtone);
+
+static struct attribute *yld_attributes[] = {
+ &dev_attr_line1.attr,
+ &dev_attr_line2.attr,
+ &dev_attr_line3.attr,
+ &dev_attr_get_icons.attr,
+ &dev_attr_show_icon.attr,
+ &dev_attr_hide_icon.attr,
+ &dev_attr_map_seg7.attr,
+ &dev_attr_ringtone.attr,
+ NULL
+};
+
+static struct attribute_group yld_attr_group = {
+ .attrs = yld_attributes
+};
+
+/*******************************************************************************
+ * Linux interface and usb initialisation
+ ******************************************************************************/
+
+struct driver_info {
+ char *name;
+};
+
+static const struct driver_info info_P1K = {
+ .name = "Yealink usb-p1k",
+};
+
+static const struct usb_device_id usb_table [] = {
+ {
+ .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+ USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x6993,
+ .idProduct = 0xb001,
+ .bInterfaceClass = USB_CLASS_HID,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ .driver_info = (kernel_ulong_t)&info_P1K
+ },
+ { }
+};
+
+static int usb_cleanup(struct yealink_dev *yld, int err)
+{
+ if (yld == NULL)
+ return err;
+
+ if (yld->idev) {
+ if (err)
+ input_free_device(yld->idev);
+ else
+ input_unregister_device(yld->idev);
+ }
+
+ usb_free_urb(yld->urb_irq);
+ usb_free_urb(yld->urb_ctl);
+
+ kfree(yld->ctl_req);
+ usb_free_coherent(yld->udev, USB_PKT_LEN, yld->ctl_data, yld->ctl_dma);
+ usb_free_coherent(yld->udev, USB_PKT_LEN, yld->irq_data, yld->irq_dma);
+
+ kfree(yld);
+ return err;
+}
+
+static void usb_disconnect(struct usb_interface *intf)
+{
+ struct yealink_dev *yld;
+
+ down_write(&sysfs_rwsema);
+ yld = usb_get_intfdata(intf);
+ sysfs_remove_group(&intf->dev.kobj, &yld_attr_group);
+ usb_set_intfdata(intf, NULL);
+ up_write(&sysfs_rwsema);
+
+ usb_cleanup(yld, 0);
+}
+
+static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev (intf);
+ struct driver_info *nfo = (struct driver_info *)id->driver_info;
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct yealink_dev *yld;
+ struct input_dev *input_dev;
+ int ret, pipe, i;
+
+ interface = intf->cur_altsetting;
+ endpoint = &interface->endpoint[0].desc;
+ if (!usb_endpoint_is_int_in(endpoint))
+ return -ENODEV;
+
+ yld = kzalloc(sizeof(struct yealink_dev), GFP_KERNEL);
+ if (!yld)
+ return -ENOMEM;
+
+ yld->udev = udev;
+ yld->intf = intf;
+
+ yld->idev = input_dev = input_allocate_device();
+ if (!input_dev)
+ return usb_cleanup(yld, -ENOMEM);
+
+ /* allocate usb buffers */
+ yld->irq_data = usb_alloc_coherent(udev, USB_PKT_LEN,
+ GFP_ATOMIC, &yld->irq_dma);
+ if (yld->irq_data == NULL)
+ return usb_cleanup(yld, -ENOMEM);
+
+ yld->ctl_data = usb_alloc_coherent(udev, USB_PKT_LEN,
+ GFP_ATOMIC, &yld->ctl_dma);
+ if (!yld->ctl_data)
+ return usb_cleanup(yld, -ENOMEM);
+
+ yld->ctl_req = kmalloc(sizeof(*(yld->ctl_req)), GFP_KERNEL);
+ if (yld->ctl_req == NULL)
+ return usb_cleanup(yld, -ENOMEM);
+
+ /* allocate urb structures */
+ yld->urb_irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (yld->urb_irq == NULL)
+ return usb_cleanup(yld, -ENOMEM);
+
+ yld->urb_ctl = usb_alloc_urb(0, GFP_KERNEL);
+ if (yld->urb_ctl == NULL)
+ return usb_cleanup(yld, -ENOMEM);
+
+ /* get a handle to the interrupt data pipe */
+ pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
+ ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+ if (ret != USB_PKT_LEN)
+ dev_err(&intf->dev, "invalid payload size %d, expected %zd\n",
+ ret, USB_PKT_LEN);
+
+ /* initialise irq urb */
+ usb_fill_int_urb(yld->urb_irq, udev, pipe, yld->irq_data,
+ USB_PKT_LEN,
+ urb_irq_callback,
+ yld, endpoint->bInterval);
+ yld->urb_irq->transfer_dma = yld->irq_dma;
+ yld->urb_irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ yld->urb_irq->dev = udev;
+
+ /* initialise ctl urb */
+ yld->ctl_req->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE |
+ USB_DIR_OUT;
+ yld->ctl_req->bRequest = USB_REQ_SET_CONFIGURATION;
+ yld->ctl_req->wValue = cpu_to_le16(0x200);
+ yld->ctl_req->wIndex = cpu_to_le16(interface->desc.bInterfaceNumber);
+ yld->ctl_req->wLength = cpu_to_le16(USB_PKT_LEN);
+
+ usb_fill_control_urb(yld->urb_ctl, udev, usb_sndctrlpipe(udev, 0),
+ (void *)yld->ctl_req, yld->ctl_data, USB_PKT_LEN,
+ urb_ctl_callback, yld);
+ yld->urb_ctl->transfer_dma = yld->ctl_dma;
+ yld->urb_ctl->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ yld->urb_ctl->dev = udev;
+
+ /* find out the physical bus location */
+ usb_make_path(udev, yld->phys, sizeof(yld->phys));
+ strlcat(yld->phys, "/input0", sizeof(yld->phys));
+
+ /* register settings for the input device */
+ input_dev->name = nfo->name;
+ input_dev->phys = yld->phys;
+ usb_to_input_id(udev, &input_dev->id);
+ input_dev->dev.parent = &intf->dev;
+
+ input_set_drvdata(input_dev, yld);
+
+ input_dev->open = input_open;
+ input_dev->close = input_close;
+ /* input_dev->event = input_ev; TODO */
+
+ /* register available key events */
+ input_dev->evbit[0] = BIT_MASK(EV_KEY);
+ for (i = 0; i < 256; i++) {
+ int k = map_p1k_to_key(i);
+ if (k >= 0) {
+ set_bit(k & 0xff, input_dev->keybit);
+ if (k >> 8)
+ set_bit(k >> 8, input_dev->keybit);
+ }
+ }
+
+ ret = input_register_device(yld->idev);
+ if (ret)
+ return usb_cleanup(yld, ret);
+
+ usb_set_intfdata(intf, yld);
+
+ /* clear visible elements */
+ for (i = 0; i < ARRAY_SIZE(lcdMap); i++)
+ setChar(yld, i, ' ');
+
+ /* display driver version on LCD line 3 */
+ store_line3(&intf->dev, NULL,
+ DRIVER_VERSION, sizeof(DRIVER_VERSION));
+
+ /* Register sysfs hooks (don't care about failure) */
+ ret = sysfs_create_group(&intf->dev.kobj, &yld_attr_group);
+ return 0;
+}
+
+static struct usb_driver yealink_driver = {
+ .name = "yealink",
+ .probe = usb_probe,
+ .disconnect = usb_disconnect,
+ .id_table = usb_table,
+};
+
+module_usb_driver(yealink_driver);
+
+MODULE_DEVICE_TABLE (usb, usb_table);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/yealink.h b/drivers/input/misc/yealink.h
new file mode 100644
index 00000000..1e0f5239
--- /dev/null
+++ b/drivers/input/misc/yealink.h
@@ -0,0 +1,220 @@
+/*
+ * drivers/usb/input/yealink.h
+ *
+ * Copyright (c) 2005 Henk Vergonet <Henk.Vergonet@gmail.com>
+ *
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef INPUT_YEALINK_H
+#define INPUT_YEALINK_H
+
+/* Using the control channel on interface 3 various aspects of the phone
+ * can be controlled like LCD, LED, dialtone and the ringtone.
+ */
+
+struct yld_ctl_packet {
+ u8 cmd; /* command code, see below */
+ u8 size; /* 1-11, size of used data bytes. */
+ u16 offset; /* internal packet offset */
+ u8 data[11];
+ s8 sum; /* negative sum of 15 preceding bytes */
+} __attribute__ ((packed));
+
+#define USB_PKT_LEN sizeof(struct yld_ctl_packet)
+
+/* The following yld_ctl_packet's are available: */
+
+/* Init registers
+ *
+ * cmd 0x8e
+ * size 10
+ * offset 0
+ * data 0,0,0,0....
+ */
+#define CMD_INIT 0x8e
+
+/* Request key scan
+ *
+ * cmd 0x80
+ * size 1
+ * offset 0
+ * data[0] on return returns the key number, if it changes there's a new
+ * key pressed.
+ */
+#define CMD_KEYPRESS 0x80
+
+/* Request scancode
+ *
+ * cmd 0x81
+ * size 1
+ * offset key number [0-1f]
+ * data[0] on return returns the scancode
+ */
+#define CMD_SCANCODE 0x81
+
+/* Set LCD
+ *
+ * cmd 0x04
+ * size 1-11
+ * offset 0-23
+ * data segment bits
+ */
+#define CMD_LCD 0x04
+
+/* Set led
+ *
+ * cmd 0x05
+ * size 1
+ * offset 0
+ * data[0] 0 OFF / 1 ON
+ */
+#define CMD_LED 0x05
+
+/* Set ringtone volume
+ *
+ * cmd 0x11
+ * size 1
+ * offset 0
+ * data[0] 0-0xff volume
+ */
+#define CMD_RING_VOLUME 0x11
+
+/* Set ringtone notes
+ *
+ * cmd 0x02
+ * size 1-11
+ * offset 0->
+ * data binary representation LE16(-freq), LE16(duration) ....
+ */
+#define CMD_RING_NOTE 0x02
+
+/* Sound ringtone via the speaker on the back
+ *
+ * cmd 0x03
+ * size 1
+ * offset 0
+ * data[0] 0 OFF / 0x24 ON
+ */
+#define CMD_RINGTONE 0x03
+
+/* Sound dial tone via the ear speaker
+ *
+ * cmd 0x09
+ * size 1
+ * offset 0
+ * data[0] 0 OFF / 1 ON
+ */
+#define CMD_DIALTONE 0x09
+
+#endif /* INPUT_YEALINK_H */
+
+
+#if defined(_SEG) && defined(_PIC)
+/* This table maps the LCD segments onto individual bit positions in the
+ * yld_status struct.
+ */
+
+/* LCD, each segment must be driven separately.
+ *
+ * Layout:
+ *
+ * |[] [][] [][] [][] in |[][]
+ * |[] M [][] D [][] : [][] out |[][]
+ * store
+ *
+ * NEW REP SU MO TU WE TH FR SA
+ *
+ * [] [] [] [] [] [] [] [] [] [] [] []
+ * [] [] [] [] [] [] [] [] [] [] [] []
+ */
+
+/* Line 1
+ * Format : 18.e8.M8.88...188
+ * Icon names : M D : IN OUT STORE
+ */
+#define LCD_LINE1_OFFSET 0
+#define LCD_LINE1_SIZE 17
+
+/* Note: first g then f => ! ! */
+/* _SEG( type a b c d e g f ) */
+ _SEG('1', 0,0 , 22,2 , 22,2 , 0,0 , 0,0 , 0,0 , 0,0 ),
+ _SEG('8', 20,1 , 20,2 , 20,4 , 20,8 , 21,4 , 21,2 , 21,1 ),
+ _PIC('.', 22,1 , "M" ),
+ _SEG('e', 18,1 , 18,2 , 18,4 , 18,1 , 19,2 , 18,1 , 19,1 ),
+ _SEG('8', 16,1 , 16,2 , 16,4 , 16,8 , 17,4 , 17,2 , 17,1 ),
+ _PIC('.', 15,8 , "D" ),
+ _SEG('M', 14,1 , 14,2 , 14,4 , 14,1 , 15,4 , 15,2 , 15,1 ),
+ _SEG('8', 12,1 , 12,2 , 12,4 , 12,8 , 13,4 , 13,2 , 13,1 ),
+ _PIC('.', 11,8 , ":" ),
+ _SEG('8', 10,1 , 10,2 , 10,4 , 10,8 , 11,4 , 11,2 , 11,1 ),
+ _SEG('8', 8,1 , 8,2 , 8,4 , 8,8 , 9,4 , 9,2 , 9,1 ),
+ _PIC('.', 7,1 , "IN" ),
+ _PIC('.', 7,2 , "OUT" ),
+ _PIC('.', 7,4 , "STORE" ),
+ _SEG('1', 0,0 , 5,1 , 5,1 , 0,0 , 0,0 , 0,0 , 0,0 ),
+ _SEG('8', 4,1 , 4,2 , 4,4 , 4,8 , 5,8 , 5,4 , 5,2 ),
+ _SEG('8', 2,1 , 2,2 , 2,4 , 2,8 , 3,4 , 3,2 , 3,1 ),
+
+/* Line 2
+ * Format : .........
+ * Pict. name : NEW REP SU MO TU WE TH FR SA
+ */
+#define LCD_LINE2_OFFSET LCD_LINE1_OFFSET + LCD_LINE1_SIZE
+#define LCD_LINE2_SIZE 9
+
+ _PIC('.', 23,2 , "NEW" ),
+ _PIC('.', 23,4 , "REP" ),
+ _PIC('.', 1,8 , "SU" ),
+ _PIC('.', 1,4 , "MO" ),
+ _PIC('.', 1,2 , "TU" ),
+ _PIC('.', 1,1 , "WE" ),
+ _PIC('.', 0,1 , "TH" ),
+ _PIC('.', 0,2 , "FR" ),
+ _PIC('.', 0,4 , "SA" ),
+
+/* Line 3
+ * Format : 888888888888
+ */
+#define LCD_LINE3_OFFSET LCD_LINE2_OFFSET + LCD_LINE2_SIZE
+#define LCD_LINE3_SIZE 12
+
+ _SEG('8', 22,16, 22,32, 22,64, 22,128, 23,128, 23,64, 23,32 ),
+ _SEG('8', 20,16, 20,32, 20,64, 20,128, 21,128, 21,64, 21,32 ),
+ _SEG('8', 18,16, 18,32, 18,64, 18,128, 19,128, 19,64, 19,32 ),
+ _SEG('8', 16,16, 16,32, 16,64, 16,128, 17,128, 17,64, 17,32 ),
+ _SEG('8', 14,16, 14,32, 14,64, 14,128, 15,128, 15,64, 15,32 ),
+ _SEG('8', 12,16, 12,32, 12,64, 12,128, 13,128, 13,64, 13,32 ),
+ _SEG('8', 10,16, 10,32, 10,64, 10,128, 11,128, 11,64, 11,32 ),
+ _SEG('8', 8,16, 8,32, 8,64, 8,128, 9,128, 9,64, 9,32 ),
+ _SEG('8', 6,16, 6,32, 6,64, 6,128, 7,128, 7,64, 7,32 ),
+ _SEG('8', 4,16, 4,32, 4,64, 4,128, 5,128, 5,64, 5,32 ),
+ _SEG('8', 2,16, 2,32, 2,64, 2,128, 3,128, 3,64, 3,32 ),
+ _SEG('8', 0,16, 0,32, 0,64, 0,128, 1,128, 1,64, 1,32 ),
+
+/* Line 4
+ *
+ * The LED, DIALTONE and RINGTONE are implemented as icons and use the same
+ * sysfs interface.
+ */
+#define LCD_LINE4_OFFSET LCD_LINE3_OFFSET + LCD_LINE3_SIZE
+
+ _PIC('.', offsetof(struct yld_status, led) , 0x01, "LED" ),
+ _PIC('.', offsetof(struct yld_status, dialtone) , 0x01, "DIALTONE" ),
+ _PIC('.', offsetof(struct yld_status, ringtone) , 0x24, "RINGTONE" ),
+
+#undef _SEG
+#undef _PIC
+#endif /* _SEG && _PIC */