mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 17:18:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
154
drivers/power/reset/Kconfig
Normal file
154
drivers/power/reset/Kconfig
Normal 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
|
||||
|
19
drivers/power/reset/Makefile
Normal file
19
drivers/power/reset/Makefile
Normal 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
|
111
drivers/power/reset/arm-versatile-reboot.c
Normal file
111
drivers/power/reset/arm-versatile-reboot.c
Normal 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);
|
96
drivers/power/reset/as3722-poweroff.c
Normal file
96
drivers/power/reset/as3722-poweroff.c
Normal 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");
|
156
drivers/power/reset/at91-poweroff.c
Normal file
156
drivers/power/reset/at91-poweroff.c
Normal 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);
|
252
drivers/power/reset/at91-reset.c
Normal file
252
drivers/power/reset/at91-reset.c
Normal 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);
|
88
drivers/power/reset/axxia-reset.c
Normal file
88
drivers/power/reset/axxia-reset.c
Normal 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);
|
120
drivers/power/reset/brcmstb-reboot.c
Normal file
120
drivers/power/reset/brcmstb-reboot.c
Normal 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);
|
112
drivers/power/reset/gpio-poweroff.c
Normal file
112
drivers/power/reset/gpio-poweroff.c
Normal 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");
|
149
drivers/power/reset/gpio-restart.c
Normal file
149
drivers/power/reset/gpio-restart.c
Normal 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");
|
67
drivers/power/reset/hisi-reboot.c
Normal file
67
drivers/power/reset/hisi-reboot.c
Normal 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);
|
166
drivers/power/reset/keystone-reset.c
Normal file
166
drivers/power/reset/keystone-reset.c
Normal 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);
|
386
drivers/power/reset/ltc2952-poweroff.c
Normal file
386
drivers/power/reset/ltc2952-poweroff.c
Normal 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(<c2952_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 = <c2952_poweroff_timer_trigger;
|
||||
|
||||
hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
data->timer_wde.function = <c2952_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 = <c2952_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,
|
||||
<c2952_poweroff_panic_nb);
|
||||
|
||||
return platform_driver_register(<c2952_poweroff_driver);
|
||||
}
|
||||
|
||||
static void __exit ltc2952_poweroff_platform_exit(void)
|
||||
{
|
||||
atomic_notifier_chain_unregister(&panic_notifier_list,
|
||||
<c2952_poweroff_panic_nb);
|
||||
|
||||
platform_driver_unregister(<c2952_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");
|
81
drivers/power/reset/msm-poweroff.c
Normal file
81
drivers/power/reset/msm-poweroff.c
Normal 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);
|
141
drivers/power/reset/qnap-poweroff.c
Normal file
141
drivers/power/reset/qnap-poweroff.c
Normal 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");
|
67
drivers/power/reset/restart-poweroff.c
Normal file
67
drivers/power/reset/restart-poweroff.c
Normal 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");
|
151
drivers/power/reset/st-poweroff.c
Normal file
151
drivers/power/reset/st-poweroff.c
Normal 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");
|
85
drivers/power/reset/sun6i-reboot.c
Normal file
85
drivers/power/reset/sun6i-reboot.c
Normal 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);
|
91
drivers/power/reset/syscon-reboot.c
Normal file
91
drivers/power/reset/syscon-reboot.c
Normal 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);
|
147
drivers/power/reset/vexpress-poweroff.c
Normal file
147
drivers/power/reset/vexpress-poweroff.c
Normal 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);
|
103
drivers/power/reset/xgene-reboot.c
Normal file
103
drivers/power/reset/xgene-reboot.c
Normal 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);
|
Loading…
Add table
Add a link
Reference in a new issue