diff options
Diffstat (limited to 'drivers/pinctrl/qcom/pinctrl-msm.c')
| -rw-r--r-- | drivers/pinctrl/qcom/pinctrl-msm.c | 142 |
1 files changed, 134 insertions, 8 deletions
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index a9d2e8a0aa85..22496ad167a0 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2013, Sony Mobile Communications AB. - * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -27,9 +27,10 @@ #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/spinlock.h> +#include <linux/syscore_ops.h> #include <linux/reboot.h> -#include <linux/pm.h> - +#include <linux/irqchip/msm-mpm-irq.h> +#include <linux/wakeup_reason.h> #include "../core.h" #include "../pinconf.h" #include "pinctrl-msm.h" @@ -44,6 +45,7 @@ * @pctrl: pinctrl handle. * @chip: gpiochip handle. * @restart_nb: restart notifier block. + * @irq_chip_extn: MPM extension of TLMM irqchip. * @irq: parent irq for the TLMM irq_chip. * @lock: Spinlock to protect register resources as well * as msm_pinctrl data structures. @@ -58,6 +60,7 @@ struct msm_pinctrl { struct pinctrl_dev *pctrl; struct gpio_chip chip; struct notifier_block restart_nb; + struct irq_chip *irq_chip_extn; int irq; spinlock_t lock; @@ -69,6 +72,8 @@ struct msm_pinctrl { void __iomem *regs; }; +static struct msm_pinctrl *msm_pinctrl_data; + static inline struct msm_pinctrl *to_msm_pinctrl(struct gpio_chip *gc) { return container_of(gc, struct msm_pinctrl, chip); @@ -421,7 +426,6 @@ static int msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, in writel(val, pctrl->regs + g->ctl_reg); spin_unlock_irqrestore(&pctrl->lock, flags); - return 0; } @@ -501,6 +505,10 @@ static void msm_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) unsigned i; for (i = 0; i < chip->ngpio; i++, gpio++) { + /* Bypass GPIO pins owned by TZ */ + switch (gpio) + case 81 ... 84: continue; + msm_gpio_dbg_show_one(s, NULL, chip, i, gpio); seq_puts(s, "\n"); } @@ -583,6 +591,41 @@ static void msm_gpio_irq_mask(struct irq_data *d) clear_bit(d->hwirq, pctrl->enabled_irqs); spin_unlock_irqrestore(&pctrl->lock, flags); + if (pctrl->irq_chip_extn->irq_mask) + pctrl->irq_chip_extn->irq_mask(d); +} + +static void msm_gpio_irq_enable(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = to_msm_pinctrl(gc); + const struct msm_pingroup *g; + unsigned long flags; + u32 val; + + g = &pctrl->soc->groups[d->hwirq]; + + spin_lock_irqsave(&pctrl->lock, flags); + /* clear the interrupt status bit before unmask to avoid + * any erroneous interrupts that would have got latched + * when the interrupt is not in use. + */ + val = readl_relaxed(pctrl->regs + g->intr_status_reg); + if (g->intr_ack_high) + val |= BIT(g->intr_status_bit); + else + val &= ~BIT(g->intr_status_bit); + writel_relaxed(val, pctrl->regs + g->intr_status_reg); + + val = readl_relaxed(pctrl->regs + g->intr_cfg_reg); + val |= BIT(g->intr_enable_bit); + writel_relaxed(val, pctrl->regs + g->intr_cfg_reg); + + set_bit(d->hwirq, pctrl->enabled_irqs); + + spin_unlock_irqrestore(&pctrl->lock, flags); + if (pctrl->irq_chip_extn->irq_enable) + pctrl->irq_chip_extn->irq_enable(d); } static void msm_gpio_irq_unmask(struct irq_data *d) @@ -597,6 +640,10 @@ static void msm_gpio_irq_unmask(struct irq_data *d) spin_lock_irqsave(&pctrl->lock, flags); + val = readl(pctrl->regs + g->intr_status_reg); + val &= ~BIT(g->intr_status_bit); + writel(val, pctrl->regs + g->intr_status_reg); + val = readl(pctrl->regs + g->intr_cfg_reg); val |= BIT(g->intr_enable_bit); writel(val, pctrl->regs + g->intr_cfg_reg); @@ -604,6 +651,8 @@ static void msm_gpio_irq_unmask(struct irq_data *d) set_bit(d->hwirq, pctrl->enabled_irqs); spin_unlock_irqrestore(&pctrl->lock, flags); + if (pctrl->irq_chip_extn->irq_unmask) + pctrl->irq_chip_extn->irq_unmask(d); } static void msm_gpio_irq_ack(struct irq_data *d) @@ -722,6 +771,9 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) else if (type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) irq_set_handler_locked(d, handle_edge_irq); + if (pctrl->irq_chip_extn->irq_set_type) + pctrl->irq_chip_extn->irq_set_type(d, type); + return 0; } @@ -737,11 +789,16 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on) spin_unlock_irqrestore(&pctrl->lock, flags); + if (pctrl->irq_chip_extn->irq_set_wake) + pctrl->irq_chip_extn->irq_set_wake(d, on); + return 0; } static struct irq_chip msm_gpio_irq_chip = { .name = "msmgpio", + .flags = IRQCHIP_MASK_ON_SUSPEND, + .irq_enable = msm_gpio_irq_enable, .irq_mask = msm_gpio_irq_mask, .irq_unmask = msm_gpio_irq_unmask, .irq_ack = msm_gpio_irq_ack, @@ -749,7 +806,7 @@ static struct irq_chip msm_gpio_irq_chip = { .irq_set_wake = msm_gpio_irq_set_wake, }; -static void msm_gpio_irq_handler(struct irq_desc *desc) +static bool msm_gpio_irq_handler(struct irq_desc *desc) { struct gpio_chip *gc = irq_desc_get_handler_data(desc); const struct msm_pingroup *g; @@ -759,6 +816,7 @@ static void msm_gpio_irq_handler(struct irq_desc *desc) int handled = 0; u32 val; int i; + bool ret; chained_irq_enter(chip, desc); @@ -776,13 +834,31 @@ static void msm_gpio_irq_handler(struct irq_desc *desc) } } + ret = (handled != 0); /* No interrupts were flagged */ if (handled == 0) - handle_bad_irq(desc); + ret = handle_bad_irq(desc); chained_irq_exit(chip, desc); + return ret; } +/* + * Add MPM extensions for the irqchip. + * Enable the MPM driver to enable/disable + * suspend/resume based on gpio interrupts + */ + +struct irq_chip mpm_pinctrl_extn = { + .irq_eoi = NULL, + .irq_mask = NULL, + .irq_unmask = NULL, + .irq_retrigger = NULL, + .irq_set_type = NULL, + .irq_set_wake = NULL, + .irq_disable = NULL, +}; + static int msm_gpio_init(struct msm_pinctrl *pctrl) { struct gpio_chip *chip; @@ -839,6 +915,7 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) gpiochip_set_chained_irqchip(chip, &msm_gpio_irq_chip, pctrl->irq, msm_gpio_irq_handler); + of_mpm_init(); return 0; } @@ -878,6 +955,52 @@ static void msm_pinctrl_setup_pm_reset(struct msm_pinctrl *pctrl) } } +#ifdef CONFIG_PM +static int msm_pinctrl_suspend(void) +{ + return 0; +} + +static void msm_pinctrl_resume(void) +{ + int i, irq; + u32 val; + unsigned long flags; + struct irq_desc *desc; + const struct msm_pingroup *g; + const char *name = "null"; + struct msm_pinctrl *pctrl = msm_pinctrl_data; + + if (!msm_show_resume_irq_mask) + return; + + spin_lock_irqsave(&pctrl->lock, flags); + for_each_set_bit(i, pctrl->enabled_irqs, pctrl->chip.ngpio) { + g = &pctrl->soc->groups[i]; + val = readl_relaxed(pctrl->regs + g->intr_status_reg); + if (val & BIT(g->intr_status_bit)) { + irq = irq_find_mapping(pctrl->chip.irqdomain, i); + desc = irq_to_desc(irq); + if (desc == NULL) + name = "stray irq"; + else if (desc->action && desc->action->name) + name = desc->action->name; + log_base_wakeup_reason(irq); + pr_warn("%s: %d triggered %s\n", __func__, irq, name); + } + } + spin_unlock_irqrestore(&pctrl->lock, flags); +} +#else +#define msm_pinctrl_suspend NULL +#define msm_pinctrl_resume NULL +#endif + +static struct syscore_ops msm_pinctrl_pm_ops = { + .suspend = msm_pinctrl_suspend, + .resume = msm_pinctrl_resume, +}; + int msm_pinctrl_probe(struct platform_device *pdev, const struct msm_pinctrl_soc_data *soc_data) { @@ -885,7 +1008,8 @@ int msm_pinctrl_probe(struct platform_device *pdev, struct resource *res; int ret; - pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); + msm_pinctrl_data = pctrl = devm_kzalloc(&pdev->dev, + sizeof(*pctrl), GFP_KERNEL); if (!pctrl) { dev_err(&pdev->dev, "Can't allocate msm_pinctrl\n"); return -ENOMEM; @@ -923,9 +1047,10 @@ int msm_pinctrl_probe(struct platform_device *pdev, pinctrl_unregister(pctrl->pctrl); return ret; } - + pctrl->irq_chip_extn = &mpm_pinctrl_extn; platform_set_drvdata(pdev, pctrl); + register_syscore_ops(&msm_pinctrl_pm_ops); dev_dbg(&pdev->dev, "Probed Qualcomm pinctrl driver\n"); return 0; @@ -940,6 +1065,7 @@ int msm_pinctrl_remove(struct platform_device *pdev) pinctrl_unregister(pctrl->pctrl); unregister_restart_handler(&pctrl->restart_nb); + unregister_syscore_ops(&msm_pinctrl_pm_ops); return 0; } |
