Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Watchdog driver for the MEN z069 IP-Core
0004  *
0005  * Copyright (C) 2018 Johannes Thumshirn <jth@kernel.org>
0006  */
0007 #include <linux/io.h>
0008 #include <linux/kernel.h>
0009 #include <linux/mcb.h>
0010 #include <linux/module.h>
0011 #include <linux/watchdog.h>
0012 
0013 struct men_z069_drv {
0014     struct watchdog_device wdt;
0015     void __iomem *base;
0016     struct resource *mem;
0017 };
0018 
0019 #define MEN_Z069_WTR            0x10
0020 #define MEN_Z069_WTR_WDEN       BIT(15)
0021 #define MEN_Z069_WTR_WDET_MASK      0x7fff
0022 #define MEN_Z069_WVR            0x14
0023 
0024 #define MEN_Z069_TIMER_FREQ     500 /* 500 Hz */
0025 #define MEN_Z069_WDT_COUNTER_MIN    1
0026 #define MEN_Z069_WDT_COUNTER_MAX    0x7fff
0027 #define MEN_Z069_DEFAULT_TIMEOUT    30
0028 
0029 static bool nowayout = WATCHDOG_NOWAYOUT;
0030 module_param(nowayout, bool, 0);
0031 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
0032                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
0033 
0034 static int men_z069_wdt_start(struct watchdog_device *wdt)
0035 {
0036     struct men_z069_drv *drv = watchdog_get_drvdata(wdt);
0037     u16 val;
0038 
0039     val = readw(drv->base + MEN_Z069_WTR);
0040     val |= MEN_Z069_WTR_WDEN;
0041     writew(val, drv->base + MEN_Z069_WTR);
0042 
0043     return 0;
0044 }
0045 
0046 static int men_z069_wdt_stop(struct watchdog_device *wdt)
0047 {
0048     struct men_z069_drv *drv = watchdog_get_drvdata(wdt);
0049     u16 val;
0050 
0051     val = readw(drv->base + MEN_Z069_WTR);
0052     val &= ~MEN_Z069_WTR_WDEN;
0053     writew(val, drv->base + MEN_Z069_WTR);
0054 
0055     return 0;
0056 }
0057 
0058 static int men_z069_wdt_ping(struct watchdog_device *wdt)
0059 {
0060     struct men_z069_drv *drv = watchdog_get_drvdata(wdt);
0061     u16 val;
0062 
0063     /* The watchdog trigger value toggles between 0x5555 and 0xaaaa */
0064     val = readw(drv->base + MEN_Z069_WVR);
0065     val ^= 0xffff;
0066     writew(val, drv->base + MEN_Z069_WVR);
0067 
0068     return 0;
0069 }
0070 
0071 static int men_z069_wdt_set_timeout(struct watchdog_device *wdt,
0072                     unsigned int timeout)
0073 {
0074     struct men_z069_drv *drv = watchdog_get_drvdata(wdt);
0075     u16 reg, val, ena;
0076 
0077     wdt->timeout = timeout;
0078     val = timeout * MEN_Z069_TIMER_FREQ;
0079 
0080     reg = readw(drv->base + MEN_Z069_WVR);
0081     ena = reg & MEN_Z069_WTR_WDEN;
0082     reg = ena | val;
0083     writew(reg, drv->base + MEN_Z069_WTR);
0084 
0085     return 0;
0086 }
0087 
0088 static const struct watchdog_info men_z069_info = {
0089     .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
0090     .identity = "MEN z069 Watchdog",
0091 };
0092 
0093 static const struct watchdog_ops men_z069_ops = {
0094     .owner = THIS_MODULE,
0095     .start = men_z069_wdt_start,
0096     .stop = men_z069_wdt_stop,
0097     .ping = men_z069_wdt_ping,
0098     .set_timeout = men_z069_wdt_set_timeout,
0099 };
0100 
0101 static struct watchdog_device men_z069_wdt = {
0102     .info = &men_z069_info,
0103     .ops = &men_z069_ops,
0104     .timeout = MEN_Z069_DEFAULT_TIMEOUT,
0105     .min_timeout = 1,
0106     .max_timeout = MEN_Z069_WDT_COUNTER_MAX / MEN_Z069_TIMER_FREQ,
0107 };
0108 
0109 static int men_z069_probe(struct mcb_device *dev,
0110               const struct mcb_device_id *id)
0111 {
0112     struct men_z069_drv *drv;
0113     struct resource *mem;
0114 
0115     drv = devm_kzalloc(&dev->dev, sizeof(struct men_z069_drv), GFP_KERNEL);
0116     if (!drv)
0117         return -ENOMEM;
0118 
0119     mem = mcb_request_mem(dev, "z069-wdt");
0120     if (IS_ERR(mem))
0121         return PTR_ERR(mem);
0122 
0123     drv->base = devm_ioremap(&dev->dev, mem->start, resource_size(mem));
0124     if (drv->base == NULL)
0125         goto release_mem;
0126 
0127     drv->mem = mem;
0128 
0129     drv->wdt = men_z069_wdt;
0130     watchdog_init_timeout(&drv->wdt, 0, &dev->dev);
0131     watchdog_set_nowayout(&drv->wdt, nowayout);
0132     watchdog_set_drvdata(&drv->wdt, drv);
0133     drv->wdt.parent = &dev->dev;
0134     mcb_set_drvdata(dev, drv);
0135 
0136     return watchdog_register_device(&men_z069_wdt);
0137 
0138 release_mem:
0139     mcb_release_mem(mem);
0140     return -ENOMEM;
0141 }
0142 
0143 static void men_z069_remove(struct mcb_device *dev)
0144 {
0145     struct men_z069_drv *drv = mcb_get_drvdata(dev);
0146 
0147     watchdog_unregister_device(&drv->wdt);
0148     mcb_release_mem(drv->mem);
0149 }
0150 
0151 static const struct mcb_device_id men_z069_ids[] = {
0152     { .device = 0x45 },
0153     { }
0154 };
0155 MODULE_DEVICE_TABLE(mcb, men_z069_ids);
0156 
0157 static struct mcb_driver men_z069_driver = {
0158     .driver = {
0159         .name = "z069-wdt",
0160         .owner = THIS_MODULE,
0161     },
0162     .probe = men_z069_probe,
0163     .remove = men_z069_remove,
0164     .id_table = men_z069_ids,
0165 };
0166 module_mcb_driver(men_z069_driver);
0167 
0168 MODULE_AUTHOR("Johannes Thumshirn <jth@kernel.org>");
0169 MODULE_LICENSE("GPL v2");
0170 MODULE_ALIAS("mcb:16z069");
0171 MODULE_IMPORT_NS(MCB);