Skip to content

Commit

Permalink
Merge branch 'timers-rtc-for-linus' of git://git.kernel.org/pub/scm/l…
Browse files Browse the repository at this point in the history
…inux/kernel/git/tip/linux-2.6-tip

* 'timers-rtc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip:
  RTC: Fix up rtc.txt documentation to reflect changes to generic rtc layer
  RTC: sa1100: Update the sa1100 RTC driver.
  RTC: Fix the cross interrupt issue on rtc-test.
  RTC: Remove UIE and PIE information from the sa1100 driver proc.
  RTC: Include information about UIE and PIE in RTC driver proc.
  RTC: Clean out UIE icotl implementations
  RTC: Cleanup rtc_class_ops->update_irq_enable()
  RTC: Cleanup rtc_class_ops->irq_set_freq()
  RTC: Cleanup rtc_class_ops->irq_set_state
  RTC: Initialize kernel state from RTC
  • Loading branch information
Linus Torvalds committed Mar 16, 2011
2 parents 420c1c5 + ea04683 commit 21a3281
Show file tree
Hide file tree
Showing 36 changed files with 221 additions and 916 deletions.
29 changes: 10 additions & 19 deletions Documentation/rtc.txt
Original file line number Diff line number Diff line change
Expand Up @@ -178,38 +178,29 @@ RTC class framework, but can't be supported by the older driver.
setting the longer alarm time and enabling its IRQ using a single
request (using the same model as EFI firmware).

* RTC_UIE_ON, RTC_UIE_OFF ... if the RTC offers IRQs, it probably
also offers update IRQs whenever the "seconds" counter changes.
If needed, the RTC framework can emulate this mechanism.
* RTC_UIE_ON, RTC_UIE_OFF ... if the RTC offers IRQs, the RTC framework
will emulate this mechanism.

* RTC_PIE_ON, RTC_PIE_OFF, RTC_IRQP_SET, RTC_IRQP_READ ... another
feature often accessible with an IRQ line is a periodic IRQ, issued
at settable frequencies (usually 2^N Hz).
* RTC_PIE_ON, RTC_PIE_OFF, RTC_IRQP_SET, RTC_IRQP_READ ... these icotls
are emulated via a kernel hrtimer.

In many cases, the RTC alarm can be a system wake event, used to force
Linux out of a low power sleep state (or hibernation) back to a fully
operational state. For example, a system could enter a deep power saving
state until it's time to execute some scheduled tasks.

Note that many of these ioctls need not actually be implemented by your
driver. The common rtc-dev interface handles many of these nicely if your
driver returns ENOIOCTLCMD. Some common examples:
Note that many of these ioctls are handled by the common rtc-dev interface.
Some common examples:

* RTC_RD_TIME, RTC_SET_TIME: the read_time/set_time functions will be
called with appropriate values.

* RTC_ALM_SET, RTC_ALM_READ, RTC_WKALM_SET, RTC_WKALM_RD: the
set_alarm/read_alarm functions will be called.
* RTC_ALM_SET, RTC_ALM_READ, RTC_WKALM_SET, RTC_WKALM_RD: gets or sets
the alarm rtc_timer. May call the set_alarm driver function.

* RTC_IRQP_SET, RTC_IRQP_READ: the irq_set_freq function will be called
to set the frequency while the framework will handle the read for you
since the frequency is stored in the irq_freq member of the rtc_device
structure. Your driver needs to initialize the irq_freq member during
init. Make sure you check the requested frequency is in range of your
hardware in the irq_set_freq function. If it isn't, return -EINVAL. If
you cannot actually change the frequency, do not define irq_set_freq.
* RTC_IRQP_SET, RTC_IRQP_READ: These are emulated by the generic code.

* RTC_PIE_ON, RTC_PIE_OFF: the irq_set_state function will be called.
* RTC_PIE_ON, RTC_PIE_OFF: These are also emulated by the generic code.

If all else fails, check out the rtc-test.c driver!

Expand Down
7 changes: 7 additions & 0 deletions drivers/rtc/class.c
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
struct module *owner)
{
struct rtc_device *rtc;
struct rtc_wkalrm alrm;
int id, err;

if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
Expand Down Expand Up @@ -166,6 +167,12 @@ struct rtc_device *rtc_device_register(const char *name, struct device *dev,
rtc->pie_timer.function = rtc_pie_update_irq;
rtc->pie_enabled = 0;

/* Check to see if there is an ALARM already set in hw */
err = __rtc_read_alarm(rtc, &alrm);

if (!err && !rtc_valid_tm(&alrm.time))
rtc_set_alarm(rtc, &alrm);

strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
dev_set_name(&rtc->dev, "rtc%d", id);

Expand Down
180 changes: 180 additions & 0 deletions drivers/rtc/interface.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,186 @@ int rtc_set_mmss(struct rtc_device *rtc, unsigned long secs)
}
EXPORT_SYMBOL_GPL(rtc_set_mmss);

static int rtc_read_alarm_internal(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
int err;

err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return err;

if (rtc->ops == NULL)
err = -ENODEV;
else if (!rtc->ops->read_alarm)
err = -EINVAL;
else {
memset(alarm, 0, sizeof(struct rtc_wkalrm));
err = rtc->ops->read_alarm(rtc->dev.parent, alarm);
}

mutex_unlock(&rtc->ops_lock);
return err;
}

int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
int err;
struct rtc_time before, now;
int first_time = 1;
unsigned long t_now, t_alm;
enum { none, day, month, year } missing = none;
unsigned days;

/* The lower level RTC driver may return -1 in some fields,
* creating invalid alarm->time values, for reasons like:
*
* - The hardware may not be capable of filling them in;
* many alarms match only on time-of-day fields, not
* day/month/year calendar data.
*
* - Some hardware uses illegal values as "wildcard" match
* values, which non-Linux firmware (like a BIOS) may try
* to set up as e.g. "alarm 15 minutes after each hour".
* Linux uses only oneshot alarms.
*
* When we see that here, we deal with it by using values from
* a current RTC timestamp for any missing (-1) values. The
* RTC driver prevents "periodic alarm" modes.
*
* But this can be racey, because some fields of the RTC timestamp
* may have wrapped in the interval since we read the RTC alarm,
* which would lead to us inserting inconsistent values in place
* of the -1 fields.
*
* Reading the alarm and timestamp in the reverse sequence
* would have the same race condition, and not solve the issue.
*
* So, we must first read the RTC timestamp,
* then read the RTC alarm value,
* and then read a second RTC timestamp.
*
* If any fields of the second timestamp have changed
* when compared with the first timestamp, then we know
* our timestamp may be inconsistent with that used by
* the low-level rtc_read_alarm_internal() function.
*
* So, when the two timestamps disagree, we just loop and do
* the process again to get a fully consistent set of values.
*
* This could all instead be done in the lower level driver,
* but since more than one lower level RTC implementation needs it,
* then it's probably best best to do it here instead of there..
*/

/* Get the "before" timestamp */
err = rtc_read_time(rtc, &before);
if (err < 0)
return err;
do {
if (!first_time)
memcpy(&before, &now, sizeof(struct rtc_time));
first_time = 0;

/* get the RTC alarm values, which may be incomplete */
err = rtc_read_alarm_internal(rtc, alarm);
if (err)
return err;

/* full-function RTCs won't have such missing fields */
if (rtc_valid_tm(&alarm->time) == 0)
return 0;

/* get the "after" timestamp, to detect wrapped fields */
err = rtc_read_time(rtc, &now);
if (err < 0)
return err;

/* note that tm_sec is a "don't care" value here: */
} while ( before.tm_min != now.tm_min
|| before.tm_hour != now.tm_hour
|| before.tm_mon != now.tm_mon
|| before.tm_year != now.tm_year);

/* Fill in the missing alarm fields using the timestamp; we
* know there's at least one since alarm->time is invalid.
*/
if (alarm->time.tm_sec == -1)
alarm->time.tm_sec = now.tm_sec;
if (alarm->time.tm_min == -1)
alarm->time.tm_min = now.tm_min;
if (alarm->time.tm_hour == -1)
alarm->time.tm_hour = now.tm_hour;

/* For simplicity, only support date rollover for now */
if (alarm->time.tm_mday == -1) {
alarm->time.tm_mday = now.tm_mday;
missing = day;
}
if (alarm->time.tm_mon == -1) {
alarm->time.tm_mon = now.tm_mon;
if (missing == none)
missing = month;
}
if (alarm->time.tm_year == -1) {
alarm->time.tm_year = now.tm_year;
if (missing == none)
missing = year;
}

/* with luck, no rollover is needed */
rtc_tm_to_time(&now, &t_now);
rtc_tm_to_time(&alarm->time, &t_alm);
if (t_now < t_alm)
goto done;

switch (missing) {

/* 24 hour rollover ... if it's now 10am Monday, an alarm that
* that will trigger at 5am will do so at 5am Tuesday, which
* could also be in the next month or year. This is a common
* case, especially for PCs.
*/
case day:
dev_dbg(&rtc->dev, "alarm rollover: %s\n", "day");
t_alm += 24 * 60 * 60;
rtc_time_to_tm(t_alm, &alarm->time);
break;

/* Month rollover ... if it's the 31th, an alarm on the 3rd will
* be next month. An alarm matching on the 30th, 29th, or 28th
* may end up in the month after that! Many newer PCs support
* this type of alarm.
*/
case month:
dev_dbg(&rtc->dev, "alarm rollover: %s\n", "month");
do {
if (alarm->time.tm_mon < 11)
alarm->time.tm_mon++;
else {
alarm->time.tm_mon = 0;
alarm->time.tm_year++;
}
days = rtc_month_days(alarm->time.tm_mon,
alarm->time.tm_year);
} while (days < alarm->time.tm_mday);
break;

/* Year rollover ... easy except for leap years! */
case year:
dev_dbg(&rtc->dev, "alarm rollover: %s\n", "year");
do {
alarm->time.tm_year++;
} while (rtc_valid_tm(&alarm->time) != 0);
break;

default:
dev_warn(&rtc->dev, "alarm rollover not handled\n");
}

done:
return 0;
}

int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
{
int err;
Expand Down
28 changes: 0 additions & 28 deletions drivers/rtc/rtc-at91rm9200.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,33 +183,6 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
return 0;
}

/*
* Handle commands from user-space
*/
static int at91_rtc_ioctl(struct device *dev, unsigned int cmd,
unsigned long arg)
{
int ret = 0;

pr_debug("%s(): cmd=%08x, arg=%08lx.\n", __func__, cmd, arg);

/* important: scrub old status before enabling IRQs */
switch (cmd) {
case RTC_UIE_OFF: /* update off */
at91_sys_write(AT91_RTC_IDR, AT91_RTC_SECEV);
break;
case RTC_UIE_ON: /* update on */
at91_sys_write(AT91_RTC_SCCR, AT91_RTC_SECEV);
at91_sys_write(AT91_RTC_IER, AT91_RTC_SECEV);
break;
default:
ret = -ENOIOCTLCMD;
break;
}

return ret;
}

static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
pr_debug("%s(): cmd=%08x\n", __func__, enabled);
Expand Down Expand Up @@ -269,7 +242,6 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id)
}

static const struct rtc_class_ops at91_rtc_ops = {
.ioctl = at91_rtc_ioctl,
.read_time = at91_rtc_readtime,
.set_time = at91_rtc_settime,
.read_alarm = at91_rtc_readalarm,
Expand Down
28 changes: 0 additions & 28 deletions drivers/rtc/rtc-at91sam9.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,33 +216,6 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
return 0;
}

/*
* Handle commands from user-space
*/
static int at91_rtc_ioctl(struct device *dev, unsigned int cmd,
unsigned long arg)
{
struct sam9_rtc *rtc = dev_get_drvdata(dev);
int ret = 0;
u32 mr = rtt_readl(rtc, MR);

dev_dbg(dev, "ioctl: cmd=%08x, arg=%08lx, mr %08x\n", cmd, arg, mr);

switch (cmd) {
case RTC_UIE_OFF: /* update off */
rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN);
break;
case RTC_UIE_ON: /* update on */
rtt_writel(rtc, MR, mr | AT91_RTT_RTTINCIEN);
break;
default:
ret = -ENOIOCTLCMD;
break;
}

return ret;
}

static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct sam9_rtc *rtc = dev_get_drvdata(dev);
Expand Down Expand Up @@ -303,7 +276,6 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc)
}

static const struct rtc_class_ops at91_rtc_ops = {
.ioctl = at91_rtc_ioctl,
.read_time = at91_rtc_readtime,
.set_time = at91_rtc_settime,
.read_alarm = at91_rtc_readalarm,
Expand Down
27 changes: 0 additions & 27 deletions drivers/rtc/rtc-bfin.c
Original file line number Diff line number Diff line change
Expand Up @@ -240,32 +240,6 @@ static void bfin_rtc_int_set_alarm(struct bfin_rtc *rtc)
*/
bfin_rtc_int_set(rtc->rtc_alarm.tm_yday == -1 ? RTC_ISTAT_ALARM : RTC_ISTAT_ALARM_DAY);
}
static int bfin_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
struct bfin_rtc *rtc = dev_get_drvdata(dev);
int ret = 0;

dev_dbg_stamp(dev);

bfin_rtc_sync_pending(dev);

switch (cmd) {
case RTC_UIE_ON:
dev_dbg_stamp(dev);
bfin_rtc_int_set(RTC_ISTAT_SEC);
break;
case RTC_UIE_OFF:
dev_dbg_stamp(dev);
bfin_rtc_int_clear(~RTC_ISTAT_SEC);
break;

default:
dev_dbg_stamp(dev);
ret = -ENOIOCTLCMD;
}

return ret;
}

static int bfin_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
Expand Down Expand Up @@ -358,7 +332,6 @@ static int bfin_rtc_proc(struct device *dev, struct seq_file *seq)
}

static struct rtc_class_ops bfin_rtc_ops = {
.ioctl = bfin_rtc_ioctl,
.read_time = bfin_rtc_read_time,
.set_time = bfin_rtc_set_time,
.read_alarm = bfin_rtc_read_alarm,
Expand Down
Loading

0 comments on commit 21a3281

Please sign in to comment.