diff options
Diffstat (limited to 'arch/powerpc/sysdev/xive/common.c')
| -rw-r--r-- | arch/powerpc/sysdev/xive/common.c | 146 | 
1 files changed, 111 insertions, 35 deletions
diff --git a/arch/powerpc/sysdev/xive/common.c b/arch/powerpc/sysdev/xive/common.c index 1cdb39575eae..df832b09e3e9 100644 --- a/arch/powerpc/sysdev/xive/common.c +++ b/arch/powerpc/sysdev/xive/common.c @@ -135,7 +135,7 @@ static u32 xive_read_eq(struct xive_q *q, bool just_peek)  static u32 xive_scan_interrupts(struct xive_cpu *xc, bool just_peek)  {  	u32 irq = 0; -	u8 prio; +	u8 prio = 0;  	/* Find highest pending priority */  	while (xc->pending_prio != 0) { @@ -148,8 +148,19 @@ static u32 xive_scan_interrupts(struct xive_cpu *xc, bool just_peek)  		irq = xive_read_eq(&xc->queue[prio], just_peek);  		/* Found something ? That's it */ -		if (irq) -			break; +		if (irq) { +			if (just_peek || irq_to_desc(irq)) +				break; +			/* +			 * We should never get here; if we do then we must +			 * have failed to synchronize the interrupt properly +			 * when shutting it down. +			 */ +			pr_crit("xive: got interrupt %d without descriptor, dropping\n", +				irq); +			WARN_ON(1); +			continue; +		}  		/* Clear pending bits */  		xc->pending_prio &= ~(1 << prio); @@ -185,7 +196,7 @@ static u32 xive_scan_interrupts(struct xive_cpu *xc, bool just_peek)  /*   * This is used to perform the magic loads from an ESB - * described in xive.h + * described in xive-regs.h   */  static notrace u8 xive_esb_read(struct xive_irq_data *xd, u32 offset)  { @@ -226,26 +237,61 @@ static notrace void xive_dump_eq(const char *name, struct xive_q *q)  	i0 = be32_to_cpup(q->qpage + idx);  	idx = (idx + 1) & q->msk;  	i1 = be32_to_cpup(q->qpage + idx); -	xmon_printf("  %s Q T=%d %08x %08x ...\n", name, -		    q->toggle, i0, i1); +	xmon_printf("%s idx=%d T=%d %08x %08x ...", name, +		     q->idx, q->toggle, i0, i1);  }  notrace void xmon_xive_do_dump(int cpu)  {  	struct xive_cpu *xc = per_cpu(xive_cpu, cpu); -	xmon_printf("XIVE state for CPU %d:\n", cpu); -	xmon_printf("  pp=%02x cppr=%02x\n", xc->pending_prio, xc->cppr); -	xive_dump_eq("IRQ", &xc->queue[xive_irq_priority]); +	xmon_printf("CPU %d:", cpu); +	if (xc) { +		xmon_printf("pp=%02x CPPR=%02x ", xc->pending_prio, xc->cppr); +  #ifdef CONFIG_SMP -	{ -		u64 val = xive_esb_read(&xc->ipi_data, XIVE_ESB_GET); -		xmon_printf("  IPI state: %x:%c%c\n", xc->hw_ipi, -			val & XIVE_ESB_VAL_P ? 'P' : 'p', -			val & XIVE_ESB_VAL_Q ? 'Q' : 'q'); -	} +		{ +			u64 val = xive_esb_read(&xc->ipi_data, XIVE_ESB_GET); + +			xmon_printf("IPI=0x%08x PQ=%c%c ", xc->hw_ipi, +				    val & XIVE_ESB_VAL_P ? 'P' : '-', +				    val & XIVE_ESB_VAL_Q ? 'Q' : '-'); +		}  #endif +		xive_dump_eq("EQ", &xc->queue[xive_irq_priority]); +	} +	xmon_printf("\n"); +} + +int xmon_xive_get_irq_config(u32 hw_irq, struct irq_data *d) +{ +	int rc; +	u32 target; +	u8 prio; +	u32 lirq; + +	rc = xive_ops->get_irq_config(hw_irq, &target, &prio, &lirq); +	if (rc) { +		xmon_printf("IRQ 0x%08x : no config rc=%d\n", hw_irq, rc); +		return rc; +	} + +	xmon_printf("IRQ 0x%08x : target=0x%x prio=%02x lirq=0x%x ", +		    hw_irq, target, prio, lirq); + +	if (d) { +		struct xive_irq_data *xd = irq_data_get_irq_handler_data(d); +		u64 val = xive_esb_read(xd, XIVE_ESB_GET); + +		xmon_printf("PQ=%c%c", +			    val & XIVE_ESB_VAL_P ? 'P' : '-', +			    val & XIVE_ESB_VAL_Q ? 'Q' : '-'); +	} + +	xmon_printf("\n"); +	return 0;  } +  #endif /* CONFIG_XMON */  static unsigned int xive_get_irq(void) @@ -307,6 +353,7 @@ static void xive_do_queue_eoi(struct xive_cpu *xc)   */  static void xive_do_source_eoi(u32 hw_irq, struct xive_irq_data *xd)  { +	xd->stale_p = false;  	/* If the XIVE supports the new "store EOI facility, use it */  	if (xd->flags & XIVE_IRQ_FLAG_STORE_EOI)  		xive_esb_write(xd, XIVE_ESB_STORE_EOI, 0); @@ -350,7 +397,7 @@ static void xive_do_source_eoi(u32 hw_irq, struct xive_irq_data *xd)  	}  } -/* irq_chip eoi callback */ +/* irq_chip eoi callback, called with irq descriptor lock held */  static void xive_irq_eoi(struct irq_data *d)  {  	struct xive_irq_data *xd = irq_data_get_irq_handler_data(d); @@ -366,6 +413,8 @@ static void xive_irq_eoi(struct irq_data *d)  	if (!irqd_irq_disabled(d) && !irqd_is_forwarded_to_vcpu(d) &&  	    !(xd->flags & XIVE_IRQ_NO_EOI))  		xive_do_source_eoi(irqd_to_hwirq(d), xd); +	else +		xd->stale_p = true;  	/*  	 * Clear saved_p to indicate that it's no longer occupying @@ -397,11 +446,16 @@ static void xive_do_source_set_mask(struct xive_irq_data *xd,  	 */  	if (mask) {  		val = xive_esb_read(xd, XIVE_ESB_SET_PQ_01); -		xd->saved_p = !!(val & XIVE_ESB_VAL_P); -	} else if (xd->saved_p) +		if (!xd->stale_p && !!(val & XIVE_ESB_VAL_P)) +			xd->saved_p = true; +		xd->stale_p = false; +	} else if (xd->saved_p) {  		xive_esb_read(xd, XIVE_ESB_SET_PQ_10); -	else +		xd->saved_p = false; +	} else {  		xive_esb_read(xd, XIVE_ESB_SET_PQ_00); +		xd->stale_p = false; +	}  }  /* @@ -541,6 +595,8 @@ static unsigned int xive_irq_startup(struct irq_data *d)  	unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);  	int target, rc; +	xd->saved_p = false; +	xd->stale_p = false;  	pr_devel("xive_irq_startup: irq %d [0x%x] data @%p\n",  		 d->irq, hw_irq, d); @@ -587,6 +643,7 @@ static unsigned int xive_irq_startup(struct irq_data *d)  	return 0;  } +/* called with irq descriptor lock held */  static void xive_irq_shutdown(struct irq_data *d)  {  	struct xive_irq_data *xd = irq_data_get_irq_handler_data(d); @@ -602,16 +659,6 @@ static void xive_irq_shutdown(struct irq_data *d)  	xive_do_source_set_mask(xd, true);  	/* -	 * The above may have set saved_p. We clear it otherwise it -	 * will prevent re-enabling later on. It is ok to forget the -	 * fact that the interrupt might be in a queue because we are -	 * accounting that already in xive_dec_target_count() and will -	 * be re-routing it to a new queue with proper accounting when -	 * it's started up again -	 */ -	xd->saved_p = false; - -	/*  	 * Mask the interrupt in HW in the IVT/EAS and set the number  	 * to be the "bad" IRQ number  	 */ @@ -797,6 +844,10 @@ static int xive_irq_retrigger(struct irq_data *d)  	return 1;  } +/* + * Caller holds the irq descriptor lock, so this won't be called + * concurrently with xive_get_irqchip_state on the same interrupt. + */  static int xive_irq_set_vcpu_affinity(struct irq_data *d, void *state)  {  	struct xive_irq_data *xd = irq_data_get_irq_handler_data(d); @@ -820,6 +871,10 @@ static int xive_irq_set_vcpu_affinity(struct irq_data *d, void *state)  		/* Set it to PQ=10 state to prevent further sends */  		pq = xive_esb_read(xd, XIVE_ESB_SET_PQ_10); +		if (!xd->stale_p) { +			xd->saved_p = !!(pq & XIVE_ESB_VAL_P); +			xd->stale_p = !xd->saved_p; +		}  		/* No target ? nothing to do */  		if (xd->target == XIVE_INVALID_TARGET) { @@ -827,7 +882,7 @@ static int xive_irq_set_vcpu_affinity(struct irq_data *d, void *state)  			 * An untargetted interrupt should have been  			 * also masked at the source  			 */ -			WARN_ON(pq & 2); +			WARN_ON(xd->saved_p);  			return 0;  		} @@ -847,9 +902,8 @@ static int xive_irq_set_vcpu_affinity(struct irq_data *d, void *state)  		 * This saved_p is cleared by the host EOI, when we know  		 * for sure the queue slot is no longer in use.  		 */ -		if (pq & 2) { -			pq = xive_esb_read(xd, XIVE_ESB_SET_PQ_11); -			xd->saved_p = true; +		if (xd->saved_p) { +			xive_esb_read(xd, XIVE_ESB_SET_PQ_11);  			/*  			 * Sync the XIVE source HW to ensure the interrupt @@ -862,8 +916,7 @@ static int xive_irq_set_vcpu_affinity(struct irq_data *d, void *state)  			 */  			if (xive_ops->sync_source)  				xive_ops->sync_source(hw_irq); -		} else -			xd->saved_p = false; +		}  	} else {  		irqd_clr_forwarded_to_vcpu(d); @@ -914,6 +967,23 @@ static int xive_irq_set_vcpu_affinity(struct irq_data *d, void *state)  	return 0;  } +/* Called with irq descriptor lock held. */ +static int xive_get_irqchip_state(struct irq_data *data, +				  enum irqchip_irq_state which, bool *state) +{ +	struct xive_irq_data *xd = irq_data_get_irq_handler_data(data); + +	switch (which) { +	case IRQCHIP_STATE_ACTIVE: +		*state = !xd->stale_p && +			 (xd->saved_p || +			  !!(xive_esb_read(xd, XIVE_ESB_GET) & XIVE_ESB_VAL_P)); +		return 0; +	default: +		return -EINVAL; +	} +} +  static struct irq_chip xive_irq_chip = {  	.name = "XIVE-IRQ",  	.irq_startup = xive_irq_startup, @@ -925,6 +995,7 @@ static struct irq_chip xive_irq_chip = {  	.irq_set_type = xive_irq_set_type,  	.irq_retrigger = xive_irq_retrigger,  	.irq_set_vcpu_affinity = xive_irq_set_vcpu_affinity, +	.irq_get_irqchip_state = xive_get_irqchip_state,  };  bool is_xive_irq(struct irq_chip *chip) @@ -1338,6 +1409,11 @@ static void xive_flush_cpu_queue(unsigned int cpu, struct xive_cpu *xc)  		xd = irq_desc_get_handler_data(desc);  		/* +		 * Clear saved_p to indicate that it's no longer pending +		 */ +		xd->saved_p = false; + +		/*  		 * For LSIs, we EOI, this will cause a resend if it's  		 * still asserted. Otherwise do an MSI retrigger.  		 */  |