diff options
Diffstat (limited to 'drivers/pinctrl/mediatek/pinctrl-mtk-common.c')
-rw-r--r-- | drivers/pinctrl/mediatek/pinctrl-mtk-common.c | 76 |
1 files changed, 73 insertions, 3 deletions
diff --git a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c index 2864fe333ad9..a82ae1a6141d 100644 --- a/drivers/pinctrl/mediatek/pinctrl-mtk-common.c +++ b/drivers/pinctrl/mediatek/pinctrl-mtk-common.c @@ -792,6 +792,32 @@ static unsigned int mtk_eint_get_mask(struct mtk_pinctrl *pctl, return !!(readl(reg) & bit); } +static int mtk_eint_flip_edge(struct mtk_pinctrl *pctl, int hwirq) +{ + int start_level, curr_level; + unsigned int reg_offset; + const struct mtk_eint_offsets *eint_offsets = &(pctl->devdata->eint_offsets); + u32 mask = 1 << (hwirq & 0x1f); + u32 port = (hwirq >> 5) & eint_offsets->port_mask; + void __iomem *reg = pctl->eint_reg_base + (port << 2); + const struct mtk_desc_pin *pin; + + pin = mtk_find_pin_by_eint_num(pctl, hwirq); + curr_level = mtk_gpio_get(pctl->chip, pin->pin.number); + do { + start_level = curr_level; + if (start_level) + reg_offset = eint_offsets->pol_clr; + else + reg_offset = eint_offsets->pol_set; + writel(mask, reg + reg_offset); + + curr_level = mtk_gpio_get(pctl->chip, pin->pin.number); + } while (start_level != curr_level); + + return start_level; +} + static void mtk_eint_mask(struct irq_data *d) { struct mtk_pinctrl *pctl = irq_data_get_irq_chip_data(d); @@ -814,6 +840,9 @@ static void mtk_eint_unmask(struct irq_data *d) eint_offsets->mask_clr); writel(mask, reg); + + if (pctl->eint_dual_edges[d->hwirq]) + mtk_eint_flip_edge(pctl, d->hwirq); } static int mtk_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, @@ -893,13 +922,17 @@ static int mtk_eint_set_type(struct irq_data *d, void __iomem *reg; if (((type & IRQ_TYPE_EDGE_BOTH) && (type & IRQ_TYPE_LEVEL_MASK)) || - ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) || ((type & IRQ_TYPE_LEVEL_MASK) == IRQ_TYPE_LEVEL_MASK)) { dev_err(pctl->dev, "Can't configure IRQ%d (EINT%lu) for type 0x%X\n", d->irq, d->hwirq, type); return -EINVAL; } + if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) + pctl->eint_dual_edges[d->hwirq] = 1; + else + pctl->eint_dual_edges[d->hwirq] = 0; + if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) { reg = mtk_eint_get_offset(pctl, d->hwirq, eint_offsets->pol_clr); @@ -920,6 +953,9 @@ static int mtk_eint_set_type(struct irq_data *d, writel(mask, reg); } + if (pctl->eint_dual_edges[d->hwirq]) + mtk_eint_flip_edge(pctl, d->hwirq); + return 0; } @@ -986,6 +1022,8 @@ static void mtk_eint_irq_handler(unsigned irq, struct irq_desc *desc) const struct mtk_eint_offsets *eint_offsets = &pctl->devdata->eint_offsets; void __iomem *reg = mtk_eint_get_offset(pctl, 0, eint_offsets->stat); + int dual_edges, start_level, curr_level; + const struct mtk_desc_pin *pin; chained_irq_enter(chip, desc); for (eint_num = 0; eint_num < pctl->devdata->ap_num; eint_num += 32) { @@ -997,8 +1035,31 @@ static void mtk_eint_irq_handler(unsigned irq, struct irq_desc *desc) virq = irq_find_mapping(pctl->domain, index); status &= ~BIT(offset); + dual_edges = pctl->eint_dual_edges[index]; + if (dual_edges) { + /* Clear soft-irq in case we raised it + last time */ + writel(BIT(offset), reg - eint_offsets->stat + + eint_offsets->soft_clr); + + pin = mtk_find_pin_by_eint_num(pctl, index); + start_level = mtk_gpio_get(pctl->chip, + pin->pin.number); + } + generic_handle_irq(virq); + if (dual_edges) { + curr_level = mtk_eint_flip_edge(pctl, index); + + /* If level changed, we might lost one edge + interrupt, raised it through soft-irq */ + if (start_level != curr_level) + writel(BIT(offset), reg - + eint_offsets->stat + + eint_offsets->soft_set); + } + if (index < pctl->devdata->db_cnt) mtk_eint_debounce_process(pctl , index); } @@ -1149,11 +1210,18 @@ int mtk_pctrl_init(struct platform_device *pdev, goto chip_error; } + pctl->eint_dual_edges = devm_kzalloc(&pdev->dev, + sizeof(int) * pctl->devdata->ap_num, GFP_KERNEL); + if (!pctl->eint_dual_edges) { + ret = -ENOMEM; + goto chip_error; + } + irq = irq_of_parse_and_map(np, 0); if (!irq) { dev_err(&pdev->dev, "couldn't parse and map irq\n"); ret = -EINVAL; - goto chip_error; + goto free_edges; } pctl->domain = irq_domain_add_linear(np, @@ -1161,7 +1229,7 @@ int mtk_pctrl_init(struct platform_device *pdev, if (!pctl->domain) { dev_err(&pdev->dev, "Couldn't register IRQ domain\n"); ret = -ENOMEM; - goto chip_error; + goto free_edges; } mtk_eint_init(pctl); @@ -1179,6 +1247,8 @@ int mtk_pctrl_init(struct platform_device *pdev, set_irq_flags(irq, IRQF_VALID); return 0; +free_edges: + kfree(pctl->eint_dual_edges); chip_error: gpiochip_remove(pctl->chip); pctrl_error: |