summaryrefslogtreecommitdiffstats
path: root/sys/dev/mk48txx
diff options
context:
space:
mode:
authormarius <marius@FreeBSD.org>2005-05-19 21:16:50 +0000
committermarius <marius@FreeBSD.org>2005-05-19 21:16:50 +0000
commitbedc4e05717f5dabf8d7fab67cee6819f1b93358 (patch)
tree2cc09e97a4f5281f80ee84f17efa48f6183f015d /sys/dev/mk48txx
parent3a5fc7dc75e619408c9f7ce037a57772e9685af5 (diff)
downloadFreeBSD-src-bedc4e05717f5dabf8d7fab67cee6819f1b93358.zip
FreeBSD-src-bedc4e05717f5dabf8d7fab67cee6819f1b93358.tar.gz
- Add locking.
- Add support for storing the century in MK48TXX_WDAY_CB on MK48Txx with extended registers when the MK48TXX_NO_CENT_ADJUST flag is set (and which is termed somewhat confusing as it actually means don't manually adjust the century in the driver). - Add the MI part of interfacing the watchdog functionality of MK48Txx with extended registers with watchdog(9). This is inspired by the SunOS/Solaris drivers for the 'eeprom' devices also having watchdog support. I actually expected this to work out of the box on Sun Exx00 machines with 'eeprom' devices which have a 'watchdog-enable' property. On terminal count of the the watchdog timer however only the MK48TXX_FLAGS_WDF bit rises but the reset signal and the interrupt respectively (depending on whether the MK48TXX_WDOG_WDS bit of the chip and the MK48TXX_WDOG_ENABLE_WDS flag of the driver respectively is set) goes nowhere. Apparently passing the reset signal on to the WDR line of the CPUs has to be enabled somewhere else but we don't have documentation for the Exx00 specific controllers. I decided to commit this nevertheless so it can be enabled in the eeprom(4) front-end later in e.g. 6.0-STABLE without breaking the API. Besides the Exx00 the watchdog part of the MK48Txx should also work on E250 and E450. Possibly also without extra fiddling on these machines but I haven't found someone willing to give it a try on such a machine so far. - Use uintXX_t instead of u_intXX_t, use __func__ instead of hardcoded function names in error strings.
Diffstat (limited to 'sys/dev/mk48txx')
-rw-r--r--sys/dev/mk48txx/mk48txx.c148
-rw-r--r--sys/dev/mk48txx/mk48txxreg.h2
-rw-r--r--sys/dev/mk48txx/mk48txxvar.h11
3 files changed, 120 insertions, 41 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 */
OpenPOWER on IntegriCloud