11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
296cb4eb0SGiel van Schijndel /***************************************************************************
396cb4eb0SGiel van Schijndel * Copyright (C) 2006 by Hans Edgington <hans@edgington.nl> *
496cb4eb0SGiel van Schijndel * Copyright (C) 2007-2009 Hans de Goede <hdegoede@redhat.com> *
596cb4eb0SGiel van Schijndel * Copyright (C) 2010 Giel van Schijndel <me@mortis.eu> *
696cb4eb0SGiel van Schijndel * *
796cb4eb0SGiel van Schijndel ***************************************************************************/
896cb4eb0SGiel van Schijndel
927c766aaSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1027c766aaSJoe Perches
1196cb4eb0SGiel van Schijndel #include <linux/err.h>
1296cb4eb0SGiel van Schijndel #include <linux/init.h>
1396cb4eb0SGiel van Schijndel #include <linux/io.h>
1496cb4eb0SGiel van Schijndel #include <linux/ioport.h>
1596cb4eb0SGiel van Schijndel #include <linux/module.h>
1627e0fe00SAhmad Fatoum #include <linux/platform_device.h>
1796cb4eb0SGiel van Schijndel #include <linux/watchdog.h>
1896cb4eb0SGiel van Schijndel
1996cb4eb0SGiel van Schijndel #define DRVNAME "f71808e_wdt"
2096cb4eb0SGiel van Schijndel
2196cb4eb0SGiel van Schijndel #define SIO_F71808FG_LD_WDT 0x07 /* Watchdog timer logical device */
2296cb4eb0SGiel van Schijndel #define SIO_UNLOCK_KEY 0x87 /* Key to enable Super-I/O */
2385c130a8SKnud Poulsen #define SIO_LOCK_KEY 0xAA /* Key to disable Super-I/O */
2496cb4eb0SGiel van Schijndel
2596cb4eb0SGiel van Schijndel #define SIO_REG_LDSEL 0x07 /* Logical device select */
2696cb4eb0SGiel van Schijndel #define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */
2796cb4eb0SGiel van Schijndel #define SIO_REG_DEVREV 0x22 /* Device revision */
2896cb4eb0SGiel van Schijndel #define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */
29ca2fc5efSJaret Cantu #define SIO_REG_CLOCK_SEL 0x26 /* Clock select */
307977ff6eSLutz Ballaschke #define SIO_REG_ROM_ADDR_SEL 0x27 /* ROM address select */
3114b24a88SJi-Ze Hong (Peter Hong) #define SIO_F81866_REG_PORT_SEL 0x27 /* F81866 Multi-Function Register */
32ca2fc5efSJaret Cantu #define SIO_REG_TSI_LEVEL_SEL 0x28 /* TSI Level select */
33f9a9f096SLutz Ballaschke #define SIO_REG_MFUNCT1 0x29 /* Multi function select 1 */
34f9a9f096SLutz Ballaschke #define SIO_REG_MFUNCT2 0x2a /* Multi function select 2 */
35f9a9f096SLutz Ballaschke #define SIO_REG_MFUNCT3 0x2b /* Multi function select 3 */
3614b24a88SJi-Ze Hong (Peter Hong) #define SIO_F81866_REG_GPIO1 0x2c /* F81866 GPIO1 Enable Register */
3796cb4eb0SGiel van Schijndel #define SIO_REG_ENABLE 0x30 /* Logical device enable */
3896cb4eb0SGiel van Schijndel #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */
3996cb4eb0SGiel van Schijndel
4096cb4eb0SGiel van Schijndel #define SIO_FINTEK_ID 0x1934 /* Manufacturers ID */
4196cb4eb0SGiel van Schijndel #define SIO_F71808_ID 0x0901 /* Chipset ID */
4296cb4eb0SGiel van Schijndel #define SIO_F71858_ID 0x0507 /* Chipset ID */
4396cb4eb0SGiel van Schijndel #define SIO_F71862_ID 0x0601 /* Chipset ID */
44166fbcf8SMaciej S. Szmigiero #define SIO_F71868_ID 0x1106 /* Chipset ID */
45df278dacSMichel Arboi #define SIO_F71869_ID 0x0814 /* Chipset ID */
463017020dSJustin Wheeler #define SIO_F71869A_ID 0x1007 /* Chipset ID */
4796cb4eb0SGiel van Schijndel #define SIO_F71882_ID 0x0541 /* Chipset ID */
4896cb4eb0SGiel van Schijndel #define SIO_F71889_ID 0x0723 /* Chipset ID */
49ca2fc5efSJaret Cantu #define SIO_F81803_ID 0x1210 /* Chipset ID */
50ea0c03e8SKnud Poulsen #define SIO_F81865_ID 0x0704 /* Chipset ID */
5114b24a88SJi-Ze Hong (Peter Hong) #define SIO_F81866_ID 0x1010 /* Chipset ID */
52cea62f9fSAaeonIot #define SIO_F81966_ID 0x1502 /* F81804 chipset ID, same for f81966 */
5396cb4eb0SGiel van Schijndel
5496cb4eb0SGiel van Schijndel #define F71808FG_REG_WDO_CONF 0xf0
5596cb4eb0SGiel van Schijndel #define F71808FG_REG_WDT_CONF 0xf5
5696cb4eb0SGiel van Schijndel #define F71808FG_REG_WD_TIME 0xf6
5796cb4eb0SGiel van Schijndel
5896cb4eb0SGiel van Schijndel #define F71808FG_FLAG_WDOUT_EN 7
5996cb4eb0SGiel van Schijndel
60b97cb21aSKnud Poulsen #define F71808FG_FLAG_WDTMOUT_STS 6
6196cb4eb0SGiel van Schijndel #define F71808FG_FLAG_WD_EN 5
6296cb4eb0SGiel van Schijndel #define F71808FG_FLAG_WD_PULSE 4
6396cb4eb0SGiel van Schijndel #define F71808FG_FLAG_WD_UNIT 3
6496cb4eb0SGiel van Schijndel
65ea0c03e8SKnud Poulsen #define F81865_REG_WDO_CONF 0xfa
66ea0c03e8SKnud Poulsen #define F81865_FLAG_WDOUT_EN 0
67ea0c03e8SKnud Poulsen
6896cb4eb0SGiel van Schijndel /* Default values */
6996cb4eb0SGiel van Schijndel #define WATCHDOG_TIMEOUT 60 /* 1 minute default timeout */
7096cb4eb0SGiel van Schijndel #define WATCHDOG_MAX_TIMEOUT (60 * 255)
7196cb4eb0SGiel van Schijndel #define WATCHDOG_PULSE_WIDTH 125 /* 125 ms, default pulse width for
7296cb4eb0SGiel van Schijndel watchdog signal */
737977ff6eSLutz Ballaschke #define WATCHDOG_F71862FG_PIN 63 /* default watchdog reset output
747977ff6eSLutz Ballaschke pin number 63 */
7596cb4eb0SGiel van Schijndel
7696cb4eb0SGiel van Schijndel static unsigned short force_id;
7796cb4eb0SGiel van Schijndel module_param(force_id, ushort, 0);
7896cb4eb0SGiel van Schijndel MODULE_PARM_DESC(force_id, "Override the detected device ID");
7996cb4eb0SGiel van Schijndel
80f9a9f096SLutz Ballaschke static int timeout = WATCHDOG_TIMEOUT; /* default timeout in seconds */
8196cb4eb0SGiel van Schijndel module_param(timeout, int, 0);
8296cb4eb0SGiel van Schijndel MODULE_PARM_DESC(timeout,
8396cb4eb0SGiel van Schijndel "Watchdog timeout in seconds. 1<= timeout <="
8496cb4eb0SGiel van Schijndel __MODULE_STRING(WATCHDOG_MAX_TIMEOUT) " (default="
8596cb4eb0SGiel van Schijndel __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
8696cb4eb0SGiel van Schijndel
8796cb4eb0SGiel van Schijndel static unsigned int pulse_width = WATCHDOG_PULSE_WIDTH;
8896cb4eb0SGiel van Schijndel module_param(pulse_width, uint, 0);
8996cb4eb0SGiel van Schijndel MODULE_PARM_DESC(pulse_width,
90166fbcf8SMaciej S. Szmigiero "Watchdog signal pulse width. 0(=level), 1, 25, 30, 125, 150, 5000 or 6000 ms"
9196cb4eb0SGiel van Schijndel " (default=" __MODULE_STRING(WATCHDOG_PULSE_WIDTH) ")");
9296cb4eb0SGiel van Schijndel
937977ff6eSLutz Ballaschke static unsigned int f71862fg_pin = WATCHDOG_F71862FG_PIN;
947977ff6eSLutz Ballaschke module_param(f71862fg_pin, uint, 0);
957977ff6eSLutz Ballaschke MODULE_PARM_DESC(f71862fg_pin,
967977ff6eSLutz Ballaschke "Watchdog f71862fg reset output pin configuration. Choose pin 56 or 63"
977977ff6eSLutz Ballaschke " (default=" __MODULE_STRING(WATCHDOG_F71862FG_PIN)")");
987977ff6eSLutz Ballaschke
9990ab5ee9SRusty Russell static bool nowayout = WATCHDOG_NOWAYOUT;
10096cb4eb0SGiel van Schijndel module_param(nowayout, bool, 0444);
10196cb4eb0SGiel van Schijndel MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
10296cb4eb0SGiel van Schijndel
10396cb4eb0SGiel van Schijndel static unsigned int start_withtimeout;
10496cb4eb0SGiel van Schijndel module_param(start_withtimeout, uint, 0);
10596cb4eb0SGiel van Schijndel MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
10696cb4eb0SGiel van Schijndel " given initial timeout. Zero (default) disables this feature.");
10796cb4eb0SGiel van Schijndel
108166fbcf8SMaciej S. Szmigiero enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg,
109cea62f9fSAaeonIot f81803, f81865, f81866, f81966};
11096cb4eb0SGiel van Schijndel
1113a2c4895SAhmad Fatoum static const char * const fintek_wdt_names[] = {
11296cb4eb0SGiel van Schijndel "f71808fg",
11396cb4eb0SGiel van Schijndel "f71858fg",
11496cb4eb0SGiel van Schijndel "f71862fg",
115166fbcf8SMaciej S. Szmigiero "f71868",
116df278dacSMichel Arboi "f71869",
11796cb4eb0SGiel van Schijndel "f71882fg",
11896cb4eb0SGiel van Schijndel "f71889fg",
119ca2fc5efSJaret Cantu "f81803",
120ea0c03e8SKnud Poulsen "f81865",
12114b24a88SJi-Ze Hong (Peter Hong) "f81866",
122cea62f9fSAaeonIot "f81966"
12396cb4eb0SGiel van Schijndel };
12496cb4eb0SGiel van Schijndel
12596cb4eb0SGiel van Schijndel /* Super-I/O Function prototypes */
12696cb4eb0SGiel van Schijndel static inline int superio_inb(int base, int reg);
12796cb4eb0SGiel van Schijndel static inline int superio_inw(int base, int reg);
12896cb4eb0SGiel van Schijndel static inline void superio_outb(int base, int reg, u8 val);
12996cb4eb0SGiel van Schijndel static inline void superio_set_bit(int base, int reg, int bit);
13096cb4eb0SGiel van Schijndel static inline void superio_clear_bit(int base, int reg, int bit);
13196cb4eb0SGiel van Schijndel static inline int superio_enter(int base);
13296cb4eb0SGiel van Schijndel static inline void superio_select(int base, int ld);
13396cb4eb0SGiel van Schijndel static inline void superio_exit(int base);
13496cb4eb0SGiel van Schijndel
1353a2c4895SAhmad Fatoum struct fintek_wdt {
1368bea27edSAhmad Fatoum struct watchdog_device wdd;
13796cb4eb0SGiel van Schijndel unsigned short sioaddr;
13896cb4eb0SGiel van Schijndel enum chips type;
13996cb4eb0SGiel van Schijndel struct watchdog_info ident;
14096cb4eb0SGiel van Schijndel
14196cb4eb0SGiel van Schijndel u8 timer_val; /* content for the wd_time register */
14296cb4eb0SGiel van Schijndel char minutes_mode;
14396cb4eb0SGiel van Schijndel u8 pulse_val; /* pulse width flag */
14496cb4eb0SGiel van Schijndel char pulse_mode; /* enable pulse output mode? */
14596cb4eb0SGiel van Schijndel };
14696cb4eb0SGiel van Schijndel
147a7876735SAhmad Fatoum struct fintek_wdt_pdata {
148a7876735SAhmad Fatoum enum chips type;
149a7876735SAhmad Fatoum };
15096cb4eb0SGiel van Schijndel
15196cb4eb0SGiel van Schijndel /* Super I/O functions */
superio_inb(int base,int reg)15296cb4eb0SGiel van Schijndel static inline int superio_inb(int base, int reg)
15396cb4eb0SGiel van Schijndel {
15496cb4eb0SGiel van Schijndel outb(reg, base);
15596cb4eb0SGiel van Schijndel return inb(base + 1);
15696cb4eb0SGiel van Schijndel }
15796cb4eb0SGiel van Schijndel
superio_inw(int base,int reg)15896cb4eb0SGiel van Schijndel static int superio_inw(int base, int reg)
15996cb4eb0SGiel van Schijndel {
16096cb4eb0SGiel van Schijndel int val;
16196cb4eb0SGiel van Schijndel val = superio_inb(base, reg) << 8;
16296cb4eb0SGiel van Schijndel val |= superio_inb(base, reg + 1);
16396cb4eb0SGiel van Schijndel return val;
16496cb4eb0SGiel van Schijndel }
16596cb4eb0SGiel van Schijndel
superio_outb(int base,int reg,u8 val)16696cb4eb0SGiel van Schijndel static inline void superio_outb(int base, int reg, u8 val)
16796cb4eb0SGiel van Schijndel {
16896cb4eb0SGiel van Schijndel outb(reg, base);
16996cb4eb0SGiel van Schijndel outb(val, base + 1);
17096cb4eb0SGiel van Schijndel }
17196cb4eb0SGiel van Schijndel
superio_set_bit(int base,int reg,int bit)17296cb4eb0SGiel van Schijndel static inline void superio_set_bit(int base, int reg, int bit)
17396cb4eb0SGiel van Schijndel {
17496cb4eb0SGiel van Schijndel unsigned long val = superio_inb(base, reg);
17596cb4eb0SGiel van Schijndel __set_bit(bit, &val);
17696cb4eb0SGiel van Schijndel superio_outb(base, reg, val);
17796cb4eb0SGiel van Schijndel }
17896cb4eb0SGiel van Schijndel
superio_clear_bit(int base,int reg,int bit)17996cb4eb0SGiel van Schijndel static inline void superio_clear_bit(int base, int reg, int bit)
18096cb4eb0SGiel van Schijndel {
18196cb4eb0SGiel van Schijndel unsigned long val = superio_inb(base, reg);
18296cb4eb0SGiel van Schijndel __clear_bit(bit, &val);
18396cb4eb0SGiel van Schijndel superio_outb(base, reg, val);
18496cb4eb0SGiel van Schijndel }
18596cb4eb0SGiel van Schijndel
superio_enter(int base)18696cb4eb0SGiel van Schijndel static inline int superio_enter(int base)
18796cb4eb0SGiel van Schijndel {
18896cb4eb0SGiel van Schijndel /* Don't step on other drivers' I/O space by accident */
18996cb4eb0SGiel van Schijndel if (!request_muxed_region(base, 2, DRVNAME)) {
19027c766aaSJoe Perches pr_err("I/O address 0x%04x already in use\n", (int)base);
19196cb4eb0SGiel van Schijndel return -EBUSY;
19296cb4eb0SGiel van Schijndel }
19396cb4eb0SGiel van Schijndel
1943017020dSJustin Wheeler /* according to the datasheet the key must be sent twice! */
19596cb4eb0SGiel van Schijndel outb(SIO_UNLOCK_KEY, base);
19696cb4eb0SGiel van Schijndel outb(SIO_UNLOCK_KEY, base);
19796cb4eb0SGiel van Schijndel
19896cb4eb0SGiel van Schijndel return 0;
19996cb4eb0SGiel van Schijndel }
20096cb4eb0SGiel van Schijndel
superio_select(int base,int ld)20196cb4eb0SGiel van Schijndel static inline void superio_select(int base, int ld)
20296cb4eb0SGiel van Schijndel {
20396cb4eb0SGiel van Schijndel outb(SIO_REG_LDSEL, base);
20496cb4eb0SGiel van Schijndel outb(ld, base + 1);
20596cb4eb0SGiel van Schijndel }
20696cb4eb0SGiel van Schijndel
superio_exit(int base)20796cb4eb0SGiel van Schijndel static inline void superio_exit(int base)
20896cb4eb0SGiel van Schijndel {
20996cb4eb0SGiel van Schijndel outb(SIO_LOCK_KEY, base);
21096cb4eb0SGiel van Schijndel release_region(base, 2);
21196cb4eb0SGiel van Schijndel }
21296cb4eb0SGiel van Schijndel
fintek_wdt_set_timeout(struct watchdog_device * wdd,unsigned int timeout)2138bea27edSAhmad Fatoum static int fintek_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
21496cb4eb0SGiel van Schijndel {
215a7876735SAhmad Fatoum struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
216a7876735SAhmad Fatoum
21796cb4eb0SGiel van Schijndel if (timeout > 0xff) {
218a7876735SAhmad Fatoum wd->timer_val = DIV_ROUND_UP(timeout, 60);
219a7876735SAhmad Fatoum wd->minutes_mode = true;
220a7876735SAhmad Fatoum timeout = wd->timer_val * 60;
22196cb4eb0SGiel van Schijndel } else {
222a7876735SAhmad Fatoum wd->timer_val = timeout;
223a7876735SAhmad Fatoum wd->minutes_mode = false;
22496cb4eb0SGiel van Schijndel }
22596cb4eb0SGiel van Schijndel
2268bea27edSAhmad Fatoum wdd->timeout = timeout;
22796cb4eb0SGiel van Schijndel
22896cb4eb0SGiel van Schijndel return 0;
22996cb4eb0SGiel van Schijndel }
23096cb4eb0SGiel van Schijndel
fintek_wdt_set_pulse_width(struct fintek_wdt * wd,unsigned int pw)231a7876735SAhmad Fatoum static int fintek_wdt_set_pulse_width(struct fintek_wdt *wd, unsigned int pw)
23296cb4eb0SGiel van Schijndel {
233166fbcf8SMaciej S. Szmigiero unsigned int t1 = 25, t2 = 125, t3 = 5000;
234166fbcf8SMaciej S. Szmigiero
235a7876735SAhmad Fatoum if (wd->type == f71868) {
236166fbcf8SMaciej S. Szmigiero t1 = 30;
237166fbcf8SMaciej S. Szmigiero t2 = 150;
238166fbcf8SMaciej S. Szmigiero t3 = 6000;
239166fbcf8SMaciej S. Szmigiero }
24096cb4eb0SGiel van Schijndel
24196cb4eb0SGiel van Schijndel if (pw <= 1) {
242a7876735SAhmad Fatoum wd->pulse_val = 0;
243166fbcf8SMaciej S. Szmigiero } else if (pw <= t1) {
244a7876735SAhmad Fatoum wd->pulse_val = 1;
245166fbcf8SMaciej S. Szmigiero } else if (pw <= t2) {
246a7876735SAhmad Fatoum wd->pulse_val = 2;
247166fbcf8SMaciej S. Szmigiero } else if (pw <= t3) {
248a7876735SAhmad Fatoum wd->pulse_val = 3;
24996cb4eb0SGiel van Schijndel } else {
25027c766aaSJoe Perches pr_err("pulse width out of range\n");
2518bea27edSAhmad Fatoum return -EINVAL;
25296cb4eb0SGiel van Schijndel }
25396cb4eb0SGiel van Schijndel
254a7876735SAhmad Fatoum wd->pulse_mode = pw;
25596cb4eb0SGiel van Schijndel
2568bea27edSAhmad Fatoum return 0;
25796cb4eb0SGiel van Schijndel }
25896cb4eb0SGiel van Schijndel
fintek_wdt_keepalive(struct watchdog_device * wdd)2598bea27edSAhmad Fatoum static int fintek_wdt_keepalive(struct watchdog_device *wdd)
26096cb4eb0SGiel van Schijndel {
261a7876735SAhmad Fatoum struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
2628bea27edSAhmad Fatoum int err;
26396cb4eb0SGiel van Schijndel
264a7876735SAhmad Fatoum err = superio_enter(wd->sioaddr);
26596cb4eb0SGiel van Schijndel if (err)
2668bea27edSAhmad Fatoum return err;
267a7876735SAhmad Fatoum superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
26896cb4eb0SGiel van Schijndel
269a7876735SAhmad Fatoum if (wd->minutes_mode)
27096cb4eb0SGiel van Schijndel /* select minutes for timer units */
271a7876735SAhmad Fatoum superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
27296cb4eb0SGiel van Schijndel F71808FG_FLAG_WD_UNIT);
27396cb4eb0SGiel van Schijndel else
27496cb4eb0SGiel van Schijndel /* select seconds for timer units */
275a7876735SAhmad Fatoum superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
27696cb4eb0SGiel van Schijndel F71808FG_FLAG_WD_UNIT);
27796cb4eb0SGiel van Schijndel
27896cb4eb0SGiel van Schijndel /* Set timer value */
279a7876735SAhmad Fatoum superio_outb(wd->sioaddr, F71808FG_REG_WD_TIME,
280a7876735SAhmad Fatoum wd->timer_val);
28196cb4eb0SGiel van Schijndel
282a7876735SAhmad Fatoum superio_exit(wd->sioaddr);
28396cb4eb0SGiel van Schijndel
2848bea27edSAhmad Fatoum return 0;
28596cb4eb0SGiel van Schijndel }
28696cb4eb0SGiel van Schijndel
fintek_wdt_start(struct watchdog_device * wdd)2878bea27edSAhmad Fatoum static int fintek_wdt_start(struct watchdog_device *wdd)
28896cb4eb0SGiel van Schijndel {
289a7876735SAhmad Fatoum struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
290a3f764d2SJi-Ze Hong (Peter Hong) int err;
291e347afa5SJi-Ze Hong (Peter Hong) u8 tmp;
292a3f764d2SJi-Ze Hong (Peter Hong)
29396cb4eb0SGiel van Schijndel /* Make sure we don't die as soon as the watchdog is enabled below */
2948bea27edSAhmad Fatoum err = fintek_wdt_keepalive(wdd);
29596cb4eb0SGiel van Schijndel if (err)
29696cb4eb0SGiel van Schijndel return err;
29796cb4eb0SGiel van Schijndel
298a7876735SAhmad Fatoum err = superio_enter(wd->sioaddr);
29996cb4eb0SGiel van Schijndel if (err)
3008bea27edSAhmad Fatoum return err;
301a7876735SAhmad Fatoum superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
30296cb4eb0SGiel van Schijndel
30396cb4eb0SGiel van Schijndel /* Watchdog pin configuration */
304a7876735SAhmad Fatoum switch (wd->type) {
30596cb4eb0SGiel van Schijndel case f71808fg:
30696cb4eb0SGiel van Schijndel /* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */
307a7876735SAhmad Fatoum superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT2, 3);
308a7876735SAhmad Fatoum superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 3);
30996cb4eb0SGiel van Schijndel break;
31096cb4eb0SGiel van Schijndel
3117977ff6eSLutz Ballaschke case f71862fg:
3125edc8c68SAhmad Fatoum if (f71862fg_pin == 63) {
3135edc8c68SAhmad Fatoum /* SPI must be disabled first to use this pin! */
314a7876735SAhmad Fatoum superio_clear_bit(wd->sioaddr, SIO_REG_ROM_ADDR_SEL, 6);
315a7876735SAhmad Fatoum superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT3, 4);
3165edc8c68SAhmad Fatoum } else if (f71862fg_pin == 56) {
317a7876735SAhmad Fatoum superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1);
3185edc8c68SAhmad Fatoum }
3197977ff6eSLutz Ballaschke break;
3207977ff6eSLutz Ballaschke
321166fbcf8SMaciej S. Szmigiero case f71868:
322df278dacSMichel Arboi case f71869:
323df278dacSMichel Arboi /* GPIO14 --> WDTRST# */
324a7876735SAhmad Fatoum superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT1, 4);
325df278dacSMichel Arboi break;
326df278dacSMichel Arboi
32796cb4eb0SGiel van Schijndel case f71882fg:
32896cb4eb0SGiel van Schijndel /* Set pin 56 to WDTRST# */
329a7876735SAhmad Fatoum superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1);
33096cb4eb0SGiel van Schijndel break;
33196cb4eb0SGiel van Schijndel
332dee00abbSGiel van Schijndel case f71889fg:
333dee00abbSGiel van Schijndel /* set pin 40 to WDTRST# */
334a7876735SAhmad Fatoum superio_outb(wd->sioaddr, SIO_REG_MFUNCT3,
335a7876735SAhmad Fatoum superio_inb(wd->sioaddr, SIO_REG_MFUNCT3) & 0xcf);
336dee00abbSGiel van Schijndel break;
337dee00abbSGiel van Schijndel
338ca2fc5efSJaret Cantu case f81803:
339ca2fc5efSJaret Cantu /* Enable TSI Level register bank */
340a7876735SAhmad Fatoum superio_clear_bit(wd->sioaddr, SIO_REG_CLOCK_SEL, 3);
341ca2fc5efSJaret Cantu /* Set pin 27 to WDTRST# */
342a7876735SAhmad Fatoum superio_outb(wd->sioaddr, SIO_REG_TSI_LEVEL_SEL, 0x5f &
343a7876735SAhmad Fatoum superio_inb(wd->sioaddr, SIO_REG_TSI_LEVEL_SEL));
344ca2fc5efSJaret Cantu break;
345ca2fc5efSJaret Cantu
346ea0c03e8SKnud Poulsen case f81865:
347ea0c03e8SKnud Poulsen /* Set pin 70 to WDTRST# */
348a7876735SAhmad Fatoum superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 5);
349ea0c03e8SKnud Poulsen break;
350ea0c03e8SKnud Poulsen
35114b24a88SJi-Ze Hong (Peter Hong) case f81866:
352cea62f9fSAaeonIot case f81966:
35314b24a88SJi-Ze Hong (Peter Hong) /*
35414b24a88SJi-Ze Hong (Peter Hong) * GPIO1 Control Register when 27h BIT3:2 = 01 & BIT0 = 0.
35514b24a88SJi-Ze Hong (Peter Hong) * The PIN 70(GPIO15/WDTRST) is controlled by 2Ch:
35614b24a88SJi-Ze Hong (Peter Hong) * BIT5: 0 -> WDTRST#
35714b24a88SJi-Ze Hong (Peter Hong) * 1 -> GPIO15
35814b24a88SJi-Ze Hong (Peter Hong) */
359a7876735SAhmad Fatoum tmp = superio_inb(wd->sioaddr, SIO_F81866_REG_PORT_SEL);
360e347afa5SJi-Ze Hong (Peter Hong) tmp &= ~(BIT(3) | BIT(0));
361e347afa5SJi-Ze Hong (Peter Hong) tmp |= BIT(2);
362a7876735SAhmad Fatoum superio_outb(wd->sioaddr, SIO_F81866_REG_PORT_SEL, tmp);
363e347afa5SJi-Ze Hong (Peter Hong)
364a7876735SAhmad Fatoum superio_clear_bit(wd->sioaddr, SIO_F81866_REG_GPIO1, 5);
36514b24a88SJi-Ze Hong (Peter Hong) break;
36614b24a88SJi-Ze Hong (Peter Hong)
36796cb4eb0SGiel van Schijndel default:
36896cb4eb0SGiel van Schijndel /*
36996cb4eb0SGiel van Schijndel * 'default' label to shut up the compiler and catch
37096cb4eb0SGiel van Schijndel * programmer errors
37196cb4eb0SGiel van Schijndel */
37296cb4eb0SGiel van Schijndel err = -ENODEV;
37396cb4eb0SGiel van Schijndel goto exit_superio;
37496cb4eb0SGiel van Schijndel }
37596cb4eb0SGiel van Schijndel
376a7876735SAhmad Fatoum superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
377a7876735SAhmad Fatoum superio_set_bit(wd->sioaddr, SIO_REG_ENABLE, 0);
378ea0c03e8SKnud Poulsen
379cea62f9fSAaeonIot if (wd->type == f81865 || wd->type == f81866 || wd->type == f81966)
380a7876735SAhmad Fatoum superio_set_bit(wd->sioaddr, F81865_REG_WDO_CONF,
381ea0c03e8SKnud Poulsen F81865_FLAG_WDOUT_EN);
382ea0c03e8SKnud Poulsen else
383a7876735SAhmad Fatoum superio_set_bit(wd->sioaddr, F71808FG_REG_WDO_CONF,
38496cb4eb0SGiel van Schijndel F71808FG_FLAG_WDOUT_EN);
38596cb4eb0SGiel van Schijndel
386a7876735SAhmad Fatoum superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
38796cb4eb0SGiel van Schijndel F71808FG_FLAG_WD_EN);
38896cb4eb0SGiel van Schijndel
389a7876735SAhmad Fatoum if (wd->pulse_mode) {
39096cb4eb0SGiel van Schijndel /* Select "pulse" output mode with given duration */
391a7876735SAhmad Fatoum u8 wdt_conf = superio_inb(wd->sioaddr,
39296cb4eb0SGiel van Schijndel F71808FG_REG_WDT_CONF);
39396cb4eb0SGiel van Schijndel
39496cb4eb0SGiel van Schijndel /* Set WD_PSWIDTH bits (1:0) */
395a7876735SAhmad Fatoum wdt_conf = (wdt_conf & 0xfc) | (wd->pulse_val & 0x03);
39696cb4eb0SGiel van Schijndel /* Set WD_PULSE to "pulse" mode */
39796cb4eb0SGiel van Schijndel wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE);
39896cb4eb0SGiel van Schijndel
399a7876735SAhmad Fatoum superio_outb(wd->sioaddr, F71808FG_REG_WDT_CONF,
40096cb4eb0SGiel van Schijndel wdt_conf);
40196cb4eb0SGiel van Schijndel } else {
40296cb4eb0SGiel van Schijndel /* Select "level" output mode */
403a7876735SAhmad Fatoum superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
40496cb4eb0SGiel van Schijndel F71808FG_FLAG_WD_PULSE);
40596cb4eb0SGiel van Schijndel }
40696cb4eb0SGiel van Schijndel
40796cb4eb0SGiel van Schijndel exit_superio:
408a7876735SAhmad Fatoum superio_exit(wd->sioaddr);
40996cb4eb0SGiel van Schijndel
41096cb4eb0SGiel van Schijndel return err;
41196cb4eb0SGiel van Schijndel }
41296cb4eb0SGiel van Schijndel
fintek_wdt_stop(struct watchdog_device * wdd)4138bea27edSAhmad Fatoum static int fintek_wdt_stop(struct watchdog_device *wdd)
41496cb4eb0SGiel van Schijndel {
415a7876735SAhmad Fatoum struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
4168bea27edSAhmad Fatoum int err;
41796cb4eb0SGiel van Schijndel
418a7876735SAhmad Fatoum err = superio_enter(wd->sioaddr);
41996cb4eb0SGiel van Schijndel if (err)
4208bea27edSAhmad Fatoum return err;
421a7876735SAhmad Fatoum superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
42296cb4eb0SGiel van Schijndel
423a7876735SAhmad Fatoum superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
42496cb4eb0SGiel van Schijndel F71808FG_FLAG_WD_EN);
42596cb4eb0SGiel van Schijndel
426a7876735SAhmad Fatoum superio_exit(wd->sioaddr);
42796cb4eb0SGiel van Schijndel
42896cb4eb0SGiel van Schijndel return 0;
42996cb4eb0SGiel van Schijndel }
43096cb4eb0SGiel van Schijndel
fintek_wdt_is_running(struct fintek_wdt * wd,u8 wdt_conf)431a7876735SAhmad Fatoum static bool fintek_wdt_is_running(struct fintek_wdt *wd, u8 wdt_conf)
43296cb4eb0SGiel van Schijndel {
433a7876735SAhmad Fatoum return (superio_inb(wd->sioaddr, SIO_REG_ENABLE) & BIT(0))
4348bea27edSAhmad Fatoum && (wdt_conf & BIT(F71808FG_FLAG_WD_EN));
43596cb4eb0SGiel van Schijndel }
43696cb4eb0SGiel van Schijndel
4378bea27edSAhmad Fatoum static const struct watchdog_ops fintek_wdt_ops = {
43896cb4eb0SGiel van Schijndel .owner = THIS_MODULE,
4398bea27edSAhmad Fatoum .start = fintek_wdt_start,
4408bea27edSAhmad Fatoum .stop = fintek_wdt_stop,
4418bea27edSAhmad Fatoum .ping = fintek_wdt_keepalive,
4428bea27edSAhmad Fatoum .set_timeout = fintek_wdt_set_timeout,
44396cb4eb0SGiel van Schijndel };
44496cb4eb0SGiel van Schijndel
fintek_wdt_probe(struct platform_device * pdev)44527e0fe00SAhmad Fatoum static int fintek_wdt_probe(struct platform_device *pdev)
44696cb4eb0SGiel van Schijndel {
44727e0fe00SAhmad Fatoum struct device *dev = &pdev->dev;
448a7876735SAhmad Fatoum struct fintek_wdt_pdata *pdata;
4498bea27edSAhmad Fatoum struct watchdog_device *wdd;
450a7876735SAhmad Fatoum struct fintek_wdt *wd;
45196cb4eb0SGiel van Schijndel int wdt_conf, err = 0;
45227e0fe00SAhmad Fatoum struct resource *res;
45327e0fe00SAhmad Fatoum int sioaddr;
45427e0fe00SAhmad Fatoum
45527e0fe00SAhmad Fatoum res = platform_get_resource(pdev, IORESOURCE_IO, 0);
45627e0fe00SAhmad Fatoum if (!res)
45727e0fe00SAhmad Fatoum return -ENXIO;
45827e0fe00SAhmad Fatoum
45927e0fe00SAhmad Fatoum sioaddr = res->start;
46096cb4eb0SGiel van Schijndel
461a7876735SAhmad Fatoum wd = devm_kzalloc(dev, sizeof(*wd), GFP_KERNEL);
462a7876735SAhmad Fatoum if (!wd)
463a7876735SAhmad Fatoum return -ENOMEM;
464a7876735SAhmad Fatoum
465a7876735SAhmad Fatoum pdata = dev->platform_data;
466a7876735SAhmad Fatoum
467a7876735SAhmad Fatoum wd->type = pdata->type;
468a7876735SAhmad Fatoum wd->sioaddr = sioaddr;
469a7876735SAhmad Fatoum wd->ident.options = WDIOF_SETTIMEOUT
4708bea27edSAhmad Fatoum | WDIOF_MAGICCLOSE
471e871e93fSAhmad Fatoum | WDIOF_KEEPALIVEPING
472e871e93fSAhmad Fatoum | WDIOF_CARDRESET;
47396cb4eb0SGiel van Schijndel
474a7876735SAhmad Fatoum snprintf(wd->ident.identity,
475a7876735SAhmad Fatoum sizeof(wd->ident.identity), "%s watchdog",
476a7876735SAhmad Fatoum fintek_wdt_names[wd->type]);
47796cb4eb0SGiel van Schijndel
47896cb4eb0SGiel van Schijndel err = superio_enter(sioaddr);
47996cb4eb0SGiel van Schijndel if (err)
48096cb4eb0SGiel van Schijndel return err;
481a7876735SAhmad Fatoum superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
48296cb4eb0SGiel van Schijndel
48396cb4eb0SGiel van Schijndel wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF);
48496cb4eb0SGiel van Schijndel
4854f39d575SAhmad Fatoum /*
4864f39d575SAhmad Fatoum * We don't want WDTMOUT_STS to stick around till regular reboot.
4874f39d575SAhmad Fatoum * Write 1 to the bit to clear it to zero.
4884f39d575SAhmad Fatoum */
4894f39d575SAhmad Fatoum superio_outb(sioaddr, F71808FG_REG_WDT_CONF,
4904f39d575SAhmad Fatoum wdt_conf | BIT(F71808FG_FLAG_WDTMOUT_STS));
4914f39d575SAhmad Fatoum
492a7876735SAhmad Fatoum wdd = &wd->wdd;
4938bea27edSAhmad Fatoum
494a7876735SAhmad Fatoum if (fintek_wdt_is_running(wd, wdt_conf))
4958bea27edSAhmad Fatoum set_bit(WDOG_HW_RUNNING, &wdd->status);
4968bea27edSAhmad Fatoum
49796cb4eb0SGiel van Schijndel superio_exit(sioaddr);
49896cb4eb0SGiel van Schijndel
49927e0fe00SAhmad Fatoum wdd->parent = dev;
500a7876735SAhmad Fatoum wdd->info = &wd->ident;
5018bea27edSAhmad Fatoum wdd->ops = &fintek_wdt_ops;
5028bea27edSAhmad Fatoum wdd->min_timeout = 1;
5038bea27edSAhmad Fatoum wdd->max_timeout = WATCHDOG_MAX_TIMEOUT;
50496cb4eb0SGiel van Schijndel
505a7876735SAhmad Fatoum watchdog_set_drvdata(wdd, wd);
5068bea27edSAhmad Fatoum watchdog_set_nowayout(wdd, nowayout);
5078bea27edSAhmad Fatoum watchdog_stop_on_unregister(wdd);
5088bea27edSAhmad Fatoum watchdog_stop_on_reboot(wdd);
5098bea27edSAhmad Fatoum watchdog_init_timeout(wdd, start_withtimeout ?: timeout, NULL);
51096cb4eb0SGiel van Schijndel
5118bea27edSAhmad Fatoum if (wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS))
5128bea27edSAhmad Fatoum wdd->bootstatus = WDIOF_CARDRESET;
5138bea27edSAhmad Fatoum
5148bea27edSAhmad Fatoum /*
5158bea27edSAhmad Fatoum * WATCHDOG_HANDLE_BOOT_ENABLED can result in keepalive being directly
5168bea27edSAhmad Fatoum * called without a set_timeout before, so it needs to be done here
5178bea27edSAhmad Fatoum * unconditionally.
5188bea27edSAhmad Fatoum */
5198bea27edSAhmad Fatoum fintek_wdt_set_timeout(wdd, wdd->timeout);
520a7876735SAhmad Fatoum fintek_wdt_set_pulse_width(wd, pulse_width);
52196cb4eb0SGiel van Schijndel
52296cb4eb0SGiel van Schijndel if (start_withtimeout) {
5238bea27edSAhmad Fatoum err = fintek_wdt_start(wdd);
52496cb4eb0SGiel van Schijndel if (err) {
52527e0fe00SAhmad Fatoum dev_err(dev, "cannot start watchdog timer\n");
5268bea27edSAhmad Fatoum return err;
52796cb4eb0SGiel van Schijndel }
52896cb4eb0SGiel van Schijndel
5298bea27edSAhmad Fatoum set_bit(WDOG_HW_RUNNING, &wdd->status);
53027e0fe00SAhmad Fatoum dev_info(dev, "watchdog started with initial timeout of %u sec\n",
53196cb4eb0SGiel van Schijndel start_withtimeout);
53296cb4eb0SGiel van Schijndel }
53396cb4eb0SGiel van Schijndel
53427e0fe00SAhmad Fatoum return devm_watchdog_register_device(dev, wdd);
53596cb4eb0SGiel van Schijndel }
53696cb4eb0SGiel van Schijndel
fintek_wdt_find(int sioaddr)5373a2c4895SAhmad Fatoum static int __init fintek_wdt_find(int sioaddr)
53896cb4eb0SGiel van Schijndel {
539a7876735SAhmad Fatoum enum chips type;
54096cb4eb0SGiel van Schijndel u16 devid;
54196cb4eb0SGiel van Schijndel int err = superio_enter(sioaddr);
54296cb4eb0SGiel van Schijndel if (err)
54396cb4eb0SGiel van Schijndel return err;
54496cb4eb0SGiel van Schijndel
54596cb4eb0SGiel van Schijndel devid = superio_inw(sioaddr, SIO_REG_MANID);
54696cb4eb0SGiel van Schijndel if (devid != SIO_FINTEK_ID) {
54727c766aaSJoe Perches pr_debug("Not a Fintek device\n");
54896cb4eb0SGiel van Schijndel err = -ENODEV;
54996cb4eb0SGiel van Schijndel goto exit;
55096cb4eb0SGiel van Schijndel }
55196cb4eb0SGiel van Schijndel
55296cb4eb0SGiel van Schijndel devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID);
55396cb4eb0SGiel van Schijndel switch (devid) {
55496cb4eb0SGiel van Schijndel case SIO_F71808_ID:
555a7876735SAhmad Fatoum type = f71808fg;
55696cb4eb0SGiel van Schijndel break;
5577977ff6eSLutz Ballaschke case SIO_F71862_ID:
558a7876735SAhmad Fatoum type = f71862fg;
5597977ff6eSLutz Ballaschke break;
560166fbcf8SMaciej S. Szmigiero case SIO_F71868_ID:
561a7876735SAhmad Fatoum type = f71868;
562166fbcf8SMaciej S. Szmigiero break;
563df278dacSMichel Arboi case SIO_F71869_ID:
5643017020dSJustin Wheeler case SIO_F71869A_ID:
565a7876735SAhmad Fatoum type = f71869;
566df278dacSMichel Arboi break;
56796cb4eb0SGiel van Schijndel case SIO_F71882_ID:
568a7876735SAhmad Fatoum type = f71882fg;
56996cb4eb0SGiel van Schijndel break;
57096cb4eb0SGiel van Schijndel case SIO_F71889_ID:
571a7876735SAhmad Fatoum type = f71889fg;
572dee00abbSGiel van Schijndel break;
57396cb4eb0SGiel van Schijndel case SIO_F71858_ID:
57496cb4eb0SGiel van Schijndel /* Confirmed (by datasheet) not to have a watchdog. */
57596cb4eb0SGiel van Schijndel err = -ENODEV;
57696cb4eb0SGiel van Schijndel goto exit;
577ca2fc5efSJaret Cantu case SIO_F81803_ID:
578a7876735SAhmad Fatoum type = f81803;
579ca2fc5efSJaret Cantu break;
580ea0c03e8SKnud Poulsen case SIO_F81865_ID:
581a7876735SAhmad Fatoum type = f81865;
582ea0c03e8SKnud Poulsen break;
58314b24a88SJi-Ze Hong (Peter Hong) case SIO_F81866_ID:
584a7876735SAhmad Fatoum type = f81866;
58514b24a88SJi-Ze Hong (Peter Hong) break;
586cea62f9fSAaeonIot case SIO_F81966_ID:
587cea62f9fSAaeonIot type = f81966;
588cea62f9fSAaeonIot break;
58996cb4eb0SGiel van Schijndel default:
59027c766aaSJoe Perches pr_info("Unrecognized Fintek device: %04x\n",
59196cb4eb0SGiel van Schijndel (unsigned int)devid);
59296cb4eb0SGiel van Schijndel err = -ENODEV;
59396cb4eb0SGiel van Schijndel goto exit;
59496cb4eb0SGiel van Schijndel }
59596cb4eb0SGiel van Schijndel
59627c766aaSJoe Perches pr_info("Found %s watchdog chip, revision %d\n",
597a7876735SAhmad Fatoum fintek_wdt_names[type],
59896cb4eb0SGiel van Schijndel (int)superio_inb(sioaddr, SIO_REG_DEVREV));
599a7876735SAhmad Fatoum
60096cb4eb0SGiel van Schijndel exit:
60196cb4eb0SGiel van Schijndel superio_exit(sioaddr);
602a7876735SAhmad Fatoum return err ? err : type;
60396cb4eb0SGiel van Schijndel }
60496cb4eb0SGiel van Schijndel
60527e0fe00SAhmad Fatoum static struct platform_driver fintek_wdt_driver = {
60627e0fe00SAhmad Fatoum .probe = fintek_wdt_probe,
60727e0fe00SAhmad Fatoum .driver = {
60827e0fe00SAhmad Fatoum .name = DRVNAME,
60927e0fe00SAhmad Fatoum },
61027e0fe00SAhmad Fatoum };
61127e0fe00SAhmad Fatoum
61227e0fe00SAhmad Fatoum static struct platform_device *fintek_wdt_pdev;
61327e0fe00SAhmad Fatoum
fintek_wdt_init(void)6143a2c4895SAhmad Fatoum static int __init fintek_wdt_init(void)
61596cb4eb0SGiel van Schijndel {
61696cb4eb0SGiel van Schijndel static const unsigned short addrs[] = { 0x2e, 0x4e };
617a7876735SAhmad Fatoum struct fintek_wdt_pdata pdata;
61827e0fe00SAhmad Fatoum struct resource wdt_res = {};
619a7876735SAhmad Fatoum int ret;
62096cb4eb0SGiel van Schijndel int i;
62196cb4eb0SGiel van Schijndel
6225edc8c68SAhmad Fatoum if (f71862fg_pin != 63 && f71862fg_pin != 56) {
6235edc8c68SAhmad Fatoum pr_err("Invalid argument f71862fg_pin=%d\n", f71862fg_pin);
6245edc8c68SAhmad Fatoum return -EINVAL;
6255edc8c68SAhmad Fatoum }
6265edc8c68SAhmad Fatoum
62796cb4eb0SGiel van Schijndel for (i = 0; i < ARRAY_SIZE(addrs); i++) {
628a7876735SAhmad Fatoum ret = fintek_wdt_find(addrs[i]);
629a7876735SAhmad Fatoum if (ret >= 0)
63096cb4eb0SGiel van Schijndel break;
63196cb4eb0SGiel van Schijndel }
63296cb4eb0SGiel van Schijndel if (i == ARRAY_SIZE(addrs))
633a7876735SAhmad Fatoum return ret;
634a7876735SAhmad Fatoum
635a7876735SAhmad Fatoum pdata.type = ret;
63696cb4eb0SGiel van Schijndel
637*97d5ec54SJiasheng Jiang ret = platform_driver_register(&fintek_wdt_driver);
638*97d5ec54SJiasheng Jiang if (ret)
639*97d5ec54SJiasheng Jiang return ret;
64027e0fe00SAhmad Fatoum
64127e0fe00SAhmad Fatoum wdt_res.name = "superio port";
64227e0fe00SAhmad Fatoum wdt_res.flags = IORESOURCE_IO;
64327e0fe00SAhmad Fatoum wdt_res.start = addrs[i];
64427e0fe00SAhmad Fatoum wdt_res.end = addrs[i] + 1;
64527e0fe00SAhmad Fatoum
646a7876735SAhmad Fatoum fintek_wdt_pdev = platform_device_register_resndata(NULL, DRVNAME, -1,
647a7876735SAhmad Fatoum &wdt_res, 1,
648a7876735SAhmad Fatoum &pdata, sizeof(pdata));
64927e0fe00SAhmad Fatoum if (IS_ERR(fintek_wdt_pdev)) {
65027e0fe00SAhmad Fatoum platform_driver_unregister(&fintek_wdt_driver);
65127e0fe00SAhmad Fatoum return PTR_ERR(fintek_wdt_pdev);
65227e0fe00SAhmad Fatoum }
65327e0fe00SAhmad Fatoum
65427e0fe00SAhmad Fatoum return 0;
65596cb4eb0SGiel van Schijndel }
65696cb4eb0SGiel van Schijndel
fintek_wdt_exit(void)6573a2c4895SAhmad Fatoum static void __exit fintek_wdt_exit(void)
65896cb4eb0SGiel van Schijndel {
65927e0fe00SAhmad Fatoum platform_device_unregister(fintek_wdt_pdev);
66027e0fe00SAhmad Fatoum platform_driver_unregister(&fintek_wdt_driver);
66196cb4eb0SGiel van Schijndel }
66296cb4eb0SGiel van Schijndel
66396cb4eb0SGiel van Schijndel MODULE_DESCRIPTION("F71808E Watchdog Driver");
66496cb4eb0SGiel van Schijndel MODULE_AUTHOR("Giel van Schijndel <me@mortis.eu>");
66596cb4eb0SGiel van Schijndel MODULE_LICENSE("GPL");
66696cb4eb0SGiel van Schijndel
6673a2c4895SAhmad Fatoum module_init(fintek_wdt_init);
6683a2c4895SAhmad Fatoum module_exit(fintek_wdt_exit);
669