mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-11 14:10:34 +00:00
- 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:
parent
8e56d2ee05
commit
fb596371a9
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=146416
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user