Patchwork [RFC,v1,2/6] input: pm8058_keypad: Qualcomm PMIC8058 keypad controller driver

login
register
mail settings
Submitter Trilok Soni
Date Nov. 10, 2010, 12:47 p.m.
Message ID <1289393281-4459-3-git-send-email-tsoni@codeaurora.org>
Download mbox | patch
Permalink /patch/70629/
State New
Headers show

Comments

Trilok Soni - Nov. 10, 2010, 12:47 p.m.
Add Qualcomm PMIC8058 based keypad controller driver
supporting upto 18x8 matrix configuration.

Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Trilok Soni <tsoni@codeaurora.org>
---
 drivers/input/keyboard/Kconfig           |   11 +
 drivers/input/keyboard/Makefile          |    1 +
 drivers/input/keyboard/pmic8058-keypad.c |  769 ++++++++++++++++++++++++++++++
 include/linux/input/pmic8058-keypad.h    |   58 +++
 4 files changed, 839 insertions(+), 0 deletions(-)
 create mode 100644 drivers/input/keyboard/pmic8058-keypad.c
 create mode 100644 include/linux/input/pmic8058-keypad.h
Trilok Soni - Nov. 16, 2010, 3:49 a.m.
Hi Dmitry,

On 11/10/2010 6:17 PM, Trilok Soni wrote:
> Add Qualcomm PMIC8058 based keypad controller driver
> supporting upto 18x8 matrix configuration.
> 
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Signed-off-by: Trilok Soni <tsoni@codeaurora.org>
> ---
>  drivers/input/keyboard/Kconfig           |   11 +
>  drivers/input/keyboard/Makefile          |    1 +
>  drivers/input/keyboard/pmic8058-keypad.c |  769 ++++++++++++++++++++++++++++++
>  include/linux/input/pmic8058-keypad.h    |   58 +++
>  4 files changed, 839 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/input/keyboard/pmic8058-keypad.c
>  create mode 100644 include/linux/input/pmic8058-keypad.h

Any comments on this patch?

---Trilok Soni
Dmitry Torokhov - Nov. 17, 2010, 6:02 p.m.
On Wed, Nov 10, 2010 at 06:17:57PM +0530, Trilok Soni wrote:
> Add Qualcomm PMIC8058 based keypad controller driver
> supporting upto 18x8 matrix configuration.
> 

Looks good, just a couple of nitpicks:

> +struct pmic8058_kp {
> +	const struct pmic8058_keypad_data *pdata;
> +	struct input_dev *input;
> +	int key_sense_irq;
> +	int key_stuck_irq;
> +
> +	unsigned short *keycodes;

Since the size of keycode table is constant there is no need to allocate
it separately (and even if it was variable you still could move it to
the end of the structure and allocate together).

> +
> +static int pmic8058_detect_ghost_keys(struct pmic8058_kp *kp, u16 *new_state)

bool

> +{
> +	int row, found_first = -1;
> +	u16 check, row_state;
> +
> +	check = 0;
> +	for (row = 0; row < kp->pdata->num_rows; row++) {
> +		row_state = (~new_state[row]) &
> +				 ((1 << kp->pdata->num_cols) - 1);
> +
> +		if (hweight16(row_state) > 1) {
> +			if (found_first == -1)
> +				found_first = row;
> +			if (check & row_state) {
> +				dev_dbg(kp->dev, "detected ghost key on row[%d]"
> +					 " and row[%d]\n", found_first, row);
> +				return 1;

true

> +			}
> +		}
> +		check |= row_state;
> +	}
> +	return 0;

false

> +static int __devinit pmic8058_kp_probe(struct platform_device *pdev)
> +{
> +	struct pmic8058_keypad_data *pdata = pdev->dev.platform_data;
> +	const struct matrix_keymap_data *keymap_data;
> +	struct pmic8058_kp *kp;
> +	int rc;
> +	unsigned short *keycodes;
> +	u8 ctrl_val;
> +	struct pm8058_chip	*pm_chip;
> +
> +	pm_chip = platform_get_drvdata(pdev);
> +	if (pm_chip == NULL) {
> +		dev_err(&pdev->dev, "no parent data passed in\n");
> +		return -EFAULT;

EFAULT is really odd, maybe -EINVAL?

> +	}
> +
> +	/* Check PMIC8058 version. A0 version is not supported */
> +	if (pm8058_rev(pm_chip) == PM_8058_REV_1p0) {
> +		dev_err(&pdev->dev, "PMIC8058 1.0 version is not supported\n");
> +		return -ENODEV;
> +	}
> +
> +	if (!pdata || !pdata->num_cols || !pdata->num_rows ||
> +		pdata->num_cols > PM8058_MAX_COLS ||
> +		pdata->num_rows > PM8058_MAX_ROWS ||
> +		pdata->num_cols < PM8058_MIN_COLS ||
> +		pdata->num_rows < PM8058_MIN_ROWS) {
> +		dev_err(&pdev->dev, "invalid platform data\n");
> +		return -EINVAL;
> +	}
> +
> +	if (pdata->rows_gpio_start < 0 || pdata->cols_gpio_start < 0) {
> +		dev_err(&pdev->dev, "invalid gpio_start platform data\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!pdata->scan_delay_ms || pdata->scan_delay_ms > MAX_SCAN_DELAY
> +		|| pdata->scan_delay_ms < MIN_SCAN_DELAY ||
> +		!is_power_of_2(pdata->scan_delay_ms)) {
> +		dev_err(&pdev->dev, "invalid keypad scan time supplied\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!pdata->row_hold_ns || pdata->row_hold_ns > MAX_ROW_HOLD_DELAY
> +		|| pdata->row_hold_ns < MIN_ROW_HOLD_DELAY ||
> +		((pdata->row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) {
> +		dev_err(&pdev->dev, "invalid keypad row hold time supplied\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!pdata->debounce_ms
> +		|| ((pdata->debounce_ms % 5) != 0)
> +		|| pdata->debounce_ms > MAX_DEBOUNCE_TIME
> +		|| pdata->debounce_ms < MIN_DEBOUNCE_TIME) {
> +		dev_err(&pdev->dev, "invalid debounce time supplied\n");

Mixing style (logical || either in front of expression or after), please use
same style (and I prefer after).


> +		return -EINVAL;
> +	}
> +
> +	keymap_data = pdata->keymap_data;
> +	if (!keymap_data) {
> +		dev_err(&pdev->dev, "no keymap data supplied\n");
> +		return -EINVAL;
> +	}
> +
> +	kp = kzalloc(sizeof(*kp), GFP_KERNEL);
> +	if (!kp)
> +		return -ENOMEM;
> +
> +	keycodes = kzalloc(PM8058_MATRIX_MAX_SIZE * sizeof(*keycodes),
> +				 GFP_KERNEL);
> +	if (!keycodes) {
> +		rc = -ENOMEM;
> +		goto err_alloc_mem;
> +	}
> +
> +	platform_set_drvdata(pdev, kp);
> +
> +	kp->pdata	= pdata;
> +	kp->dev		= &pdev->dev;
> +	kp->keycodes	= keycodes;
> +	kp->pm_chip	= pm_chip;
> +
> +	kp->input = input_allocate_device();
> +	if (!kp->input) {
> +		dev_err(&pdev->dev, "unable to allocate input device\n");
> +		rc = -ENOMEM;
> +		goto err_alloc_device;
> +	}
> +
> +	kp->key_sense_irq = platform_get_irq(pdev, 0);
> +	if (kp->key_sense_irq < 0) {
> +		dev_err(&pdev->dev, "unable to get keypad sense irq\n");
> +		rc = -ENXIO;
> +		goto err_get_irq;
> +	}
> +
> +	kp->key_stuck_irq = platform_get_irq(pdev, 1);
> +	if (kp->key_stuck_irq < 0) {
> +		dev_err(&pdev->dev, "unable to get keypad stuck irq\n");
> +		rc = -ENXIO;
> +		goto err_get_irq;
> +	}
> +
> +	if (pdata->input_name)
> +		kp->input->name = pdata->input_name;
> +	else
> +		kp->input->name = "PMIC8058 keypad";
> +
> +	if (pdata->input_phys_device)
> +		kp->input->phys = pdata->input_phys_device;
> +	else
> +		kp->input->phys = "pmic8058_keypad/input0";

	kp->input->phys = pdata->input_phys_device ?: "pmic8058_keypad/input0";

Is shorter.

Thanks.
Dmitry Torokhov - Dec. 6, 2010, 6:14 p.m.
Hi Trilok,

On Wed, Nov 10, 2010 at 06:17:57PM +0530, Trilok Soni wrote:
> Add Qualcomm PMIC8058 based keypad controller driver
> supporting upto 18x8 matrix configuration.
> 

Looks very good, just a couple of small things:

> +
> +#include <linux/input/pmic8058-keypad.h>

Move to MFD directory with the rest of pmic8058 definitions?

> + */
> +struct pmic8058_kp {
> +	const struct pmic8058_keypad_data *pdata;
> +	struct input_dev *input;
> +	int key_sense_irq;
> +	int key_stuck_irq;
> +
> +	unsigned short *keycodes;

I'd pull the keycodes into this structure (at the end) so it can be
allocated in one shot. Hmm it even appears to be constant-sized. So just
declare it right here and be done with it.

> +
> +static int pmic8058_detect_ghost_keys(struct pmic8058_kp *kp, u16 *new_state)

bool

> +{
> +	int row, found_first = -1;
> +	u16 check, row_state;
> +
> +	check = 0;
> +	for (row = 0; row < kp->pdata->num_rows; row++) {
> +		row_state = (~new_state[row]) &
> +				 ((1 << kp->pdata->num_cols) - 1);
> +
> +		if (hweight16(row_state) > 1) {
> +			if (found_first == -1)
> +				found_first = row;
> +			if (check & row_state) {
> +				dev_dbg(kp->dev, "detected ghost key on row[%d]"
> +					 " and row[%d]\n", found_first, row);
> +				return 1;

true

> +			}
> +		}
> +		check |= row_state;
> +	}
> +	return 0;

false

> +
> +static int pmic8058_kpd_init(struct pmic8058_kp *kp)
> +{
> +	int bits, rc, cycles;
> +	u8 scan_val = 0, ctrl_val = 0;
> +	static u8 row_bits[] = {

const?

> +		0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7,
> +	};
> +
> +}
> +
> +static int pmic8058_kp_enable(struct pmic8058_kp *kp)
> +{
> +	int rc;
> +
> +	kp->ctrl_reg |= KEYP_CTRL_KEYP_EN;
> +
> +	rc = pmic8058_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL);
> +	if (rc < 0)
> +		return rc;
> +
> +	enable_irq(kp->key_sense_irq);
> +	enable_irq(kp->key_stuck_irq);
> +
> +	return rc;
> +}
> +
> +static int pmic8058_kp_disable(struct pmic8058_kp *kp)
> +{
> +	int rc;
> +
> +	kp->ctrl_reg &= ~KEYP_CTRL_KEYP_EN;
> +
> +	rc = pmic8058_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL);
> +	if (rc < 0)
> +		return rc;
> +
> +	disable_irq(kp->key_sense_irq);
> +	disable_irq(kp->key_stuck_irq);
> +
> +	return rc;
> +}
> +
> +static int pmic8058_kp_open(struct input_dev *dev)
> +{
> +	struct pmic8058_kp *kp = input_get_drvdata(dev);
> +
> +	return pmic8058_kp_enable(kp);
> +}
> +
> +static void pmic8058_kp_close(struct input_dev *dev)
> +{
> +	struct pmic8058_kp *kp = input_get_drvdata(dev);
> +
> +	pmic8058_kp_disable(kp);
> +}
> +

You need to protect suspend/resume from racing with open_close. Take
dev->mutex and act depending on whether there are users of the device.

> +	if (pdata->rows_gpio_start < 0 || pdata->cols_gpio_start < 0) {
> +		dev_err(&pdev->dev, "invalid gpio_start platform data\n");
> +		return -EINVAL;

These are declared as unsigned. Hmm, doesn't sparse catch it?

Thanks.
Trilok Soni - Dec. 7, 2010, 9:22 a.m.
Hi Dmitry

On 12/6/2010 11:44 PM, Dmitry Torokhov wrote:
> Hi Trilok,
> 
> On Wed, Nov 10, 2010 at 06:17:57PM +0530, Trilok Soni wrote:
>> Add Qualcomm PMIC8058 based keypad controller driver
>> supporting upto 18x8 matrix configuration.
>>
> 
> Looks very good, just a couple of small things:

Thanks for reviewing same revision of the patch again :)

> 
>> +
>> +#include <linux/input/pmic8058-keypad.h>
> 
> Move to MFD directory with the rest of pmic8058 definitions?

As discussed earlier we can either have one big header file having all of the
sub-device(s) or have separate file for each sub-device driver, I need to check
which one would be better but I will atleast move pmic8058-keypad.h header
to "mfd" directory, as these patch series is in RFC state, we can have final
changes like along with when core-driver gets submitted for review.

> 
>> + */
>> +struct pmic8058_kp {
>> +	const struct pmic8058_keypad_data *pdata;
>> +	struct input_dev *input;
>> +	int key_sense_irq;
>> +	int key_stuck_irq;
>> +
>> +	unsigned short *keycodes;
> 
> I'd pull the keycodes into this structure (at the end) so it can be
> allocated in one shot. Hmm it even appears to be constant-sized. So just
> declare it right here and be done with it.

Done. You had already commented on it during the 1st review :)

> 
>> +
>> +static int pmic8058_detect_ghost_keys(struct pmic8058_kp *kp, u16 *new_state)
> 
> bool

Done.

> 
>> +{
>> +	int row, found_first = -1;
>> +	u16 check, row_state;
>> +
>> +	check = 0;
>> +	for (row = 0; row < kp->pdata->num_rows; row++) {
>> +		row_state = (~new_state[row]) &
>> +				 ((1 << kp->pdata->num_cols) - 1);
>> +
>> +		if (hweight16(row_state) > 1) {
>> +			if (found_first == -1)
>> +				found_first = row;
>> +			if (check & row_state) {
>> +				dev_dbg(kp->dev, "detected ghost key on row[%d]"
>> +					 " and row[%d]\n", found_first, row);
>> +				return 1;
> 
> true
> 
>> +			}
>> +		}
>> +		check |= row_state;
>> +	}
>> +	return 0;
> 
> false

Done.

> 
>> +
>> +static int pmic8058_kpd_init(struct pmic8058_kp *kp)
>> +{
>> +	int bits, rc, cycles;
>> +	u8 scan_val = 0, ctrl_val = 0;
>> +	static u8 row_bits[] = {
> 
> const?
> 
>> +		0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7,
>> +	};

Sure.

>> +
>> +static int pmic8058_kp_open(struct input_dev *dev)
>> +{
>> +	struct pmic8058_kp *kp = input_get_drvdata(dev);
>> +
>> +	return pmic8058_kp_enable(kp);
>> +}
>> +
>> +static void pmic8058_kp_close(struct input_dev *dev)
>> +{
>> +	struct pmic8058_kp *kp = input_get_drvdata(dev);
>> +
>> +	pmic8058_kp_disable(kp);
>> +}
>> +
> 
> You need to protect suspend/resume from racing with open_close. Take
> dev->mutex and act depending on whether there are users of the device.

Sure.

> 
>> +	if (pdata->rows_gpio_start < 0 || pdata->cols_gpio_start < 0) {
>> +		dev_err(&pdev->dev, "invalid gpio_start platform data\n");
>> +		return -EINVAL;
> 
> These are declared as unsigned. Hmm, doesn't sparse catch it?

I will fix it.

---Trilok Soni
Dmitry Torokhov - Dec. 7, 2010, 9:30 a.m.
On Tue, Dec 07, 2010 at 02:52:11PM +0530, Trilok Soni wrote:
> Hi Dmitry
> 
> On 12/6/2010 11:44 PM, Dmitry Torokhov wrote:
> > Hi Trilok,
> > 
> > On Wed, Nov 10, 2010 at 06:17:57PM +0530, Trilok Soni wrote:
> >> Add Qualcomm PMIC8058 based keypad controller driver
> >> supporting upto 18x8 matrix configuration.
> >>
> > 
> > Looks very good, just a couple of small things:
> 
> Thanks for reviewing same revision of the patch again :)
> 

Oops ;) Did I need to review something else from you then? I have a
nagging feeling that I have a patch outstanding...
Trilok Soni - Dec. 7, 2010, 9:32 a.m.
Hi Dmitry,

On 12/7/2010 3:00 PM, Dmitry Torokhov wrote:
> 
> Oops ;) Did I need to review something else from you then? I have a
> nagging feeling that I have a patch outstanding...
> 

You need to review PMIC8058 OTHC patch.

https://patchwork.kernel.org/patch/314092/

---Trilok Soni

Patch

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index b8c51b9..d536acb 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -364,6 +364,17 @@  config KEYBOARD_PXA930_ROTARY
 	  To compile this driver as a module, choose M here: the
 	  module will be called pxa930_rotary.
 
+config KEYBOARD_PMIC8058
+	tristate "Qualcomm PMIC8058 keypad support"
+	depends on PMIC8058
+	help
+	  Say Y here if you want to enable the driver for the PMIC8058
+	  keypad provided as a reference design from Qualcomm. This is intended
+	  to support upto 18x8 matrix based keypad design.
+
+	  To compile this driver as a module, choose M here: the module will
+	  be called pmic8058-keypad.
+
 config KEYBOARD_SAMSUNG
 	tristate "Samsung keypad support"
 	depends on SAMSUNG_DEV_KEYPAD
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index a34452e..9ff8dbe 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -34,6 +34,7 @@  obj-$(CONFIG_KEYBOARD_OMAP4)		+= omap4-keypad.o
 obj-$(CONFIG_KEYBOARD_OPENCORES)	+= opencores-kbd.o
 obj-$(CONFIG_KEYBOARD_PXA27x)		+= pxa27x_keypad.o
 obj-$(CONFIG_KEYBOARD_PXA930_ROTARY)	+= pxa930_rotary.o
+obj-$(CONFIG_KEYBOARD_PMIC8058)		+= pmic8058-keypad.o
 obj-$(CONFIG_KEYBOARD_QT2160)		+= qt2160.o
 obj-$(CONFIG_KEYBOARD_SAMSUNG)		+= samsung-keypad.o
 obj-$(CONFIG_KEYBOARD_SH_KEYSC)		+= sh_keysc.o
diff --git a/drivers/input/keyboard/pmic8058-keypad.c b/drivers/input/keyboard/pmic8058-keypad.c
new file mode 100644
index 0000000..77ddda6
--- /dev/null
+++ b/drivers/input/keyboard/pmic8058-keypad.c
@@ -0,0 +1,769 @@ 
+/* Copyright (c) 2009-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.
+ *
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/bitops.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/delay.h>
+
+#include <linux/input/pmic8058-keypad.h>
+
+#define PM8058_MAX_ROWS		18
+#define PM8058_MAX_COLS		8
+#define PM8058_ROW_SHIFT	3
+#define PM8058_MATRIX_MAX_SIZE	(PM8058_MAX_ROWS * PM8058_MAX_COLS)
+
+#define PM8058_MIN_ROWS		5
+#define PM8058_MIN_COLS		5
+
+#define MAX_SCAN_DELAY		128
+#define MIN_SCAN_DELAY		1
+
+/* in nanoseconds */
+#define MAX_ROW_HOLD_DELAY	122000
+#define MIN_ROW_HOLD_DELAY	30500
+
+#define MAX_DEBOUNCE_TIME	20
+#define MIN_DEBOUNCE_TIME	5
+
+#define KEYP_CTRL			0x148
+
+#define KEYP_CTRL_EVNTS			BIT(0)
+#define KEYP_CTRL_EVNTS_MASK		0x3
+
+#define KEYP_CTRL_SCAN_COLS_SHIFT	5
+#define KEYP_CTRL_SCAN_COLS_MIN		5
+#define KEYP_CTRL_SCAN_COLS_BITS	0x3
+
+#define KEYP_CTRL_SCAN_ROWS_SHIFT	2
+#define KEYP_CTRL_SCAN_ROWS_MIN		5
+#define KEYP_CTRL_SCAN_ROWS_BITS	0x7
+
+#define KEYP_CTRL_KEYP_EN		BIT(7)
+
+#define KEYP_SCAN			0x149
+
+#define KEYP_SCAN_READ_STATE		BIT(0)
+#define KEYP_SCAN_DBOUNCE_SHIFT		1
+#define KEYP_SCAN_PAUSE_SHIFT		3
+#define KEYP_SCAN_ROW_HOLD_SHIFT	6
+
+#define KEYP_TEST			0x14A
+
+#define KEYP_TEST_CLEAR_RECENT_SCAN	BIT(6)
+#define KEYP_TEST_CLEAR_OLD_SCAN	BIT(5)
+#define KEYP_TEST_READ_RESET		BIT(4)
+#define KEYP_TEST_DTEST_EN		BIT(3)
+#define KEYP_TEST_ABORT_READ		BIT(0)
+
+#define KEYP_TEST_DBG_SELECT_SHIFT	1
+
+/* bits of these registers represent
+ * '0' for key press
+ * '1' for key release
+ */
+#define KEYP_RECENT_DATA		0x14B
+#define KEYP_OLD_DATA			0x14C
+
+#define KEYP_CLOCK_FREQ			32768
+
+/**
+ * struct pmic8058_kp - internal keypad data structure
+ * @pdata - keypad platform data pointer
+ * @input - input device pointer for keypad
+ * @key_sense_irq - key press/release irq number
+ * @key_stuck_irq - key stuck notification irq number
+ * @keycodes - array to hold the key codes
+ * @dev - parent device pointer
+ * @keystate - present key press/release state
+ * @stuckstate - present state when key stuck irq
+ * @pm_chip - parent MFD core device
+ * @ctrl_reg - control register value
+ */
+struct pmic8058_kp {
+	const struct pmic8058_keypad_data *pdata;
+	struct input_dev *input;
+	int key_sense_irq;
+	int key_stuck_irq;
+
+	unsigned short *keycodes;
+
+	struct device *dev;
+	u16 keystate[PM8058_MAX_ROWS];
+	u16 stuckstate[PM8058_MAX_ROWS];
+
+	struct pm8058_chip	*pm_chip;
+
+	u8			ctrl_reg;
+};
+
+static int pmic8058_kp_write_u8(struct pmic8058_kp *kp,
+				 u8 data, u16 reg)
+{
+	int rc;
+
+	rc = pm8058_write(kp->pm_chip, reg, &data, 1);
+	if (rc < 0)
+		dev_warn(kp->dev, "Error writing pmic8058: %X - ret %X\n",
+				reg, rc);
+	return rc;
+}
+
+static int pmic8058_kp_read(struct pmic8058_kp *kp,
+				 u8 *data, u16 reg, unsigned num_bytes)
+{
+	int rc;
+
+	rc = pm8058_read(kp->pm_chip, reg, data, num_bytes);
+	if (rc < 0)
+		dev_warn(kp->dev, "Error reading pmic8058: %X - ret %X\n",
+				reg, rc);
+
+	return rc;
+}
+
+static int pmic8058_kp_read_u8(struct pmic8058_kp *kp,
+				 u8 *data, u16 reg)
+{
+	int rc;
+
+	rc = pmic8058_kp_read(kp, data, reg, 1);
+	if (rc < 0)
+		dev_warn(kp->dev, "Error reading pmic8058: %X - ret %X\n",
+				reg, rc);
+	return rc;
+}
+
+static u8 pmic8058_col_state(struct pmic8058_kp *kp, u8 col)
+{
+	/* all keys pressed on that particular row? */
+	if (col == 0x00)
+		return 1 << kp->pdata->num_cols;
+	else
+		return col & ((1 << kp->pdata->num_cols) - 1);
+}
+
+/*
+ * Synchronous read protocol for RevB0 onwards:
+ *
+ * 1. Write '1' to ReadState bit in KEYP_SCAN register
+ * 2. Wait 2*32KHz clocks, so that HW can successfully enter read mode
+ *    synchronously
+ * 3. Read rows in old array first if events are more than one
+ * 4. Read rows in recent array
+ * 5. Wait 4*32KHz clocks
+ * 6. Write '0' to ReadState bit of KEYP_SCAN register so that hw can
+ *    synchronously exit read mode.
+ */
+static int pmic8058_chk_sync_read(struct pmic8058_kp *kp)
+{
+	int rc;
+	u8 scan_val;
+
+	rc = pmic8058_kp_read_u8(kp, &scan_val, KEYP_SCAN);
+	if (rc < 0)
+		return rc;
+
+	scan_val |= 0x1;
+
+	rc = pmic8058_kp_write_u8(kp, scan_val, KEYP_SCAN);
+	if (rc < 0)
+		return rc;
+
+	/* 2 * 32KHz clocks */
+	udelay((2 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1);
+
+	return rc;
+}
+
+static int pmic8058_kp_read_data(struct pmic8058_kp *kp, u16 *state,
+					u16 data_reg, int read_rows)
+{
+	int rc, row;
+	u8 new_data[PM8058_MAX_ROWS];
+
+	rc = pmic8058_kp_read(kp, new_data, data_reg, read_rows);
+
+	if (!rc) {
+		for (row = 0; row < kp->pdata->num_rows; row++) {
+			dev_dbg(kp->dev, "new_data[%d] = %d\n", row,
+						new_data[row]);
+			state[row] = pmic8058_col_state(kp, new_data[row]);
+		}
+	}
+
+	return rc;
+}
+
+static int pmic8058_kp_read_matrix(struct pmic8058_kp *kp, u16 *new_state,
+					 u16 *old_state)
+{
+	int rc, read_rows;
+	u8 scan_val;
+
+	read_rows = kp->pdata->num_rows;
+
+	pmic8058_chk_sync_read(kp);
+
+	if (old_state) {
+		rc = pmic8058_kp_read_data(kp, old_state, KEYP_OLD_DATA,
+						read_rows);
+		if (rc < 0)
+			return rc;
+	}
+
+	rc = pmic8058_kp_read_data(kp, new_state, KEYP_RECENT_DATA,
+					 read_rows);
+	if (rc < 0)
+		return rc;
+
+	/* 4 * 32KHz clocks */
+	udelay((4 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1);
+
+	rc = pmic8058_kp_read_u8(kp, &scan_val, KEYP_SCAN);
+	if (rc < 0)
+		return rc;
+	scan_val &= 0xFE;
+	rc = pmic8058_kp_write_u8(kp, scan_val, KEYP_SCAN);
+	if (rc < 0)
+		return rc;
+
+	return rc;
+}
+
+static void __pmic8058_kp_scan_matrix(struct pmic8058_kp *kp, u16 *new_state,
+					 u16 *old_state)
+{
+	int row, col, code;
+
+	for (row = 0; row < kp->pdata->num_rows; row++) {
+		int bits_changed = new_state[row] ^ old_state[row];
+
+		if (!bits_changed)
+			continue;
+
+		for (col = 0; col < kp->pdata->num_cols; col++) {
+			if (!(bits_changed & (1 << col)))
+				continue;
+
+			dev_dbg(kp->dev, "key [%d:%d] %s\n", row, col,
+					!(new_state[row] & (1 << col)) ?
+					"pressed" : "released");
+
+			code = MATRIX_SCAN_CODE(row, col, PM8058_ROW_SHIFT);
+			input_event(kp->input, EV_MSC, MSC_SCAN, code);
+			input_report_key(kp->input,
+					kp->keycodes[code],
+					!(new_state[row] & (1 << col)));
+
+			input_sync(kp->input);
+		}
+	}
+}
+
+static int pmic8058_detect_ghost_keys(struct pmic8058_kp *kp, u16 *new_state)
+{
+	int row, found_first = -1;
+	u16 check, row_state;
+
+	check = 0;
+	for (row = 0; row < kp->pdata->num_rows; row++) {
+		row_state = (~new_state[row]) &
+				 ((1 << kp->pdata->num_cols) - 1);
+
+		if (hweight16(row_state) > 1) {
+			if (found_first == -1)
+				found_first = row;
+			if (check & row_state) {
+				dev_dbg(kp->dev, "detected ghost key on row[%d]"
+					 " and row[%d]\n", found_first, row);
+				return 1;
+			}
+		}
+		check |= row_state;
+	}
+	return 0;
+}
+
+static int pmic8058_kp_scan_matrix(struct pmic8058_kp *kp, unsigned int events)
+{
+	u16 new_state[PM8058_MAX_ROWS];
+	u16 old_state[PM8058_MAX_ROWS];
+	int rc;
+
+	switch (events) {
+	case 0x1:
+		rc = pmic8058_kp_read_matrix(kp, new_state, NULL);
+		if (rc < 0)
+			return rc;
+
+		/* detecting ghost key is not an error */
+		if (pmic8058_detect_ghost_keys(kp, new_state))
+			return 0;
+		__pmic8058_kp_scan_matrix(kp, new_state, kp->keystate);
+		memcpy(kp->keystate, new_state, sizeof(new_state));
+	break;
+	case 0x3: /* two events - eventcounter is gray-coded */
+		rc = pmic8058_kp_read_matrix(kp, new_state, old_state);
+		if (rc < 0)
+			return rc;
+
+		__pmic8058_kp_scan_matrix(kp, old_state, kp->keystate);
+		__pmic8058_kp_scan_matrix(kp, new_state, old_state);
+		memcpy(kp->keystate, new_state, sizeof(new_state));
+	break;
+	case 0x2:
+		dev_dbg(kp->dev, "Some key events were lost\n");
+		rc = pmic8058_kp_read_matrix(kp, new_state, old_state);
+		if (rc < 0)
+			return rc;
+		__pmic8058_kp_scan_matrix(kp, old_state, kp->keystate);
+		__pmic8058_kp_scan_matrix(kp, new_state, old_state);
+		memcpy(kp->keystate, new_state, sizeof(new_state));
+	break;
+	default:
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+/*
+ * NOTE: We are reading recent and old data registers blindly
+ * whenever key-stuck interrupt happens, because events counter doesn't
+ * get updated when this interrupt happens due to key stuck doesn't get
+ * considered as key state change.
+ *
+ * We are not using old data register contents after they are being read
+ * because it might report the key which was pressed before the key being stuck
+ * as stuck key because it's pressed status is stored in the old data
+ * register.
+ */
+static irqreturn_t pmic8058_kp_stuck_irq(int irq, void *data)
+{
+	u16 new_state[PM8058_MAX_ROWS];
+	u16 old_state[PM8058_MAX_ROWS];
+	int rc;
+	struct pmic8058_kp *kp = data;
+
+	rc = pmic8058_kp_read_matrix(kp, new_state, old_state);
+	if (rc < 0) {
+		dev_err(kp->dev, "failed to read keypad matrix\n");
+		return IRQ_HANDLED;
+	}
+
+	__pmic8058_kp_scan_matrix(kp, new_state, kp->stuckstate);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pmic8058_kp_irq(int irq, void *data)
+{
+	struct pmic8058_kp *kp = data;
+	u8 ctrl_val, events;
+	int rc;
+
+	rc = pmic8058_kp_read(kp, &ctrl_val, KEYP_CTRL, 1);
+	if (rc < 0) {
+		dev_err(kp->dev, "failed to read keyp_ctrl register\n");
+		return IRQ_HANDLED;
+	}
+
+	events = ctrl_val & KEYP_CTRL_EVNTS_MASK;
+
+	rc = pmic8058_kp_scan_matrix(kp, events);
+	if (rc < 0)
+		dev_err(kp->dev, "failed to scan matrix\n");
+
+	return IRQ_HANDLED;
+}
+
+static int pmic8058_kpd_init(struct pmic8058_kp *kp)
+{
+	int bits, rc, cycles;
+	u8 scan_val = 0, ctrl_val = 0;
+	static u8 row_bits[] = {
+		0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7,
+	};
+
+	/* Find column bits */
+	if (kp->pdata->num_cols < KEYP_CTRL_SCAN_COLS_MIN)
+		bits = 0;
+	else
+		bits = kp->pdata->num_cols - KEYP_CTRL_SCAN_COLS_MIN;
+	ctrl_val = (bits & KEYP_CTRL_SCAN_COLS_BITS) <<
+		KEYP_CTRL_SCAN_COLS_SHIFT;
+
+	/* Find row bits */
+	if (kp->pdata->num_rows < KEYP_CTRL_SCAN_ROWS_MIN)
+		bits = 0;
+	else
+		bits = row_bits[kp->pdata->num_rows - KEYP_CTRL_SCAN_ROWS_MIN];
+
+	ctrl_val |= (bits << KEYP_CTRL_SCAN_ROWS_SHIFT);
+
+	rc = pmic8058_kp_write_u8(kp, ctrl_val, KEYP_CTRL);
+	if (rc < 0)
+		return rc;
+
+	bits = (kp->pdata->debounce_ms / 5) - 1;
+
+	scan_val |= (bits << KEYP_SCAN_DBOUNCE_SHIFT);
+
+	bits = fls(kp->pdata->scan_delay_ms) - 1;
+	scan_val |= (bits << KEYP_SCAN_PAUSE_SHIFT);
+
+	/* Row hold time is a multiple of 32KHz cycles. */
+	cycles = (kp->pdata->row_hold_ns * KEYP_CLOCK_FREQ) / NSEC_PER_SEC;
+
+	scan_val |= (cycles << KEYP_SCAN_ROW_HOLD_SHIFT);
+
+	rc = pmic8058_kp_write_u8(kp, scan_val, KEYP_SCAN);
+
+	return rc;
+}
+
+static int pmic8058_kp_enable(struct pmic8058_kp *kp)
+{
+	int rc;
+
+	kp->ctrl_reg |= KEYP_CTRL_KEYP_EN;
+
+	rc = pmic8058_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL);
+	if (rc < 0)
+		return rc;
+
+	enable_irq(kp->key_sense_irq);
+	enable_irq(kp->key_stuck_irq);
+
+	return rc;
+}
+
+static int pmic8058_kp_disable(struct pmic8058_kp *kp)
+{
+	int rc;
+
+	kp->ctrl_reg &= ~KEYP_CTRL_KEYP_EN;
+
+	rc = pmic8058_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL);
+	if (rc < 0)
+		return rc;
+
+	disable_irq(kp->key_sense_irq);
+	disable_irq(kp->key_stuck_irq);
+
+	return rc;
+}
+
+static int pmic8058_kp_open(struct input_dev *dev)
+{
+	struct pmic8058_kp *kp = input_get_drvdata(dev);
+
+	return pmic8058_kp_enable(kp);
+}
+
+static void pmic8058_kp_close(struct input_dev *dev)
+{
+	struct pmic8058_kp *kp = input_get_drvdata(dev);
+
+	pmic8058_kp_disable(kp);
+}
+
+/*
+ * keypad controller should be initialized in the following sequence
+ * only, otherwise it might get into FSM stuck state.
+ *
+ * - Initialize keypad control parameters, like no. of rows, columns,
+ *   timing values etc.,
+ * - configure rows and column gpios pull up/down.
+ * - set irq edge type.
+ * - enable the keypad controller.
+ */
+static int __devinit pmic8058_kp_probe(struct platform_device *pdev)
+{
+	struct pmic8058_keypad_data *pdata = pdev->dev.platform_data;
+	const struct matrix_keymap_data *keymap_data;
+	struct pmic8058_kp *kp;
+	int rc;
+	unsigned short *keycodes;
+	u8 ctrl_val;
+	struct pm8058_chip	*pm_chip;
+
+	pm_chip = platform_get_drvdata(pdev);
+	if (pm_chip == NULL) {
+		dev_err(&pdev->dev, "no parent data passed in\n");
+		return -EFAULT;
+	}
+
+	/* Check PMIC8058 version. A0 version is not supported */
+	if (pm8058_rev(pm_chip) == PM_8058_REV_1p0) {
+		dev_err(&pdev->dev, "PMIC8058 1.0 version is not supported\n");
+		return -ENODEV;
+	}
+
+	if (!pdata || !pdata->num_cols || !pdata->num_rows ||
+		pdata->num_cols > PM8058_MAX_COLS ||
+		pdata->num_rows > PM8058_MAX_ROWS ||
+		pdata->num_cols < PM8058_MIN_COLS ||
+		pdata->num_rows < PM8058_MIN_ROWS) {
+		dev_err(&pdev->dev, "invalid platform data\n");
+		return -EINVAL;
+	}
+
+	if (pdata->rows_gpio_start < 0 || pdata->cols_gpio_start < 0) {
+		dev_err(&pdev->dev, "invalid gpio_start platform data\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->scan_delay_ms || pdata->scan_delay_ms > MAX_SCAN_DELAY
+		|| pdata->scan_delay_ms < MIN_SCAN_DELAY ||
+		!is_power_of_2(pdata->scan_delay_ms)) {
+		dev_err(&pdev->dev, "invalid keypad scan time supplied\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->row_hold_ns || pdata->row_hold_ns > MAX_ROW_HOLD_DELAY
+		|| pdata->row_hold_ns < MIN_ROW_HOLD_DELAY ||
+		((pdata->row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) {
+		dev_err(&pdev->dev, "invalid keypad row hold time supplied\n");
+		return -EINVAL;
+	}
+
+	if (!pdata->debounce_ms
+		|| ((pdata->debounce_ms % 5) != 0)
+		|| pdata->debounce_ms > MAX_DEBOUNCE_TIME
+		|| pdata->debounce_ms < MIN_DEBOUNCE_TIME) {
+		dev_err(&pdev->dev, "invalid debounce time supplied\n");
+		return -EINVAL;
+	}
+
+	keymap_data = pdata->keymap_data;
+	if (!keymap_data) {
+		dev_err(&pdev->dev, "no keymap data supplied\n");
+		return -EINVAL;
+	}
+
+	kp = kzalloc(sizeof(*kp), GFP_KERNEL);
+	if (!kp)
+		return -ENOMEM;
+
+	keycodes = kzalloc(PM8058_MATRIX_MAX_SIZE * sizeof(*keycodes),
+				 GFP_KERNEL);
+	if (!keycodes) {
+		rc = -ENOMEM;
+		goto err_alloc_mem;
+	}
+
+	platform_set_drvdata(pdev, kp);
+
+	kp->pdata	= pdata;
+	kp->dev		= &pdev->dev;
+	kp->keycodes	= keycodes;
+	kp->pm_chip	= pm_chip;
+
+	kp->input = input_allocate_device();
+	if (!kp->input) {
+		dev_err(&pdev->dev, "unable to allocate input device\n");
+		rc = -ENOMEM;
+		goto err_alloc_device;
+	}
+
+	kp->key_sense_irq = platform_get_irq(pdev, 0);
+	if (kp->key_sense_irq < 0) {
+		dev_err(&pdev->dev, "unable to get keypad sense irq\n");
+		rc = -ENXIO;
+		goto err_get_irq;
+	}
+
+	kp->key_stuck_irq = platform_get_irq(pdev, 1);
+	if (kp->key_stuck_irq < 0) {
+		dev_err(&pdev->dev, "unable to get keypad stuck irq\n");
+		rc = -ENXIO;
+		goto err_get_irq;
+	}
+
+	if (pdata->input_name)
+		kp->input->name = pdata->input_name;
+	else
+		kp->input->name = "PMIC8058 keypad";
+
+	if (pdata->input_phys_device)
+		kp->input->phys = pdata->input_phys_device;
+	else
+		kp->input->phys = "pmic8058_keypad/input0";
+
+	kp->input->dev.parent	= &pdev->dev;
+
+	kp->input->id.bustype	= BUS_I2C;
+	kp->input->id.version	= 0x0001;
+	kp->input->id.product	= 0x0001;
+	kp->input->id.vendor	= 0x0001;
+
+	kp->input->evbit[0]	= BIT_MASK(EV_KEY);
+
+	if (pdata->rep)
+		__set_bit(EV_REP, kp->input->evbit);
+
+	kp->input->keycode	= keycodes;
+	kp->input->keycodemax	= PM8058_MATRIX_MAX_SIZE;
+	kp->input->keycodesize	= sizeof(*keycodes);
+	kp->input->open		= pmic8058_kp_open;
+	kp->input->close	= pmic8058_kp_close;
+
+	matrix_keypad_build_keymap(keymap_data, PM8058_ROW_SHIFT,
+					kp->input->keycode, kp->input->keybit);
+
+	input_set_capability(kp->input, EV_MSC, MSC_SCAN);
+	input_set_drvdata(kp->input, kp);
+
+	/* initialize keypad state */
+	memset(kp->keystate, 0xff, sizeof(kp->keystate));
+	memset(kp->stuckstate, 0xff, sizeof(kp->stuckstate));
+
+	rc = pmic8058_kpd_init(kp);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "unable to initialize keypad controller\n");
+		goto err_kpd_init;
+	}
+
+	rc = request_any_context_irq(kp->key_sense_irq, pmic8058_kp_irq,
+				 IRQF_TRIGGER_RISING, "pmic-keypad", kp);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "failed to request keypad sense irq\n");
+		goto err_req_sense_irq;
+	}
+	/* kp->open will enable the irq */
+	disable_irq(kp->key_sense_irq);
+
+	rc = request_any_context_irq(kp->key_stuck_irq, pmic8058_kp_stuck_irq,
+				 IRQF_TRIGGER_RISING, "pmic-keypad-stuck", kp);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "failed to request keypad stuck irq\n");
+		goto err_req_stuck_irq;
+	}
+	/* kp->open will enable the irq */
+	disable_irq(kp->key_stuck_irq);
+
+	rc = pmic8058_kp_read_u8(kp, &ctrl_val, KEYP_CTRL);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "failed to read KEYP_CTRL register\n");
+		goto err_pmic_reg_read;
+	}
+
+	kp->ctrl_reg = ctrl_val;
+
+	rc = input_register_device(kp->input);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "unable to register keypad input device\n");
+		goto err_reg_input_dev;
+	}
+
+	device_init_wakeup(&pdev->dev, pdata->wakeup);
+
+	return 0;
+
+err_reg_input_dev:
+err_pmic_reg_read:
+	free_irq(kp->key_stuck_irq, NULL);
+err_req_stuck_irq:
+	free_irq(kp->key_sense_irq, NULL);
+err_req_sense_irq:
+err_kpd_init:
+err_get_irq:
+	input_free_device(kp->input);
+err_alloc_device:
+	kfree(keycodes);
+err_alloc_mem:
+	kfree(kp);
+	return rc;
+}
+
+static int __devexit pmic8058_kp_remove(struct platform_device *pdev)
+{
+	struct pmic8058_kp *kp = platform_get_drvdata(pdev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	free_irq(kp->key_stuck_irq, NULL);
+	free_irq(kp->key_sense_irq, NULL);
+	input_unregister_device(kp->input);
+	kfree(kp->input->keycode);
+	kfree(kp);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int pmic8058_kp_suspend(struct device *dev)
+{
+	struct pmic8058_kp *kp = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		enable_irq_wake(kp->key_sense_irq);
+
+	return 0;
+}
+
+static int pmic8058_kp_resume(struct device *dev)
+{
+	struct pmic8058_kp *kp = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(kp->key_sense_irq);
+
+	return 0;
+}
+
+static const struct dev_pm_ops pm8058_kp_pm_ops = {
+	.suspend	= pmic8058_kp_suspend,
+	.resume		= pmic8058_kp_resume,
+};
+#endif
+
+static struct platform_driver pmic8058_kp_driver = {
+	.probe		= pmic8058_kp_probe,
+	.remove		= __devexit_p(pmic8058_kp_remove),
+	.driver		= {
+		.name = "pm8058-keypad",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &pm8058_kp_pm_ops,
+#endif
+	},
+};
+
+static int __init pmic8058_kp_init(void)
+{
+	return platform_driver_register(&pmic8058_kp_driver);
+}
+module_init(pmic8058_kp_init);
+
+static void __exit pmic8058_kp_exit(void)
+{
+	platform_driver_unregister(&pmic8058_kp_driver);
+}
+module_exit(pmic8058_kp_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8058 keypad driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pmic8058_keypad");
+MODULE_AUTHOR("Trilok Soni <tsoni@codeaurora.org>");
diff --git a/include/linux/input/pmic8058-keypad.h b/include/linux/input/pmic8058-keypad.h
new file mode 100644
index 0000000..901f6cc
--- /dev/null
+++ b/include/linux/input/pmic8058-keypad.h
@@ -0,0 +1,58 @@ 
+/* Copyright (c) 2009-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.
+ *
+ * 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.
+ */
+
+#ifndef __PMIC8058_KEYPAD_H__
+#define __PMIC8058_KEYPAD_H__
+
+#include <linux/input/matrix_keypad.h>
+
+/**
+ * struct pmic8058_keypad_data - platform data for keypad
+ * @keymap_data - matrix keymap data
+ * @input_name - input device name
+ * @input_phys_device - input device name
+ * @num_cols - number of columns of keypad
+ * @num_rows - number of row of keypad
+ * @rows_gpio_start - starting PMIC gpio number for rows
+ * @cols_gpio_start - starting PMIC gpio number for cols
+ * @debounce_ms - debounce period in milliseconds
+ * @scan_delay_ms - scan delay in milliseconds
+ * @row_hold_ns - row hold period in nanoseconds
+ * @wakeup - configure keypad as wakeup
+ * @rep - enable or disable key repeat bit
+ */
+struct pmic8058_keypad_data {
+	const struct matrix_keymap_data *keymap_data;
+
+	const char *input_name;
+	const char *input_phys_device;
+
+	unsigned int num_cols;
+	unsigned int num_rows;
+
+	unsigned int rows_gpio_start;
+	unsigned int cols_gpio_start;
+
+	unsigned int debounce_ms;
+	unsigned int scan_delay_ms;
+	unsigned int row_hold_ns;
+
+	bool wakeup;
+	bool rep;
+};
+
+#endif /*__PMIC8058_KEYPAD_H__ */