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-wdt.c (5644B)


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