Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1be7f552 authored by Maxime Ripard's avatar Maxime Ripard
Browse files

power: reset: Add Allwinner A31 reset code



That code used to be in the machine code, but it's more fit here with other
restart hooks.

That will allow to cleanup the machine directory, while waiting for a proper
watchdog driver for the A31.

Signed-off-by: default avatarMaxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
parent cd90f0cf
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -43,6 +43,13 @@ config POWER_RESET_RESTART
	  Instead they restart, and u-boot holds the SoC until the
	  user presses a key. u-boot then boots into Linux.

config POWER_RESET_SUN6I
	bool "Allwinner A31 SoC reset driver"
	depends on ARCH_SUNXI
	depends on POWER_RESET
	help
	  Reboot support for the Allwinner A31 SoCs.

config POWER_RESET_VEXPRESS
	bool "ARM Versatile Express power-off and reset driver"
	depends on ARM || ARM64
+1 −0
Original line number Diff line number Diff line
@@ -3,5 +3,6 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o
obj-$(CONFIG_POWER_RESET_SUN6I) += sun6i-reboot.o
obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o
obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o
+85 −0
Original line number Diff line number Diff line
/*
 * Allwinner A31 SoCs reset code
 *
 * Copyright (C) 2012-2014 Maxime Ripard
 *
 * Maxime Ripard <maxime.ripard@free-electrons.com>
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2.  This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */

#include <linux/delay.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>

#include <asm/system_misc.h>

#define SUN6I_WATCHDOG1_IRQ_REG		0x00
#define SUN6I_WATCHDOG1_CTRL_REG	0x10
#define SUN6I_WATCHDOG1_CTRL_RESTART		BIT(0)
#define SUN6I_WATCHDOG1_CONFIG_REG	0x14
#define SUN6I_WATCHDOG1_CONFIG_RESTART		BIT(0)
#define SUN6I_WATCHDOG1_CONFIG_IRQ		BIT(1)
#define SUN6I_WATCHDOG1_MODE_REG	0x18
#define SUN6I_WATCHDOG1_MODE_ENABLE		BIT(0)

static void __iomem *wdt_base;

static void sun6i_wdt_restart(enum reboot_mode mode, const char *cmd)
{
	if (!wdt_base)
		return;

	/* Disable interrupts */
	writel(0, wdt_base + SUN6I_WATCHDOG1_IRQ_REG);

	/* We want to disable the IRQ and just reset the whole system */
	writel(SUN6I_WATCHDOG1_CONFIG_RESTART,
		wdt_base + SUN6I_WATCHDOG1_CONFIG_REG);

	/* Enable timer. The default and lowest interval value is 0.5s */
	writel(SUN6I_WATCHDOG1_MODE_ENABLE,
		wdt_base + SUN6I_WATCHDOG1_MODE_REG);

	/* Restart the watchdog. */
	writel(SUN6I_WATCHDOG1_CTRL_RESTART,
		wdt_base + SUN6I_WATCHDOG1_CTRL_REG);

	while (1) {
		mdelay(5);
		writel(SUN6I_WATCHDOG1_MODE_ENABLE,
			wdt_base + SUN6I_WATCHDOG1_MODE_REG);
	}
}

static int sun6i_reboot_probe(struct platform_device *pdev)
{
	wdt_base = of_iomap(pdev->dev.of_node, 0);
	if (!wdt_base) {
		WARN(1, "failed to map watchdog base address");
		return -ENODEV;
	}

	arm_pm_restart = sun6i_wdt_restart;

	return 0;
}

static struct of_device_id sun6i_reboot_of_match[] = {
	{ .compatible = "allwinner,sun6i-a31-wdt" },
	{}
};

static struct platform_driver sun6i_reboot_driver = {
	.probe = sun6i_reboot_probe,
	.driver = {
		.name = "sun6i-reboot",
		.of_match_table = sun6i_reboot_of_match,
	},
};
module_platform_driver(sun6i_reboot_driver);