diff options
Diffstat (limited to 'drivers/base/regmap/regmap-irq.c')
| -rw-r--r-- | drivers/base/regmap/regmap-irq.c | 35 | 
1 files changed, 27 insertions, 8 deletions
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index d10456ffd81..6299a50a596 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -10,13 +10,13 @@   * published by the Free Software Foundation.   */ -#include <linux/export.h>  #include <linux/device.h> -#include <linux/regmap.h> -#include <linux/irq.h> +#include <linux/export.h>  #include <linux/interrupt.h> +#include <linux/irq.h>  #include <linux/irqdomain.h>  #include <linux/pm_runtime.h> +#include <linux/regmap.h>  #include <linux/slab.h>  #include "internal.h" @@ -105,6 +105,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data)  					"Failed to sync wakes in %x: %d\n",  					reg, ret);  		} + +		if (!d->chip->init_ack_masked) +			continue; +		/* +		 * Ack all the masked interrupts uncondictionly, +		 * OR if there is masked interrupt which hasn't been Acked, +		 * it'll be ignored in irq handler, then may introduce irq storm +		 */ +		if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) { +			reg = d->chip->ack_base + +				(i * map->reg_stride * d->irq_reg_stride); +			ret = regmap_write(map, reg, d->mask_buf[i]); +			if (ret != 0) +				dev_err(d->map->dev, "Failed to ack 0x%x: %d\n", +					reg, ret); +		}  	}  	if (d->chip->runtime_pm) @@ -255,7 +271,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)  	for (i = 0; i < data->chip->num_regs; i++) {  		data->status_buf[i] &= ~data->mask_buf[i]; -		if (data->status_buf[i] && chip->ack_base) { +		if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) {  			reg = chip->ack_base +  				(i * map->reg_stride * data->irq_reg_stride);  			ret = regmap_write(map, reg, data->status_buf[i]); @@ -331,6 +347,9 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,  	int ret = -ENOMEM;  	u32 reg; +	if (chip->num_regs <= 0) +		return -EINVAL; +  	for (i = 0; i < chip->num_irqs; i++) {  		if (chip->irqs[i].reg_offset % map->reg_stride)  			return -EINVAL; @@ -352,8 +371,6 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,  	if (!d)  		return -ENOMEM; -	*data = d; -  	d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,  				GFP_KERNEL);  	if (!d->status_buf) @@ -432,7 +449,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,  			goto err_alloc;  		} -		if (d->status_buf[i] && chip->ack_base) { +		if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) {  			reg = chip->ack_base +  				(i * map->reg_stride * d->irq_reg_stride);  			ret = regmap_write(map, reg, @@ -490,6 +507,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,  		goto err_domain;  	} +	*data = d; +  	return 0;  err_domain: @@ -517,7 +536,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)  		return;  	free_irq(irq, d); -	/* We should unmap the domain but... */ +	irq_domain_remove(d->domain);  	kfree(d->wake_buf);  	kfree(d->mask_buf_def);  	kfree(d->mask_buf);  | 
