diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-19 10:02:26 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-05-19 10:02:26 -0700 |
commit | 07b75260ebc2c789724c594d7eaf0194fa47b3be (patch) | |
tree | d88b770bca479789e688d95e50aacd5d09b59b21 /drivers/watchdog | |
parent | 0efacbbaee1e94e9942da0912f5b46ffd45a74bd (diff) | |
parent | 6e4ad1b413604b9130bdbe532aafdbd47ff5318e (diff) | |
download | linux-07b75260ebc2c789724c594d7eaf0194fa47b3be.tar.gz |
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
Pull MIPS updates from Ralf Baechle: "This is the main pull request for MIPS for 4.7. Here's the summary of the changes: - ATH79: Support for DTB passuing using the UHI boot protocol - ATH79: Remove support for builtin DTB. - ATH79: Add zboot debug serial support. - ATH79: Add initial support for Dragino MS14 (Dragine 2), Onion Omega and DPT-Module. - ATH79: Update devicetree clock support for AR9132 and AR9331. - ATH79: Cleanup the DT code. - ATH79: Support newer SOCs in ath79_ddr_ctrl_init. - ATH79: Fix regression in PCI window initialization. - BCM47xx: Move SPROM driver to drivers/firmware/ - BCM63xx: Enable partition parser in defconfig. - BMIPS: BMIPS5000 has I cache filing from D cache - BMIPS: BMIPS: Add cpu-feature-overrides.h - BMIPS: Add Whirlwind support - BMIPS: Adjust mips-hpt-frequency for BCM7435 - BMIPS: Remove maxcpus from BCM97435SVMB DTS - BMIPS: Add missing 7038 L1 register cells to BCM7435 - BMIPS: Various tweaks to initialization code. - BMIPS: Enable partition parser in defconfig. - BMIPS: Cache tweaks. - BMIPS: Add UART, I2C and SATA devices to DT. - BMIPS: Add BCM6358 and BCM63268support - BMIPS: Add device tree example for BCM6358. - BMIPS: Improve Improve BCM6328 and BCM6368 device trees - Lantiq: Add support for device tree file from boot loader - Lantiq: Allow build with no built-in DT. - Loongson 3: Reserve 32MB for RS780E integrated GPU. - Loongson 3: Fix build error after ld-version.sh modification - Loongson 3: Move chipset ACPI code from drivers to arch. - Loongson 3: Speedup irq processing. - Loongson 3: Add basic Loongson 3A support. - Loongson 3: Set cache flush handlers to nop. - Loongson 3: Invalidate special TLBs when needed. - Loongson 3: Fast TLB refill handler. - MT7620: Fallback strategy for invalid syscfg0. - Netlogic: Fix CP0_EBASE redefinition warnings - Octeon: Initialization fixes - Octeon: Add DTS files for the D-Link DSR-1000N and EdgeRouter Lite - Octeon: Enable add Octeon-drivers in cavium_octeon_defconfig - Octeon: Correctly handle endian-swapped initramfs images. - Octeon: Support CN73xx, CN75xx and CN78xx. - Octeon: Remove dead code from cvmx-sysinfo. - Octeon: Extend number of supported CPUs past 32. - Octeon: Remove some code limiting NR_IRQS to 255. - Octeon: Simplify octeon_irq_ciu_gpio_set_type. - Octeon: Mark some functions __init in smp.c - Octeon: Octeon: Add Octeon III CN7xxx interface detection - PIC32: Add serial driver and bindings for it. - PIC32: Add PIC32 deadman timer driver and bindings. - PIC32: Add PIC32 clock timer driver and bindings. - Pistachio: Determine SoC revision during boot - Sibyte: Fix Kconfig dependencies of SIBYTE_BUS_WATCHER. - Sibyte: Strip redundant comments from bcm1480_regs.h. - Panic immediately if panic_on_oops is set. - module: fix incorrect IS_ERR_VALUE macro usage. - module: Make consistent use of pr_* - Remove no longer needed work_on_cpu() call. - Remove CONFIG_IPV6_PRIVACY from defconfigs. - Fix registers of non-crashing CPUs in dumps. - Handle MIPSisms in new vmcore_elf32_check_arch. - Select CONFIG_HANDLE_DOMAIN_IRQ and make it work. - Allow RIXI to be used on non-R2 or R6 cores. - Reserve nosave data for hibernation - Fix siginfo.h to use strict POSIX types. - Don't unwind user mode with EVA. - Fix watchpoint restoration - Ptrace watchpoints for R6. - Sync icache when it fills from dcache - I6400 I-cache fills from dcache. - Various MSA fixes. - Cleanup MIPS_CPU_* definitions. - Signal: Move generic copy_siginfo to signal.h - Signal: Fix uapi include in exported asm/siginfo.h - Timer fixes for sake of KVM. - XPA TLB refill fixes. - Treat perf counter feature - Update John Crispin's email address - Add PIC32 watchdog and bindings. - Handle R10000 LL/SC bug in set_pte() - cpufreq: Various fixes for Longson1. - R6: Fix R2 emulation. - mathemu: Cosmetic fix to ADDIUPC emulation, plenty of other small fixes - ELF: ABI and FP fixes. - Allow for relocatable kernel and use that to support KASLR. - Fix CPC_BASE_ADDR mask - Plenty fo smp-cps, CM, R6 and M6250 fixes. - Make reset_control_ops const. - Fix kernel command line handling of leading whitespace. - Cleanups to cache handling. - Add brcm, bcm6345-l1-intc device tree bindings. - Use generic clkdev.h header - Remove CLK_IS_ROOT usage. - Misc small cleanups. - CM: Fix compilation error when !MIPS_CM - oprofile: Fix a preemption issue - Detect DSP ASE v3 support:1" * 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus: (275 commits) MIPS: pic32mzda: fix getting timer clock rate. MIPS: ath79: fix regression in PCI window initialization MIPS: ath79: make ath79_ddr_ctrl_init() compatible for newer SoCs MIPS: Fix VZ probe gas errors with binutils <2.24 MIPS: perf: Fix I6400 event numbers MIPS: DEC: Export `ioasic_ssr_lock' to modules MIPS: MSA: Fix a link error on `_init_msa_upper' with older GCC MIPS: CM: Fix compilation error when !MIPS_CM MIPS: Fix genvdso error on rebuild USB: ohci-jz4740: Remove obsolete driver MIPS: JZ4740: Probe OHCI platform device via DT MIPS: JZ4740: Qi LB60: Remove support for AVT2 variant MIPS: pistachio: Determine SoC revision during boot MIPS: BMIPS: Adjust mips-hpt-frequency for BCM7435 mips: mt7620: fallback to SDRAM when syscfg0 does not have a valid value for the memory type MIPS: Prevent "restoration" of MSA context in non-MSA kernels MIPS: cevt-r4k: Dynamically calculate min_delta_ns MIPS: malta-time: Take seconds into account MIPS: malta-time: Start GIC count before syncing to RTC MIPS: Force CPUs to lose FP context during mode switches ...
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Kconfig | 26 | ||||
-rw-r--r-- | drivers/watchdog/Makefile | 2 | ||||
-rw-r--r-- | drivers/watchdog/pic32-dmt.c | 257 | ||||
-rw-r--r-- | drivers/watchdog/pic32-wdt.c | 263 |
4 files changed, 548 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index fb947655badd..9c4143112e6c 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1475,6 +1475,32 @@ config MT7621_WDT help Hardware driver for the Mediatek/Ralink MT7621/8 SoC Watchdog Timer. +config PIC32_WDT + tristate "Microchip PIC32 hardware watchdog" + select WATCHDOG_CORE + depends on MACH_PIC32 + help + Watchdog driver for the built in watchdog hardware in a PIC32. + + Configuration bits must be set appropriately for the watchdog to be + controlled by this driver. + + To compile this driver as a loadable module, choose M here. + The module will be called pic32-wdt. + +config PIC32_DMT + tristate "Microchip PIC32 Deadman Timer" + select WATCHDOG_CORE + depends on MACH_PIC32 + help + Watchdog driver for PIC32 instruction fetch counting timer. This specific + timer is typically be used in misson critical and safety critical + applications, where any single failure of the software functionality + and sequencing must be detected. + + To compile this driver as a loadable module, choose M here. + The module will be called pic32-dmt. + # PARISC Architecture # POWERPC Architecture diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index feb6270fdbde..9bde095ff691 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -157,6 +157,8 @@ obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o obj-$(CONFIG_RALINK_WDT) += rt2880_wdt.o obj-$(CONFIG_IMGPDC_WDT) += imgpdc_wdt.o obj-$(CONFIG_MT7621_WDT) += mt7621_wdt.o +obj-$(CONFIG_PIC32_WDT) += pic32-wdt.o +obj-$(CONFIG_PIC32_DMT) += pic32-dmt.o # PARISC Architecture diff --git a/drivers/watchdog/pic32-dmt.c b/drivers/watchdog/pic32-dmt.c new file mode 100644 index 000000000000..962f58c03353 --- /dev/null +++ b/drivers/watchdog/pic32-dmt.c @@ -0,0 +1,257 @@ +/* + * PIC32 deadman timer driver + * + * Purna Chandra Mandal <purna.mandal@microchip.com> + * Copyright (c) 2016, Microchip Technology Inc. + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/watchdog.h> + +#include <asm/mach-pic32/pic32.h> + +/* Deadman Timer Regs */ +#define DMTCON_REG 0x00 +#define DMTPRECLR_REG 0x10 +#define DMTCLR_REG 0x20 +#define DMTSTAT_REG 0x30 +#define DMTCNT_REG 0x40 +#define DMTPSCNT_REG 0x60 +#define DMTPSINTV_REG 0x70 + +/* Deadman Timer Regs fields */ +#define DMT_ON BIT(15) +#define DMT_STEP1_KEY BIT(6) +#define DMT_STEP2_KEY BIT(3) +#define DMTSTAT_WINOPN BIT(0) +#define DMTSTAT_EVENT BIT(5) +#define DMTSTAT_BAD2 BIT(6) +#define DMTSTAT_BAD1 BIT(7) + +/* Reset Control Register fields for watchdog */ +#define RESETCON_DMT_TIMEOUT BIT(5) + +struct pic32_dmt { + void __iomem *regs; + struct clk *clk; +}; + +static inline void dmt_enable(struct pic32_dmt *dmt) +{ + writel(DMT_ON, PIC32_SET(dmt->regs + DMTCON_REG)); +} + +static inline void dmt_disable(struct pic32_dmt *dmt) +{ + writel(DMT_ON, PIC32_CLR(dmt->regs + DMTCON_REG)); + /* + * Cannot touch registers in the CPU cycle following clearing the + * ON bit. + */ + nop(); +} + +static inline int dmt_bad_status(struct pic32_dmt *dmt) +{ + u32 val; + + val = readl(dmt->regs + DMTSTAT_REG); + val &= (DMTSTAT_BAD1 | DMTSTAT_BAD2 | DMTSTAT_EVENT); + if (val) + return -EAGAIN; + + return 0; +} + +static inline int dmt_keepalive(struct pic32_dmt *dmt) +{ + u32 v; + u32 timeout = 500; + + /* set pre-clear key */ + writel(DMT_STEP1_KEY << 8, dmt->regs + DMTPRECLR_REG); + + /* wait for DMT window to open */ + while (--timeout) { + v = readl(dmt->regs + DMTSTAT_REG) & DMTSTAT_WINOPN; + if (v == DMTSTAT_WINOPN) + break; + } + + /* apply key2 */ + writel(DMT_STEP2_KEY, dmt->regs + DMTCLR_REG); + + /* check whether keys are latched correctly */ + return dmt_bad_status(dmt); +} + +static inline u32 pic32_dmt_get_timeout_secs(struct pic32_dmt *dmt) +{ + unsigned long rate; + + rate = clk_get_rate(dmt->clk); + if (rate) + return readl(dmt->regs + DMTPSCNT_REG) / rate; + + return 0; +} + +static inline u32 pic32_dmt_bootstatus(struct pic32_dmt *dmt) +{ + u32 v; + void __iomem *rst_base; + + rst_base = ioremap(PIC32_BASE_RESET, 0x10); + if (!rst_base) + return 0; + + v = readl(rst_base); + + writel(RESETCON_DMT_TIMEOUT, PIC32_CLR(rst_base)); + + iounmap(rst_base); + return v & RESETCON_DMT_TIMEOUT; +} + +static int pic32_dmt_start(struct watchdog_device *wdd) +{ + struct pic32_dmt *dmt = watchdog_get_drvdata(wdd); + + dmt_enable(dmt); + return dmt_keepalive(dmt); +} + +static int pic32_dmt_stop(struct watchdog_device *wdd) +{ + struct pic32_dmt *dmt = watchdog_get_drvdata(wdd); + + dmt_disable(dmt); + + return 0; +} + +static int pic32_dmt_ping(struct watchdog_device *wdd) +{ + struct pic32_dmt *dmt = watchdog_get_drvdata(wdd); + + return dmt_keepalive(dmt); +} + +static const struct watchdog_ops pic32_dmt_fops = { + .owner = THIS_MODULE, + .start = pic32_dmt_start, + .stop = pic32_dmt_stop, + .ping = pic32_dmt_ping, +}; + +static const struct watchdog_info pic32_dmt_ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, + .identity = "PIC32 Deadman Timer", +}; + +static struct watchdog_device pic32_dmt_wdd = { + .info = &pic32_dmt_ident, + .ops = &pic32_dmt_fops, +}; + +static int pic32_dmt_probe(struct platform_device *pdev) +{ + int ret; + struct pic32_dmt *dmt; + struct resource *mem; + struct watchdog_device *wdd = &pic32_dmt_wdd; + + dmt = devm_kzalloc(&pdev->dev, sizeof(*dmt), GFP_KERNEL); + if (IS_ERR(dmt)) + return PTR_ERR(dmt); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dmt->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(dmt->regs)) + return PTR_ERR(dmt->regs); + + dmt->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(dmt->clk)) { + dev_err(&pdev->dev, "clk not found\n"); + return PTR_ERR(dmt->clk); + } + + ret = clk_prepare_enable(dmt->clk); + if (ret) + return ret; + + wdd->timeout = pic32_dmt_get_timeout_secs(dmt); + if (!wdd->timeout) { + dev_err(&pdev->dev, + "failed to read watchdog register timeout\n"); + ret = -EINVAL; + goto out_disable_clk; + } + + dev_info(&pdev->dev, "timeout %d\n", wdd->timeout); + + wdd->bootstatus = pic32_dmt_bootstatus(dmt) ? WDIOF_CARDRESET : 0; + + watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT); + watchdog_set_drvdata(wdd, dmt); + + ret = watchdog_register_device(wdd); + if (ret) { + dev_err(&pdev->dev, "watchdog register failed, err %d\n", ret); + goto out_disable_clk; + } + + platform_set_drvdata(pdev, wdd); + return 0; + +out_disable_clk: + clk_disable_unprepare(dmt->clk); + return ret; +} + +static int pic32_dmt_remove(struct platform_device *pdev) +{ + struct watchdog_device *wdd = platform_get_drvdata(pdev); + struct pic32_dmt *dmt = watchdog_get_drvdata(wdd); + + watchdog_unregister_device(wdd); + clk_disable_unprepare(dmt->clk); + + return 0; +} + +static const struct of_device_id pic32_dmt_of_ids[] = { + { .compatible = "microchip,pic32mzda-dmt",}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pic32_dmt_of_ids); + +static struct platform_driver pic32_dmt_driver = { + .probe = pic32_dmt_probe, + .remove = pic32_dmt_remove, + .driver = { + .name = "pic32-dmt", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pic32_dmt_of_ids), + } +}; + +module_platform_driver(pic32_dmt_driver); + +MODULE_AUTHOR("Purna Chandra Mandal <purna.mandal@microchip.com>"); +MODULE_DESCRIPTION("Microchip PIC32 DMT Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/pic32-wdt.c b/drivers/watchdog/pic32-wdt.c new file mode 100644 index 000000000000..6047aa89a4d3 --- /dev/null +++ b/drivers/watchdog/pic32-wdt.c @@ -0,0 +1,263 @@ +/* + * PIC32 watchdog driver + * + * Joshua Henderson <joshua.henderson@microchip.com> + * Copyright (c) 2016, Microchip Technology Inc. + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/watchdog.h> + +#include <asm/mach-pic32/pic32.h> + +/* Watchdog Timer Registers */ +#define WDTCON_REG 0x00 + +/* Watchdog Timer Control Register fields */ +#define WDTCON_WIN_EN BIT(0) +#define WDTCON_RMCS_MASK 0x0003 +#define WDTCON_RMCS_SHIFT 0x0006 +#define WDTCON_RMPS_MASK 0x001F +#define WDTCON_RMPS_SHIFT 0x0008 +#define WDTCON_ON BIT(15) +#define WDTCON_CLR_KEY 0x5743 + +/* Reset Control Register fields for watchdog */ +#define RESETCON_TIMEOUT_IDLE BIT(2) +#define RESETCON_TIMEOUT_SLEEP BIT(3) +#define RESETCON_WDT_TIMEOUT BIT(4) + +struct pic32_wdt { + void __iomem *regs; + void __iomem *rst_base; + struct clk *clk; +}; + +static inline bool pic32_wdt_is_win_enabled(struct pic32_wdt *wdt) +{ + return !!(readl(wdt->regs + WDTCON_REG) & WDTCON_WIN_EN); +} + +static inline u32 pic32_wdt_get_post_scaler(struct pic32_wdt *wdt) +{ + u32 v = readl(wdt->regs + WDTCON_REG); + + return (v >> WDTCON_RMPS_SHIFT) & WDTCON_RMPS_MASK; +} + +static inline u32 pic32_wdt_get_clk_id(struct pic32_wdt *wdt) +{ + u32 v = readl(wdt->regs + WDTCON_REG); + + return (v >> WDTCON_RMCS_SHIFT) & WDTCON_RMCS_MASK; +} + +static int pic32_wdt_bootstatus(struct pic32_wdt *wdt) +{ + u32 v = readl(wdt->rst_base); + + writel(RESETCON_WDT_TIMEOUT, PIC32_CLR(wdt->rst_base)); + + return v & RESETCON_WDT_TIMEOUT; +} + +static u32 pic32_wdt_get_timeout_secs(struct pic32_wdt *wdt, struct device *dev) +{ + unsigned long rate; + u32 period, ps, terminal; + + rate = clk_get_rate(wdt->clk); + + dev_dbg(dev, "wdt: clk_id %d, clk_rate %lu (prescale)\n", + pic32_wdt_get_clk_id(wdt), rate); + + /* default, prescaler of 32 (i.e. div-by-32) is implicit. */ + rate >>= 5; + if (!rate) + return 0; + + /* calculate terminal count from postscaler. */ + ps = pic32_wdt_get_post_scaler(wdt); + terminal = BIT(ps); + + /* find time taken (in secs) to reach terminal count */ + period = terminal / rate; + dev_dbg(dev, + "wdt: clk_rate %lu (postscale) / terminal %d, timeout %dsec\n", + rate, terminal, period); + + return period; +} + +static void pic32_wdt_keepalive(struct pic32_wdt *wdt) +{ + /* write key through single half-word */ + writew(WDTCON_CLR_KEY, wdt->regs + WDTCON_REG + 2); +} + +static int pic32_wdt_start(struct watchdog_device *wdd) +{ + struct pic32_wdt *wdt = watchdog_get_drvdata(wdd); + + writel(WDTCON_ON, PIC32_SET(wdt->regs + WDTCON_REG)); + pic32_wdt_keepalive(wdt); + + return 0; +} + +static int pic32_wdt_stop(struct watchdog_device *wdd) +{ + struct pic32_wdt *wdt = watchdog_get_drvdata(wdd); + + writel(WDTCON_ON, PIC32_CLR(wdt->regs + WDTCON_REG)); + + /* + * Cannot touch registers in the CPU cycle following clearing the + * ON bit. + */ + nop(); + + return 0; +} + +static int pic32_wdt_ping(struct watchdog_device *wdd) +{ + struct pic32_wdt *wdt = watchdog_get_drvdata(wdd); + + pic32_wdt_keepalive(wdt); + + return 0; +} + +static const struct watchdog_ops pic32_wdt_fops = { + .owner = THIS_MODULE, + .start = pic32_wdt_start, + .stop = pic32_wdt_stop, + .ping = pic32_wdt_ping, +}; + +static const struct watchdog_info pic32_wdt_ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE | WDIOF_CARDRESET, + .identity = "PIC32 Watchdog", +}; + +static struct watchdog_device pic32_wdd = { + .info = &pic32_wdt_ident, + .ops = &pic32_wdt_fops, +}; + +static const struct of_device_id pic32_wdt_dt_ids[] = { + { .compatible = "microchip,pic32mzda-wdt", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pic32_wdt_dt_ids); + +static int pic32_wdt_drv_probe(struct platform_device *pdev) +{ + int ret; + struct watchdog_device *wdd = &pic32_wdd; + struct pic32_wdt *wdt; + struct resource *mem; + + wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL); + if (IS_ERR(wdt)) + return PTR_ERR(wdt); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wdt->regs = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(wdt->regs)) + return PTR_ERR(wdt->regs); + + wdt->rst_base = devm_ioremap(&pdev->dev, PIC32_BASE_RESET, 0x10); + if (IS_ERR(wdt->rst_base)) + return PTR_ERR(wdt->rst_base); + + wdt->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(wdt->clk)) { + dev_err(&pdev->dev, "clk not found\n"); + return PTR_ERR(wdt->clk); + } + + ret = clk_prepare_enable(wdt->clk); + if (ret) { + dev_err(&pdev->dev, "clk enable failed\n"); + return ret; + } + + if (pic32_wdt_is_win_enabled(wdt)) { + dev_err(&pdev->dev, "windowed-clear mode is not supported.\n"); + ret = -ENODEV; + goto out_disable_clk; + } + + wdd->timeout = pic32_wdt_get_timeout_secs(wdt, &pdev->dev); + if (!wdd->timeout) { + dev_err(&pdev->dev, + "failed to read watchdog register timeout\n"); + ret = -EINVAL; + goto out_disable_clk; + } + + dev_info(&pdev->dev, "timeout %d\n", wdd->timeout); + + wdd->bootstatus = pic32_wdt_bootstatus(wdt) ? WDIOF_CARDRESET : 0; + + watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT); + watchdog_set_drvdata(wdd, wdt); + + ret = watchdog_register_device(wdd); + if (ret) { + dev_err(&pdev->dev, "watchdog register failed, err %d\n", ret); + goto out_disable_clk; + } + + platform_set_drvdata(pdev, wdd); + + return 0; + +out_disable_clk: + clk_disable_unprepare(wdt->clk); + + return ret; +} + +static int pic32_wdt_drv_remove(struct platform_device *pdev) +{ + struct watchdog_device *wdd = platform_get_drvdata(pdev); + struct pic32_wdt *wdt = watchdog_get_drvdata(wdd); + + watchdog_unregister_device(wdd); + clk_disable_unprepare(wdt->clk); + + return 0; +} + +static struct platform_driver pic32_wdt_driver = { + .probe = pic32_wdt_drv_probe, + .remove = pic32_wdt_drv_remove, + .driver = { + .name = "pic32-wdt", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pic32_wdt_dt_ids), + } +}; + +module_platform_driver(pic32_wdt_driver); + +MODULE_AUTHOR("Joshua Henderson <joshua.henderson@microchip.com>"); +MODULE_DESCRIPTION("Microchip PIC32 Watchdog Timer"); +MODULE_LICENSE("GPL"); |