aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/busfreq_lpddr2.c
blob: ac209e2a3cd098c012fd47ddff92454fedefef9b (plain) (blame)
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
/*
 * Copyright (C) 2011-2014 Freescale Semiconductor, Inc. All Rights Reserved.
 */

/*
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */

/*!
 * @file busfreq_lpddr2.c
 *
 * @brief iMX6 LPDDR2 frequency change specific file.
 *
 * @ingroup PM
 */
#include <asm/cacheflush.h>
#include <asm/fncpy.h>
#include <asm/io.h>
#include <asm/mach/map.h>
#include <asm/mach-types.h>
#include <asm/tlb.h>
#include <linux/clk.h>
#include <linux/cpumask.h>
#include <linux/delay.h>
#include <linux/genalloc.h>
#include <linux/interrupt.h>
#include <linux/irqchip/arm-gic.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/smp.h>

#include "hardware.h"


static struct device *busfreq_dev;
static void *ddr_freq_change_iram_base;
static int curr_ddr_rate;

unsigned long ddr_freq_change_iram_paddr;

void (*mx6_change_lpddr2_freq)(u32 ddr_freq, int bus_freq_mode) = NULL;

extern unsigned int ddr_normal_rate;
extern int low_bus_freq_mode;
extern int ultra_low_bus_freq_mode;
extern void mx6_lpddr2_freq_change(u32 freq, int bus_freq_mode);

extern unsigned long save_ttbr1(void);
extern void restore_ttbr1(unsigned long ttbr1);

/* change the DDR frequency. */
int update_lpddr2_freq(int ddr_rate)
{
	unsigned long ttbr1;
	if (ddr_rate == curr_ddr_rate)
		return 0;

	dev_dbg(busfreq_dev, "\nBus freq set to %d start...\n", ddr_rate);

	/*
	 * Flush the TLB, to ensure no TLB maintenance occurs
	 * when DDR is in self-refresh.
	 */
	ttbr1 = save_ttbr1();

	/* Now change DDR frequency. */
	mx6_change_lpddr2_freq(ddr_rate,
		(low_bus_freq_mode | ultra_low_bus_freq_mode));
	restore_ttbr1(ttbr1);

	curr_ddr_rate = ddr_rate;

	dev_dbg(busfreq_dev, "\nBus freq set to %d done...\n", ddr_rate);

	return 0;
}

int init_mmdc_lpddr2_settings(struct platform_device *busfreq_pdev)
{
	busfreq_dev = &busfreq_pdev->dev;

	ddr_freq_change_iram_paddr = MX6SL_LPDDR2_FREQ_ADDR;
	/* Calculate the virtual address of the code */
	ddr_freq_change_iram_base =
			(void *)IMX_IO_P2V(MX6Q_IRAM_TLB_BASE_ADDR) +
			(ddr_freq_change_iram_paddr - MX6Q_IRAM_TLB_BASE_ADDR);

	mx6_change_lpddr2_freq = (void *)fncpy(ddr_freq_change_iram_base,
		&mx6_lpddr2_freq_change, LPDDR2_FREQ_CODE_SIZE);

	curr_ddr_rate = ddr_normal_rate;

	return 0;
}