|
@@ -25,6 +25,7 @@
|
|
|
#include <linux/regmap.h>
|
|
|
#include <linux/err.h>
|
|
|
#include <linux/soc/samsung/exynos-pmu.h>
|
|
|
+#include <linux/soc/samsung/exynos-regs-pmu.h>
|
|
|
|
|
|
#include <dt-bindings/pinctrl/samsung.h>
|
|
|
|
|
@@ -37,6 +38,8 @@ struct exynos_irq_chip {
|
|
|
u32 eint_con;
|
|
|
u32 eint_mask;
|
|
|
u32 eint_pend;
|
|
|
+ u32 eint_wake_mask_value;
|
|
|
+ u32 eint_wake_mask_reg;
|
|
|
};
|
|
|
|
|
|
static inline struct exynos_irq_chip *to_exynos_irq_chip(struct irq_chip *chip)
|
|
@@ -215,6 +218,7 @@ static struct exynos_irq_chip exynos_gpio_irq_chip = {
|
|
|
.eint_con = EXYNOS_GPIO_ECON_OFFSET,
|
|
|
.eint_mask = EXYNOS_GPIO_EMASK_OFFSET,
|
|
|
.eint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
|
|
+ /* eint_wake_mask_value not used */
|
|
|
};
|
|
|
|
|
|
static int exynos_eint_irq_map(struct irq_domain *h, unsigned int virq,
|
|
@@ -330,6 +334,8 @@ u32 exynos_get_eint_wake_mask(void)
|
|
|
|
|
|
static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on)
|
|
|
{
|
|
|
+ struct irq_chip *chip = irq_data_get_irq_chip(irqd);
|
|
|
+ struct exynos_irq_chip *our_chip = to_exynos_irq_chip(chip);
|
|
|
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
|
|
|
unsigned long bit = 1UL << (2 * bank->eint_offset + irqd->hwirq);
|
|
|
|
|
@@ -339,6 +345,7 @@ static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on)
|
|
|
exynos_eint_wake_mask |= bit;
|
|
|
else
|
|
|
exynos_eint_wake_mask &= ~bit;
|
|
|
+ our_chip->eint_wake_mask_value = exynos_eint_wake_mask;
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -346,6 +353,25 @@ static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on)
|
|
|
/*
|
|
|
* irq_chip for wakeup interrupts
|
|
|
*/
|
|
|
+static const struct exynos_irq_chip s5pv210_wkup_irq_chip __initconst = {
|
|
|
+ .chip = {
|
|
|
+ .name = "s5pv210_wkup_irq_chip",
|
|
|
+ .irq_unmask = exynos_irq_unmask,
|
|
|
+ .irq_mask = exynos_irq_mask,
|
|
|
+ .irq_ack = exynos_irq_ack,
|
|
|
+ .irq_set_type = exynos_irq_set_type,
|
|
|
+ .irq_set_wake = exynos_wkup_irq_set_wake,
|
|
|
+ .irq_request_resources = exynos_irq_request_resources,
|
|
|
+ .irq_release_resources = exynos_irq_release_resources,
|
|
|
+ },
|
|
|
+ .eint_con = EXYNOS_WKUP_ECON_OFFSET,
|
|
|
+ .eint_mask = EXYNOS_WKUP_EMASK_OFFSET,
|
|
|
+ .eint_pend = EXYNOS_WKUP_EPEND_OFFSET,
|
|
|
+ .eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED,
|
|
|
+ /* Only difference with exynos4210_wkup_irq_chip: */
|
|
|
+ .eint_wake_mask_reg = S5PV210_EINT_WAKEUP_MASK,
|
|
|
+};
|
|
|
+
|
|
|
static const struct exynos_irq_chip exynos4210_wkup_irq_chip __initconst = {
|
|
|
.chip = {
|
|
|
.name = "exynos4210_wkup_irq_chip",
|
|
@@ -360,6 +386,8 @@ static const struct exynos_irq_chip exynos4210_wkup_irq_chip __initconst = {
|
|
|
.eint_con = EXYNOS_WKUP_ECON_OFFSET,
|
|
|
.eint_mask = EXYNOS_WKUP_EMASK_OFFSET,
|
|
|
.eint_pend = EXYNOS_WKUP_EPEND_OFFSET,
|
|
|
+ .eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED,
|
|
|
+ .eint_wake_mask_reg = EXYNOS_EINT_WAKEUP_MASK,
|
|
|
};
|
|
|
|
|
|
static const struct exynos_irq_chip exynos7_wkup_irq_chip __initconst = {
|
|
@@ -376,10 +404,14 @@ static const struct exynos_irq_chip exynos7_wkup_irq_chip __initconst = {
|
|
|
.eint_con = EXYNOS7_WKUP_ECON_OFFSET,
|
|
|
.eint_mask = EXYNOS7_WKUP_EMASK_OFFSET,
|
|
|
.eint_pend = EXYNOS7_WKUP_EPEND_OFFSET,
|
|
|
+ .eint_wake_mask_value = EXYNOS_EINT_WAKEUP_MASK_DISABLED,
|
|
|
+ .eint_wake_mask_reg = EXYNOS5433_EINT_WAKEUP_MASK,
|
|
|
};
|
|
|
|
|
|
/* list of external wakeup controllers supported */
|
|
|
static const struct of_device_id exynos_wkup_irq_ids[] = {
|
|
|
+ { .compatible = "samsung,s5pv210-wakeup-eint",
|
|
|
+ .data = &s5pv210_wkup_irq_chip },
|
|
|
{ .compatible = "samsung,exynos4210-wakeup-eint",
|
|
|
.data = &exynos4210_wkup_irq_chip },
|
|
|
{ .compatible = "samsung,exynos7-wakeup-eint",
|
|
@@ -542,6 +574,27 @@ int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void
|
|
|
+exynos_pinctrl_set_eint_wakeup_mask(struct samsung_pinctrl_drv_data *drvdata,
|
|
|
+ struct exynos_irq_chip *irq_chip)
|
|
|
+{
|
|
|
+ struct regmap *pmu_regs;
|
|
|
+
|
|
|
+ if (!drvdata->retention_ctrl || !drvdata->retention_ctrl->priv) {
|
|
|
+ dev_warn(drvdata->dev,
|
|
|
+ "No retention data configured bank with external wakeup interrupt. Wake-up mask will not be set.\n");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ pmu_regs = drvdata->retention_ctrl->priv;
|
|
|
+ dev_info(drvdata->dev,
|
|
|
+ "Setting external wakeup interrupt wakeup mask: 0x%x\n",
|
|
|
+ irq_chip->eint_wake_mask_value);
|
|
|
+
|
|
|
+ regmap_write(pmu_regs, irq_chip->eint_wake_mask_reg,
|
|
|
+ irq_chip->eint_wake_mask_value);
|
|
|
+}
|
|
|
+
|
|
|
static void exynos_pinctrl_suspend_bank(
|
|
|
struct samsung_pinctrl_drv_data *drvdata,
|
|
|
struct samsung_pin_bank *bank)
|
|
@@ -564,11 +617,24 @@ static void exynos_pinctrl_suspend_bank(
|
|
|
void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata)
|
|
|
{
|
|
|
struct samsung_pin_bank *bank = drvdata->pin_banks;
|
|
|
+ struct exynos_irq_chip *irq_chip = NULL;
|
|
|
int i;
|
|
|
|
|
|
- for (i = 0; i < drvdata->nr_banks; ++i, ++bank)
|
|
|
+ for (i = 0; i < drvdata->nr_banks; ++i, ++bank) {
|
|
|
if (bank->eint_type == EINT_TYPE_GPIO)
|
|
|
exynos_pinctrl_suspend_bank(drvdata, bank);
|
|
|
+ else if (bank->eint_type == EINT_TYPE_WKUP) {
|
|
|
+ if (!irq_chip) {
|
|
|
+ irq_chip = bank->irq_chip;
|
|
|
+ exynos_pinctrl_set_eint_wakeup_mask(drvdata,
|
|
|
+ irq_chip);
|
|
|
+ } else if (bank->irq_chip != irq_chip) {
|
|
|
+ dev_warn(drvdata->dev,
|
|
|
+ "More than one external wakeup interrupt chip configured (bank: %s). This is not supported by hardware nor by driver.\n",
|
|
|
+ bank->name);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static void exynos_pinctrl_resume_bank(
|