2019-05-29 17:17:56 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2011-05-27 03:25:03 +04:00
/*
* drivers / rtc / rtc - vt8500 . c
*
* Copyright ( C ) 2010 Alexey Charkov < alchark @ gmail . com >
*
* Based on rtc - pxa . c
*/
# include <linux/module.h>
# include <linux/rtc.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/bcd.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
2012-08-03 12:55:28 +04:00
# include <linux/of.h>
2011-05-27 03:25:03 +04:00
/*
* Register definitions
*/
# define VT8500_RTC_TS 0x00 /* Time set */
# define VT8500_RTC_DS 0x04 /* Date set */
# define VT8500_RTC_AS 0x08 /* Alarm set */
# define VT8500_RTC_CR 0x0c /* Control */
# define VT8500_RTC_TR 0x10 /* Time read */
# define VT8500_RTC_DR 0x14 /* Date read */
# define VT8500_RTC_WS 0x18 /* Write status */
# define VT8500_RTC_CL 0x20 /* Calibration */
# define VT8500_RTC_IS 0x24 /* Interrupt status */
# define VT8500_RTC_ST 0x28 /* Status */
# define INVALID_TIME_BIT (1 << 31)
# define DATE_CENTURY_S 19
# define DATE_YEAR_S 11
# define DATE_YEAR_MASK (0xff << DATE_YEAR_S)
# define DATE_MONTH_S 6
# define DATE_MONTH_MASK (0x1f << DATE_MONTH_S)
# define DATE_DAY_MASK 0x3f
# define TIME_DOW_S 20
# define TIME_DOW_MASK (0x07 << TIME_DOW_S)
# define TIME_HOUR_S 14
# define TIME_HOUR_MASK (0x3f << TIME_HOUR_S)
# define TIME_MIN_S 7
# define TIME_MIN_MASK (0x7f << TIME_MIN_S)
# define TIME_SEC_MASK 0x7f
# define ALARM_DAY_S 20
# define ALARM_DAY_MASK (0x3f << ALARM_DAY_S)
# define ALARM_DAY_BIT (1 << 29)
# define ALARM_HOUR_BIT (1 << 28)
# define ALARM_MIN_BIT (1 << 27)
# define ALARM_SEC_BIT (1 << 26)
# define ALARM_ENABLE_MASK (ALARM_DAY_BIT \
| ALARM_HOUR_BIT \
| ALARM_MIN_BIT \
| ALARM_SEC_BIT )
# define VT8500_RTC_CR_ENABLE (1 << 0) /* Enable RTC */
2013-01-05 03:35:47 +04:00
# define VT8500_RTC_CR_12H (1 << 1) /* 12h time format */
2011-05-27 03:25:03 +04:00
# define VT8500_RTC_CR_SM_ENABLE (1 << 2) /* Enable periodic irqs */
# define VT8500_RTC_CR_SM_SEC (1 << 3) /* 0: 1Hz/60, 1: 1Hz */
# define VT8500_RTC_CR_CALIB (1 << 4) /* Enable calibration */
2011-06-23 01:20:14 +04:00
# define VT8500_RTC_IS_ALARM (1 << 0) /* Alarm interrupt status */
2011-05-27 03:25:03 +04:00
struct vt8500_rtc {
void __iomem * regbase ;
int irq_alarm ;
struct rtc_device * rtc ;
spinlock_t lock ; /* Protects this structure */
} ;
static irqreturn_t vt8500_rtc_irq ( int irq , void * dev_id )
{
struct vt8500_rtc * vt8500_rtc = dev_id ;
u32 isr ;
unsigned long events = 0 ;
spin_lock ( & vt8500_rtc - > lock ) ;
/* clear interrupt sources */
isr = readl ( vt8500_rtc - > regbase + VT8500_RTC_IS ) ;
writel ( isr , vt8500_rtc - > regbase + VT8500_RTC_IS ) ;
spin_unlock ( & vt8500_rtc - > lock ) ;
2011-06-23 01:20:14 +04:00
if ( isr & VT8500_RTC_IS_ALARM )
2011-05-27 03:25:03 +04:00
events | = RTC_AF | RTC_IRQF ;
rtc_update_irq ( vt8500_rtc - > rtc , 1 , events ) ;
return IRQ_HANDLED ;
}
static int vt8500_rtc_read_time ( struct device * dev , struct rtc_time * tm )
{
struct vt8500_rtc * vt8500_rtc = dev_get_drvdata ( dev ) ;
u32 date , time ;
date = readl ( vt8500_rtc - > regbase + VT8500_RTC_DR ) ;
time = readl ( vt8500_rtc - > regbase + VT8500_RTC_TR ) ;
tm - > tm_sec = bcd2bin ( time & TIME_SEC_MASK ) ;
tm - > tm_min = bcd2bin ( ( time & TIME_MIN_MASK ) > > TIME_MIN_S ) ;
tm - > tm_hour = bcd2bin ( ( time & TIME_HOUR_MASK ) > > TIME_HOUR_S ) ;
tm - > tm_mday = bcd2bin ( date & DATE_DAY_MASK ) ;
2013-01-05 03:35:48 +04:00
tm - > tm_mon = bcd2bin ( ( date & DATE_MONTH_MASK ) > > DATE_MONTH_S ) - 1 ;
2011-05-27 03:25:03 +04:00
tm - > tm_year = bcd2bin ( ( date & DATE_YEAR_MASK ) > > DATE_YEAR_S )
+ ( ( date > > DATE_CENTURY_S ) & 1 ? 200 : 100 ) ;
tm - > tm_wday = ( time & TIME_DOW_MASK ) > > TIME_DOW_S ;
return 0 ;
}
static int vt8500_rtc_set_time ( struct device * dev , struct rtc_time * tm )
{
struct vt8500_rtc * vt8500_rtc = dev_get_drvdata ( dev ) ;
2013-02-05 02:28:44 +04:00
writel ( ( bin2bcd ( tm - > tm_year % 100 ) < < DATE_YEAR_S )
2013-01-05 03:35:48 +04:00
| ( bin2bcd ( tm - > tm_mon + 1 ) < < DATE_MONTH_S )
| ( bin2bcd ( tm - > tm_mday ) )
| ( ( tm - > tm_year > = 200 ) < < DATE_CENTURY_S ) ,
2011-05-27 03:25:03 +04:00
vt8500_rtc - > regbase + VT8500_RTC_DS ) ;
writel ( ( bin2bcd ( tm - > tm_wday ) < < TIME_DOW_S )
| ( bin2bcd ( tm - > tm_hour ) < < TIME_HOUR_S )
| ( bin2bcd ( tm - > tm_min ) < < TIME_MIN_S )
| ( bin2bcd ( tm - > tm_sec ) ) ,
vt8500_rtc - > regbase + VT8500_RTC_TS ) ;
return 0 ;
}
static int vt8500_rtc_read_alarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
struct vt8500_rtc * vt8500_rtc = dev_get_drvdata ( dev ) ;
u32 isr , alarm ;
alarm = readl ( vt8500_rtc - > regbase + VT8500_RTC_AS ) ;
isr = readl ( vt8500_rtc - > regbase + VT8500_RTC_IS ) ;
alrm - > time . tm_mday = bcd2bin ( ( alarm & ALARM_DAY_MASK ) > > ALARM_DAY_S ) ;
alrm - > time . tm_hour = bcd2bin ( ( alarm & TIME_HOUR_MASK ) > > TIME_HOUR_S ) ;
alrm - > time . tm_min = bcd2bin ( ( alarm & TIME_MIN_MASK ) > > TIME_MIN_S ) ;
alrm - > time . tm_sec = bcd2bin ( ( alarm & TIME_SEC_MASK ) ) ;
alrm - > enabled = ( alarm & ALARM_ENABLE_MASK ) ? 1 : 0 ;
2011-06-23 01:20:14 +04:00
alrm - > pending = ( isr & VT8500_RTC_IS_ALARM ) ? 1 : 0 ;
2011-05-27 03:25:03 +04:00
return rtc_valid_tm ( & alrm - > time ) ;
}
static int vt8500_rtc_set_alarm ( struct device * dev , struct rtc_wkalrm * alrm )
{
struct vt8500_rtc * vt8500_rtc = dev_get_drvdata ( dev ) ;
writel ( ( alrm - > enabled ? ALARM_ENABLE_MASK : 0 )
| ( bin2bcd ( alrm - > time . tm_mday ) < < ALARM_DAY_S )
| ( bin2bcd ( alrm - > time . tm_hour ) < < TIME_HOUR_S )
| ( bin2bcd ( alrm - > time . tm_min ) < < TIME_MIN_S )
| ( bin2bcd ( alrm - > time . tm_sec ) ) ,
vt8500_rtc - > regbase + VT8500_RTC_AS ) ;
return 0 ;
}
static int vt8500_alarm_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_AS ) ;
if ( enabled )
tmp | = ALARM_ENABLE_MASK ;
else
tmp & = ~ ALARM_ENABLE_MASK ;
writel ( tmp , vt8500_rtc - > regbase + VT8500_RTC_AS ) ;
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 ,
} ;
2012-12-22 01:09:38 +04:00
static int vt8500_rtc_probe ( struct platform_device * pdev )
2011-05-27 03:25:03 +04:00
{
struct vt8500_rtc * vt8500_rtc ;
int ret ;
2012-12-18 04:02:26 +04:00
vt8500_rtc = devm_kzalloc ( & pdev - > dev ,
sizeof ( struct vt8500_rtc ) , GFP_KERNEL ) ;
2011-05-27 03:25:03 +04:00
if ( ! vt8500_rtc )
return - ENOMEM ;
spin_lock_init ( & vt8500_rtc - > lock ) ;
platform_set_drvdata ( pdev , vt8500_rtc ) ;
vt8500_rtc - > irq_alarm = platform_get_irq ( pdev , 0 ) ;
2019-07-30 21:15:39 +03:00
if ( vt8500_rtc - > irq_alarm < 0 )
2013-11-13 03:10:28 +04:00
return vt8500_rtc - > irq_alarm ;
2011-05-27 03:25:03 +04:00
2019-10-06 13:29:20 +03:00
vt8500_rtc - > regbase = devm_platform_ioremap_resource ( pdev , 0 ) ;
2014-04-04 01:49:49 +04:00
if ( IS_ERR ( vt8500_rtc - > regbase ) )
return PTR_ERR ( vt8500_rtc - > regbase ) ;
2011-05-27 03:25:03 +04:00
2011-06-23 01:20:13 +04:00
/* Enable RTC and set it to 24-hour mode */
2013-01-05 03:35:47 +04:00
writel ( VT8500_RTC_CR_ENABLE ,
2011-05-27 03:25:03 +04:00
vt8500_rtc - > regbase + VT8500_RTC_CR ) ;
2019-10-16 23:16:25 +03:00
vt8500_rtc - > rtc = devm_rtc_allocate_device ( & pdev - > dev ) ;
2019-10-16 23:16:24 +03:00
if ( IS_ERR ( vt8500_rtc - > rtc ) )
2019-10-16 23:16:23 +03:00
return PTR_ERR ( vt8500_rtc - > rtc ) ;
2011-05-27 03:25:03 +04:00
2019-10-16 23:16:25 +03:00
vt8500_rtc - > rtc - > ops = & vt8500_rtc_ops ;
2019-10-16 23:16:26 +03:00
vt8500_rtc - > rtc - > range_min = RTC_TIMESTAMP_BEGIN_2000 ;
vt8500_rtc - > rtc - > range_max = RTC_TIMESTAMP_END_2199 ;
2019-10-16 23:16:25 +03:00
2013-02-22 04:45:37 +04:00
ret = devm_request_irq ( & pdev - > dev , vt8500_rtc - > irq_alarm ,
vt8500_rtc_irq , 0 , " rtc alarm " , vt8500_rtc ) ;
2011-05-27 03:25:03 +04:00
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " can't get irq %i, err %d \n " ,
vt8500_rtc - > irq_alarm , ret ) ;
2019-10-16 23:16:23 +03:00
return ret ;
2011-05-27 03:25:03 +04:00
}
2020-11-09 19:34:08 +03:00
return devm_rtc_register_device ( vt8500_rtc - > rtc ) ;
2011-05-27 03:25:03 +04:00
}
2012-12-22 01:09:38 +04:00
static int vt8500_rtc_remove ( struct platform_device * pdev )
2011-05-27 03:25:03 +04:00
{
struct vt8500_rtc * vt8500_rtc = platform_get_drvdata ( pdev ) ;
/* Disable alarm matching */
writel ( 0 , vt8500_rtc - > regbase + VT8500_RTC_IS ) ;
return 0 ;
}
2012-08-03 12:55:28 +04:00
static const struct of_device_id wmt_dt_ids [ ] = {
{ . compatible = " via,vt8500-rtc " , } ,
{ }
} ;
2015-08-27 14:52:02 +03:00
MODULE_DEVICE_TABLE ( of , wmt_dt_ids ) ;
2012-08-03 12:55:28 +04:00
2011-05-27 03:25:03 +04:00
static struct platform_driver vt8500_rtc_driver = {
. probe = vt8500_rtc_probe ,
2012-12-22 01:09:38 +04:00
. remove = vt8500_rtc_remove ,
2011-05-27 03:25:03 +04:00
. driver = {
. name = " vt8500-rtc " ,
2013-11-13 03:10:54 +04:00
. of_match_table = wmt_dt_ids ,
2011-05-27 03:25:03 +04:00
} ,
} ;
2012-01-11 03:10:48 +04:00
module_platform_driver ( vt8500_rtc_driver ) ;
2011-05-27 03:25:03 +04:00
MODULE_AUTHOR ( " Alexey Charkov <alchark@gmail.com> " ) ;
MODULE_DESCRIPTION ( " VIA VT8500 SoC Realtime Clock Driver (RTC) " ) ;
2012-08-03 12:55:28 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
2011-05-27 03:25:03 +04:00
MODULE_ALIAS ( " platform:vt8500-rtc " ) ;