2006-03-27 01:16:45 -08: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-15 23:18:26 +01: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 01:16:45 -08:00
2009-04-15 23:18:26 +01: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 01:16:45 -08:00
2009-04-15 23:18:26 +01:00
static int ep93xx_rtc_get_swcomp ( struct device * dev , unsigned short * preload ,
2006-03-27 01:16:45 -08:00
unsigned short * delete )
{
2009-04-15 23:18:26 +01: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 01:16:45 -08:00
if ( preload )
2009-04-15 23:18:26 +01:00
* preload = ( comp & EP93XX_RTC_SWCOMP_INT_MASK )
> > EP93XX_RTC_SWCOMP_INT_SHIFT ;
2006-03-27 01:16:45 -08:00
if ( delete )
2009-04-15 23:18:26 +01:00
* delete = ( comp & EP93XX_RTC_SWCOMP_DEL_MASK )
> > EP93XX_RTC_SWCOMP_DEL_SHIFT ;
2006-03-27 01:16:45 -08:00
return 0 ;
}
static int ep93xx_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
2009-04-15 23:18:26 +01: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 01:16:45 -08:00
rtc_time_to_tm ( time , tm ) ;
return 0 ;
}
static int ep93xx_rtc_set_mmss ( struct device * dev , unsigned long secs )
{
2009-04-15 23:18:26 +01:00
struct ep93xx_rtc * ep93xx_rtc = dev - > platform_data ;
__raw_writel ( secs + 1 , ep93xx_rtc - > mmio_base + EP93XX_RTC_LOAD ) ;
2006-03-27 01:16:45 -08:00
return 0 ;
}
static int ep93xx_rtc_proc ( struct device * dev , struct seq_file * seq )
{
unsigned short preload , delete ;
2009-04-15 23:18:26 +01:00
ep93xx_rtc_get_swcomp ( dev , & preload , & delete ) ;
2006-03-27 01:16:45 -08:00
seq_printf ( seq , " preload \t \t : %d \n " , preload ) ;
seq_printf ( seq , " delete \t \t : %d \n " , delete ) ;
return 0 ;
}
2006-09-30 23:28:17 -07:00
static const struct rtc_class_ops ep93xx_rtc_ops = {
2006-03-27 01:16:45 -08:00
. read_time = ep93xx_rtc_read_time ,
. set_mmss = ep93xx_rtc_set_mmss ,
. proc = ep93xx_rtc_proc ,
} ;
2009-04-15 23:18:26 +01:00
static ssize_t ep93xx_rtc_show_comp_preload ( struct device * dev ,
2006-03-27 01:16:45 -08:00
struct device_attribute * attr , char * buf )
{
unsigned short preload ;
2009-04-15 23:18:26 +01:00
ep93xx_rtc_get_swcomp ( dev , & preload , NULL ) ;
2006-03-27 01:16:45 -08:00
return sprintf ( buf , " %d \n " , preload ) ;
}
2009-04-15 23:18:26 +01:00
static DEVICE_ATTR ( comp_preload , S_IRUGO , ep93xx_rtc_show_comp_preload , NULL ) ;
2006-03-27 01:16:45 -08:00
2009-04-15 23:18:26 +01:00
static ssize_t ep93xx_rtc_show_comp_delete ( struct device * dev ,
2006-03-27 01:16:45 -08:00
struct device_attribute * attr , char * buf )
{
unsigned short delete ;
2009-04-15 23:18:26 +01:00
ep93xx_rtc_get_swcomp ( dev , NULL , & delete ) ;
2006-03-27 01:16:45 -08:00
return sprintf ( buf , " %d \n " , delete ) ;
}
2009-04-15 23:18:26 +01:00
static DEVICE_ATTR ( comp_delete , S_IRUGO , ep93xx_rtc_show_comp_delete , NULL ) ;
2006-03-27 01:16:45 -08:00
2010-03-05 13:44:20 -08:00
static struct attribute * ep93xx_rtc_attrs [ ] = {
& dev_attr_comp_preload . attr ,
& dev_attr_comp_delete . attr ,
NULL
} ;
static const struct attribute_group ep93xx_rtc_sysfs_files = {
. attrs = ep93xx_rtc_attrs ,
} ;
2006-03-27 01:16:45 -08:00
2009-04-15 23:18:26 +01:00
static int __init ep93xx_rtc_probe ( struct platform_device * pdev )
2006-03-27 01:16:45 -08:00
{
2009-04-15 23:18:26 +01:00
struct ep93xx_rtc * ep93xx_rtc ;
struct resource * res ;
struct rtc_device * rtc ;
int err ;
2010-03-05 13:44:20 -08:00
ep93xx_rtc = devm_kzalloc ( & pdev - > dev , sizeof ( * ep93xx_rtc ) , GFP_KERNEL ) ;
if ( ! ep93xx_rtc )
2009-04-15 23:18:26 +01:00
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2010-03-05 13:44:20 -08:00
if ( ! res )
return - ENXIO ;
2009-04-15 23:18:26 +01:00
2010-03-05 13:44:20 -08:00
if ( ! devm_request_mem_region ( & pdev - > dev , res - > start ,
resource_size ( res ) , pdev - > name ) )
return - EBUSY ;
2009-04-15 23:18:26 +01:00
2010-03-05 13:44:20 -08:00
ep93xx_rtc - > mmio_base = devm_ioremap ( & pdev - > dev , res - > start ,
resource_size ( res ) ) ;
if ( ! ep93xx_rtc - > mmio_base )
return - ENXIO ;
2006-03-27 01:16:45 -08:00
2009-04-15 23:18:26 +01:00
pdev - > dev . platform_data = ep93xx_rtc ;
rtc = rtc_device_register ( pdev - > name ,
& pdev - > dev , & ep93xx_rtc_ops , THIS_MODULE ) ;
2006-03-27 01:16:45 -08:00
if ( IS_ERR ( rtc ) ) {
2009-04-15 23:18:26 +01:00
err = PTR_ERR ( rtc ) ;
2010-03-05 13:44:20 -08:00
goto exit ;
2006-03-27 01:16:45 -08:00
}
2009-04-15 23:18:26 +01:00
platform_set_drvdata ( pdev , rtc ) ;
2006-03-27 01:16:45 -08:00
2010-03-05 13:44:20 -08:00
err = sysfs_create_group ( & pdev - > dev . kobj , & ep93xx_rtc_sysfs_files ) ;
2009-04-15 23:18:26 +01:00
if ( err )
goto fail ;
2006-03-27 01:16:45 -08:00
return 0 ;
2009-04-15 23:18:26 +01:00
fail :
2010-03-05 13:44:20 -08:00
platform_set_drvdata ( pdev , NULL ) ;
rtc_device_unregister ( rtc ) ;
exit :
pdev - > dev . platform_data = NULL ;
2009-04-15 23:18:26 +01:00
return err ;
2006-03-27 01:16:45 -08:00
}
2009-04-15 23:18:26 +01:00
static int __exit ep93xx_rtc_remove ( struct platform_device * pdev )
2006-03-27 01:16:45 -08:00
{
2009-04-15 23:18:26 +01:00
struct rtc_device * rtc = platform_get_drvdata ( pdev ) ;
2010-03-05 13:44:20 -08:00
sysfs_remove_group ( & pdev - > dev . kobj , & ep93xx_rtc_sysfs_files ) ;
platform_set_drvdata ( pdev , NULL ) ;
2009-04-15 23:18:26 +01:00
rtc_device_unregister ( rtc ) ;
pdev - > dev . platform_data = NULL ;
2006-03-27 01:16:45 -08:00
return 0 ;
}
2008-04-10 21:29:25 -07:00
/* work with hotplug and coldplug */
MODULE_ALIAS ( " platform:ep93xx-rtc " ) ;
2009-04-15 23:18:26 +01:00
static struct platform_driver ep93xx_rtc_driver = {
2006-03-27 01:16:45 -08:00
. driver = {
. name = " ep93xx-rtc " ,
. owner = THIS_MODULE ,
} ,
2009-04-15 23:18:26 +01:00
. remove = __exit_p ( ep93xx_rtc_remove ) ,
2006-03-27 01:16:45 -08:00
} ;
static int __init ep93xx_rtc_init ( void )
{
2009-04-15 23:18:26 +01:00
return platform_driver_probe ( & ep93xx_rtc_driver , ep93xx_rtc_probe ) ;
2006-03-27 01:16:45 -08:00
}
static void __exit ep93xx_rtc_exit ( void )
{
2009-04-15 23:18:26 +01:00
platform_driver_unregister ( & ep93xx_rtc_driver ) ;
2006-03-27 01:16:45 -08: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 ) ;