Message ID | 1256408743-6644-2-git-send-email-geomatsi@gmail.com |
---|---|
State | Changes Requested, archived |
Headers | show |
On Sat, Oct 24, 2009 at 10:25:42PM +0400, Sergey Matyukevich wrote: > > This patch contains the following fixes and enhancements > for fm3130 rtc driver: > > * add sanity check for alarm data in fm3130_probe > > * fix fm3130_set_alarm: > according to datasheet, setting match bit '0' indicates > that the corresponding alarm field will be used in the > match process > > * add ioctls for RTC_AIE_ON, RTC_AIE_OFF > > * 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 This patch is mostly OK, but see below. Anyway, I think it can go in as is. Acked-by: Sergey Lapin <slapin@ossfans.org> > --- > drivers/rtc/rtc-fm3130.c | 179 +++++++++++++++++++++++++++++++++------------- > 1 files changed, 128 insertions(+), 51 deletions(-) > > diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c > index 812c667..5df991a 100644 > --- a/drivers/rtc/rtc-fm3130.c > +++ b/drivers/rtc/rtc-fm3130.c > @@ -51,8 +51,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 }, > @@ -86,11 +86,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]); > } > @@ -207,6 +203,15 @@ 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); > @@ -221,20 +226,30 @@ static int fm3130_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) > fm3130->regs[FM3130_ALARM_DATE], > fm3130->regs[FM3130_ALARM_MONTHS]); > > - I think that dosn't belong here.... > 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); > + Ditto. > if (tm->tm_mon > 0) > tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */ > + And so on. > 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; > } > > @@ -250,25 +265,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], > @@ -284,11 +294,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] & > @@ -297,12 +304,52 @@ 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_ioctl(struct device *dev, unsigned int cmd, > + unsigned long arg) > +{ > + 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, "ioctl: cmd=%08x, FM3130_RTC_CONTROL=%08x\n", > + cmd, fm3130->regs[FM3130_RTC_CONTROL]); > + > + switch (cmd) { > + case RTC_AIE_OFF: /* 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 RTC_AIE_ON: /* 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 = -ENOIOCTLCMD; > + break; > + } > + > + return ret; > +} > + > static const struct rtc_class_ops fm3130_rtc_ops = { > + .ioctl = fm3130_ioctl, > .read_time = fm3130_get_time, > .set_time = fm3130_set_time, > .read_alarm = fm3130_read_alarm, > @@ -355,6 +402,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); > @@ -369,12 +417,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, > @@ -399,44 +441,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", > -- > 1.6.2.5 > > > --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to "rtc-linux". Membership options at http://groups.google.com/group/rtc-linux . Please read http://groups.google.com/group/rtc-linux/web/checklist before submitting a driver. -~----------~----~----~----~------~----~------~--~---
diff --git a/drivers/rtc/rtc-fm3130.c b/drivers/rtc/rtc-fm3130.c index 812c667..5df991a 100644 --- a/drivers/rtc/rtc-fm3130.c +++ b/drivers/rtc/rtc-fm3130.c @@ -51,8 +51,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 }, @@ -86,11 +86,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]); } @@ -207,6 +203,15 @@ 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); @@ -221,20 +226,30 @@ 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; } @@ -250,25 +265,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], @@ -284,11 +294,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] & @@ -297,12 +304,52 @@ 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_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + 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, "ioctl: cmd=%08x, FM3130_RTC_CONTROL=%08x\n", + cmd, fm3130->regs[FM3130_RTC_CONTROL]); + + switch (cmd) { + case RTC_AIE_OFF: /* 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 RTC_AIE_ON: /* 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 = -ENOIOCTLCMD; + break; + } + + return ret; +} + static const struct rtc_class_ops fm3130_rtc_ops = { + .ioctl = fm3130_ioctl, .read_time = fm3130_get_time, .set_time = fm3130_set_time, .read_alarm = fm3130_read_alarm, @@ -355,6 +402,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); @@ -369,12 +417,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, @@ -399,44 +441,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",