126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn/*
226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn * Watchdog driver for the A21 VME CPU Boards
326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn *
426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn * Copyright (C) 2013 MEN Mikro Elektronik Nuernberg GmbH
526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn *
626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn * This program is free software; you can redistribute it and/or
726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn * modify it under the terms of the GNU General Public License
826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn * as published by the Free Software Foundation
926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn */
1026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn#include <linux/module.h>
1126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn#include <linux/moduleparam.h>
1226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn#include <linux/types.h>
1326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn#include <linux/kernel.h>
1426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn#include <linux/slab.h>
1526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn#include <linux/platform_device.h>
1626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn#include <linux/watchdog.h>
1726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn#include <linux/uaccess.h>
1826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn#include <linux/gpio.h>
1926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn#include <linux/of_gpio.h>
2026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn#include <linux/delay.h>
2126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn#include <linux/bitops.h>
2226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
2326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn#define NUM_GPIOS 6
2426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
2526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnenum a21_wdt_gpios {
2626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	GPIO_WD_ENAB,
2726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	GPIO_WD_FAST,
2826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	GPIO_WD_TRIG,
2926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	GPIO_WD_RST0,
3026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	GPIO_WD_RST1,
3126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	GPIO_WD_RST2,
3226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn};
3326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
3426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstruct a21_wdt_drv {
3526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	struct watchdog_device wdt;
3626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	struct mutex lock;
3726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	unsigned gpios[NUM_GPIOS];
3826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn};
3926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
4026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstatic bool nowayout = WATCHDOG_NOWAYOUT;
4126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnmodule_param(nowayout, bool, 0);
4226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes ThumshirnMODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
4326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn			    __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
4426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
4526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstatic unsigned int a21_wdt_get_bootstatus(struct a21_wdt_drv *drv)
4626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn{
4726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	int reset = 0;
4826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
4926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST0]) ? (1 << 0) : 0;
5026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST1]) ? (1 << 1) : 0;
5126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	reset |= gpio_get_value(drv->gpios[GPIO_WD_RST2]) ? (1 << 2) : 0;
5226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
5326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	return reset;
5426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn}
5526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
5626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstatic int a21_wdt_start(struct watchdog_device *wdt)
5726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn{
5826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
5926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
6026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	mutex_lock(&drv->lock);
6126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
6226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 1);
6326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
6426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	mutex_unlock(&drv->lock);
6526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
6626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	return 0;
6726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn}
6826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
6926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstatic int a21_wdt_stop(struct watchdog_device *wdt)
7026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn{
7126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
7226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
7326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	mutex_lock(&drv->lock);
7426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
7526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
7626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
7726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	mutex_unlock(&drv->lock);
7826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
7926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	return 0;
8026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn}
8126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
8226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstatic int a21_wdt_ping(struct watchdog_device *wdt)
8326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn{
8426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
8526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
8626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	mutex_lock(&drv->lock);
8726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
8826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	gpio_set_value(drv->gpios[GPIO_WD_TRIG], 0);
8926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	ndelay(10);
9026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	gpio_set_value(drv->gpios[GPIO_WD_TRIG], 1);
9126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
9226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	mutex_unlock(&drv->lock);
9326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
9426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	return 0;
9526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn}
9626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
9726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstatic int a21_wdt_set_timeout(struct watchdog_device *wdt,
9826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn			       unsigned int timeout)
9926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn{
10026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	struct a21_wdt_drv *drv = watchdog_get_drvdata(wdt);
10126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
10226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	if (timeout != 1 && timeout != 30) {
10326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		dev_err(wdt->dev, "Only 1 and 30 allowed as timeout\n");
10426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		return -EINVAL;
10526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	}
10626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
10726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	if (timeout == 30 && wdt->timeout == 1) {
10826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		dev_err(wdt->dev,
10926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn			"Transition from fast to slow mode not allowed\n");
11026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		return -EINVAL;
11126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	}
11226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
11326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	mutex_lock(&drv->lock);
11426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
11526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	if (timeout == 1)
11626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		gpio_set_value(drv->gpios[GPIO_WD_FAST], 1);
11726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	else
11826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		gpio_set_value(drv->gpios[GPIO_WD_FAST], 0);
11926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
12026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	wdt->timeout = timeout;
12126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
12226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	mutex_unlock(&drv->lock);
12326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
12426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	return 0;
12526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn}
12626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
12726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstatic const struct watchdog_info a21_wdt_info = {
12826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
12926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.identity = "MEN A21 Watchdog",
13026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn};
13126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
13226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstatic const struct watchdog_ops a21_wdt_ops = {
13326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.owner = THIS_MODULE,
13426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.start = a21_wdt_start,
13526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.stop = a21_wdt_stop,
13626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.ping = a21_wdt_ping,
13726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.set_timeout = a21_wdt_set_timeout,
13826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn};
13926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
14026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstatic struct watchdog_device a21_wdt = {
14126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.info = &a21_wdt_info,
14226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.ops = &a21_wdt_ops,
14326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.min_timeout = 1,
14426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.max_timeout = 30,
14526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn};
14626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
14726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstatic int a21_wdt_probe(struct platform_device *pdev)
14826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn{
14926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	struct device_node *node;
15026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	struct a21_wdt_drv *drv;
15126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	unsigned int reset = 0;
15226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	int num_gpios;
15326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	int ret;
15426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	int i;
15526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
15626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	drv = devm_kzalloc(&pdev->dev, sizeof(struct a21_wdt_drv), GFP_KERNEL);
15726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	if (!drv)
15826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		return -ENOMEM;
15926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
16026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	/* Fill GPIO pin array */
16126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	node = pdev->dev.of_node;
16226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
16326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	num_gpios = of_gpio_count(node);
16426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	if (num_gpios != NUM_GPIOS) {
16526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		dev_err(&pdev->dev, "gpios DT property wrong, got %d want %d",
16626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn			num_gpios, NUM_GPIOS);
16726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		return -ENODEV;
16826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	}
16926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
17026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	for (i = 0; i < num_gpios; i++) {
17126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		int val;
17226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
17326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		val = of_get_gpio(node, i);
17426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		if (val < 0)
17526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn			return val;
17626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
17726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		drv->gpios[i] = val;
17826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	}
17926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
18026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	/* Request the used GPIOs */
18126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	for (i = 0; i < num_gpios; i++) {
18226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		ret = devm_gpio_request(&pdev->dev, drv->gpios[i],
18326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn					"MEN A21 Watchdog");
18426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		if (ret)
18526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn			return ret;
18626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
18726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		if (i < GPIO_WD_RST0)
18826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn			ret = gpio_direction_output(drv->gpios[i],
18926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn						gpio_get_value(drv->gpios[i]));
19026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		else		/* GPIO_WD_RST[0..2] are inputs */
19126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn			ret = gpio_direction_input(drv->gpios[i]);
19226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		if (ret)
19326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn			return ret;
19426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	}
19526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
19626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	mutex_init(&drv->lock);
19726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	watchdog_init_timeout(&a21_wdt, 30, &pdev->dev);
19826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	watchdog_set_nowayout(&a21_wdt, nowayout);
19926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	watchdog_set_drvdata(&a21_wdt, drv);
20026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
20126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	reset = a21_wdt_get_bootstatus(drv);
20226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	if (reset == 2)
20326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		a21_wdt.bootstatus |= WDIOF_EXTERN1;
20426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	else if (reset == 4)
20526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		a21_wdt.bootstatus |= WDIOF_CARDRESET;
20626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	else if (reset == 5)
20726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		a21_wdt.bootstatus |= WDIOF_POWERUNDER;
20826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	else if (reset == 7)
20926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		a21_wdt.bootstatus |= WDIOF_EXTERN2;
21026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
21126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	ret = watchdog_register_device(&a21_wdt);
21226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	if (ret) {
21326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		dev_err(&pdev->dev, "Cannot register watchdog device\n");
21426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		goto err_register_wd;
21526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	}
21626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
21726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	dev_set_drvdata(&pdev->dev, drv);
21826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
21926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	dev_info(&pdev->dev, "MEN A21 watchdog timer driver enabled\n");
22026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
22126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	return 0;
22226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
22326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnerr_register_wd:
22426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	mutex_destroy(&drv->lock);
22526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
22626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	return ret;
22726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn}
22826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
22926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstatic int a21_wdt_remove(struct platform_device *pdev)
23026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn{
23126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
23226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
23326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	dev_warn(&pdev->dev,
23426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		"Unregistering A21 watchdog driver, board may reboot\n");
23526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
23626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	watchdog_unregister_device(&drv->wdt);
23726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
23826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	mutex_destroy(&drv->lock);
23926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
24026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	return 0;
24126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn}
24226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
24326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstatic void a21_wdt_shutdown(struct platform_device *pdev)
24426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn{
24526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	struct a21_wdt_drv *drv = dev_get_drvdata(&pdev->dev);
24626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
24726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	gpio_set_value(drv->gpios[GPIO_WD_ENAB], 0);
24826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn}
24926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
25026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstatic const struct of_device_id a21_wdt_ids[] = {
25126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	{ .compatible = "men,a021-wdt" },
25226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	{ },
25326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn};
25426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
25526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnstatic struct platform_driver a21_wdt_driver = {
25626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.probe = a21_wdt_probe,
25726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.remove = a21_wdt_remove,
25826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.shutdown = a21_wdt_shutdown,
25926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	.driver = {
26026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		.name = "a21-watchdog",
26126c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn		.of_match_table = a21_wdt_ids,
26226c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn	},
26326c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn};
26426c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
26526c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirnmodule_platform_driver(a21_wdt_driver);
26626c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes Thumshirn
26726c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes ThumshirnMODULE_AUTHOR("MEN Mikro Elektronik");
26826c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes ThumshirnMODULE_DESCRIPTION("MEN A21 Watchdog");
26926c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes ThumshirnMODULE_LICENSE("GPL");
27026c57ef1ea35f2e7b73db5fad3c81c388d6d056aJohannes ThumshirnMODULE_ALIAS("platform:a21-watchdog");
271