1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Watchdog driver for Conexant Digicolor 4 * 5 * Copyright (C) 2015 Paradox Innovation Ltd. 6 * 7 */ 8 9 #include <linux/types.h> 10 #include <linux/module.h> 11 #include <linux/io.h> 12 #include <linux/delay.h> 13 #include <linux/clk.h> 14 #include <linux/watchdog.h> 15 #include <linux/platform_device.h> 16 #include <linux/of_address.h> 17 18 #define TIMER_A_CONTROL 0 19 #define TIMER_A_COUNT 4 20 21 #define TIMER_A_ENABLE_COUNT BIT(0) 22 #define TIMER_A_ENABLE_WATCHDOG BIT(1) 23 24 struct dc_wdt { 25 void __iomem *base; 26 struct clk *clk; 27 spinlock_t lock; 28 }; 29 30 static unsigned timeout; 31 module_param(timeout, uint, 0); 32 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); 33 34 static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks) 35 { 36 unsigned long flags; 37 38 spin_lock_irqsave(&wdt->lock, flags); 39 40 writel_relaxed(0, wdt->base + TIMER_A_CONTROL); 41 writel_relaxed(ticks, wdt->base + TIMER_A_COUNT); 42 writel_relaxed(TIMER_A_ENABLE_COUNT | TIMER_A_ENABLE_WATCHDOG, 43 wdt->base + TIMER_A_CONTROL); 44 45 spin_unlock_irqrestore(&wdt->lock, flags); 46 } 47 48 static int dc_wdt_restart(struct watchdog_device *wdog, unsigned long action, 49 void *data) 50 { 51 struct dc_wdt *wdt = watchdog_get_drvdata(wdog); 52 53 dc_wdt_set(wdt, 1); 54 /* wait for reset to assert... */ 55 mdelay(500); 56 57 return 0; 58 } 59 60 static int dc_wdt_start(struct watchdog_device *wdog) 61 { 62 struct dc_wdt *wdt = watchdog_get_drvdata(wdog); 63 64 dc_wdt_set(wdt, wdog->timeout * clk_get_rate(wdt->clk)); 65 66 return 0; 67 } 68 69 static int dc_wdt_stop(struct watchdog_device *wdog) 70 { 71 struct dc_wdt *wdt = watchdog_get_drvdata(wdog); 72 73 writel_relaxed(0, wdt->base + TIMER_A_CONTROL); 74 75 return 0; 76 } 77 78 static int dc_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t) 79 { 80 struct dc_wdt *wdt = watchdog_get_drvdata(wdog); 81 82 dc_wdt_set(wdt, t * clk_get_rate(wdt->clk)); 83 wdog->timeout = t; 84 85 return 0; 86 } 87 88 static unsigned int dc_wdt_get_timeleft(struct watchdog_device *wdog) 89 { 90 struct dc_wdt *wdt = watchdog_get_drvdata(wdog); 91 uint32_t count = readl_relaxed(wdt->base + TIMER_A_COUNT); 92 93 return count / clk_get_rate(wdt->clk); 94 } 95 96 static const struct watchdog_ops dc_wdt_ops = { 97 .owner = THIS_MODULE, 98 .start = dc_wdt_start, 99 .stop = dc_wdt_stop, 100 .set_timeout = dc_wdt_set_timeout, 101 .get_timeleft = dc_wdt_get_timeleft, 102 .restart = dc_wdt_restart, 103 }; 104 105 static const struct watchdog_info dc_wdt_info = { 106 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE 107 | WDIOF_KEEPALIVEPING, 108 .identity = "Conexant Digicolor Watchdog", 109 }; 110 111 static struct watchdog_device dc_wdt_wdd = { 112 .info = &dc_wdt_info, 113 .ops = &dc_wdt_ops, 114 .min_timeout = 1, 115 }; 116 117 static int dc_wdt_probe(struct platform_device *pdev) 118 { 119 struct device *dev = &pdev->dev; 120 struct dc_wdt *wdt; 121 122 wdt = devm_kzalloc(dev, sizeof(struct dc_wdt), GFP_KERNEL); 123 if (!wdt) 124 return -ENOMEM; 125 126 wdt->base = devm_platform_ioremap_resource(pdev, 0); 127 if (IS_ERR(wdt->base)) 128 return PTR_ERR(wdt->base); 129 130 wdt->clk = devm_clk_get(dev, NULL); 131 if (IS_ERR(wdt->clk)) 132 return PTR_ERR(wdt->clk); 133 dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk); 134 dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout; 135 dc_wdt_wdd.parent = dev; 136 137 spin_lock_init(&wdt->lock); 138 139 watchdog_set_drvdata(&dc_wdt_wdd, wdt); 140 watchdog_set_restart_priority(&dc_wdt_wdd, 128); 141 watchdog_init_timeout(&dc_wdt_wdd, timeout, dev); 142 watchdog_stop_on_reboot(&dc_wdt_wdd); 143 return devm_watchdog_register_device(dev, &dc_wdt_wdd); 144 } 145 146 static const struct of_device_id dc_wdt_of_match[] = { 147 { .compatible = "cnxt,cx92755-wdt", }, 148 {}, 149 }; 150 MODULE_DEVICE_TABLE(of, dc_wdt_of_match); 151 152 static struct platform_driver dc_wdt_driver = { 153 .probe = dc_wdt_probe, 154 .driver = { 155 .name = "digicolor-wdt", 156 .of_match_table = dc_wdt_of_match, 157 }, 158 }; 159 module_platform_driver(dc_wdt_driver); 160 161 MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); 162 MODULE_DESCRIPTION("Driver for Conexant Digicolor watchdog timer"); 163 MODULE_LICENSE("GPL"); 164