0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/types.h>
0010 #include <linux/module.h>
0011 #include <linux/io.h>
0012 #include <linux/delay.h>
0013 #include <linux/clk.h>
0014 #include <linux/watchdog.h>
0015 #include <linux/platform_device.h>
0016 #include <linux/of_address.h>
0017
0018 #define TIMER_A_CONTROL 0
0019 #define TIMER_A_COUNT 4
0020
0021 #define TIMER_A_ENABLE_COUNT BIT(0)
0022 #define TIMER_A_ENABLE_WATCHDOG BIT(1)
0023
0024 struct dc_wdt {
0025 void __iomem *base;
0026 struct clk *clk;
0027 spinlock_t lock;
0028 };
0029
0030 static unsigned timeout;
0031 module_param(timeout, uint, 0);
0032 MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
0033
0034 static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks)
0035 {
0036 unsigned long flags;
0037
0038 spin_lock_irqsave(&wdt->lock, flags);
0039
0040 writel_relaxed(0, wdt->base + TIMER_A_CONTROL);
0041 writel_relaxed(ticks, wdt->base + TIMER_A_COUNT);
0042 writel_relaxed(TIMER_A_ENABLE_COUNT | TIMER_A_ENABLE_WATCHDOG,
0043 wdt->base + TIMER_A_CONTROL);
0044
0045 spin_unlock_irqrestore(&wdt->lock, flags);
0046 }
0047
0048 static int dc_wdt_restart(struct watchdog_device *wdog, unsigned long action,
0049 void *data)
0050 {
0051 struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
0052
0053 dc_wdt_set(wdt, 1);
0054
0055 mdelay(500);
0056
0057 return 0;
0058 }
0059
0060 static int dc_wdt_start(struct watchdog_device *wdog)
0061 {
0062 struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
0063
0064 dc_wdt_set(wdt, wdog->timeout * clk_get_rate(wdt->clk));
0065
0066 return 0;
0067 }
0068
0069 static int dc_wdt_stop(struct watchdog_device *wdog)
0070 {
0071 struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
0072
0073 writel_relaxed(0, wdt->base + TIMER_A_CONTROL);
0074
0075 return 0;
0076 }
0077
0078 static int dc_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t)
0079 {
0080 struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
0081
0082 dc_wdt_set(wdt, t * clk_get_rate(wdt->clk));
0083 wdog->timeout = t;
0084
0085 return 0;
0086 }
0087
0088 static unsigned int dc_wdt_get_timeleft(struct watchdog_device *wdog)
0089 {
0090 struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
0091 uint32_t count = readl_relaxed(wdt->base + TIMER_A_COUNT);
0092
0093 return count / clk_get_rate(wdt->clk);
0094 }
0095
0096 static const struct watchdog_ops dc_wdt_ops = {
0097 .owner = THIS_MODULE,
0098 .start = dc_wdt_start,
0099 .stop = dc_wdt_stop,
0100 .set_timeout = dc_wdt_set_timeout,
0101 .get_timeleft = dc_wdt_get_timeleft,
0102 .restart = dc_wdt_restart,
0103 };
0104
0105 static const struct watchdog_info dc_wdt_info = {
0106 .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE
0107 | WDIOF_KEEPALIVEPING,
0108 .identity = "Conexant Digicolor Watchdog",
0109 };
0110
0111 static struct watchdog_device dc_wdt_wdd = {
0112 .info = &dc_wdt_info,
0113 .ops = &dc_wdt_ops,
0114 .min_timeout = 1,
0115 };
0116
0117 static int dc_wdt_probe(struct platform_device *pdev)
0118 {
0119 struct device *dev = &pdev->dev;
0120 struct dc_wdt *wdt;
0121
0122 wdt = devm_kzalloc(dev, sizeof(struct dc_wdt), GFP_KERNEL);
0123 if (!wdt)
0124 return -ENOMEM;
0125
0126 wdt->base = devm_platform_ioremap_resource(pdev, 0);
0127 if (IS_ERR(wdt->base))
0128 return PTR_ERR(wdt->base);
0129
0130 wdt->clk = devm_clk_get(dev, NULL);
0131 if (IS_ERR(wdt->clk))
0132 return PTR_ERR(wdt->clk);
0133 dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk);
0134 dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout;
0135 dc_wdt_wdd.parent = dev;
0136
0137 spin_lock_init(&wdt->lock);
0138
0139 watchdog_set_drvdata(&dc_wdt_wdd, wdt);
0140 watchdog_set_restart_priority(&dc_wdt_wdd, 128);
0141 watchdog_init_timeout(&dc_wdt_wdd, timeout, dev);
0142 watchdog_stop_on_reboot(&dc_wdt_wdd);
0143 return devm_watchdog_register_device(dev, &dc_wdt_wdd);
0144 }
0145
0146 static const struct of_device_id dc_wdt_of_match[] = {
0147 { .compatible = "cnxt,cx92755-wdt", },
0148 {},
0149 };
0150 MODULE_DEVICE_TABLE(of, dc_wdt_of_match);
0151
0152 static struct platform_driver dc_wdt_driver = {
0153 .probe = dc_wdt_probe,
0154 .driver = {
0155 .name = "digicolor-wdt",
0156 .of_match_table = dc_wdt_of_match,
0157 },
0158 };
0159 module_platform_driver(dc_wdt_driver);
0160
0161 MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
0162 MODULE_DESCRIPTION("Driver for Conexant Digicolor watchdog timer");
0163 MODULE_LICENSE("GPL");