【文件属性】:
文件名称:pt7c4307 驱动
文件大小:8KB
文件格式:C
更新时间:2013-11-03 09:59:37
pt7c4307 驱动
pt7c4307 驱动
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define DRIVER_VERSION "0.01"
/* Register map */
/* rtc section */
#define REG_SC 0x00
#define REG_MN 0x01
#define REG_HR 0x02
#define REG_HR_MIL (1<<6) /* 24h/12h mode */
#define REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */
#define REG_DW 0x03
#define REG_DT 0x04
#define REG_MO 0x05
#define REG_YR 0x06
#define RTC_SECTION_LEN 7
/* i2c configuration */
#define I2C_ADDR 0xd0
/////////////////////////////////////////////////////////////
#define DEFAULT_I2C_CLOCKDIV 180//for APB 108MHz
static unsigned long rtc_status;
static volatile unsigned long rtc_irq_data;
static unsigned long rtc_freq = 1; /*FTRTC010 supports only 1Hz clock*/
static struct fasync_struct *rtc_async_queue;
static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
extern spinlock_t rtc_lock;
MODULE_AUTHOR("GM Corp.");
MODULE_LICENSE("GM License");
extern int GM_i2c_xfer(struct i2c_msg *msgs, int num, int clockdiv);
/* block read */
static int i2c_read_regs(u8 reg, u8 buf[], unsigned len)
{
struct i2c_msg msgs[1];
//////////////
buf[0] = reg;
msgs[0].addr = I2C_ADDR>>1;
msgs[0].flags = 0;
msgs[0].len = 1;
msgs[0].buf = buf;
if (GM_i2c_xfer(msgs, 1, DEFAULT_I2C_CLOCKDIV) != 1)
return -1;
//////////////
msgs[0].addr = I2C_ADDR>>1;
msgs[0].flags = 1;
msgs[0].len = len+1;
msgs[0].buf = buf;
if (GM_i2c_xfer(msgs, 1, DEFAULT_I2C_CLOCKDIV) != 1)
return -1;
return 0;
}
/* block write */
static int i2c_set_regs(u8 reg, u8 const buf[], unsigned len)
{
u8 i2c_buf[7];
struct i2c_msg msgs[1];
i2c_buf[0] = reg;
memcpy(&i2c_buf[1], &buf[0], len);
msgs[0].addr = I2C_ADDR>>1;
msgs[0].flags = 0;
msgs[0].len = len+1;
msgs[0].buf = i2c_buf;
if (GM_i2c_xfer(msgs, 1, DEFAULT_I2C_CLOCKDIV) != 1)
return -1;
return 0;
}
static int set_time(struct rtc_time const *tm)
{
int sr;
u8 regs[RTC_SECTION_LEN] = { 0, };
//printk("set_time Date(y/m/d):%d/%d/%d Time(h/m/s):%d/%d/%d\n",tm->tm_year,tm->tm_mon,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
regs[REG_SC] = BIN2BCD(tm->tm_sec);
regs[REG_MN] = BIN2BCD(tm->tm_min);
regs[REG_HR] = BIN2BCD(tm->tm_hour);
regs[REG_DT] = BIN2BCD(tm->tm_mday);
regs[REG_MO] = BIN2BCD(tm->tm_mon + 1);
regs[REG_YR] = BIN2BCD(tm->tm_year - 100);
regs[REG_DW] = BIN2BCD(tm->tm_wday & 7);
/* write RTC registers */
sr = i2c_set_regs(0, regs, RTC_SECTION_LEN);
if (sr < 0) {
printk("%s: writing RTC section failed\n",
__func__);
return sr;
}
return 0;
}
static int read_time (struct rtc_time *tm)
{
int sr;
u8 regs[RTC_SECTION_LEN] = { 0, };
sr = i2c_read_regs(0, regs, RTC_SECTION_LEN);
if (sr < 0) {
printk("%s: reading RTC section failed\n",
__func__);
return sr;
}
tm->tm_sec = BCD2BIN(regs[REG_SC]);
tm->tm_min = BCD2BIN(regs[REG_MN]);
{ /* HR field has a more complex interpretation */
const u8 _hr = regs[REG_HR];
if ((_hr & REG_HR_MIL) == 0){ /* 24h format */
tm->tm_hour = BCD2BIN(_hr & 0x3f) - 1;
// if (tm->tm_hour > 23)
// tm->tm_hour = 0;
}
else { // 12h format
tm->tm_hour = BCD2BIN(_hr & 0x1f);
if (_hr & REG_HR_PM) /* PM flag set */
tm->tm_hour += 12;
}
}
tm->tm_mday = BCD2BIN(regs[REG_DT]);
tm->tm_mon = BCD2BIN(regs[REG_MO]) - 1; /* rtc starts at 1 */
tm->tm_year = BCD2BIN(regs[REG_YR]) + 100;
tm->tm_wday = BCD2BIN(regs[REG_DW]);
//printk("read_time Date(y/m/d):%d/%d/%d Time(h/m/s):%d/%d/%d\n",tm->tm_year,tm->tm_mon,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
return 0;
}
static unsigned AIE_stat=0;
/*ijsung: arch-indep function*/
static int rtc_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit (1, &rtc_status))
return -EBUSY;
rtc_irq_data = 0;
return 0;
}
static int rtc_release(struct inode *inode, struct file *file)
{
rtc_status = 0;
return 0;
}
static int rtc_fasync (int fd, struct file *filp, int on)
{
return fasync_helper (fd, filp, on, &rtc_async_queue);
}
static unsigned int rtc_poll(struct file *file, poll_table *wait)
{
poll_wait (file, &rtc_wait, wait);
return (rtc_irq_data) ? 0 : POLLIN | POLLRDNORM;
}
static loff_t rtc_llseek(struct file *file, loff_t offset, int origin)
{
return -ESPIPE;
}
ssize_t rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos)
{
DECLARE_WAITQUEUE(wait, current);
unsigned long data;
ssize_t retval;
if (count < sizeof(unsigned long))
return -EINVAL;
add_wait_queue(&rtc_wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);
for (;;) {
spin_lock_irq (&rtc_lock);
data = rtc_irq_data;
if (data != 0) {
rtc_irq_data = 0;
break;
}
spin_unlock_irq (&rtc_lock);
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
goto out;
}
if (signal_pending(current)) {
retval = -ERESTARTSYS;
goto out;
}
schedule();
}
spin_unlock_irq (&rtc_lock);
data -= 0x100; /* the first IRQ wasn't actually missed */
retval = put_user(data, (unsigned long *)buf);
if (!retval)
retval = sizeof(unsigned long);
out:
set_current_state(TASK_RUNNING);
remove_wait_queue(&rtc_wait, &wait);
return retval;
}
static int rtc_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct rtc_time tm;
switch (cmd) {
case RTC_AIE_OFF:
printk("Not Support\n");
return 0;
case RTC_AIE_ON:
printk("Not Support\n");
return 0;
case RTC_ALM_READ:
printk("Not Support\n");
return 0;
case RTC_ALM_SET:
printk("Not Support\n");
return 0;
case RTC_RD_TIME:
read_time(&tm);
break;
case RTC_SET_TIME:
{
if (!capable(CAP_SYS_TIME))
return -EACCES;
if (copy_from_user (&tm, (struct rtc_time*)arg, sizeof (tm)))
return -EFAULT;
set_time(&tm);
}
return 0;
case RTC_IRQP_READ:
return put_user(rtc_freq, (unsigned long *)arg);
case RTC_IRQP_SET:
if (arg != 1) return -EINVAL;
return 0;
case RTC_EPOCH_READ:
return put_user (1970, (unsigned long *)arg);
default:
return -EINVAL;
}
return copy_to_user ((void *)arg, &tm, sizeof (tm)) ? -EFAULT : 0;
}
static struct file_operations rtc_fops = {
owner: THIS_MODULE,
llseek: rtc_llseek,
read: rtc_read,
poll: rtc_poll,
ioctl: rtc_ioctl,
open: rtc_open,
release: rtc_release,
fasync: rtc_fasync,
};
static struct miscdevice ftrtc010rtc_miscdev = {
RTC_MINOR,
"rtc",
&rtc_fops
};
static int rtc_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
char *p = page;
int len;
struct rtc_time tm;
read_time(&tm);
//printk("RTC ... %d\n",xtime.tv_sec);
p += sprintf(p, "rtc_time\t: %02d:%02d:%02d\n"
"rtc_date\t: %04d-%02d-%02d\n"
"rtc_epoch\t: %04d\n",
tm.tm_hour + 1, tm.tm_min, tm.tm_sec,
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 2000);
// read_alarm(&tm);
// p += sprintf(p, "alrm_time\t: %2dth day of week day\n"
// "alrm_date\t: N/A for Platform\n",
// tm.tm_wday);
p += sprintf(p, "alrm_time\t: Not Support\n"
"alrm_date\t: Not Support\n");
p += sprintf(p, "alarm_IRQ\t: %s\n", AIE_stat ? "yes" : "no" );
len = (p - page) - off;
if (len < 0)
len = 0;
*eof = (len <= count) ? 1 : 0;
*start = page + off;
return len;
}
static int __init rtc_init(void)
{
#if 0
volatile unsigned char buf[0];
buf[0] = 0x13;//output clock
printk("clock adjustment\n");
i2c_set_regs(7, buf, 1);
buf[0] = 0xA5;
i2c_read_regs(7, buf, 1);
printk("data = %x\n",buf[0]);
#endif
misc_register (&ftrtc010rtc_miscdev);
create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL);
printk("pt7c4307 Real Time Clock driver\n");
return 0;
}
static void __exit rtc_exit(void)
{
remove_proc_entry ("driver/rtc", NULL);
misc_deregister (&ftrtc010rtc_miscdev);
}
module_init(rtc_init);
module_exit(rtc_exit);