Commit 884fdaa5 authored by Jaewon Kim's avatar Jaewon Kim Committed by Krzysztof Kozlowski
Browse files

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: default avatarJaewon Kim <jaewon02.kim@samsung.com>
Link: https://lore.kernel.org/r/20231211114145.106255-2-jaewon02.kim@samsung.com


Signed-off-by: default avatarKrzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
parent 4a8be01a
Loading
Loading
Loading
Loading
+64 −8
Original line number Diff line number Diff line
@@ -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)
		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,15 +755,34 @@ 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)
		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)
{
+3 −0
Original line number Diff line number Diff line
@@ -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);
+12 −0
Original line number Diff line number Diff line
@@ -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;