Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (c) 2016 Yang Ling <gnaygnil@gmail.com>
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");