code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright 2006 Ben Dooks <ben-linux@fluff.org>
 *
 * Copyright (c) 2005 Simtec Electronics
 *	http://armlinux.simtec.co.uk/
 *	Ben Dooks <ben@simtec.co.uk>
 *
 * iPAQ H1940 series definitions
 */

#ifndef __MACH_S3C24XX_H1940_H
#define __MACH_S3C24XX_H1940_H __FILE__

#define H1940_SUSPEND_CHECKSUM		(0x30003ff8)
#define H1940_SUSPEND_RESUMEAT		(0x30081000)
#define H1940_SUSPEND_CHECK		(0x30080000)

struct gpio_desc;

extern void h1940_pm_return(void);
extern int h1940_led_blink_set(struct gpio_desc *desc, int state,
			       unsigned long *delay_on,
			       unsigned long *delay_off);

#include <linux/gpio.h>

#define H1940_LATCH_GPIO(x)		(S3C_GPIO_END + (x))

/* SD layer latch */

#define H1940_LATCH_LCD_P0		H1940_LATCH_GPIO(0)
#define H1940_LATCH_LCD_P1		H1940_LATCH_GPIO(1)
#define H1940_LATCH_LCD_P2		H1940_LATCH_GPIO(2)
#define H1940_LATCH_LCD_P3		H1940_LATCH_GPIO(3)
#define H1940_LATCH_MAX1698_nSHUTDOWN	H1940_LATCH_GPIO(4)
#define H1940_LATCH_LED_RED		H1940_LATCH_GPIO(5)
#define H1940_LATCH_SDQ7		H1940_LATCH_GPIO(6)
#define H1940_LATCH_USB_DP		H1940_LATCH_GPIO(7)

/* CPU layer latch */

#define H1940_LATCH_UDA_POWER		H1940_LATCH_GPIO(8)
#define H1940_LATCH_AUDIO_POWER		H1940_LATCH_GPIO(9)
#define H1940_LATCH_SM803_ENABLE	H1940_LATCH_GPIO(10)
#define H1940_LATCH_LCD_P4		H1940_LATCH_GPIO(11)
#define H1940_LATCH_SD_POWER		H1940_LATCH_GPIO(12)
#define H1940_LATCH_BLUETOOTH_POWER	H1940_LATCH_GPIO(13)
#define H1940_LATCH_LED_GREEN		H1940_LATCH_GPIO(14)
#define H1940_LATCH_LED_FLASH		H1940_LATCH_GPIO(15)

#endif /* __MACH_S3C24XX_H1940_H */


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (c) 2003 Simtec Electronics
 *	Ben Dooks <ben@simtec.co.uk>
 *
 * S3C2410 - hardware
 */

#ifndef __ASM_ARCH_HARDWARE_S3C24XX_H
#define __ASM_ARCH_HARDWARE_S3C24XX_H

extern unsigned int s3c2410_modify_misccr(unsigned int clr, unsigned int chg);

#endif /* __ASM_ARCH_HARDWARE_S3C24XX_H */

repo_name: angery/linux


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright 2008 Openmoko, Inc.
 * Copyright 2008 Simtec Electronics
 *	Ben Dooks <ben@simtec.co.uk>
 *
 * S3C - I2C Controller core functions
 */

#ifndef __ASM_ARCH_IIC_CORE_H
#define __ASM_ARCH_IIC_CORE_H __FILE__

/* These functions are only for use with the core support code, such as
 * the cpu specific initialisation code
 */

/* re-define device name depending on support. */
static inline void s3c_i2c0_setname(char *name)
{
	/* currently this device is always compiled in */
	s3c_device_i2c0.name = name;
}

static inline void s3c_i2c1_setname(char *name)
{
#ifdef CONFIG_S3C_DEV_I2C1
	s3c_device_i2c1.name = name;
#endif
}

static inline void s3c_i2c2_setname(char *name)
{
#ifdef CONFIG_S3C_DEV_I2C2
	s3c_device_i2c2.name = name;
#endif
}

#endif /* __ASM_ARCH_IIC_H */


code:
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2008 Simtec Electronics
//	Ben Dooks <ben@simtec.co.uk>
//	http://armlinux.simtec.co.uk/
//
// S3C series CPU initialisation

/*
 * NOTE: Code in this file is not used on S3C64xx when booting with
 * Device Tree support.
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/serial_core.h>
#include <linux/serial_s3c.h>
#include <linux/platform_device.h>
#include <linux/of.h>

#include <asm/mach/arch.h>
#include <asm/mach/map.h>

#include "cpu.h"
#include "devs.h"

static struct cpu_table *cpu;

static struct cpu_table * __init s3c_lookup_cpu(unsigned long idcode,
						struct cpu_table *tab,
						unsigned int count)
{
	for (; count != 0; count--, tab++) {
		if ((idcode & tab->idmask) == (tab->idcode & tab->idmask))
			return tab;
	}

	return NULL;
}

void __init s3c_init_cpu(unsigned long idcode,
			 struct cpu_table *cputab, unsigned int cputab_size)
{
	cpu = s3c_lookup_cpu(idcode, cputab, cputab_size);

	if (cpu == NULL) {
		printk(KERN_ERR "Unknown CPU type 0x%08lx\n", idcode);
		panic("Unknown S3C24XX CPU");
	}

	printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode);

	if (cpu->init == NULL) {
		printk(KERN_ERR "CPU %s support not enabled\n", cpu->name);
		panic("Unsupported Samsung CPU");
	}

	if (cpu->map_io)
		cpu->map_io();

	pr_err("The platform is deprecated and scheduled for removal. Please reach to the maintainers of the platform and linux-samsung-soc@vger.kernel.org if you still use it.  Without such feedback, the platform will be removed after 2022.\n");
}

/* s3c24xx_init_clocks
 *
 * Initialise the clock subsystem and associated information from the
 * given master crystal value.
 *
 * xtal  = 0 -> use default PLL crystal value (normally 12MHz)
 *      != 0 -> PLL crystal value in Hz
*/

void __init s3c24xx_init_clocks(int xtal)
{
	if (xtal == 0)
		xtal = 12*1000*1000;

	if (cpu == NULL)
		panic("s3c24xx_init_clocks: no cpu setup?\n");

	if (cpu->init_clocks == NULL)
		panic("s3c24xx_init_clocks: cpu has no clock init\n");
	else
		(cpu->init_clocks)(xtal);
}

/* uart management */
#if IS_ENABLED(CONFIG_SAMSUNG_ATAGS)
static int nr_uarts __initdata = 0;

#ifdef CONFIG_SERIAL_SAMSUNG_UARTS
static struct s3c2410_uartcfg uart_cfgs[CONFIG_SERIAL_SAMSUNG_UARTS];
#endif

/* s3c24xx_init_uartdevs
 *
 * copy the specified platform data and configuration into our central
 * set of devices, before the data is thrown away after the init process.
 *
 * This also fills in the array passed to the serial driver for the
 * early initialisation of the console.
*/

void __init s3c24xx_init_uartdevs(char *name,
				  struct s3c24xx_uart_resources *res,
				  struct s3c2410_uartcfg *cfg, int no)
{
#ifdef CONFIG_SERIAL_SAMSUNG_UARTS
	struct platform_device *platdev;
	struct s3c2410_uartcfg *cfgptr = uart_cfgs;
	struct s3c24xx_uart_resources *resp;
	int uart;

	memcpy(cfgptr, cfg, sizeof(struct s3c2410_uartcfg) * no);

	for (uart = 0; uart < no; uart++, cfg++, cfgptr++) {
		platdev = s3c24xx_uart_src[cfgptr->hwport];

		resp = res + cfgptr->hwport;

		s3c24xx_uart_devs[uart] = platdev;

		platdev->name = name;
		platdev->resource = resp->resources;
		platdev->num_resources = resp->nr_resources;

		platdev->dev.platform_data = cfgptr;
	}

	nr_uarts = no;
#endif
}

void __init s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no)
{
	if (cpu == NULL)
		return;

	if (cpu->init_uarts == NULL && IS_ENABLED(CONFIG_SAMSUNG_ATAGS)) {
		printk(KERN_ERR "s3c24xx_init_uarts: cpu has no uart init\n");
	} else
		(cpu->init_uarts)(cfg, no);
}
#endif

static int __init s3c_arch_init(void)
{
	int ret;

	/* init is only needed for ATAGS based platforms */
	if (!IS_ENABLED(CONFIG_ATAGS) ||
	    (!soc_is_s3c24xx() && !soc_is_s3c64xx()))
		return 0;

	// do the correct init for cpu

	if (cpu == NULL) {
		/* Not needed when booting with device tree. */
		if (of_have_populated_dt())
			return 0;
		panic("s3c_arch_init: NULL cpu\n");
	}

	ret = (cpu->init)();
	if (ret != 0)
		return ret;
#if IS_ENABLED(CONFIG_SAMSUNG_ATAGS)
	ret = platform_add_devices(s3c24xx_uart_devs, nr_uarts);
#endif
	return ret;
}

arch_initcall(s3c_arch_init);


code:
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2006-2009 Simtec Electronics
//	http://armlinux.simtec.co.uk/
//	Ben Dooks <ben@simtec.co.uk>
//
// S3C24XX CPU Frequency scaling - IO timing for S3C2410/S3C2440/S3C2442

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/cpufreq.h>
#include <linux/seq_file.h>
#include <linux/io.h>
#include <linux/slab.h>

#include "map.h"
#include "regs-clock.h"

#include <linux/soc/samsung/s3c-cpufreq-core.h>

#include "regs-mem-s3c24xx.h"

#define print_ns(x) ((x) / 10), ((x) % 10)

/**
 * s3c2410_print_timing - print bank timing data for debug purposes
 * @pfx: The prefix to put on the output
 * @timings: The timing inforamtion to print.
*/
static void s3c2410_print_timing(const char *pfx,
				 struct s3c_iotimings *timings)
{
	struct s3c2410_iobank_timing *bt;
	int bank;

	for (bank = 0; bank < MAX_BANKS; bank++) {
		bt = timings->bank[bank].io_2410;
		if (!bt)
			continue;

		printk(KERN_DEBUG "%s %d: Tacs=%d.%d, Tcos=%d.%d, Tacc=%d.%d, "
		       "Tcoh=%d.%d, Tcah=%d.%d\n", pfx, bank,
		       print_ns(bt->tacs),
		       print_ns(bt->tcos),
		       print_ns(bt->tacc),
		       print_ns(bt->tcoh),
		       print_ns(bt->tcah));
	}
}

/**
 * bank_reg - convert bank number to pointer to the control register.
 * @bank: The IO bank number.
 */
static inline void __iomem *bank_reg(unsigned int bank)
{
	return S3C2410_BANKCON0 + (bank << 2);
}

/**
 * bank_is_io - test whether bank is used for IO
 * @bankcon: The bank control register.
 *
 * This is a simplistic test to see if any BANKCON[x] is not an IO
 * bank. It currently does not take into account whether BWSCON has
 * an illegal width-setting in it, or if the pin connected to nCS[x]
 * is actually being handled as a chip-select.
 */
static inline int bank_is_io(unsigned long bankcon)
{
	return !(bankcon & S3C2410_BANKCON_SDRAM);
}

/**
 * to_div - convert cycle time to divisor
 * @cyc: The cycle time, in 10ths of nanoseconds.
 * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
 *
 * Convert the given cycle time into the divisor to use to obtain it from
 * HCLK.
*/
static inline unsigned int to_div(unsigned int cyc, unsigned int hclk_tns)
{
	if (cyc == 0)
		return 0;

	return DIV_ROUND_UP(cyc, hclk_tns);
}

/**
 * calc_0124 - calculate divisor control for divisors that do /0, /1. /2 and /4
 * @cyc: The cycle time, in 10ths of nanoseconds.
 * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
 * @v: Pointer to register to alter.
 * @shift: The shift to get to the control bits.
 *
 * Calculate the divisor, and turn it into the correct control bits to
 * set in the result, @v.
 */
static unsigned int calc_0124(unsigned int cyc, unsigned long hclk_tns,
			      unsigned long *v, int shift)
{
	unsigned int div = to_div(cyc, hclk_tns);
	unsigned long val;

	s3c_freq_iodbg("%s: cyc=%d, hclk=%lu, shift=%d => div %d\n",
		       __func__, cyc, hclk_tns, shift, div);

	switch (div) {
	case 0:
		val = 0;
		break;
	case 1:
		val = 1;
		break;
	case 2:
		val = 2;
		break;
	case 3:
	case 4:
		val = 3;
		break;
	default:
		return -1;
	}

	*v |= val << shift;
	return 0;
}

static int calc_tacp(unsigned int cyc, unsigned long hclk, unsigned long *v)
{
	/* Currently no support for Tacp calculations. */
	return 0;
}

/**
 * calc_tacc - calculate divisor control for tacc.
 * @cyc: The cycle time, in 10ths of nanoseconds.
 * @nwait_en: IS nWAIT enabled for this bank.
 * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
 * @v: Pointer to register to alter.
 *
 * Calculate the divisor control for tACC, taking into account whether
 * the bank has nWAIT enabled. The result is used to modify the value
 * pointed to by @v.
*/
static int calc_tacc(unsigned int cyc, int nwait_en,
		     unsigned long hclk_tns, unsigned long *v)
{
	unsigned int div = to_div(cyc, hclk_tns);
	unsigned long val;

	s3c_freq_iodbg("%s: cyc=%u, nwait=%d, hclk=%lu => div=%u\n",
		       __func__, cyc, nwait_en, hclk_tns, div);

	/* if nWait enabled on an bank, Tacc must be at-least 4 cycles. */
	if (nwait_en && div < 4)
		div = 4;

	switch (div) {
	case 0:
		val = 0;
		break;

	case 1:
	case 2:
	case 3:
	case 4:
		val = div - 1;
		break;

	case 5:
	case 6:
		val = 4;
		break;

	case 7:
	case 8:
		val = 5;
		break;

	case 9:
	case 10:
		val = 6;
		break;

	case 11:
	case 12:
	case 13:
	case 14:
		val = 7;
		break;

	default:
		return -1;
	}

	*v |= val << 8;
	return 0;
}

/**
 * s3c2410_calc_bank - calculate bank timing information
 * @cfg: The configuration we need to calculate for.
 * @bt: The bank timing information.
 *
 * Given the cycle timine for a bank @bt, calculate the new BANKCON
 * setting for the @cfg timing. This updates the timing information
 * ready for the cpu frequency change.
 */
static int s3c2410_calc_bank(struct s3c_cpufreq_config *cfg,
			     struct s3c2410_iobank_timing *bt)
{
	unsigned long hclk = cfg->freq.hclk_tns;
	unsigned long res;
	int ret;

	res  = bt->bankcon;
	res &= (S3C2410_BANKCON_SDRAM | S3C2410_BANKCON_PMC16);

	/* tacp: 2,3,4,5 */
	/* tcah: 0,1,2,4 */
	/* tcoh: 0,1,2,4 */
	/* tacc: 1,2,3,4,6,7,10,14 (>4 for nwait) */
	/* tcos: 0,1,2,4 */
	/* tacs: 0,1,2,4 */

	ret  = calc_0124(bt->tacs, hclk, &res, S3C2410_BANKCON_Tacs_SHIFT);
	ret |= calc_0124(bt->tcos, hclk, &res, S3C2410_BANKCON_Tcos_SHIFT);
	ret |= calc_0124(bt->tcah, hclk, &res, S3C2410_BANKCON_Tcah_SHIFT);
	ret |= calc_0124(bt->tcoh, hclk, &res, S3C2410_BANKCON_Tcoh_SHIFT);

	if (ret)
		return -EINVAL;

	ret |= calc_tacp(bt->tacp, hclk, &res);
	ret |= calc_tacc(bt->tacc, bt->nwait_en, hclk, &res);

	if (ret)
		return -EINVAL;

	bt->bankcon = res;
	return 0;
}

static const unsigned int tacc_tab[] = {
	[0]	= 1,
	[1]	= 2,
	[2]	= 3,
	[3]	= 4,
	[4]	= 6,
	[5]	= 9,
	[6]	= 10,
	[7]	= 14,
};

/**
 * get_tacc - turn tACC value into cycle time
 * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
 * @val: The bank timing register value, shifted down.
 */
static unsigned int get_tacc(unsigned long hclk_tns,
			     unsigned long val)
{
	val &= 7;
	return hclk_tns * tacc_tab[val];
}

/**
 * get_0124 - turn 0/1/2/4 divider into cycle time
 * @hclk_tns: The cycle time for HCLK, in 10ths of nanoseconds.
 * @val: The bank timing register value, shifed down.
 */
static unsigned int get_0124(unsigned long hclk_tns,
			     unsigned long val)
{
	val &= 3;
	return hclk_tns * ((val == 3) ? 4 : val);
}

/**
 * s3c2410_iotiming_getbank - turn BANKCON into cycle time information
 * @cfg: The frequency configuration
 * @bt: The bank timing to fill in (uses cached BANKCON)
 *
 * Given the BANKCON setting in @bt and the current frequency settings
 * in @cfg, update the cycle timing information.
 */
static void s3c2410_iotiming_getbank(struct s3c_cpufreq_config *cfg,
				     struct s3c2410_iobank_timing *bt)
{
	unsigned long bankcon = bt->bankcon;
	unsigned long hclk = cfg->freq.hclk_tns;

	bt->tcah = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcah_SHIFT);
	bt->tcoh = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcoh_SHIFT);
	bt->tcos = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcos_SHIFT);
	bt->tacs = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tacs_SHIFT);
	bt->tacc = get_tacc(hclk, bankcon >> S3C2410_BANKCON_Tacc_SHIFT);
}

/**
 * s3c2410_iotiming_debugfs - debugfs show io bank timing information
 * @seq: The seq_file to write output to using seq_printf().
 * @cfg: The current configuration.
 * @iob: The IO bank information to decode.
 */
void s3c2410_iotiming_debugfs(struct seq_file *seq,
			      struct s3c_cpufreq_config *cfg,
			      union s3c_iobank *iob)
{
	struct s3c2410_iobank_timing *bt = iob->io_2410;
	unsigned long bankcon = bt->bankcon;
	unsigned long hclk = cfg->freq.hclk_tns;
	unsigned int tacs;
	unsigned int tcos;
	unsigned int tacc;
	unsigned int tcoh;
	unsigned int tcah;

	seq_printf(seq, "BANKCON=0x%08lx\n", bankcon);

	tcah = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcah_SHIFT);
	tcoh = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcoh_SHIFT);
	tcos = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcos_SHIFT);
	tacs = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tacs_SHIFT);
	tacc = get_tacc(hclk, bankcon >> S3C2410_BANKCON_Tacc_SHIFT);

	seq_printf(seq,
		   "\tRead: Tacs=%d.%d, Tcos=%d.%d, Tacc=%d.%d, Tcoh=%d.%d, Tcah=%d.%d\n",
		   print_ns(bt->tacs),
		   print_ns(bt->tcos),
		   print_ns(bt->tacc),
		   print_ns(bt->tcoh),
		   print_ns(bt->tcah));

	seq_printf(seq,
		   "\t Set: Tacs=%d.%d, Tcos=%d.%d, Tacc=%d.%d, Tcoh=%d.%d, Tcah=%d.%d\n",
		   print_ns(tacs),
		   print_ns(tcos),
		   print_ns(tacc),
		   print_ns(tcoh),
		   print_ns(tcah));
}

/**
 * s3c2410_iotiming_calc - Calculate bank timing for frequency change.
 * @cfg: The frequency configuration
 * @iot: The IO timing information to fill out.
 *
 * Calculate the new values for the banks in @iot based on the new
 * frequency information in @cfg. This is then used by s3c2410_iotiming_set()
 * to update the timing when necessary.
 */
int s3c2410_iotiming_calc(struct s3c_cpufreq_config *cfg,
			  struct s3c_iotimings *iot)
{
	struct s3c2410_iobank_timing *bt;
	unsigned long bankcon;
	int bank;
	int ret;

	for (bank = 0; bank < MAX_BANKS; bank++) {
		bankcon = __raw_readl(bank_reg(bank));
		bt = iot->bank[bank].io_2410;

		if (!bt)
			continue;

		bt->bankcon = bankcon;

		ret = s3c2410_calc_bank(cfg, bt);
		if (ret) {
			printk(KERN_ERR "%s: cannot calculate bank %d io\n",
			       __func__, bank);
			goto err;
		}

		s3c_freq_iodbg("%s: bank %d: con=%08lx\n",
			       __func__, bank, bt->bankcon);
	}

	return 0;
 err:
	return ret;
}

/**
 * s3c2410_iotiming_set - set the IO timings from the given setup.
 * @cfg: The frequency configuration
 * @iot: The IO timing information to use.
 *
 * Set all the currently used IO bank timing information generated
 * by s3c2410_iotiming_calc() once the core has validated that all
 * the new values are within permitted bounds.
 */
void s3c2410_iotiming_set(struct s3c_cpufreq_config *cfg,
			  struct s3c_iotimings *iot)
{
	struct s3c2410_iobank_timing *bt;
	int bank;

	/* set the io timings from the specifier */

	for (bank = 0; bank < MAX_BANKS; bank++) {
		bt = iot->bank[bank].io_2410;
		if (!bt)
			continue;

		__raw_writel(bt->bankcon, bank_reg(bank));
	}
}

/**
 * s3c2410_iotiming_get - Get the timing information from current registers.
 * @cfg: The frequency configuration
 * @timings: The IO timing information to fill out.
 *
 * Calculate the @timings timing information from the current frequency
 * information in @cfg, and the new frequency configuration
 * through all the IO banks, reading the state and then updating @iot
 * as necessary.
 *
 * This is used at the moment on initialisation to get the current
 * configuration so that boards do not have to carry their own setup
 * if the timings are correct on initialisation.
 */

int s3c2410_iotiming_get(struct s3c_cpufreq_config *cfg,
			 struct s3c_iotimings *timings)
{
	struct s3c2410_iobank_timing *bt;
	unsigned long bankcon;
	unsigned long bwscon;
	int bank;

	bwscon = __raw_readl(S3C2410_BWSCON);

	/* look through all banks to see what is currently set. */

	for (bank = 0; bank < MAX_BANKS; bank++) {
		bankcon = __raw_readl(bank_reg(bank));

		if (!bank_is_io(bankcon))
			continue;

		s3c_freq_iodbg("%s: bank %d: con %08lx\n",
			       __func__, bank, bankcon);

		bt = kzalloc(sizeof(*bt), GFP_KERNEL);
		if (!bt)
			return -ENOMEM;

		/* find out in nWait is enabled for bank. */

		if (bank != 0) {
			unsigned long tmp  = S3C2410_BWSCON_GET(bwscon, bank);
			if (tmp & S3C2410_BWSCON_WS)
				bt->nwait_en = 1;
		}

		timings->bank[bank].io_2410 = bt;
		bt->bankcon = bankcon;

		s3c2410_iotiming_getbank(cfg, bt);
	}

	s3c2410_print_timing("get", timings);
	return 0;
}


code:
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2006-2008 Simtec Electronics
//	http://armlinux.simtec.co.uk/
//	Ben Dooks <ben@simtec.co.uk>
//
// S3C2412/S3C2443 (PL093 based) IO timing support

#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/cpufreq.h>
#include <linux/seq_file.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/slab.h>

#include <linux/amba/pl093.h>

#include <asm/mach/arch.h>
#include <asm/mach/map.h>

#include "cpu.h"
#include <linux/soc/samsung/s3c-cpufreq-core.h>

#include "s3c2412.h"

#define print_ns(x) ((x) / 10), ((x) % 10)

/**
 * s3c2412_print_timing - print timing information via printk.
 * @pfx: The prefix to print each line with.
 * @iot: The IO timing information
 */
static void s3c2412_print_timing(const char *pfx, struct s3c_iotimings *iot)
{
	struct s3c2412_iobank_timing *bt;
	unsigned int bank;

	for (bank = 0; bank < MAX_BANKS; bank++) {
		bt = iot->bank[bank].io_2412;
		if (!bt)
			continue;

		printk(KERN_DEBUG "%s: %d: idcy=%d.%d wstrd=%d.%d wstwr=%d,%d"
		       "wstoen=%d.%d wstwen=%d.%d wstbrd=%d.%d\n", pfx, bank,
		       print_ns(bt->idcy),
		       print_ns(bt->wstrd),
		       print_ns(bt->wstwr),
		       print_ns(bt->wstoen),
		       print_ns(bt->wstwen),
		       print_ns(bt->wstbrd));
	}
}

/**
 * to_div - turn a cycle length into a divisor setting.
 * @cyc_tns: The cycle time in 10ths of nanoseconds.
 * @clk_tns: The clock period in 10ths of nanoseconds.
 */
static inline unsigned int to_div(unsigned int cyc_tns, unsigned int clk_tns)
{
	return cyc_tns ? DIV_ROUND_UP(cyc_tns, clk_tns) : 0;
}

/**
 * calc_timing - calculate timing divisor value and check in range.
 * @hwtm: The hardware timing in 10ths of nanoseconds.
 * @clk_tns: The clock period in 10ths of nanoseconds.
 * @err: Pointer to err variable to update in event of failure.
 */
static unsigned int calc_timing(unsigned int hwtm, unsigned int clk_tns,
				unsigned int *err)
{
	unsigned int ret = to_div(hwtm, clk_tns);

	if (ret > 0xf)
		*err = -EINVAL;

	return ret;
}

/**
 * s3c2412_calc_bank - calculate the bank divisor settings.
 * @cfg: The current frequency configuration.
 * @bt: The bank timing.
 */
static int s3c2412_calc_bank(struct s3c_cpufreq_config *cfg,
			     struct s3c2412_iobank_timing *bt)
{
	unsigned int hclk = cfg->freq.hclk_tns;
	int err = 0;

	bt->smbidcyr = calc_timing(bt->idcy, hclk, &err);
	bt->smbwstrd = calc_timing(bt->wstrd, hclk, &err);
	bt->smbwstwr = calc_timing(bt->wstwr, hclk, &err);
	bt->smbwstoen = calc_timing(bt->wstoen, hclk, &err);
	bt->smbwstwen = calc_timing(bt->wstwen, hclk, &err);
	bt->smbwstbrd = calc_timing(bt->wstbrd, hclk, &err);

	return err;
}

/**
 * s3c2412_iotiming_debugfs - debugfs show io bank timing information
 * @seq: The seq_file to write output to using seq_printf().
 * @cfg: The current configuration.
 * @iob: The IO bank information to decode.
*/
void s3c2412_iotiming_debugfs(struct seq_file *seq,
			      struct s3c_cpufreq_config *cfg,
			      union s3c_iobank *iob)
{
	struct s3c2412_iobank_timing *bt = iob->io_2412;

	seq_printf(seq,
		   "\tRead: idcy=%d.%d wstrd=%d.%d wstwr=%d,%d"
		   "wstoen=%d.%d wstwen=%d.%d wstbrd=%d.%d\n",
		   print_ns(bt->idcy),
		   print_ns(bt->wstrd),
		   print_ns(bt->wstwr),
		   print_ns(bt->wstoen),
		   print_ns(bt->wstwen),
		   print_ns(bt->wstbrd));
}

/**
 * s3c2412_iotiming_calc - calculate all the bank divisor settings.
 * @cfg: The current frequency configuration.
 * @iot: The bank timing information.
 *
 * Calculate the timing information for all the banks that are
 * configured as IO, using s3c2412_calc_bank().
 */
int s3c2412_iotiming_calc(struct s3c_cpufreq_config *cfg,
			  struct s3c_iotimings *iot)
{
	struct s3c2412_iobank_timing *bt;
	int bank;
	int ret;

	for (bank = 0; bank < MAX_BANKS; bank++) {
		bt = iot->bank[bank].io_2412;
		if (!bt)
			continue;

		ret = s3c2412_calc_bank(cfg, bt);
		if (ret) {
			printk(KERN_ERR "%s: cannot calculate bank %d io\n",
			       __func__, bank);
			goto err;
		}
	}

	return 0;
 err:
	return ret;
}

/**
 * s3c2412_iotiming_set - set the timing information
 * @cfg: The current frequency configuration.
 * @iot: The bank timing information.
 *
 * Set the IO bank information from the details calculated earlier from
 * calling s3c2412_iotiming_calc().
 */
void s3c2412_iotiming_set(struct s3c_cpufreq_config *cfg,
			  struct s3c_iotimings *iot)
{
	struct s3c2412_iobank_timing *bt;
	void __iomem *regs;
	int bank;

	/* set the io timings from the specifier */

	for (bank = 0; bank < MAX_BANKS; bank++) {
		bt = iot->bank[bank].io_2412;
		if (!bt)
			continue;

		regs = S3C2412_SSMC_BANK(bank);

		__raw_writel(bt->smbidcyr, regs + SMBIDCYR);
		__raw_writel(bt->smbwstrd, regs + SMBWSTRDR);
		__raw_writel(bt->smbwstwr, regs + SMBWSTWRR);
		__raw_writel(bt->smbwstoen, regs + SMBWSTOENR);
		__raw_writel(bt->smbwstwen, regs + SMBWSTWENR);
		__raw_writel(bt->smbwstbrd, regs + SMBWSTBRDR);
	}
}

static inline unsigned int s3c2412_decode_timing(unsigned int clock, u32 reg)
{
	return (reg & 0xf) * clock;
}

static void s3c2412_iotiming_getbank(struct s3c_cpufreq_config *cfg,
				     struct s3c2412_iobank_timing *bt,
				     unsigned int bank)
{
	unsigned long clk = cfg->freq.hclk_tns;  /* ssmc clock??? */
	void __iomem *regs = S3C2412_SSMC_BANK(bank);

	bt->idcy = s3c2412_decode_timing(clk, __raw_readl(regs + SMBIDCYR));
	bt->wstrd = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTRDR));
	bt->wstoen = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTOENR));
	bt->wstwen = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTWENR));
	bt->wstbrd = s3c2412_decode_timing(clk, __raw_readl(regs + SMBWSTBRDR));
}

/**
 * bank_is_io - return true if bank is (possibly) IO.
 * @bank: The bank number.
 * @bankcfg: The value of S3C2412_EBI_BANKCFG.
 */
static inline bool bank_is_io(unsigned int bank, u32 bankcfg)
{
	if (bank < 2)
		return true;

	return !(bankcfg & (1 << bank));
}

int s3c2412_iotiming_get(struct s3c_cpufreq_config *cfg,
			 struct s3c_iotimings *timings)
{
	struct s3c2412_iobank_timing *bt;
	u32 bankcfg = __raw_readl(S3C2412_EBI_BANKCFG);
	unsigned int bank;

	/* look through all banks to see what is currently set. */

	for (bank = 0; bank < MAX_BANKS; bank++) {
		if (!bank_is_io(bank, bankcfg))
			continue;

		bt = kzalloc(sizeof(*bt), GFP_KERNEL);
		if (!bt)
			return -ENOMEM;

		timings->bank[bank].io_2412 = bt;
		s3c2412_iotiming_getbank(cfg, bt, bank);
	}

	s3c2412_print_timing("get", timings);
	return 0;
}

/* this is in here as it is so small, it doesn't currently warrant a file
 * to itself. We expect that any s3c24xx needing this is going to also
 * need the iotiming support.
 */
void s3c2412_cpufreq_setrefresh(struct s3c_cpufreq_config *cfg)
{
	struct s3c_cpufreq_board *board = cfg->board;
	u32 refresh;

	WARN_ON(board == NULL);

	/* Reduce both the refresh time (in ns) and the frequency (in MHz)
	 * down to ensure that we do not overflow 32 bit numbers.
	 *
	 * This should work for HCLK up to 133MHz and refresh period up
	 * to 30usec.
	 */

	refresh = (cfg->freq.hclk / 100) * (board->refresh / 10);
	refresh = DIV_ROUND_UP(refresh, (1000 * 1000)); /* apply scale  */
	refresh &= ((1 << 16) - 1);

	s3c_freq_dbg("%s: refresh value %u\n", __func__, (unsigned int)refresh);

	__raw_writel(refresh, S3C2412_REFRESH);
}


code:
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2003-2004 Simtec Electronics
//	Ben Dooks <ben@simtec.co.uk>
//	http://armlinux.simtec.co.uk/
//
// S3C24XX - IRQ PM code

#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/syscore_ops.h>
#include <linux/io.h>

#include "cpu.h"
#include "pm.h"
#include "map-base.h"
#include "map-s3c.h"

#include "regs-irq.h"
#include "regs-gpio.h"
#include "pm-core.h"

#include <asm/irq.h>

int s3c_irq_wake(struct irq_data *data, unsigned int state)
{
	unsigned long irqbit = 1 << data->hwirq;

	if (!(s3c_irqwake_intallow & irqbit))
		return -ENOENT;

	pr_info("wake %s for hwirq %lu\n",
		state ? "enabled" : "disabled", data->hwirq);

	if (!state)
		s3c_irqwake_intmask |= irqbit;
	else
		s3c_irqwake_intmask &= ~irqbit;

	return 0;
}

static struct sleep_save irq_save[] = {
	SAVE_ITEM(S3C2410_INTMSK),
	SAVE_ITEM(S3C2410_INTSUBMSK),
};

/* the extint values move between the s3c2410/s3c2440 and the s3c2412
 * so we use an array to hold them, and to calculate the address of
 * the register at run-time
*/

static unsigned long save_extint[3];
static unsigned long save_eintflt[4];
static unsigned long save_eintmask;

static int s3c24xx_irq_suspend(void)
{
	unsigned int i;

	for (i = 0; i < ARRAY_SIZE(save_extint); i++)
		save_extint[i] = __raw_readl(S3C24XX_EXTINT0 + (i*4));

	for (i = 0; i < ARRAY_SIZE(save_eintflt); i++)
		save_eintflt[i] = __raw_readl(S3C24XX_EINFLT0 + (i*4));

	s3c_pm_do_save(irq_save, ARRAY_SIZE(irq_save));
	save_eintmask = __raw_readl(S3C24XX_EINTMASK);

	return 0;
}

static void s3c24xx_irq_resume(void)
{
	unsigned int i;

	for (i = 0; i < ARRAY_SIZE(save_extint); i++)
		__raw_writel(save_extint[i], S3C24XX_EXTINT0 + (i*4));

	for (i = 0; i < ARRAY_SIZE(save_eintflt); i++)
		__raw_writel(save_eintflt[i], S3C24XX_EINFLT0 + (i*4));

	s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));
	__raw_writel(save_eintmask, S3C24XX_EINTMASK);
}

struct syscore_ops s3c24xx_irq_syscore_ops = {
	.suspend	= s3c24xx_irq_suspend,
	.resume		= s3c24xx_irq_resume,
};

#ifdef CONFIG_CPU_S3C2416
static struct sleep_save s3c2416_irq_save[] = {
	SAVE_ITEM(S3C2416_INTMSK2),
};

static int s3c2416_irq_suspend(void)
{
	s3c_pm_do_save(s3c2416_irq_save, ARRAY_SIZE(s3c2416_irq_save));

	return 0;
}

static void s3c2416_irq_resume(void)
{
	s3c_pm_do_restore(s3c2416_irq_save, ARRAY_SIZE(s3c2416_irq_save));
}

struct syscore_ops s3c2416_irq_syscore_ops = {
	.suspend	= s3c2416_irq_suspend,
	.resume		= s3c2416_irq_resume,
};
#endif


code:
// SPDX-License-Identifier: GPL-2.0
//
// Copyright 2008 Openmoko, Inc.
// Copyright 2008 Simtec Electronics
//      Ben Dooks <ben@simtec.co.uk>
//      http://armlinux.simtec.co.uk/
//
// S3C64XX - Interrupt handling Power Management

/*
 * NOTE: Code in this file is not used when booting with Device Tree support.
 */

#include <linux/kernel.h>
#include <linux/syscore_ops.h>
#include <linux/interrupt.h>
#include <linux/serial_core.h>
#include <linux/serial_s3c.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/of.h>

#include "map.h"

#include "regs-gpio.h"
#include "cpu.h"
#include "pm.h"

/* We handled all the IRQ types in this code, to save having to make several
 * small files to handle each different type separately. Having the EINT_GRP
 * code here shouldn't be as much bloat as the IRQ table space needed when
 * they are enabled. The added benefit is we ensure that these registers are
 * in the same state as we suspended.
 */

static struct sleep_save irq_save[] = {
	SAVE_ITEM(S3C64XX_PRIORITY),
	SAVE_ITEM(S3C64XX_EINT0CON0),
	SAVE_ITEM(S3C64XX_EINT0CON1),
	SAVE_ITEM(S3C64XX_EINT0FLTCON0),
	SAVE_ITEM(S3C64XX_EINT0FLTCON1),
	SAVE_ITEM(S3C64XX_EINT0FLTCON2),
	SAVE_ITEM(S3C64XX_EINT0FLTCON3),
	SAVE_ITEM(S3C64XX_EINT0MASK),
};

static struct irq_grp_save {
	u32	fltcon;
	u32	con;
	u32	mask;
} eint_grp_save[5];

#ifndef CONFIG_SERIAL_SAMSUNG_UARTS
#define SERIAL_SAMSUNG_UARTS 0
#else
#define	SERIAL_SAMSUNG_UARTS CONFIG_SERIAL_SAMSUNG_UARTS
#endif

static u32 irq_uart_mask[SERIAL_SAMSUNG_UARTS];

static int s3c64xx_irq_pm_suspend(void)
{
	struct irq_grp_save *grp = eint_grp_save;
	int i;

	S3C_PMDBG("%s: suspending IRQs\n", __func__);

	s3c_pm_do_save(irq_save, ARRAY_SIZE(irq_save));

	for (i = 0; i < SERIAL_SAMSUNG_UARTS; i++)
		irq_uart_mask[i] = __raw_readl(S3C_VA_UARTx(i) + S3C64XX_UINTM);

	for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) {
		grp->con = __raw_readl(S3C64XX_EINT12CON + (i * 4));
		grp->mask = __raw_readl(S3C64XX_EINT12MASK + (i * 4));
		grp->fltcon = __raw_readl(S3C64XX_EINT12FLTCON + (i * 4));
	}

	return 0;
}

static void s3c64xx_irq_pm_resume(void)
{
	struct irq_grp_save *grp = eint_grp_save;
	int i;

	S3C_PMDBG("%s: resuming IRQs\n", __func__);

	s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save));

	for (i = 0; i < SERIAL_SAMSUNG_UARTS; i++)
		__raw_writel(irq_uart_mask[i], S3C_VA_UARTx(i) + S3C64XX_UINTM);

	for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) {
		__raw_writel(grp->con, S3C64XX_EINT12CON + (i * 4));
		__raw_writel(grp->mask, S3C64XX_EINT12MASK + (i * 4));
		__raw_writel(grp->fltcon, S3C64XX_EINT12FLTCON + (i * 4));
	}

	S3C_PMDBG("%s: IRQ configuration restored\n", __func__);
}

static struct syscore_ops s3c64xx_irq_syscore_ops = {
	.suspend = s3c64xx_irq_pm_suspend,
	.resume	 = s3c64xx_irq_pm_resume,
};

static __init int s3c64xx_syscore_init(void)
{
	/* Appropriate drivers (pinctrl, uart) handle this when using DT. */
	if (of_have_populated_dt() || !soc_is_s3c64xx())
		return 0;

	register_syscore_ops(&s3c64xx_irq_syscore_ops);

	return 0;
}

core_initcall(s3c64xx_syscore_init);


code:
// SPDX-License-Identifier: GPL-2.0-only

#include <linux/stddef.h>
#include <linux/export.h>
#include <linux/spi/s3c24xx-fiq.h>

EXPORT_SYMBOL(s3c24xx_spi_fiq_rx);
EXPORT_SYMBOL(s3c24xx_spi_fiq_txrx);
EXPORT_SYMBOL(s3c24xx_spi_fiq_tx);


code:
/* SPDX-License-Identifier: GPL-2.0-only */
/* linux/drivers/spi/spi_s3c24xx_fiq.S
 *
 * Copyright 2009 Simtec Electronics
 *	Ben Dooks <ben@simtec.co.uk>
 *
 * S3C24XX SPI - FIQ pseudo-DMA transfer code
*/

#include <linux/linkage.h>
#include <asm/assembler.h>

#include "map.h"
#include "regs-irq.h"

#include <linux/spi/s3c24xx-fiq.h>

#define S3C2410_SPTDAT           (0x10)
#define S3C2410_SPRDAT           (0x14)

	.text

	@ entry to these routines is as follows, with the register names
	@ defined in fiq.h so that they can be shared with the C files which
	@ setup the calling registers.
	@
	@ fiq_rirq	The base of the IRQ registers to find S3C2410_SRCPND
	@ fiq_rtmp	Temporary register to hold tx/rx data
	@ fiq_rspi	The base of the SPI register block
	@ fiq_rtx	The tx buffer pointer
	@ fiq_rrx	The rx buffer pointer
	@ fiq_rcount	The number of bytes to move

	@ each entry starts with a word entry of how long it is
	@ and an offset to the irq acknowledgment word

ENTRY(s3c24xx_spi_fiq_rx)
	.word	fiq_rx_end - fiq_rx_start
	.word	fiq_rx_irq_ack - fiq_rx_start
fiq_rx_start:
	ldr	fiq_rtmp, fiq_rx_irq_ack
	str	fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]

	ldrb	fiq_rtmp, [ fiq_rspi, #  S3C2410_SPRDAT ]
	strb	fiq_rtmp, [ fiq_rrx ], #1

	mov	fiq_rtmp, #0xff
	strb	fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]

	subs	fiq_rcount, fiq_rcount, #1
	subsne	pc, lr, #4		@@ return, still have work to do

	@@ set IRQ controller so that next op will trigger IRQ
	mov	fiq_rtmp, #0
	str	fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD  - S3C24XX_VA_IRQ ]
	subs	pc, lr, #4

fiq_rx_irq_ack:
	.word	0
fiq_rx_end:

ENTRY(s3c24xx_spi_fiq_txrx)
	.word	fiq_txrx_end - fiq_txrx_start
	.word	fiq_txrx_irq_ack - fiq_txrx_start
fiq_txrx_start:

	ldrb	fiq_rtmp, [ fiq_rspi, #  S3C2410_SPRDAT ]
	strb	fiq_rtmp, [ fiq_rrx ], #1

	ldr	fiq_rtmp, fiq_txrx_irq_ack
	str	fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]

	ldrb	fiq_rtmp, [ fiq_rtx ], #1
	strb	fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]

	subs	fiq_rcount, fiq_rcount, #1
	subsne	pc, lr, #4		@@ return, still have work to do

	mov	fiq_rtmp, #0
	str	fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD  - S3C24XX_VA_IRQ ]
	subs	pc, lr, #4

fiq_txrx_irq_ack:
	.word	0

fiq_txrx_end:

ENTRY(s3c24xx_spi_fiq_tx)
	.word	fiq_tx_end - fiq_tx_start
	.word	fiq_tx_irq_ack - fiq_tx_start
fiq_tx_start:
	ldrb	fiq_rtmp, [ fiq_rspi, #  S3C2410_SPRDAT ]

	ldr	fiq_rtmp, fiq_tx_irq_ack
	str	fiq_rtmp, [ fiq_rirq, # S3C2410_SRCPND - S3C24XX_VA_IRQ ]

	ldrb	fiq_rtmp, [ fiq_rtx ], #1
	strb	fiq_rtmp, [ fiq_rspi, # S3C2410_SPTDAT ]

	subs	fiq_rcount, fiq_rcount, #1
	subsne	pc, lr, #4		@@ return, still have work to do

	mov	fiq_rtmp, #0
	str	fiq_rtmp, [ fiq_rirq, # S3C2410_INTMOD  - S3C24XX_VA_IRQ ]
	subs	pc, lr, #4

fiq_tx_irq_ack:
	.word	0

fiq_tx_end:

	.end


code:
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * S3C24XX IRQ handling
 *
 * Copyright (c) 2003-2004 Simtec Electronics
 *	Ben Dooks <ben@simtec.co.uk>
 * Copyright (c) 2012 Heiko Stuebner <heiko@sntech.de>
*/

#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/irqdomain.h>
#include <linux/irqchip.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/spi/s3c24xx.h>

#include <asm/exception.h>
#include <asm/mach/irq.h>

#include "irqs.h"
#include "regs-irq.h"
#include "regs-gpio.h"

#include "cpu.h"
#include "regs-irqtype.h"
#include "pm.h"
#include "s3c24xx.h"

#define S3C_IRQTYPE_NONE	0
#define S3C_IRQTYPE_EINT	1
#define S3C_IRQTYPE_EDGE	2
#define S3C_IRQTYPE_LEVEL	3

struct s3c_irq_data {
	unsigned int type;
	unsigned long offset;
	unsigned long parent_irq;

	/* data gets filled during init */
	struct s3c_irq_intc *intc;
	unsigned long sub_bits;
	struct s3c_irq_intc *sub_intc;
};

/*
 * Structure holding the controller data
 * @reg_pending		register holding pending irqs
 * @reg_intpnd		special register intpnd in main intc
 * @reg_mask		mask register
 * @domain		irq_domain of the controller
 * @parent		parent controller for ext and sub irqs
 * @irqs		irq-data, always s3c_irq_data[32]
 */
struct s3c_irq_intc {
	void __iomem		*reg_pending;
	void __iomem		*reg_intpnd;
	void __iomem		*reg_mask;
	struct irq_domain	*domain;
	struct s3c_irq_intc	*parent;
	struct s3c_irq_data	*irqs;
};

/*
 * Array holding pointers to the global controller structs
 * [0] ... main_intc
 * [1] ... sub_intc
 * [2] ... main_intc2 on s3c2416
 */
static struct s3c_irq_intc *s3c_intc[3];

static void s3c_irq_mask(struct irq_data *data)
{
	struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data);
	struct s3c_irq_intc *intc = irq_data->intc;
	struct s3c_irq_intc *parent_intc = intc->parent;
	struct s3c_irq_data *parent_data;
	unsigned long mask;
	unsigned int irqno;

	mask = readl_relaxed(intc->reg_mask);
	mask |= (1UL << irq_data->offset);
	writel_relaxed(mask, intc->reg_mask);

	if (parent_intc) {
		parent_data = &parent_intc->irqs[irq_data->parent_irq];

		/* check to see if we need to mask the parent IRQ
		 * The parent_irq is always in main_intc, so the hwirq
		 * for find_mapping does not need an offset in any case.
		 */
		if ((mask & parent_data->sub_bits) == parent_data->sub_bits) {
			irqno = irq_find_mapping(parent_intc->domain,
					 irq_data->parent_irq);
			s3c_irq_mask(irq_get_irq_data(irqno));
		}
	}
}

static void s3c_irq_unmask(struct irq_data *data)
{
	struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data);
	struct s3c_irq_intc *intc = irq_data->intc;
	struct s3c_irq_intc *parent_intc = intc->parent;
	unsigned long mask;
	unsigned int irqno;

	mask = readl_relaxed(intc->reg_mask);
	mask &= ~(1UL << irq_data->offset);
	writel_relaxed(mask, intc->reg_mask);

	if (parent_intc) {
		irqno = irq_find_mapping(parent_intc->domain,
					 irq_data->parent_irq);
		s3c_irq_unmask(irq_get_irq_data(irqno));
	}
}

static inline void s3c_irq_ack(struct irq_data *data)
{
	struct s3c_irq_data *irq_data = irq_data_get_irq_chip_data(data);
	struct s3c_irq_intc *intc = irq_data->intc;
	unsigned long bitval = 1UL << irq_data->offset;

	writel_relaxed(bitval, intc->reg_pending);
	if (intc->reg_intpnd)
		writel_relaxed(bitval, intc->reg_intpnd);
}

static int s3c_irq_type(struct irq_data *data, unsigned int type)
{
	switch (type) {
	case IRQ_TYPE_NONE:
		break;
	case IRQ_TYPE_EDGE_RISING:
	case IRQ_TYPE_EDGE_FALLING:
	case IRQ_TYPE_EDGE_BOTH:
		irq_set_handler(data->irq, handle_edge_irq);
		break;
	case IRQ_TYPE_LEVEL_LOW:
	case IRQ_TYPE_LEVEL_HIGH:
		irq_set_handler(data->irq, handle_level_irq);
		break;
	default:
		pr_err("No such irq type %d\n", type);
		return -EINVAL;
	}

	return 0;
}

static int s3c_irqext_type_set(void __iomem *gpcon_reg,
			       void __iomem *extint_reg,
			       unsigned long gpcon_offset,
			       unsigned long extint_offset,
			       unsigned int type)
{
	unsigned long newvalue = 0, value;

	/* Set the GPIO to external interrupt mode */
	value = readl_relaxed(gpcon_reg);
	value = (value & ~(3 << gpcon_offset)) | (0x02 << gpcon_offset);
	writel_relaxed(value, gpcon_reg);

	/* Set the external interrupt to pointed trigger type */
	switch (type)
	{
		case IRQ_TYPE_NONE:
			pr_warn("No edge setting!\n");
			break;

		case IRQ_TYPE_EDGE_RISING:
			newvalue = S3C2410_EXTINT_RISEEDGE;
			break;

		case IRQ_TYPE_EDGE_FALLING:
			newvalue = S3C2410_EXTINT_FALLEDGE;
			break;

		case IRQ_TYPE_EDGE_BOTH:
			newvalue = S3C2410_EXTINT_BOTHEDGE;
			break;

		case IRQ_TYPE_LEVEL_LOW:
			newvalue = S3C2410_EXTINT_LOWLEV;
			break;

		case IRQ_TYPE_LEVEL_HIGH:
			newvalue = S3C2410_EXTINT_HILEV;
			break;

		default:
			pr_err("No such irq type %d\n", type);
			return -EINVAL;
	}

	value = readl_relaxed(extint_reg);
	value = (value & ~(7 << extint_offset)) | (newvalue << extint_offset);
	writel_relaxed(value, extint_reg);

	return 0;
}

static int s3c_irqext_type(struct irq_data *data, unsigned int type)
{
	void __iomem *extint_reg;
	void __iomem *gpcon_reg;
	unsigned long gpcon_offset, extint_offset;

	if ((data->hwirq >= 4) && (data->hwirq <= 7)) {
		gpcon_reg = S3C2410_GPFCON;
		extint_reg = S3C24XX_EXTINT0;
		gpcon_offset = (data->hwirq) * 2;
		extint_offset = (data->hwirq) * 4;
	} else if ((data->hwirq >= 8) && (data->hwirq <= 15)) {
		gpcon_reg = S3C2410_GPGCON;
		extint_reg = S3C24XX_EXTINT1;
		gpcon_offset = (data->hwirq - 8) * 2;
		extint_offset = (data->hwirq - 8) * 4;
	} else if ((data->hwirq >= 16) && (data->hwirq <= 23)) {
		gpcon_reg = S3C2410_GPGCON;
		extint_reg = S3C24XX_EXTINT2;
		gpcon_offset = (data->hwirq - 8) * 2;
		extint_offset = (data->hwirq - 16) * 4;
	} else {
		return -EINVAL;
	}

	return s3c_irqext_type_set(gpcon_reg, extint_reg, gpcon_offset,
				   extint_offset, type);
}

static int s3c_irqext0_type(struct irq_data *data, unsigned int type)
{
	void __iomem *extint_reg;
	void __iomem *gpcon_reg;
	unsigned long gpcon_offset, extint_offset;

	if (data->hwirq <= 3) {
		gpcon_reg = S3C2410_GPFCON;
		extint_reg = S3C24XX_EXTINT0;
		gpcon_offset = (data->hwirq) * 2;
		extint_offset = (data->hwirq) * 4;
	} else {
		return -EINVAL;
	}

	return s3c_irqext_type_set(gpcon_reg, extint_reg, gpcon_offset,
				   extint_offset, type);
}

static struct irq_chip s3c_irq_chip = {
	.name		= "s3c",
	.irq_ack	= s3c_irq_ack,
	.irq_mask	= s3c_irq_mask,
	.irq_unmask	= s3c_irq_unmask,
	.irq_set_type	= s3c_irq_type,
	.irq_set_wake	= s3c_irq_wake
};

static struct irq_chip s3c_irq_level_chip = {
	.name		= "s3c-level",
	.irq_mask	= s3c_irq_mask,
	.irq_unmask	= s3c_irq_unmask,
	.irq_ack	= s3c_irq_ack,
	.irq_set_type	= s3c_irq_type,
};

static struct irq_chip s3c_irqext_chip = {
	.name		= "s3c-ext",
	.irq_mask	= s3c_irq_mask,
	.irq_unmask	= s3c_irq_unmask,
	.irq_ack	= s3c_irq_ack,
	.irq_set_type	= s3c_irqext_type,
	.irq_set_wake	= s3c_irqext_wake
};

static struct irq_chip s3c_irq_eint0t4 = {
	.name		= "s3c-ext0",
	.irq_ack	= s3c_irq_ack,
	.irq_mask	= s3c_irq_mask,
	.irq_unmask	= s3c_irq_unmask,
	.irq_set_wake	= s3c_irq_wake,
	.irq_set_type	= s3c_irqext0_type,
};

static void s3c_irq_demux(struct irq_desc *desc)
{
	struct irq_chip *chip = irq_desc_get_chip(desc);
	struct s3c_irq_data *irq_data = irq_desc_get_chip_data(desc);
	struct s3c_irq_intc *intc = irq_data->intc;
	struct s3c_irq_intc *sub_intc = irq_data->sub_intc;
	unsigned int n, offset;
	unsigned long src, msk;

	/* we're using individual domains for the non-dt case
	 * and one big domain for the dt case where the subintc
	 * starts at hwirq number 32.
	 */
	offset = irq_domain_get_of_node(intc->domain) ? 32 : 0;

	chained_irq_enter(chip, desc);

	src = readl_relaxed(sub_intc->reg_pending);
	msk = readl_relaxed(sub_intc->reg_mask);

	src &= ~msk;
	src &= irq_data->sub_bits;

	while (src) {
		n = __ffs(src);
		src &= ~(1 << n);
		generic_handle_domain_irq(sub_intc->domain, offset + n);
	}

	chained_irq_exit(chip, desc);
}

static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc,
				      struct pt_regs *regs, int intc_offset)
{
	int pnd;
	int offset;

	pnd = readl_relaxed(intc->reg_intpnd);
	if (!pnd)
		return false;

	/* non-dt machines use individual domains */
	if (!irq_domain_get_of_node(intc->domain))
		intc_offset = 0;

	/* We have a problem that the INTOFFSET register does not always
	 * show one interrupt. Occasionally we get two interrupts through
	 * the prioritiser, and this causes the INTOFFSET register to show
	 * what looks like the logical-or of the two interrupt numbers.
	 *
	 * Thanks to Klaus, Shannon, et al for helping to debug this problem
	 */
	offset = readl_relaxed(intc->reg_intpnd + 4);

	/* Find the bit manually, when the offset is wrong.
	 * The pending register only ever contains the one bit of the next
	 * interrupt to handle.
	 */
	if (!(pnd & (1 << offset)))
		offset =  __ffs(pnd);

	generic_handle_domain_irq(intc->domain, intc_offset + offset);
	return true;
}

static asmlinkage void __exception_irq_entry s3c24xx_handle_irq(struct pt_regs *regs)
{
	do {
		/*
		 * For platform based machines, neither ERR nor NULL can happen here.
		 * The s3c24xx_handle_irq() will be set as IRQ handler iff this succeeds:
		 *
		 *    s3c_intc[0] = s3c24xx_init_intc()
		 *
		 * If this fails, the next calls to s3c24xx_init_intc() won't be executed.
		 *
		 * For DT machine, s3c_init_intc_of() could set the IRQ handler without
		 * setting s3c_intc[0] only if it was called with num_ctrl=0. There is no
		 * such code path, so again the s3c_intc[0] will have a valid pointer if
		 * set_handle_irq() is called.
		 *
		 * Therefore in s3c24xx_handle_irq(), the s3c_intc[0] is always something.
		 */
		if (s3c24xx_handle_intc(s3c_intc[0], regs, 0))
			continue;

		if (!IS_ERR_OR_NULL(s3c_intc[2]))
			if (s3c24xx_handle_intc(s3c_intc[2], regs, 64))
				continue;

		break;
	} while (1);
}

#ifdef CONFIG_FIQ
/**
 * s3c24xx_set_fiq - set the FIQ routing
 * @irq: IRQ number to route to FIQ on processor.
 * @ack_ptr: pointer to a location for storing the bit mask
 * @on: Whether to route @irq to the FIQ, or to remove the FIQ routing.
 *
 * Change the state of the IRQ to FIQ routing depending on @irq and @on. If
 * @on is true, the @irq is checked to see if it can be routed and the
 * interrupt controller updated to route the IRQ. If @on is false, the FIQ
 * routing is cleared, regardless of which @irq is specified.
 *
 * returns the mask value for the register.
 */
int s3c24xx_set_fiq(unsigned int irq, u32 *ack_ptr, bool on)
{
	u32 intmod;
	unsigned offs;

	if (on) {
		offs = irq - FIQ_START;
		if (offs > 31)
			return 0;

		intmod = 1 << offs;
	} else {
		intmod = 0;
	}

	if (ack_ptr)
		*ack_ptr = intmod;
	writel_relaxed(intmod, S3C2410_INTMOD);

	return intmod;
}

EXPORT_SYMBOL_GPL(s3c24xx_set_fiq);
#endif

static int s3c24xx_irq_map(struct irq_domain *h, unsigned int virq,
							irq_hw_number_t hw)
{
	struct s3c_irq_intc *intc = h->host_data;
	struct s3c_irq_data *irq_data = &intc->irqs[hw];
	struct s3c_irq_intc *parent_intc;
	struct s3c_irq_data *parent_irq_data;
	unsigned int irqno;

	/* attach controller pointer to irq_data */
	irq_data->intc = intc;
	irq_data->offset = hw;

	parent_intc = intc->parent;

	/* set handler and flags */
	switch (irq_data->type) {
	case S3C_IRQTYPE_NONE:
		return 0;
	case S3C_IRQTYPE_EINT:
		/* On the S3C2412, the EINT0to3 have a parent irq
		 * but need the s3c_irq_eint0t4 chip
		 */
		if (parent_intc && (!soc_is_s3c2412() || hw >= 4))
			irq_set_chip_and_handler(virq, &s3c_irqext_chip,
						 handle_edge_irq);
		else
			irq_set_chip_and_handler(virq, &s3c_irq_eint0t4,
						 handle_edge_irq);
		break;
	case S3C_IRQTYPE_EDGE:
		if (parent_intc || intc->reg_pending == S3C2416_SRCPND2)
			irq_set_chip_and_handler(virq, &s3c_irq_level_chip,
						 handle_edge_irq);
		else
			irq_set_chip_and_handler(virq, &s3c_irq_chip,
						 handle_edge_irq);
		break;
	case S3C_IRQTYPE_LEVEL:
		if (parent_intc)
			irq_set_chip_and_handler(virq, &s3c_irq_level_chip,
						 handle_level_irq);
		else
			irq_set_chip_and_handler(virq, &s3c_irq_chip,
						 handle_level_irq);
		break;
	default:
		pr_err("irq-s3c24xx: unsupported irqtype %d\n", irq_data->type);
		return -EINVAL;
	}

	irq_set_chip_data(virq, irq_data);

	if (parent_intc && irq_data->type != S3C_IRQTYPE_NONE) {
		if (irq_data->parent_irq > 31) {
			pr_err("irq-s3c24xx: parent irq %lu is out of range\n",
			       irq_data->parent_irq);
			return -EINVAL;
		}

		parent_irq_data = &parent_intc->irqs[irq_data->parent_irq];
		parent_irq_data->sub_intc = intc;
		parent_irq_data->sub_bits |= (1UL << hw);

		/* attach the demuxer to the parent irq */
		irqno = irq_find_mapping(parent_intc->domain,
					 irq_data->parent_irq);
		if (!irqno) {
			pr_err("irq-s3c24xx: could not find mapping for parent irq %lu\n",
			       irq_data->parent_irq);
			return -EINVAL;
		}
		irq_set_chained_handler(irqno, s3c_irq_demux);
	}

	return 0;
}

static const struct irq_domain_ops s3c24xx_irq_ops = {
	.map = s3c24xx_irq_map,
	.xlate = irq_domain_xlate_twocell,
};

static void s3c24xx_clear_intc(struct s3c_irq_intc *intc)
{
	void __iomem *reg_source;
	unsigned long pend;
	unsigned long last;
	int i;

	/* if intpnd is set, read the next pending irq from there */
	reg_source = intc->reg_intpnd ? intc->reg_intpnd : intc->reg_pending;

	last = 0;
	for (i = 0; i < 4; i++) {
		pend = readl_relaxed(reg_source);

		if (pend == 0 || pend == last)
			break;

		writel_relaxed(pend, intc->reg_pending);
		if (intc->reg_intpnd)
			writel_relaxed(pend, intc->reg_intpnd);

		pr_info("irq: clearing pending status %08x\n", (int)pend);
		last = pend;
	}
}

static struct s3c_irq_intc * __init s3c24xx_init_intc(struct device_node *np,
				       struct s3c_irq_data *irq_data,
				       struct s3c_irq_intc *parent,
				       unsigned long address)
{
	struct s3c_irq_intc *intc;
	void __iomem *base = (void *)0xf6000000; /* static mapping */
	int irq_num;
	int irq_start;
	int ret;

	intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL);
	if (!intc)
		return ERR_PTR(-ENOMEM);

	intc->irqs = irq_data;

	if (parent)
		intc->parent = parent;

	/* select the correct data for the controller.
	 * Need to hard code the irq num start and offset
	 * to preserve the static mapping for now
	 */
	switch (address) {
	case 0x4a000000:
		pr_debug("irq: found main intc\n");
		intc->reg_pending = base;
		intc->reg_mask = base + 0x08;
		intc->reg_intpnd = base + 0x10;
		irq_num = 32;
		irq_start = S3C2410_IRQ(0);
		break;
	case 0x4a000018:
		pr_debug("irq: found subintc\n");
		intc->reg_pending = base + 0x18;
		intc->reg_mask = base + 0x1c;
		irq_num = 29;
		irq_start = S3C2410_IRQSUB(0);
		break;
	case 0x4a000040:
		pr_debug("irq: found intc2\n");
		intc->reg_pending = base + 0x40;
		intc->reg_mask = base + 0x48;
		intc->reg_intpnd = base + 0x50;
		irq_num = 8;
		irq_start = S3C2416_IRQ(0);
		break;
	case 0x560000a4:
		pr_debug("irq: found eintc\n");
		base = (void *)0xfd000000;

		intc->reg_mask = base + 0xa4;
		intc->reg_pending = base + 0xa8;
		irq_num = 24;
		irq_start = S3C2410_IRQ(32);
		break;
	default:
		pr_err("irq: unsupported controller address\n");
		ret = -EINVAL;
		goto err;
	}

	/* now that all the data is complete, init the irq-domain */
	s3c24xx_clear_intc(intc);
	intc->domain = irq_domain_add_legacy(np, irq_num, irq_start,
					     0, &s3c24xx_irq_ops,
					     intc);
	if (!intc->domain) {
		pr_err("irq: could not create irq-domain\n");
		ret = -EINVAL;
		goto err;
	}

	set_handle_irq(s3c24xx_handle_irq);

	return intc;

err:
	kfree(intc);
	return ERR_PTR(ret);
}

static struct s3c_irq_data __maybe_unused init_eint[32] = {
	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT4 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT5 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT6 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT7 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT8 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT9 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT10 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT11 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT12 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT13 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT14 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT15 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT16 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT17 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT18 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT19 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT20 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT21 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT22 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT23 */
};

#ifdef CONFIG_CPU_S3C2410
static struct s3c_irq_data init_s3c2410base[32] = {
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */
	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
	{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */
	{ .type = S3C_IRQTYPE_EDGE, }, /* WDT */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* LCD */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SDI */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */
	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
	{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */
	{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */
	{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */
};

static struct s3c_irq_data init_s3c2410subint[32] = {
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */
	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */
	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */
};

void __init s3c2410_init_irq(void)
{
#ifdef CONFIG_FIQ
	init_FIQ(FIQ_START);
#endif

	s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2410base[0], NULL,
					0x4a000000);
	if (IS_ERR(s3c_intc[0])) {
		pr_err("irq: could not create main interrupt controller\n");
		return;
	}

	s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2410subint[0],
					s3c_intc[0], 0x4a000018);
	s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4);
}
#endif

#ifdef CONFIG_CPU_S3C2412
static struct s3c_irq_data init_s3c2412base[32] = {
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT0 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT1 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT2 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT3 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */
	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
	{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */
	{ .type = S3C_IRQTYPE_EDGE, }, /* WDT */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* LCD */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* SDI/CF */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */
	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
	{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */
	{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */
	{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */
};

static struct s3c_irq_data init_s3c2412eint[32] = {
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 0 }, /* EINT0 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 1 }, /* EINT1 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 2 }, /* EINT2 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 3 }, /* EINT3 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT4 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT5 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT6 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 4 }, /* EINT7 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT8 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT9 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT10 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT11 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT12 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT13 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT14 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT15 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT16 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT17 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT18 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT19 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT20 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT21 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT22 */
	{ .type = S3C_IRQTYPE_EINT, .parent_irq = 5 }, /* EINT23 */
};

static struct s3c_irq_data init_s3c2412subint[32] = {
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */
	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */
	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */
	{ .type = S3C_IRQTYPE_NONE, },
	{ .type = S3C_IRQTYPE_NONE, },
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 21 }, /* SDI */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 21 }, /* CF */
};

void __init s3c2412_init_irq(void)
{
	pr_info("S3C2412: IRQ Support\n");

#ifdef CONFIG_FIQ
	init_FIQ(FIQ_START);
#endif

	s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2412base[0], NULL,
					0x4a000000);
	if (IS_ERR(s3c_intc[0])) {
		pr_err("irq: could not create main interrupt controller\n");
		return;
	}

	s3c24xx_init_intc(NULL, &init_s3c2412eint[0], s3c_intc[0], 0x560000a4);
	s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2412subint[0],
					s3c_intc[0], 0x4a000018);
}
#endif

#ifdef CONFIG_CPU_S3C2416
static struct s3c_irq_data init_s3c2416base[32] = {
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */
	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
	{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* LCD */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* DMA */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART3 */
	{ .type = S3C_IRQTYPE_NONE, }, /* reserved */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SDI1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SDI0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* NAND */
	{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */
	{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */
	{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */
	{ .type = S3C_IRQTYPE_NONE, },
	{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */
};

static struct s3c_irq_data init_s3c2416subint[32] = {
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */
	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */
	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */
	{ .type = S3C_IRQTYPE_NONE }, /* reserved */
	{ .type = S3C_IRQTYPE_NONE }, /* reserved */
	{ .type = S3C_IRQTYPE_NONE }, /* reserved */
	{ .type = S3C_IRQTYPE_NONE }, /* reserved */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD2 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD3 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD4 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA0 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA1 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA2 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA3 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA4 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA5 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-ERR */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */
};

static struct s3c_irq_data init_s3c2416_second[32] = {
	{ .type = S3C_IRQTYPE_EDGE }, /* 2D */
	{ .type = S3C_IRQTYPE_NONE }, /* reserved */
	{ .type = S3C_IRQTYPE_NONE }, /* reserved */
	{ .type = S3C_IRQTYPE_NONE }, /* reserved */
	{ .type = S3C_IRQTYPE_EDGE }, /* PCM0 */
	{ .type = S3C_IRQTYPE_NONE }, /* reserved */
	{ .type = S3C_IRQTYPE_EDGE }, /* I2S0 */
};

void __init s3c2416_init_irq(void)
{
	pr_info("S3C2416: IRQ Support\n");

#ifdef CONFIG_FIQ
	init_FIQ(FIQ_START);
#endif

	s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2416base[0], NULL,
					0x4a000000);
	if (IS_ERR(s3c_intc[0])) {
		pr_err("irq: could not create main interrupt controller\n");
		return;
	}

	s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4);
	s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2416subint[0],
					s3c_intc[0], 0x4a000018);

	s3c_intc[2] = s3c24xx_init_intc(NULL, &init_s3c2416_second[0],
					NULL, 0x4a000040);
}

#endif

#ifdef CONFIG_CPU_S3C2440
static struct s3c_irq_data init_s3c2440base[32] = {
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* CAM */
	{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* LCD */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SDI */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* NFCON */
	{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */
	{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */
	{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */
};

static struct s3c_irq_data init_s3c2440subint[32] = {
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */
	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */
	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */
};

void __init s3c2440_init_irq(void)
{
	pr_info("S3C2440: IRQ Support\n");

#ifdef CONFIG_FIQ
	init_FIQ(FIQ_START);
#endif

	s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2440base[0], NULL,
					0x4a000000);
	if (IS_ERR(s3c_intc[0])) {
		pr_err("irq: could not create main interrupt controller\n");
		return;
	}

	s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4);
	s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2440subint[0],
					s3c_intc[0], 0x4a000018);
}
#endif

#ifdef CONFIG_CPU_S3C2442
static struct s3c_irq_data init_s3c2442base[32] = {
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* CAM */
	{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */
	{ .type = S3C_IRQTYPE_EDGE, }, /* WDT */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* LCD */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA2 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* DMA3 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SDI */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* NFCON */
	{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */
	{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */
	{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */
};

static struct s3c_irq_data init_s3c2442subint[32] = {
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */
	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */
	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */
};

void __init s3c2442_init_irq(void)
{
	pr_info("S3C2442: IRQ Support\n");

#ifdef CONFIG_FIQ
	init_FIQ(FIQ_START);
#endif

	s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2442base[0], NULL,
					0x4a000000);
	if (IS_ERR(s3c_intc[0])) {
		pr_err("irq: could not create main interrupt controller\n");
		return;
	}

	s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4);
	s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2442subint[0],
					s3c_intc[0], 0x4a000018);
}
#endif

#ifdef CONFIG_CPU_S3C2443
static struct s3c_irq_data init_s3c2443base[32] = {
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT0 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT1 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT2 */
	{ .type = S3C_IRQTYPE_EINT, }, /* EINT3 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT4to7 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* EINT8to23 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* CAM */
	{ .type = S3C_IRQTYPE_EDGE, }, /* nBATT_FLT */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TICK */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* WDT/AC97 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER2 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER3 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* TIMER4 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART2 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* LCD */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* DMA */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART3 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* CFON */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SDI1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SDI0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI0 */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* NAND */
	{ .type = S3C_IRQTYPE_EDGE, }, /* USBD */
	{ .type = S3C_IRQTYPE_EDGE, }, /* USBH */
	{ .type = S3C_IRQTYPE_EDGE, }, /* IIC */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* UART0 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* SPI1 */
	{ .type = S3C_IRQTYPE_EDGE, }, /* RTC */
	{ .type = S3C_IRQTYPE_LEVEL, }, /* ADCPARENT */
};

static struct s3c_irq_data init_s3c2443subint[32] = {
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 28 }, /* UART0-ERR */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 23 }, /* UART1-ERR */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 15 }, /* UART2-ERR */
	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* TC */
	{ .type = S3C_IRQTYPE_EDGE, .parent_irq = 31 }, /* ADC */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_C */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 6 }, /* CAM_P */
	{ .type = S3C_IRQTYPE_NONE }, /* reserved */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD1 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD2 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD3 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 16 }, /* LCD4 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA0 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA1 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA2 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA3 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA4 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 17 }, /* DMA5 */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-RX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-TX */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 18 }, /* UART3-ERR */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* WDT */
	{ .type = S3C_IRQTYPE_LEVEL, .parent_irq = 9 }, /* AC97 */
};

void __init s3c2443_init_irq(void)
{
	pr_info("S3C2443: IRQ Support\n");

#ifdef CONFIG_FIQ
	init_FIQ(FIQ_START);
#endif

	s3c_intc[0] = s3c24xx_init_intc(NULL, &init_s3c2443base[0], NULL,
					0x4a000000);
	if (IS_ERR(s3c_intc[0])) {
		pr_err("irq: could not create main interrupt controller\n");
		return;
	}

	s3c24xx_init_intc(NULL, &init_eint[0], s3c_intc[0], 0x560000a4);
	s3c_intc[1] = s3c24xx_init_intc(NULL, &init_s3c2443subint[0],
					s3c_intc[0], 0x4a000018);
}
#endif

#ifdef CONFIG_OF
static int s3c24xx_irq_map_of(struct irq_domain *h, unsigned int virq,
							irq_hw_number_t hw)
{
	unsigned int ctrl_num = hw / 32;
	unsigned int intc_hw = hw % 32;
	struct s3c_irq_intc *intc = s3c_intc[ctrl_num];
	struct s3c_irq_intc *parent_intc = intc->parent;
	struct s3c_irq_data *irq_data = &intc->irqs[intc_hw];

	/* attach controller pointer to irq_data */
	irq_data->intc = intc;
	irq_data->offset = intc_hw;

	if (!parent_intc)
		irq_set_chip_and_handler(virq, &s3c_irq_chip, handle_edge_irq);
	else
		irq_set_chip_and_handler(virq, &s3c_irq_level_chip,
					 handle_edge_irq);

	irq_set_chip_data(virq, irq_data);

	return 0;
}

/* Translate our of irq notation
 * format: <ctrl_num ctrl_irq parent_irq type>
 */
static int s3c24xx_irq_xlate_of(struct irq_domain *d, struct device_node *n,
			const u32 *intspec, unsigned int intsize,
			irq_hw_number_t *out_hwirq, unsigned int *out_type)
{
	struct s3c_irq_intc *intc;
	struct s3c_irq_intc *parent_intc;
	struct s3c_irq_data *irq_data;
	struct s3c_irq_data *parent_irq_data;
	int irqno;

	if (WARN_ON(intsize < 4))
		return -EINVAL;

	if (intspec[0] > 2 || !s3c_intc[intspec[0]]) {
		pr_err("controller number %d invalid\n", intspec[0]);
		return -EINVAL;
	}
	intc = s3c_intc[intspec[0]];

	*out_hwirq = intspec[0] * 32 + intspec[2];
	*out_type = intspec[3] & IRQ_TYPE_SENSE_MASK;

	parent_intc = intc->parent;
	if (parent_intc) {
		irq_data = &intc->irqs[intspec[2]];
		irq_data->parent_irq = intspec[1];
		parent_irq_data = &parent_intc->irqs[irq_data->parent_irq];
		parent_irq_data->sub_intc = intc;
		parent_irq_data->sub_bits |= (1UL << intspec[2]);

		/* parent_intc is always s3c_intc[0], so no offset */
		irqno = irq_create_mapping(parent_intc->domain, intspec[1]);
		if (irqno < 0) {
			pr_err("irq: could not map parent interrupt\n");
			return irqno;
		}

		irq_set_chained_handler(irqno, s3c_irq_demux);
	}

	return 0;
}

static const struct irq_domain_ops s3c24xx_irq_ops_of = {
	.map = s3c24xx_irq_map_of,
	.xlate = s3c24xx_irq_xlate_of,
};

struct s3c24xx_irq_of_ctrl {
	char			*name;
	unsigned long		offset;
	struct s3c_irq_intc	**handle;
	struct s3c_irq_intc	**parent;
	struct irq_domain_ops	*ops;
};

static int __init s3c_init_intc_of(struct device_node *np,
			struct device_node *interrupt_parent,
			struct s3c24xx_irq_of_ctrl *s3c_ctrl, int num_ctrl)
{
	struct s3c_irq_intc *intc;
	struct s3c24xx_irq_of_ctrl *ctrl;
	struct irq_domain *domain;
	void __iomem *reg_base;
	int i;

	reg_base = of_iomap(np, 0);
	if (!reg_base) {
		pr_err("irq-s3c24xx: could not map irq registers\n");
		return -EINVAL;
	}

	domain = irq_domain_add_linear(np, num_ctrl * 32,
						     &s3c24xx_irq_ops_of, NULL);
	if (!domain) {
		pr_err("irq: could not create irq-domain\n");
		return -EINVAL;
	}

	for (i = 0; i < num_ctrl; i++) {
		ctrl = &s3c_ctrl[i];

		pr_debug("irq: found controller %s\n", ctrl->name);

		intc = kzalloc(sizeof(struct s3c_irq_intc), GFP_KERNEL);
		if (!intc)
			return -ENOMEM;

		intc->domain = domain;
		intc->irqs = kcalloc(32, sizeof(struct s3c_irq_data),
				     GFP_KERNEL);
		if (!intc->irqs) {
			kfree(intc);
			return -ENOMEM;
		}

		if (ctrl->parent) {
			intc->reg_pending = reg_base + ctrl->offset;
			intc->reg_mask = reg_base + ctrl->offset + 0x4;

			if (*(ctrl->parent)) {
				intc->parent = *(ctrl->parent);
			} else {
				pr_warn("irq: parent of %s missing\n",
					ctrl->name);
				kfree(intc->irqs);
				kfree(intc);
				continue;
			}
		} else {
			intc->reg_pending = reg_base + ctrl->offset;
			intc->reg_mask = reg_base + ctrl->offset + 0x08;
			intc->reg_intpnd = reg_base + ctrl->offset + 0x10;
		}

		s3c24xx_clear_intc(intc);
		s3c_intc[i] = intc;
	}

	set_handle_irq(s3c24xx_handle_irq);

	return 0;
}

static struct s3c24xx_irq_of_ctrl s3c2410_ctrl[] = {
	{
		.name = "intc",
		.offset = 0,
	}, {
		.name = "subintc",
		.offset = 0x18,
		.parent = &s3c_intc[0],
	}
};

static int __init s3c2410_init_intc_of(struct device_node *np,
			struct device_node *interrupt_parent)
{
	return s3c_init_intc_of(np, interrupt_parent,
				s3c2410_ctrl, ARRAY_SIZE(s3c2410_ctrl));
}
IRQCHIP_DECLARE(s3c2410_irq, "samsung,s3c2410-irq", s3c2410_init_intc_of);

static struct s3c24xx_irq_of_ctrl s3c2416_ctrl[] = {
	{
		.name = "intc",
		.offset = 0,
	}, {
		.name = "subintc",
		.offset = 0x18,
		.parent = &s3c_intc[0],
	}, {
		.name = "intc2",
		.offset = 0x40,
	}
};

static int __init s3c2416_init_intc_of(struct device_node *np,
			struct device_node *interrupt_parent)
{
	return s3c_init_intc_of(np, interrupt_parent,
				s3c2416_ctrl, ARRAY_SIZE(s3c2416_ctrl));
}
IRQCHIP_DECLARE(s3c2416_irq, "samsung,s3c2416-irq", s3c2416_init_intc_of);
#endif


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (c) 2010 Simtec Electronics
 *	Ben Dooks <ben@simtec.co.uk>
 *
 * Header file for Samsung SoC UART IRQ demux for S3C64XX and later
 */

struct s3c_uart_irq {
	void __iomem	*regs;
	unsigned int	 base_irq;
	unsigned int	 parent_irq;
};

extern void s3c_init_uart_irqs(struct s3c_uart_irq *irq, unsigned int nr_irqs);


code:
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (c) 2003-2005 Simtec Electronics
 *   Ben Dooks <ben@simtec.co.uk>
 */

#ifndef __ASM_ARCH_IRQS_H
#define __ASM_ARCH_IRQS_H __FILE__

/* we keep the first set of CPU IRQs out of the range of
 * the ISA space, so that the PC104 has them to itself
 * and we don't end up having to do horrible things to the
 * standard ISA drivers....
 */

#define S3C2410_CPUIRQ_OFFSET	 (16)

#define S3C2410_IRQ(x) ((x) + S3C2410_CPUIRQ_OFFSET)

/* main cpu interrupts */
#define IRQ_EINT0      S3C2410_IRQ(0)	    /* 16 */
#define IRQ_EINT1      S3C2410_IRQ(1)
#define IRQ_EINT2      S3C2410_IRQ(2)
#define IRQ_EINT3      S3C2410_IRQ(3)
#define IRQ_EINT4t7    S3C2410_IRQ(4)	    /* 20 */
#define IRQ_EINT8t23   S3C2410_IRQ(5)
#define IRQ_RESERVED6  S3C2410_IRQ(6)	    /* for s3c2410 */
#define IRQ_CAM        S3C2410_IRQ(6)	    /* for s3c2440,s3c2443 */
#define IRQ_BATT_FLT   S3C2410_IRQ(7)
#define IRQ_TICK       S3C2410_IRQ(8)	    /* 24 */
#define IRQ_WDT	       S3C2410_IRQ(9)	    /* WDT/AC97 for s3c2443 */
#define IRQ_TIMER0     S3C2410_IRQ(10)
#define IRQ_TIMER1     S3C2410_IRQ(11)
#define IRQ_TIMER2     S3C2410_IRQ(12)
#define IRQ_TIMER3     S3C2410_IRQ(13)
#define IRQ_TIMER4     S3C2410_IRQ(14)
#define IRQ_UART2      S3C2410_IRQ(15)
#define IRQ_LCD	       S3C2410_IRQ(16)	    /* 32 */
#define IRQ_DMA0       S3C2410_IRQ(17)	    /* IRQ_DMA for s3c2443 */
#define IRQ_DMA1       S3C2410_IRQ(18)
#define IRQ_DMA2       S3C2410_IRQ(19)
#define IRQ_DMA3       S3C2410_IRQ(20)
#define IRQ_SDI	       S3C2410_IRQ(21)
#define IRQ_SPI0       S3C2410_IRQ(22)
#define IRQ_UART1      S3C2410_IRQ(23)
#define IRQ_RESERVED24 S3C2410_IRQ(24)	    /* 40 */
#define IRQ_NFCON      S3C2410_IRQ(24)	    /* for s3c2440 */
#define IRQ_USBD       S3C2410_IRQ(25)
#define IRQ_USBH       S3C2410_IRQ(26)
#define IRQ_IIC	       S3C2410_IRQ(27)
#define IRQ_UART0      S3C2410_IRQ(28)	    /* 44 */
#define IRQ_SPI1       S3C2410_IRQ(29)
#define IRQ_RTC	       S3C2410_IRQ(30)
#define IRQ_ADCPARENT  S3C2410_IRQ(31)

/* interrupts generated from the external interrupts sources */
#define IRQ_EINT0_2412 S3C2410_IRQ(32)
#define IRQ_EINT1_2412 S3C2410_IRQ(33)
#define IRQ_EINT2_2412 S3C2410_IRQ(34)
#define IRQ_EINT3_2412 S3C2410_IRQ(35)
#define IRQ_EINT4      S3C2410_IRQ(36)	   /* 52 */
#define IRQ_EINT5      S3C2410_IRQ(37)
#define IRQ_EINT6      S3C2410_IRQ(38)
#define IRQ_EINT7      S3C2410_IRQ(39)
#define IRQ_EINT8      S3C2410_IRQ(40)
#define IRQ_EINT9      S3C2410_IRQ(41)
#define IRQ_EINT10     S3C2410_IRQ(42)
#define IRQ_EINT11     S3C2410_IRQ(43)
#define IRQ_EINT12     S3C2410_IRQ(44)
#define IRQ_EINT13     S3C2410_IRQ(45)
#define IRQ_EINT14     S3C2410_IRQ(46)
#define IRQ_EINT15     S3C2410_IRQ(47)
#define IRQ_EINT16     S3C2410_IRQ(48)
#define IRQ_EINT17     S3C2410_IRQ(49)
#define IRQ_EINT18     S3C2410_IRQ(50)
#define IRQ_EINT19     S3C2410_IRQ(51)
#define IRQ_EINT20     S3C2410_IRQ(52)	   /* 68 */
#define IRQ_EINT21     S3C2410_IRQ(53)
#define IRQ_EINT22     S3C2410_IRQ(54)
#define IRQ_EINT23     S3C2410_IRQ(55)

#define IRQ_EINT_BIT(x)	((x) - IRQ_EINT4 + 4)
#define IRQ_EINT(x)    (((x) >= 4) ? (IRQ_EINT4 + (x) - 4) : (IRQ_EINT0 + (x)))

#define IRQ_LCD_FIFO   S3C2410_IRQ(56)
#define IRQ_LCD_FRAME  S3C2410_IRQ(57)

/* IRQs for the interal UARTs, and ADC
 * these need to be ordered in number of appearance in the
 * SUBSRC mask register
*/

#define S3C2410_IRQSUB(x)	S3C2410_IRQ((x)+58)

#define IRQ_S3CUART_RX0		S3C2410_IRQSUB(0)	/* 74 */
#define IRQ_S3CUART_TX0		S3C2410_IRQSUB(1)
#define IRQ_S3CUART_ERR0	S3C2410_IRQSUB(2)

#define IRQ_S3CUART_RX1		S3C2410_IRQSUB(3)	/* 77 */
#define IRQ_S3CUART_TX1		S3C2410_IRQSUB(4)
#define IRQ_S3CUART_ERR1	S3C2410_IRQSUB(5)

#define IRQ_S3CUART_RX2		S3C2410_IRQSUB(6)	/* 80 */
#define IRQ_S3CUART_TX2		S3C2410_IRQSUB(7)
#define IRQ_S3CUART_ERR2	S3C2410_IRQSUB(8)

#define IRQ_TC			S3C2410_IRQSUB(9)
#define IRQ_ADC			S3C2410_IRQSUB(10)

#define NR_IRQS_S3C2410		(S3C2410_IRQSUB(10) + 1)

/* extra irqs for s3c2412 */

#define IRQ_S3C2412_CFSDI	S3C2410_IRQ(21)

#define IRQ_S3C2412_SDI		S3C2410_IRQSUB(13)
#define IRQ_S3C2412_CF		S3C2410_IRQSUB(14)

#define NR_IRQS_S3C2412		(S3C2410_IRQSUB(14) + 1)

#define IRQ_S3C2416_EINT8t15	S3C2410_IRQ(5)
#define IRQ_S3C2416_DMA		S3C2410_IRQ(17)
#define IRQ_S3C2416_UART3	S3C2410_IRQ(18)
#define IRQ_S3C2416_SDI1	S3C2410_IRQ(20)
#define IRQ_S3C2416_SDI0	S3C2410_IRQ(21)

#define IRQ_S3C2416_LCD2	S3C2410_IRQSUB(15)
#define IRQ_S3C2416_LCD3	S3C2410_IRQSUB(16)
#define IRQ_S3C2416_LCD4	S3C2410_IRQSUB(17)
#define IRQ_S3C2416_DMA0	S3C2410_IRQSUB(18)
#define IRQ_S3C2416_DMA1	S3C2410_IRQSUB(19)
#define IRQ_S3C2416_DMA2	S3C2410_IRQSUB(20)
#define IRQ_S3C2416_DMA3	S3C2410_IRQSUB(21)
#define IRQ_S3C2416_DMA4	S3C2410_IRQSUB(22)
#define IRQ_S3C2416_DMA5	S3C2410_IRQSUB(23)
#define IRQ_S32416_WDT		S3C2410_IRQSUB(27)
#define IRQ_S32416_AC97		S3C2410_IRQSUB(28)

/* second interrupt-register of s3c2416/s3c2450 */

#define S3C2416_IRQ(x)		S3C2410_IRQ((x) + 58 + 29)
#define IRQ_S3C2416_2D		S3C2416_IRQ(0)
#define IRQ_S3C2416_IIC1	S3C2416_IRQ(1)
#define IRQ_S3C2416_RESERVED2	S3C2416_IRQ(2)
#define IRQ_S3C2416_RESERVED3	S3C2416_IRQ(3)
#define IRQ_S3C2416_PCM0	S3C2416_IRQ(4)
#define IRQ_S3C2416_PCM1	S3C2416_IRQ(5)
#define IRQ_S3C2416_I2S0	S3C2416_IRQ(6)
#define IRQ_S3C2416_I2S1	S3C2416_IRQ(7)

#define NR_IRQS_S3C2416		(S3C2416_IRQ(7) + 1)

/* extra irqs for s3c2440/s3c2442 */

#define IRQ_S3C2440_CAM_C	S3C2410_IRQSUB(11)	/* S3C2443 too */
#define IRQ_S3C2440_CAM_P	S3C2410_IRQSUB(12)	/* S3C2443 too */

#define NR_IRQS_S3C2442		(S3C2410_IRQSUB(12) + 1)

#define IRQ_S3C2440_WDT		S3C2410_IRQSUB(13)
#define IRQ_S3C2440_AC97	S3C2410_IRQSUB(14)

#define NR_IRQS_S3C2440		(S3C2410_IRQSUB(14) + 1)

/* irqs for s3c2443 */

#define IRQ_S3C2443_DMA		S3C2410_IRQ(17)		/* IRQ_DMA1 */
#define IRQ_S3C2443_UART3	S3C2410_IRQ(18)		/* IRQ_DMA2 */
#define IRQ_S3C2443_CFCON	S3C2410_IRQ(19)		/* IRQ_DMA3 */
#define IRQ_S3C2443_HSMMC	S3C2410_IRQ(20)		/* IRQ_SDI */
#define IRQ_S3C2443_NAND	S3C2410_IRQ(24)		/* reserved */

#define IRQ_S3C2416_HSMMC0	S3C2410_IRQ(21)		/* S3C2416/S3C2450 */

#define IRQ_HSMMC0		IRQ_S3C2416_HSMMC0
#define IRQ_HSMMC1		IRQ_S3C2443_HSMMC

#define IRQ_S3C2443_LCD1	S3C2410_IRQSUB(14)
#define IRQ_S3C2443_LCD2	S3C2410_IRQSUB(15)
#define IRQ_S3C2443_LCD3	S3C2410_IRQSUB(16)
#define IRQ_S3C2443_LCD4	S3C2410_IRQSUB(17)

#define IRQ_S3C2443_DMA0	S3C2410_IRQSUB(18)
#define IRQ_S3C2443_DMA1	S3C2410_IRQSUB(19)
#define IRQ_S3C2443_DMA2	S3C2410_IRQSUB(20)
#define IRQ_S3C2443_DMA3	S3C2410_IRQSUB(21)
#define IRQ_S3C2443_DMA4	S3C2410_IRQSUB(22)
#define IRQ_S3C2443_DMA5	S3C2410_IRQSUB(23)

/* UART3 */
#define IRQ_S3C2443_RX3		S3C2410_IRQSUB(24)
#define IRQ_S3C2443_TX3		S3C2410_IRQSUB(25)
#define IRQ_S3C2443_ERR3	S3C2410_IRQSUB(26)

#define IRQ_S3C2443_WDT		S3C2410_IRQSUB(27)
#define IRQ_S3C2443_AC97	S3C2410_IRQSUB(28)

#define NR_IRQS_S3C2443		(S3C2410_IRQSUB(28) + 1)

/* compatibility define. */
#define IRQ_UART3		IRQ_S3C2443_UART3
#define IRQ_S3CUART_RX3		IRQ_S3C2443_RX3
#define IRQ_S3CUART_TX3		IRQ_S3C2443_TX3
#define IRQ_S3CUART_ERR3	IRQ_S3C2443_ERR3

#define IRQ_LCD_VSYNC		IRQ_S3C2443_LCD3
#define IRQ_LCD_SYSTEM		IRQ_S3C2443_LCD2

#ifdef CONFIG_CPU_S3C2440
#define IRQ_S3C244X_AC97 IRQ_S3C2440_AC97
#else
#define IRQ_S3C244X_AC97 IRQ_S3C2443_AC97
#endif

/* Our FIQs are routable from IRQ_EINT0 to IRQ_ADCPARENT */
#define FIQ_START		IRQ_EINT0

#endif /* __ASM_ARCH_IRQ_H */


code:
/* SPDX-License-Identifier: GPL-2.0 */
/* linux/arch/arm/mach-s3c64xx/include/mach/irqs.h
 *
 * Copyright 2008 Openmoko, Inc.
 * Copyright 2008 Simtec Electronics
 *      Ben Dooks <ben@simtec.co.uk>
 *      http://armlinux.simtec.co.uk/
 *
 * S3C64XX - IRQ support
 */

#ifndef __ASM_MACH_S3C64XX_IRQS_H
#define __ASM_MACH_S3C64XX_IRQS_H __FILE__

/* we keep the first set of CPU IRQs out of the range of
 * the ISA space, so that the PC104 has them to itself
 * and we don't end up having to do horrible things to the
 * standard ISA drivers....
 *
 * note, since we're using the VICs, our start must be a
 * mulitple of 32 to allow the common code to work
 */

#define S3C_IRQ_OFFSET	(32)

#define S3C_IRQ(x)	((x) + S3C_IRQ_OFFSET)

#define IRQ_VIC0_BASE	S3C_IRQ(0)
#define IRQ_VIC1_BASE	S3C_IRQ(32)

/* VIC based IRQs */

#define S3C64XX_IRQ_VIC0(x)	(IRQ_VIC0_BASE + (x))
#define S3C64XX_IRQ_VIC1(x)	(IRQ_VIC1_BASE + (x))

/* VIC0 */

#define IRQ_EINT0_3		S3C64XX_IRQ_VIC0(0)
#define IRQ_EINT4_11		S3C64XX_IRQ_VIC0(1)
#define IRQ_RTC_TIC		S3C64XX_IRQ_VIC0(2)
#define IRQ_CAMIF_C		S3C64XX_IRQ_VIC0(3)
#define IRQ_CAMIF_P		S3C64XX_IRQ_VIC0(4)
#define IRQ_CAMIF_MC		S3C64XX_IRQ_VIC0(5)
#define IRQ_S3C6410_IIC1	S3C64XX_IRQ_VIC0(5)
#define IRQ_S3C6410_IIS		S3C64XX_IRQ_VIC0(6)
#define IRQ_S3C6400_CAMIF_MP	S3C64XX_IRQ_VIC0(6)
#define IRQ_CAMIF_WE_C		S3C64XX_IRQ_VIC0(7)
#define IRQ_S3C6410_G3D		S3C64XX_IRQ_VIC0(8)
#define IRQ_S3C6400_CAMIF_WE_P	S3C64XX_IRQ_VIC0(8)
#define IRQ_POST0		S3C64XX_IRQ_VIC0(9)
#define IRQ_ROTATOR		S3C64XX_IRQ_VIC0(10)
#define IRQ_2D			S3C64XX_IRQ_VIC0(11)
#define IRQ_TVENC		S3C64XX_IRQ_VIC0(12)
#define IRQ_SCALER		S3C64XX_IRQ_VIC0(13)
#define IRQ_BATF		S3C64XX_IRQ_VIC0(14)
#define IRQ_JPEG		S3C64XX_IRQ_VIC0(15)
#define IRQ_MFC			S3C64XX_IRQ_VIC0(16)
#define IRQ_SDMA0		S3C64XX_IRQ_VIC0(17)
#define IRQ_SDMA1		S3C64XX_IRQ_VIC0(18)
#define IRQ_ARM_DMAERR		S3C64XX_IRQ_VIC0(19)
#define IRQ_ARM_DMA		S3C64XX_IRQ_VIC0(20)
#define IRQ_ARM_DMAS		S3C64XX_IRQ_VIC0(21)
#define IRQ_KEYPAD		S3C64XX_IRQ_VIC0(22)
#define IRQ_TIMER0_VIC		S3C64XX_IRQ_VIC0(23)
#define IRQ_TIMER1_VIC		S3C64XX_IRQ_VIC0(24)
#define IRQ_TIMER2_VIC		S3C64XX_IRQ_VIC0(25)
#define IRQ_WDT			S3C64XX_IRQ_VIC0(26)
#define IRQ_TIMER3_VIC		S3C64XX_IRQ_VIC0(27)
#define IRQ_TIMER4_VIC		S3C64XX_IRQ_VIC0(28)
#define IRQ_LCD_FIFO		S3C64XX_IRQ_VIC0(29)
#define IRQ_LCD_VSYNC		S3C64XX_IRQ_VIC0(30)
#define IRQ_LCD_SYSTEM		S3C64XX_IRQ_VIC0(31)

/* VIC1 */

#define IRQ_EINT12_19		S3C64XX_IRQ_VIC1(0)
#define IRQ_EINT20_27		S3C64XX_IRQ_VIC1(1)
#define IRQ_PCM0		S3C64XX_IRQ_VIC1(2)
#define IRQ_PCM1		S3C64XX_IRQ_VIC1(3)
#define IRQ_AC97		S3C64XX_IRQ_VIC1(4)
#define IRQ_UART0		S3C64XX_IRQ_VIC1(5)
#define IRQ_UART1		S3C64XX_IRQ_VIC1(6)
#define IRQ_UART2		S3C64XX_IRQ_VIC1(7)
#define IRQ_UART3		S3C64XX_IRQ_VIC1(8)
#define IRQ_DMA0		S3C64XX_IRQ_VIC1(9)
#define IRQ_DMA1		S3C64XX_IRQ_VIC1(10)
#define IRQ_ONENAND0		S3C64XX_IRQ_VIC1(11)
#define IRQ_ONENAND1		S3C64XX_IRQ_VIC1(12)
#define IRQ_NFC			S3C64XX_IRQ_VIC1(13)
#define IRQ_CFCON		S3C64XX_IRQ_VIC1(14)
#define IRQ_USBH		S3C64XX_IRQ_VIC1(15)
#define IRQ_SPI0		S3C64XX_IRQ_VIC1(16)
#define IRQ_SPI1		S3C64XX_IRQ_VIC1(17)
#define IRQ_IIC			S3C64XX_IRQ_VIC1(18)
#define IRQ_HSItx		S3C64XX_IRQ_VIC1(19)
#define IRQ_HSIrx		S3C64XX_IRQ_VIC1(20)
#define IRQ_RESERVED		S3C64XX_IRQ_VIC1(21)
#define IRQ_MSM			S3C64XX_IRQ_VIC1(22)
#define IRQ_HOSTIF		S3C64XX_IRQ_VIC1(23)
#define IRQ_HSMMC0		S3C64XX_IRQ_VIC1(24)
#define IRQ_HSMMC1		S3C64XX_IRQ_VIC1(25)
#define IRQ_HSMMC2		IRQ_SPI1	/* shared with SPI1 */
#define IRQ_OTG			S3C64XX_IRQ_VIC1(26)
#define IRQ_IRDA		S3C64XX_IRQ_VIC1(27)
#define IRQ_RTC_ALARM		S3C64XX_IRQ_VIC1(28)
#define IRQ_SEC			S3C64XX_IRQ_VIC1(29)
#define IRQ_PENDN		S3C64XX_IRQ_VIC1(30)
#define IRQ_TC			IRQ_PENDN
#define IRQ_ADC			S3C64XX_IRQ_VIC1(31)

/* compatibility for device defines */

#define IRQ_IIC1		IRQ_S3C6410_IIC1

/* Since the IRQ_EINT(x) are a linear mapping on current s3c64xx series
 * we just defined them as an IRQ_EINT(x) macro from S3C_IRQ_EINT_BASE
 * which we place after the pair of VICs. */

#define S3C_IRQ_EINT_BASE	S3C_IRQ(64+5)

#define S3C_EINT(x)		((x) + S3C_IRQ_EINT_BASE)
#define IRQ_EINT(x)		S3C_EINT(x)
#define IRQ_EINT_BIT(x)		((x) - S3C_EINT(0))

/* Next the external interrupt groups. These are similar to the IRQ_EINT(x)
 * that they are sourced from the GPIO pins but with a different scheme for
 * priority and source indication.
 *
 * The IRQ_EINT(x) can be thought of as 'group 0' of the available GPIO
 * interrupts, but for historical reasons they are kept apart from these
 * next interrupts.
 *
 * Use IRQ_EINT_GROUP(group, offset) to get the number for use in the
 * machine specific support files.
 */

#define IRQ_EINT_GROUP1_NR	(15)
#define IRQ_EINT_GROUP2_NR	(8)
#define IRQ_EINT_GROUP3_NR	(5)
#define IRQ_EINT_GROUP4_NR	(14)
#define IRQ_EINT_GROUP5_NR	(7)
#define IRQ_EINT_GROUP6_NR	(10)
#define IRQ_EINT_GROUP7_NR	(16)
#define IRQ_EINT_GROUP8_NR	(15)
#define IRQ_EINT_GROUP9_NR	(9)

#define IRQ_EINT_GROUP_BASE	S3C_EINT(28)
#define IRQ_EINT_GROUP1_BASE	(IRQ_EINT_GROUP_BASE + 0x00)
#define IRQ_EINT_GROUP2_BASE	(IRQ_EINT_GROUP1_BASE + IRQ_EINT_GROUP1_NR)
#define IRQ_EINT_GROUP3_BASE	(IRQ_EINT_GROUP2_BASE + IRQ_EINT_GROUP2_NR)
#define IRQ_EINT_GROUP4_BASE	(IRQ_EINT_GROUP3_BASE + IRQ_EINT_GROUP3_NR)
#define IRQ_EINT_GROUP5_BASE	(IRQ_EINT_GROUP4_BASE + IRQ_EINT_GROUP4_NR)
#define IRQ_EINT_GROUP6_BASE	(IRQ_EINT_GROUP5_BASE + IRQ_EINT_GROUP5_NR)
#define IRQ_EINT_GROUP7_BASE	(IRQ_EINT_GROUP6_BASE + IRQ_EINT_GROUP6_NR)
#define IRQ_EINT_GROUP8_BASE	(IRQ_EINT_GROUP7_BASE + IRQ_EINT_GROUP7_NR)
#define IRQ_EINT_GROUP9_BASE	(IRQ_EINT_GROUP8_BASE + IRQ_EINT_GROUP8_NR)

#define IRQ_EINT_GROUP(group, no)	(IRQ_EINT_GROUP##group##_BASE + (no))

/* Some boards have their own IRQs behind this */
#define IRQ_BOARD_START (IRQ_EINT_GROUP9_BASE + IRQ_EINT_GROUP9_NR + 1)

/* Set the default nr_irqs, boards can override if necessary */
#define S3C64XX_NR_IRQS	IRQ_BOARD_START

/* Compatibility */

#define IRQ_ONENAND	IRQ_ONENAND0
#define IRQ_I2S0	IRQ_S3C6410_IIS

#endif /* __ASM_MACH_S3C64XX_IRQS_H */


code:
/* SPDX-License-Identifier: GPL-2.0 */

#ifdef CONFIG_ARCH_S3C24XX
#include "irqs-s3c24xx.h"
#endif

#ifdef CONFIG_ARCH_S3C64XX
#include "irqs-s3c64xx.h"
#endif


code:
/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * Samsung Platform - Keypad platform data definitions
 *
 * Copyright (C) 2010 Samsung Electronics Co.Ltd
 * Author: Joonyoung Shim <jy0922.shim@samsung.com>
 */

#ifndef __PLAT_SAMSUNG_KEYPAD_H
#define __PLAT_SAMSUNG_KEYPAD_H

#include <linux/input/samsung-keypad.h>

/**
 * samsung_keypad_set_platdata - Set platform data for Samsung Keypad device.
 * @pd: Platform data to register to device.
 *
 * Register the given platform data for use with Samsung Keypad device.
 * The call will copy the platform data, so the board definitions can
 * make the structure itself __initdata.
 */
extern void samsung_keypad_set_platdata(struct samsung_keypad_platdata *pd);

/* defined by architecture to configure gpio. */
extern void samsung_keypad_cfg_gpio(unsigned int rows, unsigned int cols);

#endif /* __PLAT_SAMSUNG_KEYPAD_H */


code:
// SPDX-License-Identifier: GPL-2.0+
//
// Copyright (c) 2006 American Microsystems Limited
//	David Anders <danders@amltd.com>
//
// @History:
// derived from linux/arch/arm/mach-s3c2410/mach-bast.c, written by
// Ben Dooks <ben@simtec.co.uk>

#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/gpio/machine.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/serial_core.h>
#include <linux/serial_s3c.h>
#include <linux/io.h>

#include <asm/mach/arch.h>
#include <asm/mach/map.h>
#include <asm/mach/irq.h>
#include <asm/mach/flash.h>

#include <asm/irq.h>
#include <asm/mach-types.h>
#include <linux/platform_data/fb-s3c2410.h>

#include "regs-gpio.h"
#include "gpio-samsung.h"

#include <linux/platform_data/i2c-s3c2410.h>
#include "devs.h"
#include "cpu.h"
#include "gpio-cfg.h"

#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/map.h>
#include <linux/mtd/physmap.h>

#include "s3c24xx.h"

static struct resource amlm5900_nor_resource =
			DEFINE_RES_MEM(0x00000000, SZ_16M);

static struct mtd_partition amlm5900_mtd_partitions[] = {
	{
		.name		= "System",
		.size		= 0x240000,
		.offset		= 0,
		.mask_flags 	= MTD_WRITEABLE,  /* force read-only */
	}, {
		.name		= "Kernel",
		.size		= 0x100000,
		.offset		= MTDPART_OFS_APPEND,
	}, {
		.name		= "Ramdisk",
		.size		= 0x300000,
		.offset		= MTDPART_OFS_APPEND,
	}, {
		.name		= "JFFS2",
		.size		= 0x9A0000,
		.offset		= MTDPART_OFS_APPEND,
	}, {
		.name		= "Settings",
		.size		= MTDPART_SIZ_FULL,
		.offset		= MTDPART_OFS_APPEND,
	}
};

static struct physmap_flash_data amlm5900_flash_data = {
	.width		= 2,
	.parts		= amlm5900_mtd_partitions,
	.nr_parts	= ARRAY_SIZE(amlm5900_mtd_partitions),
};

static struct platform_device amlm5900_device_nor = {
	.name		= "physmap-flash",
	.id		= 0,
	.dev = {
			.platform_data = &amlm5900_flash_data,
		},
	.num_resources	= 1,
	.resource	= &amlm5900_nor_resource,
};

static struct map_desc amlm5900_iodesc[] __initdata = {
};

#define UCON S3C2410_UCON_DEFAULT
#define ULCON S3C2410_LCON_CS8 | S3C2410_LCON_PNONE | S3C2410_LCON_STOPB
#define UFCON S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE

static struct s3c2410_uartcfg amlm5900_uartcfgs[] = {
	[0] = {
		.hwport	     = 0,
		.flags	     = 0,
		.ucon	     = UCON,
		.ulcon	     = ULCON,
		.ufcon	     = UFCON,
	},
	[1] = {
		.hwport	     = 1,
		.flags	     = 0,
		.ucon	     = UCON,
		.ulcon	     = ULCON,
		.ufcon	     = UFCON,
	},
	[2] = {
		.hwport	     = 2,
		.flags	     = 0,
		.ucon	     = UCON,
		.ulcon	     = ULCON,
		.ufcon	     = UFCON,
	}
};

static struct gpiod_lookup_table amlm5900_mmc_gpio_table = {
	.dev_id = "s3c2410-sdi",
	.table = {
		/* bus pins */
		GPIO_LOOKUP_IDX("GPIOE",  5, "bus", 0, GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP_IDX("GPIOE",  6, "bus", 1, GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP_IDX("GPIOE",  7, "bus", 2, GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP_IDX("GPIOE",  8, "bus", 3, GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP_IDX("GPIOE",  9, "bus", 4, GPIO_ACTIVE_HIGH),
		GPIO_LOOKUP_IDX("GPIOE", 10, "bus", 5, GPIO_ACTIVE_HIGH),
		{ },
	},
};

static struct platform_device *amlm5900_devices[] __initdata = {
#ifdef CONFIG_FB_S3C2410
	&s3c_device_lcd,
#endif
	&s3c_device_adc,
	&s3c_device_wdt,
	&s3c_device_i2c0,
	&s3c_device_ohci,
 	&s3c_device_rtc,
	&s3c_device_usbgadget,
        &s3c_device_sdi,
	&amlm5900_device_nor,
};

static void __init amlm5900_map_io(void)
{
	s3c24xx_init_io(amlm5900_iodesc, ARRAY_SIZE(amlm5900_iodesc));
	s3c24xx_init_uarts(amlm5900_uartcfgs, ARRAY_SIZE(amlm5900_uartcfgs));
	s3c24xx_set_timer_source(S3C24XX_PWM3, S3C24XX_PWM4);
}

static void __init amlm5900_init_time(void)
{
	s3c2410_init_clocks(12000000);
	s3c24xx_timer_init();
}

#ifdef CONFIG_FB_S3C2410
static struct s3c2410fb_display __initdata amlm5900_lcd_info = {
	.width		= 160,
	.height		= 160,

	.type		= S3C2410_LCDCON1_STN4,

	.pixclock	= 680000, /* HCLK = 100MHz */
	.xres		= 160,
	.yres		= 160,
	.bpp		= 4,
	.left_margin	= 1 << (4 + 3),
	.right_margin	= 8 << 3,
	.hsync_len	= 48,
	.upper_margin	= 0,
	.lower_margin	= 0,

	.lcdcon5	= 0x00000001,
};

static struct s3c2410fb_mach_info __initdata amlm5900_fb_info = {

	.displays = &amlm5900_lcd_info,
	.num_displays = 1,
	.default_display = 0,

	.gpccon =	0xaaaaaaaa,
	.gpccon_mask =	0xffffffff,
	.gpccon_reg =	S3C2410_GPCCON,
	.gpcup =	0x0000ffff,
	.gpcup_mask =	0xffffffff,
	.gpcup_reg =	S3C2410_GPCUP,

	.gpdcon =	0xaaaaaaaa,
	.gpdcon_mask =	0xffffffff,
	.gpdcon_reg =	S3C2410_GPDCON,
	.gpdup =	0x0000ffff,
	.gpdup_mask =	0xffffffff,
	.gpdup_reg =	S3C2410_GPDUP,
};
#endif

static irqreturn_t
amlm5900_wake_interrupt(int irq, void *ignored)
{
	return IRQ_HANDLED;
}

static void amlm5900_init_pm(void)
{
	int ret = 0;

	ret = request_irq(IRQ_EINT9, &amlm5900_wake_interrupt,
				IRQF_TRIGGER_RISING | IRQF_SHARED,
				"amlm5900_wakeup", &amlm5900_wake_interrupt);
	if (ret != 0) {
		printk(KERN_ERR "AML-M5900: no wakeup irq, %d?\n", ret);
	} else {
		enable_irq_wake(IRQ_EINT9);
		/* configure the suspend/resume status pin */
		s3c_gpio_cfgpin(S3C2410_GPF(2), S3C2410_GPIO_OUTPUT);
		s3c_gpio_setpull(S3C2410_GPF(2), S3C_GPIO_PULL_UP);
	}
}
static void __init amlm5900_init(void)
{
	amlm5900_init_pm();
#ifdef CONFIG_FB_S3C2410
	s3c24xx_fb_set_platdata(&amlm5900_fb_info);
#endif
	s3c_i2c0_set_platdata(NULL);
	gpiod_add_lookup_table(&amlm5900_mmc_gpio_table);
	platform_add_devices(amlm5900_devices, ARRAY_SIZE(amlm5900_devices));
}

MACHINE_START(AML_M5900, "AML_M5900")
	.atag_offset	= 0x100,
	.nr_irqs	= NR_IRQS_S3C2410,
	.map_io		= amlm5900_map_io,
	.nr_irqs	= NR_IRQS_S3C2410,
	.init_irq	= s3c2410_init_irq,
	.init_machine	= amlm5900_init,
	.init_time	= amlm5900_init_time,
MACHINE_END