2410 watchdog driver

Source: Internet
Author: User

2410 watchdog driver Author: yd2763132 submission date: 17:10:00 |
Category: Linux | access volume: 718

# Include <Linux/module. h>
# Include <Linux/moduleparam. h>
# Include <Linux/types. h>
# Include <Linux/Timer. h>
# Include <Linux/miscdevice. h>
# Include <Linux/watchdog. h>
# Include <Linux/fs. h>
# Include <Linux/init. h>
# Include <Linux/platform_device.h>
# Include <Linux/interrupt. h>
# Include <Linux/CLK. h>
 
# Include <ASM/uaccess. h>
# Include <ASM/IO. h>
  
# Include <ASM/map. h/arch-s3c2410>
  
# UNDEF s3c24xx_va_watchdog // the previous definition has been canceled because the static ing has been canceled before dynamic ing is used again
# Define s3c24xx_va_watchdog (0)
  
# Include <ASM/arch-s3c2410/regs-watchdog.h>
  
# Define pfx "s3c2410-wdt :"
  
# Define config_s3c2410_watchdog_atboot (0)
# Define config_s3c2410_watchdog_default_time (15)
  
Static int nowayout = watchdog_nowayout;
// Config_watchdog_nowayout 'is set to 'y'. By default, the watchdog cannot be stopped after it is started.
Static int tmr_margin = config_s3c2410_watchdog_default_time;
Static int tmr_atboot = config_s3c2410_watchdog_atboot;
// To enable power-on, use the default value of 1 as the watchdog.
Static int soft_noboot = 0; // when this parameter is set to 1, the watchdog is a general timer.
Static int DEBUG = 0; // debug mode
  
Module_param (tmr_margin, Int, 0); // 0 indicates that the parameter does not have the read/write permission. Therefore, in the SYS/module/paraments directory
Module_param (tmr_atboot, Int, 0 );
Module_param (nowayout, Int, 0 );
Module_param (soft_noboot, Int, 0 );
Module_param (debug, Int, 0 );
  
Module_parm_desc (tmr_margin, "watchdog tmr_margin in seconds. Default =" _ module_string (config_s3c2410_watchdog_default_time) "); // parameter description
  
Module_parm_desc (tmr_atboot, "watchdog is started at boot time if set to 1, default =" _ module_string (config_s3c2410_watchdog_atboot ));
  
Module_parm_desc (nowayout, "watchdog cannot be stopped once started (default = config_watchdog_nowayout )");
  
Module_parm_desc (soft_noboot, "watchdog action, set to 1 to ignore reboots, 0 to reboot (default depends on only_testing )");
  
Module_parm_desc (debug, "watchdog debug, set to> 1 for debug, (default 0 )");
  
  
Typedef Enum close_state {
Close_state_not,
Close_state_allow = 0x4021
} Close_state_t;
  
Static declare_mutex (open_lock );
  
Static struct resource * wdt_mem;
Static struct resource * wdt_irq;
Static struct CLK * wdt_clock;
Static void _ iomem * wdt_base;
Static unsigned int wdt_count;
Static close_state_t allow_close;
  
/* Watchdog control routines */
  
# Define dbg (MSG...) do {\
If (Debug )\
Printk (kern_info MSG );\
} While (0)
  
/* Functions */
  
Static int s3c2410wdt_keepalive (void) // feed the dog
{
Writel (wdt_count, wdt_base + s3c2410_wtcnt );
Return 0;
}
  
Static int s3c2410wdt_stop (void)
{
Unsigned long wtcon;
  
Wtcon = readl (wdt_base + s3c2410_wtcon );
Wtcon & = ~ (S3c2410_wtcon_enable | s3c2410_wtcon_rsten );
Writel (wtcon, wdt_base + s3c2410_wtcon );
  
Return 0;
}
  
Static int s3c2410wdt_start (void)
{
Unsigned long wtcon;
  
S3c2410wdt_stop ();
  
Wtcon = readl (wdt_base + s3c2410_wtcon );
Wtcon | = s3c2410_wtcon_enable | s3c2410_wtcon_div128;
  
If (soft_noboot ){
Wtcon | = s3c2410_wtcon_inten; // when this parameter is set to 1, the watchdog indicates that the timer is interrupted.
Wtcon & = ~ S3c2410_wtcon_rsten; // the restart is invalid.
} Else {
Wtcon & = ~ S3c2410_wtcon_inten; // The default value is reset.
Wtcon | = s3c2410_wtcon_rsten;
}
  
Dbg ("% s: wdt_count = 0x % 08x, wtcon = % 08lx \ n ",
_ FUNCTION __, wdt_count, wtcon );
  
Writel (wdt_count, wdt_base + s3c2410_wtdat );
Writel (wdt_count, wdt_base + s3c2410_wtcnt );
Writel (wtcon, wdt_base + s3c2410_wtcon );
  
Return 0;
}
  
Static int s3c2410wdt_set_heartbeat (INT timeout) // set prescaler value to 0 and wtdat to zero
{
Unsigned int freq = clk_get_rate (wdt_clock );
Unsigned int count;
Unsigned int divisor = 1;
Unsigned long wtcon;
  
If (timeout <1)
Return-einval;
  
Freq // = 128; // 0
Count = timeout * freq; // 0
  
Dbg ("% s: Count = % d, timeout = % d, freq = % d \ n ",
_ FUNCTION __, count, timeout, freq );
  
/* If the count is bigger than the watchdog register,
Then work out what we need to do (and if) we can
Actually make this value
*/
  
If (count> = 0x10000) {// The Count value exceeds 65536.
For (divisor = 1; divisor <= 0x100; divisor ++ ){
If (count/divisor) <0x10000)
Break;
}
  
If (count/divisor)> = 0x10000 ){
Printk (kern_err pfx "timeout % d too big \ n", timeout );
Return-einval;
}
}
  
Tmr_margin = timeout;
  
Dbg ("% s: timeout = % d, divisor = % d, Count = % d (% 08x) \ n ",
_ FUNCTION __, timeout, divisor, Count, count/divisor );
  
Count/= divisor; // 0
Wdt_count = count; // 0
  
/* Update the pre-scaler */
Wtcon = readl (wdt_base + s3c2410_wtcon );
Wtcon & = ~ S3c2410_wtcon_prescale_mask;
Wtcon | = s3c2410_wtcon_prescale (divisor-1); // prescaler value = 0
  
Writel (count, wdt_base + s3c2410_wtdat); // clear wtdat
Writel (wtcon, wdt_base + s3c2410_wtcon); // prescaler value = 0
  
Return 0;
}
  
/*
*/Dev/watchdog handling
*/
  
Static int s3c2410wdt_open (struct inode * inode, struct file * file)
{
If (down_trylock (& open_lock) // non-blocking
Return-ebusy;
  
If (nowayout) {// Kernel configuration config_watchdog_nowayout
_ Module_get (this_module); // Add 1 to the number of modules used
} Else {
Allow_close = close_state_allow; // config_watchdog_nowayout is not configured, And the watchdog can be disabled in close.
}
  
/* Start the timer */
S3c2410wdt_start ();
Return nonseekable_open (inode, file); // identifies a given filp as unmovable. lseek cannot be called.
}
  
Static int s3c2410wdt_release (struct inode * inode, struct file * file)
{
/*
* Shut off the timer.
* Lock it in if it's a module and we set nowayout
*/
If (allow_close = close_state_allow ){
S3c2410wdt_stop (); // disable config_watchdog_nowayout.
} Else {
Printk (kern_crit pfx "unexpected close, not stopping watchdog! \ N ");
S3c2410wdt_keepalive ();
}
  
Allow_close = close_state_not;
Up (& open_lock );
Return 0;
}
  
Static ssize_t s3c2410wdt_write (struct file * file, const char _ User * data,
Size_t Len, loff_t * PPOs)
{
/*
* "Refresh" the timer.
*/
If (LEN ){
If (! Nowayout) {// do not configure config_watchdog_nowayout
Size_t I;
  
/* In case it was set long ago */
Allow_close = close_state_not;
  
For (I = 0; I! = Len; I ++ ){
Char C;
  
If (get_user (C, Data + I ))
Return-efault;
If (C = 'V') // write character V allows the dog to be disabled, but the premise is that config_watchdog_nowayout is not configured.
Allow_close = close_state_allow;
}
}
  
S3c2410wdt_keepalive (); // feed the dog
}
Return Len;
}
  
# Define options wdiof_settimeout | wdiof_keepaliveping | wdiof_magicclose // Processing Command
  
Static struct watchdog_info s3c2410_wdt_ident = {
. Options = options,
. Firmware_version = 0,
. Identity = "S3C2410 watchdog ",
};
  
  
Static int s3c2410wdt_ioctl (struct inode * inode, struct file * file,
Unsigned int cmd, unsigned long Arg)
{
Void _ User * argp = (void _ User *) ARG;
Int _ User * P = argp;
Int new_margin;
  
Switch (CMD ){
Default:
Return-enoioctlcmd;
  
Case wdioc_getsupport: // obtain the watchdog Information
Return copy_to_user (argp, & s3c2410_wdt_ident,
Sizeof (s3c2410_wdt_ident ))? -Efault: 0; // read to argp, that is, the application's 3rd parameter addresses.
  
Case wdioc_getstatus:
Case wdioc_getbootstatus: // get the checking status
Return put_user (0, P );
  
Case wdioc_keepalive: // dog Feed command
S3c2410wdt_keepalive ();
Return 0;
  
Case wdioc_settimeout: // sets the overflow time value (seconds)
If (get_user (new_margin, p) // obtain the time value
Return-efault;
  
If (s3c2410wdt_set_heartbeat (new_margin) // set it to TCN.
Return-einval;
  
S3c2410wdt_keepalive (); // feed the dog
Return put_user (tmr_margin, P );
  
Case wdioc_gettimeout: // read default overflow time (seconds)
Return put_user (tmr_margin, P );
}
}
  
/* Kernel interface */
  
Static struct file_operations s3c2410wdt_fops = {// application call. Only one layer of driver is simpler than RTC.
. Owner = this_module,
. Llseek = no_llseek, // It is defined as a file that cannot be moved.
. Write = s3c2410wdt_write,
. IOCTL = s3c2410wdt_ioctl,
. Open = s3c2410wdt_open,
. Release = s3c2410wdt_release,
};
  
Static struct miscdevice s3c2410wdt_miscdev = {
. Minor = watchdog_minor, // statically assigned
. Name = "Watchdog ",
. Fops = & s3c2410wdt_fops,
};
  
/* Interrupt handler Code */
  
Static irqreturn_t s3c2410wdt_irq (INT irqno, void * Param, // The interrupt is valid only when resetting is disabled.
Struct pt_regs * regs)
{
Printk (kern_info pfx "watchdog timer expired! \ N ");
  
S3c2410wdt_keepalive (); // feed the dog
Return irq_handled;
}
/* Device interface */
  
Static int s3c2410wdt_probe (struct platform_device * pdev)
// Set prescaler value to clear wtdat to register Miscellaneous devices
{
Struct resource * res;
Int started = 0;
Int ret;
Int size;
  
Dbg ("% s: Probe = % P \ n", _ FUNCTION __, pdev );
  
/* Get the memory region for the watchdog timer */
  
Res = platform_get_resource (pdev, ioresource_mem, 0 );
// Search for the resource address in the pearl, the first ioresource_mem
If (RES = NULL ){
Printk (kern_info pfx "failed to get memory region resouce \ n ");
Return-enoent;
}
  
Size = (res-> end-res-> Start) + 1;
Wdt_mem = request_mem_region (res-> Start, size, pdev-> name); // apply for Io memory
If (wdt_mem = NULL ){
Printk (kern_info pfx "failed to get memory region \ n ");
Return-enoent;
}
  
Wdt_base = ioremap (res-> Start, size); // dynamically map virtual addresses
If (wdt_base = 0 ){
Printk (kern_info pfx "failed to ioremap () region \ n ");
Return-einval;
}
  
Dbg ("probe: mapped wdt_base = % P \ n", wdt_base );
  
Res = platform_get_resource (pdev, ioresource_irq, 0 );
// Search for the resource address in the pearl, the first ioresource_irq
If (RES = NULL ){
Printk (kern_info pfx "failed to get IRQ resource \ n ");
Return-enoent;
}
  
Ret = request_irq (res-> Start, s3c2410wdt_irq, 0, pdev-> name, pdev); // request to interrupt resources
If (Ret! = 0 ){
Printk (kern_info pfx "failed to install IRQ (% d) \ n", RET );
Return ret;
}
  
Wdt_clock = clk_get (& pdev-> Dev, "Watchdog"); // obtain the watchdog CLK struct from the clock linked list.
If (wdt_clock = NULL ){
Printk (kern_info pfx "failed to find watchdog clock source \ n ");
Return-enoent;
}
  
Clk_enable (wdt_clock); // enable the watchdog CLK
  
/* See if we can actually set the requested timer margin, and if
* Not, try the default value */
  
If (s3c2410wdt_set_heartbeat (tmr_margin) {// will not enter
Started = s3c2410wdt_set_heartbeat (config_s3c2410_watchdog_default_time );
  
If (started = 0 ){
Printk (kern_info pfx "tmr_margin value out of range, default % d used \ n ",
Config_s3c2410_watchdog_default_time );
} Else {
Printk (kern_info pfx "Default timer value is out of range, cannot start \ n ");
}
}
  
Ret = misc_register (& s3c2410wdt_miscdev); // register
If (RET ){
Printk (kern_err pfx "cannot register miscdev on minor = % d (% d) \ n ",
Watchdog_minor, RET );
Return ret;
}
  
If (tmr_atboot & started = 0) {// to use a watchdog, you must change the module parameter tmr_atboot to 1.
Printk (kern_info pfx "Starting watchdog timer \ n ");
S3c2410wdt_start ();
}
  
Return 0;
}
  
Static int s3c2410wdt_remove (struct platform_device * Dev)
{
If (wdt_mem! = NULL ){
// Iounmap (wdt_base); should be added
Release_resource (wdt_mem); // deregister Io memory
Kfree (wdt_mem); // releases the memory.
Wdt_mem = NULL;
}
  
If (wdt_irq! = NULL ){
Free_irq (wdt_irq-> Start, Dev); // release the interrupt source
Wdt_irq = NULL;
}
  
If (wdt_clock! = NULL ){
Clk_disable (wdt_clock); // disable wdt clock
Clk_put (wdt_clock); // The number of use times minus 1
Wdt_clock = NULL;
}
  
Misc_deregister (& s3c2410wdt_miscdev );
Return 0;
}
  
Static void s3c2410wdt_shutdown (struct platform_device * Dev)
{
S3c2410wdt_stop ();
}
  
# Ifdef config_pm
  
Static unsigned long wtcon_save;
Static unsigned long wtdat_save;
  
Static int s3c2410wdt_suspend (struct platform_device * Dev, pm_message_t state)
{
/* Save watchdog state, and turn it off .*/
Wtcon_save = readl (wdt_base + s3c2410_wtcon );
Wtdat_save = readl (wdt_base + s3c2410_wtdat );
  
/* Note that wtcnt doesn' t need to be saved .*/
S3c2410wdt_stop ();
  
Return 0;
}
  
Static int s3c2410wdt_resume (struct platform_device * Dev)
{
/* Restore watchdog state .*/
  
Writel (wtdat_save, wdt_base + s3c2410_wtdat );
Writel (wtdat_save, wdt_base + s3c2410_wtcnt);/* reset count */
Writel (wtcon_save, wdt_base + s3c2410_wtcon );
  
Printk (kern_info pfx "watchdog % sabled \ n ",
(Wtcon_save & s3c2410_wtcon_enable )? "EN": "dis ");
  
Return 0;
}
# Else
# Define s3c2410wdt_suspend null
# Define s3c2410wdt_resume null
# Endif/* config_pm */
  
  
Static struct platform_driver s3c2410wdt_driver = {
. Probe = s3c2410wdt_probe,
. Remove = s3c2410wdt_remove,
. Shutdown = s3c2410wdt_shutdown,
. Suspend = s3c2410wdt_suspend,
. Resume = s3c2410wdt_resume,
. Driver = {
. Owner = this_module,
. Name = "s3c2410-wdt ",
},
};
  
  
Static char banner [] _ initdata = kern_info "S3C2410 watchdog timer, (c) 2004 simtec electronics \ n ";
  
Static int _ init watchdog_init (void)
{
Printk (banner );
Return platform_driver_register (& s3c2410wdt_driver); // register the platform device
}
  
Static void _ exit watchdog_exit (void)
{
Platform_driver_unregister (& s3c2410wdt_driver );
}
  
Module_init (watchdog_init );
Module_exit (watchdog_exit );
  
Module_author ("Ben dooks ,"
"Dimitry Andric ");
Module_description ("S3C2410 watchdog device driver ");
Module_license ("GPL ");
Module_alias_miscdev (watchdog_minor );}

Platform device Watchdog
Unlike RTC, which has a common layer that ignores hardware, a watchdog has only one structure, that is, a miscellaneous device structure. The application layer directly deals with miscellaneous device files. The development process is similar to that of RTC, but there are several parameters that need attention.
Nowayout: it is related to the config_watchdog_nowayout configuration. Once the application layer is configured to call the close function, the watchdog cannot be disabled;
Tmr_margin: Default dog feeding time;
Tmr_atboot: To enable the watchdog automatically upon power-on, the value is 1. It is best to use 0, because we want to open the watchdog through the open function;
Soft_noboot: when it is set to 1, the watchdog will be used as the general interrupt timer. When it is set to 0, it will be used as the watchdog of the reset circuit. The default value is 0;

After an application calls open to open the dog, if it writes a non-"v" to call close, the dog cannot be closed. That is, the watchdog device can be disabled only after "v" is written. See the wdt_test.c file.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.