Module Name: src Committed By: jmcneill Date: Sat Oct 7 16:44:24 UTC 2017
Modified Files: src/sys/arch/arm/sunxi: sunxi_wdt.c Log Message: Add sun4i watchdog support. To generate a diff of this commit: cvs rdiff -u -r1.1 -r1.2 src/sys/arch/arm/sunxi/sunxi_wdt.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/sunxi/sunxi_wdt.c diff -u src/sys/arch/arm/sunxi/sunxi_wdt.c:1.1 src/sys/arch/arm/sunxi/sunxi_wdt.c:1.2 --- src/sys/arch/arm/sunxi/sunxi_wdt.c:1.1 Tue Jul 25 17:04:52 2017 +++ src/sys/arch/arm/sunxi/sunxi_wdt.c Sat Oct 7 16:44:24 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_wdt.c,v 1.1 2017/07/25 17:04:52 jmcneill Exp $ */ +/* $NetBSD: sunxi_wdt.c,v 1.2 2017/10/07 16:44:24 jmcneill Exp $ */ /*- * Copyright (c) 2017 Jared McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sunxi_wdt.c,v 1.1 2017/07/25 17:04:52 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sunxi_wdt.c,v 1.2 2017/10/07 16:44:24 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -43,31 +43,46 @@ __KERNEL_RCSID(0, "$NetBSD: sunxi_wdt.c, #define SUNXI_WDT_PERIOD_DEFAULT 16 -#define WDT_IRQ_EN_REG 0x00 -#define WDT_IRQ_EN_EN __BIT(0) -#define WDT_IRQ_STA_REG 0x04 -#define WDT_IRQ_STA_PEND __BIT(0) -#define WDT_CTRL_REG 0x10 -#define WDT_CTRL_KEY_FIELD __BITS(12,1) -#define WDT_CTRL_KEY_FIELD_V 0xa57 -#define WDT_CTRL_RSTART __BIT(0) -#define WDT_CFG_REG 0x14 -#define WDT_CFG_CONFIG __BITS(1,0) -#define WDT_CFG_CONFIG_SYS 1 -#define WDT_CFG_CONFIG_IRQ 2 -#define WDT_MODE_REG 0x18 -#define WDT_MODE_INTV __BITS(7,4) -#define WDT_MODE_EN __BIT(0) +#define SUN4I_WDT_CTRL_REG 0x00 +#define SUN4I_WDT_CTRL_KEY_FIELD __BITS(12,1) +#define SUN4I_WDT_CTRL_KEY_FIELD_V 0xa57 +#define SUN4I_WDT_CTRL_RSTART __BIT(0) +#define SUN4I_WDT_MODE_REG 0x04 +#define SUN4I_WDT_MODE_INTV __BITS(6,3) +#define SUN4I_WDT_MODE_RST_EN __BIT(1) +#define SUN4I_WDT_MODE_EN __BIT(0) + +#define SUN6I_WDT_IRQ_EN_REG 0x00 +#define SUN6I_WDT_IRQ_EN_EN __BIT(0) +#define SUN6I_WDT_IRQ_STA_REG 0x04 +#define SUN6I_WDT_IRQ_STA_PEND __BIT(0) +#define SUN6I_WDT_CTRL_REG 0x10 +#define SUN6I_WDT_CTRL_KEY_FIELD __BITS(12,1) +#define SUN6I_WDT_CTRL_KEY_FIELD_V 0xa57 +#define SUN6I_WDT_CTRL_RSTART __BIT(0) +#define SUN6I_WDT_CFG_REG 0x14 +#define SUN6I_WDT_CFG_CONFIG __BITS(1,0) +#define SUN6I_WDT_CFG_CONFIG_SYS 1 +#define SUN6I_WDT_CFG_CONFIG_IRQ 2 +#define SUN6I_WDT_MODE_REG 0x18 +#define SUN6I_WDT_MODE_INTV __BITS(7,4) +#define SUN6I_WDT_MODE_EN __BIT(0) -static const int sun6i_periods[] = { +static const int sunxi_periods[] = { 500, 1000, 2000, 3000, 4000, 5000, 6000, 8000, 10000, 12000, 14000, 16000, -1 }; +enum sunxi_wdt_type { + WDT_SUN4I = 1, + WDT_SUN6I, +}; + static const struct of_compat_data compat_data[] = { - { "allwinner,sun6i-a31-wdt", (uintptr_t)&sun6i_periods }, + { "allwinner,sun4i-a10-wdt", WDT_SUN4I }, + { "allwinner,sun6i-a31-wdt", WDT_SUN6I }, { NULL } }; @@ -106,14 +121,52 @@ sunxi_wdt_map_period(struct sunxi_wdt_so } static int -sunxi_wdt_setmode(struct sysmon_wdog *smw) +sun4i_wdt_setmode(struct sysmon_wdog *smw) +{ + struct sunxi_wdt_softc * const sc = smw->smw_cookie; + uint32_t mode; + int intv; + + if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { + WDT_WRITE(sc, SUN4I_WDT_MODE_REG, 0); + } else { + if (smw->smw_period == WDOG_PERIOD_DEFAULT) + smw->smw_period = SUNXI_WDT_PERIOD_DEFAULT; + intv = sunxi_wdt_map_period(sc, smw->smw_period, + &sc->sc_smw.smw_period); + if (intv == -1) + return EINVAL; + + mode = SUN4I_WDT_MODE_EN | SUN4I_WDT_MODE_RST_EN | + __SHIFTIN(intv, SUN4I_WDT_MODE_INTV); + + WDT_WRITE(sc, SUN4I_WDT_MODE_REG, mode); + } + + return 0; +} + +static int +sun4i_wdt_tickle(struct sysmon_wdog *smw) +{ + struct sunxi_wdt_softc * const sc = smw->smw_cookie; + const uint32_t ctrl = SUN4I_WDT_CTRL_RSTART | + __SHIFTIN(SUN4I_WDT_CTRL_KEY_FIELD_V, SUN4I_WDT_CTRL_KEY_FIELD); + + WDT_WRITE(sc, SUN4I_WDT_CTRL_REG, ctrl); + + return 0; +} + +static int +sun6i_wdt_setmode(struct sysmon_wdog *smw) { struct sunxi_wdt_softc * const sc = smw->smw_cookie; uint32_t cfg, mode; int intv; if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { - WDT_WRITE(sc, WDT_MODE_REG, 0); + WDT_WRITE(sc, SUN6I_WDT_MODE_REG, 0); } else { if (smw->smw_period == WDOG_PERIOD_DEFAULT) smw->smw_period = SUNXI_WDT_PERIOD_DEFAULT; @@ -122,24 +175,24 @@ sunxi_wdt_setmode(struct sysmon_wdog *sm if (intv == -1) return EINVAL; - cfg = __SHIFTIN(WDT_CFG_CONFIG_SYS, WDT_CFG_CONFIG); - mode = WDT_MODE_EN | __SHIFTIN(intv, WDT_MODE_INTV); + cfg = __SHIFTIN(SUN6I_WDT_CFG_CONFIG_SYS, SUN6I_WDT_CFG_CONFIG); + mode = SUN6I_WDT_MODE_EN | __SHIFTIN(intv, SUN6I_WDT_MODE_INTV); - WDT_WRITE(sc, WDT_CFG_REG, cfg); - WDT_WRITE(sc, WDT_MODE_REG, mode); + WDT_WRITE(sc, SUN6I_WDT_CFG_REG, cfg); + WDT_WRITE(sc, SUN6I_WDT_MODE_REG, mode); } return 0; } static int -sunxi_wdt_tickle(struct sysmon_wdog *smw) +sun6i_wdt_tickle(struct sysmon_wdog *smw) { struct sunxi_wdt_softc * const sc = smw->smw_cookie; - const uint32_t ctrl = WDT_CTRL_RSTART | - __SHIFTIN(WDT_CTRL_KEY_FIELD_V, WDT_CTRL_KEY_FIELD); + const uint32_t ctrl = SUN6I_WDT_CTRL_RSTART | + __SHIFTIN(SUN6I_WDT_CTRL_KEY_FIELD_V, SUN6I_WDT_CTRL_KEY_FIELD); - WDT_WRITE(sc, WDT_CTRL_REG, ctrl); + WDT_WRITE(sc, SUN6I_WDT_CTRL_REG, ctrl); return 0; } @@ -158,6 +211,7 @@ sunxi_wdt_attach(device_t parent, device struct sunxi_wdt_softc * const sc = device_private(self); struct fdt_attach_args * const faa = aux; const int phandle = faa->faa_phandle; + enum sunxi_wdt_type type; bus_addr_t addr; bus_size_t size; @@ -172,19 +226,30 @@ sunxi_wdt_attach(device_t parent, device aprint_error(": couldn't map registers\n"); return; } - sc->sc_periods = (void *)of_search_compatible(phandle, compat_data)->data; + sc->sc_periods = sunxi_periods; aprint_naive("\n"); aprint_normal(": Watchdog\n"); - WDT_WRITE(sc, WDT_IRQ_EN_REG, 0); - sc->sc_smw.smw_name = device_xname(self); sc->sc_smw.smw_cookie = sc; - sc->sc_smw.smw_setmode = sunxi_wdt_setmode; - sc->sc_smw.smw_tickle = sunxi_wdt_tickle; sc->sc_smw.smw_period = SUNXI_WDT_PERIOD_DEFAULT; + type = of_search_compatible(phandle, compat_data)->data; + switch (type) { + case WDT_SUN4I: + sc->sc_smw.smw_setmode = sun4i_wdt_setmode; + sc->sc_smw.smw_tickle = sun4i_wdt_tickle; + break; + case WDT_SUN6I: + sc->sc_smw.smw_setmode = sun6i_wdt_setmode; + sc->sc_smw.smw_tickle = sun6i_wdt_tickle; + + /* Disable watchdog IRQs */ + WDT_WRITE(sc, SUN6I_WDT_IRQ_EN_REG, 0); + break; + } + aprint_normal_dev(self, "default watchdog period is %u seconds\n", sc->sc_smw.smw_period);