Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

watchdog: loongson1: Add Loongson-2k0300 watchdog support

According to the manual, the Loongson-2K0300 watchdog is similar to the
Loongson-1, except for some register offsets and inconsistent register
bit definitions. Separate definitions via driver_data suffice.

Co-developed-by: Xiaochuang Mao <maoxiaochuan@loongson.cn>
Signed-off-by: Xiaochuang Mao <maoxiaochuan@loongson.cn>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
Reviewed-by: Huacai Chen <chenhuacai@loongson.cn>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>

authored by

Binbin Zhou and committed by
Wim Van Sebroeck
e0c50cdd e4948e80

+62 -15
+2 -2
drivers/watchdog/Kconfig
··· 1984 1984 1985 1985 config LOONGSON1_WDT 1986 1986 tristate "Loongson1 SoC hardware watchdog" 1987 - depends on MACH_LOONGSON32 || COMPILE_TEST 1987 + depends on MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST 1988 1988 select WATCHDOG_CORE 1989 1989 help 1990 - Hardware driver for the Loongson1 SoC Watchdog Timer. 1990 + Hardware driver for the Loongson family Watchdog Timer. 1991 1991 1992 1992 config RALINK_WDT 1993 1993 tristate "Ralink SoC watchdog"
+60 -13
drivers/watchdog/loongson1_wdt.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-or-later 2 2 /* 3 3 * Copyright (c) 2016 Yang Ling <gnaygnil@gmail.com> 4 + * Copyright (C) 2025 Binbin Zhou <zhoubinbin@loongson.cn> 4 5 */ 5 6 6 7 #include <linux/clk.h> ··· 11 10 #include <linux/platform_device.h> 12 11 #include <linux/watchdog.h> 13 12 14 - /* Loongson 1 Watchdog Register Definitions */ 13 + /* Loongson Watchdog Register Definitions */ 15 14 #define WDT_EN 0x0 16 - #define WDT_TIMER 0x4 17 - #define WDT_SET 0x8 18 15 19 16 #define DEFAULT_HEARTBEAT 30 20 17 ··· 26 27 MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (default=" 27 28 __MODULE_STRING(DEFAULT_HEARTBEAT) ")"); 28 29 30 + struct ls1x_wdt_pdata { 31 + u32 timer_offset; 32 + u32 set_offset; 33 + u32 wdt_en_bit; 34 + }; 35 + 36 + static const struct ls1x_wdt_pdata ls1b_wdt_pdata = { 37 + .timer_offset = 0x4, 38 + .set_offset = 0x8, 39 + .wdt_en_bit = BIT(0), 40 + }; 41 + 42 + static const struct ls1x_wdt_pdata ls2k0300_wdt_pdata = { 43 + .timer_offset = 0x8, 44 + .set_offset = 0x4, 45 + .wdt_en_bit = BIT(1), 46 + }; 47 + 29 48 struct ls1x_wdt_drvdata { 30 49 void __iomem *base; 31 50 struct clk *clk; 32 51 unsigned long clk_rate; 33 52 struct watchdog_device wdt; 53 + const struct ls1x_wdt_pdata *pdata; 34 54 }; 35 55 36 56 static int ls1x_wdt_ping(struct watchdog_device *wdt_dev) 37 57 { 38 58 struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); 39 59 40 - writel(0x1, drvdata->base + WDT_SET); 60 + writel(0x1, drvdata->base + drvdata->pdata->set_offset); 41 61 42 62 return 0; 43 63 } ··· 71 53 wdt_dev->timeout = timeout; 72 54 73 55 counts = drvdata->clk_rate * min(timeout, max_hw_heartbeat); 74 - writel(counts, drvdata->base + WDT_TIMER); 56 + writel(counts, drvdata->base + drvdata->pdata->timer_offset); 75 57 76 58 return 0; 77 59 } ··· 80 62 { 81 63 struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); 82 64 83 - writel(0x1, drvdata->base + WDT_EN); 65 + writel(drvdata->pdata->wdt_en_bit, drvdata->base + WDT_EN); 84 66 85 67 return 0; 86 68 } ··· 88 70 static int ls1x_wdt_stop(struct watchdog_device *wdt_dev) 89 71 { 90 72 struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); 73 + u32 val = readl(drvdata->base + WDT_EN); 91 74 92 - writel(0x0, drvdata->base + WDT_EN); 75 + val &= ~(drvdata->pdata->wdt_en_bit); 76 + writel(val, drvdata->base + WDT_EN); 93 77 94 78 return 0; 95 79 } ··· 101 81 { 102 82 struct ls1x_wdt_drvdata *drvdata = watchdog_get_drvdata(wdt_dev); 103 83 104 - writel(0x1, drvdata->base + WDT_EN); 105 - writel(0x1, drvdata->base + WDT_TIMER); 106 - writel(0x1, drvdata->base + WDT_SET); 84 + writel(drvdata->pdata->wdt_en_bit, drvdata->base + WDT_EN); 85 + writel(0x1, drvdata->base + drvdata->pdata->timer_offset); 86 + writel(0x1, drvdata->base + drvdata->pdata->set_offset); 107 87 108 88 return 0; 109 89 } ··· 134 114 return -ENOMEM; 135 115 platform_set_drvdata(pdev, drvdata); 136 116 117 + drvdata->pdata = of_device_get_match_data(dev); 118 + 137 119 drvdata->base = devm_platform_ioremap_resource(pdev, 0); 138 120 if (IS_ERR(drvdata->base)) 139 121 return PTR_ERR(drvdata->base); ··· 164 142 return devm_watchdog_register_device(dev, &drvdata->wdt); 165 143 } 166 144 145 + static int ls1x_wdt_resume(struct device *dev) 146 + { 147 + struct ls1x_wdt_drvdata *data = dev_get_drvdata(dev); 148 + 149 + if (watchdog_active(&data->wdt)) 150 + ls1x_wdt_start(&data->wdt); 151 + 152 + return 0; 153 + } 154 + 155 + static int ls1x_wdt_suspend(struct device *dev) 156 + { 157 + struct ls1x_wdt_drvdata *data = dev_get_drvdata(dev); 158 + 159 + if (watchdog_active(&data->wdt)) 160 + ls1x_wdt_stop(&data->wdt); 161 + 162 + return 0; 163 + } 164 + 165 + static DEFINE_SIMPLE_DEV_PM_OPS(ls1x_wdt_pm_ops, ls1x_wdt_suspend, ls1x_wdt_resume); 166 + 167 167 static const struct of_device_id ls1x_wdt_dt_ids[] = { 168 - { .compatible = "loongson,ls1b-wdt", }, 169 - { .compatible = "loongson,ls1c-wdt", }, 168 + { .compatible = "loongson,ls1b-wdt", .data = &ls1b_wdt_pdata }, 169 + { .compatible = "loongson,ls1c-wdt", .data = &ls1b_wdt_pdata }, 170 + { .compatible = "loongson,ls2k0300-wdt", .data = &ls2k0300_wdt_pdata }, 170 171 { /* sentinel */ } 171 172 }; 172 173 MODULE_DEVICE_TABLE(of, ls1x_wdt_dt_ids); ··· 199 154 .driver = { 200 155 .name = "ls1x-wdt", 201 156 .of_match_table = ls1x_wdt_dt_ids, 157 + .pm = pm_ptr(&ls1x_wdt_pm_ops), 202 158 }, 203 159 }; 204 160 205 161 module_platform_driver(ls1x_wdt_driver); 206 162 207 163 MODULE_AUTHOR("Yang Ling <gnaygnil@gmail.com>"); 208 - MODULE_DESCRIPTION("Loongson1 Watchdog Driver"); 164 + MODULE_AUTHOR("Binbin Zhou <zhoubinbin@loongson.cn>"); 165 + MODULE_DESCRIPTION("Loongson Watchdog Driver"); 209 166 MODULE_LICENSE("GPL");