]> Git Repo - linux.git/commitdiff
Merge tag 'pm-5.19-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
authorLinus Torvalds <[email protected]>
Mon, 30 May 2022 18:37:26 +0000 (11:37 -0700)
committerLinus Torvalds <[email protected]>
Mon, 30 May 2022 18:37:26 +0000 (11:37 -0700)
Pull more power management updates from Rafael Wysocki:
 "These update the ARM cpufreq drivers and fix up the CPPC cpufreq
  driver after recent changes, update the OPP code and PM documentation
  and add power sequences support to the system reboot and power off
  code.

  Specifics:

   - Add Tegra234 cpufreq support (Sumit Gupta)

   - Clean up and enhance the Mediatek cpufreq driver (Wan Jiabing,
     Rex-BC Chen, and Jia-Wei Chang)

   - Fix up the CPPC cpufreq driver after recent changes (Zheng Bin,
     Pierre Gondois)

   - Minor update to dt-binding for Qcom's opp-v2-kryo-cpu (Yassine
     Oudjana)

   - Use list iterator only inside the list_for_each_entry loop
     (Xiaomeng Tong, and Jakob Koschel)

   - New APIs related to finding OPP based on interconnect bandwidth
     (Krzysztof Kozlowski)

   - Fix the missing of_node_put() in _bandwidth_supported() (Dan
     Carpenter)

   - Cleanups (Krzysztof Kozlowski, and Viresh Kumar)

   - Add Out of Band mode description to the intel-speed-select utility
     documentation (Srinivas Pandruvada)

   - Add power sequences support to the system reboot and power off code
     and make related platform-specific changes for multiple platforms
     (Dmitry Osipenko, Geert Uytterhoeven)"

* tag 'pm-5.19-rc1-2' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (60 commits)
  cpufreq: CPPC: Fix unused-function warning
  cpufreq: CPPC: Fix build error without CONFIG_ACPI_CPPC_CPUFREQ_FIE
  Documentation: admin-guide: PM: Add Out of Band mode
  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()
  ...

1  2 
arch/m68k/include/asm/machdep.h
arch/m68k/kernel/setup_mm.c
arch/m68k/kernel/setup_no.c
arch/powerpc/kernel/setup-common.c
arch/powerpc/xmon/xmon.c
drivers/memory/emif.c
drivers/soc/tegra/pmc.c
include/linux/pm.h
include/linux/reboot.h
kernel/reboot.c

index 841ba6aa3fcb18f7fe672794c6916c29b3a03066,8d8c3ee2069f0cff19525899d3c3302331dd2d6f..48d27f1fecc70cd95d029b8d6467cab985fc3a7d
@@@ -19,11 -19,11 +19,10 @@@ extern void (*mach_get_model) (char *mo
  extern void (*mach_get_hardware_list) (struct seq_file *m);
  /* machine dependent timer functions */
  extern int (*mach_hwclk)(int, struct rtc_time*);
 -extern unsigned int (*mach_get_ss)(void);
  extern int (*mach_get_rtc_pll)(struct rtc_pll_info *);
  extern int (*mach_set_rtc_pll)(struct rtc_pll_info *);
  extern void (*mach_reset)( void );
  extern void (*mach_halt)( void );
- extern void (*mach_power_off)( void );
  extern unsigned long (*mach_hd_init) (unsigned long, unsigned long);
  extern void (*mach_hd_setup)(char *, int *);
  extern void (*mach_heartbeat) (int);
index 656841defd2aabd0f756efd2c4a12168b8344c12,42691abcd908a6d406673b8b0d81d6fc18f008e4..e62fa8f2149b3574df6bed602e28571aa7d73d0c
@@@ -87,9 -87,17 +87,8 @@@ void (*mach_sched_init) (void) __initda
  void (*mach_init_IRQ) (void) __initdata = NULL;
  void (*mach_get_model) (char *model);
  void (*mach_get_hardware_list) (struct seq_file *m);
 -/* machine dependent timer functions */
 -int (*mach_hwclk) (int, struct rtc_time*);
 -EXPORT_SYMBOL(mach_hwclk);
 -unsigned int (*mach_get_ss)(void);
 -int (*mach_get_rtc_pll)(struct rtc_pll_info *);
 -int (*mach_set_rtc_pll)(struct rtc_pll_info *);
 -EXPORT_SYMBOL(mach_get_ss);
 -EXPORT_SYMBOL(mach_get_rtc_pll);
 -EXPORT_SYMBOL(mach_set_rtc_pll);
  void (*mach_reset)( void );
  void (*mach_halt)( void );
- void (*mach_power_off)( void );
  #ifdef CONFIG_HEARTBEAT
  void (*mach_heartbeat) (int);
  EXPORT_SYMBOL(mach_heartbeat);
index 19eea73d3c170664516ffb042dd9561a17911a64,00bf8225823372cdfbcc0f9ab494e3380e4f7c8f..cb6def585851e6c2fb9b3055239eecf612b377a4
@@@ -50,11 -50,11 +50,10 @@@ char __initdata command_line[COMMAND_LI
  
  /* machine dependent timer functions */
  void (*mach_sched_init)(void) __initdata = NULL;
 -int (*mach_hwclk) (int, struct rtc_time*);
  
  /* machine dependent reboot functions */
  void (*mach_reset)(void);
  void (*mach_halt)(void);
- void (*mach_power_off)(void);
  
  #ifdef CONFIG_M68000
  #if defined(CONFIG_M68328)
index 9d83d16fef9a4a203c11507b8a60980a7495d1b9,1b586577e75b99330d123d4e7bb085ccc2830404..eb0077b302e24fe0024dfd76b8e3ac096f7ca784
  #include <linux/console.h>
  #include <linux/screen_info.h>
  #include <linux/root_dev.h>
 -#include <linux/notifier.h>
  #include <linux/cpu.h>
  #include <linux/unistd.h>
  #include <linux/serial.h>
  #include <linux/serial_8250.h>
  #include <linux/percpu.h>
  #include <linux/memblock.h>
 +#include <linux/of_irq.h>
 +#include <linux/of_fdt.h>
  #include <linux/of_platform.h>
  #include <linux/hugetlb.h>
  #include <linux/pgtable.h>
  #include <asm/io.h>
  #include <asm/paca.h>
 -#include <asm/prom.h>
  #include <asm/processor.h>
  #include <asm/vdso_datapage.h>
  #include <asm/smp.h>
@@@ -161,9 -161,7 +161,7 @@@ void machine_restart(char *cmd
  void machine_power_off(void)
  {
        machine_shutdown();
-       if (pm_power_off)
-               pm_power_off();
+       do_kernel_power_off();
        smp_send_stop();
        machine_hang();
  }
@@@ -279,7 -277,7 +277,7 @@@ static int show_cpuinfo(struct seq_fil
                           proc_freq / 1000000, proc_freq % 1000000);
  
        /* If we are a Freescale core do a simple check so
 -       * we dont have to keep adding cases in the future */
 +       * we don't have to keep adding cases in the future */
        if (PVR_VER(pvr) & 0x8000) {
                switch (PVR_VER(pvr)) {
                case 0x8000:    /* 7441/7450/7451, Voyager */
@@@ -680,25 -678,8 +678,25 @@@ int check_legacy_ioport(unsigned long b
  }
  EXPORT_SYMBOL(check_legacy_ioport);
  
 -static int ppc_panic_event(struct notifier_block *this,
 -                             unsigned long event, void *ptr)
 +/*
 + * Panic notifiers setup
 + *
 + * We have 3 notifiers for powerpc, each one from a different "nature":
 + *
 + * - ppc_panic_fadump_handler() is a hypervisor notifier, which hard-disables
 + *   IRQs and deal with the Firmware-Assisted dump, when it is configured;
 + *   should run early in the panic path.
 + *
 + * - dump_kernel_offset() is an informative notifier, just showing the KASLR
 + *   offset if we have RANDOMIZE_BASE set.
 + *
 + * - ppc_panic_platform_handler() is a low-level handler that's registered
 + *   only if the platform wishes to perform final actions in the panic path,
 + *   hence it should run late and might not even return. Currently, only
 + *   pseries and ps3 platforms register callbacks.
 + */
 +static int ppc_panic_fadump_handler(struct notifier_block *this,
 +                                  unsigned long event, void *ptr)
  {
        /*
         * panic does a local_irq_disable, but we really
  
        /*
         * If firmware-assisted dump has been registered then trigger
 -       * firmware-assisted dump and let firmware handle everything else.
 +       * its callback and let the firmware handles everything else.
         */
        crash_fadump(NULL, ptr);
 -      if (ppc_md.panic)
 -              ppc_md.panic(ptr);  /* May not return */
 +
        return NOTIFY_DONE;
  }
  
 -static struct notifier_block ppc_panic_block = {
 -      .notifier_call = ppc_panic_event,
 -      .priority = INT_MIN /* may not return; must be done last */
 -};
 -
 -/*
 - * Dump out kernel offset information on panic.
 - */
  static int dump_kernel_offset(struct notifier_block *self, unsigned long v,
                              void *p)
  {
        pr_emerg("Kernel Offset: 0x%lx from 0x%lx\n",
                 kaslr_offset(), KERNELBASE);
  
 -      return 0;
 +      return NOTIFY_DONE;
  }
  
 +static int ppc_panic_platform_handler(struct notifier_block *this,
 +                                    unsigned long event, void *ptr)
 +{
 +      /*
 +       * This handler is only registered if we have a panic callback
 +       * on ppc_md, hence NULL check is not needed.
 +       * Also, it may not return, so it runs really late on panic path.
 +       */
 +      ppc_md.panic(ptr);
 +
 +      return NOTIFY_DONE;
 +}
 +
 +static struct notifier_block ppc_fadump_block = {
 +      .notifier_call = ppc_panic_fadump_handler,
 +      .priority = INT_MAX, /* run early, to notify the firmware ASAP */
 +};
 +
  static struct notifier_block kernel_offset_notifier = {
 -      .notifier_call = dump_kernel_offset
 +      .notifier_call = dump_kernel_offset,
 +};
 +
 +static struct notifier_block ppc_panic_block = {
 +      .notifier_call = ppc_panic_platform_handler,
 +      .priority = INT_MIN, /* may not return; must be done last */
  };
  
  void __init setup_panic(void)
  {
 +      /* Hard-disables IRQs + deal with FW-assisted dump (fadump) */
 +      atomic_notifier_chain_register(&panic_notifier_list,
 +                                     &ppc_fadump_block);
 +
        if (IS_ENABLED(CONFIG_RANDOMIZE_BASE) && kaslr_offset() > 0)
                atomic_notifier_chain_register(&panic_notifier_list,
                                               &kernel_offset_notifier);
  
 -      /* PPC64 always does a hard irq disable in its panic handler */
 -      if (!IS_ENABLED(CONFIG_PPC64) && !ppc_md.panic)
 -              return;
 -      atomic_notifier_chain_register(&panic_notifier_list, &ppc_panic_block);
 +      /* Low-level platform-specific routines that should run on panic */
 +      if (ppc_md.panic)
 +              atomic_notifier_chain_register(&panic_notifier_list,
 +                                             &ppc_panic_block);
  }
  
  #ifdef CONFIG_CHECK_CACHE_COHERENCY
diff --combined arch/powerpc/xmon/xmon.c
index fff81c2300facc709cc66e774397f1284076d7b4,c916bf250796639c57375fbe8a53090f356e4bd9..3d9782ea3fa7b6af297aa216cd2641f515b6180b
@@@ -31,6 -31,7 +31,6 @@@
  #include <asm/ptrace.h>
  #include <asm/smp.h>
  #include <asm/string.h>
 -#include <asm/prom.h>
  #include <asm/machdep.h>
  #include <asm/xmon.h>
  #include <asm/processor.h>
@@@ -372,7 -373,7 +372,7 @@@ static void write_ciabr(unsigned long c
   * set_ciabr() - set the CIABR
   * @addr:     The value to set.
   *
 - * This function sets the correct privilege value into the the HW
 + * This function sets the correct privilege value into the HW
   * breakpoint address before writing it up in the CIABR register.
   */
  static void set_ciabr(unsigned long addr)
@@@ -920,9 -921,9 +920,9 @@@ static void insert_bpts(void
                        bp->enabled = 0;
                        continue;
                }
 -              if (IS_MTMSRD(instr) || IS_RFID(instr)) {
 -                      printf("Breakpoint at %lx is on an mtmsrd or rfid "
 -                             "instruction, disabling it\n", bp->address);
 +              if (!can_single_step(ppc_inst_val(instr))) {
 +                      printf("Breakpoint at %lx is on an instruction that can't be single stepped, disabling it\n",
 +                                      bp->address);
                        bp->enabled = 0;
                        continue;
                }
@@@ -1242,8 -1243,7 +1242,7 @@@ static void bootcmds(void
        } else if (cmd == 'h') {
                ppc_md.halt();
        } else if (cmd == 'p') {
-               if (pm_power_off)
-                       pm_power_off();
+               do_kernel_power_off();
        }
  }
  
@@@ -1469,8 -1469,9 +1468,8 @@@ static long check_bp_loc(unsigned long 
                printf("Can't read instruction at address %lx\n", addr);
                return 0;
        }
 -      if (IS_MTMSRD(instr) || IS_RFID(instr)) {
 -              printf("Breakpoints may not be placed on mtmsrd or rfid "
 -                     "instructions\n");
 +      if (!can_single_step(ppc_inst_val(instr))) {
 +              printf("Breakpoints may not be placed on instructions that can't be single stepped\n");
                return 0;
        }
        return 1;
@@@ -2022,7 -2023,7 +2021,7 @@@ static void dump_206_sprs(void
        if (!cpu_has_feature(CPU_FTR_ARCH_206))
                return;
  
 -      /* Actually some of these pre-date 2.06, but whatevs */
 +      /* Actually some of these pre-date 2.06, but whatever */
  
        printf("srr0   = %.16lx  srr1  = %.16lx dsisr  = %.8lx\n",
                mfspr(SPRN_SRR0), mfspr(SPRN_SRR1), mfspr(SPRN_DSISR));
diff --combined drivers/memory/emif.c
index 6c2a421b86e3349830f404bddce0438e115decba,99d2df34e584ef6c4b64c9e9c829ccaa5756aef6..f305643209f03bc8dd58dde7ea3b722fa2c46dc6
@@@ -630,7 -630,7 +630,7 @@@ static irqreturn_t emif_threaded_isr(in
                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 (pm_power_off) {
+               if (kernel_can_power_off()) {
                        kernel_power_off();
                } else {
                        WARN(1, "FIXME: NO pm_power_off!!! trying restart\n");
@@@ -1025,8 -1025,10 +1025,8 @@@ static struct emif_data *__init_or_modu
        temp    = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
        dev_info = devm_kzalloc(dev, sizeof(*dev_info), GFP_KERNEL);
  
 -      if (!emif || !temp || !dev_info) {
 -              dev_err(dev, "%s:%d: allocation error\n", __func__, __LINE__);
 +      if (!emif || !temp || !dev_info)
                goto error;
 -      }
  
        memcpy(temp, pd, sizeof(*pd));
        pd = temp;
                temp = devm_kzalloc(dev, sizeof(*cust_cfgs), GFP_KERNEL);
                if (temp)
                        memcpy(temp, cust_cfgs, sizeof(*cust_cfgs));
 -              else
 -                      dev_warn(dev, "%s:%d: allocation error\n", __func__,
 -                              __LINE__);
                pd->custom_configs = temp;
        }
  
                        memcpy(temp, pd->timings, size);
                        pd->timings = temp;
                } else {
 -                      dev_warn(dev, "%s:%d: allocation error\n", __func__,
 -                              __LINE__);
                        get_default_timings(emif);
                }
        } else {
                        memcpy(temp, pd->min_tck, sizeof(*pd->min_tck));
                        pd->min_tck = temp;
                } else {
 -                      dev_warn(dev, "%s:%d: allocation error\n", __func__,
 -                              __LINE__);
                        pd->min_tck = &lpddr2_jedec_min_tck;
                }
        } else {
@@@ -1107,6 -1116,7 +1107,6 @@@ error
  static int __init_or_module emif_probe(struct platform_device *pdev)
  {
        struct emif_data        *emif;
 -      struct resource         *res;
        int                     irq, ret;
  
        if (pdev->dev.of_node)
        emif->dev = &pdev->dev;
        platform_set_drvdata(pdev, emif);
  
 -      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 -      emif->base = devm_ioremap_resource(emif->dev, res);
 +      emif->base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(emif->base))
                goto error;
  
diff --combined drivers/soc/tegra/pmc.c
index c77ecf61818bae1849789eb24869e7870fd4d5b3,9ddc7eac351ebaef84108084ddfed5497fd1c52f..5611d14d3ba2adbaa9eb17aaed30855fdce52a8a
@@@ -39,6 -39,7 +39,7 @@@
  #include <linux/platform_device.h>
  #include <linux/pm_domain.h>
  #include <linux/pm_opp.h>
+ #include <linux/power_supply.h>
  #include <linux/reboot.h>
  #include <linux/regmap.h>
  #include <linux/reset.h>
  #define PMC_USB_DEBOUNCE_DEL          0xec
  #define PMC_USB_AO                    0xf0
  
+ #define PMC_SCRATCH37                 0x130
  #define PMC_SCRATCH41                 0x140
  
  #define PMC_WAKE2_MASK                        0x160
@@@ -394,8 -396,6 +396,8 @@@ struct tegra_pmc_soc 
   * @domain: IRQ domain provided by the PMC
   * @irq: chip implementation for the IRQ domain
   * @clk_nb: pclk clock changes handler
 + * @core_domain_state_synced: flag marking the core domain's state as synced
 + * @core_domain_registered: flag marking the core domain as registered
   */
  struct tegra_pmc {
        struct device *dev;
@@@ -1101,8 -1101,7 +1103,7 @@@ static struct notifier_block tegra_pmc_
        .notifier_call = tegra_pmc_reboot_notify,
  };
  
- static int tegra_pmc_restart_notify(struct notifier_block *this,
-                                   unsigned long action, void *data)
+ static void tegra_pmc_restart(void)
  {
        u32 value;
  
        value = tegra_pmc_readl(pmc, PMC_CNTRL);
        value |= PMC_CNTRL_MAIN_RST;
        tegra_pmc_writel(pmc, value, PMC_CNTRL);
+ }
+ static int tegra_pmc_restart_handler(struct sys_off_data *data)
+ {
+       tegra_pmc_restart();
  
        return NOTIFY_DONE;
  }
  
- static struct notifier_block tegra_pmc_restart_handler = {
-       .notifier_call = tegra_pmc_restart_notify,
-       .priority = 128,
- };
+ static int tegra_pmc_power_off_handler(struct sys_off_data *data)
+ {
+       /*
+        * 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)
  {
@@@ -2879,6 -2895,42 +2897,42 @@@ static int tegra_pmc_probe(struct platf
                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
         * causes lockup if CPU enters LP2 idle state from some other
                        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);
        if (err)
-               goto cleanup_restart_handler;
+               goto cleanup_debugfs;
  
        err = tegra_pmc_regmap_init(pmc);
        if (err < 0)
-               goto cleanup_restart_handler;
+               goto cleanup_debugfs;
  
        err = tegra_powergate_init(pmc, pdev->dev.of_node);
        if (err < 0)
  
  cleanup_powergates:
        tegra_powergate_remove_all(pdev->dev.of_node);
- cleanup_restart_handler:
-       unregister_restart_handler(&tegra_pmc_restart_handler);
  cleanup_debugfs:
        debugfs_remove(pmc->debugfs);
  cleanup_sysfs:
@@@ -3768,7 -3803,7 +3805,7 @@@ static const struct tegra_pmc_regs tegr
  };
  
  static const char * const tegra234_reset_sources[] = {
 -      "SYS_RESET_N",
 +      "SYS_RESET_N",  /* 0x0 */
        "AOWDT",
        "BCCPLEXWDT",
        "BPMPWDT",
        "SPEWDT",
        "APEWDT",
        "LCCPLEXWDT",
 -      "SENSOR",
 -      "AOTAG",
 -      "VFSENSOR",
 +      "SENSOR",       /* 0x8 */
 +      NULL,
 +      NULL,
        "MAINSWRST",
        "SC7",
        "HSM",
 -      "CSITE",
 +      NULL,
        "RCEWDT",
 -      "PVA0WDT",
 -      "PVA1WDT",
 -      "L1A_ASYNC",
 +      NULL,           /* 0x10 */
 +      NULL,
 +      NULL,
        "BPMPBOOT",
        "FUSECRC",
 +      "DCEWDT",
 +      "PSCWDT",
 +      "PSC",
 +      "CSITE_SW",     /* 0x18 */
 +      "POD",
 +      "SCPM",
 +      "VREFRO_POWERBAD",
 +      "VMON",
 +      "FMON",
 +      "FSI_R5WDT",
 +      "FSI_THERM",
 +      "FSI_R52C0WDT", /* 0x20 */
 +      "FSI_R52C1WDT",
 +      "FSI_R52C2WDT",
 +      "FSI_R52C3WDT",
 +      "FSI_FMON",
 +      "FSI_VMON",     /* 0x25 */
  };
  
  static const struct tegra_wake_event tegra234_wake_events[] = {
diff --combined include/linux/pm.h
index 70ec69d8bafd1859e50418eb00fdd276ec7510a7,6cdf279c7f2fd67241895245129375a540cac6ef..871c9c49ec9d2b08a7cfbeb76be43906719bfbb2
@@@ -21,7 -21,6 +21,6 @@@
   * Callbacks for platform drivers to implement.
   */
  extern void (*pm_power_off)(void);
- extern void (*pm_power_off_prepare)(void);
  
  struct device; /* we have a circular dep with device.h */
  #ifdef CONFIG_VT_CONSOLE_SLEEP
@@@ -36,15 -35,6 +35,15 @@@ static inline void pm_vt_switch_unregis
  }
  #endif /* CONFIG_VT_CONSOLE_SLEEP */
  
 +#ifdef CONFIG_CXL_SUSPEND
 +bool cxl_mem_active(void);
 +#else
 +static inline bool cxl_mem_active(void)
 +{
 +      return false;
 +}
 +#endif
 +
  /*
   * Device power management
   */
diff --combined include/linux/reboot.h
index a2429648d8315dcbf8e1c52ccb4f867d68e7e49d,d29d37a2abb610c844deda222046a885d260b58c..e5d9ef886179c0924a12f98ca8e378a1462e3606
@@@ -7,6 -7,7 +7,7 @@@
  #include <uapi/linux/reboot.h>
  
  struct device;
+ struct sys_off_handler;
  
  #define SYS_DOWN      0x0001  /* Notify of system down */
  #define SYS_RESTART   SYS_DOWN
@@@ -62,6 -63,95 +63,95 @@@ extern void machine_shutdown(void)
  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.
   */
@@@ -70,9 -160,14 +160,10 @@@ extern void kernel_restart_prepare(cha
  extern void kernel_restart(char *cmd);
  extern void kernel_halt(void);
  extern void kernel_power_off(void);
+ extern bool kernel_can_power_off(void);
  
 -extern int C_A_D; /* for sysctl */
  void ctrl_alt_del(void);
  
 -#define POWEROFF_CMD_PATH_LEN 256
 -extern char poweroff_cmd[POWEROFF_CMD_PATH_LEN];
 -
  extern void orderly_poweroff(bool force);
  extern void orderly_reboot(void);
  void hw_protection_shutdown(const char *reason, int ms_until_forced);
diff --combined kernel/reboot.c
index 44228a93742b727ceae4fb121a8002fd4232b56d,d4aa372c0cdcd11cf50520cc7fd99b7aad68ad3a..a091145ee7104bb2750f69f0885554c393c7bb62
@@@ -23,7 -23,7 +23,7 @@@
   * this indicates whether you can reboot with ctrl-alt-del: the default is yes
   */
  
 -int C_A_D = 1;
 +static int C_A_D = 1;
  struct pid *cad_pid;
  EXPORT_SYMBOL(cad_pid);
  
@@@ -48,12 -48,20 +48,20 @@@ int reboot_cpu
  enum reboot_type reboot_type = BOOT_ACPI;
  int reboot_force;
  
+ struct sys_off_handler {
+       struct notifier_block nb;
+       int (*sys_off_cb)(struct sys_off_data *data);
+       void *cb_data;
+       enum sys_off_mode mode;
+       bool blocking;
+       void *list;
+ };
  /*
-  * If set, this is used for preparing the system to power off.
+  * Temporary stub that prevents linkage failure while we're in process
+  * of removing all uses of legacy pm_power_off() around the kernel.
   */
- void (*pm_power_off_prepare)(void);
- EXPORT_SYMBOL_GPL(pm_power_off_prepare);
+ void __weak (*pm_power_off)(void);
  
  /**
   *    emergency_restart - reboot the system
@@@ -281,6 -289,316 +289,316 @@@ void kernel_halt(void
  }
  EXPORT_SYMBOL_GPL(kernel_halt);
  
+ /*
+  *    Notifier list for kernel code which wants to be called
+  *    to prepare system for power off.
+  */
+ static BLOCKING_NOTIFIER_HEAD(power_off_prep_handler_list);
+ /*
+  *    Notifier list for kernel code which wants to be called
+  *    to power off system.
+  */
+ static ATOMIC_NOTIFIER_HEAD(power_off_handler_list);
+ static int sys_off_notify(struct notifier_block *nb,
+                         unsigned long mode, void *cmd)
+ {
+       struct sys_off_handler *handler;
+       struct sys_off_data data = {};
+       handler = container_of(nb, struct sys_off_handler, nb);
+       data.cb_data = handler->cb_data;
+       data.mode = mode;
+       data.cmd = cmd;
+       return handler->sys_off_cb(&data);
+ }
+ /**
+  *    register_sys_off_handler - Register sys-off handler
+  *    @mode: Sys-off mode
+  *    @priority: Handler priority
+  *    @callback: Callback function
+  *    @cb_data: Callback argument
+  *
+  *    Registers system power-off or restart handler that will be invoked
+  *    at the step corresponding to the given sys-off mode. Handler's callback
+  *    should return NOTIFY_DONE to permit execution of the next handler in
+  *    the call chain or NOTIFY_STOP to break the chain (in error case for
+  *    example).
+  *
+  *    Multiple handlers can be registered at the default priority level.
+  *
+  *    Only one handler can be registered at the non-default priority level,
+  *    otherwise ERR_PTR(-EBUSY) is returned.
+  *
+  *    Returns a new instance of struct sys_off_handler on success, or
+  *    an ERR_PTR()-encoded error code otherwise.
+  */
+ 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)
+ {
+       struct sys_off_handler *handler;
+       int err;
+       handler = kzalloc(sizeof(*handler), GFP_KERNEL);
+       if (!handler)
+               return ERR_PTR(-ENOMEM);
+       switch (mode) {
+       case SYS_OFF_MODE_POWER_OFF_PREPARE:
+               handler->list = &power_off_prep_handler_list;
+               handler->blocking = true;
+               break;
+       case SYS_OFF_MODE_POWER_OFF:
+               handler->list = &power_off_handler_list;
+               break;
+       case SYS_OFF_MODE_RESTART:
+               handler->list = &restart_handler_list;
+               break;
+       default:
+               kfree(handler);
+               return ERR_PTR(-EINVAL);
+       }
+       handler->nb.notifier_call = sys_off_notify;
+       handler->nb.priority = priority;
+       handler->sys_off_cb = callback;
+       handler->cb_data = cb_data;
+       handler->mode = mode;
+       if (handler->blocking) {
+               if (priority == SYS_OFF_PRIO_DEFAULT)
+                       err = blocking_notifier_chain_register(handler->list,
+                                                              &handler->nb);
+               else
+                       err = blocking_notifier_chain_register_unique_prio(handler->list,
+                                                                          &handler->nb);
+       } else {
+               if (priority == SYS_OFF_PRIO_DEFAULT)
+                       err = atomic_notifier_chain_register(handler->list,
+                                                            &handler->nb);
+               else
+                       err = atomic_notifier_chain_register_unique_prio(handler->list,
+                                                                        &handler->nb);
+       }
+       if (err) {
+               kfree(handler);
+               return ERR_PTR(err);
+       }
+       return handler;
+ }
+ EXPORT_SYMBOL_GPL(register_sys_off_handler);
+ /**
+  *    unregister_sys_off_handler - Unregister sys-off handler
+  *    @handler: Sys-off handler
+  *
+  *    Unregisters given sys-off handler.
+  */
+ void unregister_sys_off_handler(struct sys_off_handler *handler)
+ {
+       int err;
+       if (!handler)
+               return;
+       if (handler->blocking)
+               err = blocking_notifier_chain_unregister(handler->list,
+                                                        &handler->nb);
+       else
+               err = atomic_notifier_chain_unregister(handler->list,
+                                                      &handler->nb);
+       /* sanity check, shall never happen */
+       WARN_ON(err);
+       kfree(handler);
+ }
+ EXPORT_SYMBOL_GPL(unregister_sys_off_handler);
+ static void devm_unregister_sys_off_handler(void *data)
+ {
+       struct sys_off_handler *handler = data;
+       unregister_sys_off_handler(handler);
+ }
+ /**
+  *    devm_register_sys_off_handler - Register sys-off handler
+  *    @dev: Device that registers handler
+  *    @mode: Sys-off mode
+  *    @priority: Handler priority
+  *    @callback: Callback function
+  *    @cb_data: Callback argument
+  *
+  *    Registers resource-managed sys-off handler.
+  *
+  *    Returns zero on success, or error code on failure.
+  */
+ 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)
+ {
+       struct sys_off_handler *handler;
+       handler = register_sys_off_handler(mode, priority, callback, cb_data);
+       if (IS_ERR(handler))
+               return PTR_ERR(handler);
+       return devm_add_action_or_reset(dev, devm_unregister_sys_off_handler,
+                                       handler);
+ }
+ EXPORT_SYMBOL_GPL(devm_register_sys_off_handler);
+ /**
+  *    devm_register_power_off_handler - Register power-off handler
+  *    @dev: Device that registers callback
+  *    @callback: Callback function
+  *    @cb_data: Callback's argument
+  *
+  *    Registers resource-managed sys-off handler with a default priority
+  *    and using power-off mode.
+  *
+  *    Returns zero on success, or error code on failure.
+  */
+ int devm_register_power_off_handler(struct device *dev,
+                                   int (*callback)(struct sys_off_data *data),
+                                   void *cb_data)
+ {
+       return devm_register_sys_off_handler(dev,
+                                            SYS_OFF_MODE_POWER_OFF,
+                                            SYS_OFF_PRIO_DEFAULT,
+                                            callback, cb_data);
+ }
+ EXPORT_SYMBOL_GPL(devm_register_power_off_handler);
+ /**
+  *    devm_register_restart_handler - Register restart handler
+  *    @dev: Device that registers callback
+  *    @callback: Callback function
+  *    @cb_data: Callback's argument
+  *
+  *    Registers resource-managed sys-off handler with a default priority
+  *    and using restart mode.
+  *
+  *    Returns zero on success, or error code on failure.
+  */
+ int devm_register_restart_handler(struct device *dev,
+                                 int (*callback)(struct sys_off_data *data),
+                                 void *cb_data)
+ {
+       return devm_register_sys_off_handler(dev,
+                                            SYS_OFF_MODE_RESTART,
+                                            SYS_OFF_PRIO_DEFAULT,
+                                            callback, cb_data);
+ }
+ EXPORT_SYMBOL_GPL(devm_register_restart_handler);
+ static struct sys_off_handler *platform_power_off_handler;
+ static int platform_power_off_notify(struct sys_off_data *data)
+ {
+       void (*platform_power_power_off_cb)(void) = data->cb_data;
+       platform_power_power_off_cb();
+       return NOTIFY_DONE;
+ }
+ /**
+  *    register_platform_power_off - Register platform-level power-off callback
+  *    @power_off: Power-off callback
+  *
+  *    Registers power-off callback that will be called as last step
+  *    of the power-off sequence. This callback is expected to be invoked
+  *    for the last resort. Only one platform power-off callback is allowed
+  *    to be registered at a time.
+  *
+  *    Returns zero on success, or error code on failure.
+  */
+ int register_platform_power_off(void (*power_off)(void))
+ {
+       struct sys_off_handler *handler;
+       handler = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
+                                          SYS_OFF_PRIO_PLATFORM,
+                                          platform_power_off_notify,
+                                          power_off);
+       if (IS_ERR(handler))
+               return PTR_ERR(handler);
+       platform_power_off_handler = handler;
+       return 0;
+ }
+ EXPORT_SYMBOL_GPL(register_platform_power_off);
+ /**
+  *    unregister_platform_power_off - Unregister platform-level power-off callback
+  *    @power_off: Power-off callback
+  *
+  *    Unregisters previously registered platform power-off callback.
+  */
+ void unregister_platform_power_off(void (*power_off)(void))
+ {
+       if (platform_power_off_handler &&
+           platform_power_off_handler->cb_data == power_off) {
+               unregister_sys_off_handler(platform_power_off_handler);
+               platform_power_off_handler = NULL;
+       }
+ }
+ EXPORT_SYMBOL_GPL(unregister_platform_power_off);
+ static int legacy_pm_power_off(struct sys_off_data *data)
+ {
+       if (pm_power_off)
+               pm_power_off();
+       return NOTIFY_DONE;
+ }
+ static void do_kernel_power_off_prepare(void)
+ {
+       blocking_notifier_call_chain(&power_off_prep_handler_list, 0, NULL);
+ }
+ /**
+  *    do_kernel_power_off - Execute kernel power-off handler call chain
+  *
+  *    Expected to be called as last step of the power-off sequence.
+  *
+  *    Powers off the system immediately if a power-off handler function has
+  *    been registered. Otherwise does nothing.
+  */
+ void do_kernel_power_off(void)
+ {
+       atomic_notifier_call_chain(&power_off_handler_list, 0, NULL);
+ }
+ /**
+  *    kernel_can_power_off - check whether system can be powered off
+  *
+  *    Returns true if power-off handler is registered and system can be
+  *    powered off, false otherwise.
+  */
+ bool kernel_can_power_off(void)
+ {
+       return !atomic_notifier_call_chain_is_empty(&power_off_handler_list);
+ }
+ EXPORT_SYMBOL_GPL(kernel_can_power_off);
  /**
   *    kernel_power_off - power_off the system
   *
  void kernel_power_off(void)
  {
        kernel_shutdown_prepare(SYSTEM_POWER_OFF);
-       if (pm_power_off_prepare)
-               pm_power_off_prepare();
+       do_kernel_power_off_prepare();
        migrate_to_reboot_cpu();
        syscore_shutdown();
        pr_emerg("Power down\n");
@@@ -313,6 -630,7 +630,7 @@@ SYSCALL_DEFINE4(reboot, int, magic1, in
                void __user *, arg)
  {
        struct pid_namespace *pid_ns = task_active_pid_ns(current);
+       struct sys_off_handler *sys_off = NULL;
        char buffer[256];
        int ret = 0;
  
        if (ret)
                return ret;
  
+       /*
+        * Register sys-off handlers for legacy PM callback. This allows
+        * legacy PM callbacks temporary co-exist with the new sys-off API.
+        *
+        * TODO: Remove legacy handlers once all legacy PM users will be
+        *       switched to the sys-off based APIs.
+        */
+       if (pm_power_off) {
+               sys_off = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF,
+                                                  SYS_OFF_PRIO_DEFAULT,
+                                                  legacy_pm_power_off, NULL);
+               if (IS_ERR(sys_off))
+                       return PTR_ERR(sys_off);
+       }
        /* Instead of trying to make the power_off code look like
         * halt when pm_power_off is not set do it the easy way.
         */
-       if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
+       if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !kernel_can_power_off())
                cmd = LINUX_REBOOT_CMD_HALT;
  
        mutex_lock(&system_transition_mutex);
                break;
        }
        mutex_unlock(&system_transition_mutex);
+       unregister_sys_off_handler(sys_off);
        return ret;
  }
  
@@@ -417,8 -751,7 +751,8 @@@ void ctrl_alt_del(void
                kill_cad_pid(SIGINT, 1);
  }
  
 -char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
 +#define POWEROFF_CMD_PATH_LEN  256
 +static char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
  static const char reboot_cmd[] = "/sbin/reboot";
  
  static int run_cmd(const char *cmd)
@@@ -448,11 -781,9 +782,11 @@@ static int __orderly_reboot(void
        ret = run_cmd(reboot_cmd);
  
        if (ret) {
 +              printk_prefer_direct_enter();
                pr_warn("Failed to start orderly reboot: forcing the issue\n");
                emergency_sync();
                kernel_restart(NULL);
 +              printk_prefer_direct_exit();
        }
  
        return ret;
@@@ -465,7 -796,6 +799,7 @@@ static int __orderly_poweroff(bool forc
        ret = run_cmd(poweroff_cmd);
  
        if (ret && force) {
 +              printk_prefer_direct_enter();
                pr_warn("Failed to start orderly shutdown: forcing the issue\n");
  
                /*
                 */
                emergency_sync();
                kernel_power_off();
 +              printk_prefer_direct_exit();
        }
  
        return ret;
@@@ -533,8 -862,6 +867,8 @@@ EXPORT_SYMBOL_GPL(orderly_reboot)
   */
  static void hw_failure_emergency_poweroff_func(struct work_struct *work)
  {
 +      printk_prefer_direct_enter();
 +
        /*
         * We have reached here after the emergency shutdown waiting period has
         * expired. This means orderly_poweroff has not been able to shut off
         */
        pr_emerg("Hardware protection shutdown failed. Trying emergency restart\n");
        emergency_restart();
 +
 +      printk_prefer_direct_exit();
  }
  
  static DECLARE_DELAYED_WORK(hw_failure_emergency_poweroff_work,
@@@ -591,13 -916,11 +925,13 @@@ void hw_protection_shutdown(const char 
  {
        static atomic_t allow_proceed = ATOMIC_INIT(1);
  
 +      printk_prefer_direct_enter();
 +
        pr_emerg("HARDWARE PROTECTION shutdown (%s)\n", reason);
  
        /* Shutdown should be initiated only once. */
        if (!atomic_dec_and_test(&allow_proceed))
 -              return;
 +              goto out;
  
        /*
         * Queue a backup emergency shutdown in the event of
         */
        hw_failure_emergency_poweroff(ms_until_forced);
        orderly_poweroff(true);
 +out:
 +      printk_prefer_direct_exit();
  }
  EXPORT_SYMBOL_GPL(hw_protection_shutdown);
  
@@@ -880,33 -1201,6 +1214,33 @@@ static struct attribute *reboot_attrs[
        NULL,
  };
  
 +#ifdef CONFIG_SYSCTL
 +static struct ctl_table kern_reboot_table[] = {
 +      {
 +              .procname       = "poweroff_cmd",
 +              .data           = &poweroff_cmd,
 +              .maxlen         = POWEROFF_CMD_PATH_LEN,
 +              .mode           = 0644,
 +              .proc_handler   = proc_dostring,
 +      },
 +      {
 +              .procname       = "ctrl-alt-del",
 +              .data           = &C_A_D,
 +              .maxlen         = sizeof(int),
 +              .mode           = 0644,
 +              .proc_handler   = proc_dointvec,
 +      },
 +      { }
 +};
 +
 +static void __init kernel_reboot_sysctls_init(void)
 +{
 +      register_sysctl_init("kernel", kern_reboot_table);
 +}
 +#else
 +#define kernel_reboot_sysctls_init() do { } while (0)
 +#endif /* CONFIG_SYSCTL */
 +
  static const struct attribute_group reboot_attr_group = {
        .attrs = reboot_attrs,
  };
@@@ -926,8 -1220,6 +1260,8 @@@ static int __init reboot_ksysfs_init(vo
                return ret;
        }
  
 +      kernel_reboot_sysctls_init();
 +
        return 0;
  }
  late_initcall(reboot_ksysfs_init);
This page took 0.131953 seconds and 4 git commands to generate.