/* * CPLD Watchdog Driver * * Copyright (c) 2018 Ingrasys Corp. * * Author: Wade He * * 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 . */ /* * Includes, defines, variables, module parameters, ... */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt /* Module and version information */ #define DRV_NAME "cpld_wdt" #define DRV_VERSION "1.0" /* Includes */ #include /* For module specific items */ #include /* For new moduleparam's */ #include /* For standard types (like size_t) */ #include /* For the -ENODEV/... values */ #include /* For printk/panic/... */ #include /* For the watchdog specific items */ #include /* For __init/__exit/... */ #include /* For file operations */ #include /* For platform_driver framework */ #include /* For pci functions */ #include /* For io-port access */ #include /* For spin_lock/spin_unlock/... */ #include /* For copy_to_user/put_user/... */ #include /* For inb/outb/... */ #include #include #include #include /* Address definitions for the CPLD */ /* CPLD base address */ #define TCOBASE 0x600 /* SMI Control and Enable Register */ #define TCO_RLD (TCOBASE + 0x00) /* TCO Timer Reload and Curr. Value */ #define TCOv1_TMR (TCOBASE + 0x01) /* TCOv1 Timer Initial Value */ #define TCO_DAT_IN (TCOBASE + 0x02) /* TCO Data In Register */ #define TCO_DAT_OUT (TCOBASE + 0x03) /* TCO Data Out Register */ #define TCO1_STS (TCOBASE + 0x04) /* Control Watchdog Register */ #define TCO2_STS (TCOBASE + 0x06) /* TCO2 Status Register */ #define TCO1_CNT (TCOBASE + 0x08) /* TCO1 Control Register */ #define TCO2_CNT (TCOBASE + 0x0a) /* TCO2 Control Register */ #define TCOv2_TMR (TCOBASE + 0x12) /* TCOv2 Timer Initial Value */ #define DEBUG #ifdef DEBUG #define DEBUG_PRINT(fmt, args...) \ printk (KERN_INFO "%s[%d]: " fmt "\r\n", \ __FUNCTION__, __LINE__, ##args) #else #define DEBUG_PRINT(fmt, args...) #endif #define ERROR_MSG(fmt, args...) \ printk(KERN_ERR "%s[%d]: " fmt "\r\n", \ __FUNCTION__, __LINE__, ##args) /* internal variables */ static struct { /* this is private data for the cpld_wdt device */ /* the lock for io operations */ spinlock_t io_lock; struct platform_device *dev; } cpld_wdt_private; static struct task_struct *cpld_wdt_tsk; static int data; static void device_release(struct device *dev) { return; } static struct platform_device cpld_wdt = { .name = DRV_NAME, .id = 0, .dev = { .platform_data = NULL, .release = device_release }, }; /* module parameters */ #define WATCHDOG_TIMEOUT 15 /* 15 sec default heartbeat */ static int heartbeat = WATCHDOG_TIMEOUT; /* in seconds */ module_param(heartbeat, int, 0); MODULE_PARM_DESC(heartbeat, "Watchdog ping period in seconds. " "5..20, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")"); 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 cpld_wdt_check_timeout_range(unsigned int tmrval) { if (tmrval < 5 || tmrval > 20) { DEBUG_PRINT("heartbeat out of range, using default=%d\n", WATCHDOG_TIMEOUT); heartbeat = WATCHDOG_TIMEOUT; } else { DEBUG_PRINT("heartbeat using %d seconds\n", heartbeat); } return 0; } /* * Some TCO specific functions */ static int cpld_wdt_stop(void *arg) { spin_lock(&cpld_wdt_private.io_lock); outb(0x1, TCO1_STS); DEBUG_PRINT("cpld_wdt_stop done"); spin_unlock(&cpld_wdt_private.io_lock); return 0; } static int cpld_wdt_ping(void *arg) { spin_lock(&cpld_wdt_private.io_lock); /* Reload the timer by writing to the TCO Timer Counter register */ outb(0x1, TCO1_STS); /* write 1 to clear bit */ udelay(100); outb(0x3, TCO1_STS); DEBUG_PRINT("cpld_wdt_ping done"); spin_unlock(&cpld_wdt_private.io_lock); return 0; } static int kthread_wdt_ping_loop(void *arg) { int i; set_current_state(TASK_INTERRUPTIBLE); while(!kthread_should_stop()) { DEBUG_PRINT("ping start"); cpld_wdt_ping(NULL); set_current_state(TASK_INTERRUPTIBLE); for (i=0;i"); MODULE_DESCRIPTION("CPLD Watchdog Timer Kernel Driver"); MODULE_VERSION(DRV_VERSION); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRV_NAME);