Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1669432 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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)
parents 0051db82 58331d61
Loading
Loading
Loading
Loading
+1 −11
Original line number Diff line number Diff line
@@ -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)
{
+1 −11
Original line number Diff line number Diff line
@@ -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,
+99 −43
Original line number Diff line number Diff line
@@ -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,6 +174,8 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
		}
	}

	/* 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;
@@ -172,6 +191,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
				dev_err(d->map->dev, "Failed to sync type in %x\n",
					reg);
		}
	}

	if (d->chip->runtime_pm)
		pm_runtime_put(map->dev);
@@ -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,
	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;
			}
+64 −32
Original line number Diff line number Diff line
@@ -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,
		.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,
		.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,
		.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,
		.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,
		.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,
		.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,
		.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,
		.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,
		},
	},
};

+35 −6
Original line number Diff line number Diff line
@@ -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;