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

📄 rtc-m41t80.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	    i2c_smbus_write_byte_data(client, M41T80_REG_SQW, sqw) < 0)		return -EIO;	if (val && i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,					     almon | M41T80_ALMON_SQWE) < 0)		return -EIO;	return count;}static DEVICE_ATTR(sqwfreq, S_IRUGO | S_IWUSR,		   m41t80_sysfs_show_sqwfreq, m41t80_sysfs_set_sqwfreq);static struct attribute *attrs[] = {	&dev_attr_flags.attr,	&dev_attr_sqwfreq.attr,	NULL,};static struct attribute_group attr_group = {	.attrs = attrs,};static int m41t80_sysfs_register(struct device *dev){	return sysfs_create_group(&dev->kobj, &attr_group);}#elsestatic int m41t80_sysfs_register(struct device *dev){	return 0;}#endif#ifdef CONFIG_RTC_DRV_M41T80_WDT/* ***************************************************************************** * * Watchdog Driver * ***************************************************************************** */static struct i2c_client *save_client;/* Default margin */#define WD_TIMO 60		/* 1..31 seconds */static int wdt_margin = WD_TIMO;module_param(wdt_margin, int, 0);MODULE_PARM_DESC(wdt_margin, "Watchdog timeout in seconds (default 60s)");static unsigned long wdt_is_open;static int boot_flag;/** *	wdt_ping: * *	Reload counter one with the watchdog timeout. We don't bother reloading *	the cascade counter. */static void wdt_ping(void){	unsigned char i2c_data[2];	struct i2c_msg msgs1[1] = {		{			.addr	= save_client->addr,			.flags	= 0,			.len	= 2,			.buf	= i2c_data,		},	};	i2c_data[0] = 0x09;		/* watchdog register */	if (wdt_margin > 31)		i2c_data[1] = (wdt_margin & 0xFC) | 0x83; /* resolution = 4s */	else		/*		 * WDS = 1 (0x80), mulitplier = WD_TIMO, resolution = 1s (0x02)		 */		i2c_data[1] = wdt_margin<<2 | 0x82;	i2c_transfer(save_client->adapter, msgs1, 1);}/** *	wdt_disable: * *	disables watchdog. */static void wdt_disable(void){	unsigned char i2c_data[2], i2c_buf[0x10];	struct i2c_msg msgs0[2] = {		{			.addr	= save_client->addr,			.flags	= 0,			.len	= 1,			.buf	= i2c_data,		},		{			.addr	= save_client->addr,			.flags	= I2C_M_RD,			.len	= 1,			.buf	= i2c_buf,		},	};	struct i2c_msg msgs1[1] = {		{			.addr	= save_client->addr,			.flags	= 0,			.len	= 2,			.buf	= i2c_data,		},	};	i2c_data[0] = 0x09;	i2c_transfer(save_client->adapter, msgs0, 2);	i2c_data[0] = 0x09;	i2c_data[1] = 0x00;	i2c_transfer(save_client->adapter, msgs1, 1);}/** *	wdt_write: *	@file: file handle to the watchdog *	@buf: buffer to write (unused as data does not matter here *	@count: count of bytes *	@ppos: pointer to the position to write. No seeks allowed * *	A write to a watchdog device is defined as a keepalive signal. Any *	write of data will do, as we we don't define content meaning. */static ssize_t wdt_write(struct file *file, const char __user *buf,			 size_t count, loff_t *ppos){	/*  Can't seek (pwrite) on this device	if (ppos != &file->f_pos)	return -ESPIPE;	*/	if (count) {		wdt_ping();		return 1;	}	return 0;}static ssize_t wdt_read(struct file *file, char __user *buf,			size_t count, loff_t *ppos){	return 0;}/** *	wdt_ioctl: *	@inode: inode of the device *	@file: file handle to the device *	@cmd: watchdog command *	@arg: argument pointer * *	The watchdog API defines a common set of functions for all watchdogs *	according to their available features. We only actually usefully support *	querying capabilities and current status. */static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,		     unsigned long arg){	int new_margin, rv;	static struct watchdog_info ident = {		.options = WDIOF_POWERUNDER | WDIOF_KEEPALIVEPING |			WDIOF_SETTIMEOUT,		.firmware_version = 1,		.identity = "M41T80 WTD"	};	switch (cmd) {	case WDIOC_GETSUPPORT:		return copy_to_user((struct watchdog_info __user *)arg, &ident,				    sizeof(ident)) ? -EFAULT : 0;	case WDIOC_GETSTATUS:	case WDIOC_GETBOOTSTATUS:		return put_user(boot_flag, (int __user *)arg);	case WDIOC_KEEPALIVE:		wdt_ping();		return 0;	case WDIOC_SETTIMEOUT:		if (get_user(new_margin, (int __user *)arg))			return -EFAULT;		/* Arbitrary, can't find the card's limits */		if (new_margin < 1 || new_margin > 124)			return -EINVAL;		wdt_margin = new_margin;		wdt_ping();		/* Fall */	case WDIOC_GETTIMEOUT:		return put_user(wdt_margin, (int __user *)arg);	case WDIOC_SETOPTIONS:		if (copy_from_user(&rv, (int __user *)arg, sizeof(int)))			return -EFAULT;		if (rv & WDIOS_DISABLECARD) {			printk(KERN_INFO			       "rtc-m41t80: disable watchdog\n");			wdt_disable();		}		if (rv & WDIOS_ENABLECARD) {			printk(KERN_INFO			       "rtc-m41t80: enable watchdog\n");			wdt_ping();		}		return -EINVAL;	}	return -ENOTTY;}/** *	wdt_open: *	@inode: inode of device *	@file: file handle to device * */static int wdt_open(struct inode *inode, struct file *file){	if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) {		if (test_and_set_bit(0, &wdt_is_open))			return -EBUSY;		/*		 *	Activate		 */		wdt_is_open = 1;		return 0;	}	return -ENODEV;}/** *	wdt_close: *	@inode: inode to board *	@file: file handle to board * */static int wdt_release(struct inode *inode, struct file *file){	if (MINOR(inode->i_rdev) == WATCHDOG_MINOR)		clear_bit(0, &wdt_is_open);	return 0;}/** *	notify_sys: *	@this: our notifier block *	@code: the event being reported *	@unused: unused * *	Our notifier is called on system shutdowns. We want to turn the card *	off at reboot otherwise the machine will reboot again during memory *	test or worse yet during the following fsck. This would suck, in fact *	trust me - if it happens it does suck. */static int wdt_notify_sys(struct notifier_block *this, unsigned long code,			  void *unused){	if (code == SYS_DOWN || code == SYS_HALT)		/* Disable Watchdog */		wdt_disable();	return NOTIFY_DONE;}static const struct file_operations wdt_fops = {	.owner	= THIS_MODULE,	.read	= wdt_read,	.ioctl	= wdt_ioctl,	.write	= wdt_write,	.open	= wdt_open,	.release = wdt_release,};static struct miscdevice wdt_dev = {	.minor = WATCHDOG_MINOR,	.name = "watchdog",	.fops = &wdt_fops,};/* *	The WDT card needs to learn about soft shutdowns in order to *	turn the timebomb registers off. */static struct notifier_block wdt_notifier = {	.notifier_call = wdt_notify_sys,};#endif /* CONFIG_RTC_DRV_M41T80_WDT *//* ***************************************************************************** * *	Driver Interface * ***************************************************************************** */static int m41t80_probe(struct i2c_client *client){	int i, rc = 0;	struct rtc_device *rtc = NULL;	struct rtc_time tm;	const struct m41t80_chip_info *chip;	struct m41t80_data *clientdata = NULL;	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C				     | I2C_FUNC_SMBUS_BYTE_DATA)) {		rc = -ENODEV;		goto exit;	}	dev_info(&client->dev,		 "chip found, driver version " DRV_VERSION "\n");	chip = NULL;	for (i = 0; i < ARRAY_SIZE(m41t80_chip_info_tbl); i++) {		if (!strcmp(m41t80_chip_info_tbl[i].name, client->name)) {			chip = &m41t80_chip_info_tbl[i];			break;		}	}	if (!chip) {		dev_err(&client->dev, "%s is not supported\n", client->name);		rc = -ENODEV;		goto exit;	}	clientdata = kzalloc(sizeof(*clientdata), GFP_KERNEL);	if (!clientdata) {		rc = -ENOMEM;		goto exit;	}	rtc = rtc_device_register(client->name, &client->dev,				  &m41t80_rtc_ops, THIS_MODULE);	if (IS_ERR(rtc)) {		rc = PTR_ERR(rtc);		rtc = NULL;		goto exit;	}	clientdata->rtc = rtc;	clientdata->chip = chip;	i2c_set_clientdata(client, clientdata);	/* Make sure HT (Halt Update) bit is cleared */	rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR);	if (rc < 0)		goto ht_err;	if (rc & M41T80_ALHOUR_HT) {		if (chip->features & M41T80_FEATURE_HT) {			m41t80_get_datetime(client, &tm);			dev_info(&client->dev, "HT bit was set!\n");			dev_info(&client->dev,				 "Power Down at "				 "%04i-%02i-%02i %02i:%02i:%02i\n",				 tm.tm_year + 1900,				 tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,				 tm.tm_min, tm.tm_sec);		}		if (i2c_smbus_write_byte_data(client,					      M41T80_REG_ALARM_HOUR,					      rc & ~M41T80_ALHOUR_HT) < 0)			goto ht_err;	}	/* Make sure ST (stop) bit is cleared */	rc = i2c_smbus_read_byte_data(client, M41T80_REG_SEC);	if (rc < 0)		goto st_err;	if (rc & M41T80_SEC_ST) {		if (i2c_smbus_write_byte_data(client, M41T80_REG_SEC,					      rc & ~M41T80_SEC_ST) < 0)			goto st_err;	}	rc = m41t80_sysfs_register(&client->dev);	if (rc)		goto exit;#ifdef CONFIG_RTC_DRV_M41T80_WDT	if (chip->features & M41T80_FEATURE_HT) {		rc = misc_register(&wdt_dev);		if (rc)			goto exit;		rc = register_reboot_notifier(&wdt_notifier);		if (rc) {			misc_deregister(&wdt_dev);			goto exit;		}		save_client = client;	}#endif	return 0;st_err:	rc = -EIO;	dev_err(&client->dev, "Can't clear ST bit\n");	goto exit;ht_err:	rc = -EIO;	dev_err(&client->dev, "Can't clear HT bit\n");	goto exit;exit:	if (rtc)		rtc_device_unregister(rtc);	kfree(clientdata);	return rc;}static int m41t80_remove(struct i2c_client *client){	struct m41t80_data *clientdata = i2c_get_clientdata(client);	struct rtc_device *rtc = clientdata->rtc;#ifdef CONFIG_RTC_DRV_M41T80_WDT	if (clientdata->chip->features & M41T80_FEATURE_HT) {		misc_deregister(&wdt_dev);		unregister_reboot_notifier(&wdt_notifier);	}#endif	if (rtc)		rtc_device_unregister(rtc);	kfree(clientdata);	return 0;}static struct i2c_driver m41t80_driver = {	.driver = {		.name = "rtc-m41t80",	},	.probe = m41t80_probe,	.remove = m41t80_remove,};static int __init m41t80_rtc_init(void){	return i2c_add_driver(&m41t80_driver);}static void __exit m41t80_rtc_exit(void){	i2c_del_driver(&m41t80_driver);}MODULE_AUTHOR("Alexander Bigga <ab@mycable.de>");MODULE_DESCRIPTION("ST Microelectronics M41T80 series RTC I2C Client Driver");MODULE_LICENSE("GPL");MODULE_VERSION(DRV_VERSION);module_init(m41t80_rtc_init);module_exit(m41t80_rtc_exit);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -