Merge tag 'timers-urgent-2020-12-27' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / drivers / irqchip / irq-csky-mpintc.c
index a4c1aac..a1534ed 100644 (file)
@@ -32,8 +32,8 @@ 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
 #define INTCL_SENR     0xa0
 #define INTCL_CENR     0xa4
@@ -41,21 +41,49 @@ 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);
 
-       do {
-               handle_domain_irq(root_domain,
-                                 readl_relaxed(reg_base + INTCL_RDYIR),
-                                 regs);
-       } while (readl_relaxed(reg_base + INTCL_HPPIR) & BIT(31));
+       handle_domain_irq(root_domain,
+               readl_relaxed(reg_base + INTCL_RDYIR), regs);
 }
 
 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 +101,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,
@@ -116,6 +166,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
@@ -136,9 +187,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
@@ -172,6 +240,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);