diff options
Diffstat (limited to 'drivers/clocksource/timer-clint.c')
| -rw-r--r-- | drivers/clocksource/timer-clint.c | 65 | 
1 files changed, 49 insertions, 16 deletions
diff --git a/drivers/clocksource/timer-clint.c b/drivers/clocksource/timer-clint.c index 6cfe2ab73eb0..9a55e733ae99 100644 --- a/drivers/clocksource/timer-clint.c +++ b/drivers/clocksource/timer-clint.c @@ -17,6 +17,9 @@  #include <linux/sched_clock.h>  #include <linux/io-64-nonatomic-lo-hi.h>  #include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> +#include <linux/irqdomain.h>  #include <linux/of_irq.h>  #include <linux/smp.h>  #include <linux/timex.h> @@ -31,6 +34,7 @@  /* CLINT manages IPI and Timer for RISC-V M-mode  */  static u32 __iomem *clint_ipi_base; +static unsigned int clint_ipi_irq;  static u64 __iomem *clint_timer_cmp;  static u64 __iomem *clint_timer_val;  static unsigned long clint_timer_freq; @@ -41,12 +45,10 @@ u64 __iomem *clint_time_val;  EXPORT_SYMBOL(clint_time_val);  #endif -static void clint_send_ipi(const struct cpumask *target) +#ifdef CONFIG_SMP +static void clint_send_ipi(unsigned int cpu)  { -	unsigned int cpu; - -	for_each_cpu(cpu, target) -		writel(1, clint_ipi_base + cpuid_to_hartid_map(cpu)); +	writel(1, clint_ipi_base + cpuid_to_hartid_map(cpu));  }  static void clint_clear_ipi(void) @@ -54,10 +56,18 @@ static void clint_clear_ipi(void)  	writel(0, clint_ipi_base + cpuid_to_hartid_map(smp_processor_id()));  } -static struct riscv_ipi_ops clint_ipi_ops = { -	.ipi_inject = clint_send_ipi, -	.ipi_clear = clint_clear_ipi, -}; +static void clint_ipi_interrupt(struct irq_desc *desc) +{ +	struct irq_chip *chip = irq_desc_get_chip(desc); + +	chained_irq_enter(chip, desc); + +	clint_clear_ipi(); +	ipi_mux_process(); + +	chained_irq_exit(chip, desc); +} +#endif  #ifdef CONFIG_64BIT  #define clint_get_cycles()	readq_relaxed(clint_timer_val) @@ -125,12 +135,19 @@ static int clint_timer_starting_cpu(unsigned int cpu)  	enable_percpu_irq(clint_timer_irq,  			  irq_get_trigger_type(clint_timer_irq)); +	enable_percpu_irq(clint_ipi_irq, +			  irq_get_trigger_type(clint_ipi_irq));  	return 0;  }  static int clint_timer_dying_cpu(unsigned int cpu)  {  	disable_percpu_irq(clint_timer_irq); +	/* +	 * Don't disable IPI when CPU goes offline because +	 * the masking/unmasking of virtual IPIs is done +	 * via generic IPI-Mux +	 */  	return 0;  } @@ -170,6 +187,12 @@ static int __init clint_timer_init_dt(struct device_node *np)  			return -ENODEV;  		} +		/* Find parent irq domain and map ipi irq */ +		if (!clint_ipi_irq && +		    oirq.args[0] == RV_IRQ_SOFT && +		    irq_find_host(oirq.np)) +			clint_ipi_irq = irq_of_parse_and_map(np, i); +  		/* Find parent irq domain and map timer irq */  		if (!clint_timer_irq &&  		    oirq.args[0] == RV_IRQ_TIMER && @@ -177,9 +200,9 @@ static int __init clint_timer_init_dt(struct device_node *np)  			clint_timer_irq = irq_of_parse_and_map(np, i);  	} -	/* If CLINT timer irq not found then fail */ -	if (!clint_timer_irq) { -		pr_err("%pOFP: timer irq not found\n", np); +	/* If CLINT ipi or timer irq not found then fail */ +	if (!clint_ipi_irq || !clint_timer_irq) { +		pr_err("%pOFP: ipi/timer irq not found\n", np);  		return -ENODEV;  	} @@ -219,6 +242,19 @@ static int __init clint_timer_init_dt(struct device_node *np)  		goto fail_iounmap;  	} +#ifdef CONFIG_SMP +	rc = ipi_mux_create(BITS_PER_BYTE, clint_send_ipi); +	if (rc <= 0) { +		pr_err("unable to create muxed IPIs\n"); +		rc = (rc < 0) ? rc : -ENODEV; +		goto fail_free_irq; +	} + +	irq_set_chained_handler(clint_ipi_irq, clint_ipi_interrupt); +	riscv_ipi_set_virq_range(rc, BITS_PER_BYTE, true); +	clint_clear_ipi(); +#endif +  	rc = cpuhp_setup_state(CPUHP_AP_CLINT_TIMER_STARTING,  				"clockevents/clint/timer:starting",  				clint_timer_starting_cpu, @@ -228,13 +264,10 @@ static int __init clint_timer_init_dt(struct device_node *np)  		goto fail_free_irq;  	} -	riscv_set_ipi_ops(&clint_ipi_ops); -	clint_clear_ipi(); -  	return 0;  fail_free_irq: -	free_irq(clint_timer_irq, &clint_clock_event); +	free_percpu_irq(clint_timer_irq, &clint_clock_event);  fail_iounmap:  	iounmap(base);  	return rc;  |