0001
0002
0003
0004
0005
0006 #include <linux/clk.h>
0007 #include <linux/module.h>
0008 #include <linux/platform_device.h>
0009 #include <linux/watchdog.h>
0010 #include <loongson1.h>
0011
0012 #define DEFAULT_HEARTBEAT 30
0013
0014 static bool nowayout = WATCHDOG_NOWAYOUT;
0015 module_param(nowayout, bool, 0444);
0016
0017 static unsigned int heartbeat;
0018 module_param(heartbeat, uint, 0444);
0019
0020 struct ls1x_wdt_drvdata {
0021 void __iomem *base;
0022 struct clk *clk;
0023 unsigned long clk_rate;
0024 struct watchdog_device wdt;
0025 };
0026
0027 static int ls1x_wdt_ping(struct watchdog_device *wdt_dev)
0028 {
0029 struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
0030
0031 writel(0x1, drvdata->base + WDT_SET);
0032
0033 return 0;
0034 }
0035
0036 static int ls1x_wdt_set_timeout(struct watchdog_device *wdt_dev,
0037 unsigned int timeout)
0038 {
0039 struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
0040 unsigned int max_hw_heartbeat = wdt_dev->max_hw_heartbeat_ms / 1000;
0041 unsigned int counts;
0042
0043 wdt_dev->timeout = timeout;
0044
0045 counts = drvdata->clk_rate * min(timeout, max_hw_heartbeat);
0046 writel(counts, drvdata->base + WDT_TIMER);
0047
0048 return 0;
0049 }
0050
0051 static int ls1x_wdt_start(struct watchdog_device *wdt_dev)
0052 {
0053 struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
0054
0055 writel(0x1, drvdata->base + WDT_EN);
0056
0057 return 0;
0058 }
0059
0060 static int ls1x_wdt_stop(struct watchdog_device *wdt_dev)
0061 {
0062 struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);
0063
0064 writel(0x0, drvdata->base + WDT_EN);
0065
0066 return 0;
0067 }
0068
0069 static const struct watchdog_info ls1x_wdt_info = {
0070 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
0071 .identity = "Loongson1 Watchdog",
0072 };
0073
0074 static const struct watchdog_ops ls1x_wdt_ops = {
0075 .owner = THIS_MODULE,
0076 .start = ls1x_wdt_start,
0077 .stop = ls1x_wdt_stop,
0078 .ping = ls1x_wdt_ping,
0079 .set_timeout = ls1x_wdt_set_timeout,
0080 };
0081
0082 static void ls1x_clk_disable_unprepare(void *data)
0083 {
0084 clk_disable_unprepare(data);
0085 }
0086
0087 static int ls1x_wdt_probe(struct platform_device *pdev)
0088 {
0089 struct device *dev = &pdev->dev;
0090 struct ls1x_wdt_drvdata *drvdata;
0091 struct watchdog_device *ls1x_wdt;
0092 unsigned long clk_rate;
0093 int err;
0094
0095 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
0096 if (!drvdata)
0097 return -ENOMEM;
0098
0099 drvdata->base = devm_platform_ioremap_resource(pdev, 0);
0100 if (IS_ERR(drvdata->base))
0101 return PTR_ERR(drvdata->base);
0102
0103 drvdata->clk = devm_clk_get(dev, pdev->name);
0104 if (IS_ERR(drvdata->clk))
0105 return PTR_ERR(drvdata->clk);
0106
0107 err = clk_prepare_enable(drvdata->clk);
0108 if (err) {
0109 dev_err(dev, "clk enable failed\n");
0110 return err;
0111 }
0112 err = devm_add_action_or_reset(dev, ls1x_clk_disable_unprepare,
0113 drvdata->clk);
0114 if (err)
0115 return err;
0116
0117 clk_rate = clk_get_rate(drvdata->clk);
0118 if (!clk_rate)
0119 return -EINVAL;
0120 drvdata->clk_rate = clk_rate;
0121
0122 ls1x_wdt = &drvdata->wdt;
0123 ls1x_wdt->info = &ls1x_wdt_info;
0124 ls1x_wdt->ops = &ls1x_wdt_ops;
0125 ls1x_wdt->timeout = DEFAULT_HEARTBEAT;
0126 ls1x_wdt->min_timeout = 1;
0127 ls1x_wdt->max_hw_heartbeat_ms = U32_MAX / clk_rate * 1000;
0128 ls1x_wdt->parent = dev;
0129
0130 watchdog_init_timeout(ls1x_wdt, heartbeat, dev);
0131 watchdog_set_nowayout(ls1x_wdt, nowayout);
0132 watchdog_set_drvdata(ls1x_wdt, drvdata);
0133
0134 err = devm_watchdog_register_device(dev, &drvdata->wdt);
0135 if (err)
0136 return err;
0137
0138 platform_set_drvdata(pdev, drvdata);
0139
0140 dev_info(dev, "Loongson1 Watchdog driver registered\n");
0141
0142 return 0;
0143 }
0144
0145 static struct platform_driver ls1x_wdt_driver = {
0146 .probe = ls1x_wdt_probe,
0147 .driver = {
0148 .name = "ls1x-wdt",
0149 },
0150 };
0151
0152 module_platform_driver(ls1x_wdt_driver);
0153
0154 MODULE_AUTHOR("Yang Ling <gnaygnil@gmail.com>");
0155 MODULE_DESCRIPTION("Loongson1 Watchdog Driver");
0156 MODULE_LICENSE("GPL");