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

meson_wdt.c (5827B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *      Meson Watchdog Driver
      4 *
      5 *      Copyright (c) 2014 Carlo Caione
      6 */
      7
      8#include <linux/clk.h>
      9#include <linux/delay.h>
     10#include <linux/err.h>
     11#include <linux/init.h>
     12#include <linux/io.h>
     13#include <linux/kernel.h>
     14#include <linux/module.h>
     15#include <linux/moduleparam.h>
     16#include <linux/of.h>
     17#include <linux/of_device.h>
     18#include <linux/platform_device.h>
     19#include <linux/types.h>
     20#include <linux/watchdog.h>
     21
     22#define DRV_NAME		"meson_wdt"
     23
     24#define MESON_WDT_TC		0x00
     25#define MESON_WDT_DC_RESET	(3 << 24)
     26
     27#define MESON_WDT_RESET		0x04
     28
     29#define MESON_WDT_TIMEOUT	30
     30#define MESON_WDT_MIN_TIMEOUT	1
     31
     32#define MESON_SEC_TO_TC(s, c)	((s) * (c))
     33
     34static bool nowayout = WATCHDOG_NOWAYOUT;
     35static unsigned int timeout;
     36
     37struct meson_wdt_data {
     38	unsigned int enable;
     39	unsigned int terminal_count_mask;
     40	unsigned int count_unit;
     41};
     42
     43static struct meson_wdt_data meson6_wdt_data = {
     44	.enable			= BIT(22),
     45	.terminal_count_mask	= 0x3fffff,
     46	.count_unit		= 100000, /* 10 us */
     47};
     48
     49static struct meson_wdt_data meson8b_wdt_data = {
     50	.enable			= BIT(19),
     51	.terminal_count_mask	= 0xffff,
     52	.count_unit		= 7812, /* 128 us */
     53};
     54
     55struct meson_wdt_dev {
     56	struct watchdog_device wdt_dev;
     57	void __iomem *wdt_base;
     58	const struct meson_wdt_data *data;
     59};
     60
     61static int meson_wdt_restart(struct watchdog_device *wdt_dev,
     62			     unsigned long action, void *data)
     63{
     64	struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev);
     65	u32 tc_reboot = MESON_WDT_DC_RESET;
     66
     67	tc_reboot |= meson_wdt->data->enable;
     68
     69	while (1) {
     70		writel(tc_reboot, meson_wdt->wdt_base + MESON_WDT_TC);
     71		mdelay(5);
     72	}
     73
     74	return 0;
     75}
     76
     77static int meson_wdt_ping(struct watchdog_device *wdt_dev)
     78{
     79	struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev);
     80
     81	writel(0, meson_wdt->wdt_base + MESON_WDT_RESET);
     82
     83	return 0;
     84}
     85
     86static void meson_wdt_change_timeout(struct watchdog_device *wdt_dev,
     87				     unsigned int timeout)
     88{
     89	struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev);
     90	u32 reg;
     91
     92	reg = readl(meson_wdt->wdt_base + MESON_WDT_TC);
     93	reg &= ~meson_wdt->data->terminal_count_mask;
     94	reg |= MESON_SEC_TO_TC(timeout, meson_wdt->data->count_unit);
     95	writel(reg, meson_wdt->wdt_base + MESON_WDT_TC);
     96}
     97
     98static int meson_wdt_set_timeout(struct watchdog_device *wdt_dev,
     99				 unsigned int timeout)
    100{
    101	wdt_dev->timeout = timeout;
    102
    103	meson_wdt_change_timeout(wdt_dev, timeout);
    104	meson_wdt_ping(wdt_dev);
    105
    106	return 0;
    107}
    108
    109static int meson_wdt_stop(struct watchdog_device *wdt_dev)
    110{
    111	struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev);
    112	u32 reg;
    113
    114	reg = readl(meson_wdt->wdt_base + MESON_WDT_TC);
    115	reg &= ~meson_wdt->data->enable;
    116	writel(reg, meson_wdt->wdt_base + MESON_WDT_TC);
    117
    118	return 0;
    119}
    120
    121static int meson_wdt_start(struct watchdog_device *wdt_dev)
    122{
    123	struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev);
    124	u32 reg;
    125
    126	meson_wdt_change_timeout(wdt_dev, meson_wdt->wdt_dev.timeout);
    127	meson_wdt_ping(wdt_dev);
    128
    129	reg = readl(meson_wdt->wdt_base + MESON_WDT_TC);
    130	reg |= meson_wdt->data->enable;
    131	writel(reg, meson_wdt->wdt_base + MESON_WDT_TC);
    132
    133	return 0;
    134}
    135
    136static const struct watchdog_info meson_wdt_info = {
    137	.identity	= DRV_NAME,
    138	.options	= WDIOF_SETTIMEOUT |
    139			  WDIOF_KEEPALIVEPING |
    140			  WDIOF_MAGICCLOSE,
    141};
    142
    143static const struct watchdog_ops meson_wdt_ops = {
    144	.owner		= THIS_MODULE,
    145	.start		= meson_wdt_start,
    146	.stop		= meson_wdt_stop,
    147	.ping		= meson_wdt_ping,
    148	.set_timeout	= meson_wdt_set_timeout,
    149	.restart        = meson_wdt_restart,
    150};
    151
    152static const struct of_device_id meson_wdt_dt_ids[] = {
    153	{ .compatible = "amlogic,meson6-wdt", .data = &meson6_wdt_data },
    154	{ .compatible = "amlogic,meson8-wdt", .data = &meson6_wdt_data },
    155	{ .compatible = "amlogic,meson8b-wdt", .data = &meson8b_wdt_data },
    156	{ .compatible = "amlogic,meson8m2-wdt", .data = &meson8b_wdt_data },
    157	{ /* sentinel */ }
    158};
    159MODULE_DEVICE_TABLE(of, meson_wdt_dt_ids);
    160
    161static int meson_wdt_probe(struct platform_device *pdev)
    162{
    163	struct device *dev = &pdev->dev;
    164	struct meson_wdt_dev *meson_wdt;
    165	int err;
    166
    167	meson_wdt = devm_kzalloc(dev, sizeof(*meson_wdt), GFP_KERNEL);
    168	if (!meson_wdt)
    169		return -ENOMEM;
    170
    171	meson_wdt->wdt_base = devm_platform_ioremap_resource(pdev, 0);
    172	if (IS_ERR(meson_wdt->wdt_base))
    173		return PTR_ERR(meson_wdt->wdt_base);
    174
    175	meson_wdt->data = device_get_match_data(dev);
    176
    177	meson_wdt->wdt_dev.parent = dev;
    178	meson_wdt->wdt_dev.info = &meson_wdt_info;
    179	meson_wdt->wdt_dev.ops = &meson_wdt_ops;
    180	meson_wdt->wdt_dev.max_timeout =
    181		meson_wdt->data->terminal_count_mask / meson_wdt->data->count_unit;
    182	meson_wdt->wdt_dev.min_timeout = MESON_WDT_MIN_TIMEOUT;
    183	meson_wdt->wdt_dev.timeout = min_t(unsigned int,
    184					   MESON_WDT_TIMEOUT,
    185					   meson_wdt->wdt_dev.max_timeout);
    186
    187	watchdog_set_drvdata(&meson_wdt->wdt_dev, meson_wdt);
    188
    189	watchdog_init_timeout(&meson_wdt->wdt_dev, timeout, dev);
    190	watchdog_set_nowayout(&meson_wdt->wdt_dev, nowayout);
    191	watchdog_set_restart_priority(&meson_wdt->wdt_dev, 128);
    192
    193	meson_wdt_stop(&meson_wdt->wdt_dev);
    194
    195	watchdog_stop_on_reboot(&meson_wdt->wdt_dev);
    196	err = devm_watchdog_register_device(dev, &meson_wdt->wdt_dev);
    197	if (err)
    198		return err;
    199
    200	dev_info(dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)",
    201		 meson_wdt->wdt_dev.timeout, nowayout);
    202
    203	return 0;
    204}
    205
    206static struct platform_driver meson_wdt_driver = {
    207	.probe		= meson_wdt_probe,
    208	.driver		= {
    209		.name		= DRV_NAME,
    210		.of_match_table	= meson_wdt_dt_ids,
    211	},
    212};
    213
    214module_platform_driver(meson_wdt_driver);
    215
    216module_param(timeout, uint, 0);
    217MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds");
    218
    219module_param(nowayout, bool, 0);
    220MODULE_PARM_DESC(nowayout,
    221		 "Watchdog cannot be stopped once started (default="
    222		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
    223
    224MODULE_LICENSE("GPL");
    225MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
    226MODULE_DESCRIPTION("Meson Watchdog Timer Driver");