summaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/ar9344_wdt.c
blob: c7cd552dc7115fa18765e4037d306a152a32f68f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
/*
 *  AR9344 Watchdog driver
 *
 *  Copyright (C) 2017 Oleksij Rempel <linux@rempel-privat.de>
 *
 *  This program is free software; you can redistribute it and/or modify it
 *  under  the terms of the GNU General  Public License as published by the
 *  Free Software Foundation;  either version 2 of the License, or (at your
 *  option) any later version.
 *
 */

#include <common.h>
#include <errno.h>
#include <init.h>
#include <io.h>
#include <linux/clk.h>
#include <of.h>
#include <reset_source.h>
#include <watchdog.h>

#define AR9344_WD_REG_CTRL		0x00
#define AR9344_WD_CTRL_LAST_RESET	BIT(31)
#define AR9344_WD_CTRL_ACTION_MASK	3
#define AR9344_WD_CTRL_ACTION_NONE	0	/* no action */
#define AR9344_WD_CTRL_ACTION_GPI	1	/* general purpose interrupt */
#define AR9344_WD_CTRL_ACTION_NMI	2	/* NMI */
#define AR9344_WD_CTRL_ACTION_FCR	3	/* full chip reset */

#define AR9344_WD_REG_TIMER		0x04

#define to_ar9344_wd(h) container_of(h, struct ar9344_wd, wd)

struct ar9344_wd {
	struct watchdog		wd;
	void __iomem		*base;
	struct device_d		*dev;
	unsigned int		rate;
};

static int ar9344_watchdog_set_timeout(struct watchdog *wd, unsigned timeout)
{
	struct ar9344_wd *priv = to_ar9344_wd(wd);
	u32 val, ctrl;

	if (timeout) {
		ctrl = AR9344_WD_CTRL_ACTION_FCR;
		val = timeout * priv->rate;
	} else {
		ctrl = AR9344_WD_CTRL_ACTION_NONE;
		val = U32_MAX;
	}

	dev_dbg(priv->dev, "%s: %d, timer:%x, ctrl: %x \n", __func__,
		timeout, val, ctrl);

	iowrite32be(val, priv->base + AR9344_WD_REG_TIMER);
	iowrite32be(ctrl, priv->base + AR9344_WD_REG_CTRL);

	return 0;
}

static void ar9344_watchdog_detect_reset_source(struct ar9344_wd *priv)
{
	u32 val = readw(priv->base + AR9344_WD_REG_CTRL);

	if (val & AR9344_WD_CTRL_LAST_RESET)
		reset_source_set(RESET_WDG);

	/* else keep the default 'unknown' state */
}

static int ar9344_wdt_probe(struct device_d *dev)
{
	struct resource *iores;
	struct ar9344_wd *priv;
	struct clk *clk;
	int ret;

	priv = xzalloc(sizeof(struct ar9344_wd));
	iores = dev_request_mem_resource(dev, 0);
	if (IS_ERR(iores)) {
		dev_err(dev, "could not get memory region\n");
		ret = PTR_ERR(iores);
		goto on_error;
	}

	priv->base = IOMEM(iores->start);
	priv->wd.set_timeout = ar9344_watchdog_set_timeout;
	priv->wd.hwdev = dev;
	priv->dev = dev;

	dev->priv = priv;

	ar9344_watchdog_detect_reset_source(priv);

	clk = clk_get(dev, NULL);
	if (IS_ERR(clk)) {
		dev_err(dev, "could not get clk\n");
		ret = PTR_ERR(clk);
		goto on_error;
	}

	clk_enable(clk);

	priv->rate = clk_get_rate(clk);
	if (priv->rate == 0) {
		ret = -EINVAL;
		goto on_error;
	}

	priv->wd.timeout_max = U32_MAX / priv->rate;

	ret = watchdog_register(&priv->wd);
	if (ret)
		goto on_error;

	return 0;

on_error:
	free(priv);
	return ret;
}

static __maybe_unused struct of_device_id ar9344_wdt_dt_ids[] = {
	{
		.compatible = "qca,ar9344-wdt",
	}, {
		/* sentinel */
	}
};

static struct driver_d ar9344_wdt_driver = {
	.name   = "ar9344-wdt",
	.probe  = ar9344_wdt_probe,
	.of_compatible = DRV_OF_COMPAT(ar9344_wdt_dt_ids),
};
device_platform_driver(ar9344_wdt_driver);