/* $OpenBSD: opal.c,v 1.4 2020/06/22 21:13:40 kettenis Exp $ */ /* * Copyright (c) 2020 Mark Kettenis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include struct opal_softc { struct device sc_dev; struct todr_chip_handle sc_todr; }; int opal_match(struct device *, void *, void *); void opal_attach(struct device *, struct device *, void *); struct cfattach opal_ca = { sizeof (struct opal_softc), opal_match, opal_attach }; struct cfdriver opal_cd = { NULL, "opal", DV_DULL }; void opal_attach_node(struct opal_softc *, int); int opal_gettime(struct todr_chip_handle *, struct timeval *); int opal_settime(struct todr_chip_handle *, struct timeval *); int opal_match(struct device *parent, void *match, void *aux) { struct fdt_attach_args *faa = aux; return OF_is_compatible(faa->fa_node, "ibm,opal-v3"); } void opal_attach(struct device *parent, struct device *self, void *aux) { struct opal_softc *sc = (struct opal_softc *)self; struct fdt_attach_args *faa = aux; int node; node = OF_getnodebyname(faa->fa_node, "firmware"); if (node) { char version[64]; version[0] = 0; OF_getprop(node, "version", version, sizeof(version)); version[sizeof(version) - 1] = 0; printf(": %s", version); } printf("\n"); sc->sc_todr.todr_gettime = opal_gettime; sc->sc_todr.todr_settime = opal_settime; todr_attach(&sc->sc_todr); node = OF_getnodebyname(faa->fa_node, "consoles"); if (node) { for (node = OF_child(node); node; node = OF_peer(node)) opal_attach_node(sc, node); } } int opal_print(void *aux, const char *pnp) { struct fdt_attach_args *faa = aux; char name[32]; if (!pnp) return (QUIET); if (OF_getprop(faa->fa_node, "name", name, sizeof(name)) > 0) { name[sizeof(name) - 1] = 0; printf("\"%s\"", name); } else printf("node %u", faa->fa_node); printf(" at %s", pnp); return (UNCONF); } void opal_attach_node(struct opal_softc *sc, int node) { struct fdt_attach_args faa; char buf[32]; if (OF_getproplen(node, "compatible") <= 0) return; if (OF_getprop(node, "status", buf, sizeof(buf)) > 0 && strcmp(buf, "disabled") == 0) return; memset(&faa, 0, sizeof(faa)); faa.fa_name = ""; faa.fa_node = node; config_found(&sc->sc_dev, &faa, opal_print); } int opal_gettime(struct todr_chip_handle *ch, struct timeval *tv) { struct clock_ymdhms dt; uint64_t time; uint32_t date; int64_t error; do { error = opal_rtc_read(opal_phys(&date), opal_phys(&time)); if (error == OPAL_BUSY_EVENT) opal_poll_events(NULL); } while (error == OPAL_BUSY_EVENT); if (error != OPAL_SUCCESS) return EIO; dt.dt_sec = FROMBCD((time >> 40) & 0xff); dt.dt_min = FROMBCD((time >> 48) & 0xff); dt.dt_hour = FROMBCD((time >> 56) & 0xff); dt.dt_day = FROMBCD((date >> 0) & 0xff); dt.dt_mon = FROMBCD((date >> 8) & 0xff); dt.dt_year = FROMBCD((date >> 16) & 0xff); dt.dt_year += 100 * FROMBCD((date >> 24) & 0xff); tv->tv_sec = clock_ymdhms_to_secs(&dt); tv->tv_usec = 0; return 0; } int opal_settime(struct todr_chip_handle *ch, struct timeval *tv) { struct clock_ymdhms dt; uint64_t time = 0; uint32_t date = 0; int64_t error; clock_secs_to_ymdhms(tv->tv_sec, &dt); time |= (uint64_t)TOBCD(dt.dt_sec) << 40; time |= (uint64_t)TOBCD(dt.dt_min) << 48; time |= (uint64_t)TOBCD(dt.dt_hour) << 56; date |= (uint32_t)TOBCD(dt.dt_day); date |= (uint32_t)TOBCD(dt.dt_mon) << 8; date |= (uint32_t)TOBCD(dt.dt_year) << 16; date |= (uint32_t)TOBCD(dt.dt_year / 100) << 24; do { error = opal_rtc_write(date, time); if (error == OPAL_BUSY_EVENT) opal_poll_events(NULL); } while (error == OPAL_BUSY_EVENT); if (error != OPAL_SUCCESS) return EIO; return 0; }