From bc998a730367a69a1449320d321187d7414668fa Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 7 Dec 2018 14:04:52 +0100 Subject: regmap: irq: handle HW using separate rising/falling edge interrupts Some interrupt controllers use separate bits for controlling rising and falling edge interrupts in the mask register i.e. they have one interrupt for rising edge and one for falling. We already handle the case where we have a single interrupt in the mask register and a separate type configuration register. Add a new switch to regmap_irq_chip which tells the framework to use the mask_base address for configuring the edge of the interrupts that define type_falling/rising_mask values. For such interrupts we never update the type_base bits. For interrupts that don't define type masks or their regmap irq chip doesn't set the type_in_mask to true everything stays the same. Signed-off-by: Bartosz Golaszewski Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 64 +++++++++++++++++++++++++++------------- 1 file changed, 44 insertions(+), 20 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 429ca8ed7e51..603b1554f81c 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -157,20 +157,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 +197,27 @@ 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_falling_mask | irq_data->type_rising_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; - d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; + d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~mask; } static void regmap_irq_disable(struct irq_data *data) @@ -430,6 +452,7 @@ 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; @@ -479,13 +502,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,7 +624,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, } } - if (chip->num_type_reg) { + if (chip->num_type_reg && !chip->type_in_mask) { 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 | -- cgit v1.2.3 From 32fa7b852feaf838a145ac0408ad0a14d24d2eec Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sat, 15 Dec 2018 03:38:37 -0500 Subject: regmap: rbtree: convert to DEFINE_SHOW_ATTRIBUTE Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code. Signed-off-by: Yangtao Li Signed-off-by: Mark Brown --- drivers/base/regmap/regcache-rbtree.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers/base') 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) { -- cgit v1.2.3 From 580d48573c43211fe51f411d484dbb693cc93f7c Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sat, 15 Dec 2018 03:41:19 -0500 Subject: regmap: debugfs: convert to DEFINE_SHOW_ATTRIBUTE Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code. Signed-off-by: Yangtao Li Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'drivers/base') 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, -- cgit v1.2.3 From 84267d1b18abee0c141553396f52a23db71660d3 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 18 Dec 2018 12:58:13 +0200 Subject: regmap: regmap-irq: Remove default irq type setting from core The common code should not set IRQ type. Read HW defaults to the cache at startup instead of forcing type to EDGE_BOTH. If default setting is needed this should be done via normal mechanisms or by chip specific code if normal mechanisms are not suitable for some reason. Common regmap-irq code should not have defaults hard-coded but keep the HW/boot defaults untouched. Signed-off-by: Matti Vaittinen Tested-by: Bartosz Golaszewski Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 603b1554f81c..8b216b2e2c19 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -625,26 +625,20 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, } if (chip->num_type_reg && !chip->type_in_mask) { - 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; - } 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; } -- cgit v1.2.3 From 1c2928e3e3212252b505b746ec10951027a95813 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 18 Dec 2018 13:59:31 +0200 Subject: regmap: regmap-irq/gpio-max77620: add level-irq support Add level active IRQ support to regmap-irq irqchip. Change breaks existing regmap-irq type setting. Convert the existing drivers which use regmap-irq with trigger type setting (gpio-max77620) to work with this new approach. So we do not magically support level-active IRQs on gpio-max77620 - but add support to the regmap-irq for chips which support them =) We do not support distinguishing situation where HW supports rising and falling edge detection but not both. Separating this would require inventing yet another flags for IRQ types. Signed-off-by: Matti Vaittinen Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 35 +++++++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 10 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 8b216b2e2c19..31d23c9a5ae7 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -199,7 +199,7 @@ static void regmap_irq_enable(struct irq_data *data) const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); unsigned int mask, type; - type = irq_data->type_falling_mask | irq_data->type_rising_mask; + type = irq_data->type.type_falling_val | irq_data->type.type_rising_val; /* * The type_in_mask flag means that the underlying hardware uses @@ -234,27 +234,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; + + reg = t->type_reg_offset / map->reg_stride; - d->type_buf[reg] &= ~(irq_data->type_falling_mask | - irq_data->type_rising_mask); + 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; } -- cgit v1.2.3 From c82ea33ead18801605b236523f21e5c893c7c253 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 19 Dec 2018 12:18:05 +0100 Subject: regmap: irq: add an option to clear status registers on unmask Some interrupt controllers whose interrupts are acked on read will set the status bits for masked interrupts without changing the state of the IRQ line. Some chips have an additional "feature" where if those set bits are not cleared before unmasking their respective interrupts, the IRQ line will change the state and we'll interpret this as an interrupt although it actually fired when it was masked. Add a new field to the irq chip struct that tells the regmap irq chip code to always clear the status registers before actually changing the irq mask values. Signed-off-by: Bartosz Golaszewski Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-irq.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 31d23c9a5ae7..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 @@ -217,6 +234,9 @@ static void regmap_irq_enable(struct irq_data *data) 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; } @@ -474,6 +494,9 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, 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; -- cgit v1.2.3