1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * WDT driver for Lenovo SE30G2 & SE60. 4 */ 5 6 #include <linux/delay.h> 7 #include <linux/dmi.h> 8 #include <linux/io.h> 9 #include <linux/module.h> 10 #include <linux/moduleparam.h> 11 #include <linux/platform_device.h> 12 #include <linux/string.h> 13 #include <linux/types.h> 14 #include <linux/watchdog.h> 15 16 #define CFG_PORT 0x2E 17 #define CFG_SIZE 2 18 19 #define CFG_LDN 0x07 20 #define CFG_BRAM_LDN 0x10 21 22 #define BRAM_SIZE 2 23 24 #define BRAM_WDT_REG 0x48 25 26 #define DRVNAME "lenovo-se30g2-se60-wdt" 27 28 /*The timeout range is 1-255 seconds*/ 29 #define MIN_TIMEOUT 1 30 #define MAX_TIMEOUT 255 31 #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ 32 33 static unsigned short bram_base; 34 static struct platform_device *se_30g2_60_pdev; 35 36 static int timeout; /* in seconds */ 37 module_param(timeout, int, 0); 38 MODULE_PARM_DESC(timeout, 39 "Watchdog timeout in seconds. 1 <= timeout <= 255, default=" 40 __MODULE_STRING(WATCHDOG_TIMEOUT) "."); 41 42 static bool nowayout = WATCHDOG_NOWAYOUT; 43 module_param(nowayout, bool, 0); 44 MODULE_PARM_DESC(nowayout, 45 "Watchdog cannot be stopped once started (default=" 46 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 47 48 struct se_30g2_60_wdt { 49 struct watchdog_device wdd; 50 }; 51 52 static void enter_pnp_mode(void) 53 { 54 outb(0x87, CFG_PORT); 55 outb(0x01, CFG_PORT); 56 outb(0x55, CFG_PORT); 57 outb(0x55, CFG_PORT); 58 } 59 60 static void exit_pnp_mode(void) 61 { 62 outb(0x2, CFG_PORT); 63 outb(0x2, CFG_PORT + 1); 64 } 65 66 static void lpc_write(unsigned char index, unsigned char data) 67 { 68 outb(index, CFG_PORT); 69 outb(data, CFG_PORT + 1); 70 } 71 72 static unsigned char lpc_read(unsigned char index) 73 { 74 outb(index, CFG_PORT); 75 return inb(CFG_PORT + 1); 76 } 77 78 static unsigned short lpc_chip_id(void) 79 { 80 unsigned char msb, lsb; 81 82 msb = lpc_read(0x20); 83 lsb = lpc_read(0x21); 84 85 return (msb << 8 | lsb); 86 } 87 88 static void bram_write(unsigned char reg, unsigned char val) 89 { 90 outb(reg | 0x80, bram_base); 91 outb(val, bram_base + 1); 92 } 93 94 static unsigned char bram_read(unsigned char reg) 95 { 96 unsigned char val; 97 98 outb(reg | 0x80, bram_base); 99 val = inb(bram_base + 1); 100 return val; 101 } 102 103 static int wdt_read(unsigned short *val) 104 { 105 if (!request_muxed_region(bram_base, BRAM_SIZE, DRVNAME)) 106 return -EACCES; 107 108 *val = ((bram_read(BRAM_WDT_REG) & 0xFF) << 8); 109 *val |= bram_read(BRAM_WDT_REG + 1) & 0xFF; 110 release_region(bram_base, BRAM_SIZE); 111 112 return 0; 113 } 114 115 static int wdt_write(unsigned short val) 116 { 117 if (!request_muxed_region(bram_base, BRAM_SIZE, DRVNAME)) 118 return -EACCES; 119 120 bram_write(BRAM_WDT_REG, (val >> 8) & 0xFF); 121 bram_write(BRAM_WDT_REG + 1, val & 0xFF); 122 release_region(bram_base, BRAM_SIZE); 123 124 return 0; 125 } 126 127 static int wdt_start(struct watchdog_device *wdog) 128 { 129 return wdt_write(wdog->timeout); 130 } 131 132 static int wdt_stop(struct watchdog_device *wdog) 133 { 134 return wdt_write(0); 135 } 136 137 static int wdt_ping(struct watchdog_device *wdog) 138 { 139 return wdt_write(wdog->timeout); 140 } 141 142 static unsigned int wdt_get_timeleft(struct watchdog_device *wdog) 143 { 144 unsigned short val; 145 int err; 146 147 err = wdt_read(&val); 148 return err ? 0 : val; 149 } 150 151 static const struct watchdog_info wdt_info = { 152 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 153 .identity = "Lenovo SE30G2 SE60 WDOG", 154 }; 155 156 static const struct watchdog_ops se_30g2_60_wdt_ops = { 157 .owner = THIS_MODULE, 158 .start = wdt_start, 159 .stop = wdt_stop, 160 .ping = wdt_ping, 161 .get_timeleft = wdt_get_timeleft, 162 }; 163 164 static int se_30g2_60_wdt_probe(struct platform_device *pdev) 165 { 166 struct device *dev = &pdev->dev; 167 struct se_30g2_60_wdt *priv; 168 unsigned int chip_id; 169 int ret; 170 171 if (!request_muxed_region(CFG_PORT, CFG_SIZE, DRVNAME)) 172 return -EBUSY; 173 174 /* identify the chip */ 175 enter_pnp_mode(); 176 chip_id = lpc_chip_id(); 177 if (chip_id != 0x5782) { 178 exit_pnp_mode(); 179 release_region(CFG_PORT, CFG_SIZE); 180 return -ENODEV; 181 } 182 183 /* probe the BRAM base address */ 184 lpc_write(CFG_LDN, CFG_BRAM_LDN); 185 bram_base = (lpc_read(0x60) << 8) | lpc_read(0x61); 186 exit_pnp_mode(); 187 release_region(CFG_PORT, CFG_SIZE); 188 dev_info(dev, "Found Lenovo SE30G2 SE60 0x%x\n", chip_id); 189 190 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 191 if (!priv) 192 return -ENOMEM; 193 194 watchdog_set_drvdata(&priv->wdd, priv); 195 196 priv->wdd.parent = dev; 197 priv->wdd.info = &wdt_info; 198 priv->wdd.ops = &se_30g2_60_wdt_ops; 199 priv->wdd.timeout = WATCHDOG_TIMEOUT; /* Set default timeout */ 200 priv->wdd.min_timeout = MIN_TIMEOUT; 201 priv->wdd.max_timeout = MAX_TIMEOUT; 202 203 watchdog_init_timeout(&priv->wdd, timeout, dev); 204 watchdog_set_nowayout(&priv->wdd, nowayout); 205 watchdog_stop_on_reboot(&priv->wdd); 206 watchdog_stop_on_unregister(&priv->wdd); 207 208 ret = devm_watchdog_register_device(dev, &priv->wdd); 209 210 dev_dbg(&pdev->dev, "initialized. timeout=%d sec (nowayout=%d)\n", 211 priv->wdd.timeout, nowayout); 212 213 return ret; 214 } 215 216 static struct platform_driver se_30g2_60_wdt_driver = { 217 .driver = { 218 .name = DRVNAME, 219 }, 220 .probe = se_30g2_60_wdt_probe, 221 }; 222 223 static int se_30g2_60_create_device(const struct dmi_system_id *id) 224 { 225 int err; 226 227 se_30g2_60_pdev = platform_device_alloc("lenovo-se30g2-se60-wdt", -1); 228 if (!se_30g2_60_pdev) 229 return -ENOMEM; 230 231 err = platform_device_add(se_30g2_60_pdev); 232 if (err) { 233 platform_device_put(se_30g2_60_pdev); 234 se_30g2_60_pdev = NULL; 235 } 236 237 return err; 238 } 239 240 static const struct dmi_system_id se_30g2_60[] __initconst = { 241 { 242 .ident = "LENOVO-SE30G2", 243 .matches = { 244 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 245 DMI_MATCH(DMI_BOARD_NAME, "33BD"), 246 }, 247 .callback = se_30g2_60_create_device, 248 }, 249 { 250 .ident = "LENOVO-SE60", 251 .matches = { 252 DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), 253 DMI_MATCH(DMI_BOARD_NAME, "33BF"), 254 }, 255 .callback = se_30g2_60_create_device, 256 }, 257 {} 258 }; 259 MODULE_DEVICE_TABLE(dmi, se_30g2_60); 260 261 static int __init se_30g2_60_wdt_init(void) 262 { 263 int err; 264 265 if (!dmi_check_system(se_30g2_60)) 266 return -ENODEV; 267 268 err = platform_driver_register(&se_30g2_60_wdt_driver); 269 if (err && se_30g2_60_pdev) { 270 platform_device_unregister(se_30g2_60_pdev); 271 se_30g2_60_pdev = NULL; 272 } 273 274 return err; 275 } 276 277 static void __exit se_30g2_60_wdt_exit(void) 278 { 279 if (se_30g2_60_pdev) 280 platform_device_unregister(se_30g2_60_pdev); 281 platform_driver_unregister(&se_30g2_60_wdt_driver); 282 } 283 284 module_init(se_30g2_60_wdt_init); 285 module_exit(se_30g2_60_wdt_exit); 286 287 MODULE_LICENSE("GPL"); 288 MODULE_AUTHOR("Mark Pearson <mpearson-lenovo@squebb.ca>"); 289 MODULE_DESCRIPTION("WDT driver for Lenovo SE30G2 & SE60"); 290