host_notifier.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. /*
  2. * Copyright (C) 2011-2013 Samsung Electronics Co. Ltd.
  3. * Hyuk Kang <hyuk78.kang@samsung.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. */
  10. #include <linux/module.h>
  11. #include <linux/delay.h>
  12. #include <linux/platform_device.h>
  13. #include <linux/usb/otg.h>
  14. #include <linux/host_notify.h>
  15. #ifdef CONFIG_EXTCON
  16. #include <linux/power_supply.h>
  17. #endif
  18. #include <linux/usb_notify_sysfs.h>
  19. #if defined(CONFIG_MFD_MAX77803)
  20. #include <linux/mfd/max77803.h>
  21. #elif defined(CONFIG_MFD_MAX77804K)
  22. #include <linux/mfd/max77804k.h>
  23. #endif
  24. #ifdef CONFIG_USB_SWITCH_FSA9485
  25. #include <linux/i2c/fsa9485.h>
  26. #endif
  27. #ifdef pr_fmt
  28. #undef pr_fmt
  29. #endif
  30. #define pr_fmt(fmt) "notifier %s %d: " fmt, __func__, __LINE__
  31. struct hnotifier_info {
  32. struct usb_phy *phy;
  33. struct host_notify_dev ndev;
  34. struct usb_notify_dev udev;
  35. struct work_struct noti_work;
  36. struct booster_data *booster;
  37. int event;
  38. int prev_event;
  39. int cable;
  40. int block_type;
  41. };
  42. static struct hnotifier_info ninfo = {
  43. .ndev.name = "usb_otg",
  44. };
  45. extern int sec_handle_event(int enable);
  46. static int safe_boost(struct hnotifier_info *pinfo, int enable)
  47. {
  48. if (pinfo && pinfo->booster) {
  49. pinfo->booster->boost(enable);
  50. return 0;
  51. } else {
  52. pr_err("Error! No booster.\n");
  53. return -1;
  54. }
  55. }
  56. static int host_notifier_booster(int enable, struct host_notify_dev *ndev)
  57. {
  58. struct hnotifier_info *pinfo;
  59. int ret = 0;
  60. pr_info("booster %s\n", enable ? "ON" : "OFF");
  61. pinfo = container_of(ndev, struct hnotifier_info, ndev);
  62. safe_boost(pinfo, enable);
  63. return ret;
  64. }
  65. static void hnotifier_work(struct work_struct *w)
  66. {
  67. struct hnotifier_info *pinfo;
  68. int event;
  69. pinfo = container_of(w, struct hnotifier_info, noti_work);
  70. event = pinfo->event;
  71. pr_info("hnotifier_work : event %d\n", pinfo->event);
  72. switch (event) {
  73. case HNOTIFY_NONE:
  74. case HNOTIFY_VBUS: break;
  75. case HNOTIFY_ID:
  76. pr_info("!ID\n");
  77. host_state_notify(&pinfo->ndev, NOTIFY_HOST_ADD);
  78. #if defined(CONFIG_MUIC_MAX77804K_SUPPORT_LANHUB)
  79. safe_boost(pinfo, 2);
  80. #else
  81. safe_boost(pinfo, 1);
  82. #endif
  83. sec_handle_event(1);
  84. break;
  85. case HNOTIFY_ENUMERATED:
  86. case HNOTIFY_CHARGER: break;
  87. case HNOTIFY_ID_PULL:
  88. pr_info("ID\n");
  89. host_state_notify(&pinfo->ndev, NOTIFY_HOST_REMOVE);
  90. sec_handle_event(0);
  91. safe_boost(pinfo, 0);
  92. break;
  93. case HNOTIFY_OVERCURRENT:
  94. pr_info("OVP\n");
  95. #ifdef CONFIG_USB_SWITCH_FSA9485
  96. if(check_mmdock_connect()) /*check mmdock is connected , return 1 - True , 0 - False*/
  97. host_state_notify(&pinfo->ndev, NOTIFY_HOST_NONE);
  98. else
  99. #endif
  100. host_state_notify(&pinfo->ndev, NOTIFY_HOST_OVERCURRENT);
  101. break;
  102. case HNOTIFY_OTG_POWER_ON:
  103. pinfo->ndev.booster = NOTIFY_POWER_ON;
  104. break;
  105. case HNOTIFY_OTG_POWER_OFF:
  106. pinfo->ndev.booster = NOTIFY_POWER_OFF;
  107. break;
  108. case HNOTIFY_SMARTDOCK_ON:
  109. sec_handle_event(1);
  110. break;
  111. case HNOTIFY_SMARTDOCK_OFF:
  112. sec_handle_event(0);
  113. break;
  114. case HNOTIFY_AUDIODOCK_ON:
  115. sec_handle_event(1);
  116. break;
  117. case HNOTIFY_AUDIODOCK_OFF:
  118. sec_handle_event(0);
  119. break;
  120. case HNOTIFY_LANHUB_ON:
  121. host_state_notify(&pinfo->ndev, NOTIFY_HOST_ADD);
  122. sec_handle_event(1);
  123. break;
  124. case HNOTIFY_LANHUB_OFF:
  125. host_state_notify(&pinfo->ndev, NOTIFY_HOST_REMOVE);
  126. sec_handle_event(0);
  127. break;
  128. case HNOTIFY_LANHUBTA_ON:
  129. safe_boost(pinfo, 2);
  130. break;
  131. case HNOTIFY_LANHUBTA_OFF:
  132. safe_boost(pinfo, 1);
  133. break;
  134. default:
  135. break;
  136. }
  137. #if 0
  138. atomic_notifier_call_chain(&pinfo->phy->notifier,
  139. event, pinfo);
  140. #endif
  141. }
  142. int check_usb_block(int event)
  143. {
  144. pr_info("check_usb_block : event = %d, ninfo.block_type = %d\n", event, ninfo.block_type);
  145. ninfo.cable = event;
  146. if (event == HNOTIFY_VBUS || event == HNOTIFY_NONE) {
  147. if (ninfo.block_type== NOTIFY_BLOCK_TYPE_CLIENT || ninfo.block_type == NOTIFY_BLOCK_TYPE_ALL) {
  148. return 1;
  149. }
  150. } else if (event == HNOTIFY_ID || event == HNOTIFY_SMARTDOCK_ON || event == HNOTIFY_AUDIODOCK_ON) {
  151. if (ninfo.block_type== NOTIFY_BLOCK_TYPE_HOST || ninfo.block_type == NOTIFY_BLOCK_TYPE_ALL) {
  152. host_state_notify(&ninfo.ndev, NOTIFY_HOST_BLOCK);
  153. return 1;
  154. }
  155. } else if (event == HNOTIFY_ID_PULL|| event == HNOTIFY_SMARTDOCK_OFF || event == HNOTIFY_AUDIODOCK_OFF) {
  156. if (ninfo.block_type== NOTIFY_BLOCK_TYPE_HOST || ninfo.block_type == NOTIFY_BLOCK_TYPE_ALL) {
  157. return 1;
  158. }
  159. }
  160. return 0;
  161. }
  162. int check_usb_block_type(void)
  163. {
  164. return ninfo.block_type;
  165. }
  166. EXPORT_SYMBOL(check_usb_block_type);
  167. int sec_otg_notify(int event)
  168. {
  169. pr_info("sec_otg_notify : %d\n", event);
  170. ninfo.prev_event = ninfo.event;
  171. ninfo.event = event;
  172. if (check_usb_block(event)) {
  173. pr_info("sec_otg_notify : usb is blocked, ninfo.block_type = %d\n", ninfo.block_type);
  174. return 0;
  175. }
  176. #ifdef CONFIG_EXTCON
  177. if (ninfo.phy) {
  178. pr_info("hnotifier_work : event %d in sec_otg_notify\n", ninfo.event);
  179. switch (event) {
  180. case HNOTIFY_NONE:
  181. case HNOTIFY_VBUS: break;
  182. case HNOTIFY_ID:
  183. pr_info("!ID\n");
  184. host_state_notify(&ninfo.ndev, NOTIFY_HOST_ADD);
  185. #if defined(CONFIG_MUIC_MAX77804K_SUPPORT_LANHUB)
  186. safe_boost(&ninfo, 2);
  187. #else
  188. safe_boost(&ninfo, 1);
  189. #endif
  190. sec_handle_event(1);
  191. break;
  192. case HNOTIFY_ENUMERATED:
  193. case HNOTIFY_CHARGER: break;
  194. case HNOTIFY_ID_PULL:
  195. pr_info("ID\n");
  196. host_state_notify(&ninfo.ndev, NOTIFY_HOST_REMOVE);
  197. sec_handle_event(0);
  198. safe_boost(&ninfo, 0);
  199. break;
  200. case HNOTIFY_OVERCURRENT:
  201. pr_info("OVP\n");
  202. host_state_notify(&ninfo.ndev, NOTIFY_HOST_OVERCURRENT);
  203. break;
  204. case HNOTIFY_OTG_POWER_ON:
  205. ninfo.ndev.booster = NOTIFY_POWER_ON;
  206. break;
  207. case HNOTIFY_OTG_POWER_OFF:
  208. ninfo.ndev.booster = NOTIFY_POWER_OFF;
  209. break;
  210. case HNOTIFY_SMARTDOCK_ON:
  211. sec_handle_event(1);
  212. break;
  213. case HNOTIFY_SMARTDOCK_OFF:
  214. sec_handle_event(0);
  215. break;
  216. case HNOTIFY_AUDIODOCK_ON:
  217. sec_handle_event(1);
  218. break;
  219. case HNOTIFY_AUDIODOCK_OFF:
  220. sec_handle_event(0);
  221. break;
  222. case HNOTIFY_LANHUB_ON:
  223. host_state_notify(&ninfo.ndev, NOTIFY_HOST_ADD);
  224. sec_handle_event(1);
  225. break;
  226. case HNOTIFY_LANHUB_OFF:
  227. host_state_notify(&ninfo.ndev, NOTIFY_HOST_REMOVE);
  228. sec_handle_event(0);
  229. break;
  230. case HNOTIFY_LANHUBTA_ON:
  231. safe_boost(&ninfo, 2);
  232. break;
  233. case HNOTIFY_LANHUBTA_OFF:
  234. safe_boost(&ninfo, 1);
  235. break;
  236. default:
  237. break;
  238. }
  239. }
  240. #else
  241. if (ninfo.phy)
  242. schedule_work(&ninfo.noti_work);
  243. #endif
  244. return 0;
  245. }
  246. EXPORT_SYMBOL(sec_otg_notify);
  247. int sec_otg_register_booster(struct booster_data *booster)
  248. {
  249. int ret = 0;
  250. if (ninfo.booster) {
  251. pr_err("booster %s is already registered.\n", ninfo.booster->name);
  252. return -EBUSY;
  253. }
  254. if (booster && booster->name && booster->boost) {
  255. pr_info("register %s\n", booster->name);
  256. ninfo.booster = booster;
  257. } else {
  258. pr_err("register failed\n");
  259. ret = -ENODATA;
  260. }
  261. return ret;
  262. }
  263. EXPORT_SYMBOL(sec_otg_register_booster);
  264. int sec_get_notification(int ndata)
  265. {
  266. int ret = 0;
  267. switch (ndata) {
  268. case HNOTIFY_EVENT: ret = ninfo.event; break;
  269. case HNOTIFY_MODE: ret = ninfo.ndev.mode; break;
  270. case HNOTIFY_BOOSTER: ret = ninfo.ndev.booster; break;
  271. default:
  272. break;
  273. }
  274. pr_info("ndata %d : %d\n", ndata, ret);
  275. return ret;
  276. }
  277. EXPORT_SYMBOL(sec_get_notification);
  278. static const char *block_string(enum otg_notify_block_type type)
  279. {
  280. switch (type) {
  281. case NOTIFY_BLOCK_TYPE_NONE:
  282. return "block_off";
  283. case NOTIFY_BLOCK_TYPE_HOST:
  284. return "block_host";
  285. case NOTIFY_BLOCK_TYPE_CLIENT:
  286. return "block_client";
  287. case NOTIFY_BLOCK_TYPE_ALL:
  288. return "block_all";
  289. default:
  290. return "undefined";
  291. }
  292. }
  293. #ifdef CONFIG_EXTCON
  294. /* USB3.0 Popup option */
  295. #if defined(CONFIG_SEC_K_PROJECT)
  296. extern u8 usb30en;
  297. extern int sec_qcom_usb_rdrv;
  298. #endif
  299. extern void set_ncm_ready(bool ready);
  300. static void host_notifier_usb_work(int usb_mode)
  301. {
  302. struct power_supply *psy;
  303. #if defined(CONFIG_SEC_K_PROJECT)
  304. gpio_set_value(sec_qcom_usb_rdrv, usb_mode);
  305. pr_info("%s klte_usb_rdrv_pin = %d, enable=%d\n",
  306. __func__,
  307. sec_qcom_usb_rdrv,
  308. usb_mode);
  309. if(!usb_mode)
  310. /* USB3.0 Popup option */
  311. usb30en = 0;
  312. #endif
  313. if(!usb_mode)
  314. set_ncm_ready(false);
  315. psy = power_supply_get_by_name("dwc-usb");
  316. pr_info("usb: dwc3 power supply set(%d)", usb_mode);
  317. power_supply_set_present(psy, usb_mode);
  318. }
  319. #endif
  320. int set_usb_disable(struct usb_notify_dev *udev, int disable)
  321. {
  322. pr_info("%s disable=%s(%d)\n", __func__,
  323. block_string(disable), disable);
  324. if (disable == NOTIFY_BLOCK_TYPE_NONE) {
  325. if (ninfo.cable == HNOTIFY_VBUS) {
  326. if (ninfo.block_type != NOTIFY_BLOCK_TYPE_HOST) {
  327. #ifdef CONFIG_EXTCON
  328. host_notifier_usb_work(1);
  329. #endif
  330. }
  331. } else if (ninfo.cable == HNOTIFY_ID || ninfo.cable == HNOTIFY_SMARTDOCK_ON || ninfo.cable == HNOTIFY_AUDIODOCK_ON) {
  332. if (ninfo.block_type != NOTIFY_BLOCK_TYPE_CLIENT) {
  333. sec_otg_notify(ninfo.cable);
  334. }
  335. }
  336. } else if (disable == NOTIFY_BLOCK_TYPE_CLIENT) {
  337. if (ninfo.cable == HNOTIFY_VBUS) {
  338. #ifdef CONFIG_EXTCON
  339. host_notifier_usb_work(0);
  340. #endif
  341. } else if (ninfo.cable == HNOTIFY_ID || ninfo.cable == HNOTIFY_SMARTDOCK_ON || ninfo.cable == HNOTIFY_AUDIODOCK_ON) {
  342. if (ninfo.block_type == NOTIFY_BLOCK_TYPE_HOST || ninfo.block_type == NOTIFY_BLOCK_TYPE_ALL) {
  343. sec_otg_notify(ninfo.cable);
  344. }
  345. }
  346. } else if (disable == NOTIFY_BLOCK_TYPE_HOST) {
  347. if (ninfo.cable == HNOTIFY_VBUS) {
  348. if (ninfo.block_type == NOTIFY_BLOCK_TYPE_CLIENT || ninfo.block_type == NOTIFY_BLOCK_TYPE_ALL) {
  349. #ifdef CONFIG_EXTCON
  350. host_notifier_usb_work(1);
  351. #endif
  352. }
  353. } else if (ninfo.cable == HNOTIFY_ID || ninfo.cable == HNOTIFY_SMARTDOCK_ON || ninfo.cable == HNOTIFY_AUDIODOCK_ON) {
  354. if (ninfo.cable == HNOTIFY_ID) {
  355. sec_otg_notify(HNOTIFY_ID_PULL);
  356. ninfo.cable = HNOTIFY_ID;
  357. } else if (ninfo.cable == HNOTIFY_SMARTDOCK_ON) {
  358. sec_otg_notify(HNOTIFY_SMARTDOCK_OFF);
  359. ninfo.cable = HNOTIFY_SMARTDOCK_ON;
  360. } else if (ninfo.cable == HNOTIFY_AUDIODOCK_ON) {
  361. sec_otg_notify(HNOTIFY_AUDIODOCK_OFF);
  362. ninfo.cable = HNOTIFY_AUDIODOCK_ON;
  363. }
  364. }
  365. } else if (disable == NOTIFY_BLOCK_TYPE_ALL) {
  366. if (ninfo.cable == HNOTIFY_VBUS) {
  367. #ifdef CONFIG_EXTCON
  368. host_notifier_usb_work(0);
  369. #endif
  370. } else if (ninfo.cable == HNOTIFY_ID || ninfo.cable == HNOTIFY_SMARTDOCK_ON || ninfo.cable == HNOTIFY_AUDIODOCK_ON) {
  371. if (ninfo.cable == HNOTIFY_ID) {
  372. sec_otg_notify(HNOTIFY_ID_PULL);
  373. ninfo.cable = HNOTIFY_ID;
  374. } else if (ninfo.cable == HNOTIFY_SMARTDOCK_ON) {
  375. sec_otg_notify(HNOTIFY_SMARTDOCK_OFF);
  376. ninfo.cable = HNOTIFY_SMARTDOCK_ON;
  377. } else if (ninfo.cable == HNOTIFY_AUDIODOCK_ON) {
  378. sec_otg_notify(HNOTIFY_AUDIODOCK_OFF);
  379. ninfo.cable = HNOTIFY_AUDIODOCK_ON;
  380. }
  381. }
  382. }
  383. ninfo.block_type = disable;
  384. return 0;
  385. }
  386. static int host_notifier_probe(struct platform_device *pdev)
  387. {
  388. int ret = 0;
  389. dev_info(&pdev->dev, "notifier_probe\n");
  390. INIT_WORK(&ninfo.noti_work, hnotifier_work);
  391. ninfo.ndev.set_booster = host_notifier_booster;
  392. ninfo.phy = usb_get_transceiver();
  393. if(!ninfo.phy){
  394. return -ENODEV;
  395. }
  396. ATOMIC_INIT_NOTIFIER_HEAD(&ninfo.phy->notifier);
  397. ret = host_notify_dev_register(&ninfo.ndev);
  398. if (ret < 0) {
  399. dev_err(&pdev->dev, "Failed to host_notify_dev_register\n");
  400. return ret;
  401. }
  402. ninfo.udev.name = "usb_control";
  403. ninfo.udev.set_disable = set_usb_disable;
  404. ret = usb_notify_dev_register(&ninfo.udev);
  405. if (ret < 0) {
  406. pr_err("usb_notify_dev_register is failed\n");
  407. return ret;
  408. }
  409. ninfo.cable = HNOTIFY_NONE;
  410. ninfo.block_type = NOTIFY_BLOCK_TYPE_NONE;
  411. return 0;
  412. }
  413. static int host_notifier_remove(struct platform_device *pdev)
  414. {
  415. cancel_work_sync(&ninfo.noti_work);
  416. host_notify_dev_unregister(&ninfo.ndev);
  417. return 0;
  418. }
  419. static struct of_device_id host_notifier_of_match[] = {
  420. { .compatible = "sec,host-notifier", },
  421. {}
  422. };
  423. static struct platform_driver host_notifier_driver = {
  424. .probe = host_notifier_probe,
  425. .remove = host_notifier_remove,
  426. .driver = {
  427. .name = "host_notifier",
  428. .owner = THIS_MODULE,
  429. .of_match_table = host_notifier_of_match,
  430. },
  431. };
  432. module_platform_driver(host_notifier_driver);
  433. MODULE_AUTHOR("Hyuk Kang <hyuk78.kang@samsung.com>");
  434. MODULE_DESCRIPTION("USB Host notifier");
  435. MODULE_LICENSE("GPL");