linux/drivers/watchdog/ixp4xx_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * drivers/char/watchdog/ixp4xx_wdt.c
   4 *
   5 * Watchdog driver for Intel IXP4xx network processors
   6 *
   7 * Author: Deepak Saxena <dsaxena@plexity.net>
   8 * Author: Linus Walleij <linus.walleij@linaro.org>
   9 *
  10 * Copyright 2004 (c) MontaVista, Software, Inc.
  11 * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
  12 */
  13
  14#include <linux/module.h>
  15#include <linux/types.h>
  16#include <linux/kernel.h>
  17#include <linux/watchdog.h>
  18#include <linux/bits.h>
  19#include <linux/platform_device.h>
  20#include <linux/clk.h>
  21#include <linux/soc/ixp4xx/cpu.h>
  22
  23struct ixp4xx_wdt {
  24        struct watchdog_device wdd;
  25        void __iomem *base;
  26        unsigned long rate;
  27};
  28
  29/* Fallback if we do not have a clock for this */
  30#define IXP4XX_TIMER_FREQ       66666000
  31
  32/* Registers after the timer registers */
  33#define IXP4XX_OSWT_OFFSET      0x14  /* Watchdog Timer */
  34#define IXP4XX_OSWE_OFFSET      0x18  /* Watchdog Enable */
  35#define IXP4XX_OSWK_OFFSET      0x1C  /* Watchdog Key */
  36#define IXP4XX_OSST_OFFSET      0x20  /* Timer Status */
  37
  38#define IXP4XX_OSST_TIMER_WDOG_PEND     0x00000008
  39#define IXP4XX_OSST_TIMER_WARM_RESET    0x00000010
  40#define IXP4XX_WDT_KEY                  0x0000482E
  41#define IXP4XX_WDT_RESET_ENABLE         0x00000001
  42#define IXP4XX_WDT_IRQ_ENABLE           0x00000002
  43#define IXP4XX_WDT_COUNT_ENABLE         0x00000004
  44
  45static inline
  46struct ixp4xx_wdt *to_ixp4xx_wdt(struct watchdog_device *wdd)
  47{
  48        return container_of(wdd, struct ixp4xx_wdt, wdd);
  49}
  50
  51static int ixp4xx_wdt_start(struct watchdog_device *wdd)
  52{
  53        struct ixp4xx_wdt *iwdt = to_ixp4xx_wdt(wdd);
  54
  55        __raw_writel(IXP4XX_WDT_KEY, iwdt->base + IXP4XX_OSWK_OFFSET);
  56        __raw_writel(0, iwdt->base + IXP4XX_OSWE_OFFSET);
  57        __raw_writel(wdd->timeout * iwdt->rate,
  58                     iwdt->base + IXP4XX_OSWT_OFFSET);
  59        __raw_writel(IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE,
  60                     iwdt->base + IXP4XX_OSWE_OFFSET);
  61        __raw_writel(0, iwdt->base + IXP4XX_OSWK_OFFSET);
  62
  63        return 0;
  64}
  65
  66static int ixp4xx_wdt_stop(struct watchdog_device *wdd)
  67{
  68        struct ixp4xx_wdt *iwdt = to_ixp4xx_wdt(wdd);
  69
  70        __raw_writel(IXP4XX_WDT_KEY, iwdt->base + IXP4XX_OSWK_OFFSET);
  71        __raw_writel(0, iwdt->base + IXP4XX_OSWE_OFFSET);
  72        __raw_writel(0, iwdt->base + IXP4XX_OSWK_OFFSET);
  73
  74        return 0;
  75}
  76
  77static int ixp4xx_wdt_set_timeout(struct watchdog_device *wdd,
  78                                  unsigned int timeout)
  79{
  80        wdd->timeout = timeout;
  81        if (watchdog_active(wdd))
  82                ixp4xx_wdt_start(wdd);
  83
  84        return 0;
  85}
  86
  87static const struct watchdog_ops ixp4xx_wdt_ops = {
  88        .start = ixp4xx_wdt_start,
  89        .stop = ixp4xx_wdt_stop,
  90        .set_timeout = ixp4xx_wdt_set_timeout,
  91        .owner = THIS_MODULE,
  92};
  93
  94static const struct watchdog_info ixp4xx_wdt_info = {
  95        .options = WDIOF_KEEPALIVEPING
  96                | WDIOF_MAGICCLOSE
  97                | WDIOF_SETTIMEOUT,
  98        .identity = KBUILD_MODNAME,
  99};
 100
 101/* Devres-handled clock disablement */
 102static void ixp4xx_clock_action(void *d)
 103{
 104        clk_disable_unprepare(d);
 105}
 106
 107static int ixp4xx_wdt_probe(struct platform_device *pdev)
 108{
 109        struct device *dev = &pdev->dev;
 110        struct ixp4xx_wdt *iwdt;
 111        struct clk *clk;
 112        int ret;
 113
 114        if (!(read_cpuid_id() & 0xf) && !cpu_is_ixp46x()) {
 115                dev_err(dev, "Rev. A0 IXP42x CPU detected - watchdog disabled\n");
 116                return -ENODEV;
 117        }
 118
 119        iwdt = devm_kzalloc(dev, sizeof(*iwdt), GFP_KERNEL);
 120        if (!iwdt)
 121                return -ENOMEM;
 122        iwdt->base = (void __iomem *)dev->platform_data;
 123
 124        /*
 125         * Retrieve rate from a fixed clock from the device tree if
 126         * the parent has that, else use the default clock rate.
 127         */
 128        clk = devm_clk_get(dev->parent, NULL);
 129        if (!IS_ERR(clk)) {
 130                ret = clk_prepare_enable(clk);
 131                if (ret)
 132                        return ret;
 133                ret = devm_add_action_or_reset(dev, ixp4xx_clock_action, clk);
 134                if (ret)
 135                        return ret;
 136                iwdt->rate = clk_get_rate(clk);
 137        }
 138        if (!iwdt->rate)
 139                iwdt->rate = IXP4XX_TIMER_FREQ;
 140
 141        iwdt->wdd.info = &ixp4xx_wdt_info;
 142        iwdt->wdd.ops = &ixp4xx_wdt_ops;
 143        iwdt->wdd.min_timeout = 1;
 144        iwdt->wdd.max_timeout = U32_MAX / iwdt->rate;
 145        iwdt->wdd.parent = dev;
 146        /* Default to 60 seconds */
 147        iwdt->wdd.timeout = 60U;
 148        watchdog_init_timeout(&iwdt->wdd, 0, dev);
 149
 150        if (__raw_readl(iwdt->base + IXP4XX_OSST_OFFSET) &
 151            IXP4XX_OSST_TIMER_WARM_RESET)
 152                iwdt->wdd.bootstatus = WDIOF_CARDRESET;
 153
 154        ret = devm_watchdog_register_device(dev, &iwdt->wdd);
 155        if (ret)
 156                return ret;
 157
 158        dev_info(dev, "IXP4xx watchdog available\n");
 159
 160        return 0;
 161}
 162
 163static struct platform_driver ixp4xx_wdt_driver = {
 164        .probe = ixp4xx_wdt_probe,
 165        .driver = {
 166                .name   = "ixp4xx-watchdog",
 167        },
 168};
 169module_platform_driver(ixp4xx_wdt_driver);
 170
 171MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
 172MODULE_DESCRIPTION("IXP4xx Network Processor Watchdog");
 173MODULE_LICENSE("GPL");
 174