Index: sys/dev/acpica/acpi_rtc.c =================================================================== --- /dev/null +++ sys/dev/acpica/acpi_rtc.c @@ -0,0 +1,414 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021 Takanori Watanabe + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_acpi.h" +#include +#include +#include +#include +#include + +#include + +#include "acpi_if.h" +#include "clock_if.h" +#include +#include +#include + +#define ACPI_RTC_AC_WAKE 1 +#define ACPI_RTC_DC_WAKE 2 +#define ACPI_RTC_RTC 4 +#define ACPI_RTC_RTC_MSEC 8 +#define ACPI_RTC_GWS 0x10 +#define ACPI_RTC_S4AC 0x20 +#define ACPI_RTC_S5AC 0x40 +#define ACPI_RTC_S4DC 0x80 +#define ACPI_RTC_S5DC 0x100 +#define ACPI_RTC_TZ_UNSPEC 0x7ff + +struct acpi_rtc_ct{ + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t valid; + uint16_t milisecond; + int16_t TimeZone; + uint8_t Daylight; + uint8_t pad[3]; +}__attribute((__packed__)); + +static char *acpi_rtc_id[] = {"ACPI000E", NULL}; +struct acpi_rtc_softc { + int device_cap; + int tzmin; +}; +static ACPI_STATUS acpi_rtc_grt(device_t dev, struct acpi_rtc_ct *act) +{ + ACPI_BUFFER buf; + ACPI_STATUS status ; + ACPI_OBJECT *ao; + + buf.Length = ACPI_ALLOCATE_BUFFER; + buf.Pointer = NULL; + status = AcpiEvaluateObject(acpi_get_handle(dev), "_GRT", NULL, &buf); + if(ACPI_FAILURE(status)){ + goto error; + } + + ao = buf.Pointer; + + if(ao->Type != ACPI_TYPE_BUFFER){ + status = AE_ERROR; + }else{ + memcpy(act, ao->Buffer.Pointer, sizeof(*act)); + } + + AcpiOsFree(buf.Pointer); +error: + return status; +} + +static ACPI_STATUS acpi_rtc_arg1_call(device_t dev, char *name, int arg, + int *rv) +{ + ACPI_OBJECT ao, reto; + ACPI_STATUS status; + ACPI_OBJECT_LIST aol; + ACPI_BUFFER buf; + + ao.Type = ACPI_TYPE_INTEGER; + ao.Integer.Value = arg; + aol.Count = 1; + aol.Pointer = &ao; + buf.Length = sizeof(reto); + buf.Pointer = &reto; + status = AcpiEvaluateObjectTyped(acpi_get_handle(dev), name, + &aol, &buf, ACPI_TYPE_INTEGER); + + *rv = reto.Integer.Value; + + return status; +} + +static ACPI_STATUS acpi_rtc_arg2_call(device_t dev, char *name, int arg1, + int arg2, int *rv) +{ + ACPI_OBJECT ao[2], reto; + ACPI_STATUS status; + ACPI_OBJECT_LIST aol; + ACPI_BUFFER buf; + + ao[0].Type = ACPI_TYPE_INTEGER; + ao[0].Integer.Value = arg1; + ao[1].Type = ACPI_TYPE_INTEGER; + ao[1].Integer.Value = arg2; + aol.Count = 2; + aol.Pointer = ao; + buf.Length = sizeof(reto); + buf.Pointer = &reto; + status = AcpiEvaluateObjectTyped(acpi_get_handle(dev), name, + &aol, &buf, ACPI_TYPE_INTEGER); + + *rv = reto.Integer.Value; + + return status; +} + +static int acpi_rtc_gettime(device_t dev, struct timespec *ts) +{ + struct clocktime ct; + struct acpi_rtc_ct grt; + int rv; + if (ACPI_FAILURE(acpi_rtc_grt(dev, &grt))) + return EINVAL; + + if (grt.valid == 0) { + return EINVAL; + } + + ct.year = grt.year; + ct.mon = grt.month; + ct.day = grt.day; + ct.hour = grt.hour; + ct.min = grt.minute; + ct.sec = grt.second; + ct.dow = 0; /* ignored.*/ + ct.nsec = grt.milisecond * 1000 * 1000; + + rv = clock_ct_to_ts(&ct, ts); + if (rv != 0) + return rv; + + /* Return UTC time when TZ is set.*/ + if (grt.TimeZone != ACPI_RTC_TZ_UNSPEC) + ts->tv_sec += grt.TimeZone * 60; + + return 0; +} + +static int acpi_rtc_settime(device_t dev, struct timespec *ts) +{ + struct acpi_rtc_ct srt; + struct clocktime ct; + ACPI_OBJECT ao, reto; + ACPI_STATUS status; + ACPI_OBJECT_LIST aol; + ACPI_BUFFER buf; + + struct acpi_rtc_softc *sc = device_get_softc(dev); + + memset(&srt, 0, sizeof(srt)); + + /* Set Local time when TZ is set.*/ + if ( sc->tzmin != ACPI_RTC_TZ_UNSPEC ) + ts->tv_sec -= sc->tzmin * 60; + + clock_ts_to_ct(ts, &ct); + srt.year = ct.year; + srt.month = ct.mon; + srt.day = ct.day; + srt.hour = ct.hour; + srt.minute = ct.min; + srt.second = ct.sec; + srt.milisecond = ct.nsec / 1000 / 1000 ; + srt.TimeZone = 0x7ff; + srt.Daylight = 0; + + ao.Type = ACPI_TYPE_BUFFER; + ao.Buffer.Length = sizeof(srt); + ao.Buffer.Pointer = (void*)&srt; + aol.Count = 1; + aol.Pointer = &ao; + buf.Length = sizeof(reto); + buf.Pointer = &reto; + status = AcpiEvaluateObjectTyped(acpi_get_handle(dev), "_SRT", + &aol, &buf, ACPI_TYPE_INTEGER); + + if (ACPI_FAILURE(status)){ + return (EINVAL); + } + + if(reto.Integer.Value != 0){ + return (EINVAL); + } + + return (0); +} +static void acpi_rtc_notify_handler(ACPI_HANDLE h, UINT32 notify, void *ctx) +{ +#if 0 + device_t dev = (device_t) ctx; + device_printf(dev, "Notify %d\n"); +#endif + acpi_UserNotify("RTCWAKE", h, notify); +} + +static int acpi_rtc_probe(device_t dev) +{ + int ret; + + ret = ACPI_ID_PROBE(device_get_parent(dev), dev, acpi_rtc_id, NULL); + if (ret <= 0) { + device_set_desc(dev, "Date and Alarm Device"); + } + + return (ret); +} +static int acpi_rtc_wakestate(SYSCTL_HANDLER_ARGS) +{ + device_t dev ; + int arg, val; + ACPI_STATUS status; + int error; + + dev = (device_t) oidp->oid_arg1; + arg = oidp->oid_arg2; + + if (ACPI_FAILURE(acpi_rtc_arg1_call(dev, "_GWS", arg, &val))) + return EINVAL; + error = sysctl_handle_int(oidp, &val, 0,req); + if ( error || ! req->newptr) + return error; + + status = acpi_rtc_arg1_call(dev, "_CWS", arg, &val); + if(ACPI_FAILURE(status) || (val != 0)) + return EINVAL; + + return 0; +} +static int acpi_rtc_timer(SYSCTL_HANDLER_ARGS) +{ + device_t dev ; + int arg, val; + ACPI_STATUS status; + int error; + + dev = (device_t) oidp->oid_arg1; + arg = oidp->oid_arg2; + + if (ACPI_FAILURE(acpi_rtc_arg1_call(dev, "_TIV", arg, &val))) + return EINVAL; + error = sysctl_handle_int(oidp, &val, 0,req); + if ( error || ! req->newptr) + return error; + + status = acpi_rtc_arg2_call(dev, "_STV", arg, val, &val); + + if(ACPI_FAILURE(status) || (val != 0)) + return EINVAL; + + return 0; +} +static int acpi_rtc_policy(SYSCTL_HANDLER_ARGS) +{ + device_t dev ; + int arg, val; + ACPI_STATUS status; + int error; + + dev = (device_t) oidp->oid_arg1; + arg = oidp->oid_arg2; + + if (ACPI_FAILURE(acpi_rtc_arg1_call(dev, "_TIP", arg, &val))) + return EINVAL; + error = sysctl_handle_int(oidp, &val, 0,req); + if ( error || ! req->newptr) + return error; + + status = acpi_rtc_arg2_call(dev, "_STP", arg, val, &val); + + if(ACPI_FAILURE(status) || (val != 0)) + return EINVAL; + + return 0; +} +static int acpi_rtc_attach(device_t dev) +{ + struct acpi_rtc_softc *sc = device_get_softc(dev); + struct acpi_rtc_ct act; + struct sysctl_ctx_list *sysctl_ctx = device_get_sysctl_ctx(dev); + struct sysctl_oid *sysctl_tree = device_get_sysctl_tree(dev); + + if(ACPI_FAILURE(acpi_GetInteger(acpi_get_handle(dev), "_GCP", &sc->device_cap))){ + return ENXIO; + } + + device_printf(dev, "Capacity %b\n", sc->device_cap, "\020\1AC\2DC\3RTC\4mS\5GWSS45\6S4AC\7S5AC\010S4DC\011S5DC"); + if(sc->device_cap & ACPI_RTC_RTC){ + acpi_rtc_grt(dev, &act); + + sc->tzmin = act.TimeZone; + if(act.Daylight) + device_printf (dev, "Warning: Daylight time affect the value\n"); + + clock_register_flags(dev, + (sc->device_cap & ACPI_RTC_RTC_MSEC) ? 1000 : 1000000, + (sc->tzmin == ACPI_RTC_TZ_UNSPEC) ? 0 : + (CLOCKF_GETTIME_NO_ADJ | CLOCKF_SETTIME_NO_ADJ)); + + } + AcpiInstallNotifyHandler(acpi_get_handle(dev), + ACPI_ALL_NOTIFY, + acpi_rtc_notify_handler, dev); + + acpi_wake_set_enable(dev, 1); + SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "ac_wake_status", + CTLTYPE_INT | CTLFLAG_RW |CTLFLAG_MPSAFE, + dev, 0, acpi_rtc_wakestate, "I", + "AC Wake status. write any value to clear"); + + SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "dc_wake_status", + CTLTYPE_INT | CTLFLAG_RW |CTLFLAG_MPSAFE, + dev, 1, acpi_rtc_wakestate, "I", + "DC Wake status. write any value to clear"); + + SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "ac_wake_timer", + CTLTYPE_INT | CTLFLAG_RW |CTLFLAG_MPSAFE, + dev, 0, acpi_rtc_timer, "I", + "AC Wake count down timer in second. -1 means timer expired"); + + SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "dc_wake_timer", + CTLTYPE_INT | CTLFLAG_RW |CTLFLAG_MPSAFE, + dev, 1, acpi_rtc_timer, "I", + "DC Wake count down timer in second. -1 means timer expired."); + + SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "ac_wake_policy", + CTLTYPE_INT | CTLFLAG_RW |CTLFLAG_MPSAFE, + dev, 0, acpi_rtc_policy, "I", + "AC Wake policy"); + + SYSCTL_ADD_PROC(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree), + OID_AUTO, "dc_wake_policy", + CTLTYPE_INT | CTLFLAG_RW |CTLFLAG_MPSAFE, + dev, 1, acpi_rtc_policy, "I", + "DC Wake policy"); + + return 0; +} + +static int acpi_rtc_detach(device_t dev) +{ + AcpiRemoveNotifyHandler(acpi_get_handle(dev), + ACPI_ALL_NOTIFY, + acpi_rtc_notify_handler); + return 0; +} + +static device_method_t acpi_rtc_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, acpi_rtc_probe), + DEVMETHOD(device_attach, acpi_rtc_attach), + DEVMETHOD(device_detach, acpi_rtc_detach), + + DEVMETHOD(clock_gettime, acpi_rtc_gettime), + DEVMETHOD(clock_settime, acpi_rtc_settime), + + DEVMETHOD_END +}; + +static driver_t acpi_rtc_driver = { + "acpi_rtc", + acpi_rtc_methods, + sizeof(struct acpi_rtc_softc), +}; +devclass_t acpi_rtc_devclass; + +DRIVER_MODULE(acpi_rtc, acpi, acpi_rtc_driver, acpi_rtc_devclass, 0, 0); +MODULE_DEPEND(acpi_rtc, acpi, 1, 1, 1); Index: sys/modules/acpi/acpi_rtc/Makefile =================================================================== --- /dev/null +++ sys/modules/acpi/acpi_rtc/Makefile @@ -0,0 +1,6 @@ +.PATH: ${SRCTOP}/sys/dev/acpica + +KMOD= acpi_rtc +SRCS= acpi_rtc.c opt_acpi.h acpi_if.h acpi_wmi_if.h device_if.h bus_if.h clock_if.h + +.include