irqchip/irq-csky-mpintc: Add triger type
authorGuo Ren <ren_guo@c-sky.com>
Thu, 6 Jun 2019 07:37:31 +0000 (15:37 +0800)
committerMarc Zyngier <marc.zyngier@arm.com>
Tue, 11 Jun 2019 11:28:13 +0000 (12:28 +0100)
Support 4 triger types:
 - IRQ_TYPE_LEVEL_HIGH
 - IRQ_TYPE_LEVEL_LOW
 - IRQ_TYPE_EDGE_RISING
 - IRQ_TYPE_EDGE_FALLING

All of above could be set in DeviceTree file and it still compatible
with the old DeviceTree format.

Signed-off-by: Guo Ren <ren_guo@c-sky.com>
Cc: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
drivers/irqchip/irq-csky-mpintc.c

index c67c961ab6cc3b828aa128bf939c1c71cb8727d1..4457722cdd18a7cf47a80236038865bedca997d2 100644 (file)
@@ -32,6 +32,7 @@ static void __iomem *INTCL_base;
 #define INTCG_CIDSTR   0x1000
 
 #define INTCL_PICTLR   0x0
+#define INTCL_CFGR     0x14
 #define INTCL_SIGR     0x60
 #define INTCL_HPPIR    0x68
 #define INTCL_RDYIR    0x6c
@@ -41,6 +42,35 @@ static void __iomem *INTCL_base;
 
 static DEFINE_PER_CPU(void __iomem *, intcl_reg);
 
+static unsigned long *__trigger;
+
+#define IRQ_OFFSET(irq) ((irq < COMM_IRQ_BASE) ? irq : (irq - COMM_IRQ_BASE))
+
+#define TRIG_BYTE_OFFSET(i)    ((((i) * 2) / 32) * 4)
+#define TRIG_BIT_OFFSET(i)      (((i) * 2) % 32)
+
+#define TRIG_VAL(trigger, irq) (trigger << TRIG_BIT_OFFSET(IRQ_OFFSET(irq)))
+#define TRIG_VAL_MSK(irq)          (~(3 << TRIG_BIT_OFFSET(IRQ_OFFSET(irq))))
+
+#define TRIG_BASE(irq) \
+       (TRIG_BYTE_OFFSET(IRQ_OFFSET(irq)) + ((irq < COMM_IRQ_BASE) ? \
+       (this_cpu_read(intcl_reg) + INTCL_CFGR) : (INTCG_base + INTCG_CICFGR)))
+
+static DEFINE_SPINLOCK(setup_lock);
+static void setup_trigger(unsigned long irq, unsigned long trigger)
+{
+       unsigned int tmp;
+
+       spin_lock(&setup_lock);
+
+       /* setup trigger */
+       tmp = readl_relaxed(TRIG_BASE(irq)) & TRIG_VAL_MSK(irq);
+
+       writel_relaxed(tmp | TRIG_VAL(trigger, irq), TRIG_BASE(irq));
+
+       spin_unlock(&setup_lock);
+}
+
 static void csky_mpintc_handler(struct pt_regs *regs)
 {
        void __iomem *reg_base = this_cpu_read(intcl_reg);
@@ -56,6 +86,8 @@ static void csky_mpintc_enable(struct irq_data *d)
 {
        void __iomem *reg_base = this_cpu_read(intcl_reg);
 
+       setup_trigger(d->hwirq, __trigger[d->hwirq]);
+
        writel_relaxed(d->hwirq, reg_base + INTCL_SENR);
 }
 
@@ -73,6 +105,28 @@ static void csky_mpintc_eoi(struct irq_data *d)
        writel_relaxed(d->hwirq, reg_base + INTCL_CACR);
 }
 
+static int csky_mpintc_set_type(struct irq_data *d, unsigned int type)
+{
+       switch (type & IRQ_TYPE_SENSE_MASK) {
+       case IRQ_TYPE_LEVEL_HIGH:
+               __trigger[d->hwirq] = 0;
+               break;
+       case IRQ_TYPE_LEVEL_LOW:
+               __trigger[d->hwirq] = 1;
+               break;
+       case IRQ_TYPE_EDGE_RISING:
+               __trigger[d->hwirq] = 2;
+               break;
+       case IRQ_TYPE_EDGE_FALLING:
+               __trigger[d->hwirq] = 3;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 #ifdef CONFIG_SMP
 static int csky_irq_set_affinity(struct irq_data *d,
                                 const struct cpumask *mask_val,
@@ -105,6 +159,7 @@ static struct irq_chip csky_irq_chip = {
        .irq_eoi        = csky_mpintc_eoi,
        .irq_enable     = csky_mpintc_enable,
        .irq_disable    = csky_mpintc_disable,
+       .irq_set_type   = csky_mpintc_set_type,
 #ifdef CONFIG_SMP
        .irq_set_affinity = csky_irq_set_affinity,
 #endif
@@ -125,9 +180,26 @@ static int csky_irqdomain_map(struct irq_domain *d, unsigned int irq,
        return 0;
 }
 
+static int csky_irq_domain_xlate_cells(struct irq_domain *d,
+               struct device_node *ctrlr, const u32 *intspec,
+               unsigned int intsize, unsigned long *out_hwirq,
+               unsigned int *out_type)
+{
+       if (WARN_ON(intsize < 1))
+               return -EINVAL;
+
+       *out_hwirq = intspec[0];
+       if (intsize > 1)
+               *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
+       else
+               *out_type = IRQ_TYPE_LEVEL_HIGH;
+
+       return 0;
+}
+
 static const struct irq_domain_ops csky_irqdomain_ops = {
        .map    = csky_irqdomain_map,
-       .xlate  = irq_domain_xlate_onecell,
+       .xlate  = csky_irq_domain_xlate_cells,
 };
 
 #ifdef CONFIG_SMP
@@ -161,6 +233,10 @@ csky_mpintc_init(struct device_node *node, struct device_node *parent)
        if (ret < 0)
                nr_irq = INTC_IRQS;
 
+       __trigger  = kcalloc(nr_irq, sizeof(unsigned long), GFP_KERNEL);
+       if (__trigger == NULL)
+               return -ENXIO;
+
        if (INTCG_base == NULL) {
                INTCG_base = ioremap(mfcr("cr<31, 14>"),
                                     INTCL_SIZE*nr_cpu_ids + INTCG_SIZE);