linux/arch/arm/kernel/suspend.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/init.h>
   3#include <linux/slab.h>
   4#include <linux/mm_types.h>
   5
   6#include <asm/bugs.h>
   7#include <asm/cacheflush.h>
   8#include <asm/idmap.h>
   9#include <asm/pgalloc.h>
  10#include <asm/pgtable.h>
  11#include <asm/memory.h>
  12#include <asm/smp_plat.h>
  13#include <asm/suspend.h>
  14#include <asm/tlbflush.h>
  15
  16extern int __cpu_suspend(unsigned long, int (*)(unsigned long), u32 cpuid);
  17extern void cpu_resume_mmu(void);
  18
  19#ifdef CONFIG_MMU
  20int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
  21{
  22        struct mm_struct *mm = current->active_mm;
  23        u32 __mpidr = cpu_logical_map(smp_processor_id());
  24        int ret;
  25
  26        if (!idmap_pgd)
  27                return -EINVAL;
  28
  29        /*
  30         * Provide a temporary page table with an identity mapping for
  31         * the MMU-enable code, required for resuming.  On successful
  32         * resume (indicated by a zero return code), we need to switch
  33         * back to the correct page tables.
  34         */
  35        ret = __cpu_suspend(arg, fn, __mpidr);
  36        if (ret == 0) {
  37                cpu_switch_mm(mm->pgd, mm);
  38                local_flush_bp_all();
  39                local_flush_tlb_all();
  40                check_other_bugs();
  41        }
  42
  43        return ret;
  44}
  45#else
  46int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
  47{
  48        u32 __mpidr = cpu_logical_map(smp_processor_id());
  49        return __cpu_suspend(arg, fn, __mpidr);
  50}
  51#define idmap_pgd       NULL
  52#endif
  53
  54/*
  55 * This is called by __cpu_suspend() to save the state, and do whatever
  56 * flushing is required to ensure that when the CPU goes to sleep we have
  57 * the necessary data available when the caches are not searched.
  58 */
  59void __cpu_suspend_save(u32 *ptr, u32 ptrsz, u32 sp, u32 *save_ptr)
  60{
  61        u32 *ctx = ptr;
  62
  63        *save_ptr = virt_to_phys(ptr);
  64
  65        /* This must correspond to the LDM in cpu_resume() assembly */
  66        *ptr++ = virt_to_phys(idmap_pgd);
  67        *ptr++ = sp;
  68        *ptr++ = virt_to_phys(cpu_do_resume);
  69
  70        cpu_do_suspend(ptr);
  71
  72        flush_cache_louis();
  73
  74        /*
  75         * flush_cache_louis does not guarantee that
  76         * save_ptr and ptr are cleaned to main memory,
  77         * just up to the Level of Unification Inner Shareable.
  78         * Since the context pointer and context itself
  79         * are to be retrieved with the MMU off that
  80         * data must be cleaned from all cache levels
  81         * to main memory using "area" cache primitives.
  82        */
  83        __cpuc_flush_dcache_area(ctx, ptrsz);
  84        __cpuc_flush_dcache_area(save_ptr, sizeof(*save_ptr));
  85
  86        outer_clean_range(*save_ptr, *save_ptr + ptrsz);
  87        outer_clean_range(virt_to_phys(save_ptr),
  88                          virt_to_phys(save_ptr) + sizeof(*save_ptr));
  89}
  90
  91extern struct sleep_save_sp sleep_save_sp;
  92
  93static int cpu_suspend_alloc_sp(void)
  94{
  95        void *ctx_ptr;
  96        /* ctx_ptr is an array of physical addresses */
  97        ctx_ptr = kcalloc(mpidr_hash_size(), sizeof(u32), GFP_KERNEL);
  98
  99        if (WARN_ON(!ctx_ptr))
 100                return -ENOMEM;
 101        sleep_save_sp.save_ptr_stash = ctx_ptr;
 102        sleep_save_sp.save_ptr_stash_phys = virt_to_phys(ctx_ptr);
 103        sync_cache_w(&sleep_save_sp);
 104        return 0;
 105}
 106early_initcall(cpu_suspend_alloc_sp);
 107