diff options
Diffstat (limited to 'drivers/base/regmap/regmap-irq.c')
| -rw-r--r-- | drivers/base/regmap/regmap-irq.c | 126 | 
1 files changed, 97 insertions, 29 deletions
| diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 19db764ffa4a..760296a4b606 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -38,6 +38,7 @@ struct regmap_irq_chip_data {  	unsigned int *wake_buf;  	unsigned int *type_buf;  	unsigned int *type_buf_def; +	unsigned int **virt_buf;  	unsigned int irq_reg_stride;  	unsigned int type_reg_stride; @@ -45,6 +46,27 @@ struct regmap_irq_chip_data {  	bool clear_status:1;  }; +static int sub_irq_reg(struct regmap_irq_chip_data *data, +		       unsigned int base_reg, int i) +{ +	const struct regmap_irq_chip *chip = data->chip; +	struct regmap *map = data->map; +	struct regmap_irq_sub_irq_map *subreg; +	unsigned int offset; +	int reg = 0; + +	if (!chip->sub_reg_offsets || !chip->not_fixed_stride) { +		/* Assume linear mapping */ +		reg = base_reg + (i * map->reg_stride * data->irq_reg_stride); +	} else { +		subreg = &chip->sub_reg_offsets[i]; +		offset = subreg->offset[0]; +		reg = base_reg + offset; +	} + +	return reg; +} +  static inline const  struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data,  				     int irq) @@ -73,7 +95,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)  {  	struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);  	struct regmap *map = d->map; -	int i, ret; +	int i, j, ret;  	u32 reg;  	u32 unmask_offset;  	u32 val; @@ -87,8 +109,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)  	if (d->clear_status) {  		for (i = 0; i < d->chip->num_regs; i++) { -			reg = d->chip->status_base + -				(i * map->reg_stride * d->irq_reg_stride); +			reg = sub_irq_reg(d, d->chip->status_base, i);  			ret = regmap_read(map, reg, &val);  			if (ret) @@ -108,8 +129,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)  		if (!d->chip->mask_base)  			continue; -		reg = d->chip->mask_base + -			(i * map->reg_stride * d->irq_reg_stride); +		reg = sub_irq_reg(d, d->chip->mask_base, i);  		if (d->chip->mask_invert) {  			ret = regmap_irq_update_bits(d, reg,  					 d->mask_buf_def[i], ~d->mask_buf[i]); @@ -136,8 +156,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)  			dev_err(d->map->dev, "Failed to sync masks in %x\n",  				reg); -		reg = d->chip->wake_base + -			(i * map->reg_stride * d->irq_reg_stride); +		reg = sub_irq_reg(d, d->chip->wake_base, i);  		if (d->wake_buf) {  			if (d->chip->wake_invert)  				ret = regmap_irq_update_bits(d, reg, @@ -161,8 +180,8 @@ static void regmap_irq_sync_unlock(struct irq_data *data)  		 * 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); +			reg = sub_irq_reg(d, d->chip->ack_base, i); +  			/* some chips ack by write 0 */  			if (d->chip->ack_invert)  				ret = regmap_write(map, reg, ~d->mask_buf[i]); @@ -187,8 +206,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)  		for (i = 0; i < d->chip->num_type_reg; i++) {  			if (!d->type_buf_def[i])  				continue; -			reg = d->chip->type_base + -				(i * map->reg_stride * d->type_reg_stride); +			reg = sub_irq_reg(d, d->chip->type_base, i);  			if (d->chip->type_invert)  				ret = regmap_irq_update_bits(d, reg,  					d->type_buf_def[i], ~d->type_buf[i]); @@ -201,6 +219,20 @@ static void regmap_irq_sync_unlock(struct irq_data *data)  		}  	} +	if (d->chip->num_virt_regs) { +		for (i = 0; i < d->chip->num_virt_regs; i++) { +			for (j = 0; j < d->chip->num_regs; j++) { +				reg = sub_irq_reg(d, d->chip->virt_reg_base[i], +						  j); +				ret = regmap_write(map, reg, d->virt_buf[i][j]); +				if (ret != 0) +					dev_err(d->map->dev, +						"Failed to write virt 0x%x: %d\n", +						reg, ret); +			} +		} +	} +  	if (d->chip->runtime_pm)  		pm_runtime_put(map->dev); @@ -301,6 +333,11 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type)  	default:  		return -EINVAL;  	} + +	if (d->chip->set_type_virt) +		return d->chip->set_type_virt(d->virt_buf, type, data->hwirq, +					      reg); +  	return 0;  } @@ -352,8 +389,15 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data,  		for (i = 0; i < subreg->num_regs; i++) {  			unsigned int offset = subreg->offset[i]; -			ret = regmap_read(map, chip->status_base + offset, -					  &data->status_buf[offset]); +			if (chip->not_fixed_stride) +				ret = regmap_read(map, +						chip->status_base + offset, +						&data->status_buf[b]); +			else +				ret = regmap_read(map, +						chip->status_base + offset, +						&data->status_buf[offset]); +  			if (ret)  				break;  		} @@ -474,10 +518,9 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)  	} else {  		for (i = 0; i < data->chip->num_regs; i++) { -			ret = regmap_read(map, chip->status_base + -					  (i * map->reg_stride -					   * data->irq_reg_stride), -					  &data->status_buf[i]); +			unsigned int reg = sub_irq_reg(data, +					data->chip->status_base, i); +			ret = regmap_read(map, reg, &data->status_buf[i]);  			if (ret != 0) {  				dev_err(map->dev, @@ -499,8 +542,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)  		data->status_buf[i] &= ~data->mask_buf[i];  		if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) { -			reg = chip->ack_base + -				(i * map->reg_stride * data->irq_reg_stride); +			reg = sub_irq_reg(data, data->chip->ack_base, i); +  			if (chip->ack_invert)  				ret = regmap_write(map, reg,  						~data->status_buf[i]); @@ -605,6 +648,12 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,  			return -EINVAL;  	} +	if (chip->not_fixed_stride) { +		for (i = 0; i < chip->num_regs; i++) +			if (chip->sub_reg_offsets[i].num_regs != 1) +				return -EINVAL; +	} +  	if (irq_base) {  		irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0);  		if (irq_base < 0) { @@ -662,6 +711,24 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,  			goto err_alloc;  	} +	if (chip->num_virt_regs) { +		/* +		 * Create virt_buf[chip->num_extra_config_regs][chip->num_regs] +		 */ +		d->virt_buf = kcalloc(chip->num_virt_regs, sizeof(*d->virt_buf), +				      GFP_KERNEL); +		if (!d->virt_buf) +			goto err_alloc; + +		for (i = 0; i < chip->num_virt_regs; i++) { +			d->virt_buf[i] = kcalloc(chip->num_regs, +						 sizeof(unsigned int), +						 GFP_KERNEL); +			if (!d->virt_buf[i]) +				goto err_alloc; +		} +	} +  	d->irq_chip = regmap_irq_chip;  	d->irq_chip.name = chip->name;  	d->irq = irq; @@ -700,8 +767,8 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,  		if (!chip->mask_base)  			continue; -		reg = chip->mask_base + -			(i * map->reg_stride * d->irq_reg_stride); +		reg = sub_irq_reg(d, d->chip->mask_base, i); +  		if (chip->mask_invert)  			ret = regmap_irq_update_bits(d, reg,  					 d->mask_buf[i], ~d->mask_buf[i]); @@ -725,8 +792,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,  			continue;  		/* Ack masked but set interrupts */ -		reg = chip->status_base + -			(i * map->reg_stride * d->irq_reg_stride); +		reg = sub_irq_reg(d, d->chip->status_base, i);  		ret = regmap_read(map, reg, &d->status_buf[i]);  		if (ret != 0) {  			dev_err(map->dev, "Failed to read IRQ status: %d\n", @@ -735,8 +801,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,  		}  		if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) { -			reg = chip->ack_base + -				(i * map->reg_stride * d->irq_reg_stride); +			reg = sub_irq_reg(d, d->chip->ack_base, i);  			if (chip->ack_invert)  				ret = regmap_write(map, reg,  					~(d->status_buf[i] & d->mask_buf[i])); @@ -765,8 +830,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,  	if (d->wake_buf) {  		for (i = 0; i < chip->num_regs; i++) {  			d->wake_buf[i] = d->mask_buf_def[i]; -			reg = chip->wake_base + -				(i * map->reg_stride * d->irq_reg_stride); +			reg = sub_irq_reg(d, d->chip->wake_base, i);  			if (chip->wake_invert)  				ret = regmap_irq_update_bits(d, reg, @@ -786,8 +850,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode,  	if (chip->num_type_reg && !chip->type_in_mask) {  		for (i = 0; i < chip->num_type_reg; ++i) { -			reg = chip->type_base + -				(i * map->reg_stride * d->type_reg_stride); +			reg = sub_irq_reg(d, d->chip->type_base, i);  			ret = regmap_read(map, reg, &d->type_buf_def[i]); @@ -838,6 +901,11 @@ err_alloc:  	kfree(d->mask_buf);  	kfree(d->status_buf);  	kfree(d->status_reg_buf); +	if (d->virt_buf) { +		for (i = 0; i < chip->num_virt_regs; i++) +			kfree(d->virt_buf[i]); +		kfree(d->virt_buf); +	}  	kfree(d);  	return ret;  } | 
