Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (c) 2004 Simtec Electronics
  4 *	Ben Dooks <ben@simtec.co.uk>
  5 *
  6 * S3C2410 Watchdog Timer Support
  7 *
  8 * Based on, softdog.c by Alan Cox,
  9 *     (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>
 
 
 
 
 
 
 
 
 
 
 10 */
 11
 12#include <linux/bits.h>
 13#include <linux/module.h>
 14#include <linux/moduleparam.h>
 15#include <linux/types.h>
 16#include <linux/timer.h>
 17#include <linux/watchdog.h>
 18#include <linux/platform_device.h>
 19#include <linux/interrupt.h>
 20#include <linux/clk.h>
 21#include <linux/uaccess.h>
 22#include <linux/io.h>
 23#include <linux/cpufreq.h>
 24#include <linux/slab.h>
 25#include <linux/err.h>
 26#include <linux/of.h>
 
 27#include <linux/mfd/syscon.h>
 28#include <linux/regmap.h>
 29#include <linux/delay.h>
 30
 31#define S3C2410_WTCON		0x00
 32#define S3C2410_WTDAT		0x04
 33#define S3C2410_WTCNT		0x08
 34#define S3C2410_WTCLRINT	0x0c
 35
 36#define S3C2410_WTCNT_MAXCNT	0xffff
 37
 38#define S3C2410_WTCON_RSTEN		BIT(0)
 39#define S3C2410_WTCON_INTEN		BIT(2)
 40#define S3C2410_WTCON_ENABLE		BIT(5)
 41#define S3C2410_WTCON_DBGACK_MASK	BIT(16)
 42
 43#define S3C2410_WTCON_DIV16	(0 << 3)
 44#define S3C2410_WTCON_DIV32	(1 << 3)
 45#define S3C2410_WTCON_DIV64	(2 << 3)
 46#define S3C2410_WTCON_DIV128	(3 << 3)
 47
 48#define S3C2410_WTCON_MAXDIV	0x80
 49
 50#define S3C2410_WTCON_PRESCALE(x)	((x) << 8)
 51#define S3C2410_WTCON_PRESCALE_MASK	(0xff << 8)
 52#define S3C2410_WTCON_PRESCALE_MAX	0xff
 53
 54#define S3C2410_WATCHDOG_ATBOOT		(0)
 55#define S3C2410_WATCHDOG_DEFAULT_TIME	(15)
 56
 57#define EXYNOS5_RST_STAT_REG_OFFSET		0x0404
 58#define EXYNOS5_WDT_DISABLE_REG_OFFSET		0x0408
 59#define EXYNOS5_WDT_MASK_RESET_REG_OFFSET	0x040c
 60#define EXYNOS850_CLUSTER0_NONCPU_OUT		0x1220
 61#define EXYNOS850_CLUSTER0_NONCPU_INT_EN	0x1244
 62#define EXYNOS850_CLUSTER1_NONCPU_OUT		0x1620
 63#define EXYNOS850_CLUSTER1_NONCPU_INT_EN	0x1644
 64#define EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT	0x1520
 65#define EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN	0x1544
 66#define EXYNOSAUTOV920_CLUSTER0_NONCPU_OUT	0x1420
 67#define EXYNOSAUTOV920_CLUSTER0_NONCPU_INT_EN	0x1444
 68#define EXYNOSAUTOV920_CLUSTER1_NONCPU_OUT	0x1720
 69#define EXYNOSAUTOV920_CLUSTER1_NONCPU_INT_EN	0x1744
 70
 71#define EXYNOS850_CLUSTER0_WDTRESET_BIT		24
 72#define EXYNOS850_CLUSTER1_WDTRESET_BIT		23
 73#define EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT	25
 74#define EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT	24
 75
 76#define GS_CLUSTER0_NONCPU_OUT			0x1220
 77#define GS_CLUSTER1_NONCPU_OUT			0x1420
 78#define GS_CLUSTER0_NONCPU_INT_EN		0x1244
 79#define GS_CLUSTER1_NONCPU_INT_EN		0x1444
 80#define GS_CLUSTER2_NONCPU_INT_EN		0x1644
 81#define GS_RST_STAT_REG_OFFSET			0x3B44
 82
 83/**
 84 * DOC: Quirk flags for different Samsung watchdog IP-cores
 85 *
 86 * This driver supports multiple Samsung SoCs, each of which might have
 87 * different set of registers and features supported. As watchdog block
 88 * sometimes requires modifying PMU registers for proper functioning, register
 89 * differences in both watchdog and PMU IP-cores should be accounted for. Quirk
 90 * flags described below serve the purpose of telling the driver about mentioned
 91 * SoC traits, and can be specified in driver data for each particular supported
 92 * device.
 93 *
 94 * %QUIRK_HAS_WTCLRINT_REG: Watchdog block has WTCLRINT register. It's used to
 95 * clear the interrupt once the interrupt service routine is complete. It's
 96 * write-only, writing any values to this register clears the interrupt, but
 97 * reading is not permitted.
 98 *
 99 * %QUIRK_HAS_PMU_MASK_RESET: PMU block has the register for disabling/enabling
100 * WDT reset request. On old SoCs it's usually called MASK_WDT_RESET_REQUEST,
101 * new SoCs have CLUSTERx_NONCPU_INT_EN register, which 'mask_bit' value is
102 * inverted compared to the former one.
103 *
104 * %QUIRK_HAS_PMU_RST_STAT: PMU block has RST_STAT (reset status) register,
105 * which contains bits indicating the reason for most recent CPU reset. If
106 * present, driver will use this register to check if previous reboot was due to
107 * watchdog timer reset.
108 *
109 * %QUIRK_HAS_PMU_AUTO_DISABLE: PMU block has AUTOMATIC_WDT_RESET_DISABLE
110 * register. If 'mask_bit' bit is set, PMU will disable WDT reset when
111 * corresponding processor is in reset state.
112 *
113 * %QUIRK_HAS_PMU_CNT_EN: PMU block has some register (e.g. CLUSTERx_NONCPU_OUT)
114 * with "watchdog counter enable" bit. That bit should be set to make watchdog
115 * counter running.
116 *
117 * %QUIRK_HAS_DBGACK_BIT: WTCON register has DBGACK_MASK bit. Setting the
118 * DBGACK_MASK bit disables the watchdog outputs when the SoC is in debug mode.
119 * Debug mode is determined by the DBGACK CPU signal.
120 */
121#define QUIRK_HAS_WTCLRINT_REG			BIT(0)
122#define QUIRK_HAS_PMU_MASK_RESET		BIT(1)
123#define QUIRK_HAS_PMU_RST_STAT			BIT(2)
124#define QUIRK_HAS_PMU_AUTO_DISABLE		BIT(3)
125#define QUIRK_HAS_PMU_CNT_EN			BIT(4)
126#define QUIRK_HAS_DBGACK_BIT			BIT(5)
127
128/* These quirks require that we have a PMU register map */
129#define QUIRKS_HAVE_PMUREG \
130	(QUIRK_HAS_PMU_MASK_RESET | QUIRK_HAS_PMU_RST_STAT | \
131	 QUIRK_HAS_PMU_AUTO_DISABLE | QUIRK_HAS_PMU_CNT_EN)
132
133static bool nowayout	= WATCHDOG_NOWAYOUT;
134static int tmr_margin;
135static int tmr_atboot	= S3C2410_WATCHDOG_ATBOOT;
136static int soft_noboot;
137
138module_param(tmr_margin,  int, 0);
139module_param(tmr_atboot,  int, 0);
140module_param(nowayout,   bool, 0);
141module_param(soft_noboot, int, 0);
142
143MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default="
144		__MODULE_STRING(S3C2410_WATCHDOG_DEFAULT_TIME) ")");
145MODULE_PARM_DESC(tmr_atboot,
146		"Watchdog is started at boot time if set to 1, default="
147			__MODULE_STRING(S3C2410_WATCHDOG_ATBOOT));
148MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
149			__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
150MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default 0)");
151
152/**
153 * struct s3c2410_wdt_variant - Per-variant config data
154 *
155 * @disable_reg: Offset in pmureg for the register that disables the watchdog
156 * timer reset functionality.
157 * @mask_reset_reg: Offset in pmureg for the register that masks the watchdog
158 * timer reset functionality.
159 * @mask_reset_inv: If set, mask_reset_reg value will have inverted meaning.
160 * @mask_bit: Bit number for the watchdog timer in the disable register and the
161 * mask reset register.
162 * @rst_stat_reg: Offset in pmureg for the register that has the reset status.
163 * @rst_stat_bit: Bit number in the rst_stat register indicating a watchdog
164 * reset.
165 * @cnt_en_reg: Offset in pmureg for the register that enables WDT counter.
166 * @cnt_en_bit: Bit number for "watchdog counter enable" in cnt_en register.
167 * @quirks: A bitfield of quirks.
168 */
169
170struct s3c2410_wdt_variant {
171	int disable_reg;
172	int mask_reset_reg;
173	bool mask_reset_inv;
174	int mask_bit;
175	int rst_stat_reg;
176	int rst_stat_bit;
177	int cnt_en_reg;
178	int cnt_en_bit;
179	u32 quirks;
180};
181
182struct s3c2410_wdt {
183	struct device		*dev;
184	struct clk		*bus_clk; /* for register interface (PCLK) */
185	struct clk		*src_clk; /* for WDT counter */
186	void __iomem		*reg_base;
187	unsigned int		count;
188	spinlock_t		lock;
189	unsigned long		wtcon_save;
190	unsigned long		wtdat_save;
191	struct watchdog_device	wdt_device;
192	struct notifier_block	freq_transition;
193	const struct s3c2410_wdt_variant *drv_data;
194	struct regmap *pmureg;
195};
196
197static const struct s3c2410_wdt_variant drv_data_s3c2410 = {
198	.quirks = 0
199};
200
201#ifdef CONFIG_OF
202static const struct s3c2410_wdt_variant drv_data_s3c6410 = {
203	.quirks = QUIRK_HAS_WTCLRINT_REG,
204};
205
206static const struct s3c2410_wdt_variant drv_data_exynos5250  = {
207	.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
208	.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
209	.mask_bit = 20,
210	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
211	.rst_stat_bit = 20,
212	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
213		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
214};
215
216static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
217	.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
218	.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
219	.mask_bit = 0,
220	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
221	.rst_stat_bit = 9,
222	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
223		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
224};
225
226static const struct s3c2410_wdt_variant drv_data_exynos7 = {
227	.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
228	.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
229	.mask_bit = 23,
230	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
231	.rst_stat_bit = 23,	/* A57 WDTRESET */
232	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
233		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_AUTO_DISABLE,
234};
235
236static const struct s3c2410_wdt_variant drv_data_exynos850_cl0 = {
237	.mask_reset_reg = EXYNOS850_CLUSTER0_NONCPU_INT_EN,
238	.mask_bit = 2,
239	.mask_reset_inv = true,
240	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
241	.rst_stat_bit = EXYNOS850_CLUSTER0_WDTRESET_BIT,
242	.cnt_en_reg = EXYNOS850_CLUSTER0_NONCPU_OUT,
243	.cnt_en_bit = 7,
244	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
245		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
246};
247
248static const struct s3c2410_wdt_variant drv_data_exynos850_cl1 = {
249	.mask_reset_reg = EXYNOS850_CLUSTER1_NONCPU_INT_EN,
250	.mask_bit = 2,
251	.mask_reset_inv = true,
252	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
253	.rst_stat_bit = EXYNOS850_CLUSTER1_WDTRESET_BIT,
254	.cnt_en_reg = EXYNOS850_CLUSTER1_NONCPU_OUT,
255	.cnt_en_bit = 7,
256	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | \
257		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
258};
259
260static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl0 = {
261	.mask_reset_reg = EXYNOS850_CLUSTER0_NONCPU_INT_EN,
262	.mask_bit = 2,
263	.mask_reset_inv = true,
264	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
265	.rst_stat_bit = EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT,
266	.cnt_en_reg = EXYNOS850_CLUSTER0_NONCPU_OUT,
267	.cnt_en_bit = 7,
268	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
269		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
270};
271
272static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl1 = {
273	.mask_reset_reg = EXYNOSAUTOV9_CLUSTER1_NONCPU_INT_EN,
274	.mask_bit = 2,
275	.mask_reset_inv = true,
276	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
277	.rst_stat_bit = EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT,
278	.cnt_en_reg = EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT,
279	.cnt_en_bit = 7,
280	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
281		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN,
282};
283
284static const struct s3c2410_wdt_variant drv_data_gs101_cl0 = {
285	.mask_reset_reg = GS_CLUSTER0_NONCPU_INT_EN,
286	.mask_bit = 2,
287	.mask_reset_inv = true,
288	.rst_stat_reg = GS_RST_STAT_REG_OFFSET,
289	.rst_stat_bit = 0,
290	.cnt_en_reg = GS_CLUSTER0_NONCPU_OUT,
291	.cnt_en_bit = 8,
292	.quirks = QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_MASK_RESET |
293		  QUIRK_HAS_PMU_CNT_EN | QUIRK_HAS_WTCLRINT_REG |
294		  QUIRK_HAS_DBGACK_BIT,
295};
296
297static const struct s3c2410_wdt_variant drv_data_gs101_cl1 = {
298	.mask_reset_reg = GS_CLUSTER1_NONCPU_INT_EN,
299	.mask_bit = 2,
300	.mask_reset_inv = true,
301	.rst_stat_reg = GS_RST_STAT_REG_OFFSET,
302	.rst_stat_bit = 1,
303	.cnt_en_reg = GS_CLUSTER1_NONCPU_OUT,
304	.cnt_en_bit = 7,
305	.quirks = QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_MASK_RESET |
306		  QUIRK_HAS_PMU_CNT_EN | QUIRK_HAS_WTCLRINT_REG |
307		  QUIRK_HAS_DBGACK_BIT,
308};
309
310static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl0 = {
311	.mask_reset_reg = EXYNOSAUTOV920_CLUSTER0_NONCPU_INT_EN,
312	.mask_bit = 2,
313	.mask_reset_inv = true,
314	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
315	.rst_stat_bit = EXYNOSAUTOV9_CLUSTER0_WDTRESET_BIT,
316	.cnt_en_reg = EXYNOSAUTOV920_CLUSTER0_NONCPU_OUT,
317	.cnt_en_bit = 7,
318	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
319		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN |
320		  QUIRK_HAS_DBGACK_BIT,
321};
322
323static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl1 = {
324	.mask_reset_reg = EXYNOSAUTOV920_CLUSTER1_NONCPU_INT_EN,
325	.mask_bit = 2,
326	.mask_reset_inv = true,
327	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
328	.rst_stat_bit = EXYNOSAUTOV9_CLUSTER1_WDTRESET_BIT,
329	.cnt_en_reg = EXYNOSAUTOV920_CLUSTER1_NONCPU_OUT,
330	.cnt_en_bit = 7,
331	.quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET |
332		  QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN |
333		  QUIRK_HAS_DBGACK_BIT,
334};
335
336static const struct of_device_id s3c2410_wdt_match[] = {
337	{ .compatible = "google,gs101-wdt",
338	  .data = &drv_data_gs101_cl0 },
339	{ .compatible = "samsung,s3c2410-wdt",
340	  .data = &drv_data_s3c2410 },
341	{ .compatible = "samsung,s3c6410-wdt",
342	  .data = &drv_data_s3c6410 },
343	{ .compatible = "samsung,exynos5250-wdt",
344	  .data = &drv_data_exynos5250 },
345	{ .compatible = "samsung,exynos5420-wdt",
346	  .data = &drv_data_exynos5420 },
347	{ .compatible = "samsung,exynos7-wdt",
348	  .data = &drv_data_exynos7 },
349	{ .compatible = "samsung,exynos850-wdt",
350	  .data = &drv_data_exynos850_cl0 },
351	{ .compatible = "samsung,exynosautov9-wdt",
352	  .data = &drv_data_exynosautov9_cl0 },
353	{ .compatible = "samsung,exynosautov920-wdt",
354	  .data = &drv_data_exynosautov920_cl0 },
355	{},
356};
357MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
358#endif
359
360static const struct platform_device_id s3c2410_wdt_ids[] = {
361	{
362		.name = "s3c2410-wdt",
363		.driver_data = (unsigned long)&drv_data_s3c2410,
364	},
365	{}
366};
367MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids);
368
369/* functions */
370
371static inline unsigned long s3c2410wdt_get_freq(struct s3c2410_wdt *wdt)
372{
373	return clk_get_rate(wdt->src_clk ? wdt->src_clk : wdt->bus_clk);
374}
375
376static inline unsigned int s3c2410wdt_max_timeout(struct s3c2410_wdt *wdt)
377{
378	const unsigned long freq = s3c2410wdt_get_freq(wdt);
379
380	return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1)
381				       / S3C2410_WTCON_MAXDIV);
382}
383
384static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
385{
386	const u32 mask_val = BIT(wdt->drv_data->mask_bit);
387	const u32 val = mask ? mask_val : 0;
388	int ret;
389
390	ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->disable_reg,
391				 mask_val, val);
392	if (ret < 0)
393		dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
394
395	return ret;
396}
397
398static int s3c2410wdt_mask_wdt_reset(struct s3c2410_wdt *wdt, bool mask)
399{
400	const u32 mask_val = BIT(wdt->drv_data->mask_bit);
401	const bool val_inv = wdt->drv_data->mask_reset_inv;
402	const u32 val = (mask ^ val_inv) ? mask_val : 0;
403	int ret;
 
 
404
405	ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->mask_reset_reg,
406				 mask_val, val);
407	if (ret < 0)
408		dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
409
410	return ret;
411}
412
413static int s3c2410wdt_enable_counter(struct s3c2410_wdt *wdt, bool en)
414{
415	const u32 mask_val = BIT(wdt->drv_data->cnt_en_bit);
416	const u32 val = en ? mask_val : 0;
417	int ret;
418
419	ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->cnt_en_reg,
420				 mask_val, val);
 
 
421	if (ret < 0)
422		dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
423
424	return ret;
425}
426
427static int s3c2410wdt_enable(struct s3c2410_wdt *wdt, bool en)
428{
429	int ret;
430
431	if (wdt->drv_data->quirks & QUIRK_HAS_PMU_AUTO_DISABLE) {
432		ret = s3c2410wdt_disable_wdt_reset(wdt, !en);
433		if (ret < 0)
434			return ret;
435	}
436
437	if (wdt->drv_data->quirks & QUIRK_HAS_PMU_MASK_RESET) {
438		ret = s3c2410wdt_mask_wdt_reset(wdt, !en);
439		if (ret < 0)
440			return ret;
441	}
442
443	if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CNT_EN) {
444		ret = s3c2410wdt_enable_counter(wdt, en);
445		if (ret < 0)
446			return ret;
447	}
448
449	return 0;
450}
451
452/* Disable watchdog outputs if CPU is in debug mode */
453static void s3c2410wdt_mask_dbgack(struct s3c2410_wdt *wdt)
454{
455	unsigned long wtcon;
456
457	if (!(wdt->drv_data->quirks & QUIRK_HAS_DBGACK_BIT))
458		return;
459
460	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
461	wtcon |= S3C2410_WTCON_DBGACK_MASK;
462	writel(wtcon, wdt->reg_base + S3C2410_WTCON);
463}
464
465static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
466{
467	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
468	unsigned long flags;
469
470	spin_lock_irqsave(&wdt->lock, flags);
471	writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
472	spin_unlock_irqrestore(&wdt->lock, flags);
473
474	return 0;
475}
476
477static void __s3c2410wdt_stop(struct s3c2410_wdt *wdt)
478{
479	unsigned long wtcon;
480
481	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
482	wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);
483	writel(wtcon, wdt->reg_base + S3C2410_WTCON);
484}
485
486static int s3c2410wdt_stop(struct watchdog_device *wdd)
487{
488	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
489	unsigned long flags;
490
491	spin_lock_irqsave(&wdt->lock, flags);
492	__s3c2410wdt_stop(wdt);
493	spin_unlock_irqrestore(&wdt->lock, flags);
494
495	return 0;
496}
497
498static int s3c2410wdt_start(struct watchdog_device *wdd)
499{
500	unsigned long wtcon;
501	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
502	unsigned long flags;
503
504	spin_lock_irqsave(&wdt->lock, flags);
505
506	__s3c2410wdt_stop(wdt);
507
508	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
509	wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;
510
511	if (soft_noboot) {
512		wtcon |= S3C2410_WTCON_INTEN;
513		wtcon &= ~S3C2410_WTCON_RSTEN;
514	} else {
515		wtcon &= ~S3C2410_WTCON_INTEN;
516		wtcon |= S3C2410_WTCON_RSTEN;
517	}
518
519	dev_dbg(wdt->dev, "Starting watchdog: count=0x%08x, wtcon=%08lx\n",
520		wdt->count, wtcon);
521
522	writel(wdt->count, wdt->reg_base + S3C2410_WTDAT);
523	writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
524	writel(wtcon, wdt->reg_base + S3C2410_WTCON);
525	spin_unlock_irqrestore(&wdt->lock, flags);
526
527	return 0;
528}
529
 
 
 
 
 
530static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd,
531				    unsigned int timeout)
532{
533	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
534	unsigned long freq = s3c2410wdt_get_freq(wdt);
535	unsigned int count;
536	unsigned int divisor = 1;
537	unsigned long wtcon;
538
539	if (timeout < 1)
540		return -EINVAL;
541
542	freq = DIV_ROUND_UP(freq, 128);
543	count = timeout * freq;
544
545	dev_dbg(wdt->dev, "Heartbeat: count=%d, timeout=%d, freq=%lu\n",
546		count, timeout, freq);
547
548	/* if the count is bigger than the watchdog register,
549	   then work out what we need to do (and if) we can
550	   actually make this value
551	*/
552
553	if (count >= 0x10000) {
554		divisor = DIV_ROUND_UP(count, 0xffff);
555
556		if (divisor > 0x100) {
557			dev_err(wdt->dev, "timeout %d too big\n", timeout);
558			return -EINVAL;
559		}
560	}
561
562	dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%d (%08x)\n",
563		timeout, divisor, count, DIV_ROUND_UP(count, divisor));
564
565	count = DIV_ROUND_UP(count, divisor);
566	wdt->count = count;
567
568	/* update the pre-scaler */
569	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
570	wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;
571	wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);
572
573	writel(count, wdt->reg_base + S3C2410_WTDAT);
574	writel(wtcon, wdt->reg_base + S3C2410_WTCON);
575
576	wdd->timeout = (count * divisor) / freq;
577
578	return 0;
579}
580
581static int s3c2410wdt_restart(struct watchdog_device *wdd, unsigned long action,
582			      void *data)
583{
584	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
585	void __iomem *wdt_base = wdt->reg_base;
586
587	/* disable watchdog, to be safe  */
588	writel(0, wdt_base + S3C2410_WTCON);
589
590	/* put initial values into count and data */
591	writel(0x80, wdt_base + S3C2410_WTCNT);
592	writel(0x80, wdt_base + S3C2410_WTDAT);
593
594	/* set the watchdog to go and reset... */
595	writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV16 |
596		S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x20),
597		wdt_base + S3C2410_WTCON);
598
599	/* wait for reset to assert... */
600	mdelay(500);
601
602	return 0;
603}
604
605#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
606
607static const struct watchdog_info s3c2410_wdt_ident = {
608	.options          =     OPTIONS,
609	.firmware_version =	0,
610	.identity         =	"S3C2410 Watchdog",
611};
612
613static const struct watchdog_ops s3c2410wdt_ops = {
614	.owner = THIS_MODULE,
615	.start = s3c2410wdt_start,
616	.stop = s3c2410wdt_stop,
617	.ping = s3c2410wdt_keepalive,
618	.set_timeout = s3c2410wdt_set_heartbeat,
619	.restart = s3c2410wdt_restart,
620};
621
622static const struct watchdog_device s3c2410_wdd = {
623	.info = &s3c2410_wdt_ident,
624	.ops = &s3c2410wdt_ops,
625	.timeout = S3C2410_WATCHDOG_DEFAULT_TIME,
626};
627
628/* interrupt handler code */
629
630static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
631{
632	struct s3c2410_wdt *wdt = platform_get_drvdata(param);
633
634	dev_info(wdt->dev, "watchdog timer expired (irq)\n");
635
636	s3c2410wdt_keepalive(&wdt->wdt_device);
637
638	if (wdt->drv_data->quirks & QUIRK_HAS_WTCLRINT_REG)
639		writel(0x1, wdt->reg_base + S3C2410_WTCLRINT);
640
641	return IRQ_HANDLED;
642}
643
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
644static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt)
645{
646	unsigned int rst_stat;
647	int ret;
648
649	if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_RST_STAT))
650		return 0;
651
652	ret = regmap_read(wdt->pmureg, wdt->drv_data->rst_stat_reg, &rst_stat);
653	if (ret)
654		dev_warn(wdt->dev, "Couldn't get RST_STAT register\n");
655	else if (rst_stat & BIT(wdt->drv_data->rst_stat_bit))
656		return WDIOF_CARDRESET;
657
658	return 0;
659}
660
661static inline int
662s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt)
663{
664	const struct s3c2410_wdt_variant *variant;
665	struct device *dev = &pdev->dev;
666
667	variant = of_device_get_match_data(dev);
668	if (!variant) {
669		/* Device matched by platform_device_id */
670		variant = (struct s3c2410_wdt_variant *)
671			   platform_get_device_id(pdev)->driver_data;
672	}
673
674#ifdef CONFIG_OF
675	/* Choose Exynos850/ExynosAutov9 driver data w.r.t. cluster index */
676	if (variant == &drv_data_exynos850_cl0 ||
677	    variant == &drv_data_exynosautov9_cl0 ||
678	    variant == &drv_data_gs101_cl0 ||
679	    variant == &drv_data_exynosautov920_cl0) {
680		u32 index;
681		int err;
682
683		err = of_property_read_u32(dev->of_node,
684					   "samsung,cluster-index", &index);
685		if (err)
686			return dev_err_probe(dev, -EINVAL, "failed to get cluster index\n");
687
688		switch (index) {
689		case 0:
690			break;
691		case 1:
692			if (variant == &drv_data_exynos850_cl0)
693				variant = &drv_data_exynos850_cl1;
694			else if (variant == &drv_data_exynosautov9_cl0)
695				variant = &drv_data_exynosautov9_cl1;
696			else if (variant == &drv_data_gs101_cl0)
697				variant = &drv_data_gs101_cl1;
698			else if (variant == &drv_data_exynosautov920_cl0)
699				variant = &drv_data_exynosautov920_cl1;
700			break;
701		default:
702			return dev_err_probe(dev, -EINVAL, "wrong cluster index: %u\n", index);
703		}
704	}
705#endif
706
707	wdt->drv_data = variant;
708	return 0;
709}
710
711static void s3c2410wdt_wdt_disable_action(void *data)
712{
713	s3c2410wdt_enable(data, false);
714}
715
716static int s3c2410wdt_probe(struct platform_device *pdev)
717{
718	struct device *dev = &pdev->dev;
719	struct s3c2410_wdt *wdt;
 
 
720	unsigned int wtcon;
721	int wdt_irq;
722	int ret;
723
724	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
725	if (!wdt)
726		return -ENOMEM;
727
728	wdt->dev = dev;
729	spin_lock_init(&wdt->lock);
730	wdt->wdt_device = s3c2410_wdd;
731
732	ret = s3c2410_get_wdt_drv_data(pdev, wdt);
733	if (ret)
734		return ret;
735
736	if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
737		wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
738						"samsung,syscon-phandle");
739		if (IS_ERR(wdt->pmureg))
740			return dev_err_probe(dev, PTR_ERR(wdt->pmureg),
741					     "syscon regmap lookup failed.\n");
 
742	}
743
744	wdt_irq = platform_get_irq(pdev, 0);
745	if (wdt_irq < 0)
746		return wdt_irq;
 
 
 
747
748	/* get the memory region for the watchdog timer */
749	wdt->reg_base = devm_platform_ioremap_resource(pdev, 0);
750	if (IS_ERR(wdt->reg_base))
751		return PTR_ERR(wdt->reg_base);
752
753	wdt->bus_clk = devm_clk_get_enabled(dev, "watchdog");
754	if (IS_ERR(wdt->bus_clk))
755		return dev_err_probe(dev, PTR_ERR(wdt->bus_clk), "failed to get bus clock\n");
756
757	/*
758	 * "watchdog_src" clock is optional; if it's not present -- just skip it
759	 * and use "watchdog" clock as both bus and source clock.
760	 */
761	wdt->src_clk = devm_clk_get_optional_enabled(dev, "watchdog_src");
762	if (IS_ERR(wdt->src_clk))
763		return dev_err_probe(dev, PTR_ERR(wdt->src_clk), "failed to get source clock\n");
 
 
 
 
764
765	wdt->wdt_device.min_timeout = 1;
766	wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt);
 
 
 
 
 
 
767
768	watchdog_set_drvdata(&wdt->wdt_device, wdt);
769
770	/* see if we can actually set the requested timer margin, and if
771	 * not, try the default value */
772
773	watchdog_init_timeout(&wdt->wdt_device, tmr_margin, dev);
774	ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
775					wdt->wdt_device.timeout);
776	if (ret) {
777		ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
778					       S3C2410_WATCHDOG_DEFAULT_TIME);
779		if (ret == 0)
780			dev_warn(dev, "tmr_margin value out of range, default %d used\n",
 
 
781				 S3C2410_WATCHDOG_DEFAULT_TIME);
782		else
783			return dev_err_probe(dev, ret, "failed to use default timeout\n");
784	}
785
786	ret = devm_request_irq(dev, wdt_irq, s3c2410wdt_irq, 0,
787			       pdev->name, pdev);
788	if (ret != 0)
789		return dev_err_probe(dev, ret, "failed to install irq (%d)\n", ret);
 
 
790
791	watchdog_set_nowayout(&wdt->wdt_device, nowayout);
792	watchdog_set_restart_priority(&wdt->wdt_device, 128);
793
794	wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
795	wdt->wdt_device.parent = dev;
796
797	s3c2410wdt_mask_dbgack(wdt);
 
 
 
 
 
 
 
 
798
799	/*
800	 * If "tmr_atboot" param is non-zero, start the watchdog right now. Also
801	 * set WDOG_HW_RUNNING bit, so that watchdog core can kick the watchdog.
802	 *
803	 * If we're not enabling the watchdog, then ensure it is disabled if it
804	 * has been left running from the bootloader or other source.
805	 */
806	if (tmr_atboot) {
807		dev_info(dev, "starting watchdog timer\n");
808		s3c2410wdt_start(&wdt->wdt_device);
809		set_bit(WDOG_HW_RUNNING, &wdt->wdt_device.status);
810	} else {
 
 
 
811		s3c2410wdt_stop(&wdt->wdt_device);
812	}
813
814	ret = devm_watchdog_register_device(dev, &wdt->wdt_device);
815	if (ret)
816		return ret;
817
818	ret = s3c2410wdt_enable(wdt, true);
819	if (ret < 0)
820		return ret;
821
822	ret = devm_add_action_or_reset(dev, s3c2410wdt_wdt_disable_action, wdt);
823	if (ret)
824		return ret;
825
826	platform_set_drvdata(pdev, wdt);
827
828	/* print out a statement of readiness */
829
830	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
831
832	dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
833		 (wtcon & S3C2410_WTCON_ENABLE) ?  "" : "in",
834		 (wtcon & S3C2410_WTCON_RSTEN) ? "en" : "dis",
835		 (wtcon & S3C2410_WTCON_INTEN) ? "en" : "dis");
836
837	return 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
838}
839
840static void s3c2410wdt_shutdown(struct platform_device *dev)
841{
842	struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
843
844	s3c2410wdt_enable(wdt, false);
 
845	s3c2410wdt_stop(&wdt->wdt_device);
846}
847
 
 
848static int s3c2410wdt_suspend(struct device *dev)
849{
850	int ret;
851	struct s3c2410_wdt *wdt = dev_get_drvdata(dev);
852
853	/* Save watchdog state, and turn it off. */
854	wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON);
855	wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT);
856
857	ret = s3c2410wdt_enable(wdt, false);
858	if (ret < 0)
859		return ret;
860
861	/* Note that WTCNT doesn't need to be saved. */
862	s3c2410wdt_stop(&wdt->wdt_device);
863
864	return 0;
865}
866
867static int s3c2410wdt_resume(struct device *dev)
868{
869	int ret;
870	struct s3c2410_wdt *wdt = dev_get_drvdata(dev);
871
872	/* Restore watchdog state. */
873	writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTDAT);
874	writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);/* Reset count */
875	writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON);
876
877	ret = s3c2410wdt_enable(wdt, true);
878	if (ret < 0)
879		return ret;
880
881	dev_info(dev, "watchdog %sabled\n",
882		(wdt->wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
883
884	return 0;
885}
 
886
887static DEFINE_SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops,
888				s3c2410wdt_suspend, s3c2410wdt_resume);
889
890static struct platform_driver s3c2410wdt_driver = {
891	.probe		= s3c2410wdt_probe,
 
892	.shutdown	= s3c2410wdt_shutdown,
893	.id_table	= s3c2410_wdt_ids,
894	.driver		= {
895		.name	= "s3c2410-wdt",
896		.pm	= pm_sleep_ptr(&s3c2410wdt_pm_ops),
897		.of_match_table	= of_match_ptr(s3c2410_wdt_match),
898	},
899};
900
901module_platform_driver(s3c2410wdt_driver);
902
903MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Dimitry Andric <dimitry.andric@tomtom.com>");
904MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
905MODULE_LICENSE("GPL");
v4.17
 
  1/*
  2 * Copyright (c) 2004 Simtec Electronics
  3 *	Ben Dooks <ben@simtec.co.uk>
  4 *
  5 * S3C2410 Watchdog Timer Support
  6 *
  7 * Based on, softdog.c by Alan Cox,
  8 *     (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>
  9 *
 10 * This program is free software; you can redistribute it and/or modify
 11 * it under the terms of the GNU General Public License as published by
 12 * the Free Software Foundation; either version 2 of the License, or
 13 * (at your option) any later version.
 14 *
 15 * This program is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 18 * GNU General Public License for more details.
 19 */
 20
 
 21#include <linux/module.h>
 22#include <linux/moduleparam.h>
 23#include <linux/types.h>
 24#include <linux/timer.h>
 25#include <linux/watchdog.h>
 26#include <linux/platform_device.h>
 27#include <linux/interrupt.h>
 28#include <linux/clk.h>
 29#include <linux/uaccess.h>
 30#include <linux/io.h>
 31#include <linux/cpufreq.h>
 32#include <linux/slab.h>
 33#include <linux/err.h>
 34#include <linux/of.h>
 35#include <linux/of_device.h>
 36#include <linux/mfd/syscon.h>
 37#include <linux/regmap.h>
 38#include <linux/delay.h>
 39
 40#define S3C2410_WTCON		0x00
 41#define S3C2410_WTDAT		0x04
 42#define S3C2410_WTCNT		0x08
 43#define S3C2410_WTCLRINT	0x0c
 44
 45#define S3C2410_WTCNT_MAXCNT	0xffff
 46
 47#define S3C2410_WTCON_RSTEN	(1 << 0)
 48#define S3C2410_WTCON_INTEN	(1 << 2)
 49#define S3C2410_WTCON_ENABLE	(1 << 5)
 
 50
 51#define S3C2410_WTCON_DIV16	(0 << 3)
 52#define S3C2410_WTCON_DIV32	(1 << 3)
 53#define S3C2410_WTCON_DIV64	(2 << 3)
 54#define S3C2410_WTCON_DIV128	(3 << 3)
 55
 56#define S3C2410_WTCON_MAXDIV	0x80
 57
 58#define S3C2410_WTCON_PRESCALE(x)	((x) << 8)
 59#define S3C2410_WTCON_PRESCALE_MASK	(0xff << 8)
 60#define S3C2410_WTCON_PRESCALE_MAX	0xff
 61
 62#define S3C2410_WATCHDOG_ATBOOT		(0)
 63#define S3C2410_WATCHDOG_DEFAULT_TIME	(15)
 64
 65#define EXYNOS5_RST_STAT_REG_OFFSET		0x0404
 66#define EXYNOS5_WDT_DISABLE_REG_OFFSET		0x0408
 67#define EXYNOS5_WDT_MASK_RESET_REG_OFFSET	0x040c
 68#define QUIRK_HAS_PMU_CONFIG			(1 << 0)
 69#define QUIRK_HAS_RST_STAT			(1 << 1)
 70#define QUIRK_HAS_WTCLRINT_REG			(1 << 2)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 71
 72/* These quirks require that we have a PMU register map */
 73#define QUIRKS_HAVE_PMUREG			(QUIRK_HAS_PMU_CONFIG | \
 74						 QUIRK_HAS_RST_STAT)
 
 75
 76static bool nowayout	= WATCHDOG_NOWAYOUT;
 77static int tmr_margin;
 78static int tmr_atboot	= S3C2410_WATCHDOG_ATBOOT;
 79static int soft_noboot;
 80
 81module_param(tmr_margin,  int, 0);
 82module_param(tmr_atboot,  int, 0);
 83module_param(nowayout,   bool, 0);
 84module_param(soft_noboot, int, 0);
 85
 86MODULE_PARM_DESC(tmr_margin, "Watchdog tmr_margin in seconds. (default="
 87		__MODULE_STRING(S3C2410_WATCHDOG_DEFAULT_TIME) ")");
 88MODULE_PARM_DESC(tmr_atboot,
 89		"Watchdog is started at boot time if set to 1, default="
 90			__MODULE_STRING(S3C2410_WATCHDOG_ATBOOT));
 91MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 92			__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 93MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, 0 to reboot (default 0)");
 94
 95/**
 96 * struct s3c2410_wdt_variant - Per-variant config data
 97 *
 98 * @disable_reg: Offset in pmureg for the register that disables the watchdog
 99 * timer reset functionality.
100 * @mask_reset_reg: Offset in pmureg for the register that masks the watchdog
101 * timer reset functionality.
 
102 * @mask_bit: Bit number for the watchdog timer in the disable register and the
103 * mask reset register.
104 * @rst_stat_reg: Offset in pmureg for the register that has the reset status.
105 * @rst_stat_bit: Bit number in the rst_stat register indicating a watchdog
106 * reset.
 
 
107 * @quirks: A bitfield of quirks.
108 */
109
110struct s3c2410_wdt_variant {
111	int disable_reg;
112	int mask_reset_reg;
 
113	int mask_bit;
114	int rst_stat_reg;
115	int rst_stat_bit;
 
 
116	u32 quirks;
117};
118
119struct s3c2410_wdt {
120	struct device		*dev;
121	struct clk		*clock;
 
122	void __iomem		*reg_base;
123	unsigned int		count;
124	spinlock_t		lock;
125	unsigned long		wtcon_save;
126	unsigned long		wtdat_save;
127	struct watchdog_device	wdt_device;
128	struct notifier_block	freq_transition;
129	const struct s3c2410_wdt_variant *drv_data;
130	struct regmap *pmureg;
131};
132
133static const struct s3c2410_wdt_variant drv_data_s3c2410 = {
134	.quirks = 0
135};
136
137#ifdef CONFIG_OF
138static const struct s3c2410_wdt_variant drv_data_s3c6410 = {
139	.quirks = QUIRK_HAS_WTCLRINT_REG,
140};
141
142static const struct s3c2410_wdt_variant drv_data_exynos5250  = {
143	.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
144	.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
145	.mask_bit = 20,
146	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
147	.rst_stat_bit = 20,
148	.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
149		  | QUIRK_HAS_WTCLRINT_REG,
150};
151
152static const struct s3c2410_wdt_variant drv_data_exynos5420 = {
153	.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
154	.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
155	.mask_bit = 0,
156	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
157	.rst_stat_bit = 9,
158	.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
159		  | QUIRK_HAS_WTCLRINT_REG,
160};
161
162static const struct s3c2410_wdt_variant drv_data_exynos7 = {
163	.disable_reg = EXYNOS5_WDT_DISABLE_REG_OFFSET,
164	.mask_reset_reg = EXYNOS5_WDT_MASK_RESET_REG_OFFSET,
165	.mask_bit = 23,
166	.rst_stat_reg = EXYNOS5_RST_STAT_REG_OFFSET,
167	.rst_stat_bit = 23,	/* A57 WDTRESET */
168	.quirks = QUIRK_HAS_PMU_CONFIG | QUIRK_HAS_RST_STAT \
169		  | QUIRK_HAS_WTCLRINT_REG,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
170};
171
172static const struct of_device_id s3c2410_wdt_match[] = {
 
 
173	{ .compatible = "samsung,s3c2410-wdt",
174	  .data = &drv_data_s3c2410 },
175	{ .compatible = "samsung,s3c6410-wdt",
176	  .data = &drv_data_s3c6410 },
177	{ .compatible = "samsung,exynos5250-wdt",
178	  .data = &drv_data_exynos5250 },
179	{ .compatible = "samsung,exynos5420-wdt",
180	  .data = &drv_data_exynos5420 },
181	{ .compatible = "samsung,exynos7-wdt",
182	  .data = &drv_data_exynos7 },
 
 
 
 
 
 
183	{},
184};
185MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
186#endif
187
188static const struct platform_device_id s3c2410_wdt_ids[] = {
189	{
190		.name = "s3c2410-wdt",
191		.driver_data = (unsigned long)&drv_data_s3c2410,
192	},
193	{}
194};
195MODULE_DEVICE_TABLE(platform, s3c2410_wdt_ids);
196
197/* functions */
198
199static inline unsigned int s3c2410wdt_max_timeout(struct clk *clock)
200{
201	unsigned long freq = clk_get_rate(clock);
 
 
 
 
 
202
203	return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1)
204				       / S3C2410_WTCON_MAXDIV);
205}
206
207static inline struct s3c2410_wdt *freq_to_wdt(struct notifier_block *nb)
208{
209	return container_of(nb, struct s3c2410_wdt, freq_transition);
 
 
 
 
 
 
 
 
 
210}
211
212static int s3c2410wdt_mask_and_disable_reset(struct s3c2410_wdt *wdt, bool mask)
213{
 
 
 
214	int ret;
215	u32 mask_val = 1 << wdt->drv_data->mask_bit;
216	u32 val = 0;
217
218	/* No need to do anything if no PMU CONFIG needed */
219	if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_CONFIG))
220		return 0;
 
221
222	if (mask)
223		val = mask_val;
224
225	ret = regmap_update_bits(wdt->pmureg,
226			wdt->drv_data->disable_reg,
227			mask_val, val);
228	if (ret < 0)
229		goto error;
230
231	ret = regmap_update_bits(wdt->pmureg,
232			wdt->drv_data->mask_reset_reg,
233			mask_val, val);
234 error:
235	if (ret < 0)
236		dev_err(wdt->dev, "failed to update reg(%d)\n", ret);
237
238	return ret;
239}
240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
242{
243	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
 
244
245	spin_lock(&wdt->lock);
246	writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
247	spin_unlock(&wdt->lock);
248
249	return 0;
250}
251
252static void __s3c2410wdt_stop(struct s3c2410_wdt *wdt)
253{
254	unsigned long wtcon;
255
256	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
257	wtcon &= ~(S3C2410_WTCON_ENABLE | S3C2410_WTCON_RSTEN);
258	writel(wtcon, wdt->reg_base + S3C2410_WTCON);
259}
260
261static int s3c2410wdt_stop(struct watchdog_device *wdd)
262{
263	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
 
264
265	spin_lock(&wdt->lock);
266	__s3c2410wdt_stop(wdt);
267	spin_unlock(&wdt->lock);
268
269	return 0;
270}
271
272static int s3c2410wdt_start(struct watchdog_device *wdd)
273{
274	unsigned long wtcon;
275	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
 
276
277	spin_lock(&wdt->lock);
278
279	__s3c2410wdt_stop(wdt);
280
281	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
282	wtcon |= S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV128;
283
284	if (soft_noboot) {
285		wtcon |= S3C2410_WTCON_INTEN;
286		wtcon &= ~S3C2410_WTCON_RSTEN;
287	} else {
288		wtcon &= ~S3C2410_WTCON_INTEN;
289		wtcon |= S3C2410_WTCON_RSTEN;
290	}
291
292	dev_dbg(wdt->dev, "Starting watchdog: count=0x%08x, wtcon=%08lx\n",
293		wdt->count, wtcon);
294
295	writel(wdt->count, wdt->reg_base + S3C2410_WTDAT);
296	writel(wdt->count, wdt->reg_base + S3C2410_WTCNT);
297	writel(wtcon, wdt->reg_base + S3C2410_WTCON);
298	spin_unlock(&wdt->lock);
299
300	return 0;
301}
302
303static inline int s3c2410wdt_is_running(struct s3c2410_wdt *wdt)
304{
305	return readl(wdt->reg_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE;
306}
307
308static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd,
309				    unsigned int timeout)
310{
311	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
312	unsigned long freq = clk_get_rate(wdt->clock);
313	unsigned int count;
314	unsigned int divisor = 1;
315	unsigned long wtcon;
316
317	if (timeout < 1)
318		return -EINVAL;
319
320	freq = DIV_ROUND_UP(freq, 128);
321	count = timeout * freq;
322
323	dev_dbg(wdt->dev, "Heartbeat: count=%d, timeout=%d, freq=%lu\n",
324		count, timeout, freq);
325
326	/* if the count is bigger than the watchdog register,
327	   then work out what we need to do (and if) we can
328	   actually make this value
329	*/
330
331	if (count >= 0x10000) {
332		divisor = DIV_ROUND_UP(count, 0xffff);
333
334		if (divisor > 0x100) {
335			dev_err(wdt->dev, "timeout %d too big\n", timeout);
336			return -EINVAL;
337		}
338	}
339
340	dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%d (%08x)\n",
341		timeout, divisor, count, DIV_ROUND_UP(count, divisor));
342
343	count = DIV_ROUND_UP(count, divisor);
344	wdt->count = count;
345
346	/* update the pre-scaler */
347	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
348	wtcon &= ~S3C2410_WTCON_PRESCALE_MASK;
349	wtcon |= S3C2410_WTCON_PRESCALE(divisor-1);
350
351	writel(count, wdt->reg_base + S3C2410_WTDAT);
352	writel(wtcon, wdt->reg_base + S3C2410_WTCON);
353
354	wdd->timeout = (count * divisor) / freq;
355
356	return 0;
357}
358
359static int s3c2410wdt_restart(struct watchdog_device *wdd, unsigned long action,
360			      void *data)
361{
362	struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd);
363	void __iomem *wdt_base = wdt->reg_base;
364
365	/* disable watchdog, to be safe  */
366	writel(0, wdt_base + S3C2410_WTCON);
367
368	/* put initial values into count and data */
369	writel(0x80, wdt_base + S3C2410_WTCNT);
370	writel(0x80, wdt_base + S3C2410_WTDAT);
371
372	/* set the watchdog to go and reset... */
373	writel(S3C2410_WTCON_ENABLE | S3C2410_WTCON_DIV16 |
374		S3C2410_WTCON_RSTEN | S3C2410_WTCON_PRESCALE(0x20),
375		wdt_base + S3C2410_WTCON);
376
377	/* wait for reset to assert... */
378	mdelay(500);
379
380	return 0;
381}
382
383#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
384
385static const struct watchdog_info s3c2410_wdt_ident = {
386	.options          =     OPTIONS,
387	.firmware_version =	0,
388	.identity         =	"S3C2410 Watchdog",
389};
390
391static const struct watchdog_ops s3c2410wdt_ops = {
392	.owner = THIS_MODULE,
393	.start = s3c2410wdt_start,
394	.stop = s3c2410wdt_stop,
395	.ping = s3c2410wdt_keepalive,
396	.set_timeout = s3c2410wdt_set_heartbeat,
397	.restart = s3c2410wdt_restart,
398};
399
400static const struct watchdog_device s3c2410_wdd = {
401	.info = &s3c2410_wdt_ident,
402	.ops = &s3c2410wdt_ops,
403	.timeout = S3C2410_WATCHDOG_DEFAULT_TIME,
404};
405
406/* interrupt handler code */
407
408static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
409{
410	struct s3c2410_wdt *wdt = platform_get_drvdata(param);
411
412	dev_info(wdt->dev, "watchdog timer expired (irq)\n");
413
414	s3c2410wdt_keepalive(&wdt->wdt_device);
415
416	if (wdt->drv_data->quirks & QUIRK_HAS_WTCLRINT_REG)
417		writel(0x1, wdt->reg_base + S3C2410_WTCLRINT);
418
419	return IRQ_HANDLED;
420}
421
422#ifdef CONFIG_ARM_S3C24XX_CPUFREQ
423
424static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb,
425					  unsigned long val, void *data)
426{
427	int ret;
428	struct s3c2410_wdt *wdt = freq_to_wdt(nb);
429
430	if (!s3c2410wdt_is_running(wdt))
431		goto done;
432
433	if (val == CPUFREQ_PRECHANGE) {
434		/* To ensure that over the change we don't cause the
435		 * watchdog to trigger, we perform an keep-alive if
436		 * the watchdog is running.
437		 */
438
439		s3c2410wdt_keepalive(&wdt->wdt_device);
440	} else if (val == CPUFREQ_POSTCHANGE) {
441		s3c2410wdt_stop(&wdt->wdt_device);
442
443		ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
444						wdt->wdt_device.timeout);
445
446		if (ret >= 0)
447			s3c2410wdt_start(&wdt->wdt_device);
448		else
449			goto err;
450	}
451
452done:
453	return 0;
454
455 err:
456	dev_err(wdt->dev, "cannot set new value for timeout %d\n",
457				wdt->wdt_device.timeout);
458	return ret;
459}
460
461static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt)
462{
463	wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition;
464
465	return cpufreq_register_notifier(&wdt->freq_transition,
466					 CPUFREQ_TRANSITION_NOTIFIER);
467}
468
469static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt)
470{
471	wdt->freq_transition.notifier_call = s3c2410wdt_cpufreq_transition;
472
473	cpufreq_unregister_notifier(&wdt->freq_transition,
474				    CPUFREQ_TRANSITION_NOTIFIER);
475}
476
477#else
478
479static inline int s3c2410wdt_cpufreq_register(struct s3c2410_wdt *wdt)
480{
481	return 0;
482}
483
484static inline void s3c2410wdt_cpufreq_deregister(struct s3c2410_wdt *wdt)
485{
486}
487#endif
488
489static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt)
490{
491	unsigned int rst_stat;
492	int ret;
493
494	if (!(wdt->drv_data->quirks & QUIRK_HAS_RST_STAT))
495		return 0;
496
497	ret = regmap_read(wdt->pmureg, wdt->drv_data->rst_stat_reg, &rst_stat);
498	if (ret)
499		dev_warn(wdt->dev, "Couldn't get RST_STAT register\n");
500	else if (rst_stat & BIT(wdt->drv_data->rst_stat_bit))
501		return WDIOF_CARDRESET;
502
503	return 0;
504}
505
506static inline const struct s3c2410_wdt_variant *
507s3c2410_get_wdt_drv_data(struct platform_device *pdev)
508{
509	const struct s3c2410_wdt_variant *variant;
 
510
511	variant = of_device_get_match_data(&pdev->dev);
512	if (!variant) {
513		/* Device matched by platform_device_id */
514		variant = (struct s3c2410_wdt_variant *)
515			   platform_get_device_id(pdev)->driver_data;
516	}
517
518	return variant;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
519}
520
521static int s3c2410wdt_probe(struct platform_device *pdev)
522{
523	struct device *dev = &pdev->dev;
524	struct s3c2410_wdt *wdt;
525	struct resource *wdt_mem;
526	struct resource *wdt_irq;
527	unsigned int wtcon;
528	int started = 0;
529	int ret;
530
531	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
532	if (!wdt)
533		return -ENOMEM;
534
535	wdt->dev = dev;
536	spin_lock_init(&wdt->lock);
537	wdt->wdt_device = s3c2410_wdd;
538
539	wdt->drv_data = s3c2410_get_wdt_drv_data(pdev);
 
 
 
540	if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) {
541		wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
542						"samsung,syscon-phandle");
543		if (IS_ERR(wdt->pmureg)) {
544			dev_err(dev, "syscon regmap lookup failed.\n");
545			return PTR_ERR(wdt->pmureg);
546		}
547	}
548
549	wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
550	if (wdt_irq == NULL) {
551		dev_err(dev, "no irq resource specified\n");
552		ret = -ENOENT;
553		goto err;
554	}
555
556	/* get the memory region for the watchdog timer */
557	wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
558	wdt->reg_base = devm_ioremap_resource(dev, wdt_mem);
559	if (IS_ERR(wdt->reg_base)) {
560		ret = PTR_ERR(wdt->reg_base);
561		goto err;
562	}
563
564	wdt->clock = devm_clk_get(dev, "watchdog");
565	if (IS_ERR(wdt->clock)) {
566		dev_err(dev, "failed to find watchdog clock source\n");
567		ret = PTR_ERR(wdt->clock);
568		goto err;
569	}
570
571	ret = clk_prepare_enable(wdt->clock);
572	if (ret < 0) {
573		dev_err(dev, "failed to enable clock\n");
574		return ret;
575	}
576
577	wdt->wdt_device.min_timeout = 1;
578	wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt->clock);
579
580	ret = s3c2410wdt_cpufreq_register(wdt);
581	if (ret < 0) {
582		dev_err(dev, "failed to register cpufreq\n");
583		goto err_clk;
584	}
585
586	watchdog_set_drvdata(&wdt->wdt_device, wdt);
587
588	/* see if we can actually set the requested timer margin, and if
589	 * not, try the default value */
590
591	watchdog_init_timeout(&wdt->wdt_device, tmr_margin, dev);
592	ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
593					wdt->wdt_device.timeout);
594	if (ret) {
595		started = s3c2410wdt_set_heartbeat(&wdt->wdt_device,
596					S3C2410_WATCHDOG_DEFAULT_TIME);
597
598		if (started == 0)
599			dev_info(dev,
600				 "tmr_margin value out of range, default %d used\n",
601				 S3C2410_WATCHDOG_DEFAULT_TIME);
602		else
603			dev_info(dev, "default timer value is out of range, cannot start\n");
604	}
605
606	ret = devm_request_irq(dev, wdt_irq->start, s3c2410wdt_irq, 0,
607				pdev->name, pdev);
608	if (ret != 0) {
609		dev_err(dev, "failed to install irq (%d)\n", ret);
610		goto err_cpufreq;
611	}
612
613	watchdog_set_nowayout(&wdt->wdt_device, nowayout);
614	watchdog_set_restart_priority(&wdt->wdt_device, 128);
615
616	wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt);
617	wdt->wdt_device.parent = dev;
618
619	ret = watchdog_register_device(&wdt->wdt_device);
620	if (ret) {
621		dev_err(dev, "cannot register watchdog (%d)\n", ret);
622		goto err_cpufreq;
623	}
624
625	ret = s3c2410wdt_mask_and_disable_reset(wdt, false);
626	if (ret < 0)
627		goto err_unregister;
628
629	if (tmr_atboot && started == 0) {
 
 
 
 
 
 
 
630		dev_info(dev, "starting watchdog timer\n");
631		s3c2410wdt_start(&wdt->wdt_device);
632	} else if (!tmr_atboot) {
633		/* if we're not enabling the watchdog, then ensure it is
634		 * disabled if it has been left running from the bootloader
635		 * or other source */
636
637		s3c2410wdt_stop(&wdt->wdt_device);
638	}
639
 
 
 
 
 
 
 
 
 
 
 
 
640	platform_set_drvdata(pdev, wdt);
641
642	/* print out a statement of readiness */
643
644	wtcon = readl(wdt->reg_base + S3C2410_WTCON);
645
646	dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
647		 (wtcon & S3C2410_WTCON_ENABLE) ?  "" : "in",
648		 (wtcon & S3C2410_WTCON_RSTEN) ? "en" : "dis",
649		 (wtcon & S3C2410_WTCON_INTEN) ? "en" : "dis");
650
651	return 0;
652
653 err_unregister:
654	watchdog_unregister_device(&wdt->wdt_device);
655
656 err_cpufreq:
657	s3c2410wdt_cpufreq_deregister(wdt);
658
659 err_clk:
660	clk_disable_unprepare(wdt->clock);
661
662 err:
663	return ret;
664}
665
666static int s3c2410wdt_remove(struct platform_device *dev)
667{
668	int ret;
669	struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
670
671	ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
672	if (ret < 0)
673		return ret;
674
675	watchdog_unregister_device(&wdt->wdt_device);
676
677	s3c2410wdt_cpufreq_deregister(wdt);
678
679	clk_disable_unprepare(wdt->clock);
680
681	return 0;
682}
683
684static void s3c2410wdt_shutdown(struct platform_device *dev)
685{
686	struct s3c2410_wdt *wdt = platform_get_drvdata(dev);
687
688	s3c2410wdt_mask_and_disable_reset(wdt, true);
689
690	s3c2410wdt_stop(&wdt->wdt_device);
691}
692
693#ifdef CONFIG_PM_SLEEP
694
695static int s3c2410wdt_suspend(struct device *dev)
696{
697	int ret;
698	struct s3c2410_wdt *wdt = dev_get_drvdata(dev);
699
700	/* Save watchdog state, and turn it off. */
701	wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON);
702	wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT);
703
704	ret = s3c2410wdt_mask_and_disable_reset(wdt, true);
705	if (ret < 0)
706		return ret;
707
708	/* Note that WTCNT doesn't need to be saved. */
709	s3c2410wdt_stop(&wdt->wdt_device);
710
711	return 0;
712}
713
714static int s3c2410wdt_resume(struct device *dev)
715{
716	int ret;
717	struct s3c2410_wdt *wdt = dev_get_drvdata(dev);
718
719	/* Restore watchdog state. */
720	writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTDAT);
721	writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);/* Reset count */
722	writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON);
723
724	ret = s3c2410wdt_mask_and_disable_reset(wdt, false);
725	if (ret < 0)
726		return ret;
727
728	dev_info(dev, "watchdog %sabled\n",
729		(wdt->wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis");
730
731	return 0;
732}
733#endif
734
735static SIMPLE_DEV_PM_OPS(s3c2410wdt_pm_ops, s3c2410wdt_suspend,
736			s3c2410wdt_resume);
737
738static struct platform_driver s3c2410wdt_driver = {
739	.probe		= s3c2410wdt_probe,
740	.remove		= s3c2410wdt_remove,
741	.shutdown	= s3c2410wdt_shutdown,
742	.id_table	= s3c2410_wdt_ids,
743	.driver		= {
744		.name	= "s3c2410-wdt",
745		.pm	= &s3c2410wdt_pm_ops,
746		.of_match_table	= of_match_ptr(s3c2410_wdt_match),
747	},
748};
749
750module_platform_driver(s3c2410wdt_driver);
751
752MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Dimitry Andric <dimitry.andric@tomtom.com>");
753MODULE_DESCRIPTION("S3C2410 Watchdog Device Driver");
754MODULE_LICENSE("GPL");