2019-03-14 01:02:48 +03:00
// SPDX-License-Identifier: GPL-2.0
2006-03-27 13:16:39 +04:00
/*
* RTC subsystem , sysfs interface
*
* Copyright ( C ) 2005 Tower Technologies
* Author : Alessandro Zummo < a . zummo @ towertech . it >
2019-03-14 01:02:48 +03:00
*/
2006-03-27 13:16:39 +04:00
# include <linux/module.h>
# include <linux/rtc.h>
2007-05-08 11:33:30 +04:00
# include "rtc-core.h"
2006-03-27 13:16:39 +04:00
/* device attributes */
2008-02-06 12:38:45 +03:00
/*
* NOTE : RTC times displayed in sysfs use the RTC ' s timezone . That ' s
* ideally UTC . However , PCs that also boot to MS - Windows normally use
* the local time and change to match daylight savings time . That affects
* attributes including date , time , since_epoch , and wakealarm .
*/
2007-05-08 11:33:40 +04:00
static ssize_t
2013-07-25 02:05:22 +04:00
name_show ( struct device * dev , struct device_attribute * attr , char * buf )
2006-03-27 13:16:39 +04:00
{
2017-06-02 14:57:03 +03:00
return sprintf ( buf , " %s %s \n " , dev_driver_string ( dev - > parent ) ,
dev_name ( dev - > parent ) ) ;
2006-03-27 13:16:39 +04:00
}
2013-07-25 02:05:22 +04:00
static DEVICE_ATTR_RO ( name ) ;
2006-03-27 13:16:39 +04:00
2007-05-08 11:33:40 +04:00
static ssize_t
2013-07-25 02:05:22 +04:00
date_show ( struct device * dev , struct device_attribute * attr , char * buf )
2006-03-27 13:16:39 +04:00
{
ssize_t retval ;
struct rtc_time tm ;
2007-05-08 11:33:30 +04:00
retval = rtc_read_time ( to_rtc_device ( dev ) , & tm ) ;
2018-12-05 00:23:12 +03:00
if ( retval )
return retval ;
2006-03-27 13:16:39 +04:00
2018-12-05 00:23:12 +03:00
return sprintf ( buf , " %ptRd \n " , & tm ) ;
2006-03-27 13:16:39 +04:00
}
2013-07-25 02:05:22 +04:00
static DEVICE_ATTR_RO ( date ) ;
2006-03-27 13:16:39 +04:00
2007-05-08 11:33:40 +04:00
static ssize_t
2013-07-25 02:05:22 +04:00
time_show ( struct device * dev , struct device_attribute * attr , char * buf )
2006-03-27 13:16:39 +04:00
{
ssize_t retval ;
struct rtc_time tm ;
2007-05-08 11:33:30 +04:00
retval = rtc_read_time ( to_rtc_device ( dev ) , & tm ) ;
2018-12-05 00:23:12 +03:00
if ( retval )
return retval ;
2006-03-27 13:16:39 +04:00
2018-12-05 00:23:12 +03:00
return sprintf ( buf , " %ptRt \n " , & tm ) ;
2006-03-27 13:16:39 +04:00
}
2013-07-25 02:05:22 +04:00
static DEVICE_ATTR_RO ( time ) ;
2006-03-27 13:16:39 +04:00
2007-05-08 11:33:40 +04:00
static ssize_t
2013-07-25 02:05:22 +04:00
since_epoch_show ( struct device * dev , struct device_attribute * attr , char * buf )
2006-03-27 13:16:39 +04:00
{
ssize_t retval ;
struct rtc_time tm ;
2007-05-08 11:33:30 +04:00
retval = rtc_read_time ( to_rtc_device ( dev ) , & tm ) ;
2006-03-27 13:16:39 +04:00
if ( retval = = 0 ) {
2017-11-09 10:09:20 +03:00
time64_t time ;
time = rtc_tm_to_time64 ( & tm ) ;
retval = sprintf ( buf , " %lld \n " , time ) ;
2006-03-27 13:16:39 +04:00
}
return retval ;
}
2013-07-25 02:05:22 +04:00
static DEVICE_ATTR_RO ( since_epoch ) ;
2006-03-27 13:16:39 +04:00
2007-10-16 12:28:22 +04:00
static ssize_t
2013-07-25 02:05:22 +04:00
max_user_freq_show ( struct device * dev , struct device_attribute * attr , char * buf )
2007-10-16 12:28:22 +04:00
{
return sprintf ( buf , " %d \n " , to_rtc_device ( dev ) - > max_user_freq ) ;
}
static ssize_t
2013-07-25 02:05:22 +04:00
max_user_freq_store ( struct device * dev , struct device_attribute * attr ,
2019-03-20 14:59:09 +03:00
const char * buf , size_t n )
2007-10-16 12:28:22 +04:00
{
struct rtc_device * rtc = to_rtc_device ( dev ) ;
2015-12-17 16:11:04 +03:00
unsigned long val ;
int err ;
err = kstrtoul ( buf , 0 , & val ) ;
if ( err )
return err ;
2007-10-16 12:28:22 +04:00
if ( val > = 4096 | | val = = 0 )
return - EINVAL ;
rtc - > max_user_freq = ( int ) val ;
return n ;
}
2013-07-25 02:05:22 +04:00
static DEVICE_ATTR_RW ( max_user_freq ) ;
2007-10-16 12:28:22 +04:00
2012-10-05 04:14:12 +04:00
/**
2021-05-17 07:59:01 +03:00
* hctosys_show - indicate if the given RTC set the system time
2019-11-22 13:22:05 +03:00
* @ dev : The device that the attribute belongs to .
* @ attr : The attribute being read .
* @ buf : The result buffer .
2012-10-05 04:14:12 +04:00
*
2019-11-22 13:22:05 +03:00
* buf is " 1 " if the system clock was set by this RTC at the last
2012-10-05 04:14:12 +04:00
* boot or resume event .
*/
2009-09-23 03:46:32 +04:00
static ssize_t
2013-07-25 02:05:22 +04:00
hctosys_show ( struct device * dev , struct device_attribute * attr , char * buf )
2009-09-23 03:46:32 +04:00
{
# ifdef CONFIG_RTC_HCTOSYS_DEVICE
2010-03-11 02:20:35 +03:00
if ( rtc_hctosys_ret = = 0 & &
2019-03-20 14:59:09 +03:00
strcmp ( dev_name ( & to_rtc_device ( dev ) - > dev ) ,
CONFIG_RTC_HCTOSYS_DEVICE ) = = 0 )
2009-09-23 03:46:32 +04:00
return sprintf ( buf , " 1 \n " ) ;
# endif
2019-03-20 14:59:09 +03:00
return sprintf ( buf , " 0 \n " ) ;
2009-09-23 03:46:32 +04:00
}
2013-07-25 02:05:22 +04:00
static DEVICE_ATTR_RO ( hctosys ) ;
2007-02-12 11:52:47 +03:00
static ssize_t
2015-07-24 02:01:07 +03:00
wakealarm_show ( struct device * dev , struct device_attribute * attr , char * buf )
2007-02-12 11:52:47 +03:00
{
ssize_t retval ;
2017-11-09 10:09:20 +03:00
time64_t alarm ;
2007-02-12 11:52:47 +03:00
struct rtc_wkalrm alm ;
2008-02-06 12:38:45 +03:00
/* Don't show disabled alarms. For uniformity, RTC alarms are
* conceptually one - shot , even though some common RTCs ( on PCs )
* don ' t actually work that way .
2007-02-12 11:52:47 +03:00
*
2008-02-06 12:38:45 +03:00
* NOTE : RTC implementations where the alarm doesn ' t match an
* exact YYYY - MM - DD HH : MM [ : SS ] date * must * disable their RTC
* alarms after they trigger , to ensure one - shot semantics .
2007-02-12 11:52:47 +03:00
*/
2007-05-08 11:33:30 +04:00
retval = rtc_read_alarm ( to_rtc_device ( dev ) , & alm ) ;
2007-02-12 11:52:47 +03:00
if ( retval = = 0 & & alm . enabled ) {
2017-11-09 10:09:20 +03:00
alarm = rtc_tm_to_time64 ( & alm . time ) ;
retval = sprintf ( buf , " %lld \n " , alarm ) ;
2007-02-12 11:52:47 +03:00
}
return retval ;
}
static ssize_t
2015-07-24 02:01:07 +03:00
wakealarm_store ( struct device * dev , struct device_attribute * attr ,
2007-05-08 11:33:40 +04:00
const char * buf , size_t n )
2007-02-12 11:52:47 +03:00
{
ssize_t retval ;
2017-11-09 10:09:20 +03:00
time64_t now , alarm ;
time64_t push = 0 ;
2007-02-12 11:52:47 +03:00
struct rtc_wkalrm alm ;
2007-05-08 11:33:30 +04:00
struct rtc_device * rtc = to_rtc_device ( dev ) ;
2016-08-12 15:46:14 +03:00
const char * buf_ptr ;
2008-04-28 13:11:58 +04:00
int adjust = 0 ;
2007-02-12 11:52:47 +03:00
/* Only request alarms that trigger in the future. Disable them
* by writing another time , e . g . 0 meaning Jan 1 1970 UTC .
*/
2007-05-08 11:33:30 +04:00
retval = rtc_read_time ( rtc , & alm . time ) ;
2007-02-12 11:52:47 +03:00
if ( retval < 0 )
return retval ;
2017-11-09 10:09:20 +03:00
now = rtc_tm_to_time64 ( & alm . time ) ;
2007-02-12 11:52:47 +03:00
2016-08-12 15:46:14 +03:00
buf_ptr = buf ;
2008-04-28 13:11:58 +04:00
if ( * buf_ptr = = ' + ' ) {
buf_ptr + + ;
2013-07-04 02:07:03 +04:00
if ( * buf_ptr = = ' = ' ) {
buf_ptr + + ;
push = 1 ;
2019-03-20 14:59:09 +03:00
} else {
2013-07-04 02:07:03 +04:00
adjust = 1 ;
2019-03-20 14:59:09 +03:00
}
2008-04-28 13:11:58 +04:00
}
2017-11-09 10:09:20 +03:00
retval = kstrtos64 ( buf_ptr , 0 , & alarm ) ;
2015-12-17 16:11:04 +03:00
if ( retval )
return retval ;
2019-03-20 14:59:09 +03:00
if ( adjust )
2008-04-28 13:11:58 +04:00
alarm + = now ;
2013-07-04 02:07:03 +04:00
if ( alarm > now | | push ) {
2007-02-12 11:52:47 +03:00
/* Avoid accidentally clobbering active alarms; we can't
* entirely prevent that here , without even the minimal
* locking from the / dev / rtcN api .
*/
2007-05-08 11:33:30 +04:00
retval = rtc_read_alarm ( rtc , & alm ) ;
2007-02-12 11:52:47 +03:00
if ( retval < 0 )
return retval ;
2013-07-04 02:07:03 +04:00
if ( alm . enabled ) {
if ( push ) {
2017-11-09 10:09:20 +03:00
push = rtc_tm_to_time64 ( & alm . time ) ;
2013-07-04 02:07:03 +04:00
alarm + = push ;
} else
return - EBUSY ;
} else if ( push )
return - EINVAL ;
2007-02-12 11:52:47 +03:00
alm . enabled = 1 ;
} else {
alm . enabled = 0 ;
/* Provide a valid future alarm time. Linux isn't EFI,
* this time won ' t be ignored when disabling the alarm .
*/
alarm = now + 300 ;
}
2017-11-09 10:09:20 +03:00
rtc_time64_to_tm ( alarm , & alm . time ) ;
2007-02-12 11:52:47 +03:00
2007-05-08 11:33:30 +04:00
retval = rtc_set_alarm ( rtc , & alm ) ;
2007-02-12 11:52:47 +03:00
return ( retval < 0 ) ? retval : n ;
}
2015-07-24 02:01:07 +03:00
static DEVICE_ATTR_RW ( wakealarm ) ;
2007-02-12 11:52:47 +03:00
2016-02-05 23:41:12 +03:00
static ssize_t
offset_show ( struct device * dev , struct device_attribute * attr , char * buf )
{
ssize_t retval ;
long offset ;
retval = rtc_read_offset ( to_rtc_device ( dev ) , & offset ) ;
if ( retval = = 0 )
retval = sprintf ( buf , " %ld \n " , offset ) ;
return retval ;
}
static ssize_t
offset_store ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t n )
{
ssize_t retval ;
long offset ;
retval = kstrtol ( buf , 10 , & offset ) ;
if ( retval = = 0 )
retval = rtc_set_offset ( to_rtc_device ( dev ) , offset ) ;
return ( retval < 0 ) ? retval : n ;
}
static DEVICE_ATTR_RW ( offset ) ;
2018-02-17 16:58:40 +03:00
static ssize_t
range_show ( struct device * dev , struct device_attribute * attr , char * buf )
{
return sprintf ( buf , " [%lld,%llu] \n " , to_rtc_device ( dev ) - > range_min ,
to_rtc_device ( dev ) - > range_max ) ;
}
static DEVICE_ATTR_RO ( range ) ;
2015-07-24 02:01:08 +03:00
static struct attribute * rtc_attrs [ ] = {
& dev_attr_name . attr ,
& dev_attr_date . attr ,
& dev_attr_time . attr ,
& dev_attr_since_epoch . attr ,
& dev_attr_max_user_freq . attr ,
& dev_attr_hctosys . attr ,
& dev_attr_wakealarm . attr ,
2016-02-05 23:41:12 +03:00
& dev_attr_offset . attr ,
2018-02-17 16:58:40 +03:00
& dev_attr_range . attr ,
2015-07-24 02:01:08 +03:00
NULL ,
} ;
2007-02-12 11:52:47 +03:00
/* The reason to trigger an alarm with no process watching it (via sysfs)
* is its side effect : waking from a system state like suspend - to - RAM or
* suspend - to - disk . So : no attribute unless that side effect is possible .
* ( Userspace may disable that mechanism later . )
*/
2015-07-24 02:01:06 +03:00
static bool rtc_does_wakealarm ( struct rtc_device * rtc )
2007-02-12 11:52:47 +03:00
{
2007-05-08 11:33:40 +04:00
if ( ! device_can_wakeup ( rtc - > dev . parent ) )
2015-07-24 02:01:06 +03:00
return false ;
2021-04-30 00:44:03 +03:00
return ! ! test_bit ( RTC_FEATURE_ALARM , rtc - > features ) ;
2007-02-12 11:52:47 +03:00
}
2015-07-24 02:01:08 +03:00
static umode_t rtc_attr_is_visible ( struct kobject * kobj ,
struct attribute * attr , int n )
2006-03-27 13:16:39 +04:00
{
2020-02-25 05:19:23 +03:00
struct device * dev = kobj_to_dev ( kobj ) ;
2015-07-24 02:01:08 +03:00
struct rtc_device * rtc = to_rtc_device ( dev ) ;
umode_t mode = attr - > mode ;
2006-03-27 13:16:39 +04:00
2016-02-05 23:41:12 +03:00
if ( attr = = & dev_attr_wakealarm . attr ) {
2015-07-24 02:01:08 +03:00
if ( ! rtc_does_wakealarm ( rtc ) )
mode = 0 ;
2016-02-05 23:41:12 +03:00
} else if ( attr = = & dev_attr_offset . attr ) {
if ( ! rtc - > ops - > set_offset )
mode = 0 ;
2018-02-17 16:58:40 +03:00
} else if ( attr = = & dev_attr_range . attr ) {
if ( ! ( rtc - > range_max - rtc - > range_min ) )
mode = 0 ;
2016-02-05 23:41:12 +03:00
}
2006-03-27 13:16:39 +04:00
2015-07-24 02:01:08 +03:00
return mode ;
2006-03-27 13:16:39 +04:00
}
2015-07-24 02:01:08 +03:00
static struct attribute_group rtc_attr_group = {
. is_visible = rtc_attr_is_visible ,
. attrs = rtc_attrs ,
} ;
static const struct attribute_group * rtc_attr_groups [ ] = {
& rtc_attr_group ,
NULL
} ;
2006-03-27 13:16:39 +04:00
2015-07-24 02:01:08 +03:00
const struct attribute_group * * rtc_get_dev_attribute_groups ( void )
2006-03-27 13:16:39 +04:00
{
2015-07-24 02:01:08 +03:00
return rtc_attr_groups ;
2006-03-27 13:16:39 +04:00
}
2018-07-24 14:31:22 +03:00
int rtc_add_groups ( struct rtc_device * rtc , const struct attribute_group * * grps )
{
size_t old_cnt = 0 , add_cnt = 0 , new_cnt ;
const struct attribute_group * * groups , * * old ;
if ( ! grps )
return - EINVAL ;
groups = rtc - > dev . groups ;
if ( groups )
for ( ; * groups ; groups + + )
old_cnt + + ;
for ( groups = grps ; * groups ; groups + + )
add_cnt + + ;
new_cnt = old_cnt + add_cnt + 1 ;
groups = devm_kcalloc ( & rtc - > dev , new_cnt , sizeof ( * groups ) , GFP_KERNEL ) ;
2018-08-27 12:22:34 +03:00
if ( ! groups )
return - ENOMEM ;
2018-07-24 14:31:22 +03:00
memcpy ( groups , rtc - > dev . groups , old_cnt * sizeof ( * groups ) ) ;
memcpy ( groups + old_cnt , grps , add_cnt * sizeof ( * groups ) ) ;
groups [ old_cnt + add_cnt ] = NULL ;
old = rtc - > dev . groups ;
rtc - > dev . groups = groups ;
if ( old & & old ! = rtc_attr_groups )
devm_kfree ( & rtc - > dev , old ) ;
return 0 ;
}
EXPORT_SYMBOL ( rtc_add_groups ) ;
int rtc_add_group ( struct rtc_device * rtc , const struct attribute_group * grp )
{
const struct attribute_group * groups [ ] = { grp , NULL } ;
return rtc_add_groups ( rtc , groups ) ;
}
EXPORT_SYMBOL ( rtc_add_group ) ;