Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

154
drivers/power/reset/Kconfig Normal file
View file

@ -0,0 +1,154 @@
menuconfig POWER_RESET
bool "Board level reset or power off"
help
Provides a number of drivers which either reset a complete board
or shut it down, by manipulating the main power supply on the board.
Say Y here to enable board reset and power off
if POWER_RESET
config POWER_RESET_AS3722
bool "ams AS3722 power-off driver"
depends on MFD_AS3722
help
This driver supports turning off board via a ams AS3722 power-off.
config POWER_RESET_AT91_POWEROFF
bool "Atmel AT91 poweroff driver"
depends on ARCH_AT91
default SOC_AT91SAM9 || SOC_SAMA5
help
This driver supports poweroff for Atmel AT91SAM9 and SAMA5
SoCs
config POWER_RESET_AT91_RESET
bool "Atmel AT91 reset driver"
depends on ARCH_AT91
default SOC_AT91SAM9 || SOC_SAMA5
help
This driver supports restart for Atmel AT91SAM9 and SAMA5
SoCs
config POWER_RESET_AXXIA
bool "LSI Axxia reset driver"
depends on ARCH_AXXIA
help
This driver supports restart for Axxia SoC.
Say Y if you have an Axxia family SoC.
config POWER_RESET_BRCMSTB
bool "Broadcom STB reset driver" if COMPILE_TEST
depends on ARM
default ARCH_BRCMSTB
help
This driver provides restart support for ARM-based Broadcom STB
boards.
Say Y here if you have an ARM-based Broadcom STB board and you wish
to have restart support.
config POWER_RESET_GPIO
bool "GPIO power-off driver"
depends on OF_GPIO
help
This driver supports turning off your board via a GPIO line.
If your board needs a GPIO high/low to power down, say Y and
create a binding in your devicetree.
config POWER_RESET_GPIO_RESTART
bool "GPIO restart driver"
depends on OF_GPIO
help
This driver supports restarting your board via a GPIO line.
If your board needs a GPIO high/low to restart, say Y and
create a binding in your devicetree.
config POWER_RESET_HISI
bool "Hisilicon power-off driver"
depends on ARCH_HISI
help
Reboot support for Hisilicon boards.
config POWER_RESET_MSM
bool "Qualcomm MSM power-off driver"
depends on ARCH_QCOM
help
Power off and restart support for Qualcomm boards.
config POWER_RESET_LTC2952
bool "LTC2952 PowerPath power-off driver"
depends on OF_GPIO
help
This driver supports an external powerdown trigger and board power
down via the LTC2952. Bindings are made in the device tree.
config POWER_RESET_QNAP
bool "QNAP power-off driver"
depends on OF_GPIO && PLAT_ORION
help
This driver supports turning off QNAP NAS devices by sending
commands to the microcontroller which controls the main power.
Say Y if you have a QNAP NAS.
config POWER_RESET_RESTART
bool "Restart power-off driver"
depends on ARM
help
Some boards don't actually have the ability to power off.
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
help
Reboot support for the Allwinner A31 SoCs.
config POWER_RESET_ST
bool "ST restart power-off driver"
depends on ARCH_STI
help
Power off and reset support for STMicroelectronics boards.
config POWER_RESET_VERSATILE
bool "ARM Versatile family reboot driver"
depends on ARM
depends on MFD_SYSCON
depends on OF
help
Power off and restart support for ARM Versatile family of
reference boards.
config POWER_RESET_VEXPRESS
bool "ARM Versatile Express power-off and reset driver"
depends on ARM || ARM64
depends on VEXPRESS_CONFIG
help
Power off and reset support for the ARM Ltd. Versatile
Express boards.
config POWER_RESET_XGENE
bool "APM SoC X-Gene reset driver"
depends on ARM64
help
Reboot support for the APM SoC X-Gene Eval boards.
config POWER_RESET_KEYSTONE
bool "Keystone reset driver"
depends on ARCH_KEYSTONE
select MFD_SYSCON
help
Reboot support for the KEYSTONE SoCs.
config POWER_RESET_SYSCON
bool "Generic SYSCON regmap reset driver"
depends on OF
select MFD_SYSCON
help
Reboot support for generic SYSCON mapped register reset.
endif

View file

@ -0,0 +1,19 @@
obj-$(CONFIG_POWER_RESET_AS3722) += as3722-poweroff.o
obj-$(CONFIG_POWER_RESET_AT91_POWEROFF) += at91-poweroff.o
obj-$(CONFIG_POWER_RESET_AT91_RESET) += at91-reset.o
obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o
obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o
obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-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_ST) += st-poweroff.o
obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o
obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o
obj-$(CONFIG_POWER_RESET_XGENE) += xgene-reboot.o
obj-$(CONFIG_POWER_RESET_KEYSTONE) += keystone-reset.o
obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o

View file

@ -0,0 +1,111 @@
/*
* Copyright (C) 2014 Linaro Ltd.
*
* Author: Linus Walleij <linus.walleij@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
*/
#include <linux/init.h>
#include <linux/mfd/syscon.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/of.h>
#include <asm/system_misc.h>
#define REALVIEW_SYS_LOCK_OFFSET 0x20
#define REALVIEW_SYS_LOCK_VAL 0xA05F
#define REALVIEW_SYS_RESETCTL_OFFSET 0x40
/*
* We detect the different syscon types from the compatible strings.
*/
enum versatile_reboot {
REALVIEW_REBOOT_EB,
REALVIEW_REBOOT_PB1176,
REALVIEW_REBOOT_PB11MP,
REALVIEW_REBOOT_PBA8,
REALVIEW_REBOOT_PBX,
};
/* Pointer to the system controller */
static struct regmap *syscon_regmap;
static enum versatile_reboot versatile_reboot_type;
static const struct of_device_id versatile_reboot_of_match[] = {
{
.compatible = "arm,realview-eb-syscon",
.data = (void *)REALVIEW_REBOOT_EB,
},
{
.compatible = "arm,realview-pb1176-syscon",
.data = (void *)REALVIEW_REBOOT_PB1176,
},
{
.compatible = "arm,realview-pb11mp-syscon",
.data = (void *)REALVIEW_REBOOT_PB11MP,
},
{
.compatible = "arm,realview-pba8-syscon",
.data = (void *)REALVIEW_REBOOT_PBA8,
},
{
.compatible = "arm,realview-pbx-syscon",
.data = (void *)REALVIEW_REBOOT_PBX,
},
};
static void versatile_reboot(enum reboot_mode mode, const char *cmd)
{
/* Unlock the reset register */
regmap_write(syscon_regmap, REALVIEW_SYS_LOCK_OFFSET,
REALVIEW_SYS_LOCK_VAL);
/* Then hit reset on the different machines */
switch (versatile_reboot_type) {
case REALVIEW_REBOOT_EB:
regmap_write(syscon_regmap,
REALVIEW_SYS_RESETCTL_OFFSET, 0x0008);
break;
case REALVIEW_REBOOT_PB1176:
regmap_write(syscon_regmap,
REALVIEW_SYS_RESETCTL_OFFSET, 0x0100);
break;
case REALVIEW_REBOOT_PB11MP:
case REALVIEW_REBOOT_PBA8:
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
0x0000);
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
0x0004);
break;
case REALVIEW_REBOOT_PBX:
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
0x00f0);
regmap_write(syscon_regmap, REALVIEW_SYS_RESETCTL_OFFSET,
0x00f4);
break;
}
dsb();
}
static int __init versatile_reboot_probe(void)
{
const struct of_device_id *reboot_id;
struct device_node *np;
np = of_find_matching_node_and_match(NULL, versatile_reboot_of_match,
&reboot_id);
if (!np)
return -ENODEV;
versatile_reboot_type = (enum versatile_reboot)reboot_id->data;
syscon_regmap = syscon_node_to_regmap(np);
if (IS_ERR(syscon_regmap))
return PTR_ERR(syscon_regmap);
arm_pm_restart = versatile_reboot;
pr_info("versatile reboot driver registered\n");
return 0;
}
device_initcall(versatile_reboot_probe);

View file

@ -0,0 +1,96 @@
/*
* Power off driver for ams AS3722 device.
*
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
*
* Author: Laxman Dewangan <ldewangan@nvidia.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*/
#include <linux/mfd/as3722.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
struct as3722_poweroff {
struct device *dev;
struct as3722 *as3722;
};
static struct as3722_poweroff *as3722_pm_poweroff;
static void as3722_pm_power_off(void)
{
int ret;
if (!as3722_pm_poweroff) {
pr_err("AS3722 poweroff is not initialised\n");
return;
}
ret = as3722_update_bits(as3722_pm_poweroff->as3722,
AS3722_RESET_CONTROL_REG, AS3722_POWER_OFF, AS3722_POWER_OFF);
if (ret < 0)
dev_err(as3722_pm_poweroff->dev,
"RESET_CONTROL_REG update failed, %d\n", ret);
}
static int as3722_poweroff_probe(struct platform_device *pdev)
{
struct as3722_poweroff *as3722_poweroff;
struct device_node *np = pdev->dev.parent->of_node;
if (!np)
return -EINVAL;
if (!of_property_read_bool(np, "ams,system-power-controller"))
return 0;
as3722_poweroff = devm_kzalloc(&pdev->dev, sizeof(*as3722_poweroff),
GFP_KERNEL);
if (!as3722_poweroff)
return -ENOMEM;
as3722_poweroff->as3722 = dev_get_drvdata(pdev->dev.parent);
as3722_poweroff->dev = &pdev->dev;
as3722_pm_poweroff = as3722_poweroff;
if (!pm_power_off)
pm_power_off = as3722_pm_power_off;
return 0;
}
static int as3722_poweroff_remove(struct platform_device *pdev)
{
if (pm_power_off == as3722_pm_power_off)
pm_power_off = NULL;
as3722_pm_poweroff = NULL;
return 0;
}
static struct platform_driver as3722_poweroff_driver = {
.driver = {
.name = "as3722-power-off",
.owner = THIS_MODULE,
},
.probe = as3722_poweroff_probe,
.remove = as3722_poweroff_remove,
};
module_platform_driver(as3722_poweroff_driver);
MODULE_DESCRIPTION("Power off driver for ams AS3722 PMIC Device");
MODULE_ALIAS("platform:as3722-power-off");
MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,156 @@
/*
* Atmel AT91 SAM9 SoCs reset code
*
* Copyright (C) 2007 Atmel Corporation.
* Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
* Copyright (C) 2014 Free Electrons
*
* 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/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#define AT91_SHDW_CR 0x00 /* Shut Down Control Register */
#define AT91_SHDW_SHDW BIT(0) /* Shut Down command */
#define AT91_SHDW_KEY (0xa5 << 24) /* KEY Password */
#define AT91_SHDW_MR 0x04 /* Shut Down Mode Register */
#define AT91_SHDW_WKMODE0 GENMASK(2, 0) /* Wake-up 0 Mode Selection */
#define AT91_SHDW_CPTWK0_MAX 0xf /* Maximum Counter On Wake Up 0 */
#define AT91_SHDW_CPTWK0 (AT91_SHDW_CPTWK0_MAX << 4) /* Counter On Wake Up 0 */
#define AT91_SHDW_CPTWK0_(x) ((x) << 4)
#define AT91_SHDW_RTTWKEN BIT(16) /* Real Time Timer Wake-up Enable */
#define AT91_SHDW_RTCWKEN BIT(17) /* Real Time Clock Wake-up Enable */
#define AT91_SHDW_SR 0x08 /* Shut Down Status Register */
#define AT91_SHDW_WAKEUP0 BIT(0) /* Wake-up 0 Status */
#define AT91_SHDW_RTTWK BIT(16) /* Real-time Timer Wake-up */
#define AT91_SHDW_RTCWK BIT(17) /* Real-time Clock Wake-up [SAM9RL] */
enum wakeup_type {
AT91_SHDW_WKMODE0_NONE = 0,
AT91_SHDW_WKMODE0_HIGH = 1,
AT91_SHDW_WKMODE0_LOW = 2,
AT91_SHDW_WKMODE0_ANYLEVEL = 3,
};
static const char *shdwc_wakeup_modes[] = {
[AT91_SHDW_WKMODE0_NONE] = "none",
[AT91_SHDW_WKMODE0_HIGH] = "high",
[AT91_SHDW_WKMODE0_LOW] = "low",
[AT91_SHDW_WKMODE0_ANYLEVEL] = "any",
};
static void __iomem *at91_shdwc_base;
static void __init at91_wakeup_status(void)
{
u32 reg = readl(at91_shdwc_base + AT91_SHDW_SR);
char *reason = "unknown";
/* Simple power-on, just bail out */
if (!reg)
return;
if (reg & AT91_SHDW_RTTWK)
reason = "RTT";
else if (reg & AT91_SHDW_RTCWK)
reason = "RTC";
pr_info("AT91: Wake-Up source: %s\n", reason);
}
static void at91_poweroff(void)
{
writel(AT91_SHDW_KEY | AT91_SHDW_SHDW, at91_shdwc_base + AT91_SHDW_CR);
}
const enum wakeup_type at91_poweroff_get_wakeup_mode(struct device_node *np)
{
const char *pm;
int err, i;
err = of_property_read_string(np, "atmel,wakeup-mode", &pm);
if (err < 0)
return AT91_SHDW_WKMODE0_ANYLEVEL;
for (i = 0; i < ARRAY_SIZE(shdwc_wakeup_modes); i++)
if (!strcasecmp(pm, shdwc_wakeup_modes[i]))
return i;
return -ENODEV;
}
static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
enum wakeup_type wakeup_mode;
u32 mode = 0, tmp;
wakeup_mode = at91_poweroff_get_wakeup_mode(np);
if (wakeup_mode < 0) {
dev_warn(&pdev->dev, "shdwc unknown wakeup mode\n");
return;
}
if (!of_property_read_u32(np, "atmel,wakeup-counter", &tmp)) {
if (tmp > AT91_SHDW_CPTWK0_MAX) {
dev_warn(&pdev->dev,
"shdwc wakeup counter 0x%x > 0x%x reduce it to 0x%x\n",
tmp, AT91_SHDW_CPTWK0_MAX, AT91_SHDW_CPTWK0_MAX);
tmp = AT91_SHDW_CPTWK0_MAX;
}
mode |= AT91_SHDW_CPTWK0_(tmp);
}
if (of_property_read_bool(np, "atmel,wakeup-rtc-timer"))
mode |= AT91_SHDW_RTCWKEN;
if (of_property_read_bool(np, "atmel,wakeup-rtt-timer"))
mode |= AT91_SHDW_RTTWKEN;
writel(wakeup_mode | mode, at91_shdwc_base + AT91_SHDW_MR);
}
static int at91_poweroff_probe(struct platform_device *pdev)
{
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(at91_shdwc_base)) {
dev_err(&pdev->dev, "Could not map reset controller address\n");
return PTR_ERR(at91_shdwc_base);
}
at91_wakeup_status();
if (pdev->dev.of_node)
at91_poweroff_dt_set_wakeup_mode(pdev);
pm_power_off = at91_poweroff;
return 0;
}
static struct of_device_id at91_poweroff_of_match[] = {
{ .compatible = "atmel,at91sam9260-shdwc", },
{ .compatible = "atmel,at91sam9rl-shdwc", },
{ .compatible = "atmel,at91sam9x5-shdwc", },
{ /*sentinel*/ }
};
static struct platform_driver at91_poweroff_driver = {
.probe = at91_poweroff_probe,
.driver = {
.name = "at91-poweroff",
.of_match_table = at91_poweroff_of_match,
},
};
module_platform_driver(at91_poweroff_driver);

View file

@ -0,0 +1,252 @@
/*
* Atmel AT91 SAM9 SoCs reset code
*
* Copyright (C) 2007 Atmel Corporation.
* Copyright (C) BitBox Ltd 2010
* Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcosoft.com>
* Copyright (C) 2014 Free Electrons
*
* 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/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>
#include <mach/at91sam9_ddrsdr.h>
#include <mach/at91sam9_sdramc.h>
#define AT91_RSTC_CR 0x00 /* Reset Controller Control Register */
#define AT91_RSTC_PROCRST BIT(0) /* Processor Reset */
#define AT91_RSTC_PERRST BIT(2) /* Peripheral Reset */
#define AT91_RSTC_EXTRST BIT(3) /* External Reset */
#define AT91_RSTC_KEY (0xa5 << 24) /* KEY Password */
#define AT91_RSTC_SR 0x04 /* Reset Controller Status Register */
#define AT91_RSTC_URSTS BIT(0) /* User Reset Status */
#define AT91_RSTC_RSTTYP GENMASK(10, 8) /* Reset Type */
#define AT91_RSTC_NRSTL BIT(16) /* NRST Pin Level */
#define AT91_RSTC_SRCMP BIT(17) /* Software Reset Command in Progress */
#define AT91_RSTC_MR 0x08 /* Reset Controller Mode Register */
#define AT91_RSTC_URSTEN BIT(0) /* User Reset Enable */
#define AT91_RSTC_URSTIEN BIT(4) /* User Reset Interrupt Enable */
#define AT91_RSTC_ERSTL GENMASK(11, 8) /* External Reset Length */
enum reset_type {
RESET_TYPE_GENERAL = 0,
RESET_TYPE_WAKEUP = 1,
RESET_TYPE_WATCHDOG = 2,
RESET_TYPE_SOFTWARE = 3,
RESET_TYPE_USER = 4,
};
static void __iomem *at91_ramc_base[2], *at91_rstc_base;
/*
* unless the SDRAM is cleanly shutdown before we hit the
* reset register it can be left driving the data bus and
* killing the chance of a subsequent boot from NAND
*/
static void at91sam9260_restart(enum reboot_mode mode, const char *cmd)
{
asm volatile(
/* Align to cache lines */
".balign 32\n\t"
/* Disable SDRAM accesses */
"str %2, [%0, #" __stringify(AT91_SDRAMC_TR) "]\n\t"
/* Power down SDRAM */
"str %3, [%0, #" __stringify(AT91_SDRAMC_LPR) "]\n\t"
/* Reset CPU */
"str %4, [%1, #" __stringify(AT91_RSTC_CR) "]\n\t"
"b .\n\t"
:
: "r" (at91_ramc_base[0]),
"r" (at91_rstc_base),
"r" (1),
"r" (AT91_SDRAMC_LPCB_POWER_DOWN),
"r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST));
}
static void at91sam9g45_restart(enum reboot_mode mode, const char *cmd)
{
asm volatile(
/*
* Test wether we have a second RAM controller to care
* about.
*
* First, test that we can dereference the virtual address.
*/
"cmp %1, #0\n\t"
"beq 1f\n\t"
/* Then, test that the RAM controller is enabled */
"ldr r0, [%1]\n\t"
"cmp r0, #0\n\t"
/* Align to cache lines */
".balign 32\n\t"
/* Disable SDRAM0 accesses */
"1: str %3, [%0, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
/* Power down SDRAM0 */
" str %4, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
/* Disable SDRAM1 accesses */
" strne %3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t"
/* Power down SDRAM1 */
" strne %4, [%1, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
/* Reset CPU */
" str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t"
" b .\n\t"
:
: "r" (at91_ramc_base[0]),
"r" (at91_ramc_base[1]),
"r" (at91_rstc_base),
"r" (1),
"r" (AT91_DDRSDRC_LPCB_POWER_DOWN),
"r" (AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST)
: "r0");
}
static void __init at91_reset_status(struct platform_device *pdev)
{
u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
char *reason;
switch ((reg & AT91_RSTC_RSTTYP) >> 8) {
case RESET_TYPE_GENERAL:
reason = "general reset";
break;
case RESET_TYPE_WAKEUP:
reason = "wakeup";
break;
case RESET_TYPE_WATCHDOG:
reason = "watchdog reset";
break;
case RESET_TYPE_SOFTWARE:
reason = "software reset";
break;
case RESET_TYPE_USER:
reason = "user reset";
break;
default:
reason = "unknown reset";
break;
}
pr_info("AT91: Starting after %s\n", reason);
}
static struct of_device_id at91_ramc_of_match[] = {
{ .compatible = "atmel,at91sam9260-sdramc", },
{ .compatible = "atmel,at91sam9g45-ddramc", },
{ .compatible = "atmel,sama5d3-ddramc", },
{ /* sentinel */ }
};
static struct of_device_id at91_reset_of_match[] = {
{ .compatible = "atmel,at91sam9260-rstc", .data = at91sam9260_restart },
{ .compatible = "atmel,at91sam9g45-rstc", .data = at91sam9g45_restart },
{ /* sentinel */ }
};
static int at91_reset_of_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
struct device_node *np;
int idx = 0;
at91_rstc_base = of_iomap(pdev->dev.of_node, 0);
if (!at91_rstc_base) {
dev_err(&pdev->dev, "Could not map reset controller address\n");
return -ENODEV;
}
for_each_matching_node(np, at91_ramc_of_match) {
at91_ramc_base[idx] = of_iomap(np, 0);
if (!at91_ramc_base[idx]) {
dev_err(&pdev->dev, "Could not map ram controller address\n");
return -ENODEV;
}
idx++;
}
match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
arm_pm_restart = match->data;
return 0;
}
static int at91_reset_platform_probe(struct platform_device *pdev)
{
const struct platform_device_id *match;
struct resource *res;
int idx = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
at91_rstc_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(at91_rstc_base)) {
dev_err(&pdev->dev, "Could not map reset controller address\n");
return PTR_ERR(at91_rstc_base);
}
for (idx = 0; idx < 2; idx++) {
res = platform_get_resource(pdev, IORESOURCE_MEM, idx + 1 );
at91_ramc_base[idx] = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (IS_ERR(at91_ramc_base[idx])) {
dev_err(&pdev->dev, "Could not map ram controller address\n");
return PTR_ERR(at91_ramc_base[idx]);
}
}
match = platform_get_device_id(pdev);
arm_pm_restart = (void (*)(enum reboot_mode, const char*))
match->driver_data;
return 0;
}
static int at91_reset_probe(struct platform_device *pdev)
{
int ret;
if (pdev->dev.of_node)
ret = at91_reset_of_probe(pdev);
else
ret = at91_reset_platform_probe(pdev);
if (ret)
return ret;
at91_reset_status(pdev);
return 0;
}
static struct platform_device_id at91_reset_plat_match[] = {
{ "at91-sam9260-reset", (unsigned long)at91sam9260_restart },
{ "at91-sam9g45-reset", (unsigned long)at91sam9g45_restart },
{ /* sentinel */ }
};
static struct platform_driver at91_reset_driver = {
.probe = at91_reset_probe,
.driver = {
.name = "at91-reset",
.of_match_table = at91_reset_of_match,
},
.id_table = at91_reset_plat_match,
};
module_platform_driver(at91_reset_driver);

View file

@ -0,0 +1,88 @@
/*
* Reset driver for Axxia devices
*
* Copyright (C) 2014 LSI
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/init.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <asm/system_misc.h>
#define SC_CRIT_WRITE_KEY 0x1000
#define SC_LATCH_ON_RESET 0x1004
#define SC_RESET_CONTROL 0x1008
#define RSTCTL_RST_ZERO (1<<3)
#define RSTCTL_RST_FAB (1<<2)
#define RSTCTL_RST_CHIP (1<<1)
#define RSTCTL_RST_SYS (1<<0)
#define SC_EFUSE_INT_STATUS 0x180c
#define EFUSE_READ_DONE (1<<31)
static struct regmap *syscon;
static void do_axxia_restart(enum reboot_mode reboot_mode, const char *cmd)
{
/* Access Key (0xab) */
regmap_write(syscon, SC_CRIT_WRITE_KEY, 0xab);
/* Select internal boot from 0xffff0000 */
regmap_write(syscon, SC_LATCH_ON_RESET, 0x00000040);
/* Assert ResetReadDone (to avoid hanging in boot ROM) */
regmap_write(syscon, SC_EFUSE_INT_STATUS, EFUSE_READ_DONE);
/* Assert chip reset */
regmap_update_bits(syscon, SC_RESET_CONTROL,
RSTCTL_RST_CHIP, RSTCTL_RST_CHIP);
}
static int axxia_reset_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon");
if (IS_ERR(syscon)) {
pr_err("%s: syscon lookup failed\n", dev->of_node->name);
return PTR_ERR(syscon);
}
arm_pm_restart = do_axxia_restart;
return 0;
}
static const struct of_device_id of_axxia_reset_match[] = {
{ .compatible = "lsi,axm55xx-reset", },
{},
};
MODULE_DEVICE_TABLE(of, of_axxia_reset_match);
static struct platform_driver axxia_reset_driver = {
.probe = axxia_reset_probe,
.driver = {
.name = "axxia-reset",
.of_match_table = of_match_ptr(of_axxia_reset_match),
},
};
static int __init axxia_reset_init(void)
{
return platform_driver_register(&axxia_reset_driver);
}
device_initcall(axxia_reset_init);

View file

@ -0,0 +1,120 @@
/*
* Copyright (C) 2013 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation version 2.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/smp.h>
#include <linux/mfd/syscon.h>
#include <asm/system_misc.h>
#define RESET_SOURCE_ENABLE_REG 1
#define SW_MASTER_RESET_REG 2
static struct regmap *regmap;
static u32 rst_src_en;
static u32 sw_mstr_rst;
static void brcmstb_reboot(enum reboot_mode mode, const char *cmd)
{
int rc;
u32 tmp;
rc = regmap_write(regmap, rst_src_en, 1);
if (rc) {
pr_err("failed to write rst_src_en (%d)\n", rc);
return;
}
rc = regmap_read(regmap, rst_src_en, &tmp);
if (rc) {
pr_err("failed to read rst_src_en (%d)\n", rc);
return;
}
rc = regmap_write(regmap, sw_mstr_rst, 1);
if (rc) {
pr_err("failed to write sw_mstr_rst (%d)\n", rc);
return;
}
rc = regmap_read(regmap, sw_mstr_rst, &tmp);
if (rc) {
pr_err("failed to read sw_mstr_rst (%d)\n", rc);
return;
}
while (1)
;
}
static int brcmstb_reboot_probe(struct platform_device *pdev)
{
int rc;
struct device_node *np = pdev->dev.of_node;
regmap = syscon_regmap_lookup_by_phandle(np, "syscon");
if (IS_ERR(regmap)) {
pr_err("failed to get syscon phandle\n");
return -EINVAL;
}
rc = of_property_read_u32_index(np, "syscon", RESET_SOURCE_ENABLE_REG,
&rst_src_en);
if (rc) {
pr_err("can't get rst_src_en offset (%d)\n", rc);
return -EINVAL;
}
rc = of_property_read_u32_index(np, "syscon", SW_MASTER_RESET_REG,
&sw_mstr_rst);
if (rc) {
pr_err("can't get sw_mstr_rst offset (%d)\n", rc);
return -EINVAL;
}
arm_pm_restart = brcmstb_reboot;
return 0;
}
static const struct of_device_id of_match[] = {
{ .compatible = "brcm,brcmstb-reboot", },
{},
};
static struct platform_driver brcmstb_reboot_driver = {
.probe = brcmstb_reboot_probe,
.driver = {
.name = "brcmstb-reboot",
.owner = THIS_MODULE,
.of_match_table = of_match,
},
};
static int __init brcmstb_reboot_init(void)
{
return platform_driver_probe(&brcmstb_reboot_driver,
brcmstb_reboot_probe);
}
subsys_initcall(brcmstb_reboot_init);

View file

@ -0,0 +1,112 @@
/*
* Toggles a GPIO pin to power down a device
*
* Jamie Lentin <jm@lentin.co.uk>
* Andrew Lunn <andrew@lunn.ch>
*
* Copyright (C) 2012 Jamie Lentin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/of_platform.h>
#include <linux/module.h>
/*
* Hold configuration here, cannot be more than one instance of the driver
* since pm_power_off itself is global.
*/
static struct gpio_desc *reset_gpio;
static void gpio_poweroff_do_poweroff(void)
{
BUG_ON(!reset_gpio);
/* drive it active, also inactive->active edge */
gpiod_direction_output(reset_gpio, 1);
mdelay(100);
/* drive inactive, also active->inactive edge */
gpiod_set_value(reset_gpio, 0);
mdelay(100);
/* drive it active, also inactive->active edge */
gpiod_set_value(reset_gpio, 1);
/* give it some time */
mdelay(3000);
WARN_ON(1);
}
static int gpio_poweroff_probe(struct platform_device *pdev)
{
bool input = false;
/* If a pm_power_off function has already been added, leave it alone */
if (pm_power_off != NULL) {
dev_err(&pdev->dev,
"%s: pm_power_off function already registered",
__func__);
return -EBUSY;
}
reset_gpio = devm_gpiod_get(&pdev->dev, NULL);
if (IS_ERR(reset_gpio))
return PTR_ERR(reset_gpio);
input = of_property_read_bool(pdev->dev.of_node, "input");
if (input) {
if (gpiod_direction_input(reset_gpio)) {
dev_err(&pdev->dev,
"Could not set direction of reset GPIO to input\n");
return -ENODEV;
}
} else {
if (gpiod_direction_output(reset_gpio, 0)) {
dev_err(&pdev->dev,
"Could not set direction of reset GPIO\n");
return -ENODEV;
}
}
pm_power_off = &gpio_poweroff_do_poweroff;
return 0;
}
static int gpio_poweroff_remove(struct platform_device *pdev)
{
if (pm_power_off == &gpio_poweroff_do_poweroff)
pm_power_off = NULL;
return 0;
}
static const struct of_device_id of_gpio_poweroff_match[] = {
{ .compatible = "gpio-poweroff", },
{},
};
static struct platform_driver gpio_poweroff_driver = {
.probe = gpio_poweroff_probe,
.remove = gpio_poweroff_remove,
.driver = {
.name = "poweroff-gpio",
.owner = THIS_MODULE,
.of_match_table = of_gpio_poweroff_match,
},
};
module_platform_driver(gpio_poweroff_driver);
MODULE_AUTHOR("Jamie Lentin <jm@lentin.co.uk>");
MODULE_DESCRIPTION("GPIO poweroff driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:poweroff-gpio");

View file

@ -0,0 +1,149 @@
/*
* Toggles a GPIO pin to restart a device
*
* Copyright (C) 2014 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Based on the gpio-poweroff driver.
*/
#include <linux/reboot.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/gpio/consumer.h>
#include <linux/of_platform.h>
#include <linux/module.h>
struct gpio_restart {
struct gpio_desc *reset_gpio;
struct notifier_block restart_handler;
u32 active_delay_ms;
u32 inactive_delay_ms;
u32 wait_delay_ms;
};
static int gpio_restart_notify(struct notifier_block *this,
unsigned long mode, void *cmd)
{
struct gpio_restart *gpio_restart =
container_of(this, struct gpio_restart, restart_handler);
/* drive it active, also inactive->active edge */
gpiod_direction_output(gpio_restart->reset_gpio, 1);
mdelay(gpio_restart->active_delay_ms);
/* drive inactive, also active->inactive edge */
gpiod_set_value(gpio_restart->reset_gpio, 0);
mdelay(gpio_restart->inactive_delay_ms);
/* drive it active, also inactive->active edge */
gpiod_set_value(gpio_restart->reset_gpio, 1);
/* give it some time */
mdelay(gpio_restart->wait_delay_ms);
WARN_ON(1);
return NOTIFY_DONE;
}
static int gpio_restart_probe(struct platform_device *pdev)
{
struct gpio_restart *gpio_restart;
bool open_source = false;
u32 property;
int ret;
gpio_restart = devm_kzalloc(&pdev->dev, sizeof(*gpio_restart),
GFP_KERNEL);
if (!gpio_restart)
return -ENOMEM;
open_source = of_property_read_bool(pdev->dev.of_node, "open-source");
gpio_restart->reset_gpio = devm_gpiod_get(&pdev->dev, NULL,
open_source ? GPIOD_IN : GPIOD_OUT_LOW);
if (IS_ERR(gpio_restart->reset_gpio)) {
dev_err(&pdev->dev, "Could net get reset GPIO\n");
return PTR_ERR(gpio_restart->reset_gpio);
}
gpio_restart->restart_handler.notifier_call = gpio_restart_notify;
gpio_restart->restart_handler.priority = 128;
gpio_restart->active_delay_ms = 100;
gpio_restart->inactive_delay_ms = 100;
gpio_restart->wait_delay_ms = 3000;
ret = of_property_read_u32(pdev->dev.of_node, "priority", &property);
if (!ret) {
if (property > 255)
dev_err(&pdev->dev, "Invalid priority property: %u\n",
property);
else
gpio_restart->restart_handler.priority = property;
}
of_property_read_u32(pdev->dev.of_node, "active-delay",
&gpio_restart->active_delay_ms);
of_property_read_u32(pdev->dev.of_node, "inactive-delay",
&gpio_restart->inactive_delay_ms);
of_property_read_u32(pdev->dev.of_node, "wait-delay",
&gpio_restart->wait_delay_ms);
platform_set_drvdata(pdev, gpio_restart);
ret = register_restart_handler(&gpio_restart->restart_handler);
if (ret) {
dev_err(&pdev->dev, "%s: cannot register restart handler, %d\n",
__func__, ret);
return -ENODEV;
}
return 0;
}
static int gpio_restart_remove(struct platform_device *pdev)
{
struct gpio_restart *gpio_restart = platform_get_drvdata(pdev);
int ret;
ret = unregister_restart_handler(&gpio_restart->restart_handler);
if (ret) {
dev_err(&pdev->dev,
"%s: cannot unregister restart handler, %d\n",
__func__, ret);
return -ENODEV;
}
return 0;
}
static const struct of_device_id of_gpio_restart_match[] = {
{ .compatible = "gpio-restart", },
{},
};
static struct platform_driver gpio_restart_driver = {
.probe = gpio_restart_probe,
.remove = gpio_restart_remove,
.driver = {
.name = "restart-gpio",
.owner = THIS_MODULE,
.of_match_table = of_gpio_restart_match,
},
};
module_platform_driver(gpio_restart_driver);
MODULE_AUTHOR("David Riley <davidriley@chromium.org>");
MODULE_DESCRIPTION("GPIO restart driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,67 @@
/*
* Hisilicon SoC reset code
*
* Copyright (c) 2014 Hisilicon Ltd.
* Copyright (c) 2014 Linaro Ltd.
*
* Author: Haojian Zhuang <haojian.zhuang@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#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/proc-fns.h>
#include <asm/system_misc.h>
static void __iomem *base;
static u32 reboot_offset;
static void hisi_restart(enum reboot_mode mode, const char *cmd)
{
writel_relaxed(0xdeadbeef, base + reboot_offset);
while (1)
cpu_do_idle();
}
static int hisi_reboot_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
base = of_iomap(np, 0);
if (!base) {
WARN(1, "failed to map base address");
return -ENODEV;
}
if (of_property_read_u32(np, "reboot-offset", &reboot_offset) < 0) {
pr_err("failed to find reboot-offset property\n");
return -EINVAL;
}
arm_pm_restart = hisi_restart;
return 0;
}
static struct of_device_id hisi_reboot_of_match[] = {
{ .compatible = "hisilicon,sysctrl" },
{}
};
static struct platform_driver hisi_reboot_driver = {
.probe = hisi_reboot_probe,
.driver = {
.name = "hisi-reboot",
.of_match_table = hisi_reboot_of_match,
},
};
module_platform_driver(hisi_reboot_driver);

View file

@ -0,0 +1,166 @@
/*
* TI keystone reboot driver
*
* Copyright (C) 2014 Texas Instruments Incorporated. http://www.ti.com/
*
* Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <asm/system_misc.h>
#include <linux/mfd/syscon.h>
#include <linux/of_platform.h>
#define RSTYPE_RG 0x0
#define RSCTRL_RG 0x4
#define RSCFG_RG 0x8
#define RSISO_RG 0xc
#define RSCTRL_KEY_MASK 0x0000ffff
#define RSCTRL_RESET_MASK BIT(16)
#define RSCTRL_KEY 0x5a69
#define RSMUX_OMODE_MASK 0xe
#define RSMUX_OMODE_RESET_ON 0xa
#define RSMUX_OMODE_RESET_OFF 0x0
#define RSMUX_LOCK_MASK 0x1
#define RSMUX_LOCK_SET 0x1
#define RSCFG_RSTYPE_SOFT 0x300f
#define RSCFG_RSTYPE_HARD 0x0
#define WDT_MUX_NUMBER 0x4
static int rspll_offset;
static struct regmap *pllctrl_regs;
/**
* rsctrl_enable_rspll_write - enable access to RSCTRL, RSCFG
* To be able to access to RSCTRL, RSCFG registers
* we have to write a key before
*/
static inline int rsctrl_enable_rspll_write(void)
{
return regmap_update_bits(pllctrl_regs, rspll_offset + RSCTRL_RG,
RSCTRL_KEY_MASK, RSCTRL_KEY);
}
static void rsctrl_restart(enum reboot_mode mode, const char *cmd)
{
/* enable write access to RSTCTRL */
rsctrl_enable_rspll_write();
/* reset the SOC */
regmap_update_bits(pllctrl_regs, rspll_offset + RSCTRL_RG,
RSCTRL_RESET_MASK, 0);
}
static struct of_device_id rsctrl_of_match[] = {
{.compatible = "ti,keystone-reset", },
{},
};
static int rsctrl_probe(struct platform_device *pdev)
{
int i;
int ret;
u32 val;
unsigned int rg;
u32 rsmux_offset;
struct regmap *devctrl_regs;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
if (!np)
return -ENODEV;
/* get regmaps */
pllctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-pll");
if (IS_ERR(pllctrl_regs))
return PTR_ERR(pllctrl_regs);
devctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev");
if (IS_ERR(devctrl_regs))
return PTR_ERR(devctrl_regs);
ret = of_property_read_u32_index(np, "ti,syscon-pll", 1, &rspll_offset);
if (ret) {
dev_err(dev, "couldn't read the reset pll offset!\n");
return -EINVAL;
}
ret = of_property_read_u32_index(np, "ti,syscon-dev", 1, &rsmux_offset);
if (ret) {
dev_err(dev, "couldn't read the rsmux offset!\n");
return -EINVAL;
}
/* set soft/hard reset */
val = of_property_read_bool(np, "ti,soft-reset");
val = val ? RSCFG_RSTYPE_SOFT : RSCFG_RSTYPE_HARD;
ret = rsctrl_enable_rspll_write();
if (ret)
return ret;
ret = regmap_write(pllctrl_regs, rspll_offset + RSCFG_RG, val);
if (ret)
return ret;
arm_pm_restart = rsctrl_restart;
/* disable a reset isolation for all module clocks */
ret = regmap_write(pllctrl_regs, rspll_offset + RSISO_RG, 0);
if (ret)
return ret;
/* enable a reset for watchdogs from wdt-list */
for (i = 0; i < WDT_MUX_NUMBER; i++) {
ret = of_property_read_u32_index(np, "ti,wdt-list", i, &val);
if (ret == -EOVERFLOW && !i) {
dev_err(dev, "ti,wdt-list property has to contain at"
"least one entry\n");
return -EINVAL;
} else if (ret) {
break;
}
if (val >= WDT_MUX_NUMBER) {
dev_err(dev, "ti,wdt-list property can contain"
"only numbers < 4\n");
return -EINVAL;
}
rg = rsmux_offset + val * 4;
ret = regmap_update_bits(devctrl_regs, rg, RSMUX_OMODE_MASK,
RSMUX_OMODE_RESET_ON |
RSMUX_LOCK_SET);
if (ret)
return ret;
}
return 0;
}
static struct platform_driver rsctrl_driver = {
.probe = rsctrl_probe,
.driver = {
.owner = THIS_MODULE,
.name = KBUILD_MODNAME,
.of_match_table = rsctrl_of_match,
},
};
module_platform_driver(rsctrl_driver);
MODULE_AUTHOR("Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>");
MODULE_DESCRIPTION("Texas Instruments keystone reset driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" KBUILD_MODNAME);

View file

@ -0,0 +1,386 @@
/*
* LTC2952 (PowerPath) driver
*
* Copyright (C) 2014, Xsens Technologies BV <info@xsens.com>
* Maintainer: René Moll <linux@r-moll.nl>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* ----------------------------------------
* - Description
* ----------------------------------------
*
* This driver is to be used with an external PowerPath Controller (LTC2952).
* Its function is to determine when a external shut down is triggered
* and react by properly shutting down the system.
*
* This driver expects a device tree with a ltc2952 entry for pin mapping.
*
* ----------------------------------------
* - GPIO
* ----------------------------------------
*
* The following GPIOs are used:
* - trigger (input)
* A level change indicates the shut-down trigger. If it's state reverts
* within the time-out defined by trigger_delay, the shut down is not
* executed.
*
* - watchdog (output)
* Once a shut down is triggered, the driver will toggle this signal,
* with an internal (wde_interval) to stall the hardware shut down.
*
* - kill (output)
* The last action during shut down is triggering this signalling, such
* that the PowerPath Control will power down the hardware.
*
* ----------------------------------------
* - Interrupts
* ----------------------------------------
*
* The driver requires a non-shared, edge-triggered interrupt on the trigger
* GPIO.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/ktime.h>
#include <linux/slab.h>
#include <linux/kmod.h>
#include <linux/module.h>
#include <linux/gpio/consumer.h>
#include <linux/reboot.h>
struct ltc2952_poweroff_data {
struct hrtimer timer_trigger;
struct hrtimer timer_wde;
ktime_t trigger_delay;
ktime_t wde_interval;
struct device *dev;
unsigned int virq;
/**
* 0: trigger
* 1: watchdog
* 2: kill
*/
struct gpio_desc *gpio[3];
};
static int ltc2952_poweroff_panic;
static struct ltc2952_poweroff_data *ltc2952_data;
#define POWERPATH_IO_TRIGGER 0
#define POWERPATH_IO_WATCHDOG 1
#define POWERPATH_IO_KILL 2
/**
* ltc2952_poweroff_timer_wde - Timer callback
* Toggles the watchdog reset signal each wde_interval
*
* @timer: corresponding timer
*
* Returns HRTIMER_RESTART for an infinite loop which will only stop when the
* machine actually shuts down
*/
static enum hrtimer_restart ltc2952_poweroff_timer_wde(struct hrtimer *timer)
{
ktime_t now;
int state;
unsigned long overruns;
if (ltc2952_poweroff_panic)
return HRTIMER_NORESTART;
state = gpiod_get_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG]);
gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], !state);
now = hrtimer_cb_get_time(timer);
overruns = hrtimer_forward(timer, now, ltc2952_data->wde_interval);
return HRTIMER_RESTART;
}
static enum hrtimer_restart ltc2952_poweroff_timer_trigger(
struct hrtimer *timer)
{
int ret;
ret = hrtimer_start(&ltc2952_data->timer_wde,
ltc2952_data->wde_interval, HRTIMER_MODE_REL);
if (ret) {
dev_err(ltc2952_data->dev, "unable to start the timer\n");
/*
* The device will not toggle the watchdog reset,
* thus shut down is only safe if the PowerPath controller
* has a long enough time-off before triggering a hardware
* power-off.
*
* Only sending a warning as the system will power-off anyway
*/
}
dev_info(ltc2952_data->dev, "executing shutdown\n");
orderly_poweroff(true);
return HRTIMER_NORESTART;
}
/**
* ltc2952_poweroff_handler - Interrupt handler
* Triggered each time the trigger signal changes state and (de)activates a
* time-out (timer_trigger). Once the time-out is actually reached the shut
* down is executed.
*
* @irq: IRQ number
* @dev_id: pointer to the main data structure
*/
static irqreturn_t ltc2952_poweroff_handler(int irq, void *dev_id)
{
int ret;
struct ltc2952_poweroff_data *data = dev_id;
if (ltc2952_poweroff_panic)
goto irq_ok;
if (hrtimer_active(&data->timer_wde)) {
/* shutdown is already triggered, nothing to do any more */
goto irq_ok;
}
if (!hrtimer_active(&data->timer_trigger)) {
ret = hrtimer_start(&data->timer_trigger, data->trigger_delay,
HRTIMER_MODE_REL);
if (ret)
dev_err(data->dev, "unable to start the wait timer\n");
} else {
ret = hrtimer_cancel(&data->timer_trigger);
/* omitting return value check, timer should have been valid */
}
irq_ok:
return IRQ_HANDLED;
}
static void ltc2952_poweroff_kill(void)
{
gpiod_set_value(ltc2952_data->gpio[POWERPATH_IO_KILL], 1);
}
static int ltc2952_poweroff_suspend(struct platform_device *pdev,
pm_message_t state)
{
return -ENOSYS;
}
static int ltc2952_poweroff_resume(struct platform_device *pdev)
{
return -ENOSYS;
}
static void ltc2952_poweroff_default(struct ltc2952_poweroff_data *data)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(data->gpio); i++)
data->gpio[i] = NULL;
data->wde_interval = ktime_set(0, 300L*1E6L);
data->trigger_delay = ktime_set(2, 500L*1E6L);
hrtimer_init(&data->timer_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
data->timer_trigger.function = &ltc2952_poweroff_timer_trigger;
hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
data->timer_wde.function = &ltc2952_poweroff_timer_wde;
}
static int ltc2952_poweroff_init(struct platform_device *pdev)
{
int ret, virq;
unsigned int i;
struct ltc2952_poweroff_data *data;
static char *name[] = {
"trigger",
"watchdog",
"kill",
NULL
};
data = ltc2952_data;
ltc2952_poweroff_default(ltc2952_data);
for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++) {
ltc2952_data->gpio[i] = gpiod_get(&pdev->dev, name[i]);
if (IS_ERR(ltc2952_data->gpio[i])) {
ret = PTR_ERR(ltc2952_data->gpio[i]);
dev_err(&pdev->dev,
"unable to claim the following gpio: %s\n",
name[i]);
goto err_io;
}
}
ret = gpiod_direction_output(
ltc2952_data->gpio[POWERPATH_IO_WATCHDOG], 0);
if (ret) {
dev_err(&pdev->dev, "unable to use watchdog-gpio as output\n");
goto err_io;
}
ret = gpiod_direction_output(ltc2952_data->gpio[POWERPATH_IO_KILL], 0);
if (ret) {
dev_err(&pdev->dev, "unable to use kill-gpio as output\n");
goto err_io;
}
virq = gpiod_to_irq(ltc2952_data->gpio[POWERPATH_IO_TRIGGER]);
if (virq < 0) {
dev_err(&pdev->dev, "cannot map GPIO as interrupt");
goto err_io;
}
ltc2952_data->virq = virq;
ret = request_irq(virq,
ltc2952_poweroff_handler,
(IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING),
"ltc2952-poweroff",
ltc2952_data
);
if (ret) {
dev_err(&pdev->dev, "cannot configure an interrupt handler\n");
goto err_io;
}
return 0;
err_io:
for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++)
if (ltc2952_data->gpio[i])
gpiod_put(ltc2952_data->gpio[i]);
return ret;
}
static int ltc2952_poweroff_probe(struct platform_device *pdev)
{
int ret;
if (pm_power_off) {
dev_err(&pdev->dev, "pm_power_off already registered");
return -EBUSY;
}
ltc2952_data = kzalloc(sizeof(*ltc2952_data), GFP_KERNEL);
if (!ltc2952_data)
return -ENOMEM;
ltc2952_data->dev = &pdev->dev;
ret = ltc2952_poweroff_init(pdev);
if (ret)
goto err;
pm_power_off = &ltc2952_poweroff_kill;
dev_info(&pdev->dev, "probe successful\n");
return 0;
err:
kfree(ltc2952_data);
return ret;
}
static int ltc2952_poweroff_remove(struct platform_device *pdev)
{
unsigned int i;
pm_power_off = NULL;
if (ltc2952_data) {
free_irq(ltc2952_data->virq, ltc2952_data);
for (i = 0; i < ARRAY_SIZE(ltc2952_data->gpio); i++)
gpiod_put(ltc2952_data->gpio[i]);
kfree(ltc2952_data);
}
return 0;
}
static const struct of_device_id of_ltc2952_poweroff_match[] = {
{ .compatible = "lltc,ltc2952"},
{},
};
MODULE_DEVICE_TABLE(of, of_ltc2952_poweroff_match);
static struct platform_driver ltc2952_poweroff_driver = {
.probe = ltc2952_poweroff_probe,
.remove = ltc2952_poweroff_remove,
.driver = {
.name = "ltc2952-poweroff",
.owner = THIS_MODULE,
.of_match_table = of_ltc2952_poweroff_match,
},
.suspend = ltc2952_poweroff_suspend,
.resume = ltc2952_poweroff_resume,
};
static int ltc2952_poweroff_notify_panic(struct notifier_block *nb,
unsigned long code, void *unused)
{
ltc2952_poweroff_panic = 1;
return NOTIFY_DONE;
}
static struct notifier_block ltc2952_poweroff_panic_nb = {
.notifier_call = ltc2952_poweroff_notify_panic,
};
static int __init ltc2952_poweroff_platform_init(void)
{
ltc2952_poweroff_panic = 0;
atomic_notifier_chain_register(&panic_notifier_list,
&ltc2952_poweroff_panic_nb);
return platform_driver_register(&ltc2952_poweroff_driver);
}
static void __exit ltc2952_poweroff_platform_exit(void)
{
atomic_notifier_chain_unregister(&panic_notifier_list,
&ltc2952_poweroff_panic_nb);
platform_driver_unregister(&ltc2952_poweroff_driver);
}
module_init(ltc2952_poweroff_platform_init);
module_exit(ltc2952_poweroff_platform_exit);
MODULE_AUTHOR("René Moll <rene.moll@xsens.com>");
MODULE_DESCRIPTION("LTC PowerPath power-off driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,81 @@
/* Copyright (c) 2013, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/reboot.h>
#include <linux/pm.h>
static void __iomem *msm_ps_hold;
static int do_msm_restart(struct notifier_block *nb, unsigned long action,
void *data)
{
writel(0, msm_ps_hold);
mdelay(10000);
return NOTIFY_DONE;
}
static struct notifier_block restart_nb = {
.notifier_call = do_msm_restart,
.priority = 128,
};
static void do_msm_poweroff(void)
{
/* TODO: Add poweroff capability */
do_msm_restart(&restart_nb, 0, NULL);
}
static int msm_restart_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct resource *mem;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
msm_ps_hold = devm_ioremap_resource(dev, mem);
if (IS_ERR(msm_ps_hold))
return PTR_ERR(msm_ps_hold);
register_restart_handler(&restart_nb);
pm_power_off = do_msm_poweroff;
return 0;
}
static const struct of_device_id of_msm_restart_match[] = {
{ .compatible = "qcom,pshold", },
{},
};
MODULE_DEVICE_TABLE(of, of_msm_restart_match);
static struct platform_driver msm_restart_driver = {
.probe = msm_restart_probe,
.driver = {
.name = "msm-restart",
.of_match_table = of_match_ptr(of_msm_restart_match),
},
};
static int __init msm_restart_init(void)
{
return platform_driver_register(&msm_restart_driver);
}
device_initcall(msm_restart_init);

View file

@ -0,0 +1,141 @@
/*
* QNAP Turbo NAS Board power off. Can also be used on Synology devices.
*
* Copyright (C) 2012 Andrew Lunn <andrew@lunn.ch>
*
* Based on the code from:
*
* Copyright (C) 2009 Martin Michlmayr <tbm@cyrius.com>
* Copyright (C) 2008 Byron Bradley <byron.bbradley@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/serial_reg.h>
#include <linux/kallsyms.h>
#include <linux/of.h>
#include <linux/io.h>
#include <linux/clk.h>
#define UART1_REG(x) (base + ((UART_##x) << 2))
struct power_off_cfg {
u32 baud;
char cmd;
};
static const struct power_off_cfg qnap_power_off_cfg = {
.baud = 19200,
.cmd = 'A',
};
static const struct power_off_cfg synology_power_off_cfg = {
.baud = 9600,
.cmd = '1',
};
static const struct of_device_id qnap_power_off_of_match_table[] = {
{ .compatible = "qnap,power-off",
.data = &qnap_power_off_cfg,
},
{ .compatible = "synology,power-off",
.data = &synology_power_off_cfg,
},
{}
};
MODULE_DEVICE_TABLE(of, qnap_power_off_of_match_table);
static void __iomem *base;
static unsigned long tclk;
static const struct power_off_cfg *cfg;
static void qnap_power_off(void)
{
const unsigned divisor = ((tclk + (8 * cfg->baud)) / (16 * cfg->baud));
pr_err("%s: triggering power-off...\n", __func__);
/* hijack UART1 and reset into sane state */
writel(0x83, UART1_REG(LCR));
writel(divisor & 0xff, UART1_REG(DLL));
writel((divisor >> 8) & 0xff, UART1_REG(DLM));
writel(0x03, UART1_REG(LCR));
writel(0x00, UART1_REG(IER));
writel(0x00, UART1_REG(FCR));
writel(0x00, UART1_REG(MCR));
/* send the power-off command to PIC */
writel(cfg->cmd, UART1_REG(TX));
}
static int qnap_power_off_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct resource *res;
struct clk *clk;
char symname[KSYM_NAME_LEN];
const struct of_device_id *match =
of_match_node(qnap_power_off_of_match_table, np);
cfg = match->data;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Missing resource");
return -EINVAL;
}
base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!base) {
dev_err(&pdev->dev, "Unable to map resource");
return -EINVAL;
}
/* We need to know tclk in order to calculate the UART divisor */
clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "Clk missing");
return PTR_ERR(clk);
}
tclk = clk_get_rate(clk);
/* Check that nothing else has already setup a handler */
if (pm_power_off) {
lookup_symbol_name((ulong)pm_power_off, symname);
dev_err(&pdev->dev,
"pm_power_off already claimed %p %s",
pm_power_off, symname);
return -EBUSY;
}
pm_power_off = qnap_power_off;
return 0;
}
static int qnap_power_off_remove(struct platform_device *pdev)
{
pm_power_off = NULL;
return 0;
}
static struct platform_driver qnap_power_off_driver = {
.probe = qnap_power_off_probe,
.remove = qnap_power_off_remove,
.driver = {
.owner = THIS_MODULE,
.name = "qnap_power_off",
.of_match_table = of_match_ptr(qnap_power_off_of_match_table),
},
};
module_platform_driver(qnap_power_off_driver);
MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
MODULE_DESCRIPTION("QNAP Power off driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,67 @@
/*
* Power off by restarting and let u-boot keep hold of the machine
* until the user presses a button for example.
*
* Andrew Lunn <andrew@lunn.ch>
*
* Copyright (C) 2012 Andrew Lunn
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/of_platform.h>
#include <linux/module.h>
#include <linux/reboot.h>
#include <asm/system_misc.h>
static void restart_poweroff_do_poweroff(void)
{
reboot_mode = REBOOT_HARD;
machine_restart(NULL);
}
static int restart_poweroff_probe(struct platform_device *pdev)
{
/* If a pm_power_off function has already been added, leave it alone */
if (pm_power_off != NULL) {
dev_err(&pdev->dev,
"pm_power_off function already registered");
return -EBUSY;
}
pm_power_off = &restart_poweroff_do_poweroff;
return 0;
}
static int restart_poweroff_remove(struct platform_device *pdev)
{
if (pm_power_off == &restart_poweroff_do_poweroff)
pm_power_off = NULL;
return 0;
}
static const struct of_device_id of_restart_poweroff_match[] = {
{ .compatible = "restart-poweroff", },
{},
};
static struct platform_driver restart_poweroff_driver = {
.probe = restart_poweroff_probe,
.remove = restart_poweroff_remove,
.driver = {
.name = "poweroff-restart",
.owner = THIS_MODULE,
.of_match_table = of_restart_poweroff_match,
},
};
module_platform_driver(restart_poweroff_driver);
MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch");
MODULE_DESCRIPTION("restart poweroff driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:poweroff-restart");

View file

@ -0,0 +1,151 @@
/*
* Copyright (C) 2014 STMicroelectronics
*
* Power off Restart driver, used in STMicroelectronics devices.
*
* Author: Christophe Kerello <christophe.kerello@st.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <asm/system_misc.h>
struct reset_syscfg {
struct regmap *regmap;
/* syscfg used for reset */
unsigned int offset_rst;
unsigned int mask_rst;
/* syscfg used for unmask the reset */
unsigned int offset_rst_msk;
unsigned int mask_rst_msk;
};
/* STiH415 */
#define STIH415_SYSCFG_11 0x2c
#define STIH415_SYSCFG_15 0x3c
static struct reset_syscfg stih415_reset = {
.offset_rst = STIH415_SYSCFG_11,
.mask_rst = BIT(0),
.offset_rst_msk = STIH415_SYSCFG_15,
.mask_rst_msk = BIT(0)
};
/* STiH416 */
#define STIH416_SYSCFG_500 0x7d0
#define STIH416_SYSCFG_504 0x7e0
static struct reset_syscfg stih416_reset = {
.offset_rst = STIH416_SYSCFG_500,
.mask_rst = BIT(0),
.offset_rst_msk = STIH416_SYSCFG_504,
.mask_rst_msk = BIT(0)
};
/* STiH407 */
#define STIH407_SYSCFG_4000 0x0
#define STIH407_SYSCFG_4008 0x20
static struct reset_syscfg stih407_reset = {
.offset_rst = STIH407_SYSCFG_4000,
.mask_rst = BIT(0),
.offset_rst_msk = STIH407_SYSCFG_4008,
.mask_rst_msk = BIT(0)
};
/* STiD127 */
#define STID127_SYSCFG_700 0x0
#define STID127_SYSCFG_773 0x124
static struct reset_syscfg stid127_reset = {
.offset_rst = STID127_SYSCFG_773,
.mask_rst = BIT(0),
.offset_rst_msk = STID127_SYSCFG_700,
.mask_rst_msk = BIT(8)
};
static struct reset_syscfg *st_restart_syscfg;
static void st_restart(enum reboot_mode reboot_mode, const char *cmd)
{
/* reset syscfg updated */
regmap_update_bits(st_restart_syscfg->regmap,
st_restart_syscfg->offset_rst,
st_restart_syscfg->mask_rst,
0);
/* unmask the reset */
regmap_update_bits(st_restart_syscfg->regmap,
st_restart_syscfg->offset_rst_msk,
st_restart_syscfg->mask_rst_msk,
0);
}
static struct of_device_id st_reset_of_match[] = {
{
.compatible = "st,stih415-restart",
.data = (void *)&stih415_reset,
}, {
.compatible = "st,stih416-restart",
.data = (void *)&stih416_reset,
}, {
.compatible = "st,stih407-restart",
.data = (void *)&stih407_reset,
}, {
.compatible = "st,stid127-restart",
.data = (void *)&stid127_reset,
},
{}
};
static int st_reset_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *match;
struct device *dev = &pdev->dev;
match = of_match_device(st_reset_of_match, dev);
if (!match)
return -ENODEV;
st_restart_syscfg = (struct reset_syscfg *)match->data;
st_restart_syscfg->regmap =
syscon_regmap_lookup_by_phandle(np, "st,syscfg");
if (IS_ERR(st_restart_syscfg->regmap)) {
dev_err(dev, "No syscfg phandle specified\n");
return PTR_ERR(st_restart_syscfg->regmap);
}
arm_pm_restart = st_restart;
return 0;
}
static struct platform_driver st_reset_driver = {
.probe = st_reset_probe,
.driver = {
.name = "st_reset",
.of_match_table = st_reset_of_match,
},
};
static int __init st_reset_init(void)
{
return platform_driver_register(&st_reset_driver);
}
device_initcall(st_reset_init);
MODULE_AUTHOR("Christophe Kerello <christophe.kerello@st.com>");
MODULE_DESCRIPTION("STMicroelectronics Power off Restart driver");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,85 @@
/*
* 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);

View file

@ -0,0 +1,91 @@
/*
* Generic Syscon Reboot Driver
*
* Copyright (c) 2013, Applied Micro Circuits Corporation
* Author: Feng Kan <fkan@apm.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/notifier.h>
#include <linux/mfd/syscon.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
struct syscon_reboot_context {
struct regmap *map;
u32 offset;
u32 mask;
struct notifier_block restart_handler;
};
static int syscon_restart_handle(struct notifier_block *this,
unsigned long mode, void *cmd)
{
struct syscon_reboot_context *ctx =
container_of(this, struct syscon_reboot_context,
restart_handler);
/* Issue the reboot */
regmap_write(ctx->map, ctx->offset, ctx->mask);
mdelay(1000);
pr_emerg("Unable to restart system\n");
return NOTIFY_DONE;
}
static int syscon_reboot_probe(struct platform_device *pdev)
{
struct syscon_reboot_context *ctx;
struct device *dev = &pdev->dev;
int err;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->map = syscon_regmap_lookup_by_phandle(dev->of_node, "regmap");
if (IS_ERR(ctx->map))
return PTR_ERR(ctx->map);
if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset))
return -EINVAL;
if (of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask))
return -EINVAL;
ctx->restart_handler.notifier_call = syscon_restart_handle;
ctx->restart_handler.priority = 128;
err = register_restart_handler(&ctx->restart_handler);
if (err)
dev_err(dev, "can't register restart notifier (err=%d)\n", err);
return err;
}
static struct of_device_id syscon_reboot_of_match[] = {
{ .compatible = "syscon-reboot" },
{}
};
static struct platform_driver syscon_reboot_driver = {
.probe = syscon_reboot_probe,
.driver = {
.name = "syscon-reboot",
.of_match_table = syscon_reboot_of_match,
},
};
module_platform_driver(syscon_reboot_driver);

View file

@ -0,0 +1,147 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* Copyright (C) 2012 ARM Limited
*/
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/stat.h>
#include <linux/vexpress.h>
#include <asm/system_misc.h>
static void vexpress_reset_do(struct device *dev, const char *what)
{
int err = -ENOENT;
struct regmap *reg = dev_get_drvdata(dev);
if (reg) {
err = regmap_write(reg, 0, 0);
if (!err)
mdelay(1000);
}
dev_emerg(dev, "Unable to %s (%d)\n", what, err);
}
static struct device *vexpress_power_off_device;
static void vexpress_power_off(void)
{
vexpress_reset_do(vexpress_power_off_device, "power off");
}
static struct device *vexpress_restart_device;
static void vexpress_restart(enum reboot_mode reboot_mode, const char *cmd)
{
vexpress_reset_do(vexpress_restart_device, "restart");
}
static ssize_t vexpress_reset_active_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", vexpress_restart_device == dev);
}
static ssize_t vexpress_reset_active_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
long value;
int err = kstrtol(buf, 0, &value);
if (!err && value)
vexpress_restart_device = dev;
return err ? err : count;
}
DEVICE_ATTR(active, S_IRUGO | S_IWUSR, vexpress_reset_active_show,
vexpress_reset_active_store);
enum vexpress_reset_func { FUNC_RESET, FUNC_SHUTDOWN, FUNC_REBOOT };
static struct of_device_id vexpress_reset_of_match[] = {
{
.compatible = "arm,vexpress-reset",
.data = (void *)FUNC_RESET,
}, {
.compatible = "arm,vexpress-shutdown",
.data = (void *)FUNC_SHUTDOWN
}, {
.compatible = "arm,vexpress-reboot",
.data = (void *)FUNC_REBOOT
},
{}
};
static int vexpress_reset_probe(struct platform_device *pdev)
{
enum vexpress_reset_func func;
const struct of_device_id *match =
of_match_device(vexpress_reset_of_match, &pdev->dev);
struct regmap *regmap;
if (match)
func = (enum vexpress_reset_func)match->data;
else
func = pdev->id_entry->driver_data;
regmap = devm_regmap_init_vexpress_config(&pdev->dev);
if (IS_ERR(regmap))
return PTR_ERR(regmap);
dev_set_drvdata(&pdev->dev, regmap);
switch (func) {
case FUNC_SHUTDOWN:
vexpress_power_off_device = &pdev->dev;
pm_power_off = vexpress_power_off;
break;
case FUNC_RESET:
if (!vexpress_restart_device)
vexpress_restart_device = &pdev->dev;
arm_pm_restart = vexpress_restart;
device_create_file(&pdev->dev, &dev_attr_active);
break;
case FUNC_REBOOT:
vexpress_restart_device = &pdev->dev;
arm_pm_restart = vexpress_restart;
device_create_file(&pdev->dev, &dev_attr_active);
break;
};
return 0;
}
static const struct platform_device_id vexpress_reset_id_table[] = {
{ .name = "vexpress-reset", .driver_data = FUNC_RESET, },
{ .name = "vexpress-shutdown", .driver_data = FUNC_SHUTDOWN, },
{ .name = "vexpress-reboot", .driver_data = FUNC_REBOOT, },
{}
};
static struct platform_driver vexpress_reset_driver = {
.probe = vexpress_reset_probe,
.driver = {
.name = "vexpress-reset",
.of_match_table = vexpress_reset_of_match,
},
.id_table = vexpress_reset_id_table,
};
static int __init vexpress_reset_init(void)
{
return platform_driver_register(&vexpress_reset_driver);
}
device_initcall(vexpress_reset_init);

View file

@ -0,0 +1,103 @@
/*
* AppliedMicro X-Gene SoC Reboot Driver
*
* Copyright (c) 2013, Applied Micro Circuits Corporation
* Author: Feng Kan <fkan@apm.com>
* Author: Loc Ho <lho@apm.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* This driver provides system reboot functionality for APM X-Gene SoC.
* For system shutdown, this is board specify. If a board designer
* implements GPIO shutdown, use the gpio-poweroff.c driver.
*/
#include <linux/io.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <asm/system_misc.h>
struct xgene_reboot_context {
struct platform_device *pdev;
void *csr;
u32 mask;
};
static struct xgene_reboot_context *xgene_restart_ctx;
static void xgene_restart(enum reboot_mode mode, const char *cmd)
{
struct xgene_reboot_context *ctx = xgene_restart_ctx;
unsigned long timeout;
/* Issue the reboot */
if (ctx)
writel(ctx->mask, ctx->csr);
timeout = jiffies + HZ;
while (time_before(jiffies, timeout))
cpu_relax();
dev_emerg(&ctx->pdev->dev, "Unable to restart system\n");
}
static int xgene_reboot_probe(struct platform_device *pdev)
{
struct xgene_reboot_context *ctx;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx) {
dev_err(&pdev->dev, "out of memory for context\n");
return -ENODEV;
}
ctx->csr = of_iomap(pdev->dev.of_node, 0);
if (!ctx->csr) {
devm_kfree(&pdev->dev, ctx);
dev_err(&pdev->dev, "can not map resource\n");
return -ENODEV;
}
if (of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask))
ctx->mask = 0xFFFFFFFF;
ctx->pdev = pdev;
arm_pm_restart = xgene_restart;
xgene_restart_ctx = ctx;
return 0;
}
static struct of_device_id xgene_reboot_of_match[] = {
{ .compatible = "apm,xgene-reboot" },
{}
};
static struct platform_driver xgene_reboot_driver = {
.probe = xgene_reboot_probe,
.driver = {
.name = "xgene-reboot",
.of_match_table = xgene_reboot_of_match,
},
};
static int __init xgene_reboot_init(void)
{
return platform_driver_register(&xgene_reboot_driver);
}
device_initcall(xgene_reboot_init);