xhci features for usb-next
Subsystems (2)
cover xhci features for usb-next
Hi Greg
A set of xhci changes for usb-next.
Refactoring the xhci even thandling to simplify support for serveral xhci interrupters, and decoupling port resume handling from get_port_status xhci roothub request handling a bit.
Thanks
Mathias
Mathias Nyman (11):
xhci: fix event ring segment table related masks and variables in
header
xhci: remove xhci_test_trb_in_td_math early development check
xhci: Refactor interrupter code for initial multi interrupter support.
xhci: add helpers for enabling and disabling interrupters
xhci: cleanup xhci_hub_control port references
xhci: pass port pointer as parameter to xhci_set_port_power()
xhci: move port specific items such as state completions to port
structure
xhci: Pass port structure as parameter to xhci_disable_port().
xhci: rename resume_done to resume_timestamp
xhci: clear usb2 resume related variables in one place.
xhci: decouple usb2 port resume and get_port_status request handling
drivers/usb/host/xhci-debugfs.c | 2 +- drivers/usb/host/xhci-hub.c | 256 ++++++++++++------------ drivers/usb/host/xhci-mem.c | 338 +++++++++++--------------------- drivers/usb/host/xhci-ring.c | 81 ++++---- drivers/usb/host/xhci.c | 79 ++++++-- drivers/usb/host/xhci.h | 38 ++-- 6 files changed, 366 insertions(+), 428 deletions(-)
Signature · 1 line
2.25.1
PATCH 1/11 xhci: fix event ring segment table related masks and variables in header
xHC controller can supports up to 1024 interrupters. To fit these change the max_interrupters varable from u8 to u16.
Add a separate mask for the reserve and preserve bits [5:0] in the erst base register and use it instead of the ERST_PRT_MASK. ERSR_PTR_MASK [3:0] is intended for masking bits in the event ring dequeue pointer register.
--- drivers/usb/host/xhci-mem.c | 4 ++-- drivers/usb/host/xhci.h | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-)
drivers/usb/host/xhci-mem.c +2 -2
@@ -2529,8 +2529,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)"// Set ERST base address for ir_set 0 = 0x%llx" , (unsigned long long)xhci->erst.erst_dma_addr); val_64 =xhci_read_64 (xhci, &xhci->ir_set->erst_base); - val_64 &=ERST_PTR_MASK ; - val_64 |= (xhci->erst.erst_dma_addr & (u64 ) ~ERST_PTR_MASK ); + val_64 &=ERST_BASE_RSVDP ; + val_64 |= (xhci->erst.erst_dma_addr & (u64 ) ~ERST_BASE_RSVDP );xhci_write_64 (xhci, val_64, &xhci->ir_set->erst_base);/* Set the event ring dequeue address */
drivers/usb/host/xhci.h +4 -1
@@ -513,6 +513,9 @@ struct xhci_intr_reg {/* Preserve bits 16:31 of erst_size */ #define ERST_SIZE_MASK (0xffff << 16) +/* erst_base bitmasks */ +#define ERST_BASE_RSVDP (0x3f) +/* erst_dequeue bitmasks */ /* Dequeue ERST Segment Index (DESI) - Segment number (or alias) * where the current dequeue pointer lies. This is an optional HW hint.
@@ -1774,7 +1777,7 @@ struct xhci_hcd {u8 sbrn;u16 hci_version;u8 max_slots; -u8 max_interrupters; +u16 max_interrupters;u8 max_ports;u8 isoc_threshold;/* imod_interval in ns (I * 250ns) */
Signature · 1 line
2.25.1
PATCH 2/11 xhci: remove xhci_test_trb_in_td_math early development check
In reply to Mathias NymanTime to remove this test trb in td math check that was added in early stage of xhci driver development.
It verified that the size, alignment and boundaries of the event and command rings allocated by the driver itself are correct.
--- drivers/usb/host/xhci-mem.c | 160 ------------------------------------ 1 file changed, 160 deletions(-)
drivers/usb/host/xhci-mem.c +0 -160
@@ -1929,164 +1929,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) xhci->usb3_rhub.bus_state.bus_suspended =0 ; } -static int xhci_test_trb_in_td (struct xhci_hcd *xhci, -struct xhci_segment *input_seg, -union xhci_trb *start_trb, -union xhci_trb *end_trb, -dma_addr_t input_dma, -struct xhci_segment *result_seg, -char *test_name,int test_number) -{ -unsigned long long start_dma; -unsigned long long end_dma; -struct xhci_segment *seg; - - start_dma =xhci_trb_virt_to_dma (input_seg, start_trb); - end_dma =xhci_trb_virt_to_dma (input_seg, end_trb); - - seg =trb_in_td (xhci, input_seg, start_trb, end_trb, input_dma, false); -if (seg != result_seg) { -xhci_warn (xhci,"WARN: %s TRB math test %d failed!\n" , - test_name, test_number); -xhci_warn (xhci,"Tested TRB math w/ seg %p and " -"input DMA 0x%llx\n" , - input_seg, - (unsigned long long ) input_dma); -xhci_warn (xhci,"starting TRB %p (0x%llx DMA), " -"ending TRB %p (0x%llx DMA)\n" , - start_trb, start_dma, - end_trb, end_dma); -xhci_warn (xhci,"Expected seg %p, got seg %p\n" , - result_seg, seg); -trb_in_td (xhci, input_seg, start_trb, end_trb, input_dma, - true); -return -1 ; - } -return 0 ; -} - -/* TRB math checks for xhci_trb_in_td(), using the command and event rings. */ -static int xhci_check_trb_in_td_math (struct xhci_hcd *xhci) -{ -struct { -dma_addr_t input_dma; -struct xhci_segment *result_seg; - } simple_test_vector [] = { -/* A zeroed DMA field should fail */ - {0 ,NULL }, -/* One TRB before the ring start should fail */ - { xhci->event_ring->first_seg->dma -16 ,NULL }, -/* One byte before the ring start should fail */ - { xhci->event_ring->first_seg->dma -1 ,NULL }, -/* Starting TRB should succeed */ - { xhci->event_ring->first_seg->dma, xhci->event_ring->first_seg }, -/* Ending TRB should succeed */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT -1 )*16 , - xhci->event_ring->first_seg }, -/* One byte after the ring end should fail */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT -1 )*16 +1 ,NULL }, -/* One TRB after the ring end should fail */ - { xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT )*16 ,NULL }, -/* An address of all ones should fail */ - { (dma_addr_t) (~0 ),NULL }, - }; -struct { -struct xhci_segment *input_seg; -union xhci_trb *start_trb; -union xhci_trb *end_trb; -dma_addr_t input_dma; -struct xhci_segment *result_seg; - } complex_test_vector [] = { -/* Test feeding a valid DMA address from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->event_ring->first_seg->trbs, - .end_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT -1 ], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg =NULL , - }, -/* Test feeding a valid end TRB from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->event_ring->first_seg->trbs, - .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT -1 ], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg =NULL , - }, -/* Test feeding a valid start and end TRB from a different ring */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = xhci->cmd_ring->first_seg->trbs, - .end_trb = &xhci->cmd_ring->first_seg->trbs[TRBS_PER_SEGMENT -1 ], - .input_dma = xhci->cmd_ring->first_seg->dma, - .result_seg =NULL , - }, -/* TRB in this ring, but after this TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[0 ], - .end_trb = &xhci->event_ring->first_seg->trbs[3 ], - .input_dma = xhci->event_ring->first_seg->dma +4 *16 , - .result_seg =NULL , - }, -/* TRB in this ring, but before this TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[3 ], - .end_trb = &xhci->event_ring->first_seg->trbs[6 ], - .input_dma = xhci->event_ring->first_seg->dma +2 *16 , - .result_seg =NULL , - }, -/* TRB in this ring, but after this wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT -3 ], - .end_trb = &xhci->event_ring->first_seg->trbs[1 ], - .input_dma = xhci->event_ring->first_seg->dma +2 *16 , - .result_seg =NULL , - }, -/* TRB in this ring, but before this wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT -3 ], - .end_trb = &xhci->event_ring->first_seg->trbs[1 ], - .input_dma = xhci->event_ring->first_seg->dma + (TRBS_PER_SEGMENT -4 )*16 , - .result_seg =NULL , - }, -/* TRB not in this ring, and we have a wrapped TD */ - { .input_seg = xhci->event_ring->first_seg, - .start_trb = &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT -3 ], - .end_trb = &xhci->event_ring->first_seg->trbs[1 ], - .input_dma = xhci->cmd_ring->first_seg->dma +2 *16 , - .result_seg =NULL , - }, - }; - -unsigned int num_tests; -int i, ret; - - num_tests =ARRAY_SIZE (simple_test_vector); -for (i =0 ; i < num_tests; i++) { - ret =xhci_test_trb_in_td (xhci, - xhci->event_ring->first_seg, - xhci->event_ring->first_seg->trbs, - &xhci->event_ring->first_seg->trbs[TRBS_PER_SEGMENT -1 ], - simple_test_vector[i].input_dma, - simple_test_vector[i].result_seg, -"Simple" , i); -if (ret <0 ) -return ret; - } - - num_tests =ARRAY_SIZE (complex_test_vector); -for (i =0 ; i < num_tests; i++) { - ret =xhci_test_trb_in_td (xhci, - complex_test_vector[i].input_seg, - complex_test_vector[i].start_trb, - complex_test_vector[i].end_trb, - complex_test_vector[i].input_dma, - complex_test_vector[i].result_seg, -"Complex" , i); -if (ret <0 ) -return ret; - } -xhci_dbg (xhci,"TRB math tests passed.\n" ); -return 0 ; -} -static void xhci_set_hc_event_deq (struct xhci_hcd *xhci) {u64 temp;
@@ -2506,8 +2348,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) 0, flags);if (!xhci->event_ring) goto fail; -if (xhci_check_trb_in_td_math (xhci) <0 ) - goto fail; ret =xhci_alloc_erst (xhci, xhci->event_ring, &xhci->erst, flags);if (ret)
Signature · 1 line
2.25.1
PATCH 3/11 xhci: Refactor interrupter code for initial multi interrupter support.
In reply to Mathias NymanxHC supports several interrupters, each with its own mmio register set, event ring and MSI/MSI-X vector. Transfers can be assigned different interrupters when queued. See xhci 4.17 for details.
Current driver only supports one interrupter.
Create a xhci_interrupter structure containing an event ring, pointer to mmio registers for this interrupter, variables to store registers over s3 suspend, erst, etc. Add functions to create and free an interrupter, and pass an interrupter pointer to functions that deal with events.
Secondary interrupters are also useful without having an interrupt vector. One use case is the xHCI audio sideband offloading where a DSP can take care of specific audio endpoints.
When all transfer events of an offloaded endpoint can be mapped to a separate interrupter event ring the DSP can poll this ring, and we can mask these events preventing waking up the CPU.
Only minor functional changes such as clearing some of the interrupter registers when freeing the interrupter.
Still create only one primary interrupter.
--- drivers/usb/host/xhci-debugfs.c | 2 +- drivers/usb/host/xhci-mem.c | 168 +++++++++++++++++++++----------- drivers/usb/host/xhci-ring.c | 68 +++++++------ drivers/usb/host/xhci.c | 54 ++++++---- drivers/usb/host/xhci.h | 24 +++-- 5 files changed, 196 insertions(+), 120 deletions(-)
drivers/usb/host/xhci-debugfs.c +1 -1
@@ -692,7 +692,7 @@ void xhci_debugfs_init(struct xhci_hcd *xhci)"command-ring" , xhci->debugfs_root); -xhci_debugfs_create_ring_dir (xhci, &xhci->event_ring, +xhci_debugfs_create_ring_dir (xhci, &xhci->interrupter->event_ring,"event-ring" , xhci->debugfs_root);
drivers/usb/host/xhci-mem.c +110 -58
223 lines not on this page · raw message
drivers/usb/host/xhci-ring.c +37 -31
192 lines not on this page · raw message
drivers/usb/host/xhci.c +34 -20
122 lines not on this page · raw message
drivers/usb/host/xhci.h +14 -10
@@ -1687,11 +1687,6 @@ struct s3_save {u32 dev_nt;u64 dcbaa_ptr;u32 config_reg; -u32 irq_pending; -u32 irq_control; -u32 erst_size; -u64 erst_base; -u64 erst_dequeue; };/* Use for lpm */
@@ -1718,7 +1713,18 @@ struct xhci_bus_state {struct completion u3exit_done[USB_MAXCHILDREN ]; }; - +struct xhci_interrupter { +struct xhci_ring *event_ring; +struct xhci_erst erst; +struct xhci_intr_reg __iomem *ir_set; +unsigned int intr_num; +/* For interrupter registers save and restore over suspend/resume */ +u32 s3_irq_pending; +u32 s3_irq_control; +u32 s3_erst_size; +u64 s3_erst_base; +u64 s3_erst_dequeue; +};/* * It can take up to 20 ms to transition from RExit to U0 on the * Intel Lynx Point LP xHCI host.
@@ -1761,8 +1767,6 @@ struct xhci_hcd {struct xhci_op_regs __iomem *op_regs;struct xhci_run_regs __iomem *run_regs;struct xhci_doorbell_array __iomem *dba; -/* Our HCD's current interrupter register set */ -struct xhci_intr_reg __iomem *ir_set;/* Cached register copies of read-only HC data */ __u32 hcs_params1;
@@ -1797,6 +1801,7 @@ struct xhci_hcd {struct reset_control *reset;/* data structures */ struct xhci_device_context_array *dcbaa; +struct xhci_interrupter *interrupter;struct xhci_ring *cmd_ring;unsigned int cmd_ring_state;#define CMD_RING_STATE_RUNNING (1 << 0)
@@ -1807,8 +1812,7 @@ struct xhci_hcd {struct delayed_work cmd_timer;struct completion cmd_ring_stop_completion;struct xhci_command *current_cmd; -struct xhci_ring *event_ring; -struct xhci_erst erst; +/* Scratchpad */ struct xhci_scratchpad *scratchpad;
Signature · 1 line
2.25.1
PATCH 4/11 xhci: add helpers for enabling and disabling interrupters
In reply to Mathias NymanSimple helpers to set and clear the IE (interrupter enable) bit for an interrupter.
--- drivers/usb/host/xhci.c | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-)
drivers/usb/host/xhci.c +30 -7
@@ -292,6 +292,32 @@ static void xhci_zero_64b_regs(struct xhci_hcd *xhci)xhci_info (xhci,"Fault detected\n" ); } +static int xhci_enable_interrupter (struct xhci_interrupter *ir) +{ +u32 iman; + +if (!ir || !ir->ir_set) +return -EINVAL ; + + iman =readl (&ir->ir_set->irq_pending); +writel (ER_IRQ_ENABLE (iman), &ir->ir_set->irq_pending); + +return 0 ; +} + +static int xhci_disable_interrupter (struct xhci_interrupter *ir) +{ +u32 iman; + +if (!ir || !ir->ir_set) +return -EINVAL ; + + iman =readl (&ir->ir_set->irq_pending); +writel (ER_IRQ_DISABLE (iman), &ir->ir_set->irq_pending); + +return 0 ; +} +#ifdef CONFIG_USB_PCI /* * Set up MSI
@@ -610,7 +636,6 @@ static int xhci_init(struct usb_hcd *hcd)/*-------------------------------------------------------------------------*/ -static int xhci_run_finished (struct xhci_hcd *xhci) {struct xhci_interrupter *ir = xhci->interrupter;
@@ -629,8 +654,7 @@ static int xhci_run_finished(struct xhci_hcd *xhci)writel (temp, &xhci->op_regs->command);xhci_dbg_trace (xhci, trace_xhci_dbg_init,"Enable primary interrupter" ); - temp =readl (&ir->ir_set->irq_pending); -writel (ER_IRQ_ENABLE (temp), &ir->ir_set->irq_pending); +xhci_enable_interrupter (ir);if (xhci_start (xhci)) {xhci_halt (xhci);
@@ -734,6 +758,7 @@ static void xhci_stop(struct usb_hcd *hcd) {u32 temp;struct xhci_hcd *xhci =hcd_to_xhci (hcd); +struct xhci_interrupter *ir = xhci->interrupter;mutex_lock (&xhci->mutex);
@@ -770,8 +795,7 @@ static void xhci_stop(struct usb_hcd *hcd) "// Disabling event ring interrupts"); temp =readl (&xhci->op_regs->status);writel ((temp & ~0x1fff ) |STS_EINT , &xhci->op_regs->status); - temp =readl (&xhci->interrupter->ir_set->irq_pending); -writel (ER_IRQ_DISABLE (temp), &xhci->interrupter->ir_set->irq_pending); +xhci_disable_interrupter (ir);xhci_dbg_trace (xhci, trace_xhci_dbg_init,"cleaning up memory" );xhci_mem_cleanup (xhci);
@@ -1227,8 +1251,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)xhci_dbg (xhci,"// Disabling event ring interrupts\n" ); temp =readl (&xhci->op_regs->status);writel ((temp & ~0x1fff ) |STS_EINT , &xhci->op_regs->status); - temp =readl (&xhci->interrupter->ir_set->irq_pending); -writel (ER_IRQ_DISABLE (temp), &xhci->interrupter->ir_set->irq_pending); +xhci_disable_interrupter (xhci->interrupter);xhci_dbg (xhci,"cleaning up memory\n" );xhci_mem_cleanup (xhci);
Signature · 1 line
2.25.1
PATCH 5/11 xhci: cleanup xhci_hub_control port references
In reply to Mathias NymanBoth port number and port structure of a port are referred to several times when handing hub requests in xhci.
Use more suitable data types and readable names for these. Cleanup only, no functional changes
--- drivers/usb/host/xhci-hub.c | 123 ++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 60 deletions(-)
drivers/usb/host/xhci-hub.c +63 -60
@@ -1209,11 +1209,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,u16 test_mode =0 ;struct xhci_hub *rhub;struct xhci_port **ports; +struct xhci_port *port; +int portnum1; rhub =xhci_get_rhub (hcd); ports = rhub->ports; max_ports = rhub->num_ports; bus_state = &rhub->bus_state; + portnum1 = wIndex &0xff ;spin_lock_irqsave (&xhci->lock, flags);switch (typeReq) {
@@ -1247,10 +1250,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,spin_unlock_irqrestore (&xhci->lock, flags);return retval;case GetPortStatus: -if (!wIndex || wIndex > max_ports) +if (!portnum1 || portnum1 > max_ports) goto error; + wIndex--; - temp =readl (ports[wIndex]->addr); + port = ports[portnum1 -1 ]; + temp =readl (port->addr);if (temp == ~(u32 )0 ) {xhci_hc_died (xhci); retval = -ENODEV ;
@@ -1263,7 +1268,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error;xhci_dbg (xhci,"Get port status %d-%d read: 0x%x, return 0x%x" , - hcd->self.busnum, wIndex +1 , temp, status); + hcd->self.busnum, portnum1, temp, status);put_unaligned (cpu_to_le32 (status), (__le32 *) buf);/* if USB 3.1 extended port status return additional 4 bytes */
@@ -1275,7 +1280,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, retval = -EINVAL ;break ; } - port_li =readl (ports[wIndex]->addr +PORTLI ); + port_li =readl (port->addr +PORTLI ); status =xhci_get_ext_port_status (temp, port_li);put_unaligned_le32 (status, &buf[4 ]); }
@@ -1289,11 +1294,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, test_mode = (wIndex &0xff00 ) >>8 ;/* The MSB of wIndex is the U1/U2 timeout */ timeout = (wIndex &0xff00 ) >>8 ; + wIndex &=0xff ; -if (!wIndex || wIndex > max_ports) +if (!portnum1 || portnum1 > max_ports) goto error; + + port = ports[portnum1 -1 ]; wIndex--; - temp =readl (ports[wIndex]->addr); + temp =readl (port->addr);if (temp == ~(u32 )0 ) {xhci_hc_died (xhci); retval = -ENODEV ;
@@ -1303,11 +1311,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,/* FIXME: What new port features do we need to support? */ switch (wValue) {case USB_PORT_FEAT_SUSPEND : - temp =readl (ports[wIndex]->addr); + temp =readl (port->addr);if ((temp &PORT_PLS_MASK ) !=XDEV_U0 ) {/* Resume the port to U0 first */ -xhci_set_link_state (xhci, ports[wIndex], -XDEV_U0 ); +xhci_set_link_state (xhci, port,XDEV_U0 );spin_unlock_irqrestore (&xhci->lock, flags);msleep (10 );spin_lock_irqsave (&xhci->lock, flags);
@@ -1316,16 +1323,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,* a port unless the port reports that it is in the * enabled (PED = ‘1’,PLS < ‘3’) state. */ - temp =readl (ports[wIndex]->addr); + temp =readl (port->addr);if ((temp &PORT_PE ) ==0 || (temp &PORT_RESET ) || (temp &PORT_PLS_MASK ) >=XDEV_U3 ) {xhci_warn (xhci,"USB core suspending port %d-%d not in U0/U1/U2\n" , - hcd->self.busnum, wIndex +1 ); + hcd->self.busnum, portnum1); goto error; } slot_id =xhci_find_slot_id_by_port (hcd, xhci, - wIndex +1 ); + portnum1);if (!slot_id) {xhci_warn (xhci,"slot_id is zero\n" ); goto error;
@@ -1335,21 +1342,21 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,xhci_stop_device (xhci, slot_id,1 );spin_lock_irqsave (&xhci->lock, flags); -xhci_set_link_state (xhci, ports[wIndex],XDEV_U3 ); +xhci_set_link_state (xhci, port,XDEV_U3 );spin_unlock_irqrestore (&xhci->lock, flags);msleep (10 );/* wait device to enter */ spin_lock_irqsave (&xhci->lock, flags); - temp =readl (ports[wIndex]->addr); + temp =readl (port->addr); bus_state->suspended_ports |=1 << wIndex;break ;case USB_PORT_FEAT_LINK_STATE : - temp =readl (ports[wIndex]->addr); + temp =readl (port->addr);/* Disable port */ if (link_state ==USB_SS_PORT_LS_SS_DISABLED ) {xhci_dbg (xhci,"Disable port %d-%d\n" , - hcd->self.busnum, wIndex +1 ); + hcd->self.busnum, portnum1); temp =xhci_port_state_to_neutral (temp);/* * Clear all change bits, so that we get a new
@@ -1358,18 +1365,17 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp |=PORT_CSC |PORT_PEC |PORT_WRC |PORT_OCC |PORT_RC |PORT_PLC |PORT_CEC ; -writel (temp |PORT_PE , ports[wIndex]->addr); - temp =readl (ports[wIndex]->addr); +writel (temp |PORT_PE , port->addr); + temp =readl (port->addr);break ; }/* Put link in RxDetect (enable port) */ if (link_state ==USB_SS_PORT_LS_RX_DETECT ) {xhci_dbg (xhci,"Enable port %d-%d\n" , - hcd->self.busnum, wIndex +1 ); -xhci_set_link_state (xhci, ports[wIndex], - link_state); - temp =readl (ports[wIndex]->addr); + hcd->self.busnum, portnum1); +xhci_set_link_state (xhci, port, link_state); + temp =readl (port->addr);break ; }
@@ -1399,11 +1405,10 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, }xhci_dbg (xhci,"Enable compliance mode transition for port %d-%d\n" , - hcd->self.busnum, wIndex +1 ); -xhci_set_link_state (xhci, ports[wIndex], - link_state); + hcd->self.busnum, portnum1); +xhci_set_link_state (xhci, port, link_state); - temp =readl (ports[wIndex]->addr); + temp =readl (port->addr);break ; }/* Port must be enabled */
@@ -1414,8 +1419,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,/* Can't set port link state above '3' (U3) */ if (link_state >USB_SS_PORT_LS_U3 ) {xhci_warn (xhci,"Cannot set port %d-%d link state %d\n" , - hcd->self.busnum, wIndex +1 , - link_state); + hcd->self.busnum, portnum1, link_state); goto error; }
@@ -1440,8 +1444,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,reinit_completion (&bus_state->u3exit_done[wIndex]); }if (pls <=XDEV_U3 )/* U1, U2, U3 */ -xhci_set_link_state (xhci, ports[wIndex], -USB_SS_PORT_LS_U0 ); +xhci_set_link_state (xhci, port,USB_SS_PORT_LS_U0 );if (!wait_u0) {if (pls >XDEV_U3 ) goto error;
@@ -1451,16 +1454,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,if (!wait_for_completion_timeout (&bus_state->u3exit_done[wIndex],msecs_to_jiffies (500 )))xhci_dbg (xhci,"missing U0 port change event for port %d-%d\n" , - hcd->self.busnum, wIndex +1 ); + hcd->self.busnum, portnum1);spin_lock_irqsave (&xhci->lock, flags); - temp =readl (ports[wIndex]->addr); + temp =readl (port->addr);break ; }if (link_state ==USB_SS_PORT_LS_U3 ) {int retries =16 ; slot_id =xhci_find_slot_id_by_port (hcd, xhci, - wIndex +1 ); + portnum1);if (slot_id) {/* unlock to execute stop endpoint * commands */
@@ -1469,16 +1472,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,xhci_stop_device (xhci, slot_id,1 );spin_lock_irqsave (&xhci->lock, flags); } -xhci_set_link_state (xhci, ports[wIndex],USB_SS_PORT_LS_U3 ); +xhci_set_link_state (xhci, port,USB_SS_PORT_LS_U3 );spin_unlock_irqrestore (&xhci->lock, flags);while (retries--) {usleep_range (4000 ,8000 ); - temp =readl (ports[wIndex]->addr); + temp =readl (port->addr);if ((temp &PORT_PLS_MASK ) ==XDEV_U3 )break ; }spin_lock_irqsave (&xhci->lock, flags); - temp =readl (ports[wIndex]->addr); + temp =readl (port->addr); bus_state->suspended_ports |=1 << wIndex; }break ;
@@ -1493,39 +1496,38 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,break ;case USB_PORT_FEAT_RESET : temp = (temp |PORT_RESET ); -writel (temp, ports[wIndex]->addr); +writel (temp, port->addr); - temp =readl (ports[wIndex]->addr); + temp =readl (port->addr);xhci_dbg (xhci,"set port reset, actual port %d-%d status = 0x%x\n" , - hcd->self.busnum, wIndex +1 , temp); + hcd->self.busnum, portnum1, temp);break ;case USB_PORT_FEAT_REMOTE_WAKE_MASK : -xhci_set_remote_wake_mask (xhci, ports[wIndex], - wake_mask); - temp =readl (ports[wIndex]->addr); +xhci_set_remote_wake_mask (xhci, port, wake_mask); + temp =readl (port->addr);xhci_dbg (xhci,"set port remote wake mask, actual port %d-%d status = 0x%x\n" , - hcd->self.busnum, wIndex +1 , temp); + hcd->self.busnum, portnum1, temp);break ;case USB_PORT_FEAT_BH_PORT_RESET : temp |=PORT_WR ; -writel (temp, ports[wIndex]->addr); - temp =readl (ports[wIndex]->addr); +writel (temp, port->addr); + temp =readl (port->addr);break ;case USB_PORT_FEAT_U1_TIMEOUT :if (hcd->speed <HCD_USB3 ) goto error; - temp =readl (ports[wIndex]->addr +PORTPMSC ); + temp =readl (port->addr +PORTPMSC ); temp &= ~PORT_U1_TIMEOUT_MASK ; temp |=PORT_U1_TIMEOUT (timeout); -writel (temp, ports[wIndex]->addr +PORTPMSC ); +writel (temp, port->addr +PORTPMSC );break ;case USB_PORT_FEAT_U2_TIMEOUT :if (hcd->speed <HCD_USB3 ) goto error; - temp =readl (ports[wIndex]->addr +PORTPMSC ); + temp =readl (port->addr +PORTPMSC ); temp &= ~PORT_U2_TIMEOUT_MASK ; temp |=PORT_U2_TIMEOUT (timeout); -writel (temp, ports[wIndex]->addr +PORTPMSC ); +writel (temp, port->addr +PORTPMSC );break ;case USB_PORT_FEAT_TEST :/* 4.19.6 Port Test Modes (USB2 Test Mode) */
@@ -1541,13 +1543,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto error; }/* unblock any posted writes */ - temp =readl (ports[wIndex]->addr); + temp =readl (port->addr);break ;case ClearPortFeature: -if (!wIndex || wIndex > max_ports) +if (!portnum1 || portnum1 > max_ports) goto error; + + port = ports[portnum1 -1 ]; + wIndex--; - temp =readl (ports[wIndex]->addr); + temp =readl (port->addr);if (temp == ~(u32 )0 ) {xhci_hc_died (xhci); retval = -ENODEV ;
@@ -1557,7 +1562,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp =xhci_port_state_to_neutral (temp);switch (wValue) {case USB_PORT_FEAT_SUSPEND : - temp =readl (ports[wIndex]->addr); + temp =readl (port->addr);xhci_dbg (xhci,"clear USB_PORT_FEAT_SUSPEND\n" );xhci_dbg (xhci,"PORTSC %04x\n" , temp);if (temp &PORT_RESET )
@@ -1568,20 +1573,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,set_bit (wIndex, &bus_state->resuming_ports);usb_hcd_start_port_resume (&hcd->self, wIndex); -xhci_set_link_state (xhci, ports[wIndex], -XDEV_RESUME ); +xhci_set_link_state (xhci, port,XDEV_RESUME );spin_unlock_irqrestore (&xhci->lock, flags);msleep (USB_RESUME_TIMEOUT );spin_lock_irqsave (&xhci->lock, flags); -xhci_set_link_state (xhci, ports[wIndex], -XDEV_U0 ); +xhci_set_link_state (xhci, port,XDEV_U0 );clear_bit (wIndex, &bus_state->resuming_ports);usb_hcd_end_port_resume (&hcd->self, wIndex); } bus_state->port_c_suspend |=1 << wIndex; slot_id =xhci_find_slot_id_by_port (hcd, xhci, - wIndex +1 ); + portnum1);if (!slot_id) {xhci_dbg (xhci,"slot_id is zero\n" ); goto error;
@@ -1599,11 +1602,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,case USB_PORT_FEAT_C_PORT_LINK_STATE :case USB_PORT_FEAT_C_PORT_CONFIG_ERROR :xhci_clear_port_change_bit (xhci, wValue, wIndex, - ports[wIndex]->addr, temp); + port->addr, temp);break ;case USB_PORT_FEAT_ENABLE :xhci_disable_port (hcd, xhci, wIndex, - ports[wIndex]->addr, temp); + port->addr, temp);break ;case USB_PORT_FEAT_POWER :xhci_set_port_power (xhci, hcd, wIndex, false, &flags);
Signature · 1 line
2.25.1
PATCH 6/11 xhci: pass port pointer as parameter to xhci_set_port_power()
In reply to Mathias NymanPass the port structure pointer directly to xhci_set_port_power() instead of hcd and port index.
cleanup
--- drivers/usb/host/xhci-hub.c | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-)
drivers/usb/host/xhci-hub.c +11 -14
@@ -666,20 +666,18 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd)* It will release and re-aquire the lock while calling ACPI * method. */ -static void xhci_set_port_power (struct xhci_hcd *xhci,struct usb_hcd *hcd, -u16 index,bool on,unsigned long *flags) +static void xhci_set_port_power (struct xhci_hcd *xhci,struct xhci_port *port, +bool on,unsigned long *flags) __must_hold(&xhci->lock) { -struct xhci_hub *rhub; -struct xhci_port *port; +struct usb_hcd *hcd;u32 temp; - rhub =xhci_get_rhub (hcd); - port = rhub->ports[index]; + hcd = port->rhub->hcd; temp =readl (port->addr);xhci_dbg (xhci,"set port power %d-%d %s, portsc: 0x%x\n" , - hcd->self.busnum, index +1 , on ?"ON" :"OFF" , temp); + hcd->self.busnum, port->hcd_portnum +1 , on ?"ON" :"OFF" , temp); temp =xhci_port_state_to_neutral (temp);
@@ -694,10 +692,10 @@ static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd,spin_unlock_irqrestore (&xhci->lock, *flags); temp =usb_acpi_power_manageable (hcd->self.root_hub, - index); + port->hcd_portnum);if (temp)usb_acpi_set_power_state (hcd->self.root_hub, - index, on); + port->hcd_portnum, on);spin_lock_irqsave (&xhci->lock, *flags); }
@@ -721,7 +719,6 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci, u16 test_mode, u16 wIndex, unsigned long *flags) __must_hold(&xhci->lock) { - struct usb_hcd *usb3_hcd = xhci_get_usb3_hcd(xhci); int i, retval;/* Disable all Device Slots */
@@ -742,10 +739,10 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci,xhci_dbg (xhci,"Disable all port (PP = 0)\n" );/* Power off USB3 ports*/ for (i =0 ; i < xhci->usb3_rhub.num_ports; i++) -xhci_set_port_power (xhci, usb3_hcd, i, false, flags); +xhci_set_port_power (xhci, xhci->usb3_rhub.ports[i], false, flags);/* Power off USB2 ports*/ for (i =0 ; i < xhci->usb2_rhub.num_ports; i++) -xhci_set_port_power (xhci, xhci->main_hcd, i, false, flags); +xhci_set_port_power (xhci, xhci->usb2_rhub.ports[i], false, flags);/* Stop the controller */ xhci_dbg (xhci,"Stop controller\n" ); retval =xhci_halt (xhci);
@@ -1492,7 +1489,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,* However, hub_wq will ignore the roothub events until * the roothub is registered. */ -xhci_set_port_power (xhci, hcd, wIndex, true, &flags); +xhci_set_port_power (xhci, port, true, &flags);break ;case USB_PORT_FEAT_RESET : temp = (temp |PORT_RESET );
@@ -1609,7 +1606,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, port->addr, temp);break ;case USB_PORT_FEAT_POWER : -xhci_set_port_power (xhci, hcd, wIndex, false, &flags); +xhci_set_port_power (xhci, port, false, &flags);break ;case USB_PORT_FEAT_TEST : retval =xhci_exit_test_mode (xhci);
Signature · 1 line
2.25.1
PATCH 7/11 xhci: move port specific items such as state completions to port structure
In reply to Mathias NymanNow that we have a port structure for each port it makes sense to move per port variables, timestamps and completions there. Get rid of storing bitfileds and arrays of port specific items per bus.
Move
unsigned long resume_done;
insigned long rexit_ports
struct completion rexit_done;
struct completion u3exit_done;
Rename rexit_ports to rexit_active, and remove a redundant hcd speed check while checking if rexit_active is set.
--- drivers/usb/host/xhci-hub.c | 31 +++++++++++++++---------------- drivers/usb/host/xhci-mem.c | 10 +++------- drivers/usb/host/xhci-ring.c | 13 ++++++------- drivers/usb/host/xhci.h | 9 ++++----- 4 files changed, 28 insertions(+), 35 deletions(-)
drivers/usb/host/xhci-hub.c +15 -16
@@ -936,7 +936,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,return -EINVAL ; }/* did port event handler already start resume timing? */ -if (!bus_state->resume_done[wIndex]) { +if (!port->resume_done) {/* If not, maybe we are in a host initated resume? */ if (test_bit (wIndex, &bus_state->resuming_ports)) {/* Host initated resume doesn't time the resume
@@ -953,28 +953,27 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,msecs_to_jiffies (USB_RESUME_TIMEOUT );set_bit (wIndex, &bus_state->resuming_ports); - bus_state->resume_done[wIndex] = timeout; + port->resume_done = timeout;mod_timer (&hcd->rh_timer, timeout);usb_hcd_start_port_resume (&hcd->self, wIndex); }/* Has resume been signalled for USB_RESUME_TIME yet? */ - }else if (time_after_eq (jiffies, bus_state->resume_done[wIndex])) { + }else if (time_after_eq (jiffies, port->resume_done)) {int time_left;xhci_dbg (xhci,"resume USB2 port %d-%d\n" , hcd->self.busnum, wIndex +1 ); - bus_state->resume_done[wIndex] =0 ; + port->resume_done =0 ;clear_bit (wIndex, &bus_state->resuming_ports); - -set_bit (wIndex, &bus_state->rexit_ports); + port->rexit_active = true;xhci_test_and_clear_bit (xhci, port,PORT_PLC );xhci_set_link_state (xhci, port,XDEV_U0 );spin_unlock_irqrestore (&xhci->lock, *flags); time_left =wait_for_completion_timeout ( - &bus_state->rexit_done[wIndex], + &port->rexit_done,msecs_to_jiffies (XHCI_MAX_REXIT_TIMEOUT_MS ));spin_lock_irqsave (&xhci->lock, *flags);
@@ -993,7 +992,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,xhci_warn (xhci,"Port resume timed out, port %d-%d: 0x%x\n" , hcd->self.busnum, wIndex +1 , port_status); *status |=USB_PORT_STAT_SUSPEND ; -clear_bit (wIndex, &bus_state->rexit_ports); + port->rexit_active = false; }usb_hcd_end_port_resume (&hcd->self, wIndex);
@@ -1100,10 +1099,10 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status,if (link_state ==XDEV_U2 ) *status |=USB_PORT_STAT_L1 ;if (link_state ==XDEV_U0 ) { -if (bus_state->resume_done[portnum]) +if (port->resume_done)usb_hcd_end_port_resume (&port->rhub->hcd->self, portnum); - bus_state->resume_done[portnum] =0 ; + port->resume_done =0 ;clear_bit (portnum, &bus_state->resuming_ports);if (bus_state->suspended_ports & (1 << portnum)) { bus_state->suspended_ports &= ~(1 << portnum);
@@ -1175,11 +1174,11 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,* Clear stale usb2 resume signalling variables in case port changed * state during resume signalling. For example on error */ -if ((bus_state->resume_done[wIndex] || +if ((port->resume_done ||test_bit (wIndex, &bus_state->resuming_ports)) && (raw_port_status &PORT_PLS_MASK ) !=XDEV_U3 && (raw_port_status &PORT_PLS_MASK ) !=XDEV_RESUME ) { - bus_state->resume_done[wIndex] =0 ; + port->resume_done =0 ;clear_bit (wIndex, &bus_state->resuming_ports);usb_hcd_end_port_resume (&hcd->self, wIndex); }
@@ -1438,7 +1437,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, pls == XDEV_RESUME || pls == XDEV_RECOVERY) { wait_u0 = true; -reinit_completion (&bus_state->u3exit_done[wIndex]); +reinit_completion (&port->u3exit_done); }if (pls <=XDEV_U3 )/* U1, U2, U3 */ xhci_set_link_state (xhci, port,USB_SS_PORT_LS_U0 );
@@ -1448,7 +1447,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,break ; }spin_unlock_irqrestore (&xhci->lock, flags); -if (!wait_for_completion_timeout (&bus_state->u3exit_done[wIndex], +if (!wait_for_completion_timeout (&port->u3exit_done,msecs_to_jiffies (500 )))xhci_dbg (xhci,"missing U0 port change event for port %d-%d\n" , hcd->self.busnum, portnum1);
@@ -1688,8 +1687,8 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)if ((temp & mask) !=0 || (bus_state->port_c_suspend &1 << i) || - (bus_state->resume_done[i] &&time_after_eq ( - jiffies, bus_state->resume_done[i]))) { + (ports[i]->resume_done &&time_after_eq ( + jiffies, ports[i]->resume_done))) { buf[(i +1 ) /8 ] |=1 << (i +1 ) %8 ; status =1 ; }
drivers/usb/host/xhci-mem.c +3 -7
@@ -2154,6 +2154,9 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) xhci->hw_ports[i].addr = &xhci->op_regs->port_status_base +NUM_PORT_REGS * i; xhci->hw_ports[i].hw_portnum = i; + +init_completion (&xhci->hw_ports[i].rexit_done); +init_completion (&xhci->hw_ports[i].u3exit_done); } xhci->rh_bw = kcalloc_node(num_ports, sizeof(*xhci->rh_bw), flags,
@@ -2439,13 +2442,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)*/ for (i =0 ; i <MAX_HC_SLOTS ; i++) xhci->devs[i] =NULL ; -for (i =0 ; i <USB_MAXCHILDREN ; i++) { - xhci->usb2_rhub.bus_state.resume_done[i] =0 ; - xhci->usb3_rhub.bus_state.resume_done[i] =0 ; -/* Only the USB 2.0 completions will ever be used. */ -init_completion (&xhci->usb2_rhub.bus_state.rexit_done[i]); -init_completion (&xhci->usb3_rhub.bus_state.u3exit_done[i]); - }if (scratchpad_alloc (xhci, flags)) goto fail;
drivers/usb/host/xhci-ring.c +6 -7
@@ -1924,7 +1924,7 @@ static void handle_port_status(struct xhci_hcd *xhci, goto cleanup; }else if (!test_bit (hcd_portnum, &bus_state->resuming_ports)) {xhci_dbg (xhci,"resume HS port %d\n" , port_id); - bus_state->resume_done[hcd_portnum] = jiffies + + port->resume_done = jiffies +msecs_to_jiffies (USB_RESUME_TIMEOUT );set_bit (hcd_portnum, &bus_state->resuming_ports);/* Do the rest in GetPortStatus after resume time delay.
@@ -1933,7 +1933,7 @@ static void handle_port_status(struct xhci_hcd *xhci,*/ set_bit (HCD_FLAG_POLL_RH , &hcd->flags);mod_timer (&hcd->rh_timer, - bus_state->resume_done[hcd_portnum]); + port->resume_done);usb_hcd_start_port_resume (&hcd->self, hcd_portnum); bogus_port_status = true; }
@@ -1945,7 +1945,7 @@ static void handle_port_status(struct xhci_hcd *xhci, (portsc & PORT_PLS_MASK) == XDEV_U1 || (portsc & PORT_PLS_MASK) == XDEV_U2)) {xhci_dbg (xhci,"resume SS port %d finished\n" , port_id); -complete (&bus_state->u3exit_done[hcd_portnum]); +complete (&port->u3exit_done);/* We've just brought the device into U0/1/2 through either the * Resume state after a device remote wakeup, or through the * U3Exit state after a host-initiated resume. If it's a device
@@ -1970,10 +1970,9 @@ static void handle_port_status(struct xhci_hcd *xhci,* RExit to a disconnect state). If so, let the driver know it's * out of the RExit state. */ -if (!DEV_SUPERSPEED_ANY (portsc) && hcd->speed <HCD_USB3 && -test_and_clear_bit (hcd_portnum, - &bus_state->rexit_ports)) { -complete (&bus_state->rexit_done[hcd_portnum]); +if (hcd->speed <HCD_USB3 && port->rexit_active) { +complete (&port->rexit_done); + port->rexit_active = false; bogus_port_status = true; goto cleanup; }
drivers/usb/host/xhci.h +4 -5
@@ -1704,13 +1704,8 @@ struct xhci_bus_state {u32 port_c_suspend;u32 suspended_ports;u32 port_remote_wakeup; -unsigned long resume_done[USB_MAXCHILDREN ];/* which ports have started to resume */ unsigned long resuming_ports; -/* Which ports are waiting on RExit to U0 transition. */ -unsigned long rexit_ports; -struct completion rexit_done[USB_MAXCHILDREN ]; -struct completion u3exit_done[USB_MAXCHILDREN ]; };struct xhci_interrupter {
@@ -1745,6 +1740,10 @@ struct xhci_port {struct xhci_hub *rhub;struct xhci_port_cap *port_cap; unsigned int lpm_incapable:1; +unsigned long resume_done; +bool rexit_active; +struct completion rexit_done; +struct completion u3exit_done; };struct xhci_hub {
Signature · 1 line
2.25.1
PATCH 8/11 xhci: Pass port structure as parameter to xhci_disable_port().
In reply to Mathias NymanPass the port structure to xhci_disable_port() instead of address, index, and value.
re-read the port portsc value before disabling the port.
--- drivers/usb/host/xhci-hub.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-)
drivers/usb/host/xhci-hub.c +15 -9
@@ -578,13 +578,16 @@ void xhci_ring_device(struct xhci_hcd *xhci, int slot_id)return ; } -static void xhci_disable_port (struct usb_hcd *hcd,struct xhci_hcd *xhci, -u16 wIndex,__le32 __iomem *addr,u32 port_status) +static void xhci_disable_port (struct xhci_hcd *xhci,struct xhci_port *port) { +struct usb_hcd *hcd; +u32 portsc; + + hcd = port->rhub->hcd; +/* Don't allow the USB core to disable SuperSpeed ports. */ if (hcd->speed >=HCD_USB3 ) { -xhci_dbg (xhci,"Ignoring request to disable " -"SuperSpeed port.\n" ); +xhci_dbg (xhci,"Ignoring request to disable SuperSpeed port.\n" );return ; }
@@ -594,11 +597,15 @@ static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci,return ; } + portsc =readl (port->addr); + portsc =xhci_port_state_to_neutral (portsc); +/* Write 1 to disable the port */ -writel (port_status |PORT_PE , addr); - port_status =readl (addr); +writel (portsc |PORT_PE , port->addr); + + portsc =readl (port->addr);xhci_dbg (xhci,"disable port %d-%d, portsc: 0x%x\n" , - hcd->self.busnum, wIndex +1 , port_status); + hcd->self.busnum, port->hcd_portnum +1 , portsc); } static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
@@ -1601,8 +1608,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, port->addr, temp);break ;case USB_PORT_FEAT_ENABLE : -xhci_disable_port (hcd, xhci, wIndex, - port->addr, temp); +xhci_disable_port (xhci, port);break ;case USB_PORT_FEAT_POWER :xhci_set_port_power (xhci, port, false, &flags);
Signature · 1 line
2.25.1
PATCH 9/11 xhci: rename resume_done to resume_timestamp
In reply to Mathias Nymanresume_done is just a timestamp, avoid confusing it with completions related to port state transitions that are named *_done
--- drivers/usb/host/xhci-hub.c | 20 ++++++++++---------- drivers/usb/host/xhci-ring.c | 4 ++-- drivers/usb/host/xhci.h | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-)
drivers/usb/host/xhci-hub.c +10 -10
@@ -943,7 +943,7 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,return -EINVAL ; }/* did port event handler already start resume timing? */ -if (!port->resume_done) { +if (!port->resume_timestamp) {/* If not, maybe we are in a host initated resume? */ if (test_bit (wIndex, &bus_state->resuming_ports)) {/* Host initated resume doesn't time the resume
@@ -960,18 +960,18 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,msecs_to_jiffies (USB_RESUME_TIMEOUT );set_bit (wIndex, &bus_state->resuming_ports); - port->resume_done = timeout; + port->resume_timestamp = timeout;mod_timer (&hcd->rh_timer, timeout);usb_hcd_start_port_resume (&hcd->self, wIndex); }/* Has resume been signalled for USB_RESUME_TIME yet? */ - }else if (time_after_eq (jiffies, port->resume_done)) { + }else if (time_after_eq (jiffies, port->resume_timestamp)) {int time_left;xhci_dbg (xhci,"resume USB2 port %d-%d\n" , hcd->self.busnum, wIndex +1 ); - port->resume_done =0 ; + port->resume_timestamp =0 ;clear_bit (wIndex, &bus_state->resuming_ports); port->rexit_active = true;
@@ -1106,10 +1106,10 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status,if (link_state ==XDEV_U2 ) *status |=USB_PORT_STAT_L1 ;if (link_state ==XDEV_U0 ) { -if (port->resume_done) +if (port->resume_timestamp)usb_hcd_end_port_resume (&port->rhub->hcd->self, portnum); - port->resume_done =0 ; + port->resume_timestamp =0 ;clear_bit (portnum, &bus_state->resuming_ports);if (bus_state->suspended_ports & (1 << portnum)) { bus_state->suspended_ports &= ~(1 << portnum);
@@ -1181,11 +1181,11 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,* Clear stale usb2 resume signalling variables in case port changed * state during resume signalling. For example on error */ -if ((port->resume_done || +if ((port->resume_timestamp ||test_bit (wIndex, &bus_state->resuming_ports)) && (raw_port_status &PORT_PLS_MASK ) !=XDEV_U3 && (raw_port_status &PORT_PLS_MASK ) !=XDEV_RESUME ) { - port->resume_done =0 ; + port->resume_timestamp =0 ;clear_bit (wIndex, &bus_state->resuming_ports);usb_hcd_end_port_resume (&hcd->self, wIndex); }
@@ -1693,8 +1693,8 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)if ((temp & mask) !=0 || (bus_state->port_c_suspend &1 << i) || - (ports[i]->resume_done &&time_after_eq ( - jiffies, ports[i]->resume_done))) { + (ports[i]->resume_timestamp &&time_after_eq ( + jiffies, ports[i]->resume_timestamp))) { buf[(i +1 ) /8 ] |=1 << (i +1 ) %8 ; status =1 ; }
drivers/usb/host/xhci-ring.c +2 -2
@@ -1924,7 +1924,7 @@ static void handle_port_status(struct xhci_hcd *xhci, goto cleanup; }else if (!test_bit (hcd_portnum, &bus_state->resuming_ports)) {xhci_dbg (xhci,"resume HS port %d\n" , port_id); - port->resume_done = jiffies + + port->resume_timestamp = jiffies +msecs_to_jiffies (USB_RESUME_TIMEOUT );set_bit (hcd_portnum, &bus_state->resuming_ports);/* Do the rest in GetPortStatus after resume time delay.
@@ -1933,7 +1933,7 @@ static void handle_port_status(struct xhci_hcd *xhci,*/ set_bit (HCD_FLAG_POLL_RH , &hcd->flags);mod_timer (&hcd->rh_timer, - port->resume_done); + port->resume_timestamp);usb_hcd_start_port_resume (&hcd->self, hcd_portnum); bogus_port_status = true; }
drivers/usb/host/xhci.h +1 -1
@@ -1740,7 +1740,7 @@ struct xhci_port {struct xhci_hub *rhub;struct xhci_port_cap *port_cap; unsigned int lpm_incapable:1; -unsigned long resume_done; +unsigned long resume_timestamp;bool rexit_active;struct completion rexit_done;struct completion u3exit_done;
Signature · 1 line
2.25.1
PATCH 10/11 xhci: clear usb2 resume related variables in one place.
In reply to Mathias NymanInitially resume related USB2 variables were cleared once port successfully resumed to U0. Later code was added to clean up stale resume variables in case of port failed to resume to U0.
Clear the variables in one place after port is no longer resuming or in suspended U3 state.
--- drivers/usb/host/xhci-hub.c | 38 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 22 deletions(-)
drivers/usb/host/xhci-hub.c +16 -22
@@ -1090,7 +1090,6 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status,struct xhci_bus_state *bus_state;u32 link_state;u32 portnum; -int ret; bus_state = &port->rhub->bus_state; link_state = portsc &PORT_PLS_MASK ;
@@ -1106,23 +1105,30 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status,if (link_state ==XDEV_U2 ) *status |=USB_PORT_STAT_L1 ;if (link_state ==XDEV_U0 ) { -if (port->resume_timestamp) -usb_hcd_end_port_resume (&port->rhub->hcd->self, - portnum); - port->resume_timestamp =0 ; -clear_bit (portnum, &bus_state->resuming_ports);if (bus_state->suspended_ports & (1 << portnum)) { bus_state->suspended_ports &= ~(1 << portnum); bus_state->port_c_suspend |=1 << portnum; } }if (link_state ==XDEV_RESUME ) { - ret =xhci_handle_usb2_port_link_resume (port, status, - portsc, flags); -if (ret) -return ; +xhci_handle_usb2_port_link_resume (port, status, portsc, + flags); } } + +/* +* Clear usb2 resume signalling variables if port is no longer suspended +* or resuming. Port either resumed to U0/U1/U2, disconnected, or in a +* error state. Resume related variables should be cleared in all those cases. +*/ +if ((link_state !=XDEV_U3 && + link_state !=XDEV_RESUME ) && + (port->resume_timestamp || +test_bit (portnum, &bus_state->resuming_ports))) { + port->resume_timestamp =0 ; +clear_bit (portnum, &bus_state->resuming_ports); +usb_hcd_end_port_resume (&port->rhub->hcd->self, portnum); + } }/*
@@ -1177,18 +1183,6 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd,else xhci_get_usb2_port_status (port, &status, raw_port_status, flags); -/* -* Clear stale usb2 resume signalling variables in case port changed -* state during resume signalling. For example on error -*/ -if ((port->resume_timestamp || -test_bit (wIndex, &bus_state->resuming_ports)) && - (raw_port_status &PORT_PLS_MASK ) !=XDEV_U3 && - (raw_port_status &PORT_PLS_MASK ) !=XDEV_RESUME ) { - port->resume_timestamp =0 ; -clear_bit (wIndex, &bus_state->resuming_ports); -usb_hcd_end_port_resume (&hcd->self, wIndex); - }if (bus_state->port_c_suspend & (1 << wIndex)) status |=USB_PORT_STAT_C_SUSPEND <<16 ;
Signature · 1 line
2.25.1
PATCH 11/11 xhci: decouple usb2 port resume and get_port_status request handling
In reply to Mathias NymanThe get port status hub request code in xhci-hub.c will complete usb2 port resume signalling if signalling has been going on for long enough.
The code that completes the resume signalling, and the code that returns the port status have gotten too intertwined, so separate them a bit.
--- drivers/usb/host/xhci-hub.c | 47 ++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 22 deletions(-)
drivers/usb/host/xhci-hub.c +25 -22
@@ -924,7 +924,7 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, }static int xhci_handle_usb2_port_link_resume (struct xhci_port *port, -u32 *status,u32 portsc, +u32 portsc,unsigned long *flags) {struct xhci_bus_state *bus_state;
@@ -939,7 +939,6 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, wIndex = port->hcd_portnum;if ((portsc &PORT_RESET ) || !(portsc &PORT_PE )) { - *status =0xffffffff ;return -EINVAL ; }/* did port event handler already start resume timing? */
@@ -973,6 +972,8 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, port->resume_timestamp =0 ;clear_bit (wIndex, &bus_state->resuming_ports); + +reinit_completion (&port->rexit_done); port->rexit_active = true;xhci_test_and_clear_bit (xhci, port,PORT_PLC );
@@ -989,7 +990,6 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port, wIndex + 1);if (!slot_id) {xhci_dbg (xhci,"slot_id is zero\n" ); - *status =0xffffffff ;return -ENODEV ; }xhci_ring_device (xhci, slot_id);
@@ -998,22 +998,19 @@ static int xhci_handle_usb2_port_link_resume(struct xhci_port *port,xhci_warn (xhci,"Port resume timed out, port %d-%d: 0x%x\n" , hcd->self.busnum, wIndex +1 , port_status); - *status |=USB_PORT_STAT_SUSPEND ; - port->rexit_active = false; +/* +* keep rexit_active set if U0 transition failed so we +* know to report PORT_STAT_SUSPEND status back to +* usbcore. It will be cleared later once the port is +* out of RESUME/U3 state +*/ }usb_hcd_end_port_resume (&hcd->self, wIndex); bus_state->port_c_suspend |=1 << wIndex; bus_state->suspended_ports &= ~(1 << wIndex); - }else { -/* -* The resume has been signaling for less than -* USB_RESUME_TIME. Report the port status as SUSPEND, -* let the usbcore check port status again and clear -* resume signaling later. -*/ - *status |=USB_PORT_STAT_SUSPEND ; } +return 0 ; }
@@ -1090,6 +1087,7 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status,struct xhci_bus_state *bus_state;u32 link_state;u32 portnum; +int err; bus_state = &port->rhub->bus_state; link_state = portsc &PORT_PLS_MASK ;
@@ -1111,8 +1109,12 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status, } }if (link_state ==XDEV_RESUME ) { -xhci_handle_usb2_port_link_resume (port, status, portsc, - flags); + err =xhci_handle_usb2_port_link_resume (port, portsc, + flags); +if (err <0 ) + *status =0xffffffff ; +else if (port->resume_timestamp || port->rexit_active) + *status |=USB_PORT_STAT_SUSPEND ; } }
@@ -1121,13 +1123,14 @@ static void xhci_get_usb2_port_status(struct xhci_port *port, u32 *status,* or resuming. Port either resumed to U0/U1/U2, disconnected, or in a * error state. Resume related variables should be cleared in all those cases. */ -if ((link_state !=XDEV_U3 && - link_state !=XDEV_RESUME ) && - (port->resume_timestamp || -test_bit (portnum, &bus_state->resuming_ports))) { - port->resume_timestamp =0 ; -clear_bit (portnum, &bus_state->resuming_ports); -usb_hcd_end_port_resume (&port->rhub->hcd->self, portnum); +if (link_state !=XDEV_U3 && link_state !=XDEV_RESUME ) { +if (port->resume_timestamp || +test_bit (portnum, &bus_state->resuming_ports)) { + port->resume_timestamp =0 ; +clear_bit (portnum, &bus_state->resuming_ports); +usb_hcd_end_port_resume (&port->rhub->hcd->self, portnum); + } + port->rexit_active =0 ; } }
Signature · 1 line
2.25.1