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

📄 nx8025.c

📁 最近完成的LINUX下RTC8025的驱动
💻 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 + -