Subversion Repositories shark

Rev

Details | Last modification | View Log | RSS feed

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