2005-04-16 15:20:36 -07:00
/*
* HP i8042 SDC + MSM - 58321 BBRTC driver .
*
* Copyright ( c ) 2001 Brian S . Julin
* All rights reserved .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
* 1. Redistributions of source code must retain the above copyright
* notice , this list of conditions , and the following disclaimer ,
* without modification .
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* Alternatively , this software may be distributed under the terms of the
* GNU General Public License ( " GPL " ) .
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ` ` AS IS ' ' AND
* ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED . IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL
* DAMAGES ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION )
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT
* LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY
*
* References :
* System Device Controller Microprocessor Firmware Theory of Operation
* for Part Number 1820 - 4784 Revision B . Dwg No . A - 1820 - 4784 - 2
* efirtc . c by Stephane Eranian / Hewlett Packard
*
*/
# include <linux/hp_sdc.h>
# include <linux/errno.h>
# include <linux/types.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/time.h>
# include <linux/miscdevice.h>
# include <linux/proc_fs.h>
2013-04-10 15:52:18 +01:00
# include <linux/seq_file.h>
2005-04-16 15:20:36 -07:00
# include <linux/poll.h>
# include <linux/rtc.h>
2010-06-02 14:28:52 +02:00
# include <linux/mutex.h>
2008-07-24 08:30:48 -04:00
# include <linux/semaphore.h>
2005-04-16 15:20:36 -07:00
MODULE_AUTHOR ( " Brian S. Julin <bri@calyx.com> " ) ;
MODULE_DESCRIPTION ( " HP i8042 SDC + MSM-58321 RTC Driver " ) ;
MODULE_LICENSE ( " Dual BSD/GPL " ) ;
# define RTC_VERSION "1.10d"
2010-06-02 14:28:52 +02:00
static DEFINE_MUTEX ( hp_sdc_rtc_mutex ) ;
2005-04-16 15:20:36 -07:00
static unsigned long epoch = 2000 ;
static struct semaphore i8042tregs ;
static hp_sdc_irqhook hp_sdc_rtc_isr ;
static struct fasync_struct * hp_sdc_rtc_async_queue ;
static DECLARE_WAIT_QUEUE_HEAD ( hp_sdc_rtc_wait ) ;
2006-10-14 16:52:36 +01:00
static ssize_t hp_sdc_rtc_read ( struct file * file , char __user * buf ,
2005-04-16 15:20:36 -07:00
size_t count , loff_t * ppos ) ;
2010-04-27 00:24:05 +02:00
static long hp_sdc_rtc_unlocked_ioctl ( struct file * file ,
unsigned int cmd , unsigned long arg ) ;
2005-04-16 15:20:36 -07:00
static unsigned int hp_sdc_rtc_poll ( struct file * file , poll_table * wait ) ;
static int hp_sdc_rtc_open ( struct inode * inode , struct file * file ) ;
static int hp_sdc_rtc_fasync ( int fd , struct file * filp , int on ) ;
static void hp_sdc_rtc_isr ( int irq , void * dev_id ,
uint8_t status , uint8_t data )
{
return ;
}
static int hp_sdc_rtc_do_read_bbrtc ( struct rtc_time * rtctm )
{
struct semaphore tsem ;
hp_sdc_transaction t ;
uint8_t tseq [ 91 ] ;
int i ;
i = 0 ;
while ( i < 91 ) {
tseq [ i + + ] = HP_SDC_ACT_DATAREG |
HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN ;
tseq [ i + + ] = 0x01 ; /* write i8042[0x70] */
tseq [ i ] = i / 7 ; /* BBRTC reg address */
i + + ;
tseq [ i + + ] = HP_SDC_CMD_DO_RTCR ; /* Trigger command */
tseq [ i + + ] = 2 ; /* expect 1 stat/dat pair back. */
i + + ; i + + ; /* buffer for stat/dat pair */
}
tseq [ 84 ] | = HP_SDC_ACT_SEMAPHORE ;
t . endidx = 91 ;
t . seq = tseq ;
t . act . semaphore = & tsem ;
2010-09-07 14:31:58 +00:00
sema_init ( & tsem , 0 ) ;
2005-04-16 15:20:36 -07:00
if ( hp_sdc_enqueue_transaction ( & t ) ) return - 1 ;
2013-02-27 22:46:56 +01:00
/* Put ourselves to sleep for results. */
if ( WARN_ON ( down_interruptible ( & tsem ) ) )
return - 1 ;
2005-04-16 15:20:36 -07:00
/* Check for nonpresence of BBRTC */
if ( ! ( ( tseq [ 83 ] | tseq [ 90 ] | tseq [ 69 ] | tseq [ 76 ] |
tseq [ 55 ] | tseq [ 62 ] | tseq [ 34 ] | tseq [ 41 ] |
tseq [ 20 ] | tseq [ 27 ] | tseq [ 6 ] | tseq [ 13 ] ) & 0x0f ) )
return - 1 ;
memset ( rtctm , 0 , sizeof ( struct rtc_time ) ) ;
rtctm - > tm_year = ( tseq [ 83 ] & 0x0f ) + ( tseq [ 90 ] & 0x0f ) * 10 ;
rtctm - > tm_mon = ( tseq [ 69 ] & 0x0f ) + ( tseq [ 76 ] & 0x0f ) * 10 ;
rtctm - > tm_mday = ( tseq [ 55 ] & 0x0f ) + ( tseq [ 62 ] & 0x0f ) * 10 ;
rtctm - > tm_wday = ( tseq [ 48 ] & 0x0f ) ;
rtctm - > tm_hour = ( tseq [ 34 ] & 0x0f ) + ( tseq [ 41 ] & 0x0f ) * 10 ;
rtctm - > tm_min = ( tseq [ 20 ] & 0x0f ) + ( tseq [ 27 ] & 0x0f ) * 10 ;
rtctm - > tm_sec = ( tseq [ 6 ] & 0x0f ) + ( tseq [ 13 ] & 0x0f ) * 10 ;
return 0 ;
}
static int hp_sdc_rtc_read_bbrtc ( struct rtc_time * rtctm )
{
struct rtc_time tm , tm_last ;
int i = 0 ;
/* MSM-58321 has no read latch, so must read twice and compare. */
if ( hp_sdc_rtc_do_read_bbrtc ( & tm_last ) ) return - 1 ;
if ( hp_sdc_rtc_do_read_bbrtc ( & tm ) ) return - 1 ;
while ( memcmp ( & tm , & tm_last , sizeof ( struct rtc_time ) ) ) {
if ( i + + > 4 ) return - 1 ;
memcpy ( & tm_last , & tm , sizeof ( struct rtc_time ) ) ;
if ( hp_sdc_rtc_do_read_bbrtc ( & tm ) ) return - 1 ;
}
memcpy ( rtctm , & tm , sizeof ( struct rtc_time ) ) ;
return 0 ;
}
static int64_t hp_sdc_rtc_read_i8042timer ( uint8_t loadcmd , int numreg )
{
hp_sdc_transaction t ;
uint8_t tseq [ 26 ] = {
HP_SDC_ACT_PRECMD | HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN ,
0 ,
HP_SDC_CMD_READ_T1 , 2 , 0 , 0 ,
HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN ,
HP_SDC_CMD_READ_T2 , 2 , 0 , 0 ,
HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN ,
HP_SDC_CMD_READ_T3 , 2 , 0 , 0 ,
HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN ,
HP_SDC_CMD_READ_T4 , 2 , 0 , 0 ,
HP_SDC_ACT_POSTCMD | HP_SDC_ACT_DATAIN ,
HP_SDC_CMD_READ_T5 , 2 , 0 , 0
} ;
t . endidx = numreg * 5 ;
tseq [ 1 ] = loadcmd ;
tseq [ t . endidx - 4 ] | = HP_SDC_ACT_SEMAPHORE ; /* numreg assumed > 1 */
t . seq = tseq ;
t . act . semaphore = & i8042tregs ;
2013-02-27 22:46:56 +01:00
/* Sleep if output regs in use. */
if ( WARN_ON ( down_interruptible ( & i8042tregs ) ) )
return - 1 ;
2005-04-16 15:20:36 -07:00
2013-11-19 13:28:06 -08:00
if ( hp_sdc_enqueue_transaction ( & t ) ) {
up ( & i8042tregs ) ;
return - 1 ;
}
2005-04-16 15:20:36 -07:00
2013-02-27 22:46:56 +01:00
/* Sleep until results come back. */
if ( WARN_ON ( down_interruptible ( & i8042tregs ) ) )
return - 1 ;
2005-04-16 15:20:36 -07:00
up ( & i8042tregs ) ;
return ( tseq [ 5 ] |
( ( uint64_t ) ( tseq [ 10 ] ) < < 8 ) | ( ( uint64_t ) ( tseq [ 15 ] ) < < 16 ) |
( ( uint64_t ) ( tseq [ 20 ] ) < < 24 ) | ( ( uint64_t ) ( tseq [ 25 ] ) < < 32 ) ) ;
}
/* Read the i8042 real-time clock */
2015-10-18 16:33:47 -07:00
static inline int hp_sdc_rtc_read_rt ( struct timespec64 * res ) {
2005-04-16 15:20:36 -07:00
int64_t raw ;
uint32_t tenms ;
unsigned int days ;
raw = hp_sdc_rtc_read_i8042timer ( HP_SDC_CMD_LOAD_RT , 5 ) ;
if ( raw < 0 ) return - 1 ;
tenms = ( uint32_t ) raw & 0xffffff ;
days = ( unsigned int ) ( raw > > 24 ) & 0xffff ;
2015-10-18 16:33:47 -07:00
res - > tv_nsec = ( long ) ( tenms % 100 ) * 10000 * 1000 ;
res - > tv_sec = ( tenms / 100 ) + ( time64_t ) days * 86400 ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
/* Read the i8042 fast handshake timer */
2015-10-18 16:33:47 -07:00
static inline int hp_sdc_rtc_read_fhs ( struct timespec64 * res ) {
2009-10-18 00:17:15 -07:00
int64_t raw ;
2005-04-16 15:20:36 -07:00
unsigned int tenms ;
raw = hp_sdc_rtc_read_i8042timer ( HP_SDC_CMD_LOAD_FHS , 2 ) ;
if ( raw < 0 ) return - 1 ;
tenms = ( unsigned int ) raw & 0xffff ;
2015-10-18 16:33:47 -07:00
res - > tv_nsec = ( long ) ( tenms % 100 ) * 10000 * 1000 ;
res - > tv_sec = ( time64_t ) ( tenms / 100 ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
/* Read the i8042 match timer (a.k.a. alarm) */
2015-10-18 16:33:47 -07:00
static inline int hp_sdc_rtc_read_mt ( struct timespec64 * res ) {
2005-04-16 15:20:36 -07:00
int64_t raw ;
uint32_t tenms ;
raw = hp_sdc_rtc_read_i8042timer ( HP_SDC_CMD_LOAD_MT , 3 ) ;
if ( raw < 0 ) return - 1 ;
tenms = ( uint32_t ) raw & 0xffffff ;
2015-10-18 16:33:47 -07:00
res - > tv_nsec = ( long ) ( tenms % 100 ) * 10000 * 1000 ;
res - > tv_sec = ( time64_t ) ( tenms / 100 ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
/* Read the i8042 delay timer */
2015-10-18 16:33:47 -07:00
static inline int hp_sdc_rtc_read_dt ( struct timespec64 * res ) {
2005-04-16 15:20:36 -07:00
int64_t raw ;
uint32_t tenms ;
raw = hp_sdc_rtc_read_i8042timer ( HP_SDC_CMD_LOAD_DT , 3 ) ;
if ( raw < 0 ) return - 1 ;
tenms = ( uint32_t ) raw & 0xffffff ;
2015-10-18 16:33:47 -07:00
res - > tv_nsec = ( long ) ( tenms % 100 ) * 10000 * 1000 ;
res - > tv_sec = ( time64_t ) ( tenms / 100 ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
/* Read the i8042 cycle timer (a.k.a. periodic) */
2015-10-18 16:33:47 -07:00
static inline int hp_sdc_rtc_read_ct ( struct timespec64 * res ) {
2005-04-16 15:20:36 -07:00
int64_t raw ;
uint32_t tenms ;
raw = hp_sdc_rtc_read_i8042timer ( HP_SDC_CMD_LOAD_CT , 3 ) ;
if ( raw < 0 ) return - 1 ;
tenms = ( uint32_t ) raw & 0xffffff ;
2015-10-18 16:33:47 -07:00
res - > tv_nsec = ( long ) ( tenms % 100 ) * 10000 * 1000 ;
res - > tv_sec = ( time64_t ) ( tenms / 100 ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2013-02-27 22:46:56 +01:00
#if 0 /* not used yet */
2005-04-16 15:20:36 -07:00
/* Set the i8042 real-time clock */
static int hp_sdc_rtc_set_rt ( struct timeval * setto )
{
uint32_t tenms ;
unsigned int days ;
hp_sdc_transaction t ;
uint8_t tseq [ 11 ] = {
HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT ,
HP_SDC_CMD_SET_RTMS , 3 , 0 , 0 , 0 ,
HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT ,
HP_SDC_CMD_SET_RTD , 2 , 0 , 0
} ;
t . endidx = 10 ;
if ( 0xffff < setto - > tv_sec / 86400 ) return - 1 ;
days = setto - > tv_sec / 86400 ;
if ( 0xffff < setto - > tv_usec / 1000000 / 86400 ) return - 1 ;
days + = ( ( setto - > tv_sec % 86400 ) + setto - > tv_usec / 1000000 ) / 86400 ;
if ( days > 0xffff ) return - 1 ;
if ( 0xffffff < setto - > tv_sec ) return - 1 ;
tenms = setto - > tv_sec * 100 ;
if ( 0xffffff < setto - > tv_usec / 10000 ) return - 1 ;
tenms + = setto - > tv_usec / 10000 ;
if ( tenms > 0xffffff ) return - 1 ;
tseq [ 3 ] = ( uint8_t ) ( tenms & 0xff ) ;
tseq [ 4 ] = ( uint8_t ) ( ( tenms > > 8 ) & 0xff ) ;
tseq [ 5 ] = ( uint8_t ) ( ( tenms > > 16 ) & 0xff ) ;
tseq [ 9 ] = ( uint8_t ) ( days & 0xff ) ;
tseq [ 10 ] = ( uint8_t ) ( ( days > > 8 ) & 0xff ) ;
t . seq = tseq ;
if ( hp_sdc_enqueue_transaction ( & t ) ) return - 1 ;
return 0 ;
}
/* Set the i8042 fast handshake timer */
static int hp_sdc_rtc_set_fhs ( struct timeval * setto )
{
uint32_t tenms ;
hp_sdc_transaction t ;
uint8_t tseq [ 5 ] = {
HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT ,
HP_SDC_CMD_SET_FHS , 2 , 0 , 0
} ;
t . endidx = 4 ;
if ( 0xffff < setto - > tv_sec ) return - 1 ;
tenms = setto - > tv_sec * 100 ;
if ( 0xffff < setto - > tv_usec / 10000 ) return - 1 ;
tenms + = setto - > tv_usec / 10000 ;
if ( tenms > 0xffff ) return - 1 ;
tseq [ 3 ] = ( uint8_t ) ( tenms & 0xff ) ;
tseq [ 4 ] = ( uint8_t ) ( ( tenms > > 8 ) & 0xff ) ;
t . seq = tseq ;
if ( hp_sdc_enqueue_transaction ( & t ) ) return - 1 ;
return 0 ;
}
/* Set the i8042 match timer (a.k.a. alarm) */
# define hp_sdc_rtc_set_mt (setto) \
hp_sdc_rtc_set_i8042timer ( setto , HP_SDC_CMD_SET_MT )
/* Set the i8042 delay timer */
# define hp_sdc_rtc_set_dt (setto) \
hp_sdc_rtc_set_i8042timer ( setto , HP_SDC_CMD_SET_DT )
/* Set the i8042 cycle timer (a.k.a. periodic) */
# define hp_sdc_rtc_set_ct (setto) \
hp_sdc_rtc_set_i8042timer ( setto , HP_SDC_CMD_SET_CT )
/* Set one of the i8042 3-byte wide timers */
static int hp_sdc_rtc_set_i8042timer ( struct timeval * setto , uint8_t setcmd )
{
uint32_t tenms ;
hp_sdc_transaction t ;
uint8_t tseq [ 6 ] = {
HP_SDC_ACT_PRECMD | HP_SDC_ACT_DATAOUT ,
0 , 3 , 0 , 0 , 0
} ;
t . endidx = 6 ;
if ( 0xffffff < setto - > tv_sec ) return - 1 ;
tenms = setto - > tv_sec * 100 ;
if ( 0xffffff < setto - > tv_usec / 10000 ) return - 1 ;
tenms + = setto - > tv_usec / 10000 ;
if ( tenms > 0xffffff ) return - 1 ;
tseq [ 1 ] = setcmd ;
tseq [ 3 ] = ( uint8_t ) ( tenms & 0xff ) ;
tseq [ 4 ] = ( uint8_t ) ( ( tenms > > 8 ) & 0xff ) ;
tseq [ 5 ] = ( uint8_t ) ( ( tenms > > 16 ) & 0xff ) ;
t . seq = tseq ;
if ( hp_sdc_enqueue_transaction ( & t ) ) {
return - 1 ;
}
return 0 ;
}
2013-02-27 22:46:56 +01:00
# endif
2005-04-16 15:20:36 -07:00
2006-10-14 16:52:36 +01:00
static ssize_t hp_sdc_rtc_read ( struct file * file , char __user * buf ,
2005-04-16 15:20:36 -07:00
size_t count , loff_t * ppos ) {
ssize_t retval ;
if ( count < sizeof ( unsigned long ) )
return - EINVAL ;
2006-10-14 16:52:36 +01:00
retval = put_user ( 68 , ( unsigned long __user * ) buf ) ;
2005-04-16 15:20:36 -07:00
return retval ;
}
2017-07-03 06:39:46 -04:00
static __poll_t hp_sdc_rtc_poll ( struct file * file , poll_table * wait )
2005-04-16 15:20:36 -07:00
{
unsigned long l ;
l = 0 ;
if ( l ! = 0 )
2018-02-11 14:34:03 -08:00
return EPOLLIN | EPOLLRDNORM ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
static int hp_sdc_rtc_open ( struct inode * inode , struct file * file )
{
return 0 ;
}
static int hp_sdc_rtc_fasync ( int fd , struct file * filp , int on )
{
return fasync_helper ( fd , filp , on , & hp_sdc_rtc_async_queue ) ;
}
2013-04-10 15:52:18 +01:00
static int hp_sdc_rtc_proc_show ( struct seq_file * m , void * v )
2005-04-16 15:20:36 -07:00
{
# define YN(bit) ("no")
# define NY(bit) ("yes")
struct rtc_time tm ;
2015-10-18 16:33:47 -07:00
struct timespec64 tv ;
2005-04-16 15:20:36 -07:00
memset ( & tm , 0 , sizeof ( struct rtc_time ) ) ;
if ( hp_sdc_rtc_read_bbrtc ( & tm ) ) {
2013-04-10 15:52:18 +01:00
seq_puts ( m , " BBRTC \t \t : READ FAILED! \n " ) ;
2005-04-16 15:20:36 -07:00
} else {
2013-04-10 15:52:18 +01:00
seq_printf ( m ,
2005-04-16 15:20:36 -07:00
" rtc_time \t : %02d:%02d:%02d \n "
" rtc_date \t : %04d-%02d-%02d \n "
" rtc_epoch \t : %04lu \n " ,
tm . tm_hour , tm . tm_min , tm . tm_sec ,
tm . tm_year + 1900 , tm . tm_mon + 1 ,
tm . tm_mday , epoch ) ;
}
if ( hp_sdc_rtc_read_rt ( & tv ) ) {
2013-04-10 15:52:18 +01:00
seq_puts ( m , " i8042 rtc \t : READ FAILED! \n " ) ;
2005-04-16 15:20:36 -07:00
} else {
2015-10-18 16:33:47 -07:00
seq_printf ( m , " i8042 rtc \t : %lld.%02ld seconds \n " ,
( s64 ) tv . tv_sec , ( long ) tv . tv_nsec / 1000000L ) ;
2005-04-16 15:20:36 -07:00
}
if ( hp_sdc_rtc_read_fhs ( & tv ) ) {
2013-04-10 15:52:18 +01:00
seq_puts ( m , " handshake \t : READ FAILED! \n " ) ;
2005-04-16 15:20:36 -07:00
} else {
2015-10-18 16:33:47 -07:00
seq_printf ( m , " handshake \t : %lld.%02ld seconds \n " ,
( s64 ) tv . tv_sec , ( long ) tv . tv_nsec / 1000000L ) ;
2005-04-16 15:20:36 -07:00
}
if ( hp_sdc_rtc_read_mt ( & tv ) ) {
2013-04-10 15:52:18 +01:00
seq_puts ( m , " alarm \t \t : READ FAILED! \n " ) ;
2005-04-16 15:20:36 -07:00
} else {
2015-10-18 16:33:47 -07:00
seq_printf ( m , " alarm \t \t : %lld.%02ld seconds \n " ,
( s64 ) tv . tv_sec , ( long ) tv . tv_nsec / 1000000L ) ;
2005-04-16 15:20:36 -07:00
}
if ( hp_sdc_rtc_read_dt ( & tv ) ) {
2013-04-10 15:52:18 +01:00
seq_puts ( m , " delay \t \t : READ FAILED! \n " ) ;
2005-04-16 15:20:36 -07:00
} else {
2015-10-18 16:33:47 -07:00
seq_printf ( m , " delay \t \t : %lld.%02ld seconds \n " ,
( s64 ) tv . tv_sec , ( long ) tv . tv_nsec / 1000000L ) ;
2005-04-16 15:20:36 -07:00
}
if ( hp_sdc_rtc_read_ct ( & tv ) ) {
2013-04-10 15:52:18 +01:00
seq_puts ( m , " periodic \t : READ FAILED! \n " ) ;
2005-04-16 15:20:36 -07:00
} else {
2015-10-18 16:33:47 -07:00
seq_printf ( m , " periodic \t : %lld.%02ld seconds \n " ,
( s64 ) tv . tv_sec , ( long ) tv . tv_nsec / 1000000L ) ;
2005-04-16 15:20:36 -07:00
}
2013-04-10 15:52:18 +01:00
seq_printf ( m ,
2005-04-16 15:20:36 -07:00
" DST_enable \t : %s \n "
" BCD \t \t : %s \n "
" 24hr \t \t : %s \n "
" square_wave \t : %s \n "
" alarm_IRQ \t : %s \n "
" update_IRQ \t : %s \n "
" periodic_IRQ \t : %s \n "
" periodic_freq \t : %ld \n "
" batt_status \t : %s \n " ,
YN ( RTC_DST_EN ) ,
NY ( RTC_DM_BINARY ) ,
YN ( RTC_24H ) ,
YN ( RTC_SQWE ) ,
YN ( RTC_AIE ) ,
YN ( RTC_UIE ) ,
YN ( RTC_PIE ) ,
1UL ,
1 ? " okay " : " dead " ) ;
2013-04-10 15:52:18 +01:00
return 0 ;
2005-04-16 15:20:36 -07:00
# undef YN
# undef NY
}
2010-04-27 00:24:05 +02:00
static int hp_sdc_rtc_ioctl ( struct file * file ,
2005-04-16 15:20:36 -07:00
unsigned int cmd , unsigned long arg )
{
# if 1
return - EINVAL ;
# else
struct rtc_time wtime ;
struct timeval ttime ;
int use_wtime = 0 ;
/* This needs major work. */
switch ( cmd ) {
case RTC_AIE_OFF : /* Mask alarm int. enab. bit */
case RTC_AIE_ON : /* Allow alarm interrupts. */
case RTC_PIE_OFF : /* Mask periodic int. enab. bit */
case RTC_PIE_ON : /* Allow periodic ints */
case RTC_UIE_ON : /* Allow ints for RTC updates. */
case RTC_UIE_OFF : /* Allow ints for RTC updates. */
{
/* We cannot mask individual user timers and we
cannot tell them apart when they occur , so it
would be disingenuous to succeed these IOCTLs */
return - EINVAL ;
}
case RTC_ALM_READ : /* Read the present alarm time */
{
if ( hp_sdc_rtc_read_mt ( & ttime ) ) return - EFAULT ;
if ( hp_sdc_rtc_read_bbrtc ( & wtime ) ) return - EFAULT ;
wtime . tm_hour = ttime . tv_sec / 3600 ; ttime . tv_sec % = 3600 ;
wtime . tm_min = ttime . tv_sec / 60 ; ttime . tv_sec % = 60 ;
wtime . tm_sec = ttime . tv_sec ;
break ;
}
case RTC_IRQP_READ : /* Read the periodic IRQ rate. */
{
return put_user ( hp_sdc_rtc_freq , ( unsigned long * ) arg ) ;
}
case RTC_IRQP_SET : /* Set periodic IRQ rate. */
{
/*
* The max we can do is 100 Hz .
*/
if ( ( arg < 1 ) | | ( arg > 100 ) ) return - EINVAL ;
ttime . tv_sec = 0 ;
ttime . tv_usec = 1000000 / arg ;
if ( hp_sdc_rtc_set_ct ( & ttime ) ) return - EFAULT ;
hp_sdc_rtc_freq = arg ;
return 0 ;
}
case RTC_ALM_SET : /* Store a time into the alarm */
{
/*
* This expects a struct hp_sdc_rtc_time . Writing 0xff means
* " don't care " or " match all " for PC timers . The HP SDC
* does not support that perk , but it could be emulated fairly
* easily . Only the tm_hour , tm_min and tm_sec are used .
* We could do it with 10 ms accuracy with the HP SDC , if the
* rtc interface left us a way to do that .
*/
struct hp_sdc_rtc_time alm_tm ;
if ( copy_from_user ( & alm_tm , ( struct hp_sdc_rtc_time * ) arg ,
sizeof ( struct hp_sdc_rtc_time ) ) )
return - EFAULT ;
if ( alm_tm . tm_hour > 23 ) return - EINVAL ;
if ( alm_tm . tm_min > 59 ) return - EINVAL ;
if ( alm_tm . tm_sec > 59 ) return - EINVAL ;
ttime . sec = alm_tm . tm_hour * 3600 +
alm_tm . tm_min * 60 + alm_tm . tm_sec ;
ttime . usec = 0 ;
if ( hp_sdc_rtc_set_mt ( & ttime ) ) return - EFAULT ;
return 0 ;
}
case RTC_RD_TIME : /* Read the time/date from RTC */
{
if ( hp_sdc_rtc_read_bbrtc ( & wtime ) ) return - EFAULT ;
break ;
}
case RTC_SET_TIME : /* Set the RTC */
{
struct rtc_time hp_sdc_rtc_tm ;
unsigned char mon , day , hrs , min , sec , leap_yr ;
unsigned int yrs ;
if ( ! capable ( CAP_SYS_TIME ) )
return - EACCES ;
if ( copy_from_user ( & hp_sdc_rtc_tm , ( struct rtc_time * ) arg ,
sizeof ( struct rtc_time ) ) )
return - EFAULT ;
yrs = hp_sdc_rtc_tm . tm_year + 1900 ;
mon = hp_sdc_rtc_tm . tm_mon + 1 ; /* tm_mon starts at zero */
day = hp_sdc_rtc_tm . tm_mday ;
hrs = hp_sdc_rtc_tm . tm_hour ;
min = hp_sdc_rtc_tm . tm_min ;
sec = hp_sdc_rtc_tm . tm_sec ;
if ( yrs < 1970 )
return - EINVAL ;
leap_yr = ( ( ! ( yrs % 4 ) & & ( yrs % 100 ) ) | | ! ( yrs % 400 ) ) ;
if ( ( mon > 12 ) | | ( day = = 0 ) )
return - EINVAL ;
if ( day > ( days_in_mo [ mon ] + ( ( mon = = 2 ) & & leap_yr ) ) )
return - EINVAL ;
if ( ( hrs > = 24 ) | | ( min > = 60 ) | | ( sec > = 60 ) )
return - EINVAL ;
if ( ( yrs - = eH ) > 255 ) /* They are unsigned */
return - EINVAL ;
return 0 ;
}
case RTC_EPOCH_READ : /* Read the epoch. */
{
return put_user ( epoch , ( unsigned long * ) arg ) ;
}
case RTC_EPOCH_SET : /* Set the epoch. */
{
/*
* There were no RTC clocks before 1900.
*/
if ( arg < 1900 )
return - EINVAL ;
if ( ! capable ( CAP_SYS_TIME ) )
return - EACCES ;
epoch = arg ;
return 0 ;
}
default :
return - EINVAL ;
}
return copy_to_user ( ( void * ) arg , & wtime , sizeof wtime ) ? - EFAULT : 0 ;
# endif
}
2010-04-27 00:24:05 +02:00
static long hp_sdc_rtc_unlocked_ioctl ( struct file * file ,
unsigned int cmd , unsigned long arg )
{
int ret ;
2010-06-02 14:28:52 +02:00
mutex_lock ( & hp_sdc_rtc_mutex ) ;
2010-04-27 00:24:05 +02:00
ret = hp_sdc_rtc_ioctl ( file , cmd , arg ) ;
2010-06-02 14:28:52 +02:00
mutex_unlock ( & hp_sdc_rtc_mutex ) ;
2010-04-27 00:24:05 +02:00
return ret ;
}
2007-02-12 00:55:32 -08:00
static const struct file_operations hp_sdc_rtc_fops = {
2010-04-27 00:24:05 +02:00
. owner = THIS_MODULE ,
. llseek = no_llseek ,
. read = hp_sdc_rtc_read ,
. poll = hp_sdc_rtc_poll ,
2010-06-05 22:52:21 +02:00
. unlocked_ioctl = hp_sdc_rtc_unlocked_ioctl ,
2010-04-27 00:24:05 +02:00
. open = hp_sdc_rtc_open ,
. fasync = hp_sdc_rtc_fasync ,
2005-04-16 15:20:36 -07:00
} ;
static struct miscdevice hp_sdc_rtc_dev = {
. minor = RTC_MINOR ,
. name = " rtc_HIL " ,
. fops = & hp_sdc_rtc_fops
} ;
static int __init hp_sdc_rtc_init ( void )
{
int ret ;
2008-05-18 20:47:17 +02:00
# ifdef __mc68000__
if ( ! MACH_IS_HP300 )
return - ENODEV ;
# endif
2010-09-07 14:31:58 +00:00
sema_init ( & i8042tregs , 1 ) ;
2005-04-16 15:20:36 -07:00
if ( ( ret = hp_sdc_request_timer_irq ( & hp_sdc_rtc_isr ) ) )
return ret ;
2006-12-06 20:37:08 -08:00
if ( misc_register ( & hp_sdc_rtc_dev ) ! = 0 )
printk ( KERN_INFO " Could not register misc. dev for i8042 rtc \n " ) ;
2018-05-15 15:57:23 +02:00
proc_create_single ( " driver/rtc " , 0 , NULL , hp_sdc_rtc_proc_show ) ;
2005-04-16 15:20:36 -07:00
printk ( KERN_INFO " HP i8042 SDC + MSM-58321 RTC support loaded "
" (RTC v " RTC_VERSION " ) \n " ) ;
return 0 ;
}
static void __exit hp_sdc_rtc_exit ( void )
{
remove_proc_entry ( " driver/rtc " , NULL ) ;
misc_deregister ( & hp_sdc_rtc_dev ) ;
hp_sdc_release_timer_irq ( hp_sdc_rtc_isr ) ;
printk ( KERN_INFO " HP i8042 SDC + MSM-58321 RTC support unloaded \n " ) ;
}
module_init ( hp_sdc_rtc_init ) ;
module_exit ( hp_sdc_rtc_exit ) ;