/*
- * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2008-2012 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
reg |= MXC_CCM_CCGRx_CG_MASK << clk->enable_shift;
__raw_writel(reg, clk->enable_reg);
- if (clk->flags & AHB_HIGH_SET_POINT)
- lp_high_freq++;
- else if (clk->flags & AHB_MED_SET_POINT)
- lp_med_freq++;
-
return 0;
}
reg = __raw_readl(clk->enable_reg);
reg &= ~(MXC_CCM_CCGRx_CG_MASK << clk->enable_shift);
__raw_writel(reg, clk->enable_reg);
-
- if (clk->flags & AHB_HIGH_SET_POINT)
- lp_high_freq--;
- else if (clk->flags & AHB_MED_SET_POINT)
- lp_med_freq--;
}
static void _clk_disable_inwait(struct clk *clk)
#
# Object file lists.
-obj-y := cpu.o mm.o system.o devices.o dummy_gpio.o irq.o bus_freq.o usb_dr.o usb_h1.o usb_h2.o usb_h3.o pm.o cpu_op-mx6.o mx6_wfi.o mx6_fec.o mx6_anatop_regulator.o cpu_regulator-mx6.o
+obj-y := cpu.o mm.o system.o devices.o dummy_gpio.o irq.o bus_freq.o usb_dr.o usb_h1.o usb_h2.o usb_h3.o \
+pm.o cpu_op-mx6.o mx6_wfi.o mx6_fec.o mx6_anatop_regulator.o cpu_regulator-mx6.o \
+mx6_mmdc.o mx6_ddr_freq.o
obj-$(CONFIG_ARCH_MX6) += clock.o mx6q_suspend.o
obj-$(CONFIG_MACH_MX6Q_ARM2) += board-mx6q_arm2.o
gp_reg_id = arm2_dvfscore_data.reg_id;
mx6_arm2_init_uart();
+
+
imx6q_add_mipi_csi2(&mipi_csi2_pdata);
imx6q_add_mxc_hdmi_core(&hdmi_core_data);
imx6dl_add_imx_epdc(&epdc_data);
}
imx6q_add_pcie(&mx6_arm2_pcie_data);
+ imx6q_add_busfreq();
}
extern void __iomem *twd_base;
mxc_register_device(&si4763_codec_device, NULL);
mxc_register_device(&mxc_si4763_audio_device, &si4763_audio_data);
+ imx6q_add_busfreq();
}
extern void __iomem *twd_base;
rate = clk_round_rate(clko2, 24000000);
clk_set_rate(clko2, rate);
clk_enable(clko2);
+ imx6q_add_busfreq();
}
extern void __iomem *twd_base;
/* Register charger chips */
platform_device_register(&sabresd_max8903_charger_1);
pm_power_off = mx6_snvs_poweroff;
+ imx6q_add_busfreq();
imx6q_add_pcie(&mx6_sabresd_pcie_data);
}
/*
- * Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright (C) 2011-2012 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
#include <asm/mach-types.h>
#include <asm/cacheflush.h>
#include <asm/tlb.h>
+#include "crm_regs.h"
-#define LP_LOW_VOLTAGE 1050000
-#define LP_NORMAL_VOLTAGE 1250000
-#define LP_APM_CLK 24000000
-#define NAND_LP_APM_CLK 12000000
-#define AXI_A_NORMAL_CLK 166250000
-#define AXI_A_CLK_NORMAL_DIV 4
-#define AXI_B_CLK_NORMAL_DIV 5
-#define AHB_CLK_NORMAL_DIV AXI_B_CLK_NORMAL_DIV
-#define EMI_SLOW_CLK_NORMAL_DIV AXI_B_CLK_NORMAL_DIV
-#define NFC_CLK_NORMAL_DIV 4
-#define SPIN_DELAY 1000000 /* in nanoseconds */
-#define DDR_TYPE_DDR3 0x0
-#define DDR_TYPE_DDR2 0x1
-DEFINE_SPINLOCK(ddr_freq_lock);
-
-unsigned long lp_normal_rate;
-unsigned long lp_med_rate;
-unsigned long ddr_normal_rate;
-unsigned long ddr_med_rate;
-unsigned long ddr_low_rate;
+#define LPAPM_CLK 24000000
+#define DDR_MED_CLK 400000000
+#define DDR3_NORMAL_CLK 528000000
-struct regulator *pll_regulator;
+DEFINE_SPINLOCK(ddr_freq_lock);
-struct regulator *lp_regulator;
int low_bus_freq_mode;
int high_bus_freq_mode;
int med_bus_freq_mode;
int bus_freq_scaling_initialized;
-char *lp_reg_id;
-
static struct device *busfreq_dev;
static int busfreq_suspended;
/* True if bus_frequency is scaled not using DVFS-PER */
int bus_freq_scaling_is_active;
-int cpu_op_nr;
int lp_high_freq;
int lp_med_freq;
-
-struct workqueue_struct *voltage_wq;
-struct completion voltage_change_cmpl;
+unsigned int ddr_low_rate;
+unsigned int ddr_med_rate;
+unsigned int ddr_normal_rate;
int low_freq_bus_used(void);
void set_ddr_freq(int ddr_freq);
extern struct cpu_op *(*get_cpu_op)(int *op);
-extern void __iomem *ccm_base;
-extern void __iomem *databahn_base;
extern int update_ddr_freq(int ddr_rate);
struct timeval start_time;
struct timeval end_time;
-int set_low_bus_freq(void)
+static int cpu_op_nr;
+static struct cpu_op *cpu_op_tbl;
+static struct clk *pll2_400;
+static struct clk *cpu_clk;
+static unsigned int org_ldo;
+static struct clk *pll3;
+
+static struct delayed_work low_bus_freq_handler;
+
+static void reduce_bus_freq_handler(struct work_struct *work)
{
- return 0;
+ unsigned long reg;
+
+ if (low_bus_freq_mode || !low_freq_bus_used())
+ return;
+
+ while (!mutex_trylock(&bus_freq_mutex))
+ msleep(1);
+
+ /* PLL3 is used in the DDR freq change process, enable it. */
+
+ if (low_bus_freq_mode || !low_freq_bus_used()) {
+ mutex_unlock(&bus_freq_mutex);
+ return;
+ }
+ clk_enable(pll3);
+
+
+ update_ddr_freq(24000000);
+
+ if (med_bus_freq_mode)
+ clk_disable(pll2_400);
+
+ low_bus_freq_mode = 1;
+ high_bus_freq_mode = 0;
+ med_bus_freq_mode = 0;
+
+ /* Power gate the PU LDO. */
+ org_ldo = reg = __raw_readl(ANADIG_REG_CORE);
+ reg &= ~(ANADIG_REG_TARGET_MASK << ANADIG_REG1_PU_TARGET_OFFSET);
+ __raw_writel(reg, ANADIG_REG_CORE);
+
+ mutex_unlock(&bus_freq_mutex);
+ clk_disable(pll3);
+
+
}
-int set_high_bus_freq(int high_bus_freq)
+/* 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.
+ */
+int set_low_bus_freq(void)
{
+ if (busfreq_suspended)
+ return 0;
+
+ if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active)
+ return 0;
+
+ /* 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;
}
-void exit_lpapm_mode_mx6q(int high_bus_freq)
+/* Set the DDR to either 528MHz or 400MHz for MX6q
+ * or 400MHz for MX6DL.
+ */
+int set_high_bus_freq(int high_bus_freq)
{
+ if (busfreq_suspended)
+ return 0;
-}
+ if (!bus_freq_scaling_initialized || !bus_freq_scaling_is_active)
+ return 0;
+ if (high_bus_freq_mode && high_bus_freq)
+ return 0;
-void set_ddr_freq(int ddr_rate)
-{
+ if (med_bus_freq_mode && !high_bus_freq)
+ return 0;
+ while (!mutex_trylock(&bus_freq_mutex))
+ msleep(1);
+
+ if ((high_bus_freq_mode && (high_bus_freq || lp_high_freq)) ||
+ (med_bus_freq_mode && !high_bus_freq && lp_med_freq && !lp_high_freq)) {
+ mutex_unlock(&bus_freq_mutex);
+ return 0;
+ }
+ clk_enable(pll3);
+
+ /* Enable the PU LDO */
+ if (low_bus_freq_mode)
+ __raw_writel(org_ldo, ANADIG_REG_CORE);
+
+ if (high_bus_freq) {
+ update_ddr_freq(ddr_normal_rate);
+ if (med_bus_freq_mode)
+ clk_disable(pll2_400);
+ low_bus_freq_mode = 0;
+ high_bus_freq_mode = 1;
+ med_bus_freq_mode = 0;
+ } else {
+ clk_enable(pll2_400);
+ update_ddr_freq(ddr_med_rate);
+ low_bus_freq_mode = 0;
+ high_bus_freq_mode = 0;
+ med_bus_freq_mode = 1;
+ }
+
+ mutex_unlock(&bus_freq_mutex);
+ clk_disable(pll3);
+
+ return 0;
}
+
int low_freq_bus_used(void)
{
+ if (!bus_freq_scaling_initialized)
+ return 0;
+
+ /* We only go the lowest setpoint if ARM is also
+ * at the lowest setpoint.
+ */
+ if ((clk_get_rate(cpu_clk) >
+ cpu_op_tbl[cpu_op_nr - 1].cpu_rate)
+ || (cpu_op_nr == 1)) {
+ return 0;
+ }
+
if ((lp_high_freq == 0)
&& (lp_med_freq == 0))
return 1;
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(0);
+ } 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 __devinit busfreq_probe(struct platform_device *pdev)
{
+ u32 err;
+
+ busfreq_dev = &pdev->dev;
+
+ pll2_400 = clk_get(NULL, "pll2_pfd_400M");
+ if (IS_ERR(pll2_400)) {
+ printk(KERN_DEBUG "%s: failed to get axi_clk\n",
+ __func__);
+ return PTR_ERR(pll2_400);
+ }
+
+ cpu_clk = clk_get(NULL, "cpu_clk");
+ if (IS_ERR(cpu_clk)) {
+ printk(KERN_DEBUG "%s: failed to get cpu_clk\n",
+ __func__);
+ return PTR_ERR(cpu_clk);
+ }
+
+ pll3 = clk_get(NULL, "pll3_main_clk");
+
+ err = sysfs_create_file(&busfreq_dev->kobj, &dev_attr_enable.attr);
+ if (err) {
+ printk(KERN_ERR
+ "Unable to register sysdev entry for BUSFREQ");
+ return err;
+ }
+
+ cpu_op_tbl = get_cpu_op(&cpu_op_nr);
+ low_bus_freq_mode = 0;
+ high_bus_freq_mode = 1;
+ med_bus_freq_mode = 0;
+ bus_freq_scaling_is_active = 0;
+ bus_freq_scaling_initialized = 1;
+
+ if (cpu_is_mx6q()) {
+ ddr_low_rate = LPAPM_CLK;
+ ddr_med_rate = DDR_MED_CLK;
+ ddr_normal_rate = DDR3_NORMAL_CLK;
+ }
+ if (cpu_is_mx6dl()) {
+ ddr_low_rate = LPAPM_CLK;
+ ddr_normal_rate = ddr_med_rate = DDR_MED_CLK;
+ }
+
+ INIT_DELAYED_WORK(&low_bus_freq_handler, reduce_bus_freq_handler);
+
+ mutex_init(&bus_freq_mutex);
+
return 0;
}
bus_freq_scaling_initialized = 0;
}
-module_init(busfreq_init);
+late_initcall(busfreq_init);
module_exit(busfreq_cleanup);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
reg |= MXC_CCM_CCGRx_CG_MASK << clk->enable_shift;
__raw_writel(reg, clk->enable_reg);
- if (clk->flags & AHB_HIGH_SET_POINT)
- lp_high_freq++;
- else if (clk->flags & AHB_MED_SET_POINT)
- lp_med_freq++;
-
return 0;
}
reg = __raw_readl(clk->enable_reg);
reg &= ~(MXC_CCM_CCGRx_CG_MASK << clk->enable_shift);
__raw_writel(reg, clk->enable_reg);
-
- if (clk->flags & AHB_HIGH_SET_POINT)
- lp_high_freq--;
- else if (clk->flags & AHB_MED_SET_POINT)
- lp_med_freq--;
}
static void _clk_disable_inwait(struct clk *clk)
static struct clk usdhc_dep_clk = {
.parent = &mmdc_ch0_axi_clk[0],
.secondary = &mx6per1_clk,
- .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
};
static unsigned long _clk_usdhc_round_rate(struct clk *clk,
.round_rate = _clk_usdhc_round_rate,
.set_rate = _clk_usdhc1_set_rate,
.get_rate = _clk_usdhc1_get_rate,
- .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
};
static int _clk_usdhc2_set_parent(struct clk *clk, struct clk *parent)
.round_rate = _clk_usdhc_round_rate,
.set_rate = _clk_usdhc2_set_rate,
.get_rate = _clk_usdhc2_get_rate,
- .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
};
static int _clk_usdhc3_set_parent(struct clk *clk, struct clk *parent)
.round_rate = _clk_usdhc_round_rate,
.set_rate = _clk_usdhc3_set_rate,
.get_rate = _clk_usdhc3_get_rate,
- .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
};
static int _clk_usdhc4_set_parent(struct clk *clk, struct clk *parent)
.round_rate = _clk_usdhc_round_rate,
.set_rate = _clk_usdhc4_set_rate,
.get_rate = _clk_usdhc4_get_rate,
- .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE,
};
static unsigned long _clk_ssi_round_rate(struct clk *clk,
unsigned long ckih1, unsigned long ckih2)
{
__iomem void *base;
- int i;
+ int i, reg;
external_low_reference = ckil;
external_high_reference = ckih1;
__raw_writel(0, MXC_CCM_CCGR6);
- /* Lower the ipg_perclk frequency to 8.25MHz. */
- clk_set_rate(&ipg_perclk, 8250000);
+ /* Lower the ipg_perclk frequency to 6MHz. */
+ clk_set_rate(&ipg_perclk, 6000000);
/* S/PDIF */
clk_set_parent(&spdif0_clk[0], &pll3_pfd_454M);
lp_high_freq = 0;
lp_med_freq = 0;
+ /* Turn OFF all unnecessary PHYs. */
+ /* Turn off SATA PHY. */
+ base = ioremap(MX6Q_SATA_BASE_ADDR, SZ_8K);
+ reg = __raw_readl(base + PORT_PHY_CTL);
+ __raw_writel(reg | PORT_PHY_CTL_PDDQ_LOC, base + PORT_PHY_CTL);
+
+ /* Turn off HDMI PHY. */
+ base = ioremap(MX6Q_HDMI_ARB_BASE_ADDR, SZ_128K);
+ reg = __raw_readb(base + HDMI_PHY_CONF0);
+ __raw_writeb(reg | HDMI_PHY_CONF0_GEN2_PDDQ_MASK, base + HDMI_PHY_CONF0);
+
+ reg = __raw_readb(base + HDMI_MC_PHYRSTZ);
+ __raw_writeb(reg | HDMI_MC_PHYRSTZ_DEASSERT, base + HDMI_MC_PHYRSTZ);
+
+ iounmap(base);
+
+ base = ioremap(MX6Q_IOMUXC_BASE_ADDR, SZ_4K);
+ /* Close PLL inside SATA PHY. */
+ reg = __raw_readl(base + 0x34);
+ __raw_writel(reg | (1 << 1), base + 0x34);
+
+ /* Close PCIE PHY. */
+ reg = __raw_readl(base + 0x04);
+ reg |= (1 << 18);
+ __raw_writel(reg, base + 0x04);
+ iounmap(base);
+
return 0;
}
extern unsigned int num_cpu_idle_lock;
+void *mx6_wait_in_iram_base;
+void (*mx6_wait_in_iram)(void);
+extern void mx6_wait(void);
+extern int init_mmdc_settings(void);
+
struct cpu_op *(*get_cpu_op)(int *op);
bool enable_wait_mode;
u32 arm_max_freq = CPU_AT_1GHz;
{
unsigned int reg;
void __iomem *base;
- unsigned long iram_paddr, cpaddr;
-
iram_init(MX6Q_IRAM_BASE_ADDR, MX6Q_IRAM_SIZE);
num_cpu_idle_lock = 0x0;
+ init_mmdc_settings();
return 0;
}
postcore_initcall(post_cpu_init);
extern const struct imx_pcie_data imx6q_pcie_data __initconst;
#define imx6q_add_pcie(pdata) imx_add_pcie(&imx6q_pcie_data, pdata)
+
+#define imx6q_add_busfreq(pdata) imx_add_busfreq(pdata)
+
--- /dev/null
+/*
+ * Copyright (C) 2011-2012 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.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/linkage.h>
+#include <mach/hardware.h>
+
+ .macro switch_to_528MHz
+
+ /* DDR freq change to 528MHz */
+
+ /* check if periph_clk_sel is already set */
+ ldr r0, [r6, #0x14]
+ and r0, r0, #0x2000000
+ cmp r0, #0x2000000
+ beq switch_pre_periph_clk_528
+
+ /* Step 1: Change periph_clk to be sourced from pll3_clk. */
+ /* Ensure PLL3 is the source and set the divider to 1. */
+ ldr r0, [r6, #0x18]
+ bic r0, r0, #0x3000
+ str r0, [r6, #0x18]
+
+ ldr r0, [r6, #0x14]
+ bic r0, r0, #0x38000000
+ str r0, [r6, #0x14]
+
+ /* Now switch periph_clk to pll3_main_clk. */
+ ldr r0, [r6, #0x14]
+ orr r0, r0, #0x2000000
+ str r0, [r6, #0x14]
+
+periph_clk_switch3:
+ ldr r0, [r6, #0x48]
+ cmp r0, #0
+ bne periph_clk_switch3
+
+switch_pre_periph_clk_528:
+
+ /* Now switch pre_periph_clk to PLL2_528MHz. */
+ ldr r0, [r6, #0x18]
+ bic r0, r0, #0xC0000
+ str r0, [r6, #0x18]
+
+ /* Set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=4 (need to maintain GPT divider). */
+ ldr r0, [r6, #0x14]
+ ldr r2, =0x3F1D00
+ bic r0, r0, r2
+ orr r0, r0, #0x10000
+ orr r0, r0, #0xD00
+ str r0, [r6, #0x14]
+
+wait_div_update1:
+ ldr r0, [r6, #0x48]
+ cmp r0, #0
+ bne wait_div_update1
+
+ /* Now switch periph_clk back. */
+ ldr r0, [r6, #0x14]
+ bic r0, r0, #0x2000000
+ str r0, [r6, #0x14]
+
+periph_clk_switch4:
+ ldr r0, [r6, #0x48]
+ cmp r0, #0
+ bne periph_clk_switch4
+
+ /* Change the GPT divider so that its at 6MHz. */
+ ldr r0, [r6, #0x1C]
+ bic r0, r0, #0x3F
+ orr r0, r0, #0xB
+ str r0, [r6, #0x1C]
+
+ .endm
+
+ .macro switch_to_400MHz
+
+ /* check if periph_clk_sel is already set */
+ ldr r0, [r6, #0x14]
+ and r0, r0, #0x2000000
+ cmp r0, #0x2000000
+ beq switch_pre_periph_clk_400
+
+ /* Step 1: Change periph_clk to be sourced from pll3_clk. */
+ /* Ensure PLL3 is the source and set the divider to 1. */
+ ldr r0, [r6, #0x18]
+ bic r0, r0, #0x3000
+ str r0, [r6, #0x18]
+
+ ldr r0, [r6, #0x14]
+ bic r0, r0, #0x38000000
+ str r0, [r6, #0x14]
+
+ /* Now switch periph_clk to pll3_main_clk. */
+ ldr r0, [r6, #0x14]
+ orr r0, r0, #0x2000000
+ str r0, [r6, #0x14]
+
+periph_clk_switch5:
+ ldr r0, [r6, #0x48]
+ cmp r0, #0
+ bne periph_clk_switch5
+
+switch_pre_periph_clk_400:
+
+ /* Now switch pre_periph_clk to PFD_400MHz. */
+ ldr r0, [r6, #0x18]
+ bic r0, r0, #0xC0000
+ orr r0, r0, #0x40000
+ str r0, [r6, #0x18]
+
+ /* Set the MMDC_DIV=1, AXI_DIV = 2, AHB_DIV=3 (need to maintain GPT divider). */
+ ldr r0, [r6, #0x14]
+ ldr r2, =0x3F1D00
+ bic r0, r0, r2
+ orr r0, r0, #0x10000
+ orr r0, r0, #0x900
+ str r0, [r6, #0x14]
+
+wait_div_update400:
+ ldr r0, [r6, #0x48]
+ cmp r0, #0
+ bne wait_div_update400
+
+ /* Now switch periph_clk back. */
+ ldr r0, [r6, #0x14]
+ bic r0, r0, #0x2000000
+ str r0, [r6, #0x14]
+
+periph_clk_switch6:
+ ldr r0, [r6, #0x48]
+ cmp r0, #0
+ bne periph_clk_switch6
+
+ /* Change the GPT divider so that its at 6MHz. */
+ ldr r0, [r6, #0x1C]
+ bic r0, r0, #0x3F
+ orr r0, r0, #0xB
+ str r0, [r6, #0x1C]
+
+ .endm
+
+ .macro switch_to_50MHz
+
+ /* Set DDR to 50MHz. */
+ /* check if periph_clk_sel is already set */
+ ldr r0, [r6, #0x14]
+ and r0, r0, #0x2000000
+ cmp r0, #0x2000000
+ beq switch_pre_periph_clk_50
+
+ /* Set the periph_clk to be sourced from PLL2_PFD_200M */
+ /* Step 1: Change periph_clk to be sourced from pll3_clk. */
+ /* Ensure PLL3 is the source and set the divider to 1. */
+ ldr r0, [r6, #0x18]
+ bic r0, r0, #0x3000
+ str r0, [r6, #0x18]
+
+ ldr r0, [r6, #0x14]
+ bic r0, r0, #0x38000000
+ str r0, [r6, #0x14]
+
+ /* Now switch periph_clk to pll3_main_clk. */
+ ldr r0, [r6, #0x14]
+ orr r0, r0, #0x2000000
+ str r0, [r6, #0x14]
+
+periph_clk_switch_50:
+ ldr r0, [r6, #0x48]
+ cmp r0, #0
+ bne periph_clk_switch_50
+
+switch_pre_periph_clk_50:
+
+ /* Now switch pre_periph_clk to PFD_200MHz. */
+ ldr r0, [r6, #0x18]
+ orr r0, r0, #0xC0000
+ str r0, [r6, #0x18]
+
+ /* Set the MMDC_DIV=4, AXI_DIV = 4, AHB_DIV=6 (need to maintain GPT divider). */
+ ldr r0, [r6, #0x14]
+ ldr r2, =0x3F1C00
+ bic r0, r0, r2
+
+ orr r0, r0, #0x180000
+ orr r0, r0, #0x10000
+
+ /* If changing AHB divider remember to change the IPGPER divider too below. */
+ orr r0, r0, #0xC00
+ str r0, [r6, #0x14]
+
+wait_div_update_50:
+ ldr r0, [r6, #0x48]
+ cmp r0, #0
+ bne wait_div_update_50
+
+ /* Now switch periph_clk back. */
+ ldr r0, [r6, #0x14]
+ bic r0, r0, #0x2000000
+ str r0, [r6, #0x14]
+
+periph_clk_switch2:
+ ldr r0, [r6, #0x48]
+ cmp r0, #0
+ bne periph_clk_switch2
+
+ /* Change the GPT divider so that its at 6MHz. */
+ ldr r0, [r6, #0x1C]
+ bic r0, r0, #0x3F
+ orr r0, r0, #0x3
+ str r0, [r6, #0x1C]
+
+ .endm
+
+ .macro switch_to_24MHz
+ /* Change the freq now */
+ /* Try setting DDR to 24MHz. */
+ /* Source it from the periph_clk2 */
+ /* Ensure the periph_clk2 is sourced from 24MHz
+ and the divider is 1. */
+ ldr r0, [r6, #0x18]
+ bic r0, r0, #0x3000
+ orr r0, r0, #0x1000
+ str r0, [r6, #0x18]
+
+ ldr r0, [r6, #0x14]
+ bic r0, r0, #0x38000000
+ str r0, [r6, #0x14]
+
+ /* Now switch periph_clk to 24MHz. */
+ ldr r0, [r6, #0x14]
+ orr r0, r0, #0x2000000
+ str r0, [r6, #0x14]
+
+periph_clk_switch1:
+ ldr r0, [r6, #0x48]
+ cmp r0, #0
+ bne periph_clk_switch1
+
+ /* Change all the dividers to 1. */
+ ldr r0, [r6, #0x14]
+ ldr r2, =0x3F1C00
+ bic r0, r0, r2
+ str r0, [r6, #0x14]
+
+ /* Wait for the divider to change. */
+wait_div_update:
+ ldr r0, [r6, #0x48]
+ cmp r0, #0
+ bne wait_div_update
+
+ /* Change the GPT divider so that its at 6MHz. */
+ ldr r0, [r6, #0x1C]
+ bic r0, r0, #0x3F
+ orr r0, r0, #0x1
+ str r0, [r6, #0x1C]
+
+ .endm
+
+/*
+ * mx6_ddr_freq_change
+ *
+ * Idle the processor (eg, wait for interrupt).
+ * Make sure DDR is in self-refresh.
+ * IRQs are already disabled.
+ */
+ENTRY(mx6_ddr_freq_change)
+
+ stmfd sp!, {r4,r5,r6, r7, r8, r9, r10, r11} @ Save registers
+
+ ldr r6, =CCM_BASE_ADDR
+ add r6, r6, #PERIPBASE_VIRT
+ ldr r5, =MMDC_P0_BASE_ADDR
+ add r5, r5, #PERIPBASE_VIRT
+ ldr r7, =MX6Q_IOMUXC_BASE_ADDR
+ add r7, r7, #PERIPBASE_VIRT
+
+ mov r4, r0 @save new freq requested
+ mov r8, r1 @save the ddr settings for the new rate
+ mov r9, r2 @save the mode DDR is currently in (DLL ON/OFF)
+ mov r11, r3 @save iomux offsets
+
+ddr_freq_change:
+ /* Make sure no TLB miss will occur when the DDR is in self refresh. */
+ /* Invalidate TLB single entry to ensure that the address is not
+ * already in the TLB.
+ */
+
+ adr r10, ddr_freq_change @Address in this function.
+
+
+ mcr p15, 0, r10, c8, c7, 1 @//@ Make sure freq code address
+ @// @ is not already in TLB.
+ mcr p15, 0, r6, c8, c7, 1 @//@ Make sure CCM address
+ @//@ is not already in TLB.
+ mcr p15, 0, r5, c8, c7, 1 @//@ make sure MMDC address
+ @//@ is not already in TLB.
+ mcr p15, 0, r7, c8, c7, 1 @//@ make sure IOMUX address
+ @//@ is not already in TLB.
+
+ mrc p15, 0, r0, c10, c0, 0 @//@ Read the TLB lockdown register
+ orr r0, r0, #1 @//@ Set the Preserve bit.
+ mcr p15, 0, r0, c10, c0, 0 @//@ Write to the lockdown register
+
+ ldr r2, [r6] @ TLB will miss,
+ @CCM address will be loaded
+ ldr r2, [r5] @ TLB will miss,
+ @MMDC address will be loaded
+ ldr r2, [r7] @ TLB will miss,
+ @IOMUX will be loaded
+
+ ldr r2, [r8] @ Get the DDR settings.
+
+ ldr r2, [r10] @ TLB will miss
+
+ ldr r2, [r11] @Get the IOMUX settings
+
+ mrc p15, 0, r0, c10, c0, 0 @//@ Read the lockdown register
+ @//@ (victim will be incremented)
+ bic r0, r0, #1 @//@ Clear the preserve bit
+ mcr p15, 0, r0, c10, c0, 0 @//@ Write to the lockdown register
+
+ /* Disable automatic power saving. */
+
+ ldr r0, [r5, #0x404]
+ orr r0, r0, #0x01
+ str r0, [r5, #0x404]
+
+ /* Disable MMDC power down timer. */
+ /*MMDC0_MDPDC disable power down timer */
+ ldr r0, [r5, #0x4]
+ bic r0, r0, #0xff00
+ str r0, [r5, #0x4]
+
+ /* set CON_REG */
+ ldr r0, =0x8000
+ str r0, [r5, #0x1C]
+poll_conreq_set_1:
+ ldr r0, [r5, #0x1C]
+ and r0, r0, #0x4000
+ cmp r0, #0x4000
+ bne poll_conreq_set_1
+
+ /*setmem /32 0x021b001c = 0x00008010 //Precharge all on cs0 */
+ /*setmem /32 0x021b001c = 0x00008018 //Precharge all on cs1 */
+ ldr r0, =0x00008010
+ str r0, [r5, #0x1C]
+ ldr r0, =0x00008018
+ str r0, [r5, #0x1C]
+
+ /* if requested frequency is greater than 300MHz go to DLL on mode */
+ ldr r1, =300000000
+ cmp r4, r1
+ bge dll_on_mode
+
+dll_off_mode:
+
+ /* if DLL is currently on, turn it off
+ cmp r9, #1
+ beq continue_dll_off_1
+
+ /* setmem /32 0x021b001c = 0x00018031 //Rtt_NOM off + set dll off, cs 0 */
+ /* setmem /32 0x021b001c = 0x00018039 //Rtt_NOM off + set dll off, cs 1 */
+ ldr r0, =0x00018031
+ str r0, [r5, #0x1C]
+
+ ldr r0, =0x00018039
+ str r0, [r5, #0x1C]
+
+ ldr r1, =10
+delay1a:
+ ldr r2, =0
+cont1a:
+ ldr r0, [r5, r2]
+ add r2, r2, #4
+ cmp r2, #16
+ bne cont1a
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt delay1a
+
+continue_dll_off_1:
+
+ /* set DVFS - enter self refresh mode */
+ ldr r0, [r5, #0x404]
+ orr r0, r0, #0x200000
+ str r0, [r5, #0x404]
+
+ /* de-assert con_req */
+ mov r0, #0x0
+ str r0, [r5, #0x1C]
+
+poll_dvfs_set_1:
+ ldr r0, [r5, #0x404]
+ and r0, r0, #0x2000000
+ cmp r0, #0x2000000
+ bne poll_dvfs_set_1
+
+ ldr r1, =24000000
+ cmp r4, r1
+ beq switch_freq_24
+
+ switch_to_50MHz
+ b continue_dll_off_2
+
+switch_freq_24:
+ switch_to_24MHz
+
+continue_dll_off_2:
+
+ /* set SBS - block ddr accesses */
+ ldr r0, [r5, #0x410]
+ orr r0, r0, #0x100
+ str r0, [r5, #0x410]
+
+ /* clear DVFS - exit from self refresh mode */
+ ldr r0, [r5, #0x404]
+ bic r0, r0, #0x200000
+ str r0, [r5, #0x404]
+
+poll_dvfs_clear_1:
+ ldr r0, [r5, #0x404]
+ and r0, r0, #0x2000000
+ cmp r0, #0x2000000
+ beq poll_dvfs_clear_1
+
+ /* if DLL was previously on, continue DLL off routine
+ cmp r9, #1
+ beq continue_dll_off_3
+
+ /* setmem /32 0x021b001c = 0x00018031 //Rtt_NOM off + set dll off, cs 0 */
+ /* setmem /32 0x021b001c = 0x00018039 //Rtt_NOM off + set dll off, cs 1 */
+ ldr r0, =0x00018031
+ str r0, [r5, #0x1C]
+
+ ldr r0, =0x00018039
+ str r0, [r5, #0x1C]
+
+ /* setmem /32 0x021b001c = 0x04208030 //write mode reg MR0: CL=6, wr=6 ,cs 0 */
+ /* setmem /32 0x021b001c = 0x04208038 //write mode reg MR0: CL=6, wr=6 ,cs 1 */
+ ldr r0, =0x08208030
+ str r0, [r5, #0x1C]
+
+ ldr r0, =0x08208038
+ str r0, [r5, #0x1C]
+
+ /* setmem /32 0x021b001c = 0x02088032 //write mode reg MR2 , CWL=6 ,cs0 */
+ /* setmem /32 0x021b001c = 0x0208803A //write mode reg MR2 , CWL=6 ,cs1 */
+ ldr r0, =0x00088032
+ str r0, [r5, #0x1C]
+
+ ldr r0, =0x0008803A
+ str r0, [r5, #0x1C]
+
+ /* double refresh ????
+ ldr r0, =0x00001800
+ str r0, [r5, #0x20]*/
+
+ /* delay for a while. */
+ ldr r1, =4
+delay_1:
+ ldr r2, =0
+cont_1:
+ ldr r0, [r5, r2]
+ add r2, r2, #4
+ cmp r2, #16
+ bne cont_1
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt delay_1
+
+ /* MMDC0_MDCFG0 see spread sheet for timings, CAS=6 */
+ ldr r0, [r5, #0x0C]
+ bic r0, r0, #0xf
+ orr r0, r0, #0x3
+ str r0, [r5, #0x0C]
+
+ /* MMDC0_MDCFG1 see spread sheet for timings, tCWL=6 */
+ ldr r0, [r5, #0x10]
+ bic r0, r0, #0x7
+ orr r0, r0, #0x4
+ str r0, [r5, #0x10]
+
+ /* Enable bank interleaving, Address mirror on, WALAT = 0x1, RALAT = 0x2, DDR2_EN = 0 */
+ /*setmem /32 0x021b0018 = 0x00091680 */
+ ldr r0, =0x00091680
+ str r0, [r5, #0x18]
+
+ /* enable dqs pull down in the IOMUX. */
+ /*
+ setmem /32 0x020e05a8 = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS0 - DSE=110
+ setmem /32 0x020e05b0 = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS1 - DSE=110
+ setmem /32 0x020e0524 = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS2 - DSE=110
+ setmem /32 0x020e051c = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS3 - DSE=110
+ setmem /32 0x020e0518 = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS4 - DSE=110
+ setmem /32 0x020e050c = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS5 - DSE=110
+ setmem /32 0x020e05b8 = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS6 - DSE=110
+ setmem /32 0x020e05c0 = 0x00003030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS7 - DSE=110
+ */
+ ldr r1, [r11] @size of array
+ add r11, r11, #8 @skip first eight bytes in array
+ ldr r2, =0x3028
+update_iomux:
+ ldr r0, [r11, #0x0] @ offset
+ ldr r3, [r7, r0]
+ bic r3, r3, r2 @ Clear the DSE, PUE and PKE bits
+ orr r3, r3, #0x3000 @ Enable the Pull downs and lower the drive strength.
+ orr r3, r3, #0x28
+ str r3, [r7, r0]
+ add r11, r11, #8
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt update_iomux
+
+ /* ODT disabled */
+ /* setmem /32 0x021b0818 = 0x0 // DDR_PHY_P0_MPODTCTRL */
+ /* setmem /32 0x021b4818 = 0x0 // DDR_PHY_P1_MPODTCTRL */
+ ldr r0, =0x0
+ ldr r2, =0x818
+ str r0, [r5, r2]
+ ldr r2, =0x4818
+ str r0, [r5, r2]
+
+ /* DQS gating disabled */
+ /* setmem /32 0x021b083c = 0x233f033f */
+ ldr r2, =0x83c
+ ldr r0, [r5, r2]
+ orr r0, r0, #0x20000000
+ str r0, [r5, r2]
+
+ ldr r2, =0x483c
+ ldr r0, [r5, r2]
+ orr r0, r0, #0x20000000
+ str r0, [r5, r2]
+
+ /* MMDC0_MAPSR adopt power down enable */
+ /* setmem /32 0x021b0404 = 0x00011006 */
+ ldr r0, [r5, #0x404]
+ bic r0, r0, #0x01
+ str r0, [r5, #0x404]
+
+ /* frc_msr + mu bypass*/
+ ldr r0, =0x00000060
+ str r0, [r5, #0x8b8]
+ ldr r2, =0x48b8
+ str r0, [r5, r2]
+ ldr r0, =0x00000460
+ str r0, [r5, #0x8b8]
+ ldr r2, =0x48b8
+ str r0, [r5, r2]
+ ldr r0, =0x00000c60
+ str r0, [r5, #0x8b8]
+ ldr r2, =0x48b8
+ str r0, [r5, r2]
+
+continue_dll_off_3:
+
+ /* clear SBS - unblock accesses to DDR */
+ ldr r0, [r5, #0x410]
+ bic r0, r0, #0x100
+ str r0, [r5, #0x410]
+
+ mov r0, #0x0
+ str r0, [r5, #0x1C]
+poll_conreq_clear_1:
+ ldr r0, [r5, #0x1C]
+ and r0, r0, #0x4000
+ cmp r0, #0x4000
+ beq poll_conreq_clear_1
+
+ b done
+
+dll_on_mode:
+ /* assert DVFS - enter self refresh mode */
+ ldr r0, [r5, #0x404]
+ orr r0, r0, #0x200000
+ str r0, [r5, #0x404]
+
+ /* de-assert CON_REQ */
+ mov r0, #0x0
+ str r0, [r5, #0x1C]
+
+ /* poll DVFS ack */
+poll_dvfs_set_2:
+ ldr r0, [r5, #0x404]
+ and r0, r0, #0x2000000
+ cmp r0, #0x2000000
+ bne poll_dvfs_set_2
+
+ ldr r1, =528000000
+ cmp r4, r1
+ beq switch_freq_528
+
+ switch_to_400MHz
+
+ b continue_dll_on
+
+switch_freq_528:
+ switch_to_528MHz
+
+continue_dll_on:
+
+ /* set step-by-step mode */
+ ldr r0, [r5, #0x410]
+ orr r0, r0, #0x100
+ str r0, [r5, #0x410]
+
+ /* clear DVFS - exit self refresh mode */
+ ldr r0, [r5, #0x404]
+ bic r0, r0, #0x200000
+ str r0, [r5, #0x404]
+
+poll_dvfs_clear_2:
+ ldr r0, [r5, #0x404]
+ and r0, r0, #0x2000000
+ cmp r0, #0x2000000
+ beq poll_dvfs_clear_2
+
+ /* if DLL is currently off, turn it back on */
+ cmp r9, #0
+ beq update_calibration
+
+ ldr r0, =0xa5390003
+ str r0, [r5, #0x800]
+ ldr r2, =0x4800
+ str r0, [r5, r2]
+
+ /* enable DQS gating */
+ ldr r2, =0x83c
+ ldr r0, [r5, r2]
+ bic r0, r0, #0x20000000
+ str r0, [r5, r2]
+
+ ldr r2, =0x483c
+ ldr r0, [r5, r2]
+ bic r0, r0, #0x20000000
+ str r0, [r5, r2]
+
+ /* force measure */
+ ldr r0, =0x00000800
+ str r0, [r5, #0x8b8]
+ ldr r2, =0x48b8
+ str r0, [r5, r2]
+
+ /* delay for while */
+ ldr r1, =4
+delay5:
+ ldr r2, =0
+cont5:
+ ldr r0, [r5, r2]
+ add r2, r2, #4
+ cmp r2, #16
+ bne cont5
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt delay5
+
+ /* Disable dqs pull down in the IOMUX. */
+ /*
+ setmem /32 0x020e05a8 = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS0 - DSE=110
+ setmem /32 0x020e05b0 = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS1 - DSE=110
+ setmem /32 0x020e0524 = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS2 - DSE=110
+ setmem /32 0x020e051c = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS3 - DSE=110
+ setmem /32 0x020e0518 = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS4 - DSE=110
+ setmem /32 0x020e050c = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS5 - DSE=110
+ setmem /32 0x020e05b8 = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS6 - DSE=110
+ setmem /32 0x020e05c0 = 0x00000030 // IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS7 - DSE=110
+ */
+ ldr r1, [r11] @size of array
+ add r11, r11, #8 @skip first eight bytes in array
+update_iomux1:
+ ldr r0, [r11, #0x0] @ offset
+ ldr r3, [r11, #0x4]
+ str r3, [r7, r0] @Store the original IOMUX value read during boot
+ add r11, r11, #8
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt update_iomux1
+
+ /* config ESDCTL timings to 528MHz:
+ @// setmem /32 0x021b000c = 0x555A7975 @// MMDC0_MDCFG0 see spread sheet for timings
+ @//setmem /32 0x021b0010 = 0xFF538E64 @// MMDC0_MDCFG1 see spread sheet for timings
+ @//setmem /32 0x021b0014 = 0x01ff00db @// MMDC0_MDCFG2 - tRRD - 4ck; tWTR - 4ck; tRTP - 4ck; tDLLK - 512ck
+ @//setmem /32 0x021b0018 = 0x00081740 @// MMDC0_MDMISC, RALAT=0x5 (original value)
+ */
+
+ ldr r0, [r5, #0x0C]
+ bic r0, r0, #0xf
+ orr r0, r0, #0x5
+ str r0, [r5, #0x0C]
+
+ ldr r0, [r5, #0x10]
+ bic r0, r0, #0x7
+ orr r0, r0, #0x4
+ str r0, [r5, #0x10]
+
+ /* update MISC register: WALAT, RALAT */
+ ldr r0, =0x00081740
+ str r0, [r5, #0x18]
+
+ /*configure ddr devices to dll on, odt
+ @//setmem /32 0x021b001c = 0x00428031
+ @//setmem /32 0x021b001c = 0x00428039
+ */
+ ldr r0, =0x00028031
+ str r0, [r5, #0x1C]
+
+ ldr r0, =0x00028039
+ str r0, [r5, #0x1C]
+
+ /* delay for while */
+ ldr r1, =4
+delay7:
+ ldr r2, =0
+cont7:
+ ldr r0, [r5, r2]
+ add r2, r2, #4
+ cmp r2, #16
+ bne cont7
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt delay7
+
+ /* reset dll
+ @// setmem /32 0x021b001c = 0x09208030
+ @// setmem /32 0x021b001c = 0x09208038
+ */
+ ldr r0, =0x09208030
+ str r0, [r5, #0x1C]
+
+ ldr r0, =0x09208038
+ str r0, [r5, #0x1C]
+
+ /* delay for while */
+ ldr r1, =100
+delay8:
+ ldr r2, =0
+cont8:
+ ldr r0, [r5, r2]
+ add r2, r2, #4
+ cmp r2, #16
+ bne cont8
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt delay8
+
+ /* tcwl=6:
+ @//setmem /32 0x021b001c = 0x04088032
+ @//setmem /32 0x021b001c = 0x0408803a
+ */
+ ldr r0, =0x04088032
+ str r0, [r5, #0x1C]
+
+ ldr r0, =0x0408803a
+ str r0, [r5, #0x1C]
+
+ ldr r0, =0x00428031
+ str r0, [r5, #0x1C]
+
+ ldr r0, =0x00428039
+ str r0, [r5, #0x1C]
+
+ /* tcl=8
+ @// setmem /32 0x021b001c = 0x08408030
+ @// setmem /32 0x021b001c = 0x08408038
+ */
+ ldr r0, =0x08408030
+ str r0, [r5, #0x1C]
+
+ ldr r0, =0x08408038
+ str r0, [r5, #0x1C]
+
+ /* issue a zq command
+ @// setmem /32 0x021b001c = 0x04000040
+ @// setmem /32 0x021b001c = 0x04000048
+ */
+ ldr r0, =0x04008040
+ str r0, [r5, #0x1C]
+
+ ldr r0, =0x04008048
+ str r0, [r5, #0x1C]
+
+ /* ESDCTL ODT enable
+ @//setmem /32 0x021b0818 = 0x00022225 @// DDR_PHY_P0_MPODTCTRL
+ @//setmem /32 0x021b4818 = 0x00022225 @// DDR_PHY_P1_MPODTCTRL
+ */
+ ldr r0, =0x00022225
+ str r0, [r5, #0x818]
+ ldr r2, =0x4818
+ str r0, [r5, r2]
+
+ /* delay for while */
+ ldr r1, =40
+delay15:
+ ldr r2, =0
+cont15:
+ ldr r0, [r5, r2]
+ add r2, r2, #4
+ cmp r2, #16
+ bne cont15
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt delay15
+
+ /* MMDC0_MAPSR adopt power down enable */
+ /* setmem /32 0x021b0404 = 0x00011006 */
+ ldr r0, [r5, #0x404]
+ bic r0, r0, #0x01
+ str r0, [r5, #0x404]
+
+ /* Enable MMDC power down timer. */
+ ldr r0, [r5, #0x4]
+ orr r0, r0, #0x5500
+ str r0, [r5, #0x4]
+
+update_calibration:
+ /* Write the new calibration values. */
+ ldr r1, [r8] @size of array
+ add r8, r8, #8 @skip first eight bytes in array
+update_calib:
+ ldr r0, [r8, #0x0] @ offset
+ ldr r3, [r8, #0x4] @ value
+ str r3, [r5, r0]
+ add r8, r8, #8
+ sub r1, r1, #1
+ cmp r1, #0
+ bgt update_calib
+
+ /* Perform a force measurement. */
+ ldr r0, =0x800
+ str r0, [r5, #0x8B8]
+ ldr r2, =0x48B8
+ str r0, [r5, r2]
+
+ /* clear SBS - unblock DDR accesses */
+ ldr r0, [r5, #0x410]
+ bic r0, r0, #0x100
+ str r0, [r5, #0x410]
+
+ mov r0, #0x0
+ str r0, [r5, #0x1C]
+poll_conreq_clear_2:
+ ldr r0, [r5, #0x1C]
+ and r0, r0, #0x4000
+ cmp r0, #0x4000
+ beq poll_conreq_clear_2
+
+done:
+
+ /* Restore registers */
+
+ ldmfd sp!, {r4,r5,r6, r7, r8, r9, r10, r11}
+
+ mov pc, lr
+
+ .type mx6_do_ddr_freq_change, #object
+ENTRY(mx6_do_ddr_freq_change)
+ .word mx6_ddr_freq_change
+ .size mx6_ddr_freq_change, . - mx6_ddr_freq_change
--- /dev/null
+/*
+ * Copyright (C) 2011-2012 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 mx6_mmdc.c
+ *
+ * @brief MX6 MMDC specific file.
+ *
+ * @ingroup PM
+ */
+#include <asm/io.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/iram_alloc.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/cpumask.h>
+#include <linux/kernel.h>
+#include <linux/smp.h>
+#include <mach/hardware.h>
+#include <mach/clock.h>
+#include <asm/mach/map.h>
+#include <asm/mach-types.h>
+#include <asm/cacheflush.h>
+#include <asm/tlb.h>
+#include <asm/hardware/gic.h>
+#include "crm_regs.h"
+
+
+/* DDR settings */
+unsigned long (*iram_ddr_settings)[2];
+unsigned long (*normal_mmdc_settings)[2];
+unsigned long (*iram_iomux_settings)[2];
+void __iomem *mmdc_base;
+void __iomem *iomux_base;
+void __iomem *gic_dist_base;
+void __iomem *gic_cpu_base;
+
+void (*mx6_change_ddr_freq)(u32 freq, void *ddr_settings, bool dll_mode, void* iomux_offsets) = NULL;
+
+extern unsigned int ddr_low_rate;
+extern unsigned int ddr_med_rate;
+extern unsigned int ddr_normal_rate;
+extern int low_bus_freq_mode;
+extern int mmdc_med_rate;
+extern void __iomem *ccm_base;
+extern void mx6_ddr_freq_change(u32 freq, void *ddr_settings, bool dll_mode, void *iomux_offsets);
+
+static void *ddr_freq_change_iram_base;
+static int ddr_settings_size;
+static int iomux_settings_size;
+static volatile unsigned int cpus_in_wfe;
+static volatile bool wait_for_ddr_freq_update;
+
+
+#define MIN_DLL_ON_FREQ 333000000
+#define MAX_DLL_OFF_FREQ 125000000
+
+unsigned long ddr3_mmdc_regs_offsets[][2] = {
+ {0x83c, 0x0},
+ {0x840, 0x0},
+ {0x483c, 0x0},
+ {0x4840, 0x0},
+ {0x848, 0x0},
+ {0x4848, 0x0},
+ {0x850, 0x0},
+ {0x4850, 0x0},
+};
+
+unsigned long iomux_offsets_mx6q[][2] = {
+ {0x5A8, 0x0},
+ {0x5B0, 0x0},
+ {0x524, 0x0},
+ {0x51C, 0x0},
+ {0x518, 0x0},
+ {0x50C, 0x0},
+ {0x5B8, 0x0},
+ {0x5C0, 0x0},
+};
+unsigned long iomux_offsets_mx6dl[][2] = {
+ {0x4BC, 0x0},
+ {0x4C0, 0x0},
+ {0x4C4, 0x0},
+ {0x4C8, 0x0},
+ {0x4CC, 0x0},
+ {0x4D0, 0x0},
+ {0x4D4, 0x0},
+ {0x4D8, 0x0},
+};
+
+unsigned long ddr3_400[][2] = {
+ {0x83c, 0x42490249},
+ {0x840, 0x02470247},
+ {0x483c, 0x42570257},
+ {0x4840, 0x02400240},
+ {0x848, 0x4039363C},
+ {0x4848, 0x3A39333F},
+ {0x850, 0x38414441},
+ {0x4850, 0x472D4833}
+};
+
+unsigned long *irq_used;
+
+unsigned long irqs_used_mx6q[] = {
+ MXC_INT_INTERRUPT_139_NUM,
+ MX6Q_INT_PERFMON1,
+ MX6Q_INT_PERFMON2,
+ MX6Q_INT_PERFMON3,
+};
+
+unsigned long irqs_used_mx6dl[] = {
+ MXC_INT_INTERRUPT_139_NUM,
+ MX6Q_INT_PERFMON1,
+};
+
+int can_change_ddr_freq(void)
+{
+ return 1;
+}
+
+
+/* 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.
+ */
+irqreturn_t wait_in_wfe_irq(int irq, void *dev_id)
+{
+ u32 me = smp_processor_id();
+
+ *((char *)(&cpus_in_wfe) + (u8)me) = 0xff;
+
+ while (wait_for_ddr_freq_update)
+ wfe();
+
+ *((char *)(&cpus_in_wfe) + (u8)me) = 0;
+ return IRQ_HANDLED;
+}
+
+/* Change the DDR frequency. */
+int update_ddr_freq(int ddr_rate)
+{
+ int i;
+ unsigned int reg;
+ bool dll_off = false;
+ unsigned int online_cpus = 0;
+ int cpu = 0;
+ int me;
+
+ if (!can_change_ddr_freq())
+ return -1;
+
+ if (low_bus_freq_mode)
+ dll_off = true;
+
+ iram_ddr_settings[0][0] = ddr_settings_size;
+ iram_iomux_settings[0][0] = iomux_settings_size;
+ if (ddr_rate == ddr_med_rate) {
+ for (i = 0; i < iram_ddr_settings[0][0]; i++) {
+ iram_ddr_settings[i + 1][0] =
+ ddr3_400[i][0];
+ iram_ddr_settings[i + 1][1] =
+ ddr3_400[i][1];
+ }
+ } else if (ddr_rate == ddr_normal_rate) {
+ for (i = 0; i < iram_ddr_settings[0][0]; i++) {
+ iram_ddr_settings[i + 1][0] =
+ normal_mmdc_settings[i][0];
+ iram_ddr_settings[i + 1][1] =
+ normal_mmdc_settings[i][1];
+ }
+ }
+
+ /* Ensure that all Cores are in WFE. */
+ local_irq_disable();
+
+ me = smp_processor_id();
+
+ *((char *)(&cpus_in_wfe) + (u8)me) = 0xff;
+ wait_for_ddr_freq_update = true;
+
+ for_each_online_cpu(cpu) {
+ *((char *)(&online_cpus) + (u8)cpu) = 0xff;
+ if (cpu != me) {
+ /* Set the interrupt to be pending in the GIC. */
+ reg = 1 << (irq_used[cpu] % 32);
+ writel_relaxed(reg, gic_dist_base + GIC_DIST_PENDING_SET + (irq_used[cpu] / 32) * 4);
+ udelay(10);
+ }
+ }
+ while (cpus_in_wfe != online_cpus)
+ udelay(5);
+
+ /* Now we can change the DDR frequency. */
+ mx6_change_ddr_freq(ddr_rate, iram_ddr_settings, dll_off, iram_iomux_settings);
+
+ /* DDR frequency change is done . */
+ wait_for_ddr_freq_update = false;
+
+ /* Wake up all the cores. */
+ sev();
+
+ *((char *)(&cpus_in_wfe) + (u8)me) = 0;
+
+ local_irq_enable();
+
+ return 0;
+}
+
+int init_mmdc_settings(void)
+{
+ unsigned long iram_paddr;
+ int i, err, cpu;
+
+ mmdc_base = ioremap(MMDC_P0_BASE_ADDR, SZ_32K);
+ iomux_base = ioremap(MX6Q_IOMUXC_BASE_ADDR, SZ_16K);
+ gic_dist_base = ioremap(IC_DISTRIBUTOR_BASE_ADDR, SZ_16K);
+ gic_cpu_base = ioremap(IC_INTERFACES_BASE_ADDR, SZ_16K);
+
+ normal_mmdc_settings = ddr3_mmdc_regs_offsets;
+ ddr_settings_size = ARRAY_SIZE(ddr3_mmdc_regs_offsets);
+
+ /* Store the original DDR settings at boot. */
+ for (i = 0; i < ddr_settings_size; i++) {
+ normal_mmdc_settings[i][1] =
+ __raw_readl(mmdc_base
+ + normal_mmdc_settings[i][0]);
+ }
+ /* Store the size of the array in iRAM also,
+ * increase the size by 8 bytes.
+ */
+ iram_ddr_settings = iram_alloc(ddr_settings_size + 8, &iram_paddr);
+ if (iram_ddr_settings == NULL) {
+ printk(KERN_DEBUG
+ "%s: failed to allocate iRAM memory for ddr settings\n",
+ __func__);
+ return ENOMEM;
+ }
+
+ /* Store the size of the iomux settings in iRAM also,
+ * increase the size by 8 bytes.
+ */
+ iram_iomux_settings = iram_alloc(iomux_settings_size + 8, &iram_paddr);
+ if (iram_iomux_settings == NULL) {
+ printk(KERN_DEBUG
+ "%s: failed to allocate iRAM memory for iomuxr settings\n",
+ __func__);
+ return ENOMEM;
+ }
+
+ /* Store the IOMUX settings at boot. */
+ if (cpu_is_mx6q()) {
+ iomux_settings_size = ARRAY_SIZE(iomux_offsets_mx6q);
+ for (i = 0; i < iomux_settings_size; i++) {
+ iomux_offsets_mx6q[i][1] =
+ __raw_readl(iomux_base
+ + iomux_offsets_mx6q[i][0]);
+ iram_iomux_settings[i+1][0] = iomux_offsets_mx6q[i][0];
+ iram_iomux_settings[i+1][1] = iomux_offsets_mx6q[i][1];
+ }
+ irq_used = irqs_used_mx6q;
+ }
+ if (cpu_is_mx6dl()) {
+ iomux_settings_size = ARRAY_SIZE(iomux_offsets_mx6dl);
+ for (i = 0; i < iomux_settings_size; i++) {
+ iomux_offsets_mx6dl[i][1] =
+ __raw_readl(iomux_base
+ + iomux_offsets_mx6dl[i][0]);
+ iram_iomux_settings[i+1][0] = iomux_offsets_mx6dl[i][0];
+ iram_iomux_settings[i+1][1] = iomux_offsets_mx6dl[i][1];
+ }
+ irq_used = irqs_used_mx6dl;
+ }
+
+ /* Allocate IRAM for the DDR freq change code. */
+ iram_alloc(SZ_8K, &iram_paddr);
+ /* Need to remap the area here since we want the memory region
+ to be executable. */
+ ddr_freq_change_iram_base = __arm_ioremap(iram_paddr,
+ SZ_8K, MT_MEMORY_NONCACHED);
+ memcpy(ddr_freq_change_iram_base, mx6_ddr_freq_change, SZ_8K);
+ mx6_change_ddr_freq = (void *)ddr_freq_change_iram_base;
+
+ for_each_online_cpu(cpu) {
+ /* Set up a reserved interrupt to get all the active cores into a WFE state
+ * before changing the DDR frequency.
+ */
+ err = request_irq(irq_used[cpu], wait_in_wfe_irq, IRQF_PERCPU, "mmdc_1",
+ NULL);
+ if (err) {
+ printk(KERN_ERR "MMDC: Unable to attach to %ld,err = %d\n", irq_used[cpu], err);
+ return err;
+ }
+ err = irq_set_affinity(irq_used[cpu], cpumask_of(cpu));
+ if (err) {
+ printk(KERN_ERR "MMDC: unable to set irq affinity irq=%ld,\n", irq_used[cpu]);
+ return err;
+ }
+ }
+ return 0;
+}
return;
if (!clk->usecount) {
- WARN(1, "clock enable/disable mismatch!\n");
+ WARN(1, "clock enable/disable mismatch! clk %s\n", clk->name);
return;
}
if (clk == NULL || IS_ERR(clk))
return -EINVAL;
+ if (clk->flags & AHB_HIGH_SET_POINT)
+ lp_high_freq++;
+ else if (clk->flags & AHB_MED_SET_POINT)
+ lp_med_freq++;
+
if ((clk->flags & CPU_FREQ_TRIG_UPDATE)
&& (clk_get_usecount(clk) == 0)) {
if (!(clk->flags &
set_high_bus_freq(1);
}
}
-
mutex_lock(&clocks_mutex);
ret = __clk_enable(clk);
mutex_unlock(&clocks_mutex);
if (clk == NULL || IS_ERR(clk))
return;
+ if (clk->flags & AHB_HIGH_SET_POINT)
+ lp_high_freq--;
+ else if (clk->flags & AHB_MED_SET_POINT)
+ lp_med_freq--;
+
mutex_lock(&clocks_mutex);
__clk_disable(clk);
mutex_unlock(&clocks_mutex);
extern struct regulator *cpu_regulator;
extern int dvfs_core_is_active;
extern struct cpu_op *(*get_cpu_op)(int *op);
+extern int low_bus_freq_mode;
+extern int high_bus_freq_mode;
+extern int set_low_bus_freq(void);
+extern int set_high_bus_freq(int high_bus_speed);
+extern int low_freq_bus_used(void);
int set_cpu_freq(int freq)
{
/*Set the voltage for the GP domain. */
if (freq > org_cpu_rate) {
+ if (low_bus_freq_mode)
+ set_high_bus_freq(0);
ret = regulator_set_voltage(cpu_regulator, gp_volt,
gp_volt);
if (ret < 0) {
printk(KERN_DEBUG "COULD NOT SET GP VOLTAGE!!!!\n");
return ret;
}
+ if (low_freq_bus_used() && !low_bus_freq_mode)
+ set_low_bus_freq();
}
return ret;