brintos

brintos / linux-shallow public Read only

0
0
Text · 4.0 KiB · 0587ff4 Raw
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