From: Hyunki Koo Date: Thu, 4 Jan 2018 04:09:44 +0000 (+0900) Subject: [COMMON] pinctrl: samsung: change FLTCON register setting routine. X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=7f0d53f1e9aa73e29e4f99d5ccb01c536f2595be;p=GitHub%2FLineageOS%2Fandroid_kernel_motorola_exynos9610.git [COMMON] pinctrl: samsung: change FLTCON register setting routine. 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 Signed-off-by: Hyunki Koo --- diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c index c8d0de7ea160..e782d2f08c39 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos.c +++ b/drivers/pinctrl/samsung/pinctrl-exynos.c @@ -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) diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.h b/drivers/pinctrl/samsung/pinctrl-exynos.h index db3c3d328ef3..0c8f052790fb 100644 --- a/drivers/pinctrl/samsung/pinctrl-exynos.h +++ b/drivers/pinctrl/samsung/pinctrl-exynos.h @@ -49,6 +49,13 @@ #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