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

📄 rtc-isl1208.c

📁 linux 内核源代码
💻 C
字号:
/* * Intersil ISL1208 rtc class driver * * Copyright 2005,2006 Hebert Valerio Riedel <hvr@gnu.org> * *  This program is free software; you can redistribute  it and/or modify it *  under  the terms of  the GNU General  Public License as published by the *  Free Software Foundation;  either version 2 of the  License, or (at your *  option) any later version. * */#include <linux/module.h>#include <linux/i2c.h>#include <linux/bcd.h>#include <linux/rtc.h>#define DRV_NAME "isl1208"#define DRV_VERSION "0.2"/* Register map *//* rtc section */#define ISL1208_REG_SC  0x00#define ISL1208_REG_MN  0x01#define ISL1208_REG_HR  0x02#define ISL1208_REG_HR_MIL     (1<<7) /* 24h/12h mode */#define ISL1208_REG_HR_PM      (1<<5) /* PM/AM bit in 12h mode */#define ISL1208_REG_DT  0x03#define ISL1208_REG_MO  0x04#define ISL1208_REG_YR  0x05#define ISL1208_REG_DW  0x06#define ISL1208_RTC_SECTION_LEN 7/* control/status section */#define ISL1208_REG_SR  0x07#define ISL1208_REG_SR_ARST    (1<<7) /* auto reset */#define ISL1208_REG_SR_XTOSCB  (1<<6) /* crystal oscillator */#define ISL1208_REG_SR_WRTC    (1<<4) /* write rtc */#define ISL1208_REG_SR_ALM     (1<<2) /* alarm */#define ISL1208_REG_SR_BAT     (1<<1) /* battery */#define ISL1208_REG_SR_RTCF    (1<<0) /* rtc fail */#define ISL1208_REG_INT 0x08#define ISL1208_REG_09  0x09 /* reserved */#define ISL1208_REG_ATR 0x0a#define ISL1208_REG_DTR 0x0b/* alarm section */#define ISL1208_REG_SCA 0x0c#define ISL1208_REG_MNA 0x0d#define ISL1208_REG_HRA 0x0e#define ISL1208_REG_DTA 0x0f#define ISL1208_REG_MOA 0x10#define ISL1208_REG_DWA 0x11#define ISL1208_ALARM_SECTION_LEN 6/* user section */#define ISL1208_REG_USR1 0x12#define ISL1208_REG_USR2 0x13#define ISL1208_USR_SECTION_LEN 2/* i2c configuration */#define ISL1208_I2C_ADDR 0xdestatic unsigned short normal_i2c[] = {	ISL1208_I2C_ADDR>>1, I2C_CLIENT_END};I2C_CLIENT_INSMOD; /* defines addr_data */static int isl1208_attach_adapter(struct i2c_adapter *adapter);static int isl1208_detach_client(struct i2c_client *client);static struct i2c_driver isl1208_driver = {	.driver		= {		.name	= DRV_NAME,	},	.id		= I2C_DRIVERID_ISL1208,	.attach_adapter = &isl1208_attach_adapter,	.detach_client	= &isl1208_detach_client,};/* block read */static intisl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[],		       unsigned len){	u8 reg_addr[1] = { reg };	struct i2c_msg msgs[2] = {		{ client->addr, client->flags, sizeof(reg_addr), reg_addr },		{ client->addr, client->flags | I2C_M_RD, len, buf }	};	int ret;	BUG_ON(len == 0);	BUG_ON(reg > ISL1208_REG_USR2);	BUG_ON(reg + len > ISL1208_REG_USR2 + 1);	ret = i2c_transfer(client->adapter, msgs, 2);	if (ret > 0)		ret = 0;	return ret;}/* block write */static intisl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[],		       unsigned len){	u8 i2c_buf[ISL1208_REG_USR2 + 2];	struct i2c_msg msgs[1] = {		{ client->addr, client->flags, len + 1, i2c_buf }	};	int ret;	BUG_ON(len == 0);	BUG_ON(reg > ISL1208_REG_USR2);	BUG_ON(reg + len > ISL1208_REG_USR2 + 1);	i2c_buf[0] = reg;	memcpy(&i2c_buf[1], &buf[0], len);	ret = i2c_transfer(client->adapter, msgs, 1);	if (ret > 0)		ret = 0;	return ret;}/* simple check to see wether we have a isl1208 */static int isl1208_i2c_validate_client(struct i2c_client *client){	u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };	u8 zero_mask[ISL1208_RTC_SECTION_LEN] = {		0x80, 0x80, 0x40, 0xc0, 0xe0, 0x00, 0xf8	};	int i;	int ret;	ret = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN);	if (ret < 0)		return ret;	for (i = 0; i < ISL1208_RTC_SECTION_LEN; ++i) {		if (regs[i] & zero_mask[i]) /* check if bits are cleared */			return -ENODEV;	}	return 0;}static int isl1208_i2c_get_sr(struct i2c_client *client){	return i2c_smbus_read_byte_data(client, ISL1208_REG_SR) == -1 ? -EIO:0;}static int isl1208_i2c_get_atr(struct i2c_client *client){	int atr = i2c_smbus_read_byte_data(client, ISL1208_REG_ATR);	if (atr < 0)		return -EIO;	/* The 6bit value in the ATR register controls the load	 * capacitance C_load * in steps of 0.25pF	 *	 * bit (1<<5) of the ATR register is inverted	 *	 * C_load(ATR=0x20) =  4.50pF	 * C_load(ATR=0x00) = 12.50pF	 * C_load(ATR=0x1f) = 20.25pF	 *	 */	atr &= 0x3f; /* mask out lsb */	atr ^= 1<<5; /* invert 6th bit */	atr += 2*9; /* add offset of 4.5pF; unit[atr] = 0.25pF */	return atr;}static int isl1208_i2c_get_dtr(struct i2c_client *client){	int dtr = i2c_smbus_read_byte_data(client, ISL1208_REG_DTR);	if (dtr < 0)		return -EIO;	/* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */	dtr = ((dtr & 0x3) * 20) * (dtr & (1<<2) ? -1 : 1);	return dtr;}static int isl1208_i2c_get_usr(struct i2c_client *client){	u8 buf[ISL1208_USR_SECTION_LEN] = { 0, };	int ret;	ret = isl1208_i2c_read_regs (client, ISL1208_REG_USR1, buf,				   ISL1208_USR_SECTION_LEN);	if (ret < 0)		return ret;	return (buf[1] << 8) | buf[0];}static int isl1208_i2c_set_usr(struct i2c_client *client, u16 usr){	u8 buf[ISL1208_USR_SECTION_LEN];	buf[0] = usr & 0xff;	buf[1] = (usr >> 8) & 0xff;	return isl1208_i2c_set_regs (client, ISL1208_REG_USR1, buf,				     ISL1208_USR_SECTION_LEN);}static int isl1208_rtc_proc(struct device *dev, struct seq_file *seq){	struct i2c_client *const client = to_i2c_client(dev);	int sr, dtr, atr, usr;	sr = isl1208_i2c_get_sr(client);	if (sr < 0) {		dev_err(&client->dev, "%s: reading SR failed\n", __func__);		return sr;	}	seq_printf(seq, "status_reg\t:%s%s%s%s%s%s (0x%.2x)\n",		   (sr & ISL1208_REG_SR_RTCF) ? " RTCF" : "",		   (sr & ISL1208_REG_SR_BAT) ? " BAT" : "",		   (sr & ISL1208_REG_SR_ALM) ? " ALM" : "",		   (sr & ISL1208_REG_SR_WRTC) ? " WRTC" : "",		   (sr & ISL1208_REG_SR_XTOSCB) ? " XTOSCB" : "",		   (sr & ISL1208_REG_SR_ARST) ? " ARST" : "",		   sr);	seq_printf(seq, "batt_status\t: %s\n",		   (sr & ISL1208_REG_SR_RTCF) ? "bad" : "okay");	dtr = isl1208_i2c_get_dtr(client);	if (dtr >= 0 -1)		seq_printf(seq, "digital_trim\t: %d ppm\n", dtr);	atr = isl1208_i2c_get_atr(client);	if (atr >= 0)		seq_printf(seq, "analog_trim\t: %d.%.2d pF\n",			   atr>>2, (atr&0x3)*25);	usr = isl1208_i2c_get_usr(client);	if (usr >= 0)		seq_printf(seq, "user_data\t: 0x%.4x\n", usr);	return 0;}static int isl1208_i2c_read_time(struct i2c_client *client,				 struct rtc_time *tm){	int sr;	u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };	sr = isl1208_i2c_get_sr(client);	if (sr < 0) {		dev_err(&client->dev, "%s: reading SR failed\n", __func__);		return -EIO;	}	sr = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN);	if (sr < 0) {		dev_err(&client->dev, "%s: reading RTC section failed\n",			__func__);		return sr;	}	tm->tm_sec = BCD2BIN(regs[ISL1208_REG_SC]);	tm->tm_min = BCD2BIN(regs[ISL1208_REG_MN]);	{ /* HR field has a more complex interpretation */		const u8 _hr = regs[ISL1208_REG_HR];		if (_hr & ISL1208_REG_HR_MIL) /* 24h format */			tm->tm_hour = BCD2BIN(_hr & 0x3f);		else { // 12h format			tm->tm_hour = BCD2BIN(_hr & 0x1f);			if (_hr & ISL1208_REG_HR_PM) /* PM flag set */				tm->tm_hour += 12;		}	}	tm->tm_mday = BCD2BIN(regs[ISL1208_REG_DT]);	tm->tm_mon = BCD2BIN(regs[ISL1208_REG_MO]) - 1; /* rtc starts at 1 */	tm->tm_year = BCD2BIN(regs[ISL1208_REG_YR]) + 100;	tm->tm_wday = BCD2BIN(regs[ISL1208_REG_DW]);	return 0;}static int isl1208_i2c_read_alarm(struct i2c_client *client,				  struct rtc_wkalrm *alarm){	struct rtc_time *const tm = &alarm->time;	u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, };	int sr;	sr = isl1208_i2c_get_sr(client);	if (sr < 0) {		dev_err(&client->dev, "%s: reading SR failed\n", __func__);		return sr;	}	sr = isl1208_i2c_read_regs(client, ISL1208_REG_SCA, regs,				  ISL1208_ALARM_SECTION_LEN);	if (sr < 0) {		dev_err(&client->dev, "%s: reading alarm section failed\n",			__func__);		return sr;	}	/* MSB of each alarm register is an enable bit */	tm->tm_sec  = BCD2BIN(regs[ISL1208_REG_SCA-ISL1208_REG_SCA] & 0x7f);	tm->tm_min  = BCD2BIN(regs[ISL1208_REG_MNA-ISL1208_REG_SCA] & 0x7f);	tm->tm_hour = BCD2BIN(regs[ISL1208_REG_HRA-ISL1208_REG_SCA] & 0x3f);	tm->tm_mday = BCD2BIN(regs[ISL1208_REG_DTA-ISL1208_REG_SCA] & 0x3f);	tm->tm_mon  = BCD2BIN(regs[ISL1208_REG_MOA-ISL1208_REG_SCA] & 0x1f)-1;	tm->tm_wday = BCD2BIN(regs[ISL1208_REG_DWA-ISL1208_REG_SCA] & 0x03);	return 0;}static int isl1208_rtc_read_time(struct device *dev, struct rtc_time *tm){	return isl1208_i2c_read_time(to_i2c_client(dev), tm);}static int isl1208_i2c_set_time(struct i2c_client *client,				struct rtc_time const *tm){	int sr;	u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };	regs[ISL1208_REG_SC] = BIN2BCD(tm->tm_sec);	regs[ISL1208_REG_MN] = BIN2BCD(tm->tm_min);	regs[ISL1208_REG_HR] = BIN2BCD(tm->tm_hour) | ISL1208_REG_HR_MIL;	regs[ISL1208_REG_DT] = BIN2BCD(tm->tm_mday);	regs[ISL1208_REG_MO] = BIN2BCD(tm->tm_mon + 1);	regs[ISL1208_REG_YR] = BIN2BCD(tm->tm_year - 100);	regs[ISL1208_REG_DW] = BIN2BCD(tm->tm_wday & 7);	sr = isl1208_i2c_get_sr(client);	if (sr < 0) {		dev_err(&client->dev, "%s: reading SR failed\n", __func__);		return sr;	}	/* set WRTC */	sr = i2c_smbus_write_byte_data (client, ISL1208_REG_SR,				       sr | ISL1208_REG_SR_WRTC);	if (sr < 0) {		dev_err(&client->dev, "%s: writing SR failed\n", __func__);		return sr;	}	/* write RTC registers */	sr = isl1208_i2c_set_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN);	if (sr < 0) {		dev_err(&client->dev, "%s: writing RTC section failed\n",			__func__);		return sr;	}	/* clear WRTC again */	sr = i2c_smbus_write_byte_data (client, ISL1208_REG_SR,				       sr & ~ISL1208_REG_SR_WRTC);	if (sr < 0) {		dev_err(&client->dev, "%s: writing SR failed\n", __func__);		return sr;	}	return 0;}static int isl1208_rtc_set_time(struct device *dev, struct rtc_time *tm){	return isl1208_i2c_set_time(to_i2c_client(dev), tm);}static int isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm){	return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm);}static const struct rtc_class_ops isl1208_rtc_ops = {	.proc		= isl1208_rtc_proc,	.read_time	= isl1208_rtc_read_time,	.set_time	= isl1208_rtc_set_time,	.read_alarm	= isl1208_rtc_read_alarm,	//.set_alarm	= isl1208_rtc_set_alarm,};/* sysfs interface */static ssize_t isl1208_sysfs_show_atrim(struct device *dev,					struct device_attribute *attr,					char *buf){	int atr;	atr = isl1208_i2c_get_atr(to_i2c_client(dev));	if (atr < 0)		return atr;	return sprintf(buf, "%d.%.2d pF\n", atr>>2, (atr&0x3)*25);}static DEVICE_ATTR(atrim, S_IRUGO, isl1208_sysfs_show_atrim, NULL);static ssize_t isl1208_sysfs_show_dtrim(struct device *dev,					struct device_attribute *attr,					char *buf){	int dtr;	dtr = isl1208_i2c_get_dtr(to_i2c_client(dev));	if (dtr < 0)		return dtr;	return sprintf(buf, "%d ppm\n", dtr);}static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL);static ssize_t isl1208_sysfs_show_usr(struct device *dev,				       struct device_attribute *attr,				       char *buf){	int usr;	usr = isl1208_i2c_get_usr(to_i2c_client(dev));	if (usr < 0)		return usr;	return sprintf(buf, "0x%.4x\n", usr);}static ssize_t isl1208_sysfs_store_usr(struct device *dev,				       struct device_attribute *attr,				       const char *buf, size_t count){	int usr = -1;	if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) {		if (sscanf(buf, "%x", &usr) != 1)			return -EINVAL;	} else {		if (sscanf(buf, "%d", &usr) != 1)			return -EINVAL;	}	if (usr < 0 || usr > 0xffff)		return -EINVAL;	return isl1208_i2c_set_usr(to_i2c_client(dev), usr) ? -EIO : count;}static DEVICE_ATTR(usr, S_IRUGO | S_IWUSR, isl1208_sysfs_show_usr,		   isl1208_sysfs_store_usr);static intisl1208_probe(struct i2c_adapter *adapter, int addr, int kind){	int rc = 0;	struct i2c_client *new_client = NULL;	struct rtc_device *rtc = NULL;	if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {		rc = -ENODEV;		goto failout;	}	new_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);	if (new_client == NULL) {		rc = -ENOMEM;		goto failout;	}	new_client->addr = addr;	new_client->adapter = adapter;	new_client->driver = &isl1208_driver;	new_client->flags = 0;	strcpy(new_client->name, DRV_NAME);	if (kind < 0) {		rc = isl1208_i2c_validate_client(new_client);		if (rc < 0)			goto failout;	}	rc = i2c_attach_client(new_client);	if (rc < 0)		goto failout;	dev_info(&new_client->dev,		 "chip found, driver version " DRV_VERSION "\n");	rtc = rtc_device_register(isl1208_driver.driver.name,				  &new_client->dev,				  &isl1208_rtc_ops, THIS_MODULE);	if (IS_ERR(rtc)) {		rc = PTR_ERR(rtc);		goto failout_detach;	}	i2c_set_clientdata(new_client, rtc);	rc = isl1208_i2c_get_sr(new_client);	if (rc < 0) {		dev_err(&new_client->dev, "reading status failed\n");		goto failout_unregister;	}	if (rc & ISL1208_REG_SR_RTCF)		dev_warn(&new_client->dev, "rtc power failure detected, "			 "please set clock.\n");	rc = device_create_file(&new_client->dev, &dev_attr_atrim);	if (rc < 0)		goto failout_unregister;	rc = device_create_file(&new_client->dev, &dev_attr_dtrim);	if (rc < 0)		goto failout_atrim;	rc = device_create_file(&new_client->dev, &dev_attr_usr);	if (rc < 0)		goto failout_dtrim;	return 0; failout_dtrim:	device_remove_file(&new_client->dev, &dev_attr_dtrim); failout_atrim:	device_remove_file(&new_client->dev, &dev_attr_atrim); failout_unregister:	rtc_device_unregister(rtc); failout_detach:	i2c_detach_client(new_client); failout:	kfree(new_client);	return rc;}static intisl1208_attach_adapter (struct i2c_adapter *adapter){	return i2c_probe(adapter, &addr_data, isl1208_probe);}static intisl1208_detach_client(struct i2c_client *client){	int rc;	struct rtc_device *const rtc = i2c_get_clientdata(client);	if (rtc)		rtc_device_unregister(rtc); /* do we need to kfree? */	rc = i2c_detach_client(client);	if (rc)		return rc;	kfree(client);	return 0;}/* module management */static int __init isl1208_init(void){	return i2c_add_driver(&isl1208_driver);}static void __exit isl1208_exit(void){	i2c_del_driver(&isl1208_driver);}MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>");MODULE_DESCRIPTION("Intersil ISL1208 RTC driver");MODULE_LICENSE("GPL");MODULE_VERSION(DRV_VERSION);module_init(isl1208_init);module_exit(isl1208_exit);

⌨️ 快捷键说明

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