RTC for 6.18

Subsystem:
  - Fix race when setting alarm
  - Ensure alarm irq is enabled when UIE is enabled
  - remove unneeded 'fast_io' parameter in regmap_config
 
 New driver:
  - SpacemiT P1 RTC
 
 Drivers:
  - efi: Remove wakeup functionality
  - optee: add alarms support
  - s3c: Drop support for S3C2410
  - zynqmp: Restore alarm functionality after kexec transition
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmjqgaEACgkQY6TcMGxw
 OjK/uBAAskG29umNu0QmQYOdtihk45mOFe6DZgCgDoTKa+OcV26/Y0Nak2n13rzL
 j7yLYfiGf0IjghcrdfXwONHwYR2y0LoQBZK5i9klZYNkuabe9bqEq7k0t7RPc33D
 mgycZVHYgQh9Jx1DM4XAUKCyE08TJ4AjdybpXIhyJbovAgNcHbdZ+Zv/A+Kq3P+d
 AjaGjcuwQI/3tiVDgry4BfPrvK/UzPB1SblDnmFW6w7AyMjYgarXYCW9qDlm2NAM
 l0mr48XI1V0g3KNZ/1AhH/R79tyq/57lmu4ynS6FTIL6ppkyWu0yM86Xzr6xpVK7
 Xtb+BBGmB4lSKqmBVT3pM6iX14XKM3gYc4Dn9ZfvLwW1bVrMKfx0kIzzHn1pIgX0
 dSrwbhuJVrUCwqczUZ5Ul+D9i8tcLBL20RcbobdOEqxkkoS18gpIpqX5bGxCLSKM
 DEwewK2tP/VRWxFp57yUgzHdmEUg66l8Z6IEoZsM+2d3AXWblssijmDnQ1fNxXx8
 94gfcVqn5HhmmGQzf8ZUWZQvxqZN9merZQuTFj0mxZnzXBCnqjQ1pLxvgxlyqhLT
 IjSsIDhQTsOFTSxAR+1rK4Yo4o60U59/HrgcTdgP4mSaf0AWHFjhlVf+WfuJhVMY
 M5qtwTO1IuTpxlyzQgPaClF/CFOfC6OaKqvf6LRZPd0l4hMlOU8=
 =dH7Y
 -----END PGP SIGNATURE-----

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

Pull RTC updates from Alexandre Belloni:
 "This cycle, we have a new RTC driver, for the SpacemiT P1. The optee
  driver gets alarm support. We also get a fix for a race condition that
  was fairly rare unless while stress testing the alarms.

  Subsystem:
   - Fix race when setting alarm
   - Ensure alarm irq is enabled when UIE is enabled
   - remove unneeded 'fast_io' parameter in regmap_config

  New driver:
   - SpacemiT P1 RTC

  Drivers:
   - efi: Remove wakeup functionality
   - optee: add alarms support
   - s3c: Drop support for S3C2410
   - zynqmp: Restore alarm functionality after kexec transition"

* tag 'rtc-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (29 commits)
  rtc: interface: Ensure alarm irq is enabled when UIE is enabled
  rtc: tps6586x: Fix initial enable_irq/disable_irq balance
  rtc: cpcap: Fix initial enable_irq/disable_irq balance
  rtc: isl12022: Fix initial enable_irq/disable_irq balance
  rtc: interface: Fix long-standing race when setting alarm
  rtc: pcf2127: fix watchdog interrupt mask on pcf2131
  rtc: zynqmp: Restore alarm functionality after kexec transition
  rtc: amlogic-a4: Optimize global variables
  rtc: sd2405al: Add I2C address.
  rtc: Kconfig: move symbols to proper section
  rtc: optee: make optee_rtc_pm_ops static
  rtc: optee: Fix error code in optee_rtc_read_alarm()
  rtc: optee: fix error code in probe()
  dt-bindings: rtc: Convert apm,xgene-rtc to DT schema
  rtc: spacemit: support the SpacemiT P1 RTC
  rtc: optee: add alarm related rtc ops to optee rtc driver
  rtc: optee: remove unnecessary memory operations
  rtc: optee: fix memory leak on driver removal
  rtc: x1205: Fix Xicor X1205 vendor prefix
  dt-bindings: rtc: Fix Xicor X1205 vendor prefix
  ...
This commit is contained in:
Linus Torvalds 2025-10-11 11:56:47 -07:00
commit 98906f9d85
23 changed files with 753 additions and 354 deletions

View File

@ -0,0 +1,45 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/apm,xgene-rtc.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: APM X-Gene Real Time Clock
maintainers:
- Khuong Dinh <khuong@os.amperecomputing.com>
properties:
compatible:
const: apm,xgene-rtc
reg:
maxItems: 1
interrupts:
maxItems: 1
'#clock-cells':
const: 1
clocks:
maxItems: 1
required:
- compatible
- reg
- interrupts
- '#clock-cells'
- clocks
additionalProperties: false
examples:
- |
rtc@10510000 {
compatible = "apm,xgene-rtc";
reg = <0x10510000 0x400>;
interrupts = <0x0 0x46 0x4>;
#clock-cells = <1>;
clocks = <&rtcclk 0>;
};

View File

@ -1,74 +0,0 @@
Intersil ISL12057 I2C RTC/Alarm chip
ISL12057 is a trivial I2C device (it has simple device tree bindings,
consisting of a compatible field, an address and possibly an interrupt
line).
Nonetheless, it also supports an option boolean property
("wakeup-source") to handle the specific use-case found
on at least three in-tree users of the chip (NETGEAR ReadyNAS 102, 104
and 2120 ARM-based NAS); On those devices, the IRQ#2 pin of the chip
(associated with the alarm supported by the driver) is not connected
to the SoC but to a PMIC. It allows the device to be powered up when
RTC alarm rings. In order to mark the device has a wakeup source and
get access to the 'wakealarm' sysfs entry, this specific property can
be set when the IRQ#2 pin of the chip is not connected to the SoC but
can wake up the device.
Required properties supported by the device:
- "compatible": must be "isil,isl12057"
- "reg": I2C bus address of the device
Optional properties:
- "wakeup-source": mark the chip as a wakeup source, independently of
the availability of an IRQ line connected to the SoC.
Example isl12057 node without IRQ#2 pin connected (no alarm support):
isl12057: isl12057@68 {
compatible = "isil,isl12057";
reg = <0x68>;
};
Example isl12057 node with IRQ#2 pin connected to main SoC via MPP6 (note
that the pinctrl-related properties below are given for completeness and
may not be required or may be different depending on your system or
SoC, and the main function of the MPP used as IRQ line, i.e.
"interrupt-parent" and "interrupts" are usually sufficient):
pinctrl {
...
rtc_alarm_pin: rtc_alarm_pin {
marvell,pins = "mpp6";
marvell,function = "gpio";
};
...
};
...
isl12057: isl12057@68 {
compatible = "isil,isl12057";
reg = <0x68>;
pinctrl-0 = <&rtc_alarm_pin>;
pinctrl-names = "default";
interrupt-parent = <&gpio0>;
interrupts = <6 IRQ_TYPE_EDGE_FALLING>;
};
Example isl12057 node without IRQ#2 pin connected to the SoC but to a
PMIC, allowing the device to be started based on configured alarm:
isl12057: isl12057@68 {
compatible = "isil,isl12057";
reg = <0x68>;
wakeup-source;
};

View File

@ -62,16 +62,6 @@ allOf:
then:
properties:
quartz-load-femtofarads: false
- if:
properties:
compatible:
contains:
enum:
- nxp,pcf85063
then:
properties:
quartz-load-femtofarads:
const: 7000
- if:
properties:
compatible:

View File

@ -13,9 +13,6 @@ properties:
compatible:
oneOf:
- enum:
- samsung,s3c2410-rtc
- samsung,s3c2416-rtc
- samsung,s3c2443-rtc
- samsung,s3c6410-rtc
- items:
- enum:
@ -29,19 +26,12 @@ properties:
maxItems: 1
clocks:
description:
Must contain a list of phandle and clock specifier for the rtc
clock and in the case of a s3c6410 compatible controller, also
a source clock.
minItems: 1
maxItems: 2
clock-names:
description:
Must contain "rtc" and for a s3c6410 compatible controller
also "rtc_src".
minItems: 1
maxItems: 2
items:
- const: rtc
- const: rtc_src
interrupts:
description:
@ -54,30 +44,6 @@ properties:
allOf:
- $ref: rtc.yaml#
- if:
properties:
compatible:
contains:
enum:
- samsung,s3c6410-rtc
- samsung,exynos3250-rtc
then:
properties:
clocks:
minItems: 2
maxItems: 2
clock-names:
items:
- const: rtc
- const: rtc_src
else:
properties:
clocks:
minItems: 1
maxItems: 1
clock-names:
items:
- const: rtc
unevaluatedProperties: false

View File

@ -38,6 +38,8 @@ properties:
- dallas,ds1672
# Extremely Accurate I²C RTC with Integrated Crystal and SRAM
- dallas,ds3232
# Dallas m41t00 Real-time Clock
- dallas,m41t00
# SD2405AL Real-Time Clock
- dfrobot,sd2405al
# EM Microelectronic EM3027 RTC
@ -83,8 +85,8 @@ properties:
- via,vt8500-rtc
# I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
- whwave,sd3078
# Xircom X1205 I2C RTC
- xircom,x1205
# Xicor/Intersil X1205 I2C RTC
- xicor,x1205
reg:
maxItems: 1

View File

@ -1,28 +0,0 @@
* APM X-Gene Real Time Clock
RTC controller for the APM X-Gene Real Time Clock
Required properties:
- compatible : Should be "apm,xgene-rtc"
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: IRQ line for the RTC.
- #clock-cells: Should be 1.
- clocks: Reference to the clock entry.
Example:
rtcclk: rtcclk {
compatible = "fixed-clock";
#clock-cells = <1>;
clock-frequency = <100000000>;
clock-output-names = "rtcclk";
};
rtc: rtc@10510000 {
compatible = "apm,xgene-rtc";
reg = <0x0 0x10510000 0x0 0x400>;
interrupts = <0x0 0x46 0x4>;
#clock-cells = <1>;
clocks = <&rtcclk 0>;
};

View File

@ -406,6 +406,16 @@ config RTC_DRV_MAX77686
This driver can also be built as a module. If so, the module
will be called rtc-max77686.
config RTC_DRV_SPACEMIT_P1
tristate "SpacemiT P1 RTC"
depends on ARCH_SPACEMIT || COMPILE_TEST
select MFD_SPACEMIT_P1
default ARCH_SPACEMIT
help
Enable support for the RTC function in the SpacemiT P1 PMIC.
This driver can also be built as a module, which will be called
"spacemit-p1-rtc".
config RTC_DRV_NCT3018Y
tristate "Nuvoton NCT3018Y"
depends on OF
@ -2044,20 +2054,6 @@ config RTC_DRV_RENESAS_RTCA3
This driver can also be built as a module, if so, the module
will be called "rtc-rtca3".
comment "HID Sensor RTC drivers"
config RTC_DRV_HID_SENSOR_TIME
tristate "HID Sensor Time"
depends on USB_HID
depends on HID_SENSOR_HUB && IIO
select HID_SENSOR_IIO_COMMON
help
Say yes here to build support for the HID Sensors of type Time.
This drivers makes such sensors available as RTCs.
If this driver is compiled as a module, it will be named
rtc-hid-sensor-time.
config RTC_DRV_GOLDFISH
tristate "Goldfish Real Time Clock"
depends on HAS_IOMEM
@ -2132,4 +2128,18 @@ config RTC_DRV_S32G
This RTC module can be used as a wakeup source.
Please note that it is not battery-powered.
comment "HID Sensor RTC drivers"
config RTC_DRV_HID_SENSOR_TIME
tristate "HID Sensor Time"
depends on USB_HID
depends on HID_SENSOR_HUB && IIO
select HID_SENSOR_IIO_COMMON
help
Say yes here to build support for the HID Sensors of type Time.
This drivers makes such sensors available as RTCs.
If this driver is compiled as a module, it will be named
rtc-hid-sensor-time.
endif # RTC_CLASS

View File

@ -172,6 +172,7 @@ obj-$(CONFIG_RTC_DRV_SD2405AL) += rtc-sd2405al.o
obj-$(CONFIG_RTC_DRV_SD3078) += rtc-sd3078.o
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
obj-$(CONFIG_RTC_DRV_SPACEMIT_P1) += rtc-spacemit-p1.o
obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o

View File

@ -443,6 +443,29 @@ static int __rtc_set_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
else
err = rtc->ops->set_alarm(rtc->dev.parent, alarm);
/*
* Check for potential race described above. If the waiting for next
* second, and the second just ticked since the check above, either
*
* 1) It ticked after the alarm was set, and an alarm irq should be
* generated.
*
* 2) It ticked before the alarm was set, and alarm irq most likely will
* not be generated.
*
* While we cannot easily check for which of these two scenarios we
* are in, we can return -ETIME to signal that the timer has already
* expired, which is true in both cases.
*/
if ((scheduled - now) <= 1) {
err = __rtc_read_time(rtc, &tm);
if (err)
return err;
now = rtc_tm_to_time64(&tm);
if (scheduled <= now)
return -ETIME;
}
trace_rtc_set_alarm(rtc_tm_to_time64(&alarm->time), err);
return err;
}
@ -594,6 +617,10 @@ int rtc_update_irq_enable(struct rtc_device *rtc, unsigned int enabled)
rtc->uie_rtctimer.node.expires = ktime_add(now, onesec);
rtc->uie_rtctimer.period = ktime_set(1, 0);
err = rtc_timer_enqueue(rtc, &rtc->uie_rtctimer);
if (!err && rtc->ops && rtc->ops->alarm_irq_enable)
err = rtc->ops->alarm_irq_enable(rtc->dev.parent, 1);
if (err)
goto out;
} else {
rtc_timer_remove(rtc, &rtc->uie_rtctimer);
}

View File

@ -72,13 +72,6 @@ struct aml_rtc_data {
const struct aml_rtc_config *config;
};
static const struct regmap_config aml_rtc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = RTC_REAL_TIME,
};
static inline u32 gray_to_binary(u32 gray)
{
u32 bcd = gray;
@ -328,6 +321,13 @@ static int aml_rtc_probe(struct platform_device *pdev)
void __iomem *base;
int ret = 0;
const struct regmap_config aml_rtc_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = RTC_REAL_TIME,
};
rtc = devm_kzalloc(dev, sizeof(*rtc), GFP_KERNEL);
if (!rtc)
return -ENOMEM;

View File

@ -268,6 +268,7 @@ static int cpcap_rtc_probe(struct platform_device *pdev)
return err;
rtc->alarm_irq = platform_get_irq(pdev, 0);
rtc->alarm_enabled = true;
err = devm_request_threaded_irq(dev, rtc->alarm_irq, NULL,
cpcap_rtc_alarm_irq,
IRQF_TRIGGER_NONE | IRQF_ONESHOT,

View File

@ -112,48 +112,6 @@ convert_from_efi_time(efi_time_t *eft, struct rtc_time *wtime)
return true;
}
static int efi_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
efi_time_t eft;
efi_status_t status;
/*
* As of EFI v1.10, this call always returns an unsupported status
*/
status = efi.get_wakeup_time((efi_bool_t *)&wkalrm->enabled,
(efi_bool_t *)&wkalrm->pending, &eft);
if (status != EFI_SUCCESS)
return -EINVAL;
if (!convert_from_efi_time(&eft, &wkalrm->time))
return -EIO;
return rtc_valid_tm(&wkalrm->time);
}
static int efi_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
efi_time_t eft;
efi_status_t status;
convert_to_efi_time(&wkalrm->time, &eft);
/*
* XXX Fixme:
* As of EFI 0.92 with the firmware I have on my
* machine this call does not seem to work quite
* right
*
* As of v1.10, this call always returns an unsupported status
*/
status = efi.set_wakeup_time((efi_bool_t)wkalrm->enabled, &eft);
dev_warn(dev, "write status is %d\n", (int)status);
return status == EFI_SUCCESS ? 0 : -EINVAL;
}
static int efi_read_time(struct device *dev, struct rtc_time *tm)
{
efi_status_t status;
@ -188,17 +146,13 @@ static int efi_set_time(struct device *dev, struct rtc_time *tm)
static int efi_procfs(struct device *dev, struct seq_file *seq)
{
efi_time_t eft, alm;
efi_time_t eft;
efi_time_cap_t cap;
efi_bool_t enabled, pending;
struct rtc_device *rtc = dev_get_drvdata(dev);
memset(&eft, 0, sizeof(eft));
memset(&alm, 0, sizeof(alm));
memset(&cap, 0, sizeof(cap));
efi.get_time(&eft, &cap);
efi.get_wakeup_time(&enabled, &pending, &alm);
seq_printf(seq,
"Time\t\t: %u:%u:%u.%09u\n"
@ -214,26 +168,6 @@ static int efi_procfs(struct device *dev, struct seq_file *seq)
/* XXX fixme: convert to string? */
seq_printf(seq, "Timezone\t: %u\n", eft.timezone);
if (test_bit(RTC_FEATURE_ALARM, rtc->features)) {
seq_printf(seq,
"Alarm Time\t: %u:%u:%u.%09u\n"
"Alarm Date\t: %u-%u-%u\n"
"Alarm Daylight\t: %u\n"
"Enabled\t\t: %s\n"
"Pending\t\t: %s\n",
alm.hour, alm.minute, alm.second, alm.nanosecond,
alm.year, alm.month, alm.day,
alm.daylight,
enabled == 1 ? "yes" : "no",
pending == 1 ? "yes" : "no");
if (alm.timezone == EFI_UNSPECIFIED_TIMEZONE)
seq_puts(seq, "Timezone\t: unspecified\n");
else
/* XXX fixme: convert to string? */
seq_printf(seq, "Timezone\t: %u\n", alm.timezone);
}
/*
* now prints the capabilities
*/
@ -249,8 +183,6 @@ static int efi_procfs(struct device *dev, struct seq_file *seq)
static const struct rtc_class_ops efi_rtc_ops = {
.read_time = efi_read_time,
.set_time = efi_set_time,
.read_alarm = efi_read_alarm,
.set_alarm = efi_set_alarm,
.proc = efi_procfs,
};
@ -271,10 +203,6 @@ static int __init efi_rtc_probe(struct platform_device *dev)
platform_set_drvdata(dev, rtc);
rtc->ops = &efi_rtc_ops;
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
if (efi_rt_services_supported(EFI_RT_SUPPORTED_WAKEUP_SERVICES))
set_bit(RTC_FEATURE_ALARM_WAKEUP_ONLY, rtc->features);
else
clear_bit(RTC_FEATURE_ALARM, rtc->features);
device_init_wakeup(&dev->dev, true);

View File

@ -413,6 +413,7 @@ static int isl12022_setup_irq(struct device *dev, int irq)
if (ret)
return ret;
isl12022->irq_enabled = true;
ret = devm_request_threaded_irq(dev, irq, NULL,
isl12022_rtc_interrupt,
IRQF_SHARED | IRQF_ONESHOT,

View File

@ -72,7 +72,6 @@ static const struct regmap_config meson_rtc_peripheral_regmap_config = {
.val_bits = 32,
.reg_stride = 4,
.max_register = RTC_REG4,
.fast_io = true,
};
/* RTC front-end serialiser controls */

View File

@ -5,19 +5,104 @@
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/rtc.h>
#include <linux/tee_drv.h>
#define RTC_INFO_VERSION 0x1
#define TA_CMD_RTC_GET_INFO 0x0
#define TA_CMD_RTC_GET_TIME 0x1
#define TA_CMD_RTC_SET_TIME 0x2
#define TA_CMD_RTC_GET_OFFSET 0x3
#define TA_CMD_RTC_SET_OFFSET 0x4
#define TA_RTC_FEATURE_CORRECTION BIT(0)
#define TA_RTC_FEATURE_ALARM BIT(1)
#define TA_RTC_FEATURE_WAKEUP_ALARM BIT(2)
enum rtc_optee_pta_cmd {
/* PTA_CMD_RTC_GET_INFO - Get RTC information
*
* [out] memref[0] RTC buffer memory reference containing a struct pta_rtc_info
*/
PTA_CMD_RTC_GET_INF = 0x0,
/*
* PTA_CMD_RTC_GET_TIME - Get time from RTC
*
* [out] memref[0] RTC buffer memory reference containing a struct pta_rtc_time
*/
PTA_CMD_RTC_GET_TIME = 0x1,
/*
* PTA_CMD_RTC_SET_TIME - Set time from RTC
*
* [in] memref[0] RTC buffer memory reference containing a struct pta_rtc_time to be
* used as RTC time
*/
PTA_CMD_RTC_SET_TIME = 0x2,
/*
* PTA_CMD_RTC_GET_OFFSET - Get RTC offset
*
* [out] value[0].a RTC offset (signed 32bit value)
*/
PTA_CMD_RTC_GET_OFFSET = 0x3,
/*
* PTA_CMD_RTC_SET_OFFSET - Set RTC offset
*
* [in] value[0].a RTC offset to be set (signed 32bit value)
*/
PTA_CMD_RTC_SET_OFFSET = 0x4,
/*
* PTA_CMD_RTC_READ_ALARM - Read RTC alarm
*
* [out] memref[0] RTC buffer memory reference containing a struct pta_rtc_alarm
*/
PTA_CMD_RTC_READ_ALARM = 0x5,
/*
* PTA_CMD_RTC_SET_ALARM - Set RTC alarm
*
* [in] memref[0] RTC buffer memory reference containing a struct pta_rtc_alarm to be
* used as RTC alarm
*/
PTA_CMD_RTC_SET_ALARM = 0x6,
/*
* PTA_CMD_RTC_ENABLE_ALARM - Enable Alarm
*
* [in] value[0].a RTC IRQ flag (uint32_t), 0 to disable the alarm, 1 to enable
*/
PTA_CMD_RTC_ENABLE_ALARM = 0x7,
/*
* PTA_CMD_RTC_WAIT_ALARM - Get alarm event
*
* [out] value[0].a RTC wait alarm return status (uint32_t):
* - 0: No alarm event
* - 1: Alarm event occurred
* - 2: Alarm event canceled
*/
PTA_CMD_RTC_WAIT_ALARM = 0x8,
/*
* PTA_CMD_RTC_CANCEL_WAIT - Cancel wait for alarm event
*/
PTA_CMD_RTC_CANCEL_WAIT = 0x9,
/*
* PTA_CMD_RTC_SET_WAKE_ALARM_STATUS - Set RTC wake alarm status flag
*
* [in] value[0].a RTC IRQ wake alarm flag (uint32_t), 0 to disable the wake up
* capability, 1 to enable.
*/
PTA_CMD_RTC_SET_WAKE_ALARM_STATUS = 0xA,
};
enum rtc_wait_alarm_status {
WAIT_ALARM_RESET = 0x0,
WAIT_ALARM_ALARM_OCCURRED = 0x1,
WAIT_ALARM_CANCELED = 0x2,
};
struct optee_rtc_time {
u32 tm_sec;
@ -29,6 +114,12 @@ struct optee_rtc_time {
u32 tm_wday;
};
struct optee_rtc_alarm {
u8 enabled;
u8 pending;
struct optee_rtc_time time;
};
struct optee_rtc_info {
u64 version;
u64 features;
@ -41,15 +132,21 @@ struct optee_rtc_info {
* @dev: OP-TEE based RTC device.
* @ctx: OP-TEE context handler.
* @session_id: RTC TA session identifier.
* @session2_id: RTC wait alarm session identifier.
* @shm: Memory pool shared with RTC device.
* @features: Bitfield of RTC features
* @alarm_task: RTC wait alamr task.
* @rtc: RTC device.
*/
struct optee_rtc {
struct device *dev;
struct tee_context *ctx;
u32 session_id;
u32 session2_id;
struct tee_shm *shm;
u64 features;
struct task_struct *alarm_task;
struct rtc_device *rtc;
};
static int optee_rtc_readtime(struct device *dev, struct rtc_time *tm)
@ -60,7 +157,7 @@ static int optee_rtc_readtime(struct device *dev, struct rtc_time *tm)
struct tee_param param[4] = {0};
int ret;
inv_arg.func = TA_CMD_RTC_GET_TIME;
inv_arg.func = PTA_CMD_RTC_GET_TIME;
inv_arg.session = priv->session_id;
inv_arg.num_params = 4;
@ -97,19 +194,10 @@ static int optee_rtc_settime(struct device *dev, struct rtc_time *tm)
struct optee_rtc *priv = dev_get_drvdata(dev);
struct tee_ioctl_invoke_arg inv_arg = {0};
struct tee_param param[4] = {0};
struct optee_rtc_time optee_tm;
void *rtc_data;
struct optee_rtc_time *optee_tm;
int ret;
optee_tm.tm_sec = tm->tm_sec;
optee_tm.tm_min = tm->tm_min;
optee_tm.tm_hour = tm->tm_hour;
optee_tm.tm_mday = tm->tm_mday;
optee_tm.tm_mon = tm->tm_mon;
optee_tm.tm_year = tm->tm_year + 1900;
optee_tm.tm_wday = tm->tm_wday;
inv_arg.func = TA_CMD_RTC_SET_TIME;
inv_arg.func = PTA_CMD_RTC_SET_TIME;
inv_arg.session = priv->session_id;
inv_arg.num_params = 4;
@ -117,11 +205,17 @@ static int optee_rtc_settime(struct device *dev, struct rtc_time *tm)
param[0].u.memref.shm = priv->shm;
param[0].u.memref.size = sizeof(struct optee_rtc_time);
rtc_data = tee_shm_get_va(priv->shm, 0);
if (IS_ERR(rtc_data))
return PTR_ERR(rtc_data);
optee_tm = tee_shm_get_va(priv->shm, 0);
if (IS_ERR(optee_tm))
return PTR_ERR(optee_tm);
memcpy(rtc_data, &optee_tm, sizeof(struct optee_rtc_time));
optee_tm->tm_min = tm->tm_min;
optee_tm->tm_sec = tm->tm_sec;
optee_tm->tm_hour = tm->tm_hour;
optee_tm->tm_mday = tm->tm_mday;
optee_tm->tm_mon = tm->tm_mon;
optee_tm->tm_year = tm->tm_year + 1900;
optee_tm->tm_wday = tm->tm_wday;
ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
if (ret < 0 || inv_arg.ret != 0)
@ -140,7 +234,7 @@ static int optee_rtc_readoffset(struct device *dev, long *offset)
if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
return -EOPNOTSUPP;
inv_arg.func = TA_CMD_RTC_GET_OFFSET;
inv_arg.func = PTA_CMD_RTC_GET_OFFSET;
inv_arg.session = priv->session_id;
inv_arg.num_params = 4;
@ -165,7 +259,7 @@ static int optee_rtc_setoffset(struct device *dev, long offset)
if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
return -EOPNOTSUPP;
inv_arg.func = TA_CMD_RTC_SET_OFFSET;
inv_arg.func = PTA_CMD_RTC_SET_OFFSET;
inv_arg.session = priv->session_id;
inv_arg.num_params = 4;
@ -179,13 +273,228 @@ static int optee_rtc_setoffset(struct device *dev, long offset)
return 0;
}
static int optee_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct optee_rtc *priv = dev_get_drvdata(dev);
struct tee_ioctl_invoke_arg inv_arg = {0};
struct optee_rtc_alarm *optee_alarm;
struct tee_param param[1] = {0};
int ret;
if (!(priv->features & TA_RTC_FEATURE_ALARM))
return -EOPNOTSUPP;
inv_arg.func = PTA_CMD_RTC_READ_ALARM;
inv_arg.session = priv->session_id;
inv_arg.num_params = 1;
/* Fill invoke cmd params */
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_OUTPUT;
param[0].u.memref.shm = priv->shm;
param[0].u.memref.size = sizeof(struct optee_rtc_alarm);
ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
if (ret < 0 || inv_arg.ret != 0)
return ret ? ret : -EPROTO;
optee_alarm = tee_shm_get_va(priv->shm, 0);
if (IS_ERR(optee_alarm))
return PTR_ERR(optee_alarm);
if (param[0].u.memref.size != sizeof(*optee_alarm))
return -EPROTO;
alarm->enabled = optee_alarm->enabled;
alarm->pending = optee_alarm->pending;
alarm->time.tm_sec = optee_alarm->time.tm_sec;
alarm->time.tm_min = optee_alarm->time.tm_min;
alarm->time.tm_hour = optee_alarm->time.tm_hour;
alarm->time.tm_mday = optee_alarm->time.tm_mday;
alarm->time.tm_mon = optee_alarm->time.tm_mon;
alarm->time.tm_year = optee_alarm->time.tm_year - 1900;
alarm->time.tm_wday = optee_alarm->time.tm_wday;
alarm->time.tm_yday = rtc_year_days(alarm->time.tm_mday,
alarm->time.tm_mon,
alarm->time.tm_year);
return 0;
}
static int optee_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct optee_rtc *priv = dev_get_drvdata(dev);
struct tee_ioctl_invoke_arg inv_arg = {0};
struct optee_rtc_alarm *optee_alarm;
struct tee_param param[1] = {0};
int ret;
if (!(priv->features & TA_RTC_FEATURE_ALARM))
return -EOPNOTSUPP;
inv_arg.func = PTA_CMD_RTC_SET_ALARM;
inv_arg.session = priv->session_id;
inv_arg.num_params = 1;
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INPUT;
param[0].u.memref.shm = priv->shm;
param[0].u.memref.size = sizeof(struct optee_rtc_alarm);
optee_alarm = tee_shm_get_va(priv->shm, 0);
if (IS_ERR(optee_alarm))
return PTR_ERR(optee_alarm);
optee_alarm->enabled = alarm->enabled;
optee_alarm->pending = alarm->pending;
optee_alarm->time.tm_sec = alarm->time.tm_sec;
optee_alarm->time.tm_min = alarm->time.tm_min;
optee_alarm->time.tm_hour = alarm->time.tm_hour;
optee_alarm->time.tm_mday = alarm->time.tm_mday;
optee_alarm->time.tm_mon = alarm->time.tm_mon;
optee_alarm->time.tm_year = alarm->time.tm_year + 1900;
optee_alarm->time.tm_wday = alarm->time.tm_wday;
ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
if (ret < 0 || inv_arg.ret != 0)
return ret ? ret : -EPROTO;
return 0;
}
static int optee_rtc_enable_alarm(struct device *dev, unsigned int enabled)
{
struct optee_rtc *priv = dev_get_drvdata(dev);
struct tee_ioctl_invoke_arg inv_arg = {0};
struct tee_param param[1] = {0};
int ret;
if (!(priv->features & TA_RTC_FEATURE_ALARM))
return -EOPNOTSUPP;
inv_arg.func = PTA_CMD_RTC_ENABLE_ALARM;
inv_arg.session = priv->session_id;
inv_arg.num_params = 1;
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
param[0].u.value.a = (bool)enabled;
ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
if (ret < 0 || inv_arg.ret != 0)
return ret ? ret : -EPROTO;
return 0;
}
static const struct rtc_class_ops optee_rtc_ops = {
.read_time = optee_rtc_readtime,
.set_time = optee_rtc_settime,
.set_offset = optee_rtc_setoffset,
.read_offset = optee_rtc_readoffset,
.read_alarm = optee_rtc_read_alarm,
.set_alarm = optee_rtc_set_alarm,
.alarm_irq_enable = optee_rtc_enable_alarm,
};
static int optee_rtc_wait_alarm(struct device *dev, int *return_status)
{
struct optee_rtc *priv = dev_get_drvdata(dev);
struct tee_ioctl_invoke_arg inv_arg = {0};
struct tee_param param[1] = {0};
int ret;
if (!(priv->features & TA_RTC_FEATURE_ALARM))
return -EOPNOTSUPP;
inv_arg.func = PTA_CMD_RTC_WAIT_ALARM;
inv_arg.session = priv->session2_id;
inv_arg.num_params = 1;
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
if (ret < 0 || inv_arg.ret != 0)
return ret ? ret : -EPROTO;
*return_status = param[0].u.value.a;
return 0;
}
static int optee_rtc_cancel_wait_alarm(struct device *dev)
{
struct optee_rtc *priv = dev_get_drvdata(dev);
struct tee_ioctl_invoke_arg inv_arg = {0};
struct tee_param param[1] = {0};
int ret;
if (!(priv->features & TA_RTC_FEATURE_ALARM))
return -EOPNOTSUPP;
inv_arg.func = PTA_CMD_RTC_CANCEL_WAIT;
inv_arg.session = priv->session_id;
inv_arg.num_params = 0;
ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
if (ret < 0 || inv_arg.ret != 0)
return ret ? ret : -EPROTO;
return 0;
}
static int optee_rtc_set_alarm_wake_status(struct device *dev, bool status)
{
struct optee_rtc *priv = dev_get_drvdata(dev);
struct tee_ioctl_invoke_arg inv_arg = {0};
struct tee_param param[1] = {0};
int ret;
if (!(priv->features & TA_RTC_FEATURE_ALARM))
return -EOPNOTSUPP;
inv_arg.func = PTA_CMD_RTC_SET_WAKE_ALARM_STATUS;
inv_arg.session = priv->session_id;
inv_arg.num_params = 1;
param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT;
param[0].u.value.a = status;
ret = tee_client_invoke_func(priv->ctx, &inv_arg, param);
if (ret < 0 || inv_arg.ret != 0)
return ret ? ret : -EPROTO;
return 0;
}
static int optee_rtc_handle_alarm_event(void *data)
{
struct optee_rtc *priv = (struct optee_rtc *)data;
int wait_alarm_return_status = 0;
int ret;
while (!kthread_should_stop()) {
ret = optee_rtc_wait_alarm(priv->dev, &wait_alarm_return_status);
if (ret) {
dev_err(priv->dev, "Failed to wait for alarm: %d\n", ret);
return ret;
}
switch (wait_alarm_return_status) {
case WAIT_ALARM_ALARM_OCCURRED:
dev_dbg(priv->dev, "Alarm occurred\n");
rtc_update_irq(priv->rtc, 1, RTC_IRQF | RTC_AF);
break;
case WAIT_ALARM_CANCELED:
dev_dbg(priv->dev, "Alarm canceled\n");
break;
default:
dev_warn(priv->dev, "Unknown return status: %d\n",
wait_alarm_return_status);
break;
}
}
return 0;
}
static int optee_rtc_read_info(struct device *dev, struct rtc_device *rtc,
u64 *features)
{
@ -196,7 +505,7 @@ static int optee_rtc_read_info(struct device *dev, struct rtc_device *rtc,
struct optee_rtc_time *tm;
int ret;
inv_arg.func = TA_CMD_RTC_GET_INFO;
inv_arg.func = PTA_CMD_RTC_GET_INF;
inv_arg.session = priv->session_id;
inv_arg.num_params = 4;
@ -241,14 +550,13 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
static int optee_rtc_probe(struct device *dev)
{
struct tee_client_device *rtc_device = to_tee_client_device(dev);
struct tee_ioctl_open_session_arg sess_arg;
struct tee_ioctl_open_session_arg sess2_arg = {0};
struct tee_ioctl_open_session_arg sess_arg = {0};
struct optee_rtc *priv;
struct rtc_device *rtc;
struct tee_shm *shm;
int ret, err;
memset(&sess_arg, 0, sizeof(sess_arg));
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
@ -257,12 +565,14 @@ static int optee_rtc_probe(struct device *dev)
if (IS_ERR(rtc))
return PTR_ERR(rtc);
priv->rtc = rtc;
/* Open context with TEE driver */
priv->ctx = tee_client_open_context(NULL, optee_ctx_match, NULL, NULL);
if (IS_ERR(priv->ctx))
return -ENODEV;
/* Open session with rtc Trusted App */
/* Open first session with rtc Pseudo Trusted App */
export_uuid(sess_arg.uuid, &rtc_device->id.uuid);
sess_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
@ -274,6 +584,11 @@ static int optee_rtc_probe(struct device *dev)
}
priv->session_id = sess_arg.session;
/*
* Shared memory is used for passing an instance of either struct optee_rtc_info,
* struct optee_rtc_time or struct optee_rtc_alarm to OP-TEE service.
* The former is by definition large enough to cover both parameter cases.
*/
shm = tee_shm_alloc_kernel_buf(priv->ctx, sizeof(struct optee_rtc_info));
if (IS_ERR(shm)) {
dev_err(priv->dev, "tee_shm_alloc_kernel_buf failed\n");
@ -293,19 +608,70 @@ static int optee_rtc_probe(struct device *dev)
goto out_shm;
}
err = devm_rtc_register_device(rtc);
if (err)
/* Handle feature's related setup before registering to rtc framework */
if (priv->features & TA_RTC_FEATURE_ALARM) {
priv->alarm_task = kthread_create(optee_rtc_handle_alarm_event,
priv, "rtc_alarm_evt");
if (IS_ERR(priv->alarm_task)) {
dev_err(dev, "Failed to create alarm thread\n");
err = PTR_ERR(priv->alarm_task);
goto out_shm;
}
/*
* We must clear this bit after registering because rtc_register_device
* will set it if it sees that .set_offset is provided.
* In case of supported alarm feature on optee side, we create a kthread
* that will, in a new optee session, call a PTA interface "rtc_wait_alarm".
* This call return in case of alarm and in case of canceled alarm.
* The new optee session is therefore only needed in this case as we cannot
* use the same session for parallel calls to optee PTA.
* Hence one session is reserved to wait for alarms and the other to make
* standard calls to RTC PTA.
*/
/* Open second session with rtc Trusted App */
export_uuid(sess2_arg.uuid, &rtc_device->id.uuid);
sess2_arg.clnt_login = TEE_IOCTL_LOGIN_REE_KERNEL;
ret = tee_client_open_session(priv->ctx, &sess2_arg, NULL);
if (ret < 0 || sess2_arg.ret != 0) {
dev_err(dev, "tee_client_open_session failed, err: %x\n", sess2_arg.ret);
err = -EINVAL;
goto out_thrd;
}
priv->session2_id = sess2_arg.session;
if (priv->features & TA_RTC_FEATURE_WAKEUP_ALARM)
device_init_wakeup(dev, true);
}
err = devm_rtc_register_device(rtc);
if (err)
goto out_wk;
/*
* We must clear those bits after registering because registering a rtc_device
* will set them if it sees that .set_offset and .set_alarm are provided.
*/
if (!(priv->features & TA_RTC_FEATURE_CORRECTION))
clear_bit(RTC_FEATURE_CORRECTION, rtc->features);
if (!(priv->features & TA_RTC_FEATURE_ALARM))
clear_bit(RTC_FEATURE_ALARM, rtc->features);
/* Start the thread after the rtc is setup */
if (priv->alarm_task) {
wake_up_process(priv->alarm_task);
dev_dbg(dev, "Wait alarm thread successfully started\n");
}
return 0;
out_wk:
if (priv->features & TA_RTC_FEATURE_ALARM) {
device_init_wakeup(dev, false);
tee_client_close_session(priv->ctx, priv->session2_id);
}
out_thrd:
if (priv->features & TA_RTC_FEATURE_ALARM)
kthread_stop(priv->alarm_task);
out_shm:
tee_shm_free(priv->shm);
out_sess:
@ -320,12 +686,34 @@ static int optee_rtc_remove(struct device *dev)
{
struct optee_rtc *priv = dev_get_drvdata(dev);
if (priv->features & TA_RTC_FEATURE_ALARM) {
optee_rtc_cancel_wait_alarm(dev);
kthread_stop(priv->alarm_task);
device_init_wakeup(dev, false);
tee_client_close_session(priv->ctx, priv->session2_id);
}
tee_shm_free(priv->shm);
tee_client_close_session(priv->ctx, priv->session_id);
tee_client_close_context(priv->ctx);
return 0;
}
static int optee_rtc_suspend(struct device *dev)
{
int res = optee_rtc_set_alarm_wake_status(dev, device_may_wakeup(dev));
if (res) {
dev_err(dev, "Unable to transmit wakeup information to optee rtc\n");
return res;
}
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(optee_rtc_pm_ops, optee_rtc_suspend, NULL);
static const struct tee_client_device_id optee_rtc_id_table[] = {
{UUID_INIT(0xf389f8c8, 0x845f, 0x496c,
0x8b, 0xbe, 0xd6, 0x4b, 0xd2, 0x4c, 0x92, 0xfd)},
@ -341,6 +729,7 @@ static struct tee_client_driver optee_rtc_driver = {
.bus = &tee_bus_type,
.probe = optee_rtc_probe,
.remove = optee_rtc_remove,
.pm = pm_sleep_ptr(&optee_rtc_pm_ops),
},
};

View File

@ -42,6 +42,7 @@
#define PCF2127_BIT_CTRL2_AF BIT(4)
#define PCF2127_BIT_CTRL2_TSF2 BIT(5)
#define PCF2127_BIT_CTRL2_WDTF BIT(6)
#define PCF2127_BIT_CTRL2_MSF BIT(7)
/* Control register 3 */
#define PCF2127_REG_CTRL3 0x02
#define PCF2127_BIT_CTRL3_BLIE BIT(0)
@ -96,7 +97,8 @@
#define PCF2127_CTRL2_IRQ_MASK ( \
PCF2127_BIT_CTRL2_AF | \
PCF2127_BIT_CTRL2_WDTF | \
PCF2127_BIT_CTRL2_TSF2)
PCF2127_BIT_CTRL2_TSF2 | \
PCF2127_BIT_CTRL2_MSF)
#define PCF2127_MAX_TS_SUPPORTED 4
@ -606,6 +608,21 @@ static int pcf2127_watchdog_init(struct device *dev, struct pcf2127 *pcf2127)
set_bit(WDOG_HW_RUNNING, &pcf2127->wdd.status);
}
/*
* When using interrupt pin (INT A) as watchdog output, only allow
* watchdog interrupt (PCF2131_BIT_INT_WD_CD) and disable (mask) all
* other interrupts.
*/
if (pcf2127->cfg->type == PCF2131) {
ret = regmap_write(pcf2127->regmap,
PCF2131_REG_INT_A_MASK1,
PCF2131_BIT_INT_BLIE |
PCF2131_BIT_INT_BIE |
PCF2131_BIT_INT_AIE |
PCF2131_BIT_INT_SI |
PCF2131_BIT_INT_MI);
}
return devm_watchdog_register_device(dev, &pcf2127->wdd);
}

View File

@ -331,7 +331,7 @@ static const struct rtc_class_ops s3c_rtcops = {
.alarm_irq_enable = s3c_rtc_setaie,
};
static void s3c24xx_rtc_enable(struct s3c_rtc *info)
static void s3c6410_rtc_enable(struct s3c_rtc *info)
{
unsigned int con, tmp;
@ -361,19 +361,6 @@ static void s3c24xx_rtc_enable(struct s3c_rtc *info)
}
}
static void s3c24xx_rtc_disable(struct s3c_rtc *info)
{
unsigned int con;
con = readw(info->base + S3C2410_RTCCON);
con &= ~S3C2410_RTCCON_RTCEN;
writew(con, info->base + S3C2410_RTCCON);
con = readb(info->base + S3C2410_TICNT);
con &= ~S3C2410_TICNT_ENABLE;
writeb(con, info->base + S3C2410_TICNT);
}
static void s3c6410_rtc_disable(struct s3c_rtc *info)
{
unsigned int con;
@ -538,53 +525,21 @@ static int s3c_rtc_resume(struct device *dev)
#endif
static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume);
static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask)
{
rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
}
static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask)
{
rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF);
writeb(mask, info->base + S3C2410_INTP);
}
static const struct s3c_rtc_data s3c2410_rtc_data = {
.irq_handler = s3c24xx_rtc_irq,
.enable = s3c24xx_rtc_enable,
.disable = s3c24xx_rtc_disable,
};
static const struct s3c_rtc_data s3c2416_rtc_data = {
.irq_handler = s3c24xx_rtc_irq,
.enable = s3c24xx_rtc_enable,
.disable = s3c24xx_rtc_disable,
};
static const struct s3c_rtc_data s3c2443_rtc_data = {
.irq_handler = s3c24xx_rtc_irq,
.enable = s3c24xx_rtc_enable,
.disable = s3c24xx_rtc_disable,
};
static const struct s3c_rtc_data s3c6410_rtc_data = {
.needs_src_clk = true,
.irq_handler = s3c6410_rtc_irq,
.enable = s3c24xx_rtc_enable,
.enable = s3c6410_rtc_enable,
.disable = s3c6410_rtc_disable,
};
static const __maybe_unused struct of_device_id s3c_rtc_dt_match[] = {
{
.compatible = "samsung,s3c2410-rtc",
.data = &s3c2410_rtc_data,
}, {
.compatible = "samsung,s3c2416-rtc",
.data = &s3c2416_rtc_data,
}, {
.compatible = "samsung,s3c2443-rtc",
.data = &s3c2443_rtc_data,
}, {
.compatible = "samsung,s3c6410-rtc",
.data = &s3c6410_rtc_data,
}, {

View File

@ -21,25 +21,6 @@
#define S3C2443_RTCCON_TICSEL (1 << 4)
#define S3C64XX_RTCCON_TICEN (1 << 8)
#define S3C2410_TICNT S3C2410_RTCREG(0x44)
#define S3C2410_TICNT_ENABLE (1 << 7)
/* S3C2443: tick count is 15 bit wide
* TICNT[6:0] contains upper 7 bits
* TICNT1[7:0] contains lower 8 bits
*/
#define S3C2443_TICNT_PART(x) ((x & 0x7f00) >> 8)
#define S3C2443_TICNT1 S3C2410_RTCREG(0x4C)
#define S3C2443_TICNT1_PART(x) (x & 0xff)
/* S3C2416: tick count is 32 bit wide
* TICNT[6:0] contains bits [14:8]
* TICNT1[7:0] contains lower 8 bits
* TICNT2[16:0] contains upper 17 bits
*/
#define S3C2416_TICNT2 S3C2410_RTCREG(0x48)
#define S3C2416_TICNT2_PART(x) ((x & 0xffff8000) >> 15)
#define S3C2410_RTCALM S3C2410_RTCREG(0x50)
#define S3C2410_RTCALM_ALMEN (1 << 6)
#define S3C2410_RTCALM_YEAREN (1 << 5)

View File

@ -5,7 +5,9 @@
* Datasheet:
* https://image.dfrobot.com/image/data/TOY0021/SD2405AL%20datasheet%20(Angelo%20v0.1).pdf
*
* Copyright (C) 2024 Tóth János <gomba007@gmail.com>
* I2C slave address: 0x32
*
* Copyright (C) 2024-2025 Tóth János <gomba007@gmail.com>
*/
#include <linux/bcd.h>

View File

@ -0,0 +1,167 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for the RTC found in the SpacemiT P1 PMIC
*
* Copyright (C) 2025 by RISCstar Solutions Corporation. All rights reserved.
*/
#include <linux/bits.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/rtc.h>
#define MOD_NAME "spacemit-p1-rtc"
/*
* Six consecutive 1-byte registers hold the seconds, minutes, hours,
* day-of-month, month, and year (respectively).
*
* The range of values in these registers is:
* seconds 0-59
* minutes 0-59
* hours 0-59
* day 0-30 (struct tm is 1-31)
* month 0-11
* year years since 2000 (struct tm is since 1900)
*
* Note that the day and month must be converted after reading and
* before writing.
*/
#define RTC_TIME 0x0d /* Offset of the seconds register */
#define RTC_CTRL 0x1d
#define RTC_EN BIT(2)
/* Number of attempts to read a consistent time stamp before giving up */
#define RTC_READ_TRIES 20 /* At least 1 */
struct p1_rtc {
struct regmap *regmap;
struct rtc_device *rtc;
};
/*
* The P1 hardware documentation states that the register values are
* latched to ensure a consistent time snapshot within the registers,
* but these are in fact unstable due to a bug in the hardware design.
* So we loop until we get two identical readings.
*/
static int p1_rtc_read_time(struct device *dev, struct rtc_time *t)
{
struct p1_rtc *p1 = dev_get_drvdata(dev);
struct regmap *regmap = p1->regmap;
u32 count = RTC_READ_TRIES;
u8 seconds;
u8 time[6];
int ret;
if (!regmap_test_bits(regmap, RTC_CTRL, RTC_EN))
return -EINVAL; /* RTC is disabled */
ret = regmap_bulk_read(regmap, RTC_TIME, time, sizeof(time));
if (ret)
return ret;
do {
seconds = time[0];
ret = regmap_bulk_read(regmap, RTC_TIME, time, sizeof(time));
if (ret)
return ret;
} while (time[0] != seconds && --count);
if (!count)
return -EIO; /* Unable to get a consistent result */
t->tm_sec = time[0] & GENMASK(5, 0);
t->tm_min = time[1] & GENMASK(5, 0);
t->tm_hour = time[2] & GENMASK(4, 0);
t->tm_mday = (time[3] & GENMASK(4, 0)) + 1;
t->tm_mon = time[4] & GENMASK(3, 0);
t->tm_year = (time[5] & GENMASK(5, 0)) + 100;
return 0;
}
/*
* The P1 hardware documentation states that values in the registers are
* latched so when written they represent a consistent time snapshot.
* Nevertheless, this is not guaranteed by the implementation, so we must
* disable the RTC while updating it.
*/
static int p1_rtc_set_time(struct device *dev, struct rtc_time *t)
{
struct p1_rtc *p1 = dev_get_drvdata(dev);
struct regmap *regmap = p1->regmap;
u8 time[6];
int ret;
time[0] = t->tm_sec;
time[1] = t->tm_min;
time[2] = t->tm_hour;
time[3] = t->tm_mday - 1;
time[4] = t->tm_mon;
time[5] = t->tm_year - 100;
/* Disable the RTC to update; re-enable again when done */
ret = regmap_clear_bits(regmap, RTC_CTRL, RTC_EN);
if (ret)
return ret;
/* If something goes wrong, leave the RTC disabled */
ret = regmap_bulk_write(regmap, RTC_TIME, time, sizeof(time));
if (ret)
return ret;
return regmap_set_bits(regmap, RTC_CTRL, RTC_EN);
}
static const struct rtc_class_ops p1_rtc_class_ops = {
.read_time = p1_rtc_read_time,
.set_time = p1_rtc_set_time,
};
static int p1_rtc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rtc_device *rtc;
struct p1_rtc *p1;
p1 = devm_kzalloc(dev, sizeof(*p1), GFP_KERNEL);
if (!p1)
return -ENOMEM;
dev_set_drvdata(dev, p1);
p1->regmap = dev_get_regmap(dev->parent, NULL);
if (!p1->regmap)
return dev_err_probe(dev, -ENODEV, "failed to get regmap\n");
rtc = devm_rtc_allocate_device(dev);
if (IS_ERR(rtc))
return dev_err_probe(dev, PTR_ERR(rtc),
"error allocating device\n");
p1->rtc = rtc;
rtc->ops = &p1_rtc_class_ops;
rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
rtc->range_max = RTC_TIMESTAMP_END_2063;
clear_bit(RTC_FEATURE_ALARM, rtc->features);
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
return devm_rtc_register_device(rtc);
}
static struct platform_driver p1_rtc_driver = {
.probe = p1_rtc_probe,
.driver = {
.name = MOD_NAME,
},
};
module_platform_driver(p1_rtc_driver);
MODULE_DESCRIPTION("SpacemiT P1 RTC driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" MOD_NAME);

View File

@ -258,6 +258,7 @@ static int tps6586x_rtc_probe(struct platform_device *pdev)
irq_set_status_flags(rtc->irq, IRQ_NOAUTOEN);
rtc->irq_en = true;
ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
tps6586x_rtc_irq,
IRQF_ONESHOT,

View File

@ -669,7 +669,7 @@ static const struct i2c_device_id x1205_id[] = {
MODULE_DEVICE_TABLE(i2c, x1205_id);
static const struct of_device_id x1205_dt_ids[] = {
{ .compatible = "xircom,x1205", },
{ .compatible = "xicor,x1205", },
{},
};
MODULE_DEVICE_TABLE(of, x1205_dt_ids);

View File

@ -277,6 +277,10 @@ static irqreturn_t xlnx_rtc_interrupt(int irq, void *id)
static int xlnx_rtc_probe(struct platform_device *pdev)
{
struct xlnx_rtc_dev *xrtcdev;
bool is_alarm_set = false;
u32 pending_alrm_irq;
u32 current_time;
u32 alarm_time;
int ret;
xrtcdev = devm_kzalloc(&pdev->dev, sizeof(*xrtcdev), GFP_KERNEL);
@ -296,6 +300,17 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
if (IS_ERR(xrtcdev->reg_base))
return PTR_ERR(xrtcdev->reg_base);
/* Clear any pending alarm interrupts from previous kernel/boot */
pending_alrm_irq = readl(xrtcdev->reg_base + RTC_INT_STS) & RTC_INT_ALRM;
if (pending_alrm_irq)
writel(pending_alrm_irq, xrtcdev->reg_base + RTC_INT_STS);
/* Check if a valid alarm is already set from previous kernel/boot */
alarm_time = readl(xrtcdev->reg_base + RTC_ALRM);
current_time = readl(xrtcdev->reg_base + RTC_CUR_TM);
if (alarm_time > current_time && alarm_time != 0)
is_alarm_set = true;
xrtcdev->alarm_irq = platform_get_irq_byname(pdev, "alarm");
if (xrtcdev->alarm_irq < 0)
return xrtcdev->alarm_irq;
@ -337,6 +352,10 @@ static int xlnx_rtc_probe(struct platform_device *pdev)
xlnx_init_rtc(xrtcdev);
/* Re-enable alarm interrupt if a valid alarm was found */
if (is_alarm_set)
writel(RTC_INT_ALRM, xrtcdev->reg_base + RTC_INT_EN);
device_init_wakeup(&pdev->dev, true);
return devm_rtc_register_device(xrtcdev->rtc);