diff --git a/sys/dev/mk48txx/mk48txx.c b/sys/dev/mk48txx/mk48txx.c index ff6105e400de..634a64426f19 100644 --- a/sys/dev/mk48txx/mk48txx.c +++ b/sys/dev/mk48txx/mk48txx.c @@ -47,6 +47,10 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include +#include +#include +#include #include @@ -56,7 +60,8 @@ __FBSDID("$FreeBSD$"); #include "clock_if.h" static uint8_t mk48txx_def_nvrd(device_t, int); -static void mk48txx_def_nvwr(device_t, int, u_int8_t); +static void mk48txx_def_nvwr(device_t, int, uint8_t); +static void mk48txx_watchdog(void *, u_int, int *); struct { const char *name; @@ -76,9 +81,15 @@ mk48txx_attach(device_t dev) { struct mk48txx_softc *sc; int i; + uint8_t wday; sc = device_get_softc(dev); + if (mtx_initialized(&sc->sc_mtx) == 0) { + device_printf(dev, "%s: mutex not initialized\n", __func__); + return (ENXIO); + } + device_printf(dev, "model %s", sc->sc_model); i = sizeof(mk48txx_models) / sizeof(mk48txx_models[0]); while (--i >= 0) { @@ -99,15 +110,46 @@ mk48txx_attach(device_t dev) if (sc->sc_nvwr == NULL) sc->sc_nvwr = mk48txx_def_nvwr; - if ((mk48txx_models[i].flags & MK48TXX_EXT_REGISTERS) && - ((*sc->sc_nvrd)(dev, sc->sc_clkoffset + MK48TXX_FLAGS) & - MK48TXX_FLAGS_BL)) { - device_printf(dev, "mk48txx_attach: battery low\n"); - return (ENXIO); + if (mk48txx_models[i].flags & MK48TXX_EXT_REGISTERS) { + mtx_lock(&sc->sc_mtx); + if ((*sc->sc_nvrd)(dev, sc->sc_clkoffset + MK48TXX_FLAGS) & + MK48TXX_FLAGS_BL) { + mtx_unlock(&sc->sc_mtx); + device_printf(dev, "%s: battery low\n", __func__); + return (ENXIO); + } + mtx_unlock(&sc->sc_mtx); + } + + if (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) { + /* + * Use MK48TXX_WDAY_CB instead of manually adjusting the + * century. + */ + if (!(mk48txx_models[i].flags & MK48TXX_EXT_REGISTERS)) { + device_printf(dev, "%s: no century bit\n", __func__); + return (ENXIO); + } else { + mtx_lock(&sc->sc_mtx); + wday = (*sc->sc_nvrd) + (dev, sc->sc_clkoffset + MK48TXX_IWDAY); + wday |= MK48TXX_WDAY_CEB; + (*sc->sc_nvwr) + (dev, sc->sc_clkoffset + MK48TXX_IWDAY, wday); + mtx_unlock(&sc->sc_mtx); + } } clock_register(dev, 1000000); /* 1 second resolution. */ + if ((sc->sc_flag & MK48TXX_WDOG_REGISTER) && + (mk48txx_models[i].flags & MK48TXX_EXT_REGISTERS)) { + sc->sc_wet = EVENTHANDLER_REGISTER(watchdog_list, + mk48txx_watchdog, dev, 0); + device_printf(dev, + "watchdog registered, timeout intervall max. 128 sec\n"); + } + return (0); } @@ -122,11 +164,12 @@ mk48txx_gettime(device_t dev, struct timespec *ts) bus_size_t clkoff; struct clocktime ct; int year; - u_int8_t csr; + uint8_t csr; sc = device_get_softc(dev); clkoff = sc->sc_clkoffset; + mtx_lock(&sc->sc_mtx); /* enable read (stop time) */ csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR); csr |= MK48TXX_CSR_READ; @@ -143,33 +186,22 @@ mk48txx_gettime(device_t dev, struct timespec *ts) ct.dow = FROMREG(MK48TXX_IWDAY, MK48TXX_WDAY_MASK) - 1; ct.mon = FROMBCD(FROMREG(MK48TXX_IMON, MK48TXX_MON_MASK)); year = FROMBCD(FROMREG(MK48TXX_IYEAR, MK48TXX_YEAR_MASK)); - - /* - * XXX: At least the MK48T59 (probably all MK48Txx models with - * extended registers) has a century bit in the MK48TXX_IWDAY - * register which should be used here to make up the century - * when MK48TXX_NO_CENT_ADJUST (which actually means don't - * _manually_ adjust the century in the driver) is set to 1. - * Sun/Solaris doesn't use this bit (probably for backwards - * compatibility with Sun hardware equipped with older MK48Txx - * models) and at present this driver is only used on sparc64 - * so not respecting the century bit doesn't really matter at - * the moment but generally this should be implemented. - */ + year += sc->sc_year0; + if (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) + year += (FROMREG(MK48TXX_IWDAY, MK48TXX_WDAY_CB) >> + MK48TXX_WDAY_CB_SHIFT) * 100; + else if (year < POSIX_BASE_YEAR) + year += 100; #undef FROMREG - year += sc->sc_year0; - if (year < POSIX_BASE_YEAR && - (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) == 0) - year += 100; - ct.year = year; /* time wears on */ csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR); csr &= ~MK48TXX_CSR_READ; (*sc->sc_nvwr)(dev, clkoff + MK48TXX_ICSR, csr); + mtx_unlock(&sc->sc_mtx); return (clock_ct_to_ts(&ct, ts)); } @@ -184,8 +216,8 @@ mk48txx_settime(device_t dev, struct timespec *ts) struct mk48txx_softc *sc; bus_size_t clkoff; struct clocktime ct; - u_int8_t csr; - int year; + uint8_t csr; + int cent, year; sc = device_get_softc(dev); clkoff = sc->sc_clkoffset; @@ -196,10 +228,7 @@ mk48txx_settime(device_t dev, struct timespec *ts) ts->tv_nsec = 0; clock_ts_to_ct(ts, &ct); - year = ct.year - sc->sc_year0; - if (year > 99 && (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) == 0) - year -= 100; - + mtx_lock(&sc->sc_mtx); /* enable write */ csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR); csr |= MK48TXX_CSR_WRITE; @@ -217,12 +246,16 @@ mk48txx_settime(device_t dev, struct timespec *ts) TOREG(MK48TXX_IWDAY, MK48TXX_WDAY_MASK, ct.dow + 1); TOREG(MK48TXX_IDAY, MK48TXX_DAY_MASK, TOBCD(ct.day)); TOREG(MK48TXX_IMON, MK48TXX_MON_MASK, TOBCD(ct.mon)); - TOREG(MK48TXX_IYEAR, MK48TXX_YEAR_MASK, TOBCD(year)); - /* - * XXX: Use the century bit for storing the century when - * MK48TXX_NO_CENT_ADJUST is set to 1. - */ + year = ct.year - sc->sc_year0; + if (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) { + cent = year / 100; + TOREG(MK48TXX_IWDAY, MK48TXX_WDAY_CB, + cent << MK48TXX_WDAY_CB_SHIFT); + year -= cent * 100; + } else if (year > 99) + year -= 100; + TOREG(MK48TXX_IYEAR, MK48TXX_YEAR_MASK, TOBCD(year)); #undef TOREG @@ -230,10 +263,11 @@ mk48txx_settime(device_t dev, struct timespec *ts) csr = (*sc->sc_nvrd)(dev, clkoff + MK48TXX_ICSR); csr &= ~MK48TXX_CSR_WRITE; (*sc->sc_nvwr)(dev, clkoff + MK48TXX_ICSR, csr); + mtx_unlock(&sc->sc_mtx); return (0); } -static u_int8_t +static uint8_t mk48txx_def_nvrd(device_t dev, int off) { struct mk48txx_softc *sc; @@ -243,10 +277,48 @@ mk48txx_def_nvrd(device_t dev, int off) } static void -mk48txx_def_nvwr(device_t dev, int off, u_int8_t v) +mk48txx_def_nvwr(device_t dev, int off, uint8_t v) { struct mk48txx_softc *sc; sc = device_get_softc(dev); bus_space_write_1(sc->sc_bst, sc->sc_bsh, off, v); } + +static void +mk48txx_watchdog(void *arg, u_int cmd, int *error) +{ + device_t dev; + struct mk48txx_softc *sc; + uint8_t t, wdog; + + dev = arg; + sc = device_get_softc(dev); + + wdog = 0; + t = cmd & WD_INTERVAL; + if (cmd != 0 && t >= 26 && t <= 37) { + if (t <= WD_TO_2SEC) { + wdog |= MK48TXX_WDOG_RB_1_16; + t -= 26; + } else if (t <= WD_TO_8SEC) { + wdog |= MK48TXX_WDOG_RB_1_4; + t -= WD_TO_250MS; + } else if (t <= WD_TO_32SEC) { + wdog |= MK48TXX_WDOG_RB_1; + t -= WD_TO_1SEC; + } else { + wdog |= MK48TXX_WDOG_RB_4; + t -= WD_TO_4SEC; + } + wdog |= (min(1 << t, + MK48TXX_WDOG_BMB_MASK >> MK48TXX_WDOG_BMB_SHIFT)) << + MK48TXX_WDOG_BMB_SHIFT; + if (sc->sc_flag & MK48TXX_WDOG_ENABLE_WDS) + wdog |= MK48TXX_WDOG_WDS; + *error = 0; + } + mtx_lock(&sc->sc_mtx); + (*sc->sc_nvwr)(dev, sc->sc_clkoffset + MK48TXX_WDOG, wdog); + mtx_unlock(&sc->sc_mtx); +} diff --git a/sys/dev/mk48txx/mk48txxreg.h b/sys/dev/mk48txx/mk48txxreg.h index 731ea87676df..25b9cb392086 100644 --- a/sys/dev/mk48txx/mk48txxreg.h +++ b/sys/dev/mk48txx/mk48txxreg.h @@ -114,6 +114,7 @@ #define MK48TXX_WDOG_RB_1 0x02 /* watchdog resolution 1 second */ #define MK48TXX_WDOG_RB_4 0x03 /* watchdog resolution 4 seconds */ #define MK48TXX_WDOG_BMB_MASK 0x7c /* mask for watchdog multiplier */ +#define MK48TXX_WDOG_BMB_SHIFT 2 /* shift for watchdog multiplier */ #define MK48TXX_WDOG_WDS 0x80 /* watchdog steering bit */ /* Bits in the control register */ @@ -135,6 +136,7 @@ /* Bits in the century/weekday register */ #define MK48TXX_WDAY_MASK 0x07 /* mask for weekday */ #define MK48TXX_WDAY_CB 0x10 /* century bit (extended only) */ +#define MK48TXX_WDAY_CB_SHIFT 4 /* shift for century bit */ #define MK48TXX_WDAY_CEB 0x20 /* century enable bit (extended only) */ #define MK48TXX_WDAY_FT 0x40 /* frequency test */ diff --git a/sys/dev/mk48txx/mk48txxvar.h b/sys/dev/mk48txx/mk48txxvar.h index 2cf47d5cc0d7..f7c3d3195062 100644 --- a/sys/dev/mk48txx/mk48txxvar.h +++ b/sys/dev/mk48txx/mk48txxvar.h @@ -38,19 +38,24 @@ * $FreeBSD$ */ -typedef u_int8_t (*mk48txx_nvrd_t)(device_t, int); -typedef void (*mk48txx_nvwr_t)(device_t, int, u_int8_t); +typedef uint8_t (*mk48txx_nvrd_t)(device_t, int); +typedef void (*mk48txx_nvwr_t)(device_t, int, uint8_t); struct mk48txx_softc { bus_space_tag_t sc_bst; /* bus space tag */ bus_space_handle_t sc_bsh; /* bus space handle */ + struct mtx sc_mtx; /* hardware mutex */ + eventhandler_tag sc_wet; /* watchdog event handler tag */ + const char *sc_model; /* chip model name */ bus_size_t sc_nvramsz; /* Size of NVRAM on the chip */ bus_size_t sc_clkoffset; /* Offset in NVRAM to clock bits */ u_int sc_year0; /* year counter offset */ u_int sc_flag; /* MD flags */ -#define MK48TXX_NO_CENT_ADJUST 0x0001 +#define MK48TXX_NO_CENT_ADJUST 0x0001 /* don't manually adjust century */ +#define MK48TXX_WDOG_REGISTER 0x0002 /* register watchdog */ +#define MK48TXX_WDOG_ENABLE_WDS 0x0004 /* enable watchdog steering bit */ mk48txx_nvrd_t sc_nvrd; /* NVRAM/RTC read function */ mk48txx_nvwr_t sc_nvwr; /* NVRAM/RTC write function */ diff --git a/sys/sparc64/sparc64/eeprom.c b/sys/sparc64/sparc64/eeprom.c index 1c9b9899242b..e6b895590f0f 100644 --- a/sys/sparc64/sparc64/eeprom.c +++ b/sys/sparc64/sparc64/eeprom.c @@ -58,8 +58,11 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include +#include #include +#include #include #include @@ -126,11 +129,14 @@ eeprom_attach(device_t dev) sc = device_get_softc(dev); bzero(sc, sizeof(struct mk48txx_softc)); + mtx_init(&sc->sc_mtx, "eeprom_mtx", NULL, MTX_DEF); + rid = 0; res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (res == NULL) { device_printf(dev, "cannot allocate resources\n"); - return (ENXIO); + error = ENXIO; + goto fail_mtx; } sc->sc_bst = rman_get_bustag(res); sc->sc_bsh = rman_get_bushandle(res); @@ -158,6 +164,7 @@ eeprom_attach(device_t dev) * on the latter models. A generic way to retrieve the hostid is to * use the `idprom' node. */ + mtx_lock(&sc->sc_mtx); h = bus_space_read_1(sc->sc_bst, sc->sc_bsh, sc->sc_nvramsz - IDPROM_OFFSET + offsetof(struct idprom, id_machine)) << 24; for (i = 0; i < 3; i++) { @@ -165,6 +172,7 @@ eeprom_attach(device_t dev) IDPROM_OFFSET + offsetof(struct idprom, id_hostid[i])) << ((2 - i) * 8); } + mtx_unlock(&sc->sc_mtx); if (h != 0) device_printf(dev, "hostid %x\n", (u_int)h); @@ -178,6 +186,8 @@ eeprom_attach(device_t dev) fail_res: bus_release_resource(dev, SYS_RES_MEMORY, rid, res); + fail_mtx: + mtx_destroy(&sc->sc_mtx); return (error); }