[Feature]add MT2731_MP2_MR2_SVN388 baseline version

Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/kernel/modules/connectivity/gps_driver/gps.c b/src/kernel/modules/connectivity/gps_driver/gps.c
new file mode 100644
index 0000000..2a0be3c
--- /dev/null
+++ b/src/kernel/modules/connectivity/gps_driver/gps.c
@@ -0,0 +1,1291 @@
+ /*
+  * Copyright (C) 2011-2014 MediaTek Inc.
+  *
+  * This program is free software: you can redistribute it and/or modify it
+  * under the terms of the GNU General Public License 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, see <http://www.gnu.org/licenses/>.
+  */
+
+/******************************************************************************
+ * Dependency
+ ******************************************************************************/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/sched/signal.h>
+#include <linux/poll.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/cdev.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/uaccess.h>
+/* #include <linux/xlog.h> */
+#include <linux/printk.h>
+#include <linux/semaphore.h>
+#include <linux/version.h>
+#include <linux/regulator/consumer.h>
+
+static int antSwitchFlag;
+static struct regulator *vmch_reg;
+
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+#define pr_fmt(fmt) "["KBUILD_MODNAME"]" fmt
+
+/******************************************************************************
+ * Function Configuration
+ ******************************************************************************/
+/* #define FAKE_DATA */
+#define GPS_SUSPEND_RESUME
+#define GPS_CONFIGURABLE_RESET_DELAY
+/******************************************************************************
+ * Definition
+ ******************************************************************************/
+/* device name and major number */
+#define GPS_DEVNAME            "gps"
+/******************************************************************************
+ * structure & enumeration
+ ******************************************************************************/
+enum {
+	GPS_PWRCTL_UNSUPPORTED = 0xFF,
+	GPS_PWRCTL_OFF = 0x00,
+	GPS_PWRCTL_ON = 0x01,
+	GPS_PWRCTL_RST = 0x02,
+	GPS_PWRCTL_OFF_FORCE = 0x03,
+	GPS_PWRCTL_RST_FORCE = 0x04,
+	GPS_PWRCTL_MAX = 0x05,
+};
+enum {
+	GPS_PWR_UNSUPPORTED = 0xFF,
+	GPS_PWR_RESUME = 0x00,
+	GPS_PWR_SUSPEND = 0x01,
+	GPS_PWR_MAX = 0x02,
+};
+enum {
+	GPS_STATE_UNSUPPORTED = 0xFF,
+	GPS_STATE_OFF = 0x00,	/*cleanup/power off, default state */
+	GPS_STATE_INIT = 0x01,	/*init */
+	GPS_STATE_START = 0x02,	/*start navigating */
+	GPS_STATE_STOP = 0x03,	/*stop navigating */
+	GPS_STATE_DEC_FREQ = 0x04,
+	GPS_STATE_SLEEP = 0x05,
+	GPS_STATE_MAX = 0x06,
+};
+enum {
+	GPS_PWRSAVE_UNSUPPORTED = 0xFF,
+	GPS_PWRSAVE_DEC_FREQ = 0x00,
+	GPS_PWRSAVE_SLEEP = 0x01,
+	GPS_PWRSAVE_OFF = 0x02,
+	GPS_PWRSAVE_MAX = 0x03,
+};
+/*---------------------------------------------------------------------------*/
+struct gps_data {
+	int dat_len;
+	int dat_pos;
+	char dat_buf[4096];
+	spinlock_t lock;
+	wait_queue_head_t read_wait;
+	struct semaphore sem;
+};
+/*---------------------------------------------------------------------------*/
+struct gps_sta_itm {		/*gps status record */
+	unsigned char year;	/*current year - 1900 */
+	unsigned char month;	/*1~12 */
+	unsigned char day;	/*1~31 */
+	unsigned char hour;	/*0~23 */
+
+	unsigned char minute;	/*0~59 */
+	unsigned char sec;	/*0~59 */
+	unsigned char count;	/*reborn count */
+	unsigned char reason;	/*reason: 0: timeout; 1: force */
+};
+/*---------------------------------------------------------------------------*/
+struct gps_sta_obj {
+	int index;
+	struct gps_sta_itm items[32];
+};
+/*---------------------------------------------------------------------------*/
+struct gps_drv_obj {
+	unsigned char pwrctl;
+	unsigned char suspend;
+	unsigned char state;
+	unsigned char pwrsave;
+	int rdelay;		/*power reset delay */
+	struct kobject *kobj;
+	struct mutex sem;
+	struct gps_sta_obj status;
+	struct mt3326_gps_hardware *hw;
+};
+/*---------------------------------------------------------------------------*/
+struct gps_dev_obj {
+	struct class *cls;
+	struct device *dev;
+	dev_t devno;
+	struct cdev chdev;
+	struct mt3326_gps_hardware *hw;
+};
+/******************************************************************************
+ * GPS driver
+ ******************************************************************************/
+#ifdef CONFIG_OF
+static void mt3303_gps_get_dts_data(void);
+#endif
+static int mt3303_power_on(struct regulator *, int state);
+static int mt3303_power_off(struct regulator *, int state);
+
+struct mt3326_gps_hardware {
+	int (*ext_power_on)(struct regulator *, int);
+	int (*ext_power_off)(struct regulator *, int);
+	struct regulator *reg_id;
+};
+
+static struct mt3326_gps_hardware mt3326_gps_hw = {
+	.ext_power_on =  mt3303_power_on,
+	.ext_power_off = mt3303_power_off,
+};
+
+#define min_uV 3300000
+#define max_uV 3500000
+/******************************************************************************
+ * local variables
+ ******************************************************************************/
+static struct gps_data gps_private = { 0 };
+
+#if defined(FAKE_DATA)
+static char fake_data[] = {
+"$GPGGA,135036.000,2446.3713,N,12101.3605,E,1,5,1.61,191.1,M,15.1,M,,*51\r\n"
+"$GPGSA,A,3,22,18,14,30,31,,,,,,,,1.88,1.61,0.98*09\r\n"
+"$GPGSV,2,1,6,18,83,106,32,22,58,324,35,30,45,157,35,14,28,308,32*44\r\n"
+"$GPGSV,2,2,6,40,21,254,,31,17,237,29*42\r\n"
+"$GPRMC,135036.000,A,2446.37125,N,12101.36054,E,0.243,56.48,140109,,A*46\r\n"
+"$GPVTG,56.48,T,,M,0.243,N,0.451,K,A*07\r\n"
+};
+#endif /* FAKE_DATA */
+
+/*
+ * this should be synchronous with mnld.c
+ * enum {
+ *     MNL_RESTART_NONE            = 0x00, //recording the 1st of mnld
+ *     MNL_RESTART_TIMEOUT_INIT    = 0x01, //restart due to timeout
+ *     MNL_RESTART_TIMEOUT_MONITOR = 0x02, //restart due to timeout
+ *     MNL_RESTART_TIMEOUT_WAKEUP  = 0x03, //restart due to timeout
+ *     MNL_RESTART_TIMEOUT_TTFF    = 0x04, //restart due to TTFF timeout
+ *     MNL_RESTART_FORCE           = 0x04, //restart due to external command
+ * };
+ */
+/*---------------------------------------------------------------------------*/
+static char *str_reason[] = {
+	"none",
+	"init",
+	"monitor",
+	"wakeup",
+	"TTFF",
+	"force",
+	"unknown"
+};
+
+/******************************************************************************
+ * Functions
+ ******************************************************************************/
+static inline void mt3326_gps_power(struct mt3326_gps_hardware *hw,
+		unsigned int on, unsigned int force)
+{
+	/*FIX ME: PM_api should provide a function to get current status */
+	static unsigned int power_on = 1;
+	int err;
+
+	pr_info("Switching GPS device %s\n", (on != 0U) ? "on" : "off");
+
+	if (hw == NULL) {
+		pr_debug("null pointer!!\n");
+		return;
+	}
+
+	if (power_on == on) {
+		pr_info("ignore power control: %d\n", on);
+	} else if (on != 0U) {
+		(void)pr_info("power on: %d\n", on);
+		/*power on */
+		if (hw->ext_power_on != NULL) {
+			err = hw->ext_power_on(hw->reg_id, 0);
+			if (err != 0)
+				(void)pr_info("ext_power_on fail\n");
+		}
+		if (hw->ext_power_on != NULL) {
+			err = hw->ext_power_on(hw->reg_id, 1);
+			if (err != 0)
+				(void)pr_info("ext_power_on fail\n");
+		}
+		mdelay(120UL);
+	} else {
+		pr_info("power off: %d\n", on);
+		if (hw->ext_power_off != NULL) {
+			err = hw->ext_power_off(hw->reg_id, force);
+			if (err != 0)
+				(void)pr_info("ext_power_off fail\n");
+		}
+		pr_info("power off ok: %d\n", on);
+	}
+	power_on = on;
+}
+
+/*****************************************************************************/
+static inline void mt3326_gps_reset(struct mt3326_gps_hardware *hw,
+		int delay, int force)
+{
+	mt3326_gps_power(hw, 1, 0);
+	mdelay((unsigned long)delay);
+	mt3326_gps_power(hw, 0, (unsigned int)force);
+	mdelay((unsigned long)delay);
+	mt3326_gps_power(hw, 1, 0);
+}
+
+/******************************************************************************/
+static inline int mt3326_gps_set_suspend(struct gps_drv_obj *obj,
+		unsigned char suspend)
+{
+	if (obj == NULL)
+		return -1;
+	mutex_lock(&obj->sem);
+	if (obj->suspend != suspend) {
+		pr_debug("issue sysfs_notify : %p\n", obj->kobj->sd);
+		sysfs_notify(obj->kobj, NULL, "suspend");
+	}
+	obj->suspend = suspend;
+	mutex_unlock(&obj->sem);
+	return 0;
+}
+
+/******************************************************************************/
+static inline int mt3326_gps_set_pwrctl(struct gps_drv_obj *obj,
+		unsigned char pwrctl)
+{
+	int err = 0;
+
+	if (obj == NULL)
+		return -1;
+	mutex_lock(&obj->sem);
+
+	if ((pwrctl == (unsigned char)GPS_PWRCTL_ON) ||
+		(pwrctl == (unsigned char)GPS_PWRCTL_OFF)) {
+		obj->pwrctl = pwrctl;
+		mt3326_gps_power(obj->hw, pwrctl, 0);
+	} else if (pwrctl == (unsigned char)GPS_PWRCTL_OFF_FORCE) {
+		obj->pwrctl = pwrctl;
+		mt3326_gps_power(obj->hw, pwrctl, 1);
+	} else if (pwrctl == (unsigned char)GPS_PWRCTL_RST) {
+		mt3326_gps_reset(obj->hw, obj->rdelay, 0);
+		obj->pwrctl = (unsigned char)GPS_PWRCTL_ON;
+	} else if (pwrctl == (unsigned char)GPS_PWRCTL_RST_FORCE) {
+		mt3326_gps_reset(obj->hw, obj->rdelay, 1);
+		obj->pwrctl = (unsigned char)GPS_PWRCTL_ON;
+	} else {
+		err = -1;
+	}
+	mutex_unlock(&obj->sem);
+	return err;
+}
+
+/******************************************************************************/
+static inline int mt3326_gps_set_status(struct gps_drv_obj *obj,
+		const char *buf, size_t count)
+{
+	int err = 0;
+	int year, mon, day, hour, minute, sec, cnt, reason, idx;
+
+	if (obj == NULL)
+		return -1;
+
+	mutex_lock(&obj->sem);
+	if (sscanf(buf, "(%d/%d/%d %d:%d:%d) - %d/%d", &year, &mon, &day,
+			&hour, &minute, &sec, &cnt, &reason) == 8) {
+		int number = (int)ARRAY_SIZE(obj->status.items);
+
+		idx = obj->status.index % number;
+		obj->status.items[idx].year = (unsigned char)year;
+		obj->status.items[idx].month = (unsigned char)mon;
+		obj->status.items[idx].day = (unsigned char)day;
+		obj->status.items[idx].hour = (unsigned char)hour;
+		obj->status.items[idx].minute = (unsigned char)minute;
+		obj->status.items[idx].sec = (unsigned char)sec;
+		obj->status.items[idx].count = (unsigned char)cnt;
+		obj->status.items[idx].reason = (unsigned char)reason;
+		obj->status.index++;
+	} else {
+		err = -1;
+	}
+	mutex_unlock(&obj->sem);
+	return err;
+}
+
+/******************************************************************************/
+static inline int mt3326_gps_set_state(struct gps_drv_obj *obj,
+		unsigned char state)
+{
+	int err = 0;
+
+	if (obj == NULL)
+		return -1;
+	mutex_lock(&obj->sem);
+	if (state < (unsigned char)GPS_STATE_MAX)
+		obj->state = state;
+	else
+		err = -1;
+	mutex_unlock(&obj->sem);
+	return err;
+}
+
+/******************************************************************************/
+static inline int mt3326_gps_set_pwrsave(struct gps_drv_obj *obj,
+		unsigned char pwrsave)
+{
+	int err = 0;
+
+	if (obj == NULL)
+		return -1;
+	mutex_lock(&obj->sem);
+	if (pwrsave < (unsigned char)GPS_PWRSAVE_MAX)
+		obj->pwrsave = pwrsave;
+	else
+		err = -1;
+	mutex_unlock(&obj->sem);
+	return err;
+}
+
+/******************************************************************************/
+static inline int mt3326_gps_dev_suspend(struct gps_drv_obj *obj)
+{
+#if defined(GPS_SUSPEND_RESUME)
+	int err;
+
+	err = mt3326_gps_set_suspend(obj, GPS_PWR_SUSPEND);
+	if (err != 0)
+		pr_debug("set suspend fail: %d\n", err);
+	err = mt3326_gps_set_pwrctl(obj, GPS_PWRCTL_OFF);
+	if (err != 0)
+		pr_debug("set pwrctl fail: %d\n", err);
+	return err;
+#endif
+}
+
+/******************************************************************************/
+static inline int mt3326_gps_dev_resume(struct gps_drv_obj *obj)
+{
+#if defined(GPS_SUSPEND_RESUME)
+	int err;
+
+	err = mt3326_gps_set_suspend(obj, GPS_PWR_RESUME);
+	if (err != 0)
+		pr_debug("set suspend fail: %d\n", err);
+	/*don't power on device automatically */
+	return err;
+#endif
+}
+
+/******************************************************************************/
+static ssize_t mt3326_show_pwrctl(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct gps_drv_obj *obj;
+	ssize_t res;
+
+	if (dev == NULL) {
+		pr_debug("dev is null!!\n");
+		return 0;
+	}
+	obj = (struct gps_drv_obj *)dev_get_drvdata(dev);
+	if (obj == NULL) {
+		pr_debug("drv data is null!!\n");
+		return 0;
+	}
+	mutex_lock(&obj->sem);
+	res = snprintf(buf, PAGE_SIZE, "%d\n", obj->pwrctl);
+	mutex_unlock(&obj->sem);
+	return res;
+}
+
+/******************************************************************************/
+static ssize_t mt3326_store_pwrctl(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct gps_drv_obj *obj;
+
+	if (dev == NULL) {
+		pr_debug("dev is null!!\n");
+		return 0;
+	}
+	obj = (struct gps_drv_obj *)dev_get_drvdata(dev);
+	if (obj == NULL) {
+		pr_debug("drv data is null!!\n");
+		return 0;
+	}
+	if ((count == (size_t)1) || ((count == (size_t)2)
+		&& (buf[1] == '\n')))	{
+		unsigned char pwrctl = (unsigned char)(buf[0] - '0');
+
+		if (mt3326_gps_set_pwrctl(obj, pwrctl) == 0)
+			return (ssize_t)count;
+	}
+	return (ssize_t)count;
+}
+
+/******************************************************************************/
+static ssize_t mt3326_show_suspend(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct gps_drv_obj *obj;
+	ssize_t res;
+
+	if (dev == NULL) {
+		pr_debug("dev is null!!\n");
+		return 0;
+	}
+	obj = (struct gps_drv_obj *)dev_get_drvdata(dev);
+	if (obj == NULL) {
+		pr_debug("drv data is null!!\n");
+		return 0;
+	}
+	mutex_lock(&obj->sem);
+	res = snprintf(buf, PAGE_SIZE, "%d\n", obj->suspend);
+	mutex_unlock(&obj->sem);
+	return res;
+}
+
+/******************************************************************************/
+static ssize_t mt3326_store_suspend(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct gps_drv_obj *obj;
+
+	if (dev == NULL) {
+		pr_debug("dev is null!!\n");
+		return 0;
+	}
+	obj = (struct gps_drv_obj *)dev_get_drvdata(dev);
+	if (obj == NULL) {
+		pr_debug("drv data is null!!\n");
+		return 0;
+	}
+	if ((count == (size_t)1) || ((count == (size_t)2)
+		&& (buf[1] == '\n'))) {
+		int suspend = buf[0] - '0';
+
+		if (suspend == GPS_PWR_SUSPEND) {
+			if (mt3326_gps_dev_suspend(obj) == 0)
+				return (ssize_t)count;
+		} else if (suspend == GPS_PWR_RESUME) {
+			if (mt3326_gps_dev_resume(obj) == 0)
+				return (ssize_t)count;
+		} else {
+			pr_debug("suspend value error: %d!!\n", suspend);
+			return 0;
+		}
+	}
+	return (ssize_t)count;
+}
+
+/******************************************************************************/
+static ssize_t mt3326_show_status(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int res, idx, num, left, cnt, len;
+	struct gps_drv_obj *obj;
+	char *reason = NULL;
+	int reason_max = (int)ARRAY_SIZE(str_reason);
+
+	if (dev == NULL) {
+		pr_debug("dev is null!!\n");
+		return 0;
+	}
+	obj = (struct gps_drv_obj *)dev_get_drvdata(dev);
+	if (obj == NULL) {
+		pr_debug("drv data is null!!\n");
+		return 0;
+	}
+	mutex_lock(&obj->sem);
+	num = (int)ARRAY_SIZE(obj->status.items);
+	left = (int)PAGE_SIZE;
+	cnt = 0;
+	len = 0;
+	for (idx = 0; idx < num; idx++) {
+		if (obj->status.items[idx].month == 0)
+			continue;
+		if (obj->status.items[idx].reason >= reason_max)
+			reason = str_reason[reason_max - 1];
+		else
+			reason = str_reason[obj->status.items[idx].reason];
+		cnt = snprintf(&buf[len], left,
+			"[%d] %.4d/%.2d/%.2d %.2d:%.2d:%.2d - %d, %s\n",
+			idx, obj->status.items[idx].year + 1900,
+			obj->status.items[idx].month,
+			obj->status.items[idx].day,
+			obj->status.items[idx].hour,
+			obj->status.items[idx].minute,
+			obj->status.items[idx].sec,
+			obj->status.items[idx].count, reason);
+		left -= cnt;
+		len += cnt;
+	}
+	res = PAGE_SIZE - left;
+	mutex_unlock(&obj->sem);
+	return res;
+}
+
+/******************************************************************************/
+static ssize_t mt3326_store_status(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	int res = 0;
+	struct gps_drv_obj *obj;
+
+	if (dev == NULL) {
+		pr_debug("dev is null!!\n");
+		return 0;
+	}
+	obj = (struct gps_drv_obj *)dev_get_drvdata(dev);
+	if (obj == NULL) {
+		pr_debug("drv data is null!!\n");
+		return 0;
+	}
+	res = mt3326_gps_set_status(obj, buf, count);
+	if (res == 0)
+		return (ssize_t)count;
+
+	pr_debug("invalid content: '%p', length = %zu\n", buf, count);
+	return (ssize_t)count;
+}
+
+/******************************************************************************/
+static ssize_t mt3326_show_state(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	ssize_t res;
+	struct gps_drv_obj *obj;
+
+	if (dev == NULL) {
+		pr_debug("dev is null!!\n");
+		return 0;
+	}
+	obj = (struct gps_drv_obj *)dev_get_drvdata(dev);
+	if (obj == NULL) {
+		pr_debug("drv data is null!!\n");
+		return 0;
+	}
+	mutex_lock(&obj->sem);
+	res = snprintf(buf, PAGE_SIZE, "%d\n", obj->state);
+	mutex_unlock(&obj->sem);
+	return res;
+}
+
+/******************************************************************************/
+static ssize_t mt3326_store_state(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct gps_drv_obj *obj;
+
+	if (dev == NULL) {
+		pr_debug("dev is null!!\n");
+		return 0;
+	}
+	obj = (struct gps_drv_obj *)dev_get_drvdata(dev);
+	if (obj == NULL) {
+		pr_debug("drv data is null!!\n");
+		return 0;
+	}
+	if ((count == (size_t)1) || ((count == (size_t)2) &&
+		(buf[1] == '\n'))) {
+	/*To Do: dynamic change according to input */
+		unsigned char state = (unsigned char)(buf[0] - '0');
+
+		if (mt3326_gps_set_state(obj, state) == 0)
+			return (ssize_t)count;
+	}
+	pr_debug("invalid content: '%p', length = %zu\n", buf, count);
+	return (ssize_t)count;
+}
+
+/******************************************************************************/
+static ssize_t mt3326_show_pwrsave(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	ssize_t res;
+	struct gps_drv_obj *obj;
+
+	if (dev == NULL) {
+		pr_debug("dev is null!!\n");
+		return 0;
+	}
+	obj = (struct gps_drv_obj *)dev_get_drvdata(dev);
+	if (obj == NULL) {
+		pr_debug("drv data is null!!\n");
+		return 0;
+	}
+	mutex_lock(&obj->sem);
+	res = snprintf(buf, PAGE_SIZE, "%d\n", obj->pwrsave);
+	mutex_unlock(&obj->sem);
+	return res;
+}
+
+/******************************************************************************/
+static ssize_t mt3326_store_pwrsave(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct gps_drv_obj *obj;
+
+	if (dev == NULL) {
+		pr_debug("dev is null!!\n");
+		return 0;
+	}
+	obj = (struct gps_drv_obj *)dev_get_drvdata(dev);
+	if (obj == NULL) {
+		pr_debug("drv data is null!!\n");
+		return 0;
+	}
+	if ((count == (size_t)1) || ((count == (size_t)2) &&
+		(buf[1] == '\n')))	{
+		unsigned char pwrsave = (unsigned char)(buf[0] - '0');
+
+		if (mt3326_gps_set_pwrsave(obj, pwrsave) == 0)
+			return (ssize_t)count;
+	}
+	pr_debug("invalid content: '%p', length = %zu\n", buf, count);
+	return (ssize_t)count;
+}
+
+/******************************************************************************/
+#if defined(GPS_CONFIGURABLE_RESET_DELAY)
+/******************************************************************************/
+static ssize_t mt3326_show_rdelay(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	ssize_t res;
+	struct gps_drv_obj *obj;
+
+	if (dev == NULL) {
+		pr_debug("dev is null!!\n");
+		return 0;
+	}
+	obj = (struct gps_drv_obj *)dev_get_drvdata(dev);
+	if (obj == NULL) {
+		pr_debug("drv data is null!!\n");
+		return 0;
+	}
+	mutex_lock(&obj->sem);
+	res = snprintf(buf, PAGE_SIZE, "%d\n", obj->rdelay);
+	mutex_unlock(&obj->sem);
+	return res;
+}
+
+/******************************************************************************/
+static ssize_t mt3326_store_rdelay(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct gps_drv_obj *obj;
+	int rdelay;
+	unsigned long val = 0;
+
+	if (dev == NULL) {
+		pr_debug("dev is null!!\n");
+		return 0;
+	}
+	obj = (struct gps_drv_obj *)dev_get_drvdata(dev);
+	if (obj == NULL) {
+		pr_debug("drv data is null!!\n");
+		return 0;
+	}
+	rdelay = (int)kstrtol(buf, 10, &val);
+	if (rdelay == 0 && val <= 2000) {
+		mutex_lock(&obj->sem);
+		obj->rdelay = val;
+		mutex_unlock(&obj->sem);
+		return (ssize_t)count;
+	}
+	pr_debug("invalid content: '%p', length = %zu\n", buf, count);
+	return (ssize_t)count;
+}
+
+/******************************************************************************/
+#endif
+/******************************************************************************/
+DEVICE_ATTR(pwrctl, 0664, mt3326_show_pwrctl, mt3326_store_pwrctl);
+DEVICE_ATTR(suspend, 0664, mt3326_show_suspend, mt3326_store_suspend);
+DEVICE_ATTR(status, 0664, mt3326_show_status, mt3326_store_status);
+DEVICE_ATTR(state, 0664, mt3326_show_state, mt3326_store_state);
+DEVICE_ATTR(pwrsave, 0664, mt3326_show_pwrsave, mt3326_store_pwrsave);
+#if defined(GPS_CONFIGURABLE_RESET_DELAY)
+DEVICE_ATTR(rdelay, 0664, mt3326_show_rdelay, mt3326_store_rdelay);
+#endif
+static struct device_attribute *gps_attr_list[] = {
+	&dev_attr_pwrctl,
+	&dev_attr_suspend,
+	&dev_attr_status,
+	&dev_attr_state,
+	&dev_attr_pwrsave,
+#if defined(GPS_CONFIGURABLE_RESET_DELAY)
+	&dev_attr_rdelay,
+#endif
+};
+
+/******************************************************************************/
+static int mt3326_gps_create_attr(struct device *dev)
+{
+	int idx, err = 0;
+	int num = (int)ARRAY_SIZE(gps_attr_list);
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	for (idx = 0; idx < num; idx++) {
+		err = device_create_file(dev, gps_attr_list[idx]);
+		if (err != 0) {
+			pr_debug("device_create_file (%s) = %d\n",
+				gps_attr_list[idx]->attr.name, err);
+			break;
+		}
+	}
+
+	return err;
+}
+
+/******************************************************************************/
+static int mt3326_gps_delete_attr(struct device *dev)
+{
+	int idx, err = 0;
+	int num = (int)ARRAY_SIZE(gps_attr_list);
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	for (idx = 0; idx < num; idx++)
+		device_remove_file(dev, gps_attr_list[idx]);
+
+	return err;
+}
+
+/******************************************************************************/
+static long mt3326_gps_unlocked_ioctl(struct file *file, unsigned int cmd,
+		unsigned long arg)
+{
+	pr_debug("%s!!\n", __func__);
+	return -ENOIOCTLCMD;
+}
+
+/******************************************************************************/
+static long mt3326_gps_compat_ioctl(struct file *file, unsigned int cmd,
+		unsigned long arg)
+{
+	long ret;
+
+	pr_debug("%s!!\n", __func__);
+	ret = mt3326_gps_unlocked_ioctl(file, cmd, arg);
+	return ret;
+}
+
+/*****************************************************************************/
+static int mt3326_gps_open(struct inode *inode, struct file *file)
+{
+	/* all files share the same buffer */
+	file->private_data = &gps_private;
+	return nonseekable_open(inode, file);
+}
+
+/*****************************************************************************/
+static int mt3326_gps_release(struct inode *inode, struct file *file)
+{
+	struct gps_data *dev = file->private_data;
+
+	if (dev != NULL)
+		file->private_data = NULL;
+
+	return 0;
+}
+
+/******************************************************************************/
+static ssize_t mt3326_gps_read(struct file *file, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct gps_data *dev = file->private_data;
+	ssize_t ret = 0;
+	int copy_len = 0;
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	if (signal_pending(current) != 0)
+		return -ERESTARTSYS;
+
+	if (down_interruptible(&dev->sem) != 0)
+		return -ERESTARTSYS;
+
+	if (dev->dat_len == 0) {	/*no data to be read */
+		up(&dev->sem);
+		/*non-block mode */
+		if ((file->f_flags &
+			(unsigned int)O_NONBLOCK) == (unsigned int)O_NONBLOCK)
+			return -EAGAIN;
+		do {		/*block mode */
+			ret = wait_event_interruptible
+				(dev->read_wait, (dev->dat_len > 0));
+			if (ret == -ERESTARTSYS)
+				return -ERESTARTSYS;
+		} while (ret == 0);
+		if (down_interruptible(&dev->sem) != 0)
+			return -ERESTARTSYS;
+	}
+
+	/*data is available */
+	copy_len = (dev->dat_len < (int)count) ? (dev->dat_len) : (int)(count);
+	if (copy_to_user(buf, (dev->dat_buf + dev->dat_pos),
+			(unsigned long)copy_len) != 0UL) {
+		ret = -EFAULT;
+	} else {
+		/*pr_debug("mt3326_gps_read(%ld,%d,%d) = %d\n",
+		 *count, dev->dat_pos, dev->dat_len, copy_len);
+		 */
+		if (dev->dat_len > (copy_len + dev->dat_pos)) {
+			dev->dat_pos += copy_len;
+		} else {
+			dev->dat_len = 0;
+			dev->dat_pos = 0;
+		}
+		ret = copy_len;
+	}
+
+	up(&dev->sem);
+	/* pr_debug("%s return %ld bytes\n", __func__, ret); */
+	return ret;
+}
+
+/******************************************************************************/
+static ssize_t mt3326_gps_write(struct file *file, const char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct gps_data *dev = file->private_data;
+	ssize_t ret = 0;
+	size_t copy_size = 0;
+
+	if (dev == NULL)
+		return -EINVAL;
+
+	if (count == 0UL)		/*no data written */
+		return 0;
+
+	if (signal_pending(current) != 0)
+		return -ERESTARTSYS;
+
+	if (down_interruptible(&dev->sem) != 0)
+		return -ERESTARTSYS;
+
+	copy_size = (count < (size_t)4096) ? count : (size_t)4096;
+	if (copy_from_user(dev->dat_buf, buf, copy_size) != 0UL) {
+		pr_debug("copy_from_user error");
+		ret = -EFAULT;
+	} else {
+		dev->dat_len = (int)count;
+		dev->dat_pos = 0;
+		ret = (ssize_t)count;
+	}
+	up(&dev->sem);
+	wake_up_interruptible(&dev->read_wait);
+	pr_debug("%s: write %d bytes\n", __func__, dev->dat_len);
+	return ret;
+}
+
+/******************************************************************************/
+static unsigned int mt3326_gps_poll(struct file *file, poll_table *wait)
+{
+	struct gps_data *dev = file->private_data;
+	unsigned int mask = 0;
+
+	if (dev == NULL)
+		return 0;
+
+	down(&dev->sem);
+	poll_wait(file, &dev->read_wait, wait);
+	if (dev->dat_len != 0)	/*readable if data is available */
+		mask = (((unsigned int)POLLIN | (unsigned int)POLLRDNORM)
+			| ((unsigned int)POLLOUT | (unsigned int)POLLWRNORM));
+	else			/*always writable */
+		mask = ((unsigned int)POLLOUT | (unsigned int)POLLWRNORM);
+	up(&dev->sem);
+	pr_debug("%s: mask : 0x%X\n", __func__, mask);
+	return mask;
+}
+
+/*****************************************************************************/
+/* Kernel interface */
+static const struct file_operations mt3326_gps_fops = {
+	.owner = THIS_MODULE,
+	.unlocked_ioctl = mt3326_gps_unlocked_ioctl,
+	.compat_ioctl = mt3326_gps_compat_ioctl,
+	.open = mt3326_gps_open,
+	.read = mt3326_gps_read,
+	.write = mt3326_gps_write,
+	.release = mt3326_gps_release,
+	.poll = mt3326_gps_poll,
+};
+
+/*****************************************************************************/
+static void mt3326_gps_hw_init(struct mt3326_gps_hardware *hw)
+{
+	mt3326_gps_power(hw, 1, 0);
+}
+
+/*****************************************************************************/
+static void mt3326_gps_hw_exit(struct mt3326_gps_hardware *hw)
+{
+	mt3326_gps_power(hw, 0, 0);
+}
+
+/*****************************************************************************/
+static int mt3326_gps_probe(struct platform_device *dev)
+{
+	int ret = 0;
+	int err = 0;
+	struct gps_drv_obj *drvobj = NULL;
+	/*struct mt3326_gps_hardware *hw =
+	 *(struct mt3326_gps_hardware *)dev->dev.platform_data;
+	 */
+	struct mt3326_gps_hardware *hw = &mt3326_gps_hw;
+	struct gps_dev_obj *devobj = NULL;
+
+#ifdef CONFIG_OF
+	mt3303_gps_get_dts_data();
+#endif
+
+	devobj = kzalloc(sizeof(*devobj), GFP_KERNEL);
+	if (devobj  == NULL) {
+		/*(void)pr_err("kzalloc fail\n");*/
+		err = -ENOMEM;
+		return -1;
+	}
+
+	pr_info("get regulator");
+#ifdef CONFIG_MTK_PMIC_CHIP_MT6356
+	hw->reg_id = regulator_get(&dev->dev, "vcn33_wifi");
+#else
+	hw->reg_id = regulator_get(&dev->dev, "vcn33");
+#endif
+	if (!hw->reg_id) {
+		pr_info("regulator_get reg_id failed.\n");
+		return -1;
+	}
+
+	pr_info("regulator_get_voltage reg_id = %d uV\n",
+		regulator_get_voltage(hw->reg_id));
+
+	/*set voltage*/
+	if (regulator_set_voltage(hw->reg_id, min_uV, max_uV)) {
+		pr_info("regulator_set_voltage reg_id failed.\n");
+	};
+
+	mt3326_gps_hw_init(hw);
+	ret = alloc_chrdev_region(&devobj->devno, 0, 1, GPS_DEVNAME);
+	if (ret != 0) {
+		(void)pr_err("alloc_chrdev_region fail: %d\n", ret);
+		goto error;
+	} else {
+		pr_debug("major: %d, minor: %d\n", MAJOR(devobj->devno),
+				MINOR(devobj->devno));
+	}
+	cdev_init(&devobj->chdev, &mt3326_gps_fops);
+	devobj->chdev.owner = THIS_MODULE;
+	err = cdev_add(&devobj->chdev, devobj->devno, 1);
+	if (err != 0) {
+		(void)pr_err("cdev_add fail: %d\n", err);
+		goto error;
+	}
+	drvobj = kmalloc(sizeof(*drvobj), GFP_KERNEL);
+	if (drvobj == NULL) {
+		err = -ENOMEM;
+		goto error;
+	}
+	memset(drvobj, 0, sizeof(*drvobj));
+
+	devobj->cls = class_create(THIS_MODULE, "gpsdrv");
+	if (IS_ERR(devobj->cls)) {
+		(void)pr_err("Unable to create class, err = %d\n",
+				(int)PTR_ERR(devobj->cls));
+		goto error;
+	}
+	devobj->dev = device_create(devobj->cls, NULL,
+		devobj->devno, drvobj, "gps");
+	drvobj->hw = hw;
+	drvobj->pwrctl = 0;
+	drvobj->suspend = 0;
+	drvobj->state = GPS_STATE_UNSUPPORTED;
+	drvobj->pwrsave = GPS_PWRSAVE_UNSUPPORTED;
+	drvobj->rdelay = 50;
+	drvobj->kobj = &devobj->dev->kobj;
+	mutex_init(&drvobj->sem);
+
+	err = mt3326_gps_create_attr(devobj->dev);
+	if (err != 0)
+		goto error;
+
+	/* initialize members */
+	spin_lock_init(&gps_private.lock);
+	init_waitqueue_head(&gps_private.read_wait);
+	sema_init(&gps_private.sem, 1);
+
+	gps_private.dat_len = 0;
+	gps_private.dat_pos = 0;
+	(void)memset(gps_private.dat_buf, 0x00, sizeof(gps_private.dat_buf));
+
+	/* set platform data: a new device created for gps */
+	platform_set_drvdata(dev, devobj);
+
+	if (antSwitchFlag) {
+		pr_info("mt3303 get regulator");
+		vmch_reg = regulator_get(devobj->dev, "vmch");
+		if (!vmch_reg) {
+			pr_info("mt3303 regulator_get vmch failed.\n");
+			return -1;
+		}
+
+		pr_info("mt3303 regulator_get_voltage reg_id = %d uV\n",
+			regulator_get_voltage(vmch_reg));
+	}
+	pr_debug("Done\n");
+
+	return 0;
+
+error:
+	if (err == 0)
+		cdev_del(&devobj->chdev);
+	if (ret == 0)
+		unregister_chrdev_region(devobj->devno, 1);
+	kfree(devobj);
+	kfree(drvobj);
+
+	return -1;
+}
+
+/*****************************************************************************/
+static int mt3326_gps_remove(struct platform_device *dev)
+{
+	int err;
+	struct gps_dev_obj *devobj =
+		(struct gps_dev_obj *)platform_get_drvdata(dev);
+	struct gps_drv_obj *drvobj;
+
+	if (devobj == NULL) {
+		(void)pr_err("null pointer: %p\n", devobj);
+		return -1;
+	}
+
+	drvobj = (struct gps_drv_obj *)dev_get_drvdata(devobj->dev);
+	if (drvobj == NULL) {
+		(void)pr_err("null pointer: %p\n", drvobj);
+		return -1;
+	}
+	pr_debug("Unregistering chardev\n");
+
+	cdev_del(&devobj->chdev);
+	unregister_chrdev_region(devobj->devno, 1);
+
+	mt3326_gps_hw_exit(devobj->hw);
+	err = mt3326_gps_delete_attr(devobj->dev);
+	if (err != 0)
+		pr_debug("delete attr fails: %d\n", err);
+	device_destroy(devobj->cls, devobj->devno);
+	class_destroy(devobj->cls);
+
+	kfree(devobj);
+	pr_debug("Done\n");
+	return 0;
+}
+
+/*****************************************************************************/
+static void mt3326_gps_shutdown(struct platform_device *dev)
+{
+	struct gps_dev_obj *devobj =
+		(struct gps_dev_obj *)platform_get_drvdata(dev);
+
+	pr_debug("Shutting down\n");
+	mt3326_gps_hw_exit(devobj->hw);
+}
+
+/*****************************************************************************/
+#ifdef CONFIG_PM
+/*****************************************************************************/
+static int mt3326_gps_suspend(struct platform_device *dev,
+	pm_message_t state)
+{
+	int err = 0;
+	struct gps_dev_obj *devobj =
+		(struct gps_dev_obj *)platform_get_drvdata(dev);
+	struct gps_drv_obj *drvobj;
+
+	if (devobj == NULL) {
+		(void)pr_err("null pointer: %p\n", devobj);
+		return -1;
+	}
+
+	drvobj = (struct gps_drv_obj *)dev_get_drvdata(devobj->dev);
+	if (drvobj == NULL) {
+		(void)pr_err("null pointer: %p\n", drvobj);
+		return -1;
+	}
+
+	pr_debug("dev = %p, event = %u,", dev, state.event);
+	if (state.event == PM_EVENT_SUSPEND)
+		err = mt3326_gps_dev_suspend(drvobj);
+	return err;
+}
+
+/*****************************************************************************/
+static int mt3326_gps_resume(struct platform_device *dev)
+{
+	struct gps_dev_obj *devobj =
+		(struct gps_dev_obj *)platform_get_drvdata(dev);
+	struct gps_drv_obj *drvobj =
+		(struct gps_drv_obj *)dev_get_drvdata(devobj->dev);
+
+	return mt3326_gps_dev_resume(drvobj);
+}
+
+/*****************************************************************************/
+#endif /* CONFIG_PM */
+/*****************************************************************************/
+#ifdef CONFIG_OF
+static const struct of_device_id apgps_of_ids[] = {
+	{.compatible = "mediatek,mt3303",},
+	{}
+};
+#endif
+static struct platform_driver mt3326_gps_driver = {
+	.probe = mt3326_gps_probe,
+	.remove = mt3326_gps_remove,
+	.shutdown = mt3326_gps_shutdown,
+#if defined(CONFIG_PM)
+	.suspend = mt3326_gps_suspend,
+	.resume = mt3326_gps_resume,
+#endif
+	.driver = {
+		   .name = GPS_DEVNAME,
+		   .bus = &platform_bus_type,
+#ifdef CONFIG_OF
+		   .of_match_table = apgps_of_ids,
+#endif
+	},
+};
+
+#ifdef CONFIG_OF
+static void mt3303_gps_get_dts_data(void)
+{
+	struct device_node *node = NULL;
+
+	pr_info("mt3303 before: %d", antSwitchFlag);
+	node = of_find_matching_node(node, apgps_of_ids);
+	if (node) {
+		pr_info("of_property_read_u32");
+		of_property_read_u32(node, "antSwitchFlag", &antSwitchFlag);
+	}
+	pr_info("mt3303 after: %d", antSwitchFlag);
+}
+#endif
+
+/*****************************************************************************/
+static int __init mt3326_gps_mod_init(void)
+{
+	int ret = 0;
+	(void)pr_info("%s", __func__);
+	/* ret = driver_register(&mt3326_gps_driver); */
+	ret = platform_driver_register(&mt3326_gps_driver);
+
+	return ret;
+}
+
+/*****************************************************************************/
+static void __exit mt3326_gps_mod_exit(void)
+{
+	platform_driver_unregister(&mt3326_gps_driver);
+}
+
+static int mt3303_power_on(struct regulator *reg_id, int state)
+{
+	int ret;
+
+	if (antSwitchFlag && vmch_reg) {
+		if (!regulator_is_enabled(vmch_reg)) {
+			if (regulator_enable(vmch_reg)) {
+				pr_info("mt3303 regulator_enable vmch failed\n");
+				return 1;
+			}
+		}
+		pr_info("mt3303 regulator_enabled vmch is %s\n",
+			regulator_is_enabled(vmch_reg)?"enable":"non-enable");
+	}
+
+	if (reg_id) {
+		if (!regulator_is_enabled(reg_id)) {
+			ret = regulator_enable(reg_id);
+			if (ret) {
+				pr_info("mt3303 regulator_enable failed\n");
+				return 1;
+			}
+		}
+		pr_info("mt3303 regulator_enabled is %s\n",
+			regulator_is_enabled(reg_id)?"enable":"non-enable");
+		return 0;
+	}
+
+	return 1;
+}
+
+static int mt3303_power_off(struct regulator *reg_id, int state)
+{
+	int ret;
+
+	if (antSwitchFlag && vmch_reg) {
+		if (regulator_is_enabled(vmch_reg)) {
+			if (regulator_disable(vmch_reg)) {
+				pr_info("mt3303 regulator_disable vmch failed\n");
+				return 1;
+			}
+		}
+		pr_info("mt3303 regulator_disable vmch is %s\n",
+			regulator_is_enabled(vmch_reg)?"enable":"non-enable");
+	}
+
+	if (reg_id) {
+		if (regulator_is_enabled(reg_id)) {
+			ret = regulator_disable(reg_id);
+			if (ret) {
+				pr_info("mt3303 regulator_disable failed\n");
+				return 1;
+			}
+		}
+		pr_info("mt3303 regulator_disable is %s\n",
+			regulator_is_enabled(reg_id)?"enable":"non-enable");
+		return 0;
+	}
+
+	return 1;
+}
+
+/*****************************************************************************/
+module_init(mt3326_gps_mod_init);
+module_exit(mt3326_gps_mod_exit);
+/*****************************************************************************/
+MODULE_AUTHOR("MingHsien Hsieh <MingHsien.Hsieh@mediatek.com>");
+MODULE_DESCRIPTION("MT3326 GPS Driver");
+MODULE_LICENSE("GPL");