phy-tahvo.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Tahvo USB transceiver driver
  4. *
  5. * Copyright (C) 2005-2006 Nokia Corporation
  6. *
  7. * Parts copied from isp1301_omap.c.
  8. * Copyright (C) 2004 Texas Instruments
  9. * Copyright (C) 2004 David Brownell
  10. *
  11. * Original driver written by Juha Yrjölä, Tony Lindgren and Timo Teräs.
  12. * Modified for Retu/Tahvo MFD by Aaro Koskinen.
  13. */
  14. #include <linux/io.h>
  15. #include <linux/clk.h>
  16. #include <linux/usb.h>
  17. #include <linux/extcon-provider.h>
  18. #include <linux/kernel.h>
  19. #include <linux/module.h>
  20. #include <linux/usb/otg.h>
  21. #include <linux/mfd/retu.h>
  22. #include <linux/usb/gadget.h>
  23. #include <linux/platform_device.h>
  24. #define DRIVER_NAME "tahvo-usb"
  25. #define TAHVO_REG_IDSR 0x02
  26. #define TAHVO_REG_USBR 0x06
  27. #define USBR_SLAVE_CONTROL (1 << 8)
  28. #define USBR_VPPVIO_SW (1 << 7)
  29. #define USBR_SPEED (1 << 6)
  30. #define USBR_REGOUT (1 << 5)
  31. #define USBR_MASTER_SW2 (1 << 4)
  32. #define USBR_MASTER_SW1 (1 << 3)
  33. #define USBR_SLAVE_SW (1 << 2)
  34. #define USBR_NSUSPEND (1 << 1)
  35. #define USBR_SEMODE (1 << 0)
  36. #define TAHVO_MODE_HOST 0
  37. #define TAHVO_MODE_PERIPHERAL 1
  38. struct tahvo_usb {
  39. struct platform_device *pt_dev;
  40. struct usb_phy phy;
  41. int vbus_state;
  42. struct mutex serialize;
  43. struct clk *ick;
  44. int irq;
  45. int tahvo_mode;
  46. struct extcon_dev *extcon;
  47. };
  48. static const unsigned int tahvo_cable[] = {
  49. EXTCON_USB,
  50. EXTCON_USB_HOST,
  51. EXTCON_NONE,
  52. };
  53. static ssize_t vbus_show(struct device *device,
  54. struct device_attribute *attr, char *buf)
  55. {
  56. struct tahvo_usb *tu = dev_get_drvdata(device);
  57. return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off");
  58. }
  59. static DEVICE_ATTR_RO(vbus);
  60. static void check_vbus_state(struct tahvo_usb *tu)
  61. {
  62. struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
  63. int reg, prev_state;
  64. reg = retu_read(rdev, TAHVO_REG_IDSR);
  65. if (reg & TAHVO_STAT_VBUS) {
  66. switch (tu->phy.otg->state) {
  67. case OTG_STATE_B_IDLE:
  68. /* Enable the gadget driver */
  69. if (tu->phy.otg->gadget)
  70. usb_gadget_vbus_connect(tu->phy.otg->gadget);
  71. tu->phy.otg->state = OTG_STATE_B_PERIPHERAL;
  72. usb_phy_set_event(&tu->phy, USB_EVENT_ENUMERATED);
  73. break;
  74. case OTG_STATE_A_IDLE:
  75. /*
  76. * Session is now valid assuming the USB hub is driving
  77. * Vbus.
  78. */
  79. tu->phy.otg->state = OTG_STATE_A_HOST;
  80. break;
  81. default:
  82. break;
  83. }
  84. dev_info(&tu->pt_dev->dev, "USB cable connected\n");
  85. } else {
  86. switch (tu->phy.otg->state) {
  87. case OTG_STATE_B_PERIPHERAL:
  88. if (tu->phy.otg->gadget)
  89. usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
  90. tu->phy.otg->state = OTG_STATE_B_IDLE;
  91. usb_phy_set_event(&tu->phy, USB_EVENT_NONE);
  92. break;
  93. case OTG_STATE_A_HOST:
  94. tu->phy.otg->state = OTG_STATE_A_IDLE;
  95. break;
  96. default:
  97. break;
  98. }
  99. dev_info(&tu->pt_dev->dev, "USB cable disconnected\n");
  100. }
  101. prev_state = tu->vbus_state;
  102. tu->vbus_state = reg & TAHVO_STAT_VBUS;
  103. if (prev_state != tu->vbus_state) {
  104. extcon_set_state_sync(tu->extcon, EXTCON_USB, tu->vbus_state);
  105. sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state");
  106. }
  107. }
  108. static void tahvo_usb_become_host(struct tahvo_usb *tu)
  109. {
  110. struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
  111. extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, true);
  112. /* Power up the transceiver in USB host mode */
  113. retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND |
  114. USBR_MASTER_SW2 | USBR_MASTER_SW1);
  115. tu->phy.otg->state = OTG_STATE_A_IDLE;
  116. check_vbus_state(tu);
  117. }
  118. static void tahvo_usb_stop_host(struct tahvo_usb *tu)
  119. {
  120. tu->phy.otg->state = OTG_STATE_A_IDLE;
  121. }
  122. static void tahvo_usb_become_peripheral(struct tahvo_usb *tu)
  123. {
  124. struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
  125. extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST, false);
  126. /* Power up transceiver and set it in USB peripheral mode */
  127. retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT |
  128. USBR_NSUSPEND | USBR_SLAVE_SW);
  129. tu->phy.otg->state = OTG_STATE_B_IDLE;
  130. check_vbus_state(tu);
  131. }
  132. static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu)
  133. {
  134. if (tu->phy.otg->gadget)
  135. usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
  136. tu->phy.otg->state = OTG_STATE_B_IDLE;
  137. }
  138. static void tahvo_usb_power_off(struct tahvo_usb *tu)
  139. {
  140. struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
  141. /* Disable gadget controller if any */
  142. if (tu->phy.otg->gadget)
  143. usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
  144. /* Power off transceiver */
  145. retu_write(rdev, TAHVO_REG_USBR, 0);
  146. tu->phy.otg->state = OTG_STATE_UNDEFINED;
  147. }
  148. static int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend)
  149. {
  150. struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, phy);
  151. struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
  152. u16 w;
  153. dev_dbg(&tu->pt_dev->dev, "%s\n", __func__);
  154. w = retu_read(rdev, TAHVO_REG_USBR);
  155. if (suspend)
  156. w &= ~USBR_NSUSPEND;
  157. else
  158. w |= USBR_NSUSPEND;
  159. retu_write(rdev, TAHVO_REG_USBR, w);
  160. return 0;
  161. }
  162. static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
  163. {
  164. struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb,
  165. phy);
  166. dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, host);
  167. mutex_lock(&tu->serialize);
  168. if (host == NULL) {
  169. if (tu->tahvo_mode == TAHVO_MODE_HOST)
  170. tahvo_usb_power_off(tu);
  171. otg->host = NULL;
  172. mutex_unlock(&tu->serialize);
  173. return 0;
  174. }
  175. if (tu->tahvo_mode == TAHVO_MODE_HOST) {
  176. otg->host = NULL;
  177. tahvo_usb_become_host(tu);
  178. }
  179. otg->host = host;
  180. mutex_unlock(&tu->serialize);
  181. return 0;
  182. }
  183. static int tahvo_usb_set_peripheral(struct usb_otg *otg,
  184. struct usb_gadget *gadget)
  185. {
  186. struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb,
  187. phy);
  188. dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, gadget);
  189. mutex_lock(&tu->serialize);
  190. if (!gadget) {
  191. if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
  192. tahvo_usb_power_off(tu);
  193. tu->phy.otg->gadget = NULL;
  194. mutex_unlock(&tu->serialize);
  195. return 0;
  196. }
  197. tu->phy.otg->gadget = gadget;
  198. if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
  199. tahvo_usb_become_peripheral(tu);
  200. mutex_unlock(&tu->serialize);
  201. return 0;
  202. }
  203. static irqreturn_t tahvo_usb_vbus_interrupt(int irq, void *_tu)
  204. {
  205. struct tahvo_usb *tu = _tu;
  206. mutex_lock(&tu->serialize);
  207. check_vbus_state(tu);
  208. mutex_unlock(&tu->serialize);
  209. return IRQ_HANDLED;
  210. }
  211. static ssize_t otg_mode_show(struct device *device,
  212. struct device_attribute *attr, char *buf)
  213. {
  214. struct tahvo_usb *tu = dev_get_drvdata(device);
  215. switch (tu->tahvo_mode) {
  216. case TAHVO_MODE_HOST:
  217. return sprintf(buf, "host\n");
  218. case TAHVO_MODE_PERIPHERAL:
  219. return sprintf(buf, "peripheral\n");
  220. }
  221. return -EINVAL;
  222. }
  223. static ssize_t otg_mode_store(struct device *device,
  224. struct device_attribute *attr,
  225. const char *buf, size_t count)
  226. {
  227. struct tahvo_usb *tu = dev_get_drvdata(device);
  228. int r;
  229. mutex_lock(&tu->serialize);
  230. if (count >= 4 && strncmp(buf, "host", 4) == 0) {
  231. if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
  232. tahvo_usb_stop_peripheral(tu);
  233. tu->tahvo_mode = TAHVO_MODE_HOST;
  234. if (tu->phy.otg->host) {
  235. dev_info(device, "HOST mode: host controller present\n");
  236. tahvo_usb_become_host(tu);
  237. } else {
  238. dev_info(device, "HOST mode: no host controller, powering off\n");
  239. tahvo_usb_power_off(tu);
  240. }
  241. r = strlen(buf);
  242. } else if (count >= 10 && strncmp(buf, "peripheral", 10) == 0) {
  243. if (tu->tahvo_mode == TAHVO_MODE_HOST)
  244. tahvo_usb_stop_host(tu);
  245. tu->tahvo_mode = TAHVO_MODE_PERIPHERAL;
  246. if (tu->phy.otg->gadget) {
  247. dev_info(device, "PERIPHERAL mode: gadget driver present\n");
  248. tahvo_usb_become_peripheral(tu);
  249. } else {
  250. dev_info(device, "PERIPHERAL mode: no gadget driver, powering off\n");
  251. tahvo_usb_power_off(tu);
  252. }
  253. r = strlen(buf);
  254. } else {
  255. r = -EINVAL;
  256. }
  257. mutex_unlock(&tu->serialize);
  258. return r;
  259. }
  260. static DEVICE_ATTR_RW(otg_mode);
  261. static struct attribute *tahvo_attributes[] = {
  262. &dev_attr_vbus.attr,
  263. &dev_attr_otg_mode.attr,
  264. NULL
  265. };
  266. static const struct attribute_group tahvo_attr_group = {
  267. .attrs = tahvo_attributes,
  268. };
  269. static int tahvo_usb_probe(struct platform_device *pdev)
  270. {
  271. struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
  272. struct tahvo_usb *tu;
  273. int ret;
  274. tu = devm_kzalloc(&pdev->dev, sizeof(*tu), GFP_KERNEL);
  275. if (!tu)
  276. return -ENOMEM;
  277. tu->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*tu->phy.otg),
  278. GFP_KERNEL);
  279. if (!tu->phy.otg)
  280. return -ENOMEM;
  281. tu->pt_dev = pdev;
  282. /* Default mode */
  283. #ifdef CONFIG_TAHVO_USB_HOST_BY_DEFAULT
  284. tu->tahvo_mode = TAHVO_MODE_HOST;
  285. #else
  286. tu->tahvo_mode = TAHVO_MODE_PERIPHERAL;
  287. #endif
  288. mutex_init(&tu->serialize);
  289. tu->ick = devm_clk_get(&pdev->dev, "usb_l4_ick");
  290. if (!IS_ERR(tu->ick))
  291. clk_enable(tu->ick);
  292. /*
  293. * Set initial state, so that we generate kevents only on state changes.
  294. */
  295. tu->vbus_state = retu_read(rdev, TAHVO_REG_IDSR) & TAHVO_STAT_VBUS;
  296. tu->extcon = devm_extcon_dev_allocate(&pdev->dev, tahvo_cable);
  297. if (IS_ERR(tu->extcon)) {
  298. dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
  299. ret = PTR_ERR(tu->extcon);
  300. goto err_disable_clk;
  301. }
  302. ret = devm_extcon_dev_register(&pdev->dev, tu->extcon);
  303. if (ret) {
  304. dev_err(&pdev->dev, "could not register extcon device: %d\n",
  305. ret);
  306. goto err_disable_clk;
  307. }
  308. /* Set the initial cable state. */
  309. extcon_set_state_sync(tu->extcon, EXTCON_USB_HOST,
  310. tu->tahvo_mode == TAHVO_MODE_HOST);
  311. extcon_set_state_sync(tu->extcon, EXTCON_USB, tu->vbus_state);
  312. /* Create OTG interface */
  313. tahvo_usb_power_off(tu);
  314. tu->phy.dev = &pdev->dev;
  315. tu->phy.otg->state = OTG_STATE_UNDEFINED;
  316. tu->phy.label = DRIVER_NAME;
  317. tu->phy.set_suspend = tahvo_usb_set_suspend;
  318. tu->phy.otg->usb_phy = &tu->phy;
  319. tu->phy.otg->set_host = tahvo_usb_set_host;
  320. tu->phy.otg->set_peripheral = tahvo_usb_set_peripheral;
  321. ret = usb_add_phy(&tu->phy, USB_PHY_TYPE_USB2);
  322. if (ret < 0) {
  323. dev_err(&pdev->dev, "cannot register USB transceiver: %d\n",
  324. ret);
  325. goto err_disable_clk;
  326. }
  327. dev_set_drvdata(&pdev->dev, tu);
  328. tu->irq = platform_get_irq(pdev, 0);
  329. ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt,
  330. IRQF_ONESHOT,
  331. "tahvo-vbus", tu);
  332. if (ret) {
  333. dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n",
  334. ret);
  335. goto err_remove_phy;
  336. }
  337. /* Attributes */
  338. ret = sysfs_create_group(&pdev->dev.kobj, &tahvo_attr_group);
  339. if (ret) {
  340. dev_err(&pdev->dev, "cannot create sysfs group: %d\n", ret);
  341. goto err_free_irq;
  342. }
  343. return 0;
  344. err_free_irq:
  345. free_irq(tu->irq, tu);
  346. err_remove_phy:
  347. usb_remove_phy(&tu->phy);
  348. err_disable_clk:
  349. if (!IS_ERR(tu->ick))
  350. clk_disable(tu->ick);
  351. return ret;
  352. }
  353. static int tahvo_usb_remove(struct platform_device *pdev)
  354. {
  355. struct tahvo_usb *tu = platform_get_drvdata(pdev);
  356. sysfs_remove_group(&pdev->dev.kobj, &tahvo_attr_group);
  357. free_irq(tu->irq, tu);
  358. usb_remove_phy(&tu->phy);
  359. if (!IS_ERR(tu->ick))
  360. clk_disable(tu->ick);
  361. return 0;
  362. }
  363. static struct platform_driver tahvo_usb_driver = {
  364. .probe = tahvo_usb_probe,
  365. .remove = tahvo_usb_remove,
  366. .driver = {
  367. .name = "tahvo-usb",
  368. },
  369. };
  370. module_platform_driver(tahvo_usb_driver);
  371. MODULE_DESCRIPTION("Tahvo USB transceiver driver");
  372. MODULE_LICENSE("GPL");
  373. MODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs");
  374. MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");