summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJaewon Kim <jaewon02.kim@samsung.com>2023-12-11 14:41:44 +0300
committerKrzysztof Kozlowski <krzysztof.kozlowski@linaro.org>2023-12-13 10:42:38 +0300
commit884fdaa53b38921165cd9afdb230502b4e1690b0 (patch)
tree577759ac6048e408b30b66c02e781b6a576f2e81
parent4a8be01a1a7a030ae7b6138602d2e060cf7a0946 (diff)
downloadlinux-884fdaa53b38921165cd9afdb230502b4e1690b0.tar.xz
pinctrl: samsung: support ExynosAuto GPIO structure
New ExynosAuto series GPIO have a different register structure. In the existing Exynos series, EINT control register is enumerated after a specific offset (e.g EXYNOS_GPIO_ECON_OFFSET, EXYNOS_GPIO_EMASK_OFFSET). However, from ExynosAutov920 SoC, the register that controls EINT belongs to each GPIO bank, and each GPIO bank has 0x1000 align. This is a structure to protect the GPIO bank using S2MPU in VM environment, and will only be applied in ExynosAuto series SoCs. -------------------------------------------------------------- | Original Exynos | ExynosAuto | |------------------------------------------------------------| | 0x0 GPIO_CON | 0x0 GPIO_CON | | 0x4 GPIO_DAT | 0x4 GPIO_DAT | | 0x8 GPIO_PUD | 0x8 GPIO_PUD | | 0xc GPIO_DRV | 0xc GPIO_DRV | | 0x10 GPIO_CONPDN | 0x10 GPIO_CONPDN | | 0x14 GPIO_PUDPDN | 0x14 GPIO_PUDPDN | |----------------------------| 0x18 EINT_CON (per_bank) | | ... | 0x1c EINT_FLTCON0 (per_bank) | | ... | 0x20 EINT_FLTCON1 (per_bank) | | ... | 0x24 EINT_MASK (per_bank) | | ... | 0x28 EINT_PEND (per_bank) | |----------------------------|-------------------------------| | 0x700 EINT_CON (global) | ... | | 0x800 EINT_FLTCON (global) | ... | | 0x900 EINT_MASK (global) | ... | | 0xa00 EINT_FEND (global) | ... | -------------------------------------------------------------- Signed-off-by: Jaewon Kim <jaewon02.kim@samsung.com> Link: https://lore.kernel.org/r/20231211114145.106255-2-jaewon02.kim@samsung.com Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
-rw-r--r--drivers/pinctrl/samsung/pinctrl-exynos.c72
-rw-r--r--drivers/pinctrl/samsung/pinctrl-samsung.c3
-rw-r--r--drivers/pinctrl/samsung/pinctrl-samsung.h12
3 files changed, 79 insertions, 8 deletions
diff --git a/drivers/pinctrl/samsung/pinctrl-exynos.c b/drivers/pinctrl/samsung/pinctrl-exynos.c
index 5d7b788282e9..71e86b2e7b21 100644
--- a/drivers/pinctrl/samsung/pinctrl-exynos.c
+++ b/drivers/pinctrl/samsung/pinctrl-exynos.c
@@ -52,10 +52,15 @@ static void exynos_irq_mask(struct irq_data *irqd)
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 reg_mask = our_chip->eint_mask + bank->eint_offset;
+ unsigned long reg_mask;
unsigned int mask;
unsigned long flags;
+ if (bank->eint_mask_offset)
+ reg_mask = bank->pctl_offset + bank->eint_mask_offset;
+ else
+ reg_mask = our_chip->eint_mask + bank->eint_offset;
+
raw_spin_lock_irqsave(&bank->slock, flags);
mask = readl(bank->eint_base + reg_mask);
@@ -70,7 +75,12 @@ static void exynos_irq_ack(struct irq_data *irqd)
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 reg_pend = our_chip->eint_pend + bank->eint_offset;
+ unsigned long reg_pend;
+
+ if (bank->eint_pend_offset)
+ reg_pend = bank->pctl_offset + bank->eint_pend_offset;
+ else
+ reg_pend = our_chip->eint_pend + bank->eint_offset;
writel(1 << irqd->hwirq, bank->eint_base + reg_pend);
}
@@ -80,7 +90,7 @@ static void exynos_irq_unmask(struct irq_data *irqd)
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 reg_mask = our_chip->eint_mask + bank->eint_offset;
+ unsigned long reg_mask;
unsigned int mask;
unsigned long flags;
@@ -95,6 +105,11 @@ static void exynos_irq_unmask(struct irq_data *irqd)
if (irqd_get_trigger_type(irqd) & IRQ_TYPE_LEVEL_MASK)
exynos_irq_ack(irqd);
+ if (bank->eint_mask_offset)
+ reg_mask = bank->pctl_offset + bank->eint_mask_offset;
+ else
+ reg_mask = our_chip->eint_mask + bank->eint_offset;
+
raw_spin_lock_irqsave(&bank->slock, flags);
mask = readl(bank->eint_base + reg_mask);
@@ -111,7 +126,7 @@ static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type)
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
unsigned int shift = EXYNOS_EINT_CON_LEN * irqd->hwirq;
unsigned int con, trig_type;
- unsigned long reg_con = our_chip->eint_con + bank->eint_offset;
+ unsigned long reg_con;
switch (type) {
case IRQ_TYPE_EDGE_RISING:
@@ -139,6 +154,11 @@ static int exynos_irq_set_type(struct irq_data *irqd, unsigned int type)
else
irq_set_handler_locked(irqd, handle_level_irq);
+ if (bank->eint_con_offset)
+ reg_con = bank->pctl_offset + bank->eint_con_offset;
+ else
+ reg_con = our_chip->eint_con + bank->eint_offset;
+
con = readl(bank->eint_base + reg_con);
con &= ~(EXYNOS_EINT_CON_MASK << shift);
con |= trig_type << shift;
@@ -669,6 +689,19 @@ static void exynos_pinctrl_suspend_bank(
pr_debug("%s: save mask %#010x\n", bank->name, save->eint_mask);
}
+static void exynosauto_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 = bank->eint_base;
+
+ save->eint_con = readl(regs + bank->pctl_offset + bank->eint_con_offset);
+ save->eint_mask = readl(regs + bank->pctl_offset + bank->eint_mask_offset);
+
+ pr_debug("%s: save con %#010x\n", bank->name, save->eint_con);
+ pr_debug("%s: save mask %#010x\n", bank->name, save->eint_mask);
+}
+
void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata)
{
struct samsung_pin_bank *bank = drvdata->pin_banks;
@@ -676,8 +709,12 @@ void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *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);
+ if (bank->eint_type == EINT_TYPE_GPIO) {
+ if (bank->eint_con_offset)
+ exynosauto_pinctrl_suspend_bank(drvdata, bank);
+ else
+ exynos_pinctrl_suspend_bank(drvdata, bank);
+ }
else if (bank->eint_type == EINT_TYPE_WKUP) {
if (!irq_chip) {
irq_chip = bank->irq_chip;
@@ -718,14 +755,33 @@ static void exynos_pinctrl_resume_bank(
+ bank->eint_offset);
}
+static void exynosauto_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 = bank->eint_base;
+
+ pr_debug("%s: con %#010x => %#010x\n", bank->name,
+ readl(regs + bank->pctl_offset + bank->eint_con_offset), save->eint_con);
+ pr_debug("%s: mask %#010x => %#010x\n", bank->name,
+ readl(regs + bank->pctl_offset + bank->eint_mask_offset), save->eint_mask);
+
+ writel(save->eint_con, regs + bank->pctl_offset + bank->eint_con_offset);
+ writel(save->eint_mask, regs + bank->pctl_offset + bank->eint_mask_offset);
+}
+
void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
{
struct samsung_pin_bank *bank = drvdata->pin_banks;
int i;
for (i = 0; i < drvdata->nr_banks; ++i, ++bank)
- if (bank->eint_type == EINT_TYPE_GPIO)
- exynos_pinctrl_resume_bank(drvdata, bank);
+ if (bank->eint_type == EINT_TYPE_GPIO) {
+ if (bank->eint_con_offset)
+ exynosauto_pinctrl_resume_bank(drvdata, bank);
+ else
+ exynos_pinctrl_resume_bank(drvdata, bank);
+ }
}
static void exynos_retention_enable(struct samsung_pinctrl_drv_data *drvdata)
diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.c b/drivers/pinctrl/samsung/pinctrl-samsung.c
index 5ad92600320e..ce34f2968a16 100644
--- a/drivers/pinctrl/samsung/pinctrl-samsung.c
+++ b/drivers/pinctrl/samsung/pinctrl-samsung.c
@@ -1106,6 +1106,9 @@ samsung_pinctrl_get_soc_data(struct samsung_pinctrl_drv_data *d,
bank->eint_type = bdata->eint_type;
bank->eint_mask = bdata->eint_mask;
bank->eint_offset = bdata->eint_offset;
+ bank->eint_con_offset = bdata->eint_con_offset;
+ bank->eint_mask_offset = bdata->eint_mask_offset;
+ bank->eint_pend_offset = bdata->eint_pend_offset;
bank->name = bdata->name;
raw_spin_lock_init(&bank->slock);
diff --git a/drivers/pinctrl/samsung/pinctrl-samsung.h b/drivers/pinctrl/samsung/pinctrl-samsung.h
index 0b459651bc4a..a49d87841bec 100644
--- a/drivers/pinctrl/samsung/pinctrl-samsung.h
+++ b/drivers/pinctrl/samsung/pinctrl-samsung.h
@@ -122,6 +122,9 @@ struct samsung_pin_bank_type {
* @eint_type: type of the external interrupt supported by the bank.
* @eint_mask: bit mask of pins which support EINT function.
* @eint_offset: SoC-specific EINT register or interrupt offset of bank.
+ * @eint_con_offset: ExynosAuto SoC-specific EINT control register offset of bank.
+ * @eint_mask_offset: ExynosAuto SoC-specific EINT mask register offset of bank.
+ * @eint_pend_offset: ExynosAuto SoC-specific EINT pend register offset of bank.
* @name: name to be prefixed for each pin in this pin bank.
*/
struct samsung_pin_bank_data {
@@ -133,6 +136,9 @@ struct samsung_pin_bank_data {
enum eint_type eint_type;
u32 eint_mask;
u32 eint_offset;
+ u32 eint_con_offset;
+ u32 eint_mask_offset;
+ u32 eint_pend_offset;
const char *name;
};
@@ -147,6 +153,9 @@ struct samsung_pin_bank_data {
* @eint_type: type of the external interrupt supported by the bank.
* @eint_mask: bit mask of pins which support EINT function.
* @eint_offset: SoC-specific EINT register or interrupt offset of bank.
+ * @eint_con_offset: ExynosAuto SoC-specific EINT register or interrupt offset of bank.
+ * @eint_mask_offset: ExynosAuto SoC-specific EINT mask register offset of bank.
+ * @eint_pend_offset: ExynosAuto SoC-specific EINT pend register offset of bank.
* @name: name to be prefixed for each pin in this pin bank.
* @id: id of the bank, propagated to the pin range.
* @pin_base: starting pin number of the bank.
@@ -170,6 +179,9 @@ struct samsung_pin_bank {
enum eint_type eint_type;
u32 eint_mask;
u32 eint_offset;
+ u32 eint_con_offset;
+ u32 eint_mask_offset;
+ u32 eint_pend_offset;
const char *name;
u32 id;