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/arch/arm/mach-msm/pmic8058-gpio.c b/arch/arm/mach-msm/pmic8058-gpio.c
new file mode 100644
index 0000000..63a26de
--- /dev/null
+++ b/arch/arm/mach-msm/pmic8058-gpio.c
@@ -0,0 +1,705 @@
+/* Copyright (c) 2009-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.
+ */
+/*
+ * Qualcomm PMIC8058 GPIO driver
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pmic8058.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/seq_file.h>
+
+#ifndef CONFIG_GPIOLIB
+#include "gpio_chip.h"
+#endif
+
+/* GPIO registers */
+#define	SSBI_REG_ADDR_GPIO_BASE		0x150
+#define	SSBI_REG_ADDR_GPIO(n)		(SSBI_REG_ADDR_GPIO_BASE + n)
+
+/* GPIO */
+#define	PM8058_GPIO_BANK_MASK		0x70
+#define	PM8058_GPIO_BANK_SHIFT		4
+#define	PM8058_GPIO_WRITE		0x80
+
+/* Bank 0 */
+#define	PM8058_GPIO_VIN_MASK		0x0E
+#define	PM8058_GPIO_VIN_SHIFT		1
+#define	PM8058_GPIO_MODE_ENABLE		0x01
+
+/* Bank 1 */
+#define	PM8058_GPIO_MODE_MASK		0x0C
+#define	PM8058_GPIO_MODE_SHIFT		2
+#define	PM8058_GPIO_OUT_BUFFER		0x02
+#define	PM8058_GPIO_OUT_INVERT		0x01
+
+#define	PM8058_GPIO_MODE_OFF		3
+#define	PM8058_GPIO_MODE_OUTPUT		2
+#define	PM8058_GPIO_MODE_INPUT		0
+#define	PM8058_GPIO_MODE_BOTH		1
+
+/* Bank 2 */
+#define	PM8058_GPIO_PULL_MASK		0x0E
+#define	PM8058_GPIO_PULL_SHIFT		1
+
+/* Bank 3 */
+#define	PM8058_GPIO_OUT_STRENGTH_MASK	0x0C
+#define	PM8058_GPIO_OUT_STRENGTH_SHIFT	2
+#define	PM8058_GPIO_PIN_ENABLE		0x00
+#define	PM8058_GPIO_PIN_DISABLE		0x01
+
+/* Bank 4 */
+#define	PM8058_GPIO_FUNC_MASK		0x0E
+#define	PM8058_GPIO_FUNC_SHIFT		1
+
+/* Bank 5 */
+#define	PM8058_GPIO_NON_INT_POL_INV	0x08
+#define PM8058_GPIO_BANKS		6
+
+struct pm8058_gpio_chip {
+	struct gpio_chip	gpio_chip;
+	struct pm8058_chip	*pm_chip;
+	struct mutex		pm_lock;
+	u8			bank1[PM8058_GPIOS];
+};
+
+static int pm8058_gpio_get(struct pm8058_gpio_chip *chip, unsigned gpio)
+{
+	struct pm8058_gpio_platform_data	*pdata;
+	int	mode;
+
+	if (gpio >= PM8058_GPIOS || chip == NULL)
+		return -EINVAL;
+
+	pdata = chip->gpio_chip.dev->platform_data;
+
+	/* Get gpio value from config bank 1 if output gpio.
+	   Get gpio value from IRQ RT status register for all other gpio modes.
+	 */
+	mode = (chip->bank1[gpio] & PM8058_GPIO_MODE_MASK) >>
+		PM8058_GPIO_MODE_SHIFT;
+	if (mode == PM8058_GPIO_MODE_OUTPUT)
+		return chip->bank1[gpio] & PM8058_GPIO_OUT_INVERT;
+	else
+		return pm8058_irq_get_rt_status(chip->pm_chip,
+				pdata->irq_base + gpio);
+}
+
+static int pm8058_gpio_set(struct pm8058_gpio_chip *chip,
+		unsigned gpio, int value)
+{
+	int	rc;
+	u8	bank1;
+
+	if (gpio >= PM8058_GPIOS || chip == NULL)
+		return -EINVAL;
+
+	mutex_lock(&chip->pm_lock);
+	bank1 = chip->bank1[gpio] & ~PM8058_GPIO_OUT_INVERT;
+
+	if (value)
+		bank1 |= PM8058_GPIO_OUT_INVERT;
+
+	chip->bank1[gpio] = bank1;
+	rc = pm8058_write(chip->pm_chip, SSBI_REG_ADDR_GPIO(gpio), &bank1, 1);
+	mutex_unlock(&chip->pm_lock);
+
+	if (rc)
+		pr_err("%s: FAIL pm8058_write(): rc=%d. "
+		       "(gpio=%d, value=%d)\n",
+		       __func__, rc, gpio, value);
+
+	return rc;
+}
+
+static int pm8058_gpio_set_direction(struct pm8058_gpio_chip *chip,
+			      unsigned gpio, int direction)
+{
+	int	rc;
+	u8	bank1;
+	static int	dir_map[] = {
+		PM8058_GPIO_MODE_OFF,
+		PM8058_GPIO_MODE_OUTPUT,
+		PM8058_GPIO_MODE_INPUT,
+		PM8058_GPIO_MODE_BOTH,
+	};
+
+	if (!direction || chip == NULL)
+		return -EINVAL;
+
+	mutex_lock(&chip->pm_lock);
+	bank1 = chip->bank1[gpio] & ~PM8058_GPIO_MODE_MASK;
+
+	bank1 |= ((dir_map[direction] << PM8058_GPIO_MODE_SHIFT)
+		  & PM8058_GPIO_MODE_MASK);
+
+	chip->bank1[gpio] = bank1;
+	rc = pm8058_write(chip->pm_chip, SSBI_REG_ADDR_GPIO(gpio), &bank1, 1);
+	mutex_unlock(&chip->pm_lock);
+
+	if (rc)
+		pr_err("%s: Failed on pm8058_write(): rc=%d (GPIO config)\n",
+				__func__, rc);
+
+	return rc;
+}
+
+static int pm8058_gpio_init_bank1(struct pm8058_gpio_chip *chip)
+{
+	int i, rc;
+	u8 bank;
+
+	for (i = 0; i < PM8058_GPIOS; i++) {
+		bank = 1 << PM8058_GPIO_BANK_SHIFT;
+		rc = pm8058_write(chip->pm_chip,
+				SSBI_REG_ADDR_GPIO(i),
+				&bank, 1);
+		if (rc) {
+			pr_err("%s: error setting bank\n", __func__);
+			return rc;
+		}
+
+		rc = pm8058_read(chip->pm_chip,
+				SSBI_REG_ADDR_GPIO(i),
+				&chip->bank1[i], 1);
+		if (rc) {
+			pr_err("%s: error reading bank 1\n", __func__);
+			return rc;
+		}
+	}
+	return 0;
+}
+
+#ifndef CONFIG_GPIOLIB
+static int pm8058_gpio_configure(struct gpio_chip *chip,
+				 unsigned int gpio,
+				 unsigned long flags)
+{
+	int	rc = 0, direction;
+	struct pm8058_gpio_chip	*gpio_chip;
+
+	gpio -= chip->start;
+
+	if (flags & (GPIOF_INPUT | GPIOF_DRIVE_OUTPUT)) {
+		direction = 0;
+		if (flags & GPIOF_INPUT)
+			direction |= PM_GPIO_DIR_IN;
+		if (flags & GPIOF_DRIVE_OUTPUT)
+			direction |= PM_GPIO_DIR_OUT;
+
+		gpio_chip = dev_get_drvdata(chip->dev);
+
+		if (flags & (GPIOF_OUTPUT_LOW | GPIOF_OUTPUT_HIGH)) {
+			if (flags & GPIOF_OUTPUT_HIGH)
+				rc = pm8058_gpio_set(gpio_chip,
+						gpio, 1);
+			else
+				rc = pm8058_gpio_set(gpio_chip,
+						gpio, 0);
+
+			if (rc) {
+				pr_err("%s: FAIL pm8058_gpio_set(): rc=%d.\n",
+					__func__, rc);
+				goto bail_out;
+			}
+		}
+
+		rc = pm8058_gpio_set_direction(gpio_chip,
+				gpio, direction);
+		if (rc)
+			pr_err("%s: FAIL pm8058_gpio_config(): rc=%d.\n",
+				__func__, rc);
+	}
+
+bail_out:
+	return rc;
+}
+
+static int pm8058_gpio_get_irq_num(struct gpio_chip *chip,
+				   unsigned int gpio,
+				   unsigned int *irqp,
+				   unsigned long *irqnumflagsp)
+{
+	struct pm8058_gpio_platform_data *pdata;
+
+	pdata = chip->dev->platform_data;
+	gpio -= chip->start;
+	*irqp = pdata->irq_base + gpio;
+	if (irqnumflagsp)
+		*irqnumflagsp = 0;
+	return 0;
+}
+
+static int pm8058_gpio_read(struct gpio_chip *chip, unsigned n)
+{
+	struct pm8058_gpio_chip	*gpio_chip;
+
+	n -= chip->start;
+	gpio_chip = dev_get_drvdata(chip->dev);
+	return pm8058_gpio_get(gpio_chip, n);
+}
+
+static int pm8058_gpio_write(struct gpio_chip *chip, unsigned n, unsigned on)
+{
+	struct pm8058_gpio_chip	*gpio_chip;
+
+	n -= chip->start;
+	gpio_chip = dev_get_drvdata(chip->dev);
+	return pm8058_gpio_set(gpio_chip, n, on);
+}
+
+static struct pm8058_gpio_chip pm8058_gpio_chip = {
+	.gpio_chip = {
+		.configure = pm8058_gpio_configure,
+		.get_irq_num = pm8058_gpio_get_irq_num,
+		.read = pm8058_gpio_read,
+		.write = pm8058_gpio_write,
+	},
+};
+
+static int __devinit pm8058_gpio_probe(struct platform_device *pdev)
+{
+	int	rc = 0;
+	struct pm8058_gpio_platform_data *pdata = pdev->dev.platform_data;
+
+	mutex_init(&pm8058_gpio_chip.pm_lock);
+	pm8058_gpio_chip.gpio_chip.dev = &pdev->dev;
+	pm8058_gpio_chip.gpio_chip.start = pdata->gpio_base;
+	pm8058_gpio_chip.gpio_chip.end = pdata->gpio_base +
+		PM8058_GPIOS - 1;
+	pm8058_gpio_chip.pm_chip = platform_get_drvdata(pdev);
+	platform_set_drvdata(pdev, &pm8058_gpio_chip);
+
+	rc = register_gpio_chip(&pm8058_gpio_chip.gpio_chip);
+	if (!rc)
+		goto bail;
+
+	rc = pm8058_gpio_init_bank1(&pm8058_gpio_chip);
+	if (rc)
+		goto bail;
+
+	if (pdata->init)
+		rc = pdata->init();
+
+bail:
+	if (rc)
+		platform_set_drvdata(pdev, pm8058_gpio_chip.pm_chip);
+
+	pr_info("%s: register_gpio_chip(): rc=%d\n", __func__, rc);
+	return rc;
+}
+
+static int __devexit pm8058_gpio_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+#else
+
+static int pm8058_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct pm8058_gpio_platform_data *pdata;
+	pdata = chip->dev->platform_data;
+	return pdata->irq_base + offset;
+}
+
+static int pm8058_gpio_read(struct gpio_chip *chip, unsigned offset)
+{
+	struct pm8058_gpio_chip *gpio_chip;
+	gpio_chip = dev_get_drvdata(chip->dev);
+	return pm8058_gpio_get(gpio_chip, offset);
+}
+
+static void pm8058_gpio_write(struct gpio_chip *chip,
+		unsigned offset, int val)
+{
+	struct pm8058_gpio_chip *gpio_chip;
+	gpio_chip = dev_get_drvdata(chip->dev);
+	pm8058_gpio_set(gpio_chip, offset, val);
+}
+
+static int pm8058_gpio_direction_input(struct gpio_chip *chip,
+		unsigned offset)
+{
+	struct pm8058_gpio_chip *gpio_chip;
+	gpio_chip = dev_get_drvdata(chip->dev);
+	return pm8058_gpio_set_direction(gpio_chip, offset, PM_GPIO_DIR_IN);
+}
+
+static int pm8058_gpio_direction_output(struct gpio_chip *chip,
+		unsigned offset,
+		int val)
+{
+	struct pm8058_gpio_chip *gpio_chip;
+	int ret;
+
+	gpio_chip = dev_get_drvdata(chip->dev);
+	ret = pm8058_gpio_set_direction(gpio_chip, offset, PM_GPIO_DIR_OUT);
+	if (!ret)
+		ret = pm8058_gpio_set(gpio_chip, offset, val);
+
+	return ret;
+}
+
+static void pm8058_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+	static const char *cmode[] = { "in", "in/out", "out", "off" };
+	struct pm8058_gpio_chip *gpio_chip = dev_get_drvdata(chip->dev);
+	u8 mode, state, bank;
+	const char *label;
+	int i, j;
+
+	for (i = 0; i < PM8058_GPIOS; i++) {
+		label = gpiochip_is_requested(chip, i);
+		mode = (gpio_chip->bank1[i] & PM8058_GPIO_MODE_MASK) >>
+			PM8058_GPIO_MODE_SHIFT;
+		state = pm8058_gpio_get(gpio_chip, i);
+		seq_printf(s, "gpio-%-3d (%-12.12s) %-10.10s"
+				" %s",
+				chip->base + i,
+				label ? label : "--",
+				cmode[mode],
+				state ? "hi" : "lo");
+		for (j = 0; j < PM8058_GPIO_BANKS; j++) {
+			bank = j << PM8058_GPIO_BANK_SHIFT;
+			pm8058_write(gpio_chip->pm_chip,
+					SSBI_REG_ADDR_GPIO(i),
+					&bank, 1);
+			pm8058_read(gpio_chip->pm_chip,
+					SSBI_REG_ADDR_GPIO(i),
+					&bank, 1);
+			seq_printf(s, " 0x%02x", bank);
+		}
+		seq_printf(s, "\n");
+	}
+}
+
+static struct pm8058_gpio_chip pm8058_gpio_chip = {
+	.gpio_chip = {
+		.label			= "pm8058-gpio",
+		.direction_input	= pm8058_gpio_direction_input,
+		.direction_output	= pm8058_gpio_direction_output,
+		.to_irq			= pm8058_gpio_to_irq,
+		.get			= pm8058_gpio_read,
+		.set			= pm8058_gpio_write,
+		.dbg_show		= pm8058_gpio_dbg_show,
+		.ngpio			= PM8058_GPIOS,
+		.can_sleep		= 1,
+	},
+};
+
+static int __devinit pm8058_gpio_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct pm8058_gpio_platform_data *pdata = pdev->dev.platform_data;
+
+	mutex_init(&pm8058_gpio_chip.pm_lock);
+	pm8058_gpio_chip.gpio_chip.dev = &pdev->dev;
+	pm8058_gpio_chip.gpio_chip.base = pdata->gpio_base;
+	pm8058_gpio_chip.pm_chip = dev_get_drvdata(pdev->dev.parent);
+	platform_set_drvdata(pdev, &pm8058_gpio_chip);
+
+	ret = gpiochip_add(&pm8058_gpio_chip.gpio_chip);
+	if (ret)
+		goto unset_drvdata;
+
+	ret = pm8058_gpio_init_bank1(&pm8058_gpio_chip);
+	if (ret)
+		goto remove_chip;
+
+	if (pdata->init)
+		ret = pdata->init();
+	if (!ret)
+		goto ok;
+
+remove_chip:
+	if (gpiochip_remove(&pm8058_gpio_chip.gpio_chip))
+		pr_err("%s: failed to remove gpio chip\n", __func__);
+unset_drvdata:
+	platform_set_drvdata(pdev, pm8058_gpio_chip.pm_chip);
+ok:
+	pr_info("%s: gpiochip_add(): rc=%d\n", __func__, ret);
+
+	return ret;
+}
+
+static int __devexit pm8058_gpio_remove(struct platform_device *pdev)
+{
+	return gpiochip_remove(&pm8058_gpio_chip.gpio_chip);
+}
+
+#endif
+
+int pm8058_gpio_config(int gpio, struct pm8058_gpio *param)
+{
+	int	rc;
+	u8	bank[8];
+	static int	dir_map[] = {
+		PM8058_GPIO_MODE_OFF,
+		PM8058_GPIO_MODE_OUTPUT,
+		PM8058_GPIO_MODE_INPUT,
+		PM8058_GPIO_MODE_BOTH,
+	};
+
+	if (param == NULL)
+		return -EINVAL;
+
+	/* Select banks and configure the gpio */
+	bank[0] = PM8058_GPIO_WRITE |
+		((param->vin_sel << PM8058_GPIO_VIN_SHIFT) &
+			PM8058_GPIO_VIN_MASK) |
+		PM8058_GPIO_MODE_ENABLE;
+	bank[1] = PM8058_GPIO_WRITE |
+		((1 << PM8058_GPIO_BANK_SHIFT) &
+			PM8058_GPIO_BANK_MASK) |
+		((dir_map[param->direction] <<
+			PM8058_GPIO_MODE_SHIFT) &
+			PM8058_GPIO_MODE_MASK) |
+		((param->direction & PM_GPIO_DIR_OUT) ?
+			((param->output_buffer & 1) ?
+			 PM8058_GPIO_OUT_BUFFER : 0) : 0) |
+		((param->direction & PM_GPIO_DIR_OUT) ?
+			param->output_value & 0x01 : 0);
+	bank[2] = PM8058_GPIO_WRITE |
+		((2 << PM8058_GPIO_BANK_SHIFT) &
+			PM8058_GPIO_BANK_MASK) |
+		((param->pull << PM8058_GPIO_PULL_SHIFT) &
+			PM8058_GPIO_PULL_MASK);
+	bank[3] = PM8058_GPIO_WRITE |
+		((3 << PM8058_GPIO_BANK_SHIFT) &
+			PM8058_GPIO_BANK_MASK) |
+		((param->out_strength <<
+			PM8058_GPIO_OUT_STRENGTH_SHIFT) &
+			PM8058_GPIO_OUT_STRENGTH_MASK) |
+		(param->disable_pin ?
+			PM8058_GPIO_PIN_DISABLE : PM8058_GPIO_PIN_ENABLE);
+	bank[4] = PM8058_GPIO_WRITE |
+		((4 << PM8058_GPIO_BANK_SHIFT) &
+			PM8058_GPIO_BANK_MASK) |
+		((param->function << PM8058_GPIO_FUNC_SHIFT) &
+			PM8058_GPIO_FUNC_MASK);
+	bank[5] = PM8058_GPIO_WRITE |
+		((5 << PM8058_GPIO_BANK_SHIFT) & PM8058_GPIO_BANK_MASK) |
+		(param->inv_int_pol ? 0 : PM8058_GPIO_NON_INT_POL_INV);
+
+	mutex_lock(&pm8058_gpio_chip.pm_lock);
+	/* Remember bank1 for later use */
+	pm8058_gpio_chip.bank1[gpio] = bank[1];
+	rc = pm8058_write(pm8058_gpio_chip.pm_chip,
+			SSBI_REG_ADDR_GPIO(gpio), bank, 6);
+	mutex_unlock(&pm8058_gpio_chip.pm_lock);
+
+	if (rc)
+		pr_err("%s: Failed on pm8058_write(): rc=%d (GPIO config)\n",
+				__func__, rc);
+
+	return rc;
+}
+EXPORT_SYMBOL(pm8058_gpio_config);
+
+static struct platform_driver pm8058_gpio_driver = {
+	.probe		= pm8058_gpio_probe,
+	.remove		= __devexit_p(pm8058_gpio_remove),
+	.driver		= {
+		.name = "pm8058-gpio",
+		.owner = THIS_MODULE,
+	},
+};
+
+#if defined(CONFIG_DEBUG_FS)
+
+#define DEBUG_MAX_RW_BUF   128
+#define DEBUG_MAX_FNAME    8
+
+static struct dentry *debug_dent;
+
+static char debug_read_buf[DEBUG_MAX_RW_BUF];
+static char debug_write_buf[DEBUG_MAX_RW_BUF];
+
+static int debug_gpios[PM8058_GPIOS];
+
+static int debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static int debug_read_gpio_bank(int gpio, int bank, u8 *data)
+{
+	int rc;
+
+	mutex_lock(&pm8058_gpio_chip.pm_lock);
+
+	*data = bank << PM8058_GPIO_BANK_SHIFT;
+	rc = pm8058_write(pm8058_gpio_chip.pm_chip,
+			SSBI_REG_ADDR_GPIO(gpio), data, 1);
+	if (rc)
+		goto bail_out;
+
+	*data = bank << PM8058_GPIO_BANK_SHIFT;
+	rc = pm8058_read(pm8058_gpio_chip.pm_chip,
+			SSBI_REG_ADDR_GPIO(gpio), data, 1);
+
+bail_out:
+	mutex_unlock(&pm8058_gpio_chip.pm_lock);
+
+	return rc;
+}
+
+static ssize_t debug_read(struct file *file, char __user *buf,
+			  size_t count, loff_t *ppos)
+{
+	int gpio = *((int *) file->private_data);
+	int len = 0;
+	int rc = -EINVAL;
+	u8 bank[PM8058_GPIO_BANKS];
+	int val = -1;
+	int mode;
+	int i;
+
+	for (i = 0; i < PM8058_GPIO_BANKS; i++) {
+		rc = debug_read_gpio_bank(gpio, i, &bank[i]);
+		if (rc)
+			pr_err("pmic failed to read bank %d\n", i);
+	}
+
+	if (rc) {
+		len = snprintf(debug_read_buf, DEBUG_MAX_RW_BUF - 1, "-1\n");
+		goto bail_out;
+	}
+
+	val = pm8058_gpio_get(&pm8058_gpio_chip, gpio);
+
+	/* print the mode and the value */
+	mode = (bank[1] & PM8058_GPIO_MODE_MASK) >>  PM8058_GPIO_MODE_SHIFT;
+	if (mode == PM8058_GPIO_MODE_BOTH)
+		len = snprintf(debug_read_buf, DEBUG_MAX_RW_BUF - 1,
+			       "BOTH %d ", val);
+	else if (mode == PM8058_GPIO_MODE_INPUT)
+		len = snprintf(debug_read_buf, DEBUG_MAX_RW_BUF - 1,
+			       "IN   %d ", val);
+	else if (mode == PM8058_GPIO_MODE_OUTPUT)
+		len = snprintf(debug_read_buf, DEBUG_MAX_RW_BUF - 1,
+			       "OUT  %d ", val);
+	else
+		len = snprintf(debug_read_buf, DEBUG_MAX_RW_BUF - 1,
+			       "OFF  %d ", val);
+
+	/* print the control register values */
+	len += snprintf(debug_read_buf + len, DEBUG_MAX_RW_BUF - len - 1,
+			"[0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x]\n",
+			bank[0], bank[1], bank[2], bank[3], bank[4], bank[5]);
+
+bail_out:
+	rc = simple_read_from_buffer((void __user *) buf, len,
+				     ppos, (void *) debug_read_buf, len);
+
+	return rc;
+}
+
+static ssize_t debug_write(struct file *file, const char __user *buf,
+			   size_t count, loff_t *ppos)
+{
+	int gpio = *((int *) file->private_data);
+	unsigned long val;
+	int mode, rc;
+
+	mode = (pm8058_gpio_chip.bank1[gpio] & PM8058_GPIO_MODE_MASK) >>
+		PM8058_GPIO_MODE_SHIFT;
+	if (mode == PM8058_GPIO_MODE_OFF || mode == PM8058_GPIO_MODE_INPUT)
+		return count;
+
+	if (count > sizeof(debug_write_buf))
+		return -EFAULT;
+
+	if (copy_from_user(debug_write_buf, buf, count)) {
+		pr_err("failed to copy from user\n");
+		return -EFAULT;
+	}
+	debug_write_buf[count] = '\0';
+
+	rc = strict_strtoul(debug_write_buf, 10, &val);
+	if (rc)
+		return rc;
+
+	if (pm8058_gpio_set(&pm8058_gpio_chip, gpio, val)) {
+		pr_err("gpio write failed\n");
+		return -EINVAL;
+	}
+
+	return count;
+}
+
+static const struct file_operations debug_ops = {
+	.open =         debug_open,
+	.read =         debug_read,
+	.write =        debug_write,
+};
+
+static void debug_init(void)
+{
+	int i;
+	char name[DEBUG_MAX_FNAME];
+
+	debug_dent = debugfs_create_dir("pm_gpio", NULL);
+	if (IS_ERR(debug_dent)) {
+		pr_err("pmic8058 debugfs_create_dir fail, error %ld\n",
+		       PTR_ERR(debug_dent));
+		return;
+	}
+
+	for (i = 0; i < PM8058_GPIOS; i++) {
+		snprintf(name, DEBUG_MAX_FNAME-1, "%d", i+1);
+		debug_gpios[i] = i;
+		if (debugfs_create_file(name, 0644, debug_dent,
+					&debug_gpios[i], &debug_ops) == NULL) {
+			pr_err("pmic8058 debugfs_create_file %s failed\n",
+			       name);
+		}
+	}
+}
+
+static void debug_exit(void)
+{
+	debugfs_remove_recursive(debug_dent);
+}
+
+#else
+static void debug_init(void) { }
+static void debug_exit(void) { }
+#endif
+
+static int __init pm8058_gpio_init(void)
+{
+	int rc = platform_driver_register(&pm8058_gpio_driver);
+	if (!rc)
+		debug_init();
+	return rc;
+}
+
+static void __exit pm8058_gpio_exit(void)
+{
+	platform_driver_unregister(&pm8058_gpio_driver);
+	debug_exit();
+}
+
+subsys_initcall(pm8058_gpio_init);
+module_exit(pm8058_gpio_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC8058 GPIO driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:pm8058-gpio");