1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl> 4 * 5 * This program is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License as published by the 7 * Free Software Foundation; either version 2 of the License, or (at your 8 * option) any later version. 9 */ 10 11 #include <linux/slab.h> 12 #include <linux/irq_sim.h> 13 #include <linux/irq.h> 14 15 struct irq_sim_devres { 16 struct irq_sim *sim; 17 }; 18 19 static void irq_sim_irqmask(struct irq_data *data) 20 { 21 struct irq_sim_irq_ctx *irq_ctx = irq_data_get_irq_chip_data(data); 22 23 irq_ctx->enabled = false; 24 } 25 26 static void irq_sim_irqunmask(struct irq_data *data) 27 { 28 struct irq_sim_irq_ctx *irq_ctx = irq_data_get_irq_chip_data(data); 29 30 irq_ctx->enabled = true; 31 } 32 33 static struct irq_chip irq_sim_irqchip = { 34 .name = "irq_sim", 35 .irq_mask = irq_sim_irqmask, 36 .irq_unmask = irq_sim_irqunmask, 37 }; 38 39 static void irq_sim_handle_irq(struct irq_work *work) 40 { 41 struct irq_sim_work_ctx *work_ctx; 42 43 work_ctx = container_of(work, struct irq_sim_work_ctx, work); 44 handle_simple_irq(irq_to_desc(work_ctx->irq)); 45 } 46 47 /** 48 * irq_sim_init - Initialize the interrupt simulator: allocate a range of 49 * dummy interrupts. 50 * 51 * @sim: The interrupt simulator object to initialize. 52 * @num_irqs: Number of interrupts to allocate 53 * 54 * On success: return the base of the allocated interrupt range. 55 * On failure: a negative errno. 56 */ 57 int irq_sim_init(struct irq_sim *sim, unsigned int num_irqs) 58 { 59 int i; 60 61 sim->irqs = kmalloc_array(num_irqs, sizeof(*sim->irqs), GFP_KERNEL); 62 if (!sim->irqs) 63 return -ENOMEM; 64 65 sim->irq_base = irq_alloc_descs(-1, 0, num_irqs, 0); 66 if (sim->irq_base < 0) { 67 kfree(sim->irqs); 68 return sim->irq_base; 69 } 70 71 for (i = 0; i < num_irqs; i++) { 72 sim->irqs[i].irqnum = sim->irq_base + i; 73 sim->irqs[i].enabled = false; 74 irq_set_chip(sim->irq_base + i, &irq_sim_irqchip); 75 irq_set_chip_data(sim->irq_base + i, &sim->irqs[i]); 76 irq_set_handler(sim->irq_base + i, &handle_simple_irq); 77 irq_modify_status(sim->irq_base + i, 78 IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE); 79 } 80 81 init_irq_work(&sim->work_ctx.work, irq_sim_handle_irq); 82 sim->irq_count = num_irqs; 83 84 return sim->irq_base; 85 } 86 EXPORT_SYMBOL_GPL(irq_sim_init); 87 88 /** 89 * irq_sim_fini - Deinitialize the interrupt simulator: free the interrupt 90 * descriptors and allocated memory. 91 * 92 * @sim: The interrupt simulator to tear down. 93 */ 94 void irq_sim_fini(struct irq_sim *sim) 95 { 96 irq_work_sync(&sim->work_ctx.work); 97 irq_free_descs(sim->irq_base, sim->irq_count); 98 kfree(sim->irqs); 99 } 100 EXPORT_SYMBOL_GPL(irq_sim_fini); 101 102 static void devm_irq_sim_release(struct device *dev, void *res) 103 { 104 struct irq_sim_devres *this = res; 105 106 irq_sim_fini(this->sim); 107 } 108 109 /** 110 * irq_sim_init - Initialize the interrupt simulator for a managed device. 111 * 112 * @dev: Device to initialize the simulator object for. 113 * @sim: The interrupt simulator object to initialize. 114 * @num_irqs: Number of interrupts to allocate 115 * 116 * On success: return the base of the allocated interrupt range. 117 * On failure: a negative errno. 118 */ 119 int devm_irq_sim_init(struct device *dev, struct irq_sim *sim, 120 unsigned int num_irqs) 121 { 122 struct irq_sim_devres *dr; 123 int rv; 124 125 dr = devres_alloc(devm_irq_sim_release, sizeof(*dr), GFP_KERNEL); 126 if (!dr) 127 return -ENOMEM; 128 129 rv = irq_sim_init(sim, num_irqs); 130 if (rv < 0) { 131 devres_free(dr); 132 return rv; 133 } 134 135 dr->sim = sim; 136 devres_add(dev, dr); 137 138 return rv; 139 } 140 EXPORT_SYMBOL_GPL(devm_irq_sim_init); 141 142 /** 143 * irq_sim_fire - Enqueue an interrupt. 144 * 145 * @sim: The interrupt simulator object. 146 * @offset: Offset of the simulated interrupt which should be fired. 147 */ 148 void irq_sim_fire(struct irq_sim *sim, unsigned int offset) 149 { 150 if (sim->irqs[offset].enabled) { 151 sim->work_ctx.irq = irq_sim_irqnum(sim, offset); 152 irq_work_queue(&sim->work_ctx.work); 153 } 154 } 155 EXPORT_SYMBOL_GPL(irq_sim_fire); 156 157 /** 158 * irq_sim_irqnum - Get the allocated number of a dummy interrupt. 159 * 160 * @sim: The interrupt simulator object. 161 * @offset: Offset of the simulated interrupt for which to retrieve 162 * the number. 163 */ 164 int irq_sim_irqnum(struct irq_sim *sim, unsigned int offset) 165 { 166 return sim->irqs[offset].irqnum; 167 } 168 EXPORT_SYMBOL_GPL(irq_sim_irqnum); 169