blob: b95c0acd5f00c8c117cb67a55f0e64597e486e6a [file] [log] [blame]
/*
* ImgTec PowerDown Controller (PDC) RTC
*
* Copyright 2010-2012 Imagination Technologies Ltd.
*
*/
#include <linux/init.h>
#include <linux/rtc.h>
#include <linux/io.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
/* needed for clk32k interface */
#include <asm/soc-tz1090/clock.h>
#define HW_EPOCH 2000
#define HW_YEARS 100
#define LIN_EPOCH 1900
#define HW2LIN_EPOCH (HW_EPOCH - LIN_EPOCH)
#define LIN2HW_EPOCH (LIN_EPOCH - HW_EPOCH)
/* Registers */
#define PDC_RTC_CONTROL 0x00
#define PDC_RTC_SEC 0x04
#define PDC_RTC_MIN 0x08
#define PDC_RTC_HOUR 0x0c
#define PDC_RTC_DAY 0x10
#define PDC_RTC_MON 0x14
#define PDC_RTC_YEAR 0x18
#define PDC_RTC_ASEC 0x1c
#define PDC_RTC_AMIN 0x20
#define PDC_RTC_AHOUR 0x24
#define PDC_RTC_ADAY 0x28
#define PDC_RTC_AMON 0x2c
#define PDC_RTC_AYEAR 0x30
#define PDC_RTC_IRQ_STATUS 0x34
#define PDC_RTC_IRQ_CLEAR 0x38
#define PDC_RTC_IRQ_EN 0x3c
/* Register field masks */
#define PDC_RTC_CONTROL_GAE 0x08 /* global alarm enable */
#define PDC_RTC_CONTROL_FAST 0x04
#define PDC_RTC_CONTROL_UPDATE 0x02
#define PDC_RTC_CONTROL_CE 0x01 /* clock enable */
#define PDC_RTC_SEC_SEC 0x3f
#define PDC_RTC_MIN_MIN 0x3f
#define PDC_RTC_HOUR_HOUR 0x1f
#define PDC_RTC_DAY_DAY 0x1f
#define PDC_RTC_MON_MON 0x0f
#define PDC_RTC_YEAR_YEAR 0x7f
#define PDC_RTC_ASEC_EN 0x40
#define PDC_RTC_ASEC_ASEC 0x3f
#define PDC_RTC_AMIN_EN 0x40
#define PDC_RTC_AMIN_AMIN 0x3f
#define PDC_RTC_AHOUR_EN 0x20
#define PDC_RTC_AHOUR_AHOUR 0x1f
#define PDC_RTC_ADAY_EN 0x20
#define PDC_RTC_ADAY_ADAY 0x1f
#define PDC_RTC_AMON_EN 0x10
#define PDC_RTC_AMON_AMON 0x0f
#define PDC_RTC_AYEAR_EN 0x80
#define PDC_RTC_AYEAR_AYEAR 0x7f
#define PDC_RTC_IRQ_ALARM 0x04
#define PDC_RTC_IRQ_MIN 0x02
#define PDC_RTC_IRQ_SEC 0x01
/**
* struct pdc_rtc_priv - Private PDC RTC data.
* @rtc_dev: RTC device structure.
* @dev: Platform device (used for dev_dbg messages etc).
* @irq: IRQ number of RTC device.
* @reg_base: Base of registers memory.
* @nonvolatile_base: Base of non-volatile registers if provided.
* @nonvolatile_len: Length of non-volatile registers.
* @time_set_delay: Number of seconds it takes to set the time.
* @alarm_irq_delay: Number of seconds the alarm IRQ is delayed.
* @clk_nb: Notifier block for clock notify events.
* @alarm_pending: Whether an alarm has fired and hasn't been handled.
* @lock: Protects PDC_RTC_CONTROL, control_reg, and
* softalrm_sec.
* @control_reg: Back up of PDC_RTC_CONTROL to work around buggy
* hardware. It takes time for some last written values to
* take effect, and later writes can replace a write that
* hasn't taken effect yet.
* @softalrm_sec: Software alarm second, for emulating alarms which need
* to fire very soon, which would otherwise foil our work
* around for late alarm interrupts.
* @hardalrm_offset: Offset from desired alarm time given to hardware.
* @hardstop_time: Time the alarm was stopped (or possibly second before).
* @alrm_time: Time of current alarm, for filtering out delayed
* cancelled alarm interrupts.
* @adj_alrm_time: Time of current adjusted alarm (for wrong clock rate).
* @time_update_diff: Difference between the old time and the new time, which
* should be used while hardware is still updating the
* time.
* @suspended: Whether the device is in suspend mode, in which case rtc
* interrupt events should be postponed until resume (see
* postponed_rtc_int).
* @wakeup: Whether the device can wake the system from a sleep
* state.
* @postponed_rtc_int: Postponed rtc interrupt flags to submit on resume.
* @last_irq_en: Preserved IRQ enable state when wakeup is in use.
*/
struct pdc_rtc_priv {
struct rtc_device *rtc_dev;
struct device *dev;
int irq;
void __iomem *reg_base;
void __iomem *nonvolatile_base;
unsigned long nonvolatile_len;
unsigned int time_set_delay;
unsigned int alarm_irq_delay;
struct notifier_block clk_nb;
int alarm_pending;
spinlock_t lock;
u32 control_reg;
int softalrm_sec;
int hardalrm_offset;
unsigned long hardstop_time;
unsigned long alrm_time;
unsigned long adj_alrm_time;
unsigned long time_update_diff;
/* suspend data */
bool suspended;
bool wakeup;
unsigned int postponed_rtc_int;
unsigned int last_irq_en;
};
static void pdc_rtc_write(struct pdc_rtc_priv *priv,
unsigned int reg_offs, unsigned int data)
{
iowrite32(data, priv->reg_base + reg_offs);
}
static unsigned int pdc_rtc_read(struct pdc_rtc_priv *priv,
unsigned int reg_offs)
{
return ioread32(priv->reg_base + reg_offs);
}
static int pdc_rtc_write_nonvolatile(struct pdc_rtc_priv *priv,
unsigned int reg_offs, unsigned long in)
{
if (reg_offs >= priv->nonvolatile_len)
return -EINVAL;
iowrite32(in, priv->nonvolatile_base + reg_offs);
return 0;
}
static int pdc_rtc_read_nonvolatile(struct pdc_rtc_priv *priv,
unsigned int reg_offs, unsigned long *out)
{
if (reg_offs >= priv->nonvolatile_len)
return -EINVAL;
*out = ioread32(priv->nonvolatile_base + reg_offs);
return 0;
}
/* caller must hold lock. does not handle read during time update. */
static void _pdc_rtc_read_time_raw(struct pdc_rtc_priv *priv,
struct rtc_time *tm, int *updating)
{
int min, upd = 0;
/*
* If it takes time for the time to get set and an update is in
* progress, we need to check that the update is still in progress
* afterwards otherwise it will have changed while we were reading it.
*/
if (priv->time_set_delay)
upd = pdc_rtc_read(priv, PDC_RTC_CONTROL)
& PDC_RTC_CONTROL_UPDATE;
start_again:
/*
* We re-read the minute at the end of the loop to check it hasn't
* changed while we were reading the others. If it has then we didn't
* read atomically and should try again. The second is allowed to
* change by itself as that won't result in an inconsistent time.
*/
min = pdc_rtc_read(priv, PDC_RTC_MIN) & PDC_RTC_MIN_MIN;
do {
tm->tm_sec = (pdc_rtc_read(priv, PDC_RTC_SEC) &
PDC_RTC_SEC_SEC);
tm->tm_min = min;
tm->tm_hour = (pdc_rtc_read(priv, PDC_RTC_HOUR) &
PDC_RTC_HOUR_HOUR);
tm->tm_mday = (pdc_rtc_read(priv, PDC_RTC_DAY) &
PDC_RTC_DAY_DAY);
tm->tm_mon = (pdc_rtc_read(priv, PDC_RTC_MON) &
PDC_RTC_MON_MON) - 1;
tm->tm_year = (pdc_rtc_read(priv, PDC_RTC_YEAR) &
PDC_RTC_YEAR_YEAR) + HW2LIN_EPOCH;
if (upd) {
upd = pdc_rtc_read(priv, PDC_RTC_CONTROL)
& PDC_RTC_CONTROL_UPDATE;
/* did the update finish while we were reading */
if (!upd)
goto start_again;
}
min = pdc_rtc_read(priv, PDC_RTC_MIN) & PDC_RTC_MIN_MIN;
if (min != tm->tm_min)
dev_dbg(priv->dev,
"time read %02d:%02d:%02d nonatomic, retrying\n",
tm->tm_hour, tm->tm_min, tm->tm_sec);
} while (min != tm->tm_min);
if (updating)
*updating = upd;
}
/* caller must hold lock. does handle read during time update. */
static void _pdc_rtc_read_time(struct pdc_rtc_priv *priv, struct rtc_time *tm)
{
int upd;
unsigned long time;
_pdc_rtc_read_time_raw(priv, tm, &upd);
dev_dbg(priv->dev, "time read %02d:%02d:%02d\n",
tm->tm_hour, tm->tm_min, tm->tm_sec);
/* if we got the old time during an update, add the difference */
if (upd && priv->time_update_diff) {
rtc_tm_to_time(tm, &time);
time += priv->time_update_diff;
rtc_time_to_tm(time, tm);
dev_dbg(priv->dev,
"update was in progress, adjusting to %02d:%02d:%02d\n",
tm->tm_hour, tm->tm_min, tm->tm_sec);
}
}
static int pdc_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct pdc_rtc_priv *priv = dev_get_drvdata(dev);
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
_pdc_rtc_read_time(priv, tm);
spin_unlock_irqrestore(&priv->lock, flags);
tm->tm_wday = -1;
tm->tm_yday = -1;
tm->tm_isdst = -1;
return 0;
}
static int pdc_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct pdc_rtc_priv *priv = dev_get_drvdata(dev);
unsigned int hw_year;
unsigned int ctrl;
unsigned long flags;
unsigned long time, rtime;
struct rtc_time tm_adj;
dev_dbg(priv->dev, "time set %02d:%02d:%02d\n",
tm->tm_hour, tm->tm_min, tm->tm_sec);
/*
* Due to a hardware quirk the time may only be set after several
* seconds.
*/
if (priv->time_set_delay) {
rtc_tm_to_time(tm, &time);
rtc_time_to_tm(time + priv->time_set_delay, &tm_adj);
tm = &tm_adj;
}
hw_year = tm->tm_year + LIN2HW_EPOCH;
/* year must be in range */
if (hw_year >= HW_YEARS)
return -EINVAL;
spin_lock_irqsave(&priv->lock, flags);
/* write out the values */
pdc_rtc_write(priv, PDC_RTC_SEC, tm->tm_sec);
pdc_rtc_write(priv, PDC_RTC_MIN, tm->tm_min);
pdc_rtc_write(priv, PDC_RTC_HOUR, tm->tm_hour);
pdc_rtc_write(priv, PDC_RTC_DAY, tm->tm_mday);
pdc_rtc_write(priv, PDC_RTC_MON, tm->tm_mon + 1);
pdc_rtc_write(priv, PDC_RTC_YEAR, hw_year);
/* update the clock with the written values */
ctrl = priv->control_reg | PDC_RTC_CONTROL_UPDATE;
pdc_rtc_write(priv, PDC_RTC_CONTROL, ctrl);
/*
* Record the offset of the new time so that we can calculate the
* current time before the update is complete.
*/
if (priv->time_set_delay) {
_pdc_rtc_read_time_raw(priv, &tm_adj, NULL);
rtc_tm_to_time(&tm_adj, &rtime);
priv->time_update_diff = time - rtime;
}
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int pdc_rtc_alarm_enabled(struct pdc_rtc_priv *priv)
{
return !!(priv->control_reg & PDC_RTC_CONTROL_GAE);
}
static int pdc_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct pdc_rtc_priv *priv = dev_get_drvdata(dev);
unsigned long flags;
unsigned long scheduled;
int offset;
spin_lock_irqsave(&priv->lock, flags);
offset = priv->hardalrm_offset;
/* Just get the register values */
alrm->enabled = pdc_rtc_alarm_enabled(priv);
alrm->pending = priv->alarm_pending;
alrm->time.tm_sec = pdc_rtc_read(priv, PDC_RTC_ASEC);
alrm->time.tm_min = pdc_rtc_read(priv, PDC_RTC_AMIN);
alrm->time.tm_hour = pdc_rtc_read(priv, PDC_RTC_AHOUR);
alrm->time.tm_mday = pdc_rtc_read(priv, PDC_RTC_ADAY);
alrm->time.tm_mon = pdc_rtc_read(priv, PDC_RTC_AMON);
alrm->time.tm_year = pdc_rtc_read(priv, PDC_RTC_AYEAR);
spin_unlock_irqrestore(&priv->lock, flags);
/* Any misisng _EN bit translated to -1 */
if (alrm->time.tm_sec & PDC_RTC_ASEC_EN)
alrm->time.tm_sec &= PDC_RTC_ASEC_ASEC;
else
alrm->time.tm_sec = -1;
if (alrm->time.tm_min & PDC_RTC_AMIN_EN)
alrm->time.tm_min &= PDC_RTC_AMIN_AMIN;
else
alrm->time.tm_min = -1;
if (alrm->time.tm_hour & PDC_RTC_AHOUR_EN)
alrm->time.tm_hour &= PDC_RTC_AHOUR_AHOUR;
else
alrm->time.tm_hour = -1;
if (alrm->time.tm_mday & PDC_RTC_ADAY_EN)
alrm->time.tm_mday &= PDC_RTC_ADAY_ADAY;
else
alrm->time.tm_mday = -1;
if (alrm->time.tm_wday & PDC_RTC_ADAY_EN)
alrm->time.tm_wday &= PDC_RTC_ADAY_ADAY;
else
alrm->time.tm_wday = -1;
if (alrm->time.tm_mon & PDC_RTC_AMON_EN)
alrm->time.tm_mon = (alrm->time.tm_mon & PDC_RTC_AMON_AMON) - 1;
else
alrm->time.tm_mon = -1;
if (alrm->time.tm_year & PDC_RTC_AYEAR_EN)
alrm->time.tm_year = (alrm->time.tm_year & PDC_RTC_AYEAR_AYEAR)
+ HW2LIN_EPOCH;
else
alrm->time.tm_year = -1;
alrm->time.tm_wday = -1;
alrm->time.tm_yday = -1;
alrm->time.tm_isdst = -1;
/*
* The alarm time in the hardware is offset to compensate for the late
* alarm interrupts, so we need to adjust it to get the original alarm.
*/
rtc_tm_to_time(&alrm->time, &scheduled);
scheduled -= offset;
rtc_time_to_tm(scheduled, &alrm->time);
return 0;
}
/* caller must hold priv->lock */
static void _pdc_rtc_stop_alarm(struct pdc_rtc_priv *priv, unsigned long now)
{
/* disable the secondly interrupt */
pdc_rtc_write(priv, PDC_RTC_IRQ_EN, PDC_RTC_IRQ_ALARM);
priv->softalrm_sec = -1;
/* disable the global alarm enable bit */
priv->control_reg &= ~PDC_RTC_CONTROL_GAE;
pdc_rtc_write(priv, PDC_RTC_CONTROL, priv->control_reg);
priv->hardstop_time = now;
}
static void pdc_rtc_stop_alarm(struct pdc_rtc_priv *priv)
{
unsigned long flags;
struct rtc_time tm;
unsigned long now = 0;
spin_lock_irqsave(&priv->lock, flags);
if (priv->alarm_irq_delay) {
pdc_rtc_read_time(priv->dev, &tm);
rtc_tm_to_time(&tm, &now);
}
_pdc_rtc_stop_alarm(priv, now);
spin_unlock_irqrestore(&priv->lock, flags);
}
static void pdc_rtc_start_alarm(struct pdc_rtc_priv *priv)
{
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
priv->control_reg |= PDC_RTC_CONTROL_GAE;
pdc_rtc_write(priv, PDC_RTC_CONTROL, priv->control_reg);
spin_unlock_irqrestore(&priv->lock, flags);
}
static int _pdc_rtc_set_alarm(struct pdc_rtc_priv *priv, bool temporary,
unsigned long scheduled, struct rtc_wkalrm *alrm)
{
struct rtc_time tm;
unsigned long flags;
unsigned long now, adjusted;
struct rtc_wkalrm alrm_adj, *alrm_orig = alrm;
spin_lock_irqsave(&priv->lock, flags);
/*
* If we're compensating for hardware bugs, don't change the stored
* alarm time.
*/
if (!temporary)
priv->alrm_time = scheduled;
/*
* But still record the alarm being set so we can tell whether we need
* to change it back again.
*/
priv->adj_alrm_time = scheduled;
/*
* Due to a hardware quirk the alarm may fire several seconds late, so
* rewind the scheduled alarm time to compensate.
*/
if (priv->alarm_irq_delay) {
alrm_adj = *alrm;
pdc_rtc_read_time(priv->dev, &tm);
rtc_tm_to_time(&tm, &now);
try_again_locked:
adjusted = scheduled - priv->alarm_irq_delay;
/* disable the alarm while we set it up */
_pdc_rtc_stop_alarm(priv, now);
/* Make sure we're not setting the alarm in the past */
if (scheduled <= now) {
spin_unlock_irqrestore(&priv->lock, flags);
return -ETIME;
}
/* If adjusted time is in past, emulate with a soft alarm */
if (adjusted <= now && alrm->enabled) {
/* clear and enable secondly interrupt */
pdc_rtc_write(priv, PDC_RTC_IRQ_CLEAR, PDC_RTC_IRQ_SEC);
pdc_rtc_write(priv, PDC_RTC_IRQ_EN,
PDC_RTC_IRQ_ALARM | PDC_RTC_IRQ_SEC);
priv->softalrm_sec = alrm_orig->time.tm_sec;
/* still set the real alarm as early as possible */
adjusted = now + 1;
}
priv->hardalrm_offset = adjusted - scheduled;
spin_unlock_irqrestore(&priv->lock, flags);
rtc_time_to_tm(adjusted, &alrm_adj.time);
alrm = &alrm_adj;
dev_dbg(priv->dev, "alarm setting %02d:%02d:%02d\n",
alrm->time.tm_hour, alrm->time.tm_min,
alrm->time.tm_sec);
} else {
/* disable the alarm while we set it up */
_pdc_rtc_stop_alarm(priv, 0);
adjusted = scheduled;
priv->hardalrm_offset = 0;
spin_unlock_irqrestore(&priv->lock, flags);
}
/*
* don't use fields set to -1
* all smaller fields than a field in use must also be in use
*/
if (alrm->time.tm_year >= 0) {
tm.tm_year = alrm->time.tm_year + LIN2HW_EPOCH;
/* year must be in range */
if ((unsigned int)tm.tm_year > HW_YEARS)
return -EINVAL;
tm.tm_year |= PDC_RTC_AYEAR_EN;
} else
tm.tm_year = 0;
if (alrm->time.tm_mon >= 0)
tm.tm_mon = (alrm->time.tm_mon + 1) | PDC_RTC_AMON_EN;
else if (tm.tm_year & PDC_RTC_AYEAR_EN)
return -EINVAL;
else
tm.tm_mon = 0;
if (alrm->time.tm_mday >= 0)
tm.tm_mday = alrm->time.tm_mday | PDC_RTC_ADAY_EN;
else if (tm.tm_mon & PDC_RTC_AMON_EN)
return -EINVAL;
else
tm.tm_mday = 0;
if (alrm->time.tm_hour >= 0)
tm.tm_hour = alrm->time.tm_hour | PDC_RTC_AHOUR_EN;
else if (tm.tm_mday & PDC_RTC_ADAY_EN)
return -EINVAL;
else
tm.tm_hour = 0;
if (alrm->time.tm_min >= 0)
tm.tm_min = alrm->time.tm_min | PDC_RTC_AMIN_EN;
else if (tm.tm_hour & PDC_RTC_AHOUR_EN)
return -EINVAL;
else
tm.tm_min = 0;
if (alrm->time.tm_sec >= 0)
tm.tm_sec = alrm->time.tm_sec | PDC_RTC_ASEC_EN;
else if (tm.tm_min & PDC_RTC_AMIN_EN)
return -EINVAL;
else
tm.tm_sec = 0;
spin_lock_irqsave(&priv->lock, flags);
pdc_rtc_write(priv, PDC_RTC_ASEC, tm.tm_sec);
pdc_rtc_write(priv, PDC_RTC_AMIN, tm.tm_min);
pdc_rtc_write(priv, PDC_RTC_AHOUR, tm.tm_hour);
pdc_rtc_write(priv, PDC_RTC_ADAY, tm.tm_mday);
pdc_rtc_write(priv, PDC_RTC_AMON, tm.tm_mon);
pdc_rtc_write(priv, PDC_RTC_AYEAR, tm.tm_year);
priv->alarm_pending = 0;
spin_unlock_irqrestore(&priv->lock, flags);
/* re-enable the alarm if applicable */
if (alrm->enabled) {
pdc_rtc_start_alarm(priv);
/*
* Check that setting the alarm didn't lose the race against the
* next clock tick (which may be the one we're trying to set the
* alarm on). It may be that the interrupt just hasn't been
* handled yet (e.g. on another CPU), but it does no harm to
* handle it here instead.
*/
spin_lock_irqsave(&priv->lock, flags);
if (!priv->alarm_pending) {
pdc_rtc_read_time(priv->dev, &tm);
rtc_tm_to_time(&tm, &now);
/* If it's too late, immediately trigger the alarm */
if (scheduled <= now) {
_pdc_rtc_stop_alarm(priv, now);
dev_dbg(priv->dev,
"alarm set race lost, triggering immediately\n");
spin_unlock_irqrestore(&priv->lock, flags);
return -ETIME;
}
/*
* If we've missed the window of oportunity to set the
* alarm interrupt, we need to reconsider.
*/
if (adjusted <= now) {
dev_dbg(priv->dev,
"alarm set race lost, retrying\n");
goto try_again_locked;
}
}
spin_unlock_irqrestore(&priv->lock, flags);
}
return 0;
}
static int pdc_rtc_adjust_alarm_time(struct pdc_rtc_priv *priv,
unsigned long scheduled)
{
struct rtc_wkalrm alrm;
alrm.enabled = 1;
alrm.pending = 0;
rtc_time_to_tm(scheduled, &alrm.time);
return _pdc_rtc_set_alarm(priv, true, scheduled, &alrm);
}
static int pdc_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct pdc_rtc_priv *priv = dev_get_drvdata(dev);
unsigned long scheduled;
dev_dbg(priv->dev, "alarm set %02d:%02d:%02d\n",
alrm->time.tm_hour, alrm->time.tm_min, alrm->time.tm_sec);
rtc_tm_to_time(&alrm->time, &scheduled);
return _pdc_rtc_set_alarm(priv, false, scheduled, alrm);
}
static int pdc_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct pdc_rtc_priv *priv = dev_get_drvdata(dev);
dev_dbg(priv->dev, "alarm irq enable %d\n",
enabled);
if (enabled)
pdc_rtc_start_alarm(priv);
else
pdc_rtc_stop_alarm(priv);
return 0;
}
static struct rtc_class_ops pdc_rtc_ops = {
.read_time = pdc_rtc_read_time,
.set_time = pdc_rtc_set_time,
.read_alarm = pdc_rtc_read_alarm,
.set_alarm = pdc_rtc_set_alarm,
.alarm_irq_enable = pdc_rtc_alarm_irq_enable,
};
static irqreturn_t pdc_rtc_isr(int irq, void *dev_id)
{
struct pdc_rtc_priv *priv = dev_id;
unsigned int status;
unsigned long events = RTC_IRQF;
unsigned long flags;
struct rtc_time tm;
unsigned long now = 0;
u32 sec;
spin_lock_irqsave(&priv->lock, flags);
status = pdc_rtc_read(priv, PDC_RTC_IRQ_STATUS);
pdc_rtc_write(priv, PDC_RTC_IRQ_CLEAR, status);
/* ignore delayed alarm interrupts after turned alarm off */
if (pdc_rtc_alarm_enabled(priv)) {
if (status & PDC_RTC_IRQ_ALARM) {
/*
* Alarm interrupt.
* If an alarm was cancelled, we may still get the
* delayed interrupt, so we need to check the current
* time has actually exceeded the alarm time.
* Of course if we've corrected the time we may also get
* a legitimate interrupt early, so we use hardstop_time
* to check whether the alarm could be the second after
* we've disabled it, which would indicate an echo.
*/
_pdc_rtc_read_time(priv, &tm);
rtc_tm_to_time(&tm, &now);
if (now >= priv->alrm_time) {
events |= RTC_AF;
dev_dbg(priv->dev, "isr alarm %02d:%02d:%02d\n",
tm.tm_hour, tm.tm_min, tm.tm_sec);
} else if (now > priv->hardstop_time &&
now <= priv->hardstop_time + 2) {
dev_dbg(priv->dev,
"isr alarm %02d:%02d:%02d ignored (echo %lu disabled %lu)\n",
tm.tm_hour, tm.tm_min, tm.tm_sec,
now, priv->hardstop_time);
} else {
/*
* It looks like a legitimate interrupt since we
* haven't just cancelled an alarm.
*/
events |= RTC_AF;
dev_dbg(priv->dev,
"isr alarm %02d:%02d:%02d (early %lu < %lu)\n",
tm.tm_hour, tm.tm_min, tm.tm_sec,
now, priv->alrm_time);
}
} else if (status & PDC_RTC_IRQ_SEC) {
/*
* Secondly interrupt.
* Check if the current second matches the soft alarm.
*/
sec = pdc_rtc_read(priv, PDC_RTC_SEC);
if (priv->softalrm_sec == sec)
events |= RTC_AF;
dev_dbg(priv->dev,
"isr second %02d:%02d:%02d (compare %02d)\n",
pdc_rtc_read(priv, PDC_RTC_HOUR),
pdc_rtc_read(priv, PDC_RTC_MIN),
sec, priv->softalrm_sec);
}
/* rtc alarms are one-shot */
priv->hardstop_time = 0;
if (events & RTC_AF) {
if (!now && priv->alarm_irq_delay) {
pdc_rtc_read_time(priv->dev, &tm);
rtc_tm_to_time(&tm, &now);
}
priv->alarm_pending = 1;
_pdc_rtc_stop_alarm(priv, now);
}
} else {
/* make absolutely sure that the alarm is properly stopped */
if (priv->alarm_irq_delay) {
pdc_rtc_read_time(priv->dev, &tm);
rtc_tm_to_time(&tm, &now);
}
_pdc_rtc_stop_alarm(priv, now);
dev_dbg(priv->dev,
"isr irq %#x ignored (alarm disabled)\n",
status);
}
spin_unlock_irqrestore(&priv->lock, flags);
if (events != RTC_IRQF)
rtc_update_irq(priv->rtc_dev, 1, events);
return IRQ_HANDLED;
}
/* Non-volatile registers available to the RTC */
/* The last time when the RTC was adjusted and should have been correct */
#define PDC_RTC_SRPROT_LASTTIME 0x0
/*
* Sub-second accumulated skew due to the changes in clock frequency.
* Fixed point number with shift of PDC_RTC_SWPROT_SKEW_BITS.
* I.e. best_time = rtctime + skew
* Must be in the range -0.5 <= X < 0.5.
*/
#define PDC_RTC_SRPROT_SKEW 0x4
#define PDC_RTC_SWPROT_SKEW_BITS 15
static void pdc_rtc_change_frequency(struct pdc_rtc_priv *priv,
struct clk32k_change_freq *change)
{
unsigned long last_time;
long skew, half;
int skew_err, alrm_err, alrm_en;
unsigned long now, adj;
unsigned long orig_alrm_time, adj_alrm_time, alrm_time;
unsigned long diff;
u64 diff64;
struct rtc_time tm;
unsigned long flags;
dev_dbg(priv->dev, "clk changed %lu HZ -> %lu HZ\n",
change->old_freq, change->new_freq);
/* adjust time since last recorded time */
if (pdc_rtc_read_nonvolatile(priv, PDC_RTC_SRPROT_LASTTIME, &last_time))
return;
skew_err = pdc_rtc_read_nonvolatile(priv, PDC_RTC_SRPROT_SKEW,
(unsigned long *)&skew);
/* sanity check range of skew */
half = 1 << (PDC_RTC_SWPROT_SKEW_BITS - 1);
if (!skew_err && (skew >= half ||
skew < -half))
skew = 0;
dev_dbg(priv->dev, "last_time = %lx, skew = %lx (%d)\n",
last_time, skew, skew_err);
pdc_rtc_read_time(priv->dev, &tm);
rtc_tm_to_time(&tm, &now);
if (last_time && change->old_freq != CLK32K_DESIRED_FREQUENCY) {
diff = now - last_time;
if ((long)diff > 0) {
dev_dbg(priv->dev, "%#lx seconds since last change\n",
diff);
/* fixed point (using shift of SKEW_BITS) */
diff64 = div_u64((u64)(CLK32K_DESIRED_FREQUENCY
<< PDC_RTC_SWPROT_SKEW_BITS)
* diff, change->old_freq);
/* add 0.5 so we round to closest */
diff64 += half;
if (!skew_err) {
/* adjust using the accumulated clock skew */
diff64 += skew;
/*
* Update the skew from rounding the time to the
* nearest second.
*/
skew = diff64 &
((1 << PDC_RTC_SWPROT_SKEW_BITS) - 1);
skew -= half;
}
diff = diff64 >> PDC_RTC_SWPROT_SKEW_BITS;
adj = last_time + diff;
dev_dbg(priv->dev, "scaled to %#lx seconds\n",
diff);
if (adj != now) {
rtc_time_to_tm(adj, &tm);
pdc_rtc_set_time(priv->dev, &tm);
now = adj;
}
}
}
/* adjust the alarm */
spin_lock_irqsave(&priv->lock, flags);
orig_alrm_time = priv->alrm_time;
adj_alrm_time = priv->adj_alrm_time;
alrm_en = pdc_rtc_alarm_enabled(priv);
spin_unlock_irqrestore(&priv->lock, flags);
if (orig_alrm_time && alrm_en) {
if (change->new_freq == CLK32K_DESIRED_FREQUENCY) {
dev_dbg(priv->dev, "%#lx seconds until alarm\n",
orig_alrm_time - now);
alrm_time = orig_alrm_time;
} else {
diff = orig_alrm_time - now;
dev_dbg(priv->dev, "%#lx seconds until alarm\n",
diff);
/* scale time until alarm, rounding to closest second */
diff = (2 * diff * change->new_freq + 1)
/ (2 * CLK32K_DESIRED_FREQUENCY);
alrm_time = now + diff;
dev_dbg(priv->dev, "scaled to %#lx seconds\n",
diff);
}
if (adj_alrm_time != alrm_time) {
alrm_err = pdc_rtc_adjust_alarm_time(priv, alrm_time);
if (alrm_err == -ETIME) {
/* alarm has already expired, trigger */
dev_dbg(priv->dev,
"alarm expired during adjustment\n");
priv->alarm_pending = 1;
/* if suspended, postpone event until resume */
if (priv->suspended)
priv->postponed_rtc_int =
RTC_IRQF | RTC_AF;
else
rtc_update_irq(priv->rtc_dev, 1,
RTC_IRQF | RTC_AF);
}
}
}
dev_dbg(priv->dev, "writing now = %lx, skew = %lx (%d)\n",
now, skew, skew_err);
pdc_rtc_write_nonvolatile(priv, PDC_RTC_SRPROT_LASTTIME, now);
if (!skew_err)
pdc_rtc_write_nonvolatile(priv, PDC_RTC_SRPROT_SKEW, skew);
}
static int pdc_rtc_clk_notify(struct notifier_block *self, unsigned long action,
void *data)
{
struct pdc_rtc_priv *priv;
priv = container_of(self, struct pdc_rtc_priv, clk_nb);
switch (action) {
case CLK32K_CHANGE_FREQUENCY:
pdc_rtc_change_frequency(priv, data);
break;
default:
break;
}
return NOTIFY_OK;
}
static int pdc_rtc_setup(struct pdc_rtc_priv *priv)
{
unsigned long flags;
/* enable appropriate interrupts */
pdc_rtc_write(priv, PDC_RTC_IRQ_EN, PDC_RTC_IRQ_ALARM);
/* make sure clock is enabled */
spin_lock_irqsave(&priv->lock, flags);
priv->control_reg |= PDC_RTC_CONTROL_CE;
pdc_rtc_write(priv, PDC_RTC_CONTROL, priv->control_reg);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
}
static int pdc_rtc_probe(struct platform_device *pdev)
{
struct pdc_rtc_priv *priv;
struct resource *res_regs;
struct resource *res_nonvolatile;
struct device_node *node = pdev->dev.of_node;
int irq, ret, error;
u32 val;
if (!node)
return -ENOENT;
/* Get resources from platform device */
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "cannot find IRQ resource\n");
error = irq;
goto err_pdata;
}
res_regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
if (res_regs == NULL) {
dev_err(&pdev->dev, "cannot find registers resource\n");
error = -ENOENT;
goto err_pdata;
}
res_nonvolatile = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"nonvolatile");
/* nonvolatile registers are optional */
/* Private driver data */
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(&pdev->dev, "cannot allocate device data\n");
error = -ENOMEM;
goto err_dev;
}
spin_lock_init(&priv->lock);
platform_set_drvdata(pdev, priv);
priv->dev = &pdev->dev;
/* Get devicetree properties */
ret = of_property_read_u32(node, "time-set-delay", &val);
if (!ret)
priv->time_set_delay = val;
ret = of_property_read_u32(node, "alarm-irq-delay", &val);
if (!ret)
priv->alarm_irq_delay = val;
/* Ioremap the registers */
priv->reg_base = devm_ioremap(&pdev->dev, res_regs->start,
res_regs->end - res_regs->start);
if (!priv->reg_base) {
dev_err(&pdev->dev,
"cannot ioremap registers\n");
error = -EIO;
goto err_regs;
}
/* Ioremap the non-volatile registers if available */
if (res_nonvolatile) {
priv->nonvolatile_len = res_nonvolatile->end -
res_nonvolatile->start;
priv->nonvolatile_base = devm_ioremap(&pdev->dev,
res_nonvolatile->start,
priv->nonvolatile_len);
if (!priv->nonvolatile_base) {
dev_err(&pdev->dev,
"cannot ioremap nonvolatile registers\n");
error = -EIO;
goto err_regs;
}
}
/* disable interrupts */
pdc_rtc_write(priv, PDC_RTC_IRQ_EN, 0);
priv->softalrm_sec = -1;
priv->irq = irq;
error = devm_request_irq(&pdev->dev, priv->irq, pdc_rtc_isr, 0,
"pdc-rtc", priv);
if (error) {
dev_err(&pdev->dev, "cannot register IRQ %u\n",
priv->irq);
error = -EIO;
goto err_irq;
}
if (clk32k_bootfreq) {
/* Compensate for boot time frequency */
struct clk32k_change_freq change;
change.old_freq = clk32k_bootfreq;
change.new_freq = CLK32K_DESIRED_FREQUENCY;
pdc_rtc_change_frequency(priv, &change);
}
/* Register a clock notifier */
priv->clk_nb.notifier_call = pdc_rtc_clk_notify;
clk32k_register_notify(&priv->clk_nb);
/* Register our RTC with the RTC framework */
device_init_wakeup(&pdev->dev, 1);
priv->rtc_dev = rtc_device_register(pdev->name, &pdev->dev,
&pdc_rtc_ops,
THIS_MODULE);
if (unlikely(IS_ERR(priv->rtc_dev))) {
error = PTR_ERR(priv->rtc_dev);
goto err_rtc;
}
pdc_rtc_setup(priv);
return 0;
rtc_device_unregister(priv->rtc_dev);
err_rtc:
clk32k_unregister_notify(&priv->clk_nb);
err_irq:
err_regs:
err_dev:
err_pdata:
return error;
}
static int pdc_rtc_remove(struct platform_device *pdev)
{
struct pdc_rtc_priv *priv = platform_get_drvdata(pdev);
clk32k_unregister_notify(&priv->clk_nb);
rtc_device_unregister(priv->rtc_dev);
return 0;
}
#ifdef CONFIG_PM_SLEEP
/*
* We use noirq callbacks because an ISR after the normal callbacks can clear
* the secondly interrupt which would then get restored on resume and keep
* firing.
*/
static int pdc_rtc_suspend_noirq(struct device *dev)
{
struct pdc_rtc_priv *priv = dev_get_drvdata(dev);
unsigned int irq_en;
priv->suspended = true;
/* only wake if the alarm is enabled */
if (device_may_wakeup(dev) && pdc_rtc_alarm_enabled(priv)) {
/* disable interrupts other than the alarm */
irq_en = priv->last_irq_en = pdc_rtc_read(priv, PDC_RTC_IRQ_EN);
irq_en &= PDC_RTC_IRQ_ALARM;
pdc_rtc_write(priv, PDC_RTC_IRQ_EN, irq_en);
/* and enable wakeup on the interrupt */
enable_irq_wake(priv->irq);
priv->wakeup = true;
}
return 0;
}
static int pdc_rtc_resume_noirq(struct device *dev)
{
struct pdc_rtc_priv *priv = dev_get_drvdata(dev);
if (priv->wakeup) {
/* disable wakeup */
disable_irq_wake(priv->irq);
/* and restore the previous interrupt enable bits */
pdc_rtc_write(priv, PDC_RTC_IRQ_EN, priv->last_irq_en);
priv->wakeup = false;
}
/* submit any postponed rtc interrupt */
priv->suspended = false;
if (priv->postponed_rtc_int) {
dev_dbg(priv->dev,
"submitting postponed rtc interrupt %x\n",
priv->postponed_rtc_int);
rtc_update_irq(priv->rtc_dev, 1, priv->postponed_rtc_int);
priv->postponed_rtc_int = 0;
}
return 0;
}
#else
#define pdc_rtc_suspend NULL
#define pdc_rtc_resume NULL
#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops pdc_rtc_pmops = {
#ifdef CONFIG_PM_SLEEP
.suspend_noirq = pdc_rtc_suspend_noirq,
.resume_noirq = pdc_rtc_resume_noirq,
.freeze_noirq = pdc_rtc_suspend_noirq,
.thaw_noirq = pdc_rtc_resume_noirq,
.poweroff_noirq = pdc_rtc_suspend_noirq,
.restore_noirq = pdc_rtc_resume_noirq,
#endif
};
static const struct of_device_id pdc_rtc_match[] = {
{ .compatible = "img,pdc-rtc" },
{}
};
MODULE_DEVICE_TABLE(of, pdc_rtc_match);
static struct platform_driver pdc_rtc_driver = {
.driver = {
.name = "pdc-rtc",
.owner = THIS_MODULE,
.of_match_table = pdc_rtc_match,
.pm = &pdc_rtc_pmops,
},
.probe = pdc_rtc_probe,
.remove = pdc_rtc_remove,
};
module_platform_driver(pdc_rtc_driver);
MODULE_AUTHOR("Imagination Technologies Ltd.");
MODULE_DESCRIPTION("ImgTec PowerDown Controller RTC");
MODULE_LICENSE("GPL");