~ubuntu-branches/ubuntu/utopic/linux-ti-omap/utopic

« back to all changes in this revision

Viewing changes to drivers/usb/host/xhci-hub.c

  • Committer: Bazaar Package Importer
  • Author(s): Amit Kucheria, Amit Kucheria
  • Date: 2010-03-10 02:28:15 UTC
  • Revision ID: james.westby@ubuntu.com-20100310022815-7sd3gwvn5kenaq33
Tags: 2.6.33-500.1
[ Amit Kucheria ]

* Initial release of a 2.6.33-based OMAP kernel
* UBUNTU: [Upstream] Fix omap 1-wire driver compilation
* UBUNTU: ubuntu: AppArmor -- update to mainline 2010-03-04

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * xHCI host controller driver
 
3
 *
 
4
 * Copyright (C) 2008 Intel Corp.
 
5
 *
 
6
 * Author: Sarah Sharp
 
7
 * Some code borrowed from the Linux EHCI driver.
 
8
 *
 
9
 * This program is free software; you can redistribute it and/or modify
 
10
 * it under the terms of the GNU General Public License version 2 as
 
11
 * published by the Free Software Foundation.
 
12
 *
 
13
 * This program is distributed in the hope that it will be useful, but
 
14
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 
15
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 
16
 * 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 Foundation,
 
20
 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
21
 */
 
22
 
 
23
#include <asm/unaligned.h>
 
24
 
 
25
#include "xhci.h"
 
26
 
 
27
static void xhci_hub_descriptor(struct xhci_hcd *xhci,
 
28
                struct usb_hub_descriptor *desc)
 
29
{
 
30
        int ports;
 
31
        u16 temp;
 
32
 
 
33
        ports = HCS_MAX_PORTS(xhci->hcs_params1);
 
34
 
 
35
        /* USB 3.0 hubs have a different descriptor, but we fake this for now */
 
36
        desc->bDescriptorType = 0x29;
 
37
        desc->bPwrOn2PwrGood = 10;      /* xhci section 5.4.9 says 20ms max */
 
38
        desc->bHubContrCurrent = 0;
 
39
 
 
40
        desc->bNbrPorts = ports;
 
41
        temp = 1 + (ports / 8);
 
42
        desc->bDescLength = 7 + 2 * temp;
 
43
 
 
44
        /* Why does core/hcd.h define bitmap?  It's just confusing. */
 
45
        memset(&desc->DeviceRemovable[0], 0, temp);
 
46
        memset(&desc->DeviceRemovable[temp], 0xff, temp);
 
47
 
 
48
        /* Ugh, these should be #defines, FIXME */
 
49
        /* Using table 11-13 in USB 2.0 spec. */
 
50
        temp = 0;
 
51
        /* Bits 1:0 - support port power switching, or power always on */
 
52
        if (HCC_PPC(xhci->hcc_params))
 
53
                temp |= 0x0001;
 
54
        else
 
55
                temp |= 0x0002;
 
56
        /* Bit  2 - root hubs are not part of a compound device */
 
57
        /* Bits 4:3 - individual port over current protection */
 
58
        temp |= 0x0008;
 
59
        /* Bits 6:5 - no TTs in root ports */
 
60
        /* Bit  7 - no port indicators */
 
61
        desc->wHubCharacteristics = (__force __u16) cpu_to_le16(temp);
 
62
}
 
63
 
 
64
static unsigned int xhci_port_speed(unsigned int port_status)
 
65
{
 
66
        if (DEV_LOWSPEED(port_status))
 
67
                return 1 << USB_PORT_FEAT_LOWSPEED;
 
68
        if (DEV_HIGHSPEED(port_status))
 
69
                return 1 << USB_PORT_FEAT_HIGHSPEED;
 
70
        if (DEV_SUPERSPEED(port_status))
 
71
                return 1 << USB_PORT_FEAT_SUPERSPEED;
 
72
        /*
 
73
         * FIXME: Yes, we should check for full speed, but the core uses that as
 
74
         * a default in portspeed() in usb/core/hub.c (which is the only place
 
75
         * USB_PORT_FEAT_*SPEED is used).
 
76
         */
 
77
        return 0;
 
78
}
 
79
 
 
80
/*
 
81
 * These bits are Read Only (RO) and should be saved and written to the
 
82
 * registers: 0, 3, 10:13, 30
 
83
 * connect status, over-current status, port speed, and device removable.
 
84
 * connect status and port speed are also sticky - meaning they're in
 
85
 * the AUX well and they aren't changed by a hot, warm, or cold reset.
 
86
 */
 
87
#define XHCI_PORT_RO    ((1<<0) | (1<<3) | (0xf<<10) | (1<<30))
 
88
/*
 
89
 * These bits are RW; writing a 0 clears the bit, writing a 1 sets the bit:
 
90
 * bits 5:8, 9, 14:15, 25:27
 
91
 * link state, port power, port indicator state, "wake on" enable state
 
92
 */
 
93
#define XHCI_PORT_RWS   ((0xf<<5) | (1<<9) | (0x3<<14) | (0x7<<25))
 
94
/*
 
95
 * These bits are RW; writing a 1 sets the bit, writing a 0 has no effect:
 
96
 * bit 4 (port reset)
 
97
 */
 
98
#define XHCI_PORT_RW1S  ((1<<4))
 
99
/*
 
100
 * These bits are RW; writing a 1 clears the bit, writing a 0 has no effect:
 
101
 * bits 1, 17, 18, 19, 20, 21, 22, 23
 
102
 * port enable/disable, and
 
103
 * change bits: connect, PED, warm port reset changed (reserved zero for USB 2.0 ports),
 
104
 * over-current, reset, link state, and L1 change
 
105
 */
 
106
#define XHCI_PORT_RW1CS ((1<<1) | (0x7f<<17))
 
107
/*
 
108
 * Bit 16 is RW, and writing a '1' to it causes the link state control to be
 
109
 * latched in
 
110
 */
 
111
#define XHCI_PORT_RW    ((1<<16))
 
112
/*
 
113
 * These bits are Reserved Zero (RsvdZ) and zero should be written to them:
 
114
 * bits 2, 24, 28:31
 
115
 */
 
116
#define XHCI_PORT_RZ    ((1<<2) | (1<<24) | (0xf<<28))
 
117
 
 
118
/*
 
119
 * Given a port state, this function returns a value that would result in the
 
120
 * port being in the same state, if the value was written to the port status
 
121
 * control register.
 
122
 * Save Read Only (RO) bits and save read/write bits where
 
123
 * writing a 0 clears the bit and writing a 1 sets the bit (RWS).
 
124
 * For all other types (RW1S, RW1CS, RW, and RZ), writing a '0' has no effect.
 
125
 */
 
126
static u32 xhci_port_state_to_neutral(u32 state)
 
127
{
 
128
        /* Save read-only status and port state */
 
129
        return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS);
 
130
}
 
131
 
 
132
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 
133
                u16 wIndex, char *buf, u16 wLength)
 
134
{
 
135
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
 
136
        int ports;
 
137
        unsigned long flags;
 
138
        u32 temp, status;
 
139
        int retval = 0;
 
140
        u32 __iomem *addr;
 
141
        char *port_change_bit;
 
142
 
 
143
        ports = HCS_MAX_PORTS(xhci->hcs_params1);
 
144
 
 
145
        spin_lock_irqsave(&xhci->lock, flags);
 
146
        switch (typeReq) {
 
147
        case GetHubStatus:
 
148
                /* No power source, over-current reported per port */
 
149
                memset(buf, 0, 4);
 
150
                break;
 
151
        case GetHubDescriptor:
 
152
                xhci_hub_descriptor(xhci, (struct usb_hub_descriptor *) buf);
 
153
                break;
 
154
        case GetPortStatus:
 
155
                if (!wIndex || wIndex > ports)
 
156
                        goto error;
 
157
                wIndex--;
 
158
                status = 0;
 
159
                addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff);
 
160
                temp = xhci_readl(xhci, addr);
 
161
                xhci_dbg(xhci, "get port status, actual port %d status  = 0x%x\n", wIndex, temp);
 
162
 
 
163
                /* wPortChange bits */
 
164
                if (temp & PORT_CSC)
 
165
                        status |= 1 << USB_PORT_FEAT_C_CONNECTION;
 
166
                if (temp & PORT_PEC)
 
167
                        status |= 1 << USB_PORT_FEAT_C_ENABLE;
 
168
                if ((temp & PORT_OCC))
 
169
                        status |= 1 << USB_PORT_FEAT_C_OVER_CURRENT;
 
170
                /*
 
171
                 * FIXME ignoring suspend, reset, and USB 2.1/3.0 specific
 
172
                 * changes
 
173
                 */
 
174
                if (temp & PORT_CONNECT) {
 
175
                        status |= 1 << USB_PORT_FEAT_CONNECTION;
 
176
                        status |= xhci_port_speed(temp);
 
177
                }
 
178
                if (temp & PORT_PE)
 
179
                        status |= 1 << USB_PORT_FEAT_ENABLE;
 
180
                if (temp & PORT_OC)
 
181
                        status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
 
182
                if (temp & PORT_RESET)
 
183
                        status |= 1 << USB_PORT_FEAT_RESET;
 
184
                if (temp & PORT_POWER)
 
185
                        status |= 1 << USB_PORT_FEAT_POWER;
 
186
                xhci_dbg(xhci, "Get port status returned 0x%x\n", status);
 
187
                put_unaligned(cpu_to_le32(status), (__le32 *) buf);
 
188
                break;
 
189
        case SetPortFeature:
 
190
                wIndex &= 0xff;
 
191
                if (!wIndex || wIndex > ports)
 
192
                        goto error;
 
193
                wIndex--;
 
194
                addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff);
 
195
                temp = xhci_readl(xhci, addr);
 
196
                temp = xhci_port_state_to_neutral(temp);
 
197
                switch (wValue) {
 
198
                case USB_PORT_FEAT_POWER:
 
199
                        /*
 
200
                         * Turn on ports, even if there isn't per-port switching.
 
201
                         * HC will report connect events even before this is set.
 
202
                         * However, khubd will ignore the roothub events until
 
203
                         * the roothub is registered.
 
204
                         */
 
205
                        xhci_writel(xhci, temp | PORT_POWER, addr);
 
206
 
 
207
                        temp = xhci_readl(xhci, addr);
 
208
                        xhci_dbg(xhci, "set port power, actual port %d status  = 0x%x\n", wIndex, temp);
 
209
                        break;
 
210
                case USB_PORT_FEAT_RESET:
 
211
                        temp = (temp | PORT_RESET);
 
212
                        xhci_writel(xhci, temp, addr);
 
213
 
 
214
                        temp = xhci_readl(xhci, addr);
 
215
                        xhci_dbg(xhci, "set port reset, actual port %d status  = 0x%x\n", wIndex, temp);
 
216
                        break;
 
217
                default:
 
218
                        goto error;
 
219
                }
 
220
                temp = xhci_readl(xhci, addr); /* unblock any posted writes */
 
221
                break;
 
222
        case ClearPortFeature:
 
223
                if (!wIndex || wIndex > ports)
 
224
                        goto error;
 
225
                wIndex--;
 
226
                addr = &xhci->op_regs->port_status_base +
 
227
                        NUM_PORT_REGS*(wIndex & 0xff);
 
228
                temp = xhci_readl(xhci, addr);
 
229
                temp = xhci_port_state_to_neutral(temp);
 
230
                switch (wValue) {
 
231
                case USB_PORT_FEAT_C_RESET:
 
232
                        status = PORT_RC;
 
233
                        port_change_bit = "reset";
 
234
                        break;
 
235
                case USB_PORT_FEAT_C_CONNECTION:
 
236
                        status = PORT_CSC;
 
237
                        port_change_bit = "connect";
 
238
                        break;
 
239
                case USB_PORT_FEAT_C_OVER_CURRENT:
 
240
                        status = PORT_OCC;
 
241
                        port_change_bit = "over-current";
 
242
                        break;
 
243
                default:
 
244
                        goto error;
 
245
                }
 
246
                /* Change bits are all write 1 to clear */
 
247
                xhci_writel(xhci, temp | status, addr);
 
248
                temp = xhci_readl(xhci, addr);
 
249
                xhci_dbg(xhci, "clear port %s change, actual port %d status  = 0x%x\n",
 
250
                                port_change_bit, wIndex, temp);
 
251
                temp = xhci_readl(xhci, addr); /* unblock any posted writes */
 
252
                break;
 
253
        default:
 
254
error:
 
255
                /* "stall" on error */
 
256
                retval = -EPIPE;
 
257
        }
 
258
        spin_unlock_irqrestore(&xhci->lock, flags);
 
259
        return retval;
 
260
}
 
261
 
 
262
/*
 
263
 * Returns 0 if the status hasn't changed, or the number of bytes in buf.
 
264
 * Ports are 0-indexed from the HCD point of view,
 
265
 * and 1-indexed from the USB core pointer of view.
 
266
 * xHCI instances can have up to 127 ports, so FIXME if you see more than 15.
 
267
 *
 
268
 * Note that the status change bits will be cleared as soon as a port status
 
269
 * change event is generated, so we use the saved status from that event.
 
270
 */
 
271
int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
 
272
{
 
273
        unsigned long flags;
 
274
        u32 temp, status;
 
275
        int i, retval;
 
276
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
 
277
        int ports;
 
278
        u32 __iomem *addr;
 
279
 
 
280
        ports = HCS_MAX_PORTS(xhci->hcs_params1);
 
281
 
 
282
        /* Initial status is no changes */
 
283
        buf[0] = 0;
 
284
        status = 0;
 
285
        if (ports > 7) {
 
286
                buf[1] = 0;
 
287
                retval = 2;
 
288
        } else {
 
289
                retval = 1;
 
290
        }
 
291
 
 
292
        spin_lock_irqsave(&xhci->lock, flags);
 
293
        /* For each port, did anything change?  If so, set that bit in buf. */
 
294
        for (i = 0; i < ports; i++) {
 
295
                addr = &xhci->op_regs->port_status_base +
 
296
                        NUM_PORT_REGS*i;
 
297
                temp = xhci_readl(xhci, addr);
 
298
                if (temp & (PORT_CSC | PORT_PEC | PORT_OCC)) {
 
299
                        if (i < 7)
 
300
                                buf[0] |= 1 << (i + 1);
 
301
                        else
 
302
                                buf[1] |= 1 << (i - 7);
 
303
                        status = 1;
 
304
                }
 
305
        }
 
306
        spin_unlock_irqrestore(&xhci->lock, flags);
 
307
        return status ? retval : 0;
 
308
}