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");