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

📄 rtc-rs5c372.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * An I2C driver for Ricoh RS5C372 and RV5C38[67] RTCs * * Copyright (C) 2005 Pavel Mironchik <pmironchik@optifacio.net> * Copyright (C) 2006 Tower Technologies * * 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/i2c.h>#include <linux/rtc.h>#include <linux/bcd.h>#define DRV_VERSION "0.5"/* * Ricoh has a family of I2C based RTCs, which differ only slightly from * each other.  Differences center on pinout (e.g. how many interrupts, * output clock, etc) and how the control registers are used.  The '372 * is significant only because that's the one this driver first supported. */#define RS5C372_REG_SECS	0#define RS5C372_REG_MINS	1#define RS5C372_REG_HOURS	2#define RS5C372_REG_WDAY	3#define RS5C372_REG_DAY		4#define RS5C372_REG_MONTH	5#define RS5C372_REG_YEAR	6#define RS5C372_REG_TRIM	7#	define RS5C372_TRIM_XSL		0x80#	define RS5C372_TRIM_MASK	0x7F#define RS5C_REG_ALARM_A_MIN	8			/* or ALARM_W */#define RS5C_REG_ALARM_A_HOURS	9#define RS5C_REG_ALARM_A_WDAY	10#define RS5C_REG_ALARM_B_MIN	11			/* or ALARM_D */#define RS5C_REG_ALARM_B_HOURS	12#define RS5C_REG_ALARM_B_WDAY	13			/* (ALARM_B only) */#define RS5C_REG_CTRL1		14#	define RS5C_CTRL1_AALE		(1 << 7)	/* or WALE */#	define RS5C_CTRL1_BALE		(1 << 6)	/* or DALE */#	define RV5C387_CTRL1_24		(1 << 5)#	define RS5C372A_CTRL1_SL1	(1 << 5)#	define RS5C_CTRL1_CT_MASK	(7 << 0)#	define RS5C_CTRL1_CT0		(0 << 0)	/* no periodic irq */#	define RS5C_CTRL1_CT4		(4 << 0)	/* 1 Hz level irq */#define RS5C_REG_CTRL2		15#	define RS5C372_CTRL2_24		(1 << 5)#	define RS5C_CTRL2_XSTP		(1 << 4)#	define RS5C_CTRL2_CTFG		(1 << 2)#	define RS5C_CTRL2_AAFG		(1 << 1)	/* or WAFG */#	define RS5C_CTRL2_BAFG		(1 << 0)	/* or DAFG *//* to read (style 1) or write registers starting at R */#define RS5C_ADDR(R)		(((R) << 4) | 0)enum rtc_type {	rtc_undef = 0,	rtc_rs5c372a,	rtc_rs5c372b,	rtc_rv5c386,	rtc_rv5c387a,};/* REVISIT:  this assumes that: *  - we're in the 21st century, so it's safe to ignore the century *    bit for rv5c38[67] (REG_MONTH bit 7); *  - we should use ALARM_A not ALARM_B (may be wrong on some boards) */struct rs5c372 {	struct i2c_client	*client;	struct rtc_device	*rtc;	enum rtc_type		type;	unsigned		time24:1;	unsigned		has_irq:1;	char			buf[17];	char			*regs;};static int rs5c_get_regs(struct rs5c372 *rs5c){	struct i2c_client	*client = rs5c->client;	struct i2c_msg		msgs[] = {		{ client->addr, I2C_M_RD, sizeof rs5c->buf, rs5c->buf },	};	/* This implements the third reading method from the datasheet, using	 * an internal address that's reset after each transaction (by STOP)	 * to 0x0f ... so we read extra registers, and skip the first one.	 *	 * The first method doesn't work with the iop3xx adapter driver, on at	 * least 80219 chips; this works around that bug.	 */	if ((i2c_transfer(client->adapter, msgs, 1)) != 1) {		pr_debug("%s: can't read registers\n", rs5c->rtc->name);		return -EIO;	}	dev_dbg(&client->dev,		"%02x %02x %02x (%02x) %02x %02x %02x (%02x), "		"%02x %02x %02x, %02x %02x %02x; %02x %02x\n",		rs5c->regs[0],  rs5c->regs[1],  rs5c->regs[2],  rs5c->regs[3],		rs5c->regs[4],  rs5c->regs[5],  rs5c->regs[6],  rs5c->regs[7],		rs5c->regs[8],  rs5c->regs[9],  rs5c->regs[10], rs5c->regs[11],		rs5c->regs[12], rs5c->regs[13], rs5c->regs[14], rs5c->regs[15]);	return 0;}static unsigned rs5c_reg2hr(struct rs5c372 *rs5c, unsigned reg){	unsigned	hour;	if (rs5c->time24)		return BCD2BIN(reg & 0x3f);	hour = BCD2BIN(reg & 0x1f);	if (hour == 12)		hour = 0;	if (reg & 0x20)		hour += 12;	return hour;}static unsigned rs5c_hr2reg(struct rs5c372 *rs5c, unsigned hour){	if (rs5c->time24)		return BIN2BCD(hour);	if (hour > 12)		return 0x20 | BIN2BCD(hour - 12);	if (hour == 12)		return 0x20 | BIN2BCD(12);	if (hour == 0)		return BIN2BCD(12);	return BIN2BCD(hour);}static int rs5c372_get_datetime(struct i2c_client *client, struct rtc_time *tm){	struct rs5c372	*rs5c = i2c_get_clientdata(client);	int		status = rs5c_get_regs(rs5c);	if (status < 0)		return status;	tm->tm_sec = BCD2BIN(rs5c->regs[RS5C372_REG_SECS] & 0x7f);	tm->tm_min = BCD2BIN(rs5c->regs[RS5C372_REG_MINS] & 0x7f);	tm->tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C372_REG_HOURS]);	tm->tm_wday = BCD2BIN(rs5c->regs[RS5C372_REG_WDAY] & 0x07);	tm->tm_mday = BCD2BIN(rs5c->regs[RS5C372_REG_DAY] & 0x3f);	/* tm->tm_mon is zero-based */	tm->tm_mon = BCD2BIN(rs5c->regs[RS5C372_REG_MONTH] & 0x1f) - 1;	/* year is 1900 + tm->tm_year */	tm->tm_year = BCD2BIN(rs5c->regs[RS5C372_REG_YEAR]) + 100;	dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "		"mday=%d, mon=%d, year=%d, wday=%d\n",		__FUNCTION__,		tm->tm_sec, tm->tm_min, tm->tm_hour,		tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);	/* rtc might need initialization */	return rtc_valid_tm(tm);}static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm){	struct rs5c372	*rs5c = i2c_get_clientdata(client);	unsigned char	buf[8];	dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d "		"mday=%d, mon=%d, year=%d, wday=%d\n",		__FUNCTION__,		tm->tm_sec, tm->tm_min, tm->tm_hour,		tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);	buf[0] = RS5C_ADDR(RS5C372_REG_SECS);	buf[1] = BIN2BCD(tm->tm_sec);	buf[2] = BIN2BCD(tm->tm_min);	buf[3] = rs5c_hr2reg(rs5c, tm->tm_hour);	buf[4] = BIN2BCD(tm->tm_wday);	buf[5] = BIN2BCD(tm->tm_mday);	buf[6] = BIN2BCD(tm->tm_mon + 1);	buf[7] = BIN2BCD(tm->tm_year - 100);	if ((i2c_master_send(client, buf, 8)) != 8) {		dev_err(&client->dev, "%s: write error\n", __FUNCTION__);		return -EIO;	}	return 0;}#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)#define	NEED_TRIM#endif#if defined(CONFIG_RTC_INTF_SYSFS) || defined(CONFIG_RTC_INTF_SYSFS_MODULE)#define	NEED_TRIM#endif#ifdef	NEED_TRIMstatic int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim){	struct rs5c372 *rs5c372 = i2c_get_clientdata(client);	u8 tmp = rs5c372->regs[RS5C372_REG_TRIM];	if (osc)		*osc = (tmp & RS5C372_TRIM_XSL) ? 32000 : 32768;	if (trim) {		dev_dbg(&client->dev, "%s: raw trim=%x\n", __FUNCTION__, tmp);		tmp &= RS5C372_TRIM_MASK;		if (tmp & 0x3e) {			int t = tmp & 0x3f;			if (tmp & 0x40)				t = (~t | (s8)0xc0) + 1;			else				t = t - 1;			tmp = t * 2;		} else			tmp = 0;		*trim = tmp;	}	return 0;}#endifstatic int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm){	return rs5c372_get_datetime(to_i2c_client(dev), tm);}static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm){	return rs5c372_set_datetime(to_i2c_client(dev), tm);}#if defined(CONFIG_RTC_INTF_DEV) || defined(CONFIG_RTC_INTF_DEV_MODULE)static intrs5c_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg){	struct i2c_client	*client = to_i2c_client(dev);	struct rs5c372		*rs5c = i2c_get_clientdata(client);	unsigned char		buf[2];	int			status;	buf[1] = rs5c->regs[RS5C_REG_CTRL1];	switch (cmd) {	case RTC_UIE_OFF:	case RTC_UIE_ON:		/* some 327a modes use a different IRQ pin for 1Hz irqs */		if (rs5c->type == rtc_rs5c372a				&& (buf[1] & RS5C372A_CTRL1_SL1))			return -ENOIOCTLCMD;	case RTC_AIE_OFF:	case RTC_AIE_ON:		/* these irq management calls only make sense for chips		 * which are wired up to an IRQ.		 */		if (!rs5c->has_irq)			return -ENOIOCTLCMD;		break;	default:		return -ENOIOCTLCMD;	}	status = rs5c_get_regs(rs5c);	if (status < 0)		return status;	buf[0] = RS5C_ADDR(RS5C_REG_CTRL1);	switch (cmd) {	case RTC_AIE_OFF:	/* alarm off */		buf[1] &= ~RS5C_CTRL1_AALE;		break;	case RTC_AIE_ON:	/* alarm on */		buf[1] |= RS5C_CTRL1_AALE;		break;	case RTC_UIE_OFF:	/* update off */		buf[1] &= ~RS5C_CTRL1_CT_MASK;		break;	case RTC_UIE_ON:	/* update on */		buf[1] &= ~RS5C_CTRL1_CT_MASK;		buf[1] |= RS5C_CTRL1_CT4;		break;	}	if ((i2c_master_send(client, buf, 2)) != 2) {		printk(KERN_WARNING "%s: can't update alarm\n",			rs5c->rtc->name);		status = -EIO;	} else		rs5c->regs[RS5C_REG_CTRL1] = buf[1];	return status;}#else#define	rs5c_rtc_ioctl	NULL#endif/* NOTE:  Since RTC_WKALM_{RD,SET} were originally defined for EFI, * which only exposes a polled programming interface; and since * these calls map directly to those EFI requests; we don't demand * we have an IRQ for this chip when we go through this API. * * The older x86_pc derived RTC_ALM_{READ,SET} calls require irqs * though, managed through RTC_AIE_{ON,OFF} requests. */static int rs5c_read_alarm(struct device *dev, struct rtc_wkalrm *t){	struct i2c_client	*client = to_i2c_client(dev);	struct rs5c372		*rs5c = i2c_get_clientdata(client);	int			status;	status = rs5c_get_regs(rs5c);	if (status < 0)		return status;	/* report alarm time */	t->time.tm_sec = 0;	t->time.tm_min = BCD2BIN(rs5c->regs[RS5C_REG_ALARM_A_MIN] & 0x7f);

⌨️ 快捷键说明

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