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

watchdog: digicolor: driver for Conexant Digicolor CX92755 SoC

This commit add a driver for the watchdog functionality of the Conexant CX92755
SoC, from the Digicolor series of SoCs. Of 8 system timers provided by the
CX92755, the first one, timer A, can reset the chip when its counter reaches
zero. This driver uses this capability to provide userspace with a standard
watchdog, using the watchdog timer driver core framework. This driver also
implements a reboot handler for the reboot(2) system call.

The watchdog driver shares the timer registers with the CX92755 timer driver
(drivers/clocksource/timer-digicolor.c). The timer driver, however, uses only
timers other than A, so both drivers should coexist.

Signed-off-by: Baruch Siach <baruch@tkos.co.il>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

authored by

Baruch Siach and committed by
Wim Van Sebroeck
336694a0 b0abc8ff

+216
+10
drivers/watchdog/Kconfig
··· 526 526 To compile this driver as a module, choose M here: the 527 527 module will be called mtk_wdt. 528 528 529 + config DIGICOLOR_WATCHDOG 530 + tristate "Conexant Digicolor SoCs watchdog support" 531 + depends on ARCH_DIGICOLOR 532 + select WATCHDOG_CORE 533 + help 534 + Say Y here to include support for the watchdog timer 535 + in Conexant Digicolor SoCs. 536 + To compile this driver as a module, choose M here: the 537 + module will be called digicolor_wdt. 538 + 529 539 # AVR32 Architecture 530 540 531 541 config AT32AP700X_WDT
+1
drivers/watchdog/Makefile
··· 65 65 obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o 66 66 obj-$(CONFIG_MESON_WATCHDOG) += meson_wdt.o 67 67 obj-$(CONFIG_MEDIATEK_WATCHDOG) += mtk_wdt.o 68 + obj-$(CONFIG_DIGICOLOR_WATCHDOG) += digicolor_wdt.o 68 69 69 70 # AVR32 Architecture 70 71 obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
+205
drivers/watchdog/digicolor_wdt.c
··· 1 + /* 2 + * Watchdog driver for Conexant Digicolor 3 + * 4 + * Copyright (C) 2015 Paradox Innovation Ltd. 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms of the GNU General Public License as published by the 8 + * Free Software Foundation; either version 2 of the License, or (at your 9 + * option) any later version. 10 + */ 11 + 12 + #include <linux/types.h> 13 + #include <linux/module.h> 14 + #include <linux/io.h> 15 + #include <linux/delay.h> 16 + #include <linux/clk.h> 17 + #include <linux/watchdog.h> 18 + #include <linux/reboot.h> 19 + #include <linux/platform_device.h> 20 + #include <linux/of_address.h> 21 + 22 + #define TIMER_A_CONTROL 0 23 + #define TIMER_A_COUNT 4 24 + 25 + #define TIMER_A_ENABLE_COUNT BIT(0) 26 + #define TIMER_A_ENABLE_WATCHDOG BIT(1) 27 + 28 + struct dc_wdt { 29 + void __iomem *base; 30 + struct clk *clk; 31 + struct notifier_block restart_handler; 32 + spinlock_t lock; 33 + }; 34 + 35 + static unsigned timeout; 36 + module_param(timeout, uint, 0); 37 + MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); 38 + 39 + static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks) 40 + { 41 + unsigned long flags; 42 + 43 + spin_lock_irqsave(&wdt->lock, flags); 44 + 45 + writel_relaxed(0, wdt->base + TIMER_A_CONTROL); 46 + writel_relaxed(ticks, wdt->base + TIMER_A_COUNT); 47 + writel_relaxed(TIMER_A_ENABLE_COUNT | TIMER_A_ENABLE_WATCHDOG, 48 + wdt->base + TIMER_A_CONTROL); 49 + 50 + spin_unlock_irqrestore(&wdt->lock, flags); 51 + } 52 + 53 + static int dc_restart_handler(struct notifier_block *this, unsigned long mode, 54 + void *cmd) 55 + { 56 + struct dc_wdt *wdt = container_of(this, struct dc_wdt, restart_handler); 57 + 58 + dc_wdt_set(wdt, 1); 59 + /* wait for reset to assert... */ 60 + mdelay(500); 61 + 62 + return NOTIFY_DONE; 63 + } 64 + 65 + static int dc_wdt_start(struct watchdog_device *wdog) 66 + { 67 + struct dc_wdt *wdt = watchdog_get_drvdata(wdog); 68 + 69 + dc_wdt_set(wdt, wdog->timeout * clk_get_rate(wdt->clk)); 70 + 71 + return 0; 72 + } 73 + 74 + static int dc_wdt_stop(struct watchdog_device *wdog) 75 + { 76 + struct dc_wdt *wdt = watchdog_get_drvdata(wdog); 77 + 78 + writel_relaxed(0, wdt->base + TIMER_A_CONTROL); 79 + 80 + return 0; 81 + } 82 + 83 + static int dc_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t) 84 + { 85 + struct dc_wdt *wdt = watchdog_get_drvdata(wdog); 86 + 87 + dc_wdt_set(wdt, t * clk_get_rate(wdt->clk)); 88 + wdog->timeout = t; 89 + 90 + return 0; 91 + } 92 + 93 + static unsigned int dc_wdt_get_timeleft(struct watchdog_device *wdog) 94 + { 95 + struct dc_wdt *wdt = watchdog_get_drvdata(wdog); 96 + uint32_t count = readl_relaxed(wdt->base + TIMER_A_COUNT); 97 + 98 + return count / clk_get_rate(wdt->clk); 99 + } 100 + 101 + static struct watchdog_ops dc_wdt_ops = { 102 + .owner = THIS_MODULE, 103 + .start = dc_wdt_start, 104 + .stop = dc_wdt_stop, 105 + .set_timeout = dc_wdt_set_timeout, 106 + .get_timeleft = dc_wdt_get_timeleft, 107 + }; 108 + 109 + static struct watchdog_info dc_wdt_info = { 110 + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE 111 + | WDIOF_KEEPALIVEPING, 112 + .identity = "Conexant Digicolor Watchdog", 113 + }; 114 + 115 + static struct watchdog_device dc_wdt_wdd = { 116 + .info = &dc_wdt_info, 117 + .ops = &dc_wdt_ops, 118 + .min_timeout = 1, 119 + }; 120 + 121 + static int dc_wdt_probe(struct platform_device *pdev) 122 + { 123 + struct device *dev = &pdev->dev; 124 + struct device_node *np = dev->of_node; 125 + struct dc_wdt *wdt; 126 + int ret; 127 + 128 + wdt = devm_kzalloc(dev, sizeof(struct dc_wdt), GFP_KERNEL); 129 + if (!wdt) 130 + return -ENOMEM; 131 + platform_set_drvdata(pdev, wdt); 132 + 133 + wdt->base = of_iomap(np, 0); 134 + if (!wdt->base) { 135 + dev_err(dev, "Failed to remap watchdog regs"); 136 + return -ENODEV; 137 + } 138 + 139 + wdt->clk = devm_clk_get(&pdev->dev, NULL); 140 + if (IS_ERR(wdt->clk)) { 141 + ret = PTR_ERR(wdt->clk); 142 + goto err_iounmap; 143 + } 144 + dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk); 145 + dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout; 146 + 147 + spin_lock_init(&wdt->lock); 148 + 149 + watchdog_set_drvdata(&dc_wdt_wdd, wdt); 150 + watchdog_init_timeout(&dc_wdt_wdd, timeout, dev); 151 + ret = watchdog_register_device(&dc_wdt_wdd); 152 + if (ret) { 153 + dev_err(dev, "Failed to register watchdog device"); 154 + goto err_iounmap; 155 + } 156 + 157 + wdt->restart_handler.notifier_call = dc_restart_handler; 158 + wdt->restart_handler.priority = 128; 159 + ret = register_restart_handler(&wdt->restart_handler); 160 + if (ret) 161 + dev_warn(&pdev->dev, "cannot register restart handler\n"); 162 + 163 + return 0; 164 + 165 + err_iounmap: 166 + iounmap(wdt->base); 167 + return ret; 168 + } 169 + 170 + static int dc_wdt_remove(struct platform_device *pdev) 171 + { 172 + struct dc_wdt *wdt = platform_get_drvdata(pdev); 173 + 174 + unregister_restart_handler(&wdt->restart_handler); 175 + watchdog_unregister_device(&dc_wdt_wdd); 176 + iounmap(wdt->base); 177 + 178 + return 0; 179 + } 180 + 181 + static void dc_wdt_shutdown(struct platform_device *pdev) 182 + { 183 + dc_wdt_stop(&dc_wdt_wdd); 184 + } 185 + 186 + static const struct of_device_id dc_wdt_of_match[] = { 187 + { .compatible = "cnxt,cx92755-wdt", }, 188 + {}, 189 + }; 190 + MODULE_DEVICE_TABLE(of, dc_wdt_of_match); 191 + 192 + static struct platform_driver dc_wdt_driver = { 193 + .probe = dc_wdt_probe, 194 + .remove = dc_wdt_remove, 195 + .shutdown = dc_wdt_shutdown, 196 + .driver = { 197 + .name = "digicolor-wdt", 198 + .of_match_table = dc_wdt_of_match, 199 + }, 200 + }; 201 + module_platform_driver(dc_wdt_driver); 202 + 203 + MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); 204 + MODULE_DESCRIPTION("Driver for Conexant Digicolor watchdog timer"); 205 + MODULE_LICENSE("GPL");