diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-26 01:48:06 +0300 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-12-26 01:48:06 +0300 | 
| commit | b1669432b3555954124ffd987a6ff2308344c88f (patch) | |
| tree | c3a4069092f3cb5501a66d30d43bb15b7ff9f929 | |
| parent | 0051db82182bfd80d6c76982bcb36b09eb338a89 (diff) | |
| parent | 58331d618bd9ced88a21a9b68c7743b84c2f4803 (diff) | |
| download | linux-b1669432b3555954124ffd987a6ff2308344c88f.tar.xz | |
Merge tag 'regmap-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap
Pull regmap updates from Mark Brown:
 "This has been a busy release for the regmap-irq code, there's several
  new features been added, including an API cleanup for how we specify
  types that affected one existing driver (gpio-max77620):
   - Support for hardware that flags rising and falling edges on
     separate status bits from Bartosz Golaszewski.
   - Support for explicitly clearing interrupts before unmasking from
     Bartosz Golaszewski.
   - Support for level triggered IRQs from Matti Vaittinen"
* tag 'regmap-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: irq: add an option to clear status registers on unmask
  regmap: regmap-irq/gpio-max77620: add level-irq support
  regmap: regmap-irq: Remove default irq type setting from core
  regmap: debugfs: convert to DEFINE_SHOW_ATTRIBUTE
  regmap: rbtree: convert to DEFINE_SHOW_ATTRIBUTE
  regmap: irq: handle HW using separate rising/falling edge interrupts
  regmap: add a new macro:REGMAP_IRQ_REG_LINE(_id, _reg_bits)
| -rw-r--r-- | drivers/base/regmap/regcache-rbtree.c | 12 | ||||
| -rw-r--r-- | drivers/base/regmap/regmap-debugfs.c | 12 | ||||
| -rw-r--r-- | drivers/base/regmap/regmap-irq.c | 142 | ||||
| -rw-r--r-- | drivers/gpio/gpio-max77620.c | 96 | ||||
| -rw-r--r-- | include/linux/regmap.h | 41 | 
5 files changed, 200 insertions, 103 deletions
diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index b1e9aae9a5d0..2e8f0144f9ab 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -177,17 +177,7 @@ static int rbtree_show(struct seq_file *s, void *ignored)  	return 0;  } -static int rbtree_open(struct inode *inode, struct file *file) -{ -	return single_open(file, rbtree_show, inode->i_private); -} - -static const struct file_operations rbtree_fops = { -	.open		= rbtree_open, -	.read		= seq_read, -	.llseek		= seq_lseek, -	.release	= single_release, -}; +DEFINE_SHOW_ATTRIBUTE(rbtree);  static void rbtree_debugfs_init(struct regmap *map)  { diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 87b562e49a43..19eb454f26c3 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -435,17 +435,7 @@ static int regmap_access_show(struct seq_file *s, void *ignored)  	return 0;  } -static int access_open(struct inode *inode, struct file *file) -{ -	return single_open(file, regmap_access_show, inode->i_private); -} - -static const struct file_operations regmap_access_fops = { -	.open		= access_open, -	.read		= seq_read, -	.llseek		= seq_lseek, -	.release	= single_release, -}; +DEFINE_SHOW_ATTRIBUTE(regmap_access);  static ssize_t regmap_cache_only_write_file(struct file *file,  					    const char __user *user_buf, diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 429ca8ed7e51..1bd1145ad8b5 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -44,6 +44,8 @@ struct regmap_irq_chip_data {  	unsigned int irq_reg_stride;  	unsigned int type_reg_stride; + +	bool clear_status:1;  };  static inline const @@ -77,6 +79,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)  	int i, ret;  	u32 reg;  	u32 unmask_offset; +	u32 val;  	if (d->chip->runtime_pm) {  		ret = pm_runtime_get_sync(map->dev); @@ -85,6 +88,20 @@ static void regmap_irq_sync_unlock(struct irq_data *data)  				ret);  	} +	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); + +			ret = regmap_read(map, reg, &val); +			if (ret) +				dev_err(d->map->dev, +					"Failed to clear the interrupt status bits\n"); +		} + +		d->clear_status = false; +	} +  	/*  	 * If there's been a change in the mask write it back to the  	 * hardware.  We rely on the use of the regmap core cache to @@ -157,20 +174,23 @@ 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); -		if (d->chip->type_invert) -			ret = regmap_irq_update_bits(d, reg, -				d->type_buf_def[i], ~d->type_buf[i]); -		else -			ret = regmap_irq_update_bits(d, reg, -				d->type_buf_def[i], d->type_buf[i]); -		if (ret != 0) -			dev_err(d->map->dev, "Failed to sync type in %x\n", -				reg); +	/* Don't update the type bits if we're using mask bits for irq type. */ +	if (!d->chip->type_in_mask) { +		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); +			if (d->chip->type_invert) +				ret = regmap_irq_update_bits(d, reg, +					d->type_buf_def[i], ~d->type_buf[i]); +			else +				ret = regmap_irq_update_bits(d, reg, +					d->type_buf_def[i], d->type_buf[i]); +			if (ret != 0) +				dev_err(d->map->dev, "Failed to sync type in %x\n", +					reg); +		}  	}  	if (d->chip->runtime_pm) @@ -194,8 +214,30 @@ static void regmap_irq_enable(struct irq_data *data)  	struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);  	struct regmap *map = d->map;  	const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); +	unsigned int mask, type; + +	type = irq_data->type.type_falling_val | irq_data->type.type_rising_val; -	d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; +	/* +	 * The type_in_mask flag means that the underlying hardware uses +	 * separate mask bits for rising and falling edge interrupts, but +	 * we want to make them into a single virtual interrupt with +	 * configurable edge. +	 * +	 * If the interrupt we're enabling defines the falling or rising +	 * masks then instead of using the regular mask bits for this +	 * interrupt, use the value previously written to the type buffer +	 * at the corresponding offset in regmap_irq_set_type(). +	 */ +	if (d->chip->type_in_mask && type) +		mask = d->type_buf[irq_data->reg_offset / map->reg_stride]; +	else +		mask = irq_data->mask; + +	if (d->chip->clear_on_unmask) +		d->clear_status = true; + +	d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~mask;  }  static void regmap_irq_disable(struct irq_data *data) @@ -212,27 +254,42 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type)  	struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);  	struct regmap *map = d->map;  	const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); -	int reg = irq_data->type_reg_offset / map->reg_stride; +	int reg; +	const struct regmap_irq_type *t = &irq_data->type; -	if (!(irq_data->type_rising_mask | irq_data->type_falling_mask)) -		return 0; +	if ((t->types_supported & type) != type) +		return -ENOTSUPP; -	d->type_buf[reg] &= ~(irq_data->type_falling_mask | -					irq_data->type_rising_mask); +	reg = t->type_reg_offset / map->reg_stride; + +	if (t->type_reg_mask) +		d->type_buf[reg] &= ~t->type_reg_mask; +	else +		d->type_buf[reg] &= ~(t->type_falling_val | +				      t->type_rising_val | +				      t->type_level_low_val | +				      t->type_level_high_val);  	switch (type) {  	case IRQ_TYPE_EDGE_FALLING: -		d->type_buf[reg] |= irq_data->type_falling_mask; +		d->type_buf[reg] |= t->type_falling_val;  		break;  	case IRQ_TYPE_EDGE_RISING: -		d->type_buf[reg] |= irq_data->type_rising_mask; +		d->type_buf[reg] |= t->type_rising_val;  		break;  	case IRQ_TYPE_EDGE_BOTH: -		d->type_buf[reg] |= (irq_data->type_falling_mask | -					irq_data->type_rising_mask); +		d->type_buf[reg] |= (t->type_falling_val | +					t->type_rising_val);  		break; +	case IRQ_TYPE_LEVEL_HIGH: +		d->type_buf[reg] |= t->type_level_high_val; +		break; + +	case IRQ_TYPE_LEVEL_LOW: +		d->type_buf[reg] |= t->type_level_low_val; +		break;  	default:  		return -EINVAL;  	} @@ -430,12 +487,16 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,  	struct regmap_irq_chip_data *d;  	int i;  	int ret = -ENOMEM; +	int num_type_reg;  	u32 reg;  	u32 unmask_offset;  	if (chip->num_regs <= 0)  		return -EINVAL; +	if (chip->clear_on_unmask && (chip->ack_base || chip->use_ack)) +		return -EINVAL; +  	for (i = 0; i < chip->num_irqs; i++) {  		if (chip->irqs[i].reg_offset % map->reg_stride)  			return -EINVAL; @@ -479,13 +540,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,  			goto err_alloc;  	} -	if (chip->num_type_reg) { -		d->type_buf_def = kcalloc(chip->num_type_reg, -					sizeof(unsigned int), GFP_KERNEL); +	num_type_reg = chip->type_in_mask ? chip->num_regs : chip->num_type_reg; +	if (num_type_reg) { +		d->type_buf_def = kcalloc(num_type_reg, +					  sizeof(unsigned int), GFP_KERNEL);  		if (!d->type_buf_def)  			goto err_alloc; -		d->type_buf = kcalloc(chip->num_type_reg, sizeof(unsigned int), +		d->type_buf = kcalloc(num_type_reg, sizeof(unsigned int),  				      GFP_KERNEL);  		if (!d->type_buf)  			goto err_alloc; @@ -600,27 +662,21 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,  		}  	} -	if (chip->num_type_reg) { -		for (i = 0; i < chip->num_irqs; i++) { -			reg = chip->irqs[i].type_reg_offset / map->reg_stride; -			d->type_buf_def[reg] |= chip->irqs[i].type_rising_mask | -					chip->irqs[i].type_falling_mask; -		} +	if (chip->num_type_reg && !chip->type_in_mask) {  		for (i = 0; i < chip->num_type_reg; ++i) {  			if (!d->type_buf_def[i])  				continue;  			reg = chip->type_base +  				(i * map->reg_stride * d->type_reg_stride); -			if (chip->type_invert) -				ret = regmap_irq_update_bits(d, reg, -					d->type_buf_def[i], 0xFF); -			else -				ret = regmap_irq_update_bits(d, reg, -					d->type_buf_def[i], 0x0); -			if (ret != 0) { -				dev_err(map->dev, -					"Failed to set type in 0x%x: %x\n", + +			ret = regmap_read(map, reg, &d->type_buf_def[i]); + +			if (d->chip->type_invert) +				d->type_buf_def[i] = ~d->type_buf_def[i]; + +			if (ret) { +				dev_err(map->dev, "Failed to get type defaults at 0x%x: %d\n",  					reg, ret);  				goto err_alloc;  			} diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 538bce4b5b42..65fa3a198ebd 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -25,60 +25,92 @@ struct max77620_gpio {  static const struct regmap_irq max77620_gpio_irqs[] = {  	[0] = { -		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE0, -		.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, -		.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,  		.reg_offset = 0, -		.type_reg_offset = 0, +		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE0, +		.type = { +			.type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, +			.type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, +			.type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, +			.type_reg_offset = 0, +			.types_supported = IRQ_TYPE_EDGE_BOTH, +		},  	},  	[1] = { -		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE1, -		.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, -		.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,  		.reg_offset = 0, -		.type_reg_offset = 1, +		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE1, +		.type = { +			.type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, +			.type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, +			.type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, +			.type_reg_offset = 1, +			.types_supported = IRQ_TYPE_EDGE_BOTH, +		},  	},  	[2] = { -		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE2, -		.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, -		.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,  		.reg_offset = 0, -		.type_reg_offset = 2, +		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE2, +		.type = { +			.type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, +			.type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, +			.type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, +			.type_reg_offset = 2, +			.types_supported = IRQ_TYPE_EDGE_BOTH, +		},  	},  	[3] = { -		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE3, -		.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, -		.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,  		.reg_offset = 0, -		.type_reg_offset = 3, +		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE3, +		.type = { +			.type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, +			.type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, +			.type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, +			.type_reg_offset = 3, +			.types_supported = IRQ_TYPE_EDGE_BOTH, +		},  	},  	[4] = { -		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE4, -		.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, -		.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,  		.reg_offset = 0, -		.type_reg_offset = 4, +		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE4, +		.type = { +			.type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, +			.type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, +			.type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, +			.type_reg_offset = 4, +			.types_supported = IRQ_TYPE_EDGE_BOTH, +		},  	},  	[5] = { -		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE5, -		.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, -		.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,  		.reg_offset = 0, -		.type_reg_offset = 5, +		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE5, +		.type = { +			.type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, +			.type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, +			.type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, +			.type_reg_offset = 5, +			.types_supported = IRQ_TYPE_EDGE_BOTH, +		},  	},  	[6] = { -		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE6, -		.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, -		.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,  		.reg_offset = 0, -		.type_reg_offset = 6, +		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE6, +		.type = { +			.type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, +			.type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, +			.type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, +			.type_reg_offset = 6, +			.types_supported = IRQ_TYPE_EDGE_BOTH, +		},  	},  	[7] = { -		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE7, -		.type_rising_mask = MAX77620_CNFG_GPIO_INT_RISING, -		.type_falling_mask = MAX77620_CNFG_GPIO_INT_FALLING,  		.reg_offset = 0, -		.type_reg_offset = 7, +		.mask = MAX77620_IRQ_LVL2_GPIO_EDGE7, +		.type = { +			.type_rising_val = MAX77620_CNFG_GPIO_INT_RISING, +			.type_falling_val = MAX77620_CNFG_GPIO_INT_FALLING, +			.type_reg_mask = MAX77620_CNFG_GPIO_INT_MASK, +			.type_reg_offset = 7, +			.types_supported = IRQ_TYPE_EDGE_BOTH, +		},  	},  }; diff --git a/include/linux/regmap.h b/include/linux/regmap.h index a367d59c301d..1781b6cb793c 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -1089,27 +1089,48 @@ int regmap_fields_read(struct regmap_field *field, unsigned int id,  int regmap_fields_update_bits_base(struct regmap_field *field,  unsigned int id,  				   unsigned int mask, unsigned int val,  				   bool *change, bool async, bool force); +/** + * struct regmap_irq_type - IRQ type definitions. + * + * @type_reg_offset: Offset register for the irq type setting. + * @type_rising_val: Register value to configure RISING type irq. + * @type_falling_val: Register value to configure FALLING type irq. + * @type_level_low_val: Register value to configure LEVEL_LOW type irq. + * @type_level_high_val: Register value to configure LEVEL_HIGH type irq. + * @types_supported: logical OR of IRQ_TYPE_* flags indicating supported types. + */ +struct regmap_irq_type { +	unsigned int type_reg_offset; +	unsigned int type_reg_mask; +	unsigned int type_rising_val; +	unsigned int type_falling_val; +	unsigned int type_level_low_val; +	unsigned int type_level_high_val; +	unsigned int types_supported; +};  /**   * struct regmap_irq - Description of an IRQ for the generic regmap irq_chip.   *   * @reg_offset: Offset of the status/mask register within the bank   * @mask:       Mask used to flag/control the register. - * @type_reg_offset: Offset register for the irq type setting. - * @type_rising_mask: Mask bit to configure RISING type irq. - * @type_falling_mask: Mask bit to configure FALLING type irq. + * @type:	IRQ trigger type setting details if supported.   */  struct regmap_irq {  	unsigned int reg_offset;  	unsigned int mask; -	unsigned int type_reg_offset; -	unsigned int type_rising_mask; -	unsigned int type_falling_mask; +	struct regmap_irq_type type;  };  #define REGMAP_IRQ_REG(_irq, _off, _mask)		\  	[_irq] = { .reg_offset = (_off), .mask = (_mask) } +#define REGMAP_IRQ_REG_LINE(_id, _reg_bits) \ +	[_id] = {				\ +		.mask = BIT((_id) % (_reg_bits)),	\ +		.reg_offset = (_id) / (_reg_bits),	\ +	} +  /**   * struct regmap_irq_chip - Description of a generic regmap irq_chip.   * @@ -1131,6 +1152,12 @@ struct regmap_irq {   * @ack_invert:  Inverted ack register: cleared bits for ack.   * @wake_invert: Inverted wake register: cleared bits are wake enabled.   * @type_invert: Invert the type flags. + * @type_in_mask: Use the mask registers for controlling irq type. For + *                interrupts defining type_rising/falling_mask use mask_base + *                for edge configuration and never update bits in type_base. + * @clear_on_unmask: For chips with interrupts cleared on read: read the status + *                   registers before unmasking interrupts to clear any bits + *                   set when they were masked.   * @runtime_pm:  Hold a runtime PM lock on the device when accessing it.   *   * @num_regs:    Number of registers in each control bank. @@ -1169,6 +1196,8 @@ struct regmap_irq_chip {  	bool wake_invert:1;  	bool runtime_pm:1;  	bool type_invert:1; +	bool type_in_mask:1; +	bool clear_on_unmask:1;  	int num_regs;  | 
