Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

genirq/irq_sim: add an extended irq_sim initializer

Currently users of the interrupt simulator don't have any way of being
notified about interrupts from the simulated domain being requested or
released. This causes a problem for one of the users - the GPIO
simulator - which is unable to lock the pins as interrupts.

Define a structure containing callbacks to be executed on various
irq_sim-related events (for now: irq request and release) and provide an
extended function for creating simulated interrupt domains that takes it
and a pointer to custom user data (to be passed to said callbacks) as
arguments.

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://lore.kernel.org/r/20240624093934.17089-2-brgl@bgdev.pl
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>

+74 -3
+17
include/linux/irq_sim.h
··· 16 16 * requested like normal irqs and enqueued from process context. 17 17 */ 18 18 19 + struct irq_sim_ops { 20 + int (*irq_sim_irq_requested)(struct irq_domain *domain, 21 + irq_hw_number_t hwirq, void *data); 22 + void (*irq_sim_irq_released)(struct irq_domain *domain, 23 + irq_hw_number_t hwirq, void *data); 24 + }; 25 + 19 26 struct irq_domain *irq_domain_create_sim(struct fwnode_handle *fwnode, 20 27 unsigned int num_irqs); 21 28 struct irq_domain *devm_irq_domain_create_sim(struct device *dev, 22 29 struct fwnode_handle *fwnode, 23 30 unsigned int num_irqs); 31 + struct irq_domain *irq_domain_create_sim_full(struct fwnode_handle *fwnode, 32 + unsigned int num_irqs, 33 + const struct irq_sim_ops *ops, 34 + void *data); 35 + struct irq_domain * 36 + devm_irq_domain_create_sim_full(struct device *dev, 37 + struct fwnode_handle *fwnode, 38 + unsigned int num_irqs, 39 + const struct irq_sim_ops *ops, 40 + void *data); 24 41 void irq_domain_remove_sim(struct irq_domain *domain); 25 42 26 43 #endif /* _LINUX_IRQ_SIM_H */
+57 -3
kernel/irq/irq_sim.c
··· 17 17 unsigned int irq_count; 18 18 unsigned long *pending; 19 19 struct irq_domain *domain; 20 + struct irq_sim_ops ops; 21 + void *user_data; 20 22 }; 21 23 22 24 struct irq_sim_irq_ctx { ··· 90 88 return 0; 91 89 } 92 90 91 + static int irq_sim_request_resources(struct irq_data *data) 92 + { 93 + struct irq_sim_irq_ctx *irq_ctx = irq_data_get_irq_chip_data(data); 94 + struct irq_sim_work_ctx *work_ctx = irq_ctx->work_ctx; 95 + irq_hw_number_t hwirq = irqd_to_hwirq(data); 96 + 97 + if (work_ctx->ops.irq_sim_irq_requested) 98 + return work_ctx->ops.irq_sim_irq_requested(work_ctx->domain, 99 + hwirq, 100 + work_ctx->user_data); 101 + 102 + return 0; 103 + } 104 + 105 + static void irq_sim_release_resources(struct irq_data *data) 106 + { 107 + struct irq_sim_irq_ctx *irq_ctx = irq_data_get_irq_chip_data(data); 108 + struct irq_sim_work_ctx *work_ctx = irq_ctx->work_ctx; 109 + irq_hw_number_t hwirq = irqd_to_hwirq(data); 110 + 111 + if (work_ctx->ops.irq_sim_irq_released) 112 + work_ctx->ops.irq_sim_irq_released(work_ctx->domain, hwirq, 113 + work_ctx->user_data); 114 + } 115 + 93 116 static struct irq_chip irq_sim_irqchip = { 94 117 .name = "irq_sim", 95 118 .irq_mask = irq_sim_irqmask, ··· 122 95 .irq_set_type = irq_sim_set_type, 123 96 .irq_get_irqchip_state = irq_sim_get_irqchip_state, 124 97 .irq_set_irqchip_state = irq_sim_set_irqchip_state, 98 + .irq_request_resources = irq_sim_request_resources, 99 + .irq_release_resources = irq_sim_release_resources, 125 100 }; 126 101 127 102 static void irq_sim_handle_irq(struct irq_work *work) ··· 193 164 struct irq_domain *irq_domain_create_sim(struct fwnode_handle *fwnode, 194 165 unsigned int num_irqs) 195 166 { 167 + return irq_domain_create_sim_full(fwnode, num_irqs, NULL, NULL); 168 + } 169 + EXPORT_SYMBOL_GPL(irq_domain_create_sim); 170 + 171 + struct irq_domain *irq_domain_create_sim_full(struct fwnode_handle *fwnode, 172 + unsigned int num_irqs, 173 + const struct irq_sim_ops *ops, 174 + void *data) 175 + { 196 176 struct irq_sim_work_ctx *work_ctx __free(kfree) = 197 177 kmalloc(sizeof(*work_ctx), GFP_KERNEL); 198 178 ··· 221 183 work_ctx->irq_count = num_irqs; 222 184 work_ctx->work = IRQ_WORK_INIT_HARD(irq_sim_handle_irq); 223 185 work_ctx->pending = no_free_ptr(pending); 186 + work_ctx->user_data = data; 187 + 188 + if (ops) 189 + memcpy(&work_ctx->ops, ops, sizeof(*ops)); 224 190 225 191 return no_free_ptr(work_ctx)->domain; 226 192 } 227 - EXPORT_SYMBOL_GPL(irq_domain_create_sim); 193 + EXPORT_SYMBOL_GPL(irq_domain_create_sim_full); 228 194 229 195 /** 230 196 * irq_domain_remove_sim - Deinitialize the interrupt simulator domain: free ··· 270 228 struct fwnode_handle *fwnode, 271 229 unsigned int num_irqs) 272 230 { 231 + return devm_irq_domain_create_sim_full(dev, fwnode, num_irqs, 232 + NULL, NULL); 233 + } 234 + EXPORT_SYMBOL_GPL(devm_irq_domain_create_sim); 235 + 236 + struct irq_domain * 237 + devm_irq_domain_create_sim_full(struct device *dev, 238 + struct fwnode_handle *fwnode, 239 + unsigned int num_irqs, 240 + const struct irq_sim_ops *ops, 241 + void *data) 242 + { 273 243 struct irq_domain *domain; 274 244 int ret; 275 245 276 - domain = irq_domain_create_sim(fwnode, num_irqs); 246 + domain = irq_domain_create_sim_full(fwnode, num_irqs, ops, data); 277 247 if (IS_ERR(domain)) 278 248 return domain; 279 249 ··· 295 241 296 242 return domain; 297 243 } 298 - EXPORT_SYMBOL_GPL(devm_irq_domain_create_sim); 244 + EXPORT_SYMBOL_GPL(devm_irq_domain_create_sim_full);