Update of /cvsroot/leaf/devel/etitl/kernel/drivers/char
In directory sc8-pr-cvs1:/tmp/cvs-serv30754

Added Files:
        wd1100.c 
Log Message:
initial version

--- NEW FILE: wd1100.c ---
/*
 *   National Semiconductor SC1x00 CPU watchdog driver
 *   Copyright (c) Inprimis Technologies 2002
 *
 *   by Mark Grosberg <[EMAIL PROTECTED]>
 *   and Rolando Goldman <[EMAIL PROTECTED]>
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public License
 *   as published by the Free Software Foundation; either version
 *   2 of the License, or (at your option) any later version.
 *   
 */

#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/spinlock.h>
#include <linux/sysctl.h>
#include <linux/pci.h>

/* 
 * Since the SC1100 is an x86 clone, we don't even bother with 
 * allowing other architectures to compile us.
 */
#ifndef CONFIG_X86
# error Sorry this driver is only for x86.
#endif

#include <asm/system.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/processor.h>

/* #define DEBUG_WD1100 */

static int proc_wd_timeout(ctl_table   *ctl, 
                           int          write,
                           struct file *file,
                           void        *buffer,
                           size_t      *lenp);
static int proc_wd_graceful(ctl_table   *ctl, 
                            int          write,
                            struct file *file,
                            void        *buffer,
                            size_t      *lenp);
                           
/* Register definitions */

#define SC1100_F5_VENDOR_ID  0x100B
#define SC1100_F5_DEVICE_ID  0x0515   

#define CPU_WDTO_REG    0x00 /* watchdog time out, 16 bit register */
#define CPU_WDCNFG_REG  0x02 /* watchdog config , 16 bit register */
#define CPU_WDSTS_REG   0x04 /* watchdog status , 8 bit register */

/* Default timeout: 4 seconds (changeable via sysctl) */
static unsigned int    sysctl_wd_timeout  = 4;
static unsigned int    sysctl_wd_graceful = 1;

static int             in_use    = 0;
static unsigned short  cpu_base;
static spinlock_t      wd_lock;

/**************************************************************************/

/* XXX To-do: DEV_WATCHDOG must be in include/linux/sysctl.h */
enum
{ DEV_WATCHDOG = 6 };

enum
{
   DEV_WD_TIMEOUT   = 1,
   DEV_WD_GRACEFUL  = 2
};

static struct ctl_table_header *wd_table_header;

static ctl_table wd_table[] = {
   {
    DEV_WD_TIMEOUT, "timeout",
    &sysctl_wd_timeout, sizeof(int), 0644, NULL, &proc_wd_timeout
   },
   
   {
    DEV_WD_GRACEFUL, "graceful",
    &sysctl_wd_graceful, sizeof(int), 0644, NULL, &proc_wd_graceful
   },

   {0}
};

static ctl_table wd_dir_table[] = {
   {DEV_WATCHDOG, "wd", NULL, 0, 0555, wd_table},
   {0}
};

static ctl_table wd_root_table[] = {
   {CTL_DEV, "dev", NULL, 0, 0555, wd_dir_table},
   {0}
};

static int proc_wd_timeout(ctl_table   *ctl, 
                           int          write,
                           struct file *file,
                           void        *buffer,
                           size_t      *lenp)
{
  int   rc;
  
  rc = proc_dointvec(ctl, write, file, buffer, lenp);
  if (write && (rc == 0))
  {
     /* Clamp to limits. */
     if (sysctl_wd_timeout < 1) 
       sysctl_wd_timeout = 1;
     else if (sysctl_wd_timeout > 65535)
       sysctl_wd_timeout = 65535;
  }
  
  return (rc);
}

static int proc_wd_graceful(ctl_table   *ctl, 
                            int          write,
                            struct file *file,
                            void        *buffer,
                            size_t      *lenp)
{
  int   rc;
  
  rc = proc_dointvec(ctl, write, file, buffer, lenp);
  if (write && (rc == 0))
  {
     /* Clamp to true/false. */
     if (sysctl_wd_graceful) 
       sysctl_wd_graceful = 1;
  }
  
  return (rc);
}                           

/**************************************************************************/

static __inline__ void reset_wd(void)
{
  outw(sysctl_wd_timeout * 8, cpu_base + CPU_WDTO_REG);
}

static int reboot_reason(void)
{
   static int    result;
   static int    fetched = 0;
 
   if (!fetched)
   {
     unsigned char sr;
     
     sr = inb(cpu_base + CPU_WDSTS_REG);
     outb(sr | 1, cpu_base + CPU_WDSTS_REG);
   
     fetched = 1;
   }
   
   return (result);
}

static struct watchdog_info wd_info =
{
   0,   /* Options          */
   0,   /* Firmware version */
   "NSC SC1x00 WD"
};

static int wd_ioctl(struct inode *inode, 
                    struct file  *file, 
                    unsigned int  cmd,
                    unsigned long arg)
{
   int  i;
   
   switch (cmd)
   {
      default:
        return (-ENOTTY);
        
      case WDIOC_GETSUPPORT:
        i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct watchdog_info));
        if (i)
          return (i);
        else
          return copy_to_user((struct watchdog_info *)arg, 
                              &wd_info, 
                              sizeof(wd_info));
        break;
        
      case WDIOC_KEEPALIVE:
        reset_wd();
        return (0);
        
      case WDIOC_GETBOOTSTATUS:
        i = reboot_reason();
        return (put_user(i, (int *)arg));
        
      case WDIOC_GETSTATUS:
        i = inw(cpu_base + CPU_WDTO_REG) / 8;
        return (put_user(i, (int *)arg));
   }       
}                    

static int wd_open(struct inode *inode, 
                   struct file  *file)
{
  spin_lock(&wd_lock);
  if (in_use)
  {
    spin_unlock(&wd_lock);
    return (-EBUSY);
  }
  else
    in_use++;
    
  spin_unlock(&wd_lock);

  MOD_INC_USE_COUNT;

  /*
   * Configure the chip to do a reset if the timer goes to 0.
   * Set the clock divisor to 4096.
   */
     
  outw(0xfc, cpu_base + CPU_WDCNFG_REG);
  
  /* Start the watchdog: It won't run until we write the TO reg. */
  reset_wd();

  return (0);
}

static int wd_release(struct inode *inode,
                       struct file  *file)
{
  spin_lock(&wd_lock);
    
  in_use = 0;
  
  /*
   * If graceful shutdown is not set, then don't bother to stop the
   * watchdog timer. This handles the scenario where the user process
   * that is poking the watchdog gets terminated due to some error
   * (say a SEGV or some VM condition). 
   *
   * In that case, the kernel would happily close the descriptor for
   * us and leave us in a state where we aren't watching the dog...
   *
   * To work around this, the "graceful" sysctl prevents reset of the
   * watchdog on close.
   */
  if (sysctl_wd_graceful)
    outw(0, cpu_base + CPU_WDCNFG_REG);
  
  spin_unlock(&wd_lock);
  MOD_DEC_USE_COUNT;
  
  return (0);
}           

static ssize_t wd_write(struct file *file,
                        const char  *data,
                        size_t       len,
                        loff_t      *ppos)
{
  /* Device is non-seekable. */
  if (ppos != &file->f_pos)
    return (-ESPIPE);
  
  if (len > 0)
    reset_wd();   
  
  return (len);
}

static struct file_operations wd_fops=
{
   owner:      THIS_MODULE,
   write:      wd_write,
   ioctl:      wd_ioctl,
   open:       wd_open,
   release:    wd_release,
};
 
static struct miscdevice sc1x00wd_miscdev=
{
   WATCHDOG_MINOR,
   "watchdog",
   &wd_fops
};

static int __init wd_init(void)
{
   int              ret;
   struct pci_dev  *dev;
   unsigned int     cw;

   if ((strcmp(boot_cpu_data.x86_vendor_id, "Geode by NSC") != 0)
   ||  (boot_cpu_data.x86_model  != 4))
   {
      printk(KERN_WARNING "wd1100.c: This is not an SC1100 processor!\n");
      return (0);
   }

   /* get the CONFIG BLOCK ADDRESS from scratch pad register */ 
   dev = pci_find_device(SC1100_F5_VENDOR_ID,SC1100_F5_DEVICE_ID,0);
   if (dev == NULL)
   {
     printk(KERN_ERR "wd1100.c: Can not find bridge device.\n");
     return (0);
   }

   pci_read_config_dword(dev, 0x64, &cw);
   cpu_base = (unsigned short )cw;
  
#ifdef DEBUG_WD1100
   printk("wd1100.c: CPU base = 0x%X\n", (unsigned int )cpu_base);
#endif

   printk(KERN_INFO "SC1x00 Watchdog driver by Inprimis Technolgies.\n");
   /*
    * We must call reboot_reason() to reset the flag in the WD.
    *
    * Even though it is available as an ioctl(), we call it during
    * module initialization to perform the clear. You can take out
    * the printk(), but don't take out the call to reboot_reason().
    */
   if (reboot_reason())
      printk(KERN_INFO "Last reboot was by watchdog!\n");
    
   spin_lock_init(&wd_lock);
        
   ret = misc_register(&sc1x00wd_miscdev);
   if (ret)
     printk(KERN_ERR "wd1100.c: Can't register device.\n");
   else
   {
     wd_table_header = register_sysctl_table(wd_root_table, 1); 
      if (wd_table_header == NULL)
        printk(KERN_ERR "wd1100.c: Can't register sysctl.\n");
   }

   return 0;
}   

static void __exit wd_exit(void)
{
   if (wd_table_header != NULL)
      unregister_sysctl_table(wd_table_header);
   
   misc_deregister(&sc1x00wd_miscdev);
}

EXPORT_NO_SYMBOLS; 

module_init(wd_init);
module_exit(wd_exit);

MODULE_LICENSE("GPL");





-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf
_______________________________________________
Leaf-cvs-commits mailing list
[EMAIL PROTECTED]
https://lists.sourceforge.net/lists/listinfo/leaf-cvs-commits

Reply via email to