www.pudn.com > RTC_ds1307.rar > at91_rtcex.c
/* ** linux/drivers/at91/rtcex/at91_rtcex.c ** DS1307 time clock ** ** Copyright (C) 2004 Hyesco Technology Co.,Ltd ** ** Author: Zheng Geng** ** History: ** ** 2004.10 Zheng Geng ** Original version */ #define __KERNEL__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 "); MODULE_DESCRIPTION("AT91 Extra Realtime Clock Driver (AT91_RTCEX)"); MODULE_LICENSE("Proprietary");