18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Renesas INTC External IRQ Pin Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2013 Magnus Damm 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/of.h> 108c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 118c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/ioport.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/irq.h> 168c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 178c2ecf20Sopenharmony_ci#include <linux/err.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/module.h> 208c2ecf20Sopenharmony_ci#include <linux/of_device.h> 218c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define INTC_IRQPIN_REG_SENSE 0 /* ICRn */ 268c2ecf20Sopenharmony_ci#define INTC_IRQPIN_REG_PRIO 1 /* INTPRInn */ 278c2ecf20Sopenharmony_ci#define INTC_IRQPIN_REG_SOURCE 2 /* INTREQnn */ 288c2ecf20Sopenharmony_ci#define INTC_IRQPIN_REG_MASK 3 /* INTMSKnn */ 298c2ecf20Sopenharmony_ci#define INTC_IRQPIN_REG_CLEAR 4 /* INTMSKCLRnn */ 308c2ecf20Sopenharmony_ci#define INTC_IRQPIN_REG_NR_MANDATORY 5 318c2ecf20Sopenharmony_ci#define INTC_IRQPIN_REG_IRLM 5 /* ICR0 with IRLM bit (optional) */ 328c2ecf20Sopenharmony_ci#define INTC_IRQPIN_REG_NR 6 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* INTC external IRQ PIN hardware register access: 358c2ecf20Sopenharmony_ci * 368c2ecf20Sopenharmony_ci * SENSE is read-write 32-bit with 2-bits or 4-bits per IRQ (*) 378c2ecf20Sopenharmony_ci * PRIO is read-write 32-bit with 4-bits per IRQ (**) 388c2ecf20Sopenharmony_ci * SOURCE is read-only 32-bit or 8-bit with 1-bit per IRQ (***) 398c2ecf20Sopenharmony_ci * MASK is write-only 32-bit or 8-bit with 1-bit per IRQ (***) 408c2ecf20Sopenharmony_ci * CLEAR is write-only 32-bit or 8-bit with 1-bit per IRQ (***) 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * (*) May be accessed by more than one driver instance - lock needed 438c2ecf20Sopenharmony_ci * (**) Read-modify-write access by one driver instance - lock needed 448c2ecf20Sopenharmony_ci * (***) Accessed by one driver instance only - no locking needed 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistruct intc_irqpin_iomem { 488c2ecf20Sopenharmony_ci void __iomem *iomem; 498c2ecf20Sopenharmony_ci unsigned long (*read)(void __iomem *iomem); 508c2ecf20Sopenharmony_ci void (*write)(void __iomem *iomem, unsigned long data); 518c2ecf20Sopenharmony_ci int width; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistruct intc_irqpin_irq { 558c2ecf20Sopenharmony_ci int hw_irq; 568c2ecf20Sopenharmony_ci int requested_irq; 578c2ecf20Sopenharmony_ci int domain_irq; 588c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p; 598c2ecf20Sopenharmony_ci}; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct intc_irqpin_priv { 628c2ecf20Sopenharmony_ci struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR]; 638c2ecf20Sopenharmony_ci struct intc_irqpin_irq irq[INTC_IRQPIN_MAX]; 648c2ecf20Sopenharmony_ci unsigned int sense_bitfield_width; 658c2ecf20Sopenharmony_ci struct platform_device *pdev; 668c2ecf20Sopenharmony_ci struct irq_chip irq_chip; 678c2ecf20Sopenharmony_ci struct irq_domain *irq_domain; 688c2ecf20Sopenharmony_ci atomic_t wakeup_path; 698c2ecf20Sopenharmony_ci unsigned shared_irqs:1; 708c2ecf20Sopenharmony_ci u8 shared_irq_mask; 718c2ecf20Sopenharmony_ci}; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistruct intc_irqpin_config { 748c2ecf20Sopenharmony_ci int irlm_bit; /* -1 if non-existent */ 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic unsigned long intc_irqpin_read32(void __iomem *iomem) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci return ioread32(iomem); 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic unsigned long intc_irqpin_read8(void __iomem *iomem) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci return ioread8(iomem); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic void intc_irqpin_write32(void __iomem *iomem, unsigned long data) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci iowrite32(data, iomem); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void intc_irqpin_write8(void __iomem *iomem, unsigned long data) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci iowrite8(data, iomem); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic inline unsigned long intc_irqpin_read(struct intc_irqpin_priv *p, 988c2ecf20Sopenharmony_ci int reg) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci struct intc_irqpin_iomem *i = &p->iomem[reg]; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci return i->read(i->iomem); 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic inline void intc_irqpin_write(struct intc_irqpin_priv *p, 1068c2ecf20Sopenharmony_ci int reg, unsigned long data) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct intc_irqpin_iomem *i = &p->iomem[reg]; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci i->write(i->iomem, data); 1118c2ecf20Sopenharmony_ci} 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic inline unsigned long intc_irqpin_hwirq_mask(struct intc_irqpin_priv *p, 1148c2ecf20Sopenharmony_ci int reg, int hw_irq) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci return BIT((p->iomem[reg].width - 1) - hw_irq); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic inline void intc_irqpin_irq_write_hwirq(struct intc_irqpin_priv *p, 1208c2ecf20Sopenharmony_ci int reg, int hw_irq) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci intc_irqpin_write(p, reg, intc_irqpin_hwirq_mask(p, reg, hw_irq)); 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(intc_irqpin_lock); /* only used by slow path */ 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic void intc_irqpin_read_modify_write(struct intc_irqpin_priv *p, 1288c2ecf20Sopenharmony_ci int reg, int shift, 1298c2ecf20Sopenharmony_ci int width, int value) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci unsigned long flags; 1328c2ecf20Sopenharmony_ci unsigned long tmp; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci raw_spin_lock_irqsave(&intc_irqpin_lock, flags); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci tmp = intc_irqpin_read(p, reg); 1378c2ecf20Sopenharmony_ci tmp &= ~(((1 << width) - 1) << shift); 1388c2ecf20Sopenharmony_ci tmp |= value << shift; 1398c2ecf20Sopenharmony_ci intc_irqpin_write(p, reg, tmp); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci raw_spin_unlock_irqrestore(&intc_irqpin_lock, flags); 1428c2ecf20Sopenharmony_ci} 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic void intc_irqpin_mask_unmask_prio(struct intc_irqpin_priv *p, 1458c2ecf20Sopenharmony_ci int irq, int do_mask) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci /* The PRIO register is assumed to be 32-bit with fixed 4-bit fields. */ 1488c2ecf20Sopenharmony_ci int bitfield_width = 4; 1498c2ecf20Sopenharmony_ci int shift = 32 - (irq + 1) * bitfield_width; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_PRIO, 1528c2ecf20Sopenharmony_ci shift, bitfield_width, 1538c2ecf20Sopenharmony_ci do_mask ? 0 : (1 << bitfield_width) - 1); 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci /* The SENSE register is assumed to be 32-bit. */ 1598c2ecf20Sopenharmony_ci int bitfield_width = p->sense_bitfield_width; 1608c2ecf20Sopenharmony_ci int shift = 32 - (irq + 1) * bitfield_width; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci dev_dbg(&p->pdev->dev, "sense irq = %d, mode = %d\n", irq, value); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci if (value >= (1 << bitfield_width)) 1658c2ecf20Sopenharmony_ci return -EINVAL; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_SENSE, shift, 1688c2ecf20Sopenharmony_ci bitfield_width, value); 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void intc_irqpin_dbg(struct intc_irqpin_irq *i, char *str) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", 1758c2ecf20Sopenharmony_ci str, i->requested_irq, i->hw_irq, i->domain_irq); 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic void intc_irqpin_irq_enable(struct irq_data *d) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 1818c2ecf20Sopenharmony_ci int hw_irq = irqd_to_hwirq(d); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci intc_irqpin_dbg(&p->irq[hw_irq], "enable"); 1848c2ecf20Sopenharmony_ci intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic void intc_irqpin_irq_disable(struct irq_data *d) 1888c2ecf20Sopenharmony_ci{ 1898c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 1908c2ecf20Sopenharmony_ci int hw_irq = irqd_to_hwirq(d); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci intc_irqpin_dbg(&p->irq[hw_irq], "disable"); 1938c2ecf20Sopenharmony_ci intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic void intc_irqpin_shared_irq_enable(struct irq_data *d) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 1998c2ecf20Sopenharmony_ci int hw_irq = irqd_to_hwirq(d); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci intc_irqpin_dbg(&p->irq[hw_irq], "shared enable"); 2028c2ecf20Sopenharmony_ci intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci p->shared_irq_mask &= ~BIT(hw_irq); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void intc_irqpin_shared_irq_disable(struct irq_data *d) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 2108c2ecf20Sopenharmony_ci int hw_irq = irqd_to_hwirq(d); 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci intc_irqpin_dbg(&p->irq[hw_irq], "shared disable"); 2138c2ecf20Sopenharmony_ci intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci p->shared_irq_mask |= BIT(hw_irq); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic void intc_irqpin_irq_enable_force(struct irq_data *d) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 2218c2ecf20Sopenharmony_ci int irq = p->irq[irqd_to_hwirq(d)].requested_irq; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci intc_irqpin_irq_enable(d); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* enable interrupt through parent interrupt controller, 2268c2ecf20Sopenharmony_ci * assumes non-shared interrupt with 1:1 mapping 2278c2ecf20Sopenharmony_ci * needed for busted IRQs on some SoCs like sh73a0 2288c2ecf20Sopenharmony_ci */ 2298c2ecf20Sopenharmony_ci irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq)); 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic void intc_irqpin_irq_disable_force(struct irq_data *d) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 2358c2ecf20Sopenharmony_ci int irq = p->irq[irqd_to_hwirq(d)].requested_irq; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci /* disable interrupt through parent interrupt controller, 2388c2ecf20Sopenharmony_ci * assumes non-shared interrupt with 1:1 mapping 2398c2ecf20Sopenharmony_ci * needed for busted IRQs on some SoCs like sh73a0 2408c2ecf20Sopenharmony_ci */ 2418c2ecf20Sopenharmony_ci irq_get_chip(irq)->irq_mask(irq_get_irq_data(irq)); 2428c2ecf20Sopenharmony_ci intc_irqpin_irq_disable(d); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci#define INTC_IRQ_SENSE_VALID 0x10 2468c2ecf20Sopenharmony_ci#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic unsigned char intc_irqpin_sense[IRQ_TYPE_SENSE_MASK + 1] = { 2498c2ecf20Sopenharmony_ci [IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x00), 2508c2ecf20Sopenharmony_ci [IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x01), 2518c2ecf20Sopenharmony_ci [IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x02), 2528c2ecf20Sopenharmony_ci [IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x03), 2538c2ecf20Sopenharmony_ci [IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x04), 2548c2ecf20Sopenharmony_ci}; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int intc_irqpin_irq_set_type(struct irq_data *d, unsigned int type) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci unsigned char value = intc_irqpin_sense[type & IRQ_TYPE_SENSE_MASK]; 2598c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (!(value & INTC_IRQ_SENSE_VALID)) 2628c2ecf20Sopenharmony_ci return -EINVAL; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return intc_irqpin_set_sense(p, irqd_to_hwirq(d), 2658c2ecf20Sopenharmony_ci value ^ INTC_IRQ_SENSE_VALID); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_cistatic int intc_irqpin_irq_set_wake(struct irq_data *d, unsigned int on) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 2718c2ecf20Sopenharmony_ci int hw_irq = irqd_to_hwirq(d); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci irq_set_irq_wake(p->irq[hw_irq].requested_irq, on); 2748c2ecf20Sopenharmony_ci if (on) 2758c2ecf20Sopenharmony_ci atomic_inc(&p->wakeup_path); 2768c2ecf20Sopenharmony_ci else 2778c2ecf20Sopenharmony_ci atomic_dec(&p->wakeup_path); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci return 0; 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci struct intc_irqpin_irq *i = dev_id; 2858c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p = i->p; 2868c2ecf20Sopenharmony_ci unsigned long bit; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci intc_irqpin_dbg(i, "demux1"); 2898c2ecf20Sopenharmony_ci bit = intc_irqpin_hwirq_mask(p, INTC_IRQPIN_REG_SOURCE, i->hw_irq); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE) & bit) { 2928c2ecf20Sopenharmony_ci intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, ~bit); 2938c2ecf20Sopenharmony_ci intc_irqpin_dbg(i, "demux2"); 2948c2ecf20Sopenharmony_ci generic_handle_irq(i->domain_irq); 2958c2ecf20Sopenharmony_ci return IRQ_HANDLED; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci return IRQ_NONE; 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic irqreturn_t intc_irqpin_shared_irq_handler(int irq, void *dev_id) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p = dev_id; 3038c2ecf20Sopenharmony_ci unsigned int reg_source = intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE); 3048c2ecf20Sopenharmony_ci irqreturn_t status = IRQ_NONE; 3058c2ecf20Sopenharmony_ci int k; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci for (k = 0; k < 8; k++) { 3088c2ecf20Sopenharmony_ci if (reg_source & BIT(7 - k)) { 3098c2ecf20Sopenharmony_ci if (BIT(k) & p->shared_irq_mask) 3108c2ecf20Sopenharmony_ci continue; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci status |= intc_irqpin_irq_handler(irq, &p->irq[k]); 3138c2ecf20Sopenharmony_ci } 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci return status; 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/* 3208c2ecf20Sopenharmony_ci * This lock class tells lockdep that INTC External IRQ Pin irqs are in a 3218c2ecf20Sopenharmony_ci * different category than their parents, so it won't report false recursion. 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_cistatic struct lock_class_key intc_irqpin_irq_lock_class; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/* And this is for the request mutex */ 3268c2ecf20Sopenharmony_cistatic struct lock_class_key intc_irqpin_irq_request_class; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, 3298c2ecf20Sopenharmony_ci irq_hw_number_t hw) 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p = h->host_data; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci p->irq[hw].domain_irq = virq; 3348c2ecf20Sopenharmony_ci p->irq[hw].hw_irq = hw; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci intc_irqpin_dbg(&p->irq[hw], "map"); 3378c2ecf20Sopenharmony_ci irq_set_chip_data(virq, h->host_data); 3388c2ecf20Sopenharmony_ci irq_set_lockdep_class(virq, &intc_irqpin_irq_lock_class, 3398c2ecf20Sopenharmony_ci &intc_irqpin_irq_request_class); 3408c2ecf20Sopenharmony_ci irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); 3418c2ecf20Sopenharmony_ci return 0; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic const struct irq_domain_ops intc_irqpin_irq_domain_ops = { 3458c2ecf20Sopenharmony_ci .map = intc_irqpin_irq_domain_map, 3468c2ecf20Sopenharmony_ci .xlate = irq_domain_xlate_twocell, 3478c2ecf20Sopenharmony_ci}; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic const struct intc_irqpin_config intc_irqpin_irlm_r8a777x = { 3508c2ecf20Sopenharmony_ci .irlm_bit = 23, /* ICR0.IRLM0 */ 3518c2ecf20Sopenharmony_ci}; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic const struct intc_irqpin_config intc_irqpin_rmobile = { 3548c2ecf20Sopenharmony_ci .irlm_bit = -1, 3558c2ecf20Sopenharmony_ci}; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic const struct of_device_id intc_irqpin_dt_ids[] = { 3588c2ecf20Sopenharmony_ci { .compatible = "renesas,intc-irqpin", }, 3598c2ecf20Sopenharmony_ci { .compatible = "renesas,intc-irqpin-r8a7778", 3608c2ecf20Sopenharmony_ci .data = &intc_irqpin_irlm_r8a777x }, 3618c2ecf20Sopenharmony_ci { .compatible = "renesas,intc-irqpin-r8a7779", 3628c2ecf20Sopenharmony_ci .data = &intc_irqpin_irlm_r8a777x }, 3638c2ecf20Sopenharmony_ci { .compatible = "renesas,intc-irqpin-r8a7740", 3648c2ecf20Sopenharmony_ci .data = &intc_irqpin_rmobile }, 3658c2ecf20Sopenharmony_ci { .compatible = "renesas,intc-irqpin-sh73a0", 3668c2ecf20Sopenharmony_ci .data = &intc_irqpin_rmobile }, 3678c2ecf20Sopenharmony_ci {}, 3688c2ecf20Sopenharmony_ci}; 3698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic int intc_irqpin_probe(struct platform_device *pdev) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci const struct intc_irqpin_config *config; 3748c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 3758c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p; 3768c2ecf20Sopenharmony_ci struct intc_irqpin_iomem *i; 3778c2ecf20Sopenharmony_ci struct resource *io[INTC_IRQPIN_REG_NR]; 3788c2ecf20Sopenharmony_ci struct resource *irq; 3798c2ecf20Sopenharmony_ci struct irq_chip *irq_chip; 3808c2ecf20Sopenharmony_ci void (*enable_fn)(struct irq_data *d); 3818c2ecf20Sopenharmony_ci void (*disable_fn)(struct irq_data *d); 3828c2ecf20Sopenharmony_ci const char *name = dev_name(dev); 3838c2ecf20Sopenharmony_ci bool control_parent; 3848c2ecf20Sopenharmony_ci unsigned int nirqs; 3858c2ecf20Sopenharmony_ci int ref_irq; 3868c2ecf20Sopenharmony_ci int ret; 3878c2ecf20Sopenharmony_ci int k; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); 3908c2ecf20Sopenharmony_ci if (!p) 3918c2ecf20Sopenharmony_ci return -ENOMEM; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* deal with driver instance configuration */ 3948c2ecf20Sopenharmony_ci of_property_read_u32(dev->of_node, "sense-bitfield-width", 3958c2ecf20Sopenharmony_ci &p->sense_bitfield_width); 3968c2ecf20Sopenharmony_ci control_parent = of_property_read_bool(dev->of_node, "control-parent"); 3978c2ecf20Sopenharmony_ci if (!p->sense_bitfield_width) 3988c2ecf20Sopenharmony_ci p->sense_bitfield_width = 4; /* default to 4 bits */ 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci p->pdev = pdev; 4018c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, p); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci config = of_device_get_match_data(dev); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci pm_runtime_enable(dev); 4068c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* get hold of register banks */ 4098c2ecf20Sopenharmony_ci memset(io, 0, sizeof(io)); 4108c2ecf20Sopenharmony_ci for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { 4118c2ecf20Sopenharmony_ci io[k] = platform_get_resource(pdev, IORESOURCE_MEM, k); 4128c2ecf20Sopenharmony_ci if (!io[k] && k < INTC_IRQPIN_REG_NR_MANDATORY) { 4138c2ecf20Sopenharmony_ci dev_err(dev, "not enough IOMEM resources\n"); 4148c2ecf20Sopenharmony_ci ret = -EINVAL; 4158c2ecf20Sopenharmony_ci goto err0; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci /* allow any number of IRQs between 1 and INTC_IRQPIN_MAX */ 4208c2ecf20Sopenharmony_ci for (k = 0; k < INTC_IRQPIN_MAX; k++) { 4218c2ecf20Sopenharmony_ci irq = platform_get_resource(pdev, IORESOURCE_IRQ, k); 4228c2ecf20Sopenharmony_ci if (!irq) 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci p->irq[k].p = p; 4268c2ecf20Sopenharmony_ci p->irq[k].requested_irq = irq->start; 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci nirqs = k; 4308c2ecf20Sopenharmony_ci if (nirqs < 1) { 4318c2ecf20Sopenharmony_ci dev_err(dev, "not enough IRQ resources\n"); 4328c2ecf20Sopenharmony_ci ret = -EINVAL; 4338c2ecf20Sopenharmony_ci goto err0; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* ioremap IOMEM and setup read/write callbacks */ 4378c2ecf20Sopenharmony_ci for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { 4388c2ecf20Sopenharmony_ci i = &p->iomem[k]; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* handle optional registers */ 4418c2ecf20Sopenharmony_ci if (!io[k]) 4428c2ecf20Sopenharmony_ci continue; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci switch (resource_size(io[k])) { 4458c2ecf20Sopenharmony_ci case 1: 4468c2ecf20Sopenharmony_ci i->width = 8; 4478c2ecf20Sopenharmony_ci i->read = intc_irqpin_read8; 4488c2ecf20Sopenharmony_ci i->write = intc_irqpin_write8; 4498c2ecf20Sopenharmony_ci break; 4508c2ecf20Sopenharmony_ci case 4: 4518c2ecf20Sopenharmony_ci i->width = 32; 4528c2ecf20Sopenharmony_ci i->read = intc_irqpin_read32; 4538c2ecf20Sopenharmony_ci i->write = intc_irqpin_write32; 4548c2ecf20Sopenharmony_ci break; 4558c2ecf20Sopenharmony_ci default: 4568c2ecf20Sopenharmony_ci dev_err(dev, "IOMEM size mismatch\n"); 4578c2ecf20Sopenharmony_ci ret = -EINVAL; 4588c2ecf20Sopenharmony_ci goto err0; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci i->iomem = devm_ioremap(dev, io[k]->start, 4628c2ecf20Sopenharmony_ci resource_size(io[k])); 4638c2ecf20Sopenharmony_ci if (!i->iomem) { 4648c2ecf20Sopenharmony_ci dev_err(dev, "failed to remap IOMEM\n"); 4658c2ecf20Sopenharmony_ci ret = -ENXIO; 4668c2ecf20Sopenharmony_ci goto err0; 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* configure "individual IRQ mode" where needed */ 4718c2ecf20Sopenharmony_ci if (config && config->irlm_bit >= 0) { 4728c2ecf20Sopenharmony_ci if (io[INTC_IRQPIN_REG_IRLM]) 4738c2ecf20Sopenharmony_ci intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_IRLM, 4748c2ecf20Sopenharmony_ci config->irlm_bit, 1, 1); 4758c2ecf20Sopenharmony_ci else 4768c2ecf20Sopenharmony_ci dev_warn(dev, "unable to select IRLM mode\n"); 4778c2ecf20Sopenharmony_ci } 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* mask all interrupts using priority */ 4808c2ecf20Sopenharmony_ci for (k = 0; k < nirqs; k++) 4818c2ecf20Sopenharmony_ci intc_irqpin_mask_unmask_prio(p, k, 1); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* clear all pending interrupts */ 4848c2ecf20Sopenharmony_ci intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, 0x0); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci /* scan for shared interrupt lines */ 4878c2ecf20Sopenharmony_ci ref_irq = p->irq[0].requested_irq; 4888c2ecf20Sopenharmony_ci p->shared_irqs = 1; 4898c2ecf20Sopenharmony_ci for (k = 1; k < nirqs; k++) { 4908c2ecf20Sopenharmony_ci if (ref_irq != p->irq[k].requested_irq) { 4918c2ecf20Sopenharmony_ci p->shared_irqs = 0; 4928c2ecf20Sopenharmony_ci break; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* use more severe masking method if requested */ 4978c2ecf20Sopenharmony_ci if (control_parent) { 4988c2ecf20Sopenharmony_ci enable_fn = intc_irqpin_irq_enable_force; 4998c2ecf20Sopenharmony_ci disable_fn = intc_irqpin_irq_disable_force; 5008c2ecf20Sopenharmony_ci } else if (!p->shared_irqs) { 5018c2ecf20Sopenharmony_ci enable_fn = intc_irqpin_irq_enable; 5028c2ecf20Sopenharmony_ci disable_fn = intc_irqpin_irq_disable; 5038c2ecf20Sopenharmony_ci } else { 5048c2ecf20Sopenharmony_ci enable_fn = intc_irqpin_shared_irq_enable; 5058c2ecf20Sopenharmony_ci disable_fn = intc_irqpin_shared_irq_disable; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci irq_chip = &p->irq_chip; 5098c2ecf20Sopenharmony_ci irq_chip->name = "intc-irqpin"; 5108c2ecf20Sopenharmony_ci irq_chip->parent_device = dev; 5118c2ecf20Sopenharmony_ci irq_chip->irq_mask = disable_fn; 5128c2ecf20Sopenharmony_ci irq_chip->irq_unmask = enable_fn; 5138c2ecf20Sopenharmony_ci irq_chip->irq_set_type = intc_irqpin_irq_set_type; 5148c2ecf20Sopenharmony_ci irq_chip->irq_set_wake = intc_irqpin_irq_set_wake; 5158c2ecf20Sopenharmony_ci irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci p->irq_domain = irq_domain_add_simple(dev->of_node, nirqs, 0, 5188c2ecf20Sopenharmony_ci &intc_irqpin_irq_domain_ops, p); 5198c2ecf20Sopenharmony_ci if (!p->irq_domain) { 5208c2ecf20Sopenharmony_ci ret = -ENXIO; 5218c2ecf20Sopenharmony_ci dev_err(dev, "cannot initialize irq domain\n"); 5228c2ecf20Sopenharmony_ci goto err0; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (p->shared_irqs) { 5268c2ecf20Sopenharmony_ci /* request one shared interrupt */ 5278c2ecf20Sopenharmony_ci if (devm_request_irq(dev, p->irq[0].requested_irq, 5288c2ecf20Sopenharmony_ci intc_irqpin_shared_irq_handler, 5298c2ecf20Sopenharmony_ci IRQF_SHARED, name, p)) { 5308c2ecf20Sopenharmony_ci dev_err(dev, "failed to request low IRQ\n"); 5318c2ecf20Sopenharmony_ci ret = -ENOENT; 5328c2ecf20Sopenharmony_ci goto err1; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci } else { 5358c2ecf20Sopenharmony_ci /* request interrupts one by one */ 5368c2ecf20Sopenharmony_ci for (k = 0; k < nirqs; k++) { 5378c2ecf20Sopenharmony_ci if (devm_request_irq(dev, p->irq[k].requested_irq, 5388c2ecf20Sopenharmony_ci intc_irqpin_irq_handler, 0, name, 5398c2ecf20Sopenharmony_ci &p->irq[k])) { 5408c2ecf20Sopenharmony_ci dev_err(dev, "failed to request low IRQ\n"); 5418c2ecf20Sopenharmony_ci ret = -ENOENT; 5428c2ecf20Sopenharmony_ci goto err1; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* unmask all interrupts on prio level */ 5488c2ecf20Sopenharmony_ci for (k = 0; k < nirqs; k++) 5498c2ecf20Sopenharmony_ci intc_irqpin_mask_unmask_prio(p, k, 0); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci dev_info(dev, "driving %d irqs\n", nirqs); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci return 0; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cierr1: 5568c2ecf20Sopenharmony_ci irq_domain_remove(p->irq_domain); 5578c2ecf20Sopenharmony_cierr0: 5588c2ecf20Sopenharmony_ci pm_runtime_put(dev); 5598c2ecf20Sopenharmony_ci pm_runtime_disable(dev); 5608c2ecf20Sopenharmony_ci return ret; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_cistatic int intc_irqpin_remove(struct platform_device *pdev) 5648c2ecf20Sopenharmony_ci{ 5658c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p = platform_get_drvdata(pdev); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci irq_domain_remove(p->irq_domain); 5688c2ecf20Sopenharmony_ci pm_runtime_put(&pdev->dev); 5698c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 5708c2ecf20Sopenharmony_ci return 0; 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic int __maybe_unused intc_irqpin_suspend(struct device *dev) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct intc_irqpin_priv *p = dev_get_drvdata(dev); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (atomic_read(&p->wakeup_path)) 5788c2ecf20Sopenharmony_ci device_set_wakeup_path(dev); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci return 0; 5818c2ecf20Sopenharmony_ci} 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(intc_irqpin_pm_ops, intc_irqpin_suspend, NULL); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic struct platform_driver intc_irqpin_device_driver = { 5868c2ecf20Sopenharmony_ci .probe = intc_irqpin_probe, 5878c2ecf20Sopenharmony_ci .remove = intc_irqpin_remove, 5888c2ecf20Sopenharmony_ci .driver = { 5898c2ecf20Sopenharmony_ci .name = "renesas_intc_irqpin", 5908c2ecf20Sopenharmony_ci .of_match_table = intc_irqpin_dt_ids, 5918c2ecf20Sopenharmony_ci .pm = &intc_irqpin_pm_ops, 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci}; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic int __init intc_irqpin_init(void) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci return platform_driver_register(&intc_irqpin_device_driver); 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_cipostcore_initcall(intc_irqpin_init); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_cistatic void __exit intc_irqpin_exit(void) 6028c2ecf20Sopenharmony_ci{ 6038c2ecf20Sopenharmony_ci platform_driver_unregister(&intc_irqpin_device_driver); 6048c2ecf20Sopenharmony_ci} 6058c2ecf20Sopenharmony_cimodule_exit(intc_irqpin_exit); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ciMODULE_AUTHOR("Magnus Damm"); 6088c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Renesas INTC External IRQ Pin Driver"); 6098c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 610