Re: [PATCH RFC 4/5] xHCI: PCI power management implementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, 2010-03-03 at 13:50 -0800, Sarah Sharp wrote:
> I have questions about this patch, see below.
> 
> Sarah Sharp
> 
> On Wed, Mar 03, 2010 at 06:10:45PM +0800, Libin wrote:
> > >From bf08f32104f0d4d13b3a61eea14133f0f4d1e3cc Mon Sep 17 00:00:00 2001
> > From: Libin Yang <libin.yang@xxxxxxx>
> > Date: Wed, 3 Mar 2010 16:49:57 +0800
> > Subject: [PATCH 4/5] xHCI: PCI power management implementation
> > 
> > This patch implements the PCI suspend/resume.
> > 

> > +
> > +	/* step 4: set CSS flag */
> > +	command = xhci_readl(xhci, &xhci->op_regs->command);
> > +	command |= CMD_CSS;
> > +	xhci_writel(xhci, command, &xhci->op_regs->command);
> > +	udelay(1000);
> 
> Why is there this delay here?  It's not required by the xHCI
> specification.  There is a major effort to make Linux suspend very fast,
> and it's likely this will impede that effort.  Why do you need this?

Just make sure host has saved the internal state. I will remove it.

> > +	if (handshake(xhci, &xhci->op_regs->status, STS_SAVE, 0, 10*100)) {
> > +		xhci_dbg(xhci, "WARN: xHC CMD_CSS timeout\n");
> > +		spin_unlock_irq(&xhci->lock y);
> > +		return -ETIMEDOUT;
> > +	}

> > +		/* step 2: initialize command ring buffer */
> > +		val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
> > +		val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
> > +			 (xhci->cmd_ring->first_seg->dma &
> > +			 (u64) ~CMD_RING_RSVD_BITS) |
> > +			 xhci->cmd_ring->cycle_state;
> 
> Um, what if you've enqueued some commands before you suspend the system?
> If you set the command ring dequeue pointer to the first TRB in the
> command ring, those commands will get replayed.  Instead, you need to
> set the command ring dequeue pointer to xhci->cmd_ring->dequeue.  You
> can get the DMA pointer from the virtual pointer with
> xhci_trb_virt_to_dma().

OK, I will set the it to xhci->cmd_ring->dequeue.

> Alternatively, you could mem_set() the command ring segments and move
> xhci->cmd_ring->enqueue and xhci->cmd_ring->dequeue.  But the xHCI
> internal command ring pointers and the hardware pointers have to match.
> > +		xhci_dbg(xhci, "// Setting command ring address to 0x%llx\n",
> > +				(long unsigned long) val_64);
> > +		xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
> > +		/* step 3: restore state and start state*/
> > +		/* step 3: set CRS flag */
> > +		command = xhci_readl(xhci, &xhci->op_regs->command);
> > +		command |= CMD_CRS;
> > +		xhci_writel(xhci, command, &xhci->op_regs->command);
> > +		udelay(1000);
> 
> Again, why the delay here?

OK, I will remove it.

> 
> > +		if (handshake(xhci, &xhci->op_regs->status,
> > +			      STS_RESTORE, 0, 10*100)) {
> > +			xhci_dbg(xhci, "WARN: xHC CMD_CSS timeout\n");
> > +			spin_unlock_irq(&xhci->lock);
> > +			return -ETIMEDOUT;
> > +		}
> > +		temp = xhci_readl(xhci, &xhci->op_regs->status);
> > +	}
> > +
> > +	/* If restore operation fails, reset the HC during resume */
> > +	if ((temp & STS_SRE) || hibernated) {
> > +		usb_root_hub_lost_power(hcd->self.root_hub);
> > +
> > +		retval = xhci_halt(xhci);
> > +		if (retval) {
> > +			spin_unlock_irq(&xhci->lock);
> > +			return retval;
> > +		}
> > +
> > +		xhci_dbg(xhci, "Resetting HCD\n");
> > +		/* Reset the internal HC memory state and registers. */
> > +		retval = xhci_reset(xhci);
> > +		if (retval) {
> > +			spin_unlock_irq(&xhci->lock);
> > +			return retval;
> > +		}
> > +
> > +		xhci_dbg(xhci, "Reset complete\n");
> > +
> > +		/*
> > +		 * Initialize HCD and host controller data structures.
> > +		 */
> 
> Why not just call xhci_mem_cleanup() and then xhci_mem_init() here?  Or
> break out the shared code between xhci_mem_init() and this function
> instead of copying and pasting it?

There are some codes in xhci_mem_init() not necessary. So to speed up
the reset, I just copy out some of the xhci_mem_init() code.

> 
> > +		val = HCS_MAX_SLOTS(xhci_readl(xhci,
> > +					       &xhci->cap_regs->hcs_params1));
> > +		val2 = xhci_readl(xhci, &xhci->op_regs->config_reg);
> > +		val |= (val2 & ~HCS_SLOTS_MASK);
> > +		xhci_writel(xhci, val, &xhci->op_regs->config_reg);
> > +
> > +		xhci_write_64(xhci, xhci->dcbaa->dma,
> > +			      &xhci->op_regs->dcbaa_ptr);
> > +
> > +		/*
> > +		 * Set the address in the Command Ring Control register
> > +		 */
> > +		val_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
> > +		val_64 = (val_64 & (u64) CMD_RING_RSVD_BITS) |
> > +			 (xhci->cmd_ring->first_seg->dma &
> > +			 (u64) ~CMD_RING_RSVD_BITS) |
> > +			 xhci->cmd_ring->cycle_state;
> > +		xhci_dbg(xhci,
> > +			"// Setting command ring address to 0x%x\n", val);
> > +			xhci_write_64(xhci, val_64, &xhci->op_regs->cmd_ring);
> > +
> > +		xhci_dbg(xhci, "// Allocating event ring\n");
> > +		xhci_ring_free(xhci, xhci->event_ring);
> > +		xhci->event_ring = NULL;
> > +		xhci->event_ring = xhci_ring_alloc(xhci, ERST_NUM_SEGS,
> > +						   false, GFP_KERNEL);
> > +		if (!xhci->event_ring)
> > +			goto fail;
> 
> Why are you reallocating the event ring?  Why not just zero it and reset
> the internal event ring pointers?

At the beginning when I wrote the code, I did not zero the event ring. I
just set the internal event ring pointer to the next address. It fails.
So I reallocated the event ring later and it works. It is a good
suggestion to zero the ring. I will try later.

> 
> > +
> > +		memset(xhci->erst.entries, 0,
> > +		       sizeof(struct xhci_erst_entry)*ERST_NUM_SEGS);
> > +
> > +		for (val = 0, seg = xhci->event_ring->first_seg;
> > +		     val < ERST_NUM_SEGS; val++) {
> > +			struct xhci_erst_entry *entry =
> > +				&xhci->erst.entries[val];
> > +			entry->seg_addr = seg->dma;
> > +			entry->seg_size = TRBS_PER_SEGMENT;
> > +			entry->rsvd = 0;
> > +			seg = seg->next;
> > +		}
> > +
> > +		val = xhci_readl(xhci, &xhci->ir_set->erst_size);
> > +		val &= ERST_SIZE_MASK;
> > +		val |= ERST_NUM_SEGS;
> > +		xhci_writel(xhci, val, &xhci->ir_set->erst_size);
> > +
> > +		xhci_dbg(xhci,
> > +			"// Set ERST entries to point to event ring.\n");
> > +		/* set the segment table base address */
> > +		xhci_dbg(xhci,
> > +			"// Set ERST base address for ir_set 0 = 0x%llx\n",
> > +			(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);
> > +		xhci_write_64(xhci, val_64, &xhci->ir_set->erst_base);
> > +
> > +		/* Set the event ring dequeue address */
> > +		xhci_set_hc_event_deq(xhci);
> > +		xhci_dbg(xhci, "Wrote ERST address to ir_set 0.\n");
> > +
> > +		for (i = 0; i < MAX_HC_SLOTS; ++i) {
> > +			xhci_free_virt_device(xhci, i);
> > +			xhci->resume_done[i] = 0;
> > +		}
> > +
> > +		goto out;
> > +fail:
> > +		xhci_warn(xhci, "Couldn't initialize memory\n");
> > +		spin_unlock_irq(&xhci->lock);
> > +		xhci_mem_cleanup(xhci);
> > +		return -ENOMEM;
> > +out:
> 
> These goto statements are a little bit wacky.  Can you rewrite the code
> without them?  The label fail is only used if the event ring didn't get
> reallocated, so move it into that if statement instead.  Then the code
> will be cleaner looking.

OK, I will change code.

> > +
> > +		if (!pci_set_mwi(pdev))
> > +			xhci_dbg(xhci, "MWI active\n");
> > +	xhci_dbg(xhci, "TRACE: exit xhci_suspend\n");
> > +
> > +bail:
> > +
> > +	xhci_dbg(xhci, "TRACE: xhci_pci_suspend: EXIT, rc = %d\n", rc);
> 
> Is there a reason these debug statements have TRACE at the beginning?

I will remove it.

> > +	return rc;
> > +}
> > +


--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux