]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/irqchip/irq-mbigen.c
irqchip/mbigen: Introduce mbigen_of_create_domain()
[karo-tx-linux.git] / drivers / irqchip / irq-mbigen.c
1 /*
2  * Copyright (C) 2015 Hisilicon Limited, All Rights Reserved.
3  * Author: Jun Ma <majun258@huawei.com>
4  * Author: Yun Wu <wuyun.wu@huawei.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include <linux/interrupt.h>
20 #include <linux/irqchip.h>
21 #include <linux/module.h>
22 #include <linux/msi.h>
23 #include <linux/of_address.h>
24 #include <linux/of_irq.h>
25 #include <linux/of_platform.h>
26 #include <linux/platform_device.h>
27 #include <linux/slab.h>
28
29 /* Interrupt numbers per mbigen node supported */
30 #define IRQS_PER_MBIGEN_NODE            128
31
32 /* 64 irqs (Pin0-pin63) are reserved for each mbigen chip */
33 #define RESERVED_IRQ_PER_MBIGEN_CHIP    64
34
35 /* The maximum IRQ pin number of mbigen chip(start from 0) */
36 #define MAXIMUM_IRQ_PIN_NUM             1407
37
38 /**
39  * In mbigen vector register
40  * bit[21:12]:  event id value
41  * bit[11:0]:   device id
42  */
43 #define IRQ_EVENT_ID_SHIFT              12
44 #define IRQ_EVENT_ID_MASK               0x3ff
45
46 /* register range of each mbigen node */
47 #define MBIGEN_NODE_OFFSET              0x1000
48
49 /* offset of vector register in mbigen node */
50 #define REG_MBIGEN_VEC_OFFSET           0x200
51
52 /**
53  * offset of clear register in mbigen node
54  * This register is used to clear the status
55  * of interrupt
56  */
57 #define REG_MBIGEN_CLEAR_OFFSET         0xa000
58
59 /**
60  * offset of interrupt type register
61  * This register is used to configure interrupt
62  * trigger type
63  */
64 #define REG_MBIGEN_TYPE_OFFSET          0x0
65
66 /**
67  * struct mbigen_device - holds the information of mbigen device.
68  *
69  * @pdev:               pointer to the platform device structure of mbigen chip.
70  * @base:               mapped address of this mbigen chip.
71  */
72 struct mbigen_device {
73         struct platform_device  *pdev;
74         void __iomem            *base;
75 };
76
77 static inline unsigned int get_mbigen_vec_reg(irq_hw_number_t hwirq)
78 {
79         unsigned int nid, pin;
80
81         hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP;
82         nid = hwirq / IRQS_PER_MBIGEN_NODE + 1;
83         pin = hwirq % IRQS_PER_MBIGEN_NODE;
84
85         return pin * 4 + nid * MBIGEN_NODE_OFFSET
86                         + REG_MBIGEN_VEC_OFFSET;
87 }
88
89 static inline void get_mbigen_type_reg(irq_hw_number_t hwirq,
90                                         u32 *mask, u32 *addr)
91 {
92         unsigned int nid, irq_ofst, ofst;
93
94         hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP;
95         nid = hwirq / IRQS_PER_MBIGEN_NODE + 1;
96         irq_ofst = hwirq % IRQS_PER_MBIGEN_NODE;
97
98         *mask = 1 << (irq_ofst % 32);
99         ofst = irq_ofst / 32 * 4;
100
101         *addr = ofst + nid * MBIGEN_NODE_OFFSET
102                 + REG_MBIGEN_TYPE_OFFSET;
103 }
104
105 static inline void get_mbigen_clear_reg(irq_hw_number_t hwirq,
106                                         u32 *mask, u32 *addr)
107 {
108         unsigned int ofst;
109
110         hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP;
111         ofst = hwirq / 32 * 4;
112
113         *mask = 1 << (hwirq % 32);
114         *addr = ofst + REG_MBIGEN_CLEAR_OFFSET;
115 }
116
117 static void mbigen_eoi_irq(struct irq_data *data)
118 {
119         void __iomem *base = data->chip_data;
120         u32 mask, addr;
121
122         get_mbigen_clear_reg(data->hwirq, &mask, &addr);
123
124         writel_relaxed(mask, base + addr);
125
126         irq_chip_eoi_parent(data);
127 }
128
129 static int mbigen_set_type(struct irq_data *data, unsigned int type)
130 {
131         void __iomem *base = data->chip_data;
132         u32 mask, addr, val;
133
134         if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
135                 return -EINVAL;
136
137         get_mbigen_type_reg(data->hwirq, &mask, &addr);
138
139         val = readl_relaxed(base + addr);
140
141         if (type == IRQ_TYPE_LEVEL_HIGH)
142                 val |= mask;
143         else
144                 val &= ~mask;
145
146         writel_relaxed(val, base + addr);
147
148         return 0;
149 }
150
151 static struct irq_chip mbigen_irq_chip = {
152         .name =                 "mbigen-v2",
153         .irq_mask =             irq_chip_mask_parent,
154         .irq_unmask =           irq_chip_unmask_parent,
155         .irq_eoi =              mbigen_eoi_irq,
156         .irq_set_type =         mbigen_set_type,
157         .irq_set_affinity =     irq_chip_set_affinity_parent,
158 };
159
160 static void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg)
161 {
162         struct irq_data *d = irq_get_irq_data(desc->irq);
163         void __iomem *base = d->chip_data;
164         u32 val;
165
166         base += get_mbigen_vec_reg(d->hwirq);
167         val = readl_relaxed(base);
168
169         val &= ~(IRQ_EVENT_ID_MASK << IRQ_EVENT_ID_SHIFT);
170         val |= (msg->data << IRQ_EVENT_ID_SHIFT);
171
172         /* The address of doorbell is encoded in mbigen register by default
173          * So,we don't need to program the doorbell address at here
174          */
175         writel_relaxed(val, base);
176 }
177
178 static int mbigen_domain_translate(struct irq_domain *d,
179                                     struct irq_fwspec *fwspec,
180                                     unsigned long *hwirq,
181                                     unsigned int *type)
182 {
183         if (is_of_node(fwspec->fwnode)) {
184                 if (fwspec->param_count != 2)
185                         return -EINVAL;
186
187                 if ((fwspec->param[0] > MAXIMUM_IRQ_PIN_NUM) ||
188                         (fwspec->param[0] < RESERVED_IRQ_PER_MBIGEN_CHIP))
189                         return -EINVAL;
190                 else
191                         *hwirq = fwspec->param[0];
192
193                 /* If there is no valid irq type, just use the default type */
194                 if ((fwspec->param[1] == IRQ_TYPE_EDGE_RISING) ||
195                         (fwspec->param[1] == IRQ_TYPE_LEVEL_HIGH))
196                         *type = fwspec->param[1];
197                 else
198                         return -EINVAL;
199
200                 return 0;
201         }
202         return -EINVAL;
203 }
204
205 static int mbigen_irq_domain_alloc(struct irq_domain *domain,
206                                         unsigned int virq,
207                                         unsigned int nr_irqs,
208                                         void *args)
209 {
210         struct irq_fwspec *fwspec = args;
211         irq_hw_number_t hwirq;
212         unsigned int type;
213         struct mbigen_device *mgn_chip;
214         int i, err;
215
216         err = mbigen_domain_translate(domain, fwspec, &hwirq, &type);
217         if (err)
218                 return err;
219
220         err = platform_msi_domain_alloc(domain, virq, nr_irqs);
221         if (err)
222                 return err;
223
224         mgn_chip = platform_msi_get_host_data(domain);
225
226         for (i = 0; i < nr_irqs; i++)
227                 irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
228                                       &mbigen_irq_chip, mgn_chip->base);
229
230         return 0;
231 }
232
233 static struct irq_domain_ops mbigen_domain_ops = {
234         .translate      = mbigen_domain_translate,
235         .alloc          = mbigen_irq_domain_alloc,
236         .free           = irq_domain_free_irqs_common,
237 };
238
239 static int mbigen_of_create_domain(struct platform_device *pdev,
240                                    struct mbigen_device *mgn_chip)
241 {
242         struct device *parent;
243         struct platform_device *child;
244         struct irq_domain *domain;
245         struct device_node *np;
246         u32 num_pins;
247
248         for_each_child_of_node(pdev->dev.of_node, np) {
249                 if (!of_property_read_bool(np, "interrupt-controller"))
250                         continue;
251
252                 parent = platform_bus_type.dev_root;
253                 child = of_platform_device_create(np, NULL, parent);
254                 if (!child)
255                         return -ENOMEM;
256
257                 if (of_property_read_u32(child->dev.of_node, "num-pins",
258                                          &num_pins) < 0) {
259                         dev_err(&pdev->dev, "No num-pins property\n");
260                         return -EINVAL;
261                 }
262
263                 domain = platform_msi_create_device_domain(&child->dev, num_pins,
264                                                            mbigen_write_msg,
265                                                            &mbigen_domain_ops,
266                                                            mgn_chip);
267                 if (!domain)
268                         return -ENOMEM;
269         }
270
271         return 0;
272 }
273
274 static int mbigen_device_probe(struct platform_device *pdev)
275 {
276         struct mbigen_device *mgn_chip;
277         struct resource *res;
278         int err;
279
280         mgn_chip = devm_kzalloc(&pdev->dev, sizeof(*mgn_chip), GFP_KERNEL);
281         if (!mgn_chip)
282                 return -ENOMEM;
283
284         mgn_chip->pdev = pdev;
285
286         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
287         mgn_chip->base = devm_ioremap(&pdev->dev, res->start,
288                                       resource_size(res));
289         if (IS_ERR(mgn_chip->base))
290                 return PTR_ERR(mgn_chip->base);
291
292         err = mbigen_of_create_domain(pdev, mgn_chip);
293         if (err)
294                 return err;
295
296         platform_set_drvdata(pdev, mgn_chip);
297         return 0;
298 }
299
300 static const struct of_device_id mbigen_of_match[] = {
301         { .compatible = "hisilicon,mbigen-v2" },
302         { /* END */ }
303 };
304 MODULE_DEVICE_TABLE(of, mbigen_of_match);
305
306 static struct platform_driver mbigen_platform_driver = {
307         .driver = {
308                 .name           = "Hisilicon MBIGEN-V2",
309                 .of_match_table = mbigen_of_match,
310         },
311         .probe                  = mbigen_device_probe,
312 };
313
314 module_platform_driver(mbigen_platform_driver);
315
316 MODULE_AUTHOR("Jun Ma <majun258@huawei.com>");
317 MODULE_AUTHOR("Yun Wu <wuyun.wu@huawei.com>");
318 MODULE_LICENSE("GPL");
319 MODULE_DESCRIPTION("Hisilicon MBI Generator driver");