linux/sound/core/rtctimer.c
<<
>>
Prefs
   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 <linux/init.h>
  24#include <linux/interrupt.h>
  25#include <linux/moduleparam.h>
  26#include <linux/log2.h>
  27#include <sound/core.h>
  28#include <sound/timer.h>
  29
  30#if defined(CONFIG_RTC) || defined(CONFIG_RTC_MODULE)
  31
  32#include <linux/mc146818rtc.h>
  33
  34#define RTC_FREQ        1024            /* default frequency */
  35#define NANO_SEC        1000000000L     /* 10^9 in sec */
  36
  37/*
  38 * prototypes
  39 */
  40static int rtctimer_open(struct snd_timer *t);
  41static int rtctimer_close(struct snd_timer *t);
  42static int rtctimer_start(struct snd_timer *t);
  43static int rtctimer_stop(struct snd_timer *t);
  44
  45
  46/*
  47 * The hardware dependent description for this timer.
  48 */
  49static struct snd_timer_hardware rtc_hw = {
  50        .flags =        SNDRV_TIMER_HW_AUTO |
  51                        SNDRV_TIMER_HW_FIRST |
  52                        SNDRV_TIMER_HW_TASKLET,
  53        .ticks =        100000000L,             /* FIXME: XXX */
  54        .open =         rtctimer_open,
  55        .close =        rtctimer_close,
  56        .start =        rtctimer_start,
  57        .stop =         rtctimer_stop,
  58};
  59
  60static int rtctimer_freq = RTC_FREQ;            /* frequency */
  61static struct snd_timer *rtctimer;
  62static struct tasklet_struct rtc_tasklet;
  63static rtc_task_t rtc_task;
  64
  65
  66static int
  67rtctimer_open(struct snd_timer *t)
  68{
  69        int err;
  70
  71        err = rtc_register(&rtc_task);
  72        if (err < 0)
  73                return err;
  74        t->private_data = &rtc_task;
  75        return 0;
  76}
  77
  78static int
  79rtctimer_close(struct snd_timer *t)
  80{
  81        rtc_task_t *rtc = t->private_data;
  82        if (rtc) {
  83                rtc_unregister(rtc);
  84                tasklet_kill(&rtc_tasklet);
  85                t->private_data = NULL;
  86        }
  87        return 0;
  88}
  89
  90static int
  91rtctimer_start(struct snd_timer *timer)
  92{
  93        rtc_task_t *rtc = timer->private_data;
  94        if (snd_BUG_ON(!rtc))
  95                return -EINVAL;
  96        rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
  97        rtc_control(rtc, RTC_PIE_ON, 0);
  98        return 0;
  99}
 100
 101static int
 102rtctimer_stop(struct snd_timer *timer)
 103{
 104        rtc_task_t *rtc = timer->private_data;
 105        if (snd_BUG_ON(!rtc))
 106                return -EINVAL;
 107        rtc_control(rtc, RTC_PIE_OFF, 0);
 108        return 0;
 109}
 110
 111static void rtctimer_tasklet(unsigned long data)
 112{
 113        snd_timer_interrupt((struct snd_timer *)data, 1);
 114}
 115
 116/*
 117 * interrupt
 118 */
 119static void rtctimer_interrupt(void *private_data)
 120{
 121        tasklet_schedule(private_data);
 122}
 123
 124
 125/*
 126 *  ENTRY functions
 127 */
 128static int __init rtctimer_init(void)
 129{
 130        int err;
 131        struct snd_timer *timer;
 132
 133        if (rtctimer_freq < 2 || rtctimer_freq > 8192 ||
 134            !is_power_of_2(rtctimer_freq)) {
 135                snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n",
 136                           rtctimer_freq);
 137                return -EINVAL;
 138        }
 139
 140        /* Create a new timer and set up the fields */
 141        err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer);
 142        if (err < 0)
 143                return err;
 144
 145        timer->module = THIS_MODULE;
 146        strcpy(timer->name, "RTC timer");
 147        timer->hw = rtc_hw;
 148        timer->hw.resolution = NANO_SEC / rtctimer_freq;
 149
 150        tasklet_init(&rtc_tasklet, rtctimer_tasklet, (unsigned long)timer);
 151
 152        /* set up RTC callback */
 153        rtc_task.func = rtctimer_interrupt;
 154        rtc_task.private_data = &rtc_tasklet;
 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_free(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 */
 189