Linux-libre 3.18.37-gnu
[librecmc/linux-libre.git] / drivers / power / reset / sun6i-reboot.c
1 /*
2  * Allwinner A31 SoCs reset code
3  *
4  * Copyright (C) 2012-2014 Maxime Ripard
5  *
6  * Maxime Ripard <maxime.ripard@free-electrons.com>
7  *
8  * This file is licensed under the terms of the GNU General Public
9  * License version 2.  This program is licensed "as is" without any
10  * warranty of any kind, whether express or implied.
11  */
12
13 #include <linux/delay.h>
14 #include <linux/io.h>
15 #include <linux/module.h>
16 #include <linux/of_address.h>
17 #include <linux/platform_device.h>
18 #include <linux/reboot.h>
19
20 #include <asm/system_misc.h>
21
22 #define SUN6I_WATCHDOG1_IRQ_REG         0x00
23 #define SUN6I_WATCHDOG1_CTRL_REG        0x10
24 #define SUN6I_WATCHDOG1_CTRL_RESTART            BIT(0)
25 #define SUN6I_WATCHDOG1_CONFIG_REG      0x14
26 #define SUN6I_WATCHDOG1_CONFIG_RESTART          BIT(0)
27 #define SUN6I_WATCHDOG1_CONFIG_IRQ              BIT(1)
28 #define SUN6I_WATCHDOG1_MODE_REG        0x18
29 #define SUN6I_WATCHDOG1_MODE_ENABLE             BIT(0)
30
31 static void __iomem *wdt_base;
32
33 static void sun6i_wdt_restart(enum reboot_mode mode, const char *cmd)
34 {
35         if (!wdt_base)
36                 return;
37
38         /* Disable interrupts */
39         writel(0, wdt_base + SUN6I_WATCHDOG1_IRQ_REG);
40
41         /* We want to disable the IRQ and just reset the whole system */
42         writel(SUN6I_WATCHDOG1_CONFIG_RESTART,
43                 wdt_base + SUN6I_WATCHDOG1_CONFIG_REG);
44
45         /* Enable timer. The default and lowest interval value is 0.5s */
46         writel(SUN6I_WATCHDOG1_MODE_ENABLE,
47                 wdt_base + SUN6I_WATCHDOG1_MODE_REG);
48
49         /* Restart the watchdog. */
50         writel(SUN6I_WATCHDOG1_CTRL_RESTART,
51                 wdt_base + SUN6I_WATCHDOG1_CTRL_REG);
52
53         while (1) {
54                 mdelay(5);
55                 writel(SUN6I_WATCHDOG1_MODE_ENABLE,
56                         wdt_base + SUN6I_WATCHDOG1_MODE_REG);
57         }
58 }
59
60 static int sun6i_reboot_probe(struct platform_device *pdev)
61 {
62         wdt_base = of_iomap(pdev->dev.of_node, 0);
63         if (!wdt_base) {
64                 WARN(1, "failed to map watchdog base address");
65                 return -ENODEV;
66         }
67
68         arm_pm_restart = sun6i_wdt_restart;
69
70         return 0;
71 }
72
73 static struct of_device_id sun6i_reboot_of_match[] = {
74         { .compatible = "allwinner,sun6i-a31-wdt" },
75         {}
76 };
77
78 static struct platform_driver sun6i_reboot_driver = {
79         .probe = sun6i_reboot_probe,
80         .driver = {
81                 .name = "sun6i-reboot",
82                 .of_match_table = sun6i_reboot_of_match,
83         },
84 };
85 module_platform_driver(sun6i_reboot_driver);