Add a .flag property to the struct clk. The flag can have the following information: The clock is enabled at the boot time. The clock cannot be disabled. The clock is enabled at the boot time and cannot be disabled. The clock is disabled at the boot time. Note that both CLKFLAGS_BOOT_ON and CLKFLAGS_CANNOT_DISABLE can override CLKFLAGS_BOOT_OFF. Please stop using this clock. When a DEPRECATED clock is clk_get'd, clk_get function will printk a warning message. The previous patch related with powerdomain / blcok-gating control requires this patch for the stability related with clocks that are turned on at the boot time. Note that clocks without both BOOT_ON and BOOT_OFF keep the previous states; however, powerdomain / block-gating controls do NOT have any information about the states of such clocks, which in turn, may incur instable kernel behaviors. For example, a powerdomain may be turned off while a clock of the domain is still being used. With the flag support feature, we highly recommend to define .flag fields fully for clocks related to powerdomain and block-gating. Clocks not connected to powerdomain and block-gating are ok without flag field. Signed-off-by: MyungJoo Ham <myungjoo.ham@xxxxxxxxxxx> Signed-off-by: Kyungmin Park <kyungmin.park@xxxxxxxxxxx> --- arch/arm/plat-samsung/clock.c | 70 ++++++++++++++++++++++++++- arch/arm/plat-samsung/include/plat/clock.h | 12 +++++ 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/arch/arm/plat-samsung/clock.c b/arch/arm/plat-samsung/clock.c index f8a30f7..9ff9d63 100644 --- a/arch/arm/plat-samsung/clock.c +++ b/arch/arm/plat-samsung/clock.c @@ -103,6 +103,14 @@ struct clk *clk_get(struct device *dev, const char *id) } spin_unlock(&clocks_lock); + + if (!IS_ERR(clk) && clk) + if (clk->flags & CLKFLAGS_DEPRECATED) + printk(KERN_WARNING "[%s:%d] clk %s:%d is deprecated. " + "Please reconsider using it.\n", + __FILE__, __LINE__, clk->name, + clk->id); + return clk; } @@ -169,7 +177,25 @@ void clk_disable(struct clk *clk) spin_lock(&clocks_lock); if ((--clk->usage) == 0) { - (clk->enable)(clk, 0); +#ifdef CONFIG_SAMSUNG_POWERDOMAIN + if ((clk->flags | CLKFLAGS_BOOT_ON) && + !(clk->flags | CLKFLAGS_CANNOT_DISABLE)) { + /* BOOT_ON became NO EFFECT. Let PD/BD be able + * to turn themselves off */ + + if (clk->pd) + clk->pd->num_clks_boot_on--; +#ifdef CONFIG_SAMSUNG_BLOCKGATING + if (clk->bd) + clk->bd->num_clks_boot_on--; +#endif + clk->flags &= ~CLKFLAGS_BOOT_ON; + } +#endif + + if (clk->enable && !(clk->flags | CLKFLAGS_CANNOT_DISABLE)) + (clk->enable)(clk, 0); + #ifdef CONFIG_SAMSUNG_POWERDOMAIN if (clk->pd) { if (clk->pd->ref_count == 1 && @@ -193,7 +219,9 @@ void clk_disable(struct clk *clk) } spin_unlock(&clocks_lock); - clk_disable(clk->parent); + + if (!(clk->flags | CLKFLAGS_CANNOT_DISABLE)) + clk_disable(clk->parent); } @@ -370,6 +398,13 @@ struct clk s3c24xx_uclk = { */ int s3c24xx_register_clock(struct clk *clk) { + int ret = 0; + + if (clk == NULL) + return -EINVAL; + if (clk->name == NULL) + return -EINVAL; + if (clk->enable == NULL) clk->enable = clk_null_enable; @@ -382,6 +417,21 @@ int s3c24xx_register_clock(struct clk *clk) list_add(&clk->list, &clocks); spin_unlock(&clocks_lock); + if (clk->flags & CLKFLAGS_BOOT_ON) { + /* Use clk_enable, not clk->enable as clk's parent is + * also required to be turned on */ + clk_enable(clk); + +#ifdef CONFIG_SAMSUNG_POWERDOMAIN + if (clk->pd) + clk->pd->num_clks_boot_on++; +#endif +#ifdef CONFIG_SAMSUNG_BLOCKGATING + if (clk->bd) + clk->bd->num_clks_boot_on++; +#endif + } + #ifdef CONFIG_SAMSUNG_POWERDOMAIN if (clk->pd) { spin_lock(&clocks_lock); @@ -397,7 +447,21 @@ int s3c24xx_register_clock(struct clk *clk) } #endif - return 0; + if (clk->flags & CLKFLAGS_BOOT_OFF) { + if ((clk->flags & CLKFLAGS_BOOT_ON) || + (clk->flags & CLKFLAGS_CANNOT_DISABLE)) { + printk(KERN_WARNING "[%s:%d] clk %s:%d has incompatible" + " initilization flags: %x.\n", + __FILE__, __LINE__, clk->name, clk->id, + clk->flags); + ret = -EINVAL; + } else + /* Do NOT use clk_disable(clk) because clk_disable + * may disable clk's parent */ + ret = (clk->enable)(clk, 0); + } + + return ret; } /** diff --git a/arch/arm/plat-samsung/include/plat/clock.h b/arch/arm/plat-samsung/include/plat/clock.h index bf851e1..0f7d556 100644 --- a/arch/arm/plat-samsung/include/plat/clock.h +++ b/arch/arm/plat-samsung/include/plat/clock.h @@ -59,6 +59,17 @@ struct clk_ops { int (*set_parent)(struct clk *c, struct clk *parent); }; +/* Note that when BOOT_ON and OFF are both not set, the kernel + * keeps the state initialized by the boot-loader or cpu. + */ +#define CLKFLAGS_BOOT_ON (0x1) +#define CLKFLAGS_CANNOT_DISABLE (0x2) +#define CLKFLAGS_ALWAYS_ON (CLKFLAGS_BOOT_ON | CLKFLAGS_CANNOT_DISABLE) +#define CLKFLAGS_BOOT_OFF (0x4) /* Force off when boot */ +#define CLKFLAGS_DEPRECATED (0x8) /* Warn when clk_get'd */ +/* Note that CLKFLAGS_BOOT_ON and CLKFLAGS_CANNOT_DISABLE overrides + * CLKFLAGS_BOOT_OFF */ + struct clk { struct list_head list; struct module *owner; @@ -79,6 +90,7 @@ struct clk { struct powerdomain *bd; struct list_head blockgating_list; #endif + unsigned int flags; }; /* other clocks which may be registered by board support */ -- 1.6.3.3 -- To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html