digicolor_wdt.c (3876B)
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 24struct dc_wdt { 25 void __iomem *base; 26 struct clk *clk; 27 spinlock_t lock; 28}; 29 30static unsigned timeout; 31module_param(timeout, uint, 0); 32MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); 33 34static 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 48static 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 60static 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 69static 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 78static 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 88static 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 96static 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 105static const struct watchdog_info dc_wdt_info = { 106 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE 107 | WDIOF_KEEPALIVEPING, 108 .identity = "Conexant Digicolor Watchdog", 109}; 110 111static struct watchdog_device dc_wdt_wdd = { 112 .info = &dc_wdt_info, 113 .ops = &dc_wdt_ops, 114 .min_timeout = 1, 115}; 116 117static 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 146static const struct of_device_id dc_wdt_of_match[] = { 147 { .compatible = "cnxt,cx92755-wdt", }, 148 {}, 149}; 150MODULE_DEVICE_TABLE(of, dc_wdt_of_match); 151 152static 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}; 159module_platform_driver(dc_wdt_driver); 160 161MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); 162MODULE_DESCRIPTION("Driver for Conexant Digicolor watchdog timer"); 163MODULE_LICENSE("GPL");