1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * WDT driver for Lenovo SE10.
6 #include <linux/delay.h>
9 #include <linux/module.h>
10 #include <linux/moduleparam.h>
11 #include <linux/platform_device.h>
12 #include <linux/string.h>
13 #include <linux/types.h>
14 #include <linux/watchdog.h>
16 #define STATUS_PORT 0x6C
18 #define DATA_PORT 0x68
19 #define OUTBUF_FULL 0x01
20 #define INBUF_EMPTY 0x02
22 #define CFG_BRAM_LDN 0x10 /* for BRAM Base */
28 #define UNLOCK_KEY 0x87
31 #define CUS_WDT_SWI 0x1A
32 #define CUS_WDT_CFG 0x1B
33 #define CUS_WDT_FEED 0xB0
34 #define CUS_WDT_CNT 0xB1
36 #define DRVNAME "lenovo-se10-wdt"
38 /*The timeout range is 1-255 seconds*/
40 #define MAX_TIMEOUT 255
43 #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */
44 static unsigned short bram_base
;
45 static struct platform_device
*se10_pdev
;
47 static int timeout
; /* in seconds */
48 module_param(timeout
, int, 0);
49 MODULE_PARM_DESC(timeout
,
50 "Watchdog timeout in seconds. 1 <= timeout <= 255, default="
51 __MODULE_STRING(WATCHDOG_TIMEOUT
) ".");
53 static bool nowayout
= WATCHDOG_NOWAYOUT
;
54 module_param(nowayout
, bool, 0);
55 MODULE_PARM_DESC(nowayout
,
56 "Watchdog cannot be stopped once started (default="
57 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
60 struct watchdog_device wdd
;
63 static int set_bram(unsigned char offset
, unsigned char val
)
65 if (!request_muxed_region(bram_base
, BRAM_SIZE
, DRVNAME
))
67 outb(offset
, bram_base
);
68 outb(val
, bram_base
+ 1);
69 release_region(bram_base
, BRAM_SIZE
);
73 static void wait_for_buffer(int condition
)
78 if (inb(STATUS_PORT
) & condition
|| loop
> MAX_WAIT
)
81 usleep_range(10, 125);
85 static void send_cmd(unsigned char cmd
)
87 wait_for_buffer(INBUF_EMPTY
);
89 wait_for_buffer(INBUF_EMPTY
);
92 static void lpc_write(unsigned char index
, unsigned char data
)
94 outb(index
, CFG_PORT
);
95 outb(data
, CFG_PORT
+ 1);
98 static unsigned char lpc_read(unsigned char index
)
100 outb(index
, CFG_PORT
);
101 return inb(CFG_PORT
+ 1);
104 static int wdt_start(struct watchdog_device
*wdog
)
106 return set_bram(CUS_WDT_SWI
, 0x80);
109 static int wdt_set_timeout(struct watchdog_device
*wdog
, unsigned int timeout
)
111 wdog
->timeout
= timeout
;
112 return set_bram(CUS_WDT_CFG
, wdog
->timeout
);
115 static int wdt_stop(struct watchdog_device
*wdog
)
117 return set_bram(CUS_WDT_SWI
, 0);
120 static unsigned int wdt_get_time(struct watchdog_device
*wdog
)
124 if (!request_muxed_region(CMD_PORT
, CMD_SIZE
, DRVNAME
))
126 send_cmd(CUS_WDT_CNT
);
127 wait_for_buffer(OUTBUF_FULL
);
128 time
= inb(DATA_PORT
);
129 release_region(CMD_PORT
, CMD_SIZE
);
133 static int wdt_ping(struct watchdog_device
*wdog
)
135 if (!request_muxed_region(CMD_PORT
, CMD_SIZE
, DRVNAME
))
137 send_cmd(CUS_WDT_FEED
);
138 release_region(CMD_PORT
, CMD_SIZE
);
142 static const struct watchdog_info wdt_info
= {
143 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
,
144 .identity
= "Lenovo SE10 Watchdog",
147 static const struct watchdog_ops se10_wdt_ops
= {
148 .owner
= THIS_MODULE
,
152 .set_timeout
= wdt_set_timeout
,
153 .get_timeleft
= wdt_get_time
,
156 static unsigned int get_chipID(void)
158 unsigned char msb
, lsb
;
160 outb(UNLOCK_KEY
, CFG_PORT
);
161 outb(0x01, CFG_PORT
);
162 outb(0x55, CFG_PORT
);
163 outb(0x55, CFG_PORT
);
164 msb
= lpc_read(0x20);
165 lsb
= lpc_read(0x21);
166 outb(LOCK_KEY
, CFG_PORT
);
167 return (msb
* 256 + lsb
);
170 static int se10_wdt_probe(struct platform_device
*pdev
)
172 struct device
*dev
= &pdev
->dev
;
173 struct se10_wdt
*priv
;
174 unsigned int chip_id
;
177 if (!request_muxed_region(CFG_PORT
, CFG_SIZE
, DRVNAME
))
180 chip_id
= get_chipID();
181 if (chip_id
!= 0x5632) {
182 release_region(CFG_PORT
, CFG_SIZE
);
186 lpc_write(CFG_LDN
, CFG_BRAM_LDN
);
187 bram_base
= (lpc_read(0x60) << 8) | lpc_read(0x61);
188 release_region(CFG_PORT
, CFG_SIZE
);
190 dev_info(dev
, "Found Lenovo SE10 0x%x\n", chip_id
);
192 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
196 watchdog_set_drvdata(&priv
->wdd
, priv
);
198 priv
->wdd
.parent
= dev
;
199 priv
->wdd
.info
= &wdt_info
;
200 priv
->wdd
.ops
= &se10_wdt_ops
;
201 priv
->wdd
.timeout
= WATCHDOG_TIMEOUT
; /* Set default timeout */
202 priv
->wdd
.min_timeout
= MIN_TIMEOUT
;
203 priv
->wdd
.max_timeout
= MAX_TIMEOUT
;
205 set_bram(CUS_WDT_CFG
, WATCHDOG_TIMEOUT
); /* Set time to default */
207 watchdog_init_timeout(&priv
->wdd
, timeout
, dev
);
208 watchdog_set_nowayout(&priv
->wdd
, nowayout
);
209 watchdog_stop_on_reboot(&priv
->wdd
);
210 watchdog_stop_on_unregister(&priv
->wdd
);
212 ret
= devm_watchdog_register_device(dev
, &priv
->wdd
);
214 dev_dbg(&pdev
->dev
, "initialized. timeout=%d sec (nowayout=%d)\n",
215 priv
->wdd
.timeout
, nowayout
);
220 static struct platform_driver se10_wdt_driver
= {
224 .probe
= se10_wdt_probe
,
227 static int se10_create_platform_device(const struct dmi_system_id
*id
)
231 se10_pdev
= platform_device_alloc("lenovo-se10-wdt", -1);
235 err
= platform_device_add(se10_pdev
);
237 platform_device_put(se10_pdev
);
242 static const struct dmi_system_id se10_dmi_table
[] __initconst
= {
244 .ident
= "LENOVO-SE10",
246 DMI_MATCH(DMI_SYS_VENDOR
, "LENOVO"),
247 DMI_MATCH(DMI_PRODUCT_NAME
, "12NH"),
249 .callback
= se10_create_platform_device
,
252 .ident
= "LENOVO-SE10",
254 DMI_MATCH(DMI_SYS_VENDOR
, "LENOVO"),
255 DMI_MATCH(DMI_PRODUCT_NAME
, "12NJ"),
257 .callback
= se10_create_platform_device
,
260 .ident
= "LENOVO-SE10",
262 DMI_MATCH(DMI_SYS_VENDOR
, "LENOVO"),
263 DMI_MATCH(DMI_PRODUCT_NAME
, "12NK"),
265 .callback
= se10_create_platform_device
,
268 .ident
= "LENOVO-SE10",
270 DMI_MATCH(DMI_SYS_VENDOR
, "LENOVO"),
271 DMI_MATCH(DMI_PRODUCT_NAME
, "12NL"),
273 .callback
= se10_create_platform_device
,
276 .ident
= "LENOVO-SE10",
278 DMI_MATCH(DMI_SYS_VENDOR
, "LENOVO"),
279 DMI_MATCH(DMI_PRODUCT_NAME
, "12NM"),
281 .callback
= se10_create_platform_device
,
285 MODULE_DEVICE_TABLE(dmi
, se10_dmi_table
);
287 static int __init
se10_wdt_init(void)
289 if (!dmi_check_system(se10_dmi_table
))
292 return platform_driver_register(&se10_wdt_driver
);
295 static void __exit
se10_wdt_exit(void)
298 platform_device_unregister(se10_pdev
);
299 platform_driver_unregister(&se10_wdt_driver
);
302 module_init(se10_wdt_init
);
303 module_exit(se10_wdt_exit
);
305 MODULE_LICENSE("GPL");
306 MODULE_AUTHOR("David Ober<dober@lenovo.com>");
307 MODULE_AUTHOR("Mark Pearson <mpearson-lenovo@squebb.ca>");
308 MODULE_DESCRIPTION("WDT driver for Lenovo SE10");