ehci-hub.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:9k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * Copyright (c) 2001-2002 by David Brownell
  3.  * 
  4.  * This program is free software; you can redistribute it and/or modify it
  5.  * under the terms of the GNU General Public License as published by the
  6.  * Free Software Foundation; either version 2 of the License, or (at your
  7.  * option) any later version.
  8.  *
  9.  * This program is distributed in the hope that it will be useful, but
  10.  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  11.  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12.  * for more details.
  13.  *
  14.  * You should have received a copy of the GNU General Public License
  15.  * along with this program; if not, write to the Free Software Foundation,
  16.  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  */
  18. /* this file is part of ehci-hcd.c */
  19. /*-------------------------------------------------------------------------*/
  20. /*
  21.  * EHCI Root Hub ... the nonsharable stuff
  22.  *
  23.  * Registers don't need cpu_to_le32, that happens transparently
  24.  */
  25. /*-------------------------------------------------------------------------*/
  26. static int check_reset_complete (
  27. struct ehci_hcd *ehci,
  28. int index,
  29. int port_status
  30. ) {
  31. if (!(port_status & PORT_CONNECT)) {
  32. ehci->reset_done [index] = 0;
  33. return port_status;
  34. }
  35. /* if reset finished and it's still not enabled -- handoff */
  36. if (!(port_status & PORT_PE)) {
  37. dbg ("%s port %d full speed, give to companion, 0x%x",
  38. ehci->hcd.bus_name, index + 1, port_status);
  39. // what happens if HCS_N_CC(params) == 0 ?
  40. port_status |= PORT_OWNER;
  41. writel (port_status, &ehci->regs->port_status [index]);
  42. } else
  43. dbg ("%s port %d high speed", ehci->hcd.bus_name, index + 1);
  44. return port_status;
  45. }
  46. /*-------------------------------------------------------------------------*/
  47. /* build "status change" packet (one or two bytes) from HC registers */
  48. static int
  49. ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
  50. {
  51. struct ehci_hcd *ehci = hcd_to_ehci (hcd);
  52. u32 temp, status = 0;
  53. int ports, i, retval = 1;
  54. unsigned long flags;
  55. /* init status to no-changes */
  56. buf [0] = 0;
  57. ports = HCS_N_PORTS (ehci->hcs_params);
  58. if (ports > 7) {
  59. buf [1] = 0;
  60. retval++;
  61. }
  62. /* no hub change reports (bit 0) for now (power, ...) */
  63. /* port N changes (bit N)? */
  64. spin_lock_irqsave (&ehci->lock, flags);
  65. for (i = 0; i < ports; i++) {
  66. temp = readl (&ehci->regs->port_status [i]);
  67. if (temp & PORT_OWNER) {
  68. /* don't report this in GetPortStatus */
  69. if (temp & PORT_CSC) {
  70. temp &= ~PORT_CSC;
  71. writel (temp, &ehci->regs->port_status [i]);
  72. }
  73. continue;
  74. }
  75. if (!(temp & PORT_CONNECT))
  76. ehci->reset_done [i] = 0;
  77. if ((temp & (PORT_CSC | PORT_PEC | PORT_OCC)) != 0) {
  78. if (i < 7)
  79.     buf [0] |= 1 << (i + 1);
  80. else
  81.     buf [1] |= 1 << (i - 7);
  82. status = STS_PCD;
  83. }
  84. }
  85. spin_unlock_irqrestore (&ehci->lock, flags);
  86. return status ? retval : 0;
  87. }
  88. /*-------------------------------------------------------------------------*/
  89. static void
  90. ehci_hub_descriptor (
  91. struct ehci_hcd *ehci,
  92. struct usb_hub_descriptor *desc
  93. ) {
  94. int ports = HCS_N_PORTS (ehci->hcs_params);
  95. u16 temp;
  96. desc->bDescriptorType = 0x29;
  97. desc->bPwrOn2PwrGood = 0; /* FIXME: f(system power) */
  98. desc->bHubContrCurrent = 0;
  99. desc->bNbrPorts = ports;
  100. temp = 1 + (ports / 8);
  101. desc->bDescLength = 7 + 2 * temp;
  102. /* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
  103. memset (&desc->bitmap [0], 0, temp);
  104. memset (&desc->bitmap [temp], 0xff, temp);
  105. temp = 0x0008; /* per-port overcurrent reporting */
  106. if (HCS_PPC (ehci->hcs_params))
  107. temp |= 0x0001; /* per-port power control */
  108. if (HCS_INDICATOR (ehci->hcs_params))
  109. temp |= 0x0080; /* per-port indicators (LEDs) */
  110. desc->wHubCharacteristics = cpu_to_le16 (temp);
  111. }
  112. /*-------------------------------------------------------------------------*/
  113. static int ehci_hub_control (
  114. struct usb_hcd *hcd,
  115. u16 typeReq,
  116. u16 wValue,
  117. u16 wIndex,
  118. char *buf,
  119. u16 wLength
  120. ) {
  121. struct ehci_hcd *ehci = hcd_to_ehci (hcd);
  122. int ports = HCS_N_PORTS (ehci->hcs_params);
  123. u32 temp, status;
  124. unsigned long flags;
  125. int retval = 0;
  126. /*
  127.  * FIXME:  support SetPortFeatures USB_PORT_FEAT_INDICATOR.
  128.  * HCS_INDICATOR may say we can change LEDs to off/amber/green.
  129.  * (track current state ourselves) ... blink for diagnostics,
  130.  * power, "this is the one", etc.  EHCI spec supports this.
  131.  */
  132. spin_lock_irqsave (&ehci->lock, flags);
  133. switch (typeReq) {
  134. case ClearHubFeature:
  135. switch (wValue) {
  136. case C_HUB_LOCAL_POWER:
  137. case C_HUB_OVER_CURRENT:
  138. /* no hub-wide feature/status flags */
  139. break;
  140. default:
  141. goto error;
  142. }
  143. break;
  144. case ClearPortFeature:
  145. if (!wIndex || wIndex > ports)
  146. goto error;
  147. wIndex--;
  148. temp = readl (&ehci->regs->port_status [wIndex]);
  149. if (temp & PORT_OWNER)
  150. break;
  151. switch (wValue) {
  152. case USB_PORT_FEAT_ENABLE:
  153. writel (temp & ~PORT_PE,
  154. &ehci->regs->port_status [wIndex]);
  155. break;
  156. case USB_PORT_FEAT_C_ENABLE:
  157. writel (temp | PORT_PEC,
  158. &ehci->regs->port_status [wIndex]);
  159. break;
  160. case USB_PORT_FEAT_SUSPEND:
  161. case USB_PORT_FEAT_C_SUSPEND:
  162. /* ? */
  163. break;
  164. case USB_PORT_FEAT_POWER:
  165. if (HCS_PPC (ehci->hcs_params))
  166. writel (temp & ~PORT_POWER,
  167. &ehci->regs->port_status [wIndex]);
  168. break;
  169. case USB_PORT_FEAT_C_CONNECTION:
  170. writel (temp | PORT_CSC,
  171. &ehci->regs->port_status [wIndex]);
  172. break;
  173. case USB_PORT_FEAT_C_OVER_CURRENT:
  174. writel (temp | PORT_OCC,
  175. &ehci->regs->port_status [wIndex]);
  176. break;
  177. case USB_PORT_FEAT_C_RESET:
  178. /* GetPortStatus clears reset */
  179. break;
  180. default:
  181. goto error;
  182. }
  183. readl (&ehci->regs->command); /* unblock posted write */
  184. break;
  185. case GetHubDescriptor:
  186. ehci_hub_descriptor (ehci, (struct usb_hub_descriptor *)
  187. buf);
  188. break;
  189. case GetHubStatus:
  190. /* no hub-wide feature/status flags */
  191. memset (buf, 0, 4);
  192. //cpu_to_le32s ((u32 *) buf);
  193. break;
  194. case GetPortStatus:
  195. if (!wIndex || wIndex > ports)
  196. goto error;
  197. wIndex--;
  198. status = 0;
  199. temp = readl (&ehci->regs->port_status [wIndex]);
  200. // wPortChange bits
  201. if (temp & PORT_CSC)
  202. status |= 1 << USB_PORT_FEAT_C_CONNECTION;
  203. if (temp & PORT_PEC)
  204. status |= 1 << USB_PORT_FEAT_C_ENABLE;
  205. // USB_PORT_FEAT_C_SUSPEND
  206. if (temp & PORT_OCC)
  207. status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
  208. /* whoever resets must GetPortStatus to complete it!! */
  209. if ((temp & PORT_RESET)
  210. && jiffies > ehci->reset_done [wIndex]) {
  211. status |= 1 << USB_PORT_FEAT_C_RESET;
  212. /* force reset to complete */
  213. writel (temp & ~PORT_RESET,
  214. &ehci->regs->port_status [wIndex]);
  215. do {
  216. temp = readl (
  217. &ehci->regs->port_status [wIndex]);
  218. udelay (10);
  219. } while (temp & PORT_RESET);
  220. /* see what we found out */
  221. temp = check_reset_complete (ehci, wIndex, temp);
  222. }
  223. // don't show wPortStatus if it's owned by a companion hc
  224. if (!(temp & PORT_OWNER)) {
  225. if (temp & PORT_CONNECT) {
  226. status |= 1 << USB_PORT_FEAT_CONNECTION;
  227. status |= 1 << USB_PORT_FEAT_HIGHSPEED;
  228. }
  229. if (temp & PORT_PE)
  230. status |= 1 << USB_PORT_FEAT_ENABLE;
  231. if (temp & PORT_SUSPEND)
  232. status |= 1 << USB_PORT_FEAT_SUSPEND;
  233. if (temp & PORT_OC)
  234. status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
  235. if (temp & PORT_RESET)
  236. status |= 1 << USB_PORT_FEAT_RESET;
  237. if (temp & PORT_POWER)
  238. status |= 1 << USB_PORT_FEAT_POWER;
  239. }
  240. #ifndef EHCI_VERBOSE_DEBUG
  241. if (status & ~0xffff) /* only if wPortChange is interesting */
  242. #endif
  243. dbg_port (hcd, "GetStatus", wIndex + 1, temp);
  244. // we "know" this alignment is good, caller used kmalloc()...
  245. *((u32 *) buf) = cpu_to_le32 (status);
  246. break;
  247. case SetHubFeature:
  248. switch (wValue) {
  249. case C_HUB_LOCAL_POWER:
  250. case C_HUB_OVER_CURRENT:
  251. /* no hub-wide feature/status flags */
  252. break;
  253. default:
  254. goto error;
  255. }
  256. break;
  257. case SetPortFeature:
  258. if (!wIndex || wIndex > ports)
  259. goto error;
  260. wIndex--;
  261. temp = readl (&ehci->regs->port_status [wIndex]);
  262. if (temp & PORT_OWNER)
  263. break;
  264. switch (wValue) {
  265. case USB_PORT_FEAT_SUSPEND:
  266. writel (temp | PORT_SUSPEND,
  267. &ehci->regs->port_status [wIndex]);
  268. break;
  269. case USB_PORT_FEAT_POWER:
  270. if (HCS_PPC (ehci->hcs_params))
  271. writel (temp | PORT_POWER,
  272. &ehci->regs->port_status [wIndex]);
  273. break;
  274. case USB_PORT_FEAT_RESET:
  275. /* line status bits may report this as low speed */
  276. if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
  277. && PORT_USB11 (temp)) {
  278. dbg ("%s port %d low speed, give to companion",
  279. hcd->bus_name, wIndex + 1);
  280. temp |= PORT_OWNER;
  281. } else {
  282. vdbg ("%s port %d reset",
  283. hcd->bus_name, wIndex + 1);
  284. temp |= PORT_RESET;
  285. temp &= ~PORT_PE;
  286. /*
  287.  * caller must wait, then call GetPortStatus
  288.  * usb 2.0 spec says 50 ms resets on root
  289.  */
  290. ehci->reset_done [wIndex] = jiffies
  291.      + ((50 /* msec */ * HZ) / 1000);
  292. }
  293. writel (temp, &ehci->regs->port_status [wIndex]);
  294. break;
  295. default:
  296. goto error;
  297. }
  298. readl (&ehci->regs->command); /* unblock posted writes */
  299. break;
  300. default:
  301. error:
  302. /* "stall" on error */
  303. retval = -EPIPE;
  304. }
  305. spin_unlock_irqrestore (&ehci->lock, flags);
  306. return retval;
  307. }