From a83ed9bb0b68e516dfed5ddec64a8dd885b25109 Mon Sep 17 00:00:00 2001 From: Dj Date: Sat, 10 Oct 2026 23:45:39 -0400 Subject: [PATCH] rtc: apple: Add Abbey PMU counter and wake alarm support Keep the Baku implementation intact and match the Abbey layout explicitly. Use named NVMEM cells, checked little-endian accesses and bounded stable reads. Mask and disarm before programming an alarm, preserve unrelated bits, and fail closed when the interrupt cannot be acknowledged. Manage wake IRQ teardown and provide the native APIF configuration and checked legacy DT migration. Exercise the production reader and alarm transaction without touching hardware. Signed-off-by: Dj --- .../bindings/rtc/apple,abbey-pmu-rtc.yaml | 51 +++ MAINTAINERS | 7 + arch/arm64/configs/aurora-apif.config | 6 + drivers/rtc/Kconfig | 9 + drivers/rtc/Makefile | 2 + drivers/rtc/rtc-apple-spmi-pmu.c | 380 ++++++++++++++++++ tools/aurora-apif/README.rst | 4 + tools/aurora-apif/prepare_guest.py | 8 + tools/testing/selftests/aurora-apif/Makefile | 2 +- tools/testing/selftests/aurora-apif/rtc.py | 98 +++++ 10 files changed, 566 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/rtc/apple,abbey-pmu-rtc.yaml create mode 100644 drivers/rtc/rtc-apple-spmi-pmu.c create mode 100755 tools/testing/selftests/aurora-apif/rtc.py diff --git a/Documentation/devicetree/bindings/rtc/apple,abbey-pmu-rtc.yaml b/Documentation/devicetree/bindings/rtc/apple,abbey-pmu-rtc.yaml new file mode 100644 index 0000000000000..7236884172833 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/apple,abbey-pmu-rtc.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0-only +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/apple,abbey-pmu-rtc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple Abbey SPMI PMU real-time clock + +maintainers: + - Dj + +description: | + The Abbey PMU exposes a 48-bit 65536 Hz counter and a signed 48-bit + wall-clock offset in units of 1/32768 second. Its alarm comparator uses + 32-bit counter seconds. NVMEM cells use little-endian byte order. + The one-byte alarm control has event bit 1, monitor bit 0 and enable + bit 6. The one-byte interrupt mask has the alarm mask in bit 0. + This layout is distinct from the Baku PMU RTC. + +allOf: + - $ref: rtc.yaml# + +properties: + compatible: + const: apple,abbey-pmu-rtc + + interrupts: + maxItems: 1 + description: Alarm event interrupt from the SPMI controller + + nvmem-cells: + minItems: 5 + maxItems: 5 + description: Counter (6), offset (6), alarm (4), control (1), mask (1) bytes + + nvmem-cell-names: + items: + - const: counter + - const: rtc_offset + - const: alarm + - const: alarm_ctrl + - const: irq_mask + + wakeup-source: true + +required: + - compatible + - nvmem-cells + - nvmem-cell-names + +unevaluatedProperties: false diff --git a/MAINTAINERS b/MAINTAINERS index 58cd37d740fd4..8631c190f0669 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4359,6 +4359,13 @@ F: rust/helpers/auxiliary.c F: rust/kernel/auxiliary.rs F: samples/rust/rust_driver_auxiliary.rs +AURORA APPLE SPMI PMU RTC +M: Dj +L: linux-rtc@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/rtc/apple,abbey-pmu-rtc.yaml +F: drivers/rtc/rtc-apple-spmi-pmu.c + AURORA PLATFORM MMIO INTERFACE M: Dj S: Maintained diff --git a/arch/arm64/configs/aurora-apif.config b/arch/arm64/configs/aurora-apif.config index 84234623ca4ce..7389821ef0447 100644 --- a/arch/arm64/configs/aurora-apif.config +++ b/arch/arm64/configs/aurora-apif.config @@ -12,3 +12,9 @@ CONFIG_PCIE_APPLE_APIF=y CONFIG_PM=y CONFIG_RUST=y CONFIG_APPLE_PMP=y +CONFIG_SPMI=y +CONFIG_SPMI_APPLE=y +CONFIG_NVMEM=y +CONFIG_NVMEM_APPLE_SPMI=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_APPLE_SPMI_PMU=y diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index e6077d109afb4..ed4619db0aa3d 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -2111,6 +2111,15 @@ config RTC_DRV_APPLE_PMU This driver can be built as the module rtc-apple-pmu. +config RTC_DRV_APPLE_SPMI_PMU + tristate "Apple Abbey SPMI PMU RTC" + depends on ARCH_APPLE || COMPILE_TEST + depends on NVMEM && OF + help + Access the Abbey PMU counter, signed wall-clock offset and wake alarm + through named NVMEM cells. This uses a different register layout from + the J700 Baku PMU RTC. + config RTC_DRV_MACSMC tristate "Apple Mac System Management Controller RTC" depends on MFD_MACSMC diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 729ff56c871d3..298f82ea9acb8 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -201,3 +201,5 @@ obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o obj-$(CONFIG_RTC_DRV_XGENE) += rtc-xgene.o obj-$(CONFIG_RTC_DRV_ZYNQMP) += rtc-zynqmp.o + +obj-$(CONFIG_RTC_DRV_APPLE_SPMI_PMU) += rtc-apple-spmi-pmu.o diff --git a/drivers/rtc/rtc-apple-spmi-pmu.c b/drivers/rtc/rtc-apple-spmi-pmu.c new file mode 100644 index 0000000000000..1416ecf58b83b --- /dev/null +++ b/drivers/rtc/rtc-apple-spmi-pmu.c @@ -0,0 +1,380 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Apple SPMI PMU RTC with a 48-bit counter and signed wall-clock offset. + * Counter and alarm accesses use named NVMEM cells supplied by the boot DT. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RTC_BYTES 6 +#define RTC_BITS (8 * RTC_BYTES) +/* Wall time and offset are in 1/32768 s; the counter runs twice as fast. */ +#define RTC_SEC_SHIFT 15 + +/* Abbey PMU alarm control bits; match this layout explicitly. */ +#define ALARM_EVENT BIT(1) +#define ALARM_MONITOR BIT(0) +#define ALARM_ENABLE BIT(6) +#define ALARM_IRQ_MASK BIT(0) + +struct apple_pmu_rtc { + struct device *dev; + struct rtc_device *rtc; + struct nvmem_cell *counter, *offset, *alarm, *ctrl, *irq_mask; + struct mutex lock; /* Serializes counter, offset and alarm transactions. */ + bool alarm_fault; +}; + +static int apple_pmu_rtc_read_cell(struct nvmem_cell *cell, void *val, size_t len) +{ + size_t got; + void *buf = nvmem_cell_read(cell, &got); + + if (IS_ERR(buf)) + return PTR_ERR(buf); + if (got != len) { + kfree(buf); + return -EIO; + } + memcpy(val, buf, len); + kfree(buf); + return 0; +} + +static int apple_pmu_rtc_write_cell(struct nvmem_cell *cell, void *val, size_t len) +{ + int ret = nvmem_cell_write(cell, val, len); + + return ret < 0 ? ret : ret == len ? 0 : -EIO; +} + +static int apple_pmu_rtc_write_u8(struct nvmem_cell *cell, u8 val) +{ + return apple_pmu_rtc_write_cell(cell, &val, sizeof(val)); +} + +static int apple_pmu_rtc_read_u48(struct nvmem_cell *cell, u64 *val) +{ + u8 bytes[8] = {}; + int ret = apple_pmu_rtc_read_cell(cell, bytes, RTC_BYTES); + + if (!ret) + *val = get_unaligned_le64(bytes); + return ret; +} + +/* Wall time in 1/32768 s: counter / 2 + offset. */ +static int apple_pmu_rtc_ticks(struct apple_pmu_rtc *prtc, u64 *ticks, u64 *offset) +{ + u64 a = 0, b = 0, off = 0; + int ret, tries = 3; + + do { + a = b; + ret = apple_pmu_rtc_read_u48(prtc->counter, &b); + if (ret) + return ret; + } while (a >> 16 != b >> 16 && --tries); + + if (!tries) + return -EIO; + + /* Do not use an inconsistent offset to construct wall time. */ + for (tries = 3; tries; tries--) { + u64 check = 0; + + ret = apple_pmu_rtc_read_u48(prtc->offset, &off); + if (!ret) + ret = apple_pmu_rtc_read_u48(prtc->offset, &check); + if (ret) + return ret; + if (off == check) + break; + } + if (!tries) + return -EIO; + *ticks = (b >> 1) + off; + if (offset) + *offset = off; + return 0; +} + +static int apple_pmu_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct apple_pmu_rtc *prtc = dev_get_drvdata(dev); + u64 ticks; + int ret; + + guard(mutex)(&prtc->lock); + ret = apple_pmu_rtc_ticks(prtc, &ticks, NULL); + + if (ret) + return ret; + rtc_time64_to_tm(sign_extend64(ticks, RTC_BITS - 1) >> RTC_SEC_SHIFT, tm); + return 0; +} + +static int apple_pmu_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct apple_pmu_rtc *prtc = dev_get_drvdata(dev); + u64 ctr = 0, off; + u8 bytes[8]; + int ret; + + guard(mutex)(&prtc->lock); + ret = apple_pmu_rtc_read_u48(prtc->counter, &ctr); + + if (ret) + return ret; + /* The set second begins now. */ + off = ((u64)rtc_tm_to_time64(tm) << RTC_SEC_SHIFT) - (ctr >> 1); + put_unaligned_le64(off, bytes); + return apple_pmu_rtc_write_cell(prtc->offset, bytes, RTC_BYTES); +} + +/* Arm or disarm the programmed alarm; caller holds prtc->lock. */ +static int apple_pmu_rtc_program(struct apple_pmu_rtc *prtc, bool enable) +{ + u8 ctrl, mask; + int ret; + + if (prtc->alarm_fault) + return -EIO; + ret = apple_pmu_rtc_read_cell(prtc->ctrl, &ctrl, 1); + if (ret) + return ret; + ret = apple_pmu_rtc_read_cell(prtc->irq_mask, &mask, 1); + if (ret) + return ret; + ret = apple_pmu_rtc_write_u8(prtc->irq_mask, mask | ALARM_IRQ_MASK); + if (ret) + return ret; + /* Disarm and acknowledge before changing the comparator or unmasking. */ + ret = apple_pmu_rtc_write_u8(prtc->ctrl, ctrl & ~ALARM_ENABLE); + if (ret) + return ret; + ctrl &= ~(ALARM_EVENT | ALARM_ENABLE); + ret = apple_pmu_rtc_write_u8(prtc->ctrl, + ctrl | ALARM_MONITOR | (enable ? ALARM_ENABLE : 0)); + if (ret || !enable) + return ret; + return apple_pmu_rtc_write_u8(prtc->irq_mask, mask & ~ALARM_IRQ_MASK); +} + +static int apple_pmu_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct apple_pmu_rtc *prtc = dev_get_drvdata(dev); + u64 ticks, off; + __le32 alarm = 0; + u8 ctrl = 0; + int ret; + + mutex_lock(&prtc->lock); + ret = apple_pmu_rtc_ticks(prtc, &ticks, &off); + if (!ret) + ret = apple_pmu_rtc_read_cell(prtc->alarm, &alarm, sizeof(alarm)); + if (!ret) + ret = apple_pmu_rtc_read_cell(prtc->ctrl, &ctrl, 1); + mutex_unlock(&prtc->lock); + if (ret) + return ret; + + wkalrm->enabled = !!(ctrl & ALARM_ENABLE); + wkalrm->pending = !!(ctrl & ALARM_EVENT); + /* The alarm is in counter seconds; one counter second is 1 s. */ + ticks = ((u64)le32_to_cpu(alarm) << RTC_SEC_SHIFT) + off; + rtc_time64_to_tm(sign_extend64(ticks, RTC_BITS - 1) >> RTC_SEC_SHIFT, + &wkalrm->time); + return 0; +} + +static int apple_pmu_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) +{ + struct apple_pmu_rtc *prtc = dev_get_drvdata(dev); + u64 off = 0, ticks; + int ret; + + __le32 alarm; + + mutex_lock(&prtc->lock); + ret = apple_pmu_rtc_program(prtc, false); + if (!ret) + ret = apple_pmu_rtc_read_u48(prtc->offset, &off); + if (!ret) { + /* Counter seconds; round up so the alarm never fires early. */ + ticks = ((u64)rtc_tm_to_time64(&wkalrm->time) << RTC_SEC_SHIFT) - off; + ticks &= GENMASK_ULL(RTC_BITS - 1, 0); + alarm = cpu_to_le32(DIV_ROUND_UP_ULL(ticks, 1ULL << RTC_SEC_SHIFT)); + ret = apple_pmu_rtc_write_cell(prtc->alarm, &alarm, sizeof(alarm)); + } + if (!ret) + ret = apple_pmu_rtc_program(prtc, wkalrm->enabled); + mutex_unlock(&prtc->lock); + return ret; +} + +static int apple_pmu_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct apple_pmu_rtc *prtc = dev_get_drvdata(dev); + int ret; + + mutex_lock(&prtc->lock); + ret = apple_pmu_rtc_program(prtc, enabled); + mutex_unlock(&prtc->lock); + return ret; +} + +static irqreturn_t apple_pmu_rtc_irq(int irq, void *data) +{ + struct apple_pmu_rtc *prtc = data; + u8 ctrl = 0; + int ret; + + mutex_lock(&prtc->lock); + ret = apple_pmu_rtc_read_cell(prtc->ctrl, &ctrl, 1); + if (ret) { + prtc->alarm_fault = true; + disable_irq_nosync(irq); + mutex_unlock(&prtc->lock); + dev_err_ratelimited(prtc->dev, "alarm status read failed: %d\n", ret); + return IRQ_HANDLED; + } + if (!(ctrl & ALARM_EVENT)) { + mutex_unlock(&prtc->lock); + return IRQ_NONE; + } + /* One-shot, as the RTC core expects: clear the event and disarm. */ + ret = apple_pmu_rtc_program(prtc, false); + if (ret) { + prtc->alarm_fault = true; + disable_irq_nosync(irq); + dev_err_ratelimited(prtc->dev, "alarm acknowledgment failed: %d\n", ret); + } + mutex_unlock(&prtc->lock); + rtc_update_irq(prtc->rtc, 1, RTC_AF | RTC_IRQF); + return IRQ_HANDLED; +} + +static const struct rtc_class_ops apple_pmu_rtc_ops = { + .read_time = apple_pmu_rtc_read_time, + .set_time = apple_pmu_rtc_set_time, + .read_alarm = apple_pmu_rtc_read_alarm, + .set_alarm = apple_pmu_rtc_set_alarm, + .alarm_irq_enable = apple_pmu_rtc_alarm_irq_enable, +}; + +static void apple_pmu_rtc_clear_wake(void *data) +{ + struct device *dev = data; + + dev_pm_clear_wake_irq(dev); + device_init_wakeup(dev, false); +} + +static void apple_pmu_rtc_stop(void *data) +{ + struct apple_pmu_rtc *prtc = data; + + guard(mutex)(&prtc->lock); + if (apple_pmu_rtc_program(prtc, false)) + dev_err(prtc->dev, "failed to disarm RTC alarm\n"); +} + +static int apple_pmu_rtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct apple_pmu_rtc *prtc; + int i, irq, nirq, ret; + + prtc = devm_kzalloc(dev, sizeof(*prtc), GFP_KERNEL); + if (!prtc) + return -ENOMEM; + prtc->dev = dev; + mutex_init(&prtc->lock); + platform_set_drvdata(pdev, prtc); + + struct { + struct nvmem_cell **cell; + const char *name; + } cells[] = { + { &prtc->counter, "counter" }, + { &prtc->offset, "rtc_offset" }, + { &prtc->alarm, "alarm" }, + { &prtc->ctrl, "alarm_ctrl" }, + { &prtc->irq_mask, "irq_mask" }, + }; + for (i = 0; i < ARRAY_SIZE(cells); i++) { + *cells[i].cell = devm_nvmem_cell_get(dev, cells[i].name); + if (IS_ERR(*cells[i].cell)) + return dev_err_probe(dev, PTR_ERR(*cells[i].cell), + "missing NVMEM cell %s\n", cells[i].name); + } + + prtc->rtc = devm_rtc_allocate_device(dev); + if (IS_ERR(prtc->rtc)) + return PTR_ERR(prtc->rtc); + prtc->rtc->ops = &apple_pmu_rtc_ops; + prtc->rtc->range_min = S64_MIN >> (RTC_SEC_SHIFT + (64 - RTC_BITS)); + prtc->rtc->range_max = S64_MAX >> (RTC_SEC_SHIFT + (64 - RTC_BITS)); + + nirq = platform_irq_count(pdev); + if (nirq < 0) + return nirq; + if (nirq > 1) + return -EINVAL; + if (!nirq) { + clear_bit(RTC_FEATURE_ALARM, prtc->rtc->features); + } else { + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + ret = apple_pmu_rtc_program(prtc, false); + if (ret) + return ret; + ret = devm_request_threaded_irq(dev, irq, NULL, apple_pmu_rtc_irq, + IRQF_ONESHOT, dev_name(dev), prtc); + if (ret) + return ret; + ret = device_init_wakeup(dev, true); + if (ret) + return ret; + ret = devm_add_action_or_reset(dev, apple_pmu_rtc_clear_wake, dev); + if (ret) + return ret; + ret = dev_pm_set_wake_irq(dev, irq); + if (ret) + return ret; + ret = devm_add_action_or_reset(dev, apple_pmu_rtc_stop, prtc); + if (ret) + return ret; + } + + return devm_rtc_register_device(prtc->rtc); +} + +static const struct of_device_id apple_pmu_rtc_of_match[] = { + { .compatible = "apple,abbey-pmu-rtc" }, + {} +}; +MODULE_DEVICE_TABLE(of, apple_pmu_rtc_of_match); + +static struct platform_driver apple_pmu_rtc_driver = { + .driver = { + .name = "apple-spmi-pmu-rtc", + .of_match_table = apple_pmu_rtc_of_match, + }, + .probe = apple_pmu_rtc_probe, +}; +module_platform_driver(apple_pmu_rtc_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Apple SPMI PMU RTC with wake alarm"); diff --git a/tools/aurora-apif/README.rst b/tools/aurora-apif/README.rst index 75e2468d29a75..66f14c6190fed 100644 --- a/tools/aurora-apif/README.rst +++ b/tools/aurora-apif/README.rst @@ -49,3 +49,7 @@ thermal policy or hardware qualification. Old live-session m5 trees lacking queue reservations cannot be converted into a safe stopped handoff merely by setting a boolean. Update the producer first, retain its original DTB, and qualify boot, storage durability, MSI, USB DMA, PMP and teardown separately. + +The matched T6050 Abbey RTC is migrated to ``apple,abbey-pmu-rtc`` only +when its five named cells describe the counter, offset, alarm, control +and interrupt mask. This leaves the separate Baku RTC binding unchanged. diff --git a/tools/aurora-apif/prepare_guest.py b/tools/aurora-apif/prepare_guest.py index 65f30177beabf..c8afab20624fa 100755 --- a/tools/aurora-apif/prepare_guest.py +++ b/tools/aurora-apif/prepare_guest.py @@ -214,6 +214,14 @@ def migrate(blob, pcie_layout=None, enable_pmp=False): put_strings(tree, path, "compatible", pci_layout(tree, path, pcie_layout)) if LEGACY_PCIE not in c: put_cells(tree, path, "aurora,apif", [handle]) + # Identify the legacy RTC layout only on matched T6050 guests. + if "apple,t6050" in compatible.get("/", []): + for path, c in compatible.items(): + if "apple,pmu-rtc" in c: + names = strings(tree, path, "nvmem-cell-names") + if names != ["counter", "rtc_offset", "alarm", "alarm_ctrl", "irq_mask"]: + raise ValueError(f"{path}: unknown PMU RTC cell layout") + put_strings(tree, path, "compatible", ["apple,abbey-pmu-rtc"]) # The old driver applied a hidden guard to the matched PMP consumer. # Record that existing ABI rule explicitly on its provider instead. for path, c in compatible.items(): diff --git a/tools/testing/selftests/aurora-apif/Makefile b/tools/testing/selftests/aurora-apif/Makefile index 678abc357e559..dd0508aa99bf8 100644 --- a/tools/testing/selftests/aurora-apif/Makefile +++ b/tools/testing/selftests/aurora-apif/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0-only -TEST_PROGS := apif_mmio.py pmp.py guest.py spmi.py +TEST_PROGS := apif_mmio.py pmp.py guest.py spmi.py rtc.py include ../lib.mk diff --git a/tools/testing/selftests/aurora-apif/rtc.py b/tools/testing/selftests/aurora-apif/rtc.py new file mode 100755 index 0000000000000..a5b02fb7f15d9 --- /dev/null +++ b/tools/testing/selftests/aurora-apif/rtc.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-only +"""Check the production PMU clock reader and alarm transaction.""" +from pathlib import Path +import shutil +import subprocess +import tempfile +from pmp import block + +ROOT = Path(__file__).resolve().parents[4] + + +def main(): + print("TAP version 13\n1..1", flush=True) + if not shutil.which("cc"): + print("ok 1 - PMU RTC transactions # SKIP cc unavailable") + return 4 + source = (ROOT / "drivers/rtc/rtc-apple-spmi-pmu.c").read_text() + program = r''' +#include +#include +#include +#include +#include +typedef uint64_t u64; +typedef uint8_t u8; +#define BIT(n) (1U << (n)) +#define RTC_SEC_SHIFT 15 +#define ALARM_EVENT BIT(1) +#define ALARM_MONITOR BIT(0) +#define ALARM_ENABLE BIT(6) +#define ALARM_IRQ_MASK BIT(0) +struct nvmem_cell { int id; }; +struct apple_pmu_rtc { + struct nvmem_cell *counter, *offset, *ctrl, *irq_mask; + bool alarm_fault; +}; +static int unstable_counter, unstable_offset, fault, writes, reads; +static u8 values[2] = {ALARM_EVENT | ALARM_ENABLE | BIT(7), BIT(3)}; +static int ids[8]; +static u8 out[8]; +static int apple_pmu_rtc_read_u48(struct nvmem_cell *c, u64 *v) { + reads++; + if (fault) return -EIO; + if (c->id == 2) *v = 10ULL << 16 | (unstable_counter ? (u64)reads << 16 : 0); + else *v = (1ULL << 48) - (2ULL << 15) + (unstable_offset ? reads : 0); + return 0; +} +static int apple_pmu_rtc_read_cell(struct nvmem_cell *c, void *v, size_t n) { + if (fault) return -EIO; + *(u8 *)v = values[c->id]; + return 0; +} +static int apple_pmu_rtc_write_u8(struct nvmem_cell *c, u8 v) { + ids[writes] = c->id; out[writes++] = v; + return 0; +} +''' + for name in ["ticks", "program"]: + program += block(source, "^static int apple_pmu_rtc_" + name + r"\(") + program += r''' +int main(void) { + struct nvmem_cell counter = {2}, offset = {3}, ctrl = {0}, mask = {1}; + struct apple_pmu_rtc rtc = {&counter, &offset, &ctrl, &mask, false}; + u64 ticks, off; + assert(apple_pmu_rtc_ticks(&rtc, &ticks, &off) == 0); + assert((ticks & ((1ULL << 48) - 1)) == 8ULL << 15); + unstable_counter = 1; reads = 0; + assert(apple_pmu_rtc_ticks(&rtc, &ticks, &off) == -EIO && reads == 3); + unstable_counter = 0; unstable_offset = 1; + assert(apple_pmu_rtc_ticks(&rtc, &ticks, &off) == -EIO); + unstable_offset = 0; fault = 1; + assert(apple_pmu_rtc_program(&rtc, true) == -EIO && writes == 0); + fault = 0; + assert(apple_pmu_rtc_program(&rtc, true) == 0 && writes == 4); + assert(ids[0] == 1 && out[0] == (BIT(3) | ALARM_IRQ_MASK)); + assert(ids[1] == 0 && !(out[1] & ALARM_ENABLE) && (out[1] & ALARM_EVENT)); + assert(ids[2] == 0 && (out[2] & ALARM_ENABLE) && !(out[2] & ALARM_EVENT)); + assert((out[2] & BIT(7)) && ids[3] == 1 && out[3] == BIT(3)); + rtc.alarm_fault = true; + assert(apple_pmu_rtc_program(&rtc, true) == -EIO && writes == 4); +} +''' + try: + with tempfile.TemporaryDirectory() as tmp: + src, binary = Path(tmp) / "test.c", Path(tmp) / "test" + src.write_text(program) + subprocess.run(["cc", "-std=gnu11", str(src), "-o", str(binary)], check=True) + subprocess.run([str(binary)], check=True) + except subprocess.CalledProcessError: + print("not ok 1 - PMU RTC transactions") + return 1 + print("ok 1 - PMU RTC transactions") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main())