All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/8] mfd: watchdog: rtc: New driver for ST's LPC IP
@ 2014-12-15 11:25 ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, a.zummo, rtc-linux, wim, linux-watchdog, devicetree

ST's Low Power Controller (LPC) controls two devices; watchdog and RTC.
Only one of the devices can be used at any one time, which is enforced
by the correlating MFD driver.

This driver set provides everything you need to choose one (and only
one) of the LPC devices to run per I/P block, of which there are two
on the enabled STiH407 h/w.

Lee Jones (8):
  ARM: multi_v7_defconfig: Enable support for ST's LPC Watchdog
  ARM: multi_v7_defconfig: Enable support for ST's LPC RTC
  mfd: bindings: Provide ST bindings for ST's LPC device
  mfd: dt-bindings: Provide human readable defines for LPC mode choosing
  mfd: Add ST's Low Power Controller driver
  watchdog: st_wdt: Add new driver for ST's LPC Watchdog
  rtc: st: add new driver for ST's LPC RTC
  ARM: STi: DT: STiH407: Add Device Tree node for the LPC

 Documentation/devicetree/bindings/mfd/st-lpc.txt |  36 +++
 arch/arm/boot/dts/stih407.dtsi                   |  20 ++
 arch/arm/configs/multi_v7_defconfig              |   2 +
 drivers/mfd/Kconfig                              |   8 +
 drivers/mfd/Makefile                             |   1 +
 drivers/mfd/st-lpc.c                             |  88 ++++++
 drivers/rtc/Kconfig                              |  13 +
 drivers/rtc/Makefile                             |   1 +
 drivers/rtc/rtc-st-lpc.c                         | 330 +++++++++++++++++++++++
 drivers/watchdog/Kconfig                         |  13 +
 drivers/watchdog/Makefile                        |   1 +
 drivers/watchdog/st_wdt.c                        | 312 +++++++++++++++++++++
 include/dt-bindings/mfd/st-lpc.h                 |  15 ++
 13 files changed, 840 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/st-lpc.txt
 create mode 100644 drivers/mfd/st-lpc.c
 create mode 100644 drivers/rtc/rtc-st-lpc.c
 create mode 100644 drivers/watchdog/st_wdt.c
 create mode 100644 include/dt-bindings/mfd/st-lpc.h

-- 
1.9.1


^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 0/8] mfd: watchdog: rtc: New driver for ST's LPC IP
@ 2014-12-15 11:25 ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	a.zummo-BfzFCNDTiLLj+vYz1yj4TQ, rtc-linux-/JYPxA39Uh5TLH3MbocFFw,
	wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

ST's Low Power Controller (LPC) controls two devices; watchdog and RTC.
Only one of the devices can be used at any one time, which is enforced
by the correlating MFD driver.

This driver set provides everything you need to choose one (and only
one) of the LPC devices to run per I/P block, of which there are two
on the enabled STiH407 h/w.

Lee Jones (8):
  ARM: multi_v7_defconfig: Enable support for ST's LPC Watchdog
  ARM: multi_v7_defconfig: Enable support for ST's LPC RTC
  mfd: bindings: Provide ST bindings for ST's LPC device
  mfd: dt-bindings: Provide human readable defines for LPC mode choosing
  mfd: Add ST's Low Power Controller driver
  watchdog: st_wdt: Add new driver for ST's LPC Watchdog
  rtc: st: add new driver for ST's LPC RTC
  ARM: STi: DT: STiH407: Add Device Tree node for the LPC

 Documentation/devicetree/bindings/mfd/st-lpc.txt |  36 +++
 arch/arm/boot/dts/stih407.dtsi                   |  20 ++
 arch/arm/configs/multi_v7_defconfig              |   2 +
 drivers/mfd/Kconfig                              |   8 +
 drivers/mfd/Makefile                             |   1 +
 drivers/mfd/st-lpc.c                             |  88 ++++++
 drivers/rtc/Kconfig                              |  13 +
 drivers/rtc/Makefile                             |   1 +
 drivers/rtc/rtc-st-lpc.c                         | 330 +++++++++++++++++++++++
 drivers/watchdog/Kconfig                         |  13 +
 drivers/watchdog/Makefile                        |   1 +
 drivers/watchdog/st_wdt.c                        | 312 +++++++++++++++++++++
 include/dt-bindings/mfd/st-lpc.h                 |  15 ++
 13 files changed, 840 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/st-lpc.txt
 create mode 100644 drivers/mfd/st-lpc.c
 create mode 100644 drivers/rtc/rtc-st-lpc.c
 create mode 100644 drivers/watchdog/st_wdt.c
 create mode 100644 include/dt-bindings/mfd/st-lpc.h

-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 0/8] mfd: watchdog: rtc: New driver for ST's LPC IP
@ 2014-12-15 11:25 ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel

ST's Low Power Controller (LPC) controls two devices; watchdog and RTC.
Only one of the devices can be used at any one time, which is enforced
by the correlating MFD driver.

This driver set provides everything you need to choose one (and only
one) of the LPC devices to run per I/P block, of which there are two
on the enabled STiH407 h/w.

Lee Jones (8):
  ARM: multi_v7_defconfig: Enable support for ST's LPC Watchdog
  ARM: multi_v7_defconfig: Enable support for ST's LPC RTC
  mfd: bindings: Provide ST bindings for ST's LPC device
  mfd: dt-bindings: Provide human readable defines for LPC mode choosing
  mfd: Add ST's Low Power Controller driver
  watchdog: st_wdt: Add new driver for ST's LPC Watchdog
  rtc: st: add new driver for ST's LPC RTC
  ARM: STi: DT: STiH407: Add Device Tree node for the LPC

 Documentation/devicetree/bindings/mfd/st-lpc.txt |  36 +++
 arch/arm/boot/dts/stih407.dtsi                   |  20 ++
 arch/arm/configs/multi_v7_defconfig              |   2 +
 drivers/mfd/Kconfig                              |   8 +
 drivers/mfd/Makefile                             |   1 +
 drivers/mfd/st-lpc.c                             |  88 ++++++
 drivers/rtc/Kconfig                              |  13 +
 drivers/rtc/Makefile                             |   1 +
 drivers/rtc/rtc-st-lpc.c                         | 330 +++++++++++++++++++++++
 drivers/watchdog/Kconfig                         |  13 +
 drivers/watchdog/Makefile                        |   1 +
 drivers/watchdog/st_wdt.c                        | 312 +++++++++++++++++++++
 include/dt-bindings/mfd/st-lpc.h                 |  15 ++
 13 files changed, 840 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/st-lpc.txt
 create mode 100644 drivers/mfd/st-lpc.c
 create mode 100644 drivers/rtc/rtc-st-lpc.c
 create mode 100644 drivers/watchdog/st_wdt.c
 create mode 100644 include/dt-bindings/mfd/st-lpc.h

-- 
1.9.1

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 1/8] ARM: multi_v7_defconfig: Enable support for ST's LPC Watchdog
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, a.zummo, rtc-linux, wim, linux-watchdog, devicetree

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/configs/multi_v7_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 99a8a4a..e34d5c6 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -238,6 +238,7 @@ CONFIG_ST_THERMAL_SYSCFG=y
 CONFIG_ST_THERMAL_MEMMAP=y
 CONFIG_WATCHDOG=y
 CONFIG_ORION_WATCHDOG=y
+CONFIG_ST_WATCHDOG=y
 CONFIG_SUNXI_WATCHDOG=y
 CONFIG_MFD_AS3722=y
 CONFIG_MFD_BCM590XX=y
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 1/8] ARM: multi_v7_defconfig: Enable support for ST's LPC Watchdog
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	a.zummo-BfzFCNDTiLLj+vYz1yj4TQ, rtc-linux-/JYPxA39Uh5TLH3MbocFFw,
	wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Signed-off-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 arch/arm/configs/multi_v7_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 99a8a4a..e34d5c6 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -238,6 +238,7 @@ CONFIG_ST_THERMAL_SYSCFG=y
 CONFIG_ST_THERMAL_MEMMAP=y
 CONFIG_WATCHDOG=y
 CONFIG_ORION_WATCHDOG=y
+CONFIG_ST_WATCHDOG=y
 CONFIG_SUNXI_WATCHDOG=y
 CONFIG_MFD_AS3722=y
 CONFIG_MFD_BCM590XX=y
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 1/8] ARM: multi_v7_defconfig: Enable support for ST's LPC Watchdog
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/configs/multi_v7_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index 99a8a4a..e34d5c6 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -238,6 +238,7 @@ CONFIG_ST_THERMAL_SYSCFG=y
 CONFIG_ST_THERMAL_MEMMAP=y
 CONFIG_WATCHDOG=y
 CONFIG_ORION_WATCHDOG=y
+CONFIG_ST_WATCHDOG=y
 CONFIG_SUNXI_WATCHDOG=y
 CONFIG_MFD_AS3722=y
 CONFIG_MFD_BCM590XX=y
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 2/8] ARM: multi_v7_defconfig: Enable support for ST's LPC RTC
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, a.zummo, rtc-linux, wim, linux-watchdog, devicetree

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/configs/multi_v7_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index e34d5c6..09b8bce 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -344,6 +344,7 @@ CONFIG_RTC_DRV_AS3722=y
 CONFIG_RTC_DRV_DS1307=y
 CONFIG_RTC_DRV_MAX8907=y
 CONFIG_RTC_DRV_PALMAS=y
+CONFIG_RTC_DRV_ST_LPC=y
 CONFIG_RTC_DRV_TWL4030=y
 CONFIG_RTC_DRV_TPS6586X=y
 CONFIG_RTC_DRV_TPS65910=y
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 2/8] ARM: multi_v7_defconfig: Enable support for ST's LPC RTC
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	a.zummo-BfzFCNDTiLLj+vYz1yj4TQ, rtc-linux-/JYPxA39Uh5TLH3MbocFFw,
	wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Signed-off-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 arch/arm/configs/multi_v7_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index e34d5c6..09b8bce 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -344,6 +344,7 @@ CONFIG_RTC_DRV_AS3722=y
 CONFIG_RTC_DRV_DS1307=y
 CONFIG_RTC_DRV_MAX8907=y
 CONFIG_RTC_DRV_PALMAS=y
+CONFIG_RTC_DRV_ST_LPC=y
 CONFIG_RTC_DRV_TWL4030=y
 CONFIG_RTC_DRV_TPS6586X=y
 CONFIG_RTC_DRV_TPS65910=y
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 2/8] ARM: multi_v7_defconfig: Enable support for ST's LPC RTC
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/configs/multi_v7_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig
index e34d5c6..09b8bce 100644
--- a/arch/arm/configs/multi_v7_defconfig
+++ b/arch/arm/configs/multi_v7_defconfig
@@ -344,6 +344,7 @@ CONFIG_RTC_DRV_AS3722=y
 CONFIG_RTC_DRV_DS1307=y
 CONFIG_RTC_DRV_MAX8907=y
 CONFIG_RTC_DRV_PALMAS=y
+CONFIG_RTC_DRV_ST_LPC=y
 CONFIG_RTC_DRV_TWL4030=y
 CONFIG_RTC_DRV_TPS6586X=y
 CONFIG_RTC_DRV_TPS65910=y
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 3/8] mfd: bindings: Provide ST bindings for ST's LPC device
  2014-12-15 11:25 ` Lee Jones
  (?)
@ 2014-12-15 11:25   ` Lee Jones
  -1 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, a.zummo, rtc-linux, wim, linux-watchdog, devicetree

On current ST platforms the LPC controls a number of functions including
Watchdog and Real Time Clock.  This patch provides the bindings used to
choose and configure the devices.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 Documentation/devicetree/bindings/mfd/st-lpc.txt | 36 ++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/st-lpc.txt

diff --git a/Documentation/devicetree/bindings/mfd/st-lpc.txt b/Documentation/devicetree/bindings/mfd/st-lpc.txt
new file mode 100644
index 0000000..b13f143
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/st-lpc.txt
@@ -0,0 +1,36 @@
+STMicroelectronics Low Power Controller (LPC)
+=============================================
+
+Currently supports Watchdog OR Real Time Clock functionality.
+
+Required properties [common]
+
+- compatible 	: Must be one of: "st,stih407-lpc" "st,stih416-lpc"
+				  "st,stih415-lpc" "st,stid127-lpc"
+- reg		: LPC registers base address + size
+- interrupts    : LPC interrupt line number and associated flags
+- clocks	: Clock used by LPC device (See: ../clock/clock-bindings.txt)
+- st,lpc-mode	: The LPC can run either one of two modes ST_LPC_MODE_RTC [0] or
+		  ST_LPC_MODE_WDT [1].  One (and only one) mode must be
+		  selected.
+
+Required properties [watchdog mode]
+
+- st,syscfg	: Phandle to syscfg node used to enable watchdog and configure
+		  CPU reset type.
+- timeout-sec	: Watchdog timeout in seconds
+
+Optional properties [watchdog mode]
+
+- st,warm-reset	: If present reset type will be 'warm' - if not it will be cold
+
+Example:
+	lpc@fde05000 {
+		compatible	= "st,stih416-lpc-watchdog";
+		reg		= <0xfde05000 0x1000>;
+		clocks 		= <&clk_s_d3_flexgen CLK_LPC_0>;
+		st,syscfg	= <&syscfg_core>;
+		timeout-sec	= <600>;
+		st,lpc-mode	= <ST_LPC_MODE_WDT>;
+		st,warm-reset;
+	};
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 3/8] mfd: bindings: Provide ST bindings for ST's LPC device
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: a.zummo, kernel, rtc-linux, devicetree, wim, lee.jones, linux-watchdog

On current ST platforms the LPC controls a number of functions including
Watchdog and Real Time Clock.  This patch provides the bindings used to
choose and configure the devices.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 Documentation/devicetree/bindings/mfd/st-lpc.txt | 36 ++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/st-lpc.txt

diff --git a/Documentation/devicetree/bindings/mfd/st-lpc.txt b/Documentation/devicetree/bindings/mfd/st-lpc.txt
new file mode 100644
index 0000000..b13f143
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/st-lpc.txt
@@ -0,0 +1,36 @@
+STMicroelectronics Low Power Controller (LPC)
+=============================================
+
+Currently supports Watchdog OR Real Time Clock functionality.
+
+Required properties [common]
+
+- compatible 	: Must be one of: "st,stih407-lpc" "st,stih416-lpc"
+				  "st,stih415-lpc" "st,stid127-lpc"
+- reg		: LPC registers base address + size
+- interrupts    : LPC interrupt line number and associated flags
+- clocks	: Clock used by LPC device (See: ../clock/clock-bindings.txt)
+- st,lpc-mode	: The LPC can run either one of two modes ST_LPC_MODE_RTC [0] or
+		  ST_LPC_MODE_WDT [1].  One (and only one) mode must be
+		  selected.
+
+Required properties [watchdog mode]
+
+- st,syscfg	: Phandle to syscfg node used to enable watchdog and configure
+		  CPU reset type.
+- timeout-sec	: Watchdog timeout in seconds
+
+Optional properties [watchdog mode]
+
+- st,warm-reset	: If present reset type will be 'warm' - if not it will be cold
+
+Example:
+	lpc@fde05000 {
+		compatible	= "st,stih416-lpc-watchdog";
+		reg		= <0xfde05000 0x1000>;
+		clocks 		= <&clk_s_d3_flexgen CLK_LPC_0>;
+		st,syscfg	= <&syscfg_core>;
+		timeout-sec	= <600>;
+		st,lpc-mode	= <ST_LPC_MODE_WDT>;
+		st,warm-reset;
+	};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 3/8] mfd: bindings: Provide ST bindings for ST's LPC device
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel

On current ST platforms the LPC controls a number of functions including
Watchdog and Real Time Clock.  This patch provides the bindings used to
choose and configure the devices.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 Documentation/devicetree/bindings/mfd/st-lpc.txt | 36 ++++++++++++++++++++++++
 1 file changed, 36 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mfd/st-lpc.txt

diff --git a/Documentation/devicetree/bindings/mfd/st-lpc.txt b/Documentation/devicetree/bindings/mfd/st-lpc.txt
new file mode 100644
index 0000000..b13f143
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/st-lpc.txt
@@ -0,0 +1,36 @@
+STMicroelectronics Low Power Controller (LPC)
+=============================================
+
+Currently supports Watchdog OR Real Time Clock functionality.
+
+Required properties [common]
+
+- compatible 	: Must be one of: "st,stih407-lpc" "st,stih416-lpc"
+				  "st,stih415-lpc" "st,stid127-lpc"
+- reg		: LPC registers base address + size
+- interrupts    : LPC interrupt line number and associated flags
+- clocks	: Clock used by LPC device (See: ../clock/clock-bindings.txt)
+- st,lpc-mode	: The LPC can run either one of two modes ST_LPC_MODE_RTC [0] or
+		  ST_LPC_MODE_WDT [1].  One (and only one) mode must be
+		  selected.
+
+Required properties [watchdog mode]
+
+- st,syscfg	: Phandle to syscfg node used to enable watchdog and configure
+		  CPU reset type.
+- timeout-sec	: Watchdog timeout in seconds
+
+Optional properties [watchdog mode]
+
+- st,warm-reset	: If present reset type will be 'warm' - if not it will be cold
+
+Example:
+	lpc at fde05000 {
+		compatible	= "st,stih416-lpc-watchdog";
+		reg		= <0xfde05000 0x1000>;
+		clocks 		= <&clk_s_d3_flexgen CLK_LPC_0>;
+		st,syscfg	= <&syscfg_core>;
+		timeout-sec	= <600>;
+		st,lpc-mode	= <ST_LPC_MODE_WDT>;
+		st,warm-reset;
+	};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 4/8] mfd: dt-bindings: Provide human readable defines for LPC mode choosing
  2014-12-15 11:25 ` Lee Jones
@ 2014-12-15 11:25   ` Lee Jones
  -1 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, a.zummo, rtc-linux, wim, linux-watchdog, devicetree

ST's Low Power Controller can currently operate in two supported modes;
Watchdog and Real Time Clock.  These defines will aid engineers to easily
identify the selected mode.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 include/dt-bindings/mfd/st-lpc.h | 15 +++++++++++++++
 1 file changed, 15 insertions(+)
 create mode 100644 include/dt-bindings/mfd/st-lpc.h

diff --git a/include/dt-bindings/mfd/st-lpc.h b/include/dt-bindings/mfd/st-lpc.h
new file mode 100644
index 0000000..e3e6c75
--- /dev/null
+++ b/include/dt-bindings/mfd/st-lpc.h
@@ -0,0 +1,15 @@
+/*
+ * This header provides shared DT/Driver defines for ST's LPC device
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ */
+
+#ifndef __DT_BINDINGS_ST_LPC_H__
+#define __DT_BINDINGS_ST_LPC_H__
+
+#define ST_LPC_MODE_RTC		0
+#define ST_LPC_MODE_WDT		1
+
+#endif /* __DT_BINDINGS_ST_LPC_H__ */
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 4/8] mfd: dt-bindings: Provide human readable defines for LPC mode choosing
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel

ST's Low Power Controller can currently operate in two supported modes;
Watchdog and Real Time Clock.  These defines will aid engineers to easily
identify the selected mode.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 include/dt-bindings/mfd/st-lpc.h | 15 +++++++++++++++
 1 file changed, 15 insertions(+)
 create mode 100644 include/dt-bindings/mfd/st-lpc.h

diff --git a/include/dt-bindings/mfd/st-lpc.h b/include/dt-bindings/mfd/st-lpc.h
new file mode 100644
index 0000000..e3e6c75
--- /dev/null
+++ b/include/dt-bindings/mfd/st-lpc.h
@@ -0,0 +1,15 @@
+/*
+ * This header provides shared DT/Driver defines for ST's LPC device
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ */
+
+#ifndef __DT_BINDINGS_ST_LPC_H__
+#define __DT_BINDINGS_ST_LPC_H__
+
+#define ST_LPC_MODE_RTC		0
+#define ST_LPC_MODE_WDT		1
+
+#endif /* __DT_BINDINGS_ST_LPC_H__ */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 5/8] mfd: Add ST's Low Power Controller driver
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, a.zummo, rtc-linux, wim, linux-watchdog, devicetree

On current ST platforms the LPC controls a number of functions.  This
patch enables possible support for the LPC Watchdog and LPC RTC devices
and ensures only one of them operates at any one time.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mfd/Kconfig  |  8 +++++
 drivers/mfd/Makefile |  1 +
 drivers/mfd/st-lpc.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 drivers/mfd/st-lpc.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index de5abf2..75db2f2 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -752,6 +752,14 @@ config STMPE_SPI
 	  This is used to enable SPI interface of STMPE
 endmenu
 
+config MFD_ST_LPC
+	tristate "STMicroelectronics Low-Power-Controller"
+	depends on OF
+	select MFD_CORE
+	help
+	  Say yes here to add support for ST's Low-Power-Controller (LPC).
+	  This device provides Real-Time Clock and Watchdog functionality.
+
 config MFD_STA2X11
 	bool "STMicroelectronics STA2X11"
 	depends on STA2X11
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f001487..09aeb95 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_MFD_STA2X11)	+= sta2x11-mfd.o
 obj-$(CONFIG_MFD_STMPE)		+= stmpe.o
 obj-$(CONFIG_STMPE_I2C)		+= stmpe-i2c.o
 obj-$(CONFIG_STMPE_SPI)		+= stmpe-spi.o
+obj-$(CONFIG_MFD_ST_LPC)	+= st-lpc.o
 obj-$(CONFIG_MFD_SUN6I_PRCM)	+= sun6i-prcm.o
 obj-$(CONFIG_MFD_TC3589X)	+= tc3589x.o
 obj-$(CONFIG_MFD_T7L66XB)	+= t7l66xb.o tmio_core.o
diff --git a/drivers/mfd/st-lpc.c b/drivers/mfd/st-lpc.c
new file mode 100644
index 0000000..13906d9
--- /dev/null
+++ b/drivers/mfd/st-lpc.c
@@ -0,0 +1,88 @@
+/*
+ * st-lpc.c - ST's Low Power Controller (LPC) Multi-Function Device Driver
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <dt-bindings/mfd/st-lpc.h>
+
+static int st_lpc_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct mfd_cell *cell;
+	u32 mode;
+	int ret;
+
+	cell = devm_kzalloc(&pdev->dev, sizeof(*cell), GFP_KERNEL);
+	if (!cell)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
+	if (ret) {
+		dev_err(&pdev->dev, "An LPC mode must be selected\n");
+		return ret;
+	}
+
+	switch (mode) {
+	case ST_LPC_MODE_RTC:
+		cell->name = "st-lpc-rtc";
+		break;
+	case ST_LPC_MODE_WDT:
+		cell->name = "st-lpc-wdt";
+		break;
+	default:
+		dev_err(&pdev->dev, "Unsupported mode: %d\n", mode);
+		return ret;
+	}
+
+	/* Pass resources though to selected child device. */
+	cell->resources = pdev->resource;
+	cell->num_resources = pdev->num_resources;
+
+	ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
+			      cell, 1, NULL, 0, NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register child devices\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int st_lpc_remove(struct platform_device *pdev)
+{
+	mfd_remove_devices(&pdev->dev);
+
+	return 0;
+}
+
+static const struct of_device_id st_lpc_dt_match[] = {
+	{ .compatible = "st,stih407-lpc" },
+	{ }
+};
+
+static struct platform_driver st_lpc_driver = {
+	.driver = {
+		.name = "st-lpc",
+		.of_match_table = st_lpc_dt_match,
+	},
+	.probe = st_lpc_probe,
+	.remove = st_lpc_remove,
+};
+
+module_platform_driver(st_lpc_driver);
+
+MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
+MODULE_DESCRIPTION("ST's Low Power Controller (LPC) Multi-Function Device");
+MODULE_LICENSE("GPL");
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 5/8] mfd: Add ST's Low Power Controller driver
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	a.zummo-BfzFCNDTiLLj+vYz1yj4TQ, rtc-linux-/JYPxA39Uh5TLH3MbocFFw,
	wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On current ST platforms the LPC controls a number of functions.  This
patch enables possible support for the LPC Watchdog and LPC RTC devices
and ensures only one of them operates at any one time.

Signed-off-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 drivers/mfd/Kconfig  |  8 +++++
 drivers/mfd/Makefile |  1 +
 drivers/mfd/st-lpc.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 drivers/mfd/st-lpc.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index de5abf2..75db2f2 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -752,6 +752,14 @@ config STMPE_SPI
 	  This is used to enable SPI interface of STMPE
 endmenu
 
+config MFD_ST_LPC
+	tristate "STMicroelectronics Low-Power-Controller"
+	depends on OF
+	select MFD_CORE
+	help
+	  Say yes here to add support for ST's Low-Power-Controller (LPC).
+	  This device provides Real-Time Clock and Watchdog functionality.
+
 config MFD_STA2X11
 	bool "STMicroelectronics STA2X11"
 	depends on STA2X11
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f001487..09aeb95 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_MFD_STA2X11)	+= sta2x11-mfd.o
 obj-$(CONFIG_MFD_STMPE)		+= stmpe.o
 obj-$(CONFIG_STMPE_I2C)		+= stmpe-i2c.o
 obj-$(CONFIG_STMPE_SPI)		+= stmpe-spi.o
+obj-$(CONFIG_MFD_ST_LPC)	+= st-lpc.o
 obj-$(CONFIG_MFD_SUN6I_PRCM)	+= sun6i-prcm.o
 obj-$(CONFIG_MFD_TC3589X)	+= tc3589x.o
 obj-$(CONFIG_MFD_T7L66XB)	+= t7l66xb.o tmio_core.o
diff --git a/drivers/mfd/st-lpc.c b/drivers/mfd/st-lpc.c
new file mode 100644
index 0000000..13906d9
--- /dev/null
+++ b/drivers/mfd/st-lpc.c
@@ -0,0 +1,88 @@
+/*
+ * st-lpc.c - ST's Low Power Controller (LPC) Multi-Function Device Driver
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> for STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <dt-bindings/mfd/st-lpc.h>
+
+static int st_lpc_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct mfd_cell *cell;
+	u32 mode;
+	int ret;
+
+	cell = devm_kzalloc(&pdev->dev, sizeof(*cell), GFP_KERNEL);
+	if (!cell)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
+	if (ret) {
+		dev_err(&pdev->dev, "An LPC mode must be selected\n");
+		return ret;
+	}
+
+	switch (mode) {
+	case ST_LPC_MODE_RTC:
+		cell->name = "st-lpc-rtc";
+		break;
+	case ST_LPC_MODE_WDT:
+		cell->name = "st-lpc-wdt";
+		break;
+	default:
+		dev_err(&pdev->dev, "Unsupported mode: %d\n", mode);
+		return ret;
+	}
+
+	/* Pass resources though to selected child device. */
+	cell->resources = pdev->resource;
+	cell->num_resources = pdev->num_resources;
+
+	ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
+			      cell, 1, NULL, 0, NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register child devices\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int st_lpc_remove(struct platform_device *pdev)
+{
+	mfd_remove_devices(&pdev->dev);
+
+	return 0;
+}
+
+static const struct of_device_id st_lpc_dt_match[] = {
+	{ .compatible = "st,stih407-lpc" },
+	{ }
+};
+
+static struct platform_driver st_lpc_driver = {
+	.driver = {
+		.name = "st-lpc",
+		.of_match_table = st_lpc_dt_match,
+	},
+	.probe = st_lpc_probe,
+	.remove = st_lpc_remove,
+};
+
+module_platform_driver(st_lpc_driver);
+
+MODULE_AUTHOR("Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>");
+MODULE_DESCRIPTION("ST's Low Power Controller (LPC) Multi-Function Device");
+MODULE_LICENSE("GPL");
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 5/8] mfd: Add ST's Low Power Controller driver
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel

On current ST platforms the LPC controls a number of functions.  This
patch enables possible support for the LPC Watchdog and LPC RTC devices
and ensures only one of them operates at any one time.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/mfd/Kconfig  |  8 +++++
 drivers/mfd/Makefile |  1 +
 drivers/mfd/st-lpc.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 97 insertions(+)
 create mode 100644 drivers/mfd/st-lpc.c

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index de5abf2..75db2f2 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -752,6 +752,14 @@ config STMPE_SPI
 	  This is used to enable SPI interface of STMPE
 endmenu
 
+config MFD_ST_LPC
+	tristate "STMicroelectronics Low-Power-Controller"
+	depends on OF
+	select MFD_CORE
+	help
+	  Say yes here to add support for ST's Low-Power-Controller (LPC).
+	  This device provides Real-Time Clock and Watchdog functionality.
+
 config MFD_STA2X11
 	bool "STMicroelectronics STA2X11"
 	depends on STA2X11
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index f001487..09aeb95 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_MFD_STA2X11)	+= sta2x11-mfd.o
 obj-$(CONFIG_MFD_STMPE)		+= stmpe.o
 obj-$(CONFIG_STMPE_I2C)		+= stmpe-i2c.o
 obj-$(CONFIG_STMPE_SPI)		+= stmpe-spi.o
+obj-$(CONFIG_MFD_ST_LPC)	+= st-lpc.o
 obj-$(CONFIG_MFD_SUN6I_PRCM)	+= sun6i-prcm.o
 obj-$(CONFIG_MFD_TC3589X)	+= tc3589x.o
 obj-$(CONFIG_MFD_T7L66XB)	+= t7l66xb.o tmio_core.o
diff --git a/drivers/mfd/st-lpc.c b/drivers/mfd/st-lpc.c
new file mode 100644
index 0000000..13906d9
--- /dev/null
+++ b/drivers/mfd/st-lpc.c
@@ -0,0 +1,88 @@
+/*
+ * st-lpc.c - ST's Low Power Controller (LPC) Multi-Function Device Driver
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/err.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <dt-bindings/mfd/st-lpc.h>
+
+static int st_lpc_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct mfd_cell *cell;
+	u32 mode;
+	int ret;
+
+	cell = devm_kzalloc(&pdev->dev, sizeof(*cell), GFP_KERNEL);
+	if (!cell)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
+	if (ret) {
+		dev_err(&pdev->dev, "An LPC mode must be selected\n");
+		return ret;
+	}
+
+	switch (mode) {
+	case ST_LPC_MODE_RTC:
+		cell->name = "st-lpc-rtc";
+		break;
+	case ST_LPC_MODE_WDT:
+		cell->name = "st-lpc-wdt";
+		break;
+	default:
+		dev_err(&pdev->dev, "Unsupported mode: %d\n", mode);
+		return ret;
+	}
+
+	/* Pass resources though to selected child device. */
+	cell->resources = pdev->resource;
+	cell->num_resources = pdev->num_resources;
+
+	ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
+			      cell, 1, NULL, 0, NULL);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to register child devices\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int st_lpc_remove(struct platform_device *pdev)
+{
+	mfd_remove_devices(&pdev->dev);
+
+	return 0;
+}
+
+static const struct of_device_id st_lpc_dt_match[] = {
+	{ .compatible = "st,stih407-lpc" },
+	{ }
+};
+
+static struct platform_driver st_lpc_driver = {
+	.driver = {
+		.name = "st-lpc",
+		.of_match_table = st_lpc_dt_match,
+	},
+	.probe = st_lpc_probe,
+	.remove = st_lpc_remove,
+};
+
+module_platform_driver(st_lpc_driver);
+
+MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
+MODULE_DESCRIPTION("ST's Low Power Controller (LPC) Multi-Function Device");
+MODULE_LICENSE("GPL");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
  2014-12-15 11:25 ` Lee Jones
@ 2014-12-15 11:25   ` Lee Jones
  -1 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, a.zummo, rtc-linux, wim, linux-watchdog,
	devicetree, David Paris

Signed-off-by: David Paris <david.paris@st.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/watchdog/Kconfig  |  13 ++
 drivers/watchdog/Makefile |   1 +
 drivers/watchdog/st_wdt.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 326 insertions(+)
 create mode 100644 drivers/watchdog/st_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f57312f..5a538af 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
 	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
 	  the watchdog triggers the system will be reset.
 
+config ST_WATCHDOG
+	tristate "STMicroelectronics LPC Watchdog"
+	depends on ARCH_STI
+	depends on OF
+	select WATCHDOG_CORE
+	select MFD_ST_LPC
+	help
+	  Say Y here to include STMicroelectronics Low Power Controller
+	  (LPC) based Watchdog timer support.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called st_wdt.
+
 config TEGRA_WATCHDOG
 	tristate "Tegra watchdog"
 	depends on ARCH_TEGRA || COMPILE_TEST
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 468c320..eb19937 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
 obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
 obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
 obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
+obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
 obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
 obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
 
diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
new file mode 100644
index 0000000..7e06f15
--- /dev/null
+++ b/drivers/watchdog/st_wdt.c
@@ -0,0 +1,312 @@
+/*
+ * st-wdt.c - ST's LPC Watchdog
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: David Paris <david.paris@st.com> for STMicroelectronics
+ *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/watchdog.h>
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF			0x410
+#define LPC_LPA_START_OFF		0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF			0x510
+
+struct st_wdog_syscfg {
+	struct regmap *regmap;
+	unsigned int reset_type_reg;
+	unsigned int reset_type_mask;
+	unsigned int enable_reg;
+	unsigned int enable_mask;
+};
+
+struct st_wdog {
+	void __iomem *base;
+	struct device *dev;
+	struct st_wdog_syscfg *syscfg;
+	struct clk *clk;
+	int warm_reset;
+};
+
+static struct st_wdog_syscfg stid127_syscfg = {
+	.reset_type_reg		= 0x004,
+	.reset_type_mask	= BIT(2),
+	.enable_reg		= 0x000,
+	.enable_mask		= BIT(2),
+};
+
+static struct st_wdog_syscfg stih415_syscfg = {
+	.reset_type_reg		= 0x0B8,
+	.reset_type_mask	= BIT(6),
+	.enable_reg		= 0x0B4,
+	.enable_mask		= BIT(7),
+};
+
+static struct st_wdog_syscfg stih416_syscfg = {
+	.reset_type_reg		= 0x88C,
+	.reset_type_mask	= BIT(6),
+	.enable_reg		= 0x888,
+	.enable_mask		= BIT(7),
+};
+
+static struct st_wdog_syscfg stih407_syscfg = {
+	.enable_reg		= 0x204,
+	.enable_mask		= BIT(19),
+};
+
+static struct of_device_id st_wdog_match[] = {
+	{
+		.compatible = "st,stih407-lpc",
+		.data = (void *)&stih407_syscfg,
+	},
+	{
+		.compatible = "st,stih416-lpc",
+		.data = (void *)&stih416_syscfg,
+	},
+	{
+		.compatible = "st,stih415-lpc",
+		.data = (void *)&stih415_syscfg,
+	},
+	{
+		.compatible = "st,stid127-lpc",
+		.data = (void *)&stid127_syscfg,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, st_wdog_match);
+
+static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
+{
+	/* Type of watchdog reset - 0: Cold 1: Warm */
+	if (st_wdog->syscfg->reset_type_reg)
+		regmap_update_bits(st_wdog->syscfg->regmap,
+				   st_wdog->syscfg->reset_type_reg,
+				   st_wdog->syscfg->reset_type_mask,
+				   st_wdog->warm_reset);
+
+	/* Mask/unmask watchdog reset */
+	regmap_update_bits(st_wdog->syscfg->regmap,
+			   st_wdog->syscfg->enable_reg,
+			   st_wdog->syscfg->enable_mask,
+			   enable ? 0 : st_wdog->syscfg->enable_mask);
+}
+
+static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
+{
+	unsigned long clkrate = clk_get_rate(st_wdog->clk);
+	unsigned int maxtimeout = 0xFFFFFFFF / clkrate;
+
+	if (timeout > maxtimeout) {
+		dev_warn(st_wdog->dev,
+			 "timer overrun detected at current freq (%luHz)\n"
+			 "wdog timeout set for %ds instead of requested %uis",
+			 clkrate, maxtimeout, timeout);
+		timeout = maxtimeout;
+	}
+	timeout *= clkrate;
+
+	writel_relaxed(timeout, st_wdog->base + LPC_LPA_LSB_OFF);
+	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
+}
+
+static int st_wdog_start(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
+
+	return 0;
+}
+
+static int st_wdog_stop(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
+
+	return 0;
+}
+
+static int st_wdog_set_timeout(struct watchdog_device *wdd,
+			       unsigned int timeout)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	wdd->timeout = timeout;
+	st_wdog_load_timer(st_wdog, timeout);
+
+	return 0;
+}
+
+static int st_wdog_keepalive(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	st_wdog_load_timer(st_wdog, wdd->timeout);
+
+	return 0;
+}
+
+static const struct watchdog_info st_wdog_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "ST LPC WDT",
+};
+
+static const struct watchdog_ops st_wdog_ops = {
+	.owner		= THIS_MODULE,
+	.start		= st_wdog_start,
+	.stop		= st_wdog_stop,
+	.ping		= st_wdog_keepalive,
+	.set_timeout	= st_wdog_set_timeout,
+};
+
+static struct watchdog_device st_wdog_dev = {
+	.info		= &st_wdog_info,
+	.ops		= &st_wdog_ops,
+};
+
+static int st_wdog_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct device_node *np;
+	struct st_wdog *st_wdog;
+	struct regmap *regmap;
+	struct resource *res;
+	struct clk *clk;
+	void __iomem *base;
+	int ret;
+
+	/* This is a single node MFD device. */
+	np = pdev->dev.of_node = pdev->dev.parent->of_node;
+
+	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
+	if (!st_wdog)
+		return -ENOMEM;
+
+	match = of_match_device(st_wdog_match, &pdev->dev);
+	if (!match) {
+		dev_err(&pdev->dev, "Couldn't match device\n");
+		return -ENODEV;
+	}
+	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base)) {
+		dev_err(&pdev->dev, "Failed to ioremap base\n");
+		return PTR_ERR(base);
+	}
+
+	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+	if (IS_ERR(regmap)) {
+		dev_err(&pdev->dev, "No syscfg phandle specified\n");
+		return PTR_ERR(regmap);
+	}
+
+	clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "Unable to request clock\n");
+		return PTR_ERR(clk);
+	}
+	clk_prepare_enable(st_wdog->clk);
+
+	st_wdog->dev		= &pdev->dev;
+	st_wdog->base		= base;
+	st_wdog->clk		= clk;
+	st_wdog->syscfg->regmap = regmap;
+	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
+
+	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
+	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
+
+	/* Init Watchdog timeout with value in DT */
+	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
+		return ret;
+	}
+
+	ret = watchdog_register_device(&st_wdog_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to register watchdog\n");
+		clk_disable_unprepare(clk);
+		return ret;
+	}
+
+	st_wdog_setup(st_wdog, true);
+
+	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
+		 st_wdog->warm_reset ? "warm" : "cold");
+
+	return ret;
+}
+
+static int st_wdog_remove(struct platform_device *pdev)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+	st_wdog_setup(st_wdog, false);
+	watchdog_unregister_device(&st_wdog_dev);
+	clk_disable_unprepare(st_wdog->clk);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int st_wdog_suspend(struct device *dev)
+{
+	if (watchdog_active(&st_wdog_dev))
+		st_wdog_stop(&st_wdog_dev);
+
+	return 0;
+}
+
+static int st_wdog_resume(struct device *dev)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+	if (watchdog_active(&st_wdog_dev)) {
+		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
+		st_wdog_start(&st_wdog_dev);
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
+			 st_wdog_suspend,
+			 st_wdog_resume);
+
+static struct platform_driver st_wdog_driver = {
+	.driver	= {
+		.name = "st-lpc-wdt",
+		.pm = &st_wdog_pm_ops,
+	},
+	.probe = st_wdog_probe,
+	.remove = st_wdog_remove,
+};
+module_platform_driver(st_wdog_driver);
+
+MODULE_AUTHOR("David Paris <david.paris@st.com>");
+MODULE_DESCRIPTION("ST LPC Watchdog Driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: David Paris <david.paris@st.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/watchdog/Kconfig  |  13 ++
 drivers/watchdog/Makefile |   1 +
 drivers/watchdog/st_wdt.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 326 insertions(+)
 create mode 100644 drivers/watchdog/st_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f57312f..5a538af 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
 	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
 	  the watchdog triggers the system will be reset.
 
+config ST_WATCHDOG
+	tristate "STMicroelectronics LPC Watchdog"
+	depends on ARCH_STI
+	depends on OF
+	select WATCHDOG_CORE
+	select MFD_ST_LPC
+	help
+	  Say Y here to include STMicroelectronics Low Power Controller
+	  (LPC) based Watchdog timer support.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called st_wdt.
+
 config TEGRA_WATCHDOG
 	tristate "Tegra watchdog"
 	depends on ARCH_TEGRA || COMPILE_TEST
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 468c320..eb19937 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
 obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
 obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
 obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
+obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
 obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
 obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
 
diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
new file mode 100644
index 0000000..7e06f15
--- /dev/null
+++ b/drivers/watchdog/st_wdt.c
@@ -0,0 +1,312 @@
+/*
+ * st-wdt.c - ST's LPC Watchdog
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: David Paris <david.paris@st.com> for STMicroelectronics
+ *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/watchdog.h>
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF			0x410
+#define LPC_LPA_START_OFF		0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF			0x510
+
+struct st_wdog_syscfg {
+	struct regmap *regmap;
+	unsigned int reset_type_reg;
+	unsigned int reset_type_mask;
+	unsigned int enable_reg;
+	unsigned int enable_mask;
+};
+
+struct st_wdog {
+	void __iomem *base;
+	struct device *dev;
+	struct st_wdog_syscfg *syscfg;
+	struct clk *clk;
+	int warm_reset;
+};
+
+static struct st_wdog_syscfg stid127_syscfg = {
+	.reset_type_reg		= 0x004,
+	.reset_type_mask	= BIT(2),
+	.enable_reg		= 0x000,
+	.enable_mask		= BIT(2),
+};
+
+static struct st_wdog_syscfg stih415_syscfg = {
+	.reset_type_reg		= 0x0B8,
+	.reset_type_mask	= BIT(6),
+	.enable_reg		= 0x0B4,
+	.enable_mask		= BIT(7),
+};
+
+static struct st_wdog_syscfg stih416_syscfg = {
+	.reset_type_reg		= 0x88C,
+	.reset_type_mask	= BIT(6),
+	.enable_reg		= 0x888,
+	.enable_mask		= BIT(7),
+};
+
+static struct st_wdog_syscfg stih407_syscfg = {
+	.enable_reg		= 0x204,
+	.enable_mask		= BIT(19),
+};
+
+static struct of_device_id st_wdog_match[] = {
+	{
+		.compatible = "st,stih407-lpc",
+		.data = (void *)&stih407_syscfg,
+	},
+	{
+		.compatible = "st,stih416-lpc",
+		.data = (void *)&stih416_syscfg,
+	},
+	{
+		.compatible = "st,stih415-lpc",
+		.data = (void *)&stih415_syscfg,
+	},
+	{
+		.compatible = "st,stid127-lpc",
+		.data = (void *)&stid127_syscfg,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, st_wdog_match);
+
+static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
+{
+	/* Type of watchdog reset - 0: Cold 1: Warm */
+	if (st_wdog->syscfg->reset_type_reg)
+		regmap_update_bits(st_wdog->syscfg->regmap,
+				   st_wdog->syscfg->reset_type_reg,
+				   st_wdog->syscfg->reset_type_mask,
+				   st_wdog->warm_reset);
+
+	/* Mask/unmask watchdog reset */
+	regmap_update_bits(st_wdog->syscfg->regmap,
+			   st_wdog->syscfg->enable_reg,
+			   st_wdog->syscfg->enable_mask,
+			   enable ? 0 : st_wdog->syscfg->enable_mask);
+}
+
+static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
+{
+	unsigned long clkrate = clk_get_rate(st_wdog->clk);
+	unsigned int maxtimeout = 0xFFFFFFFF / clkrate;
+
+	if (timeout > maxtimeout) {
+		dev_warn(st_wdog->dev,
+			 "timer overrun detected at current freq (%luHz)\n"
+			 "wdog timeout set for %ds instead of requested %uis",
+			 clkrate, maxtimeout, timeout);
+		timeout = maxtimeout;
+	}
+	timeout *= clkrate;
+
+	writel_relaxed(timeout, st_wdog->base + LPC_LPA_LSB_OFF);
+	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
+}
+
+static int st_wdog_start(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
+
+	return 0;
+}
+
+static int st_wdog_stop(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
+
+	return 0;
+}
+
+static int st_wdog_set_timeout(struct watchdog_device *wdd,
+			       unsigned int timeout)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	wdd->timeout = timeout;
+	st_wdog_load_timer(st_wdog, timeout);
+
+	return 0;
+}
+
+static int st_wdog_keepalive(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	st_wdog_load_timer(st_wdog, wdd->timeout);
+
+	return 0;
+}
+
+static const struct watchdog_info st_wdog_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "ST LPC WDT",
+};
+
+static const struct watchdog_ops st_wdog_ops = {
+	.owner		= THIS_MODULE,
+	.start		= st_wdog_start,
+	.stop		= st_wdog_stop,
+	.ping		= st_wdog_keepalive,
+	.set_timeout	= st_wdog_set_timeout,
+};
+
+static struct watchdog_device st_wdog_dev = {
+	.info		= &st_wdog_info,
+	.ops		= &st_wdog_ops,
+};
+
+static int st_wdog_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct device_node *np;
+	struct st_wdog *st_wdog;
+	struct regmap *regmap;
+	struct resource *res;
+	struct clk *clk;
+	void __iomem *base;
+	int ret;
+
+	/* This is a single node MFD device. */
+	np = pdev->dev.of_node = pdev->dev.parent->of_node;
+
+	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
+	if (!st_wdog)
+		return -ENOMEM;
+
+	match = of_match_device(st_wdog_match, &pdev->dev);
+	if (!match) {
+		dev_err(&pdev->dev, "Couldn't match device\n");
+		return -ENODEV;
+	}
+	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base)) {
+		dev_err(&pdev->dev, "Failed to ioremap base\n");
+		return PTR_ERR(base);
+	}
+
+	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+	if (IS_ERR(regmap)) {
+		dev_err(&pdev->dev, "No syscfg phandle specified\n");
+		return PTR_ERR(regmap);
+	}
+
+	clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "Unable to request clock\n");
+		return PTR_ERR(clk);
+	}
+	clk_prepare_enable(st_wdog->clk);
+
+	st_wdog->dev		= &pdev->dev;
+	st_wdog->base		= base;
+	st_wdog->clk		= clk;
+	st_wdog->syscfg->regmap = regmap;
+	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
+
+	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
+	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
+
+	/* Init Watchdog timeout with value in DT */
+	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
+		return ret;
+	}
+
+	ret = watchdog_register_device(&st_wdog_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to register watchdog\n");
+		clk_disable_unprepare(clk);
+		return ret;
+	}
+
+	st_wdog_setup(st_wdog, true);
+
+	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
+		 st_wdog->warm_reset ? "warm" : "cold");
+
+	return ret;
+}
+
+static int st_wdog_remove(struct platform_device *pdev)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+	st_wdog_setup(st_wdog, false);
+	watchdog_unregister_device(&st_wdog_dev);
+	clk_disable_unprepare(st_wdog->clk);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int st_wdog_suspend(struct device *dev)
+{
+	if (watchdog_active(&st_wdog_dev))
+		st_wdog_stop(&st_wdog_dev);
+
+	return 0;
+}
+
+static int st_wdog_resume(struct device *dev)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+	if (watchdog_active(&st_wdog_dev)) {
+		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
+		st_wdog_start(&st_wdog_dev);
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
+			 st_wdog_suspend,
+			 st_wdog_resume);
+
+static struct platform_driver st_wdog_driver = {
+	.driver	= {
+		.name = "st-lpc-wdt",
+		.pm = &st_wdog_pm_ops,
+	},
+	.probe = st_wdog_probe,
+	.remove = st_wdog_remove,
+};
+module_platform_driver(st_wdog_driver);
+
+MODULE_AUTHOR("David Paris <david.paris@st.com>");
+MODULE_DESCRIPTION("ST LPC Watchdog Driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 7/8] rtc: st: add new driver for ST's LPC RTC
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, a.zummo, rtc-linux, wim, linux-watchdog, devicetree

ST's Low Power Controller (LPC) controls two devices; watchdog and RTC.
Only one of the devices can be used at any one time.  This is enforced
by the correlating MFD driver.  This portion of the driver-set controls
the Real Time Clock.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/rtc/Kconfig      |  13 ++
 drivers/rtc/Makefile     |   1 +
 drivers/rtc/rtc-st-lpc.c | 330 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 344 insertions(+)
 create mode 100644 drivers/rtc/rtc-st-lpc.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index a168e96..aa4bd90 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1355,6 +1355,18 @@ config RTC_DRV_SIRFSOC
 	  Say "yes" here to support the real time clock on SiRF SOC chips.
 	  This driver can also be built as a module called rtc-sirfsoc.
 
+config RTC_DRV_ST_LPC
+	tristate "STMicroelectronics LPC RTC"
+	depends on ARCH_STI
+	depends on OF
+	select MFD_ST_LPC
+	help
+	  Say Y here to include STMicroelectronics Low Power Controller
+	  (LPC) based RTC support.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rtc-st-lpc.
+
 config RTC_DRV_MOXART
 	tristate "MOXA ART RTC"
 	depends on ARCH_MOXART || COMPILE_TEST
@@ -1390,4 +1402,5 @@ config RTC_DRV_HID_SENSOR_TIME
 	  rtc-hid-sensor-time.
 
 
+
 endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 56f061c..ce5860b 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -145,4 +145,5 @@ obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
 obj-$(CONFIG_RTC_DRV_XGENE)	+= rtc-xgene.o
 obj-$(CONFIG_RTC_DRV_SIRFSOC)	+= rtc-sirfsoc.o
+obj-$(CONFIG_RTC_DRV_ST_LPC)	+= rtc-st-lpc.o
 obj-$(CONFIG_RTC_DRV_MOXART)	+= rtc-moxart.o
diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c
new file mode 100644
index 0000000..60b1ab4
--- /dev/null
+++ b/drivers/rtc/rtc-st-lpc.c
@@ -0,0 +1,330 @@
+/*
+ * rtc-st-lpc.c - ST's LPC RTC, powered by the Low Power Timer
+ *
+ * Copyright (C) 2014 STMicroelectronics Limited
+ *
+ * Author: David Paris <david.paris@st.com> for STMicroelectronics
+ *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * Based on the original driver written by Stuart Menefy.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+/* Low Power Timer */
+#define LPC_LPT_LSB_OFF		0x400
+#define LPC_LPT_MSB_OFF		0x404
+#define LPC_LPT_START_OFF	0x408
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF		0x410
+#define LPC_LPA_MSB_OFF		0x414
+#define LPC_LPA_START_OFF	0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF		0x510
+#define LPC_WDT_FLAG_OFF	0x514
+
+struct st_rtc {
+	struct rtc_device *rtc_dev;
+	struct rtc_wkalrm alarm;
+	struct resource *res;
+	struct clk *clk;
+	void __iomem *ioaddr;
+	bool irq_enabled:1;
+	spinlock_t lock;
+	short irq;
+};
+
+static void st_rtc_set_hw_alarm(struct st_rtc *rtc,
+				unsigned long msb, unsigned long  lsb)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&rtc->lock, flags);
+
+	writel(1, rtc->ioaddr + LPC_WDT_OFF);
+
+	writel(msb, rtc->ioaddr + LPC_LPA_MSB_OFF);
+	writel(lsb, rtc->ioaddr + LPC_LPA_LSB_OFF);
+	writel(1, rtc->ioaddr + LPC_LPA_START_OFF);
+
+	writel(0, rtc->ioaddr + LPC_WDT_OFF);
+
+	spin_unlock_irqrestore(&rtc->lock, flags);
+}
+
+static irqreturn_t st_rtc_handler(int this_irq, void *data)
+{
+	struct st_rtc *rtc = (struct st_rtc *)data;
+
+	rtc_update_irq(rtc->rtc_dev, 1, RTC_AF);
+
+	return IRQ_HANDLED;
+}
+
+static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+	unsigned long lpt_lsb, lpt_msb;
+	unsigned long long lpt;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rtc->lock, flags);
+
+	do {
+		lpt_msb = readl(rtc->ioaddr + LPC_LPT_MSB_OFF);
+		lpt_lsb = readl(rtc->ioaddr + LPC_LPT_LSB_OFF);
+	} while (readl(rtc->ioaddr + LPC_LPT_MSB_OFF) != lpt_msb);
+
+	spin_unlock_irqrestore(&rtc->lock, flags);
+
+	lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
+	do_div(lpt, clk_get_rate(rtc->clk));
+	rtc_time_to_tm(lpt, tm);
+
+	return 0;
+}
+
+static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+	unsigned long long lpt;
+	unsigned long secs, flags;
+	int ret;
+
+	ret = rtc_tm_to_time(tm, &secs);
+	if (ret)
+		return ret;
+
+	lpt = (unsigned long long)secs * clk_get_rate(rtc->clk);
+
+	spin_lock_irqsave(&rtc->lock, flags);
+
+	writel(lpt >> 32, rtc->ioaddr + LPC_LPT_MSB_OFF);
+	writel(lpt, rtc->ioaddr + LPC_LPT_LSB_OFF);
+	writel(1, rtc->ioaddr + LPC_LPT_START_OFF);
+
+	spin_unlock_irqrestore(&rtc->lock, flags);
+
+	return 0;
+}
+
+static int st_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rtc->lock, flags);
+
+	memcpy(wkalrm, &rtc->alarm, sizeof(struct rtc_wkalrm));
+
+	spin_unlock_irqrestore(&rtc->lock, flags);
+
+	return 0;
+}
+
+static int st_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+
+	if (enabled && !rtc->irq_enabled) {
+		enable_irq(rtc->irq);
+		rtc->irq_enabled = true;
+	} else if (!enabled && rtc->irq_enabled) {
+		disable_irq(rtc->irq);
+		rtc->irq_enabled = false;
+	}
+
+	return 0;
+}
+
+static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+	struct rtc_time now;
+	unsigned long now_secs;
+	unsigned long alarm_secs;
+	unsigned long long lpa;
+
+	st_rtc_read_time(dev, &now);
+	rtc_tm_to_time(&now, &now_secs);
+	rtc_tm_to_time(&t->time, &alarm_secs);
+
+	/* Invalid alarm time */
+	if (now_secs > alarm_secs)
+		return -EINVAL;
+
+	memcpy(&rtc->alarm, t, sizeof(struct rtc_wkalrm));
+
+	/* Now many secs to fire */
+	alarm_secs -= now_secs;
+	lpa = (unsigned long long)alarm_secs * clk_get_rate(rtc->clk);
+
+	st_rtc_set_hw_alarm(rtc, lpa >> 32, lpa);
+	st_rtc_alarm_irq_enable(dev, t->enabled);
+
+	return 0;
+}
+
+static struct rtc_class_ops st_rtc_ops = {
+	.read_time		= st_rtc_read_time,
+	.set_time		= st_rtc_set_time,
+	.read_alarm		= st_rtc_read_alarm,
+	.set_alarm		= st_rtc_set_alarm,
+	.alarm_irq_enable	= st_rtc_alarm_irq_enable,
+};
+
+static int st_rtc_probe(struct platform_device *pdev)
+{
+	struct device_node *np;
+	struct st_rtc *rtc;
+	struct resource *res;
+	struct rtc_time tm_check;
+	int ret = 0;
+
+	/* This is a single [shared] node MFD device. */
+	np = pdev->dev.of_node = pdev->dev.parent->of_node;
+
+	rtc = devm_kzalloc(&pdev->dev, sizeof(struct st_rtc), GFP_KERNEL);
+	if (!rtc)
+		return -ENOMEM;
+
+	spin_lock_init(&rtc->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	rtc->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(rtc->ioaddr))
+		return PTR_ERR(rtc->ioaddr);
+
+	rtc->irq = irq_of_parse_and_map(np, 0);
+	if (!rtc->irq) {
+		dev_err(&pdev->dev, "IRQ missing or invalid\n");
+		return -EINVAL;
+	}
+
+	ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler, 0,
+			       pdev->name, rtc);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq);
+		return ret;
+	}
+
+	enable_irq_wake(rtc->irq);
+	disable_irq(rtc->irq);
+
+	rtc->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(rtc->clk)) {
+		dev_err(&pdev->dev, "Unable to request clock\n");
+		return PTR_ERR(rtc->clk);
+	}
+
+	clk_prepare_enable(rtc->clk);
+
+	device_set_wakeup_capable(&pdev->dev, 1);
+
+	platform_set_drvdata(pdev, rtc);
+
+	/*
+	 * The RTC-LPC is able to manage date.year > 2038
+	 * but currently the kernel can not manage this date!
+	 * If the RTC-LPC has a date.year > 2038 then
+	 * it's set to the epoch "Jan 1st 2000"
+	 */
+	st_rtc_read_time(&pdev->dev, &tm_check);
+
+	if (tm_check.tm_year >=  (2038 - 1900)) {
+		memset(&tm_check, 0, sizeof(tm_check));
+		tm_check.tm_year = 100;
+		tm_check.tm_mday = 1;
+		st_rtc_set_time(&pdev->dev, &tm_check);
+	}
+
+	rtc->rtc_dev = rtc_device_register("st-lpc-rtc", &pdev->dev,
+					   &st_rtc_ops, THIS_MODULE);
+	if (IS_ERR(rtc->rtc_dev)) {
+		clk_disable_unprepare(rtc->clk);
+		return PTR_ERR(rtc->rtc_dev);
+	}
+
+	return 0;
+}
+
+static int st_rtc_remove(struct platform_device *pdev)
+{
+	struct st_rtc *rtc = platform_get_drvdata(pdev);
+
+	if (likely(rtc->rtc_dev))
+		rtc_device_unregister(rtc->rtc_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int st_rtc_suspend(struct device *dev)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		return 0;
+
+	writel(1, rtc->ioaddr + LPC_WDT_OFF);
+	writel(0, rtc->ioaddr + LPC_LPA_START_OFF);
+	writel(0, rtc->ioaddr + LPC_WDT_OFF);
+
+	return 0;
+}
+
+static int st_rtc_resume(struct device *dev)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+
+	rtc_alarm_irq_enable(rtc->rtc_dev, 0);
+
+	/*
+	 * clean 'rtc->alarm' to allow a new
+	 * a new .set_alarm to the upper RTC layer
+	 */
+	memset(&rtc->alarm, 0, sizeof(struct rtc_wkalrm));
+
+	writel(0, rtc->ioaddr + LPC_LPA_MSB_OFF);
+	writel(0, rtc->ioaddr + LPC_LPA_LSB_OFF);
+	writel(1, rtc->ioaddr + LPC_WDT_OFF);
+	writel(1, rtc->ioaddr + LPC_LPA_START_OFF);
+	writel(0, rtc->ioaddr + LPC_WDT_OFF);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_rtc_pm_ops, st_rtc_suspend, st_rtc_resume);
+
+static struct platform_driver st_rtc_platform_driver = {
+	.driver = {
+		.name = "st-lpc-rtc",
+		.pm = &st_rtc_pm_ops,
+	},
+	.probe = st_rtc_probe,
+	.remove = st_rtc_remove,
+};
+
+module_platform_driver(st_rtc_platform_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics LPC RTC driver");
+MODULE_AUTHOR("David Paris <david.paris@st.com>");
+MODULE_LICENSE("GPLv2");
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 7/8] rtc: st: add new driver for ST's LPC RTC
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	a.zummo-BfzFCNDTiLLj+vYz1yj4TQ, rtc-linux-/JYPxA39Uh5TLH3MbocFFw,
	wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

ST's Low Power Controller (LPC) controls two devices; watchdog and RTC.
Only one of the devices can be used at any one time.  This is enforced
by the correlating MFD driver.  This portion of the driver-set controls
the Real Time Clock.

Signed-off-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 drivers/rtc/Kconfig      |  13 ++
 drivers/rtc/Makefile     |   1 +
 drivers/rtc/rtc-st-lpc.c | 330 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 344 insertions(+)
 create mode 100644 drivers/rtc/rtc-st-lpc.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index a168e96..aa4bd90 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1355,6 +1355,18 @@ config RTC_DRV_SIRFSOC
 	  Say "yes" here to support the real time clock on SiRF SOC chips.
 	  This driver can also be built as a module called rtc-sirfsoc.
 
+config RTC_DRV_ST_LPC
+	tristate "STMicroelectronics LPC RTC"
+	depends on ARCH_STI
+	depends on OF
+	select MFD_ST_LPC
+	help
+	  Say Y here to include STMicroelectronics Low Power Controller
+	  (LPC) based RTC support.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rtc-st-lpc.
+
 config RTC_DRV_MOXART
 	tristate "MOXA ART RTC"
 	depends on ARCH_MOXART || COMPILE_TEST
@@ -1390,4 +1402,5 @@ config RTC_DRV_HID_SENSOR_TIME
 	  rtc-hid-sensor-time.
 
 
+
 endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 56f061c..ce5860b 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -145,4 +145,5 @@ obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
 obj-$(CONFIG_RTC_DRV_XGENE)	+= rtc-xgene.o
 obj-$(CONFIG_RTC_DRV_SIRFSOC)	+= rtc-sirfsoc.o
+obj-$(CONFIG_RTC_DRV_ST_LPC)	+= rtc-st-lpc.o
 obj-$(CONFIG_RTC_DRV_MOXART)	+= rtc-moxart.o
diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c
new file mode 100644
index 0000000..60b1ab4
--- /dev/null
+++ b/drivers/rtc/rtc-st-lpc.c
@@ -0,0 +1,330 @@
+/*
+ * rtc-st-lpc.c - ST's LPC RTC, powered by the Low Power Timer
+ *
+ * Copyright (C) 2014 STMicroelectronics Limited
+ *
+ * Author: David Paris <david.paris-qxv4g6HH51o@public.gmane.org> for STMicroelectronics
+ *         Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> for STMicroelectronics
+ *
+ * Based on the original driver written by Stuart Menefy.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+/* Low Power Timer */
+#define LPC_LPT_LSB_OFF		0x400
+#define LPC_LPT_MSB_OFF		0x404
+#define LPC_LPT_START_OFF	0x408
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF		0x410
+#define LPC_LPA_MSB_OFF		0x414
+#define LPC_LPA_START_OFF	0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF		0x510
+#define LPC_WDT_FLAG_OFF	0x514
+
+struct st_rtc {
+	struct rtc_device *rtc_dev;
+	struct rtc_wkalrm alarm;
+	struct resource *res;
+	struct clk *clk;
+	void __iomem *ioaddr;
+	bool irq_enabled:1;
+	spinlock_t lock;
+	short irq;
+};
+
+static void st_rtc_set_hw_alarm(struct st_rtc *rtc,
+				unsigned long msb, unsigned long  lsb)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&rtc->lock, flags);
+
+	writel(1, rtc->ioaddr + LPC_WDT_OFF);
+
+	writel(msb, rtc->ioaddr + LPC_LPA_MSB_OFF);
+	writel(lsb, rtc->ioaddr + LPC_LPA_LSB_OFF);
+	writel(1, rtc->ioaddr + LPC_LPA_START_OFF);
+
+	writel(0, rtc->ioaddr + LPC_WDT_OFF);
+
+	spin_unlock_irqrestore(&rtc->lock, flags);
+}
+
+static irqreturn_t st_rtc_handler(int this_irq, void *data)
+{
+	struct st_rtc *rtc = (struct st_rtc *)data;
+
+	rtc_update_irq(rtc->rtc_dev, 1, RTC_AF);
+
+	return IRQ_HANDLED;
+}
+
+static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+	unsigned long lpt_lsb, lpt_msb;
+	unsigned long long lpt;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rtc->lock, flags);
+
+	do {
+		lpt_msb = readl(rtc->ioaddr + LPC_LPT_MSB_OFF);
+		lpt_lsb = readl(rtc->ioaddr + LPC_LPT_LSB_OFF);
+	} while (readl(rtc->ioaddr + LPC_LPT_MSB_OFF) != lpt_msb);
+
+	spin_unlock_irqrestore(&rtc->lock, flags);
+
+	lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
+	do_div(lpt, clk_get_rate(rtc->clk));
+	rtc_time_to_tm(lpt, tm);
+
+	return 0;
+}
+
+static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+	unsigned long long lpt;
+	unsigned long secs, flags;
+	int ret;
+
+	ret = rtc_tm_to_time(tm, &secs);
+	if (ret)
+		return ret;
+
+	lpt = (unsigned long long)secs * clk_get_rate(rtc->clk);
+
+	spin_lock_irqsave(&rtc->lock, flags);
+
+	writel(lpt >> 32, rtc->ioaddr + LPC_LPT_MSB_OFF);
+	writel(lpt, rtc->ioaddr + LPC_LPT_LSB_OFF);
+	writel(1, rtc->ioaddr + LPC_LPT_START_OFF);
+
+	spin_unlock_irqrestore(&rtc->lock, flags);
+
+	return 0;
+}
+
+static int st_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rtc->lock, flags);
+
+	memcpy(wkalrm, &rtc->alarm, sizeof(struct rtc_wkalrm));
+
+	spin_unlock_irqrestore(&rtc->lock, flags);
+
+	return 0;
+}
+
+static int st_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+
+	if (enabled && !rtc->irq_enabled) {
+		enable_irq(rtc->irq);
+		rtc->irq_enabled = true;
+	} else if (!enabled && rtc->irq_enabled) {
+		disable_irq(rtc->irq);
+		rtc->irq_enabled = false;
+	}
+
+	return 0;
+}
+
+static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+	struct rtc_time now;
+	unsigned long now_secs;
+	unsigned long alarm_secs;
+	unsigned long long lpa;
+
+	st_rtc_read_time(dev, &now);
+	rtc_tm_to_time(&now, &now_secs);
+	rtc_tm_to_time(&t->time, &alarm_secs);
+
+	/* Invalid alarm time */
+	if (now_secs > alarm_secs)
+		return -EINVAL;
+
+	memcpy(&rtc->alarm, t, sizeof(struct rtc_wkalrm));
+
+	/* Now many secs to fire */
+	alarm_secs -= now_secs;
+	lpa = (unsigned long long)alarm_secs * clk_get_rate(rtc->clk);
+
+	st_rtc_set_hw_alarm(rtc, lpa >> 32, lpa);
+	st_rtc_alarm_irq_enable(dev, t->enabled);
+
+	return 0;
+}
+
+static struct rtc_class_ops st_rtc_ops = {
+	.read_time		= st_rtc_read_time,
+	.set_time		= st_rtc_set_time,
+	.read_alarm		= st_rtc_read_alarm,
+	.set_alarm		= st_rtc_set_alarm,
+	.alarm_irq_enable	= st_rtc_alarm_irq_enable,
+};
+
+static int st_rtc_probe(struct platform_device *pdev)
+{
+	struct device_node *np;
+	struct st_rtc *rtc;
+	struct resource *res;
+	struct rtc_time tm_check;
+	int ret = 0;
+
+	/* This is a single [shared] node MFD device. */
+	np = pdev->dev.of_node = pdev->dev.parent->of_node;
+
+	rtc = devm_kzalloc(&pdev->dev, sizeof(struct st_rtc), GFP_KERNEL);
+	if (!rtc)
+		return -ENOMEM;
+
+	spin_lock_init(&rtc->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	rtc->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(rtc->ioaddr))
+		return PTR_ERR(rtc->ioaddr);
+
+	rtc->irq = irq_of_parse_and_map(np, 0);
+	if (!rtc->irq) {
+		dev_err(&pdev->dev, "IRQ missing or invalid\n");
+		return -EINVAL;
+	}
+
+	ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler, 0,
+			       pdev->name, rtc);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq);
+		return ret;
+	}
+
+	enable_irq_wake(rtc->irq);
+	disable_irq(rtc->irq);
+
+	rtc->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(rtc->clk)) {
+		dev_err(&pdev->dev, "Unable to request clock\n");
+		return PTR_ERR(rtc->clk);
+	}
+
+	clk_prepare_enable(rtc->clk);
+
+	device_set_wakeup_capable(&pdev->dev, 1);
+
+	platform_set_drvdata(pdev, rtc);
+
+	/*
+	 * The RTC-LPC is able to manage date.year > 2038
+	 * but currently the kernel can not manage this date!
+	 * If the RTC-LPC has a date.year > 2038 then
+	 * it's set to the epoch "Jan 1st 2000"
+	 */
+	st_rtc_read_time(&pdev->dev, &tm_check);
+
+	if (tm_check.tm_year >=  (2038 - 1900)) {
+		memset(&tm_check, 0, sizeof(tm_check));
+		tm_check.tm_year = 100;
+		tm_check.tm_mday = 1;
+		st_rtc_set_time(&pdev->dev, &tm_check);
+	}
+
+	rtc->rtc_dev = rtc_device_register("st-lpc-rtc", &pdev->dev,
+					   &st_rtc_ops, THIS_MODULE);
+	if (IS_ERR(rtc->rtc_dev)) {
+		clk_disable_unprepare(rtc->clk);
+		return PTR_ERR(rtc->rtc_dev);
+	}
+
+	return 0;
+}
+
+static int st_rtc_remove(struct platform_device *pdev)
+{
+	struct st_rtc *rtc = platform_get_drvdata(pdev);
+
+	if (likely(rtc->rtc_dev))
+		rtc_device_unregister(rtc->rtc_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int st_rtc_suspend(struct device *dev)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		return 0;
+
+	writel(1, rtc->ioaddr + LPC_WDT_OFF);
+	writel(0, rtc->ioaddr + LPC_LPA_START_OFF);
+	writel(0, rtc->ioaddr + LPC_WDT_OFF);
+
+	return 0;
+}
+
+static int st_rtc_resume(struct device *dev)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+
+	rtc_alarm_irq_enable(rtc->rtc_dev, 0);
+
+	/*
+	 * clean 'rtc->alarm' to allow a new
+	 * a new .set_alarm to the upper RTC layer
+	 */
+	memset(&rtc->alarm, 0, sizeof(struct rtc_wkalrm));
+
+	writel(0, rtc->ioaddr + LPC_LPA_MSB_OFF);
+	writel(0, rtc->ioaddr + LPC_LPA_LSB_OFF);
+	writel(1, rtc->ioaddr + LPC_WDT_OFF);
+	writel(1, rtc->ioaddr + LPC_LPA_START_OFF);
+	writel(0, rtc->ioaddr + LPC_WDT_OFF);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_rtc_pm_ops, st_rtc_suspend, st_rtc_resume);
+
+static struct platform_driver st_rtc_platform_driver = {
+	.driver = {
+		.name = "st-lpc-rtc",
+		.pm = &st_rtc_pm_ops,
+	},
+	.probe = st_rtc_probe,
+	.remove = st_rtc_remove,
+};
+
+module_platform_driver(st_rtc_platform_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics LPC RTC driver");
+MODULE_AUTHOR("David Paris <david.paris-qxv4g6HH51o@public.gmane.org>");
+MODULE_LICENSE("GPLv2");
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 7/8] rtc: st: add new driver for ST's LPC RTC
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel

ST's Low Power Controller (LPC) controls two devices; watchdog and RTC.
Only one of the devices can be used at any one time.  This is enforced
by the correlating MFD driver.  This portion of the driver-set controls
the Real Time Clock.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/rtc/Kconfig      |  13 ++
 drivers/rtc/Makefile     |   1 +
 drivers/rtc/rtc-st-lpc.c | 330 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 344 insertions(+)
 create mode 100644 drivers/rtc/rtc-st-lpc.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index a168e96..aa4bd90 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1355,6 +1355,18 @@ config RTC_DRV_SIRFSOC
 	  Say "yes" here to support the real time clock on SiRF SOC chips.
 	  This driver can also be built as a module called rtc-sirfsoc.
 
+config RTC_DRV_ST_LPC
+	tristate "STMicroelectronics LPC RTC"
+	depends on ARCH_STI
+	depends on OF
+	select MFD_ST_LPC
+	help
+	  Say Y here to include STMicroelectronics Low Power Controller
+	  (LPC) based RTC support.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called rtc-st-lpc.
+
 config RTC_DRV_MOXART
 	tristate "MOXA ART RTC"
 	depends on ARCH_MOXART || COMPILE_TEST
@@ -1390,4 +1402,5 @@ config RTC_DRV_HID_SENSOR_TIME
 	  rtc-hid-sensor-time.
 
 
+
 endif # RTC_CLASS
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 56f061c..ce5860b 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -145,4 +145,5 @@ obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
 obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
 obj-$(CONFIG_RTC_DRV_XGENE)	+= rtc-xgene.o
 obj-$(CONFIG_RTC_DRV_SIRFSOC)	+= rtc-sirfsoc.o
+obj-$(CONFIG_RTC_DRV_ST_LPC)	+= rtc-st-lpc.o
 obj-$(CONFIG_RTC_DRV_MOXART)	+= rtc-moxart.o
diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c
new file mode 100644
index 0000000..60b1ab4
--- /dev/null
+++ b/drivers/rtc/rtc-st-lpc.c
@@ -0,0 +1,330 @@
+/*
+ * rtc-st-lpc.c - ST's LPC RTC, powered by the Low Power Timer
+ *
+ * Copyright (C) 2014 STMicroelectronics Limited
+ *
+ * Author: David Paris <david.paris@st.com> for STMicroelectronics
+ *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * Based on the original driver written by Stuart Menefy.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/rtc.h>
+
+/* Low Power Timer */
+#define LPC_LPT_LSB_OFF		0x400
+#define LPC_LPT_MSB_OFF		0x404
+#define LPC_LPT_START_OFF	0x408
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF		0x410
+#define LPC_LPA_MSB_OFF		0x414
+#define LPC_LPA_START_OFF	0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF		0x510
+#define LPC_WDT_FLAG_OFF	0x514
+
+struct st_rtc {
+	struct rtc_device *rtc_dev;
+	struct rtc_wkalrm alarm;
+	struct resource *res;
+	struct clk *clk;
+	void __iomem *ioaddr;
+	bool irq_enabled:1;
+	spinlock_t lock;
+	short irq;
+};
+
+static void st_rtc_set_hw_alarm(struct st_rtc *rtc,
+				unsigned long msb, unsigned long  lsb)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&rtc->lock, flags);
+
+	writel(1, rtc->ioaddr + LPC_WDT_OFF);
+
+	writel(msb, rtc->ioaddr + LPC_LPA_MSB_OFF);
+	writel(lsb, rtc->ioaddr + LPC_LPA_LSB_OFF);
+	writel(1, rtc->ioaddr + LPC_LPA_START_OFF);
+
+	writel(0, rtc->ioaddr + LPC_WDT_OFF);
+
+	spin_unlock_irqrestore(&rtc->lock, flags);
+}
+
+static irqreturn_t st_rtc_handler(int this_irq, void *data)
+{
+	struct st_rtc *rtc = (struct st_rtc *)data;
+
+	rtc_update_irq(rtc->rtc_dev, 1, RTC_AF);
+
+	return IRQ_HANDLED;
+}
+
+static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+	unsigned long lpt_lsb, lpt_msb;
+	unsigned long long lpt;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rtc->lock, flags);
+
+	do {
+		lpt_msb = readl(rtc->ioaddr + LPC_LPT_MSB_OFF);
+		lpt_lsb = readl(rtc->ioaddr + LPC_LPT_LSB_OFF);
+	} while (readl(rtc->ioaddr + LPC_LPT_MSB_OFF) != lpt_msb);
+
+	spin_unlock_irqrestore(&rtc->lock, flags);
+
+	lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
+	do_div(lpt, clk_get_rate(rtc->clk));
+	rtc_time_to_tm(lpt, tm);
+
+	return 0;
+}
+
+static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+	unsigned long long lpt;
+	unsigned long secs, flags;
+	int ret;
+
+	ret = rtc_tm_to_time(tm, &secs);
+	if (ret)
+		return ret;
+
+	lpt = (unsigned long long)secs * clk_get_rate(rtc->clk);
+
+	spin_lock_irqsave(&rtc->lock, flags);
+
+	writel(lpt >> 32, rtc->ioaddr + LPC_LPT_MSB_OFF);
+	writel(lpt, rtc->ioaddr + LPC_LPT_LSB_OFF);
+	writel(1, rtc->ioaddr + LPC_LPT_START_OFF);
+
+	spin_unlock_irqrestore(&rtc->lock, flags);
+
+	return 0;
+}
+
+static int st_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+	unsigned long flags;
+
+	spin_lock_irqsave(&rtc->lock, flags);
+
+	memcpy(wkalrm, &rtc->alarm, sizeof(struct rtc_wkalrm));
+
+	spin_unlock_irqrestore(&rtc->lock, flags);
+
+	return 0;
+}
+
+static int st_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+
+	if (enabled && !rtc->irq_enabled) {
+		enable_irq(rtc->irq);
+		rtc->irq_enabled = true;
+	} else if (!enabled && rtc->irq_enabled) {
+		disable_irq(rtc->irq);
+		rtc->irq_enabled = false;
+	}
+
+	return 0;
+}
+
+static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+	struct rtc_time now;
+	unsigned long now_secs;
+	unsigned long alarm_secs;
+	unsigned long long lpa;
+
+	st_rtc_read_time(dev, &now);
+	rtc_tm_to_time(&now, &now_secs);
+	rtc_tm_to_time(&t->time, &alarm_secs);
+
+	/* Invalid alarm time */
+	if (now_secs > alarm_secs)
+		return -EINVAL;
+
+	memcpy(&rtc->alarm, t, sizeof(struct rtc_wkalrm));
+
+	/* Now many secs to fire */
+	alarm_secs -= now_secs;
+	lpa = (unsigned long long)alarm_secs * clk_get_rate(rtc->clk);
+
+	st_rtc_set_hw_alarm(rtc, lpa >> 32, lpa);
+	st_rtc_alarm_irq_enable(dev, t->enabled);
+
+	return 0;
+}
+
+static struct rtc_class_ops st_rtc_ops = {
+	.read_time		= st_rtc_read_time,
+	.set_time		= st_rtc_set_time,
+	.read_alarm		= st_rtc_read_alarm,
+	.set_alarm		= st_rtc_set_alarm,
+	.alarm_irq_enable	= st_rtc_alarm_irq_enable,
+};
+
+static int st_rtc_probe(struct platform_device *pdev)
+{
+	struct device_node *np;
+	struct st_rtc *rtc;
+	struct resource *res;
+	struct rtc_time tm_check;
+	int ret = 0;
+
+	/* This is a single [shared] node MFD device. */
+	np = pdev->dev.of_node = pdev->dev.parent->of_node;
+
+	rtc = devm_kzalloc(&pdev->dev, sizeof(struct st_rtc), GFP_KERNEL);
+	if (!rtc)
+		return -ENOMEM;
+
+	spin_lock_init(&rtc->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	rtc->ioaddr = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(rtc->ioaddr))
+		return PTR_ERR(rtc->ioaddr);
+
+	rtc->irq = irq_of_parse_and_map(np, 0);
+	if (!rtc->irq) {
+		dev_err(&pdev->dev, "IRQ missing or invalid\n");
+		return -EINVAL;
+	}
+
+	ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler, 0,
+			       pdev->name, rtc);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq);
+		return ret;
+	}
+
+	enable_irq_wake(rtc->irq);
+	disable_irq(rtc->irq);
+
+	rtc->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(rtc->clk)) {
+		dev_err(&pdev->dev, "Unable to request clock\n");
+		return PTR_ERR(rtc->clk);
+	}
+
+	clk_prepare_enable(rtc->clk);
+
+	device_set_wakeup_capable(&pdev->dev, 1);
+
+	platform_set_drvdata(pdev, rtc);
+
+	/*
+	 * The RTC-LPC is able to manage date.year > 2038
+	 * but currently the kernel can not manage this date!
+	 * If the RTC-LPC has a date.year > 2038 then
+	 * it's set to the epoch "Jan 1st 2000"
+	 */
+	st_rtc_read_time(&pdev->dev, &tm_check);
+
+	if (tm_check.tm_year >=  (2038 - 1900)) {
+		memset(&tm_check, 0, sizeof(tm_check));
+		tm_check.tm_year = 100;
+		tm_check.tm_mday = 1;
+		st_rtc_set_time(&pdev->dev, &tm_check);
+	}
+
+	rtc->rtc_dev = rtc_device_register("st-lpc-rtc", &pdev->dev,
+					   &st_rtc_ops, THIS_MODULE);
+	if (IS_ERR(rtc->rtc_dev)) {
+		clk_disable_unprepare(rtc->clk);
+		return PTR_ERR(rtc->rtc_dev);
+	}
+
+	return 0;
+}
+
+static int st_rtc_remove(struct platform_device *pdev)
+{
+	struct st_rtc *rtc = platform_get_drvdata(pdev);
+
+	if (likely(rtc->rtc_dev))
+		rtc_device_unregister(rtc->rtc_dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int st_rtc_suspend(struct device *dev)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+
+	if (device_may_wakeup(dev))
+		return 0;
+
+	writel(1, rtc->ioaddr + LPC_WDT_OFF);
+	writel(0, rtc->ioaddr + LPC_LPA_START_OFF);
+	writel(0, rtc->ioaddr + LPC_WDT_OFF);
+
+	return 0;
+}
+
+static int st_rtc_resume(struct device *dev)
+{
+	struct st_rtc *rtc = dev_get_drvdata(dev);
+
+	rtc_alarm_irq_enable(rtc->rtc_dev, 0);
+
+	/*
+	 * clean 'rtc->alarm' to allow a new
+	 * a new .set_alarm to the upper RTC layer
+	 */
+	memset(&rtc->alarm, 0, sizeof(struct rtc_wkalrm));
+
+	writel(0, rtc->ioaddr + LPC_LPA_MSB_OFF);
+	writel(0, rtc->ioaddr + LPC_LPA_LSB_OFF);
+	writel(1, rtc->ioaddr + LPC_WDT_OFF);
+	writel(1, rtc->ioaddr + LPC_LPA_START_OFF);
+	writel(0, rtc->ioaddr + LPC_WDT_OFF);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_rtc_pm_ops, st_rtc_suspend, st_rtc_resume);
+
+static struct platform_driver st_rtc_platform_driver = {
+	.driver = {
+		.name = "st-lpc-rtc",
+		.pm = &st_rtc_pm_ops,
+	},
+	.probe = st_rtc_probe,
+	.remove = st_rtc_remove,
+};
+
+module_platform_driver(st_rtc_platform_driver);
+
+MODULE_DESCRIPTION("STMicroelectronics LPC RTC driver");
+MODULE_AUTHOR("David Paris <david.paris@st.com>");
+MODULE_LICENSE("GPLv2");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 8/8] ARM: STi: DT: STiH407: Add Device Tree node for the LPC
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, a.zummo, rtc-linux, wim, linux-watchdog,
	devicetree, David Paris

On current ST platforms the LPC controls a number of functions.  This
patch enables support for the LPC Watchdog and LPC RTC devices on LPC1
and LPC2 respectively.

Signed-off-by: David Paris <david.paris@st.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/boot/dts/stih407.dtsi | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/arch/arm/boot/dts/stih407.dtsi b/arch/arm/boot/dts/stih407.dtsi
index f539627a..a2bba83 100644
--- a/arch/arm/boot/dts/stih407.dtsi
+++ b/arch/arm/boot/dts/stih407.dtsi
@@ -8,6 +8,7 @@
  */
 #include "stih407-clock.dtsi"
 #include "stih407-pinctrl.dtsi"
+#include <dt-bindings/mfd/st-lpc.h>
 / {
 	#address-cells = <1>;
 	#size-cells = <1>;
@@ -259,5 +260,24 @@
 
 			status = "disabled";
 		};
+
+		/* Watchdog and Real-Time Clock */
+		lpc@8787000 {
+			compatible = "st,stih407-lpc";
+			reg = <0x8787000 0x1000>;
+			interrupts = <GIC_SPI 129 IRQ_TYPE_EDGE_RISING>;
+			clocks = <&clk_s_d3_flexgen CLK_LPC_0>;
+			timeout-sec = <600>;
+			st,syscfg = <&syscfg_core>;
+			st,lpc-mode = <ST_LPC_MODE_WDT>;
+		};
+
+		lpc@8788000 {
+			compatible = "st,stih407-lpc";
+			reg = <0x8788000 0x1000>;
+			interrupts = <GIC_SPI 130 IRQ_TYPE_EDGE_RISING>;
+			clocks = <&clk_s_d3_flexgen CLK_LPC_1>;
+			st,lpc-mode = <ST_LPC_MODE_RTC>;
+		};
 	};
 };
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 8/8] ARM: STi: DT: STiH407: Add Device Tree node for the LPC
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	a.zummo-BfzFCNDTiLLj+vYz1yj4TQ, rtc-linux-/JYPxA39Uh5TLH3MbocFFw,
	wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, David Paris

On current ST platforms the LPC controls a number of functions.  This
patch enables support for the LPC Watchdog and LPC RTC devices on LPC1
and LPC2 respectively.

Signed-off-by: David Paris <david.paris-qxv4g6HH51o@public.gmane.org>
Signed-off-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 arch/arm/boot/dts/stih407.dtsi | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/arch/arm/boot/dts/stih407.dtsi b/arch/arm/boot/dts/stih407.dtsi
index f539627a..a2bba83 100644
--- a/arch/arm/boot/dts/stih407.dtsi
+++ b/arch/arm/boot/dts/stih407.dtsi
@@ -8,6 +8,7 @@
  */
 #include "stih407-clock.dtsi"
 #include "stih407-pinctrl.dtsi"
+#include <dt-bindings/mfd/st-lpc.h>
 / {
 	#address-cells = <1>;
 	#size-cells = <1>;
@@ -259,5 +260,24 @@
 
 			status = "disabled";
 		};
+
+		/* Watchdog and Real-Time Clock */
+		lpc@8787000 {
+			compatible = "st,stih407-lpc";
+			reg = <0x8787000 0x1000>;
+			interrupts = <GIC_SPI 129 IRQ_TYPE_EDGE_RISING>;
+			clocks = <&clk_s_d3_flexgen CLK_LPC_0>;
+			timeout-sec = <600>;
+			st,syscfg = <&syscfg_core>;
+			st,lpc-mode = <ST_LPC_MODE_WDT>;
+		};
+
+		lpc@8788000 {
+			compatible = "st,stih407-lpc";
+			reg = <0x8788000 0x1000>;
+			interrupts = <GIC_SPI 130 IRQ_TYPE_EDGE_RISING>;
+			clocks = <&clk_s_d3_flexgen CLK_LPC_1>;
+			st,lpc-mode = <ST_LPC_MODE_RTC>;
+		};
 	};
 };
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 8/8] ARM: STi: DT: STiH407: Add Device Tree node for the LPC
@ 2014-12-15 11:25   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 11:25 UTC (permalink / raw)
  To: linux-arm-kernel

On current ST platforms the LPC controls a number of functions.  This
patch enables support for the LPC Watchdog and LPC RTC devices on LPC1
and LPC2 respectively.

Signed-off-by: David Paris <david.paris@st.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 arch/arm/boot/dts/stih407.dtsi | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/arch/arm/boot/dts/stih407.dtsi b/arch/arm/boot/dts/stih407.dtsi
index f539627a..a2bba83 100644
--- a/arch/arm/boot/dts/stih407.dtsi
+++ b/arch/arm/boot/dts/stih407.dtsi
@@ -8,6 +8,7 @@
  */
 #include "stih407-clock.dtsi"
 #include "stih407-pinctrl.dtsi"
+#include <dt-bindings/mfd/st-lpc.h>
 / {
 	#address-cells = <1>;
 	#size-cells = <1>;
@@ -259,5 +260,24 @@
 
 			status = "disabled";
 		};
+
+		/* Watchdog and Real-Time Clock */
+		lpc at 8787000 {
+			compatible = "st,stih407-lpc";
+			reg = <0x8787000 0x1000>;
+			interrupts = <GIC_SPI 129 IRQ_TYPE_EDGE_RISING>;
+			clocks = <&clk_s_d3_flexgen CLK_LPC_0>;
+			timeout-sec = <600>;
+			st,syscfg = <&syscfg_core>;
+			st,lpc-mode = <ST_LPC_MODE_WDT>;
+		};
+
+		lpc at 8788000 {
+			compatible = "st,stih407-lpc";
+			reg = <0x8788000 0x1000>;
+			interrupts = <GIC_SPI 130 IRQ_TYPE_EDGE_RISING>;
+			clocks = <&clk_s_d3_flexgen CLK_LPC_1>;
+			st,lpc-mode = <ST_LPC_MODE_RTC>;
+		};
 	};
 };
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* Re: [STLinux Kernel] [PATCH 7/8] rtc: st: add new driver for ST's LPC RTC
@ 2014-12-15 12:28     ` David Paris
  0 siblings, 0 replies; 69+ messages in thread
From: David Paris @ 2014-12-15 12:28 UTC (permalink / raw)
  To: Lee Jones, linux-arm-kernel, linux-kernel
  Cc: a.zummo, kernel, rtc-linux, devicetree, wim, linux-watchdog

Hi Lee,

     See my comment below.
     David

On 12/15/2014 12:25 PM, Lee Jones wrote:
> ST's Low Power Controller (LPC) controls two devices; watchdog and RTC.
> Only one of the devices can be used at any one time.  This is enforced
> by the correlating MFD driver.  This portion of the driver-set controls
> the Real Time Clock.
>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>   drivers/rtc/Kconfig      |  13 ++
>   drivers/rtc/Makefile     |   1 +
>   drivers/rtc/rtc-st-lpc.c | 330 +++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 344 insertions(+)
>   create mode 100644 drivers/rtc/rtc-st-lpc.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index a168e96..aa4bd90 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -1355,6 +1355,18 @@ config RTC_DRV_SIRFSOC
>   	  Say "yes" here to support the real time clock on SiRF SOC chips.
>   	  This driver can also be built as a module called rtc-sirfsoc.
>   
> +config RTC_DRV_ST_LPC
> +	tristate "STMicroelectronics LPC RTC"
> +	depends on ARCH_STI
> +	depends on OF
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based RTC support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called rtc-st-lpc.
> +
>   config RTC_DRV_MOXART
>   	tristate "MOXA ART RTC"
>   	depends on ARCH_MOXART || COMPILE_TEST
> @@ -1390,4 +1402,5 @@ config RTC_DRV_HID_SENSOR_TIME
>   	  rtc-hid-sensor-time.
>   
>   
> +
>   endif # RTC_CLASS
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 56f061c..ce5860b 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -145,4 +145,5 @@ obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
>   obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
>   obj-$(CONFIG_RTC_DRV_XGENE)	+= rtc-xgene.o
>   obj-$(CONFIG_RTC_DRV_SIRFSOC)	+= rtc-sirfsoc.o
> +obj-$(CONFIG_RTC_DRV_ST_LPC)	+= rtc-st-lpc.o
>   obj-$(CONFIG_RTC_DRV_MOXART)	+= rtc-moxart.o
> diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c
> new file mode 100644
> index 0000000..60b1ab4
> --- /dev/null
> +++ b/drivers/rtc/rtc-st-lpc.c
> @@ -0,0 +1,330 @@
> +/*
> + * rtc-st-lpc.c - ST's LPC RTC, powered by the Low Power Timer
> + *
> + * Copyright (C) 2014 STMicroelectronics Limited
> + *
> + * Author: David Paris <david.paris@st.com> for STMicroelectronics
> + *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
> + *
> + * Based on the original driver written by Stuart Menefy.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/rtc.h>
> +
> +/* Low Power Timer */
> +#define LPC_LPT_LSB_OFF		0x400
> +#define LPC_LPT_MSB_OFF		0x404
> +#define LPC_LPT_START_OFF	0x408
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF		0x410
> +#define LPC_LPA_MSB_OFF		0x414
> +#define LPC_LPA_START_OFF	0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF		0x510
> +#define LPC_WDT_FLAG_OFF	0x514
> +
> +struct st_rtc {
> +	struct rtc_device *rtc_dev;
> +	struct rtc_wkalrm alarm;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *ioaddr;
> +	bool irq_enabled:1;
> +	spinlock_t lock;
> +	short irq;
> +};
> +
> +static void st_rtc_set_hw_alarm(struct st_rtc *rtc,
> +				unsigned long msb, unsigned long  lsb)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&rtc->lock, flags);
> +
> +	writel(1, rtc->ioaddr + LPC_WDT_OFF);
> +
> +	writel(msb, rtc->ioaddr + LPC_LPA_MSB_OFF);
> +	writel(lsb, rtc->ioaddr + LPC_LPA_LSB_OFF);
> +	writel(1, rtc->ioaddr + LPC_LPA_START_OFF);
> +
> +	writel(0, rtc->ioaddr + LPC_WDT_OFF);
> +
> +	spin_unlock_irqrestore(&rtc->lock, flags);
> +}
> +
> +static irqreturn_t st_rtc_handler(int this_irq, void *data)
> +{
> +	struct st_rtc *rtc = (struct st_rtc *)data;
> +
> +	rtc_update_irq(rtc->rtc_dev, 1, RTC_AF);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +	unsigned long lpt_lsb, lpt_msb;
> +	unsigned long long lpt;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&rtc->lock, flags);
> +
> +	do {
> +		lpt_msb = readl(rtc->ioaddr + LPC_LPT_MSB_OFF);
> +		lpt_lsb = readl(rtc->ioaddr + LPC_LPT_LSB_OFF);
> +	} while (readl(rtc->ioaddr + LPC_LPT_MSB_OFF) != lpt_msb);
> +
> +	spin_unlock_irqrestore(&rtc->lock, flags);
> +
> +	lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
> +	do_div(lpt, clk_get_rate(rtc->clk));
> +	rtc_time_to_tm(lpt, tm);
> +
> +	return 0;
> +}
> +
> +static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +	unsigned long long lpt;
> +	unsigned long secs, flags;
> +	int ret;
> +
> +	ret = rtc_tm_to_time(tm, &secs);
> +	if (ret)
> +		return ret;
> +
> +	lpt = (unsigned long long)secs * clk_get_rate(rtc->clk);
> +
> +	spin_lock_irqsave(&rtc->lock, flags);
> +
> +	writel(lpt >> 32, rtc->ioaddr + LPC_LPT_MSB_OFF);
> +	writel(lpt, rtc->ioaddr + LPC_LPT_LSB_OFF);
> +	writel(1, rtc->ioaddr + LPC_LPT_START_OFF);
> +
> +	spin_unlock_irqrestore(&rtc->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int st_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&rtc->lock, flags);
> +
> +	memcpy(wkalrm, &rtc->alarm, sizeof(struct rtc_wkalrm));
> +
> +	spin_unlock_irqrestore(&rtc->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int st_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +
> +	if (enabled && !rtc->irq_enabled) {
> +		enable_irq(rtc->irq);
> +		rtc->irq_enabled = true;
> +	} else if (!enabled && rtc->irq_enabled) {
> +		disable_irq(rtc->irq);
> +		rtc->irq_enabled = false;
> +	}
> +
> +	return 0;
> +}
> +
> +static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +	struct rtc_time now;
> +	unsigned long now_secs;
> +	unsigned long alarm_secs;
> +	unsigned long long lpa;
> +
> +	st_rtc_read_time(dev, &now);
> +	rtc_tm_to_time(&now, &now_secs);
> +	rtc_tm_to_time(&t->time, &alarm_secs);
> +
> +	/* Invalid alarm time */
> +	if (now_secs > alarm_secs)
> +		return -EINVAL;
> +
> +	memcpy(&rtc->alarm, t, sizeof(struct rtc_wkalrm));
> +
> +	/* Now many secs to fire */
> +	alarm_secs -= now_secs;
> +	lpa = (unsigned long long)alarm_secs * clk_get_rate(rtc->clk);
> +
> +	st_rtc_set_hw_alarm(rtc, lpa >> 32, lpa);
> +	st_rtc_alarm_irq_enable(dev, t->enabled);
> +
> +	return 0;
> +}
> +
> +static struct rtc_class_ops st_rtc_ops = {
> +	.read_time		= st_rtc_read_time,
> +	.set_time		= st_rtc_set_time,
> +	.read_alarm		= st_rtc_read_alarm,
> +	.set_alarm		= st_rtc_set_alarm,
> +	.alarm_irq_enable	= st_rtc_alarm_irq_enable,
> +};
> +
> +static int st_rtc_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np;
> +	struct st_rtc *rtc;
> +	struct resource *res;
> +	struct rtc_time tm_check;
> +	int ret = 0;
> +
> +	/* This is a single [shared] node MFD device. */
> +	np = pdev->dev.of_node = pdev->dev.parent->of_node;
> +
> +	rtc = devm_kzalloc(&pdev->dev, sizeof(struct st_rtc), GFP_KERNEL);
> +	if (!rtc)
> +		return -ENOMEM;
> +
> +	spin_lock_init(&rtc->lock);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	rtc->ioaddr = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(rtc->ioaddr))
> +		return PTR_ERR(rtc->ioaddr);
> +
> +	rtc->irq = irq_of_parse_and_map(np, 0);
> +	if (!rtc->irq) {
> +		dev_err(&pdev->dev, "IRQ missing or invalid\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler, 0,
> +			       pdev->name, rtc);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq);
> +		return ret;
> +	}
> +
> +	enable_irq_wake(rtc->irq);
> +	disable_irq(rtc->irq);
> +
> +	rtc->clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(rtc->clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(rtc->clk);
> +	}
> +
> +	clk_prepare_enable(rtc->clk);
> +
> +	device_set_wakeup_capable(&pdev->dev, 1);
> +
> +	platform_set_drvdata(pdev, rtc);
> +
> +	/*
> +	 * The RTC-LPC is able to manage date.year > 2038
> +	 * but currently the kernel can not manage this date!
> +	 * If the RTC-LPC has a date.year > 2038 then
> +	 * it's set to the epoch "Jan 1st 2000"
> +	 */
> +	st_rtc_read_time(&pdev->dev, &tm_check);
> +
> +	if (tm_check.tm_year >=  (2038 - 1900)) {
> +		memset(&tm_check, 0, sizeof(tm_check));
> +		tm_check.tm_year = 100;
> +		tm_check.tm_mday = 1;
> +		st_rtc_set_time(&pdev->dev, &tm_check);
> +	}
> +
> +	rtc->rtc_dev = rtc_device_register("st-lpc-rtc", &pdev->dev,
> +					   &st_rtc_ops, THIS_MODULE);
> +	if (IS_ERR(rtc->rtc_dev)) {
> +		clk_disable_unprepare(rtc->clk);
> +		return PTR_ERR(rtc->rtc_dev);
> +	}
> +
> +	return 0;
> +}
> +
> +static int st_rtc_remove(struct platform_device *pdev)
> +{
> +	struct st_rtc *rtc = platform_get_drvdata(pdev);
> +
> +	if (likely(rtc->rtc_dev))
> +		rtc_device_unregister(rtc->rtc_dev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
CONFIG_PM_SLEEP is enough. If CONFIG_PM_SLEEP unset and 
CONFIG_PM_RUNTIME set, this will lead in a compilation warning. API 
defined but not used.
> +static int st_rtc_suspend(struct device *dev)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +
> +	if (device_may_wakeup(dev))
> +		return 0;
> +
> +	writel(1, rtc->ioaddr + LPC_WDT_OFF);
> +	writel(0, rtc->ioaddr + LPC_LPA_START_OFF);
> +	writel(0, rtc->ioaddr + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_rtc_resume(struct device *dev)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +
> +	rtc_alarm_irq_enable(rtc->rtc_dev, 0);
> +
> +	/*
> +	 * clean 'rtc->alarm' to allow a new
> +	 * a new .set_alarm to the upper RTC layer
> +	 */
duplicate "a new"
> +	memset(&rtc->alarm, 0, sizeof(struct rtc_wkalrm));
> +
> +	writel(0, rtc->ioaddr + LPC_LPA_MSB_OFF);
> +	writel(0, rtc->ioaddr + LPC_LPA_LSB_OFF);
> +	writel(1, rtc->ioaddr + LPC_WDT_OFF);
> +	writel(1, rtc->ioaddr + LPC_LPA_START_OFF);
> +	writel(0, rtc->ioaddr + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_rtc_pm_ops, st_rtc_suspend, st_rtc_resume);
> +
> +static struct platform_driver st_rtc_platform_driver = {
> +	.driver = {
> +		.name = "st-lpc-rtc",
> +		.pm = &st_rtc_pm_ops,
> +	},
> +	.probe = st_rtc_probe,
> +	.remove = st_rtc_remove,
> +};
> +
> +module_platform_driver(st_rtc_platform_driver);
> +
> +MODULE_DESCRIPTION("STMicroelectronics LPC RTC driver");
> +MODULE_AUTHOR("David Paris <david.paris@st.com>");
> +MODULE_LICENSE("GPLv2");


^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [STLinux Kernel] [PATCH 7/8] rtc: st: add new driver for ST's LPC RTC
@ 2014-12-15 12:28     ` David Paris
  0 siblings, 0 replies; 69+ messages in thread
From: David Paris @ 2014-12-15 12:28 UTC (permalink / raw)
  To: Lee Jones, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: a.zummo-BfzFCNDTiLLj+vYz1yj4TQ, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw,
	devicetree-u79uwXL29TY76Z2rM5mHXA, wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA

Hi Lee,

     See my comment below.
     David

On 12/15/2014 12:25 PM, Lee Jones wrote:
> ST's Low Power Controller (LPC) controls two devices; watchdog and RTC.
> Only one of the devices can be used at any one time.  This is enforced
> by the correlating MFD driver.  This portion of the driver-set controls
> the Real Time Clock.
>
> Signed-off-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>   drivers/rtc/Kconfig      |  13 ++
>   drivers/rtc/Makefile     |   1 +
>   drivers/rtc/rtc-st-lpc.c | 330 +++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 344 insertions(+)
>   create mode 100644 drivers/rtc/rtc-st-lpc.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index a168e96..aa4bd90 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -1355,6 +1355,18 @@ config RTC_DRV_SIRFSOC
>   	  Say "yes" here to support the real time clock on SiRF SOC chips.
>   	  This driver can also be built as a module called rtc-sirfsoc.
>   
> +config RTC_DRV_ST_LPC
> +	tristate "STMicroelectronics LPC RTC"
> +	depends on ARCH_STI
> +	depends on OF
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based RTC support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called rtc-st-lpc.
> +
>   config RTC_DRV_MOXART
>   	tristate "MOXA ART RTC"
>   	depends on ARCH_MOXART || COMPILE_TEST
> @@ -1390,4 +1402,5 @@ config RTC_DRV_HID_SENSOR_TIME
>   	  rtc-hid-sensor-time.
>   
>   
> +
>   endif # RTC_CLASS
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 56f061c..ce5860b 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -145,4 +145,5 @@ obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
>   obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
>   obj-$(CONFIG_RTC_DRV_XGENE)	+= rtc-xgene.o
>   obj-$(CONFIG_RTC_DRV_SIRFSOC)	+= rtc-sirfsoc.o
> +obj-$(CONFIG_RTC_DRV_ST_LPC)	+= rtc-st-lpc.o
>   obj-$(CONFIG_RTC_DRV_MOXART)	+= rtc-moxart.o
> diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c
> new file mode 100644
> index 0000000..60b1ab4
> --- /dev/null
> +++ b/drivers/rtc/rtc-st-lpc.c
> @@ -0,0 +1,330 @@
> +/*
> + * rtc-st-lpc.c - ST's LPC RTC, powered by the Low Power Timer
> + *
> + * Copyright (C) 2014 STMicroelectronics Limited
> + *
> + * Author: David Paris <david.paris-qxv4g6HH51o@public.gmane.org> for STMicroelectronics
> + *         Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> for STMicroelectronics
> + *
> + * Based on the original driver written by Stuart Menefy.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/rtc.h>
> +
> +/* Low Power Timer */
> +#define LPC_LPT_LSB_OFF		0x400
> +#define LPC_LPT_MSB_OFF		0x404
> +#define LPC_LPT_START_OFF	0x408
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF		0x410
> +#define LPC_LPA_MSB_OFF		0x414
> +#define LPC_LPA_START_OFF	0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF		0x510
> +#define LPC_WDT_FLAG_OFF	0x514
> +
> +struct st_rtc {
> +	struct rtc_device *rtc_dev;
> +	struct rtc_wkalrm alarm;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *ioaddr;
> +	bool irq_enabled:1;
> +	spinlock_t lock;
> +	short irq;
> +};
> +
> +static void st_rtc_set_hw_alarm(struct st_rtc *rtc,
> +				unsigned long msb, unsigned long  lsb)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&rtc->lock, flags);
> +
> +	writel(1, rtc->ioaddr + LPC_WDT_OFF);
> +
> +	writel(msb, rtc->ioaddr + LPC_LPA_MSB_OFF);
> +	writel(lsb, rtc->ioaddr + LPC_LPA_LSB_OFF);
> +	writel(1, rtc->ioaddr + LPC_LPA_START_OFF);
> +
> +	writel(0, rtc->ioaddr + LPC_WDT_OFF);
> +
> +	spin_unlock_irqrestore(&rtc->lock, flags);
> +}
> +
> +static irqreturn_t st_rtc_handler(int this_irq, void *data)
> +{
> +	struct st_rtc *rtc = (struct st_rtc *)data;
> +
> +	rtc_update_irq(rtc->rtc_dev, 1, RTC_AF);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +	unsigned long lpt_lsb, lpt_msb;
> +	unsigned long long lpt;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&rtc->lock, flags);
> +
> +	do {
> +		lpt_msb = readl(rtc->ioaddr + LPC_LPT_MSB_OFF);
> +		lpt_lsb = readl(rtc->ioaddr + LPC_LPT_LSB_OFF);
> +	} while (readl(rtc->ioaddr + LPC_LPT_MSB_OFF) != lpt_msb);
> +
> +	spin_unlock_irqrestore(&rtc->lock, flags);
> +
> +	lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
> +	do_div(lpt, clk_get_rate(rtc->clk));
> +	rtc_time_to_tm(lpt, tm);
> +
> +	return 0;
> +}
> +
> +static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +	unsigned long long lpt;
> +	unsigned long secs, flags;
> +	int ret;
> +
> +	ret = rtc_tm_to_time(tm, &secs);
> +	if (ret)
> +		return ret;
> +
> +	lpt = (unsigned long long)secs * clk_get_rate(rtc->clk);
> +
> +	spin_lock_irqsave(&rtc->lock, flags);
> +
> +	writel(lpt >> 32, rtc->ioaddr + LPC_LPT_MSB_OFF);
> +	writel(lpt, rtc->ioaddr + LPC_LPT_LSB_OFF);
> +	writel(1, rtc->ioaddr + LPC_LPT_START_OFF);
> +
> +	spin_unlock_irqrestore(&rtc->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int st_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&rtc->lock, flags);
> +
> +	memcpy(wkalrm, &rtc->alarm, sizeof(struct rtc_wkalrm));
> +
> +	spin_unlock_irqrestore(&rtc->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int st_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +
> +	if (enabled && !rtc->irq_enabled) {
> +		enable_irq(rtc->irq);
> +		rtc->irq_enabled = true;
> +	} else if (!enabled && rtc->irq_enabled) {
> +		disable_irq(rtc->irq);
> +		rtc->irq_enabled = false;
> +	}
> +
> +	return 0;
> +}
> +
> +static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +	struct rtc_time now;
> +	unsigned long now_secs;
> +	unsigned long alarm_secs;
> +	unsigned long long lpa;
> +
> +	st_rtc_read_time(dev, &now);
> +	rtc_tm_to_time(&now, &now_secs);
> +	rtc_tm_to_time(&t->time, &alarm_secs);
> +
> +	/* Invalid alarm time */
> +	if (now_secs > alarm_secs)
> +		return -EINVAL;
> +
> +	memcpy(&rtc->alarm, t, sizeof(struct rtc_wkalrm));
> +
> +	/* Now many secs to fire */
> +	alarm_secs -= now_secs;
> +	lpa = (unsigned long long)alarm_secs * clk_get_rate(rtc->clk);
> +
> +	st_rtc_set_hw_alarm(rtc, lpa >> 32, lpa);
> +	st_rtc_alarm_irq_enable(dev, t->enabled);
> +
> +	return 0;
> +}
> +
> +static struct rtc_class_ops st_rtc_ops = {
> +	.read_time		= st_rtc_read_time,
> +	.set_time		= st_rtc_set_time,
> +	.read_alarm		= st_rtc_read_alarm,
> +	.set_alarm		= st_rtc_set_alarm,
> +	.alarm_irq_enable	= st_rtc_alarm_irq_enable,
> +};
> +
> +static int st_rtc_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np;
> +	struct st_rtc *rtc;
> +	struct resource *res;
> +	struct rtc_time tm_check;
> +	int ret = 0;
> +
> +	/* This is a single [shared] node MFD device. */
> +	np = pdev->dev.of_node = pdev->dev.parent->of_node;
> +
> +	rtc = devm_kzalloc(&pdev->dev, sizeof(struct st_rtc), GFP_KERNEL);
> +	if (!rtc)
> +		return -ENOMEM;
> +
> +	spin_lock_init(&rtc->lock);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	rtc->ioaddr = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(rtc->ioaddr))
> +		return PTR_ERR(rtc->ioaddr);
> +
> +	rtc->irq = irq_of_parse_and_map(np, 0);
> +	if (!rtc->irq) {
> +		dev_err(&pdev->dev, "IRQ missing or invalid\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler, 0,
> +			       pdev->name, rtc);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq);
> +		return ret;
> +	}
> +
> +	enable_irq_wake(rtc->irq);
> +	disable_irq(rtc->irq);
> +
> +	rtc->clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(rtc->clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(rtc->clk);
> +	}
> +
> +	clk_prepare_enable(rtc->clk);
> +
> +	device_set_wakeup_capable(&pdev->dev, 1);
> +
> +	platform_set_drvdata(pdev, rtc);
> +
> +	/*
> +	 * The RTC-LPC is able to manage date.year > 2038
> +	 * but currently the kernel can not manage this date!
> +	 * If the RTC-LPC has a date.year > 2038 then
> +	 * it's set to the epoch "Jan 1st 2000"
> +	 */
> +	st_rtc_read_time(&pdev->dev, &tm_check);
> +
> +	if (tm_check.tm_year >=  (2038 - 1900)) {
> +		memset(&tm_check, 0, sizeof(tm_check));
> +		tm_check.tm_year = 100;
> +		tm_check.tm_mday = 1;
> +		st_rtc_set_time(&pdev->dev, &tm_check);
> +	}
> +
> +	rtc->rtc_dev = rtc_device_register("st-lpc-rtc", &pdev->dev,
> +					   &st_rtc_ops, THIS_MODULE);
> +	if (IS_ERR(rtc->rtc_dev)) {
> +		clk_disable_unprepare(rtc->clk);
> +		return PTR_ERR(rtc->rtc_dev);
> +	}
> +
> +	return 0;
> +}
> +
> +static int st_rtc_remove(struct platform_device *pdev)
> +{
> +	struct st_rtc *rtc = platform_get_drvdata(pdev);
> +
> +	if (likely(rtc->rtc_dev))
> +		rtc_device_unregister(rtc->rtc_dev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
CONFIG_PM_SLEEP is enough. If CONFIG_PM_SLEEP unset and 
CONFIG_PM_RUNTIME set, this will lead in a compilation warning. API 
defined but not used.
> +static int st_rtc_suspend(struct device *dev)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +
> +	if (device_may_wakeup(dev))
> +		return 0;
> +
> +	writel(1, rtc->ioaddr + LPC_WDT_OFF);
> +	writel(0, rtc->ioaddr + LPC_LPA_START_OFF);
> +	writel(0, rtc->ioaddr + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_rtc_resume(struct device *dev)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +
> +	rtc_alarm_irq_enable(rtc->rtc_dev, 0);
> +
> +	/*
> +	 * clean 'rtc->alarm' to allow a new
> +	 * a new .set_alarm to the upper RTC layer
> +	 */
duplicate "a new"
> +	memset(&rtc->alarm, 0, sizeof(struct rtc_wkalrm));
> +
> +	writel(0, rtc->ioaddr + LPC_LPA_MSB_OFF);
> +	writel(0, rtc->ioaddr + LPC_LPA_LSB_OFF);
> +	writel(1, rtc->ioaddr + LPC_WDT_OFF);
> +	writel(1, rtc->ioaddr + LPC_LPA_START_OFF);
> +	writel(0, rtc->ioaddr + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_rtc_pm_ops, st_rtc_suspend, st_rtc_resume);
> +
> +static struct platform_driver st_rtc_platform_driver = {
> +	.driver = {
> +		.name = "st-lpc-rtc",
> +		.pm = &st_rtc_pm_ops,
> +	},
> +	.probe = st_rtc_probe,
> +	.remove = st_rtc_remove,
> +};
> +
> +module_platform_driver(st_rtc_platform_driver);
> +
> +MODULE_DESCRIPTION("STMicroelectronics LPC RTC driver");
> +MODULE_AUTHOR("David Paris <david.paris-qxv4g6HH51o@public.gmane.org>");
> +MODULE_LICENSE("GPLv2");

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [STLinux Kernel] [PATCH 7/8] rtc: st: add new driver for ST's LPC RTC
@ 2014-12-15 12:28     ` David Paris
  0 siblings, 0 replies; 69+ messages in thread
From: David Paris @ 2014-12-15 12:28 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Lee,

     See my comment below.
     David

On 12/15/2014 12:25 PM, Lee Jones wrote:
> ST's Low Power Controller (LPC) controls two devices; watchdog and RTC.
> Only one of the devices can be used at any one time.  This is enforced
> by the correlating MFD driver.  This portion of the driver-set controls
> the Real Time Clock.
>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>   drivers/rtc/Kconfig      |  13 ++
>   drivers/rtc/Makefile     |   1 +
>   drivers/rtc/rtc-st-lpc.c | 330 +++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 344 insertions(+)
>   create mode 100644 drivers/rtc/rtc-st-lpc.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index a168e96..aa4bd90 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -1355,6 +1355,18 @@ config RTC_DRV_SIRFSOC
>   	  Say "yes" here to support the real time clock on SiRF SOC chips.
>   	  This driver can also be built as a module called rtc-sirfsoc.
>   
> +config RTC_DRV_ST_LPC
> +	tristate "STMicroelectronics LPC RTC"
> +	depends on ARCH_STI
> +	depends on OF
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based RTC support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called rtc-st-lpc.
> +
>   config RTC_DRV_MOXART
>   	tristate "MOXA ART RTC"
>   	depends on ARCH_MOXART || COMPILE_TEST
> @@ -1390,4 +1402,5 @@ config RTC_DRV_HID_SENSOR_TIME
>   	  rtc-hid-sensor-time.
>   
>   
> +
>   endif # RTC_CLASS
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 56f061c..ce5860b 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -145,4 +145,5 @@ obj-$(CONFIG_RTC_DRV_WM8350)	+= rtc-wm8350.o
>   obj-$(CONFIG_RTC_DRV_X1205)	+= rtc-x1205.o
>   obj-$(CONFIG_RTC_DRV_XGENE)	+= rtc-xgene.o
>   obj-$(CONFIG_RTC_DRV_SIRFSOC)	+= rtc-sirfsoc.o
> +obj-$(CONFIG_RTC_DRV_ST_LPC)	+= rtc-st-lpc.o
>   obj-$(CONFIG_RTC_DRV_MOXART)	+= rtc-moxart.o
> diff --git a/drivers/rtc/rtc-st-lpc.c b/drivers/rtc/rtc-st-lpc.c
> new file mode 100644
> index 0000000..60b1ab4
> --- /dev/null
> +++ b/drivers/rtc/rtc-st-lpc.c
> @@ -0,0 +1,330 @@
> +/*
> + * rtc-st-lpc.c - ST's LPC RTC, powered by the Low Power Timer
> + *
> + * Copyright (C) 2014 STMicroelectronics Limited
> + *
> + * Author: David Paris <david.paris@st.com> for STMicroelectronics
> + *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
> + *
> + * Based on the original driver written by Stuart Menefy.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_irq.h>
> +#include <linux/platform_device.h>
> +#include <linux/rtc.h>
> +
> +/* Low Power Timer */
> +#define LPC_LPT_LSB_OFF		0x400
> +#define LPC_LPT_MSB_OFF		0x404
> +#define LPC_LPT_START_OFF	0x408
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF		0x410
> +#define LPC_LPA_MSB_OFF		0x414
> +#define LPC_LPA_START_OFF	0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF		0x510
> +#define LPC_WDT_FLAG_OFF	0x514
> +
> +struct st_rtc {
> +	struct rtc_device *rtc_dev;
> +	struct rtc_wkalrm alarm;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *ioaddr;
> +	bool irq_enabled:1;
> +	spinlock_t lock;
> +	short irq;
> +};
> +
> +static void st_rtc_set_hw_alarm(struct st_rtc *rtc,
> +				unsigned long msb, unsigned long  lsb)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&rtc->lock, flags);
> +
> +	writel(1, rtc->ioaddr + LPC_WDT_OFF);
> +
> +	writel(msb, rtc->ioaddr + LPC_LPA_MSB_OFF);
> +	writel(lsb, rtc->ioaddr + LPC_LPA_LSB_OFF);
> +	writel(1, rtc->ioaddr + LPC_LPA_START_OFF);
> +
> +	writel(0, rtc->ioaddr + LPC_WDT_OFF);
> +
> +	spin_unlock_irqrestore(&rtc->lock, flags);
> +}
> +
> +static irqreturn_t st_rtc_handler(int this_irq, void *data)
> +{
> +	struct st_rtc *rtc = (struct st_rtc *)data;
> +
> +	rtc_update_irq(rtc->rtc_dev, 1, RTC_AF);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int st_rtc_read_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +	unsigned long lpt_lsb, lpt_msb;
> +	unsigned long long lpt;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&rtc->lock, flags);
> +
> +	do {
> +		lpt_msb = readl(rtc->ioaddr + LPC_LPT_MSB_OFF);
> +		lpt_lsb = readl(rtc->ioaddr + LPC_LPT_LSB_OFF);
> +	} while (readl(rtc->ioaddr + LPC_LPT_MSB_OFF) != lpt_msb);
> +
> +	spin_unlock_irqrestore(&rtc->lock, flags);
> +
> +	lpt = ((unsigned long long)lpt_msb << 32) | lpt_lsb;
> +	do_div(lpt, clk_get_rate(rtc->clk));
> +	rtc_time_to_tm(lpt, tm);
> +
> +	return 0;
> +}
> +
> +static int st_rtc_set_time(struct device *dev, struct rtc_time *tm)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +	unsigned long long lpt;
> +	unsigned long secs, flags;
> +	int ret;
> +
> +	ret = rtc_tm_to_time(tm, &secs);
> +	if (ret)
> +		return ret;
> +
> +	lpt = (unsigned long long)secs * clk_get_rate(rtc->clk);
> +
> +	spin_lock_irqsave(&rtc->lock, flags);
> +
> +	writel(lpt >> 32, rtc->ioaddr + LPC_LPT_MSB_OFF);
> +	writel(lpt, rtc->ioaddr + LPC_LPT_LSB_OFF);
> +	writel(1, rtc->ioaddr + LPC_LPT_START_OFF);
> +
> +	spin_unlock_irqrestore(&rtc->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int st_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&rtc->lock, flags);
> +
> +	memcpy(wkalrm, &rtc->alarm, sizeof(struct rtc_wkalrm));
> +
> +	spin_unlock_irqrestore(&rtc->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int st_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +
> +	if (enabled && !rtc->irq_enabled) {
> +		enable_irq(rtc->irq);
> +		rtc->irq_enabled = true;
> +	} else if (!enabled && rtc->irq_enabled) {
> +		disable_irq(rtc->irq);
> +		rtc->irq_enabled = false;
> +	}
> +
> +	return 0;
> +}
> +
> +static int st_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +	struct rtc_time now;
> +	unsigned long now_secs;
> +	unsigned long alarm_secs;
> +	unsigned long long lpa;
> +
> +	st_rtc_read_time(dev, &now);
> +	rtc_tm_to_time(&now, &now_secs);
> +	rtc_tm_to_time(&t->time, &alarm_secs);
> +
> +	/* Invalid alarm time */
> +	if (now_secs > alarm_secs)
> +		return -EINVAL;
> +
> +	memcpy(&rtc->alarm, t, sizeof(struct rtc_wkalrm));
> +
> +	/* Now many secs to fire */
> +	alarm_secs -= now_secs;
> +	lpa = (unsigned long long)alarm_secs * clk_get_rate(rtc->clk);
> +
> +	st_rtc_set_hw_alarm(rtc, lpa >> 32, lpa);
> +	st_rtc_alarm_irq_enable(dev, t->enabled);
> +
> +	return 0;
> +}
> +
> +static struct rtc_class_ops st_rtc_ops = {
> +	.read_time		= st_rtc_read_time,
> +	.set_time		= st_rtc_set_time,
> +	.read_alarm		= st_rtc_read_alarm,
> +	.set_alarm		= st_rtc_set_alarm,
> +	.alarm_irq_enable	= st_rtc_alarm_irq_enable,
> +};
> +
> +static int st_rtc_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np;
> +	struct st_rtc *rtc;
> +	struct resource *res;
> +	struct rtc_time tm_check;
> +	int ret = 0;
> +
> +	/* This is a single [shared] node MFD device. */
> +	np = pdev->dev.of_node = pdev->dev.parent->of_node;
> +
> +	rtc = devm_kzalloc(&pdev->dev, sizeof(struct st_rtc), GFP_KERNEL);
> +	if (!rtc)
> +		return -ENOMEM;
> +
> +	spin_lock_init(&rtc->lock);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	rtc->ioaddr = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(rtc->ioaddr))
> +		return PTR_ERR(rtc->ioaddr);
> +
> +	rtc->irq = irq_of_parse_and_map(np, 0);
> +	if (!rtc->irq) {
> +		dev_err(&pdev->dev, "IRQ missing or invalid\n");
> +		return -EINVAL;
> +	}
> +
> +	ret = devm_request_irq(&pdev->dev, rtc->irq, st_rtc_handler, 0,
> +			       pdev->name, rtc);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Failed to request irq %i\n", rtc->irq);
> +		return ret;
> +	}
> +
> +	enable_irq_wake(rtc->irq);
> +	disable_irq(rtc->irq);
> +
> +	rtc->clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(rtc->clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(rtc->clk);
> +	}
> +
> +	clk_prepare_enable(rtc->clk);
> +
> +	device_set_wakeup_capable(&pdev->dev, 1);
> +
> +	platform_set_drvdata(pdev, rtc);
> +
> +	/*
> +	 * The RTC-LPC is able to manage date.year > 2038
> +	 * but currently the kernel can not manage this date!
> +	 * If the RTC-LPC has a date.year > 2038 then
> +	 * it's set to the epoch "Jan 1st 2000"
> +	 */
> +	st_rtc_read_time(&pdev->dev, &tm_check);
> +
> +	if (tm_check.tm_year >=  (2038 - 1900)) {
> +		memset(&tm_check, 0, sizeof(tm_check));
> +		tm_check.tm_year = 100;
> +		tm_check.tm_mday = 1;
> +		st_rtc_set_time(&pdev->dev, &tm_check);
> +	}
> +
> +	rtc->rtc_dev = rtc_device_register("st-lpc-rtc", &pdev->dev,
> +					   &st_rtc_ops, THIS_MODULE);
> +	if (IS_ERR(rtc->rtc_dev)) {
> +		clk_disable_unprepare(rtc->clk);
> +		return PTR_ERR(rtc->rtc_dev);
> +	}
> +
> +	return 0;
> +}
> +
> +static int st_rtc_remove(struct platform_device *pdev)
> +{
> +	struct st_rtc *rtc = platform_get_drvdata(pdev);
> +
> +	if (likely(rtc->rtc_dev))
> +		rtc_device_unregister(rtc->rtc_dev);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
CONFIG_PM_SLEEP is enough. If CONFIG_PM_SLEEP unset and 
CONFIG_PM_RUNTIME set, this will lead in a compilation warning. API 
defined but not used.
> +static int st_rtc_suspend(struct device *dev)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +
> +	if (device_may_wakeup(dev))
> +		return 0;
> +
> +	writel(1, rtc->ioaddr + LPC_WDT_OFF);
> +	writel(0, rtc->ioaddr + LPC_LPA_START_OFF);
> +	writel(0, rtc->ioaddr + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_rtc_resume(struct device *dev)
> +{
> +	struct st_rtc *rtc = dev_get_drvdata(dev);
> +
> +	rtc_alarm_irq_enable(rtc->rtc_dev, 0);
> +
> +	/*
> +	 * clean 'rtc->alarm' to allow a new
> +	 * a new .set_alarm to the upper RTC layer
> +	 */
duplicate "a new"
> +	memset(&rtc->alarm, 0, sizeof(struct rtc_wkalrm));
> +
> +	writel(0, rtc->ioaddr + LPC_LPA_MSB_OFF);
> +	writel(0, rtc->ioaddr + LPC_LPA_LSB_OFF);
> +	writel(1, rtc->ioaddr + LPC_WDT_OFF);
> +	writel(1, rtc->ioaddr + LPC_LPA_START_OFF);
> +	writel(0, rtc->ioaddr + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_rtc_pm_ops, st_rtc_suspend, st_rtc_resume);
> +
> +static struct platform_driver st_rtc_platform_driver = {
> +	.driver = {
> +		.name = "st-lpc-rtc",
> +		.pm = &st_rtc_pm_ops,
> +	},
> +	.probe = st_rtc_probe,
> +	.remove = st_rtc_remove,
> +};
> +
> +module_platform_driver(st_rtc_platform_driver);
> +
> +MODULE_DESCRIPTION("STMicroelectronics LPC RTC driver");
> +MODULE_AUTHOR("David Paris <david.paris@st.com>");
> +MODULE_LICENSE("GPLv2");

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-15 12:29     ` David Paris
  0 siblings, 0 replies; 69+ messages in thread
From: David Paris @ 2014-12-15 12:29 UTC (permalink / raw)
  To: Lee Jones, linux-arm-kernel, linux-kernel
  Cc: kernel, a.zummo, rtc-linux, wim, linux-watchdog, devicetree

Hi Lee,

     1. During resume stage the st_wdog_setup should be called again to 
reconfigure LPC RTC as watchdog.
     2. For Suspend/resume API, is CONFIG_PM necessary, CONFIG_PM_SLEEP 
is enough as no PM_RUNTIME in this driver ! This will results in a 
compilation warning if CONFIG_PM_SLEEP is unset for PM_RUNTIME not.

     David

On 12/15/2014 12:25 PM, Lee Jones wrote:
> Signed-off-by: David Paris <david.paris@st.com>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>   drivers/watchdog/Kconfig  |  13 ++
>   drivers/watchdog/Makefile |   1 +
>   drivers/watchdog/st_wdt.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 326 insertions(+)
>   create mode 100644 drivers/watchdog/st_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index f57312f..5a538af 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
>   	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
>   	  the watchdog triggers the system will be reset.
>   
> +config ST_WATCHDOG
> +	tristate "STMicroelectronics LPC Watchdog"
> +	depends on ARCH_STI
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based Watchdog timer support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_wdt.
> +
>   config TEGRA_WATCHDOG
>   	tristate "Tegra watchdog"
>   	depends on ARCH_TEGRA || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 468c320..eb19937 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
>   obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
>   obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
>   obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
> +obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
>   obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
>   obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
>   
> diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
> new file mode 100644
> index 0000000..7e06f15
> --- /dev/null
> +++ b/drivers/watchdog/st_wdt.c
> @@ -0,0 +1,312 @@
> +/*
> + * st-wdt.c - ST's LPC Watchdog
> + *
> + * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
> + *
> + * Author: David Paris <david.paris@st.com> for STMicroelectronics
> + *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/watchdog.h>
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF			0x410
> +#define LPC_LPA_START_OFF		0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF			0x510
> +
> +struct st_wdog_syscfg {
> +	struct regmap *regmap;
> +	unsigned int reset_type_reg;
> +	unsigned int reset_type_mask;
> +	unsigned int enable_reg;
> +	unsigned int enable_mask;
> +};
> +
> +struct st_wdog {
> +	void __iomem *base;
> +	struct device *dev;
> +	struct st_wdog_syscfg *syscfg;
> +	struct clk *clk;
> +	int warm_reset;
> +};
> +
> +static struct st_wdog_syscfg stid127_syscfg = {
> +	.reset_type_reg		= 0x004,
> +	.reset_type_mask	= BIT(2),
> +	.enable_reg		= 0x000,
> +	.enable_mask		= BIT(2),
> +};
> +
> +static struct st_wdog_syscfg stih415_syscfg = {
> +	.reset_type_reg		= 0x0B8,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x0B4,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih416_syscfg = {
> +	.reset_type_reg		= 0x88C,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x888,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih407_syscfg = {
> +	.enable_reg		= 0x204,
> +	.enable_mask		= BIT(19),
> +};
> +
> +static struct of_device_id st_wdog_match[] = {
> +	{
> +		.compatible = "st,stih407-lpc",
> +		.data = (void *)&stih407_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih416-lpc",
> +		.data = (void *)&stih416_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih415-lpc",
> +		.data = (void *)&stih415_syscfg,
> +	},
> +	{
> +		.compatible = "st,stid127-lpc",
> +		.data = (void *)&stid127_syscfg,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, st_wdog_match);
> +
> +static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
> +{
> +	/* Type of watchdog reset - 0: Cold 1: Warm */
> +	if (st_wdog->syscfg->reset_type_reg)
> +		regmap_update_bits(st_wdog->syscfg->regmap,
> +				   st_wdog->syscfg->reset_type_reg,
> +				   st_wdog->syscfg->reset_type_mask,
> +				   st_wdog->warm_reset);
> +
> +	/* Mask/unmask watchdog reset */
> +	regmap_update_bits(st_wdog->syscfg->regmap,
> +			   st_wdog->syscfg->enable_reg,
> +			   st_wdog->syscfg->enable_mask,
> +			   enable ? 0 : st_wdog->syscfg->enable_mask);
> +}
> +
> +static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
> +{
> +	unsigned long clkrate = clk_get_rate(st_wdog->clk);
> +	unsigned int maxtimeout = 0xFFFFFFFF / clkrate;
> +
> +	if (timeout > maxtimeout) {
> +		dev_warn(st_wdog->dev,
> +			 "timer overrun detected at current freq (%luHz)\n"
> +			 "wdog timeout set for %ds instead of requested %uis",
> +			 clkrate, maxtimeout, timeout);
> +		timeout = maxtimeout;
> +	}
> +	timeout *= clkrate;
> +
> +	writel_relaxed(timeout, st_wdog->base + LPC_LPA_LSB_OFF);
> +	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
> +}
> +
> +static int st_wdog_start(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_stop(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_set_timeout(struct watchdog_device *wdd,
> +			       unsigned int timeout)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	wdd->timeout = timeout;
> +	st_wdog_load_timer(st_wdog, timeout);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_keepalive(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	st_wdog_load_timer(st_wdog, wdd->timeout);
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info st_wdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "ST LPC WDT",
> +};
> +
> +static const struct watchdog_ops st_wdog_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= st_wdog_start,
> +	.stop		= st_wdog_stop,
> +	.ping		= st_wdog_keepalive,
> +	.set_timeout	= st_wdog_set_timeout,
> +};
> +
> +static struct watchdog_device st_wdog_dev = {
> +	.info		= &st_wdog_info,
> +	.ops		= &st_wdog_ops,
> +};
> +
> +static int st_wdog_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np;
> +	struct st_wdog *st_wdog;
> +	struct regmap *regmap;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *base;
> +	int ret;
> +
> +	/* This is a single node MFD device. */
> +	np = pdev->dev.of_node = pdev->dev.parent->of_node;
> +
> +	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
> +	if (!st_wdog)
> +		return -ENOMEM;
> +
> +	match = of_match_device(st_wdog_match, &pdev->dev);
> +	if (!match) {
> +		dev_err(&pdev->dev, "Couldn't match device\n");
> +		return -ENODEV;
> +	}
> +	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(base)) {
> +		dev_err(&pdev->dev, "Failed to ioremap base\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(regmap)) {
> +		dev_err(&pdev->dev, "No syscfg phandle specified\n");
> +		return PTR_ERR(regmap);
> +	}
> +
> +	clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(clk);
> +	}
> +	clk_prepare_enable(st_wdog->clk);
> +
> +	st_wdog->dev		= &pdev->dev;
> +	st_wdog->base		= base;
> +	st_wdog->clk		= clk;
> +	st_wdog->syscfg->regmap = regmap;
> +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> +
> +	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
> +	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
> +
> +	/* Init Watchdog timeout with value in DT */
> +	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
> +		return ret;
> +	}
> +
> +	ret = watchdog_register_device(&st_wdog_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register watchdog\n");
> +		clk_disable_unprepare(clk);
> +		return ret;
> +	}
> +
> +	st_wdog_setup(st_wdog, true);
> +
> +	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
> +		 st_wdog->warm_reset ? "warm" : "cold");
> +
> +	return ret;
> +}
> +
> +static int st_wdog_remove(struct platform_device *pdev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	st_wdog_setup(st_wdog, false);
> +	watchdog_unregister_device(&st_wdog_dev);
> +	clk_disable_unprepare(st_wdog->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int st_wdog_suspend(struct device *dev)
> +{
> +	if (watchdog_active(&st_wdog_dev))
> +		st_wdog_stop(&st_wdog_dev);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_resume(struct device *dev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	if (watchdog_active(&st_wdog_dev)) {
> +		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
> +		st_wdog_start(&st_wdog_dev);
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
> +			 st_wdog_suspend,
> +			 st_wdog_resume);
> +
> +static struct platform_driver st_wdog_driver = {
> +	.driver	= {
> +		.name = "st-lpc-wdt",
> +		.pm = &st_wdog_pm_ops,
> +	},
> +	.probe = st_wdog_probe,
> +	.remove = st_wdog_remove,
> +};
> +module_platform_driver(st_wdog_driver);
> +
> +MODULE_AUTHOR("David Paris <david.paris@st.com>");
> +MODULE_DESCRIPTION("ST LPC Watchdog Driver");
> +MODULE_LICENSE("GPL v2");


^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-15 12:29     ` David Paris
  0 siblings, 0 replies; 69+ messages in thread
From: David Paris @ 2014-12-15 12:29 UTC (permalink / raw)
  To: Lee Jones, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: kernel-F5mvAk5X5gdBDgjK7y7TUQ, a.zummo-BfzFCNDTiLLj+vYz1yj4TQ,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw, wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Lee,

     1. During resume stage the st_wdog_setup should be called again to 
reconfigure LPC RTC as watchdog.
     2. For Suspend/resume API, is CONFIG_PM necessary, CONFIG_PM_SLEEP 
is enough as no PM_RUNTIME in this driver ! This will results in a 
compilation warning if CONFIG_PM_SLEEP is unset for PM_RUNTIME not.

     David

On 12/15/2014 12:25 PM, Lee Jones wrote:
> Signed-off-by: David Paris <david.paris-qxv4g6HH51o@public.gmane.org>
> Signed-off-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>   drivers/watchdog/Kconfig  |  13 ++
>   drivers/watchdog/Makefile |   1 +
>   drivers/watchdog/st_wdt.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 326 insertions(+)
>   create mode 100644 drivers/watchdog/st_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index f57312f..5a538af 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
>   	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
>   	  the watchdog triggers the system will be reset.
>   
> +config ST_WATCHDOG
> +	tristate "STMicroelectronics LPC Watchdog"
> +	depends on ARCH_STI
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based Watchdog timer support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_wdt.
> +
>   config TEGRA_WATCHDOG
>   	tristate "Tegra watchdog"
>   	depends on ARCH_TEGRA || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 468c320..eb19937 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
>   obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
>   obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
>   obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
> +obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
>   obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
>   obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
>   
> diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
> new file mode 100644
> index 0000000..7e06f15
> --- /dev/null
> +++ b/drivers/watchdog/st_wdt.c
> @@ -0,0 +1,312 @@
> +/*
> + * st-wdt.c - ST's LPC Watchdog
> + *
> + * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
> + *
> + * Author: David Paris <david.paris-qxv4g6HH51o@public.gmane.org> for STMicroelectronics
> + *         Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> for STMicroelectronics
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/watchdog.h>
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF			0x410
> +#define LPC_LPA_START_OFF		0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF			0x510
> +
> +struct st_wdog_syscfg {
> +	struct regmap *regmap;
> +	unsigned int reset_type_reg;
> +	unsigned int reset_type_mask;
> +	unsigned int enable_reg;
> +	unsigned int enable_mask;
> +};
> +
> +struct st_wdog {
> +	void __iomem *base;
> +	struct device *dev;
> +	struct st_wdog_syscfg *syscfg;
> +	struct clk *clk;
> +	int warm_reset;
> +};
> +
> +static struct st_wdog_syscfg stid127_syscfg = {
> +	.reset_type_reg		= 0x004,
> +	.reset_type_mask	= BIT(2),
> +	.enable_reg		= 0x000,
> +	.enable_mask		= BIT(2),
> +};
> +
> +static struct st_wdog_syscfg stih415_syscfg = {
> +	.reset_type_reg		= 0x0B8,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x0B4,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih416_syscfg = {
> +	.reset_type_reg		= 0x88C,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x888,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih407_syscfg = {
> +	.enable_reg		= 0x204,
> +	.enable_mask		= BIT(19),
> +};
> +
> +static struct of_device_id st_wdog_match[] = {
> +	{
> +		.compatible = "st,stih407-lpc",
> +		.data = (void *)&stih407_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih416-lpc",
> +		.data = (void *)&stih416_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih415-lpc",
> +		.data = (void *)&stih415_syscfg,
> +	},
> +	{
> +		.compatible = "st,stid127-lpc",
> +		.data = (void *)&stid127_syscfg,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, st_wdog_match);
> +
> +static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
> +{
> +	/* Type of watchdog reset - 0: Cold 1: Warm */
> +	if (st_wdog->syscfg->reset_type_reg)
> +		regmap_update_bits(st_wdog->syscfg->regmap,
> +				   st_wdog->syscfg->reset_type_reg,
> +				   st_wdog->syscfg->reset_type_mask,
> +				   st_wdog->warm_reset);
> +
> +	/* Mask/unmask watchdog reset */
> +	regmap_update_bits(st_wdog->syscfg->regmap,
> +			   st_wdog->syscfg->enable_reg,
> +			   st_wdog->syscfg->enable_mask,
> +			   enable ? 0 : st_wdog->syscfg->enable_mask);
> +}
> +
> +static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
> +{
> +	unsigned long clkrate = clk_get_rate(st_wdog->clk);
> +	unsigned int maxtimeout = 0xFFFFFFFF / clkrate;
> +
> +	if (timeout > maxtimeout) {
> +		dev_warn(st_wdog->dev,
> +			 "timer overrun detected at current freq (%luHz)\n"
> +			 "wdog timeout set for %ds instead of requested %uis",
> +			 clkrate, maxtimeout, timeout);
> +		timeout = maxtimeout;
> +	}
> +	timeout *= clkrate;
> +
> +	writel_relaxed(timeout, st_wdog->base + LPC_LPA_LSB_OFF);
> +	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
> +}
> +
> +static int st_wdog_start(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_stop(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_set_timeout(struct watchdog_device *wdd,
> +			       unsigned int timeout)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	wdd->timeout = timeout;
> +	st_wdog_load_timer(st_wdog, timeout);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_keepalive(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	st_wdog_load_timer(st_wdog, wdd->timeout);
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info st_wdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "ST LPC WDT",
> +};
> +
> +static const struct watchdog_ops st_wdog_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= st_wdog_start,
> +	.stop		= st_wdog_stop,
> +	.ping		= st_wdog_keepalive,
> +	.set_timeout	= st_wdog_set_timeout,
> +};
> +
> +static struct watchdog_device st_wdog_dev = {
> +	.info		= &st_wdog_info,
> +	.ops		= &st_wdog_ops,
> +};
> +
> +static int st_wdog_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np;
> +	struct st_wdog *st_wdog;
> +	struct regmap *regmap;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *base;
> +	int ret;
> +
> +	/* This is a single node MFD device. */
> +	np = pdev->dev.of_node = pdev->dev.parent->of_node;
> +
> +	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
> +	if (!st_wdog)
> +		return -ENOMEM;
> +
> +	match = of_match_device(st_wdog_match, &pdev->dev);
> +	if (!match) {
> +		dev_err(&pdev->dev, "Couldn't match device\n");
> +		return -ENODEV;
> +	}
> +	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(base)) {
> +		dev_err(&pdev->dev, "Failed to ioremap base\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(regmap)) {
> +		dev_err(&pdev->dev, "No syscfg phandle specified\n");
> +		return PTR_ERR(regmap);
> +	}
> +
> +	clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(clk);
> +	}
> +	clk_prepare_enable(st_wdog->clk);
> +
> +	st_wdog->dev		= &pdev->dev;
> +	st_wdog->base		= base;
> +	st_wdog->clk		= clk;
> +	st_wdog->syscfg->regmap = regmap;
> +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> +
> +	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
> +	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
> +
> +	/* Init Watchdog timeout with value in DT */
> +	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
> +		return ret;
> +	}
> +
> +	ret = watchdog_register_device(&st_wdog_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register watchdog\n");
> +		clk_disable_unprepare(clk);
> +		return ret;
> +	}
> +
> +	st_wdog_setup(st_wdog, true);
> +
> +	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
> +		 st_wdog->warm_reset ? "warm" : "cold");
> +
> +	return ret;
> +}
> +
> +static int st_wdog_remove(struct platform_device *pdev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	st_wdog_setup(st_wdog, false);
> +	watchdog_unregister_device(&st_wdog_dev);
> +	clk_disable_unprepare(st_wdog->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int st_wdog_suspend(struct device *dev)
> +{
> +	if (watchdog_active(&st_wdog_dev))
> +		st_wdog_stop(&st_wdog_dev);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_resume(struct device *dev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	if (watchdog_active(&st_wdog_dev)) {
> +		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
> +		st_wdog_start(&st_wdog_dev);
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
> +			 st_wdog_suspend,
> +			 st_wdog_resume);
> +
> +static struct platform_driver st_wdog_driver = {
> +	.driver	= {
> +		.name = "st-lpc-wdt",
> +		.pm = &st_wdog_pm_ops,
> +	},
> +	.probe = st_wdog_probe,
> +	.remove = st_wdog_remove,
> +};
> +module_platform_driver(st_wdog_driver);
> +
> +MODULE_AUTHOR("David Paris <david.paris-qxv4g6HH51o@public.gmane.org>");
> +MODULE_DESCRIPTION("ST LPC Watchdog Driver");
> +MODULE_LICENSE("GPL v2");

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-15 12:29     ` David Paris
  0 siblings, 0 replies; 69+ messages in thread
From: David Paris @ 2014-12-15 12:29 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Lee,

     1. During resume stage the st_wdog_setup should be called again to 
reconfigure LPC RTC as watchdog.
     2. For Suspend/resume API, is CONFIG_PM necessary, CONFIG_PM_SLEEP 
is enough as no PM_RUNTIME in this driver ! This will results in a 
compilation warning if CONFIG_PM_SLEEP is unset for PM_RUNTIME not.

     David

On 12/15/2014 12:25 PM, Lee Jones wrote:
> Signed-off-by: David Paris <david.paris@st.com>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>   drivers/watchdog/Kconfig  |  13 ++
>   drivers/watchdog/Makefile |   1 +
>   drivers/watchdog/st_wdt.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 326 insertions(+)
>   create mode 100644 drivers/watchdog/st_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index f57312f..5a538af 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
>   	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
>   	  the watchdog triggers the system will be reset.
>   
> +config ST_WATCHDOG
> +	tristate "STMicroelectronics LPC Watchdog"
> +	depends on ARCH_STI
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based Watchdog timer support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_wdt.
> +
>   config TEGRA_WATCHDOG
>   	tristate "Tegra watchdog"
>   	depends on ARCH_TEGRA || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 468c320..eb19937 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
>   obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
>   obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
>   obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
> +obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
>   obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
>   obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
>   
> diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
> new file mode 100644
> index 0000000..7e06f15
> --- /dev/null
> +++ b/drivers/watchdog/st_wdt.c
> @@ -0,0 +1,312 @@
> +/*
> + * st-wdt.c - ST's LPC Watchdog
> + *
> + * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
> + *
> + * Author: David Paris <david.paris@st.com> for STMicroelectronics
> + *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/watchdog.h>
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF			0x410
> +#define LPC_LPA_START_OFF		0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF			0x510
> +
> +struct st_wdog_syscfg {
> +	struct regmap *regmap;
> +	unsigned int reset_type_reg;
> +	unsigned int reset_type_mask;
> +	unsigned int enable_reg;
> +	unsigned int enable_mask;
> +};
> +
> +struct st_wdog {
> +	void __iomem *base;
> +	struct device *dev;
> +	struct st_wdog_syscfg *syscfg;
> +	struct clk *clk;
> +	int warm_reset;
> +};
> +
> +static struct st_wdog_syscfg stid127_syscfg = {
> +	.reset_type_reg		= 0x004,
> +	.reset_type_mask	= BIT(2),
> +	.enable_reg		= 0x000,
> +	.enable_mask		= BIT(2),
> +};
> +
> +static struct st_wdog_syscfg stih415_syscfg = {
> +	.reset_type_reg		= 0x0B8,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x0B4,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih416_syscfg = {
> +	.reset_type_reg		= 0x88C,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x888,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih407_syscfg = {
> +	.enable_reg		= 0x204,
> +	.enable_mask		= BIT(19),
> +};
> +
> +static struct of_device_id st_wdog_match[] = {
> +	{
> +		.compatible = "st,stih407-lpc",
> +		.data = (void *)&stih407_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih416-lpc",
> +		.data = (void *)&stih416_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih415-lpc",
> +		.data = (void *)&stih415_syscfg,
> +	},
> +	{
> +		.compatible = "st,stid127-lpc",
> +		.data = (void *)&stid127_syscfg,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, st_wdog_match);
> +
> +static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
> +{
> +	/* Type of watchdog reset - 0: Cold 1: Warm */
> +	if (st_wdog->syscfg->reset_type_reg)
> +		regmap_update_bits(st_wdog->syscfg->regmap,
> +				   st_wdog->syscfg->reset_type_reg,
> +				   st_wdog->syscfg->reset_type_mask,
> +				   st_wdog->warm_reset);
> +
> +	/* Mask/unmask watchdog reset */
> +	regmap_update_bits(st_wdog->syscfg->regmap,
> +			   st_wdog->syscfg->enable_reg,
> +			   st_wdog->syscfg->enable_mask,
> +			   enable ? 0 : st_wdog->syscfg->enable_mask);
> +}
> +
> +static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
> +{
> +	unsigned long clkrate = clk_get_rate(st_wdog->clk);
> +	unsigned int maxtimeout = 0xFFFFFFFF / clkrate;
> +
> +	if (timeout > maxtimeout) {
> +		dev_warn(st_wdog->dev,
> +			 "timer overrun detected at current freq (%luHz)\n"
> +			 "wdog timeout set for %ds instead of requested %uis",
> +			 clkrate, maxtimeout, timeout);
> +		timeout = maxtimeout;
> +	}
> +	timeout *= clkrate;
> +
> +	writel_relaxed(timeout, st_wdog->base + LPC_LPA_LSB_OFF);
> +	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
> +}
> +
> +static int st_wdog_start(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_stop(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_set_timeout(struct watchdog_device *wdd,
> +			       unsigned int timeout)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	wdd->timeout = timeout;
> +	st_wdog_load_timer(st_wdog, timeout);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_keepalive(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	st_wdog_load_timer(st_wdog, wdd->timeout);
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info st_wdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "ST LPC WDT",
> +};
> +
> +static const struct watchdog_ops st_wdog_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= st_wdog_start,
> +	.stop		= st_wdog_stop,
> +	.ping		= st_wdog_keepalive,
> +	.set_timeout	= st_wdog_set_timeout,
> +};
> +
> +static struct watchdog_device st_wdog_dev = {
> +	.info		= &st_wdog_info,
> +	.ops		= &st_wdog_ops,
> +};
> +
> +static int st_wdog_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np;
> +	struct st_wdog *st_wdog;
> +	struct regmap *regmap;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *base;
> +	int ret;
> +
> +	/* This is a single node MFD device. */
> +	np = pdev->dev.of_node = pdev->dev.parent->of_node;
> +
> +	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
> +	if (!st_wdog)
> +		return -ENOMEM;
> +
> +	match = of_match_device(st_wdog_match, &pdev->dev);
> +	if (!match) {
> +		dev_err(&pdev->dev, "Couldn't match device\n");
> +		return -ENODEV;
> +	}
> +	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(base)) {
> +		dev_err(&pdev->dev, "Failed to ioremap base\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(regmap)) {
> +		dev_err(&pdev->dev, "No syscfg phandle specified\n");
> +		return PTR_ERR(regmap);
> +	}
> +
> +	clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(clk);
> +	}
> +	clk_prepare_enable(st_wdog->clk);
> +
> +	st_wdog->dev		= &pdev->dev;
> +	st_wdog->base		= base;
> +	st_wdog->clk		= clk;
> +	st_wdog->syscfg->regmap = regmap;
> +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> +
> +	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
> +	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
> +
> +	/* Init Watchdog timeout with value in DT */
> +	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
> +		return ret;
> +	}
> +
> +	ret = watchdog_register_device(&st_wdog_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register watchdog\n");
> +		clk_disable_unprepare(clk);
> +		return ret;
> +	}
> +
> +	st_wdog_setup(st_wdog, true);
> +
> +	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
> +		 st_wdog->warm_reset ? "warm" : "cold");
> +
> +	return ret;
> +}
> +
> +static int st_wdog_remove(struct platform_device *pdev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	st_wdog_setup(st_wdog, false);
> +	watchdog_unregister_device(&st_wdog_dev);
> +	clk_disable_unprepare(st_wdog->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int st_wdog_suspend(struct device *dev)
> +{
> +	if (watchdog_active(&st_wdog_dev))
> +		st_wdog_stop(&st_wdog_dev);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_resume(struct device *dev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	if (watchdog_active(&st_wdog_dev)) {
> +		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
> +		st_wdog_start(&st_wdog_dev);
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
> +			 st_wdog_suspend,
> +			 st_wdog_resume);
> +
> +static struct platform_driver st_wdog_driver = {
> +	.driver	= {
> +		.name = "st-lpc-wdt",
> +		.pm = &st_wdog_pm_ops,
> +	},
> +	.probe = st_wdog_probe,
> +	.remove = st_wdog_remove,
> +};
> +module_platform_driver(st_wdog_driver);
> +
> +MODULE_AUTHOR("David Paris <david.paris@st.com>");
> +MODULE_DESCRIPTION("ST LPC Watchdog Driver");
> +MODULE_LICENSE("GPL v2");

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-15 12:52     ` David Paris
  0 siblings, 0 replies; 69+ messages in thread
From: David Paris @ 2014-12-15 12:52 UTC (permalink / raw)
  To: Lee Jones, linux-arm-kernel, linux-kernel
  Cc: kernel, a.zummo, rtc-linux, wim, linux-watchdog, devicetree

Hi Lee,

     Something I didn't pointed out before, this driver is only supposed 
to support LPC_0 for stih407. The enable mask is only coded for LPC_0. 
Is it a wanted behavior ?

     In our current implementation here in ST is that LPC_0 is for A9 
Watchdog and LPC_1 for A9 global timer. This has been translated in the 
driver by hardcoding enable mask of LPC watchdog to only support LPC_0. 
But do we want to have a more generic driver to be applicable both for 
LPC_0 and LPC_1 ?

     David

On 12/15/2014 12:25 PM, Lee Jones wrote:
> Signed-off-by: David Paris <david.paris@st.com>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>   drivers/watchdog/Kconfig  |  13 ++
>   drivers/watchdog/Makefile |   1 +
>   drivers/watchdog/st_wdt.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 326 insertions(+)
>   create mode 100644 drivers/watchdog/st_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index f57312f..5a538af 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
>   	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
>   	  the watchdog triggers the system will be reset.
>   
> +config ST_WATCHDOG
> +	tristate "STMicroelectronics LPC Watchdog"
> +	depends on ARCH_STI
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based Watchdog timer support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_wdt.
> +
>   config TEGRA_WATCHDOG
>   	tristate "Tegra watchdog"
>   	depends on ARCH_TEGRA || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 468c320..eb19937 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
>   obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
>   obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
>   obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
> +obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
>   obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
>   obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
>   
> diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
> new file mode 100644
> index 0000000..7e06f15
> --- /dev/null
> +++ b/drivers/watchdog/st_wdt.c
> @@ -0,0 +1,312 @@
> +/*
> + * st-wdt.c - ST's LPC Watchdog
> + *
> + * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
> + *
> + * Author: David Paris <david.paris@st.com> for STMicroelectronics
> + *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/watchdog.h>
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF			0x410
> +#define LPC_LPA_START_OFF		0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF			0x510
> +
> +struct st_wdog_syscfg {
> +	struct regmap *regmap;
> +	unsigned int reset_type_reg;
> +	unsigned int reset_type_mask;
> +	unsigned int enable_reg;
> +	unsigned int enable_mask;
> +};
> +
> +struct st_wdog {
> +	void __iomem *base;
> +	struct device *dev;
> +	struct st_wdog_syscfg *syscfg;
> +	struct clk *clk;
> +	int warm_reset;
> +};
> +
> +static struct st_wdog_syscfg stid127_syscfg = {
> +	.reset_type_reg		= 0x004,
> +	.reset_type_mask	= BIT(2),
> +	.enable_reg		= 0x000,
> +	.enable_mask		= BIT(2),
> +};
> +
> +static struct st_wdog_syscfg stih415_syscfg = {
> +	.reset_type_reg		= 0x0B8,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x0B4,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih416_syscfg = {
> +	.reset_type_reg		= 0x88C,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x888,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih407_syscfg = {
> +	.enable_reg		= 0x204,
> +	.enable_mask		= BIT(19),
> +};
> +
> +static struct of_device_id st_wdog_match[] = {
> +	{
> +		.compatible = "st,stih407-lpc",
> +		.data = (void *)&stih407_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih416-lpc",
> +		.data = (void *)&stih416_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih415-lpc",
> +		.data = (void *)&stih415_syscfg,
> +	},
> +	{
> +		.compatible = "st,stid127-lpc",
> +		.data = (void *)&stid127_syscfg,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, st_wdog_match);
> +
> +static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
> +{
> +	/* Type of watchdog reset - 0: Cold 1: Warm */
> +	if (st_wdog->syscfg->reset_type_reg)
> +		regmap_update_bits(st_wdog->syscfg->regmap,
> +				   st_wdog->syscfg->reset_type_reg,
> +				   st_wdog->syscfg->reset_type_mask,
> +				   st_wdog->warm_reset);
> +
> +	/* Mask/unmask watchdog reset */
> +	regmap_update_bits(st_wdog->syscfg->regmap,
> +			   st_wdog->syscfg->enable_reg,
> +			   st_wdog->syscfg->enable_mask,
> +			   enable ? 0 : st_wdog->syscfg->enable_mask);
> +}
> +
> +static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
> +{
> +	unsigned long clkrate = clk_get_rate(st_wdog->clk);
> +	unsigned int maxtimeout = 0xFFFFFFFF / clkrate;
> +
> +	if (timeout > maxtimeout) {
> +		dev_warn(st_wdog->dev,
> +			 "timer overrun detected at current freq (%luHz)\n"
> +			 "wdog timeout set for %ds instead of requested %uis",
> +			 clkrate, maxtimeout, timeout);
> +		timeout = maxtimeout;
> +	}
> +	timeout *= clkrate;
> +
> +	writel_relaxed(timeout, st_wdog->base + LPC_LPA_LSB_OFF);
> +	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
> +}
> +
> +static int st_wdog_start(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_stop(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_set_timeout(struct watchdog_device *wdd,
> +			       unsigned int timeout)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	wdd->timeout = timeout;
> +	st_wdog_load_timer(st_wdog, timeout);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_keepalive(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	st_wdog_load_timer(st_wdog, wdd->timeout);
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info st_wdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "ST LPC WDT",
> +};
> +
> +static const struct watchdog_ops st_wdog_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= st_wdog_start,
> +	.stop		= st_wdog_stop,
> +	.ping		= st_wdog_keepalive,
> +	.set_timeout	= st_wdog_set_timeout,
> +};
> +
> +static struct watchdog_device st_wdog_dev = {
> +	.info		= &st_wdog_info,
> +	.ops		= &st_wdog_ops,
> +};
> +
> +static int st_wdog_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np;
> +	struct st_wdog *st_wdog;
> +	struct regmap *regmap;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *base;
> +	int ret;
> +
> +	/* This is a single node MFD device. */
> +	np = pdev->dev.of_node = pdev->dev.parent->of_node;
> +
> +	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
> +	if (!st_wdog)
> +		return -ENOMEM;
> +
> +	match = of_match_device(st_wdog_match, &pdev->dev);
> +	if (!match) {
> +		dev_err(&pdev->dev, "Couldn't match device\n");
> +		return -ENODEV;
> +	}
> +	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(base)) {
> +		dev_err(&pdev->dev, "Failed to ioremap base\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(regmap)) {
> +		dev_err(&pdev->dev, "No syscfg phandle specified\n");
> +		return PTR_ERR(regmap);
> +	}
> +
> +	clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(clk);
> +	}
> +	clk_prepare_enable(st_wdog->clk);
> +
> +	st_wdog->dev		= &pdev->dev;
> +	st_wdog->base		= base;
> +	st_wdog->clk		= clk;
> +	st_wdog->syscfg->regmap = regmap;
> +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> +
> +	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
> +	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
> +
> +	/* Init Watchdog timeout with value in DT */
> +	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
> +		return ret;
> +	}
> +
> +	ret = watchdog_register_device(&st_wdog_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register watchdog\n");
> +		clk_disable_unprepare(clk);
> +		return ret;
> +	}
> +
> +	st_wdog_setup(st_wdog, true);
> +
> +	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
> +		 st_wdog->warm_reset ? "warm" : "cold");
> +
> +	return ret;
> +}
> +
> +static int st_wdog_remove(struct platform_device *pdev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	st_wdog_setup(st_wdog, false);
> +	watchdog_unregister_device(&st_wdog_dev);
> +	clk_disable_unprepare(st_wdog->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int st_wdog_suspend(struct device *dev)
> +{
> +	if (watchdog_active(&st_wdog_dev))
> +		st_wdog_stop(&st_wdog_dev);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_resume(struct device *dev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	if (watchdog_active(&st_wdog_dev)) {
> +		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
> +		st_wdog_start(&st_wdog_dev);
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
> +			 st_wdog_suspend,
> +			 st_wdog_resume);
> +
> +static struct platform_driver st_wdog_driver = {
> +	.driver	= {
> +		.name = "st-lpc-wdt",
> +		.pm = &st_wdog_pm_ops,
> +	},
> +	.probe = st_wdog_probe,
> +	.remove = st_wdog_remove,
> +};
> +module_platform_driver(st_wdog_driver);
> +
> +MODULE_AUTHOR("David Paris <david.paris@st.com>");
> +MODULE_DESCRIPTION("ST LPC Watchdog Driver");
> +MODULE_LICENSE("GPL v2");


^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-15 12:52     ` David Paris
  0 siblings, 0 replies; 69+ messages in thread
From: David Paris @ 2014-12-15 12:52 UTC (permalink / raw)
  To: Lee Jones, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: kernel-F5mvAk5X5gdBDgjK7y7TUQ, a.zummo-BfzFCNDTiLLj+vYz1yj4TQ,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw, wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

Hi Lee,

     Something I didn't pointed out before, this driver is only supposed 
to support LPC_0 for stih407. The enable mask is only coded for LPC_0. 
Is it a wanted behavior ?

     In our current implementation here in ST is that LPC_0 is for A9 
Watchdog and LPC_1 for A9 global timer. This has been translated in the 
driver by hardcoding enable mask of LPC watchdog to only support LPC_0. 
But do we want to have a more generic driver to be applicable both for 
LPC_0 and LPC_1 ?

     David

On 12/15/2014 12:25 PM, Lee Jones wrote:
> Signed-off-by: David Paris <david.paris-qxv4g6HH51o@public.gmane.org>
> Signed-off-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>   drivers/watchdog/Kconfig  |  13 ++
>   drivers/watchdog/Makefile |   1 +
>   drivers/watchdog/st_wdt.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 326 insertions(+)
>   create mode 100644 drivers/watchdog/st_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index f57312f..5a538af 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
>   	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
>   	  the watchdog triggers the system will be reset.
>   
> +config ST_WATCHDOG
> +	tristate "STMicroelectronics LPC Watchdog"
> +	depends on ARCH_STI
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based Watchdog timer support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_wdt.
> +
>   config TEGRA_WATCHDOG
>   	tristate "Tegra watchdog"
>   	depends on ARCH_TEGRA || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 468c320..eb19937 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
>   obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
>   obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
>   obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
> +obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
>   obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
>   obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
>   
> diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
> new file mode 100644
> index 0000000..7e06f15
> --- /dev/null
> +++ b/drivers/watchdog/st_wdt.c
> @@ -0,0 +1,312 @@
> +/*
> + * st-wdt.c - ST's LPC Watchdog
> + *
> + * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
> + *
> + * Author: David Paris <david.paris-qxv4g6HH51o@public.gmane.org> for STMicroelectronics
> + *         Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> for STMicroelectronics
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/watchdog.h>
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF			0x410
> +#define LPC_LPA_START_OFF		0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF			0x510
> +
> +struct st_wdog_syscfg {
> +	struct regmap *regmap;
> +	unsigned int reset_type_reg;
> +	unsigned int reset_type_mask;
> +	unsigned int enable_reg;
> +	unsigned int enable_mask;
> +};
> +
> +struct st_wdog {
> +	void __iomem *base;
> +	struct device *dev;
> +	struct st_wdog_syscfg *syscfg;
> +	struct clk *clk;
> +	int warm_reset;
> +};
> +
> +static struct st_wdog_syscfg stid127_syscfg = {
> +	.reset_type_reg		= 0x004,
> +	.reset_type_mask	= BIT(2),
> +	.enable_reg		= 0x000,
> +	.enable_mask		= BIT(2),
> +};
> +
> +static struct st_wdog_syscfg stih415_syscfg = {
> +	.reset_type_reg		= 0x0B8,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x0B4,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih416_syscfg = {
> +	.reset_type_reg		= 0x88C,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x888,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih407_syscfg = {
> +	.enable_reg		= 0x204,
> +	.enable_mask		= BIT(19),
> +};
> +
> +static struct of_device_id st_wdog_match[] = {
> +	{
> +		.compatible = "st,stih407-lpc",
> +		.data = (void *)&stih407_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih416-lpc",
> +		.data = (void *)&stih416_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih415-lpc",
> +		.data = (void *)&stih415_syscfg,
> +	},
> +	{
> +		.compatible = "st,stid127-lpc",
> +		.data = (void *)&stid127_syscfg,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, st_wdog_match);
> +
> +static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
> +{
> +	/* Type of watchdog reset - 0: Cold 1: Warm */
> +	if (st_wdog->syscfg->reset_type_reg)
> +		regmap_update_bits(st_wdog->syscfg->regmap,
> +				   st_wdog->syscfg->reset_type_reg,
> +				   st_wdog->syscfg->reset_type_mask,
> +				   st_wdog->warm_reset);
> +
> +	/* Mask/unmask watchdog reset */
> +	regmap_update_bits(st_wdog->syscfg->regmap,
> +			   st_wdog->syscfg->enable_reg,
> +			   st_wdog->syscfg->enable_mask,
> +			   enable ? 0 : st_wdog->syscfg->enable_mask);
> +}
> +
> +static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
> +{
> +	unsigned long clkrate = clk_get_rate(st_wdog->clk);
> +	unsigned int maxtimeout = 0xFFFFFFFF / clkrate;
> +
> +	if (timeout > maxtimeout) {
> +		dev_warn(st_wdog->dev,
> +			 "timer overrun detected at current freq (%luHz)\n"
> +			 "wdog timeout set for %ds instead of requested %uis",
> +			 clkrate, maxtimeout, timeout);
> +		timeout = maxtimeout;
> +	}
> +	timeout *= clkrate;
> +
> +	writel_relaxed(timeout, st_wdog->base + LPC_LPA_LSB_OFF);
> +	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
> +}
> +
> +static int st_wdog_start(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_stop(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_set_timeout(struct watchdog_device *wdd,
> +			       unsigned int timeout)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	wdd->timeout = timeout;
> +	st_wdog_load_timer(st_wdog, timeout);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_keepalive(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	st_wdog_load_timer(st_wdog, wdd->timeout);
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info st_wdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "ST LPC WDT",
> +};
> +
> +static const struct watchdog_ops st_wdog_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= st_wdog_start,
> +	.stop		= st_wdog_stop,
> +	.ping		= st_wdog_keepalive,
> +	.set_timeout	= st_wdog_set_timeout,
> +};
> +
> +static struct watchdog_device st_wdog_dev = {
> +	.info		= &st_wdog_info,
> +	.ops		= &st_wdog_ops,
> +};
> +
> +static int st_wdog_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np;
> +	struct st_wdog *st_wdog;
> +	struct regmap *regmap;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *base;
> +	int ret;
> +
> +	/* This is a single node MFD device. */
> +	np = pdev->dev.of_node = pdev->dev.parent->of_node;
> +
> +	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
> +	if (!st_wdog)
> +		return -ENOMEM;
> +
> +	match = of_match_device(st_wdog_match, &pdev->dev);
> +	if (!match) {
> +		dev_err(&pdev->dev, "Couldn't match device\n");
> +		return -ENODEV;
> +	}
> +	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(base)) {
> +		dev_err(&pdev->dev, "Failed to ioremap base\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(regmap)) {
> +		dev_err(&pdev->dev, "No syscfg phandle specified\n");
> +		return PTR_ERR(regmap);
> +	}
> +
> +	clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(clk);
> +	}
> +	clk_prepare_enable(st_wdog->clk);
> +
> +	st_wdog->dev		= &pdev->dev;
> +	st_wdog->base		= base;
> +	st_wdog->clk		= clk;
> +	st_wdog->syscfg->regmap = regmap;
> +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> +
> +	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
> +	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
> +
> +	/* Init Watchdog timeout with value in DT */
> +	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
> +		return ret;
> +	}
> +
> +	ret = watchdog_register_device(&st_wdog_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register watchdog\n");
> +		clk_disable_unprepare(clk);
> +		return ret;
> +	}
> +
> +	st_wdog_setup(st_wdog, true);
> +
> +	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
> +		 st_wdog->warm_reset ? "warm" : "cold");
> +
> +	return ret;
> +}
> +
> +static int st_wdog_remove(struct platform_device *pdev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	st_wdog_setup(st_wdog, false);
> +	watchdog_unregister_device(&st_wdog_dev);
> +	clk_disable_unprepare(st_wdog->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int st_wdog_suspend(struct device *dev)
> +{
> +	if (watchdog_active(&st_wdog_dev))
> +		st_wdog_stop(&st_wdog_dev);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_resume(struct device *dev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	if (watchdog_active(&st_wdog_dev)) {
> +		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
> +		st_wdog_start(&st_wdog_dev);
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
> +			 st_wdog_suspend,
> +			 st_wdog_resume);
> +
> +static struct platform_driver st_wdog_driver = {
> +	.driver	= {
> +		.name = "st-lpc-wdt",
> +		.pm = &st_wdog_pm_ops,
> +	},
> +	.probe = st_wdog_probe,
> +	.remove = st_wdog_remove,
> +};
> +module_platform_driver(st_wdog_driver);
> +
> +MODULE_AUTHOR("David Paris <david.paris-qxv4g6HH51o@public.gmane.org>");
> +MODULE_DESCRIPTION("ST LPC Watchdog Driver");
> +MODULE_LICENSE("GPL v2");

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-15 12:52     ` David Paris
  0 siblings, 0 replies; 69+ messages in thread
From: David Paris @ 2014-12-15 12:52 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Lee,

     Something I didn't pointed out before, this driver is only supposed 
to support LPC_0 for stih407. The enable mask is only coded for LPC_0. 
Is it a wanted behavior ?

     In our current implementation here in ST is that LPC_0 is for A9 
Watchdog and LPC_1 for A9 global timer. This has been translated in the 
driver by hardcoding enable mask of LPC watchdog to only support LPC_0. 
But do we want to have a more generic driver to be applicable both for 
LPC_0 and LPC_1 ?

     David

On 12/15/2014 12:25 PM, Lee Jones wrote:
> Signed-off-by: David Paris <david.paris@st.com>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>   drivers/watchdog/Kconfig  |  13 ++
>   drivers/watchdog/Makefile |   1 +
>   drivers/watchdog/st_wdt.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 326 insertions(+)
>   create mode 100644 drivers/watchdog/st_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index f57312f..5a538af 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
>   	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
>   	  the watchdog triggers the system will be reset.
>   
> +config ST_WATCHDOG
> +	tristate "STMicroelectronics LPC Watchdog"
> +	depends on ARCH_STI
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based Watchdog timer support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_wdt.
> +
>   config TEGRA_WATCHDOG
>   	tristate "Tegra watchdog"
>   	depends on ARCH_TEGRA || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 468c320..eb19937 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
>   obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
>   obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
>   obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
> +obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
>   obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
>   obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
>   
> diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
> new file mode 100644
> index 0000000..7e06f15
> --- /dev/null
> +++ b/drivers/watchdog/st_wdt.c
> @@ -0,0 +1,312 @@
> +/*
> + * st-wdt.c - ST's LPC Watchdog
> + *
> + * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
> + *
> + * Author: David Paris <david.paris@st.com> for STMicroelectronics
> + *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/watchdog.h>
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF			0x410
> +#define LPC_LPA_START_OFF		0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF			0x510
> +
> +struct st_wdog_syscfg {
> +	struct regmap *regmap;
> +	unsigned int reset_type_reg;
> +	unsigned int reset_type_mask;
> +	unsigned int enable_reg;
> +	unsigned int enable_mask;
> +};
> +
> +struct st_wdog {
> +	void __iomem *base;
> +	struct device *dev;
> +	struct st_wdog_syscfg *syscfg;
> +	struct clk *clk;
> +	int warm_reset;
> +};
> +
> +static struct st_wdog_syscfg stid127_syscfg = {
> +	.reset_type_reg		= 0x004,
> +	.reset_type_mask	= BIT(2),
> +	.enable_reg		= 0x000,
> +	.enable_mask		= BIT(2),
> +};
> +
> +static struct st_wdog_syscfg stih415_syscfg = {
> +	.reset_type_reg		= 0x0B8,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x0B4,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih416_syscfg = {
> +	.reset_type_reg		= 0x88C,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x888,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih407_syscfg = {
> +	.enable_reg		= 0x204,
> +	.enable_mask		= BIT(19),
> +};
> +
> +static struct of_device_id st_wdog_match[] = {
> +	{
> +		.compatible = "st,stih407-lpc",
> +		.data = (void *)&stih407_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih416-lpc",
> +		.data = (void *)&stih416_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih415-lpc",
> +		.data = (void *)&stih415_syscfg,
> +	},
> +	{
> +		.compatible = "st,stid127-lpc",
> +		.data = (void *)&stid127_syscfg,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, st_wdog_match);
> +
> +static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
> +{
> +	/* Type of watchdog reset - 0: Cold 1: Warm */
> +	if (st_wdog->syscfg->reset_type_reg)
> +		regmap_update_bits(st_wdog->syscfg->regmap,
> +				   st_wdog->syscfg->reset_type_reg,
> +				   st_wdog->syscfg->reset_type_mask,
> +				   st_wdog->warm_reset);
> +
> +	/* Mask/unmask watchdog reset */
> +	regmap_update_bits(st_wdog->syscfg->regmap,
> +			   st_wdog->syscfg->enable_reg,
> +			   st_wdog->syscfg->enable_mask,
> +			   enable ? 0 : st_wdog->syscfg->enable_mask);
> +}
> +
> +static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
> +{
> +	unsigned long clkrate = clk_get_rate(st_wdog->clk);
> +	unsigned int maxtimeout = 0xFFFFFFFF / clkrate;
> +
> +	if (timeout > maxtimeout) {
> +		dev_warn(st_wdog->dev,
> +			 "timer overrun detected at current freq (%luHz)\n"
> +			 "wdog timeout set for %ds instead of requested %uis",
> +			 clkrate, maxtimeout, timeout);
> +		timeout = maxtimeout;
> +	}
> +	timeout *= clkrate;
> +
> +	writel_relaxed(timeout, st_wdog->base + LPC_LPA_LSB_OFF);
> +	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
> +}
> +
> +static int st_wdog_start(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_stop(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_set_timeout(struct watchdog_device *wdd,
> +			       unsigned int timeout)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	wdd->timeout = timeout;
> +	st_wdog_load_timer(st_wdog, timeout);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_keepalive(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	st_wdog_load_timer(st_wdog, wdd->timeout);
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info st_wdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "ST LPC WDT",
> +};
> +
> +static const struct watchdog_ops st_wdog_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= st_wdog_start,
> +	.stop		= st_wdog_stop,
> +	.ping		= st_wdog_keepalive,
> +	.set_timeout	= st_wdog_set_timeout,
> +};
> +
> +static struct watchdog_device st_wdog_dev = {
> +	.info		= &st_wdog_info,
> +	.ops		= &st_wdog_ops,
> +};
> +
> +static int st_wdog_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np;
> +	struct st_wdog *st_wdog;
> +	struct regmap *regmap;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *base;
> +	int ret;
> +
> +	/* This is a single node MFD device. */
> +	np = pdev->dev.of_node = pdev->dev.parent->of_node;
> +
> +	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
> +	if (!st_wdog)
> +		return -ENOMEM;
> +
> +	match = of_match_device(st_wdog_match, &pdev->dev);
> +	if (!match) {
> +		dev_err(&pdev->dev, "Couldn't match device\n");
> +		return -ENODEV;
> +	}
> +	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(base)) {
> +		dev_err(&pdev->dev, "Failed to ioremap base\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(regmap)) {
> +		dev_err(&pdev->dev, "No syscfg phandle specified\n");
> +		return PTR_ERR(regmap);
> +	}
> +
> +	clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(clk);
> +	}
> +	clk_prepare_enable(st_wdog->clk);
> +
> +	st_wdog->dev		= &pdev->dev;
> +	st_wdog->base		= base;
> +	st_wdog->clk		= clk;
> +	st_wdog->syscfg->regmap = regmap;
> +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> +
> +	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
> +	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
> +
> +	/* Init Watchdog timeout with value in DT */
> +	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
> +		return ret;
> +	}
> +
> +	ret = watchdog_register_device(&st_wdog_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register watchdog\n");
> +		clk_disable_unprepare(clk);
> +		return ret;
> +	}
> +
> +	st_wdog_setup(st_wdog, true);
> +
> +	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
> +		 st_wdog->warm_reset ? "warm" : "cold");
> +
> +	return ret;
> +}
> +
> +static int st_wdog_remove(struct platform_device *pdev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	st_wdog_setup(st_wdog, false);
> +	watchdog_unregister_device(&st_wdog_dev);
> +	clk_disable_unprepare(st_wdog->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int st_wdog_suspend(struct device *dev)
> +{
> +	if (watchdog_active(&st_wdog_dev))
> +		st_wdog_stop(&st_wdog_dev);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_resume(struct device *dev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	if (watchdog_active(&st_wdog_dev)) {
> +		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
> +		st_wdog_start(&st_wdog_dev);
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
> +			 st_wdog_suspend,
> +			 st_wdog_resume);
> +
> +static struct platform_driver st_wdog_driver = {
> +	.driver	= {
> +		.name = "st-lpc-wdt",
> +		.pm = &st_wdog_pm_ops,
> +	},
> +	.probe = st_wdog_probe,
> +	.remove = st_wdog_remove,
> +};
> +module_platform_driver(st_wdog_driver);
> +
> +MODULE_AUTHOR("David Paris <david.paris@st.com>");
> +MODULE_DESCRIPTION("ST LPC Watchdog Driver");
> +MODULE_LICENSE("GPL v2");

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 5/8] mfd: Add ST's Low Power Controller driver
@ 2014-12-15 13:38     ` Arnd Bergmann
  0 siblings, 0 replies; 69+ messages in thread
From: Arnd Bergmann @ 2014-12-15 13:38 UTC (permalink / raw)
  To: Lee Jones
  Cc: linux-arm-kernel, linux-kernel, kernel, a.zummo, rtc-linux, wim,
	linux-watchdog, devicetree

On Monday 15 December 2014 11:25:35 Lee Jones wrote:
> +       ret = of_property_read_u32(np, "st,lpc-mode", &mode);
> +       if (ret) {
> +               dev_err(&pdev->dev, "An LPC mode must be selected\n");
> +               return ret;
> +       }
> +
> +       switch (mode) {
> +       case ST_LPC_MODE_RTC:
> +               cell->name = "st-lpc-rtc";
> +               break;
> +       case ST_LPC_MODE_WDT:
> +               cell->name = "st-lpc-wdt";
> +               break;
> +       default:
> +               dev_err(&pdev->dev, "Unsupported mode: %d\n", mode);
> +               return ret;
> +       }
> +
> +       /* Pass resources though to selected child device. */
> +       cell->resources = pdev->resource;
> +       cell->num_resources = pdev->num_resources;
> +
> +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
> +                             cell, 1, NULL, 0, NULL);
> 

I don't think it's necessary to have the MFD node if only one of the
two modes can be used based on a DT property. It should be enough
to have both the rtc and the wdt driver list the same compatible
string and check the property in the probe function to decide if
they want to drive the device or not:

	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
	if (!ret && mode != ST_LPC_MODE_RTC)
		return -ENXIO


	Arnd

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 5/8] mfd: Add ST's Low Power Controller driver
@ 2014-12-15 13:38     ` Arnd Bergmann
  0 siblings, 0 replies; 69+ messages in thread
From: Arnd Bergmann @ 2014-12-15 13:38 UTC (permalink / raw)
  To: Lee Jones
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kernel-F5mvAk5X5gdBDgjK7y7TUQ, a.zummo-BfzFCNDTiLLj+vYz1yj4TQ,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw, wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Monday 15 December 2014 11:25:35 Lee Jones wrote:
> +       ret = of_property_read_u32(np, "st,lpc-mode", &mode);
> +       if (ret) {
> +               dev_err(&pdev->dev, "An LPC mode must be selected\n");
> +               return ret;
> +       }
> +
> +       switch (mode) {
> +       case ST_LPC_MODE_RTC:
> +               cell->name = "st-lpc-rtc";
> +               break;
> +       case ST_LPC_MODE_WDT:
> +               cell->name = "st-lpc-wdt";
> +               break;
> +       default:
> +               dev_err(&pdev->dev, "Unsupported mode: %d\n", mode);
> +               return ret;
> +       }
> +
> +       /* Pass resources though to selected child device. */
> +       cell->resources = pdev->resource;
> +       cell->num_resources = pdev->num_resources;
> +
> +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
> +                             cell, 1, NULL, 0, NULL);
> 

I don't think it's necessary to have the MFD node if only one of the
two modes can be used based on a DT property. It should be enough
to have both the rtc and the wdt driver list the same compatible
string and check the property in the probe function to decide if
they want to drive the device or not:

	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
	if (!ret && mode != ST_LPC_MODE_RTC)
		return -ENXIO


	Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 5/8] mfd: Add ST's Low Power Controller driver
@ 2014-12-15 13:38     ` Arnd Bergmann
  0 siblings, 0 replies; 69+ messages in thread
From: Arnd Bergmann @ 2014-12-15 13:38 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 15 December 2014 11:25:35 Lee Jones wrote:
> +       ret = of_property_read_u32(np, "st,lpc-mode", &mode);
> +       if (ret) {
> +               dev_err(&pdev->dev, "An LPC mode must be selected\n");
> +               return ret;
> +       }
> +
> +       switch (mode) {
> +       case ST_LPC_MODE_RTC:
> +               cell->name = "st-lpc-rtc";
> +               break;
> +       case ST_LPC_MODE_WDT:
> +               cell->name = "st-lpc-wdt";
> +               break;
> +       default:
> +               dev_err(&pdev->dev, "Unsupported mode: %d\n", mode);
> +               return ret;
> +       }
> +
> +       /* Pass resources though to selected child device. */
> +       cell->resources = pdev->resource;
> +       cell->num_resources = pdev->num_resources;
> +
> +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
> +                             cell, 1, NULL, 0, NULL);
> 

I don't think it's necessary to have the MFD node if only one of the
two modes can be used based on a DT property. It should be enough
to have both the rtc and the wdt driver list the same compatible
string and check the property in the probe function to decide if
they want to drive the device or not:

	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
	if (!ret && mode != ST_LPC_MODE_RTC)
		return -ENXIO


	Arnd

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 5/8] mfd: Add ST's Low Power Controller driver
  2014-12-15 13:38     ` Arnd Bergmann
  (?)
@ 2014-12-15 13:50       ` Lee Jones
  -1 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 13:50 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel, linux-kernel, kernel, a.zummo, rtc-linux, wim,
	linux-watchdog, devicetree

On Mon, 15 Dec 2014, Arnd Bergmann wrote:

> On Monday 15 December 2014 11:25:35 Lee Jones wrote:
> > +       ret = of_property_read_u32(np, "st,lpc-mode", &mode);
> > +       if (ret) {
> > +               dev_err(&pdev->dev, "An LPC mode must be selected\n");
> > +               return ret;
> > +       }
> > +
> > +       switch (mode) {
> > +       case ST_LPC_MODE_RTC:
> > +               cell->name = "st-lpc-rtc";
> > +               break;
> > +       case ST_LPC_MODE_WDT:
> > +               cell->name = "st-lpc-wdt";
> > +               break;
> > +       default:
> > +               dev_err(&pdev->dev, "Unsupported mode: %d\n", mode);
> > +               return ret;
> > +       }
> > +
> > +       /* Pass resources though to selected child device. */
> > +       cell->resources = pdev->resource;
> > +       cell->num_resources = pdev->num_resources;
> > +
> > +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
> > +                             cell, 1, NULL, 0, NULL);
> > 
> 
> I don't think it's necessary to have the MFD node if only one of the
> two modes can be used based on a DT property. It should be enough
> to have both the rtc and the wdt driver list the same compatible
> string and check the property in the probe function to decide if
> they want to drive the device or not:

I tried that and it didn't work.  Only one driver probed.

> 	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
> 	if (!ret && mode != ST_LPC_MODE_RTC)
> 		return -ENXIO
> 
> 
> 	Arnd

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 5/8] mfd: Add ST's Low Power Controller driver
@ 2014-12-15 13:50       ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 13:50 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kernel-F5mvAk5X5gdBDgjK7y7TUQ, a.zummo-BfzFCNDTiLLj+vYz1yj4TQ,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw, wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Mon, 15 Dec 2014, Arnd Bergmann wrote:

> On Monday 15 December 2014 11:25:35 Lee Jones wrote:
> > +       ret = of_property_read_u32(np, "st,lpc-mode", &mode);
> > +       if (ret) {
> > +               dev_err(&pdev->dev, "An LPC mode must be selected\n");
> > +               return ret;
> > +       }
> > +
> > +       switch (mode) {
> > +       case ST_LPC_MODE_RTC:
> > +               cell->name = "st-lpc-rtc";
> > +               break;
> > +       case ST_LPC_MODE_WDT:
> > +               cell->name = "st-lpc-wdt";
> > +               break;
> > +       default:
> > +               dev_err(&pdev->dev, "Unsupported mode: %d\n", mode);
> > +               return ret;
> > +       }
> > +
> > +       /* Pass resources though to selected child device. */
> > +       cell->resources = pdev->resource;
> > +       cell->num_resources = pdev->num_resources;
> > +
> > +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
> > +                             cell, 1, NULL, 0, NULL);
> > 
> 
> I don't think it's necessary to have the MFD node if only one of the
> two modes can be used based on a DT property. It should be enough
> to have both the rtc and the wdt driver list the same compatible
> string and check the property in the probe function to decide if
> they want to drive the device or not:

I tried that and it didn't work.  Only one driver probed.

> 	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
> 	if (!ret && mode != ST_LPC_MODE_RTC)
> 		return -ENXIO
> 
> 
> 	Arnd

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 5/8] mfd: Add ST's Low Power Controller driver
@ 2014-12-15 13:50       ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 13:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, 15 Dec 2014, Arnd Bergmann wrote:

> On Monday 15 December 2014 11:25:35 Lee Jones wrote:
> > +       ret = of_property_read_u32(np, "st,lpc-mode", &mode);
> > +       if (ret) {
> > +               dev_err(&pdev->dev, "An LPC mode must be selected\n");
> > +               return ret;
> > +       }
> > +
> > +       switch (mode) {
> > +       case ST_LPC_MODE_RTC:
> > +               cell->name = "st-lpc-rtc";
> > +               break;
> > +       case ST_LPC_MODE_WDT:
> > +               cell->name = "st-lpc-wdt";
> > +               break;
> > +       default:
> > +               dev_err(&pdev->dev, "Unsupported mode: %d\n", mode);
> > +               return ret;
> > +       }
> > +
> > +       /* Pass resources though to selected child device. */
> > +       cell->resources = pdev->resource;
> > +       cell->num_resources = pdev->num_resources;
> > +
> > +       ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
> > +                             cell, 1, NULL, 0, NULL);
> > 
> 
> I don't think it's necessary to have the MFD node if only one of the
> two modes can be used based on a DT property. It should be enough
> to have both the rtc and the wdt driver list the same compatible
> string and check the property in the probe function to decide if
> they want to drive the device or not:

I tried that and it didn't work.  Only one driver probed.

> 	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
> 	if (!ret && mode != ST_LPC_MODE_RTC)
> 		return -ENXIO
> 
> 
> 	Arnd

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 5/8] mfd: Add ST's Low Power Controller driver
  2014-12-15 13:50       ` Lee Jones
  (?)
@ 2014-12-15 14:06         ` Arnd Bergmann
  -1 siblings, 0 replies; 69+ messages in thread
From: Arnd Bergmann @ 2014-12-15 14:06 UTC (permalink / raw)
  To: Lee Jones
  Cc: linux-arm-kernel, linux-kernel, kernel, a.zummo, rtc-linux, wim,
	linux-watchdog, devicetree

On Monday 15 December 2014 13:50:52 Lee Jones wrote:
> > > 
> > 
> > I don't think it's necessary to have the MFD node if only one of the
> > two modes can be used based on a DT property. It should be enough
> > to have both the rtc and the wdt driver list the same compatible
> > string and check the property in the probe function to decide if
> > they want to drive the device or not:
> 
> I tried that and it didn't work.  Only one driver probed.
> 
> 

Strange, the code in really_probe() and the comment in device_attach()
suggest that this is not the intentional behavior. What error
code did you return? If it's -ENODEV or -ENXIO, it should keep
trying the other drivers, otherwise it will give up.

	Arnd

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 5/8] mfd: Add ST's Low Power Controller driver
@ 2014-12-15 14:06         ` Arnd Bergmann
  0 siblings, 0 replies; 69+ messages in thread
From: Arnd Bergmann @ 2014-12-15 14:06 UTC (permalink / raw)
  To: Lee Jones
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kernel-F5mvAk5X5gdBDgjK7y7TUQ, a.zummo-BfzFCNDTiLLj+vYz1yj4TQ,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw, wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Monday 15 December 2014 13:50:52 Lee Jones wrote:
> > > 
> > 
> > I don't think it's necessary to have the MFD node if only one of the
> > two modes can be used based on a DT property. It should be enough
> > to have both the rtc and the wdt driver list the same compatible
> > string and check the property in the probe function to decide if
> > they want to drive the device or not:
> 
> I tried that and it didn't work.  Only one driver probed.
> 
> 

Strange, the code in really_probe() and the comment in device_attach()
suggest that this is not the intentional behavior. What error
code did you return? If it's -ENODEV or -ENXIO, it should keep
trying the other drivers, otherwise it will give up.

	Arnd
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 5/8] mfd: Add ST's Low Power Controller driver
@ 2014-12-15 14:06         ` Arnd Bergmann
  0 siblings, 0 replies; 69+ messages in thread
From: Arnd Bergmann @ 2014-12-15 14:06 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday 15 December 2014 13:50:52 Lee Jones wrote:
> > > 
> > 
> > I don't think it's necessary to have the MFD node if only one of the
> > two modes can be used based on a DT property. It should be enough
> > to have both the rtc and the wdt driver list the same compatible
> > string and check the property in the probe function to decide if
> > they want to drive the device or not:
> 
> I tried that and it didn't work.  Only one driver probed.
> 
> 

Strange, the code in really_probe() and the comment in device_attach()
suggest that this is not the intentional behavior. What error
code did you return? If it's -ENODEV or -ENXIO, it should keep
trying the other drivers, otherwise it will give up.

	Arnd

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-15 14:15     ` Guenter Roeck
  0 siblings, 0 replies; 69+ messages in thread
From: Guenter Roeck @ 2014-12-15 14:15 UTC (permalink / raw)
  To: Lee Jones, linux-arm-kernel, linux-kernel
  Cc: kernel, a.zummo, rtc-linux, wim, linux-watchdog, devicetree, David Paris

On 12/15/2014 03:25 AM, Lee Jones wrote:
> Signed-off-by: David Paris <david.paris@st.com>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>   drivers/watchdog/Kconfig  |  13 ++
>   drivers/watchdog/Makefile |   1 +
>   drivers/watchdog/st_wdt.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 326 insertions(+)
>   create mode 100644 drivers/watchdog/st_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index f57312f..5a538af 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
>   	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
>   	  the watchdog triggers the system will be reset.
>
> +config ST_WATCHDOG
> +	tristate "STMicroelectronics LPC Watchdog"
> +	depends on ARCH_STI
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based Watchdog timer support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_wdt.
> +
>   config TEGRA_WATCHDOG
>   	tristate "Tegra watchdog"
>   	depends on ARCH_TEGRA || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 468c320..eb19937 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
>   obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
>   obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
>   obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
> +obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
>   obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
>   obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
>
> diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
> new file mode 100644
> index 0000000..7e06f15
> --- /dev/null
> +++ b/drivers/watchdog/st_wdt.c
> @@ -0,0 +1,312 @@
> +/*
> + * st-wdt.c - ST's LPC Watchdog
> + *
> + * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
> + *
> + * Author: David Paris <david.paris@st.com> for STMicroelectronics
> + *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/watchdog.h>
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF			0x410
> +#define LPC_LPA_START_OFF		0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF			0x510
> +
> +struct st_wdog_syscfg {
> +	struct regmap *regmap;
> +	unsigned int reset_type_reg;
> +	unsigned int reset_type_mask;
> +	unsigned int enable_reg;
> +	unsigned int enable_mask;
> +};
> +
> +struct st_wdog {
> +	void __iomem *base;
> +	struct device *dev;
> +	struct st_wdog_syscfg *syscfg;
> +	struct clk *clk;
> +	int warm_reset;
> +};
> +
> +static struct st_wdog_syscfg stid127_syscfg = {
> +	.reset_type_reg		= 0x004,
> +	.reset_type_mask	= BIT(2),
> +	.enable_reg		= 0x000,
> +	.enable_mask		= BIT(2),
> +};
> +
> +static struct st_wdog_syscfg stih415_syscfg = {
> +	.reset_type_reg		= 0x0B8,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x0B4,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih416_syscfg = {
> +	.reset_type_reg		= 0x88C,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x888,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih407_syscfg = {
> +	.enable_reg		= 0x204,
> +	.enable_mask		= BIT(19),
> +};
> +
> +static struct of_device_id st_wdog_match[] = {
> +	{
> +		.compatible = "st,stih407-lpc",
> +		.data = (void *)&stih407_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih416-lpc",
> +		.data = (void *)&stih416_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih415-lpc",
> +		.data = (void *)&stih415_syscfg,
> +	},
> +	{
> +		.compatible = "st,stid127-lpc",
> +		.data = (void *)&stid127_syscfg,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, st_wdog_match);
> +
> +static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
> +{
> +	/* Type of watchdog reset - 0: Cold 1: Warm */
> +	if (st_wdog->syscfg->reset_type_reg)
> +		regmap_update_bits(st_wdog->syscfg->regmap,
> +				   st_wdog->syscfg->reset_type_reg,
> +				   st_wdog->syscfg->reset_type_mask,
> +				   st_wdog->warm_reset);
> +
> +	/* Mask/unmask watchdog reset */
> +	regmap_update_bits(st_wdog->syscfg->regmap,
> +			   st_wdog->syscfg->enable_reg,
> +			   st_wdog->syscfg->enable_mask,
> +			   enable ? 0 : st_wdog->syscfg->enable_mask);
> +}
> +
> +static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
> +{
> +	unsigned long clkrate = clk_get_rate(st_wdog->clk);
> +	unsigned int maxtimeout = 0xFFFFFFFF / clkrate;
> +
> +	if (timeout > maxtimeout) {
> +		dev_warn(st_wdog->dev,
> +			 "timer overrun detected at current freq (%luHz)\n"
> +			 "wdog timeout set for %ds instead of requested %uis",
> +			 clkrate, maxtimeout, timeout);

This warning will be repeated each time the watchdog is pinged and thus
has zero value except for filling up the console log.

> +		timeout = maxtimeout;
> +	}
> +	timeout *= clkrate;
> +
> +	writel_relaxed(timeout, st_wdog->base + LPC_LPA_LSB_OFF);
> +	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
> +}
> +
> +static int st_wdog_start(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_stop(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_set_timeout(struct watchdog_device *wdd,
> +			       unsigned int timeout)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	wdd->timeout = timeout;
> +	st_wdog_load_timer(st_wdog, timeout);
> +

You should set the valid limits in the watchdog_device variable
to have the infrastructure enforce it.

Accepting all timeouts and then dumping a warning on the console each time
the watchdog is pinged will create a lot of unnecessary console noise.

Guenter

> +	return 0;
> +}
> +
> +static int st_wdog_keepalive(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	st_wdog_load_timer(st_wdog, wdd->timeout);
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info st_wdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "ST LPC WDT",
> +};
> +
> +static const struct watchdog_ops st_wdog_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= st_wdog_start,
> +	.stop		= st_wdog_stop,
> +	.ping		= st_wdog_keepalive,
> +	.set_timeout	= st_wdog_set_timeout,
> +};
> +
> +static struct watchdog_device st_wdog_dev = {
> +	.info		= &st_wdog_info,
> +	.ops		= &st_wdog_ops,
> +};
> +
> +static int st_wdog_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np;
> +	struct st_wdog *st_wdog;
> +	struct regmap *regmap;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *base;
> +	int ret;
> +
> +	/* This is a single node MFD device. */
> +	np = pdev->dev.of_node = pdev->dev.parent->of_node;
> +
> +	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
> +	if (!st_wdog)
> +		return -ENOMEM;
> +
> +	match = of_match_device(st_wdog_match, &pdev->dev);
> +	if (!match) {
> +		dev_err(&pdev->dev, "Couldn't match device\n");
> +		return -ENODEV;
> +	}
> +	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(base)) {
> +		dev_err(&pdev->dev, "Failed to ioremap base\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(regmap)) {
> +		dev_err(&pdev->dev, "No syscfg phandle specified\n");
> +		return PTR_ERR(regmap);
> +	}
> +
> +	clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(clk);
> +	}
> +	clk_prepare_enable(st_wdog->clk);
> +
> +	st_wdog->dev		= &pdev->dev;
> +	st_wdog->base		= base;
> +	st_wdog->clk		= clk;
> +	st_wdog->syscfg->regmap = regmap;
> +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> +
> +	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
> +	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
> +
> +	/* Init Watchdog timeout with value in DT */
> +	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
> +		return ret;
> +	}
> +
> +	ret = watchdog_register_device(&st_wdog_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register watchdog\n");
> +		clk_disable_unprepare(clk);
> +		return ret;
> +	}
> +
> +	st_wdog_setup(st_wdog, true);
> +
> +	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
> +		 st_wdog->warm_reset ? "warm" : "cold");
> +
> +	return ret;
> +}
> +
> +static int st_wdog_remove(struct platform_device *pdev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	st_wdog_setup(st_wdog, false);
> +	watchdog_unregister_device(&st_wdog_dev);
> +	clk_disable_unprepare(st_wdog->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int st_wdog_suspend(struct device *dev)
> +{
> +	if (watchdog_active(&st_wdog_dev))
> +		st_wdog_stop(&st_wdog_dev);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_resume(struct device *dev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	if (watchdog_active(&st_wdog_dev)) {
> +		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
> +		st_wdog_start(&st_wdog_dev);
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
> +			 st_wdog_suspend,
> +			 st_wdog_resume);
> +
> +static struct platform_driver st_wdog_driver = {
> +	.driver	= {
> +		.name = "st-lpc-wdt",
> +		.pm = &st_wdog_pm_ops,
> +	},
> +	.probe = st_wdog_probe,
> +	.remove = st_wdog_remove,
> +};
> +module_platform_driver(st_wdog_driver);
> +
> +MODULE_AUTHOR("David Paris <david.paris@st.com>");
> +MODULE_DESCRIPTION("ST LPC Watchdog Driver");
> +MODULE_LICENSE("GPL v2");
>


^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-15 14:15     ` Guenter Roeck
  0 siblings, 0 replies; 69+ messages in thread
From: Guenter Roeck @ 2014-12-15 14:15 UTC (permalink / raw)
  To: Lee Jones, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: kernel-F5mvAk5X5gdBDgjK7y7TUQ, a.zummo-BfzFCNDTiLLj+vYz1yj4TQ,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw, wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, David Paris

On 12/15/2014 03:25 AM, Lee Jones wrote:
> Signed-off-by: David Paris <david.paris-qxv4g6HH51o@public.gmane.org>
> Signed-off-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>   drivers/watchdog/Kconfig  |  13 ++
>   drivers/watchdog/Makefile |   1 +
>   drivers/watchdog/st_wdt.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 326 insertions(+)
>   create mode 100644 drivers/watchdog/st_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index f57312f..5a538af 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
>   	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
>   	  the watchdog triggers the system will be reset.
>
> +config ST_WATCHDOG
> +	tristate "STMicroelectronics LPC Watchdog"
> +	depends on ARCH_STI
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based Watchdog timer support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_wdt.
> +
>   config TEGRA_WATCHDOG
>   	tristate "Tegra watchdog"
>   	depends on ARCH_TEGRA || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 468c320..eb19937 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
>   obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
>   obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
>   obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
> +obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
>   obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
>   obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
>
> diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
> new file mode 100644
> index 0000000..7e06f15
> --- /dev/null
> +++ b/drivers/watchdog/st_wdt.c
> @@ -0,0 +1,312 @@
> +/*
> + * st-wdt.c - ST's LPC Watchdog
> + *
> + * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
> + *
> + * Author: David Paris <david.paris-qxv4g6HH51o@public.gmane.org> for STMicroelectronics
> + *         Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> for STMicroelectronics
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/watchdog.h>
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF			0x410
> +#define LPC_LPA_START_OFF		0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF			0x510
> +
> +struct st_wdog_syscfg {
> +	struct regmap *regmap;
> +	unsigned int reset_type_reg;
> +	unsigned int reset_type_mask;
> +	unsigned int enable_reg;
> +	unsigned int enable_mask;
> +};
> +
> +struct st_wdog {
> +	void __iomem *base;
> +	struct device *dev;
> +	struct st_wdog_syscfg *syscfg;
> +	struct clk *clk;
> +	int warm_reset;
> +};
> +
> +static struct st_wdog_syscfg stid127_syscfg = {
> +	.reset_type_reg		= 0x004,
> +	.reset_type_mask	= BIT(2),
> +	.enable_reg		= 0x000,
> +	.enable_mask		= BIT(2),
> +};
> +
> +static struct st_wdog_syscfg stih415_syscfg = {
> +	.reset_type_reg		= 0x0B8,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x0B4,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih416_syscfg = {
> +	.reset_type_reg		= 0x88C,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x888,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih407_syscfg = {
> +	.enable_reg		= 0x204,
> +	.enable_mask		= BIT(19),
> +};
> +
> +static struct of_device_id st_wdog_match[] = {
> +	{
> +		.compatible = "st,stih407-lpc",
> +		.data = (void *)&stih407_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih416-lpc",
> +		.data = (void *)&stih416_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih415-lpc",
> +		.data = (void *)&stih415_syscfg,
> +	},
> +	{
> +		.compatible = "st,stid127-lpc",
> +		.data = (void *)&stid127_syscfg,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, st_wdog_match);
> +
> +static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
> +{
> +	/* Type of watchdog reset - 0: Cold 1: Warm */
> +	if (st_wdog->syscfg->reset_type_reg)
> +		regmap_update_bits(st_wdog->syscfg->regmap,
> +				   st_wdog->syscfg->reset_type_reg,
> +				   st_wdog->syscfg->reset_type_mask,
> +				   st_wdog->warm_reset);
> +
> +	/* Mask/unmask watchdog reset */
> +	regmap_update_bits(st_wdog->syscfg->regmap,
> +			   st_wdog->syscfg->enable_reg,
> +			   st_wdog->syscfg->enable_mask,
> +			   enable ? 0 : st_wdog->syscfg->enable_mask);
> +}
> +
> +static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
> +{
> +	unsigned long clkrate = clk_get_rate(st_wdog->clk);
> +	unsigned int maxtimeout = 0xFFFFFFFF / clkrate;
> +
> +	if (timeout > maxtimeout) {
> +		dev_warn(st_wdog->dev,
> +			 "timer overrun detected at current freq (%luHz)\n"
> +			 "wdog timeout set for %ds instead of requested %uis",
> +			 clkrate, maxtimeout, timeout);

This warning will be repeated each time the watchdog is pinged and thus
has zero value except for filling up the console log.

> +		timeout = maxtimeout;
> +	}
> +	timeout *= clkrate;
> +
> +	writel_relaxed(timeout, st_wdog->base + LPC_LPA_LSB_OFF);
> +	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
> +}
> +
> +static int st_wdog_start(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_stop(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_set_timeout(struct watchdog_device *wdd,
> +			       unsigned int timeout)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	wdd->timeout = timeout;
> +	st_wdog_load_timer(st_wdog, timeout);
> +

You should set the valid limits in the watchdog_device variable
to have the infrastructure enforce it.

Accepting all timeouts and then dumping a warning on the console each time
the watchdog is pinged will create a lot of unnecessary console noise.

Guenter

> +	return 0;
> +}
> +
> +static int st_wdog_keepalive(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	st_wdog_load_timer(st_wdog, wdd->timeout);
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info st_wdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "ST LPC WDT",
> +};
> +
> +static const struct watchdog_ops st_wdog_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= st_wdog_start,
> +	.stop		= st_wdog_stop,
> +	.ping		= st_wdog_keepalive,
> +	.set_timeout	= st_wdog_set_timeout,
> +};
> +
> +static struct watchdog_device st_wdog_dev = {
> +	.info		= &st_wdog_info,
> +	.ops		= &st_wdog_ops,
> +};
> +
> +static int st_wdog_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np;
> +	struct st_wdog *st_wdog;
> +	struct regmap *regmap;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *base;
> +	int ret;
> +
> +	/* This is a single node MFD device. */
> +	np = pdev->dev.of_node = pdev->dev.parent->of_node;
> +
> +	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
> +	if (!st_wdog)
> +		return -ENOMEM;
> +
> +	match = of_match_device(st_wdog_match, &pdev->dev);
> +	if (!match) {
> +		dev_err(&pdev->dev, "Couldn't match device\n");
> +		return -ENODEV;
> +	}
> +	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(base)) {
> +		dev_err(&pdev->dev, "Failed to ioremap base\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(regmap)) {
> +		dev_err(&pdev->dev, "No syscfg phandle specified\n");
> +		return PTR_ERR(regmap);
> +	}
> +
> +	clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(clk);
> +	}
> +	clk_prepare_enable(st_wdog->clk);
> +
> +	st_wdog->dev		= &pdev->dev;
> +	st_wdog->base		= base;
> +	st_wdog->clk		= clk;
> +	st_wdog->syscfg->regmap = regmap;
> +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> +
> +	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
> +	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
> +
> +	/* Init Watchdog timeout with value in DT */
> +	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
> +		return ret;
> +	}
> +
> +	ret = watchdog_register_device(&st_wdog_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register watchdog\n");
> +		clk_disable_unprepare(clk);
> +		return ret;
> +	}
> +
> +	st_wdog_setup(st_wdog, true);
> +
> +	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
> +		 st_wdog->warm_reset ? "warm" : "cold");
> +
> +	return ret;
> +}
> +
> +static int st_wdog_remove(struct platform_device *pdev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	st_wdog_setup(st_wdog, false);
> +	watchdog_unregister_device(&st_wdog_dev);
> +	clk_disable_unprepare(st_wdog->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int st_wdog_suspend(struct device *dev)
> +{
> +	if (watchdog_active(&st_wdog_dev))
> +		st_wdog_stop(&st_wdog_dev);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_resume(struct device *dev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	if (watchdog_active(&st_wdog_dev)) {
> +		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
> +		st_wdog_start(&st_wdog_dev);
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
> +			 st_wdog_suspend,
> +			 st_wdog_resume);
> +
> +static struct platform_driver st_wdog_driver = {
> +	.driver	= {
> +		.name = "st-lpc-wdt",
> +		.pm = &st_wdog_pm_ops,
> +	},
> +	.probe = st_wdog_probe,
> +	.remove = st_wdog_remove,
> +};
> +module_platform_driver(st_wdog_driver);
> +
> +MODULE_AUTHOR("David Paris <david.paris-qxv4g6HH51o@public.gmane.org>");
> +MODULE_DESCRIPTION("ST LPC Watchdog Driver");
> +MODULE_LICENSE("GPL v2");
>

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-15 14:15     ` Guenter Roeck
  0 siblings, 0 replies; 69+ messages in thread
From: Guenter Roeck @ 2014-12-15 14:15 UTC (permalink / raw)
  To: linux-arm-kernel

On 12/15/2014 03:25 AM, Lee Jones wrote:
> Signed-off-by: David Paris <david.paris@st.com>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>   drivers/watchdog/Kconfig  |  13 ++
>   drivers/watchdog/Makefile |   1 +
>   drivers/watchdog/st_wdt.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 326 insertions(+)
>   create mode 100644 drivers/watchdog/st_wdt.c
>
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index f57312f..5a538af 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
>   	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
>   	  the watchdog triggers the system will be reset.
>
> +config ST_WATCHDOG
> +	tristate "STMicroelectronics LPC Watchdog"
> +	depends on ARCH_STI
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based Watchdog timer support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_wdt.
> +
>   config TEGRA_WATCHDOG
>   	tristate "Tegra watchdog"
>   	depends on ARCH_TEGRA || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 468c320..eb19937 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
>   obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
>   obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
>   obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
> +obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
>   obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
>   obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
>
> diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
> new file mode 100644
> index 0000000..7e06f15
> --- /dev/null
> +++ b/drivers/watchdog/st_wdt.c
> @@ -0,0 +1,312 @@
> +/*
> + * st-wdt.c - ST's LPC Watchdog
> + *
> + * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
> + *
> + * Author: David Paris <david.paris@st.com> for STMicroelectronics
> + *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/watchdog.h>
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF			0x410
> +#define LPC_LPA_START_OFF		0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF			0x510
> +
> +struct st_wdog_syscfg {
> +	struct regmap *regmap;
> +	unsigned int reset_type_reg;
> +	unsigned int reset_type_mask;
> +	unsigned int enable_reg;
> +	unsigned int enable_mask;
> +};
> +
> +struct st_wdog {
> +	void __iomem *base;
> +	struct device *dev;
> +	struct st_wdog_syscfg *syscfg;
> +	struct clk *clk;
> +	int warm_reset;
> +};
> +
> +static struct st_wdog_syscfg stid127_syscfg = {
> +	.reset_type_reg		= 0x004,
> +	.reset_type_mask	= BIT(2),
> +	.enable_reg		= 0x000,
> +	.enable_mask		= BIT(2),
> +};
> +
> +static struct st_wdog_syscfg stih415_syscfg = {
> +	.reset_type_reg		= 0x0B8,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x0B4,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih416_syscfg = {
> +	.reset_type_reg		= 0x88C,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x888,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih407_syscfg = {
> +	.enable_reg		= 0x204,
> +	.enable_mask		= BIT(19),
> +};
> +
> +static struct of_device_id st_wdog_match[] = {
> +	{
> +		.compatible = "st,stih407-lpc",
> +		.data = (void *)&stih407_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih416-lpc",
> +		.data = (void *)&stih416_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih415-lpc",
> +		.data = (void *)&stih415_syscfg,
> +	},
> +	{
> +		.compatible = "st,stid127-lpc",
> +		.data = (void *)&stid127_syscfg,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, st_wdog_match);
> +
> +static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
> +{
> +	/* Type of watchdog reset - 0: Cold 1: Warm */
> +	if (st_wdog->syscfg->reset_type_reg)
> +		regmap_update_bits(st_wdog->syscfg->regmap,
> +				   st_wdog->syscfg->reset_type_reg,
> +				   st_wdog->syscfg->reset_type_mask,
> +				   st_wdog->warm_reset);
> +
> +	/* Mask/unmask watchdog reset */
> +	regmap_update_bits(st_wdog->syscfg->regmap,
> +			   st_wdog->syscfg->enable_reg,
> +			   st_wdog->syscfg->enable_mask,
> +			   enable ? 0 : st_wdog->syscfg->enable_mask);
> +}
> +
> +static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
> +{
> +	unsigned long clkrate = clk_get_rate(st_wdog->clk);
> +	unsigned int maxtimeout = 0xFFFFFFFF / clkrate;
> +
> +	if (timeout > maxtimeout) {
> +		dev_warn(st_wdog->dev,
> +			 "timer overrun detected at current freq (%luHz)\n"
> +			 "wdog timeout set for %ds instead of requested %uis",
> +			 clkrate, maxtimeout, timeout);

This warning will be repeated each time the watchdog is pinged and thus
has zero value except for filling up the console log.

> +		timeout = maxtimeout;
> +	}
> +	timeout *= clkrate;
> +
> +	writel_relaxed(timeout, st_wdog->base + LPC_LPA_LSB_OFF);
> +	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
> +}
> +
> +static int st_wdog_start(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_stop(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_set_timeout(struct watchdog_device *wdd,
> +			       unsigned int timeout)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	wdd->timeout = timeout;
> +	st_wdog_load_timer(st_wdog, timeout);
> +

You should set the valid limits in the watchdog_device variable
to have the infrastructure enforce it.

Accepting all timeouts and then dumping a warning on the console each time
the watchdog is pinged will create a lot of unnecessary console noise.

Guenter

> +	return 0;
> +}
> +
> +static int st_wdog_keepalive(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	st_wdog_load_timer(st_wdog, wdd->timeout);
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info st_wdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "ST LPC WDT",
> +};
> +
> +static const struct watchdog_ops st_wdog_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= st_wdog_start,
> +	.stop		= st_wdog_stop,
> +	.ping		= st_wdog_keepalive,
> +	.set_timeout	= st_wdog_set_timeout,
> +};
> +
> +static struct watchdog_device st_wdog_dev = {
> +	.info		= &st_wdog_info,
> +	.ops		= &st_wdog_ops,
> +};
> +
> +static int st_wdog_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np;
> +	struct st_wdog *st_wdog;
> +	struct regmap *regmap;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *base;
> +	int ret;
> +
> +	/* This is a single node MFD device. */
> +	np = pdev->dev.of_node = pdev->dev.parent->of_node;
> +
> +	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
> +	if (!st_wdog)
> +		return -ENOMEM;
> +
> +	match = of_match_device(st_wdog_match, &pdev->dev);
> +	if (!match) {
> +		dev_err(&pdev->dev, "Couldn't match device\n");
> +		return -ENODEV;
> +	}
> +	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(base)) {
> +		dev_err(&pdev->dev, "Failed to ioremap base\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(regmap)) {
> +		dev_err(&pdev->dev, "No syscfg phandle specified\n");
> +		return PTR_ERR(regmap);
> +	}
> +
> +	clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(clk);
> +	}
> +	clk_prepare_enable(st_wdog->clk);
> +
> +	st_wdog->dev		= &pdev->dev;
> +	st_wdog->base		= base;
> +	st_wdog->clk		= clk;
> +	st_wdog->syscfg->regmap = regmap;
> +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> +
> +	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
> +	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
> +
> +	/* Init Watchdog timeout with value in DT */
> +	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
> +		return ret;
> +	}
> +
> +	ret = watchdog_register_device(&st_wdog_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register watchdog\n");
> +		clk_disable_unprepare(clk);
> +		return ret;
> +	}
> +
> +	st_wdog_setup(st_wdog, true);
> +
> +	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
> +		 st_wdog->warm_reset ? "warm" : "cold");
> +
> +	return ret;
> +}
> +
> +static int st_wdog_remove(struct platform_device *pdev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	st_wdog_setup(st_wdog, false);
> +	watchdog_unregister_device(&st_wdog_dev);
> +	clk_disable_unprepare(st_wdog->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int st_wdog_suspend(struct device *dev)
> +{
> +	if (watchdog_active(&st_wdog_dev))
> +		st_wdog_stop(&st_wdog_dev);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_resume(struct device *dev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	if (watchdog_active(&st_wdog_dev)) {
> +		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
> +		st_wdog_start(&st_wdog_dev);
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
> +			 st_wdog_suspend,
> +			 st_wdog_resume);
> +
> +static struct platform_driver st_wdog_driver = {
> +	.driver	= {
> +		.name = "st-lpc-wdt",
> +		.pm = &st_wdog_pm_ops,
> +	},
> +	.probe = st_wdog_probe,
> +	.remove = st_wdog_remove,
> +};
> +module_platform_driver(st_wdog_driver);
> +
> +MODULE_AUTHOR("David Paris <david.paris@st.com>");
> +MODULE_DESCRIPTION("ST LPC Watchdog Driver");
> +MODULE_LICENSE("GPL v2");
>

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 7/8] rtc: st: add new driver for ST's LPC RTC
@ 2014-12-15 14:17     ` Guenter Roeck
  0 siblings, 0 replies; 69+ messages in thread
From: Guenter Roeck @ 2014-12-15 14:17 UTC (permalink / raw)
  To: Lee Jones, linux-arm-kernel, linux-kernel
  Cc: kernel, a.zummo, rtc-linux, wim, linux-watchdog, devicetree

On 12/15/2014 03:25 AM, Lee Jones wrote:
> ST's Low Power Controller (LPC) controls two devices; watchdog and RTC.
> Only one of the devices can be used at any one time.  This is enforced
> by the correlating MFD driver.  This portion of the driver-set controls
> the Real Time Clock.
>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>   drivers/rtc/Kconfig      |  13 ++
>   drivers/rtc/Makefile     |   1 +
>   drivers/rtc/rtc-st-lpc.c | 330 +++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 344 insertions(+)
>   create mode 100644 drivers/rtc/rtc-st-lpc.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index a168e96..aa4bd90 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -1355,6 +1355,18 @@ config RTC_DRV_SIRFSOC
>   	  Say "yes" here to support the real time clock on SiRF SOC chips.
>   	  This driver can also be built as a module called rtc-sirfsoc.
>
> +config RTC_DRV_ST_LPC
> +	tristate "STMicroelectronics LPC RTC"
> +	depends on ARCH_STI
> +	depends on OF
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based RTC support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called rtc-st-lpc.
> +
>   config RTC_DRV_MOXART
>   	tristate "MOXA ART RTC"
>   	depends on ARCH_MOXART || COMPILE_TEST
> @@ -1390,4 +1402,5 @@ config RTC_DRV_HID_SENSOR_TIME
>   	  rtc-hid-sensor-time.
>
>
> +

Hi Lee,

Yet another empty line ?

Guenter


^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 7/8] rtc: st: add new driver for ST's LPC RTC
@ 2014-12-15 14:17     ` Guenter Roeck
  0 siblings, 0 replies; 69+ messages in thread
From: Guenter Roeck @ 2014-12-15 14:17 UTC (permalink / raw)
  To: Lee Jones, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: kernel-F5mvAk5X5gdBDgjK7y7TUQ, a.zummo-BfzFCNDTiLLj+vYz1yj4TQ,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw, wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On 12/15/2014 03:25 AM, Lee Jones wrote:
> ST's Low Power Controller (LPC) controls two devices; watchdog and RTC.
> Only one of the devices can be used at any one time.  This is enforced
> by the correlating MFD driver.  This portion of the driver-set controls
> the Real Time Clock.
>
> Signed-off-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>   drivers/rtc/Kconfig      |  13 ++
>   drivers/rtc/Makefile     |   1 +
>   drivers/rtc/rtc-st-lpc.c | 330 +++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 344 insertions(+)
>   create mode 100644 drivers/rtc/rtc-st-lpc.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index a168e96..aa4bd90 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -1355,6 +1355,18 @@ config RTC_DRV_SIRFSOC
>   	  Say "yes" here to support the real time clock on SiRF SOC chips.
>   	  This driver can also be built as a module called rtc-sirfsoc.
>
> +config RTC_DRV_ST_LPC
> +	tristate "STMicroelectronics LPC RTC"
> +	depends on ARCH_STI
> +	depends on OF
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based RTC support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called rtc-st-lpc.
> +
>   config RTC_DRV_MOXART
>   	tristate "MOXA ART RTC"
>   	depends on ARCH_MOXART || COMPILE_TEST
> @@ -1390,4 +1402,5 @@ config RTC_DRV_HID_SENSOR_TIME
>   	  rtc-hid-sensor-time.
>
>
> +

Hi Lee,

Yet another empty line ?

Guenter

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 7/8] rtc: st: add new driver for ST's LPC RTC
@ 2014-12-15 14:17     ` Guenter Roeck
  0 siblings, 0 replies; 69+ messages in thread
From: Guenter Roeck @ 2014-12-15 14:17 UTC (permalink / raw)
  To: linux-arm-kernel

On 12/15/2014 03:25 AM, Lee Jones wrote:
> ST's Low Power Controller (LPC) controls two devices; watchdog and RTC.
> Only one of the devices can be used at any one time.  This is enforced
> by the correlating MFD driver.  This portion of the driver-set controls
> the Real Time Clock.
>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>   drivers/rtc/Kconfig      |  13 ++
>   drivers/rtc/Makefile     |   1 +
>   drivers/rtc/rtc-st-lpc.c | 330 +++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 344 insertions(+)
>   create mode 100644 drivers/rtc/rtc-st-lpc.c
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index a168e96..aa4bd90 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -1355,6 +1355,18 @@ config RTC_DRV_SIRFSOC
>   	  Say "yes" here to support the real time clock on SiRF SOC chips.
>   	  This driver can also be built as a module called rtc-sirfsoc.
>
> +config RTC_DRV_ST_LPC
> +	tristate "STMicroelectronics LPC RTC"
> +	depends on ARCH_STI
> +	depends on OF
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based RTC support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called rtc-st-lpc.
> +
>   config RTC_DRV_MOXART
>   	tristate "MOXA ART RTC"
>   	depends on ARCH_MOXART || COMPILE_TEST
> @@ -1390,4 +1402,5 @@ config RTC_DRV_HID_SENSOR_TIME
>   	  rtc-hid-sensor-time.
>
>
> +

Hi Lee,

Yet another empty line ?

Guenter

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 5/8] mfd: Add ST's Low Power Controller driver
  2014-12-15 14:06         ` Arnd Bergmann
  (?)
  (?)
@ 2014-12-15 14:38           ` Lee Jones
  -1 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 14:38 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel, linux-kernel, kernel, a.zummo, rtc-linux, wim,
	linux-watchdog, devicetree

On Mon, 15 Dec 2014, Arnd Bergmann wrote:

> On Monday 15 December 2014 13:50:52 Lee Jones wrote:
> > > > 
> > > 
> > > I don't think it's necessary to have the MFD node if only one of the
> > > two modes can be used based on a DT property. It should be enough
> > > to have both the rtc and the wdt driver list the same compatible
> > > string and check the property in the probe function to decide if
> > > they want to drive the device or not:
> > 
> > I tried that and it didn't work.  Only one driver probed.
> > 
> > 
> 
> Strange, the code in really_probe() and the comment in device_attach()
> suggest that this is not the intentional behavior. What error
> code did you return? If it's -ENODEV or -ENXIO, it should keep
> trying the other drivers, otherwise it will give up.

Oh I see.  So if I return -ENODEV it will keep trying to bind with
other drivers.  I'll try that and report back.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 5/8] mfd: Add ST's Low Power Controller driver
@ 2014-12-15 14:38           ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 14:38 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kernel-F5mvAk5X5gdBDgjK7y7TUQ, a.zummo-BfzFCNDTiLLj+vYz1yj4TQ,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw, wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Mon, 15 Dec 2014, Arnd Bergmann wrote:

> On Monday 15 December 2014 13:50:52 Lee Jones wrote:
> > > > 
> > > 
> > > I don't think it's necessary to have the MFD node if only one of the
> > > two modes can be used based on a DT property. It should be enough
> > > to have both the rtc and the wdt driver list the same compatible
> > > string and check the property in the probe function to decide if
> > > they want to drive the device or not:
> > 
> > I tried that and it didn't work.  Only one driver probed.
> > 
> > 
> 
> Strange, the code in really_probe() and the comment in device_attach()
> suggest that this is not the intentional behavior. What error
> code did you return? If it's -ENODEV or -ENXIO, it should keep
> trying the other drivers, otherwise it will give up.

Oh I see.  So if I return -ENODEV it will keep trying to bind with
other drivers.  I'll try that and report back.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 5/8] mfd: Add ST's Low Power Controller driver
@ 2014-12-15 14:38           ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 14:38 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel, linux-kernel, kernel, a.zummo, rtc-linux, wim,
	linux-watchdog, devicetree

On Mon, 15 Dec 2014, Arnd Bergmann wrote:

> On Monday 15 December 2014 13:50:52 Lee Jones wrote:
> > > > 
> > > 
> > > I don't think it's necessary to have the MFD node if only one of the
> > > two modes can be used based on a DT property. It should be enough
> > > to have both the rtc and the wdt driver list the same compatible
> > > string and check the property in the probe function to decide if
> > > they want to drive the device or not:
> > 
> > I tried that and it didn't work.  Only one driver probed.
> > 
> > 
> 
> Strange, the code in really_probe() and the comment in device_attach()
> suggest that this is not the intentional behavior. What error
> code did you return? If it's -ENODEV or -ENXIO, it should keep
> trying the other drivers, otherwise it will give up.

Oh I see.  So if I return -ENODEV it will keep trying to bind with
other drivers.  I'll try that and report back.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 5/8] mfd: Add ST's Low Power Controller driver
@ 2014-12-15 14:38           ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-15 14:38 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, 15 Dec 2014, Arnd Bergmann wrote:

> On Monday 15 December 2014 13:50:52 Lee Jones wrote:
> > > > 
> > > 
> > > I don't think it's necessary to have the MFD node if only one of the
> > > two modes can be used based on a DT property. It should be enough
> > > to have both the rtc and the wdt driver list the same compatible
> > > string and check the property in the probe function to decide if
> > > they want to drive the device or not:
> > 
> > I tried that and it didn't work.  Only one driver probed.
> > 
> > 
> 
> Strange, the code in really_probe() and the comment in device_attach()
> suggest that this is not the intentional behavior. What error
> code did you return? If it's -ENODEV or -ENXIO, it should keep
> trying the other drivers, otherwise it will give up.

Oh I see.  So if I return -ENODEV it will keep trying to bind with
other drivers.  I'll try that and report back.

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
  2014-12-15 11:25   ` Lee Jones
@ 2014-12-15 16:23     ` Guenter Roeck
  -1 siblings, 0 replies; 69+ messages in thread
From: Guenter Roeck @ 2014-12-15 16:23 UTC (permalink / raw)
  To: Lee Jones
  Cc: linux-arm-kernel, linux-kernel, kernel, a.zummo, rtc-linux, wim,
	linux-watchdog, devicetree, David Paris

On Mon, Dec 15, 2014 at 11:25:36AM +0000, Lee Jones wrote:
> Signed-off-by: David Paris <david.paris@st.com>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>  drivers/watchdog/Kconfig  |  13 ++
>  drivers/watchdog/Makefile |   1 +
>  drivers/watchdog/st_wdt.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 326 insertions(+)
>  create mode 100644 drivers/watchdog/st_wdt.c
> 
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index f57312f..5a538af 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
>  	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
>  	  the watchdog triggers the system will be reset.
>  
> +config ST_WATCHDOG
> +	tristate "STMicroelectronics LPC Watchdog"
> +	depends on ARCH_STI
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based Watchdog timer support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_wdt.
> +
>  config TEGRA_WATCHDOG
>  	tristate "Tegra watchdog"
>  	depends on ARCH_TEGRA || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 468c320..eb19937 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
>  obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
>  obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
>  obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
> +obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
>  obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
>  obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
>  
> diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
> new file mode 100644
> index 0000000..7e06f15
> --- /dev/null
> +++ b/drivers/watchdog/st_wdt.c
> @@ -0,0 +1,312 @@
> +/*
> + * st-wdt.c - ST's LPC Watchdog
> + *
> + * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
> + *
> + * Author: David Paris <david.paris@st.com> for STMicroelectronics
> + *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/watchdog.h>
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF			0x410
> +#define LPC_LPA_START_OFF		0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF			0x510
> +
> +struct st_wdog_syscfg {
> +	struct regmap *regmap;
> +	unsigned int reset_type_reg;
> +	unsigned int reset_type_mask;
> +	unsigned int enable_reg;
> +	unsigned int enable_mask;
> +};
> +
> +struct st_wdog {
> +	void __iomem *base;
> +	struct device *dev;
> +	struct st_wdog_syscfg *syscfg;
> +	struct clk *clk;
> +	int warm_reset;
> +};
> +
> +static struct st_wdog_syscfg stid127_syscfg = {
> +	.reset_type_reg		= 0x004,
> +	.reset_type_mask	= BIT(2),
> +	.enable_reg		= 0x000,
> +	.enable_mask		= BIT(2),
> +};
> +
> +static struct st_wdog_syscfg stih415_syscfg = {
> +	.reset_type_reg		= 0x0B8,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x0B4,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih416_syscfg = {
> +	.reset_type_reg		= 0x88C,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x888,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih407_syscfg = {
> +	.enable_reg		= 0x204,
> +	.enable_mask		= BIT(19),
> +};
> +
> +static struct of_device_id st_wdog_match[] = {
> +	{
> +		.compatible = "st,stih407-lpc",
> +		.data = (void *)&stih407_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih416-lpc",
> +		.data = (void *)&stih416_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih415-lpc",
> +		.data = (void *)&stih415_syscfg,
> +	},
> +	{
> +		.compatible = "st,stid127-lpc",
> +		.data = (void *)&stid127_syscfg,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, st_wdog_match);
> +
> +static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
> +{
> +	/* Type of watchdog reset - 0: Cold 1: Warm */
> +	if (st_wdog->syscfg->reset_type_reg)
> +		regmap_update_bits(st_wdog->syscfg->regmap,
> +				   st_wdog->syscfg->reset_type_reg,
> +				   st_wdog->syscfg->reset_type_mask,
> +				   st_wdog->warm_reset);
> +
> +	/* Mask/unmask watchdog reset */
> +	regmap_update_bits(st_wdog->syscfg->regmap,
> +			   st_wdog->syscfg->enable_reg,
> +			   st_wdog->syscfg->enable_mask,
> +			   enable ? 0 : st_wdog->syscfg->enable_mask);
> +}
> +
> +static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
> +{
> +	unsigned long clkrate = clk_get_rate(st_wdog->clk);
> +	unsigned int maxtimeout = 0xFFFFFFFF / clkrate;
> +

Wondering ... is this re-calculation for each keepalive really necessary ?
If yes (ie if the clock rate can change) that may be problematic, since
a clock rate change might happen right after a keepalive, and the watchdog
might time out if the clock rate increased. Not really sure if / how that
problem could be solved unless it is possible to register a callback
associated with clocks.

If the clock rate does not change, it might be better to retrieve it
once in probe, calculate maxtimeout there, and store it in the
watchdog_device structure. Similar, clkrate can be stored in
struct st_wdog (or just store timeout * clkrate so it doesn't have
to be recalculated unnecessarily).

Also, clk_get_rate can return 0, at least on some architectures/platforms.
It might make sense to check its return value in the probe function.

Thanks,
Guenter

> +	if (timeout > maxtimeout) {
> +		dev_warn(st_wdog->dev,
> +			 "timer overrun detected at current freq (%luHz)\n"
> +			 "wdog timeout set for %ds instead of requested %uis",
> +			 clkrate, maxtimeout, timeout);
> +		timeout = maxtimeout;
> +	}
> +	timeout *= clkrate;
> +
> +	writel_relaxed(timeout, st_wdog->base + LPC_LPA_LSB_OFF);
> +	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
> +}
> +
> +static int st_wdog_start(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_stop(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_set_timeout(struct watchdog_device *wdd,
> +			       unsigned int timeout)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	wdd->timeout = timeout;
> +	st_wdog_load_timer(st_wdog, timeout);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_keepalive(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	st_wdog_load_timer(st_wdog, wdd->timeout);
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info st_wdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "ST LPC WDT",
> +};
> +
> +static const struct watchdog_ops st_wdog_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= st_wdog_start,
> +	.stop		= st_wdog_stop,
> +	.ping		= st_wdog_keepalive,
> +	.set_timeout	= st_wdog_set_timeout,
> +};
> +
> +static struct watchdog_device st_wdog_dev = {
> +	.info		= &st_wdog_info,
> +	.ops		= &st_wdog_ops,
> +};
> +
> +static int st_wdog_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np;
> +	struct st_wdog *st_wdog;
> +	struct regmap *regmap;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *base;
> +	int ret;
> +
> +	/* This is a single node MFD device. */
> +	np = pdev->dev.of_node = pdev->dev.parent->of_node;
> +
> +	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
> +	if (!st_wdog)
> +		return -ENOMEM;
> +
> +	match = of_match_device(st_wdog_match, &pdev->dev);
> +	if (!match) {
> +		dev_err(&pdev->dev, "Couldn't match device\n");
> +		return -ENODEV;
> +	}
> +	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(base)) {
> +		dev_err(&pdev->dev, "Failed to ioremap base\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(regmap)) {
> +		dev_err(&pdev->dev, "No syscfg phandle specified\n");
> +		return PTR_ERR(regmap);
> +	}
> +
> +	clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(clk);
> +	}
> +	clk_prepare_enable(st_wdog->clk);
> +
> +	st_wdog->dev		= &pdev->dev;
> +	st_wdog->base		= base;
> +	st_wdog->clk		= clk;
> +	st_wdog->syscfg->regmap = regmap;
> +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> +
> +	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
> +	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
> +
> +	/* Init Watchdog timeout with value in DT */
> +	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
> +		return ret;
> +	}
> +
> +	ret = watchdog_register_device(&st_wdog_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register watchdog\n");
> +		clk_disable_unprepare(clk);
> +		return ret;
> +	}
> +
> +	st_wdog_setup(st_wdog, true);
> +
> +	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
> +		 st_wdog->warm_reset ? "warm" : "cold");
> +
> +	return ret;
> +}
> +
> +static int st_wdog_remove(struct platform_device *pdev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	st_wdog_setup(st_wdog, false);
> +	watchdog_unregister_device(&st_wdog_dev);
> +	clk_disable_unprepare(st_wdog->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int st_wdog_suspend(struct device *dev)
> +{
> +	if (watchdog_active(&st_wdog_dev))
> +		st_wdog_stop(&st_wdog_dev);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_resume(struct device *dev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	if (watchdog_active(&st_wdog_dev)) {
> +		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
> +		st_wdog_start(&st_wdog_dev);
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
> +			 st_wdog_suspend,
> +			 st_wdog_resume);
> +
> +static struct platform_driver st_wdog_driver = {
> +	.driver	= {
> +		.name = "st-lpc-wdt",
> +		.pm = &st_wdog_pm_ops,
> +	},
> +	.probe = st_wdog_probe,
> +	.remove = st_wdog_remove,
> +};
> +module_platform_driver(st_wdog_driver);
> +
> +MODULE_AUTHOR("David Paris <david.paris@st.com>");
> +MODULE_DESCRIPTION("ST LPC Watchdog Driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 1.9.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-15 16:23     ` Guenter Roeck
  0 siblings, 0 replies; 69+ messages in thread
From: Guenter Roeck @ 2014-12-15 16:23 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Dec 15, 2014 at 11:25:36AM +0000, Lee Jones wrote:
> Signed-off-by: David Paris <david.paris@st.com>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>
> ---
>  drivers/watchdog/Kconfig  |  13 ++
>  drivers/watchdog/Makefile |   1 +
>  drivers/watchdog/st_wdt.c | 312 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 326 insertions(+)
>  create mode 100644 drivers/watchdog/st_wdt.c
> 
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index f57312f..5a538af 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
>  	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
>  	  the watchdog triggers the system will be reset.
>  
> +config ST_WATCHDOG
> +	tristate "STMicroelectronics LPC Watchdog"
> +	depends on ARCH_STI
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based Watchdog timer support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_wdt.
> +
>  config TEGRA_WATCHDOG
>  	tristate "Tegra watchdog"
>  	depends on ARCH_TEGRA || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 468c320..eb19937 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
>  obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
>  obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
>  obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
> +obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
>  obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
>  obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
>  
> diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
> new file mode 100644
> index 0000000..7e06f15
> --- /dev/null
> +++ b/drivers/watchdog/st_wdt.c
> @@ -0,0 +1,312 @@
> +/*
> + * st-wdt.c - ST's LPC Watchdog
> + *
> + * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
> + *
> + * Author: David Paris <david.paris@st.com> for STMicroelectronics
> + *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/watchdog.h>
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF			0x410
> +#define LPC_LPA_START_OFF		0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF			0x510
> +
> +struct st_wdog_syscfg {
> +	struct regmap *regmap;
> +	unsigned int reset_type_reg;
> +	unsigned int reset_type_mask;
> +	unsigned int enable_reg;
> +	unsigned int enable_mask;
> +};
> +
> +struct st_wdog {
> +	void __iomem *base;
> +	struct device *dev;
> +	struct st_wdog_syscfg *syscfg;
> +	struct clk *clk;
> +	int warm_reset;
> +};
> +
> +static struct st_wdog_syscfg stid127_syscfg = {
> +	.reset_type_reg		= 0x004,
> +	.reset_type_mask	= BIT(2),
> +	.enable_reg		= 0x000,
> +	.enable_mask		= BIT(2),
> +};
> +
> +static struct st_wdog_syscfg stih415_syscfg = {
> +	.reset_type_reg		= 0x0B8,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x0B4,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih416_syscfg = {
> +	.reset_type_reg		= 0x88C,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x888,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih407_syscfg = {
> +	.enable_reg		= 0x204,
> +	.enable_mask		= BIT(19),
> +};
> +
> +static struct of_device_id st_wdog_match[] = {
> +	{
> +		.compatible = "st,stih407-lpc",
> +		.data = (void *)&stih407_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih416-lpc",
> +		.data = (void *)&stih416_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih415-lpc",
> +		.data = (void *)&stih415_syscfg,
> +	},
> +	{
> +		.compatible = "st,stid127-lpc",
> +		.data = (void *)&stid127_syscfg,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, st_wdog_match);
> +
> +static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
> +{
> +	/* Type of watchdog reset - 0: Cold 1: Warm */
> +	if (st_wdog->syscfg->reset_type_reg)
> +		regmap_update_bits(st_wdog->syscfg->regmap,
> +				   st_wdog->syscfg->reset_type_reg,
> +				   st_wdog->syscfg->reset_type_mask,
> +				   st_wdog->warm_reset);
> +
> +	/* Mask/unmask watchdog reset */
> +	regmap_update_bits(st_wdog->syscfg->regmap,
> +			   st_wdog->syscfg->enable_reg,
> +			   st_wdog->syscfg->enable_mask,
> +			   enable ? 0 : st_wdog->syscfg->enable_mask);
> +}
> +
> +static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
> +{
> +	unsigned long clkrate = clk_get_rate(st_wdog->clk);
> +	unsigned int maxtimeout = 0xFFFFFFFF / clkrate;
> +

Wondering ... is this re-calculation for each keepalive really necessary ?
If yes (ie if the clock rate can change) that may be problematic, since
a clock rate change might happen right after a keepalive, and the watchdog
might time out if the clock rate increased. Not really sure if / how that
problem could be solved unless it is possible to register a callback
associated with clocks.

If the clock rate does not change, it might be better to retrieve it
once in probe, calculate maxtimeout there, and store it in the
watchdog_device structure. Similar, clkrate can be stored in
struct st_wdog (or just store timeout * clkrate so it doesn't have
to be recalculated unnecessarily).

Also, clk_get_rate can return 0, at least on some architectures/platforms.
It might make sense to check its return value in the probe function.

Thanks,
Guenter

> +	if (timeout > maxtimeout) {
> +		dev_warn(st_wdog->dev,
> +			 "timer overrun detected at current freq (%luHz)\n"
> +			 "wdog timeout set for %ds instead of requested %uis",
> +			 clkrate, maxtimeout, timeout);
> +		timeout = maxtimeout;
> +	}
> +	timeout *= clkrate;
> +
> +	writel_relaxed(timeout, st_wdog->base + LPC_LPA_LSB_OFF);
> +	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
> +}
> +
> +static int st_wdog_start(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_stop(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_set_timeout(struct watchdog_device *wdd,
> +			       unsigned int timeout)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	wdd->timeout = timeout;
> +	st_wdog_load_timer(st_wdog, timeout);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_keepalive(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	st_wdog_load_timer(st_wdog, wdd->timeout);
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info st_wdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "ST LPC WDT",
> +};
> +
> +static const struct watchdog_ops st_wdog_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= st_wdog_start,
> +	.stop		= st_wdog_stop,
> +	.ping		= st_wdog_keepalive,
> +	.set_timeout	= st_wdog_set_timeout,
> +};
> +
> +static struct watchdog_device st_wdog_dev = {
> +	.info		= &st_wdog_info,
> +	.ops		= &st_wdog_ops,
> +};
> +
> +static int st_wdog_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np;
> +	struct st_wdog *st_wdog;
> +	struct regmap *regmap;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *base;
> +	int ret;
> +
> +	/* This is a single node MFD device. */
> +	np = pdev->dev.of_node = pdev->dev.parent->of_node;
> +
> +	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
> +	if (!st_wdog)
> +		return -ENOMEM;
> +
> +	match = of_match_device(st_wdog_match, &pdev->dev);
> +	if (!match) {
> +		dev_err(&pdev->dev, "Couldn't match device\n");
> +		return -ENODEV;
> +	}
> +	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(base)) {
> +		dev_err(&pdev->dev, "Failed to ioremap base\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(regmap)) {
> +		dev_err(&pdev->dev, "No syscfg phandle specified\n");
> +		return PTR_ERR(regmap);
> +	}
> +
> +	clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(clk);
> +	}
> +	clk_prepare_enable(st_wdog->clk);
> +
> +	st_wdog->dev		= &pdev->dev;
> +	st_wdog->base		= base;
> +	st_wdog->clk		= clk;
> +	st_wdog->syscfg->regmap = regmap;
> +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> +
> +	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
> +	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
> +
> +	/* Init Watchdog timeout with value in DT */
> +	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
> +		return ret;
> +	}
> +
> +	ret = watchdog_register_device(&st_wdog_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register watchdog\n");
> +		clk_disable_unprepare(clk);
> +		return ret;
> +	}
> +
> +	st_wdog_setup(st_wdog, true);
> +
> +	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
> +		 st_wdog->warm_reset ? "warm" : "cold");
> +
> +	return ret;
> +}
> +
> +static int st_wdog_remove(struct platform_device *pdev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	st_wdog_setup(st_wdog, false);
> +	watchdog_unregister_device(&st_wdog_dev);
> +	clk_disable_unprepare(st_wdog->clk);
> +
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int st_wdog_suspend(struct device *dev)
> +{
> +	if (watchdog_active(&st_wdog_dev))
> +		st_wdog_stop(&st_wdog_dev);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_resume(struct device *dev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	if (watchdog_active(&st_wdog_dev)) {
> +		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
> +		st_wdog_start(&st_wdog_dev);
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
> +			 st_wdog_suspend,
> +			 st_wdog_resume);
> +
> +static struct platform_driver st_wdog_driver = {
> +	.driver	= {
> +		.name = "st-lpc-wdt",
> +		.pm = &st_wdog_pm_ops,
> +	},
> +	.probe = st_wdog_probe,
> +	.remove = st_wdog_remove,
> +};
> +module_platform_driver(st_wdog_driver);
> +
> +MODULE_AUTHOR("David Paris <david.paris@st.com>");
> +MODULE_DESCRIPTION("ST LPC Watchdog Driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 1.9.1
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-18 17:20           ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-18 17:20 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-arm-kernel, linux-kernel, kernel, a.zummo, rtc-linux, wim,
	linux-watchdog, devicetree, arnd, David Paris

On Thu, 18 Dec 2014, Guenter Roeck wrote:

> On Thu, Dec 18, 2014 at 08:26:52AM +0000, Lee Jones wrote:
> > On Wed, 17 Dec 2014, Guenter Roeck wrote:
> > > On Wed, Dec 17, 2014 at 04:45:25PM +0000, Lee Jones wrote:
> > > > Signed-off-by: David Paris <david.paris@st.com>
> > > > Signed-off-by: Lee Jones <lee.jones@linaro.org>
> > > 
> > > Hi Lee and David,
> > > 
> > > I still have a couple of comments below. Sorry I didn't catch those earlier.
> > > 
> > > Thanks,
> > > Guenter
> > > 
> > > > ---
> > > >  drivers/watchdog/Kconfig  |  13 ++
> > > >  drivers/watchdog/Makefile |   1 +
> > > >  drivers/watchdog/st_wdt.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++
> > > >  3 files changed, 337 insertions(+)
> > > >  create mode 100644 drivers/watchdog/st_wdt.c
> > 
> > Chopping all the crud.
> > 
> > [...]
> > 
> > > > +static struct of_device_id st_wdog_match[] = {
> > > > +	{
> > > > +		.compatible = "st,stih407-lpc",
> > > > +		.data = (void *)&stih407_syscfg,
> > > 
> > > Nitpick: typecast to and from void * is not necessary.
> > 
> > Actually that's not true, but it is superfluous in this case.
> > 
> Point taken. Would you agree to a more detailed "typecasting a pointer
> to and from void * is not necessary" ?

Sure, why not? ;)

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-18 17:20           ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-18 17:20 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kernel-F5mvAk5X5gdBDgjK7y7TUQ, a.zummo-BfzFCNDTiLLj+vYz1yj4TQ,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw, wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, arnd-r2nGTMty4D4, David Paris

On Thu, 18 Dec 2014, Guenter Roeck wrote:

> On Thu, Dec 18, 2014 at 08:26:52AM +0000, Lee Jones wrote:
> > On Wed, 17 Dec 2014, Guenter Roeck wrote:
> > > On Wed, Dec 17, 2014 at 04:45:25PM +0000, Lee Jones wrote:
> > > > Signed-off-by: David Paris <david.paris-qxv4g6HH51o@public.gmane.org>
> > > > Signed-off-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> > > 
> > > Hi Lee and David,
> > > 
> > > I still have a couple of comments below. Sorry I didn't catch those earlier.
> > > 
> > > Thanks,
> > > Guenter
> > > 
> > > > ---
> > > >  drivers/watchdog/Kconfig  |  13 ++
> > > >  drivers/watchdog/Makefile |   1 +
> > > >  drivers/watchdog/st_wdt.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++
> > > >  3 files changed, 337 insertions(+)
> > > >  create mode 100644 drivers/watchdog/st_wdt.c
> > 
> > Chopping all the crud.
> > 
> > [...]
> > 
> > > > +static struct of_device_id st_wdog_match[] = {
> > > > +	{
> > > > +		.compatible = "st,stih407-lpc",
> > > > +		.data = (void *)&stih407_syscfg,
> > > 
> > > Nitpick: typecast to and from void * is not necessary.
> > 
> > Actually that's not true, but it is superfluous in this case.
> > 
> Point taken. Would you agree to a more detailed "typecasting a pointer
> to and from void * is not necessary" ?

Sure, why not? ;)

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-18 17:20           ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-18 17:20 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-arm-kernel, linux-kernel, kernel, a.zummo, rtc-linux, wim,
	linux-watchdog, devicetree, arnd, David Paris

On Thu, 18 Dec 2014, Guenter Roeck wrote:

> On Thu, Dec 18, 2014 at 08:26:52AM +0000, Lee Jones wrote:
> > On Wed, 17 Dec 2014, Guenter Roeck wrote:
> > > On Wed, Dec 17, 2014 at 04:45:25PM +0000, Lee Jones wrote:
> > > > Signed-off-by: David Paris <david.paris@st.com>
> > > > Signed-off-by: Lee Jones <lee.jones@linaro.org>
> > > 
> > > Hi Lee and David,
> > > 
> > > I still have a couple of comments below. Sorry I didn't catch those earlier.
> > > 
> > > Thanks,
> > > Guenter
> > > 
> > > > ---
> > > >  drivers/watchdog/Kconfig  |  13 ++
> > > >  drivers/watchdog/Makefile |   1 +
> > > >  drivers/watchdog/st_wdt.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++
> > > >  3 files changed, 337 insertions(+)
> > > >  create mode 100644 drivers/watchdog/st_wdt.c
> > 
> > Chopping all the crud.
> > 
> > [...]
> > 
> > > > +static struct of_device_id st_wdog_match[] = {
> > > > +	{
> > > > +		.compatible = "st,stih407-lpc",
> > > > +		.data = (void *)&stih407_syscfg,
> > > 
> > > Nitpick: typecast to and from void * is not necessary.
> > 
> > Actually that's not true, but it is superfluous in this case.
> > 
> Point taken. Would you agree to a more detailed "typecasting a pointer
> to and from void * is not necessary" ?

Sure, why not? ;)

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-18 17:20           ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-18 17:20 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 18 Dec 2014, Guenter Roeck wrote:

> On Thu, Dec 18, 2014 at 08:26:52AM +0000, Lee Jones wrote:
> > On Wed, 17 Dec 2014, Guenter Roeck wrote:
> > > On Wed, Dec 17, 2014 at 04:45:25PM +0000, Lee Jones wrote:
> > > > Signed-off-by: David Paris <david.paris@st.com>
> > > > Signed-off-by: Lee Jones <lee.jones@linaro.org>
> > > 
> > > Hi Lee and David,
> > > 
> > > I still have a couple of comments below. Sorry I didn't catch those earlier.
> > > 
> > > Thanks,
> > > Guenter
> > > 
> > > > ---
> > > >  drivers/watchdog/Kconfig  |  13 ++
> > > >  drivers/watchdog/Makefile |   1 +
> > > >  drivers/watchdog/st_wdt.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++
> > > >  3 files changed, 337 insertions(+)
> > > >  create mode 100644 drivers/watchdog/st_wdt.c
> > 
> > Chopping all the crud.
> > 
> > [...]
> > 
> > > > +static struct of_device_id st_wdog_match[] = {
> > > > +	{
> > > > +		.compatible = "st,stih407-lpc",
> > > > +		.data = (void *)&stih407_syscfg,
> > > 
> > > Nitpick: typecast to and from void * is not necessary.
> > 
> > Actually that's not true, but it is superfluous in this case.
> > 
> Point taken. Would you agree to a more detailed "typecasting a pointer
> to and from void * is not necessary" ?

Sure, why not? ;)

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
  2014-12-18  8:26       ` Lee Jones
@ 2014-12-18 16:00         ` Guenter Roeck
  -1 siblings, 0 replies; 69+ messages in thread
From: Guenter Roeck @ 2014-12-18 16:00 UTC (permalink / raw)
  To: Lee Jones
  Cc: linux-arm-kernel, linux-kernel, kernel, a.zummo, rtc-linux, wim,
	linux-watchdog, devicetree, arnd, David Paris

On Thu, Dec 18, 2014 at 08:26:52AM +0000, Lee Jones wrote:
> On Wed, 17 Dec 2014, Guenter Roeck wrote:
> > On Wed, Dec 17, 2014 at 04:45:25PM +0000, Lee Jones wrote:
> > > Signed-off-by: David Paris <david.paris@st.com>
> > > Signed-off-by: Lee Jones <lee.jones@linaro.org>
> > 
> > Hi Lee and David,
> > 
> > I still have a couple of comments below. Sorry I didn't catch those earlier.
> > 
> > Thanks,
> > Guenter
> > 
> > > ---
> > >  drivers/watchdog/Kconfig  |  13 ++
> > >  drivers/watchdog/Makefile |   1 +
> > >  drivers/watchdog/st_wdt.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++
> > >  3 files changed, 337 insertions(+)
> > >  create mode 100644 drivers/watchdog/st_wdt.c
> 
> Chopping all the crud.
> 
> [...]
> 
> > > +static struct of_device_id st_wdog_match[] = {
> > > +	{
> > > +		.compatible = "st,stih407-lpc",
> > > +		.data = (void *)&stih407_syscfg,
> > 
> > Nitpick: typecast to and from void * is not necessary.
> 
> Actually that's not true, but it is superfluous in this case.
> 
Point taken. Would you agree to a more detailed "typecasting a pointer
to and from void * is not necessary" ?

Guenter

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-18 16:00         ` Guenter Roeck
  0 siblings, 0 replies; 69+ messages in thread
From: Guenter Roeck @ 2014-12-18 16:00 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Dec 18, 2014 at 08:26:52AM +0000, Lee Jones wrote:
> On Wed, 17 Dec 2014, Guenter Roeck wrote:
> > On Wed, Dec 17, 2014 at 04:45:25PM +0000, Lee Jones wrote:
> > > Signed-off-by: David Paris <david.paris@st.com>
> > > Signed-off-by: Lee Jones <lee.jones@linaro.org>
> > 
> > Hi Lee and David,
> > 
> > I still have a couple of comments below. Sorry I didn't catch those earlier.
> > 
> > Thanks,
> > Guenter
> > 
> > > ---
> > >  drivers/watchdog/Kconfig  |  13 ++
> > >  drivers/watchdog/Makefile |   1 +
> > >  drivers/watchdog/st_wdt.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++
> > >  3 files changed, 337 insertions(+)
> > >  create mode 100644 drivers/watchdog/st_wdt.c
> 
> Chopping all the crud.
> 
> [...]
> 
> > > +static struct of_device_id st_wdog_match[] = {
> > > +	{
> > > +		.compatible = "st,stih407-lpc",
> > > +		.data = (void *)&stih407_syscfg,
> > 
> > Nitpick: typecast to and from void * is not necessary.
> 
> Actually that's not true, but it is superfluous in this case.
> 
Point taken. Would you agree to a more detailed "typecasting a pointer
to and from void * is not necessary" ?

Guenter

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-18  8:26       ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-18  8:26 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-arm-kernel, linux-kernel, kernel, a.zummo, rtc-linux, wim,
	linux-watchdog, devicetree, arnd, David Paris

On Wed, 17 Dec 2014, Guenter Roeck wrote:
> On Wed, Dec 17, 2014 at 04:45:25PM +0000, Lee Jones wrote:
> > Signed-off-by: David Paris <david.paris@st.com>
> > Signed-off-by: Lee Jones <lee.jones@linaro.org>
> 
> Hi Lee and David,
> 
> I still have a couple of comments below. Sorry I didn't catch those earlier.
> 
> Thanks,
> Guenter
> 
> > ---
> >  drivers/watchdog/Kconfig  |  13 ++
> >  drivers/watchdog/Makefile |   1 +
> >  drivers/watchdog/st_wdt.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 337 insertions(+)
> >  create mode 100644 drivers/watchdog/st_wdt.c

Chopping all the crud.

[...]

> > +static struct of_device_id st_wdog_match[] = {
> > +	{
> > +		.compatible = "st,stih407-lpc",
> > +		.data = (void *)&stih407_syscfg,
> 
> Nitpick: typecast to and from void * is not necessary.

Actually that's not true, but it is superfluous in this case.

I will fix-up.

[...]

> > +static int st_wdog_probe(struct platform_device *pdev)
> > +{
> > +	const struct of_device_id *match;
> > +	struct device_node *np = pdev->dev.of_node;
> > +	struct st_wdog *st_wdog;
> > +	struct regmap *regmap;
> > +	struct resource *res;
> > +	struct clk *clk;
> > +	void __iomem *base;
> > +	uint32_t mode;
> > +	int ret;

[...]

> > +	clk = clk_get(&pdev->dev, NULL);
> > +	if (IS_ERR(clk)) {
> > +		dev_err(&pdev->dev, "Unable to request clock\n");
> > +		return PTR_ERR(clk);
> > +	}
> > +	clk_prepare_enable(st_wdog->clk);
> 
> How does this work ? st_wdog->clk isn't set yet.

Great spot.  That should be 'clk'.

It works because the LPC clk is already on.

> > +
> > +	st_wdog->dev		= &pdev->dev;
> > +	st_wdog->base		= base;
> > +	st_wdog->clk		= clk;
> > +	st_wdog->syscfg->regmap = regmap;
> > +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> > +	st_wdog->clkrate	= clk_get_rate(st_wdog->clk);
> > +
> > +	if (!st_wdog->clkrate) {
> > +		dev_err(&pdev->dev, "Unable to fetch clock rate\n");
> 
> I think this is missing
> 	clk_disable_unprepare();
> 	clk_put();
> 
> Same for the rest of the error path handling below.

Actually instead of clk_put we should be using s/clk_get/devm_clk_get/.

You're right about the error paths.  I will enforce a better one.

Thanks.

[...]

> > +#ifdef CONFIG_PM
> 
> I think this needs to be CONFIG_PM_SLEEP.
> Another option might be to drop the #ifdef entirely and use
> __maybe_unused instead.

Hmmm... what version is this?

[2 mins pass]

Oh rubbish.  I've been a silly boy!

[spoiler alert: take a look at the end of the RTC driver patch]

Will be fixed in v3.

[...]

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-18  8:26       ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-18  8:26 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	kernel-F5mvAk5X5gdBDgjK7y7TUQ, a.zummo-BfzFCNDTiLLj+vYz1yj4TQ,
	rtc-linux-/JYPxA39Uh5TLH3MbocFFw, wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, arnd-r2nGTMty4D4, David Paris

On Wed, 17 Dec 2014, Guenter Roeck wrote:
> On Wed, Dec 17, 2014 at 04:45:25PM +0000, Lee Jones wrote:
> > Signed-off-by: David Paris <david.paris-qxv4g6HH51o@public.gmane.org>
> > Signed-off-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> 
> Hi Lee and David,
> 
> I still have a couple of comments below. Sorry I didn't catch those earlier.
> 
> Thanks,
> Guenter
> 
> > ---
> >  drivers/watchdog/Kconfig  |  13 ++
> >  drivers/watchdog/Makefile |   1 +
> >  drivers/watchdog/st_wdt.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 337 insertions(+)
> >  create mode 100644 drivers/watchdog/st_wdt.c

Chopping all the crud.

[...]

> > +static struct of_device_id st_wdog_match[] = {
> > +	{
> > +		.compatible = "st,stih407-lpc",
> > +		.data = (void *)&stih407_syscfg,
> 
> Nitpick: typecast to and from void * is not necessary.

Actually that's not true, but it is superfluous in this case.

I will fix-up.

[...]

> > +static int st_wdog_probe(struct platform_device *pdev)
> > +{
> > +	const struct of_device_id *match;
> > +	struct device_node *np = pdev->dev.of_node;
> > +	struct st_wdog *st_wdog;
> > +	struct regmap *regmap;
> > +	struct resource *res;
> > +	struct clk *clk;
> > +	void __iomem *base;
> > +	uint32_t mode;
> > +	int ret;

[...]

> > +	clk = clk_get(&pdev->dev, NULL);
> > +	if (IS_ERR(clk)) {
> > +		dev_err(&pdev->dev, "Unable to request clock\n");
> > +		return PTR_ERR(clk);
> > +	}
> > +	clk_prepare_enable(st_wdog->clk);
> 
> How does this work ? st_wdog->clk isn't set yet.

Great spot.  That should be 'clk'.

It works because the LPC clk is already on.

> > +
> > +	st_wdog->dev		= &pdev->dev;
> > +	st_wdog->base		= base;
> > +	st_wdog->clk		= clk;
> > +	st_wdog->syscfg->regmap = regmap;
> > +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> > +	st_wdog->clkrate	= clk_get_rate(st_wdog->clk);
> > +
> > +	if (!st_wdog->clkrate) {
> > +		dev_err(&pdev->dev, "Unable to fetch clock rate\n");
> 
> I think this is missing
> 	clk_disable_unprepare();
> 	clk_put();
> 
> Same for the rest of the error path handling below.

Actually instead of clk_put we should be using s/clk_get/devm_clk_get/.

You're right about the error paths.  I will enforce a better one.

Thanks.

[...]

> > +#ifdef CONFIG_PM
> 
> I think this needs to be CONFIG_PM_SLEEP.
> Another option might be to drop the #ifdef entirely and use
> __maybe_unused instead.

Hmmm... what version is this?

[2 mins pass]

Oh rubbish.  I've been a silly boy!

[spoiler alert: take a look at the end of the RTC driver patch]

Will be fixed in v3.

[...]

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-18  8:26       ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-18  8:26 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, 17 Dec 2014, Guenter Roeck wrote:
> On Wed, Dec 17, 2014 at 04:45:25PM +0000, Lee Jones wrote:
> > Signed-off-by: David Paris <david.paris@st.com>
> > Signed-off-by: Lee Jones <lee.jones@linaro.org>
> 
> Hi Lee and David,
> 
> I still have a couple of comments below. Sorry I didn't catch those earlier.
> 
> Thanks,
> Guenter
> 
> > ---
> >  drivers/watchdog/Kconfig  |  13 ++
> >  drivers/watchdog/Makefile |   1 +
> >  drivers/watchdog/st_wdt.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 337 insertions(+)
> >  create mode 100644 drivers/watchdog/st_wdt.c

Chopping all the crud.

[...]

> > +static struct of_device_id st_wdog_match[] = {
> > +	{
> > +		.compatible = "st,stih407-lpc",
> > +		.data = (void *)&stih407_syscfg,
> 
> Nitpick: typecast to and from void * is not necessary.

Actually that's not true, but it is superfluous in this case.

I will fix-up.

[...]

> > +static int st_wdog_probe(struct platform_device *pdev)
> > +{
> > +	const struct of_device_id *match;
> > +	struct device_node *np = pdev->dev.of_node;
> > +	struct st_wdog *st_wdog;
> > +	struct regmap *regmap;
> > +	struct resource *res;
> > +	struct clk *clk;
> > +	void __iomem *base;
> > +	uint32_t mode;
> > +	int ret;

[...]

> > +	clk = clk_get(&pdev->dev, NULL);
> > +	if (IS_ERR(clk)) {
> > +		dev_err(&pdev->dev, "Unable to request clock\n");
> > +		return PTR_ERR(clk);
> > +	}
> > +	clk_prepare_enable(st_wdog->clk);
> 
> How does this work ? st_wdog->clk isn't set yet.

Great spot.  That should be 'clk'.

It works because the LPC clk is already on.

> > +
> > +	st_wdog->dev		= &pdev->dev;
> > +	st_wdog->base		= base;
> > +	st_wdog->clk		= clk;
> > +	st_wdog->syscfg->regmap = regmap;
> > +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> > +	st_wdog->clkrate	= clk_get_rate(st_wdog->clk);
> > +
> > +	if (!st_wdog->clkrate) {
> > +		dev_err(&pdev->dev, "Unable to fetch clock rate\n");
> 
> I think this is missing
> 	clk_disable_unprepare();
> 	clk_put();
> 
> Same for the rest of the error path handling below.

Actually instead of clk_put we should be using s/clk_get/devm_clk_get/.

You're right about the error paths.  I will enforce a better one.

Thanks.

[...]

> > +#ifdef CONFIG_PM
> 
> I think this needs to be CONFIG_PM_SLEEP.
> Another option might be to drop the #ifdef entirely and use
> __maybe_unused instead.

Hmmm... what version is this?

[2 mins pass]

Oh rubbish.  I've been a silly boy!

[spoiler alert: take a look at the end of the RTC driver patch]

Will be fixed in v3.

[...]

-- 
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org ? Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

^ permalink raw reply	[flat|nested] 69+ messages in thread

* Re: [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
  2014-12-17 16:45   ` Lee Jones
@ 2014-12-17 18:02     ` Guenter Roeck
  -1 siblings, 0 replies; 69+ messages in thread
From: Guenter Roeck @ 2014-12-17 18:02 UTC (permalink / raw)
  To: Lee Jones
  Cc: linux-arm-kernel, linux-kernel, kernel, a.zummo, rtc-linux, wim,
	linux-watchdog, devicetree, arnd, David Paris

On Wed, Dec 17, 2014 at 04:45:25PM +0000, Lee Jones wrote:
> Signed-off-by: David Paris <david.paris@st.com>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>

Hi Lee and David,

I still have a couple of comments below. Sorry I didn't catch those earlier.

Thanks,
Guenter

> ---
>  drivers/watchdog/Kconfig  |  13 ++
>  drivers/watchdog/Makefile |   1 +
>  drivers/watchdog/st_wdt.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 337 insertions(+)
>  create mode 100644 drivers/watchdog/st_wdt.c
> 
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index f57312f..5a538af 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
>  	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
>  	  the watchdog triggers the system will be reset.
>  
> +config ST_WATCHDOG
> +	tristate "STMicroelectronics LPC Watchdog"
> +	depends on ARCH_STI
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based Watchdog timer support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_wdt.
> +
>  config TEGRA_WATCHDOG
>  	tristate "Tegra watchdog"
>  	depends on ARCH_TEGRA || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 468c320..eb19937 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
>  obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
>  obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
>  obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
> +obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
>  obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
>  obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
>  
> diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
> new file mode 100644
> index 0000000..cf3a4f7
> --- /dev/null
> +++ b/drivers/watchdog/st_wdt.c
> @@ -0,0 +1,323 @@
> +/*
> + * st-wdt.c - ST's LPC Watchdog
> + *
> + * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
> + *
> + * Author: David Paris <david.paris@st.com> for STMicroelectronics
> + *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/watchdog.h>
> +
> +#include <dt-bindings/mfd/st-lpc.h>
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF			0x410
> +#define LPC_LPA_START_OFF		0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF			0x510
> +
> +static struct watchdog_device st_wdog_dev;
> +
> +struct st_wdog_syscfg {
> +	struct regmap *regmap;
> +	unsigned int reset_type_reg;
> +	unsigned int reset_type_mask;
> +	unsigned int enable_reg;
> +	unsigned int enable_mask;
> +};
> +
> +struct st_wdog {
> +	void __iomem *base;
> +	struct device *dev;
> +	struct st_wdog_syscfg *syscfg;
> +	struct clk *clk;
> +	unsigned long clkrate;
> +	bool warm_reset;
> +};
> +
> +static struct st_wdog_syscfg stid127_syscfg = {
> +	.reset_type_reg		= 0x004,
> +	.reset_type_mask	= BIT(2),
> +	.enable_reg		= 0x000,
> +	.enable_mask		= BIT(2),
> +};
> +
> +static struct st_wdog_syscfg stih415_syscfg = {
> +	.reset_type_reg		= 0x0B8,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x0B4,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih416_syscfg = {
> +	.reset_type_reg		= 0x88C,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x888,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih407_syscfg = {
> +	.enable_reg		= 0x204,
> +	.enable_mask		= BIT(19),
> +};
> +
> +static struct of_device_id st_wdog_match[] = {
> +	{
> +		.compatible = "st,stih407-lpc",
> +		.data = (void *)&stih407_syscfg,

Nitpick: typecast to and from void * is not necessary.

> +	},
> +	{
> +		.compatible = "st,stih416-lpc",
> +		.data = (void *)&stih416_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih415-lpc",
> +		.data = (void *)&stih415_syscfg,
> +	},
> +	{
> +		.compatible = "st,stid127-lpc",
> +		.data = (void *)&stid127_syscfg,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, st_wdog_match);
> +
> +static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
> +{
> +	/* Type of watchdog reset - 0: Cold 1: Warm */
> +	if (st_wdog->syscfg->reset_type_reg)
> +		regmap_update_bits(st_wdog->syscfg->regmap,
> +				   st_wdog->syscfg->reset_type_reg,
> +				   st_wdog->syscfg->reset_type_mask,
> +				   st_wdog->warm_reset);
> +
> +	/* Mask/unmask watchdog reset */
> +	regmap_update_bits(st_wdog->syscfg->regmap,
> +			   st_wdog->syscfg->enable_reg,
> +			   st_wdog->syscfg->enable_mask,
> +			   enable ? 0 : st_wdog->syscfg->enable_mask);
> +}
> +
> +static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
> +{
> +	unsigned long clkrate = st_wdog->clkrate;
> +
> +	writel_relaxed(timeout * clkrate, st_wdog->base + LPC_LPA_LSB_OFF);
> +	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
> +}
> +
> +static int st_wdog_start(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_stop(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_set_timeout(struct watchdog_device *wdd,
> +			       unsigned int timeout)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	wdd->timeout = timeout;
> +	st_wdog_load_timer(st_wdog, timeout);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_keepalive(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	st_wdog_load_timer(st_wdog, wdd->timeout);
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info st_wdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "ST LPC WDT",
> +};
> +
> +static const struct watchdog_ops st_wdog_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= st_wdog_start,
> +	.stop		= st_wdog_stop,
> +	.ping		= st_wdog_keepalive,
> +	.set_timeout	= st_wdog_set_timeout,
> +};
> +
> +static struct watchdog_device st_wdog_dev = {
> +	.info		= &st_wdog_info,
> +	.ops		= &st_wdog_ops,
> +};
> +
> +static int st_wdog_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct st_wdog *st_wdog;
> +	struct regmap *regmap;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *base;
> +	uint32_t mode;
> +	int ret;
> +
> +	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
> +	if (ret) {
> +		dev_err(&pdev->dev, "An LPC mode must be provided\n");
> +		return -EINVAL;
> +	}
> +
> +	/* LPC can either run in RTC or WDT mode */
> +	if (mode != ST_LPC_MODE_WDT)
> +		return -ENODEV;
> +
> +	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
> +	if (!st_wdog)
> +		return -ENOMEM;
> +
> +	match = of_match_device(st_wdog_match, &pdev->dev);
> +	if (!match) {
> +		dev_err(&pdev->dev, "Couldn't match device\n");
> +		return -ENODEV;
> +	}
> +	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(base)) {
> +		dev_err(&pdev->dev, "Failed to ioremap base\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(regmap)) {
> +		dev_err(&pdev->dev, "No syscfg phandle specified\n");
> +		return PTR_ERR(regmap);
> +	}
> +
> +	clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(clk);
> +	}
> +	clk_prepare_enable(st_wdog->clk);

How does this work ? st_wdog->clk isn't set yet.

> +
> +	st_wdog->dev		= &pdev->dev;
> +	st_wdog->base		= base;
> +	st_wdog->clk		= clk;
> +	st_wdog->syscfg->regmap = regmap;
> +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> +	st_wdog->clkrate	= clk_get_rate(st_wdog->clk);
> +
> +	if (!st_wdog->clkrate) {
> +		dev_err(&pdev->dev, "Unable to fetch clock rate\n");

I think this is missing
	clk_disable_unprepare();
	clk_put();

Same for the rest of the error path handling below.

> +		return -EINVAL;
> +	}
> +	st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
> +
> +	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
> +	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
> +
> +	/* Init Watchdog timeout with value in DT */
> +	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
> +		return ret;
> +	}
> +
> +	ret = watchdog_register_device(&st_wdog_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register watchdog\n");
> +		clk_disable_unprepare(clk);
> +		return ret;
> +	}
> +
> +	st_wdog_setup(st_wdog, true);
> +
> +	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
> +		 st_wdog->warm_reset ? "warm" : "cold");
> +
> +	return ret;
> +}
> +
> +static int st_wdog_remove(struct platform_device *pdev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	st_wdog_setup(st_wdog, false);
> +	watchdog_unregister_device(&st_wdog_dev);
> +	clk_disable_unprepare(st_wdog->clk);
> +
clk_put() ?

> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM

I think this needs to be CONFIG_PM_SLEEP.
Another option might be to drop the #ifdef entirely and use
__maybe_unused instead.

> +static int st_wdog_suspend(struct device *dev)
> +{
> +	if (watchdog_active(&st_wdog_dev))
> +		st_wdog_stop(&st_wdog_dev);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_resume(struct device *dev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	if (watchdog_active(&st_wdog_dev)) {
> +		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
> +		st_wdog_start(&st_wdog_dev);
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
> +			 st_wdog_suspend,
> +			 st_wdog_resume);
> +
> +static struct platform_driver st_wdog_driver = {
> +	.driver	= {
> +		.name = "st-lpc-wdt",
> +		.pm = &st_wdog_pm_ops,
> +		.of_match_table = st_wdog_match,
> +	},
> +	.probe = st_wdog_probe,
> +	.remove = st_wdog_remove,
> +};
> +module_platform_driver(st_wdog_driver);
> +
> +MODULE_AUTHOR("David Paris <david.paris@st.com>");
> +MODULE_DESCRIPTION("ST LPC Watchdog Driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 1.9.1
> 

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-17 18:02     ` Guenter Roeck
  0 siblings, 0 replies; 69+ messages in thread
From: Guenter Roeck @ 2014-12-17 18:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Dec 17, 2014 at 04:45:25PM +0000, Lee Jones wrote:
> Signed-off-by: David Paris <david.paris@st.com>
> Signed-off-by: Lee Jones <lee.jones@linaro.org>

Hi Lee and David,

I still have a couple of comments below. Sorry I didn't catch those earlier.

Thanks,
Guenter

> ---
>  drivers/watchdog/Kconfig  |  13 ++
>  drivers/watchdog/Makefile |   1 +
>  drivers/watchdog/st_wdt.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 337 insertions(+)
>  create mode 100644 drivers/watchdog/st_wdt.c
> 
> diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> index f57312f..5a538af 100644
> --- a/drivers/watchdog/Kconfig
> +++ b/drivers/watchdog/Kconfig
> @@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
>  	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
>  	  the watchdog triggers the system will be reset.
>  
> +config ST_WATCHDOG
> +	tristate "STMicroelectronics LPC Watchdog"
> +	depends on ARCH_STI
> +	depends on OF
> +	select WATCHDOG_CORE
> +	select MFD_ST_LPC
> +	help
> +	  Say Y here to include STMicroelectronics Low Power Controller
> +	  (LPC) based Watchdog timer support.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called st_wdt.
> +
>  config TEGRA_WATCHDOG
>  	tristate "Tegra watchdog"
>  	depends on ARCH_TEGRA || COMPILE_TEST
> diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> index 468c320..eb19937 100644
> --- a/drivers/watchdog/Makefile
> +++ b/drivers/watchdog/Makefile
> @@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
>  obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
>  obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
>  obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
> +obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
>  obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
>  obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
>  
> diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
> new file mode 100644
> index 0000000..cf3a4f7
> --- /dev/null
> +++ b/drivers/watchdog/st_wdt.c
> @@ -0,0 +1,323 @@
> +/*
> + * st-wdt.c - ST's LPC Watchdog
> + *
> + * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
> + *
> + * Author: David Paris <david.paris@st.com> for STMicroelectronics
> + *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public Licence
> + * as published by the Free Software Foundation; either version
> + * 2 of the Licence, or (at your option) any later version.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/init.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/watchdog.h>
> +
> +#include <dt-bindings/mfd/st-lpc.h>
> +
> +/* Low Power Alarm */
> +#define LPC_LPA_LSB_OFF			0x410
> +#define LPC_LPA_START_OFF		0x418
> +
> +/* LPC as WDT */
> +#define LPC_WDT_OFF			0x510
> +
> +static struct watchdog_device st_wdog_dev;
> +
> +struct st_wdog_syscfg {
> +	struct regmap *regmap;
> +	unsigned int reset_type_reg;
> +	unsigned int reset_type_mask;
> +	unsigned int enable_reg;
> +	unsigned int enable_mask;
> +};
> +
> +struct st_wdog {
> +	void __iomem *base;
> +	struct device *dev;
> +	struct st_wdog_syscfg *syscfg;
> +	struct clk *clk;
> +	unsigned long clkrate;
> +	bool warm_reset;
> +};
> +
> +static struct st_wdog_syscfg stid127_syscfg = {
> +	.reset_type_reg		= 0x004,
> +	.reset_type_mask	= BIT(2),
> +	.enable_reg		= 0x000,
> +	.enable_mask		= BIT(2),
> +};
> +
> +static struct st_wdog_syscfg stih415_syscfg = {
> +	.reset_type_reg		= 0x0B8,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x0B4,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih416_syscfg = {
> +	.reset_type_reg		= 0x88C,
> +	.reset_type_mask	= BIT(6),
> +	.enable_reg		= 0x888,
> +	.enable_mask		= BIT(7),
> +};
> +
> +static struct st_wdog_syscfg stih407_syscfg = {
> +	.enable_reg		= 0x204,
> +	.enable_mask		= BIT(19),
> +};
> +
> +static struct of_device_id st_wdog_match[] = {
> +	{
> +		.compatible = "st,stih407-lpc",
> +		.data = (void *)&stih407_syscfg,

Nitpick: typecast to and from void * is not necessary.

> +	},
> +	{
> +		.compatible = "st,stih416-lpc",
> +		.data = (void *)&stih416_syscfg,
> +	},
> +	{
> +		.compatible = "st,stih415-lpc",
> +		.data = (void *)&stih415_syscfg,
> +	},
> +	{
> +		.compatible = "st,stid127-lpc",
> +		.data = (void *)&stid127_syscfg,
> +	},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, st_wdog_match);
> +
> +static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
> +{
> +	/* Type of watchdog reset - 0: Cold 1: Warm */
> +	if (st_wdog->syscfg->reset_type_reg)
> +		regmap_update_bits(st_wdog->syscfg->regmap,
> +				   st_wdog->syscfg->reset_type_reg,
> +				   st_wdog->syscfg->reset_type_mask,
> +				   st_wdog->warm_reset);
> +
> +	/* Mask/unmask watchdog reset */
> +	regmap_update_bits(st_wdog->syscfg->regmap,
> +			   st_wdog->syscfg->enable_reg,
> +			   st_wdog->syscfg->enable_mask,
> +			   enable ? 0 : st_wdog->syscfg->enable_mask);
> +}
> +
> +static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
> +{
> +	unsigned long clkrate = st_wdog->clkrate;
> +
> +	writel_relaxed(timeout * clkrate, st_wdog->base + LPC_LPA_LSB_OFF);
> +	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
> +}
> +
> +static int st_wdog_start(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_stop(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_set_timeout(struct watchdog_device *wdd,
> +			       unsigned int timeout)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	wdd->timeout = timeout;
> +	st_wdog_load_timer(st_wdog, timeout);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_keepalive(struct watchdog_device *wdd)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
> +
> +	st_wdog_load_timer(st_wdog, wdd->timeout);
> +
> +	return 0;
> +}
> +
> +static const struct watchdog_info st_wdog_info = {
> +	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
> +	.identity = "ST LPC WDT",
> +};
> +
> +static const struct watchdog_ops st_wdog_ops = {
> +	.owner		= THIS_MODULE,
> +	.start		= st_wdog_start,
> +	.stop		= st_wdog_stop,
> +	.ping		= st_wdog_keepalive,
> +	.set_timeout	= st_wdog_set_timeout,
> +};
> +
> +static struct watchdog_device st_wdog_dev = {
> +	.info		= &st_wdog_info,
> +	.ops		= &st_wdog_ops,
> +};
> +
> +static int st_wdog_probe(struct platform_device *pdev)
> +{
> +	const struct of_device_id *match;
> +	struct device_node *np = pdev->dev.of_node;
> +	struct st_wdog *st_wdog;
> +	struct regmap *regmap;
> +	struct resource *res;
> +	struct clk *clk;
> +	void __iomem *base;
> +	uint32_t mode;
> +	int ret;
> +
> +	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
> +	if (ret) {
> +		dev_err(&pdev->dev, "An LPC mode must be provided\n");
> +		return -EINVAL;
> +	}
> +
> +	/* LPC can either run in RTC or WDT mode */
> +	if (mode != ST_LPC_MODE_WDT)
> +		return -ENODEV;
> +
> +	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
> +	if (!st_wdog)
> +		return -ENOMEM;
> +
> +	match = of_match_device(st_wdog_match, &pdev->dev);
> +	if (!match) {
> +		dev_err(&pdev->dev, "Couldn't match device\n");
> +		return -ENODEV;
> +	}
> +	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(base)) {
> +		dev_err(&pdev->dev, "Failed to ioremap base\n");
> +		return PTR_ERR(base);
> +	}
> +
> +	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(regmap)) {
> +		dev_err(&pdev->dev, "No syscfg phandle specified\n");
> +		return PTR_ERR(regmap);
> +	}
> +
> +	clk = clk_get(&pdev->dev, NULL);
> +	if (IS_ERR(clk)) {
> +		dev_err(&pdev->dev, "Unable to request clock\n");
> +		return PTR_ERR(clk);
> +	}
> +	clk_prepare_enable(st_wdog->clk);

How does this work ? st_wdog->clk isn't set yet.

> +
> +	st_wdog->dev		= &pdev->dev;
> +	st_wdog->base		= base;
> +	st_wdog->clk		= clk;
> +	st_wdog->syscfg->regmap = regmap;
> +	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
> +	st_wdog->clkrate	= clk_get_rate(st_wdog->clk);
> +
> +	if (!st_wdog->clkrate) {
> +		dev_err(&pdev->dev, "Unable to fetch clock rate\n");

I think this is missing
	clk_disable_unprepare();
	clk_put();

Same for the rest of the error path handling below.

> +		return -EINVAL;
> +	}
> +	st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
> +
> +	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
> +	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
> +
> +	/* Init Watchdog timeout with value in DT */
> +	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
> +		return ret;
> +	}
> +
> +	ret = watchdog_register_device(&st_wdog_dev);
> +	if (ret) {
> +		dev_err(&pdev->dev, "Unable to register watchdog\n");
> +		clk_disable_unprepare(clk);
> +		return ret;
> +	}
> +
> +	st_wdog_setup(st_wdog, true);
> +
> +	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
> +		 st_wdog->warm_reset ? "warm" : "cold");
> +
> +	return ret;
> +}
> +
> +static int st_wdog_remove(struct platform_device *pdev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	st_wdog_setup(st_wdog, false);
> +	watchdog_unregister_device(&st_wdog_dev);
> +	clk_disable_unprepare(st_wdog->clk);
> +
clk_put() ?

> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM

I think this needs to be CONFIG_PM_SLEEP.
Another option might be to drop the #ifdef entirely and use
__maybe_unused instead.

> +static int st_wdog_suspend(struct device *dev)
> +{
> +	if (watchdog_active(&st_wdog_dev))
> +		st_wdog_stop(&st_wdog_dev);
> +
> +	return 0;
> +}
> +
> +static int st_wdog_resume(struct device *dev)
> +{
> +	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
> +
> +	if (watchdog_active(&st_wdog_dev)) {
> +		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
> +		st_wdog_start(&st_wdog_dev);
> +	}
> +
> +	return 0;
> +}
> +#endif
> +
> +static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
> +			 st_wdog_suspend,
> +			 st_wdog_resume);
> +
> +static struct platform_driver st_wdog_driver = {
> +	.driver	= {
> +		.name = "st-lpc-wdt",
> +		.pm = &st_wdog_pm_ops,
> +		.of_match_table = st_wdog_match,
> +	},
> +	.probe = st_wdog_probe,
> +	.remove = st_wdog_remove,
> +};
> +module_platform_driver(st_wdog_driver);
> +
> +MODULE_AUTHOR("David Paris <david.paris@st.com>");
> +MODULE_DESCRIPTION("ST LPC Watchdog Driver");
> +MODULE_LICENSE("GPL v2");
> -- 
> 1.9.1
> 

^ permalink raw reply	[flat|nested] 69+ messages in thread

* [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-17 16:45   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-17 16:45 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: lee.jones, kernel, a.zummo, rtc-linux, wim, linux-watchdog,
	devicetree, linux, arnd, David Paris

Signed-off-by: David Paris <david.paris@st.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/watchdog/Kconfig  |  13 ++
 drivers/watchdog/Makefile |   1 +
 drivers/watchdog/st_wdt.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 337 insertions(+)
 create mode 100644 drivers/watchdog/st_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f57312f..5a538af 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
 	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
 	  the watchdog triggers the system will be reset.
 
+config ST_WATCHDOG
+	tristate "STMicroelectronics LPC Watchdog"
+	depends on ARCH_STI
+	depends on OF
+	select WATCHDOG_CORE
+	select MFD_ST_LPC
+	help
+	  Say Y here to include STMicroelectronics Low Power Controller
+	  (LPC) based Watchdog timer support.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called st_wdt.
+
 config TEGRA_WATCHDOG
 	tristate "Tegra watchdog"
 	depends on ARCH_TEGRA || COMPILE_TEST
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 468c320..eb19937 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
 obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
 obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
 obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
+obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
 obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
 obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
 
diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
new file mode 100644
index 0000000..cf3a4f7
--- /dev/null
+++ b/drivers/watchdog/st_wdt.c
@@ -0,0 +1,323 @@
+/*
+ * st-wdt.c - ST's LPC Watchdog
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: David Paris <david.paris@st.com> for STMicroelectronics
+ *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/watchdog.h>
+
+#include <dt-bindings/mfd/st-lpc.h>
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF			0x410
+#define LPC_LPA_START_OFF		0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF			0x510
+
+static struct watchdog_device st_wdog_dev;
+
+struct st_wdog_syscfg {
+	struct regmap *regmap;
+	unsigned int reset_type_reg;
+	unsigned int reset_type_mask;
+	unsigned int enable_reg;
+	unsigned int enable_mask;
+};
+
+struct st_wdog {
+	void __iomem *base;
+	struct device *dev;
+	struct st_wdog_syscfg *syscfg;
+	struct clk *clk;
+	unsigned long clkrate;
+	bool warm_reset;
+};
+
+static struct st_wdog_syscfg stid127_syscfg = {
+	.reset_type_reg		= 0x004,
+	.reset_type_mask	= BIT(2),
+	.enable_reg		= 0x000,
+	.enable_mask		= BIT(2),
+};
+
+static struct st_wdog_syscfg stih415_syscfg = {
+	.reset_type_reg		= 0x0B8,
+	.reset_type_mask	= BIT(6),
+	.enable_reg		= 0x0B4,
+	.enable_mask		= BIT(7),
+};
+
+static struct st_wdog_syscfg stih416_syscfg = {
+	.reset_type_reg		= 0x88C,
+	.reset_type_mask	= BIT(6),
+	.enable_reg		= 0x888,
+	.enable_mask		= BIT(7),
+};
+
+static struct st_wdog_syscfg stih407_syscfg = {
+	.enable_reg		= 0x204,
+	.enable_mask		= BIT(19),
+};
+
+static struct of_device_id st_wdog_match[] = {
+	{
+		.compatible = "st,stih407-lpc",
+		.data = (void *)&stih407_syscfg,
+	},
+	{
+		.compatible = "st,stih416-lpc",
+		.data = (void *)&stih416_syscfg,
+	},
+	{
+		.compatible = "st,stih415-lpc",
+		.data = (void *)&stih415_syscfg,
+	},
+	{
+		.compatible = "st,stid127-lpc",
+		.data = (void *)&stid127_syscfg,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, st_wdog_match);
+
+static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
+{
+	/* Type of watchdog reset - 0: Cold 1: Warm */
+	if (st_wdog->syscfg->reset_type_reg)
+		regmap_update_bits(st_wdog->syscfg->regmap,
+				   st_wdog->syscfg->reset_type_reg,
+				   st_wdog->syscfg->reset_type_mask,
+				   st_wdog->warm_reset);
+
+	/* Mask/unmask watchdog reset */
+	regmap_update_bits(st_wdog->syscfg->regmap,
+			   st_wdog->syscfg->enable_reg,
+			   st_wdog->syscfg->enable_mask,
+			   enable ? 0 : st_wdog->syscfg->enable_mask);
+}
+
+static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
+{
+	unsigned long clkrate = st_wdog->clkrate;
+
+	writel_relaxed(timeout * clkrate, st_wdog->base + LPC_LPA_LSB_OFF);
+	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
+}
+
+static int st_wdog_start(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
+
+	return 0;
+}
+
+static int st_wdog_stop(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
+
+	return 0;
+}
+
+static int st_wdog_set_timeout(struct watchdog_device *wdd,
+			       unsigned int timeout)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	wdd->timeout = timeout;
+	st_wdog_load_timer(st_wdog, timeout);
+
+	return 0;
+}
+
+static int st_wdog_keepalive(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	st_wdog_load_timer(st_wdog, wdd->timeout);
+
+	return 0;
+}
+
+static const struct watchdog_info st_wdog_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "ST LPC WDT",
+};
+
+static const struct watchdog_ops st_wdog_ops = {
+	.owner		= THIS_MODULE,
+	.start		= st_wdog_start,
+	.stop		= st_wdog_stop,
+	.ping		= st_wdog_keepalive,
+	.set_timeout	= st_wdog_set_timeout,
+};
+
+static struct watchdog_device st_wdog_dev = {
+	.info		= &st_wdog_info,
+	.ops		= &st_wdog_ops,
+};
+
+static int st_wdog_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct device_node *np = pdev->dev.of_node;
+	struct st_wdog *st_wdog;
+	struct regmap *regmap;
+	struct resource *res;
+	struct clk *clk;
+	void __iomem *base;
+	uint32_t mode;
+	int ret;
+
+	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
+	if (ret) {
+		dev_err(&pdev->dev, "An LPC mode must be provided\n");
+		return -EINVAL;
+	}
+
+	/* LPC can either run in RTC or WDT mode */
+	if (mode != ST_LPC_MODE_WDT)
+		return -ENODEV;
+
+	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
+	if (!st_wdog)
+		return -ENOMEM;
+
+	match = of_match_device(st_wdog_match, &pdev->dev);
+	if (!match) {
+		dev_err(&pdev->dev, "Couldn't match device\n");
+		return -ENODEV;
+	}
+	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base)) {
+		dev_err(&pdev->dev, "Failed to ioremap base\n");
+		return PTR_ERR(base);
+	}
+
+	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+	if (IS_ERR(regmap)) {
+		dev_err(&pdev->dev, "No syscfg phandle specified\n");
+		return PTR_ERR(regmap);
+	}
+
+	clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "Unable to request clock\n");
+		return PTR_ERR(clk);
+	}
+	clk_prepare_enable(st_wdog->clk);
+
+	st_wdog->dev		= &pdev->dev;
+	st_wdog->base		= base;
+	st_wdog->clk		= clk;
+	st_wdog->syscfg->regmap = regmap;
+	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
+	st_wdog->clkrate	= clk_get_rate(st_wdog->clk);
+
+	if (!st_wdog->clkrate) {
+		dev_err(&pdev->dev, "Unable to fetch clock rate\n");
+		return -EINVAL;
+	}
+	st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
+
+	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
+	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
+
+	/* Init Watchdog timeout with value in DT */
+	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
+		return ret;
+	}
+
+	ret = watchdog_register_device(&st_wdog_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to register watchdog\n");
+		clk_disable_unprepare(clk);
+		return ret;
+	}
+
+	st_wdog_setup(st_wdog, true);
+
+	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
+		 st_wdog->warm_reset ? "warm" : "cold");
+
+	return ret;
+}
+
+static int st_wdog_remove(struct platform_device *pdev)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+	st_wdog_setup(st_wdog, false);
+	watchdog_unregister_device(&st_wdog_dev);
+	clk_disable_unprepare(st_wdog->clk);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int st_wdog_suspend(struct device *dev)
+{
+	if (watchdog_active(&st_wdog_dev))
+		st_wdog_stop(&st_wdog_dev);
+
+	return 0;
+}
+
+static int st_wdog_resume(struct device *dev)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+	if (watchdog_active(&st_wdog_dev)) {
+		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
+		st_wdog_start(&st_wdog_dev);
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
+			 st_wdog_suspend,
+			 st_wdog_resume);
+
+static struct platform_driver st_wdog_driver = {
+	.driver	= {
+		.name = "st-lpc-wdt",
+		.pm = &st_wdog_pm_ops,
+		.of_match_table = st_wdog_match,
+	},
+	.probe = st_wdog_probe,
+	.remove = st_wdog_remove,
+};
+module_platform_driver(st_wdog_driver);
+
+MODULE_AUTHOR("David Paris <david.paris@st.com>");
+MODULE_DESCRIPTION("ST LPC Watchdog Driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-17 16:45   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-17 16:45 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: lee.jones-QSEj5FYQhm4dnm+yROfE0A, kernel-F5mvAk5X5gdBDgjK7y7TUQ,
	a.zummo-BfzFCNDTiLLj+vYz1yj4TQ, rtc-linux-/JYPxA39Uh5TLH3MbocFFw,
	wim-IQzOog9fTRqzQB+pC5nmwQ,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA, linux-0h96xk9xTtrk1uMJSBkQmQ,
	arnd-r2nGTMty4D4, David Paris

Signed-off-by: David Paris <david.paris-qxv4g6HH51o@public.gmane.org>
Signed-off-by: Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 drivers/watchdog/Kconfig  |  13 ++
 drivers/watchdog/Makefile |   1 +
 drivers/watchdog/st_wdt.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 337 insertions(+)
 create mode 100644 drivers/watchdog/st_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f57312f..5a538af 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
 	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
 	  the watchdog triggers the system will be reset.
 
+config ST_WATCHDOG
+	tristate "STMicroelectronics LPC Watchdog"
+	depends on ARCH_STI
+	depends on OF
+	select WATCHDOG_CORE
+	select MFD_ST_LPC
+	help
+	  Say Y here to include STMicroelectronics Low Power Controller
+	  (LPC) based Watchdog timer support.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called st_wdt.
+
 config TEGRA_WATCHDOG
 	tristate "Tegra watchdog"
 	depends on ARCH_TEGRA || COMPILE_TEST
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 468c320..eb19937 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
 obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
 obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
 obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
+obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
 obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
 obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
 
diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
new file mode 100644
index 0000000..cf3a4f7
--- /dev/null
+++ b/drivers/watchdog/st_wdt.c
@@ -0,0 +1,323 @@
+/*
+ * st-wdt.c - ST's LPC Watchdog
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: David Paris <david.paris-qxv4g6HH51o@public.gmane.org> for STMicroelectronics
+ *         Lee Jones <lee.jones-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> for STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/watchdog.h>
+
+#include <dt-bindings/mfd/st-lpc.h>
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF			0x410
+#define LPC_LPA_START_OFF		0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF			0x510
+
+static struct watchdog_device st_wdog_dev;
+
+struct st_wdog_syscfg {
+	struct regmap *regmap;
+	unsigned int reset_type_reg;
+	unsigned int reset_type_mask;
+	unsigned int enable_reg;
+	unsigned int enable_mask;
+};
+
+struct st_wdog {
+	void __iomem *base;
+	struct device *dev;
+	struct st_wdog_syscfg *syscfg;
+	struct clk *clk;
+	unsigned long clkrate;
+	bool warm_reset;
+};
+
+static struct st_wdog_syscfg stid127_syscfg = {
+	.reset_type_reg		= 0x004,
+	.reset_type_mask	= BIT(2),
+	.enable_reg		= 0x000,
+	.enable_mask		= BIT(2),
+};
+
+static struct st_wdog_syscfg stih415_syscfg = {
+	.reset_type_reg		= 0x0B8,
+	.reset_type_mask	= BIT(6),
+	.enable_reg		= 0x0B4,
+	.enable_mask		= BIT(7),
+};
+
+static struct st_wdog_syscfg stih416_syscfg = {
+	.reset_type_reg		= 0x88C,
+	.reset_type_mask	= BIT(6),
+	.enable_reg		= 0x888,
+	.enable_mask		= BIT(7),
+};
+
+static struct st_wdog_syscfg stih407_syscfg = {
+	.enable_reg		= 0x204,
+	.enable_mask		= BIT(19),
+};
+
+static struct of_device_id st_wdog_match[] = {
+	{
+		.compatible = "st,stih407-lpc",
+		.data = (void *)&stih407_syscfg,
+	},
+	{
+		.compatible = "st,stih416-lpc",
+		.data = (void *)&stih416_syscfg,
+	},
+	{
+		.compatible = "st,stih415-lpc",
+		.data = (void *)&stih415_syscfg,
+	},
+	{
+		.compatible = "st,stid127-lpc",
+		.data = (void *)&stid127_syscfg,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, st_wdog_match);
+
+static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
+{
+	/* Type of watchdog reset - 0: Cold 1: Warm */
+	if (st_wdog->syscfg->reset_type_reg)
+		regmap_update_bits(st_wdog->syscfg->regmap,
+				   st_wdog->syscfg->reset_type_reg,
+				   st_wdog->syscfg->reset_type_mask,
+				   st_wdog->warm_reset);
+
+	/* Mask/unmask watchdog reset */
+	regmap_update_bits(st_wdog->syscfg->regmap,
+			   st_wdog->syscfg->enable_reg,
+			   st_wdog->syscfg->enable_mask,
+			   enable ? 0 : st_wdog->syscfg->enable_mask);
+}
+
+static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
+{
+	unsigned long clkrate = st_wdog->clkrate;
+
+	writel_relaxed(timeout * clkrate, st_wdog->base + LPC_LPA_LSB_OFF);
+	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
+}
+
+static int st_wdog_start(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
+
+	return 0;
+}
+
+static int st_wdog_stop(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
+
+	return 0;
+}
+
+static int st_wdog_set_timeout(struct watchdog_device *wdd,
+			       unsigned int timeout)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	wdd->timeout = timeout;
+	st_wdog_load_timer(st_wdog, timeout);
+
+	return 0;
+}
+
+static int st_wdog_keepalive(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	st_wdog_load_timer(st_wdog, wdd->timeout);
+
+	return 0;
+}
+
+static const struct watchdog_info st_wdog_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "ST LPC WDT",
+};
+
+static const struct watchdog_ops st_wdog_ops = {
+	.owner		= THIS_MODULE,
+	.start		= st_wdog_start,
+	.stop		= st_wdog_stop,
+	.ping		= st_wdog_keepalive,
+	.set_timeout	= st_wdog_set_timeout,
+};
+
+static struct watchdog_device st_wdog_dev = {
+	.info		= &st_wdog_info,
+	.ops		= &st_wdog_ops,
+};
+
+static int st_wdog_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct device_node *np = pdev->dev.of_node;
+	struct st_wdog *st_wdog;
+	struct regmap *regmap;
+	struct resource *res;
+	struct clk *clk;
+	void __iomem *base;
+	uint32_t mode;
+	int ret;
+
+	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
+	if (ret) {
+		dev_err(&pdev->dev, "An LPC mode must be provided\n");
+		return -EINVAL;
+	}
+
+	/* LPC can either run in RTC or WDT mode */
+	if (mode != ST_LPC_MODE_WDT)
+		return -ENODEV;
+
+	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
+	if (!st_wdog)
+		return -ENOMEM;
+
+	match = of_match_device(st_wdog_match, &pdev->dev);
+	if (!match) {
+		dev_err(&pdev->dev, "Couldn't match device\n");
+		return -ENODEV;
+	}
+	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base)) {
+		dev_err(&pdev->dev, "Failed to ioremap base\n");
+		return PTR_ERR(base);
+	}
+
+	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+	if (IS_ERR(regmap)) {
+		dev_err(&pdev->dev, "No syscfg phandle specified\n");
+		return PTR_ERR(regmap);
+	}
+
+	clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "Unable to request clock\n");
+		return PTR_ERR(clk);
+	}
+	clk_prepare_enable(st_wdog->clk);
+
+	st_wdog->dev		= &pdev->dev;
+	st_wdog->base		= base;
+	st_wdog->clk		= clk;
+	st_wdog->syscfg->regmap = regmap;
+	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
+	st_wdog->clkrate	= clk_get_rate(st_wdog->clk);
+
+	if (!st_wdog->clkrate) {
+		dev_err(&pdev->dev, "Unable to fetch clock rate\n");
+		return -EINVAL;
+	}
+	st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
+
+	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
+	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
+
+	/* Init Watchdog timeout with value in DT */
+	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
+		return ret;
+	}
+
+	ret = watchdog_register_device(&st_wdog_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to register watchdog\n");
+		clk_disable_unprepare(clk);
+		return ret;
+	}
+
+	st_wdog_setup(st_wdog, true);
+
+	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
+		 st_wdog->warm_reset ? "warm" : "cold");
+
+	return ret;
+}
+
+static int st_wdog_remove(struct platform_device *pdev)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+	st_wdog_setup(st_wdog, false);
+	watchdog_unregister_device(&st_wdog_dev);
+	clk_disable_unprepare(st_wdog->clk);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int st_wdog_suspend(struct device *dev)
+{
+	if (watchdog_active(&st_wdog_dev))
+		st_wdog_stop(&st_wdog_dev);
+
+	return 0;
+}
+
+static int st_wdog_resume(struct device *dev)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+	if (watchdog_active(&st_wdog_dev)) {
+		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
+		st_wdog_start(&st_wdog_dev);
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
+			 st_wdog_suspend,
+			 st_wdog_resume);
+
+static struct platform_driver st_wdog_driver = {
+	.driver	= {
+		.name = "st-lpc-wdt",
+		.pm = &st_wdog_pm_ops,
+		.of_match_table = st_wdog_match,
+	},
+	.probe = st_wdog_probe,
+	.remove = st_wdog_remove,
+};
+module_platform_driver(st_wdog_driver);
+
+MODULE_AUTHOR("David Paris <david.paris-qxv4g6HH51o@public.gmane.org>");
+MODULE_DESCRIPTION("ST LPC Watchdog Driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 69+ messages in thread

* [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog
@ 2014-12-17 16:45   ` Lee Jones
  0 siblings, 0 replies; 69+ messages in thread
From: Lee Jones @ 2014-12-17 16:45 UTC (permalink / raw)
  To: linux-arm-kernel

Signed-off-by: David Paris <david.paris@st.com>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
---
 drivers/watchdog/Kconfig  |  13 ++
 drivers/watchdog/Makefile |   1 +
 drivers/watchdog/st_wdt.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 337 insertions(+)
 create mode 100644 drivers/watchdog/st_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index f57312f..5a538af 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -432,6 +432,19 @@ config SIRFSOC_WATCHDOG
 	  Support for CSR SiRFprimaII and SiRFatlasVI watchdog. When
 	  the watchdog triggers the system will be reset.
 
+config ST_WATCHDOG
+	tristate "STMicroelectronics LPC Watchdog"
+	depends on ARCH_STI
+	depends on OF
+	select WATCHDOG_CORE
+	select MFD_ST_LPC
+	help
+	  Say Y here to include STMicroelectronics Low Power Controller
+	  (LPC) based Watchdog timer support.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called st_wdt.
+
 config TEGRA_WATCHDOG
 	tristate "Tegra watchdog"
 	depends on ARCH_TEGRA || COMPILE_TEST
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 468c320..eb19937 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_RETU_WATCHDOG) += retu_wdt.o
 obj-$(CONFIG_BCM2835_WDT) += bcm2835_wdt.o
 obj-$(CONFIG_MOXART_WDT) += moxart_wdt.o
 obj-$(CONFIG_SIRFSOC_WATCHDOG) += sirfsoc_wdt.o
+obj-$(CONFIG_ST_WATCHDOG) += st_wdt.o
 obj-$(CONFIG_BCM_KONA_WDT) += bcm_kona_wdt.o
 obj-$(CONFIG_TEGRA_WATCHDOG) += tegra_wdt.o
 
diff --git a/drivers/watchdog/st_wdt.c b/drivers/watchdog/st_wdt.c
new file mode 100644
index 0000000..cf3a4f7
--- /dev/null
+++ b/drivers/watchdog/st_wdt.c
@@ -0,0 +1,323 @@
+/*
+ * st-wdt.c - ST's LPC Watchdog
+ *
+ * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
+ *
+ * Author: David Paris <david.paris@st.com> for STMicroelectronics
+ *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/watchdog.h>
+
+#include <dt-bindings/mfd/st-lpc.h>
+
+/* Low Power Alarm */
+#define LPC_LPA_LSB_OFF			0x410
+#define LPC_LPA_START_OFF		0x418
+
+/* LPC as WDT */
+#define LPC_WDT_OFF			0x510
+
+static struct watchdog_device st_wdog_dev;
+
+struct st_wdog_syscfg {
+	struct regmap *regmap;
+	unsigned int reset_type_reg;
+	unsigned int reset_type_mask;
+	unsigned int enable_reg;
+	unsigned int enable_mask;
+};
+
+struct st_wdog {
+	void __iomem *base;
+	struct device *dev;
+	struct st_wdog_syscfg *syscfg;
+	struct clk *clk;
+	unsigned long clkrate;
+	bool warm_reset;
+};
+
+static struct st_wdog_syscfg stid127_syscfg = {
+	.reset_type_reg		= 0x004,
+	.reset_type_mask	= BIT(2),
+	.enable_reg		= 0x000,
+	.enable_mask		= BIT(2),
+};
+
+static struct st_wdog_syscfg stih415_syscfg = {
+	.reset_type_reg		= 0x0B8,
+	.reset_type_mask	= BIT(6),
+	.enable_reg		= 0x0B4,
+	.enable_mask		= BIT(7),
+};
+
+static struct st_wdog_syscfg stih416_syscfg = {
+	.reset_type_reg		= 0x88C,
+	.reset_type_mask	= BIT(6),
+	.enable_reg		= 0x888,
+	.enable_mask		= BIT(7),
+};
+
+static struct st_wdog_syscfg stih407_syscfg = {
+	.enable_reg		= 0x204,
+	.enable_mask		= BIT(19),
+};
+
+static struct of_device_id st_wdog_match[] = {
+	{
+		.compatible = "st,stih407-lpc",
+		.data = (void *)&stih407_syscfg,
+	},
+	{
+		.compatible = "st,stih416-lpc",
+		.data = (void *)&stih416_syscfg,
+	},
+	{
+		.compatible = "st,stih415-lpc",
+		.data = (void *)&stih415_syscfg,
+	},
+	{
+		.compatible = "st,stid127-lpc",
+		.data = (void *)&stid127_syscfg,
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, st_wdog_match);
+
+static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
+{
+	/* Type of watchdog reset - 0: Cold 1: Warm */
+	if (st_wdog->syscfg->reset_type_reg)
+		regmap_update_bits(st_wdog->syscfg->regmap,
+				   st_wdog->syscfg->reset_type_reg,
+				   st_wdog->syscfg->reset_type_mask,
+				   st_wdog->warm_reset);
+
+	/* Mask/unmask watchdog reset */
+	regmap_update_bits(st_wdog->syscfg->regmap,
+			   st_wdog->syscfg->enable_reg,
+			   st_wdog->syscfg->enable_mask,
+			   enable ? 0 : st_wdog->syscfg->enable_mask);
+}
+
+static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
+{
+	unsigned long clkrate = st_wdog->clkrate;
+
+	writel_relaxed(timeout * clkrate, st_wdog->base + LPC_LPA_LSB_OFF);
+	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
+}
+
+static int st_wdog_start(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
+
+	return 0;
+}
+
+static int st_wdog_stop(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
+
+	return 0;
+}
+
+static int st_wdog_set_timeout(struct watchdog_device *wdd,
+			       unsigned int timeout)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	wdd->timeout = timeout;
+	st_wdog_load_timer(st_wdog, timeout);
+
+	return 0;
+}
+
+static int st_wdog_keepalive(struct watchdog_device *wdd)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
+
+	st_wdog_load_timer(st_wdog, wdd->timeout);
+
+	return 0;
+}
+
+static const struct watchdog_info st_wdog_info = {
+	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+	.identity = "ST LPC WDT",
+};
+
+static const struct watchdog_ops st_wdog_ops = {
+	.owner		= THIS_MODULE,
+	.start		= st_wdog_start,
+	.stop		= st_wdog_stop,
+	.ping		= st_wdog_keepalive,
+	.set_timeout	= st_wdog_set_timeout,
+};
+
+static struct watchdog_device st_wdog_dev = {
+	.info		= &st_wdog_info,
+	.ops		= &st_wdog_ops,
+};
+
+static int st_wdog_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *match;
+	struct device_node *np = pdev->dev.of_node;
+	struct st_wdog *st_wdog;
+	struct regmap *regmap;
+	struct resource *res;
+	struct clk *clk;
+	void __iomem *base;
+	uint32_t mode;
+	int ret;
+
+	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
+	if (ret) {
+		dev_err(&pdev->dev, "An LPC mode must be provided\n");
+		return -EINVAL;
+	}
+
+	/* LPC can either run in RTC or WDT mode */
+	if (mode != ST_LPC_MODE_WDT)
+		return -ENODEV;
+
+	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
+	if (!st_wdog)
+		return -ENOMEM;
+
+	match = of_match_device(st_wdog_match, &pdev->dev);
+	if (!match) {
+		dev_err(&pdev->dev, "Couldn't match device\n");
+		return -ENODEV;
+	}
+	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(base)) {
+		dev_err(&pdev->dev, "Failed to ioremap base\n");
+		return PTR_ERR(base);
+	}
+
+	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+	if (IS_ERR(regmap)) {
+		dev_err(&pdev->dev, "No syscfg phandle specified\n");
+		return PTR_ERR(regmap);
+	}
+
+	clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR(clk)) {
+		dev_err(&pdev->dev, "Unable to request clock\n");
+		return PTR_ERR(clk);
+	}
+	clk_prepare_enable(st_wdog->clk);
+
+	st_wdog->dev		= &pdev->dev;
+	st_wdog->base		= base;
+	st_wdog->clk		= clk;
+	st_wdog->syscfg->regmap = regmap;
+	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
+	st_wdog->clkrate	= clk_get_rate(st_wdog->clk);
+
+	if (!st_wdog->clkrate) {
+		dev_err(&pdev->dev, "Unable to fetch clock rate\n");
+		return -EINVAL;
+	}
+	st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
+
+	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
+	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
+
+	/* Init Watchdog timeout with value in DT */
+	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
+		return ret;
+	}
+
+	ret = watchdog_register_device(&st_wdog_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "Unable to register watchdog\n");
+		clk_disable_unprepare(clk);
+		return ret;
+	}
+
+	st_wdog_setup(st_wdog, true);
+
+	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
+		 st_wdog->warm_reset ? "warm" : "cold");
+
+	return ret;
+}
+
+static int st_wdog_remove(struct platform_device *pdev)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+	st_wdog_setup(st_wdog, false);
+	watchdog_unregister_device(&st_wdog_dev);
+	clk_disable_unprepare(st_wdog->clk);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int st_wdog_suspend(struct device *dev)
+{
+	if (watchdog_active(&st_wdog_dev))
+		st_wdog_stop(&st_wdog_dev);
+
+	return 0;
+}
+
+static int st_wdog_resume(struct device *dev)
+{
+	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
+
+	if (watchdog_active(&st_wdog_dev)) {
+		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
+		st_wdog_start(&st_wdog_dev);
+	}
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
+			 st_wdog_suspend,
+			 st_wdog_resume);
+
+static struct platform_driver st_wdog_driver = {
+	.driver	= {
+		.name = "st-lpc-wdt",
+		.pm = &st_wdog_pm_ops,
+		.of_match_table = st_wdog_match,
+	},
+	.probe = st_wdog_probe,
+	.remove = st_wdog_remove,
+};
+module_platform_driver(st_wdog_driver);
+
+MODULE_AUTHOR("David Paris <david.paris@st.com>");
+MODULE_DESCRIPTION("ST LPC Watchdog Driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 69+ messages in thread

end of thread, other threads:[~2014-12-18 17:20 UTC | newest]

Thread overview: 69+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-12-15 11:25 [PATCH 0/8] mfd: watchdog: rtc: New driver for ST's LPC IP Lee Jones
2014-12-15 11:25 ` Lee Jones
2014-12-15 11:25 ` Lee Jones
2014-12-15 11:25 ` [PATCH 1/8] ARM: multi_v7_defconfig: Enable support for ST's LPC Watchdog Lee Jones
2014-12-15 11:25   ` Lee Jones
2014-12-15 11:25   ` Lee Jones
2014-12-15 11:25 ` [PATCH 2/8] ARM: multi_v7_defconfig: Enable support for ST's LPC RTC Lee Jones
2014-12-15 11:25   ` Lee Jones
2014-12-15 11:25   ` Lee Jones
2014-12-15 11:25 ` [PATCH 3/8] mfd: bindings: Provide ST bindings for ST's LPC device Lee Jones
2014-12-15 11:25   ` Lee Jones
2014-12-15 11:25   ` Lee Jones
2014-12-15 11:25 ` [PATCH 4/8] mfd: dt-bindings: Provide human readable defines for LPC mode choosing Lee Jones
2014-12-15 11:25   ` Lee Jones
2014-12-15 11:25 ` [PATCH 5/8] mfd: Add ST's Low Power Controller driver Lee Jones
2014-12-15 11:25   ` Lee Jones
2014-12-15 11:25   ` Lee Jones
2014-12-15 13:38   ` Arnd Bergmann
2014-12-15 13:38     ` Arnd Bergmann
2014-12-15 13:38     ` Arnd Bergmann
2014-12-15 13:50     ` Lee Jones
2014-12-15 13:50       ` Lee Jones
2014-12-15 13:50       ` Lee Jones
2014-12-15 14:06       ` Arnd Bergmann
2014-12-15 14:06         ` Arnd Bergmann
2014-12-15 14:06         ` Arnd Bergmann
2014-12-15 14:38         ` Lee Jones
2014-12-15 14:38           ` Lee Jones
2014-12-15 14:38           ` Lee Jones
2014-12-15 14:38           ` Lee Jones
2014-12-15 11:25 ` [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog Lee Jones
2014-12-15 11:25   ` Lee Jones
2014-12-15 12:29   ` David Paris
2014-12-15 12:29     ` David Paris
2014-12-15 12:29     ` David Paris
2014-12-15 12:52   ` David Paris
2014-12-15 12:52     ` David Paris
2014-12-15 12:52     ` David Paris
2014-12-15 14:15   ` Guenter Roeck
2014-12-15 14:15     ` Guenter Roeck
2014-12-15 14:15     ` Guenter Roeck
2014-12-15 16:23   ` Guenter Roeck
2014-12-15 16:23     ` Guenter Roeck
2014-12-15 11:25 ` [PATCH 7/8] rtc: st: add new driver for ST's LPC RTC Lee Jones
2014-12-15 11:25   ` Lee Jones
2014-12-15 11:25   ` Lee Jones
2014-12-15 12:28   ` [STLinux Kernel] " David Paris
2014-12-15 12:28     ` David Paris
2014-12-15 12:28     ` David Paris
2014-12-15 14:17   ` Guenter Roeck
2014-12-15 14:17     ` Guenter Roeck
2014-12-15 14:17     ` Guenter Roeck
2014-12-15 11:25 ` [PATCH 8/8] ARM: STi: DT: STiH407: Add Device Tree node for the LPC Lee Jones
2014-12-15 11:25   ` Lee Jones
2014-12-15 11:25   ` Lee Jones
2014-12-17 16:45 [PATCH v2 0/8] watchdog: rtc: New driver(s) for ST's LPC IP Lee Jones
2014-12-17 16:45 ` [PATCH 6/8] watchdog: st_wdt: Add new driver for ST's LPC Watchdog Lee Jones
2014-12-17 16:45   ` Lee Jones
2014-12-17 16:45   ` Lee Jones
2014-12-17 18:02   ` Guenter Roeck
2014-12-17 18:02     ` Guenter Roeck
2014-12-18  8:26     ` Lee Jones
2014-12-18  8:26       ` Lee Jones
2014-12-18  8:26       ` Lee Jones
2014-12-18 16:00       ` Guenter Roeck
2014-12-18 16:00         ` Guenter Roeck
2014-12-18 17:20         ` Lee Jones
2014-12-18 17:20           ` Lee Jones
2014-12-18 17:20           ` Lee Jones
2014-12-18 17:20           ` Lee Jones

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.