Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  *      Meson Watchdog Driver
0004  *
0005  *      Copyright (c) 2014 Carlo Caione
0006  */
0007 
0008 #include <linux/clk.h>
0009 #include <linux/delay.h>
0010 #include <linux/err.h>
0011 #include <linux/init.h>
0012 #include <linux/io.h>
0013 #include <linux/kernel.h>
0014 #include <linux/module.h>
0015 #include <linux/moduleparam.h>
0016 #include <linux/of.h>
0017 #include <linux/of_device.h>
0018 #include <linux/platform_device.h>
0019 #include <linux/types.h>
0020 #include <linux/watchdog.h>
0021 
0022 #define DRV_NAME        "meson_wdt"
0023 
0024 #define MESON_WDT_TC        0x00
0025 #define MESON_WDT_DC_RESET  (3 << 24)
0026 
0027 #define MESON_WDT_RESET     0x04
0028 
0029 #define MESON_WDT_TIMEOUT   30
0030 #define MESON_WDT_MIN_TIMEOUT   1
0031 
0032 #define MESON_SEC_TO_TC(s, c)   ((s) * (c))
0033 
0034 static bool nowayout = WATCHDOG_NOWAYOUT;
0035 static unsigned int timeout;
0036 
0037 struct meson_wdt_data {
0038     unsigned int enable;
0039     unsigned int terminal_count_mask;
0040     unsigned int count_unit;
0041 };
0042 
0043 static struct meson_wdt_data meson6_wdt_data = {
0044     .enable         = BIT(22),
0045     .terminal_count_mask    = 0x3fffff,
0046     .count_unit     = 100000, /* 10 us */
0047 };
0048 
0049 static struct meson_wdt_data meson8b_wdt_data = {
0050     .enable         = BIT(19),
0051     .terminal_count_mask    = 0xffff,
0052     .count_unit     = 7812, /* 128 us */
0053 };
0054 
0055 struct meson_wdt_dev {
0056     struct watchdog_device wdt_dev;
0057     void __iomem *wdt_base;
0058     const struct meson_wdt_data *data;
0059 };
0060 
0061 static int meson_wdt_restart(struct watchdog_device *wdt_dev,
0062                  unsigned long action, void *data)
0063 {
0064     struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev);
0065     u32 tc_reboot = MESON_WDT_DC_RESET;
0066 
0067     tc_reboot |= meson_wdt->data->enable;
0068 
0069     while (1) {
0070         writel(tc_reboot, meson_wdt->wdt_base + MESON_WDT_TC);
0071         mdelay(5);
0072     }
0073 
0074     return 0;
0075 }
0076 
0077 static int meson_wdt_ping(struct watchdog_device *wdt_dev)
0078 {
0079     struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev);
0080 
0081     writel(0, meson_wdt->wdt_base + MESON_WDT_RESET);
0082 
0083     return 0;
0084 }
0085 
0086 static void meson_wdt_change_timeout(struct watchdog_device *wdt_dev,
0087                      unsigned int timeout)
0088 {
0089     struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev);
0090     u32 reg;
0091 
0092     reg = readl(meson_wdt->wdt_base + MESON_WDT_TC);
0093     reg &= ~meson_wdt->data->terminal_count_mask;
0094     reg |= MESON_SEC_TO_TC(timeout, meson_wdt->data->count_unit);
0095     writel(reg, meson_wdt->wdt_base + MESON_WDT_TC);
0096 }
0097 
0098 static int meson_wdt_set_timeout(struct watchdog_device *wdt_dev,
0099                  unsigned int timeout)
0100 {
0101     wdt_dev->timeout = timeout;
0102 
0103     meson_wdt_change_timeout(wdt_dev, timeout);
0104     meson_wdt_ping(wdt_dev);
0105 
0106     return 0;
0107 }
0108 
0109 static int meson_wdt_stop(struct watchdog_device *wdt_dev)
0110 {
0111     struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev);
0112     u32 reg;
0113 
0114     reg = readl(meson_wdt->wdt_base + MESON_WDT_TC);
0115     reg &= ~meson_wdt->data->enable;
0116     writel(reg, meson_wdt->wdt_base + MESON_WDT_TC);
0117 
0118     return 0;
0119 }
0120 
0121 static int meson_wdt_start(struct watchdog_device *wdt_dev)
0122 {
0123     struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev);
0124     u32 reg;
0125 
0126     meson_wdt_change_timeout(wdt_dev, meson_wdt->wdt_dev.timeout);
0127     meson_wdt_ping(wdt_dev);
0128 
0129     reg = readl(meson_wdt->wdt_base + MESON_WDT_TC);
0130     reg |= meson_wdt->data->enable;
0131     writel(reg, meson_wdt->wdt_base + MESON_WDT_TC);
0132 
0133     return 0;
0134 }
0135 
0136 static const struct watchdog_info meson_wdt_info = {
0137     .identity   = DRV_NAME,
0138     .options    = WDIOF_SETTIMEOUT |
0139               WDIOF_KEEPALIVEPING |
0140               WDIOF_MAGICCLOSE,
0141 };
0142 
0143 static const struct watchdog_ops meson_wdt_ops = {
0144     .owner      = THIS_MODULE,
0145     .start      = meson_wdt_start,
0146     .stop       = meson_wdt_stop,
0147     .ping       = meson_wdt_ping,
0148     .set_timeout    = meson_wdt_set_timeout,
0149     .restart        = meson_wdt_restart,
0150 };
0151 
0152 static const struct of_device_id meson_wdt_dt_ids[] = {
0153     { .compatible = "amlogic,meson6-wdt", .data = &meson6_wdt_data },
0154     { .compatible = "amlogic,meson8-wdt", .data = &meson6_wdt_data },
0155     { .compatible = "amlogic,meson8b-wdt", .data = &meson8b_wdt_data },
0156     { .compatible = "amlogic,meson8m2-wdt", .data = &meson8b_wdt_data },
0157     { /* sentinel */ }
0158 };
0159 MODULE_DEVICE_TABLE(of, meson_wdt_dt_ids);
0160 
0161 static int meson_wdt_probe(struct platform_device *pdev)
0162 {
0163     struct device *dev = &pdev->dev;
0164     struct meson_wdt_dev *meson_wdt;
0165     int err;
0166 
0167     meson_wdt = devm_kzalloc(dev, sizeof(*meson_wdt), GFP_KERNEL);
0168     if (!meson_wdt)
0169         return -ENOMEM;
0170 
0171     meson_wdt->wdt_base = devm_platform_ioremap_resource(pdev, 0);
0172     if (IS_ERR(meson_wdt->wdt_base))
0173         return PTR_ERR(meson_wdt->wdt_base);
0174 
0175     meson_wdt->data = device_get_match_data(dev);
0176 
0177     meson_wdt->wdt_dev.parent = dev;
0178     meson_wdt->wdt_dev.info = &meson_wdt_info;
0179     meson_wdt->wdt_dev.ops = &meson_wdt_ops;
0180     meson_wdt->wdt_dev.max_timeout =
0181         meson_wdt->data->terminal_count_mask / meson_wdt->data->count_unit;
0182     meson_wdt->wdt_dev.min_timeout = MESON_WDT_MIN_TIMEOUT;
0183     meson_wdt->wdt_dev.timeout = min_t(unsigned int,
0184                        MESON_WDT_TIMEOUT,
0185                        meson_wdt->wdt_dev.max_timeout);
0186 
0187     watchdog_set_drvdata(&meson_wdt->wdt_dev, meson_wdt);
0188 
0189     watchdog_init_timeout(&meson_wdt->wdt_dev, timeout, dev);
0190     watchdog_set_nowayout(&meson_wdt->wdt_dev, nowayout);
0191     watchdog_set_restart_priority(&meson_wdt->wdt_dev, 128);
0192 
0193     meson_wdt_stop(&meson_wdt->wdt_dev);
0194 
0195     watchdog_stop_on_reboot(&meson_wdt->wdt_dev);
0196     err = devm_watchdog_register_device(dev, &meson_wdt->wdt_dev);
0197     if (err)
0198         return err;
0199 
0200     dev_info(dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)",
0201          meson_wdt->wdt_dev.timeout, nowayout);
0202 
0203     return 0;
0204 }
0205 
0206 static struct platform_driver meson_wdt_driver = {
0207     .probe      = meson_wdt_probe,
0208     .driver     = {
0209         .name       = DRV_NAME,
0210         .of_match_table = meson_wdt_dt_ids,
0211     },
0212 };
0213 
0214 module_platform_driver(meson_wdt_driver);
0215 
0216 module_param(timeout, uint, 0);
0217 MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds");
0218 
0219 module_param(nowayout, bool, 0);
0220 MODULE_PARM_DESC(nowayout,
0221          "Watchdog cannot be stopped once started (default="
0222          __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0223 
0224 MODULE_LICENSE("GPL");
0225 MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
0226 MODULE_DESCRIPTION("Meson Watchdog Timer Driver");