Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v4.18 237 lines 6.3 kB view raw
1/* 2 * Meson Watchdog Driver 3 * 4 * Copyright (c) 2014 Carlo Caione 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 */ 11 12#include <linux/clk.h> 13#include <linux/delay.h> 14#include <linux/err.h> 15#include <linux/init.h> 16#include <linux/io.h> 17#include <linux/kernel.h> 18#include <linux/module.h> 19#include <linux/moduleparam.h> 20#include <linux/of.h> 21#include <linux/of_device.h> 22#include <linux/platform_device.h> 23#include <linux/types.h> 24#include <linux/watchdog.h> 25 26#define DRV_NAME "meson_wdt" 27 28#define MESON_WDT_TC 0x00 29#define MESON_WDT_DC_RESET (3 << 24) 30 31#define MESON_WDT_RESET 0x04 32 33#define MESON_WDT_TIMEOUT 30 34#define MESON_WDT_MIN_TIMEOUT 1 35 36#define MESON_SEC_TO_TC(s, c) ((s) * (c)) 37 38static bool nowayout = WATCHDOG_NOWAYOUT; 39static unsigned int timeout; 40 41struct meson_wdt_data { 42 unsigned int enable; 43 unsigned int terminal_count_mask; 44 unsigned int count_unit; 45}; 46 47static struct meson_wdt_data meson6_wdt_data = { 48 .enable = BIT(22), 49 .terminal_count_mask = 0x3fffff, 50 .count_unit = 100000, /* 10 us */ 51}; 52 53static struct meson_wdt_data meson8b_wdt_data = { 54 .enable = BIT(19), 55 .terminal_count_mask = 0xffff, 56 .count_unit = 7812, /* 128 us */ 57}; 58 59struct meson_wdt_dev { 60 struct watchdog_device wdt_dev; 61 void __iomem *wdt_base; 62 const struct meson_wdt_data *data; 63}; 64 65static int meson_wdt_restart(struct watchdog_device *wdt_dev, 66 unsigned long action, void *data) 67{ 68 struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); 69 u32 tc_reboot = MESON_WDT_DC_RESET; 70 71 tc_reboot |= meson_wdt->data->enable; 72 73 while (1) { 74 writel(tc_reboot, meson_wdt->wdt_base + MESON_WDT_TC); 75 mdelay(5); 76 } 77 78 return 0; 79} 80 81static int meson_wdt_ping(struct watchdog_device *wdt_dev) 82{ 83 struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); 84 85 writel(0, meson_wdt->wdt_base + MESON_WDT_RESET); 86 87 return 0; 88} 89 90static void meson_wdt_change_timeout(struct watchdog_device *wdt_dev, 91 unsigned int timeout) 92{ 93 struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); 94 u32 reg; 95 96 reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); 97 reg &= ~meson_wdt->data->terminal_count_mask; 98 reg |= MESON_SEC_TO_TC(timeout, meson_wdt->data->count_unit); 99 writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); 100} 101 102static int meson_wdt_set_timeout(struct watchdog_device *wdt_dev, 103 unsigned int timeout) 104{ 105 wdt_dev->timeout = timeout; 106 107 meson_wdt_change_timeout(wdt_dev, timeout); 108 meson_wdt_ping(wdt_dev); 109 110 return 0; 111} 112 113static int meson_wdt_stop(struct watchdog_device *wdt_dev) 114{ 115 struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); 116 u32 reg; 117 118 reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); 119 reg &= ~meson_wdt->data->enable; 120 writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); 121 122 return 0; 123} 124 125static int meson_wdt_start(struct watchdog_device *wdt_dev) 126{ 127 struct meson_wdt_dev *meson_wdt = watchdog_get_drvdata(wdt_dev); 128 u32 reg; 129 130 meson_wdt_change_timeout(wdt_dev, meson_wdt->wdt_dev.timeout); 131 meson_wdt_ping(wdt_dev); 132 133 reg = readl(meson_wdt->wdt_base + MESON_WDT_TC); 134 reg |= meson_wdt->data->enable; 135 writel(reg, meson_wdt->wdt_base + MESON_WDT_TC); 136 137 return 0; 138} 139 140static const struct watchdog_info meson_wdt_info = { 141 .identity = DRV_NAME, 142 .options = WDIOF_SETTIMEOUT | 143 WDIOF_KEEPALIVEPING | 144 WDIOF_MAGICCLOSE, 145}; 146 147static const struct watchdog_ops meson_wdt_ops = { 148 .owner = THIS_MODULE, 149 .start = meson_wdt_start, 150 .stop = meson_wdt_stop, 151 .ping = meson_wdt_ping, 152 .set_timeout = meson_wdt_set_timeout, 153 .restart = meson_wdt_restart, 154}; 155 156static const struct of_device_id meson_wdt_dt_ids[] = { 157 { .compatible = "amlogic,meson6-wdt", .data = &meson6_wdt_data }, 158 { .compatible = "amlogic,meson8-wdt", .data = &meson6_wdt_data }, 159 { .compatible = "amlogic,meson8b-wdt", .data = &meson8b_wdt_data }, 160 { .compatible = "amlogic,meson8m2-wdt", .data = &meson8b_wdt_data }, 161 { /* sentinel */ } 162}; 163MODULE_DEVICE_TABLE(of, meson_wdt_dt_ids); 164 165static int meson_wdt_probe(struct platform_device *pdev) 166{ 167 struct resource *res; 168 struct meson_wdt_dev *meson_wdt; 169 const struct of_device_id *of_id; 170 int err; 171 172 meson_wdt = devm_kzalloc(&pdev->dev, sizeof(*meson_wdt), GFP_KERNEL); 173 if (!meson_wdt) 174 return -ENOMEM; 175 176 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 177 meson_wdt->wdt_base = devm_ioremap_resource(&pdev->dev, res); 178 if (IS_ERR(meson_wdt->wdt_base)) 179 return PTR_ERR(meson_wdt->wdt_base); 180 181 of_id = of_match_device(meson_wdt_dt_ids, &pdev->dev); 182 if (!of_id) { 183 dev_err(&pdev->dev, "Unable to initialize WDT data\n"); 184 return -ENODEV; 185 } 186 meson_wdt->data = of_id->data; 187 188 meson_wdt->wdt_dev.parent = &pdev->dev; 189 meson_wdt->wdt_dev.info = &meson_wdt_info; 190 meson_wdt->wdt_dev.ops = &meson_wdt_ops; 191 meson_wdt->wdt_dev.max_timeout = 192 meson_wdt->data->terminal_count_mask / meson_wdt->data->count_unit; 193 meson_wdt->wdt_dev.min_timeout = MESON_WDT_MIN_TIMEOUT; 194 meson_wdt->wdt_dev.timeout = min_t(unsigned int, 195 MESON_WDT_TIMEOUT, 196 meson_wdt->wdt_dev.max_timeout); 197 198 watchdog_set_drvdata(&meson_wdt->wdt_dev, meson_wdt); 199 200 watchdog_init_timeout(&meson_wdt->wdt_dev, timeout, &pdev->dev); 201 watchdog_set_nowayout(&meson_wdt->wdt_dev, nowayout); 202 watchdog_set_restart_priority(&meson_wdt->wdt_dev, 128); 203 204 meson_wdt_stop(&meson_wdt->wdt_dev); 205 206 watchdog_stop_on_reboot(&meson_wdt->wdt_dev); 207 err = devm_watchdog_register_device(&pdev->dev, &meson_wdt->wdt_dev); 208 if (err) 209 return err; 210 211 dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)", 212 meson_wdt->wdt_dev.timeout, nowayout); 213 214 return 0; 215} 216 217static struct platform_driver meson_wdt_driver = { 218 .probe = meson_wdt_probe, 219 .driver = { 220 .name = DRV_NAME, 221 .of_match_table = meson_wdt_dt_ids, 222 }, 223}; 224 225module_platform_driver(meson_wdt_driver); 226 227module_param(timeout, uint, 0); 228MODULE_PARM_DESC(timeout, "Watchdog heartbeat in seconds"); 229 230module_param(nowayout, bool, 0); 231MODULE_PARM_DESC(nowayout, 232 "Watchdog cannot be stopped once started (default=" 233 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 234 235MODULE_LICENSE("GPL"); 236MODULE_AUTHOR("Carlo Caione <carlo@caione.org>"); 237MODULE_DESCRIPTION("Meson Watchdog Timer Driver");