brintos

brintos / linux-shallow public Read only

0
0
Text · 4.8 KiB · ab06824 Raw
227 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * PIC32 deadman timer driver4 *5 * Purna Chandra Mandal <purna.mandal@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/* Deadman Timer Regs */22#define DMTCON_REG	0x0023#define DMTPRECLR_REG	0x1024#define DMTCLR_REG	0x2025#define DMTSTAT_REG	0x3026#define DMTCNT_REG	0x4027#define DMTPSCNT_REG	0x6028#define DMTPSINTV_REG	0x7029 30/* Deadman Timer Regs fields */31#define DMT_ON			BIT(15)32#define DMT_STEP1_KEY		BIT(6)33#define DMT_STEP2_KEY		BIT(3)34#define DMTSTAT_WINOPN		BIT(0)35#define DMTSTAT_EVENT		BIT(5)36#define DMTSTAT_BAD2		BIT(6)37#define DMTSTAT_BAD1		BIT(7)38 39/* Reset Control Register fields for watchdog */40#define RESETCON_DMT_TIMEOUT	BIT(5)41 42struct pic32_dmt {43	void __iomem	*regs;44	struct clk	*clk;45};46 47static inline void dmt_enable(struct pic32_dmt *dmt)48{49	writel(DMT_ON, PIC32_SET(dmt->regs + DMTCON_REG));50}51 52static inline void dmt_disable(struct pic32_dmt *dmt)53{54	writel(DMT_ON, PIC32_CLR(dmt->regs + DMTCON_REG));55	/*56	 * Cannot touch registers in the CPU cycle following clearing the57	 * ON bit.58	 */59	nop();60}61 62static inline int dmt_bad_status(struct pic32_dmt *dmt)63{64	u32 val;65 66	val = readl(dmt->regs + DMTSTAT_REG);67	val &= (DMTSTAT_BAD1 | DMTSTAT_BAD2 | DMTSTAT_EVENT);68	if (val)69		return -EAGAIN;70 71	return 0;72}73 74static inline int dmt_keepalive(struct pic32_dmt *dmt)75{76	u32 v;77	u32 timeout = 500;78 79	/* set pre-clear key */80	writel(DMT_STEP1_KEY << 8, dmt->regs + DMTPRECLR_REG);81 82	/* wait for DMT window to open */83	while (--timeout) {84		v = readl(dmt->regs + DMTSTAT_REG) & DMTSTAT_WINOPN;85		if (v == DMTSTAT_WINOPN)86			break;87	}88 89	/* apply key2 */90	writel(DMT_STEP2_KEY, dmt->regs + DMTCLR_REG);91 92	/* check whether keys are latched correctly */93	return dmt_bad_status(dmt);94}95 96static inline u32 pic32_dmt_get_timeout_secs(struct pic32_dmt *dmt)97{98	unsigned long rate;99 100	rate = clk_get_rate(dmt->clk);101	if (rate)102		return readl(dmt->regs + DMTPSCNT_REG) / rate;103 104	return 0;105}106 107static inline u32 pic32_dmt_bootstatus(struct pic32_dmt *dmt)108{109	u32 v;110	void __iomem *rst_base;111 112	rst_base = ioremap(PIC32_BASE_RESET, 0x10);113	if (!rst_base)114		return 0;115 116	v = readl(rst_base);117 118	writel(RESETCON_DMT_TIMEOUT, PIC32_CLR(rst_base));119 120	iounmap(rst_base);121	return v & RESETCON_DMT_TIMEOUT;122}123 124static int pic32_dmt_start(struct watchdog_device *wdd)125{126	struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);127 128	dmt_enable(dmt);129	return dmt_keepalive(dmt);130}131 132static int pic32_dmt_stop(struct watchdog_device *wdd)133{134	struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);135 136	dmt_disable(dmt);137 138	return 0;139}140 141static int pic32_dmt_ping(struct watchdog_device *wdd)142{143	struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);144 145	return dmt_keepalive(dmt);146}147 148static const struct watchdog_ops pic32_dmt_fops = {149	.owner		= THIS_MODULE,150	.start		= pic32_dmt_start,151	.stop		= pic32_dmt_stop,152	.ping		= pic32_dmt_ping,153};154 155static const struct watchdog_info pic32_dmt_ident = {156	.options	= WDIOF_KEEPALIVEPING |157			  WDIOF_MAGICCLOSE,158	.identity	= "PIC32 Deadman Timer",159};160 161static struct watchdog_device pic32_dmt_wdd = {162	.info		= &pic32_dmt_ident,163	.ops		= &pic32_dmt_fops,164};165 166static int pic32_dmt_probe(struct platform_device *pdev)167{168	struct device *dev = &pdev->dev;169	int ret;170	struct pic32_dmt *dmt;171	struct watchdog_device *wdd = &pic32_dmt_wdd;172 173	dmt = devm_kzalloc(dev, sizeof(*dmt), GFP_KERNEL);174	if (!dmt)175		return -ENOMEM;176 177	dmt->regs = devm_platform_ioremap_resource(pdev, 0);178	if (IS_ERR(dmt->regs))179		return PTR_ERR(dmt->regs);180 181	dmt->clk = devm_clk_get_enabled(dev, NULL);182	if (IS_ERR(dmt->clk)) {183		dev_err(dev, "clk not found\n");184		return PTR_ERR(dmt->clk);185	}186 187	wdd->timeout = pic32_dmt_get_timeout_secs(dmt);188	if (!wdd->timeout) {189		dev_err(dev, "failed to read watchdog register timeout\n");190		return -EINVAL;191	}192 193	dev_info(dev, "timeout %d\n", wdd->timeout);194 195	wdd->bootstatus = pic32_dmt_bootstatus(dmt) ? WDIOF_CARDRESET : 0;196 197	watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);198	watchdog_set_drvdata(wdd, dmt);199 200	ret = devm_watchdog_register_device(dev, wdd);201	if (ret)202		return ret;203 204	platform_set_drvdata(pdev, wdd);205	return 0;206}207 208static const struct of_device_id pic32_dmt_of_ids[] = {209	{ .compatible = "microchip,pic32mzda-dmt",},210	{ /* sentinel */ }211};212MODULE_DEVICE_TABLE(of, pic32_dmt_of_ids);213 214static struct platform_driver pic32_dmt_driver = {215	.probe		= pic32_dmt_probe,216	.driver		= {217		.name		= "pic32-dmt",218		.of_match_table = of_match_ptr(pic32_dmt_of_ids),219	}220};221 222module_platform_driver(pic32_dmt_driver);223 224MODULE_AUTHOR("Purna Chandra Mandal <purna.mandal@microchip.com>");225MODULE_DESCRIPTION("Microchip PIC32 DMT Driver");226MODULE_LICENSE("GPL");227