Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Watchdog driver for Conexant Digicolor
0004  *
0005  * Copyright (C) 2015 Paradox Innovation Ltd.
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     /* wait for reset to assert... */
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");