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