/****************************************************************************** ** ** FILE NAME : amazon_s_wdt.c ** PROJECT : amazon_s ** MODULES : WDT ** ** DATE : 4 Aug 2005 ** AUTHOR : Huang Xiaogang ** DESCRIPTION : amazon_s Watchdog Timer driver ** COPYRIGHT : Copyright (c) 2006 ** Infineon Technologies AG ** Am Campeon 1-12, 85579 Neubiberg, Germany ** ** 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. ** ** HISTORY ** $Date $Author $Comment ** 01 Jan 2006 Huang Xiaogang modification & verification on amazon_s chip *******************************************************************************/ #if defined(MODVERSIONS) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct wdt_dev{ char name[16]; int major; int minor; int full; char buff[10]; }wdt_dev; #define AMAZON_S_WDT_EMSG(fmt, args...) printk( "%s: " fmt, __FUNCTION__ , ##args) #define AMAZON_S_WDT_DMSG(fmt, args...) printk(" %s:" fmt, __FUNCTION__, ##args) extern unsigned int amazon_s_get_fpi_hz(void); static struct wdt_dev *amazon_s_wdt_dev; static int occupied=0; int wdt_enable(u32 timeout) { u32 wdt_cr=0; u32 wdt_reload=0; u32 wdt_clkdiv=0, clkdiv, wdt_pwl=0, pwl, ffpi; /* clock divider & prewarning limit */ switch(clkdiv = AMAZON_S_BIU_WDT_CR_CLKDIV_GET(*AMAZON_S_BIU_WDT_CR)){ case 0: wdt_clkdiv = 1; break; case 1: wdt_clkdiv = 64; break; case 2: wdt_clkdiv = 4096; break; case 3: wdt_clkdiv = 262144; break; } switch(pwl = AMAZON_S_BIU_WDT_CR_PWL_GET(*AMAZON_S_BIU_WDT_CR)){ case 0: wdt_pwl = 0x8000; break; case 1: wdt_pwl = 0x4000; break; case 2: wdt_pwl = 0x2000; break; case 3: wdt_pwl = 0x1000; break; } #ifdef CONFIG_USE_EMULATOR ffpi = 10000000; //1250000; #else // ffpi = amazon_s_get_cpu_hz(); // ffpi = cgu_get_cpu_clock(); ffpi = cgu_get_io_region_clock(); //printk("cpu clock = %d\n", ffpi); #endif /* caculate reload value */ wdt_reload = (timeout * (ffpi / wdt_clkdiv)) + wdt_pwl; //printk("wdt_pwl=0x%x, wdt_clkdiv=%d, ffpi=%d, wdt_reload = 0x%x\n", wdt_pwl, wdt_clkdiv, ffpi, wdt_reload); if (wdt_reload > 0xFFFF){ AMAZON_S_WDT_EMSG("timeout too large %d\n", timeout); return -EINVAL; } /* Write first part of password access */ *AMAZON_S_BIU_WDT_CR = AMAZON_S_BIU_WDT_CR_PW_SET(AMAZON_S_WDT_PW1); wdt_cr = *AMAZON_S_BIU_WDT_CR; wdt_cr &= ( !AMAZON_S_BIU_WDT_CR_PW_SET(0xff) & !AMAZON_S_BIU_WDT_CR_PWL_SET(0x3) & !AMAZON_S_BIU_WDT_CR_CLKDIV_SET(0x3) & !AMAZON_S_BIU_WDT_CR_RELOAD_SET(0xffff)); wdt_cr |= ( AMAZON_S_BIU_WDT_CR_PW_SET(AMAZON_S_WDT_PW2) | AMAZON_S_BIU_WDT_CR_PWL_SET(pwl) | AMAZON_S_BIU_WDT_CR_CLKDIV_SET(clkdiv) | AMAZON_S_BIU_WDT_CR_RELOAD_SET(wdt_reload) | AMAZON_S_BIU_WDT_CR_GEN); /* Set reload value in second password access */ *AMAZON_S_BIU_WDT_CR = wdt_cr; //printk("wdt enabled\n"); return 0 ; } void wdt_disable(void) { /* Write first part of password access */ *AMAZON_S_BIU_WDT_CR = AMAZON_S_BIU_WDT_CR_PW_SET(AMAZON_S_WDT_PW1); /* Disable the watchdog in second password access (GEN=0) */ *AMAZON_S_BIU_WDT_CR = AMAZON_S_BIU_WDT_CR_PW_SET(AMAZON_S_WDT_PW2); } void wdt_low_power(int en) { u32 wdt_cr=0; *AMAZON_S_BIU_WDT_CR = AMAZON_S_BIU_WDT_CR_PW_SET(AMAZON_S_WDT_PW1); wdt_cr = *AMAZON_S_BIU_WDT_CR; if(en){ wdt_cr &= (!AMAZON_S_BIU_WDT_CR_PW_SET(0xff)); wdt_cr |= (AMAZON_S_BIU_WDT_CR_PW_SET(AMAZON_S_WDT_PW2) | AMAZON_S_BIU_WDT_CR_LPEN); } else{ wdt_cr &= (!AMAZON_S_BIU_WDT_CR_PW_SET(0xff) & !AMAZON_S_BIU_WDT_CR_LPEN); wdt_cr |= AMAZON_S_BIU_WDT_CR_PW_SET(AMAZON_S_WDT_PW2); } /* Set reload value in second password access */ *AMAZON_S_BIU_WDT_CR = wdt_cr; } void wdt_debug_suspend(int en) { u32 wdt_cr=0; *AMAZON_S_BIU_WDT_CR = AMAZON_S_BIU_WDT_CR_PW_SET(AMAZON_S_WDT_PW1); wdt_cr = *AMAZON_S_BIU_WDT_CR; if(en){ wdt_cr &= (!AMAZON_S_BIU_WDT_CR_PW_SET(0xff)); wdt_cr |= (AMAZON_S_BIU_WDT_CR_PW_SET(AMAZON_S_WDT_PW2) | AMAZON_S_BIU_WDT_CR_DSEN); } else{ wdt_cr &= (!AMAZON_S_BIU_WDT_CR_PW_SET(0xff) & !AMAZON_S_BIU_WDT_CR_DSEN); wdt_cr |= AMAZON_S_BIU_WDT_CR_PW_SET(AMAZON_S_WDT_PW2); } /* Set reload value in second password access */ *AMAZON_S_BIU_WDT_CR = wdt_cr; } void wdt_prewarning_limit(int pwl) { u32 wdt_cr=0; wdt_cr = *AMAZON_S_BIU_WDT_CR; *AMAZON_S_BIU_WDT_CR = AMAZON_S_BIU_WDT_CR_PW_SET(AMAZON_S_WDT_PW1); wdt_cr &= 0xff00ffff;//(!AMAZON_S_BIU_WDT_CR_PW_SET(0xff)); wdt_cr &= 0xf3ffffff;//(!AMAZON_S_BIU_WDT_CR_PWL_SET(3)); wdt_cr |= (AMAZON_S_BIU_WDT_CR_PW_SET(AMAZON_S_WDT_PW2) | AMAZON_S_BIU_WDT_CR_PWL_SET(pwl)); /* Set reload value in second password access */ *AMAZON_S_BIU_WDT_CR = wdt_cr; } void wdt_set_clkdiv(int clkdiv) { u32 wdt_cr=0; wdt_cr = *AMAZON_S_BIU_WDT_CR; *AMAZON_S_BIU_WDT_CR = AMAZON_S_BIU_WDT_CR_PW_SET(AMAZON_S_WDT_PW1); wdt_cr &= 0xff00ffff; //(!AMAZON_S_BIU_WDT_CR_PW_SET(0xff)); wdt_cr &= 0xfcffffff; //(!AMAZON_S_BIU_WDT_CR_CLKDIV_SET(3)); wdt_cr |= (AMAZON_S_BIU_WDT_CR_PW_SET(AMAZON_S_WDT_PW2) | AMAZON_S_BIU_WDT_CR_CLKDIV_SET(clkdiv)); /* Set reload value in second password access */ *AMAZON_S_BIU_WDT_CR = wdt_cr; } static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int result=0; int en=0; int istatus; int pwl, clkdiv; static int timeout = -1; switch(cmd){ case AMAZON_S_WDT_IOC_START: AMAZON_S_WDT_DMSG("enable watch dog timer!\n"); if ( copy_from_user((void*)&timeout, (void*)arg, sizeof (int)) ){ AMAZON_S_WDT_EMSG("invalid argument\n"); result=-EINVAL; }else{ if((result = wdt_enable(timeout)) < 0){ timeout = -1; } } break; case AMAZON_S_WDT_IOC_STOP: AMAZON_S_WDT_DMSG("disable watch dog timer\n"); timeout = -1; wdt_disable(); break; case AMAZON_S_WDT_IOC_PING: if(timeout < 0){ result = -EIO; }else{ result = wdt_enable(timeout); } break; case AMAZON_S_WDT_IOC_SET_PWL: if ( copy_from_user((void*)&pwl, (void*)arg, sizeof (int)) ){ AMAZON_S_WDT_EMSG("invalid argument\n"); result=-EINVAL; } wdt_prewarning_limit(pwl); break; case AMAZON_S_WDT_IOC_SET_DSEN: if ( copy_from_user((void*)&en, (void*)arg, sizeof (int)) ){ AMAZON_S_WDT_EMSG("invalid argument\n"); result=-EINVAL; } wdt_debug_suspend(en); break; case AMAZON_S_WDT_IOC_SET_LPEN: if ( copy_from_user((void*)&en, (void*)arg, sizeof (int)) ){ AMAZON_S_WDT_EMSG("invalid argument\n"); result=-EINVAL; } wdt_low_power(en); break; case AMAZON_S_WDT_IOC_SET_CLKDIV: if ( copy_from_user((void*)&clkdiv, (void*)arg, sizeof (int)) ){ AMAZON_S_WDT_EMSG("invalid argument\n"); result=-EINVAL; } wdt_set_clkdiv(clkdiv); break; case AMAZON_S_WDT_IOC_GET_STATUS: istatus = AMAZON_S_WDT_REG32(AMAZON_S_BIU_WDT_SR); copy_to_user((int *)arg, (int *)&istatus, sizeof(int)); break; } return result; } static int wdt_open(struct inode *inode, struct file *file) { AMAZON_S_WDT_DMSG("wdt_open\n"); if (occupied == 1) return -EBUSY; occupied = 1; // MOD_INC_USE_COUNT; return 0; } static int wdt_release(struct inode *inode, struct file *file) { AMAZON_S_WDT_DMSG("wdt_release\n"); occupied = 0; // MOD_DEC_USE_COUNT; return 0; } int wdt_register_proc_read(char *buf, char **start, off_t offset, int count, int *eof, void *data) { int len=0; len+=sprintf(buf+len,"AMAZON_S_BIU_WDT_PROC_READ\n"); len+=sprintf(buf+len,"AMAZON_S_BIU_WDT_CR(0x%08x) : 0x%08x\n", (unsigned int)AMAZON_S_BIU_WDT_CR, AMAZON_S_WDT_REG32(AMAZON_S_BIU_WDT_CR)); len+=sprintf(buf+len,"AMAZON_S_BIU_WDT_SR(0x%08x) : 0x%08x\n", (unsigned int)AMAZON_S_BIU_WDT_SR, AMAZON_S_WDT_REG32(AMAZON_S_BIU_WDT_SR)); *eof = 1; return len; } static struct file_operations wdt_fops = { owner: THIS_MODULE, ioctl: wdt_ioctl, open: wdt_open, release: wdt_release, }; int __init amazon_s_wdt_init_module(void) { int result =0; amazon_s_wdt_dev = (wdt_dev*)kmalloc(sizeof(wdt_dev),GFP_KERNEL); #define IFX_WDT_DRV_VERSION "1.0.0" printk(KERN_INFO "Infineon Technologies WDT driver version %s \n", IFX_WDT_DRV_VERSION); if (amazon_s_wdt_dev == NULL){ return -ENOMEM; } memset(amazon_s_wdt_dev,0,sizeof(wdt_dev)); strcpy(amazon_s_wdt_dev->name, DEVICE_NAME); result = register_chrdev(0,amazon_s_wdt_dev->name,&wdt_fops); if (result < 0) { AMAZON_S_WDT_EMSG("cannot register device\n"); kfree(amazon_s_wdt_dev); return -EINVAL; } amazon_s_wdt_dev->major = result; /* Create proc file */ create_proc_read_entry("amazon_s_wdt", 0, NULL, wdt_register_proc_read , NULL); return 0; } void amazon_s_wdt_cleanup_module(void) { unregister_chrdev(amazon_s_wdt_dev->major,amazon_s_wdt_dev->name); remove_proc_entry("amazon_s_wdt", NULL); kfree(amazon_s_wdt_dev); return; } module_init(amazon_s_wdt_init_module); module_exit(amazon_s_wdt_cleanup_module);