Blame view

drivers/watchdog/ftwdt010_wdt.c 5.25 KB
d2912cb15   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
eca10ae60   Linus Walleij   watchdog: add dri...
2
  /*
766a2aad6   Linus Walleij   watchdog: gemini/...
3
   * Watchdog driver for Faraday Technology FTWDT010
eca10ae60   Linus Walleij   watchdog: add dri...
4
5
6
7
8
   *
   * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
   *
   * Inspired by the out-of-tree drivers from OpenWRT:
   * Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
eca10ae60   Linus Walleij   watchdog: add dri...
9
10
11
12
13
14
15
16
17
18
19
20
   */
  
  #include <linux/bitops.h>
  #include <linux/init.h>
  #include <linux/interrupt.h>
  #include <linux/io.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/of_device.h>
  #include <linux/platform_device.h>
  #include <linux/slab.h>
  #include <linux/watchdog.h>
766a2aad6   Linus Walleij   watchdog: gemini/...
21
22
23
24
  #define FTWDT010_WDCOUNTER	0x0
  #define FTWDT010_WDLOAD		0x4
  #define FTWDT010_WDRESTART	0x8
  #define FTWDT010_WDCR		0xC
eca10ae60   Linus Walleij   watchdog: add dri...
25
26
27
28
  
  #define WDRESTART_MAGIC		0x5AB9
  
  #define WDCR_CLOCK_5MHZ		BIT(4)
d5433fd60   Linus Walleij   watchdog: ftwdt01...
29
30
  #define WDCR_WDEXT		BIT(3)
  #define WDCR_WDINTR		BIT(2)
eca10ae60   Linus Walleij   watchdog: add dri...
31
32
33
34
  #define WDCR_SYS_RST		BIT(1)
  #define WDCR_ENABLE		BIT(0)
  
  #define WDT_CLOCK		5000000		/* 5 MHz */
766a2aad6   Linus Walleij   watchdog: gemini/...
35
  struct ftwdt010_wdt {
eca10ae60   Linus Walleij   watchdog: add dri...
36
37
38
  	struct watchdog_device	wdd;
  	struct device		*dev;
  	void __iomem		*base;
d5433fd60   Linus Walleij   watchdog: ftwdt01...
39
  	bool			has_irq;
eca10ae60   Linus Walleij   watchdog: add dri...
40
41
42
  };
  
  static inline
766a2aad6   Linus Walleij   watchdog: gemini/...
43
  struct ftwdt010_wdt *to_ftwdt010_wdt(struct watchdog_device *wdd)
eca10ae60   Linus Walleij   watchdog: add dri...
44
  {
766a2aad6   Linus Walleij   watchdog: gemini/...
45
  	return container_of(wdd, struct ftwdt010_wdt, wdd);
eca10ae60   Linus Walleij   watchdog: add dri...
46
  }
766a2aad6   Linus Walleij   watchdog: gemini/...
47
  static int ftwdt010_wdt_start(struct watchdog_device *wdd)
eca10ae60   Linus Walleij   watchdog: add dri...
48
  {
766a2aad6   Linus Walleij   watchdog: gemini/...
49
  	struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
d5433fd60   Linus Walleij   watchdog: ftwdt01...
50
  	u32 enable;
eca10ae60   Linus Walleij   watchdog: add dri...
51

766a2aad6   Linus Walleij   watchdog: gemini/...
52
53
  	writel(wdd->timeout * WDT_CLOCK, gwdt->base + FTWDT010_WDLOAD);
  	writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART);
eca10ae60   Linus Walleij   watchdog: add dri...
54
  	/* set clock before enabling */
d5433fd60   Linus Walleij   watchdog: ftwdt01...
55
56
57
58
59
60
  	enable = WDCR_CLOCK_5MHZ | WDCR_SYS_RST;
  	writel(enable, gwdt->base + FTWDT010_WDCR);
  	if (gwdt->has_irq)
  		enable |= WDCR_WDINTR;
  	enable |= WDCR_ENABLE;
  	writel(enable, gwdt->base + FTWDT010_WDCR);
eca10ae60   Linus Walleij   watchdog: add dri...
61
62
63
  
  	return 0;
  }
766a2aad6   Linus Walleij   watchdog: gemini/...
64
  static int ftwdt010_wdt_stop(struct watchdog_device *wdd)
eca10ae60   Linus Walleij   watchdog: add dri...
65
  {
766a2aad6   Linus Walleij   watchdog: gemini/...
66
  	struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
eca10ae60   Linus Walleij   watchdog: add dri...
67

766a2aad6   Linus Walleij   watchdog: gemini/...
68
  	writel(0, gwdt->base + FTWDT010_WDCR);
eca10ae60   Linus Walleij   watchdog: add dri...
69
70
71
  
  	return 0;
  }
766a2aad6   Linus Walleij   watchdog: gemini/...
72
  static int ftwdt010_wdt_ping(struct watchdog_device *wdd)
eca10ae60   Linus Walleij   watchdog: add dri...
73
  {
766a2aad6   Linus Walleij   watchdog: gemini/...
74
  	struct ftwdt010_wdt *gwdt = to_ftwdt010_wdt(wdd);
eca10ae60   Linus Walleij   watchdog: add dri...
75

766a2aad6   Linus Walleij   watchdog: gemini/...
76
  	writel(WDRESTART_MAGIC, gwdt->base + FTWDT010_WDRESTART);
eca10ae60   Linus Walleij   watchdog: add dri...
77
78
79
  
  	return 0;
  }
766a2aad6   Linus Walleij   watchdog: gemini/...
80
  static int ftwdt010_wdt_set_timeout(struct watchdog_device *wdd,
eca10ae60   Linus Walleij   watchdog: add dri...
81
82
83
84
  				  unsigned int timeout)
  {
  	wdd->timeout = timeout;
  	if (watchdog_active(wdd))
766a2aad6   Linus Walleij   watchdog: gemini/...
85
  		ftwdt010_wdt_start(wdd);
eca10ae60   Linus Walleij   watchdog: add dri...
86
87
88
  
  	return 0;
  }
766a2aad6   Linus Walleij   watchdog: gemini/...
89
  static irqreturn_t ftwdt010_wdt_interrupt(int irq, void *data)
eca10ae60   Linus Walleij   watchdog: add dri...
90
  {
766a2aad6   Linus Walleij   watchdog: gemini/...
91
  	struct ftwdt010_wdt *gwdt = data;
eca10ae60   Linus Walleij   watchdog: add dri...
92
93
94
95
96
  
  	watchdog_notify_pretimeout(&gwdt->wdd);
  
  	return IRQ_HANDLED;
  }
766a2aad6   Linus Walleij   watchdog: gemini/...
97
98
99
100
101
  static const struct watchdog_ops ftwdt010_wdt_ops = {
  	.start		= ftwdt010_wdt_start,
  	.stop		= ftwdt010_wdt_stop,
  	.ping		= ftwdt010_wdt_ping,
  	.set_timeout	= ftwdt010_wdt_set_timeout,
eca10ae60   Linus Walleij   watchdog: add dri...
102
103
  	.owner		= THIS_MODULE,
  };
766a2aad6   Linus Walleij   watchdog: gemini/...
104
  static const struct watchdog_info ftwdt010_wdt_info = {
eca10ae60   Linus Walleij   watchdog: add dri...
105
106
107
108
109
  	.options	= WDIOF_KEEPALIVEPING
  			| WDIOF_MAGICCLOSE
  			| WDIOF_SETTIMEOUT,
  	.identity	= KBUILD_MODNAME,
  };
766a2aad6   Linus Walleij   watchdog: gemini/...
110
  static int ftwdt010_wdt_probe(struct platform_device *pdev)
eca10ae60   Linus Walleij   watchdog: add dri...
111
112
  {
  	struct device *dev = &pdev->dev;
766a2aad6   Linus Walleij   watchdog: gemini/...
113
  	struct ftwdt010_wdt *gwdt;
eca10ae60   Linus Walleij   watchdog: add dri...
114
115
116
117
118
119
120
  	unsigned int reg;
  	int irq;
  	int ret;
  
  	gwdt = devm_kzalloc(dev, sizeof(*gwdt), GFP_KERNEL);
  	if (!gwdt)
  		return -ENOMEM;
0f0a6a285   Guenter Roeck   watchdog: Convert...
121
  	gwdt->base = devm_platform_ioremap_resource(pdev, 0);
eca10ae60   Linus Walleij   watchdog: add dri...
122
123
  	if (IS_ERR(gwdt->base))
  		return PTR_ERR(gwdt->base);
eca10ae60   Linus Walleij   watchdog: add dri...
124
  	gwdt->dev = dev;
766a2aad6   Linus Walleij   watchdog: gemini/...
125
126
  	gwdt->wdd.info = &ftwdt010_wdt_info;
  	gwdt->wdd.ops = &ftwdt010_wdt_ops;
eca10ae60   Linus Walleij   watchdog: add dri...
127
128
129
130
131
132
133
134
135
136
  	gwdt->wdd.min_timeout = 1;
  	gwdt->wdd.max_timeout = 0xFFFFFFFF / WDT_CLOCK;
  	gwdt->wdd.parent = dev;
  
  	/*
  	 * If 'timeout-sec' unspecified in devicetree, assume a 13 second
  	 * default.
  	 */
  	gwdt->wdd.timeout = 13U;
  	watchdog_init_timeout(&gwdt->wdd, 0, dev);
766a2aad6   Linus Walleij   watchdog: gemini/...
137
  	reg = readw(gwdt->base + FTWDT010_WDCR);
eca10ae60   Linus Walleij   watchdog: add dri...
138
139
140
  	if (reg & WDCR_ENABLE) {
  		/* Watchdog was enabled by the bootloader, disable it. */
  		reg &= ~WDCR_ENABLE;
766a2aad6   Linus Walleij   watchdog: gemini/...
141
  		writel(reg, gwdt->base + FTWDT010_WDCR);
eca10ae60   Linus Walleij   watchdog: add dri...
142
  	}
d5433fd60   Linus Walleij   watchdog: ftwdt01...
143
144
145
146
147
148
149
150
  	irq = platform_get_irq(pdev, 0);
  	if (irq) {
  		ret = devm_request_irq(dev, irq, ftwdt010_wdt_interrupt, 0,
  				       "watchdog bark", gwdt);
  		if (ret)
  			return ret;
  		gwdt->has_irq = true;
  	}
eca10ae60   Linus Walleij   watchdog: add dri...
151
152
  
  	ret = devm_watchdog_register_device(dev, &gwdt->wdd);
2d065d2e9   Wolfram Sang   watchdog: ftwdt01...
153
  	if (ret)
eca10ae60   Linus Walleij   watchdog: add dri...
154
  		return ret;
eca10ae60   Linus Walleij   watchdog: add dri...
155
156
157
  
  	/* Set up platform driver data */
  	platform_set_drvdata(pdev, gwdt);
766a2aad6   Linus Walleij   watchdog: gemini/...
158
159
  	dev_info(dev, "FTWDT010 watchdog driver enabled
  ");
eca10ae60   Linus Walleij   watchdog: add dri...
160
161
162
  
  	return 0;
  }
766a2aad6   Linus Walleij   watchdog: gemini/...
163
  static int __maybe_unused ftwdt010_wdt_suspend(struct device *dev)
eca10ae60   Linus Walleij   watchdog: add dri...
164
  {
766a2aad6   Linus Walleij   watchdog: gemini/...
165
  	struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev);
eca10ae60   Linus Walleij   watchdog: add dri...
166
  	unsigned int reg;
766a2aad6   Linus Walleij   watchdog: gemini/...
167
  	reg = readw(gwdt->base + FTWDT010_WDCR);
eca10ae60   Linus Walleij   watchdog: add dri...
168
  	reg &= ~WDCR_ENABLE;
766a2aad6   Linus Walleij   watchdog: gemini/...
169
  	writel(reg, gwdt->base + FTWDT010_WDCR);
eca10ae60   Linus Walleij   watchdog: add dri...
170
171
172
  
  	return 0;
  }
766a2aad6   Linus Walleij   watchdog: gemini/...
173
  static int __maybe_unused ftwdt010_wdt_resume(struct device *dev)
eca10ae60   Linus Walleij   watchdog: add dri...
174
  {
766a2aad6   Linus Walleij   watchdog: gemini/...
175
  	struct ftwdt010_wdt *gwdt = dev_get_drvdata(dev);
eca10ae60   Linus Walleij   watchdog: add dri...
176
177
178
  	unsigned int reg;
  
  	if (watchdog_active(&gwdt->wdd)) {
766a2aad6   Linus Walleij   watchdog: gemini/...
179
  		reg = readw(gwdt->base + FTWDT010_WDCR);
eca10ae60   Linus Walleij   watchdog: add dri...
180
  		reg |= WDCR_ENABLE;
766a2aad6   Linus Walleij   watchdog: gemini/...
181
  		writel(reg, gwdt->base + FTWDT010_WDCR);
eca10ae60   Linus Walleij   watchdog: add dri...
182
183
184
185
  	}
  
  	return 0;
  }
766a2aad6   Linus Walleij   watchdog: gemini/...
186
187
188
  static const struct dev_pm_ops ftwdt010_wdt_dev_pm_ops = {
  	SET_SYSTEM_SLEEP_PM_OPS(ftwdt010_wdt_suspend,
  				ftwdt010_wdt_resume)
eca10ae60   Linus Walleij   watchdog: add dri...
189
190
191
  };
  
  #ifdef CONFIG_OF
766a2aad6   Linus Walleij   watchdog: gemini/...
192
193
  static const struct of_device_id ftwdt010_wdt_match[] = {
  	{ .compatible = "faraday,ftwdt010" },
eca10ae60   Linus Walleij   watchdog: add dri...
194
195
196
  	{ .compatible = "cortina,gemini-watchdog" },
  	{},
  };
766a2aad6   Linus Walleij   watchdog: gemini/...
197
  MODULE_DEVICE_TABLE(of, ftwdt010_wdt_match);
eca10ae60   Linus Walleij   watchdog: add dri...
198
  #endif
766a2aad6   Linus Walleij   watchdog: gemini/...
199
200
  static struct platform_driver ftwdt010_wdt_driver = {
  	.probe		= ftwdt010_wdt_probe,
eca10ae60   Linus Walleij   watchdog: add dri...
201
  	.driver		= {
766a2aad6   Linus Walleij   watchdog: gemini/...
202
203
204
  		.name	= "ftwdt010-wdt",
  		.of_match_table = of_match_ptr(ftwdt010_wdt_match),
  		.pm = &ftwdt010_wdt_dev_pm_ops,
eca10ae60   Linus Walleij   watchdog: add dri...
205
206
  	},
  };
766a2aad6   Linus Walleij   watchdog: gemini/...
207
  module_platform_driver(ftwdt010_wdt_driver);
eca10ae60   Linus Walleij   watchdog: add dri...
208
  MODULE_AUTHOR("Linus Walleij");
766a2aad6   Linus Walleij   watchdog: gemini/...
209
  MODULE_DESCRIPTION("Watchdog driver for Faraday Technology FTWDT010");
eca10ae60   Linus Walleij   watchdog: add dri...
210
  MODULE_LICENSE("GPL");