Port-RISCV archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
JH7110 watchdog driver
For reasons not entirely clear to me :) I just picked up a VisionFive 2
to hack around on.
As a way to ease myself in, I wrote a driver for the watchdog timer -
does this (attached) look OK to commit? Be gentle, it's my first NetBSD
driver in a few years...
I don't want to step on anyone's toes, but I'm hoping to make at least
some minor improvements to the support of this board.
/* $NetBSD$ */
/*-
* Copyright (c) 2026 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jeffrey C. Rizzo
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD$");
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/device.h>
#include <sys/intr.h>
#include <sys/systm.h>
#include <sys/mutex.h>
#include <sys/wdog.h>
#include <dev/sysmon/sysmonvar.h>
#include <dev/fdt/fdtvar.h>
/* The JH7110 WDT appears to be a clone of the ARM SP805 watchdog module.
*
* Register definitions and operating principles were taken from the
* ARM Watchdog Module (SP805) Technical Reference Manual, retrieved from
* https://community.arm.com/cfs-file/__key/communityserver-discussions-components-files/468/DDI0270-_2800_4_2900_-2-_2800_1_2900_.pdf
*
*/
#define JH7110_WDT_PERIOD_DEFAULT 15
#define JH7110_WDT_LOAD 0x00
#define JH7110_WDT_VALUE 0x04
#define JH7110_WDT_CONTROL 0x08
#define JH7110_WDT_INTCLR 0x0c
#define JH7110_WDT_MIS 0x14
#define JH7110_WDT_LOCK 0xc00
#define JH7110_WDT_UNLOCK_KEY 0x1ACCE551
#define JH7110_WDT_PERIPHID0 0xfe0
#define JH7110_WDT_PERIPHID1 0xfe4
#define JH7110_WDT_PERIPHID2 0xfe8
#define JH7110_WDT_PERIPHID3 0xfec
#define JH7110_WDT_PCELLID0 0xff0
#define JH7110_WDT_PCELLID1 0xff4
#define JH7110_WDT_PCELLID2 0xff8
#define JH7110_WDT_PCELLID3 0xffc
/* bits for the control register */
#define INTEN __BIT(0)
#define RESEN __BIT(1)
static const struct device_compatible_entry compat_data[] = {
{ .compat = "starfive,jh7110-wdt" },
DEVICE_COMPAT_EOL
};
struct jh7110_wdt_softc {
device_t sc_dev;
bus_space_tag_t sc_bst;
bus_space_handle_t sc_bsh;
struct sysmon_wdog sc_smw;
u_int sc_rate;
};
static inline uint32_t
jh7110_wdt_read(struct jh7110_wdt_softc *sc, bus_size_t reg)
{
return bus_space_read_4(sc->sc_bst, sc->sc_bsh, reg);
}
static inline void
jh7110_wdt_write(struct jh7110_wdt_softc *sc, bus_size_t reg, uint32_t val)
{
bus_space_write_4(sc->sc_bst, sc->sc_bsh, reg, val);
}
static void
jh7110_wdt_stop(struct jh7110_wdt_softc *sc)
{
jh7110_wdt_write(sc, JH7110_WDT_LOCK, JH7110_WDT_UNLOCK_KEY);
uint32_t val = jh7110_wdt_read(sc, JH7110_WDT_CONTROL);
jh7110_wdt_write(sc, JH7110_WDT_CONTROL, val & ~INTEN);
jh7110_wdt_write(sc, JH7110_WDT_LOCK, 1);
}
static void
jh7110_wdt_start(struct jh7110_wdt_softc *sc, uint32_t count)
{
jh7110_wdt_write(sc, JH7110_WDT_LOCK, JH7110_WDT_UNLOCK_KEY);
uint32_t val = jh7110_wdt_read(sc, JH7110_WDT_CONTROL);
jh7110_wdt_write(sc, JH7110_WDT_CONTROL, val & ~INTEN);
val = jh7110_wdt_read(sc, JH7110_WDT_CONTROL);
jh7110_wdt_write(sc, JH7110_WDT_CONTROL, val | RESEN);
jh7110_wdt_write(sc, JH7110_WDT_INTCLR, 1);
jh7110_wdt_write(sc, JH7110_WDT_LOAD, count);
val = jh7110_wdt_read(sc, JH7110_WDT_CONTROL);
jh7110_wdt_write(sc, JH7110_WDT_CONTROL, val | INTEN);
}
static int
jh7110_wdt_setmode(struct sysmon_wdog *smw)
{
struct jh7110_wdt_softc * const sc = smw->smw_cookie;
if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) {
jh7110_wdt_stop(sc);
return 0;
}
if (smw->smw_period == WDOG_PERIOD_DEFAULT)
sc->sc_smw.smw_period = JH7110_WDT_PERIOD_DEFAULT;
else
sc->sc_smw.smw_period = smw->smw_period;
if (sc->sc_smw.smw_period == 0) {
jh7110_wdt_stop(sc);
} else {
/* wdt counts down twice before resetting, so halve rate */
uint32_t counter = sc->sc_smw.smw_period * sc->sc_rate / 2;
jh7110_wdt_start(sc, (uint32_t)counter);
}
return 0;
}
static int
jh7110_wdt_tickle(struct sysmon_wdog *smw)
{
struct jh7110_wdt_softc * const sc = smw->smw_cookie;
jh7110_wdt_write(sc, JH7110_WDT_INTCLR, 1);
return 0;
}
static int
jh7110_wdt_match(device_t parent, cfdata_t cf, void *aux)
{
struct fdt_attach_args * const faa = aux;
return of_compatible_match(faa->faa_phandle, compat_data);
}
static void
jh7110_wdt_attach(device_t parent, device_t self, void *aux)
{
struct jh7110_wdt_softc * const sc = device_private(self);
struct fdt_attach_args * const faa = aux;
const int phandle = faa->faa_phandle;
bus_addr_t addr;
bus_size_t size;
int error;
if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
aprint_error(": couldn't get registers\n");
return;
}
sc->sc_dev = self;
sc->sc_bst = faa->faa_bst;
error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
if (error) {
aprint_error(": couldn't map %#" PRIxBUSADDR ": %d", addr,
error);
return;
}
const char *crs[] = { "apb", "core" };
u_int rate[__arraycount(crs)];
for (size_t i = 0; i < __arraycount(crs); i++) {
const char *cr = crs[i];
struct clk *clk = fdtbus_clock_get(phandle, cr);
if (clk == NULL) {
aprint_error(": couldn't get clock '%s'\n", cr);
return;
}
error = clk_enable(clk);
if (error) {
aprint_error(": couldn't enable clock '%s'\n", cr);
return;
}
/* dts doesn't have reset-names for the wdt for some reason
* so do this by index */
struct fdtbus_reset * rst = fdtbus_reset_get_index(phandle, i);
if (rst == NULL) {
aprint_error(": couldn't get reset index '%zu'\n", i);
return;
}
error = fdtbus_reset_deassert(rst);
if (error) {
aprint_error(": couldn't de-assert reset '%s'\n", cr);
return;
}
rate[i] = clk_get_rate(clk);
if (rate[i] == 0) {
aprint_error(": couldn't get rate for clock '%s'\n", cr);
return;
}
}
sc->sc_rate = rate[1]; /* index of "core" clock from crs[] */
aprint_naive("\n");
aprint_normal(": watchdog\n");
sc->sc_smw.smw_name = device_xname(self);
sc->sc_smw.smw_cookie = sc;
sc->sc_smw.smw_setmode = jh7110_wdt_setmode;
sc->sc_smw.smw_tickle = jh7110_wdt_tickle;
sc->sc_smw.smw_period = JH7110_WDT_PERIOD_DEFAULT;
sysmon_wdog_register(&sc->sc_smw);
}
CFATTACH_DECL_NEW(jh7110_wdt, sizeof(struct jh7110_wdt_softc),
jh7110_wdt_match, jh7110_wdt_attach, NULL, NULL);
Home |
Main Index |
Thread Index |
Old Index