18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/arch/arm/common/vic.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 1999 - 2003 ARM Limited 68c2ecf20Sopenharmony_ci * Copyright (C) 2000 Deep Blue Solutions Ltd 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/export.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/list.h> 128c2ecf20Sopenharmony_ci#include <linux/io.h> 138c2ecf20Sopenharmony_ci#include <linux/irq.h> 148c2ecf20Sopenharmony_ci#include <linux/irqchip.h> 158c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 168c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 178c2ecf20Sopenharmony_ci#include <linux/of.h> 188c2ecf20Sopenharmony_ci#include <linux/of_address.h> 198c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 208c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 218c2ecf20Sopenharmony_ci#include <linux/device.h> 228c2ecf20Sopenharmony_ci#include <linux/amba/bus.h> 238c2ecf20Sopenharmony_ci#include <linux/irqchip/arm-vic.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include <asm/exception.h> 268c2ecf20Sopenharmony_ci#include <asm/irq.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define VIC_IRQ_STATUS 0x00 298c2ecf20Sopenharmony_ci#define VIC_FIQ_STATUS 0x04 308c2ecf20Sopenharmony_ci#define VIC_RAW_STATUS 0x08 318c2ecf20Sopenharmony_ci#define VIC_INT_SELECT 0x0c /* 1 = FIQ, 0 = IRQ */ 328c2ecf20Sopenharmony_ci#define VIC_INT_ENABLE 0x10 /* 1 = enable, 0 = disable */ 338c2ecf20Sopenharmony_ci#define VIC_INT_ENABLE_CLEAR 0x14 348c2ecf20Sopenharmony_ci#define VIC_INT_SOFT 0x18 358c2ecf20Sopenharmony_ci#define VIC_INT_SOFT_CLEAR 0x1c 368c2ecf20Sopenharmony_ci#define VIC_PROTECT 0x20 378c2ecf20Sopenharmony_ci#define VIC_PL190_VECT_ADDR 0x30 /* PL190 only */ 388c2ecf20Sopenharmony_ci#define VIC_PL190_DEF_VECT_ADDR 0x34 /* PL190 only */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define VIC_VECT_ADDR0 0x100 /* 0 to 15 (0..31 PL192) */ 418c2ecf20Sopenharmony_ci#define VIC_VECT_CNTL0 0x200 /* 0 to 15 (0..31 PL192) */ 428c2ecf20Sopenharmony_ci#define VIC_ITCR 0x300 /* VIC test control register */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define VIC_VECT_CNTL_ENABLE (1 << 5) 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define VIC_PL192_VECT_ADDR 0xF00 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/** 498c2ecf20Sopenharmony_ci * struct vic_device - VIC PM device 508c2ecf20Sopenharmony_ci * @parent_irq: The parent IRQ number of the VIC if cascaded, or 0. 518c2ecf20Sopenharmony_ci * @irq: The IRQ number for the base of the VIC. 528c2ecf20Sopenharmony_ci * @base: The register base for the VIC. 538c2ecf20Sopenharmony_ci * @valid_sources: A bitmask of valid interrupts 548c2ecf20Sopenharmony_ci * @resume_sources: A bitmask of interrupts for resume. 558c2ecf20Sopenharmony_ci * @resume_irqs: The IRQs enabled for resume. 568c2ecf20Sopenharmony_ci * @int_select: Save for VIC_INT_SELECT. 578c2ecf20Sopenharmony_ci * @int_enable: Save for VIC_INT_ENABLE. 588c2ecf20Sopenharmony_ci * @soft_int: Save for VIC_INT_SOFT. 598c2ecf20Sopenharmony_ci * @protect: Save for VIC_PROTECT. 608c2ecf20Sopenharmony_ci * @domain: The IRQ domain for the VIC. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_cistruct vic_device { 638c2ecf20Sopenharmony_ci void __iomem *base; 648c2ecf20Sopenharmony_ci int irq; 658c2ecf20Sopenharmony_ci u32 valid_sources; 668c2ecf20Sopenharmony_ci u32 resume_sources; 678c2ecf20Sopenharmony_ci u32 resume_irqs; 688c2ecf20Sopenharmony_ci u32 int_select; 698c2ecf20Sopenharmony_ci u32 int_enable; 708c2ecf20Sopenharmony_ci u32 soft_int; 718c2ecf20Sopenharmony_ci u32 protect; 728c2ecf20Sopenharmony_ci struct irq_domain *domain; 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* we cannot allocate memory when VICs are initially registered */ 768c2ecf20Sopenharmony_cistatic struct vic_device vic_devices[CONFIG_ARM_VIC_NR]; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int vic_id; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void vic_handle_irq(struct pt_regs *regs); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci/** 838c2ecf20Sopenharmony_ci * vic_init2 - common initialisation code 848c2ecf20Sopenharmony_ci * @base: Base of the VIC. 858c2ecf20Sopenharmony_ci * 868c2ecf20Sopenharmony_ci * Common initialisation code for registration 878c2ecf20Sopenharmony_ci * and resume. 888c2ecf20Sopenharmony_ci*/ 898c2ecf20Sopenharmony_cistatic void vic_init2(void __iomem *base) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci int i; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) { 948c2ecf20Sopenharmony_ci void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); 958c2ecf20Sopenharmony_ci writel(VIC_VECT_CNTL_ENABLE | i, reg); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci writel(32, base + VIC_PL190_DEF_VECT_ADDR); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 1028c2ecf20Sopenharmony_cistatic void resume_one_vic(struct vic_device *vic) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci void __iomem *base = vic->base; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: resuming vic at %p\n", __func__, base); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* re-initialise static settings */ 1098c2ecf20Sopenharmony_ci vic_init2(base); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci writel(vic->int_select, base + VIC_INT_SELECT); 1128c2ecf20Sopenharmony_ci writel(vic->protect, base + VIC_PROTECT); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* set the enabled ints and then clear the non-enabled */ 1158c2ecf20Sopenharmony_ci writel(vic->int_enable, base + VIC_INT_ENABLE); 1168c2ecf20Sopenharmony_ci writel(~vic->int_enable, base + VIC_INT_ENABLE_CLEAR); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci /* and the same for the soft-int register */ 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci writel(vic->soft_int, base + VIC_INT_SOFT); 1218c2ecf20Sopenharmony_ci writel(~vic->soft_int, base + VIC_INT_SOFT_CLEAR); 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void vic_resume(void) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci int id; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci for (id = vic_id - 1; id >= 0; id--) 1298c2ecf20Sopenharmony_ci resume_one_vic(vic_devices + id); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void suspend_one_vic(struct vic_device *vic) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci void __iomem *base = vic->base; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci printk(KERN_DEBUG "%s: suspending vic at %p\n", __func__, base); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci vic->int_select = readl(base + VIC_INT_SELECT); 1398c2ecf20Sopenharmony_ci vic->int_enable = readl(base + VIC_INT_ENABLE); 1408c2ecf20Sopenharmony_ci vic->soft_int = readl(base + VIC_INT_SOFT); 1418c2ecf20Sopenharmony_ci vic->protect = readl(base + VIC_PROTECT); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci /* set the interrupts (if any) that are used for 1448c2ecf20Sopenharmony_ci * resuming the system */ 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci writel(vic->resume_irqs, base + VIC_INT_ENABLE); 1478c2ecf20Sopenharmony_ci writel(~vic->resume_irqs, base + VIC_INT_ENABLE_CLEAR); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int vic_suspend(void) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci int id; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci for (id = 0; id < vic_id; id++) 1558c2ecf20Sopenharmony_ci suspend_one_vic(vic_devices + id); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic struct syscore_ops vic_syscore_ops = { 1618c2ecf20Sopenharmony_ci .suspend = vic_suspend, 1628c2ecf20Sopenharmony_ci .resume = vic_resume, 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci/** 1668c2ecf20Sopenharmony_ci * vic_pm_init - initcall to register VIC pm 1678c2ecf20Sopenharmony_ci * 1688c2ecf20Sopenharmony_ci * This is called via late_initcall() to register 1698c2ecf20Sopenharmony_ci * the resources for the VICs due to the early 1708c2ecf20Sopenharmony_ci * nature of the VIC's registration. 1718c2ecf20Sopenharmony_ci*/ 1728c2ecf20Sopenharmony_cistatic int __init vic_pm_init(void) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci if (vic_id > 0) 1758c2ecf20Sopenharmony_ci register_syscore_ops(&vic_syscore_ops); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_cilate_initcall(vic_pm_init); 1808c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic struct irq_chip vic_chip; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int vic_irqdomain_map(struct irq_domain *d, unsigned int irq, 1858c2ecf20Sopenharmony_ci irq_hw_number_t hwirq) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct vic_device *v = d->host_data; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* Skip invalid IRQs, only register handlers for the real ones */ 1908c2ecf20Sopenharmony_ci if (!(v->valid_sources & (1 << hwirq))) 1918c2ecf20Sopenharmony_ci return -EPERM; 1928c2ecf20Sopenharmony_ci irq_set_chip_and_handler(irq, &vic_chip, handle_level_irq); 1938c2ecf20Sopenharmony_ci irq_set_chip_data(irq, v->base); 1948c2ecf20Sopenharmony_ci irq_set_probe(irq); 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/* 1998c2ecf20Sopenharmony_ci * Handle each interrupt in a single VIC. Returns non-zero if we've 2008c2ecf20Sopenharmony_ci * handled at least one interrupt. This reads the status register 2018c2ecf20Sopenharmony_ci * before handling each interrupt, which is necessary given that 2028c2ecf20Sopenharmony_ci * handle_IRQ may briefly re-enable interrupts for soft IRQ handling. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_cistatic int handle_one_vic(struct vic_device *vic, struct pt_regs *regs) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci u32 stat, irq; 2078c2ecf20Sopenharmony_ci int handled = 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) { 2108c2ecf20Sopenharmony_ci irq = ffs(stat) - 1; 2118c2ecf20Sopenharmony_ci handle_domain_irq(vic->domain, irq, regs); 2128c2ecf20Sopenharmony_ci handled = 1; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci return handled; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic void vic_handle_irq_cascaded(struct irq_desc *desc) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci u32 stat, hwirq; 2218c2ecf20Sopenharmony_ci struct irq_chip *host_chip = irq_desc_get_chip(desc); 2228c2ecf20Sopenharmony_ci struct vic_device *vic = irq_desc_get_handler_data(desc); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci chained_irq_enter(host_chip, desc); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci while ((stat = readl_relaxed(vic->base + VIC_IRQ_STATUS))) { 2278c2ecf20Sopenharmony_ci hwirq = ffs(stat) - 1; 2288c2ecf20Sopenharmony_ci generic_handle_irq(irq_find_mapping(vic->domain, hwirq)); 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci chained_irq_exit(host_chip, desc); 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/* 2358c2ecf20Sopenharmony_ci * Keep iterating over all registered VIC's until there are no pending 2368c2ecf20Sopenharmony_ci * interrupts. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_cistatic void __exception_irq_entry vic_handle_irq(struct pt_regs *regs) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci int i, handled; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci do { 2438c2ecf20Sopenharmony_ci for (i = 0, handled = 0; i < vic_id; ++i) 2448c2ecf20Sopenharmony_ci handled |= handle_one_vic(&vic_devices[i], regs); 2458c2ecf20Sopenharmony_ci } while (handled); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic const struct irq_domain_ops vic_irqdomain_ops = { 2498c2ecf20Sopenharmony_ci .map = vic_irqdomain_map, 2508c2ecf20Sopenharmony_ci .xlate = irq_domain_xlate_onetwocell, 2518c2ecf20Sopenharmony_ci}; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci/** 2548c2ecf20Sopenharmony_ci * vic_register() - Register a VIC. 2558c2ecf20Sopenharmony_ci * @base: The base address of the VIC. 2568c2ecf20Sopenharmony_ci * @parent_irq: The parent IRQ if cascaded, else 0. 2578c2ecf20Sopenharmony_ci * @irq: The base IRQ for the VIC. 2588c2ecf20Sopenharmony_ci * @valid_sources: bitmask of valid interrupts 2598c2ecf20Sopenharmony_ci * @resume_sources: bitmask of interrupts allowed for resume sources. 2608c2ecf20Sopenharmony_ci * @node: The device tree node associated with the VIC. 2618c2ecf20Sopenharmony_ci * 2628c2ecf20Sopenharmony_ci * Register the VIC with the system device tree so that it can be notified 2638c2ecf20Sopenharmony_ci * of suspend and resume requests and ensure that the correct actions are 2648c2ecf20Sopenharmony_ci * taken to re-instate the settings on resume. 2658c2ecf20Sopenharmony_ci * 2668c2ecf20Sopenharmony_ci * This also configures the IRQ domain for the VIC. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_cistatic void __init vic_register(void __iomem *base, unsigned int parent_irq, 2698c2ecf20Sopenharmony_ci unsigned int irq, 2708c2ecf20Sopenharmony_ci u32 valid_sources, u32 resume_sources, 2718c2ecf20Sopenharmony_ci struct device_node *node) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct vic_device *v; 2748c2ecf20Sopenharmony_ci int i; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci if (vic_id >= ARRAY_SIZE(vic_devices)) { 2778c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", __func__); 2788c2ecf20Sopenharmony_ci return; 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci v = &vic_devices[vic_id]; 2828c2ecf20Sopenharmony_ci v->base = base; 2838c2ecf20Sopenharmony_ci v->valid_sources = valid_sources; 2848c2ecf20Sopenharmony_ci v->resume_sources = resume_sources; 2858c2ecf20Sopenharmony_ci set_handle_irq(vic_handle_irq); 2868c2ecf20Sopenharmony_ci vic_id++; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (parent_irq) { 2898c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(parent_irq, 2908c2ecf20Sopenharmony_ci vic_handle_irq_cascaded, v); 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci v->domain = irq_domain_add_simple(node, fls(valid_sources), irq, 2948c2ecf20Sopenharmony_ci &vic_irqdomain_ops, v); 2958c2ecf20Sopenharmony_ci /* create an IRQ mapping for each valid IRQ */ 2968c2ecf20Sopenharmony_ci for (i = 0; i < fls(valid_sources); i++) 2978c2ecf20Sopenharmony_ci if (valid_sources & (1 << i)) 2988c2ecf20Sopenharmony_ci irq_create_mapping(v->domain, i); 2998c2ecf20Sopenharmony_ci /* If no base IRQ was passed, figure out our allocated base */ 3008c2ecf20Sopenharmony_ci if (irq) 3018c2ecf20Sopenharmony_ci v->irq = irq; 3028c2ecf20Sopenharmony_ci else 3038c2ecf20Sopenharmony_ci v->irq = irq_find_mapping(v->domain, 0); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic void vic_ack_irq(struct irq_data *d) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci void __iomem *base = irq_data_get_irq_chip_data(d); 3098c2ecf20Sopenharmony_ci unsigned int irq = d->hwirq; 3108c2ecf20Sopenharmony_ci writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); 3118c2ecf20Sopenharmony_ci /* moreover, clear the soft-triggered, in case it was the reason */ 3128c2ecf20Sopenharmony_ci writel(1 << irq, base + VIC_INT_SOFT_CLEAR); 3138c2ecf20Sopenharmony_ci} 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_cistatic void vic_mask_irq(struct irq_data *d) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci void __iomem *base = irq_data_get_irq_chip_data(d); 3188c2ecf20Sopenharmony_ci unsigned int irq = d->hwirq; 3198c2ecf20Sopenharmony_ci writel(1 << irq, base + VIC_INT_ENABLE_CLEAR); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic void vic_unmask_irq(struct irq_data *d) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci void __iomem *base = irq_data_get_irq_chip_data(d); 3258c2ecf20Sopenharmony_ci unsigned int irq = d->hwirq; 3268c2ecf20Sopenharmony_ci writel(1 << irq, base + VIC_INT_ENABLE); 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci#if defined(CONFIG_PM) 3308c2ecf20Sopenharmony_cistatic struct vic_device *vic_from_irq(unsigned int irq) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct vic_device *v = vic_devices; 3338c2ecf20Sopenharmony_ci unsigned int base_irq = irq & ~31; 3348c2ecf20Sopenharmony_ci int id; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci for (id = 0; id < vic_id; id++, v++) { 3378c2ecf20Sopenharmony_ci if (v->irq == base_irq) 3388c2ecf20Sopenharmony_ci return v; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return NULL; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic int vic_set_wake(struct irq_data *d, unsigned int on) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci struct vic_device *v = vic_from_irq(d->irq); 3478c2ecf20Sopenharmony_ci unsigned int off = d->hwirq; 3488c2ecf20Sopenharmony_ci u32 bit = 1 << off; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci if (!v) 3518c2ecf20Sopenharmony_ci return -EINVAL; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci if (!(bit & v->resume_sources)) 3548c2ecf20Sopenharmony_ci return -EINVAL; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (on) 3578c2ecf20Sopenharmony_ci v->resume_irqs |= bit; 3588c2ecf20Sopenharmony_ci else 3598c2ecf20Sopenharmony_ci v->resume_irqs &= ~bit; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci return 0; 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci#else 3648c2ecf20Sopenharmony_ci#define vic_set_wake NULL 3658c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */ 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic struct irq_chip vic_chip = { 3688c2ecf20Sopenharmony_ci .name = "VIC", 3698c2ecf20Sopenharmony_ci .irq_ack = vic_ack_irq, 3708c2ecf20Sopenharmony_ci .irq_mask = vic_mask_irq, 3718c2ecf20Sopenharmony_ci .irq_unmask = vic_unmask_irq, 3728c2ecf20Sopenharmony_ci .irq_set_wake = vic_set_wake, 3738c2ecf20Sopenharmony_ci}; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic void __init vic_disable(void __iomem *base) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci writel(0, base + VIC_INT_SELECT); 3788c2ecf20Sopenharmony_ci writel(0, base + VIC_INT_ENABLE); 3798c2ecf20Sopenharmony_ci writel(~0, base + VIC_INT_ENABLE_CLEAR); 3808c2ecf20Sopenharmony_ci writel(0, base + VIC_ITCR); 3818c2ecf20Sopenharmony_ci writel(~0, base + VIC_INT_SOFT_CLEAR); 3828c2ecf20Sopenharmony_ci} 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_cistatic void __init vic_clear_interrupts(void __iomem *base) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci unsigned int i; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci writel(0, base + VIC_PL190_VECT_ADDR); 3898c2ecf20Sopenharmony_ci for (i = 0; i < 19; i++) { 3908c2ecf20Sopenharmony_ci unsigned int value; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci value = readl(base + VIC_PL190_VECT_ADDR); 3938c2ecf20Sopenharmony_ci writel(value, base + VIC_PL190_VECT_ADDR); 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci/* 3988c2ecf20Sopenharmony_ci * The PL190 cell from ARM has been modified by ST to handle 64 interrupts. 3998c2ecf20Sopenharmony_ci * The original cell has 32 interrupts, while the modified one has 64, 4008c2ecf20Sopenharmony_ci * replicating two blocks 0x00..0x1f in 0x20..0x3f. In that case 4018c2ecf20Sopenharmony_ci * the probe function is called twice, with base set to offset 000 4028c2ecf20Sopenharmony_ci * and 020 within the page. We call this "second block". 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_cistatic void __init vic_init_st(void __iomem *base, unsigned int irq_start, 4058c2ecf20Sopenharmony_ci u32 vic_sources, struct device_node *node) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci unsigned int i; 4088c2ecf20Sopenharmony_ci int vic_2nd_block = ((unsigned long)base & ~PAGE_MASK) != 0; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* Disable all interrupts initially. */ 4118c2ecf20Sopenharmony_ci vic_disable(base); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci /* 4148c2ecf20Sopenharmony_ci * Make sure we clear all existing interrupts. The vector registers 4158c2ecf20Sopenharmony_ci * in this cell are after the second block of general registers, 4168c2ecf20Sopenharmony_ci * so we can address them using standard offsets, but only from 4178c2ecf20Sopenharmony_ci * the second base address, which is 0x20 in the page 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci if (vic_2nd_block) { 4208c2ecf20Sopenharmony_ci vic_clear_interrupts(base); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci /* ST has 16 vectors as well, but we don't enable them by now */ 4238c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) { 4248c2ecf20Sopenharmony_ci void __iomem *reg = base + VIC_VECT_CNTL0 + (i * 4); 4258c2ecf20Sopenharmony_ci writel(0, reg); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci writel(32, base + VIC_PL190_DEF_VECT_ADDR); 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci vic_register(base, 0, irq_start, vic_sources, 0, node); 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic void __init __vic_init(void __iomem *base, int parent_irq, int irq_start, 4358c2ecf20Sopenharmony_ci u32 vic_sources, u32 resume_sources, 4368c2ecf20Sopenharmony_ci struct device_node *node) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci unsigned int i; 4398c2ecf20Sopenharmony_ci u32 cellid = 0; 4408c2ecf20Sopenharmony_ci enum amba_vendor vendor; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* Identify which VIC cell this one is, by reading the ID */ 4438c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 4448c2ecf20Sopenharmony_ci void __iomem *addr; 4458c2ecf20Sopenharmony_ci addr = (void __iomem *)((u32)base & PAGE_MASK) + 0xfe0 + (i * 4); 4468c2ecf20Sopenharmony_ci cellid |= (readl(addr) & 0xff) << (8 * i); 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci vendor = (cellid >> 12) & 0xff; 4498c2ecf20Sopenharmony_ci printk(KERN_INFO "VIC @%p: id 0x%08x, vendor 0x%02x\n", 4508c2ecf20Sopenharmony_ci base, cellid, vendor); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci switch(vendor) { 4538c2ecf20Sopenharmony_ci case AMBA_VENDOR_ST: 4548c2ecf20Sopenharmony_ci vic_init_st(base, irq_start, vic_sources, node); 4558c2ecf20Sopenharmony_ci return; 4568c2ecf20Sopenharmony_ci default: 4578c2ecf20Sopenharmony_ci printk(KERN_WARNING "VIC: unknown vendor, continuing anyways\n"); 4588c2ecf20Sopenharmony_ci fallthrough; 4598c2ecf20Sopenharmony_ci case AMBA_VENDOR_ARM: 4608c2ecf20Sopenharmony_ci break; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Disable all interrupts initially. */ 4648c2ecf20Sopenharmony_ci vic_disable(base); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci /* Make sure we clear all existing interrupts */ 4678c2ecf20Sopenharmony_ci vic_clear_interrupts(base); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci vic_init2(base); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci vic_register(base, parent_irq, irq_start, vic_sources, resume_sources, node); 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci/** 4758c2ecf20Sopenharmony_ci * vic_init() - initialise a vectored interrupt controller 4768c2ecf20Sopenharmony_ci * @base: iomem base address 4778c2ecf20Sopenharmony_ci * @irq_start: starting interrupt number, must be muliple of 32 4788c2ecf20Sopenharmony_ci * @vic_sources: bitmask of interrupt sources to allow 4798c2ecf20Sopenharmony_ci * @resume_sources: bitmask of interrupt sources to allow for resume 4808c2ecf20Sopenharmony_ci */ 4818c2ecf20Sopenharmony_civoid __init vic_init(void __iomem *base, unsigned int irq_start, 4828c2ecf20Sopenharmony_ci u32 vic_sources, u32 resume_sources) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci __vic_init(base, 0, irq_start, vic_sources, resume_sources, NULL); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 4888c2ecf20Sopenharmony_cistatic int __init vic_of_init(struct device_node *node, 4898c2ecf20Sopenharmony_ci struct device_node *parent) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci void __iomem *regs; 4928c2ecf20Sopenharmony_ci u32 interrupt_mask = ~0; 4938c2ecf20Sopenharmony_ci u32 wakeup_mask = ~0; 4948c2ecf20Sopenharmony_ci int parent_irq; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci regs = of_iomap(node, 0); 4978c2ecf20Sopenharmony_ci if (WARN_ON(!regs)) 4988c2ecf20Sopenharmony_ci return -EIO; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci of_property_read_u32(node, "valid-mask", &interrupt_mask); 5018c2ecf20Sopenharmony_ci of_property_read_u32(node, "valid-wakeup-mask", &wakeup_mask); 5028c2ecf20Sopenharmony_ci parent_irq = of_irq_get(node, 0); 5038c2ecf20Sopenharmony_ci if (parent_irq < 0) 5048c2ecf20Sopenharmony_ci parent_irq = 0; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci /* 5078c2ecf20Sopenharmony_ci * Passing 0 as first IRQ makes the simple domain allocate descriptors 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_ci __vic_init(regs, parent_irq, 0, interrupt_mask, wakeup_mask, node); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci return 0; 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(arm_pl190_vic, "arm,pl190-vic", vic_of_init); 5148c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(arm_pl192_vic, "arm,pl192-vic", vic_of_init); 5158c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(arm_versatile_vic, "arm,versatile-vic", vic_of_init); 5168c2ecf20Sopenharmony_ci#endif /* CONFIG OF */ 517