rtc: fixes and new functionality for fm3130
- add sanity check for alarm data in fm3130_probe - fix fm3130_set_alarm. According to the datasheet, setting match bit '0' indicates that the corresponding alarm field will be used in the match process - add operation alarm_irq_enable operation which is responsible for handling RTC_AIE_ON, RTC_AIE_OFF ioctls - remove clearing of AF bit after reading rtc/alarm control register: according to datasheet this bit is cleared anyway when rtc/alarm control register is read [akpm@linux-foundation.org: make fm3130_alarm_irq_enable() static, fix comment layout] Signed-off-by: Sergey Matyukevich <geomatsi@gmail.com> Acked-by: Wan ZongShun <mcuos.com@gmail.com> Acked-by: Sergey Lapin <slapin@ossfans.org> Cc: Alessandro Zummo <a.zummo@towertech.it> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
		
							parent
							
								
									5824c7e667
								
							
						
					
					
						commit
						f3f99cf391
					
				| @ -52,8 +52,8 @@ struct fm3130 { | ||||
| 	struct i2c_msg		msg[4]; | ||||
| 	struct i2c_client	*client; | ||||
| 	struct rtc_device	*rtc; | ||||
| 	int			alarm_valid; | ||||
| 	int			data_valid; | ||||
| 	int			alarm; | ||||
| }; | ||||
| static const struct i2c_device_id fm3130_id[] = { | ||||
| 	{ "fm3130", 0 }, | ||||
| @ -87,11 +87,7 @@ static void fm3130_rtc_mode(struct device *dev, int mode) | ||||
| 		dev_dbg(dev, "invalid mode %d\n", mode); | ||||
| 		break; | ||||
| 	} | ||||
| 	/* Checking for alarm */ | ||||
| 	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) { | ||||
| 		fm3130->alarm = 1; | ||||
| 		fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF; | ||||
| 	} | ||||
| 
 | ||||
| 	i2c_smbus_write_byte_data(fm3130->client, | ||||
| 		 FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL]); | ||||
| } | ||||
| @ -208,6 +204,17 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||||
| 	struct fm3130 *fm3130 = dev_get_drvdata(dev); | ||||
| 	int tmp; | ||||
| 	struct rtc_time *tm = &alrm->time; | ||||
| 
 | ||||
| 	if (!fm3130->alarm_valid) { | ||||
| 		/*
 | ||||
| 		 * We have invalid alarm in RTC, probably due to battery faults | ||||
| 		 * or other problems. Return EIO for now, it will allow us to | ||||
| 		 * set alarm value later instead of error during probing which | ||||
| 		 * disables device | ||||
| 		 */ | ||||
| 		return -EIO; | ||||
| 	} | ||||
| 
 | ||||
| 	/* read the RTC alarm registers all at once */ | ||||
| 	tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent), | ||||
| 			&fm3130->msg[2], 2); | ||||
| @ -222,20 +229,31 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||||
| 			fm3130->regs[FM3130_ALARM_DATE], | ||||
| 			fm3130->regs[FM3130_ALARM_MONTHS]); | ||||
| 
 | ||||
| 
 | ||||
| 	tm->tm_sec	= bcd2bin(fm3130->regs[FM3130_ALARM_SECONDS] & 0x7F); | ||||
| 	tm->tm_min	= bcd2bin(fm3130->regs[FM3130_ALARM_MINUTES] & 0x7F); | ||||
| 	tm->tm_hour	= bcd2bin(fm3130->regs[FM3130_ALARM_HOURS] & 0x3F); | ||||
| 	tm->tm_mday	= bcd2bin(fm3130->regs[FM3130_ALARM_DATE] & 0x3F); | ||||
| 	tm->tm_mon	= bcd2bin(fm3130->regs[FM3130_ALARM_MONTHS] & 0x1F); | ||||
| 
 | ||||
| 	if (tm->tm_mon > 0) | ||||
| 		tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */ | ||||
| 
 | ||||
| 	dev_dbg(dev, "%s secs=%d, mins=%d, " | ||||
| 		"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n", | ||||
| 		"read alarm", tm->tm_sec, tm->tm_min, | ||||
| 		tm->tm_hour, tm->tm_mday, | ||||
| 		tm->tm_mon, tm->tm_year, tm->tm_wday); | ||||
| 
 | ||||
| 	/* check if alarm enabled */ | ||||
| 	fm3130->regs[FM3130_RTC_CONTROL] = | ||||
| 		i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); | ||||
| 
 | ||||
| 	if ((fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AEN) && | ||||
| 		(~fm3130->regs[FM3130_RTC_CONTROL] & | ||||
| 			FM3130_RTC_CONTROL_BIT_CAL)) { | ||||
| 		alrm->enabled = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| @ -251,25 +269,20 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||||
| 		tm->tm_hour, tm->tm_mday, | ||||
| 		tm->tm_mon, tm->tm_year, tm->tm_wday); | ||||
| 
 | ||||
| 	if (tm->tm_sec != -1) | ||||
| 		fm3130->regs[FM3130_ALARM_SECONDS] = | ||||
| 			bin2bcd(tm->tm_sec) | 0x80; | ||||
| 	fm3130->regs[FM3130_ALARM_SECONDS] = | ||||
| 		(tm->tm_sec != -1) ? bin2bcd(tm->tm_sec) : 0x80; | ||||
| 
 | ||||
| 	if (tm->tm_min != -1) | ||||
| 		fm3130->regs[FM3130_ALARM_MINUTES] = | ||||
| 			bin2bcd(tm->tm_min) | 0x80; | ||||
| 	fm3130->regs[FM3130_ALARM_MINUTES] = | ||||
| 		(tm->tm_min != -1) ? bin2bcd(tm->tm_min) : 0x80; | ||||
| 
 | ||||
| 	if (tm->tm_hour != -1) | ||||
| 		fm3130->regs[FM3130_ALARM_HOURS] = | ||||
| 			bin2bcd(tm->tm_hour) | 0x80; | ||||
| 	fm3130->regs[FM3130_ALARM_HOURS] = | ||||
| 		(tm->tm_hour != -1) ? bin2bcd(tm->tm_hour) : 0x80; | ||||
| 
 | ||||
| 	if (tm->tm_mday != -1) | ||||
| 		fm3130->regs[FM3130_ALARM_DATE] = | ||||
| 			bin2bcd(tm->tm_mday) | 0x80; | ||||
| 	fm3130->regs[FM3130_ALARM_DATE] = | ||||
| 		(tm->tm_mday != -1) ? bin2bcd(tm->tm_mday) : 0x80; | ||||
| 
 | ||||
| 	if (tm->tm_mon != -1) | ||||
| 		fm3130->regs[FM3130_ALARM_MONTHS] = | ||||
| 			bin2bcd(tm->tm_mon + 1) | 0x80; | ||||
| 	fm3130->regs[FM3130_ALARM_MONTHS] = | ||||
| 		(tm->tm_mon != -1) ? bin2bcd(tm->tm_mon + 1) : 0x80; | ||||
| 
 | ||||
| 	dev_dbg(dev, "alarm write %02x %02x %02x %02x %02x\n", | ||||
| 			fm3130->regs[FM3130_ALARM_SECONDS], | ||||
| @ -285,11 +298,8 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||||
| 	} | ||||
| 	fm3130->regs[FM3130_RTC_CONTROL] = | ||||
| 		i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); | ||||
| 	/* Checking for alarm */ | ||||
| 	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) { | ||||
| 		fm3130->alarm = 1; | ||||
| 		fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF; | ||||
| 	} | ||||
| 
 | ||||
| 	/* enable or disable alarm */ | ||||
| 	if (alrm->enabled) { | ||||
| 		i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL, | ||||
| 			(fm3130->regs[FM3130_RTC_CONTROL] & | ||||
| @ -298,16 +308,55 @@ static int fm3130_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) | ||||
| 	} else { | ||||
| 		i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL, | ||||
| 			fm3130->regs[FM3130_RTC_CONTROL] & | ||||
| 				~(FM3130_RTC_CONTROL_BIT_AEN)); | ||||
| 				~(FM3130_RTC_CONTROL_BIT_CAL) & | ||||
| 					~(FM3130_RTC_CONTROL_BIT_AEN)); | ||||
| 	} | ||||
| 
 | ||||
| 	/* We assume here that data is valid once written */ | ||||
| 	if (!fm3130->alarm_valid) | ||||
| 		fm3130->alarm_valid = 1; | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| 
 | ||||
| static int fm3130_alarm_irq_enable(struct device *dev, unsigned int enabled) | ||||
| { | ||||
| 	struct fm3130 *fm3130 = dev_get_drvdata(dev); | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| 	fm3130->regs[FM3130_RTC_CONTROL] = | ||||
| 		i2c_smbus_read_byte_data(fm3130->client, FM3130_RTC_CONTROL); | ||||
| 
 | ||||
| 	dev_dbg(dev, "alarm_irq_enable: enable=%d, FM3130_RTC_CONTROL=%02x\n", | ||||
| 		enabled, fm3130->regs[FM3130_RTC_CONTROL]); | ||||
| 
 | ||||
| 	switch (enabled) { | ||||
| 	case 0:		/* alarm off */ | ||||
| 		ret = i2c_smbus_write_byte_data(fm3130->client, | ||||
| 			FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL] & | ||||
| 				~(FM3130_RTC_CONTROL_BIT_CAL) & | ||||
| 					~(FM3130_RTC_CONTROL_BIT_AEN)); | ||||
| 		break; | ||||
| 	case 1:		/* alarm on */ | ||||
| 		ret = i2c_smbus_write_byte_data(fm3130->client, | ||||
| 			FM3130_RTC_CONTROL, (fm3130->regs[FM3130_RTC_CONTROL] & | ||||
| 				~(FM3130_RTC_CONTROL_BIT_CAL)) | | ||||
| 					FM3130_RTC_CONTROL_BIT_AEN); | ||||
| 		break; | ||||
| 	default: | ||||
| 		ret = -EINVAL; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| static const struct rtc_class_ops fm3130_rtc_ops = { | ||||
| 	.read_time	= fm3130_get_time, | ||||
| 	.set_time	= fm3130_set_time, | ||||
| 	.read_alarm	= fm3130_read_alarm, | ||||
| 	.set_alarm	= fm3130_set_alarm, | ||||
| 	.alarm_irq_enable = fm3130_alarm_irq_enable, | ||||
| }; | ||||
| 
 | ||||
| static struct i2c_driver fm3130_driver; | ||||
| @ -356,6 +405,7 @@ static int __devinit fm3130_probe(struct i2c_client *client, | ||||
| 	fm3130->msg[3].len = FM3130_ALARM_REGS; | ||||
| 	fm3130->msg[3].buf = &fm3130->regs[FM3130_ALARM_SECONDS]; | ||||
| 
 | ||||
| 	fm3130->alarm_valid = 0; | ||||
| 	fm3130->data_valid = 0; | ||||
| 
 | ||||
| 	tmp = i2c_transfer(adapter, fm3130->msg, 4); | ||||
| @ -370,12 +420,6 @@ static int __devinit fm3130_probe(struct i2c_client *client, | ||||
| 	fm3130->regs[FM3130_CAL_CONTROL] = | ||||
| 		i2c_smbus_read_byte_data(client, FM3130_CAL_CONTROL); | ||||
| 
 | ||||
| 	/* Checking for alarm */ | ||||
| 	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_AF) { | ||||
| 		fm3130->alarm = 1; | ||||
| 		fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF; | ||||
| 	} | ||||
| 
 | ||||
| 	/* Disabling calibration mode */ | ||||
| 	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL) { | ||||
| 		i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, | ||||
| @ -400,44 +444,79 @@ static int __devinit fm3130_probe(struct i2c_client *client, | ||||
| 			fm3130->regs[FM3130_CAL_CONTROL] & | ||||
| 				~(FM3130_CAL_CONTROL_BIT_nOSCEN)); | ||||
| 
 | ||||
| 	/* oscillator fault?  clear flag, and warn */ | ||||
| 	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) | ||||
| 	/* low battery?  clear flag, and warn */ | ||||
| 	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB) { | ||||
| 		i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, | ||||
| 			fm3130->regs[FM3130_RTC_CONTROL] & | ||||
| 				~(FM3130_RTC_CONTROL_BIT_LB)); | ||||
| 		dev_warn(&client->dev, "Low battery!\n"); | ||||
| 	} | ||||
| 
 | ||||
| 	/* oscillator fault?  clear flag, and warn */ | ||||
| 	/* check if Power On Reset bit is set */ | ||||
| 	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_POR) { | ||||
| 		i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL, | ||||
| 			fm3130->regs[FM3130_RTC_CONTROL] & | ||||
| 				~FM3130_RTC_CONTROL_BIT_POR); | ||||
| 		dev_warn(&client->dev, "SET TIME!\n"); | ||||
| 		dev_dbg(&client->dev, "POR bit is set\n"); | ||||
| 	} | ||||
| 	/* ACS is controlled by alarm */ | ||||
| 	i2c_smbus_write_byte_data(client, FM3130_ALARM_WP_CONTROL, 0x80); | ||||
| 
 | ||||
| 	/* TODO */ | ||||
| 	/* TODO need to sanity check alarm */ | ||||
| 	tmp = fm3130->regs[FM3130_RTC_SECONDS]; | ||||
| 	tmp = bcd2bin(tmp & 0x7f); | ||||
| 	if (tmp > 60) | ||||
| 		goto exit_bad; | ||||
| 	/* alarm registers sanity check */ | ||||
| 	tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); | ||||
| 	if (tmp > 59) | ||||
| 		goto bad_alarm; | ||||
| 
 | ||||
| 	tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); | ||||
| 	if (tmp > 60) | ||||
| 		goto exit_bad; | ||||
| 	if (tmp > 59) | ||||
| 		goto bad_alarm; | ||||
| 
 | ||||
| 	tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f); | ||||
| 	if (tmp > 23) | ||||
| 		goto bad_alarm; | ||||
| 
 | ||||
| 	tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f); | ||||
| 	if (tmp == 0 || tmp > 31) | ||||
| 		goto exit_bad; | ||||
| 		goto bad_alarm; | ||||
| 
 | ||||
| 	tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f); | ||||
| 	if (tmp == 0 || tmp > 12) | ||||
| 		goto exit_bad; | ||||
| 		goto bad_alarm; | ||||
| 
 | ||||
| 	tmp = fm3130->regs[FM3130_RTC_HOURS]; | ||||
| 	fm3130->alarm_valid = 1; | ||||
| 
 | ||||
| bad_alarm: | ||||
| 
 | ||||
| 	/* clock registers sanity chek */ | ||||
| 	tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f); | ||||
| 	if (tmp > 59) | ||||
| 		goto bad_clock; | ||||
| 
 | ||||
| 	tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f); | ||||
| 	if (tmp > 59) | ||||
| 		goto bad_clock; | ||||
| 
 | ||||
| 	tmp = bcd2bin(fm3130->regs[FM3130_RTC_HOURS] & 0x3f); | ||||
| 	if (tmp > 23) | ||||
| 		goto bad_clock; | ||||
| 
 | ||||
| 	tmp = bcd2bin(fm3130->regs[FM3130_RTC_DAY] & 0x7); | ||||
| 	if (tmp == 0 || tmp > 7) | ||||
| 		goto bad_clock; | ||||
| 
 | ||||
| 	tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f); | ||||
| 	if (tmp == 0 || tmp > 31) | ||||
| 		goto bad_clock; | ||||
| 
 | ||||
| 	tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f); | ||||
| 	if (tmp == 0 || tmp > 12) | ||||
| 		goto bad_clock; | ||||
| 
 | ||||
| 	fm3130->data_valid = 1; | ||||
| 
 | ||||
| exit_bad: | ||||
| 	if (!fm3130->data_valid) | ||||
| bad_clock: | ||||
| 
 | ||||
| 	if (!fm3130->data_valid || !fm3130->alarm_valid) | ||||
| 		dev_dbg(&client->dev, | ||||
| 				"%s: %02x %02x %02x %02x %02x %02x %02x %02x" | ||||
| 				"%02x %02x %02x %02x %02x %02x %02x\n", | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user