tegra: ictlr: fix crash when an IRQ fire during the probe
[linux-3.10.git] / drivers / platform / tegra / hier_ictlr / hier_ictlr.c
1 /*
2  * Copyright (c) 2013-2014, NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17  */
18
19 #include <linux/module.h>
20 #include <linux/interrupt.h>
21 #include <linux/platform_device.h>
22 #include <linux/err.h>
23 #include <linux/io.h>
24 #include "hier_ictlr.h"
25
26 #define HIER_GROUP_CPU_ENABLE                                 0x00000000
27 #define HIER_GROUP_CPU_STATUS                                 0x00000004
28 #define HIER_GROUP1_MSELECT_ERROR                                     15
29
30 #define MSELECT_CONFIG_0                                             0x0
31 #define MSELECT_CONFIG_0_READ_TIMEOUT_EN_SLAVE0_SHIFT                 16
32 #define MSELECT_CONFIG_0_WRITE_TIMEOUT_EN_SLAVE0_SHIFT                17
33 #define MSELECT_CONFIG_0_READ_TIMEOUT_EN_SLAVE1_SHIFT                 18
34 #define MSELECT_CONFIG_0_WRITE_TIMEOUT_EN_SLAVE1_SHIFT                19
35 #define MSELECT_CONFIG_0_READ_TIMEOUT_EN_SLAVE2_SHIFT                 20
36 #define MSELECT_CONFIG_0_WRITE_TIMEOUT_EN_SLAVE2_SHIFT                21
37 #define MSELECT_CONFIG_0_ERR_RESP_EN_SLAVE1_SHIFT                     24
38 #define MSELECT_CONFIG_0_ERR_RESP_EN_SLAVE2_SHIFT                     25
39
40 #define MSELECT_TIMEOUT_TIMER_0                                     0x5c
41 #define MSELECT_ERROR_STATUS_0                                      0x60
42 #define MSELECT_DEFAULT_TIMEOUT                                 0xFFFFFF
43
44 static irqreturn_t tegra_hier_ictlr_irq_handler(int irq, void *data)
45 {
46         struct device *dev = data;
47         struct tegra_hier_ictlr *ictlr = dev_get_drvdata(dev);
48         unsigned long status;
49
50         status = readl(ictlr->mselect_base + MSELECT_ERROR_STATUS_0);
51         if (status != 0) {
52                 pr_err("MSELECT error detected! status=0x%x\n",
53                         (unsigned int)status);
54                 BUG();
55         }
56
57         return IRQ_HANDLED;
58 }
59
60 static int tegra_hier_ictlr_map_memory(struct platform_device *pdev,
61                                        struct tegra_hier_ictlr *ictlr)
62 {
63         struct resource *res;
64
65         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
66         if (!res) {
67                 dev_err(&pdev->dev,
68                         "Unable to allocate resources (hier-ictlr).\n");
69                 return -EBUSY;
70         }
71
72         ictlr->hier_ictlr_base = devm_request_and_ioremap(&pdev->dev, res);
73         if (!ictlr->hier_ictlr_base) {
74                 dev_err(&pdev->dev, "Unable to map memory (hier-ictlr).\n");
75                 return -EBUSY;
76         }
77
78         res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
79         if (!res) {
80                 dev_err(&pdev->dev,
81                     "Unable to allocate resources (mselect).\n");
82                 return -EBUSY;
83         }
84
85         ictlr->mselect_base = devm_request_and_ioremap(&pdev->dev, res);
86         if (!ictlr->mselect_base) {
87                 dev_err(&pdev->dev, "Unable to map memory (mselect).\n");
88                 return -EBUSY;
89         }
90
91         return 0;
92 }
93
94 static int tegra_hier_ictlr_irq_init(struct platform_device *pdev,
95                                      struct tegra_hier_ictlr *ictlr)
96 {
97         unsigned long reg;
98         int ret;
99
100         ictlr->irq = platform_get_irq(pdev, 0);
101         if (ictlr->irq <= 0)
102                 return -EBUSY;
103
104         ret = devm_request_irq(&pdev->dev, ictlr->irq,
105                 tegra_hier_ictlr_irq_handler, IRQF_TRIGGER_HIGH,
106                 "hier_ictlr_irq", &pdev->dev);
107
108         if (ret) {
109                 dev_err(&pdev->dev,
110                         "Unable to request interrupt for device (err=%d).\n",
111                         ret);
112                 return ret;
113         }
114
115         reg = readl(ictlr->hier_ictlr_base + HIER_GROUP_CPU_ENABLE);
116         writel(reg | (1 << HIER_GROUP1_MSELECT_ERROR),
117                 ictlr->hier_ictlr_base + HIER_GROUP_CPU_ENABLE);
118
119         return 0;
120 }
121
122 static int tegra_hier_ictlr_mselect_init(struct platform_device *pdev,
123                                          struct tegra_hier_ictlr *ictlr)
124 {
125         unsigned long reg;
126
127         tegra_hier_ictlr_set_mselect_timeout(ictlr, MSELECT_DEFAULT_TIMEOUT);
128
129         reg = readl(ictlr->mselect_base + MSELECT_CONFIG_0);
130         writel(reg |
131                 ((1 << MSELECT_CONFIG_0_READ_TIMEOUT_EN_SLAVE0_SHIFT)  |
132                  (1 << MSELECT_CONFIG_0_WRITE_TIMEOUT_EN_SLAVE0_SHIFT) |
133                  (1 << MSELECT_CONFIG_0_READ_TIMEOUT_EN_SLAVE1_SHIFT)  |
134                  (1 << MSELECT_CONFIG_0_WRITE_TIMEOUT_EN_SLAVE1_SHIFT) |
135                  (1 << MSELECT_CONFIG_0_READ_TIMEOUT_EN_SLAVE2_SHIFT)  |
136                  (1 << MSELECT_CONFIG_0_WRITE_TIMEOUT_EN_SLAVE2_SHIFT) |
137                  (1 << MSELECT_CONFIG_0_ERR_RESP_EN_SLAVE1_SHIFT)      |
138                  (1 << MSELECT_CONFIG_0_ERR_RESP_EN_SLAVE2_SHIFT)),
139                 ictlr->mselect_base + MSELECT_CONFIG_0);
140
141         return 0;
142 }
143
144 void tegra_hier_ictlr_set_mselect_timeout(struct tegra_hier_ictlr *ictlr,
145                                           u32 timeout_cycles)
146 {
147         ictlr->mselect_timeout_cycles = timeout_cycles;
148
149         writel(ictlr->mselect_timeout_cycles, ictlr->mselect_base +
150                 MSELECT_TIMEOUT_TIMER_0);
151 }
152
153 static int tegra_hier_ictlr_probe(struct platform_device *pdev)
154 {
155         struct tegra_hier_ictlr *ictlr;
156         int ret;
157
158         ictlr = devm_kzalloc(&pdev->dev, sizeof(struct tegra_hier_ictlr),
159                 GFP_KERNEL);
160         if (!ictlr)
161                 return -ENOMEM;
162
163         dev_set_drvdata(&pdev->dev, ictlr);
164
165         ret = tegra_hier_ictlr_map_memory(pdev, ictlr);
166         if (ret)
167                 return ret;
168
169         ret = tegra_hier_ictlr_mselect_init(pdev, ictlr);
170         if (ret)
171                 return ret;
172
173         ret = tegra_hier_ictlr_irq_init(pdev, ictlr);
174         if (ret)
175                 return ret;
176
177         tegra_hier_ictlr_create_sysfs(pdev);
178
179         dev_notice(&pdev->dev, "probed\n");
180
181         return 0;
182 }
183
184 static int __exit tegra_hier_ictlr_remove(struct platform_device *pdev)
185 {
186         tegra_hier_ictlr_remove_sysfs(pdev);
187         return 0;
188 }
189
190 static struct platform_driver tegra_hier_ictlr_driver = {
191         .driver = {
192                    .name = "tegra-hier-ictlr",
193                    .owner = THIS_MODULE,
194                    },
195         .probe = tegra_hier_ictlr_probe,
196         .remove = __exit_p(tegra_hier_ictrl_remove),
197 };
198
199 static int __init tegra_hier_ictlr_init(void)
200 {
201         return platform_driver_register(&tegra_hier_ictlr_driver);
202 }
203
204 static void __exit tegra_hier_ictlr_exit(void)
205 {
206         platform_driver_unregister(&tegra_hier_ictlr_driver);
207 }
208
209 module_init(tegra_hier_ictlr_init);
210 module_exit(tegra_hier_ictlr_exit);
211
212 MODULE_DESCRIPTION("Tegra Hierarchical Interrupt Controller Driver");
213 MODULE_LICENSE("GPL v2");