1*2bdf6acbSAndreas Färber /* 2*2bdf6acbSAndreas Färber * Realtek RTD129x watchdog 3*2bdf6acbSAndreas Färber * 4*2bdf6acbSAndreas Färber * Copyright (c) 2017 Andreas Färber 5*2bdf6acbSAndreas Färber * 6*2bdf6acbSAndreas Färber * SPDX-License-Identifier: GPL-2.0+ 7*2bdf6acbSAndreas Färber */ 8*2bdf6acbSAndreas Färber 9*2bdf6acbSAndreas Färber #include <linux/bitops.h> 10*2bdf6acbSAndreas Färber #include <linux/clk.h> 11*2bdf6acbSAndreas Färber #include <linux/io.h> 12*2bdf6acbSAndreas Färber #include <linux/module.h> 13*2bdf6acbSAndreas Färber #include <linux/of.h> 14*2bdf6acbSAndreas Färber #include <linux/of_address.h> 15*2bdf6acbSAndreas Färber #include <linux/platform_device.h> 16*2bdf6acbSAndreas Färber #include <linux/watchdog.h> 17*2bdf6acbSAndreas Färber 18*2bdf6acbSAndreas Färber #define RTD119X_TCWCR 0x0 19*2bdf6acbSAndreas Färber #define RTD119X_TCWTR 0x4 20*2bdf6acbSAndreas Färber #define RTD119X_TCWOV 0xc 21*2bdf6acbSAndreas Färber 22*2bdf6acbSAndreas Färber #define RTD119X_TCWCR_WDEN_DISABLED 0xa5 23*2bdf6acbSAndreas Färber #define RTD119X_TCWCR_WDEN_ENABLED 0xff 24*2bdf6acbSAndreas Färber #define RTD119X_TCWCR_WDEN_MASK 0xff 25*2bdf6acbSAndreas Färber 26*2bdf6acbSAndreas Färber #define RTD119X_TCWTR_WDCLR BIT(0) 27*2bdf6acbSAndreas Färber 28*2bdf6acbSAndreas Färber struct rtd119x_watchdog_device { 29*2bdf6acbSAndreas Färber struct watchdog_device wdt_dev; 30*2bdf6acbSAndreas Färber void __iomem *base; 31*2bdf6acbSAndreas Färber struct clk *clk; 32*2bdf6acbSAndreas Färber }; 33*2bdf6acbSAndreas Färber 34*2bdf6acbSAndreas Färber static int rtd119x_wdt_start(struct watchdog_device *wdev) 35*2bdf6acbSAndreas Färber { 36*2bdf6acbSAndreas Färber struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev); 37*2bdf6acbSAndreas Färber u32 val; 38*2bdf6acbSAndreas Färber 39*2bdf6acbSAndreas Färber val = readl_relaxed(data->base + RTD119X_TCWCR); 40*2bdf6acbSAndreas Färber val &= ~RTD119X_TCWCR_WDEN_MASK; 41*2bdf6acbSAndreas Färber val |= RTD119X_TCWCR_WDEN_ENABLED; 42*2bdf6acbSAndreas Färber writel(val, data->base + RTD119X_TCWCR); 43*2bdf6acbSAndreas Färber 44*2bdf6acbSAndreas Färber return 0; 45*2bdf6acbSAndreas Färber } 46*2bdf6acbSAndreas Färber 47*2bdf6acbSAndreas Färber static int rtd119x_wdt_stop(struct watchdog_device *wdev) 48*2bdf6acbSAndreas Färber { 49*2bdf6acbSAndreas Färber struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev); 50*2bdf6acbSAndreas Färber u32 val; 51*2bdf6acbSAndreas Färber 52*2bdf6acbSAndreas Färber val = readl_relaxed(data->base + RTD119X_TCWCR); 53*2bdf6acbSAndreas Färber val &= ~RTD119X_TCWCR_WDEN_MASK; 54*2bdf6acbSAndreas Färber val |= RTD119X_TCWCR_WDEN_DISABLED; 55*2bdf6acbSAndreas Färber writel(val, data->base + RTD119X_TCWCR); 56*2bdf6acbSAndreas Färber 57*2bdf6acbSAndreas Färber return 0; 58*2bdf6acbSAndreas Färber } 59*2bdf6acbSAndreas Färber 60*2bdf6acbSAndreas Färber static int rtd119x_wdt_ping(struct watchdog_device *wdev) 61*2bdf6acbSAndreas Färber { 62*2bdf6acbSAndreas Färber struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev); 63*2bdf6acbSAndreas Färber 64*2bdf6acbSAndreas Färber writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR); 65*2bdf6acbSAndreas Färber 66*2bdf6acbSAndreas Färber return rtd119x_wdt_start(wdev); 67*2bdf6acbSAndreas Färber } 68*2bdf6acbSAndreas Färber 69*2bdf6acbSAndreas Färber static int rtd119x_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val) 70*2bdf6acbSAndreas Färber { 71*2bdf6acbSAndreas Färber struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev); 72*2bdf6acbSAndreas Färber 73*2bdf6acbSAndreas Färber writel(val * clk_get_rate(data->clk), data->base + RTD119X_TCWOV); 74*2bdf6acbSAndreas Färber 75*2bdf6acbSAndreas Färber data->wdt_dev.timeout = val; 76*2bdf6acbSAndreas Färber 77*2bdf6acbSAndreas Färber return 0; 78*2bdf6acbSAndreas Färber } 79*2bdf6acbSAndreas Färber 80*2bdf6acbSAndreas Färber static const struct watchdog_ops rtd119x_wdt_ops = { 81*2bdf6acbSAndreas Färber .owner = THIS_MODULE, 82*2bdf6acbSAndreas Färber .start = rtd119x_wdt_start, 83*2bdf6acbSAndreas Färber .stop = rtd119x_wdt_stop, 84*2bdf6acbSAndreas Färber .ping = rtd119x_wdt_ping, 85*2bdf6acbSAndreas Färber .set_timeout = rtd119x_wdt_set_timeout, 86*2bdf6acbSAndreas Färber }; 87*2bdf6acbSAndreas Färber 88*2bdf6acbSAndreas Färber static const struct watchdog_info rtd119x_wdt_info = { 89*2bdf6acbSAndreas Färber .identity = "rtd119x-wdt", 90*2bdf6acbSAndreas Färber .options = 0, 91*2bdf6acbSAndreas Färber }; 92*2bdf6acbSAndreas Färber 93*2bdf6acbSAndreas Färber static const struct of_device_id rtd119x_wdt_dt_ids[] = { 94*2bdf6acbSAndreas Färber { .compatible = "realtek,rtd1295-watchdog" }, 95*2bdf6acbSAndreas Färber { } 96*2bdf6acbSAndreas Färber }; 97*2bdf6acbSAndreas Färber 98*2bdf6acbSAndreas Färber static int rtd119x_wdt_probe(struct platform_device *pdev) 99*2bdf6acbSAndreas Färber { 100*2bdf6acbSAndreas Färber struct rtd119x_watchdog_device *data; 101*2bdf6acbSAndreas Färber struct resource *res; 102*2bdf6acbSAndreas Färber int ret; 103*2bdf6acbSAndreas Färber 104*2bdf6acbSAndreas Färber data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 105*2bdf6acbSAndreas Färber if (!data) 106*2bdf6acbSAndreas Färber return -ENOMEM; 107*2bdf6acbSAndreas Färber 108*2bdf6acbSAndreas Färber res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 109*2bdf6acbSAndreas Färber data->base = devm_ioremap_resource(&pdev->dev, res); 110*2bdf6acbSAndreas Färber if (IS_ERR(data->base)) 111*2bdf6acbSAndreas Färber return PTR_ERR(data->base); 112*2bdf6acbSAndreas Färber 113*2bdf6acbSAndreas Färber data->clk = of_clk_get(pdev->dev.of_node, 0); 114*2bdf6acbSAndreas Färber if (IS_ERR(data->clk)) 115*2bdf6acbSAndreas Färber return PTR_ERR(data->clk); 116*2bdf6acbSAndreas Färber 117*2bdf6acbSAndreas Färber ret = clk_prepare_enable(data->clk); 118*2bdf6acbSAndreas Färber if (ret) { 119*2bdf6acbSAndreas Färber clk_put(data->clk); 120*2bdf6acbSAndreas Färber return ret; 121*2bdf6acbSAndreas Färber } 122*2bdf6acbSAndreas Färber 123*2bdf6acbSAndreas Färber data->wdt_dev.info = &rtd119x_wdt_info; 124*2bdf6acbSAndreas Färber data->wdt_dev.ops = &rtd119x_wdt_ops; 125*2bdf6acbSAndreas Färber data->wdt_dev.timeout = 120; 126*2bdf6acbSAndreas Färber data->wdt_dev.max_timeout = 0xffffffff / clk_get_rate(data->clk); 127*2bdf6acbSAndreas Färber data->wdt_dev.min_timeout = 1; 128*2bdf6acbSAndreas Färber data->wdt_dev.parent = &pdev->dev; 129*2bdf6acbSAndreas Färber 130*2bdf6acbSAndreas Färber watchdog_stop_on_reboot(&data->wdt_dev); 131*2bdf6acbSAndreas Färber watchdog_set_drvdata(&data->wdt_dev, data); 132*2bdf6acbSAndreas Färber platform_set_drvdata(pdev, data); 133*2bdf6acbSAndreas Färber 134*2bdf6acbSAndreas Färber writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR); 135*2bdf6acbSAndreas Färber rtd119x_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout); 136*2bdf6acbSAndreas Färber rtd119x_wdt_stop(&data->wdt_dev); 137*2bdf6acbSAndreas Färber 138*2bdf6acbSAndreas Färber ret = devm_watchdog_register_device(&pdev->dev, &data->wdt_dev); 139*2bdf6acbSAndreas Färber if (ret) { 140*2bdf6acbSAndreas Färber clk_disable_unprepare(data->clk); 141*2bdf6acbSAndreas Färber clk_put(data->clk); 142*2bdf6acbSAndreas Färber return ret; 143*2bdf6acbSAndreas Färber } 144*2bdf6acbSAndreas Färber 145*2bdf6acbSAndreas Färber return 0; 146*2bdf6acbSAndreas Färber } 147*2bdf6acbSAndreas Färber 148*2bdf6acbSAndreas Färber static int rtd119x_wdt_remove(struct platform_device *pdev) 149*2bdf6acbSAndreas Färber { 150*2bdf6acbSAndreas Färber struct rtd119x_watchdog_device *data = platform_get_drvdata(pdev); 151*2bdf6acbSAndreas Färber 152*2bdf6acbSAndreas Färber watchdog_unregister_device(&data->wdt_dev); 153*2bdf6acbSAndreas Färber 154*2bdf6acbSAndreas Färber clk_disable_unprepare(data->clk); 155*2bdf6acbSAndreas Färber clk_put(data->clk); 156*2bdf6acbSAndreas Färber 157*2bdf6acbSAndreas Färber return 0; 158*2bdf6acbSAndreas Färber } 159*2bdf6acbSAndreas Färber 160*2bdf6acbSAndreas Färber static struct platform_driver rtd119x_wdt_driver = { 161*2bdf6acbSAndreas Färber .probe = rtd119x_wdt_probe, 162*2bdf6acbSAndreas Färber .remove = rtd119x_wdt_remove, 163*2bdf6acbSAndreas Färber .driver = { 164*2bdf6acbSAndreas Färber .name = "rtd1295-watchdog", 165*2bdf6acbSAndreas Färber .of_match_table = rtd119x_wdt_dt_ids, 166*2bdf6acbSAndreas Färber }, 167*2bdf6acbSAndreas Färber }; 168*2bdf6acbSAndreas Färber builtin_platform_driver(rtd119x_wdt_driver); 169