From b549a26fc3088b85602caf412bebd16f9e3e8ae9 Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Thu, 31 Jul 2014 19:11:31 +0400 Subject: watchdog: add minimal jz4740 driver Also move reset_cpu() for jz4755 SoC from platform code into the new driver code. At the moment mach-xburst lacks clk support so jz4740 watchdog driver looks like a template. We can improve jz4740 watchdog driver later after adding clk support. Signed-off-by: Antony Pavlov Signed-off-by: Sascha Hauer --- arch/mips/dts/jz4755.dtsi | 5 ++ arch/mips/mach-xburst/Kconfig | 2 + arch/mips/mach-xburst/include/mach/jz4750d_regs.h | 22 ----- arch/mips/mach-xburst/reset-jz4750.c | 18 ---- drivers/watchdog/Kconfig | 7 ++ drivers/watchdog/Makefile | 1 + drivers/watchdog/jz4740.c | 102 ++++++++++++++++++++++ 7 files changed, 117 insertions(+), 40 deletions(-) create mode 100644 drivers/watchdog/jz4740.c diff --git a/arch/mips/dts/jz4755.dtsi b/arch/mips/dts/jz4755.dtsi index 0e655b65a1..718463548b 100644 --- a/arch/mips/dts/jz4755.dtsi +++ b/arch/mips/dts/jz4755.dtsi @@ -8,6 +8,11 @@ #size-cells = <1>; ranges; + wdt: wdt@b0002000 { + compatible = "ingenic,jz4740-wdt"; + reg = <0xb0002000 0x10>; + }; + serial0: serial@b0030000 { compatible = "ingenic,jz4740-uart"; reg = <0xb0030000 0x20>; diff --git a/arch/mips/mach-xburst/Kconfig b/arch/mips/mach-xburst/Kconfig index 706d59249b..f7b8470cb8 100644 --- a/arch/mips/mach-xburst/Kconfig +++ b/arch/mips/mach-xburst/Kconfig @@ -6,6 +6,8 @@ config ARCH_TEXT_BASE config CPU_JZ4755 bool + select WATCHDOG + select WATCHDOG_JZ4740 choice prompt "Board type" diff --git a/arch/mips/mach-xburst/include/mach/jz4750d_regs.h b/arch/mips/mach-xburst/include/mach/jz4750d_regs.h index 7a3daadb18..396c823a1f 100644 --- a/arch/mips/mach-xburst/include/mach/jz4750d_regs.h +++ b/arch/mips/mach-xburst/include/mach/jz4750d_regs.h @@ -58,28 +58,6 @@ #define TCU_OSTCSR_RTC_EN (1 << 1) /* select rtcclk as the timer clock input */ #define TCU_OSTCSR_PCK_EN (1 << 0) /* select pclk as the timer clock input */ -/************************************************************************* - * WDT (WatchDog Timer) - *************************************************************************/ -#define WDT_TDR (WDT_BASE + 0x00) -#define WDT_TCER (WDT_BASE + 0x04) -#define WDT_TCNT (WDT_BASE + 0x08) -#define WDT_TCSR (WDT_BASE + 0x0c) - -#define WDT_TCSR_PRESCALE_BIT 3 -#define WDT_TCSR_PRESCALE_MASK (0x7 << WDT_TCSR_PRESCALE_BIT) - #define WDT_TCSR_PRESCALE1 (0x0 << WDT_TCSR_PRESCALE_BIT) - #define WDT_TCSR_PRESCALE4 (0x1 << WDT_TCSR_PRESCALE_BIT) - #define WDT_TCSR_PRESCALE16 (0x2 << WDT_TCSR_PRESCALE_BIT) - #define WDT_TCSR_PRESCALE64 (0x3 << WDT_TCSR_PRESCALE_BIT) - #define WDT_TCSR_PRESCALE256 (0x4 << WDT_TCSR_PRESCALE_BIT) - #define WDT_TCSR_PRESCALE1024 (0x5 << WDT_TCSR_PRESCALE_BIT) -#define WDT_TCSR_EXT_EN (1 << 2) -#define WDT_TCSR_RTC_EN (1 << 1) -#define WDT_TCSR_PCK_EN (1 << 0) - -#define WDT_TCER_TCEN (1 << 0) - /************************************************************************* * RTC *************************************************************************/ diff --git a/arch/mips/mach-xburst/reset-jz4750.c b/arch/mips/mach-xburst/reset-jz4750.c index 8f33672280..25830f130e 100644 --- a/arch/mips/mach-xburst/reset-jz4750.c +++ b/arch/mips/mach-xburst/reset-jz4750.c @@ -24,8 +24,6 @@ #include #include -#define JZ_EXTAL 24000000 - static void __noreturn jz4750d_halt(void) { while (1) { @@ -39,22 +37,6 @@ static void __noreturn jz4750d_halt(void) unreachable(); } -void __noreturn reset_cpu(ulong addr) -{ - __raw_writew(WDT_TCSR_PRESCALE4 | WDT_TCSR_EXT_EN, (u16 *)WDT_TCSR); - __raw_writew(0, (u16 *)WDT_TCNT); - - /* reset after 4ms */ - __raw_writew(JZ_EXTAL / 1000, (u16 *)WDT_TDR); - /* enable wdt clock */ - __raw_writel(TCU_TSCR_WDTSC, (u32 *)TCU_TSCR); - /* start wdt */ - __raw_writeb(WDT_TCER_TCEN, (u8 *)WDT_TCER); - - unreachable(); -} -EXPORT_SYMBOL(reset_cpu); - void __noreturn poweroff() { u32 ctrl; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 2e2900c10f..7f7b02e306 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -21,4 +21,11 @@ config WATCHDOG_IMX depends on ARCH_IMX help Add support for watchdog found on Freescale i.MX SoCs. + +config WATCHDOG_JZ4740 + bool "Ingenic jz4740 SoC hardware watchdog" + depends on MACH_MIPS_XBURST + help + Hardware driver for the built-in watchdog timer on Ingenic jz4740 SoCs. + endif diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index f522b88708..865fc477b1 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_WATCHDOG) += wd_core.o obj-$(CONFIG_WATCHDOG_MXS28) += im28wd.o +obj-$(CONFIG_WATCHDOG_JZ4740) += jz4740.o obj-$(CONFIG_WATCHDOG_IMX_RESET_SOURCE) += imxwd.o diff --git a/drivers/watchdog/jz4740.c b/drivers/watchdog/jz4740.c new file mode 100644 index 0000000000..8ac51e060a --- /dev/null +++ b/drivers/watchdog/jz4740.c @@ -0,0 +1,102 @@ +/* + * JZ4740 Watchdog driver + * + * Copyright (C) 2014 Antony Pavlov + * + * Based on jz4740_wdt.c from linux-3.15. + * + * Copyright (C) 2010, Paul Cercueil + * + * 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 +#include +#include + +#define JZ_REG_WDT_TIMER_DATA 0x0 +#define JZ_REG_WDT_COUNTER_ENABLE 0x4 +#define JZ_REG_WDT_TIMER_COUNTER 0x8 +#define JZ_REG_WDT_TIMER_CONTROL 0xC + +#define JZ_WDT_CLOCK_PCLK 0x1 +#define JZ_WDT_CLOCK_RTC 0x2 +#define JZ_WDT_CLOCK_EXT 0x4 + +#define JZ_WDT_CLOCK_DIV_SHIFT 3 + +#define JZ_WDT_CLOCK_DIV_1 (0 << JZ_WDT_CLOCK_DIV_SHIFT) +#define JZ_WDT_CLOCK_DIV_4 (1 << JZ_WDT_CLOCK_DIV_SHIFT) +#define JZ_WDT_CLOCK_DIV_16 (2 << JZ_WDT_CLOCK_DIV_SHIFT) +#define JZ_WDT_CLOCK_DIV_64 (3 << JZ_WDT_CLOCK_DIV_SHIFT) +#define JZ_WDT_CLOCK_DIV_256 (4 << JZ_WDT_CLOCK_DIV_SHIFT) +#define JZ_WDT_CLOCK_DIV_1024 (5 << JZ_WDT_CLOCK_DIV_SHIFT) + +#define JZ_EXTAL 24000000 + +struct jz4740_wdt_drvdata { + void __iomem *base; +}; + +static struct jz4740_wdt_drvdata *reset_wd; + +void __noreturn reset_cpu(unsigned long addr) +{ + if (reset_wd) { + void __iomem *base = reset_wd->base; + + writew(JZ_WDT_CLOCK_DIV_4 | JZ_WDT_CLOCK_EXT, + base + JZ_REG_WDT_TIMER_CONTROL); + writew(0, base + JZ_REG_WDT_TIMER_COUNTER); + + /* reset after 4ms */ + writew(JZ_EXTAL / 1000, base + JZ_REG_WDT_TIMER_DATA); + + /* start wdt */ + writeb(0x1, base + JZ_REG_WDT_COUNTER_ENABLE); + + mdelay(1000); + } else + pr_err("%s: can't reset cpu\n", __func__); + + hang(); +} +EXPORT_SYMBOL(reset_cpu); + +static int jz4740_wdt_probe(struct device_d *dev) +{ + struct jz4740_wdt_drvdata *priv; + + priv = xzalloc(sizeof(struct jz4740_wdt_drvdata)); + priv->base = dev_request_mem_region(dev, 0); + if (!priv->base) { + dev_err(dev, "could not get memory region\n"); + return -ENODEV; + } + + if (!reset_wd) + reset_wd = priv; + + dev->priv = priv; + + return 0; +} + +static __maybe_unused struct of_device_id jz4740_wdt_dt_ids[] = { + { + .compatible = "ingenic,jz4740-wdt", + }, { + /* sentinel */ + } +}; + +static struct driver_d jz4740_wdt_driver = { + .name = "jz4740-wdt", + .probe = jz4740_wdt_probe, + .of_compatible = DRV_OF_COMPAT(jz4740_wdt_dt_ids), +}; +device_platform_driver(jz4740_wdt_driver); -- cgit v1.2.3