root/drivers/watchdog/meson_wdt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. meson_wdt_restart
  2. meson_wdt_ping
  3. meson_wdt_change_timeout
  4. meson_wdt_set_timeout
  5. meson_wdt_stop
  6. meson_wdt_start
  7. meson_wdt_probe

   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 
  34 static bool nowayout = WATCHDOG_NOWAYOUT;
  35 static unsigned int timeout;
  36 
  37 struct meson_wdt_data {
  38         unsigned int enable;
  39         unsigned int terminal_count_mask;
  40         unsigned int count_unit;
  41 };
  42 
  43 static struct meson_wdt_data meson6_wdt_data = {
  44         .enable                 = BIT(22),
  45         .terminal_count_mask    = 0x3fffff,
  46         .count_unit             = 100000, /* 10 us */
  47 };
  48 
  49 static struct meson_wdt_data meson8b_wdt_data = {
  50         .enable                 = BIT(19),
  51         .terminal_count_mask    = 0xffff,
  52         .count_unit             = 7812, /* 128 us */
  53 };
  54 
  55 struct meson_wdt_dev {
  56         struct watchdog_device wdt_dev;
  57         void __iomem *wdt_base;
  58         const struct meson_wdt_data *data;
  59 };
  60 
  61 static 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 
  77 static 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 
  86 static 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 
  98 static 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 
 109 static 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 
 121 static 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 
 136 static const struct watchdog_info meson_wdt_info = {
 137         .identity       = DRV_NAME,
 138         .options        = WDIOF_SETTIMEOUT |
 139                           WDIOF_KEEPALIVEPING |
 140                           WDIOF_MAGICCLOSE,
 141 };
 142 
 143 static 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 
 152 static 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 };
 159 MODULE_DEVICE_TABLE(of, meson_wdt_dt_ids);
 160 
 161 static int meson_wdt_probe(struct platform_device *pdev)
 162 {
 163         struct device *dev = &pdev->dev;
 164         struct meson_wdt_dev *meson_wdt;
 165         const struct of_device_id *of_id;
 166         int err;
 167 
 168         meson_wdt = devm_kzalloc(dev, sizeof(*meson_wdt), GFP_KERNEL);
 169         if (!meson_wdt)
 170                 return -ENOMEM;
 171 
 172         meson_wdt->wdt_base = devm_platform_ioremap_resource(pdev, 0);
 173         if (IS_ERR(meson_wdt->wdt_base))
 174                 return PTR_ERR(meson_wdt->wdt_base);
 175 
 176         of_id = of_match_device(meson_wdt_dt_ids, dev);
 177         if (!of_id) {
 178                 dev_err(dev, "Unable to initialize WDT data\n");
 179                 return -ENODEV;
 180         }
 181         meson_wdt->data = of_id->data;
 182 
 183         meson_wdt->wdt_dev.parent = dev;
 184         meson_wdt->wdt_dev.info = &meson_wdt_info;
 185         meson_wdt->wdt_dev.ops = &meson_wdt_ops;
 186         meson_wdt->wdt_dev.max_timeout =
 187                 meson_wdt->data->terminal_count_mask / meson_wdt->data->count_unit;
 188         meson_wdt->wdt_dev.min_timeout = MESON_WDT_MIN_TIMEOUT;
 189         meson_wdt->wdt_dev.timeout = min_t(unsigned int,
 190                                            MESON_WDT_TIMEOUT,
 191                                            meson_wdt->wdt_dev.max_timeout);
 192 
 193         watchdog_set_drvdata(&meson_wdt->wdt_dev, meson_wdt);
 194 
 195         watchdog_init_timeout(&meson_wdt->wdt_dev, timeout, dev);
 196         watchdog_set_nowayout(&meson_wdt->wdt_dev, nowayout);
 197         watchdog_set_restart_priority(&meson_wdt->wdt_dev, 128);
 198 
 199         meson_wdt_stop(&meson_wdt->wdt_dev);
 200 
 201         watchdog_stop_on_reboot(&meson_wdt->wdt_dev);
 202         err = devm_watchdog_register_device(dev, &meson_wdt->wdt_dev);
 203         if (err)
 204                 return err;
 205 
 206         dev_info(dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)",
 207                  meson_wdt->wdt_dev.timeout, nowayout);
 208 
 209         return 0;
 210 }
 211 
 212 static struct platform_driver meson_wdt_driver = {
 213         .probe          = meson_wdt_probe,
 214         .driver         = {
 215                 .name           = DRV_NAME,
 216                 .of_match_table = meson_wdt_dt_ids,
 217         },
 218 };
 219 
 220 module_platform_driver(meson_wdt_driver);
 221 
 222 module_param(timeout, uint, 0);
 223 MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds");
 224 
 225 module_param(nowayout, bool, 0);
 226 MODULE_PARM_DESC(nowayout,
 227                  "Watchdog cannot be stopped once started (default="
 228                  __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 229 
 230 MODULE_LICENSE("GPL");
 231 MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
 232 MODULE_DESCRIPTION("Meson Watchdog Timer Driver");

/* [<][>][^][v][top][bottom][index][help] */