Blame view

drivers/irqchip/irq-sun4i.c 4.47 KB
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  /*
   * Allwinner A1X SoCs IRQ chip driver.
   *
   * Copyright (C) 2012 Maxime Ripard
   *
   * Maxime Ripard <maxime.ripard@free-electrons.com>
   *
   * Based on code from
   * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
   * Benn Huang <benn@allwinnertech.com>
   *
   * This file is licensed under the terms of the GNU General Public
   * License version 2.  This program is licensed "as is" without any
   * warranty of any kind, whether express or implied.
   */
  
  #include <linux/io.h>
  #include <linux/irq.h>
41a83e06e   Joel Porquet   irqchip: Prepare ...
19
  #include <linux/irqchip.h>
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
20
21
22
23
24
  #include <linux/of.h>
  #include <linux/of_address.h>
  #include <linux/of_irq.h>
  
  #include <asm/exception.h>
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
25

d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
26
27
28
29
30
31
32
33
34
35
  #define SUN4I_IRQ_VECTOR_REG		0x00
  #define SUN4I_IRQ_PROTECTION_REG	0x08
  #define SUN4I_IRQ_NMI_CTRL_REG		0x0c
  #define SUN4I_IRQ_PENDING_REG(x)	(0x10 + 0x4 * x)
  #define SUN4I_IRQ_FIQ_PENDING_REG(x)	(0x20 + 0x4 * x)
  #define SUN4I_IRQ_ENABLE_REG(x)		(0x40 + 0x4 * x)
  #define SUN4I_IRQ_MASK_REG(x)		(0x50 + 0x4 * x)
  
  static void __iomem *sun4i_irq_base;
  static struct irq_domain *sun4i_irq_domain;
8783dd3a3   Stephen Boyd   irqchip: Remove a...
36
  static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs);
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
37

baaecfa72   Axel Lin   irqchip: sun4i: S...
38
  static void sun4i_irq_ack(struct irq_data *irqd)
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
39
40
  {
  	unsigned int irq = irqd_to_hwirq(irqd);
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
41

915b78ce8   Hans de Goede   irqchip: sun4i: U...
42
43
  	if (irq != 0)
  		return; /* Only IRQ 0 / the ENMI needs to be acked */
cc3b68fea   Hans de Goede   irqchip: sun4i: S...
44
  	writel(BIT(0), sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0));
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  }
  
  static void sun4i_irq_mask(struct irq_data *irqd)
  {
  	unsigned int irq = irqd_to_hwirq(irqd);
  	unsigned int irq_off = irq % 32;
  	int reg = irq / 32;
  	u32 val;
  
  	val = readl(sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg));
  	writel(val & ~(1 << irq_off),
  	       sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg));
  }
  
  static void sun4i_irq_unmask(struct irq_data *irqd)
  {
  	unsigned int irq = irqd_to_hwirq(irqd);
  	unsigned int irq_off = irq % 32;
  	int reg = irq / 32;
  	u32 val;
  
  	val = readl(sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg));
  	writel(val | (1 << irq_off),
  	       sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg));
  }
  
  static struct irq_chip sun4i_irq_chip = {
  	.name		= "sun4i_irq",
e9df9e221   Hans de Goede   irqchip: sun4i: D...
73
74
75
76
77
  	.irq_eoi	= sun4i_irq_ack,
  	.irq_mask	= sun4i_irq_mask,
  	.irq_unmask	= sun4i_irq_unmask,
  	.flags		= IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED,
  };
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
78
79
80
  static int sun4i_irq_map(struct irq_domain *d, unsigned int virq,
  			 irq_hw_number_t hw)
  {
915b78ce8   Hans de Goede   irqchip: sun4i: U...
81
  	irq_set_chip_and_handler(virq, &sun4i_irq_chip, handle_fasteoi_irq);
d17cab445   Rob Herring   irqchip: Kill off...
82
  	irq_set_probe(virq);
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
83
84
85
  
  	return 0;
  }
960097365   Krzysztof Kozlowski   irqchip: Constify...
86
  static const struct irq_domain_ops sun4i_irq_ops = {
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
  	.map = sun4i_irq_map,
  	.xlate = irq_domain_xlate_onecell,
  };
  
  static int __init sun4i_of_init(struct device_node *node,
  				struct device_node *parent)
  {
  	sun4i_irq_base = of_iomap(node, 0);
  	if (!sun4i_irq_base)
  		panic("%s: unable to map IC registers
  ",
  			node->full_name);
  
  	/* Disable all interrupts */
  	writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(0));
  	writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(1));
  	writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(2));
649ff46e5   Hans de Goede   irqchip: sun4i: F...
104
  	/* Unmask all the interrupts, ENABLE_REG(x) is used for masking */
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  	writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(0));
  	writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(1));
  	writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(2));
  
  	/* Clear all the pending interrupts */
  	writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0));
  	writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(1));
  	writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(2));
  
  	/* Enable protection mode */
  	writel(0x01, sun4i_irq_base + SUN4I_IRQ_PROTECTION_REG);
  
  	/* Configure the external interrupt source type */
  	writel(0x00, sun4i_irq_base + SUN4I_IRQ_NMI_CTRL_REG);
  
  	sun4i_irq_domain = irq_domain_add_linear(node, 3 * 32,
  						 &sun4i_irq_ops, NULL);
  	if (!sun4i_irq_domain)
  		panic("%s: unable to create IRQ domain
  ", node->full_name);
  
  	set_handle_irq(sun4i_handle_irq);
  
  	return 0;
  }
a7e8b4b51   Maxime Ripard   irqchip: sunxi: C...
130
  IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-a10-ic", sun4i_of_init);
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
131

8783dd3a3   Stephen Boyd   irqchip: Remove a...
132
  static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs)
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
133
  {
21d06d91c   Marc Zyngier   irqchip: sun4i: C...
134
  	u32 hwirq;
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
135

56af0416b   Hans de Goede   irqchip: sun4i: F...
136
137
138
139
140
141
142
143
144
145
  	/*
  	 * hwirq == 0 can mean one of 3 things:
  	 * 1) no more irqs pending
  	 * 2) irq 0 pending
  	 * 3) spurious irq
  	 * So if we immediately get a reading of 0, check the irq-pending reg
  	 * to differentiate between 2 and 3. We only do this once to avoid
  	 * the extra check in the common case of 1 hapening after having
  	 * read the vector-reg once.
  	 */
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
146
  	hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2;
56af0416b   Hans de Goede   irqchip: sun4i: F...
147
148
149
150
151
  	if (hwirq == 0 &&
  		  !(readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)) & BIT(0)))
  		return;
  
  	do {
21d06d91c   Marc Zyngier   irqchip: sun4i: C...
152
  		handle_domain_irq(sun4i_irq_domain, hwirq, regs);
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
153
  		hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2;
56af0416b   Hans de Goede   irqchip: sun4i: F...
154
  	} while (hwirq != 0);
d7fbc6ca3   Maxime Ripard   irqchip: sunxi: R...
155
  }