ARM: imx: add i.MX7D bus-freq support
Add i.MX7D bus-freq support, it supports High/Low/Audio bus mode. Signed-off-by: Anson Huang <Anson.Huang@nxp.com>5.4-rM2-2.2.x-imx-squashed
parent
a4f408b44e
commit
dc782dd284
|
@ -43,6 +43,13 @@ config HAVE_IMX_GPC
|
||||||
config HAVE_IMX_MMDC
|
config HAVE_IMX_MMDC
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
config HAVE_IMX_DDRC
|
||||||
|
bool
|
||||||
|
select HAVE_IMX_BUSFREQ
|
||||||
|
|
||||||
|
config HAVE_IMX_BUSFREQ
|
||||||
|
bool
|
||||||
|
|
||||||
config HAVE_IMX_SRC
|
config HAVE_IMX_SRC
|
||||||
def_bool y if SMP
|
def_bool y if SMP
|
||||||
select ARCH_HAS_RESET_CONTROLLER
|
select ARCH_HAS_RESET_CONTROLLER
|
||||||
|
@ -546,6 +553,7 @@ config SOC_IMX7D_CA7
|
||||||
select HAVE_IMX_MMDC
|
select HAVE_IMX_MMDC
|
||||||
select HAVE_IMX_SRC
|
select HAVE_IMX_SRC
|
||||||
select IMX_GPCV2
|
select IMX_GPCV2
|
||||||
|
select HAVE_IMX_DDRC
|
||||||
|
|
||||||
config SOC_IMX7D_CM4
|
config SOC_IMX7D_CM4
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
obj-y := cpu.o system.o irq-common.o
|
obj-y := cpu.o system.o irq-common.o common.o
|
||||||
|
|
||||||
obj-$(CONFIG_SOC_IMX21) += mm-imx21.o
|
obj-$(CONFIG_SOC_IMX21) += mm-imx21.o
|
||||||
|
|
||||||
|
@ -72,6 +72,7 @@ obj-$(CONFIG_HAVE_IMX_ANATOP) += anatop.o
|
||||||
obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
|
obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o
|
||||||
obj-$(CONFIG_HAVE_IMX_MMDC) += mmdc.o
|
obj-$(CONFIG_HAVE_IMX_MMDC) += mmdc.o
|
||||||
obj-$(CONFIG_HAVE_IMX_SRC) += src.o
|
obj-$(CONFIG_HAVE_IMX_SRC) += src.o
|
||||||
|
obj-$(CONFIG_HAVE_IMX_DDRC) += ddrc.o
|
||||||
ifneq ($(CONFIG_SOC_IMX6)$(CONFIG_SOC_LS1021A),)
|
ifneq ($(CONFIG_SOC_IMX6)$(CONFIG_SOC_LS1021A),)
|
||||||
AFLAGS_headsmp.o :=-Wa,-march=armv7-a
|
AFLAGS_headsmp.o :=-Wa,-march=armv7-a
|
||||||
obj-$(CONFIG_SMP) += headsmp.o platsmp.o
|
obj-$(CONFIG_SMP) += headsmp.o platsmp.o
|
||||||
|
@ -82,10 +83,16 @@ obj-$(CONFIG_SOC_IMX6SL) += mach-imx6sl.o
|
||||||
obj-$(CONFIG_SOC_IMX6SLL) += mach-imx6sl.o
|
obj-$(CONFIG_SOC_IMX6SLL) += mach-imx6sl.o
|
||||||
obj-$(CONFIG_SOC_IMX6SX) += mach-imx6sx.o
|
obj-$(CONFIG_SOC_IMX6SX) += mach-imx6sx.o
|
||||||
obj-$(CONFIG_SOC_IMX6UL) += mach-imx6ul.o
|
obj-$(CONFIG_SOC_IMX6UL) += mach-imx6ul.o
|
||||||
obj-$(CONFIG_SOC_IMX7D_CA7) += mach-imx7d.o
|
obj-$(CONFIG_SOC_IMX7D_CA7) += mach-imx7d.o pm-imx7.o ddr3_freq_imx7d.o smp_wfe.o \
|
||||||
|
lpddr3_freq_imx.o
|
||||||
obj-$(CONFIG_SOC_IMX7D_CM4) += mach-imx7d-cm4.o
|
obj-$(CONFIG_SOC_IMX7D_CM4) += mach-imx7d-cm4.o
|
||||||
obj-$(CONFIG_SOC_IMX7ULP) += mach-imx7ulp.o pm-imx7ulp.o
|
obj-$(CONFIG_SOC_IMX7ULP) += mach-imx7ulp.o pm-imx7ulp.o
|
||||||
|
|
||||||
|
obj-y += busfreq-imx.o busfreq_ddr3.o
|
||||||
|
AFLAGS_smp_wfe.o :=-Wa,-march=armv7-a
|
||||||
|
AFLAGS_ddr3_freq_imx7d.o :=-Wa,-march=armv7-a
|
||||||
|
AFLAGS_lpddr3_freq_imx.o :=-Wa,-march=armv7-a
|
||||||
|
|
||||||
ifeq ($(CONFIG_SUSPEND),y)
|
ifeq ($(CONFIG_SUSPEND),y)
|
||||||
AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
|
AFLAGS_suspend-imx6.o :=-Wa,-march=armv7-a
|
||||||
obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o
|
obj-$(CONFIG_SOC_IMX6) += suspend-imx6.o
|
||||||
|
|
|
@ -0,0 +1,764 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2016 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||||
|
* Copyright 2017 NXP.
|
||||||
|
* Copyright 2018 NXP.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
|
||||||
|
* 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 <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/busfreq-imx.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/clk-provider.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_fdt.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/proc_fs.h>
|
||||||
|
#include <linux/reboot.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/suspend.h>
|
||||||
|
#include "hardware.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define LPAPM_CLK 24000000
|
||||||
|
#define LOW_AUDIO_CLK 50000000
|
||||||
|
#define HIGH_AUDIO_CLK 100000000
|
||||||
|
|
||||||
|
#define LOW_POWER_RUN_VOLTAGE 950000
|
||||||
|
|
||||||
|
#define MMDC_MDMISC_DDR_TYPE_DDR3 0
|
||||||
|
#define MMDC_MDMISC_DDR_TYPE_LPDDR2 1
|
||||||
|
|
||||||
|
unsigned int ddr_med_rate;
|
||||||
|
unsigned int ddr_normal_rate;
|
||||||
|
unsigned long ddr_freq_change_total_size;
|
||||||
|
unsigned long ddr_freq_change_iram_base;
|
||||||
|
unsigned long ddr_freq_change_iram_phys;
|
||||||
|
|
||||||
|
static int ddr_type;
|
||||||
|
static int low_bus_freq_mode;
|
||||||
|
static int audio_bus_freq_mode;
|
||||||
|
static int ultra_low_bus_freq_mode;
|
||||||
|
static int high_bus_freq_mode;
|
||||||
|
static int med_bus_freq_mode;
|
||||||
|
static int bus_freq_scaling_initialized;
|
||||||
|
static bool cancel_reduce_bus_freq;
|
||||||
|
static struct device *busfreq_dev;
|
||||||
|
static int busfreq_suspended;
|
||||||
|
static int bus_freq_scaling_is_active;
|
||||||
|
static int high_bus_count, med_bus_count, audio_bus_count, low_bus_count;
|
||||||
|
static unsigned int ddr_low_rate;
|
||||||
|
static int cur_bus_freq_mode;
|
||||||
|
|
||||||
|
extern unsigned long iram_tlb_phys_addr;
|
||||||
|
extern int unsigned long iram_tlb_base_addr;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Bus frequency management by Linux
|
||||||
|
*/
|
||||||
|
extern int init_mmdc_lpddr2_settings(struct platform_device *dev);
|
||||||
|
extern int init_mmdc_lpddr2_settings_mx6q(struct platform_device *dev);
|
||||||
|
extern int init_mmdc_ddr3_settings_imx6_up(struct platform_device *dev);
|
||||||
|
extern int init_mmdc_ddr3_settings_imx6_smp(struct platform_device *dev);
|
||||||
|
extern int init_ddrc_ddr_settings(struct platform_device *dev);
|
||||||
|
extern int update_ddr_freq_imx_smp(int ddr_rate);
|
||||||
|
extern int update_ddr_freq_imx6_up(int ddr_rate);
|
||||||
|
extern int update_lpddr2_freq(int ddr_rate);
|
||||||
|
extern int update_lpddr2_freq_smp(int ddr_rate);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Functions to init and update the busfreq function of
|
||||||
|
* device and memory type
|
||||||
|
*/
|
||||||
|
static struct busfreq_func {
|
||||||
|
int (*init)(struct platform_device *dev);
|
||||||
|
int (*update)(int ddr_rate);
|
||||||
|
} busfreq_func = {NULL, NULL};
|
||||||
|
|
||||||
|
DEFINE_MUTEX(bus_freq_mutex);
|
||||||
|
|
||||||
|
static struct clk *osc_clk;
|
||||||
|
static struct clk *ahb_clk;
|
||||||
|
static struct clk *axi_sel_clk;
|
||||||
|
static struct clk *dram_root;
|
||||||
|
static struct clk *dram_alt_sel;
|
||||||
|
static struct clk *dram_alt_root;
|
||||||
|
static struct clk *pfd0_392m;
|
||||||
|
static struct clk *pfd2_270m;
|
||||||
|
static struct clk *pfd1_332m;
|
||||||
|
static struct clk *pll_dram;
|
||||||
|
static struct clk *ahb_sel_clk;
|
||||||
|
static struct clk *axi_clk;
|
||||||
|
|
||||||
|
static struct delayed_work low_bus_freq_handler;
|
||||||
|
static struct delayed_work bus_freq_daemon;
|
||||||
|
|
||||||
|
static RAW_NOTIFIER_HEAD(busfreq_notifier_chain);
|
||||||
|
|
||||||
|
static bool busfreq_notified_low = false;
|
||||||
|
|
||||||
|
static int busfreq_notify(enum busfreq_event event)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (event == LOW_BUSFREQ_ENTER) {
|
||||||
|
WARN_ON(busfreq_notified_low);
|
||||||
|
busfreq_notified_low = true;
|
||||||
|
} else if (event == LOW_BUSFREQ_EXIT) {
|
||||||
|
WARN_ON(!busfreq_notified_low);
|
||||||
|
busfreq_notified_low = false;
|
||||||
|
}
|
||||||
|
ret = raw_notifier_call_chain(&busfreq_notifier_chain, event, NULL);
|
||||||
|
|
||||||
|
return notifier_to_errno(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
int register_busfreq_notifier(struct notifier_block *nb)
|
||||||
|
{
|
||||||
|
return raw_notifier_chain_register(&busfreq_notifier_chain, nb);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(register_busfreq_notifier);
|
||||||
|
|
||||||
|
int unregister_busfreq_notifier(struct notifier_block *nb)
|
||||||
|
{
|
||||||
|
return raw_notifier_chain_unregister(&busfreq_notifier_chain, nb);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(unregister_busfreq_notifier);
|
||||||
|
|
||||||
|
static void enter_lpm_imx7d(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The AHB clock parent switch and divider change
|
||||||
|
* needs to keep previous/current parent enabled
|
||||||
|
* per design requirement, but when we switch the
|
||||||
|
* clock parent, previous AHB clock parent may be
|
||||||
|
* disabled by common clock framework, so here we
|
||||||
|
* have to make sure AHB's previous parent pfd2_270m
|
||||||
|
* is enabled during AHB set rate.
|
||||||
|
*/
|
||||||
|
clk_prepare_enable(pfd2_270m);
|
||||||
|
if (audio_bus_count) {
|
||||||
|
clk_prepare_enable(pfd0_392m);
|
||||||
|
busfreq_func.update(HIGH_AUDIO_CLK);
|
||||||
|
|
||||||
|
clk_set_parent(dram_alt_sel, pfd0_392m);
|
||||||
|
clk_set_parent(dram_root, dram_alt_root);
|
||||||
|
if (high_bus_freq_mode) {
|
||||||
|
clk_set_parent(axi_sel_clk, osc_clk);
|
||||||
|
clk_set_parent(ahb_sel_clk, osc_clk);
|
||||||
|
clk_set_rate(ahb_clk, LPAPM_CLK);
|
||||||
|
}
|
||||||
|
clk_disable_unprepare(pfd0_392m);
|
||||||
|
audio_bus_freq_mode = 1;
|
||||||
|
low_bus_freq_mode = 0;
|
||||||
|
cur_bus_freq_mode = BUS_FREQ_AUDIO;
|
||||||
|
} else {
|
||||||
|
busfreq_func.update(LPAPM_CLK);
|
||||||
|
|
||||||
|
clk_set_parent(dram_alt_sel, osc_clk);
|
||||||
|
clk_set_parent(dram_root, dram_alt_root);
|
||||||
|
if (high_bus_freq_mode) {
|
||||||
|
clk_set_parent(axi_sel_clk, osc_clk);
|
||||||
|
clk_set_parent(ahb_sel_clk, osc_clk);
|
||||||
|
clk_set_rate(ahb_clk, LPAPM_CLK);
|
||||||
|
}
|
||||||
|
low_bus_freq_mode = 1;
|
||||||
|
audio_bus_freq_mode = 0;
|
||||||
|
cur_bus_freq_mode = BUS_FREQ_LOW;
|
||||||
|
}
|
||||||
|
clk_disable_unprepare(pfd2_270m);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exit_lpm_imx7d(void)
|
||||||
|
{
|
||||||
|
clk_set_parent(axi_sel_clk, pfd1_332m);
|
||||||
|
clk_set_rate(ahb_clk, LPAPM_CLK / 2);
|
||||||
|
clk_set_parent(ahb_sel_clk, pfd2_270m);
|
||||||
|
|
||||||
|
busfreq_func.update(ddr_normal_rate);
|
||||||
|
|
||||||
|
clk_set_parent(dram_root, pll_dram);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reduce_bus_freq(void)
|
||||||
|
{
|
||||||
|
if (audio_bus_count && (low_bus_freq_mode || ultra_low_bus_freq_mode))
|
||||||
|
busfreq_notify(LOW_BUSFREQ_EXIT);
|
||||||
|
else if (!audio_bus_count)
|
||||||
|
busfreq_notify(LOW_BUSFREQ_ENTER);
|
||||||
|
|
||||||
|
if (cpu_is_imx7d())
|
||||||
|
enter_lpm_imx7d();
|
||||||
|
|
||||||
|
med_bus_freq_mode = 0;
|
||||||
|
high_bus_freq_mode = 0;
|
||||||
|
|
||||||
|
if (audio_bus_freq_mode)
|
||||||
|
dev_dbg(busfreq_dev,
|
||||||
|
"Bus freq set to audio mode. Count: high %d, med %d, audio %d\n",
|
||||||
|
high_bus_count, med_bus_count, audio_bus_count);
|
||||||
|
if (low_bus_freq_mode)
|
||||||
|
dev_dbg(busfreq_dev,
|
||||||
|
"Bus freq set to low mode. Count: high %d, med %d, audio %d\n",
|
||||||
|
high_bus_count, med_bus_count, audio_bus_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void cancel_low_bus_freq_handler(void)
|
||||||
|
{
|
||||||
|
cancel_delayed_work(&low_bus_freq_handler);
|
||||||
|
cancel_reduce_bus_freq = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reduce_bus_freq_handler(struct work_struct *work)
|
||||||
|
{
|
||||||
|
mutex_lock(&bus_freq_mutex);
|
||||||
|
|
||||||
|
if (!cancel_reduce_bus_freq) {
|
||||||
|
reduce_bus_freq();
|
||||||
|
cancel_low_bus_freq_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the DDR, AHB to 24MHz.
|
||||||
|
* This mode will be activated only when none of the modules that
|
||||||
|
* need a higher DDR or AHB frequency are active.
|
||||||
|
*/
|
||||||
|
static int set_low_bus_freq(void)
|
||||||
|
{
|
||||||
|
if (busfreq_suspended)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cancel_reduce_bus_freq = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check to see if we need to got from
|
||||||
|
* low bus freq mode to audio bus freq mode.
|
||||||
|
* If so, the change needs to be done immediately.
|
||||||
|
*/
|
||||||
|
if (audio_bus_count && (low_bus_freq_mode || ultra_low_bus_freq_mode))
|
||||||
|
reduce_bus_freq();
|
||||||
|
else
|
||||||
|
/*
|
||||||
|
* Don't lower the frequency immediately. Instead
|
||||||
|
* scheduled a delayed work and drop the freq if
|
||||||
|
* the conditions still remain the same.
|
||||||
|
*/
|
||||||
|
schedule_delayed_work(&low_bus_freq_handler,
|
||||||
|
usecs_to_jiffies(3000000));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the DDR to either 528MHz or 400MHz for iMX6qd
|
||||||
|
* or 400MHz for iMX6dl.
|
||||||
|
*/
|
||||||
|
static int set_high_bus_freq(int high_bus_freq)
|
||||||
|
{
|
||||||
|
if (bus_freq_scaling_initialized && bus_freq_scaling_is_active)
|
||||||
|
cancel_low_bus_freq_handler();
|
||||||
|
|
||||||
|
if (busfreq_suspended)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (high_bus_freq_mode)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* medium bus freq is only supported for MX6DQ */
|
||||||
|
if (med_bus_freq_mode && !high_bus_freq)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (low_bus_freq_mode || ultra_low_bus_freq_mode)
|
||||||
|
busfreq_notify(LOW_BUSFREQ_EXIT);
|
||||||
|
|
||||||
|
if (cpu_is_imx7d())
|
||||||
|
exit_lpm_imx7d();
|
||||||
|
|
||||||
|
high_bus_freq_mode = 1;
|
||||||
|
med_bus_freq_mode = 0;
|
||||||
|
low_bus_freq_mode = 0;
|
||||||
|
audio_bus_freq_mode = 0;
|
||||||
|
cur_bus_freq_mode = BUS_FREQ_HIGH;
|
||||||
|
|
||||||
|
if (high_bus_freq_mode)
|
||||||
|
dev_dbg(busfreq_dev,
|
||||||
|
"Bus freq set to high mode. Count: high %d, med %d, audio %d\n",
|
||||||
|
high_bus_count, med_bus_count, audio_bus_count);
|
||||||
|
if (med_bus_freq_mode)
|
||||||
|
dev_dbg(busfreq_dev,
|
||||||
|
"Bus freq set to med mode. Count: high %d, med %d, audio %d\n",
|
||||||
|
high_bus_count, med_bus_count, audio_bus_count);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void request_bus_freq(enum bus_freq_mode mode)
|
||||||
|
{
|
||||||
|
mutex_lock(&bus_freq_mutex);
|
||||||
|
|
||||||
|
if (mode == BUS_FREQ_ULTRA_LOW) {
|
||||||
|
dev_dbg(busfreq_dev, "This mode cannot be requested!\n");
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == BUS_FREQ_HIGH)
|
||||||
|
high_bus_count++;
|
||||||
|
else if (mode == BUS_FREQ_MED)
|
||||||
|
med_bus_count++;
|
||||||
|
else if (mode == BUS_FREQ_AUDIO)
|
||||||
|
audio_bus_count++;
|
||||||
|
else if (mode == BUS_FREQ_LOW)
|
||||||
|
low_bus_count++;
|
||||||
|
|
||||||
|
if (busfreq_suspended || !bus_freq_scaling_initialized ||
|
||||||
|
!bus_freq_scaling_is_active) {
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel_low_bus_freq_handler();
|
||||||
|
|
||||||
|
if ((mode == BUS_FREQ_HIGH) && (!high_bus_freq_mode)) {
|
||||||
|
set_high_bus_freq(1);
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((mode == BUS_FREQ_MED) && (!high_bus_freq_mode) &&
|
||||||
|
(!med_bus_freq_mode)) {
|
||||||
|
set_high_bus_freq(0);
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((mode == BUS_FREQ_AUDIO) && (!high_bus_freq_mode) &&
|
||||||
|
(!med_bus_freq_mode) && (!audio_bus_freq_mode)) {
|
||||||
|
set_low_bus_freq();
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(request_bus_freq);
|
||||||
|
|
||||||
|
void release_bus_freq(enum bus_freq_mode mode)
|
||||||
|
{
|
||||||
|
mutex_lock(&bus_freq_mutex);
|
||||||
|
|
||||||
|
if (mode == BUS_FREQ_ULTRA_LOW) {
|
||||||
|
dev_dbg(busfreq_dev,
|
||||||
|
"This mode cannot be released!\n");
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == BUS_FREQ_HIGH) {
|
||||||
|
if (high_bus_count == 0) {
|
||||||
|
dev_err(busfreq_dev, "high bus count mismatch!\n");
|
||||||
|
dump_stack();
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
high_bus_count--;
|
||||||
|
} else if (mode == BUS_FREQ_MED) {
|
||||||
|
if (med_bus_count == 0) {
|
||||||
|
dev_err(busfreq_dev, "med bus count mismatch!\n");
|
||||||
|
dump_stack();
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
med_bus_count--;
|
||||||
|
} else if (mode == BUS_FREQ_AUDIO) {
|
||||||
|
if (audio_bus_count == 0) {
|
||||||
|
dev_err(busfreq_dev, "audio bus count mismatch!\n");
|
||||||
|
dump_stack();
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
audio_bus_count--;
|
||||||
|
} else if (mode == BUS_FREQ_LOW) {
|
||||||
|
if (low_bus_count == 0) {
|
||||||
|
dev_err(busfreq_dev, "low bus count mismatch!\n");
|
||||||
|
dump_stack();
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
low_bus_count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (busfreq_suspended || !bus_freq_scaling_initialized ||
|
||||||
|
!bus_freq_scaling_is_active) {
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!audio_bus_freq_mode) && (high_bus_count == 0) &&
|
||||||
|
(med_bus_count == 0) && (audio_bus_count != 0)) {
|
||||||
|
set_low_bus_freq();
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((!low_bus_freq_mode) && (high_bus_count == 0) &&
|
||||||
|
(med_bus_count == 0) && (audio_bus_count == 0) &&
|
||||||
|
(low_bus_count != 0)) {
|
||||||
|
set_low_bus_freq();
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((!ultra_low_bus_freq_mode) && (high_bus_count == 0) &&
|
||||||
|
(med_bus_count == 0) && (audio_bus_count == 0) &&
|
||||||
|
(low_bus_count == 0)) {
|
||||||
|
set_low_bus_freq();
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(release_bus_freq);
|
||||||
|
|
||||||
|
int get_bus_freq_mode(void)
|
||||||
|
{
|
||||||
|
return cur_bus_freq_mode;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(get_bus_freq_mode);
|
||||||
|
|
||||||
|
static struct map_desc ddr_iram_io_desc __initdata = {
|
||||||
|
/* .virtual and .pfn are run-time assigned */
|
||||||
|
.length = SZ_1M,
|
||||||
|
.type = MT_MEMORY_RWX_NONCACHED,
|
||||||
|
};
|
||||||
|
|
||||||
|
const static char *ddr_freq_iram_match[] __initconst = {
|
||||||
|
"fsl,ddr-lpm-sram",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init imx_dt_find_ddr_sram(unsigned long node,
|
||||||
|
const char *uname, int depth, void *data)
|
||||||
|
{
|
||||||
|
unsigned long ddr_iram_addr;
|
||||||
|
const __be32 *prop;
|
||||||
|
|
||||||
|
if (of_flat_dt_match(node, ddr_freq_iram_match)) {
|
||||||
|
unsigned int len;
|
||||||
|
|
||||||
|
prop = of_get_flat_dt_prop(node, "reg", &len);
|
||||||
|
if (prop == NULL || len != (sizeof(unsigned long) * 2))
|
||||||
|
return -EINVAL;
|
||||||
|
ddr_iram_addr = be32_to_cpu(prop[0]);
|
||||||
|
ddr_freq_change_total_size = be32_to_cpu(prop[1]);
|
||||||
|
ddr_freq_change_iram_phys = ddr_iram_addr;
|
||||||
|
|
||||||
|
/* Make sure ddr_freq_change_iram_phys is 8 byte aligned. */
|
||||||
|
if ((uintptr_t)(ddr_freq_change_iram_phys) & (FNCPY_ALIGN - 1))
|
||||||
|
ddr_freq_change_iram_phys += FNCPY_ALIGN -
|
||||||
|
((uintptr_t)ddr_freq_change_iram_phys %
|
||||||
|
(FNCPY_ALIGN));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __init imx_busfreq_map_io(void)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Get the address of IRAM to be used by the ddr frequency
|
||||||
|
* change code from the device tree.
|
||||||
|
*/
|
||||||
|
WARN_ON(of_scan_flat_dt(imx_dt_find_ddr_sram, NULL));
|
||||||
|
if (ddr_freq_change_iram_phys) {
|
||||||
|
ddr_freq_change_iram_base = IMX_IO_P2V(
|
||||||
|
ddr_freq_change_iram_phys);
|
||||||
|
if ((iram_tlb_phys_addr & 0xFFF00000) !=
|
||||||
|
(ddr_freq_change_iram_phys & 0xFFF00000)) {
|
||||||
|
/* We need to create a 1M page table entry. */
|
||||||
|
ddr_iram_io_desc.virtual = IMX_IO_P2V(
|
||||||
|
ddr_freq_change_iram_phys & 0xFFF00000);
|
||||||
|
ddr_iram_io_desc.pfn = __phys_to_pfn(
|
||||||
|
ddr_freq_change_iram_phys & 0xFFF00000);
|
||||||
|
iotable_init(&ddr_iram_io_desc, 1);
|
||||||
|
}
|
||||||
|
memset((void *)ddr_freq_change_iram_base, 0,
|
||||||
|
ddr_freq_change_total_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bus_freq_daemon_handler(struct work_struct *work)
|
||||||
|
{
|
||||||
|
mutex_lock(&bus_freq_mutex);
|
||||||
|
if ((!low_bus_freq_mode) && (!ultra_low_bus_freq_mode)
|
||||||
|
&& (high_bus_count == 0) &&
|
||||||
|
(med_bus_count == 0) && (audio_bus_count == 0))
|
||||||
|
set_low_bus_freq();
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t bus_freq_scaling_enable_show(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
if (bus_freq_scaling_is_active)
|
||||||
|
return sprintf(buf, "Bus frequency scaling is enabled\n");
|
||||||
|
else
|
||||||
|
return sprintf(buf, "Bus frequency scaling is disabled\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t bus_freq_scaling_enable_store(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t size)
|
||||||
|
{
|
||||||
|
if (strncmp(buf, "1", 1) == 0) {
|
||||||
|
bus_freq_scaling_is_active = 1;
|
||||||
|
set_high_bus_freq(1);
|
||||||
|
/*
|
||||||
|
* We set bus freq to highest at the beginning,
|
||||||
|
* so we use this daemon thread to make sure system
|
||||||
|
* can enter low bus mode if
|
||||||
|
* there is no high bus request pending
|
||||||
|
*/
|
||||||
|
schedule_delayed_work(&bus_freq_daemon,
|
||||||
|
usecs_to_jiffies(5000000));
|
||||||
|
} else if (strncmp(buf, "0", 1) == 0) {
|
||||||
|
if (bus_freq_scaling_is_active)
|
||||||
|
set_high_bus_freq(1);
|
||||||
|
bus_freq_scaling_is_active = 0;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bus_freq_pm_notify(struct notifier_block *nb, unsigned long event,
|
||||||
|
void *dummy)
|
||||||
|
{
|
||||||
|
mutex_lock(&bus_freq_mutex);
|
||||||
|
|
||||||
|
if (event == PM_SUSPEND_PREPARE) {
|
||||||
|
high_bus_count++;
|
||||||
|
set_high_bus_freq(1);
|
||||||
|
busfreq_suspended = 1;
|
||||||
|
} else if (event == PM_POST_SUSPEND) {
|
||||||
|
busfreq_suspended = 0;
|
||||||
|
high_bus_count--;
|
||||||
|
schedule_delayed_work(&bus_freq_daemon,
|
||||||
|
usecs_to_jiffies(5000000));
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&bus_freq_mutex);
|
||||||
|
|
||||||
|
return NOTIFY_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int busfreq_reboot_notifier_event(struct notifier_block *this,
|
||||||
|
unsigned long event, void *ptr)
|
||||||
|
{
|
||||||
|
/* System is rebooting. Set the system into high_bus_freq_mode. */
|
||||||
|
request_bus_freq(BUS_FREQ_HIGH);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct notifier_block imx_bus_freq_pm_notifier = {
|
||||||
|
.notifier_call = bus_freq_pm_notify,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct notifier_block imx_busfreq_reboot_notifier = {
|
||||||
|
.notifier_call = busfreq_reboot_notifier_event,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static DEVICE_ATTR(enable, 0644, bus_freq_scaling_enable_show,
|
||||||
|
bus_freq_scaling_enable_store);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* This is the probe routine for the bus frequency driver.
|
||||||
|
*
|
||||||
|
* @param pdev The platform device structure
|
||||||
|
*
|
||||||
|
* @return The function returns 0 on success
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int busfreq_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
u32 err;
|
||||||
|
|
||||||
|
busfreq_dev = &pdev->dev;
|
||||||
|
|
||||||
|
/* Return if no IRAM space is allocated for ddr freq change code. */
|
||||||
|
if (!ddr_freq_change_iram_base)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (cpu_is_imx7d()) {
|
||||||
|
osc_clk = devm_clk_get(&pdev->dev, "osc");
|
||||||
|
axi_sel_clk = devm_clk_get(&pdev->dev, "axi_sel");
|
||||||
|
ahb_sel_clk = devm_clk_get(&pdev->dev, "ahb_sel");
|
||||||
|
pfd0_392m = devm_clk_get(&pdev->dev, "pfd0_392m");
|
||||||
|
dram_root = devm_clk_get(&pdev->dev, "dram_root");
|
||||||
|
dram_alt_sel = devm_clk_get(&pdev->dev, "dram_alt_sel");
|
||||||
|
pll_dram = devm_clk_get(&pdev->dev, "pll_dram");
|
||||||
|
dram_alt_root = devm_clk_get(&pdev->dev, "dram_alt_root");
|
||||||
|
pfd1_332m = devm_clk_get(&pdev->dev, "pfd1_332m");
|
||||||
|
pfd2_270m = devm_clk_get(&pdev->dev, "pfd2_270m");
|
||||||
|
ahb_clk = devm_clk_get(&pdev->dev, "ahb");
|
||||||
|
axi_clk = devm_clk_get(&pdev->dev, "axi");
|
||||||
|
if (IS_ERR(osc_clk) || IS_ERR(axi_sel_clk) || IS_ERR(ahb_clk)
|
||||||
|
|| IS_ERR(pfd0_392m) || IS_ERR(dram_root)
|
||||||
|
|| IS_ERR(dram_alt_sel) || IS_ERR(pll_dram)
|
||||||
|
|| IS_ERR(dram_alt_root) || IS_ERR(pfd1_332m)
|
||||||
|
|| IS_ERR(ahb_clk) || IS_ERR(axi_clk)
|
||||||
|
|| IS_ERR(pfd2_270m)) {
|
||||||
|
dev_err(busfreq_dev,
|
||||||
|
"%s: failed to get busfreq clk\n", __func__);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = sysfs_create_file(&busfreq_dev->kobj, &dev_attr_enable.attr);
|
||||||
|
if (err) {
|
||||||
|
dev_err(busfreq_dev,
|
||||||
|
"Unable to register sysdev entry for BUSFREQ");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (of_property_read_u32(pdev->dev.of_node, "fsl,max_ddr_freq",
|
||||||
|
&ddr_normal_rate)) {
|
||||||
|
dev_err(busfreq_dev, "max_ddr_freq entry missing\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
high_bus_freq_mode = 1;
|
||||||
|
med_bus_freq_mode = 0;
|
||||||
|
low_bus_freq_mode = 0;
|
||||||
|
audio_bus_freq_mode = 0;
|
||||||
|
ultra_low_bus_freq_mode = 0;
|
||||||
|
cur_bus_freq_mode = BUS_FREQ_HIGH;
|
||||||
|
|
||||||
|
bus_freq_scaling_is_active = 1;
|
||||||
|
bus_freq_scaling_initialized = 1;
|
||||||
|
|
||||||
|
ddr_low_rate = LPAPM_CLK;
|
||||||
|
|
||||||
|
INIT_DELAYED_WORK(&low_bus_freq_handler, reduce_bus_freq_handler);
|
||||||
|
INIT_DELAYED_WORK(&bus_freq_daemon, bus_freq_daemon_handler);
|
||||||
|
register_pm_notifier(&imx_bus_freq_pm_notifier);
|
||||||
|
register_reboot_notifier(&imx_busfreq_reboot_notifier);
|
||||||
|
|
||||||
|
/* enter low bus mode if no high speed device enabled */
|
||||||
|
schedule_delayed_work(&bus_freq_daemon,
|
||||||
|
msecs_to_jiffies(20000));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Need to make sure to an entry for the ddr freq change code
|
||||||
|
* address in the IRAM page table.
|
||||||
|
* This is only required if the DDR freq code and suspend/idle
|
||||||
|
* code are in different OCRAM spaces.
|
||||||
|
*/
|
||||||
|
if ((iram_tlb_phys_addr & 0xFFF00000) !=
|
||||||
|
(ddr_freq_change_iram_phys & 0xFFF00000)) {
|
||||||
|
unsigned long i;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the ddr_iram virtual address has a mapping
|
||||||
|
* in the IRAM page table.
|
||||||
|
*/
|
||||||
|
i = ((IMX_IO_P2V(ddr_freq_change_iram_phys) >> 20) << 2) / 4;
|
||||||
|
*((unsigned long *)iram_tlb_base_addr + i) =
|
||||||
|
(ddr_freq_change_iram_phys & 0xFFF00000) |
|
||||||
|
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cpu_is_imx7d()) {
|
||||||
|
ddr_type = imx_ddrc_get_ddr_type();
|
||||||
|
/* reduce ddr3 normal rate to 400M due to CKE issue on TO1.1 */
|
||||||
|
if (imx_get_soc_revision() == IMX_CHIP_REVISION_1_1 &&
|
||||||
|
ddr_type == IMX_DDR_TYPE_DDR3) {
|
||||||
|
ddr_normal_rate = 400000000;
|
||||||
|
pr_info("ddr3 normal rate changed to 400MHz for TO1.1.\n");
|
||||||
|
}
|
||||||
|
busfreq_func.init = &init_ddrc_ddr_settings;
|
||||||
|
busfreq_func.update = &update_ddr_freq_imx_smp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (busfreq_func.init)
|
||||||
|
err = busfreq_func.init(pdev);
|
||||||
|
else
|
||||||
|
err = -EINVAL;
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
dev_err(busfreq_dev, "Busfreq init of ddr controller failed\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct of_device_id imx_busfreq_ids[] = {
|
||||||
|
{ .compatible = "fsl,imx_busfreq", },
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver busfreq_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "imx_busfreq",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.of_match_table = imx_busfreq_ids,
|
||||||
|
},
|
||||||
|
.probe = busfreq_probe,
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Initialise the busfreq_driver.
|
||||||
|
*
|
||||||
|
* @return The function always returns 0.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int __init busfreq_init(void)
|
||||||
|
{
|
||||||
|
#ifndef CONFIG_MX6_VPU_352M
|
||||||
|
if (platform_driver_register(&busfreq_driver) != 0)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
pr_info("Bus freq driver module loaded\n");
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit busfreq_cleanup(void)
|
||||||
|
{
|
||||||
|
sysfs_remove_file(&busfreq_dev->kobj, &dev_attr_enable.attr);
|
||||||
|
|
||||||
|
/* Unregister the device structure */
|
||||||
|
platform_driver_unregister(&busfreq_driver);
|
||||||
|
bus_freq_scaling_initialized = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(busfreq_init);
|
||||||
|
module_exit(busfreq_cleanup);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||||
|
MODULE_DESCRIPTION("BusFreq driver");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,310 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2016 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_ddr3.c
|
||||||
|
*
|
||||||
|
* @brief iMX6 DDR3 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/busfreq-imx.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/clockchips.h>
|
||||||
|
#include <linux/cpumask.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/genalloc.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irq.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/slab.h>
|
||||||
|
#include <linux/smp.h>
|
||||||
|
|
||||||
|
#include "hardware.h"
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define SMP_WFE_CODE_SIZE 0x400
|
||||||
|
|
||||||
|
#define MIN_DLL_ON_FREQ 333000000
|
||||||
|
#define MAX_DLL_OFF_FREQ 125000000
|
||||||
|
#define MMDC0_MPMUR0 0x8b8
|
||||||
|
#define MMDC0_MPMUR0_OFFSET 16
|
||||||
|
#define MMDC0_MPMUR0_MASK 0x3ff
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This structure is for passing necessary data for low level ocram
|
||||||
|
* busfreq code(arch/arm/mach-imx/ddr3_freq_imx6.S), if this struct
|
||||||
|
* definition is changed, the offset definition in
|
||||||
|
* arch/arm/mach-imx/ddr3_freq_imx6.S must be also changed accordingly,
|
||||||
|
* otherwise, the busfreq change function will be broken!
|
||||||
|
*
|
||||||
|
* This structure will be placed in front of the asm code on ocram.
|
||||||
|
*/
|
||||||
|
struct imx6_busfreq_info {
|
||||||
|
u32 freq;
|
||||||
|
void *ddr_settings;
|
||||||
|
u32 dll_off;
|
||||||
|
void *iomux_offsets;
|
||||||
|
u32 mu_delay_val;
|
||||||
|
} __aligned(8);
|
||||||
|
|
||||||
|
/* DDR settings */
|
||||||
|
static unsigned long (*iram_iomux_settings)[2];
|
||||||
|
|
||||||
|
static void __iomem *gic_dist_base;
|
||||||
|
|
||||||
|
static int curr_ddr_rate;
|
||||||
|
|
||||||
|
void (*imx7d_change_ddr_freq)(u32 freq) = NULL;
|
||||||
|
extern void imx7d_ddr3_freq_change(u32 freq);
|
||||||
|
extern void imx_lpddr3_freq_change(u32 freq);
|
||||||
|
|
||||||
|
extern unsigned int ddr_normal_rate;
|
||||||
|
extern int low_bus_freq_mode;
|
||||||
|
extern int audio_bus_freq_mode;
|
||||||
|
|
||||||
|
extern unsigned long save_ttbr1(void);
|
||||||
|
extern void restore_ttbr1(unsigned long ttbr1);
|
||||||
|
extern unsigned long ddr_freq_change_iram_base;
|
||||||
|
|
||||||
|
extern unsigned long ddr_freq_change_total_size;
|
||||||
|
extern unsigned long iram_tlb_phys_addr;
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
volatile u32 *wait_for_ddr_freq_update;
|
||||||
|
static unsigned int online_cpus;
|
||||||
|
static u32 *irqs_used;
|
||||||
|
|
||||||
|
void (*wfe_change_ddr_freq)(u32 cpuid, u32 *ddr_freq_change_done);
|
||||||
|
void (*imx7_wfe_change_ddr_freq)(u32 cpuid, u32 ocram_base);
|
||||||
|
extern void wfe_smp_freq_change(u32 cpuid, u32 *ddr_freq_change_done);
|
||||||
|
extern void imx7_smp_wfe(u32 cpuid, u32 ocram_base);
|
||||||
|
extern unsigned long wfe_smp_freq_change_start asm("wfe_smp_freq_change_start");
|
||||||
|
extern unsigned long wfe_smp_freq_change_end asm("wfe_smp_freq_change_end");
|
||||||
|
extern void __iomem *imx_scu_base;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int can_change_ddr_freq(void)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
/*
|
||||||
|
* each active core apart from the one changing
|
||||||
|
* the DDR frequency will execute this function.
|
||||||
|
* the rest of the cores have to remain in WFE
|
||||||
|
* state until the frequency is changed.
|
||||||
|
*/
|
||||||
|
static irqreturn_t wait_in_wfe_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
u32 me;
|
||||||
|
|
||||||
|
me = smp_processor_id();
|
||||||
|
#ifdef CONFIG_LOCAL_TIMERS
|
||||||
|
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER,
|
||||||
|
&me);
|
||||||
|
#endif
|
||||||
|
if (cpu_is_imx7d())
|
||||||
|
imx7_wfe_change_ddr_freq(0x8 * me,
|
||||||
|
(u32)ddr_freq_change_iram_base);
|
||||||
|
else
|
||||||
|
wfe_change_ddr_freq(0xff << (me * 8),
|
||||||
|
(u32 *)&iram_iomux_settings[0][1]);
|
||||||
|
#ifdef CONFIG_LOCAL_TIMERS
|
||||||
|
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT,
|
||||||
|
&me);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* change the DDR frequency. */
|
||||||
|
int update_ddr_freq_imx_smp(int ddr_rate)
|
||||||
|
{
|
||||||
|
int me = 0;
|
||||||
|
unsigned long ttbr1;
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
unsigned int reg = 0;
|
||||||
|
int cpu = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!can_change_ddr_freq())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (ddr_rate == curr_ddr_rate)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
printk(KERN_DEBUG "\nBus freq set to %d start...\n", ddr_rate);
|
||||||
|
|
||||||
|
/* ensure that all Cores are in WFE. */
|
||||||
|
local_irq_disable();
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
me = smp_processor_id();
|
||||||
|
|
||||||
|
/* Make sure all the online cores are active */
|
||||||
|
while (1) {
|
||||||
|
bool not_exited_busfreq = false;
|
||||||
|
u32 reg = 0;
|
||||||
|
|
||||||
|
for_each_online_cpu(cpu) {
|
||||||
|
if (cpu_is_imx7d())
|
||||||
|
reg = *(wait_for_ddr_freq_update + 1);
|
||||||
|
|
||||||
|
if (reg & (0x02 << (cpu * 8)))
|
||||||
|
not_exited_busfreq = true;
|
||||||
|
}
|
||||||
|
if (!not_exited_busfreq)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
wmb();
|
||||||
|
*wait_for_ddr_freq_update = 1;
|
||||||
|
dsb();
|
||||||
|
if (cpu_is_imx7d())
|
||||||
|
online_cpus = *(wait_for_ddr_freq_update + 1);
|
||||||
|
for_each_online_cpu(cpu) {
|
||||||
|
*((char *)(&online_cpus) + (u8)cpu) = 0x02;
|
||||||
|
if (cpu != me) {
|
||||||
|
/* set the interrupt to be pending in the GIC. */
|
||||||
|
reg = 1 << (irqs_used[cpu] % 32);
|
||||||
|
writel_relaxed(reg, gic_dist_base + GIC_DIST_PENDING_SET
|
||||||
|
+ (irqs_used[cpu] / 32) * 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Wait for the other active CPUs to idle */
|
||||||
|
while (1) {
|
||||||
|
u32 reg = 0;
|
||||||
|
|
||||||
|
if (cpu_is_imx7d())
|
||||||
|
reg = *(wait_for_ddr_freq_update + 1);
|
||||||
|
reg |= (0x02 << (me * 8));
|
||||||
|
if (reg == online_cpus)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Ensure iram_tlb_phys_addr is flushed to DDR. */
|
||||||
|
__cpuc_flush_dcache_area(&iram_tlb_phys_addr,
|
||||||
|
sizeof(iram_tlb_phys_addr));
|
||||||
|
|
||||||
|
ttbr1 = save_ttbr1();
|
||||||
|
/* Now we can change the DDR frequency. */
|
||||||
|
if (cpu_is_imx7d())
|
||||||
|
imx7d_change_ddr_freq(ddr_rate);
|
||||||
|
restore_ttbr1(ttbr1);
|
||||||
|
curr_ddr_rate = ddr_rate;
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
wmb();
|
||||||
|
/* DDR frequency change is done . */
|
||||||
|
*wait_for_ddr_freq_update = 0;
|
||||||
|
dsb();
|
||||||
|
|
||||||
|
/* wake up all the cores. */
|
||||||
|
sev();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
local_irq_enable();
|
||||||
|
|
||||||
|
printk(KERN_DEBUG "Bus freq set to %d done! cpu=%d\n", ddr_rate, me);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_ddrc_ddr_settings(struct platform_device *busfreq_pdev)
|
||||||
|
{
|
||||||
|
int ddr_type = imx_ddrc_get_ddr_type();
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
struct device_node *node;
|
||||||
|
u32 cpu;
|
||||||
|
struct device *dev = &busfreq_pdev->dev;
|
||||||
|
int err;
|
||||||
|
struct irq_data *d;
|
||||||
|
|
||||||
|
node = of_find_compatible_node(NULL, NULL, "arm,cortex-a7-gic");
|
||||||
|
if (!node) {
|
||||||
|
printk(KERN_ERR "failed to find imx7d-a7-gic device tree data!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
gic_dist_base = of_iomap(node, 0);
|
||||||
|
WARN(!gic_dist_base, "unable to map gic dist registers\n");
|
||||||
|
|
||||||
|
irqs_used = devm_kzalloc(dev, sizeof(u32) * num_present_cpus(),
|
||||||
|
GFP_KERNEL);
|
||||||
|
for_each_online_cpu(cpu) {
|
||||||
|
int irq;
|
||||||
|
/*
|
||||||
|
* set up a reserved interrupt to get all
|
||||||
|
* the active cores into a WFE state
|
||||||
|
* before changing the DDR frequency.
|
||||||
|
*/
|
||||||
|
irq = platform_get_irq(busfreq_pdev, cpu);
|
||||||
|
err = request_irq(irq, wait_in_wfe_irq,
|
||||||
|
IRQF_PERCPU, "ddrc", NULL);
|
||||||
|
if (err) {
|
||||||
|
dev_err(dev,
|
||||||
|
"Busfreq:request_irq failed %d, err = %d\n",
|
||||||
|
irq, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
err = irq_set_affinity(irq, cpumask_of(cpu));
|
||||||
|
if (err) {
|
||||||
|
dev_err(dev,
|
||||||
|
"Busfreq: Cannot set irq affinity irq=%d\n",
|
||||||
|
irq);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
d = irq_get_irq_data(irq);
|
||||||
|
irqs_used[cpu] = d->hwirq + 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store the variable used to communicate between cores */
|
||||||
|
wait_for_ddr_freq_update = (u32 *)ddr_freq_change_iram_base;
|
||||||
|
imx7_wfe_change_ddr_freq = (void *)fncpy(
|
||||||
|
(void *)ddr_freq_change_iram_base + 0x8,
|
||||||
|
&imx7_smp_wfe, SMP_WFE_CODE_SIZE - 0x8);
|
||||||
|
#endif
|
||||||
|
if (ddr_type == IMX_DDR_TYPE_DDR3)
|
||||||
|
imx7d_change_ddr_freq = (void *)fncpy(
|
||||||
|
(void *)ddr_freq_change_iram_base + SMP_WFE_CODE_SIZE,
|
||||||
|
&imx7d_ddr3_freq_change,
|
||||||
|
MX7_BUSFREQ_OCRAM_SIZE - SMP_WFE_CODE_SIZE);
|
||||||
|
else if (ddr_type == IMX_DDR_TYPE_LPDDR3
|
||||||
|
|| ddr_type == IMX_DDR_TYPE_LPDDR2)
|
||||||
|
imx7d_change_ddr_freq = (void *)fncpy(
|
||||||
|
(void *)ddr_freq_change_iram_base +
|
||||||
|
SMP_WFE_CODE_SIZE,
|
||||||
|
&imx_lpddr3_freq_change,
|
||||||
|
MX7_BUSFREQ_OCRAM_SIZE - SMP_WFE_CODE_SIZE);
|
||||||
|
|
||||||
|
curr_ddr_rate = ddr_normal_rate;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,373 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011-2016 Freescale Semiconductor, Inc. All Rights Reserved.
|
||||||
|
* Copyright 2017 NXP.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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/busfreq-imx.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/clockchips.h>
|
||||||
|
#include <linux/cpumask.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/genalloc.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irq.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 <linux/slab.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "hardware.h"
|
||||||
|
|
||||||
|
static struct device *busfreq_dev;
|
||||||
|
static int curr_ddr_rate;
|
||||||
|
static DEFINE_SPINLOCK(freq_lock);
|
||||||
|
|
||||||
|
void (*mx6_change_lpddr2_freq)(u32 ddr_freq, int bus_freq_mode) = NULL;
|
||||||
|
|
||||||
|
extern unsigned int ddr_normal_rate;
|
||||||
|
extern void mx6_lpddr2_freq_change(u32 freq, int bus_freq_mode);
|
||||||
|
extern void imx6_up_lpddr2_freq_change(u32 freq, int bus_freq_mode);
|
||||||
|
extern void imx6sll_lpddr2_freq_change(u32 freq, int bus_freq_mode);
|
||||||
|
extern unsigned long save_ttbr1(void);
|
||||||
|
extern void restore_ttbr1(unsigned long ttbr1);
|
||||||
|
extern void mx6q_lpddr2_freq_change(u32 freq, void *ddr_settings);
|
||||||
|
extern unsigned long ddr_freq_change_iram_base;
|
||||||
|
extern unsigned long imx6_lpddr2_freq_change_start asm("imx6_lpddr2_freq_change_start");
|
||||||
|
extern unsigned long imx6_lpddr2_freq_change_end asm("imx6_lpddr2_freq_change_end");
|
||||||
|
extern unsigned long mx6q_lpddr2_freq_change_start asm("mx6q_lpddr2_freq_change_start");
|
||||||
|
extern unsigned long mx6q_lpddr2_freq_change_end asm("mx6q_lpddr2_freq_change_end");
|
||||||
|
extern unsigned long iram_tlb_phys_addr;
|
||||||
|
|
||||||
|
struct mmdc_settings_info {
|
||||||
|
u32 size;
|
||||||
|
void *settings;
|
||||||
|
int freq;
|
||||||
|
} __aligned(8);
|
||||||
|
static struct mmdc_settings_info *mmdc_settings_info;
|
||||||
|
void (*mx6_change_lpddr2_freq_smp)(u32 ddr_freq, struct mmdc_settings_info
|
||||||
|
*mmdc_settings_info) = NULL;
|
||||||
|
|
||||||
|
static int mmdc_settings_size;
|
||||||
|
static unsigned long (*mmdc_settings)[2];
|
||||||
|
static unsigned long (*iram_mmdc_settings)[2];
|
||||||
|
static unsigned long *iram_settings_size;
|
||||||
|
static unsigned long *iram_ddr_freq_chage;
|
||||||
|
unsigned long mmdc_timing_settings[][2] = {
|
||||||
|
{0x0C, 0x0}, /* mmdc_mdcfg0 */
|
||||||
|
{0x10, 0x0}, /* mmdc_mdcfg1 */
|
||||||
|
{0x14, 0x0}, /* mmdc_mdcfg2 */
|
||||||
|
{0x18, 0x0}, /* mmdc_mdmisc */
|
||||||
|
{0x38, 0x0}, /* mmdc_mdcfg3lp */
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
volatile u32 *wait_for_lpddr2_freq_update;
|
||||||
|
static unsigned int online_cpus;
|
||||||
|
static u32 *irqs_used;
|
||||||
|
void (*wfe_change_lpddr2_freq)(u32 cpuid, u32 *ddr_freq_change_done);
|
||||||
|
extern void wfe_smp_freq_change(u32 cpuid, u32 *ddr_freq_change_done);
|
||||||
|
extern unsigned long wfe_smp_freq_change_start asm("wfe_smp_freq_change_start");
|
||||||
|
extern unsigned long wfe_smp_freq_change_end asm("wfe_smp_freq_change_end");
|
||||||
|
extern void __iomem *imx_scu_base;
|
||||||
|
static void __iomem *gic_dist_base;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
static irqreturn_t wait_in_wfe_irq(int irq, void *dev_id)
|
||||||
|
{
|
||||||
|
u32 me;
|
||||||
|
|
||||||
|
me = smp_processor_id();
|
||||||
|
#ifdef CONFIG_LOCAL_TIMERS
|
||||||
|
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &me);
|
||||||
|
#endif
|
||||||
|
wfe_change_lpddr2_freq(0xff << (me * 8),
|
||||||
|
(u32 *)ddr_freq_change_iram_base);
|
||||||
|
#ifdef CONFIG_LOCAL_TIMERS
|
||||||
|
clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &me);
|
||||||
|
#endif
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* change the DDR frequency. */
|
||||||
|
int update_lpddr2_freq(int ddr_rate)
|
||||||
|
{
|
||||||
|
unsigned long ttbr1, flags;
|
||||||
|
int mode = get_bus_freq_mode();
|
||||||
|
|
||||||
|
if (ddr_rate == curr_ddr_rate)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
printk(KERN_DEBUG "\nBus freq set to %d start...\n", ddr_rate);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&freq_lock, flags);
|
||||||
|
/*
|
||||||
|
* Flush the TLB, to ensure no TLB maintenance occurs
|
||||||
|
* when DDR is in self-refresh.
|
||||||
|
*/
|
||||||
|
ttbr1 = save_ttbr1();
|
||||||
|
|
||||||
|
/* Now change DDR frequency. */
|
||||||
|
if (cpu_is_imx6sl())
|
||||||
|
mx6_change_lpddr2_freq(ddr_rate,
|
||||||
|
(mode == BUS_FREQ_LOW || mode == BUS_FREQ_ULTRA_LOW) ? 1 : 0);
|
||||||
|
else
|
||||||
|
mx6_change_lpddr2_freq(ddr_rate,
|
||||||
|
(mode == BUS_FREQ_LOW || mode == BUS_FREQ_AUDIO) ? 1 : 0);
|
||||||
|
|
||||||
|
restore_ttbr1(ttbr1);
|
||||||
|
|
||||||
|
curr_ddr_rate = ddr_rate;
|
||||||
|
spin_unlock_irqrestore(&freq_lock, flags);
|
||||||
|
|
||||||
|
printk(KERN_DEBUG "\nBus freq set to %d done...\n", ddr_rate);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_mmdc_lpddr2_settings(struct platform_device *busfreq_pdev)
|
||||||
|
{
|
||||||
|
unsigned long ddr_code_size;
|
||||||
|
busfreq_dev = &busfreq_pdev->dev;
|
||||||
|
|
||||||
|
ddr_code_size = SZ_4K;
|
||||||
|
|
||||||
|
if (cpu_is_imx6sl())
|
||||||
|
mx6_change_lpddr2_freq = (void *)fncpy(
|
||||||
|
(void *)ddr_freq_change_iram_base,
|
||||||
|
&mx6_lpddr2_freq_change, ddr_code_size);
|
||||||
|
if (cpu_is_imx6sx() || cpu_is_imx6ul() || cpu_is_imx6ull())
|
||||||
|
mx6_change_lpddr2_freq = (void *)fncpy(
|
||||||
|
(void *)ddr_freq_change_iram_base,
|
||||||
|
&imx6_up_lpddr2_freq_change, ddr_code_size);
|
||||||
|
if (cpu_is_imx6sll())
|
||||||
|
mx6_change_lpddr2_freq = (void *)fncpy(
|
||||||
|
(void *)ddr_freq_change_iram_base,
|
||||||
|
&imx6sll_lpddr2_freq_change, ddr_code_size);
|
||||||
|
|
||||||
|
curr_ddr_rate = ddr_normal_rate;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int update_lpddr2_freq_smp(int ddr_rate)
|
||||||
|
{
|
||||||
|
unsigned long ttbr1;
|
||||||
|
int i, me = 0;
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
int cpu = 0;
|
||||||
|
u32 reg = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (ddr_rate == curr_ddr_rate)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
printk(KERN_DEBUG "Bus freq set to %d start...\n", ddr_rate);
|
||||||
|
|
||||||
|
for (i=0; i < mmdc_settings_size; i++) {
|
||||||
|
iram_mmdc_settings[i][0] = mmdc_settings[i][0];
|
||||||
|
iram_mmdc_settings[i][1] = mmdc_settings[i][1];
|
||||||
|
}
|
||||||
|
|
||||||
|
mmdc_settings_info->size = mmdc_settings_size;
|
||||||
|
mmdc_settings_info->settings = iram_mmdc_settings;
|
||||||
|
mmdc_settings_info->freq = curr_ddr_rate;
|
||||||
|
|
||||||
|
/* ensure that all Cores are in WFE. */
|
||||||
|
local_irq_disable();
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
me = smp_processor_id();
|
||||||
|
|
||||||
|
/* Make sure all the online cores are active */
|
||||||
|
while (1) {
|
||||||
|
bool not_exited_busfreq = false;
|
||||||
|
for_each_online_cpu(cpu) {
|
||||||
|
reg = __raw_readl(imx_scu_base + 0x08);
|
||||||
|
if (reg & (0x02 << (cpu * 8)))
|
||||||
|
not_exited_busfreq = true;
|
||||||
|
}
|
||||||
|
if (!not_exited_busfreq)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
wmb();
|
||||||
|
*wait_for_lpddr2_freq_update = 1;
|
||||||
|
dsb();
|
||||||
|
online_cpus = readl_relaxed(imx_scu_base + 0x08);
|
||||||
|
for_each_online_cpu(cpu) {
|
||||||
|
*((char *)(&online_cpus) + (u8)cpu) = 0x02;
|
||||||
|
if (cpu != me) {
|
||||||
|
reg = 1 << (irqs_used[cpu] % 32);
|
||||||
|
writel_relaxed(reg, gic_dist_base + GIC_DIST_PENDING_SET
|
||||||
|
+ (irqs_used[cpu] / 32) * 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait for the other active CPUs to idle */
|
||||||
|
while (1) {
|
||||||
|
reg = 0;
|
||||||
|
reg = readl_relaxed(imx_scu_base + 0x08);
|
||||||
|
reg |= (0x02 << (me * 8));
|
||||||
|
if (reg == online_cpus)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Ensure iram_tlb_phys_addr is flushed to DDR. */
|
||||||
|
__cpuc_flush_dcache_area(&iram_tlb_phys_addr,
|
||||||
|
sizeof(iram_tlb_phys_addr));
|
||||||
|
outer_clean_range(__pa(&iram_tlb_phys_addr),
|
||||||
|
__pa(&iram_tlb_phys_addr + 1));
|
||||||
|
/*
|
||||||
|
* Flush the TLB, to ensure no TLB maintenance occurs
|
||||||
|
* when DDR is in self-refresh.
|
||||||
|
*/
|
||||||
|
ttbr1 = save_ttbr1();
|
||||||
|
|
||||||
|
curr_ddr_rate = ddr_rate;
|
||||||
|
|
||||||
|
/* Now change DDR frequency. */
|
||||||
|
mx6_change_lpddr2_freq_smp(ddr_rate, mmdc_settings_info);
|
||||||
|
|
||||||
|
restore_ttbr1(ttbr1);
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
wmb();
|
||||||
|
/* DDR frequency change is done . */
|
||||||
|
*wait_for_lpddr2_freq_update = 0;
|
||||||
|
dsb();
|
||||||
|
/* wake up all the cores. */
|
||||||
|
sev();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
local_irq_enable();
|
||||||
|
|
||||||
|
printk(KERN_DEBUG "Bus freq set to %d done! cpu=%d\n", ddr_rate, me);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_mmdc_lpddr2_settings_mx6q(struct platform_device *busfreq_pdev)
|
||||||
|
{
|
||||||
|
struct device *dev = &busfreq_pdev->dev;
|
||||||
|
unsigned long ddr_code_size = 0;
|
||||||
|
unsigned long wfe_code_size = 0;
|
||||||
|
struct device_node *node;
|
||||||
|
void __iomem *mmdc_base;
|
||||||
|
int i;
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
struct irq_data *d;
|
||||||
|
u32 cpu;
|
||||||
|
int err;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
node = of_find_compatible_node(NULL, NULL, "fsl,imx6q-mmdc");
|
||||||
|
if (!node) {
|
||||||
|
printk(KERN_ERR "failed to find mmdc device tree data!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mmdc_base = of_iomap(node, 0);
|
||||||
|
if (!mmdc_base) {
|
||||||
|
dev_err(dev, "unable to map mmdc registers\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mmdc_settings_size = ARRAY_SIZE(mmdc_timing_settings);
|
||||||
|
mmdc_settings = kmalloc((mmdc_settings_size * 8), GFP_KERNEL);
|
||||||
|
memcpy(mmdc_settings, mmdc_timing_settings,
|
||||||
|
sizeof(mmdc_timing_settings));
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
node = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-gic");
|
||||||
|
if (!node) {
|
||||||
|
printk(KERN_ERR "failed to find imx6q-a9-gic device tree data!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gic_dist_base = of_iomap(node, 0);
|
||||||
|
WARN(!gic_dist_base, "unable to map gic dist registers\n");
|
||||||
|
|
||||||
|
irqs_used = devm_kzalloc(dev, sizeof(u32) * num_present_cpus(),
|
||||||
|
GFP_KERNEL);
|
||||||
|
|
||||||
|
for_each_online_cpu(cpu) {
|
||||||
|
int irq = platform_get_irq(busfreq_pdev, cpu);
|
||||||
|
err = request_irq(irq, wait_in_wfe_irq, IRQF_PERCPU,
|
||||||
|
"mmdc_1", NULL);
|
||||||
|
if (err) {
|
||||||
|
dev_err(dev,
|
||||||
|
"Busfreq:request_irq failed %d, err = %d\n",
|
||||||
|
irq, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
err = irq_set_affinity(irq, cpumask_of(cpu));
|
||||||
|
if (err) {
|
||||||
|
dev_err(dev,
|
||||||
|
"Busfreq: Cannot set irq affinity irq=%d,\n",
|
||||||
|
irq);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
d = irq_get_irq_data(irq);
|
||||||
|
irqs_used[cpu] = d->hwirq + 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stoange_iram_basee the variable used to communicate between cores in
|
||||||
|
* a non-cacheable IRAM area */
|
||||||
|
wait_for_lpddr2_freq_update = (u32 *)ddr_freq_change_iram_base;
|
||||||
|
wfe_code_size = (&wfe_smp_freq_change_end - &wfe_smp_freq_change_start) *4;
|
||||||
|
|
||||||
|
wfe_change_lpddr2_freq = (void *)fncpy((void *)ddr_freq_change_iram_base + 0x8,
|
||||||
|
&wfe_smp_freq_change, wfe_code_size);
|
||||||
|
#endif
|
||||||
|
iram_settings_size = (void *)ddr_freq_change_iram_base + wfe_code_size + 0x8;
|
||||||
|
iram_mmdc_settings = (void *)iram_settings_size + sizeof(*mmdc_settings_info);
|
||||||
|
iram_ddr_freq_chage = (void *)iram_mmdc_settings + (mmdc_settings_size * 8) + 0x8;
|
||||||
|
mmdc_settings_info = (struct mmdc_settings_info *)iram_settings_size;
|
||||||
|
|
||||||
|
ddr_code_size = (&mx6q_lpddr2_freq_change_end -&mx6q_lpddr2_freq_change_start) *4;
|
||||||
|
|
||||||
|
mx6_change_lpddr2_freq_smp = (void *)fncpy(iram_ddr_freq_chage,
|
||||||
|
&mx6q_lpddr2_freq_change, ddr_code_size);
|
||||||
|
|
||||||
|
/* save initial mmdc boot timing settings */
|
||||||
|
for (i=0; i < mmdc_settings_size; i++)
|
||||||
|
mmdc_settings[i][1] = readl_relaxed(mmdc_base +
|
||||||
|
mmdc_settings[i][0]);
|
||||||
|
|
||||||
|
curr_ddr_rate = ddr_normal_rate;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015-2016 Freescale Semiconductor, Inc.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/of_net.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include "hardware.h"
|
||||||
|
|
||||||
|
unsigned long iram_tlb_base_addr;
|
||||||
|
unsigned long iram_tlb_phys_addr;
|
||||||
|
|
||||||
|
unsigned long save_ttbr1(void)
|
||||||
|
{
|
||||||
|
unsigned long lttbr1;
|
||||||
|
asm volatile(
|
||||||
|
".align 4\n"
|
||||||
|
"mrc p15, 0, %0, c2, c0, 1\n"
|
||||||
|
: "=r" (lttbr1)
|
||||||
|
);
|
||||||
|
return lttbr1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void restore_ttbr1(unsigned long ttbr1)
|
||||||
|
{
|
||||||
|
asm volatile(
|
||||||
|
".align 4\n"
|
||||||
|
"mcr p15, 0, %0, c2, c0, 1\n"
|
||||||
|
: : "r" (ttbr1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define OCOTP_MAC_OFF (cpu_is_imx7d() ? 0x640 : 0x620)
|
||||||
|
#define OCOTP_MACn(n) (OCOTP_MAC_OFF + (n) * 0x10)
|
||||||
|
void __init imx6_enet_mac_init(const char *enet_compat, const char *ocotp_compat)
|
||||||
|
{
|
||||||
|
struct device_node *ocotp_np, *enet_np, *from = NULL;
|
||||||
|
void __iomem *base;
|
||||||
|
struct property *newmac;
|
||||||
|
u32 macaddr_low;
|
||||||
|
u32 macaddr_high = 0;
|
||||||
|
u32 macaddr1_high = 0;
|
||||||
|
u8 *macaddr;
|
||||||
|
int i, id;
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
enet_np = of_find_compatible_node(from, NULL, enet_compat);
|
||||||
|
if (!enet_np)
|
||||||
|
return;
|
||||||
|
|
||||||
|
from = enet_np;
|
||||||
|
|
||||||
|
if (of_get_mac_address(enet_np))
|
||||||
|
goto put_enet_node;
|
||||||
|
|
||||||
|
id = of_alias_get_id(enet_np, "ethernet");
|
||||||
|
if (id < 0)
|
||||||
|
id = i;
|
||||||
|
|
||||||
|
ocotp_np = of_find_compatible_node(NULL, NULL, ocotp_compat);
|
||||||
|
if (!ocotp_np) {
|
||||||
|
pr_warn("failed to find ocotp node\n");
|
||||||
|
goto put_enet_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
base = of_iomap(ocotp_np, 0);
|
||||||
|
if (!base) {
|
||||||
|
pr_warn("failed to map ocotp\n");
|
||||||
|
goto put_ocotp_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
macaddr_low = readl_relaxed(base + OCOTP_MACn(1));
|
||||||
|
if (id)
|
||||||
|
macaddr1_high = readl_relaxed(base + OCOTP_MACn(2));
|
||||||
|
else
|
||||||
|
macaddr_high = readl_relaxed(base + OCOTP_MACn(0));
|
||||||
|
|
||||||
|
newmac = kzalloc(sizeof(*newmac) + 6, GFP_KERNEL);
|
||||||
|
if (!newmac)
|
||||||
|
goto put_ocotp_node;
|
||||||
|
|
||||||
|
newmac->value = newmac + 1;
|
||||||
|
newmac->length = 6;
|
||||||
|
newmac->name = kstrdup("local-mac-address", GFP_KERNEL);
|
||||||
|
if (!newmac->name) {
|
||||||
|
kfree(newmac);
|
||||||
|
goto put_ocotp_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
macaddr = newmac->value;
|
||||||
|
if (id) {
|
||||||
|
macaddr[5] = (macaddr_low >> 16) & 0xff;
|
||||||
|
macaddr[4] = (macaddr_low >> 24) & 0xff;
|
||||||
|
macaddr[3] = macaddr1_high & 0xff;
|
||||||
|
macaddr[2] = (macaddr1_high >> 8) & 0xff;
|
||||||
|
macaddr[1] = (macaddr1_high >> 16) & 0xff;
|
||||||
|
macaddr[0] = (macaddr1_high >> 24) & 0xff;
|
||||||
|
} else {
|
||||||
|
macaddr[5] = macaddr_high & 0xff;
|
||||||
|
macaddr[4] = (macaddr_high >> 8) & 0xff;
|
||||||
|
macaddr[3] = (macaddr_high >> 16) & 0xff;
|
||||||
|
macaddr[2] = (macaddr_high >> 24) & 0xff;
|
||||||
|
macaddr[1] = macaddr_low & 0xff;
|
||||||
|
macaddr[0] = (macaddr_low >> 8) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
of_update_property(enet_np, newmac);
|
||||||
|
|
||||||
|
put_ocotp_node:
|
||||||
|
of_node_put(ocotp_np);
|
||||||
|
put_enet_node:
|
||||||
|
of_node_put(enet_np);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef CONFIG_HAVE_IMX_GPC
|
||||||
|
int imx_gpc_mf_request_on(unsigned int irq, unsigned int on) { return 0; }
|
||||||
|
EXPORT_SYMBOL_GPL(imx_gpc_mf_request_on);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(CONFIG_SOC_IMX6SL)
|
||||||
|
u32 imx6_lpddr2_freq_change_start, imx6_lpddr2_freq_change_end;
|
||||||
|
void mx6_lpddr2_freq_change(u32 freq, int bus_freq_mode) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(CONFIG_SOC_IMX6SLL)
|
||||||
|
void imx6sll_lpddr2_freq_change(u32 freq, int bus_freq_mode) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(CONFIG_SOC_IMX6SX) && !defined(CONFIG_SOC_IMX6UL)
|
||||||
|
u32 imx6_up_ddr3_freq_change_start, imx6_up_ddr3_freq_change_end;
|
||||||
|
struct imx6_busfreq_info {
|
||||||
|
} __aligned(8);
|
||||||
|
void imx6_up_ddr3_freq_change(struct imx6_busfreq_info *busfreq_info) {}
|
||||||
|
void imx6_up_lpddr2_freq_change(u32 freq, int bus_freq_mode) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(CONFIG_SOC_IMX6ULL)
|
||||||
|
u32 mx6ull_lpm_wfi_start, mx6ull_lpm_wfi_end;
|
||||||
|
void imx6ull_low_power_idle(void) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(CONFIG_SOC_IMX6Q)
|
||||||
|
u32 mx6_ddr3_freq_change_start, mx6_ddr3_freq_change_end;
|
||||||
|
u32 mx6q_lpddr2_freq_change_start, mx6q_lpddr2_freq_change_end;
|
||||||
|
u32 wfe_smp_freq_change_start, wfe_smp_freq_change_end;
|
||||||
|
void mx6_ddr3_freq_change(u32 freq, void *ddr_settings,
|
||||||
|
bool dll_mode, void *iomux_offsets) {}
|
||||||
|
void mx6q_lpddr2_freq_change(u32 freq, void *ddr_settings) {}
|
||||||
|
void wfe_smp_freq_change(u32 cpuid, u32 *ddr_freq_change_done) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(CONFIG_SOC_IMX7D)
|
||||||
|
void imx7_smp_wfe(u32 cpuid, u32 ocram_base) {}
|
||||||
|
void imx7d_ddr3_freq_change(u32 freq) {}
|
||||||
|
#endif
|
||||||
|
|
|
@ -104,6 +104,8 @@ void imx6_set_int_mem_clk_lpm(bool enable);
|
||||||
void imx6sl_set_wait_clk(bool enter);
|
void imx6sl_set_wait_clk(bool enter);
|
||||||
int imx_mmdc_get_ddr_type(void);
|
int imx_mmdc_get_ddr_type(void);
|
||||||
int imx7ulp_set_lpm(enum ulp_cpu_pwr_mode mode);
|
int imx7ulp_set_lpm(enum ulp_cpu_pwr_mode mode);
|
||||||
|
void imx_busfreq_map_io(void);
|
||||||
|
void imx7_pm_map_io(void);
|
||||||
|
|
||||||
void imx_cpu_die(unsigned int cpu);
|
void imx_cpu_die(unsigned int cpu);
|
||||||
int imx_cpu_kill(unsigned int cpu);
|
int imx_cpu_kill(unsigned int cpu);
|
||||||
|
@ -120,6 +122,12 @@ static const u32 imx53_suspend_sz;
|
||||||
static inline void imx6_suspend(void __iomem *ocram_vbase) {}
|
static inline void imx6_suspend(void __iomem *ocram_vbase) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_HAVE_IMX_DDRC
|
||||||
|
int imx_ddrc_get_ddr_type(void);
|
||||||
|
#else
|
||||||
|
static inline int imx_ddrc_get_ddr_type(void) { return 0; }
|
||||||
|
#endif
|
||||||
|
|
||||||
void imx6_pm_ccm_init(const char *ccm_compat);
|
void imx6_pm_ccm_init(const char *ccm_compat);
|
||||||
void imx6q_pm_init(void);
|
void imx6q_pm_init(void);
|
||||||
void imx6dl_pm_init(void);
|
void imx6dl_pm_init(void);
|
||||||
|
|
|
@ -0,0 +1,586 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015-2016 Freescale Semiconductor, Inc. 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 as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
|
||||||
|
* 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/linkage.h>
|
||||||
|
#include "hardware.h"
|
||||||
|
|
||||||
|
#define DDRC_MSTR 0x0
|
||||||
|
#define DDRC_STAT 0x4
|
||||||
|
#define DDRC_MRCTRL0 0x10
|
||||||
|
#define DDRC_MRCTRL1 0x14
|
||||||
|
#define DDRC_MRSTAT 0x18
|
||||||
|
#define DDRC_PWRCTL 0x30
|
||||||
|
#define DDRC_RFSHCTL3 0x60
|
||||||
|
#define DDRC_RFSHTMG 0x64
|
||||||
|
#define DDRC_DBG1 0x304
|
||||||
|
#define DDRC_SWCTL 0x320
|
||||||
|
#define DDRC_SWSTAT 0x324
|
||||||
|
#define DDRC_PSTAT 0x3fc
|
||||||
|
#define DDRC_PCTRL_0 0x490
|
||||||
|
#define DDRC_ZQCTL0 0x180
|
||||||
|
#define DDRC_DFIMISC 0x1b0
|
||||||
|
#define DDRC_DBGCAM 0x308
|
||||||
|
#define DDRPHY_LP_CON0 0x18
|
||||||
|
#define IOMUXC_GPR8 0x20
|
||||||
|
#define DDRPHY_MDLL_CON0 0xb0
|
||||||
|
#define DDRPHY_MDLL_CON1 0xb4
|
||||||
|
#define DDRPHY_OFFSETD_CON0 0x50
|
||||||
|
#define DDRPHY_OFFSETR_CON0 0x20
|
||||||
|
#define DDRPHY_OFFSETR_CON1 0x24
|
||||||
|
#define DDRPHY_OFFSETR_CON2 0x28
|
||||||
|
#define DDRPHY_OFFSETW_CON0 0x30
|
||||||
|
#define DDRPHY_OFFSETW_CON1 0x34
|
||||||
|
#define DDRPHY_OFFSETW_CON2 0x38
|
||||||
|
#define DDRPHY_CA_WLDSKEW_CON0 0x6c
|
||||||
|
#define DDRPHY_CA_DSKEW_CON0 0x7c
|
||||||
|
#define DDRPHY_CA_DSKEW_CON1 0x80
|
||||||
|
#define DDRPHY_CA_DSKEW_CON2 0x84
|
||||||
|
|
||||||
|
#define ANADIG_DIGPROG 0x800
|
||||||
|
|
||||||
|
.align 3
|
||||||
|
|
||||||
|
.macro switch_to_below_100m
|
||||||
|
|
||||||
|
ldr r7, =0x2
|
||||||
|
str r7, [r4, #DDRC_DBG1]
|
||||||
|
|
||||||
|
ldr r6, =0x36000000
|
||||||
|
1:
|
||||||
|
ldr r7, [r4, #DDRC_DBGCAM]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
bne 1b
|
||||||
|
|
||||||
|
ldr r6, =0x1
|
||||||
|
2:
|
||||||
|
ldr r7, [r4, #DDRC_MRSTAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
beq 2b
|
||||||
|
|
||||||
|
ldr r7, =0x10f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL1]
|
||||||
|
ldr r7, =0x800010f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
|
||||||
|
ldr r6, =0x1
|
||||||
|
3:
|
||||||
|
ldr r7, [r4, #DDRC_MRSTAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
beq 3b
|
||||||
|
|
||||||
|
ldr r7, =0x20f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
ldr r7, =0x8
|
||||||
|
str r7, [r4, #DDRC_MRCTRL1]
|
||||||
|
ldr r7, =0x800020f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
|
||||||
|
ldr r6, =0x1
|
||||||
|
4:
|
||||||
|
ldr r7, [r4, #DDRC_MRSTAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
beq 4b
|
||||||
|
|
||||||
|
ldr r7, =0x10f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
ldr r7, =0x1
|
||||||
|
str r7, [r4, #DDRC_MRCTRL1]
|
||||||
|
ldr r7, =0x800010f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
|
||||||
|
ldr r7, =0x20
|
||||||
|
str r7, [r4, #DDRC_PWRCTL]
|
||||||
|
|
||||||
|
ldr r6, =0x23
|
||||||
|
5:
|
||||||
|
ldr r7, [r4, #DDRC_STAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
bne 5b
|
||||||
|
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r4, #DDRC_SWCTL]
|
||||||
|
|
||||||
|
ldr r7, =0x03048001
|
||||||
|
str r7, [r4, #DDRC_MSTR]
|
||||||
|
|
||||||
|
ldr r7, =0x1
|
||||||
|
str r7, [r4, #DDRC_SWCTL]
|
||||||
|
|
||||||
|
ldr r6, =0x1
|
||||||
|
6:
|
||||||
|
ldr r7, [r4, #DDRC_SWSTAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
bne 6b
|
||||||
|
|
||||||
|
ldr r7, =0x10010100
|
||||||
|
str r7, [r5, #0x4]
|
||||||
|
|
||||||
|
ldr r6, =24000000
|
||||||
|
cmp r0, r6
|
||||||
|
beq 25f
|
||||||
|
|
||||||
|
ldr r7, =0x000B000D
|
||||||
|
str r7,[r4, #DDRC_RFSHTMG]
|
||||||
|
b 7f
|
||||||
|
|
||||||
|
25:
|
||||||
|
ldr r7, =0x00030004
|
||||||
|
str r7,[r4, #DDRC_RFSHTMG]
|
||||||
|
|
||||||
|
/* dram alt sel set to OSC */
|
||||||
|
ldr r7, =0x10000000
|
||||||
|
ldr r8, =0xa080
|
||||||
|
str r7, [r2, r8]
|
||||||
|
/* dram root set to from dram alt, div by 1 */
|
||||||
|
ldr r7, =0x11000000
|
||||||
|
ldr r8, =0x9880
|
||||||
|
str r7, [r2, r8]
|
||||||
|
b 8f
|
||||||
|
7:
|
||||||
|
/* dram alt sel set to pfd0_392m */
|
||||||
|
ldr r7, =0x15000000
|
||||||
|
ldr r8, =0xa080
|
||||||
|
str r7, [r2, r8]
|
||||||
|
/* dram root set to from dram alt, div by 4 */
|
||||||
|
ldr r7, =0x11000003
|
||||||
|
ldr r8, =0x9880
|
||||||
|
str r7, [r2, r8]
|
||||||
|
8:
|
||||||
|
ldr r7, =0x202ffd0
|
||||||
|
str r7, [r5, #DDRPHY_MDLL_CON0]
|
||||||
|
|
||||||
|
ldr r7, =0x1000007f
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||||
|
|
||||||
|
ldr r7, =0x7f7f7f7f
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETR_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETR_CON1]
|
||||||
|
ldr r7, =0x7f
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETR_CON2]
|
||||||
|
|
||||||
|
ldr r7, =0x7f7f7f7f
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETW_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETW_CON1]
|
||||||
|
ldr r7, =0x7f
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETW_CON2]
|
||||||
|
|
||||||
|
ldr r7, [r9, #ANADIG_DIGPROG]
|
||||||
|
and r7, r7, #0x11
|
||||||
|
cmp r7, #0x11
|
||||||
|
bne 20f
|
||||||
|
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r5, #DDRPHY_CA_WLDSKEW_CON0]
|
||||||
|
ldr r7, =0x60606060
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||||
|
ldr r7, =0x00006060
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||||
|
b 21f
|
||||||
|
20:
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||||
|
21:
|
||||||
|
ldr r7, =0x1100007f
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||||
|
ldr r7, =0x1000007f
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||||
|
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r4, #DDRC_PWRCTL]
|
||||||
|
|
||||||
|
ldr r6, =0x1
|
||||||
|
9:
|
||||||
|
ldr r7, [r4, #DDRC_MRSTAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
beq 9b
|
||||||
|
|
||||||
|
ldr r7, =0xf0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
ldr r7, =0x820
|
||||||
|
str r7, [r4, #DDRC_MRCTRL1]
|
||||||
|
ldr r7, =0x800000f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
|
||||||
|
ldr r6, =0x1
|
||||||
|
10:
|
||||||
|
ldr r7, [r4, #DDRC_MRSTAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
beq 10b
|
||||||
|
|
||||||
|
ldr r7, =0x800020
|
||||||
|
str r7, [r4, #DDRC_ZQCTL0]
|
||||||
|
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r4, #DDRC_DBG1]
|
||||||
|
|
||||||
|
/* enable auto self-refresh */
|
||||||
|
ldr r7, [r4, #DDRC_PWRCTL]
|
||||||
|
orr r7, r7, #(1 << 0)
|
||||||
|
str r7, [r4, #DDRC_PWRCTL]
|
||||||
|
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro switch_to_533m
|
||||||
|
|
||||||
|
ldr r7, =0x2
|
||||||
|
str r7, [r4, #DDRC_DBG1]
|
||||||
|
|
||||||
|
ldr r7, =0x78
|
||||||
|
str r7, [r3, #IOMUXC_GPR8]
|
||||||
|
orr r7, r7, #0x100
|
||||||
|
str r7, [r3, #IOMUXC_GPR8]
|
||||||
|
|
||||||
|
ldr r6, =0x30000000
|
||||||
|
11:
|
||||||
|
ldr r7, [r4, #DDRC_DBGCAM]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
bne 11b
|
||||||
|
|
||||||
|
ldr r6, =0x1
|
||||||
|
12:
|
||||||
|
ldr r7, [r4, #DDRC_MRSTAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
beq 12b
|
||||||
|
|
||||||
|
ldr r7, =0x10f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
ldr r7, =0x1
|
||||||
|
str r7, [r4, #DDRC_MRCTRL1]
|
||||||
|
ldr r7, =0x800010f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
|
||||||
|
ldr r7, =0x20
|
||||||
|
str r7, [r4, #DDRC_PWRCTL]
|
||||||
|
|
||||||
|
ldr r6, =0x23
|
||||||
|
13:
|
||||||
|
ldr r7, [r4, #DDRC_STAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
bne 13b
|
||||||
|
|
||||||
|
ldr r7, =0x03040001
|
||||||
|
str r7, [r4, #DDRC_MSTR]
|
||||||
|
|
||||||
|
ldr r7, =0x40800020
|
||||||
|
str r7, [r4, #DDRC_ZQCTL0]
|
||||||
|
|
||||||
|
|
||||||
|
ldr r7, =0x10210100
|
||||||
|
str r7, [r5, #0x4]
|
||||||
|
|
||||||
|
ldr r7, =0x00040046
|
||||||
|
str r7, [r4, #DDRC_RFSHTMG]
|
||||||
|
|
||||||
|
/* dram root set to from dram main, div by 2 */
|
||||||
|
ldr r7, =0x10000001
|
||||||
|
ldr r8, =0x9880
|
||||||
|
str r7, [r2, r8]
|
||||||
|
|
||||||
|
ldr r7, =0x1010007e
|
||||||
|
str r7, [r5, #DDRPHY_MDLL_CON0]
|
||||||
|
|
||||||
|
ldr r7, =0x10000008
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||||
|
|
||||||
|
ldr r7, =0x08080808
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETR_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETR_CON1]
|
||||||
|
ldr r7, =0x8
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETR_CON2]
|
||||||
|
|
||||||
|
ldr r7, =0x08080808
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETW_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETW_CON1]
|
||||||
|
ldr r7, =0x8
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETW_CON2]
|
||||||
|
|
||||||
|
ldr r7, [r9, #ANADIG_DIGPROG]
|
||||||
|
and r7, r7, #0x11
|
||||||
|
cmp r7, #0x11
|
||||||
|
bne 22f
|
||||||
|
|
||||||
|
ldr r7, =0x40404040
|
||||||
|
str r7, [r5, #DDRPHY_CA_WLDSKEW_CON0]
|
||||||
|
ldr r7, =0x18181818
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||||
|
ldr r7, =0x40401818
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||||
|
b 23f
|
||||||
|
22:
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||||
|
23:
|
||||||
|
ldr r7, =0x11000008
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||||
|
ldr r7, =0x10000008
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||||
|
|
||||||
|
ldr r6, =0x4
|
||||||
|
14:
|
||||||
|
ldr r7, [r5, #DDRPHY_MDLL_CON1]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
bne 14b
|
||||||
|
|
||||||
|
ldr r7, =0x1
|
||||||
|
str r7, [r4, #DDRC_RFSHCTL3]
|
||||||
|
ldr r7, =0x3
|
||||||
|
str r7, [r4, #DDRC_RFSHCTL3]
|
||||||
|
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r4, #DDRC_PWRCTL]
|
||||||
|
|
||||||
|
ldr r6, =0x1
|
||||||
|
15:
|
||||||
|
ldr r7, [r4, #DDRC_MRSTAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
beq 15b
|
||||||
|
|
||||||
|
ldr r7, =0x10f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL1]
|
||||||
|
ldr r7, =0x800010f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
|
||||||
|
ldr r6, =0x1
|
||||||
|
16:
|
||||||
|
ldr r7, [r4, #DDRC_MRSTAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
beq 16b
|
||||||
|
|
||||||
|
ldr r7, =0xf0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
ldr r7, =0x930
|
||||||
|
str r7, [r4, #DDRC_MRCTRL1]
|
||||||
|
ldr r7, =0x800000f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r4, #DDRC_RFSHCTL3]
|
||||||
|
ldr r7, =0x2
|
||||||
|
str r7, [r4, #DDRC_RFSHCTL3]
|
||||||
|
|
||||||
|
ldr r6, =0x1
|
||||||
|
17:
|
||||||
|
ldr r7, [r4, #DDRC_MRSTAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
beq 17b
|
||||||
|
|
||||||
|
ldr r7, =0xf0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
ldr r7, =0x930
|
||||||
|
str r7, [r4, #DDRC_MRCTRL1]
|
||||||
|
ldr r7, =0x800000f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
|
||||||
|
ldr r6, =0x1
|
||||||
|
18:
|
||||||
|
ldr r7, [r4, #DDRC_MRSTAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
beq 18b
|
||||||
|
|
||||||
|
ldr r7, =0x20f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
ldr r7, =0x408
|
||||||
|
str r7, [r4, #DDRC_MRCTRL1]
|
||||||
|
ldr r7, =0x800020f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
|
||||||
|
ldr r6, =0x1
|
||||||
|
19:
|
||||||
|
ldr r7, [r4, #DDRC_MRSTAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
beq 19b
|
||||||
|
|
||||||
|
ldr r7, =0x10f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
ldr r7, =0x4
|
||||||
|
str r7, [r4, #DDRC_MRCTRL1]
|
||||||
|
ldr r7, =0x800010f0
|
||||||
|
str r7, [r4, #DDRC_MRCTRL0]
|
||||||
|
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r4, #DDRC_DBG1]
|
||||||
|
|
||||||
|
/* enable auto self-refresh */
|
||||||
|
ldr r7, [r4, #DDRC_PWRCTL]
|
||||||
|
orr r7, r7, #(1 << 0)
|
||||||
|
str r7, [r4, #DDRC_PWRCTL]
|
||||||
|
|
||||||
|
.endm
|
||||||
|
|
||||||
|
ENTRY(imx7d_ddr3_freq_change)
|
||||||
|
push {r2 - r9}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To ensure no page table walks occur in DDR, we
|
||||||
|
* have a another page table stored in IRAM that only
|
||||||
|
* contains entries pointing to IRAM, AIPS1 and AIPS2.
|
||||||
|
* We need to set the TTBR1 to the new IRAM TLB.
|
||||||
|
* Do the following steps:
|
||||||
|
* 1. Flush the Branch Target Address Cache (BTAC)
|
||||||
|
* 2. Set TTBR1 to point to IRAM page table.
|
||||||
|
* 3. Disable page table walks in TTBR0 (PD0 = 1)
|
||||||
|
* 4. Set TTBR0.N=1, implying 0-2G is translated by TTBR0
|
||||||
|
* and 2-4G is translated by TTBR1.
|
||||||
|
*/
|
||||||
|
ldr r6, =0x0
|
||||||
|
mcr p15, 0, r6, c8, c3, 0
|
||||||
|
|
||||||
|
ldr r6, =iram_tlb_phys_addr
|
||||||
|
ldr r7, [r6]
|
||||||
|
|
||||||
|
/* Disable Branch Prediction, Z bit in SCTLR. */
|
||||||
|
mrc p15, 0, r6, c1, c0, 0
|
||||||
|
bic r6, r6, #0x800
|
||||||
|
mcr p15, 0, r6, c1, c0, 0
|
||||||
|
|
||||||
|
/* Flush the Branch Target Address Cache (BTAC) */
|
||||||
|
ldr r6, =0x0
|
||||||
|
mcr p15, 0, r6, c7, c1, 6
|
||||||
|
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
/* Store the IRAM table in TTBR1 */
|
||||||
|
mcr p15, 0, r7, c2, c0, 1
|
||||||
|
|
||||||
|
/* Read TTBCR and set PD0=1, N = 1 */
|
||||||
|
mrc p15, 0, r6, c2, c0, 2
|
||||||
|
orr r6, r6, #0x11
|
||||||
|
mcr p15, 0, r6, c2, c0, 2
|
||||||
|
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
|
||||||
|
/* flush the TLB */
|
||||||
|
ldr r6, =0x0
|
||||||
|
mcr p15, 0, r6, c8, c3, 0
|
||||||
|
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
|
||||||
|
ldr r2, =IMX_IO_P2V(MX7D_CCM_BASE_ADDR)
|
||||||
|
ldr r3, =IMX_IO_P2V(MX7D_IOMUXC_GPR_BASE_ADDR)
|
||||||
|
ldr r4, =IMX_IO_P2V(MX7D_DDRC_BASE_ADDR)
|
||||||
|
ldr r5, =IMX_IO_P2V(MX7D_DDRC_PHY_BASE_ADDR)
|
||||||
|
ldr r9, =IMX_IO_P2V(MX7D_ANATOP_BASE_ADDR)
|
||||||
|
|
||||||
|
ldr r6, =100000000
|
||||||
|
cmp r0, r6
|
||||||
|
bgt set_to_533m
|
||||||
|
|
||||||
|
set_to_below_100m:
|
||||||
|
switch_to_below_100m
|
||||||
|
b done
|
||||||
|
|
||||||
|
set_to_533m:
|
||||||
|
switch_to_533m
|
||||||
|
b done
|
||||||
|
|
||||||
|
done:
|
||||||
|
/* Enable L1 data cache. */
|
||||||
|
mrc p15, 0, r6, c1, c0, 0
|
||||||
|
orr r6, r6, #0x4
|
||||||
|
mcr p15, 0, r6, c1, c0, 0
|
||||||
|
|
||||||
|
/* Restore the TTBCR */
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
|
||||||
|
/* Read TTBCR and set PD0=0, N = 0 */
|
||||||
|
mrc p15, 0, r6, c2, c0, 2
|
||||||
|
bic r6, r6, #0x11
|
||||||
|
mcr p15, 0, r6, c2, c0, 2
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
|
||||||
|
/* flush the TLB */
|
||||||
|
ldr r6, =0x0
|
||||||
|
mcr p15, 0, r6, c8, c3, 0
|
||||||
|
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
|
||||||
|
/* Enable Branch Prediction, Z bit in SCTLR. */
|
||||||
|
mrc p15, 0, r6, c1, c0, 0
|
||||||
|
orr r6, r6, #0x800
|
||||||
|
mcr p15, 0, r6, c1, c0, 0
|
||||||
|
|
||||||
|
/* Flush the Branch Target Address Cache (BTAC) */
|
||||||
|
ldr r6, =0x0
|
||||||
|
mcr p15, 0, r6, c7, c1, 6
|
||||||
|
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
|
||||||
|
/* Restore registers */
|
||||||
|
pop {r2 - r9}
|
||||||
|
mov pc, lr
|
||||||
|
.ltorg
|
||||||
|
ENDPROC(imx7d_ddr3_freq_change)
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2015 Freescale Semiconductor, Inc.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/of_device.h>
|
||||||
|
|
||||||
|
#include "hardware.h"
|
||||||
|
|
||||||
|
#define DDRC_MSTR 0x0
|
||||||
|
#define BM_DDRC_MSTR_DDR3 0x1
|
||||||
|
#define BM_DDRC_MSTR_LPDDR2 0x4
|
||||||
|
#define BM_DDRC_MSTR_LPDDR3 0x8
|
||||||
|
|
||||||
|
static int ddr_type;
|
||||||
|
|
||||||
|
static int imx_ddrc_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device_node *np = pdev->dev.of_node;
|
||||||
|
void __iomem *ddrc_base, *reg;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
|
ddrc_base = of_iomap(np, 0);
|
||||||
|
WARN_ON(!ddrc_base);
|
||||||
|
|
||||||
|
reg = ddrc_base + DDRC_MSTR;
|
||||||
|
/* Get ddr type */
|
||||||
|
val = readl_relaxed(reg);
|
||||||
|
val &= (BM_DDRC_MSTR_DDR3 | BM_DDRC_MSTR_LPDDR2
|
||||||
|
| BM_DDRC_MSTR_LPDDR3);
|
||||||
|
|
||||||
|
switch (val) {
|
||||||
|
case BM_DDRC_MSTR_DDR3:
|
||||||
|
pr_info("DDR type is DDR3!\n");
|
||||||
|
ddr_type = IMX_DDR_TYPE_DDR3;
|
||||||
|
break;
|
||||||
|
case BM_DDRC_MSTR_LPDDR2:
|
||||||
|
pr_info("DDR type is LPDDR2!\n");
|
||||||
|
ddr_type = IMX_DDR_TYPE_LPDDR2;
|
||||||
|
break;
|
||||||
|
case BM_DDRC_MSTR_LPDDR3:
|
||||||
|
pr_info("DDR type is LPDDR3!\n");
|
||||||
|
ddr_type = IMX_DDR_TYPE_LPDDR3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int imx_ddrc_get_ddr_type(void)
|
||||||
|
{
|
||||||
|
return ddr_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct of_device_id imx_ddrc_dt_ids[] = {
|
||||||
|
{ .compatible = "fsl,imx7-ddrc", },
|
||||||
|
{ /* sentinel */ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct platform_driver imx_ddrc_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "imx-ddrc",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.of_match_table = imx_ddrc_dt_ids,
|
||||||
|
},
|
||||||
|
.probe = imx_ddrc_probe,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init imx_ddrc_init(void)
|
||||||
|
{
|
||||||
|
return platform_driver_register(&imx_ddrc_driver);
|
||||||
|
}
|
||||||
|
postcore_initcall(imx_ddrc_init);
|
|
@ -81,13 +81,16 @@
|
||||||
* CCM 0x020c4000+0x004000 -> 0xf42c4000+0x004000
|
* CCM 0x020c4000+0x004000 -> 0xf42c4000+0x004000
|
||||||
* ANATOP 0x020c8000+0x004000 -> 0xf42c8000+0x004000
|
* ANATOP 0x020c8000+0x004000 -> 0xf42c8000+0x004000
|
||||||
* UART4 0x021f0000+0x004000 -> 0xf42f0000+0x004000
|
* UART4 0x021f0000+0x004000 -> 0xf42f0000+0x004000
|
||||||
|
* mx7d:
|
||||||
|
* CCM 0x30380000+0x010000 -> 0xf5380000+0x010000
|
||||||
|
* ANATOP 0x30360000+0x010000 -> 0xf5360000+0x010000
|
||||||
|
* UART1 0x30860000+0x010000 -> 0xf5860000+0x010000
|
||||||
*/
|
*/
|
||||||
#define IMX_IO_P2V(x) ( \
|
#define IMX_IO_P2V(x) ( \
|
||||||
(((x) & 0x80000000) >> 7) | \
|
|
||||||
(0xf4000000 + \
|
(0xf4000000 + \
|
||||||
(((x) & 0x50000000) >> 6) + \
|
(((x) & 0x50000000) >> 4) + \
|
||||||
(((x) & 0x0b000000) >> 4) + \
|
(((x) & 0x0a000000) >> 4) + \
|
||||||
(((x) & 0x000fffff))))
|
(((x) & 0x00ffffff))))
|
||||||
|
|
||||||
#define IMX_IO_ADDRESS(x) IOMEM(IMX_IO_P2V(x))
|
#define IMX_IO_ADDRESS(x) IOMEM(IMX_IO_P2V(x))
|
||||||
|
|
||||||
|
@ -99,6 +102,7 @@
|
||||||
#include "mx2x.h"
|
#include "mx2x.h"
|
||||||
#include "mx21.h"
|
#include "mx21.h"
|
||||||
#include "mx27.h"
|
#include "mx27.h"
|
||||||
|
#include "mx7.h"
|
||||||
|
|
||||||
#define imx_map_entry(soc, name, _type) { \
|
#define imx_map_entry(soc, name, _type) { \
|
||||||
.virtual = soc ## _IO_P2V(soc ## _ ## name ## _BASE_ADDR), \
|
.virtual = soc ## _IO_P2V(soc ## _ ## name ## _BASE_ADDR), \
|
||||||
|
|
|
@ -0,0 +1,444 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015-2016 Freescale Semiconductor, Inc. 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 as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
|
||||||
|
* 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/linkage.h>
|
||||||
|
#include "hardware.h"
|
||||||
|
|
||||||
|
#define DDRC_MSTR 0x0
|
||||||
|
#define DDRC_STAT 0x4
|
||||||
|
#define DDRC_PWRCTL 0x30
|
||||||
|
#define DDRC_RFSHTMG 0x64
|
||||||
|
#define DDRC_DBG1 0x304
|
||||||
|
#define DDRC_PSTAT 0x3fc
|
||||||
|
#define DDRC_PCTRL_0 0x490
|
||||||
|
#define DDRC_DFIMISC 0x1b0
|
||||||
|
#define DDRC_DBGCAM 0x308
|
||||||
|
#define DDRC_SWCTL 0x320
|
||||||
|
#define DDRC_SWSTAT 0x324
|
||||||
|
#define DDRPHY_LP_CON0 0x18
|
||||||
|
#define IOMUXC_GPR8 0x20
|
||||||
|
#define DDRPHY_PHY_CON1 0x4
|
||||||
|
#define DDRPHY_MDLL_CON0 0xb0
|
||||||
|
#define DDRPHY_MDLL_CON1 0xb4
|
||||||
|
#define DDRPHY_OFFSETD_CON0 0x50
|
||||||
|
#define DDRPHY_OFFSETR_CON0 0x20
|
||||||
|
#define DDRPHY_OFFSETR_CON1 0x24
|
||||||
|
#define DDRPHY_OFFSETR_CON2 0x28
|
||||||
|
#define DDRPHY_OFFSETW_CON0 0x30
|
||||||
|
#define DDRPHY_OFFSETW_CON1 0x34
|
||||||
|
#define DDRPHY_OFFSETW_CON2 0x38
|
||||||
|
#define DDRPHY_RFSHTMG 0x64
|
||||||
|
#define DDRPHY_CA_WLDSKEW_CON0 0x6c
|
||||||
|
#define DDRPHY_CA_DSKEW_CON0 0x7c
|
||||||
|
#define DDRPHY_CA_DSKEW_CON1 0x80
|
||||||
|
#define DDRPHY_CA_DSKEW_CON2 0x84
|
||||||
|
|
||||||
|
#define ANADIG_DIGPROG 0x800
|
||||||
|
|
||||||
|
.align 3
|
||||||
|
|
||||||
|
.macro ddrc_prepare
|
||||||
|
|
||||||
|
/* disable port */
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r4, #DDRC_PCTRL_0]
|
||||||
|
|
||||||
|
/* wait port busy done */
|
||||||
|
ldr r6, =0x10001
|
||||||
|
1:
|
||||||
|
ldr r7, [r4, #DDRC_PSTAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, #0
|
||||||
|
bne 1b
|
||||||
|
|
||||||
|
ldr r7, =0x20
|
||||||
|
str r7, [r4, #DDRC_PWRCTL]
|
||||||
|
|
||||||
|
ldr r6, =0x23
|
||||||
|
2:
|
||||||
|
ldr r7, [r4, #DDRC_STAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
bne 2b
|
||||||
|
|
||||||
|
ldr r7, =0x1
|
||||||
|
str r7, [r4, #DDRC_DBG1]
|
||||||
|
|
||||||
|
ldr r6, =0x30000000
|
||||||
|
3:
|
||||||
|
ldr r7, [r4, #DDRC_DBGCAM]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
bne 3b
|
||||||
|
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r4, #DDRC_SWCTL]
|
||||||
|
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r4, #DDRC_DFIMISC]
|
||||||
|
|
||||||
|
ldr r7, =0x1
|
||||||
|
str r7, [r4, #DDRC_SWCTL]
|
||||||
|
|
||||||
|
ldr r6, =0x1
|
||||||
|
4:
|
||||||
|
ldr r7, [r4, #DDRC_SWSTAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
bne 4b
|
||||||
|
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro ddrc_done
|
||||||
|
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r4, #DDRC_PWRCTL]
|
||||||
|
|
||||||
|
ldr r6, =0x3
|
||||||
|
5:
|
||||||
|
ldr r7, [r4, #DDRC_STAT]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
beq 5b
|
||||||
|
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r4, #DDRC_DBG1]
|
||||||
|
|
||||||
|
ldr r7, =0x1
|
||||||
|
str r7, [r4, #DDRC_PCTRL_0]
|
||||||
|
|
||||||
|
/* enable auto self-refresh */
|
||||||
|
ldr r7, [r4, #DDRC_PWRCTL]
|
||||||
|
orr r7, r7, #(1 << 0)
|
||||||
|
str r7, [r4, #DDRC_PWRCTL]
|
||||||
|
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro switch_to_below_100m
|
||||||
|
|
||||||
|
/* LPDDR2 and LPDDR3 has different setting */
|
||||||
|
ldr r8, [r4, #DDRC_MSTR]
|
||||||
|
ands r8, r8, #0x4
|
||||||
|
bne 9f
|
||||||
|
|
||||||
|
/* LPDDR3 */
|
||||||
|
ldr r7, =0x00000100
|
||||||
|
str r7, [r5, #DDRPHY_PHY_CON1]
|
||||||
|
b 10f
|
||||||
|
9:
|
||||||
|
/* LPDDR2 */
|
||||||
|
ldr r7, =0x10010100
|
||||||
|
str r7, [r5, #DDRPHY_PHY_CON1]
|
||||||
|
10:
|
||||||
|
ldr r6, =24000000
|
||||||
|
cmp r0, r6
|
||||||
|
beq 16f
|
||||||
|
|
||||||
|
ldr r7, =0x0005000B
|
||||||
|
str r7, [r4, #DDRC_RFSHTMG]
|
||||||
|
b 6f
|
||||||
|
16:
|
||||||
|
ldr r7, =0x00010003
|
||||||
|
str r7, [r4, #DDRC_RFSHTMG]
|
||||||
|
|
||||||
|
/* dram alt sel set to OSC */
|
||||||
|
ldr r7, =0x10000000
|
||||||
|
ldr r8, =0xa080
|
||||||
|
str r7, [r2, r8]
|
||||||
|
/* dram root set to from dram alt, div by 1 */
|
||||||
|
ldr r7, =0x11000000
|
||||||
|
ldr r8, =0x9880
|
||||||
|
str r7, [r2, r8]
|
||||||
|
b 7f
|
||||||
|
|
||||||
|
6:
|
||||||
|
/* dram alt sel set to pfd0_392m */
|
||||||
|
ldr r7, =0x15000000
|
||||||
|
ldr r8, =0xa080
|
||||||
|
str r7, [r2, r8]
|
||||||
|
/* dram root set to from dram alt, div by 4 */
|
||||||
|
ldr r7, =0x11000003
|
||||||
|
ldr r8, =0x9880
|
||||||
|
str r7, [r2, r8]
|
||||||
|
7:
|
||||||
|
ldr r7, =0x202ffd0
|
||||||
|
str r7, [r5, #DDRPHY_MDLL_CON0]
|
||||||
|
|
||||||
|
ldr r7, =0x7f
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||||
|
|
||||||
|
ldr r7, =0x7f7f7f7f
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETR_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETR_CON1]
|
||||||
|
ldr r7, =0x7f
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETR_CON2]
|
||||||
|
|
||||||
|
ldr r7, =0x7f7f7f7f
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETW_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETW_CON1]
|
||||||
|
ldr r7, =0x7f
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETW_CON2]
|
||||||
|
|
||||||
|
ldr r7, [r9, #ANADIG_DIGPROG]
|
||||||
|
and r7, r7, #0x11
|
||||||
|
cmp r7, #0x11
|
||||||
|
bne 11f
|
||||||
|
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r5, #DDRPHY_CA_WLDSKEW_CON0]
|
||||||
|
ldr r7, =0x60606060
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||||
|
ldr r7, =0x00006060
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||||
|
b 12f
|
||||||
|
11:
|
||||||
|
ldr r7, =0x0
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||||
|
12:
|
||||||
|
ldr r7, =0x100007f
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||||
|
ldr r7, =0x7f
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||||
|
|
||||||
|
.endm
|
||||||
|
|
||||||
|
.macro switch_to_533m
|
||||||
|
|
||||||
|
ldr r7, =0x10210100
|
||||||
|
str r7, [r5, #DDRPHY_PHY_CON1]
|
||||||
|
|
||||||
|
ldr r7, =0x00200038
|
||||||
|
str r7, [r4, #DDRC_RFSHTMG]
|
||||||
|
|
||||||
|
/* dram root set to from dram main, div by 2 */
|
||||||
|
ldr r7, =0x10000001
|
||||||
|
ldr r8, =0x9880
|
||||||
|
str r7, [r2, r8]
|
||||||
|
|
||||||
|
ldr r7, =0x1010007e
|
||||||
|
str r7, [r5, #DDRPHY_MDLL_CON0]
|
||||||
|
|
||||||
|
ldr r7, =0x10000008
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||||
|
|
||||||
|
ldr r7, =0x08080808
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETR_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETR_CON1]
|
||||||
|
ldr r7, =0x8
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETR_CON2]
|
||||||
|
|
||||||
|
ldr r7, =0x08080808
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETW_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETW_CON1]
|
||||||
|
ldr r7, =0x8
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETW_CON2]
|
||||||
|
|
||||||
|
/* LPDDR2 and LPDDR3 has different setting */
|
||||||
|
ldr r8, [r4, #DDRC_MSTR]
|
||||||
|
ands r8, r8, #0x4
|
||||||
|
beq 15f
|
||||||
|
|
||||||
|
ldr r7, [r9, #ANADIG_DIGPROG]
|
||||||
|
and r7, r7, #0x11
|
||||||
|
cmp r7, #0x11
|
||||||
|
bne 14f
|
||||||
|
|
||||||
|
ldr r7, =0x08080808
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||||
|
ldr r7, =0x0a0a0808
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||||
|
ldr r7, =0x0a0a0a0a
|
||||||
|
str r7, [r5, #DDRPHY_CA_WLDSKEW_CON0]
|
||||||
|
b 14f
|
||||||
|
15:
|
||||||
|
ldr r7, [r9, #ANADIG_DIGPROG]
|
||||||
|
and r7, r7, #0x11
|
||||||
|
cmp r7, #0x11
|
||||||
|
bne 13f
|
||||||
|
|
||||||
|
ldr r7, =0x1c1c1c1c
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||||
|
ldr r7, =0x30301c1c
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||||
|
ldr r7, =0x30303030
|
||||||
|
str r7, [r5, #DDRPHY_CA_WLDSKEW_CON0]
|
||||||
|
b 14f
|
||||||
|
13:
|
||||||
|
ldr r7, =0x08080808
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON0]
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON1]
|
||||||
|
ldr r7, =0x0808
|
||||||
|
str r7, [r5, #DDRPHY_CA_DSKEW_CON2]
|
||||||
|
14:
|
||||||
|
ldr r7, =0x11000008
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||||
|
ldr r7, =0x10000008
|
||||||
|
str r7, [r5, #DDRPHY_OFFSETD_CON0]
|
||||||
|
|
||||||
|
ldr r6, =0x4
|
||||||
|
8:
|
||||||
|
ldr r7, [r5, #DDRPHY_MDLL_CON1]
|
||||||
|
and r7, r7, r6
|
||||||
|
cmp r7, r6
|
||||||
|
bne 8b
|
||||||
|
|
||||||
|
.endm
|
||||||
|
|
||||||
|
ENTRY(imx_lpddr3_freq_change)
|
||||||
|
push {r2 - r9}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* To ensure no page table walks occur in DDR, we
|
||||||
|
* have a another page table stored in IRAM that only
|
||||||
|
* contains entries pointing to IRAM, AIPS1 and AIPS2.
|
||||||
|
* We need to set the TTBR1 to the new IRAM TLB.
|
||||||
|
* Do the following steps:
|
||||||
|
* 1. Flush the Branch Target Address Cache (BTAC)
|
||||||
|
* 2. Set TTBR1 to point to IRAM page table.
|
||||||
|
* 3. Disable page table walks in TTBR0 (PD0 = 1)
|
||||||
|
* 4. Set TTBR0.N=1, implying 0-2G is translated by TTBR0
|
||||||
|
* and 2-4G is translated by TTBR1.
|
||||||
|
*/
|
||||||
|
|
||||||
|
ldr r6, =iram_tlb_phys_addr
|
||||||
|
ldr r7, [r6]
|
||||||
|
|
||||||
|
/* Flush the Branch Target Address Cache (BTAC) */
|
||||||
|
ldr r6, =0x0
|
||||||
|
mcr p15, 0, r6, c7, c1, 6
|
||||||
|
|
||||||
|
/* Disable Branch Prediction, Z bit in SCTLR. */
|
||||||
|
mrc p15, 0, r6, c1, c0, 0
|
||||||
|
bic r6, r6, #0x800
|
||||||
|
mcr p15, 0, r6, c1, c0, 0
|
||||||
|
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
/* Store the IRAM table in TTBR1 */
|
||||||
|
mcr p15, 0, r7, c2, c0, 1
|
||||||
|
|
||||||
|
/* Read TTBCR and set PD0=1, N = 1 */
|
||||||
|
mrc p15, 0, r6, c2, c0, 2
|
||||||
|
orr r6, r6, #0x11
|
||||||
|
mcr p15, 0, r6, c2, c0, 2
|
||||||
|
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
|
||||||
|
/* flush the TLB */
|
||||||
|
ldr r6, =0x0
|
||||||
|
mcr p15, 0, r6, c8, c3, 0
|
||||||
|
|
||||||
|
/* Disable L1 data cache. */
|
||||||
|
mrc p15, 0, r6, c1, c0, 0
|
||||||
|
bic r6, r6, #0x4
|
||||||
|
mcr p15, 0, r6, c1, c0, 0
|
||||||
|
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
|
||||||
|
ldr r2, =IMX_IO_P2V(MX7D_CCM_BASE_ADDR)
|
||||||
|
ldr r3, =IMX_IO_P2V(MX7D_IOMUXC_GPR_BASE_ADDR)
|
||||||
|
ldr r4, =IMX_IO_P2V(MX7D_DDRC_BASE_ADDR)
|
||||||
|
ldr r5, =IMX_IO_P2V(MX7D_DDRC_PHY_BASE_ADDR)
|
||||||
|
ldr r9, =IMX_IO_P2V(MX7D_ANATOP_BASE_ADDR)
|
||||||
|
|
||||||
|
ddrc_prepare
|
||||||
|
|
||||||
|
ldr r6, =100000000
|
||||||
|
cmp r0, r6
|
||||||
|
bgt set_to_533m
|
||||||
|
|
||||||
|
set_to_below_100m:
|
||||||
|
switch_to_below_100m
|
||||||
|
b done
|
||||||
|
|
||||||
|
set_to_533m:
|
||||||
|
switch_to_533m
|
||||||
|
b done
|
||||||
|
|
||||||
|
done:
|
||||||
|
ddrc_done
|
||||||
|
|
||||||
|
/* Enable L1 data cache. */
|
||||||
|
mrc p15, 0, r6, c1, c0, 0
|
||||||
|
orr r6, r6, #0x4
|
||||||
|
mcr p15, 0, r6, c1, c0, 0
|
||||||
|
|
||||||
|
/* Restore the TTBCR */
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
|
||||||
|
/* Read TTBCR and set PD0=0, N = 0 */
|
||||||
|
mrc p15, 0, r6, c2, c0, 2
|
||||||
|
bic r6, r6, #0x11
|
||||||
|
mcr p15, 0, r6, c2, c0, 2
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
|
||||||
|
/* flush the TLB */
|
||||||
|
ldr r6, =0x0
|
||||||
|
mcr p15, 0, r6, c8, c3, 0
|
||||||
|
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
|
||||||
|
/* Enable Branch Prediction, Z bit in SCTLR. */
|
||||||
|
mrc p15, 0, r6, c1, c0, 0
|
||||||
|
orr r6, r6, #0x800
|
||||||
|
mcr p15, 0, r6, c1, c0, 0
|
||||||
|
|
||||||
|
/* Flush the Branch Target Address Cache (BTAC) */
|
||||||
|
ldr r6, =0x0
|
||||||
|
mcr p15, 0, r6, c7, c1, 6
|
||||||
|
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
nop
|
||||||
|
|
||||||
|
/* Restore registers */
|
||||||
|
pop {r2 - r9}
|
||||||
|
mov pc, lr
|
||||||
|
ENDPROC(imx_lpddr3_freq_change)
|
|
@ -101,6 +101,13 @@ static void __init imx7d_init_irq(void)
|
||||||
irqchip_init();
|
irqchip_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void __init imx7d_map_io(void)
|
||||||
|
{
|
||||||
|
debug_ll_io_init();
|
||||||
|
imx7_pm_map_io();
|
||||||
|
imx_busfreq_map_io();
|
||||||
|
}
|
||||||
|
|
||||||
static const char *const imx7d_dt_compat[] __initconst = {
|
static const char *const imx7d_dt_compat[] __initconst = {
|
||||||
"fsl,imx7d",
|
"fsl,imx7d",
|
||||||
"fsl,imx7s",
|
"fsl,imx7s",
|
||||||
|
@ -108,6 +115,7 @@ static const char *const imx7d_dt_compat[] __initconst = {
|
||||||
};
|
};
|
||||||
|
|
||||||
DT_MACHINE_START(IMX7D, "Freescale i.MX7 Dual (Device Tree)")
|
DT_MACHINE_START(IMX7D, "Freescale i.MX7 Dual (Device Tree)")
|
||||||
|
.map_io = imx7d_map_io,
|
||||||
.init_irq = imx7d_init_irq,
|
.init_irq = imx7d_init_irq,
|
||||||
.init_machine = imx7d_init_machine,
|
.init_machine = imx7d_init_machine,
|
||||||
.init_late = imx7d_init_late,
|
.init_late = imx7d_init_late,
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Freescale Semiconductor, Inc. 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 as
|
||||||
|
* * published by the Free Software Foundation.
|
||||||
|
* */
|
||||||
|
|
||||||
|
#ifndef __ASM_ARCH_MX7_IOMAP_H__
|
||||||
|
#define __ASM_ARCH_MX7_IOMAP_H__
|
||||||
|
|
||||||
|
#define MX7D_IO_P2V(x) IMX_IO_P2V(x)
|
||||||
|
#define MX7D_IO_ADDRESS(x) IOMEM(MX7D_IO_P2V(x))
|
||||||
|
|
||||||
|
#define MX7D_LPSR_BASE_ADDR 0x30270000
|
||||||
|
#define MX7D_LPSR_SIZE 0x10000
|
||||||
|
#define MX7D_CCM_BASE_ADDR 0x30380000
|
||||||
|
#define MX7D_CCM_SIZE 0x10000
|
||||||
|
#define MX7D_IOMUXC_BASE_ADDR 0x30330000
|
||||||
|
#define MX7D_IOMUXC_SIZE 0x10000
|
||||||
|
#define MX7D_IOMUXC_GPR_BASE_ADDR 0x30340000
|
||||||
|
#define MX7D_IOMUXC_GPR_SIZE 0x10000
|
||||||
|
#define MX7D_ANATOP_BASE_ADDR 0x30360000
|
||||||
|
#define MX7D_ANATOP_SIZE 0x10000
|
||||||
|
#define MX7D_SNVS_BASE_ADDR 0x30370000
|
||||||
|
#define MX7D_SNVS_SIZE 0x10000
|
||||||
|
#define MX7D_GPC_BASE_ADDR 0x303a0000
|
||||||
|
#define MX7D_GPC_SIZE 0x10000
|
||||||
|
#define MX7D_SRC_BASE_ADDR 0x30390000
|
||||||
|
#define MX7D_SRC_SIZE 0x10000
|
||||||
|
#define MX7D_DDRC_BASE_ADDR 0x307a0000
|
||||||
|
#define MX7D_DDRC_SIZE 0x10000
|
||||||
|
#define MX7D_DDRC_PHY_BASE_ADDR 0x30790000
|
||||||
|
#define MX7D_DDRC_PHY_SIZE 0x10000
|
||||||
|
#define MX7D_AIPS1_BASE_ADDR 0x30000000
|
||||||
|
#define MX7D_AIPS1_SIZE 0x400000
|
||||||
|
#define MX7D_AIPS2_BASE_ADDR 0x30400000
|
||||||
|
#define MX7D_AIPS2_SIZE 0x400000
|
||||||
|
#define MX7D_AIPS3_BASE_ADDR 0x30900000
|
||||||
|
#define MX7D_AIPS3_SIZE 0x300000
|
||||||
|
#define MX7D_GIC_BASE_ADDR 0x31000000
|
||||||
|
#define MX7D_GIC_SIZE 0x100000
|
||||||
|
|
||||||
|
#define TT_ATTRIB_NON_CACHEABLE_1M 0x802
|
||||||
|
#define MX7_IRAM_TLB_SIZE 0x4000
|
||||||
|
#define MX7_SUSPEND_OCRAM_SIZE 0x1000
|
||||||
|
#define MX7_CPUIDLE_OCRAM_ADDR_OFFSET 0x1000
|
||||||
|
#define MX7_CPUIDLE_OCRAM_SIZE 0x1000
|
||||||
|
#define MX7_BUSFREQ_OCRAM_ADDR_OFFSET 0x2000
|
||||||
|
#define MX7_BUSFREQ_OCRAM_SIZE 0x1000
|
||||||
|
|
||||||
|
#endif
|
|
@ -33,7 +33,9 @@
|
||||||
#define MXC_CPU_IMX7D 0x72
|
#define MXC_CPU_IMX7D 0x72
|
||||||
#define MXC_CPU_IMX7ULP 0xff
|
#define MXC_CPU_IMX7ULP 0xff
|
||||||
|
|
||||||
|
#define IMX_DDR_TYPE_DDR3 0
|
||||||
#define IMX_DDR_TYPE_LPDDR2 1
|
#define IMX_DDR_TYPE_LPDDR2 1
|
||||||
|
#define IMX_DDR_TYPE_LPDDR3 2
|
||||||
|
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
extern unsigned int __mxc_cpu_type;
|
extern unsigned int __mxc_cpu_type;
|
||||||
|
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Freescale Semiconductor, Inc.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/busfreq-imx.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_address.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/of_fdt.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
|
#include <asm/cacheflush.h>
|
||||||
|
#include <asm/mach/map.h>
|
||||||
|
#include <asm/proc-fns.h>
|
||||||
|
#include <asm/tlb.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "hardware.h"
|
||||||
|
|
||||||
|
#define M4_OCRAMS_RESERVED_SIZE 0xc
|
||||||
|
|
||||||
|
extern unsigned long iram_tlb_base_addr;
|
||||||
|
extern unsigned long iram_tlb_phys_addr;
|
||||||
|
|
||||||
|
static struct map_desc imx7_pm_io_desc[] __initdata = {
|
||||||
|
imx_map_entry(MX7D, AIPS1, MT_DEVICE),
|
||||||
|
imx_map_entry(MX7D, AIPS2, MT_DEVICE),
|
||||||
|
imx_map_entry(MX7D, AIPS3, MT_DEVICE),
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char * const low_power_ocram_match[] __initconst = {
|
||||||
|
"fsl,lpm-sram",
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct map_desc iram_tlb_io_desc __initdata = {
|
||||||
|
/* .virtual and .pfn are run-time assigned */
|
||||||
|
.length = SZ_1M,
|
||||||
|
.type = MT_MEMORY_RWX_NONCACHED,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init imx7_dt_find_lpsram(unsigned long node, const char *uname,
|
||||||
|
int depth, void *data)
|
||||||
|
{
|
||||||
|
unsigned long lpram_addr;
|
||||||
|
const __be32 *prop = of_get_flat_dt_prop(node, "reg", NULL);
|
||||||
|
|
||||||
|
if (of_flat_dt_match(node, low_power_ocram_match)) {
|
||||||
|
if (!prop)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
lpram_addr = be32_to_cpup(prop);
|
||||||
|
|
||||||
|
/* We need to create a 1M page table entry. */
|
||||||
|
iram_tlb_io_desc.virtual = IMX_IO_P2V(lpram_addr & 0xFFF00000);
|
||||||
|
iram_tlb_io_desc.pfn = __phys_to_pfn(lpram_addr & 0xFFF00000);
|
||||||
|
iram_tlb_phys_addr = lpram_addr;
|
||||||
|
iram_tlb_base_addr = IMX_IO_P2V(lpram_addr);
|
||||||
|
iotable_init(&iram_tlb_io_desc, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __init imx7_pm_map_io(void)
|
||||||
|
{
|
||||||
|
unsigned long i, j;
|
||||||
|
|
||||||
|
iotable_init(imx7_pm_io_desc, ARRAY_SIZE(imx7_pm_io_desc));
|
||||||
|
/*
|
||||||
|
* Get the address of IRAM or OCRAM to be used by the low
|
||||||
|
* power code from the device tree.
|
||||||
|
*/
|
||||||
|
WARN_ON(of_scan_flat_dt(imx7_dt_find_lpsram, NULL));
|
||||||
|
|
||||||
|
/* Return if no IRAM space is allocated for suspend/resume code. */
|
||||||
|
if (!iram_tlb_base_addr) {
|
||||||
|
pr_warn("No valid ocram available for suspend/resume!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: Handle M4 in TEE? */
|
||||||
|
/* Set all entries to 0 except first 3 words reserved for M4. */
|
||||||
|
memset((void *)(iram_tlb_base_addr + M4_OCRAMS_RESERVED_SIZE),
|
||||||
|
0, MX7_IRAM_TLB_SIZE - M4_OCRAMS_RESERVED_SIZE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the IRAM virtual address has a mapping in the IRAM
|
||||||
|
* page table.
|
||||||
|
*
|
||||||
|
* Only use the top 12 bits [31-20] when storing the physical
|
||||||
|
* address in the page table as only these bits are required
|
||||||
|
* for 1M mapping.
|
||||||
|
*/
|
||||||
|
j = ((iram_tlb_base_addr >> 20) << 2) / 4;
|
||||||
|
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||||
|
(iram_tlb_phys_addr & 0xFFF00000) | TT_ATTRIB_NON_CACHEABLE_1M;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the AIPS1 virtual address has a mapping in the
|
||||||
|
* IRAM page table.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
j = ((IMX_IO_P2V(MX7D_AIPS1_BASE_ADDR + i * 0x100000) >> 20) << 2) / 4;
|
||||||
|
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||||
|
((MX7D_AIPS1_BASE_ADDR + i * 0x100000) & 0xFFF00000) |
|
||||||
|
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the AIPS2 virtual address has a mapping in the
|
||||||
|
* IRAM page table.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
j = ((IMX_IO_P2V(MX7D_AIPS2_BASE_ADDR + i * 0x100000) >> 20) << 2) / 4;
|
||||||
|
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||||
|
((MX7D_AIPS2_BASE_ADDR + i * 0x100000) & 0xFFF00000) |
|
||||||
|
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the AIPS3 virtual address has a mapping
|
||||||
|
* in the IRAM page table.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < 4; i++) {
|
||||||
|
j = ((IMX_IO_P2V(MX7D_AIPS3_BASE_ADDR + i * 0x100000) >> 20) << 2) / 4;
|
||||||
|
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||||
|
((MX7D_AIPS3_BASE_ADDR + i * 0x100000) & 0xFFF00000) |
|
||||||
|
TT_ATTRIB_NON_CACHEABLE_1M;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure the GIC virtual address has a mapping in the
|
||||||
|
* IRAM page table.
|
||||||
|
*/
|
||||||
|
j = ((IMX_IO_P2V(MX7D_GIC_BASE_ADDR) >> 20) << 2) / 4;
|
||||||
|
*((unsigned long *)iram_tlb_base_addr + j) =
|
||||||
|
(MX7D_GIC_BASE_ADDR & 0xFFF00000) | TT_ATTRIB_NON_CACHEABLE_1M;
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Freescale Semiconductor, Inc. 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 as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
|
||||||
|
* 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/linkage.h>
|
||||||
|
#include <asm/smp_scu.h>
|
||||||
|
#include "hardware.h"
|
||||||
|
|
||||||
|
.macro disable_l1_dcache
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flush all data from the L1 data cache before disabling
|
||||||
|
* SCTLR.C bit.
|
||||||
|
*/
|
||||||
|
push {r0 - r10, lr}
|
||||||
|
ldr r7, =v7_flush_dcache_all
|
||||||
|
mov lr, pc
|
||||||
|
mov pc, r7
|
||||||
|
pop {r0 - r10, lr}
|
||||||
|
|
||||||
|
/* disable d-cache */
|
||||||
|
mrc p15, 0, r7, c1, c0, 0
|
||||||
|
bic r7, r7, #(1 << 2)
|
||||||
|
mcr p15, 0, r7, c1, c0, 0
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
|
||||||
|
push {r0 - r10, lr}
|
||||||
|
ldr r7, =v7_flush_dcache_all
|
||||||
|
mov lr, pc
|
||||||
|
mov pc, r7
|
||||||
|
pop {r0 - r10, lr}
|
||||||
|
|
||||||
|
.endm
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
.align 3
|
||||||
|
|
||||||
|
ENTRY(imx7_smp_wfe)
|
||||||
|
push {r4 - r11, lr}
|
||||||
|
|
||||||
|
dsb
|
||||||
|
isb
|
||||||
|
|
||||||
|
disable_l1_dcache
|
||||||
|
|
||||||
|
isb
|
||||||
|
|
||||||
|
/* Turn off SMP bit. */
|
||||||
|
mrc p15, 0, r8, c1, c0, 1
|
||||||
|
bic r8, r8, #0x40
|
||||||
|
mcr p15, 0, r8, c1, c0, 1
|
||||||
|
|
||||||
|
isb
|
||||||
|
/* Set flag of entering WFE. */
|
||||||
|
mov r7, #0xff
|
||||||
|
lsl r7, r7, r0
|
||||||
|
mov r6, #SCU_PM_DORMANT
|
||||||
|
lsl r6, r6, r0
|
||||||
|
ldr r8, [r1, #0x4]
|
||||||
|
bic r8, r8, r7
|
||||||
|
orr r6, r6, r8
|
||||||
|
str r6, [r1, #0x4]
|
||||||
|
|
||||||
|
go_back_wfe:
|
||||||
|
wfe
|
||||||
|
|
||||||
|
/* Offset 0x0 stores busfeq done flag */
|
||||||
|
ldr r6, [r1]
|
||||||
|
cmp r6, #1
|
||||||
|
beq go_back_wfe
|
||||||
|
|
||||||
|
/* Turn ON SMP bit. */
|
||||||
|
mrc p15, 0, r8, c1, c0, 1
|
||||||
|
orr r8, r8, #0x40
|
||||||
|
mcr p15, 0, r8, c1, c0, 1
|
||||||
|
|
||||||
|
isb
|
||||||
|
/* Enable L1 data cache. */
|
||||||
|
mrc p15, 0, r8, c1, c0, 0
|
||||||
|
orr r8, r8, #0x4
|
||||||
|
mcr p15, 0, r8, c1, c0, 0
|
||||||
|
isb
|
||||||
|
|
||||||
|
/* Set flag of exiting WFE. */
|
||||||
|
mov r7, #0xff
|
||||||
|
lsl r7, r7, r0
|
||||||
|
mov r6, #SCU_PM_NORMAL
|
||||||
|
lsl r6, r6, r0
|
||||||
|
ldr r8, [r1, #0x4]
|
||||||
|
bic r8, r8, r7
|
||||||
|
orr r6, r6, r8
|
||||||
|
str r6, [r1, #0x4]
|
||||||
|
|
||||||
|
/* Pop all saved registers. */
|
||||||
|
pop {r4 - r11, lr}
|
||||||
|
mov pc, lr
|
||||||
|
.ltorg
|
||||||
|
ENDPROC(imx7_smp_wfe)
|
||||||
|
#endif
|
Loading…
Reference in New Issue