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())