[COMMON] pinctrl: samsung: change FLTCON register setting routine.
authorHyunki Koo <hyunki00.koo@samsung.com>
Thu, 4 Jan 2018 04:09:44 +0000 (13:09 +0900)
committerJaehyoung Choi <jkkkkk.choi@samsung.com>
Thu, 3 May 2018 11:11:51 +0000 (20:11 +0900)
The FLTCON register offset of each GPIO block is not regular.
To support irregular FLTCON offset of all GPIO block,
we should use fltcon_offset value in driver data to set FLTCON properly.

And we need to check whether both FLTCON0 and FLTCON1 should be set or not.
When nr_pins(The number of pins in the signle GPIO bank)
is greater than 4(The max number of pins in the single GPIO bank),
The FILTCON1 register also should be set.
When nr_pins is not greater than 4, we don't need to set FLTCON1.

Only alive block(gpa) has the filter selection bit.
So we need to set filter selction bit in case of alive block.
If some GPIO block is not alive, we should not set filter selection bit.

Change-Id: Icadc5f8df49fbf9b983918150e7016f96c40e57c
Signed-off-by: Youngmin Nam <youngmin.nam@samsung.com>
Signed-off-by: Hyunki Koo <hyunki00.koo@samsung.com>
drivers/pinctrl/samsung/pinctrl-exynos.c
drivers/pinctrl/samsung/pinctrl-exynos.h

index c8d0de7ea16054d616a2aaaa493e6df1386fedd4..e782d2f08c39786c5c376d246cbcc0990ecaa967 100644 (file)
@@ -268,6 +268,49 @@ struct exynos_eint_gpio_save {
        u32 eint_fltcon1;
 };
 
+static void exynos_eint_flt_config(int en, int sel, int width,
+                                  struct samsung_pinctrl_drv_data *d,
+                                  struct samsung_pin_bank *bank)
+{
+       unsigned int flt_reg, flt_con;
+       unsigned int val, shift;
+       int i;
+       int loop_cnt;
+
+       flt_con = 0;
+
+       if (en)
+               flt_con |= EXYNOS_EINT_FLTCON_EN;
+
+       if (sel)
+               flt_con |= EXYNOS_EINT_FLTCON_SEL;
+
+       flt_con |= EXYNOS_EINT_FLTCON_WIDTH(width);
+
+       flt_reg = EXYNOS_GPIO_EFLTCON_OFFSET + bank->fltcon_offset;
+
+       if (bank->nr_pins > 4)
+               /* if nr_pins > 4, we should set FLTCON0 register fully. (pin0 ~ 3) */
+               /* So, we shoud loop 4 times in case of FLTCON0. */
+               loop_cnt = 4;
+       else
+               loop_cnt = bank->nr_pins;
+
+       val = readl(d->virt_base + flt_reg);
+
+       for (i = 0; i < loop_cnt; i++) {
+               shift = i * EXYNOS_EINT_FLTCON_LEN;
+               val &= ~(EXYNOS_EINT_FLTCON_MASK << shift);
+               val |= (flt_con << shift);
+       }
+
+       writel(val, d->virt_base + flt_reg);
+
+       /* if nr_pins > 4, we should also set FLTCON1 register like FLTCON0. (pin4 ~ ) */
+       if (bank->nr_pins > 4)
+               writel(val, d->virt_base + flt_reg + 0x4);
+};
+
 /*
  * exynos_eint_gpio_init() - setup handling of external gpio interrupts.
  * @d: driver data of samsung pinctrl driver.
@@ -312,6 +355,10 @@ int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d)
                }
 
                bank->irq_chip = &exynos_gpio_irq_chip;
+
+               /* There is no filter selection register except for alive block */
+               /* Except for alive block, digital filter is default setting */
+               exynos_eint_flt_config(EXYNOS_EINT_FLTCON_EN, 0, 0, d, bank);
        }
 
        return 0;
@@ -481,6 +528,17 @@ int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
                if (bank->eint_type != EINT_TYPE_WKUP)
                        continue;
 
+               if (strncmp(bank->name, "gpa", 3) == 0) {
+                       /* Only alive block has filter selection register. */
+                       /* Setting Digital Filter */
+                       exynos_eint_flt_config(EXYNOS_EINT_FLTCON_EN,
+                               EXYNOS_EINT_FLTCON_SEL, 0, d, bank);
+               } else {
+                       /* There is no filter selection register except for alive block */
+                       /* Except for alive block, digital filter is default setting */
+                       exynos_eint_flt_config(EXYNOS_EINT_FLTCON_EN, 0, 0, d, bank);
+               }
+
                bank->irq_domain = irq_domain_add_linear(bank->of_node,
                                bank->nr_pins, &exynos_eint_irqd_ops, bank);
                if (!bank->irq_domain) {
@@ -555,24 +613,34 @@ static void exynos_pinctrl_suspend_bank(
 
        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);
+                                               + bank->fltcon_offset);
+       if (bank->nr_pins > 4)
+               save->eint_fltcon1 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
+                                                       + bank->fltcon_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);
+       if (bank->nr_pins > 4)
+               pr_debug("%s: save fltcon1 %#010x\n", bank->name, save->eint_fltcon1);
 }
 
 void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata)
 {
        struct samsung_pin_bank *bank = drvdata->pin_banks;
+       struct samsung_pinctrl_drv_data *d = bank->drvdata;
        int i;
 
        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 ||
+                       bank->eint_type == EINT_TYPE_WKUP_MUX) {
+                       /* Setting Analog Filter */
+                       exynos_eint_flt_config(EXYNOS_EINT_FLTCON_EN,
+                                       0, 0, d, bank);
+               }
 }
 
 static void exynos_pinctrl_resume_bank(
@@ -587,27 +655,49 @@ static void exynos_pinctrl_resume_bank(
                        + 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);
+                       + bank->fltcon_offset), save->eint_fltcon0);
+       if (bank->nr_pins > 4) {
        pr_debug("%s: fltcon1 %#010x => %#010x\n", bank->name,
                        readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
-                       + 2 * bank->eint_offset + 4), save->eint_fltcon1);
+                               + bank->fltcon_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);
+                                                       + bank->fltcon_offset);
+       if (bank->nr_pins > 4) {
        writel(save->eint_fltcon1, regs + EXYNOS_GPIO_EFLTCON_OFFSET
-                                               + 2 * bank->eint_offset + 4);
+                                                       + bank->fltcon_offset + 4);
+       }
 }
 
 void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
 {
        struct samsung_pin_bank *bank = drvdata->pin_banks;
+       struct samsung_pinctrl_drv_data *d = bank->drvdata;
        int i;
 
-       for (i = 0; i < drvdata->nr_banks; ++i, ++bank)
-               if (bank->eint_type == EINT_TYPE_GPIO)
+       for (i = 0; i < drvdata->nr_banks; ++i, ++bank) {
+               if (bank->eint_type == EINT_TYPE_GPIO) {
                        exynos_pinctrl_resume_bank(drvdata, bank);
+               } else if (bank->eint_type == EINT_TYPE_WKUP ||
+                       bank->eint_type == EINT_TYPE_WKUP_MUX) {
+                       /* Only alive block(gpa) has filter selection register. */
+                       if (strncmp(bank->name, "gpa", 3) == 0) {
+                               /* Setting Digital Filter */
+                               exynos_eint_flt_config(EXYNOS_EINT_FLTCON_EN,
+                                               EXYNOS_EINT_FLTCON_SEL, 0, d, bank);
+                       } else {
+                               /*
+                                * There is no filter selection register except for alive block.
+                                * Except for alive block, digital filter is default setting
+                                * without any setting.
+                                */
+                               exynos_eint_flt_config(EXYNOS_EINT_FLTCON_EN, 0, 0, d, bank);
+                       }
+               }
+       }
 }
 
 static void exynos_retention_enable(struct samsung_pinctrl_drv_data *drvdata)
index db3c3d328ef3c9ca99e66401ab5b1e7e4f2633d6..0c8f052790fbfd40fff0cd4738b6c24d87590af1 100644 (file)
 #define EXYNOS_EINT_CON_MASK           0xF
 #define EXYNOS_EINT_CON_LEN            4
 
+/* EINT filter configuration */
+#define EXYNOS_EINT_FLTCON_EN          (1 << 7)
+#define EXYNOS_EINT_FLTCON_SEL         (1 << 6)
+#define EXYNOS_EINT_FLTCON_WIDTH(x)    ((x) & 0x3f)
+#define EXYNOS_EINT_FLTCON_MASK                0xFF
+#define EXYNOS_EINT_FLTCON_LEN         8
+
 #define EXYNOS_EINT_MAX_PER_BANK       8
 #define EXYNOS_EINT_NR_WKUP_EINT