📄 at91_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 + -