18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IMG PowerDown Controller (PDC) 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2010-2013 Imagination Technologies Ltd. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Exposes the syswake and PDC peripheral wake interrupts to the system. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/bitops.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* PDC interrupt register numbers */ 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define PDC_IRQ_STATUS 0x310 238c2ecf20Sopenharmony_ci#define PDC_IRQ_ENABLE 0x314 248c2ecf20Sopenharmony_ci#define PDC_IRQ_CLEAR 0x318 258c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE 0x31c 268c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_BASE 0x330 278c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_STRIDE 0x8 288c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_CONFIG_BASE 0x334 298c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_CONFIG_STRIDE 0x8 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* PDC interrupt register field masks */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define PDC_IRQ_SYS3 0x08 348c2ecf20Sopenharmony_ci#define PDC_IRQ_SYS2 0x04 358c2ecf20Sopenharmony_ci#define PDC_IRQ_SYS1 0x02 368c2ecf20Sopenharmony_ci#define PDC_IRQ_SYS0 0x01 378c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_WU_EN_SYS3 0x08000000 388c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_WU_EN_SYS2 0x04000000 398c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_WU_EN_SYS1 0x02000000 408c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_WU_EN_SYS0 0x01000000 418c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_WU_EN_WD 0x00040000 428c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_WU_EN_IR 0x00020000 438c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_WU_EN_RTC 0x00010000 448c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_EXT_EN_SYS3 0x00000800 458c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_EXT_EN_SYS2 0x00000400 468c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_EXT_EN_SYS1 0x00000200 478c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_EXT_EN_SYS0 0x00000100 488c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_EXT_EN_WD 0x00000004 498c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_EXT_EN_IR 0x00000002 508c2ecf20Sopenharmony_ci#define PDC_IRQ_ROUTE_EXT_EN_RTC 0x00000001 518c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_RESET 0x00000010 528c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_MODE 0x0000000e 538c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_MODE_SHIFT 1 548c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_PIN_VAL 0x00000001 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* PDC interrupt constants */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_LOW 0x0 598c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_HIGH 0x1 608c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_DOWN 0x2 618c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_UP 0x3 628c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_CHANGE 0x6 638c2ecf20Sopenharmony_ci#define PDC_SYS_WAKE_INT_NONE 0x4 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/** 668c2ecf20Sopenharmony_ci * struct pdc_intc_priv - private pdc interrupt data. 678c2ecf20Sopenharmony_ci * @nr_perips: Number of peripheral interrupt signals. 688c2ecf20Sopenharmony_ci * @nr_syswakes: Number of syswake signals. 698c2ecf20Sopenharmony_ci * @perip_irqs: List of peripheral IRQ numbers handled. 708c2ecf20Sopenharmony_ci * @syswake_irq: Shared PDC syswake IRQ number. 718c2ecf20Sopenharmony_ci * @domain: IRQ domain for PDC peripheral and syswake IRQs. 728c2ecf20Sopenharmony_ci * @pdc_base: Base of PDC registers. 738c2ecf20Sopenharmony_ci * @irq_route: Cached version of PDC_IRQ_ROUTE register. 748c2ecf20Sopenharmony_ci * @lock: Lock to protect the PDC syswake registers and the cached 758c2ecf20Sopenharmony_ci * values of those registers in this struct. 768c2ecf20Sopenharmony_ci */ 778c2ecf20Sopenharmony_cistruct pdc_intc_priv { 788c2ecf20Sopenharmony_ci unsigned int nr_perips; 798c2ecf20Sopenharmony_ci unsigned int nr_syswakes; 808c2ecf20Sopenharmony_ci unsigned int *perip_irqs; 818c2ecf20Sopenharmony_ci unsigned int syswake_irq; 828c2ecf20Sopenharmony_ci struct irq_domain *domain; 838c2ecf20Sopenharmony_ci void __iomem *pdc_base; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci u32 irq_route; 868c2ecf20Sopenharmony_ci raw_spinlock_t lock; 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic void pdc_write(struct pdc_intc_priv *priv, unsigned int reg_offs, 908c2ecf20Sopenharmony_ci unsigned int data) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci iowrite32(data, priv->pdc_base + reg_offs); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic unsigned int pdc_read(struct pdc_intc_priv *priv, 968c2ecf20Sopenharmony_ci unsigned int reg_offs) 978c2ecf20Sopenharmony_ci{ 988c2ecf20Sopenharmony_ci return ioread32(priv->pdc_base + reg_offs); 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci/* Generic IRQ callbacks */ 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#define SYS0_HWIRQ 8 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic unsigned int hwirq_is_syswake(irq_hw_number_t hw) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci return hw >= SYS0_HWIRQ; 1088c2ecf20Sopenharmony_ci} 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic unsigned int hwirq_to_syswake(irq_hw_number_t hw) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci return hw - SYS0_HWIRQ; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic irq_hw_number_t syswake_to_hwirq(unsigned int syswake) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci return SYS0_HWIRQ + syswake; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic struct pdc_intc_priv *irqd_to_priv(struct irq_data *data) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci return (struct pdc_intc_priv *)data->domain->host_data; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* 1268c2ecf20Sopenharmony_ci * perip_irq_mask() and perip_irq_unmask() use IRQ_ROUTE which also contains 1278c2ecf20Sopenharmony_ci * wake bits, therefore we cannot use the generic irqchip mask callbacks as they 1288c2ecf20Sopenharmony_ci * cache the mask. 1298c2ecf20Sopenharmony_ci */ 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void perip_irq_mask(struct irq_data *data) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct pdc_intc_priv *priv = irqd_to_priv(data); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci raw_spin_lock(&priv->lock); 1368c2ecf20Sopenharmony_ci priv->irq_route &= ~data->mask; 1378c2ecf20Sopenharmony_ci pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route); 1388c2ecf20Sopenharmony_ci raw_spin_unlock(&priv->lock); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic void perip_irq_unmask(struct irq_data *data) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct pdc_intc_priv *priv = irqd_to_priv(data); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci raw_spin_lock(&priv->lock); 1468c2ecf20Sopenharmony_ci priv->irq_route |= data->mask; 1478c2ecf20Sopenharmony_ci pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route); 1488c2ecf20Sopenharmony_ci raw_spin_unlock(&priv->lock); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int syswake_irq_set_type(struct irq_data *data, unsigned int flow_type) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci struct pdc_intc_priv *priv = irqd_to_priv(data); 1548c2ecf20Sopenharmony_ci unsigned int syswake = hwirq_to_syswake(data->hwirq); 1558c2ecf20Sopenharmony_ci unsigned int irq_mode; 1568c2ecf20Sopenharmony_ci unsigned int soc_sys_wake_regoff, soc_sys_wake; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* translate to syswake IRQ mode */ 1598c2ecf20Sopenharmony_ci switch (flow_type) { 1608c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 1618c2ecf20Sopenharmony_ci irq_mode = PDC_SYS_WAKE_INT_CHANGE; 1628c2ecf20Sopenharmony_ci break; 1638c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 1648c2ecf20Sopenharmony_ci irq_mode = PDC_SYS_WAKE_INT_UP; 1658c2ecf20Sopenharmony_ci break; 1668c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 1678c2ecf20Sopenharmony_ci irq_mode = PDC_SYS_WAKE_INT_DOWN; 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 1708c2ecf20Sopenharmony_ci irq_mode = PDC_SYS_WAKE_INT_HIGH; 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 1738c2ecf20Sopenharmony_ci irq_mode = PDC_SYS_WAKE_INT_LOW; 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci default: 1768c2ecf20Sopenharmony_ci return -EINVAL; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci raw_spin_lock(&priv->lock); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci /* set the IRQ mode */ 1828c2ecf20Sopenharmony_ci soc_sys_wake_regoff = PDC_SYS_WAKE_BASE + syswake*PDC_SYS_WAKE_STRIDE; 1838c2ecf20Sopenharmony_ci soc_sys_wake = pdc_read(priv, soc_sys_wake_regoff); 1848c2ecf20Sopenharmony_ci soc_sys_wake &= ~PDC_SYS_WAKE_INT_MODE; 1858c2ecf20Sopenharmony_ci soc_sys_wake |= irq_mode << PDC_SYS_WAKE_INT_MODE_SHIFT; 1868c2ecf20Sopenharmony_ci pdc_write(priv, soc_sys_wake_regoff, soc_sys_wake); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* and update the handler */ 1898c2ecf20Sopenharmony_ci irq_setup_alt_chip(data, flow_type); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci raw_spin_unlock(&priv->lock); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* applies to both peripheral and syswake interrupts */ 1978c2ecf20Sopenharmony_cistatic int pdc_irq_set_wake(struct irq_data *data, unsigned int on) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct pdc_intc_priv *priv = irqd_to_priv(data); 2008c2ecf20Sopenharmony_ci irq_hw_number_t hw = data->hwirq; 2018c2ecf20Sopenharmony_ci unsigned int mask = (1 << 16) << hw; 2028c2ecf20Sopenharmony_ci unsigned int dst_irq; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci raw_spin_lock(&priv->lock); 2058c2ecf20Sopenharmony_ci if (on) 2068c2ecf20Sopenharmony_ci priv->irq_route |= mask; 2078c2ecf20Sopenharmony_ci else 2088c2ecf20Sopenharmony_ci priv->irq_route &= ~mask; 2098c2ecf20Sopenharmony_ci pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route); 2108c2ecf20Sopenharmony_ci raw_spin_unlock(&priv->lock); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* control the destination IRQ wakeup too for standby mode */ 2138c2ecf20Sopenharmony_ci if (hwirq_is_syswake(hw)) 2148c2ecf20Sopenharmony_ci dst_irq = priv->syswake_irq; 2158c2ecf20Sopenharmony_ci else 2168c2ecf20Sopenharmony_ci dst_irq = priv->perip_irqs[hw]; 2178c2ecf20Sopenharmony_ci irq_set_irq_wake(dst_irq, on); 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return 0; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic void pdc_intc_perip_isr(struct irq_desc *desc) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci unsigned int irq = irq_desc_get_irq(desc); 2258c2ecf20Sopenharmony_ci struct pdc_intc_priv *priv; 2268c2ecf20Sopenharmony_ci unsigned int i, irq_no; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci priv = (struct pdc_intc_priv *)irq_desc_get_handler_data(desc); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* find the peripheral number */ 2318c2ecf20Sopenharmony_ci for (i = 0; i < priv->nr_perips; ++i) 2328c2ecf20Sopenharmony_ci if (irq == priv->perip_irqs[i]) 2338c2ecf20Sopenharmony_ci goto found; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* should never get here */ 2368c2ecf20Sopenharmony_ci return; 2378c2ecf20Sopenharmony_cifound: 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* pass on the interrupt */ 2408c2ecf20Sopenharmony_ci irq_no = irq_linear_revmap(priv->domain, i); 2418c2ecf20Sopenharmony_ci generic_handle_irq(irq_no); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic void pdc_intc_syswake_isr(struct irq_desc *desc) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct pdc_intc_priv *priv; 2478c2ecf20Sopenharmony_ci unsigned int syswake, irq_no; 2488c2ecf20Sopenharmony_ci unsigned int status; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci priv = (struct pdc_intc_priv *)irq_desc_get_handler_data(desc); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci status = pdc_read(priv, PDC_IRQ_STATUS) & 2538c2ecf20Sopenharmony_ci pdc_read(priv, PDC_IRQ_ENABLE); 2548c2ecf20Sopenharmony_ci status &= (1 << priv->nr_syswakes) - 1; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci for (syswake = 0; status; status >>= 1, ++syswake) { 2578c2ecf20Sopenharmony_ci /* Has this sys_wake triggered? */ 2588c2ecf20Sopenharmony_ci if (!(status & 1)) 2598c2ecf20Sopenharmony_ci continue; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci irq_no = irq_linear_revmap(priv->domain, 2628c2ecf20Sopenharmony_ci syswake_to_hwirq(syswake)); 2638c2ecf20Sopenharmony_ci generic_handle_irq(irq_no); 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_cistatic void pdc_intc_setup(struct pdc_intc_priv *priv) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci int i; 2708c2ecf20Sopenharmony_ci unsigned int soc_sys_wake_regoff; 2718c2ecf20Sopenharmony_ci unsigned int soc_sys_wake; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* 2748c2ecf20Sopenharmony_ci * Mask all syswake interrupts before routing, or we could receive an 2758c2ecf20Sopenharmony_ci * interrupt before we're ready to handle it. 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci pdc_write(priv, PDC_IRQ_ENABLE, 0); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* 2808c2ecf20Sopenharmony_ci * Enable routing of all syswakes 2818c2ecf20Sopenharmony_ci * Disable all wake sources 2828c2ecf20Sopenharmony_ci */ 2838c2ecf20Sopenharmony_ci priv->irq_route = ((PDC_IRQ_ROUTE_EXT_EN_SYS0 << priv->nr_syswakes) - 2848c2ecf20Sopenharmony_ci PDC_IRQ_ROUTE_EXT_EN_SYS0); 2858c2ecf20Sopenharmony_ci pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci /* Initialise syswake IRQ */ 2888c2ecf20Sopenharmony_ci for (i = 0; i < priv->nr_syswakes; ++i) { 2898c2ecf20Sopenharmony_ci /* set the IRQ mode to none */ 2908c2ecf20Sopenharmony_ci soc_sys_wake_regoff = PDC_SYS_WAKE_BASE + i*PDC_SYS_WAKE_STRIDE; 2918c2ecf20Sopenharmony_ci soc_sys_wake = PDC_SYS_WAKE_INT_NONE 2928c2ecf20Sopenharmony_ci << PDC_SYS_WAKE_INT_MODE_SHIFT; 2938c2ecf20Sopenharmony_ci pdc_write(priv, soc_sys_wake_regoff, soc_sys_wake); 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int pdc_intc_probe(struct platform_device *pdev) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct pdc_intc_priv *priv; 3008c2ecf20Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 3018c2ecf20Sopenharmony_ci struct resource *res_regs; 3028c2ecf20Sopenharmony_ci struct irq_chip_generic *gc; 3038c2ecf20Sopenharmony_ci unsigned int i; 3048c2ecf20Sopenharmony_ci int irq, ret; 3058c2ecf20Sopenharmony_ci u32 val; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if (!node) 3088c2ecf20Sopenharmony_ci return -ENOENT; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* Get registers */ 3118c2ecf20Sopenharmony_ci res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 3128c2ecf20Sopenharmony_ci if (res_regs == NULL) { 3138c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot find registers resource\n"); 3148c2ecf20Sopenharmony_ci return -ENOENT; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* Allocate driver data */ 3188c2ecf20Sopenharmony_ci priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 3198c2ecf20Sopenharmony_ci if (!priv) { 3208c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot allocate device data\n"); 3218c2ecf20Sopenharmony_ci return -ENOMEM; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci raw_spin_lock_init(&priv->lock); 3248c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, priv); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci /* Ioremap the registers */ 3278c2ecf20Sopenharmony_ci priv->pdc_base = devm_ioremap(&pdev->dev, res_regs->start, 3288c2ecf20Sopenharmony_ci resource_size(res_regs)); 3298c2ecf20Sopenharmony_ci if (!priv->pdc_base) 3308c2ecf20Sopenharmony_ci return -EIO; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci /* Get number of peripherals */ 3338c2ecf20Sopenharmony_ci ret = of_property_read_u32(node, "num-perips", &val); 3348c2ecf20Sopenharmony_ci if (ret) { 3358c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No num-perips node property found\n"); 3368c2ecf20Sopenharmony_ci return -EINVAL; 3378c2ecf20Sopenharmony_ci } 3388c2ecf20Sopenharmony_ci if (val > SYS0_HWIRQ) { 3398c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "num-perips (%u) out of range\n", val); 3408c2ecf20Sopenharmony_ci return -EINVAL; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci priv->nr_perips = val; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* Get number of syswakes */ 3458c2ecf20Sopenharmony_ci ret = of_property_read_u32(node, "num-syswakes", &val); 3468c2ecf20Sopenharmony_ci if (ret) { 3478c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "No num-syswakes node property found\n"); 3488c2ecf20Sopenharmony_ci return -EINVAL; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci if (val > SYS0_HWIRQ) { 3518c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "num-syswakes (%u) out of range\n", val); 3528c2ecf20Sopenharmony_ci return -EINVAL; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci priv->nr_syswakes = val; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* Get peripheral IRQ numbers */ 3578c2ecf20Sopenharmony_ci priv->perip_irqs = devm_kcalloc(&pdev->dev, 4, priv->nr_perips, 3588c2ecf20Sopenharmony_ci GFP_KERNEL); 3598c2ecf20Sopenharmony_ci if (!priv->perip_irqs) { 3608c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot allocate perip IRQ list\n"); 3618c2ecf20Sopenharmony_ci return -ENOMEM; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci for (i = 0; i < priv->nr_perips; ++i) { 3648c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 1 + i); 3658c2ecf20Sopenharmony_ci if (irq < 0) 3668c2ecf20Sopenharmony_ci return irq; 3678c2ecf20Sopenharmony_ci priv->perip_irqs[i] = irq; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci /* check if too many were provided */ 3708c2ecf20Sopenharmony_ci if (platform_get_irq(pdev, 1 + i) >= 0) { 3718c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "surplus perip IRQs detected\n"); 3728c2ecf20Sopenharmony_ci return -EINVAL; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci /* Get syswake IRQ number */ 3768c2ecf20Sopenharmony_ci irq = platform_get_irq(pdev, 0); 3778c2ecf20Sopenharmony_ci if (irq < 0) 3788c2ecf20Sopenharmony_ci return irq; 3798c2ecf20Sopenharmony_ci priv->syswake_irq = irq; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci /* Set up an IRQ domain */ 3828c2ecf20Sopenharmony_ci priv->domain = irq_domain_add_linear(node, 16, &irq_generic_chip_ops, 3838c2ecf20Sopenharmony_ci priv); 3848c2ecf20Sopenharmony_ci if (unlikely(!priv->domain)) { 3858c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot add IRQ domain\n"); 3868c2ecf20Sopenharmony_ci return -ENOMEM; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* 3908c2ecf20Sopenharmony_ci * Set up 2 generic irq chips with 2 chip types. 3918c2ecf20Sopenharmony_ci * The first one for peripheral irqs (only 1 chip type used) 3928c2ecf20Sopenharmony_ci * The second one for syswake irqs (edge and level chip types) 3938c2ecf20Sopenharmony_ci */ 3948c2ecf20Sopenharmony_ci ret = irq_alloc_domain_generic_chips(priv->domain, 8, 2, "pdc", 3958c2ecf20Sopenharmony_ci handle_level_irq, 0, 0, 3968c2ecf20Sopenharmony_ci IRQ_GC_INIT_NESTED_LOCK); 3978c2ecf20Sopenharmony_ci if (ret) 3988c2ecf20Sopenharmony_ci goto err_generic; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci /* peripheral interrupt chip */ 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci gc = irq_get_domain_generic_chip(priv->domain, 0); 4038c2ecf20Sopenharmony_ci gc->unused = ~(BIT(priv->nr_perips) - 1); 4048c2ecf20Sopenharmony_ci gc->reg_base = priv->pdc_base; 4058c2ecf20Sopenharmony_ci /* 4068c2ecf20Sopenharmony_ci * IRQ_ROUTE contains wake bits, so we can't use the generic versions as 4078c2ecf20Sopenharmony_ci * they cache the mask 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_ci gc->chip_types[0].regs.mask = PDC_IRQ_ROUTE; 4108c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_mask = perip_irq_mask; 4118c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_unmask = perip_irq_unmask; 4128c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_set_wake = pdc_irq_set_wake; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* syswake interrupt chip */ 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci gc = irq_get_domain_generic_chip(priv->domain, 8); 4178c2ecf20Sopenharmony_ci gc->unused = ~(BIT(priv->nr_syswakes) - 1); 4188c2ecf20Sopenharmony_ci gc->reg_base = priv->pdc_base; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* edge interrupts */ 4218c2ecf20Sopenharmony_ci gc->chip_types[0].type = IRQ_TYPE_EDGE_BOTH; 4228c2ecf20Sopenharmony_ci gc->chip_types[0].handler = handle_edge_irq; 4238c2ecf20Sopenharmony_ci gc->chip_types[0].regs.ack = PDC_IRQ_CLEAR; 4248c2ecf20Sopenharmony_ci gc->chip_types[0].regs.mask = PDC_IRQ_ENABLE; 4258c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; 4268c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; 4278c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; 4288c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_set_type = syswake_irq_set_type; 4298c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_set_wake = pdc_irq_set_wake; 4308c2ecf20Sopenharmony_ci /* for standby we pass on to the shared syswake IRQ */ 4318c2ecf20Sopenharmony_ci gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci /* level interrupts */ 4348c2ecf20Sopenharmony_ci gc->chip_types[1].type = IRQ_TYPE_LEVEL_MASK; 4358c2ecf20Sopenharmony_ci gc->chip_types[1].handler = handle_level_irq; 4368c2ecf20Sopenharmony_ci gc->chip_types[1].regs.ack = PDC_IRQ_CLEAR; 4378c2ecf20Sopenharmony_ci gc->chip_types[1].regs.mask = PDC_IRQ_ENABLE; 4388c2ecf20Sopenharmony_ci gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit; 4398c2ecf20Sopenharmony_ci gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit; 4408c2ecf20Sopenharmony_ci gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit; 4418c2ecf20Sopenharmony_ci gc->chip_types[1].chip.irq_set_type = syswake_irq_set_type; 4428c2ecf20Sopenharmony_ci gc->chip_types[1].chip.irq_set_wake = pdc_irq_set_wake; 4438c2ecf20Sopenharmony_ci /* for standby we pass on to the shared syswake IRQ */ 4448c2ecf20Sopenharmony_ci gc->chip_types[1].chip.flags = IRQCHIP_MASK_ON_SUSPEND; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci /* Set up the hardware to enable interrupt routing */ 4478c2ecf20Sopenharmony_ci pdc_intc_setup(priv); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci /* Setup chained handlers for the peripheral IRQs */ 4508c2ecf20Sopenharmony_ci for (i = 0; i < priv->nr_perips; ++i) { 4518c2ecf20Sopenharmony_ci irq = priv->perip_irqs[i]; 4528c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(irq, pdc_intc_perip_isr, 4538c2ecf20Sopenharmony_ci priv); 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* Setup chained handler for the syswake IRQ */ 4578c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(priv->syswake_irq, 4588c2ecf20Sopenharmony_ci pdc_intc_syswake_isr, priv); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 4618c2ecf20Sopenharmony_ci "PDC IRQ controller initialised (%u perip IRQs, %u syswake IRQs)\n", 4628c2ecf20Sopenharmony_ci priv->nr_perips, 4638c2ecf20Sopenharmony_ci priv->nr_syswakes); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci return 0; 4668c2ecf20Sopenharmony_cierr_generic: 4678c2ecf20Sopenharmony_ci irq_domain_remove(priv->domain); 4688c2ecf20Sopenharmony_ci return ret; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic int pdc_intc_remove(struct platform_device *pdev) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci struct pdc_intc_priv *priv = platform_get_drvdata(pdev); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci irq_domain_remove(priv->domain); 4768c2ecf20Sopenharmony_ci return 0; 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_cistatic const struct of_device_id pdc_intc_match[] = { 4808c2ecf20Sopenharmony_ci { .compatible = "img,pdc-intc" }, 4818c2ecf20Sopenharmony_ci {} 4828c2ecf20Sopenharmony_ci}; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cistatic struct platform_driver pdc_intc_driver = { 4858c2ecf20Sopenharmony_ci .driver = { 4868c2ecf20Sopenharmony_ci .name = "pdc-intc", 4878c2ecf20Sopenharmony_ci .of_match_table = pdc_intc_match, 4888c2ecf20Sopenharmony_ci }, 4898c2ecf20Sopenharmony_ci .probe = pdc_intc_probe, 4908c2ecf20Sopenharmony_ci .remove = pdc_intc_remove, 4918c2ecf20Sopenharmony_ci}; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cistatic int __init pdc_intc_init(void) 4948c2ecf20Sopenharmony_ci{ 4958c2ecf20Sopenharmony_ci return platform_driver_register(&pdc_intc_driver); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_cicore_initcall(pdc_intc_init); 498