1*d17bf24eSQais Yousef /* 2*d17bf24eSQais Yousef * linux/kernel/irq/ipi.c 3*d17bf24eSQais Yousef * 4*d17bf24eSQais Yousef * Copyright (C) 2015 Imagination Technologies Ltd 5*d17bf24eSQais Yousef * Author: Qais Yousef <qais.yousef@imgtec.com> 6*d17bf24eSQais Yousef * 7*d17bf24eSQais Yousef * This file contains driver APIs to the IPI subsystem. 8*d17bf24eSQais Yousef */ 9*d17bf24eSQais Yousef 10*d17bf24eSQais Yousef #define pr_fmt(fmt) "genirq/ipi: " fmt 11*d17bf24eSQais Yousef 12*d17bf24eSQais Yousef #include <linux/irqdomain.h> 13*d17bf24eSQais Yousef #include <linux/irq.h> 14*d17bf24eSQais Yousef 15*d17bf24eSQais Yousef /** 16*d17bf24eSQais Yousef * irq_reserve_ipi() - Setup an IPI to destination cpumask 17*d17bf24eSQais Yousef * @domain: IPI domain 18*d17bf24eSQais Yousef * @dest: cpumask of cpus which can receive the IPI 19*d17bf24eSQais Yousef * 20*d17bf24eSQais Yousef * Allocate a virq that can be used to send IPI to any CPU in dest mask. 21*d17bf24eSQais Yousef * 22*d17bf24eSQais Yousef * On success it'll return linux irq number and 0 on failure 23*d17bf24eSQais Yousef */ 24*d17bf24eSQais Yousef unsigned int irq_reserve_ipi(struct irq_domain *domain, 25*d17bf24eSQais Yousef const struct cpumask *dest) 26*d17bf24eSQais Yousef { 27*d17bf24eSQais Yousef unsigned int nr_irqs, offset; 28*d17bf24eSQais Yousef struct irq_data *data; 29*d17bf24eSQais Yousef int virq, i; 30*d17bf24eSQais Yousef 31*d17bf24eSQais Yousef if (!domain ||!irq_domain_is_ipi(domain)) { 32*d17bf24eSQais Yousef pr_warn("Reservation on a non IPI domain\n"); 33*d17bf24eSQais Yousef return 0; 34*d17bf24eSQais Yousef } 35*d17bf24eSQais Yousef 36*d17bf24eSQais Yousef if (!cpumask_subset(dest, cpu_possible_mask)) { 37*d17bf24eSQais Yousef pr_warn("Reservation is not in possible_cpu_mask\n"); 38*d17bf24eSQais Yousef return 0; 39*d17bf24eSQais Yousef } 40*d17bf24eSQais Yousef 41*d17bf24eSQais Yousef nr_irqs = cpumask_weight(dest); 42*d17bf24eSQais Yousef if (!nr_irqs) { 43*d17bf24eSQais Yousef pr_warn("Reservation for empty destination mask\n"); 44*d17bf24eSQais Yousef return 0; 45*d17bf24eSQais Yousef } 46*d17bf24eSQais Yousef 47*d17bf24eSQais Yousef if (irq_domain_is_ipi_single(domain)) { 48*d17bf24eSQais Yousef /* 49*d17bf24eSQais Yousef * If the underlying implementation uses a single HW irq on 50*d17bf24eSQais Yousef * all cpus then we only need a single Linux irq number for 51*d17bf24eSQais Yousef * it. We have no restrictions vs. the destination mask. The 52*d17bf24eSQais Yousef * underlying implementation can deal with holes nicely. 53*d17bf24eSQais Yousef */ 54*d17bf24eSQais Yousef nr_irqs = 1; 55*d17bf24eSQais Yousef offset = 0; 56*d17bf24eSQais Yousef } else { 57*d17bf24eSQais Yousef unsigned int next; 58*d17bf24eSQais Yousef 59*d17bf24eSQais Yousef /* 60*d17bf24eSQais Yousef * The IPI requires a seperate HW irq on each CPU. We require 61*d17bf24eSQais Yousef * that the destination mask is consecutive. If an 62*d17bf24eSQais Yousef * implementation needs to support holes, it can reserve 63*d17bf24eSQais Yousef * several IPI ranges. 64*d17bf24eSQais Yousef */ 65*d17bf24eSQais Yousef offset = cpumask_first(dest); 66*d17bf24eSQais Yousef /* 67*d17bf24eSQais Yousef * Find a hole and if found look for another set bit after the 68*d17bf24eSQais Yousef * hole. For now we don't support this scenario. 69*d17bf24eSQais Yousef */ 70*d17bf24eSQais Yousef next = cpumask_next_zero(offset, dest); 71*d17bf24eSQais Yousef if (next < nr_cpu_ids) 72*d17bf24eSQais Yousef next = cpumask_next(next, dest); 73*d17bf24eSQais Yousef if (next < nr_cpu_ids) { 74*d17bf24eSQais Yousef pr_warn("Destination mask has holes\n"); 75*d17bf24eSQais Yousef return 0; 76*d17bf24eSQais Yousef } 77*d17bf24eSQais Yousef } 78*d17bf24eSQais Yousef 79*d17bf24eSQais Yousef virq = irq_domain_alloc_descs(-1, nr_irqs, 0, NUMA_NO_NODE); 80*d17bf24eSQais Yousef if (virq <= 0) { 81*d17bf24eSQais Yousef pr_warn("Can't reserve IPI, failed to alloc descs\n"); 82*d17bf24eSQais Yousef return 0; 83*d17bf24eSQais Yousef } 84*d17bf24eSQais Yousef 85*d17bf24eSQais Yousef virq = __irq_domain_alloc_irqs(domain, virq, nr_irqs, NUMA_NO_NODE, 86*d17bf24eSQais Yousef (void *) dest, true); 87*d17bf24eSQais Yousef 88*d17bf24eSQais Yousef if (virq <= 0) { 89*d17bf24eSQais Yousef pr_warn("Can't reserve IPI, failed to alloc hw irqs\n"); 90*d17bf24eSQais Yousef goto free_descs; 91*d17bf24eSQais Yousef } 92*d17bf24eSQais Yousef 93*d17bf24eSQais Yousef for (i = 0; i < nr_irqs; i++) { 94*d17bf24eSQais Yousef data = irq_get_irq_data(virq + i); 95*d17bf24eSQais Yousef cpumask_copy(data->common->affinity, dest); 96*d17bf24eSQais Yousef data->common->ipi_offset = offset; 97*d17bf24eSQais Yousef } 98*d17bf24eSQais Yousef return virq; 99*d17bf24eSQais Yousef 100*d17bf24eSQais Yousef free_descs: 101*d17bf24eSQais Yousef irq_free_descs(virq, nr_irqs); 102*d17bf24eSQais Yousef return 0; 103*d17bf24eSQais Yousef } 104*d17bf24eSQais Yousef 105*d17bf24eSQais Yousef /** 106*d17bf24eSQais Yousef * irq_destroy_ipi() - unreserve an IPI that was previously allocated 107*d17bf24eSQais Yousef * @irq: linux irq number to be destroyed 108*d17bf24eSQais Yousef * 109*d17bf24eSQais Yousef * Return the IPIs allocated with irq_reserve_ipi() to the system destroying 110*d17bf24eSQais Yousef * all virqs associated with them. 111*d17bf24eSQais Yousef */ 112*d17bf24eSQais Yousef void irq_destroy_ipi(unsigned int irq) 113*d17bf24eSQais Yousef { 114*d17bf24eSQais Yousef struct irq_data *data = irq_get_irq_data(irq); 115*d17bf24eSQais Yousef struct cpumask *ipimask = data ? irq_data_get_affinity_mask(data) : NULL; 116*d17bf24eSQais Yousef struct irq_domain *domain; 117*d17bf24eSQais Yousef unsigned int nr_irqs; 118*d17bf24eSQais Yousef 119*d17bf24eSQais Yousef if (!irq || !data || !ipimask) 120*d17bf24eSQais Yousef return; 121*d17bf24eSQais Yousef 122*d17bf24eSQais Yousef domain = data->domain; 123*d17bf24eSQais Yousef if (WARN_ON(domain == NULL)) 124*d17bf24eSQais Yousef return; 125*d17bf24eSQais Yousef 126*d17bf24eSQais Yousef if (!irq_domain_is_ipi(domain)) { 127*d17bf24eSQais Yousef pr_warn("Trying to destroy a non IPI domain!\n"); 128*d17bf24eSQais Yousef return; 129*d17bf24eSQais Yousef } 130*d17bf24eSQais Yousef 131*d17bf24eSQais Yousef if (irq_domain_is_ipi_per_cpu(domain)) 132*d17bf24eSQais Yousef nr_irqs = cpumask_weight(ipimask); 133*d17bf24eSQais Yousef else 134*d17bf24eSQais Yousef nr_irqs = 1; 135*d17bf24eSQais Yousef 136*d17bf24eSQais Yousef irq_domain_free_irqs(irq, nr_irqs); 137*d17bf24eSQais Yousef } 138