c67x00-hcd.c 9.8 KB

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