GNU Linux-libre 4.19.264-gnu1
[releases.git] / arch / arm / mach-davinci / cp_intc.c
1 /*
2  * TI Common Platform Interrupt Controller (cp_intc) driver
3  *
4  * Author: Steve Chen <schen@mvista.com>
5  * Copyright (C) 2008-2009, MontaVista Software, Inc. <source@mvista.com>
6  *
7  * This file is licensed under the terms of the GNU General Public License
8  * version 2. This program is licensed "as is" without any warranty of any
9  * kind, whether express or implied.
10  */
11
12 #include <linux/export.h>
13 #include <linux/init.h>
14 #include <linux/irq.h>
15 #include <linux/irqchip.h>
16 #include <linux/irqdomain.h>
17 #include <linux/io.h>
18 #include <linux/of.h>
19 #include <linux/of_address.h>
20 #include <linux/of_irq.h>
21
22 #include <mach/common.h>
23 #include "cp_intc.h"
24
25 static inline unsigned int cp_intc_read(unsigned offset)
26 {
27         return __raw_readl(davinci_intc_base + offset);
28 }
29
30 static inline void cp_intc_write(unsigned long value, unsigned offset)
31 {
32         __raw_writel(value, davinci_intc_base + offset);
33 }
34
35 static void cp_intc_ack_irq(struct irq_data *d)
36 {
37         cp_intc_write(d->hwirq, CP_INTC_SYS_STAT_IDX_CLR);
38 }
39
40 /* Disable interrupt */
41 static void cp_intc_mask_irq(struct irq_data *d)
42 {
43         /* XXX don't know why we need to disable nIRQ here... */
44         cp_intc_write(1, CP_INTC_HOST_ENABLE_IDX_CLR);
45         cp_intc_write(d->hwirq, CP_INTC_SYS_ENABLE_IDX_CLR);
46         cp_intc_write(1, CP_INTC_HOST_ENABLE_IDX_SET);
47 }
48
49 /* Enable interrupt */
50 static void cp_intc_unmask_irq(struct irq_data *d)
51 {
52         cp_intc_write(d->hwirq, CP_INTC_SYS_ENABLE_IDX_SET);
53 }
54
55 static int cp_intc_set_irq_type(struct irq_data *d, unsigned int flow_type)
56 {
57         unsigned reg            = BIT_WORD(d->hwirq);
58         unsigned mask           = BIT_MASK(d->hwirq);
59         unsigned polarity       = cp_intc_read(CP_INTC_SYS_POLARITY(reg));
60         unsigned type           = cp_intc_read(CP_INTC_SYS_TYPE(reg));
61
62         switch (flow_type) {
63         case IRQ_TYPE_EDGE_RISING:
64                 polarity |= mask;
65                 type |= mask;
66                 break;
67         case IRQ_TYPE_EDGE_FALLING:
68                 polarity &= ~mask;
69                 type |= mask;
70                 break;
71         case IRQ_TYPE_LEVEL_HIGH:
72                 polarity |= mask;
73                 type &= ~mask;
74                 break;
75         case IRQ_TYPE_LEVEL_LOW:
76                 polarity &= ~mask;
77                 type &= ~mask;
78                 break;
79         default:
80                 return -EINVAL;
81         }
82
83         cp_intc_write(polarity, CP_INTC_SYS_POLARITY(reg));
84         cp_intc_write(type, CP_INTC_SYS_TYPE(reg));
85
86         return 0;
87 }
88
89 static struct irq_chip cp_intc_irq_chip = {
90         .name           = "cp_intc",
91         .irq_ack        = cp_intc_ack_irq,
92         .irq_mask       = cp_intc_mask_irq,
93         .irq_unmask     = cp_intc_unmask_irq,
94         .irq_set_type   = cp_intc_set_irq_type,
95         .flags          = IRQCHIP_SKIP_SET_WAKE,
96 };
97
98 static struct irq_domain *cp_intc_domain;
99
100 static int cp_intc_host_map(struct irq_domain *h, unsigned int virq,
101                           irq_hw_number_t hw)
102 {
103         pr_debug("cp_intc_host_map(%d, 0x%lx)\n", virq, hw);
104
105         irq_set_chip(virq, &cp_intc_irq_chip);
106         irq_set_probe(virq);
107         irq_set_handler(virq, handle_edge_irq);
108         return 0;
109 }
110
111 static const struct irq_domain_ops cp_intc_host_ops = {
112         .map = cp_intc_host_map,
113         .xlate = irq_domain_xlate_onetwocell,
114 };
115
116 int __init cp_intc_of_init(struct device_node *node, struct device_node *parent)
117 {
118         u32 num_irq             = davinci_soc_info.intc_irq_num;
119         u8 *irq_prio            = davinci_soc_info.intc_irq_prios;
120         u32 *host_map           = davinci_soc_info.intc_host_map;
121         unsigned num_reg        = BITS_TO_LONGS(num_irq);
122         int i, irq_base;
123
124         davinci_intc_type = DAVINCI_INTC_TYPE_CP_INTC;
125         if (node) {
126                 davinci_intc_base = of_iomap(node, 0);
127                 if (of_property_read_u32(node, "ti,intc-size", &num_irq))
128                         pr_warn("unable to get intc-size, default to %d\n",
129                                 num_irq);
130         } else {
131                 davinci_intc_base = ioremap(davinci_soc_info.intc_base, SZ_8K);
132         }
133         if (WARN_ON(!davinci_intc_base))
134                 return -EINVAL;
135
136         cp_intc_write(0, CP_INTC_GLOBAL_ENABLE);
137
138         /* Disable all host interrupts */
139         cp_intc_write(0, CP_INTC_HOST_ENABLE(0));
140
141         /* Disable system interrupts */
142         for (i = 0; i < num_reg; i++)
143                 cp_intc_write(~0, CP_INTC_SYS_ENABLE_CLR(i));
144
145         /* Set to normal mode, no nesting, no priority hold */
146         cp_intc_write(0, CP_INTC_CTRL);
147         cp_intc_write(0, CP_INTC_HOST_CTRL);
148
149         /* Clear system interrupt status */
150         for (i = 0; i < num_reg; i++)
151                 cp_intc_write(~0, CP_INTC_SYS_STAT_CLR(i));
152
153         /* Enable nIRQ (what about nFIQ?) */
154         cp_intc_write(1, CP_INTC_HOST_ENABLE_IDX_SET);
155
156         /*
157          * Priority is determined by host channel: lower channel number has
158          * higher priority i.e. channel 0 has highest priority and channel 31
159          * had the lowest priority.
160          */
161         num_reg = (num_irq + 3) >> 2;   /* 4 channels per register */
162         if (irq_prio) {
163                 unsigned j, k;
164                 u32 val;
165
166                 for (k = i = 0; i < num_reg; i++) {
167                         for (val = j = 0; j < 4; j++, k++) {
168                                 val >>= 8;
169                                 if (k < num_irq)
170                                         val |= irq_prio[k] << 24;
171                         }
172
173                         cp_intc_write(val, CP_INTC_CHAN_MAP(i));
174                 }
175         } else  {
176                 /*
177                  * Default everything to channel 15 if priority not specified.
178                  * Note that channel 0-1 are mapped to nFIQ and channels 2-31
179                  * are mapped to nIRQ.
180                  */
181                 for (i = 0; i < num_reg; i++)
182                         cp_intc_write(0x0f0f0f0f, CP_INTC_CHAN_MAP(i));
183         }
184
185         if (host_map)
186                 for (i = 0; host_map[i] != -1; i++)
187                         cp_intc_write(host_map[i], CP_INTC_HOST_MAP(i));
188
189         irq_base = irq_alloc_descs(-1, 0, num_irq, 0);
190         if (irq_base < 0) {
191                 pr_warn("Couldn't allocate IRQ numbers\n");
192                 irq_base = 0;
193         }
194
195         /* create a legacy host */
196         cp_intc_domain = irq_domain_add_legacy(node, num_irq,
197                                         irq_base, 0, &cp_intc_host_ops, NULL);
198
199         if (!cp_intc_domain) {
200                 pr_err("cp_intc: failed to allocate irq host!\n");
201                 return -EINVAL;
202         }
203
204         /* Enable global interrupt */
205         cp_intc_write(1, CP_INTC_GLOBAL_ENABLE);
206
207         return 0;
208 }
209
210 void __init cp_intc_init(void)
211 {
212         cp_intc_of_init(NULL, NULL);
213 }
214
215 IRQCHIP_DECLARE(cp_intc, "ti,cp-intc", cp_intc_of_init);