RTC for 6.16

Core:
  - support negative offsets for RTCs that have shipped with an epoch earlier
    than 1970
 
 New drivers:
  - NXP S32G2/S32G3
  - Sophgo CV1800
 
 Drivers:
  - loongson: fix missing alarm notifications for ACPI
  - m41t80: kickstart ocillator upon failure
  - mt6359: mt6357 support
  - pcf8563: fix wrong alarm register
  - sh: cleanups
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmhBYI8ACgkQY6TcMGxw
 OjLElBAAi/6D+9+EEkmsaG9JJPdqj4QH7B7XUpgigu0aADMVdkFQpU/gyJJP36Ct
 5x9xl2/VnZ7g36TmX+f7fpcQigs40y6jEvekbSIBiLf0GsX5cT5l4949QxzmJ8WI
 lq8LZZpIUDJvYAFXuf+jdnDdbJwrJSe7gDiaqvNjIzkzYrmSFEx4145VnFmtCBZ3
 LWpKwKn3H9sJ2Par8Nbq7QVPlxaQFrfYxqglrF4enChAN14Pou/PPnemYTIEGdZ1
 uT6zw797bDcZfNFv15XGlmQ9MokPLQI4/N9wumrwDsHJoiYh4uM+yo2L2dBLEIPj
 8meE419oQbGYtuRac1+PsyrXbB03gRIhHrSOhx7sT3YEeQsXBYKfX64D6giGIp8b
 +DDcvl7OimE3ILDuFPF2KgqFp7e6Z4uC3YqKJS/BH6F/CXQ787VFdMe/AKxc1gcd
 9cNxjz4xU3PpZ6fd9SPiXtTuLQ20z3tcdZuGtlo25mFJcXINdmnPibrd2oWb8Bpd
 3SGzDV6fzPF9lTPxbDdKD3M69qFpFEA7iwA+b0GvwofFRf5Fs5ysllx2JA9dN+GM
 KVgwnT4q6zs6e0EulLqrzSfj2DXFC99yjBNDc5itsho79FTflUlLZtVuwiopvorA
 G8RX6svP6Qa8aVtyWYoOiJpNP+i+VbOyEcQAWyCLuuhsz3Np+K8=
 =Xxip
 -----END PGP SIGNATURE-----

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

Pull RTC updates from Alexandre Belloni:
 "There are two new drivers this cycle. There is also support for a
  negative offset for RTCs that have been shipped with a date set using
  an epoch that is before 1970. This unfortunately happens with some
  products that ship with a vendor kernel and an out of tree driver.

  Core:
   - support negative offsets for RTCs that have shipped with an epoch
     earlier than 1970

  New drivers:
   - NXP S32G2/S32G3
   - Sophgo CV1800

  Drivers:
   - loongson: fix missing alarm notifications for ACPI
   - m41t80: kickstart ocillator upon failure
   - mt6359: mt6357 support
   - pcf8563: fix wrong alarm register
   - sh: cleanups"

* tag 'rtc-6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (39 commits)
  rtc: mt6359: Add mt6357 support
  rtc: test: Test date conversion for dates starting in 1900
  rtc: test: Also test time and wday outcome of rtc_time64_to_tm()
  rtc: test: Emit the seconds-since-1970 value instead of days-since-1970
  rtc: Fix offset calculation for .start_secs < 0
  rtc: Make rtc_time64_to_tm() support dates before 1970
  rtc: pcf8563: fix wrong alarm register
  rtc: rzn1: support input frequencies other than 32768Hz
  rtc: rzn1: Disable controller before initialization
  dt-bindings: rtc: rzn1: add optional second clock
  rtc: m41t80: reduce verbosity
  rtc: m41t80: kickstart ocillator upon failure
  rtc: s32g: add NXP S32G2/S32G3 SoC support
  dt-bindings: rtc: add schema for NXP S32G2/S32G3 SoCs
  dt-bindings: at91rm9260-rtt: add microchip,sama7d65-rtt
  dt-bindings: rtc: at91rm9200: add microchip,sama7d65-rtc
  rtc: loongson: Add missing alarm notifications for ACPI RTC events
  rtc: sophgo: add rtc support for Sophgo CV1800 SoC
  rtc: stm32: drop unused module alias
  rtc: s3c: drop unused module alias
  ...
This commit is contained in:
Linus Torvalds 2025-06-05 08:54:47 -07:00
commit 7fdaba9129
26 changed files with 946 additions and 329 deletions

View file

@ -23,7 +23,9 @@ properties:
- microchip,sam9x60-rtc
- microchip,sama7g5-rtc
- items:
- const: microchip,sam9x7-rtc
- enum:
- microchip,sam9x7-rtc
- microchip,sama7d65-rtc
- const: microchip,sam9x60-rtc
reg:

View file

@ -22,6 +22,7 @@ properties:
- enum:
- microchip,sam9x60-rtt
- microchip,sam9x7-rtt
- microchip,sama7d65-rtt
- const: atmel,at91sam9260-rtt
- items:
- const: microchip,sama7g5-rtt

View file

@ -0,0 +1,72 @@
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/nxp,s32g-rtc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP S32G2/S32G3 Real Time Clock (RTC)
maintainers:
- Bogdan Hamciuc <bogdan.hamciuc@nxp.com>
- Ciprian Marian Costea <ciprianmarian.costea@nxp.com>
description:
RTC hardware module present on S32G2/S32G3 SoCs is used as a wakeup source.
It is not kept alive during system reset and it is not battery-powered.
allOf:
- $ref: rtc.yaml#
properties:
compatible:
oneOf:
- enum:
- nxp,s32g2-rtc
- items:
- const: nxp,s32g3-rtc
- const: nxp,s32g2-rtc
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
items:
- description: ipg clock drives the access to the RTC iomapped registers
- description: Clock source for the RTC module. Can be selected between
4 different clock sources using an integrated hardware mux.
On S32G2/S32G3 SoCs, 'source0' is the SIRC clock (~32KHz) and it is
available during standby and runtime. 'source1' is reserved and cannot
be used. 'source2' is the FIRC clock and it is only available during
runtime providing a better resolution (~48MHz). 'source3' is an external
RTC clock source which can be additionally added in hardware.
clock-names:
items:
- const: ipg
- enum: [ source0, source1, source2, source3 ]
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
rtc@40060000 {
compatible = "nxp,s32g3-rtc",
"nxp,s32g2-rtc";
reg = <0x40060000 0x1000>;
interrupts = <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks 54>, <&clks 55>;
clock-names = "ipg", "source0";
};

View file

@ -55,6 +55,12 @@ properties:
description:
RTC alarm is not owned by the OS
qcom,uefi-rtc-info:
type: boolean
description:
RTC offset is stored as a four-byte GPS time offset in a 12-byte UEFI
variable 882f8c2b-9646-435f-8de5-f208ff80c1bd-RTCInfo
wakeup-source: true
required:

View file

@ -33,10 +33,14 @@ properties:
- const: pps
clocks:
maxItems: 1
minItems: 1
maxItems: 2
clock-names:
const: hclk
minItems: 1
items:
- const: hclk
- const: xtal
power-domains:
maxItems: 1

View file

@ -1388,6 +1388,18 @@ config RTC_DRV_ASM9260
This driver can also be built as a module. If so, the module
will be called rtc-asm9260.
config RTC_DRV_CV1800
tristate "Sophgo CV1800 RTC"
depends on SOPHGO_CV1800_RTCSYS || COMPILE_TEST
select MFD_SYSCON
select REGMAP
help
If you say yes here you get support the RTC driver for Sophgo CV1800
series SoC.
This driver can also be built as a module. If so, the module will be
called rtc-cv1800.
config RTC_DRV_DIGICOLOR
tristate "Conexant Digicolor RTC"
depends on ARCH_DIGICOLOR || COMPILE_TEST
@ -2088,7 +2100,7 @@ config RTC_DRV_AMLOGIC_A4
tristate "Amlogic RTC"
depends on ARCH_MESON || COMPILE_TEST
select REGMAP_MMIO
default y
default ARCH_MESON
help
If you say yes here you get support for the RTC block on the
Amlogic A113L2(A4) and A113X2(A5) SoCs.
@ -2096,4 +2108,15 @@ config RTC_DRV_AMLOGIC_A4
This driver can also be built as a module. If so, the module
will be called "rtc-amlogic-a4".
config RTC_DRV_S32G
tristate "RTC driver for S32G2/S32G3 SoCs"
depends on ARCH_S32 || COMPILE_TEST
depends on COMMON_CLK
help
Say yes to enable RTC driver for platforms based on the
S32G2/S32G3 SoC family.
This RTC module can be used as a wakeup source.
Please note that it is not battery-powered.
endif # RTC_CLASS

View file

@ -44,6 +44,7 @@ obj-$(CONFIG_RTC_DRV_CADENCE) += rtc-cadence.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_CPCAP) += rtc-cpcap.o
obj-$(CONFIG_RTC_DRV_CROS_EC) += rtc-cros-ec.o
obj-$(CONFIG_RTC_DRV_CV1800) += rtc-cv1800.o
obj-$(CONFIG_RTC_DRV_DA9052) += rtc-da9052.o
obj-$(CONFIG_RTC_DRV_DA9055) += rtc-da9055.o
obj-$(CONFIG_RTC_DRV_DA9063) += rtc-da9063.o
@ -160,6 +161,7 @@ obj-$(CONFIG_RTC_DRV_RX8111) += rtc-rx8111.o
obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o
obj-$(CONFIG_RTC_DRV_RZN1) += rtc-rzn1.o
obj-$(CONFIG_RTC_DRV_RENESAS_RTCA3) += rtc-renesas-rtca3.o
obj-$(CONFIG_RTC_DRV_S32G) += rtc-s32g.o
obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o

View file

@ -326,7 +326,7 @@ static void rtc_device_get_offset(struct rtc_device *rtc)
*
* Otherwise the offset seconds should be 0.
*/
if (rtc->start_secs > rtc->range_max ||
if ((rtc->start_secs >= 0 && rtc->start_secs > rtc->range_max) ||
rtc->start_secs + range_secs - 1 < rtc->range_min)
rtc->offset_secs = rtc->start_secs - rtc->range_min;
else if (rtc->start_secs > rtc->range_min)

View file

@ -205,7 +205,7 @@ static int rtc_read_alarm_internal(struct rtc_device *rtc,
mutex_unlock(&rtc->ops_lock);
trace_rtc_read_alarm(rtc_tm_to_time64(&alarm->time), err);
trace_rtc_read_alarm(err?0:rtc_tm_to_time64(&alarm->time), err);
return err;
}

View file

@ -46,24 +46,38 @@ EXPORT_SYMBOL(rtc_year_days);
* rtc_time64_to_tm - converts time64_t to rtc_time.
*
* @time: The number of seconds since 01-01-1970 00:00:00.
* (Must be positive.)
* Works for values since at least 1900
* @tm: Pointer to the struct rtc_time.
*/
void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
{
unsigned int secs;
int days;
int days, secs;
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 */
/*
* Get days and seconds while preserving the sign to
* handle negative time values (dates before 1970-01-01)
*/
days = div_s64_rem(time, 86400, &secs);
/*
* We need 0 <= secs < 86400 which isn't given for negative
* values of time. Fixup accordingly.
*/
if (secs < 0) {
days -= 1;
secs += 86400;
}
/* day of the week, 1970-01-01 was a Thursday */
tm->tm_wday = (days + 4) % 7;
/* Ensure tm_wday is always positive */
if (tm->tm_wday < 0)
tm->tm_wday += 7;
/*
* The following algorithm is, basically, Proposition 6.3 of Neri
@ -93,7 +107,7 @@ void rtc_time64_to_tm(time64_t time, struct rtc_time *tm)
* thus, is slightly different from [1].
*/
udays = ((u32) days) + 719468;
udays = days + 719468;
u32tmp = 4 * udays + 3;
century = u32tmp / 146097;

View file

@ -6,8 +6,10 @@
/*
* Advance a date by one day.
*/
static void advance_date(int *year, int *month, int *mday, int *yday)
static void advance_date(int *year, int *month, int *mday, int *yday, int *wday)
{
*wday = (*wday + 1) % 7;
if (*mday != rtc_month_days(*month - 1, *year)) {
++*mday;
++*yday;
@ -39,35 +41,38 @@ static void rtc_time64_to_tm_test_date_range(struct kunit *test, int years)
*/
time64_t total_secs = ((time64_t)years) / 400 * 146097 * 86400;
int year = 1970;
int year = 1900;
int month = 1;
int mday = 1;
int yday = 1;
int wday = 1; /* Jan 1st 1900 was a Monday */
struct rtc_time result;
time64_t secs;
s64 days;
const time64_t sec_offset = RTC_TIMESTAMP_BEGIN_1900 + ((1 * 60) + 2) * 60 + 3;
for (secs = 0; secs <= total_secs; secs += 86400) {
rtc_time64_to_tm(secs, &result);
rtc_time64_to_tm(secs + sec_offset, &result);
days = div_s64(secs, 86400);
#define FAIL_MSG "%d/%02d/%02d (%2d) : %lld", \
year, month, mday, yday, days
#define FAIL_MSG "%d/%02d/%02d (%2d, %d) : %lld", \
year, month, mday, yday, wday, secs + sec_offset
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);
KUNIT_ASSERT_EQ_MSG(test, 1, result.tm_hour, FAIL_MSG);
KUNIT_ASSERT_EQ_MSG(test, 2, result.tm_min, FAIL_MSG);
KUNIT_ASSERT_EQ_MSG(test, 3, result.tm_sec, FAIL_MSG);
KUNIT_ASSERT_EQ_MSG(test, wday, result.tm_wday, FAIL_MSG);
advance_date(&year, &month, &mday, &yday);
advance_date(&year, &month, &mday, &yday, &wday);
}
}
/*
* Checks every day in a 160000 years interval starting on 1970-01-01
* Checks every day in a 160000 years interval starting on 1900-01-01
* against the expected result.
*/
static void rtc_time64_to_tm_test_date_range_160000(struct kunit *test)
@ -76,7 +81,7 @@ static void rtc_time64_to_tm_test_date_range_160000(struct kunit *test)
}
/*
* Checks every day in a 1000 years interval starting on 1970-01-01
* Checks every day in a 1000 years interval starting on 1900-01-01
* against the expected result.
*/
static void rtc_time64_to_tm_test_date_range_1000(struct kunit *test)

View file

@ -654,4 +654,3 @@ module_platform_driver_probe(at91_rtc_driver, at91_rtc_probe);
MODULE_AUTHOR("Rick Bronson");
MODULE_DESCRIPTION("RTC driver for Atmel AT91RM9200");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:at91_rtc");

View file

@ -320,7 +320,6 @@ static struct platform_driver cpcap_rtc_driver = {
module_platform_driver(cpcap_rtc_driver);
MODULE_ALIAS("platform:cpcap-rtc");
MODULE_DESCRIPTION("CPCAP RTC driver");
MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
MODULE_LICENSE("GPL");

218
drivers/rtc/rtc-cv1800.c Normal file
View file

@ -0,0 +1,218 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* rtc-cv1800.c: RTC driver for Sophgo cv1800 RTC
*
* Author: Jingbao Qiu <qiujingbao.dlmu@gmail.com>
*/
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/rtc.h>
#define SEC_PULSE_GEN 0x1004
#define ALARM_TIME 0x1008
#define ALARM_ENABLE 0x100C
#define SET_SEC_CNTR_VAL 0x1010
#define SET_SEC_CNTR_TRIG 0x1014
#define SEC_CNTR_VAL 0x1018
/*
* When in VDDBKUP domain, this MACRO register
* does not power down
*/
#define MACRO_RO_T 0x14A8
#define MACRO_RG_SET_T 0x1498
#define ALARM_ENABLE_MASK BIT(0)
#define SEL_SEC_PULSE BIT(31)
struct cv1800_rtc_priv {
struct rtc_device *rtc_dev;
struct regmap *rtc_map;
struct clk *clk;
int irq;
};
static bool cv1800_rtc_enabled(struct device *dev)
{
struct cv1800_rtc_priv *info = dev_get_drvdata(dev);
u32 reg;
regmap_read(info->rtc_map, SEC_PULSE_GEN, &reg);
return (reg & SEL_SEC_PULSE) == 0;
}
static void cv1800_rtc_enable(struct device *dev)
{
struct cv1800_rtc_priv *info = dev_get_drvdata(dev);
/* Sec pulse generated internally */
regmap_update_bits(info->rtc_map, SEC_PULSE_GEN, SEL_SEC_PULSE, 0);
}
static int cv1800_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct cv1800_rtc_priv *info = dev_get_drvdata(dev);
regmap_write(info->rtc_map, ALARM_ENABLE, enabled);
return 0;
}
static int cv1800_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct cv1800_rtc_priv *info = dev_get_drvdata(dev);
unsigned long alarm_time;
alarm_time = rtc_tm_to_time64(&alrm->time);
cv1800_rtc_alarm_irq_enable(dev, 0);
regmap_write(info->rtc_map, ALARM_TIME, alarm_time);
cv1800_rtc_alarm_irq_enable(dev, alrm->enabled);
return 0;
}
static int cv1800_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct cv1800_rtc_priv *info = dev_get_drvdata(dev);
u32 enabled;
u32 time;
if (!cv1800_rtc_enabled(dev)) {
alarm->enabled = 0;
return 0;
}
regmap_read(info->rtc_map, ALARM_ENABLE, &enabled);
alarm->enabled = enabled & ALARM_ENABLE_MASK;
regmap_read(info->rtc_map, ALARM_TIME, &time);
rtc_time64_to_tm(time, &alarm->time);
return 0;
}
static int cv1800_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct cv1800_rtc_priv *info = dev_get_drvdata(dev);
u32 sec;
if (!cv1800_rtc_enabled(dev))
return -EINVAL;
regmap_read(info->rtc_map, SEC_CNTR_VAL, &sec);
rtc_time64_to_tm(sec, tm);
return 0;
}
static int cv1800_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct cv1800_rtc_priv *info = dev_get_drvdata(dev);
unsigned long sec;
sec = rtc_tm_to_time64(tm);
regmap_write(info->rtc_map, SET_SEC_CNTR_VAL, sec);
regmap_write(info->rtc_map, SET_SEC_CNTR_TRIG, 1);
regmap_write(info->rtc_map, MACRO_RG_SET_T, sec);
cv1800_rtc_enable(dev);
return 0;
}
static irqreturn_t cv1800_rtc_irq_handler(int irq, void *dev_id)
{
struct cv1800_rtc_priv *info = dev_id;
rtc_update_irq(info->rtc_dev, 1, RTC_IRQF | RTC_AF);
regmap_write(info->rtc_map, ALARM_ENABLE, 0);
return IRQ_HANDLED;
}
static const struct rtc_class_ops cv1800_rtc_ops = {
.read_time = cv1800_rtc_read_time,
.set_time = cv1800_rtc_set_time,
.read_alarm = cv1800_rtc_read_alarm,
.set_alarm = cv1800_rtc_set_alarm,
.alarm_irq_enable = cv1800_rtc_alarm_irq_enable,
};
static int cv1800_rtc_probe(struct platform_device *pdev)
{
struct cv1800_rtc_priv *rtc;
int ret;
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return -ENOMEM;
rtc->rtc_map = device_node_to_regmap(pdev->dev.parent->of_node);
if (IS_ERR(rtc->rtc_map))
return dev_err_probe(&pdev->dev, PTR_ERR(rtc->rtc_map),
"cannot get parent regmap\n");
rtc->irq = platform_get_irq(pdev, 0);
if (rtc->irq < 0)
return rtc->irq;
rtc->clk = devm_clk_get_enabled(pdev->dev.parent, "rtc");
if (IS_ERR(rtc->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(rtc->clk),
"rtc clk not found\n");
platform_set_drvdata(pdev, rtc);
device_init_wakeup(&pdev->dev, 1);
rtc->rtc_dev = devm_rtc_allocate_device(&pdev->dev);
if (IS_ERR(rtc->rtc_dev))
return PTR_ERR(rtc->rtc_dev);
rtc->rtc_dev->ops = &cv1800_rtc_ops;
rtc->rtc_dev->range_max = U32_MAX;
ret = devm_request_irq(&pdev->dev, rtc->irq, cv1800_rtc_irq_handler,
IRQF_TRIGGER_HIGH, "rtc alarm", rtc);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"cannot register interrupt handler\n");
return devm_rtc_register_device(rtc->rtc_dev);
}
static const struct platform_device_id cv1800_rtc_id[] = {
{ .name = "cv1800b-rtc" },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(platform, cv1800_rtc_id);
static struct platform_driver cv1800_rtc_driver = {
.driver = {
.name = "sophgo-cv1800-rtc",
},
.probe = cv1800_rtc_probe,
.id_table = cv1800_rtc_id,
};
module_platform_driver(cv1800_rtc_driver);
MODULE_AUTHOR("Jingbao Qiu");
MODULE_DESCRIPTION("Sophgo cv1800 RTC Driver");
MODULE_LICENSE("GPL");

View file

@ -194,26 +194,17 @@ static void da9063_tm_to_data(struct rtc_time *tm, u8 *data,
config->rtc_count_year_mask;
}
static int da9063_rtc_stop_alarm(struct device *dev)
static int da9063_rtc_alarm_irq_enable(struct device *dev,
unsigned int enabled)
{
struct da9063_compatible_rtc *rtc = dev_get_drvdata(dev);
const struct da9063_compatible_rtc_regmap *config = rtc->config;
u8 set_bit = enabled ? config->rtc_alarm_on_mask : 0;
return regmap_update_bits(rtc->regmap,
config->rtc_alarm_year_reg,
config->rtc_alarm_on_mask,
0);
}
static int da9063_rtc_start_alarm(struct device *dev)
{
struct da9063_compatible_rtc *rtc = dev_get_drvdata(dev);
const struct da9063_compatible_rtc_regmap *config = rtc->config;
return regmap_update_bits(rtc->regmap,
config->rtc_alarm_year_reg,
config->rtc_alarm_on_mask,
config->rtc_alarm_on_mask);
set_bit);
}
static int da9063_rtc_read_time(struct device *dev, struct rtc_time *tm)
@ -312,7 +303,7 @@ static int da9063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
da9063_tm_to_data(&alrm->time, data, rtc);
ret = da9063_rtc_stop_alarm(dev);
ret = da9063_rtc_alarm_irq_enable(dev, 0);
if (ret < 0) {
dev_err(dev, "Failed to stop alarm: %d\n", ret);
return ret;
@ -330,7 +321,7 @@ static int da9063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
da9063_data_to_tm(data, &rtc->alarm_time, rtc);
if (alrm->enabled) {
ret = da9063_rtc_start_alarm(dev);
ret = da9063_rtc_alarm_irq_enable(dev, 1);
if (ret < 0) {
dev_err(dev, "Failed to start alarm: %d\n", ret);
return ret;
@ -340,15 +331,6 @@ static int da9063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
return ret;
}
static int da9063_rtc_alarm_irq_enable(struct device *dev,
unsigned int enabled)
{
if (enabled)
return da9063_rtc_start_alarm(dev);
else
return da9063_rtc_stop_alarm(dev);
}
static irqreturn_t da9063_alarm_event(int irq, void *data)
{
struct da9063_compatible_rtc *rtc = data;
@ -513,4 +495,3 @@ module_platform_driver(da9063_rtc_driver);
MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
MODULE_DESCRIPTION("Real time clock device driver for Dialog DA9063");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DA9063_DRVNAME_RTC);

View file

@ -437,4 +437,3 @@ module_platform_driver(jz4740_rtc_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n");
MODULE_ALIAS("platform:jz4740-rtc");

View file

@ -129,6 +129,14 @@ static u32 loongson_rtc_handler(void *id)
{
struct loongson_rtc_priv *priv = (struct loongson_rtc_priv *)id;
rtc_update_irq(priv->rtcdev, 1, RTC_AF | RTC_IRQF);
/*
* The TOY_MATCH0_REG should be cleared 0 here,
* otherwise the interrupt cannot be cleared.
*/
regmap_write(priv->regmap, TOY_MATCH0_REG, 0);
spin_lock(&priv->lock);
/* Disable RTC alarm wakeup and interrupt */
writel(readl(priv->pm_base + PM1_EN_REG) & ~RTC_EN,

View file

@ -22,6 +22,7 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/string.h>
#include <linux/delay.h>
#ifdef CONFIG_RTC_DRV_M41T80_WDT
#include <linux/fs.h>
#include <linux/ioctl.h>
@ -204,14 +205,14 @@ static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm)
return flags;
if (flags & M41T80_FLAGS_OF) {
dev_err(&client->dev, "Oscillator failure, data is invalid.\n");
dev_err(&client->dev, "Oscillator failure, time may not be accurate, write time to RTC to fix it.\n");
return -EINVAL;
}
err = i2c_smbus_read_i2c_block_data(client, M41T80_REG_SSEC,
sizeof(buf), buf);
if (err < 0) {
dev_err(&client->dev, "Unable to read date\n");
dev_dbg(&client->dev, "Unable to read date\n");
return err;
}
@ -227,21 +228,31 @@ static int m41t80_rtc_read_time(struct device *dev, struct rtc_time *tm)
return 0;
}
static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm)
static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *in_tm)
{
struct i2c_client *client = to_i2c_client(dev);
struct m41t80_data *clientdata = i2c_get_clientdata(client);
struct rtc_time tm = *in_tm;
unsigned char buf[8];
int err, flags;
time64_t time = 0;
flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
if (flags < 0)
return flags;
if (flags & M41T80_FLAGS_OF) {
/* add 4sec of oscillator stablize time otherwise we are behind 4sec */
time = rtc_tm_to_time64(&tm);
rtc_time64_to_tm(time + 4, &tm);
}
buf[M41T80_REG_SSEC] = 0;
buf[M41T80_REG_SEC] = bin2bcd(tm->tm_sec);
buf[M41T80_REG_MIN] = bin2bcd(tm->tm_min);
buf[M41T80_REG_HOUR] = bin2bcd(tm->tm_hour);
buf[M41T80_REG_DAY] = bin2bcd(tm->tm_mday);
buf[M41T80_REG_MON] = bin2bcd(tm->tm_mon + 1);
buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year - 100);
buf[M41T80_REG_WDAY] = tm->tm_wday;
buf[M41T80_REG_SEC] = bin2bcd(tm.tm_sec);
buf[M41T80_REG_MIN] = bin2bcd(tm.tm_min);
buf[M41T80_REG_HOUR] = bin2bcd(tm.tm_hour);
buf[M41T80_REG_DAY] = bin2bcd(tm.tm_mday);
buf[M41T80_REG_MON] = bin2bcd(tm.tm_mon + 1);
buf[M41T80_REG_YEAR] = bin2bcd(tm.tm_year - 100);
buf[M41T80_REG_WDAY] = tm.tm_wday;
/* If the square wave output is controlled in the weekday register */
if (clientdata->features & M41T80_FEATURE_SQ_ALT) {
@ -257,20 +268,37 @@ static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm)
err = i2c_smbus_write_i2c_block_data(client, M41T80_REG_SSEC,
sizeof(buf), buf);
if (err < 0) {
dev_err(&client->dev, "Unable to write to date registers\n");
dev_dbg(&client->dev, "Unable to write to date registers\n");
return err;
}
/* Clear the OF bit of Flags Register */
flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
if (flags < 0)
return flags;
err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS,
flags & ~M41T80_FLAGS_OF);
if (err < 0) {
dev_err(&client->dev, "Unable to write flags register\n");
return err;
if (flags & M41T80_FLAGS_OF) {
/* OF cannot be immediately reset: oscillator has to be restarted. */
dev_warn(&client->dev, "OF bit is still set, kickstarting clock.\n");
err = i2c_smbus_write_byte_data(client, M41T80_REG_SEC, M41T80_SEC_ST);
if (err < 0) {
dev_dbg(&client->dev, "Can't set ST bit\n");
return err;
}
err = i2c_smbus_write_byte_data(client, M41T80_REG_SEC, flags & ~M41T80_SEC_ST);
if (err < 0) {
dev_dbg(&client->dev, "Can't clear ST bit\n");
return err;
}
/* oscillator must run for 4sec before we attempt to reset OF bit */
msleep(4000);
/* Clear the OF bit of Flags Register */
err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS, flags & ~M41T80_FLAGS_OF);
if (err < 0) {
dev_dbg(&client->dev, "Unable to write flags register\n");
return err;
}
flags = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
if (flags < 0) {
return flags;
} else if (flags & M41T80_FLAGS_OF) {
dev_dbg(&client->dev, "Can't clear the OF bit check battery\n");
return err;
}
}
return err;
@ -308,7 +336,7 @@ static int m41t80_alarm_irq_enable(struct device *dev, unsigned int enabled)
retval = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, flags);
if (retval < 0) {
dev_err(dev, "Unable to enable alarm IRQ %d\n", retval);
dev_dbg(dev, "Unable to enable alarm IRQ %d\n", retval);
return retval;
}
return 0;
@ -333,7 +361,7 @@ static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
err = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
ret & ~(M41T80_ALMON_AFE));
if (err < 0) {
dev_err(dev, "Unable to clear AFE bit\n");
dev_dbg(dev, "Unable to clear AFE bit\n");
return err;
}
@ -347,7 +375,7 @@ static int m41t80_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
err = i2c_smbus_write_byte_data(client, M41T80_REG_FLAGS,
ret & ~(M41T80_FLAGS_AF));
if (err < 0) {
dev_err(dev, "Unable to clear AF bit\n");
dev_dbg(dev, "Unable to clear AF bit\n");
return err;
}

View file

@ -332,6 +332,7 @@ static const struct mtk_rtc_data mt6397_rtc_data = {
static const struct of_device_id mt6397_rtc_of_match[] = {
{ .compatible = "mediatek,mt6323-rtc", .data = &mt6397_rtc_data },
{ .compatible = "mediatek,mt6357-rtc", .data = &mt6358_rtc_data },
{ .compatible = "mediatek,mt6358-rtc", .data = &mt6358_rtc_data },
{ .compatible = "mediatek,mt6397-rtc", .data = &mt6397_rtc_data },
{ }

View file

@ -285,7 +285,7 @@ static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)
buf[2] = bin2bcd(tm->time.tm_mday);
buf[3] = tm->time.tm_wday & 0x07;
err = regmap_bulk_write(pcf8563->regmap, PCF8563_REG_SC, buf,
err = regmap_bulk_write(pcf8563->regmap, PCF8563_REG_AMN, buf,
sizeof(buf));
if (err)
return err;

View file

@ -576,13 +576,20 @@ static int pm8xxx_rtc_probe_offset(struct pm8xxx_rtc *rtc_dd)
}
/* Use UEFI storage as fallback if available */
if (efivar_is_available()) {
rc = pm8xxx_rtc_read_uefi_offset(rtc_dd);
if (rc == 0)
rtc_dd->use_uefi = true;
rtc_dd->use_uefi = of_property_read_bool(rtc_dd->dev->of_node,
"qcom,uefi-rtc-info");
if (!rtc_dd->use_uefi)
return 0;
if (!efivar_is_available()) {
if (IS_ENABLED(CONFIG_EFI))
return -EPROBE_DEFER;
dev_warn(rtc_dd->dev, "efivars not available\n");
rtc_dd->use_uefi = false;
}
return 0;
return pm8xxx_rtc_read_uefi_offset(rtc_dd);
}
static int pm8xxx_rtc_probe(struct platform_device *pdev)
@ -676,7 +683,6 @@ static struct platform_driver pm8xxx_rtc_driver = {
module_platform_driver(pm8xxx_rtc_driver);
MODULE_ALIAS("platform:rtc-pm8xxx");
MODULE_DESCRIPTION("PMIC8xxx RTC driver");
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Anirudh Ghayal <aghayal@codeaurora.org>");

View file

@ -12,6 +12,7 @@
*/
#include <linux/bcd.h>
#include <linux/clk.h>
#include <linux/init.h>
#include <linux/iopoll.h>
#include <linux/module.h>
@ -22,9 +23,9 @@
#include <linux/spinlock.h>
#define RZN1_RTC_CTL0 0x00
#define RZN1_RTC_CTL0_SLSB_SUBU 0
#define RZN1_RTC_CTL0_SLSB_SCMP BIT(4)
#define RZN1_RTC_CTL0_AMPM BIT(5)
#define RZN1_RTC_CTL0_CEST BIT(6)
#define RZN1_RTC_CTL0_CE BIT(7)
#define RZN1_RTC_CTL1 0x04
@ -49,6 +50,8 @@
#define RZN1_RTC_SUBU_DEV BIT(7)
#define RZN1_RTC_SUBU_DECR BIT(6)
#define RZN1_RTC_SCMP 0x3c
#define RZN1_RTC_ALM 0x40
#define RZN1_RTC_ALH 0x44
#define RZN1_RTC_ALW 0x48
@ -356,7 +359,7 @@ static int rzn1_rtc_set_offset(struct device *dev, long offset)
return 0;
}
static const struct rtc_class_ops rzn1_rtc_ops = {
static const struct rtc_class_ops rzn1_rtc_ops_subu = {
.read_time = rzn1_rtc_read_time,
.set_time = rzn1_rtc_set_time,
.read_alarm = rzn1_rtc_read_alarm,
@ -366,11 +369,21 @@ static const struct rtc_class_ops rzn1_rtc_ops = {
.set_offset = rzn1_rtc_set_offset,
};
static const struct rtc_class_ops rzn1_rtc_ops_scmp = {
.read_time = rzn1_rtc_read_time,
.set_time = rzn1_rtc_set_time,
.read_alarm = rzn1_rtc_read_alarm,
.set_alarm = rzn1_rtc_set_alarm,
.alarm_irq_enable = rzn1_rtc_alarm_irq_enable,
};
static int rzn1_rtc_probe(struct platform_device *pdev)
{
struct rzn1_rtc *rtc;
int irq;
int ret;
u32 val, scmp_val = 0;
struct clk *xtal;
unsigned long rate;
int irq, ret;
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
if (!rtc)
@ -393,7 +406,6 @@ static int rzn1_rtc_probe(struct platform_device *pdev)
rtc->rtcdev->range_min = RTC_TIMESTAMP_BEGIN_2000;
rtc->rtcdev->range_max = RTC_TIMESTAMP_END_2099;
rtc->rtcdev->alarm_offset_max = 7 * 86400;
rtc->rtcdev->ops = &rzn1_rtc_ops;
ret = devm_pm_runtime_enable(&pdev->dev);
if (ret < 0)
@ -402,12 +414,44 @@ static int rzn1_rtc_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
/*
* Ensure the clock counter is enabled.
* Set 24-hour mode and possible oscillator offset compensation in SUBU mode.
*/
writel(RZN1_RTC_CTL0_CE | RZN1_RTC_CTL0_AMPM | RZN1_RTC_CTL0_SLSB_SUBU,
rtc->base + RZN1_RTC_CTL0);
/* Only switch to scmp if we have an xtal clock with a valid rate and != 32768 */
xtal = devm_clk_get_optional(&pdev->dev, "xtal");
if (IS_ERR(xtal)) {
ret = PTR_ERR(xtal);
goto dis_runtime_pm;
} else if (xtal) {
rate = clk_get_rate(xtal);
if (rate < 32000 || rate > BIT(22)) {
ret = -EOPNOTSUPP;
goto dis_runtime_pm;
}
if (rate != 32768)
scmp_val = RZN1_RTC_CTL0_SLSB_SCMP;
}
/* Disable controller during SUBU/SCMP setup */
val = readl(rtc->base + RZN1_RTC_CTL0) & ~RZN1_RTC_CTL0_CE;
writel(val, rtc->base + RZN1_RTC_CTL0);
/* Wait 2-4 32k clock cycles for the disabled controller */
ret = readl_poll_timeout(rtc->base + RZN1_RTC_CTL0, val,
!(val & RZN1_RTC_CTL0_CEST), 62, 123);
if (ret)
goto dis_runtime_pm;
/* Set desired modes leaving the controller disabled */
writel(RZN1_RTC_CTL0_AMPM | scmp_val, rtc->base + RZN1_RTC_CTL0);
if (scmp_val) {
writel(rate - 1, rtc->base + RZN1_RTC_SCMP);
rtc->rtcdev->ops = &rzn1_rtc_ops_scmp;
} else {
rtc->rtcdev->ops = &rzn1_rtc_ops_subu;
}
/* Enable controller finally */
writel(RZN1_RTC_CTL0_CE | RZN1_RTC_CTL0_AMPM | scmp_val, rtc->base + RZN1_RTC_CTL0);
/* Disable all interrupts */
writel(0, rtc->base + RZN1_RTC_CTL1);
@ -444,6 +488,11 @@ dis_runtime_pm:
static void rzn1_rtc_remove(struct platform_device *pdev)
{
struct rzn1_rtc *rtc = platform_get_drvdata(pdev);
/* Disable all interrupts */
writel(0, rtc->base + RZN1_RTC_CTL1);
pm_runtime_put(&pdev->dev);
}

385
drivers/rtc/rtc-s32g.c Normal file
View file

@ -0,0 +1,385 @@
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2025 NXP
*/
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#define RTCC_OFFSET 0x4ul
#define RTCS_OFFSET 0x8ul
#define APIVAL_OFFSET 0x10ul
/* RTCC fields */
#define RTCC_CNTEN BIT(31)
#define RTCC_APIEN BIT(15)
#define RTCC_APIIE BIT(14)
#define RTCC_CLKSEL_MASK GENMASK(13, 12)
#define RTCC_DIV512EN BIT(11)
#define RTCC_DIV32EN BIT(10)
/* RTCS fields */
#define RTCS_INV_API BIT(17)
#define RTCS_APIF BIT(13)
#define APIVAL_MAX_VAL GENMASK(31, 0)
#define RTC_SYNCH_TIMEOUT (100 * USEC_PER_MSEC)
/*
* S32G2 and S32G3 SoCs have RTC clock source1 reserved and
* should not be used.
*/
#define RTC_CLK_SRC1_RESERVED BIT(1)
/*
* S32G RTC module has a 512 value and a 32 value hardware frequency
* divisors (DIV512 and DIV32) which could be used to achieve higher
* counter ranges by lowering the RTC frequency.
*/
enum {
DIV1 = 1,
DIV32 = 32,
DIV512 = 512,
DIV512_32 = 16384
};
static const char *const rtc_clk_src[] = {
"source0",
"source1",
"source2",
"source3"
};
struct rtc_priv {
struct rtc_device *rdev;
void __iomem *rtc_base;
struct clk *ipg;
struct clk *clk_src;
const struct rtc_soc_data *rtc_data;
u64 rtc_hz;
time64_t sleep_sec;
int irq;
u32 clk_src_idx;
};
struct rtc_soc_data {
u32 clk_div;
u32 reserved_clk_mask;
};
static const struct rtc_soc_data rtc_s32g2_data = {
.clk_div = DIV512_32,
.reserved_clk_mask = RTC_CLK_SRC1_RESERVED,
};
static irqreturn_t s32g_rtc_handler(int irq, void *dev)
{
struct rtc_priv *priv = platform_get_drvdata(dev);
u32 status;
status = readl(priv->rtc_base + RTCS_OFFSET);
if (status & RTCS_APIF) {
writel(0x0, priv->rtc_base + APIVAL_OFFSET);
writel(status | RTCS_APIF, priv->rtc_base + RTCS_OFFSET);
}
rtc_update_irq(priv->rdev, 1, RTC_IRQF | RTC_AF);
return IRQ_HANDLED;
}
/*
* The function is not really getting time from the RTC since the S32G RTC
* has several limitations. Thus, to setup alarm use system time.
*/
static int s32g_rtc_read_time(struct device *dev,
struct rtc_time *tm)
{
struct rtc_priv *priv = dev_get_drvdata(dev);
time64_t sec;
if (check_add_overflow(ktime_get_real_seconds(),
priv->sleep_sec, &sec))
return -ERANGE;
rtc_time64_to_tm(sec, tm);
return 0;
}
static int s32g_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct rtc_priv *priv = dev_get_drvdata(dev);
u32 rtcc, rtcs;
rtcc = readl(priv->rtc_base + RTCC_OFFSET);
rtcs = readl(priv->rtc_base + RTCS_OFFSET);
alrm->enabled = rtcc & RTCC_APIIE;
if (alrm->enabled)
alrm->pending = !(rtcs & RTCS_APIF);
return 0;
}
static int s32g_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct rtc_priv *priv = dev_get_drvdata(dev);
u32 rtcc;
/* RTC API functionality is used both for triggering interrupts
* and as a wakeup event. Hence it should always be enabled.
*/
rtcc = readl(priv->rtc_base + RTCC_OFFSET);
rtcc |= RTCC_APIEN | RTCC_APIIE;
writel(rtcc, priv->rtc_base + RTCC_OFFSET);
return 0;
}
static int s32g_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct rtc_priv *priv = dev_get_drvdata(dev);
unsigned long long cycles;
long long t_offset;
time64_t alrm_time;
u32 rtcs;
int ret;
alrm_time = rtc_tm_to_time64(&alrm->time);
t_offset = alrm_time - ktime_get_real_seconds() - priv->sleep_sec;
if (t_offset < 0)
return -ERANGE;
cycles = t_offset * priv->rtc_hz;
if (cycles > APIVAL_MAX_VAL)
return -ERANGE;
/* APIVAL could have been reset from the IRQ handler.
* Hence, we wait in case there is a synchronization process.
*/
ret = read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API),
0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET);
if (ret)
return ret;
writel(cycles, priv->rtc_base + APIVAL_OFFSET);
return read_poll_timeout(readl, rtcs, !(rtcs & RTCS_INV_API),
0, RTC_SYNCH_TIMEOUT, false, priv->rtc_base + RTCS_OFFSET);
}
/*
* Disable the 32-bit free running counter.
* This allows Clock Source and Divisors selection
* to be performed without causing synchronization issues.
*/
static void s32g_rtc_disable(struct rtc_priv *priv)
{
u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET);
rtcc &= ~RTCC_CNTEN;
writel(rtcc, priv->rtc_base + RTCC_OFFSET);
}
static void s32g_rtc_enable(struct rtc_priv *priv)
{
u32 rtcc = readl(priv->rtc_base + RTCC_OFFSET);
rtcc |= RTCC_CNTEN;
writel(rtcc, priv->rtc_base + RTCC_OFFSET);
}
static int rtc_clk_src_setup(struct rtc_priv *priv)
{
u32 rtcc;
rtcc = FIELD_PREP(RTCC_CLKSEL_MASK, priv->clk_src_idx);
switch (priv->rtc_data->clk_div) {
case DIV512_32:
rtcc |= RTCC_DIV512EN;
rtcc |= RTCC_DIV32EN;
break;
case DIV512:
rtcc |= RTCC_DIV512EN;
break;
case DIV32:
rtcc |= RTCC_DIV32EN;
break;
case DIV1:
break;
default:
return -EINVAL;
}
rtcc |= RTCC_APIEN | RTCC_APIIE;
/*
* Make sure the CNTEN is 0 before we configure
* the clock source and dividers.
*/
s32g_rtc_disable(priv);
writel(rtcc, priv->rtc_base + RTCC_OFFSET);
s32g_rtc_enable(priv);
return 0;
}
static const struct rtc_class_ops rtc_ops = {
.read_time = s32g_rtc_read_time,
.read_alarm = s32g_rtc_read_alarm,
.set_alarm = s32g_rtc_set_alarm,
.alarm_irq_enable = s32g_rtc_alarm_irq_enable,
};
static int rtc_clk_dts_setup(struct rtc_priv *priv,
struct device *dev)
{
u32 i;
priv->ipg = devm_clk_get_enabled(dev, "ipg");
if (IS_ERR(priv->ipg))
return dev_err_probe(dev, PTR_ERR(priv->ipg),
"Failed to get 'ipg' clock\n");
for (i = 0; i < ARRAY_SIZE(rtc_clk_src); i++) {
if (priv->rtc_data->reserved_clk_mask & BIT(i))
return -EOPNOTSUPP;
priv->clk_src = devm_clk_get_enabled(dev, rtc_clk_src[i]);
if (!IS_ERR(priv->clk_src)) {
priv->clk_src_idx = i;
break;
}
}
if (IS_ERR(priv->clk_src))
return dev_err_probe(dev, PTR_ERR(priv->clk_src),
"Failed to get rtc module clock source\n");
return 0;
}
static int s32g_rtc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rtc_priv *priv;
unsigned long rtc_hz;
int ret;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->rtc_data = of_device_get_match_data(dev);
if (!priv->rtc_data)
return -ENODEV;
priv->rtc_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(priv->rtc_base))
return PTR_ERR(priv->rtc_base);
device_init_wakeup(dev, true);
ret = rtc_clk_dts_setup(priv, dev);
if (ret)
return ret;
priv->rdev = devm_rtc_allocate_device(dev);
if (IS_ERR(priv->rdev))
return PTR_ERR(priv->rdev);
ret = rtc_clk_src_setup(priv);
if (ret)
return ret;
priv->irq = platform_get_irq(pdev, 0);
if (priv->irq < 0) {
ret = priv->irq;
goto disable_rtc;
}
rtc_hz = clk_get_rate(priv->clk_src);
if (!rtc_hz) {
dev_err(dev, "Failed to get RTC frequency\n");
ret = -EINVAL;
goto disable_rtc;
}
priv->rtc_hz = DIV_ROUND_UP(rtc_hz, priv->rtc_data->clk_div);
platform_set_drvdata(pdev, priv);
priv->rdev->ops = &rtc_ops;
ret = devm_request_irq(dev, priv->irq,
s32g_rtc_handler, 0, dev_name(dev), pdev);
if (ret) {
dev_err(dev, "Request interrupt %d failed, error: %d\n",
priv->irq, ret);
goto disable_rtc;
}
ret = devm_rtc_register_device(priv->rdev);
if (ret)
goto disable_rtc;
return 0;
disable_rtc:
s32g_rtc_disable(priv);
return ret;
}
static int s32g_rtc_suspend(struct device *dev)
{
struct rtc_priv *priv = dev_get_drvdata(dev);
u32 apival = readl(priv->rtc_base + APIVAL_OFFSET);
if (check_add_overflow(priv->sleep_sec, div64_u64(apival, priv->rtc_hz),
&priv->sleep_sec)) {
dev_warn(dev, "Overflow on sleep cycles occurred. Resetting to 0.\n");
priv->sleep_sec = 0;
}
return 0;
}
static int s32g_rtc_resume(struct device *dev)
{
struct rtc_priv *priv = dev_get_drvdata(dev);
/* The transition from resume to run is a reset event.
* This leads to the RTC registers being reset after resume from
* suspend. It is uncommon, but this behaviour has been observed
* on S32G RTC after issuing a Suspend to RAM operation.
* Thus, reconfigure RTC registers on the resume path.
*/
return rtc_clk_src_setup(priv);
}
static const struct of_device_id rtc_dt_ids[] = {
{ .compatible = "nxp,s32g2-rtc", .data = &rtc_s32g2_data },
{ /* sentinel */ },
};
static DEFINE_SIMPLE_DEV_PM_OPS(s32g_rtc_pm_ops,
s32g_rtc_suspend, s32g_rtc_resume);
static struct platform_driver s32g_rtc_driver = {
.driver = {
.name = "s32g-rtc",
.pm = pm_sleep_ptr(&s32g_rtc_pm_ops),
.of_match_table = rtc_dt_ids,
},
.probe = s32g_rtc_probe,
};
module_platform_driver(s32g_rtc_driver);
MODULE_AUTHOR("NXP");
MODULE_DESCRIPTION("NXP RTC driver for S32G2/S32G3");
MODULE_LICENSE("GPL");

View file

@ -609,4 +609,3 @@ module_platform_driver(s3c_rtc_driver);
MODULE_DESCRIPTION("Samsung S3C RTC Driver");
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:s3c2410-rtc");

View file

@ -5,6 +5,7 @@
* Copyright (C) 2006 - 2009 Paul Mundt
* Copyright (C) 2006 Jamie Lenehan
* Copyright (C) 2008 Angelo Castello
* Copyright (C) 2025 Wolfram Sang, Renesas Electronics Corporation
*
* Based on the old arch/sh/kernel/cpu/rtc.c by:
*
@ -31,7 +32,7 @@
/* Default values for RZ/A RTC */
#define rtc_reg_size sizeof(u16)
#define RTC_BIT_INVERTED 0 /* no chip bugs */
#define RTC_CAP_4_DIGIT_YEAR (1 << 0)
#define RTC_CAP_4_DIGIT_YEAR BIT(0)
#define RTC_DEF_CAPABILITIES RTC_CAP_4_DIGIT_YEAR
#endif
@ -70,62 +71,35 @@
*/
/* ALARM Bits - or with BCD encoded value */
#define AR_ENB 0x80 /* Enable for alarm cmp */
/* Period Bits */
#define PF_HP 0x100 /* Enable Half Period to support 8,32,128Hz */
#define PF_COUNT 0x200 /* Half periodic counter */
#define PF_OXS 0x400 /* Periodic One x Second */
#define PF_KOU 0x800 /* Kernel or User periodic request 1=kernel */
#define PF_MASK 0xf00
#define AR_ENB BIT(7) /* Enable for alarm cmp */
/* RCR1 Bits */
#define RCR1_CF 0x80 /* Carry Flag */
#define RCR1_CIE 0x10 /* Carry Interrupt Enable */
#define RCR1_AIE 0x08 /* Alarm Interrupt Enable */
#define RCR1_AF 0x01 /* Alarm Flag */
#define RCR1_CF BIT(7) /* Carry Flag */
#define RCR1_CIE BIT(4) /* Carry Interrupt Enable */
#define RCR1_AIE BIT(3) /* Alarm Interrupt Enable */
#define RCR1_AF BIT(0) /* Alarm Flag */
/* RCR2 Bits */
#define RCR2_PEF 0x80 /* PEriodic interrupt Flag */
#define RCR2_PESMASK 0x70 /* Periodic interrupt Set */
#define RCR2_RTCEN 0x08 /* ENable RTC */
#define RCR2_ADJ 0x04 /* ADJustment (30-second) */
#define RCR2_RESET 0x02 /* Reset bit */
#define RCR2_START 0x01 /* Start bit */
#define RCR2_RTCEN BIT(3) /* ENable RTC */
#define RCR2_ADJ BIT(2) /* ADJustment (30-second) */
#define RCR2_RESET BIT(1) /* Reset bit */
#define RCR2_START BIT(0) /* Start bit */
struct sh_rtc {
void __iomem *regbase;
unsigned long regsize;
struct resource *res;
int alarm_irq;
int periodic_irq;
int carry_irq;
struct clk *clk;
struct rtc_device *rtc_dev;
spinlock_t lock;
spinlock_t lock; /* protecting register access */
unsigned long capabilities; /* See asm/rtc.h for cap bits */
unsigned short periodic_freq;
};
static int __sh_rtc_interrupt(struct sh_rtc *rtc)
static irqreturn_t sh_rtc_alarm(int irq, void *dev_id)
{
struct sh_rtc *rtc = dev_id;
unsigned int tmp, pending;
tmp = readb(rtc->regbase + RCR1);
pending = tmp & RCR1_CF;
tmp &= ~RCR1_CF;
writeb(tmp, rtc->regbase + RCR1);
/* Users have requested One x Second IRQ */
if (pending && rtc->periodic_freq & PF_OXS)
rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF);
return pending;
}
static int __sh_rtc_alarm(struct sh_rtc *rtc)
{
unsigned int tmp, pending;
spin_lock(&rtc->lock);
tmp = readb(rtc->regbase + RCR1);
pending = tmp & RCR1_AF;
@ -135,84 +109,12 @@ static int __sh_rtc_alarm(struct sh_rtc *rtc)
if (pending)
rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF);
return pending;
}
static int __sh_rtc_periodic(struct sh_rtc *rtc)
{
unsigned int tmp, pending;
tmp = readb(rtc->regbase + RCR2);
pending = tmp & RCR2_PEF;
tmp &= ~RCR2_PEF;
writeb(tmp, rtc->regbase + RCR2);
if (!pending)
return 0;
/* Half period enabled than one skipped and the next notified */
if ((rtc->periodic_freq & PF_HP) && (rtc->periodic_freq & PF_COUNT))
rtc->periodic_freq &= ~PF_COUNT;
else {
if (rtc->periodic_freq & PF_HP)
rtc->periodic_freq |= PF_COUNT;
rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF);
}
return pending;
}
static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id)
{
struct sh_rtc *rtc = dev_id;
int ret;
spin_lock(&rtc->lock);
ret = __sh_rtc_interrupt(rtc);
spin_unlock(&rtc->lock);
return IRQ_RETVAL(ret);
return IRQ_RETVAL(pending);
}
static irqreturn_t sh_rtc_alarm(int irq, void *dev_id)
{
struct sh_rtc *rtc = dev_id;
int ret;
spin_lock(&rtc->lock);
ret = __sh_rtc_alarm(rtc);
spin_unlock(&rtc->lock);
return IRQ_RETVAL(ret);
}
static irqreturn_t sh_rtc_periodic(int irq, void *dev_id)
{
struct sh_rtc *rtc = dev_id;
int ret;
spin_lock(&rtc->lock);
ret = __sh_rtc_periodic(rtc);
spin_unlock(&rtc->lock);
return IRQ_RETVAL(ret);
}
static irqreturn_t sh_rtc_shared(int irq, void *dev_id)
{
struct sh_rtc *rtc = dev_id;
int ret;
spin_lock(&rtc->lock);
ret = __sh_rtc_interrupt(rtc);
ret |= __sh_rtc_alarm(rtc);
ret |= __sh_rtc_periodic(rtc);
spin_unlock(&rtc->lock);
return IRQ_RETVAL(ret);
}
static inline void sh_rtc_setaie(struct device *dev, unsigned int enable)
static int sh_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
unsigned int tmp;
@ -229,45 +131,7 @@ static inline void sh_rtc_setaie(struct device *dev, unsigned int enable)
writeb(tmp, rtc->regbase + RCR1);
spin_unlock_irq(&rtc->lock);
}
static int sh_rtc_proc(struct device *dev, struct seq_file *seq)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
unsigned int tmp;
tmp = readb(rtc->regbase + RCR1);
seq_printf(seq, "carry_IRQ\t: %s\n", (tmp & RCR1_CIE) ? "yes" : "no");
tmp = readb(rtc->regbase + RCR2);
seq_printf(seq, "periodic_IRQ\t: %s\n",
(tmp & RCR2_PESMASK) ? "yes" : "no");
return 0;
}
static inline void sh_rtc_setcie(struct device *dev, unsigned int enable)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
unsigned int tmp;
spin_lock_irq(&rtc->lock);
tmp = readb(rtc->regbase + RCR1);
if (!enable)
tmp &= ~RCR1_CIE;
else
tmp |= RCR1_CIE;
writeb(tmp, rtc->regbase + RCR1);
spin_unlock_irq(&rtc->lock);
}
static int sh_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
sh_rtc_setaie(dev, enabled);
return 0;
}
@ -320,14 +184,8 @@ static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm)
tm->tm_sec--;
#endif
/* only keep the carry interrupt enabled if UIE is on */
if (!(rtc->periodic_freq & PF_OXS))
sh_rtc_setcie(dev, 0);
dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__func__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
__func__, tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday);
return 0;
@ -461,16 +319,17 @@ static const struct rtc_class_ops sh_rtc_ops = {
.set_time = sh_rtc_set_time,
.read_alarm = sh_rtc_read_alarm,
.set_alarm = sh_rtc_set_alarm,
.proc = sh_rtc_proc,
.alarm_irq_enable = sh_rtc_alarm_irq_enable,
};
static int __init sh_rtc_probe(struct platform_device *pdev)
{
struct sh_rtc *rtc;
struct resource *res;
struct resource *res, *req_res;
char clk_name[14];
int clk_id, ret;
unsigned int tmp;
resource_size_t regsize;
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
if (unlikely(!rtc))
@ -478,34 +337,32 @@ static int __init sh_rtc_probe(struct platform_device *pdev)
spin_lock_init(&rtc->lock);
/* get periodic/carry/alarm irqs */
ret = platform_get_irq(pdev, 0);
if (unlikely(ret <= 0)) {
dev_err(&pdev->dev, "No IRQ resource\n");
return -ENOENT;
}
rtc->periodic_irq = ret;
rtc->carry_irq = platform_get_irq(pdev, 1);
rtc->alarm_irq = platform_get_irq(pdev, 2);
if (!pdev->dev.of_node)
rtc->alarm_irq = platform_get_irq(pdev, 2);
else
rtc->alarm_irq = ret;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (!res)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(res == NULL)) {
if (!res) {
dev_err(&pdev->dev, "No IO resource\n");
return -ENOENT;
}
rtc->regsize = resource_size(res);
rtc->res = devm_request_mem_region(&pdev->dev, res->start,
rtc->regsize, pdev->name);
if (unlikely(!rtc->res))
regsize = resource_size(res);
req_res = devm_request_mem_region(&pdev->dev, res->start, regsize, pdev->name);
if (!req_res)
return -EBUSY;
rtc->regbase = devm_ioremap(&pdev->dev, rtc->res->start, rtc->regsize);
if (unlikely(!rtc->regbase))
rtc->regbase = devm_ioremap(&pdev->dev, req_res->start, regsize);
if (!rtc->regbase)
return -EINVAL;
if (!pdev->dev.of_node) {
@ -515,8 +372,9 @@ static int __init sh_rtc_probe(struct platform_device *pdev)
clk_id = 0;
snprintf(clk_name, sizeof(clk_name), "rtc%d", clk_id);
} else
} else {
snprintf(clk_name, sizeof(clk_name), "fck");
}
rtc->clk = devm_clk_get(&pdev->dev, clk_name);
if (IS_ERR(rtc->clk)) {
@ -550,51 +408,19 @@ static int __init sh_rtc_probe(struct platform_device *pdev)
}
#endif
if (rtc->carry_irq <= 0) {
/* register shared periodic/carry/alarm irq */
ret = devm_request_irq(&pdev->dev, rtc->periodic_irq,
sh_rtc_shared, 0, "sh-rtc", rtc);
if (unlikely(ret)) {
dev_err(&pdev->dev,
"request IRQ failed with %d, IRQ %d\n", ret,
rtc->periodic_irq);
goto err_unmap;
}
} else {
/* register periodic/carry/alarm irqs */
ret = devm_request_irq(&pdev->dev, rtc->periodic_irq,
sh_rtc_periodic, 0, "sh-rtc period", rtc);
if (unlikely(ret)) {
dev_err(&pdev->dev,
"request period IRQ failed with %d, IRQ %d\n",
ret, rtc->periodic_irq);
goto err_unmap;
}
ret = devm_request_irq(&pdev->dev, rtc->carry_irq,
sh_rtc_interrupt, 0, "sh-rtc carry", rtc);
if (unlikely(ret)) {
dev_err(&pdev->dev,
"request carry IRQ failed with %d, IRQ %d\n",
ret, rtc->carry_irq);
goto err_unmap;
}
ret = devm_request_irq(&pdev->dev, rtc->alarm_irq,
sh_rtc_alarm, 0, "sh-rtc alarm", rtc);
if (unlikely(ret)) {
dev_err(&pdev->dev,
"request alarm IRQ failed with %d, IRQ %d\n",
ret, rtc->alarm_irq);
goto err_unmap;
}
ret = devm_request_irq(&pdev->dev, rtc->alarm_irq, sh_rtc_alarm, 0, "sh-rtc", rtc);
if (ret) {
dev_err(&pdev->dev, "request alarm IRQ failed with %d, IRQ %d\n",
ret, rtc->alarm_irq);
goto err_unmap;
}
platform_set_drvdata(pdev, rtc);
/* everything disabled by default */
sh_rtc_setaie(&pdev->dev, 0);
sh_rtc_setcie(&pdev->dev, 0);
tmp = readb(rtc->regbase + RCR1);
tmp &= ~(RCR1_CIE | RCR1_AIE);
writeb(tmp, rtc->regbase + RCR1);
rtc->rtc_dev->ops = &sh_rtc_ops;
rtc->rtc_dev->max_user_freq = 256;
@ -624,36 +450,27 @@ static void __exit sh_rtc_remove(struct platform_device *pdev)
{
struct sh_rtc *rtc = platform_get_drvdata(pdev);
sh_rtc_setaie(&pdev->dev, 0);
sh_rtc_setcie(&pdev->dev, 0);
sh_rtc_alarm_irq_enable(&pdev->dev, 0);
clk_disable(rtc->clk);
}
static void sh_rtc_set_irq_wake(struct device *dev, int enabled)
static int __maybe_unused sh_rtc_suspend(struct device *dev)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
irq_set_irq_wake(rtc->periodic_irq, enabled);
if (rtc->carry_irq > 0) {
irq_set_irq_wake(rtc->carry_irq, enabled);
irq_set_irq_wake(rtc->alarm_irq, enabled);
}
}
static int __maybe_unused sh_rtc_suspend(struct device *dev)
{
if (device_may_wakeup(dev))
sh_rtc_set_irq_wake(dev, 1);
irq_set_irq_wake(rtc->alarm_irq, 1);
return 0;
}
static int __maybe_unused sh_rtc_resume(struct device *dev)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
if (device_may_wakeup(dev))
sh_rtc_set_irq_wake(dev, 0);
irq_set_irq_wake(rtc->alarm_irq, 0);
return 0;
}
@ -684,8 +501,8 @@ static struct platform_driver sh_rtc_platform_driver __refdata = {
module_platform_driver_probe(sh_rtc_platform_driver, sh_rtc_probe);
MODULE_DESCRIPTION("SuperH on-chip RTC driver");
MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, "
"Jamie Lenehan <lenehan@twibble.org>, "
"Angelo Castello <angelo.castello@st.com>");
MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
MODULE_AUTHOR("Jamie Lenehan <lenehan@twibble.org>");
MODULE_AUTHOR("Angelo Castello <angelo.castello@st.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);

View file

@ -1283,7 +1283,6 @@ static struct platform_driver stm32_rtc_driver = {
module_platform_driver(stm32_rtc_driver);
MODULE_ALIAS("platform:" DRIVER_NAME);
MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
MODULE_DESCRIPTION("STMicroelectronics STM32 Real Time Clock driver");
MODULE_LICENSE("GPL v2");