2006-03-27 13:16:45 +04:00
/*
* A driver for the RTC embedded in the Cirrus Logic EP93XX processors
* Copyright ( c ) 2006 Tower Technologies
*
* Author : Alessandro Zummo < a . zummo @ towertech . it >
*
* 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/rtc.h>
# include <linux/platform_device.h>
2009-04-16 02:18:26 +04:00
# include <linux/io.h>
# define EP93XX_RTC_DATA 0x000
# define EP93XX_RTC_MATCH 0x004
# define EP93XX_RTC_STATUS 0x008
# define EP93XX_RTC_STATUS_INTR (1<<0)
# define EP93XX_RTC_LOAD 0x00C
# define EP93XX_RTC_CONTROL 0x010
# define EP93XX_RTC_CONTROL_MIE (1<<0)
# define EP93XX_RTC_SWCOMP 0x108
# define EP93XX_RTC_SWCOMP_DEL_MASK 0x001f0000
# define EP93XX_RTC_SWCOMP_DEL_SHIFT 16
# define EP93XX_RTC_SWCOMP_INT_MASK 0x0000ffff
# define EP93XX_RTC_SWCOMP_INT_SHIFT 0
# define DRV_VERSION "0.3"
2006-03-27 13:16:45 +04:00
2009-04-16 02:18:26 +04:00
/*
* struct device dev . platform_data is used to store our private data
* because struct rtc_device does not have a variable to hold it .
*/
struct ep93xx_rtc {
void __iomem * mmio_base ;
} ;
2006-03-27 13:16:45 +04:00
2009-04-16 02:18:26 +04:00
static int ep93xx_rtc_get_swcomp ( struct device * dev , unsigned short * preload ,
2006-03-27 13:16:45 +04:00
unsigned short * delete )
{
2009-04-16 02:18:26 +04:00
struct ep93xx_rtc * ep93xx_rtc = dev - > platform_data ;
unsigned long comp ;
comp = __raw_readl ( ep93xx_rtc - > mmio_base + EP93XX_RTC_SWCOMP ) ;
2006-03-27 13:16:45 +04:00
if ( preload )
2009-04-16 02:18:26 +04:00
* preload = ( comp & EP93XX_RTC_SWCOMP_INT_MASK )
> > EP93XX_RTC_SWCOMP_INT_SHIFT ;
2006-03-27 13:16:45 +04:00
if ( delete )
2009-04-16 02:18:26 +04:00
* delete = ( comp & EP93XX_RTC_SWCOMP_DEL_MASK )
> > EP93XX_RTC_SWCOMP_DEL_SHIFT ;
2006-03-27 13:16:45 +04:00
return 0 ;
}
static int ep93xx_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
2009-04-16 02:18:26 +04:00
struct ep93xx_rtc * ep93xx_rtc = dev - > platform_data ;
unsigned long time ;
time = __raw_readl ( ep93xx_rtc - > mmio_base + EP93XX_RTC_DATA ) ;
2006-03-27 13:16:45 +04:00
rtc_time_to_tm ( time , tm ) ;
return 0 ;
}
static int ep93xx_rtc_set_mmss ( struct device * dev , unsigned long secs )
{
2009-04-16 02:18:26 +04:00
struct ep93xx_rtc * ep93xx_rtc = dev - > platform_data ;
__raw_writel ( secs + 1 , ep93xx_rtc - > mmio_base + EP93XX_RTC_LOAD ) ;
2006-03-27 13:16:45 +04:00
return 0 ;
}
static int ep93xx_rtc_proc ( struct device * dev , struct seq_file * seq )
{
unsigned short preload , delete ;
2009-04-16 02:18:26 +04:00
ep93xx_rtc_get_swcomp ( dev , & preload , & delete ) ;
2006-03-27 13:16:45 +04:00
seq_printf ( seq , " preload \t \t : %d \n " , preload ) ;
seq_printf ( seq , " delete \t \t : %d \n " , delete ) ;
return 0 ;
}
2006-10-01 10:28:17 +04:00
static const struct rtc_class_ops ep93xx_rtc_ops = {
2006-03-27 13:16:45 +04:00
. read_time = ep93xx_rtc_read_time ,
. set_mmss = ep93xx_rtc_set_mmss ,
. proc = ep93xx_rtc_proc ,
} ;
2009-04-16 02:18:26 +04:00
static ssize_t ep93xx_rtc_show_comp_preload ( struct device * dev ,
2006-03-27 13:16:45 +04:00
struct device_attribute * attr , char * buf )
{
unsigned short preload ;
2009-04-16 02:18:26 +04:00
ep93xx_rtc_get_swcomp ( dev , & preload , NULL ) ;
2006-03-27 13:16:45 +04:00
return sprintf ( buf , " %d \n " , preload ) ;
}
2009-04-16 02:18:26 +04:00
static DEVICE_ATTR ( comp_preload , S_IRUGO , ep93xx_rtc_show_comp_preload , NULL ) ;
2006-03-27 13:16:45 +04:00
2009-04-16 02:18:26 +04:00
static ssize_t ep93xx_rtc_show_comp_delete ( struct device * dev ,
2006-03-27 13:16:45 +04:00
struct device_attribute * attr , char * buf )
{
unsigned short delete ;
2009-04-16 02:18:26 +04:00
ep93xx_rtc_get_swcomp ( dev , NULL , & delete ) ;
2006-03-27 13:16:45 +04:00
return sprintf ( buf , " %d \n " , delete ) ;
}
2009-04-16 02:18:26 +04:00
static DEVICE_ATTR ( comp_delete , S_IRUGO , ep93xx_rtc_show_comp_delete , NULL ) ;
2006-03-27 13:16:45 +04:00
2009-04-16 02:18:26 +04:00
static int __init ep93xx_rtc_probe ( struct platform_device * pdev )
2006-03-27 13:16:45 +04:00
{
2009-04-16 02:18:26 +04:00
struct ep93xx_rtc * ep93xx_rtc ;
struct resource * res ;
struct rtc_device * rtc ;
int err ;
ep93xx_rtc = kzalloc ( sizeof ( struct ep93xx_rtc ) , GFP_KERNEL ) ;
if ( ep93xx_rtc = = NULL )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( res = = NULL )
return - ENXIO ;
res = request_mem_region ( res - > start , resource_size ( res ) , pdev - > name ) ;
if ( res = = NULL )
return - EBUSY ;
ep93xx_rtc - > mmio_base = ioremap ( res - > start , resource_size ( res ) ) ;
if ( ep93xx_rtc - > mmio_base = = NULL ) {
err = - ENXIO ;
goto fail ;
}
2006-03-27 13:16:45 +04:00
2009-04-16 02:18:26 +04:00
pdev - > dev . platform_data = ep93xx_rtc ;
rtc = rtc_device_register ( pdev - > name ,
& pdev - > dev , & ep93xx_rtc_ops , THIS_MODULE ) ;
2006-03-27 13:16:45 +04:00
if ( IS_ERR ( rtc ) ) {
2009-04-16 02:18:26 +04:00
err = PTR_ERR ( rtc ) ;
goto fail ;
2006-03-27 13:16:45 +04:00
}
2009-04-16 02:18:26 +04:00
platform_set_drvdata ( pdev , rtc ) ;
2006-03-27 13:16:45 +04:00
2009-04-16 02:18:26 +04:00
err = device_create_file ( & pdev - > dev , & dev_attr_comp_preload ) ;
if ( err )
goto fail ;
err = device_create_file ( & pdev - > dev , & dev_attr_comp_delete ) ;
if ( err ) {
device_remove_file ( & pdev - > dev , & dev_attr_comp_preload ) ;
goto fail ;
}
2006-03-27 13:16:45 +04:00
return 0 ;
2009-04-16 02:18:26 +04:00
fail :
if ( ep93xx_rtc - > mmio_base ) {
iounmap ( ep93xx_rtc - > mmio_base ) ;
pdev - > dev . platform_data = NULL ;
}
release_mem_region ( res - > start , resource_size ( res ) ) ;
return err ;
2006-03-27 13:16:45 +04:00
}
2009-04-16 02:18:26 +04:00
static int __exit ep93xx_rtc_remove ( struct platform_device * pdev )
2006-03-27 13:16:45 +04:00
{
2009-04-16 02:18:26 +04:00
struct rtc_device * rtc = platform_get_drvdata ( pdev ) ;
struct ep93xx_rtc * ep93xx_rtc = pdev - > dev . platform_data ;
struct resource * res ;
/* cleanup sysfs */
device_remove_file ( & pdev - > dev , & dev_attr_comp_delete ) ;
device_remove_file ( & pdev - > dev , & dev_attr_comp_preload ) ;
rtc_device_unregister ( rtc ) ;
iounmap ( ep93xx_rtc - > mmio_base ) ;
pdev - > dev . platform_data = NULL ;
2006-03-27 13:16:45 +04:00
2009-04-16 02:18:26 +04:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
release_mem_region ( res - > start , resource_size ( res ) ) ;
2006-03-27 13:16:45 +04:00
2009-04-16 02:18:26 +04:00
platform_set_drvdata ( pdev , NULL ) ;
2006-03-27 13:16:45 +04:00
return 0 ;
}
2008-04-11 08:29:25 +04:00
/* work with hotplug and coldplug */
MODULE_ALIAS ( " platform:ep93xx-rtc " ) ;
2009-04-16 02:18:26 +04:00
static struct platform_driver ep93xx_rtc_driver = {
2006-03-27 13:16:45 +04:00
. driver = {
. name = " ep93xx-rtc " ,
. owner = THIS_MODULE ,
} ,
2009-04-16 02:18:26 +04:00
. remove = __exit_p ( ep93xx_rtc_remove ) ,
2006-03-27 13:16:45 +04:00
} ;
static int __init ep93xx_rtc_init ( void )
{
2009-04-16 02:18:26 +04:00
return platform_driver_probe ( & ep93xx_rtc_driver , ep93xx_rtc_probe ) ;
2006-03-27 13:16:45 +04:00
}
static void __exit ep93xx_rtc_exit ( void )
{
2009-04-16 02:18:26 +04:00
platform_driver_unregister ( & ep93xx_rtc_driver ) ;
2006-03-27 13:16:45 +04:00
}
MODULE_AUTHOR ( " Alessandro Zummo <a.zummo@towertech.it> " ) ;
MODULE_DESCRIPTION ( " EP93XX RTC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( DRV_VERSION ) ;
module_init ( ep93xx_rtc_init ) ;
module_exit ( ep93xx_rtc_exit ) ;