162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// Copyright (c) 2011 Samsung Electronics Co., Ltd. 462306a36Sopenharmony_ci// http://www.samsung.com 562306a36Sopenharmony_ci// 662306a36Sopenharmony_ci// Copyright 2008 Openmoko, Inc. 762306a36Sopenharmony_ci// Copyright 2008 Simtec Electronics 862306a36Sopenharmony_ci// Ben Dooks <ben@simtec.co.uk> 962306a36Sopenharmony_ci// http://armlinux.simtec.co.uk/ 1062306a36Sopenharmony_ci// 1162306a36Sopenharmony_ci// Common Codes for S3C64XX machines 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * NOTE: Code in this file is not used when booting with Device Tree support. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/init.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/interrupt.h> 2162306a36Sopenharmony_ci#include <linux/ioport.h> 2262306a36Sopenharmony_ci#include <linux/serial_core.h> 2362306a36Sopenharmony_ci#include <linux/serial_s3c.h> 2462306a36Sopenharmony_ci#include <linux/of.h> 2562306a36Sopenharmony_ci#include <linux/platform_device.h> 2662306a36Sopenharmony_ci#include <linux/reboot.h> 2762306a36Sopenharmony_ci#include <linux/io.h> 2862306a36Sopenharmony_ci#include <linux/clk/samsung.h> 2962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 3062306a36Sopenharmony_ci#include <linux/irq.h> 3162306a36Sopenharmony_ci#include <linux/irqchip/arm-vic.h> 3262306a36Sopenharmony_ci#include <clocksource/samsung_pwm.h> 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <asm/mach/arch.h> 3562306a36Sopenharmony_ci#include <asm/mach/map.h> 3662306a36Sopenharmony_ci#include <asm/system_misc.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "map.h" 3962306a36Sopenharmony_ci#include "irqs.h" 4062306a36Sopenharmony_ci#include "regs-gpio.h" 4162306a36Sopenharmony_ci#include "gpio-samsung.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#include "cpu.h" 4462306a36Sopenharmony_ci#include "devs.h" 4562306a36Sopenharmony_ci#include "pm.h" 4662306a36Sopenharmony_ci#include "gpio-cfg.h" 4762306a36Sopenharmony_ci#include "pwm-core.h" 4862306a36Sopenharmony_ci#include "regs-irqtype.h" 4962306a36Sopenharmony_ci#include "s3c64xx.h" 5062306a36Sopenharmony_ci#include "irq-uart-s3c64xx.h" 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* External clock frequency */ 5362306a36Sopenharmony_cistatic unsigned long xtal_f __ro_after_init = 12000000; 5462306a36Sopenharmony_cistatic unsigned long xusbxti_f __ro_after_init = 48000000; 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_civoid __init s3c64xx_set_xtal_freq(unsigned long freq) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci xtal_f = freq; 5962306a36Sopenharmony_ci} 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_civoid __init s3c64xx_set_xusbxti_freq(unsigned long freq) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci xusbxti_f = freq; 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* uart registration process */ 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void __init s3c64xx_init_uarts(struct s3c2410_uartcfg *cfg, int no) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci s3c24xx_init_uartdevs("s3c6400-uart", s3c64xx_uart_resources, cfg, no); 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* table of supported CPUs */ 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic const char name_s3c6410[] = "S3C6410"; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic struct cpu_table cpu_ids[] __initdata = { 7862306a36Sopenharmony_ci { 7962306a36Sopenharmony_ci .idcode = S3C6410_CPU_ID, 8062306a36Sopenharmony_ci .idmask = S3C64XX_CPU_MASK, 8162306a36Sopenharmony_ci .map_io = s3c6410_map_io, 8262306a36Sopenharmony_ci .init_uarts = s3c64xx_init_uarts, 8362306a36Sopenharmony_ci .init = s3c6410_init, 8462306a36Sopenharmony_ci .name = name_s3c6410, 8562306a36Sopenharmony_ci }, 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/* minimal IO mapping */ 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* 9162306a36Sopenharmony_ci * note, for the boot process to work we have to keep the UART 9262306a36Sopenharmony_ci * virtual address aligned to an 1MiB boundary for the L1 9362306a36Sopenharmony_ci * mapping the head code makes. We keep the UART virtual address 9462306a36Sopenharmony_ci * aligned and add in the offset when we load the value here. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci#define UART_OFFS (S3C_PA_UART & 0xfffff) 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_cistatic struct map_desc s3c_iodesc[] __initdata = { 9962306a36Sopenharmony_ci { 10062306a36Sopenharmony_ci .virtual = (unsigned long)S3C_VA_SYS, 10162306a36Sopenharmony_ci .pfn = __phys_to_pfn(S3C64XX_PA_SYSCON), 10262306a36Sopenharmony_ci .length = SZ_4K, 10362306a36Sopenharmony_ci .type = MT_DEVICE, 10462306a36Sopenharmony_ci }, { 10562306a36Sopenharmony_ci .virtual = (unsigned long)S3C_VA_MEM, 10662306a36Sopenharmony_ci .pfn = __phys_to_pfn(S3C64XX_PA_SROM), 10762306a36Sopenharmony_ci .length = SZ_4K, 10862306a36Sopenharmony_ci .type = MT_DEVICE, 10962306a36Sopenharmony_ci }, { 11062306a36Sopenharmony_ci .virtual = (unsigned long)(S3C_VA_UART + UART_OFFS), 11162306a36Sopenharmony_ci .pfn = __phys_to_pfn(S3C_PA_UART), 11262306a36Sopenharmony_ci .length = SZ_4K, 11362306a36Sopenharmony_ci .type = MT_DEVICE, 11462306a36Sopenharmony_ci }, { 11562306a36Sopenharmony_ci .virtual = (unsigned long)VA_VIC0, 11662306a36Sopenharmony_ci .pfn = __phys_to_pfn(S3C64XX_PA_VIC0), 11762306a36Sopenharmony_ci .length = SZ_16K, 11862306a36Sopenharmony_ci .type = MT_DEVICE, 11962306a36Sopenharmony_ci }, { 12062306a36Sopenharmony_ci .virtual = (unsigned long)VA_VIC1, 12162306a36Sopenharmony_ci .pfn = __phys_to_pfn(S3C64XX_PA_VIC1), 12262306a36Sopenharmony_ci .length = SZ_16K, 12362306a36Sopenharmony_ci .type = MT_DEVICE, 12462306a36Sopenharmony_ci }, { 12562306a36Sopenharmony_ci .virtual = (unsigned long)S3C_VA_TIMER, 12662306a36Sopenharmony_ci .pfn = __phys_to_pfn(S3C_PA_TIMER), 12762306a36Sopenharmony_ci .length = SZ_16K, 12862306a36Sopenharmony_ci .type = MT_DEVICE, 12962306a36Sopenharmony_ci }, { 13062306a36Sopenharmony_ci .virtual = (unsigned long)S3C64XX_VA_GPIO, 13162306a36Sopenharmony_ci .pfn = __phys_to_pfn(S3C64XX_PA_GPIO), 13262306a36Sopenharmony_ci .length = SZ_4K, 13362306a36Sopenharmony_ci .type = MT_DEVICE, 13462306a36Sopenharmony_ci }, { 13562306a36Sopenharmony_ci .virtual = (unsigned long)S3C64XX_VA_MODEM, 13662306a36Sopenharmony_ci .pfn = __phys_to_pfn(S3C64XX_PA_MODEM), 13762306a36Sopenharmony_ci .length = SZ_4K, 13862306a36Sopenharmony_ci .type = MT_DEVICE, 13962306a36Sopenharmony_ci }, { 14062306a36Sopenharmony_ci .virtual = (unsigned long)S3C_VA_WATCHDOG, 14162306a36Sopenharmony_ci .pfn = __phys_to_pfn(S3C64XX_PA_WATCHDOG), 14262306a36Sopenharmony_ci .length = SZ_4K, 14362306a36Sopenharmony_ci .type = MT_DEVICE, 14462306a36Sopenharmony_ci }, { 14562306a36Sopenharmony_ci .virtual = (unsigned long)S3C_VA_USB_HSPHY, 14662306a36Sopenharmony_ci .pfn = __phys_to_pfn(S3C64XX_PA_USB_HSPHY), 14762306a36Sopenharmony_ci .length = SZ_1K, 14862306a36Sopenharmony_ci .type = MT_DEVICE, 14962306a36Sopenharmony_ci }, 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic struct bus_type s3c64xx_subsys = { 15362306a36Sopenharmony_ci .name = "s3c64xx-core", 15462306a36Sopenharmony_ci .dev_name = "s3c64xx-core", 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic struct device s3c64xx_dev = { 15862306a36Sopenharmony_ci .bus = &s3c64xx_subsys, 15962306a36Sopenharmony_ci}; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_cistatic struct samsung_pwm_variant s3c64xx_pwm_variant = { 16262306a36Sopenharmony_ci .bits = 32, 16362306a36Sopenharmony_ci .div_base = 0, 16462306a36Sopenharmony_ci .has_tint_cstat = true, 16562306a36Sopenharmony_ci .tclk_mask = (1 << 7) | (1 << 6) | (1 << 5), 16662306a36Sopenharmony_ci}; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_civoid __init s3c64xx_set_timer_source(enum s3c64xx_timer_mode event, 16962306a36Sopenharmony_ci enum s3c64xx_timer_mode source) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci s3c64xx_pwm_variant.output_mask = BIT(SAMSUNG_PWM_NUM) - 1; 17262306a36Sopenharmony_ci s3c64xx_pwm_variant.output_mask &= ~(BIT(event) | BIT(source)); 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_civoid __init s3c64xx_timer_init(void) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci unsigned int timer_irqs[SAMSUNG_PWM_NUM] = { 17862306a36Sopenharmony_ci IRQ_TIMER0_VIC, IRQ_TIMER1_VIC, IRQ_TIMER2_VIC, 17962306a36Sopenharmony_ci IRQ_TIMER3_VIC, IRQ_TIMER4_VIC, 18062306a36Sopenharmony_ci }; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci samsung_pwm_clocksource_init(S3C_VA_TIMER, 18362306a36Sopenharmony_ci timer_irqs, &s3c64xx_pwm_variant); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* read cpu identification code */ 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_civoid __init s3c64xx_init_io(struct map_desc *mach_desc, int size) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci /* initialise the io descriptors we need for initialisation */ 19162306a36Sopenharmony_ci iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc)); 19262306a36Sopenharmony_ci iotable_init(mach_desc, size); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* detect cpu id */ 19562306a36Sopenharmony_ci s3c64xx_init_cpu(); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci s3c_init_cpu(samsung_cpu_id, cpu_ids, ARRAY_SIZE(cpu_ids)); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci samsung_pwm_set_platdata(&s3c64xx_pwm_variant); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic __init int s3c64xx_dev_init(void) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci /* Not applicable when using DT. */ 20562306a36Sopenharmony_ci if (of_have_populated_dt() || !soc_is_s3c64xx()) 20662306a36Sopenharmony_ci return 0; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci subsys_system_register(&s3c64xx_subsys, NULL); 20962306a36Sopenharmony_ci return device_register(&s3c64xx_dev); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_cicore_initcall(s3c64xx_dev_init); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci/* 21462306a36Sopenharmony_ci * setup the sources the vic should advertise resume 21562306a36Sopenharmony_ci * for, even though it is not doing the wake 21662306a36Sopenharmony_ci * (set_irq_wake needs to be valid) 21762306a36Sopenharmony_ci */ 21862306a36Sopenharmony_ci#define IRQ_VIC0_RESUME (1 << (IRQ_RTC_TIC - IRQ_VIC0_BASE)) 21962306a36Sopenharmony_ci#define IRQ_VIC1_RESUME (1 << (IRQ_RTC_ALARM - IRQ_VIC1_BASE) | \ 22062306a36Sopenharmony_ci 1 << (IRQ_PENDN - IRQ_VIC1_BASE) | \ 22162306a36Sopenharmony_ci 1 << (IRQ_HSMMC0 - IRQ_VIC1_BASE) | \ 22262306a36Sopenharmony_ci 1 << (IRQ_HSMMC1 - IRQ_VIC1_BASE) | \ 22362306a36Sopenharmony_ci 1 << (IRQ_HSMMC2 - IRQ_VIC1_BASE)) 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_civoid __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci s3c64xx_clk_init(NULL, xtal_f, xusbxti_f, soc_is_s3c6400(), S3C_VA_SYS); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci printk(KERN_DEBUG "%s: initialising interrupts\n", __func__); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* initialise the pair of VICs */ 23262306a36Sopenharmony_ci vic_init(VA_VIC0, IRQ_VIC0_BASE, vic0_valid, IRQ_VIC0_RESUME); 23362306a36Sopenharmony_ci vic_init(VA_VIC1, IRQ_VIC1_BASE, vic1_valid, IRQ_VIC1_RESUME); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci#define eint_offset(irq) ((irq) - IRQ_EINT(0)) 23762306a36Sopenharmony_ci#define eint_irq_to_bit(irq) ((u32)(1 << eint_offset(irq))) 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic inline void s3c_irq_eint_mask(struct irq_data *data) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci u32 mask; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci mask = __raw_readl(S3C64XX_EINT0MASK); 24462306a36Sopenharmony_ci mask |= (u32)data->chip_data; 24562306a36Sopenharmony_ci __raw_writel(mask, S3C64XX_EINT0MASK); 24662306a36Sopenharmony_ci} 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_cistatic void s3c_irq_eint_unmask(struct irq_data *data) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci u32 mask; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci mask = __raw_readl(S3C64XX_EINT0MASK); 25362306a36Sopenharmony_ci mask &= ~((u32)data->chip_data); 25462306a36Sopenharmony_ci __raw_writel(mask, S3C64XX_EINT0MASK); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic inline void s3c_irq_eint_ack(struct irq_data *data) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci __raw_writel((u32)data->chip_data, S3C64XX_EINT0PEND); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic void s3c_irq_eint_maskack(struct irq_data *data) 26362306a36Sopenharmony_ci{ 26462306a36Sopenharmony_ci /* compiler should in-line these */ 26562306a36Sopenharmony_ci s3c_irq_eint_mask(data); 26662306a36Sopenharmony_ci s3c_irq_eint_ack(data); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int s3c_irq_eint_set_type(struct irq_data *data, unsigned int type) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci int offs = eint_offset(data->irq); 27262306a36Sopenharmony_ci int pin, pin_val; 27362306a36Sopenharmony_ci int shift; 27462306a36Sopenharmony_ci u32 ctrl, mask; 27562306a36Sopenharmony_ci u32 newvalue = 0; 27662306a36Sopenharmony_ci void __iomem *reg; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (offs > 27) 27962306a36Sopenharmony_ci return -EINVAL; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (offs <= 15) 28262306a36Sopenharmony_ci reg = S3C64XX_EINT0CON0; 28362306a36Sopenharmony_ci else 28462306a36Sopenharmony_ci reg = S3C64XX_EINT0CON1; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci switch (type) { 28762306a36Sopenharmony_ci case IRQ_TYPE_NONE: 28862306a36Sopenharmony_ci printk(KERN_WARNING "No edge setting!\n"); 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 29262306a36Sopenharmony_ci newvalue = S3C2410_EXTINT_RISEEDGE; 29362306a36Sopenharmony_ci break; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 29662306a36Sopenharmony_ci newvalue = S3C2410_EXTINT_FALLEDGE; 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 30062306a36Sopenharmony_ci newvalue = S3C2410_EXTINT_BOTHEDGE; 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 30462306a36Sopenharmony_ci newvalue = S3C2410_EXTINT_LOWLEV; 30562306a36Sopenharmony_ci break; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 30862306a36Sopenharmony_ci newvalue = S3C2410_EXTINT_HILEV; 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci default: 31262306a36Sopenharmony_ci printk(KERN_ERR "No such irq type %d", type); 31362306a36Sopenharmony_ci return -1; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (offs <= 15) 31762306a36Sopenharmony_ci shift = (offs / 2) * 4; 31862306a36Sopenharmony_ci else 31962306a36Sopenharmony_ci shift = ((offs - 16) / 2) * 4; 32062306a36Sopenharmony_ci mask = 0x7 << shift; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci ctrl = __raw_readl(reg); 32362306a36Sopenharmony_ci ctrl &= ~mask; 32462306a36Sopenharmony_ci ctrl |= newvalue << shift; 32562306a36Sopenharmony_ci __raw_writel(ctrl, reg); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci /* set the GPIO pin appropriately */ 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (offs < 16) { 33062306a36Sopenharmony_ci pin = S3C64XX_GPN(offs); 33162306a36Sopenharmony_ci pin_val = S3C_GPIO_SFN(2); 33262306a36Sopenharmony_ci } else if (offs < 23) { 33362306a36Sopenharmony_ci pin = S3C64XX_GPL(offs + 8 - 16); 33462306a36Sopenharmony_ci pin_val = S3C_GPIO_SFN(3); 33562306a36Sopenharmony_ci } else { 33662306a36Sopenharmony_ci pin = S3C64XX_GPM(offs - 23); 33762306a36Sopenharmony_ci pin_val = S3C_GPIO_SFN(3); 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci s3c_gpio_cfgpin(pin, pin_val); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return 0; 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic struct irq_chip s3c_irq_eint = { 34662306a36Sopenharmony_ci .name = "s3c-eint", 34762306a36Sopenharmony_ci .irq_mask = s3c_irq_eint_mask, 34862306a36Sopenharmony_ci .irq_unmask = s3c_irq_eint_unmask, 34962306a36Sopenharmony_ci .irq_mask_ack = s3c_irq_eint_maskack, 35062306a36Sopenharmony_ci .irq_ack = s3c_irq_eint_ack, 35162306a36Sopenharmony_ci .irq_set_type = s3c_irq_eint_set_type, 35262306a36Sopenharmony_ci .irq_set_wake = s3c_irqext_wake, 35362306a36Sopenharmony_ci}; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci/* s3c_irq_demux_eint 35662306a36Sopenharmony_ci * 35762306a36Sopenharmony_ci * This function demuxes the IRQ from the group0 external interrupts, 35862306a36Sopenharmony_ci * from IRQ_EINT(0) to IRQ_EINT(27). It is designed to be inlined into 35962306a36Sopenharmony_ci * the specific handlers s3c_irq_demux_eintX_Y. 36062306a36Sopenharmony_ci */ 36162306a36Sopenharmony_cistatic inline void s3c_irq_demux_eint(unsigned int start, unsigned int end) 36262306a36Sopenharmony_ci{ 36362306a36Sopenharmony_ci u32 status = __raw_readl(S3C64XX_EINT0PEND); 36462306a36Sopenharmony_ci u32 mask = __raw_readl(S3C64XX_EINT0MASK); 36562306a36Sopenharmony_ci unsigned int irq; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci status &= ~mask; 36862306a36Sopenharmony_ci status >>= start; 36962306a36Sopenharmony_ci status &= (1 << (end - start + 1)) - 1; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci for (irq = IRQ_EINT(start); irq <= IRQ_EINT(end); irq++) { 37262306a36Sopenharmony_ci if (status & 1) 37362306a36Sopenharmony_ci generic_handle_irq(irq); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci status >>= 1; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic void s3c_irq_demux_eint0_3(struct irq_desc *desc) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci s3c_irq_demux_eint(0, 3); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic void s3c_irq_demux_eint4_11(struct irq_desc *desc) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci s3c_irq_demux_eint(4, 11); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic void s3c_irq_demux_eint12_19(struct irq_desc *desc) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci s3c_irq_demux_eint(12, 19); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_cistatic void s3c_irq_demux_eint20_27(struct irq_desc *desc) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci s3c_irq_demux_eint(20, 27); 39762306a36Sopenharmony_ci} 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_cistatic int __init s3c64xx_init_irq_eint(void) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci int irq; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* On DT-enabled systems EINTs are handled by pinctrl-s3c64xx driver. */ 40462306a36Sopenharmony_ci if (of_have_populated_dt() || !soc_is_s3c64xx()) 40562306a36Sopenharmony_ci return -ENODEV; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci for (irq = IRQ_EINT(0); irq <= IRQ_EINT(27); irq++) { 40862306a36Sopenharmony_ci irq_set_chip_and_handler(irq, &s3c_irq_eint, handle_level_irq); 40962306a36Sopenharmony_ci irq_set_chip_data(irq, (void *)eint_irq_to_bit(irq)); 41062306a36Sopenharmony_ci irq_clear_status_flags(irq, IRQ_NOREQUEST); 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci irq_set_chained_handler(IRQ_EINT0_3, s3c_irq_demux_eint0_3); 41462306a36Sopenharmony_ci irq_set_chained_handler(IRQ_EINT4_11, s3c_irq_demux_eint4_11); 41562306a36Sopenharmony_ci irq_set_chained_handler(IRQ_EINT12_19, s3c_irq_demux_eint12_19); 41662306a36Sopenharmony_ci irq_set_chained_handler(IRQ_EINT20_27, s3c_irq_demux_eint20_27); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return 0; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ciarch_initcall(s3c64xx_init_irq_eint); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci#ifndef CONFIG_COMPILE_TEST 42362306a36Sopenharmony_ci#pragma message "The platform is deprecated and scheduled for removal. " \ 42462306a36Sopenharmony_ci "Please reach to the maintainers of the platform " \ 42562306a36Sopenharmony_ci "and linux-samsung-soc@vger.kernel.org if you still use it." \ 42662306a36Sopenharmony_ci "Without such feedback, the platform will be removed after 2024." 42762306a36Sopenharmony_ci#endif 428