| /* | |
| * 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> | |
| #include <linux/moduleparam.h> | |
| #include <linux/log2.h> | |
| #include <sound/core.h> | |
| #include <sound/timer.h> | |
| #if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE) | |
| #include <linux/mc146818rtc.h> | |
| #define RTC_FREQ 1024 /* default frequency */ | |
| #define NANO_SEC 1000000000L /* 10^9 in sec */ | |
| /* | |
| * prototypes | |
| */ | |
| 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); | |
| /* | |
| * The hardware dependent description for this timer. | |
| */ | |
| static struct snd_timer_hardware rtc_hw = { | |
| .flags = SNDRV_TIMER_HW_AUTO | | |
| SNDRV_TIMER_HW_FIRST | | |
| SNDRV_TIMER_HW_TASKLET, | |
| .ticks = 100000000L, /* FIXME: XXX */ | |
| .open = rtctimer_open, | |
| .close = rtctimer_close, | |
| .start = rtctimer_start, | |
| .stop = rtctimer_stop, | |
| }; | |
| static int rtctimer_freq = RTC_FREQ; /* frequency */ | |
| static struct snd_timer *rtctimer; | |
| static struct tasklet_struct rtc_tasklet; | |
| static rtc_task_t rtc_task; | |
| static int | |
| rtctimer_open(struct snd_timer *t) | |
| { | |
| int err; | |
| err = rtc_register(&rtc_task); | |
| if (err < 0) | |
| return err; | |
| t->private_data = &rtc_task; | |
| return 0; | |
| } | |
| static int | |
| rtctimer_close(struct snd_timer *t) | |
| { | |
| rtc_task_t *rtc = t->private_data; | |
| if (rtc) { | |
| rtc_unregister(rtc); | |
| tasklet_kill(&rtc_tasklet); | |
| t->private_data = NULL; | |
| } | |
| return 0; | |
| } | |
| static int | |
| rtctimer_start(struct snd_timer *timer) | |
| { | |
| rtc_task_t *rtc = timer->private_data; | |
| if (snd_BUG_ON(!rtc)) | |
| return -EINVAL; | |
| rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq); | |
| rtc_control(rtc, RTC_PIE_ON, 0); | |
| return 0; | |
| } | |
| static int | |
| rtctimer_stop(struct snd_timer *timer) | |
| { | |
| rtc_task_t *rtc = timer->private_data; | |
| if (snd_BUG_ON(!rtc)) | |
| return -EINVAL; | |
| rtc_control(rtc, RTC_PIE_OFF, 0); | |
| return 0; | |
| } | |
| static void rtctimer_tasklet(unsigned long data) | |
| { | |
| snd_timer_interrupt((struct snd_timer *)data, 1); | |
| } | |
| /* | |
| * interrupt | |
| */ | |
| static void rtctimer_interrupt(void *private_data) | |
| { | |
| tasklet_schedule(private_data); | |
| } | |
| /* | |
| * ENTRY functions | |
| */ | |
| static int __init rtctimer_init(void) | |
| { | |
| int err; | |
| struct snd_timer *timer; | |
| if (rtctimer_freq < 2 || rtctimer_freq > 8192 || | |
| !is_power_of_2(rtctimer_freq)) { | |
| snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", | |
| rtctimer_freq); | |
| 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; | |
| timer->module = THIS_MODULE; | |
| strcpy(timer->name, "RTC timer"); | |
| timer->hw = rtc_hw; | |
| timer->hw.resolution = NANO_SEC / rtctimer_freq; | |
| tasklet_init(&rtc_tasklet, rtctimer_tasklet, (unsigned long)timer); | |
| /* set up RTC callback */ | |
| rtc_task.func = rtctimer_interrupt; | |
| rtc_task.private_data = &rtc_tasklet; | |
| 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) { | |
| snd_timer_global_free(rtctimer); | |
| 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)); | |
| #endif /* CONFIG_RTC || CONFIG_RTC_MODULE */ |