xref: /openbmc/linux/drivers/watchdog/menz69_wdt.c (revision 130c3f7e)
181ceed41SJohannes Thumshirn // SPDX-License-Identifier: GPL-2.0
281ceed41SJohannes Thumshirn /*
381ceed41SJohannes Thumshirn  * Watchdog driver for the MEN z069 IP-Core
481ceed41SJohannes Thumshirn  *
581ceed41SJohannes Thumshirn  * Copyright (C) 2018 Johannes Thumshirn <jth@kernel.org>
681ceed41SJohannes Thumshirn  */
781ceed41SJohannes Thumshirn #include <linux/io.h>
881ceed41SJohannes Thumshirn #include <linux/kernel.h>
981ceed41SJohannes Thumshirn #include <linux/mcb.h>
1081ceed41SJohannes Thumshirn #include <linux/module.h>
1181ceed41SJohannes Thumshirn #include <linux/watchdog.h>
1281ceed41SJohannes Thumshirn 
1381ceed41SJohannes Thumshirn struct men_z069_drv {
1481ceed41SJohannes Thumshirn 	struct watchdog_device wdt;
1581ceed41SJohannes Thumshirn 	void __iomem *base;
1681ceed41SJohannes Thumshirn 	struct resource *mem;
1781ceed41SJohannes Thumshirn };
1881ceed41SJohannes Thumshirn 
1981ceed41SJohannes Thumshirn #define MEN_Z069_WTR			0x10
2081ceed41SJohannes Thumshirn #define MEN_Z069_WTR_WDEN		BIT(15)
2181ceed41SJohannes Thumshirn #define MEN_Z069_WTR_WDET_MASK		0x7fff
2281ceed41SJohannes Thumshirn #define MEN_Z069_WVR			0x14
2381ceed41SJohannes Thumshirn 
2481ceed41SJohannes Thumshirn #define MEN_Z069_TIMER_FREQ		500 /* 500 Hz */
2581ceed41SJohannes Thumshirn #define MEN_Z069_WDT_COUNTER_MIN	1
2681ceed41SJohannes Thumshirn #define MEN_Z069_WDT_COUNTER_MAX	0x7fff
2781ceed41SJohannes Thumshirn #define MEN_Z069_DEFAULT_TIMEOUT	30
2881ceed41SJohannes Thumshirn 
2981ceed41SJohannes Thumshirn static bool nowayout = WATCHDOG_NOWAYOUT;
3081ceed41SJohannes Thumshirn module_param(nowayout, bool, 0);
3181ceed41SJohannes Thumshirn MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
3281ceed41SJohannes Thumshirn 			    __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
3381ceed41SJohannes Thumshirn 
men_z069_wdt_start(struct watchdog_device * wdt)3481ceed41SJohannes Thumshirn static int men_z069_wdt_start(struct watchdog_device *wdt)
3581ceed41SJohannes Thumshirn {
3681ceed41SJohannes Thumshirn 	struct men_z069_drv *drv = watchdog_get_drvdata(wdt);
3781ceed41SJohannes Thumshirn 	u16 val;
3881ceed41SJohannes Thumshirn 
3981ceed41SJohannes Thumshirn 	val = readw(drv->base + MEN_Z069_WTR);
4081ceed41SJohannes Thumshirn 	val |= MEN_Z069_WTR_WDEN;
4181ceed41SJohannes Thumshirn 	writew(val, drv->base + MEN_Z069_WTR);
4281ceed41SJohannes Thumshirn 
4381ceed41SJohannes Thumshirn 	return 0;
4481ceed41SJohannes Thumshirn }
4581ceed41SJohannes Thumshirn 
men_z069_wdt_stop(struct watchdog_device * wdt)4681ceed41SJohannes Thumshirn static int men_z069_wdt_stop(struct watchdog_device *wdt)
4781ceed41SJohannes Thumshirn {
4881ceed41SJohannes Thumshirn 	struct men_z069_drv *drv = watchdog_get_drvdata(wdt);
4981ceed41SJohannes Thumshirn 	u16 val;
5081ceed41SJohannes Thumshirn 
5181ceed41SJohannes Thumshirn 	val = readw(drv->base + MEN_Z069_WTR);
5281ceed41SJohannes Thumshirn 	val &= ~MEN_Z069_WTR_WDEN;
5381ceed41SJohannes Thumshirn 	writew(val, drv->base + MEN_Z069_WTR);
5481ceed41SJohannes Thumshirn 
5581ceed41SJohannes Thumshirn 	return 0;
5681ceed41SJohannes Thumshirn }
5781ceed41SJohannes Thumshirn 
men_z069_wdt_ping(struct watchdog_device * wdt)5881ceed41SJohannes Thumshirn static int men_z069_wdt_ping(struct watchdog_device *wdt)
5981ceed41SJohannes Thumshirn {
6081ceed41SJohannes Thumshirn 	struct men_z069_drv *drv = watchdog_get_drvdata(wdt);
6181ceed41SJohannes Thumshirn 	u16 val;
6281ceed41SJohannes Thumshirn 
6381ceed41SJohannes Thumshirn 	/* The watchdog trigger value toggles between 0x5555 and 0xaaaa */
6481ceed41SJohannes Thumshirn 	val = readw(drv->base + MEN_Z069_WVR);
6581ceed41SJohannes Thumshirn 	val ^= 0xffff;
6681ceed41SJohannes Thumshirn 	writew(val, drv->base + MEN_Z069_WVR);
6781ceed41SJohannes Thumshirn 
6881ceed41SJohannes Thumshirn 	return 0;
6981ceed41SJohannes Thumshirn }
7081ceed41SJohannes Thumshirn 
men_z069_wdt_set_timeout(struct watchdog_device * wdt,unsigned int timeout)7181ceed41SJohannes Thumshirn static int men_z069_wdt_set_timeout(struct watchdog_device *wdt,
7281ceed41SJohannes Thumshirn 				    unsigned int timeout)
7381ceed41SJohannes Thumshirn {
7481ceed41SJohannes Thumshirn 	struct men_z069_drv *drv = watchdog_get_drvdata(wdt);
7581ceed41SJohannes Thumshirn 	u16 reg, val, ena;
7681ceed41SJohannes Thumshirn 
7781ceed41SJohannes Thumshirn 	wdt->timeout = timeout;
7881ceed41SJohannes Thumshirn 	val = timeout * MEN_Z069_TIMER_FREQ;
7981ceed41SJohannes Thumshirn 
80*bd858e49SJohannes Thumshirn 	reg = readw(drv->base + MEN_Z069_WTR);
8181ceed41SJohannes Thumshirn 	ena = reg & MEN_Z069_WTR_WDEN;
8281ceed41SJohannes Thumshirn 	reg = ena | val;
8381ceed41SJohannes Thumshirn 	writew(reg, drv->base + MEN_Z069_WTR);
8481ceed41SJohannes Thumshirn 
8581ceed41SJohannes Thumshirn 	return 0;
8681ceed41SJohannes Thumshirn }
8781ceed41SJohannes Thumshirn 
8881ceed41SJohannes Thumshirn static const struct watchdog_info men_z069_info = {
8981ceed41SJohannes Thumshirn 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
9081ceed41SJohannes Thumshirn 	.identity = "MEN z069 Watchdog",
9181ceed41SJohannes Thumshirn };
9281ceed41SJohannes Thumshirn 
9381ceed41SJohannes Thumshirn static const struct watchdog_ops men_z069_ops = {
9481ceed41SJohannes Thumshirn 	.owner = THIS_MODULE,
9581ceed41SJohannes Thumshirn 	.start = men_z069_wdt_start,
9681ceed41SJohannes Thumshirn 	.stop = men_z069_wdt_stop,
9781ceed41SJohannes Thumshirn 	.ping = men_z069_wdt_ping,
9881ceed41SJohannes Thumshirn 	.set_timeout = men_z069_wdt_set_timeout,
9981ceed41SJohannes Thumshirn };
10081ceed41SJohannes Thumshirn 
men_z069_probe(struct mcb_device * dev,const struct mcb_device_id * id)10181ceed41SJohannes Thumshirn static int men_z069_probe(struct mcb_device *dev,
10281ceed41SJohannes Thumshirn 			  const struct mcb_device_id *id)
10381ceed41SJohannes Thumshirn {
10481ceed41SJohannes Thumshirn 	struct men_z069_drv *drv;
10581ceed41SJohannes Thumshirn 	struct resource *mem;
10681ceed41SJohannes Thumshirn 
10781ceed41SJohannes Thumshirn 	drv = devm_kzalloc(&dev->dev, sizeof(struct men_z069_drv), GFP_KERNEL);
10881ceed41SJohannes Thumshirn 	if (!drv)
10981ceed41SJohannes Thumshirn 		return -ENOMEM;
11081ceed41SJohannes Thumshirn 
11181ceed41SJohannes Thumshirn 	mem = mcb_request_mem(dev, "z069-wdt");
11281ceed41SJohannes Thumshirn 	if (IS_ERR(mem))
11381ceed41SJohannes Thumshirn 		return PTR_ERR(mem);
11481ceed41SJohannes Thumshirn 
11581ceed41SJohannes Thumshirn 	drv->base = devm_ioremap(&dev->dev, mem->start, resource_size(mem));
11681ceed41SJohannes Thumshirn 	if (drv->base == NULL)
11781ceed41SJohannes Thumshirn 		goto release_mem;
11881ceed41SJohannes Thumshirn 
11981ceed41SJohannes Thumshirn 	drv->mem = mem;
12087b22656SJohannes Thumshirn 	drv->wdt.info = &men_z069_info;
12187b22656SJohannes Thumshirn 	drv->wdt.ops = &men_z069_ops;
12287b22656SJohannes Thumshirn 	drv->wdt.timeout = MEN_Z069_DEFAULT_TIMEOUT;
12387b22656SJohannes Thumshirn 	drv->wdt.min_timeout = 1;
12487b22656SJohannes Thumshirn 	drv->wdt.max_timeout = MEN_Z069_WDT_COUNTER_MAX / MEN_Z069_TIMER_FREQ;
12581ceed41SJohannes Thumshirn 
12681ceed41SJohannes Thumshirn 	watchdog_init_timeout(&drv->wdt, 0, &dev->dev);
12781ceed41SJohannes Thumshirn 	watchdog_set_nowayout(&drv->wdt, nowayout);
12881ceed41SJohannes Thumshirn 	watchdog_set_drvdata(&drv->wdt, drv);
12981ceed41SJohannes Thumshirn 	drv->wdt.parent = &dev->dev;
13081ceed41SJohannes Thumshirn 	mcb_set_drvdata(dev, drv);
13181ceed41SJohannes Thumshirn 
13287b22656SJohannes Thumshirn 	return watchdog_register_device(&drv->wdt);
13381ceed41SJohannes Thumshirn 
13481ceed41SJohannes Thumshirn release_mem:
13581ceed41SJohannes Thumshirn 	mcb_release_mem(mem);
13681ceed41SJohannes Thumshirn 	return -ENOMEM;
13781ceed41SJohannes Thumshirn }
13881ceed41SJohannes Thumshirn 
men_z069_remove(struct mcb_device * dev)13981ceed41SJohannes Thumshirn static void men_z069_remove(struct mcb_device *dev)
14081ceed41SJohannes Thumshirn {
14181ceed41SJohannes Thumshirn 	struct men_z069_drv *drv = mcb_get_drvdata(dev);
14281ceed41SJohannes Thumshirn 
14381ceed41SJohannes Thumshirn 	watchdog_unregister_device(&drv->wdt);
14481ceed41SJohannes Thumshirn 	mcb_release_mem(drv->mem);
14581ceed41SJohannes Thumshirn }
14681ceed41SJohannes Thumshirn 
14781ceed41SJohannes Thumshirn static const struct mcb_device_id men_z069_ids[] = {
14881ceed41SJohannes Thumshirn 	{ .device = 0x45 },
14981ceed41SJohannes Thumshirn 	{ }
15081ceed41SJohannes Thumshirn };
15181ceed41SJohannes Thumshirn MODULE_DEVICE_TABLE(mcb, men_z069_ids);
15281ceed41SJohannes Thumshirn 
15381ceed41SJohannes Thumshirn static struct mcb_driver men_z069_driver = {
15481ceed41SJohannes Thumshirn 	.driver = {
15581ceed41SJohannes Thumshirn 		.name = "z069-wdt",
15681ceed41SJohannes Thumshirn 	},
15781ceed41SJohannes Thumshirn 	.probe = men_z069_probe,
15881ceed41SJohannes Thumshirn 	.remove = men_z069_remove,
15981ceed41SJohannes Thumshirn 	.id_table = men_z069_ids,
16081ceed41SJohannes Thumshirn };
16181ceed41SJohannes Thumshirn module_mcb_driver(men_z069_driver);
16281ceed41SJohannes Thumshirn 
16381ceed41SJohannes Thumshirn MODULE_AUTHOR("Johannes Thumshirn <jth@kernel.org>");
16481ceed41SJohannes Thumshirn MODULE_LICENSE("GPL v2");
16581ceed41SJohannes Thumshirn MODULE_ALIAS("mcb:16z069");
166891e6036SJohannes Thumshirn MODULE_IMPORT_NS(MCB);
167