Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f3f99cf3 authored by Sergey Matyukevich's avatar Sergey Matyukevich Committed by Linus Torvalds
Browse files

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: default avatarSergey Matyukevich <geomatsi@gmail.com>
Acked-by: default avatarWan ZongShun <mcuos.com@gmail.com>
Acked-by: default avatarSergey Lapin <slapin@ossfans.org>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 5824c7e6
Loading
Loading
Loading
Loading
+130 −51
Original line number Original line Diff line number Diff line
@@ -52,8 +52,8 @@ struct fm3130 {
	struct i2c_msg		msg[4];
	struct i2c_msg		msg[4];
	struct i2c_client	*client;
	struct i2c_client	*client;
	struct rtc_device	*rtc;
	struct rtc_device	*rtc;
	int			alarm_valid;
	int			data_valid;
	int			data_valid;
	int			alarm;
};
};
static const struct i2c_device_id fm3130_id[] = {
static const struct i2c_device_id fm3130_id[] = {
	{ "fm3130", 0 },
	{ "fm3130", 0 },
@@ -87,11 +87,7 @@ static void fm3130_rtc_mode(struct device *dev, int mode)
		dev_dbg(dev, "invalid mode %d\n", mode);
		dev_dbg(dev, "invalid mode %d\n", mode);
		break;
		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,
	i2c_smbus_write_byte_data(fm3130->client,
		 FM3130_RTC_CONTROL, fm3130->regs[FM3130_RTC_CONTROL]);
		 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);
	struct fm3130 *fm3130 = dev_get_drvdata(dev);
	int tmp;
	int tmp;
	struct rtc_time *tm = &alrm->time;
	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 */
	/* read the RTC alarm registers all at once */
	tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent),
	tmp = i2c_transfer(to_i2c_adapter(fm3130->client->dev.parent),
			&fm3130->msg[2], 2);
			&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_DATE],
			fm3130->regs[FM3130_ALARM_MONTHS]);
			fm3130->regs[FM3130_ALARM_MONTHS]);



	tm->tm_sec	= bcd2bin(fm3130->regs[FM3130_ALARM_SECONDS] & 0x7F);
	tm->tm_sec	= bcd2bin(fm3130->regs[FM3130_ALARM_SECONDS] & 0x7F);
	tm->tm_min	= bcd2bin(fm3130->regs[FM3130_ALARM_MINUTES] & 0x7F);
	tm->tm_min	= bcd2bin(fm3130->regs[FM3130_ALARM_MINUTES] & 0x7F);
	tm->tm_hour	= bcd2bin(fm3130->regs[FM3130_ALARM_HOURS] & 0x3F);
	tm->tm_hour	= bcd2bin(fm3130->regs[FM3130_ALARM_HOURS] & 0x3F);
	tm->tm_mday	= bcd2bin(fm3130->regs[FM3130_ALARM_DATE] & 0x3F);
	tm->tm_mday	= bcd2bin(fm3130->regs[FM3130_ALARM_DATE] & 0x3F);
	tm->tm_mon	= bcd2bin(fm3130->regs[FM3130_ALARM_MONTHS] & 0x1F);
	tm->tm_mon	= bcd2bin(fm3130->regs[FM3130_ALARM_MONTHS] & 0x1F);

	if (tm->tm_mon > 0)
	if (tm->tm_mon > 0)
		tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */
		tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */

	dev_dbg(dev, "%s secs=%d, mins=%d, "
	dev_dbg(dev, "%s secs=%d, mins=%d, "
		"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
		"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
		"read alarm", tm->tm_sec, tm->tm_min,
		"read alarm", tm->tm_sec, tm->tm_min,
		tm->tm_hour, tm->tm_mday,
		tm->tm_hour, tm->tm_mday,
		tm->tm_mon, tm->tm_year, tm->tm_wday);
		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;
	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_hour, tm->tm_mday,
		tm->tm_mon, tm->tm_year, tm->tm_wday);
		tm->tm_mon, tm->tm_year, tm->tm_wday);


	if (tm->tm_sec != -1)
	fm3130->regs[FM3130_ALARM_SECONDS] =
	fm3130->regs[FM3130_ALARM_SECONDS] =
			bin2bcd(tm->tm_sec) | 0x80;
		(tm->tm_sec != -1) ? bin2bcd(tm->tm_sec) : 0x80;


	if (tm->tm_min != -1)
	fm3130->regs[FM3130_ALARM_MINUTES] =
	fm3130->regs[FM3130_ALARM_MINUTES] =
			bin2bcd(tm->tm_min) | 0x80;
		(tm->tm_min != -1) ? bin2bcd(tm->tm_min) : 0x80;


	if (tm->tm_hour != -1)
	fm3130->regs[FM3130_ALARM_HOURS] =
	fm3130->regs[FM3130_ALARM_HOURS] =
			bin2bcd(tm->tm_hour) | 0x80;
		(tm->tm_hour != -1) ? bin2bcd(tm->tm_hour) : 0x80;


	if (tm->tm_mday != -1)
	fm3130->regs[FM3130_ALARM_DATE] =
	fm3130->regs[FM3130_ALARM_DATE] =
			bin2bcd(tm->tm_mday) | 0x80;
		(tm->tm_mday != -1) ? bin2bcd(tm->tm_mday) : 0x80;


	if (tm->tm_mon != -1)
	fm3130->regs[FM3130_ALARM_MONTHS] =
	fm3130->regs[FM3130_ALARM_MONTHS] =
			bin2bcd(tm->tm_mon + 1) | 0x80;
		(tm->tm_mon != -1) ? bin2bcd(tm->tm_mon + 1) : 0x80;


	dev_dbg(dev, "alarm write %02x %02x %02x %02x %02x\n",
	dev_dbg(dev, "alarm write %02x %02x %02x %02x %02x\n",
			fm3130->regs[FM3130_ALARM_SECONDS],
			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] =
	fm3130->regs[FM3130_RTC_CONTROL] =
		i2c_smbus_read_byte_data(fm3130->client, 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) {
	/* enable or disable alarm */
		fm3130->alarm = 1;
		fm3130->regs[FM3130_RTC_CONTROL] &= ~FM3130_RTC_CONTROL_BIT_AF;
	}
	if (alrm->enabled) {
	if (alrm->enabled) {
		i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
		i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
			(fm3130->regs[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 {
	} else {
		i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
		i2c_smbus_write_byte_data(fm3130->client, FM3130_RTC_CONTROL,
			fm3130->regs[FM3130_RTC_CONTROL] &
			fm3130->regs[FM3130_RTC_CONTROL] &
				~(FM3130_RTC_CONTROL_BIT_CAL) &
					~(FM3130_RTC_CONTROL_BIT_AEN));
					~(FM3130_RTC_CONTROL_BIT_AEN));
	}
	}

	/* We assume here that data is valid once written */
	if (!fm3130->alarm_valid)
		fm3130->alarm_valid = 1;

	return 0;
	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 = {
static const struct rtc_class_ops fm3130_rtc_ops = {
	.read_time	= fm3130_get_time,
	.read_time	= fm3130_get_time,
	.set_time	= fm3130_set_time,
	.set_time	= fm3130_set_time,
	.read_alarm	= fm3130_read_alarm,
	.read_alarm	= fm3130_read_alarm,
	.set_alarm	= fm3130_set_alarm,
	.set_alarm	= fm3130_set_alarm,
	.alarm_irq_enable = fm3130_alarm_irq_enable,
};
};


static struct i2c_driver fm3130_driver;
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].len = FM3130_ALARM_REGS;
	fm3130->msg[3].buf = &fm3130->regs[FM3130_ALARM_SECONDS];
	fm3130->msg[3].buf = &fm3130->regs[FM3130_ALARM_SECONDS];


	fm3130->alarm_valid = 0;
	fm3130->data_valid = 0;
	fm3130->data_valid = 0;


	tmp = i2c_transfer(adapter, fm3130->msg, 4);
	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] =
	fm3130->regs[FM3130_CAL_CONTROL] =
		i2c_smbus_read_byte_data(client, 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 */
	/* Disabling calibration mode */
	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL) {
	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_CAL) {
		i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
		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->regs[FM3130_CAL_CONTROL] &
				~(FM3130_CAL_CONTROL_BIT_nOSCEN));
				~(FM3130_CAL_CONTROL_BIT_nOSCEN));


	/* oscillator fault?  clear flag, and warn */
	/* low battery?  clear flag, and warn */
	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_LB)
	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");
		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) {
	if (fm3130->regs[FM3130_RTC_CONTROL] & FM3130_RTC_CONTROL_BIT_POR) {
		i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
		i2c_smbus_write_byte_data(client, FM3130_RTC_CONTROL,
			fm3130->regs[FM3130_RTC_CONTROL] &
			fm3130->regs[FM3130_RTC_CONTROL] &
				~FM3130_RTC_CONTROL_BIT_POR);
				~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 */
	/* ACS is controlled by alarm */
	i2c_smbus_write_byte_data(client, FM3130_ALARM_WP_CONTROL, 0x80);
	i2c_smbus_write_byte_data(client, FM3130_ALARM_WP_CONTROL, 0x80);


	/* TODO */
	/* alarm registers sanity check */
	/* TODO need to sanity check alarm */
	tmp = bcd2bin(fm3130->regs[FM3130_RTC_SECONDS] & 0x7f);
	tmp = fm3130->regs[FM3130_RTC_SECONDS];
	if (tmp > 59)
	tmp = bcd2bin(tmp & 0x7f);
		goto bad_alarm;
	if (tmp > 60)

		goto exit_bad;
	tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
	tmp = bcd2bin(fm3130->regs[FM3130_RTC_MINUTES] & 0x7f);
	if (tmp > 60)
	if (tmp > 59)
		goto exit_bad;
		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);
	tmp = bcd2bin(fm3130->regs[FM3130_RTC_DATE] & 0x3f);
	if (tmp == 0 || tmp > 31)
	if (tmp == 0 || tmp > 31)
		goto exit_bad;
		goto bad_alarm;


	tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);
	tmp = bcd2bin(fm3130->regs[FM3130_RTC_MONTHS] & 0x1f);
	if (tmp == 0 || tmp > 12)
	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;
	fm3130->data_valid = 1;


exit_bad:
bad_clock:
	if (!fm3130->data_valid)

	if (!fm3130->data_valid || !fm3130->alarm_valid)
		dev_dbg(&client->dev,
		dev_dbg(&client->dev,
				"%s: %02x %02x %02x %02x %02x %02x %02x %02x"
				"%s: %02x %02x %02x %02x %02x %02x %02x %02x"
				"%02x %02x %02x %02x %02x %02x %02x\n",
				"%02x %02x %02x %02x %02x %02x %02x\n",