mirror of
https://github.com/torvalds/linux.git
synced 2026-03-13 23:46:14 +01:00
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:
commit
7fdaba9129
26 changed files with 946 additions and 329 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ properties:
|
|||
- enum:
|
||||
- microchip,sam9x60-rtt
|
||||
- microchip,sam9x7-rtt
|
||||
- microchip,sama7d65-rtt
|
||||
- const: atmel,at91sam9260-rtt
|
||||
- items:
|
||||
- const: microchip,sama7g5-rtt
|
||||
|
|
|
|||
72
Documentation/devicetree/bindings/rtc/nxp,s32g-rtc.yaml
Normal file
72
Documentation/devicetree/bindings/rtc/nxp,s32g-rtc.yaml
Normal 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";
|
||||
};
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
218
drivers/rtc/rtc-cv1800.c
Normal 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, ®);
|
||||
|
||||
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");
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
{ }
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>");
|
||||
|
|
|
|||
|
|
@ -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
385
drivers/rtc/rtc-s32g.c
Normal 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");
|
||||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue