Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/watchdog/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 13 kB image not shown  

Quelle  kempld_wdt.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Kontron PLD watchdog driver
 *
 * Copyright (c) 2010-2013 Kontron Europe GmbH
 * Author: Michael Brunner <michael.brunner@kontron.com>
 *
 * Note: From the PLD watchdog point of view timeout and pretimeout are
 *       defined differently than in the kernel.
 *       First the pretimeout stage runs out before the timeout stage gets
 *       active.
 *
 * Kernel/API:                     P-----| pretimeout
 *               |-----------------------T timeout
 * Watchdog:     |-----------------P       pretimeout_stage
 *                                 |-----T timeout_stage
 */


#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h>
#include <linux/platform_device.h>
#include <linux/mfd/kempld.h>

#define KEMPLD_WDT_STAGE_TIMEOUT(x) (0x1b + (x) * 4)
#define KEMPLD_WDT_STAGE_CFG(x)  (0x18 + (x))
#define STAGE_CFG_GET_PRESCALER(x) (((x) & 0x30) >> 4)
#define STAGE_CFG_SET_PRESCALER(x) (((x) & 0x3) << 4)
#define STAGE_CFG_PRESCALER_MASK 0x30
#define STAGE_CFG_ACTION_MASK  0x7
#define STAGE_CFG_ASSERT  (1 << 3)

#define KEMPLD_WDT_MAX_STAGES  2
#define KEMPLD_WDT_KICK   0x16
#define KEMPLD_WDT_CFG   0x17
#define KEMPLD_WDT_CFG_ENABLE  0x10
#define KEMPLD_WDT_CFG_ENABLE_LOCK 0x8
#define KEMPLD_WDT_CFG_GLOBAL_LOCK 0x80

enum {
 ACTION_NONE = 0,
 ACTION_RESET,
 ACTION_NMI,
 ACTION_SMI,
 ACTION_SCI,
 ACTION_DELAY,
};

enum {
 STAGE_TIMEOUT = 0,
 STAGE_PRETIMEOUT,
};

enum {
 PRESCALER_21 = 0,
 PRESCALER_17,
 PRESCALER_12,
};

static const u32 kempld_prescaler[] = {
 [PRESCALER_21] = (1 << 21) - 1,
 [PRESCALER_17] = (1 << 17) - 1,
 [PRESCALER_12] = (1 << 12) - 1,
 0,
};

struct kempld_wdt_stage {
 unsigned int id;
 u32  mask;
};

struct kempld_wdt_data {
 struct kempld_device_data *pld;
 struct watchdog_device  wdd;
 unsigned int   pretimeout;
 struct kempld_wdt_stage  stage[KEMPLD_WDT_MAX_STAGES];
 u8    pm_status_store;
};

#define DEFAULT_TIMEOUT  30 /* seconds */
#define DEFAULT_PRETIMEOUT 0

static unsigned int timeout = DEFAULT_TIMEOUT;
module_param(timeout, uint, 0);
MODULE_PARM_DESC(timeout,
 "Watchdog timeout in seconds. (>=0, default="
 __MODULE_STRING(DEFAULT_TIMEOUT) ")");

static unsigned int pretimeout = DEFAULT_PRETIMEOUT;
module_param(pretimeout, uint, 0);
MODULE_PARM_DESC(pretimeout,
 "Watchdog pretimeout in seconds. (>=0, default="
 __MODULE_STRING(DEFAULT_PRETIMEOUT) ")");

static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout,
 "Watchdog cannot be stopped once started (default="
 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");

static int kempld_wdt_set_stage_action(struct kempld_wdt_data *wdt_data,
     struct kempld_wdt_stage *stage,
     u8 action)
{
 struct kempld_device_data *pld = wdt_data->pld;
 u8 stage_cfg;

 if (!stage || !stage->mask)
  return -EINVAL;

 kempld_get_mutex(pld);
 stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
 stage_cfg &= ~STAGE_CFG_ACTION_MASK;
 stage_cfg |= (action & STAGE_CFG_ACTION_MASK);

 if (action == ACTION_RESET)
  stage_cfg |= STAGE_CFG_ASSERT;
 else
  stage_cfg &= ~STAGE_CFG_ASSERT;

 kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg);
 kempld_release_mutex(pld);

 return 0;
}

static int kempld_wdt_set_stage_timeout(struct kempld_wdt_data *wdt_data,
     struct kempld_wdt_stage *stage,
     unsigned int timeout)
{
 struct kempld_device_data *pld = wdt_data->pld;
 u32 prescaler;
 u64 stage_timeout64;
 u32 stage_timeout;
 u32 remainder;
 u8 stage_cfg;

 prescaler = kempld_prescaler[PRESCALER_21];

 if (!stage)
  return -EINVAL;

 stage_timeout64 = (u64)timeout * pld->pld_clock;
 remainder = do_div(stage_timeout64, prescaler);
 if (remainder)
  stage_timeout64++;

 if (stage_timeout64 > stage->mask)
  return -EINVAL;

 stage_timeout = stage_timeout64 & stage->mask;

 kempld_get_mutex(pld);
 stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
 stage_cfg &= ~STAGE_CFG_PRESCALER_MASK;
 stage_cfg |= STAGE_CFG_SET_PRESCALER(PRESCALER_21);
 kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg);
 kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id),
   stage_timeout);
 kempld_release_mutex(pld);

 return 0;
}

/*
 * kempld_get_mutex must be called prior to calling this function.
 */

static unsigned int kempld_wdt_get_timeout(struct kempld_wdt_data *wdt_data,
      struct kempld_wdt_stage *stage)
{
 struct kempld_device_data *pld = wdt_data->pld;
 unsigned int timeout;
 u64 stage_timeout;
 u32 prescaler;
 u32 remainder;
 u8 stage_cfg;

 if (!stage->mask)
  return 0;

 stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
 stage_timeout = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id));
 prescaler = kempld_prescaler[STAGE_CFG_GET_PRESCALER(stage_cfg)];

 stage_timeout = (stage_timeout & stage->mask) * prescaler;
 remainder = do_div(stage_timeout, pld->pld_clock);
 if (remainder)
  stage_timeout++;

 timeout = stage_timeout;
 WARN_ON_ONCE(timeout != stage_timeout);

 return timeout;
}

static int kempld_wdt_set_timeout(struct watchdog_device *wdd,
     unsigned int timeout)
{
 struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
 struct kempld_wdt_stage *pretimeout_stage;
 struct kempld_wdt_stage *timeout_stage;
 int ret;

 timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
 pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];

 if (pretimeout_stage->mask && wdt_data->pretimeout > 0)
  timeout = wdt_data->pretimeout;

 ret = kempld_wdt_set_stage_action(wdt_data, timeout_stage,
      ACTION_RESET);
 if (ret)
  return ret;
 ret = kempld_wdt_set_stage_timeout(wdt_data, timeout_stage,
      timeout);
 if (ret)
  return ret;

 wdd->timeout = timeout;
 return 0;
}

static int kempld_wdt_set_pretimeout(struct watchdog_device *wdd,
     unsigned int pretimeout)
{
 struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
 struct kempld_wdt_stage *pretimeout_stage;
 u8 action = ACTION_NONE;
 int ret;

 pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];

 if (!pretimeout_stage->mask)
  return -ENXIO;

 if (pretimeout > wdd->timeout)
  return -EINVAL;

 if (pretimeout > 0)
  action = ACTION_NMI;

 ret = kempld_wdt_set_stage_action(wdt_data, pretimeout_stage,
      action);
 if (ret)
  return ret;
 ret = kempld_wdt_set_stage_timeout(wdt_data, pretimeout_stage,
      wdd->timeout - pretimeout);
 if (ret)
  return ret;

 wdt_data->pretimeout = pretimeout;
 return 0;
}

static void kempld_wdt_update_timeouts(struct kempld_wdt_data *wdt_data)
{
 struct kempld_device_data *pld = wdt_data->pld;
 struct kempld_wdt_stage *pretimeout_stage;
 struct kempld_wdt_stage *timeout_stage;
 unsigned int pretimeout, timeout;

 pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
 timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];

 kempld_get_mutex(pld);
 pretimeout = kempld_wdt_get_timeout(wdt_data, pretimeout_stage);
 timeout = kempld_wdt_get_timeout(wdt_data, timeout_stage);
 kempld_release_mutex(pld);

 if (pretimeout)
  wdt_data->pretimeout = timeout;
 else
  wdt_data->pretimeout = 0;

 wdt_data->wdd.timeout = pretimeout + timeout;
}

static int kempld_wdt_start(struct watchdog_device *wdd)
{
 struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
 struct kempld_device_data *pld = wdt_data->pld;
 u8 status;
 int ret;

 ret = kempld_wdt_set_timeout(wdd, wdd->timeout);
 if (ret)
  return ret;

 kempld_get_mutex(pld);
 status = kempld_read8(pld, KEMPLD_WDT_CFG);
 status |= KEMPLD_WDT_CFG_ENABLE;
 kempld_write8(pld, KEMPLD_WDT_CFG, status);
 status = kempld_read8(pld, KEMPLD_WDT_CFG);
 kempld_release_mutex(pld);

 /* Check if the watchdog was enabled */
 if (!(status & KEMPLD_WDT_CFG_ENABLE))
  return -EACCES;

 return 0;
}

static int kempld_wdt_stop(struct watchdog_device *wdd)
{
 struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
 struct kempld_device_data *pld = wdt_data->pld;
 u8 status;

 kempld_get_mutex(pld);
 status = kempld_read8(pld, KEMPLD_WDT_CFG);
 status &= ~KEMPLD_WDT_CFG_ENABLE;
 kempld_write8(pld, KEMPLD_WDT_CFG, status);
 status = kempld_read8(pld, KEMPLD_WDT_CFG);
 kempld_release_mutex(pld);

 /* Check if the watchdog was disabled */
 if (status & KEMPLD_WDT_CFG_ENABLE)
  return -EACCES;

 return 0;
}

static int kempld_wdt_keepalive(struct watchdog_device *wdd)
{
 struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
 struct kempld_device_data *pld = wdt_data->pld;

 kempld_get_mutex(pld);
 kempld_write8(pld, KEMPLD_WDT_KICK, 'K');
 kempld_release_mutex(pld);

 return 0;
}

static long kempld_wdt_ioctl(struct watchdog_device *wdd, unsigned int cmd,
    unsigned long arg)
{
 struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
 void __user *argp = (void __user *)arg;
 int ret = -ENOIOCTLCMD;
 int __user *p = argp;
 int new_value;

 switch (cmd) {
 case WDIOC_SETPRETIMEOUT:
  if (get_user(new_value, p))
   return -EFAULT;
  ret = kempld_wdt_set_pretimeout(wdd, new_value);
  if (ret)
   return ret;
  ret = kempld_wdt_keepalive(wdd);
  break;
 case WDIOC_GETPRETIMEOUT:
  ret = put_user(wdt_data->pretimeout, (int __user *)arg);
  break;
 }

 return ret;
}

static int kempld_wdt_probe_stages(struct watchdog_device *wdd)
{
 struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
 struct kempld_device_data *pld = wdt_data->pld;
 struct kempld_wdt_stage *pretimeout_stage;
 struct kempld_wdt_stage *timeout_stage;
 u8 index, data, data_orig;
 u32 mask;
 int i, j;

 pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
 timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];

 pretimeout_stage->mask = 0;
 timeout_stage->mask = 0;

 for (i = 0; i < 3; i++) {
  index = KEMPLD_WDT_STAGE_TIMEOUT(i);
  mask = 0;

  kempld_get_mutex(pld);
  /* Probe each byte individually. */
  for (j = 0; j < 4; j++) {
   data_orig = kempld_read8(pld, index + j);
   kempld_write8(pld, index + j, 0x00);
   data = kempld_read8(pld, index + j);
   /* A failed write means this byte is reserved */
   if (data != 0x00)
    break;
   kempld_write8(pld, index + j, data_orig);
   mask |= 0xff << (j * 8);
  }
  kempld_release_mutex(pld);

  /* Assign available stages to timeout and pretimeout */
  if (!timeout_stage->mask) {
   timeout_stage->mask = mask;
   timeout_stage->id = i;
  } else {
   if (pld->feature_mask & KEMPLD_FEATURE_BIT_NMI) {
    pretimeout_stage->mask = timeout_stage->mask;
    timeout_stage->mask = mask;
    pretimeout_stage->id = timeout_stage->id;
    timeout_stage->id = i;
   }
   break;
  }
 }

 if (!timeout_stage->mask)
  return -ENODEV;

 return 0;
}

static const struct watchdog_info kempld_wdt_info = {
 .identity = "KEMPLD Watchdog",
 .options = WDIOF_SETTIMEOUT |
   WDIOF_KEEPALIVEPING |
   WDIOF_MAGICCLOSE |
   WDIOF_PRETIMEOUT
};

static const struct watchdog_ops kempld_wdt_ops = {
 .owner  = THIS_MODULE,
 .start  = kempld_wdt_start,
 .stop  = kempld_wdt_stop,
 .ping  = kempld_wdt_keepalive,
 .set_timeout = kempld_wdt_set_timeout,
 .ioctl  = kempld_wdt_ioctl,
};

static int kempld_wdt_probe(struct platform_device *pdev)
{
 struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent);
 struct kempld_wdt_data *wdt_data;
 struct device *dev = &pdev->dev;
 struct watchdog_device *wdd;
 u8 status;
 int ret = 0;

 wdt_data = devm_kzalloc(dev, sizeof(*wdt_data), GFP_KERNEL);
 if (!wdt_data)
  return -ENOMEM;

 wdt_data->pld = pld;
 wdd = &wdt_data->wdd;
 wdd->parent = dev;

 kempld_get_mutex(pld);
 status = kempld_read8(pld, KEMPLD_WDT_CFG);
 kempld_release_mutex(pld);

 /* Enable nowayout if watchdog is already locked */
 if (status & (KEMPLD_WDT_CFG_ENABLE_LOCK |
   KEMPLD_WDT_CFG_GLOBAL_LOCK)) {
  if (!nowayout)
   dev_warn(dev,
     "Forcing nowayout - watchdog lock enabled!\n");
  nowayout = true;
 }

 wdd->info = &kempld_wdt_info;
 wdd->ops = &kempld_wdt_ops;

 watchdog_set_drvdata(wdd, wdt_data);
 watchdog_set_nowayout(wdd, nowayout);

 ret = kempld_wdt_probe_stages(wdd);
 if (ret)
  return ret;

 kempld_wdt_set_timeout(wdd, timeout);
 kempld_wdt_set_pretimeout(wdd, pretimeout);

 /* Check if watchdog is already enabled */
 if (status & KEMPLD_WDT_CFG_ENABLE) {
  /* Get current watchdog settings */
  kempld_wdt_update_timeouts(wdt_data);
  dev_info(dev, "Watchdog was already enabled\n");
 }

 platform_set_drvdata(pdev, wdt_data);
 watchdog_stop_on_reboot(wdd);
 watchdog_stop_on_unregister(wdd);
 ret = devm_watchdog_register_device(dev, wdd);
 if (ret)
  return ret;

 dev_info(dev, "Watchdog registered with %ds timeout\n", wdd->timeout);

 return 0;
}

/* Disable watchdog if it is active during suspend */
static int kempld_wdt_suspend(struct platform_device *pdev,
    pm_message_t message)
{
 struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
 struct kempld_device_data *pld = wdt_data->pld;
 struct watchdog_device *wdd = &wdt_data->wdd;

 kempld_get_mutex(pld);
 wdt_data->pm_status_store = kempld_read8(pld, KEMPLD_WDT_CFG);
 kempld_release_mutex(pld);

 kempld_wdt_update_timeouts(wdt_data);

 if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE)
  return kempld_wdt_stop(wdd);

 return 0;
}

/* Enable watchdog and configure it if necessary */
static int kempld_wdt_resume(struct platform_device *pdev)
{
 struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
 struct watchdog_device *wdd = &wdt_data->wdd;

 /*
 * If watchdog was stopped before suspend be sure it gets disabled
 * again, for the case BIOS has enabled it during resume
 */

 if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE)
  return kempld_wdt_start(wdd);
 else
  return kempld_wdt_stop(wdd);
}

static struct platform_driver kempld_wdt_driver = {
 .driver  = {
  .name = "kempld-wdt",
 },
 .probe  = kempld_wdt_probe,
 .suspend = pm_ptr(kempld_wdt_suspend),
 .resume  = pm_ptr(kempld_wdt_resume),
};

module_platform_driver(kempld_wdt_driver);

MODULE_DESCRIPTION("KEM PLD Watchdog Driver");
MODULE_AUTHOR("Michael Brunner ");
MODULE_LICENSE("GPL");

Messung V0.5
C=94 H=96 G=94

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.