Commit 9f9c1f68 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge branch 'pm-sysoff'

Merge system power off handling rework from Dmitry Osipenko for
5.19-rc1.

This introduces a mechanism allowing power sequences to be used for
powering off the system and makes related changes in platform-specific
code for multiple platforms.

* pm-sysoff: (29 commits)
  kernel/reboot: Change registration order of legacy power-off handler
  m68k: virt: Switch to new sys-off handler API
  kernel/reboot: Add devm_register_restart_handler()
  kernel/reboot: Add devm_register_power_off_handler()
  soc/tegra: pmc: Use sys-off handler API to power off Nexus 7 properly
  reboot: Remove pm_power_off_prepare()
  regulator: pfuze100: Use devm_register_sys_off_handler()
  ACPI: power: Switch to sys-off handler API
  memory: emif: Use kernel_can_power_off()
  mips: Use do_kernel_power_off()
  ia64: Use do_kernel_power_off()
  x86: Use do_kernel_power_off()
  sh: Use do_kernel_power_off()
  m68k: Switch to new sys-off handler API
  powerpc: Use do_kernel_power_off()
  xen/x86: Use do_kernel_power_off()
  parisc: Use do_kernel_power_off()
  arm64: Use do_kernel_power_off()
  riscv: Use do_kernel_power_off()
  csky: Use do_kernel_power_off()
  ...
parents 1cdc5ba0 da007f17
...@@ -116,9 +116,7 @@ void machine_power_off(void) ...@@ -116,9 +116,7 @@ void machine_power_off(void)
{ {
local_irq_disable(); local_irq_disable();
smp_send_stop(); smp_send_stop();
do_kernel_power_off();
if (pm_power_off)
pm_power_off();
} }
/* /*
......
...@@ -111,8 +111,7 @@ void machine_power_off(void) ...@@ -111,8 +111,7 @@ void machine_power_off(void)
{ {
local_irq_disable(); local_irq_disable();
smp_send_stop(); smp_send_stop();
if (pm_power_off) do_kernel_power_off();
pm_power_off();
} }
/* /*
......
...@@ -9,16 +9,14 @@ EXPORT_SYMBOL(pm_power_off); ...@@ -9,16 +9,14 @@ EXPORT_SYMBOL(pm_power_off);
void machine_power_off(void) void machine_power_off(void)
{ {
local_irq_disable(); local_irq_disable();
if (pm_power_off) do_kernel_power_off();
pm_power_off();
asm volatile ("bkpt"); asm volatile ("bkpt");
} }
void machine_halt(void) void machine_halt(void)
{ {
local_irq_disable(); local_irq_disable();
if (pm_power_off) do_kernel_power_off();
pm_power_off();
asm volatile ("bkpt"); asm volatile ("bkpt");
} }
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/reboot.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/sched/debug.h> #include <linux/sched/debug.h>
#include <linux/sched/hotplug.h> #include <linux/sched/hotplug.h>
...@@ -599,8 +600,7 @@ machine_halt (void) ...@@ -599,8 +600,7 @@ machine_halt (void)
void void
machine_power_off (void) machine_power_off (void)
{ {
if (pm_power_off) do_kernel_power_off();
pm_power_off();
machine_halt(); machine_halt();
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/string.h> #include <linux/string.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/reboot.h>
#include <linux/io.h> #include <linux/io.h>
#include <asm/machdep.h> #include <asm/machdep.h>
#include <asm/natfeat.h> #include <asm/natfeat.h>
...@@ -90,5 +91,5 @@ void __init nf_init(void) ...@@ -90,5 +91,5 @@ void __init nf_init(void)
pr_info("NatFeats found (%s, %lu.%lu)\n", buf, version >> 16, pr_info("NatFeats found (%s, %lu.%lu)\n", buf, version >> 16,
version & 0xffff); version & 0xffff);
mach_power_off = nf_poweroff; register_platform_power_off(nf_poweroff);
} }
...@@ -24,7 +24,6 @@ extern int (*mach_get_rtc_pll)(struct rtc_pll_info *); ...@@ -24,7 +24,6 @@ extern int (*mach_get_rtc_pll)(struct rtc_pll_info *);
extern int (*mach_set_rtc_pll)(struct rtc_pll_info *); extern int (*mach_set_rtc_pll)(struct rtc_pll_info *);
extern void (*mach_reset)( void ); extern void (*mach_reset)( void );
extern void (*mach_halt)( void ); extern void (*mach_halt)( void );
extern void (*mach_power_off)( void );
extern unsigned long (*mach_hd_init) (unsigned long, unsigned long); extern unsigned long (*mach_hd_init) (unsigned long, unsigned long);
extern void (*mach_hd_setup)(char *, int *); extern void (*mach_hd_setup)(char *, int *);
extern void (*mach_heartbeat) (int); extern void (*mach_heartbeat) (int);
......
...@@ -67,12 +67,11 @@ void machine_halt(void) ...@@ -67,12 +67,11 @@ void machine_halt(void)
void machine_power_off(void) void machine_power_off(void)
{ {
if (mach_power_off) do_kernel_power_off();
mach_power_off();
for (;;); for (;;);
} }
void (*pm_power_off)(void) = machine_power_off; void (*pm_power_off)(void);
EXPORT_SYMBOL(pm_power_off); EXPORT_SYMBOL(pm_power_off);
void show_regs(struct pt_regs * regs) void show_regs(struct pt_regs * regs)
......
...@@ -98,7 +98,6 @@ EXPORT_SYMBOL(mach_get_rtc_pll); ...@@ -98,7 +98,6 @@ EXPORT_SYMBOL(mach_get_rtc_pll);
EXPORT_SYMBOL(mach_set_rtc_pll); EXPORT_SYMBOL(mach_set_rtc_pll);
void (*mach_reset)( void ); void (*mach_reset)( void );
void (*mach_halt)( void ); void (*mach_halt)( void );
void (*mach_power_off)( void );
#ifdef CONFIG_HEARTBEAT #ifdef CONFIG_HEARTBEAT
void (*mach_heartbeat) (int); void (*mach_heartbeat) (int);
EXPORT_SYMBOL(mach_heartbeat); EXPORT_SYMBOL(mach_heartbeat);
......
...@@ -55,7 +55,6 @@ int (*mach_hwclk) (int, struct rtc_time*); ...@@ -55,7 +55,6 @@ int (*mach_hwclk) (int, struct rtc_time*);
/* machine dependent reboot functions */ /* machine dependent reboot functions */
void (*mach_reset)(void); void (*mach_reset)(void);
void (*mach_halt)(void); void (*mach_halt)(void);
void (*mach_power_off)(void);
#ifdef CONFIG_M68000 #ifdef CONFIG_M68000
#if defined(CONFIG_M68328) #if defined(CONFIG_M68328)
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/reboot.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/tty.h> #include <linux/tty.h>
...@@ -140,7 +141,6 @@ void __init config_mac(void) ...@@ -140,7 +141,6 @@ void __init config_mac(void)
mach_hwclk = mac_hwclk; mach_hwclk = mac_hwclk;
mach_reset = mac_reset; mach_reset = mac_reset;
mach_halt = mac_poweroff; mach_halt = mac_poweroff;
mach_power_off = mac_poweroff;
#if IS_ENABLED(CONFIG_INPUT_M68K_BEEP) #if IS_ENABLED(CONFIG_INPUT_M68K_BEEP)
mach_beep = mac_mksound; mach_beep = mac_mksound;
#endif #endif
...@@ -160,6 +160,8 @@ void __init config_mac(void) ...@@ -160,6 +160,8 @@ void __init config_mac(void)
if (macintosh_config->ident == MAC_MODEL_IICI) if (macintosh_config->ident == MAC_MODEL_IICI)
mach_l2_flush = via_l2_flush; mach_l2_flush = via_l2_flush;
register_platform_power_off(mac_poweroff);
} }
......
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
#include <linux/reboot.h>
#include <linux/serial_core.h> #include <linux/serial_core.h>
#include <clocksource/timer-goldfish.h> #include <clocksource/timer-goldfish.h>
...@@ -126,5 +127,6 @@ void __init config_virt(void) ...@@ -126,5 +127,6 @@ void __init config_virt(void)
mach_get_model = virt_get_model; mach_get_model = virt_get_model;
mach_reset = virt_reset; mach_reset = virt_reset;
mach_halt = virt_halt; mach_halt = virt_halt;
mach_power_off = virt_halt;
register_platform_power_off(virt_halt);
} }
...@@ -114,8 +114,7 @@ void machine_halt(void) ...@@ -114,8 +114,7 @@ void machine_halt(void)
void machine_power_off(void) void machine_power_off(void)
{ {
if (pm_power_off) do_kernel_power_off();
pm_power_off();
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
preempt_disable(); preempt_disable();
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/reboot.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/sched/debug.h> #include <linux/sched/debug.h>
#include <linux/sched/task.h> #include <linux/sched/task.h>
...@@ -116,8 +117,7 @@ void machine_power_off(void) ...@@ -116,8 +117,7 @@ void machine_power_off(void)
pdc_chassis_send_status(PDC_CHASSIS_DIRECT_SHUTDOWN); pdc_chassis_send_status(PDC_CHASSIS_DIRECT_SHUTDOWN);
/* ipmi_poweroff may have been installed. */ /* ipmi_poweroff may have been installed. */
if (pm_power_off) do_kernel_power_off();
pm_power_off();
/* It seems we have no way to power the system off via /* It seems we have no way to power the system off via
* software. The user has to press the button himself. */ * software. The user has to press the button himself. */
......
...@@ -161,9 +161,7 @@ void machine_restart(char *cmd) ...@@ -161,9 +161,7 @@ void machine_restart(char *cmd)
void machine_power_off(void) void machine_power_off(void)
{ {
machine_shutdown(); machine_shutdown();
if (pm_power_off) do_kernel_power_off();
pm_power_off();
smp_send_stop(); smp_send_stop();
machine_hang(); machine_hang();
} }
......
...@@ -1243,8 +1243,7 @@ static void bootcmds(void) ...@@ -1243,8 +1243,7 @@ static void bootcmds(void)
} else if (cmd == 'h') { } else if (cmd == 'h') {
ppc_md.halt(); ppc_md.halt();
} else if (cmd == 'p') { } else if (cmd == 'p') {
if (pm_power_off) do_kernel_power_off();
pm_power_off();
} }
} }
......
...@@ -23,16 +23,12 @@ void machine_restart(char *cmd) ...@@ -23,16 +23,12 @@ void machine_restart(char *cmd)
void machine_halt(void) void machine_halt(void)
{ {
if (pm_power_off != NULL) do_kernel_power_off();
pm_power_off(); default_power_off();
else
default_power_off();
} }
void machine_power_off(void) void machine_power_off(void)
{ {
if (pm_power_off != NULL) do_kernel_power_off();
pm_power_off(); default_power_off();
else
default_power_off();
} }
...@@ -46,8 +46,7 @@ static void native_machine_shutdown(void) ...@@ -46,8 +46,7 @@ static void native_machine_shutdown(void)
static void native_machine_power_off(void) static void native_machine_power_off(void)
{ {
if (pm_power_off) do_kernel_power_off();
pm_power_off();
} }
static void native_machine_halt(void) static void native_machine_halt(void)
......
...@@ -739,10 +739,10 @@ static void native_machine_halt(void) ...@@ -739,10 +739,10 @@ static void native_machine_halt(void)
static void native_machine_power_off(void) static void native_machine_power_off(void)
{ {
if (pm_power_off) { if (kernel_can_power_off()) {
if (!reboot_force) if (!reboot_force)
machine_shutdown(); machine_shutdown();
pm_power_off(); do_kernel_power_off();
} }
/* A fallback in case there is no PM info available */ /* A fallback in case there is no PM info available */
tboot_shutdown(TB_SHUTDOWN_HALT); tboot_shutdown(TB_SHUTDOWN_HALT);
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/edd.h> #include <linux/edd.h>
#include <linux/reboot.h>
#include <xen/xen.h> #include <xen/xen.h>
#include <xen/events.h> #include <xen/events.h>
...@@ -1069,8 +1070,7 @@ static void xen_machine_halt(void) ...@@ -1069,8 +1070,7 @@ static void xen_machine_halt(void)
static void xen_machine_power_off(void) static void xen_machine_power_off(void)
{ {
if (pm_power_off) do_kernel_power_off();
pm_power_off();
xen_reboot(SHUTDOWN_poweroff); xen_reboot(SHUTDOWN_poweroff);
} }
......
...@@ -1035,20 +1035,22 @@ static void acpi_sleep_hibernate_setup(void) ...@@ -1035,20 +1035,22 @@ static void acpi_sleep_hibernate_setup(void)
static inline void acpi_sleep_hibernate_setup(void) {} static inline void acpi_sleep_hibernate_setup(void) {}
#endif /* !CONFIG_HIBERNATION */ #endif /* !CONFIG_HIBERNATION */
static void acpi_power_off_prepare(void) static int acpi_power_off_prepare(struct sys_off_data *data)
{ {
/* Prepare to power off the system */ /* Prepare to power off the system */
acpi_sleep_prepare(ACPI_STATE_S5); acpi_sleep_prepare(ACPI_STATE_S5);
acpi_disable_all_gpes(); acpi_disable_all_gpes();
acpi_os_wait_events_complete(); acpi_os_wait_events_complete();
return NOTIFY_DONE;
} }
static void acpi_power_off(void) static int acpi_power_off(struct sys_off_data *data)
{ {
/* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */ /* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */
pr_debug("%s called\n", __func__); pr_debug("%s called\n", __func__);
local_irq_disable(); local_irq_disable();
acpi_enter_sleep_state(ACPI_STATE_S5); acpi_enter_sleep_state(ACPI_STATE_S5);
return NOTIFY_DONE;
} }
int __init acpi_sleep_init(void) int __init acpi_sleep_init(void)
...@@ -1067,8 +1069,14 @@ int __init acpi_sleep_init(void) ...@@ -1067,8 +1069,14 @@ int __init acpi_sleep_init(void)
if (acpi_sleep_state_supported(ACPI_STATE_S5)) { if (acpi_sleep_state_supported(ACPI_STATE_S5)) {
sleep_states[ACPI_STATE_S5] = 1; sleep_states[ACPI_STATE_S5] = 1;
pm_power_off_prepare = acpi_power_off_prepare;
pm_power_off = acpi_power_off; register_sys_off_handler(SYS_OFF_MODE_POWER_OFF_PREPARE,
SYS_OFF_PRIO_FIRMWARE,
acpi_power_off_prepare, NULL);
register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
SYS_OFF_PRIO_FIRMWARE,
acpi_power_off, NULL);
} else { } else {
acpi_no_s5 = true; acpi_no_s5 = true;
} }
......
...@@ -630,7 +630,7 @@ static irqreturn_t emif_threaded_isr(int irq, void *dev_id) ...@@ -630,7 +630,7 @@ static irqreturn_t emif_threaded_isr(int irq, void *dev_id)
dev_emerg(emif->dev, "SDRAM temperature exceeds operating limit.. Needs shut down!!!\n"); dev_emerg(emif->dev, "SDRAM temperature exceeds operating limit.. Needs shut down!!!\n");
/* If we have Power OFF ability, use it, else try restarting */ /* If we have Power OFF ability, use it, else try restarting */
if (pm_power_off) { if (kernel_can_power_off()) {
kernel_power_off(); kernel_power_off();
} else { } else {
WARN(1, "FIXME: NO pm_power_off!!! trying restart\n"); WARN(1, "FIXME: NO pm_power_off!!! trying restart\n");
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/regulator/of_regulator.h> #include <linux/regulator/of_regulator.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/regulator/driver.h> #include <linux/regulator/driver.h>
#include <linux/regulator/machine.h> #include <linux/regulator/machine.h>
#include <linux/regulator/pfuze100.h> #include <linux/regulator/pfuze100.h>
...@@ -571,10 +572,10 @@ static inline struct device_node *match_of_node(int index) ...@@ -571,10 +572,10 @@ static inline struct device_node *match_of_node(int index)
return pfuze_matches[index].of_node; return pfuze_matches[index].of_node;
} }
static struct pfuze_chip *syspm_pfuze_chip; static int pfuze_power_off_prepare(struct sys_off_data *data)
static void pfuze_power_off_prepare(void)
{ {
struct pfuze_chip *syspm_pfuze_chip = data->cb_data;
dev_info(syspm_pfuze_chip->dev, "Configure standby mode for power off"); dev_info(syspm_pfuze_chip->dev, "Configure standby mode for power off");
/* Switch from default mode: APS/APS to APS/Off */ /* Switch from default mode: APS/APS to APS/Off */
...@@ -609,28 +610,30 @@ static void pfuze_power_off_prepare(void) ...@@ -609,28 +610,30 @@ static void pfuze_power_off_prepare(void)
regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_VGEN6VOL, regmap_update_bits(syspm_pfuze_chip->regmap, PFUZE100_VGEN6VOL,
PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY, PFUZE100_VGENxLPWR | PFUZE100_VGENxSTBY,
PFUZE100_VGENxSTBY); PFUZE100_VGENxSTBY);
return NOTIFY_DONE;
} }
static int pfuze_power_off_prepare_init(struct pfuze_chip *pfuze_chip) static int pfuze_power_off_prepare_init(struct pfuze_chip *pfuze_chip)
{ {
int err;
if (pfuze_chip->chip_id != PFUZE100) { if (pfuze_chip->chip_id != PFUZE100) {
dev_warn(pfuze_chip->dev, "Requested pm_power_off_prepare handler for not supported chip\n"); dev_warn(pfuze_chip->dev, "Requested pm_power_off_prepare handler for not supported chip\n");
return -ENODEV; return -ENODEV;
} }
if (pm_power_off_prepare) { err = devm_register_sys_off_handler(pfuze_chip->dev,
dev_warn(pfuze_chip->dev, "pm_power_off_prepare is already registered.\n"); SYS_OFF_MODE_POWER_OFF_PREPARE,
return -EBUSY; SYS_OFF_PRIO_DEFAULT,
pfuze_power_off_prepare,
pfuze_chip);
if (err) {
dev_err(pfuze_chip->dev, "failed to register sys-off handler: %d\n",
err);
return err;
} }
if (syspm_pfuze_chip) {
dev_warn(pfuze_chip->dev, "syspm_pfuze_chip is already set.\n");
return -EBUSY;
}
syspm_pfuze_chip = pfuze_chip;
pm_power_off_prepare = pfuze_power_off_prepare;
return 0; return 0;
} }
...@@ -839,23 +842,12 @@ static int pfuze100_regulator_probe(struct i2c_client *client, ...@@ -839,23 +842,12 @@ static int pfuze100_regulator_probe(struct i2c_client *client,
return 0; return 0;
} }
static int pfuze100_regulator_remove(struct i2c_client *client)
{
if (syspm_pfuze_chip) {
syspm_pfuze_chip = NULL;
pm_power_off_prepare = NULL;
}
return 0;
}
static struct i2c_driver pfuze_driver = { static struct i2c_driver pfuze_driver = {
.driver = { .driver = {
.name = "pfuze100-regulator", .name = "pfuze100-regulator",
.of_match_table = pfuze_dt_ids, .of_match_table = pfuze_dt_ids,
}, },
.probe = pfuze100_regulator_probe, .probe = pfuze100_regulator_probe,
.remove = pfuze100_regulator_remove,
}; };
module_i2c_driver(pfuze_driver); module_i2c_driver(pfuze_driver);
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/pm_domain.h> #include <linux/pm_domain.h>
#include <linux/pm_opp.h> #include <linux/pm_opp.h>
#include <linux/power_supply.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/reset.h> #include <linux/reset.h>
...@@ -108,6 +109,7 @@ ...@@ -108,6 +109,7 @@
#define PMC_USB_DEBOUNCE_DEL 0xec #define PMC_USB_DEBOUNCE_DEL 0xec
#define PMC_USB_AO 0xf0 #define PMC_USB_AO 0xf0
#define PMC_SCRATCH37 0x130
#define PMC_SCRATCH41 0x140 #define PMC_SCRATCH41 0x140
#define PMC_WAKE2_MASK 0x160 #define PMC_WAKE2_MASK 0x160
...@@ -1099,8 +1101,7 @@ static struct notifier_block tegra_pmc_reboot_notifier = { ...@@ -1099,8 +1101,7 @@ static struct notifier_block tegra_pmc_reboot_notifier = {
.notifier_call = tegra_pmc_reboot_notify, .notifier_call = tegra_pmc_reboot_notify,
}; };
static int tegra_pmc_restart_notify(struct notifier_block *this, static void tegra_pmc_restart(void)
unsigned long action, void *data)
{ {
u32 value; u32 value;
...@@ -1108,14 +1109,31 @@ static int tegra_pmc_restart_notify(struct notifier_block *this, ...@@ -1108,14 +1109,31 @@ static int tegra_pmc_restart_notify(struct notifier_block *this,
value = tegra_pmc_readl(pmc, PMC_CNTRL); value = tegra_pmc_readl(pmc, PMC_CNTRL);
value |= PMC_CNTRL_MAIN_RST; value |= PMC_CNTRL_MAIN_RST;
tegra_pmc_writel(pmc, value, PMC_CNTRL); tegra_pmc_writel(pmc, value, PMC_CNTRL);
}
static int tegra_pmc_restart_handler(struct sys_off_data *data)
{
tegra_pmc_restart();
return NOTIFY_DONE; return NOTIFY_DONE;
} }
static struct notifier_block tegra_pmc_restart_handler = { static int tegra_pmc_power_off_handler(struct sys_off_data *data)
.notifier_call = tegra_pmc_restart_notify, {
.priority = 128, /*
}; * Reboot Nexus 7 into special bootloader mode if USB cable is
* connected in order to display battery status and power off.
*/
if (of_machine_is_compatible("asus,grouper") &&
power_supply_is_system_supplied()) {
const u32 go_to_charger_mode = 0xa5a55a5a;
tegra_pmc_writel(pmc, go_to_charger_mode, PMC_SCRATCH37);
tegra_pmc_restart();
}
return NOTIFY_DONE;
}
static int powergate_show(struct seq_file *s, void *data) static int powergate_show(struct seq_file *s, void *data)
{ {
...@@ -2877,6 +2895,42 @@ static int tegra_pmc_probe(struct platform_device *pdev) ...@@ -2877,6 +2895,42 @@ static int tegra_pmc_probe(struct platform_device *pdev)
pmc->clk = NULL; pmc->clk = NULL;
} }
/*
* PMC should be last resort for restarting since it soft-resets
* CPU without resetting everything else.
*/
err = devm_register_reboot_notifier(&pdev->dev,
&tegra_pmc_reboot_notifier);
if (err) {
dev_err(&pdev->dev, "unable to register reboot notifier, %d\n",
err);
return err;
}
err = devm_register_sys_off_handler(&pdev->dev,
SYS_OFF_MODE_RESTART,
SYS_OFF_PRIO_LOW,
tegra_pmc_restart_handler, NULL);
if (err) {
dev_err(&pdev->dev, "failed to register sys-off handler: %d\n",
err);
return err;
}
/*
* PMC should be primary power-off method if it soft-resets CPU,
* asking bootloader to shutdown hardware.
*/
err = devm_register_sys_off_handler(&pdev->dev,
SYS_OFF_MODE_POWER_OFF,
SYS_OFF_PRIO_FIRMWARE,
tegra_pmc_power_off_handler, NULL);
if (err) {
dev_err(&pdev->dev, "failed to register sys-off handler: %d\n",
err);
return err;
}
/* /*
* PCLK clock rate can't be retrieved using CLK API because it * PCLK clock rate can't be retrieved using CLK API because it
* causes lockup if CPU enters LP2 idle state from some other * causes lockup if CPU enters LP2 idle state from some other
...@@ -2908,28 +2962,13 @@ static int tegra_pmc_probe(struct platform_device *pdev) ...@@ -2908,28 +2962,13 @@ static int tegra_pmc_probe(struct platform_device *pdev)
goto cleanup_sysfs; goto cleanup_sysfs;
} }
err = devm_register_reboot_notifier(&pdev->dev,
&tegra_pmc_reboot_notifier);
if (err) {
dev_err(&pdev->dev, "unable to register reboot notifier, %d\n",
err);
goto cleanup_debugfs;
}
err = register_restart_handler(&tegra_pmc_restart_handler);
if (err) {
dev_err(&pdev->dev, "unable to register restart handler, %d\n",
err);
goto cleanup_debugfs;
}
err = tegra_pmc_pinctrl_init(pmc); err = tegra_pmc_pinctrl_init(pmc);
if (err) if (err)
goto cleanup_restart_handler; goto cleanup_debugfs;
err = tegra_pmc_regmap_init(pmc); err = tegra_pmc_regmap_init(pmc);
if (err < 0) if (err < 0)
goto cleanup_restart_handler; goto cleanup_debugfs;
err = tegra_powergate_init(pmc, pdev->dev.of_node); err = tegra_powergate_init(pmc, pdev->dev.of_node);
if (err < 0) if (err < 0)
...@@ -2952,8 +2991,6 @@ static int tegra_pmc_probe(struct platform_device *pdev) ...@@ -2952,8 +2991,6 @@ static int tegra_pmc_probe(struct platform_device *pdev)
cleanup_powergates: cleanup_powergates:
tegra_powergate_remove_all(pdev->dev.of_node); tegra_powergate_remove_all(pdev->dev.of_node);
cleanup_restart_handler:
unregister_restart_handler(&tegra_pmc_restart_handler);
cleanup_debugfs: cleanup_debugfs:
debugfs_remove(pmc->debugfs); debugfs_remove(pmc->debugfs);
cleanup_sysfs: cleanup_sysfs:
......
...@@ -150,6 +150,11 @@ extern int raw_notifier_chain_register(struct raw_notifier_head *nh, ...@@ -150,6 +150,11 @@ extern int raw_notifier_chain_register(struct raw_notifier_head *nh,
extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh, extern int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
struct notifier_block *nb); struct notifier_block *nb);
extern int atomic_notifier_chain_register_unique_prio(
struct atomic_notifier_head *nh, struct notifier_block *nb);
extern int blocking_notifier_chain_register_unique_prio(
struct blocking_notifier_head *nh, struct notifier_block *nb);
extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh, extern int atomic_notifier_chain_unregister(struct atomic_notifier_head *nh,
struct notifier_block *nb); struct notifier_block *nb);
extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh, extern int blocking_notifier_chain_unregister(struct blocking_notifier_head *nh,
...@@ -173,6 +178,8 @@ extern int blocking_notifier_call_chain_robust(struct blocking_notifier_head *nh ...@@ -173,6 +178,8 @@ extern int blocking_notifier_call_chain_robust(struct blocking_notifier_head *nh
extern int raw_notifier_call_chain_robust(struct raw_notifier_head *nh, extern int raw_notifier_call_chain_robust(struct raw_notifier_head *nh,
unsigned long val_up, unsigned long val_down, void *v); unsigned long val_up, unsigned long val_down, void *v);
extern bool atomic_notifier_call_chain_is_empty(struct atomic_notifier_head *nh);
#define NOTIFY_DONE 0x0000 /* Don't care */ #define NOTIFY_DONE 0x0000 /* Don't care */
#define NOTIFY_OK 0x0001 /* Suits me */ #define NOTIFY_OK 0x0001 /* Suits me */
#define NOTIFY_STOP_MASK 0x8000 /* Don't call further */ #define NOTIFY_STOP_MASK 0x8000 /* Don't call further */
......
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
* Callbacks for platform drivers to implement. * Callbacks for platform drivers to implement.
*/ */
extern void (*pm_power_off)(void); extern void (*pm_power_off)(void);
extern void (*pm_power_off_prepare)(void);
struct device; /* we have a circular dep with device.h */ struct device; /* we have a circular dep with device.h */
#ifdef CONFIG_VT_CONSOLE_SLEEP #ifdef CONFIG_VT_CONSOLE_SLEEP
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <uapi/linux/reboot.h> #include <uapi/linux/reboot.h>
struct device; struct device;
struct sys_off_handler;
#define SYS_DOWN 0x0001 /* Notify of system down */ #define SYS_DOWN 0x0001 /* Notify of system down */
#define SYS_RESTART SYS_DOWN #define SYS_RESTART SYS_DOWN
...@@ -62,6 +63,95 @@ extern void machine_shutdown(void); ...@@ -62,6 +63,95 @@ extern void machine_shutdown(void);
struct pt_regs; struct pt_regs;
extern void machine_crash_shutdown(struct pt_regs *); extern void machine_crash_shutdown(struct pt_regs *);
void do_kernel_power_off(void);
/*
* sys-off handler API.
*/
/*
* Standard sys-off priority levels. Users are expected to set priorities
* relative to the standard levels.
*
* SYS_OFF_PRIO_PLATFORM: Use this for platform-level handlers.
*
* SYS_OFF_PRIO_LOW: Use this for handler of last resort.
*
* SYS_OFF_PRIO_DEFAULT: Use this for normal handlers.
*
* SYS_OFF_PRIO_HIGH: Use this for higher priority handlers.
*
* SYS_OFF_PRIO_FIRMWARE: Use this if handler uses firmware call.
*/
#define SYS_OFF_PRIO_PLATFORM -256
#define SYS_OFF_PRIO_LOW -128
#define SYS_OFF_PRIO_DEFAULT 0
#define SYS_OFF_PRIO_HIGH 192
#define SYS_OFF_PRIO_FIRMWARE 224
enum sys_off_mode {
/**
* @SYS_OFF_MODE_POWER_OFF_PREPARE:
*
* Handlers prepare system to be powered off. Handlers are
* allowed to sleep.
*/
SYS_OFF_MODE_POWER_OFF_PREPARE,
/**
* @SYS_OFF_MODE_POWER_OFF:
*
* Handlers power-off system. Handlers are disallowed to sleep.
*/
SYS_OFF_MODE_POWER_OFF,
/**
* @SYS_OFF_MODE_RESTART:
*
* Handlers restart system. Handlers are disallowed to sleep.
*/
SYS_OFF_MODE_RESTART,
};
/**
* struct sys_off_data - sys-off callback argument
*
* @mode: Mode ID. Currently used only by the sys-off restart mode,
* see enum reboot_mode for the available modes.
* @cb_data: User's callback data.
* @cmd: Command string. Currently used only by the sys-off restart mode,
* NULL otherwise.
*/
struct sys_off_data {
int mode;
void *cb_data;
const char *cmd;
};
struct sys_off_handler *
register_sys_off_handler(enum sys_off_mode mode,
int priority,
int (*callback)(struct sys_off_data *data),
void *cb_data);
void unregister_sys_off_handler(struct sys_off_handler *handler);
int devm_register_sys_off_handler(struct device *dev,
enum sys_off_mode mode,
int priority,
int (*callback)(struct sys_off_data *data),
void *cb_data);
int devm_register_power_off_handler(struct device *dev,
int (*callback)(struct sys_off_data *data),
void *cb_data);
int devm_register_restart_handler(struct device *dev,
int (*callback)(struct sys_off_data *data),
void *cb_data);
int register_platform_power_off(void (*power_off)(void));
void unregister_platform_power_off(void (*power_off)(void));
/* /*
* Architecture independent implemenations of sys_reboot commands. * Architecture independent implemenations of sys_reboot commands.
*/ */
...@@ -70,6 +160,7 @@ extern void kernel_restart_prepare(char *cmd); ...@@ -70,6 +160,7 @@ extern void kernel_restart_prepare(char *cmd);
extern void kernel_restart(char *cmd); extern void kernel_restart(char *cmd);
extern void kernel_halt(void); extern void kernel_halt(void);
extern void kernel_power_off(void); extern void kernel_power_off(void);
extern bool kernel_can_power_off(void);
extern int C_A_D; /* for sysctl */ extern int C_A_D; /* for sysctl */
void ctrl_alt_del(void); void ctrl_alt_del(void);
......
...@@ -20,7 +20,8 @@ BLOCKING_NOTIFIER_HEAD(reboot_notifier_list); ...@@ -20,7 +20,8 @@ BLOCKING_NOTIFIER_HEAD(reboot_notifier_list);
*/ */
static int notifier_chain_register(struct notifier_block **nl, static int notifier_chain_register(struct notifier_block **nl,
struct notifier_block *n) struct notifier_block *n,
bool unique_priority)
{ {
while ((*nl) != NULL) { while ((*nl) != NULL) {
if (unlikely((*nl) == n)) { if (unlikely((*nl) == n)) {
...@@ -30,6 +31,8 @@ static int notifier_chain_register(struct notifier_block **nl, ...@@ -30,6 +31,8 @@ static int notifier_chain_register(struct notifier_block **nl,
} }
if (n->priority > (*nl)->priority) if (n->priority > (*nl)->priority)
break; break;
if (n->priority == (*nl)->priority && unique_priority)
return -EBUSY;
nl = &((*nl)->next); nl = &((*nl)->next);
} }
n->next = *nl; n->next = *nl;
...@@ -144,12 +147,35 @@ int atomic_notifier_chain_register(struct atomic_notifier_head *nh, ...@@ -144,12 +147,35 @@ int atomic_notifier_chain_register(struct atomic_notifier_head *nh,
int ret; int ret;
spin_lock_irqsave(&nh->lock, flags); spin_lock_irqsave(&nh->lock, flags);
ret = notifier_chain_register(&nh->head, n); ret = notifier_chain_register(&nh->head, n, false);
spin_unlock_irqrestore(&nh->lock, flags); spin_unlock_irqrestore(&nh->lock, flags);
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(atomic_notifier_chain_register); EXPORT_SYMBOL_GPL(atomic_notifier_chain_register);
/**
* atomic_notifier_chain_register_unique_prio - Add notifier to an atomic notifier chain
* @nh: Pointer to head of the atomic notifier chain
* @n: New entry in notifier chain
*
* Adds a notifier to an atomic notifier chain if there is no other
* notifier registered using the same priority.
*
* Returns 0 on success, %-EEXIST or %-EBUSY on error.
*/
int atomic_notifier_chain_register_unique_prio(struct atomic_notifier_head *nh,
struct notifier_block *n)
{
unsigned long flags;
int ret;
spin_lock_irqsave(&nh->lock, flags);
ret = notifier_chain_register(&nh->head, n, true);
spin_unlock_irqrestore(&nh->lock, flags);
return ret;
}
EXPORT_SYMBOL_GPL(atomic_notifier_chain_register_unique_prio);
/** /**
* atomic_notifier_chain_unregister - Remove notifier from an atomic notifier chain * atomic_notifier_chain_unregister - Remove notifier from an atomic notifier chain
* @nh: Pointer to head of the atomic notifier chain * @nh: Pointer to head of the atomic notifier chain
...@@ -204,23 +230,27 @@ int atomic_notifier_call_chain(struct atomic_notifier_head *nh, ...@@ -204,23 +230,27 @@ int atomic_notifier_call_chain(struct atomic_notifier_head *nh,
EXPORT_SYMBOL_GPL(atomic_notifier_call_chain); EXPORT_SYMBOL_GPL(atomic_notifier_call_chain);
NOKPROBE_SYMBOL(atomic_notifier_call_chain); NOKPROBE_SYMBOL(atomic_notifier_call_chain);
/**
* atomic_notifier_call_chain_is_empty - Check whether notifier chain is empty
* @nh: Pointer to head of the atomic notifier chain
*
* Checks whether notifier chain is empty.
*
* Returns true is notifier chain is empty, false otherwise.
*/
bool atomic_notifier_call_chain_is_empty(struct atomic_notifier_head *nh)
{
return !rcu_access_pointer(nh->head);
}
/* /*
* Blocking notifier chain routines. All access to the chain is * Blocking notifier chain routines. All access to the chain is
* synchronized by an rwsem. * synchronized by an rwsem.
*/ */
/** static int __blocking_notifier_chain_register(struct blocking_notifier_head *nh,
* blocking_notifier_chain_register - Add notifier to a blocking notifier chain struct notifier_block *n,
* @nh: Pointer to head of the blocking notifier chain bool unique_priority)
* @n: New entry in notifier chain
*
* Adds a notifier to a blocking notifier chain.
* Must be called in process context.
*
* Returns 0 on success, %-EEXIST on error.
*/
int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
struct notifier_block *n)
{ {
int ret; int ret;
...@@ -230,15 +260,48 @@ int blocking_notifier_chain_register(struct blocking_notifier_head *nh, ...@@ -230,15 +260,48 @@ int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
* such times we must not call down_write(). * such times we must not call down_write().
*/ */
if (unlikely(system_state == SYSTEM_BOOTING)) if (unlikely(system_state == SYSTEM_BOOTING))
return notifier_chain_register(&nh->head, n); return notifier_chain_register(&nh->head, n, unique_priority);
down_write(&nh->rwsem); down_write(&nh->rwsem);
ret = notifier_chain_register(&nh->head, n); ret = notifier_chain_register(&nh->head, n, unique_priority);
up_write(&nh->rwsem); up_write(&nh->rwsem);
return ret; return ret;
} }
/**
* blocking_notifier_chain_register - Add notifier to a blocking notifier chain
* @nh: Pointer to head of the blocking notifier chain
* @n: New entry in notifier chain
*
* Adds a notifier to a blocking notifier chain.
* Must be called in process context.
*
* Returns 0 on success, %-EEXIST on error.
*/
int blocking_notifier_chain_register(struct blocking_notifier_head *nh,
struct notifier_block *n)
{
return __blocking_notifier_chain_register(nh, n, false);
}
EXPORT_SYMBOL_GPL(blocking_notifier_chain_register); EXPORT_SYMBOL_GPL(blocking_notifier_chain_register);
/**
* blocking_notifier_chain_register_unique_prio - Add notifier to a blocking notifier chain
* @nh: Pointer to head of the blocking notifier chain
* @n: New entry in notifier chain
*
* Adds a notifier to an blocking notifier chain if there is no other
* notifier registered using the same priority.
*
* Returns 0 on success, %-EEXIST or %-EBUSY on error.
*/
int blocking_notifier_chain_register_unique_prio(struct blocking_notifier_head *nh,
struct notifier_block *n)
{
return __blocking_notifier_chain_register(nh, n, true);
}
EXPORT_SYMBOL_GPL(blocking_notifier_chain_register_unique_prio);
/** /**
* blocking_notifier_chain_unregister - Remove notifier from a blocking notifier chain * blocking_notifier_chain_unregister - Remove notifier from a blocking notifier chain
* @nh: Pointer to head of the blocking notifier chain * @nh: Pointer to head of the blocking notifier chain
...@@ -341,7 +404,7 @@ EXPORT_SYMBOL_GPL(blocking_notifier_call_chain); ...@@ -341,7 +404,7 @@ EXPORT_SYMBOL_GPL(blocking_notifier_call_chain);
int raw_notifier_chain_register(struct raw_notifier_head *nh, int raw_notifier_chain_register(struct raw_notifier_head *nh,
struct notifier_block *n) struct notifier_block *n)
{ {
return notifier_chain_register(&nh->head, n); return notifier_chain_register(&nh->head, n, false);
} }
EXPORT_SYMBOL_GPL(raw_notifier_chain_register); EXPORT_SYMBOL_GPL(raw_notifier_chain_register);
...@@ -420,10 +483,10 @@ int srcu_notifier_chain_register(struct srcu_notifier_head *nh, ...@@ -420,10 +483,10 @@ int srcu_notifier_chain_register(struct srcu_notifier_head *nh,
* such times we must not call mutex_lock(). * such times we must not call mutex_lock().
*/ */
if (unlikely(system_state == SYSTEM_BOOTING)) if (unlikely(system_state == SYSTEM_BOOTING))
return notifier_chain_register(&nh->head, n); return notifier_chain_register(&nh->head, n, false);
mutex_lock(&nh->mutex); mutex_lock(&nh->mutex);
ret = notifier_chain_register(&nh->head, n); ret = notifier_chain_register(&nh->head, n, false);
mutex_unlock(&nh->mutex); mutex_unlock(&nh->mutex);
return ret; return ret;
} }
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment