gpio: npi-down-status: NPI download check driver bringup

Issue: ALM:11026504
Issue: FP4-INT#2
Change-Id: I76de6b76a99d19ff0445ba64420dea1255d8074f
(cherry picked from commit c164763edf419a3a90a3db27fc5aac38f67d5120)
diff --git a/arch/arm64/configs/vendor/fp4-perf_defconfig b/arch/arm64/configs/vendor/fp4-perf_defconfig
index 31b987e..8ebca9e 100644
--- a/arch/arm64/configs/vendor/fp4-perf_defconfig
+++ b/arch/arm64/configs/vendor/fp4-perf_defconfig
@@ -2678,6 +2678,7 @@
 #
 # USB GPIO expanders
 #
+CONFIG_GPIO_NPI_DOWN_STATUS=y
 # CONFIG_W1 is not set
 # CONFIG_POWER_AVS is not set
 CONFIG_POWER_RESET=y
diff --git a/arch/arm64/configs/vendor/fp4_defconfig b/arch/arm64/configs/vendor/fp4_defconfig
index fa4c431..452499a 100644
--- a/arch/arm64/configs/vendor/fp4_defconfig
+++ b/arch/arm64/configs/vendor/fp4_defconfig
@@ -2704,6 +2704,7 @@
 #
 # USB GPIO expanders
 #
+CONFIG_GPIO_NPI_DOWN_STATUS=y
 # CONFIG_W1 is not set
 # CONFIG_POWER_AVS is not set
 CONFIG_POWER_RESET=y
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 89a84e7..9ed6df5 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -1393,4 +1393,11 @@
 
 endmenu
 
+config GPIO_NPI_DOWN_STATUS
+         bool "NPI Down status"
+	 def_bool y
+         help
+           Say 'y' here to include debug support for the Qualcomm
+           QPNP gpio status.
+
 endif
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 20bc3c1..188b2d3 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -160,3 +160,4 @@
 obj-$(CONFIG_GPIO_ZX)		+= gpio-zx.o
 obj-$(CONFIG_GPIO_LOONGSON1)	+= gpio-loongson1.o
 obj-$(CONFIG_FP4_BOARD_ID)	+= fp4-board-id.o
+obj-$(CONFIG_GPIO_NPI_DOWN_STATUS)      += gpio-npi-down-status.o
diff --git a/drivers/gpio/gpio-npi-down-status.c b/drivers/gpio/gpio-npi-down-status.c
new file mode 100644
index 0000000..2b06e3d
--- /dev/null
+++ b/drivers/gpio/gpio-npi-down-status.c
@@ -0,0 +1,213 @@
+/* Copyright (C) 2018 Tcl Corporation Limited */
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+//#include <linux/device.h>
+
+#define REQUEST_NPI_DOWN_GPIO 1
+//npi_down_gpio		902+69 //VDF
+
+/*  add npi down gpio in dtsi file and use function "of_get_named_gpio" get the gpio number, can't use gpio index directly ,
+because in kener vision 3.10 gpio_number=base(8916 8936 8939 is 902)+gpio_index , the base value we can see in "gpio_lib.conf" file .  TCTNB  ZXZ add in 2014/12/13 */
+
+static int npi_down_gpio;
+
+static char npi_down_status[4]={0};
+
+struct pinctrl		*pinctrl;
+struct pinctrl_state	*pin_default;
+struct pinctrl_state	*pin_suspend;
+
+int npi_down_status_detect(void)
+{
+	int status;
+
+/* remove gpio_request and gpio_free,GPIO69 is used by usb_otg and npi down status,
+so gpio_request will fail here, we not request and get gpio status directly*/
+	#if REQUEST_NPI_DOWN_GPIO
+	int rc = 0;
+	rc = gpio_request(npi_down_gpio, "npi down status");
+
+		if (rc) {
+			pr_err("npi down status request gpio=%d failed, rc=%d\n", npi_down_gpio,rc);
+			goto err_gpio;
+		}
+
+		rc = gpio_direction_input(npi_down_gpio);
+		if (rc) {
+			pr_err("set_direction for gpio=%d failed, rc=%d\n",npi_down_gpio,rc);
+			goto err_gpio;
+		}
+	#endif
+
+		status=gpio_get_value(npi_down_gpio);
+
+	#if REQUEST_NPI_DOWN_GPIO
+		gpio_free(npi_down_gpio);
+	#endif
+
+		if(status)
+		strcpy(npi_down_status,"1");
+		else
+		strcpy(npi_down_status,"0");
+
+		return 0;
+
+
+	#if REQUEST_NPI_DOWN_GPIO
+	err_gpio:
+			gpio_free(npi_down_gpio);
+		return  -ENODEV;
+	#endif
+
+}
+
+
+static ssize_t npi_down_status_read(struct class *class,
+				struct class_attribute *attr, char *buf)
+{
+	int err;
+
+	err = pinctrl_select_state(pinctrl, pin_default);
+	if (err) {
+		pr_err("npi_down_status: Can't select pinctrl state\n");
+		return err;
+	}
+
+	err=npi_down_status_detect();
+
+	err = pinctrl_select_state(pinctrl, pin_suspend);
+	if (err) {
+		pr_err("npi_down_status: Can't select pinctrl state\n");
+		return err;
+	}
+
+	if(err == -ENODEV) {
+		pr_err("npi down status read error!\n");
+	}
+	return sprintf(buf, "%s\n", npi_down_status);
+}
+
+
+/* npi_down_status value attribute (/<sysfs>/class/npi_down_status/status) */
+static struct class_attribute npi_down_status_value =
+	__ATTR(status, 0444, npi_down_status_read, NULL);
+
+
+static int npi_down_status_creat_file(void)
+{
+	int ret;
+	struct class *npi_down_class;
+
+	/* npi_down_status create (/<sysfs>/class/npi_down_status) */
+	npi_down_class = class_create(THIS_MODULE, "npi_down_status");
+	if (IS_ERR(npi_down_class)) {
+		ret = PTR_ERR(npi_down_class);
+		printk(KERN_ERR "npi_down_class: couldn't create npi_down_status\n");
+	}
+	ret = class_create_file(npi_down_class, &npi_down_status_value);
+	if (ret) {
+		printk(KERN_ERR "npi_down_status: couldn't create npi_down_status_value\n");
+	}
+
+	return 0;
+
+}
+
+
+static int npi_down_status_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+
+	pinctrl = devm_pinctrl_get(&pdev->dev);
+	if (IS_ERR_OR_NULL(pinctrl)) {
+		pr_err("npi_down_status: Failed to get pinctrl\n");
+		return PTR_ERR(pinctrl);
+	}
+	pin_default = pinctrl_lookup_state(pinctrl, "default");
+	if (IS_ERR_OR_NULL(pin_default)) {
+		pr_err("npi_down_status: Failed to look up default state\n");
+		return PTR_ERR(pinctrl);
+	}
+	pin_suspend = pinctrl_lookup_state(pinctrl, "suspend");
+	if (IS_ERR_OR_NULL(pin_suspend)) {
+		pr_err("npi_down_status: Failed to look up suspend state\n");
+		return PTR_ERR(pinctrl);
+	}
+	rc = pinctrl_select_state(pinctrl, pin_default);
+	if (rc) {
+		pr_err("npi_down_status: Can't select pinctrl state\n");
+		return rc;
+	}
+
+
+	npi_down_gpio=of_get_named_gpio(pdev->dev.of_node,
+		"qcom,npi-down-gpio", 0);
+
+	rc=npi_down_status_creat_file();
+
+	return rc;
+}
+
+
+static int npi_down_status_remove(struct platform_device *pdev)
+{
+	#if REQUEST_NPI_DOWN_GPIO
+	gpio_free(npi_down_gpio);
+	#endif
+
+	return 0;
+}
+
+
+static const struct of_device_id npi_down_dt_match[] = {
+	{.compatible = "qcom,npi-down-status"},
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, npi_down_dt_match);
+
+static struct platform_driver npi_down_status_driver = {
+	.probe = npi_down_status_probe,
+	.remove = npi_down_status_remove,
+	.shutdown = NULL,
+	.driver = {
+		.name = "npi_down_status",
+		.of_match_table = npi_down_dt_match,
+	},
+};
+
+static int npi_down_register_driver(void)
+{
+	return platform_driver_register(&npi_down_status_driver);
+}
+
+
+
+static int __init npi_down_status_init(void)
+{
+
+	int ret;
+
+	ret = npi_down_register_driver();
+	if (ret) {
+		pr_err("npi_down_register_driver() failed!\n");
+		return ret;
+	}
+
+	return ret;
+
+}
+
+module_init(npi_down_status_init);
+
+static void __exit npi_down_status_exit(void)
+{
+}
+module_exit(npi_down_status_exit);
+
+MODULE_DESCRIPTION("Get NPI down status");
+MODULE_LICENSE("GPL v2");
+