171 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (c) 2016 Yang Ling <gnaygnil@gmail.com>4 */5 6#include <linux/clk.h>7#include <linux/io.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/platform_device.h>11#include <linux/watchdog.h>12 13/* Loongson 1 Watchdog Register Definitions */14#define WDT_EN 0x015#define WDT_TIMER 0x416#define WDT_SET 0x817 18#define DEFAULT_HEARTBEAT 3019 20static bool nowayout = WATCHDOG_NOWAYOUT;21module_param(nowayout, bool, 0444);22 23static unsigned int heartbeat;24module_param(heartbeat, uint, 0444);25 26struct ls1x_wdt_drvdata {27 void __iomem *base;28 struct clk *clk;29 unsigned long clk_rate;30 struct watchdog_device wdt;31};32 33static int ls1x_wdt_ping(struct watchdog_device *wdt_dev)34{35 struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);36 37 writel(0x1, drvdata->base + WDT_SET);38 39 return 0;40}41 42static int ls1x_wdt_set_timeout(struct watchdog_device *wdt_dev,43 unsigned int timeout)44{45 struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);46 unsigned int max_hw_heartbeat = wdt_dev->max_hw_heartbeat_ms / 1000;47 unsigned int counts;48 49 wdt_dev->timeout = timeout;50 51 counts = drvdata->clk_rate * min(timeout, max_hw_heartbeat);52 writel(counts, drvdata->base + WDT_TIMER);53 54 return 0;55}56 57static int ls1x_wdt_start(struct watchdog_device *wdt_dev)58{59 struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);60 61 writel(0x1, drvdata->base + WDT_EN);62 63 return 0;64}65 66static int ls1x_wdt_stop(struct watchdog_device *wdt_dev)67{68 struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);69 70 writel(0x0, drvdata->base + WDT_EN);71 72 return 0;73}74 75static int ls1x_wdt_restart(struct watchdog_device *wdt_dev,76 unsigned long action, void *data)77{78 struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev);79 80 writel(0x1, drvdata->base + WDT_EN);81 writel(0x1, drvdata->base + WDT_TIMER);82 writel(0x1, drvdata->base + WDT_SET);83 84 return 0;85}86 87static const struct watchdog_info ls1x_wdt_info = {88 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,89 .identity = "Loongson1 Watchdog",90};91 92static const struct watchdog_ops ls1x_wdt_ops = {93 .owner = THIS_MODULE,94 .start = ls1x_wdt_start,95 .stop = ls1x_wdt_stop,96 .ping = ls1x_wdt_ping,97 .set_timeout = ls1x_wdt_set_timeout,98 .restart = ls1x_wdt_restart,99};100 101static int ls1x_wdt_probe(struct platform_device *pdev)102{103 struct device *dev = &pdev->dev;104 struct ls1x_wdt_drvdata *drvdata;105 struct watchdog_device *ls1x_wdt;106 unsigned long clk_rate;107 int err;108 109 drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);110 if (!drvdata)111 return -ENOMEM;112 113 drvdata->base = devm_platform_ioremap_resource(pdev, 0);114 if (IS_ERR(drvdata->base))115 return PTR_ERR(drvdata->base);116 117 drvdata->clk = devm_clk_get_enabled(dev, NULL);118 if (IS_ERR(drvdata->clk))119 return PTR_ERR(drvdata->clk);120 121 clk_rate = clk_get_rate(drvdata->clk);122 if (!clk_rate)123 return -EINVAL;124 drvdata->clk_rate = clk_rate;125 126 ls1x_wdt = &drvdata->wdt;127 ls1x_wdt->info = &ls1x_wdt_info;128 ls1x_wdt->ops = &ls1x_wdt_ops;129 ls1x_wdt->timeout = DEFAULT_HEARTBEAT;130 ls1x_wdt->min_timeout = 1;131 ls1x_wdt->max_hw_heartbeat_ms = U32_MAX / clk_rate * 1000;132 ls1x_wdt->parent = dev;133 134 watchdog_init_timeout(ls1x_wdt, heartbeat, dev);135 watchdog_set_nowayout(ls1x_wdt, nowayout);136 watchdog_set_drvdata(ls1x_wdt, drvdata);137 138 err = devm_watchdog_register_device(dev, &drvdata->wdt);139 if (err)140 return err;141 142 platform_set_drvdata(pdev, drvdata);143 144 dev_info(dev, "Loongson1 Watchdog driver registered\n");145 146 return 0;147}148 149#ifdef CONFIG_OF150static const struct of_device_id ls1x_wdt_dt_ids[] = {151 { .compatible = "loongson,ls1b-wdt", },152 { .compatible = "loongson,ls1c-wdt", },153 { /* sentinel */ }154};155MODULE_DEVICE_TABLE(of, ls1x_wdt_dt_ids);156#endif157 158static struct platform_driver ls1x_wdt_driver = {159 .probe = ls1x_wdt_probe,160 .driver = {161 .name = "ls1x-wdt",162 .of_match_table = of_match_ptr(ls1x_wdt_dt_ids),163 },164};165 166module_platform_driver(ls1x_wdt_driver);167 168MODULE_AUTHOR("Yang Ling <gnaygnil@gmail.com>");169MODULE_DESCRIPTION("Loongson1 Watchdog Driver");170MODULE_LICENSE("GPL");171