cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

pic32-dmt.c (5207B)


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