Linux GPIO模拟I2C做RTC驱动

王伟1990 2015-01-05 06:50:54
最近用爱普生的RX8010芯片做rtc驱动,发现每次在经过零点的时候,读出来的日期寄存器的值都会乘以2,求问各位有没有遇见过啊?或者能给提下意见吗?不胜感激~~~
描述一下细节问题:
当没有经过零点的时候,每次写入多少,断电一段时间后读出来的都很准。但是如果我写入2014-01-05 23:59,断电后过一会读出来的却是2014-01-10 00:01,只有日期寄存器的值变了,而且每次都是乘以2,也就是日期该加1,但却x2。
驱动程序里面只是简单的写入和读取,根本没有对是否经过零点做处理啊!求各位帮忙解答啦。
驱动代码如下:
#define RX8010_REG_SC 0x10 /* datetime */
#define RX8010_REG_MN 0x11
#define RX8010_REG_HR 0x12
#define RX8010_REG_DM 0x13
#define RX8010_REG_DW 0x14
#define RX8010_REG_MO 0x15
#define RX8010_REG_YR 0x16
#define RX8010_MO_C 0x80 /* century */

#define RTC_DEV_ADDR 0x32
#define GPIO_0_BASE 0x201A0000 //need change


void __iomem *reg_gpio0_base_va;
#define IO_ADDRESS_VERIFY(x) (reg_gpio0_base_va + ((x)-(GPIO_0_BASE)))

//define direction!
#define GPIO_0_DIR IO_ADDRESS_VERIFY(GPIO_0_BASE + 0x400)

#define SCL (1 << 4) /* GPIO 5_4 */
#define SDA (1 << 3) /* GPIO 5_3 */
#define GPIO_I2C_SDA_REG IO_ADDRESS_VERIFY(GPIO_0_BASE + 0x20)
#define GPIO_I2C_SCL_REG IO_ADDRESS_VERIFY(GPIO_0_BASE + 0x40)

#define GPIO_I2C_SCLSDA_REG IO_ADDRESS_VERIFY(GPIO_0_BASE + 0x60)

//read register value!
#define HW_REG(reg) *((volatile unsigned int *)(reg))
#define DELAY(us) time_delay_us(us)

static int s_c_polarity = 0; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */

/*
* I2C by GPIO simulated read data routine.
*
* @return value: a bit for read
*
*/

static unsigned char i2c_data_read(void)
{
unsigned char regvalue;

regvalue = HW_REG(GPIO_0_DIR);
regvalue &= (~SDA);
HW_REG(GPIO_0_DIR) = regvalue;
DELAY(1);

regvalue = HW_REG(GPIO_I2C_SDA_REG);
if((regvalue&SDA) != 0)
return 1;
else
return 0;
}



/*
* sends a start bit via I2C rountine.
*
*/
static void i2c_start_bit(void)
{
DELAY(1);
i2c_set(SDA | SCL);
DELAY(1);
i2c_clr(SDA);
DELAY(1);
}

/*
* sends a stop bit via I2C rountine.
*
*/
static void i2c_stop_bit(void)
{
/*clock the ack*/
DELAY(1);
i2c_set(SCL);
DELAY(1);
i2c_clr(SCL);

/* actual stop bit */
DELAY(1);
i2c_clr(SDA);
DELAY(1);
i2c_set(SCL);
DELAY(1);
i2c_set(SDA);
DELAY(1);
}

/*
* sends a character over I2C rountine.
*
* @param c: character to send
*
*/
static void i2c_send_byte(unsigned char c)
{
int i;
local_irq_disable();
for (i=0; i<8; i++)
{
DELAY(1);
i2c_clr(SCL);
DELAY(1);

if (c & (1<<(7-i)))
i2c_set(SDA);
else
i2c_clr(SDA);

DELAY(1);
i2c_set(SCL);
DELAY(1);
i2c_clr(SCL);
}
DELAY(1);

local_irq_enable();
}

/* receives a character from I2C rountine.
*
* @return value: character received
*
*/
static unsigned char i2c_receive_byte(void)
{
int j=0;
int i;
unsigned char regvalue;

local_irq_disable();
for (i=0; i<8; i++)
{
DELAY(1);
i2c_clr(SCL);
DELAY(1);
i2c_set(SCL);

regvalue = HW_REG(GPIO_0_DIR);
regvalue &= (~SDA);
HW_REG(GPIO_0_DIR) = regvalue;
DELAY(1);
if (i2c_data_read())
j+=(1<<(7-i));

DELAY(1);
i2c_clr(SCL);
}
local_irq_enable();
DELAY(1);

return j;
}

/* receives an acknowledge from I2C rountine.
*
* @return value: 0--Ack received; 1--Nack received
*
*/
static int i2c_receive_ack(void)
{
int nack;
unsigned char regvalue;

DELAY(1);

regvalue = HW_REG(GPIO_0_DIR);
regvalue &= (~SDA);
HW_REG(GPIO_0_DIR) = regvalue;

DELAY(1);
i2c_clr(SCL);
DELAY(1);
i2c_set(SCL);
DELAY(1);



nack = i2c_data_read();

DELAY(1);
i2c_clr(SCL);
DELAY(1);

if (nack == 0)
return 1;

return 0;
}

#if 0
static void i2c_send_ack(void)
{
DELAY(1);
i2c_clr(SCL);
DELAY(1);
i2c_set(SDA);
DELAY(1);
i2c_set(SCL);
DELAY(1);
i2c_clr(SCL);
DELAY(1);
i2c_clr(SDA);
DELAY(1);
}
#endif

static unsigned char gpio_i2c_read(unsigned char devaddress, unsigned char address)
{
int rxdata;

i2c_start_bit();
i2c_send_byte((unsigned char)(devaddress));
i2c_receive_ack();
i2c_send_byte(address);
i2c_receive_ack();
i2c_start_bit();
i2c_send_byte((unsigned char)(devaddress) | 1);
i2c_receive_ack();
rxdata = i2c_receive_byte();

//i2c_send_ack();
//--------change --------
//i2c_send_ack();
i2c_stop_bit();

return rxdata;
}
//EXPORT_SYMBOL(gpio_i2c_read);

static void gpio_i2c_write(unsigned char devaddress, unsigned char address, unsigned char data)
{
//PRINTK("device_addr:0x%2x; reg_addr:0x%2x; reg_value:0x%2x.\n", devaddress, address, data);
i2c_start_bit();
i2c_send_byte((unsigned char)(devaddress));
i2c_receive_ack();
i2c_send_byte(address);
i2c_receive_ack();
i2c_send_byte(data);
i2c_stop_bit();
}
//EXPORT_SYMBOL(gpio_i2c_write);
static void gpio_i2c_init()
{
gpio_i2c_write(0x64,0x17,0xd8);
DELAY(20);
gpio_i2c_write(0x64,0x30,0x00);
DELAY(20);
gpio_i2c_write(0x64,0x31,0x08);
DELAY(20);
gpio_i2c_write(0x64,0x32,0x00);
DELAY(20);
gpio_i2c_write(0x64,0x1d,0x04);
DELAY(20);
gpio_i2c_write(0x64,0x1e,0x00);
DELAY(20);
gpio_i2c_write(0x64,0x1f,0x00);
DELAY(20);
}
EXPORT_SYMBOL(rx8010_read_datetime);
int rx8010_read_datetime(struct rtc_time *rdtm)
{
unsigned char device_addr = (RTC_DEV_ADDR << 1) & (~(1 << 0));
unsigned char cur_addr;
unsigned char buf[25] = {0};

for (cur_addr = RX8010_REG_SC; cur_addr < RX8010_REG_YR + 1; cur_addr ++)
{
buf[cur_addr] = gpio_i2c_read(device_addr, cur_addr);
PRINTK("0x%x 0x%x\n", cur_addr, buf[cur_addr]);
}
rdtm->tm_sec = bcd2bin(buf[RX8010_REG_SC] & 0x7F);
rdtm->tm_min = bcd2bin(buf[RX8010_REG_MN] & 0x7F);
rdtm->tm_hour = bcd2bin(buf[RX8010_REG_HR] & 0x3F); /* rtc hr 0-23 */
rdtm->tm_mday = bcd2bin(buf[RX8010_REG_DM] & 0x3F);
rdtm->tm_wday = buf[RX8010_REG_DW] & 0x07;
rdtm->tm_mon = bcd2bin(buf[RX8010_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
rdtm->tm_year = bcd2bin(buf[RX8010_REG_YR]);
if (rdtm->tm_year < 70)
rdtm->tm_year += 100; /* assume we are in 1970...2069 */
/* detect the polarity heuristically. see note above. */
s_c_polarity = (buf[RX8010_REG_MO] & RX8010_MO_C) ?
(rdtm->tm_year >= 100) : (rdtm->tm_year < 100);

rdtm->tm_isdst = 0;
rdtm->tm_yday = rtc_year_days(rdtm->tm_mday, rdtm->tm_mon, rdtm->tm_year + 1900);

PRINTK("tm is secs=%d, mins=%d, hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
rdtm->tm_sec, rdtm->tm_min, rdtm->tm_hour, rdtm->tm_mday, rdtm->tm_mon, rdtm->tm_year, rdtm->tm_wday);

/* the clock can give out invalid datetime, but we cannot return
* -EINVAL otherwise hwclock will refuse to set the time on bootup.
*/
if (rtc_valid_tm(rdtm) < 0)
{
PRINTK("retrieved date/time is not valid.\n");
return -1;
}


return 0;
}

EXPORT_SYMBOL(rx8010_write_datetime);
void rx8010_write_datetime(struct rtc_time *tm)
{
unsigned char device_addr = (RTC_DEV_ADDR << 1) & (~(1 << 0));
unsigned char cur_addr;
unsigned char buf[25] = {0};

/* hours, minutes and seconds */
buf[RX8010_REG_SC] = bin2bcd(tm->tm_sec);
buf[RX8010_REG_MN] = bin2bcd(tm->tm_min);
buf[RX8010_REG_HR] = bin2bcd(tm->tm_hour);

buf[RX8010_REG_DM] = bin2bcd(tm->tm_mday);

/* month, 1 - 12 */
buf[RX8010_REG_MO] = bin2bcd(tm->tm_mon + 1);

/* year and century */
buf[RX8010_REG_YR] = bin2bcd(tm->tm_year % 100);
if (s_c_polarity ? (tm->tm_year >= 100) : (tm->tm_year < 100))
buf[RX8010_REG_MO] |= RX8010_MO_C;

buf[RX8010_REG_DW] = tm->tm_wday & 0x07;


for (cur_addr=RX8010_REG_SC; cur_addr<RX8010_REG_YR+1; cur_addr++)
{
gpio_i2c_write(device_addr, cur_addr, buf[cur_addr]);
PRINTK("0x%x 0x%x\n", cur_addr, buf[cur_addr]);
}
}

static int rx8010_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
struct rtc_time *tm = (struct rtc_time *)arg;
gpio_i2c_init();
switch(cmd)
{
case RTC8010_READ:
if(rx8010_read_datetime(tm))
{
return -1;
}
break;

case RTC8010_WRITE:
rx8010_write_datetime(tm);
break;

default:
return -1;
}
return 0;
}


int rx8010_open(struct inode * inode, struct file * file)
{
return 0;
}
int rx8010_close(struct inode * inode, struct file * file)
{
return 0;
}

static struct file_operations rx8010_fops = {
.owner = THIS_MODULE,
.ioctl = rx8010_ioctl,
.open = rx8010_open,
.release = rx8010_close
};


static struct miscdevice rtc_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "rtcrx8010",
.fops = &rx8010_fops,
};

static int __init rtc_rx8010_init(void)
{
int ret;

reg_gpio0_base_va = ioremap_nocache(GPIO_0_BASE, 0x10000);


ret = misc_register(&rtc_dev);
if(0 != ret)
return -1;

#if 1
PRINTK("rx8010 init is ok!\n");
i2c_set(SCL | SDA);
#endif
return 0;
}

static void __exit rtc_rx8010_exit(void)
{
iounmap((void*)reg_gpio0_base_va);
misc_deregister(&rtc_dev);
}


module_init(rtc_rx8010_init);
module_exit(rtc_rx8010_exit);

#ifdef MODULE
//#include <linux/compile.h>
#endif
//MODULE_INFO(build, UTS_VERSION);
MODULE_LICENSE("GPL");
...全文
861 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
rongdongxie 2016-06-07
  • 打赏
  • 举报
回复
每次读写都执行了初始化: gpio_i2c_init(),可能是这个原因吧
dceacho 2015-01-08
  • 打赏
  • 举报
回复
没遇见过,太怪了
lr2131 2015-01-05
  • 打赏
  • 举报
回复
咩有时间仔细看,不过数字乘2了,我觉得有可能是不是左移了一位。 再仔细看看datasheet的吧

6,125

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 硬件设计
社区管理员
  • 硬件设计社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧