158bf0164SHarini Katakam /* 258bf0164SHarini Katakam * Cadence WDT driver - Used by Xilinx Zynq 358bf0164SHarini Katakam * 458bf0164SHarini Katakam * Copyright (C) 2010 - 2014 Xilinx, Inc. 558bf0164SHarini Katakam * 658bf0164SHarini Katakam * This program is free software; you can redistribute it and/or 758bf0164SHarini Katakam * modify it under the terms of the GNU General Public License 858bf0164SHarini Katakam * as published by the Free Software Foundation; either version 958bf0164SHarini Katakam * 2 of the License, or (at your option) any later version. 1058bf0164SHarini Katakam */ 1158bf0164SHarini Katakam 1258bf0164SHarini Katakam #include <linux/clk.h> 1358bf0164SHarini Katakam #include <linux/init.h> 1458bf0164SHarini Katakam #include <linux/interrupt.h> 1558bf0164SHarini Katakam #include <linux/io.h> 1658bf0164SHarini Katakam #include <linux/irq.h> 1758bf0164SHarini Katakam #include <linux/kernel.h> 1858bf0164SHarini Katakam #include <linux/module.h> 1958bf0164SHarini Katakam #include <linux/of.h> 2058bf0164SHarini Katakam #include <linux/platform_device.h> 2158bf0164SHarini Katakam #include <linux/watchdog.h> 2258bf0164SHarini Katakam 2358bf0164SHarini Katakam #define CDNS_WDT_DEFAULT_TIMEOUT 10 2458bf0164SHarini Katakam /* Supports 1 - 516 sec */ 2558bf0164SHarini Katakam #define CDNS_WDT_MIN_TIMEOUT 1 2658bf0164SHarini Katakam #define CDNS_WDT_MAX_TIMEOUT 516 2758bf0164SHarini Katakam 2858bf0164SHarini Katakam /* Restart key */ 2958bf0164SHarini Katakam #define CDNS_WDT_RESTART_KEY 0x00001999 3058bf0164SHarini Katakam 3158bf0164SHarini Katakam /* Counter register access key */ 3258bf0164SHarini Katakam #define CDNS_WDT_REGISTER_ACCESS_KEY 0x00920000 3358bf0164SHarini Katakam 3458bf0164SHarini Katakam /* Counter value divisor */ 3558bf0164SHarini Katakam #define CDNS_WDT_COUNTER_VALUE_DIVISOR 0x1000 3658bf0164SHarini Katakam 3758bf0164SHarini Katakam /* Clock prescaler value and selection */ 3858bf0164SHarini Katakam #define CDNS_WDT_PRESCALE_64 64 3958bf0164SHarini Katakam #define CDNS_WDT_PRESCALE_512 512 4058bf0164SHarini Katakam #define CDNS_WDT_PRESCALE_4096 4096 4158bf0164SHarini Katakam #define CDNS_WDT_PRESCALE_SELECT_64 1 4258bf0164SHarini Katakam #define CDNS_WDT_PRESCALE_SELECT_512 2 4358bf0164SHarini Katakam #define CDNS_WDT_PRESCALE_SELECT_4096 3 4458bf0164SHarini Katakam 4558bf0164SHarini Katakam /* Input clock frequency */ 4658bf0164SHarini Katakam #define CDNS_WDT_CLK_10MHZ 10000000 4758bf0164SHarini Katakam #define CDNS_WDT_CLK_75MHZ 75000000 4858bf0164SHarini Katakam 4958bf0164SHarini Katakam /* Counter maximum value */ 5058bf0164SHarini Katakam #define CDNS_WDT_COUNTER_MAX 0xFFF 5158bf0164SHarini Katakam 5258bf0164SHarini Katakam static int wdt_timeout = CDNS_WDT_DEFAULT_TIMEOUT; 5358bf0164SHarini Katakam static int nowayout = WATCHDOG_NOWAYOUT; 5458bf0164SHarini Katakam 5558bf0164SHarini Katakam module_param(wdt_timeout, int, 0); 5658bf0164SHarini Katakam MODULE_PARM_DESC(wdt_timeout, 5758bf0164SHarini Katakam "Watchdog time in seconds. (default=" 5858bf0164SHarini Katakam __MODULE_STRING(CDNS_WDT_DEFAULT_TIMEOUT) ")"); 5958bf0164SHarini Katakam 6058bf0164SHarini Katakam module_param(nowayout, int, 0); 6158bf0164SHarini Katakam MODULE_PARM_DESC(nowayout, 6258bf0164SHarini Katakam "Watchdog cannot be stopped once started (default=" 6358bf0164SHarini Katakam __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 6458bf0164SHarini Katakam 6558bf0164SHarini Katakam /** 6658bf0164SHarini Katakam * struct cdns_wdt - Watchdog device structure 6758bf0164SHarini Katakam * @regs: baseaddress of device 6858bf0164SHarini Katakam * @rst: reset flag 6958bf0164SHarini Katakam * @clk: struct clk * of a clock source 7058bf0164SHarini Katakam * @prescaler: for saving prescaler value 7158bf0164SHarini Katakam * @ctrl_clksel: counter clock prescaler selection 7258bf0164SHarini Katakam * @io_lock: spinlock for IO register access 7358bf0164SHarini Katakam * @cdns_wdt_device: watchdog device structure 7458bf0164SHarini Katakam * 7558bf0164SHarini Katakam * Structure containing parameters specific to cadence watchdog. 7658bf0164SHarini Katakam */ 7758bf0164SHarini Katakam struct cdns_wdt { 7858bf0164SHarini Katakam void __iomem *regs; 7958bf0164SHarini Katakam bool rst; 8058bf0164SHarini Katakam struct clk *clk; 8158bf0164SHarini Katakam u32 prescaler; 8258bf0164SHarini Katakam u32 ctrl_clksel; 8358bf0164SHarini Katakam spinlock_t io_lock; 8458bf0164SHarini Katakam struct watchdog_device cdns_wdt_device; 8558bf0164SHarini Katakam }; 8658bf0164SHarini Katakam 8758bf0164SHarini Katakam /* Write access to Registers */ 8858bf0164SHarini Katakam static inline void cdns_wdt_writereg(struct cdns_wdt *wdt, u32 offset, u32 val) 8958bf0164SHarini Katakam { 9058bf0164SHarini Katakam writel_relaxed(val, wdt->regs + offset); 9158bf0164SHarini Katakam } 9258bf0164SHarini Katakam 9358bf0164SHarini Katakam /*************************Register Map**************************************/ 9458bf0164SHarini Katakam 9558bf0164SHarini Katakam /* Register Offsets for the WDT */ 9658bf0164SHarini Katakam #define CDNS_WDT_ZMR_OFFSET 0x0 /* Zero Mode Register */ 9758bf0164SHarini Katakam #define CDNS_WDT_CCR_OFFSET 0x4 /* Counter Control Register */ 9858bf0164SHarini Katakam #define CDNS_WDT_RESTART_OFFSET 0x8 /* Restart Register */ 9958bf0164SHarini Katakam #define CDNS_WDT_SR_OFFSET 0xC /* Status Register */ 10058bf0164SHarini Katakam 10158bf0164SHarini Katakam /* 10258bf0164SHarini Katakam * Zero Mode Register - This register controls how the time out is indicated 10358bf0164SHarini Katakam * and also contains the access code to allow writes to the register (0xABC). 10458bf0164SHarini Katakam */ 10558bf0164SHarini Katakam #define CDNS_WDT_ZMR_WDEN_MASK 0x00000001 /* Enable the WDT */ 10658bf0164SHarini Katakam #define CDNS_WDT_ZMR_RSTEN_MASK 0x00000002 /* Enable the reset output */ 10758bf0164SHarini Katakam #define CDNS_WDT_ZMR_IRQEN_MASK 0x00000004 /* Enable IRQ output */ 10858bf0164SHarini Katakam #define CDNS_WDT_ZMR_RSTLEN_16 0x00000030 /* Reset pulse of 16 pclk cycles */ 10958bf0164SHarini Katakam #define CDNS_WDT_ZMR_ZKEY_VAL 0x00ABC000 /* Access key, 0xABC << 12 */ 11058bf0164SHarini Katakam /* 11158bf0164SHarini Katakam * Counter Control register - This register controls how fast the timer runs 11258bf0164SHarini Katakam * and the reset value and also contains the access code to allow writes to 11358bf0164SHarini Katakam * the register. 11458bf0164SHarini Katakam */ 11558bf0164SHarini Katakam #define CDNS_WDT_CCR_CRV_MASK 0x00003FFC /* Counter reset value */ 11658bf0164SHarini Katakam 11758bf0164SHarini Katakam /** 11858bf0164SHarini Katakam * cdns_wdt_stop - Stop the watchdog. 11958bf0164SHarini Katakam * 12058bf0164SHarini Katakam * @wdd: watchdog device 12158bf0164SHarini Katakam * 12258bf0164SHarini Katakam * Read the contents of the ZMR register, clear the WDEN bit 12358bf0164SHarini Katakam * in the register and set the access key for successful write. 12458bf0164SHarini Katakam * 12558bf0164SHarini Katakam * Return: always 0 12658bf0164SHarini Katakam */ 12758bf0164SHarini Katakam static int cdns_wdt_stop(struct watchdog_device *wdd) 12858bf0164SHarini Katakam { 12958bf0164SHarini Katakam struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); 13058bf0164SHarini Katakam 13158bf0164SHarini Katakam spin_lock(&wdt->io_lock); 13258bf0164SHarini Katakam cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, 13358bf0164SHarini Katakam CDNS_WDT_ZMR_ZKEY_VAL & (~CDNS_WDT_ZMR_WDEN_MASK)); 13458bf0164SHarini Katakam spin_unlock(&wdt->io_lock); 13558bf0164SHarini Katakam 13658bf0164SHarini Katakam return 0; 13758bf0164SHarini Katakam } 13858bf0164SHarini Katakam 13958bf0164SHarini Katakam /** 14058bf0164SHarini Katakam * cdns_wdt_reload - Reload the watchdog timer (i.e. pat the watchdog). 14158bf0164SHarini Katakam * 14258bf0164SHarini Katakam * @wdd: watchdog device 14358bf0164SHarini Katakam * 14458bf0164SHarini Katakam * Write the restart key value (0x00001999) to the restart register. 14558bf0164SHarini Katakam * 14658bf0164SHarini Katakam * Return: always 0 14758bf0164SHarini Katakam */ 14858bf0164SHarini Katakam static int cdns_wdt_reload(struct watchdog_device *wdd) 14958bf0164SHarini Katakam { 15058bf0164SHarini Katakam struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); 15158bf0164SHarini Katakam 15258bf0164SHarini Katakam spin_lock(&wdt->io_lock); 15358bf0164SHarini Katakam cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET, 15458bf0164SHarini Katakam CDNS_WDT_RESTART_KEY); 15558bf0164SHarini Katakam spin_unlock(&wdt->io_lock); 15658bf0164SHarini Katakam 15758bf0164SHarini Katakam return 0; 15858bf0164SHarini Katakam } 15958bf0164SHarini Katakam 16058bf0164SHarini Katakam /** 16158bf0164SHarini Katakam * cdns_wdt_start - Enable and start the watchdog. 16258bf0164SHarini Katakam * 16358bf0164SHarini Katakam * @wdd: watchdog device 16458bf0164SHarini Katakam * 16558bf0164SHarini Katakam * The counter value is calculated according to the formula: 16658bf0164SHarini Katakam * calculated count = (timeout * clock) / prescaler + 1. 16758bf0164SHarini Katakam * The calculated count is divided by 0x1000 to obtain the field value 16858bf0164SHarini Katakam * to write to counter control register. 16958bf0164SHarini Katakam * Clears the contents of prescaler and counter reset value. Sets the 17058bf0164SHarini Katakam * prescaler to 4096 and the calculated count and access key 17158bf0164SHarini Katakam * to write to CCR Register. 17258bf0164SHarini Katakam * Sets the WDT (WDEN bit) and either the Reset signal(RSTEN bit) 17358bf0164SHarini Katakam * or Interrupt signal(IRQEN) with a specified cycles and the access 17458bf0164SHarini Katakam * key to write to ZMR Register. 17558bf0164SHarini Katakam * 17658bf0164SHarini Katakam * Return: always 0 17758bf0164SHarini Katakam */ 17858bf0164SHarini Katakam static int cdns_wdt_start(struct watchdog_device *wdd) 17958bf0164SHarini Katakam { 18058bf0164SHarini Katakam struct cdns_wdt *wdt = watchdog_get_drvdata(wdd); 18158bf0164SHarini Katakam unsigned int data = 0; 18258bf0164SHarini Katakam unsigned short count; 18358bf0164SHarini Katakam unsigned long clock_f = clk_get_rate(wdt->clk); 18458bf0164SHarini Katakam 18558bf0164SHarini Katakam /* 18658bf0164SHarini Katakam * Counter value divisor to obtain the value of 18758bf0164SHarini Katakam * counter reset to be written to control register. 18858bf0164SHarini Katakam */ 18958bf0164SHarini Katakam count = (wdd->timeout * (clock_f / wdt->prescaler)) / 19058bf0164SHarini Katakam CDNS_WDT_COUNTER_VALUE_DIVISOR + 1; 19158bf0164SHarini Katakam 19258bf0164SHarini Katakam if (count > CDNS_WDT_COUNTER_MAX) 19358bf0164SHarini Katakam count = CDNS_WDT_COUNTER_MAX; 19458bf0164SHarini Katakam 19558bf0164SHarini Katakam spin_lock(&wdt->io_lock); 19658bf0164SHarini Katakam cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, 19758bf0164SHarini Katakam CDNS_WDT_ZMR_ZKEY_VAL); 19858bf0164SHarini Katakam 19958bf0164SHarini Katakam count = (count << 2) & CDNS_WDT_CCR_CRV_MASK; 20058bf0164SHarini Katakam 20158bf0164SHarini Katakam /* Write counter access key first to be able write to register */ 20258bf0164SHarini Katakam data = count | CDNS_WDT_REGISTER_ACCESS_KEY | wdt->ctrl_clksel; 20358bf0164SHarini Katakam cdns_wdt_writereg(wdt, CDNS_WDT_CCR_OFFSET, data); 20458bf0164SHarini Katakam data = CDNS_WDT_ZMR_WDEN_MASK | CDNS_WDT_ZMR_RSTLEN_16 | 20558bf0164SHarini Katakam CDNS_WDT_ZMR_ZKEY_VAL; 20658bf0164SHarini Katakam 20758bf0164SHarini Katakam /* Reset on timeout if specified in device tree. */ 20858bf0164SHarini Katakam if (wdt->rst) { 20958bf0164SHarini Katakam data |= CDNS_WDT_ZMR_RSTEN_MASK; 21058bf0164SHarini Katakam data &= ~CDNS_WDT_ZMR_IRQEN_MASK; 21158bf0164SHarini Katakam } else { 21258bf0164SHarini Katakam data &= ~CDNS_WDT_ZMR_RSTEN_MASK; 21358bf0164SHarini Katakam data |= CDNS_WDT_ZMR_IRQEN_MASK; 21458bf0164SHarini Katakam } 21558bf0164SHarini Katakam cdns_wdt_writereg(wdt, CDNS_WDT_ZMR_OFFSET, data); 21658bf0164SHarini Katakam cdns_wdt_writereg(wdt, CDNS_WDT_RESTART_OFFSET, 21758bf0164SHarini Katakam CDNS_WDT_RESTART_KEY); 21858bf0164SHarini Katakam spin_unlock(&wdt->io_lock); 21958bf0164SHarini Katakam 22058bf0164SHarini Katakam return 0; 22158bf0164SHarini Katakam } 22258bf0164SHarini Katakam 22358bf0164SHarini Katakam /** 22458bf0164SHarini Katakam * cdns_wdt_settimeout - Set a new timeout value for the watchdog device. 22558bf0164SHarini Katakam * 22658bf0164SHarini Katakam * @wdd: watchdog device 22758bf0164SHarini Katakam * @new_time: new timeout value that needs to be set 22858bf0164SHarini Katakam * Return: 0 on success 22958bf0164SHarini Katakam * 23058bf0164SHarini Katakam * Update the watchdog_device timeout with new value which is used when 23158bf0164SHarini Katakam * cdns_wdt_start is called. 23258bf0164SHarini Katakam */ 23358bf0164SHarini Katakam static int cdns_wdt_settimeout(struct watchdog_device *wdd, 23458bf0164SHarini Katakam unsigned int new_time) 23558bf0164SHarini Katakam { 23658bf0164SHarini Katakam wdd->timeout = new_time; 23758bf0164SHarini Katakam 23858bf0164SHarini Katakam return cdns_wdt_start(wdd); 23958bf0164SHarini Katakam } 24058bf0164SHarini Katakam 24158bf0164SHarini Katakam /** 24258bf0164SHarini Katakam * cdns_wdt_irq_handler - Notifies of watchdog timeout. 24358bf0164SHarini Katakam * 24458bf0164SHarini Katakam * @irq: interrupt number 24558bf0164SHarini Katakam * @dev_id: pointer to a platform device structure 24658bf0164SHarini Katakam * Return: IRQ_HANDLED 24758bf0164SHarini Katakam * 24858bf0164SHarini Katakam * The handler is invoked when the watchdog times out and a 24958bf0164SHarini Katakam * reset on timeout has not been enabled. 25058bf0164SHarini Katakam */ 25158bf0164SHarini Katakam static irqreturn_t cdns_wdt_irq_handler(int irq, void *dev_id) 25258bf0164SHarini Katakam { 25358bf0164SHarini Katakam struct platform_device *pdev = dev_id; 25458bf0164SHarini Katakam 25558bf0164SHarini Katakam dev_info(&pdev->dev, 25658bf0164SHarini Katakam "Watchdog timed out. Internal reset not enabled\n"); 25758bf0164SHarini Katakam 25858bf0164SHarini Katakam return IRQ_HANDLED; 25958bf0164SHarini Katakam } 26058bf0164SHarini Katakam 26158bf0164SHarini Katakam /* 26258bf0164SHarini Katakam * Info structure used to indicate the features supported by the device 26358bf0164SHarini Katakam * to the upper layers. This is defined in watchdog.h header file. 26458bf0164SHarini Katakam */ 265*6c368932SBhumika Goyal static const struct watchdog_info cdns_wdt_info = { 26658bf0164SHarini Katakam .identity = "cdns_wdt watchdog", 26758bf0164SHarini Katakam .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | 26858bf0164SHarini Katakam WDIOF_MAGICCLOSE, 26958bf0164SHarini Katakam }; 27058bf0164SHarini Katakam 27158bf0164SHarini Katakam /* Watchdog Core Ops */ 27285f15cfcSJulia Lawall static const struct watchdog_ops cdns_wdt_ops = { 27358bf0164SHarini Katakam .owner = THIS_MODULE, 27458bf0164SHarini Katakam .start = cdns_wdt_start, 27558bf0164SHarini Katakam .stop = cdns_wdt_stop, 27658bf0164SHarini Katakam .ping = cdns_wdt_reload, 27758bf0164SHarini Katakam .set_timeout = cdns_wdt_settimeout, 27858bf0164SHarini Katakam }; 27958bf0164SHarini Katakam 28058bf0164SHarini Katakam /************************Platform Operations*****************************/ 28158bf0164SHarini Katakam /** 28258bf0164SHarini Katakam * cdns_wdt_probe - Probe call for the device. 28358bf0164SHarini Katakam * 28458bf0164SHarini Katakam * @pdev: handle to the platform device structure. 28558bf0164SHarini Katakam * Return: 0 on success, negative error otherwise. 28658bf0164SHarini Katakam * 28758bf0164SHarini Katakam * It does all the memory allocation and registration for the device. 28858bf0164SHarini Katakam */ 28958bf0164SHarini Katakam static int cdns_wdt_probe(struct platform_device *pdev) 29058bf0164SHarini Katakam { 29158bf0164SHarini Katakam struct resource *res; 29258bf0164SHarini Katakam int ret, irq; 29358bf0164SHarini Katakam unsigned long clock_f; 29458bf0164SHarini Katakam struct cdns_wdt *wdt; 29558bf0164SHarini Katakam struct watchdog_device *cdns_wdt_device; 29658bf0164SHarini Katakam 29758bf0164SHarini Katakam wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); 29858bf0164SHarini Katakam if (!wdt) 29958bf0164SHarini Katakam return -ENOMEM; 30058bf0164SHarini Katakam 30158bf0164SHarini Katakam cdns_wdt_device = &wdt->cdns_wdt_device; 30258bf0164SHarini Katakam cdns_wdt_device->info = &cdns_wdt_info; 30358bf0164SHarini Katakam cdns_wdt_device->ops = &cdns_wdt_ops; 30458bf0164SHarini Katakam cdns_wdt_device->timeout = CDNS_WDT_DEFAULT_TIMEOUT; 30558bf0164SHarini Katakam cdns_wdt_device->min_timeout = CDNS_WDT_MIN_TIMEOUT; 30658bf0164SHarini Katakam cdns_wdt_device->max_timeout = CDNS_WDT_MAX_TIMEOUT; 30758bf0164SHarini Katakam 30858bf0164SHarini Katakam res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 30958bf0164SHarini Katakam wdt->regs = devm_ioremap_resource(&pdev->dev, res); 31058bf0164SHarini Katakam if (IS_ERR(wdt->regs)) 31158bf0164SHarini Katakam return PTR_ERR(wdt->regs); 31258bf0164SHarini Katakam 31358bf0164SHarini Katakam /* Register the interrupt */ 31458bf0164SHarini Katakam wdt->rst = of_property_read_bool(pdev->dev.of_node, "reset-on-timeout"); 31558bf0164SHarini Katakam irq = platform_get_irq(pdev, 0); 31658bf0164SHarini Katakam if (!wdt->rst && irq >= 0) { 31758bf0164SHarini Katakam ret = devm_request_irq(&pdev->dev, irq, cdns_wdt_irq_handler, 0, 31858bf0164SHarini Katakam pdev->name, pdev); 31958bf0164SHarini Katakam if (ret) { 32058bf0164SHarini Katakam dev_err(&pdev->dev, 32158bf0164SHarini Katakam "cannot register interrupt handler err=%d\n", 32258bf0164SHarini Katakam ret); 32358bf0164SHarini Katakam return ret; 32458bf0164SHarini Katakam } 32558bf0164SHarini Katakam } 32658bf0164SHarini Katakam 32758bf0164SHarini Katakam /* Initialize the members of cdns_wdt structure */ 32858bf0164SHarini Katakam cdns_wdt_device->parent = &pdev->dev; 32958bf0164SHarini Katakam 33058bf0164SHarini Katakam ret = watchdog_init_timeout(cdns_wdt_device, wdt_timeout, &pdev->dev); 33158bf0164SHarini Katakam if (ret) { 33258bf0164SHarini Katakam dev_err(&pdev->dev, "unable to set timeout value\n"); 33358bf0164SHarini Katakam return ret; 33458bf0164SHarini Katakam } 33558bf0164SHarini Katakam 33658bf0164SHarini Katakam watchdog_set_nowayout(cdns_wdt_device, nowayout); 3375e1f976fSDamien Riegel watchdog_stop_on_reboot(cdns_wdt_device); 33858bf0164SHarini Katakam watchdog_set_drvdata(cdns_wdt_device, wdt); 33958bf0164SHarini Katakam 34058bf0164SHarini Katakam wdt->clk = devm_clk_get(&pdev->dev, NULL); 34158bf0164SHarini Katakam if (IS_ERR(wdt->clk)) { 34258bf0164SHarini Katakam dev_err(&pdev->dev, "input clock not found\n"); 34358bf0164SHarini Katakam ret = PTR_ERR(wdt->clk); 34458bf0164SHarini Katakam return ret; 34558bf0164SHarini Katakam } 34658bf0164SHarini Katakam 34758bf0164SHarini Katakam ret = clk_prepare_enable(wdt->clk); 34858bf0164SHarini Katakam if (ret) { 34958bf0164SHarini Katakam dev_err(&pdev->dev, "unable to enable clock\n"); 35058bf0164SHarini Katakam return ret; 35158bf0164SHarini Katakam } 35258bf0164SHarini Katakam 35358bf0164SHarini Katakam clock_f = clk_get_rate(wdt->clk); 35458bf0164SHarini Katakam if (clock_f <= CDNS_WDT_CLK_75MHZ) { 35558bf0164SHarini Katakam wdt->prescaler = CDNS_WDT_PRESCALE_512; 35658bf0164SHarini Katakam wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_512; 35758bf0164SHarini Katakam } else { 35858bf0164SHarini Katakam wdt->prescaler = CDNS_WDT_PRESCALE_4096; 35958bf0164SHarini Katakam wdt->ctrl_clksel = CDNS_WDT_PRESCALE_SELECT_4096; 36058bf0164SHarini Katakam } 36158bf0164SHarini Katakam 36258bf0164SHarini Katakam spin_lock_init(&wdt->io_lock); 36358bf0164SHarini Katakam 36458bf0164SHarini Katakam ret = watchdog_register_device(cdns_wdt_device); 36558bf0164SHarini Katakam if (ret) { 36658bf0164SHarini Katakam dev_err(&pdev->dev, "Failed to register wdt device\n"); 36758bf0164SHarini Katakam goto err_clk_disable; 36858bf0164SHarini Katakam } 36958bf0164SHarini Katakam platform_set_drvdata(pdev, wdt); 37058bf0164SHarini Katakam 37158bf0164SHarini Katakam dev_dbg(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds%s\n", 37258bf0164SHarini Katakam wdt->regs, cdns_wdt_device->timeout, 37358bf0164SHarini Katakam nowayout ? ", nowayout" : ""); 37458bf0164SHarini Katakam 37558bf0164SHarini Katakam return 0; 37658bf0164SHarini Katakam 37758bf0164SHarini Katakam err_clk_disable: 37858bf0164SHarini Katakam clk_disable_unprepare(wdt->clk); 37958bf0164SHarini Katakam 38058bf0164SHarini Katakam return ret; 38158bf0164SHarini Katakam } 38258bf0164SHarini Katakam 38358bf0164SHarini Katakam /** 38458bf0164SHarini Katakam * cdns_wdt_remove - Probe call for the device. 38558bf0164SHarini Katakam * 38658bf0164SHarini Katakam * @pdev: handle to the platform device structure. 38758bf0164SHarini Katakam * Return: 0 on success, otherwise negative error. 38858bf0164SHarini Katakam * 38958bf0164SHarini Katakam * Unregister the device after releasing the resources. 39058bf0164SHarini Katakam */ 39158bf0164SHarini Katakam static int cdns_wdt_remove(struct platform_device *pdev) 39258bf0164SHarini Katakam { 39358bf0164SHarini Katakam struct cdns_wdt *wdt = platform_get_drvdata(pdev); 39458bf0164SHarini Katakam 39558bf0164SHarini Katakam cdns_wdt_stop(&wdt->cdns_wdt_device); 39658bf0164SHarini Katakam watchdog_unregister_device(&wdt->cdns_wdt_device); 39758bf0164SHarini Katakam clk_disable_unprepare(wdt->clk); 39858bf0164SHarini Katakam 39958bf0164SHarini Katakam return 0; 40058bf0164SHarini Katakam } 40158bf0164SHarini Katakam 40258bf0164SHarini Katakam /** 40358bf0164SHarini Katakam * cdns_wdt_shutdown - Stop the device. 40458bf0164SHarini Katakam * 40558bf0164SHarini Katakam * @pdev: handle to the platform structure. 40658bf0164SHarini Katakam * 40758bf0164SHarini Katakam */ 40858bf0164SHarini Katakam static void cdns_wdt_shutdown(struct platform_device *pdev) 40958bf0164SHarini Katakam { 41058bf0164SHarini Katakam struct cdns_wdt *wdt = platform_get_drvdata(pdev); 41158bf0164SHarini Katakam 41258bf0164SHarini Katakam cdns_wdt_stop(&wdt->cdns_wdt_device); 41358bf0164SHarini Katakam clk_disable_unprepare(wdt->clk); 41458bf0164SHarini Katakam } 41558bf0164SHarini Katakam 41658bf0164SHarini Katakam /** 41758bf0164SHarini Katakam * cdns_wdt_suspend - Stop the device. 41858bf0164SHarini Katakam * 41958bf0164SHarini Katakam * @dev: handle to the device structure. 42058bf0164SHarini Katakam * Return: 0 always. 42158bf0164SHarini Katakam */ 42258bf0164SHarini Katakam static int __maybe_unused cdns_wdt_suspend(struct device *dev) 42358bf0164SHarini Katakam { 42462e21f5dSGeliang Tang struct platform_device *pdev = to_platform_device(dev); 42558bf0164SHarini Katakam struct cdns_wdt *wdt = platform_get_drvdata(pdev); 42658bf0164SHarini Katakam 427eadc4fe1SShubhrajyoti Datta if (watchdog_active(&wdt->cdns_wdt_device)) { 42858bf0164SHarini Katakam cdns_wdt_stop(&wdt->cdns_wdt_device); 42958bf0164SHarini Katakam clk_disable_unprepare(wdt->clk); 430eadc4fe1SShubhrajyoti Datta } 43158bf0164SHarini Katakam 43258bf0164SHarini Katakam return 0; 43358bf0164SHarini Katakam } 43458bf0164SHarini Katakam 43558bf0164SHarini Katakam /** 43658bf0164SHarini Katakam * cdns_wdt_resume - Resume the device. 43758bf0164SHarini Katakam * 43858bf0164SHarini Katakam * @dev: handle to the device structure. 43958bf0164SHarini Katakam * Return: 0 on success, errno otherwise. 44058bf0164SHarini Katakam */ 44158bf0164SHarini Katakam static int __maybe_unused cdns_wdt_resume(struct device *dev) 44258bf0164SHarini Katakam { 44358bf0164SHarini Katakam int ret; 44462e21f5dSGeliang Tang struct platform_device *pdev = to_platform_device(dev); 44558bf0164SHarini Katakam struct cdns_wdt *wdt = platform_get_drvdata(pdev); 44658bf0164SHarini Katakam 447eadc4fe1SShubhrajyoti Datta if (watchdog_active(&wdt->cdns_wdt_device)) { 44858bf0164SHarini Katakam ret = clk_prepare_enable(wdt->clk); 44958bf0164SHarini Katakam if (ret) { 45058bf0164SHarini Katakam dev_err(dev, "unable to enable clock\n"); 45158bf0164SHarini Katakam return ret; 45258bf0164SHarini Katakam } 45358bf0164SHarini Katakam cdns_wdt_start(&wdt->cdns_wdt_device); 454eadc4fe1SShubhrajyoti Datta } 45558bf0164SHarini Katakam 45658bf0164SHarini Katakam return 0; 45758bf0164SHarini Katakam } 45858bf0164SHarini Katakam 45958bf0164SHarini Katakam static SIMPLE_DEV_PM_OPS(cdns_wdt_pm_ops, cdns_wdt_suspend, cdns_wdt_resume); 46058bf0164SHarini Katakam 46158bf0164SHarini Katakam static struct of_device_id cdns_wdt_of_match[] = { 46258bf0164SHarini Katakam { .compatible = "cdns,wdt-r1p2", }, 46358bf0164SHarini Katakam { /* end of table */ } 46458bf0164SHarini Katakam }; 46558bf0164SHarini Katakam MODULE_DEVICE_TABLE(of, cdns_wdt_of_match); 46658bf0164SHarini Katakam 46758bf0164SHarini Katakam /* Driver Structure */ 46858bf0164SHarini Katakam static struct platform_driver cdns_wdt_driver = { 46958bf0164SHarini Katakam .probe = cdns_wdt_probe, 47058bf0164SHarini Katakam .remove = cdns_wdt_remove, 47158bf0164SHarini Katakam .shutdown = cdns_wdt_shutdown, 47258bf0164SHarini Katakam .driver = { 47358bf0164SHarini Katakam .name = "cdns-wdt", 47458bf0164SHarini Katakam .of_match_table = cdns_wdt_of_match, 47558bf0164SHarini Katakam .pm = &cdns_wdt_pm_ops, 47658bf0164SHarini Katakam }, 47758bf0164SHarini Katakam }; 47858bf0164SHarini Katakam 47958bf0164SHarini Katakam module_platform_driver(cdns_wdt_driver); 48058bf0164SHarini Katakam 48158bf0164SHarini Katakam MODULE_AUTHOR("Xilinx, Inc."); 48258bf0164SHarini Katakam MODULE_DESCRIPTION("Watchdog driver for Cadence WDT"); 48358bf0164SHarini Katakam MODULE_LICENSE("GPL"); 484