13b52634fSMaxime Ripard /* 23b52634fSMaxime Ripard * Device Tree support for Allwinner A1X SoCs 33b52634fSMaxime Ripard * 43b52634fSMaxime Ripard * Copyright (C) 2012 Maxime Ripard 53b52634fSMaxime Ripard * 63b52634fSMaxime Ripard * Maxime Ripard <maxime.ripard@free-electrons.com> 73b52634fSMaxime Ripard * 83b52634fSMaxime Ripard * This file is licensed under the terms of the GNU General Public 93b52634fSMaxime Ripard * License version 2. This program is licensed "as is" without any 103b52634fSMaxime Ripard * warranty of any kind, whether express or implied. 113b52634fSMaxime Ripard */ 123b52634fSMaxime Ripard 13751b2ac4SMaxime Ripard #include <linux/clk-provider.h> 14751b2ac4SMaxime Ripard #include <linux/clocksource.h> 155e51651dSJosh Cartwright #include <linux/delay.h> 163b52634fSMaxime Ripard #include <linux/kernel.h> 173b52634fSMaxime Ripard #include <linux/init.h> 1867bea88dSMaxime Ripard #include <linux/of_address.h> 193b52634fSMaxime Ripard #include <linux/of_irq.h> 203b52634fSMaxime Ripard #include <linux/of_platform.h> 213b52634fSMaxime Ripard #include <linux/io.h> 227b6d864bSRobin Holt #include <linux/reboot.h> 233b52634fSMaxime Ripard 243b52634fSMaxime Ripard #include <asm/mach/arch.h> 253b52634fSMaxime Ripard #include <asm/mach/map.h> 26bc34b5f2SMaxime Ripard #include <asm/system_misc.h> 273b52634fSMaxime Ripard 28bc34b5f2SMaxime Ripard #define SUN4I_WATCHDOG_CTRL_REG 0x00 2906d71bcfSMaxime Ripard #define SUN4I_WATCHDOG_CTRL_RESTART BIT(0) 30bc34b5f2SMaxime Ripard #define SUN4I_WATCHDOG_MODE_REG 0x04 3106d71bcfSMaxime Ripard #define SUN4I_WATCHDOG_MODE_ENABLE BIT(0) 3206d71bcfSMaxime Ripard #define SUN4I_WATCHDOG_MODE_RESET_ENABLE BIT(1) 3306d71bcfSMaxime Ripard 3406d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_IRQ_REG 0x00 3506d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_CTRL_REG 0x10 3606d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_CTRL_RESTART BIT(0) 3706d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_CONFIG_REG 0x14 3806d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_CONFIG_RESTART BIT(0) 3906d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_CONFIG_IRQ BIT(1) 4006d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_MODE_REG 0x18 4106d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_MODE_ENABLE BIT(0) 4267bea88dSMaxime Ripard 4367bea88dSMaxime Ripard static void __iomem *wdt_base; 4467bea88dSMaxime Ripard 457b6d864bSRobin Holt static void sun4i_restart(enum reboot_mode mode, const char *cmd) 4667bea88dSMaxime Ripard { 4767bea88dSMaxime Ripard if (!wdt_base) 4867bea88dSMaxime Ripard return; 4967bea88dSMaxime Ripard 5067bea88dSMaxime Ripard /* Enable timer and set reset bit in the watchdog */ 51bc34b5f2SMaxime Ripard writel(SUN4I_WATCHDOG_MODE_ENABLE | SUN4I_WATCHDOG_MODE_RESET_ENABLE, 52bc34b5f2SMaxime Ripard wdt_base + SUN4I_WATCHDOG_MODE_REG); 53b60decadSMaxime Ripard 54b60decadSMaxime Ripard /* 55b60decadSMaxime Ripard * Restart the watchdog. The default (and lowest) interval 56b60decadSMaxime Ripard * value for the watchdog is 0.5s. 57b60decadSMaxime Ripard */ 58bc34b5f2SMaxime Ripard writel(SUN4I_WATCHDOG_CTRL_RESTART, wdt_base + SUN4I_WATCHDOG_CTRL_REG); 59b60decadSMaxime Ripard 6067bea88dSMaxime Ripard while (1) { 6167bea88dSMaxime Ripard mdelay(5); 62bc34b5f2SMaxime Ripard writel(SUN4I_WATCHDOG_MODE_ENABLE | SUN4I_WATCHDOG_MODE_RESET_ENABLE, 63bc34b5f2SMaxime Ripard wdt_base + SUN4I_WATCHDOG_MODE_REG); 6467bea88dSMaxime Ripard } 6567bea88dSMaxime Ripard } 6667bea88dSMaxime Ripard 6706d71bcfSMaxime Ripard static void sun6i_restart(enum reboot_mode mode, const char *cmd) 6806d71bcfSMaxime Ripard { 6906d71bcfSMaxime Ripard if (!wdt_base) 7006d71bcfSMaxime Ripard return; 7106d71bcfSMaxime Ripard 7206d71bcfSMaxime Ripard /* Disable interrupts */ 7306d71bcfSMaxime Ripard writel(0, wdt_base + SUN6I_WATCHDOG1_IRQ_REG); 7406d71bcfSMaxime Ripard 7506d71bcfSMaxime Ripard /* We want to disable the IRQ and just reset the whole system */ 7606d71bcfSMaxime Ripard writel(SUN6I_WATCHDOG1_CONFIG_RESTART, 7706d71bcfSMaxime Ripard wdt_base + SUN6I_WATCHDOG1_CONFIG_REG); 7806d71bcfSMaxime Ripard 7906d71bcfSMaxime Ripard /* Enable timer. The default and lowest interval value is 0.5s */ 8006d71bcfSMaxime Ripard writel(SUN6I_WATCHDOG1_MODE_ENABLE, 8106d71bcfSMaxime Ripard wdt_base + SUN6I_WATCHDOG1_MODE_REG); 8206d71bcfSMaxime Ripard 8306d71bcfSMaxime Ripard /* Restart the watchdog. */ 8406d71bcfSMaxime Ripard writel(SUN6I_WATCHDOG1_CTRL_RESTART, 8506d71bcfSMaxime Ripard wdt_base + SUN6I_WATCHDOG1_CTRL_REG); 8606d71bcfSMaxime Ripard 8706d71bcfSMaxime Ripard while (1) { 8806d71bcfSMaxime Ripard mdelay(5); 8906d71bcfSMaxime Ripard writel(SUN6I_WATCHDOG1_MODE_ENABLE, 9006d71bcfSMaxime Ripard wdt_base + SUN6I_WATCHDOG1_MODE_REG); 9106d71bcfSMaxime Ripard } 9206d71bcfSMaxime Ripard } 9306d71bcfSMaxime Ripard 94bc34b5f2SMaxime Ripard static struct of_device_id sunxi_restart_ids[] = { 9553ea6887SMaxime Ripard { .compatible = "allwinner,sun4i-wdt" }, 9653ea6887SMaxime Ripard { .compatible = "allwinner,sun6i-wdt" }, 97bc34b5f2SMaxime Ripard { /*sentinel*/ } 98bc34b5f2SMaxime Ripard }; 99bc34b5f2SMaxime Ripard 100bc34b5f2SMaxime Ripard static void sunxi_setup_restart(void) 101bc34b5f2SMaxime Ripard { 102bc34b5f2SMaxime Ripard struct device_node *np; 103bc34b5f2SMaxime Ripard 104bc34b5f2SMaxime Ripard np = of_find_matching_node(NULL, sunxi_restart_ids); 105bc34b5f2SMaxime Ripard if (WARN(!np, "unable to setup watchdog restart")) 106bc34b5f2SMaxime Ripard return; 107bc34b5f2SMaxime Ripard 108bc34b5f2SMaxime Ripard wdt_base = of_iomap(np, 0); 109bc34b5f2SMaxime Ripard WARN(!wdt_base, "failed to map watchdog base address"); 110bc34b5f2SMaxime Ripard } 111bc34b5f2SMaxime Ripard 1123b52634fSMaxime Ripard static void __init sunxi_dt_init(void) 1133b52634fSMaxime Ripard { 11467bea88dSMaxime Ripard sunxi_setup_restart(); 11567bea88dSMaxime Ripard 1163b52634fSMaxime Ripard of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); 1173b52634fSMaxime Ripard } 1183b52634fSMaxime Ripard 1193b52634fSMaxime Ripard static const char * const sunxi_board_dt_compat[] = { 12043880f70SMaxime Ripard "allwinner,sun4i-a10", 12181265dfbSMaxime Ripard "allwinner,sun5i-a10s", 12243880f70SMaxime Ripard "allwinner,sun5i-a13", 1233b52634fSMaxime Ripard NULL, 1243b52634fSMaxime Ripard }; 1253b52634fSMaxime Ripard 1263b52634fSMaxime Ripard DT_MACHINE_START(SUNXI_DT, "Allwinner A1X (Device Tree)") 1273b52634fSMaxime Ripard .init_machine = sunxi_dt_init, 1283b52634fSMaxime Ripard .dt_compat = sunxi_board_dt_compat, 12953ea6887SMaxime Ripard .restart = sun4i_restart, 1303b52634fSMaxime Ripard MACHINE_END 13191a31977SMaxime Ripard 13291a31977SMaxime Ripard static const char * const sun6i_board_dt_compat[] = { 13391a31977SMaxime Ripard "allwinner,sun6i-a31", 13491a31977SMaxime Ripard NULL, 13591a31977SMaxime Ripard }; 13691a31977SMaxime Ripard 137751b2ac4SMaxime Ripard extern void __init sun6i_reset_init(void); 138751b2ac4SMaxime Ripard static void __init sun6i_timer_init(void) 139751b2ac4SMaxime Ripard { 140751b2ac4SMaxime Ripard of_clk_init(NULL); 141751b2ac4SMaxime Ripard sun6i_reset_init(); 142751b2ac4SMaxime Ripard clocksource_of_init(); 143751b2ac4SMaxime Ripard } 144751b2ac4SMaxime Ripard 14591a31977SMaxime Ripard DT_MACHINE_START(SUN6I_DT, "Allwinner sun6i (A31) Family") 14691a31977SMaxime Ripard .init_machine = sunxi_dt_init, 147751b2ac4SMaxime Ripard .init_time = sun6i_timer_init, 14891a31977SMaxime Ripard .dt_compat = sun6i_board_dt_compat, 14953ea6887SMaxime Ripard .restart = sun6i_restart, 15091a31977SMaxime Ripard MACHINE_END 15191a31977SMaxime Ripard 15291a31977SMaxime Ripard static const char * const sun7i_board_dt_compat[] = { 15391a31977SMaxime Ripard "allwinner,sun7i-a20", 15491a31977SMaxime Ripard NULL, 15591a31977SMaxime Ripard }; 15691a31977SMaxime Ripard 15791a31977SMaxime Ripard DT_MACHINE_START(SUN7I_DT, "Allwinner sun7i (A20) Family") 15891a31977SMaxime Ripard .init_machine = sunxi_dt_init, 15991a31977SMaxime Ripard .dt_compat = sun7i_board_dt_compat, 16053ea6887SMaxime Ripard .restart = sun4i_restart, 1613b52634fSMaxime Ripard MACHINE_END 162