Changeset View
Changeset View
Standalone View
Standalone View
sys/arm/allwinner/aw_wdog.c
/*- | /*- | ||||
* Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org> | * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@freebsd.org> | ||||
* Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org> | * Copyright (c) 2016 Emmanuel Vadot <manu@freebsd.org> | ||||
* Copyright (c) 2022 Julien Cassette <julien.cassette@gmail.com> | |||||
* All rights reserved. | * All rights reserved. | ||||
* | * | ||||
* Redistribution and use in source and binary forms, with or without | * Redistribution and use in source and binary forms, with or without | ||||
* modification, are permitted provided that the following conditions | * modification, are permitted provided that the following conditions | ||||
* are met: | * are met: | ||||
* 1. Redistributions of source code must retain the above copyright | * 1. Redistributions of source code must retain the above copyright | ||||
* notice, this list of conditions and the following disclaimer. | * notice, this list of conditions and the following disclaimer. | ||||
* 2. Redistributions in binary form must reproduce the above copyright | * 2. Redistributions in binary form must reproduce the above copyright | ||||
Show All 27 Lines | |||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
#include <dev/ofw/openfirm.h> | #include <dev/ofw/openfirm.h> | ||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <machine/machdep.h> | |||||
#include <arm/allwinner/aw_wdog.h> | #include <arm/allwinner/aw_wdog.h> | ||||
#define READ(_sc, _r) bus_read_4((_sc)->res, (_r)) | #define READ(_sc, _r) bus_read_4((_sc)->res, (_r)) | ||||
#define WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v)) | #define WRITE(_sc, _r, _v) bus_write_4((_sc)->res, (_r), (_v)) | ||||
#define A10_WDOG_CTRL 0x00 | #define A10_WDOG_CTRL 0x00 | ||||
#define A31_WDOG_CTRL 0x10 | #define A31_WDOG_CTRL 0x10 | ||||
#define D1_WDOG_CTRL 0x10 | |||||
#define WDOG_CTRL_RESTART (1 << 0) | #define WDOG_CTRL_RESTART (1 << 0) | ||||
#define A31_WDOG_CTRL_KEY (0xa57 << 1) | #define A31_WDOG_CTRL_KEY (0xa57 << 1) | ||||
#define D1_WDOG_CTRL_KEY (0xa57 << 1) | |||||
#define A10_WDOG_MODE 0x04 | #define A10_WDOG_MODE 0x04 | ||||
#define A31_WDOG_MODE 0x18 | #define A31_WDOG_MODE 0x18 | ||||
#define D1_WDOG_MODE 0x18 | |||||
#define D1_WDOG_MODE_KEY (0x16AA << 16) | |||||
#define A10_WDOG_MODE_INTVL_SHIFT 3 | #define A10_WDOG_MODE_INTVL_SHIFT 3 | ||||
#define A31_WDOG_MODE_INTVL_SHIFT 4 | #define A31_WDOG_MODE_INTVL_SHIFT 4 | ||||
#define D1_WDOG_MODE_INTVL_SHIFT 4 | |||||
#define A10_WDOG_MODE_RST_EN (1 << 1) | #define A10_WDOG_MODE_RST_EN (1 << 1) | ||||
#define WDOG_MODE_EN (1 << 0) | #define WDOG_MODE_EN (1 << 0) | ||||
#define A31_WDOG_CONFIG 0x14 | #define A31_WDOG_CONFIG 0x14 | ||||
#define D1_WDOG_CONFIG 0x14 | |||||
#define A31_WDOG_CONFIG_RST_EN_SYSTEM (1 << 0) | #define A31_WDOG_CONFIG_RST_EN_SYSTEM (1 << 0) | ||||
#define A31_WDOG_CONFIG_RST_EN_INT (2 << 0) | #define A31_WDOG_CONFIG_RST_EN_INT (2 << 0) | ||||
#define D1_WDOG_CONFIG_KEY (0x16AA << 16) | |||||
#define D1_WDOG_CONFIG_RST_EN_SYSTEM (1 << 0) | |||||
#define D1_WDOG_CONFIG_RST_EN_INT (2 << 0) | |||||
struct aw_wdog_interval { | struct aw_wdog_interval { | ||||
uint64_t milliseconds; | uint64_t milliseconds; | ||||
unsigned int value; | unsigned int value; | ||||
}; | }; | ||||
struct aw_wdog_interval wd_intervals[] = { | struct aw_wdog_interval wd_intervals[] = { | ||||
{ 500, 0 }, | { 500, 0 }, | ||||
Show All 15 Lines | |||||
struct aw_wdog_softc { | struct aw_wdog_softc { | ||||
device_t dev; | device_t dev; | ||||
struct resource * res; | struct resource * res; | ||||
struct mtx mtx; | struct mtx mtx; | ||||
uint8_t wdog_ctrl; | uint8_t wdog_ctrl; | ||||
uint32_t wdog_ctrl_key; | uint32_t wdog_ctrl_key; | ||||
uint8_t wdog_mode; | uint8_t wdog_mode; | ||||
uint32_t wdog_mode_key; | |||||
uint8_t wdog_mode_intvl_shift; | uint8_t wdog_mode_intvl_shift; | ||||
uint8_t wdog_mode_en; | uint8_t wdog_mode_en; | ||||
uint8_t wdog_config; | uint8_t wdog_config; | ||||
uint8_t wdog_config_value; | uint32_t wdog_config_value; | ||||
}; | }; | ||||
#define A10_WATCHDOG 1 | #define A10_WATCHDOG 1 | ||||
#define A31_WATCHDOG 2 | #define A31_WATCHDOG 2 | ||||
#define D1_WATCHDOG 3 | |||||
static struct ofw_compat_data compat_data[] = { | static struct ofw_compat_data compat_data[] = { | ||||
{"allwinner,sun4i-a10-wdt", A10_WATCHDOG}, | {"allwinner,sun4i-a10-wdt", A10_WATCHDOG}, | ||||
{"allwinner,sun6i-a31-wdt", A31_WATCHDOG}, | {"allwinner,sun6i-a31-wdt", A31_WATCHDOG}, | ||||
{"allwinner,sun20i-d1-wdt", D1_WATCHDOG}, | |||||
{NULL, 0} | {NULL, 0} | ||||
}; | }; | ||||
static void aw_wdog_watchdog_fn(void *, u_int, int *); | static void aw_wdog_watchdog_fn(void *, u_int, int *); | ||||
static void aw_wdog_shutdown_fn(void *, int); | static void aw_wdog_shutdown_fn(void *, int); | ||||
static int | static int | ||||
aw_wdog_probe(device_t dev) | aw_wdog_probe(device_t dev) | ||||
{ | { | ||||
if (!ofw_bus_status_okay(dev)) | if (!ofw_bus_status_okay(dev)) | ||||
return (ENXIO); | return (ENXIO); | ||||
switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { | switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { | ||||
case A10_WATCHDOG: | case A10_WATCHDOG: | ||||
device_set_desc(dev, "Allwinner A10 Watchdog"); | device_set_desc(dev, "Allwinner A10 Watchdog"); | ||||
return (BUS_PROBE_DEFAULT); | return (BUS_PROBE_DEFAULT); | ||||
case A31_WATCHDOG: | case A31_WATCHDOG: | ||||
device_set_desc(dev, "Allwinner A31 Watchdog"); | device_set_desc(dev, "Allwinner A31 Watchdog"); | ||||
return (BUS_PROBE_DEFAULT); | return (BUS_PROBE_DEFAULT); | ||||
case D1_WATCHDOG: | |||||
device_set_desc(dev, "Allwinner D1 Watchdog"); | |||||
return (BUS_PROBE_DEFAULT); | |||||
} | } | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
static int | static int | ||||
aw_wdog_attach(device_t dev) | aw_wdog_attach(device_t dev) | ||||
{ | { | ||||
struct aw_wdog_softc *sc; | struct aw_wdog_softc *sc; | ||||
Show All 13 Lines | aw_wdog_attach(device_t dev) | ||||
} | } | ||||
aw_wdog_sc = sc; | aw_wdog_sc = sc; | ||||
switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { | switch (ofw_bus_search_compatible(dev, compat_data)->ocd_data) { | ||||
case A10_WATCHDOG: | case A10_WATCHDOG: | ||||
sc->wdog_ctrl = A10_WDOG_CTRL; | sc->wdog_ctrl = A10_WDOG_CTRL; | ||||
sc->wdog_mode = A10_WDOG_MODE; | sc->wdog_mode = A10_WDOG_MODE; | ||||
sc->wdog_mode_key = 0; | |||||
sc->wdog_mode_intvl_shift = A10_WDOG_MODE_INTVL_SHIFT; | sc->wdog_mode_intvl_shift = A10_WDOG_MODE_INTVL_SHIFT; | ||||
sc->wdog_mode_en = A10_WDOG_MODE_RST_EN | WDOG_MODE_EN; | sc->wdog_mode_en = A10_WDOG_MODE_RST_EN | WDOG_MODE_EN; | ||||
break; | break; | ||||
case A31_WATCHDOG: | case A31_WATCHDOG: | ||||
sc->wdog_ctrl = A31_WDOG_CTRL; | sc->wdog_ctrl = A31_WDOG_CTRL; | ||||
sc->wdog_ctrl_key = A31_WDOG_CTRL_KEY; | sc->wdog_ctrl_key = A31_WDOG_CTRL_KEY; | ||||
sc->wdog_mode = A31_WDOG_MODE; | sc->wdog_mode = A31_WDOG_MODE; | ||||
sc->wdog_mode_key = 0; | |||||
sc->wdog_mode_intvl_shift = A31_WDOG_MODE_INTVL_SHIFT; | sc->wdog_mode_intvl_shift = A31_WDOG_MODE_INTVL_SHIFT; | ||||
sc->wdog_mode_en = WDOG_MODE_EN; | sc->wdog_mode_en = WDOG_MODE_EN; | ||||
sc->wdog_config = A31_WDOG_CONFIG; | sc->wdog_config = A31_WDOG_CONFIG; | ||||
sc->wdog_config_value = A31_WDOG_CONFIG_RST_EN_SYSTEM; | sc->wdog_config_value = A31_WDOG_CONFIG_RST_EN_SYSTEM; | ||||
break; | break; | ||||
case D1_WATCHDOG: | |||||
sc->wdog_ctrl = D1_WDOG_CTRL; | |||||
sc->wdog_ctrl_key = D1_WDOG_CTRL_KEY; | |||||
sc->wdog_mode = D1_WDOG_MODE; | |||||
sc->wdog_mode_key = D1_WDOG_MODE_KEY; | |||||
sc->wdog_mode_intvl_shift = D1_WDOG_MODE_INTVL_SHIFT; | |||||
sc->wdog_mode_en = WDOG_MODE_EN; | |||||
sc->wdog_config = D1_WDOG_CONFIG; | |||||
sc->wdog_config_value = D1_WDOG_CONFIG_KEY | D1_WDOG_CONFIG_RST_EN_SYSTEM; | |||||
break; | |||||
default: | default: | ||||
bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res); | bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->res); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
mtx_init(&sc->mtx, "AW Watchdog", "aw_wdog", MTX_DEF); | mtx_init(&sc->mtx, "AW Watchdog", "aw_wdog", MTX_DEF); | ||||
EVENTHANDLER_REGISTER(watchdog_list, aw_wdog_watchdog_fn, sc, 0); | EVENTHANDLER_REGISTER(watchdog_list, aw_wdog_watchdog_fn, sc, 0); | ||||
EVENTHANDLER_REGISTER(shutdown_final, aw_wdog_shutdown_fn, sc, | EVENTHANDLER_REGISTER(shutdown_final, aw_wdog_shutdown_fn, sc, | ||||
Show All 16 Lines | aw_wdog_watchdog_fn(void *private, u_int cmd, int *error) | ||||
if (cmd > 0) { | if (cmd > 0) { | ||||
ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000; | ms = ((uint64_t)1 << (cmd & WD_INTERVAL)) / 1000000; | ||||
i = 0; | i = 0; | ||||
while (wd_intervals[i].milliseconds && | while (wd_intervals[i].milliseconds && | ||||
(ms > wd_intervals[i].milliseconds)) | (ms > wd_intervals[i].milliseconds)) | ||||
i++; | i++; | ||||
if (wd_intervals[i].milliseconds) { | if (wd_intervals[i].milliseconds) { | ||||
WRITE(sc, sc->wdog_mode, | WRITE(sc, sc->wdog_mode, sc->wdog_mode_key | | ||||
(wd_intervals[i].value << sc->wdog_mode_intvl_shift) | | (wd_intervals[i].value << sc->wdog_mode_intvl_shift) | | ||||
sc->wdog_mode_en); | sc->wdog_mode_en); | ||||
WRITE(sc, sc->wdog_ctrl, | WRITE(sc, sc->wdog_ctrl, | ||||
WDOG_CTRL_RESTART | sc->wdog_ctrl_key); | WDOG_CTRL_RESTART | sc->wdog_ctrl_key); | ||||
if (sc->wdog_config) | if (sc->wdog_config) | ||||
WRITE(sc, sc->wdog_config, | WRITE(sc, sc->wdog_config, | ||||
sc->wdog_config_value); | sc->wdog_config_value); | ||||
*error = 0; | *error = 0; | ||||
} | } | ||||
else { | else { | ||||
/* | /* | ||||
* Can't arm | * Can't arm | ||||
* disable watchdog as watchdog(9) requires | * disable watchdog as watchdog(9) requires | ||||
*/ | */ | ||||
device_printf(sc->dev, | device_printf(sc->dev, | ||||
"Can't arm, timeout is more than 16 sec\n"); | "Can't arm, timeout is more than 16 sec\n"); | ||||
mtx_unlock(&sc->mtx); | mtx_unlock(&sc->mtx); | ||||
WRITE(sc, sc->wdog_mode, 0); | WRITE(sc, sc->wdog_mode, sc->wdog_mode_key); | ||||
return; | return; | ||||
} | } | ||||
} | } | ||||
else | else | ||||
WRITE(sc, sc->wdog_mode, 0); | WRITE(sc, sc->wdog_mode, sc->wdog_mode_key); | ||||
mtx_unlock(&sc->mtx); | mtx_unlock(&sc->mtx); | ||||
} | } | ||||
static void | static void | ||||
aw_wdog_shutdown_fn(void *private, int howto) | aw_wdog_shutdown_fn(void *private, int howto) | ||||
{ | { | ||||
if ((howto & (RB_POWEROFF|RB_HALT)) == 0) | if ((howto & (RB_POWEROFF|RB_HALT)) == 0) | ||||
aw_wdog_watchdog_reset(); | aw_wdog_watchdog_reset(); | ||||
} | } | ||||
void | void | ||||
aw_wdog_watchdog_reset(void) | aw_wdog_watchdog_reset(void) | ||||
{ | { | ||||
if (aw_wdog_sc == NULL) { | if (aw_wdog_sc == NULL) { | ||||
printf("Reset: watchdog device has not been initialized\n"); | printf("Reset: watchdog device has not been initialized\n"); | ||||
return; | return; | ||||
} | } | ||||
WRITE(aw_wdog_sc, aw_wdog_sc->wdog_mode, | WRITE(aw_wdog_sc, aw_wdog_sc->wdog_mode, aw_wdog_sc->wdog_mode_key | | ||||
(wd_intervals[0].value << aw_wdog_sc->wdog_mode_intvl_shift) | | (wd_intervals[0].value << aw_wdog_sc->wdog_mode_intvl_shift) | | ||||
aw_wdog_sc->wdog_mode_en); | aw_wdog_sc->wdog_mode_en); | ||||
if (aw_wdog_sc->wdog_config) | if (aw_wdog_sc->wdog_config) | ||||
WRITE(aw_wdog_sc, aw_wdog_sc->wdog_config, | WRITE(aw_wdog_sc, aw_wdog_sc->wdog_config, | ||||
aw_wdog_sc->wdog_config_value); | aw_wdog_sc->wdog_config_value); | ||||
WRITE(aw_wdog_sc, aw_wdog_sc->wdog_ctrl, | WRITE(aw_wdog_sc, aw_wdog_sc->wdog_ctrl, | ||||
WDOG_CTRL_RESTART | aw_wdog_sc->wdog_ctrl_key); | WDOG_CTRL_RESTART | aw_wdog_sc->wdog_ctrl_key); | ||||
while(1) | while(1) | ||||
Show All 19 Lines |