root/kernel/irq/irq_sim.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. irq_sim_irqmask
  2. irq_sim_irqunmask
  3. irq_sim_set_type
  4. irq_sim_handle_irq
  5. irq_sim_init
  6. irq_sim_fini
  7. devm_irq_sim_release
  8. devm_irq_sim_init
  9. irq_sim_fire
  10. irq_sim_irqnum

   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 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         irq_ctx->enabled = false;
  19 }
  20 
  21 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         irq_ctx->enabled = true;
  26 }
  27 
  28 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         if (type & ~IRQ_TYPE_EDGE_BOTH)
  32                 return -EINVAL;
  33 
  34         irqd_set_trigger_type(data, type);
  35 
  36         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 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         while (!bitmap_empty(work_ctx->pending, sim->irq_count)) {
  57                 offset = find_next_bit(work_ctx->pending,
  58                                        sim->irq_count, offset);
  59                 clear_bit(offset, work_ctx->pending);
  60                 irqnum = irq_sim_irqnum(sim, offset);
  61                 handle_simple_irq(irq_to_desc(irqnum));
  62         }
  63 }
  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 int irq_sim_init(struct irq_sim *sim, unsigned int num_irqs)
  76 {
  77         int i;
  78 
  79         sim->irqs = kmalloc_array(num_irqs, sizeof(*sim->irqs), GFP_KERNEL);
  80         if (!sim->irqs)
  81                 return -ENOMEM;
  82 
  83         sim->irq_base = irq_alloc_descs(-1, 0, num_irqs, 0);
  84         if (sim->irq_base < 0) {
  85                 kfree(sim->irqs);
  86                 return sim->irq_base;
  87         }
  88 
  89         sim->work_ctx.pending = bitmap_zalloc(num_irqs, GFP_KERNEL);
  90         if (!sim->work_ctx.pending) {
  91                 kfree(sim->irqs);
  92                 irq_free_descs(sim->irq_base, num_irqs);
  93                 return -ENOMEM;
  94         }
  95 
  96         for (i = 0; i < num_irqs; i++) {
  97                 sim->irqs[i].irqnum = sim->irq_base + i;
  98                 sim->irqs[i].enabled = false;
  99                 irq_set_chip(sim->irq_base + i, &irq_sim_irqchip);
 100                 irq_set_chip_data(sim->irq_base + i, &sim->irqs[i]);
 101                 irq_set_handler(sim->irq_base + i, &handle_simple_irq);
 102                 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         sim->irq_count = num_irqs;
 108 
 109         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 void irq_sim_fini(struct irq_sim *sim)
 120 {
 121         irq_work_sync(&sim->work_ctx.work);
 122         bitmap_free(sim->work_ctx.pending);
 123         irq_free_descs(sim->irq_base, sim->irq_count);
 124         kfree(sim->irqs);
 125 }
 126 EXPORT_SYMBOL_GPL(irq_sim_fini);
 127 
 128 static void devm_irq_sim_release(struct device *dev, void *res)
 129 {
 130         struct irq_sim_devres *this = res;
 131 
 132         irq_sim_fini(this->sim);
 133 }
 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 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         if (!dr)
 153                 return -ENOMEM;
 154 
 155         rv = irq_sim_init(sim, num_irqs);
 156         if (rv < 0) {
 157                 devres_free(dr);
 158                 return rv;
 159         }
 160 
 161         dr->sim = sim;
 162         devres_add(dev, dr);
 163 
 164         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 void irq_sim_fire(struct irq_sim *sim, unsigned int offset)
 175 {
 176         if (sim->irqs[offset].enabled) {
 177                 set_bit(offset, sim->work_ctx.pending);
 178                 irq_work_queue(&sim->work_ctx.work);
 179         }
 180 }
 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 int irq_sim_irqnum(struct irq_sim *sim, unsigned int offset)
 191 {
 192         return sim->irqs[offset].irqnum;
 193 }
 194 EXPORT_SYMBOL_GPL(irq_sim_irqnum);

/* [<][>][^][v][top][bottom][index][help] */