📄 at91_tc.c
字号:
/* * Timer Counter interface for Linux on Atmel AT91RM9200 * * Copyright (c) 2002 Rick Bronson * * 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. *Timer/Counter driver. This enables you to opena Timer/Counter channel and wait on a specific time. Also allows youto output a specific frequency on a pin. */#include <linux/module.h>#include <linux/fs.h>#include <linux/miscdevice.h>#include <linux/string.h>#include <linux/init.h>#include <linux/poll.h>#include <linux/proc_fs.h>#include <asm/bitops.h>#include <asm/hardware.h>#include <asm/irq.h>#include <linux/rtc.h>#include <asm/arch/tc.h> /* get ioctl stuff for tc */#include "at91_tc.h"#define DRIVER_VERSION "1.00"static AT91PS_TC p_tc_lut[TC_NUM] = { /* TC address lookup table */ AT91C_BASE_TC0, AT91C_BASE_TC1, AT91C_BASE_TC2, AT91C_BASE_TC3, AT91C_BASE_TC4, AT91C_BASE_TC5, };static AT91PS_TCB p_tcb_lut[TC_NUM] = { /* TC block address lookup table */ AT91C_BASE_TCB0, AT91C_BASE_TCB0, AT91C_BASE_TCB0, AT91C_BASE_TCB1, AT91C_BASE_TCB1, AT91C_BASE_TCB1, };static void at91_tc_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct tc_private *priv = dev_id; /* pointer to the priv */ AT91PS_TC p_tc = priv->p_tc; unsigned int rtsr = p_tc->TC_SR & p_tc->TC_IMR; if (rtsr & AT91C_TC_CPAS) wake_up_interruptible(&priv->tc_wait); /* wake up waiting process */ }static int at91_tc_open(struct inode *inode, struct file *file){ struct tc_private *priv; int minor = MINOR(inode->i_rdev); if (minor > AT91_TC_MINOR_LAST) return -EINVAL; priv = (struct tc_private *)kmalloc(sizeof(struct tc_private), GFP_KERNEL); if (!priv) return -ENOMEM; priv->minor = minor; priv->p_tc = p_tc_lut[minor]; /* save TC pointer */ priv->p_tcb = p_tcb_lut[minor]; /* save TC Block pointer */ file->private_data = (void *)priv; spin_lock_init(&priv->lock1); priv->first_time_flag = 0; priv->irq = AT91C_ID_TC0 + minor; *AT91C_PMC_PCER = 1 << priv->irq; // turn clock on init_waitqueue_head(&priv->tc_wait); if (request_irq (priv->irq, at91_tc_interrupt, 0, "at91_tc", priv)) { kfree(priv); printk("at91_tc: unable to get IRQ %d\n", priv->irq); return -EAGAIN; } return 0;}static int at91_tc_release(struct inode *inode, struct file *file){ struct tc_private *priv = (struct tc_private *)file->private_data; AT91PS_TC p_tc = priv->p_tc; p_tc->TC_IDR = -1; /* disable the interrupt */ p_tc->TC_CCR = AT91C_TC_CLKDIS; /* disable the clock */ *AT91C_PMC_PCDR = 1 << priv->irq; /* turn clock off */ wake_up_interruptible(&priv->tc_wait); /* wake up waiting process if any */ free_irq(priv->irq, priv); /* get rid of this int */ kfree(priv); return 0;}ssize_t at91_tc_read(struct file * file, char *buf, size_t count, loff_t * ppos){ struct tc_private *priv = (struct tc_private *)file->private_data; AT91PS_TC p_tc = priv->p_tc; if (count < sizeof(unsigned long)) return -EINVAL; return put_user(p_tc->TC_CV, (unsigned long *) buf);}/* * Handle commands from user-space */static int at91_tc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int ret = 0; unsigned long flags, val; struct tc_private *priv = (struct tc_private *)file->private_data; struct at91_tc_ioctl_clock tc_clock; struct at91_pck_ioctl pmc_pck; AT91PS_TC p_tc = priv->p_tc; AT91PS_TCB p_tcb = priv->p_tcb; AT91PS_PIO p_pio; /* pio init if any for this Timer */ switch (cmd) { case AT91_TC_WAIT: /* set up periodict interrupt */ if (copy_from_user (&val, (void *)arg, sizeof (val))) ret = -EFAULT; spin_lock_irqsave(&priv->lock1, flags); /* stop int's else we wakeup b4 we sleep */ p_tc->TC_CMR = AT91C_TC_WAVE | AT91C_TC_TIMER_DIV5_CLOCK; /* 32768 Hz clock */ if (priv->first_time_flag == 0) { p_tc->TC_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG; //Channel Control Register p_tc->TC_RA = val; p_tc->TC_IER = AT91C_TC_CPAS; /* enable the interrupt */ priv->first_time_flag = 1; } else p_tc->TC_RA += val; interruptible_sleep_on(&priv->tc_wait); spin_unlock_irqrestore(&priv->lock1, flags); ret = copy_to_user ((void *)arg, &val, sizeof (val)) ? -EFAULT : 0; break; case AT91_TC_SET_CLOCK: /* set up timer modes, and possible output on a pin */ if (copy_from_user (&tc_clock, (void *)arg, sizeof (tc_clock))) return -EFAULT; if ((p_pio = tc_clock.p_pio) != NULL) { /* configure port pin for peripheral */ p_pio->PIO_ASR = tc_clock.pio_asr; p_pio->PIO_BSR = tc_clock.pio_bsr; p_pio->PIO_PDR = tc_clock.pio_asr | tc_clock.pio_bsr; } p_tcb->TCB_BCR = tc_clock.tcb_bcr; /* TC Block Control Register */ p_tcb->TCB_BMR = tc_clock.tcb_bmr; /* TC Block Mode Register */ p_tc->TC_CMR = tc_clock.tc_cmr; /* Channel Mode Register, must write 1st */ p_tc->TC_CCR = tc_clock.tc_ccr; /* Channel Control Register */ p_tc->TC_RA = tc_clock.tc_ra; /* RA Register */ p_tc->TC_RC = tc_clock.tc_rc; /* RA Register */ break; case AT91_TC_ENABLE_PCK: /* set up timer modes, and possible output on a pin */ if (copy_from_user (&pmc_pck, (void *)arg, sizeof (pmc_pck))) return -EFAULT; if ((p_pio = pmc_pck.p_pio) != NULL) { /* configure port pin for peripheral */ p_pio->PIO_ASR = pmc_pck.pio_asr; p_pio->PIO_BSR = pmc_pck.pio_bsr; p_pio->PIO_PDR = pmc_pck.pio_asr | pmc_pck.pio_bsr; } AT91C_BASE_SYS->PMC_PCKR[pmc_pck.pck_no] = pmc_pck.pmc_pck; /* program it */ AT91C_BASE_SYS->PMC_SCER = AT91C_PMC_PCK0 << pmc_pck.pck_no; /* enable it */ break; case AT91_TC_DISABLE_PCK: /* set up timer modes, and possible output on a pin */ if (copy_from_user (&pmc_pck, (void *)arg, sizeof (pmc_pck))) return -EFAULT; AT91C_BASE_SYS->PMC_SCDR = AT91C_PMC_PCK0 << pmc_pck.pck_no; /* disable it */ if ((p_pio = pmc_pck.p_pio) != NULL) { /* configure port pin for peripheral */ p_pio->PIO_PER = pmc_pck.pio_asr | pmc_pck.pio_bsr; } break; default: ret = -EINVAL; break; } return ret; }static struct file_operations at91_tc_fops = { owner:THIS_MODULE, read:at91_tc_read, ioctl:at91_tc_ioctl, open:at91_tc_open, release:at91_tc_release,};/* * Initialize and install TC driver */static int __init at91_tc_init(void) { int res; res = register_chrdev(TC_MAJOR, "at91 tc", &at91_tc_fops); if (res < 0) { printk(KERN_ERR "at91_tc: couldn't get a major number.\n"); return res; } printk(KERN_INFO "AT91 Timer/Counter driver v" DRIVER_VERSION "\n"); return 0; }module_init(at91_tc_init);MODULE_AUTHOR("Rick Bronson");MODULE_DESCRIPTION("AT91 Timer Counter Driver (AT91_TC)");EXPORT_NO_SYMBOLS;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -