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