diff options
Diffstat (limited to 'sys/arch/sgi/hpc/dpclock.c')
-rw-r--r-- | sys/arch/sgi/hpc/dpclock.c | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/sys/arch/sgi/hpc/dpclock.c b/sys/arch/sgi/hpc/dpclock.c new file mode 100644 index 00000000000..600c3ede567 --- /dev/null +++ b/sys/arch/sgi/hpc/dpclock.c @@ -0,0 +1,312 @@ +/* $OpenBSD: dpclock.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: dpclock.c,v 1.3 2011/07/01 18:53:46 dyoung Exp $ */ + +/* + * Copyright (c) 2012 Miodrag Vallat. + * + * 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. + */ +/* + * Copyright (c) 2001 Erik Reid + * Copyright (c) 2001 Rafal K. Boni + * Copyright (c) 2001 Christopher Sekiya + * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Portions of this code are derived from software contributed to The + * NetBSD Foundation by Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center. + * + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ +/* + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1982, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * from: Utah $Hdr: clock.c 1.18 91/01/21$ + * + * @(#)clock.c 8.2 (Berkeley) 1/12/94 + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> + +#include <mips64/archtype.h> +#include <mips64/dev/clockvar.h> + +#include <dev/ic/dp8573areg.h> +#include <sgi/hpc/hpcvar.h> + +#define IRIX_BASE_YEAR 1940 + +struct dpclock_softc { + struct device sc_dev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +int dpclock_match(struct device *, void *, void *); +void dpclock_attach(struct device *, struct device *, void *); + +struct cfdriver dpclock_cd = { + NULL, "dpclock", DV_DULL +}; + +const struct cfattach dpclock_ca = { + sizeof(struct dpclock_softc), dpclock_match, dpclock_attach +}; + +#define dpclock_read(sc,r) \ + bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, ((r) << 2) + 3) +#define dpclock_write(sc,r,v) \ + bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, ((r) << 2) + 3, (v)) + +static inline int frombcd(int); +static inline int tobcd(int); +static inline int leapyear(int year); + +static inline int +frombcd(int x) +{ + return (x >> 4) * 10 + (x & 0xf); +} +static inline int +tobcd(int x) +{ + return (x / 10 * 16) + (x % 10); +} +/* + * This inline avoids some unnecessary modulo operations + * as compared with the usual macro: + * ( ((year % 4) == 0 && + * (year % 100) != 0) || + * ((year % 400) == 0) ) + * It is otherwise equivalent. + * (borrowed from kern/clock_subr.c) + */ +static inline int +leapyear(int year) +{ + int rv = 0; + + if ((year & 3) == 0) { + rv = 1; + if ((year % 100) == 0) { + rv = 0; + if ((year % 400) == 0) + rv = 1; + } + } + return (rv); +} + +void dpclock_gettime(void *, time_t, struct tod_time *); +void dpclock_settime(void *, struct tod_time *); + +int +dpclock_match(struct device *parent, void *vcf, void *aux) +{ + struct hpc_attach_args *haa = aux; + + if (strcmp(haa->ha_name, dpclock_cd.cd_name) != 0) + return 0; + + return 1; +} + +void +dpclock_attach(struct device *parent, struct device *self, void *aux) +{ + struct dpclock_softc *sc = (void *)self; + struct hpc_attach_args *haa = aux; + uint8_t st, r; + + sc->sc_iot = haa->ha_st; + if (bus_space_subregion(haa->ha_st, haa->ha_sh, haa->ha_devoff, + 4 * DP8573A_NREG, &sc->sc_ioh) != 0) { + printf(": can't map registers\n"); + return; + } + + st = dpclock_read(sc, DP8573A_STATUS); + dpclock_write(sc, DP8573A_STATUS, st | DP8573A_STATUS_REGSEL); + r = dpclock_read(sc, DP8573A_RT_MODE); + if ((r & DP8573A_RT_MODE_CLKSS) == 0) { + printf(": clock stopped"); + dpclock_write(sc, DP8573A_RT_MODE, r | DP8573A_RT_MODE_CLKSS); + dpclock_write(sc, DP8573A_INT0_CTL, 0); + dpclock_write(sc, DP8573A_INT1_CTL, DP8573A_INT1_CTL_PWRINT); + } + dpclock_write(sc, DP8573A_STATUS, st & ~DP8573A_STATUS_REGSEL); + r = dpclock_read(sc, DP8573A_PFLAG); + if (r & DP8573A_PFLAG_TESTMODE) { + dpclock_write(sc, DP8573A_RAM_1F, 0); + dpclock_write(sc, DP8573A_PFLAG, r & ~DP8573A_PFLAG_TESTMODE); + } + + printf("\n"); + + sys_tod.tod_get = dpclock_gettime; + sys_tod.tod_set = dpclock_settime; + sys_tod.tod_cookie = self; +} + +/* + * Get the time of day, based on the clock's value and/or the base value. + */ +void +dpclock_gettime(void *cookie, time_t base, struct tod_time *ct) +{ + struct dpclock_softc *sc = (void *)cookie; + uint i; + uint8_t regs[DP8573A_NREG]; + + i = dpclock_read(sc, DP8573A_TIMESAVE_CTL); + dpclock_write(sc, DP8573A_TIMESAVE_CTL, i | DP8573A_TIMESAVE_CTL_EN); + dpclock_write(sc, DP8573A_TIMESAVE_CTL, i); + + for (i = 0; i < DP8573A_NREG; i++) + regs[i] = dpclock_read(sc, i); + + ct->sec = frombcd(regs[DP8573A_SAVE_SEC]); + ct->min = frombcd(regs[DP8573A_SAVE_MIN]); + + if (regs[DP8573A_RT_MODE] & DP8573A_RT_MODE_1224) { + ct->hour = frombcd(regs[DP8573A_SAVE_HOUR] & + DP8573A_HOUR_12HR_MASK) + + ((regs[DP8573A_SAVE_HOUR] & DP8573A_RT_MODE_1224) ? 0 : 12); + + /* + * In AM/PM mode, hour range is 01-12, so adding in 12 hours + * for PM gives us 01-24, whereas we want 00-23, so map hour + * 24 to hour 0. + */ + if (ct->hour == 24) + ct->hour = 0; + } else { + ct->hour = frombcd(regs[DP8573A_SAVE_HOUR] & + DP8573A_HOUR_24HR_MASK); + } + + ct->day = frombcd(regs[DP8573A_SAVE_DOM]); + ct->mon = frombcd(regs[DP8573A_SAVE_MONTH]); + ct->year = frombcd(regs[DP8573A_YEAR]) + (IRIX_BASE_YEAR - 1900); +} + +/* + * Reset the TODR based on the time value. + */ +void +dpclock_settime(void *cookie, struct tod_time *ct) +{ + struct dpclock_softc *sc = (void *)cookie; + uint i; + uint st, r, delta; + uint8_t regs[DP8573A_NREG]; + + r = dpclock_read(sc, DP8573A_TIMESAVE_CTL); + dpclock_write(sc, DP8573A_TIMESAVE_CTL, r | DP8573A_TIMESAVE_CTL_EN); + dpclock_write(sc, DP8573A_TIMESAVE_CTL, r); + + for (i = 0; i < DP8573A_NREG; i++) + regs[i] = dpclock_read(sc, i); + + regs[DP8573A_SUBSECOND] = 0; + regs[DP8573A_SECOND] = tobcd(ct->sec); + regs[DP8573A_MINUTE] = tobcd(ct->min); + regs[DP8573A_HOUR] = tobcd(ct->hour) & DP8573A_HOUR_24HR_MASK; + regs[DP8573A_DOW] = tobcd(ct->dow); + regs[DP8573A_DOM] = tobcd(ct->day); + regs[DP8573A_MONTH] = tobcd(ct->mon); + regs[DP8573A_YEAR] = tobcd(ct->year - (IRIX_BASE_YEAR - 1900)); + + st = dpclock_read(sc, DP8573A_STATUS); + dpclock_write(sc, DP8573A_STATUS, st | DP8573A_STATUS_REGSEL); + r = dpclock_read(sc, DP8573A_RT_MODE); + dpclock_write(sc, DP8573A_RT_MODE, r & ~DP8573A_RT_MODE_CLKSS); + + for (i = 0; i < 10; i++) + dpclock_write(sc, DP8573A_COUNTERS + i, + regs[DP8573A_COUNTERS + i]); + + /* + * We now need to set the leap year counter to the correct value. + * Unfortunately it is only two bits wide, while eight years can + * happen between two leap years. Skirting this is left as an + * exercise to the reader with an Indigo in working condition + * by year 2100. + */ + delta = 0; + while (delta < 3 && !leapyear(ct->year - delta)) + delta++; + + r &= ~(DP8573A_RT_MODE_LYLSB | DP8573A_RT_MODE_LYMSB); + dpclock_write(sc, DP8573A_RT_MODE, r | delta | DP8573A_RT_MODE_CLKSS); + + dpclock_write(sc, DP8573A_STATUS, st & ~DP8573A_STATUS_REGSEL); +} |