Subversion Repositories shark

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
846 giacomo 1
/*
2
 * OHCI HCD (Host Controller Driver) for USB.
3
 *
4
 * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
5
 * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net>
6
 *
7
 * This file is licenced under GPL
8
 */
9
 
10
/*-------------------------------------------------------------------------*/
11
 
12
/*
13
 * OHCI Root Hub ... the nonsharable stuff
14
 *
15
 * Registers don't need cpu_to_le32, that happens transparently
16
 */
17
 
18
/* AMD-756 (D2 rev) reports corrupt register contents in some cases.
19
 * The erratum (#4) description is incorrect.  AMD's workaround waits
20
 * till some bits (mostly reserved) are clear; ok for all revs.
21
 */
22
#define read_roothub(hc, register, mask) ({ \
23
        u32 temp = readl (&hc->regs->roothub.register); \
24
        if (temp == -1) \
25
                disable (hc); \
26
        else if (hc->flags & OHCI_QUIRK_AMD756) \
27
                while (temp & mask) \
28
                        temp = readl (&hc->regs->roothub.register); \
29
        temp; })
30
 
31
static u32 roothub_a (struct ohci_hcd *hc)
32
        { return read_roothub (hc, a, 0xfc0fe000); }
33
static inline u32 roothub_b (struct ohci_hcd *hc)
34
        { return readl (&hc->regs->roothub.b); }
35
static inline u32 roothub_status (struct ohci_hcd *hc)
36
        { return readl (&hc->regs->roothub.status); }
37
static u32 roothub_portstatus (struct ohci_hcd *hc, int i)
38
        { return read_roothub (hc, portstatus [i], 0xffe0fce0); }
39
 
40
/*-------------------------------------------------------------------------*/
41
 
42
#define dbg_port(hc,label,num,value) \
43
        ohci_dbg (hc, \
44
                "%s roothub.portstatus [%d] " \
45
                "= 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s\n", \
46
                label, num, temp, \
47
                (temp & RH_PS_PRSC) ? " PRSC" : "", \
48
                (temp & RH_PS_OCIC) ? " OCIC" : "", \
49
                (temp & RH_PS_PSSC) ? " PSSC" : "", \
50
                (temp & RH_PS_PESC) ? " PESC" : "", \
51
                (temp & RH_PS_CSC) ? " CSC" : "", \
52
                \
53
                (temp & RH_PS_LSDA) ? " LSDA" : "", \
54
                (temp & RH_PS_PPS) ? " PPS" : "", \
55
                (temp & RH_PS_PRS) ? " PRS" : "", \
56
                (temp & RH_PS_POCI) ? " POCI" : "", \
57
                (temp & RH_PS_PSS) ? " PSS" : "", \
58
                \
59
                (temp & RH_PS_PES) ? " PES" : "", \
60
                (temp & RH_PS_CCS) ? " CCS" : "" \
61
                );
62
 
63
 
64
/*-------------------------------------------------------------------------*/
65
 
66
/* build "status change" packet (one or two bytes) from HC registers */
67
 
68
static int
69
ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
70
{
71
        struct ohci_hcd *ohci = hcd_to_ohci (hcd);
72
        int             ports, i, changed = 0, length = 1;
73
 
74
        ports = roothub_a (ohci) & RH_A_NDP;
75
        if (ports > MAX_ROOT_PORTS) {
76
                if (!HCD_IS_RUNNING(ohci->hcd.state))
77
                        return -ESHUTDOWN;
78
                ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n",
79
                        ports, readl (&ohci->regs->roothub.a) & RH_A_NDP);
80
                /* retry later; "should not happen" */
81
                return 0;
82
        }
83
 
84
        /* init status */
85
        if (roothub_status (ohci) & (RH_HS_LPSC | RH_HS_OCIC))
86
                buf [0] = changed = 1;
87
        else
88
                buf [0] = 0;
89
        if (ports > 7) {
90
                buf [1] = 0;
91
                length++;
92
        }
93
 
94
        /* look at each port */
95
        for (i = 0; i < ports; i++) {
96
                u32     status = roothub_portstatus (ohci, i);
97
 
98
                status &= RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
99
                                | RH_PS_OCIC | RH_PS_PRSC;
100
                if (status) {
101
                        changed = 1;
102
                        if (i < 7)
103
                            buf [0] |= 1 << (i + 1);
104
                        else
105
                            buf [1] |= 1 << (i - 7);
106
                }
107
        }
108
        return changed ? length : 0;
109
}
110
 
111
/*-------------------------------------------------------------------------*/
112
 
113
static void
114
ohci_hub_descriptor (
115
        struct ohci_hcd                 *ohci,
116
        struct usb_hub_descriptor       *desc
117
) {
118
        u32             rh = roothub_a (ohci);
119
        int             ports = rh & RH_A_NDP;
120
        u16             temp;
121
 
122
        desc->bDescriptorType = 0x29;
123
        desc->bPwrOn2PwrGood = (rh & RH_A_POTPGT) >> 24;
124
        desc->bHubContrCurrent = 0;
125
 
126
        desc->bNbrPorts = ports;
127
        temp = 1 + (ports / 8);
128
        desc->bDescLength = 7 + 2 * temp;
129
 
130
        temp = 0;
131
        if (rh & RH_A_PSM)              /* per-port power switching? */
132
            temp |= 0x0001;
133
        if (rh & RH_A_NOCP)             /* no overcurrent reporting? */
134
            temp |= 0x0010;
135
        else if (rh & RH_A_OCPM)        /* per-port overcurrent reporting? */
136
            temp |= 0x0008;
137
        desc->wHubCharacteristics = cpu_to_le16 (temp);
138
 
139
        /* two bitmaps:  ports removable, and usb 1.0 legacy PortPwrCtrlMask */
140
        rh = roothub_b (ohci);
141
        desc->bitmap [0] = rh & RH_B_DR;
142
        if (ports > 7) {
143
                desc->bitmap [1] = (rh & RH_B_DR) >> 8;
144
                desc->bitmap [2] = desc->bitmap [3] = 0xff;
145
        } else
146
                desc->bitmap [1] = 0xff;
147
}
148
 
149
/*-------------------------------------------------------------------------*/
150
 
151
static int ohci_hub_control (
152
        struct usb_hcd  *hcd,
153
        u16             typeReq,
154
        u16             wValue,
155
        u16             wIndex,
156
        char            *buf,
157
        u16             wLength
158
) {
159
        struct ohci_hcd *ohci = hcd_to_ohci (hcd);
160
        int             ports = hcd_to_bus (hcd)->root_hub->maxchild;
161
        u32             temp;
162
        int             retval = 0;
163
 
164
        switch (typeReq) {
165
        case ClearHubFeature:
166
                switch (wValue) {
167
                case C_HUB_OVER_CURRENT:
168
                        writel (RH_HS_OCIC, &ohci->regs->roothub.status);
169
                case C_HUB_LOCAL_POWER:
170
                        break;
171
                default:
172
                        goto error;
173
                }
174
                break;
175
        case ClearPortFeature:
176
                if (!wIndex || wIndex > ports)
177
                        goto error;
178
                wIndex--;
179
 
180
                switch (wValue) {
181
                case USB_PORT_FEAT_ENABLE:
182
                        temp = RH_PS_CCS;
183
                        break;
184
                case USB_PORT_FEAT_C_ENABLE:
185
                        temp = RH_PS_PESC;
186
                        break;
187
                case USB_PORT_FEAT_SUSPEND:
188
                        temp = RH_PS_POCI;
189
                        break;
190
                case USB_PORT_FEAT_C_SUSPEND:
191
                        temp = RH_PS_PSSC;
192
                        break;
193
                case USB_PORT_FEAT_POWER:
194
                        temp = RH_PS_LSDA;
195
                        break;
196
                case USB_PORT_FEAT_C_CONNECTION:
197
                        temp = RH_PS_CSC;
198
                        break;
199
                case USB_PORT_FEAT_C_OVER_CURRENT:
200
                        temp = RH_PS_OCIC;
201
                        break;
202
                case USB_PORT_FEAT_C_RESET:
203
                        temp = RH_PS_PRSC;
204
                        break;
205
                default:
206
                        goto error;
207
                }
208
                writel (temp, &ohci->regs->roothub.portstatus [wIndex]);
209
                // readl (&ohci->regs->roothub.portstatus [wIndex]);
210
                break;
211
        case GetHubDescriptor:
212
                ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf);
213
                break;
214
        case GetHubStatus:
215
                temp = roothub_status (ohci) & ~(RH_HS_CRWE | RH_HS_DRWE);
216
                *(u32 *) buf = cpu_to_le32 (temp);
217
                break;
218
        case GetPortStatus:
219
                if (!wIndex || wIndex > ports)
220
                        goto error;
221
                wIndex--;
222
                temp = roothub_portstatus (ohci, wIndex);
223
                *(u32 *) buf = cpu_to_le32 (temp);
224
 
225
#ifndef OHCI_VERBOSE_DEBUG
226
        if (*(u16*)(buf+2))     /* only if wPortChange is interesting */
227
#endif
228
                dbg_port (ohci, "GetStatus", wIndex + 1, temp);
229
                break;
230
        case SetHubFeature:
231
                switch (wValue) {
232
                case C_HUB_OVER_CURRENT:
233
                        // FIXME:  this can be cleared, yes?
234
                case C_HUB_LOCAL_POWER:
235
                        break;
236
                default:
237
                        goto error;
238
                }
239
                break;
240
        case SetPortFeature:
241
                if (!wIndex || wIndex > ports)
242
                        goto error;
243
                wIndex--;
244
                switch (wValue) {
245
                case USB_PORT_FEAT_SUSPEND:
246
                        writel (RH_PS_PSS,
247
                                &ohci->regs->roothub.portstatus [wIndex]);
248
                        break;
249
                case USB_PORT_FEAT_POWER:
250
                        writel (RH_PS_PPS,
251
                                &ohci->regs->roothub.portstatus [wIndex]);
252
                        break;
253
                case USB_PORT_FEAT_RESET:
254
                        temp = readl (&ohci->regs->roothub.portstatus [wIndex]);
255
                        if (temp & RH_PS_CCS)
256
                                writel (RH_PS_PRS,
257
                                    &ohci->regs->roothub.portstatus [wIndex]);
258
                        break;
259
                default:
260
                        goto error;
261
                }
262
                break;
263
 
264
        default:
265
error:
266
                /* "protocol stall" on error */
267
                retval = -EPIPE;
268
        }
269
        return retval;
270
}
271