Skip to content

Commit

Permalink
Merge tag 'imx-soc-3.20' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/git/shawnguo/linux into next/soc

Merge "ARM: imx: soc changes for 3.20" from Shawn Guo:

The i.MX SoC changes for 3.20:
 - Add .disable_unused function hook for shared gate clock to ensure
   the clock tree use count matches the hardware state
 - Add a deeper idle state for i.MX6SX cpuidle driver powering off the
   ARM core
 - One correction on i.MX6Q esai_ipg parent clock setting
 - Add a missing iounmap call for imx6q_opp_check_speed_grading()
 - Add missing clocks for VF610 UART4, UART5 and SNVS blocks
 - Expand VF610 device tree compatible matching table to cover more
   Vybrid family SoCs
 - Expand i.MX clk-pllv3 a bit with the shift for frequency multiplier
   to support Vybrid's USB PLL oddity

* tag 'imx-soc-3.20' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux:
  ARM: clk-imx6q: refine esai_ipg's parent
  ARM i.MX6q: unmap memory mapped at imx6q_opp_check_speed_grading()
  ARM: imx: clk-vf610: Add clock for SNVS
  ARM: imx: clk-vf610: Add clock for UART4 and UART5
  ARM: imx: drop CPUIDLE_FLAG_TIME_VALID from cpuidle-imx6sx
  ARM: imx: support arm power off in cpuidle for i.mx6sx
  ARM: imx: remove unnecessary setting for DSM
  ARM: imx: correct the hardware clock gate setting for shared nodes
  ARM: imx: pllv3: add shift for frequency multiplier
  ARM vf610: add compatibilty strings of supported Vybrid SoC's

Signed-off-by: Olof Johansson <olof@lixom.net>
  • Loading branch information
Olof Johansson committed Jan 23, 2015
2 parents eeec043 + ade9233 commit 085dd64
Show file tree
Hide file tree
Showing 16 changed files with 195 additions and 22 deletions.
12 changes: 12 additions & 0 deletions Documentation/devicetree/bindings/arm/fsl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,18 @@ i.MX6q generic board
Required root node properties:
- compatible = "fsl,imx6q";

Freescale Vybrid Platform Device Tree Bindings
----------------------------------------------

For the Vybrid SoC familiy all variants with DDR controller are supported,
which is the VF5xx and VF6xx series. Out of historical reasons, in most
places the kernel uses vf610 to refer to the whole familiy.

Required root node compatible property (one of them):
- compatible = "fsl,vf500";
- compatible = "fsl,vf510";
- compatible = "fsl,vf600";
- compatible = "fsl,vf610";

Freescale LS1021A Platform Device Tree Bindings
------------------------------------------------
Expand Down
3 changes: 1 addition & 2 deletions arch/arm/mach-imx/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,7 @@ ifeq ($(CONFIG_CPU_IDLE),y)
obj-$(CONFIG_SOC_IMX5) += cpuidle-imx5.o
obj-$(CONFIG_SOC_IMX6Q) += cpuidle-imx6q.o
obj-$(CONFIG_SOC_IMX6SL) += cpuidle-imx6sl.o
# i.MX6SX reuses i.MX6Q cpuidle driver
obj-$(CONFIG_SOC_IMX6SX) += cpuidle-imx6q.o
obj-$(CONFIG_SOC_IMX6SX) += cpuidle-imx6sx.o
endif

ifdef CONFIG_SND_IMX_SOC
Expand Down
23 changes: 19 additions & 4 deletions arch/arm/mach-imx/clk-gate2.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,30 @@ static int clk_gate2_is_enabled(struct clk_hw *hw)
{
struct clk_gate2 *gate = to_clk_gate2(hw);

if (gate->share_count)
return !!__clk_get_enable_count(hw->clk);
else
return clk_gate2_reg_is_enabled(gate->reg, gate->bit_idx);
return clk_gate2_reg_is_enabled(gate->reg, gate->bit_idx);
}

static void clk_gate2_disable_unused(struct clk_hw *hw)
{
struct clk_gate2 *gate = to_clk_gate2(hw);
unsigned long flags = 0;
u32 reg;

spin_lock_irqsave(gate->lock, flags);

if (!gate->share_count || *gate->share_count == 0) {
reg = readl(gate->reg);
reg &= ~(3 << gate->bit_idx);
writel(reg, gate->reg);
}

spin_unlock_irqrestore(gate->lock, flags);
}

static struct clk_ops clk_gate2_ops = {
.enable = clk_gate2_enable,
.disable = clk_gate2_disable,
.disable_unused = clk_gate2_disable_unused,
.is_enabled = clk_gate2_is_enabled,
};

Expand Down
2 changes: 1 addition & 1 deletion arch/arm/mach-imx/clk-imx6q.c
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
clk[IMX6Q_CLK_ECSPI5] = imx_clk_gate2("ecspi5", "ecspi_root", base + 0x6c, 8);
clk[IMX6QDL_CLK_ENET] = imx_clk_gate2("enet", "ipg", base + 0x6c, 10);
clk[IMX6QDL_CLK_ESAI_EXTAL] = imx_clk_gate2_shared("esai_extal", "esai_podf", base + 0x6c, 16, &share_count_esai);
clk[IMX6QDL_CLK_ESAI_IPG] = imx_clk_gate2_shared("esai_ipg", "ipg", base + 0x6c, 16, &share_count_esai);
clk[IMX6QDL_CLK_ESAI_IPG] = imx_clk_gate2_shared("esai_ipg", "ahb", base + 0x6c, 16, &share_count_esai);
clk[IMX6QDL_CLK_ESAI_MEM] = imx_clk_gate2_shared("esai_mem", "ahb", base + 0x6c, 16, &share_count_esai);
clk[IMX6QDL_CLK_GPT_IPG] = imx_clk_gate2("gpt_ipg", "ipg", base + 0x6c, 20);
clk[IMX6QDL_CLK_GPT_IPG_PER] = imx_clk_gate2("gpt_ipg_per", "ipg_per", base + 0x6c, 22);
Expand Down
10 changes: 7 additions & 3 deletions arch/arm/mach-imx/clk-pllv3.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
* @base: base address of PLL registers
* @powerup_set: set POWER bit to power up the PLL
* @div_mask: mask of divider bits
* @div_shift: shift of divider bits
*
* IMX PLL clock version 3, found on i.MX6 series. Divider for pllv3
* is actually a multiplier, and always sits at bit 0.
Expand All @@ -40,6 +41,7 @@ struct clk_pllv3 {
void __iomem *base;
bool powerup_set;
u32 div_mask;
u32 div_shift;
};

#define to_clk_pllv3(_hw) container_of(_hw, struct clk_pllv3, hw)
Expand Down Expand Up @@ -97,7 +99,7 @@ static unsigned long clk_pllv3_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_pllv3 *pll = to_clk_pllv3(hw);
u32 div = readl_relaxed(pll->base) & pll->div_mask;
u32 div = (readl_relaxed(pll->base) >> pll->div_shift) & pll->div_mask;

return (div == 1) ? parent_rate * 22 : parent_rate * 20;
}
Expand Down Expand Up @@ -125,8 +127,8 @@ static int clk_pllv3_set_rate(struct clk_hw *hw, unsigned long rate,
return -EINVAL;

val = readl_relaxed(pll->base);
val &= ~pll->div_mask;
val |= div;
val &= ~(pll->div_mask << pll->div_shift);
val |= (div << pll->div_shift);
writel_relaxed(val, pll->base);

return clk_pllv3_wait_lock(pll);
Expand Down Expand Up @@ -295,6 +297,8 @@ struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
case IMX_PLLV3_SYS:
ops = &clk_pllv3_sys_ops;
break;
case IMX_PLLV3_USB_VF610:
pll->div_shift = 1;
case IMX_PLLV3_USB:
ops = &clk_pllv3_ops;
pll->powerup_set = true;
Expand Down
8 changes: 6 additions & 2 deletions arch/arm/mach-imx/clk-vf610.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,11 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)

clk[VF610_CLK_PLL1] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll1", "pll1_bypass_src", PLL1_CTRL, 0x1);
clk[VF610_CLK_PLL2] = imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2", "pll2_bypass_src", PLL2_CTRL, 0x1);
clk[VF610_CLK_PLL3] = imx_clk_pllv3(IMX_PLLV3_USB, "pll3", "pll3_bypass_src", PLL3_CTRL, 0x1);
clk[VF610_CLK_PLL3] = imx_clk_pllv3(IMX_PLLV3_USB_VF610, "pll3", "pll3_bypass_src", PLL3_CTRL, 0x2);
clk[VF610_CLK_PLL4] = imx_clk_pllv3(IMX_PLLV3_AV, "pll4", "pll4_bypass_src", PLL4_CTRL, 0x7f);
clk[VF610_CLK_PLL5] = imx_clk_pllv3(IMX_PLLV3_ENET, "pll5", "pll5_bypass_src", PLL5_CTRL, 0x3);
clk[VF610_CLK_PLL6] = imx_clk_pllv3(IMX_PLLV3_AV, "pll6", "pll6_bypass_src", PLL6_CTRL, 0x7f);
clk[VF610_CLK_PLL7] = imx_clk_pllv3(IMX_PLLV3_USB, "pll7", "pll7_bypass_src", PLL7_CTRL, 0x1);
clk[VF610_CLK_PLL7] = imx_clk_pllv3(IMX_PLLV3_USB_VF610, "pll7", "pll7_bypass_src", PLL7_CTRL, 0x2);

clk[VF610_PLL1_BYPASS] = imx_clk_mux_flags("pll1_bypass", PLL1_CTRL, 16, 1, pll1_bypass_sels, ARRAY_SIZE(pll1_bypass_sels), CLK_SET_RATE_PARENT);
clk[VF610_PLL2_BYPASS] = imx_clk_mux_flags("pll2_bypass", PLL2_CTRL, 16, 1, pll2_bypass_sels, ARRAY_SIZE(pll2_bypass_sels), CLK_SET_RATE_PARENT);
Expand Down Expand Up @@ -267,6 +267,8 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
clk[VF610_CLK_UART1] = imx_clk_gate2("uart1", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(8));
clk[VF610_CLK_UART2] = imx_clk_gate2("uart2", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(9));
clk[VF610_CLK_UART3] = imx_clk_gate2("uart3", "ipg_bus", CCM_CCGR0, CCM_CCGRx_CGn(10));
clk[VF610_CLK_UART4] = imx_clk_gate2("uart4", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(9));
clk[VF610_CLK_UART5] = imx_clk_gate2("uart5", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(10));

clk[VF610_CLK_I2C0] = imx_clk_gate2("i2c0", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(6));
clk[VF610_CLK_I2C1] = imx_clk_gate2("i2c1", "ipg_bus", CCM_CCGR4, CCM_CCGRx_CGn(7));
Expand Down Expand Up @@ -380,6 +382,8 @@ static void __init vf610_clocks_init(struct device_node *ccm_node)
clk[VF610_CLK_DMAMUX2] = imx_clk_gate2("dmamux2", "platform_bus", CCM_CCGR6, CCM_CCGRx_CGn(1));
clk[VF610_CLK_DMAMUX3] = imx_clk_gate2("dmamux3", "platform_bus", CCM_CCGR6, CCM_CCGRx_CGn(2));

clk[VF610_CLK_SNVS] = imx_clk_gate2("snvs-rtc", "ipg_bus", CCM_CCGR6, CCM_CCGRx_CGn(7));

imx_check_clocks(clk, ARRAY_SIZE(clk));

clk_set_parent(clk[VF610_CLK_QSPI0_SEL], clk[VF610_CLK_PLL1_PFD4]);
Expand Down
1 change: 1 addition & 0 deletions arch/arm/mach-imx/clk.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ enum imx_pllv3_type {
IMX_PLLV3_GENERIC,
IMX_PLLV3_SYS,
IMX_PLLV3_USB,
IMX_PLLV3_USB_VF610,
IMX_PLLV3_AV,
IMX_PLLV3_ENET,
};
Expand Down
4 changes: 4 additions & 0 deletions arch/arm/mach-imx/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ void imx_set_soc_revision(unsigned int rev);
unsigned int imx_get_soc_revision(void);
void imx_init_revision_from_anatop(void);
struct device *imx_soc_device_init(void);
void imx6_enable_rbc(bool enable);
void imx_gpc_set_arm_power_in_lpm(bool power_off);
void imx_gpc_set_arm_power_up_timing(u32 sw2iso, u32 sw);
void imx_gpc_set_arm_power_down_timing(u32 sw2iso, u32 sw);

enum mxc_cpu_pwr_mode {
WAIT_CLOCKED, /* wfi only */
Expand Down
105 changes: 105 additions & 0 deletions arch/arm/mach-imx/cpuidle-imx6sx.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright (C) 2014 Freescale Semiconductor, Inc.
*
* 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.
*/

#include <linux/cpuidle.h>
#include <linux/cpu_pm.h>
#include <linux/module.h>
#include <asm/cpuidle.h>
#include <asm/proc-fns.h>
#include <asm/suspend.h>

#include "common.h"
#include "cpuidle.h"

static int imx6sx_idle_finish(unsigned long val)
{
cpu_do_idle();

return 0;
}

static int imx6sx_enter_wait(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
imx6q_set_lpm(WAIT_UNCLOCKED);

switch (index) {
case 1:
cpu_do_idle();
break;
case 2:
imx6_enable_rbc(true);
imx_gpc_set_arm_power_in_lpm(true);
imx_set_cpu_jump(0, v7_cpu_resume);
/* Need to notify there is a cpu pm operation. */
cpu_pm_enter();
cpu_cluster_pm_enter();

cpu_suspend(0, imx6sx_idle_finish);

cpu_cluster_pm_exit();
cpu_pm_exit();
imx_gpc_set_arm_power_in_lpm(false);
imx6_enable_rbc(false);
break;
default:
break;
}

imx6q_set_lpm(WAIT_CLOCKED);

return index;
}

static struct cpuidle_driver imx6sx_cpuidle_driver = {
.name = "imx6sx_cpuidle",
.owner = THIS_MODULE,
.states = {
/* WFI */
ARM_CPUIDLE_WFI_STATE,
/* WAIT */
{
.exit_latency = 50,
.target_residency = 75,
.flags = CPUIDLE_FLAG_TIMER_STOP,
.enter = imx6sx_enter_wait,
.name = "WAIT",
.desc = "Clock off",
},
/* WAIT + ARM power off */
{
/*
* ARM gating 31us * 5 + RBC clear 65us
* and some margin for SW execution, here set it
* to 300us.
*/
.exit_latency = 300,
.target_residency = 500,
.enter = imx6sx_enter_wait,
.name = "LOW-POWER-IDLE",
.desc = "ARM power off",
},
},
.state_count = 3,
.safe_state_index = 0,
};

int __init imx6sx_cpuidle_init(void)
{
imx6_enable_rbc(false);
/*
* set ARM power up/down timing to the fastest,
* sw2iso and sw can be set to one 32K cycle = 31us
* except for power up sw2iso which need to be
* larger than LDO ramp up time.
*/
imx_gpc_set_arm_power_up_timing(2, 1);
imx_gpc_set_arm_power_down_timing(1, 1);

return cpuidle_register(&imx6sx_cpuidle_driver, NULL);
}
5 changes: 5 additions & 0 deletions arch/arm/mach-imx/cpuidle.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
extern int imx5_cpuidle_init(void);
extern int imx6q_cpuidle_init(void);
extern int imx6sl_cpuidle_init(void);
extern int imx6sx_cpuidle_init(void);
#else
static inline int imx5_cpuidle_init(void)
{
Expand All @@ -27,4 +28,8 @@ static inline int imx6sl_cpuidle_init(void)
{
return 0;
}
static inline int imx6sx_cpuidle_init(void)
{
return 0;
}
#endif
25 changes: 23 additions & 2 deletions arch/arm/mach-imx/gpc.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,42 @@

#define GPC_IMR1 0x008
#define GPC_PGC_CPU_PDN 0x2a0
#define GPC_PGC_CPU_PUPSCR 0x2a4
#define GPC_PGC_CPU_PDNSCR 0x2a8
#define GPC_PGC_SW2ISO_SHIFT 0x8
#define GPC_PGC_SW_SHIFT 0x0

#define IMR_NUM 4

static void __iomem *gpc_base;
static u32 gpc_wake_irqs[IMR_NUM];
static u32 gpc_saved_imrs[IMR_NUM];

void imx_gpc_set_arm_power_up_timing(u32 sw2iso, u32 sw)
{
writel_relaxed((sw2iso << GPC_PGC_SW2ISO_SHIFT) |
(sw << GPC_PGC_SW_SHIFT), gpc_base + GPC_PGC_CPU_PUPSCR);
}

void imx_gpc_set_arm_power_down_timing(u32 sw2iso, u32 sw)
{
writel_relaxed((sw2iso << GPC_PGC_SW2ISO_SHIFT) |
(sw << GPC_PGC_SW_SHIFT), gpc_base + GPC_PGC_CPU_PDNSCR);
}

void imx_gpc_set_arm_power_in_lpm(bool power_off)
{
writel_relaxed(power_off, gpc_base + GPC_PGC_CPU_PDN);
}

void imx_gpc_pre_suspend(bool arm_power_off)
{
void __iomem *reg_imr1 = gpc_base + GPC_IMR1;
int i;

/* Tell GPC to power off ARM core when suspend */
if (arm_power_off)
writel_relaxed(0x1, gpc_base + GPC_PGC_CPU_PDN);
imx_gpc_set_arm_power_in_lpm(arm_power_off);

for (i = 0; i < IMR_NUM; i++) {
gpc_saved_imrs[i] = readl_relaxed(reg_imr1 + i * 4);
Expand All @@ -48,7 +69,7 @@ void imx_gpc_post_resume(void)
int i;

/* Keep ARM core powered on for other low-power modes */
writel_relaxed(0x0, gpc_base + GPC_PGC_CPU_PDN);
imx_gpc_set_arm_power_in_lpm(false);

for (i = 0; i < IMR_NUM; i++)
writel_relaxed(gpc_saved_imrs[i], reg_imr1 + i * 4);
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/mach-imx/mach-imx6q.c
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ static void __init imx6q_opp_check_speed_grading(struct device *cpu_dev)
if (dev_pm_opp_disable(cpu_dev, 852000000))
pr_warn("failed to disable 852 MHz OPP\n");
}

iounmap(base);
put_node:
of_node_put(np);
}
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/mach-imx/mach-imx6sx.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ static void __init imx6sx_init_irq(void)

static void __init imx6sx_init_late(void)
{
imx6q_cpuidle_init();
imx6sx_cpuidle_init();

if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ))
platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0);
Expand Down
5 changes: 4 additions & 1 deletion arch/arm/mach-imx/mach-vf610.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
#include <asm/hardware/cache-l2x0.h>

static const char * const vf610_dt_compat[] __initconst = {
"fsl,vf500",
"fsl,vf510",
"fsl,vf600",
"fsl,vf610",
NULL,
};

DT_MACHINE_START(VYBRID_VF610, "Freescale Vybrid VF610 (Device Tree)")
DT_MACHINE_START(VYBRID_VF610, "Freescale Vybrid VF5xx/VF6xx (Device Tree)")
.l2c_aux_val = 0,
.l2c_aux_mask = ~0,
.dt_compat = vf610_dt_compat,
Expand Down
Loading

0 comments on commit 085dd64

Please sign in to comment.