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.11-rc2 213 lines 5.4 kB view raw
1/* 2 * Watchdog driver for Renesas WDT watchdog 3 * 4 * Copyright (C) 2015-16 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com> 5 * Copyright (C) 2015-16 Renesas Electronics Corporation 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 as published by 9 * the Free Software Foundation. 10 */ 11#include <linux/bitops.h> 12#include <linux/clk.h> 13#include <linux/io.h> 14#include <linux/kernel.h> 15#include <linux/module.h> 16#include <linux/of.h> 17#include <linux/platform_device.h> 18#include <linux/pm_runtime.h> 19#include <linux/watchdog.h> 20 21#define RWTCNT 0 22#define RWTCSRA 4 23#define RWTCSRA_WOVF BIT(4) 24#define RWTCSRA_WRFLG BIT(5) 25#define RWTCSRA_TME BIT(7) 26 27#define RWDT_DEFAULT_TIMEOUT 60U 28 29static const unsigned int clk_divs[] = { 1, 4, 16, 32, 64, 128, 1024 }; 30 31static bool nowayout = WATCHDOG_NOWAYOUT; 32module_param(nowayout, bool, 0); 33MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 34 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 35 36struct rwdt_priv { 37 void __iomem *base; 38 struct watchdog_device wdev; 39 struct clk *clk; 40 unsigned int clks_per_sec; 41 u8 cks; 42}; 43 44static void rwdt_write(struct rwdt_priv *priv, u32 val, unsigned int reg) 45{ 46 if (reg == RWTCNT) 47 val |= 0x5a5a0000; 48 else 49 val |= 0xa5a5a500; 50 51 writel_relaxed(val, priv->base + reg); 52} 53 54static int rwdt_init_timeout(struct watchdog_device *wdev) 55{ 56 struct rwdt_priv *priv = watchdog_get_drvdata(wdev); 57 58 rwdt_write(priv, 65536 - wdev->timeout * priv->clks_per_sec, RWTCNT); 59 60 return 0; 61} 62 63static int rwdt_start(struct watchdog_device *wdev) 64{ 65 struct rwdt_priv *priv = watchdog_get_drvdata(wdev); 66 67 clk_prepare_enable(priv->clk); 68 69 rwdt_write(priv, priv->cks, RWTCSRA); 70 rwdt_init_timeout(wdev); 71 72 while (readb_relaxed(priv->base + RWTCSRA) & RWTCSRA_WRFLG) 73 cpu_relax(); 74 75 rwdt_write(priv, priv->cks | RWTCSRA_TME, RWTCSRA); 76 77 return 0; 78} 79 80static int rwdt_stop(struct watchdog_device *wdev) 81{ 82 struct rwdt_priv *priv = watchdog_get_drvdata(wdev); 83 84 rwdt_write(priv, priv->cks, RWTCSRA); 85 clk_disable_unprepare(priv->clk); 86 87 return 0; 88} 89 90static unsigned int rwdt_get_timeleft(struct watchdog_device *wdev) 91{ 92 struct rwdt_priv *priv = watchdog_get_drvdata(wdev); 93 u16 val = readw_relaxed(priv->base + RWTCNT); 94 95 return DIV_ROUND_CLOSEST(65536 - val, priv->clks_per_sec); 96} 97 98static const struct watchdog_info rwdt_ident = { 99 .options = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, 100 .identity = "Renesas WDT Watchdog", 101}; 102 103static const struct watchdog_ops rwdt_ops = { 104 .owner = THIS_MODULE, 105 .start = rwdt_start, 106 .stop = rwdt_stop, 107 .ping = rwdt_init_timeout, 108 .get_timeleft = rwdt_get_timeleft, 109}; 110 111static int rwdt_probe(struct platform_device *pdev) 112{ 113 struct rwdt_priv *priv; 114 struct resource *res; 115 unsigned long rate; 116 unsigned int clks_per_sec; 117 int ret, i; 118 119 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 120 if (!priv) 121 return -ENOMEM; 122 123 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 124 priv->base = devm_ioremap_resource(&pdev->dev, res); 125 if (IS_ERR(priv->base)) 126 return PTR_ERR(priv->base); 127 128 priv->clk = devm_clk_get(&pdev->dev, NULL); 129 if (IS_ERR(priv->clk)) 130 return PTR_ERR(priv->clk); 131 132 rate = clk_get_rate(priv->clk); 133 if (!rate) 134 return -ENOENT; 135 136 for (i = ARRAY_SIZE(clk_divs) - 1; i >= 0; i--) { 137 clks_per_sec = DIV_ROUND_UP(rate, clk_divs[i]); 138 if (clks_per_sec) { 139 priv->clks_per_sec = clks_per_sec; 140 priv->cks = i; 141 break; 142 } 143 } 144 145 if (!clks_per_sec) { 146 dev_err(&pdev->dev, "Can't find suitable clock divider\n"); 147 return -ERANGE; 148 } 149 150 pm_runtime_enable(&pdev->dev); 151 pm_runtime_get_sync(&pdev->dev); 152 153 priv->wdev.info = &rwdt_ident, 154 priv->wdev.ops = &rwdt_ops, 155 priv->wdev.parent = &pdev->dev; 156 priv->wdev.min_timeout = 1; 157 priv->wdev.max_timeout = 65536 / clks_per_sec; 158 priv->wdev.timeout = min(priv->wdev.max_timeout, RWDT_DEFAULT_TIMEOUT); 159 160 platform_set_drvdata(pdev, priv); 161 watchdog_set_drvdata(&priv->wdev, priv); 162 watchdog_set_nowayout(&priv->wdev, nowayout); 163 164 /* This overrides the default timeout only if DT configuration was found */ 165 ret = watchdog_init_timeout(&priv->wdev, 0, &pdev->dev); 166 if (ret) 167 dev_warn(&pdev->dev, "Specified timeout value invalid, using default\n"); 168 169 ret = watchdog_register_device(&priv->wdev); 170 if (ret < 0) { 171 pm_runtime_put(&pdev->dev); 172 pm_runtime_disable(&pdev->dev); 173 return ret; 174 } 175 176 return 0; 177} 178 179static int rwdt_remove(struct platform_device *pdev) 180{ 181 struct rwdt_priv *priv = platform_get_drvdata(pdev); 182 183 watchdog_unregister_device(&priv->wdev); 184 pm_runtime_put(&pdev->dev); 185 pm_runtime_disable(&pdev->dev); 186 187 return 0; 188} 189 190/* 191 * This driver does also fit for R-Car Gen2 (r8a779[0-4]) WDT. However, for SMP 192 * to work there, one also needs a RESET (RST) driver which does not exist yet 193 * due to HW issues. This needs to be solved before adding compatibles here. 194 */ 195static const struct of_device_id rwdt_ids[] = { 196 { .compatible = "renesas,rcar-gen3-wdt", }, 197 { /* sentinel */ } 198}; 199MODULE_DEVICE_TABLE(of, rwdt_ids); 200 201static struct platform_driver rwdt_driver = { 202 .driver = { 203 .name = "renesas_wdt", 204 .of_match_table = rwdt_ids, 205 }, 206 .probe = rwdt_probe, 207 .remove = rwdt_remove, 208}; 209module_platform_driver(rwdt_driver); 210 211MODULE_DESCRIPTION("Renesas WDT Watchdog Driver"); 212MODULE_LICENSE("GPL v2"); 213MODULE_AUTHOR("Wolfram Sang <wsa@sang-engineering.com>");