162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/arch/arm/mach-pxa/irq.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Generic PXA IRQ handling 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Nicolas Pitre 862306a36Sopenharmony_ci * Created: Jun 15, 2001 962306a36Sopenharmony_ci * Copyright: MontaVista Software Inc. 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/bitops.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/syscore_ops.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci#include <linux/irq.h> 1862306a36Sopenharmony_ci#include <linux/of_address.h> 1962306a36Sopenharmony_ci#include <linux/of_irq.h> 2062306a36Sopenharmony_ci#include <linux/soc/pxa/cpu.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <asm/exception.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "irqs.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "generic.h" 2762306a36Sopenharmony_ci#include "pxa-regs.h" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define ICIP (0x000) 3062306a36Sopenharmony_ci#define ICMR (0x004) 3162306a36Sopenharmony_ci#define ICLR (0x008) 3262306a36Sopenharmony_ci#define ICFR (0x00c) 3362306a36Sopenharmony_ci#define ICPR (0x010) 3462306a36Sopenharmony_ci#define ICCR (0x014) 3562306a36Sopenharmony_ci#define ICHP (0x018) 3662306a36Sopenharmony_ci#define IPR(i) (((i) < 32) ? (0x01c + ((i) << 2)) : \ 3762306a36Sopenharmony_ci ((i) < 64) ? (0x0b0 + (((i) - 32) << 2)) : \ 3862306a36Sopenharmony_ci (0x144 + (((i) - 64) << 2))) 3962306a36Sopenharmony_ci#define ICHP_VAL_IRQ (1 << 31) 4062306a36Sopenharmony_ci#define ICHP_IRQ(i) (((i) >> 16) & 0x7fff) 4162306a36Sopenharmony_ci#define IPR_VALID (1 << 31) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define MAX_INTERNAL_IRQS 128 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * This is for peripheral IRQs internal to the PXA chip. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic void __iomem *pxa_irq_base; 5062306a36Sopenharmony_cistatic int pxa_internal_irq_nr; 5162306a36Sopenharmony_cistatic bool cpu_has_ipr; 5262306a36Sopenharmony_cistatic struct irq_domain *pxa_irq_domain; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic inline void __iomem *irq_base(int i) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci static unsigned long phys_base_offset[] = { 5762306a36Sopenharmony_ci 0x0, 5862306a36Sopenharmony_ci 0x9c, 5962306a36Sopenharmony_ci 0x130, 6062306a36Sopenharmony_ci }; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return pxa_irq_base + phys_base_offset[i]; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_civoid pxa_mask_irq(struct irq_data *d) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci void __iomem *base = irq_data_get_irq_chip_data(d); 6862306a36Sopenharmony_ci irq_hw_number_t irq = irqd_to_hwirq(d); 6962306a36Sopenharmony_ci uint32_t icmr = __raw_readl(base + ICMR); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci icmr &= ~BIT(irq & 0x1f); 7262306a36Sopenharmony_ci __raw_writel(icmr, base + ICMR); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_civoid pxa_unmask_irq(struct irq_data *d) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci void __iomem *base = irq_data_get_irq_chip_data(d); 7862306a36Sopenharmony_ci irq_hw_number_t irq = irqd_to_hwirq(d); 7962306a36Sopenharmony_ci uint32_t icmr = __raw_readl(base + ICMR); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci icmr |= BIT(irq & 0x1f); 8262306a36Sopenharmony_ci __raw_writel(icmr, base + ICMR); 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic struct irq_chip pxa_internal_irq_chip = { 8662306a36Sopenharmony_ci .name = "SC", 8762306a36Sopenharmony_ci .irq_ack = pxa_mask_irq, 8862306a36Sopenharmony_ci .irq_mask = pxa_mask_irq, 8962306a36Sopenharmony_ci .irq_unmask = pxa_unmask_irq, 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ciasmlinkage void __exception_irq_entry icip_handle_irq(struct pt_regs *regs) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci uint32_t icip, icmr, mask; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci do { 9762306a36Sopenharmony_ci icip = __raw_readl(pxa_irq_base + ICIP); 9862306a36Sopenharmony_ci icmr = __raw_readl(pxa_irq_base + ICMR); 9962306a36Sopenharmony_ci mask = icip & icmr; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (mask == 0) 10262306a36Sopenharmony_ci break; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci handle_IRQ(PXA_IRQ(fls(mask) - 1), regs); 10562306a36Sopenharmony_ci } while (1); 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ciasmlinkage void __exception_irq_entry ichp_handle_irq(struct pt_regs *regs) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci uint32_t ichp; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci do { 11362306a36Sopenharmony_ci __asm__ __volatile__("mrc p6, 0, %0, c5, c0, 0\n": "=r"(ichp)); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if ((ichp & ICHP_VAL_IRQ) == 0) 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci handle_IRQ(PXA_IRQ(ICHP_IRQ(ichp)), regs); 11962306a36Sopenharmony_ci } while (1); 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistatic int pxa_irq_map(struct irq_domain *h, unsigned int virq, 12362306a36Sopenharmony_ci irq_hw_number_t hw) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci void __iomem *base = irq_base(hw / 32); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* initialize interrupt priority */ 12862306a36Sopenharmony_ci if (cpu_has_ipr) 12962306a36Sopenharmony_ci __raw_writel(hw | IPR_VALID, pxa_irq_base + IPR(hw)); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci irq_set_chip_and_handler(virq, &pxa_internal_irq_chip, 13262306a36Sopenharmony_ci handle_level_irq); 13362306a36Sopenharmony_ci irq_set_chip_data(virq, base); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic const struct irq_domain_ops pxa_irq_ops = { 13962306a36Sopenharmony_ci .map = pxa_irq_map, 14062306a36Sopenharmony_ci .xlate = irq_domain_xlate_onecell, 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic __init void 14462306a36Sopenharmony_cipxa_init_irq_common(struct device_node *node, int irq_nr, 14562306a36Sopenharmony_ci int (*fn)(struct irq_data *, unsigned int)) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci int n; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci pxa_internal_irq_nr = irq_nr; 15062306a36Sopenharmony_ci pxa_irq_domain = irq_domain_add_legacy(node, irq_nr, 15162306a36Sopenharmony_ci PXA_IRQ(0), 0, 15262306a36Sopenharmony_ci &pxa_irq_ops, NULL); 15362306a36Sopenharmony_ci if (!pxa_irq_domain) 15462306a36Sopenharmony_ci panic("Unable to add PXA IRQ domain\n"); 15562306a36Sopenharmony_ci irq_set_default_host(pxa_irq_domain); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci for (n = 0; n < irq_nr; n += 32) { 15862306a36Sopenharmony_ci void __iomem *base = irq_base(n >> 5); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci __raw_writel(0, base + ICMR); /* disable all IRQs */ 16162306a36Sopenharmony_ci __raw_writel(0, base + ICLR); /* all IRQs are IRQ, not FIQ */ 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci /* only unmasked interrupts kick us out of idle */ 16462306a36Sopenharmony_ci __raw_writel(1, irq_base(0) + ICCR); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci pxa_internal_irq_chip.irq_set_wake = fn; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_civoid __init pxa_init_irq(int irq_nr, int (*fn)(struct irq_data *, unsigned int)) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci BUG_ON(irq_nr > MAX_INTERNAL_IRQS); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci pxa_irq_base = io_p2v(0x40d00000); 17462306a36Sopenharmony_ci cpu_has_ipr = !cpu_is_pxa25x(); 17562306a36Sopenharmony_ci pxa_init_irq_common(NULL, irq_nr, fn); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci#ifdef CONFIG_PM 17962306a36Sopenharmony_cistatic unsigned long saved_icmr[MAX_INTERNAL_IRQS/32]; 18062306a36Sopenharmony_cistatic unsigned long saved_ipr[MAX_INTERNAL_IRQS]; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int pxa_irq_suspend(void) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci int i; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(pxa_internal_irq_nr, 32); i++) { 18762306a36Sopenharmony_ci void __iomem *base = irq_base(i); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci saved_icmr[i] = __raw_readl(base + ICMR); 19062306a36Sopenharmony_ci __raw_writel(0, base + ICMR); 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (cpu_has_ipr) { 19462306a36Sopenharmony_ci for (i = 0; i < pxa_internal_irq_nr; i++) 19562306a36Sopenharmony_ci saved_ipr[i] = __raw_readl(pxa_irq_base + IPR(i)); 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return 0; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic void pxa_irq_resume(void) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci int i; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci for (i = 0; i < DIV_ROUND_UP(pxa_internal_irq_nr, 32); i++) { 20662306a36Sopenharmony_ci void __iomem *base = irq_base(i); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci __raw_writel(saved_icmr[i], base + ICMR); 20962306a36Sopenharmony_ci __raw_writel(0, base + ICLR); 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (cpu_has_ipr) 21362306a36Sopenharmony_ci for (i = 0; i < pxa_internal_irq_nr; i++) 21462306a36Sopenharmony_ci __raw_writel(saved_ipr[i], pxa_irq_base + IPR(i)); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci __raw_writel(1, pxa_irq_base + ICCR); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci#else 21962306a36Sopenharmony_ci#define pxa_irq_suspend NULL 22062306a36Sopenharmony_ci#define pxa_irq_resume NULL 22162306a36Sopenharmony_ci#endif 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistruct syscore_ops pxa_irq_syscore_ops = { 22462306a36Sopenharmony_ci .suspend = pxa_irq_suspend, 22562306a36Sopenharmony_ci .resume = pxa_irq_resume, 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci#ifdef CONFIG_OF 22962306a36Sopenharmony_cistatic const struct of_device_id intc_ids[] __initconst = { 23062306a36Sopenharmony_ci { .compatible = "marvell,pxa-intc", }, 23162306a36Sopenharmony_ci {} 23262306a36Sopenharmony_ci}; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_civoid __init pxa_dt_irq_init(int (*fn)(struct irq_data *, unsigned int)) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct device_node *node; 23762306a36Sopenharmony_ci struct resource res; 23862306a36Sopenharmony_ci int ret; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci node = of_find_matching_node(NULL, intc_ids); 24162306a36Sopenharmony_ci if (!node) { 24262306a36Sopenharmony_ci pr_err("Failed to find interrupt controller in arch-pxa\n"); 24362306a36Sopenharmony_ci return; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci ret = of_property_read_u32(node, "marvell,intc-nr-irqs", 24762306a36Sopenharmony_ci &pxa_internal_irq_nr); 24862306a36Sopenharmony_ci if (ret) { 24962306a36Sopenharmony_ci pr_err("Not found marvell,intc-nr-irqs property\n"); 25062306a36Sopenharmony_ci return; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci ret = of_address_to_resource(node, 0, &res); 25462306a36Sopenharmony_ci if (ret < 0) { 25562306a36Sopenharmony_ci pr_err("No registers defined for node\n"); 25662306a36Sopenharmony_ci return; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ci pxa_irq_base = io_p2v(res.start); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci cpu_has_ipr = of_property_read_bool(node, "marvell,intc-priority"); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci ret = irq_alloc_descs(-1, 0, pxa_internal_irq_nr, 0); 26362306a36Sopenharmony_ci if (ret < 0) { 26462306a36Sopenharmony_ci pr_err("Failed to allocate IRQ numbers\n"); 26562306a36Sopenharmony_ci return; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci pxa_init_irq_common(node, pxa_internal_irq_nr, fn); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci#endif /* CONFIG_OF */ 271