source: trunk/target/linux/gemini/patches-3.3/110-watchdog-add-gemini_wdt-driver.patch @ 31698

Last change on this file since 31698 was 31698, checked in by juhosg, 4 years ago

gemini: add support for 3.3

File size: 10.1 KB
  • new file drivers/watchdog/gemini_wdt.c

    - +  
     1/* 
     2 *  Watchdog driver for Cortina Systems Gemini SoC 
     3 * 
     4 *  Copyright (C) 2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt> 
     5 * 
     6 * This program is free software; you can redistribute it and/or modify 
     7 * it under the terms of the GNU General Public License version 2 as 
     8 * published by the Free Software Foundation. 
     9 */ 
     10 
     11#include <linux/kernel.h> 
     12#include <linux/init.h> 
     13#include <linux/module.h> 
     14#include <linux/io.h> 
     15#include <linux/fs.h> 
     16#include <linux/uaccess.h> 
     17#include <linux/miscdevice.h> 
     18#include <linux/platform_device.h> 
     19#include <linux/watchdog.h> 
     20#include <linux/slab.h> 
     21 
     22#define GEMINI_WDCOUNTER        0x0 
     23#define GEMINI_WDLOAD           0x4 
     24#define GEMINI_WDRESTART        0x8 
     25 
     26#define WDRESTART_MAGIC         0x5AB9 
     27 
     28#define GEMINI_WDCR             0xC 
     29 
     30#define WDCR_CLOCK_5MHZ         (1 << 4) 
     31#define WDCR_SYS_RST            (1 << 1) 
     32#define WDCR_ENABLE             (1 << 0) 
     33 
     34#define WDT_CLOCK               5000000         /* 5 MHz */ 
     35#define WDT_DEFAULT_TIMEOUT     13 
     36#define WDT_MAX_TIMEOUT         (0xFFFFFFFF / WDT_CLOCK) 
     37 
     38/* status bits */ 
     39#define WDT_ACTIVE              0 
     40#define WDT_OK_TO_CLOSE         1 
     41 
     42static unsigned int timeout = WDT_DEFAULT_TIMEOUT; 
     43static int nowayout = WATCHDOG_NOWAYOUT; 
     44 
     45static DEFINE_SPINLOCK(gemini_wdt_lock); 
     46 
     47static struct platform_device *gemini_wdt_dev; 
     48 
     49struct gemini_wdt_struct { 
     50        struct resource         *res; 
     51        struct device           *dev; 
     52        void __iomem            *base; 
     53        unsigned long           status; 
     54}; 
     55 
     56static struct watchdog_info gemini_wdt_info = { 
     57        .identity       = "Gemini watchdog", 
     58        .options        = WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING | 
     59                          WDIOF_SETTIMEOUT, 
     60}; 
     61 
     62/* Disable the watchdog. */ 
     63static void gemini_wdt_stop(struct gemini_wdt_struct *gemini_wdt) 
     64{ 
     65        spin_lock(&gemini_wdt_lock); 
     66 
     67        __raw_writel(0, gemini_wdt->base + GEMINI_WDCR); 
     68 
     69        clear_bit(WDT_ACTIVE, &gemini_wdt->status); 
     70 
     71        spin_unlock(&gemini_wdt_lock); 
     72} 
     73 
     74/* Service the watchdog */ 
     75static void gemini_wdt_service(struct gemini_wdt_struct *gemini_wdt) 
     76{ 
     77        __raw_writel(WDRESTART_MAGIC, gemini_wdt->base + GEMINI_WDRESTART); 
     78} 
     79 
     80/* Enable and reset the watchdog. */ 
     81static void gemini_wdt_start(struct gemini_wdt_struct *gemini_wdt) 
     82{ 
     83        spin_lock(&gemini_wdt_lock); 
     84 
     85        __raw_writel(timeout * WDT_CLOCK, gemini_wdt->base + GEMINI_WDLOAD); 
     86 
     87        gemini_wdt_service(gemini_wdt); 
     88 
     89        /* set clock before enabling */ 
     90        __raw_writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST, 
     91                        gemini_wdt->base + GEMINI_WDCR); 
     92 
     93        __raw_writel(WDCR_CLOCK_5MHZ | WDCR_SYS_RST | WDCR_ENABLE, 
     94                        gemini_wdt->base + GEMINI_WDCR); 
     95 
     96        set_bit(WDT_ACTIVE, &gemini_wdt->status); 
     97 
     98        spin_unlock(&gemini_wdt_lock); 
     99} 
     100 
     101/* Watchdog device is opened, and watchdog starts running. */ 
     102static int gemini_wdt_open(struct inode *inode, struct file *file) 
     103{ 
     104        struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(gemini_wdt_dev); 
     105 
     106        if (test_bit(WDT_ACTIVE, &gemini_wdt->status)) 
     107                return -EBUSY; 
     108 
     109        file->private_data = gemini_wdt; 
     110 
     111        gemini_wdt_start(gemini_wdt); 
     112 
     113        return nonseekable_open(inode, file); 
     114} 
     115 
     116/* Close the watchdog device. */ 
     117static int gemini_wdt_close(struct inode *inode, struct file *file) 
     118{ 
     119        struct gemini_wdt_struct *gemini_wdt = file->private_data; 
     120 
     121        /* Disable the watchdog if possible */ 
     122        if (test_bit(WDT_OK_TO_CLOSE, &gemini_wdt->status)) 
     123                gemini_wdt_stop(gemini_wdt); 
     124        else 
     125                dev_warn(gemini_wdt->dev, "Device closed unexpectedly - timer will not stop\n"); 
     126 
     127        return 0; 
     128} 
     129 
     130/* Handle commands from user-space. */ 
     131static long gemini_wdt_ioctl(struct file *file, unsigned int cmd, 
     132                             unsigned long arg) 
     133{ 
     134        struct gemini_wdt_struct *gemini_wdt = file->private_data; 
     135 
     136        int value; 
     137 
     138        switch (cmd) { 
     139        case WDIOC_KEEPALIVE: 
     140                gemini_wdt_service(gemini_wdt); 
     141                return 0; 
     142 
     143        case WDIOC_GETSUPPORT: 
     144                return copy_to_user((struct watchdog_info *)arg, &gemini_wdt_info, 
     145                        sizeof(gemini_wdt_info)) ? -EFAULT : 0; 
     146 
     147        case WDIOC_SETTIMEOUT: 
     148                if (get_user(value, (int *)arg)) 
     149                        return -EFAULT; 
     150 
     151                if ((value < 1) || (value > WDT_MAX_TIMEOUT)) 
     152                        return -EINVAL; 
     153 
     154                timeout = value; 
     155 
     156                /* restart wdt to use new timeout */ 
     157                gemini_wdt_stop(gemini_wdt); 
     158                gemini_wdt_start(gemini_wdt); 
     159 
     160                /* Fall through */ 
     161        case WDIOC_GETTIMEOUT: 
     162                return put_user(timeout, (int *)arg); 
     163 
     164        case WDIOC_GETTIMELEFT: 
     165                value = __raw_readl(gemini_wdt->base + GEMINI_WDCOUNTER); 
     166                return put_user(value / WDT_CLOCK, (int *)arg); 
     167 
     168        default: 
     169                return -ENOTTY; 
     170        } 
     171} 
     172 
     173/* Refresh the watchdog whenever device is written to. */ 
     174static ssize_t gemini_wdt_write(struct file *file, const char *data, 
     175                                                size_t len, loff_t *ppos) 
     176{ 
     177        struct gemini_wdt_struct *gemini_wdt = file->private_data; 
     178 
     179        if (len) { 
     180                if (!nowayout) { 
     181                        size_t i; 
     182 
     183                        clear_bit(WDT_OK_TO_CLOSE, &gemini_wdt->status); 
     184                        for (i = 0; i != len; i++) { 
     185                                char c; 
     186 
     187                                if (get_user(c, data + i)) 
     188                                        return -EFAULT; 
     189                                if (c == 'V') 
     190                                        set_bit(WDT_OK_TO_CLOSE, 
     191                                                &gemini_wdt->status); 
     192                        } 
     193                } 
     194                gemini_wdt_service(gemini_wdt); 
     195        } 
     196 
     197        return len; 
     198} 
     199 
     200static const struct file_operations gemini_wdt_fops = { 
     201        .owner          = THIS_MODULE, 
     202        .llseek         = no_llseek, 
     203        .unlocked_ioctl = gemini_wdt_ioctl, 
     204        .open           = gemini_wdt_open, 
     205        .release        = gemini_wdt_close, 
     206        .write          = gemini_wdt_write, 
     207}; 
     208 
     209static struct miscdevice gemini_wdt_miscdev = { 
     210        .minor          = WATCHDOG_MINOR, 
     211        .name           = "watchdog", 
     212        .fops           = &gemini_wdt_fops, 
     213}; 
     214 
     215static void gemini_wdt_shutdown(struct platform_device *pdev) 
     216{ 
     217        struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev); 
     218 
     219        gemini_wdt_stop(gemini_wdt); 
     220} 
     221 
     222static int __devinit gemini_wdt_probe(struct platform_device *pdev) 
     223{ 
     224        int ret; 
     225        int res_size; 
     226        struct resource *res; 
     227        void __iomem *base; 
     228        struct gemini_wdt_struct *gemini_wdt; 
     229        unsigned int reg; 
     230 
     231        res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 
     232        if (!res) { 
     233                dev_err(&pdev->dev, "can't get device resources\n"); 
     234                return -ENODEV; 
     235        } 
     236 
     237        res_size = resource_size(res); 
     238        if (!request_mem_region(res->start, res_size, res->name)) { 
     239                dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", 
     240                        res_size, res->start); 
     241                return -ENOMEM; 
     242        } 
     243 
     244        base = ioremap(res->start, res_size); 
     245        if (!base) { 
     246                dev_err(&pdev->dev, "ioremap failed\n"); 
     247                ret = -EIO; 
     248                goto fail0; 
     249        } 
     250 
     251        gemini_wdt = kzalloc(sizeof(struct gemini_wdt_struct), GFP_KERNEL); 
     252        if (!gemini_wdt) { 
     253                dev_err(&pdev->dev, "can't allocate interface\n"); 
     254                ret = -ENOMEM; 
     255                goto fail1; 
     256        } 
     257 
     258        /* Setup gemini_wdt driver structure */ 
     259        gemini_wdt->base = base; 
     260        gemini_wdt->res = res; 
     261 
     262        /* Set up platform driver data */ 
     263        platform_set_drvdata(pdev, gemini_wdt); 
     264        gemini_wdt_dev = pdev; 
     265 
     266        if (gemini_wdt_miscdev.parent) { 
     267                ret = -EBUSY; 
     268                goto fail2; 
     269        } 
     270 
     271        gemini_wdt_miscdev.parent = &pdev->dev; 
     272 
     273        reg = __raw_readw(gemini_wdt->base + GEMINI_WDCR); 
     274        if (reg & WDCR_ENABLE) { 
     275                /* Watchdog was enabled by the bootloader, disable it. */ 
     276                reg &= ~(WDCR_ENABLE); 
     277                __raw_writel(reg, gemini_wdt->base + GEMINI_WDCR); 
     278        } 
     279 
     280        ret = misc_register(&gemini_wdt_miscdev); 
     281        if (ret) 
     282                goto fail2; 
     283 
     284        return 0; 
     285 
     286fail2: 
     287        platform_set_drvdata(pdev, NULL); 
     288        kfree(gemini_wdt); 
     289fail1: 
     290        iounmap(base); 
     291fail0: 
     292        release_mem_region(res->start, res_size); 
     293 
     294        return ret; 
     295} 
     296 
     297static int __devexit gemini_wdt_remove(struct platform_device *pdev) 
     298{ 
     299        struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev); 
     300 
     301        platform_set_drvdata(pdev, NULL); 
     302        misc_deregister(&gemini_wdt_miscdev); 
     303        gemini_wdt_dev = NULL; 
     304        iounmap(gemini_wdt->base); 
     305        release_mem_region(gemini_wdt->res->start, resource_size(gemini_wdt->res)); 
     306 
     307        kfree(gemini_wdt); 
     308 
     309        return 0; 
     310} 
     311 
     312#ifdef CONFIG_PM 
     313static int gemini_wdt_suspend(struct platform_device *pdev, pm_message_t message) 
     314{ 
     315        struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev); 
     316        unsigned int reg; 
     317 
     318        reg = __raw_readw(gemini_wdt->base + GEMINI_WDCR); 
     319        reg &= ~(WDCR_WDENABLE); 
     320        __raw_writel(reg, gemini_wdt->base + GEMINI_WDCR); 
     321 
     322        return 0; 
     323} 
     324 
     325static int gemini_wdt_resume(struct platform_device *pdev) 
     326{ 
     327        struct gemini_wdt_struct *gemini_wdt = platform_get_drvdata(pdev); 
     328        unsigned int reg; 
     329 
     330        if (gemini_wdt->status) { 
     331                reg = __raw_readw(gemini_wdt->base + GEMINI_WDCR); 
     332                reg |= WDCR_WDENABLE; 
     333                __raw_writel(reg, gemini_wdt->base + GEMINI_WDCR); 
     334        } 
     335 
     336        return 0; 
     337} 
     338#else 
     339#define gemini_wdt_suspend      NULL 
     340#define gemini_wdt_resume       NULL 
     341#endif 
     342 
     343static struct platform_driver gemini_wdt_driver = { 
     344        .probe          = gemini_wdt_probe, 
     345        .remove         = __devexit_p(gemini_wdt_remove), 
     346        .shutdown       = gemini_wdt_shutdown, 
     347        .suspend        = gemini_wdt_suspend, 
     348        .resume         = gemini_wdt_resume, 
     349        .driver         = { 
     350                .name   = "gemini-wdt", 
     351                .owner  = THIS_MODULE, 
     352        }, 
     353}; 
     354 
     355static int __init gemini_wdt_init(void) 
     356{ 
     357        return platform_driver_probe(&gemini_wdt_driver, gemini_wdt_probe); 
     358} 
     359 
     360static void __exit gemini_wdt_exit(void) 
     361{ 
     362        platform_driver_unregister(&gemini_wdt_driver); 
     363} 
     364 
     365module_init(gemini_wdt_init); 
     366module_exit(gemini_wdt_exit); 
     367 
     368module_param(timeout, uint, 0); 
     369MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds"); 
     370 
     371module_param(nowayout, int, 0); 
     372MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); 
     373 
     374MODULE_AUTHOR("Paulius Zaleckas"); 
     375MODULE_DESCRIPTION("Watchdog driver for Gemini"); 
     376MODULE_LICENSE("GPL"); 
     377MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 
     378MODULE_ALIAS("platform:gemini-wdt"); 
  • drivers/watchdog/Kconfig

    a b config 977_WATCHDOG 
    127127 
    128128          Not sure? It's safe to say N. 
    129129 
     130config GEMINI_WATCHDOG 
     131        tristate "Gemini watchdog" 
     132        depends on ARCH_GEMINI 
     133        help 
     134          Say Y here if to include support for the watchdog timer 
     135          embedded in the Cortina Systems Gemini family of devices. 
     136 
     137          To compile this driver as a module, choose M here: the 
     138          module will be called gemini_wdt. 
     139 
    130140config IXP2000_WATCHDOG 
    131141        tristate "IXP2000 Watchdog" 
    132142        depends on ARCH_IXP2000 
  • drivers/watchdog/Makefile

    a b obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt. 
    3636obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o 
    3737obj-$(CONFIG_21285_WATCHDOG) += wdt285.o 
    3838obj-$(CONFIG_977_WATCHDOG) += wdt977.o 
     39obj-$(CONFIG_GEMINI_WATCHDOG) += gemini_wdt.o 
    3940obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o 
    4041obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o 
    4142obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o 
Note: See TracBrowser for help on using the repository browser.