diff options
Diffstat (limited to 'arch/arm/mach-mx6/cpu.c')
-rw-r--r-- | arch/arm/mach-mx6/cpu.c | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/arch/arm/mach-mx6/cpu.c b/arch/arm/mach-mx6/cpu.c new file mode 100644 index 00000000..86df0826 --- /dev/null +++ b/arch/arm/mach-mx6/cpu.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2011-2013 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/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/iram_alloc.h> +#include <linux/delay.h> + +#include <mach/hardware.h> +#include <mach/system.h> +#include <asm/io.h> +#include <asm/mach/map.h> + +#include "crm_regs.h" +#include "cpu_op-mx6.h" + +struct cpu_op *(*get_cpu_op)(int *op); +bool enable_wait_mode = true; +u32 enable_ldo_mode = LDO_MODE_DEFAULT; +u32 arm_max_freq = CPU_AT_1_2GHz; +bool mem_clk_on_in_wait; +int chip_rev; +unsigned long iram_tlb_base_addr; +unsigned long iram_tlb_phys_addr; + +void __iomem *gpc_base; +void __iomem *ccm_base; + +extern unsigned int num_cpu_idle_lock; + +static int cpu_silicon_rev = -1; +#define MX6_USB_ANALOG_DIGPROG 0x260 +#define MX6SL_USB_ANALOG_DIGPROG 0x280 + +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(u32 ttbr1) +{ + asm volatile( + ".align 4\n" + "mcr p15, 0, %0, c2, c0, 1\n" + : : "r" (ttbr1) + ); +} + +static int mx6_get_srev(void) +{ + void __iomem *anatop = MX6_IO_ADDRESS(ANATOP_BASE_ADDR); + u32 rev; + if (cpu_is_mx6sl()) + rev = __raw_readl(anatop + MX6SL_USB_ANALOG_DIGPROG); + else + rev = __raw_readl(anatop + MX6_USB_ANALOG_DIGPROG); + + rev &= 0xff; + + if (rev == 0) + return IMX_CHIP_REVISION_1_0; + else if (rev == 1) + return IMX_CHIP_REVISION_1_1; + else if (rev == 2) + return IMX_CHIP_REVISION_1_2; + + return IMX_CHIP_REVISION_UNKNOWN; +} + +/* + * Returns: + * the silicon revision of the cpu + */ +int mx6q_revision(void) +{ + if (!cpu_is_mx6q()) + return -EINVAL; + + if (cpu_silicon_rev == -1) + cpu_silicon_rev = mx6_get_srev(); + + return cpu_silicon_rev; +} +EXPORT_SYMBOL(mx6q_revision); + +/* + * Returns: + * the silicon revision of the cpu + */ +int mx6dl_revision(void) +{ + if (!cpu_is_mx6dl()) + return -EINVAL; + + if (cpu_silicon_rev == -1) + cpu_silicon_rev = mx6_get_srev(); + + return cpu_silicon_rev; +} +EXPORT_SYMBOL(mx6dl_revision); + +/* + * Returns: + * the silicon revision of the cpu + */ +int mx6sl_revision(void) +{ + if (!cpu_is_mx6sl()) + return -EINVAL; + + if (cpu_silicon_rev == -1) + cpu_silicon_rev = mx6_get_srev(); + + return cpu_silicon_rev; +} +EXPORT_SYMBOL(mx6sl_revision); + +static int __init post_cpu_init(void) +{ + unsigned int reg; + void __iomem *base; + u32 iram_size; + + if (cpu_is_mx6q()) + iram_size = MX6Q_IRAM_SIZE; + else + iram_size = MX6DL_MX6SL_IRAM_SIZE; + + iram_init(MX6Q_IRAM_BASE_ADDR, iram_size); + + base = ioremap(AIPS1_ON_BASE_ADDR, PAGE_SIZE); + __raw_writel(0x0, base + 0x40); + __raw_writel(0x0, base + 0x44); + __raw_writel(0x0, base + 0x48); + __raw_writel(0x0, base + 0x4C); + reg = __raw_readl(base + 0x50) & 0x00FFFFFF; + __raw_writel(reg, base + 0x50); + iounmap(base); + + base = ioremap(AIPS2_ON_BASE_ADDR, PAGE_SIZE); + __raw_writel(0x0, base + 0x40); + __raw_writel(0x0, base + 0x44); + __raw_writel(0x0, base + 0x48); + __raw_writel(0x0, base + 0x4C); + reg = __raw_readl(base + 0x50) & 0x00FFFFFF; + __raw_writel(reg, base + 0x50); + iounmap(base); + + /* Force IOMUXC irq to be pending for CCM LPM */ + base = IO_ADDRESS(MX6Q_IOMUXC_BASE_ADDR); + reg = __raw_readl(base + 0x4); + reg |= 0x1000; + __raw_writel(reg, base + 0x4); + + /* Allow SCU_CLK to be disabled when all cores are in WFI*/ + base = IO_ADDRESS(SCU_BASE_ADDR); + reg = __raw_readl(base); + reg |= 0x20; + __raw_writel(reg, base); + + /* Disable SRC warm reset to work aound system reboot issue */ + base = IO_ADDRESS(SRC_BASE_ADDR); + reg = __raw_readl(base); + reg &= ~0x1; + __raw_writel(reg, base); + + gpc_base = MX6_IO_ADDRESS(GPC_BASE_ADDR); + ccm_base = MX6_IO_ADDRESS(CCM_BASE_ADDR); + + /* enable AXI cache for VDOA/VPU/IPU + * set IPU AXI-id0 Qos=0xf(bypass) AXI-id1 Qos=0x7 + * clear OCRAM_CTL bits to disable pipeline control + */ + reg = __raw_readl(IOMUXC_GPR3); + reg &= ~IOMUXC_GPR3_OCRAM_CTL_EN; + __raw_writel(reg, IOMUXC_GPR3); + reg = __raw_readl(IOMUXC_GPR4); + reg |= IOMUXC_GPR4_VDOA_CACHE_EN | IOMUXC_GPR4_VPU_CACHE_EN | + IOMUXC_GPR4_IPU_CACHE_EN; + __raw_writel(reg, IOMUXC_GPR4); + __raw_writel(IOMUXC_GPR6_IPU1_QOS, IOMUXC_GPR6); + __raw_writel(IOMUXC_GPR7_IPU2_QOS, IOMUXC_GPR7); + + num_cpu_idle_lock = 0x0; + if (cpu_is_mx6dl()) + num_cpu_idle_lock = 0xffff0000; + +#ifdef CONFIG_SMP + switch (setup_max_cpus) { + case 3: + num_cpu_idle_lock = 0xff000000; + break; + case 2: + num_cpu_idle_lock = 0xffff0000; + break; + case 1: + case 0: + num_cpu_idle_lock = 0xffffff00; + break; + } +#endif + /* + * The option to keep ARM memory clocks enabled during WAIT + * is only available on MX6SL, MX6DQ TO1.2 (or later) and + * MX6DL TO1.1 (or later) + * So if the user specifies "mem_clk_on" on any other chip, + * ensure that it is disabled. + */ + if (!cpu_is_mx6sl() && (mx6q_revision() < IMX_CHIP_REVISION_1_2) && + (mx6dl_revision() < IMX_CHIP_REVISION_1_1)) + mem_clk_on_in_wait = false; + + if (cpu_is_mx6q()) + chip_rev = mx6q_revision(); + else if (cpu_is_mx6dl()) + chip_rev = mx6dl_revision(); + else + chip_rev = mx6sl_revision(); + + /* mx6sl doesn't have pcie. save power, disable PCIe PHY */ + if (!cpu_is_mx6sl()) { + reg = __raw_readl(IOMUXC_GPR1); + reg = reg & (~IOMUXC_GPR1_PCIE_REF_CLK_EN); + reg |= IOMUXC_GPR1_TEST_POWERDOWN; + __raw_writel(reg, IOMUXC_GPR1); + } + if (cpu_is_mx6sl()) { + int i; + /* + * Allocate the bottom 16K of IRAM page tables that + * will be used when DDR is in self-refresh. + */ + iram_tlb_phys_addr = MX6_IRAM_TLB_BASE_ADDR; + iram_tlb_base_addr = (unsigned long)__arm_ioremap(iram_tlb_phys_addr, + MX6_IRAM_TLB_SIZE, MT_MEMORY_NONCACHED); + + /* Set all entries to 0. */ + memset((void *)iram_tlb_base_addr, 0, MX6_IRAM_TLB_SIZE); + + /* Make sure the IRAM virtual address has a mapping in the IRAM page table. */ + i = ((IRAM_BASE_ADDR_VIRT >> 20) << 2 ) / 4; + *((unsigned long *)iram_tlb_base_addr + i) = + (IRAM_BASE_ADDR & 0xFFF0000) | TT_ATTRIB_NON_CACHEABLE_1M; + /* Make sure the AIPS1 virtual address has a mapping in the IRAM page table. */ + i = ((AIPS1_BASE_ADDR_VIRT >> 20) << 2) / 4; + *((unsigned long *)iram_tlb_base_addr + i) = + (AIPS1_ARB_BASE_ADDR & 0xFFF00000) | TT_ATTRIB_NON_CACHEABLE_1M; + /* Make sure the AIPS2 virtual address has a mapping in the IRAM page table. */ + i = ((AIPS2_BASE_ADDR_VIRT >> 20) << 2) / 4; + *((unsigned long *)iram_tlb_base_addr + i) = + (AIPS2_ARB_BASE_ADDR & 0xFFF00000) | TT_ATTRIB_NON_CACHEABLE_1M; + /* Make sure the AIPS2 virtual address has a mapping in the IRAM page table. */ + i = ((L2_BASE_ADDR_VIRT >> 20) << 2) / 4; + *((unsigned long *)iram_tlb_base_addr + i) = + (L2_BASE_ADDR & 0xFFF0000) | TT_ATTRIB_NON_CACHEABLE_1M; + + } + return 0; +} +postcore_initcall(post_cpu_init); + +static int __init enable_wait(char *p) +{ + if (memcmp(p, "on", 2) == 0) { + enable_wait_mode = true; + p += 2; + } else if (memcmp(p, "off", 3) == 0) { + enable_wait_mode = false; + p += 3; + } + return 0; +} +early_param("enable_wait_mode", enable_wait); + +static int __init arm_core_max(char *p) +{ + if (memcmp(p, "1200", 4) == 0) { + arm_max_freq = CPU_AT_1_2GHz; + p += 4; + } else if (memcmp(p, "1000", 4) == 0) { + arm_max_freq = CPU_AT_1GHz; + p += 4; + } else if (memcmp(p, "800", 3) == 0) { + arm_max_freq = CPU_AT_800MHz; + p += 3; + } + return 0; +} + +early_param("arm_freq", arm_core_max); + +static int __init enable_ldo(char *p) +{ + if (memcmp(p, "on", 2) == 0) { + enable_ldo_mode = LDO_MODE_ENABLED; + p += 2; + } else if (memcmp(p, "off", 3) == 0) { + enable_ldo_mode = LDO_MODE_BYPASSED; + p += 3; + } + return 0; +} +early_param("ldo_active", enable_ldo); + +static int __init enable_mem_clk_in_wait(char *p) +{ + mem_clk_on_in_wait = true; + + return 0; +} + +early_param("mem_clk_on", enable_mem_clk_in_wait); + + + |