Merge branch 'parisc-4.9-1' of git://git.kernel.org/pub/scm/linux/kernel/git/deller...
[cascardo/linux.git] / drivers / irqchip / irq-st.c
1 /*
2  *  Copyright (C) 2014 STMicroelectronics – All Rights Reserved
3  *
4  *  Author: Lee Jones <lee.jones@linaro.org>
5  *
6  *  This is a re-write of Christophe Kerello's PMU driver.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  */
12
13 #include <dt-bindings/interrupt-controller/irq-st.h>
14 #include <linux/err.h>
15 #include <linux/mfd/syscon.h>
16 #include <linux/of_device.h>
17 #include <linux/platform_device.h>
18 #include <linux/regmap.h>
19 #include <linux/slab.h>
20
21 #define STIH415_SYSCFG_642              0x0a8
22 #define STIH416_SYSCFG_7543             0x87c
23 #define STIH407_SYSCFG_5102             0x198
24 #define STID127_SYSCFG_734              0x088
25
26 #define ST_A9_IRQ_MASK                  0x001FFFFF
27 #define ST_A9_IRQ_MAX_CHANS             2
28
29 #define ST_A9_IRQ_EN_CTI_0              BIT(0)
30 #define ST_A9_IRQ_EN_CTI_1              BIT(1)
31 #define ST_A9_IRQ_EN_PMU_0              BIT(2)
32 #define ST_A9_IRQ_EN_PMU_1              BIT(3)
33 #define ST_A9_IRQ_EN_PL310_L2           BIT(4)
34 #define ST_A9_IRQ_EN_EXT_0              BIT(5)
35 #define ST_A9_IRQ_EN_EXT_1              BIT(6)
36 #define ST_A9_IRQ_EN_EXT_2              BIT(7)
37
38 #define ST_A9_FIQ_N_SEL(dev, chan)      (dev << (8  + (chan * 3)))
39 #define ST_A9_IRQ_N_SEL(dev, chan)      (dev << (14 + (chan * 3)))
40 #define ST_A9_EXTIRQ_INV_SEL(dev)       (dev << 20)
41
42 struct st_irq_syscfg {
43         struct regmap *regmap;
44         unsigned int syscfg;
45         unsigned int config;
46         bool ext_inverted;
47 };
48
49 static const struct of_device_id st_irq_syscfg_match[] = {
50         {
51                 .compatible = "st,stih415-irq-syscfg",
52                 .data = (void *)STIH415_SYSCFG_642,
53         },
54         {
55                 .compatible = "st,stih416-irq-syscfg",
56                 .data = (void *)STIH416_SYSCFG_7543,
57         },
58         {
59                 .compatible = "st,stih407-irq-syscfg",
60                 .data = (void *)STIH407_SYSCFG_5102,
61         },
62         {
63                 .compatible = "st,stid127-irq-syscfg",
64                 .data = (void *)STID127_SYSCFG_734,
65         },
66         {}
67 };
68
69 static int st_irq_xlate(struct platform_device *pdev,
70                         int device, int channel, bool irq)
71 {
72         struct st_irq_syscfg *ddata = dev_get_drvdata(&pdev->dev);
73
74         /* Set the device enable bit. */
75         switch (device) {
76         case ST_IRQ_SYSCFG_EXT_0:
77                 ddata->config |= ST_A9_IRQ_EN_EXT_0;
78                 break;
79         case ST_IRQ_SYSCFG_EXT_1:
80                 ddata->config |= ST_A9_IRQ_EN_EXT_1;
81                 break;
82         case ST_IRQ_SYSCFG_EXT_2:
83                 ddata->config |= ST_A9_IRQ_EN_EXT_2;
84                 break;
85         case ST_IRQ_SYSCFG_CTI_0:
86                 ddata->config |= ST_A9_IRQ_EN_CTI_0;
87                 break;
88         case ST_IRQ_SYSCFG_CTI_1:
89                 ddata->config |= ST_A9_IRQ_EN_CTI_1;
90                 break;
91         case ST_IRQ_SYSCFG_PMU_0:
92                 ddata->config |= ST_A9_IRQ_EN_PMU_0;
93                 break;
94         case ST_IRQ_SYSCFG_PMU_1:
95                 ddata->config |= ST_A9_IRQ_EN_PMU_1;
96                 break;
97         case ST_IRQ_SYSCFG_pl310_L2:
98                 ddata->config |= ST_A9_IRQ_EN_PL310_L2;
99                 break;
100         case ST_IRQ_SYSCFG_DISABLED:
101                 return 0;
102         default:
103                 dev_err(&pdev->dev, "Unrecognised device %d\n", device);
104                 return -EINVAL;
105         }
106
107         /* Select IRQ/FIQ channel for device. */
108         ddata->config |= irq ?
109                 ST_A9_IRQ_N_SEL(device, channel) :
110                 ST_A9_FIQ_N_SEL(device, channel);
111
112         return 0;
113 }
114
115 static int st_irq_syscfg_enable(struct platform_device *pdev)
116 {
117         struct device_node *np = pdev->dev.of_node;
118         struct st_irq_syscfg *ddata = dev_get_drvdata(&pdev->dev);
119         int channels, ret, i;
120         u32 device, invert;
121
122         channels = of_property_count_u32_elems(np, "st,irq-device");
123         if (channels != ST_A9_IRQ_MAX_CHANS) {
124                 dev_err(&pdev->dev, "st,enable-irq-device must have 2 elems\n");
125                 return -EINVAL;
126         }
127
128         channels = of_property_count_u32_elems(np, "st,fiq-device");
129         if (channels != ST_A9_IRQ_MAX_CHANS) {
130                 dev_err(&pdev->dev, "st,enable-fiq-device must have 2 elems\n");
131                 return -EINVAL;
132         }
133
134         for (i = 0; i < ST_A9_IRQ_MAX_CHANS; i++) {
135                 of_property_read_u32_index(np, "st,irq-device", i, &device);
136
137                 ret = st_irq_xlate(pdev, device, i, true);
138                 if (ret)
139                         return ret;
140
141                 of_property_read_u32_index(np, "st,fiq-device", i, &device);
142
143                 ret = st_irq_xlate(pdev, device, i, false);
144                 if (ret)
145                         return ret;
146         }
147
148         /* External IRQs may be inverted. */
149         of_property_read_u32(np, "st,invert-ext", &invert);
150         ddata->config |= ST_A9_EXTIRQ_INV_SEL(invert);
151
152         return regmap_update_bits(ddata->regmap, ddata->syscfg,
153                                   ST_A9_IRQ_MASK, ddata->config);
154 }
155
156 static int st_irq_syscfg_probe(struct platform_device *pdev)
157 {
158         struct device_node *np = pdev->dev.of_node;
159         const struct of_device_id *match;
160         struct st_irq_syscfg *ddata;
161
162         ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL);
163         if (!ddata)
164                 return -ENOMEM;
165
166         match = of_match_device(st_irq_syscfg_match, &pdev->dev);
167         if (!match)
168                 return -ENODEV;
169
170         ddata->syscfg = (unsigned int)match->data;
171
172         ddata->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
173         if (IS_ERR(ddata->regmap)) {
174                 dev_err(&pdev->dev, "syscfg phandle missing\n");
175                 return PTR_ERR(ddata->regmap);
176         }
177
178         dev_set_drvdata(&pdev->dev, ddata);
179
180         return st_irq_syscfg_enable(pdev);
181 }
182
183 static int st_irq_syscfg_resume(struct device *dev)
184 {
185         struct st_irq_syscfg *ddata = dev_get_drvdata(dev);
186
187         return regmap_update_bits(ddata->regmap, ddata->syscfg,
188                                   ST_A9_IRQ_MASK, ddata->config);
189 }
190
191 static SIMPLE_DEV_PM_OPS(st_irq_syscfg_pm_ops, NULL, st_irq_syscfg_resume);
192
193 static struct platform_driver st_irq_syscfg_driver = {
194         .driver = {
195                 .name = "st_irq_syscfg",
196                 .pm = &st_irq_syscfg_pm_ops,
197                 .of_match_table = st_irq_syscfg_match,
198         },
199         .probe = st_irq_syscfg_probe,
200 };
201
202 static int __init st_irq_syscfg_init(void)
203 {
204         return platform_driver_register(&st_irq_syscfg_driver);
205 }
206 core_initcall(st_irq_syscfg_init);