xref: /openbmc/linux/drivers/watchdog/pic32-dmt.c (revision b0d8a082ce9c82600ebf8949dab937cf53019356)
1*b0d8a082SPurna Chandra Mandal /*
2*b0d8a082SPurna Chandra Mandal  * PIC32 deadman timer driver
3*b0d8a082SPurna Chandra Mandal  *
4*b0d8a082SPurna Chandra Mandal  * Purna Chandra Mandal <purna.mandal@microchip.com>
5*b0d8a082SPurna Chandra Mandal  * Copyright (c) 2016, Microchip Technology Inc.
6*b0d8a082SPurna Chandra Mandal  *
7*b0d8a082SPurna Chandra Mandal  * This program is free software; you can redistribute it and/or
8*b0d8a082SPurna Chandra Mandal  * modify it under the terms of the GNU General Public License
9*b0d8a082SPurna Chandra Mandal  * as published by the Free Software Foundation; either version
10*b0d8a082SPurna Chandra Mandal  * 2 of the License, or (at your option) any later version.
11*b0d8a082SPurna Chandra Mandal  */
12*b0d8a082SPurna Chandra Mandal #include <linux/clk.h>
13*b0d8a082SPurna Chandra Mandal #include <linux/device.h>
14*b0d8a082SPurna Chandra Mandal #include <linux/err.h>
15*b0d8a082SPurna Chandra Mandal #include <linux/io.h>
16*b0d8a082SPurna Chandra Mandal #include <linux/kernel.h>
17*b0d8a082SPurna Chandra Mandal #include <linux/module.h>
18*b0d8a082SPurna Chandra Mandal #include <linux/of.h>
19*b0d8a082SPurna Chandra Mandal #include <linux/of_device.h>
20*b0d8a082SPurna Chandra Mandal #include <linux/platform_device.h>
21*b0d8a082SPurna Chandra Mandal #include <linux/pm.h>
22*b0d8a082SPurna Chandra Mandal #include <linux/watchdog.h>
23*b0d8a082SPurna Chandra Mandal 
24*b0d8a082SPurna Chandra Mandal #include <asm/mach-pic32/pic32.h>
25*b0d8a082SPurna Chandra Mandal 
26*b0d8a082SPurna Chandra Mandal /* Deadman Timer Regs */
27*b0d8a082SPurna Chandra Mandal #define DMTCON_REG	0x00
28*b0d8a082SPurna Chandra Mandal #define DMTPRECLR_REG	0x10
29*b0d8a082SPurna Chandra Mandal #define DMTCLR_REG	0x20
30*b0d8a082SPurna Chandra Mandal #define DMTSTAT_REG	0x30
31*b0d8a082SPurna Chandra Mandal #define DMTCNT_REG	0x40
32*b0d8a082SPurna Chandra Mandal #define DMTPSCNT_REG	0x60
33*b0d8a082SPurna Chandra Mandal #define DMTPSINTV_REG	0x70
34*b0d8a082SPurna Chandra Mandal 
35*b0d8a082SPurna Chandra Mandal /* Deadman Timer Regs fields */
36*b0d8a082SPurna Chandra Mandal #define DMT_ON			BIT(15)
37*b0d8a082SPurna Chandra Mandal #define DMT_STEP1_KEY		BIT(6)
38*b0d8a082SPurna Chandra Mandal #define DMT_STEP2_KEY		BIT(3)
39*b0d8a082SPurna Chandra Mandal #define DMTSTAT_WINOPN		BIT(0)
40*b0d8a082SPurna Chandra Mandal #define DMTSTAT_EVENT		BIT(5)
41*b0d8a082SPurna Chandra Mandal #define DMTSTAT_BAD2		BIT(6)
42*b0d8a082SPurna Chandra Mandal #define DMTSTAT_BAD1		BIT(7)
43*b0d8a082SPurna Chandra Mandal 
44*b0d8a082SPurna Chandra Mandal /* Reset Control Register fields for watchdog */
45*b0d8a082SPurna Chandra Mandal #define RESETCON_DMT_TIMEOUT	BIT(5)
46*b0d8a082SPurna Chandra Mandal 
47*b0d8a082SPurna Chandra Mandal struct pic32_dmt {
48*b0d8a082SPurna Chandra Mandal 	void __iomem	*regs;
49*b0d8a082SPurna Chandra Mandal 	struct clk	*clk;
50*b0d8a082SPurna Chandra Mandal };
51*b0d8a082SPurna Chandra Mandal 
52*b0d8a082SPurna Chandra Mandal static inline void dmt_enable(struct pic32_dmt *dmt)
53*b0d8a082SPurna Chandra Mandal {
54*b0d8a082SPurna Chandra Mandal 	writel(DMT_ON, PIC32_SET(dmt->regs + DMTCON_REG));
55*b0d8a082SPurna Chandra Mandal }
56*b0d8a082SPurna Chandra Mandal 
57*b0d8a082SPurna Chandra Mandal static inline void dmt_disable(struct pic32_dmt *dmt)
58*b0d8a082SPurna Chandra Mandal {
59*b0d8a082SPurna Chandra Mandal 	writel(DMT_ON, PIC32_CLR(dmt->regs + DMTCON_REG));
60*b0d8a082SPurna Chandra Mandal 	/*
61*b0d8a082SPurna Chandra Mandal 	 * Cannot touch registers in the CPU cycle following clearing the
62*b0d8a082SPurna Chandra Mandal 	 * ON bit.
63*b0d8a082SPurna Chandra Mandal 	 */
64*b0d8a082SPurna Chandra Mandal 	nop();
65*b0d8a082SPurna Chandra Mandal }
66*b0d8a082SPurna Chandra Mandal 
67*b0d8a082SPurna Chandra Mandal static inline int dmt_bad_status(struct pic32_dmt *dmt)
68*b0d8a082SPurna Chandra Mandal {
69*b0d8a082SPurna Chandra Mandal 	u32 val;
70*b0d8a082SPurna Chandra Mandal 
71*b0d8a082SPurna Chandra Mandal 	val = readl(dmt->regs + DMTSTAT_REG);
72*b0d8a082SPurna Chandra Mandal 	val &= (DMTSTAT_BAD1 | DMTSTAT_BAD2 | DMTSTAT_EVENT);
73*b0d8a082SPurna Chandra Mandal 	if (val)
74*b0d8a082SPurna Chandra Mandal 		return -EAGAIN;
75*b0d8a082SPurna Chandra Mandal 
76*b0d8a082SPurna Chandra Mandal 	return 0;
77*b0d8a082SPurna Chandra Mandal }
78*b0d8a082SPurna Chandra Mandal 
79*b0d8a082SPurna Chandra Mandal static inline int dmt_keepalive(struct pic32_dmt *dmt)
80*b0d8a082SPurna Chandra Mandal {
81*b0d8a082SPurna Chandra Mandal 	u32 v;
82*b0d8a082SPurna Chandra Mandal 	u32 timeout = 500;
83*b0d8a082SPurna Chandra Mandal 
84*b0d8a082SPurna Chandra Mandal 	/* set pre-clear key */
85*b0d8a082SPurna Chandra Mandal 	writel(DMT_STEP1_KEY << 8, dmt->regs + DMTPRECLR_REG);
86*b0d8a082SPurna Chandra Mandal 
87*b0d8a082SPurna Chandra Mandal 	/* wait for DMT window to open */
88*b0d8a082SPurna Chandra Mandal 	while (--timeout) {
89*b0d8a082SPurna Chandra Mandal 		v = readl(dmt->regs + DMTSTAT_REG) & DMTSTAT_WINOPN;
90*b0d8a082SPurna Chandra Mandal 		if (v == DMTSTAT_WINOPN)
91*b0d8a082SPurna Chandra Mandal 			break;
92*b0d8a082SPurna Chandra Mandal 	}
93*b0d8a082SPurna Chandra Mandal 
94*b0d8a082SPurna Chandra Mandal 	/* apply key2 */
95*b0d8a082SPurna Chandra Mandal 	writel(DMT_STEP2_KEY, dmt->regs + DMTCLR_REG);
96*b0d8a082SPurna Chandra Mandal 
97*b0d8a082SPurna Chandra Mandal 	/* check whether keys are latched correctly */
98*b0d8a082SPurna Chandra Mandal 	return dmt_bad_status(dmt);
99*b0d8a082SPurna Chandra Mandal }
100*b0d8a082SPurna Chandra Mandal 
101*b0d8a082SPurna Chandra Mandal static inline u32 pic32_dmt_get_timeout_secs(struct pic32_dmt *dmt)
102*b0d8a082SPurna Chandra Mandal {
103*b0d8a082SPurna Chandra Mandal 	unsigned long rate;
104*b0d8a082SPurna Chandra Mandal 
105*b0d8a082SPurna Chandra Mandal 	rate = clk_get_rate(dmt->clk);
106*b0d8a082SPurna Chandra Mandal 	if (rate)
107*b0d8a082SPurna Chandra Mandal 		return readl(dmt->regs + DMTPSCNT_REG) / rate;
108*b0d8a082SPurna Chandra Mandal 
109*b0d8a082SPurna Chandra Mandal 	return 0;
110*b0d8a082SPurna Chandra Mandal }
111*b0d8a082SPurna Chandra Mandal 
112*b0d8a082SPurna Chandra Mandal static inline u32 pic32_dmt_bootstatus(struct pic32_dmt *dmt)
113*b0d8a082SPurna Chandra Mandal {
114*b0d8a082SPurna Chandra Mandal 	u32 v;
115*b0d8a082SPurna Chandra Mandal 	void __iomem *rst_base;
116*b0d8a082SPurna Chandra Mandal 
117*b0d8a082SPurna Chandra Mandal 	rst_base = ioremap(PIC32_BASE_RESET, 0x10);
118*b0d8a082SPurna Chandra Mandal 	if (!rst_base)
119*b0d8a082SPurna Chandra Mandal 		return 0;
120*b0d8a082SPurna Chandra Mandal 
121*b0d8a082SPurna Chandra Mandal 	v = readl(rst_base);
122*b0d8a082SPurna Chandra Mandal 
123*b0d8a082SPurna Chandra Mandal 	writel(RESETCON_DMT_TIMEOUT, PIC32_CLR(rst_base));
124*b0d8a082SPurna Chandra Mandal 
125*b0d8a082SPurna Chandra Mandal 	iounmap(rst_base);
126*b0d8a082SPurna Chandra Mandal 	return v & RESETCON_DMT_TIMEOUT;
127*b0d8a082SPurna Chandra Mandal }
128*b0d8a082SPurna Chandra Mandal 
129*b0d8a082SPurna Chandra Mandal static int pic32_dmt_start(struct watchdog_device *wdd)
130*b0d8a082SPurna Chandra Mandal {
131*b0d8a082SPurna Chandra Mandal 	struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);
132*b0d8a082SPurna Chandra Mandal 
133*b0d8a082SPurna Chandra Mandal 	dmt_enable(dmt);
134*b0d8a082SPurna Chandra Mandal 	return dmt_keepalive(dmt);
135*b0d8a082SPurna Chandra Mandal }
136*b0d8a082SPurna Chandra Mandal 
137*b0d8a082SPurna Chandra Mandal static int pic32_dmt_stop(struct watchdog_device *wdd)
138*b0d8a082SPurna Chandra Mandal {
139*b0d8a082SPurna Chandra Mandal 	struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);
140*b0d8a082SPurna Chandra Mandal 
141*b0d8a082SPurna Chandra Mandal 	dmt_disable(dmt);
142*b0d8a082SPurna Chandra Mandal 
143*b0d8a082SPurna Chandra Mandal 	return 0;
144*b0d8a082SPurna Chandra Mandal }
145*b0d8a082SPurna Chandra Mandal 
146*b0d8a082SPurna Chandra Mandal static int pic32_dmt_ping(struct watchdog_device *wdd)
147*b0d8a082SPurna Chandra Mandal {
148*b0d8a082SPurna Chandra Mandal 	struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);
149*b0d8a082SPurna Chandra Mandal 
150*b0d8a082SPurna Chandra Mandal 	return dmt_keepalive(dmt);
151*b0d8a082SPurna Chandra Mandal }
152*b0d8a082SPurna Chandra Mandal 
153*b0d8a082SPurna Chandra Mandal static const struct watchdog_ops pic32_dmt_fops = {
154*b0d8a082SPurna Chandra Mandal 	.owner		= THIS_MODULE,
155*b0d8a082SPurna Chandra Mandal 	.start		= pic32_dmt_start,
156*b0d8a082SPurna Chandra Mandal 	.stop		= pic32_dmt_stop,
157*b0d8a082SPurna Chandra Mandal 	.ping		= pic32_dmt_ping,
158*b0d8a082SPurna Chandra Mandal };
159*b0d8a082SPurna Chandra Mandal 
160*b0d8a082SPurna Chandra Mandal static const struct watchdog_info pic32_dmt_ident = {
161*b0d8a082SPurna Chandra Mandal 	.options	= WDIOF_KEEPALIVEPING |
162*b0d8a082SPurna Chandra Mandal 			  WDIOF_MAGICCLOSE,
163*b0d8a082SPurna Chandra Mandal 	.identity	= "PIC32 Deadman Timer",
164*b0d8a082SPurna Chandra Mandal };
165*b0d8a082SPurna Chandra Mandal 
166*b0d8a082SPurna Chandra Mandal static struct watchdog_device pic32_dmt_wdd = {
167*b0d8a082SPurna Chandra Mandal 	.info		= &pic32_dmt_ident,
168*b0d8a082SPurna Chandra Mandal 	.ops		= &pic32_dmt_fops,
169*b0d8a082SPurna Chandra Mandal };
170*b0d8a082SPurna Chandra Mandal 
171*b0d8a082SPurna Chandra Mandal static int pic32_dmt_probe(struct platform_device *pdev)
172*b0d8a082SPurna Chandra Mandal {
173*b0d8a082SPurna Chandra Mandal 	int ret;
174*b0d8a082SPurna Chandra Mandal 	struct pic32_dmt *dmt;
175*b0d8a082SPurna Chandra Mandal 	struct resource *mem;
176*b0d8a082SPurna Chandra Mandal 	struct watchdog_device *wdd = &pic32_dmt_wdd;
177*b0d8a082SPurna Chandra Mandal 
178*b0d8a082SPurna Chandra Mandal 	dmt = devm_kzalloc(&pdev->dev, sizeof(*dmt), GFP_KERNEL);
179*b0d8a082SPurna Chandra Mandal 	if (IS_ERR(dmt))
180*b0d8a082SPurna Chandra Mandal 		return PTR_ERR(dmt);
181*b0d8a082SPurna Chandra Mandal 
182*b0d8a082SPurna Chandra Mandal 	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
183*b0d8a082SPurna Chandra Mandal 	dmt->regs = devm_ioremap_resource(&pdev->dev, mem);
184*b0d8a082SPurna Chandra Mandal 	if (IS_ERR(dmt->regs))
185*b0d8a082SPurna Chandra Mandal 		return PTR_ERR(dmt->regs);
186*b0d8a082SPurna Chandra Mandal 
187*b0d8a082SPurna Chandra Mandal 	dmt->clk = devm_clk_get(&pdev->dev, NULL);
188*b0d8a082SPurna Chandra Mandal 	if (IS_ERR(dmt->clk)) {
189*b0d8a082SPurna Chandra Mandal 		dev_err(&pdev->dev, "clk not found\n");
190*b0d8a082SPurna Chandra Mandal 		return PTR_ERR(dmt->clk);
191*b0d8a082SPurna Chandra Mandal 	}
192*b0d8a082SPurna Chandra Mandal 
193*b0d8a082SPurna Chandra Mandal 	ret = clk_prepare_enable(dmt->clk);
194*b0d8a082SPurna Chandra Mandal 	if (ret)
195*b0d8a082SPurna Chandra Mandal 		return ret;
196*b0d8a082SPurna Chandra Mandal 
197*b0d8a082SPurna Chandra Mandal 	wdd->timeout = pic32_dmt_get_timeout_secs(dmt);
198*b0d8a082SPurna Chandra Mandal 	if (!wdd->timeout) {
199*b0d8a082SPurna Chandra Mandal 		dev_err(&pdev->dev,
200*b0d8a082SPurna Chandra Mandal 			"failed to read watchdog register timeout\n");
201*b0d8a082SPurna Chandra Mandal 		ret = -EINVAL;
202*b0d8a082SPurna Chandra Mandal 		goto out_disable_clk;
203*b0d8a082SPurna Chandra Mandal 	}
204*b0d8a082SPurna Chandra Mandal 
205*b0d8a082SPurna Chandra Mandal 	dev_info(&pdev->dev, "timeout %d\n", wdd->timeout);
206*b0d8a082SPurna Chandra Mandal 
207*b0d8a082SPurna Chandra Mandal 	wdd->bootstatus = pic32_dmt_bootstatus(dmt) ? WDIOF_CARDRESET : 0;
208*b0d8a082SPurna Chandra Mandal 
209*b0d8a082SPurna Chandra Mandal 	watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
210*b0d8a082SPurna Chandra Mandal 	watchdog_set_drvdata(wdd, dmt);
211*b0d8a082SPurna Chandra Mandal 
212*b0d8a082SPurna Chandra Mandal 	ret = watchdog_register_device(wdd);
213*b0d8a082SPurna Chandra Mandal 	if (ret) {
214*b0d8a082SPurna Chandra Mandal 		dev_err(&pdev->dev, "watchdog register failed, err %d\n", ret);
215*b0d8a082SPurna Chandra Mandal 		goto out_disable_clk;
216*b0d8a082SPurna Chandra Mandal 	}
217*b0d8a082SPurna Chandra Mandal 
218*b0d8a082SPurna Chandra Mandal 	platform_set_drvdata(pdev, wdd);
219*b0d8a082SPurna Chandra Mandal 	return 0;
220*b0d8a082SPurna Chandra Mandal 
221*b0d8a082SPurna Chandra Mandal out_disable_clk:
222*b0d8a082SPurna Chandra Mandal 	clk_disable_unprepare(dmt->clk);
223*b0d8a082SPurna Chandra Mandal 	return ret;
224*b0d8a082SPurna Chandra Mandal }
225*b0d8a082SPurna Chandra Mandal 
226*b0d8a082SPurna Chandra Mandal static int pic32_dmt_remove(struct platform_device *pdev)
227*b0d8a082SPurna Chandra Mandal {
228*b0d8a082SPurna Chandra Mandal 	struct watchdog_device *wdd = platform_get_drvdata(pdev);
229*b0d8a082SPurna Chandra Mandal 	struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);
230*b0d8a082SPurna Chandra Mandal 
231*b0d8a082SPurna Chandra Mandal 	watchdog_unregister_device(wdd);
232*b0d8a082SPurna Chandra Mandal 	clk_disable_unprepare(dmt->clk);
233*b0d8a082SPurna Chandra Mandal 
234*b0d8a082SPurna Chandra Mandal 	return 0;
235*b0d8a082SPurna Chandra Mandal }
236*b0d8a082SPurna Chandra Mandal 
237*b0d8a082SPurna Chandra Mandal static const struct of_device_id pic32_dmt_of_ids[] = {
238*b0d8a082SPurna Chandra Mandal 	{ .compatible = "microchip,pic32mzda-dmt",},
239*b0d8a082SPurna Chandra Mandal 	{ /* sentinel */ }
240*b0d8a082SPurna Chandra Mandal };
241*b0d8a082SPurna Chandra Mandal MODULE_DEVICE_TABLE(of, pic32_dmt_of_ids);
242*b0d8a082SPurna Chandra Mandal 
243*b0d8a082SPurna Chandra Mandal static struct platform_driver pic32_dmt_driver = {
244*b0d8a082SPurna Chandra Mandal 	.probe		= pic32_dmt_probe,
245*b0d8a082SPurna Chandra Mandal 	.remove		= pic32_dmt_remove,
246*b0d8a082SPurna Chandra Mandal 	.driver		= {
247*b0d8a082SPurna Chandra Mandal 		.name		= "pic32-dmt",
248*b0d8a082SPurna Chandra Mandal 		.owner		= THIS_MODULE,
249*b0d8a082SPurna Chandra Mandal 		.of_match_table = of_match_ptr(pic32_dmt_of_ids),
250*b0d8a082SPurna Chandra Mandal 	}
251*b0d8a082SPurna Chandra Mandal };
252*b0d8a082SPurna Chandra Mandal 
253*b0d8a082SPurna Chandra Mandal module_platform_driver(pic32_dmt_driver);
254*b0d8a082SPurna Chandra Mandal 
255*b0d8a082SPurna Chandra Mandal MODULE_AUTHOR("Purna Chandra Mandal <purna.mandal@microchip.com>");
256*b0d8a082SPurna Chandra Mandal MODULE_DESCRIPTION("Microchip PIC32 DMT Driver");
257*b0d8a082SPurna Chandra Mandal MODULE_LICENSE("GPL");
258