// SPDX-License-Identifier: GPL-2.0-or-later /* * Real Time Clock interface for Linux on Atmel AT91RM9200 * * Copyright (C) 2002 Rick Bronson * * Converted to RTC class model by Andrew Victor * * Ported to Linux 2.6 by Steven Scholz * Based on s3c2410-rtc.c Simtec Electronics * * Based on sa1100-rtc.c by Nils Faerber * Based on rtc.c by Paul Gortmaker
*/
#define AT91_RTC_TIMR 0x08 /* Time Register */ #define AT91_RTC_SEC GENMASK(6, 0) /* Current Second */ #define AT91_RTC_MIN GENMASK(14, 8) /* Current Minute */ #define AT91_RTC_HOUR GENMASK(21, 16) /* Current Hour */ #define AT91_RTC_AMPM BIT(22) /* Ante Meridiem Post Meridiem Indicator */
#define AT91_RTC_CALR 0x0c /* Calendar Register */ #define AT91_RTC_CENT GENMASK(6, 0) /* Current Century */ #define AT91_RTC_YEAR GENMASK(15, 8) /* Current Year */ #define AT91_RTC_MONTH GENMASK(20, 16) /* Current Month */ #define AT91_RTC_DAY GENMASK(23, 21) /* Current Day */ #define AT91_RTC_DATE GENMASK(29, 24) /* Current Date */
spin_lock_irqsave(&at91_rtc_lock, flags);
at91_rtc_write(AT91_RTC_IDR, mask); /* * Register read back (of any RTC-register) needed to make sure * IDR-register write has reached the peripheral before updating * shadow mask. * * Note that there is still a possibility that the mask is updated * before interrupts have actually been disabled in hardware. The only * way to be certain would be to poll the IMR-register, which is * the very register we are trying to emulate. The register read back * is a reasonable heuristic.
*/
at91_rtc_read(AT91_RTC_SR);
at91_rtc_shadow_imr &= ~mask;
spin_unlock_irqrestore(&at91_rtc_lock, flags);
}
/* * Decode time/date into rtc_time structure
*/ staticvoid at91_rtc_decodetime(unsignedint timereg, unsignedint calreg, struct rtc_time *tm)
{ unsignedint time, date;
/* must read twice in case it changes */ do {
time = at91_rtc_read(timereg);
date = at91_rtc_read(calreg);
} while ((time != at91_rtc_read(timereg)) ||
(date != at91_rtc_read(calreg)));
/* * The Calendar Alarm register does not have a field for * the year - so these will return an invalid value.
*/
tm->tm_year = bcd2bin(date & AT91_RTC_CENT) * 100; /* century */
tm->tm_year += bcd2bin(FIELD_GET(AT91_RTC_YEAR, date)); /* year */
tm->tm_wday = bcd2bin(FIELD_GET(AT91_RTC_DAY, date)) - 1; /* day of the week [0-6], Sunday=0 */
tm->tm_mon = bcd2bin(FIELD_GET(AT91_RTC_MONTH, date)) - 1;
tm->tm_mday = bcd2bin(FIELD_GET(AT91_RTC_DATE, date));
}
/* * Read current time and date in RTC
*/ staticint at91_rtc_readtime(struct device *dev, struct rtc_time *tm)
{
at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, tm);
tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
tm->tm_year = tm->tm_year - 1900;
dev_dbg(dev, "%s(): %ptR\n", __func__, tm);
return 0;
}
/* * Set current time and date in RTC
*/ staticint at91_rtc_settime(struct device *dev, struct rtc_time *tm)
{ unsignedlong cr;
/* offset less than 764 ppb, disable correction*/ if (offset < 764) {
at91_rtc_write(AT91_RTC_MR, mr & ~AT91_RTC_NEGPPM);
return 0;
}
/* * 29208 ppb is the perfect cutoff between low range and high range * low range values are never better than high range value after that.
*/ if (offset < 29208) {
corr = DIV_ROUND_CLOSEST(AT91_RTC_CORR_DIVIDEND, offset * AT91_RTC_CORR_LOW_RATIO);
} else {
corr = DIV_ROUND_CLOSEST(AT91_RTC_CORR_DIVIDEND, offset);
mr |= AT91_RTC_HIGHPPM;
}
if (corr > 128)
corr = 128;
mr |= FIELD_PREP(AT91_RTC_CORRECTION, corr - 1);
at91_rtc_write(AT91_RTC_MR, mr);
return 0;
}
/* * IRQ handler for the RTC
*/ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id)
{ struct platform_device *pdev = dev_id; struct rtc_device *rtc = platform_get_drvdata(pdev); unsignedint rtsr; unsignedlong events = 0; int ret = IRQ_NONE;
spin_lock(&suspended_lock);
rtsr = at91_rtc_read(AT91_RTC_SR) & at91_rtc_read_imr(); if (rtsr) { /* this interrupt is shared! Is it ours? */ if (rtsr & AT91_RTC_ALARM)
events |= (RTC_AF | RTC_IRQF); if (rtsr & AT91_RTC_SECEV) {
complete(&at91_rtc_upd_rdy);
at91_rtc_write_idr(AT91_RTC_SECEV);
} if (rtsr & AT91_RTC_ACKUPD)
complete(&at91_rtc_updated);
at91_rtc_write(AT91_RTC_SCCR, rtsr); /* clear status reg */
ret = devm_request_irq(&pdev->dev, irq, at91_rtc_interrupt,
IRQF_SHARED | IRQF_COND_SUSPEND, "at91_rtc", pdev); if (ret) {
dev_err(&pdev->dev, "IRQ %d already in use.\n", irq); goto err_clk;
}
/* cpu init code should really have flagged this device as * being wake-capable; if it didn't, do that here.
*/ if (!device_can_wakeup(&pdev->dev))
device_init_wakeup(&pdev->dev, true);
if (at91_rtc_config->has_correction)
rtc->ops = &sama5d4_rtc_ops; else
rtc->ops = &at91_rtc_ops;
rtc->range_min = RTC_TIMESTAMP_BEGIN_1900;
rtc->range_max = RTC_TIMESTAMP_END_2099;
ret = devm_rtc_register_device(rtc); if (ret) goto err_clk;
/* enable SECEV interrupt in order to initialize at91_rtc_upd_rdy * completion.
*/
at91_rtc_write_ier(AT91_RTC_SECEV);
dev_info(&pdev->dev, "AT91 Real Time Clock driver.\n"); return 0;
err_clk:
clk_disable_unprepare(sclk);
return ret;
}
/* * Disable and remove the RTC driver
*/ staticvoid __exit at91_rtc_remove(struct platform_device *pdev)
{ /* Disable all interrupts */
at91_rtc_write_idr(AT91_RTC_ACKUPD | AT91_RTC_ALARM |
AT91_RTC_SECEV | AT91_RTC_TIMEV |
AT91_RTC_CALEV);
staticint at91_rtc_suspend(struct device *dev)
{ /* this IRQ is shared with DBGU and other hardware which isn't * necessarily doing PM like we are...
*/
at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM);
at91_rtc_imr = at91_rtc_read_imr()
& (AT91_RTC_ALARM|AT91_RTC_SECEV); if (at91_rtc_imr) { if (device_may_wakeup(dev)) { unsignedlong flags;
/* * at91_rtc_remove() lives in .exit.text. For drivers registered via * module_platform_driver_probe() this is ok because they cannot get unbound at * runtime. So mark the driver struct with __refdata to prevent modpost * triggering a section mismatch warning.
*/ staticstruct platform_driver at91_rtc_driver __refdata = {
.remove = __exit_p(at91_rtc_remove),
.shutdown = at91_rtc_shutdown,
.driver = {
.name = "at91_rtc",
.pm = &at91_rtc_pm_ops,
.of_match_table = at91_rtc_dt_ids,
},
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.