0001
0002
0003
0004
0005
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
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
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);