171b9114dSArnd Bergmann // SPDX-License-Identifier: GPL-2.0
271b9114dSArnd Bergmann //
371b9114dSArnd Bergmann // Copyright 2008 Openmoko, Inc.
471b9114dSArnd Bergmann // Copyright 2008 Simtec Electronics
571b9114dSArnd Bergmann // Ben Dooks <ben@simtec.co.uk>
671b9114dSArnd Bergmann // http://armlinux.simtec.co.uk/
771b9114dSArnd Bergmann //
871b9114dSArnd Bergmann // S3C64XX - Interrupt handling Power Management
971b9114dSArnd Bergmann
1071b9114dSArnd Bergmann /*
1171b9114dSArnd Bergmann * NOTE: Code in this file is not used when booting with Device Tree support.
1271b9114dSArnd Bergmann */
1371b9114dSArnd Bergmann
1471b9114dSArnd Bergmann #include <linux/kernel.h>
1571b9114dSArnd Bergmann #include <linux/syscore_ops.h>
1671b9114dSArnd Bergmann #include <linux/interrupt.h>
1771b9114dSArnd Bergmann #include <linux/serial_core.h>
1871b9114dSArnd Bergmann #include <linux/serial_s3c.h>
1971b9114dSArnd Bergmann #include <linux/irq.h>
2071b9114dSArnd Bergmann #include <linux/io.h>
2171b9114dSArnd Bergmann #include <linux/of.h>
2271b9114dSArnd Bergmann
23*c6ff132dSArnd Bergmann #include "map.h"
2471b9114dSArnd Bergmann
25*c6ff132dSArnd Bergmann #include "regs-gpio.h"
26*c6ff132dSArnd Bergmann #include "cpu.h"
27*c6ff132dSArnd Bergmann #include "pm.h"
2871b9114dSArnd Bergmann
2971b9114dSArnd Bergmann /* We handled all the IRQ types in this code, to save having to make several
3071b9114dSArnd Bergmann * small files to handle each different type separately. Having the EINT_GRP
3171b9114dSArnd Bergmann * code here shouldn't be as much bloat as the IRQ table space needed when
3271b9114dSArnd Bergmann * they are enabled. The added benefit is we ensure that these registers are
3371b9114dSArnd Bergmann * in the same state as we suspended.
3471b9114dSArnd Bergmann */
3571b9114dSArnd Bergmann
3671b9114dSArnd Bergmann static struct sleep_save irq_save[] = {
3771b9114dSArnd Bergmann SAVE_ITEM(S3C64XX_PRIORITY),
3871b9114dSArnd Bergmann SAVE_ITEM(S3C64XX_EINT0CON0),
3971b9114dSArnd Bergmann SAVE_ITEM(S3C64XX_EINT0CON1),
4071b9114dSArnd Bergmann SAVE_ITEM(S3C64XX_EINT0FLTCON0),
4171b9114dSArnd Bergmann SAVE_ITEM(S3C64XX_EINT0FLTCON1),
4271b9114dSArnd Bergmann SAVE_ITEM(S3C64XX_EINT0FLTCON2),
4371b9114dSArnd Bergmann SAVE_ITEM(S3C64XX_EINT0FLTCON3),
4471b9114dSArnd Bergmann SAVE_ITEM(S3C64XX_EINT0MASK),
4571b9114dSArnd Bergmann };
4671b9114dSArnd Bergmann
4771b9114dSArnd Bergmann static struct irq_grp_save {
4871b9114dSArnd Bergmann u32 fltcon;
4971b9114dSArnd Bergmann u32 con;
5071b9114dSArnd Bergmann u32 mask;
5171b9114dSArnd Bergmann } eint_grp_save[5];
5271b9114dSArnd Bergmann
5371b9114dSArnd Bergmann #ifndef CONFIG_SERIAL_SAMSUNG_UARTS
5471b9114dSArnd Bergmann #define SERIAL_SAMSUNG_UARTS 0
5571b9114dSArnd Bergmann #else
5671b9114dSArnd Bergmann #define SERIAL_SAMSUNG_UARTS CONFIG_SERIAL_SAMSUNG_UARTS
5771b9114dSArnd Bergmann #endif
5871b9114dSArnd Bergmann
5971b9114dSArnd Bergmann static u32 irq_uart_mask[SERIAL_SAMSUNG_UARTS];
6071b9114dSArnd Bergmann
s3c64xx_irq_pm_suspend(void)6171b9114dSArnd Bergmann static int s3c64xx_irq_pm_suspend(void)
6271b9114dSArnd Bergmann {
6371b9114dSArnd Bergmann struct irq_grp_save *grp = eint_grp_save;
6471b9114dSArnd Bergmann int i;
6571b9114dSArnd Bergmann
6671b9114dSArnd Bergmann S3C_PMDBG("%s: suspending IRQs\n", __func__);
6771b9114dSArnd Bergmann
6871b9114dSArnd Bergmann s3c_pm_do_save(irq_save, ARRAY_SIZE(irq_save));
6971b9114dSArnd Bergmann
7071b9114dSArnd Bergmann for (i = 0; i < SERIAL_SAMSUNG_UARTS; i++)
7171b9114dSArnd Bergmann irq_uart_mask[i] = __raw_readl(S3C_VA_UARTx(i) + S3C64XX_UINTM);
7271b9114dSArnd Bergmann
7371b9114dSArnd Bergmann for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) {
7471b9114dSArnd Bergmann grp->con = __raw_readl(S3C64XX_EINT12CON + (i * 4));
7571b9114dSArnd Bergmann grp->mask = __raw_readl(S3C64XX_EINT12MASK + (i * 4));
7671b9114dSArnd Bergmann grp->fltcon = __raw_readl(S3C64XX_EINT12FLTCON + (i * 4));
7771b9114dSArnd Bergmann }
7871b9114dSArnd Bergmann
7971b9114dSArnd Bergmann return 0;
8071b9114dSArnd Bergmann }
8171b9114dSArnd Bergmann
s3c64xx_irq_pm_resume(void)8271b9114dSArnd Bergmann static void s3c64xx_irq_pm_resume(void)
8371b9114dSArnd Bergmann {
8471b9114dSArnd Bergmann struct irq_grp_save *grp = eint_grp_save;
8571b9114dSArnd Bergmann int i;
8671b9114dSArnd Bergmann
8771b9114dSArnd Bergmann S3C_PMDBG("%s: resuming IRQs\n", __func__);
8871b9114dSArnd Bergmann
8971b9114dSArnd Bergmann s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));
9071b9114dSArnd Bergmann
9171b9114dSArnd Bergmann for (i = 0; i < SERIAL_SAMSUNG_UARTS; i++)
9271b9114dSArnd Bergmann __raw_writel(irq_uart_mask[i], S3C_VA_UARTx(i) + S3C64XX_UINTM);
9371b9114dSArnd Bergmann
9471b9114dSArnd Bergmann for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) {
9571b9114dSArnd Bergmann __raw_writel(grp->con, S3C64XX_EINT12CON + (i * 4));
9671b9114dSArnd Bergmann __raw_writel(grp->mask, S3C64XX_EINT12MASK + (i * 4));
9771b9114dSArnd Bergmann __raw_writel(grp->fltcon, S3C64XX_EINT12FLTCON + (i * 4));
9871b9114dSArnd Bergmann }
9971b9114dSArnd Bergmann
10071b9114dSArnd Bergmann S3C_PMDBG("%s: IRQ configuration restored\n", __func__);
10171b9114dSArnd Bergmann }
10271b9114dSArnd Bergmann
10371b9114dSArnd Bergmann static struct syscore_ops s3c64xx_irq_syscore_ops = {
10471b9114dSArnd Bergmann .suspend = s3c64xx_irq_pm_suspend,
10571b9114dSArnd Bergmann .resume = s3c64xx_irq_pm_resume,
10671b9114dSArnd Bergmann };
10771b9114dSArnd Bergmann
s3c64xx_syscore_init(void)10871b9114dSArnd Bergmann static __init int s3c64xx_syscore_init(void)
10971b9114dSArnd Bergmann {
11071b9114dSArnd Bergmann /* Appropriate drivers (pinctrl, uart) handle this when using DT. */
11171b9114dSArnd Bergmann if (of_have_populated_dt() || !soc_is_s3c64xx())
11271b9114dSArnd Bergmann return 0;
11371b9114dSArnd Bergmann
11471b9114dSArnd Bergmann register_syscore_ops(&s3c64xx_irq_syscore_ops);
11571b9114dSArnd Bergmann
11671b9114dSArnd Bergmann return 0;
11771b9114dSArnd Bergmann }
11871b9114dSArnd Bergmann
11971b9114dSArnd Bergmann core_initcall(s3c64xx_syscore_init);
120