c67x00-hcd.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * c67x00-hcd.c: Cypress C67X00 USB Host Controller Driver
  4. *
  5. * Copyright (C) 2006-2008 Barco N.V.
  6. * Derived from the Cypress cy7c67200/300 ezusb linux driver and
  7. * based on multiple host controller drivers inside the linux kernel.
  8. */
  9. #include <linux/device.h>
  10. #include <linux/platform_device.h>
  11. #include <linux/usb.h>
  12. #include "c67x00.h"
  13. #include "c67x00-hcd.h"
  14. /* --------------------------------------------------------------------------
  15. * Root Hub Support
  16. */
  17. static __u8 c67x00_hub_des[] = {
  18. 0x09, /* __u8 bLength; */
  19. USB_DT_HUB, /* __u8 bDescriptorType; Hub-descriptor */
  20. 0x02, /* __u8 bNbrPorts; */
  21. 0x00, /* __u16 wHubCharacteristics; */
  22. 0x00, /* (per-port OC, no power switching) */
  23. 0x32, /* __u8 bPwrOn2pwrGood; 2ms */
  24. 0x00, /* __u8 bHubContrCurrent; 0 mA */
  25. 0x00, /* __u8 DeviceRemovable; ** 7 Ports max ** */
  26. 0xff, /* __u8 PortPwrCtrlMask; ** 7 ports max ** */
  27. };
  28. static void c67x00_hub_reset_host_port(struct c67x00_sie *sie, int port)
  29. {
  30. struct c67x00_hcd *c67x00 = sie->private_data;
  31. unsigned long flags;
  32. c67x00_ll_husb_reset(sie, port);
  33. spin_lock_irqsave(&c67x00->lock, flags);
  34. c67x00_ll_husb_reset_port(sie, port);
  35. spin_unlock_irqrestore(&c67x00->lock, flags);
  36. c67x00_ll_set_husb_eot(sie->dev, DEFAULT_EOT);
  37. }
  38. static int c67x00_hub_status_data(struct usb_hcd *hcd, char *buf)
  39. {
  40. struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
  41. struct c67x00_sie *sie = c67x00->sie;
  42. u16 status;
  43. int i;
  44. *buf = 0;
  45. status = c67x00_ll_usb_get_status(sie);
  46. for (i = 0; i < C67X00_PORTS; i++)
  47. if (status & PORT_CONNECT_CHANGE(i))
  48. *buf |= (1 << i);
  49. /* bit 0 denotes hub change, b1..n port change */
  50. *buf <<= 1;
  51. return !!*buf;
  52. }
  53. static int c67x00_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
  54. u16 wIndex, char *buf, u16 wLength)
  55. {
  56. struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
  57. struct c67x00_sie *sie = c67x00->sie;
  58. u16 status, usb_status;
  59. int len = 0;
  60. unsigned int port = wIndex-1;
  61. u16 wPortChange, wPortStatus;
  62. switch (typeReq) {
  63. case GetHubStatus:
  64. *(__le32 *) buf = cpu_to_le32(0);
  65. len = 4; /* hub power */
  66. break;
  67. case GetPortStatus:
  68. if (wIndex > C67X00_PORTS)
  69. return -EPIPE;
  70. status = c67x00_ll_usb_get_status(sie);
  71. usb_status = c67x00_ll_get_usb_ctl(sie);
  72. wPortChange = 0;
  73. if (status & PORT_CONNECT_CHANGE(port))
  74. wPortChange |= USB_PORT_STAT_C_CONNECTION;
  75. wPortStatus = USB_PORT_STAT_POWER;
  76. if (!(status & PORT_SE0_STATUS(port)))
  77. wPortStatus |= USB_PORT_STAT_CONNECTION;
  78. if (usb_status & LOW_SPEED_PORT(port)) {
  79. wPortStatus |= USB_PORT_STAT_LOW_SPEED;
  80. c67x00->low_speed_ports |= (1 << port);
  81. } else
  82. c67x00->low_speed_ports &= ~(1 << port);
  83. if (usb_status & SOF_EOP_EN(port))
  84. wPortStatus |= USB_PORT_STAT_ENABLE;
  85. *(__le16 *) buf = cpu_to_le16(wPortStatus);
  86. *(__le16 *) (buf + 2) = cpu_to_le16(wPortChange);
  87. len = 4;
  88. break;
  89. case SetHubFeature: /* We don't implement these */
  90. case ClearHubFeature:
  91. switch (wValue) {
  92. case C_HUB_OVER_CURRENT:
  93. case C_HUB_LOCAL_POWER:
  94. len = 0;
  95. break;
  96. default:
  97. return -EPIPE;
  98. }
  99. break;
  100. case SetPortFeature:
  101. if (wIndex > C67X00_PORTS)
  102. return -EPIPE;
  103. switch (wValue) {
  104. case USB_PORT_FEAT_SUSPEND:
  105. dev_dbg(c67x00_hcd_dev(c67x00),
  106. "SetPortFeature %d (SUSPEND)\n", port);
  107. len = 0;
  108. break;
  109. case USB_PORT_FEAT_RESET:
  110. c67x00_hub_reset_host_port(sie, port);
  111. len = 0;
  112. break;
  113. case USB_PORT_FEAT_POWER:
  114. /* Power always enabled */
  115. len = 0;
  116. break;
  117. default:
  118. dev_dbg(c67x00_hcd_dev(c67x00),
  119. "%s: SetPortFeature %d (0x%04x) Error!\n",
  120. __func__, port, wValue);
  121. return -EPIPE;
  122. }
  123. break;
  124. case ClearPortFeature:
  125. if (wIndex > C67X00_PORTS)
  126. return -EPIPE;
  127. switch (wValue) {
  128. case USB_PORT_FEAT_ENABLE:
  129. /* Reset the port so that the c67x00 also notices the
  130. * disconnect */
  131. c67x00_hub_reset_host_port(sie, port);
  132. len = 0;
  133. break;
  134. case USB_PORT_FEAT_C_ENABLE:
  135. dev_dbg(c67x00_hcd_dev(c67x00),
  136. "ClearPortFeature (%d): C_ENABLE\n", port);
  137. len = 0;
  138. break;
  139. case USB_PORT_FEAT_SUSPEND:
  140. dev_dbg(c67x00_hcd_dev(c67x00),
  141. "ClearPortFeature (%d): SUSPEND\n", port);
  142. len = 0;
  143. break;
  144. case USB_PORT_FEAT_C_SUSPEND:
  145. dev_dbg(c67x00_hcd_dev(c67x00),
  146. "ClearPortFeature (%d): C_SUSPEND\n", port);
  147. len = 0;
  148. break;
  149. case USB_PORT_FEAT_POWER:
  150. dev_dbg(c67x00_hcd_dev(c67x00),
  151. "ClearPortFeature (%d): POWER\n", port);
  152. return -EPIPE;
  153. case USB_PORT_FEAT_C_CONNECTION:
  154. c67x00_ll_usb_clear_status(sie,
  155. PORT_CONNECT_CHANGE(port));
  156. len = 0;
  157. break;
  158. case USB_PORT_FEAT_C_OVER_CURRENT:
  159. dev_dbg(c67x00_hcd_dev(c67x00),
  160. "ClearPortFeature (%d): OVER_CURRENT\n", port);
  161. len = 0;
  162. break;
  163. case USB_PORT_FEAT_C_RESET:
  164. dev_dbg(c67x00_hcd_dev(c67x00),
  165. "ClearPortFeature (%d): C_RESET\n", port);
  166. len = 0;
  167. break;
  168. default:
  169. dev_dbg(c67x00_hcd_dev(c67x00),
  170. "%s: ClearPortFeature %d (0x%04x) Error!\n",
  171. __func__, port, wValue);
  172. return -EPIPE;
  173. }
  174. break;
  175. case GetHubDescriptor:
  176. len = min_t(unsigned int, sizeof(c67x00_hub_des), wLength);
  177. memcpy(buf, c67x00_hub_des, len);
  178. break;
  179. default:
  180. dev_dbg(c67x00_hcd_dev(c67x00), "%s: unknown\n", __func__);
  181. return -EPIPE;
  182. }
  183. return 0;
  184. }
  185. /* ---------------------------------------------------------------------
  186. * Main part of host controller driver
  187. */
  188. /*
  189. * c67x00_hcd_irq
  190. *
  191. * This function is called from the interrupt handler in c67x00-drv.c
  192. */
  193. static void c67x00_hcd_irq(struct c67x00_sie *sie, u16 int_status, u16 msg)
  194. {
  195. struct c67x00_hcd *c67x00 = sie->private_data;
  196. struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
  197. /* Handle sie message flags */
  198. if (msg) {
  199. if (msg & HUSB_TDListDone)
  200. c67x00_sched_kick(c67x00);
  201. else
  202. dev_warn(c67x00_hcd_dev(c67x00),
  203. "Unknown SIE msg flag(s): 0x%04x\n", msg);
  204. }
  205. if (unlikely(hcd->state == HC_STATE_HALT))
  206. return;
  207. if (!HCD_HW_ACCESSIBLE(hcd))
  208. return;
  209. /* Handle Start of frame events */
  210. if (int_status & SOFEOP_FLG(sie->sie_num)) {
  211. c67x00_ll_usb_clear_status(sie, SOF_EOP_IRQ_FLG);
  212. c67x00_sched_kick(c67x00);
  213. }
  214. }
  215. /*
  216. * c67x00_hcd_start: Host controller start hook
  217. */
  218. static int c67x00_hcd_start(struct usb_hcd *hcd)
  219. {
  220. hcd->uses_new_polling = 1;
  221. hcd->state = HC_STATE_RUNNING;
  222. set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
  223. return 0;
  224. }
  225. /*
  226. * c67x00_hcd_stop: Host controller stop hook
  227. */
  228. static void c67x00_hcd_stop(struct usb_hcd *hcd)
  229. {
  230. /* Nothing to do */
  231. }
  232. static int c67x00_hcd_get_frame(struct usb_hcd *hcd)
  233. {
  234. struct c67x00_hcd *c67x00 = hcd_to_c67x00_hcd(hcd);
  235. u16 temp_val;
  236. dev_dbg(c67x00_hcd_dev(c67x00), "%s\n", __func__);
  237. temp_val = c67x00_ll_husb_get_frame(c67x00->sie);
  238. temp_val &= HOST_FRAME_MASK;
  239. return temp_val ? (temp_val - 1) : HOST_FRAME_MASK;
  240. }
  241. static const struct hc_driver c67x00_hc_driver = {
  242. .description = "c67x00-hcd",
  243. .product_desc = "Cypress C67X00 Host Controller",
  244. .hcd_priv_size = sizeof(struct c67x00_hcd),
  245. .flags = HCD_USB11 | HCD_MEMORY,
  246. /*
  247. * basic lifecycle operations
  248. */
  249. .start = c67x00_hcd_start,
  250. .stop = c67x00_hcd_stop,
  251. /*
  252. * managing i/o requests and associated device resources
  253. */
  254. .urb_enqueue = c67x00_urb_enqueue,
  255. .urb_dequeue = c67x00_urb_dequeue,
  256. .endpoint_disable = c67x00_endpoint_disable,
  257. /*
  258. * scheduling support
  259. */
  260. .get_frame_number = c67x00_hcd_get_frame,
  261. /*
  262. * root hub support
  263. */
  264. .hub_status_data = c67x00_hub_status_data,
  265. .hub_control = c67x00_hub_control,
  266. };
  267. /* ---------------------------------------------------------------------
  268. * Setup/Teardown routines
  269. */
  270. int c67x00_hcd_probe(struct c67x00_sie *sie)
  271. {
  272. struct c67x00_hcd *c67x00;
  273. struct usb_hcd *hcd;
  274. unsigned long flags;
  275. int retval;
  276. if (usb_disabled())
  277. return -ENODEV;
  278. hcd = usb_create_hcd(&c67x00_hc_driver, sie_dev(sie), "c67x00_sie");
  279. if (!hcd) {
  280. retval = -ENOMEM;
  281. goto err0;
  282. }
  283. c67x00 = hcd_to_c67x00_hcd(hcd);
  284. spin_lock_init(&c67x00->lock);
  285. c67x00->sie = sie;
  286. INIT_LIST_HEAD(&c67x00->list[PIPE_ISOCHRONOUS]);
  287. INIT_LIST_HEAD(&c67x00->list[PIPE_INTERRUPT]);
  288. INIT_LIST_HEAD(&c67x00->list[PIPE_CONTROL]);
  289. INIT_LIST_HEAD(&c67x00->list[PIPE_BULK]);
  290. c67x00->urb_count = 0;
  291. INIT_LIST_HEAD(&c67x00->td_list);
  292. c67x00->td_base_addr = CY_HCD_BUF_ADDR + SIE_TD_OFFSET(sie->sie_num);
  293. c67x00->buf_base_addr = CY_HCD_BUF_ADDR + SIE_BUF_OFFSET(sie->sie_num);
  294. c67x00->max_frame_bw = MAX_FRAME_BW_STD;
  295. c67x00_ll_husb_init_host_port(sie);
  296. init_completion(&c67x00->endpoint_disable);
  297. retval = c67x00_sched_start_scheduler(c67x00);
  298. if (retval)
  299. goto err1;
  300. retval = usb_add_hcd(hcd, 0, 0);
  301. if (retval) {
  302. dev_dbg(sie_dev(sie), "%s: usb_add_hcd returned %d\n",
  303. __func__, retval);
  304. goto err2;
  305. }
  306. device_wakeup_enable(hcd->self.controller);
  307. spin_lock_irqsave(&sie->lock, flags);
  308. sie->private_data = c67x00;
  309. sie->irq = c67x00_hcd_irq;
  310. spin_unlock_irqrestore(&sie->lock, flags);
  311. return retval;
  312. err2:
  313. c67x00_sched_stop_scheduler(c67x00);
  314. err1:
  315. usb_put_hcd(hcd);
  316. err0:
  317. return retval;
  318. }
  319. /* may be called with controller, bus, and devices active */
  320. void c67x00_hcd_remove(struct c67x00_sie *sie)
  321. {
  322. struct c67x00_hcd *c67x00 = sie->private_data;
  323. struct usb_hcd *hcd = c67x00_hcd_to_hcd(c67x00);
  324. c67x00_sched_stop_scheduler(c67x00);
  325. usb_remove_hcd(hcd);
  326. usb_put_hcd(hcd);
  327. }