diff options
Diffstat (limited to 'drivers/rtc/rtc-sa1100.c')
| -rw-r--r-- | drivers/rtc/rtc-sa1100.c | 399 | 
1 files changed, 160 insertions, 239 deletions
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index e4a44b64170..b6e1ca08c2c 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -23,100 +23,66 @@  #include <linux/platform_device.h>  #include <linux/module.h> +#include <linux/clk.h>  #include <linux/rtc.h>  #include <linux/init.h>  #include <linux/fs.h>  #include <linux/interrupt.h> +#include <linux/slab.h>  #include <linux/string.h> +#include <linux/of.h>  #include <linux/pm.h>  #include <linux/bitops.h> +#include <linux/io.h>  #include <mach/hardware.h> -#include <asm/irq.h> +#include <mach/irqs.h> -#ifdef CONFIG_ARCH_PXA +#if defined(CONFIG_ARCH_PXA) || defined(CONFIG_ARCH_MMP)  #include <mach/regs-rtc.h> -#include <mach/regs-ost.h>  #endif -#define RTC_DEF_DIVIDER		32768 - 1 +#define RTC_DEF_DIVIDER		(32768 - 1)  #define RTC_DEF_TRIM		0 - -static unsigned long rtc_freq = 1024; -static unsigned long timer_freq; -static struct rtc_time rtc_alarm; -static DEFINE_SPINLOCK(sa1100_rtc_lock); - -static inline int rtc_periodic_alarm(struct rtc_time *tm) -{ -	return  (tm->tm_year == -1) || -		((unsigned)tm->tm_mon >= 12) || -		((unsigned)(tm->tm_mday - 1) >= 31) || -		((unsigned)tm->tm_hour > 23) || -		((unsigned)tm->tm_min > 59) || -		((unsigned)tm->tm_sec > 59); -} - -/* - * Calculate the next alarm time given the requested alarm time mask - * and the current time. - */ -static void rtc_next_alarm_time(struct rtc_time *next, struct rtc_time *now, struct rtc_time *alrm) -{ -	unsigned long next_time; -	unsigned long now_time; - -	next->tm_year = now->tm_year; -	next->tm_mon = now->tm_mon; -	next->tm_mday = now->tm_mday; -	next->tm_hour = alrm->tm_hour; -	next->tm_min = alrm->tm_min; -	next->tm_sec = alrm->tm_sec; - -	rtc_tm_to_time(now, &now_time); -	rtc_tm_to_time(next, &next_time); - -	if (next_time < now_time) { -		/* Advance one day */ -		next_time += 60 * 60 * 24; -		rtc_time_to_tm(next_time, next); -	} -} - -static int rtc_update_alarm(struct rtc_time *alrm) -{ -	struct rtc_time alarm_tm, now_tm; -	unsigned long now, time; -	int ret; - -	do { -		now = RCNR; -		rtc_time_to_tm(now, &now_tm); -		rtc_next_alarm_time(&alarm_tm, &now_tm, alrm); -		ret = rtc_tm_to_time(&alarm_tm, &time); -		if (ret != 0) -			break; - -		RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL); -		RTAR = time; -	} while (now != RCNR); - -	return ret; -} +#define RTC_FREQ		1024 + +struct sa1100_rtc { +	spinlock_t		lock; +	int			irq_1hz; +	int			irq_alarm; +	struct rtc_device	*rtc; +	struct clk		*clk; +};  static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id)  { -	struct platform_device *pdev = to_platform_device(dev_id); -	struct rtc_device *rtc = platform_get_drvdata(pdev); +	struct sa1100_rtc *info = dev_get_drvdata(dev_id); +	struct rtc_device *rtc = info->rtc;  	unsigned int rtsr;  	unsigned long events = 0; -	spin_lock(&sa1100_rtc_lock); +	spin_lock(&info->lock);  	rtsr = RTSR;  	/* clear interrupt sources */  	RTSR = 0; -	RTSR = (RTSR_AL | RTSR_HZ) & (rtsr >> 2); +	/* Fix for a nasty initialization problem the in SA11xx RTSR register. +	 * See also the comments in sa1100_rtc_probe(). */ +	if (rtsr & (RTSR_ALE | RTSR_HZE)) { +		/* This is the original code, before there was the if test +		 * above. This code does not clear interrupts that were not +		 * enabled. */ +		RTSR = (RTSR_AL | RTSR_HZ) & (rtsr >> 2); +	} else { +		/* For some reason, it is possible to enter this routine +		 * without interruptions enabled, it has been tested with +		 * several units (Bug in SA11xx chip?). +		 * +		 * This situation leads to an infinite "loop" of interrupt +		 * routine calling and as a result the processor seems to +		 * lock on its first call to open(). */ +		RTSR = RTSR_AL | RTSR_HZ; +	}  	/* clear alarm interrupt if it has occurred */  	if (rtsr & RTSR_AL) @@ -131,153 +97,62 @@ static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id)  	rtc_update_irq(rtc, 1, events); -	if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm)) -		rtc_update_alarm(&rtc_alarm); - -	spin_unlock(&sa1100_rtc_lock); - -	return IRQ_HANDLED; -} - -static int rtc_timer1_count; - -static irqreturn_t timer1_interrupt(int irq, void *dev_id) -{ -	struct platform_device *pdev = to_platform_device(dev_id); -	struct rtc_device *rtc = platform_get_drvdata(pdev); - -	/* -	 * If we match for the first time, rtc_timer1_count will be 1. -	 * Otherwise, we wrapped around (very unlikely but -	 * still possible) so compute the amount of missed periods. -	 * The match reg is updated only when the data is actually retrieved -	 * to avoid unnecessary interrupts. -	 */ -	OSSR = OSSR_M1;	/* clear match on timer1 */ - -	rtc_update_irq(rtc, rtc_timer1_count, RTC_PF | RTC_IRQF); - -	if (rtc_timer1_count == 1) -		rtc_timer1_count = (rtc_freq * ((1 << 30) / (timer_freq >> 2))); +	spin_unlock(&info->lock);  	return IRQ_HANDLED;  } -static int sa1100_rtc_read_callback(struct device *dev, int data) -{ -	if (data & RTC_PF) { -		/* interpolate missed periods and set match for the next */ -		unsigned long period = timer_freq / rtc_freq; -		unsigned long oscr = OSCR; -		unsigned long osmr1 = OSMR1; -		unsigned long missed = (oscr - osmr1)/period; -		data += missed << 8; -		OSSR = OSSR_M1;	/* clear match on timer 1 */ -		OSMR1 = osmr1 + (missed + 1)*period; -		/* Ensure we didn't miss another match in the mean time. -		 * Here we compare (match - OSCR) 8 instead of 0 -- -		 * see comment in pxa_timer_interrupt() for explanation. -		 */ -		while( (signed long)((osmr1 = OSMR1) - OSCR) <= 8 ) { -			data += 0x100; -			OSSR = OSSR_M1;	/* clear match on timer 1 */ -			OSMR1 = osmr1 + period; -		} -	} -	return data; -} -  static int sa1100_rtc_open(struct device *dev)  { +	struct sa1100_rtc *info = dev_get_drvdata(dev); +	struct rtc_device *rtc = info->rtc;  	int ret; -	ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, IRQF_DISABLED, -				"rtc 1Hz", dev); +	ret = request_irq(info->irq_1hz, sa1100_rtc_interrupt, 0, "rtc 1Hz", dev);  	if (ret) { -		dev_err(dev, "IRQ %d already in use.\n", IRQ_RTC1Hz); +		dev_err(dev, "IRQ %d already in use.\n", info->irq_1hz);  		goto fail_ui;  	} -	ret = request_irq(IRQ_RTCAlrm, sa1100_rtc_interrupt, IRQF_DISABLED, -				"rtc Alrm", dev); +	ret = request_irq(info->irq_alarm, sa1100_rtc_interrupt, 0, "rtc Alrm", dev);  	if (ret) { -		dev_err(dev, "IRQ %d already in use.\n", IRQ_RTCAlrm); +		dev_err(dev, "IRQ %d already in use.\n", info->irq_alarm);  		goto fail_ai;  	} -	ret = request_irq(IRQ_OST1, timer1_interrupt, IRQF_DISABLED, -				"rtc timer", dev); -	if (ret) { -		dev_err(dev, "IRQ %d already in use.\n", IRQ_OST1); -		goto fail_pi; -	} +	rtc->max_user_freq = RTC_FREQ; +	rtc_irq_set_freq(rtc, NULL, RTC_FREQ); +  	return 0; - fail_pi: -	free_irq(IRQ_RTCAlrm, dev);   fail_ai: -	free_irq(IRQ_RTC1Hz, dev); +	free_irq(info->irq_1hz, dev);   fail_ui: +	clk_disable_unprepare(info->clk);  	return ret;  }  static void sa1100_rtc_release(struct device *dev)  { -	spin_lock_irq(&sa1100_rtc_lock); +	struct sa1100_rtc *info = dev_get_drvdata(dev); + +	spin_lock_irq(&info->lock);  	RTSR = 0; -	OIER &= ~OIER_E1; -	OSSR = OSSR_M1; -	spin_unlock_irq(&sa1100_rtc_lock); +	spin_unlock_irq(&info->lock); -	free_irq(IRQ_OST1, dev); -	free_irq(IRQ_RTCAlrm, dev); -	free_irq(IRQ_RTC1Hz, dev); +	free_irq(info->irq_alarm, dev); +	free_irq(info->irq_1hz, dev);  } - -static int sa1100_rtc_ioctl(struct device *dev, unsigned int cmd, -		unsigned long arg) +static int sa1100_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)  { -	switch(cmd) { -	case RTC_AIE_OFF: -		spin_lock_irq(&sa1100_rtc_lock); -		RTSR &= ~RTSR_ALE; -		spin_unlock_irq(&sa1100_rtc_lock); -		return 0; -	case RTC_AIE_ON: -		spin_lock_irq(&sa1100_rtc_lock); +	struct sa1100_rtc *info = dev_get_drvdata(dev); + +	spin_lock_irq(&info->lock); +	if (enabled)  		RTSR |= RTSR_ALE; -		spin_unlock_irq(&sa1100_rtc_lock); -		return 0; -	case RTC_UIE_OFF: -		spin_lock_irq(&sa1100_rtc_lock); -		RTSR &= ~RTSR_HZE; -		spin_unlock_irq(&sa1100_rtc_lock); -		return 0; -	case RTC_UIE_ON: -		spin_lock_irq(&sa1100_rtc_lock); -		RTSR |= RTSR_HZE; -		spin_unlock_irq(&sa1100_rtc_lock); -		return 0; -	case RTC_PIE_OFF: -		spin_lock_irq(&sa1100_rtc_lock); -		OIER &= ~OIER_E1; -		spin_unlock_irq(&sa1100_rtc_lock); -		return 0; -	case RTC_PIE_ON: -		spin_lock_irq(&sa1100_rtc_lock); -		OSMR1 = timer_freq / rtc_freq + OSCR; -		OIER |= OIER_E1; -		rtc_timer1_count = 1; -		spin_unlock_irq(&sa1100_rtc_lock); -		return 0; -	case RTC_IRQP_READ: -		return put_user(rtc_freq, (unsigned long *)arg); -	case RTC_IRQP_SET: -		if (arg < 1 || arg > timer_freq) -			return -EINVAL; -		rtc_freq = arg; -		return 0; -	} -	return -ENOIOCTLCMD; +	else +		RTSR &= ~RTSR_ALE; +	spin_unlock_irq(&info->lock); +	return 0;  }  static int sa1100_rtc_read_time(struct device *dev, struct rtc_time *tm) @@ -301,7 +176,6 @@ static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)  {  	u32	rtsr; -	memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time));  	rtsr = RTSR;  	alrm->enabled = (rtsr & RTSR_ALE) ? 1 : 0;  	alrm->pending = (rtsr & RTSR_AL) ? 1 : 0; @@ -310,51 +184,72 @@ static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)  static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)  { +	struct sa1100_rtc *info = dev_get_drvdata(dev); +	unsigned long time;  	int ret; -	spin_lock_irq(&sa1100_rtc_lock); -	ret = rtc_update_alarm(&alrm->time); -	if (ret == 0) { -		if (alrm->enabled) -			RTSR |= RTSR_ALE; -		else -			RTSR &= ~RTSR_ALE; -	} -	spin_unlock_irq(&sa1100_rtc_lock); +	spin_lock_irq(&info->lock); +	ret = rtc_tm_to_time(&alrm->time, &time); +	if (ret != 0) +		goto out; +	RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL); +	RTAR = time; +	if (alrm->enabled) +		RTSR |= RTSR_ALE; +	else +		RTSR &= ~RTSR_ALE; +out: +	spin_unlock_irq(&info->lock);  	return ret;  }  static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq)  { -	seq_printf(seq, "trim/divider\t: 0x%08x\n", (u32) RTTR); -	seq_printf(seq, "update_IRQ\t: %s\n", -			(RTSR & RTSR_HZE) ? "yes" : "no"); -	seq_printf(seq, "periodic_IRQ\t: %s\n", -			(OIER & OIER_E1) ? "yes" : "no"); -	seq_printf(seq, "periodic_freq\t: %ld\n", rtc_freq); +	seq_printf(seq, "trim/divider\t\t: 0x%08x\n", (u32) RTTR); +	seq_printf(seq, "RTSR\t\t\t: 0x%08x\n", (u32)RTSR);  	return 0;  }  static const struct rtc_class_ops sa1100_rtc_ops = {  	.open = sa1100_rtc_open, -	.read_callback = sa1100_rtc_read_callback,  	.release = sa1100_rtc_release, -	.ioctl = sa1100_rtc_ioctl,  	.read_time = sa1100_rtc_read_time,  	.set_time = sa1100_rtc_set_time,  	.read_alarm = sa1100_rtc_read_alarm,  	.set_alarm = sa1100_rtc_set_alarm,  	.proc = sa1100_rtc_proc, +	.alarm_irq_enable = sa1100_rtc_alarm_irq_enable,  };  static int sa1100_rtc_probe(struct platform_device *pdev)  {  	struct rtc_device *rtc; - -	timer_freq = get_clock_tick_rate(); - +	struct sa1100_rtc *info; +	int irq_1hz, irq_alarm, ret = 0; + +	irq_1hz = platform_get_irq_byname(pdev, "rtc 1Hz"); +	irq_alarm = platform_get_irq_byname(pdev, "rtc alarm"); +	if (irq_1hz < 0 || irq_alarm < 0) +		return -ENODEV; + +	info = devm_kzalloc(&pdev->dev, sizeof(struct sa1100_rtc), GFP_KERNEL); +	if (!info) +		return -ENOMEM; +	info->clk = devm_clk_get(&pdev->dev, NULL); +	if (IS_ERR(info->clk)) { +		dev_err(&pdev->dev, "failed to find rtc clock source\n"); +		return PTR_ERR(info->clk); +	} +	info->irq_1hz = irq_1hz; +	info->irq_alarm = irq_alarm; +	spin_lock_init(&info->lock); +	platform_set_drvdata(pdev, info); + +	ret = clk_prepare_enable(info->clk); +	if (ret) +		return ret;  	/*  	 * According to the manual we should be able to let RTTR be zero  	 * and then a default diviser for a 32.768KHz clock is used. @@ -364,53 +259,91 @@ static int sa1100_rtc_probe(struct platform_device *pdev)  	 */  	if (RTTR == 0) {  		RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16); -		dev_warn(&pdev->dev, "warning: initializing default clock divider/trim value\n"); +		dev_warn(&pdev->dev, "warning: " +			"initializing default clock divider/trim value\n");  		/* The current RTC value probably doesn't make sense either */  		RCNR = 0;  	}  	device_init_wakeup(&pdev->dev, 1); -	rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops, -				THIS_MODULE); +	rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &sa1100_rtc_ops, +					THIS_MODULE); -	if (IS_ERR(rtc)) -		return PTR_ERR(rtc); - -	platform_set_drvdata(pdev, rtc); +	if (IS_ERR(rtc)) { +		ret = PTR_ERR(rtc); +		goto err_dev; +	} +	info->rtc = rtc; + +	/* Fix for a nasty initialization problem the in SA11xx RTSR register. +	 * See also the comments in sa1100_rtc_interrupt(). +	 * +	 * Sometimes bit 1 of the RTSR (RTSR_HZ) will wake up 1, which means an +	 * interrupt pending, even though interrupts were never enabled. +	 * In this case, this bit it must be reset before enabling +	 * interruptions to avoid a nonexistent interrupt to occur. +	 * +	 * In principle, the same problem would apply to bit 0, although it has +	 * never been observed to happen. +	 * +	 * This issue is addressed both here and in sa1100_rtc_interrupt(). +	 * If the issue is not addressed here, in the times when the processor +	 * wakes up with the bit set there will be one spurious interrupt. +	 * +	 * The issue is also dealt with in sa1100_rtc_interrupt() to be on the +	 * safe side, once the condition that lead to this strange +	 * initialization is unknown and could in principle happen during +	 * normal processing. +	 * +	 * Notice that clearing bit 1 and 0 is accomplished by writting ONES to +	 * the corresponding bits in RTSR. */ +	RTSR = RTSR_AL | RTSR_HZ;  	return 0; +err_dev: +	clk_disable_unprepare(info->clk); +	return ret;  }  static int sa1100_rtc_remove(struct platform_device *pdev)  { -	struct rtc_device *rtc = platform_get_drvdata(pdev); +	struct sa1100_rtc *info = platform_get_drvdata(pdev); - 	if (rtc) -		rtc_device_unregister(rtc); +	if (info) +		clk_disable_unprepare(info->clk);  	return 0;  } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP  static int sa1100_rtc_suspend(struct device *dev)  { +	struct sa1100_rtc *info = dev_get_drvdata(dev);  	if (device_may_wakeup(dev)) -		enable_irq_wake(IRQ_RTCAlrm); +		enable_irq_wake(info->irq_alarm);  	return 0;  }  static int sa1100_rtc_resume(struct device *dev)  { +	struct sa1100_rtc *info = dev_get_drvdata(dev);  	if (device_may_wakeup(dev)) -		disable_irq_wake(IRQ_RTCAlrm); +		disable_irq_wake(info->irq_alarm);  	return 0;  } +#endif -static const struct dev_pm_ops sa1100_rtc_pm_ops = { -	.suspend	= sa1100_rtc_suspend, -	.resume		= sa1100_rtc_resume, +static SIMPLE_DEV_PM_OPS(sa1100_rtc_pm_ops, sa1100_rtc_suspend, +			sa1100_rtc_resume); + +#ifdef CONFIG_OF +static const struct of_device_id sa1100_rtc_dt_ids[] = { +	{ .compatible = "mrvl,sa1100-rtc", }, +	{ .compatible = "mrvl,mmp-rtc", }, +	{}  }; +MODULE_DEVICE_TABLE(of, sa1100_rtc_dt_ids);  #endif  static struct platform_driver sa1100_rtc_driver = { @@ -418,24 +351,12 @@ static struct platform_driver sa1100_rtc_driver = {  	.remove		= sa1100_rtc_remove,  	.driver		= {  		.name	= "sa1100-rtc", -#ifdef CONFIG_PM  		.pm	= &sa1100_rtc_pm_ops, -#endif +		.of_match_table = of_match_ptr(sa1100_rtc_dt_ids),  	},  }; -static int __init sa1100_rtc_init(void) -{ -	return platform_driver_register(&sa1100_rtc_driver); -} - -static void __exit sa1100_rtc_exit(void) -{ -	platform_driver_unregister(&sa1100_rtc_driver); -} - -module_init(sa1100_rtc_init); -module_exit(sa1100_rtc_exit); +module_platform_driver(sa1100_rtc_driver);  MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");  MODULE_DESCRIPTION("SA11x0/PXA2xx Realtime Clock Driver (RTC)");  | 
