aml8726_wdt.c revision 308325
167761Smsmith/*-
280070Smsmith * Copyright 2013-2015 John Wehle <john@feith.com>
367761Smsmith * All rights reserved.
467761Smsmith *
567761Smsmith * Redistribution and use in source and binary forms, with or without
667761Smsmith * modification, are permitted provided that the following conditions
767761Smsmith * are met:
867761Smsmith * 1. Redistributions of source code must retain the above copyright
967761Smsmith *    notice, this list of conditions and the following disclaimer.
1067761Smsmith * 2. Redistributions in binary form must reproduce the above copyright
1167761Smsmith *    notice, this list of conditions and the following disclaimer in the
1267761Smsmith *    documentation and/or other materials provided with the distribution.
1367761Smsmith *
1467761Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1567761Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1667761Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1767761Smsmith * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1867761Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1967761Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2067761Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2167761Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2267761Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2367761Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2467761Smsmith * SUCH DAMAGE.
2567761Smsmith *
2667761Smsmith */
2767761Smsmith
2867761Smsmith/*
2967761Smsmith * Amlogic aml8726 watchdog driver.
3067761Smsmith */
3180070Smsmith
3267761Smsmith#include <sys/cdefs.h>
3380070Smsmith__FBSDID("$FreeBSD: stable/11/sys/arm/amlogic/aml8726/aml8726_wdt.c 308325 2016-11-05 04:30:44Z mmel $");
3480070Smsmith
3567761Smsmith#include <sys/param.h>
3680070Smsmith#include <sys/systm.h>
3780070Smsmith#include <sys/bus.h>
3880070Smsmith#include <sys/kernel.h>
3980070Smsmith#include <sys/module.h>
4080070Smsmith#include <sys/malloc.h>
4167761Smsmith#include <sys/rman.h>
4267761Smsmith
4380070Smsmith#include <sys/watchdog.h>
4480070Smsmith
4567761Smsmith#include <machine/bus.h>
4669744Smsmith
4780070Smsmith#include <dev/fdt/fdt_common.h>
4880070Smsmith#include <dev/ofw/ofw_bus.h>
4980070Smsmith#include <dev/ofw/ofw_bus_subr.h>
5080070Smsmith
5180070Smsmith#include <arm/amlogic/aml8726/aml8726_soc.h>
5280070Smsmith
5369744Smsmith
5469744Smsmithstruct aml8726_wdt_softc {
5577432Smsmith	device_t		dev;
5691128Smsmith	struct resource	*	res[2];
5769744Smsmith	struct mtx		mtx;
5880070Smsmith	void *			ih_cookie;
5980070Smsmith};
6080070Smsmith
6180070Smsmithstatic struct resource_spec aml8726_wdt_spec[] = {
6280070Smsmith	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
6367761Smsmith	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
6480070Smsmith	{ -1, 0 }
6567761Smsmith};
6667761Smsmith
6767761Smsmithstatic struct {
6867761Smsmith	uint32_t ctrl_cpu_mask;
6980070Smsmith	uint32_t ctrl_en;
7081096Smsmith	uint32_t term_cnt_mask;
7180070Smsmith	uint32_t reset_cnt_mask;
7280070Smsmith} aml8726_wdt_soc_params;
7367761Smsmith
7480070Smsmith/*
7580070Smsmith * devclass_get_device / device_get_softc could be used
7680070Smsmith * to dynamically locate this, however the wdt is a
7767761Smsmith * required device which can't be unloaded so there's
7867761Smsmith * no need for the overhead.
7967761Smsmith */
8067761Smsmithstatic struct aml8726_wdt_softc *aml8726_wdt_sc = NULL;
8167761Smsmith
8267761Smsmith#define	AML_WDT_LOCK(sc)		mtx_lock_spin(&(sc)->mtx)
8367761Smsmith#define	AML_WDT_UNLOCK(sc)		mtx_unlock_spin(&(sc)->mtx)
8467761Smsmith#define	AML_WDT_LOCK_INIT(sc)		\
8567761Smsmith    mtx_init(&(sc)->mtx, device_get_nameunit((sc)->dev), \
8667761Smsmith    "wdt", MTX_SPIN)
8767761Smsmith#define	AML_WDT_LOCK_DESTROY(sc)	mtx_destroy(&(sc)->mtx);
8880070Smsmith
8967761Smsmith#define	AML_WDT_CTRL_REG		0
9067761Smsmith#define	AML_WDT_CTRL_CPU_WDRESET_MASK	aml8726_wdt_soc_params.ctrl_cpu_mask
9189054Smsmith#define	AML_WDT_CTRL_CPU_WDRESET_SHIFT	24
9267761Smsmith#define	AML_WDT_CTRL_IRQ_EN		(1 << 23)
9367761Smsmith#define	AML_WDT_CTRL_EN			aml8726_wdt_soc_params.ctrl_en
9480070Smsmith#define	AML_WDT_CTRL_TERMINAL_CNT_MASK	aml8726_wdt_soc_params.term_cnt_mask
9580070Smsmith#define	AML_WDT_CTRL_TERMINAL_CNT_SHIFT	0
9680070Smsmith#define	AML_WDT_RESET_REG		4
9780070Smsmith#define	AML_WDT_RESET_CNT_MASK		aml8726_wdt_soc_params.reset_cnt_mask
9881096Smsmith#define	AML_WDT_RESET_CNT_SHIFT		0
9980070Smsmith
10080070Smsmith#define	CSR_WRITE_4(sc, reg, val)	bus_write_4((sc)->res[0], reg, (val))
10180070Smsmith#define	CSR_READ_4(sc, reg)		bus_read_4((sc)->res[0], reg)
10280070Smsmith#define	CSR_BARRIER(sc, reg)		bus_barrier((sc)->res[0], reg, 4, \
10380070Smsmith    (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE))
10480070Smsmith
10580070Smsmithstatic void
10680070Smsmithaml8726_wdt_watchdog(void *private, u_int cmd, int *error)
10791237Sphk{
10880070Smsmith	struct aml8726_wdt_softc *sc = (struct aml8726_wdt_softc *)private;
10991237Sphk	uint32_t wcr;
11091237Sphk	uint64_t tens_of_usec;
11191237Sphk
11291237Sphk	AML_WDT_LOCK(sc);
11391237Sphk
11491237Sphk	tens_of_usec = (((uint64_t)1 << (cmd & WD_INTERVAL)) + 9999) / 10000;
11591237Sphk
11691237Sphk	if (cmd != 0 && tens_of_usec <= (AML_WDT_CTRL_TERMINAL_CNT_MASK >>
11791237Sphk	    AML_WDT_CTRL_TERMINAL_CNT_SHIFT)) {
11891237Sphk
11991237Sphk		wcr = AML_WDT_CTRL_CPU_WDRESET_MASK |
12091237Sphk		    AML_WDT_CTRL_EN | ((uint32_t)tens_of_usec <<
12191237Sphk		    AML_WDT_CTRL_TERMINAL_CNT_SHIFT);
12291237Sphk
12391237Sphk		CSR_WRITE_4(sc, AML_WDT_RESET_REG, 0);
12491237Sphk		CSR_WRITE_4(sc, AML_WDT_CTRL_REG, wcr);
12591237Sphk
12691237Sphk		*error = 0;
12791237Sphk	} else
12891237Sphk		CSR_WRITE_4(sc, AML_WDT_CTRL_REG,
12991237Sphk		    (CSR_READ_4(sc, AML_WDT_CTRL_REG) &
13091237Sphk		    ~(AML_WDT_CTRL_IRQ_EN | AML_WDT_CTRL_EN)));
13191237Sphk
13291237Sphk	AML_WDT_UNLOCK(sc);
13391237Sphk}
13493093Sphk
13593093Sphkstatic int
13693093Sphkaml8726_wdt_intr(void *arg)
13793093Sphk{
13891237Sphk	struct aml8726_wdt_softc *sc = (struct aml8726_wdt_softc *)arg;
13991237Sphk
14091237Sphk	/*
14180070Smsmith	 * Normally a timeout causes a hardware reset, however
14280070Smsmith	 * the watchdog timer can be configured to cause an
14380070Smsmith	 * interrupt instead by setting AML_WDT_CTRL_IRQ_EN
14480070Smsmith	 * and clearing AML_WDT_CTRL_CPU_WDRESET_MASK.
14567761Smsmith	 */
14667761Smsmith
14767761Smsmith	AML_WDT_LOCK(sc);
14880070Smsmith
14980070Smsmith	CSR_WRITE_4(sc, AML_WDT_CTRL_REG,
15091237Sphk	    (CSR_READ_4(sc, AML_WDT_CTRL_REG) & ~(AML_WDT_CTRL_IRQ_EN |
15167761Smsmith	    AML_WDT_CTRL_EN)));
15291128Smsmith
15369744Smsmith	CSR_BARRIER(sc, AML_WDT_CTRL_REG);
15469744Smsmith
15569744Smsmith	AML_WDT_UNLOCK(sc);
15669744Smsmith
15771872Smsmith	device_printf(sc->dev, "timeout expired\n");
15869744Smsmith
15971872Smsmith	return (FILTER_HANDLED);
16067761Smsmith}
16167761Smsmith
16269744Smsmithstatic int
16367761Smsmithaml8726_wdt_probe(device_t dev)
16480070Smsmith{
16580070Smsmith
16680070Smsmith	if (!ofw_bus_status_okay(dev))
16780070Smsmith		return (ENXIO);
16880070Smsmith
16969744Smsmith	if (!ofw_bus_is_compatible(dev, "amlogic,meson6-wdt"))
17067761Smsmith		return (ENXIO);
17180070Smsmith
17280070Smsmith	device_set_desc(dev, "Amlogic aml8726 WDT");
17367761Smsmith
17480070Smsmith	return (BUS_PROBE_DEFAULT);
17591237Sphk}
17691237Sphk
17791237Sphkstatic int
17891237Sphkaml8726_wdt_attach(device_t dev)
17991237Sphk{
18091237Sphk	struct aml8726_wdt_softc *sc = device_get_softc(dev);
18191237Sphk
18291237Sphk	/* There should be exactly one instance. */
18391237Sphk	if (aml8726_wdt_sc != NULL)
18491237Sphk		return (ENXIO);
18580070Smsmith
18680070Smsmith	sc->dev = dev;
18771872Smsmith
18867761Smsmith	if (bus_alloc_resources(dev, aml8726_wdt_spec, sc->res)) {
18969744Smsmith		device_printf(dev, "can not allocate resources for device\n");
19069744Smsmith		return (ENXIO);
19167761Smsmith	}
19267761Smsmith
19367761Smsmith	/*
19467761Smsmith	 * Certain bitfields are dependent on the hardware revision.
19567761Smsmith	 */
19680070Smsmith	switch (aml8726_soc_hw_rev) {
19767761Smsmith	case AML_SOC_HW_REV_M8:
19867761Smsmith		aml8726_wdt_soc_params.ctrl_cpu_mask = 0xf <<
19967761Smsmith		    AML_WDT_CTRL_CPU_WDRESET_SHIFT;
20067761Smsmith		switch (aml8726_soc_metal_rev) {
20167761Smsmith		case AML_SOC_M8_METAL_REV_M2_A:
20267761Smsmith			aml8726_wdt_soc_params.ctrl_en = 1 << 19;
20367761Smsmith			aml8726_wdt_soc_params.term_cnt_mask = 0x07ffff <<
20480070Smsmith			    AML_WDT_CTRL_TERMINAL_CNT_SHIFT;
20580070Smsmith			aml8726_wdt_soc_params.reset_cnt_mask = 0x07ffff <<
20667761Smsmith			    AML_WDT_RESET_CNT_SHIFT;
20780070Smsmith			break;
20881096Smsmith		default:
20980070Smsmith			aml8726_wdt_soc_params.ctrl_en = 1 << 22;
21080070Smsmith			aml8726_wdt_soc_params.term_cnt_mask = 0x3fffff <<
21180070Smsmith			    AML_WDT_CTRL_TERMINAL_CNT_SHIFT;
21280070Smsmith			aml8726_wdt_soc_params.reset_cnt_mask = 0x3fffff <<
21380602Smsmith			    AML_WDT_RESET_CNT_SHIFT;
21467761Smsmith			break;
21580070Smsmith		}
21680070Smsmith		break;
21781096Smsmith	case AML_SOC_HW_REV_M8B:
21881096Smsmith		aml8726_wdt_soc_params.ctrl_cpu_mask = 0xf <<
21981096Smsmith		    AML_WDT_CTRL_CPU_WDRESET_SHIFT;
22081096Smsmith		aml8726_wdt_soc_params.ctrl_en = 1 << 19;
22181096Smsmith		aml8726_wdt_soc_params.term_cnt_mask = 0x07ffff <<
22281096Smsmith		    AML_WDT_CTRL_TERMINAL_CNT_SHIFT;
22381096Smsmith		aml8726_wdt_soc_params.reset_cnt_mask = 0x07ffff <<
22481096Smsmith		    AML_WDT_RESET_CNT_SHIFT;
22581096Smsmith		break;
22681096Smsmith	default:
22781096Smsmith		aml8726_wdt_soc_params.ctrl_cpu_mask = 3 <<
22881096Smsmith		    AML_WDT_CTRL_CPU_WDRESET_SHIFT;
22981096Smsmith		aml8726_wdt_soc_params.ctrl_en = 1 << 22;
23081096Smsmith		aml8726_wdt_soc_params.term_cnt_mask = 0x3fffff <<
23191237Sphk		    AML_WDT_CTRL_TERMINAL_CNT_SHIFT;
23281096Smsmith		aml8726_wdt_soc_params.reset_cnt_mask = 0x3fffff <<
23381096Smsmith		    AML_WDT_RESET_CNT_SHIFT;
23481096Smsmith		break;
23581096Smsmith	}
23680070Smsmith
23780070Smsmith	/*
23880070Smsmith	 * Disable the watchdog.
23980070Smsmith	 */
24080070Smsmith	CSR_WRITE_4(sc, AML_WDT_CTRL_REG,
24180070Smsmith	    (CSR_READ_4(sc, AML_WDT_CTRL_REG) & ~(AML_WDT_CTRL_IRQ_EN |
24280070Smsmith	    AML_WDT_CTRL_EN)));
24380070Smsmith
24480070Smsmith	/*
24580070Smsmith	 * Initialize the mutex prior to installing the interrupt handler
24680070Smsmith	 * in case of a spurious interrupt.
24780070Smsmith	 */
24880070Smsmith	AML_WDT_LOCK_INIT(sc);
24980070Smsmith
25080070Smsmith	if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
25180070Smsmith	    aml8726_wdt_intr, NULL, sc, &sc->ih_cookie)) {
25280070Smsmith		device_printf(dev, "could not setup interrupt handler\n");
25380070Smsmith		bus_release_resources(dev, aml8726_wdt_spec, sc->res);
25480070Smsmith		AML_WDT_LOCK_DESTROY(sc);
25580070Smsmith		return (ENXIO);
25680070Smsmith	}
25780070Smsmith
25880070Smsmith	aml8726_wdt_sc = sc;
25980070Smsmith
26080070Smsmith	EVENTHANDLER_REGISTER(watchdog_list, aml8726_wdt_watchdog, sc, 0);
26180070Smsmith
26280070Smsmith	return (0);
26380070Smsmith}
26480070Smsmith
26580070Smsmithstatic int
26680070Smsmithaml8726_wdt_detach(device_t dev)
26780070Smsmith{
26880070Smsmith
26980070Smsmith	return (EBUSY);
27080070Smsmith}
27180070Smsmith
27280070Smsmithstatic device_method_t aml8726_wdt_methods[] = {
27380070Smsmith	/* Device interface */
27480070Smsmith	DEVMETHOD(device_probe,		aml8726_wdt_probe),
27580070Smsmith	DEVMETHOD(device_attach,	aml8726_wdt_attach),
27680070Smsmith	DEVMETHOD(device_detach,	aml8726_wdt_detach),
27780070Smsmith
27880070Smsmith	DEVMETHOD_END
27980070Smsmith};
28080070Smsmith
28180070Smsmithstatic driver_t aml8726_wdt_driver = {
28280070Smsmith	"wdt",
28380070Smsmith	aml8726_wdt_methods,
28480070Smsmith	sizeof(struct aml8726_wdt_softc),
28580070Smsmith};
28680070Smsmith
28780070Smsmithstatic devclass_t aml8726_wdt_devclass;
28880070Smsmith
28980602SmsmithEARLY_DRIVER_MODULE(wdt, simplebus, aml8726_wdt_driver, aml8726_wdt_devclass,
29080602Smsmith    0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
29180602Smsmith
29281096Smsmithvoid
29381096Smsmithcpu_reset()
29481096Smsmith{
29581096Smsmith
29681096Smsmith	/* Watchdog has not yet been initialized */
29781096Smsmith	if (aml8726_wdt_sc == NULL)
29881096Smsmith		printf("Reset hardware has not yet been initialized.\n");
29981096Smsmith	else {
30081096Smsmith		CSR_WRITE_4(aml8726_wdt_sc, AML_WDT_RESET_REG, 0);
30181096Smsmith		CSR_WRITE_4(aml8726_wdt_sc, AML_WDT_CTRL_REG,
30280602Smsmith		    (AML_WDT_CTRL_CPU_WDRESET_MASK | AML_WDT_CTRL_EN |
30380602Smsmith		    (10 << AML_WDT_CTRL_TERMINAL_CNT_SHIFT)));
30480602Smsmith	}
30580602Smsmith
30680602Smsmith	while (1);
30780602Smsmith}
30880602Smsmith