]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/blob - drivers/platform/tegra/nvdumper/nvdumper.c
nvdumper: print based on event
[sojka/nv-tegra/linux-3.10.git] / drivers / platform / tegra / nvdumper / nvdumper.c
1 /*
2  * arch/arm/mach-tegra/nvdumper.c
3  *
4  * Copyright (c) 2011-2014, NVIDIA CORPORATION.  All rights reserved.
5  *
6  * This software is licensed under the terms of the GNU General Public
7  * License version 2, as published by the Free Software Foundation, and
8  * may be copied, distributed, and modified under those terms.
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  */
16
17 #include <linux/errno.h>
18 #include <linux/init.h>
19 #include <linux/io.h>
20 #include <linux/module.h>
21 #include <linux/reboot.h>
22 #include <linux/slab.h>
23 #include <linux/uaccess.h>
24 #include "../../../arch/arm/mach-tegra/board.h"
25 #include "nvdumper.h"
26 #include "nvdumper-footprint.h"
27
28 #ifdef CONFIG_TEGRA_USE_NCT
29 #include "../../../arch/arm/mach-tegra/include/mach/nct.h"
30 #endif
31
32 static void __init nvdumper_sysfs_init(void);
33 static void __exit nvdumper_sysfs_exit(void);
34
35 #define NVDUMPER_CLEAN      0xf000caf3U
36 #define NVDUMPER_DIRTY      0x2badfaceU
37 #define NVDUMPER_DIRTY_DUMP 0xdeadbeefU
38 #define NVDUMPER_WDT_DUMP   0x2badbeefU
39
40 #define RW_MODE (S_IWUSR | S_IRUGO)
41
42 static void __iomem *nvdumper_ptr;
43 static uint32_t nvdumper_last_reboot;
44
45 static uint32_t get_dirty_state(void)
46 {
47         return ioread32(nvdumper_ptr);
48 }
49
50 static void set_dirty_state(uint32_t state)
51 {
52         pr_info("nvdumper: set_dirty_state 0x%x\n", state);
53         iowrite32(state, nvdumper_ptr);
54 }
55
56 static int nvdumper_reboot_cb(struct notifier_block *nb,
57                 unsigned long event, void *unused)
58 {
59         pr_info("nvdumper: %s cleanly.\n",
60                 (event == SYS_RESTART) ? "rebooting" : "shutting down");
61         set_dirty_state(NVDUMPER_CLEAN);
62         return NOTIFY_DONE;
63 }
64
65 static struct notifier_block nvdumper_reboot_notifier = {
66         .notifier_call = nvdumper_reboot_cb,
67 };
68
69 static int __init nvdumper_init(void)
70 {
71         int ret;
72
73 #ifdef CONFIG_TEGRA_USE_NCT
74         union nct_item_type *item;
75 #endif
76
77         if (!nvdumper_reserved) {
78                 pr_info("nvdumper: not configured\n");
79                 return -ENOTSUPP;
80         }
81         nvdumper_ptr = ioremap_nocache(nvdumper_reserved,
82                         NVDUMPER_RESERVED_SIZE);
83         if (!nvdumper_ptr) {
84                 pr_info("nvdumper: failed to ioremap memory at 0x%08lx\n",
85                                 nvdumper_reserved);
86                 return -EIO;
87         }
88         ret = register_reboot_notifier(&nvdumper_reboot_notifier);
89         if (ret)
90                 goto err_out1;
91
92         ret = nvdumper_regdump_init();
93         if (ret)
94                 goto err_out2;
95
96         nvdumper_dbg_footprint_init();
97
98         nvdumper_last_reboot = get_dirty_state();
99         switch (nvdumper_last_reboot) {
100         case NVDUMPER_CLEAN:
101                 pr_info("nvdumper: last reboot was clean\n");
102                 break;
103         case NVDUMPER_DIRTY:
104         case NVDUMPER_DIRTY_DUMP:
105         case NVDUMPER_WDT_DUMP:
106                 pr_info("nvdumper: last reboot was dirty\n");
107                 break;
108         default:
109                 pr_info("nvdumper: last reboot was unknown\n");
110                 break;
111         }
112
113         nvdumper_sysfs_init();
114
115 #ifdef CONFIG_TEGRA_USE_NCT
116         item = kzalloc(sizeof(*item), GFP_KERNEL);
117         if (!item) {
118                 pr_err("failed to allocate memory\n");
119                 goto err_out3;
120         }
121
122         ret = tegra_nct_read_item(NCT_ID_RAMDUMP, item);
123         if (ret < 0) {
124                 pr_err("%s: NCT read failure\n", __func__);
125                 kfree(item);
126                 set_dirty_state(NVDUMPER_CLEAN);
127                 goto err_out0;
128         }
129
130         pr_info("%s: RAMDUMP flag(%d) from NCT\n",
131                         __func__, item->ramdump.flag);
132         if (item->ramdump.flag == 1)
133                 set_dirty_state(NVDUMPER_DIRTY_DUMP);
134         else if (item->ramdump.flag == 2)
135                 set_dirty_state(NVDUMPER_DIRTY);
136         else if (item->ramdump.flag == 3)
137                 set_dirty_state(NVDUMPER_WDT_DUMP);
138         else
139                 set_dirty_state(NVDUMPER_CLEAN);
140
141         kfree(item);
142
143         return 0;
144
145 err_out3:
146
147 #else
148         set_dirty_state(NVDUMPER_DIRTY);
149         return 0;
150 #endif
151
152 err_out2:
153         unregister_reboot_notifier(&nvdumper_reboot_notifier);
154 err_out1:
155         iounmap(nvdumper_ptr);
156
157 #ifdef CONFIG_TEGRA_USE_NCT /* avoid build error if NCT is not enabled*/
158 err_out0:
159 #endif
160
161         return ret;
162
163 }
164
165 static void __exit nvdumper_exit(void)
166 {
167         nvdumper_sysfs_exit();
168         nvdumper_regdump_exit();
169         nvdumper_dbg_footprint_exit();
170         unregister_reboot_notifier(&nvdumper_reboot_notifier);
171         set_dirty_state(NVDUMPER_CLEAN);
172         iounmap(nvdumper_ptr);
173 }
174
175 static char *nvdumper_set_str = "dirty_dump";
176 static struct kobject *nvdumper_kobj;
177
178 static ssize_t nvdumper_set_show(struct kobject *kobj,
179         struct kobj_attribute *attr,
180         char *buf)
181 {
182         return sprintf(buf, "%s\n", nvdumper_set_str);
183 }
184
185 static ssize_t nvdumper_set_store(struct kobject *kobj,
186         struct kobj_attribute *attr,
187         const char *buf, size_t n)
188 {
189         if (n < 1)
190                 return 0;
191
192         sprintf(nvdumper_set_str, "%s", buf);
193         nvdumper_set_str[n-1] = '\0';
194
195         if (!strcmp(nvdumper_set_str, "clean"))
196                 set_dirty_state(NVDUMPER_CLEAN);
197         else if (!strcmp(nvdumper_set_str, "dirty"))
198                 set_dirty_state(NVDUMPER_DIRTY);
199         else if (!strcmp(nvdumper_set_str, "dirty_dump"))
200                 set_dirty_state(NVDUMPER_DIRTY_DUMP);
201         else if (!strcmp(nvdumper_set_str, "wdt_dump"))
202                 set_dirty_state(NVDUMPER_WDT_DUMP);
203         else
204                 strcpy(nvdumper_set_str, "unknown");
205
206         pr_info("nvdumper_set was updated to %s\n", nvdumper_set_str);
207
208         return n;
209 }
210
211 static ssize_t nvdumper_prev_show(struct kobject *kobj,
212         struct kobj_attribute *attr,
213         char *buf)
214 {
215         return sprintf(buf, "%s\n", nvdumper_set_str);
216 }
217
218 static const struct kobj_attribute nvdumper_attr[] = {
219         __ATTR(nvdumper_set, 0644, nvdumper_set_show, nvdumper_set_store),
220         __ATTR(nvdumper_prev, 0444, nvdumper_prev_show, NULL),
221 };
222
223 static void __init nvdumper_sysfs_init(void)
224 {
225         int i, ret = 0;
226
227         nvdumper_kobj = kobject_create_and_add("nvdumper", kernel_kobj);
228
229         if (!nvdumper_kobj) {
230                 pr_err("unable to create nvdumper kernel object!\n");
231                 return;
232         }
233
234         /* create sysfs */
235         for (i = 0; i < ARRAY_SIZE(nvdumper_attr); i++) {
236                 ret = sysfs_create_file(nvdumper_kobj, &nvdumper_attr[i].attr);
237                 if (ret)
238                         pr_err("failed to create %s\n",
239                                 nvdumper_attr[i].attr.name);
240         }
241
242         switch (nvdumper_last_reboot) {
243         case NVDUMPER_CLEAN:
244                 nvdumper_set_str = "clean\n";
245                 break;
246         case NVDUMPER_DIRTY:
247                 nvdumper_set_str = "dirty\n";
248                 break;
249         case NVDUMPER_DIRTY_DUMP:
250                 nvdumper_set_str = "dirty_dump\n";
251                 break;
252         default:
253                 nvdumper_set_str = "dirty\n";
254                 break;
255         }
256 }
257
258 static void __exit nvdumper_sysfs_exit(void)
259 {
260         int i;
261
262         if (!nvdumper_kobj)
263                 return;
264
265         for (i = 0; i < ARRAY_SIZE(nvdumper_attr); i++)
266                 sysfs_remove_file(nvdumper_kobj, &nvdumper_attr[i].attr);
267 }
268
269 arch_initcall(nvdumper_init);
270 module_exit(nvdumper_exit);
271
272 MODULE_LICENSE("GPL");