xref: /openbmc/linux/drivers/watchdog/mena21_wdt.c (revision 0898782247ae533d1f4e47a06bc5d4870931b284)
12e62c498SMarcus Folkesson // SPDX-License-Identifier: GPL-2.0+
226c57ef1SJohannes Thumshirn /*
326c57ef1SJohannes Thumshirn  * Watchdog driver for the A21 VME CPU Boards
426c57ef1SJohannes Thumshirn  *
526c57ef1SJohannes Thumshirn  * Copyright (C) 2013 MEN Mikro Elektronik Nuernberg GmbH
626c57ef1SJohannes Thumshirn  *
726c57ef1SJohannes Thumshirn  */
826c57ef1SJohannes Thumshirn #include <linux/module.h>
926c57ef1SJohannes Thumshirn #include <linux/moduleparam.h>
1026c57ef1SJohannes Thumshirn #include <linux/types.h>
1126c57ef1SJohannes Thumshirn #include <linux/kernel.h>
1226c57ef1SJohannes Thumshirn #include <linux/slab.h>
1326c57ef1SJohannes Thumshirn #include <linux/platform_device.h>
1426c57ef1SJohannes Thumshirn #include <linux/watchdog.h>
1526c57ef1SJohannes Thumshirn #include <linux/uaccess.h>
1622ec9bb1SLinus Walleij #include <linux/gpio/consumer.h>
1726c57ef1SJohannes Thumshirn #include <linux/delay.h>
1826c57ef1SJohannes Thumshirn #include <linux/bitops.h>
1922ec9bb1SLinus Walleij #include <linux/of.h>
2026c57ef1SJohannes Thumshirn 
2126c57ef1SJohannes Thumshirn #define NUM_GPIOS 6
2226c57ef1SJohannes Thumshirn 
2326c57ef1SJohannes Thumshirn enum a21_wdt_gpios {
2426c57ef1SJohannes Thumshirn 	GPIO_WD_ENAB,
2526c57ef1SJohannes Thumshirn 	GPIO_WD_FAST,
2626c57ef1SJohannes Thumshirn 	GPIO_WD_TRIG,
2726c57ef1SJohannes Thumshirn 	GPIO_WD_RST0,
2826c57ef1SJohannes Thumshirn 	GPIO_WD_RST1,
2926c57ef1SJohannes Thumshirn 	GPIO_WD_RST2,
3026c57ef1SJohannes Thumshirn };
3126c57ef1SJohannes Thumshirn 
3226c57ef1SJohannes Thumshirn struct a21_wdt_drv {
3326c57ef1SJohannes Thumshirn 	struct watchdog_device wdt;
3422ec9bb1SLinus Walleij 	struct gpio_desc *gpios[NUM_GPIOS];
3526c57ef1SJohannes Thumshirn };
3626c57ef1SJohannes Thumshirn 
3726c57ef1SJohannes Thumshirn static bool nowayout = WATCHDOG_NOWAYOUT;
3826c57ef1SJohannes Thumshirn module_param(nowayout, bool, 0);
3926c57ef1SJohannes Thumshirn MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
4026c57ef1SJohannes Thumshirn 			    __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
4126c57ef1SJohannes Thumshirn 
a21_wdt_get_bootstatus(struct a21_wdt_drv * drv)4226c57ef1SJohannes Thumshirn static unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv)
4326c57ef1SJohannes Thumshirn {
4426c57ef1SJohannes Thumshirn 	int reset = 0;
4526c57ef1SJohannes Thumshirn 
4622ec9bb1SLinus Walleij 	reset |= gpiod_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0;
4722ec9bb1SLinus Walleij 	reset |= gpiod_get_value(drv->gpios[GPIO_WD_RST1]) ? (1 << 1) : 0;
4822ec9bb1SLinus Walleij 	reset |= gpiod_get_value(drv->gpios[GPIO_WD_RST2]) ? (1 << 2) : 0;
4926c57ef1SJohannes Thumshirn 
5026c57ef1SJohannes Thumshirn 	return reset;
5126c57ef1SJohannes Thumshirn }
5226c57ef1SJohannes Thumshirn 
a21_wdt_start(struct watchdog_device * wdt)5326c57ef1SJohannes Thumshirn static int a21_wdt_start(struct watchdog_device *wdt)
5426c57ef1SJohannes Thumshirn {
5526c57ef1SJohannes Thumshirn 	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
5626c57ef1SJohannes Thumshirn 
5722ec9bb1SLinus Walleij 	gpiod_set_value(drv->gpios[GPIO_WD_ENAB], 1);
5826c57ef1SJohannes Thumshirn 
5926c57ef1SJohannes Thumshirn 	return 0;
6026c57ef1SJohannes Thumshirn }
6126c57ef1SJohannes Thumshirn 
a21_wdt_stop(struct watchdog_device * wdt)6226c57ef1SJohannes Thumshirn static int a21_wdt_stop(struct watchdog_device *wdt)
6326c57ef1SJohannes Thumshirn {
6426c57ef1SJohannes Thumshirn 	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
6526c57ef1SJohannes Thumshirn 
6622ec9bb1SLinus Walleij 	gpiod_set_value(drv->gpios[GPIO_WD_ENAB], 0);
6726c57ef1SJohannes Thumshirn 
6826c57ef1SJohannes Thumshirn 	return 0;
6926c57ef1SJohannes Thumshirn }
7026c57ef1SJohannes Thumshirn 
a21_wdt_ping(struct watchdog_device * wdt)7126c57ef1SJohannes Thumshirn static int a21_wdt_ping(struct watchdog_device *wdt)
7226c57ef1SJohannes Thumshirn {
7326c57ef1SJohannes Thumshirn 	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
7426c57ef1SJohannes Thumshirn 
7522ec9bb1SLinus Walleij 	gpiod_set_value(drv->gpios[GPIO_WD_TRIG], 0);
7626c57ef1SJohannes Thumshirn 	ndelay(10);
7722ec9bb1SLinus Walleij 	gpiod_set_value(drv->gpios[GPIO_WD_TRIG], 1);
7826c57ef1SJohannes Thumshirn 
7926c57ef1SJohannes Thumshirn 	return 0;
8026c57ef1SJohannes Thumshirn }
8126c57ef1SJohannes Thumshirn 
a21_wdt_set_timeout(struct watchdog_device * wdt,unsigned int timeout)8226c57ef1SJohannes Thumshirn static int a21_wdt_set_timeout(struct watchdog_device *wdt,
8326c57ef1SJohannes Thumshirn 			       unsigned int timeout)
8426c57ef1SJohannes Thumshirn {
8526c57ef1SJohannes Thumshirn 	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
8626c57ef1SJohannes Thumshirn 
8726c57ef1SJohannes Thumshirn 	if (timeout != 1 && timeout != 30) {
8807352366SGuenter Roeck 		dev_err(wdt->parent, "Only 1 and 30 allowed as timeout\n");
8926c57ef1SJohannes Thumshirn 		return -EINVAL;
9026c57ef1SJohannes Thumshirn 	}
9126c57ef1SJohannes Thumshirn 
9226c57ef1SJohannes Thumshirn 	if (timeout == 30 && wdt->timeout == 1) {
9307352366SGuenter Roeck 		dev_err(wdt->parent,
9426c57ef1SJohannes Thumshirn 			"Transition from fast to slow mode not allowed\n");
9526c57ef1SJohannes Thumshirn 		return -EINVAL;
9626c57ef1SJohannes Thumshirn 	}
9726c57ef1SJohannes Thumshirn 
9826c57ef1SJohannes Thumshirn 	if (timeout == 1)
9922ec9bb1SLinus Walleij 		gpiod_set_value(drv->gpios[GPIO_WD_FAST], 1);
10026c57ef1SJohannes Thumshirn 	else
10122ec9bb1SLinus Walleij 		gpiod_set_value(drv->gpios[GPIO_WD_FAST], 0);
10226c57ef1SJohannes Thumshirn 
10326c57ef1SJohannes Thumshirn 	wdt->timeout = timeout;
10426c57ef1SJohannes Thumshirn 
10526c57ef1SJohannes Thumshirn 	return 0;
10626c57ef1SJohannes Thumshirn }
10726c57ef1SJohannes Thumshirn 
10826c57ef1SJohannes Thumshirn static const struct watchdog_info a21_wdt_info = {
10926c57ef1SJohannes Thumshirn 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
11026c57ef1SJohannes Thumshirn 	.identity = "MEN A21 Watchdog",
11126c57ef1SJohannes Thumshirn };
11226c57ef1SJohannes Thumshirn 
11326c57ef1SJohannes Thumshirn static const struct watchdog_ops a21_wdt_ops = {
11426c57ef1SJohannes Thumshirn 	.owner = THIS_MODULE,
11526c57ef1SJohannes Thumshirn 	.start = a21_wdt_start,
11626c57ef1SJohannes Thumshirn 	.stop = a21_wdt_stop,
11726c57ef1SJohannes Thumshirn 	.ping = a21_wdt_ping,
11826c57ef1SJohannes Thumshirn 	.set_timeout = a21_wdt_set_timeout,
11926c57ef1SJohannes Thumshirn };
12026c57ef1SJohannes Thumshirn 
12126c57ef1SJohannes Thumshirn static struct watchdog_device a21_wdt = {
12226c57ef1SJohannes Thumshirn 	.info = &a21_wdt_info,
12326c57ef1SJohannes Thumshirn 	.ops = &a21_wdt_ops,
12426c57ef1SJohannes Thumshirn 	.min_timeout = 1,
12526c57ef1SJohannes Thumshirn 	.max_timeout = 30,
12626c57ef1SJohannes Thumshirn };
12726c57ef1SJohannes Thumshirn 
a21_wdt_probe(struct platform_device * pdev)12826c57ef1SJohannes Thumshirn static int a21_wdt_probe(struct platform_device *pdev)
12926c57ef1SJohannes Thumshirn {
13094ac20d8SGuenter Roeck 	struct device *dev = &pdev->dev;
13126c57ef1SJohannes Thumshirn 	struct a21_wdt_drv *drv;
13226c57ef1SJohannes Thumshirn 	unsigned int reset = 0;
13326c57ef1SJohannes Thumshirn 	int num_gpios;
13426c57ef1SJohannes Thumshirn 	int ret;
13526c57ef1SJohannes Thumshirn 	int i;
13626c57ef1SJohannes Thumshirn 
13794ac20d8SGuenter Roeck 	drv = devm_kzalloc(dev, sizeof(struct a21_wdt_drv), GFP_KERNEL);
13826c57ef1SJohannes Thumshirn 	if (!drv)
13926c57ef1SJohannes Thumshirn 		return -ENOMEM;
14026c57ef1SJohannes Thumshirn 
14194ac20d8SGuenter Roeck 	num_gpios = gpiod_count(dev, NULL);
14226c57ef1SJohannes Thumshirn 	if (num_gpios != NUM_GPIOS) {
14394ac20d8SGuenter Roeck 		dev_err(dev, "gpios DT property wrong, got %d want %d",
14426c57ef1SJohannes Thumshirn 			num_gpios, NUM_GPIOS);
14526c57ef1SJohannes Thumshirn 		return -ENODEV;
14626c57ef1SJohannes Thumshirn 	}
14726c57ef1SJohannes Thumshirn 
14826c57ef1SJohannes Thumshirn 	/* Request the used GPIOs */
14926c57ef1SJohannes Thumshirn 	for (i = 0; i < num_gpios; i++) {
15022ec9bb1SLinus Walleij 		enum gpiod_flags gflags;
15126c57ef1SJohannes Thumshirn 
15226c57ef1SJohannes Thumshirn 		if (i < GPIO_WD_RST0)
15322ec9bb1SLinus Walleij 			gflags = GPIOD_ASIS;
15422ec9bb1SLinus Walleij 		else
15522ec9bb1SLinus Walleij 			gflags = GPIOD_IN;
15694ac20d8SGuenter Roeck 		drv->gpios[i] = devm_gpiod_get_index(dev, NULL, i, gflags);
15794ac20d8SGuenter Roeck 		if (IS_ERR(drv->gpios[i]))
15894ac20d8SGuenter Roeck 			return PTR_ERR(drv->gpios[i]);
15926c57ef1SJohannes Thumshirn 
16022ec9bb1SLinus Walleij 		gpiod_set_consumer_name(drv->gpios[i], "MEN A21 Watchdog");
16122ec9bb1SLinus Walleij 
16222ec9bb1SLinus Walleij 		/*
16322ec9bb1SLinus Walleij 		 * Retrieve the initial value from the GPIOs that should be
16422ec9bb1SLinus Walleij 		 * output, then set up the line as output with that value.
16522ec9bb1SLinus Walleij 		 */
16622ec9bb1SLinus Walleij 		if (i < GPIO_WD_RST0) {
16722ec9bb1SLinus Walleij 			int val;
16822ec9bb1SLinus Walleij 
16922ec9bb1SLinus Walleij 			val = gpiod_get_value(drv->gpios[i]);
17022ec9bb1SLinus Walleij 			gpiod_direction_output(drv->gpios[i], val);
17122ec9bb1SLinus Walleij 		}
17222ec9bb1SLinus Walleij 	}
17322ec9bb1SLinus Walleij 
17494ac20d8SGuenter Roeck 	watchdog_init_timeout(&a21_wdt, 30, dev);
17526c57ef1SJohannes Thumshirn 	watchdog_set_nowayout(&a21_wdt, nowayout);
17626c57ef1SJohannes Thumshirn 	watchdog_set_drvdata(&a21_wdt, drv);
17794ac20d8SGuenter Roeck 	a21_wdt.parent = dev;
17826c57ef1SJohannes Thumshirn 
17926c57ef1SJohannes Thumshirn 	reset = a21_wdt_get_bootstatus(drv);
18026c57ef1SJohannes Thumshirn 	if (reset == 2)
18126c57ef1SJohannes Thumshirn 		a21_wdt.bootstatus |= WDIOF_EXTERN1;
18226c57ef1SJohannes Thumshirn 	else if (reset == 4)
18326c57ef1SJohannes Thumshirn 		a21_wdt.bootstatus |= WDIOF_CARDRESET;
18426c57ef1SJohannes Thumshirn 	else if (reset == 5)
18526c57ef1SJohannes Thumshirn 		a21_wdt.bootstatus |= WDIOF_POWERUNDER;
18626c57ef1SJohannes Thumshirn 	else if (reset == 7)
18726c57ef1SJohannes Thumshirn 		a21_wdt.bootstatus |= WDIOF_EXTERN2;
18826c57ef1SJohannes Thumshirn 
18957337db1SJohannes Thumshirn 	drv->wdt = a21_wdt;
19094ac20d8SGuenter Roeck 	dev_set_drvdata(dev, drv);
19157337db1SJohannes Thumshirn 
19294ac20d8SGuenter Roeck 	ret = devm_watchdog_register_device(dev, &a21_wdt);
193*eddeb07bSWolfram Sang 	if (ret)
19426c57ef1SJohannes Thumshirn 		return ret;
19526c57ef1SJohannes Thumshirn 
19694ac20d8SGuenter Roeck 	dev_info(dev, "MEN A21 watchdog timer driver enabled\n");
19726c57ef1SJohannes Thumshirn 
19826c57ef1SJohannes Thumshirn 	return 0;
19926c57ef1SJohannes Thumshirn }
20026c57ef1SJohannes Thumshirn 
a21_wdt_shutdown(struct platform_device * pdev)20126c57ef1SJohannes Thumshirn static void a21_wdt_shutdown(struct platform_device *pdev)
20226c57ef1SJohannes Thumshirn {
20326c57ef1SJohannes Thumshirn 	struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
20426c57ef1SJohannes Thumshirn 
20522ec9bb1SLinus Walleij 	gpiod_set_value(drv->gpios[GPIO_WD_ENAB], 0);
20626c57ef1SJohannes Thumshirn }
20726c57ef1SJohannes Thumshirn 
20826c57ef1SJohannes Thumshirn static const struct of_device_id a21_wdt_ids[] = {
20926c57ef1SJohannes Thumshirn 	{ .compatible = "men,a021-wdt" },
21026c57ef1SJohannes Thumshirn 	{ },
21126c57ef1SJohannes Thumshirn };
212c73318f4SLuis de Bethencourt MODULE_DEVICE_TABLE(of, a21_wdt_ids);
21326c57ef1SJohannes Thumshirn 
21426c57ef1SJohannes Thumshirn static struct platform_driver a21_wdt_driver = {
21526c57ef1SJohannes Thumshirn 	.probe = a21_wdt_probe,
21626c57ef1SJohannes Thumshirn 	.shutdown = a21_wdt_shutdown,
21726c57ef1SJohannes Thumshirn 	.driver = {
21826c57ef1SJohannes Thumshirn 		.name = "a21-watchdog",
21926c57ef1SJohannes Thumshirn 		.of_match_table = a21_wdt_ids,
22026c57ef1SJohannes Thumshirn 	},
22126c57ef1SJohannes Thumshirn };
22226c57ef1SJohannes Thumshirn 
22326c57ef1SJohannes Thumshirn module_platform_driver(a21_wdt_driver);
22426c57ef1SJohannes Thumshirn 
22526c57ef1SJohannes Thumshirn MODULE_AUTHOR("MEN Mikro Elektronik");
22626c57ef1SJohannes Thumshirn MODULE_DESCRIPTION("MEN A21 Watchdog");
22726c57ef1SJohannes Thumshirn MODULE_LICENSE("GPL");
22826c57ef1SJohannes Thumshirn MODULE_ALIAS("platform:a21-watchdog");
229