Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8725aa2a authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "xhci: hsic: Offload event handling to softirq context"

parents a1181e34 fd267ac9
Loading
Loading
Loading
Loading
+116 −1
Original line number Diff line number Diff line
@@ -133,6 +133,9 @@ struct mxhci_hsic_hcd {
	uint32_t		wakeup_int_cnt;
	uint32_t		pwr_evt_irq_inlpm;
	struct pinctrl		*hsic_pinctrl;
	struct tasklet_struct	bh;
	unsigned		handled_event_cnt;
	unsigned		cpu_yield_cnt;
};

#define SYNOPSIS_DWC3_VENDOR	0x5533
@@ -164,6 +167,9 @@ module_param_named(ep_addr_rxdbg_mask,
module_param_named(ep_addr_txdbg_mask,
			dbg_hsic.outep_log_mask, uint, S_IRUGO | S_IWUSR);

static int max_event_to_handle = 20;
module_param(max_event_to_handle, uint, S_IRUGO | S_IWUSR);

static int mxhci_hsic_data_events_show(struct seq_file *s, void *unused)
{
	unsigned long	flags;
@@ -1076,6 +1082,66 @@ unlock:
		spin_unlock_irqrestore(&xhci->lock, flags);
}

static irqreturn_t mxhci_irq(struct usb_hcd *hcd)
{
	struct mxhci_hsic_hcd *mxhci = hcd_to_hsic(hcd->primary_hcd);
	struct xhci_hcd *xhci = mxhci->xhci;
	u32 status;
	u64 temp_64;

	spin_lock(&xhci->lock);
	/* Check if the xHC generated the interrupt, or the irq is shared */
	status = xhci_readl(xhci, &xhci->op_regs->status);
	if (status == 0xffffffff) {
		spin_unlock(&xhci->lock);
		return IRQ_HANDLED;
	}

	if (!(status & STS_EINT)) {
		spin_unlock(&xhci->lock);
		return IRQ_NONE;
	}
	if (status & STS_FATAL) {
		xhci_warn(xhci, "WARNING: Host System Error\n");
		xhci_halt(xhci);
		spin_unlock(&xhci->lock);
		return IRQ_HANDLED;
	}

	/*
	 * Clear the op reg interrupt status first,
	 * so we can receive interrupts from other MSI-X interrupters.
	 * Write 1 to clear the interrupt status.
	 */
	status |= STS_EINT;
	xhci_writel(xhci, status, &xhci->op_regs->status);

	if (hcd->irq) {
		u32 irq_pending;
		/* Acknowledge the PCI interrupt */
		irq_pending = xhci_readl(xhci, &xhci->ir_set->irq_pending);
		irq_pending |= IMAN_IP;
		xhci_writel(xhci, irq_pending, &xhci->ir_set->irq_pending);
	}

	if (xhci->xhc_state & XHCI_STATE_DYING) {
		xhci_dbg(xhci, "xHCI dying, ignoring interrupt.\n");
		/* Clear the event handler busy flag (RW1C);
		 * the event ring should be empty.
		 */
		temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
		xhci_write_64(xhci, temp_64 | ERST_EHB,
				&xhci->ir_set->erst_dequeue);
		spin_unlock(&xhci->lock);
		return IRQ_HANDLED;
	}
	spin_unlock(&xhci->lock);

	tasklet_schedule(&mxhci->bh);

	return IRQ_HANDLED;
}

static struct hc_driver mxhci_hsic_hc_driver = {
	.description =		"xhci-hcd",
	.product_desc =		"Qualcomm xHCI Host Controller using HSIC",
@@ -1083,7 +1149,7 @@ static struct hc_driver mxhci_hsic_hc_driver = {
	/*
	 * generic hardware linkage
	 */
	.irq =			xhci_irq,
	.irq =			mxhci_irq,
	.flags =		HCD_MEMORY | HCD_USB3,

	/*
@@ -1131,6 +1197,51 @@ static struct hc_driver mxhci_hsic_hc_driver = {
	.udev_enum_done =	mxhci_hsic_udev_enum_done,
};

static void mxhci_irq_bh(unsigned long param)
{
	struct mxhci_hsic_hcd *mxhci = (struct mxhci_hsic_hcd *)param;
	struct xhci_hcd *xhci = mxhci->xhci;
	u64 temp_64;
	union xhci_trb *event_ring_deq;
	dma_addr_t deq;
	unsigned long flags;
	unsigned event_cnt = 0;
	int schedule_again = 0;

	spin_lock_irqsave(&xhci->lock, flags);
	event_ring_deq = xhci->event_ring->dequeue;

	while (event_cnt < max_event_to_handle) {
		schedule_again = xhci_handle_event(xhci);
		event_cnt++;
	}

	temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue);
	/* If necessary, update the HW's version of the event ring deq ptr. */
	if (event_ring_deq != xhci->event_ring->dequeue) {
		deq = xhci_trb_virt_to_dma(xhci->event_ring->deq_seg,
				xhci->event_ring->dequeue);
		if (deq == 0)
			xhci_warn(xhci,
				"WARN wrong SW event ring dequeue ptr.\n");
		/* Update HC event ring dequeue pointer */
		temp_64 &= ERST_PTR_MASK;
		temp_64 |= ((u64) deq & (u64) ~ERST_PTR_MASK);
	}

	/* Clear the event handler busy flag to get irq ; reloads imod */
	temp_64 |= ERST_EHB;
	xhci_write_64(xhci, temp_64, &xhci->ir_set->erst_dequeue);
	spin_unlock_irqrestore(&xhci->lock, flags);

	mxhci->handled_event_cnt += event_cnt - 1;
	mxhci->cpu_yield_cnt++;

	if ((schedule_again > 0) && !mxhci->xhci_remove_flag)
		tasklet_schedule(&mxhci->bh);
}


static ssize_t config_imod_store(struct device *pdev,
		struct device_attribute *attr, const char *buff, size_t size)
{
@@ -1292,6 +1403,8 @@ static int mxhci_hsic_probe(struct platform_device *pdev)
	mxhci->dev = &pdev->dev;
	mxhci->xhci_remove_flag = false;
	mxhci->xhci_shutdown_flag = false;
	mxhci->bh.func = mxhci_irq_bh;
	mxhci->bh.data = (unsigned long)mxhci;

	/* Get pinctrl if target uses pinctrl */
	mxhci->hsic_pinctrl = devm_pinctrl_get(&pdev->dev);
@@ -1589,6 +1702,8 @@ static int mxhci_hsic_remove(struct platform_device *pdev)
	pm_runtime_disable(&hcd->self.root_hub->dev);
	pm_runtime_barrier(&hcd->self.root_hub->dev);

	tasklet_kill(&mxhci->bh);

	usb_remove_hcd(xhci->shared_hcd);
	usb_put_hcd(xhci->shared_hcd);

+1 −1
Original line number Diff line number Diff line
@@ -2685,7 +2685,7 @@ cleanup:
 * Returns >0 for "possibly more events to process" (caller should call again),
 * otherwise 0 if done.  In future, <0 returns should indicate error code.
 */
static int xhci_handle_event(struct xhci_hcd *xhci)
int xhci_handle_event(struct xhci_hcd *xhci)
{
	union xhci_trb *event;
	int update_ptrs = 1;
+1 −0
Original line number Diff line number Diff line
@@ -1909,6 +1909,7 @@ int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command,
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
		unsigned int ep_index, unsigned int stream_id);
union xhci_trb *xhci_find_next_enqueue(struct xhci_ring *ring);
int xhci_handle_event(struct xhci_hcd *xhci);

/* xHCI roothub code */
void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,