1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright 2017 CS Systemes d'Information
4 */
5
6#include <common.h>
7#include <env.h>
8#include <dm.h>
9#include <wdt.h>
10#include <clock_legacy.h>
11#include <asm/io.h>
12
13struct mpc8xxx_wdt {
14	__be32 res0;
15	__be32 swcrr;	/* System watchdog control register */
16#define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */
17#define SWCRR_BME  0x00000080 /* Bus monitor enable (mpc8xx) */
18#define SWCRR_SWF  0x00000008 /* Software Watchdog Freeze (mpc8xx). */
19#define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */
20#define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/
21#define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */
22	__be32 swcnr;	/* System watchdog count register */
23	u8 res1[2];
24	__be16 swsrr;	/* System watchdog service register */
25	u8 res2[0xf0];
26};
27
28struct mpc8xxx_wdt_priv {
29	struct mpc8xxx_wdt __iomem *base;
30};
31
32static int mpc8xxx_wdt_reset(struct udevice *dev)
33{
34	struct mpc8xxx_wdt_priv *priv = dev_get_priv(dev);
35
36	out_be16(&priv->base->swsrr, 0x556c);	/* write magic1 */
37	out_be16(&priv->base->swsrr, 0xaa39);	/* write magic2 */
38
39	return 0;
40}
41
42static int mpc8xxx_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
43{
44	struct mpc8xxx_wdt_priv *priv = dev_get_priv(dev);
45	const char *mode = env_get("watchdog_mode");
46	ulong prescaler = dev_get_driver_data(dev);
47	u16 swtc = min_t(u16, timeout * get_board_sys_clk() / 1000 / prescaler, U16_MAX);
48	u32 val;
49
50	mpc8xxx_wdt_reset(dev);
51
52	if (strcmp(mode, "off") == 0)
53		val = (swtc << 16) | SWCRR_SWPR;
54	else if (strcmp(mode, "nmi") == 0)
55		val = (swtc << 16) | SWCRR_SWPR | SWCRR_SWEN;
56	else
57		val = (swtc << 16) | SWCRR_SWPR | SWCRR_SWEN | SWCRR_SWRI;
58
59	if (IS_ENABLED(CONFIG_WDT_MPC8xxx_BME))
60		val |= (CONFIG_WDT_MPC8xxx_BMT << 8) | SWCRR_BME;
61
62	out_be32(&priv->base->swcrr, val);
63
64	if (!(in_be32(&priv->base->swcrr) & SWCRR_SWEN))
65		return -EBUSY;
66	return 0;
67
68}
69
70static int mpc8xxx_wdt_stop(struct udevice *dev)
71{
72	struct mpc8xxx_wdt_priv *priv = dev_get_priv(dev);
73
74	clrbits_be32(&priv->base->swcrr, SWCRR_SWEN);
75
76	if (in_be32(&priv->base->swcrr) & SWCRR_SWEN)
77		return -EBUSY;
78	return 0;
79}
80
81static int mpc8xxx_wdt_of_to_plat(struct udevice *dev)
82{
83	struct mpc8xxx_wdt_priv *priv = dev_get_priv(dev);
84
85	priv->base = (void __iomem *)devfdt_remap_addr(dev);
86
87	if (!priv->base)
88		return -EINVAL;
89
90	return 0;
91}
92
93static const struct wdt_ops mpc8xxx_wdt_ops = {
94	.start = mpc8xxx_wdt_start,
95	.reset = mpc8xxx_wdt_reset,
96	.stop = mpc8xxx_wdt_stop,
97};
98
99static const struct udevice_id mpc8xxx_wdt_ids[] = {
100	{ .compatible = "fsl,pq1-wdt", .data = 0x800 },
101	{ .compatible = "fsl,pq2pro-wdt", .data = 0x10000 },
102	{}
103};
104
105U_BOOT_DRIVER(wdt_mpc8xxx) = {
106	.name = "wdt_mpc8xxx",
107	.id = UCLASS_WDT,
108	.of_match = mpc8xxx_wdt_ids,
109	.ops = &mpc8xxx_wdt_ops,
110	.of_to_plat = mpc8xxx_wdt_of_to_plat,
111	.priv_auto	= sizeof(struct mpc8xxx_wdt_priv),
112};
113