|
@@ -196,6 +196,12 @@ static irqreturn_t exynos_eint_gpio_irq(int irq, void *data)
|
|
|
return IRQ_HANDLED;
|
|
|
}
|
|
|
|
|
|
+struct exynos_eint_gpio_save {
|
|
|
+ u32 eint_con;
|
|
|
+ u32 eint_fltcon0;
|
|
|
+ u32 eint_fltcon1;
|
|
|
+};
|
|
|
+
|
|
|
/*
|
|
|
* exynos_eint_gpio_init() - setup handling of external gpio interrupts.
|
|
|
* @d: driver data of samsung pinctrl driver.
|
|
@@ -204,8 +210,8 @@ static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d)
|
|
|
{
|
|
|
struct samsung_pin_bank *bank;
|
|
|
struct device *dev = d->dev;
|
|
|
- unsigned int ret;
|
|
|
- unsigned int i;
|
|
|
+ int ret;
|
|
|
+ int i;
|
|
|
|
|
|
if (!d->irq) {
|
|
|
dev_err(dev, "irq number not available\n");
|
|
@@ -227,11 +233,29 @@ static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d)
|
|
|
bank->nr_pins, &exynos_gpio_irqd_ops, bank);
|
|
|
if (!bank->irq_domain) {
|
|
|
dev_err(dev, "gpio irq domain add failed\n");
|
|
|
- return -ENXIO;
|
|
|
+ ret = -ENXIO;
|
|
|
+ goto err_domains;
|
|
|
+ }
|
|
|
+
|
|
|
+ bank->soc_priv = devm_kzalloc(d->dev,
|
|
|
+ sizeof(struct exynos_eint_gpio_save), GFP_KERNEL);
|
|
|
+ if (!bank->soc_priv) {
|
|
|
+ irq_domain_remove(bank->irq_domain);
|
|
|
+ ret = -ENOMEM;
|
|
|
+ goto err_domains;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
+
|
|
|
+err_domains:
|
|
|
+ for (--i, --bank; i >= 0; --i, --bank) {
|
|
|
+ if (bank->eint_type != EINT_TYPE_GPIO)
|
|
|
+ continue;
|
|
|
+ irq_domain_remove(bank->irq_domain);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
static void exynos_wkup_irq_unmask(struct irq_data *irqd)
|
|
@@ -326,6 +350,28 @@ static int exynos_wkup_irq_set_type(struct irq_data *irqd, unsigned int type)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static u32 exynos_eint_wake_mask = 0xffffffff;
|
|
|
+
|
|
|
+u32 exynos_get_eint_wake_mask(void)
|
|
|
+{
|
|
|
+ return exynos_eint_wake_mask;
|
|
|
+}
|
|
|
+
|
|
|
+static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on)
|
|
|
+{
|
|
|
+ struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
|
|
|
+ unsigned long bit = 1UL << (2 * bank->eint_offset + irqd->hwirq);
|
|
|
+
|
|
|
+ pr_info("wake %s for irq %d\n", on ? "enabled" : "disabled", irqd->irq);
|
|
|
+
|
|
|
+ if (!on)
|
|
|
+ exynos_eint_wake_mask |= bit;
|
|
|
+ else
|
|
|
+ exynos_eint_wake_mask &= ~bit;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* irq_chip for wakeup interrupts
|
|
|
*/
|
|
@@ -335,6 +381,7 @@ static struct irq_chip exynos_wkup_irq_chip = {
|
|
|
.irq_mask = exynos_wkup_irq_mask,
|
|
|
.irq_ack = exynos_wkup_irq_ack,
|
|
|
.irq_set_type = exynos_wkup_irq_set_type,
|
|
|
+ .irq_set_wake = exynos_wkup_irq_set_wake,
|
|
|
};
|
|
|
|
|
|
/* interrupt handler for wakeup interrupts 0..15 */
|
|
@@ -505,6 +552,72 @@ static int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void exynos_pinctrl_suspend_bank(
|
|
|
+ struct samsung_pinctrl_drv_data *drvdata,
|
|
|
+ struct samsung_pin_bank *bank)
|
|
|
+{
|
|
|
+ struct exynos_eint_gpio_save *save = bank->soc_priv;
|
|
|
+ void __iomem *regs = drvdata->virt_base;
|
|
|
+
|
|
|
+ save->eint_con = readl(regs + EXYNOS_GPIO_ECON_OFFSET
|
|
|
+ + bank->eint_offset);
|
|
|
+ save->eint_fltcon0 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
|
|
|
+ + 2 * bank->eint_offset);
|
|
|
+ save->eint_fltcon1 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
|
|
|
+ + 2 * bank->eint_offset + 4);
|
|
|
+
|
|
|
+ pr_debug("%s: save con %#010x\n", bank->name, save->eint_con);
|
|
|
+ pr_debug("%s: save fltcon0 %#010x\n", bank->name, save->eint_fltcon0);
|
|
|
+ pr_debug("%s: save fltcon1 %#010x\n", bank->name, save->eint_fltcon1);
|
|
|
+}
|
|
|
+
|
|
|
+static void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata)
|
|
|
+{
|
|
|
+ struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
|
|
|
+ struct samsung_pin_bank *bank = ctrl->pin_banks;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < ctrl->nr_banks; ++i, ++bank)
|
|
|
+ if (bank->eint_type == EINT_TYPE_GPIO)
|
|
|
+ exynos_pinctrl_suspend_bank(drvdata, bank);
|
|
|
+}
|
|
|
+
|
|
|
+static void exynos_pinctrl_resume_bank(
|
|
|
+ struct samsung_pinctrl_drv_data *drvdata,
|
|
|
+ struct samsung_pin_bank *bank)
|
|
|
+{
|
|
|
+ struct exynos_eint_gpio_save *save = bank->soc_priv;
|
|
|
+ void __iomem *regs = drvdata->virt_base;
|
|
|
+
|
|
|
+ pr_debug("%s: con %#010x => %#010x\n", bank->name,
|
|
|
+ readl(regs + EXYNOS_GPIO_ECON_OFFSET
|
|
|
+ + bank->eint_offset), save->eint_con);
|
|
|
+ pr_debug("%s: fltcon0 %#010x => %#010x\n", bank->name,
|
|
|
+ readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
|
|
|
+ + 2 * bank->eint_offset), save->eint_fltcon0);
|
|
|
+ pr_debug("%s: fltcon1 %#010x => %#010x\n", bank->name,
|
|
|
+ readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
|
|
|
+ + 2 * bank->eint_offset + 4), save->eint_fltcon1);
|
|
|
+
|
|
|
+ writel(save->eint_con, regs + EXYNOS_GPIO_ECON_OFFSET
|
|
|
+ + bank->eint_offset);
|
|
|
+ writel(save->eint_fltcon0, regs + EXYNOS_GPIO_EFLTCON_OFFSET
|
|
|
+ + 2 * bank->eint_offset);
|
|
|
+ writel(save->eint_fltcon1, regs + EXYNOS_GPIO_EFLTCON_OFFSET
|
|
|
+ + 2 * bank->eint_offset + 4);
|
|
|
+}
|
|
|
+
|
|
|
+static void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
|
|
|
+{
|
|
|
+ struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
|
|
|
+ struct samsung_pin_bank *bank = ctrl->pin_banks;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < ctrl->nr_banks; ++i, ++bank)
|
|
|
+ if (bank->eint_type == EINT_TYPE_GPIO)
|
|
|
+ exynos_pinctrl_resume_bank(drvdata, bank);
|
|
|
+}
|
|
|
+
|
|
|
/* pin banks of exynos4210 pin-controller 0 */
|
|
|
static struct samsung_pin_bank exynos4210_pin_banks0[] = {
|
|
|
EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpa0", 0x00),
|
|
@@ -568,6 +681,8 @@ struct samsung_pin_ctrl exynos4210_pin_ctrl[] = {
|
|
|
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
|
|
.svc = EXYNOS_SVC_OFFSET,
|
|
|
.eint_gpio_init = exynos_eint_gpio_init,
|
|
|
+ .suspend = exynos_pinctrl_suspend,
|
|
|
+ .resume = exynos_pinctrl_resume,
|
|
|
.label = "exynos4210-gpio-ctrl0",
|
|
|
}, {
|
|
|
/* pin-controller instance 1 data */
|
|
@@ -582,6 +697,8 @@ struct samsung_pin_ctrl exynos4210_pin_ctrl[] = {
|
|
|
.svc = EXYNOS_SVC_OFFSET,
|
|
|
.eint_gpio_init = exynos_eint_gpio_init,
|
|
|
.eint_wkup_init = exynos_eint_wkup_init,
|
|
|
+ .suspend = exynos_pinctrl_suspend,
|
|
|
+ .resume = exynos_pinctrl_resume,
|
|
|
.label = "exynos4210-gpio-ctrl1",
|
|
|
}, {
|
|
|
/* pin-controller instance 2 data */
|
|
@@ -663,6 +780,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = {
|
|
|
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
|
|
.svc = EXYNOS_SVC_OFFSET,
|
|
|
.eint_gpio_init = exynos_eint_gpio_init,
|
|
|
+ .suspend = exynos_pinctrl_suspend,
|
|
|
+ .resume = exynos_pinctrl_resume,
|
|
|
.label = "exynos4x12-gpio-ctrl0",
|
|
|
}, {
|
|
|
/* pin-controller instance 1 data */
|
|
@@ -677,6 +796,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = {
|
|
|
.svc = EXYNOS_SVC_OFFSET,
|
|
|
.eint_gpio_init = exynos_eint_gpio_init,
|
|
|
.eint_wkup_init = exynos_eint_wkup_init,
|
|
|
+ .suspend = exynos_pinctrl_suspend,
|
|
|
+ .resume = exynos_pinctrl_resume,
|
|
|
.label = "exynos4x12-gpio-ctrl1",
|
|
|
}, {
|
|
|
/* pin-controller instance 2 data */
|
|
@@ -687,6 +808,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = {
|
|
|
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
|
|
.svc = EXYNOS_SVC_OFFSET,
|
|
|
.eint_gpio_init = exynos_eint_gpio_init,
|
|
|
+ .suspend = exynos_pinctrl_suspend,
|
|
|
+ .resume = exynos_pinctrl_resume,
|
|
|
.label = "exynos4x12-gpio-ctrl2",
|
|
|
}, {
|
|
|
/* pin-controller instance 3 data */
|
|
@@ -697,6 +820,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = {
|
|
|
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
|
|
.svc = EXYNOS_SVC_OFFSET,
|
|
|
.eint_gpio_init = exynos_eint_gpio_init,
|
|
|
+ .suspend = exynos_pinctrl_suspend,
|
|
|
+ .resume = exynos_pinctrl_resume,
|
|
|
.label = "exynos4x12-gpio-ctrl3",
|
|
|
},
|
|
|
};
|
|
@@ -775,6 +900,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = {
|
|
|
.svc = EXYNOS_SVC_OFFSET,
|
|
|
.eint_gpio_init = exynos_eint_gpio_init,
|
|
|
.eint_wkup_init = exynos_eint_wkup_init,
|
|
|
+ .suspend = exynos_pinctrl_suspend,
|
|
|
+ .resume = exynos_pinctrl_resume,
|
|
|
.label = "exynos5250-gpio-ctrl0",
|
|
|
}, {
|
|
|
/* pin-controller instance 1 data */
|
|
@@ -785,6 +912,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = {
|
|
|
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
|
|
.svc = EXYNOS_SVC_OFFSET,
|
|
|
.eint_gpio_init = exynos_eint_gpio_init,
|
|
|
+ .suspend = exynos_pinctrl_suspend,
|
|
|
+ .resume = exynos_pinctrl_resume,
|
|
|
.label = "exynos5250-gpio-ctrl1",
|
|
|
}, {
|
|
|
/* pin-controller instance 2 data */
|
|
@@ -795,6 +924,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = {
|
|
|
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
|
|
.svc = EXYNOS_SVC_OFFSET,
|
|
|
.eint_gpio_init = exynos_eint_gpio_init,
|
|
|
+ .suspend = exynos_pinctrl_suspend,
|
|
|
+ .resume = exynos_pinctrl_resume,
|
|
|
.label = "exynos5250-gpio-ctrl2",
|
|
|
}, {
|
|
|
/* pin-controller instance 3 data */
|
|
@@ -805,6 +936,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = {
|
|
|
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
|
|
.svc = EXYNOS_SVC_OFFSET,
|
|
|
.eint_gpio_init = exynos_eint_gpio_init,
|
|
|
+ .suspend = exynos_pinctrl_suspend,
|
|
|
+ .resume = exynos_pinctrl_resume,
|
|
|
.label = "exynos5250-gpio-ctrl3",
|
|
|
},
|
|
|
};
|