Skip to content

Commit

Permalink
Merge tag 'for-usb-next-2013-06-06' of git://git.kernel.org/pub/scm/l…
Browse files Browse the repository at this point in the history
…inux/kernel/git/sarah/xhci into usb-next

Sarah writes:

xHCI: USB 2.0 Link PM and misc cleanup patches

Hi Greg,

Here's six patches to be queued for 3.11.

The first four add support for a new type of host hardware-managed USB
2.0 Link Power Management.  Hosts with BESL support, including Intel
Haswell ULT systems, will now be able to have USB 2.0 devices go into
the lower power link state (L1) in between packets.  These patches have
been tested on Haswell ULT platforms with USB 2.0 webcams that support
Link PM.

The other two patches are clean up.  One from Julius clarifies the xHCI
endpoint context debugging to make it consistent with standard endpoint
addresses, instead of xHCI endpoint context indexes.  The one from Alex
changes the xHCI driver to be consistent about passing a void pointer to
the xHCI IRQ handler.

Sarah Sharp
  • Loading branch information
Greg Kroah-Hartman committed Jun 6, 2013
2 parents 3a57aa8 + 17f3486 commit 1c83d94
Show file tree
Hide file tree
Showing 10 changed files with 356 additions and 85 deletions.
27 changes: 27 additions & 0 deletions Documentation/ABI/testing/sysfs-bus-usb
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,30 @@ Description:
This attribute is to expose these information to user space.
The file will read "hotplug", "wired" and "not used" if the
information is available, and "unknown" otherwise.

What: /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout
Date: May 2013
Contact: Mathias Nyman <mathias.nyman@linux.intel.com>
Description:
USB 2.0 devices may support hardware link power management (LPM)
L1 sleep state. The usb2_lpm_l1_timeout attribute allows
tuning the timeout for L1 inactivity timer (LPM timer), e.g.
needed inactivity time before host requests the device to go to L1 sleep.
Useful for power management tuning.
Supported values are 0 - 65535 microseconds.

What: /sys/bus/usb/devices/.../power/usb2_lpm_besl
Date: May 2013
Contact: Mathias Nyman <mathias.nyman@linux.intel.com>
Description:
USB 2.0 devices that support hardware link power management (LPM)
L1 sleep state now use a best effort service latency value (BESL) to
indicate the best effort to resumption of service to the device after the
initiation of the resume event.
If the device does not have a preferred besl value then the host can select
one instead. This usb2_lpm_besl attribute allows to tune the host selected besl
value in order to tune power saving and service latency.

Supported values are 0 - 15.
More information on how besl values map to microseconds can be found in
USB 2.0 ECN Errata for Link Power Management, section 4.10)
54 changes: 54 additions & 0 deletions drivers/usb/core/sysfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -497,8 +497,62 @@ set_usb2_hardware_lpm(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(usb2_hardware_lpm, S_IRUGO | S_IWUSR, show_usb2_hardware_lpm,
set_usb2_hardware_lpm);

static ssize_t
show_usb2_lpm_l1_timeout(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct usb_device *udev = to_usb_device(dev);
return sprintf(buf, "%d\n", udev->l1_params.timeout);
}

static ssize_t
set_usb2_lpm_l1_timeout(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
u16 timeout;

if (kstrtou16(buf, 0, &timeout))
return -EINVAL;

udev->l1_params.timeout = timeout;

return count;
}

static DEVICE_ATTR(usb2_lpm_l1_timeout, S_IRUGO | S_IWUSR,
show_usb2_lpm_l1_timeout, set_usb2_lpm_l1_timeout);

static ssize_t
show_usb2_lpm_besl(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct usb_device *udev = to_usb_device(dev);
return sprintf(buf, "%d\n", udev->l1_params.besl);
}

static ssize_t
set_usb2_lpm_besl(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
u8 besl;

if (kstrtou8(buf, 0, &besl) || besl > 15)
return -EINVAL;

udev->l1_params.besl = besl;

return count;
}

static DEVICE_ATTR(usb2_lpm_besl, S_IRUGO | S_IWUSR,
show_usb2_lpm_besl, set_usb2_lpm_besl);

static struct attribute *usb2_hardware_lpm_attr[] = {
&dev_attr_usb2_hardware_lpm.attr,
&dev_attr_usb2_lpm_l1_timeout.attr,
&dev_attr_usb2_lpm_besl.attr,
NULL,
};
static struct attribute_group usb2_hardware_lpm_attr_group = {
Expand Down
5 changes: 4 additions & 1 deletion drivers/usb/host/xhci-dbg.c
Original file line number Diff line number Diff line change
Expand Up @@ -503,11 +503,14 @@ static void xhci_dbg_ep_ctx(struct xhci_hcd *xhci,
if (last_ep < 31)
last_ep_ctx = last_ep + 1;
for (i = 0; i < last_ep_ctx; ++i) {
unsigned int epaddr = xhci_get_endpoint_address(i);
struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, ctx, i);
dma_addr_t dma = ctx->dma +
((unsigned long)ep_ctx - (unsigned long)ctx->bytes);

xhci_dbg(xhci, "Endpoint %02d Context:\n", i);
xhci_dbg(xhci, "%s Endpoint %02d Context (ep_index %02d):\n",
usb_endpoint_out(epaddr) ? "OUT" : "IN",
epaddr & USB_ENDPOINT_NUMBER_MASK, i);
xhci_dbg(xhci, "@%p (virt) @%08llx (dma) %#08x - ep_info\n",
&ep_ctx->ep_info,
(unsigned long long)dma, ep_ctx->ep_info);
Expand Down
1 change: 1 addition & 0 deletions drivers/usb/host/xhci-ext-caps.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@

/* USB 2.0 xHCI 1.0 hardware LMP capability - section 7.2.2.1.3.2 */
#define XHCI_HLC (1 << 19)
#define XHCI_BLC (1 << 19)

/* command register values to disable interrupts and halt the HC */
/* start/stop HC execution - do not write unless HC is halted*/
Expand Down
16 changes: 7 additions & 9 deletions drivers/usb/host/xhci-hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -867,18 +867,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
case USB_PORT_FEAT_U1_TIMEOUT:
if (hcd->speed != HCD_USB3)
goto error;
temp = xhci_readl(xhci, port_array[wIndex] + 1);
temp = xhci_readl(xhci, port_array[wIndex] + PORTPMSC);
temp &= ~PORT_U1_TIMEOUT_MASK;
temp |= PORT_U1_TIMEOUT(timeout);
xhci_writel(xhci, temp, port_array[wIndex] + 1);
xhci_writel(xhci, temp, port_array[wIndex] + PORTPMSC);
break;
case USB_PORT_FEAT_U2_TIMEOUT:
if (hcd->speed != HCD_USB3)
goto error;
temp = xhci_readl(xhci, port_array[wIndex] + 1);
temp = xhci_readl(xhci, port_array[wIndex] + PORTPMSC);
temp &= ~PORT_U2_TIMEOUT_MASK;
temp |= PORT_U2_TIMEOUT(timeout);
xhci_writel(xhci, temp, port_array[wIndex] + 1);
xhci_writel(xhci, temp, port_array[wIndex] + PORTPMSC);
break;
default:
goto error;
Expand Down Expand Up @@ -1098,10 +1098,8 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
__le32 __iomem *addr;
u32 tmp;

/* Add one to the port status register address to get
* the port power control register address.
*/
addr = port_array[port_index] + 1;
/* Get the port power control register address. */
addr = port_array[port_index] + PORTPMSC;
tmp = xhci_readl(xhci, addr);
tmp |= PORT_RWE;
xhci_writel(xhci, tmp, addr);
Expand Down Expand Up @@ -1193,7 +1191,7 @@ int xhci_bus_resume(struct usb_hcd *hcd)
/* Add one to the port status register address to get
* the port power control register address.
*/
addr = port_array[port_index] + 1;
addr = port_array[port_index] + PORTPMSC;
tmp = xhci_readl(xhci, addr);
tmp &= ~PORT_RWE;
xhci_writel(xhci, tmp, addr);
Expand Down
33 changes: 29 additions & 4 deletions drivers/usb/host/xhci-mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -1852,6 +1852,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
kfree(xhci->usb3_ports);
kfree(xhci->port_array);
kfree(xhci->rh_bw);
kfree(xhci->ext_caps);

xhci->page_size = 0;
xhci->page_shift = 0;
Expand Down Expand Up @@ -2039,7 +2040,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci)
}

static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
__le32 __iomem *addr, u8 major_revision)
__le32 __iomem *addr, u8 major_revision, int max_caps)
{
u32 temp, port_offset, port_count;
int i;
Expand All @@ -2064,6 +2065,10 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
/* WTF? "Valid values are ‘1’ to MaxPorts" */
return;

/* cache usb2 port capabilities */
if (major_revision < 0x03 && xhci->num_ext_caps < max_caps)
xhci->ext_caps[xhci->num_ext_caps++] = temp;

/* Check the host's USB2 LPM capability */
if ((xhci->hci_version == 0x96) && (major_revision != 0x03) &&
(temp & XHCI_L1C)) {
Expand Down Expand Up @@ -2121,10 +2126,11 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,
*/
static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
{
__le32 __iomem *addr;
u32 offset;
__le32 __iomem *addr, *tmp_addr;
u32 offset, tmp_offset;
unsigned int num_ports;
int i, j, port_index;
int cap_count = 0;

addr = &xhci->cap_regs->hcc_params;
offset = XHCI_HCC_EXT_CAPS(xhci_readl(xhci, addr));
Expand Down Expand Up @@ -2157,13 +2163,32 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
* See section 5.3.6 for offset calculation.
*/
addr = &xhci->cap_regs->hc_capbase + offset;

tmp_addr = addr;
tmp_offset = offset;

/* count extended protocol capability entries for later caching */
do {
u32 cap_id;
cap_id = xhci_readl(xhci, tmp_addr);
if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
cap_count++;
tmp_offset = XHCI_EXT_CAPS_NEXT(cap_id);
tmp_addr += tmp_offset;
} while (tmp_offset);

xhci->ext_caps = kzalloc(sizeof(*xhci->ext_caps) * cap_count, flags);
if (!xhci->ext_caps)
return -ENOMEM;

while (1) {
u32 cap_id;

cap_id = xhci_readl(xhci, addr);
if (XHCI_EXT_CAPS_ID(cap_id) == XHCI_EXT_CAPS_PROTOCOL)
xhci_add_in_port(xhci, num_ports, addr,
(u8) XHCI_EXT_PORT_MAJOR(cap_id));
(u8) XHCI_EXT_PORT_MAJOR(cap_id),
cap_count);
offset = XHCI_EXT_CAPS_NEXT(cap_id);
if (!offset || (xhci->num_usb2_ports + xhci->num_usb3_ports)
== num_ports)
Expand Down
2 changes: 1 addition & 1 deletion drivers/usb/host/xhci-ring.c
Original file line number Diff line number Diff line change
Expand Up @@ -2799,7 +2799,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
return IRQ_HANDLED;
}

irqreturn_t xhci_msi_irq(int irq, struct usb_hcd *hcd)
irqreturn_t xhci_msi_irq(int irq, void *hcd)
{
return xhci_irq(hcd);
}
Expand Down
Loading

0 comments on commit 1c83d94

Please sign in to comment.