Merge branch 'parisc-4.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller...
[cascardo/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_device_probe(struct platform_device *pdev)
240 {
241         struct mbigen_device *mgn_chip;
242         struct platform_device *child;
243         struct irq_domain *domain;
244         struct device_node *np;
245         struct device *parent;
246         struct resource *res;
247         u32 num_pins;
248
249         mgn_chip = devm_kzalloc(&pdev->dev, sizeof(*mgn_chip), GFP_KERNEL);
250         if (!mgn_chip)
251                 return -ENOMEM;
252
253         mgn_chip->pdev = pdev;
254
255         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
256         mgn_chip->base = devm_ioremap_resource(&pdev->dev, res);
257         if (IS_ERR(mgn_chip->base))
258                 return PTR_ERR(mgn_chip->base);
259
260         for_each_child_of_node(pdev->dev.of_node, np) {
261                 if (!of_property_read_bool(np, "interrupt-controller"))
262                         continue;
263
264                 parent = platform_bus_type.dev_root;
265                 child = of_platform_device_create(np, NULL, parent);
266                 if (!child)
267                         return -ENOMEM;
268
269                 if (of_property_read_u32(child->dev.of_node, "num-pins",
270                                          &num_pins) < 0) {
271                         dev_err(&pdev->dev, "No num-pins property\n");
272                         return -EINVAL;
273                 }
274
275                 domain = platform_msi_create_device_domain(&child->dev, num_pins,
276                                                            mbigen_write_msg,
277                                                            &mbigen_domain_ops,
278                                                            mgn_chip);
279                 if (!domain)
280                         return -ENOMEM;
281         }
282
283         platform_set_drvdata(pdev, mgn_chip);
284         return 0;
285 }
286
287 static const struct of_device_id mbigen_of_match[] = {
288         { .compatible = "hisilicon,mbigen-v2" },
289         { /* END */ }
290 };
291 MODULE_DEVICE_TABLE(of, mbigen_of_match);
292
293 static struct platform_driver mbigen_platform_driver = {
294         .driver = {
295                 .name           = "Hisilicon MBIGEN-V2",
296                 .owner          = THIS_MODULE,
297                 .of_match_table = mbigen_of_match,
298         },
299         .probe                  = mbigen_device_probe,
300 };
301
302 module_platform_driver(mbigen_platform_driver);
303
304 MODULE_AUTHOR("Jun Ma <majun258@huawei.com>");
305 MODULE_AUTHOR("Yun Wu <wuyun.wu@huawei.com>");
306 MODULE_LICENSE("GPL");
307 MODULE_DESCRIPTION("Hisilicon MBI Generator driver");