This is an adaptation of the Linux v5.3 f71808e_wdt driver for the watchdog
component of the Fintek Super I/O chips.

Signed-off-by: Ahmad Fatoum <[email protected]>
---
 drivers/watchdog/Kconfig       |  10 +
 drivers/watchdog/Makefile      |   1 +
 drivers/watchdog/f71808e_wdt.c | 379 +++++++++++++++++++++++++++++++++
 3 files changed, 390 insertions(+)
 create mode 100644 drivers/watchdog/f71808e_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index fbaab896d460..b1c2a39b6629 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -90,10 +90,20 @@ config STM32_IWDG_WATCHDOG
          Enable to support configuration of the STM32's on-SoC IWDG watchdog.
          Once started by the user, the IWDG can't be disabled.
 
+
 config STPMIC1_WATCHDOG
        bool "STPMIC1 Watchdog"
        depends on MFD_STPMIC1
        help
          Enable to support configuration of the stpmic1's built-in watchdog.
 
+config F71808E_WDT
+       bool "Fintek F718xx, F818xx Super I/O Watchdog"
+       depends on X86
+       select FINTEK_SUPERIO
+       help
+         This is the driver for the hardware watchdog on the Fintek F71808E,
+         F71862FG, F71868, F71869, F71882FG, F71889FG, F81865 and F81866
+         Super I/O controllers.
+
 endif
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 1fbd780885cb..63efc2a87ec4 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -13,3 +13,4 @@ obj-$(CONFIG_ARCH_BCM283X) += bcm2835_wdt.o
 obj-$(CONFIG_RAVE_SP_WATCHDOG) += rave-sp-wdt.o
 obj-$(CONFIG_STM32_IWDG_WATCHDOG) += stm32_iwdg.o
 obj-$(CONFIG_STPMIC1_WATCHDOG) += stpmic1_wdt.o
+obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o
diff --git a/drivers/watchdog/f71808e_wdt.c b/drivers/watchdog/f71808e_wdt.c
new file mode 100644
index 000000000000..4f881a1d02bc
--- /dev/null
+++ b/drivers/watchdog/f71808e_wdt.c
@@ -0,0 +1,379 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/***************************************************************************
+ *   Copyright (C) 2006 by Hans Edgington <[email protected]>              *
+ *   Copyright (C) 2007-2009 Hans de Goede <[email protected]>           *
+ *   Copyright (C) 2010 Giel van Schijndel <[email protected]>                  *
+ *   Copyright (C) 2019 Ahmad Fatoum <[email protected]>             *
+ *                                                                         *
+ ***************************************************************************/
+
+#define pr_fmt(fmt) "f71808e_wdt: " fmt
+
+#include <init.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <driver.h>
+#include <watchdog.h>
+#include <printk.h>
+#include <reset_source.h>
+#include <superio.h>
+#include <common.h>
+
+#define SIO_F71808FG_LD_WDT    0x07    /* Watchdog timer logical device */
+#define SIO_UNLOCK_KEY         0x87    /* Key to enable Super-I/O */
+#define SIO_LOCK_KEY           0xAA    /* Key to disable Super-I/O */
+
+#define SIO_REG_LDSEL          0x07    /* Logical device select */
+#define SIO_REG_DEVREV         0x22    /* Device revision */
+#define SIO_REG_ROM_ADDR_SEL   0x27    /* ROM address select */
+#define SIO_F81866_REG_PORT_SEL        0x27    /* F81866 Multi-Function 
Register */
+#define SIO_REG_MFUNCT1                0x29    /* Multi function select 1 */
+#define SIO_REG_MFUNCT2                0x2a    /* Multi function select 2 */
+#define SIO_REG_MFUNCT3                0x2b    /* Multi function select 3 */
+#define SIO_F81866_REG_GPIO1   0x2c    /* F81866 GPIO1 Enable Register */
+#define SIO_REG_ENABLE         0x30    /* Logical device enable */
+#define SIO_REG_ADDR           0x60    /* Logical device address (2 bytes) */
+
+#define F71808FG_REG_WDO_CONF          0xf0
+#define F71808FG_REG_WDT_CONF          0xf5
+#define F71808FG_REG_WD_TIME           0xf6
+
+#define F71808FG_FLAG_WDOUT_EN         7
+
+#define F71808FG_FLAG_WDTMOUT_STS      6
+#define F71808FG_FLAG_WD_EN            5
+#define F71808FG_FLAG_WD_PULSE         4
+#define F71808FG_FLAG_WD_UNIT          3
+
+#define F81865_REG_WDO_CONF            0xfa
+#define F81865_FLAG_WDOUT_EN           0
+
+/* Default values */
+#define WATCHDOG_MAX_TIMEOUT   (60 * 255)
+
+enum pulse_width {
+       PULSE_WIDTH_LEVEL, PULSE_WIDTH_1MS,
+       PULSE_WIDTH_LOW, PULSE_WIDTH_MID, PULSE_WIDTH_HIGH
+};
+
+const char *pulse_width_names[] = { "level", "1", "25", "125", "5000" };
+const char *pulse_width_names_f71868[] = { "level", "1", "30", "150", "6000" };
+
+enum wdtrst_pin {
+       WDTRST_PIN_56, WDTRST_PIN_63,
+};
+
+const char *f71862fg_pin_names[] = { "56", "63" };
+
+enum chips { f71808fg, f71858fg, f71862fg, f71868, f71869, f71882fg, f71889fg,
+            f81865, f81866};
+
+struct f71808e_wdt;
+
+struct f71808e_variant_data {
+       enum chips      type;
+       void (*pinconf)(struct f71808e_wdt *wd);
+};
+
+struct f71808e_wdt {
+       struct watchdog wdd;
+       u16             sioaddr;
+       const struct f71808e_variant_data *variant;
+       unsigned int    timeout;
+       u8              timer_val;      /* content for the wd_time register */
+       char            minutes_mode;
+       int             pulse_width;
+       int             f71862fg_pin;
+};
+
+static inline struct f71808e_wdt *to_f71808e_wdt(struct watchdog *wdd)
+{
+       return container_of(wdd, struct f71808e_wdt, wdd);
+}
+
+static inline bool has_f81865_wdo_conf(struct f71808e_wdt *wd)
+{
+       return wd->variant->type == f81865 || wd->variant->type == f81866;
+}
+
+static inline void superio_enter(u16 base)
+{
+       /* according to the datasheet the key must be sent twice! */
+       outb(SIO_UNLOCK_KEY, base);
+       outb(SIO_UNLOCK_KEY, base);
+}
+
+static inline void superio_select(u16 base, int ld)
+{
+       outb(SIO_REG_LDSEL, base);
+       outb(ld, base + 1);
+}
+
+static inline void superio_exit(u16 base)
+{
+       outb(SIO_LOCK_KEY, base);
+}
+
+static void f71808e_wdt_keepalive(struct f71808e_wdt *wd)
+{
+       superio_enter(wd->sioaddr);
+
+       superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
+
+       if (wd->minutes_mode)
+               /* select minutes for timer units */
+               superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
+                               F71808FG_FLAG_WD_UNIT);
+       else
+               /* select seconds for timer units */
+               superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
+                               F71808FG_FLAG_WD_UNIT);
+
+       /* Set timer value */
+       superio_outb(wd->sioaddr, F71808FG_REG_WD_TIME,
+                    wd->timer_val);
+
+       superio_exit(wd->sioaddr);
+}
+
+static void f71808e_wdt_start(struct f71808e_wdt *wd)
+{
+       /* Make sure we don't die as soon as the watchdog is enabled below */
+       f71808e_wdt_keepalive(wd);
+
+       superio_enter(wd->sioaddr);
+
+       superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
+
+       /* Watchdog pin configuration */
+       wd->variant->pinconf(wd);
+
+       superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
+       superio_set_bit(wd->sioaddr, SIO_REG_ENABLE, 0);
+
+       if (has_f81865_wdo_conf(wd))
+               superio_set_bit(wd->sioaddr, F81865_REG_WDO_CONF,
+                               F81865_FLAG_WDOUT_EN);
+       else
+               superio_set_bit(wd->sioaddr, F71808FG_REG_WDO_CONF,
+                               F71808FG_FLAG_WDOUT_EN);
+
+       superio_set_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
+                       F71808FG_FLAG_WD_EN);
+
+       if (wd->pulse_width > 0) {
+               /* Select "pulse" output mode with given duration */
+               u8 wdt_conf = superio_inb(wd->sioaddr, F71808FG_REG_WDT_CONF);
+
+               /* Set WD_PSWIDTH bits (1:0) */
+               wdt_conf = (wdt_conf & 0xfc) | (wd->pulse_width & 0x03);
+               /* Set WD_PULSE to "pulse" mode */
+               wdt_conf |= BIT(F71808FG_FLAG_WD_PULSE);
+
+               superio_outb(wd->sioaddr, F71808FG_REG_WDT_CONF, wdt_conf);
+       } else {
+               /* Select "level" output mode */
+               superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
+                                 F71808FG_FLAG_WD_PULSE);
+       }
+
+       superio_exit(wd->sioaddr);
+}
+
+static void f71808e_wdt_stop(struct f71808e_wdt *wd)
+{
+       superio_enter(wd->sioaddr);
+
+       superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
+
+       superio_clear_bit(wd->sioaddr, F71808FG_REG_WDT_CONF,
+                         F71808FG_FLAG_WD_EN);
+
+       superio_exit(wd->sioaddr);
+}
+
+static int f71808e_wdt_set_timeout(struct watchdog *wdd, unsigned int 
new_timeout)
+{
+       struct f71808e_wdt *wd = to_f71808e_wdt(wdd);
+
+       if (!new_timeout) {
+               f71808e_wdt_stop(wd);
+               return 0;
+       }
+
+       if (wd->timeout != new_timeout) {
+               if (new_timeout > 0xff) {
+                       wd->timer_val = DIV_ROUND_UP(new_timeout, 60);
+                       wd->minutes_mode = true;
+               } else {
+                       wd->timer_val = new_timeout;
+                       wd->minutes_mode = false;
+               }
+
+               f71808e_wdt_start(wd);
+               wd->timeout = new_timeout;
+       }
+
+       f71808e_wdt_keepalive(wd);
+       return 0;
+}
+
+static int f71808e_wdt_init(struct f71808e_wdt *wd, struct device_d *dev)
+{
+       struct watchdog *wdd = &wd->wdd;
+       const char * const *names = pulse_width_names;
+       int wdt_conf;
+       int ret;
+
+       superio_enter(wd->sioaddr);
+
+       superio_select(wd->sioaddr, SIO_F71808FG_LD_WDT);
+
+       wdt_conf = superio_inb(wd->sioaddr, F71808FG_REG_WDT_CONF);
+
+       superio_exit(wd->sioaddr);
+
+       if (wd->variant->type == f71868)
+               names = pulse_width_names_f71868;
+
+       wd->pulse_width = PULSE_WIDTH_MID; /* either 125ms or 150ms */
+
+       dev_add_param_enum(dev, "pulse_width_ms", NULL, NULL,
+                          &wd->pulse_width, names,
+                          ARRAY_SIZE(pulse_width_names),
+                          wd);
+
+       if (wd->variant->type == f71862fg) {
+               wd->f71862fg_pin = WDTRST_PIN_63;
+
+               dev_add_param_enum(dev, "wdtrst_pin", NULL, NULL,
+                                  &wd->f71862fg_pin, f71862fg_pin_names,
+                                  ARRAY_SIZE(f71862fg_pin_names),
+                                  wd);
+       }
+
+       wdd->hwdev              = dev;
+       wdd->set_timeout        = &f71808e_wdt_set_timeout;
+       wdd->timeout_max        = WATCHDOG_MAX_TIMEOUT;
+
+       if (wdt_conf & BIT(F71808FG_FLAG_WDTMOUT_STS))
+               reset_source_set_priority(RESET_WDG,
+                                         RESET_SOURCE_DEFAULT_PRIORITY);
+
+       dev_info(dev, "reset reason: %s\n", reset_source_name());
+
+       ret = watchdog_register(wdd);
+       if (ret)
+               return ret;
+
+       superio_enter(wd->sioaddr);
+       dev_info(dev, "revision %d probed.\n",
+                superio_inb(wd->sioaddr, SIO_REG_DEVREV));
+       superio_exit(wd->sioaddr);
+
+       return 0;
+}
+
+static void f71808fg_pinconf(struct f71808e_wdt *wd)
+{
+       /* Set pin 21 to GPIO23/WDTRST#, then to WDTRST# */
+       superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT2, 3);
+       superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 3);
+}
+static void f71862fg_pinconf(struct f71808e_wdt *wd)
+{
+       u16 ioaddr = wd->sioaddr;
+
+       if (wd->f71862fg_pin == WDTRST_PIN_63) {
+               /* SPI must be disabled first to use this pin! */
+               superio_clear_bit(ioaddr, SIO_REG_ROM_ADDR_SEL, 6);
+               superio_set_bit(ioaddr, SIO_REG_MFUNCT3, 4);
+       } else if (wd->f71862fg_pin == WDTRST_PIN_56) {
+               superio_set_bit(ioaddr, SIO_REG_MFUNCT1, 1);
+       }
+}
+static void f71868_pinconf(struct f71808e_wdt *wd)
+{
+       /* GPIO14 --> WDTRST# */
+       superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT1, 4);
+}
+static void f71882fg_pinconf(struct f71808e_wdt *wd)
+{
+       /* Set pin 56 to WDTRST# */
+       superio_set_bit(wd->sioaddr, SIO_REG_MFUNCT1, 1);
+}
+static void f71889fg_pinconf(struct f71808e_wdt *wd)
+{
+       /* set pin 40 to WDTRST# */
+       superio_outb(wd->sioaddr, SIO_REG_MFUNCT3,
+               superio_inb(wd->sioaddr, SIO_REG_MFUNCT3) & 0xcf);
+}
+static void f81865_pinconf(struct f71808e_wdt *wd)
+{
+       /* Set pin 70 to WDTRST# */
+       superio_clear_bit(wd->sioaddr, SIO_REG_MFUNCT3, 5);
+}
+static void f81866_pinconf(struct f71808e_wdt *wd)
+{
+       /*
+        * GPIO1 Control Register when 27h BIT3:2 = 01 & BIT0 = 0.
+        * The PIN 70(GPIO15/WDTRST) is controlled by 2Ch:
+        *     BIT5: 0 -> WDTRST#
+        *           1 -> GPIO15
+        */
+       u8 tmp = superio_inb(wd->sioaddr, SIO_F81866_REG_PORT_SEL);
+       tmp &= ~(BIT(3) | BIT(0));
+       tmp |= BIT(2);
+       superio_outb(wd->sioaddr, SIO_F81866_REG_PORT_SEL, tmp);
+
+       superio_clear_bit(wd->sioaddr, SIO_F81866_REG_GPIO1, 5);
+}
+
+static struct f71808e_variant_data f71808fg_data = { .type = f71808fg, 
.pinconf = f71808fg_pinconf };
+static struct f71808e_variant_data f71862fg_data = { .type = f71862fg, 
.pinconf = f71862fg_pinconf };
+static struct f71808e_variant_data f71868_data = { .type = f71868, .pinconf = 
f71868_pinconf };
+static struct f71808e_variant_data f71869_data = { .type = f71869, .pinconf = 
f71868_pinconf };
+static struct f71808e_variant_data f71882fg_data = { .type = f71882fg, 
.pinconf = f71882fg_pinconf };
+static struct f71808e_variant_data f71889fg_data = { .type = f71889fg, 
.pinconf = f71889fg_pinconf };
+static struct f71808e_variant_data f81865_data = { .type = f81865, .pinconf = 
f81865_pinconf };
+static struct f71808e_variant_data f81866_data = { .type = f81866, .pinconf = 
f81866_pinconf };
+
+static struct platform_device_id f71808e_wdt_ids[] = {
+       { .name = "f71808fg_wdt", .driver_data = (unsigned long)&f71808fg_data 
},
+       { .name = "f71862fg_wdt", .driver_data = (unsigned long)&f71862fg_data 
},
+       { .name = "f71868_wdt", .driver_data = (unsigned long)&f71868_data },
+       { .name = "f71869_wdt", .driver_data = (unsigned long)&f71869_data },
+       { .name = "f71882fg_wdt", .driver_data = (unsigned long)&f71882fg_data 
},
+       { .name = "f71889fg_wdt", .driver_data = (unsigned long)&f71889fg_data 
},
+       { .name = "f81865_wdt", .driver_data = (unsigned long)&f81865_data },
+       { .name = "f81866_wdt", .driver_data = (unsigned long)&f81866_data },
+       { /* sentinel */ },
+};
+
+static int f71808e_probe(struct device_d *dev)
+{
+       struct f71808e_wdt *wd;
+       struct resource *res;
+       int ret;
+
+       wd = xzalloc(sizeof(*wd));
+
+       ret = dev_get_drvdata(dev, (const void **)&wd->variant);
+       if (ret)
+               return ret;
+
+       res = dev_get_resource(dev->parent, IORESOURCE_IO, 0);
+       if (IS_ERR(res))
+               return PTR_ERR(res);
+       wd->sioaddr = res->start;
+
+       return f71808e_wdt_init(wd, dev);
+}
+
+static struct driver_d f71808e_wdt_driver = {
+       .probe  = f71808e_probe,
+       .name   = "f71808e_wdt",
+       .id_table = f71808e_wdt_ids,
+};
+
+device_platform_driver(f71808e_wdt_driver);
-- 
2.23.0


_______________________________________________
barebox mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/barebox

Reply via email to