mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
284 lines
7.9 KiB
284 lines
7.9 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
#include <linux/module.h> |
|
#include <linux/interrupt.h> |
|
#include <linux/device.h> |
|
#include <linux/gfp.h> |
|
#include <linux/irq.h> |
|
|
|
#include "internals.h" |
|
|
|
/* |
|
* Device resource management aware IRQ request/free implementation. |
|
*/ |
|
struct irq_devres { |
|
unsigned int irq; |
|
void *dev_id; |
|
}; |
|
|
|
static void devm_irq_release(struct device *dev, void *res) |
|
{ |
|
struct irq_devres *this = res; |
|
|
|
free_irq(this->irq, this->dev_id); |
|
} |
|
|
|
static int devm_irq_match(struct device *dev, void *res, void *data) |
|
{ |
|
struct irq_devres *this = res, *match = data; |
|
|
|
return this->irq == match->irq && this->dev_id == match->dev_id; |
|
} |
|
|
|
/** |
|
* devm_request_threaded_irq - allocate an interrupt line for a managed device |
|
* @dev: device to request interrupt for |
|
* @irq: Interrupt line to allocate |
|
* @handler: Function to be called when the IRQ occurs |
|
* @thread_fn: function to be called in a threaded interrupt context. NULL |
|
* for devices which handle everything in @handler |
|
* @irqflags: Interrupt type flags |
|
* @devname: An ascii name for the claiming device, dev_name(dev) if NULL |
|
* @dev_id: A cookie passed back to the handler function |
|
* |
|
* Except for the extra @dev argument, this function takes the |
|
* same arguments and performs the same function as |
|
* request_threaded_irq(). IRQs requested with this function will be |
|
* automatically freed on driver detach. |
|
* |
|
* If an IRQ allocated with this function needs to be freed |
|
* separately, devm_free_irq() must be used. |
|
*/ |
|
int devm_request_threaded_irq(struct device *dev, unsigned int irq, |
|
irq_handler_t handler, irq_handler_t thread_fn, |
|
unsigned long irqflags, const char *devname, |
|
void *dev_id) |
|
{ |
|
struct irq_devres *dr; |
|
int rc; |
|
|
|
dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres), |
|
GFP_KERNEL); |
|
if (!dr) |
|
return -ENOMEM; |
|
|
|
if (!devname) |
|
devname = dev_name(dev); |
|
|
|
rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname, |
|
dev_id); |
|
if (rc) { |
|
devres_free(dr); |
|
return rc; |
|
} |
|
|
|
dr->irq = irq; |
|
dr->dev_id = dev_id; |
|
devres_add(dev, dr); |
|
|
|
return 0; |
|
} |
|
EXPORT_SYMBOL(devm_request_threaded_irq); |
|
|
|
/** |
|
* devm_request_any_context_irq - allocate an interrupt line for a managed device |
|
* @dev: device to request interrupt for |
|
* @irq: Interrupt line to allocate |
|
* @handler: Function to be called when the IRQ occurs |
|
* @irqflags: Interrupt type flags |
|
* @devname: An ascii name for the claiming device, dev_name(dev) if NULL |
|
* @dev_id: A cookie passed back to the handler function |
|
* |
|
* Except for the extra @dev argument, this function takes the |
|
* same arguments and performs the same function as |
|
* request_any_context_irq(). IRQs requested with this function will be |
|
* automatically freed on driver detach. |
|
* |
|
* If an IRQ allocated with this function needs to be freed |
|
* separately, devm_free_irq() must be used. |
|
*/ |
|
int devm_request_any_context_irq(struct device *dev, unsigned int irq, |
|
irq_handler_t handler, unsigned long irqflags, |
|
const char *devname, void *dev_id) |
|
{ |
|
struct irq_devres *dr; |
|
int rc; |
|
|
|
dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres), |
|
GFP_KERNEL); |
|
if (!dr) |
|
return -ENOMEM; |
|
|
|
if (!devname) |
|
devname = dev_name(dev); |
|
|
|
rc = request_any_context_irq(irq, handler, irqflags, devname, dev_id); |
|
if (rc < 0) { |
|
devres_free(dr); |
|
return rc; |
|
} |
|
|
|
dr->irq = irq; |
|
dr->dev_id = dev_id; |
|
devres_add(dev, dr); |
|
|
|
return rc; |
|
} |
|
EXPORT_SYMBOL(devm_request_any_context_irq); |
|
|
|
/** |
|
* devm_free_irq - free an interrupt |
|
* @dev: device to free interrupt for |
|
* @irq: Interrupt line to free |
|
* @dev_id: Device identity to free |
|
* |
|
* Except for the extra @dev argument, this function takes the |
|
* same arguments and performs the same function as free_irq(). |
|
* This function instead of free_irq() should be used to manually |
|
* free IRQs allocated with devm_request_irq(). |
|
*/ |
|
void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id) |
|
{ |
|
struct irq_devres match_data = { irq, dev_id }; |
|
|
|
WARN_ON(devres_destroy(dev, devm_irq_release, devm_irq_match, |
|
&match_data)); |
|
free_irq(irq, dev_id); |
|
} |
|
EXPORT_SYMBOL(devm_free_irq); |
|
|
|
struct irq_desc_devres { |
|
unsigned int from; |
|
unsigned int cnt; |
|
}; |
|
|
|
static void devm_irq_desc_release(struct device *dev, void *res) |
|
{ |
|
struct irq_desc_devres *this = res; |
|
|
|
irq_free_descs(this->from, this->cnt); |
|
} |
|
|
|
/** |
|
* __devm_irq_alloc_descs - Allocate and initialize a range of irq descriptors |
|
* for a managed device |
|
* @dev: Device to allocate the descriptors for |
|
* @irq: Allocate for specific irq number if irq >= 0 |
|
* @from: Start the search from this irq number |
|
* @cnt: Number of consecutive irqs to allocate |
|
* @node: Preferred node on which the irq descriptor should be allocated |
|
* @owner: Owning module (can be NULL) |
|
* @affinity: Optional pointer to an irq_affinity_desc array of size @cnt |
|
* which hints where the irq descriptors should be allocated |
|
* and which default affinities to use |
|
* |
|
* Returns the first irq number or error code. |
|
* |
|
* Note: Use the provided wrappers (devm_irq_alloc_desc*) for simplicity. |
|
*/ |
|
int __devm_irq_alloc_descs(struct device *dev, int irq, unsigned int from, |
|
unsigned int cnt, int node, struct module *owner, |
|
const struct irq_affinity_desc *affinity) |
|
{ |
|
struct irq_desc_devres *dr; |
|
int base; |
|
|
|
dr = devres_alloc(devm_irq_desc_release, sizeof(*dr), GFP_KERNEL); |
|
if (!dr) |
|
return -ENOMEM; |
|
|
|
base = __irq_alloc_descs(irq, from, cnt, node, owner, affinity); |
|
if (base < 0) { |
|
devres_free(dr); |
|
return base; |
|
} |
|
|
|
dr->from = base; |
|
dr->cnt = cnt; |
|
devres_add(dev, dr); |
|
|
|
return base; |
|
} |
|
EXPORT_SYMBOL_GPL(__devm_irq_alloc_descs); |
|
|
|
#ifdef CONFIG_GENERIC_IRQ_CHIP |
|
/** |
|
* devm_irq_alloc_generic_chip - Allocate and initialize a generic chip |
|
* for a managed device |
|
* @dev: Device to allocate the generic chip for |
|
* @name: Name of the irq chip |
|
* @num_ct: Number of irq_chip_type instances associated with this |
|
* @irq_base: Interrupt base nr for this chip |
|
* @reg_base: Register base address (virtual) |
|
* @handler: Default flow handler associated with this chip |
|
* |
|
* Returns an initialized irq_chip_generic structure. The chip defaults |
|
* to the primary (index 0) irq_chip_type and @handler |
|
*/ |
|
struct irq_chip_generic * |
|
devm_irq_alloc_generic_chip(struct device *dev, const char *name, int num_ct, |
|
unsigned int irq_base, void __iomem *reg_base, |
|
irq_flow_handler_t handler) |
|
{ |
|
struct irq_chip_generic *gc; |
|
|
|
gc = devm_kzalloc(dev, struct_size(gc, chip_types, num_ct), GFP_KERNEL); |
|
if (gc) |
|
irq_init_generic_chip(gc, name, num_ct, |
|
irq_base, reg_base, handler); |
|
|
|
return gc; |
|
} |
|
EXPORT_SYMBOL_GPL(devm_irq_alloc_generic_chip); |
|
|
|
struct irq_generic_chip_devres { |
|
struct irq_chip_generic *gc; |
|
u32 msk; |
|
unsigned int clr; |
|
unsigned int set; |
|
}; |
|
|
|
static void devm_irq_remove_generic_chip(struct device *dev, void *res) |
|
{ |
|
struct irq_generic_chip_devres *this = res; |
|
|
|
irq_remove_generic_chip(this->gc, this->msk, this->clr, this->set); |
|
} |
|
|
|
/** |
|
* devm_irq_setup_generic_chip - Setup a range of interrupts with a generic |
|
* chip for a managed device |
|
* |
|
* @dev: Device to setup the generic chip for |
|
* @gc: Generic irq chip holding all data |
|
* @msk: Bitmask holding the irqs to initialize relative to gc->irq_base |
|
* @flags: Flags for initialization |
|
* @clr: IRQ_* bits to clear |
|
* @set: IRQ_* bits to set |
|
* |
|
* Set up max. 32 interrupts starting from gc->irq_base. Note, this |
|
* initializes all interrupts to the primary irq_chip_type and its |
|
* associated handler. |
|
*/ |
|
int devm_irq_setup_generic_chip(struct device *dev, struct irq_chip_generic *gc, |
|
u32 msk, enum irq_gc_flags flags, |
|
unsigned int clr, unsigned int set) |
|
{ |
|
struct irq_generic_chip_devres *dr; |
|
|
|
dr = devres_alloc(devm_irq_remove_generic_chip, |
|
sizeof(*dr), GFP_KERNEL); |
|
if (!dr) |
|
return -ENOMEM; |
|
|
|
irq_setup_generic_chip(gc, msk, flags, clr, set); |
|
|
|
dr->gc = gc; |
|
dr->msk = msk; |
|
dr->clr = clr; |
|
dr->set = set; |
|
devres_add(dev, dr); |
|
|
|
return 0; |
|
} |
|
EXPORT_SYMBOL_GPL(devm_irq_setup_generic_chip); |
|
#endif /* CONFIG_GENERIC_IRQ_CHIP */
|
|
|