2005-04-17 02:20:36 +04:00
/*
* RTC based high - frequency timer
*
* Copyright ( C ) 2000 Takashi Iwai
* based on rtctimer . c by Steve Ratcliffe
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# include <linux/init.h>
# include <linux/interrupt.h>
2011-07-30 08:14:12 +04:00
# include <linux/module.h>
2007-02-22 15:23:01 +03:00
# include <linux/log2.h>
2005-04-17 02:20:36 +04:00
# include <sound/core.h>
# include <sound/timer.h>
2014-02-10 12:48:47 +04:00
# if IS_ENABLED(CONFIG_RTC)
2005-04-17 02:20:36 +04:00
# include <linux/mc146818rtc.h>
# define RTC_FREQ 1024 /* default frequency */
# define NANO_SEC 1000000000L /* 10^9 in sec */
/*
* prototypes
*/
2005-11-17 15:56:05 +03:00
static int rtctimer_open ( struct snd_timer * t ) ;
static int rtctimer_close ( struct snd_timer * t ) ;
static int rtctimer_start ( struct snd_timer * t ) ;
static int rtctimer_stop ( struct snd_timer * t ) ;
2005-04-17 02:20:36 +04:00
/*
* The hardware dependent description for this timer .
*/
2005-11-17 15:56:05 +03:00
static struct snd_timer_hardware rtc_hw = {
2006-10-27 12:45:00 +04:00
. flags = SNDRV_TIMER_HW_AUTO |
SNDRV_TIMER_HW_FIRST |
SNDRV_TIMER_HW_TASKLET ,
2005-04-17 02:20:36 +04:00
. ticks = 100000000L , /* FIXME: XXX */
. open = rtctimer_open ,
. close = rtctimer_close ,
. start = rtctimer_start ,
. stop = rtctimer_stop ,
} ;
static int rtctimer_freq = RTC_FREQ ; /* frequency */
2005-11-17 15:56:05 +03:00
static struct snd_timer * rtctimer ;
2006-10-27 12:45:00 +04:00
static struct tasklet_struct rtc_tasklet ;
2005-04-17 02:20:36 +04:00
static rtc_task_t rtc_task ;
static int
2005-11-17 15:56:05 +03:00
rtctimer_open ( struct snd_timer * t )
2005-04-17 02:20:36 +04:00
{
int err ;
err = rtc_register ( & rtc_task ) ;
if ( err < 0 )
return err ;
t - > private_data = & rtc_task ;
return 0 ;
}
static int
2005-11-17 15:56:05 +03:00
rtctimer_close ( struct snd_timer * t )
2005-04-17 02:20:36 +04:00
{
rtc_task_t * rtc = t - > private_data ;
if ( rtc ) {
rtc_unregister ( rtc ) ;
2006-10-27 12:45:00 +04:00
tasklet_kill ( & rtc_tasklet ) ;
2005-04-17 02:20:36 +04:00
t - > private_data = NULL ;
}
return 0 ;
}
static int
2005-11-17 15:56:05 +03:00
rtctimer_start ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
rtc_task_t * rtc = timer - > private_data ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! rtc ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
rtc_control ( rtc , RTC_IRQP_SET , rtctimer_freq ) ;
rtc_control ( rtc , RTC_PIE_ON , 0 ) ;
return 0 ;
}
static int
2005-11-17 15:56:05 +03:00
rtctimer_stop ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
rtc_task_t * rtc = timer - > private_data ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! rtc ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
rtc_control ( rtc , RTC_PIE_OFF , 0 ) ;
return 0 ;
}
2006-10-27 12:45:00 +04:00
static void rtctimer_tasklet ( unsigned long data )
{
snd_timer_interrupt ( ( struct snd_timer * ) data , 1 ) ;
}
2005-04-17 02:20:36 +04:00
/*
* interrupt
*/
static void rtctimer_interrupt ( void * private_data )
{
2008-12-18 14:17:55 +03:00
tasklet_schedule ( private_data ) ;
2005-04-17 02:20:36 +04:00
}
/*
* ENTRY functions
*/
static int __init rtctimer_init ( void )
{
2005-09-27 17:57:24 +04:00
int err ;
2005-11-17 15:56:05 +03:00
struct snd_timer * timer ;
2005-04-17 02:20:36 +04:00
2005-09-27 17:57:24 +04:00
if ( rtctimer_freq < 2 | | rtctimer_freq > 8192 | |
2007-02-22 15:23:01 +03:00
! is_power_of_2 ( rtctimer_freq ) ) {
2014-02-04 21:22:39 +04:00
pr_err ( " ALSA: rtctimer: invalid frequency %d \n " , rtctimer_freq ) ;
2005-04-17 02:20:36 +04:00
return - EINVAL ;
}
/* Create a new timer and set up the fields */
err = snd_timer_global_new ( " rtc " , SNDRV_TIMER_GLOBAL_RTC , & timer ) ;
if ( err < 0 )
return err ;
2005-10-12 19:12:31 +04:00
timer - > module = THIS_MODULE ;
2005-04-17 02:20:36 +04:00
strcpy ( timer - > name , " RTC timer " ) ;
timer - > hw = rtc_hw ;
timer - > hw . resolution = NANO_SEC / rtctimer_freq ;
2006-10-27 12:45:00 +04:00
tasklet_init ( & rtc_tasklet , rtctimer_tasklet , ( unsigned long ) timer ) ;
2005-04-17 02:20:36 +04:00
/* set up RTC callback */
rtc_task . func = rtctimer_interrupt ;
2006-10-27 12:45:00 +04:00
rtc_task . private_data = & rtc_tasklet ;
2005-04-17 02:20:36 +04:00
err = snd_timer_global_register ( timer ) ;
if ( err < 0 ) {
snd_timer_global_free ( timer ) ;
return err ;
}
rtctimer = timer ; /* remember this */
return 0 ;
}
static void __exit rtctimer_exit ( void )
{
if ( rtctimer ) {
2006-06-23 16:38:23 +04:00
snd_timer_global_free ( rtctimer ) ;
2005-04-17 02:20:36 +04:00
rtctimer = NULL ;
}
}
/*
* exported stuff
*/
module_init ( rtctimer_init )
module_exit ( rtctimer_exit )
module_param ( rtctimer_freq , int , 0444 ) ;
MODULE_PARM_DESC ( rtctimer_freq , " timer frequency in Hz " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " snd-timer- " __stringify ( SNDRV_TIMER_GLOBAL_RTC ) ) ;
2014-02-10 12:48:47 +04:00
# endif /* IS_ENABLED(CONFIG_RTC) */