📄 nx8025.c
字号:
/* * nx8025.c * * Device driver for EPSON's Real Time Controller NX-8025. * * Copyright (C) 2002 Fudan Software Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */#include <linux/module.h>#include <linux/kernel.h>#include <linux/poll.h>#include <linux/i2c.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/rtc.h>#include <linux/time.h>#include <linux/string.h>#include <linux/miscdevice.h>#include <linux/proc_fs.h>#include "nx8025.h"#ifdef CONFIG_PM#include <linux/pm.h>#include <linux/pm_legacy.h>static struct pm_dev *pmdev;#endif#define DEBUG 0#if DEBUGstatic unsigned int rtc_debug = DEBUG;#else#define rtc_debug 0 /* gcc will remove all the debug code for us */#endifstatic unsigned short slave_address = NX8025_I2C_SLAVE_ADDR;struct i2c_driver nx8025_driver;struct i2c_client *nx8025_i2c_client = 0;static unsigned short ignore[] = { I2C_CLIENT_END };static unsigned short normal_addr[] = { NX8025_I2C_SLAVE_ADDR, I2C_CLIENT_END };static struct i2c_client_address_data addr_data = { normal_i2c: normal_addr, probe: ignore, ignore: ignore,};static int nx8025_rtc_ioctl( struct inode *, struct file *, unsigned int, unsigned long);static int nx8025_rtc_open(struct inode *inode, struct file *file);static int nx8025_rtc_release(struct inode *inode, struct file *file);static struct file_operations rtc_fops = { owner: THIS_MODULE, ioctl: nx8025_rtc_ioctl, open: nx8025_rtc_open, release: nx8025_rtc_release,};static struct miscdevice nx8025_rtc_miscdev = { EFI_RTC_MINOR, "rtc", &rtc_fops};/* * Client data (each client gets its own) */struct nx8025_data { struct i2c_client client; // u16 data;};static int nx8025_probe(struct i2c_adapter *adap);static int nx8025_detach(struct i2c_client *client);static int nx8025_command(struct i2c_client *client, unsigned int cmd, void *arg);struct i2c_driver nx8025_driver = { .driver={ .name= "NX8025", }, .attach_adapter= nx8025_probe, .detach_client= nx8025_detach, .command= nx8025_command,};static spinlock_t nx8025_rtc_lock = SPIN_LOCK_UNLOCKED;#define DAT(x) ((unsigned int)((x)->data)) /* keep the control register info */static intnx8025_readram( char *buf, int len){ unsigned long flags; unsigned char ad[1] = { 0 }; int ret; struct i2c_msg msgs[2] = { { nx8025_i2c_client->addr , 0, 1, ad }, { nx8025_i2c_client->addr , I2C_M_RD, len, buf } }; spin_lock_irqsave(&nx8025_rtc_lock, flags); ret = i2c_transfer(nx8025_i2c_client->adapter, msgs, 2); spin_unlock_irqrestore(&nx8025_rtc_lock,flags); return ret;}static intnx8025_attach(struct i2c_adapter *adapter, int address, int kind){ struct i2c_client *new_client; struct nx8025_data *data; int err = 0; const char *name = ""; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_I2C)) return err; if (!(data = kzalloc(sizeof(struct nx8025_data), GFP_KERNEL))) { err = -ENOMEM; return err; } /* The common I2C client data is placed right before the * nx8025-specific data. */ new_client = &data->client; i2c_set_clientdata(new_client, data); new_client->addr = address; new_client->adapter = adapter; new_client->driver = &nx8025_driver; new_client->flags = 0;// ret = i2c_transfer(c->adapter, msgs, 2);// if ( ret == 2 )// {// DAT(c) = buf[0];// }// else// printk ("nx8025_attach(): i2c_transfer() returned %d.\n",ret); nx8025_i2c_client = new_client; return i2c_attach_client(new_client);}static intnx8025_probe(struct i2c_adapter *adap){ return i2c_probe(adap, &addr_data, nx8025_attach);}static intnx8025_detach(struct i2c_client *client){ i2c_detach_client(client); return 0;}static voidnx8025_convert_to_time( struct rtc_time *dt, char *buf){ if(rtc_debug) printk("buf: %x, %x, %x, %x, %x, %x\n", buf[0], buf[1], buf[2], buf[4], buf[5], buf[6]); dt->tm_sec = BCD_TO_BIN(buf[0]); dt->tm_min = BCD_TO_BIN(buf[1]); //12-hour-mode if (HOURS_AP(buf[2])) /* PM */ { dt->tm_hour = HOURS_12(buf[2]); if(dt->tm_hour<12) dt->tm_hour += 12; //PM11 -> AM12 } else /* AM */ { dt->tm_hour = HOURS_12(buf[2]); if(dt->tm_hour==12) dt->tm_hour=0; //AM 11 -> PM 12 } dt->tm_mday = BCD_TO_BIN(buf[4]); /* dt->tm_mon is one-based */ dt->tm_mon = BCD_TO_BIN(buf[5]); /* year is 2000 + dt->tm_year */ dt->tm_year = BCD_TO_BIN(buf[6]) + 2000; if( rtc_debug > 2) { printk("nx8025_get_datetime: year = %d\n", dt->tm_year); printk("nx8025_get_datetime: mon = %d\n", dt->tm_mon); printk("nx8025_get_datetime: mday = %d\n", dt->tm_mday); printk("nx8025_get_datetime: hour = %d\n", dt->tm_hour); printk("nx8025_get_datetime: min = %d\n", dt->tm_min); printk("nx8025_get_datetime: sec = %d\n", dt->tm_sec); }}static intnx8025_get_datetime(struct i2c_client *client, struct rtc_time *dt){ unsigned char buf[7], addr[1] = { 0 }; struct i2c_msg msgs[2] = { { client->addr, 0, 1, addr }, { client->addr, I2C_M_RD, 7, buf } }; int ret = -EIO; memset(buf, 0, sizeof(buf)); ret = i2c_transfer(client->adapter, msgs, 2); if (ret == 2) { nx8025_convert_to_time( dt, buf); dt->tm_year -=1900; ret = 0; } else printk("nx8025_get_datetime(), i2c_transfer() returned %d\n",ret); return ret;}static intnx8025_set_datetime(struct i2c_client *client, struct rtc_time *dt, int datetoo){ unsigned char buf[8]; int ret, len = 4; if( rtc_debug > 2) { printk("nx8025_set_datetime: tm_year = %d\n", dt->tm_year); printk("nx8025_set_datetime: tm_mon = %d\n", dt->tm_mon); printk("nx8025_set_datetime: tm_mday = %d\n", dt->tm_mday); printk("nx8025_set_datetime: tm_hour = %d\n", dt->tm_hour); printk("nx8025_set_datetime: tm_min = %d\n", dt->tm_min); printk("nx8025_set_datetime: tm_sec = %d\n", dt->tm_sec); } buf[0] = 0; /* register address on nx8025 */ buf[1] = (BIN_TO_BCD(dt->tm_sec)); buf[2] = (BIN_TO_BCD(dt->tm_min)); //12-hour-mode if(dt->tm_hour<12 && dt->tm_hour>0) buf[3] = (BIN_TO_BCD(dt->tm_hour)); else if(dt->tm_hour==0) buf[3] = (BIN_TO_BCD(12)); else if(dt->tm_hour>12 && dt->tm_hour<=23) buf[3] = (BIN_TO_BCD(dt->tm_hour + 8)); else if(dt->tm_hour == 12) buf[3] = (BIN_TO_BCD(32)); else if(dt->tm_hour<0) return -1; if (datetoo) { len = 8; /* we skip buf[4] as we don't use day-of-week. */ buf[5] = (BIN_TO_BCD(dt->tm_mday)); buf[6] = (BIN_TO_BCD(dt->tm_mon )); /* The year only ranges from 0-99, we are being passed an offset from 1900, * and the chip calulates leap years based on 2000, thus we adjust by 100. */ buf[7] = (BIN_TO_BCD(dt->tm_year - 2000)); } ret = i2c_master_send(client, (char *)buf, len); if (ret == len) ret = 0; else printk("nx8025_set_datetime(), i2c_master_send() returned %d\n",ret); return ret;}static intnx8025_get_ctrl(struct i2c_client *client, unsigned char *ctrl){ //*ctrl = DAT(client); return 0;}static intnx8025_set_ctrl(struct i2c_client *client, unsigned char *cinfo){ unsigned char buf[2]; int ret; buf[0] = 0x0F; /* control register address on nx8025 */ buf[1] = *cinfo; /* save the control reg info in the client data field so that get_ctrl * function doesn't have to do an I2C transfer to get it. */ //DAT(client) = buf[1]; ret = i2c_master_send(client, (char *)buf, 2); return ret;}static intnx8025_read_mem(struct i2c_client *client, struct rtc_mem *mem){ unsigned char addr[1]; struct i2c_msg msgs[2] = { { client->addr, 0, 1, addr }, { client->addr, 0, mem->nr, mem->data } }; if ( (mem->loc < NX8025_REG_ADDR_START) || ((mem->loc + mem->nr -1) > NX8025_REG_ADDR_END) ) return -EINVAL; addr[0] = mem->loc; return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO;}static intnx8025_write_mem(struct i2c_client *client, struct rtc_mem *mem){ unsigned char addr[1]; struct i2c_msg msgs[2] = { { client->addr, 0, 1, addr }, { client->addr, 0, mem->nr, mem->data } }; if ( (mem->loc < NX8025_REG_ADDR_START) || ((mem->loc + mem->nr -1) > NX8025_REG_ADDR_END) ) return -EINVAL; addr[0] = mem->loc; return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO;}static intnx8025_command(struct i2c_client *client, unsigned int cmd, void *arg){ switch (cmd) { case NX8025_GETDATETIME: return nx8025_get_datetime(client, arg); case NX8025_SETTIME: return nx8025_set_datetime(client, arg, 0); case NX8025_SETDATETIME: return nx8025_set_datetime(client, arg, 1); case NX8025_GETCTRL: return nx8025_get_ctrl(client, arg); case NX8025_SETCTRL: return nx8025_set_ctrl(client, arg); case NX8025_MEM_READ: return nx8025_read_mem(client, arg); case NX8025_MEM_WRITE: return nx8025_write_mem(client, arg); default: return -EINVAL; }}static intnx8025_rtc_open(struct inode *inode, struct file *file){ return 0;}static intnx8025_rtc_release(struct inode *inode, struct file *file){ return 0;}static intnx8025_rtc_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ unsigned long flags; struct rtc_time wtime; int status = 0; switch (cmd) { default: case RTC_UIE_ON: case RTC_UIE_OFF: case RTC_PIE_ON: case RTC_PIE_OFF: case RTC_AIE_ON: case RTC_AIE_OFF: case RTC_ALM_SET: case RTC_ALM_READ: case RTC_IRQP_READ: case RTC_IRQP_SET: case RTC_EPOCH_READ: case RTC_EPOCH_SET: case RTC_WKALM_SET: case RTC_WKALM_RD: status = -EINVAL; break; case RTC_RD_TIME: spin_lock_irqsave(&nx8025_rtc_lock, flags); nx8025_command( nx8025_i2c_client, NX8025_GETDATETIME, &wtime); spin_unlock_irqrestore(&nx8025_rtc_lock,flags); if( copy_to_user((void *)arg, &wtime, sizeof (struct rtc_time))) status = -EFAULT; break; case RTC_SET_TIME: if (!capable(CAP_SYS_TIME)) { status = -EACCES; break; } if (copy_from_user(&wtime, (struct rtc_time *)arg, sizeof(struct rtc_time)) ) { status = -EFAULT; break; } spin_lock_irqsave(&nx8025_rtc_lock, flags); nx8025_command( nx8025_i2c_client, NX8025_SETDATETIME, &wtime); spin_unlock_irqrestore(&nx8025_rtc_lock,flags); break; } return status;}static char *nx8025_mon2str( unsigned int mon){ char *mon2str[13] = { "NULL","Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; if( mon > 12) return "error"; else return mon2str[ mon];}static int rtc8025_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data){//#define CHECK(ctrl,bit) ((ctrl & bit) ? "yes" : "no") unsigned char ram[NX8025_REG_SIZE]; int ret; char *p = page; ret = nx8025_readram( ram,NX8025_REG_SIZE ); if( ret > 0) { struct rtc_time dt; printk("access nx8025 sucessfully!\n"); p += sprintf(p, "NX-8025 (Real Time Clock)\n"); nx8025_convert_to_time( &dt, ram); p += sprintf(p, "Date/Time : %02d-%s-%04d %02d:%02d:%02d\n", dt.tm_mday, nx8025_mon2str(dt.tm_mon), dt.tm_year, dt.tm_hour, dt.tm_min, dt.tm_sec); //p += sprintf(p, "24h mode : %s\n", CHECK(ram[2],0x40)); } else { printk("access nx8025 failed\n"); p += sprintf(p, "Failed to read RTC memory!\n"); } return p - page;}#ifdef CONFIG_PMstatic struct timespec nx8025_rtc_delta;static int nx8025_suspend(){ struct rtc_time tm; struct timespec time; time.tv_nsec = 0; /* calculate time delta for suspend */ nx8025_command( nx8025_i2c_client, NX8025_GETDATETIME, &tm); tm.tm_year -=1; rtc_tm_to_time(&tm, &time.tv_sec); save_time_delta(&nx8025_rtc_delta, &time); if(rtc_debug) printk("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__, 1900 + tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); return 0;}static void nx8025_resume(){ struct rtc_time tm; struct timespec time; time.tv_nsec = 0; nx8025_command( nx8025_i2c_client, NX8025_GETDATETIME, &tm); tm.tm_mon -=1; rtc_tm_to_time(&tm, &time.tv_sec); restore_time_delta(&nx8025_rtc_delta, &time); if(rtc_debug) printk("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__, 1900 + tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);}static int nx8025_pm_callback(struct pm_dev *dev, pm_request_t request, void *data){ switch(request){ case PM_SUSPEND: break; case PM_RESUME: nx8025_resume(); break; default: break; } return 0;}#else#define nx8025_resume NULL#endifstatic __init int nx8025_init(void){ int retval=0; if( slave_address != 0xffff) { normal_addr[0] = slave_address; } if( normal_addr[0] == 0xffff) { printk(KERN_ERR"I2C: Invalid slave address for NX-8025 RTC (%#x)\n", normal_addr[0]); return -EINVAL; } retval = i2c_add_driver(&nx8025_driver); if (retval==0) { misc_register (&nx8025_rtc_miscdev); create_proc_read_entry (PROC_NX8025_NAME, 0, 0, rtc8025_read_proc, NULL);#ifdef CONFIG_PM pmdev=pm_register(PM_SYS_DEV,PM_SYS_UNKNOWN,nx8025_pm_callback); if(pmdev==NULL) { printk("nx8025 pm register error!\n"); misc_deregister(&nx8025_rtc_miscdev); return -1; } #endif printk("I2C: NX-8025 RTC driver successfully loaded\n"); //if( rtc_debug) nx8025_dumpram(); } return retval;}static __exit void nx8025_exit(void){ remove_proc_entry (PROC_NX8025_NAME, NULL); misc_deregister(&nx8025_rtc_miscdev);#ifdef CONFIG_PM pm_unregister(pmdev);#endif i2c_del_driver(&nx8025_driver);}module_init(nx8025_init);module_exit(nx8025_exit);MODULE_PARM_DESC (slave_address, "I2C slave address for NX-8025 RTC.");MODULE_AUTHOR ("SACRROT");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -