diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-12 11:51:39 -0800 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-12 11:51:39 -0800 | 
| commit | d01e4afdbb65e030fd6f1f96c30a558e2eb0f279 (patch) | |
| tree | 02ef82b2740cf93a98199eded5ef765fa6e03052 /arch/arm/plat-omap/dmtimer.c | |
| parent | 8287361abca36504da813638310d2547469283eb (diff) | |
| parent | 794b175fc0c0c4844dbb7b137a73bbfd01f6c608 (diff) | |
| download | olio-linux-3.10-d01e4afdbb65e030fd6f1f96c30a558e2eb0f279.tar.xz olio-linux-3.10-d01e4afdbb65e030fd6f1f96c30a558e2eb0f279.zip  | |
Merge tag 'cleanup' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC cleanups on various subarchitectures from Olof Johansson:
 "Cleanup patches for various ARM platforms and some of their associated
  drivers.  There's also a branch in here that enables Freescale i.MX to
  be part of the multiplatform support -- the first "big" SoC that is
  moved over (more multiplatform work comes in a separate branch later
  during the merge window)."
Conflicts fixed as per Olof, including a silent semantic one in
arch/arm/mach-omap2/board-generic.c (omap_prcm_restart() was renamed to
omap3xxx_restart(), and a new user of the old name was added).
* tag 'cleanup' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (189 commits)
  ARM: omap: fix typo on timer cleanup
  ARM: EXYNOS: Remove unused regs-mem.h file
  ARM: EXYNOS: Remove unused non-dt support for dwmci controller
  ARM: Kirkwood: Use hw_pci.ops instead of hw_pci.scan
  ARM: OMAP3: cm-t3517: use GPTIMER for system clock
  ARM: OMAP2+: timer: remove CONFIG_OMAP_32K_TIMER
  ARM: SAMSUNG: use devm_ functions for ADC driver
  ARM: EXYNOS: no duplicate mask/unmask in eint0_15
  ARM: S3C24XX: SPI clock channel setup is fixed for S3C2443
  ARM: EXYNOS: Remove i2c0 resource information and setting of device names
  ARM: Kirkwood: checkpatch cleanups
  ARM: Kirkwood: Fix sparse warnings.
  ARM: Kirkwood: Remove unused includes
  ARM: kirkwood: cleanup lsxl board includes
  ARM: integrator: use BUG_ON where possible
  ARM: integrator: push down SC dependencies
  ARM: integrator: delete static UART1 mapping
  ARM: integrator: delete SC mapping on the CP
  ARM: integrator: remove static CP syscon mapping
  ARM: integrator: remove static AP syscon mapping
  ...
Diffstat (limited to 'arch/arm/plat-omap/dmtimer.c')
| -rw-r--r-- | arch/arm/plat-omap/dmtimer.c | 218 | 
1 files changed, 164 insertions, 54 deletions
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c index 82231a75abd..89585c29355 100644 --- a/arch/arm/plat-omap/dmtimer.c +++ b/arch/arm/plat-omap/dmtimer.c @@ -35,11 +35,16 @@   * 675 Mass Ave, Cambridge, MA 02139, USA.   */ +#include <linux/clk.h>  #include <linux/module.h>  #include <linux/io.h>  #include <linux/device.h>  #include <linux/err.h>  #include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/platform_data/dmtimer-omap.h>  #include <plat/dmtimer.h> @@ -81,10 +86,6 @@ static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg,  static void omap_timer_restore_context(struct omap_dm_timer *timer)  { -	if (timer->revision == 1) -		__raw_writel(timer->context.tistat, timer->sys_stat); - -	__raw_writel(timer->context.tisr, timer->irq_stat);  	omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG,  				timer->context.twer);  	omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, @@ -100,39 +101,38 @@ static void omap_timer_restore_context(struct omap_dm_timer *timer)  				timer->context.tclr);  } -static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer) +static int omap_dm_timer_reset(struct omap_dm_timer *timer)  { -	int c; +	u32 l, timeout = 100000; -	if (!timer->sys_stat) -		return; +	if (timer->revision != 1) +		return -EINVAL; -	c = 0; -	while (!(__raw_readl(timer->sys_stat) & 1)) { -		c++; -		if (c > 100000) { -			printk(KERN_ERR "Timer failed to reset\n"); -			return; -		} -	} -} +	omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06); -static void omap_dm_timer_reset(struct omap_dm_timer *timer) -{ -	omap_dm_timer_enable(timer); -	if (timer->pdev->id != 1) { -		omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06); -		omap_dm_timer_wait_for_reset(timer); +	do { +		l = __omap_dm_timer_read(timer, +					 OMAP_TIMER_V1_SYS_STAT_OFFSET, 0); +	} while (!l && timeout--); + +	if (!timeout) { +		dev_err(&timer->pdev->dev, "Timer failed to reset\n"); +		return -ETIMEDOUT;  	} -	__omap_dm_timer_reset(timer, 0, 0); -	omap_dm_timer_disable(timer); -	timer->posted = 1; +	/* Configure timer for smart-idle mode */ +	l = __omap_dm_timer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET, 0); +	l |= 0x2 << 0x3; +	__omap_dm_timer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, l, 0); + +	timer->posted = 0; + +	return 0;  } -int omap_dm_timer_prepare(struct omap_dm_timer *timer) +static int omap_dm_timer_prepare(struct omap_dm_timer *timer)  { -	int ret; +	int rc;  	/*  	 * FIXME: OMAP1 devices do not use the clock framework for dmtimers so @@ -147,13 +147,20 @@ int omap_dm_timer_prepare(struct omap_dm_timer *timer)  		}  	} -	if (timer->capability & OMAP_TIMER_NEEDS_RESET) -		omap_dm_timer_reset(timer); +	omap_dm_timer_enable(timer); + +	if (timer->capability & OMAP_TIMER_NEEDS_RESET) { +		rc = omap_dm_timer_reset(timer); +		if (rc) { +			omap_dm_timer_disable(timer); +			return rc; +		} +	} -	ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); +	__omap_dm_timer_enable_posted(timer); +	omap_dm_timer_disable(timer); -	timer->posted = 1; -	return ret; +	return omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ);  }  static inline u32 omap_dm_timer_reserved_systimer(int id) @@ -209,6 +216,13 @@ struct omap_dm_timer *omap_dm_timer_request_specific(int id)  	unsigned long flags;  	int ret = 0; +	/* Requesting timer by ID is not supported when device tree is used */ +	if (of_have_populated_dt()) { +		pr_warn("%s: Please use omap_dm_timer_request_by_cap()\n", +			__func__); +		return NULL; +	} +  	spin_lock_irqsave(&dm_timer_lock, flags);  	list_for_each_entry(t, &omap_timer_list, node) {  		if (t->pdev->id == id && !t->reserved) { @@ -234,6 +248,58 @@ struct omap_dm_timer *omap_dm_timer_request_specific(int id)  }  EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific); +/** + * omap_dm_timer_request_by_cap - Request a timer by capability + * @cap:	Bit mask of capabilities to match + * + * Find a timer based upon capabilities bit mask. Callers of this function + * should use the definitions found in the plat/dmtimer.h file under the + * comment "timer capabilities used in hwmod database". Returns pointer to + * timer handle on success and a NULL pointer on failure. + */ +struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap) +{ +	struct omap_dm_timer *timer = NULL, *t; +	unsigned long flags; + +	if (!cap) +		return NULL; + +	spin_lock_irqsave(&dm_timer_lock, flags); +	list_for_each_entry(t, &omap_timer_list, node) { +		if ((!t->reserved) && ((t->capability & cap) == cap)) { +			/* +			 * If timer is not NULL, we have already found one timer +			 * but it was not an exact match because it had more +			 * capabilites that what was required. Therefore, +			 * unreserve the last timer found and see if this one +			 * is a better match. +			 */ +			if (timer) +				timer->reserved = 0; + +			timer = t; +			timer->reserved = 1; + +			/* Exit loop early if we find an exact match */ +			if (t->capability == cap) +				break; +		} +	} +	spin_unlock_irqrestore(&dm_timer_lock, flags); + +	if (timer && omap_dm_timer_prepare(timer)) { +		timer->reserved = 0; +		timer = NULL; +	} + +	if (!timer) +		pr_debug("%s: timer request failed!\n", __func__); + +	return timer; +} +EXPORT_SYMBOL_GPL(omap_dm_timer_request_by_cap); +  int omap_dm_timer_free(struct omap_dm_timer *timer)  {  	if (unlikely(!timer)) @@ -388,7 +454,6 @@ int omap_dm_timer_stop(struct omap_dm_timer *timer)  	 */  	timer->context.tclr =  			omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); -	timer->context.tisr = __raw_readl(timer->irq_stat);  	omap_dm_timer_disable(timer);  	return 0;  } @@ -398,7 +463,7 @@ int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)  {  	int ret;  	char *parent_name = NULL; -	struct clk *fclk, *parent; +	struct clk *parent;  	struct dmtimer_platform_data *pdata;  	if (unlikely(!timer)) @@ -414,14 +479,11 @@ int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)  	 * use the clock framework to set the parent clock. To be removed  	 * once OMAP1 migrated to using clock framework for dmtimers  	 */ -	if (pdata->set_timer_src) +	if (pdata && pdata->set_timer_src)  		return pdata->set_timer_src(timer->pdev, source); -	fclk = clk_get(&timer->pdev->dev, "fck"); -	if (IS_ERR_OR_NULL(fclk)) { -		pr_err("%s: fck not found\n", __func__); +	if (!timer->fclk)  		return -EINVAL; -	}  	switch (source) {  	case OMAP_TIMER_SRC_SYS_CLK: @@ -440,18 +502,15 @@ int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)  	parent = clk_get(&timer->pdev->dev, parent_name);  	if (IS_ERR_OR_NULL(parent)) {  		pr_err("%s: %s not found\n", __func__, parent_name); -		ret = -EINVAL; -		goto out; +		return -EINVAL;  	} -	ret = clk_set_parent(fclk, parent); +	ret = clk_set_parent(timer->fclk, parent);  	if (IS_ERR_VALUE(ret))  		pr_err("%s: failed to set %s as parent\n", __func__,  			parent_name);  	clk_put(parent); -out: -	clk_put(fclk);  	return ret;  } @@ -534,8 +593,8 @@ int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,  		l |= OMAP_TIMER_CTRL_CE;  	else  		l &= ~OMAP_TIMER_CTRL_CE; -	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);  	omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match); +	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);  	/* Save the context */  	timer->context.tclr = l; @@ -611,6 +670,37 @@ int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,  }  EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable); +/** + * omap_dm_timer_set_int_disable - disable timer interrupts + * @timer:	pointer to timer handle + * @mask:	bit mask of interrupts to be disabled + * + * Disables the specified timer interrupts for a timer. + */ +int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask) +{ +	u32 l = mask; + +	if (unlikely(!timer)) +		return -EINVAL; + +	omap_dm_timer_enable(timer); + +	if (timer->revision == 1) +		l = __raw_readl(timer->irq_ena) & ~mask; + +	__raw_writel(l, timer->irq_dis); +	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG) & ~mask; +	omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, l); + +	/* Save the context */ +	timer->context.tier &= ~mask; +	timer->context.twer &= ~mask; +	omap_dm_timer_disable(timer); +	return 0; +} +EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_disable); +  unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)  {  	unsigned int l; @@ -632,8 +722,7 @@ int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)  		return -EINVAL;  	__omap_dm_timer_write_status(timer, value); -	/* Save the context */ -	timer->context.tisr = value; +  	return 0;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_write_status); @@ -696,7 +785,7 @@ static int __devinit omap_dm_timer_probe(struct platform_device *pdev)  	struct device *dev = &pdev->dev;  	struct dmtimer_platform_data *pdata = pdev->dev.platform_data; -	if (!pdata) { +	if (!pdata && !dev->of_node) {  		dev_err(dev, "%s: no platform data.\n", __func__);  		return -ENODEV;  	} @@ -725,12 +814,25 @@ static int __devinit omap_dm_timer_probe(struct platform_device *pdev)  		return -ENOMEM;  	} -	timer->id = pdev->id; +	if (dev->of_node) { +		if (of_find_property(dev->of_node, "ti,timer-alwon", NULL)) +			timer->capability |= OMAP_TIMER_ALWON; +		if (of_find_property(dev->of_node, "ti,timer-dsp", NULL)) +			timer->capability |= OMAP_TIMER_HAS_DSP_IRQ; +		if (of_find_property(dev->of_node, "ti,timer-pwm", NULL)) +			timer->capability |= OMAP_TIMER_HAS_PWM; +		if (of_find_property(dev->of_node, "ti,timer-secure", NULL)) +			timer->capability |= OMAP_TIMER_SECURE; +	} else { +		timer->id = pdev->id; +		timer->errata = pdata->timer_errata; +		timer->capability = pdata->timer_capability; +		timer->reserved = omap_dm_timer_reserved_systimer(timer->id); +		timer->get_context_loss_count = pdata->get_context_loss_count; +	} +  	timer->irq = irq->start; -	timer->reserved = omap_dm_timer_reserved_systimer(timer->id);  	timer->pdev = pdev; -	timer->capability = pdata->timer_capability; -	timer->get_context_loss_count = pdata->get_context_loss_count;  	/* Skip pm_runtime_enable for OMAP1 */  	if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) { @@ -770,7 +872,8 @@ static int __devexit omap_dm_timer_remove(struct platform_device *pdev)  	spin_lock_irqsave(&dm_timer_lock, flags);  	list_for_each_entry(timer, &omap_timer_list, node) -		if (timer->pdev->id == pdev->id) { +		if (!strcmp(dev_name(&timer->pdev->dev), +			    dev_name(&pdev->dev))) {  			list_del(&timer->node);  			ret = 0;  			break; @@ -780,11 +883,18 @@ static int __devexit omap_dm_timer_remove(struct platform_device *pdev)  	return ret;  } +static const struct of_device_id omap_timer_match[] = { +	{ .compatible = "ti,omap2-timer", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, omap_timer_match); +  static struct platform_driver omap_dm_timer_driver = {  	.probe  = omap_dm_timer_probe,  	.remove = __devexit_p(omap_dm_timer_remove),  	.driver = {  		.name   = "omap_timer", +		.of_match_table = of_match_ptr(omap_timer_match),  	},  };  |