From 29e9a11646e9626dc362daa4325da45769a27757 Mon Sep 17 00:00:00 2001 From: kettenis Date: Wed, 2 Mar 2022 12:44:48 +0000 Subject: [PATCH] Add RTC support to aplsmc(4). The SMC firmware distributed with macOS 12.x has a method to read the counter that forms the base of the RTC. This seems to be the preferred way to access the RTC going forward. The RTC offset is still stored in the SPMI PMU, but we can use the nvmem interface to read and write that. This makes the RTC work on systems with the M1 Pro/Max SoC. Sprinkle some #ifdef SMALL_KERNEL around and enable the driver on RAMDISK kernels. ok patrick@ --- sys/arch/arm64/conf/RAMDISK | 3 +- sys/arch/arm64/dev/aplsmc.c | 197 +++++++++++++++++++++++++----------- 2 files changed, 140 insertions(+), 60 deletions(-) diff --git a/sys/arch/arm64/conf/RAMDISK b/sys/arch/arm64/conf/RAMDISK index 65d254e74c7..ce2a7bdb008 100644 --- a/sys/arch/arm64/conf/RAMDISK +++ b/sys/arch/arm64/conf/RAMDISK @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISK,v 1.168 2022/02/25 13:51:02 visa Exp $ +# $OpenBSD: RAMDISK,v 1.169 2022/03/02 12:44:48 kettenis Exp $ machine arm64 maxusers 4 @@ -114,6 +114,7 @@ aplpcie* at fdt? pci* at aplpcie? aplpinctrl* at fdt? early 1 aplpmgr* at fdt? early 1 +aplsmc* at fdt? aplspi* at fdt? aplhidev* at spi? aplkbd* at aplhidev? diff --git a/sys/arch/arm64/dev/aplsmc.c b/sys/arch/arm64/dev/aplsmc.c index 23d6d9f10d4..bfb466a67a8 100644 --- a/sys/arch/arm64/dev/aplsmc.c +++ b/sys/arch/arm64/dev/aplsmc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aplsmc.c,v 1.8 2022/02/22 20:37:19 kettenis Exp $ */ +/* $OpenBSD: aplsmc.c,v 1.9 2022/03/02 12:44:48 kettenis Exp $ */ /* * Copyright (c) 2021 Mark Kettenis * @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -88,6 +89,9 @@ struct aplsmc_softc { struct gpio_controller sc_gc; + int sc_rtc_node; + struct todr_chip_handle sc_todr; + struct aplsmc_sensor *sc_smcsensors[APLSMC_MAX_SENSORS]; struct ksensor sc_sensors[APLSMC_MAX_SENSORS]; int sc_nsensors; @@ -96,6 +100,8 @@ struct aplsmc_softc { struct aplsmc_softc *aplsmc_sc; +#ifndef SMALL_KERNEL + struct aplsmc_sensor aplsmc_sensors[] = { { "ACDI", "ui16", SENSOR_INDICATOR, 1, "power supply" }, { "B0RM", "ui16", SENSOR_AMPHOUR, 1000, "remaining battery capacity", @@ -120,6 +126,8 @@ struct aplsmc_sensor aplsmc_sensors[] = { { "VD0R", "flt ", SENSOR_VOLTS_DC, 1000000, "input" }, }; +#endif + int aplsmc_match(struct device *, void *, void *); void aplsmc_attach(struct device *, struct device *, void *); @@ -131,67 +139,15 @@ struct cfdriver aplsmc_cd = { NULL, "aplsmc", DV_DULL }; -#if NAPM > 0 -int -aplsmc_apminfo(struct apm_power_info *info) -{ - struct aplsmc_sensor *sensor; - struct ksensor *ksensor; - struct aplsmc_softc *sc = aplsmc_sc; - int remaining = -1, capacity = -1, i; - - info->battery_state = APM_BATT_UNKNOWN; - info->ac_state = APM_AC_UNKNOWN; - info->battery_life = 0; - info->minutes_left = -1; - - for (i = 0; i < sc->sc_nsensors; i++) { - sensor = sc->sc_smcsensors[i]; - ksensor = &sc->sc_sensors[i]; - - if (ksensor->flags & SENSOR_FUNKNOWN) - continue; - - if (strcmp(sensor->key, "ACDI") == 0) { - info->ac_state = ksensor->value ? - APM_AC_ON : APM_AC_OFF; - } else if (strcmp(sensor->key, "B0RM") == 0) - remaining = ksensor->value; - else if (strcmp(sensor->key, "B0FC") == 0) - capacity = ksensor->value; - else if ((strcmp(sensor->key, "B0TE") == 0) && - (ksensor->value != 0xffff)) - info->minutes_left = ksensor->value; - else if ((strcmp(sensor->key, "B0TF") == 0) && - (ksensor->value != 0xffff)) { - info->battery_state = APM_BATT_CHARGING; - info->minutes_left = ksensor->value; - } - } - - /* calculate remaining battery if we have sane values */ - if (remaining > -1 && capacity > 0) { - info->battery_life = ((remaining * 100) / capacity); - if (info->battery_state != APM_BATT_CHARGING) { - if (info->battery_life > 50) - info->battery_state = APM_BATT_HIGH; - else if (info->battery_life > 25) - info->battery_state = APM_BATT_LOW; - else - info->battery_state = APM_BATT_CRITICAL; - } - } - - return 0; -} -#endif - void aplsmc_callback(void *, uint64_t); int aplsmc_send_cmd(struct aplsmc_softc *, uint16_t, uint32_t, uint16_t); int aplsmc_wait_cmd(struct aplsmc_softc *sc); int aplsmc_read_key(struct aplsmc_softc *, uint32_t, void *, size_t); void aplsmc_refresh_sensors(void *); +int aplsmc_apminfo(struct apm_power_info *); void aplsmc_set_pin(void *, uint32_t *, int); +int aplsmc_gettime(struct todr_chip_handle *, struct timeval *); +int aplsmc_settime(struct todr_chip_handle *, struct timeval *); void aplsmc_reset(void); int @@ -208,7 +164,9 @@ aplsmc_attach(struct device *parent, struct device *self, void *aux) struct aplsmc_softc *sc = (struct aplsmc_softc *)self; struct fdt_attach_args *faa = aux; int error, node; +#ifndef SMALL_KERNEL int i; +#endif if (faa->fa_nreg < 1) { printf(": no registers\n"); @@ -263,6 +221,20 @@ aplsmc_attach(struct device *parent, struct device *self, void *aux) gpio_controller_register(&sc->sc_gc); } + node = OF_getnodebyname(faa->fa_node, "rtc"); + if (node) { + sc->sc_rtc_node = node; + sc->sc_todr.cookie = sc; + sc->sc_todr.todr_gettime = aplsmc_gettime; + sc->sc_todr.todr_settime = aplsmc_settime; + todr_attach(&sc->sc_todr); + } + + aplsmc_sc = sc; + cpuresetfn = aplsmc_reset; + +#ifndef SMALL_KERNEL + for (i = 0; i < nitems(aplsmc_sensors); i++) { struct smc_key_info info; @@ -304,12 +276,11 @@ aplsmc_attach(struct device *parent, struct device *self, void *aux) sensordev_install(&sc->sc_sensordev); sensor_task_register(sc, aplsmc_refresh_sensors, 5); - aplsmc_sc = sc; - cpuresetfn = aplsmc_reset; - #if NAPM > 0 apm_setinfohook(aplsmc_apminfo); #endif + +#endif } void @@ -388,6 +359,8 @@ aplsmc_write_key(struct aplsmc_softc *sc, uint32_t key, void *data, size_t len) return aplsmc_wait_cmd(sc); } +#ifndef SMALL_KERNEL + void aplsmc_refresh_sensors(void *arg) { @@ -454,6 +427,64 @@ aplsmc_refresh_sensors(void *arg) } } +#if NAPM > 0 + +int +aplsmc_apminfo(struct apm_power_info *info) +{ + struct aplsmc_sensor *sensor; + struct ksensor *ksensor; + struct aplsmc_softc *sc = aplsmc_sc; + int remaining = -1, capacity = -1, i; + + info->battery_state = APM_BATT_UNKNOWN; + info->ac_state = APM_AC_UNKNOWN; + info->battery_life = 0; + info->minutes_left = -1; + + for (i = 0; i < sc->sc_nsensors; i++) { + sensor = sc->sc_smcsensors[i]; + ksensor = &sc->sc_sensors[i]; + + if (ksensor->flags & SENSOR_FUNKNOWN) + continue; + + if (strcmp(sensor->key, "ACDI") == 0) { + info->ac_state = ksensor->value ? + APM_AC_ON : APM_AC_OFF; + } else if (strcmp(sensor->key, "B0RM") == 0) + remaining = ksensor->value; + else if (strcmp(sensor->key, "B0FC") == 0) + capacity = ksensor->value; + else if ((strcmp(sensor->key, "B0TE") == 0) && + (ksensor->value != 0xffff)) + info->minutes_left = ksensor->value; + else if ((strcmp(sensor->key, "B0TF") == 0) && + (ksensor->value != 0xffff)) { + info->battery_state = APM_BATT_CHARGING; + info->minutes_left = ksensor->value; + } + } + + /* calculate remaining battery if we have sane values */ + if (remaining > -1 && capacity > 0) { + info->battery_life = ((remaining * 100) / capacity); + if (info->battery_state != APM_BATT_CHARGING) { + if (info->battery_life > 50) + info->battery_state = APM_BATT_HIGH; + else if (info->battery_life > 25) + info->battery_state = APM_BATT_LOW; + else + info->battery_state = APM_BATT_CRITICAL; + } + } + + return 0; +} + +#endif +#endif + void aplsmc_set_pin(void *cookie, uint32_t *cells, int val) { @@ -476,6 +507,54 @@ aplsmc_set_pin(void *cookie, uint32_t *cells, int val) aplsmc_write_key(sc, key, &data, sizeof(data)); } +#define RTC_OFFSET_LEN 6 +#define SMC_CLKM_LEN 6 + +int +aplsmc_gettime(struct todr_chip_handle *handle, struct timeval *tv) +{ + struct aplsmc_softc *sc = handle->cookie; + uint8_t data[8] = {}; + uint64_t offset, time; + int error; + + error = nvmem_read_cell(sc->sc_rtc_node, "rtc_offset", &data, + RTC_OFFSET_LEN); + if (error) + return error; + offset = lemtoh64(data); + + error = aplsmc_read_key(sc, SMC_KEY("CLKM"), &data, SMC_CLKM_LEN); + if (error) + return error; + time = lemtoh64(data) + offset; + + tv->tv_sec = (time >> 15); + tv->tv_usec = (((time & 0x7fff) * 1000000) >> 15); + return 0; +} + +int +aplsmc_settime(struct todr_chip_handle *handle, struct timeval *tv) +{ + struct aplsmc_softc *sc = handle->cookie; + uint8_t data[8] = {}; + uint64_t offset, time; + int error; + + error = aplsmc_read_key(sc, SMC_KEY("CLKM"), &data, SMC_CLKM_LEN); + if (error) + return error; + + time = ((uint64_t)tv->tv_sec << 15); + time |= ((uint64_t)tv->tv_usec << 15) / 1000000; + offset = time - lemtoh64(data); + + htolem64(data, offset); + return nvmem_write_cell(sc->sc_rtc_node, "rtc_offset", &data, + RTC_OFFSET_LEN); +} + void aplsmc_reset(void) { -- 2.20.1