root/drivers/watchdog/meson_gxbb_wdt.c

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

DEFINITIONS

This source file includes following definitions.
  1. meson_gxbb_wdt_start
  2. meson_gxbb_wdt_stop
  3. meson_gxbb_wdt_ping
  4. meson_gxbb_wdt_set_timeout
  5. meson_gxbb_wdt_get_timeleft
  6. meson_gxbb_wdt_resume
  7. meson_gxbb_wdt_suspend
  8. meson_clk_disable_unprepare
  9. meson_gxbb_wdt_probe

   1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2 /*
   3  * Copyright (c) 2016 BayLibre, SAS.
   4  * Author: Neil Armstrong <narmstrong@baylibre.com>
   5  *
   6  */
   7 #include <linux/clk.h>
   8 #include <linux/err.h>
   9 #include <linux/io.h>
  10 #include <linux/module.h>
  11 #include <linux/of.h>
  12 #include <linux/platform_device.h>
  13 #include <linux/slab.h>
  14 #include <linux/types.h>
  15 #include <linux/watchdog.h>
  16 
  17 #define DEFAULT_TIMEOUT 30      /* seconds */
  18 
  19 #define GXBB_WDT_CTRL_REG                       0x0
  20 #define GXBB_WDT_TCNT_REG                       0x8
  21 #define GXBB_WDT_RSET_REG                       0xc
  22 
  23 #define GXBB_WDT_CTRL_CLKDIV_EN                 BIT(25)
  24 #define GXBB_WDT_CTRL_CLK_EN                    BIT(24)
  25 #define GXBB_WDT_CTRL_EE_RESET                  BIT(21)
  26 #define GXBB_WDT_CTRL_EN                        BIT(18)
  27 #define GXBB_WDT_CTRL_DIV_MASK                  (BIT(18) - 1)
  28 
  29 #define GXBB_WDT_TCNT_SETUP_MASK                (BIT(16) - 1)
  30 #define GXBB_WDT_TCNT_CNT_SHIFT                 16
  31 
  32 struct meson_gxbb_wdt {
  33         void __iomem *reg_base;
  34         struct watchdog_device wdt_dev;
  35         struct clk *clk;
  36 };
  37 
  38 static int meson_gxbb_wdt_start(struct watchdog_device *wdt_dev)
  39 {
  40         struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
  41 
  42         writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) | GXBB_WDT_CTRL_EN,
  43                data->reg_base + GXBB_WDT_CTRL_REG);
  44 
  45         return 0;
  46 }
  47 
  48 static int meson_gxbb_wdt_stop(struct watchdog_device *wdt_dev)
  49 {
  50         struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
  51 
  52         writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) & ~GXBB_WDT_CTRL_EN,
  53                data->reg_base + GXBB_WDT_CTRL_REG);
  54 
  55         return 0;
  56 }
  57 
  58 static int meson_gxbb_wdt_ping(struct watchdog_device *wdt_dev)
  59 {
  60         struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
  61 
  62         writel(0, data->reg_base + GXBB_WDT_RSET_REG);
  63 
  64         return 0;
  65 }
  66 
  67 static int meson_gxbb_wdt_set_timeout(struct watchdog_device *wdt_dev,
  68                                       unsigned int timeout)
  69 {
  70         struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
  71         unsigned long tcnt = timeout * 1000;
  72 
  73         if (tcnt > GXBB_WDT_TCNT_SETUP_MASK)
  74                 tcnt = GXBB_WDT_TCNT_SETUP_MASK;
  75 
  76         wdt_dev->timeout = timeout;
  77 
  78         meson_gxbb_wdt_ping(wdt_dev);
  79 
  80         writel(tcnt, data->reg_base + GXBB_WDT_TCNT_REG);
  81 
  82         return 0;
  83 }
  84 
  85 static unsigned int meson_gxbb_wdt_get_timeleft(struct watchdog_device *wdt_dev)
  86 {
  87         struct meson_gxbb_wdt *data = watchdog_get_drvdata(wdt_dev);
  88         unsigned long reg;
  89 
  90         reg = readl(data->reg_base + GXBB_WDT_TCNT_REG);
  91 
  92         return ((reg & GXBB_WDT_TCNT_SETUP_MASK) -
  93                 (reg >> GXBB_WDT_TCNT_CNT_SHIFT)) / 1000;
  94 }
  95 
  96 static const struct watchdog_ops meson_gxbb_wdt_ops = {
  97         .start = meson_gxbb_wdt_start,
  98         .stop = meson_gxbb_wdt_stop,
  99         .ping = meson_gxbb_wdt_ping,
 100         .set_timeout = meson_gxbb_wdt_set_timeout,
 101         .get_timeleft = meson_gxbb_wdt_get_timeleft,
 102 };
 103 
 104 static const struct watchdog_info meson_gxbb_wdt_info = {
 105         .identity = "Meson GXBB Watchdog",
 106         .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
 107 };
 108 
 109 static int __maybe_unused meson_gxbb_wdt_resume(struct device *dev)
 110 {
 111         struct meson_gxbb_wdt *data = dev_get_drvdata(dev);
 112 
 113         if (watchdog_active(&data->wdt_dev))
 114                 meson_gxbb_wdt_start(&data->wdt_dev);
 115 
 116         return 0;
 117 }
 118 
 119 static int __maybe_unused meson_gxbb_wdt_suspend(struct device *dev)
 120 {
 121         struct meson_gxbb_wdt *data = dev_get_drvdata(dev);
 122 
 123         if (watchdog_active(&data->wdt_dev))
 124                 meson_gxbb_wdt_stop(&data->wdt_dev);
 125 
 126         return 0;
 127 }
 128 
 129 static const struct dev_pm_ops meson_gxbb_wdt_pm_ops = {
 130         SET_SYSTEM_SLEEP_PM_OPS(meson_gxbb_wdt_suspend, meson_gxbb_wdt_resume)
 131 };
 132 
 133 static const struct of_device_id meson_gxbb_wdt_dt_ids[] = {
 134          { .compatible = "amlogic,meson-gxbb-wdt", },
 135          { /* sentinel */ },
 136 };
 137 MODULE_DEVICE_TABLE(of, meson_gxbb_wdt_dt_ids);
 138 
 139 static void meson_clk_disable_unprepare(void *data)
 140 {
 141         clk_disable_unprepare(data);
 142 }
 143 
 144 static int meson_gxbb_wdt_probe(struct platform_device *pdev)
 145 {
 146         struct device *dev = &pdev->dev;
 147         struct meson_gxbb_wdt *data;
 148         int ret;
 149 
 150         data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
 151         if (!data)
 152                 return -ENOMEM;
 153 
 154         data->reg_base = devm_platform_ioremap_resource(pdev, 0);
 155         if (IS_ERR(data->reg_base))
 156                 return PTR_ERR(data->reg_base);
 157 
 158         data->clk = devm_clk_get(dev, NULL);
 159         if (IS_ERR(data->clk))
 160                 return PTR_ERR(data->clk);
 161 
 162         ret = clk_prepare_enable(data->clk);
 163         if (ret)
 164                 return ret;
 165         ret = devm_add_action_or_reset(dev, meson_clk_disable_unprepare,
 166                                        data->clk);
 167         if (ret)
 168                 return ret;
 169 
 170         platform_set_drvdata(pdev, data);
 171 
 172         data->wdt_dev.parent = dev;
 173         data->wdt_dev.info = &meson_gxbb_wdt_info;
 174         data->wdt_dev.ops = &meson_gxbb_wdt_ops;
 175         data->wdt_dev.max_hw_heartbeat_ms = GXBB_WDT_TCNT_SETUP_MASK;
 176         data->wdt_dev.min_timeout = 1;
 177         data->wdt_dev.timeout = DEFAULT_TIMEOUT;
 178         watchdog_set_drvdata(&data->wdt_dev, data);
 179 
 180         /* Setup with 1ms timebase */
 181         writel(((clk_get_rate(data->clk) / 1000) & GXBB_WDT_CTRL_DIV_MASK) |
 182                 GXBB_WDT_CTRL_EE_RESET |
 183                 GXBB_WDT_CTRL_CLK_EN |
 184                 GXBB_WDT_CTRL_CLKDIV_EN,
 185                 data->reg_base + GXBB_WDT_CTRL_REG);
 186 
 187         meson_gxbb_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout);
 188 
 189         watchdog_stop_on_reboot(&data->wdt_dev);
 190         return devm_watchdog_register_device(dev, &data->wdt_dev);
 191 }
 192 
 193 static struct platform_driver meson_gxbb_wdt_driver = {
 194         .probe  = meson_gxbb_wdt_probe,
 195         .driver = {
 196                 .name = "meson-gxbb-wdt",
 197                 .pm = &meson_gxbb_wdt_pm_ops,
 198                 .of_match_table = meson_gxbb_wdt_dt_ids,
 199         },
 200 };
 201 
 202 module_platform_driver(meson_gxbb_wdt_driver);
 203 
 204 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
 205 MODULE_DESCRIPTION("Amlogic Meson GXBB Watchdog timer driver");
 206 MODULE_LICENSE("Dual BSD/GPL");

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