Linux 6.9-rc5
[sfrench/cifs-2.6.git] / drivers / watchdog / f71808e_wdt.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /***************************************************************************
3  *   Copyright (C) 2006 by Hans Edgington <hans@edgington.nl>              *
4  *   Copyright (C) 2007-2009 Hans de Goede <hdegoede@redhat.com>           *
5  *   Copyright (C) 2010 Giel van Schijndel <me@mortis.eu>                  *
6  *                                                                         *
7  ***************************************************************************/
8
9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11 #include <linux/err.h>
12 #include <linux/init.h>
13 #include <linux/io.h>
14 #include <linux/ioport.h>
15 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/watchdog.h>
18
19 #define DRVNAME "f71808e_wdt"
20
21 #define SIO_F71808FG_LD_WDT     0x07    /* Watchdog timer logical device */
22 #define SIO_UNLOCK_KEY          0x87    /* Key to enable Super-I/O */
23 #define SIO_LOCK_KEY            0xAA    /* Key to disable Super-I/O */
24
25 #define SIO_REG_LDSEL           0x07    /* Logical device select */
26 #define SIO_REG_DEVID           0x20    /* Device ID (2 bytes) */
27 #define SIO_REG_DEVREV          0x22    /* Device revision */
28 #define SIO_REG_MANID           0x23    /* Fintek ID (2 bytes) */
29 #define SIO_REG_CLOCK_SEL       0x26    /* Clock select */
30 #define SIO_REG_ROM_ADDR_SEL    0x27    /* ROM address select */
31 #define SIO_F81866_REG_PORT_SEL 0x27    /* F81866 Multi-Function Register */
32 #define SIO_REG_TSI_LEVEL_SEL   0x28    /* TSI Level select */
33 #define SIO_REG_MFUNCT1         0x29    /* Multi function select 1 */
34 #define SIO_REG_MFUNCT2         0x2a    /* Multi function select 2 */
35 #define SIO_REG_MFUNCT3         0x2b    /* Multi function select 3 */
36 #define SIO_F81866_REG_GPIO1    0x2c    /* F81866 GPIO1 Enable Register */
37 #define SIO_REG_ENABLE          0x30    /* Logical device enable */
38 #define SIO_REG_ADDR            0x60    /* Logical device address (2 bytes) */
39
40 #define SIO_FINTEK_ID           0x1934  /* Manufacturers ID */
41 #define SIO_F71808_ID           0x0901  /* Chipset ID */
42 #define SIO_F71858_ID           0x0507  /* Chipset ID */
43 #define SIO_F71862_ID           0x0601  /* Chipset ID */
44 #define SIO_F71868_ID           0x1106  /* Chipset ID */
45 #define SIO_F71869_ID           0x0814  /* Chipset ID */
46 #define SIO_F71869A_ID          0x1007  /* Chipset ID */
47 #define SIO_F71882_ID           0x0541  /* Chipset ID */
48 #define SIO_F71889_ID           0x0723  /* Chipset ID */
49 #define SIO_F81803_ID           0x1210  /* Chipset ID */
50 #define SIO_F81865_ID           0x0704  /* Chipset ID */
51 #define SIO_F81866_ID           0x1010  /* Chipset ID */
52 #define SIO_F81966_ID           0x1502  /* F81804 chipset ID, same for f81966 */
53
54 #define F71808FG_REG_WDO_CONF           0xf0
55 #define F71808FG_REG_WDT_CONF           0xf5
56 #define F71808FG_REG_WD_TIME            0xf6
57
58 #define F71808FG_FLAG_WDOUT_EN          7
59
60 #define F71808FG_FLAG_WDTMOUT_STS       6
61 #define F71808FG_FLAG_WD_EN             5
62 #define F71808FG_FLAG_WD_PULSE          4
63 #define F71808FG_FLAG_WD_UNIT           3
64
65 #define F81865_REG_WDO_CONF             0xfa
66 #define F81865_FLAG_WDOUT_EN            0
67
68 /* Default values */
69 #define WATCHDOG_TIMEOUT        60      /* 1 minute default timeout */
70 #define WATCHDOG_MAX_TIMEOUT    (60 * 255)
71 #define WATCHDOG_PULSE_WIDTH    125     /* 125 ms, default pulse width for
72                                            watchdog signal */
73 #define WATCHDOG_F71862FG_PIN   63      /* default watchdog reset output
74                                            pin number 63 */
75
76 static unsigned short force_id;
77 module_param(force_id, ushort, 0);
78 MODULE_PARM_DESC(force_id, "Override the detected device ID");
79
80 static int timeout = WATCHDOG_TIMEOUT;  /* default timeout in seconds */
81 module_param(timeout, int, 0);
82 MODULE_PARM_DESC(timeout,
83         "Watchdog timeout in seconds. 1<= timeout <="
84                         __MODULE_STRING(WATCHDOG_MAX_TIMEOUT) " (default="
85                         __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
86
87 static unsigned int pulse_width = WATCHDOG_PULSE_WIDTH;
88 module_param(pulse_width, uint, 0);
89 MODULE_PARM_DESC(pulse_width,
90         "Watchdog signal pulse width. 0(=level), 1, 25, 30, 125, 150, 5000 or 6000 ms"
91                         " (default=" __MODULE_STRING(WATCHDOG_PULSE_WIDTH) ")");
92
93 static unsigned int f71862fg_pin = WATCHDOG_F71862FG_PIN;
94 module_param(f71862fg_pin, uint, 0);
95 MODULE_PARM_DESC(f71862fg_pin,
96         "Watchdog f71862fg reset output pin configuration. Choose pin 56 or 63"
97                         " (default=" __MODULE_STRING(WATCHDOG_F71862FG_PIN)")");
98
99 static bool nowayout = WATCHDOG_NOWAYOUT;
100 module_param(nowayout, bool, 0444);
101 MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
102
103 static unsigned int start_withtimeout;
104 module_param(start_withtimeout, uint, 0);
105 MODULE_PARM_DESC(start_withtimeout, "Start watchdog timer on module load with"
106         " given initial timeout. Zero (default) disables this feature.");
107
108 enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg,
109              f81803, f81865, f81866, f81966};
110
111 static const char * const fintek_wdt_names[] = {
112         "f71808fg",
113         "f71858fg",
114         "f71862fg",
115         "f71868",
116         "f71869",
117         "f71882fg",
118         "f71889fg",
119         "f81803",
120         "f81865",
121         "f81866",
122         "f81966"
123 };
124
125 /* Super-I/O Function prototypes */
126 static inline int superio_inb(int base, int reg);
127 static inline int superio_inw(int base, int reg);
128 static inline void superio_outb(int base, int reg, u8 val);
129 static inline void superio_set_bit(int base, int reg, int bit);
130 static inline void superio_clear_bit(int base, int reg, int bit);
131 static inline int superio_enter(int base);
132 static inline void superio_select(int base, int ld);
133 static inline void superio_exit(int base);
134
135 struct fintek_wdt {
136         struct watchdog_device wdd;
137         unsigned short  sioaddr;
138         enum chips      type;
139         struct watchdog_info ident;
140
141         u8              timer_val;      /* content for the wd_time register */
142         char            minutes_mode;
143         u8              pulse_val;      /* pulse width flag */
144         char            pulse_mode;     /* enable pulse output mode? */
145 };
146
147 struct fintek_wdt_pdata {
148         enum chips      type;
149 };
150
151 /* Super I/O functions */
152 static inline int superio_inb(int base, int reg)
153 {
154         outb(reg, base);
155         return inb(base + 1);
156 }
157
158 static int superio_inw(int base, int reg)
159 {
160         int val;
161         val  = superio_inb(base, reg) << 8;
162         val |= superio_inb(base, reg + 1);
163         return val;
164 }
165
166 static inline void superio_outb(int base, int reg, u8 val)
167 {
168         outb(reg, base);
169         outb(val, base + 1);
170 }
171
172 static inline void superio_set_bit(int base, int reg, int bit)
173 {
174         unsigned long val = superio_inb(base, reg);
175         __set_bit(bit, &val);
176         superio_outb(base, reg, val);
177 }
178
179 static inline void superio_clear_bit(int base, int reg, int bit)
180 {
181         unsigned long val = superio_inb(base, reg);
182         __clear_bit(bit, &val);
183         superio_outb(base, reg, val);
184 }
185
186 static inline int superio_enter(int base)
187 {
188         /* Don't step on other drivers' I/O space by accident */
189         if (!request_muxed_region(base, 2, DRVNAME)) {
190                 pr_err("I/O address 0x%04x already in use\n", (int)base);
191                 return -EBUSY;
192         }
193
194         /* according to the datasheet the key must be sent twice! */
195         outb(SIO_UNLOCK_KEY, base);
196         outb(SIO_UNLOCK_KEY, base);
197
198         return 0;
199 }
200
201 static inline void superio_select(int base, int ld)
202 {
203         outb(SIO_REG_LDSEL, base);
204         outb(ld, base + 1);
205 }
206
207 static inline void superio_exit(int base)
208 {
209         outb(SIO_LOCK_KEY, base);
210         release_region(base, 2);
211 }
212
213 static int fintek_wdt_set_timeout(struct watchdog_device *wdd, unsigned int timeout)
214 {
215         struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
216
217         if (timeout > 0xff) {
218                 wd->timer_val = DIV_ROUND_UP(timeout, 60);
219                 wd->minutes_mode = true;
220                 timeout = wd->timer_val * 60;
221         } else {
222                 wd->timer_val = timeout;
223                 wd->minutes_mode = false;
224         }
225
226         wdd->timeout = timeout;
227
228         return 0;
229 }
230
231 static int fintek_wdt_set_pulse_width(struct fintek_wdt *wd, unsigned int pw)
232 {
233         unsigned int t1 = 25, t2 = 125, t3 = 5000;
234
235         if (wd->type == f71868) {
236                 t1 = 30;
237                 t2 = 150;
238                 t3 = 6000;
239         }
240
241         if        (pw <=  1) {
242                 wd->pulse_val = 0;
243         } else if (pw <= t1) {
244                 wd->pulse_val = 1;
245         } else if (pw <= t2) {
246                 wd->pulse_val = 2;
247         } else if (pw <= t3) {
248                 wd->pulse_val = 3;
249         } else {
250                 pr_err("pulse width out of range\n");
251                 return -EINVAL;
252         }
253
254         wd->pulse_mode = pw;
255
256         return 0;
257 }
258
259 static int fintek_wdt_keepalive(struct watchdog_device *wdd)
260 {
261         struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
262         int err;
263
264         err = superio_enter(wd->sioaddr);
265         if (err)
266                 return err;
267         superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
268
269         if (wd->minutes_mode)
270                 /* select minutes for timer units */
271                 superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
272                                 F71808FG_FLAG_WD_UNIT);
273         else
274                 /* select seconds for timer units */
275                 superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
276                                 F71808FG_FLAG_WD_UNIT);
277
278         /* Set timer value */
279         superio_outb(wd->sioaddr, F71808FG_REG_WD_TIME,
280                            wd->timer_val);
281
282         superio_exit(wd->sioaddr);
283
284         return 0;
285 }
286
287 static int fintek_wdt_start(struct watchdog_device *wdd)
288 {
289         struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
290         int err;
291         u8 tmp;
292
293         /* Make sure we don't die as soon as the watchdog is enabled below */
294         err = fintek_wdt_keepalive(wdd);
295         if (err)
296                 return err;
297
298         err = superio_enter(wd->sioaddr);
299         if (err)
300                 return err;
301         superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
302
303         /* Watchdog pin configuration */
304         switch (wd->type) {
305         case f71808fg:
306                 /* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */
307                 superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT2, 3);
308                 superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 3);
309                 break;
310
311         case f71862fg:
312                 if (f71862fg_pin == 63) {
313                         /* SPI must be disabled first to use this pin! */
314                         superio_clear_bit(wd->sioaddr, SIO_REG_ROM_ADDR_SEL, 6);
315                         superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT3, 4);
316                 } else if (f71862fg_pin == 56) {
317                         superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1);
318                 }
319                 break;
320
321         case f71868:
322         case f71869:
323                 /* GPIO14 --> WDTRST# */
324                 superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT1, 4);
325                 break;
326
327         case f71882fg:
328                 /* Set pin 56 to WDTRST# */
329                 superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1);
330                 break;
331
332         case f71889fg:
333                 /* set pin 40 to WDTRST# */
334                 superio_outb(wd->sioaddr, SIO_REG_MFUNCT3,
335                         superio_inb(wd->sioaddr, SIO_REG_MFUNCT3) & 0xcf);
336                 break;
337
338         case f81803:
339                 /* Enable TSI Level register bank */
340                 superio_clear_bit(wd->sioaddr, SIO_REG_CLOCK_SEL, 3);
341                 /* Set pin 27 to WDTRST# */
342                 superio_outb(wd->sioaddr, SIO_REG_TSI_LEVEL_SEL, 0x5f &
343                         superio_inb(wd->sioaddr, SIO_REG_TSI_LEVEL_SEL));
344                 break;
345
346         case f81865:
347                 /* Set pin 70 to WDTRST# */
348                 superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 5);
349                 break;
350
351         case f81866:
352         case f81966:
353                 /*
354                  * GPIO1 Control Register when 27h BIT3:2 = 01 & BIT0 = 0.
355                  * The PIN 70(GPIO15/WDTRST) is controlled by 2Ch:
356                  *     BIT5: 0 -> WDTRST#
357                  *           1 -> GPIO15
358                  */
359                 tmp = superio_inb(wd->sioaddr, SIO_F81866_REG_PORT_SEL);
360                 tmp &= ~(BIT(3) | BIT(0));
361                 tmp |= BIT(2);
362                 superio_outb(wd->sioaddr, SIO_F81866_REG_PORT_SEL, tmp);
363
364                 superio_clear_bit(wd->sioaddr, SIO_F81866_REG_GPIO1, 5);
365                 break;
366
367         default:
368                 /*
369                  * 'default' label to shut up the compiler and catch
370                  * programmer errors
371                  */
372                 err = -ENODEV;
373                 goto exit_superio;
374         }
375
376         superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
377         superio_set_bit(wd->sioaddr, SIO_REG_ENABLE, 0);
378
379         if (wd->type == f81865 || wd->type == f81866 || wd->type == f81966)
380                 superio_set_bit(wd->sioaddr, F81865_REG_WDO_CONF,
381                                 F81865_FLAG_WDOUT_EN);
382         else
383                 superio_set_bit(wd->sioaddr, F71808FG_REG_WDO_CONF,
384                                 F71808FG_FLAG_WDOUT_EN);
385
386         superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
387                         F71808FG_FLAG_WD_EN);
388
389         if (wd->pulse_mode) {
390                 /* Select "pulse" output mode with given duration */
391                 u8 wdt_conf = superio_inb(wd->sioaddr,
392                                 F71808FG_REG_WDT_CONF);
393
394                 /* Set WD_PSWIDTH bits (1:0) */
395                 wdt_conf = (wdt_conf & 0xfc) | (wd->pulse_val & 0x03);
396                 /* Set WD_PULSE to "pulse" mode */
397                 wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE);
398
399                 superio_outb(wd->sioaddr, F71808FG_REG_WDT_CONF,
400                                 wdt_conf);
401         } else {
402                 /* Select "level" output mode */
403                 superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
404                                 F71808FG_FLAG_WD_PULSE);
405         }
406
407 exit_superio:
408         superio_exit(wd->sioaddr);
409
410         return err;
411 }
412
413 static int fintek_wdt_stop(struct watchdog_device *wdd)
414 {
415         struct fintek_wdt *wd = watchdog_get_drvdata(wdd);
416         int err;
417
418         err = superio_enter(wd->sioaddr);
419         if (err)
420                 return err;
421         superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
422
423         superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
424                         F71808FG_FLAG_WD_EN);
425
426         superio_exit(wd->sioaddr);
427
428         return 0;
429 }
430
431 static bool fintek_wdt_is_running(struct fintek_wdt *wd, u8 wdt_conf)
432 {
433         return (superio_inb(wd->sioaddr, SIO_REG_ENABLE) & BIT(0))
434                 && (wdt_conf & BIT(F71808FG_FLAG_WD_EN));
435 }
436
437 static const struct watchdog_ops fintek_wdt_ops = {
438         .owner = THIS_MODULE,
439         .start = fintek_wdt_start,
440         .stop = fintek_wdt_stop,
441         .ping = fintek_wdt_keepalive,
442         .set_timeout = fintek_wdt_set_timeout,
443 };
444
445 static int fintek_wdt_probe(struct platform_device *pdev)
446 {
447         struct device *dev = &pdev->dev;
448         struct fintek_wdt_pdata *pdata;
449         struct watchdog_device *wdd;
450         struct fintek_wdt *wd;
451         int wdt_conf, err = 0;
452         struct resource *res;
453         int sioaddr;
454
455         res = platform_get_resource(pdev, IORESOURCE_IO, 0);
456         if (!res)
457                 return -ENXIO;
458
459         sioaddr = res->start;
460
461         wd = devm_kzalloc(dev, sizeof(*wd), GFP_KERNEL);
462         if (!wd)
463                 return -ENOMEM;
464
465         pdata = dev->platform_data;
466
467         wd->type = pdata->type;
468         wd->sioaddr = sioaddr;
469         wd->ident.options = WDIOF_SETTIMEOUT
470                         | WDIOF_MAGICCLOSE
471                         | WDIOF_KEEPALIVEPING
472                         | WDIOF_CARDRESET;
473
474         snprintf(wd->ident.identity,
475                 sizeof(wd->ident.identity), "%s watchdog",
476                 fintek_wdt_names[wd->type]);
477
478         err = superio_enter(sioaddr);
479         if (err)
480                 return err;
481         superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
482
483         wdt_conf = superio_inb(sioaddr, F71808FG_REG_WDT_CONF);
484
485         /*
486          * We don't want WDTMOUT_STS to stick around till regular reboot.
487          * Write 1 to the bit to clear it to zero.
488          */
489         superio_outb(sioaddr, F71808FG_REG_WDT_CONF,
490                      wdt_conf | BIT(F71808FG_FLAG_WDTMOUT_STS));
491
492         wdd = &wd->wdd;
493
494         if (fintek_wdt_is_running(wd, wdt_conf))
495                 set_bit(WDOG_HW_RUNNING, &wdd->status);
496
497         superio_exit(sioaddr);
498
499         wdd->parent             = dev;
500         wdd->info               = &wd->ident;
501         wdd->ops                = &fintek_wdt_ops;
502         wdd->min_timeout        = 1;
503         wdd->max_timeout        = WATCHDOG_MAX_TIMEOUT;
504
505         watchdog_set_drvdata(wdd, wd);
506         watchdog_set_nowayout(wdd, nowayout);
507         watchdog_stop_on_unregister(wdd);
508         watchdog_stop_on_reboot(wdd);
509         watchdog_init_timeout(wdd, start_withtimeout ?: timeout, NULL);
510
511         if (wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS))
512                 wdd->bootstatus = WDIOF_CARDRESET;
513
514         /*
515          * WATCHDOG_HANDLE_BOOT_ENABLED can result in keepalive being directly
516          * called without a set_timeout before, so it needs to be done here
517          * unconditionally.
518          */
519         fintek_wdt_set_timeout(wdd, wdd->timeout);
520         fintek_wdt_set_pulse_width(wd, pulse_width);
521
522         if (start_withtimeout) {
523                 err = fintek_wdt_start(wdd);
524                 if (err) {
525                         dev_err(dev, "cannot start watchdog timer\n");
526                         return err;
527                 }
528
529                 set_bit(WDOG_HW_RUNNING, &wdd->status);
530                 dev_info(dev, "watchdog started with initial timeout of %u sec\n",
531                          start_withtimeout);
532         }
533
534         return devm_watchdog_register_device(dev, wdd);
535 }
536
537 static int __init fintek_wdt_find(int sioaddr)
538 {
539         enum chips type;
540         u16 devid;
541         int err = superio_enter(sioaddr);
542         if (err)
543                 return err;
544
545         devid = superio_inw(sioaddr, SIO_REG_MANID);
546         if (devid != SIO_FINTEK_ID) {
547                 pr_debug("Not a Fintek device\n");
548                 err = -ENODEV;
549                 goto exit;
550         }
551
552         devid = force_id ? force_id : superio_inw(sioaddr, SIO_REG_DEVID);
553         switch (devid) {
554         case SIO_F71808_ID:
555                 type = f71808fg;
556                 break;
557         case SIO_F71862_ID:
558                 type = f71862fg;
559                 break;
560         case SIO_F71868_ID:
561                 type = f71868;
562                 break;
563         case SIO_F71869_ID:
564         case SIO_F71869A_ID:
565                 type = f71869;
566                 break;
567         case SIO_F71882_ID:
568                 type = f71882fg;
569                 break;
570         case SIO_F71889_ID:
571                 type = f71889fg;
572                 break;
573         case SIO_F71858_ID:
574                 /* Confirmed (by datasheet) not to have a watchdog. */
575                 err = -ENODEV;
576                 goto exit;
577         case SIO_F81803_ID:
578                 type = f81803;
579                 break;
580         case SIO_F81865_ID:
581                 type = f81865;
582                 break;
583         case SIO_F81866_ID:
584                 type = f81866;
585                 break;
586         case SIO_F81966_ID:
587                 type = f81966;
588                 break;
589         default:
590                 pr_info("Unrecognized Fintek device: %04x\n",
591                         (unsigned int)devid);
592                 err = -ENODEV;
593                 goto exit;
594         }
595
596         pr_info("Found %s watchdog chip, revision %d\n",
597                 fintek_wdt_names[type],
598                 (int)superio_inb(sioaddr, SIO_REG_DEVREV));
599
600 exit:
601         superio_exit(sioaddr);
602         return err ? err : type;
603 }
604
605 static struct platform_driver fintek_wdt_driver = {
606         .probe          = fintek_wdt_probe,
607         .driver         = {
608                 .name   = DRVNAME,
609         },
610 };
611
612 static struct platform_device *fintek_wdt_pdev;
613
614 static int __init fintek_wdt_init(void)
615 {
616         static const unsigned short addrs[] = { 0x2e, 0x4e };
617         struct fintek_wdt_pdata pdata;
618         struct resource wdt_res = {};
619         int ret;
620         int i;
621
622         if (f71862fg_pin != 63 && f71862fg_pin != 56) {
623                 pr_err("Invalid argument f71862fg_pin=%d\n", f71862fg_pin);
624                 return -EINVAL;
625         }
626
627         for (i = 0; i < ARRAY_SIZE(addrs); i++) {
628                 ret = fintek_wdt_find(addrs[i]);
629                 if (ret >= 0)
630                         break;
631         }
632         if (i == ARRAY_SIZE(addrs))
633                 return ret;
634
635         pdata.type = ret;
636
637         ret = platform_driver_register(&fintek_wdt_driver);
638         if (ret)
639                 return ret;
640
641         wdt_res.name = "superio port";
642         wdt_res.flags = IORESOURCE_IO;
643         wdt_res.start = addrs[i];
644         wdt_res.end   = addrs[i] + 1;
645
646         fintek_wdt_pdev = platform_device_register_resndata(NULL, DRVNAME, -1,
647                                                             &wdt_res, 1,
648                                                             &pdata, sizeof(pdata));
649         if (IS_ERR(fintek_wdt_pdev)) {
650                 platform_driver_unregister(&fintek_wdt_driver);
651                 return PTR_ERR(fintek_wdt_pdev);
652         }
653
654         return 0;
655 }
656
657 static void __exit fintek_wdt_exit(void)
658 {
659         platform_device_unregister(fintek_wdt_pdev);
660         platform_driver_unregister(&fintek_wdt_driver);
661 }
662
663 MODULE_DESCRIPTION("F71808E Watchdog Driver");
664 MODULE_AUTHOR("Giel van Schijndel <me@mortis.eu>");
665 MODULE_LICENSE("GPL");
666
667 module_init(fintek_wdt_init);
668 module_exit(fintek_wdt_exit);