linux/drivers/watchdog/digicolor_wdt.c
<<
>>
Prefs
   1/*
   2 * Watchdog driver for Conexant Digicolor
   3 *
   4 * Copyright (C) 2015 Paradox Innovation Ltd.
   5 *
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms of the GNU General Public License as published by the
   8 * Free Software Foundation; either version 2 of the License, or (at your
   9 * option) any later version.
  10 */
  11
  12#include <linux/types.h>
  13#include <linux/module.h>
  14#include <linux/io.h>
  15#include <linux/delay.h>
  16#include <linux/clk.h>
  17#include <linux/watchdog.h>
  18#include <linux/platform_device.h>
  19#include <linux/of_address.h>
  20
  21#define TIMER_A_CONTROL         0
  22#define TIMER_A_COUNT           4
  23
  24#define TIMER_A_ENABLE_COUNT    BIT(0)
  25#define TIMER_A_ENABLE_WATCHDOG BIT(1)
  26
  27struct dc_wdt {
  28        void __iomem            *base;
  29        struct clk              *clk;
  30        spinlock_t              lock;
  31};
  32
  33static unsigned timeout;
  34module_param(timeout, uint, 0);
  35MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
  36
  37static void dc_wdt_set(struct dc_wdt *wdt, u32 ticks)
  38{
  39        unsigned long flags;
  40
  41        spin_lock_irqsave(&wdt->lock, flags);
  42
  43        writel_relaxed(0, wdt->base + TIMER_A_CONTROL);
  44        writel_relaxed(ticks, wdt->base + TIMER_A_COUNT);
  45        writel_relaxed(TIMER_A_ENABLE_COUNT | TIMER_A_ENABLE_WATCHDOG,
  46                       wdt->base + TIMER_A_CONTROL);
  47
  48        spin_unlock_irqrestore(&wdt->lock, flags);
  49}
  50
  51static int dc_wdt_restart(struct watchdog_device *wdog, unsigned long action,
  52                          void *data)
  53{
  54        struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
  55
  56        dc_wdt_set(wdt, 1);
  57        /* wait for reset to assert... */
  58        mdelay(500);
  59
  60        return 0;
  61}
  62
  63static int dc_wdt_start(struct watchdog_device *wdog)
  64{
  65        struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
  66
  67        dc_wdt_set(wdt, wdog->timeout * clk_get_rate(wdt->clk));
  68
  69        return 0;
  70}
  71
  72static int dc_wdt_stop(struct watchdog_device *wdog)
  73{
  74        struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
  75
  76        writel_relaxed(0, wdt->base + TIMER_A_CONTROL);
  77
  78        return 0;
  79}
  80
  81static int dc_wdt_set_timeout(struct watchdog_device *wdog, unsigned int t)
  82{
  83        struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
  84
  85        dc_wdt_set(wdt, t * clk_get_rate(wdt->clk));
  86        wdog->timeout = t;
  87
  88        return 0;
  89}
  90
  91static unsigned int dc_wdt_get_timeleft(struct watchdog_device *wdog)
  92{
  93        struct dc_wdt *wdt = watchdog_get_drvdata(wdog);
  94        uint32_t count = readl_relaxed(wdt->base + TIMER_A_COUNT);
  95
  96        return count / clk_get_rate(wdt->clk);
  97}
  98
  99static struct watchdog_ops dc_wdt_ops = {
 100        .owner          = THIS_MODULE,
 101        .start          = dc_wdt_start,
 102        .stop           = dc_wdt_stop,
 103        .set_timeout    = dc_wdt_set_timeout,
 104        .get_timeleft   = dc_wdt_get_timeleft,
 105        .restart        = dc_wdt_restart,
 106};
 107
 108static struct watchdog_info dc_wdt_info = {
 109        .options        = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE
 110                        | WDIOF_KEEPALIVEPING,
 111        .identity       = "Conexant Digicolor Watchdog",
 112};
 113
 114static struct watchdog_device dc_wdt_wdd = {
 115        .info           = &dc_wdt_info,
 116        .ops            = &dc_wdt_ops,
 117        .min_timeout    = 1,
 118};
 119
 120static int dc_wdt_probe(struct platform_device *pdev)
 121{
 122        struct device *dev = &pdev->dev;
 123        struct device_node *np = dev->of_node;
 124        struct dc_wdt *wdt;
 125        int ret;
 126
 127        wdt = devm_kzalloc(dev, sizeof(struct dc_wdt), GFP_KERNEL);
 128        if (!wdt)
 129                return -ENOMEM;
 130        platform_set_drvdata(pdev, wdt);
 131
 132        wdt->base = of_iomap(np, 0);
 133        if (!wdt->base) {
 134                dev_err(dev, "Failed to remap watchdog regs");
 135                return -ENODEV;
 136        }
 137
 138        wdt->clk = devm_clk_get(&pdev->dev, NULL);
 139        if (IS_ERR(wdt->clk)) {
 140                ret = PTR_ERR(wdt->clk);
 141                goto err_iounmap;
 142        }
 143        dc_wdt_wdd.max_timeout = U32_MAX / clk_get_rate(wdt->clk);
 144        dc_wdt_wdd.timeout = dc_wdt_wdd.max_timeout;
 145        dc_wdt_wdd.parent = &pdev->dev;
 146
 147        spin_lock_init(&wdt->lock);
 148
 149        watchdog_set_drvdata(&dc_wdt_wdd, wdt);
 150        watchdog_set_restart_priority(&dc_wdt_wdd, 128);
 151        watchdog_init_timeout(&dc_wdt_wdd, timeout, dev);
 152        ret = watchdog_register_device(&dc_wdt_wdd);
 153        if (ret) {
 154                dev_err(dev, "Failed to register watchdog device");
 155                goto err_iounmap;
 156        }
 157
 158        return 0;
 159
 160err_iounmap:
 161        iounmap(wdt->base);
 162        return ret;
 163}
 164
 165static int dc_wdt_remove(struct platform_device *pdev)
 166{
 167        struct dc_wdt *wdt = platform_get_drvdata(pdev);
 168
 169        watchdog_unregister_device(&dc_wdt_wdd);
 170        iounmap(wdt->base);
 171
 172        return 0;
 173}
 174
 175static void dc_wdt_shutdown(struct platform_device *pdev)
 176{
 177        dc_wdt_stop(&dc_wdt_wdd);
 178}
 179
 180static const struct of_device_id dc_wdt_of_match[] = {
 181        { .compatible = "cnxt,cx92755-wdt", },
 182        {},
 183};
 184MODULE_DEVICE_TABLE(of, dc_wdt_of_match);
 185
 186static struct platform_driver dc_wdt_driver = {
 187        .probe          = dc_wdt_probe,
 188        .remove         = dc_wdt_remove,
 189        .shutdown       = dc_wdt_shutdown,
 190        .driver = {
 191                .name =         "digicolor-wdt",
 192                .of_match_table = dc_wdt_of_match,
 193        },
 194};
 195module_platform_driver(dc_wdt_driver);
 196
 197MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>");
 198MODULE_DESCRIPTION("Driver for Conexant Digicolor watchdog timer");
 199MODULE_LICENSE("GPL");
 200