Commit 107ccc45 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'rtc-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux

Pull RTC updates from Alexandre Belloni:
 "The broken down time conversion is similar to what is done in the time
  subsystem since v5.14. The rest is fairly straightforward.

  Subsystem:
   - Switch to Neri and Schneider time conversion algorithm

  Drivers:
   - rx8025: add rx8035 support
   - s5m: modernize driver and set range"

* tag 'rtc-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux:
  rtc: rx8010: select REGMAP_I2C
  dt-bindings: rtc: add Epson RX-8025 and RX-8035
  rtc: rx8025: implement RX-8035 support
  rtc: cmos: remove stale REVISIT comments
  rtc: tps65910: Correct driver module alias
  rtc: move RTC_LIB_KUNIT_TEST to proper location
  rtc: lib_test: add MODULE_LICENSE
  rtc: Improve performance of rtc_time64_to_tm(). Add tests.
  rtc: s5m: set range
  rtc: s5m: enable wakeup only when available
  rtc: s5m: signal the core when alarm are not available
  rtc: s5m: switch to devm_rtc_allocate_device
parents 52926229 0c45d3e2
...@@ -32,6 +32,9 @@ properties: ...@@ -32,6 +32,9 @@ properties:
- dallas,ds3232 - dallas,ds3232
# I2C-BUS INTERFACE REAL TIME CLOCK MODULE # I2C-BUS INTERFACE REAL TIME CLOCK MODULE
- epson,rx8010 - epson,rx8010
# I2C-BUS INTERFACE REAL TIME CLOCK MODULE
- epson,rx8025
- epson,rx8035
# I2C-BUS INTERFACE REAL TIME CLOCK MODULE with Battery Backed RAM # I2C-BUS INTERFACE REAL TIME CLOCK MODULE with Battery Backed RAM
- epson,rx8571 - epson,rx8571
# I2C-BUS INTERFACE REAL TIME CLOCK MODULE # I2C-BUS INTERFACE REAL TIME CLOCK MODULE
......
...@@ -75,6 +75,15 @@ config RTC_DEBUG ...@@ -75,6 +75,15 @@ config RTC_DEBUG
Say yes here to enable debugging support in the RTC framework Say yes here to enable debugging support in the RTC framework
and individual RTC drivers. and individual RTC drivers.
config RTC_LIB_KUNIT_TEST
tristate "KUnit test for RTC lib functions" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
help
Enable this option to test RTC library functions.
If unsure, say N.
config RTC_NVMEM config RTC_NVMEM
bool "RTC non volatile storage support" bool "RTC non volatile storage support"
select NVMEM select NVMEM
...@@ -624,6 +633,7 @@ config RTC_DRV_FM3130 ...@@ -624,6 +633,7 @@ config RTC_DRV_FM3130
config RTC_DRV_RX8010 config RTC_DRV_RX8010
tristate "Epson RX8010SJ" tristate "Epson RX8010SJ"
select REGMAP_I2C
help help
If you say yes here you get support for the Epson RX8010SJ RTC If you say yes here you get support for the Epson RX8010SJ RTC
chip. chip.
......
...@@ -15,6 +15,8 @@ rtc-core-$(CONFIG_RTC_INTF_DEV) += dev.o ...@@ -15,6 +15,8 @@ rtc-core-$(CONFIG_RTC_INTF_DEV) += dev.o
rtc-core-$(CONFIG_RTC_INTF_PROC) += proc.o rtc-core-$(CONFIG_RTC_INTF_PROC) += proc.o
rtc-core-$(CONFIG_RTC_INTF_SYSFS) += sysfs.o rtc-core-$(CONFIG_RTC_INTF_SYSFS) += sysfs.o
obj-$(CONFIG_RTC_LIB_KUNIT_TEST) += lib_test.o
# Keep the list ordered. # Keep the list ordered.
obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o obj-$(CONFIG_RTC_DRV_88PM80X) += rtc-88pm80x.o
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
* Author: Alessandro Zummo <a.zummo@towertech.it> * Author: Alessandro Zummo <a.zummo@towertech.it>
* *
* based on arch/arm/common/rtctime.c and other bits * based on arch/arm/common/rtctime.c and other bits
*
* Author: Cassio Neri <cassio.neri@gmail.com> (rtc_time64_to_tm)
*/ */
#include <linux/export.h> #include <linux/export.h>
...@@ -22,8 +24,6 @@ static const unsigned short rtc_ydays[2][13] = { ...@@ -22,8 +24,6 @@ static const unsigned short rtc_ydays[2][13] = {
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
}; };
#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
/* /*
* The number of days in the month. * The number of days in the month.
*/ */
...@@ -42,42 +42,95 @@ int rtc_year_days(unsigned int day, unsigned int month, unsigned int year) ...@@ -42,42 +42,95 @@ int rtc_year_days(unsigned int day, unsigned int month, unsigned int year)
} }
EXPORT_SYMBOL(rtc_year_days); EXPORT_SYMBOL(rtc_year_days);
/* /**
* rtc_time64_to_tm - Converts time64_t to rtc_time. * rtc_time64_to_tm - converts time64_t to rtc_time.
* Convert seconds since 01-01-1970 00:00:00 to Gregorian date. *
* @time: The number of seconds since 01-01-1970 00:00:00.
* (Must be positive.)
* @tm: Pointer to the struct rtc_time.
*/ */
void rtc_time64_to_tm(time64_t time, struct rtc_time *tm) void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
{ {
unsigned int month, year, secs; unsigned int secs;
int days; int days;
u64 u64tmp;
u32 u32tmp, udays, century, day_of_century, year_of_century, year,
day_of_year, month, day;
bool is_Jan_or_Feb, is_leap_year;
/* time must be positive */ /* time must be positive */
days = div_s64_rem(time, 86400, &secs); days = div_s64_rem(time, 86400, &secs);
/* day of the week, 1970-01-01 was a Thursday */ /* day of the week, 1970-01-01 was a Thursday */
tm->tm_wday = (days + 4) % 7; tm->tm_wday = (days + 4) % 7;
year = 1970 + days / 365; /*
days -= (year - 1970) * 365 * The following algorithm is, basically, Proposition 6.3 of Neri
+ LEAPS_THRU_END_OF(year - 1) * and Schneider [1]. In a few words: it works on the computational
- LEAPS_THRU_END_OF(1970 - 1); * (fictitious) calendar where the year starts in March, month = 2
while (days < 0) { * (*), and finishes in February, month = 13. This calendar is
year -= 1; * mathematically convenient because the day of the year does not
days += 365 + is_leap_year(year); * depend on whether the year is leap or not. For instance:
} *
tm->tm_year = year - 1900; * March 1st 0-th day of the year;
tm->tm_yday = days + 1; * ...
* April 1st 31-st day of the year;
for (month = 0; month < 11; month++) { * ...
int newdays; * January 1st 306-th day of the year; (Important!)
* ...
newdays = days - rtc_month_days(month, year); * February 28th 364-th day of the year;
if (newdays < 0) * February 29th 365-th day of the year (if it exists).
break; *
days = newdays; * After having worked out the date in the computational calendar
} * (using just arithmetics) it's easy to convert it to the
tm->tm_mon = month; * corresponding date in the Gregorian calendar.
tm->tm_mday = days + 1; *
* [1] "Euclidean Affine Functions and Applications to Calendar
* Algorithms". https://arxiv.org/abs/2102.06959
*
* (*) The numbering of months follows rtc_time more closely and
* thus, is slightly different from [1].
*/
udays = ((u32) days) + 719468;
u32tmp = 4 * udays + 3;
century = u32tmp / 146097;
day_of_century = u32tmp % 146097 / 4;
u32tmp = 4 * day_of_century + 3;
u64tmp = 2939745ULL * u32tmp;
year_of_century = upper_32_bits(u64tmp);
day_of_year = lower_32_bits(u64tmp) / 2939745 / 4;
year = 100 * century + year_of_century;
is_leap_year = year_of_century != 0 ?
year_of_century % 4 == 0 : century % 4 == 0;
u32tmp = 2141 * day_of_year + 132377;
month = u32tmp >> 16;
day = ((u16) u32tmp) / 2141;
/*
* Recall that January 01 is the 306-th day of the year in the
* computational (not Gregorian) calendar.
*/
is_Jan_or_Feb = day_of_year >= 306;
/* Converts to the Gregorian calendar. */
year = year + is_Jan_or_Feb;
month = is_Jan_or_Feb ? month - 12 : month;
day = day + 1;
day_of_year = is_Jan_or_Feb ?
day_of_year - 306 : day_of_year + 31 + 28 + is_leap_year;
/* Converts to rtc_time's format. */
tm->tm_year = (int) (year - 1900);
tm->tm_mon = (int) month;
tm->tm_mday = (int) day;
tm->tm_yday = (int) day_of_year + 1;
tm->tm_hour = secs / 3600; tm->tm_hour = secs / 3600;
secs -= tm->tm_hour * 3600; secs -= tm->tm_hour * 3600;
......
// SPDX-License-Identifier: LGPL-2.1+
#include <kunit/test.h>
#include <linux/rtc.h>
/*
* Advance a date by one day.
*/
static void advance_date(int *year, int *month, int *mday, int *yday)
{
if (*mday != rtc_month_days(*month - 1, *year)) {
++*mday;
++*yday;
return;
}
*mday = 1;
if (*month != 12) {
++*month;
++*yday;
return;
}
*month = 1;
*yday = 1;
++*year;
}
/*
* Checks every day in a 160000 years interval starting on 1970-01-01
* against the expected result.
*/
static void rtc_time64_to_tm_test_date_range(struct kunit *test)
{
/*
* 160000 years = (160000 / 400) * 400 years
* = (160000 / 400) * 146097 days
* = (160000 / 400) * 146097 * 86400 seconds
*/
time64_t total_secs = ((time64_t) 160000) / 400 * 146097 * 86400;
int year = 1970;
int month = 1;
int mday = 1;
int yday = 1;
struct rtc_time result;
time64_t secs;
s64 days;
for (secs = 0; secs <= total_secs; secs += 86400) {
rtc_time64_to_tm(secs, &result);
days = div_s64(secs, 86400);
#define FAIL_MSG "%d/%02d/%02d (%2d) : %ld", \
year, month, mday, yday, days
KUNIT_ASSERT_EQ_MSG(test, year - 1900, result.tm_year, FAIL_MSG);
KUNIT_ASSERT_EQ_MSG(test, month - 1, result.tm_mon, FAIL_MSG);
KUNIT_ASSERT_EQ_MSG(test, mday, result.tm_mday, FAIL_MSG);
KUNIT_ASSERT_EQ_MSG(test, yday, result.tm_yday, FAIL_MSG);
advance_date(&year, &month, &mday, &yday);
}
}
static struct kunit_case rtc_lib_test_cases[] = {
KUNIT_CASE(rtc_time64_to_tm_test_date_range),
{}
};
static struct kunit_suite rtc_lib_test_suite = {
.name = "rtc_lib_test_cases",
.test_cases = rtc_lib_test_cases,
};
kunit_test_suite(rtc_lib_test_suite);
MODULE_LICENSE("GPL");
...@@ -229,19 +229,13 @@ static int cmos_read_time(struct device *dev, struct rtc_time *t) ...@@ -229,19 +229,13 @@ static int cmos_read_time(struct device *dev, struct rtc_time *t)
if (!pm_trace_rtc_valid()) if (!pm_trace_rtc_valid())
return -EIO; return -EIO;
/* REVISIT: if the clock has a "century" register, use
* that instead of the heuristic in mc146818_get_time().
* That'll make Y3K compatility (year > 2070) easy!
*/
mc146818_get_time(t); mc146818_get_time(t);
return 0; return 0;
} }
static int cmos_set_time(struct device *dev, struct rtc_time *t) static int cmos_set_time(struct device *dev, struct rtc_time *t)
{ {
/* REVISIT: set the "century" register if available /* NOTE: this ignores the issue whereby updating the seconds
*
* NOTE: this ignores the issue whereby updating the seconds
* takes effect exactly 500ms after we write the register. * takes effect exactly 500ms after we write the register.
* (Also queueing and other delays before we get this far.) * (Also queueing and other delays before we get this far.)
*/ */
......
...@@ -60,14 +60,23 @@ ...@@ -60,14 +60,23 @@
#define RX8025_ADJ_DATA_MAX 62 #define RX8025_ADJ_DATA_MAX 62
#define RX8025_ADJ_DATA_MIN -62 #define RX8025_ADJ_DATA_MIN -62
enum rx_model {
model_rx_unknown,
model_rx_8025,
model_rx_8035,
model_last
};
static const struct i2c_device_id rx8025_id[] = { static const struct i2c_device_id rx8025_id[] = {
{ "rx8025", 0 }, { "rx8025", model_rx_8025 },
{ "rx8035", model_rx_8035 },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, rx8025_id); MODULE_DEVICE_TABLE(i2c, rx8025_id);
struct rx8025_data { struct rx8025_data {
struct rtc_device *rtc; struct rtc_device *rtc;
enum rx_model model;
u8 ctrl1; u8 ctrl1;
}; };
...@@ -100,10 +109,26 @@ static s32 rx8025_write_regs(const struct i2c_client *client, ...@@ -100,10 +109,26 @@ static s32 rx8025_write_regs(const struct i2c_client *client,
length, values); length, values);
} }
static int rx8025_is_osc_stopped(enum rx_model model, int ctrl2)
{
int xstp = ctrl2 & RX8025_BIT_CTRL2_XST;
/* XSTP bit has different polarity on RX-8025 vs RX-8035.
* RX-8025: 0 == oscillator stopped
* RX-8035: 1 == oscillator stopped
*/
if (model == model_rx_8025)
xstp = !xstp;
return xstp;
}
static int rx8025_check_validity(struct device *dev) static int rx8025_check_validity(struct device *dev)
{ {
struct i2c_client *client = to_i2c_client(dev); struct i2c_client *client = to_i2c_client(dev);
struct rx8025_data *drvdata = dev_get_drvdata(dev);
int ctrl2; int ctrl2;
int xstp;
ctrl2 = rx8025_read_reg(client, RX8025_REG_CTRL2); ctrl2 = rx8025_read_reg(client, RX8025_REG_CTRL2);
if (ctrl2 < 0) if (ctrl2 < 0)
...@@ -117,7 +142,8 @@ static int rx8025_check_validity(struct device *dev) ...@@ -117,7 +142,8 @@ static int rx8025_check_validity(struct device *dev)
return -EINVAL; return -EINVAL;
} }
if (!(ctrl2 & RX8025_BIT_CTRL2_XST)) { xstp = rx8025_is_osc_stopped(drvdata->model, ctrl2);
if (xstp) {
dev_warn(dev, "crystal stopped, date is invalid\n"); dev_warn(dev, "crystal stopped, date is invalid\n");
return -EINVAL; return -EINVAL;
} }
...@@ -127,6 +153,7 @@ static int rx8025_check_validity(struct device *dev) ...@@ -127,6 +153,7 @@ static int rx8025_check_validity(struct device *dev)
static int rx8025_reset_validity(struct i2c_client *client) static int rx8025_reset_validity(struct i2c_client *client)
{ {
struct rx8025_data *drvdata = i2c_get_clientdata(client);
int ctrl2 = rx8025_read_reg(client, RX8025_REG_CTRL2); int ctrl2 = rx8025_read_reg(client, RX8025_REG_CTRL2);
if (ctrl2 < 0) if (ctrl2 < 0)
...@@ -134,22 +161,28 @@ static int rx8025_reset_validity(struct i2c_client *client) ...@@ -134,22 +161,28 @@ static int rx8025_reset_validity(struct i2c_client *client)
ctrl2 &= ~(RX8025_BIT_CTRL2_PON | RX8025_BIT_CTRL2_VDET); ctrl2 &= ~(RX8025_BIT_CTRL2_PON | RX8025_BIT_CTRL2_VDET);
if (drvdata->model == model_rx_8025)
ctrl2 |= RX8025_BIT_CTRL2_XST;
else
ctrl2 &= ~(RX8025_BIT_CTRL2_XST);
return rx8025_write_reg(client, RX8025_REG_CTRL2, return rx8025_write_reg(client, RX8025_REG_CTRL2,
ctrl2 | RX8025_BIT_CTRL2_XST); ctrl2);
} }
static irqreturn_t rx8025_handle_irq(int irq, void *dev_id) static irqreturn_t rx8025_handle_irq(int irq, void *dev_id)
{ {
struct i2c_client *client = dev_id; struct i2c_client *client = dev_id;
struct rx8025_data *rx8025 = i2c_get_clientdata(client); struct rx8025_data *rx8025 = i2c_get_clientdata(client);
int status; int status, xstp;
rtc_lock(rx8025->rtc); rtc_lock(rx8025->rtc);
status = rx8025_read_reg(client, RX8025_REG_CTRL2); status = rx8025_read_reg(client, RX8025_REG_CTRL2);
if (status < 0) if (status < 0)
goto out; goto out;
if (!(status & RX8025_BIT_CTRL2_XST)) xstp = rx8025_is_osc_stopped(rx8025->model, status);
if (xstp)
dev_warn(&client->dev, "Oscillation stop was detected," dev_warn(&client->dev, "Oscillation stop was detected,"
"you may have to readjust the clock\n"); "you may have to readjust the clock\n");
...@@ -519,6 +552,9 @@ static int rx8025_probe(struct i2c_client *client, ...@@ -519,6 +552,9 @@ static int rx8025_probe(struct i2c_client *client,
i2c_set_clientdata(client, rx8025); i2c_set_clientdata(client, rx8025);
if (id)
rx8025->model = id->driver_data;
err = rx8025_init_client(client); err = rx8025_init_client(client);
if (err) if (err)
return err; return err;
......
...@@ -204,15 +204,9 @@ static int s5m8767_tm_to_data(struct rtc_time *tm, u8 *data) ...@@ -204,15 +204,9 @@ static int s5m8767_tm_to_data(struct rtc_time *tm, u8 *data)
data[RTC_WEEKDAY] = 1 << tm->tm_wday; data[RTC_WEEKDAY] = 1 << tm->tm_wday;
data[RTC_DATE] = tm->tm_mday; data[RTC_DATE] = tm->tm_mday;
data[RTC_MONTH] = tm->tm_mon + 1; data[RTC_MONTH] = tm->tm_mon + 1;
data[RTC_YEAR1] = tm->tm_year > 100 ? (tm->tm_year - 100) : 0; data[RTC_YEAR1] = tm->tm_year - 100;
if (tm->tm_year < 100) { return 0;
pr_err("RTC cannot handle the year %d\n",
1900 + tm->tm_year);
return -EINVAL;
} else {
return 0;
}
} }
/* /*
...@@ -786,29 +780,35 @@ static int s5m_rtc_probe(struct platform_device *pdev) ...@@ -786,29 +780,35 @@ static int s5m_rtc_probe(struct platform_device *pdev)
if (ret) if (ret)
return ret; return ret;
device_init_wakeup(&pdev->dev, 1); info->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
info->rtc_dev = devm_rtc_device_register(&pdev->dev, "s5m-rtc",
&s5m_rtc_ops, THIS_MODULE);
if (IS_ERR(info->rtc_dev)) if (IS_ERR(info->rtc_dev))
return PTR_ERR(info->rtc_dev); return PTR_ERR(info->rtc_dev);
if (!info->irq) { info->rtc_dev->ops = &s5m_rtc_ops;
dev_info(&pdev->dev, "Alarm IRQ not available\n");
return 0; if (info->device_type == S5M8763X) {
info->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_0000;
info->rtc_dev->range_max = RTC_TIMESTAMP_END_9999;
} else {
info->rtc_dev->range_min = RTC_TIMESTAMP_BEGIN_2000;
info->rtc_dev->range_max = RTC_TIMESTAMP_END_2099;
} }
ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, if (!info->irq) {
s5m_rtc_alarm_irq, 0, "rtc-alarm0", clear_bit(RTC_FEATURE_ALARM, info->rtc_dev->features);
info); } else {
if (ret < 0) { ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL,
dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n", s5m_rtc_alarm_irq, 0, "rtc-alarm0",
info->irq, ret); info);
return ret; if (ret < 0) {
dev_err(&pdev->dev, "Failed to request alarm IRQ: %d: %d\n",
info->irq, ret);
return ret;
}
device_init_wakeup(&pdev->dev, 1);
} }
return 0; return devm_rtc_register_device(info->rtc_dev);
} }
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
......
...@@ -467,6 +467,6 @@ static struct platform_driver tps65910_rtc_driver = { ...@@ -467,6 +467,6 @@ static struct platform_driver tps65910_rtc_driver = {
}; };
module_platform_driver(tps65910_rtc_driver); module_platform_driver(tps65910_rtc_driver);
MODULE_ALIAS("platform:rtc-tps65910"); MODULE_ALIAS("platform:tps65910-rtc");
MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>"); MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment