at v2.6.12 188 lines 4.3 kB view raw
1/* 2 * RTC based high-frequency timer 3 * 4 * Copyright (C) 2000 Takashi Iwai 5 * based on rtctimer.c by Steve Ratcliffe 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 * GNU General Public License for more details. 16 * 17 * You should have received a copy of the GNU General Public License 18 * along with this program; if not, write to the Free Software 19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 * 21 */ 22 23#include <sound/driver.h> 24#include <linux/init.h> 25#include <linux/time.h> 26#include <linux/threads.h> 27#include <linux/interrupt.h> 28#include <linux/moduleparam.h> 29#include <sound/core.h> 30#include <sound/timer.h> 31#include <sound/info.h> 32 33#if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE) 34 35#include <linux/mc146818rtc.h> 36 37#define RTC_FREQ 1024 /* default frequency */ 38#define NANO_SEC 1000000000L /* 10^9 in sec */ 39 40/* 41 * prototypes 42 */ 43static int rtctimer_open(snd_timer_t *t); 44static int rtctimer_close(snd_timer_t *t); 45static int rtctimer_start(snd_timer_t *t); 46static int rtctimer_stop(snd_timer_t *t); 47 48 49/* 50 * The hardware dependent description for this timer. 51 */ 52static struct _snd_timer_hardware rtc_hw = { 53 .flags = SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO, 54 .ticks = 100000000L, /* FIXME: XXX */ 55 .open = rtctimer_open, 56 .close = rtctimer_close, 57 .start = rtctimer_start, 58 .stop = rtctimer_stop, 59}; 60 61static int rtctimer_freq = RTC_FREQ; /* frequency */ 62static snd_timer_t *rtctimer; 63static atomic_t rtc_inc = ATOMIC_INIT(0); 64static rtc_task_t rtc_task; 65 66 67static int 68rtctimer_open(snd_timer_t *t) 69{ 70 int err; 71 72 err = rtc_register(&rtc_task); 73 if (err < 0) 74 return err; 75 t->private_data = &rtc_task; 76 return 0; 77} 78 79static int 80rtctimer_close(snd_timer_t *t) 81{ 82 rtc_task_t *rtc = t->private_data; 83 if (rtc) { 84 rtc_unregister(rtc); 85 t->private_data = NULL; 86 } 87 return 0; 88} 89 90static int 91rtctimer_start(snd_timer_t *timer) 92{ 93 rtc_task_t *rtc = timer->private_data; 94 snd_assert(rtc != NULL, return -EINVAL); 95 rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq); 96 rtc_control(rtc, RTC_PIE_ON, 0); 97 atomic_set(&rtc_inc, 0); 98 return 0; 99} 100 101static int 102rtctimer_stop(snd_timer_t *timer) 103{ 104 rtc_task_t *rtc = timer->private_data; 105 snd_assert(rtc != NULL, return -EINVAL); 106 rtc_control(rtc, RTC_PIE_OFF, 0); 107 return 0; 108} 109 110/* 111 * interrupt 112 */ 113static void rtctimer_interrupt(void *private_data) 114{ 115 int ticks; 116 117 atomic_inc(&rtc_inc); 118 ticks = atomic_read(&rtc_inc); 119 snd_timer_interrupt((snd_timer_t*)private_data, ticks); 120 atomic_sub(ticks, &rtc_inc); 121} 122 123 124/* 125 * ENTRY functions 126 */ 127static int __init rtctimer_init(void) 128{ 129 int order, err; 130 snd_timer_t *timer; 131 132 if (rtctimer_freq < 2 || rtctimer_freq > 8192) { 133 snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", rtctimer_freq); 134 return -EINVAL; 135 } 136 for (order = 1; rtctimer_freq > order; order <<= 1) 137 ; 138 if (rtctimer_freq != order) { 139 snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n", rtctimer_freq); 140 return -EINVAL; 141 } 142 143 /* Create a new timer and set up the fields */ 144 err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer); 145 if (err < 0) 146 return err; 147 148 strcpy(timer->name, "RTC timer"); 149 timer->hw = rtc_hw; 150 timer->hw.resolution = NANO_SEC / rtctimer_freq; 151 152 /* set up RTC callback */ 153 rtc_task.func = rtctimer_interrupt; 154 rtc_task.private_data = timer; 155 156 err = snd_timer_global_register(timer); 157 if (err < 0) { 158 snd_timer_global_free(timer); 159 return err; 160 } 161 rtctimer = timer; /* remember this */ 162 163 return 0; 164} 165 166static void __exit rtctimer_exit(void) 167{ 168 if (rtctimer) { 169 snd_timer_global_unregister(rtctimer); 170 rtctimer = NULL; 171 } 172} 173 174 175/* 176 * exported stuff 177 */ 178module_init(rtctimer_init) 179module_exit(rtctimer_exit) 180 181module_param(rtctimer_freq, int, 0444); 182MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz"); 183 184MODULE_LICENSE("GPL"); 185 186MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_RTC)); 187 188#endif /* CONFIG_RTC || CONFIG_RTC_MODULE */