18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C)2004-2010 Freescale Semiconductor, Inc. All Rights Reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/init.h> 78c2ecf20Sopenharmony_ci#include <linux/device.h> 88c2ecf20Sopenharmony_ci#include <linux/errno.h> 98c2ecf20Sopenharmony_ci#include <linux/io.h> 108c2ecf20Sopenharmony_ci#include <linux/irqchip.h> 118c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/of_address.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <asm/mach/irq.h> 168c2ecf20Sopenharmony_ci#include <asm/exception.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include "common.h" 198c2ecf20Sopenharmony_ci#include "hardware.h" 208c2ecf20Sopenharmony_ci#include "irq-common.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* 238c2ecf20Sopenharmony_ci ***************************************** 248c2ecf20Sopenharmony_ci * TZIC Registers * 258c2ecf20Sopenharmony_ci ***************************************** 268c2ecf20Sopenharmony_ci */ 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define TZIC_INTCNTL 0x0000 /* Control register */ 298c2ecf20Sopenharmony_ci#define TZIC_INTTYPE 0x0004 /* Controller Type register */ 308c2ecf20Sopenharmony_ci#define TZIC_IMPID 0x0008 /* Distributor Implementer Identification */ 318c2ecf20Sopenharmony_ci#define TZIC_PRIOMASK 0x000C /* Priority Mask Reg */ 328c2ecf20Sopenharmony_ci#define TZIC_SYNCCTRL 0x0010 /* Synchronizer Control register */ 338c2ecf20Sopenharmony_ci#define TZIC_DSMINT 0x0014 /* DSM interrupt Holdoffregister */ 348c2ecf20Sopenharmony_ci#define TZIC_INTSEC0(i) (0x0080 + ((i) << 2)) /* Interrupt Security Reg 0 */ 358c2ecf20Sopenharmony_ci#define TZIC_ENSET0(i) (0x0100 + ((i) << 2)) /* Enable Set Reg 0 */ 368c2ecf20Sopenharmony_ci#define TZIC_ENCLEAR0(i) (0x0180 + ((i) << 2)) /* Enable Clear Reg 0 */ 378c2ecf20Sopenharmony_ci#define TZIC_SRCSET0 0x0200 /* Source Set Register 0 */ 388c2ecf20Sopenharmony_ci#define TZIC_SRCCLAR0 0x0280 /* Source Clear Register 0 */ 398c2ecf20Sopenharmony_ci#define TZIC_PRIORITY0 0x0400 /* Priority Register 0 */ 408c2ecf20Sopenharmony_ci#define TZIC_PND0 0x0D00 /* Pending Register 0 */ 418c2ecf20Sopenharmony_ci#define TZIC_HIPND(i) (0x0D80+ ((i) << 2)) /* High Priority Pending Register */ 428c2ecf20Sopenharmony_ci#define TZIC_WAKEUP0(i) (0x0E00 + ((i) << 2)) /* Wakeup Config Register */ 438c2ecf20Sopenharmony_ci#define TZIC_SWINT 0x0F00 /* Software Interrupt Rigger Register */ 448c2ecf20Sopenharmony_ci#define TZIC_ID0 0x0FD0 /* Indentification Register 0 */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic void __iomem *tzic_base; 478c2ecf20Sopenharmony_cistatic struct irq_domain *domain; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define TZIC_NUM_IRQS 128 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#ifdef CONFIG_FIQ 528c2ecf20Sopenharmony_cistatic int tzic_set_irq_fiq(unsigned int hwirq, unsigned int type) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci unsigned int index, mask, value; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci index = hwirq >> 5; 578c2ecf20Sopenharmony_ci if (unlikely(index >= 4)) 588c2ecf20Sopenharmony_ci return -EINVAL; 598c2ecf20Sopenharmony_ci mask = 1U << (hwirq & 0x1F); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci value = imx_readl(tzic_base + TZIC_INTSEC0(index)) | mask; 628c2ecf20Sopenharmony_ci if (type) 638c2ecf20Sopenharmony_ci value &= ~mask; 648c2ecf20Sopenharmony_ci imx_writel(value, tzic_base + TZIC_INTSEC0(index)); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci#else 698c2ecf20Sopenharmony_ci#define tzic_set_irq_fiq NULL 708c2ecf20Sopenharmony_ci#endif 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 738c2ecf20Sopenharmony_cistatic void tzic_irq_suspend(struct irq_data *d) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 768c2ecf20Sopenharmony_ci int idx = d->hwirq >> 5; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci imx_writel(gc->wake_active, tzic_base + TZIC_WAKEUP0(idx)); 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic void tzic_irq_resume(struct irq_data *d) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci int idx = d->hwirq >> 5; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci imx_writel(imx_readl(tzic_base + TZIC_ENSET0(idx)), 868c2ecf20Sopenharmony_ci tzic_base + TZIC_WAKEUP0(idx)); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#else 908c2ecf20Sopenharmony_ci#define tzic_irq_suspend NULL 918c2ecf20Sopenharmony_ci#define tzic_irq_resume NULL 928c2ecf20Sopenharmony_ci#endif 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic struct mxc_extra_irq tzic_extra_irq = { 958c2ecf20Sopenharmony_ci#ifdef CONFIG_FIQ 968c2ecf20Sopenharmony_ci .set_irq_fiq = tzic_set_irq_fiq, 978c2ecf20Sopenharmony_ci#endif 988c2ecf20Sopenharmony_ci}; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic __init void tzic_init_gc(int idx, unsigned int irq_start) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci struct irq_chip_generic *gc; 1038c2ecf20Sopenharmony_ci struct irq_chip_type *ct; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci gc = irq_alloc_generic_chip("tzic", 1, irq_start, tzic_base, 1068c2ecf20Sopenharmony_ci handle_level_irq); 1078c2ecf20Sopenharmony_ci gc->private = &tzic_extra_irq; 1088c2ecf20Sopenharmony_ci gc->wake_enabled = IRQ_MSK(32); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci ct = gc->chip_types; 1118c2ecf20Sopenharmony_ci ct->chip.irq_mask = irq_gc_mask_disable_reg; 1128c2ecf20Sopenharmony_ci ct->chip.irq_unmask = irq_gc_unmask_enable_reg; 1138c2ecf20Sopenharmony_ci ct->chip.irq_set_wake = irq_gc_set_wake; 1148c2ecf20Sopenharmony_ci ct->chip.irq_suspend = tzic_irq_suspend; 1158c2ecf20Sopenharmony_ci ct->chip.irq_resume = tzic_irq_resume; 1168c2ecf20Sopenharmony_ci ct->regs.disable = TZIC_ENCLEAR0(idx); 1178c2ecf20Sopenharmony_ci ct->regs.enable = TZIC_ENSET0(idx); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci irq_setup_generic_chip(gc, IRQ_MSK(32), 0, IRQ_NOREQUEST, 0); 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void __exception_irq_entry tzic_handle_irq(struct pt_regs *regs) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci u32 stat; 1258c2ecf20Sopenharmony_ci int i, irqofs, handled; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci do { 1288c2ecf20Sopenharmony_ci handled = 0; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 1318c2ecf20Sopenharmony_ci stat = imx_readl(tzic_base + TZIC_HIPND(i)) & 1328c2ecf20Sopenharmony_ci imx_readl(tzic_base + TZIC_INTSEC0(i)); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci while (stat) { 1358c2ecf20Sopenharmony_ci handled = 1; 1368c2ecf20Sopenharmony_ci irqofs = fls(stat) - 1; 1378c2ecf20Sopenharmony_ci handle_domain_irq(domain, irqofs + i * 32, regs); 1388c2ecf20Sopenharmony_ci stat &= ~(1 << irqofs); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci } while (handled); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/* 1458c2ecf20Sopenharmony_ci * This function initializes the TZIC hardware and disables all the 1468c2ecf20Sopenharmony_ci * interrupts. It registers the interrupt enable and disable functions 1478c2ecf20Sopenharmony_ci * to the kernel for each interrupt source. 1488c2ecf20Sopenharmony_ci */ 1498c2ecf20Sopenharmony_cistatic int __init tzic_init_dt(struct device_node *np, struct device_node *p) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci int irq_base; 1528c2ecf20Sopenharmony_ci int i; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci tzic_base = of_iomap(np, 0); 1558c2ecf20Sopenharmony_ci WARN_ON(!tzic_base); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci /* put the TZIC into the reset value with 1588c2ecf20Sopenharmony_ci * all interrupts disabled 1598c2ecf20Sopenharmony_ci */ 1608c2ecf20Sopenharmony_ci i = imx_readl(tzic_base + TZIC_INTCNTL); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci imx_writel(0x80010001, tzic_base + TZIC_INTCNTL); 1638c2ecf20Sopenharmony_ci imx_writel(0x1f, tzic_base + TZIC_PRIOMASK); 1648c2ecf20Sopenharmony_ci imx_writel(0x02, tzic_base + TZIC_SYNCCTRL); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 1678c2ecf20Sopenharmony_ci imx_writel(0xFFFFFFFF, tzic_base + TZIC_INTSEC0(i)); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci /* disable all interrupts */ 1708c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 1718c2ecf20Sopenharmony_ci imx_writel(0xFFFFFFFF, tzic_base + TZIC_ENCLEAR0(i)); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci /* all IRQ no FIQ Warning :: No selection */ 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci irq_base = irq_alloc_descs(-1, 0, TZIC_NUM_IRQS, numa_node_id()); 1768c2ecf20Sopenharmony_ci WARN_ON(irq_base < 0); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci domain = irq_domain_add_legacy(np, TZIC_NUM_IRQS, irq_base, 0, 1798c2ecf20Sopenharmony_ci &irq_domain_simple_ops, NULL); 1808c2ecf20Sopenharmony_ci WARN_ON(!domain); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++, irq_base += 32) 1838c2ecf20Sopenharmony_ci tzic_init_gc(i, irq_base); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci set_handle_irq(tzic_handle_irq); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci#ifdef CONFIG_FIQ 1888c2ecf20Sopenharmony_ci /* Initialize FIQ */ 1898c2ecf20Sopenharmony_ci init_FIQ(FIQ_START); 1908c2ecf20Sopenharmony_ci#endif 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci pr_info("TrustZone Interrupt Controller (TZIC) initialized\n"); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(tzic, "fsl,tzic", tzic_init_dt); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/** 1998c2ecf20Sopenharmony_ci * tzic_enable_wake() - enable wakeup interrupt 2008c2ecf20Sopenharmony_ci * 2018c2ecf20Sopenharmony_ci * @return 0 if successful; non-zero otherwise 2028c2ecf20Sopenharmony_ci * 2038c2ecf20Sopenharmony_ci * This function provides an interrupt synchronization point that is required 2048c2ecf20Sopenharmony_ci * by tzic enabled platforms before entering imx specific low power modes (ie, 2058c2ecf20Sopenharmony_ci * those low power modes beyond the WAIT_CLOCKED basic ARM WFI only mode). 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ciint tzic_enable_wake(void) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci unsigned int i; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci imx_writel(1, tzic_base + TZIC_DSMINT); 2128c2ecf20Sopenharmony_ci if (unlikely(imx_readl(tzic_base + TZIC_DSMINT) == 0)) 2138c2ecf20Sopenharmony_ci return -EAGAIN; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) 2168c2ecf20Sopenharmony_ci imx_writel(imx_readl(tzic_base + TZIC_ENSET0(i)), 2178c2ecf20Sopenharmony_ci tzic_base + TZIC_WAKEUP0(i)); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 221