1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2b6ef9161SJames Hogan /* 3b6ef9161SJames Hogan * IMG PowerDown Controller (PDC) 4b6ef9161SJames Hogan * 5b6ef9161SJames Hogan * Copyright 2010-2013 Imagination Technologies Ltd. 6b6ef9161SJames Hogan * 7b6ef9161SJames Hogan * Exposes the syswake and PDC peripheral wake interrupts to the system. 8b6ef9161SJames Hogan * 9b6ef9161SJames Hogan */ 10b6ef9161SJames Hogan 11b6ef9161SJames Hogan #include <linux/bitops.h> 12b6ef9161SJames Hogan #include <linux/interrupt.h> 13b6ef9161SJames Hogan #include <linux/irqdomain.h> 14b6ef9161SJames Hogan #include <linux/io.h> 15b6ef9161SJames Hogan #include <linux/kernel.h> 16b6ef9161SJames Hogan #include <linux/of.h> 17b6ef9161SJames Hogan #include <linux/platform_device.h> 18b6ef9161SJames Hogan #include <linux/spinlock.h> 19b6ef9161SJames Hogan 20b6ef9161SJames Hogan /* PDC interrupt register numbers */ 21b6ef9161SJames Hogan 22b6ef9161SJames Hogan #define PDC_IRQ_STATUS 0x310 23b6ef9161SJames Hogan #define PDC_IRQ_ENABLE 0x314 24b6ef9161SJames Hogan #define PDC_IRQ_CLEAR 0x318 25b6ef9161SJames Hogan #define PDC_IRQ_ROUTE 0x31c 26b6ef9161SJames Hogan #define PDC_SYS_WAKE_BASE 0x330 27b6ef9161SJames Hogan #define PDC_SYS_WAKE_STRIDE 0x8 28b6ef9161SJames Hogan #define PDC_SYS_WAKE_CONFIG_BASE 0x334 29b6ef9161SJames Hogan #define PDC_SYS_WAKE_CONFIG_STRIDE 0x8 30b6ef9161SJames Hogan 31b6ef9161SJames Hogan /* PDC interrupt register field masks */ 32b6ef9161SJames Hogan 33b6ef9161SJames Hogan #define PDC_IRQ_SYS3 0x08 34b6ef9161SJames Hogan #define PDC_IRQ_SYS2 0x04 35b6ef9161SJames Hogan #define PDC_IRQ_SYS1 0x02 36b6ef9161SJames Hogan #define PDC_IRQ_SYS0 0x01 37b6ef9161SJames Hogan #define PDC_IRQ_ROUTE_WU_EN_SYS3 0x08000000 38b6ef9161SJames Hogan #define PDC_IRQ_ROUTE_WU_EN_SYS2 0x04000000 39b6ef9161SJames Hogan #define PDC_IRQ_ROUTE_WU_EN_SYS1 0x02000000 40b6ef9161SJames Hogan #define PDC_IRQ_ROUTE_WU_EN_SYS0 0x01000000 41b6ef9161SJames Hogan #define PDC_IRQ_ROUTE_WU_EN_WD 0x00040000 42b6ef9161SJames Hogan #define PDC_IRQ_ROUTE_WU_EN_IR 0x00020000 43b6ef9161SJames Hogan #define PDC_IRQ_ROUTE_WU_EN_RTC 0x00010000 44b6ef9161SJames Hogan #define PDC_IRQ_ROUTE_EXT_EN_SYS3 0x00000800 45b6ef9161SJames Hogan #define PDC_IRQ_ROUTE_EXT_EN_SYS2 0x00000400 46b6ef9161SJames Hogan #define PDC_IRQ_ROUTE_EXT_EN_SYS1 0x00000200 47b6ef9161SJames Hogan #define PDC_IRQ_ROUTE_EXT_EN_SYS0 0x00000100 48b6ef9161SJames Hogan #define PDC_IRQ_ROUTE_EXT_EN_WD 0x00000004 49b6ef9161SJames Hogan #define PDC_IRQ_ROUTE_EXT_EN_IR 0x00000002 50b6ef9161SJames Hogan #define PDC_IRQ_ROUTE_EXT_EN_RTC 0x00000001 51b6ef9161SJames Hogan #define PDC_SYS_WAKE_RESET 0x00000010 52b6ef9161SJames Hogan #define PDC_SYS_WAKE_INT_MODE 0x0000000e 53b6ef9161SJames Hogan #define PDC_SYS_WAKE_INT_MODE_SHIFT 1 54b6ef9161SJames Hogan #define PDC_SYS_WAKE_PIN_VAL 0x00000001 55b6ef9161SJames Hogan 56b6ef9161SJames Hogan /* PDC interrupt constants */ 57b6ef9161SJames Hogan 58b6ef9161SJames Hogan #define PDC_SYS_WAKE_INT_LOW 0x0 59b6ef9161SJames Hogan #define PDC_SYS_WAKE_INT_HIGH 0x1 60b6ef9161SJames Hogan #define PDC_SYS_WAKE_INT_DOWN 0x2 61b6ef9161SJames Hogan #define PDC_SYS_WAKE_INT_UP 0x3 62b6ef9161SJames Hogan #define PDC_SYS_WAKE_INT_CHANGE 0x6 63b6ef9161SJames Hogan #define PDC_SYS_WAKE_INT_NONE 0x4 64b6ef9161SJames Hogan 65b6ef9161SJames Hogan /** 66b6ef9161SJames Hogan * struct pdc_intc_priv - private pdc interrupt data. 67b6ef9161SJames Hogan * @nr_perips: Number of peripheral interrupt signals. 68b6ef9161SJames Hogan * @nr_syswakes: Number of syswake signals. 69b6ef9161SJames Hogan * @perip_irqs: List of peripheral IRQ numbers handled. 70b6ef9161SJames Hogan * @syswake_irq: Shared PDC syswake IRQ number. 71b6ef9161SJames Hogan * @domain: IRQ domain for PDC peripheral and syswake IRQs. 72b6ef9161SJames Hogan * @pdc_base: Base of PDC registers. 73b6ef9161SJames Hogan * @irq_route: Cached version of PDC_IRQ_ROUTE register. 74b6ef9161SJames Hogan * @lock: Lock to protect the PDC syswake registers and the cached 75b6ef9161SJames Hogan * values of those registers in this struct. 76b6ef9161SJames Hogan */ 77b6ef9161SJames Hogan struct pdc_intc_priv { 78b6ef9161SJames Hogan unsigned int nr_perips; 79b6ef9161SJames Hogan unsigned int nr_syswakes; 80b6ef9161SJames Hogan unsigned int *perip_irqs; 81b6ef9161SJames Hogan unsigned int syswake_irq; 82b6ef9161SJames Hogan struct irq_domain *domain; 83b6ef9161SJames Hogan void __iomem *pdc_base; 84b6ef9161SJames Hogan 85b6ef9161SJames Hogan u32 irq_route; 86b6ef9161SJames Hogan raw_spinlock_t lock; 87b6ef9161SJames Hogan }; 88b6ef9161SJames Hogan 89b6ef9161SJames Hogan static void pdc_write(struct pdc_intc_priv *priv, unsigned int reg_offs, 90b6ef9161SJames Hogan unsigned int data) 91b6ef9161SJames Hogan { 92b6ef9161SJames Hogan iowrite32(data, priv->pdc_base + reg_offs); 93b6ef9161SJames Hogan } 94b6ef9161SJames Hogan 95b6ef9161SJames Hogan static unsigned int pdc_read(struct pdc_intc_priv *priv, 96b6ef9161SJames Hogan unsigned int reg_offs) 97b6ef9161SJames Hogan { 98b6ef9161SJames Hogan return ioread32(priv->pdc_base + reg_offs); 99b6ef9161SJames Hogan } 100b6ef9161SJames Hogan 101b6ef9161SJames Hogan /* Generic IRQ callbacks */ 102b6ef9161SJames Hogan 103b6ef9161SJames Hogan #define SYS0_HWIRQ 8 104b6ef9161SJames Hogan 105b6ef9161SJames Hogan static unsigned int hwirq_is_syswake(irq_hw_number_t hw) 106b6ef9161SJames Hogan { 107b6ef9161SJames Hogan return hw >= SYS0_HWIRQ; 108b6ef9161SJames Hogan } 109b6ef9161SJames Hogan 110b6ef9161SJames Hogan static unsigned int hwirq_to_syswake(irq_hw_number_t hw) 111b6ef9161SJames Hogan { 112b6ef9161SJames Hogan return hw - SYS0_HWIRQ; 113b6ef9161SJames Hogan } 114b6ef9161SJames Hogan 115b6ef9161SJames Hogan static irq_hw_number_t syswake_to_hwirq(unsigned int syswake) 116b6ef9161SJames Hogan { 117b6ef9161SJames Hogan return SYS0_HWIRQ + syswake; 118b6ef9161SJames Hogan } 119b6ef9161SJames Hogan 120b6ef9161SJames Hogan static struct pdc_intc_priv *irqd_to_priv(struct irq_data *data) 121b6ef9161SJames Hogan { 122b6ef9161SJames Hogan return (struct pdc_intc_priv *)data->domain->host_data; 123b6ef9161SJames Hogan } 124b6ef9161SJames Hogan 125b6ef9161SJames Hogan /* 126b6ef9161SJames Hogan * perip_irq_mask() and perip_irq_unmask() use IRQ_ROUTE which also contains 127b6ef9161SJames Hogan * wake bits, therefore we cannot use the generic irqchip mask callbacks as they 128b6ef9161SJames Hogan * cache the mask. 129b6ef9161SJames Hogan */ 130b6ef9161SJames Hogan 131b6ef9161SJames Hogan static void perip_irq_mask(struct irq_data *data) 132b6ef9161SJames Hogan { 133b6ef9161SJames Hogan struct pdc_intc_priv *priv = irqd_to_priv(data); 134b6ef9161SJames Hogan 135b6ef9161SJames Hogan raw_spin_lock(&priv->lock); 136b6ef9161SJames Hogan priv->irq_route &= ~data->mask; 137b6ef9161SJames Hogan pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route); 138b6ef9161SJames Hogan raw_spin_unlock(&priv->lock); 139b6ef9161SJames Hogan } 140b6ef9161SJames Hogan 141b6ef9161SJames Hogan static void perip_irq_unmask(struct irq_data *data) 142b6ef9161SJames Hogan { 143b6ef9161SJames Hogan struct pdc_intc_priv *priv = irqd_to_priv(data); 144b6ef9161SJames Hogan 145b6ef9161SJames Hogan raw_spin_lock(&priv->lock); 146b6ef9161SJames Hogan priv->irq_route |= data->mask; 147b6ef9161SJames Hogan pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route); 148b6ef9161SJames Hogan raw_spin_unlock(&priv->lock); 149b6ef9161SJames Hogan } 150b6ef9161SJames Hogan 151b6ef9161SJames Hogan static int syswake_irq_set_type(struct irq_data *data, unsigned int flow_type) 152b6ef9161SJames Hogan { 153b6ef9161SJames Hogan struct pdc_intc_priv *priv = irqd_to_priv(data); 154b6ef9161SJames Hogan unsigned int syswake = hwirq_to_syswake(data->hwirq); 155b6ef9161SJames Hogan unsigned int irq_mode; 156b6ef9161SJames Hogan unsigned int soc_sys_wake_regoff, soc_sys_wake; 157b6ef9161SJames Hogan 158b6ef9161SJames Hogan /* translate to syswake IRQ mode */ 159b6ef9161SJames Hogan switch (flow_type) { 160b6ef9161SJames Hogan case IRQ_TYPE_EDGE_BOTH: 161b6ef9161SJames Hogan irq_mode = PDC_SYS_WAKE_INT_CHANGE; 162b6ef9161SJames Hogan break; 163b6ef9161SJames Hogan case IRQ_TYPE_EDGE_RISING: 164b6ef9161SJames Hogan irq_mode = PDC_SYS_WAKE_INT_UP; 165b6ef9161SJames Hogan break; 166b6ef9161SJames Hogan case IRQ_TYPE_EDGE_FALLING: 167b6ef9161SJames Hogan irq_mode = PDC_SYS_WAKE_INT_DOWN; 168b6ef9161SJames Hogan break; 169b6ef9161SJames Hogan case IRQ_TYPE_LEVEL_HIGH: 170b6ef9161SJames Hogan irq_mode = PDC_SYS_WAKE_INT_HIGH; 171b6ef9161SJames Hogan break; 172b6ef9161SJames Hogan case IRQ_TYPE_LEVEL_LOW: 173b6ef9161SJames Hogan irq_mode = PDC_SYS_WAKE_INT_LOW; 174b6ef9161SJames Hogan break; 175b6ef9161SJames Hogan default: 176b6ef9161SJames Hogan return -EINVAL; 177b6ef9161SJames Hogan } 178b6ef9161SJames Hogan 179b6ef9161SJames Hogan raw_spin_lock(&priv->lock); 180b6ef9161SJames Hogan 181b6ef9161SJames Hogan /* set the IRQ mode */ 182b6ef9161SJames Hogan soc_sys_wake_regoff = PDC_SYS_WAKE_BASE + syswake*PDC_SYS_WAKE_STRIDE; 183b6ef9161SJames Hogan soc_sys_wake = pdc_read(priv, soc_sys_wake_regoff); 184b6ef9161SJames Hogan soc_sys_wake &= ~PDC_SYS_WAKE_INT_MODE; 185b6ef9161SJames Hogan soc_sys_wake |= irq_mode << PDC_SYS_WAKE_INT_MODE_SHIFT; 186b6ef9161SJames Hogan pdc_write(priv, soc_sys_wake_regoff, soc_sys_wake); 187b6ef9161SJames Hogan 188b6ef9161SJames Hogan /* and update the handler */ 189b6ef9161SJames Hogan irq_setup_alt_chip(data, flow_type); 190b6ef9161SJames Hogan 191b6ef9161SJames Hogan raw_spin_unlock(&priv->lock); 192b6ef9161SJames Hogan 193b6ef9161SJames Hogan return 0; 194b6ef9161SJames Hogan } 195b6ef9161SJames Hogan 196b6ef9161SJames Hogan /* applies to both peripheral and syswake interrupts */ 197b6ef9161SJames Hogan static int pdc_irq_set_wake(struct irq_data *data, unsigned int on) 198b6ef9161SJames Hogan { 199b6ef9161SJames Hogan struct pdc_intc_priv *priv = irqd_to_priv(data); 200b6ef9161SJames Hogan irq_hw_number_t hw = data->hwirq; 201b6ef9161SJames Hogan unsigned int mask = (1 << 16) << hw; 202b6ef9161SJames Hogan unsigned int dst_irq; 203b6ef9161SJames Hogan 204b6ef9161SJames Hogan raw_spin_lock(&priv->lock); 205b6ef9161SJames Hogan if (on) 206b6ef9161SJames Hogan priv->irq_route |= mask; 207b6ef9161SJames Hogan else 208b6ef9161SJames Hogan priv->irq_route &= ~mask; 209b6ef9161SJames Hogan pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route); 210b6ef9161SJames Hogan raw_spin_unlock(&priv->lock); 211b6ef9161SJames Hogan 212b6ef9161SJames Hogan /* control the destination IRQ wakeup too for standby mode */ 213b6ef9161SJames Hogan if (hwirq_is_syswake(hw)) 214b6ef9161SJames Hogan dst_irq = priv->syswake_irq; 215b6ef9161SJames Hogan else 216b6ef9161SJames Hogan dst_irq = priv->perip_irqs[hw]; 217b6ef9161SJames Hogan irq_set_irq_wake(dst_irq, on); 218b6ef9161SJames Hogan 219b6ef9161SJames Hogan return 0; 220b6ef9161SJames Hogan } 221b6ef9161SJames Hogan 222bd0b9ac4SThomas Gleixner static void pdc_intc_perip_isr(struct irq_desc *desc) 223b6ef9161SJames Hogan { 22499705f99SThomas Gleixner unsigned int irq = irq_desc_get_irq(desc); 225b6ef9161SJames Hogan struct pdc_intc_priv *priv; 226046a6ee2SMarc Zyngier unsigned int i; 227b6ef9161SJames Hogan 228b6ef9161SJames Hogan priv = (struct pdc_intc_priv *)irq_desc_get_handler_data(desc); 229b6ef9161SJames Hogan 230b6ef9161SJames Hogan /* find the peripheral number */ 231b6ef9161SJames Hogan for (i = 0; i < priv->nr_perips; ++i) 232b6ef9161SJames Hogan if (irq == priv->perip_irqs[i]) 233b6ef9161SJames Hogan goto found; 234b6ef9161SJames Hogan 235b6ef9161SJames Hogan /* should never get here */ 236b6ef9161SJames Hogan return; 237b6ef9161SJames Hogan found: 238b6ef9161SJames Hogan 239b6ef9161SJames Hogan /* pass on the interrupt */ 240046a6ee2SMarc Zyngier generic_handle_domain_irq(priv->domain, i); 241b6ef9161SJames Hogan } 242b6ef9161SJames Hogan 243bd0b9ac4SThomas Gleixner static void pdc_intc_syswake_isr(struct irq_desc *desc) 244b6ef9161SJames Hogan { 245b6ef9161SJames Hogan struct pdc_intc_priv *priv; 246046a6ee2SMarc Zyngier unsigned int syswake; 247b6ef9161SJames Hogan unsigned int status; 248b6ef9161SJames Hogan 249b6ef9161SJames Hogan priv = (struct pdc_intc_priv *)irq_desc_get_handler_data(desc); 250b6ef9161SJames Hogan 251b6ef9161SJames Hogan status = pdc_read(priv, PDC_IRQ_STATUS) & 252b6ef9161SJames Hogan pdc_read(priv, PDC_IRQ_ENABLE); 253b6ef9161SJames Hogan status &= (1 << priv->nr_syswakes) - 1; 254b6ef9161SJames Hogan 255b6ef9161SJames Hogan for (syswake = 0; status; status >>= 1, ++syswake) { 256b6ef9161SJames Hogan /* Has this sys_wake triggered? */ 257b6ef9161SJames Hogan if (!(status & 1)) 258b6ef9161SJames Hogan continue; 259b6ef9161SJames Hogan 260046a6ee2SMarc Zyngier generic_handle_domain_irq(priv->domain, syswake_to_hwirq(syswake)); 261b6ef9161SJames Hogan } 262b6ef9161SJames Hogan } 263b6ef9161SJames Hogan 264b6ef9161SJames Hogan static void pdc_intc_setup(struct pdc_intc_priv *priv) 265b6ef9161SJames Hogan { 266b6ef9161SJames Hogan int i; 267b6ef9161SJames Hogan unsigned int soc_sys_wake_regoff; 268b6ef9161SJames Hogan unsigned int soc_sys_wake; 269b6ef9161SJames Hogan 270b6ef9161SJames Hogan /* 271b6ef9161SJames Hogan * Mask all syswake interrupts before routing, or we could receive an 272b6ef9161SJames Hogan * interrupt before we're ready to handle it. 273b6ef9161SJames Hogan */ 274b6ef9161SJames Hogan pdc_write(priv, PDC_IRQ_ENABLE, 0); 275b6ef9161SJames Hogan 276b6ef9161SJames Hogan /* 277b6ef9161SJames Hogan * Enable routing of all syswakes 278b6ef9161SJames Hogan * Disable all wake sources 279b6ef9161SJames Hogan */ 280b6ef9161SJames Hogan priv->irq_route = ((PDC_IRQ_ROUTE_EXT_EN_SYS0 << priv->nr_syswakes) - 281b6ef9161SJames Hogan PDC_IRQ_ROUTE_EXT_EN_SYS0); 282b6ef9161SJames Hogan pdc_write(priv, PDC_IRQ_ROUTE, priv->irq_route); 283b6ef9161SJames Hogan 284b6ef9161SJames Hogan /* Initialise syswake IRQ */ 285b6ef9161SJames Hogan for (i = 0; i < priv->nr_syswakes; ++i) { 286b6ef9161SJames Hogan /* set the IRQ mode to none */ 287b6ef9161SJames Hogan soc_sys_wake_regoff = PDC_SYS_WAKE_BASE + i*PDC_SYS_WAKE_STRIDE; 288b6ef9161SJames Hogan soc_sys_wake = PDC_SYS_WAKE_INT_NONE 289b6ef9161SJames Hogan << PDC_SYS_WAKE_INT_MODE_SHIFT; 290b6ef9161SJames Hogan pdc_write(priv, soc_sys_wake_regoff, soc_sys_wake); 291b6ef9161SJames Hogan } 292b6ef9161SJames Hogan } 293b6ef9161SJames Hogan 294b6ef9161SJames Hogan static int pdc_intc_probe(struct platform_device *pdev) 295b6ef9161SJames Hogan { 296b6ef9161SJames Hogan struct pdc_intc_priv *priv; 297b6ef9161SJames Hogan struct device_node *node = pdev->dev.of_node; 298b6ef9161SJames Hogan struct resource *res_regs; 299b6ef9161SJames Hogan struct irq_chip_generic *gc; 300b6ef9161SJames Hogan unsigned int i; 301b6ef9161SJames Hogan int irq, ret; 302b6ef9161SJames Hogan u32 val; 303b6ef9161SJames Hogan 304b6ef9161SJames Hogan if (!node) 305b6ef9161SJames Hogan return -ENOENT; 306b6ef9161SJames Hogan 307b6ef9161SJames Hogan /* Get registers */ 308b6ef9161SJames Hogan res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 309b6ef9161SJames Hogan if (res_regs == NULL) { 310b6ef9161SJames Hogan dev_err(&pdev->dev, "cannot find registers resource\n"); 311b6ef9161SJames Hogan return -ENOENT; 312b6ef9161SJames Hogan } 313b6ef9161SJames Hogan 314b6ef9161SJames Hogan /* Allocate driver data */ 315b6ef9161SJames Hogan priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 316*e3f389edSZhen Lei if (!priv) 317b6ef9161SJames Hogan return -ENOMEM; 318b6ef9161SJames Hogan raw_spin_lock_init(&priv->lock); 319b6ef9161SJames Hogan platform_set_drvdata(pdev, priv); 320b6ef9161SJames Hogan 321b6ef9161SJames Hogan /* Ioremap the registers */ 322b6ef9161SJames Hogan priv->pdc_base = devm_ioremap(&pdev->dev, res_regs->start, 323328bf1b2SVasyl Gomonovych resource_size(res_regs)); 324b6ef9161SJames Hogan if (!priv->pdc_base) 325b6ef9161SJames Hogan return -EIO; 326b6ef9161SJames Hogan 327b6ef9161SJames Hogan /* Get number of peripherals */ 328b6ef9161SJames Hogan ret = of_property_read_u32(node, "num-perips", &val); 329b6ef9161SJames Hogan if (ret) { 330b6ef9161SJames Hogan dev_err(&pdev->dev, "No num-perips node property found\n"); 331b6ef9161SJames Hogan return -EINVAL; 332b6ef9161SJames Hogan } 333b6ef9161SJames Hogan if (val > SYS0_HWIRQ) { 334b6ef9161SJames Hogan dev_err(&pdev->dev, "num-perips (%u) out of range\n", val); 335b6ef9161SJames Hogan return -EINVAL; 336b6ef9161SJames Hogan } 337b6ef9161SJames Hogan priv->nr_perips = val; 338b6ef9161SJames Hogan 339b6ef9161SJames Hogan /* Get number of syswakes */ 340b6ef9161SJames Hogan ret = of_property_read_u32(node, "num-syswakes", &val); 341b6ef9161SJames Hogan if (ret) { 342b6ef9161SJames Hogan dev_err(&pdev->dev, "No num-syswakes node property found\n"); 343b6ef9161SJames Hogan return -EINVAL; 344b6ef9161SJames Hogan } 345b6ef9161SJames Hogan if (val > SYS0_HWIRQ) { 346b6ef9161SJames Hogan dev_err(&pdev->dev, "num-syswakes (%u) out of range\n", val); 347b6ef9161SJames Hogan return -EINVAL; 348b6ef9161SJames Hogan } 349b6ef9161SJames Hogan priv->nr_syswakes = val; 350b6ef9161SJames Hogan 351b6ef9161SJames Hogan /* Get peripheral IRQ numbers */ 352a86854d0SKees Cook priv->perip_irqs = devm_kcalloc(&pdev->dev, 4, priv->nr_perips, 353b6ef9161SJames Hogan GFP_KERNEL); 354*e3f389edSZhen Lei if (!priv->perip_irqs) 355b6ef9161SJames Hogan return -ENOMEM; 356b6ef9161SJames Hogan for (i = 0; i < priv->nr_perips; ++i) { 357b6ef9161SJames Hogan irq = platform_get_irq(pdev, 1 + i); 3586c9050a7SStephen Boyd if (irq < 0) 359b6ef9161SJames Hogan return irq; 360b6ef9161SJames Hogan priv->perip_irqs[i] = irq; 361b6ef9161SJames Hogan } 362b6ef9161SJames Hogan /* check if too many were provided */ 363b6ef9161SJames Hogan if (platform_get_irq(pdev, 1 + i) >= 0) { 364b6ef9161SJames Hogan dev_err(&pdev->dev, "surplus perip IRQs detected\n"); 365b6ef9161SJames Hogan return -EINVAL; 366b6ef9161SJames Hogan } 367b6ef9161SJames Hogan 368b6ef9161SJames Hogan /* Get syswake IRQ number */ 369b6ef9161SJames Hogan irq = platform_get_irq(pdev, 0); 3706c9050a7SStephen Boyd if (irq < 0) 371b6ef9161SJames Hogan return irq; 372b6ef9161SJames Hogan priv->syswake_irq = irq; 373b6ef9161SJames Hogan 374b6ef9161SJames Hogan /* Set up an IRQ domain */ 375b6ef9161SJames Hogan priv->domain = irq_domain_add_linear(node, 16, &irq_generic_chip_ops, 376b6ef9161SJames Hogan priv); 377b6ef9161SJames Hogan if (unlikely(!priv->domain)) { 378b6ef9161SJames Hogan dev_err(&pdev->dev, "cannot add IRQ domain\n"); 379b6ef9161SJames Hogan return -ENOMEM; 380b6ef9161SJames Hogan } 381b6ef9161SJames Hogan 382b6ef9161SJames Hogan /* 383b6ef9161SJames Hogan * Set up 2 generic irq chips with 2 chip types. 384b6ef9161SJames Hogan * The first one for peripheral irqs (only 1 chip type used) 385b6ef9161SJames Hogan * The second one for syswake irqs (edge and level chip types) 386b6ef9161SJames Hogan */ 387b6ef9161SJames Hogan ret = irq_alloc_domain_generic_chips(priv->domain, 8, 2, "pdc", 388b6ef9161SJames Hogan handle_level_irq, 0, 0, 389b6ef9161SJames Hogan IRQ_GC_INIT_NESTED_LOCK); 390b6ef9161SJames Hogan if (ret) 391b6ef9161SJames Hogan goto err_generic; 392b6ef9161SJames Hogan 393b6ef9161SJames Hogan /* peripheral interrupt chip */ 394b6ef9161SJames Hogan 395b6ef9161SJames Hogan gc = irq_get_domain_generic_chip(priv->domain, 0); 396b6ef9161SJames Hogan gc->unused = ~(BIT(priv->nr_perips) - 1); 397b6ef9161SJames Hogan gc->reg_base = priv->pdc_base; 398b6ef9161SJames Hogan /* 399b6ef9161SJames Hogan * IRQ_ROUTE contains wake bits, so we can't use the generic versions as 400b6ef9161SJames Hogan * they cache the mask 401b6ef9161SJames Hogan */ 402b6ef9161SJames Hogan gc->chip_types[0].regs.mask = PDC_IRQ_ROUTE; 403b6ef9161SJames Hogan gc->chip_types[0].chip.irq_mask = perip_irq_mask; 404b6ef9161SJames Hogan gc->chip_types[0].chip.irq_unmask = perip_irq_unmask; 405b6ef9161SJames Hogan gc->chip_types[0].chip.irq_set_wake = pdc_irq_set_wake; 406b6ef9161SJames Hogan 407b6ef9161SJames Hogan /* syswake interrupt chip */ 408b6ef9161SJames Hogan 409b6ef9161SJames Hogan gc = irq_get_domain_generic_chip(priv->domain, 8); 410b6ef9161SJames Hogan gc->unused = ~(BIT(priv->nr_syswakes) - 1); 411b6ef9161SJames Hogan gc->reg_base = priv->pdc_base; 412b6ef9161SJames Hogan 413b6ef9161SJames Hogan /* edge interrupts */ 414b6ef9161SJames Hogan gc->chip_types[0].type = IRQ_TYPE_EDGE_BOTH; 415b6ef9161SJames Hogan gc->chip_types[0].handler = handle_edge_irq; 416b6ef9161SJames Hogan gc->chip_types[0].regs.ack = PDC_IRQ_CLEAR; 417b6ef9161SJames Hogan gc->chip_types[0].regs.mask = PDC_IRQ_ENABLE; 418b6ef9161SJames Hogan gc->chip_types[0].chip.irq_ack = irq_gc_ack_set_bit; 419b6ef9161SJames Hogan gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; 420b6ef9161SJames Hogan gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; 421b6ef9161SJames Hogan gc->chip_types[0].chip.irq_set_type = syswake_irq_set_type; 422b6ef9161SJames Hogan gc->chip_types[0].chip.irq_set_wake = pdc_irq_set_wake; 423b6ef9161SJames Hogan /* for standby we pass on to the shared syswake IRQ */ 424b6ef9161SJames Hogan gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND; 425b6ef9161SJames Hogan 426b6ef9161SJames Hogan /* level interrupts */ 427b6ef9161SJames Hogan gc->chip_types[1].type = IRQ_TYPE_LEVEL_MASK; 428b6ef9161SJames Hogan gc->chip_types[1].handler = handle_level_irq; 429b6ef9161SJames Hogan gc->chip_types[1].regs.ack = PDC_IRQ_CLEAR; 430b6ef9161SJames Hogan gc->chip_types[1].regs.mask = PDC_IRQ_ENABLE; 431b6ef9161SJames Hogan gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit; 432b6ef9161SJames Hogan gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit; 433b6ef9161SJames Hogan gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit; 434b6ef9161SJames Hogan gc->chip_types[1].chip.irq_set_type = syswake_irq_set_type; 435b6ef9161SJames Hogan gc->chip_types[1].chip.irq_set_wake = pdc_irq_set_wake; 436b6ef9161SJames Hogan /* for standby we pass on to the shared syswake IRQ */ 437b6ef9161SJames Hogan gc->chip_types[1].chip.flags = IRQCHIP_MASK_ON_SUSPEND; 438b6ef9161SJames Hogan 439b6ef9161SJames Hogan /* Set up the hardware to enable interrupt routing */ 440b6ef9161SJames Hogan pdc_intc_setup(priv); 441b6ef9161SJames Hogan 442b6ef9161SJames Hogan /* Setup chained handlers for the peripheral IRQs */ 443b6ef9161SJames Hogan for (i = 0; i < priv->nr_perips; ++i) { 444b6ef9161SJames Hogan irq = priv->perip_irqs[i]; 445bc4d2c07SThomas Gleixner irq_set_chained_handler_and_data(irq, pdc_intc_perip_isr, 446bc4d2c07SThomas Gleixner priv); 447b6ef9161SJames Hogan } 448b6ef9161SJames Hogan 449b6ef9161SJames Hogan /* Setup chained handler for the syswake IRQ */ 450bc4d2c07SThomas Gleixner irq_set_chained_handler_and_data(priv->syswake_irq, 451bc4d2c07SThomas Gleixner pdc_intc_syswake_isr, priv); 452b6ef9161SJames Hogan 453b6ef9161SJames Hogan dev_info(&pdev->dev, 454b6ef9161SJames Hogan "PDC IRQ controller initialised (%u perip IRQs, %u syswake IRQs)\n", 455b6ef9161SJames Hogan priv->nr_perips, 456b6ef9161SJames Hogan priv->nr_syswakes); 457b6ef9161SJames Hogan 458b6ef9161SJames Hogan return 0; 459b6ef9161SJames Hogan err_generic: 460b6ef9161SJames Hogan irq_domain_remove(priv->domain); 461b6ef9161SJames Hogan return ret; 462b6ef9161SJames Hogan } 463b6ef9161SJames Hogan 464b6ef9161SJames Hogan static int pdc_intc_remove(struct platform_device *pdev) 465b6ef9161SJames Hogan { 466b6ef9161SJames Hogan struct pdc_intc_priv *priv = platform_get_drvdata(pdev); 467b6ef9161SJames Hogan 468b6ef9161SJames Hogan irq_domain_remove(priv->domain); 469b6ef9161SJames Hogan return 0; 470b6ef9161SJames Hogan } 471b6ef9161SJames Hogan 472b6ef9161SJames Hogan static const struct of_device_id pdc_intc_match[] = { 473b6ef9161SJames Hogan { .compatible = "img,pdc-intc" }, 474b6ef9161SJames Hogan {} 475b6ef9161SJames Hogan }; 476b6ef9161SJames Hogan 477b6ef9161SJames Hogan static struct platform_driver pdc_intc_driver = { 478b6ef9161SJames Hogan .driver = { 479b6ef9161SJames Hogan .name = "pdc-intc", 480b6ef9161SJames Hogan .of_match_table = pdc_intc_match, 481b6ef9161SJames Hogan }, 482b6ef9161SJames Hogan .probe = pdc_intc_probe, 483b6ef9161SJames Hogan .remove = pdc_intc_remove, 484b6ef9161SJames Hogan }; 485b6ef9161SJames Hogan 486b6ef9161SJames Hogan static int __init pdc_intc_init(void) 487b6ef9161SJames Hogan { 488b6ef9161SJames Hogan return platform_driver_register(&pdc_intc_driver); 489b6ef9161SJames Hogan } 490b6ef9161SJames Hogan core_initcall(pdc_intc_init); 491