blob: c77022900886a09155b78992d58ad9f1487b1a55 [file] [log] [blame]
/*
* Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved.
* Copyright 2017-2018 NXP.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <arch.h>
#include <arch_helpers.h>
#include <debug.h>
#include <stdbool.h>
#include <ddrc.h>
#include <mmio.h>
#include <platform_def.h>
#include <spinlock.h>
#include <soc.h>
#include "lpddr4_dvfs.h"
#define DDRC_LPDDR4 (1 << 5)
#define DDR_TYPE_MASK 0x3f
/* lock used for DDR DVFS */
spinlock_t dfs_lock;
/* IRQ used for DDR DVFS */
static uint32_t irqs_used [] = {102, 109, 110, 111};
static volatile uint32_t wfe_done;
static volatile bool wait_ddrc_hwffc_done = true;
static unsigned int init_fsp = 0x1;
static inline int get_ddr_type(void)
{
return mmio_read_32(DDRC_MSTR(0)) & DDR_TYPE_MASK;
}
void lpddr4_switch_to_3200(void)
{
if (get_ddr_type() == DDRC_LPDDR4)
lpddr4_dvfs_swffc(init_fsp, 0x0);
}
/*
* x1: for DDR frequency target info
* x2: for online cpu info.
*/
int lpddr4_dvfs_handler(uint32_t smc_fid,
u_register_t x1,
u_register_t x2,
u_register_t x3)
{
uint64_t mpidr = read_mpidr_el1();
unsigned int cpu_id = MPIDR_AFFLVL0_VAL(mpidr);
unsigned int target_freq = x1;
uint32_t online_cores = x2;
if (target_freq == 0xf) {
/* set the WFE done status */
spin_lock(&dfs_lock);
wfe_done |= (1 << cpu_id * 8);
spin_unlock(&dfs_lock);
while (1) {
/* ddr frequency change done */
wfe();
if (!wait_ddrc_hwffc_done) {
break;
}
}
} else {
wait_ddrc_hwffc_done = true;
/* trigger the IRQ */
for (int i = 0; i < 4; i++) {
int irq = irqs_used[i] % 32;
if (cpu_id != i && (online_cores & (0x1 << (i * 8)))) {
mmio_write_32(0x38800204 + (irqs_used[i] / 32) * 4, (1 << irq));
}
}
/* make sure all the core in WFE */
online_cores &= ~(0x1 << (cpu_id * 8));
while (1) {
mmio_write_32(0x30340004, mmio_read_32(0x30340004) | (1 << 12));
if (online_cores == wfe_done)
break;
}
mmio_write_32(0x30340004, mmio_read_32(0x30340004) & ~(1 << 12));
/* flush the L1/L2 cache */
dcsw_op_all(DCCSW);
/*
* for DDR dvfs, we have two method: hwffc or swffc,
* the default method we used on i.MX8MQ is swffc.
*/
#if LPDDR4_DVFS_HWFFC
lpddr4_dvfs_hwffc(init_vrcg, init_fsp, target_freq, discamdrain);
init_fsp = (~init_fsp) & 0x1;
#else
lpddr4_dvfs_swffc(init_fsp, target_freq);
#endif
wait_ddrc_hwffc_done = false;
wfe_done = 0;
dsb();
sev();
isb();
}
return 0;
}