Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Driver for STM32 Independent Watchdog
  4 *
  5 * Copyright (C) STMicroelectronics 2017
  6 * Author: Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
  7 *
  8 * This driver is based on tegra_wdt.c
  9 *
 10 */
 11
 12#include <linux/clk.h>
 13#include <linux/delay.h>
 14#include <linux/kernel.h>
 15#include <linux/module.h>
 16#include <linux/interrupt.h>
 17#include <linux/io.h>
 18#include <linux/iopoll.h>
 19#include <linux/of.h>
 20#include <linux/platform_device.h>
 21#include <linux/watchdog.h>
 22
 23/* IWDG registers */
 24#define IWDG_KR		0x00 /* Key register */
 25#define IWDG_PR		0x04 /* Prescaler Register */
 26#define IWDG_RLR	0x08 /* ReLoad Register */
 27#define IWDG_SR		0x0C /* Status Register */
 28#define IWDG_WINR	0x10 /* Windows Register */
 29
 30/* IWDG_KR register bit mask */
 31#define KR_KEY_RELOAD	0xAAAA /* reload counter enable */
 32#define KR_KEY_ENABLE	0xCCCC /* peripheral enable */
 33#define KR_KEY_EWA	0x5555 /* write access enable */
 34#define KR_KEY_DWA	0x0000 /* write access disable */
 35
 36/* IWDG_PR register bit values */
 37#define PR_4		0x00 /* prescaler set to 4 */
 38#define PR_8		0x01 /* prescaler set to 8 */
 39#define PR_16		0x02 /* prescaler set to 16 */
 40#define PR_32		0x03 /* prescaler set to 32 */
 41#define PR_64		0x04 /* prescaler set to 64 */
 42#define PR_128		0x05 /* prescaler set to 128 */
 43#define PR_256		0x06 /* prescaler set to 256 */
 44
 45/* IWDG_RLR register values */
 46#define RLR_MIN		0x07C /* min value supported by reload register */
 47#define RLR_MAX		0xFFF /* max value supported by reload register */
 48
 49/* IWDG_SR register bit mask */
 50#define FLAG_PVU	BIT(0) /* Watchdog prescaler value update */
 51#define FLAG_RVU	BIT(1) /* Watchdog counter reload value update */
 52
 53/* set timeout to 100000 us */
 54#define TIMEOUT_US	100000
 55#define SLEEP_US	1000
 56
 57struct stm32_iwdg {
 58	struct watchdog_device	wdd;
 59	void __iomem		*regs;
 60	struct clk		*clk;
 61	unsigned int		rate;
 62};
 63
 64static inline u32 reg_read(void __iomem *base, u32 reg)
 65{
 66	return readl_relaxed(base + reg);
 67}
 68
 69static inline void reg_write(void __iomem *base, u32 reg, u32 val)
 70{
 71	writel_relaxed(val, base + reg);
 72}
 73
 74static int stm32_iwdg_start(struct watchdog_device *wdd)
 75{
 76	struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
 77	u32 val = FLAG_PVU | FLAG_RVU;
 78	u32 reload;
 79	int ret;
 80
 81	dev_dbg(wdd->parent, "%s\n", __func__);
 82
 83	/* prescaler fixed to 256 */
 84	reload = clamp_t(unsigned int, ((wdd->timeout * wdt->rate) / 256) - 1,
 85			 RLR_MIN, RLR_MAX);
 86
 87	/* enable write access */
 88	reg_write(wdt->regs, IWDG_KR, KR_KEY_EWA);
 89
 90	/* set prescaler & reload registers */
 91	reg_write(wdt->regs, IWDG_PR, PR_256); /* prescaler fix to 256 */
 92	reg_write(wdt->regs, IWDG_RLR, reload);
 93	reg_write(wdt->regs, IWDG_KR, KR_KEY_ENABLE);
 94
 95	/* wait for the registers to be updated (max 100ms) */
 96	ret = readl_relaxed_poll_timeout(wdt->regs + IWDG_SR, val,
 97					 !(val & (FLAG_PVU | FLAG_RVU)),
 98					 SLEEP_US, TIMEOUT_US);
 99	if (ret) {
100		dev_err(wdd->parent,
101			"Fail to set prescaler or reload registers\n");
102		return ret;
103	}
104
105	/* reload watchdog */
106	reg_write(wdt->regs, IWDG_KR, KR_KEY_RELOAD);
107
108	return 0;
109}
110
111static int stm32_iwdg_ping(struct watchdog_device *wdd)
112{
113	struct stm32_iwdg *wdt = watchdog_get_drvdata(wdd);
114
115	dev_dbg(wdd->parent, "%s\n", __func__);
116
117	/* reload watchdog */
118	reg_write(wdt->regs, IWDG_KR, KR_KEY_RELOAD);
119
120	return 0;
121}
122
123static int stm32_iwdg_set_timeout(struct watchdog_device *wdd,
124				  unsigned int timeout)
125{
126	dev_dbg(wdd->parent, "%s timeout: %d sec\n", __func__, timeout);
127
128	wdd->timeout = timeout;
129
130	if (watchdog_active(wdd))
131		return stm32_iwdg_start(wdd);
132
133	return 0;
134}
135
136static const struct watchdog_info stm32_iwdg_info = {
137	.options	= WDIOF_SETTIMEOUT |
138			  WDIOF_MAGICCLOSE |
139			  WDIOF_KEEPALIVEPING,
140	.identity	= "STM32 Independent Watchdog",
141};
142
143static const struct watchdog_ops stm32_iwdg_ops = {
144	.owner		= THIS_MODULE,
145	.start		= stm32_iwdg_start,
146	.ping		= stm32_iwdg_ping,
147	.set_timeout	= stm32_iwdg_set_timeout,
148};
149
150static int stm32_iwdg_probe(struct platform_device *pdev)
151{
152	struct watchdog_device *wdd;
153	struct stm32_iwdg *wdt;
154	struct resource *res;
155	void __iomem *regs;
156	struct clk *clk;
157	int ret;
158
159	/* This is the timer base. */
160	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
161	regs = devm_ioremap_resource(&pdev->dev, res);
162	if (IS_ERR(regs)) {
163		dev_err(&pdev->dev, "Could not get resource\n");
164		return PTR_ERR(regs);
165	}
166
167	clk = devm_clk_get(&pdev->dev, NULL);
168	if (IS_ERR(clk)) {
169		dev_err(&pdev->dev, "Unable to get clock\n");
170		return PTR_ERR(clk);
171	}
172
173	ret = clk_prepare_enable(clk);
174	if (ret) {
175		dev_err(&pdev->dev, "Unable to prepare clock %p\n", clk);
176		return ret;
177	}
178
179	/*
180	 * Allocate our watchdog driver data, which has the
181	 * struct watchdog_device nested within it.
182	 */
183	wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
184	if (!wdt) {
185		ret = -ENOMEM;
186		goto err;
187	}
188
189	/* Initialize struct stm32_iwdg. */
190	wdt->regs = regs;
191	wdt->clk = clk;
192	wdt->rate = clk_get_rate(clk);
193
194	/* Initialize struct watchdog_device. */
195	wdd = &wdt->wdd;
196	wdd->info = &stm32_iwdg_info;
197	wdd->ops = &stm32_iwdg_ops;
198	wdd->min_timeout = ((RLR_MIN + 1) * 256) / wdt->rate;
199	wdd->max_hw_heartbeat_ms = ((RLR_MAX + 1) * 256 * 1000) / wdt->rate;
200	wdd->parent = &pdev->dev;
201
202	watchdog_set_drvdata(wdd, wdt);
203	watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
204
205	ret = watchdog_init_timeout(wdd, 0, &pdev->dev);
206	if (ret)
207		dev_warn(&pdev->dev,
208			 "unable to set timeout value, using default\n");
209
210	ret = watchdog_register_device(wdd);
211	if (ret) {
212		dev_err(&pdev->dev, "failed to register watchdog device\n");
213		goto err;
214	}
215
216	platform_set_drvdata(pdev, wdt);
217
218	return 0;
219err:
220	clk_disable_unprepare(clk);
221
222	return ret;
223}
224
225static int stm32_iwdg_remove(struct platform_device *pdev)
226{
227	struct stm32_iwdg *wdt = platform_get_drvdata(pdev);
228
229	watchdog_unregister_device(&wdt->wdd);
230	clk_disable_unprepare(wdt->clk);
231
232	return 0;
233}
234
235static const struct of_device_id stm32_iwdg_of_match[] = {
236	{ .compatible = "st,stm32-iwdg" },
237	{ /* end node */ }
238};
239MODULE_DEVICE_TABLE(of, stm32_iwdg_of_match);
240
241static struct platform_driver stm32_iwdg_driver = {
242	.probe		= stm32_iwdg_probe,
243	.remove		= stm32_iwdg_remove,
244	.driver = {
245		.name	= "iwdg",
246		.of_match_table = stm32_iwdg_of_match,
247	},
248};
249module_platform_driver(stm32_iwdg_driver);
250
251MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
252MODULE_DESCRIPTION("STMicroelectronics STM32 Independent Watchdog Driver");
253MODULE_LICENSE("GPL v2");