dwcwdt_fdt.c revision 1.1
11.1Sjmcneill/* $NetBSD: dwcwdt_fdt.c,v 1.1 2018/06/30 10:50:30 jmcneill 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.1Sjmcneill__KERNEL_RCSID(0, "$NetBSD: dwcwdt_fdt.c,v 1.1 2018/06/30 10:50:30 jmcneill 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.1Sjmcneillstatic const uint8_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.1Sjmcneillstatic const char * const compatible[] = {
861.1Sjmcneill	"snps,dw-wdt",
871.1Sjmcneill	NULL
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.1Sjmcneill		if (ms >= period) {
1151.1Sjmcneill			*aperiod = ms;
1161.1Sjmcneill			return i;
1171.1Sjmcneill		}
1181.1Sjmcneill	}
1191.1Sjmcneill
1201.1Sjmcneill	return -1;
1211.1Sjmcneill}
1221.1Sjmcneill
1231.1Sjmcneillstatic int
1241.1Sjmcneilldwcwdt_setmode(struct sysmon_wdog *smw)
1251.1Sjmcneill{
1261.1Sjmcneill	struct dwcwdt_softc * const sc = smw->smw_cookie;
1271.1Sjmcneill	uint32_t cr, torr;
1281.1Sjmcneill	int intv;
1291.1Sjmcneill
1301.1Sjmcneill	if ((smw->smw_mode & WDOG_MODE_MASK) == WDOG_MODE_DISARMED)
1311.1Sjmcneill		return EIO;
1321.1Sjmcneill
1331.1Sjmcneill	if (smw->smw_period == WDOG_PERIOD_DEFAULT)
1341.1Sjmcneill		smw->smw_period = DWCWDT_PERIOD_DEFAULT;
1351.1Sjmcneill
1361.1Sjmcneill	intv = dwcwdt_map_period(sc, smw->smw_period,
1371.1Sjmcneill	    &sc->sc_smw.smw_period);
1381.1Sjmcneill	if (intv == -1)
1391.1Sjmcneill		return EINVAL;
1401.1Sjmcneill
1411.1Sjmcneill	torr = __SHIFTIN(intv, WDT_TORR_TIMEOUT_PERIOD);
1421.1Sjmcneill	WR4(sc, WDT_TORR, torr);
1431.1Sjmcneill
1441.1Sjmcneill	cr = RD4(sc, WDT_CR);
1451.1Sjmcneill	cr &= ~WDT_CR_RESP_MODE;
1461.1Sjmcneill	cr |= WDT_CR_WDT_EN;
1471.1Sjmcneill	WR4(sc, WDT_CR, cr);
1481.1Sjmcneill
1491.1Sjmcneill	return 0;
1501.1Sjmcneill}
1511.1Sjmcneill
1521.1Sjmcneillstatic int
1531.1Sjmcneilldwcwdt_tickle(struct sysmon_wdog *smw)
1541.1Sjmcneill{
1551.1Sjmcneill	struct dwcwdt_softc * const sc = smw->smw_cookie;
1561.1Sjmcneill	const uint32_t crr =
1571.1Sjmcneill	    __SHIFTIN(WDT_CRR_CNT_RESTART_MAGIC, WDT_CRR_CNT_RESTART);
1581.1Sjmcneill
1591.1Sjmcneill	WR4(sc, WDT_CRR, crr);
1601.1Sjmcneill
1611.1Sjmcneill	return 0;
1621.1Sjmcneill}
1631.1Sjmcneill
1641.1Sjmcneillstatic int
1651.1Sjmcneilldwcwdt_match(device_t parent, cfdata_t cf, void *aux)
1661.1Sjmcneill{
1671.1Sjmcneill	struct fdt_attach_args * const faa = aux;
1681.1Sjmcneill
1691.1Sjmcneill	return of_match_compatible(faa->faa_phandle, compatible);
1701.1Sjmcneill}
1711.1Sjmcneill
1721.1Sjmcneillstatic void
1731.1Sjmcneilldwcwdt_attach(device_t parent, device_t self, void *aux)
1741.1Sjmcneill{
1751.1Sjmcneill	struct dwcwdt_softc * const sc = device_private(self);
1761.1Sjmcneill	struct fdt_attach_args * const faa = aux;
1771.1Sjmcneill	const int phandle = faa->faa_phandle;
1781.1Sjmcneill	struct fdtbus_reset *rst;
1791.1Sjmcneill	struct clk *clk;
1801.1Sjmcneill	bus_addr_t addr;
1811.1Sjmcneill	bus_size_t size;
1821.1Sjmcneill
1831.1Sjmcneill	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
1841.1Sjmcneill		aprint_error(": couldn't get registers\n");
1851.1Sjmcneill		return;
1861.1Sjmcneill	}
1871.1Sjmcneill
1881.1Sjmcneill	clk = fdtbus_clock_get_index(phandle, 0);
1891.1Sjmcneill	if (clk == NULL || clk_enable(clk) != 0) {
1901.1Sjmcneill		aprint_error(": couldn't enable clock\n");
1911.1Sjmcneill		return;
1921.1Sjmcneill	}
1931.1Sjmcneill	rst = fdtbus_reset_get_index(phandle, 0);
1941.1Sjmcneill	if (rst && fdtbus_reset_deassert(rst) != 0) {
1951.1Sjmcneill		aprint_error(": couldn't de-assert reset\n");
1961.1Sjmcneill		return;
1971.1Sjmcneill	}
1981.1Sjmcneill
1991.1Sjmcneill	sc->sc_dev = self;
2001.1Sjmcneill	sc->sc_bst = faa->faa_bst;
2011.1Sjmcneill	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
2021.1Sjmcneill		aprint_error(": couldn't map registers\n");
2031.1Sjmcneill		return;
2041.1Sjmcneill	}
2051.1Sjmcneill	sc->sc_clkrate = clk_get_rate(clk);
2061.1Sjmcneill
2071.1Sjmcneill	aprint_naive("\n");
2081.1Sjmcneill	aprint_normal(": DesignWare Watchdog Timer\n");
2091.1Sjmcneill
2101.1Sjmcneill	sc->sc_smw.smw_name = device_xname(self);
2111.1Sjmcneill	sc->sc_smw.smw_cookie = sc;
2121.1Sjmcneill	sc->sc_smw.smw_period = DWCWDT_PERIOD_DEFAULT;
2131.1Sjmcneill	sc->sc_smw.smw_setmode = dwcwdt_setmode;
2141.1Sjmcneill	sc->sc_smw.smw_tickle = dwcwdt_tickle;
2151.1Sjmcneill
2161.1Sjmcneill	aprint_normal_dev(self,
2171.1Sjmcneill	    "default watchdog period is %u seconds\n",
2181.1Sjmcneill	    sc->sc_smw.smw_period);
2191.1Sjmcneill
2201.1Sjmcneill	if (sysmon_wdog_register(&sc->sc_smw) != 0)
2211.1Sjmcneill		aprint_error_dev(self, "couldn't register with sysmon\n");
2221.1Sjmcneill}
2231.1Sjmcneill
2241.1SjmcneillCFATTACH_DECL_NEW(dwcwdt_fdt, sizeof(struct dwcwdt_softc),
2251.1Sjmcneill	dwcwdt_match, dwcwdt_attach, NULL, NULL);
226