⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 at91_rtcex.c

📁 平台:ARM9的嵌入式LINUX RTC时钟驱动
💻 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 + -