162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Renesas INTC External IRQ Pin Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2013 Magnus Damm 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/of.h> 1062306a36Sopenharmony_ci#include <linux/platform_device.h> 1162306a36Sopenharmony_ci#include <linux/spinlock.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/ioport.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/irq.h> 1662306a36Sopenharmony_ci#include <linux/irqdomain.h> 1762306a36Sopenharmony_ci#include <linux/err.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/pm_runtime.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define INTC_IRQPIN_MAX 8 /* maximum 8 interrupts per driver instance */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define INTC_IRQPIN_REG_SENSE 0 /* ICRn */ 2562306a36Sopenharmony_ci#define INTC_IRQPIN_REG_PRIO 1 /* INTPRInn */ 2662306a36Sopenharmony_ci#define INTC_IRQPIN_REG_SOURCE 2 /* INTREQnn */ 2762306a36Sopenharmony_ci#define INTC_IRQPIN_REG_MASK 3 /* INTMSKnn */ 2862306a36Sopenharmony_ci#define INTC_IRQPIN_REG_CLEAR 4 /* INTMSKCLRnn */ 2962306a36Sopenharmony_ci#define INTC_IRQPIN_REG_NR_MANDATORY 5 3062306a36Sopenharmony_ci#define INTC_IRQPIN_REG_IRLM 5 /* ICR0 with IRLM bit (optional) */ 3162306a36Sopenharmony_ci#define INTC_IRQPIN_REG_NR 6 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* INTC external IRQ PIN hardware register access: 3462306a36Sopenharmony_ci * 3562306a36Sopenharmony_ci * SENSE is read-write 32-bit with 2-bits or 4-bits per IRQ (*) 3662306a36Sopenharmony_ci * PRIO is read-write 32-bit with 4-bits per IRQ (**) 3762306a36Sopenharmony_ci * SOURCE is read-only 32-bit or 8-bit with 1-bit per IRQ (***) 3862306a36Sopenharmony_ci * MASK is write-only 32-bit or 8-bit with 1-bit per IRQ (***) 3962306a36Sopenharmony_ci * CLEAR is write-only 32-bit or 8-bit with 1-bit per IRQ (***) 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * (*) May be accessed by more than one driver instance - lock needed 4262306a36Sopenharmony_ci * (**) Read-modify-write access by one driver instance - lock needed 4362306a36Sopenharmony_ci * (***) Accessed by one driver instance only - no locking needed 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistruct intc_irqpin_iomem { 4762306a36Sopenharmony_ci void __iomem *iomem; 4862306a36Sopenharmony_ci unsigned long (*read)(void __iomem *iomem); 4962306a36Sopenharmony_ci void (*write)(void __iomem *iomem, unsigned long data); 5062306a36Sopenharmony_ci int width; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistruct intc_irqpin_irq { 5462306a36Sopenharmony_ci int hw_irq; 5562306a36Sopenharmony_ci int requested_irq; 5662306a36Sopenharmony_ci int domain_irq; 5762306a36Sopenharmony_ci struct intc_irqpin_priv *p; 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistruct intc_irqpin_priv { 6162306a36Sopenharmony_ci struct intc_irqpin_iomem iomem[INTC_IRQPIN_REG_NR]; 6262306a36Sopenharmony_ci struct intc_irqpin_irq irq[INTC_IRQPIN_MAX]; 6362306a36Sopenharmony_ci unsigned int sense_bitfield_width; 6462306a36Sopenharmony_ci struct platform_device *pdev; 6562306a36Sopenharmony_ci struct irq_chip irq_chip; 6662306a36Sopenharmony_ci struct irq_domain *irq_domain; 6762306a36Sopenharmony_ci atomic_t wakeup_path; 6862306a36Sopenharmony_ci unsigned shared_irqs:1; 6962306a36Sopenharmony_ci u8 shared_irq_mask; 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistruct intc_irqpin_config { 7362306a36Sopenharmony_ci int irlm_bit; /* -1 if non-existent */ 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic unsigned long intc_irqpin_read32(void __iomem *iomem) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci return ioread32(iomem); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic unsigned long intc_irqpin_read8(void __iomem *iomem) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci return ioread8(iomem); 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic void intc_irqpin_write32(void __iomem *iomem, unsigned long data) 8762306a36Sopenharmony_ci{ 8862306a36Sopenharmony_ci iowrite32(data, iomem); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic void intc_irqpin_write8(void __iomem *iomem, unsigned long data) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci iowrite8(data, iomem); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic inline unsigned long intc_irqpin_read(struct intc_irqpin_priv *p, 9762306a36Sopenharmony_ci int reg) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct intc_irqpin_iomem *i = &p->iomem[reg]; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci return i->read(i->iomem); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic inline void intc_irqpin_write(struct intc_irqpin_priv *p, 10562306a36Sopenharmony_ci int reg, unsigned long data) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct intc_irqpin_iomem *i = &p->iomem[reg]; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci i->write(i->iomem, data); 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic inline unsigned long intc_irqpin_hwirq_mask(struct intc_irqpin_priv *p, 11362306a36Sopenharmony_ci int reg, int hw_irq) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci return BIT((p->iomem[reg].width - 1) - hw_irq); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic inline void intc_irqpin_irq_write_hwirq(struct intc_irqpin_priv *p, 11962306a36Sopenharmony_ci int reg, int hw_irq) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci intc_irqpin_write(p, reg, intc_irqpin_hwirq_mask(p, reg, hw_irq)); 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic DEFINE_RAW_SPINLOCK(intc_irqpin_lock); /* only used by slow path */ 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void intc_irqpin_read_modify_write(struct intc_irqpin_priv *p, 12762306a36Sopenharmony_ci int reg, int shift, 12862306a36Sopenharmony_ci int width, int value) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci unsigned long flags; 13162306a36Sopenharmony_ci unsigned long tmp; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci raw_spin_lock_irqsave(&intc_irqpin_lock, flags); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci tmp = intc_irqpin_read(p, reg); 13662306a36Sopenharmony_ci tmp &= ~(((1 << width) - 1) << shift); 13762306a36Sopenharmony_ci tmp |= value << shift; 13862306a36Sopenharmony_ci intc_irqpin_write(p, reg, tmp); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&intc_irqpin_lock, flags); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void intc_irqpin_mask_unmask_prio(struct intc_irqpin_priv *p, 14462306a36Sopenharmony_ci int irq, int do_mask) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci /* The PRIO register is assumed to be 32-bit with fixed 4-bit fields. */ 14762306a36Sopenharmony_ci int bitfield_width = 4; 14862306a36Sopenharmony_ci int shift = 32 - (irq + 1) * bitfield_width; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_PRIO, 15162306a36Sopenharmony_ci shift, bitfield_width, 15262306a36Sopenharmony_ci do_mask ? 0 : (1 << bitfield_width) - 1); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int intc_irqpin_set_sense(struct intc_irqpin_priv *p, int irq, int value) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci /* The SENSE register is assumed to be 32-bit. */ 15862306a36Sopenharmony_ci int bitfield_width = p->sense_bitfield_width; 15962306a36Sopenharmony_ci int shift = 32 - (irq + 1) * bitfield_width; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci dev_dbg(&p->pdev->dev, "sense irq = %d, mode = %d\n", irq, value); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci if (value >= (1 << bitfield_width)) 16462306a36Sopenharmony_ci return -EINVAL; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_SENSE, shift, 16762306a36Sopenharmony_ci bitfield_width, value); 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void intc_irqpin_dbg(struct intc_irqpin_irq *i, char *str) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci dev_dbg(&i->p->pdev->dev, "%s (%d:%d:%d)\n", 17462306a36Sopenharmony_ci str, i->requested_irq, i->hw_irq, i->domain_irq); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void intc_irqpin_irq_enable(struct irq_data *d) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 18062306a36Sopenharmony_ci int hw_irq = irqd_to_hwirq(d); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci intc_irqpin_dbg(&p->irq[hw_irq], "enable"); 18362306a36Sopenharmony_ci intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic void intc_irqpin_irq_disable(struct irq_data *d) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 18962306a36Sopenharmony_ci int hw_irq = irqd_to_hwirq(d); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci intc_irqpin_dbg(&p->irq[hw_irq], "disable"); 19262306a36Sopenharmony_ci intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void intc_irqpin_shared_irq_enable(struct irq_data *d) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 19862306a36Sopenharmony_ci int hw_irq = irqd_to_hwirq(d); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci intc_irqpin_dbg(&p->irq[hw_irq], "shared enable"); 20162306a36Sopenharmony_ci intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_CLEAR, hw_irq); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci p->shared_irq_mask &= ~BIT(hw_irq); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void intc_irqpin_shared_irq_disable(struct irq_data *d) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 20962306a36Sopenharmony_ci int hw_irq = irqd_to_hwirq(d); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci intc_irqpin_dbg(&p->irq[hw_irq], "shared disable"); 21262306a36Sopenharmony_ci intc_irqpin_irq_write_hwirq(p, INTC_IRQPIN_REG_MASK, hw_irq); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci p->shared_irq_mask |= BIT(hw_irq); 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void intc_irqpin_irq_enable_force(struct irq_data *d) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 22062306a36Sopenharmony_ci int irq = p->irq[irqd_to_hwirq(d)].requested_irq; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci intc_irqpin_irq_enable(d); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* enable interrupt through parent interrupt controller, 22562306a36Sopenharmony_ci * assumes non-shared interrupt with 1:1 mapping 22662306a36Sopenharmony_ci * needed for busted IRQs on some SoCs like sh73a0 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq)); 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic void intc_irqpin_irq_disable_force(struct irq_data *d) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 23462306a36Sopenharmony_ci int irq = p->irq[irqd_to_hwirq(d)].requested_irq; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* disable interrupt through parent interrupt controller, 23762306a36Sopenharmony_ci * assumes non-shared interrupt with 1:1 mapping 23862306a36Sopenharmony_ci * needed for busted IRQs on some SoCs like sh73a0 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci irq_get_chip(irq)->irq_mask(irq_get_irq_data(irq)); 24162306a36Sopenharmony_ci intc_irqpin_irq_disable(d); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci#define INTC_IRQ_SENSE_VALID 0x10 24562306a36Sopenharmony_ci#define INTC_IRQ_SENSE(x) (x + INTC_IRQ_SENSE_VALID) 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic unsigned char intc_irqpin_sense[IRQ_TYPE_SENSE_MASK + 1] = { 24862306a36Sopenharmony_ci [IRQ_TYPE_EDGE_FALLING] = INTC_IRQ_SENSE(0x00), 24962306a36Sopenharmony_ci [IRQ_TYPE_EDGE_RISING] = INTC_IRQ_SENSE(0x01), 25062306a36Sopenharmony_ci [IRQ_TYPE_LEVEL_LOW] = INTC_IRQ_SENSE(0x02), 25162306a36Sopenharmony_ci [IRQ_TYPE_LEVEL_HIGH] = INTC_IRQ_SENSE(0x03), 25262306a36Sopenharmony_ci [IRQ_TYPE_EDGE_BOTH] = INTC_IRQ_SENSE(0x04), 25362306a36Sopenharmony_ci}; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic int intc_irqpin_irq_set_type(struct irq_data *d, unsigned int type) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci unsigned char value = intc_irqpin_sense[type & IRQ_TYPE_SENSE_MASK]; 25862306a36Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (!(value & INTC_IRQ_SENSE_VALID)) 26162306a36Sopenharmony_ci return -EINVAL; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci return intc_irqpin_set_sense(p, irqd_to_hwirq(d), 26462306a36Sopenharmony_ci value ^ INTC_IRQ_SENSE_VALID); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic int intc_irqpin_irq_set_wake(struct irq_data *d, unsigned int on) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct intc_irqpin_priv *p = irq_data_get_irq_chip_data(d); 27062306a36Sopenharmony_ci int hw_irq = irqd_to_hwirq(d); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci irq_set_irq_wake(p->irq[hw_irq].requested_irq, on); 27362306a36Sopenharmony_ci if (on) 27462306a36Sopenharmony_ci atomic_inc(&p->wakeup_path); 27562306a36Sopenharmony_ci else 27662306a36Sopenharmony_ci atomic_dec(&p->wakeup_path); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci return 0; 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic irqreturn_t intc_irqpin_irq_handler(int irq, void *dev_id) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct intc_irqpin_irq *i = dev_id; 28462306a36Sopenharmony_ci struct intc_irqpin_priv *p = i->p; 28562306a36Sopenharmony_ci unsigned long bit; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci intc_irqpin_dbg(i, "demux1"); 28862306a36Sopenharmony_ci bit = intc_irqpin_hwirq_mask(p, INTC_IRQPIN_REG_SOURCE, i->hw_irq); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE) & bit) { 29162306a36Sopenharmony_ci intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, ~bit); 29262306a36Sopenharmony_ci intc_irqpin_dbg(i, "demux2"); 29362306a36Sopenharmony_ci generic_handle_irq(i->domain_irq); 29462306a36Sopenharmony_ci return IRQ_HANDLED; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci return IRQ_NONE; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic irqreturn_t intc_irqpin_shared_irq_handler(int irq, void *dev_id) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci struct intc_irqpin_priv *p = dev_id; 30262306a36Sopenharmony_ci unsigned int reg_source = intc_irqpin_read(p, INTC_IRQPIN_REG_SOURCE); 30362306a36Sopenharmony_ci irqreturn_t status = IRQ_NONE; 30462306a36Sopenharmony_ci int k; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci for (k = 0; k < 8; k++) { 30762306a36Sopenharmony_ci if (reg_source & BIT(7 - k)) { 30862306a36Sopenharmony_ci if (BIT(k) & p->shared_irq_mask) 30962306a36Sopenharmony_ci continue; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci status |= intc_irqpin_irq_handler(irq, &p->irq[k]); 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci return status; 31662306a36Sopenharmony_ci} 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci/* 31962306a36Sopenharmony_ci * This lock class tells lockdep that INTC External IRQ Pin irqs are in a 32062306a36Sopenharmony_ci * different category than their parents, so it won't report false recursion. 32162306a36Sopenharmony_ci */ 32262306a36Sopenharmony_cistatic struct lock_class_key intc_irqpin_irq_lock_class; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/* And this is for the request mutex */ 32562306a36Sopenharmony_cistatic struct lock_class_key intc_irqpin_irq_request_class; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic int intc_irqpin_irq_domain_map(struct irq_domain *h, unsigned int virq, 32862306a36Sopenharmony_ci irq_hw_number_t hw) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct intc_irqpin_priv *p = h->host_data; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci p->irq[hw].domain_irq = virq; 33362306a36Sopenharmony_ci p->irq[hw].hw_irq = hw; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci intc_irqpin_dbg(&p->irq[hw], "map"); 33662306a36Sopenharmony_ci irq_set_chip_data(virq, h->host_data); 33762306a36Sopenharmony_ci irq_set_lockdep_class(virq, &intc_irqpin_irq_lock_class, 33862306a36Sopenharmony_ci &intc_irqpin_irq_request_class); 33962306a36Sopenharmony_ci irq_set_chip_and_handler(virq, &p->irq_chip, handle_level_irq); 34062306a36Sopenharmony_ci return 0; 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic const struct irq_domain_ops intc_irqpin_irq_domain_ops = { 34462306a36Sopenharmony_ci .map = intc_irqpin_irq_domain_map, 34562306a36Sopenharmony_ci .xlate = irq_domain_xlate_twocell, 34662306a36Sopenharmony_ci}; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_cistatic const struct intc_irqpin_config intc_irqpin_irlm_r8a777x = { 34962306a36Sopenharmony_ci .irlm_bit = 23, /* ICR0.IRLM0 */ 35062306a36Sopenharmony_ci}; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic const struct intc_irqpin_config intc_irqpin_rmobile = { 35362306a36Sopenharmony_ci .irlm_bit = -1, 35462306a36Sopenharmony_ci}; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic const struct of_device_id intc_irqpin_dt_ids[] = { 35762306a36Sopenharmony_ci { .compatible = "renesas,intc-irqpin", }, 35862306a36Sopenharmony_ci { .compatible = "renesas,intc-irqpin-r8a7778", 35962306a36Sopenharmony_ci .data = &intc_irqpin_irlm_r8a777x }, 36062306a36Sopenharmony_ci { .compatible = "renesas,intc-irqpin-r8a7779", 36162306a36Sopenharmony_ci .data = &intc_irqpin_irlm_r8a777x }, 36262306a36Sopenharmony_ci { .compatible = "renesas,intc-irqpin-r8a7740", 36362306a36Sopenharmony_ci .data = &intc_irqpin_rmobile }, 36462306a36Sopenharmony_ci { .compatible = "renesas,intc-irqpin-sh73a0", 36562306a36Sopenharmony_ci .data = &intc_irqpin_rmobile }, 36662306a36Sopenharmony_ci {}, 36762306a36Sopenharmony_ci}; 36862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, intc_irqpin_dt_ids); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic int intc_irqpin_probe(struct platform_device *pdev) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci const struct intc_irqpin_config *config; 37362306a36Sopenharmony_ci struct device *dev = &pdev->dev; 37462306a36Sopenharmony_ci struct intc_irqpin_priv *p; 37562306a36Sopenharmony_ci struct intc_irqpin_iomem *i; 37662306a36Sopenharmony_ci struct resource *io[INTC_IRQPIN_REG_NR]; 37762306a36Sopenharmony_ci struct irq_chip *irq_chip; 37862306a36Sopenharmony_ci void (*enable_fn)(struct irq_data *d); 37962306a36Sopenharmony_ci void (*disable_fn)(struct irq_data *d); 38062306a36Sopenharmony_ci const char *name = dev_name(dev); 38162306a36Sopenharmony_ci bool control_parent; 38262306a36Sopenharmony_ci unsigned int nirqs; 38362306a36Sopenharmony_ci int ref_irq; 38462306a36Sopenharmony_ci int ret; 38562306a36Sopenharmony_ci int k; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); 38862306a36Sopenharmony_ci if (!p) 38962306a36Sopenharmony_ci return -ENOMEM; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* deal with driver instance configuration */ 39262306a36Sopenharmony_ci of_property_read_u32(dev->of_node, "sense-bitfield-width", 39362306a36Sopenharmony_ci &p->sense_bitfield_width); 39462306a36Sopenharmony_ci control_parent = of_property_read_bool(dev->of_node, "control-parent"); 39562306a36Sopenharmony_ci if (!p->sense_bitfield_width) 39662306a36Sopenharmony_ci p->sense_bitfield_width = 4; /* default to 4 bits */ 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci p->pdev = pdev; 39962306a36Sopenharmony_ci platform_set_drvdata(pdev, p); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci config = of_device_get_match_data(dev); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci pm_runtime_enable(dev); 40462306a36Sopenharmony_ci pm_runtime_get_sync(dev); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* get hold of register banks */ 40762306a36Sopenharmony_ci memset(io, 0, sizeof(io)); 40862306a36Sopenharmony_ci for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { 40962306a36Sopenharmony_ci io[k] = platform_get_resource(pdev, IORESOURCE_MEM, k); 41062306a36Sopenharmony_ci if (!io[k] && k < INTC_IRQPIN_REG_NR_MANDATORY) { 41162306a36Sopenharmony_ci dev_err(dev, "not enough IOMEM resources\n"); 41262306a36Sopenharmony_ci ret = -EINVAL; 41362306a36Sopenharmony_ci goto err0; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* allow any number of IRQs between 1 and INTC_IRQPIN_MAX */ 41862306a36Sopenharmony_ci for (k = 0; k < INTC_IRQPIN_MAX; k++) { 41962306a36Sopenharmony_ci ret = platform_get_irq_optional(pdev, k); 42062306a36Sopenharmony_ci if (ret == -ENXIO) 42162306a36Sopenharmony_ci break; 42262306a36Sopenharmony_ci if (ret < 0) 42362306a36Sopenharmony_ci goto err0; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci p->irq[k].p = p; 42662306a36Sopenharmony_ci p->irq[k].requested_irq = ret; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci nirqs = k; 43062306a36Sopenharmony_ci if (nirqs < 1) { 43162306a36Sopenharmony_ci dev_err(dev, "not enough IRQ resources\n"); 43262306a36Sopenharmony_ci ret = -EINVAL; 43362306a36Sopenharmony_ci goto err0; 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* ioremap IOMEM and setup read/write callbacks */ 43762306a36Sopenharmony_ci for (k = 0; k < INTC_IRQPIN_REG_NR; k++) { 43862306a36Sopenharmony_ci i = &p->iomem[k]; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci /* handle optional registers */ 44162306a36Sopenharmony_ci if (!io[k]) 44262306a36Sopenharmony_ci continue; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci switch (resource_size(io[k])) { 44562306a36Sopenharmony_ci case 1: 44662306a36Sopenharmony_ci i->width = 8; 44762306a36Sopenharmony_ci i->read = intc_irqpin_read8; 44862306a36Sopenharmony_ci i->write = intc_irqpin_write8; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci case 4: 45162306a36Sopenharmony_ci i->width = 32; 45262306a36Sopenharmony_ci i->read = intc_irqpin_read32; 45362306a36Sopenharmony_ci i->write = intc_irqpin_write32; 45462306a36Sopenharmony_ci break; 45562306a36Sopenharmony_ci default: 45662306a36Sopenharmony_ci dev_err(dev, "IOMEM size mismatch\n"); 45762306a36Sopenharmony_ci ret = -EINVAL; 45862306a36Sopenharmony_ci goto err0; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci i->iomem = devm_ioremap(dev, io[k]->start, 46262306a36Sopenharmony_ci resource_size(io[k])); 46362306a36Sopenharmony_ci if (!i->iomem) { 46462306a36Sopenharmony_ci dev_err(dev, "failed to remap IOMEM\n"); 46562306a36Sopenharmony_ci ret = -ENXIO; 46662306a36Sopenharmony_ci goto err0; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* configure "individual IRQ mode" where needed */ 47162306a36Sopenharmony_ci if (config && config->irlm_bit >= 0) { 47262306a36Sopenharmony_ci if (io[INTC_IRQPIN_REG_IRLM]) 47362306a36Sopenharmony_ci intc_irqpin_read_modify_write(p, INTC_IRQPIN_REG_IRLM, 47462306a36Sopenharmony_ci config->irlm_bit, 1, 1); 47562306a36Sopenharmony_ci else 47662306a36Sopenharmony_ci dev_warn(dev, "unable to select IRLM mode\n"); 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* mask all interrupts using priority */ 48062306a36Sopenharmony_ci for (k = 0; k < nirqs; k++) 48162306a36Sopenharmony_ci intc_irqpin_mask_unmask_prio(p, k, 1); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci /* clear all pending interrupts */ 48462306a36Sopenharmony_ci intc_irqpin_write(p, INTC_IRQPIN_REG_SOURCE, 0x0); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* scan for shared interrupt lines */ 48762306a36Sopenharmony_ci ref_irq = p->irq[0].requested_irq; 48862306a36Sopenharmony_ci p->shared_irqs = 1; 48962306a36Sopenharmony_ci for (k = 1; k < nirqs; k++) { 49062306a36Sopenharmony_ci if (ref_irq != p->irq[k].requested_irq) { 49162306a36Sopenharmony_ci p->shared_irqs = 0; 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci /* use more severe masking method if requested */ 49762306a36Sopenharmony_ci if (control_parent) { 49862306a36Sopenharmony_ci enable_fn = intc_irqpin_irq_enable_force; 49962306a36Sopenharmony_ci disable_fn = intc_irqpin_irq_disable_force; 50062306a36Sopenharmony_ci } else if (!p->shared_irqs) { 50162306a36Sopenharmony_ci enable_fn = intc_irqpin_irq_enable; 50262306a36Sopenharmony_ci disable_fn = intc_irqpin_irq_disable; 50362306a36Sopenharmony_ci } else { 50462306a36Sopenharmony_ci enable_fn = intc_irqpin_shared_irq_enable; 50562306a36Sopenharmony_ci disable_fn = intc_irqpin_shared_irq_disable; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci irq_chip = &p->irq_chip; 50962306a36Sopenharmony_ci irq_chip->name = "intc-irqpin"; 51062306a36Sopenharmony_ci irq_chip->irq_mask = disable_fn; 51162306a36Sopenharmony_ci irq_chip->irq_unmask = enable_fn; 51262306a36Sopenharmony_ci irq_chip->irq_set_type = intc_irqpin_irq_set_type; 51362306a36Sopenharmony_ci irq_chip->irq_set_wake = intc_irqpin_irq_set_wake; 51462306a36Sopenharmony_ci irq_chip->flags = IRQCHIP_MASK_ON_SUSPEND; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci p->irq_domain = irq_domain_add_simple(dev->of_node, nirqs, 0, 51762306a36Sopenharmony_ci &intc_irqpin_irq_domain_ops, p); 51862306a36Sopenharmony_ci if (!p->irq_domain) { 51962306a36Sopenharmony_ci ret = -ENXIO; 52062306a36Sopenharmony_ci dev_err(dev, "cannot initialize irq domain\n"); 52162306a36Sopenharmony_ci goto err0; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci irq_domain_set_pm_device(p->irq_domain, dev); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (p->shared_irqs) { 52762306a36Sopenharmony_ci /* request one shared interrupt */ 52862306a36Sopenharmony_ci if (devm_request_irq(dev, p->irq[0].requested_irq, 52962306a36Sopenharmony_ci intc_irqpin_shared_irq_handler, 53062306a36Sopenharmony_ci IRQF_SHARED, name, p)) { 53162306a36Sopenharmony_ci dev_err(dev, "failed to request low IRQ\n"); 53262306a36Sopenharmony_ci ret = -ENOENT; 53362306a36Sopenharmony_ci goto err1; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci } else { 53662306a36Sopenharmony_ci /* request interrupts one by one */ 53762306a36Sopenharmony_ci for (k = 0; k < nirqs; k++) { 53862306a36Sopenharmony_ci if (devm_request_irq(dev, p->irq[k].requested_irq, 53962306a36Sopenharmony_ci intc_irqpin_irq_handler, 0, name, 54062306a36Sopenharmony_ci &p->irq[k])) { 54162306a36Sopenharmony_ci dev_err(dev, "failed to request low IRQ\n"); 54262306a36Sopenharmony_ci ret = -ENOENT; 54362306a36Sopenharmony_ci goto err1; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* unmask all interrupts on prio level */ 54962306a36Sopenharmony_ci for (k = 0; k < nirqs; k++) 55062306a36Sopenharmony_ci intc_irqpin_mask_unmask_prio(p, k, 0); 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci dev_info(dev, "driving %d irqs\n", nirqs); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci return 0; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cierr1: 55762306a36Sopenharmony_ci irq_domain_remove(p->irq_domain); 55862306a36Sopenharmony_cierr0: 55962306a36Sopenharmony_ci pm_runtime_put(dev); 56062306a36Sopenharmony_ci pm_runtime_disable(dev); 56162306a36Sopenharmony_ci return ret; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int intc_irqpin_remove(struct platform_device *pdev) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct intc_irqpin_priv *p = platform_get_drvdata(pdev); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci irq_domain_remove(p->irq_domain); 56962306a36Sopenharmony_ci pm_runtime_put(&pdev->dev); 57062306a36Sopenharmony_ci pm_runtime_disable(&pdev->dev); 57162306a36Sopenharmony_ci return 0; 57262306a36Sopenharmony_ci} 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_cistatic int __maybe_unused intc_irqpin_suspend(struct device *dev) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct intc_irqpin_priv *p = dev_get_drvdata(dev); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (atomic_read(&p->wakeup_path)) 57962306a36Sopenharmony_ci device_set_wakeup_path(dev); 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci return 0; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(intc_irqpin_pm_ops, intc_irqpin_suspend, NULL); 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_cistatic struct platform_driver intc_irqpin_device_driver = { 58762306a36Sopenharmony_ci .probe = intc_irqpin_probe, 58862306a36Sopenharmony_ci .remove = intc_irqpin_remove, 58962306a36Sopenharmony_ci .driver = { 59062306a36Sopenharmony_ci .name = "renesas_intc_irqpin", 59162306a36Sopenharmony_ci .of_match_table = intc_irqpin_dt_ids, 59262306a36Sopenharmony_ci .pm = &intc_irqpin_pm_ops, 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci}; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int __init intc_irqpin_init(void) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci return platform_driver_register(&intc_irqpin_device_driver); 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_cipostcore_initcall(intc_irqpin_init); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic void __exit intc_irqpin_exit(void) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci platform_driver_unregister(&intc_irqpin_device_driver); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_cimodule_exit(intc_irqpin_exit); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ciMODULE_AUTHOR("Magnus Damm"); 60962306a36Sopenharmony_ciMODULE_DESCRIPTION("Renesas INTC External IRQ Pin Driver"); 610