Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * txx9wdt: A Hardware Watchdog Driver for TXx9 SoCs
0004  *
0005  * Copyright (C) 2007 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
0006  */
0007 
0008 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0009 
0010 #include <linux/module.h>
0011 #include <linux/moduleparam.h>
0012 #include <linux/types.h>
0013 #include <linux/watchdog.h>
0014 #include <linux/init.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/clk.h>
0017 #include <linux/err.h>
0018 #include <linux/io.h>
0019 #include <asm/txx9tmr.h>
0020 
0021 #define WD_TIMER_CCD    7       /* 1/256 */
0022 #define WD_TIMER_CLK    (clk_get_rate(txx9_imclk) / (2 << WD_TIMER_CCD))
0023 #define WD_MAX_TIMEOUT  ((0xffffffff >> (32 - TXX9_TIMER_BITS)) / WD_TIMER_CLK)
0024 #define TIMER_MARGIN    60      /* Default is 60 seconds */
0025 
0026 static unsigned int timeout = TIMER_MARGIN; /* in seconds */
0027 module_param(timeout, uint, 0);
0028 MODULE_PARM_DESC(timeout,
0029     "Watchdog timeout in seconds. "
0030     "(0<timeout<((2^" __MODULE_STRING(TXX9_TIMER_BITS) ")/(IMCLK/256)), "
0031     "default=" __MODULE_STRING(TIMER_MARGIN) ")");
0032 
0033 static bool nowayout = WATCHDOG_NOWAYOUT;
0034 module_param(nowayout, bool, 0);
0035 MODULE_PARM_DESC(nowayout,
0036     "Watchdog cannot be stopped once started "
0037     "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0038 
0039 static struct txx9_tmr_reg __iomem *txx9wdt_reg;
0040 static struct clk *txx9_imclk;
0041 static DEFINE_SPINLOCK(txx9_lock);
0042 
0043 static int txx9wdt_ping(struct watchdog_device *wdt_dev)
0044 {
0045     spin_lock(&txx9_lock);
0046     __raw_writel(TXx9_TMWTMR_TWIE | TXx9_TMWTMR_TWC, &txx9wdt_reg->wtmr);
0047     spin_unlock(&txx9_lock);
0048     return 0;
0049 }
0050 
0051 static int txx9wdt_start(struct watchdog_device *wdt_dev)
0052 {
0053     spin_lock(&txx9_lock);
0054     __raw_writel(WD_TIMER_CLK * wdt_dev->timeout, &txx9wdt_reg->cpra);
0055     __raw_writel(WD_TIMER_CCD, &txx9wdt_reg->ccdr);
0056     __raw_writel(0, &txx9wdt_reg->tisr);    /* clear pending interrupt */
0057     __raw_writel(TXx9_TMTCR_TCE | TXx9_TMTCR_CCDE | TXx9_TMTCR_TMODE_WDOG,
0058              &txx9wdt_reg->tcr);
0059     __raw_writel(TXx9_TMWTMR_TWIE | TXx9_TMWTMR_TWC, &txx9wdt_reg->wtmr);
0060     spin_unlock(&txx9_lock);
0061     return 0;
0062 }
0063 
0064 static int txx9wdt_stop(struct watchdog_device *wdt_dev)
0065 {
0066     spin_lock(&txx9_lock);
0067     __raw_writel(TXx9_TMWTMR_WDIS, &txx9wdt_reg->wtmr);
0068     __raw_writel(__raw_readl(&txx9wdt_reg->tcr) & ~TXx9_TMTCR_TCE,
0069              &txx9wdt_reg->tcr);
0070     spin_unlock(&txx9_lock);
0071     return 0;
0072 }
0073 
0074 static int txx9wdt_set_timeout(struct watchdog_device *wdt_dev,
0075                    unsigned int new_timeout)
0076 {
0077     wdt_dev->timeout = new_timeout;
0078     txx9wdt_stop(wdt_dev);
0079     txx9wdt_start(wdt_dev);
0080     return 0;
0081 }
0082 
0083 static const struct watchdog_info txx9wdt_info = {
0084     .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
0085     .identity = "Hardware Watchdog for TXx9",
0086 };
0087 
0088 static const struct watchdog_ops txx9wdt_ops = {
0089     .owner = THIS_MODULE,
0090     .start = txx9wdt_start,
0091     .stop = txx9wdt_stop,
0092     .ping = txx9wdt_ping,
0093     .set_timeout = txx9wdt_set_timeout,
0094 };
0095 
0096 static struct watchdog_device txx9wdt = {
0097     .info = &txx9wdt_info,
0098     .ops = &txx9wdt_ops,
0099 };
0100 
0101 static int __init txx9wdt_probe(struct platform_device *dev)
0102 {
0103     int ret;
0104 
0105     txx9_imclk = clk_get(NULL, "imbus_clk");
0106     if (IS_ERR(txx9_imclk)) {
0107         ret = PTR_ERR(txx9_imclk);
0108         txx9_imclk = NULL;
0109         goto exit;
0110     }
0111     ret = clk_prepare_enable(txx9_imclk);
0112     if (ret) {
0113         clk_put(txx9_imclk);
0114         txx9_imclk = NULL;
0115         goto exit;
0116     }
0117 
0118     txx9wdt_reg = devm_platform_ioremap_resource(dev, 0);
0119     if (IS_ERR(txx9wdt_reg)) {
0120         ret = PTR_ERR(txx9wdt_reg);
0121         goto exit;
0122     }
0123 
0124     if (timeout < 1 || timeout > WD_MAX_TIMEOUT)
0125         timeout = TIMER_MARGIN;
0126     txx9wdt.timeout = timeout;
0127     txx9wdt.min_timeout = 1;
0128     txx9wdt.max_timeout = WD_MAX_TIMEOUT;
0129     txx9wdt.parent = &dev->dev;
0130     watchdog_set_nowayout(&txx9wdt, nowayout);
0131 
0132     ret = watchdog_register_device(&txx9wdt);
0133     if (ret)
0134         goto exit;
0135 
0136     pr_info("Hardware Watchdog Timer: timeout=%d sec (max %ld) (nowayout= %d)\n",
0137         timeout, WD_MAX_TIMEOUT, nowayout);
0138 
0139     return 0;
0140 exit:
0141     if (txx9_imclk) {
0142         clk_disable_unprepare(txx9_imclk);
0143         clk_put(txx9_imclk);
0144     }
0145     return ret;
0146 }
0147 
0148 static int __exit txx9wdt_remove(struct platform_device *dev)
0149 {
0150     watchdog_unregister_device(&txx9wdt);
0151     clk_disable_unprepare(txx9_imclk);
0152     clk_put(txx9_imclk);
0153     return 0;
0154 }
0155 
0156 static void txx9wdt_shutdown(struct platform_device *dev)
0157 {
0158     txx9wdt_stop(&txx9wdt);
0159 }
0160 
0161 static struct platform_driver txx9wdt_driver = {
0162     .remove = __exit_p(txx9wdt_remove),
0163     .shutdown = txx9wdt_shutdown,
0164     .driver = {
0165         .name = "txx9wdt",
0166     },
0167 };
0168 
0169 module_platform_driver_probe(txx9wdt_driver, txx9wdt_probe);
0170 
0171 MODULE_DESCRIPTION("TXx9 Watchdog Driver");
0172 MODULE_LICENSE("GPL");
0173 MODULE_ALIAS("platform:txx9wdt");