stp_chrdev_gps.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. /*
  2. * Copyright (C) 2011-2014 MediaTek Inc.
  3. *
  4. * This program is free software: you can redistribute it and/or modify it under the terms of the
  5. * GNU General Public License version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  8. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  9. * See the GNU General Public License for more details.
  10. *
  11. * You should have received a copy of the GNU General Public License along with this program.
  12. * If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. #include <linux/init.h>
  15. #include <linux/module.h>
  16. #include <linux/types.h>
  17. #include <linux/kernel.h>
  18. #include <linux/fs.h>
  19. #include <linux/cdev.h>
  20. #include <linux/sched.h>
  21. #include <asm/current.h>
  22. #include <asm/uaccess.h>
  23. #include <linux/skbuff.h>
  24. #if WMT_CREATE_NODE_DYNAMIC || REMOVE_MK_NODE
  25. #include <linux/device.h>
  26. #endif
  27. #include "osal_typedef.h"
  28. #include "stp_exp.h"
  29. #include "wmt_exp.h"
  30. #if defined(CONFIG_ARCH_MT6580)
  31. #include <mt_clkbuf_ctl.h>
  32. #endif
  33. MODULE_LICENSE("GPL");
  34. #define GPS_DRIVER_NAME "mtk_stp_GPS_chrdev"
  35. #define GPS_DEV_MAJOR 191 /* never used number */
  36. #define GPS_DEBUG_TRACE_GPIO 0
  37. #define GPS_DEBUG_DUMP 0
  38. #define PFX "[GPS] "
  39. #define GPS_LOG_DBG 3
  40. #define GPS_LOG_INFO 2
  41. #define GPS_LOG_WARN 1
  42. #define GPS_LOG_ERR 0
  43. #define COMBO_IOC_GPS_HWVER 6
  44. #define COMBO_IOC_GPS_IC_HW_VERSION 7
  45. #define COMBO_IOC_GPS_IC_FW_VERSION 8
  46. #define COMBO_IOC_D1_EFUSE_GET 9
  47. #define COMBO_IOC_RTC_FLAG 10
  48. #define COMBO_IOC_CO_CLOCK_FLAG 11
  49. static UINT32 gDbgLevel = GPS_LOG_DBG;
  50. #define GPS_DBG_FUNC(fmt, arg...) \
  51. do { if (gDbgLevel >= GPS_LOG_DBG) \
  52. pr_debug(PFX "[D]%s: " fmt, __func__ , ##arg); \
  53. } while (0)
  54. #define GPS_INFO_FUNC(fmt, arg...) \
  55. do { if (gDbgLevel >= GPS_LOG_INFO) \
  56. pr_info(PFX "[I]%s: " fmt, __func__ , ##arg); \
  57. } while (0)
  58. #define GPS_WARN_FUNC(fmt, arg...) \
  59. do { if (gDbgLevel >= GPS_LOG_WARN) \
  60. pr_warn(PFX "[W]%s: " fmt, __func__ , ##arg); \
  61. } while (0)
  62. #define GPS_ERR_FUNC(fmt, arg...) \
  63. do { if (gDbgLevel >= GPS_LOG_ERR) \
  64. pr_err(PFX "[E]%s: " fmt, __func__ , ##arg); \
  65. } while (0)
  66. #define GPS_TRC_FUNC(f) \
  67. do { if (gDbgLevel >= GPS_LOG_DBG) \
  68. pr_info(PFX "<%s> <%d>\n", __func__, __LINE__); \
  69. } while (0)
  70. static int GPS_devs = 1; /* device count */
  71. static int GPS_major = GPS_DEV_MAJOR; /* dynamic allocation */
  72. module_param(GPS_major, uint, 0);
  73. static struct cdev GPS_cdev;
  74. #if (defined(CONFIG_MTK_GMO_RAM_OPTIMIZE) && !defined(CONFIG_MT_ENG_BUILD))
  75. #define STP_GPS_BUFFER_SIZE 2048
  76. #else
  77. #define STP_GPS_BUFFER_SIZE MTKSTP_BUFFER_SIZE
  78. #endif
  79. static unsigned char i_buf[STP_GPS_BUFFER_SIZE]; /* input buffer of read() */
  80. static unsigned char o_buf[STP_GPS_BUFFER_SIZE]; /* output buffer of write() */
  81. static struct semaphore wr_mtx, rd_mtx;
  82. static DECLARE_WAIT_QUEUE_HEAD(GPS_wq);
  83. static int flag;
  84. static volatile int retflag;
  85. static void GPS_event_cb(void);
  86. bool rtc_GPS_low_power_detected(void)
  87. {
  88. static bool first_query = true;
  89. if (first_query) {
  90. first_query = false;
  91. /*return rtc_low_power_detected();*/
  92. return 0;
  93. } else {
  94. return false;
  95. }
  96. }
  97. ssize_t GPS_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
  98. {
  99. int retval = 0;
  100. int written = 0;
  101. down(&wr_mtx);
  102. /* GPS_TRC_FUNC(); */
  103. /*pr_warn("%s: count %d pos %lld\n", __func__, count, *f_pos); */
  104. if (count > 0) {
  105. int copy_size = (count < MTKSTP_BUFFER_SIZE) ? count : MTKSTP_BUFFER_SIZE;
  106. if (copy_from_user(&o_buf[0], &buf[0], copy_size)) {
  107. retval = -EFAULT;
  108. goto out;
  109. }
  110. /* pr_warn("%02x ", val); */
  111. #if GPS_DEBUG_TRACE_GPIO
  112. mtk_wcn_stp_debug_gpio_assert(IDX_GPS_TX, DBG_TIE_LOW);
  113. #endif
  114. written = mtk_wcn_stp_send_data(&o_buf[0], copy_size, GPS_TASK_INDX);
  115. #if GPS_DEBUG_TRACE_GPIO
  116. mtk_wcn_stp_debug_gpio_assert(IDX_GPS_TX, DBG_TIE_HIGH);
  117. #endif
  118. #if GPS_DEBUG_DUMP
  119. {
  120. unsigned char *buf_ptr = &o_buf[0];
  121. int k = 0;
  122. pr_warn("--[GPS-WRITE]--");
  123. for (k = 0; k < 10; k++) {
  124. if (k % 16 == 0)
  125. pr_warn("\n");
  126. pr_warn("0x%02x ", o_buf[k]);
  127. }
  128. pr_warn("\n");
  129. }
  130. #endif
  131. /*
  132. If cannot send successfully, enqueue again
  133. if (written != copy_size) {
  134. // George: FIXME! Move GPS retry handling from app to driver
  135. }
  136. */
  137. if (0 == written) {
  138. retval = -ENOSPC;
  139. /*no windowspace in STP is available,
  140. native process should not call GPS_write with no delay at all */
  141. GPS_ERR_FUNC
  142. ("target packet length:%zd, write success length:%d, retval = %d.\n",
  143. count, written, retval);
  144. } else {
  145. retval = written;
  146. }
  147. } else {
  148. retval = -EFAULT;
  149. GPS_ERR_FUNC("target packet length:%zd is not allowed, retval = %d.\n", count, retval);
  150. }
  151. out:
  152. up(&wr_mtx);
  153. return retval;
  154. }
  155. ssize_t GPS_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
  156. {
  157. long val = 0;
  158. int retval;
  159. down(&rd_mtx);
  160. /* pr_warn("GPS_read(): count %d pos %lld\n", count, *f_pos);*/
  161. if (count > MTKSTP_BUFFER_SIZE)
  162. count = MTKSTP_BUFFER_SIZE;
  163. #if GPS_DEBUG_TRACE_GPIO
  164. mtk_wcn_stp_debug_gpio_assert(IDX_GPS_RX, DBG_TIE_LOW);
  165. #endif
  166. retval = mtk_wcn_stp_receive_data(i_buf, count, GPS_TASK_INDX);
  167. #if GPS_DEBUG_TRACE_GPIO
  168. mtk_wcn_stp_debug_gpio_assert(IDX_GPS_RX, DBG_TIE_HIGH);
  169. #endif
  170. while (retval == 0) {
  171. /* got nothing, wait for STP's signal */
  172. /*wait_event(GPS_wq, flag != 0); *//* George: let signal wake up */
  173. val = wait_event_interruptible(GPS_wq, flag != 0);
  174. flag = 0;
  175. #if GPS_DEBUG_TRACE_GPIO
  176. mtk_wcn_stp_debug_gpio_assert(IDX_GPS_RX, DBG_TIE_LOW);
  177. #endif
  178. retval = mtk_wcn_stp_receive_data(i_buf, count, GPS_TASK_INDX);
  179. #if GPS_DEBUG_TRACE_GPIO
  180. mtk_wcn_stp_debug_gpio_assert(IDX_GPS_RX, DBG_TIE_HIGH);
  181. #endif
  182. /* if we are signaled */
  183. if (val) {
  184. if (-ERESTARTSYS == val)
  185. GPS_DBG_FUNC("signaled by -ERESTARTSYS(%ld)\n ", val);
  186. else
  187. GPS_DBG_FUNC("signaled by %ld\n ", val);
  188. break;
  189. }
  190. }
  191. #if GPS_DEBUG_DUMP
  192. {
  193. unsigned char *buf_ptr = &i_buf[0];
  194. int k = 0;
  195. pr_warn("--[GPS-READ]--");
  196. for (k = 0; k < 10; k++) {
  197. if (k % 16 == 0)
  198. pr_warn("\n");
  199. pr_warn("0x%02x ", i_buf[k]);
  200. }
  201. pr_warn("--\n");
  202. }
  203. #endif
  204. if (retval) {
  205. /* we got something from STP driver */
  206. if (copy_to_user(buf, i_buf, retval)) {
  207. retval = -EFAULT;
  208. goto OUT;
  209. } else {
  210. /* success */
  211. }
  212. } else {
  213. /* we got nothing from STP driver, being signaled */
  214. retval = val;
  215. }
  216. OUT:
  217. up(&rd_mtx);
  218. /* pr_warn("GPS_read(): retval = %d\n", retval);*/
  219. return retval;
  220. }
  221. /* int GPS_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) */
  222. long GPS_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  223. {
  224. int retval = 0;
  225. ENUM_WMTHWVER_TYPE_T hw_ver_sym = WMTHWVER_INVALID;
  226. UINT32 hw_version = 0;
  227. UINT32 fw_version = 0;
  228. pr_warn("GPS_ioctl(): cmd (%d)\n", cmd);
  229. switch (cmd) {
  230. case 0: /* enable/disable STP */
  231. GPS_DBG_FUNC("GPS_ioctl(): disable STP control from GPS dev\n");
  232. retval = -EINVAL;
  233. #if 1
  234. #else
  235. /* George: STP is controlled by WMT only */
  236. mtk_wcn_stp_enable(arg);
  237. #endif
  238. break;
  239. case 1: /* send raw data */
  240. GPS_DBG_FUNC("GPS_ioctl(): disable raw data from GPS dev\n");
  241. retval = -EINVAL;
  242. break;
  243. case COMBO_IOC_GPS_HWVER:
  244. /*get combo hw version */
  245. hw_ver_sym = mtk_wcn_wmt_hwver_get();
  246. GPS_DBG_FUNC("GPS_ioctl(): get hw version = %d, sizeof(hw_ver_sym) = %zd\n",
  247. hw_ver_sym, sizeof(hw_ver_sym));
  248. if (copy_to_user((int __user *)arg, &hw_ver_sym, sizeof(hw_ver_sym)))
  249. retval = -EFAULT;
  250. break;
  251. case COMBO_IOC_GPS_IC_HW_VERSION:
  252. /*get combo hw version from ic, without wmt mapping */
  253. hw_version = mtk_wcn_wmt_ic_info_get(WMTCHIN_HWVER);
  254. GPS_DBG_FUNC("GPS_ioctl(): get hw version = 0x%x\n", hw_version);
  255. if (copy_to_user((int __user *)arg, &hw_version, sizeof(hw_version)))
  256. retval = -EFAULT;
  257. break;
  258. case COMBO_IOC_GPS_IC_FW_VERSION:
  259. /*get combo fw version from ic, without wmt mapping */
  260. fw_version = mtk_wcn_wmt_ic_info_get(WMTCHIN_FWVER);
  261. GPS_DBG_FUNC("GPS_ioctl(): get fw version = 0x%x\n", fw_version);
  262. if (copy_to_user((int __user *)arg, &fw_version, sizeof(fw_version)))
  263. retval = -EFAULT;
  264. break;
  265. case COMBO_IOC_RTC_FLAG:
  266. retval = rtc_GPS_low_power_detected();
  267. GPS_DBG_FUNC("low power flag (%d)\n", retval);
  268. break;
  269. case COMBO_IOC_CO_CLOCK_FLAG:
  270. #if SOC_CO_CLOCK_FLAG
  271. retval = mtk_wcn_wmt_co_clock_flag_get();
  272. #endif
  273. GPS_DBG_FUNC("GPS co_clock_flag (%d)\n", retval);
  274. break;
  275. case COMBO_IOC_D1_EFUSE_GET:
  276. #if defined(CONFIG_ARCH_MT6735)
  277. do {
  278. char *addr = ioremap(0x10206198, 0x4);
  279. retval = *(volatile unsigned int *)addr;
  280. GPS_DBG_FUNC("D1 efuse (0x%x)\n", retval);
  281. iounmap(addr);
  282. } while (0);
  283. #else
  284. GPS_ERR_FUNC("Read Efuse not supported in this platform\n");
  285. #endif
  286. break;
  287. default:
  288. retval = -EFAULT;
  289. GPS_DBG_FUNC("GPS_ioctl(): unknown cmd (%d)\n", cmd);
  290. break;
  291. }
  292. /*OUT:*/
  293. return retval;
  294. }
  295. long GPS_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
  296. {
  297. long ret;
  298. pr_warn("%s: cmd (%d)\n", __func__, cmd);
  299. ret = GPS_unlocked_ioctl(filp, cmd, arg);
  300. pr_warn("%s: cmd (%d)\n", __func__, cmd);
  301. return ret;
  302. }
  303. static void gps_cdev_rst_cb(ENUM_WMTDRV_TYPE_T src,
  304. ENUM_WMTDRV_TYPE_T dst, ENUM_WMTMSG_TYPE_T type, void *buf, unsigned int sz)
  305. {
  306. /*
  307. To handle reset procedure please
  308. */
  309. ENUM_WMTRSTMSG_TYPE_T rst_msg;
  310. GPS_DBG_FUNC("sizeof(ENUM_WMTRSTMSG_TYPE_T) = %zd\n", sizeof(ENUM_WMTRSTMSG_TYPE_T));
  311. if (sz <= sizeof(ENUM_WMTRSTMSG_TYPE_T)) {
  312. memcpy((char *)&rst_msg, (char *)buf, sz);
  313. GPS_DBG_FUNC("src = %d, dst = %d, type = %d, buf = 0x%x sz = %d, max = %d\n", src,
  314. dst, type, rst_msg, sz, WMTRSTMSG_RESET_MAX);
  315. if ((src == WMTDRV_TYPE_WMT) && (dst == WMTDRV_TYPE_GPS)
  316. && (type == WMTMSG_TYPE_RESET)) {
  317. if (rst_msg == WMTRSTMSG_RESET_START) {
  318. GPS_DBG_FUNC("gps restart start!\n");
  319. /*reset_start message handling */
  320. retflag = 1;
  321. } else if ((rst_msg == WMTRSTMSG_RESET_END) || (rst_msg == WMTRSTMSG_RESET_END_FAIL)) {
  322. GPS_DBG_FUNC("gps restart end!\n");
  323. /*reset_end message handling */
  324. retflag = 0;
  325. }
  326. }
  327. } else {
  328. /*message format invalid */
  329. }
  330. }
  331. static int GPS_open(struct inode *inode, struct file *file)
  332. {
  333. pr_debug("%s: major %d minor %d (pid %d)\n", __func__, imajor(inode), iminor(inode), current->pid);
  334. if (current->pid == 1)
  335. return 0;
  336. if (retflag == 1) {
  337. GPS_WARN_FUNC("whole chip resetting...\n");
  338. return -EPERM;
  339. }
  340. #if 1 /* GeorgeKuo: turn on function before check stp ready */
  341. /* turn on BT */
  342. if (MTK_WCN_BOOL_FALSE == mtk_wcn_wmt_func_on(WMTDRV_TYPE_GPS)) {
  343. GPS_WARN_FUNC("WMT turn on GPS fail!\n");
  344. return -ENODEV;
  345. }
  346. mtk_wcn_wmt_msgcb_reg(WMTDRV_TYPE_GPS, gps_cdev_rst_cb);
  347. GPS_DBG_FUNC("WMT turn on GPS OK!\n");
  348. #endif
  349. if (mtk_wcn_stp_is_ready()) {
  350. #if 0
  351. if (MTK_WCN_BOOL_FALSE == mtk_wcn_wmt_func_on(WMTDRV_TYPE_GPS)) {
  352. GPS_WARN_FUNC("WMT turn on GPS fail!\n");
  353. return -ENODEV;
  354. }
  355. GPS_DBG_FUNC("WMT turn on GPS OK!\n");
  356. #endif
  357. mtk_wcn_stp_register_event_cb(GPS_TASK_INDX, GPS_event_cb);
  358. } else {
  359. GPS_ERR_FUNC("STP is not ready, Cannot open GPS Devices\n\r");
  360. /*return error code */
  361. return -ENODEV;
  362. }
  363. #if defined(CONFIG_ARCH_MT6580)
  364. clk_buf_ctrl(CLK_BUF_AUDIO, 1);
  365. #endif
  366. /* init_MUTEX(&wr_mtx); */
  367. sema_init(&wr_mtx, 1);
  368. /* init_MUTEX(&rd_mtx); */
  369. sema_init(&rd_mtx, 1);
  370. return 0;
  371. }
  372. static int GPS_close(struct inode *inode, struct file *file)
  373. {
  374. pr_debug("%s: major %d minor %d (pid %d)\n", __func__, imajor(inode), iminor(inode), current->pid);
  375. if (current->pid == 1)
  376. return 0;
  377. if (retflag == 1) {
  378. GPS_WARN_FUNC("whole chip resetting...\n");
  379. return -EPERM;
  380. }
  381. /*Flush Rx Queue */
  382. mtk_wcn_stp_register_event_cb(GPS_TASK_INDX, 0x0); /* unregister event callback function */
  383. mtk_wcn_wmt_msgcb_unreg(WMTDRV_TYPE_GPS);
  384. if (MTK_WCN_BOOL_FALSE == mtk_wcn_wmt_func_off(WMTDRV_TYPE_GPS)) {
  385. GPS_WARN_FUNC("WMT turn off GPS fail!\n");
  386. return -EIO; /* mostly, native programer does not care this return vlaue,
  387. but we still return error code. */
  388. }
  389. GPS_DBG_FUNC("WMT turn off GPS OK!\n");
  390. #if defined(CONFIG_ARCH_MT6580)
  391. clk_buf_ctrl(CLK_BUF_AUDIO, 0);
  392. #endif
  393. return 0;
  394. }
  395. const struct file_operations GPS_fops = {
  396. .open = GPS_open,
  397. .release = GPS_close,
  398. .read = GPS_read,
  399. .write = GPS_write,
  400. /* .ioctl = GPS_ioctl */
  401. .unlocked_ioctl = GPS_unlocked_ioctl,
  402. .compat_ioctl = GPS_compat_ioctl,
  403. };
  404. void GPS_event_cb(void)
  405. {
  406. /* pr_debug("GPS_event_cb()\n");*/
  407. flag = 1;
  408. wake_up(&GPS_wq);
  409. }
  410. #if WMT_CREATE_NODE_DYNAMIC || REMOVE_MK_NODE
  411. struct class *stpgps_class = NULL;
  412. #endif
  413. static int GPS_init(void)
  414. {
  415. dev_t dev = MKDEV(GPS_major, 0);
  416. int alloc_ret = 0;
  417. int cdev_err = 0;
  418. #if WMT_CREATE_NODE_DYNAMIC || REMOVE_MK_NODE
  419. struct device *stpgps_dev = NULL;
  420. #endif
  421. /*static allocate chrdev */
  422. alloc_ret = register_chrdev_region(dev, 1, GPS_DRIVER_NAME);
  423. if (alloc_ret) {
  424. pr_warn("fail to register chrdev\n");
  425. return alloc_ret;
  426. }
  427. cdev_init(&GPS_cdev, &GPS_fops);
  428. GPS_cdev.owner = THIS_MODULE;
  429. cdev_err = cdev_add(&GPS_cdev, dev, GPS_devs);
  430. if (cdev_err)
  431. goto error;
  432. #if WMT_CREATE_NODE_DYNAMIC || REMOVE_MK_NODE
  433. stpgps_class = class_create(THIS_MODULE, "stpgps");
  434. if (IS_ERR(stpgps_class))
  435. goto error;
  436. stpgps_dev = device_create(stpgps_class, NULL, dev, NULL, "stpgps");
  437. if (IS_ERR(stpgps_dev))
  438. goto error;
  439. #endif
  440. pr_warn("%s driver(major %d) installed.\n", GPS_DRIVER_NAME, GPS_major);
  441. return 0;
  442. error:
  443. #if WMT_CREATE_NODE_DYNAMIC || REMOVE_MK_NODE
  444. if (!IS_ERR(stpgps_dev))
  445. device_destroy(stpgps_class, dev);
  446. if (!IS_ERR(stpgps_class)) {
  447. class_destroy(stpgps_class);
  448. stpgps_class = NULL;
  449. }
  450. #endif
  451. if (cdev_err == 0)
  452. cdev_del(&GPS_cdev);
  453. if (alloc_ret == 0)
  454. unregister_chrdev_region(dev, GPS_devs);
  455. return -1;
  456. }
  457. static void GPS_exit(void)
  458. {
  459. dev_t dev = MKDEV(GPS_major, 0);
  460. #if WMT_CREATE_NODE_DYNAMIC || REMOVE_MK_NODE
  461. device_destroy(stpgps_class, dev);
  462. class_destroy(stpgps_class);
  463. stpgps_class = NULL;
  464. #endif
  465. cdev_del(&GPS_cdev);
  466. unregister_chrdev_region(dev, GPS_devs);
  467. pr_warn("%s driver removed.\n", GPS_DRIVER_NAME);
  468. }
  469. #ifdef MTK_WCN_REMOVE_KERNEL_MODULE
  470. int mtk_wcn_stpgps_drv_init(void)
  471. {
  472. return GPS_init();
  473. }
  474. EXPORT_SYMBOL(mtk_wcn_stpgps_drv_init);
  475. void mtk_wcn_stpgps_drv_exit(void)
  476. {
  477. return GPS_exit();
  478. }
  479. EXPORT_SYMBOL(mtk_wcn_stpgps_drv_exit);
  480. #else
  481. module_init(GPS_init);
  482. module_exit(GPS_exit);
  483. #endif