mirror of https://github.com/torvalds/linux.git
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:
commit
98906f9d85
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
};
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}, {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue