diff options
Diffstat (limited to 'drivers/clocksource/sh_tmu.c')
| -rw-r--r-- | drivers/clocksource/sh_tmu.c | 80 | 
1 files changed, 65 insertions, 15 deletions
diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 81b0239718e..0cc4add8827 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -45,6 +45,7 @@ struct sh_tmu_priv {  	struct clock_event_device ced;  	struct clocksource cs;  	bool cs_enabled; +	unsigned int enable_count;  };  static DEFINE_RAW_SPINLOCK(sh_tmu_lock); @@ -109,7 +110,7 @@ static void sh_tmu_start_stop_ch(struct sh_tmu_priv *p, int start)  	raw_spin_unlock_irqrestore(&sh_tmu_lock, flags);  } -static int sh_tmu_enable(struct sh_tmu_priv *p) +static int __sh_tmu_enable(struct sh_tmu_priv *p)  {  	int ret; @@ -137,7 +138,18 @@ static int sh_tmu_enable(struct sh_tmu_priv *p)  	return 0;  } -static void sh_tmu_disable(struct sh_tmu_priv *p) +static int sh_tmu_enable(struct sh_tmu_priv *p) +{ +	if (p->enable_count++ > 0) +		return 0; + +	pm_runtime_get_sync(&p->pdev->dev); +	dev_pm_syscore_device(&p->pdev->dev, true); + +	return __sh_tmu_enable(p); +} + +static void __sh_tmu_disable(struct sh_tmu_priv *p)  {  	/* disable channel */  	sh_tmu_start_stop_ch(p, 0); @@ -149,6 +161,20 @@ static void sh_tmu_disable(struct sh_tmu_priv *p)  	clk_disable(p->clk);  } +static void sh_tmu_disable(struct sh_tmu_priv *p) +{ +	if (WARN_ON(p->enable_count == 0)) +		return; + +	if (--p->enable_count > 0) +		return; + +	__sh_tmu_disable(p); + +	dev_pm_syscore_device(&p->pdev->dev, false); +	pm_runtime_put(&p->pdev->dev); +} +  static void sh_tmu_set_next(struct sh_tmu_priv *p, unsigned long delta,  			    int periodic)  { @@ -205,11 +231,15 @@ static int sh_tmu_clocksource_enable(struct clocksource *cs)  	struct sh_tmu_priv *p = cs_to_sh_tmu(cs);  	int ret; +	if (WARN_ON(p->cs_enabled)) +		return 0; +  	ret = sh_tmu_enable(p);  	if (!ret) {  		__clocksource_updatefreq_hz(cs, p->rate);  		p->cs_enabled = true;  	} +  	return ret;  } @@ -217,7 +247,8 @@ static void sh_tmu_clocksource_disable(struct clocksource *cs)  {  	struct sh_tmu_priv *p = cs_to_sh_tmu(cs); -	WARN_ON(!p->cs_enabled); +	if (WARN_ON(!p->cs_enabled)) +		return;  	sh_tmu_disable(p);  	p->cs_enabled = false; @@ -227,19 +258,26 @@ static void sh_tmu_clocksource_suspend(struct clocksource *cs)  {  	struct sh_tmu_priv *p = cs_to_sh_tmu(cs); -	if (p->cs_enabled) -		sh_tmu_disable(p); +	if (!p->cs_enabled) +		return; -	pm_genpd_syscore_poweroff(&p->pdev->dev); +	if (--p->enable_count == 0) { +		__sh_tmu_disable(p); +		pm_genpd_syscore_poweroff(&p->pdev->dev); +	}  }  static void sh_tmu_clocksource_resume(struct clocksource *cs)  {  	struct sh_tmu_priv *p = cs_to_sh_tmu(cs); -	pm_genpd_syscore_poweron(&p->pdev->dev); -	if (p->cs_enabled) -		sh_tmu_enable(p); +	if (!p->cs_enabled) +		return; + +	if (p->enable_count++ == 0) { +		pm_genpd_syscore_poweron(&p->pdev->dev); +		__sh_tmu_enable(p); +	}  }  static int sh_tmu_register_clocksource(struct sh_tmu_priv *p, @@ -434,6 +472,8 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev)  		ret = PTR_ERR(p->clk);  		goto err1;  	} +	p->cs_enabled = false; +	p->enable_count = 0;  	return sh_tmu_register(p, (char *)dev_name(&p->pdev->dev),  			       cfg->clockevent_rating, @@ -447,18 +487,17 @@ static int sh_tmu_setup(struct sh_tmu_priv *p, struct platform_device *pdev)  static int __devinit sh_tmu_probe(struct platform_device *pdev)  {  	struct sh_tmu_priv *p = platform_get_drvdata(pdev); +	struct sh_timer_config *cfg = pdev->dev.platform_data;  	int ret;  	if (!is_early_platform_device(pdev)) { -		struct sh_timer_config *cfg = pdev->dev.platform_data; - -		if (cfg->clocksource_rating || cfg->clockevent_rating) -			dev_pm_syscore_device(&pdev->dev, true); +		pm_runtime_set_active(&pdev->dev); +		pm_runtime_enable(&pdev->dev);  	}  	if (p) {  		dev_info(&pdev->dev, "kept as earlytimer\n"); -		return 0; +		goto out;  	}  	p = kmalloc(sizeof(*p), GFP_KERNEL); @@ -471,8 +510,19 @@ static int __devinit sh_tmu_probe(struct platform_device *pdev)  	if (ret) {  		kfree(p);  		platform_set_drvdata(pdev, NULL); +		pm_runtime_idle(&pdev->dev); +		return ret;  	} -	return ret; +	if (is_early_platform_device(pdev)) +		return 0; + + out: +	if (cfg->clockevent_rating || cfg->clocksource_rating) +		pm_runtime_irq_safe(&pdev->dev); +	else +		pm_runtime_idle(&pdev->dev); + +	return 0;  }  static int __devexit sh_tmu_remove(struct platform_device *pdev)  |