]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/usb/musb/virthub.c
Merge omap-drivers
[linux-2.6-omap-h63xx.git] / drivers / usb / musb / virthub.c
1 /*****************************************************************
2  * Copyright 2005 Mentor Graphics Corporation
3  * Copyright (C) 2005-2006 by Texas Instruments
4  * Copyright (C) 2006 by Nokia Corporation
5  *
6  * This file is part of the Inventra Controller Driver for Linux.
7  *
8  * The Inventra Controller Driver for Linux is free software; you
9  * can redistribute it and/or modify it under the terms of the GNU
10  * General Public License version 2 as published by the Free Software
11  * Foundation.
12  *
13  * The Inventra Controller Driver for Linux is distributed in
14  * the hope that it will be useful, but WITHOUT ANY WARRANTY;
15  * without even the implied warranty of MERCHANTABILITY or
16  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with The Inventra Controller Driver for Linux ; if not,
21  * write to the Free Software Foundation, Inc., 59 Temple Place,
22  * Suite 330, Boston, MA  02111-1307  USA
23  *
24  * ANY DOWNLOAD, USE, REPRODUCTION, MODIFICATION OR DISTRIBUTION
25  * OF THIS DRIVER INDICATES YOUR COMPLETE AND UNCONDITIONAL ACCEPTANCE
26  * OF THOSE TERMS.THIS DRIVER IS PROVIDED "AS IS" AND MENTOR GRAPHICS
27  * MAKES NO WARRANTIES, EXPRESS OR IMPLIED, RELATED TO THIS DRIVER.
28  * MENTOR GRAPHICS SPECIFICALLY DISCLAIMS ALL IMPLIED WARRANTIES
29  * OF MERCHANTABILITY; FITNESS FOR A PARTICULAR PURPOSE AND
30  * NON-INFRINGEMENT.  MENTOR GRAPHICS DOES NOT PROVIDE SUPPORT
31  * SERVICES OR UPDATES FOR THIS DRIVER, EVEN IF YOU ARE A MENTOR
32  * GRAPHICS SUPPORT CUSTOMER.
33  ******************************************************************/
34
35 #include <linux/module.h>
36 #include <linux/kernel.h>
37 #include <linux/sched.h>
38 #include <linux/slab.h>
39 #include <linux/errno.h>
40 #include <linux/init.h>
41 #include <linux/time.h>
42 #include <linux/timer.h>
43
44 #include <asm/unaligned.h>
45
46 #include "musbdefs.h"
47
48
49 static void musb_port_suspend(struct musb *musb, u8 bSuspend)
50 {
51         u8              power;
52         void __iomem    *pBase = musb->pRegs;
53
54         if (!is_host_active(musb))
55                 return;
56
57         /* NOTE:  this doesn't necessarily put PHY into low power mode,
58          * turning off its clock; that's a function of PHY integration and
59          * MGC_M_POWER_ENSUSPEND.  PHY may need a clock (sigh) to detect
60          * SE0 changing to connect (J) or wakeup (K) states.
61          */
62         power = musb_readb(pBase, MGC_O_HDRC_POWER);
63         if (bSuspend) {
64                 power &= ~MGC_M_POWER_RESUME;
65                 power |= MGC_M_POWER_SUSPENDM;
66                 musb_writeb(pBase, MGC_O_HDRC_POWER, power);
67
68                 DBG(3, "Root port suspended, power %02x\n", power);
69
70                 musb->port1_status |= USB_PORT_STAT_SUSPEND;
71                 switch (musb->xceiv.state) {
72                 case OTG_STATE_A_HOST:
73                         musb->xceiv.state = OTG_STATE_A_SUSPEND;
74                         musb->is_active = is_otg_enabled(musb)
75                                         && musb->xceiv.host->b_hnp_enable;
76                         musb_platform_try_idle(musb);
77                         break;
78                 case OTG_STATE_B_HOST:
79                         musb->xceiv.state = OTG_STATE_B_PERIPHERAL;
80                         MUSB_DEV_MODE(musb);
81                         /* REVISIT restore setting of MGC_M_DEVCTL_HR */
82                         break;
83                 default:
84                         DBG(1, "bogus rh suspend? %s\n",
85                                 otg_state_string(musb));
86                 }
87         } else if (power & MGC_M_POWER_SUSPENDM) {
88                 power &= ~MGC_M_POWER_SUSPENDM;
89                 power |= MGC_M_POWER_RESUME;
90                 musb_writeb(pBase, MGC_O_HDRC_POWER, power);
91
92                 DBG(3, "Root port resuming, power %02x\n", power);
93
94                 /* later, GetPortStatus will stop RESUME signaling */
95                 musb->port1_status |= MUSB_PORT_STAT_RESUME;
96                 musb->rh_timer = jiffies + msecs_to_jiffies(20);
97         }
98 }
99
100 static void musb_port_reset(struct musb *musb, u8 bReset)
101 {
102         u8              power;
103         void __iomem    *pBase = musb->pRegs;
104
105 #ifdef CONFIG_USB_MUSB_OTG
106         /* REVISIT this looks wrong for HNP */
107         u8 devctl = musb_readb(pBase, MGC_O_HDRC_DEVCTL);
108
109         if (musb->bDelayPortPowerOff || !(devctl & MGC_M_DEVCTL_HM)) {
110                 return;
111         }
112 #endif
113
114         if (!is_host_active(musb))
115                 return;
116
117         /* NOTE:  caller guarantees it will turn off the reset when
118          * the appropriate amount of time has passed
119          */
120         power = musb_readb(pBase, MGC_O_HDRC_POWER);
121         if (bReset) {
122                 musb->bIgnoreDisconnect = TRUE;
123                 power &= 0xf0;
124                 musb_writeb(pBase, MGC_O_HDRC_POWER,
125                                 power | MGC_M_POWER_RESET);
126
127                 musb->port1_status |= USB_PORT_STAT_RESET;
128                 musb->port1_status &= ~USB_PORT_STAT_ENABLE;
129                 musb->rh_timer = jiffies + msecs_to_jiffies(50);
130         } else {
131                 DBG(4, "root port reset stopped\n");
132                 musb_writeb(pBase, MGC_O_HDRC_POWER,
133                                 power & ~MGC_M_POWER_RESET);
134
135                 musb->bIgnoreDisconnect = FALSE;
136
137                 power = musb_readb(pBase, MGC_O_HDRC_POWER);
138                 if (power & MGC_M_POWER_HSMODE) {
139                         DBG(4, "high-speed device connected\n");
140                         musb->port1_status |= USB_PORT_STAT_HIGH_SPEED;
141                 }
142
143                 musb->port1_status &= ~USB_PORT_STAT_RESET;
144                 musb->port1_status |= USB_PORT_STAT_ENABLE
145                                         | (USB_PORT_STAT_C_RESET << 16)
146                                         | (USB_PORT_STAT_C_ENABLE << 16);
147                 usb_hcd_poll_rh_status(musb_to_hcd(musb));
148
149                 musb->vbuserr_retry = VBUSERR_RETRY_COUNT;
150         }
151 }
152
153 void musb_root_disconnect(struct musb *musb)
154 {
155         musb->port1_status = (1 << USB_PORT_FEAT_POWER)
156                         | (1 << USB_PORT_FEAT_C_CONNECTION);
157
158         usb_hcd_poll_rh_status(musb_to_hcd(musb));
159         musb->is_active = 0;
160
161         switch (musb->xceiv.state) {
162         case OTG_STATE_A_HOST:
163         case OTG_STATE_A_SUSPEND:
164                 musb->xceiv.state = OTG_STATE_A_WAIT_BCON;
165                 break;
166         case OTG_STATE_A_WAIT_VFALL:
167                 musb->xceiv.state = OTG_STATE_B_IDLE;
168                 break;
169         default:
170                 DBG(1, "host disconnect (%s)\n", otg_state_string(musb));
171         }
172 }
173
174
175 /*---------------------------------------------------------------------*/
176
177 int musb_hub_status_data(struct usb_hcd *hcd, char *buf)
178 {
179         struct musb     *musb = hcd_to_musb(hcd);
180         int             retval = 0;
181
182         /* called in_irq() via usb_hcd_poll_rh_status() */
183         if (musb->port1_status & 0xffff0000) {
184                 *buf = 0x02;
185                 retval = 1;
186         }
187         return retval;
188 }
189
190 int musb_hub_control(
191         struct usb_hcd  *hcd,
192         u16             typeReq,
193         u16             wValue,
194         u16             wIndex,
195         char            *buf,
196         u16             wLength)
197 {
198         struct musb     *musb = hcd_to_musb(hcd);
199         u32             temp;
200         int             retval = 0;
201         unsigned long   flags;
202
203         if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
204                 return -ESHUTDOWN;
205
206         /* hub features:  always zero, setting is a NOP
207          * port features: reported, sometimes updated when host is active
208          * no indicators
209          */
210         spin_lock_irqsave(&musb->Lock, flags);
211         switch (typeReq) {
212         case ClearHubFeature:
213         case SetHubFeature:
214                 switch (wValue) {
215                 case C_HUB_OVER_CURRENT:
216                 case C_HUB_LOCAL_POWER:
217                         break;
218                 default:
219                         goto error;
220                 }
221                 break;
222         case ClearPortFeature:
223                 if (wIndex != 1)
224                         goto error;
225
226                 switch (wValue) {
227                 case USB_PORT_FEAT_ENABLE:
228                         break;
229                 case USB_PORT_FEAT_SUSPEND:
230                         musb_port_suspend(musb, FALSE);
231                         break;
232                 case USB_PORT_FEAT_POWER:
233                         if (!(is_otg_enabled(musb) && hcd->self.is_b_host))
234                                 musb_set_vbus(musb, 0);
235                         break;
236                 case USB_PORT_FEAT_C_CONNECTION:
237                 case USB_PORT_FEAT_C_ENABLE:
238                 case USB_PORT_FEAT_C_OVER_CURRENT:
239                 case USB_PORT_FEAT_C_RESET:
240                 case USB_PORT_FEAT_C_SUSPEND:
241                         break;
242                 default:
243                         goto error;
244                 }
245                 DBG(5, "clear feature %d\n", wValue);
246                 musb->port1_status &= ~(1 << wValue);
247                 break;
248         case GetHubDescriptor:
249                 {
250                 struct usb_hub_descriptor *desc = (void *)buf;
251
252                 desc->bDescLength = 9;
253                 desc->bDescriptorType = 0x29;
254                 desc->bNbrPorts = 1;
255                 desc->wHubCharacteristics = __constant_cpu_to_le16(
256                                   0x0001        /* per-port power switching */
257                                 | 0x0010        /* no overcurrent reporting */
258                                 );
259                 desc->bPwrOn2PwrGood = 5;       /* msec/2 */
260                 desc->bHubContrCurrent = 0;
261
262                 /* workaround bogus struct definition */
263                 desc->DeviceRemovable[0] = 0x02;        /* port 1 */
264                 desc->DeviceRemovable[1] = 0xff;
265                 }
266                 break;
267         case GetHubStatus:
268                 temp = 0;
269                 *(__le32 *) buf = cpu_to_le32 (temp);
270                 break;
271         case GetPortStatus:
272                 if (wIndex != 1)
273                         goto error;
274
275                 /* finish RESET signaling? */
276                 if ((musb->port1_status & USB_PORT_STAT_RESET)
277                                 && time_after(jiffies, musb->rh_timer))
278                         musb_port_reset(musb, FALSE);
279
280                 /* finish RESUME signaling? */
281                 if ((musb->port1_status & MUSB_PORT_STAT_RESUME)
282                                 && time_after(jiffies, musb->rh_timer)) {
283                         u8              power;
284
285                         power = musb_readb(musb->pRegs, MGC_O_HDRC_POWER);
286                         power &= ~MGC_M_POWER_RESUME;
287                         DBG(4, "root port resume stopped, power %02x\n",
288                                         power);
289                         musb_writeb(musb->pRegs, MGC_O_HDRC_POWER, power);
290
291                         /* ISSUE:  DaVinci (RTL 1.300) disconnects after
292                          * resume of high speed peripherals (but not full
293                          * speed ones).
294                          */
295
296                         musb->is_active = 1;
297                         musb->port1_status &= ~(USB_PORT_STAT_SUSPEND
298                                         | MUSB_PORT_STAT_RESUME);
299                         musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
300                         usb_hcd_poll_rh_status(musb_to_hcd(musb));
301                         /* NOTE: it might really be A_WAIT_BCON ... */
302                         musb->xceiv.state = OTG_STATE_A_HOST;
303                 }
304
305                 put_unaligned(cpu_to_le32(musb->port1_status & ~MUSB_PORT_STAT_RESUME),
306                                 (__le32 *) buf);
307
308                 /* port change status is more interesting */
309                 DBG(get_unaligned((u16*)(buf+2)) ? 2 : 5, "port status %08x\n",
310                                 musb->port1_status);
311                 break;
312         case SetPortFeature:
313                 if ((wIndex & 0xff) != 1)
314                         goto error;
315
316                 switch (wValue) {
317                 case USB_PORT_FEAT_POWER:
318                         /* NOTE: this controller has a strange state machine
319                          * that involves "requesting sessions" according to
320                          * magic side effects from incompletely-described
321                          * rules about startup...
322                          *
323                          * This call is what really starts the host mode; be
324                          * very careful about side effects if you reorder any
325                          * initialization logic, e.g. for OTG, or change any
326                          * logic relating to VBUS power-up.
327                          */
328                         if (!(is_otg_enabled(musb) && hcd->self.is_b_host))
329                                 musb_start(musb);
330                         break;
331                 case USB_PORT_FEAT_RESET:
332                         musb_port_reset(musb, TRUE);
333                         break;
334                 case USB_PORT_FEAT_SUSPEND:
335                         musb_port_suspend(musb, TRUE);
336                         break;
337                 case USB_PORT_FEAT_TEST:
338                         if (unlikely(is_host_active(musb)))
339                                 goto error;
340
341                         wIndex >>= 8;
342                         switch (wIndex) {
343                         case 1:
344                                 pr_debug("TEST_J\n");
345                                 temp = MGC_M_TEST_J;
346                                 break;
347                         case 2:
348                                 pr_debug("TEST_K\n");
349                                 temp = MGC_M_TEST_K;
350                                 break;
351                         case 3:
352                                 pr_debug("TEST_SE0_NAK\n");
353                                 temp = MGC_M_TEST_SE0_NAK;
354                                 break;
355                         case 4:
356                                 pr_debug("TEST_PACKET\n");
357                                 temp = MGC_M_TEST_PACKET;
358                                 musb_load_testpacket(musb);
359                                 break;
360                         case 5:
361                                 pr_debug("TEST_FORCE_ENABLE\n");
362                                 temp = MGC_M_TEST_FORCE_HOST
363                                         | MGC_M_TEST_FORCE_HS;
364
365                                 /* FIXME and enable a session too */
366                                 break;
367                         default:
368                                 goto error;
369                         }
370                         musb_writeb(musb->pRegs, MGC_O_HDRC_TESTMODE, temp);
371                         break;
372                 default:
373                         goto error;
374                 }
375                 DBG(5, "set feature %d\n", wValue);
376                 musb->port1_status |= 1 << wValue;
377                 break;
378
379         default:
380 error:
381                 /* "protocol stall" on error */
382                 retval = -EPIPE;
383         }
384         spin_unlock_irqrestore(&musb->Lock, flags);
385         return retval;
386 }