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_WTR); 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 int men_z069_probe(struct mcb_device *dev, 102 const struct mcb_device_id *id) 103 { 104 struct men_z069_drv *drv; 105 struct resource *mem; 106 107 drv = devm_kzalloc(&dev->dev, sizeof(struct men_z069_drv), GFP_KERNEL); 108 if (!drv) 109 return -ENOMEM; 110 111 mem = mcb_request_mem(dev, "z069-wdt"); 112 if (IS_ERR(mem)) 113 return PTR_ERR(mem); 114 115 drv->base = devm_ioremap(&dev->dev, mem->start, resource_size(mem)); 116 if (drv->base == NULL) 117 goto release_mem; 118 119 drv->mem = mem; 120 drv->wdt.info = &men_z069_info; 121 drv->wdt.ops = &men_z069_ops; 122 drv->wdt.timeout = MEN_Z069_DEFAULT_TIMEOUT; 123 drv->wdt.min_timeout = 1; 124 drv->wdt.max_timeout = MEN_Z069_WDT_COUNTER_MAX / MEN_Z069_TIMER_FREQ; 125 126 watchdog_init_timeout(&drv->wdt, 0, &dev->dev); 127 watchdog_set_nowayout(&drv->wdt, nowayout); 128 watchdog_set_drvdata(&drv->wdt, drv); 129 drv->wdt.parent = &dev->dev; 130 mcb_set_drvdata(dev, drv); 131 132 return watchdog_register_device(&drv->wdt); 133 134 release_mem: 135 mcb_release_mem(mem); 136 return -ENOMEM; 137 } 138 139 static void men_z069_remove(struct mcb_device *dev) 140 { 141 struct men_z069_drv *drv = mcb_get_drvdata(dev); 142 143 watchdog_unregister_device(&drv->wdt); 144 mcb_release_mem(drv->mem); 145 } 146 147 static const struct mcb_device_id men_z069_ids[] = { 148 { .device = 0x45 }, 149 { } 150 }; 151 MODULE_DEVICE_TABLE(mcb, men_z069_ids); 152 153 static struct mcb_driver men_z069_driver = { 154 .driver = { 155 .name = "z069-wdt", 156 }, 157 .probe = men_z069_probe, 158 .remove = men_z069_remove, 159 .id_table = men_z069_ids, 160 }; 161 module_mcb_driver(men_z069_driver); 162 163 MODULE_AUTHOR("Johannes Thumshirn <jth@kernel.org>"); 164 MODULE_LICENSE("GPL v2"); 165 MODULE_ALIAS("mcb:16z069"); 166 MODULE_IMPORT_NS(MCB); 167