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        snd_assert(rtc != NULL, return -EINVAL);
  95        rtc_control(rtc, RTC_IRQP_SET, rtctimer_freq);
  96        rtc_control(rtc, RTC_PIE_ON, 0);
  97        return 0;
  98}
  99
 100static int
 101rtctimer_stop(struct snd_timer *timer)
 102{
 103        rtc_task_t *rtc = timer->private_data;
 104        snd_assert(rtc != NULL, return -EINVAL);
 105        rtc_control(rtc, RTC_PIE_OFF, 0);
 106        return 0;
 107}
 108
 109static void rtctimer_tasklet(unsigned long data)
 110{
 111        snd_timer_interrupt((struct snd_timer *)data, 1);
 112}
 113
 114/*
 115 * interrupt
 116 */
 117static void rtctimer_interrupt(void *private_data)
 118{
 119        tasklet_hi_schedule(private_data);
 120}
 121
 122
 123/*
 124 *  ENTRY functions
 125 */
 126static int __init rtctimer_init(void)
 127{
 128        int err;
 129        struct snd_timer *timer;
 130
 131        if (rtctimer_freq < 2 || rtctimer_freq > 8192 ||
 132            !is_power_of_2(rtctimer_freq)) {
 133                snd_printk(KERN_ERR "rtctimer: invalid frequency %d\n",
 134                           rtctimer_freq);
 135                return -EINVAL;
 136        }
 137
 138        /* Create a new timer and set up the fields */
 139        err = snd_timer_global_new("rtc", SNDRV_TIMER_GLOBAL_RTC, &timer);
 140        if (err < 0)
 141                return err;
 142
 143        timer->module = THIS_MODULE;
 144        strcpy(timer->name, "RTC timer");
 145        timer->hw = rtc_hw;
 146        timer->hw.resolution = NANO_SEC / rtctimer_freq;
 147
 148        tasklet_init(&rtc_tasklet, rtctimer_tasklet, (unsigned long)timer);
 149
 150        /* set up RTC callback */
 151        rtc_task.func = rtctimer_interrupt;
 152        rtc_task.private_data = &rtc_tasklet;
 153
 154        err = snd_timer_global_register(timer);
 155        if (err < 0) {
 156                snd_timer_global_free(timer);
 157                return err;
 158        }
 159        rtctimer = timer; /* remember this */
 160
 161        return 0;
 162}
 163
 164static void __exit rtctimer_exit(void)
 165{
 166        if (rtctimer) {
 167                snd_timer_global_free(rtctimer);
 168                rtctimer = NULL;
 169        }
 170}
 171
 172
 173/*
 174 * exported stuff
 175 */
 176module_init(rtctimer_init)
 177module_exit(rtctimer_exit)
 178
 179module_param(rtctimer_freq, int, 0444);
 180MODULE_PARM_DESC(rtctimer_freq, "timer frequency in Hz");
 181
 182MODULE_LICENSE("GPL");
 183
 184MODULE_ALIAS("snd-timer-" __stringify(SNDRV_TIMER_GLOBAL_RTC));
 185
 186#endif /* CONFIG_RTC || CONFIG_RTC_MODULE */
 187