- 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.
This commit is contained in:
Marius Strobl 2005-05-19 21:16:50 +00:00
parent 8e56d2ee05
commit fb596371a9
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=146416
4 changed files with 131 additions and 42 deletions

View file

@ -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.
*/
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);
}

View file

@ -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 */

View file

@ -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 */

View file

@ -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);
}