diff options
author | Yang Ling <gnaygnil@gmail.com> | 2016-12-07 11:10:58 -0500 |
---|---|---|
committer | Guenter Roeck <linux@roeck-us.net> | 2016-12-16 09:53:55 -0500 |
commit | 1d8565ee4f5bd9fccb738e53d6b9fc7a559f7d2b (patch) | |
tree | 85a2c31f943e2e578d248b0dfc4dc674a65cbd57 | |
parent | b6621df5c87603310c3f94903bb30adbfeb9aa69 (diff) |
watchdog: loongson1: Add Loongson1 SoC watchdog driver
Add watchdog timer specific driver for Loongson1 SoC.
Signed-off-by: Yang Ling <gnaygnil@gmail.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
-rw-r--r-- | drivers/watchdog/Kconfig | 7 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/loongson1_wdt.c | 170 |
3 files changed, 178 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index a05afcbec778..f1de1214f10f 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig | |||
@@ -1514,6 +1514,13 @@ config LANTIQ_WDT | |||
1514 | help | 1514 | help |
1515 | Hardware driver for the Lantiq SoC Watchdog Timer. | 1515 | Hardware driver for the Lantiq SoC Watchdog Timer. |
1516 | 1516 | ||
1517 | config LOONGSON1_WDT | ||
1518 | tristate "Loongson1 SoC hardware watchdog" | ||
1519 | depends on MACH_LOONGSON32 | ||
1520 | select WATCHDOG_CORE | ||
1521 | help | ||
1522 | Hardware driver for the Loongson1 SoC Watchdog Timer. | ||
1523 | |||
1517 | config RALINK_WDT | 1524 | config RALINK_WDT |
1518 | tristate "Ralink SoC watchdog" | 1525 | tristate "Ralink SoC watchdog" |
1519 | select WATCHDOG_CORE | 1526 | select WATCHDOG_CORE |
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index caa9f4aa492a..0c3d35e3c334 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -163,6 +163,7 @@ obj-$(CONFIG_TXX9_WDT) += txx9wdt.o | |||
163 | obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o | 163 | obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o |
164 | octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o | 164 | octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o |
165 | obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o | 165 | obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o |
166 | obj-$(CONFIG_LOONGSON1_WDT) += loongson1_wdt.o | ||
166 | obj-$(CONFIG_RALINK_WDT) += rt2880_wdt.o | 167 | obj-$(CONFIG_RALINK_WDT) += rt2880_wdt.o |
167 | obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o | 168 | obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o |
168 | obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o | 169 | obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o |
diff --git a/drivers/watchdog/loongson1_wdt.c b/drivers/watchdog/loongson1_wdt.c new file mode 100644 index 000000000000..3aee50c64a36 --- /dev/null +++ b/drivers/watchdog/loongson1_wdt.c | |||
@@ -0,0 +1,170 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Yang Ling <gnaygnil@gmail.com> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License as published by the | ||
6 | * Free Software Foundation; either version 2 of the License, or (at your | ||
7 | * option) any later version. | ||
8 | */ | ||
9 | |||
10 | #include <linux/clk.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/platform_device.h> | ||
13 | #include <linux/watchdog.h> | ||
14 | #include <loongson1.h> | ||
15 | |||
16 | #define DEFAULT_HEARTBEAT 30 | ||
17 | |||
18 | static bool nowayout = WATCHDOG_NOWAYOUT; | ||
19 | module_param(nowayout, bool, 0444); | ||
20 | |||
21 | static unsigned int heartbeat; | ||
22 | module_param(heartbeat, uint, 0444); | ||
23 | |||
24 | struct ls1x_wdt_drvdata { | ||
25 | void __iomem *base; | ||
26 | struct clk *clk; | ||
27 | unsigned long clk_rate; | ||
28 | struct watchdog_device wdt; | ||
29 | }; | ||
30 | |||
31 | static int ls1x_wdt_ping(struct watchdog_device *wdt_dev) | ||
32 | { | ||
33 | struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); | ||
34 | |||
35 | writel(0x1, drvdata->base + WDT_SET); | ||
36 | |||
37 | return 0; | ||
38 | } | ||
39 | |||
40 | static int ls1x_wdt_set_timeout(struct watchdog_device *wdt_dev, | ||
41 | unsigned int timeout) | ||
42 | { | ||
43 | struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); | ||
44 | unsigned int max_hw_heartbeat = wdt_dev->max_hw_heartbeat_ms / 1000; | ||
45 | unsigned int counts; | ||
46 | |||
47 | wdt_dev->timeout = timeout; | ||
48 | |||
49 | counts = drvdata->clk_rate * min(timeout, max_hw_heartbeat); | ||
50 | writel(counts, drvdata->base + WDT_TIMER); | ||
51 | |||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static int ls1x_wdt_start(struct watchdog_device *wdt_dev) | ||
56 | { | ||
57 | struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); | ||
58 | |||
59 | writel(0x1, drvdata->base + WDT_EN); | ||
60 | |||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | static int ls1x_wdt_stop(struct watchdog_device *wdt_dev) | ||
65 | { | ||
66 | struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); | ||
67 | |||
68 | writel(0x0, drvdata->base + WDT_EN); | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static const struct watchdog_info ls1x_wdt_info = { | ||
74 | .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, | ||
75 | .identity = "Loongson1 Watchdog", | ||
76 | }; | ||
77 | |||
78 | static const struct watchdog_ops ls1x_wdt_ops = { | ||
79 | .owner = THIS_MODULE, | ||
80 | .start = ls1x_wdt_start, | ||
81 | .stop = ls1x_wdt_stop, | ||
82 | .ping = ls1x_wdt_ping, | ||
83 | .set_timeout = ls1x_wdt_set_timeout, | ||
84 | }; | ||
85 | |||
86 | static int ls1x_wdt_probe(struct platform_device *pdev) | ||
87 | { | ||
88 | struct ls1x_wdt_drvdata *drvdata; | ||
89 | struct watchdog_device *ls1x_wdt; | ||
90 | unsigned long clk_rate; | ||
91 | struct resource *res; | ||
92 | int err; | ||
93 | |||
94 | drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); | ||
95 | if (!drvdata) | ||
96 | return -ENOMEM; | ||
97 | |||
98 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
99 | drvdata->base = devm_ioremap_resource(&pdev->dev, res); | ||
100 | if (IS_ERR(drvdata->base)) | ||
101 | return PTR_ERR(drvdata->base); | ||
102 | |||
103 | drvdata->clk = devm_clk_get(&pdev->dev, pdev->name); | ||
104 | if (IS_ERR(drvdata->clk)) | ||
105 | return PTR_ERR(drvdata->clk); | ||
106 | |||
107 | err = clk_prepare_enable(drvdata->clk); | ||
108 | if (err) { | ||
109 | dev_err(&pdev->dev, "clk enable failed\n"); | ||
110 | return err; | ||
111 | } | ||
112 | |||
113 | clk_rate = clk_get_rate(drvdata->clk); | ||
114 | if (!clk_rate) { | ||
115 | err = -EINVAL; | ||
116 | goto err0; | ||
117 | } | ||
118 | drvdata->clk_rate = clk_rate; | ||
119 | |||
120 | ls1x_wdt = &drvdata->wdt; | ||
121 | ls1x_wdt->info = &ls1x_wdt_info; | ||
122 | ls1x_wdt->ops = &ls1x_wdt_ops; | ||
123 | ls1x_wdt->timeout = DEFAULT_HEARTBEAT; | ||
124 | ls1x_wdt->min_timeout = 1; | ||
125 | ls1x_wdt->max_hw_heartbeat_ms = U32_MAX / clk_rate * 1000; | ||
126 | ls1x_wdt->parent = &pdev->dev; | ||
127 | |||
128 | watchdog_init_timeout(ls1x_wdt, heartbeat, &pdev->dev); | ||
129 | watchdog_set_nowayout(ls1x_wdt, nowayout); | ||
130 | watchdog_set_drvdata(ls1x_wdt, drvdata); | ||
131 | |||
132 | err = watchdog_register_device(&drvdata->wdt); | ||
133 | if (err) { | ||
134 | dev_err(&pdev->dev, "failed to register watchdog device\n"); | ||
135 | goto err0; | ||
136 | } | ||
137 | |||
138 | platform_set_drvdata(pdev, drvdata); | ||
139 | |||
140 | dev_info(&pdev->dev, "Loongson1 Watchdog driver registered\n"); | ||
141 | |||
142 | return 0; | ||
143 | err0: | ||
144 | clk_disable_unprepare(drvdata->clk); | ||
145 | return err; | ||
146 | } | ||
147 | |||
148 | static int ls1x_wdt_remove(struct platform_device *pdev) | ||
149 | { | ||
150 | struct ls1x_wdt_drvdata *drvdata = platform_get_drvdata(pdev); | ||
151 | |||
152 | watchdog_unregister_device(&drvdata->wdt); | ||
153 | clk_disable_unprepare(drvdata->clk); | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static struct platform_driver ls1x_wdt_driver = { | ||
159 | .probe = ls1x_wdt_probe, | ||
160 | .remove = ls1x_wdt_remove, | ||
161 | .driver = { | ||
162 | .name = "ls1x-wdt", | ||
163 | }, | ||
164 | }; | ||
165 | |||
166 | module_platform_driver(ls1x_wdt_driver); | ||
167 | |||
168 | MODULE_AUTHOR("Yang Ling <gnaygnil@gmail.com>"); | ||
169 | MODULE_DESCRIPTION("Loongson1 Watchdog Driver"); | ||
170 | MODULE_LICENSE("GPL"); | ||