linux/arch/arm64/kernel/cpuidle.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * ARM64 CPU idle arch support
   4 *
   5 * Copyright (C) 2014 ARM Ltd.
   6 * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
   7 */
   8
   9#include <linux/acpi.h>
  10#include <linux/cpuidle.h>
  11#include <linux/cpu_pm.h>
  12#include <linux/of.h>
  13#include <linux/of_device.h>
  14#include <linux/psci.h>
  15
  16#include <asm/cpuidle.h>
  17#include <asm/cpu_ops.h>
  18
  19int arm_cpuidle_init(unsigned int cpu)
  20{
  21        const struct cpu_operations *ops = get_cpu_ops(cpu);
  22        int ret = -EOPNOTSUPP;
  23
  24        if (ops && ops->cpu_suspend && ops->cpu_init_idle)
  25                ret = ops->cpu_init_idle(cpu);
  26
  27        return ret;
  28}
  29
  30/**
  31 * arm_cpuidle_suspend() - function to enter a low-power idle state
  32 * @index: argument to pass to CPU suspend operations
  33 *
  34 * Return: 0 on success, -EOPNOTSUPP if CPU suspend hook not initialized, CPU
  35 * operations back-end error code otherwise.
  36 */
  37int arm_cpuidle_suspend(int index)
  38{
  39        int cpu = smp_processor_id();
  40        const struct cpu_operations *ops = get_cpu_ops(cpu);
  41
  42        return ops->cpu_suspend(index);
  43}
  44
  45#ifdef CONFIG_ACPI
  46
  47#include <acpi/processor.h>
  48
  49#define ARM64_LPI_IS_RETENTION_STATE(arch_flags) (!(arch_flags))
  50
  51static int psci_acpi_cpu_init_idle(unsigned int cpu)
  52{
  53        int i, count;
  54        struct acpi_lpi_state *lpi;
  55        struct acpi_processor *pr = per_cpu(processors, cpu);
  56
  57        /*
  58         * If the PSCI cpu_suspend function hook has not been initialized
  59         * idle states must not be enabled, so bail out
  60         */
  61        if (!psci_ops.cpu_suspend)
  62                return -EOPNOTSUPP;
  63
  64        if (unlikely(!pr || !pr->flags.has_lpi))
  65                return -EINVAL;
  66
  67        count = pr->power.count - 1;
  68        if (count <= 0)
  69                return -ENODEV;
  70
  71        for (i = 0; i < count; i++) {
  72                u32 state;
  73
  74                lpi = &pr->power.lpi_states[i + 1];
  75                /*
  76                 * Only bits[31:0] represent a PSCI power_state while
  77                 * bits[63:32] must be 0x0 as per ARM ACPI FFH Specification
  78                 */
  79                state = lpi->address;
  80                if (!psci_power_state_is_valid(state)) {
  81                        pr_warn("Invalid PSCI power state %#x\n", state);
  82                        return -EINVAL;
  83                }
  84        }
  85
  86        return 0;
  87}
  88
  89int acpi_processor_ffh_lpi_probe(unsigned int cpu)
  90{
  91        return psci_acpi_cpu_init_idle(cpu);
  92}
  93
  94int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi)
  95{
  96        u32 state = lpi->address;
  97
  98        if (ARM64_LPI_IS_RETENTION_STATE(lpi->arch_flags))
  99                return CPU_PM_CPU_IDLE_ENTER_RETENTION_PARAM(psci_cpu_suspend_enter,
 100                                                lpi->index, state);
 101        else
 102                return CPU_PM_CPU_IDLE_ENTER_PARAM(psci_cpu_suspend_enter,
 103                                             lpi->index, state);
 104}
 105#endif
 106