mdmgpio: add support/driver for gpio controlled modem (tenderloin)
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 9c55cc2..e03550c 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -766,6 +766,16 @@
 	---help---
 	  Generic HSUART engine, which calls platform specific code to facilitate High speed UART driver".
 
+config MDMGPIO
+	tristate "modem gpio control"
+	depends on SYSFS
+	default n
+	help
+	  If you say yes here you get support for modem gpio control.
+
+	  This driver can also be built as a module, choose M here: the
+	  module will be called mdmgpio.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 5cb303a..110403a 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -88,3 +88,4 @@
 obj-$(CONFIG_USER_PINS)		+= user-pins.o
 obj-$(CONFIG_KERNEL_LOG)	+= klog.o
 obj-$(CONFIG_HSUART)		+= hsuart.o hsuart_tty.o
+obj-$(CONFIG_MDMGPIO)		+= mdmgpio.o
diff --git a/drivers/misc/mdmgpio.c b/drivers/misc/mdmgpio.c
new file mode 100644
index 0000000..f8670e5
--- /dev/null
+++ b/drivers/misc/mdmgpio.c
@@ -0,0 +1,230 @@
+/* Copyright (c) 2011, Hewlett-Packard Development Company,  All rights reserved.
+ *
+ * mdmgpio.c - Linux kernel modules for modem gppio control
+ *
+ * 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/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/ctype.h>
+#include <linux/module.h>
+#include <linux/mdmgpio.h>
+
+static ssize_t uim_cd_gpio_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int value = 0;
+	struct mdmgpio_platform_data *pdata;
+
+	pdata = (struct mdmgpio_platform_data *)dev->platform_data;
+
+	if (!pdata)
+		return -EINVAL;
+
+	if (pdata->get_gpio_value)
+		value = pdata->get_gpio_value(pdata->uim_cd_gpio);
+	else
+		return -EINVAL;
+
+	return sprintf(buf, "%u\n", value);
+}
+
+static ssize_t uim_cd_gpio_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	ssize_t ret = -EINVAL;
+	char *after;
+	struct mdmgpio_platform_data *pdata;
+	unsigned long value = simple_strtoul(buf, &after, 10);
+	size_t count = after - buf;
+
+	pdata = (struct mdmgpio_platform_data *)dev->platform_data;
+
+
+	if (!pdata)
+		return -EINVAL;
+	if (isspace(*after))
+		count++;
+	if (count == size) {
+		ret = count;
+		if (pdata->set_gpio_value)
+			pdata->set_gpio_value(pdata->uim_cd_gpio, !!value);
+		else
+			return -EINVAL;
+	}
+	return ret;
+}
+
+static ssize_t mdm_disable_gpio_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int value = 0;
+	struct mdmgpio_platform_data *pdata;
+
+	pdata = (struct mdmgpio_platform_data *)dev->platform_data;
+
+	if (!pdata)
+		return -EINVAL;
+	if (pdata->get_gpio_value)
+		value = pdata->get_gpio_value(pdata->mdm_disable_gpio);
+	else
+		return -EINVAL;
+
+	return sprintf(buf,"%u\n", value);
+}
+
+static ssize_t mdm_disable_gpio_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	ssize_t ret = -EINVAL;
+	char *after;
+	struct mdmgpio_platform_data *pdata;
+	unsigned long value = simple_strtoul(buf, &after, 10);
+	size_t count = after - buf;
+
+	pdata = (struct mdmgpio_platform_data *)dev->platform_data;
+
+	if (!pdata)
+		return -EINVAL;
+	if (isspace(*after))
+		count++;
+	if (count == size) {
+		ret = count;
+		if (pdata->set_gpio_value)
+			pdata->set_gpio_value(pdata->mdm_disable_gpio, !!value);
+		else
+			return -EINVAL;
+	}
+	return ret;
+}
+
+static ssize_t mdm_poweron_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int value = 0;
+	struct mdmgpio_platform_data *pdata;
+
+	pdata = (struct mdmgpio_platform_data *)dev->platform_data;
+
+	if (!pdata)
+		return -EINVAL;
+	if (pdata->mdm_poweron_status)
+		value = pdata->mdm_poweron_status();
+
+	return sprintf(buf,"%u\n", value);
+}
+
+static ssize_t mdm_poweron_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	ssize_t ret = -EINVAL;
+	char *after;
+	struct mdmgpio_platform_data *pdata;
+	unsigned long value = simple_strtoul(buf, &after, 10);
+	size_t count = after - buf;
+
+	pdata = (struct mdmgpio_platform_data *)dev->platform_data;
+
+	if (!pdata)
+		return -EINVAL;
+	if (isspace(*after))
+		count++;
+	if (count == size) {
+		ret = count;
+		if (pdata->mdm_poweron)
+			pdata->mdm_poweron(!!value);
+
+	}
+	return ret;
+}
+
+static struct device_attribute mdmgpio_attrs[] = {
+	__ATTR(uim_cd_gpio,	0644, uim_cd_gpio_show, uim_cd_gpio_store),
+	__ATTR(mdm_disable_gpio, 0644, mdm_disable_gpio_show, mdm_disable_gpio_store),
+	__ATTR(mdm_poweron, 0644, mdm_poweron_show, mdm_poweron_store),
+	__ATTR_NULL,
+};
+
+static int mdmgpio_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	int i;
+	struct mdmgpio_platform_data *pdata;
+
+	printk("%s\n", __func__);
+	pdata = (struct mdmgpio_platform_data *)pdev->dev.platform_data;
+	if (pdata == NULL) {
+		printk(KERN_ERR"%s: null platform data\n", __func__);
+		return -EINVAL;
+	}
+
+	for( i = 0; attr_name(mdmgpio_attrs[i]); i++) {
+		ret = device_create_file(&pdev->dev, &mdmgpio_attrs[i]);
+		if (ret < 0) {
+			printk(KERN_ERR "%s:failed to create sysfs file %s\n",
+			__func__, attr_name(mdmgpio_attrs[i]));
+			break;
+		}
+	}
+	if (ret < 0)
+		while(--i >= 0)
+			device_remove_file(&pdev->dev, &mdmgpio_attrs[i]);
+
+	return ret;
+}
+
+static int mdmgpio_remove(struct platform_device *pdev)
+{
+	int ret = 0;
+	int i;
+	struct mdmgpio_platform_data *pdata;
+
+	printk("%s\n", __func__);
+
+	pdata = (struct mdmgpio_platform_data *)pdev->dev.platform_data;
+
+	if (!pdata)
+		return -EINVAL;
+	for(i = 0; attr_name(mdmgpio_attrs[i]); i++) {
+		device_remove_file(&pdev->dev, &mdmgpio_attrs[i]);
+	}
+	return ret;
+}
+
+static struct platform_driver mdmgpio_driver = {
+	.probe = mdmgpio_probe,
+	.remove = mdmgpio_remove,
+	.driver = {
+		.name = "mdmgpio",
+		.owner = THIS_MODULE,
+	},
+};
+
+int __init mdmgpio_init(void)
+{
+	printk("%s\n", __func__);
+	platform_driver_register(&mdmgpio_driver);
+	return 0;
+}
+
+static void __exit mdmgpio_exit(void)
+{
+	printk("%s\n", __func__);
+	platform_driver_unregister(&mdmgpio_driver);
+}
+
+module_init(mdmgpio_init);
+module_exit(mdmgpio_exit);
+
+MODULE_AUTHOR("Hao Song<hao.song@hp.com>");
+MODULE_LICENSE("GPL");
+
diff --git a/include/linux/mdmgpio.h b/include/linux/mdmgpio.h
new file mode 100644
index 0000000..e143083
--- /dev/null
+++ b/include/linux/mdmgpio.h
@@ -0,0 +1,27 @@
+/* Copyright (c) 2010, Hewlett-Packard Development Company, L.P. All rights reserved.
+ *
+ * mdmgpio.h - Linux kernel modules for modem gpio control
+ *
+ * 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.
+ */
+
+#ifndef __LINUX_MDMGPIO_H
+#define __LINUX_MDMGPIO_H
+
+
+struct mdmgpio_platform_data {
+	int uim_cd_gpio;
+	int mdm_disable_gpio;
+	int (*get_gpio_value)(int gpionum);
+	int (*set_gpio_value)(int gpionum, int value);
+	int (*mdm_poweron)(int on);
+	int (*mdm_poweron_status)(void);
+};
+#endif
\ No newline at end of file