summaryrefslogtreecommitdiff
path: root/plat/imx/imx8mq/ddr/lpddr4_dvfs.c
blob: 7adbf9242de6d5f60e6cd826280ece4acdd06d47 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/*
 * 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;
}