1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (c) 2020 TOSHIBA CORPORATION 4 * Copyright (c) 2020 Toshiba Electronic Devices & Storage Corporation 5 * Copyright (c) 2020 Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp> 6 */ 7 8 #include <linux/clk.h> 9 #include <linux/io.h> 10 #include <linux/kernel.h> 11 #include <linux/module.h> 12 #include <linux/of.h> 13 #include <linux/platform_device.h> 14 #include <linux/watchdog.h> 15 16 #define WDT_CNT 0x00 17 #define WDT_MIN 0x04 18 #define WDT_MAX 0x08 19 #define WDT_CTL 0x0c 20 #define WDT_CMD 0x10 21 #define WDT_CMD_CLEAR 0x4352 22 #define WDT_CMD_START_STOP 0x5354 23 #define WDT_DIV 0x30 24 25 #define VISCONTI_WDT_FREQ 2000000 /* 2MHz */ 26 #define WDT_DEFAULT_TIMEOUT 10U /* in seconds */ 27 28 static bool nowayout = WATCHDOG_NOWAYOUT; 29 module_param(nowayout, bool, 0); 30 MODULE_PARM_DESC( 31 nowayout, 32 "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT)")"); 33 34 struct visconti_wdt_priv { 35 struct watchdog_device wdev; 36 void __iomem *base; 37 u32 div; 38 }; 39 40 static int visconti_wdt_start(struct watchdog_device *wdev) 41 { 42 struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev); 43 u32 timeout = wdev->timeout * VISCONTI_WDT_FREQ; 44 45 writel(priv->div, priv->base + WDT_DIV); 46 writel(0, priv->base + WDT_MIN); 47 writel(timeout, priv->base + WDT_MAX); 48 writel(0, priv->base + WDT_CTL); 49 writel(WDT_CMD_START_STOP, priv->base + WDT_CMD); 50 51 return 0; 52 } 53 54 static int visconti_wdt_stop(struct watchdog_device *wdev) 55 { 56 struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev); 57 58 writel(1, priv->base + WDT_CTL); 59 writel(WDT_CMD_START_STOP, priv->base + WDT_CMD); 60 61 return 0; 62 } 63 64 static int visconti_wdt_ping(struct watchdog_device *wdd) 65 { 66 struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdd); 67 68 writel(WDT_CMD_CLEAR, priv->base + WDT_CMD); 69 70 return 0; 71 } 72 73 static unsigned int visconti_wdt_get_timeleft(struct watchdog_device *wdev) 74 { 75 struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev); 76 u32 timeout = wdev->timeout * VISCONTI_WDT_FREQ; 77 u32 cnt = readl(priv->base + WDT_CNT); 78 79 if (timeout <= cnt) 80 return 0; 81 timeout -= cnt; 82 83 return timeout / VISCONTI_WDT_FREQ; 84 } 85 86 static int visconti_wdt_set_timeout(struct watchdog_device *wdev, unsigned int timeout) 87 { 88 u32 val; 89 struct visconti_wdt_priv *priv = watchdog_get_drvdata(wdev); 90 91 wdev->timeout = timeout; 92 val = wdev->timeout * VISCONTI_WDT_FREQ; 93 94 /* Clear counter before setting timeout because WDT expires */ 95 writel(WDT_CMD_CLEAR, priv->base + WDT_CMD); 96 writel(val, priv->base + WDT_MAX); 97 98 return 0; 99 } 100 101 static const struct watchdog_info visconti_wdt_info = { 102 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, 103 .identity = "Visconti Watchdog", 104 }; 105 106 static const struct watchdog_ops visconti_wdt_ops = { 107 .owner = THIS_MODULE, 108 .start = visconti_wdt_start, 109 .stop = visconti_wdt_stop, 110 .ping = visconti_wdt_ping, 111 .get_timeleft = visconti_wdt_get_timeleft, 112 .set_timeout = visconti_wdt_set_timeout, 113 }; 114 115 static void visconti_clk_disable_unprepare(void *data) 116 { 117 clk_disable_unprepare(data); 118 } 119 120 static int visconti_wdt_probe(struct platform_device *pdev) 121 { 122 struct watchdog_device *wdev; 123 struct visconti_wdt_priv *priv; 124 struct device *dev = &pdev->dev; 125 struct clk *clk; 126 int ret; 127 unsigned long clk_freq; 128 129 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 130 if (!priv) 131 return -ENOMEM; 132 133 priv->base = devm_platform_ioremap_resource(pdev, 0); 134 if (IS_ERR(priv->base)) 135 return PTR_ERR(priv->base); 136 137 clk = devm_clk_get(dev, NULL); 138 if (IS_ERR(clk)) 139 return dev_err_probe(dev, PTR_ERR(clk), "Could not get clock\n"); 140 141 ret = clk_prepare_enable(clk); 142 if (ret) { 143 dev_err(dev, "Could not enable clock\n"); 144 return ret; 145 } 146 147 ret = devm_add_action_or_reset(dev, visconti_clk_disable_unprepare, clk); 148 if (ret) 149 return ret; 150 151 clk_freq = clk_get_rate(clk); 152 if (!clk_freq) 153 return -EINVAL; 154 155 priv->div = clk_freq / VISCONTI_WDT_FREQ; 156 157 /* Initialize struct watchdog_device. */ 158 wdev = &priv->wdev; 159 wdev->info = &visconti_wdt_info; 160 wdev->ops = &visconti_wdt_ops; 161 wdev->parent = dev; 162 wdev->min_timeout = 1; 163 wdev->max_timeout = 0xffffffff / VISCONTI_WDT_FREQ; 164 wdev->timeout = min(wdev->max_timeout, WDT_DEFAULT_TIMEOUT); 165 166 watchdog_set_drvdata(wdev, priv); 167 watchdog_set_nowayout(wdev, nowayout); 168 watchdog_stop_on_unregister(wdev); 169 170 /* This overrides the default timeout only if DT configuration was found */ 171 ret = watchdog_init_timeout(wdev, 0, dev); 172 if (ret) 173 dev_warn(dev, "Specified timeout value invalid, using default\n"); 174 175 return devm_watchdog_register_device(dev, wdev); 176 } 177 178 static const struct of_device_id visconti_wdt_of_match[] = { 179 { .compatible = "toshiba,visconti-wdt", }, 180 {} 181 }; 182 MODULE_DEVICE_TABLE(of, visconti_wdt_of_match); 183 184 static struct platform_driver visconti_wdt_driver = { 185 .driver = { 186 .name = "visconti_wdt", 187 .of_match_table = visconti_wdt_of_match, 188 }, 189 .probe = visconti_wdt_probe, 190 }; 191 module_platform_driver(visconti_wdt_driver); 192 193 MODULE_DESCRIPTION("TOSHIBA Visconti Watchdog Driver"); 194 MODULE_AUTHOR("Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp"); 195 MODULE_LICENSE("GPL v2"); 196