xref: /openbmc/linux/arch/arm/mach-sunxi/sunxi.c (revision 91a31977)
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 
13ea71d9a6SMaxime Ripard #include <linux/clocksource.h>
145e51651dSJosh Cartwright #include <linux/delay.h>
153b52634fSMaxime Ripard #include <linux/kernel.h>
163b52634fSMaxime Ripard #include <linux/init.h>
1767bea88dSMaxime Ripard #include <linux/of_address.h>
183b52634fSMaxime Ripard #include <linux/of_irq.h>
193b52634fSMaxime Ripard #include <linux/of_platform.h>
203b52634fSMaxime Ripard #include <linux/io.h>
217b6d864bSRobin Holt #include <linux/reboot.h>
223b52634fSMaxime Ripard 
23ea71d9a6SMaxime Ripard #include <linux/clk/sunxi.h>
243b52634fSMaxime Ripard 
253b52634fSMaxime Ripard #include <asm/mach/arch.h>
263b52634fSMaxime Ripard #include <asm/mach/map.h>
27bc34b5f2SMaxime Ripard #include <asm/system_misc.h>
283b52634fSMaxime Ripard 
29bc34b5f2SMaxime Ripard #define SUN4I_WATCHDOG_CTRL_REG		0x00
3006d71bcfSMaxime Ripard #define SUN4I_WATCHDOG_CTRL_RESTART		BIT(0)
31bc34b5f2SMaxime Ripard #define SUN4I_WATCHDOG_MODE_REG		0x04
3206d71bcfSMaxime Ripard #define SUN4I_WATCHDOG_MODE_ENABLE		BIT(0)
3306d71bcfSMaxime Ripard #define SUN4I_WATCHDOG_MODE_RESET_ENABLE	BIT(1)
3406d71bcfSMaxime Ripard 
3506d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_IRQ_REG		0x00
3606d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_CTRL_REG	0x10
3706d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_CTRL_RESTART		BIT(0)
3806d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_CONFIG_REG	0x14
3906d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_CONFIG_RESTART		BIT(0)
4006d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_CONFIG_IRQ		BIT(1)
4106d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_MODE_REG	0x18
4206d71bcfSMaxime Ripard #define SUN6I_WATCHDOG1_MODE_ENABLE		BIT(0)
4367bea88dSMaxime Ripard 
4467bea88dSMaxime Ripard static void __iomem *wdt_base;
4567bea88dSMaxime Ripard 
467b6d864bSRobin Holt static void sun4i_restart(enum reboot_mode mode, const char *cmd)
4767bea88dSMaxime Ripard {
4867bea88dSMaxime Ripard 	if (!wdt_base)
4967bea88dSMaxime Ripard 		return;
5067bea88dSMaxime Ripard 
5167bea88dSMaxime Ripard 	/* Enable timer and set reset bit in the watchdog */
52bc34b5f2SMaxime Ripard 	writel(SUN4I_WATCHDOG_MODE_ENABLE | SUN4I_WATCHDOG_MODE_RESET_ENABLE,
53bc34b5f2SMaxime Ripard 	       wdt_base + SUN4I_WATCHDOG_MODE_REG);
54b60decadSMaxime Ripard 
55b60decadSMaxime Ripard 	/*
56b60decadSMaxime Ripard 	 * Restart the watchdog. The default (and lowest) interval
57b60decadSMaxime Ripard 	 * value for the watchdog is 0.5s.
58b60decadSMaxime Ripard 	 */
59bc34b5f2SMaxime Ripard 	writel(SUN4I_WATCHDOG_CTRL_RESTART, wdt_base + SUN4I_WATCHDOG_CTRL_REG);
60b60decadSMaxime Ripard 
6167bea88dSMaxime Ripard 	while (1) {
6267bea88dSMaxime Ripard 		mdelay(5);
63bc34b5f2SMaxime Ripard 		writel(SUN4I_WATCHDOG_MODE_ENABLE | SUN4I_WATCHDOG_MODE_RESET_ENABLE,
64bc34b5f2SMaxime Ripard 		       wdt_base + SUN4I_WATCHDOG_MODE_REG);
6567bea88dSMaxime Ripard 	}
6667bea88dSMaxime Ripard }
6767bea88dSMaxime Ripard 
6806d71bcfSMaxime Ripard static void sun6i_restart(enum reboot_mode mode, const char *cmd)
6906d71bcfSMaxime Ripard {
7006d71bcfSMaxime Ripard 	if (!wdt_base)
7106d71bcfSMaxime Ripard 		return;
7206d71bcfSMaxime Ripard 
7306d71bcfSMaxime Ripard 	/* Disable interrupts */
7406d71bcfSMaxime Ripard 	writel(0, wdt_base + SUN6I_WATCHDOG1_IRQ_REG);
7506d71bcfSMaxime Ripard 
7606d71bcfSMaxime Ripard 	/* We want to disable the IRQ and just reset the whole system */
7706d71bcfSMaxime Ripard 	writel(SUN6I_WATCHDOG1_CONFIG_RESTART,
7806d71bcfSMaxime Ripard 		wdt_base + SUN6I_WATCHDOG1_CONFIG_REG);
7906d71bcfSMaxime Ripard 
8006d71bcfSMaxime Ripard 	/* Enable timer. The default and lowest interval value is 0.5s */
8106d71bcfSMaxime Ripard 	writel(SUN6I_WATCHDOG1_MODE_ENABLE,
8206d71bcfSMaxime Ripard 		wdt_base + SUN6I_WATCHDOG1_MODE_REG);
8306d71bcfSMaxime Ripard 
8406d71bcfSMaxime Ripard 	/* Restart the watchdog. */
8506d71bcfSMaxime Ripard 	writel(SUN6I_WATCHDOG1_CTRL_RESTART,
8606d71bcfSMaxime Ripard 		wdt_base + SUN6I_WATCHDOG1_CTRL_REG);
8706d71bcfSMaxime Ripard 
8806d71bcfSMaxime Ripard 	while (1) {
8906d71bcfSMaxime Ripard 		mdelay(5);
9006d71bcfSMaxime Ripard 		writel(SUN6I_WATCHDOG1_MODE_ENABLE,
9106d71bcfSMaxime Ripard 			wdt_base + SUN6I_WATCHDOG1_MODE_REG);
9206d71bcfSMaxime Ripard 	}
9306d71bcfSMaxime Ripard }
9406d71bcfSMaxime Ripard 
95bc34b5f2SMaxime Ripard static struct of_device_id sunxi_restart_ids[] = {
96bc34b5f2SMaxime Ripard 	{ .compatible = "allwinner,sun4i-wdt", .data = sun4i_restart },
9706d71bcfSMaxime Ripard 	{ .compatible = "allwinner,sun6i-wdt", .data = sun6i_restart },
98bc34b5f2SMaxime Ripard 	{ /*sentinel*/ }
99bc34b5f2SMaxime Ripard };
100bc34b5f2SMaxime Ripard 
101bc34b5f2SMaxime Ripard static void sunxi_setup_restart(void)
102bc34b5f2SMaxime Ripard {
103bc34b5f2SMaxime Ripard 	const struct of_device_id *of_id;
104bc34b5f2SMaxime Ripard 	struct device_node *np;
105bc34b5f2SMaxime Ripard 
106bc34b5f2SMaxime Ripard 	np = of_find_matching_node(NULL, sunxi_restart_ids);
107bc34b5f2SMaxime Ripard 	if (WARN(!np, "unable to setup watchdog restart"))
108bc34b5f2SMaxime Ripard 		return;
109bc34b5f2SMaxime Ripard 
110bc34b5f2SMaxime Ripard 	wdt_base = of_iomap(np, 0);
111bc34b5f2SMaxime Ripard 	WARN(!wdt_base, "failed to map watchdog base address");
112bc34b5f2SMaxime Ripard 
113bc34b5f2SMaxime Ripard 	of_id = of_match_node(sunxi_restart_ids, np);
114bc34b5f2SMaxime Ripard 	WARN(!of_id, "restart function not available");
115bc34b5f2SMaxime Ripard 
116bc34b5f2SMaxime Ripard 	arm_pm_restart = of_id->data;
117bc34b5f2SMaxime Ripard }
118bc34b5f2SMaxime Ripard 
119ea71d9a6SMaxime Ripard static void __init sunxi_timer_init(void)
120ea71d9a6SMaxime Ripard {
121ea71d9a6SMaxime Ripard 	sunxi_init_clocks();
122ea71d9a6SMaxime Ripard 	clocksource_of_init();
123ea71d9a6SMaxime Ripard }
124ea71d9a6SMaxime Ripard 
1253b52634fSMaxime Ripard static void __init sunxi_dt_init(void)
1263b52634fSMaxime Ripard {
12767bea88dSMaxime Ripard 	sunxi_setup_restart();
12867bea88dSMaxime Ripard 
1293b52634fSMaxime Ripard 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
1303b52634fSMaxime Ripard }
1313b52634fSMaxime Ripard 
1323b52634fSMaxime Ripard static const char * const sunxi_board_dt_compat[] = {
13343880f70SMaxime Ripard 	"allwinner,sun4i-a10",
13481265dfbSMaxime Ripard 	"allwinner,sun5i-a10s",
13543880f70SMaxime Ripard 	"allwinner,sun5i-a13",
1363b52634fSMaxime Ripard 	NULL,
1373b52634fSMaxime Ripard };
1383b52634fSMaxime Ripard 
1393b52634fSMaxime Ripard DT_MACHINE_START(SUNXI_DT, "Allwinner A1X (Device Tree)")
1403b52634fSMaxime Ripard 	.init_machine	= sunxi_dt_init,
141ea71d9a6SMaxime Ripard 	.init_time	= sunxi_timer_init,
1423b52634fSMaxime Ripard 	.dt_compat	= sunxi_board_dt_compat,
1433b52634fSMaxime Ripard MACHINE_END
14491a31977SMaxime Ripard 
14591a31977SMaxime Ripard static const char * const sun6i_board_dt_compat[] = {
14691a31977SMaxime Ripard 	"allwinner,sun6i-a31",
14791a31977SMaxime Ripard 	NULL,
14891a31977SMaxime Ripard };
14991a31977SMaxime Ripard 
15091a31977SMaxime Ripard DT_MACHINE_START(SUN6I_DT, "Allwinner sun6i (A31) Family")
15191a31977SMaxime Ripard 	.init_machine	= sunxi_dt_init,
15291a31977SMaxime Ripard 	.init_time	= sunxi_timer_init,
15391a31977SMaxime Ripard 	.dt_compat	= sun6i_board_dt_compat,
15491a31977SMaxime Ripard MACHINE_END
15591a31977SMaxime Ripard 
15691a31977SMaxime Ripard static const char * const sun7i_board_dt_compat[] = {
15791a31977SMaxime Ripard 	"allwinner,sun7i-a20",
15891a31977SMaxime Ripard 	NULL,
15991a31977SMaxime Ripard };
16091a31977SMaxime Ripard 
16191a31977SMaxime Ripard DT_MACHINE_START(SUN7I_DT, "Allwinner sun7i (A20) Family")
16291a31977SMaxime Ripard 	.init_machine	= sunxi_dt_init,
16391a31977SMaxime Ripard 	.init_time	= sunxi_timer_init,
16491a31977SMaxime Ripard 	.dt_compat	= sun7i_board_dt_compat,
16591a31977SMaxime Ripard MACHINE_END
166