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