dwc3-sec.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. /*
  2. * Copyright (C) 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/host_notify.h>
  11. #ifdef CONFIG_USB_HOST_NOTIFY
  12. #include <linux/usb_notify_sysfs.h>
  13. #endif
  14. #include <linux/of_gpio.h>
  15. #if defined (CONFIG_CHARGER_BQ24260) || defined (CONFIG_CHARGER_SMB358)
  16. #include <linux/gpio.h>
  17. #include <mach/rpm-regulator-smd.h>
  18. #endif
  19. #ifdef CONFIG_EXTCON
  20. #include <linux/extcon.h>
  21. #include <linux/power_supply.h>
  22. #endif
  23. #if defined(CONFIG_SEC_K_PROJECT)
  24. extern int sec_qcom_usb_rdrv;
  25. #endif
  26. struct dwc3_sec {
  27. struct notifier_block nb;
  28. struct dwc3_msm *dwcm;
  29. };
  30. static struct dwc3_sec sec_noti;
  31. #ifdef CONFIG_USB_HOST_NOTIFY
  32. static int booster_enable;
  33. static int gpio_usb_vbus_msm;
  34. #endif
  35. #ifdef CONFIG_CHARGER_SMB1357
  36. struct delayed_work smb1357_late_power_work;
  37. int smb1357_otg_control(int enable)
  38. {
  39. union power_supply_propval value;
  40. int i, ret = 0;
  41. struct power_supply *psy;
  42. int current_cable_type;
  43. pr_info("%s: enable(%d)\n", __func__, enable);
  44. #ifdef CONFIG_USB_HOST_NOTIFY
  45. booster_enable = enable;
  46. #endif
  47. for (i = 0; i < 10; i++) {
  48. psy = power_supply_get_by_name("battery");
  49. if (psy)
  50. break;
  51. }
  52. if (i == 10) {
  53. pr_err("%s: fail to get battery ps\n", __func__);
  54. schedule_delayed_work(&smb1357_late_power_work, msecs_to_jiffies(5000));
  55. return -1;
  56. }
  57. if (enable == 1)
  58. current_cable_type = POWER_SUPPLY_TYPE_OTG;
  59. else if (enable == 2)
  60. current_cable_type = POWER_SUPPLY_TYPE_LAN_HUB;
  61. else
  62. current_cable_type = POWER_SUPPLY_TYPE_BATTERY;
  63. value.intval = current_cable_type;
  64. ret = psy->set_property(psy, POWER_SUPPLY_PROP_ONLINE, &value);
  65. msleep(500);
  66. if (ret) {
  67. pr_err("%s: fail to set power_suppy ONLINE property(%d)\n",
  68. __func__, ret);
  69. }
  70. return ret;
  71. }
  72. static void smb1357_late_power(struct work_struct *work)
  73. {
  74. struct dwc3_sec *snoti = &sec_noti;
  75. struct dwc3_msm *dwcm;
  76. if (!snoti) {
  77. pr_err("%s: dwc3_otg (snoti) is null\n", __func__);
  78. return;
  79. }
  80. dwcm = snoti->dwcm;
  81. if (!dwcm) {
  82. pr_err("%s: dwc3_otg (dwcm) is null\n", __func__);
  83. return;
  84. }
  85. pr_info("%s, ext_xceiv.id=%d\n", __func__, dwcm->ext_xceiv.id);
  86. #ifdef CONFIG_USB_HOST_NOTIFY
  87. if (dwcm->ext_xceiv.id == DWC3_ID_GROUND) {
  88. if (gpio_usb_vbus_msm > 0) {
  89. if (gpio_get_value(gpio_usb_vbus_msm) == 0)
  90. smb1357_otg_control(booster_enable);
  91. } else {
  92. smb1357_otg_control(booster_enable);
  93. }
  94. }
  95. #endif
  96. }
  97. static void usb_vbus_msm_init(struct dwc3_msm *dwcm, struct usb_phy *phy)
  98. {
  99. INIT_DELAYED_WORK(&smb1357_late_power_work, smb1357_late_power);
  100. }
  101. #endif
  102. #if defined (CONFIG_CHARGER_BQ24260) || defined (CONFIG_CHARGER_SMB358)
  103. struct delayed_work bq24260_late_power_work;
  104. int bq24260_otg_control(int enable)
  105. {
  106. union power_supply_propval value;
  107. int i, ret = 0;
  108. struct power_supply *psy;
  109. int current_cable_type;
  110. pr_info("%s: enable(%d)\n", __func__, enable);
  111. #ifdef CONFIG_USB_HOST_NOTIFY
  112. booster_enable = enable;
  113. #endif
  114. for (i = 0; i < 10; i++) {
  115. psy = power_supply_get_by_name("battery");
  116. if (psy)
  117. break;
  118. }
  119. if (i == 10) {
  120. pr_err("%s: fail to get battery ps\n", __func__);
  121. schedule_delayed_work(&bq24260_late_power_work, msecs_to_jiffies(5000));
  122. return -1;
  123. }
  124. if (enable == 1)
  125. current_cable_type = POWER_SUPPLY_TYPE_OTG;
  126. else if (enable == 2)
  127. current_cable_type = POWER_SUPPLY_TYPE_LAN_HUB;
  128. else
  129. current_cable_type = POWER_SUPPLY_TYPE_BATTERY;
  130. value.intval = current_cable_type;
  131. ret = psy->set_property(psy, POWER_SUPPLY_PROP_ONLINE, &value);
  132. if (ret) {
  133. pr_err("%s: fail to set power_suppy ONLINE property(%d)\n",
  134. __func__, ret);
  135. }
  136. return ret;
  137. }
  138. static void bq24260_late_power(struct work_struct *work)
  139. {
  140. struct dwc3_sec *snoti = &sec_noti;
  141. struct dwc3_msm *dwcm;
  142. if (!snoti) {
  143. pr_err("%s: dwc3_otg (snoti) is null\n", __func__);
  144. return;
  145. }
  146. dwcm = snoti->dwcm;
  147. if (!dwcm) {
  148. pr_err("%s: dwc3_otg (dwcm) is null\n", __func__);
  149. return;
  150. }
  151. pr_info("%s, ext_xceiv.id=%d\n", __func__, dwcm->ext_xceiv.id);
  152. #ifdef CONFIG_USB_HOST_NOTIFY
  153. if (dwcm->ext_xceiv.id == DWC3_ID_GROUND) {
  154. if (gpio_usb_vbus_msm > 0) {
  155. if (gpio_get_value(gpio_usb_vbus_msm) == 0)
  156. bq24260_otg_control(booster_enable);
  157. } else {
  158. bq24260_otg_control(booster_enable);
  159. }
  160. }
  161. #endif
  162. }
  163. struct booster_data sec_booster = {
  164. .name = "bq24260",
  165. .boost = bq24260_otg_control,
  166. };
  167. #ifdef CONFIG_USB_HOST_NOTIFY
  168. #if defined(CONFIG_MACH_VIENNA)
  169. static struct rpm_regulator *s2a_regulator;
  170. static void usb_vbus_s2a_force_pwm(unsigned int en)
  171. {
  172. if (!s2a_regulator) {
  173. pr_err("%s, s2a_regulator is not init\n", __func__);
  174. return;
  175. }
  176. pr_info("%s, s2a_regulator %s mode\n", __func__,
  177. en ? "HPM" : "AUTO");
  178. rpm_regulator_set_mode(s2a_regulator,
  179. en ? RPM_REGULATOR_MODE_HPM : RPM_REGULATOR_MODE_AUTO);
  180. }
  181. #endif
  182. #endif
  183. static void usb_vbus_msm_init(struct dwc3_msm *dwcm, struct usb_phy *phy)
  184. {
  185. #ifdef CONFIG_USB_HOST_NOTIFY
  186. sec_otg_register_booster(&sec_booster);
  187. #endif
  188. INIT_DELAYED_WORK(&bq24260_late_power_work, bq24260_late_power);
  189. }
  190. #endif
  191. #ifdef CONFIG_USB_HOST_NOTIFY
  192. static irqreturn_t msm_usb_vbus_msm_irq(int irq, void *data)
  193. {
  194. struct dwc3_sec *snoti = &sec_noti;
  195. struct dwc3_msm *dwcm;
  196. int enable = gpio_get_value(gpio_usb_vbus_msm);
  197. pr_info("%s usb_vbus_msm=%d\n", __func__, enable);
  198. dwcm = snoti->dwcm;
  199. if (!dwcm) {
  200. pr_err("%s: dwc3_otg (dwcm) is null\n", __func__);
  201. return NOTIFY_BAD;
  202. }
  203. if (dwcm->ext_xceiv.id == DWC3_ID_GROUND && enable == 0 && booster_enable == 1) {
  204. pr_info("%s over current\n", __func__);
  205. sec_otg_notify(HNOTIFY_OVERCURRENT);
  206. return IRQ_HANDLED;
  207. }
  208. sec_otg_notify(enable ?
  209. HNOTIFY_OTG_POWER_ON : HNOTIFY_OTG_POWER_OFF);
  210. #if defined(CONFIG_MACH_VIENNA)
  211. usb_vbus_s2a_force_pwm(enable);
  212. #endif
  213. return IRQ_HANDLED;
  214. }
  215. static int get_vbus_detect_gpio(struct dwc3_msm *dwcm, struct device *dev)
  216. {
  217. int ret;
  218. struct device_node *np = dev->of_node;
  219. gpio_usb_vbus_msm = of_get_named_gpio(np, "qcom,vbus-detect-gpio", 0);
  220. if (gpio_usb_vbus_msm < 0) {
  221. pr_err("%s, cannot get vbus-detect-gpio, ret=%d\n", __func__, gpio_usb_vbus_msm);
  222. return gpio_usb_vbus_msm;
  223. }
  224. else
  225. pr_info("%s, can get vbus-detect-gpio, ret=%d\n", __func__, gpio_usb_vbus_msm);
  226. ret = gpio_tlmm_config(GPIO_CFG(gpio_usb_vbus_msm, 0, GPIO_CFG_INPUT,
  227. GPIO_CFG_NO_PULL, GPIO_CFG_2MA), 1);
  228. if (unlikely(ret)) {
  229. pr_err("%s gpio_usb_vbus_msm gpio_tlmm_config failed. ret=%d\n", __func__, ret);
  230. return ret;
  231. }
  232. pr_info("%s usb_vbus_msm=%d\n", __func__, gpio_get_value(gpio_usb_vbus_msm));
  233. ret = request_threaded_irq(gpio_to_irq(gpio_usb_vbus_msm),
  234. NULL, msm_usb_vbus_msm_irq,
  235. IRQF_TRIGGER_RISING |
  236. IRQF_TRIGGER_FALLING,
  237. "usb_vbus_msm", dwcm);
  238. if (ret)
  239. pr_err("%s request irq failed for usb_vbus_msm\n", __func__);
  240. else
  241. pr_info("%s request irq succeed for usb_vbus_msm\n", __func__);
  242. #if defined(CONFIG_MACH_VIENNA)
  243. s2a_regulator = rpm_regulator_get(NULL, "8941_s2");
  244. if (IS_ERR_OR_NULL(s2a_regulator))
  245. pr_err("%s, could not get rpm regulator err\n", __func__);
  246. usb_vbus_s2a_force_pwm(gpio_get_value(gpio_usb_vbus_msm));
  247. #endif
  248. return ret;
  249. }
  250. #endif
  251. static int sec_otg_ext_notify(struct dwc3_msm *mdwc, int enable)
  252. {
  253. mdwc->ext_xceiv.id = enable ? DWC3_ID_GROUND : DWC3_ID_FLOAT;
  254. if (atomic_read(&mdwc->in_lpm)) {
  255. dev_info(mdwc->dev, "%s: calling resume_work\n", __func__);
  256. dwc3_resume_work(&mdwc->resume_work.work);
  257. } else {
  258. dev_info(mdwc->dev, "%s: notifying xceiv event\n", __func__);
  259. if (mdwc->otg_xceiv)
  260. mdwc->ext_xceiv.notify_ext_events(mdwc->otg_xceiv->otg,
  261. DWC3_EVENT_XCEIV_STATE);
  262. }
  263. return 0;
  264. }
  265. int sec_handle_event(int enable)
  266. {
  267. struct dwc3_sec *snoti = &sec_noti;
  268. struct dwc3_msm *dwcm;
  269. pr_info("%s: event %d\n", __func__, enable);
  270. if (!snoti) {
  271. pr_err("%s: dwc3_otg (snoti) is null\n", __func__);
  272. return NOTIFY_BAD;
  273. }
  274. dwcm = snoti->dwcm;
  275. if (!dwcm) {
  276. pr_err("%s: dwc3_otg (dwcm) is null\n", __func__);
  277. return NOTIFY_BAD;
  278. }
  279. if (enable) {
  280. pr_info("ID clear\n");
  281. sec_otg_ext_notify(dwcm, 1);
  282. } else {
  283. pr_info("ID set\n");
  284. sec_otg_ext_notify(dwcm, 0);
  285. }
  286. return 0;
  287. }
  288. EXPORT_SYMBOL(sec_handle_event);
  289. static int sec_otg_notifications(struct notifier_block *nb,
  290. unsigned long event, void *unused)
  291. {
  292. struct dwc3_sec *snoti = container_of(nb, struct dwc3_sec, nb);
  293. struct dwc3_msm *dwcm;
  294. pr_info("%s: event %lu\n", __func__, event);
  295. if (!snoti) {
  296. pr_err("%s: dwc3_otg (snoti) is null\n", __func__);
  297. return NOTIFY_BAD;
  298. }
  299. dwcm = snoti->dwcm;
  300. if (!dwcm) {
  301. pr_err("%s: dwc3_otg (dwcm) is null\n", __func__);
  302. return NOTIFY_BAD;
  303. }
  304. switch (event) {
  305. case HNOTIFY_NONE: break;
  306. case HNOTIFY_VBUS: break;
  307. case HNOTIFY_ID:
  308. pr_info("ID clear\n");
  309. sec_otg_ext_notify(dwcm, 1);
  310. break;
  311. case HNOTIFY_CHARGER: break;
  312. case HNOTIFY_ENUMERATED: break;
  313. case HNOTIFY_ID_PULL:
  314. pr_info("ID set\n");
  315. sec_otg_ext_notify(dwcm, 0);
  316. break;
  317. case HNOTIFY_OVERCURRENT: break;
  318. case HNOTIFY_OTG_POWER_ON: break;
  319. case HNOTIFY_OTG_POWER_OFF: break;
  320. case HNOTIFY_SMARTDOCK_ON:
  321. pr_info("ID clear\n");
  322. sec_otg_ext_notify(dwcm, 1);
  323. break;
  324. case HNOTIFY_SMARTDOCK_OFF:
  325. pr_info("ID set\n");
  326. sec_otg_ext_notify(dwcm, 0);
  327. break;
  328. case HNOTIFY_AUDIODOCK_ON: break;
  329. case HNOTIFY_AUDIODOCK_OFF: break;
  330. default:
  331. break;
  332. }
  333. return NOTIFY_OK;
  334. }
  335. #ifdef CONFIG_EXTCON
  336. struct sec_cable {
  337. struct work_struct work;
  338. struct notifier_block nb;
  339. struct extcon_specific_cable_nb extcon_nb;
  340. struct extcon_dev *edev;
  341. enum extcon_cable_name cable_type;
  342. int cable_state;
  343. };
  344. static struct sec_cable support_cable_list[] = {
  345. { .cable_type = EXTCON_USB, },
  346. #ifdef CONFIG_USB_HOST_NOTIFY
  347. { .cable_type = EXTCON_USB_HOST, },
  348. { .cable_type = EXTCON_USB_HOST_5V, },
  349. { .cable_type = EXTCON_TA, },
  350. { .cable_type = EXTCON_AUDIODOCK, },
  351. { .cable_type = EXTCON_SMARTDOCK_TA, },
  352. #endif
  353. { .cable_type = EXTCON_SMARTDOCK_USB, },
  354. { .cable_type = EXTCON_JIG_USBON, },
  355. { .cable_type = EXTCON_CHARGE_DOWNSTREAM, },
  356. };
  357. /* USB3.0 Popup option */
  358. #if defined(CONFIG_SEC_K_PROJECT)
  359. extern u8 usb30en;
  360. #endif
  361. extern void set_ncm_ready(bool ready);
  362. static void sec_usb_work(int usb_mode)
  363. {
  364. struct power_supply *psy;
  365. #if defined(CONFIG_SEC_K_PROJECT)
  366. gpio_set_value(sec_qcom_usb_rdrv, usb_mode);
  367. pr_info("%s klte_usb_rdrv_pin = %d, enable=%d\n",
  368. __func__,
  369. sec_qcom_usb_rdrv,
  370. usb_mode);
  371. if(!usb_mode)
  372. /* USB3.0 Popup option */
  373. usb30en = 0;
  374. #endif
  375. if(!usb_mode)
  376. set_ncm_ready(false);
  377. psy = power_supply_get_by_name("dwc-usb");
  378. pr_info("usb: dwc3 power supply set(%d)", usb_mode);
  379. power_supply_set_present(psy, usb_mode);
  380. }
  381. static void sec_cable_event_worker(struct work_struct *work)
  382. {
  383. struct sec_cable *cable =
  384. container_of(work, struct sec_cable, work);
  385. #ifdef CONFIG_USB_HOST_NOTIFY
  386. int usb_block_mode;
  387. #endif
  388. pr_info("sec otg: %s is %s\n",
  389. extcon_cable_name[cable->cable_type],
  390. cable->cable_state ? "attached" : "detached");
  391. #ifdef CONFIG_USB_HOST_NOTIFY
  392. usb_block_mode = check_usb_block_type();
  393. #endif
  394. switch (cable->cable_type) {
  395. case EXTCON_USB:
  396. case EXTCON_SMARTDOCK_USB:
  397. case EXTCON_JIG_USBON:
  398. case EXTCON_CHARGE_DOWNSTREAM:
  399. #ifdef CONFIG_USB_HOST_NOTIFY
  400. if (usb_block_mode == NOTIFY_BLOCK_TYPE_NONE || usb_block_mode == NOTIFY_BLOCK_TYPE_HOST)
  401. #endif
  402. sec_usb_work(cable->cable_state);
  403. #ifdef CONFIG_USB_HOST_NOTIFY
  404. if (cable->cable_state)
  405. sec_otg_notify(HNOTIFY_VBUS);
  406. else
  407. sec_otg_notify(HNOTIFY_NONE);
  408. #endif
  409. break;
  410. #ifdef CONFIG_USB_HOST_NOTIFY
  411. case EXTCON_USB_HOST:
  412. if (cable->cable_state)
  413. sec_otg_notify(HNOTIFY_ID);
  414. else
  415. sec_otg_notify(HNOTIFY_ID_PULL);
  416. break;
  417. case EXTCON_TA: break;
  418. case EXTCON_AUDIODOCK:
  419. if (cable->cable_state)
  420. sec_otg_notify(HNOTIFY_AUDIODOCK_ON);
  421. else
  422. sec_otg_notify(HNOTIFY_AUDIODOCK_OFF);
  423. break;
  424. case EXTCON_SMARTDOCK_TA:
  425. if (cable->cable_state)
  426. sec_otg_notify(HNOTIFY_SMARTDOCK_ON);
  427. else
  428. sec_otg_notify(HNOTIFY_SMARTDOCK_OFF);
  429. break;
  430. case EXTCON_USB_HOST_5V:
  431. if (cable->cable_state)
  432. sec_otg_notify(HNOTIFY_OTG_POWER_ON);
  433. else
  434. sec_otg_notify(HNOTIFY_OTG_POWER_OFF);
  435. break;
  436. #endif
  437. default : break;
  438. }
  439. }
  440. static int sec_cable_notifier(struct notifier_block *nb,
  441. unsigned long stat, void *ptr)
  442. {
  443. struct sec_cable *cable =
  444. container_of(nb, struct sec_cable, nb);
  445. cable->cable_state = stat;
  446. schedule_work(&cable->work);
  447. return NOTIFY_DONE;
  448. }
  449. static int __init sec_otg_init_cable_notify(void)
  450. {
  451. struct sec_cable *cable;
  452. int i;
  453. int ret;
  454. pr_info("%s register extcon notifier for usb and ta\n", __func__);
  455. for (i = 0; i < ARRAY_SIZE(support_cable_list); i++) {
  456. cable = &support_cable_list[i];
  457. INIT_WORK(&cable->work, sec_cable_event_worker);
  458. cable->nb.notifier_call = sec_cable_notifier;
  459. ret = extcon_register_interest(&cable->extcon_nb,
  460. EXTCON_DEV_NAME,
  461. extcon_cable_name[cable->cable_type],
  462. &cable->nb);
  463. if (ret)
  464. pr_err("%s: fail to register extcon notifier(%s, %d)\n",
  465. __func__, extcon_cable_name[cable->cable_type],
  466. ret);
  467. cable->edev = cable->extcon_nb.edev;
  468. if (!cable->edev)
  469. pr_err("%s: fail to get extcon device\n", __func__);
  470. }
  471. return 0;
  472. }
  473. device_initcall_sync(sec_otg_init_cable_notify);
  474. #endif
  475. static int sec_otg_init(struct dwc3_msm *dwcm, struct usb_phy *phy)
  476. {
  477. int ret = 0;
  478. pr_info("%s: register notifier\n", __func__);
  479. sec_noti.nb.notifier_call = sec_otg_notifications;
  480. sec_noti.dwcm = dwcm;
  481. #if defined (CONFIG_CHARGER_BQ24260) || defined (CONFIG_CHARGER_SMB358) || defined (CONFIG_CHARGER_SMB1357)
  482. usb_vbus_msm_init(dwcm, phy);
  483. #endif
  484. #if 0
  485. ret = usb_register_notifier(phy, &sec_noti.nb);
  486. if (ret) {
  487. pr_err("%s: usb_register_notifier failed\n", __func__);
  488. return ret;
  489. }
  490. #endif
  491. return ret;
  492. }