/* * linux/drivers/char/wmt_gpio.c * * Copyright (c) 2008 WonderMedia Technologies, Inc. * * 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. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU General Public License for more details. * You should have received a copy of the GNU General Public License along with * this program. If not, see . * * WonderMedia Technologies, Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #define OSCR_FREQ 3000000 /* 3 MHz tick rate */ static unsigned long wmt_wdt_users; static int expect_close; static int pre_margin; static unsigned int wdt_tcr; static unsigned int wdt_tmr0; static unsigned int wdt_enable; #define WDT_NS_RSR (PM_CTRL_BASE_ADDR+0x8050) //NS Reset Status Register #define WDT_NS_TMR4 (PM_CTRL_BASE_ADDR+0x80f0) //NS OS Timer Match Register 4 #define WDT_NS_TMR5 (PM_CTRL_BASE_ADDR+0x80f4) //NS OS Timer Match Register 5 #define WDT_NS_TMR6 (PM_CTRL_BASE_ADDR+0x80f8) //NS OS Timer Match Register 6 #define WDT_NS_TMR7 (PM_CTRL_BASE_ADDR+0x80fc) //NS OS Timer Match Register 7 #define WDT_NS_TMR0 (PM_CTRL_BASE_ADDR+0x8100) //NS OS Timer Match Register 0 #define WDT_NS_TMR1 (PM_CTRL_BASE_ADDR+0x8104) //NS OS Timer Match Register 1 #define WDT_NS_TMR2 (PM_CTRL_BASE_ADDR+0x8108) //NS OS Timer Match Register 2 #define WDT_NS_TMR3 (PM_CTRL_BASE_ADDR+0x810c) //NS OS Timer Match Register 3 #define WDT_NS_TCR (PM_CTRL_BASE_ADDR+0x8110) //NS OS Timer Count Register #define WDT_NS_TSR (PM_CTRL_BASE_ADDR+0x8114) //NS OS Timer Status register #define WDT_NS_TWER (PM_CTRL_BASE_ADDR+0x8118) //NS OS Timer Watch Dog Enable Register #define WDT_NS_IER (PM_CTRL_BASE_ADDR+0x811c) //NS OS Timer Interrupt Enable Register #define WDT_NS_CTRL (PM_CTRL_BASE_ADDR+0x8120) //NS OS Timer Control Register #define WDT_NS_TASR (PM_CTRL_BASE_ADDR+0x8124) //NS OS Timer Access Status Register #define WMT_NS_EN (1<<4) #define WMT_NS_RESET (1<<3) unsigned int wmt_read_ns_oscr(void) { // // Request the reading of OS Timer Count Register. // REG32_VAL(WDT_NS_CTRL) |= OSTC_RDREQ; // // Polling until free to reading. // while ( REG32_VAL(WDT_NS_TASR) & OSTA_RCA ); return REG32_VAL(WDT_NS_TCR); } void wmt_write_ns_oscr(unsigned int val) { // // Polling until free to reading. // while ( REG32_VAL(WDT_NS_TASR) & OSTA_CWA ); REG32_VAL(WDT_NS_TCR) = val; } /* wmt_wdt_open() * * There is a simple mutex to protect only one user could use it. */ static int wmt_wdt_open(struct inode *inode, struct file *file) { if (test_and_set_bit(1, &wmt_wdt_users)) return -EBUSY; /* * 1. Set timer match value. * 2. Activate watchdog timer. */ REG32_VAL(WDT_NS_TMR0) = pre_margin + wmt_read_ns_oscr(); REG32_VAL(WDT_NS_TWER) = OSTW_WE; OSTW_VAL |= WMT_NS_EN; REG32_VAL(WDT_NS_CTRL) = OSTC_ENABLE; return 0; } /* wmt_wdt_release() * * Shut off the watchdog timer. */ static int wmt_wdt_release(struct inode *inode, struct file *file) { /* * Release watchdog timer depend on NOWAYOUT option. */ #ifndef CONFIG_WATCHDOG_NOWAYOUT if (expect_close) REG32_VAL(WDT_NS_TWER) = 0; else printk("CONFIG_WATCHDOG_NOWAYOUT\n"); #else printk(KERN_CRIT "Watchdog: WDT device closed unexpectedly. " \ "WDT will not stop!\n"); #endif clear_bit(1, &wmt_wdt_users); expect_close = 0; return 0; } /* wmt_wdt_write() * */ static ssize_t wmt_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) { int ret; /* * Check if we can seek (pwrite) on this device. */ if ( len ) { #ifndef CONFIG_WATCHDOG_NOWAYOUT size_t i; expect_close = 0; for ( i=0; i!=len; i++ ) { char c; if ( get_user(c, data + i) ) return -EFAULT; /* * The magic character 'V' is for shotdown * watchdog. */ if ( c == 'V' ) expect_close = 1; } #endif /* * Refresh watchdog timer. */ //printk("Refresh %d to ",REG32_VAL(WDT_NS_TMR0)); REG32_VAL(WDT_NS_TMR0) = pre_margin + wmt_read_ns_oscr(); //printk(" to %d\n",REG32_VAL(WDT_NS_TMR0)); } ret = len ? 1 : 0; return ret; } static struct watchdog_info ident = { .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, .identity = "WMT Watchdog", .firmware_version = 0, }; /* wmt_wdt_ioctl() * */ static long wmt_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) //static int wmt_wdt_ioctl(struct inode *inode, struct file *file, // unsigned int cmd, unsigned long arg) { int ret = 0, time; switch ( cmd ) { case WDIOC_GETSUPPORT: { ret = copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)) ? -EFAULT : 0; break; } case WDIOC_GETSTATUS: return put_user(0, (int __user *)arg); case WDIOC_GETBOOTSTATUS: { /* TODO */ break; } case WDIOC_SETTIMEOUT: { if (get_user(time, (int __user *)arg)) return -EFAULT; /* Sanity check */ if ( time <= 0 || time > 255 ) { ret = -EINVAL; break; } pre_margin = OSCR_FREQ * time; REG32_VAL(WDT_NS_TMR0) = pre_margin + wmt_read_ns_oscr(); /* Falling down */ } case WDIOC_GETTIMEOUT: { ret = put_user(pre_margin / OSCR_FREQ, (int *)arg); break; } case WDIOC_KEEPALIVE: { REG32_VAL(WDT_NS_TMR0) = pre_margin + wmt_read_ns_oscr(); ret = 0; break; } default: { /* No this ioctl command */ ret = -ENOIOCTLCMD; } } return ret; } static struct file_operations wmt_dog_fops = { .owner = THIS_MODULE, .write = wmt_wdt_write, .unlocked_ioctl = wmt_wdt_ioctl, .open = wmt_wdt_open, .release = wmt_wdt_release, }; static struct miscdevice wmt_dog_miscdev = { .minor = WATCHDOG_MINOR, .name = "watchdog", .fops = &wmt_dog_fops, }; static int margin __initdata = 60; /* (secs) Default using one minute. */ static int wmt_watchdog_probe(struct platform_device *pdev) { int ret; unsigned int val; /* * Check if there is an enabled watchdog timer * due to IO_RESET or SW_RESET. * */ if ( REG32_VAL(WDT_NS_RSR) & WMT_NS_RESET) REG32_VAL(WDT_NS_RSR) = WMT_NS_RESET; val = OSTW_VAL; if (OSTW_VAL&WMT_NS_EN) { val &= ~WMT_NS_EN; OSTW_VAL = val; } if ( REG32_VAL(WDT_NS_CTRL) & OSTC_ENABLE) REG32_VAL(WDT_NS_CTRL) = 0; pre_margin = OSCR_FREQ * margin; ret = misc_register(&wmt_dog_miscdev); if (ret) { printk("Error: Can not register Watchdog driver\n"); return ret; } printk(KERN_INFO "Watchdog: timer margin %d sec\n", margin); return ret; } static int __devexit wmt_wdt_remove(struct platform_device *pdev) { return 0; } #ifdef CONFIG_PM static int wmt_watchdog_suspend ( struct device *pdev ) { wdt_enable = 0; if ( REG32_VAL(WDT_NS_CTRL) & OSTC_ENABLE) { wdt_tcr = wmt_read_ns_oscr(); wdt_tmr0 = REG32_VAL(WDT_NS_TMR0); wdt_enable = 1; printk("wdt_tcr = 0x%x , wdt_tmr0 = 0x%x \n",wdt_tcr,wdt_tmr0); } return 0; } static int wmt_watchdog_resume ( struct device *pdev ) { if (wdt_enable) { REG32_VAL(WDT_NS_TMR0) = wdt_tmr0; wmt_write_ns_oscr(wdt_tcr); REG32_VAL(WDT_NS_TWER) = OSTW_WE; OSTW_VAL |= WMT_NS_EN; REG32_VAL(WDT_NS_CTRL) = OSTC_ENABLE; printk("resume : wdt_tcr = 0x%x , wdt_tmr0 = 0x%x \n",wmt_read_ns_oscr(),REG32_VAL(WDT_NS_TMR0)); } return 0; } static int wmt_watchdog_freeze(struct device *dev) { return 0; } static int wmt_watchdog_restore(struct device *dev) { return 0; } #else #define wmt_watchdog_suspend NULL #define wmt_watchdog_resume NULL #define wmt_watchdog_freeze NULL #define wmt_watchdog_restore NULL #endif static struct dev_pm_ops wmt_watchdog_pm_ops = { .suspend = wmt_watchdog_suspend, .resume = wmt_watchdog_resume, .freeze = wmt_watchdog_freeze, .thaw = wmt_watchdog_restore, .restore = wmt_watchdog_restore, }; static struct platform_device wmt_watchdog_device = { .name = "wmt-watchdog", .id = 0, }; static struct platform_driver wmt_watchdog_driver = { .driver = { .name = "wmt-watchdog", .owner = THIS_MODULE, }, .probe = wmt_watchdog_probe, .remove = __devexit_p(wmt_wdt_remove), .driver.pm = &wmt_watchdog_pm_ops }; static int __init wmt_watchdog_init(void) { int ret; ret = platform_device_register(&wmt_watchdog_device); if(ret) { printk("Error: Can not register Watchdog platform\n"); return ret; } ret = platform_driver_register(&wmt_watchdog_driver); if(ret) { printk("Error: Can not register Watchdog driver\n"); platform_device_unregister(&wmt_watchdog_device); return ret; } return 0; } module_init(wmt_watchdog_init); static void __exit wmt_watchdog_exit(void) { platform_driver_unregister(&wmt_watchdog_driver); platform_device_unregister(&wmt_watchdog_device); } module_exit(wmt_watchdog_exit); MODULE_DESCRIPTION("WMT Watchdog Driver"); module_param(margin, int, 0); MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); MODULE_LICENSE("GPL");