diff options
author | Laxman Dewangan <ldewangan@nvidia.com> | 2013-01-20 10:09:22 +0530 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2013-09-14 12:51:05 -0700 |
commit | 2178e53e2016317f79b0956970bfbaa78f5d7cba (patch) | |
tree | 6c55ea7a4af94eb4e6829cb9d1c9ea2d9505600d /drivers/watchdog | |
parent | dfa35946f9f25de30b1f7687ac646cae6461062a (diff) |
watchdog: max77660: Add support for system watchdog timer
Add watchdog timer driver for MAX77660 system watch dog timer.
Change-Id: I2470f90f924d648d12d8303be6524dd5a2401521
Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-on: http://git-master/r/192674
Reviewed-by: Simone Willett <swillett@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 7 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 1 | ||||
-rw-r--r-- | drivers/watchdog/max77660_sys_wdt.c | 244 |
3 files changed, 252 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index e75a328c71be..3aeefc9edeea 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -335,6 +335,13 @@ config TWL4030_WATCHDOG Support for TI TWL4030 watchdog. Say 'Y' here to enable the watchdog timer support for TWL4030 chips. +config MAX77660_SYSTEM_WATCHDOG + tristate "Maxim MAX77660 System Watchdog Timer" + depends on MFD_MAX77663 + help + Support for Maxim MAX77660 System watch dog timer. Say 'Y' here to enable the + watchdog timer support for MAX77660 chips. + config STMP3XXX_RTC_WATCHDOG tristate "Freescale STMP3XXX & i.MX23/28 watchdog" depends on RTC_DRV_STMP diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index d51c9ce904d8..02d0f4f7cdb1 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o +obj-$(CONFIG_MAX77660_SYSTEM_WATCHDOG) += max77660_sys_wdt.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o diff --git a/drivers/watchdog/max77660_sys_wdt.c b/drivers/watchdog/max77660_sys_wdt.c new file mode 100644 index 000000000000..325018f59ef6 --- /dev/null +++ b/drivers/watchdog/max77660_sys_wdt.c @@ -0,0 +1,244 @@ +/* + * max77660_sys_wdt.c -- MAX77660 System WatchDog Timer. + * + * Watchdog timer for MAIXM MAX77660 PMIC. + * + * Copyright (c) 2013, NVIDIA Corporation. + * + * Author: Laxman Dewangan <ldewangan@nvidia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mfd/max77660/max77660-core.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/watchdog.h> + +static bool nowayout = WATCHDOG_NOWAYOUT; + +struct max77660_sys_wdt { + struct watchdog_device wdt_dev; + int irq; + struct device *parent; + struct device *dev; +}; + +static int max77660_twd_sys[] = {16, 32, 64, 128}; + +#define MAX77660_TWD_SYS(i) ((i & 0x3) << 4) +#define MAX77660_WDT_CHG_CLR 0x1 +#define MAX77660_WDTEN_SYS BIT(5) + +static irqreturn_t max77660_sys_wdt_irq(int irq, void *data) +{ + struct max77660_sys_wdt *wdt = data; + int ret; + + /* Reset timer before any debug prints */ + ret = max77660_reg_write(wdt->parent, MAX77660_PWR_SLAVE, + MAX77660_REG_GLOBAL_CFG4, MAX77660_WDT_CHG_CLR); + if (ret < 0) + dev_err(wdt->dev, + "MAX77660_REG_GLOBAL_CFG6 update failed: %d\n", ret); + + dev_info(wdt->dev, "System WDT interrupt occur\n"); + return IRQ_HANDLED; +} + +static int max77660_sys_wdt_start(struct watchdog_device *wdt_dev) +{ + struct max77660_sys_wdt *wdt = watchdog_get_drvdata(wdt_dev); + int ret; + + ret = max77660_reg_set_bits(wdt->parent, MAX77660_PWR_SLAVE, + MAX77660_REG_GLOBAL_CFG1, MAX77660_WDTEN_SYS); + if (ret < 0) { + dev_err(wdt->dev, + "MAX77660_REG_GLOBAL_CFG1 update failed: %d\n", ret); + return ret; + } + return 0; +} + +static int max77660_sys_wdt_stop(struct watchdog_device *wdt_dev) +{ + struct max77660_sys_wdt *wdt = watchdog_get_drvdata(wdt_dev); + int ret; + + ret = max77660_reg_clr_bits(wdt->parent, MAX77660_PWR_SLAVE, + MAX77660_REG_GLOBAL_CFG1, MAX77660_WDTEN_SYS); + if (ret < 0) { + dev_err(wdt->dev, + "MAX77660_REG_GLOBAL_CFG1 update failed: %d\n", ret); + return ret; + } + return 0; +} + +static int max77660_sys_wdt_set_timeout(struct watchdog_device *wdt_dev, + unsigned int timeout) +{ + struct max77660_sys_wdt *wdt = watchdog_get_drvdata(wdt_dev); + int i; + int ret; + + for (i = 0; i < ARRAY_SIZE(max77660_twd_sys); ++i) { + if (max77660_twd_sys[i] >= timeout) + break; + } + + if (i == ARRAY_SIZE(max77660_twd_sys)) { + dev_err(wdt->dev, " Not a valid timeout: %u\n", timeout); + return -EINVAL; + } + + ret = max77660_reg_update(wdt->parent, MAX77660_PWR_SLAVE, + MAX77660_REG_GLOBAL_CFG2, 0x30, MAX77660_TWD_SYS(i)); + if (ret < 0) { + dev_err(wdt->dev, + "MAX77660_REG_GLOBAL_CFG2 update failed: %d\n", ret); + return ret; + } + return 0; +} + +static const struct watchdog_info max77660_sys_wdt_info = { + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE, + .identity = "MAX77660 System Watchdog", +}; + +static const struct watchdog_ops max77660_sys_wdt_ops = { + .owner = THIS_MODULE, + .start = max77660_sys_wdt_start, + .stop = max77660_sys_wdt_stop, + .set_timeout = max77660_sys_wdt_set_timeout, +}; + +static int __devinit max77660_sys_wdt_probe(struct platform_device *pdev) +{ + struct max77660_platform_data *pdata; + struct max77660_sys_wdt *wdt; + struct watchdog_device *wdt_dev; + uint8_t regval; + int ret; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + pdata = dev_get_platdata(pdev->dev.parent); + + wdt->dev = &pdev->dev; + wdt->irq = platform_get_irq(pdev, 0); + wdt->parent = pdev->dev.parent; + wdt_dev = &wdt->wdt_dev; + + wdt_dev->info = &max77660_sys_wdt_info; + wdt_dev->ops = &max77660_sys_wdt_ops; + wdt_dev->timeout = 128; + wdt_dev->min_timeout = 16; + wdt_dev->max_timeout = 128; + watchdog_set_nowayout(wdt_dev, nowayout); + watchdog_set_drvdata(wdt_dev, wdt); + platform_set_drvdata(pdev, wdt); + + ret = request_threaded_irq(wdt->irq, NULL, max77660_sys_wdt_irq, + IRQF_ONESHOT | IRQF_EARLY_RESUME, + dev_name(&pdev->dev), wdt); + if (ret < 0) { + dev_err(&pdev->dev, "request IRQ:%d failed, err = %d\n", + wdt->irq, ret); + return ret; + } + + ret = watchdog_register_device(wdt_dev); + if (ret < 0) { + dev_err(&pdev->dev, "watchdog registration failed: %d\n", ret); + return ret; + } + + ret = max77660_reg_read(wdt->parent, MAX77660_PWR_SLAVE, + MAX77660_REG_GLOBAL_CFG1, ®val); + if (ret < 0) { + dev_err(wdt->dev, + "MAX77660_REG_GLOBAL_CFG1 read failed: %d\n", ret); + goto scrub; + } + + if (regval & MAX77660_WDTEN_SYS) + dev_info(wdt->dev, "System watchdog timer enabled\n"); + else + dev_info(wdt->dev, "System watchdog timer disabled\n"); + + + ret = max77660_sys_wdt_stop(wdt_dev); + if (ret < 0) { + dev_err(wdt->dev, "wdt stop failed: %d\n", ret); + goto scrub; + } + + if (pdata && (pdata->system_watchdog_timeout > 0)) { + ret = max77660_sys_wdt_set_timeout(wdt_dev, + pdata->system_watchdog_timeout); + if (ret < 0) { + dev_err(wdt->dev, "wdt set timeout failed: %d\n", ret); + goto scrub; + } + ret = max77660_sys_wdt_start(wdt_dev); + if (ret < 0) { + dev_err(wdt->dev, "wdt start failed: %d\n", ret); + goto scrub; + } + } + + return 0; +scrub: + free_irq(wdt->irq, wdt); + watchdog_unregister_device(&wdt->wdt_dev); + return ret; +} + +static int __devexit max77660_sys_wdt_remove(struct platform_device *pdev) +{ + struct max77660_sys_wdt *wdt = platform_get_drvdata(pdev); + + free_irq(wdt->irq, wdt); + watchdog_unregister_device(&wdt->wdt_dev); + return 0; +} + +static struct platform_driver max77660_sys_wdt_driver = { + .driver = { + .name = "max77660-sys-wdt", + .owner = THIS_MODULE, + }, + .probe = max77660_sys_wdt_probe, + .remove = __devexit_p(max77660_sys_wdt_remove), +}; + +module_platform_driver(max77660_sys_wdt_driver); + +MODULE_ALIAS("platform:max77660-sys-wdt"); +MODULE_DESCRIPTION("Maxim Max77660 system watchdog timer driver"); +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_LICENSE("GPL v2"); |