This driver supports the photonicat watchdog timers.

Signed-off-by: Coia Prant <coiapr...@gmail.com>
---
 drivers/watchdog/Kconfig    |   6 +
 drivers/watchdog/Makefile   |   1 +
 drivers/watchdog/pcat_wdt.c | 218 ++++++++++++++++++++++++++++++++++++
 3 files changed, 225 insertions(+)
 create mode 100644 drivers/watchdog/pcat_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 0c3e9913..f0a52ecf 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -421,4 +421,10 @@ config WDT_FTWDT010
        help
          Faraday Technology ftwdt010 watchdog is an architecture independent
          watchdog. It is usually used in SoC chip design.
+
+config WDT_PCAT
+       bool "photonicat board watchdog support"
+       depends on WDT && SERIAL && DM
+       help
+         Select this to enable watchdog timer on photonicat board.
 endmenu
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 7b39adcf..829b5ed1 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -50,3 +50,4 @@ obj-$(CONFIG_WDT_STM32MP) += stm32mp_wdt.o
 obj-$(CONFIG_WDT_SUNXI) += sunxi_wdt.o
 obj-$(CONFIG_WDT_TANGIER) += tangier_wdt.o
 obj-$(CONFIG_WDT_XILINX) += xilinx_wwdt.o
+obj-$(CONFIG_WDT_PCAT) += pcat_wdt.o
\ No newline at end of file
diff --git a/drivers/watchdog/pcat_wdt.c b/drivers/watchdog/pcat_wdt.c
new file mode 100644
index 00000000..cac07fb6
--- /dev/null
+++ b/drivers/watchdog/pcat_wdt.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <wdt.h>
+#include <asm/gpio.h>
+#include <linux/delay.h>
+#include <serial.h>
+#include <dm/device-internal.h>
+
+struct pcat_wdt_priv
+{
+       struct udevice *serial_dev;
+       struct dm_serial_ops *serial_ops;
+       uint16_t packet_count;
+};
+
+static uint16_t pcat_pmu_serial_compute_crc16(const uint8_t *data,
+                                                                               
          size_t len)
+{
+       uint16_t crc = 0xFFFF;
+       size_t i;
+       unsigned int j;
+
+       for (i = 0; i < len; i++)
+       {
+               crc ^= data[i];
+               for (j = 0; j < 8; j++)
+               {
+                       if (crc & 1)
+                       {
+                               crc = (crc >> 1) ^ 0xA001;
+                       }
+                       else
+                       {
+                               crc >>= 1;
+                       }
+               }
+       }
+
+       return crc;
+}
+
+static int pcat_wdt_reset(struct udevice *dev)
+{
+       struct pcat_wdt_priv *priv = dev_get_priv(dev);
+       uint8_t packet[13] = "\xA5\x01\x81\x00\x00\x03\x00\x01\x00"
+                                                "\x00\x00\x00\x5A";
+       uint16_t crc;
+       int err = 0;
+       unsigned int i;
+
+       packet[3] = priv->packet_count & 0xFF;
+       packet[4] = (priv->packet_count >> 8) & 0xFF;
+       priv->packet_count++;
+
+       crc = pcat_pmu_serial_compute_crc16(packet + 1, 9);
+       packet[10] = crc & 0xFF;
+       packet[11] = (crc >> 8) & 0xFF;
+
+       i = 0;
+       while (i < 13)
+       {
+               err = priv->serial_ops->putc(
+                       priv->serial_dev, ((const char *)packet)[i]);
+               if (!err)
+               {
+                       i++;
+               }
+               else if (err == -EAGAIN)
+               {
+                       ;
+               }
+               else
+               {
+                       pr_err("%s: unable to send watchdog setup "
+                                  "request: %d\n",
+                                  __func__, err);
+                       break;
+               }
+       }
+       return err;
+}
+
+static int pcat_wdt_setup(struct pcat_wdt_priv *priv, u64 timeout)
+{
+       uint8_t packet[16] = "\xA5\x01\x81\x00\x00\x06\x00\x13\x00"
+                                                "\x3C\x3C\x00\x00\x00\x00\x5A";
+       uint16_t crc;
+       int err = 0;
+       unsigned int i;
+
+       if (timeout > 255)
+       {
+               pr_warn("%s: timeout cannot be more than 255s\n",
+                               __func__);
+               timeout = 255;
+       }
+
+       packet[3] = priv->packet_count & 0xFF;
+       packet[4] = (priv->packet_count >> 8) & 0xFF;
+       priv->packet_count++;
+       packet[11] = timeout & 0xFF;
+
+       crc = pcat_pmu_serial_compute_crc16(packet + 1, 12);
+       packet[13] = crc & 0xFF;
+       packet[14] = (crc >> 8) & 0xFF;
+
+       i = 0;
+       while (i < 16)
+       {
+               err = priv->serial_ops->putc(
+                       priv->serial_dev, ((const char *)packet)[i]);
+               if (!err)
+               {
+                       i++;
+               }
+               else if (err == -EAGAIN)
+               {
+                       ;
+               }
+               else
+               {
+                       pr_err("%s: unable to send watchdog setup "
+                                  "request: %d\n",
+                                  __func__, err);
+                       break;
+               }
+       }
+
+       return err;
+}
+
+static int pcat_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
+{
+       struct pcat_wdt_priv *priv = dev_get_priv(dev);
+
+       pcat_wdt_setup(priv, timeout);
+
+       return 0;
+}
+
+static int pcat_wdt_stop(struct udevice *dev)
+{
+       struct pcat_wdt_priv *priv = dev_get_priv(dev);
+
+       pcat_wdt_setup(priv, 0);
+
+       return 0;
+}
+
+static int dm_probe(struct udevice *dev)
+{
+       struct pcat_wdt_priv *priv = dev_get_priv(dev);
+       struct udevice *serial_dev = NULL;
+       struct dm_serial_ops *ops;
+       int ret;
+
+       ret = uclass_get_device_by_phandle(UCLASS_SERIAL, dev, "port",
+                                                                          
&serial_dev);
+       if (ret)
+       {
+               pr_err("%s: unable to find serial port device: %d\n",
+                          __func__, ret);
+               return ret;
+       }
+
+       ret = device_probe(serial_dev);
+       if (ret)
+       {
+               pr_err("%s: unable to probe serial port device: %d\n",
+                          __func__, ret);
+               return ret;
+       }
+
+       ops = serial_get_ops(serial_dev);
+       if (!ops)
+       {
+               printf("Cannot get ops for PMU serial port!\n");
+               return -EINVAL;
+       }
+
+       if (ops->setconfig)
+       {
+               ops->setconfig(serial_dev, SERIAL_DEFAULT_CONFIG);
+       }
+       if (ops->setbrg)
+       {
+               ops->setbrg(serial_dev, 115200);
+       }
+
+       priv->serial_dev = serial_dev;
+       priv->serial_ops = ops;
+       priv->packet_count = 0;
+
+       pcat_wdt_stop(dev);
+
+       return 0;
+}
+
+static const struct wdt_ops pcat_wdt_ops = {
+       .start = pcat_wdt_start,
+       .reset = pcat_wdt_reset,
+       .stop = pcat_wdt_stop,
+};
+
+static const struct udevice_id pcat_wdt_ids[] = {
+       {.compatible = "linux,wdt-pcat"},
+       {}};
+
+U_BOOT_DRIVER(wdt_pcat) = {
+       .name = "wdt_pcat",
+       .id = UCLASS_WDT,
+       .of_match = pcat_wdt_ids,
+       .ops = &pcat_wdt_ops,
+       .probe = dm_probe,
+       .priv_auto = sizeof(struct pcat_wdt_priv),
+};
\ No newline at end of file
-- 
2.39.2

Reply via email to