Skip to content

Commit

Permalink
Merge tag 'for-usb-linus-2013-03-26' of git://git.kernel.org/pub/scm/…
Browse files Browse the repository at this point in the history
…linux/kernel/git/sarah/xhci into usb-linus

Misc xHCI fixes for 3.9

Hi Greg,

Here's a couple of fixes for the xHCI driver.  Three patches are nothing
major: build warning fix, macro field width fix, and removing some
unnecessary log spam.

The only interesting thing here is Tianyu's two patches to fix the USB
port connection type discovery, for the USB port power off mechanism.
This adds new USB host API, but as discussed, it's necessary to avoid
powering off the wrong USB port.  It's not marked for backport to stable
kernels, since the sysfs mechanism to manually power off a port didn't
go in until 3.9.

I've smoke tested these, including system suspend, USB device suspend,
and rocking out in my cube with a pair of USB headphones.  They look
fine to me.

Hibernate is currently broken on my system, due to some nouveau MMIO
read faults.  I'll report that separately.

Sarah Sharp
  • Loading branch information
Greg Kroah-Hartman committed Mar 26, 2013
2 parents eba0e3c + a83d675 commit d78658d
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 55 deletions.
8 changes: 8 additions & 0 deletions drivers/usb/core/hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -2412,6 +2412,14 @@ int usb_hcd_is_primary_hcd(struct usb_hcd *hcd)
}
EXPORT_SYMBOL_GPL(usb_hcd_is_primary_hcd);

int usb_hcd_find_raw_port_number(struct usb_hcd *hcd, int port1)
{
if (!hcd->driver->find_raw_port_number)
return port1;

return hcd->driver->find_raw_port_number(hcd, port1);
}

static int usb_hcd_request_irqs(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags)
{
Expand Down
8 changes: 7 additions & 1 deletion drivers/usb/core/usb-acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/acpi.h>
#include <linux/pci.h>
#include <linux/usb/hcd.h>
#include <acpi/acpi_bus.h>

#include "usb.h"
Expand Down Expand Up @@ -188,8 +189,13 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
* connected to.
*/
if (!udev->parent) {
*handle = acpi_get_child(DEVICE_ACPI_HANDLE(&udev->dev),
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
int raw_port_num;

raw_port_num = usb_hcd_find_raw_port_number(hcd,
port_num);
*handle = acpi_get_child(DEVICE_ACPI_HANDLE(&udev->dev),
raw_port_num);
if (!*handle)
return -ENODEV;
} else {
Expand Down
36 changes: 8 additions & 28 deletions drivers/usb/host/xhci-mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -1022,44 +1022,24 @@ void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci,
* is attached to (or the roothub port its ancestor hub is attached to). All we
* know is the index of that port under either the USB 2.0 or the USB 3.0
* roothub, but that doesn't give us the real index into the HW port status
* registers. Scan through the xHCI roothub port array, looking for the Nth
* entry of the correct port speed. Return the port number of that entry.
* registers. Call xhci_find_raw_port_number() to get real index.
*/
static u32 xhci_find_real_port_number(struct xhci_hcd *xhci,
struct usb_device *udev)
{
struct usb_device *top_dev;
unsigned int num_similar_speed_ports;
unsigned int faked_port_num;
int i;
struct usb_hcd *hcd;

if (udev->speed == USB_SPEED_SUPER)
hcd = xhci->shared_hcd;
else
hcd = xhci->main_hcd;

for (top_dev = udev; top_dev->parent && top_dev->parent->parent;
top_dev = top_dev->parent)
/* Found device below root hub */;
faked_port_num = top_dev->portnum;
for (i = 0, num_similar_speed_ports = 0;
i < HCS_MAX_PORTS(xhci->hcs_params1); i++) {
u8 port_speed = xhci->port_array[i];

/*
* Skip ports that don't have known speeds, or have duplicate
* Extended Capabilities port speed entries.
*/
if (port_speed == 0 || port_speed == DUPLICATE_ENTRY)
continue;

/*
* USB 3.0 ports are always under a USB 3.0 hub. USB 2.0 and
* 1.1 ports are under the USB 2.0 hub. If the port speed
* matches the device speed, it's a similar speed port.
*/
if ((port_speed == 0x03) == (udev->speed == USB_SPEED_SUPER))
num_similar_speed_ports++;
if (num_similar_speed_ports == faked_port_num)
/* Roothub ports are numbered from 1 to N */
return i+1;
}
return 0;
return xhci_find_raw_port_number(hcd, top_dev->portnum);
}

/* Setup an xHCI virtual device for a Set Address command */
Expand Down
1 change: 1 addition & 0 deletions drivers/usb/host/xhci-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ static const struct hc_driver xhci_pci_hc_driver = {
.set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm,
.enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout,
.disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout,
.find_raw_port_number = xhci_find_raw_port_number,
};

/*-------------------------------------------------------------------------*/
Expand Down
61 changes: 35 additions & 26 deletions drivers/usb/host/xhci-ring.c
Original file line number Diff line number Diff line change
Expand Up @@ -1599,14 +1599,20 @@ static void handle_port_status(struct xhci_hcd *xhci,
max_ports = HCS_MAX_PORTS(xhci->hcs_params1);
if ((port_id <= 0) || (port_id > max_ports)) {
xhci_warn(xhci, "Invalid port id %d\n", port_id);
bogus_port_status = true;
goto cleanup;
inc_deq(xhci, xhci->event_ring);
return;
}

/* Figure out which usb_hcd this port is attached to:
* is it a USB 3.0 port or a USB 2.0/1.1 port?
*/
major_revision = xhci->port_array[port_id - 1];

/* Find the right roothub. */
hcd = xhci_to_hcd(xhci);
if ((major_revision == 0x03) != (hcd->speed == HCD_USB3))
hcd = xhci->shared_hcd;

if (major_revision == 0) {
xhci_warn(xhci, "Event for port %u not in "
"Extended Capabilities, ignoring.\n",
Expand All @@ -1629,10 +1635,6 @@ static void handle_port_status(struct xhci_hcd *xhci,
* into the index into the ports on the correct split roothub, and the
* correct bus_state structure.
*/
/* Find the right roothub. */
hcd = xhci_to_hcd(xhci);
if ((major_revision == 0x03) != (hcd->speed == HCD_USB3))
hcd = xhci->shared_hcd;
bus_state = &xhci->bus_state[hcd_index(hcd)];
if (hcd->speed == HCD_USB3)
port_array = xhci->usb3_ports;
Expand Down Expand Up @@ -2027,8 +2029,8 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
if (event_trb != ep_ring->dequeue &&
event_trb != td->last_trb)
td->urb->actual_length =
td->urb->transfer_buffer_length
- TRB_LEN(le32_to_cpu(event->transfer_len));
td->urb->transfer_buffer_length -
EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
else
td->urb->actual_length = 0;

Expand Down Expand Up @@ -2060,7 +2062,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
/* Maybe the event was for the data stage? */
td->urb->actual_length =
td->urb->transfer_buffer_length -
TRB_LEN(le32_to_cpu(event->transfer_len));
EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
xhci_dbg(xhci, "Waiting for status "
"stage event\n");
return 0;
Expand Down Expand Up @@ -2096,7 +2098,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
/* handle completion code */
switch (trb_comp_code) {
case COMP_SUCCESS:
if (TRB_LEN(le32_to_cpu(event->transfer_len)) == 0) {
if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0) {
frame->status = 0;
break;
}
Expand Down Expand Up @@ -2141,7 +2143,7 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2]));
}
len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) -
TRB_LEN(le32_to_cpu(event->transfer_len));
EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));

if (trb_comp_code != COMP_STOP_INVAL) {
frame->actual_length = len;
Expand Down Expand Up @@ -2199,7 +2201,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
case COMP_SUCCESS:
/* Double check that the HW transferred everything. */
if (event_trb != td->last_trb ||
TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
xhci_warn(xhci, "WARN Successful completion "
"on short TX\n");
if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
Expand Down Expand Up @@ -2227,18 +2229,18 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
"%d bytes untransferred\n",
td->urb->ep->desc.bEndpointAddress,
td->urb->transfer_buffer_length,
TRB_LEN(le32_to_cpu(event->transfer_len)));
EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)));
/* Fast path - was this the last TRB in the TD for this URB? */
if (event_trb == td->last_trb) {
if (TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) {
td->urb->actual_length =
td->urb->transfer_buffer_length -
TRB_LEN(le32_to_cpu(event->transfer_len));
EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
if (td->urb->transfer_buffer_length <
td->urb->actual_length) {
xhci_warn(xhci, "HC gave bad length "
"of %d bytes left\n",
TRB_LEN(le32_to_cpu(event->transfer_len)));
EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)));
td->urb->actual_length = 0;
if (td->urb->transfer_flags & URB_SHORT_NOT_OK)
*status = -EREMOTEIO;
Expand Down Expand Up @@ -2280,7 +2282,7 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
if (trb_comp_code != COMP_STOP_INVAL)
td->urb->actual_length +=
TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) -
TRB_LEN(le32_to_cpu(event->transfer_len));
EVENT_TRB_LEN(le32_to_cpu(event->transfer_len));
}

return finish_td(xhci, td, event_trb, event, ep, status, false);
Expand Down Expand Up @@ -2368,7 +2370,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* transfer type
*/
case COMP_SUCCESS:
if (TRB_LEN(le32_to_cpu(event->transfer_len)) == 0)
if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) == 0)
break;
if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
trb_comp_code = COMP_SHORT_TX;
Expand Down Expand Up @@ -2461,14 +2463,21 @@ static int handle_tx_event(struct xhci_hcd *xhci,
* TD list.
*/
if (list_empty(&ep_ring->td_list)) {
xhci_warn(xhci, "WARN Event TRB for slot %d ep %d "
"with no TDs queued?\n",
TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
ep_index);
xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
(le32_to_cpu(event->flags) &
TRB_TYPE_BITMASK)>>10);
xhci_print_trb_offsets(xhci, (union xhci_trb *) event);
/*
* A stopped endpoint may generate an extra completion
* event if the device was suspended. Don't print
* warnings.
*/
if (!(trb_comp_code == COMP_STOP ||
trb_comp_code == COMP_STOP_INVAL)) {
xhci_warn(xhci, "WARN Event TRB for slot %d ep %d with no TDs queued?\n",
TRB_TO_SLOT_ID(le32_to_cpu(event->flags)),
ep_index);
xhci_dbg(xhci, "Event TRB with TRB type ID %u\n",
(le32_to_cpu(event->flags) &
TRB_TYPE_BITMASK)>>10);
xhci_print_trb_offsets(xhci, (union xhci_trb *) event);
}
if (ep->skip) {
ep->skip = false;
xhci_dbg(xhci, "td_list is empty while skip "
Expand Down
22 changes: 22 additions & 0 deletions drivers/usb/host/xhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -3779,6 +3779,28 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
return 0;
}

/*
* Transfer the port index into real index in the HW port status
* registers. Caculate offset between the port's PORTSC register
* and port status base. Divide the number of per port register
* to get the real index. The raw port number bases 1.
*/
int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
__le32 __iomem *base_addr = &xhci->op_regs->port_status_base;
__le32 __iomem *addr;
int raw_port;

if (hcd->speed != HCD_USB3)
addr = xhci->usb2_ports[port1 - 1];
else
addr = xhci->usb3_ports[port1 - 1];

raw_port = (addr - base_addr)/NUM_PORT_REGS + 1;
return raw_port;
}

#ifdef CONFIG_USB_SUSPEND

/* BESL to HIRD Encoding array for USB2 LPM */
Expand Down
5 changes: 5 additions & 0 deletions drivers/usb/host/xhci.h
Original file line number Diff line number Diff line change
Expand Up @@ -972,6 +972,10 @@ struct xhci_transfer_event {
__le32 flags;
};

/* Transfer event TRB length bit mask */
/* bits 0:23 */
#define EVENT_TRB_LEN(p) ((p) & 0xffffff)

/** Transfer Event bit fields **/
#define TRB_TO_EP_ID(p) (((p) >> 16) & 0x1f)

Expand Down Expand Up @@ -1829,6 +1833,7 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
char *buf, u16 wLength);
int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);

#ifdef CONFIG_PM
int xhci_bus_suspend(struct usb_hcd *hcd);
Expand Down
2 changes: 2 additions & 0 deletions include/linux/usb/hcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ struct hc_driver {
*/
int (*disable_usb3_lpm_timeout)(struct usb_hcd *,
struct usb_device *, enum usb3_link_state state);
int (*find_raw_port_number)(struct usb_hcd *, int);
};

extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
Expand Down Expand Up @@ -396,6 +397,7 @@ extern int usb_hcd_is_primary_hcd(struct usb_hcd *hcd);
extern int usb_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags);
extern void usb_remove_hcd(struct usb_hcd *hcd);
extern int usb_hcd_find_raw_port_number(struct usb_hcd *hcd, int port1);

struct platform_device;
extern void usb_hcd_platform_shutdown(struct platform_device *dev);
Expand Down

0 comments on commit d78658d

Please sign in to comment.