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");