Skip to content

Commit d9110a9

Browse files
committed
board: photonicat2: add photonicat usb watchdog driver
1 parent bbf12d5 commit d9110a9

File tree

2 files changed

+347
-1
lines changed

2 files changed

+347
-1
lines changed

patch/kernel/archive/rockchip64-6.18/board-photonicat2-add-photonicat-pm-driver.patch

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
22
From: HackingGate <[email protected]>
33
Date: Sat, 25 Oct 2025 23:05:48 +0900
4-
Subject: [PATCH] arm64: dts: rockchip: rk3576: add photonicat power manager
4+
Subject: [PATCH 1/2] arm64: rockchip: rk3576: add photonicat power manager
55

66
---
77
drivers/staging/Kconfig | 2 +
Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2+
From: HackingGate <[email protected]>
3+
Date: Sat, 25 Oct 2025 23:14:12 +0900
4+
Subject: [PATCH 2/2] arm64: rockchip: rk3576: add photonicat usb watchdog
5+
6+
---
7+
drivers/staging/Kconfig | 2 +
8+
drivers/staging/Makefile | 1 +
9+
drivers/staging/photonicat-usb-wdt/Kconfig | 10 +
10+
drivers/staging/photonicat-usb-wdt/Makefile | 1 +
11+
.../photonicat-usb-wdt/photonicat-usb-wdt.c | 278 ++++++++++++++++++
12+
5 files changed, 292 insertions(+)
13+
create mode 100644 drivers/staging/photonicat-usb-wdt/Kconfig
14+
create mode 100644 drivers/staging/photonicat-usb-wdt/Makefile
15+
create mode 100644 drivers/staging/photonicat-usb-wdt/photonicat-usb-wdt.c
16+
17+
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
18+
index 3fe77abdb77c..dc853abae222 100644
19+
--- a/drivers/staging/Kconfig
20+
+++ b/drivers/staging/Kconfig
21+
@@ -52,4 +52,6 @@ source "drivers/staging/gpib/Kconfig"
22+
23+
source "drivers/staging/photonicat-pm/Kconfig"
24+
25+
+source "drivers/staging/photonicat-usb-wdt/Kconfig"
26+
+
27+
endif # STAGING
28+
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
29+
index dce556962331..7c4b66691f17 100644
30+
--- a/drivers/staging/Makefile
31+
+++ b/drivers/staging/Makefile
32+
@@ -16,3 +16,4 @@ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
33+
obj-$(CONFIG_GPIB) += gpib/
34+
obj-$(CONFIG_RTL8723CS) += rtl8723cs/
35+
obj-$(CONFIG_PHOTONICAT_PM) += photonicat-pm/
36+
+obj-$(CONFIG_PHOTONICAT_USB_WDT) += photonicat-usb-wdt/
37+
diff --git a/drivers/staging/photonicat-usb-wdt/Kconfig b/drivers/staging/photonicat-usb-wdt/Kconfig
38+
new file mode 100644
39+
index 000000000000..d5a03b176f81
40+
--- /dev/null
41+
+++ b/drivers/staging/photonicat-usb-wdt/Kconfig
42+
@@ -0,0 +1,10 @@
43+
+# SPDX-License-Identifier: GPL-2.0
44+
+config PHOTONICAT_USB_WDT
45+
+ tristate "photonicat USB watchdog"
46+
+ depends on OF && GPIOLIB
47+
+ depends on USB
48+
+ select WATCHDOG_CORE
49+
+ help
50+
+ USB watchdog for photonicat board. Unless you have the platform,
51+
+ you will want to say 'N'.
52+
+
53+
diff --git a/drivers/staging/photonicat-usb-wdt/Makefile b/drivers/staging/photonicat-usb-wdt/Makefile
54+
new file mode 100644
55+
index 000000000000..b61ef615473d
56+
--- /dev/null
57+
+++ b/drivers/staging/photonicat-usb-wdt/Makefile
58+
@@ -0,0 +1 @@
59+
+obj-$(CONFIG_PHOTONICAT_USB_WDT) += photonicat-usb-wdt.o
60+
diff --git a/drivers/staging/photonicat-usb-wdt/photonicat-usb-wdt.c b/drivers/staging/photonicat-usb-wdt/photonicat-usb-wdt.c
61+
new file mode 100644
62+
index 000000000000..5e3a38a1cdb5
63+
--- /dev/null
64+
+++ b/drivers/staging/photonicat-usb-wdt/photonicat-usb-wdt.c
65+
@@ -0,0 +1,278 @@
66+
+#include <linux/module.h>
67+
+#include <linux/kernel.h>
68+
+#include <linux/init.h>
69+
+#include <linux/timer.h>
70+
+#include <linux/usb.h>
71+
+#include <linux/gpio/consumer.h>
72+
+#include <linux/delay.h>
73+
+#include <linux/workqueue.h>
74+
+#include <linux/of.h>
75+
+#include <linux/platform_device.h>
76+
+#include <linux/watchdog.h>
77+
+
78+
+#define PCAT_USB_WATCHDOG_TIMEOUT_DEFAULT 15
79+
+#define PCAT_USB_WATCHDOG_TIMEOUT_MIN 1
80+
+#define PCAT_USB_WATCHDOG_TIMEOUT_MAX 120
81+
+#define PCAT_USB_WATCHDOG_RESET_MS_DEFAULT 500
82+
+
83+
+static bool nowayout = WATCHDOG_NOWAYOUT;
84+
+module_param(nowayout, bool, 0);
85+
+MODULE_PARM_DESC(nowayout,
86+
+ "Watchdog cannot be stopped once started (default="
87+
+ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
88+
+
89+
+struct pcat_usb_watchdog_data {
90+
+ struct device *dev;
91+
+ int target_vid;
92+
+ int target_pid;
93+
+ u32 reset_ms;
94+
+ u32 scan_interval_ms;
95+
+ struct gpio_desc *reset_gpio;
96+
+ bool target_live;
97+
+ struct watchdog_device wdd;
98+
+ struct timer_list monitor_timer;
99+
+ struct work_struct gpio_work;
100+
+ struct notifier_block usb_nb;
101+
+};
102+
+
103+
+static void pcat_usb_watchdog_gpio_work(struct work_struct *work)
104+
+{
105+
+ struct pcat_usb_watchdog_data *data = container_of(work,
106+
+ struct
107+
+ pcat_usb_watchdog_data,
108+
+ gpio_work);
109+
+
110+
+ gpiod_set_value(data->reset_gpio, 0);
111+
+ msleep(data->reset_ms);
112+
+ gpiod_set_value(data->reset_gpio, 1);
113+
+}
114+
+
115+
+static int pcat_usb_watchdog_usb_notify(struct notifier_block *self,
116+
+ unsigned long action, void *dev)
117+
+{
118+
+ struct pcat_usb_watchdog_data *data = container_of(self,
119+
+ struct pcat_usb_watchdog_data,
120+
+ usb_nb);
121+
+ struct usb_device *udev = (struct usb_device *)dev;
122+
+
123+
+ if (!udev)
124+
+ return NOTIFY_OK;
125+
+
126+
+ if (le16_to_cpu(udev->descriptor.idVendor) == data->target_vid &&
127+
+ le16_to_cpu(udev->descriptor.idProduct) == data->target_pid) {
128+
+
129+
+ switch (action) {
130+
+ case USB_DEVICE_ADD:
131+
+ data->target_live = true;
132+
+ dev_info(data->dev, "Target device added.\n");
133+
+ break;
134+
+ case USB_DEVICE_REMOVE:
135+
+ data->target_live = false;
136+
+ dev_info(data->dev, "Target device removed!\n");
137+
+ break;
138+
+ }
139+
+ }
140+
+
141+
+ return NOTIFY_OK;
142+
+}
143+
+
144+
+static int pcat_usb_watchdog_ping(struct watchdog_device *wdd)
145+
+{
146+
+ /* Monitoring is handled by internal timer, this is just for userspace pings */
147+
+ return 0;
148+
+}
149+
+
150+
+static void pcat_usb_watchdog_monitor_callback(struct timer_list *timer)
151+
+{
152+
+ struct pcat_usb_watchdog_data *data = container_of(timer,
153+
+ struct pcat_usb_watchdog_data,
154+
+ monitor_timer);
155+
+
156+
+ if (!data->target_live) {
157+
+ dev_warn(data->dev, "Target device is not live, triggering reset!\n");
158+
+ schedule_work(&data->gpio_work);
159+
+ }
160+
+
161+
+ mod_timer(&data->monitor_timer,
162+
+ jiffies + msecs_to_jiffies(data->scan_interval_ms));
163+
+}
164+
+
165+
+static int pcat_usb_watchdog_start(struct watchdog_device *wdd)
166+
+{
167+
+ struct pcat_usb_watchdog_data *data = watchdog_get_drvdata(wdd);
168+
+
169+
+ dev_info(data->dev, "Watchdog started (timeout=%u sec)\n", wdd->timeout);
170+
+
171+
+ return 0;
172+
+}
173+
+
174+
+static int pcat_usb_watchdog_stop(struct watchdog_device *wdd)
175+
+{
176+
+ struct pcat_usb_watchdog_data *data = watchdog_get_drvdata(wdd);
177+
+
178+
+ dev_info(data->dev, "Watchdog stopped\n");
179+
+
180+
+ return 0;
181+
+}
182+
+
183+
+static const struct watchdog_info pcat_usb_watchdog_info = {
184+
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
185+
+ .identity = "Photonicat USB Watchdog",
186+
+};
187+
+
188+
+static const struct watchdog_ops pcat_usb_watchdog_ops = {
189+
+ .owner = THIS_MODULE,
190+
+ .start = pcat_usb_watchdog_start,
191+
+ .stop = pcat_usb_watchdog_stop,
192+
+ .ping = pcat_usb_watchdog_ping,
193+
+};
194+
+
195+
+static const struct of_device_id pcat_usb_watchdog_of_match[] = {
196+
+ {.compatible = "pcat-usb-watchdog" },
197+
+ { /* sentinel */ }
198+
+};
199+
+
200+
+MODULE_DEVICE_TABLE(of, pcat_usb_watchdog_of_match);
201+
+
202+
+static int pcat_usb_watchdog_probe(struct platform_device *pdev)
203+
+{
204+
+ struct pcat_usb_watchdog_data *wdt_data;
205+
+ struct device *dev = &pdev->dev;
206+
+ struct watchdog_device *wdd;
207+
+ u32 vid, pid, timeout_sec;
208+
+ int ret;
209+
+
210+
+ wdt_data = devm_kzalloc(dev, sizeof(*wdt_data), GFP_KERNEL);
211+
+ if (!wdt_data)
212+
+ return -ENOMEM;
213+
+
214+
+ wdt_data->dev = dev;
215+
+ platform_set_drvdata(pdev, wdt_data);
216+
+
217+
+ if (of_property_read_u32(dev->of_node, "target-vid", &vid)) {
218+
+ dev_err(dev, "No valid USB target-vid configured!\n");
219+
+ return -EINVAL;
220+
+ }
221+
+ wdt_data->target_vid = vid;
222+
+
223+
+ if (of_property_read_u32(dev->of_node, "target-pid", &pid)) {
224+
+ dev_err(dev, "No valid USB target-pid configured!\n");
225+
+ return -EINVAL;
226+
+ }
227+
+ wdt_data->target_pid = pid;
228+
+
229+
+ if (of_property_read_u32(dev->of_node, "reset-ms", &wdt_data->reset_ms)) {
230+
+ wdt_data->reset_ms = PCAT_USB_WATCHDOG_RESET_MS_DEFAULT;
231+
+ }
232+
+
233+
+ if (wdt_data->reset_ms < 1 || wdt_data->reset_ms > 10000) {
234+
+ dev_warn(dev, "reset-ms out of range, using default\n");
235+
+ wdt_data->reset_ms = PCAT_USB_WATCHDOG_RESET_MS_DEFAULT;
236+
+ }
237+
+
238+
+ wdt_data->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
239+
+ if (IS_ERR(wdt_data->reset_gpio)) {
240+
+ ret = PTR_ERR(wdt_data->reset_gpio);
241+
+ dev_err(dev, "Failed to get reset GPIO: %d\n", ret);
242+
+ return ret;
243+
+ }
244+
+
245+
+ INIT_WORK(&wdt_data->gpio_work, pcat_usb_watchdog_gpio_work);
246+
+
247+
+ /* Read scan interval from DT */
248+
+ if (of_property_read_u32(dev->of_node, "scan-interval", &timeout_sec)) {
249+
+ timeout_sec = PCAT_USB_WATCHDOG_TIMEOUT_DEFAULT * 1000;
250+
+ }
251+
+
252+
+ /* Convert to milliseconds if in seconds */
253+
+ if (timeout_sec < 1000) {
254+
+ timeout_sec = timeout_sec * 1000;
255+
+ }
256+
+
257+
+ if (timeout_sec < 1000 || timeout_sec > PCAT_USB_WATCHDOG_TIMEOUT_MAX * 1000) {
258+
+ dev_warn(dev, "scan-interval out of range, using default\n");
259+
+ timeout_sec = PCAT_USB_WATCHDOG_TIMEOUT_DEFAULT * 1000;
260+
+ }
261+
+
262+
+ wdt_data->scan_interval_ms = timeout_sec;
263+
+
264+
+ /* Setup watchdog device */
265+
+ wdd = &wdt_data->wdd;
266+
+ wdd->info = &pcat_usb_watchdog_info;
267+
+ wdd->ops = &pcat_usb_watchdog_ops;
268+
+ wdd->min_timeout = PCAT_USB_WATCHDOG_TIMEOUT_MIN;
269+
+ wdd->max_timeout = PCAT_USB_WATCHDOG_TIMEOUT_MAX;
270+
+ wdd->timeout = timeout_sec / 1000;
271+
+ wdd->parent = dev;
272+
+ watchdog_set_drvdata(wdd, wdt_data);
273+
+ watchdog_set_nowayout(wdd, nowayout);
274+
+ watchdog_stop_on_reboot(wdd);
275+
+
276+
+ /* Register USB notifier */
277+
+ wdt_data->usb_nb.notifier_call = pcat_usb_watchdog_usb_notify;
278+
+ usb_register_notify(&wdt_data->usb_nb);
279+
+
280+
+ /* Setup and start monitor timer */
281+
+ timer_setup(&wdt_data->monitor_timer, pcat_usb_watchdog_monitor_callback, 0);
282+
+ mod_timer(&wdt_data->monitor_timer, jiffies + msecs_to_jiffies(30000)); /* 30s grace */
283+
+
284+
+ /* Register watchdog device */
285+
+ ret = devm_watchdog_register_device(dev, wdd);
286+
+ if (ret) {
287+
+ dev_err(dev, "Failed to register watchdog device: %d\n", ret);
288+
+ timer_delete_sync(&wdt_data->monitor_timer);
289+
+ usb_unregister_notify(&wdt_data->usb_nb);
290+
+ return ret;
291+
+ }
292+
+
293+
+ dev_info(dev, "Photonicat USB watchdog initialized (VID:0x%04x PID:0x%04x, scan=%ums)\n",
294+
+ wdt_data->target_vid, wdt_data->target_pid, wdt_data->scan_interval_ms);
295+
+
296+
+ return 0;
297+
+}
298+
+
299+
+static void pcat_usb_watchdog_remove(struct platform_device *pdev)
300+
+{
301+
+ struct pcat_usb_watchdog_data *wdt_data = platform_get_drvdata(pdev);
302+
+
303+
+ /* Unregister USB notifier */
304+
+ usb_unregister_notify(&wdt_data->usb_nb);
305+
+
306+
+ /* Stop monitor timer */
307+
+ timer_delete_sync(&wdt_data->monitor_timer);
308+
+
309+
+ /* Cancel any pending work */
310+
+ cancel_work_sync(&wdt_data->gpio_work);
311+
+
312+
+ /* Watchdog device is automatically unregistered by devm */
313+
+ dev_info(wdt_data->dev, "Photonicat USB watchdog removed\n");
314+
+}
315+
+
316+
+static struct platform_driver pcat_usb_watchdog_driver = {
317+
+ .probe = pcat_usb_watchdog_probe,
318+
+ .remove = pcat_usb_watchdog_remove,
319+
+ .driver = {
320+
+ .name = "pcat-usb-watchdog",
321+
+ .of_match_table = pcat_usb_watchdog_of_match,
322+
+ },
323+
+};
324+
+
325+
+static int __init pcat_usb_watchdog_init(void)
326+
+{
327+
+ printk(KERN_INFO "usb_watchdog: Loading USB watchdog driver\n");
328+
+ return platform_driver_register(&pcat_usb_watchdog_driver);
329+
+}
330+
+
331+
+static void __exit pcat_usb_watchdog_exit(void)
332+
+{
333+
+ platform_driver_unregister(&pcat_usb_watchdog_driver);
334+
+}
335+
+
336+
+module_init(pcat_usb_watchdog_init);
337+
+module_exit(pcat_usb_watchdog_exit);
338+
+
339+
+MODULE_AUTHOR("Kyosuke Nekoyashiki <[email protected]>");
340+
+MODULE_AUTHOR("HackingGate <[email protected]>");
341+
+MODULE_DESCRIPTION("photonicat USB device watchdog");
342+
+MODULE_LICENSE("GPL v2");
343+
+MODULE_VERSION("1.0");
344+
--
345+
2.47.3
346+

0 commit comments

Comments
 (0)