linux/arch/arm/mach-omap2/wd_timer.c
<<
>>
Prefs
   1/*
   2 * OMAP2+ MPU WD_TIMER-specific code
   3 *
   4 * Copyright (C) 2012 Texas Instruments, Inc.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 */
  11
  12#include <linux/kernel.h>
  13#include <linux/io.h>
  14#include <linux/err.h>
  15
  16#include <linux/platform_data/omap-wd-timer.h>
  17
  18#include "omap_hwmod.h"
  19#include "omap_device.h"
  20#include "wd_timer.h"
  21#include "common.h"
  22#include "prm.h"
  23#include "soc.h"
  24
  25/*
  26 * In order to avoid any assumptions from bootloader regarding WDT
  27 * settings, WDT module is reset during init. This enables the watchdog
  28 * timer. Hence it is required to disable the watchdog after the WDT reset
  29 * during init. Otherwise the system would reboot as per the default
  30 * watchdog timer registers settings.
  31 */
  32#define OMAP_WDT_WPS            0x34
  33#define OMAP_WDT_SPR            0x48
  34
  35int omap2_wd_timer_disable(struct omap_hwmod *oh)
  36{
  37        void __iomem *base;
  38
  39        if (!oh) {
  40                pr_err("%s: Could not look up wdtimer_hwmod\n", __func__);
  41                return -EINVAL;
  42        }
  43
  44        base = omap_hwmod_get_mpu_rt_va(oh);
  45        if (!base) {
  46                pr_err("%s: Could not get the base address for %s\n",
  47                                oh->name, __func__);
  48                return -EINVAL;
  49        }
  50
  51        /* sequence required to disable watchdog */
  52        writel_relaxed(0xAAAA, base + OMAP_WDT_SPR);
  53        while (readl_relaxed(base + OMAP_WDT_WPS) & 0x10)
  54                cpu_relax();
  55
  56        writel_relaxed(0x5555, base + OMAP_WDT_SPR);
  57        while (readl_relaxed(base + OMAP_WDT_WPS) & 0x10)
  58                cpu_relax();
  59
  60        return 0;
  61}
  62
  63/**
  64 * omap2_wdtimer_reset - reset and disable the WDTIMER IP block
  65 * @oh: struct omap_hwmod *
  66 *
  67 * After the WDTIMER IP blocks are reset on OMAP2/3, we must also take
  68 * care to execute the special watchdog disable sequence.  This is
  69 * because the watchdog is re-armed upon OCP softreset.  (On OMAP4,
  70 * this behavior was apparently changed and the watchdog is no longer
  71 * re-armed after an OCP soft-reset.)  Returns -ETIMEDOUT if the reset
  72 * did not complete, or 0 upon success.
  73 *
  74 * XXX Most of this code should be moved to the omap_hwmod.c layer
  75 * during a normal merge window.  omap_hwmod_softreset() should be
  76 * renamed to omap_hwmod_set_ocp_softreset(), and omap_hwmod_softreset()
  77 * should call the hwmod _ocp_softreset() code.
  78 */
  79int omap2_wd_timer_reset(struct omap_hwmod *oh)
  80{
  81        int c = 0;
  82
  83        /* Write to the SOFTRESET bit */
  84        omap_hwmod_softreset(oh);
  85
  86        /* Poll on RESETDONE bit */
  87        omap_test_timeout((omap_hwmod_read(oh,
  88                                           oh->class->sysc->syss_offs)
  89                           & SYSS_RESETDONE_MASK),
  90                          MAX_MODULE_SOFTRESET_WAIT, c);
  91
  92        if (oh->class->sysc->srst_udelay)
  93                udelay(oh->class->sysc->srst_udelay);
  94
  95        if (c == MAX_MODULE_SOFTRESET_WAIT)
  96                pr_warn("%s: %s: softreset failed (waited %d usec)\n",
  97                        __func__, oh->name, MAX_MODULE_SOFTRESET_WAIT);
  98        else
  99                pr_debug("%s: %s: softreset in %d usec\n", __func__,
 100                         oh->name, c);
 101
 102        return (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT :
 103                omap2_wd_timer_disable(oh);
 104}
 105
 106static int __init omap_init_wdt(void)
 107{
 108        int id = -1;
 109        struct platform_device *pdev;
 110        struct omap_hwmod *oh;
 111        char *oh_name = "wd_timer2";
 112        char *dev_name = "omap_wdt";
 113        struct omap_wd_timer_platform_data pdata;
 114
 115        if (!cpu_class_is_omap2() || of_have_populated_dt())
 116                return 0;
 117
 118        oh = omap_hwmod_lookup(oh_name);
 119        if (!oh) {
 120                pr_err("Could not look up wd_timer%d hwmod\n", id);
 121                return -EINVAL;
 122        }
 123
 124        pdata.read_reset_sources = prm_read_reset_sources;
 125
 126        pdev = omap_device_build(dev_name, id, oh, &pdata,
 127                                 sizeof(struct omap_wd_timer_platform_data));
 128        WARN(IS_ERR(pdev), "Can't build omap_device for %s:%s.\n",
 129             dev_name, oh->name);
 130        return 0;
 131}
 132omap_subsys_initcall(omap_init_wdt);
 133