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

📄 at91_ds1337.c

📁 at91sam9261的linux下的时钟芯片ds1337的驱动程序
💻 C
字号:
/*
 **********************************************************
 *ds1337 读/写时序 
 *  写: START->11010000->ACK->REGISTER ADD->ACK->DATA_n->ACK->DATA_n+1->...>ACK->STOP 
 *  读: START->11010000->ACK->REGISTER ADD->ACK->START->11010001->ACK->
        DATA_n->ACK->DATA_n+1->...>ACK->STOP 
 *  地址/数据均为8bit       
 ***********************************************************
 */
#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 <linux/delay.h>
#include <asm/io.h>

#include <asm/arch/at91sam9261_pio.h>
#include "at91_ds1337.h"

#define DELAY_TIME    30 //NOTE: max delay = 2000us

#define uint  unsigned int
#define uchar unsigned char
//#define DEBUG 1

#define BCD2BIN(val) (((val)&15) + ((val)>>4)*10)
#define BIN2BCD(val) ((((val)/10)<<4) + (val)%10)
#define EPOCH		1970

static const unsigned char days_in_mo[] =
    { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

#define is_leap(year) \
	((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))

static const unsigned short int __mon_yday[2][13] =
{
	/* Normal years.  */
	{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
	/* Leap years.  */
	{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};

static void  set_sda(void);
static void  clr_sda(void);
static void  set_sck(void);
static void  clr_sck(void);
static void  sda_in(uchar dat);
uchar  sda_out(void);

/*
***********************************************************
*使用PIOA口模拟SDA,SCK 
*SDA=PA7 SCK=PA8
***********************************************************
*/
#define SDA 7
#define SCK 8
#define PIOA_REG(val) (AT91C_VA_BASE_PIOA + val)


/***********************************************************/
static void  ds1337_at91sam9261_init(void)
{      	  
	//by soloo 08.8.5
	at91_gpio_configure(AT91C_VA_BASE_PIOA, SDA, OUTPUT_MODE, NO_PULL_UP, GLITCH_FLT_ON);
	at91_gpio_configure(AT91C_VA_BASE_PIOA, SCK, OUTPUT_MODE, NO_PULL_UP, GLITCH_FLT_ON);
	writel(0x01<<AT91_ID_PIOA, AT91C_VA_BASE_PMC + PMC_PCER);  //enable pioA clock
	writel((0x01<<SDA)|(0x01<<SCK), PIOA_REG(PIO_MDER));
}

/***********************************************************/
static void  set_sda(void)
{
	writel((0x01<<SDA), PIOA_REG(PIO_OER));
	writel((0x01<<SDA), PIOA_REG(PIO_SODR));
}

/***********************************************************/
static void  clr_sda(void)
{
	writel((0x01<<SDA), PIOA_REG(PIO_OER));
	writel((0x01<<SDA), PIOA_REG(PIO_CODR));
}

/***********************************************************/
static void  set_sck(void)
{
	writel((0x01<<SCK), PIOA_REG(PIO_OER));
	writel((0x01<<SCK), PIOA_REG(PIO_SODR));	
}

/***********************************************************/
static void  clr_sck(void)
{
	writel((0x01<<SCK), PIOA_REG(PIO_OER));
	writel((0x01<<SCK), PIOA_REG(PIO_CODR));
}

/*
***********************************************************
*按bit串行输入ds1337
***********************************************************
*/
static void  sda_in(uchar dat)
{
	if((dat&0x80)==0x80)
		set_sda();
   	else
		clr_sda();				
}

/*
***********************************************************
*按bit串行从ds1337输出
***********************************************************
*/
uchar sda_out(void)
{
/*
*当PIO引脚变化中断及读引脚状态时,需使能PMC外设时钟	 
*/	

	writel((0x01<<SDA), PIOA_REG(PIO_ODR));
	writel((0x01<<SDA), PIOA_REG(PIO_OWDR));
	writel((0x01<<SDA), PIOA_REG(PIO_IFER));
	udelay(DELAY_TIME);	
	
	if (*(volatile unsigned int *)PIOA_REG(PIO_PDSR) & (0x01<<SDA))
		return 1;	
	else
		return 0;		
}

/* 
***********************************************************
*ds1337开始函数 //soloo
***********************************************************
*/
static void ds1337_start(void)
{  
	clr_sck();//SCK=0;  
	udelay(DELAY_TIME);    
	set_sck();//SCK=1;   
	udelay(DELAY_TIME);    
	set_sda();//SDA=1;                 
	udelay(DELAY_TIME);    
	clr_sda();//SDA=0;
	udelay(DELAY_TIME);    
	clr_sck();//SCK=0;  
	udelay(DELAY_TIME);         
}

/*
***********************************************************
*ds1337停止函数 
*********************************************************** 
*/
static void ds1337_stop(void)
{   
	clr_sda();//SDA=0;
	udelay(DELAY_TIME);    
	set_sck();//SCK=1;   
	udelay(DELAY_TIME);    
	set_sda();//SDA=1;
	udelay(DELAY_TIME);    
	clr_sck();//SCK=0;  
	udelay(DELAY_TIME);    
}

/*
***********************************************************
*cpu 向ds1337发确认函数 
***********************************************************
*/
static void cpu_to_ds1337_ack(void)
{      
	clr_sda();//SDA=0;bI2C_SDA = 0;
	udelay(DELAY_TIME);    
	set_sck();//SCK=1;
	udelay(DELAY_TIME);    
	clr_sck();//SCK=0;     
	udelay(DELAY_TIME);    
}

/*
***********************************************************
*cpu 不向ds1337发确认函数,将SDA拉高 
***********************************************************
*/
static void cpu_to_ds1337_no_ack(void)
{      
	clr_sck();//SCK=0;     
	udelay(DELAY_TIME);    
	set_sda();//SDA=1;
	udelay(DELAY_TIME);    
	set_sck();//SCK=1;     
	udelay(DELAY_TIME);    
	clr_sck();//SCK=0;     
}


/*
*********************************************************** 
*ds1337串行输入函数 
***********************************************************
*/
static void ds1337_si(uchar  dat)
{
	uchar i=8; 
	while(i--) 
	{
		clr_sck();//SCK=0;
		udelay(DELAY_TIME);
		sda_in(dat);//SDA=(bit)(dat&0x80);
		dat<<=1; 
		udelay(DELAY_TIME);
		set_sck();//SCK=1;	
		udelay(DELAY_TIME);
	}      
	clr_sck();//SCK=0;
	udelay(DELAY_TIME);
	
	//set_sda();//SDA=1;waitAck  SOLO+++++++++++++++++
	//udelay(DELAY_TIME);
	
	set_sck();//SCK=1;
	udelay(DELAY_TIME);
	clr_sck();//SCK=0;

	for(i=0;i<10;i++){
		udelay(2000);  // delay_20ms ,wait for ack from ds1337
	}

}

/*
***********************************************************
*ds1337串行输出函数 
***********************************************************
*/
uchar ds1337_so(void)
{
	uchar i=8;
	uchar dat=0x00;
	clr_sck();//SCK=0;
	udelay(DELAY_TIME);
	set_sda();//SDA=1;
	while(i--) 
	{      
		dat<<=1;
		set_sck();//SCK=1;
		udelay(DELAY_TIME); 
		if (sda_out()) dat|=0x01;
		clr_sck();//SCK=0;
		udelay(DELAY_TIME);     
	} 
	return(dat);
}


/* 
***********************************************************
*读 ds1337 函数 
***********************************************************
*/
static void ds1337_rb(uint addr,uint byte_num,uchar *pdat)/* random read */
{
   uint  tmp;   
   ds1337_start();
 
   ds1337_si(DEV_WRITE_ADD);     // device address /write         
   udelay(DELAY_TIME);
   ds1337_si(addr&0xFF);       //A7-A0
   udelay(DELAY_TIME);
   ds1337_start();   
   ds1337_si(DEV_READ_ADD);      // device address /read      
   udelay(DELAY_TIME);
   for(tmp=0;tmp<byte_num;tmp++)
   {
   	*pdat = ds1337_so();
   	 pdat++;
   	 if(tmp+1 < byte_num)
     	cpu_to_ds1337_ack();
   }
   cpu_to_ds1337_no_ack();
   ds1337_stop(); 	
}

/*
*********************************************************** 
* 向ds1337写字节函数 
***********************************************************
*/
static void ds1337_wb(uint addr, uint byte_num,uchar *pdat)     /* write a byte */
{
   uint tmp;	   
   ds1337_start();   
      
   ds1337_si(DEV_WRITE_ADD);     // device address /write        
   udelay(DELAY_TIME);   
   ds1337_si(addr&0xFF);       //A7-A0
   udelay(DELAY_TIME);
   for(tmp=0;tmp<byte_num;tmp++)
   {
     ds1337_si(*pdat++);
   }   
   ds1337_stop();
   
   for(tmp=0;tmp<10;tmp++)
   {
   		udelay(2000);  // delay_20ms
   } 	
}

/*
***********************************************************
* ds1337 时钟初始化
***********************************************************
*/

static void  ds1337_clock_init(void)
{
	//real time colock register(BCD format):
	//      second:minute:hour:day:month:year:dayweek:century19/20        	
	
	uchar temp = 0x00;								  
	uchar date[7]={0,0,0,2,1,0x81,0x08};// 01/12/07 15:55:0 Friday
	ds1337_wb(CR,1,&temp);//start the ds1337 oscillator
	ds1337_wb(SC,7,date);	
	
}



/*
***********************************************************
*      RTC 实时时钟寄存器顺序
*      秒,分,时,日,月,年,星期,世纪
*0x30: 0   1   2   3   4   5   6    7    
***********************************************************
*/
static void  get_time(uchar *rdbuf)
{      
      ds1337_rb(SC,7,rdbuf);   
      *(rdbuf+2) &= 0x3F;         
     
}

/*
 * Returns day since start of the year [0-365]
 *  (from drivers/char/efirtc.c)
 */
static inline int compute_yday(int year, int month, int day)
{
	return  __mon_yday[is_leap(year)][month] + day-1;
}

/*
 * Set current time and date in RTC
 */
static void ds1337_rtc_settime(struct rtc_time *tval)
{
	uchar date[7];
	date[0] = BIN2BCD(tval->tm_sec);
	date[1] = BIN2BCD(tval->tm_min);
	date[2] = BIN2BCD(tval->tm_hour);	 //24hour
	date[3] = BIN2BCD(tval->tm_wday + 1);/* day of the week [0-6], Sunday=0 */
	date[4] = BIN2BCD(tval->tm_mday);
	
	if(tval->tm_year/100 == 19)
		date[5] = BIN2BCD(tval->tm_mon+1);
	else 
		date[5] = BIN2BCD(tval->tm_mon+1) | 0x80;	//Bit7=1 is 21 centery
	
	date[6] = BIN2BCD(tval->tm_year % 100);
	
	ds1337_wb(SC,7,date);    
}

/*
 * Decode time/date into rtc_time structure
 */
static void ds1337_rtc_decodetime(uchar* time, struct rtc_time *tval)
{

	tval->tm_sec = BCD2BIN(time[0]);
	tval->tm_min = BCD2BIN(time[1]);
	tval->tm_hour = BCD2BIN(time[2]);

	tval->tm_wday = BCD2BIN(time[3]) - 1;	/* day of the week [0-6], Sunday=0 */
	tval->tm_mday = BCD2BIN(time[4]);

	/* The Calendar Alarm register does not have a field for
	   the year - so these will return an invalid value.  When an
	   alarm is set, at91_alarm_year wille store the current year. */
	if(time[5] & 0x80)
		tval->tm_year = 2000;		/* 1 is 21 centry*/
	else 
		tval->tm_year = 1900;		/* 0 is 20 century */

	tval->tm_year += BCD2BIN(time[6]);		/* year */
	
	time[5] &= 0x1F;
	tval->tm_mon = BCD2BIN(time[5])-1;  //WHY?   SOLO+++++++++++++
	
}


static int ds1337_rtc_open(struct inode *inode, struct file *file)
{
	return 0;
}


ssize_t ds1337_rtc_read(struct file * file, char *buf, size_t count, loff_t * ppos)
{
	ssize_t retval;
	uchar time[7];

	//get raw time,don't decode
	get_time(time); 
		
	if (count < 7)
		return -EINVAL;

	retval = copy_to_user((void *) buf, time, 7) ? -EFAULT : 0;
	if (!retval)
		retval = sizeof(unsigned long);  //WHY?  SOLO++++++++++++

	return retval;
}

/*
 * Handle commands from user-space
 */
static int ds1337_rtc_ioctl(struct inode *inode, struct file *file,
			  unsigned int cmd, unsigned long arg)
{
	struct rtc_time tm;
	int ret = 0;
	uchar time[7];

	switch (cmd) {
	case RTC_RD_TIME:	/* read time */
		get_time(time); 
		ds1337_rtc_decodetime(time, &tm);
		tm.tm_yday = compute_yday(tm.tm_year, tm.tm_mon, tm.tm_mday);
		ret = copy_to_user((void *) arg, &tm, sizeof(tm)) ? -EFAULT : 0;
		break;
	case RTC_SET_TIME:	/* set time */
		if (!capable(CAP_SYS_TIME))
			ret = -EACCES;
		else {
			if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(tm)))
				ret = -EFAULT;
			else {
				int tm_year = tm.tm_year;
				if (tm_year < EPOCH
				    || (unsigned) tm.tm_mon >= 12
				    || tm.tm_mday < 1
				    || tm.tm_mday > (days_in_mo[tm.tm_mon] + (tm.tm_mon == 1 && is_leap(tm_year)))
				    || (unsigned) tm.tm_hour >= 24
				    || (unsigned) tm.tm_min >= 60
				    || (unsigned) tm.tm_sec >= 60)
					ret = -EINVAL;
				else
					ds1337_rtc_settime(&tm);
			}
		}
		break;
	default:
		ret = -EINVAL;
		break;
	}
	
	return ret;
}

/*
 * Provide RTC information in /proc/driver/ds1337_rtc
 */
static int ds1337_rtc_read_proc(char *page, char **start, off_t off,
			      int count, int *eof, void *data)
{
	int len=0;
	
	char *p = page;
	struct rtc_time tm;

	uchar status_r,control_r,time[8];

	ds1337_rb(SR,1,&status_r);
	ds1337_rb(CR,1,&control_r);
	
	get_time(time); 
	ds1337_rtc_decodetime(time, &tm);
	p += sprintf(p, "SR--%x, CR--%x;\nrtc_time\t: %02d:%02d:%02d\n"
			"rtc_date\t: %04d-%02d-%02d\n"
			"rtc_epoch\t: %04d\n",
			status_r, control_r, tm.tm_hour, tm.tm_min, tm.tm_sec,
			tm.tm_year, tm.tm_mon+1, tm.tm_mday, EPOCH);

	len = (p - page) - off;
	if (len < 0)
		len = 0;

	*eof = (len <= count) ? 1 : 0;
	*start = page + off;

	return len;
}

static struct file_operations ds1337_rtc_fops = {
	.owner = THIS_MODULE,
	.read = ds1337_rtc_read,
	.ioctl = ds1337_rtc_ioctl,
	.open = ds1337_rtc_open,
};

static struct miscdevice ds1337_rtc_miscdev = {
	.minor = DS1337_RTC_MINOR,
	.name = "ds1337_rtc",
	.fops = &ds1337_rtc_fops,
};

/*
 * Initialize and install RTC driver
 */
static int __init ds1337_rtc_init(void)
{
	int ret=0;

	ds1337_at91sam9261_init();
	ds1337_clock_init();

	misc_register(&ds1337_rtc_miscdev);
	
	create_proc_read_entry("driver/ds1337_rtc", 0, 0, ds1337_rtc_read_proc, NULL);

	printk(KERN_INFO "ds1337 Real Time Clock driver\n");
	
	return ret;
}

/*
 * Disable and remove the RTC driver
 */
static void __exit ds1337_rtc_exit(void)
{
	remove_proc_entry("driver/ds1337_rtc", NULL);
	misc_deregister(&ds1337_rtc_miscdev);
}

module_init(ds1337_rtc_init);
module_exit(ds1337_rtc_exit);

MODULE_AUTHOR("SOLOO");
MODULE_DESCRIPTION("ds1337 Realtime Clock Driver (ds1337_RTC) for AT91sam9261s");
MODULE_LICENSE("GPL");

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -