/* * linux/kernel/irq/ipi.c * * Copyright (C) 2015 Imagination Technologies Ltd * Author: Qais Yousef * * This file contains driver APIs to the IPI subsystem. */ #define pr_fmt(fmt) "genirq/ipi: " fmt #include #include /** * irq_reserve_ipi() - Setup an IPI to destination cpumask * @domain: IPI domain * @dest: cpumask of cpus which can receive the IPI * * Allocate a virq that can be used to send IPI to any CPU in dest mask. * * On success it'll return linux irq number and error code on failure */ int irq_reserve_ipi(struct irq_domain *domain, const struct cpumask *dest) { unsigned int nr_irqs, offset; struct irq_data *data; int virq, i; if (!domain ||!irq_domain_is_ipi(domain)) { pr_warn("Reservation on a non IPI domain\n"); return -EINVAL; } if (!cpumask_subset(dest, cpu_possible_mask)) { pr_warn("Reservation is not in possible_cpu_mask\n"); return -EINVAL; } nr_irqs = cpumask_weight(dest); if (!nr_irqs) { pr_warn("Reservation for empty destination mask\n"); return -EINVAL; } if (irq_domain_is_ipi_single(domain)) { /* * If the underlying implementation uses a single HW irq on * all cpus then we only need a single Linux irq number for * it. We have no restrictions vs. the destination mask. The * underlying implementation can deal with holes nicely. */ nr_irqs = 1; offset = 0; } else { unsigned int next; /* * The IPI requires a seperate HW irq on each CPU. We require * that the destination mask is consecutive. If an * implementation needs to support holes, it can reserve * several IPI ranges. */ offset = cpumask_first(dest); /* * Find a hole and if found look for another set bit after the * hole. For now we don't support this scenario. */ next = cpumask_next_zero(offset, dest); if (next < nr_cpu_ids) next = cpumask_next(next, dest); if (next < nr_cpu_ids) { pr_warn("Destination mask has holes\n"); return -EINVAL; } } virq = irq_domain_alloc_descs(-1, nr_irqs, 0, NUMA_NO_NODE, NULL); if (virq <= 0) { pr_warn("Can't reserve IPI, failed to alloc descs\n"); return -ENOMEM; } virq = __irq_domain_alloc_irqs(domain, virq, nr_irqs, NUMA_NO_NODE, (void *) dest, true, NULL); if (virq <= 0) { pr_warn("Can't reserve IPI, failed to alloc hw irqs\n"); goto free_descs; } for (i = 0; i < nr_irqs; i++) { data = irq_get_irq_data(virq + i); cpumask_copy(data->common->affinity, dest); data->common->ipi_offset = offset; irq_set_status_flags(virq + i, IRQ_NO_BALANCING); } return virq; free_descs: irq_free_descs(virq, nr_irqs); return -EBUSY; } /** * irq_destroy_ipi() - unreserve an IPI that was previously allocated * @irq: linux irq number to be destroyed * @dest: cpumask of cpus which should have the IPI removed * * The IPIs allocated with irq_reserve_ipi() are retuerned to the system * destroying all virqs associated with them. * * Return 0 on success or error code on failure. */ int irq_destroy_ipi(unsigned int irq, const struct cpumask *dest) { struct irq_data *data = irq_get_irq_data(irq); struct cpumask *ipimask = data ? irq_data_get_affinity_mask(data) : NULL; struct irq_domain *domain; unsigned int nr_irqs; if (!irq || !data || !ipimask) return -EINVAL; domain = data->domain; if (WARN_ON(domain == NULL)) return -EINVAL; if (!irq_domain_is_ipi(domain)) { pr_warn("Trying to destroy a non IPI domain!\n"); return -EINVAL; } if (WARN_ON(!cpumask_subset(dest, ipimask))) /* * Must be destroying a subset of CPUs to which this IPI * was set up to target */ return -EINVAL; if (irq_domain_is_ipi_per_cpu(domain)) { irq = irq + cpumask_first(dest) - data->common->ipi_offset; nr_irqs = cpumask_weight(dest); } else { nr_irqs = 1; } irq_domain_free_irqs(irq, nr_irqs); return 0; } /** * ipi_get_hwirq - Get the hwirq associated with an IPI to a cpu * @irq: linux irq number * @cpu: the target cpu * * When dealing with coprocessors IPI, we need to inform the coprocessor of * the hwirq it needs to use to receive and send IPIs. * * Returns hwirq value on success and INVALID_HWIRQ on failure. */ irq_hw_number_t ipi_get_hwirq(unsigned int irq, unsigned int cpu) { struct irq_data *data = irq_get_irq_data(irq); struct cpumask *ipimask = data ? irq_data_get_affinity_mask(data) : NULL; if (!data || !ipimask || cpu > nr_cpu_ids) return INVALID_HWIRQ; if (!cpumask_test_cpu(cpu, ipimask)) return INVALID_HWIRQ; /* * Get the real hardware irq number if the underlying implementation * uses a seperate irq per cpu. If the underlying implementation uses * a single hardware irq for all cpus then the IPI send mechanism * needs to take care of the cpu destinations. */ if (irq_domain_is_ipi_per_cpu(data->domain)) data = irq_get_irq_data(irq + cpu - data->common->ipi_offset); return data ? irqd_to_hwirq(data) : INVALID_HWIRQ; } EXPORT_SYMBOL_GPL(ipi_get_hwirq); static int ipi_send_verify(struct irq_chip *chip, struct irq_data *data, const struct cpumask *dest, unsigned int cpu) { struct cpumask *ipimask = irq_data_get_affinity_mask(data); if (!chip || !ipimask) return -EINVAL; if (!chip->ipi_send_single && !chip->ipi_send_mask) return -EINVAL; if (cpu > nr_cpu_ids) return -EINVAL; if (dest) { if (!cpumask_subset(dest, ipimask)) return -EINVAL; } else { if (!cpumask_test_cpu(cpu, ipimask)) return -EINVAL; } return 0; } /** * __ipi_send_single - send an IPI to a target Linux SMP CPU * @desc: pointer to irq_desc of the IRQ * @cpu: destination CPU, must in the destination mask passed to * irq_reserve_ipi() * * This function is for architecture or core code to speed up IPI sending. Not * usable from driver code. * * Returns zero on success and negative error number on failure. */ int __ipi_send_single(struct irq_desc *desc, unsigned int cpu) { struct irq_data *data = irq_desc_get_irq_data(desc); struct irq_chip *chip = irq_data_get_irq_chip(data); #ifdef DEBUG /* * Minimise the overhead by omitting the checks for Linux SMP IPIs. * Since the callers should be arch or core code which is generally * trusted, only check for errors when debugging. */ if (WARN_ON_ONCE(ipi_send_verify(chip, data, NULL, cpu))) return -EINVAL; #endif if (!chip->ipi_send_single) { chip->ipi_send_mask(data, cpumask_of(cpu)); return 0; } /* FIXME: Store this information in irqdata flags */ if (irq_domain_is_ipi_per_cpu(data->domain) && cpu != data->common->ipi_offset) { /* use the correct data for that cpu */ unsigned irq = data->irq + cpu - data->common->ipi_offset; data = irq_get_irq_data(irq); } chip->ipi_send_single(data, cpu); return 0; } /** * ipi_send_mask - send an IPI to target Linux SMP CPU(s) * @desc: pointer to irq_desc of the IRQ * @dest: dest CPU(s), must be a subset of the mask passed to * irq_reserve_ipi() * * This function is for architecture or core code to speed up IPI sending. Not * usable from driver code. * * Returns zero on success and negative error number on failure. */ int __ipi_send_mask(struct irq_desc *desc, const struct cpumask *dest) { struct irq_data *data = irq_desc_get_irq_data(desc); struct irq_chip *chip = irq_data_get_irq_chip(data); unsigned int cpu; #ifdef DEBUG /* * Minimise the overhead by omitting the checks for Linux SMP IPIs. * Since the callers should be arch or core code which is generally * trusted, only check for errors when debugging. */ if (WARN_ON_ONCE(ipi_send_verify(chip, data, dest, 0))) return -EINVAL; #endif if (chip->ipi_send_mask) { chip->ipi_send_mask(data, dest); return 0; } if (irq_domain_is_ipi_per_cpu(data->domain)) { unsigned int base = data->irq; for_each_cpu(cpu, dest) { unsigned irq = base + cpu - data->common->ipi_offset; data = irq_get_irq_data(irq); chip->ipi_send_single(data, cpu); } } else { for_each_cpu(cpu, dest) chip->ipi_send_single(data, cpu); } return 0; } /** * ipi_send_single - Send an IPI to a single CPU * @virq: linux irq number from irq_reserve_ipi() * @cpu: destination CPU, must in the destination mask passed to * irq_reserve_ipi() * * Returns zero on success and negative error number on failure. */ int ipi_send_single(unsigned int virq, unsigned int cpu) { struct irq_desc *desc = irq_to_desc(virq); struct irq_data *data = desc ? irq_desc_get_irq_data(desc) : NULL; struct irq_chip *chip = data ? irq_data_get_irq_chip(data) : NULL; if (WARN_ON_ONCE(ipi_send_verify(chip, data, NULL, cpu))) return -EINVAL; return __ipi_send_single(desc, cpu); } EXPORT_SYMBOL_GPL(ipi_send_single); /** * ipi_send_mask - Send an IPI to target CPU(s) * @virq: linux irq number from irq_reserve_ipi() * @dest: dest CPU(s), must be a subset of the mask passed to * irq_reserve_ipi() * * Returns zero on success and negative error number on failure. */ int ipi_send_mask(unsigned int virq, const struct cpumask *dest) { struct irq_desc *desc = irq_to_desc(virq); struct irq_data *data = desc ? irq_desc_get_irq_data(desc) : NULL; struct irq_chip *chip = data ? irq_data_get_irq_chip(data) : NULL; if (WARN_ON_ONCE(ipi_send_verify(chip, data, dest, 0))) return -EINVAL; return __ipi_send_mask(desc, dest); } EXPORT_SYMBOL_GPL(ipi_send_mask); e 0a cb 000070 c9 36 e3 2a 96 13 07 a0 90 3f 3b bd 1d 04 1d 67 000080 be 33 14 f8 02 c2 a4 02 ab 8b 5b 74 86 17 f0 5e 000090 a1 d7 aa ef a6 21 7b 93 d1 85 86 eb 4e 8c d0 4c 0000a0 56 ac e4 45 27 44 84 9f 71 db 36 b9 f7 47 d7 b3 0000b0 f2 9c 62 41 a3 46 2b 5b e3 80 63 a4 35 b5 3c f4 0000c0 bc 1e 3a ad e4 59 4a 98 6c e8 8d ff 1b 16 f8 52 0000d0 05 5c 2f 52 2a 0f 45 5b 51 fb 93 97 a4 49 4f 06 0000e0 f3 a0 d1 1e ba 3d ed a7 60 8f bb 84 2c 21 94 2d 0000f0 b3 66 a6 61 1e 58 30 24 85 f8 c8 18 c3 77 00 22 000100 000000 73 ca cc a1 d9 bb 21 8d c3 5c f3 ab 43 6d a7 a4 000010 4a fd c5 f4 9c ba 4a 0f b1 2e 19 15 4e 84 26 e0 000020 67 c9 f2 52 4d 65 1f 81 b7 8b 6d 2b 56 7b 99 75 000030 2e cd d0 db 08 0c 4b df f3 83 c6 83 00 2e 2b b8 000040 0f af 61 1d f2 02 35 74 b5 a4 6f 28 f3 a1 09 12 000050 f2 53 b5 d2 da 45 01 e5 12 d6 46 f8 0b db ed 51 000060 7b f4 0d 54 e0 63 ea 22 e2 1d d0 d6 d0 e7 7e e0 000070 93 91 fb 87 95 43 41 28 de 3d 8b a3 a8 8f c4 9e 000080 30 95 12 7a b2 27 28 ff 37 04 2e 09 7c dd 7c 12 000090 e1 50 60 fb 6d 5f a8 65 14 40 89 e3 4c d2 87 8f 0000a0 34 76 7e 66 7a 8e 6b a3 fc cf 38 52 2e f9 26 f0 0000b0 98 63 15 06 34 99 b2 88 4f aa d8 14 88 71 f1 81 0000c0 be 51 11 2b f4 7e a0 1e 12 b2 44 2e f6 8d 84 ea 0000d0 63 82 2b 66 b3 9a fd 08 73 5a c2 cc ab 5a af b1 0000e0 88 e3 a6 80 4b fc db ed 71 e0 ae c0 0a a4 8c 35 0000f0 eb 89 f9 8a 4b 52 59 6f 09 7c 01 3f 56 e7 c7 bf 000100 Signed-off-by: David Daney <david.daney@cavium.com> Acked-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/usb/host/fhci-hcd.c')