2011-01-15 13:19:03 +03:00
/*
2011-05-26 12:43:27 +04:00
* RTC driver code specific to PKUnity SoC and UniCore ISA
2011-01-15 13:19:03 +03:00
*
* Maintained by GUAN Xue - tao < gxt @ mprc . pku . edu . cn >
* 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 ;
2011-02-26 16:21:18 +03:00
writel ( readl ( RTC_RTSR ) | RTC_RTSR_AL , RTC_RTSR ) ;
2011-01-15 13:19:03 +03:00
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 ;
2011-02-26 16:21:18 +03:00
writel ( readl ( RTC_RTSR ) | RTC_RTSR_HZ , RTC_RTSR ) ;
2011-01-15 13:19:03 +03:00
rtc_update_irq ( rdev , 1 , RTC_PF | RTC_IRQF ) ;
return IRQ_HANDLED ;
}
/* Update control registers */
2013-11-13 03:11:00 +04:00
static void puv3_rtc_setaie ( struct device * dev , int to )
2011-01-15 13:19:03 +03:00
{
unsigned int tmp ;
2013-11-13 03:11:00 +04:00
dev_dbg ( dev , " %s: aie=%d \n " , __func__ , to ) ;
2011-01-15 13:19:03 +03:00
2011-02-26 16:21:18 +03:00
tmp = readl ( RTC_RTSR ) & ~ RTC_RTSR_ALE ;
2011-01-15 13:19:03 +03:00
if ( to )
tmp | = RTC_RTSR_ALE ;
2011-02-26 16:21:18 +03:00
writel ( tmp , RTC_RTSR ) ;
2011-01-15 13:19:03 +03:00
}
static int puv3_rtc_setpie ( struct device * dev , int enabled )
{
unsigned int tmp ;
2014-05-03 09:07:57 +04:00
dev_dbg ( dev , " %s: pie=%d \n " , __func__ , enabled ) ;
2011-01-15 13:19:03 +03:00
spin_lock_irq ( & puv3_rtc_pie_lock ) ;
2011-02-26 16:21:18 +03:00
tmp = readl ( RTC_RTSR ) & ~ RTC_RTSR_HZE ;
2011-01-15 13:19:03 +03:00
if ( enabled )
tmp | = RTC_RTSR_HZE ;
2011-02-26 16:21:18 +03:00
writel ( tmp , RTC_RTSR ) ;
2011-01-15 13:19:03 +03:00
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 )
{
2011-02-26 16:21:18 +03:00
rtc_time_to_tm ( readl ( RTC_RCNR ) , rtc_tm ) ;
2011-01-15 13:19:03 +03:00
2013-11-13 03:11:00 +04:00
dev_dbg ( dev , " read time %02x.%02x.%02x %02x/%02x/%02x \n " ,
2011-01-15 13:19:03 +03:00
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 ;
2013-11-13 03:11:00 +04:00
dev_dbg ( dev , " set time %02d.%02d.%02d %02d/%02d/%02d \n " ,
2011-01-15 13:19:03 +03:00
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 ) ;
2011-02-26 16:21:18 +03:00
writel ( rtc_count , RTC_RCNR ) ;
2011-01-15 13:19:03 +03:00
return 0 ;
}
static int puv3_rtc_getalarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
struct rtc_time * alm_tm = & alrm - > time ;
2011-02-26 16:21:18 +03:00
rtc_time_to_tm ( readl ( RTC_RTAR ) , alm_tm ) ;
2011-01-15 13:19:03 +03:00
2011-02-26 16:21:18 +03:00
alrm - > enabled = readl ( RTC_RTSR ) & RTC_RTSR_ALE ;
2011-01-15 13:19:03 +03:00
2013-11-13 03:11:00 +04:00
dev_dbg ( dev , " read alarm %02x %02x.%02x.%02x %02x/%02x/%02x \n " ,
2011-01-15 13:19:03 +03:00
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 ;
2013-11-13 03:11:00 +04:00
dev_dbg ( dev , " puv3_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x \n " ,
2011-01-15 13:19:03 +03:00
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 ) ;
2011-02-26 16:21:18 +03:00
writel ( rtcalarm_count , RTC_RTAR ) ;
2011-01-15 13:19:03 +03:00
2014-05-03 09:09:02 +04:00
puv3_rtc_setaie ( dev , alrm - > enabled ) ;
2011-01-15 13:19:03 +03:00
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 " ,
2011-02-26 16:21:18 +03:00
( readl ( RTC_RTSR ) & RTC_RTSR_HZE ) ? " yes " : " no " ) ;
2011-01-15 13:19:03 +03:00
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 ,
2011-12-23 05:15:39 +04:00
0 , " pkunity-rtc alarm " , rtc_dev ) ;
2011-01-15 13:19:03 +03:00
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 ,
2011-12-23 05:15:39 +04:00
0 , " pkunity-rtc tick " , rtc_dev ) ;
2011-01-15 13:19:03 +03:00
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 ,
} ;
2013-04-30 03:21:02 +04:00
static void puv3_rtc_enable ( struct device * dev , int en )
2011-01-15 13:19:03 +03:00
{
if ( ! en ) {
2011-02-26 16:21:18 +03:00
writel ( readl ( RTC_RTSR ) & ~ RTC_RTSR_HZE , RTC_RTSR ) ;
2011-01-15 13:19:03 +03:00
} else {
/* re-enable the device, and check it is ok */
2011-02-26 16:21:18 +03:00
if ( ( readl ( RTC_RTSR ) & RTC_RTSR_HZE ) = = 0 ) {
2013-04-30 03:21:02 +04:00
dev_info ( dev , " rtc disabled, re-enabling \n " ) ;
2011-02-26 16:21:18 +03:00
writel ( readl ( RTC_RTSR ) | RTC_RTSR_HZE , RTC_RTSR ) ;
2011-01-15 13:19:03 +03:00
}
}
}
2012-12-22 01:09:38 +04:00
static int puv3_rtc_remove ( struct platform_device * dev )
2011-01-15 13:19:03 +03:00
{
struct rtc_device * rtc = platform_get_drvdata ( dev ) ;
rtc_device_unregister ( rtc ) ;
puv3_rtc_setpie ( & dev - > dev , 0 ) ;
2013-11-13 03:11:00 +04:00
puv3_rtc_setaie ( & dev - > dev , 0 ) ;
2011-01-15 13:19:03 +03:00
release_resource ( puv3_rtc_mem ) ;
kfree ( puv3_rtc_mem ) ;
return 0 ;
}
2012-12-22 01:09:38 +04:00
static int puv3_rtc_probe ( struct platform_device * pdev )
2011-01-15 13:19:03 +03:00
{
struct rtc_device * rtc ;
struct resource * res ;
int ret ;
2013-11-13 03:11:00 +04:00
dev_dbg ( & pdev - > dev , " %s: probe=%p \n " , __func__ , pdev ) ;
2011-01-15 13:19:03 +03:00
/* 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 ;
}
2013-11-13 03:11:00 +04:00
dev_dbg ( & pdev - > dev , " PKUnity_rtc: tick irq %d, alarm irq %d \n " ,
2011-01-15 13:19:03 +03:00
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 ;
}
2011-06-09 20:13:32 +04:00
puv3_rtc_mem = request_mem_region ( res - > start , resource_size ( res ) ,
pdev - > name ) ;
2011-01-15 13:19:03 +03:00
if ( puv3_rtc_mem = = NULL ) {
dev_err ( & pdev - > dev , " failed to reserve memory region \n " ) ;
ret = - ENOENT ;
goto err_nores ;
}
2013-04-30 03:21:02 +04:00
puv3_rtc_enable ( & pdev - > dev , 1 ) ;
2011-01-15 13:19:03 +03:00
/* 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 :
2013-04-30 03:21:02 +04:00
puv3_rtc_enable ( & pdev - > dev , 0 ) ;
2011-01-15 13:19:03 +03:00
release_resource ( puv3_rtc_mem ) ;
err_nores :
return ret ;
}
2013-04-30 03:21:02 +04:00
# ifdef CONFIG_PM_SLEEP
2011-01-15 13:19:03 +03:00
static int ticnt_save ;
2013-04-30 03:21:02 +04:00
static int puv3_rtc_suspend ( struct device * dev )
2011-01-15 13:19:03 +03:00
{
/* save RTAR for anyone using periodic interrupts */
2011-02-26 16:21:18 +03:00
ticnt_save = readl ( RTC_RTAR ) ;
2013-04-30 03:21:02 +04:00
puv3_rtc_enable ( dev , 0 ) ;
2011-01-15 13:19:03 +03:00
return 0 ;
}
2013-04-30 03:21:02 +04:00
static int puv3_rtc_resume ( struct device * dev )
2011-01-15 13:19:03 +03:00
{
2013-04-30 03:21:02 +04:00
puv3_rtc_enable ( dev , 1 ) ;
2011-02-26 16:21:18 +03:00
writel ( ticnt_save , RTC_RTAR ) ;
2011-01-15 13:19:03 +03:00
return 0 ;
}
# endif
2013-04-30 03:21:02 +04:00
static SIMPLE_DEV_PM_OPS ( puv3_rtc_pm_ops , puv3_rtc_suspend , puv3_rtc_resume ) ;
rtc-puv3: solve section mismatch in rtc-puv3.c
The patch renames puv3_rtcdrv to puv3_rtc_driver, so that modpost will know
that this is simply a list of pointers to driver functions, in which case
the section mismatch is OK. (Thanks Michal Marek)
Cc: Axel Lin <axel.lin@gmail.com>
Cc: Michal Marek <mmarek@suse.cz>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: rtc-linux@googlegroups.com
Signed-off-by: Guan Xuetao <gxt@mprc.pku.edu.cn>
--
Section mismatch warning information:
WARNING: drivers/rtc/built-in.o(.data+0x90): Section mismatch in
reference from the variable puv3_rtcdrv to the
function .devinit.text:puv3_rtc_probe()
The variable puv3_rtcdrv references
the function __devinit puv3_rtc_probe()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the
variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one,
*_console
WARNING: drivers/rtc/built-in.o(.data+0x94): Section mismatch in
reference from the variable puv3_rtcdrv to the
function .devexit.text:puv3_rtc_remove()
The variable puv3_rtcdrv references
the function __devexit puv3_rtc_remove()
If the reference is valid then annotate the
variable with __exit* (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one,
*_console
WARNING: drivers/built-in.o(.data+0x6c04): Section mismatch in reference
from the variable puv3_rtcdrv to the
function .devinit.text:puv3_rtc_probe()
The variable puv3_rtcdrv references
the function __devinit puv3_rtc_probe()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the
variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one,
*_console
WARNING: drivers/built-in.o(.data+0x6c08): Section mismatch in reference
from the variable puv3_rtcdrv to the
function .devexit.text:puv3_rtc_remove()
The variable puv3_rtcdrv references
the function __devexit puv3_rtc_remove()
If the reference is valid then annotate the
variable with __exit* (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one,
*_console
WARNING: vmlinux.o(.data+0x1126c): Section mismatch in reference from
the variable puv3_rtcdrv to the function .devinit.text:puv3_rtc_probe()
The variable puv3_rtcdrv references
the function __devinit puv3_rtc_probe()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the
variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one,
*_console
WARNING: vmlinux.o(.data+0x11270): Section mismatch in reference from
the variable puv3_rtcdrv to the function .devexit.text:puv3_rtc_remove()
The variable puv3_rtcdrv references
the function __devexit puv3_rtc_remove()
If the reference is valid then annotate the
variable with __exit* (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one,
*_console
2011-12-28 05:24:29 +04:00
static struct platform_driver puv3_rtc_driver = {
2011-01-15 13:19:03 +03:00
. probe = puv3_rtc_probe ,
2012-12-22 01:09:38 +04:00
. remove = puv3_rtc_remove ,
2011-01-15 13:19:03 +03:00
. driver = {
. name = " PKUnity-v3-RTC " ,
2013-04-30 03:21:02 +04:00
. pm = & puv3_rtc_pm_ops ,
2011-01-15 13:19:03 +03:00
}
} ;
rtc-puv3: solve section mismatch in rtc-puv3.c
The patch renames puv3_rtcdrv to puv3_rtc_driver, so that modpost will know
that this is simply a list of pointers to driver functions, in which case
the section mismatch is OK. (Thanks Michal Marek)
Cc: Axel Lin <axel.lin@gmail.com>
Cc: Michal Marek <mmarek@suse.cz>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: rtc-linux@googlegroups.com
Signed-off-by: Guan Xuetao <gxt@mprc.pku.edu.cn>
--
Section mismatch warning information:
WARNING: drivers/rtc/built-in.o(.data+0x90): Section mismatch in
reference from the variable puv3_rtcdrv to the
function .devinit.text:puv3_rtc_probe()
The variable puv3_rtcdrv references
the function __devinit puv3_rtc_probe()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the
variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one,
*_console
WARNING: drivers/rtc/built-in.o(.data+0x94): Section mismatch in
reference from the variable puv3_rtcdrv to the
function .devexit.text:puv3_rtc_remove()
The variable puv3_rtcdrv references
the function __devexit puv3_rtc_remove()
If the reference is valid then annotate the
variable with __exit* (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one,
*_console
WARNING: drivers/built-in.o(.data+0x6c04): Section mismatch in reference
from the variable puv3_rtcdrv to the
function .devinit.text:puv3_rtc_probe()
The variable puv3_rtcdrv references
the function __devinit puv3_rtc_probe()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the
variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one,
*_console
WARNING: drivers/built-in.o(.data+0x6c08): Section mismatch in reference
from the variable puv3_rtcdrv to the
function .devexit.text:puv3_rtc_remove()
The variable puv3_rtcdrv references
the function __devexit puv3_rtc_remove()
If the reference is valid then annotate the
variable with __exit* (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one,
*_console
WARNING: vmlinux.o(.data+0x1126c): Section mismatch in reference from
the variable puv3_rtcdrv to the function .devinit.text:puv3_rtc_probe()
The variable puv3_rtcdrv references
the function __devinit puv3_rtc_probe()
If the reference is valid then annotate the
variable with __init* or __refdata (see linux/init.h) or name the
variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one,
*_console
WARNING: vmlinux.o(.data+0x11270): Section mismatch in reference from
the variable puv3_rtcdrv to the function .devexit.text:puv3_rtc_remove()
The variable puv3_rtcdrv references
the function __devexit puv3_rtc_remove()
If the reference is valid then annotate the
variable with __exit* (see linux/init.h) or name the variable:
*driver, *_template, *_timer, *_sht, *_ops, *_probe, *_probe_one,
*_console
2011-12-28 05:24:29 +04:00
module_platform_driver ( puv3_rtc_driver ) ;
2011-01-15 13:19:03 +03:00
MODULE_DESCRIPTION ( " RTC Driver for the PKUnity v3 chip " ) ;
MODULE_AUTHOR ( " Hu Dongliang " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;