Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 208459
b: refs/heads/master
c: f3f99cf
h: refs/heads/master
i:
  208457: 707de12
  208455: 82463bb
v: v3
  • Loading branch information
Sergey Matyukevich authored and Linus Torvalds committed Aug 11, 2010
1 parent bd9da29 commit 28bc4e7
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 52 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 5824c7e66724d4d42f01c69feb818d7ac5f11bd9
refs/heads/master: f3f99cf39180fc04a36cd376f434092fae85f317
181 changes: 130 additions & 51 deletions trunk/drivers/rtc/rtc-fm3130.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down Expand Up @@ -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]);
}
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}

Expand All @@ -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],
Expand All @@ -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] &
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand All @@ -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",
Expand Down

0 comments on commit 28bc4e7

Please sign in to comment.