1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Watchdog driver for the MEN z069 IP-Core 4 * 5 * Copyright (C) 2018 Johannes Thumshirn <jth@kernel.org> 6 */ 7 #include <linux/io.h> 8 #include <linux/kernel.h> 9 #include <linux/mcb.h> 10 #include <linux/module.h> 11 #include <linux/watchdog.h> 12 13 struct men_z069_drv { 14 struct watchdog_device wdt; 15 void __iomem *base; 16 struct resource *mem; 17 }; 18 19 #define MEN_Z069_WTR 0x10 20 #define MEN_Z069_WTR_WDEN BIT(15) 21 #define MEN_Z069_WTR_WDET_MASK 0x7fff 22 #define MEN_Z069_WVR 0x14 23 24 #define MEN_Z069_TIMER_FREQ 500 /* 500 Hz */ 25 #define MEN_Z069_WDT_COUNTER_MIN 1 26 #define MEN_Z069_WDT_COUNTER_MAX 0x7fff 27 #define MEN_Z069_DEFAULT_TIMEOUT 30 28 29 static bool nowayout = WATCHDOG_NOWAYOUT; 30 module_param(nowayout, bool, 0); 31 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 32 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 33 34 static int men_z069_wdt_start(struct watchdog_device *wdt) 35 { 36 struct men_z069_drv *drv = watchdog_get_drvdata(wdt); 37 u16 val; 38 39 val = readw(drv->base + MEN_Z069_WTR); 40 val |= MEN_Z069_WTR_WDEN; 41 writew(val, drv->base + MEN_Z069_WTR); 42 43 return 0; 44 } 45 46 static int men_z069_wdt_stop(struct watchdog_device *wdt) 47 { 48 struct men_z069_drv *drv = watchdog_get_drvdata(wdt); 49 u16 val; 50 51 val = readw(drv->base + MEN_Z069_WTR); 52 val &= ~MEN_Z069_WTR_WDEN; 53 writew(val, drv->base + MEN_Z069_WTR); 54 55 return 0; 56 } 57 58 static int men_z069_wdt_ping(struct watchdog_device *wdt) 59 { 60 struct men_z069_drv *drv = watchdog_get_drvdata(wdt); 61 u16 val; 62 63 /* The watchdog trigger value toggles between 0x5555 and 0xaaaa */ 64 val = readw(drv->base + MEN_Z069_WVR); 65 val ^= 0xffff; 66 writew(val, drv->base + MEN_Z069_WVR); 67 68 return 0; 69 } 70 71 static int men_z069_wdt_set_timeout(struct watchdog_device *wdt, 72 unsigned int timeout) 73 { 74 struct men_z069_drv *drv = watchdog_get_drvdata(wdt); 75 u16 reg, val, ena; 76 77 wdt->timeout = timeout; 78 val = timeout * MEN_Z069_TIMER_FREQ; 79 80 reg = readw(drv->base + MEN_Z069_WVR); 81 ena = reg & MEN_Z069_WTR_WDEN; 82 reg = ena | val; 83 writew(reg, drv->base + MEN_Z069_WTR); 84 85 return 0; 86 } 87 88 static const struct watchdog_info men_z069_info = { 89 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 90 .identity = "MEN z069 Watchdog", 91 }; 92 93 static const struct watchdog_ops men_z069_ops = { 94 .owner = THIS_MODULE, 95 .start = men_z069_wdt_start, 96 .stop = men_z069_wdt_stop, 97 .ping = men_z069_wdt_ping, 98 .set_timeout = men_z069_wdt_set_timeout, 99 }; 100 101 static struct watchdog_device men_z069_wdt = { 102 .info = &men_z069_info, 103 .ops = &men_z069_ops, 104 .timeout = MEN_Z069_DEFAULT_TIMEOUT, 105 .min_timeout = 1, 106 .max_timeout = MEN_Z069_WDT_COUNTER_MAX / MEN_Z069_TIMER_FREQ, 107 }; 108 109 static int men_z069_probe(struct mcb_device *dev, 110 const struct mcb_device_id *id) 111 { 112 struct men_z069_drv *drv; 113 struct resource *mem; 114 115 drv = devm_kzalloc(&dev->dev, sizeof(struct men_z069_drv), GFP_KERNEL); 116 if (!drv) 117 return -ENOMEM; 118 119 mem = mcb_request_mem(dev, "z069-wdt"); 120 if (IS_ERR(mem)) 121 return PTR_ERR(mem); 122 123 drv->base = devm_ioremap(&dev->dev, mem->start, resource_size(mem)); 124 if (drv->base == NULL) 125 goto release_mem; 126 127 drv->mem = mem; 128 129 drv->wdt = men_z069_wdt; 130 watchdog_init_timeout(&drv->wdt, 0, &dev->dev); 131 watchdog_set_nowayout(&drv->wdt, nowayout); 132 watchdog_set_drvdata(&drv->wdt, drv); 133 drv->wdt.parent = &dev->dev; 134 mcb_set_drvdata(dev, drv); 135 136 return watchdog_register_device(&men_z069_wdt); 137 138 release_mem: 139 mcb_release_mem(mem); 140 return -ENOMEM; 141 } 142 143 static void men_z069_remove(struct mcb_device *dev) 144 { 145 struct men_z069_drv *drv = mcb_get_drvdata(dev); 146 147 watchdog_unregister_device(&drv->wdt); 148 mcb_release_mem(drv->mem); 149 } 150 151 static const struct mcb_device_id men_z069_ids[] = { 152 { .device = 0x45 }, 153 { } 154 }; 155 MODULE_DEVICE_TABLE(mcb, men_z069_ids); 156 157 static struct mcb_driver men_z069_driver = { 158 .driver = { 159 .name = "z069-wdt", 160 .owner = THIS_MODULE, 161 }, 162 .probe = men_z069_probe, 163 .remove = men_z069_remove, 164 .id_table = men_z069_ids, 165 }; 166 module_mcb_driver(men_z069_driver); 167 168 MODULE_AUTHOR("Johannes Thumshirn <jth@kernel.org>"); 169 MODULE_LICENSE("GPL v2"); 170 MODULE_ALIAS("mcb:16z069"); 171 MODULE_IMPORT_NS(MCB); 172