summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/mk48txx/mk48txx.c148
-rw-r--r--sys/dev/mk48txx/mk48txxreg.h2
-rw-r--r--sys/dev/mk48txx/mk48txxvar.h11
-rw-r--r--sys/sparc64/sparc64/eeprom.c12
4 files changed, 131 insertions, 42 deletions
diff --git a/sys/dev/mk48txx/mk48txx.c b/sys/dev/mk48txx/mk48txx.c
index ff6105e4..634a644 100644
--- a/sys/dev/mk48txx/mk48txx.c
+++ b/sys/dev/mk48txx/mk48txx.c
@@ -47,6 +47,10 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/clock.h>
+#include <sys/eventhandler.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/watchdog.h>
#include <machine/bus.h>
@@ -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.
- */
-
-#undef FROMREG
-
year += sc->sc_year0;
- if (year < POSIX_BASE_YEAR &&
- (sc->sc_flag & MK48TXX_NO_CENT_ADJUST) == 0)
+ 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
+
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 731ea87..25b9cb3 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 2cf47d5..f7c3d31 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 1c9b989..e6b8955 100644
--- a/sys/sparc64/sparc64/eeprom.c
+++ b/sys/sparc64/sparc64/eeprom.c
@@ -58,8 +58,11 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
+#include <sys/eventhandler.h>
#include <sys/kernel.h>
+#include <sys/lock.h>
#include <sys/module.h>
+#include <sys/mutex.h>
#include <sys/resource.h>
#include <dev/ofw/ofw_bus.h>
@@ -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);
}
OpenPOWER on IntegriCloud