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