diff options
| -rw-r--r-- | drivers/base/regmap/regmap-irq.c | 43 | ||||
| -rw-r--r-- | include/linux/regmap.h | 5 | 
2 files changed, 43 insertions, 5 deletions
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 38d1f72d869c..8d16db533527 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -63,6 +63,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)  	struct regmap *map = d->map;  	int i, ret;  	u32 reg; +	u32 unmask_offset;  	if (d->chip->runtime_pm) {  		ret = pm_runtime_get_sync(map->dev); @@ -79,12 +80,28 @@ static void regmap_irq_sync_unlock(struct irq_data *data)  	for (i = 0; i < d->chip->num_regs; i++) {  		reg = d->chip->mask_base +  			(i * map->reg_stride * d->irq_reg_stride); -		if (d->chip->mask_invert) +		if (d->chip->mask_invert) {  			ret = regmap_update_bits(d->map, reg,  					 d->mask_buf_def[i], ~d->mask_buf[i]); -		else +		} else if (d->chip->unmask_base) { +			/* set mask with mask_base register */ +			ret = regmap_update_bits(d->map, reg, +					d->mask_buf_def[i], ~d->mask_buf[i]); +			if (ret < 0) +				dev_err(d->map->dev, +					"Failed to sync unmasks in %x\n", +					reg); +			unmask_offset = d->chip->unmask_base - +							d->chip->mask_base; +			/* clear mask with unmask_base register */ +			ret = regmap_update_bits(d->map, +					reg + unmask_offset, +					d->mask_buf_def[i], +					d->mask_buf[i]); +		} else {  			ret = regmap_update_bits(d->map, reg,  					 d->mask_buf_def[i], d->mask_buf[i]); +		}  		if (ret != 0)  			dev_err(d->map->dev, "Failed to sync masks in %x\n",  				reg); @@ -116,7 +133,11 @@ static void regmap_irq_sync_unlock(struct irq_data *data)  		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]); +			/* some chips ack by write 0 */ +			if (d->chip->ack_invert) +				ret = regmap_write(map, reg, ~d->mask_buf[i]); +			else +				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); @@ -339,6 +360,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,  	int i;  	int ret = -ENOMEM;  	u32 reg; +	u32 unmask_offset;  	if (chip->num_regs <= 0)  		return -EINVAL; @@ -420,7 +442,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,  		if (chip->mask_invert)  			ret = regmap_update_bits(map, reg,  					 d->mask_buf[i], ~d->mask_buf[i]); -		else +		else if (d->chip->unmask_base) { +			unmask_offset = d->chip->unmask_base - +					d->chip->mask_base; +			ret = regmap_update_bits(d->map, +					reg + unmask_offset, +					d->mask_buf[i], +					d->mask_buf[i]); +		} else  			ret = regmap_update_bits(map, reg,  					 d->mask_buf[i], d->mask_buf[i]);  		if (ret != 0) { @@ -445,7 +474,11 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,  		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, +			if (chip->ack_invert) +				ret = regmap_write(map, reg, +					~(d->status_buf[i] & d->mask_buf[i])); +			else +				ret = regmap_write(map, reg,  					d->status_buf[i] & d->mask_buf[i]);  			if (ret != 0) {  				dev_err(map->dev, "Failed to ack 0x%x: %d\n", diff --git a/include/linux/regmap.h b/include/linux/regmap.h index 8fc0bfd8edc4..f36c9f96d32d 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -800,6 +800,8 @@ struct regmap_irq {   *   * @status_base: Base status register address.   * @mask_base:   Base mask register address. + * @unmask_base:  Base unmask register address. for chips who have + *                separate mask and unmask registers   * @ack_base:    Base ack address. If zero then the chip is clear on read.   *               Using zero value is possible with @use_ack bit.   * @wake_base:   Base address for wake enables.  If zero unsupported. @@ -807,6 +809,7 @@ struct regmap_irq {   * @init_ack_masked: Ack all masked interrupts once during initalization.   * @mask_invert: Inverted mask register: cleared bits are masked out.   * @use_ack:     Use @ack register even if it is zero. + * @ack_invert:  Inverted ack register: cleared bits for ack.   * @wake_invert: Inverted wake register: cleared bits are wake enabled.   * @runtime_pm:  Hold a runtime PM lock on the device when accessing it.   * @@ -820,12 +823,14 @@ struct regmap_irq_chip {  	unsigned int status_base;  	unsigned int mask_base; +	unsigned int unmask_base;  	unsigned int ack_base;  	unsigned int wake_base;  	unsigned int irq_reg_stride;  	bool init_ack_masked:1;  	bool mask_invert:1;  	bool use_ack:1; +	bool ack_invert:1;  	bool wake_invert:1;  	bool runtime_pm:1;  | 
