dwcwdt_fdt.c revision 1.5
11.5Sthorpej/* $NetBSD: dwcwdt_fdt.c,v 1.5 2021/01/27 03:10:21 thorpej Exp $ */ 21.1Sjmcneill 31.1Sjmcneill/*- 41.1Sjmcneill * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca> 51.1Sjmcneill * All rights reserved. 61.1Sjmcneill * 71.1Sjmcneill * Redistribution and use in source and binary forms, with or without 81.1Sjmcneill * modification, are permitted provided that the following conditions 91.1Sjmcneill * are met: 101.1Sjmcneill * 1. Redistributions of source code must retain the above copyright 111.1Sjmcneill * notice, this list of conditions and the following disclaimer. 121.1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright 131.1Sjmcneill * notice, this list of conditions and the following disclaimer in the 141.1Sjmcneill * documentation and/or other materials provided with the distribution. 151.1Sjmcneill * 161.1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 171.1Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 181.1Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 191.1Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 201.1Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 211.1Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 221.1Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 231.1Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 241.1Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 251.1Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 261.1Sjmcneill * SUCH DAMAGE. 271.1Sjmcneill */ 281.1Sjmcneill 291.1Sjmcneill#include <sys/cdefs.h> 301.5Sthorpej__KERNEL_RCSID(0, "$NetBSD: dwcwdt_fdt.c,v 1.5 2021/01/27 03:10:21 thorpej Exp $"); 311.1Sjmcneill 321.1Sjmcneill#include <sys/param.h> 331.1Sjmcneill#include <sys/bus.h> 341.1Sjmcneill#include <sys/device.h> 351.1Sjmcneill#include <sys/intr.h> 361.1Sjmcneill#include <sys/systm.h> 371.1Sjmcneill#include <sys/mutex.h> 381.1Sjmcneill#include <sys/wdog.h> 391.1Sjmcneill 401.1Sjmcneill#include <dev/sysmon/sysmonvar.h> 411.1Sjmcneill 421.1Sjmcneill#include <dev/fdt/fdtvar.h> 431.1Sjmcneill 441.1Sjmcneill#define WDT_CR 0x00 451.1Sjmcneill#define WDT_CR_RST_PULSE_LENGTH __BITS(4,2) 461.1Sjmcneill#define WDT_CR_RESP_MODE __BIT(1) 471.1Sjmcneill#define WDT_CR_WDT_EN __BIT(0) 481.1Sjmcneill 491.1Sjmcneill#define WDT_TORR 0x04 501.1Sjmcneill#define WDT_TORR_TIMEOUT_PERIOD __BITS(3,0) 511.1Sjmcneill 521.1Sjmcneill#define WDT_CCVR 0x08 531.1Sjmcneill 541.1Sjmcneill#define WDT_CRR 0x0c 551.1Sjmcneill#define WDT_CRR_CNT_RESTART __BITS(7,0) 561.1Sjmcneill#define WDT_CRR_CNT_RESTART_MAGIC 0x76 571.1Sjmcneill 581.1Sjmcneill#define WDT_STAT 0x10 591.1Sjmcneill#define WDT_STAT_WDT_STATUS __BIT(0) 601.1Sjmcneill 611.1Sjmcneill#define WDT_EOI 0x14 621.1Sjmcneill#define WDT_EOI_WDT_INT_CLR __BIT(0) 631.1Sjmcneill 641.2Saymericstatic const uint32_t wdt_torr[] = { 651.1Sjmcneill 0x0000ffff, 661.1Sjmcneill 0x0001ffff, 671.1Sjmcneill 0x0003ffff, 681.1Sjmcneill 0x0007ffff, 691.1Sjmcneill 0x000fffff, 701.1Sjmcneill 0x001fffff, 711.1Sjmcneill 0x003fffff, 721.1Sjmcneill 0x007fffff, 731.1Sjmcneill 0x00ffffff, 741.1Sjmcneill 0x01ffffff, 751.1Sjmcneill 0x03ffffff, 761.1Sjmcneill 0x07ffffff, 771.1Sjmcneill 0x0fffffff, 781.1Sjmcneill 0x1fffffff, 791.1Sjmcneill 0x3fffffff, 801.1Sjmcneill 0x7fffffff, 811.1Sjmcneill}; 821.1Sjmcneill 831.1Sjmcneill#define DWCWDT_PERIOD_DEFAULT 15 841.1Sjmcneill 851.5Sthorpejstatic const struct device_compatible_entry compat_data[] = { 861.5Sthorpej { .compat = "snps,dw-wdt" }, 871.5Sthorpej DEVICE_COMPAT_EOL 881.1Sjmcneill}; 891.1Sjmcneill 901.1Sjmcneillstruct dwcwdt_softc { 911.1Sjmcneill device_t sc_dev; 921.1Sjmcneill bus_space_tag_t sc_bst; 931.1Sjmcneill bus_space_handle_t sc_bsh; 941.1Sjmcneill struct sysmon_wdog sc_smw; 951.1Sjmcneill u_int sc_clkrate; 961.1Sjmcneill}; 971.1Sjmcneill 981.1Sjmcneill#define RD4(sc, reg) \ 991.1Sjmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 1001.1Sjmcneill#define WR4(sc, reg, val) \ 1011.1Sjmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 1021.1Sjmcneill 1031.1Sjmcneillstatic int 1041.1Sjmcneilldwcwdt_map_period(struct dwcwdt_softc *sc, u_int period, 1051.1Sjmcneill u_int *aperiod) 1061.1Sjmcneill{ 1071.1Sjmcneill int i; 1081.1Sjmcneill 1091.1Sjmcneill if (period == 0) 1101.1Sjmcneill return -1; 1111.1Sjmcneill 1121.1Sjmcneill for (i = 0; i < __arraycount(wdt_torr); i++) { 1131.1Sjmcneill const u_int ms = (u_int)((((uint64_t)wdt_torr[i] + 1) * 1000) / sc->sc_clkrate); 1141.4Stnn if (ms >= period * 1000) { 1151.4Stnn *aperiod = ms / 1000; 1161.1Sjmcneill return i; 1171.1Sjmcneill } 1181.1Sjmcneill } 1191.1Sjmcneill 1201.1Sjmcneill return -1; 1211.1Sjmcneill} 1221.1Sjmcneill 1231.1Sjmcneillstatic int 1241.4Stnndwcwdt_tickle(struct sysmon_wdog *smw) 1251.4Stnn{ 1261.4Stnn struct dwcwdt_softc * const sc = smw->smw_cookie; 1271.4Stnn const uint32_t crr = 1281.4Stnn __SHIFTIN(WDT_CRR_CNT_RESTART_MAGIC, WDT_CRR_CNT_RESTART); 1291.4Stnn 1301.4Stnn WR4(sc, WDT_CRR, crr); 1311.4Stnn 1321.4Stnn return 0; 1331.4Stnn} 1341.4Stnn 1351.4Stnnstatic int 1361.1Sjmcneilldwcwdt_setmode(struct sysmon_wdog *smw) 1371.1Sjmcneill{ 1381.1Sjmcneill struct dwcwdt_softc * const sc = smw->smw_cookie; 1391.1Sjmcneill uint32_t cr, torr; 1401.1Sjmcneill int intv; 1411.1Sjmcneill 1421.4Stnn if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED) { 1431.4Stnn /* Watchdog can only be disarmed by a reset */ 1441.1Sjmcneill return EIO; 1451.4Stnn } 1461.1Sjmcneill 1471.1Sjmcneill if (smw->smw_period == WDOG_PERIOD_DEFAULT) 1481.1Sjmcneill smw->smw_period = DWCWDT_PERIOD_DEFAULT; 1491.1Sjmcneill 1501.1Sjmcneill intv = dwcwdt_map_period(sc, smw->smw_period, 1511.1Sjmcneill &sc->sc_smw.smw_period); 1521.1Sjmcneill if (intv == -1) 1531.1Sjmcneill return EINVAL; 1541.1Sjmcneill 1551.1Sjmcneill torr = __SHIFTIN(intv, WDT_TORR_TIMEOUT_PERIOD); 1561.1Sjmcneill WR4(sc, WDT_TORR, torr); 1571.4Stnn dwcwdt_tickle(smw); 1581.1Sjmcneill cr = RD4(sc, WDT_CR); 1591.1Sjmcneill cr &= ~WDT_CR_RESP_MODE; 1601.1Sjmcneill cr |= WDT_CR_WDT_EN; 1611.1Sjmcneill WR4(sc, WDT_CR, cr); 1621.1Sjmcneill 1631.1Sjmcneill return 0; 1641.1Sjmcneill} 1651.1Sjmcneill 1661.1Sjmcneillstatic int 1671.1Sjmcneilldwcwdt_match(device_t parent, cfdata_t cf, void *aux) 1681.1Sjmcneill{ 1691.1Sjmcneill struct fdt_attach_args * const faa = aux; 1701.1Sjmcneill 1711.5Sthorpej return of_compatible_match(faa->faa_phandle, compat_data); 1721.1Sjmcneill} 1731.1Sjmcneill 1741.1Sjmcneillstatic void 1751.1Sjmcneilldwcwdt_attach(device_t parent, device_t self, void *aux) 1761.1Sjmcneill{ 1771.1Sjmcneill struct dwcwdt_softc * const sc = device_private(self); 1781.1Sjmcneill struct fdt_attach_args * const faa = aux; 1791.1Sjmcneill const int phandle = faa->faa_phandle; 1801.1Sjmcneill struct fdtbus_reset *rst; 1811.1Sjmcneill struct clk *clk; 1821.1Sjmcneill bus_addr_t addr; 1831.1Sjmcneill bus_size_t size; 1841.1Sjmcneill 1851.1Sjmcneill if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 1861.1Sjmcneill aprint_error(": couldn't get registers\n"); 1871.1Sjmcneill return; 1881.1Sjmcneill } 1891.1Sjmcneill 1901.1Sjmcneill clk = fdtbus_clock_get_index(phandle, 0); 1911.1Sjmcneill if (clk == NULL || clk_enable(clk) != 0) { 1921.1Sjmcneill aprint_error(": couldn't enable clock\n"); 1931.1Sjmcneill return; 1941.1Sjmcneill } 1951.1Sjmcneill rst = fdtbus_reset_get_index(phandle, 0); 1961.3Saymeric if (rst && fdtbus_reset_assert(rst) != 0) { 1971.3Saymeric aprint_error(": couldn't assert reset\n"); 1981.3Saymeric return; 1991.3Saymeric } 2001.1Sjmcneill if (rst && fdtbus_reset_deassert(rst) != 0) { 2011.1Sjmcneill aprint_error(": couldn't de-assert reset\n"); 2021.1Sjmcneill return; 2031.1Sjmcneill } 2041.1Sjmcneill 2051.1Sjmcneill sc->sc_dev = self; 2061.1Sjmcneill sc->sc_bst = faa->faa_bst; 2071.1Sjmcneill if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 2081.1Sjmcneill aprint_error(": couldn't map registers\n"); 2091.1Sjmcneill return; 2101.1Sjmcneill } 2111.1Sjmcneill sc->sc_clkrate = clk_get_rate(clk); 2121.1Sjmcneill 2131.1Sjmcneill aprint_naive("\n"); 2141.1Sjmcneill aprint_normal(": DesignWare Watchdog Timer\n"); 2151.1Sjmcneill 2161.1Sjmcneill sc->sc_smw.smw_name = device_xname(self); 2171.1Sjmcneill sc->sc_smw.smw_cookie = sc; 2181.1Sjmcneill sc->sc_smw.smw_period = DWCWDT_PERIOD_DEFAULT; 2191.1Sjmcneill sc->sc_smw.smw_setmode = dwcwdt_setmode; 2201.1Sjmcneill sc->sc_smw.smw_tickle = dwcwdt_tickle; 2211.1Sjmcneill 2221.1Sjmcneill aprint_normal_dev(self, 2231.1Sjmcneill "default watchdog period is %u seconds\n", 2241.1Sjmcneill sc->sc_smw.smw_period); 2251.1Sjmcneill 2261.1Sjmcneill if (sysmon_wdog_register(&sc->sc_smw) != 0) 2271.1Sjmcneill aprint_error_dev(self, "couldn't register with sysmon\n"); 2281.1Sjmcneill} 2291.1Sjmcneill 2301.1SjmcneillCFATTACH_DECL_NEW(dwcwdt_fdt, sizeof(struct dwcwdt_softc), 2311.1Sjmcneill dwcwdt_match, dwcwdt_attach, NULL, NULL); 232