mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-07 20:33:58 -04:00
Merge "drivers: soc: qcom: pm: Add legacy PM and Warmboot set address API support"
This commit is contained in:
commit
d7e2efd740
11 changed files with 3090 additions and 16 deletions
|
|
@ -19,13 +19,13 @@
|
|||
#include <linux/pm_qos.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/remote_spinlock.h>
|
||||
#include <linux/msm_remote_spinlock.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/coresight-cti.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/cpu_pm.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <soc/qcom/spm.h>
|
||||
#include <soc/qcom/pm-legacy.h>
|
||||
#include <soc/qcom/rpm-notifier.h>
|
||||
|
|
@ -48,10 +48,22 @@
|
|||
#include <soc/qcom/minidump.h>
|
||||
|
||||
#define SCLK_HZ (32768)
|
||||
#define SCM_HANDOFF_LOCK_ID "S:7"
|
||||
#define PSCI_POWER_STATE(reset) (reset << 30)
|
||||
#define PSCI_AFFINITY_LEVEL(lvl) ((lvl & 0x3) << 24)
|
||||
static remote_spinlock_t scm_handoff_lock;
|
||||
#define MUTEX_NUM_PID 128
|
||||
#define MUTEX_TID_START MUTEX_NUM_PID
|
||||
#define SCM_HANDOFF_LOCK_ID 7
|
||||
|
||||
/* sfpb implementation for hardware spinlock usage */
|
||||
static phys_addr_t reg_base;
|
||||
static uint32_t reg_size;
|
||||
static uint32_t lock_size;
|
||||
|
||||
static void __iomem *hw_mutex_reg_base;
|
||||
|
||||
struct mutex_reg {
|
||||
uint32_t regaddr;
|
||||
};
|
||||
|
||||
enum {
|
||||
MSM_LPM_LVL_DBG_SUSPEND_LIMITS = BIT(0),
|
||||
|
|
@ -1278,13 +1290,38 @@ static const struct platform_suspend_ops lpm_suspend_ops = {
|
|||
.wake = lpm_suspend_wake,
|
||||
};
|
||||
|
||||
static int init_hw_mutex(struct device_node *node)
|
||||
{
|
||||
struct resource r;
|
||||
int rc;
|
||||
static uint32_t lock_count;
|
||||
|
||||
rc = of_address_to_resource(node, 0, &r);
|
||||
if (rc) {
|
||||
pr_err("Failed to get resource\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(node, "qcom,num-locks", &lock_count);
|
||||
if (rc) {
|
||||
pr_err("Failed to get num-locks property\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
reg_base = r.start;
|
||||
reg_size = (uint32_t)(resource_size(&r));
|
||||
lock_size = reg_size / lock_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lpm_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
int size;
|
||||
struct kobject *module_kobj = NULL;
|
||||
struct md_region md_entry;
|
||||
|
||||
struct device_node *node;
|
||||
get_online_cpus();
|
||||
lpm_root_node = lpm_of_parse_cluster(pdev);
|
||||
|
||||
|
|
@ -1305,14 +1342,6 @@ static int lpm_probe(struct platform_device *pdev)
|
|||
*/
|
||||
suspend_set_ops(&lpm_suspend_ops);
|
||||
hrtimer_init(&lpm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
|
||||
ret = remote_spin_lock_init(&scm_handoff_lock, SCM_HANDOFF_LOCK_ID);
|
||||
if (ret) {
|
||||
pr_err("%s: Failed initializing scm_handoff_lock (%d)\n",
|
||||
__func__, ret);
|
||||
put_online_cpus();
|
||||
return ret;
|
||||
}
|
||||
size = num_dbg_elements * sizeof(struct lpm_debug);
|
||||
lpm_debug = dma_alloc_coherent(&pdev->dev, size,
|
||||
&lpm_debug_phys, GFP_KERNEL);
|
||||
|
|
@ -1354,6 +1383,28 @@ static int lpm_probe(struct platform_device *pdev)
|
|||
if (msm_minidump_add_region(&md_entry))
|
||||
pr_info("Failed to add lpm_debug in Minidump\n");
|
||||
|
||||
node = of_find_node_by_name(NULL, "qcom,ipc-spinlock");
|
||||
if (!node) {
|
||||
pr_err("Failed to find ipc-spinlock node\n");
|
||||
ret = -ENODEV;
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (init_hw_mutex(node)) {
|
||||
ret = -EINVAL;
|
||||
of_node_put(node);
|
||||
goto failed;
|
||||
}
|
||||
|
||||
hw_mutex_reg_base = ioremap(reg_base, reg_size);
|
||||
if (!hw_mutex_reg_base) {
|
||||
pr_err("ioremap failed\n");
|
||||
ret = -ENOMEM;
|
||||
of_node_put(node);
|
||||
goto failed;
|
||||
}
|
||||
of_node_put(node);
|
||||
|
||||
return 0;
|
||||
failed:
|
||||
free_cluster_node(lpm_root_node);
|
||||
|
|
@ -1389,11 +1440,24 @@ fail:
|
|||
}
|
||||
late_initcall(lpm_levels_module_init);
|
||||
|
||||
static void mutex_reg_write(uint32_t tid)
|
||||
{
|
||||
struct mutex_reg *lock;
|
||||
|
||||
lock = hw_mutex_reg_base + (SCM_HANDOFF_LOCK_ID * lock_size);
|
||||
do {
|
||||
writel_relaxed(tid, lock);
|
||||
/* barrier for proper semantics */
|
||||
smp_mb();
|
||||
} while (readl_relaxed(lock) != tid);
|
||||
}
|
||||
|
||||
|
||||
enum msm_pm_l2_scm_flag lpm_cpu_pre_pc_cb(unsigned int cpu)
|
||||
{
|
||||
struct lpm_cluster *cluster = per_cpu(cpu_cluster, cpu);
|
||||
enum msm_pm_l2_scm_flag retflag = MSM_SCM_L2_ON;
|
||||
|
||||
uint32_t tid;
|
||||
/*
|
||||
* No need to acquire the lock if probe isn't completed yet
|
||||
* In the event of the hotplug happening before lpm probe, we want to
|
||||
|
|
@ -1434,8 +1498,8 @@ unlock_and_return:
|
|||
update_debug_pc_event(PRE_PC_CB, retflag, 0xdeadbeef, 0xdeadbeef,
|
||||
0xdeadbeef);
|
||||
trace_pre_pc_cb(retflag);
|
||||
remote_spin_lock_rlock_id(&scm_handoff_lock,
|
||||
REMOTE_SPINLOCK_TID_START + cpu);
|
||||
tid = MUTEX_TID_START + cpu;
|
||||
mutex_reg_write(tid);
|
||||
spin_unlock(&cluster->sync_lock);
|
||||
return retflag;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -509,6 +509,22 @@ config QCOM_SMD_RPM
|
|||
Say M here if you want to include support for the Qualcomm RPM as a
|
||||
module. This will build a module called "qcom-smd-rpm".
|
||||
|
||||
config MSM_SPM
|
||||
bool "Driver support for SPM and AVS wrapper hardware"
|
||||
help
|
||||
Enables the support for SPM and AVS wrapper hardware on MSMs. SPM
|
||||
hardware is used to manage the processor power during sleep. The
|
||||
driver allows configuring SPM to allow different low power modes for
|
||||
both core and L2.
|
||||
|
||||
config MSM_L2_SPM
|
||||
bool "SPM support for L2 cache"
|
||||
help
|
||||
Enable SPM driver support for L2 cache. Some MSM chipsets allow
|
||||
control of L2 cache low power mode with a Subsystem Power manager.
|
||||
Enabling this driver allows configuring L2 SPM for low power modes
|
||||
on supported chipsets.
|
||||
|
||||
config QCOM_MEMORY_DUMP_V2
|
||||
tristate "QCOM Memory Dump V2 Support"
|
||||
help
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ obj-$(CONFIG_QCOM_SOC_SLEEP_STATS) += soc_sleep_stats.o
|
|||
obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o
|
||||
obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o
|
||||
obj-$(CONFIG_QCOM_SMEM) += smem.o
|
||||
obj-$(CONFIG_MSM_PM_LEGACY) += pm-boot.o msm-pm.o
|
||||
obj-$(CONFIG_MSM_SPM) += msm-spm.o spm_devices.o
|
||||
obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
|
||||
obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
|
||||
obj-$(CONFIG_QCOM_SUBSYSTEM_SLEEP_STATS) += subsystem_sleep_stats.o
|
||||
|
|
|
|||
21
drivers/soc/qcom/idle.h
Normal file
21
drivers/soc/qcom/idle.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007-2009,2012-2014, 2018-2019, 2021 The Linux Foundation.
|
||||
*/
|
||||
|
||||
#ifndef _ARCH_ARM_MACH_MSM_IDLE_H_
|
||||
#define _ARCH_ARM_MACH_MSM_IDLE_H_
|
||||
|
||||
#define MAX_CPUS_PER_CLUSTER 4
|
||||
#define MAX_NUM_CLUSTER 4
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#if defined(CONFIG_CPU_V7) || defined(CONFIG_ARM64)
|
||||
extern unsigned long msm_pm_boot_vector[MAX_NUM_CLUSTER * MAX_CPUS_PER_CLUSTER];
|
||||
void msm_pm_boot_entry(void);
|
||||
#else
|
||||
static inline void msm_pm_boot_entry(void) {}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
819
drivers/soc/qcom/msm-pm.c
Normal file
819
drivers/soc/qcom/msm-pm.c
Normal file
|
|
@ -0,0 +1,819 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2010-2019, 2021 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
/* Copyright (c) 2010-2019, 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/debugfs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ktime.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/tick.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <soc/qcom/spm.h>
|
||||
#include <soc/qcom/pm-legacy.h>
|
||||
#include <asm/suspend.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/system_misc.h>
|
||||
#ifdef CONFIG_VFP
|
||||
#include <asm/vfp.h>
|
||||
#endif
|
||||
#include <soc/qcom/jtag.h>
|
||||
#include "pm-boot.h"
|
||||
#include "idle.h"
|
||||
|
||||
#define SCM_CMD_TERMINATE_PC (0x2)
|
||||
#define SCM_CMD_CORE_HOTPLUGGED (0x10)
|
||||
#define SCM_FLUSH_FLAG_MASK (0x3)
|
||||
|
||||
#define SCLK_HZ (32768)
|
||||
|
||||
#define MAX_BUF_SIZE 1024
|
||||
|
||||
static int msm_pm_debug_mask = 1;
|
||||
module_param_named(
|
||||
debug_mask, msm_pm_debug_mask, int, 0664
|
||||
);
|
||||
|
||||
enum {
|
||||
MSM_PM_DEBUG_SUSPEND = BIT(0),
|
||||
MSM_PM_DEBUG_POWER_COLLAPSE = BIT(1),
|
||||
MSM_PM_DEBUG_SUSPEND_LIMITS = BIT(2),
|
||||
MSM_PM_DEBUG_CLOCK = BIT(3),
|
||||
MSM_PM_DEBUG_RESET_VECTOR = BIT(4),
|
||||
MSM_PM_DEBUG_IDLE = BIT(5),
|
||||
MSM_PM_DEBUG_IDLE_LIMITS = BIT(6),
|
||||
MSM_PM_DEBUG_HOTPLUG = BIT(7),
|
||||
};
|
||||
|
||||
enum msm_pc_count_offsets {
|
||||
MSM_PC_ENTRY_COUNTER,
|
||||
MSM_PC_EXIT_COUNTER,
|
||||
MSM_PC_FALLTHRU_COUNTER,
|
||||
MSM_PC_UNUSED,
|
||||
MSM_PC_NUM_COUNTERS,
|
||||
};
|
||||
|
||||
static bool msm_pm_ldo_retention_enabled = true;
|
||||
static bool msm_pm_tz_flushes_cache;
|
||||
static bool msm_pm_ret_no_pll_switch;
|
||||
static bool msm_no_ramp_down_pc;
|
||||
static struct msm_pm_sleep_status_data *msm_pm_slp_sts;
|
||||
static DEFINE_PER_CPU(struct clk *, cpu_clks);
|
||||
static struct clk *l2_clk;
|
||||
|
||||
static long *msm_pc_debug_counters;
|
||||
|
||||
static cpumask_t retention_cpus;
|
||||
static DEFINE_SPINLOCK(retention_lock);
|
||||
static DEFINE_MUTEX(msm_pc_debug_mutex);
|
||||
|
||||
static bool msm_pm_is_L1_writeback(void)
|
||||
{
|
||||
u32 cache_id = 0;
|
||||
|
||||
#if defined(CONFIG_CPU_V7)
|
||||
u32 sel = 0;
|
||||
|
||||
asm volatile ("mcr p15, 2, %[ccselr], c0, c0, 0\n\t"
|
||||
"isb\n\t"
|
||||
"mrc p15, 1, %[ccsidr], c0, c0, 0\n\t"
|
||||
: [ccsidr]"=r" (cache_id)
|
||||
: [ccselr]"r" (sel)
|
||||
);
|
||||
return cache_id & BIT(30);
|
||||
#elif defined(CONFIG_ARM64)
|
||||
u32 sel = 0;
|
||||
|
||||
asm volatile("msr csselr_el1, %[ccselr]\n\t"
|
||||
"isb\n\t"
|
||||
"mrs %[ccsidr],ccsidr_el1\n\t"
|
||||
: [ccsidr]"=r" (cache_id)
|
||||
: [ccselr]"r" (sel)
|
||||
);
|
||||
return cache_id & BIT(30);
|
||||
#else
|
||||
#error No valid CPU arch selected
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool msm_pm_swfi(bool from_idle)
|
||||
{
|
||||
msm_arch_idle();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool msm_pm_retention(bool from_idle)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int cpu = smp_processor_id();
|
||||
struct clk *cpu_clk = per_cpu(cpu_clks, cpu);
|
||||
|
||||
spin_lock(&retention_lock);
|
||||
|
||||
if (!msm_pm_ldo_retention_enabled)
|
||||
goto bailout;
|
||||
|
||||
cpumask_set_cpu(cpu, &retention_cpus);
|
||||
spin_unlock(&retention_lock);
|
||||
|
||||
if (!msm_pm_ret_no_pll_switch)
|
||||
clk_disable(cpu_clk);
|
||||
|
||||
ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_RETENTION, false);
|
||||
WARN_ON(ret);
|
||||
|
||||
msm_arch_idle();
|
||||
|
||||
ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
|
||||
WARN_ON(ret);
|
||||
|
||||
if (!msm_pm_ret_no_pll_switch)
|
||||
if (clk_enable(cpu_clk))
|
||||
pr_err("%s(): Error restore cpu clk\n", __func__);
|
||||
|
||||
spin_lock(&retention_lock);
|
||||
cpumask_clear_cpu(cpu, &retention_cpus);
|
||||
bailout:
|
||||
spin_unlock(&retention_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void msm_pc_inc_debug_count(uint32_t cpu,
|
||||
enum msm_pc_count_offsets offset)
|
||||
{
|
||||
int cntr_offset;
|
||||
uint32_t cluster_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1);
|
||||
uint32_t cpu_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0);
|
||||
|
||||
if (cluster_id >= MAX_NUM_CLUSTER || cpu_id >= MAX_CPUS_PER_CLUSTER)
|
||||
WARN_ON(cpu);
|
||||
|
||||
cntr_offset = (cluster_id * MAX_CPUS_PER_CLUSTER * MSM_PC_NUM_COUNTERS)
|
||||
+ (cpu_id * MSM_PC_NUM_COUNTERS) + offset;
|
||||
|
||||
if (!msm_pc_debug_counters)
|
||||
return;
|
||||
|
||||
msm_pc_debug_counters[cntr_offset]++;
|
||||
}
|
||||
|
||||
static bool msm_pm_pc_hotplug(void)
|
||||
{
|
||||
uint32_t cpu = smp_processor_id();
|
||||
enum msm_pm_l2_scm_flag flag;
|
||||
|
||||
flag = lpm_cpu_pre_pc_cb(cpu);
|
||||
|
||||
if (!msm_pm_tz_flushes_cache) {
|
||||
if (flag == MSM_SCM_L2_OFF)
|
||||
flush_cache_all();
|
||||
else if (msm_pm_is_L1_writeback())
|
||||
flush_cache_louis();
|
||||
}
|
||||
|
||||
msm_pc_inc_debug_count(cpu, MSM_PC_ENTRY_COUNTER);
|
||||
qcom_scm_cpu_power_down(SCM_CMD_CORE_HOTPLUGGED |
|
||||
(flag & SCM_FLUSH_FLAG_MASK));
|
||||
|
||||
/* Should not return here */
|
||||
msm_pc_inc_debug_count(cpu, MSM_PC_FALLTHRU_COUNTER);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool msm_pm_fastpc(bool from_idle)
|
||||
{
|
||||
int ret = 0;
|
||||
unsigned int cpu = smp_processor_id();
|
||||
|
||||
ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_FASTPC, false);
|
||||
WARN_ON(ret);
|
||||
|
||||
if (from_idle || cpu_online(cpu))
|
||||
msm_arch_idle();
|
||||
else
|
||||
msm_pm_pc_hotplug();
|
||||
|
||||
ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
|
||||
WARN_ON(ret);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int msm_pm_collapse(unsigned long unused)
|
||||
{
|
||||
uint32_t cpu = smp_processor_id();
|
||||
enum msm_pm_l2_scm_flag flag;
|
||||
|
||||
flag = lpm_cpu_pre_pc_cb(cpu);
|
||||
|
||||
if (!msm_pm_tz_flushes_cache) {
|
||||
if (flag == MSM_SCM_L2_OFF)
|
||||
flush_cache_all();
|
||||
else if (msm_pm_is_L1_writeback())
|
||||
flush_cache_louis();
|
||||
}
|
||||
msm_pc_inc_debug_count(cpu, MSM_PC_ENTRY_COUNTER);
|
||||
qcom_scm_cpu_power_down(flag & SCM_FLUSH_FLAG_MASK);
|
||||
msm_pc_inc_debug_count(cpu, MSM_PC_FALLTHRU_COUNTER);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(msm_pm_collapse);
|
||||
|
||||
static bool __ref msm_pm_spm_power_collapse(
|
||||
unsigned int cpu, int mode, bool from_idle, bool notify_rpm)
|
||||
{
|
||||
void *entry;
|
||||
bool collapsed = false;
|
||||
int ret;
|
||||
bool save_cpu_regs = (cpu_online(cpu) || from_idle);
|
||||
|
||||
if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
|
||||
pr_info("CPU%u: %s: notify_rpm %d\n",
|
||||
cpu, __func__, (int) notify_rpm);
|
||||
|
||||
ret = msm_spm_set_low_power_mode(mode, notify_rpm);
|
||||
WARN_ON(ret);
|
||||
|
||||
entry = save_cpu_regs ? cpu_resume : msm_secondary_startup;
|
||||
|
||||
msm_pm_boot_config_before_pc(cpu, virt_to_phys(entry));
|
||||
|
||||
if (MSM_PM_DEBUG_RESET_VECTOR & msm_pm_debug_mask)
|
||||
pr_info("CPU%u: %s: program vector to %pK\n",
|
||||
cpu, __func__, entry);
|
||||
|
||||
msm_jtag_save_state();
|
||||
|
||||
collapsed = save_cpu_regs ?
|
||||
!cpu_suspend(0, msm_pm_collapse) : msm_pm_pc_hotplug();
|
||||
|
||||
msm_jtag_restore_state();
|
||||
|
||||
if (collapsed)
|
||||
local_fiq_enable();
|
||||
|
||||
msm_pm_boot_config_after_pc(cpu);
|
||||
|
||||
if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
|
||||
pr_info("CPU%u: %s: msm_pm_collapse returned, collapsed %d\n",
|
||||
cpu, __func__, collapsed);
|
||||
|
||||
ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false);
|
||||
WARN_ON(ret);
|
||||
return collapsed;
|
||||
}
|
||||
|
||||
static bool msm_pm_power_collapse_standalone(
|
||||
bool from_idle)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
bool collapsed;
|
||||
|
||||
collapsed = msm_pm_spm_power_collapse(cpu,
|
||||
MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE,
|
||||
from_idle, false);
|
||||
|
||||
return collapsed;
|
||||
}
|
||||
|
||||
static int ramp_down_last_cpu(int cpu)
|
||||
{
|
||||
struct clk *cpu_clk = per_cpu(cpu_clks, cpu);
|
||||
|
||||
clk_disable(cpu_clk);
|
||||
clk_disable(l2_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ramp_up_first_cpu(int cpu, int saved_rate)
|
||||
{
|
||||
struct clk *cpu_clk = per_cpu(cpu_clks, cpu);
|
||||
int rc = 0;
|
||||
|
||||
if (MSM_PM_DEBUG_CLOCK & msm_pm_debug_mask)
|
||||
pr_info("CPU%u: %s: restore clock rate\n",
|
||||
cpu, __func__);
|
||||
|
||||
clk_enable(l2_clk);
|
||||
|
||||
if (cpu_clk) {
|
||||
int ret = clk_enable(cpu_clk);
|
||||
|
||||
if (ret) {
|
||||
pr_err("%s(): Error restoring cpu clk\n",
|
||||
__func__);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static bool msm_pm_power_collapse(bool from_idle)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
unsigned long saved_acpuclk_rate = 0;
|
||||
bool collapsed;
|
||||
|
||||
if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
|
||||
pr_info("CPU%u: %s: idle %d\n",
|
||||
cpu, __func__, (int)from_idle);
|
||||
|
||||
if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
|
||||
pr_info("CPU%u: %s: pre power down\n", cpu, __func__);
|
||||
|
||||
if (cpu_online(cpu) && !msm_no_ramp_down_pc)
|
||||
saved_acpuclk_rate = ramp_down_last_cpu(cpu);
|
||||
|
||||
collapsed = msm_pm_spm_power_collapse(cpu, MSM_SPM_MODE_POWER_COLLAPSE,
|
||||
from_idle, true);
|
||||
|
||||
if (cpu_online(cpu) && !msm_no_ramp_down_pc)
|
||||
ramp_up_first_cpu(cpu, saved_acpuclk_rate);
|
||||
|
||||
if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
|
||||
pr_info("CPU%u: %s: post power up\n", cpu, __func__);
|
||||
|
||||
if (MSM_PM_DEBUG_POWER_COLLAPSE & msm_pm_debug_mask)
|
||||
pr_info("CPU%u: %s: return\n", cpu, __func__);
|
||||
return collapsed;
|
||||
}
|
||||
/******************************************************************************
|
||||
* External Idle/Suspend Functions
|
||||
*****************************************************************************/
|
||||
|
||||
static void arch_idle(void) {}
|
||||
|
||||
static bool (*execute[MSM_PM_SLEEP_MODE_NR])(bool idle) = {
|
||||
[MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT] = msm_pm_swfi,
|
||||
[MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE] =
|
||||
msm_pm_power_collapse_standalone,
|
||||
[MSM_PM_SLEEP_MODE_RETENTION] = msm_pm_retention,
|
||||
[MSM_PM_SLEEP_MODE_FASTPC] = msm_pm_fastpc,
|
||||
[MSM_PM_SLEEP_MODE_POWER_COLLAPSE] = msm_pm_power_collapse,
|
||||
};
|
||||
|
||||
/**
|
||||
* msm_cpu_pm_enter_sleep(): Enter a low power mode on current cpu
|
||||
*
|
||||
* @mode - sleep mode to enter
|
||||
* @from_idle - bool to indicate that the mode is exercised during idle/suspend
|
||||
*
|
||||
* returns none
|
||||
*
|
||||
* The code should be with interrupts disabled and on the core on which the
|
||||
* low power is to be executed.
|
||||
*
|
||||
*/
|
||||
bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_idle)
|
||||
{
|
||||
bool exit_stat = false;
|
||||
unsigned int cpu = smp_processor_id();
|
||||
|
||||
if ((!from_idle && cpu_online(cpu))
|
||||
|| (MSM_PM_DEBUG_IDLE & msm_pm_debug_mask))
|
||||
pr_info("CPU%u:%s mode:%d during %s\n", cpu, __func__,
|
||||
mode, from_idle ? "idle" : "suspend");
|
||||
|
||||
if (execute[mode])
|
||||
exit_stat = execute[mode](from_idle);
|
||||
|
||||
return exit_stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* msm_pm_wait_cpu_shutdown() - Wait for a core to be power collapsed during
|
||||
* hotplug
|
||||
*
|
||||
* @ cpu - cpu to wait on.
|
||||
*
|
||||
* Blocking function call that waits on the core to be power collapsed. This
|
||||
* function is called from platform_cpu_die to ensure that a core is power
|
||||
* collapsed before sending the CPU_DEAD notification so the drivers could
|
||||
* remove the resource votes for this CPU(regulator and clock)
|
||||
*/
|
||||
int msm_pm_wait_cpu_shutdown(unsigned int cpu)
|
||||
{
|
||||
int timeout = 0;
|
||||
|
||||
if (!msm_pm_slp_sts)
|
||||
return 0;
|
||||
if (!msm_pm_slp_sts[cpu].base_addr)
|
||||
return 0;
|
||||
while (1) {
|
||||
/*
|
||||
* Check for the SPM of the core being hotplugged to set
|
||||
* its sleep state.The SPM sleep state indicates that the
|
||||
* core has been power collapsed.
|
||||
*/
|
||||
int acc_sts = __raw_readl(msm_pm_slp_sts[cpu].base_addr);
|
||||
|
||||
if (acc_sts & msm_pm_slp_sts[cpu].mask)
|
||||
return 0;
|
||||
|
||||
udelay(100);
|
||||
/*
|
||||
* Dump spm registers for debugging
|
||||
*/
|
||||
if (++timeout == 20) {
|
||||
msm_spm_dump_regs(cpu);
|
||||
//__WARN_printf(
|
||||
//"CPU%u didn't collapse in 2ms, sleep status: 0x%x\n",
|
||||
// cpu, acc_sts);
|
||||
}
|
||||
}
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static void msm_pm_ack_retention_disable(void *data)
|
||||
{
|
||||
/*
|
||||
* This is a NULL function to ensure that the core has woken up
|
||||
* and is safe to disable retention.
|
||||
*/
|
||||
}
|
||||
/**
|
||||
* msm_pm_enable_retention() - Disable/Enable retention on all cores
|
||||
* @enable: Enable/Disable retention
|
||||
*
|
||||
*/
|
||||
void msm_pm_enable_retention(bool enable)
|
||||
{
|
||||
if (enable == msm_pm_ldo_retention_enabled)
|
||||
return;
|
||||
|
||||
msm_pm_ldo_retention_enabled = enable;
|
||||
|
||||
/*
|
||||
* If retention is being disabled, wakeup all online core to ensure
|
||||
* that it isn't executing retention. Offlined cores need not be woken
|
||||
* up as they enter the deepest sleep mode, namely RPM assited power
|
||||
* collapse
|
||||
*/
|
||||
if (!enable) {
|
||||
preempt_disable();
|
||||
smp_call_function_many(&retention_cpus,
|
||||
msm_pm_ack_retention_disable,
|
||||
NULL, true);
|
||||
preempt_enable();
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(msm_pm_enable_retention);
|
||||
|
||||
/**
|
||||
* msm_pm_retention_enabled() - Check if retention is enabled
|
||||
*
|
||||
* returns true if retention is enabled
|
||||
*/
|
||||
bool msm_pm_retention_enabled(void)
|
||||
{
|
||||
return msm_pm_ldo_retention_enabled;
|
||||
}
|
||||
EXPORT_SYMBOL(msm_pm_retention_enabled);
|
||||
|
||||
static int msm_cpu_status_probe(struct platform_device *pdev)
|
||||
{
|
||||
u32 cpu;
|
||||
int rc;
|
||||
|
||||
if (!pdev || !pdev->dev.of_node)
|
||||
return -EFAULT;
|
||||
|
||||
msm_pm_slp_sts = devm_kzalloc(&pdev->dev,
|
||||
sizeof(*msm_pm_slp_sts) * num_possible_cpus(),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!msm_pm_slp_sts)
|
||||
return -ENOMEM;
|
||||
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct device_node *cpun, *node;
|
||||
char *key;
|
||||
|
||||
cpun = of_get_cpu_node(cpu, NULL);
|
||||
|
||||
if (!cpun) {
|
||||
__WARN();
|
||||
continue;
|
||||
}
|
||||
|
||||
node = of_parse_phandle(cpun, "qcom,sleep-status", 0);
|
||||
if (!node)
|
||||
return -ENODEV;
|
||||
|
||||
msm_pm_slp_sts[cpu].base_addr = of_iomap(node, 0);
|
||||
if (!msm_pm_slp_sts[cpu].base_addr) {
|
||||
pr_err("%s: Can't find base addr\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
key = "qcom,sleep-status-mask";
|
||||
rc = of_property_read_u32(node, key, &msm_pm_slp_sts[cpu].mask);
|
||||
if (rc) {
|
||||
pr_err("%s: Can't find %s property\n", __func__, key);
|
||||
iounmap(msm_pm_slp_sts[cpu].base_addr);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
struct msm_pc_debug_counters_buffer {
|
||||
long *reg;
|
||||
u32 len;
|
||||
char buf[MAX_BUF_SIZE];
|
||||
};
|
||||
|
||||
static char *counter_name[MSM_PC_NUM_COUNTERS] = {
|
||||
"PC Entry Counter",
|
||||
"Warmboot Entry Counter",
|
||||
"PC Bailout Counter"
|
||||
};
|
||||
|
||||
static int msm_pc_debug_counters_copy(
|
||||
struct msm_pc_debug_counters_buffer *data)
|
||||
{
|
||||
int j;
|
||||
u32 stat;
|
||||
unsigned int cpu;
|
||||
unsigned int len;
|
||||
uint32_t cluster_id;
|
||||
uint32_t cpu_id;
|
||||
uint32_t offset;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
len = scnprintf(data->buf + data->len,
|
||||
sizeof(data->buf)-data->len,
|
||||
"CPU%d\n", cpu);
|
||||
cluster_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1);
|
||||
cpu_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0);
|
||||
offset = (cluster_id * MAX_CPUS_PER_CLUSTER
|
||||
* MSM_PC_NUM_COUNTERS)
|
||||
+ (cpu_id * MSM_PC_NUM_COUNTERS);
|
||||
|
||||
data->len += len;
|
||||
|
||||
for (j = 0; j < MSM_PC_NUM_COUNTERS - 1; j++) {
|
||||
stat = data->reg[offset + j];
|
||||
len = scnprintf(data->buf + data->len,
|
||||
sizeof(data->buf) - data->len,
|
||||
"\t%s: %d", counter_name[j], stat);
|
||||
|
||||
data->len += len;
|
||||
}
|
||||
len = scnprintf(data->buf + data->len,
|
||||
sizeof(data->buf) - data->len,
|
||||
"\n");
|
||||
|
||||
data->len += len;
|
||||
}
|
||||
|
||||
return data->len;
|
||||
}
|
||||
|
||||
static ssize_t msm_pc_debug_counters_file_read(struct file *file,
|
||||
char __user *bufu, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct msm_pc_debug_counters_buffer *data;
|
||||
ssize_t ret;
|
||||
|
||||
mutex_lock(&msm_pc_debug_mutex);
|
||||
data = file->private_data;
|
||||
|
||||
if (!data) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!bufu) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!access_ok(bufu, count)) {
|
||||
ret = -EFAULT;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (*ppos >= data->len && data->len == 0)
|
||||
data->len = msm_pc_debug_counters_copy(data);
|
||||
|
||||
ret = simple_read_from_buffer(bufu, count, ppos,
|
||||
data->buf, data->len);
|
||||
exit:
|
||||
mutex_unlock(&msm_pc_debug_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pc_debug_counters_file_open(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
struct msm_pc_debug_counters_buffer *buf;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&msm_pc_debug_mutex);
|
||||
|
||||
if (!inode->i_private) {
|
||||
ret = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
file->private_data = kzalloc(
|
||||
sizeof(struct msm_pc_debug_counters_buffer), GFP_KERNEL);
|
||||
|
||||
if (!file->private_data) {
|
||||
pr_err("%s: ERROR kmalloc failed to allocate %zu bytes\n",
|
||||
__func__, sizeof(struct msm_pc_debug_counters_buffer));
|
||||
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
buf = file->private_data;
|
||||
buf->reg = (long *)inode->i_private;
|
||||
|
||||
exit:
|
||||
mutex_unlock(&msm_pc_debug_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int msm_pc_debug_counters_file_close(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
||||
mutex_lock(&msm_pc_debug_mutex);
|
||||
kfree(file->private_data);
|
||||
mutex_unlock(&msm_pc_debug_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations msm_pc_debug_counters_fops = {
|
||||
.open = msm_pc_debug_counters_file_open,
|
||||
.read = msm_pc_debug_counters_file_read,
|
||||
.release = msm_pc_debug_counters_file_close,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static int msm_pm_clk_init(struct platform_device *pdev)
|
||||
{
|
||||
bool synced_clocks;
|
||||
u32 cpu;
|
||||
char clk_name[] = "cpu??_clk";
|
||||
char *key;
|
||||
|
||||
key = "qcom,saw-turns-off-pll";
|
||||
if (of_property_read_bool(pdev->dev.of_node, key))
|
||||
return 0;
|
||||
|
||||
key = "qcom,synced-clocks";
|
||||
synced_clocks = of_property_read_bool(pdev->dev.of_node, key);
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct clk *clk;
|
||||
|
||||
snprintf(clk_name, sizeof(clk_name), "cpu%d_clk", cpu);
|
||||
clk = clk_get(&pdev->dev, clk_name);
|
||||
if (IS_ERR(clk)) {
|
||||
if (cpu && synced_clocks)
|
||||
return 0;
|
||||
clk = NULL;
|
||||
}
|
||||
per_cpu(cpu_clks, cpu) = clk;
|
||||
}
|
||||
|
||||
if (synced_clocks)
|
||||
return 0;
|
||||
|
||||
l2_clk = clk_get(&pdev->dev, "l2_clk");
|
||||
if (IS_ERR(l2_clk))
|
||||
pr_warn("%s: Could not get l2_clk (-%ld)\n", __func__,
|
||||
PTR_ERR(l2_clk));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_cpu_pm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct dentry *dent = NULL;
|
||||
struct resource *res = NULL;
|
||||
int ret = 0;
|
||||
void __iomem *msm_pc_debug_counters_imem;
|
||||
char *key;
|
||||
int alloc_size = (MAX_NUM_CLUSTER * MAX_CPUS_PER_CLUSTER
|
||||
* MSM_PC_NUM_COUNTERS
|
||||
* sizeof(*msm_pc_debug_counters));
|
||||
|
||||
msm_pc_debug_counters = dma_alloc_coherent(&pdev->dev, alloc_size,
|
||||
&msm_pc_debug_counters_phys, GFP_KERNEL);
|
||||
|
||||
if (msm_pc_debug_counters) {
|
||||
memset(msm_pc_debug_counters, 0, alloc_size);
|
||||
dent = debugfs_create_file("pc_debug_counter", 0444, NULL,
|
||||
msm_pc_debug_counters,
|
||||
&msm_pc_debug_counters_fops);
|
||||
if (!dent)
|
||||
pr_err("%s: ERROR debugfs_create_file failed\n",
|
||||
__func__);
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
goto skip_save_imem;
|
||||
msm_pc_debug_counters_imem = devm_ioremap(&pdev->dev,
|
||||
res->start, resource_size(res));
|
||||
if (msm_pc_debug_counters_imem) {
|
||||
writel_relaxed(msm_pc_debug_counters_phys,
|
||||
msm_pc_debug_counters_imem);
|
||||
/* memory barrier */
|
||||
mb();
|
||||
}
|
||||
} else {
|
||||
msm_pc_debug_counters = NULL;
|
||||
msm_pc_debug_counters_phys = 0;
|
||||
}
|
||||
skip_save_imem:
|
||||
if (pdev->dev.of_node) {
|
||||
key = "qcom,tz-flushes-cache";
|
||||
msm_pm_tz_flushes_cache =
|
||||
of_property_read_bool(pdev->dev.of_node, key);
|
||||
|
||||
key = "qcom,no-pll-switch-for-retention";
|
||||
msm_pm_ret_no_pll_switch =
|
||||
of_property_read_bool(pdev->dev.of_node, key);
|
||||
|
||||
ret = msm_pm_clk_init(pdev);
|
||||
if (ret) {
|
||||
pr_info("msm_pm_clk_init returned error\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (pdev->dev.of_node)
|
||||
of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct of_device_id msm_cpu_pm_table[] = {
|
||||
{.compatible = "qcom,pm"},
|
||||
{},
|
||||
};
|
||||
|
||||
static struct platform_driver msm_cpu_pm_driver = {
|
||||
.probe = msm_cpu_pm_probe,
|
||||
.driver = {
|
||||
.name = "msm-pm",
|
||||
.of_match_table = msm_cpu_pm_table,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init msm_pm_debug_counters_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = platform_driver_register(&msm_cpu_pm_driver);
|
||||
|
||||
if (rc)
|
||||
pr_err("%s(): failed to register driver %s\n", __func__,
|
||||
msm_cpu_pm_driver.driver.name);
|
||||
return rc;
|
||||
}
|
||||
fs_initcall(msm_pm_debug_counters_init);
|
||||
|
||||
#ifdef CONFIG_ARM
|
||||
static int idle_initialize(void)
|
||||
{
|
||||
arm_pm_idle = arch_idle;
|
||||
return 0;
|
||||
}
|
||||
early_initcall(idle_initialize);
|
||||
#endif
|
||||
762
drivers/soc/qcom/msm-spm.c
Normal file
762
drivers/soc/qcom/msm-spm.c
Normal file
|
|
@ -0,0 +1,762 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2011-2017, 2020-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "spm_driver.h"
|
||||
|
||||
#define MSM_SPM_PMIC_STATE_IDLE 0
|
||||
|
||||
enum {
|
||||
MSM_SPM_DEBUG_SHADOW = 1U << 0,
|
||||
MSM_SPM_DEBUG_VCTL = 1U << 1,
|
||||
};
|
||||
|
||||
static int msm_spm_debug_mask;
|
||||
module_param_named(
|
||||
debug_mask, msm_spm_debug_mask, int, 0664
|
||||
);
|
||||
|
||||
struct saw2_data {
|
||||
const char *ver_name;
|
||||
uint32_t major;
|
||||
uint32_t minor;
|
||||
uint32_t *spm_reg_offset_ptr;
|
||||
};
|
||||
|
||||
static uint32_t msm_spm_reg_offsets_saw2_v2_1[MSM_SPM_REG_NR] = {
|
||||
[MSM_SPM_REG_SAW_SECURE] = 0x00,
|
||||
[MSM_SPM_REG_SAW_ID] = 0x04,
|
||||
[MSM_SPM_REG_SAW_CFG] = 0x08,
|
||||
[MSM_SPM_REG_SAW_SPM_STS] = 0x0C,
|
||||
[MSM_SPM_REG_SAW_AVS_STS] = 0x10,
|
||||
[MSM_SPM_REG_SAW_PMIC_STS] = 0x14,
|
||||
[MSM_SPM_REG_SAW_RST] = 0x18,
|
||||
[MSM_SPM_REG_SAW_VCTL] = 0x1C,
|
||||
[MSM_SPM_REG_SAW_AVS_CTL] = 0x20,
|
||||
[MSM_SPM_REG_SAW_AVS_LIMIT] = 0x24,
|
||||
[MSM_SPM_REG_SAW_AVS_DLY] = 0x28,
|
||||
[MSM_SPM_REG_SAW_AVS_HYSTERESIS] = 0x2C,
|
||||
[MSM_SPM_REG_SAW_SPM_CTL] = 0x30,
|
||||
[MSM_SPM_REG_SAW_SPM_DLY] = 0x34,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_0] = 0x40,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_1] = 0x44,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_2] = 0x48,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_3] = 0x4C,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_4] = 0x50,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_5] = 0x54,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_6] = 0x58,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_7] = 0x5C,
|
||||
[MSM_SPM_REG_SAW_SEQ_ENTRY] = 0x80,
|
||||
[MSM_SPM_REG_SAW_VERSION] = 0xFD0,
|
||||
};
|
||||
|
||||
static uint32_t msm_spm_reg_offsets_saw2_v3_0[MSM_SPM_REG_NR] = {
|
||||
[MSM_SPM_REG_SAW_SECURE] = 0x00,
|
||||
[MSM_SPM_REG_SAW_ID] = 0x04,
|
||||
[MSM_SPM_REG_SAW_CFG] = 0x08,
|
||||
[MSM_SPM_REG_SAW_SPM_STS] = 0x0C,
|
||||
[MSM_SPM_REG_SAW_AVS_STS] = 0x10,
|
||||
[MSM_SPM_REG_SAW_PMIC_STS] = 0x14,
|
||||
[MSM_SPM_REG_SAW_RST] = 0x18,
|
||||
[MSM_SPM_REG_SAW_VCTL] = 0x1C,
|
||||
[MSM_SPM_REG_SAW_AVS_CTL] = 0x20,
|
||||
[MSM_SPM_REG_SAW_AVS_LIMIT] = 0x24,
|
||||
[MSM_SPM_REG_SAW_AVS_DLY] = 0x28,
|
||||
[MSM_SPM_REG_SAW_AVS_HYSTERESIS] = 0x2C,
|
||||
[MSM_SPM_REG_SAW_SPM_CTL] = 0x30,
|
||||
[MSM_SPM_REG_SAW_SPM_DLY] = 0x34,
|
||||
[MSM_SPM_REG_SAW_STS2] = 0x38,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_0] = 0x40,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_1] = 0x44,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_2] = 0x48,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_3] = 0x4C,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_4] = 0x50,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_5] = 0x54,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_6] = 0x58,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_7] = 0x5C,
|
||||
[MSM_SPM_REG_SAW_SEQ_ENTRY] = 0x400,
|
||||
[MSM_SPM_REG_SAW_VERSION] = 0xFD0,
|
||||
};
|
||||
|
||||
static uint32_t msm_spm_reg_offsets_saw2_v4_1[MSM_SPM_REG_NR] = {
|
||||
[MSM_SPM_REG_SAW_SECURE] = 0xC00,
|
||||
[MSM_SPM_REG_SAW_ID] = 0xC04,
|
||||
[MSM_SPM_REG_SAW_STS2] = 0xC10,
|
||||
[MSM_SPM_REG_SAW_SPM_STS] = 0xC0C,
|
||||
[MSM_SPM_REG_SAW_AVS_STS] = 0xC14,
|
||||
[MSM_SPM_REG_SAW_PMIC_STS] = 0xC18,
|
||||
[MSM_SPM_REG_SAW_RST] = 0xC1C,
|
||||
[MSM_SPM_REG_SAW_VCTL] = 0x900,
|
||||
[MSM_SPM_REG_SAW_AVS_CTL] = 0x904,
|
||||
[MSM_SPM_REG_SAW_AVS_LIMIT] = 0x908,
|
||||
[MSM_SPM_REG_SAW_AVS_DLY] = 0x90C,
|
||||
[MSM_SPM_REG_SAW_SPM_CTL] = 0x0,
|
||||
[MSM_SPM_REG_SAW_SPM_DLY] = 0x4,
|
||||
[MSM_SPM_REG_SAW_CFG] = 0x0C,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_0] = 0x40,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_1] = 0x44,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_2] = 0x48,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_3] = 0x4C,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_4] = 0x50,
|
||||
[MSM_SPM_REG_SAW_PMIC_DATA_5] = 0x54,
|
||||
[MSM_SPM_REG_SAW_SEQ_ENTRY] = 0x400,
|
||||
[MSM_SPM_REG_SAW_VERSION] = 0xFD0,
|
||||
};
|
||||
|
||||
static struct saw2_data saw2_info[] = {
|
||||
[0] = {
|
||||
"SAW_v2.1",
|
||||
0x2,
|
||||
0x1,
|
||||
msm_spm_reg_offsets_saw2_v2_1,
|
||||
},
|
||||
[1] = {
|
||||
"SAW_v2.3",
|
||||
0x3,
|
||||
0x0,
|
||||
msm_spm_reg_offsets_saw2_v3_0,
|
||||
},
|
||||
[2] = {
|
||||
"SAW_v3.0",
|
||||
0x1,
|
||||
0x0,
|
||||
msm_spm_reg_offsets_saw2_v3_0,
|
||||
},
|
||||
[3] = {
|
||||
"SAW_v4.0",
|
||||
0x4,
|
||||
0x1,
|
||||
msm_spm_reg_offsets_saw2_v4_1,
|
||||
},
|
||||
};
|
||||
|
||||
static uint32_t num_pmic_data;
|
||||
|
||||
static void msm_spm_drv_flush_shadow(struct msm_spm_driver_data *dev,
|
||||
unsigned int reg_index)
|
||||
{
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
__raw_writel(dev->reg_shadow[reg_index],
|
||||
dev->reg_base_addr + dev->reg_offsets[reg_index]);
|
||||
}
|
||||
|
||||
static void msm_spm_drv_load_shadow(struct msm_spm_driver_data *dev,
|
||||
unsigned int reg_index)
|
||||
{
|
||||
dev->reg_shadow[reg_index] =
|
||||
__raw_readl(dev->reg_base_addr +
|
||||
dev->reg_offsets[reg_index]);
|
||||
}
|
||||
|
||||
static inline uint32_t msm_spm_drv_get_num_spm_entry(
|
||||
struct msm_spm_driver_data *dev)
|
||||
{
|
||||
if (!dev)
|
||||
return -ENODEV;
|
||||
|
||||
msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_ID);
|
||||
return (dev->reg_shadow[MSM_SPM_REG_SAW_ID] >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
static inline void msm_spm_drv_set_start_addr(
|
||||
struct msm_spm_driver_data *dev, uint32_t ctl)
|
||||
{
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_SPM_CTL] = ctl;
|
||||
}
|
||||
|
||||
static inline bool msm_spm_pmic_arb_present(struct msm_spm_driver_data *dev)
|
||||
{
|
||||
msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_ID);
|
||||
return (dev->reg_shadow[MSM_SPM_REG_SAW_ID] >> 2) & 0x1;
|
||||
}
|
||||
|
||||
static inline void msm_spm_drv_set_vctl2(struct msm_spm_driver_data *dev,
|
||||
uint32_t vlevel, uint32_t vctl_port)
|
||||
{
|
||||
unsigned int pmic_data = 0;
|
||||
|
||||
pmic_data |= vlevel;
|
||||
pmic_data |= (vctl_port & 0x7) << 16;
|
||||
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] &= ~0x700FF;
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] |= pmic_data;
|
||||
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_PMIC_DATA_3] &= ~0x700FF;
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_PMIC_DATA_3] |= pmic_data;
|
||||
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_VCTL);
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_PMIC_DATA_3);
|
||||
}
|
||||
|
||||
static inline uint32_t msm_spm_drv_get_num_pmic_data(
|
||||
struct msm_spm_driver_data *dev)
|
||||
{
|
||||
msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_ID);
|
||||
mb(); /* Ensure we flush */
|
||||
return (dev->reg_shadow[MSM_SPM_REG_SAW_ID] >> 4) & 0x7;
|
||||
}
|
||||
|
||||
static inline uint32_t msm_spm_drv_get_sts_pmic_state(
|
||||
struct msm_spm_driver_data *dev)
|
||||
{
|
||||
msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_PMIC_STS);
|
||||
return (dev->reg_shadow[MSM_SPM_REG_SAW_PMIC_STS] >> 16) &
|
||||
0x03;
|
||||
}
|
||||
|
||||
uint32_t msm_spm_drv_get_sts_curr_pmic_data(
|
||||
struct msm_spm_driver_data *dev)
|
||||
{
|
||||
msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_PMIC_STS);
|
||||
return dev->reg_shadow[MSM_SPM_REG_SAW_PMIC_STS] & 0x300FF;
|
||||
}
|
||||
|
||||
static inline void msm_spm_drv_get_saw2_ver(struct msm_spm_driver_data *dev,
|
||||
uint32_t *major, uint32_t *minor)
|
||||
{
|
||||
uint32_t val = 0;
|
||||
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_VERSION] =
|
||||
__raw_readl(dev->reg_base_addr + dev->ver_reg);
|
||||
|
||||
val = dev->reg_shadow[MSM_SPM_REG_SAW_VERSION];
|
||||
|
||||
*major = (val >> 28) & 0xF;
|
||||
*minor = (val >> 16) & 0xFFF;
|
||||
}
|
||||
|
||||
inline int msm_spm_drv_set_spm_enable(
|
||||
struct msm_spm_driver_data *dev, bool enable)
|
||||
{
|
||||
uint32_t value = enable ? 0x01 : 0x00;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
if ((dev->reg_shadow[MSM_SPM_REG_SAW_SPM_CTL] & 0x01) ^ value) {
|
||||
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_SPM_CTL] &= ~0x1;
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_SPM_CTL] |= value;
|
||||
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_SPM_CTL);
|
||||
wmb(); /* Ensure we flush */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_spm_drv_get_avs_enable(struct msm_spm_driver_data *dev)
|
||||
{
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
return dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] & 0x01;
|
||||
}
|
||||
|
||||
int msm_spm_drv_set_avs_enable(struct msm_spm_driver_data *dev,
|
||||
bool enable)
|
||||
{
|
||||
uint32_t value = enable ? 0x1 : 0x0;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
if ((dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] & 0x1) ^ value) {
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] &= ~0x1;
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] |= value;
|
||||
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_spm_drv_set_avs_limit(struct msm_spm_driver_data *dev,
|
||||
uint32_t min_lvl, uint32_t max_lvl)
|
||||
{
|
||||
uint32_t value = (max_lvl & 0xff) << 16 | (min_lvl & 0xff);
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_AVS_LIMIT] = value;
|
||||
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_LIMIT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msm_spm_drv_avs_irq_mask(enum msm_spm_avs_irq irq)
|
||||
{
|
||||
switch (irq) {
|
||||
case MSM_SPM_AVS_IRQ_MIN:
|
||||
return BIT(1);
|
||||
case MSM_SPM_AVS_IRQ_MAX:
|
||||
return BIT(2);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
int msm_spm_drv_set_avs_irq_enable(struct msm_spm_driver_data *dev,
|
||||
enum msm_spm_avs_irq irq, bool enable)
|
||||
{
|
||||
int mask = msm_spm_drv_avs_irq_mask(irq);
|
||||
uint32_t value;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
else if (mask < 0)
|
||||
return mask;
|
||||
|
||||
value = enable ? mask : 0;
|
||||
|
||||
if ((dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] & mask) ^ value) {
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] &= ~mask;
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] |= value;
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_spm_drv_avs_clear_irq(struct msm_spm_driver_data *dev,
|
||||
enum msm_spm_avs_irq irq)
|
||||
{
|
||||
int mask = msm_spm_drv_avs_irq_mask(irq);
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
else if (mask < 0)
|
||||
return mask;
|
||||
|
||||
if (dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] & mask) {
|
||||
/*
|
||||
* The interrupt status is cleared by disabling and then
|
||||
* re-enabling the interrupt.
|
||||
*/
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] &= ~mask;
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] |= mask;
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev)
|
||||
{
|
||||
int i;
|
||||
int num_spm_entry = msm_spm_drv_get_num_spm_entry(dev);
|
||||
|
||||
if (!dev) {
|
||||
__WARN();
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_spm_entry; i++) {
|
||||
__raw_writel(dev->reg_seq_entry_shadow[i],
|
||||
dev->reg_base_addr
|
||||
+ dev->reg_offsets[MSM_SPM_REG_SAW_SEQ_ENTRY]
|
||||
+ 4 * i);
|
||||
}
|
||||
mb(); /* Ensure we flush */
|
||||
}
|
||||
|
||||
void dump_regs(struct msm_spm_driver_data *dev, int cpu)
|
||||
{
|
||||
msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_SPM_STS);
|
||||
mb(); /* Ensure we flush */
|
||||
pr_err("CPU%d: spm register MSM_SPM_REG_SAW_SPM_STS: 0x%x\n", cpu,
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_SPM_STS]);
|
||||
msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_SPM_CTL);
|
||||
mb(); /* Ensure we flush */
|
||||
pr_err("CPU%d: spm register MSM_SPM_REG_SAW_SPM_CTL: 0x%x\n", cpu,
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_SPM_CTL]);
|
||||
}
|
||||
|
||||
int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev,
|
||||
uint8_t *cmd, uint32_t *offset)
|
||||
{
|
||||
uint32_t cmd_w;
|
||||
uint32_t offset_w = *offset / 4;
|
||||
uint8_t last_cmd;
|
||||
|
||||
if (!cmd)
|
||||
return -EINVAL;
|
||||
|
||||
while (1) {
|
||||
int i;
|
||||
|
||||
cmd_w = 0;
|
||||
last_cmd = 0;
|
||||
cmd_w = dev->reg_seq_entry_shadow[offset_w];
|
||||
|
||||
for (i = (*offset % 4); i < 4; i++) {
|
||||
last_cmd = *(cmd++);
|
||||
cmd_w |= last_cmd << (i * 8);
|
||||
(*offset)++;
|
||||
if (last_cmd == 0x0f)
|
||||
break;
|
||||
}
|
||||
|
||||
dev->reg_seq_entry_shadow[offset_w++] = cmd_w;
|
||||
if (last_cmd == 0x0f)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev,
|
||||
uint32_t ctl)
|
||||
{
|
||||
|
||||
/* SPM is configured to reset start address to zero after end of Program
|
||||
*/
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
msm_spm_drv_set_start_addr(dev, ctl);
|
||||
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_SPM_CTL);
|
||||
wmb(); /* Ensure we flush */
|
||||
|
||||
if (msm_spm_debug_mask & MSM_SPM_DEBUG_SHADOW) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MSM_SPM_REG_NR; i++)
|
||||
pr_info("%s: reg %02x = 0x%08x\n", __func__,
|
||||
dev->reg_offsets[i], dev->reg_shadow[i]);
|
||||
}
|
||||
msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_SPM_STS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t msm_spm_drv_get_vdd(struct msm_spm_driver_data *dev)
|
||||
{
|
||||
msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_PMIC_STS);
|
||||
return dev->reg_shadow[MSM_SPM_REG_SAW_PMIC_STS] & 0xFF;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MSM_AVS_HW
|
||||
static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
|
||||
{
|
||||
msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
|
||||
return dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] & BIT(0);
|
||||
}
|
||||
|
||||
static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev)
|
||||
{
|
||||
msm_spm_drv_load_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] &= ~BIT(27);
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
|
||||
}
|
||||
|
||||
static void msm_spm_drv_enable_avs(struct msm_spm_driver_data *dev)
|
||||
{
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] |= BIT(27);
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
|
||||
}
|
||||
|
||||
static void msm_spm_drv_set_avs_vlevel(struct msm_spm_driver_data *dev,
|
||||
unsigned int vlevel)
|
||||
{
|
||||
vlevel &= 0x3f;
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] &= ~0x7efc00;
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] |= ((vlevel - 4) << 10);
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_AVS_CTL] |= (vlevel << 17);
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_AVS_CTL);
|
||||
}
|
||||
|
||||
#else
|
||||
static bool msm_spm_drv_is_avs_enabled(struct msm_spm_driver_data *dev)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static void msm_spm_drv_disable_avs(struct msm_spm_driver_data *dev) { }
|
||||
|
||||
static void msm_spm_drv_enable_avs(struct msm_spm_driver_data *dev) { }
|
||||
|
||||
static void msm_spm_drv_set_avs_vlevel(struct msm_spm_driver_data *dev,
|
||||
unsigned int vlevel)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline int msm_spm_drv_validate_data(struct msm_spm_driver_data *dev,
|
||||
unsigned int vlevel, int vctl_port)
|
||||
{
|
||||
int timeout_us = dev->vctl_timeout_us;
|
||||
uint32_t new_level;
|
||||
|
||||
/* Confirm the voltage we set was what hardware sent and
|
||||
* FSM is idle.
|
||||
*/
|
||||
do {
|
||||
udelay(1);
|
||||
new_level = msm_spm_drv_get_sts_curr_pmic_data(dev);
|
||||
|
||||
/**
|
||||
* VCTL_PORT has to be 0, for vlevel to be updated.
|
||||
* If port is not 0, check for PMIC_STATE only.
|
||||
*/
|
||||
|
||||
if (((new_level & 0x30000) == MSM_SPM_PMIC_STATE_IDLE) &&
|
||||
(vctl_port || ((new_level & 0xFF) == vlevel)))
|
||||
break;
|
||||
} while (--timeout_us);
|
||||
|
||||
if (!timeout_us) {
|
||||
pr_err("Wrong level %#x\n", new_level);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
|
||||
pr_info("%s: done, remaining timeout %u us\n",
|
||||
__func__, timeout_us);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev, unsigned int vlevel)
|
||||
{
|
||||
uint32_t vlevel_set = vlevel;
|
||||
bool avs_enabled;
|
||||
int ret = 0;
|
||||
|
||||
if (!dev)
|
||||
return -EINVAL;
|
||||
|
||||
avs_enabled = msm_spm_drv_is_avs_enabled(dev);
|
||||
|
||||
if (!msm_spm_pmic_arb_present(dev))
|
||||
return -ENODEV;
|
||||
|
||||
if (msm_spm_debug_mask & MSM_SPM_DEBUG_VCTL)
|
||||
pr_info("%s: requesting vlevel %#x\n", __func__, vlevel);
|
||||
|
||||
if (avs_enabled)
|
||||
msm_spm_drv_disable_avs(dev);
|
||||
|
||||
if (dev->vctl_port_ub >= 0) {
|
||||
/**
|
||||
* VCTL can send 8bit voltage level at once.
|
||||
* Send lower 8bit first, vlevel change happens
|
||||
* when upper 8bit is sent.
|
||||
*/
|
||||
vlevel = vlevel_set & 0xFF;
|
||||
}
|
||||
|
||||
/* Kick the state machine back to idle */
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_RST] = 1;
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_RST);
|
||||
|
||||
msm_spm_drv_set_vctl2(dev, vlevel, dev->vctl_port);
|
||||
|
||||
ret = msm_spm_drv_validate_data(dev, vlevel, dev->vctl_port);
|
||||
if (ret)
|
||||
goto set_vdd_bail;
|
||||
|
||||
if (dev->vctl_port_ub >= 0) {
|
||||
/* Send upper 8bit of voltage level */
|
||||
vlevel = (vlevel_set >> 8) & 0xFF;
|
||||
|
||||
/* Kick the state machine back to idle */
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_RST] = 1;
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_RST);
|
||||
|
||||
/*
|
||||
* Steps for sending for vctl port other than '0'
|
||||
* Write VCTL register with pmic data and address index
|
||||
* Perform system barrier
|
||||
* Wait for 1us
|
||||
* Read PMIC_STS register to make sure operation is complete
|
||||
*/
|
||||
msm_spm_drv_set_vctl2(dev, vlevel, dev->vctl_port_ub);
|
||||
|
||||
mb(); /* To make sure data is sent before checking status */
|
||||
|
||||
ret = msm_spm_drv_validate_data(dev, vlevel, dev->vctl_port_ub);
|
||||
if (ret)
|
||||
goto set_vdd_bail;
|
||||
}
|
||||
|
||||
/* Set AVS min/max */
|
||||
if (avs_enabled) {
|
||||
msm_spm_drv_set_avs_vlevel(dev, vlevel_set);
|
||||
msm_spm_drv_enable_avs(dev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
set_vdd_bail:
|
||||
if (avs_enabled)
|
||||
msm_spm_drv_enable_avs(dev);
|
||||
|
||||
pr_err("%s: failed %#x vlevel setting in timeout %uus\n",
|
||||
__func__, vlevel_set, dev->vctl_timeout_us);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int msm_spm_drv_get_pmic_port(struct msm_spm_driver_data *dev,
|
||||
enum msm_spm_pmic_port port)
|
||||
{
|
||||
int index = -1;
|
||||
|
||||
switch (port) {
|
||||
case MSM_SPM_PMIC_VCTL_PORT:
|
||||
index = dev->vctl_port;
|
||||
break;
|
||||
case MSM_SPM_PMIC_PHASE_PORT:
|
||||
index = dev->phase_port;
|
||||
break;
|
||||
case MSM_SPM_PMIC_PFM_PORT:
|
||||
index = dev->pfm_port;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
|
||||
enum msm_spm_pmic_port port, unsigned int data)
|
||||
{
|
||||
unsigned int pmic_data = 0;
|
||||
unsigned int timeout_us = 0;
|
||||
int index = 0;
|
||||
|
||||
if (!msm_spm_pmic_arb_present(dev))
|
||||
return -ENODEV;
|
||||
|
||||
index = msm_spm_drv_get_pmic_port(dev, port);
|
||||
if (index < 0)
|
||||
return -ENODEV;
|
||||
|
||||
pmic_data |= data & 0xFF;
|
||||
pmic_data |= (index & 0x7) << 16;
|
||||
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] &= ~0x700FF;
|
||||
dev->reg_shadow[MSM_SPM_REG_SAW_VCTL] |= pmic_data;
|
||||
msm_spm_drv_flush_shadow(dev, MSM_SPM_REG_SAW_VCTL);
|
||||
mb(); /* Ensure we flush */
|
||||
|
||||
timeout_us = dev->vctl_timeout_us;
|
||||
/**
|
||||
* Confirm the pmic data set was what hardware sent by
|
||||
* checking the PMIC FSM state.
|
||||
* We cannot use the sts_pmic_data and check it against
|
||||
* the value like we do fot set_vdd, since the PMIC_STS
|
||||
* is only updated for SAW_VCTL sent with port index 0.
|
||||
*/
|
||||
do {
|
||||
if (msm_spm_drv_get_sts_pmic_state(dev) ==
|
||||
MSM_SPM_PMIC_STATE_IDLE)
|
||||
break;
|
||||
udelay(1);
|
||||
} while (--timeout_us);
|
||||
|
||||
if (!timeout_us) {
|
||||
pr_err("%s: failed, remaining timeout %u us, data %d\n",
|
||||
__func__, timeout_us, data);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void msm_spm_drv_reinit(struct msm_spm_driver_data *dev, bool seq_write)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (seq_write)
|
||||
msm_spm_drv_flush_seq_entry(dev);
|
||||
|
||||
for (i = 0; i < MSM_SPM_REG_SAW_PMIC_DATA_0 + num_pmic_data; i++)
|
||||
msm_spm_drv_load_shadow(dev, i);
|
||||
|
||||
for (i = MSM_SPM_REG_NR_INITIALIZE + 1; i < MSM_SPM_REG_NR; i++)
|
||||
msm_spm_drv_load_shadow(dev, i);
|
||||
}
|
||||
|
||||
int msm_spm_drv_reg_init(struct msm_spm_driver_data *dev,
|
||||
struct msm_spm_platform_data *data)
|
||||
{
|
||||
int i;
|
||||
bool found = false;
|
||||
|
||||
dev->ver_reg = data->ver_reg;
|
||||
dev->reg_base_addr = data->reg_base_addr;
|
||||
msm_spm_drv_get_saw2_ver(dev, &dev->major, &dev->minor);
|
||||
for (i = 0; i < ARRAY_SIZE(saw2_info); i++)
|
||||
if (dev->major == saw2_info[i].major &&
|
||||
dev->minor == saw2_info[i].minor) {
|
||||
pr_debug("%s: Version found\n",
|
||||
saw2_info[i].ver_name);
|
||||
dev->reg_offsets = saw2_info[i].spm_reg_offset_ptr;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
pr_err("%s: No SAW version found\n", __func__);
|
||||
WARN_ON(!found);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void msm_spm_drv_upd_reg_shadow(struct msm_spm_driver_data *dev, int id,
|
||||
int val)
|
||||
{
|
||||
dev->reg_shadow[id] = val;
|
||||
msm_spm_drv_flush_shadow(dev, id);
|
||||
/* Complete the above writes before other accesses */
|
||||
mb();
|
||||
}
|
||||
|
||||
int msm_spm_drv_init(struct msm_spm_driver_data *dev,
|
||||
struct msm_spm_platform_data *data)
|
||||
{
|
||||
int num_spm_entry;
|
||||
|
||||
if (!dev || !data)
|
||||
return -ENODEV;
|
||||
|
||||
dev->vctl_port = data->vctl_port;
|
||||
dev->vctl_port_ub = data->vctl_port_ub;
|
||||
dev->phase_port = data->phase_port;
|
||||
dev->pfm_port = data->pfm_port;
|
||||
dev->reg_base_addr = data->reg_base_addr;
|
||||
memcpy(dev->reg_shadow, data->reg_init_values,
|
||||
sizeof(data->reg_init_values));
|
||||
|
||||
dev->vctl_timeout_us = data->vctl_timeout_us;
|
||||
|
||||
|
||||
if (!num_pmic_data)
|
||||
num_pmic_data = msm_spm_drv_get_num_pmic_data(dev);
|
||||
|
||||
num_spm_entry = msm_spm_drv_get_num_spm_entry(dev);
|
||||
|
||||
dev->reg_seq_entry_shadow =
|
||||
kcalloc(num_spm_entry, sizeof(*dev->reg_seq_entry_shadow),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!dev->reg_seq_entry_shadow)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
71
drivers/soc/qcom/pm-boot.c
Normal file
71
drivers/soc/qcom/pm-boot.c
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2011-2014, 2016, 2018-2019, 2021 The Linux Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <linux/qcom_scm.h>
|
||||
#include "pm-boot.h"
|
||||
#include "idle.h"
|
||||
|
||||
#define CPU_INDEX(cluster, cpu) (cluster * MAX_CPUS_PER_CLUSTER + cpu)
|
||||
#define SCM_FLAG_WARMBOOT_MC 0x04
|
||||
|
||||
static void (*msm_pm_boot_before_pc)(unsigned int cpu, unsigned long entry);
|
||||
static void (*msm_pm_boot_after_pc)(unsigned int cpu);
|
||||
|
||||
static int msm_pm_tz_boot_init(void)
|
||||
{
|
||||
phys_addr_t warmboot_addr = msm_pm_boot_entry;
|
||||
|
||||
return qcom_scm_set_warm_boot_addr_mc(warmboot_addr, ~0U, ~0U, ~0U,
|
||||
SCM_FLAG_WARMBOOT_MC);
|
||||
}
|
||||
static void msm_pm_write_boot_vector(unsigned int cpu, unsigned long address)
|
||||
{
|
||||
uint32_t clust_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 1);
|
||||
uint32_t cpu_id = MPIDR_AFFINITY_LEVEL(cpu_logical_map(cpu), 0);
|
||||
unsigned long *start_address;
|
||||
unsigned long *end_address;
|
||||
|
||||
if (clust_id >= MAX_NUM_CLUSTER || cpu_id >= MAX_CPUS_PER_CLUSTER)
|
||||
WARN_ON(cpu);
|
||||
|
||||
msm_pm_boot_vector[CPU_INDEX(clust_id, cpu_id)] = address;
|
||||
start_address = &msm_pm_boot_vector[CPU_INDEX(clust_id, cpu_id)];
|
||||
end_address = &msm_pm_boot_vector[CPU_INDEX(clust_id, cpu_id + 1)];
|
||||
}
|
||||
|
||||
static void msm_pm_config_tz_before_pc(unsigned int cpu,
|
||||
unsigned long entry)
|
||||
{
|
||||
msm_pm_write_boot_vector(cpu, entry);
|
||||
}
|
||||
|
||||
void msm_pm_boot_config_before_pc(unsigned int cpu, unsigned long entry)
|
||||
{
|
||||
if (msm_pm_boot_before_pc)
|
||||
msm_pm_boot_before_pc(cpu, entry);
|
||||
}
|
||||
|
||||
void msm_pm_boot_config_after_pc(unsigned int cpu)
|
||||
{
|
||||
if (msm_pm_boot_after_pc)
|
||||
msm_pm_boot_after_pc(cpu);
|
||||
}
|
||||
|
||||
static int __init msm_pm_boot_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = msm_pm_tz_boot_init();
|
||||
msm_pm_boot_before_pc = msm_pm_config_tz_before_pc;
|
||||
msm_pm_boot_after_pc = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
late_initcall(msm_pm_boot_init);
|
||||
13
drivers/soc/qcom/pm-boot.h
Normal file
13
drivers/soc/qcom/pm-boot.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011-2014, 2018-2019, 2021 The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _ARCH_ARM_MACH_MSM_PM_BOOT_H
|
||||
#define _ARCH_ARM_MACH_MSM_PM_BOOT_H
|
||||
|
||||
void msm_pm_boot_config_before_pc(unsigned int cpu, unsigned long entry);
|
||||
void msm_pm_boot_config_after_pc(unsigned int cpu);
|
||||
|
||||
#endif
|
||||
1002
drivers/soc/qcom/spm_devices.c
Normal file
1002
drivers/soc/qcom/spm_devices.c
Normal file
File diff suppressed because it is too large
Load diff
131
drivers/soc/qcom/spm_driver.h
Normal file
131
drivers/soc/qcom/spm_driver.h
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011-2017, 2020-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __ARCH_ARM_MACH_MSM_SPM_DEVICES_H
|
||||
#define __ARCH_ARM_MACH_MSM_SPM_DEVICES_H
|
||||
|
||||
#include <soc/qcom/spm.h>
|
||||
|
||||
enum {
|
||||
MSM_SPM_REG_SAW_CFG,
|
||||
MSM_SPM_REG_SAW_AVS_CTL,
|
||||
MSM_SPM_REG_SAW_AVS_HYSTERESIS,
|
||||
MSM_SPM_REG_SAW_SPM_CTL,
|
||||
MSM_SPM_REG_SAW_PMIC_DLY,
|
||||
MSM_SPM_REG_SAW_AVS_LIMIT,
|
||||
MSM_SPM_REG_SAW_AVS_DLY,
|
||||
MSM_SPM_REG_SAW_SPM_DLY,
|
||||
MSM_SPM_REG_SAW_PMIC_DATA_0,
|
||||
MSM_SPM_REG_SAW_PMIC_DATA_1,
|
||||
MSM_SPM_REG_SAW_PMIC_DATA_2,
|
||||
MSM_SPM_REG_SAW_PMIC_DATA_3,
|
||||
MSM_SPM_REG_SAW_PMIC_DATA_4,
|
||||
MSM_SPM_REG_SAW_PMIC_DATA_5,
|
||||
MSM_SPM_REG_SAW_PMIC_DATA_6,
|
||||
MSM_SPM_REG_SAW_PMIC_DATA_7,
|
||||
MSM_SPM_REG_SAW_RST,
|
||||
|
||||
MSM_SPM_REG_NR_INITIALIZE = MSM_SPM_REG_SAW_RST,
|
||||
|
||||
MSM_SPM_REG_SAW_ID,
|
||||
MSM_SPM_REG_SAW_SECURE,
|
||||
MSM_SPM_REG_SAW_STS0,
|
||||
MSM_SPM_REG_SAW_STS1,
|
||||
MSM_SPM_REG_SAW_STS2,
|
||||
MSM_SPM_REG_SAW_VCTL,
|
||||
MSM_SPM_REG_SAW_SEQ_ENTRY,
|
||||
MSM_SPM_REG_SAW_SPM_STS,
|
||||
MSM_SPM_REG_SAW_AVS_STS,
|
||||
MSM_SPM_REG_SAW_PMIC_STS,
|
||||
MSM_SPM_REG_SAW_VERSION,
|
||||
|
||||
MSM_SPM_REG_NR,
|
||||
};
|
||||
|
||||
struct msm_spm_seq_entry {
|
||||
uint32_t mode;
|
||||
uint8_t *cmd;
|
||||
uint32_t ctl;
|
||||
};
|
||||
|
||||
struct msm_spm_platform_data {
|
||||
void __iomem *reg_base_addr;
|
||||
uint32_t reg_init_values[MSM_SPM_REG_NR_INITIALIZE];
|
||||
|
||||
uint32_t ver_reg;
|
||||
uint32_t vctl_port;
|
||||
int vctl_port_ub;
|
||||
uint32_t phase_port;
|
||||
uint32_t pfm_port;
|
||||
|
||||
uint8_t awake_vlevel;
|
||||
uint32_t vctl_timeout_us;
|
||||
uint32_t avs_timeout_us;
|
||||
|
||||
uint32_t num_modes;
|
||||
struct msm_spm_seq_entry *modes;
|
||||
};
|
||||
|
||||
enum msm_spm_pmic_port {
|
||||
MSM_SPM_PMIC_VCTL_PORT,
|
||||
MSM_SPM_PMIC_PHASE_PORT,
|
||||
MSM_SPM_PMIC_PFM_PORT,
|
||||
};
|
||||
|
||||
struct msm_spm_driver_data {
|
||||
uint32_t major;
|
||||
uint32_t minor;
|
||||
uint32_t ver_reg;
|
||||
uint32_t vctl_port;
|
||||
int vctl_port_ub;
|
||||
uint32_t phase_port;
|
||||
uint32_t pfm_port;
|
||||
void __iomem *reg_base_addr;
|
||||
uint32_t vctl_timeout_us;
|
||||
uint32_t avs_timeout_us;
|
||||
uint32_t reg_shadow[MSM_SPM_REG_NR];
|
||||
uint32_t *reg_seq_entry_shadow;
|
||||
uint32_t *reg_offsets;
|
||||
};
|
||||
|
||||
int msm_spm_drv_init(struct msm_spm_driver_data *dev,
|
||||
struct msm_spm_platform_data *data);
|
||||
int msm_spm_drv_reg_init(struct msm_spm_driver_data *dev,
|
||||
struct msm_spm_platform_data *data);
|
||||
void msm_spm_drv_reinit(struct msm_spm_driver_data *dev, bool seq);
|
||||
int msm_spm_drv_set_low_power_mode(struct msm_spm_driver_data *dev,
|
||||
uint32_t ctl);
|
||||
int msm_spm_drv_set_vdd(struct msm_spm_driver_data *dev,
|
||||
unsigned int vlevel);
|
||||
void dump_regs(struct msm_spm_driver_data *dev, int cpu);
|
||||
uint32_t msm_spm_drv_get_sts_curr_pmic_data(
|
||||
struct msm_spm_driver_data *dev);
|
||||
int msm_spm_drv_write_seq_data(struct msm_spm_driver_data *dev,
|
||||
uint8_t *cmd, uint32_t *offset);
|
||||
void msm_spm_drv_flush_seq_entry(struct msm_spm_driver_data *dev);
|
||||
int msm_spm_drv_set_spm_enable(struct msm_spm_driver_data *dev,
|
||||
bool enable);
|
||||
int msm_spm_drv_set_pmic_data(struct msm_spm_driver_data *dev,
|
||||
enum msm_spm_pmic_port port, unsigned int data);
|
||||
|
||||
int msm_spm_drv_set_avs_limit(struct msm_spm_driver_data *dev,
|
||||
uint32_t min_lvl, uint32_t max_lvl);
|
||||
|
||||
int msm_spm_drv_set_avs_enable(struct msm_spm_driver_data *dev,
|
||||
bool enable);
|
||||
int msm_spm_drv_get_avs_enable(struct msm_spm_driver_data *dev);
|
||||
|
||||
int msm_spm_drv_set_avs_irq_enable(struct msm_spm_driver_data *dev,
|
||||
enum msm_spm_avs_irq irq, bool enable);
|
||||
int msm_spm_drv_avs_clear_irq(struct msm_spm_driver_data *dev,
|
||||
enum msm_spm_avs_irq irq);
|
||||
|
||||
void msm_spm_reinit(void);
|
||||
int msm_spm_init(struct msm_spm_platform_data *data, int nr_devs);
|
||||
void msm_spm_drv_upd_reg_shadow(struct msm_spm_driver_data *dev, int id,
|
||||
int val);
|
||||
uint32_t msm_spm_drv_get_vdd(struct msm_spm_driver_data *dev);
|
||||
#endif
|
||||
173
include/soc/qcom/spm.h
Normal file
173
include/soc/qcom/spm.h
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2010-2017, 2020-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __ARCH_ARM_MACH_MSM_SPM_H
|
||||
#define __ARCH_ARM_MACH_MSM_SPM_H
|
||||
|
||||
enum {
|
||||
MSM_SPM_MODE_DISABLED,
|
||||
MSM_SPM_MODE_CLOCK_GATING,
|
||||
MSM_SPM_MODE_RETENTION,
|
||||
MSM_SPM_MODE_GDHS,
|
||||
MSM_SPM_MODE_POWER_COLLAPSE,
|
||||
MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE,
|
||||
MSM_SPM_MODE_FASTPC,
|
||||
MSM_SPM_MODE_NR
|
||||
};
|
||||
|
||||
enum msm_spm_avs_irq {
|
||||
MSM_SPM_AVS_IRQ_MIN,
|
||||
MSM_SPM_AVS_IRQ_MAX,
|
||||
};
|
||||
|
||||
struct msm_spm_device;
|
||||
struct device_node;
|
||||
|
||||
#if defined(CONFIG_MSM_SPM)
|
||||
|
||||
int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm);
|
||||
void msm_spm_set_rpm_hs(bool allow_rpm_hs);
|
||||
int msm_spm_probe_done(void);
|
||||
int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel);
|
||||
int msm_spm_get_vdd(unsigned int cpu);
|
||||
int msm_spm_turn_on_cpu_rail(struct device_node *l2ccc_node,
|
||||
unsigned int val, int cpu, int vctl_offset);
|
||||
struct msm_spm_device *msm_spm_get_device_by_name(const char *name);
|
||||
int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
|
||||
unsigned int mode, bool notify_rpm);
|
||||
int msm_spm_config_low_power_mode_addr(struct msm_spm_device *dev,
|
||||
unsigned int mode, bool notify_rpm);
|
||||
int msm_spm_device_init(void);
|
||||
bool msm_spm_is_mode_avail(unsigned int mode);
|
||||
void msm_spm_dump_regs(unsigned int cpu);
|
||||
int msm_spm_is_avs_enabled(unsigned int cpu);
|
||||
int msm_spm_avs_enable(unsigned int cpu);
|
||||
int msm_spm_avs_disable(unsigned int cpu);
|
||||
int msm_spm_avs_set_limit(unsigned int cpu, uint32_t min_lvl,
|
||||
uint32_t max_lvl);
|
||||
int msm_spm_avs_enable_irq(unsigned int cpu, enum msm_spm_avs_irq irq);
|
||||
int msm_spm_avs_disable_irq(unsigned int cpu, enum msm_spm_avs_irq irq);
|
||||
int msm_spm_avs_clear_irq(unsigned int cpu, enum msm_spm_avs_irq irq);
|
||||
|
||||
#if defined(CONFIG_MSM_L2_SPM)
|
||||
|
||||
/* Public functions */
|
||||
|
||||
int msm_spm_apcs_set_phase(int cpu, unsigned int phase_cnt);
|
||||
int msm_spm_enable_fts_lpm(int cpu, uint32_t mode);
|
||||
|
||||
#else
|
||||
|
||||
static inline int msm_spm_apcs_set_phase(int cpu, unsigned int phase_cnt)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int msm_spm_enable_fts_lpm(int cpu, uint32_t mode)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif /* defined(CONFIG_MSM_L2_SPM) */
|
||||
#else /* defined(CONFIG_MSM_SPM) */
|
||||
static inline int msm_spm_set_low_power_mode(unsigned int mode, bool notify_rpm)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline void msm_spm_set_rpm_hs(bool allow_rpm_hs) {}
|
||||
|
||||
static inline int msm_spm_probe_done(void)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int msm_spm_set_vdd(unsigned int cpu, unsigned int vlevel)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int msm_spm_get_vdd(unsigned int cpu)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int msm_spm_turn_on_cpu_rail(struct device_node *l2ccc_node,
|
||||
unsigned int val, int cpu, int vctl_offset)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int msm_spm_device_init(void)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline void msm_spm_dump_regs(unsigned int cpu)
|
||||
{ }
|
||||
|
||||
static inline int msm_spm_config_low_power_mode(struct msm_spm_device *dev,
|
||||
unsigned int mode, bool notify_rpm)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int msm_spm_config_low_power_mode_addr(
|
||||
struct msm_spm_device *dev, unsigned int mode, bool notify_rpm)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline struct msm_spm_device *msm_spm_get_device_by_name(
|
||||
const char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline bool msm_spm_is_mode_avail(unsigned int mode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int msm_spm_is_avs_enabled(unsigned int cpu)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int msm_spm_avs_enable(unsigned int cpu)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int msm_spm_avs_disable(unsigned int cpu)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int msm_spm_avs_set_limit(unsigned int cpu, uint32_t min_lvl,
|
||||
uint32_t max_lvl)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int msm_spm_avs_enable_irq(unsigned int cpu,
|
||||
enum msm_spm_avs_irq irq)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int msm_spm_avs_disable_irq(unsigned int cpu,
|
||||
enum msm_spm_avs_irq irq)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline int msm_spm_avs_clear_irq(unsigned int cpu,
|
||||
enum msm_spm_avs_irq irq)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#endif /* defined (CONFIG_MSM_SPM) */
|
||||
#endif /* __ARCH_ARM_MACH_MSM_SPM_H */
|
||||
Loading…
Reference in a new issue