brintos

brintos / linux-shallow public Read only

0
0
Text · 5.2 KiB · 1d282de Raw
229 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * PIC32 watchdog driver4 *5 * Joshua Henderson <joshua.henderson@microchip.com>6 * Copyright (c) 2016, Microchip Technology Inc.7 */8#include <linux/clk.h>9#include <linux/device.h>10#include <linux/err.h>11#include <linux/io.h>12#include <linux/kernel.h>13#include <linux/module.h>14#include <linux/of.h>15#include <linux/platform_device.h>16#include <linux/pm.h>17#include <linux/watchdog.h>18 19#include <asm/mach-pic32/pic32.h>20 21/* Watchdog Timer Registers */22#define WDTCON_REG		0x0023 24/* Watchdog Timer Control Register fields */25#define WDTCON_WIN_EN		BIT(0)26#define WDTCON_RMCS_MASK	0x000327#define WDTCON_RMCS_SHIFT	0x000628#define WDTCON_RMPS_MASK	0x001F29#define WDTCON_RMPS_SHIFT	0x000830#define WDTCON_ON		BIT(15)31#define WDTCON_CLR_KEY		0x574332 33/* Reset Control Register fields for watchdog */34#define RESETCON_TIMEOUT_IDLE	BIT(2)35#define RESETCON_TIMEOUT_SLEEP	BIT(3)36#define RESETCON_WDT_TIMEOUT	BIT(4)37 38struct pic32_wdt {39	void __iomem	*regs;40	void __iomem	*rst_base;41	struct clk	*clk;42};43 44static inline bool pic32_wdt_is_win_enabled(struct pic32_wdt *wdt)45{46	return !!(readl(wdt->regs + WDTCON_REG) & WDTCON_WIN_EN);47}48 49static inline u32 pic32_wdt_get_post_scaler(struct pic32_wdt *wdt)50{51	u32 v = readl(wdt->regs + WDTCON_REG);52 53	return (v >> WDTCON_RMPS_SHIFT) & WDTCON_RMPS_MASK;54}55 56static inline u32 pic32_wdt_get_clk_id(struct pic32_wdt *wdt)57{58	u32 v = readl(wdt->regs + WDTCON_REG);59 60	return (v >> WDTCON_RMCS_SHIFT) & WDTCON_RMCS_MASK;61}62 63static int pic32_wdt_bootstatus(struct pic32_wdt *wdt)64{65	u32 v = readl(wdt->rst_base);66 67	writel(RESETCON_WDT_TIMEOUT, PIC32_CLR(wdt->rst_base));68 69	return v & RESETCON_WDT_TIMEOUT;70}71 72static u32 pic32_wdt_get_timeout_secs(struct pic32_wdt *wdt, struct device *dev)73{74	unsigned long rate;75	u32 period, ps, terminal;76 77	rate = clk_get_rate(wdt->clk);78 79	dev_dbg(dev, "wdt: clk_id %d, clk_rate %lu (prescale)\n",80		pic32_wdt_get_clk_id(wdt), rate);81 82	/* default, prescaler of 32 (i.e. div-by-32) is implicit. */83	rate >>= 5;84	if (!rate)85		return 0;86 87	/* calculate terminal count from postscaler. */88	ps = pic32_wdt_get_post_scaler(wdt);89	terminal = BIT(ps);90 91	/* find time taken (in secs) to reach terminal count */92	period = terminal / rate;93	dev_dbg(dev,94		"wdt: clk_rate %lu (postscale) / terminal %d, timeout %dsec\n",95		rate, terminal, period);96 97	return period;98}99 100static void pic32_wdt_keepalive(struct pic32_wdt *wdt)101{102	/* write key through single half-word */103	writew(WDTCON_CLR_KEY, wdt->regs + WDTCON_REG + 2);104}105 106static int pic32_wdt_start(struct watchdog_device *wdd)107{108	struct pic32_wdt *wdt = watchdog_get_drvdata(wdd);109 110	writel(WDTCON_ON, PIC32_SET(wdt->regs + WDTCON_REG));111	pic32_wdt_keepalive(wdt);112 113	return 0;114}115 116static int pic32_wdt_stop(struct watchdog_device *wdd)117{118	struct pic32_wdt *wdt = watchdog_get_drvdata(wdd);119 120	writel(WDTCON_ON, PIC32_CLR(wdt->regs + WDTCON_REG));121 122	/*123	 * Cannot touch registers in the CPU cycle following clearing the124	 * ON bit.125	 */126	nop();127 128	return 0;129}130 131static int pic32_wdt_ping(struct watchdog_device *wdd)132{133	struct pic32_wdt *wdt = watchdog_get_drvdata(wdd);134 135	pic32_wdt_keepalive(wdt);136 137	return 0;138}139 140static const struct watchdog_ops pic32_wdt_fops = {141	.owner		= THIS_MODULE,142	.start		= pic32_wdt_start,143	.stop		= pic32_wdt_stop,144	.ping		= pic32_wdt_ping,145};146 147static const struct watchdog_info pic32_wdt_ident = {148	.options = WDIOF_KEEPALIVEPING |149			WDIOF_MAGICCLOSE | WDIOF_CARDRESET,150	.identity = "PIC32 Watchdog",151};152 153static struct watchdog_device pic32_wdd = {154	.info		= &pic32_wdt_ident,155	.ops		= &pic32_wdt_fops,156};157 158static const struct of_device_id pic32_wdt_dt_ids[] = {159	{ .compatible = "microchip,pic32mzda-wdt", },160	{ /* sentinel */ }161};162MODULE_DEVICE_TABLE(of, pic32_wdt_dt_ids);163 164static int pic32_wdt_drv_probe(struct platform_device *pdev)165{166	struct device *dev = &pdev->dev;167	int ret;168	struct watchdog_device *wdd = &pic32_wdd;169	struct pic32_wdt *wdt;170 171	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);172	if (!wdt)173		return -ENOMEM;174 175	wdt->regs = devm_platform_ioremap_resource(pdev, 0);176	if (IS_ERR(wdt->regs))177		return PTR_ERR(wdt->regs);178 179	wdt->rst_base = devm_ioremap(dev, PIC32_BASE_RESET, 0x10);180	if (!wdt->rst_base)181		return -ENOMEM;182 183	wdt->clk = devm_clk_get_enabled(dev, NULL);184	if (IS_ERR(wdt->clk)) {185		dev_err(dev, "clk not found\n");186		return PTR_ERR(wdt->clk);187	}188 189	if (pic32_wdt_is_win_enabled(wdt)) {190		dev_err(dev, "windowed-clear mode is not supported.\n");191		return -ENODEV;192	}193 194	wdd->timeout = pic32_wdt_get_timeout_secs(wdt, dev);195	if (!wdd->timeout) {196		dev_err(dev, "failed to read watchdog register timeout\n");197		return -EINVAL;198	}199 200	dev_info(dev, "timeout %d\n", wdd->timeout);201 202	wdd->bootstatus = pic32_wdt_bootstatus(wdt) ? WDIOF_CARDRESET : 0;203 204	watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);205	watchdog_set_drvdata(wdd, wdt);206 207	ret = devm_watchdog_register_device(dev, wdd);208	if (ret)209		return ret;210 211	platform_set_drvdata(pdev, wdd);212 213	return 0;214}215 216static struct platform_driver pic32_wdt_driver = {217	.probe		= pic32_wdt_drv_probe,218	.driver		= {219		.name		= "pic32-wdt",220		.of_match_table = of_match_ptr(pic32_wdt_dt_ids),221	}222};223 224module_platform_driver(pic32_wdt_driver);225 226MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>");227MODULE_DESCRIPTION("Microchip PIC32 Watchdog Timer");228MODULE_LICENSE("GPL");229