📄 at91_rtcex.c
字号:
/*
** linux/drivers/at91/rtcex/at91_rtcex.c
** DS1307 time clock
**
** Copyright (C) 2004 Hyesco Technology Co.,Ltd
**
** Author: Zheng Geng <gzheng@hyesco.com>
**
** History:
**
** 2004.10 Zheng Geng <gzheng@hyesco.com>
** Original version
*/
#define __KERNEL__
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/fcntl.h>
#include <linux/rtc.h>#include <linux/miscdevice.h>
#include <linux/string.h>
#include <linux/proc_fs.h>
#include <asm/bitops.h>
#include <asm/arch/AT91RM9200_SYS.h>
#include <asm/arch/AT91RM9200_TWI.h>
#define AT91C_DS1307_I2C_ADDRESS (0x68<<16)
#define AT91C_DS1307_READ_OK 0
#define AT91C_DS1307_WRITE_OK 0
#define AT91C_TWI_CLOCK 100000
#define BCD2BIN(val) (((val)&15) + ((val)>>4)*10)#define BIN2BCD(val) ((((val)/10)<<4) + (val)%10)#define TWELVE_HOUR_MODE(n) (((n)>>6)&1)
#define HOURS_AP(n) (((n)>>5)&1)
#define HOURS_12(n) BIN2BCD((n)&0x1F)
#define HOURS_24(n) BIN2BCD((n)&0x3F)
static int AT91F_TWI_Write(char,char *,char);
static int AT91F_TWI_Read(char,char *,char);
static void AT91F_TWI_Init(void);
static void get_rtc_time(struct rtc_time *rtc_tm);
static int set_rtc_time(struct rtc_time *rtc_tm);
spinlock_t at91_rtc_lock;
char rtc_name[]="AT91_RTCEX";
static unsigned int major =250;
/*
----------------------------------------------------------------------------
\fn AT91F_TWI_Write
\brief Write n bytes to a slave device
----------------------------------------------------------------------------
*/
int AT91F_TWI_Write(char address, char *data2send, char size)
{
unsigned int status;
AT91PS_TWI twi = (AT91PS_TWI) AT91C_VA_BASE_TWI;
// Set the TWI Master Mode Register
twi->TWI_MMR = ( AT91C_DS1307_I2C_ADDRESS | AT91C_TWI_IADRSZ_1_BYTE ) & ~AT91C_TWI_MREAD;
// Set TWI Internal Address Register
twi->TWI_IADR = address;
status = twi->TWI_SR;
twi->TWI_THR = *(data2send++);
twi->TWI_CR = AT91C_TWI_START;
while (size-- >1){
// Wait THR Holding register to be empty
while (!(twi->TWI_SR & AT91C_TWI_TXRDY));
// Send first byte
twi->TWI_THR = *(data2send++);
}
twi->TWI_CR = AT91C_TWI_STOP;
status = twi->TWI_SR;
// Wait transfer is finished
while (!(twi->TWI_SR & AT91C_TWI_TXCOMP));
return AT91C_DS1307_WRITE_OK;
}
//*----------------------------------------------------------------------------
//* \fn AT91F_TWI_Read
//* \brief Read n bytes from a slave device
//*----------------------------------------------------------------------------
int AT91F_TWI_Read(char address, char *data, char size)
{
unsigned int status;
AT91PS_TWI twi = (AT91PS_TWI) AT91C_VA_BASE_TWI;
// Set the TWI Master Mode Register
twi->TWI_MMR = AT91C_DS1307_I2C_ADDRESS | AT91C_TWI_IADRSZ_1_BYTE | AT91C_TWI_MREAD;
// Set TWI Internal Address Register
twi->TWI_IADR = address;
// Start transfer
twi->TWI_CR = AT91C_TWI_START;
status = twi->TWI_SR;
while (size-- >1){
// Wait RHR Holding register is full
while (!(twi->TWI_SR & AT91C_TWI_RXRDY));
// Read byte
*(data++) = twi->TWI_RHR;
}
twi->TWI_CR = AT91C_TWI_STOP;
status = twi->TWI_SR;
// Wait transfer is finished
while (!(twi->TWI_SR & AT91C_TWI_TXCOMP));
// Read last byte
*data = twi->TWI_RHR;
return AT91C_DS1307_READ_OK;
}
void AT91F_SetTwiClock(AT91PS_TWI pTwi)
{
int sclock;
/* Here, CKDIV = 1 and CHDIV=CLDIV ==> CLDIV = CHDIV = 1/4*((Fmclk/FTWI) -6)*/
sclock = (10*AT91C_MASTER_CLOCK /AT91C_TWI_CLOCK);
if (sclock % 10 >= 5)
sclock = (sclock /10) - 5;
else
sclock = (sclock /10)- 6;
sclock = (sclock + (4 - sclock %4)) >> 2; // div 4
pTwi->TWI_CWGR = 0x00010000 | sclock | (sclock << 8);
}
//*----------------------------------------------------------------------------
//* \fn AT91F_TWI_Init
//* \brief Init TWI Controller
//*----------------------------------------------------------------------------
void AT91F_TWI_Init()
{
AT91PS_TWI twi = (AT91PS_TWI) AT91C_VA_BASE_TWI;
//initiate TWI
AT91_SYS->PIOA_ASR = 0x06000000;//Assigns the I/O line to the Peripheral A function(TWD,TWCK)
AT91_SYS->PIOA_PDR = 0x06000000;//enables peripheral control of the pin
AT91_SYS->PIOA_MDER = 0x06000000;//Enables Multi Drive on the I/O line.(Define TWD and TWCK as open-drain)
AT91_SYS->PMC_PCER = 0x00001000;
twi->TWI_IDR = 0xffffffff;//Disable interrupts
twi->TWI_CR = 0x24;//the master data transfer is enabled
//* Here, CKDIV = 1 and CHDIV=CLDIV ==> CLDIV = CHDIV = 1/4*((Fmclk/FTWI) -6)
AT91F_SetTwiClock((AT91PS_TWI) AT91C_VA_BASE_TWI);
twi->TWI_CWGR = 0x00019595; //Set TWI Clock Waveform Generator Register
}
static int rtc_open(struct inode *inode, struct file *file)
{ MOD_INC_USE_COUNT;
return 0;
}static int rtc_release(struct inode *inode, struct file *file)
{ MOD_DEC_USE_COUNT;
return 0;
}
static int rtc_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg){
struct rtc_time tm, tm2; int ret = 0; spin_lock_irq(&at91_rtc_lock); switch (cmd) { case RTC_AIE_OFF: /* alarm off */ break;
case RTC_AIE_ON: /* alarm on */ break;
case RTC_UIE_OFF: /* update off */ break;
case RTC_UIE_ON: /* update on */ break;
case RTC_PIE_OFF: /* periodic off */ break;
case RTC_PIE_ON: /* periodic on */ break;
case RTC_ALM_READ: /* read alarm */ break;
case RTC_ALM_SET: /* set alarm */ break;
case RTC_RD_TIME: /* read time */ memset(&tm, 0, sizeof(struct rtc_time)); get_rtc_time(&tm);
ret = copy_to_user((void *) arg, &tm, sizeof(tm)) ? -EFAULT : 0; break; case RTC_SET_TIME: /* set time */ if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(tm)))
ret = -EFAULT; else
{
if (set_rtc_time(&tm)!=0)
ret = -EFAULT;
}
break;
case RTC_IRQP_READ: /* read periodic alarm frequency */ break;
case RTC_IRQP_SET: /* set periodic alarm frequency */ break;
case RTC_EPOCH_READ: /* read epoch */ break;
default: ret = -EINVAL; break; } spin_unlock_irq(&at91_rtc_lock); return ret;
}
static void get_rtc_time(struct rtc_time *rtc_tm)
{
char data[7];
//AT91F_TWI_Write(0x3F,data,1);
AT91F_TWI_Read(0x0,(char*)&data,7);
rtc_tm->tm_year = 2000+BCD2BIN(data[6]);
rtc_tm->tm_mon = BCD2BIN(data[5]);//((data[5]>>4)&0x01)*10+data[5]&15;
rtc_tm->tm_mday =BCD2BIN(data[4]);//((data[4]>>4)&0x03)*10+data[4]&15;
rtc_tm->tm_wday =BCD2BIN(data[3]);//data[3]&0x07;
rtc_tm->tm_hour = BCD2BIN(data[2]);//((data[2]>>4)&0x03)*10+data[2]&15;
/*if ( TWELVE_HOUR_MODE(data[2]) )
{
printk("HOURS_12\n");
rtc_tm->tm_hour = HOURS_12(data[2]);
if (HOURS_AP(data[2])) // PM
{
rtc_tm->tm_hour += 12;
}
}
else //24-hour-mode
{
printk("HOURS_24\n");
rtc_tm->tm_hour = HOURS_24(data[2]);
}*/
rtc_tm->tm_min =BCD2BIN(data[1]);//((data[1]>>4)&0x07)*10+data[1]&15;
rtc_tm->tm_sec = BCD2BIN(data[0]);//((data[0]>>4)&0x07)*10+data[0]&15;
}
static int set_rtc_time(struct rtc_time *rtc_tm)
{
char data[8];
//check value
if ( (rtc_tm->tm_mon<1) || (rtc_tm->tm_mon>12)
|| (rtc_tm->tm_mday<1) || (rtc_tm->tm_mday>31)
|| (rtc_tm->tm_wday<1) || (rtc_tm->tm_wday>7)
|| (rtc_tm->tm_hour<0) || (rtc_tm->tm_hour>23)
|| (rtc_tm->tm_min<0) || (rtc_tm->tm_min>59)
|| (rtc_tm->tm_sec<0) || (rtc_tm->tm_sec>59) )
return -EINVAL;
//control
data[7]=0x10;
data[6]=BIN2BCD(rtc_tm->tm_year -2000);
data[5]=BIN2BCD(rtc_tm->tm_mon);
data[4]=BIN2BCD(rtc_tm->tm_mday);
data[3]=BIN2BCD(rtc_tm->tm_wday);
data[2]=BIN2BCD(rtc_tm->tm_hour);
data[1]=BIN2BCD(rtc_tm->tm_min);
data[0]=BIN2BCD(rtc_tm->tm_sec);
AT91F_TWI_Write(0x0,data,8);
return 0;
}
static struct file_operations rtc_fops=
{
owner: THIS_MODULE,
ioctl: rtc_ioctl,
open: rtc_open,
release: rtc_release,
};
static struct miscdevice rtc_dev=
{
RTC_MINOR,
"rtc",
&rtc_fops
};
static int __init RTC_at91_init(void)
{
int retv;
AT91F_TWI_Init();
misc_register(&rtc_dev);
return 0;
}
/* unregister module */
static void __exit RTC_at91_cleanup(void)
{
misc_deregister(&rtc_dev);
}
module_init(RTC_at91_init);
module_exit(RTC_at91_cleanup);
MODULE_AUTHOR("Zheng Geng <gzheng@hyesco.com>");
MODULE_DESCRIPTION("AT91 Extra Realtime Clock Driver (AT91_RTCEX)");
MODULE_LICENSE("Proprietary");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -