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

📄 rtc-x1205.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	/* atr is a two's complement value on 6 bits,	 * perform sign extension. The formula is	 * Catr = (atr * 0.25pF) + 11.00pF.	 */	if (atr & 0x20)		atr |= 0xC0;	dev_dbg(&client->dev, "%s: raw atr=%x (%d)\n", __FUNCTION__, atr, atr);	*trim = (atr * 250) + 11000;	dev_dbg(&client->dev, "%s: real=%d\n", __FUNCTION__, *trim);	return 0;}struct x1205_limit{	unsigned char reg, mask, min, max;};static int x1205_validate_client(struct i2c_client *client){	int i, xfer;	/* Probe array. We will read the register at the specified	 * address and check if the given bits are zero.	 */	static const unsigned char probe_zero_pattern[] = {		/* register, mask */		X1205_REG_SR,	0x18,		X1205_REG_DTR,	0xF8,		X1205_REG_ATR,	0xC0,		X1205_REG_INT,	0x18,		X1205_REG_0,	0xFF,	};	static const struct x1205_limit probe_limits_pattern[] = {		/* register, mask, min, max */		{ X1205_REG_Y2K,	0xFF,	19,	20	},		{ X1205_REG_DW,		0xFF,	0,	6	},		{ X1205_REG_YR,		0xFF,	0,	99	},		{ X1205_REG_MO,		0xFF,	0,	12	},		{ X1205_REG_DT,		0xFF,	0,	31	},		{ X1205_REG_HR,		0x7F,	0,	23	},		{ X1205_REG_MN,		0xFF,	0,	59	},		{ X1205_REG_SC,		0xFF,	0,	59	},		{ X1205_REG_Y2K1,	0xFF,	19,	20	},		{ X1205_REG_Y2K0,	0xFF,	19,	20	},	};	/* check that registers have bits a 0 where expected */	for (i = 0; i < ARRAY_SIZE(probe_zero_pattern); i += 2) {		unsigned char buf;		unsigned char addr[2] = { 0, probe_zero_pattern[i] };		struct i2c_msg msgs[2] = {			{ client->addr, 0, 2, addr },			{ client->addr, I2C_M_RD, 1, &buf },		};		if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) {			dev_err(&client->dev,				"%s: could not read register %x\n",				__FUNCTION__, probe_zero_pattern[i]);			return -EIO;		}		if ((buf & probe_zero_pattern[i+1]) != 0) {			dev_err(&client->dev,				"%s: register=%02x, zero pattern=%d, value=%x\n",				__FUNCTION__, probe_zero_pattern[i], i, buf);			return -ENODEV;		}	}	/* check limits (only registers with bcd values) */	for (i = 0; i < ARRAY_SIZE(probe_limits_pattern); i++) {		unsigned char reg, value;		unsigned char addr[2] = { 0, probe_limits_pattern[i].reg };		struct i2c_msg msgs[2] = {			{ client->addr, 0, 2, addr },			{ client->addr, I2C_M_RD, 1, &reg },		};		if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) {			dev_err(&client->dev,				"%s: could not read register %x\n",				__FUNCTION__, probe_limits_pattern[i].reg);			return -EIO;		}		value = BCD2BIN(reg & probe_limits_pattern[i].mask);		if (value > probe_limits_pattern[i].max ||			value < probe_limits_pattern[i].min) {			dev_dbg(&client->dev,				"%s: register=%x, lim pattern=%d, value=%d\n",				__FUNCTION__, probe_limits_pattern[i].reg,				i, value);			return -ENODEV;		}	}	return 0;}static int x1205_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm){	return x1205_get_datetime(to_i2c_client(dev),		&alrm->time, X1205_ALM0_BASE);}static int x1205_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm){	return x1205_set_datetime(to_i2c_client(dev),		&alrm->time, 1, X1205_ALM0_BASE);}static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm){	return x1205_get_datetime(to_i2c_client(dev),		tm, X1205_CCR_BASE);}static int x1205_rtc_set_time(struct device *dev, struct rtc_time *tm){	return x1205_set_datetime(to_i2c_client(dev),		tm, 1, X1205_CCR_BASE);}static int x1205_rtc_proc(struct device *dev, struct seq_file *seq){	int err, dtrim, atrim;	if ((err = x1205_get_dtrim(to_i2c_client(dev), &dtrim)) == 0)		seq_printf(seq, "digital_trim\t: %d ppm\n", dtrim);	if ((err = x1205_get_atrim(to_i2c_client(dev), &atrim)) == 0)		seq_printf(seq, "analog_trim\t: %d.%02d pF\n",			atrim / 1000, atrim % 1000);	return 0;}static const struct rtc_class_ops x1205_rtc_ops = {	.proc		= x1205_rtc_proc,	.read_time	= x1205_rtc_read_time,	.set_time	= x1205_rtc_set_time,	.read_alarm	= x1205_rtc_read_alarm,	.set_alarm	= x1205_rtc_set_alarm,};static ssize_t x1205_sysfs_show_atrim(struct device *dev,				struct device_attribute *attr, char *buf){	int err, atrim;	err = x1205_get_atrim(to_i2c_client(dev), &atrim);	if (err)		return err;	return sprintf(buf, "%d.%02d pF\n", atrim / 1000, atrim % 1000);}static DEVICE_ATTR(atrim, S_IRUGO, x1205_sysfs_show_atrim, NULL);static ssize_t x1205_sysfs_show_dtrim(struct device *dev,				struct device_attribute *attr, char *buf){	int err, dtrim;	err = x1205_get_dtrim(to_i2c_client(dev), &dtrim);	if (err)		return err;	return sprintf(buf, "%d ppm\n", dtrim);}static DEVICE_ATTR(dtrim, S_IRUGO, x1205_sysfs_show_dtrim, NULL);static int x1205_attach(struct i2c_adapter *adapter){	return i2c_probe(adapter, &addr_data, x1205_probe);}static int x1205_probe(struct i2c_adapter *adapter, int address, int kind){	int err = 0;	unsigned char sr;	struct i2c_client *client;	struct rtc_device *rtc;	dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);	if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {		err = -ENODEV;		goto exit;	}	if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) {		err = -ENOMEM;		goto exit;	}	/* I2C client */	client->addr = address;	client->driver = &x1205_driver;	client->adapter	= adapter;	strlcpy(client->name, x1205_driver.driver.name, I2C_NAME_SIZE);	/* Verify the chip is really an X1205 */	if (kind < 0) {		if (x1205_validate_client(client) < 0) {			err = -ENODEV;			goto exit_kfree;		}	}	/* Inform the i2c layer */	if ((err = i2c_attach_client(client)))		goto exit_kfree;	dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");	rtc = rtc_device_register(x1205_driver.driver.name, &client->dev,				&x1205_rtc_ops, THIS_MODULE);	if (IS_ERR(rtc)) {		err = PTR_ERR(rtc);		goto exit_detach;	}	i2c_set_clientdata(client, rtc);	/* Check for power failures and eventualy enable the osc */	if ((err = x1205_get_status(client, &sr)) == 0) {		if (sr & X1205_SR_RTCF) {			dev_err(&client->dev,				"power failure detected, "				"please set the clock\n");			udelay(50);			x1205_fix_osc(client);		}	}	else		dev_err(&client->dev, "couldn't read status\n");	err = device_create_file(&client->dev, &dev_attr_atrim);	if (err) goto exit_devreg;	err = device_create_file(&client->dev, &dev_attr_dtrim);	if (err) goto exit_atrim;	return 0;exit_atrim:	device_remove_file(&client->dev, &dev_attr_atrim);exit_devreg:	rtc_device_unregister(rtc);exit_detach:	i2c_detach_client(client);exit_kfree:	kfree(client);exit:	return err;}static int x1205_detach(struct i2c_client *client){	int err;	struct rtc_device *rtc = i2c_get_clientdata(client); 	if (rtc)		rtc_device_unregister(rtc);	if ((err = i2c_detach_client(client)))		return err;	kfree(client);	return 0;}static int __init x1205_init(void){	return i2c_add_driver(&x1205_driver);}static void __exit x1205_exit(void){	i2c_del_driver(&x1205_driver);}MODULE_AUTHOR(	"Karen Spearel <kas111 at gmail dot com>, "	"Alessandro Zummo <a.zummo@towertech.it>");MODULE_DESCRIPTION("Xicor/Intersil X1205 RTC driver");MODULE_LICENSE("GPL");MODULE_VERSION(DRV_VERSION);module_init(x1205_init);module_exit(x1205_exit);

⌨️ 快捷键说明

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