Initial Contribution
msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142
Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index ce28bf6..9acebc0 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -321,6 +321,18 @@
help
Say Y here if you want to support gpio based keys, wheels etc...
+config INPUT_ISA1200_FF_MEMLESS
+ tristate "ISA1200 haptic ff-memless support"
+ depends on I2C
+ select INPUT_FF_MEMLESS
+ help
+ ISA1200 is a high performance enhanced haptic chip.
+ Say Y here if you want to support ISA1200 connected via I2C,
+ and select N if you are unsure.
+
+ To compile this driver as a module, choose M here: the
+ module will be called isa1200-ff-memless.
+
config HP_SDC_RTC
tristate "HP SDC Real Time Clock"
depends on (GSC || HP300) && SERIO
@@ -505,4 +517,43 @@
To compile this driver as a module, choose M here: the
module will be called xen-kbdfront.
+config PMIC8058_PWRKEY
+ tristate "PMIC8058 power key support"
+ default n
+ depends on PMIC8058
+ help
+ Say Y here if you want support for the PMIC8058 power key.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pmic8058-pwrkey.
+
+config PMIC8058_OTHC
+ tristate "Qualcomm PMIC8058 OTHC support"
+ default n
+ depends on PMIC8058
+ help
+ Say Y here if you want support PMIC8058 OTHC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pmic8058-othc.
+
+config INPUT_PMIC8058_VIBRA_MEMLESS
+ tristate "Qualcomm PM8058 vibrator support (ff-memless)"
+ depends on PMIC8058 && INPUT_FF_MEMLESS
+ default n
+ help
+ This option enables device driver support for the vibrator
+ on Qualcomm PM8058 chip. This driver supports ff-memless interface
+ from input framework.
+
+ To compile this driver as module, choose M here: the
+ module will be called pmic8058-vib-memless.
+
+config BOSCH_BMA150
+ tristate "SMB380/BMA150 acceleration sensor support"
+ depends on I2C=y
+ help
+ If you say yes here you get support for Bosch Sensortec's
+ acceleration sensors SMB380/BMA150.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 014d45f..770eb96 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -33,6 +33,7 @@
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.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_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
@@ -48,4 +49,8 @@
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_PMIC8058_PWRKEY) += pmic8058-pwrkey.o
+obj-$(CONFIG_PMIC8058_OTHC) += pmic8058-othc.o
+obj-$(CONFIG_INPUT_PMIC8058_VIBRA_MEMLESS) += pmic8058-vib-memless.o
+obj-$(CONFIG_BOSCH_BMA150) += bma150.o
diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c
new file mode 100644
index 0000000..8911c0b
--- /dev/null
+++ b/drivers/input/misc/bma150.c
@@ -0,0 +1,791 @@
+/* Date: 2011/3/7 11:00:00
+ * Revision: 2.11
+ */
+
+/*
+ * This software program is licensed subject to the GNU General Public License
+ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html
+
+ * (C) Copyright 2011 Bosch Sensortec GmbH
+ * All Rights Reserved
+ */
+
+
+/* file BMA150.c
+ brief This file contains all function implementations for the BMA150 in linux
+
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/bma150.h>
+
+#define SENSOR_NAME "bma150"
+#define GRAVITY_EARTH 9806550
+#define ABSMIN_2G (-GRAVITY_EARTH * 2)
+#define ABSMAX_2G (GRAVITY_EARTH * 2)
+#define BMA150_MAX_DELAY 200
+#define BMA150_CHIP_ID 2
+#define BMA150_RANGE_SET 0
+#define BMA150_BW_SET 4
+
+
+
+#define BMA150_CHIP_ID_REG 0x00
+#define BMA150_X_AXIS_LSB_REG 0x02
+#define BMA150_X_AXIS_MSB_REG 0x03
+#define BMA150_Y_AXIS_LSB_REG 0x04
+#define BMA150_Y_AXIS_MSB_REG 0x05
+#define BMA150_Z_AXIS_LSB_REG 0x06
+#define BMA150_Z_AXIS_MSB_REG 0x07
+#define BMA150_STATUS_REG 0x09
+#define BMA150_CTRL_REG 0x0a
+#define BMA150_CONF1_REG 0x0b
+
+#define BMA150_CUSTOMER1_REG 0x12
+#define BMA150_CUSTOMER2_REG 0x13
+#define BMA150_RANGE_BWIDTH_REG 0x14
+#define BMA150_CONF2_REG 0x15
+
+#define BMA150_OFFS_GAIN_X_REG 0x16
+#define BMA150_OFFS_GAIN_Y_REG 0x17
+#define BMA150_OFFS_GAIN_Z_REG 0x18
+#define BMA150_OFFS_GAIN_T_REG 0x19
+#define BMA150_OFFSET_X_REG 0x1a
+#define BMA150_OFFSET_Y_REG 0x1b
+#define BMA150_OFFSET_Z_REG 0x1c
+#define BMA150_OFFSET_T_REG 0x1d
+
+#define BMA150_CHIP_ID__POS 0
+#define BMA150_CHIP_ID__MSK 0x07
+#define BMA150_CHIP_ID__LEN 3
+#define BMA150_CHIP_ID__REG BMA150_CHIP_ID_REG
+
+/* DATA REGISTERS */
+
+#define BMA150_NEW_DATA_X__POS 0
+#define BMA150_NEW_DATA_X__LEN 1
+#define BMA150_NEW_DATA_X__MSK 0x01
+#define BMA150_NEW_DATA_X__REG BMA150_X_AXIS_LSB_REG
+
+#define BMA150_ACC_X_LSB__POS 6
+#define BMA150_ACC_X_LSB__LEN 2
+#define BMA150_ACC_X_LSB__MSK 0xC0
+#define BMA150_ACC_X_LSB__REG BMA150_X_AXIS_LSB_REG
+
+#define BMA150_ACC_X_MSB__POS 0
+#define BMA150_ACC_X_MSB__LEN 8
+#define BMA150_ACC_X_MSB__MSK 0xFF
+#define BMA150_ACC_X_MSB__REG BMA150_X_AXIS_MSB_REG
+
+#define BMA150_ACC_Y_LSB__POS 6
+#define BMA150_ACC_Y_LSB__LEN 2
+#define BMA150_ACC_Y_LSB__MSK 0xC0
+#define BMA150_ACC_Y_LSB__REG BMA150_Y_AXIS_LSB_REG
+
+#define BMA150_ACC_Y_MSB__POS 0
+#define BMA150_ACC_Y_MSB__LEN 8
+#define BMA150_ACC_Y_MSB__MSK 0xFF
+#define BMA150_ACC_Y_MSB__REG BMA150_Y_AXIS_MSB_REG
+
+#define BMA150_ACC_Z_LSB__POS 6
+#define BMA150_ACC_Z_LSB__LEN 2
+#define BMA150_ACC_Z_LSB__MSK 0xC0
+#define BMA150_ACC_Z_LSB__REG BMA150_Z_AXIS_LSB_REG
+
+#define BMA150_ACC_Z_MSB__POS 0
+#define BMA150_ACC_Z_MSB__LEN 8
+#define BMA150_ACC_Z_MSB__MSK 0xFF
+#define BMA150_ACC_Z_MSB__REG BMA150_Z_AXIS_MSB_REG
+
+/* CONTROL BITS */
+
+#define BMA150_SLEEP__POS 0
+#define BMA150_SLEEP__LEN 1
+#define BMA150_SLEEP__MSK 0x01
+#define BMA150_SLEEP__REG BMA150_CTRL_REG
+
+#define BMA150_SOFT_RESET__POS 1
+#define BMA150_SOFT_RESET__LEN 1
+#define BMA150_SOFT_RESET__MSK 0x02
+#define BMA150_SOFT_RESET__REG BMA150_CTRL_REG
+
+#define BMA150_EE_W__POS 4
+#define BMA150_EE_W__LEN 1
+#define BMA150_EE_W__MSK 0x10
+#define BMA150_EE_W__REG BMA150_CTRL_REG
+
+#define BMA150_UPDATE_IMAGE__POS 5
+#define BMA150_UPDATE_IMAGE__LEN 1
+#define BMA150_UPDATE_IMAGE__MSK 0x20
+#define BMA150_UPDATE_IMAGE__REG BMA150_CTRL_REG
+
+#define BMA150_RESET_INT__POS 6
+#define BMA150_RESET_INT__LEN 1
+#define BMA150_RESET_INT__MSK 0x40
+#define BMA150_RESET_INT__REG BMA150_CTRL_REG
+
+/* BANDWIDTH dependend definitions */
+
+#define BMA150_BANDWIDTH__POS 0
+#define BMA150_BANDWIDTH__LEN 3
+#define BMA150_BANDWIDTH__MSK 0x07
+#define BMA150_BANDWIDTH__REG BMA150_RANGE_BWIDTH_REG
+
+/* RANGE */
+
+#define BMA150_RANGE__POS 3
+#define BMA150_RANGE__LEN 2
+#define BMA150_RANGE__MSK 0x18
+#define BMA150_RANGE__REG BMA150_RANGE_BWIDTH_REG
+
+/* WAKE UP */
+
+#define BMA150_WAKE_UP__POS 0
+#define BMA150_WAKE_UP__LEN 1
+#define BMA150_WAKE_UP__MSK 0x01
+#define BMA150_WAKE_UP__REG BMA150_CONF2_REG
+
+#define BMA150_WAKE_UP_PAUSE__POS 1
+#define BMA150_WAKE_UP_PAUSE__LEN 2
+#define BMA150_WAKE_UP_PAUSE__MSK 0x06
+#define BMA150_WAKE_UP_PAUSE__REG BMA150_CONF2_REG
+
+#define BMA150_GET_BITSLICE(regvar, bitname)\
+ ((regvar & bitname##__MSK) >> bitname##__POS)
+
+
+#define BMA150_SET_BITSLICE(regvar, bitname, val)\
+ ((regvar & ~bitname##__MSK) | ((val<<bitname##__POS)&bitname##__MSK))
+
+/* range and bandwidth */
+
+#define BMA150_RANGE_2G 0
+#define BMA150_RANGE_4G 1
+#define BMA150_RANGE_8G 2
+
+#define BMA150_BW_25HZ 0
+#define BMA150_BW_50HZ 1
+#define BMA150_BW_100HZ 2
+#define BMA150_BW_190HZ 3
+#define BMA150_BW_375HZ 4
+#define BMA150_BW_750HZ 5
+#define BMA150_BW_1500HZ 6
+
+/* mode settings */
+
+#define BMA150_MODE_NORMAL 0
+#define BMA150_MODE_SLEEP 2
+#define BMA150_MODE_WAKE_UP 3
+
+struct bma150acc{
+ s16 x,
+ y,
+ z;
+} ;
+
+struct bma150_data {
+ struct i2c_client *bma150_client;
+ struct bma150_platform_data *platform_data;
+ int IRQ;
+ atomic_t delay;
+ unsigned char mode;
+ struct input_dev *input;
+ struct bma150acc value;
+ struct mutex value_mutex;
+ struct mutex mode_mutex;
+ struct delayed_work work;
+ struct work_struct irq_work;
+};
+
+static int bma150_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 -EPERM;
+ *data = dummy & 0x000000ff;
+
+ return 0;
+}
+
+static int bma150_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 -EPERM;
+ return 0;
+}
+
+static int bma150_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 -EPERM;
+ return 0;
+}
+
+static int bma150_set_mode(struct i2c_client *client, unsigned char Mode)
+{
+ int comres = 0;
+ unsigned char data1 = 0, data2 = 0;
+ struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+ if (client == NULL) {
+ comres = -1;
+ } else{
+ if (Mode < 4 && Mode != 1) {
+
+ comres = bma150_smbus_read_byte(client,
+ BMA150_WAKE_UP__REG, &data1);
+ data1 = BMA150_SET_BITSLICE(data1,
+ BMA150_WAKE_UP, Mode);
+ comres += bma150_smbus_read_byte(client,
+ BMA150_SLEEP__REG, &data2);
+ data2 = BMA150_SET_BITSLICE(data2,
+ BMA150_SLEEP, (Mode>>1));
+ comres += bma150_smbus_write_byte(client,
+ BMA150_WAKE_UP__REG, &data1);
+ comres += bma150_smbus_write_byte(client,
+ BMA150_SLEEP__REG, &data2);
+ mutex_lock(&bma150->mode_mutex);
+ bma150->mode = (unsigned char) Mode;
+ mutex_unlock(&bma150->mode_mutex);
+
+ } else{
+ comres = -1;
+ }
+ }
+
+ return comres;
+}
+
+
+static int bma150_set_range(struct i2c_client *client, unsigned char Range)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ if (client == NULL) {
+ comres = -1;
+ } else{
+ if (Range < 3) {
+
+ comres = bma150_smbus_read_byte(client,
+ BMA150_RANGE__REG, &data);
+ data = BMA150_SET_BITSLICE(data, BMA150_RANGE, Range);
+ comres += bma150_smbus_write_byte(client,
+ BMA150_RANGE__REG, &data);
+
+ } else{
+ comres = -1;
+ }
+ }
+
+ return comres;
+}
+
+static int bma150_get_range(struct i2c_client *client, unsigned char *Range)
+{
+ int comres = 0;
+ unsigned char data;
+
+ if (client == NULL) {
+ comres = -1;
+ } else{
+ comres = bma150_smbus_read_byte(client,
+ BMA150_RANGE__REG, &data);
+
+ *Range = BMA150_GET_BITSLICE(data, BMA150_RANGE);
+
+ }
+
+ return comres;
+}
+
+
+
+static int bma150_set_bandwidth(struct i2c_client *client, unsigned char BW)
+{
+ int comres = 0;
+ unsigned char data = 0;
+
+ if (client == NULL) {
+ comres = -1;
+ } else{
+ if (BW < 8) {
+ comres = bma150_smbus_read_byte(client,
+ BMA150_BANDWIDTH__REG, &data);
+ data = BMA150_SET_BITSLICE(data, BMA150_BANDWIDTH, BW);
+ comres += bma150_smbus_write_byte(client,
+ BMA150_BANDWIDTH__REG, &data);
+
+ } else{
+ comres = -1;
+ }
+ }
+
+ return comres;
+}
+
+static int bma150_get_bandwidth(struct i2c_client *client, unsigned char *BW)
+{
+ int comres = 0;
+ unsigned char data;
+
+ if (client == NULL) {
+ comres = -1;
+ } else{
+
+
+ comres = bma150_smbus_read_byte(client,
+ BMA150_BANDWIDTH__REG, &data);
+
+ *BW = BMA150_GET_BITSLICE(data, BMA150_BANDWIDTH);
+
+
+ }
+
+ return comres;
+}
+
+static int bma150_read_accel_xyz(struct i2c_client *client,
+ struct bma150acc *acc)
+{
+ int comres;
+ unsigned char data[6];
+ if (client == NULL) {
+ comres = -1;
+ } else{
+
+
+ comres = bma150_smbus_read_byte_block(client,
+ BMA150_ACC_X_LSB__REG, &data[0], 6);
+
+ acc->x = BMA150_GET_BITSLICE(data[0], BMA150_ACC_X_LSB) |
+ (BMA150_GET_BITSLICE(data[1], BMA150_ACC_X_MSB)<<
+ BMA150_ACC_X_LSB__LEN);
+ acc->x = acc->x << (sizeof(short)*8-(BMA150_ACC_X_LSB__LEN+
+ BMA150_ACC_X_MSB__LEN));
+ acc->x = acc->x >> (sizeof(short)*8-(BMA150_ACC_X_LSB__LEN+
+ BMA150_ACC_X_MSB__LEN));
+
+ acc->y = BMA150_GET_BITSLICE(data[2], BMA150_ACC_Y_LSB) |
+ (BMA150_GET_BITSLICE(data[3], BMA150_ACC_Y_MSB)<<
+ BMA150_ACC_Y_LSB__LEN);
+ acc->y = acc->y << (sizeof(short)*8-(BMA150_ACC_Y_LSB__LEN +
+ BMA150_ACC_Y_MSB__LEN));
+ acc->y = acc->y >> (sizeof(short)*8-(BMA150_ACC_Y_LSB__LEN +
+ BMA150_ACC_Y_MSB__LEN));
+
+
+ acc->z = BMA150_GET_BITSLICE(data[4], BMA150_ACC_Z_LSB);
+ acc->z |= (BMA150_GET_BITSLICE(data[5], BMA150_ACC_Z_MSB)<<
+ BMA150_ACC_Z_LSB__LEN);
+ acc->z = acc->z << (sizeof(short)*8-(BMA150_ACC_Z_LSB__LEN+
+ BMA150_ACC_Z_MSB__LEN));
+ acc->z = acc->z >> (sizeof(short)*8-(BMA150_ACC_Z_LSB__LEN+
+ BMA150_ACC_Z_MSB__LEN));
+
+ }
+
+ return comres;
+}
+
+static void bma150_work_func(struct work_struct *work)
+{
+ struct bma150_data *bma150 = container_of((struct delayed_work *)work,
+ struct bma150_data, work);
+ static struct bma150acc acc;
+ unsigned long delay = msecs_to_jiffies(atomic_read(&bma150->delay));
+
+
+
+ bma150_read_accel_xyz(bma150->bma150_client, &acc);
+ input_report_abs(bma150->input, ABS_X, acc.x);
+ input_report_abs(bma150->input, ABS_Y, acc.y);
+ input_report_abs(bma150->input, ABS_Z, acc.z);
+ input_sync(bma150->input);
+ mutex_lock(&bma150->value_mutex);
+ bma150->value = acc;
+ mutex_unlock(&bma150->value_mutex);
+ schedule_delayed_work(&bma150->work, delay);
+}
+
+static ssize_t bma150_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+ mutex_lock(&bma150->mode_mutex);
+ data = bma150->mode;
+ mutex_unlock(&bma150->mode_mutex);
+
+ return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t bma150_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 bma150_data *bma150 = i2c_get_clientdata(client);
+
+ error = strict_strtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (bma150_set_mode(bma150->bma150_client, (unsigned char) data) < 0)
+ return -EINVAL;
+
+
+ return count;
+}
+static ssize_t bma150_range_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+ if (bma150_get_range(bma150->bma150_client, &data) < 0)
+ return sprintf(buf, "Read error\n");
+
+ return sprintf(buf, "%d\n", data);
+}
+
+static ssize_t bma150_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 bma150_data *bma150 = i2c_get_clientdata(client);
+
+ error = strict_strtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (bma150_set_range(bma150->bma150_client, (unsigned char) data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma150_bandwidth_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned char data;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+ if (bma150_get_bandwidth(bma150->bma150_client, &data) < 0)
+ return sprintf(buf, "Read error\n");
+
+ return sprintf(buf, "%d\n", data);
+
+}
+
+static ssize_t bma150_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 bma150_data *bma150 = i2c_get_clientdata(client);
+
+ error = strict_strtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (bma150_set_bandwidth(bma150->bma150_client,
+ (unsigned char) data) < 0)
+ return -EINVAL;
+
+ return count;
+}
+
+static ssize_t bma150_value_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_dev *input = to_input_dev(dev);
+ struct bma150_data *bma150 = input_get_drvdata(input);
+ struct bma150acc acc_value;
+
+ mutex_lock(&bma150->value_mutex);
+ acc_value = bma150->value;
+ mutex_unlock(&bma150->value_mutex);
+
+ return sprintf(buf, "%d %d %d\n", acc_value.x, acc_value.y,
+ acc_value.z);
+}
+
+
+
+static ssize_t bma150_delay_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bma150_data *bma150 = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%d\n", atomic_read(&bma150->delay));
+
+}
+
+static ssize_t bma150_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 bma150_data *bma150 = i2c_get_clientdata(client);
+
+ error = strict_strtoul(buf, 10, &data);
+ if (error)
+ return error;
+ if (data > BMA150_MAX_DELAY)
+ data = BMA150_MAX_DELAY;
+ atomic_set(&bma150->delay, (unsigned int) data);
+
+ return count;
+}
+
+static DEVICE_ATTR(range, S_IRUGO|S_IWUSR|S_IWGRP,
+ bma150_range_show, bma150_range_store);
+static DEVICE_ATTR(bandwidth, S_IRUGO|S_IWUSR|S_IWGRP,
+ bma150_bandwidth_show, bma150_bandwidth_store);
+static DEVICE_ATTR(mode, S_IRUGO|S_IWUSR|S_IWGRP,
+ bma150_mode_show, bma150_mode_store);
+static DEVICE_ATTR(value, S_IRUGO|S_IWUSR|S_IWGRP,
+ bma150_value_show, NULL);
+static DEVICE_ATTR(delay, S_IRUGO|S_IWUSR|S_IWGRP|S_IWOTH,
+ bma150_delay_show, bma150_delay_store);
+
+static struct attribute *bma150_attributes[] = {
+ &dev_attr_range.attr,
+ &dev_attr_bandwidth.attr,
+ &dev_attr_mode.attr,
+ &dev_attr_value.attr,
+ &dev_attr_delay.attr,
+ NULL
+};
+
+static struct attribute_group bma150_attribute_group = {
+ .attrs = bma150_attributes
+};
+
+static int bma150_detect(struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct i2c_adapter *adapter = client->adapter;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ return -ENODEV;
+
+ strlcpy(info->type, SENSOR_NAME, I2C_NAME_SIZE);
+
+ return 0;
+}
+
+static int bma150_input_init(struct bma150_data *bma150)
+{
+ struct input_dev *dev;
+ int err;
+
+ dev = input_allocate_device();
+ if (!dev)
+ return -ENOMEM;
+ dev->name = SENSOR_NAME;
+ dev->id.bustype = BUS_I2C;
+
+ input_set_capability(dev, EV_ABS, ABS_MISC);
+ input_set_abs_params(dev, ABS_X, ABSMIN_2G, ABSMAX_2G, 0, 0);
+ input_set_abs_params(dev, ABS_Y, ABSMIN_2G, ABSMAX_2G, 0, 0);
+ input_set_abs_params(dev, ABS_Z, ABSMIN_2G, ABSMAX_2G, 0, 0);
+ input_set_drvdata(dev, bma150);
+
+ err = input_register_device(dev);
+ if (err < 0) {
+ input_free_device(dev);
+ return err;
+ }
+ bma150->input = dev;
+
+ return 0;
+}
+
+static void bma150_input_delete(struct bma150_data *bma150)
+{
+ struct input_dev *dev = bma150->input;
+
+ input_unregister_device(dev);
+ input_free_device(dev);
+}
+
+static int bma150_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err = 0;
+ int tempvalue;
+ struct bma150_data *data;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ printk(KERN_INFO "i2c_check_functionality error\n");
+ goto exit;
+ }
+ data = kzalloc(sizeof(struct bma150_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ i2c_set_clientdata(client, data);
+ data->platform_data = client->dev.platform_data;
+
+ if (data->platform_data->power_on)
+ data->platform_data->power_on();
+ else
+ printk(KERN_ERR "power_on function not defined!!\n");
+
+ tempvalue = 0;
+ tempvalue = i2c_smbus_read_word_data(client, BMA150_CHIP_ID_REG);
+
+ if ((tempvalue&0x00FF) == BMA150_CHIP_ID) {
+ printk(KERN_INFO "Bosch Sensortec Device detected!\n" \
+ "BMA150 registered I2C driver!\n");
+ } else{
+ printk(KERN_INFO "Bosch Sensortec Device not found" \
+ "i2c error %d\n", tempvalue);
+ err = -1;
+ goto kfree_exit;
+ }
+ i2c_set_clientdata(client, data);
+ data->bma150_client = client;
+ mutex_init(&data->value_mutex);
+ mutex_init(&data->mode_mutex);
+ bma150_set_bandwidth(client, BMA150_BW_SET);
+ bma150_set_range(client, BMA150_RANGE_SET);
+
+
+ INIT_DELAYED_WORK(&data->work, bma150_work_func);
+ atomic_set(&data->delay, BMA150_MAX_DELAY);
+ err = bma150_input_init(data);
+ if (err < 0)
+ goto kfree_exit;
+
+ err = sysfs_create_group(&data->input->dev.kobj,
+ &bma150_attribute_group);
+ if (err < 0)
+ goto error_sysfs;
+
+ schedule_delayed_work(&data->work,
+ msecs_to_jiffies(atomic_read(&data->delay)));
+
+ return 0;
+
+error_sysfs:
+ bma150_input_delete(data);
+
+kfree_exit:
+ kfree(data);
+exit:
+ return err;
+}
+
+static int bma150_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct bma150_data *data = i2c_get_clientdata(client);
+
+ cancel_delayed_work_sync(&data->work);
+
+ bma150_set_mode(client, BMA150_MODE_SLEEP);
+
+ if ((data->platform_data) && (data->platform_data->power_off))
+ data->platform_data->power_off();
+
+ return 0;
+}
+
+static int bma150_resume(struct i2c_client *client)
+{
+ struct bma150_data *data = i2c_get_clientdata(client);
+
+ if ((data->platform_data) && (data->platform_data->power_on))
+ data->platform_data->power_on();
+
+ bma150_set_mode(client, BMA150_MODE_NORMAL);
+
+ schedule_delayed_work(&data->work,
+ msecs_to_jiffies(atomic_read(&data->delay)));
+
+ return 0;
+}
+
+static int bma150_remove(struct i2c_client *client)
+{
+ struct bma150_data *data = i2c_get_clientdata(client);
+
+ if (data->platform_data->power_off)
+ data->platform_data->power_off();
+ else
+ printk(KERN_ERR "power_off function not defined!!\n");
+
+ sysfs_remove_group(&data->input->dev.kobj, &bma150_attribute_group);
+ bma150_input_delete(data);
+ free_irq(data->IRQ, data);
+ kfree(data);
+
+ return 0;
+}
+
+static const struct i2c_device_id bma150_id[] = {
+ { SENSOR_NAME, 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, bma150_id);
+
+static struct i2c_driver bma150_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = SENSOR_NAME,
+ },
+ .class = I2C_CLASS_HWMON,
+ .id_table = bma150_id,
+ .probe = bma150_probe,
+ .remove = bma150_remove,
+ .detect = bma150_detect,
+ .suspend = bma150_suspend,
+ .resume = bma150_resume,
+};
+
+static int __init BMA150_init(void)
+{
+ return i2c_add_driver(&bma150_driver);
+}
+
+static void __exit BMA150_exit(void)
+{
+ i2c_del_driver(&bma150_driver);
+}
+
+MODULE_DESCRIPTION("BMA150 driver");
+
+module_init(BMA150_init);
+module_exit(BMA150_exit);
+
diff --git a/drivers/input/misc/gpio_matrix.c b/drivers/input/misc/gpio_matrix.c
index eaa9e89..56e91fd 100644
--- a/drivers/input/misc/gpio_matrix.c
+++ b/drivers/input/misc/gpio_matrix.c
@@ -181,12 +181,14 @@
gpio_set_value(gpio, polarity);
else
gpio_direction_output(gpio, polarity);
- hrtimer_start(timer, mi->settle_time, HRTIMER_MODE_REL);
+ hrtimer_start(timer, timespec_to_ktime(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_start(&kp->timer,
+ timespec_to_ktime(mi->debounce_delay),
HRTIMER_MODE_REL);
return HRTIMER_NORESTART;
}
@@ -202,7 +204,8 @@
report_sync(kp);
}
if (!kp->use_irq || kp->some_keys_pressed) {
- hrtimer_start(timer, mi->poll_time, HRTIMER_MODE_REL);
+ hrtimer_start(timer, timespec_to_ktime(mi->poll_time),
+ HRTIMER_MODE_REL);
return HRTIMER_NORESTART;
}
diff --git a/drivers/input/misc/isa1200-ff-memless.c b/drivers/input/misc/isa1200-ff-memless.c
new file mode 100644
index 0000000..f4e2c35
--- /dev/null
+++ b/drivers/input/misc/isa1200-ff-memless.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2009 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * Copyright (c) 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.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/pwm.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/i2c/isa1200.h>
+
+#define ISA1200_HCTRL0 0x30
+#define HCTRL0_MODE_CTRL_BIT (3)
+#define HCTRL0_OVERDRIVE_HIGH_BIT (5)
+#define HCTRL0_OVERDRIVE_EN_BIT (6)
+#define HCTRL0_HAP_EN (7)
+#define HCTRL0_RESET 0x01
+#define HCTRL1_RESET 0x4B
+
+#define ISA1200_HCTRL1 0x31
+#define HCTRL1_SMART_ENABLE_BIT (3)
+#define HCTRL1_ERM_BIT (5)
+#define HCTRL1_EXT_CLK_ENABLE_BIT (7)
+
+#define ISA1200_HCTRL5 0x35
+#define HCTRL5_VIB_STRT 0xD5
+#define HCTRL5_VIB_STOP 0x6B
+
+#define DIVIDER_128 (128)
+#define DIVIDER_1024 (1024)
+#define DIVIDE_SHIFTER_128 (7)
+
+#define FREQ_22400 (22400)
+#define FREQ_172600 (172600)
+
+#define POR_DELAY_USEC 250
+
+struct isa1200_chip {
+ const struct isa1200_platform_data *pdata;
+ struct i2c_client *client;
+ struct input_dev *input_device;
+ struct pwm_device *pwm;
+ unsigned int period_ns;
+ unsigned int state;
+ struct work_struct work;
+};
+
+static void isa1200_vib_set(struct isa1200_chip *haptic, int enable)
+{
+ int rc;
+
+ if (enable) {
+ if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) {
+ int period_us = haptic->period_ns / NSEC_PER_USEC;
+ rc = pwm_config(haptic->pwm,
+ (period_us * haptic->pdata->duty) / 100,
+ period_us);
+ if (rc < 0)
+ pr_err("pwm_config fail\n");
+ rc = pwm_enable(haptic->pwm);
+ if (rc < 0)
+ pr_err("pwm_enable fail\n");
+ } else if (haptic->pdata->mode_ctrl == PWM_GEN_MODE) {
+ rc = i2c_smbus_write_byte_data(haptic->client,
+ ISA1200_HCTRL5,
+ HCTRL5_VIB_STRT);
+ if (rc < 0)
+ pr_err("start vibration fail\n");
+ }
+ } else {
+ if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE)
+ pwm_disable(haptic->pwm);
+ else if (haptic->pdata->mode_ctrl == PWM_GEN_MODE) {
+ rc = i2c_smbus_write_byte_data(haptic->client,
+ ISA1200_HCTRL5,
+ HCTRL5_VIB_STOP);
+ if (rc < 0)
+ pr_err("stop vibration fail\n");
+ }
+ }
+}
+
+static int isa1200_setup(struct i2c_client *client)
+{
+ struct isa1200_chip *haptic = i2c_get_clientdata(client);
+ int value, temp, rc;
+
+ gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 0);
+ udelay(POR_DELAY_USEC);
+ gpio_set_value_cansleep(haptic->pdata->hap_en_gpio, 1);
+
+ value = (haptic->pdata->smart_en << HCTRL1_SMART_ENABLE_BIT) |
+ (haptic->pdata->is_erm << HCTRL1_ERM_BIT) |
+ (haptic->pdata->ext_clk_en << HCTRL1_EXT_CLK_ENABLE_BIT);
+
+ rc = i2c_smbus_write_byte_data(client, ISA1200_HCTRL1, value);
+ if (rc < 0) {
+ pr_err("i2c write failure\n");
+ return rc;
+ }
+
+ if (haptic->pdata->mode_ctrl == PWM_GEN_MODE) {
+ temp = haptic->pdata->pwm_fd.pwm_div;
+ if (temp < DIVIDER_128 || temp > DIVIDER_1024 ||
+ temp % DIVIDER_128) {
+ pr_err("Invalid divider\n");
+ rc = -EINVAL;
+ goto reset_hctrl1;
+ }
+ value = ((temp >> DIVIDE_SHIFTER_128) - 1);
+ } else if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) {
+ temp = haptic->pdata->pwm_fd.pwm_freq;
+ if (temp < FREQ_22400 || temp > FREQ_172600 ||
+ temp % FREQ_22400) {
+ pr_err("Invalid frequency\n");
+ rc = -EINVAL;
+ goto reset_hctrl1;
+ }
+ value = ((temp / FREQ_22400) - 1);
+ haptic->period_ns = NSEC_PER_SEC / temp;
+ }
+ value |= (haptic->pdata->mode_ctrl << HCTRL0_MODE_CTRL_BIT) |
+ (haptic->pdata->overdrive_high << HCTRL0_OVERDRIVE_HIGH_BIT) |
+ (haptic->pdata->overdrive_en << HCTRL0_OVERDRIVE_EN_BIT) |
+ (haptic->pdata->chip_en << HCTRL0_HAP_EN);
+
+ rc = i2c_smbus_write_byte_data(client, ISA1200_HCTRL0, value);
+ if (rc < 0) {
+ pr_err("i2c write failure\n");
+ goto reset_hctrl1;
+ }
+
+ return 0;
+
+reset_hctrl1:
+ i2c_smbus_write_byte_data(client, ISA1200_HCTRL1,
+ HCTRL1_RESET);
+ return rc;
+}
+
+static void isa1200_worker(struct work_struct *work)
+{
+ struct isa1200_chip *haptic;
+
+ haptic = container_of(work, struct isa1200_chip, work);
+ isa1200_vib_set(haptic, !!haptic->state);
+}
+
+static int isa1200_play_effect(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct isa1200_chip *haptic = input_get_drvdata(dev);
+
+ /* support basic vibration */
+ haptic->state = effect->u.rumble.strong_magnitude >> 8;
+ if (!haptic->state)
+ haptic->state = effect->u.rumble.weak_magnitude >> 9;
+
+ schedule_work(&haptic->work);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int isa1200_suspend(struct device *dev)
+{
+ struct isa1200_chip *haptic = dev_get_drvdata(dev);
+ int rc;
+
+ cancel_work_sync(&haptic->work);
+ /* turn-off current vibration */
+ isa1200_vib_set(haptic, 0);
+
+ if (haptic->pdata->power_on) {
+ rc = haptic->pdata->power_on(0);
+ if (rc) {
+ pr_err("power-down failed\n");
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static int isa1200_resume(struct device *dev)
+{
+ struct isa1200_chip *haptic = dev_get_drvdata(dev);
+ int rc;
+
+ if (haptic->pdata->power_on) {
+ rc = haptic->pdata->power_on(1);
+ if (rc) {
+ pr_err("power-up failed\n");
+ return rc;
+ }
+ }
+
+ isa1200_setup(haptic->client);
+ return 0;
+}
+#else
+#define isa1200_suspend NULL
+#define isa1200_resume NULL
+#endif
+
+static int isa1200_open(struct input_dev *dev)
+{
+ struct isa1200_chip *haptic = input_get_drvdata(dev);
+ int rc;
+
+ /* device setup */
+ if (haptic->pdata->dev_setup) {
+ rc = haptic->pdata->dev_setup(true);
+ if (rc < 0) {
+ pr_err("setup failed!\n");
+ return rc;
+ }
+ }
+
+ /* power on */
+ if (haptic->pdata->power_on) {
+ rc = haptic->pdata->power_on(true);
+ if (rc < 0) {
+ pr_err("power failed\n");
+ goto err_setup;
+ }
+ }
+
+ /* request gpio */
+ rc = gpio_is_valid(haptic->pdata->hap_en_gpio);
+ if (rc) {
+ rc = gpio_request(haptic->pdata->hap_en_gpio, "haptic_gpio");
+ if (rc) {
+ pr_err("gpio %d request failed\n",
+ haptic->pdata->hap_en_gpio);
+ goto err_power_on;
+ }
+ } else {
+ pr_err("Invalid gpio %d\n",
+ haptic->pdata->hap_en_gpio);
+ goto err_power_on;
+ }
+
+ rc = gpio_direction_output(haptic->pdata->hap_en_gpio, 0);
+ if (rc) {
+ pr_err("gpio %d set direction failed\n",
+ haptic->pdata->hap_en_gpio);
+ goto err_gpio_free;
+ }
+
+ /* setup registers */
+ rc = isa1200_setup(haptic->client);
+ if (rc < 0) {
+ pr_err("setup fail %d\n", rc);
+ goto err_gpio_free;
+ }
+
+ if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE) {
+ haptic->pwm = pwm_request(haptic->pdata->pwm_ch_id,
+ haptic->client->driver->id_table->name);
+ if (IS_ERR(haptic->pwm)) {
+ pr_err("pwm request failed\n");
+ rc = PTR_ERR(haptic->pwm);
+ goto err_reset_hctrl0;
+ }
+ }
+
+ /* init workqeueue */
+ INIT_WORK(&haptic->work, isa1200_worker);
+ return 0;
+
+err_reset_hctrl0:
+ i2c_smbus_write_byte_data(haptic->client, ISA1200_HCTRL0,
+ HCTRL0_RESET);
+err_gpio_free:
+ gpio_free(haptic->pdata->hap_en_gpio);
+err_power_on:
+ if (haptic->pdata->power_on)
+ haptic->pdata->power_on(0);
+err_setup:
+ if (haptic->pdata->dev_setup)
+ haptic->pdata->dev_setup(false);
+
+ return rc;
+}
+
+static void isa1200_close(struct input_dev *dev)
+{
+ struct isa1200_chip *haptic = input_get_drvdata(dev);
+
+ /* turn-off current vibration */
+ isa1200_vib_set(haptic, 0);
+
+ if (haptic->pdata->mode_ctrl == PWM_INPUT_MODE)
+ pwm_free(haptic->pwm);
+
+ gpio_free(haptic->pdata->hap_en_gpio);
+
+ /* reset hardware registers */
+ i2c_smbus_write_byte_data(haptic->client, ISA1200_HCTRL0,
+ HCTRL0_RESET);
+ i2c_smbus_write_byte_data(haptic->client, ISA1200_HCTRL1,
+ HCTRL1_RESET);
+
+ if (haptic->pdata->dev_setup)
+ haptic->pdata->dev_setup(false);
+
+ /* power-off the chip */
+ if (haptic->pdata->power_on)
+ haptic->pdata->power_on(0);
+}
+
+static int __devinit isa1200_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct isa1200_chip *haptic;
+ int rc;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA)) {
+ pr_err("i2c is not supported\n");
+ return -EIO;
+ }
+
+ if (!client->dev.platform_data) {
+ pr_err("pdata is not avaiable\n");
+ return -EINVAL;
+ }
+
+ haptic = kzalloc(sizeof(struct isa1200_chip), GFP_KERNEL);
+ if (!haptic) {
+ pr_err("no memory\n");
+ return -ENOMEM;
+ }
+
+ haptic->pdata = client->dev.platform_data;
+ haptic->client = client;
+
+ i2c_set_clientdata(client, haptic);
+
+ haptic->input_device = input_allocate_device();
+ if (!haptic->input_device) {
+ pr_err("input device alloc failed\n");
+ rc = -ENOMEM;
+ goto err_mem_alloc;
+ }
+
+ input_set_drvdata(haptic->input_device, haptic);
+ haptic->input_device->name = haptic->pdata->name ? :
+ "isa1200-ff-memless";
+
+ haptic->input_device->dev.parent = &client->dev;
+
+ input_set_capability(haptic->input_device, EV_FF, FF_RUMBLE);
+
+ haptic->input_device->open = isa1200_open;
+ haptic->input_device->close = isa1200_close;
+
+ rc = input_ff_create_memless(haptic->input_device, NULL,
+ isa1200_play_effect);
+ if (rc < 0) {
+ pr_err("unable to register with ff\n");
+ goto err_free_dev;
+ }
+
+ rc = input_register_device(haptic->input_device);
+ if (rc < 0) {
+ pr_err("unable to register input device\n");
+ goto err_ff_destroy;
+ }
+
+ return 0;
+
+err_ff_destroy:
+ input_ff_destroy(haptic->input_device);
+err_free_dev:
+ input_free_device(haptic->input_device);
+err_mem_alloc:
+ kfree(haptic);
+ return rc;
+}
+
+static int __devexit isa1200_remove(struct i2c_client *client)
+{
+ struct isa1200_chip *haptic = i2c_get_clientdata(client);
+
+ input_unregister_device(haptic->input_device);
+ kfree(haptic);
+
+ return 0;
+}
+
+static const struct i2c_device_id isa1200_id_table[] = {
+ {"isa1200_1", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, isa1200_id_table);
+
+static const struct dev_pm_ops isa1200_pm_ops = {
+ .suspend = isa1200_suspend,
+ .resume = isa1200_resume,
+};
+
+static struct i2c_driver isa1200_driver = {
+ .driver = {
+ .name = "isa1200-ff-memless",
+ .owner = THIS_MODULE,
+ .pm = &isa1200_pm_ops,
+ },
+ .probe = isa1200_probe,
+ .remove = __devexit_p(isa1200_remove),
+ .id_table = isa1200_id_table,
+};
+
+static int __init isa1200_init(void)
+{
+ return i2c_add_driver(&isa1200_driver);
+}
+module_init(isa1200_init);
+
+static void __exit isa1200_exit(void)
+{
+ i2c_del_driver(&isa1200_driver);
+}
+module_exit(isa1200_exit);
+
+MODULE_DESCRIPTION("isa1200 based vibrator chip driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
diff --git a/drivers/input/misc/pmic8058-othc.c b/drivers/input/misc/pmic8058-othc.c
new file mode 100644
index 0000000..c6be119
--- /dev/null
+++ b/drivers/input/misc/pmic8058-othc.c
@@ -0,0 +1,1199 @@
+/* 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/switch.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/hrtimer.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/mfd/pmic8058.h>
+#include <linux/pmic8058-othc.h>
+#include <linux/msm_adc.h>
+
+#define PM8058_OTHC_LOW_CURR_MASK 0xF0
+#define PM8058_OTHC_HIGH_CURR_MASK 0x0F
+#define PM8058_OTHC_EN_SIG_MASK 0x3F
+#define PM8058_OTHC_HYST_PREDIV_MASK 0xC7
+#define PM8058_OTHC_CLK_PREDIV_MASK 0xF8
+#define PM8058_OTHC_HYST_CLK_MASK 0x0F
+#define PM8058_OTHC_PERIOD_CLK_MASK 0xF0
+
+#define PM8058_OTHC_LOW_CURR_SHIFT 0x4
+#define PM8058_OTHC_EN_SIG_SHIFT 0x6
+#define PM8058_OTHC_HYST_PREDIV_SHIFT 0x3
+#define PM8058_OTHC_HYST_CLK_SHIFT 0x4
+
+#define OTHC_GPIO_MAX_LEN 25
+
+struct pm8058_othc {
+ bool othc_sw_state;
+ bool switch_reject;
+ bool othc_support_n_switch;
+ bool accessory_support;
+ bool accessories_adc_support;
+ int othc_base;
+ int othc_irq_sw;
+ int othc_irq_ir;
+ int othc_ir_state;
+ int num_accessories;
+ int curr_accessory_code;
+ int curr_accessory;
+ int video_out_gpio;
+ u32 sw_key_code;
+ u32 accessories_adc_channel;
+ int ir_gpio;
+ unsigned long switch_debounce_ms;
+ unsigned long detection_delay_ms;
+ void *adc_handle;
+ void *accessory_adc_handle;
+ spinlock_t lock;
+ struct regulator *othc_vreg;
+ struct input_dev *othc_ipd;
+ struct switch_dev othc_sdev;
+ struct pmic8058_othc_config_pdata *othc_pdata;
+ struct othc_accessory_info *accessory_info;
+ struct hrtimer timer;
+ struct othc_n_switch_config *switch_config;
+ struct pm8058_chip *pm_chip;
+ struct work_struct switch_work;
+ struct delayed_work detect_work;
+ struct delayed_work hs_work;
+};
+
+static struct pm8058_othc *config[OTHC_MICBIAS_MAX];
+
+static void hs_worker(struct work_struct *work)
+{
+ int rc;
+ struct pm8058_othc *dd =
+ container_of(work, struct pm8058_othc, hs_work.work);
+
+ rc = gpio_get_value_cansleep(dd->ir_gpio);
+ if (rc < 0) {
+ pr_err("Unable to read IR GPIO\n");
+ enable_irq(dd->othc_irq_ir);
+ return;
+ }
+
+ dd->othc_ir_state = !rc;
+ schedule_delayed_work(&dd->detect_work,
+ msecs_to_jiffies(dd->detection_delay_ms));
+}
+
+static irqreturn_t ir_gpio_irq(int irq, void *dev_id)
+{
+ unsigned long flags;
+ struct pm8058_othc *dd = dev_id;
+
+ spin_lock_irqsave(&dd->lock, flags);
+ /* Enable the switch reject flag */
+ dd->switch_reject = true;
+ spin_unlock_irqrestore(&dd->lock, flags);
+
+ /* Start the HR timer if one is not active */
+ if (hrtimer_active(&dd->timer))
+ hrtimer_cancel(&dd->timer);
+
+ hrtimer_start(&dd->timer,
+ ktime_set((dd->switch_debounce_ms / 1000),
+ (dd->switch_debounce_ms % 1000) * 1000000), HRTIMER_MODE_REL);
+
+ /* disable irq, this gets enabled in the workqueue */
+ disable_irq_nosync(dd->othc_irq_ir);
+ schedule_delayed_work(&dd->hs_work, 0);
+
+ return IRQ_HANDLED;
+}
+/*
+ * The API pm8058_micbias_enable() allows to configure
+ * the MIC_BIAS. Only the lines which are not used for
+ * headset detection can be configured using this API.
+ * The API returns an error code if it fails to configure
+ * the specified MIC_BIAS line, else it returns 0.
+ */
+int pm8058_micbias_enable(enum othc_micbias micbias,
+ enum othc_micbias_enable enable)
+{
+ int rc;
+ u8 reg;
+ struct pm8058_othc *dd = config[micbias];
+
+ if (dd == NULL) {
+ pr_err("MIC_BIAS not registered, cannot enable\n");
+ return -ENODEV;
+ }
+
+ if (dd->othc_pdata->micbias_capability != OTHC_MICBIAS) {
+ pr_err("MIC_BIAS enable capability not supported\n");
+ return -EINVAL;
+ }
+
+ rc = pm8058_read(dd->pm_chip, dd->othc_base + 1, ®, 1);
+ if (rc < 0) {
+ pr_err("PM8058 read failed\n");
+ return rc;
+ }
+
+ reg &= PM8058_OTHC_EN_SIG_MASK;
+ reg |= (enable << PM8058_OTHC_EN_SIG_SHIFT);
+
+ rc = pm8058_write(dd->pm_chip, dd->othc_base + 1, ®, 1);
+ if (rc < 0) {
+ pr_err("PM8058 write failed\n");
+ return rc;
+ }
+
+ return rc;
+}
+EXPORT_SYMBOL(pm8058_micbias_enable);
+
+int pm8058_othc_svideo_enable(enum othc_micbias micbias, bool enable)
+{
+ struct pm8058_othc *dd = config[micbias];
+
+ if (dd == NULL) {
+ pr_err("MIC_BIAS not registered, cannot enable\n");
+ return -ENODEV;
+ }
+
+ if (dd->othc_pdata->micbias_capability != OTHC_MICBIAS_HSED) {
+ pr_err("MIC_BIAS enable capability not supported\n");
+ return -EINVAL;
+ }
+
+ if (dd->accessories_adc_support) {
+ /* GPIO state for MIC_IN = 0, SVIDEO = 1 */
+ gpio_set_value_cansleep(dd->video_out_gpio, !!enable);
+ if (enable) {
+ pr_debug("Enable the video path\n");
+ switch_set_state(&dd->othc_sdev, dd->curr_accessory);
+ input_report_switch(dd->othc_ipd,
+ dd->curr_accessory_code, 1);
+ input_sync(dd->othc_ipd);
+ } else {
+ pr_debug("Disable the video path\n");
+ switch_set_state(&dd->othc_sdev, 0);
+ input_report_switch(dd->othc_ipd,
+ dd->curr_accessory_code, 0);
+ input_sync(dd->othc_ipd);
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pm8058_othc_svideo_enable);
+
+#ifdef CONFIG_PM
+static int pm8058_othc_suspend(struct device *dev)
+{
+ int rc = 0;
+ struct pm8058_othc *dd = dev_get_drvdata(dev);
+
+ if (dd->othc_pdata->micbias_capability == OTHC_MICBIAS_HSED) {
+ if (device_may_wakeup(dev)) {
+ enable_irq_wake(dd->othc_irq_sw);
+ enable_irq_wake(dd->othc_irq_ir);
+ }
+ }
+
+ if (!device_may_wakeup(dev)) {
+ rc = regulator_disable(dd->othc_vreg);
+ if (rc)
+ pr_err("othc micbais power off failed\n");
+ }
+
+ return rc;
+}
+
+static int pm8058_othc_resume(struct device *dev)
+{
+ int rc = 0;
+ struct pm8058_othc *dd = dev_get_drvdata(dev);
+
+ if (dd->othc_pdata->micbias_capability == OTHC_MICBIAS_HSED) {
+ if (device_may_wakeup(dev)) {
+ disable_irq_wake(dd->othc_irq_sw);
+ disable_irq_wake(dd->othc_irq_ir);
+ }
+ }
+
+ if (!device_may_wakeup(dev)) {
+ rc = regulator_enable(dd->othc_vreg);
+ if (rc)
+ pr_err("othc micbais power on failed\n");
+ }
+
+ return rc;
+}
+
+static struct dev_pm_ops pm8058_othc_pm_ops = {
+ .suspend = pm8058_othc_suspend,
+ .resume = pm8058_othc_resume,
+};
+#endif
+
+static int __devexit pm8058_othc_remove(struct platform_device *pd)
+{
+ struct pm8058_othc *dd = platform_get_drvdata(pd);
+
+ pm_runtime_set_suspended(&pd->dev);
+ pm_runtime_disable(&pd->dev);
+
+ if (dd->othc_pdata->micbias_capability == OTHC_MICBIAS_HSED) {
+ device_init_wakeup(&pd->dev, 0);
+ if (dd->othc_support_n_switch == true) {
+ adc_channel_close(dd->adc_handle);
+ cancel_work_sync(&dd->switch_work);
+ }
+
+ if (dd->accessory_support == true) {
+ int i;
+ for (i = 0; i < dd->num_accessories; i++) {
+ if (dd->accessory_info[i].detect_flags &
+ OTHC_GPIO_DETECT)
+ gpio_free(dd->accessory_info[i].gpio);
+ }
+ }
+ cancel_delayed_work_sync(&dd->detect_work);
+ cancel_delayed_work_sync(&dd->hs_work);
+ free_irq(dd->othc_irq_sw, dd);
+ free_irq(dd->othc_irq_ir, dd);
+ if (dd->ir_gpio != -1)
+ gpio_free(dd->ir_gpio);
+ input_unregister_device(dd->othc_ipd);
+ }
+ regulator_disable(dd->othc_vreg);
+ regulator_put(dd->othc_vreg);
+
+ kfree(dd);
+
+ return 0;
+}
+
+static enum hrtimer_restart pm8058_othc_timer(struct hrtimer *timer)
+{
+ unsigned long flags;
+ struct pm8058_othc *dd = container_of(timer,
+ struct pm8058_othc, timer);
+
+ spin_lock_irqsave(&dd->lock, flags);
+ dd->switch_reject = false;
+ spin_unlock_irqrestore(&dd->lock, flags);
+
+ return HRTIMER_NORESTART;
+}
+
+static void othc_report_switch(struct pm8058_othc *dd, u32 res)
+{
+ u8 i;
+ struct othc_switch_info *sw_info = dd->switch_config->switch_info;
+
+ for (i = 0; i < dd->switch_config->num_keys; i++) {
+ if (res >= sw_info[i].min_adc_threshold &&
+ res <= sw_info[i].max_adc_threshold) {
+ dd->othc_sw_state = true;
+ dd->sw_key_code = sw_info[i].key_code;
+ input_report_key(dd->othc_ipd, sw_info[i].key_code, 1);
+ input_sync(dd->othc_ipd);
+ return;
+ }
+ }
+
+ /*
+ * If the switch is not present in a specified ADC range
+ * report a default switch press.
+ */
+ if (dd->switch_config->default_sw_en) {
+ dd->othc_sw_state = true;
+ dd->sw_key_code =
+ sw_info[dd->switch_config->default_sw_idx].key_code;
+ input_report_key(dd->othc_ipd, dd->sw_key_code, 1);
+ input_sync(dd->othc_ipd);
+ }
+}
+
+static void switch_work_f(struct work_struct *work)
+{
+ int rc, i;
+ u32 res = 0;
+ struct adc_chan_result adc_result;
+ struct pm8058_othc *dd =
+ container_of(work, struct pm8058_othc, switch_work);
+ DECLARE_COMPLETION_ONSTACK(adc_wait);
+ u8 num_adc_samples = dd->switch_config->num_adc_samples;
+
+ /* sleep for settling time */
+ msleep(dd->switch_config->voltage_settling_time_ms);
+
+ for (i = 0; i < num_adc_samples; i++) {
+ rc = adc_channel_request_conv(dd->adc_handle, &adc_wait);
+ if (rc) {
+ pr_err("adc_channel_request_conv failed\n");
+ goto bail_out;
+ }
+ rc = wait_for_completion_interruptible(&adc_wait);
+ if (rc) {
+ pr_err("wait_for_completion_interruptible failed\n");
+ goto bail_out;
+ }
+ rc = adc_channel_read_result(dd->adc_handle, &adc_result);
+ if (rc) {
+ pr_err("adc_channel_read_result failed\n");
+ goto bail_out;
+ }
+ res += adc_result.physical;
+ }
+bail_out:
+ if (i == num_adc_samples && num_adc_samples != 0) {
+ res /= num_adc_samples;
+ othc_report_switch(dd, res);
+ } else
+ pr_err("Insufficient ADC samples\n");
+
+ enable_irq(dd->othc_irq_sw);
+}
+
+static int accessory_adc_detect(struct pm8058_othc *dd, int accessory)
+{
+ int rc;
+ u32 res;
+ struct adc_chan_result accessory_adc_result;
+ DECLARE_COMPLETION_ONSTACK(accessory_adc_wait);
+
+ rc = adc_channel_request_conv(dd->accessory_adc_handle,
+ &accessory_adc_wait);
+ if (rc) {
+ pr_err("adc_channel_request_conv failed\n");
+ goto adc_failed;
+ }
+ rc = wait_for_completion_interruptible(&accessory_adc_wait);
+ if (rc) {
+ pr_err("wait_for_completion_interruptible failed\n");
+ goto adc_failed;
+ }
+ rc = adc_channel_read_result(dd->accessory_adc_handle,
+ &accessory_adc_result);
+ if (rc) {
+ pr_err("adc_channel_read_result failed\n");
+ goto adc_failed;
+ }
+
+ res = accessory_adc_result.physical;
+
+ if (res >= dd->accessory_info[accessory].adc_thres.min_threshold &&
+ res <= dd->accessory_info[accessory].adc_thres.max_threshold) {
+ pr_debug("Accessory on ADC detected!, ADC Value = %u\n", res);
+ return 1;
+ }
+
+adc_failed:
+ return 0;
+}
+
+
+static int pm8058_accessory_report(struct pm8058_othc *dd, int status)
+{
+ int i, rc, detected = 0;
+ u8 micbias_status, switch_status;
+
+ if (dd->accessory_support == false) {
+ /* Report default headset */
+ switch_set_state(&dd->othc_sdev, !!status);
+ input_report_switch(dd->othc_ipd, SW_HEADPHONE_INSERT,
+ !!status);
+ input_sync(dd->othc_ipd);
+ return 0;
+ }
+
+ /* For accessory */
+ if (dd->accessory_support == true && status == 0) {
+ /* Report removal of the accessory. */
+
+ /*
+ * If the current accessory is video cable, reject the removal
+ * interrupt.
+ */
+ pr_info("Accessory [%d] removed\n", dd->curr_accessory);
+ if (dd->curr_accessory == OTHC_SVIDEO_OUT)
+ return 0;
+
+ switch_set_state(&dd->othc_sdev, 0);
+ input_report_switch(dd->othc_ipd, dd->curr_accessory_code, 0);
+ input_sync(dd->othc_ipd);
+ return 0;
+ }
+
+ if (dd->ir_gpio < 0) {
+ /* Check the MIC_BIAS status */
+ rc = pm8058_irq_get_rt_status(dd->pm_chip, dd->othc_irq_ir);
+ if (rc < 0) {
+ pr_err("Unable to read IR status from PMIC\n");
+ goto fail_ir_accessory;
+ }
+ micbias_status = !!rc;
+ } else {
+ rc = gpio_get_value_cansleep(dd->ir_gpio);
+ if (rc < 0) {
+ pr_err("Unable to read IR status from GPIO\n");
+ goto fail_ir_accessory;
+ }
+ micbias_status = !rc;
+ }
+
+ /* Check the switch status */
+ rc = pm8058_irq_get_rt_status(dd->pm_chip, dd->othc_irq_sw);
+ if (rc < 0) {
+ pr_err("Unable to read SWITCH status\n");
+ goto fail_ir_accessory;
+ }
+ switch_status = !!rc;
+
+ /* Loop through to check which accessory is connected */
+ for (i = 0; i < dd->num_accessories; i++) {
+ detected = 0;
+ if (dd->accessory_info[i].enabled == false)
+ continue;
+
+ if (dd->accessory_info[i].detect_flags & OTHC_MICBIAS_DETECT) {
+ if (micbias_status)
+ detected = 1;
+ else
+ continue;
+ }
+ if (dd->accessory_info[i].detect_flags & OTHC_SWITCH_DETECT) {
+ if (switch_status)
+ detected = 1;
+ else
+ continue;
+ }
+ if (dd->accessory_info[i].detect_flags & OTHC_GPIO_DETECT) {
+ rc = gpio_get_value_cansleep(
+ dd->accessory_info[i].gpio);
+ if (rc < 0)
+ continue;
+
+ if (rc ^ dd->accessory_info[i].active_low)
+ detected = 1;
+ else
+ continue;
+ }
+ if (dd->accessory_info[i].detect_flags & OTHC_ADC_DETECT)
+ detected = accessory_adc_detect(dd, i);
+
+ if (detected)
+ break;
+ }
+
+ if (detected) {
+ dd->curr_accessory = dd->accessory_info[i].accessory;
+ dd->curr_accessory_code = dd->accessory_info[i].key_code;
+
+ /* if Video out cable detected enable the video path*/
+ if (dd->curr_accessory == OTHC_SVIDEO_OUT) {
+ pm8058_othc_svideo_enable(
+ dd->othc_pdata->micbias_select, true);
+
+ } else {
+ switch_set_state(&dd->othc_sdev, dd->curr_accessory);
+ input_report_switch(dd->othc_ipd,
+ dd->curr_accessory_code, 1);
+ input_sync(dd->othc_ipd);
+ }
+ pr_info("Accessory [%d] inserted\n", dd->curr_accessory);
+ } else
+ pr_info("Unable to detect accessory. False interrupt!\n");
+
+ return 0;
+
+fail_ir_accessory:
+ return rc;
+}
+
+static void detect_work_f(struct work_struct *work)
+{
+ int rc;
+ struct pm8058_othc *dd =
+ container_of(work, struct pm8058_othc, detect_work.work);
+
+ if (dd->othc_ir_state) {
+ /* inserted */
+ rc = pm8058_accessory_report(dd, 1);
+ if (rc)
+ pr_err("Accessory could not be detected\n");
+ } else {
+ /* removed */
+ rc = pm8058_accessory_report(dd, 0);
+ if (rc)
+ pr_err("Accessory could not be detected\n");
+ /* Clear existing switch state */
+ dd->othc_sw_state = false;
+ }
+ enable_irq(dd->othc_irq_ir);
+}
+
+/*
+ * The pm8058_no_sw detects the switch press and release operation.
+ * The odd number call is press and even number call is release.
+ * The current state of the button is maintained in othc_sw_state variable.
+ * This isr gets called only for NO type headsets.
+ */
+static irqreturn_t pm8058_no_sw(int irq, void *dev_id)
+{
+ int level;
+ struct pm8058_othc *dd = dev_id;
+ unsigned long flags;
+
+ /* Check if headset has been inserted, else return */
+ if (!dd->othc_ir_state)
+ return IRQ_HANDLED;
+
+ spin_lock_irqsave(&dd->lock, flags);
+ if (dd->switch_reject == true) {
+ pr_debug("Rejected switch interrupt\n");
+ spin_unlock_irqrestore(&dd->lock, flags);
+ return IRQ_HANDLED;
+ }
+ spin_unlock_irqrestore(&dd->lock, flags);
+
+ level = pm8058_irq_get_rt_status(dd->pm_chip, dd->othc_irq_sw);
+ if (level < 0) {
+ pr_err("Unable to read IRQ status register\n");
+ return IRQ_HANDLED;
+ }
+
+ if (dd->othc_support_n_switch == true) {
+ if (level == 0) {
+ dd->othc_sw_state = false;
+ input_report_key(dd->othc_ipd, dd->sw_key_code, 0);
+ input_sync(dd->othc_ipd);
+ } else {
+ disable_irq_nosync(dd->othc_irq_sw);
+ schedule_work(&dd->switch_work);
+ }
+ return IRQ_HANDLED;
+ }
+ /*
+ * It is necessary to check the software state and the hardware state
+ * to make sure that the residual interrupt after the debounce time does
+ * not disturb the software state machine.
+ */
+ if (level == 1 && dd->othc_sw_state == false) {
+ /* Switch has been pressed */
+ dd->othc_sw_state = true;
+ input_report_key(dd->othc_ipd, KEY_MEDIA, 1);
+ } else if (level == 0 && dd->othc_sw_state == true) {
+ /* Switch has been released */
+ dd->othc_sw_state = false;
+ input_report_key(dd->othc_ipd, KEY_MEDIA, 0);
+ }
+ input_sync(dd->othc_ipd);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * The pm8058_nc_ir detects insert / remove of the headset (for NO),
+ * The current state of the headset is maintained in othc_ir_state variable.
+ * Due to a hardware bug, false switch interrupts are seen during headset
+ * insert. This is handled in the software by rejecting the switch interrupts
+ * for a small period of time after the headset has been inserted.
+ */
+static irqreturn_t pm8058_nc_ir(int irq, void *dev_id)
+{
+ unsigned long flags, rc;
+ struct pm8058_othc *dd = dev_id;
+
+ spin_lock_irqsave(&dd->lock, flags);
+ /* Enable the switch reject flag */
+ dd->switch_reject = true;
+ spin_unlock_irqrestore(&dd->lock, flags);
+
+ /* Start the HR timer if one is not active */
+ if (hrtimer_active(&dd->timer))
+ hrtimer_cancel(&dd->timer);
+
+ hrtimer_start(&dd->timer,
+ ktime_set((dd->switch_debounce_ms / 1000),
+ (dd->switch_debounce_ms % 1000) * 1000000), HRTIMER_MODE_REL);
+
+ /* disable irq, this gets enabled in the workqueue */
+ disable_irq_nosync(dd->othc_irq_ir);
+
+ /* Check the MIC_BIAS status, to check if inserted or removed */
+ rc = pm8058_irq_get_rt_status(dd->pm_chip, dd->othc_irq_ir);
+ if (rc < 0) {
+ pr_err("Unable to read IR status\n");
+ goto fail_ir;
+ }
+
+ dd->othc_ir_state = rc;
+ schedule_delayed_work(&dd->detect_work,
+ msecs_to_jiffies(dd->detection_delay_ms));
+
+fail_ir:
+ return IRQ_HANDLED;
+}
+
+static int pm8058_configure_micbias(struct pm8058_othc *dd)
+{
+ int rc;
+ u8 reg, value;
+ u32 value1;
+ u16 base_addr = dd->othc_base;
+ struct hsed_bias_config *hsed_config =
+ dd->othc_pdata->hsed_config->hsed_bias_config;
+
+ /* Intialize the OTHC module */
+ /* Control Register 1*/
+ rc = pm8058_read(dd->pm_chip, base_addr, ®, 1);
+ if (rc < 0) {
+ pr_err("PM8058 read failed\n");
+ return rc;
+ }
+
+ /* set iDAC high current threshold */
+ value = (hsed_config->othc_highcurr_thresh_uA / 100) - 2;
+ reg = (reg & PM8058_OTHC_HIGH_CURR_MASK) | value;
+
+ rc = pm8058_write(dd->pm_chip, base_addr, ®, 1);
+ if (rc < 0) {
+ pr_err("PM8058 read failed\n");
+ return rc;
+ }
+
+ /* Control register 2*/
+ rc = pm8058_read(dd->pm_chip, base_addr + 1, ®, 1);
+ if (rc < 0) {
+ pr_err("PM8058 read failed\n");
+ return rc;
+ }
+
+ value = dd->othc_pdata->micbias_enable;
+ reg &= PM8058_OTHC_EN_SIG_MASK;
+ reg |= (value << PM8058_OTHC_EN_SIG_SHIFT);
+
+ value = 0;
+ value1 = (hsed_config->othc_hyst_prediv_us << 10) / USEC_PER_SEC;
+ while (value1 != 0) {
+ value1 = value1 >> 1;
+ value++;
+ }
+ if (value > 7) {
+ pr_err("Invalid input argument - othc_hyst_prediv_us\n");
+ return -EINVAL;
+ }
+ reg &= PM8058_OTHC_HYST_PREDIV_MASK;
+ reg |= (value << PM8058_OTHC_HYST_PREDIV_SHIFT);
+
+ value = 0;
+ value1 = (hsed_config->othc_period_clkdiv_us << 10) / USEC_PER_SEC;
+ while (value1 != 1) {
+ value1 = value1 >> 1;
+ value++;
+ }
+ if (value > 8) {
+ pr_err("Invalid input argument - othc_period_clkdiv_us\n");
+ return -EINVAL;
+ }
+ reg = (reg & PM8058_OTHC_CLK_PREDIV_MASK) | (value - 1);
+
+ rc = pm8058_write(dd->pm_chip, base_addr + 1, ®, 1);
+ if (rc < 0) {
+ pr_err("PM8058 read failed\n");
+ return rc;
+ }
+
+ /* Control register 3 */
+ rc = pm8058_read(dd->pm_chip, base_addr + 2 , ®, 1);
+ if (rc < 0) {
+ pr_err("PM8058 read failed\n");
+ return rc;
+ }
+
+ value = hsed_config->othc_hyst_clk_us /
+ hsed_config->othc_hyst_prediv_us;
+ if (value > 15) {
+ pr_err("Invalid input argument - othc_hyst_prediv_us\n");
+ return -EINVAL;
+ }
+ reg &= PM8058_OTHC_HYST_CLK_MASK;
+ reg |= value << PM8058_OTHC_HYST_CLK_SHIFT;
+
+ value = hsed_config->othc_period_clk_us /
+ hsed_config->othc_period_clkdiv_us;
+ if (value > 15) {
+ pr_err("Invalid input argument - othc_hyst_prediv_us\n");
+ return -EINVAL;
+ }
+ reg = (reg & PM8058_OTHC_PERIOD_CLK_MASK) | value;
+
+ rc = pm8058_write(dd->pm_chip, base_addr + 2, ®, 1);
+ if (rc < 0) {
+ pr_err("PM8058 read failed\n");
+ return rc;
+ }
+
+ return 0;
+}
+
+static ssize_t othc_headset_print_name(struct switch_dev *sdev, char *buf)
+{
+ switch (switch_get_state(sdev)) {
+ case OTHC_NO_DEVICE:
+ return sprintf(buf, "No Device\n");
+ case OTHC_HEADSET:
+ case OTHC_HEADPHONE:
+ case OTHC_MICROPHONE:
+ case OTHC_ANC_HEADSET:
+ case OTHC_ANC_HEADPHONE:
+ case OTHC_ANC_MICROPHONE:
+ return sprintf(buf, "Headset\n");
+ }
+ return -EINVAL;
+}
+
+static int pm8058_configure_switch(struct pm8058_othc *dd)
+{
+ int rc, i;
+
+ if (dd->othc_support_n_switch == true) {
+ /* n-switch support */
+ rc = adc_channel_open(dd->switch_config->adc_channel,
+ &dd->adc_handle);
+ if (rc) {
+ pr_err("Unable to open ADC channel\n");
+ return -ENODEV;
+ }
+
+ for (i = 0; i < dd->switch_config->num_keys; i++) {
+ input_set_capability(dd->othc_ipd, EV_KEY,
+ dd->switch_config->switch_info[i].key_code);
+ }
+ } else /* Only single switch supported */
+ input_set_capability(dd->othc_ipd, EV_KEY, KEY_MEDIA);
+
+ return 0;
+}
+
+static int
+pm8058_configure_accessory(struct pm8058_othc *dd)
+{
+ int i, rc;
+ char name[OTHC_GPIO_MAX_LEN];
+
+ /*
+ * Not bailing out if the gpio_* configure calls fail. This is required
+ * as multiple accessories are detected by the same gpio.
+ */
+ for (i = 0; i < dd->num_accessories; i++) {
+ if (dd->accessory_info[i].enabled == false)
+ continue;
+ if (dd->accessory_info[i].detect_flags & OTHC_GPIO_DETECT) {
+ snprintf(name, OTHC_GPIO_MAX_LEN, "%s%d",
+ "othc_acc_gpio_", i);
+ rc = gpio_request(dd->accessory_info[i].gpio, name);
+ if (rc) {
+ pr_debug("Unable to request GPIO [%d]\n",
+ dd->accessory_info[i].gpio);
+ continue;
+ }
+ rc = gpio_direction_input(dd->accessory_info[i].gpio);
+ if (rc) {
+ pr_debug("Unable to set-direction GPIO [%d]\n",
+ dd->accessory_info[i].gpio);
+ gpio_free(dd->accessory_info[i].gpio);
+ continue;
+ }
+ }
+ input_set_capability(dd->othc_ipd, EV_SW,
+ dd->accessory_info[i].key_code);
+ }
+
+ if (dd->accessories_adc_support) {
+ /*
+ * Check if 3 switch is supported. If both are using the same
+ * ADC channel, the same handle can be used.
+ */
+ if (dd->othc_support_n_switch) {
+ if (dd->adc_handle != NULL &&
+ (dd->accessories_adc_channel ==
+ dd->switch_config->adc_channel))
+ dd->accessory_adc_handle = dd->adc_handle;
+ } else {
+ rc = adc_channel_open(dd->accessories_adc_channel,
+ &dd->accessory_adc_handle);
+ if (rc) {
+ pr_err("Unable to open ADC channel\n");
+ rc = -ENODEV;
+ goto accessory_adc_fail;
+ }
+ }
+ if (dd->video_out_gpio != 0) {
+ rc = gpio_request(dd->video_out_gpio, "vout_enable");
+ if (rc < 0) {
+ pr_err("request VOUT gpio failed (%d)\n", rc);
+ goto accessory_adc_fail;
+ }
+ rc = gpio_direction_output(dd->video_out_gpio, 0);
+ if (rc < 0) {
+ pr_err("direction_out failed (%d)\n", rc);
+ goto accessory_adc_fail;
+ }
+ }
+
+ }
+
+ return 0;
+
+accessory_adc_fail:
+ for (i = 0; i < dd->num_accessories; i++) {
+ if (dd->accessory_info[i].enabled == false)
+ continue;
+ gpio_free(dd->accessory_info[i].gpio);
+ }
+ return rc;
+}
+
+static int
+othc_configure_hsed(struct pm8058_othc *dd, struct platform_device *pd)
+{
+ int rc;
+ struct input_dev *ipd;
+ struct pmic8058_othc_config_pdata *pdata = pd->dev.platform_data;
+ struct othc_hsed_config *hsed_config = pdata->hsed_config;
+
+ dd->othc_sdev.name = "h2w";
+ dd->othc_sdev.print_name = othc_headset_print_name;
+
+ rc = switch_dev_register(&dd->othc_sdev);
+ if (rc) {
+ pr_err("Unable to register switch device\n");
+ return rc;
+ }
+
+ ipd = input_allocate_device();
+ if (ipd == NULL) {
+ pr_err("Unable to allocate memory\n");
+ rc = -ENOMEM;
+ goto fail_input_alloc;
+ }
+
+ /* Get the IRQ for Headset Insert-remove and Switch-press */
+ dd->othc_irq_sw = platform_get_irq(pd, 0);
+ dd->othc_irq_ir = platform_get_irq(pd, 1);
+ if (dd->othc_irq_ir < 0 || dd->othc_irq_sw < 0) {
+ pr_err("othc resource:IRQs absent\n");
+ rc = -ENXIO;
+ goto fail_micbias_config;
+ }
+
+ if (pdata->hsed_name != NULL)
+ ipd->name = pdata->hsed_name;
+ else
+ ipd->name = "pmic8058_othc";
+
+ ipd->phys = "pmic8058_othc/input0";
+ ipd->dev.parent = &pd->dev;
+
+ dd->othc_ipd = ipd;
+ dd->ir_gpio = hsed_config->ir_gpio;
+ dd->othc_sw_state = false;
+ dd->switch_debounce_ms = hsed_config->switch_debounce_ms;
+ dd->othc_support_n_switch = hsed_config->othc_support_n_switch;
+ dd->accessory_support = pdata->hsed_config->accessories_support;
+ dd->detection_delay_ms = pdata->hsed_config->detection_delay_ms;
+
+ if (dd->othc_support_n_switch == true)
+ dd->switch_config = hsed_config->switch_config;
+
+ if (dd->accessory_support == true) {
+ dd->accessory_info = pdata->hsed_config->accessories;
+ dd->num_accessories = pdata->hsed_config->othc_num_accessories;
+ dd->accessories_adc_support =
+ pdata->hsed_config->accessories_adc_support;
+ dd->accessories_adc_channel =
+ pdata->hsed_config->accessories_adc_channel;
+ dd->video_out_gpio = pdata->hsed_config->video_out_gpio;
+ }
+
+ /* Configure the MIC_BIAS line for headset detection */
+ rc = pm8058_configure_micbias(dd);
+ if (rc < 0)
+ goto fail_micbias_config;
+
+ /* Configure for the switch events */
+ rc = pm8058_configure_switch(dd);
+ if (rc < 0)
+ goto fail_micbias_config;
+
+ /* Configure the accessory */
+ if (dd->accessory_support == true) {
+ rc = pm8058_configure_accessory(dd);
+ if (rc < 0)
+ goto fail_micbias_config;
+ }
+
+ input_set_drvdata(ipd, dd);
+ spin_lock_init(&dd->lock);
+
+ rc = input_register_device(ipd);
+ if (rc) {
+ pr_err("Unable to register OTHC device\n");
+ goto fail_micbias_config;
+ }
+
+ hrtimer_init(&dd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ dd->timer.function = pm8058_othc_timer;
+
+ /* Request the HEADSET IR interrupt */
+ if (dd->ir_gpio < 0) {
+ rc = request_threaded_irq(dd->othc_irq_ir, NULL, pm8058_nc_ir,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+ "pm8058_othc_ir", dd);
+ if (rc < 0) {
+ pr_err("Unable to request pm8058_othc_ir IRQ\n");
+ goto fail_ir_irq;
+ }
+ } else {
+ rc = gpio_request(dd->ir_gpio, "othc_ir_gpio");
+ if (rc) {
+ pr_err("Unable to request IR GPIO\n");
+ goto fail_ir_gpio_req;
+ }
+ rc = gpio_direction_input(dd->ir_gpio);
+ if (rc) {
+ pr_err("GPIO %d set_direction failed\n", dd->ir_gpio);
+ goto fail_ir_irq;
+ }
+ dd->othc_irq_ir = gpio_to_irq(dd->ir_gpio);
+ rc = request_any_context_irq(dd->othc_irq_ir, ir_gpio_irq,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+ "othc_gpio_ir_irq", dd);
+ if (rc < 0) {
+ pr_err("could not request hs irq err=%d\n", rc);
+ goto fail_ir_irq;
+ }
+ }
+ /* Request the SWITCH press/release interrupt */
+ rc = request_threaded_irq(dd->othc_irq_sw, NULL, pm8058_no_sw,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+ "pm8058_othc_sw", dd);
+ if (rc < 0) {
+ pr_err("Unable to request pm8058_othc_sw IRQ\n");
+ goto fail_sw_irq;
+ }
+
+ /* Check if the accessory is already inserted during boot up */
+ if (dd->ir_gpio < 0) {
+ rc = pm8058_irq_get_rt_status(dd->pm_chip, dd->othc_irq_ir);
+ if (rc < 0) {
+ pr_err("Unable to get accessory status at boot\n");
+ goto fail_ir_status;
+ }
+ } else {
+ rc = gpio_get_value_cansleep(dd->ir_gpio);
+ if (rc < 0) {
+ pr_err("Unable to get accessory status at boot\n");
+ goto fail_ir_status;
+ }
+ rc = !rc;
+ }
+ if (rc) {
+ pr_debug("Accessory inserted during boot up\n");
+ /* process the data and report the inserted accessory */
+ rc = pm8058_accessory_report(dd, 1);
+ if (rc)
+ pr_debug("Unabele to detect accessory at boot up\n");
+ }
+
+ device_init_wakeup(&pd->dev,
+ hsed_config->hsed_bias_config->othc_wakeup);
+
+ INIT_DELAYED_WORK(&dd->detect_work, detect_work_f);
+
+ INIT_DELAYED_WORK(&dd->hs_work, hs_worker);
+
+ if (dd->othc_support_n_switch == true)
+ INIT_WORK(&dd->switch_work, switch_work_f);
+
+
+ return 0;
+
+fail_ir_status:
+ free_irq(dd->othc_irq_sw, dd);
+fail_sw_irq:
+ free_irq(dd->othc_irq_ir, dd);
+fail_ir_irq:
+ if (dd->ir_gpio != -1)
+ gpio_free(dd->ir_gpio);
+fail_ir_gpio_req:
+ input_unregister_device(ipd);
+ dd->othc_ipd = NULL;
+fail_micbias_config:
+ input_free_device(ipd);
+fail_input_alloc:
+ switch_dev_unregister(&dd->othc_sdev);
+ return rc;
+}
+
+static int __devinit pm8058_othc_probe(struct platform_device *pd)
+{
+ int rc;
+ struct pm8058_othc *dd;
+ struct pm8058_chip *chip;
+ struct resource *res;
+ struct pmic8058_othc_config_pdata *pdata = pd->dev.platform_data;
+
+ chip = dev_get_drvdata(pd->dev.parent);
+ if (chip == NULL) {
+ pr_err("Invalid driver information\n");
+ return -EINVAL;
+ }
+
+ /* Check PMIC8058 version. A0 version is not supported */
+ if (pm8058_rev(chip) == PM_8058_REV_1p0) {
+ pr_err("PMIC8058 version not supported\n");
+ return -ENODEV;
+ }
+
+ if (pdata == NULL) {
+ pr_err("Platform data not present\n");
+ return -EINVAL;
+ }
+
+ dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+ if (dd == NULL) {
+ pr_err("Unable to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ /* Enable runtime PM ops, start in ACTIVE mode */
+ rc = pm_runtime_set_active(&pd->dev);
+ if (rc < 0)
+ dev_dbg(&pd->dev, "unable to set runtime pm state\n");
+ pm_runtime_enable(&pd->dev);
+
+ res = platform_get_resource_byname(pd, IORESOURCE_IO, "othc_base");
+ if (res == NULL) {
+ pr_err("othc resource:Base address absent\n");
+ rc = -ENXIO;
+ goto fail_get_res;
+ }
+
+ dd->othc_pdata = pdata;
+ dd->pm_chip = chip;
+ dd->othc_base = res->start;
+ if (pdata->micbias_regulator == NULL) {
+ pr_err("OTHC regulator not specified\n");
+ goto fail_get_res;
+ }
+
+ dd->othc_vreg = regulator_get(NULL,
+ pdata->micbias_regulator->regulator);
+ if (IS_ERR(dd->othc_vreg)) {
+ pr_err("regulator get failed\n");
+ rc = PTR_ERR(dd->othc_vreg);
+ goto fail_get_res;
+ }
+
+ rc = regulator_set_voltage(dd->othc_vreg,
+ pdata->micbias_regulator->min_uV,
+ pdata->micbias_regulator->max_uV);
+ if (rc) {
+ pr_err("othc regulator set voltage failed\n");
+ goto fail_reg_enable;
+ }
+
+ rc = regulator_enable(dd->othc_vreg);
+ if (rc) {
+ pr_err("othc regulator enable failed\n");
+ goto fail_reg_enable;
+ }
+
+ platform_set_drvdata(pd, dd);
+
+ if (pdata->micbias_capability == OTHC_MICBIAS_HSED) {
+ /* HSED to be supported on this MICBIAS line */
+ if (pdata->hsed_config != NULL) {
+ rc = othc_configure_hsed(dd, pd);
+ if (rc < 0)
+ goto fail_othc_hsed;
+ } else {
+ pr_err("HSED config data not present\n");
+ rc = -EINVAL;
+ goto fail_othc_hsed;
+ }
+ }
+
+ /* Store the local driver data structure */
+ if (dd->othc_pdata->micbias_select < OTHC_MICBIAS_MAX)
+ config[dd->othc_pdata->micbias_select] = dd;
+
+ pr_debug("Device %s:%d successfully registered\n",
+ pd->name, pd->id);
+ return 0;
+
+fail_othc_hsed:
+ regulator_disable(dd->othc_vreg);
+fail_reg_enable:
+ regulator_put(dd->othc_vreg);
+fail_get_res:
+ pm_runtime_set_suspended(&pd->dev);
+ pm_runtime_disable(&pd->dev);
+
+ kfree(dd);
+ return rc;
+}
+
+static struct platform_driver pm8058_othc_driver = {
+ .driver = {
+ .name = "pm8058-othc",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &pm8058_othc_pm_ops,
+#endif
+ },
+ .probe = pm8058_othc_probe,
+ .remove = __devexit_p(pm8058_othc_remove),
+};
+
+static int __init pm8058_othc_init(void)
+{
+ return platform_driver_register(&pm8058_othc_driver);
+}
+
+static void __exit pm8058_othc_exit(void)
+{
+ platform_driver_unregister(&pm8058_othc_driver);
+}
+/*
+ * Move to late_initcall, to make sure that the ADC driver registration is
+ * completed before we open a ADC channel.
+ */
+late_initcall(pm8058_othc_init);
+module_exit(pm8058_othc_exit);
+
+MODULE_ALIAS("platform:pmic8058_othc");
+MODULE_DESCRIPTION("PMIC 8058 OTHC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/pmic8058-pwrkey.c b/drivers/input/misc/pmic8058-pwrkey.c
new file mode 100644
index 0000000..a981013
--- /dev/null
+++ b/drivers/input/misc/pmic8058-pwrkey.c
@@ -0,0 +1,375 @@
+/* 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/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/pmic8058-pwrkey.h>
+#include <linux/log2.h>
+#include <linux/spinlock.h>
+#include <linux/hrtimer.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+
+#define PON_CNTL_1 0x1C
+#define PON_CNTL_PULL_UP BIT(7)
+#define PON_CNTL_TRIG_DELAY_MASK (0x7)
+
+struct pmic8058_pwrkey {
+ struct input_dev *pwr;
+ int key_press_irq;
+ int key_release_irq;
+ struct pm8058_chip *pm_chip;
+ struct hrtimer timer;
+ bool key_pressed;
+ bool pressed_first;
+ struct pmic8058_pwrkey_pdata *pdata;
+ spinlock_t lock;
+};
+
+static enum hrtimer_restart pmic8058_pwrkey_timer(struct hrtimer *timer)
+{
+ unsigned long flags;
+ struct pmic8058_pwrkey *pwrkey = container_of(timer,
+ struct pmic8058_pwrkey, timer);
+
+ spin_lock_irqsave(&pwrkey->lock, flags);
+ pwrkey->key_pressed = true;
+
+ input_report_key(pwrkey->pwr, KEY_POWER, 1);
+ input_sync(pwrkey->pwr);
+ spin_unlock_irqrestore(&pwrkey->lock, flags);
+
+ return HRTIMER_NORESTART;
+}
+
+static irqreturn_t pwrkey_press_irq(int irq, void *_pwrkey)
+{
+ struct pmic8058_pwrkey *pwrkey = _pwrkey;
+ struct pmic8058_pwrkey_pdata *pdata = pwrkey->pdata;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pwrkey->lock, flags);
+ if (pwrkey->pressed_first) {
+ /*
+ * If pressed_first flag is set already then release interrupt
+ * has occured first. Events are handled in the release IRQ so
+ * return.
+ */
+ pwrkey->pressed_first = false;
+ spin_unlock_irqrestore(&pwrkey->lock, flags);
+ return IRQ_HANDLED;
+ } else {
+ pwrkey->pressed_first = true;
+ /*no pwrkey time duration, means no end key simulation*/
+ if (!pwrkey->pdata->pwrkey_time_ms) {
+ input_report_key(pwrkey->pwr, KEY_POWER, 1);
+ input_sync(pwrkey->pwr);
+ spin_unlock_irqrestore(&pwrkey->lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ input_report_key(pwrkey->pwr, KEY_END, 1);
+ input_sync(pwrkey->pwr);
+
+ hrtimer_start(&pwrkey->timer,
+ ktime_set(pdata->pwrkey_time_ms / 1000,
+ (pdata->pwrkey_time_ms % 1000) * 1000000),
+ HRTIMER_MODE_REL);
+ }
+ spin_unlock_irqrestore(&pwrkey->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t pwrkey_release_irq(int irq, void *_pwrkey)
+{
+ struct pmic8058_pwrkey *pwrkey = _pwrkey;
+ unsigned long flags;
+
+ spin_lock_irqsave(&pwrkey->lock, flags);
+ if (pwrkey->pressed_first) {
+ pwrkey->pressed_first = false;
+ /* no pwrkey time, means no delay in pwr key reporting */
+ if (!pwrkey->pdata->pwrkey_time_ms) {
+ input_report_key(pwrkey->pwr, KEY_POWER, 0);
+ input_sync(pwrkey->pwr);
+ spin_unlock_irqrestore(&pwrkey->lock, flags);
+ return IRQ_HANDLED;
+ }
+
+ hrtimer_cancel(&pwrkey->timer);
+
+ if (pwrkey->key_pressed) {
+ pwrkey->key_pressed = false;
+ input_report_key(pwrkey->pwr, KEY_POWER, 0);
+ input_sync(pwrkey->pwr);
+ }
+
+ input_report_key(pwrkey->pwr, KEY_END, 0);
+ input_sync(pwrkey->pwr);
+ } else {
+ /*
+ * Set this flag true so that in the subsequent interrupt of
+ * press we can know release interrupt came first
+ */
+ pwrkey->pressed_first = true;
+ /* no pwrkey time, means no delay in pwr key reporting */
+ if (!pwrkey->pdata->pwrkey_time_ms) {
+ input_report_key(pwrkey->pwr, KEY_POWER, 1);
+ input_sync(pwrkey->pwr);
+ input_report_key(pwrkey->pwr, KEY_POWER, 0);
+ input_sync(pwrkey->pwr);
+ spin_unlock_irqrestore(&pwrkey->lock, flags);
+ return IRQ_HANDLED;
+ }
+ input_report_key(pwrkey->pwr, KEY_END, 1);
+ input_sync(pwrkey->pwr);
+ input_report_key(pwrkey->pwr, KEY_END, 0);
+ input_sync(pwrkey->pwr);
+ }
+ spin_unlock_irqrestore(&pwrkey->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+#ifdef CONFIG_PM
+static int pmic8058_pwrkey_suspend(struct device *dev)
+{
+ struct pmic8058_pwrkey *pwrkey = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev)) {
+ enable_irq_wake(pwrkey->key_press_irq);
+ enable_irq_wake(pwrkey->key_release_irq);
+ }
+
+ return 0;
+}
+
+static int pmic8058_pwrkey_resume(struct device *dev)
+{
+ struct pmic8058_pwrkey *pwrkey = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev)) {
+ disable_irq_wake(pwrkey->key_press_irq);
+ disable_irq_wake(pwrkey->key_release_irq);
+ }
+
+ return 0;
+}
+
+static struct dev_pm_ops pm8058_pwr_key_pm_ops = {
+ .suspend = pmic8058_pwrkey_suspend,
+ .resume = pmic8058_pwrkey_resume,
+};
+#endif
+
+static int __devinit pmic8058_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 pmic8058_pwrkey *pwrkey;
+ struct pmic8058_pwrkey_pdata *pdata = pdev->dev.platform_data;
+ struct pm8058_chip *pm_chip;
+
+ pm_chip = dev_get_drvdata(pdev->dev.parent);
+ if (pm_chip == NULL) {
+ dev_err(&pdev->dev, "no parent data passed in\n");
+ return -EFAULT;
+ }
+
+ 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 pwr key trigger delay\n");
+ return -EINVAL;
+ }
+
+ if (pdata->pwrkey_time_ms &&
+ (pdata->pwrkey_time_ms < 500 || pdata->pwrkey_time_ms > 1000)) {
+ dev_err(&pdev->dev, "invalid pwr key time supplied\n");
+ return -EINVAL;
+ }
+
+ pwrkey = kzalloc(sizeof(*pwrkey), GFP_KERNEL);
+ if (!pwrkey)
+ return -ENOMEM;
+
+ pwrkey->pm_chip = pm_chip;
+ pwrkey->pdata = pdata;
+ pwrkey->pressed_first = false;
+ /* Enable runtime PM ops, start in ACTIVE mode */
+ err = pm_runtime_set_active(&pdev->dev);
+ if (err < 0)
+ dev_dbg(&pdev->dev, "unable to set runtime pm state\n");
+ pm_runtime_enable(&pdev->dev);
+
+ 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);
+ input_set_capability(pwr, EV_KEY, KEY_END);
+
+ pwr->name = "pmic8058_pwrkey";
+ pwr->phys = "pmic8058_pwrkey/input0";
+ pwr->dev.parent = &pdev->dev;
+
+ delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC;
+ delay = 1 + ilog2(delay);
+
+ err = pm8058_read(pwrkey->pm_chip, PON_CNTL_1, &pon_cntl, 1);
+ 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);
+ pon_cntl |= (pdata->pull_up ? PON_CNTL_PULL_UP : ~PON_CNTL_PULL_UP);
+ err = pm8058_write(pwrkey->pm_chip, PON_CNTL_1, &pon_cntl, 1);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err);
+ goto free_input_dev;
+ }
+
+ hrtimer_init(&pwrkey->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ pwrkey->timer.function = pmic8058_pwrkey_timer;
+
+ spin_lock_init(&pwrkey->lock);
+
+ 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->key_release_irq = key_release_irq;
+ pwrkey->pwr = pwr;
+
+ platform_set_drvdata(pdev, pwrkey);
+
+ /* Check if power-key is pressed at boot up */
+ err = pm8058_irq_get_rt_status(pwrkey->pm_chip, key_press_irq);
+ if (err < 0) {
+ dev_err(&pdev->dev, "Key-press status at boot failed rc=%d\n",
+ err);
+ goto unreg_input_dev;
+ }
+ if (err) {
+ if (!pwrkey->pdata->pwrkey_time_ms)
+ input_report_key(pwrkey->pwr, KEY_POWER, 1);
+ else
+ input_report_key(pwrkey->pwr, KEY_END, 1);
+ input_sync(pwrkey->pwr);
+ pwrkey->pressed_first = true;
+ }
+
+ err = request_threaded_irq(key_press_irq, NULL, pwrkey_press_irq,
+ IRQF_TRIGGER_RISING, "pmic8058_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_threaded_irq(key_release_irq, NULL, pwrkey_release_irq,
+ IRQF_TRIGGER_RISING, "pmic8058_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:
+ input_unregister_device(pwr);
+ pwr = NULL;
+free_input_dev:
+ input_free_device(pwr);
+free_pwrkey:
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ kfree(pwrkey);
+ return err;
+}
+
+static int __devexit pmic8058_pwrkey_remove(struct platform_device *pdev)
+{
+ struct pmic8058_pwrkey *pwrkey = platform_get_drvdata(pdev);
+ int key_release_irq = platform_get_irq(pdev, 0);
+ int key_press_irq = platform_get_irq(pdev, 1);
+
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ device_init_wakeup(&pdev->dev, 0);
+
+ free_irq(key_press_irq, pwrkey);
+ free_irq(key_release_irq, pwrkey);
+ input_unregister_device(pwrkey->pwr);
+ kfree(pwrkey);
+
+ return 0;
+}
+
+static struct platform_driver pmic8058_pwrkey_driver = {
+ .probe = pmic8058_pwrkey_probe,
+ .remove = __devexit_p(pmic8058_pwrkey_remove),
+ .driver = {
+ .name = "pm8058-pwrkey",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &pm8058_pwr_key_pm_ops,
+#endif
+ },
+};
+
+static int __init pmic8058_pwrkey_init(void)
+{
+ return platform_driver_register(&pmic8058_pwrkey_driver);
+}
+module_init(pmic8058_pwrkey_init);
+
+static void __exit pmic8058_pwrkey_exit(void)
+{
+ platform_driver_unregister(&pmic8058_pwrkey_driver);
+}
+module_exit(pmic8058_pwrkey_exit);
+
+MODULE_ALIAS("platform:pmic8058_pwrkey");
+MODULE_DESCRIPTION("PMIC8058 Power Key");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/pmic8058-vib-memless.c b/drivers/input/misc/pmic8058-vib-memless.c
new file mode 100644
index 0000000..ba05400
--- /dev/null
+++ b/drivers/input/misc/pmic8058-vib-memless.c
@@ -0,0 +1,282 @@
+/* Copyright (c) 2010, 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/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/pmic8058-vibrator.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/pm.h>
+#include <linux/input.h>
+#include <linux/slab.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 pmic8058_vib {
+ struct input_dev *info;
+ spinlock_t lock;
+ struct work_struct work;
+
+ bool enabled;
+ int speed;
+ struct device *dev;
+ struct pmic8058_vibrator_pdata *pdata;
+ int state;
+ int level;
+ u8 reg_vib_drv;
+
+ struct pm8058_chip *pm_chip;
+};
+
+/* REVISIT: just for debugging, will be removed in final working version */
+static void __dump_vib_regs(struct pmic8058_vib *vib, char *msg)
+{
+ u8 temp;
+
+ dev_dbg(vib->dev, "%s\n", msg);
+
+ pm8058_read(vib->pm_chip, VIB_DRV, &temp, 1);
+ dev_dbg(vib->dev, "VIB_DRV - %X\n", temp);
+}
+
+static int pmic8058_vib_read_u8(struct pmic8058_vib *vib,
+ u8 *data, u16 reg)
+{
+ int rc;
+
+ rc = pm8058_read(vib->pm_chip, reg, data, 1);
+ if (rc < 0)
+ dev_warn(vib->dev, "Error reading pmic8058: %X - ret %X\n",
+ reg, rc);
+
+ return rc;
+}
+
+static int pmic8058_vib_write_u8(struct pmic8058_vib *vib,
+ u8 data, u16 reg)
+{
+ int rc;
+
+ rc = pm8058_write(vib->pm_chip, reg, &data, 1);
+ if (rc < 0)
+ dev_warn(vib->dev, "Error writing pmic8058: %X - ret %X\n",
+ reg, rc);
+ return rc;
+}
+
+static int pmic8058_vib_set(struct pmic8058_vib *vib, int on)
+{
+ int rc;
+ u8 val;
+
+ if (on) {
+ val = vib->reg_vib_drv;
+ val |= ((vib->level << VIB_DRV_SEL_SHIFT) & VIB_DRV_SEL_MASK);
+ rc = pmic8058_vib_write_u8(vib, val, VIB_DRV);
+ if (rc < 0)
+ return rc;
+ vib->reg_vib_drv = val;
+ vib->enabled = 1;
+
+ } else {
+ val = vib->reg_vib_drv;
+ val &= ~VIB_DRV_SEL_MASK;
+ rc = pmic8058_vib_write_u8(vib, val, VIB_DRV);
+ if (rc < 0)
+ return rc;
+ vib->reg_vib_drv = val;
+ vib->enabled = 0;
+ }
+ __dump_vib_regs(vib, "vib_set_end");
+
+ return rc;
+}
+
+static void pmic8058_work_handler(struct work_struct *work)
+{
+ u8 val;
+ int rc;
+ struct pmic8058_vib *info;
+
+ info = container_of(work, struct pmic8058_vib, work);
+
+ rc = pmic8058_vib_read_u8(info, &val, VIB_DRV);
+ if (rc < 0)
+ return;
+
+ /*
+ * Vibrator support voltage ranges from 1.2 to 3.1V, so
+ * scale the FF speed to these range.
+ */
+ if (info->speed) {
+ info->state = 1;
+ info->level = ((VIB_MAX_LEVELS * info->speed) / MAX_FF_SPEED) +
+ VIB_MIN_LEVEL_mV;
+ info->level /= 100;
+ } else {
+ info->state = 0;
+ info->level = VIB_MIN_LEVEL_mV / 100;
+ }
+ pmic8058_vib_set(info, info->state);
+}
+
+static int pmic8058_vib_play_effect(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct pmic8058_vib *info = input_get_drvdata(dev);
+
+ info->speed = effect->u.rumble.strong_magnitude >> 8;
+ if (!info->speed)
+ info->speed = effect->u.rumble.weak_magnitude >> 9;
+ schedule_work(&info->work);
+ return 0;
+}
+
+static int __devinit pmic8058_vib_probe(struct platform_device *pdev)
+
+{
+ struct pmic8058_vibrator_pdata *pdata = pdev->dev.platform_data;
+ struct pmic8058_vib *vib;
+ u8 val;
+ int rc;
+
+ struct pm8058_chip *pm_chip;
+
+ pm_chip = dev_get_drvdata(pdev->parent.dev);
+ if (pm_chip == NULL) {
+ dev_err(&pdev->dev, "no parent data passed in\n");
+ return -EFAULT;
+ }
+
+ if (!pdata)
+ return -EINVAL;
+
+ if (pdata->level_mV < VIB_MIN_LEVEL_mV ||
+ pdata->level_mV > VIB_MAX_LEVEL_mV)
+ return -EINVAL;
+
+ vib = kzalloc(sizeof(*vib), GFP_KERNEL);
+ if (!vib)
+ return -ENOMEM;
+
+ vib->pm_chip = pm_chip;
+ vib->enabled = 0;
+ vib->pdata = pdata;
+ vib->level = pdata->level_mV / 100;
+ vib->dev = &pdev->dev;
+
+ spin_lock_init(&vib->lock);
+ INIT_WORK(&vib->work, pmic8058_work_handler);
+
+ vib->info = input_allocate_device();
+
+ if (vib->info == NULL) {
+ dev_err(&pdev->dev, "couldn't allocate input device\n");
+ return -ENOMEM;
+ }
+
+ input_set_drvdata(vib->info, vib);
+
+ vib->info->name = "pmic8058:vibrator";
+ vib->info->id.version = 1;
+ vib->info->dev.parent = pdev->dev.parent;
+
+ __set_bit(FF_RUMBLE, vib->info->ffbit);
+ __dump_vib_regs(vib, "boot_vib_default");
+
+ /* operate in manual mode */
+ rc = pmic8058_vib_read_u8(vib, &val, VIB_DRV);
+ if (rc < 0)
+ goto err_read_vib;
+ val &= ~VIB_DRV_EN_MANUAL_MASK;
+ rc = pmic8058_vib_write_u8(vib, val, VIB_DRV);
+ if (rc < 0)
+ goto err_read_vib;
+
+ vib->reg_vib_drv = val;
+
+ rc = input_ff_create_memless(vib->info, NULL, pmic8058_vib_play_effect);
+ if (rc < 0) {
+ dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n");
+ goto create_memless_err;
+ }
+
+ platform_set_drvdata(pdev, vib);
+
+ rc = input_register_device(vib->info);
+ if (rc < 0) {
+ dev_dbg(&pdev->dev, "couldn't register input device\n");
+ goto reg_err;
+ }
+
+ return 0;
+
+reg_err:
+ input_ff_destroy(vib->info);
+create_memless_err:
+ input_free_device(vib->info);
+err_read_vib:
+ kfree(vib);
+ return rc;
+}
+
+static int __devexit pmic8058_vib_remove(struct platform_device *pdev)
+{
+ struct pmic8058_vib *vib = platform_get_drvdata(pdev);
+
+ cancel_work_sync(&vib->work);
+ if (vib->enabled)
+ pmic8058_vib_set(vib, 0);
+
+ input_unregister_device(vib->info);
+ kfree(vib);
+
+ return 0;
+}
+
+static struct platform_driver pmic8058_vib_driver = {
+ .probe = pmic8058_vib_probe,
+ .remove = __devexit_p(pmic8058_vib_remove),
+ .driver = {
+ .name = "pm8058-vib",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pmic8058_vib_init(void)
+{
+ return platform_driver_register(&pmic8058_vib_driver);
+}
+module_init(pmic8058_vib_init);
+
+static void __exit pmic8058_vib_exit(void)
+{
+ platform_driver_unregister(&pmic8058_vib_driver);
+}
+module_exit(pmic8058_vib_exit);
+
+MODULE_ALIAS("platform:pmic8058_vib");
+MODULE_DESCRIPTION("PMIC8058 vibrator driver memless framework");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c
index b3cfb9c..02838e3 100644
--- a/drivers/input/misc/pmic8xxx-pwrkey.c
+++ b/drivers/input/misc/pmic8xxx-pwrkey.c
@@ -30,10 +30,12 @@
/**
* struct pmic8xxx_pwrkey - pmic8xxx pwrkey information
* @key_press_irq: key press irq number
+ * @pdata: platform data
*/
struct pmic8xxx_pwrkey {
struct input_dev *pwr;
int key_press_irq;
+ const struct pm8xxx_pwrkey_platform_data *pdata;
};
static irqreturn_t pwrkey_press_irq(int irq, void *_pwrkey)
@@ -107,6 +109,8 @@
if (!pwrkey)
return -ENOMEM;
+ pwrkey->pdata = pdata;
+
pwr = input_allocate_device();
if (!pwr) {
dev_dbg(&pdev->dev, "Can't allocate power button\n");
@@ -153,7 +157,7 @@
platform_set_drvdata(pdev, pwrkey);
- err = request_irq(key_press_irq, pwrkey_press_irq,
+ err = request_threaded_irq(key_press_irq, NULL, 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",
@@ -161,7 +165,7 @@
goto unreg_input_dev;
}
- err = request_irq(key_release_irq, pwrkey_release_irq,
+ err = request_threaded_irq(key_release_irq, NULL, 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",