Merge tag 'drm-misc-fixes-2021-07-13' of git://anongit.freedesktop.org/drm/drm-misc...
[linux-2.6-microblaze.git] / drivers / watchdog / sbsa_gwdt.c
index f0f1e3b..ee9ff38 100644 (file)
 #define SBSA_GWDT_WCS_WS0      BIT(1)
 #define SBSA_GWDT_WCS_WS1      BIT(2)
 
+#define SBSA_GWDT_VERSION_MASK  0xF
+#define SBSA_GWDT_VERSION_SHIFT 16
+
 /**
  * struct sbsa_gwdt - Internal representation of the SBSA GWDT
  * @wdd:               kernel watchdog_device structure
  * @clk:               store the System Counter clock frequency, in Hz.
+ * @version:            store the architecture version
  * @refresh_base:      Virtual address of the watchdog refresh frame
  * @control_base:      Virtual address of the watchdog control frame
  */
 struct sbsa_gwdt {
        struct watchdog_device  wdd;
        u32                     clk;
+       int                     version;
        void __iomem            *refresh_base;
        void __iomem            *control_base;
 };
@@ -112,6 +117,30 @@ MODULE_PARM_DESC(nowayout,
                 "Watchdog cannot be stopped once started (default="
                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
+/*
+ * Arm Base System Architecture 1.0 introduces watchdog v1 which
+ * increases the length watchdog offset register to 48 bits.
+ * - For version 0: WOR is 32 bits;
+ * - For version 1: WOR is 48 bits which comprises the register
+ * offset 0x8 and 0xC, and the bits [63:48] are reserved which are
+ * Read-As-Zero and Writes-Ignored.
+ */
+static u64 sbsa_gwdt_reg_read(struct sbsa_gwdt *gwdt)
+{
+       if (gwdt->version == 0)
+               return readl(gwdt->control_base + SBSA_GWDT_WOR);
+       else
+               return readq(gwdt->control_base + SBSA_GWDT_WOR);
+}
+
+static void sbsa_gwdt_reg_write(u64 val, struct sbsa_gwdt *gwdt)
+{
+       if (gwdt->version == 0)
+               writel((u32)val, gwdt->control_base + SBSA_GWDT_WOR);
+       else
+               writeq(val, gwdt->control_base + SBSA_GWDT_WOR);
+}
+
 /*
  * watchdog operation functions
  */
@@ -123,16 +152,14 @@ static int sbsa_gwdt_set_timeout(struct watchdog_device *wdd,
        wdd->timeout = timeout;
 
        if (action)
-               writel(gwdt->clk * timeout,
-                      gwdt->control_base + SBSA_GWDT_WOR);
+               sbsa_gwdt_reg_write(gwdt->clk * timeout, gwdt);
        else
                /*
                 * In the single stage mode, The first signal (WS0) is ignored,
                 * the timeout is (WOR * 2), so the WOR should be configured
                 * to half value of timeout.
                 */
-               writel(gwdt->clk / 2 * timeout,
-                      gwdt->control_base + SBSA_GWDT_WOR);
+               sbsa_gwdt_reg_write(gwdt->clk / 2 * timeout, gwdt);
 
        return 0;
 }
@@ -149,7 +176,7 @@ static unsigned int sbsa_gwdt_get_timeleft(struct watchdog_device *wdd)
         */
        if (!action &&
            !(readl(gwdt->control_base + SBSA_GWDT_WCS) & SBSA_GWDT_WCS_WS0))
-               timeleft += readl(gwdt->control_base + SBSA_GWDT_WOR);
+               timeleft += sbsa_gwdt_reg_read(gwdt);
 
        timeleft += lo_hi_readq(gwdt->control_base + SBSA_GWDT_WCV) -
                    arch_timer_read_counter();
@@ -172,6 +199,17 @@ static int sbsa_gwdt_keepalive(struct watchdog_device *wdd)
        return 0;
 }
 
+static void sbsa_gwdt_get_version(struct watchdog_device *wdd)
+{
+       struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
+       int ver;
+
+       ver = readl(gwdt->control_base + SBSA_GWDT_W_IIDR);
+       ver = (ver >> SBSA_GWDT_VERSION_SHIFT) & SBSA_GWDT_VERSION_MASK;
+
+       gwdt->version = ver;
+}
+
 static int sbsa_gwdt_start(struct watchdog_device *wdd)
 {
        struct sbsa_gwdt *gwdt = watchdog_get_drvdata(wdd);
@@ -252,10 +290,14 @@ static int sbsa_gwdt_probe(struct platform_device *pdev)
        wdd->info = &sbsa_gwdt_info;
        wdd->ops = &sbsa_gwdt_ops;
        wdd->min_timeout = 1;
-       wdd->max_hw_heartbeat_ms = U32_MAX / gwdt->clk * 1000;
        wdd->timeout = DEFAULT_TIMEOUT;
        watchdog_set_drvdata(wdd, gwdt);
        watchdog_set_nowayout(wdd, nowayout);
+       sbsa_gwdt_get_version(wdd);
+       if (gwdt->version == 0)
+               wdd->max_hw_heartbeat_ms = U32_MAX / gwdt->clk * 1000;
+       else
+               wdd->max_hw_heartbeat_ms = GENMASK_ULL(47, 0) / gwdt->clk * 1000;
 
        status = readl(cf_base + SBSA_GWDT_WCS);
        if (status & SBSA_GWDT_WCS_WS1) {