diff options
Diffstat (limited to 'drivers/rtc')
| -rw-r--r-- | drivers/rtc/Kconfig | 9 | ||||
| -rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
| -rw-r--r-- | drivers/rtc/interface.c | 2 | ||||
| -rw-r--r-- | drivers/rtc/rtc-dev.c | 3 | ||||
| -rw-r--r-- | drivers/rtc/rtc-ds1307.c | 1 | ||||
| -rw-r--r-- | drivers/rtc/rtc-m41t93.c | 2 | ||||
| -rw-r--r-- | drivers/rtc/rtc-puv3.c | 359 | ||||
| -rw-r--r-- | drivers/rtc/rtc-vt8500.c | 45 | 
8 files changed, 377 insertions, 45 deletions
| diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index f822e13dc04b..ce2aabf5c550 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1051,4 +1051,13 @@ config RTC_DRV_TILE  	  Enable support for the Linux driver side of the Tilera  	  hypervisor's real-time clock interface. +config RTC_DRV_PUV3 +	tristate "PKUnity v3 RTC support" +	depends on ARCH_PUV3 +	help +	  This enables support for the RTC in the PKUnity-v3 SoCs. + +	  This drive can also be built as a module. If so, the module +	  will be called rtc-puv3. +  endif # RTC_CLASS diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 213d725f16d4..0ffefe877bfa 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -78,6 +78,7 @@ obj-$(CONFIG_RTC_DRV_PCF50633)	+= rtc-pcf50633.o  obj-$(CONFIG_RTC_DRV_PL030)	+= rtc-pl030.o  obj-$(CONFIG_RTC_DRV_PL031)	+= rtc-pl031.o  obj-$(CONFIG_RTC_DRV_PS3)	+= rtc-ps3.o +obj-$(CONFIG_RTC_DRV_PUV3)	+= rtc-puv3.o  obj-$(CONFIG_RTC_DRV_PXA)	+= rtc-pxa.o  obj-$(CONFIG_RTC_DRV_R9701)	+= rtc-r9701.o  obj-$(CONFIG_RTC_DRV_RP5C01)	+= rtc-rp5c01.o diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index ef6316acec43..df68618f6dbb 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -318,7 +318,7 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)  }  EXPORT_SYMBOL_GPL(rtc_read_alarm); -int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm) +static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)  {  	struct rtc_time tm;  	long now, scheduled; diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c index d0e06edb14c5..cace6d3aed9a 100644 --- a/drivers/rtc/rtc-dev.c +++ b/drivers/rtc/rtc-dev.c @@ -421,7 +421,8 @@ static long rtc_dev_ioctl(struct file *file,  			err = ops->ioctl(rtc->dev.parent, cmd, arg);  			if (err == -ENOIOCTLCMD)  				err = -ENOTTY; -		} +		} else +			err = -ENOTTY;  		break;  	} diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index 4724ba3acf1a..b2005b44e4f7 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -149,6 +149,7 @@ static const struct i2c_device_id ds1307_id[] = {  	{ "ds1340", ds_1340 },  	{ "ds3231", ds_3231 },  	{ "m41t00", m41t00 }, +	{ "pt7c4338", ds_1307 },  	{ "rx8025", rx_8025 },  	{ }  }; diff --git a/drivers/rtc/rtc-m41t93.c b/drivers/rtc/rtc-m41t93.c index 1a84b3e227d1..7317d3b9a3d5 100644 --- a/drivers/rtc/rtc-m41t93.c +++ b/drivers/rtc/rtc-m41t93.c @@ -189,7 +189,7 @@ static int __devinit m41t93_probe(struct spi_device *spi)  static int __devexit m41t93_remove(struct spi_device *spi)  { -	struct rtc_device *rtc = platform_get_drvdata(spi); +	struct rtc_device *rtc = spi_get_drvdata(spi);  	if (rtc)  		rtc_device_unregister(rtc); diff --git a/drivers/rtc/rtc-puv3.c b/drivers/rtc/rtc-puv3.c new file mode 100644 index 000000000000..46f14b82f3ab --- /dev/null +++ b/drivers/rtc/rtc-puv3.c @@ -0,0 +1,359 @@ +/* + * RTC driver code specific to PKUnity SoC and UniCore ISA + * + *	Maintained by GUAN Xue-tao <[email protected]> + *	Copyright (C) 2001-2010 Guan Xuetao + * + * 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/module.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/rtc.h> +#include <linux/bcd.h> +#include <linux/clk.h> +#include <linux/log2.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/io.h> + +#include <asm/irq.h> +#include <mach/hardware.h> + +static struct resource *puv3_rtc_mem; + +static int puv3_rtc_alarmno = IRQ_RTCAlarm; +static int puv3_rtc_tickno  = IRQ_RTC; + +static DEFINE_SPINLOCK(puv3_rtc_pie_lock); + +/* IRQ Handlers */ +static irqreturn_t puv3_rtc_alarmirq(int irq, void *id) +{ +	struct rtc_device *rdev = id; + +	writel(readl(RTC_RTSR) | RTC_RTSR_AL, RTC_RTSR); +	rtc_update_irq(rdev, 1, RTC_AF | RTC_IRQF); +	return IRQ_HANDLED; +} + +static irqreturn_t puv3_rtc_tickirq(int irq, void *id) +{ +	struct rtc_device *rdev = id; + +	writel(readl(RTC_RTSR) | RTC_RTSR_HZ, RTC_RTSR); +	rtc_update_irq(rdev, 1, RTC_PF | RTC_IRQF); +	return IRQ_HANDLED; +} + +/* Update control registers */ +static void puv3_rtc_setaie(int to) +{ +	unsigned int tmp; + +	pr_debug("%s: aie=%d\n", __func__, to); + +	tmp = readl(RTC_RTSR) & ~RTC_RTSR_ALE; + +	if (to) +		tmp |= RTC_RTSR_ALE; + +	writel(tmp, RTC_RTSR); +} + +static int puv3_rtc_setpie(struct device *dev, int enabled) +{ +	unsigned int tmp; + +	pr_debug("%s: pie=%d\n", __func__, enabled); + +	spin_lock_irq(&puv3_rtc_pie_lock); +	tmp = readl(RTC_RTSR) & ~RTC_RTSR_HZE; + +	if (enabled) +		tmp |= RTC_RTSR_HZE; + +	writel(tmp, RTC_RTSR); +	spin_unlock_irq(&puv3_rtc_pie_lock); + +	return 0; +} + +/* Time read/write */ +static int puv3_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) +{ +	rtc_time_to_tm(readl(RTC_RCNR), rtc_tm); + +	pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n", +		 rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, +		 rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); + +	return 0; +} + +static int puv3_rtc_settime(struct device *dev, struct rtc_time *tm) +{ +	unsigned long rtc_count = 0; + +	pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n", +		 tm->tm_year, tm->tm_mon, tm->tm_mday, +		 tm->tm_hour, tm->tm_min, tm->tm_sec); + +	rtc_tm_to_time(tm, &rtc_count); +	writel(rtc_count, RTC_RCNR); + +	return 0; +} + +static int puv3_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ +	struct rtc_time *alm_tm = &alrm->time; + +	rtc_time_to_tm(readl(RTC_RTAR), alm_tm); + +	alrm->enabled = readl(RTC_RTSR) & RTC_RTSR_ALE; + +	pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n", +		 alrm->enabled, +		 alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, +		 alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec); + +	return 0; +} + +static int puv3_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ +	struct rtc_time *tm = &alrm->time; +	unsigned long rtcalarm_count = 0; + +	pr_debug("puv3_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n", +		 alrm->enabled, +		 tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff, +		 tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec); + +	rtc_tm_to_time(tm, &rtcalarm_count); +	writel(rtcalarm_count, RTC_RTAR); + +	puv3_rtc_setaie(alrm->enabled); + +	if (alrm->enabled) +		enable_irq_wake(puv3_rtc_alarmno); +	else +		disable_irq_wake(puv3_rtc_alarmno); + +	return 0; +} + +static int puv3_rtc_proc(struct device *dev, struct seq_file *seq) +{ +	seq_printf(seq, "periodic_IRQ\t: %s\n", +		     (readl(RTC_RTSR) & RTC_RTSR_HZE) ? "yes" : "no"); +	return 0; +} + +static int puv3_rtc_open(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct rtc_device *rtc_dev = platform_get_drvdata(pdev); +	int ret; + +	ret = request_irq(puv3_rtc_alarmno, puv3_rtc_alarmirq, +			  IRQF_DISABLED,  "pkunity-rtc alarm", rtc_dev); + +	if (ret) { +		dev_err(dev, "IRQ%d error %d\n", puv3_rtc_alarmno, ret); +		return ret; +	} + +	ret = request_irq(puv3_rtc_tickno, puv3_rtc_tickirq, +			  IRQF_DISABLED,  "pkunity-rtc tick", rtc_dev); + +	if (ret) { +		dev_err(dev, "IRQ%d error %d\n", puv3_rtc_tickno, ret); +		goto tick_err; +	} + +	return ret; + + tick_err: +	free_irq(puv3_rtc_alarmno, rtc_dev); +	return ret; +} + +static void puv3_rtc_release(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct rtc_device *rtc_dev = platform_get_drvdata(pdev); + +	/* do not clear AIE here, it may be needed for wake */ +	puv3_rtc_setpie(dev, 0); +	free_irq(puv3_rtc_alarmno, rtc_dev); +	free_irq(puv3_rtc_tickno, rtc_dev); +} + +static const struct rtc_class_ops puv3_rtcops = { +	.open		= puv3_rtc_open, +	.release	= puv3_rtc_release, +	.read_time	= puv3_rtc_gettime, +	.set_time	= puv3_rtc_settime, +	.read_alarm	= puv3_rtc_getalarm, +	.set_alarm	= puv3_rtc_setalarm, +	.proc	        = puv3_rtc_proc, +}; + +static void puv3_rtc_enable(struct platform_device *pdev, int en) +{ +	if (!en) { +		writel(readl(RTC_RTSR) & ~RTC_RTSR_HZE, RTC_RTSR); +	} else { +		/* re-enable the device, and check it is ok */ +		if ((readl(RTC_RTSR) & RTC_RTSR_HZE) == 0) { +			dev_info(&pdev->dev, "rtc disabled, re-enabling\n"); +			writel(readl(RTC_RTSR) | RTC_RTSR_HZE, RTC_RTSR); +		} +	} +} + +static int puv3_rtc_remove(struct platform_device *dev) +{ +	struct rtc_device *rtc = platform_get_drvdata(dev); + +	platform_set_drvdata(dev, NULL); +	rtc_device_unregister(rtc); + +	puv3_rtc_setpie(&dev->dev, 0); +	puv3_rtc_setaie(0); + +	release_resource(puv3_rtc_mem); +	kfree(puv3_rtc_mem); + +	return 0; +} + +static int puv3_rtc_probe(struct platform_device *pdev) +{ +	struct rtc_device *rtc; +	struct resource *res; +	int ret; + +	pr_debug("%s: probe=%p\n", __func__, pdev); + +	/* find the IRQs */ +	puv3_rtc_tickno = platform_get_irq(pdev, 1); +	if (puv3_rtc_tickno < 0) { +		dev_err(&pdev->dev, "no irq for rtc tick\n"); +		return -ENOENT; +	} + +	puv3_rtc_alarmno = platform_get_irq(pdev, 0); +	if (puv3_rtc_alarmno < 0) { +		dev_err(&pdev->dev, "no irq for alarm\n"); +		return -ENOENT; +	} + +	pr_debug("PKUnity_rtc: tick irq %d, alarm irq %d\n", +		 puv3_rtc_tickno, puv3_rtc_alarmno); + +	/* get the memory region */ +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (res == NULL) { +		dev_err(&pdev->dev, "failed to get memory region resource\n"); +		return -ENOENT; +	} + +	puv3_rtc_mem = request_mem_region(res->start, +					 res->end-res->start+1, +					 pdev->name); + +	if (puv3_rtc_mem == NULL) { +		dev_err(&pdev->dev, "failed to reserve memory region\n"); +		ret = -ENOENT; +		goto err_nores; +	} + +	puv3_rtc_enable(pdev, 1); + +	/* register RTC and exit */ +	rtc = rtc_device_register("pkunity", &pdev->dev, &puv3_rtcops, +				  THIS_MODULE); + +	if (IS_ERR(rtc)) { +		dev_err(&pdev->dev, "cannot attach rtc\n"); +		ret = PTR_ERR(rtc); +		goto err_nortc; +	} + +	/* platform setup code should have handled this; sigh */ +	if (!device_can_wakeup(&pdev->dev)) +		device_init_wakeup(&pdev->dev, 1); + +	platform_set_drvdata(pdev, rtc); +	return 0; + + err_nortc: +	puv3_rtc_enable(pdev, 0); +	release_resource(puv3_rtc_mem); + + err_nores: +	return ret; +} + +#ifdef CONFIG_PM + +static int ticnt_save; + +static int puv3_rtc_suspend(struct platform_device *pdev, pm_message_t state) +{ +	/* save RTAR for anyone using periodic interrupts */ +	ticnt_save = readl(RTC_RTAR); +	puv3_rtc_enable(pdev, 0); +	return 0; +} + +static int puv3_rtc_resume(struct platform_device *pdev) +{ +	puv3_rtc_enable(pdev, 1); +	writel(ticnt_save, RTC_RTAR); +	return 0; +} +#else +#define puv3_rtc_suspend NULL +#define puv3_rtc_resume  NULL +#endif + +static struct platform_driver puv3_rtcdrv = { +	.probe		= puv3_rtc_probe, +	.remove		= __devexit_p(puv3_rtc_remove), +	.suspend	= puv3_rtc_suspend, +	.resume		= puv3_rtc_resume, +	.driver		= { +		.name	= "PKUnity-v3-RTC", +		.owner	= THIS_MODULE, +	} +}; + +static char __initdata banner[] = "PKUnity-v3 RTC, (c) 2009 PKUnity Co.\n"; + +static int __init puv3_rtc_init(void) +{ +	printk(banner); +	return platform_driver_register(&puv3_rtcdrv); +} + +static void __exit puv3_rtc_exit(void) +{ +	platform_driver_unregister(&puv3_rtcdrv); +} + +module_init(puv3_rtc_init); +module_exit(puv3_rtc_exit); + +MODULE_DESCRIPTION("RTC Driver for the PKUnity v3 chip"); +MODULE_AUTHOR("Hu Dongliang"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c index b8bc862903ae..efd6066b5cd2 100644 --- a/drivers/rtc/rtc-vt8500.c +++ b/drivers/rtc/rtc-vt8500.c @@ -78,7 +78,6 @@ struct vt8500_rtc {  	void __iomem		*regbase;  	struct resource		*res;  	int			irq_alarm; -	int			irq_hz;  	struct rtc_device	*rtc;  	spinlock_t		lock;		/* Protects this structure */  }; @@ -100,10 +99,6 @@ static irqreturn_t vt8500_rtc_irq(int irq, void *dev_id)  	if (isr & 1)  		events |= RTC_AF | RTC_IRQF; -	/* Only second/minute interrupts are supported */ -	if (isr & 2) -		events |= RTC_UF | RTC_IRQF; -  	rtc_update_irq(vt8500_rtc->rtc, 1, events);  	return IRQ_HANDLED; @@ -199,27 +194,12 @@ static int vt8500_alarm_irq_enable(struct device *dev, unsigned int enabled)  	return 0;  } -static int vt8500_update_irq_enable(struct device *dev, unsigned int enabled) -{ -	struct vt8500_rtc *vt8500_rtc = dev_get_drvdata(dev); -	unsigned long tmp = readl(vt8500_rtc->regbase + VT8500_RTC_CR); - -	if (enabled) -		tmp |= VT8500_RTC_CR_SM_SEC | VT8500_RTC_CR_SM_ENABLE; -	else -		tmp &= ~VT8500_RTC_CR_SM_ENABLE; - -	writel(tmp, vt8500_rtc->regbase + VT8500_RTC_CR); -	return 0; -} -  static const struct rtc_class_ops vt8500_rtc_ops = {  	.read_time = vt8500_rtc_read_time,  	.set_time = vt8500_rtc_set_time,  	.read_alarm = vt8500_rtc_read_alarm,  	.set_alarm = vt8500_rtc_set_alarm,  	.alarm_irq_enable = vt8500_alarm_irq_enable, -	.update_irq_enable = vt8500_update_irq_enable,  };  static int __devinit vt8500_rtc_probe(struct platform_device *pdev) @@ -248,13 +228,6 @@ static int __devinit vt8500_rtc_probe(struct platform_device *pdev)  		goto err_free;  	} -	vt8500_rtc->irq_hz = platform_get_irq(pdev, 1); -	if (vt8500_rtc->irq_hz < 0) { -		dev_err(&pdev->dev, "No 1Hz IRQ resource defined\n"); -		ret = -ENXIO; -		goto err_free; -	} -  	vt8500_rtc->res = request_mem_region(vt8500_rtc->res->start,  					     resource_size(vt8500_rtc->res),  					     "vt8500-rtc"); @@ -272,9 +245,8 @@ static int __devinit vt8500_rtc_probe(struct platform_device *pdev)  		goto err_release;  	} -	/* Enable the second/minute interrupt generation and enable RTC */ -	writel(VT8500_RTC_CR_ENABLE | VT8500_RTC_CR_24H -		| VT8500_RTC_CR_SM_ENABLE | VT8500_RTC_CR_SM_SEC, +	/* Enable RTC and set it to 24-hour mode */ +	writel(VT8500_RTC_CR_ENABLE | VT8500_RTC_CR_24H,  	       vt8500_rtc->regbase + VT8500_RTC_CR);  	vt8500_rtc->rtc = rtc_device_register("vt8500-rtc", &pdev->dev, @@ -286,26 +258,16 @@ static int __devinit vt8500_rtc_probe(struct platform_device *pdev)  		goto err_unmap;  	} -	ret = request_irq(vt8500_rtc->irq_hz, vt8500_rtc_irq, 0, -			  "rtc 1Hz", vt8500_rtc); -	if (ret < 0) { -		dev_err(&pdev->dev, "can't get irq %i, err %d\n", -			vt8500_rtc->irq_hz, ret); -		goto err_unreg; -	} -  	ret = request_irq(vt8500_rtc->irq_alarm, vt8500_rtc_irq, 0,  			  "rtc alarm", vt8500_rtc);  	if (ret < 0) {  		dev_err(&pdev->dev, "can't get irq %i, err %d\n",  			vt8500_rtc->irq_alarm, ret); -		goto err_free_hz; +		goto err_unreg;  	}  	return 0; -err_free_hz: -	free_irq(vt8500_rtc->irq_hz, vt8500_rtc);  err_unreg:  	rtc_device_unregister(vt8500_rtc->rtc);  err_unmap: @@ -323,7 +285,6 @@ static int __devexit vt8500_rtc_remove(struct platform_device *pdev)  	struct vt8500_rtc *vt8500_rtc = platform_get_drvdata(pdev);  	free_irq(vt8500_rtc->irq_alarm, vt8500_rtc); -	free_irq(vt8500_rtc->irq_hz, vt8500_rtc);  	rtc_device_unregister(vt8500_rtc->rtc); |