162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci//
362306a36Sopenharmony_ci// Copyright 2008 Openmoko, Inc.
462306a36Sopenharmony_ci// Copyright 2008 Simtec Electronics
562306a36Sopenharmony_ci//      Ben Dooks <ben@simtec.co.uk>
662306a36Sopenharmony_ci//      http://armlinux.simtec.co.uk/
762306a36Sopenharmony_ci//
862306a36Sopenharmony_ci// S3C64XX - Interrupt handling Power Management
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/*
1162306a36Sopenharmony_ci * NOTE: Code in this file is not used when booting with Device Tree support.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/syscore_ops.h>
1662306a36Sopenharmony_ci#include <linux/interrupt.h>
1762306a36Sopenharmony_ci#include <linux/serial_core.h>
1862306a36Sopenharmony_ci#include <linux/serial_s3c.h>
1962306a36Sopenharmony_ci#include <linux/irq.h>
2062306a36Sopenharmony_ci#include <linux/io.h>
2162306a36Sopenharmony_ci#include <linux/of.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include "map.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "regs-gpio.h"
2662306a36Sopenharmony_ci#include "cpu.h"
2762306a36Sopenharmony_ci#include "pm.h"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* We handled all the IRQ types in this code, to save having to make several
3062306a36Sopenharmony_ci * small files to handle each different type separately. Having the EINT_GRP
3162306a36Sopenharmony_ci * code here shouldn't be as much bloat as the IRQ table space needed when
3262306a36Sopenharmony_ci * they are enabled. The added benefit is we ensure that these registers are
3362306a36Sopenharmony_ci * in the same state as we suspended.
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic struct sleep_save irq_save[] = {
3762306a36Sopenharmony_ci	SAVE_ITEM(S3C64XX_PRIORITY),
3862306a36Sopenharmony_ci	SAVE_ITEM(S3C64XX_EINT0CON0),
3962306a36Sopenharmony_ci	SAVE_ITEM(S3C64XX_EINT0CON1),
4062306a36Sopenharmony_ci	SAVE_ITEM(S3C64XX_EINT0FLTCON0),
4162306a36Sopenharmony_ci	SAVE_ITEM(S3C64XX_EINT0FLTCON1),
4262306a36Sopenharmony_ci	SAVE_ITEM(S3C64XX_EINT0FLTCON2),
4362306a36Sopenharmony_ci	SAVE_ITEM(S3C64XX_EINT0FLTCON3),
4462306a36Sopenharmony_ci	SAVE_ITEM(S3C64XX_EINT0MASK),
4562306a36Sopenharmony_ci};
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic struct irq_grp_save {
4862306a36Sopenharmony_ci	u32	fltcon;
4962306a36Sopenharmony_ci	u32	con;
5062306a36Sopenharmony_ci	u32	mask;
5162306a36Sopenharmony_ci} eint_grp_save[5];
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci#ifndef CONFIG_SERIAL_SAMSUNG_UARTS
5462306a36Sopenharmony_ci#define SERIAL_SAMSUNG_UARTS 0
5562306a36Sopenharmony_ci#else
5662306a36Sopenharmony_ci#define	SERIAL_SAMSUNG_UARTS CONFIG_SERIAL_SAMSUNG_UARTS
5762306a36Sopenharmony_ci#endif
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic u32 irq_uart_mask[SERIAL_SAMSUNG_UARTS];
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int s3c64xx_irq_pm_suspend(void)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct irq_grp_save *grp = eint_grp_save;
6462306a36Sopenharmony_ci	int i;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	S3C_PMDBG("%s: suspending IRQs\n", __func__);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	s3c_pm_do_save(irq_save, ARRAY_SIZE(irq_save));
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	for (i = 0; i < SERIAL_SAMSUNG_UARTS; i++)
7162306a36Sopenharmony_ci		irq_uart_mask[i] = __raw_readl(S3C_VA_UARTx(i) + S3C64XX_UINTM);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) {
7462306a36Sopenharmony_ci		grp->con = __raw_readl(S3C64XX_EINT12CON + (i * 4));
7562306a36Sopenharmony_ci		grp->mask = __raw_readl(S3C64XX_EINT12MASK + (i * 4));
7662306a36Sopenharmony_ci		grp->fltcon = __raw_readl(S3C64XX_EINT12FLTCON + (i * 4));
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return 0;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic void s3c64xx_irq_pm_resume(void)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct irq_grp_save *grp = eint_grp_save;
8562306a36Sopenharmony_ci	int i;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	S3C_PMDBG("%s: resuming IRQs\n", __func__);
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	for (i = 0; i < SERIAL_SAMSUNG_UARTS; i++)
9262306a36Sopenharmony_ci		__raw_writel(irq_uart_mask[i], S3C_VA_UARTx(i) + S3C64XX_UINTM);
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) {
9562306a36Sopenharmony_ci		__raw_writel(grp->con, S3C64XX_EINT12CON + (i * 4));
9662306a36Sopenharmony_ci		__raw_writel(grp->mask, S3C64XX_EINT12MASK + (i * 4));
9762306a36Sopenharmony_ci		__raw_writel(grp->fltcon, S3C64XX_EINT12FLTCON + (i * 4));
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	S3C_PMDBG("%s: IRQ configuration restored\n", __func__);
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic struct syscore_ops s3c64xx_irq_syscore_ops = {
10462306a36Sopenharmony_ci	.suspend = s3c64xx_irq_pm_suspend,
10562306a36Sopenharmony_ci	.resume	 = s3c64xx_irq_pm_resume,
10662306a36Sopenharmony_ci};
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic __init int s3c64xx_syscore_init(void)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	/* Appropriate drivers (pinctrl, uart) handle this when using DT. */
11162306a36Sopenharmony_ci	if (of_have_populated_dt() || !soc_is_s3c64xx())
11262306a36Sopenharmony_ci		return 0;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	register_syscore_ops(&s3c64xx_irq_syscore_ops);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cicore_initcall(s3c64xx_syscore_init);
120