mirror of
https://github.com/torvalds/linux.git
synced 2024-12-01 08:31:37 +00:00
de24214d0c
Modules: RTC timer driver,Timer Midlevel Add a module pointer to the timer structure and use it for refcounting instead of the card's module pointer to prevent the global timer modules (rtctimer and hpetimer) from being removed while in use. Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
179 lines
3.9 KiB
C
179 lines
3.9 KiB
C
/*
|
|
* 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 <sound/driver.h>
|
|
#include <linux/init.h>
|
|
#include <linux/time.h>
|
|
#include <linux/threads.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <sound/core.h>
|
|
#include <sound/timer.h>
|
|
#include <sound/info.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(snd_timer_t *t);
|
|
static int rtctimer_close(snd_timer_t *t);
|
|
static int rtctimer_start(snd_timer_t *t);
|
|
static int rtctimer_stop(snd_timer_t *t);
|
|
|
|
|
|
/*
|
|
* The hardware dependent description for this timer.
|
|
*/
|
|
static struct _snd_timer_hardware rtc_hw = {
|
|
.flags = SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO,
|
|
.ticks = 100000000L, /* FIXME: XXX */
|
|
.open = rtctimer_open,
|
|
.close = rtctimer_close,
|
|
.start = rtctimer_start,
|
|
.stop = rtctimer_stop,
|
|
};
|
|
|
|
static int rtctimer_freq = RTC_FREQ; /* frequency */
|
|
static snd_timer_t *rtctimer;
|
|
static rtc_task_t rtc_task;
|
|
|
|
|
|
static int
|
|
rtctimer_open(snd_timer_t *t)
|
|
{
|
|
int err;
|
|
|
|
err = rtc_register(&rtc_task);
|
|
if (err < 0)
|
|
return err;
|
|
t->private_data = &rtc_task;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
rtctimer_close(snd_timer_t *t)
|
|
{
|
|
rtc_task_t *rtc = t->private_data;
|
|
if (rtc) {
|
|
rtc_unregister(rtc);
|
|
t->private_data = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
rtctimer_start(snd_timer_t *timer)
|
|
{
|
|
rtc_task_t *rtc = timer->private_data;
|
|
snd_assert(rtc != NULL, return -EINVAL);
|
|
rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
|
|
rtc_control(rtc, RTC_PIE_ON, 0);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
rtctimer_stop(snd_timer_t *timer)
|
|
{
|
|
rtc_task_t *rtc = timer->private_data;
|
|
snd_assert(rtc != NULL, return -EINVAL);
|
|
rtc_control(rtc, RTC_PIE_OFF, 0);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* interrupt
|
|
*/
|
|
static void rtctimer_interrupt(void *private_data)
|
|
{
|
|
snd_timer_interrupt(private_data, 1);
|
|
}
|
|
|
|
|
|
/*
|
|
* ENTRY functions
|
|
*/
|
|
static int __init rtctimer_init(void)
|
|
{
|
|
int err;
|
|
snd_timer_t *timer;
|
|
|
|
if (rtctimer_freq < 2 || rtctimer_freq > 8192 ||
|
|
(rtctimer_freq & (rtctimer_freq - 1)) != 0) {
|
|
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;
|
|
|
|
/* set up RTC callback */
|
|
rtc_task.func = rtctimer_interrupt;
|
|
rtc_task.private_data = timer;
|
|
|
|
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_unregister(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 */
|