summaryrefslogtreecommitdiff
path: root/drivers/pinctrl/qcom/pinctrl-msm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/qcom/pinctrl-msm.c')
-rw-r--r--drivers/pinctrl/qcom/pinctrl-msm.c142
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;
}